summaryrefslogtreecommitdiff
path: root/prettier/main.ts
diff options
context:
space:
mode:
authorYoshiya Hinosawa <stibium121@gmail.com>2019-02-02 00:16:39 +0900
committerRyan Dahl <ry@tinyclouds.org>2019-02-01 10:16:39 -0500
commit0eb1a49b387601014f6f8eec84128988d2ad6b62 (patch)
treec10501285e48e07c7a79c841deda68b6f211cb19 /prettier/main.ts
parente6d1b5ed3e79c26825017e25ae6c229aeb6fefa8 (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-xprettier/main.ts248
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"
+ }
+ })
+);