Skip to content

Commit

Permalink
Added support for reading from stdin, and for the "--stdin-filepath" …
Browse files Browse the repository at this point in the history
…option
  • Loading branch information
fabiospampinato committed Apr 15, 2024
1 parent 6cb91c5 commit 028e631
Show file tree
Hide file tree
Showing 6 changed files with 63 additions and 12 deletions.
11 changes: 11 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@
"kasi": "^1.1.0",
"lomemo": "^1.0.0",
"pioppo": "^1.1.1",
"promise-resolve-timeout": "^2.0.0",
"specialist": "^1.4.0",
"tiny-editorconfig": "^1.0.0",
"tiny-jsonc": "^1.0.1",
Expand Down
9 changes: 3 additions & 6 deletions src/bin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -133,16 +133,13 @@ const makeBin = (): Bin => {
"--require-pragma",
'Require either "@prettier" or "@format" to be present in the file\'s first docblock comment in order for it to be formatted\nDefaults to "false"',
)
// .option(
// "--stdin-filepath <path>",
// "Path to the file to pretend that stdin comes from",
// )
.option("--stdin-filepath <path>", "Path to the file to pretend that stdin comes from")
// .option("--support-info", "Print support information as JSON")
/* DEFAULT COMMAND */
.argument("[file/dir/glob...]", "Files, directories or globs to format")
.action(async (options, files) => {
const { run } = await import("./index.js");
const baseOptions = normalizeOptions(options, files);
const baseOptions = await normalizeOptions(options, files);
const pluginsOptions = {};
return run(baseOptions, pluginsOptions);
})
Expand Down Expand Up @@ -205,7 +202,7 @@ const makePluggableBin = async (): Promise<Bin> => {

bin = bin.action(async (options, files) => {
const { run } = await import("./index.js");
const baseOptions = normalizeOptions(options, files);
const baseOptions = await normalizeOptions(options, files);
const pluginsDynamicOptions = normalizePluginOptions(options, optionsNames);
const pluginsOptions = { ...pluginsStaticOptions, ...pluginsDynamicOptions };
return run(baseOptions, pluginsOptions);
Expand Down
32 changes: 29 additions & 3 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,37 @@ import { PRETTIER_VERSION, CLI_VERSION } from "./constants.js";
import Known from "./known.js";
import Logger from "./logger.js";
import { makePrettier } from "./prettier.js";
import { castArray, getExpandedFoldersPaths, getFoldersChildrenPaths, getPluginsVersions, getProjectPath, getTargetsPaths } from "./utils.js";
import { castArray, getExpandedFoldersPaths, getFoldersChildrenPaths, getPluginsVersions, getProjectPath, getStdin, getTargetsPaths } from "./utils.js";
import { fastRelativePath, isString, isUndefined, negate, pluralize, uniq } from "./utils.js";
import type { FormatOptions, Options, PluginsOptions } from "./types.js";

async function run(options: Options, pluginsOptions: PluginsOptions): Promise<void> {
async function run(options: Options, pluginsOptions): Promise<void> {
const stdin = await getStdin();
if (isString(stdin)) {
return runStdin(options, pluginsOptions);
} else {
return runGlobs(options, pluginsOptions);
}
}

async function runStdin(options: Options, pluginsOptions: PluginsOptions): Promise<void> {
const logger = new Logger(options.logLevel);
const prettier = await import("./prettier_serial.js");

const fileName = options.stdinFilepath || "stdin";
const fileContent = (await getStdin()) || "";

try {
const formatted = await prettier.format(fileName, fileContent, options.formatOptions, options.contextOptions, pluginsOptions);
logger.always(formatted);
process.exitCode = options.check && formatted !== fileContent ? 1 : 0;
} catch (error) {
logger.prefixed.error(String(error));
process.exitCode = 1;
}
}

async function runGlobs(options: Options, pluginsOptions: PluginsOptions): Promise<void> {
const logger = new Logger(options.logLevel);
const spinner = options.check ? logger.spinner.log() : undefined;

Expand Down Expand Up @@ -193,4 +219,4 @@ async function run(options: Options, pluginsOptions: PluginsOptions): Promise<vo
process.exitCode = (!totalMatched && options.errorOnUnmatchedPattern) || totalErrored || (totalUnformatted && !options.write) ? 1 : 0;
}

export { run };
export { run, runStdin, runGlobs };
1 change: 1 addition & 0 deletions src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ type Options = {
logLevel: LogLevel;
parallel: boolean;
parallelWorkers: number;
stdinFilepath: string | undefined;
/* CONTEXT OPTIONS */
contextOptions: ContextOptions;
/* FORMAT OPTIONS */
Expand Down
21 changes: 18 additions & 3 deletions src/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,9 @@ import crypto from "node:crypto";
import fs from "node:fs";
import path from "node:path";
import process from "node:process";
import { text as stream2text } from "node:stream/consumers";
import url from "node:url";
import resolveTimeout from "promise-resolve-timeout";
import { exit } from "specialist";
import readdir from "tiny-readdir-glob";
import zeptomatchEscape from "zeptomatch-escape";
Expand Down Expand Up @@ -166,6 +168,15 @@ function getProjectPath(rootPath: string): string {
}
}

const getStdin = once(async (): Promise<string | undefined> => {
// Without a TTY, the process is likely, but not certainly, being piped
if (!process.stdin.isTTY) {
const stdin = stream2text(process.stdin);
const fallback = resolveTimeout(1_000, undefined);
return Promise.race([stdin, fallback]);
}
});

async function getTargetsPaths(
rootPath: string,
globs: string[],
Expand Down Expand Up @@ -262,15 +273,16 @@ function noop(): undefined {
return;
}

function normalizeOptions(options: unknown, targets: unknown[]): Options {
async function normalizeOptions(options: unknown, targets: unknown[]): Promise<Options> {
if (!isObject(options)) exit("Invalid options object");

const targetsGlobs = targets.filter(isString);

const targetsStatic = "--" in options && Array.isArray(options["--"]) ? options["--"].filter(isString).map(zeptomatchEscape) : [];
const globs = [...targetsGlobs, ...targetsStatic];

if (!globs.length) exit("Expected at least one target file/dir/glob");
const stdin = await getStdin();

if (!isString(stdin) && !globs.length) exit("Expected at least one target file/dir/glob");

const check = "check" in options && !!options.check;
const list = "listDifferent" in options && !!options.listDifferent;
Expand All @@ -296,6 +308,7 @@ function normalizeOptions(options: unknown, targets: unknown[]): Options {
const logLevel = "logLevel" in options ? ((options.logLevel || "log") as LogLevel) : "log";
const parallel = "parallel" in options && !!options.parallel;
const parallelWorkers = ("parallelWorkers" in options && Math.round(Number(options.parallelWorkers))) || 0;
const stdinFilepath = "stdinFilepath" in options && isString(options.stdinFilepath) ? options.stdinFilepath : undefined;

const contextOptions = normalizeContextOptions(options);
const formatOptions = normalizeFormatOptions(options);
Expand All @@ -317,6 +330,7 @@ function normalizeOptions(options: unknown, targets: unknown[]): Options {
logLevel,
parallel,
parallelWorkers,
stdinFilepath,
contextOptions,
formatOptions,
};
Expand Down Expand Up @@ -631,6 +645,7 @@ export {
getPluginsPaths,
getPluginsVersions,
getProjectPath,
getStdin,
getTargetsPaths,
isArray,
isBoolean,
Expand Down

0 comments on commit 028e631

Please sign in to comment.