diff options
| author | Yoshiya Hinosawa <stibium121@gmail.com> | 2019-02-02 00:16:39 +0900 |
|---|---|---|
| committer | Ryan Dahl <ry@tinyclouds.org> | 2019-02-01 10:16:39 -0500 |
| commit | 0eb1a49b387601014f6f8eec84128988d2ad6b62 (patch) | |
| tree | c10501285e48e07c7a79c841deda68b6f211cb19 /prettier/main.ts | |
| parent | e6d1b5ed3e79c26825017e25ae6c229aeb6fefa8 (diff) | |
Add reusable prettier wrapper CLI (denoland/deno_std#165)
This also fixes an issue with the path on azure-pipelines.
Original: https://github.com/denoland/deno_std/commit/e7837ff0f007d8f2175ec58ae3065dabfb4823f1
Diffstat (limited to 'prettier/main.ts')
| -rwxr-xr-x | prettier/main.ts | 248 |
1 files changed, 248 insertions, 0 deletions
diff --git a/prettier/main.ts b/prettier/main.ts new file mode 100755 index 000000000..435206dcd --- /dev/null +++ b/prettier/main.ts @@ -0,0 +1,248 @@ +#!/usr/bin/env deno --allow-run --allow-write +// 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. +import { + args, + platform, + readAll, + lstat, + exit, + run, + readFile, + writeFile +} from "deno"; +import { xrun } from "./util.ts"; +import { parse } from "../flags/mod.ts"; +import { prettier, prettierPlugins } from "./prettier.ts"; + +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. + --ignore <path> Ignore the given path(s). + +Example: + deno prettier/main.ts script1.ts script2.js + Formats the files + + deno prettier/main.ts --check script1.ts script2.js + Checks if the files are formatted + + deno prettier/main.ts + Formats the all files in the repository +`; + +// Available parsers +type ParserLabel = "typescript" | "babel" | "markdown" | "json"; + +const encoder = new TextEncoder(); +const decoder = new TextDecoder(); + +// Lists files in the given directory. +// TODO: Replace git usage with deno's API calls +async function listFiles(dir: string = "."): Promise<string[]> { + return decoder + .decode( + await readAll( + xrun({ + args: ["git", "ls-files", dir], + stdout: "piped" + }).stdout + ) + ) + .trim() + .split(/\r?\n/); +} + +async function getSourceFiles(args: string[]): Promise<string[]> { + if (args.length === 0) { + return listFiles(); + } + + const results = args.map(async path => { + if ((await lstat(path)).isDirectory()) { + return listFiles(path); + } + + return path; + }); + + return [].concat(...(await Promise.all(results))); +} + +// Filters out the files which contains any pattern in the given ignoreList. +function filterIgnoreList(files: string[], ignoreList: string[]) { + return files.filter(path => + ignoreList.every(pattern => !path.includes(pattern)) + ); +} + +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 +): Promise<boolean> { + const text = await readFileIfExists(filename); + + if (!text) { + // The file is deleted. Skip. + return; + } + + const formatted = prettier.check(text, { + 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 +): Promise<void> { + const text = await readFileIfExists(filename); + + if (!text) { + // The file is deleted. Skip. + return; + } + + const formatted = prettier.format(text, { + parser, + plugins: prettierPlugins + }); + + if (text !== formatted) { + console.log(`Formatting ${filename}`); + await writeFile(filename, encoder.encode(formatted)); + } +} + +/** + * Selects the right prettier parser for the given path. + */ +function selectParser(path: string): ParserLabel | null { + if (/\.ts$/.test(path)) { + return "typescript"; + } else if (/\.js$/.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( + args: string[], + ignoreList: string[] +): Promise<void> { + const checks = []; + + filterIgnoreList(await getSourceFiles(args), ignoreList).forEach(file => { + const parser = selectParser(file); + if (parser) { + checks.push(checkFile(file, parser)); + } + }); + + const results = await Promise.all(checks); + + if (results.every(result => 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( + args: string[], + ignoreList: string[] +): Promise<void> { + const formats = []; + + filterIgnoreList(await getSourceFiles(args), ignoreList).forEach(file => { + const parser = selectParser(file); + if (parser) { + formats.push(formatFile(file, parser)); + } + }); + + await Promise.all(formats); + exit(0); +} + +async function main(opts) { + const { help, ignore, check, _: args } = opts; + + if (help) { + console.log(HELP_MESSAGE); + exit(0); + } + + const ignoreList: string[] = Array.isArray(ignore) ? ignore : [ignore]; + + try { + if (check) { + await checkSourceFiles(args, ignoreList); + } else { + await formatSourceFiles(args, ignoreList); + } + } catch (e) { + console.log(e); + exit(1); + } +} + +main( + parse(args.slice(1), { + string: ["ignore"], + boolean: ["check", "help"], + default: { + ignore: [] + }, + alias: { + H: "help" + } + }) +); |
