diff --git a/src/cli/options/get-options-for-file.js b/src/cli/options/get-options-for-file.js index 1d971ed24..a11b03e26 100644 --- a/src/cli/options/get-options-for-file.js +++ b/src/cli/options/get-options-for-file.js @@ -1,6 +1,6 @@ import dashify from "dashify"; import { resolveConfig } from "../../index.js"; -import { normalizeApiOptions } from "../prettier-internal.js"; +import { normalizeOptions as normalizeApiOptions } from "../prettier-internal.js"; import minimist from "./minimist.js"; import createMinimistOptions from "./create-minimist-options.js"; import normalizeCliOptions from "./normalize-cli-options.js"; diff --git a/src/cli/options/normalize-cli-options.js b/src/cli/options/normalize-cli-options.js index 6ebddee37..842fb2440 100644 --- a/src/cli/options/normalize-cli-options.js +++ b/src/cli/options/normalize-cli-options.js @@ -1,12 +1,57 @@ import chalk from "chalk"; import leven from "leven"; -import { normalizeCliOptions as prettierNormalizeCliOptions } from "../prettier-internal.js"; +import { normalizeOptions, vnopts } from "../prettier-internal.js"; + +const descriptor = { + key: (key) => (key.length === 1 ? `-${key}` : `--${key}`), + value: (value) => vnopts.apiDescriptor.value(value), + pair: ({ key, value }) => + value === false + ? `--no-${key}` + : value === true + ? descriptor.key(key) + : value === "" + ? `${descriptor.key(key)} without an argument` + : `${descriptor.key(key)}=${value}`, +}; + +class FlagSchema extends vnopts.ChoiceSchema { + #flags = []; + + constructor({ name, flags }) { + super({ name, choices: flags }); + this.#flags = [...flags].sort(); + } + preprocess(value, utils) { + if ( + typeof value === "string" && + value.length > 0 && + !this.#flags.includes(value) + ) { + const suggestion = this.#flags.find((flag) => leven(flag, value) < 3); + if (suggestion) { + utils.logger.warn( + [ + `Unknown flag ${chalk.yellow(utils.descriptor.value(value))},`, + `did you mean ${chalk.blue(utils.descriptor.value(suggestion))}?`, + ].join(" ") + ); + return suggestion; + } + } + return value; + } + expected() { + return "a flag"; + } +} function normalizeCliOptions(options, optionInfos, opts) { - return prettierNormalizeCliOptions(options, optionInfos, { - colorsModule: chalk, - levenshteinDistance: leven, + return normalizeOptions(options, optionInfos, { ...opts, + isCLI: true, + FlagSchema, + descriptor, }); } diff --git a/src/cli/prettier-internal.js b/src/cli/prettier-internal.js index 8525f400d..0986a2d1d 100644 --- a/src/cli/prettier-internal.js +++ b/src/cli/prettier-internal.js @@ -5,7 +5,7 @@ export const { coreOptions, createIgnorer, optionsHiddenDefaults, - normalizeApiOptions, - normalizeCliOptions, + normalizeOptions, getSupportInfoWithoutPlugins, + vnopts, } = sharedWithCli; diff --git a/src/index.js b/src/index.js index acc106d54..119349d63 100644 --- a/src/index.js +++ b/src/index.js @@ -1,3 +1,4 @@ +import vnopts from "vnopts"; import core from "./main/core.js"; import { getSupportInfo as getSupportInfoWithoutPlugins } from "./main/support.js"; import getFileInfoWithoutPlugins from "./common/get-file-info.js"; @@ -15,10 +16,7 @@ import * as errors from "./common/errors.js"; import * as coreOptions from "./main/core-options.js"; import createIgnorer from "./common/create-ignorer.js"; import { hiddenDefaults as optionsHiddenDefaults } from "./main/options.js"; -import { - normalizeApiOptions, - normalizeCliOptions, -} from "./main/options-normalizer.js"; +import normalizeOptions from "./main/options-normalizer.js"; import arrayify from "./utils/arrayify.js"; import partition from "./utils/partition.js"; import { isNonEmptyArray } from "./common/util.js"; @@ -80,9 +78,9 @@ const sharedWithCli = { coreOptions, createIgnorer, optionsHiddenDefaults, - normalizeApiOptions, - normalizeCliOptions, + normalizeOptions, getSupportInfoWithoutPlugins, + vnopts, utils: { arrayify, isNonEmptyArray, diff --git a/src/main/options-normalizer.js b/src/main/options-normalizer.js index 8f8a29161..4a8bc0a02 100644 --- a/src/main/options-normalizer.js +++ b/src/main/options-normalizer.js @@ -4,64 +4,12 @@ import vnopts from "vnopts"; * @typedef {import("./support.js").NamedOptionInfo} NamedOptionInfo */ -const cliDescriptor = { - key: (key) => (key.length === 1 ? `-${key}` : `--${key}`), - value: (value) => vnopts.apiDescriptor.value(value), - pair: ({ key, value }) => - value === false - ? `--no-${key}` - : value === true - ? cliDescriptor.key(key) - : value === "" - ? `${cliDescriptor.key(key)} without an argument` - : `${cliDescriptor.key(key)}=${value}`, -}; - -// To prevent `chalk` and `leven` module from being included in the `standalone.js` bundle, it will take that as an argument if needed. -const getFlagSchema = ({ colorsModule, levenshteinDistance }) => - class FlagSchema extends vnopts.ChoiceSchema { - #flags = []; - - constructor({ name, flags }) { - super({ name, choices: flags }); - this.#flags = [...flags].sort(); - } - preprocess(value, utils) { - if ( - typeof value === "string" && - value.length > 0 && - !this.#flags.includes(value) - ) { - const suggestion = this.#flags.find( - (flag) => levenshteinDistance(flag, value) < 3 - ); - if (suggestion) { - utils.logger.warn( - [ - `Unknown flag ${colorsModule.yellow( - utils.descriptor.value(value) - )},`, - `did you mean ${colorsModule.blue( - utils.descriptor.value(suggestion) - )}?`, - ].join(" ") - ); - return suggestion; - } - } - return value; - } - expected() { - return "a flag"; - } - }; - let hasDeprecationWarned; /** * @param {*} options * @param {*} optionInfos - * @param {{ logger?: false; isCLI?: boolean; passThrough?: boolean; colorsModule?: any; levenshteinDistance?: any }} param2 + * @param {{ logger?: false; isCLI?: boolean; passThrough?: string[] | boolean; FlagSchema?: any; descriptor?: any }} param2 */ function normalizeOptions( options, @@ -70,10 +18,24 @@ function normalizeOptions( logger = false, isCLI = false, passThrough = false, - colorsModule = null, - levenshteinDistance = null, + FlagSchema, + descriptor, } = {} ) { + // TODO: Move CLI related part into `/src/cli` + /* istanbul ignore next */ + if (isCLI) { + if (!FlagSchema) { + throw new Error("'FlagSchema' option is required."); + } + + if (!descriptor) { + throw new Error("'descriptor' option is required."); + } + } else { + descriptor = vnopts.apiDescriptor; + } + const unknown = !passThrough ? (key, value, options) => { // Don't suggest `_` for unknown flags @@ -88,12 +50,7 @@ function normalizeOptions( !passThrough.includes(key) ? undefined : { [key]: value } : (key, value) => ({ [key]: value }); - const descriptor = isCLI ? cliDescriptor : vnopts.apiDescriptor; - const schemas = optionInfosToSchemas(optionInfos, { - isCLI, - colorsModule, - levenshteinDistance, - }); + const schemas = optionInfosToSchemas(optionInfos, { isCLI, FlagSchema }); const normalizer = new vnopts.Normalizer(schemas, { logger, unknown, @@ -121,10 +78,7 @@ function normalizeOptions( return normalized; } -function optionInfosToSchemas( - optionInfos, - { isCLI, colorsModule, levenshteinDistance } -) { +function optionInfosToSchemas(optionInfos, { isCLI, FlagSchema }) { const schemas = []; if (isCLI) { @@ -136,8 +90,7 @@ function optionInfosToSchemas( optionInfoToSchema(optionInfo, { isCLI, optionInfos, - colorsModule, - levenshteinDistance, + FlagSchema, }) ); @@ -160,10 +113,7 @@ function optionInfosToSchemas( * @param {any} param1 * @returns */ -function optionInfoToSchema( - optionInfo, - { isCLI, optionInfos, colorsModule, levenshteinDistance } -) { +function optionInfoToSchema(optionInfo, { isCLI, optionInfos, FlagSchema }) { const { name } = optionInfo; if (name === "plugin-search-dir" || name === "pluginSearchDirs") { @@ -223,7 +173,8 @@ function optionInfoToSchema( SchemaConstructor = vnopts.BooleanSchema; break; case "flag": - SchemaConstructor = getFlagSchema({ colorsModule, levenshteinDistance }); + // Only available when normalizing CLI options + SchemaConstructor = FlagSchema; parameters.flags = optionInfos.flatMap((optionInfo) => [ optionInfo.alias, @@ -286,23 +237,4 @@ function optionInfoToSchema( : SchemaConstructor.create({ ...parameters, ...handlers }); } -function normalizeApiOptions(options, optionInfos, opts) { - return normalizeOptions(options, optionInfos, opts); -} - -function normalizeCliOptions(options, optionInfos, opts) { - /* istanbul ignore next */ - if (process.env.NODE_ENV !== "production") { - if (!opts.colorsModule) { - throw new Error("'colorsModule' option is required."); - } - - if (!opts.levenshteinDistance) { - throw new Error("'levenshteinDistance' option is required."); - } - } - - return normalizeOptions(options, optionInfos, { isCLI: true, ...opts }); -} - -export { normalizeApiOptions, normalizeCliOptions }; +export default normalizeOptions; diff --git a/src/main/options.js b/src/main/options.js index 559a4945b..4103c61dd 100644 --- a/src/main/options.js +++ b/src/main/options.js @@ -2,7 +2,7 @@ import path from "node:path"; import { UndefinedParserError } from "../common/errors.js"; import { getSupportInfo } from "../main/support.js"; import getInterpreter from "../utils/get-interpreter.js"; -import { normalizeApiOptions } from "./options-normalizer.js"; +import normalizeOptions from "./options-normalizer.js"; import { resolveParser } from "./parser.js"; const hiddenDefaults = { @@ -52,7 +52,7 @@ async function normalize(options, opts = {}) { const parser = await resolveParser( // @ts-expect-error - normalizeApiOptions( + normalizeOptions( rawOptions, [supportOptions.find((x) => x.name === "parser")], { passThrough: true, logger: false } @@ -90,7 +90,7 @@ async function normalize(options, opts = {}) { rawOptions.trailingComma = "none"; } - return normalizeApiOptions(rawOptions, supportOptions, { + return normalizeOptions(rawOptions, supportOptions, { passThrough: Object.keys(hiddenDefaults), ...opts, });