@prettier/cli
Advanced tools
Comparing version 0.1.5 to 0.2.0
335
dist/bin.js
#!/usr/bin/env node | ||
import { bin, color } from "specialist"; | ||
import { PRETTIER_VERSION } from "./constants.js"; | ||
import { normalizeOptions } from "./utils.js"; | ||
import { toKebabCase } from "kasi"; | ||
import { bin, color, parseArgv } from "specialist"; | ||
import { PRETTIER_VERSION, IS_BUN } from "./constants.js"; | ||
import { getPlugin, isNumber, normalizeOptions, normalizeFormatOptions, normalizePluginOptions } from "./utils.js"; | ||
import { run } from "./index.js"; | ||
//TODO: --find-cache | ||
bin("prettier", "Prettier is an opinionated code formatter") | ||
/* OPTIONS */ | ||
.autoExit(true) | ||
.autoUpdateNotifier(false) | ||
.colors(true) | ||
.package("prettier", PRETTIER_VERSION) | ||
/* USAGES */ | ||
.usage(`${color.cyan("prettier")} ${color.yellow("[file/dir/glob...]")} ${color.green("[options]")}`) | ||
.usage(`${color.cyan("prettier")} ${color.yellow('"src/**/*.js"')} ${color.green("--check")} ${color.green("--parallel")}`) | ||
.usage(`${color.cyan("prettier")} ${color.yellow('"src/**/*.js"')} ${color.green("-l")} ${color.green("--no-cache")}`) | ||
.usage(`${color.cyan("prettier")} ${color.yellow('"src/**/*.js"')} ${color.green("--write")} ${color.green("--parallel")}`) | ||
.usage(`${color.cyan("prettier")} ${color.yellow("./path/to/target/file.js")} ${color.green("--cache-location")} ${color.blue("./path/to/cache/file.json")}`) | ||
/* OUTPUT OPTIONS */ | ||
.option("--check, -c", "Check if the given files are formatted, print a human-friendly summary (see also --list-different)", { section: "Output" }) | ||
.option("--list-different, -l", "Print the names of files that are different from Prettier's formatting (see also --check)", { section: "Output" }) | ||
.option("--write, -w", "Edit files in-place (Beware!)", { section: "Output" }) | ||
/* FORMAT OPTIONS */ | ||
.option("--arrow-parens <always|avoid>", 'Include parentheses around a sole arrow function parameter\nDefaults to "always"', { section: "Format", enum: ["always", "avoid"] }) | ||
.option("--bracket-same-line", 'Put ">" of opening tags on the last line instead of on a new line\nDefaults to "false"', { section: "Format" }) | ||
.option("--no-bracket-spacing", 'Do not print spaces between brackets\nDefaults to "true"', { section: "Format" }) | ||
.option("--embedded-language-formatting <auto|off>", 'Control how Prettier formats quoted code embedded in the file\nDefaults to "auto"', { section: "Format", enum: ["auto", "off"] }) | ||
.option("--end-of-line <lf|crlf|cr|auto>", 'Which end of line characters to apply\nDefaults to "lf"', { | ||
section: "Format", | ||
enum: ["lf", "crlf", "cr", "auto"], | ||
}) | ||
.option("--html-whitespace-sensitivity <css|strict|ignore>", 'How to handle whitespaces in HTML\nDefaults to "css"', { | ||
section: "Format", | ||
enum: ["css", "strict", "ignore"], | ||
}) | ||
.option("--jsx-single-quote", 'Use single quotes in JSX\nDefaults to "false"', { section: "Format" }) | ||
.option("--parser <flow|babel|babel-flow|babel-ts|typescript|acorn|espree|meriyah|css|less|scss|json|json5|json-stringify|graphql|markdown|mdx|vue|yaml|glimmer|html|angular|lwc>", "Which parser to use", { section: "Format", enum: ["flow", "babel", "babel-flow", "babel-ts", "typescript", "acorn", "espree", "meriyah", "css", "less", "scss", "json", "json5", "json-stringify", "graphql", "markdown", "mdx", "vue", "yaml", "glimmer", "html", "angular", "lwc"] }) | ||
.option("--print-width <int>", 'The line length where Prettier will try wrap\nDefaults to "80"', { | ||
section: "Format", | ||
}) | ||
.option("--prose-wrap <always|never|preserve>", 'How to wrap prose\nDefaults to "preserve"', { | ||
section: "Format", | ||
enum: ["always", "never", "preserve"], | ||
}) | ||
.option("--quote-props <as-needed|consistent|preserve>", 'Change when properties in objects are quoted\nDefaults to "as-needed"', { section: "Format", enum: ["as-needed", "consistent", "preserve"] }) | ||
.option("--no-semi", 'Do not print semicolons, except at the beginning of lines which may need them\nDefaults to "true"', { section: "Format" }) | ||
.option("--single-attribute-per-line", 'Enforce single attribute per line in HTML, Vue and JSX\nDefaults to "false"', { section: "Format" }) | ||
.option("--single-quote", 'Use single quotes instead of double quotes\nDefaults to "false"', { section: "Format" }) | ||
.option("--tab-width <int>", 'Number of spaces per indentation level\nDefaults to "2"', { section: "Format" }) | ||
.option("--trailing-comma <all|es5|none>", 'Print trailing commas wherever possible when multi-line\nDefaults to "all"', { section: "Format", enum: ["all", "es5", "none"] }) | ||
.option("--use-tabs", 'Indent with tabs instead of spaces\nDefaults to "false"', { section: "Format" }) | ||
.option("--vue-indent-script-and-style", 'Indent script and style tags in Vue files\nDefaults to "false"', { | ||
section: "Format", | ||
}) | ||
/* CONFIG OPTIONS */ | ||
// .option( | ||
// "--config <path>", | ||
// "Path to a Prettier configuration file (.prettierrc, package.json, prettier.config.js)", | ||
// { section: "Config" }, | ||
// ) | ||
.option("--no-config", "Do not look for a configuration file", { | ||
section: "Config", | ||
default: true, | ||
}) | ||
// .option( | ||
// "--config-precedence <cli-override|file-override|prefer-file>", | ||
// 'Define in which order config files and CLI options should be evaluated.\nDefaults to "cli-override"', | ||
// { section: "Config" }, | ||
// ) | ||
.option("--no-editorconfig", "Don't take .editorconfig into account when parsing configuration", { | ||
section: "Config", | ||
default: true, | ||
}) | ||
// .option( | ||
// "--find-config-path <path>", | ||
// "Find and print the path to a configuration file for the given input file", | ||
// { section: "Config" }, | ||
// ) | ||
// .option( | ||
// "--ignore-path <path...>", | ||
// "Path to a file with patterns describing files to ignore\nMultiple values are accepted\nDefaults to [.gitignore, .prettierignore]", | ||
// { section: "Config", eager: true }, | ||
// ) | ||
// .option( | ||
// "--plugin <path...>", | ||
// "Add a plugin\nMultiple plugins are accepted\nDefaults to []", | ||
// { section: "Config", eager: true }, | ||
// ) | ||
// .option( | ||
// "--with-node-modules", | ||
// 'Process files inside the "node_modules" directory', | ||
// { section: "Config" }, | ||
// ) | ||
/* EDITOR OPTIONS */ | ||
// .option( | ||
// "--cursor-offset <int>", | ||
// 'Print (to stderr) where a cursor at the given position would move to after formatting\nThis option cannot be used with --range-start and --range-end\nDefaults to "-1"', | ||
// { section: "Editor" }, | ||
// ) | ||
// .option( | ||
// "--range-end <int>", | ||
// 'Format code ending at a given character offset (exclusive)\nThe range will extend forwards to the end of the selected statement\nThis option cannot be used with --cursor-offset\nDefaults to "Infinity"', | ||
// { section: "Editor" }, | ||
// ) | ||
// .option( | ||
// "--range-start <int>", | ||
// 'Format code starting at a given character offset\nThe range will extend backwards to the start of the first line containing the selected statement\nThis option cannot be used with --cursor-offset\nDefaults to "0"', | ||
// { section: "Editor" }, | ||
// ) | ||
/* OTHER OPTIONS */ | ||
.option("--no-cache", "Do not use the built-in caching mechanism", { | ||
default: true, | ||
}) | ||
.option("--cache-location <path>", "Path to the cache file") | ||
.option("--no-color", "Do not colorize output messages") | ||
.option("--no-error-on-unmatched-pattern", "Prevent errors when pattern is unmatched", { default: true }) | ||
// .option( | ||
// "--file-info <path>", | ||
// "Extract the following info (as JSON) for a given file path. Reported fields:\n* ignored (boolean) - true if file path is filtered by --ignore-path\n* inferredParser (string | null) - name of parser inferred from file path", | ||
// ) | ||
.option("--ignore-unknown, -u", "Ignore unknown files") | ||
.option("--insert-pragma", 'Insert @format pragma into file\'s first docblock comment\nDefaults to "false"') | ||
.option("--log-level <silent|error|warn|log|debug>", 'What level of logs to report\nDefaults to "log"', { | ||
enum: ["silent", "error", "warn", "log", "debug"], | ||
}) | ||
.option("--no-parallel", 'Process files in parallel\nDefaults to "true"', { | ||
default: true, | ||
}) | ||
.option("--parallel-workers <int>", 'Number of parallel workers to use\nDefaults to "0"') | ||
.option("--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("--support-info", "Print support information as JSON") | ||
/* DEFAULT COMMAND */ | ||
.argument("[file/dir/glob...]", "Files, directories or globs to format") | ||
.action((options, files) => { | ||
return run(normalizeOptions(options, files)); | ||
}) | ||
/* RUN */ | ||
.run(); | ||
const makeBin = () => { | ||
return (bin("prettier", "An opinionated code formatter") | ||
/* OPTIONS */ | ||
.autoExit(true) | ||
.autoUpdateNotifier(false) | ||
.colors(true) | ||
.package("prettier", PRETTIER_VERSION) | ||
/* USAGES */ | ||
.usage(`${color.cyan("prettier")} ${color.yellow("[file/dir/glob...]")} ${color.green("[options]")}`) | ||
.usage(`${color.cyan("prettier")} ${color.yellow('"src/**/*.js"')} ${color.green("--check")} ${color.green("--parallel")}`) | ||
.usage(`${color.cyan("prettier")} ${color.yellow('"src/**/*.js"')} ${color.green("-l")} ${color.green("--no-cache")}`) | ||
.usage(`${color.cyan("prettier")} ${color.yellow('"src/**/*.js"')} ${color.green("--write")} ${color.green("--parallel")}`) | ||
.usage(`${color.cyan("prettier")} ${color.yellow("./path/to/target/file.js")} ${color.green("--cache-location")} ${color.blue("./path/to/cache/file.json")}`) | ||
/* OUTPUT OPTIONS */ | ||
.option("--check, -c", "Check if the given files are formatted, print a human-friendly summary (see also --list-different)", { section: "Output" }) | ||
.option("--list-different, -l", "Print the names of files that are different from Prettier's formatting (see also --check)", { section: "Output" }) | ||
.option("--write, -w", "Edit files in-place (Beware!)", { section: "Output" }) | ||
/* FORMAT OPTIONS */ | ||
.option("--experimental-ternaries", 'Use curious ternaries, with the question mark after the condition\nDefaults to "false"', { section: "Format" }) | ||
.option("--arrow-parens <always|avoid>", 'Include parentheses around a sole arrow function parameter\nDefaults to "always"', { | ||
section: "Format", | ||
enum: ["always", "avoid"], | ||
}) | ||
.option("--bracket-same-line", 'Put ">" of opening tags on the last line instead of on a new line\nDefaults to "false"', { section: "Format" }) | ||
.option("--no-bracket-spacing", 'Do not print spaces between brackets\nDefaults to "true"', { section: "Format" }) | ||
.option("--embedded-language-formatting <auto|off>", 'Control how Prettier formats quoted code embedded in the file\nDefaults to "auto"', { | ||
section: "Format", | ||
enum: ["auto", "off"], | ||
}) | ||
.option("--end-of-line <lf|crlf|cr|auto>", 'Which end of line characters to apply\nDefaults to "lf"', { | ||
section: "Format", | ||
enum: ["lf", "crlf", "cr", "auto"], | ||
}) | ||
.option("--html-whitespace-sensitivity <css|strict|ignore>", 'How to handle whitespaces in HTML\nDefaults to "css"', { | ||
section: "Format", | ||
enum: ["css", "strict", "ignore"], | ||
}) | ||
.option("--jsx-single-quote", 'Use single quotes in JSX\nDefaults to "false"', { section: "Format" }) | ||
.option("--parser <flow|babel|babel-flow|babel-ts|typescript|acorn|espree|meriyah|css|less|scss|json|json5|json-stringify|graphql|markdown|mdx|vue|yaml|glimmer|html|angular|lwc>", "Which parser to use", { section: "Format", enum: ["flow", "babel", "babel-flow", "babel-ts", "typescript", "acorn", "espree", "meriyah", "css", "less", "scss", "json", "json5", "json-stringify", "graphql", "markdown", "mdx", "vue", "yaml", "glimmer", "html", "angular", "lwc"] }) | ||
.option("--print-width <int>", 'The line length where Prettier will try wrap\nDefaults to "80"', { | ||
section: "Format", | ||
}) | ||
.option("--prose-wrap <always|never|preserve>", 'How to wrap prose\nDefaults to "preserve"', { | ||
section: "Format", | ||
enum: ["always", "never", "preserve"], | ||
}) | ||
.option("--quote-props <as-needed|consistent|preserve>", 'Change when properties in objects are quoted\nDefaults to "as-needed"', { | ||
section: "Format", | ||
enum: ["as-needed", "consistent", "preserve"], | ||
}) | ||
.option("--no-semi", 'Do not print semicolons, except at the beginning of lines which may need them\nDefaults to "true"', { section: "Format" }) | ||
.option("--single-attribute-per-line", 'Enforce single attribute per line in HTML, Vue and JSX\nDefaults to "false"', { section: "Format" }) | ||
.option("--single-quote", 'Use single quotes instead of double quotes\nDefaults to "false"', { section: "Format" }) | ||
.option("--tab-width <int>", 'Number of spaces per indentation level\nDefaults to "2"', { section: "Format" }) | ||
.option("--trailing-comma <all|es5|none>", 'Print trailing commas wherever possible when multi-line\nDefaults to "all"', { | ||
section: "Format", | ||
enum: ["all", "es5", "none"], | ||
}) | ||
.option("--use-tabs", 'Indent with tabs instead of spaces\nDefaults to "false"', { section: "Format" }) | ||
.option("--vue-indent-script-and-style", 'Indent script and style tags in Vue files\nDefaults to "false"', { | ||
section: "Format", | ||
}) | ||
/* CONFIG OPTIONS */ | ||
// .option( | ||
// "--config <path>", | ||
// "Path to a Prettier configuration file (.prettierrc, package.json, prettier.config.js)", | ||
// { section: "Config" }, | ||
// ) | ||
.option("--no-config", "Do not look for a configuration file", { | ||
section: "Config", | ||
default: true, | ||
}) | ||
.option("--no-editorconfig", "Don't take .editorconfig into account when parsing configuration", { | ||
section: "Config", | ||
default: true, | ||
}) | ||
// .option( | ||
// "--ignore-path <path...>", | ||
// "Path to a file with patterns describing files to ignore\nMultiple values are accepted\nDefaults to [.gitignore, .prettierignore]", | ||
// { section: "Config", eager: true }, | ||
// ) | ||
.option("--plugin <package...>", "Add a plugin\nMultiple plugins are accepted\nDefaults to []", { section: "Config" }) | ||
// .option( | ||
// "--with-node-modules", | ||
// 'Process files inside the "node_modules" directory', | ||
// { section: "Config" }, | ||
// ) | ||
/* EDITOR OPTIONS */ | ||
.option("--cursor-offset <int>", 'Print (to stderr) where a cursor at the given position would move to after formatting\nDefaults to "-1"', { | ||
section: "Editor", | ||
}) | ||
.option("--range-end <int>", 'Format code ending at a given character offset (exclusive)\nThe range will extend forwards to the end of the selected statement\nDefaults to "Infinity"', { section: "Editor" }) | ||
.option("--range-start <int>", 'Format code starting at a given character offset\nThe range will extend backwards to the start of the first line containing the selected statement\nDefaults to "0"', { section: "Editor" }) | ||
/* OTHER OPTIONS */ | ||
.option("--no-cache", "Do not use the built-in caching mechanism", { | ||
default: true, | ||
}) | ||
.option("--cache-location <path>", "Path to the cache file") | ||
.option("--no-color", "Do not colorize output messages") | ||
.option("--no-error-on-unmatched-pattern", "Prevent errors when pattern is unmatched", { default: true }) | ||
// .option( | ||
// "--file-info <path>", | ||
// "Extract the following info (as JSON) for a given file path. Reported fields:\n* ignored (boolean) - true if file path is filtered by --ignore-path\n* inferredParser (string | null) - name of parser inferred from file path", | ||
// ) | ||
.option("--ignore-unknown, -u", "Ignore unknown files") | ||
.option("--insert-pragma", 'Insert @format pragma into file\'s first docblock comment\nDefaults to "false"') | ||
.option("--log-level <silent|error|warn|log|debug>", 'What level of logs to report\nDefaults to "log"', { | ||
enum: ["silent", "error", "warn", "log", "debug"], | ||
}) | ||
.option("--no-parallel", 'Process files in parallel\nDefaults to "true"', { | ||
default: !IS_BUN, //TOOD: always set this to "true", once "worker_threads" work in Bun | ||
}) | ||
.option("--parallel-workers <int>", 'Number of parallel workers to use\nDefaults to "0"') | ||
.option("--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("--support-info", "Print support information as JSON") | ||
/* DEFAULT COMMAND */ | ||
.argument("[file/dir/glob...]", "Files, directories or globs to format") | ||
.action((options, files) => { | ||
const baseOptions = normalizeOptions(options, files); | ||
const pluginsOptions = {}; | ||
return run(baseOptions, pluginsOptions); | ||
})); | ||
}; | ||
const makePluggableBin = async () => { | ||
let bin = makeBin(); | ||
const argv = process.argv.slice(2); | ||
const args = parseArgv(argv); | ||
const formatOptions = normalizeFormatOptions(args); | ||
const pluginsStaticOptions = {}; | ||
const pluginsNames = formatOptions.plugins || []; | ||
const optionsNames = []; | ||
for (let i = 0, l = pluginsNames.length; i < l; i++) { | ||
const pluginName = pluginsNames[i]; | ||
const plugin = await getPlugin(pluginName); | ||
for (const option in plugin.options) { | ||
optionsNames.push(option); | ||
Object.assign(pluginsStaticOptions, plugin.defaultOptions); | ||
const schema = plugin.options[option]; | ||
const type = schema.type; | ||
const section = schema.category; | ||
const deprecated = !!schema.deprecated; | ||
const descriptionInfo = schema.description || ""; | ||
const initial = schema.default; | ||
if (type === "int") { | ||
//TODO: Support schema.range | ||
//TODO: Ensure the value is cast to a valid integer | ||
const descriptionDefault = isNumber(initial) ? `Defaults to "${initial}"` : ""; | ||
const description = `${descriptionInfo}\n${descriptionDefault}`.trim(); | ||
const variadic = !!schema.array; | ||
const args = variadic ? "<int...>" : "<int>"; | ||
bin = bin.option(`--${toKebabCase(option)} ${args}`, description, { deprecated, section, default: initial }); | ||
} | ||
else if (type === "boolean") { | ||
//TODO: Support schema.array | ||
const descriptionDefault = initial ? 'Defaults to "true"' : 'Defaults to "false"'; | ||
const description = `${descriptionInfo}\n${descriptionDefault}`.trim(); | ||
bin = bin.option(`--${toKebabCase(option)}`, description, { deprecated, section, default: initial }); | ||
} | ||
else if (type === "string" || type === "path") { | ||
const descriptionDefault = initial ? `Defaults to "${initial}"` : ""; | ||
const description = `${descriptionInfo}\n${descriptionDefault}`.trim(); | ||
const variadic = !!schema.array; | ||
const args = variadic ? "<value...>" : "<value>"; | ||
bin = bin.option(`--${toKebabCase(option)} ${args}`, description, { deprecated, section, default: initial }); | ||
} | ||
else if (type === "choice") { | ||
const descriptionDefault = initial ? `Defaults to "${initial}"` : ""; | ||
const description = `${descriptionInfo}\n${descriptionDefault}`.trim(); | ||
const values = schema.choices.map((choice) => choice.value); | ||
const args = values.length ? `<${values.join("|")}>` : "<value>"; | ||
bin = bin.option(`--${toKebabCase(option)} ${args}`, description, { deprecated, section, default: initial }); | ||
} | ||
} | ||
} | ||
bin = bin.action((options, files) => { | ||
const baseOptions = normalizeOptions(options, files); | ||
const pluginsDynamicOptions = normalizePluginOptions(options, optionsNames); | ||
const pluginsOptions = { ...pluginsStaticOptions, ...pluginsDynamicOptions }; | ||
return run(baseOptions, pluginsOptions); | ||
}); | ||
return bin; | ||
}; | ||
const runBin = async () => { | ||
const bin = await makePluggableBin(); | ||
bin.run(); | ||
}; | ||
runBin(); |
@@ -19,2 +19,3 @@ /// <reference types="node" /> | ||
private version; | ||
private rootPath; | ||
private storePath; | ||
@@ -29,4 +30,4 @@ private store; | ||
get(filePath: string): FileData; | ||
set(filePath: string, filePathHash: string, fileFormatted: boolean, fileContentExpected: string): void; | ||
set(filePath: string, fileHashPath: string, fileFormatted: boolean, fileContentExpected: string): void; | ||
} | ||
export default Cache; |
import fs from "node:fs"; | ||
import path from "node:path"; | ||
import { getCachePath, isArray, isBoolean, isObject, isString, sha1hex, sha1base64 } from "./utils.js"; | ||
import { fastRelativePath, getCachePath, isArray, isBoolean, isObject, isString, sha1hex, sha1base64 } from "./utils.js"; | ||
//TODO: Maybe remember thrown errors also, if they are under a certain size | ||
//TODO: Use some kind of relative path as the file key, if in CI enviornments parts of the path can be kinda random, or if the cache file is committed | ||
class Cache { | ||
@@ -10,2 +9,3 @@ constructor(version, rootPath, options, logger) { | ||
this.logger = logger; | ||
this.rootPath = rootPath; | ||
this.storePath = options.cacheLocation || path.join(getCachePath(rootPath), `${sha1hex(rootPath)}.json`); | ||
@@ -50,6 +50,7 @@ this.store = this.read(); | ||
get(filePath) { | ||
const filePathHash = sha1base64(filePath); | ||
const save = this.set.bind(this, filePath, filePathHash); | ||
const fileRelativePath = fastRelativePath(this.rootPath, filePath); | ||
const fileHashPath = sha1base64(fileRelativePath); | ||
const save = this.set.bind(this, filePath, fileHashPath); | ||
try { | ||
const file = this.store[this.version]?.files?.[filePathHash]; | ||
const file = this.store[this.version]?.files?.[fileHashPath]; | ||
if (!file || !isArray(file) || file.length !== 2) | ||
@@ -71,3 +72,3 @@ return { save }; | ||
} | ||
set(filePath, filePathHash, fileFormatted, fileContentExpected) { | ||
set(filePath, fileHashPath, fileFormatted, fileContentExpected) { | ||
var _a, _b; | ||
@@ -80,3 +81,3 @@ try { | ||
version.modified = Date.now(); | ||
files[filePathHash] = [hash, fileFormatted]; | ||
files[fileHashPath] = [hash, fileFormatted]; | ||
this.dirty = true; | ||
@@ -83,0 +84,0 @@ } |
import * as EditorConfig from "tiny-editorconfig"; | ||
import type { Config, ConfigWithOverrides } from "tiny-editorconfig"; | ||
import type { FormatOptions } from "./types.js"; | ||
declare const getEditorConfig: ((arg: string) => Promise<EditorConfig.ConfigWithOverrides | undefined>) & { | ||
cache: Map<string, Promise<EditorConfig.ConfigWithOverrides | undefined>>; | ||
import type { FormatOptions, PromiseMaybe } from "./types.js"; | ||
declare const getEditorConfig: ((folderPath: string, filesNames: string[]) => PromiseMaybe<EditorConfig.ConfigWithOverrides | undefined>) & { | ||
cache: Map<string, PromiseMaybe<EditorConfig.ConfigWithOverrides | undefined>>; | ||
}; | ||
declare const getEditorConfigsMap: (foldersPaths: string[]) => Promise<Partial<Record<string, ConfigWithOverrides>>>; | ||
declare const getEditorConfigsUp: ((arg: string) => Promise<EditorConfig.ConfigWithOverrides[]>) & { | ||
declare const getEditorConfigsMap: (foldersPaths: string[], filesNames: string[]) => Promise<Partial<Record<string, ConfigWithOverrides>>>; | ||
declare const getEditorConfigsUp: ((folderPath: string, filesNames: string[]) => Promise<EditorConfig.ConfigWithOverrides[]>) & { | ||
cache: Map<string, Promise<EditorConfig.ConfigWithOverrides[]>>; | ||
}; | ||
declare const getEditorConfigResolved: (filePath: string) => Promise<Config>; | ||
declare const getEditorConfigResolved: (filePath: string, filesNames: string[]) => Promise<Config>; | ||
declare const getEditorConfigFormatOptions: (config: Config) => FormatOptions; | ||
export { getEditorConfig, getEditorConfigsMap, getEditorConfigsUp, getEditorConfigResolved, getEditorConfigFormatOptions, }; | ||
export { getEditorConfig, getEditorConfigsMap, getEditorConfigsUp, getEditorConfigResolved, getEditorConfigFormatOptions }; |
@@ -5,25 +5,21 @@ import fs from "node:fs/promises"; | ||
import Known from "./known.js"; | ||
import { fastJoinedPath, findLastIndex, memoize, zipObject } from "./utils.js"; | ||
const getEditorConfig = memoize(async (folderPath) => { | ||
const filePath = fastJoinedPath(folderPath, ".editorconfig"); | ||
if (!Known.hasFile(filePath)) | ||
return; | ||
try { | ||
const fileContent = await fs.readFile(filePath, "utf8"); | ||
const config = EditorConfig.parse(fileContent); | ||
return config; | ||
import { fastJoinedPath, findLastIndex, isUndefined, memoize, noop, zipObjectUnless } from "./utils.js"; | ||
const getEditorConfig = memoize((folderPath, filesNames) => { | ||
for (let i = 0, l = filesNames.length; i < l; i++) { | ||
const fileName = filesNames[i]; | ||
const filePath = fastJoinedPath(folderPath, fileName); | ||
if (!Known.hasFilePath(filePath)) | ||
continue; | ||
return fs.readFile(filePath, "utf8").then(EditorConfig.parse).catch(noop); | ||
} | ||
catch { | ||
return; | ||
} | ||
}); | ||
const getEditorConfigsMap = async (foldersPaths) => { | ||
const configs = await Promise.all(foldersPaths.map(getEditorConfig)); | ||
const map = zipObject(foldersPaths, configs); | ||
const getEditorConfigsMap = async (foldersPaths, filesNames) => { | ||
const configs = await Promise.all(foldersPaths.map((folderPath) => getEditorConfig(folderPath, filesNames))); | ||
const map = zipObjectUnless(foldersPaths, configs, isUndefined); | ||
return map; | ||
}; | ||
const getEditorConfigsUp = memoize(async (folderPath) => { | ||
const config = await getEditorConfig(folderPath); | ||
const getEditorConfigsUp = memoize(async (folderPath, filesNames) => { | ||
const config = await getEditorConfig(folderPath, filesNames); | ||
const folderPathUp = path.dirname(folderPath); | ||
const configsUp = folderPath !== folderPathUp ? await getEditorConfigsUp(folderPathUp) : []; | ||
const configsUp = folderPath !== folderPathUp ? await getEditorConfigsUp(folderPathUp, filesNames) : []; | ||
const configs = config ? [...configsUp, config] : configsUp; | ||
@@ -33,5 +29,5 @@ const lastRootIndex = findLastIndex(configs, (config) => config.root); | ||
}); | ||
const getEditorConfigResolved = async (filePath) => { | ||
const getEditorConfigResolved = async (filePath, filesNames) => { | ||
const folderPath = path.dirname(filePath); | ||
const configs = await getEditorConfigsUp(folderPath); | ||
const configs = await getEditorConfigsUp(folderPath, filesNames); | ||
const config = EditorConfig.resolve(configs, filePath); | ||
@@ -53,2 +49,2 @@ return config; | ||
}; | ||
export { getEditorConfig, getEditorConfigsMap, getEditorConfigsUp, getEditorConfigResolved, getEditorConfigFormatOptions, }; | ||
export { getEditorConfig, getEditorConfigsMap, getEditorConfigsUp, getEditorConfigResolved, getEditorConfigFormatOptions }; |
import type { Ignore } from "./types.js"; | ||
declare const getIgnoresContent: ((arg: string) => Promise<[(string | undefined)?, (string | undefined)?] | undefined>) & { | ||
cache: Map<string, Promise<[(string | undefined)?, (string | undefined)?] | undefined>>; | ||
declare const getIgnoresContent: ((folderPath: string, filesNames: string[]) => Promise<string[] | undefined>) & { | ||
cache: Map<string, Promise<string[] | undefined>>; | ||
}; | ||
declare const getIgnoresContentMap: (foldersPaths: string[]) => Promise<Partial<Record<string, [string?, string?]>>>; | ||
declare const getIgnoreBy: (folderPath: string, fileContent: string) => Ignore; | ||
declare const getIgnores: ((arg: string) => Promise<Ignore | undefined>) & { | ||
declare const getIgnoresContentMap: (foldersPaths: string[], filesNames: string[]) => Promise<Partial<Record<string, string[]>>>; | ||
declare const getIgnoreBy: (folderPath: string, filesContents: string[]) => Ignore; | ||
declare const getIgnores: ((folderPath: string, filesNames: string[]) => Promise<Ignore | undefined>) & { | ||
cache: Map<string, Promise<Ignore | undefined>>; | ||
}; | ||
declare const getIgnoresUp: ((arg: string) => Promise<Ignore | undefined>) & { | ||
declare const getIgnoresUp: ((folderPath: string, filesNames: string[]) => Promise<Ignore | undefined>) & { | ||
cache: Map<string, Promise<Ignore | undefined>>; | ||
}; | ||
declare const getIgnoreResolved: (filePath: string) => Promise<boolean>; | ||
declare const getIgnoreResolved: (filePath: string, filesNames: string[]) => Promise<boolean>; | ||
export { getIgnoreBy, getIgnores, getIgnoresContent, getIgnoresContentMap, getIgnoresUp, getIgnoreResolved }; |
@@ -1,55 +0,42 @@ | ||
import ignore from "ignore"; | ||
import fastIgnore from "fast-ignore"; | ||
import fs from "node:fs/promises"; | ||
import path from "node:path"; | ||
import Known from "./known.js"; | ||
import { fastJoinedPath, fastRelativeChildPath, memoize, someOf, zipObject } from "./utils.js"; | ||
const getIgnoreContentBy = async (folderPath, fileName) => { | ||
import { fastJoinedPath, fastRelativeChildPath, isString, isUndefined, memoize, noop, someOf, zipObjectUnless } from "./utils.js"; | ||
const getIgnoreContent = (folderPath, fileName) => { | ||
const filePath = fastJoinedPath(folderPath, fileName); | ||
if (!Known.hasFile(filePath)) | ||
if (!Known.hasFilePath(filePath)) | ||
return; | ||
try { | ||
return await fs.readFile(filePath, "utf8"); | ||
} | ||
catch { | ||
return; | ||
} | ||
return fs.readFile(filePath, "utf8").catch(noop); | ||
}; | ||
const getIgnoresContent = memoize(async (folderPath) => { | ||
const git = await getIgnoreContentBy(folderPath, ".gitignore"); | ||
const prettier = await getIgnoreContentBy(folderPath, ".prettierignore"); | ||
const contents = git ? (prettier ? [git, prettier] : [git]) : prettier ? [prettier] : []; | ||
const getIgnoresContent = memoize(async (folderPath, filesNames) => { | ||
const contentsRaw = await Promise.all(filesNames.map((fileName) => getIgnoreContent(folderPath, fileName))); | ||
const contents = contentsRaw.filter(isString); | ||
if (!contents.length) | ||
return; | ||
return [git, prettier]; | ||
return contents; | ||
}); | ||
const getIgnoresContentMap = async (foldersPaths) => { | ||
const ignoresContent = await Promise.all(foldersPaths.map(getIgnoresContent)); | ||
const map = zipObject(foldersPaths, ignoresContent); | ||
const getIgnoresContentMap = async (foldersPaths, filesNames) => { | ||
const contents = await Promise.all(foldersPaths.map((folderPath) => getIgnoresContent(folderPath, filesNames))); | ||
const map = zipObjectUnless(foldersPaths, contents, isUndefined); | ||
return map; | ||
}; | ||
const getIgnoreBy = (folderPath, fileContent) => { | ||
const instance = ignore().add(fileContent); | ||
const ignores = instance.ignores.bind(instance); | ||
const getIgnoreBy = (folderPath, filesContents) => { | ||
const ignore = fastIgnore(filesContents); | ||
return (filePath) => { | ||
const fileRelativePath = fastRelativeChildPath(folderPath, filePath); | ||
return !!fileRelativePath && ignores(fileRelativePath); | ||
return !!fileRelativePath && ignore(fileRelativePath); | ||
}; | ||
}; | ||
const getIgnores = memoize(async (folderPath) => { | ||
const contents = await getIgnoresContent(folderPath); | ||
if (!contents) | ||
const getIgnores = memoize(async (folderPath, filesNames) => { | ||
const contents = await getIgnoresContent(folderPath, filesNames); | ||
if (!contents?.length) | ||
return; | ||
const [gitContent, prettierContent] = contents; | ||
const git = gitContent ? getIgnoreBy(folderPath, gitContent) : undefined; | ||
const prettier = prettierContent ? getIgnoreBy(folderPath, prettierContent) : undefined; | ||
const ignores = git ? (prettier ? [git, prettier] : [git]) : prettier ? [prettier] : []; | ||
if (!ignores.length) | ||
return; | ||
const ignore = someOf(ignores); | ||
const ignore = getIgnoreBy(folderPath, contents); | ||
return ignore; | ||
}); | ||
const getIgnoresUp = memoize(async (folderPath) => { | ||
const ignore = await getIgnores(folderPath); | ||
const getIgnoresUp = memoize(async (folderPath, filesNames) => { | ||
const ignore = await getIgnores(folderPath, filesNames); | ||
const folderPathUp = path.dirname(folderPath); | ||
const ignoreUp = folderPath !== folderPathUp ? await getIgnoresUp(folderPathUp) : undefined; | ||
const ignoreUp = folderPath !== folderPathUp ? await getIgnoresUp(folderPathUp, filesNames) : undefined; | ||
const ignores = ignore ? (ignoreUp ? [ignore, ignoreUp] : [ignore]) : ignoreUp ? [ignoreUp] : []; | ||
@@ -61,7 +48,8 @@ if (!ignores.length) | ||
}); | ||
const getIgnoreResolved = async (filePath) => { | ||
const getIgnoreResolved = async (filePath, filesNames) => { | ||
const folderPath = path.dirname(filePath); | ||
const ignore = await getIgnoresUp(folderPath); | ||
return !!ignore?.(filePath); | ||
const ignore = await getIgnoresUp(folderPath, filesNames); | ||
const ignored = !!ignore?.(filePath); | ||
return ignored; | ||
}; | ||
export { getIgnoreBy, getIgnores, getIgnoresContent, getIgnoresContentMap, getIgnoresUp, getIgnoreResolved }; |
@@ -1,10 +0,8 @@ | ||
import type { PrettierConfig, PrettierConfigWithOverrides } from "./types.js"; | ||
declare const getPrettierConfig: ((arg: string) => Promise<PrettierConfigWithOverrides | undefined>) & { | ||
cache: Map<string, Promise<PrettierConfigWithOverrides | undefined>>; | ||
}; | ||
declare const getPrettierConfigsMap: (foldersPaths: string[]) => Promise<Partial<Record<string, PrettierConfig>>>; | ||
declare const getPrettierConfigsUp: ((arg: string) => Promise<PrettierConfigWithOverrides[]>) & { | ||
import type { PrettierConfig, PrettierConfigWithOverrides, PromiseMaybe } from "./types.js"; | ||
declare const getPrettierConfig: (folderPath: string, fileName: string) => PromiseMaybe<PrettierConfigWithOverrides | undefined>; | ||
declare const getPrettierConfigsMap: (foldersPaths: string[], filesNames: string[]) => Promise<Partial<Record<string, PrettierConfig[]>>>; | ||
declare const getPrettierConfigsUp: ((folderPath: string, filesNames: string[]) => Promise<PrettierConfigWithOverrides[]>) & { | ||
cache: Map<string, Promise<PrettierConfigWithOverrides[]>>; | ||
}; | ||
declare const getPrettierConfigResolved: (filePath: string) => Promise<PrettierConfig>; | ||
declare const getPrettierConfigResolved: (filePath: string, filesNames: string[]) => Promise<PrettierConfig>; | ||
export { getPrettierConfig, getPrettierConfigsMap, getPrettierConfigsUp, getPrettierConfigResolved }; |
import yaml from "js-yaml"; | ||
import fs from "node:fs/promises"; | ||
import sfs from "node:fs"; | ||
import fs from "node:fs"; | ||
import path from "node:path"; | ||
@@ -9,80 +8,79 @@ import JSONC from "tiny-jsonc"; | ||
import { fastJoinedPath, fastRelativeChildPath } from "./utils.js"; | ||
import { isObject, memoize, normalizePrettierOptions, omit, zipObject } from "./utils.js"; | ||
import { isObject, isTruthy, isUndefined, memoize, noop, normalizePrettierOptions, omit, zipObjectUnless } from "./utils.js"; | ||
//TODO: Maybe completely drop support for JSON5, or implement it properly | ||
//TODO: Maybe add support for TOML | ||
//TODO: Check which of these file names have been found in the repo just once | ||
const loaders = [ | ||
{ | ||
files: ["package.json"], | ||
loader: async (filePath) => { | ||
const fileContent = await fs.readFile(filePath, "utf8"); | ||
const pkg = JSON.parse(fileContent); | ||
const config = isObject(pkg) && "prettier" in pkg ? pkg.prettier : undefined; | ||
return config; | ||
}, | ||
const Loaders = { | ||
js: async (filePath) => { | ||
const module = await import(filePath); | ||
return module.default || module.exports || module.config || module.prettier; //TODO: Streamline this | ||
}, | ||
{ | ||
files: [".prettierrc", ".prettierrc.yml", ".prettierrc.yaml"], | ||
loader: async (filePath) => { | ||
const fileContent = await fs.readFile(filePath, "utf8"); | ||
return yaml.load(fileContent, { | ||
schema: yaml.JSON_SCHEMA, | ||
}); | ||
}, | ||
json: async (filePath) => { | ||
const fileContent = fs.readFileSync(filePath, "utf8"); | ||
const config = JSON.parse(fileContent); | ||
return config; | ||
}, | ||
{ | ||
files: [".prettierrc.json", ".prettierrc.jsonc", ".prettierrc.json5"], | ||
loader: async (filePath) => { | ||
const fileContent = await fs.readFile(filePath, "utf8"); | ||
const config = JSONC.parse(fileContent); | ||
return config; | ||
}, | ||
jsonc: async (filePath) => { | ||
const fileContent = fs.readFileSync(filePath, "utf8"); | ||
const config = JSONC.parse(fileContent); | ||
return config; | ||
}, | ||
{ | ||
files: [".prettierrc.js", "prettier.config.js", ".prettierrc.cjs", "prettier.config.cjs", ".prettierrc.mjs", "prettier.config.mjs"], // prettier-ignore | ||
loader: async (filePath) => { | ||
const exists = sfs.existsSync(filePath); | ||
if (!exists) | ||
return; | ||
const module = await import(filePath); | ||
return module.default || module.exports || module.config || module.prettier; //TODO: Streamline this | ||
}, | ||
package: async (filePath) => { | ||
const fileContent = fs.readFileSync(filePath, "utf8"); | ||
const pkg = JSON.parse(fileContent); | ||
const config = isObject(pkg) && "prettier" in pkg ? pkg.prettier : undefined; | ||
return config; | ||
}, | ||
]; | ||
const getPrettierConfig = memoize(async (folderPath) => { | ||
for (let li = 0, ll = loaders.length; li < ll; li++) { | ||
const { files, loader } = loaders[li]; | ||
for (let fi = 0, fl = files.length; fi < fl; fi++) { | ||
const filePath = fastJoinedPath(folderPath, files[fi]); | ||
if (!Known.hasFile(filePath)) | ||
continue; | ||
try { | ||
const config = await loader(filePath); | ||
if (!config) | ||
continue; | ||
if (!isObject(config)) | ||
continue; | ||
return normalizePrettierOptions(config, folderPath); | ||
} | ||
catch { | ||
continue; | ||
} | ||
} | ||
} | ||
yaml: async (filePath) => { | ||
const fileContent = fs.readFileSync(filePath, "utf8"); | ||
return yaml.load(fileContent, { | ||
schema: yaml.JSON_SCHEMA, | ||
}); | ||
}, | ||
}; | ||
const File2Loader = { | ||
default: Loaders.yaml, | ||
"package.json": Loaders.package, | ||
".prettierrc": Loaders.yaml, | ||
".prettierrc.yml": Loaders.yaml, | ||
".prettierrc.yaml": Loaders.yaml, | ||
".prettierrc.json": Loaders.json, | ||
".prettierrc.jsonc": Loaders.jsonc, | ||
".prettierrc.json5": Loaders.jsonc, | ||
".prettierrc.js": Loaders.js, | ||
".prettierrc.cjs": Loaders.js, | ||
".prettierrc.mjs": Loaders.js, | ||
"prettier.config.js": Loaders.js, | ||
"prettier.config.cjs": Loaders.js, | ||
"prettier.config.mjs": Loaders.js, | ||
}; | ||
const getPrettierConfig = (folderPath, fileName) => { | ||
const filePath = fastJoinedPath(folderPath, fileName); | ||
if (!Known.hasFilePath(filePath)) | ||
return; | ||
const loader = File2Loader[fileName] || File2Loader["default"]; | ||
const normalize = (config) => (isObject(config) ? normalizePrettierOptions(config, folderPath) : undefined); | ||
return loader(filePath).then(normalize).catch(noop); | ||
}; | ||
const getPrettierConfigs = memoize(async (folderPath, filesNames) => { | ||
const configsRaw = await Promise.all(filesNames.map((fileName) => getPrettierConfig(folderPath, fileName))); | ||
const configs = configsRaw.filter(isTruthy); | ||
if (!configs.length) | ||
return; | ||
return configs; | ||
}); | ||
const getPrettierConfigsMap = async (foldersPaths) => { | ||
const configs = await Promise.all(foldersPaths.map(getPrettierConfig)); | ||
const map = zipObject(foldersPaths, configs); | ||
const getPrettierConfigsMap = async (foldersPaths, filesNames) => { | ||
const configs = await Promise.all(foldersPaths.map((folderPath) => getPrettierConfigs(folderPath, filesNames))); | ||
const map = zipObjectUnless(foldersPaths, configs, isUndefined); | ||
return map; | ||
}; | ||
const getPrettierConfigsUp = memoize(async (folderPath) => { | ||
const config = await getPrettierConfig(folderPath); | ||
const getPrettierConfigsUp = memoize(async (folderPath, filesNames) => { | ||
const config = (await getPrettierConfigs(folderPath, filesNames))?.[0]; | ||
const folderPathUp = path.dirname(folderPath); | ||
const configsUp = folderPath !== folderPathUp ? await getPrettierConfigsUp(folderPathUp) : []; | ||
const configsUp = folderPath !== folderPathUp ? await getPrettierConfigsUp(folderPathUp, filesNames) : []; | ||
const configs = config ? [...configsUp, config] : configsUp; | ||
return configs; | ||
}); | ||
const getPrettierConfigResolved = async (filePath) => { | ||
const getPrettierConfigResolved = async (filePath, filesNames) => { | ||
const folderPath = path.dirname(filePath); | ||
const configs = await getPrettierConfigsUp(folderPath); | ||
const configs = await getPrettierConfigsUp(folderPath, filesNames); | ||
let resolved = {}; | ||
@@ -89,0 +87,0 @@ for (let ci = 0, cl = configs.length; ci < cl; ci++) { |
declare const PRETTIER_VERSION: any; | ||
declare const CLI_VERSION = "0.1.2"; | ||
export { PRETTIER_VERSION, CLI_VERSION }; | ||
declare const CLI_VERSION: any; | ||
declare const IS_BUN: boolean; | ||
export { PRETTIER_VERSION, CLI_VERSION, IS_BUN }; |
import { createRequire } from "node:module"; | ||
const require = createRequire(import.meta.url); | ||
const pkg = require("prettier/package.json"); | ||
const PRETTIER_VERSION = pkg.version; | ||
const CLI_VERSION = "0.1.2"; //TODO: Hard-coding this is error-prone | ||
export { PRETTIER_VERSION, CLI_VERSION }; | ||
const prettierPackage = require("prettier/package.json"); | ||
const cliPackage = require("@prettier/cli/package.json"); | ||
const PRETTIER_VERSION = prettierPackage.version; | ||
const CLI_VERSION = cliPackage.version; | ||
const IS_BUN = !!process.versions["bun"]; | ||
export { PRETTIER_VERSION, CLI_VERSION, IS_BUN }; |
@@ -1,3 +0,3 @@ | ||
import type { Options } from "./types.js"; | ||
declare function run(options: Options): Promise<void>; | ||
import type { Options, PluginsOptions } from "./types.js"; | ||
declare function run(options: Options, pluginsOptions: PluginsOptions): Promise<void>; | ||
export { run }; |
import isBinaryPath from "is-binary-path"; | ||
import stringify from "json-sorted-stringify"; | ||
import path from "node:path"; | ||
import process from "node:process"; | ||
@@ -12,5 +13,5 @@ import Cache from "./cache.js"; | ||
import { makePrettier } from "./prettier.js"; | ||
import { getExpandedFoldersPaths, getFoldersChildrenPaths, getProjectPath, getTargetsPaths } from "./utils.js"; | ||
import { getExpandedFoldersPaths, getFoldersChildrenPaths, getPluginsVersions, getProjectPath, getTargetsPaths } from "./utils.js"; | ||
import { fastRelativePath, isString, isUndefined, negate, pluralize } from "./utils.js"; | ||
async function run(options) { | ||
async function run(options, pluginsOptions) { | ||
const logger = new Logger(options.logLevel); | ||
@@ -21,3 +22,3 @@ const spinner = options.check ? logger.spinner.log() : undefined; | ||
const projectPath = getProjectPath(rootPath); | ||
const [filesPaths, filesFoundPaths, foldersFoundPaths] = await getTargetsPaths(rootPath, options.globs); | ||
const [filesPaths, filesNames, filesFoundPaths, foldersFoundPaths] = await getTargetsPaths(rootPath, options.globs); | ||
const filesPathsTargets = filesPaths.filter(negate(isBinaryPath)).sort(); | ||
@@ -27,25 +28,31 @@ const [foldersPathsTargetsUnsorted, foldersExtraPaths] = getExpandedFoldersPaths(foldersFoundPaths, projectPath); | ||
const filesExtraPaths = await getFoldersChildrenPaths([rootPath, ...foldersExtraPaths]); | ||
Known.addFiles(filesFoundPaths); | ||
Known.addFiles(filesExtraPaths); | ||
Known.addFolders(foldersFoundPaths); | ||
Known.addFolders(foldersExtraPaths); | ||
const filesExtraNames = filesExtraPaths.map((filePath) => path.basename(filePath)); | ||
Known.addFilesPaths(filesFoundPaths); | ||
Known.addFilesPaths(filesExtraPaths); | ||
Known.addFilesNames(filesNames); | ||
Known.addFilesNames(filesExtraNames); | ||
const prettierVersion = PRETTIER_VERSION; | ||
const cliVersion = CLI_VERSION; | ||
const pluginsVersions = ""; //TODO | ||
const editorConfigs = options.editorConfig ? await getEditorConfigsMap(foldersPathsTargets) : {}; | ||
const ignoreContents = await getIgnoresContentMap(foldersPathsTargets); | ||
const prettierConfigs = options.config ? await getPrettierConfigsMap(foldersPathsTargets) : {}; | ||
const cliConfig = options.formatOptions; | ||
const cacheVersion = stringify({ prettierVersion, cliVersion, pluginsVersions, editorConfigs, ignoreContents, prettierConfigs, cliConfig }); // prettier-ignore | ||
Known.reset(); | ||
const cache = new Cache(cacheVersion, projectPath, options, logger); | ||
const pluginsNames = options.formatOptions.plugins || []; | ||
const pluginsVersions = getPluginsVersions(pluginsNames); | ||
const editorConfigNames = [".editorconfig"].filter(Known.hasFileName); | ||
const ignoreNames = [".gitignore", ".prettierignore"].filter(Known.hasFileName); | ||
const prettierConfigNames = ["package.json", ".prettierrc", ".prettierrc.yml", ".prettierrc.yaml", ".prettierrc.json", ".prettierrc.jsonc", ".prettierrc.json5", ".prettierrc.js", "prettier.config.js", ".prettierrc.cjs", "prettier.config.cjs", ".prettierrc.mjs", "prettier.config.mjs"].filter(Known.hasFileName); // prettier-ignore | ||
const editorConfigs = options.editorConfig ? await getEditorConfigsMap(foldersPathsTargets, editorConfigNames) : {}; | ||
const ignoreContents = await getIgnoresContentMap(foldersPathsTargets, ignoreNames); | ||
const prettierConfigs = options.config ? await getPrettierConfigsMap(foldersPathsTargets, prettierConfigNames) : {}; | ||
const cliContextConfig = options.contextOptions; | ||
const cliFormatConfig = options.formatOptions; | ||
const cacheVersion = stringify({ prettierVersion, cliVersion, pluginsNames, pluginsVersions, editorConfigs, prettierConfigs, cliContextConfig, cliFormatConfig, pluginsOptions }); // prettier-ignore | ||
const shouldCache = isUndefined(cliContextConfig.cursorOffset); | ||
const cache = shouldCache ? new Cache(cacheVersion, projectPath, options, logger) : undefined; | ||
const prettier = await makePrettier(options, cache); | ||
//TODO: Maybe do work in chunks here, as keeping too many formatted files in memory can be a problem | ||
const filesResults = await Promise.allSettled(filesPathsTargets.map(async (filePath) => { | ||
const ignored = await getIgnoreResolved(filePath); | ||
const ignored = await getIgnoreResolved(filePath, ignoreNames); | ||
if (ignored) | ||
return; | ||
const getFormatOptions = async () => { | ||
const editorConfig = options.editorConfig ? getEditorConfigFormatOptions(await getEditorConfigResolved(filePath)) : {}; // prettier-ignore | ||
const prettierConfig = options.config ? await getPrettierConfigResolved(filePath) : {}; | ||
const editorConfig = options.editorConfig ? getEditorConfigFormatOptions(await getEditorConfigResolved(filePath, editorConfigNames)) : {}; | ||
const prettierConfig = options.config ? await getPrettierConfigResolved(filePath, prettierConfigNames) : {}; | ||
const formatOptions = { ...editorConfig, ...prettierConfig, ...options.formatOptions }; | ||
@@ -56,9 +63,9 @@ return formatOptions; | ||
if (options.check || options.list) { | ||
return await prettier.checkWithPath(filePath, getFormatOptions); | ||
return await prettier.checkWithPath(filePath, getFormatOptions, cliContextConfig, pluginsOptions); | ||
} | ||
else if (options.write) { | ||
return await prettier.writeWithPath(filePath, getFormatOptions); | ||
return await prettier.writeWithPath(filePath, getFormatOptions, cliContextConfig, pluginsOptions); | ||
} | ||
else { | ||
return await prettier.formatWithPath(filePath, getFormatOptions); | ||
return await prettier.formatWithPath(filePath, getFormatOptions, cliContextConfig, pluginsOptions); | ||
} | ||
@@ -71,3 +78,4 @@ } | ||
spinner?.stop("Checking formatting..."); | ||
let totalFound = filesResults.length; | ||
let totalMatched = filesResults.length; | ||
let totalIgnored = 0; | ||
let totalFormatted = 0; | ||
@@ -80,4 +88,4 @@ let totalUnformatted = 0; | ||
if (isUndefined(fileResult.value)) { | ||
totalFound -= 1; | ||
continue; | ||
totalMatched -= 1; | ||
totalIgnored += 1; | ||
} | ||
@@ -120,3 +128,9 @@ else if (isString(fileResult.value)) { | ||
} | ||
if (!totalFound) { | ||
logger.prefixed.debug(`Files found: ${totalMatched + totalIgnored}`); | ||
logger.prefixed.debug(`Files matched: ${totalMatched}`); | ||
logger.prefixed.debug(`Files ignored: ${totalIgnored}`); | ||
logger.prefixed.debug(`Files formatted: ${totalFormatted}`); | ||
logger.prefixed.debug(`Files unformatted: ${totalUnformatted}`); | ||
logger.prefixed.debug(`Files errored: ${totalErrored}`); | ||
if (!totalMatched) { | ||
if (options.errorOnUnmatchedPattern) { | ||
@@ -133,5 +147,3 @@ logger.prefixed.error(`No files matching the given patterns were found`); | ||
// if (options.check) { | ||
// logger.prefixed.error( | ||
// `Parsing issues found in ${totalErrored} ${pluralize("file", totalErrored)}. Check files to fix.`, | ||
// ); | ||
// logger.prefixed.error(`Parsing issues found in ${totalErrored} ${pluralize("file", totalErrored)}. Check files to fix.`); | ||
// } | ||
@@ -144,5 +156,5 @@ // } | ||
} | ||
cache.write(); | ||
process.exitCode = (!totalFound && options.errorOnUnmatchedPattern) || totalErrored || (totalUnformatted && !options.write) ? 1 : 0; // prettier-ignore | ||
cache?.write(); | ||
process.exitCode = (!totalMatched && options.errorOnUnmatchedPattern) || totalErrored || (totalUnformatted && !options.write) ? 1 : 0; | ||
} | ||
export { run }; |
declare class Known { | ||
private files; | ||
private folders; | ||
addFiles(filesPaths: Array<string> | Set<string>): void; | ||
addFolders(folderPaths: Array<string> | Set<string>): void; | ||
hasFile(filePath: string): boolean; | ||
hasFolder(folderPath: string): boolean; | ||
reset(): void; | ||
private filesPaths; | ||
private filesNames; | ||
addFilesPaths: (filesPaths: Array<string> | Set<string>) => void; | ||
addFilesNames: (filesNames: Array<string> | Set<string>) => void; | ||
hasFilePath: (filePath: string) => boolean; | ||
hasFileName: (fileName: string) => boolean; | ||
reset: () => void; | ||
} | ||
declare const _default: Known; | ||
export default _default; |
//FIXME: This shouldn't be a singleton, but passing it through the whole CLI is a bit of a pain right now | ||
class Known { | ||
constructor() { | ||
this.files = new Set(); | ||
this.folders = new Set(); | ||
} | ||
addFiles(filesPaths) { | ||
if (!this.files.size) { | ||
this.files = new Set(filesPaths); | ||
} | ||
else { | ||
for (const filePath of filesPaths) { | ||
this.files.add(filePath); | ||
this.filesPaths = new Set(); | ||
this.filesNames = new Set(); | ||
this.addFilesPaths = (filesPaths) => { | ||
if (!this.filesPaths.size) { | ||
this.filesPaths = new Set(filesPaths); | ||
} | ||
} | ||
} | ||
addFolders(folderPaths) { | ||
if (!this.folders.size) { | ||
this.folders = new Set(folderPaths); | ||
} | ||
else { | ||
for (const folderPath of folderPaths) { | ||
this.folders.add(folderPath); | ||
else { | ||
for (const filePath of filesPaths) { | ||
this.filesPaths.add(filePath); | ||
} | ||
} | ||
} | ||
}; | ||
this.addFilesNames = (filesNames) => { | ||
if (!this.filesNames.size) { | ||
this.filesNames = new Set(filesNames); | ||
} | ||
else { | ||
for (const fileName of filesNames) { | ||
this.filesNames.add(fileName); | ||
} | ||
} | ||
}; | ||
this.hasFilePath = (filePath) => { | ||
return this.filesPaths.has(filePath); | ||
}; | ||
this.hasFileName = (fileName) => { | ||
return this.filesNames.has(fileName); | ||
}; | ||
this.reset = () => { | ||
this.filesPaths = new Set(); | ||
this.filesNames = new Set(); | ||
}; | ||
} | ||
hasFile(filePath) { | ||
return this.files.has(filePath); | ||
} | ||
hasFolder(folderPath) { | ||
return this.folders.has(folderPath); | ||
} | ||
reset() { | ||
this.files = new Set(); | ||
this.folders = new Set(); | ||
} | ||
} | ||
export default new Known(); |
@@ -8,3 +8,3 @@ import { readFile, writeFile } from "atomically"; | ||
write: prettier.write, | ||
async checkWithPath(filePath, formatOptions) { | ||
async checkWithPath(filePath, formatOptions, contextOptions, pluginsOptions) { | ||
const data = await cache.get(filePath); | ||
@@ -14,7 +14,7 @@ if (isBoolean(data?.formatted)) | ||
const fileContent = data?.content?.toString() ?? (await readFile(filePath, "utf8")); | ||
const formatted = await prettier.check(filePath, fileContent, formatOptions); | ||
const formatted = await prettier.check(filePath, fileContent, formatOptions, contextOptions, pluginsOptions); | ||
await data?.save(formatted, fileContent); | ||
return formatted; | ||
}, | ||
async formatWithPath(filePath, formatOptions) { | ||
async formatWithPath(filePath, formatOptions, contextOptions, pluginsOptions) { | ||
const data = await cache.get(filePath); | ||
@@ -24,3 +24,3 @@ const fileContent = data?.content?.toString() ?? (await readFile(filePath, "utf8")); | ||
return fileContent; | ||
const fileContentFormatted = await prettier.format(filePath, fileContent, formatOptions); | ||
const fileContentFormatted = await prettier.format(filePath, fileContent, formatOptions, contextOptions, pluginsOptions); | ||
if (fileContent === fileContentFormatted) { | ||
@@ -35,3 +35,3 @@ await data?.save(true, fileContent); | ||
}, | ||
async writeWithPath(filePath, formatOptions) { | ||
async writeWithPath(filePath, formatOptions, contextOptions, pluginsOptions) { | ||
const data = await cache.get(filePath); | ||
@@ -41,3 +41,3 @@ if (data?.formatted) | ||
const fileContent = data?.content?.toString() ?? (await readFile(filePath, "utf8")); | ||
const fileContentFormatted = await prettier.format(filePath, fileContent, formatOptions); | ||
const fileContentFormatted = await prettier.format(filePath, fileContent, formatOptions, contextOptions, pluginsOptions); | ||
if (fileContent === fileContentFormatted) { | ||
@@ -44,0 +44,0 @@ await data?.save(true, fileContent); |
@@ -9,21 +9,22 @@ import os from "node:os"; | ||
methods: new URL("./prettier_serial.js", import.meta.url), | ||
warmup: true, | ||
}); | ||
return { | ||
async check(filePath, fileContent, formatOptions) { | ||
return pool.exec("check", [filePath, fileContent, await resolve(formatOptions)]); | ||
async check(filePath, fileContent, formatOptions, contextOptions, pluginsOptions) { | ||
return pool.exec("check", [filePath, fileContent, await resolve(formatOptions), contextOptions, pluginsOptions]); | ||
}, | ||
async checkWithPath(filePath, formatOptions) { | ||
return pool.exec("checkWithPath", [filePath, await resolve(formatOptions)]); | ||
async checkWithPath(filePath, formatOptions, contextOptions, pluginsOptions) { | ||
return pool.exec("checkWithPath", [filePath, await resolve(formatOptions), contextOptions, pluginsOptions]); | ||
}, | ||
async format(filePath, fileContent, formatOptions) { | ||
return pool.exec("format", [filePath, fileContent, await resolve(formatOptions)]); | ||
async format(filePath, fileContent, formatOptions, contextOptions, pluginsOptions) { | ||
return pool.exec("format", [filePath, fileContent, await resolve(formatOptions), contextOptions, pluginsOptions]); | ||
}, | ||
async formatWithPath(filePath, formatOptions) { | ||
return pool.exec("formatWithPath", [filePath, await resolve(formatOptions)]); | ||
async formatWithPath(filePath, formatOptions, contextOptions, pluginsOptions) { | ||
return pool.exec("formatWithPath", [filePath, await resolve(formatOptions), contextOptions, pluginsOptions]); | ||
}, | ||
async write(filePath, fileContent, formatOptions) { | ||
return pool.exec("write", [filePath, fileContent, await resolve(formatOptions)]); | ||
async write(filePath, fileContent, formatOptions, contextOptions, pluginsOptions) { | ||
return pool.exec("write", [filePath, fileContent, await resolve(formatOptions), contextOptions, pluginsOptions]); | ||
}, | ||
async writeWithPath(filePath, formatOptions) { | ||
return pool.exec("writeWithPath", [filePath, await resolve(formatOptions)]); | ||
async writeWithPath(filePath, formatOptions, contextOptions, pluginsOptions) { | ||
return pool.exec("writeWithPath", [filePath, await resolve(formatOptions), contextOptions, pluginsOptions]); | ||
}, | ||
@@ -30,0 +31,0 @@ }; |
@@ -1,8 +0,8 @@ | ||
import type { LazyFormatOptions } from "./types.js"; | ||
declare function check(filePath: string, fileContent: string, formatOptions: LazyFormatOptions): Promise<boolean>; | ||
declare function checkWithPath(filePath: string, formatOptions: LazyFormatOptions): Promise<boolean>; | ||
declare function format(filePath: string, fileContent: string, formatOptions: LazyFormatOptions): Promise<string>; | ||
declare function formatWithPath(filePath: string, formatOptions: LazyFormatOptions): Promise<string>; | ||
declare function write(filePath: string, fileContent: string, formatOptions: LazyFormatOptions): Promise<boolean>; | ||
declare function writeWithPath(filePath: string, formatOptions: LazyFormatOptions): Promise<boolean>; | ||
import type { ContextOptions, LazyFormatOptions, PluginsOptions } from "./types.js"; | ||
declare function check(filePath: string, fileContent: string, formatOptions: LazyFormatOptions, contextOptions: ContextOptions, pluginsOptions: PluginsOptions): Promise<boolean>; | ||
declare function checkWithPath(filePath: string, formatOptions: LazyFormatOptions, contextOptions: ContextOptions, pluginsOptions: PluginsOptions): Promise<boolean>; | ||
declare function format(filePath: string, fileContent: string, formatOptions: LazyFormatOptions, contextOptions: ContextOptions, pluginsOptions: PluginsOptions): Promise<string>; | ||
declare function formatWithPath(filePath: string, formatOptions: LazyFormatOptions, contextOptions: ContextOptions, pluginsOptions: PluginsOptions): Promise<string>; | ||
declare function write(filePath: string, fileContent: string, formatOptions: LazyFormatOptions, contextOptions: ContextOptions, pluginsOptions: PluginsOptions): Promise<boolean>; | ||
declare function writeWithPath(filePath: string, formatOptions: LazyFormatOptions, contextOptions: ContextOptions, pluginsOptions: PluginsOptions): Promise<boolean>; | ||
export { check, checkWithPath, format, formatWithPath, write, writeWithPath }; |
import { readFile, writeFile } from "atomically"; | ||
import process from "node:process"; | ||
import prettier from "prettier/standalone"; | ||
@@ -16,15 +17,19 @@ import prettierAcorn from "prettier/plugins/acorn"; | ||
import prettierYaml from "prettier/plugins/yaml"; | ||
import { resolve } from "./utils.js"; | ||
import { getPlugins, resolve } from "./utils.js"; | ||
//TODO: Avoid loading plugins until they are actually needed | ||
async function check(filePath, fileContent, formatOptions) { | ||
const fileContentFormatted = await format(filePath, fileContent, formatOptions); | ||
async function check(filePath, fileContent, formatOptions, contextOptions, pluginsOptions) { | ||
const fileContentFormatted = await format(filePath, fileContent, formatOptions, contextOptions, pluginsOptions); | ||
return fileContent === fileContentFormatted; | ||
} | ||
async function checkWithPath(filePath, formatOptions) { | ||
async function checkWithPath(filePath, formatOptions, contextOptions, pluginsOptions) { | ||
const fileContent = await readFile(filePath, "utf8"); | ||
return check(filePath, fileContent, formatOptions); | ||
return check(filePath, fileContent, formatOptions, contextOptions, pluginsOptions); | ||
} | ||
async function format(filePath, fileContent, formatOptions) { | ||
return prettier.format(fileContent, { | ||
...(await resolve(formatOptions)), | ||
async function format(filePath, fileContent, formatOptions, contextOptions, pluginsOptions) { | ||
formatOptions = await resolve(formatOptions); | ||
const plugins = await getPlugins(formatOptions.plugins || []); | ||
const options = { | ||
...formatOptions, | ||
...pluginsOptions, | ||
...contextOptions, | ||
filepath: filePath, | ||
@@ -45,11 +50,17 @@ plugins: [ | ||
prettierYaml, | ||
...plugins, | ||
], | ||
}); | ||
}; | ||
const result = await prettier.formatWithCursor(fileContent, options); //FIXME: Prettier's own types are incorrect here | ||
if (result.cursorOffset >= 0) { | ||
process.stderr.write(`${result.cursorOffset}\n`); //TODO: This should be implemented differently, pretty ugly doing it like this | ||
} | ||
return result.formatted; | ||
} | ||
async function formatWithPath(filePath, formatOptions) { | ||
async function formatWithPath(filePath, formatOptions, contextOptions, pluginsOptions) { | ||
const fileContent = await readFile(filePath, "utf8"); | ||
return format(filePath, fileContent, formatOptions); | ||
return format(filePath, fileContent, formatOptions, contextOptions, pluginsOptions); | ||
} | ||
async function write(filePath, fileContent, formatOptions) { | ||
const fileContentFormatted = await format(filePath, fileContent, formatOptions); | ||
async function write(filePath, fileContent, formatOptions, contextOptions, pluginsOptions) { | ||
const fileContentFormatted = await format(filePath, fileContent, formatOptions, contextOptions, pluginsOptions); | ||
if (fileContent === fileContentFormatted) | ||
@@ -60,6 +71,6 @@ return true; | ||
} | ||
async function writeWithPath(filePath, formatOptions) { | ||
async function writeWithPath(filePath, formatOptions, contextOptions, pluginsOptions) { | ||
const fileContent = await readFile(filePath, "utf8"); | ||
return write(filePath, fileContent, formatOptions); | ||
return write(filePath, fileContent, formatOptions, contextOptions, pluginsOptions); | ||
} | ||
export { check, checkWithPath, format, formatWithPath, write, writeWithPath }; |
import type Cache from "./cache.js"; | ||
import type { Options, Prettier } from "./types.js"; | ||
declare function makePrettier(options: Options, cache: Cache): Promise<Prettier>; | ||
declare function makePrettier(options: Options, cache?: Cache): Promise<Prettier>; | ||
export { makePrettier }; |
@@ -5,3 +5,3 @@ import { makeCached } from "./prettier_cached.js"; | ||
const { makeParallel } = await import("./prettier_parallel.js"); | ||
if (options.cache) { | ||
if (options.cache && cache) { | ||
return makeCached(options, cache, makeParallel(options)); | ||
@@ -15,3 +15,3 @@ } | ||
const { makeLazy } = await import("./prettier_lazy.js"); | ||
if (options.cache) { | ||
if (options.cache && cache) { | ||
return makeCached(options, cache, makeLazy(options)); | ||
@@ -18,0 +18,0 @@ } |
@@ -0,2 +1,9 @@ | ||
type Bin = ReturnType<typeof import("tiny-bin").default>; | ||
type ContextOptions = { | ||
cursorOffset?: number; | ||
rangeEnd?: number; | ||
rangeStart?: number; | ||
}; | ||
type FormatOptions = { | ||
experimentalTernaries?: boolean; | ||
arrowParens?: "avoid" | "always"; | ||
@@ -11,2 +18,3 @@ bracketSameLine?: boolean; | ||
parser?: "flow" | "babel" | "babel-flow" | "babel-ts" | "typescript" | "acorn" | "espree" | "meriyah" | "css" | "less" | "scss" | "json" | "json5" | "json-stringify" | "graphql" | "markdown" | "mdx" | "vue" | "yaml" | "glimmer" | "html" | "angular" | "lwc"; | ||
plugins?: string[]; | ||
printWidth?: number; | ||
@@ -26,2 +34,3 @@ proseWrap?: "always" | "never" | "preserve"; | ||
type Ignore = (filePath: string) => boolean; | ||
type Key = string | number | symbol; | ||
type LazyFormatOptions = FunctionMaybe<PromiseMaybe<FormatOptions>>; | ||
@@ -43,4 +52,8 @@ type LogLevel = "error" | "warn" | "log" | "debug" | "silent"; | ||
parallelWorkers: number; | ||
contextOptions: ContextOptions; | ||
formatOptions: FormatOptions; | ||
}; | ||
type PluginsOptions = { | ||
[option: string]: unknown; | ||
}; | ||
type Prettier = typeof import("./prettier_serial.js"); | ||
@@ -56,3 +69,4 @@ type PrettierConfig = FormatOptions; | ||
}; | ||
type PrettierPlugin = import("prettier").Plugin; | ||
type PromiseMaybe<T> = T | Promise<T>; | ||
export type { FormatOptions, FunctionMaybe, Ignore, LazyFormatOptions, LogLevel, Options, Prettier, PrettierConfig, PrettierConfigWithOverrides, PromiseMaybe, }; | ||
export type { Bin, ContextOptions, FormatOptions, FunctionMaybe, Ignore, Key, LazyFormatOptions, LogLevel, Options, PluginsOptions, Prettier, PrettierConfig, PrettierConfigWithOverrides, PrettierPlugin, PromiseMaybe, }; |
@@ -1,2 +0,6 @@ | ||
import type { FormatOptions, FunctionMaybe, Options, PrettierConfigWithOverrides } from "./types.js"; | ||
import type { FormatOptions, FunctionMaybe, Key, Options, PrettierConfigWithOverrides, PrettierPlugin } from "./types.js"; | ||
import type { PluginsOptions, PromiseMaybe } from "./types.js"; | ||
declare function memoize<Args extends unknown[], Return>(fn: (...args: Args) => Return): ((...args: Args) => Return) & { | ||
cache: Map<Args[0], Return>; | ||
}; | ||
declare function castArray<T>(value: T | T[]): T[]; | ||
@@ -13,2 +17,10 @@ declare function everyOf<T>(fns: ((arg: T) => unknown)[]): (arg: T) => boolean; | ||
declare function getGlobPaths(rootPath: string, globs: string[]): Promise<import("tiny-readdir-glob/dist/types.js").Result>; | ||
declare const getPlugin: ((name: string) => Promise<import("prettier").Plugin<any>>) & { | ||
cache: Map<string, Promise<import("prettier").Plugin<any>>>; | ||
}; | ||
declare function getPluginPath(name: string): string; | ||
declare function getPluginVersion(name: string): string; | ||
declare function getPlugins(names: string[]): PromiseMaybe<PrettierPlugin[]>; | ||
declare function getPluginsPaths(names: string[]): string[]; | ||
declare function getPluginsVersions(names: string[]): string[]; | ||
declare function getProjectPath(rootPath: string): string; | ||
@@ -18,2 +30,3 @@ declare function getTargetsPaths(rootPath: string, globs: string[]): Promise<string[][]>; | ||
declare function isBoolean(value: unknown): value is boolean; | ||
declare function isFalsy<T>(value: T): value is Extract<T, 0 | -0 | 0n | -0n | "" | false | null | undefined | void>; | ||
declare function isFunction(value: unknown): value is Function; | ||
@@ -25,9 +38,9 @@ declare function isInteger(value: unknown): value is number; | ||
declare function isString(value: unknown): value is string; | ||
declare function isTruthy<T>(value: T): value is Exclude<T, 0 | -0 | 0n | -0n | "" | false | null | undefined | void>; | ||
declare function isUndefined(value: unknown): value is undefined; | ||
declare function memoize<T, Return>(fn: (arg: T) => Return): ((arg: T) => Return) & { | ||
cache: Map<T, Return>; | ||
}; | ||
declare function negate<T extends unknown[]>(fn: (...args: T) => boolean): (...args: T) => boolean; | ||
declare function noop(): undefined; | ||
declare function normalizeOptions(options: unknown, targets: unknown[]): Options; | ||
declare function normalizeFormatOptions(options: unknown): FormatOptions; | ||
declare function normalizePluginOptions(options: unknown, names: string[]): PluginsOptions; | ||
declare function normalizePrettierOptions(options: unknown, folderPath: string): PrettierConfigWithOverrides; | ||
@@ -41,3 +54,4 @@ declare function omit<T extends object, K extends keyof T>(object: T, keys: K[]): Omit<T, K>; | ||
declare function someOf<T>(fns: ((arg: T) => unknown)[]): (arg: T) => boolean; | ||
declare function zipObject<T extends number | string | symbol, U>(keys: T[], values: U[]): Partial<Record<T, U>>; | ||
export { castArray, everyOf, fastJoinedPath, fastRelativePath, fastRelativeChildPath, findLastIndex, getCachePath, getFolderChildrenPaths, getFoldersChildrenPaths, getExpandedFoldersPaths, getGlobPaths, getProjectPath, getTargetsPaths, isArray, isBoolean, isFunction, isInteger, isNumber, isObject, isPromise, isString, isUndefined, memoize, negate, normalizeOptions, normalizeFormatOptions, normalizePrettierOptions, omit, once, pluralize, resolve, sha1hex, sha1base64, someOf, zipObject, }; | ||
declare function zipObject<T extends Key, U>(keys: T[], values: U[]): Partial<Record<T, U>>; | ||
declare function zipObjectUnless<T extends Key, U>(keys: T[], values: U[], unless: (value: U) => boolean): Partial<Record<T, U>>; | ||
export { castArray, everyOf, fastJoinedPath, fastRelativePath, fastRelativeChildPath, findLastIndex, getCachePath, getFolderChildrenPaths, getFoldersChildrenPaths, getExpandedFoldersPaths, getGlobPaths, getPlugin, getPluginPath, getPluginVersion, getPlugins, getPluginsPaths, getPluginsVersions, getProjectPath, getTargetsPaths, isArray, isBoolean, isFalsy, isFunction, isInteger, isNumber, isObject, isPromise, isString, isTruthy, isUndefined, memoize, negate, noop, normalizeOptions, normalizeFormatOptions, normalizePluginOptions, normalizePrettierOptions, omit, once, pluralize, resolve, sha1hex, sha1base64, someOf, zipObject, zipObjectUnless, }; |
@@ -0,7 +1,27 @@ | ||
import findUp from "find-up-json"; | ||
import { moduleResolve } from "import-meta-resolve"; | ||
import crypto from "node:crypto"; | ||
import fs from "node:fs"; | ||
import path from "node:path"; | ||
import process from "node:process"; | ||
import url from "node:url"; | ||
import { exit } from "specialist"; | ||
import readdir from "tiny-readdir-glob"; | ||
import zeptomatch from "zeptomatch"; | ||
//FIXME: Ensure all arguments are actually taken into account | ||
//TODO: Publish something like this as a standalone module, rather than manually hoisting this up | ||
function memoize(fn) { | ||
const memoized = (...args) => { | ||
const { cache } = memoized; | ||
const id = args[0]; | ||
const cached = cache.get(id); | ||
if (!isUndefined(cached) || cache.has(id)) | ||
return cached; | ||
const result = fn(...args); | ||
cache.set(id, result); | ||
return result; | ||
}; | ||
memoized.cache = new Map(); | ||
return memoized; | ||
} | ||
function castArray(value) { | ||
@@ -79,5 +99,39 @@ return isArray(value) ? value : [value]; | ||
followSymlinks: false, | ||
ignore: "**/{.git,.sl,.svn,.hg,node_modules}", | ||
ignore: "**/{.git,.sl,.svn,.hg,node_modules,.DS_Store,Thumbs.db}", | ||
}); | ||
} | ||
const getPlugin = memoize(async (name) => { | ||
const pluginPath = getPluginPath(name); | ||
const pluginExports = await import(pluginPath); | ||
const plugin = pluginExports.default || pluginExports; | ||
return plugin; | ||
}); | ||
function getPluginPath(name) { | ||
const rootPath = path.join(process.cwd(), "index.js"); | ||
const rootUrl = url.pathToFileURL(rootPath); | ||
const pluginUrl = moduleResolve(name, rootUrl); | ||
const pluginPath = url.fileURLToPath(pluginUrl); | ||
return pluginPath; | ||
} | ||
function getPluginVersion(name) { | ||
const pluginPath = getPluginPath(name); | ||
const parentPath = path.dirname(pluginPath); | ||
const pkg = findUp("package.json", parentPath); | ||
if (!pkg || !pkg.content.version) | ||
throw new Error(`Version not found for plugin: "${name}"`); | ||
return pkg.content.version; | ||
} | ||
function getPlugins(names) { | ||
if (!names.length) | ||
return []; | ||
return Promise.all(names.map(getPlugin)); | ||
} | ||
function getPluginsPaths(names) { | ||
const pluginsPaths = names.map(getPluginPath); | ||
return pluginsPaths; | ||
} | ||
function getPluginsVersions(names) { | ||
const pluginsVersions = names.map(getPluginVersion); | ||
return pluginsVersions; | ||
} | ||
function getProjectPath(rootPath) { | ||
@@ -111,2 +165,3 @@ function isProjectPath(folderPath) { | ||
const targetFiles = []; | ||
const targetFilesNames = []; | ||
const targetGlobs = []; | ||
@@ -117,2 +172,3 @@ for (const glob of globs) { | ||
targetFiles.push(filePath); | ||
targetFilesNames.push(path.basename(filePath)); | ||
} | ||
@@ -125,5 +181,6 @@ else { | ||
const filesPaths = [...targetFiles, ...result.files]; | ||
const filesNames = [...targetFilesNames, ...result.filesFoundNames]; | ||
const filesFoundPaths = result.filesFound; | ||
const foldersFoundPaths = [rootPath, ...result.directoriesFound]; | ||
return [filesPaths, filesFoundPaths, foldersFoundPaths]; | ||
return [filesPaths, filesNames, filesFoundPaths, foldersFoundPaths]; | ||
} | ||
@@ -136,2 +193,5 @@ function isArray(value) { | ||
} | ||
function isFalsy(value) { | ||
return !value; | ||
} | ||
function isFile(targetPath) { | ||
@@ -171,18 +231,8 @@ try { | ||
} | ||
function isTruthy(value) { | ||
return !!value; | ||
} | ||
function isUndefined(value) { | ||
return typeof value === "undefined"; | ||
} | ||
function memoize(fn) { | ||
const memoized = (arg) => { | ||
const { cache } = memoized; | ||
const cached = cache.get(arg); | ||
if (!isUndefined(cached) || cache.has(arg)) | ||
return cached; | ||
const result = fn(arg); | ||
cache.set(arg, result); | ||
return result; | ||
}; | ||
memoized.cache = new Map(); | ||
return memoized; | ||
} | ||
function negate(fn) { | ||
@@ -193,2 +243,5 @@ return (...args) => { | ||
} | ||
function noop() { | ||
return; | ||
} | ||
function normalizeOptions(options, targets) { | ||
@@ -198,3 +251,3 @@ if (!isObject(options)) | ||
const targetsGlobs = targets.filter(isString); | ||
const targetsStatic = "--" in options && Array.isArray(options["--"]) ? options["--"].filter(isString).map(zeptomatch.escape) : []; // prettier-ignore | ||
const targetsStatic = "--" in options && Array.isArray(options["--"]) ? options["--"].filter(isString).map(zeptomatch.escape) : []; | ||
const globs = [...targetsGlobs, ...targetsStatic]; | ||
@@ -215,8 +268,9 @@ if (!globs.length) | ||
const cache = "cache" in options ? !!options.cache : true; | ||
const cacheLocation = "cacheLocation" in options && isString(options.cacheLocation) ? options.cacheLocation : undefined; // prettier-ignore | ||
const cacheLocation = "cacheLocation" in options && isString(options.cacheLocation) ? options.cacheLocation : undefined; | ||
const errorOnUnmatchedPattern = "errorOnUnmatchedPattern" in options ? !!options.errorOnUnmatchedPattern : true; | ||
const ignoreUnknown = "ignoreUnknown" in options && isBoolean(options.ignoreUnknown) ? !!options.ignoreUnknown : globs.some(isGlobStatic); // prettier-ignore | ||
const ignoreUnknown = "ignoreUnknown" in options && isBoolean(options.ignoreUnknown) ? !!options.ignoreUnknown : globs.some(isGlobStatic); | ||
const logLevel = "logLevel" in options ? (options.logLevel || "log") : "log"; | ||
const parallel = "parallel" in options && !!options.parallel; | ||
const parallelWorkers = ("parallelWorkers" in options && Math.round(Number(options.parallelWorkers))) || 0; | ||
const contextOptions = normalizeContextOptions(options); | ||
const formatOptions = normalizeFormatOptions(options); | ||
@@ -237,5 +291,30 @@ return { | ||
parallelWorkers, | ||
contextOptions, | ||
formatOptions, | ||
}; | ||
} | ||
function normalizeContextOptions(options) { | ||
if (!isObject(options)) | ||
exit("Invalid options object"); | ||
const contextOptions = {}; | ||
if ("cursorOffset" in options) { | ||
const value = Number(options.cursorOffset); | ||
if (isInteger(value) && value >= 0) { | ||
contextOptions.cursorOffset = value; | ||
} | ||
} | ||
if ("rangeEnd" in options) { | ||
const value = Number(options.rangeEnd); | ||
if (isInteger(value) && value >= 0) { | ||
contextOptions.rangeEnd = value; | ||
} | ||
} | ||
if ("rangeStart" in options) { | ||
const value = Number(options.rangeStart); | ||
if (isInteger(value) && value >= 0) { | ||
contextOptions.rangeStart = value; | ||
} | ||
} | ||
return contextOptions; | ||
} | ||
function normalizeFormatOptions(options) { | ||
@@ -245,2 +324,8 @@ if (!isObject(options)) | ||
const formatOptions = {}; | ||
if ("experimentalTernaries" in options) { | ||
const value = options.experimentalTernaries; | ||
if (isBoolean(value)) { | ||
formatOptions.experimentalTernaries = value; | ||
} | ||
} | ||
if ("arrowParens" in options) { | ||
@@ -301,2 +386,15 @@ const value = options.arrowParens; | ||
} | ||
if ("plugin" in options) { | ||
const value = options.plugin; | ||
if (isArray(value) && value.every(isString)) { | ||
formatOptions.plugins = value; | ||
} | ||
else if (isString(value)) { | ||
formatOptions.plugins = [value]; | ||
} | ||
else if (!isUndefined(value)) { | ||
//TODO: Figure out what to do here, probably just bailing out of parallelization? | ||
exit("Non-string plugin specifiers are not supported yet"); | ||
} | ||
} | ||
if ("printWidth" in options) { | ||
@@ -370,2 +468,15 @@ const value = Number(options.printWidth); | ||
} | ||
function normalizePluginOptions(options, names) { | ||
if (!isObject(options)) | ||
exit("Invalid options object"); | ||
const config = {}; | ||
for (let i = 0, l = names.length; i < l; i++) { | ||
const name = names[i]; | ||
const value = options[name]; | ||
if (isUndefined(value)) | ||
continue; | ||
config[name] = value; | ||
} | ||
return config; | ||
} | ||
function normalizePrettierOptions(options, folderPath) { | ||
@@ -443,2 +554,12 @@ if (!isObject(options)) | ||
} | ||
export { castArray, everyOf, fastJoinedPath, fastRelativePath, fastRelativeChildPath, findLastIndex, getCachePath, getFolderChildrenPaths, getFoldersChildrenPaths, getExpandedFoldersPaths, getGlobPaths, getProjectPath, getTargetsPaths, isArray, isBoolean, isFunction, isInteger, isNumber, isObject, isPromise, isString, isUndefined, memoize, negate, normalizeOptions, normalizeFormatOptions, normalizePrettierOptions, omit, once, pluralize, resolve, sha1hex, sha1base64, someOf, zipObject, }; | ||
function zipObjectUnless(keys, values, unless) { | ||
const map = {}; | ||
for (let i = 0, l = keys.length; i < l; i++) { | ||
const value = values[i]; | ||
if (!unless(value)) { | ||
map[keys[i]] = value; | ||
} | ||
} | ||
return map; | ||
} | ||
export { castArray, everyOf, fastJoinedPath, fastRelativePath, fastRelativeChildPath, findLastIndex, getCachePath, getFolderChildrenPaths, getFoldersChildrenPaths, getExpandedFoldersPaths, getGlobPaths, getPlugin, getPluginPath, getPluginVersion, getPlugins, getPluginsPaths, getPluginsVersions, getProjectPath, getTargetsPaths, isArray, isBoolean, isFalsy, isFunction, isInteger, isNumber, isObject, isPromise, isString, isTruthy, isUndefined, memoize, negate, noop, normalizeOptions, normalizeFormatOptions, normalizePluginOptions, normalizePrettierOptions, omit, once, pluralize, resolve, sha1hex, sha1base64, someOf, zipObject, zipObjectUnless, }; |
@@ -6,3 +6,3 @@ { | ||
"license": "MIT", | ||
"version": "0.1.5", | ||
"version": "0.2.0", | ||
"type": "module", | ||
@@ -15,3 +15,4 @@ "bin": { | ||
".": "./dist/index.js", | ||
"./bin": "./dist/bin.js" | ||
"./bin": "./dist/bin.js", | ||
"./package.json": "./package.json" | ||
}, | ||
@@ -31,13 +32,16 @@ "files": [ | ||
"atomically": "^2.0.2", | ||
"ignore": "^5.3.0", | ||
"fast-ignore": "^1.1.1", | ||
"find-up-json": "^2.0.2", | ||
"import-meta-resolve": "^4.0.0", | ||
"is-binary-path": "^2.1.0", | ||
"js-yaml": "^4.1.0", | ||
"json-sorted-stringify": "^1.0.0", | ||
"kasi": "^1.1.0", | ||
"pioppo": "^1.1.0", | ||
"specialist": "^1.3.0", | ||
"specialist": "^1.4.0", | ||
"tiny-editorconfig": "^1.0.0", | ||
"tiny-jsonc": "^1.0.1", | ||
"tiny-readdir-glob": "^1.0.4", | ||
"tiny-readdir-glob": "^1.1.0", | ||
"tiny-spinner": "^2.0.3", | ||
"worktank": "^2.5.2", | ||
"worktank": "^2.6.0", | ||
"zeptomatch": "^1.2.2" | ||
@@ -44,0 +48,0 @@ }, |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
New author
Supply chain riskA new npm collaborator published a version of the package for the first time. New collaborators are usually benign additions to a project, but do indicate a change to the security surface area of a package.
Found 1 instance in 1 package
84182
1808
0
17
+ Addedfast-ignore@^1.1.1
+ Addedfind-up-json@^2.0.2
+ Addedimport-meta-resolve@^4.0.0
+ Addedkasi@^1.1.0
+ Addedfast-ignore@1.1.3(transitive)
+ Addedimport-meta-resolve@4.1.0(transitive)
+ Addedkasi@1.1.0(transitive)
+ Addedstring-escape-regex@1.0.0(transitive)
- Removedignore@^5.3.0
- Removedignore@5.3.2(transitive)
Updatedspecialist@^1.4.0
Updatedtiny-readdir-glob@^1.1.0
Updatedworktank@^2.6.0