diff options
author | Bartek Iwańczuk <biwanczuk@gmail.com> | 2020-01-30 03:16:48 +0100 |
---|---|---|
committer | GitHub <noreply@github.com> | 2020-01-29 21:16:48 -0500 |
commit | 73a3cc21d0e7ceec1275078db125f57ce97725fb (patch) | |
tree | 17d845d5d125d3ba8ecefa49e57e02e9c088c68b /std/prettier/main.ts | |
parent | f0a6062012c4e09553877c9feedb3fbc3d4625b9 (diff) |
feat: dprint formatter (#3820)
* rewrite fmt_test in Rust, remove tools/fmt_test.py
* remove //std/prettier
Diffstat (limited to 'std/prettier/main.ts')
-rwxr-xr-x | std/prettier/main.ts | 616 |
1 files changed, 0 insertions, 616 deletions
diff --git a/std/prettier/main.ts b/std/prettier/main.ts deleted file mode 100755 index a61faa975..000000000 --- a/std/prettier/main.ts +++ /dev/null @@ -1,616 +0,0 @@ -#!/usr/bin/env -S deno --allow-run --allow-write -/** - * Copyright © James Long and contributors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ -// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license. -// This script formats the given source files. If the files are omitted, it -// formats the all files in the repository. -import { parse } from "../flags/mod.ts"; -import * as path from "../path/mod.ts"; -import * as toml from "../encoding/toml.ts"; -import * as yaml from "../encoding/yaml.ts"; -import * as ignore from "./ignore.ts"; -import { ExpandGlobOptions, WalkInfo, expandGlob } from "../fs/mod.ts"; -import { prettier, prettierPlugins } from "./prettier.ts"; -const { args, cwd, exit, readAll, readFile, stdin, stdout, writeFile } = Deno; - -const HELP_MESSAGE = ` -Formats the given files. If no arg is passed, then formats the all files. - -Usage: deno prettier/main.ts [options] [files...] - -Options: - -H, --help Show this help message and exit. - --check Check if the source files are formatted. - --write Whether to write to the file, otherwise - it will output to stdout, Defaults to - false. - --ignore <path> Ignore the given path(s). - --ignore-path <auto|disable|path> Path to a file containing patterns that - describe files to ignore. Optional - value: auto/disable/filepath. Defaults - to null. - --stdin Specifies to read the code from stdin. - If run the command in a pipe, you do not - need to specify this flag. - Defaults to false. - --stdin-parser <typescript|babel|markdown|json> - If set --stdin flag, then need specify a - parser for stdin. available parser: - typescript/babel/markdown/json. Defaults - to typescript. - --config <auto|disable|path> Specify the configuration file of the - prettier. - Optional value: auto/disable/filepath. - Defaults to null. - -JS/TS Styling Options: - --print-width <int> The line length where Prettier will try - wrap. Defaults to 80. - --tab-width <int> Number of spaces per indentation level. - Defaults to 2. - --use-tabs Indent with tabs instead of spaces. - Defaults to false. - --no-semi Do not print semicolons, except at the - beginning of lines which may need them. - --single-quote Use single quotes instead of double - quotes. Defaults to false. - --quote-props <as-needed|consistent|preserve> - Change when properties in objects are - quoted. Defaults to as-needed. - --jsx-single-quote Use single quotes instead of double - quotes in JSX. - --jsx-bracket-same-line Put the > of a multi-line JSX element at - the end of the last line instead of - being alone on the next line (does not - apply to self closing elements). - --trailing-comma <none|es5|all> Print trailing commas wherever possible - when multi-line. Defaults to none. - --no-bracket-spacing Do not print spaces between brackets. - --arrow-parens <avoid|always> Include parentheses around a sole arrow - function parameter. Defaults to avoid. - --end-of-line <auto|lf|crlf|cr> Which end of line characters to apply. - Defaults to auto. - -Markdown Styling Options: - --prose-wrap <always|never|preserve> How to wrap prose. Defaults to preserve. - -Example: - deno run prettier/main.ts --write script1.ts script2.js - Formats the files - - deno run prettier/main.ts --check script1.ts script2.js - Checks if the files are formatted - - deno run prettier/main.ts --write - Formats the all files in the repository - - deno run prettier/main.ts script1.ts - Print the formatted code to stdout - - cat script1.ts | deno run prettier/main.ts - Read the typescript code from stdin and - output formatted code to stdout. - - cat config.json | deno run prettier/main.ts --stdin-parser=json - Read the JSON string from stdin and - output formatted code to stdout. -`; - -// Available parsers -type ParserLabel = "typescript" | "babel" | "markdown" | "json"; - -interface PrettierBuildInOptions { - printWidth: number; - tabWidth: number; - useTabs: boolean; - semi: boolean; - singleQuote: boolean; - quoteProps: string; - jsxSingleQuote: boolean; - jsxBracketSameLine: boolean; - trailingComma: string; - bracketSpacing: boolean; - arrowParens: string; - proseWrap: string; - endOfLine: string; -} - -interface PrettierOptions extends PrettierBuildInOptions { - write: boolean; -} - -const encoder = new TextEncoder(); -const decoder = new TextDecoder(); - -async function readFileIfExists(filename: string): Promise<string | null> { - let data; - try { - data = await readFile(filename); - } catch (e) { - // The file is deleted. Returns null. - return null; - } - - return decoder.decode(data); -} - -/** - * Checks if the file has been formatted with prettier. - */ -async function checkFile( - filename: string, - parser: ParserLabel, - prettierOpts: PrettierOptions -): Promise<boolean> { - const text = await readFileIfExists(filename); - - if (!text) { - // The file is empty. Skip. - return true; - } - - const formatted = prettier.check(text, { - ...prettierOpts, - parser, - plugins: prettierPlugins - }); - - if (!formatted) { - // TODO: print some diff info here to show why this failed - console.error(`${filename} ... Not formatted`); - } - - return formatted; -} - -/** - * Formats the given file. - */ -async function formatFile( - filename: string, - parser: ParserLabel, - prettierOpts: PrettierOptions -): Promise<void> { - const text = await readFileIfExists(filename); - - if (!text) { - // The file is deleted. Skip. - return; - } - - const formatted: string = prettier.format(text, { - ...prettierOpts, - parser, - plugins: prettierPlugins - }); - - const fileUnit8 = encoder.encode(formatted); - if (prettierOpts.write) { - if (text !== formatted) { - console.log(`Formatting ${filename}`); - await writeFile(filename, fileUnit8); - } - } else { - await stdout.write(fileUnit8); - } -} - -/** - * Selects the right prettier parser for the given path. - */ -function selectParser(path: string): ParserLabel | null { - if (/\.tsx?$/.test(path)) { - return "typescript"; - } else if (/\.jsx?$/.test(path)) { - return "babel"; - } else if (/\.json$/.test(path)) { - return "json"; - } else if (/\.md$/.test(path)) { - return "markdown"; - } - - return null; -} - -/** - * Checks if the files of the given paths have been formatted with prettier. - * If paths are empty, then checks all the files. - */ -async function checkSourceFiles( - files: AsyncIterableIterator<WalkInfo>, - prettierOpts: PrettierOptions -): Promise<void> { - const checks: Array<Promise<boolean>> = []; - - for await (const { filename } of files) { - const parser = selectParser(filename); - if (parser) { - checks.push(checkFile(filename, parser, prettierOpts)); - } - } - - const results = await Promise.all(checks); - - if (results.every((result): boolean => result)) { - console.log("Every file is formatted"); - exit(0); - } else { - console.log("Some files are not formatted"); - exit(1); - } -} - -/** - * Formats the files of the given paths with prettier. - * If paths are empty, then formats all the files. - */ -async function formatSourceFiles( - files: AsyncIterableIterator<WalkInfo>, - prettierOpts: PrettierOptions -): Promise<void> { - const formats: Array<Promise<void>> = []; - - for await (const { filename } of files) { - const parser = selectParser(filename); - if (parser) { - if (prettierOpts.write) { - formats.push(formatFile(filename, parser, prettierOpts)); - } else { - await formatFile(filename, parser, prettierOpts); - } - } - } - - if (prettierOpts.write) { - await Promise.all(formats); - } - exit(0); -} - -/** - * Format source code - */ -function format( - text: string, - parser: ParserLabel, - prettierOpts: PrettierOptions -): string { - const formatted: string = prettier.format(text, { - ...prettierOpts, - parser: parser, - plugins: prettierPlugins - }); - - return formatted; -} - -/** - * Format code from stdin and output to stdout - */ -async function formatFromStdin( - parser: ParserLabel, - prettierOpts: PrettierOptions -): Promise<void> { - const byte = await readAll(stdin); - const formattedCode = format( - new TextDecoder().decode(byte), - parser, - prettierOpts - ); - await stdout.write(new TextEncoder().encode(formattedCode)); -} - -/** - * Get the files to format. - * @param include The glob patterns to select the files. - * eg `"cmd/*.ts"` to select all the typescript files in cmd - * directory. - * eg `"cmd/run.ts"` to select `cmd/run.ts` file as only. - * @param exclude The glob patterns to ignore files. - * eg `"*_test.ts"` to ignore all the test file. - * @param root The directory from which to apply default globs. - * @returns returns an async iterable object - */ -async function* getTargetFiles( - include: string[], - exclude: string[], - root: string = cwd() -): AsyncIterableIterator<WalkInfo> { - const expandGlobOpts: ExpandGlobOptions = { - root, - exclude, - includeDirs: true, - extended: true, - globstar: true - }; - - async function* expandDirectory(d: string): AsyncIterableIterator<WalkInfo> { - for await (const walkInfo of expandGlob("**/*", { - ...expandGlobOpts, - root: d, - includeDirs: false - })) { - yield walkInfo; - } - } - - for (const globString of include) { - for await (const walkInfo of expandGlob(globString, expandGlobOpts)) { - if (walkInfo.info.isDirectory()) { - yield* expandDirectory(walkInfo.filename); - } else { - yield walkInfo; - } - } - } -} - -/** - * auto detect prettier configuration file and return config if file exist. - */ -async function autoResolveConfig(): Promise<PrettierBuildInOptions> { - const configFileNamesMap = { - ".prettierrc.json": 1, - ".prettierrc.yaml": 1, - ".prettierrc.yml": 1, - ".prettierrc.js": 1, - ".prettierrc.ts": 1, - "prettier.config.js": 1, - "prettier.config.ts": 1, - ".prettierrc.toml": 1 - }; - - const files = await Deno.readDir("."); - - for (const f of files) { - if (f.isFile() && configFileNamesMap[f.name]) { - const c = await resolveConfig(f.name); - if (c) { - return c; - } - } - } - - return; -} - -/** - * parse prettier configuration file. - * @param filepath the configuration file path. - * support extension name with .json/.toml/.js - */ -async function resolveConfig( - filepath: string -): Promise<PrettierBuildInOptions> { - let config: PrettierBuildInOptions = undefined; - - function generateError(msg: string): Error { - return new Error(`Invalid prettier configuration file: ${msg}.`); - } - - const raw = new TextDecoder().decode(await Deno.readFile(filepath)); - - switch (path.extname(filepath)) { - case ".json": - try { - config = JSON.parse(raw) as PrettierBuildInOptions; - } catch (err) { - throw generateError(err.message); - } - break; - case ".yml": - case ".yaml": - try { - config = yaml.parse(raw) as PrettierBuildInOptions; - } catch (err) { - throw generateError(err.message); - } - break; - case ".toml": - try { - config = toml.parse(raw) as PrettierBuildInOptions; - } catch (err) { - throw generateError(err.message); - } - break; - case ".js": - case ".ts": - const absPath = path.isAbsolute(filepath) - ? filepath - : path.join(cwd(), filepath); - - try { - const output = await import( - // TODO: Remove platform condition - // after https://github.com/denoland/deno/issues/3355 fixed - Deno.build.os === "win" ? "file://" + absPath : absPath - ); - - if (output && output.default) { - config = output.default as PrettierBuildInOptions; - } else { - throw new Error( - "Prettier of JS version should have default exports." - ); - } - } catch (err) { - throw generateError(err.message); - } - - break; - default: - break; - } - - return config; -} - -/** - * auto detect .prettierignore and return pattern if file exist. - */ -async function autoResolveIgnoreFile(): Promise<Set<string>> { - const files = await Deno.readDir("."); - - for (const f of files) { - if (f.isFile() && f.name === ".prettierignore") { - return await resolveIgnoreFile(f.name); - } - } - - return new Set([]); -} - -/** - * parse prettier ignore file. - * @param filepath the ignore file path. - */ -async function resolveIgnoreFile(filepath: string): Promise<Set<string>> { - const raw = new TextDecoder().decode(await Deno.readFile(filepath)); - return ignore.parse(raw); -} - -async function main(opts): Promise<void> { - const { help, check, _: args } = opts; - - let prettierOpts: PrettierOptions = { - printWidth: Number(opts["print-width"]), - tabWidth: Number(opts["tab-width"]), - useTabs: Boolean(opts["use-tabs"]), - semi: Boolean(opts["semi"]), - singleQuote: Boolean(opts["single-quote"]), - quoteProps: opts["quote-props"], - jsxSingleQuote: Boolean(opts["jsx-single-quote"]), - jsxBracketSameLine: Boolean(opts["jsx-bracket-same-line "]), - trailingComma: opts["trailing-comma"], - bracketSpacing: Boolean(opts["bracket-spacing"]), - arrowParens: opts["arrow-parens"], - proseWrap: opts["prose-wrap"], - endOfLine: opts["end-of-line"], - write: opts["write"] - }; - - if (help) { - console.log(HELP_MESSAGE); - exit(0); - } - - const configFilepath = opts["config"]; - - if (configFilepath && configFilepath !== "disable") { - const config = - configFilepath === "auto" - ? await autoResolveConfig() - : await resolveConfig(configFilepath); - - if (config) { - prettierOpts = { ...prettierOpts, ...config }; - } - } - - let ignore = opts.ignore as string[]; - - if (!Array.isArray(ignore)) { - ignore = [ignore]; - } - - const ignoreFilepath = opts["ignore-path"]; - - if (ignoreFilepath && ignoreFilepath !== "disable") { - const ignorePatterns = - ignoreFilepath === "auto" - ? await autoResolveIgnoreFile() - : await resolveIgnoreFile(ignoreFilepath); - - ignore = ignore.concat(Array.from(ignorePatterns)); - } - - const files = getTargetFiles(args.length ? args : ["."], ignore); - - const tty = Deno.isTTY(); - - const shouldReadFromStdin = - (!tty.stdin && (tty.stdout || tty.stderr)) || !!opts["stdin"]; - - try { - if (shouldReadFromStdin) { - await formatFromStdin(opts["stdin-parser"], prettierOpts); - } else if (check) { - await checkSourceFiles(files, prettierOpts); - } else { - await formatSourceFiles(files, prettierOpts); - } - } catch (e) { - console.error(e); - exit(1); - } -} - -main( - parse(args, { - string: [ - "ignore", - "ignore-path", - "printWidth", - "tab-width", - "trailing-comma", - "arrow-parens", - "prose-wrap", - "end-of-line", - "stdin-parser", - "quote-props" - ], - boolean: [ - "check", - "help", - "semi", - "use-tabs", - "single-quote", - "bracket-spacing", - "write", - "stdin", - "jsx-single-quote", - "jsx-bracket-same-line" - ], - default: { - ignore: [], - "print-width": "80", - "tab-width": "2", - "use-tabs": false, - semi: true, - "single-quote": false, - "trailing-comma": "none", - "bracket-spacing": true, - "arrow-parens": "avoid", - "prose-wrap": "preserve", - "end-of-line": "auto", - write: false, - stdin: false, - "stdin-parser": "typescript", - "quote-props": "as-needed", - "jsx-single-quote": false, - "jsx-bracket-same-line": false - }, - alias: { - H: "help" - } - }) -); |