diff --git a/prettier/README.md b/prettier/README.md index 3a8d5a0c27dc3b..6485d2da21e5cc 100644 --- a/prettier/README.md +++ b/prettier/README.md @@ -6,22 +6,31 @@ Prettier APIs and tools for deno To formats the source files, run: -```console +```bash deno --allow-read --allow-write https://deno.land/std/prettier/main.ts ``` You can format only specific files by passing the arguments. -```console +```bash deno --allow-read --allow-write https://deno.land/std/prettier/main.ts path/to/script.ts ``` You can format files on specific directory by passing the directory's path. -```console +```bash deno --allow-read --allow-write https://deno.land/std/prettier/main.ts path/to/script.ts ``` +You can format the input plain text stream. default parse it as typescript code. + +```bash +cat path/to/script.ts | deno https://deno.land/std/prettier/main.ts +cat path/to/script.js | deno https://deno.land/std/prettier/main.ts --stdin-parser=babel +cat path/to/config.json | deno https://deno.land/std/prettier/main.ts --stdin-parser=json +cat path/to/README.md | deno https://deno.land/std/prettier/main.ts --stdin-parser=markdown +``` + ## Use API You can use APIs of prettier as the following: diff --git a/prettier/main.ts b/prettier/main.ts index 82a213d41cf770..03fe4605cd0731 100755 --- a/prettier/main.ts +++ b/prettier/main.ts @@ -23,7 +23,7 @@ // Copyright 2018-2019 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. -const { args, exit, readFile, writeFile, stdout } = Deno; +const { args, exit, readFile, writeFile, stdout, stdin, readAll } = Deno; import { glob, isGlob, GlobOptions } from "../fs/glob.ts"; import { walk, WalkInfo } from "../fs/walk.ts"; import { parse } from "../flags/mod.ts"; @@ -41,6 +41,15 @@ Options: it will output to stdout, Defaults to false. --ignore Ignore the given path(s). + --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 + If set --stdin flag, then need specify a + parser for stdin. available parser: + typescript/babel/markdown/json. Defaults + to typescript. JS/TS Styling Options: --print-width The line length where Prettier will try @@ -76,6 +85,14 @@ Example: 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 @@ -237,6 +254,39 @@ async function formatSourceFiles( 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 { + 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 selectors The glob patterns to select the files. @@ -329,14 +379,21 @@ async function main(opts): Promise { options ); + const tty = Deno.isTTY(); + + const shouldReadFromStdin = + (!tty.stdin && (tty.stdout || tty.stderr)) || !!opts["stdin"]; + try { - if (check) { + if (shouldReadFromStdin) { + await formatFromStdin(opts["stdin-parser"], prettierOpts); + } else if (check) { await checkSourceFiles(files, prettierOpts); } else { await formatSourceFiles(files, prettierOpts); } } catch (e) { - console.log(e); + console.error(e); exit(1); } } @@ -350,7 +407,8 @@ main( "trailing-comma", "arrow-parens", "prose-wrap", - "end-of-line" + "end-of-line", + "stdin-parser" ], boolean: [ "check", @@ -359,7 +417,8 @@ main( "use-tabs", "single-quote", "bracket-spacing", - "write" + "write", + "stdin" ], default: { ignore: [], @@ -373,7 +432,9 @@ main( "arrow-parens": "avoid", "prose-wrap": "preserve", "end-of-line": "auto", - write: false + write: false, + stdin: false, + "stdin-parser": "typescript" }, alias: { H: "help" diff --git a/prettier/main_test.ts b/prettier/main_test.ts index a2bc2570227dfc..cb23079d2ceb13 100644 --- a/prettier/main_test.ts +++ b/prettier/main_test.ts @@ -2,7 +2,7 @@ import { join } from "../fs/path.ts"; import { EOL } from "../fs/path/constants.ts"; import { assertEquals } from "../testing/asserts.ts"; -import { test } from "../testing/mod.ts"; +import { test, runIfMain } from "../testing/mod.ts"; import { xrun } from "./util.ts"; import { copy, emptyDir } from "../fs/mod.ts"; const { readAll, execPath } = Deno; @@ -233,3 +233,128 @@ test(async function testPrettierPrintToStdout(): Promise { emptyDir(tempDir); }); + +test(async function testPrettierReadFromStdin(): Promise { + interface TestCase { + stdin: string; + stdout: string; + stderr: string; + code: number; + success: boolean; + parser?: string; + } + + async function readFromStdinAssertion( + stdin: string, + expectedStdout: string, + expectedStderr: string, + expectedCode: number, + expectedSuccess: boolean, + parser?: string + ): Promise { + const inputCode = stdin; + const p1 = Deno.run({ + args: [execPath, "./prettier/testdata/echox.ts", `${inputCode}`], + stdout: "piped" + }); + + const p2 = Deno.run({ + args: [ + execPath, + "run", + "./prettier/main.ts", + "--stdin", + ...(parser ? ["--stdin-parser", parser] : []) + ], + stdin: "piped", + stdout: "piped", + stderr: "piped" + }); + + const n = await Deno.copy(p2.stdin!, p1.stdout!); + assertEquals(n, new TextEncoder().encode(stdin).length); + + const status1 = await p1.status(); + assertEquals(status1.code, 0); + assertEquals(status1.success, true); + p2.stdin!.close(); + const status2 = await p2.status(); + assertEquals(status2.code, expectedCode); + assertEquals(status2.success, expectedSuccess); + const decoder = new TextDecoder("utf-8"); + assertEquals( + decoder.decode(await Deno.readAll(p2.stdout!)), + expectedStdout + ); + assertEquals( + decoder.decode(await Deno.readAll(p2.stderr!)).split(EOL)[0], + expectedStderr + ); + p2.close(); + p1.close(); + } + + const testCases: TestCase[] = [ + { + stdin: `console.log("abc" )`, + stdout: `console.log("abc");\n`, + stderr: ``, + code: 0, + success: true + }, + { + stdin: `console.log("abc" )`, + stdout: `console.log("abc");\n`, + stderr: ``, + code: 0, + success: true, + parser: "babel" + }, + { + stdin: `{\"a\":\"b\"}`, + stdout: `{ "a": "b" }\n`, + stderr: ``, + code: 0, + success: true, + parser: "json" + }, + { + stdin: `## test`, + stdout: `## test\n`, + stderr: ``, + code: 0, + success: true, + parser: "markdown" + }, + { + stdin: `invalid typescript code##!!@@`, + stdout: ``, + stderr: `SyntaxError: ';' expected. (1:9)`, + code: 1, + success: false + }, + { + stdin: `console.log("foo");`, + stdout: ``, + stderr: + 'Error: Couldn\'t resolve parser "invalid_parser". ' + + "Parsers must be explicitly added to the standalone bundle.", + code: 1, + success: false, + parser: "invalid_parser" + } + ]; + + for (const t of testCases) { + await readFromStdinAssertion( + t.stdin, + t.stdout, + t.stderr, + t.code, + t.success, + t.parser + ); + } +}); + +runIfMain(import.meta); diff --git a/prettier/testdata/echox.ts b/prettier/testdata/echox.ts new file mode 100644 index 00000000000000..68c54b9995f5ab --- /dev/null +++ b/prettier/testdata/echox.ts @@ -0,0 +1,8 @@ +async function echox(args: string[]) { + for (const arg of args) { + await Deno.stdout.write(new TextEncoder().encode(arg)); + } + Deno.exit(0); +} + +echox(Deno.args.slice(1));