From 517f85703412ca57eff6357b72eb908f43a09515 Mon Sep 17 00:00:00 2001 From: Bernd Busse Date: Thu, 3 Jul 2025 20:46:53 +0200 Subject: [PATCH] Ensure config passed via `--config-path` is resolved Normalize the configuration that is passed via the `--config-path` cli option and resolved against the current file. This allows `overrides` in these to work as well. --- src/config_prettier.ts | 70 +++++++++++++++++++++++++++--------------- src/index.ts | 11 +++++-- src/types.ts | 3 ++ 3 files changed, 57 insertions(+), 27 deletions(-) diff --git a/src/config_prettier.ts b/src/config_prettier.ts index 0bbc206..ed3d81f 100644 --- a/src/config_prettier.ts +++ b/src/config_prettier.ts @@ -5,7 +5,7 @@ import zeptomatch from "zeptomatch"; import Known from "./known.js"; import { fastJoinedPath, fastRelativeChildPath, getModulePath } from "./utils.js"; import { isObject, isString, isTruthy, isUndefined, memoize, noop, normalizePrettierOptions, omit, zipObjectUnless } from "./utils.js"; -import type { PrettierConfig, PrettierConfigWithOverrides, PromiseMaybe } from "./types.js"; +import type { PrettierConfig, PrettierConfigResolver, PrettierConfigWithOverrides, PromiseMaybe } from "./types.js"; const Loaders = { auto: (filePath: string): Promise => { @@ -87,12 +87,17 @@ const Ext2Loader: Record Promise> = { mjs: Loaders.js, }; +const normalizeConfig = (config: unknown, folderPath: string): PrettierConfigWithOverrides | undefined => { + return isObject(config) ? { ...config, ...normalizePrettierOptions(config, folderPath) } : undefined; +}; + const getPrettierConfig = (folderPath: string, fileName: string): PromiseMaybe => { const filePath = fastJoinedPath(folderPath, fileName); if (!Known.hasFilePath(filePath)) return; const loader = File2Loader[fileName] || File2Loader["default"]; - const normalize = (config: unknown) => (isObject(config) ? { ...config, ...normalizePrettierOptions(config, folderPath) } : undefined); - return loader(filePath).then(normalize).catch(noop); + return loader(filePath) + .then((config: unknown) => normalizeConfig(config, folderPath)) + .catch(noop); }; const getPrettierConfigs = memoize(async (folderPath: string, filesNames: string[]): Promise => { @@ -108,6 +113,41 @@ const getPrettierConfigsMap = async (foldersPaths: string[], filesNames: string[ return map; }; +const getPrettierConfigResolver = (configs: PrettierConfigWithOverrides[]): PrettierConfigResolver => { + return (filePath: string): PrettierConfig => { + let resolved: PrettierConfig = {}; + + for (let ci = 0, cl = configs.length; ci < cl; ci++) { + const config = configs[ci]; + const formatOptions = omit(config, ["overrides"]); + resolved = ci ? { ...resolved, ...formatOptions } : formatOptions; + + const overrides = config.overrides; + if (overrides) { + for (let oi = 0, ol = overrides.length; oi < ol; oi++) { + const override = overrides[oi]; + const filePathRelative = fastRelativeChildPath(override.folder, filePath); + if (!filePathRelative) continue; + if (!zeptomatch(override.filesPositive, filePathRelative)) continue; + if (zeptomatch(override.filesNegative, filePathRelative)) continue; + resolved = { ...resolved, ...override.options }; + } + } + } + + return resolved; + }; +}; + +const getPrettierConfigBys = (foldersPaths: string[], filesContents: unknown[]): PrettierConfigResolver | undefined => { + if (!foldersPaths.length) return; + const configsRaw = foldersPaths.map((folderPath, index) => normalizeConfig(filesContents[index], folderPath)); + const configs = configsRaw.filter(isTruthy); + + if (!configs.length) return; + return getPrettierConfigResolver(configs); +}; + const getPrettierConfigsUp = memoize(async (folderPath: string, filesNames: string[]): Promise => { const config = (await getPrettierConfigs(folderPath, filesNames))?.[0]; const folderPathUp = path.dirname(folderPath); @@ -119,27 +159,9 @@ const getPrettierConfigsUp = memoize(async (folderPath: string, filesNames: stri const getPrettierConfigResolved = async (filePath: string, filesNames: string[]): Promise => { const folderPath = path.dirname(filePath); const configs = await getPrettierConfigsUp(folderPath, filesNames); - let resolved: PrettierConfig = {}; - - for (let ci = 0, cl = configs.length; ci < cl; ci++) { - const config = configs[ci]; - const formatOptions = omit(config, ["overrides"]); - resolved = ci ? { ...resolved, ...formatOptions } : formatOptions; - - const overrides = config.overrides; - if (overrides) { - for (let oi = 0, ol = overrides.length; oi < ol; oi++) { - const override = overrides[oi]; - const filePathRelative = fastRelativeChildPath(override.folder, filePath); - if (!filePathRelative) continue; - if (!zeptomatch(override.filesPositive, filePathRelative)) continue; - if (zeptomatch(override.filesNegative, filePathRelative)) continue; - resolved = { ...resolved, ...override.options }; - } - } - } - return resolved; + const resolver = getPrettierConfigResolver(configs); + return resolver(filePath); }; -export { Loaders, File2Loader, Ext2Loader, getPrettierConfig, getPrettierConfigsMap, getPrettierConfigsUp, getPrettierConfigResolved }; +export { Loaders, File2Loader, Ext2Loader, getPrettierConfig, getPrettierConfigsMap, getPrettierConfigBys, getPrettierConfigsUp, getPrettierConfigResolved }; diff --git a/src/index.ts b/src/index.ts index 5f8a0c1..8d40955 100644 --- a/src/index.ts +++ b/src/index.ts @@ -6,7 +6,7 @@ import process from "node:process"; import Cache from "./cache.js"; import { getEditorConfigsMap, getEditorConfigResolved, getEditorConfigFormatOptions } from "./config_editorconfig.js"; import { getIgnoresContentMap, getIgnoreBys, getIgnoreResolved } from "./config_ignore.js"; -import { Loaders, File2Loader, getPrettierConfigsMap, getPrettierConfigResolved } from "./config_prettier.js"; +import { Loaders, File2Loader, getPrettierConfigsMap, getPrettierConfigBys, getPrettierConfigResolved } from "./config_prettier.js"; import { PRETTIER_VERSION, CLI_VERSION } from "./constants.js"; import Known from "./known.js"; import Logger from "./logger.js"; @@ -109,7 +109,8 @@ async function runGlobs(options: Options, pluginsDefaultOptions: PluginsOptions, const prettierManualFilesPaths = prettierManualFilesNames.map((fileName) => path.resolve(fileName)); const prettierManualFilesContents = await Promise.all(prettierManualFilesPaths.map((filePath) => fs.readFile(filePath, "utf8"))); const prettierManualConfigs = await Promise.all(prettierManualFilesPaths.map(Loaders.auto)); - const prettierManualConfig = prettierManualConfigs.length ? Object.assign({}, ...prettierManualConfigs) : undefined; + const prettierManualFoldersPaths = prettierManualFilesPaths.map(path.dirname); + const prettierManualConfig = getPrettierConfigBys(prettierManualFoldersPaths, prettierManualConfigs); const cliContextConfig = options.contextOptions; const cliFormatConfig = options.formatOptions; @@ -130,7 +131,11 @@ async function runGlobs(options: Options, pluginsDefaultOptions: PluginsOptions, if (!isForceIncluded && isExcluded) return; const getFormatOptions = async (): Promise => { const editorConfig = options.editorConfig ? getEditorConfigFormatOptions(await getEditorConfigResolved(filePath, editorConfigNames)) : {}; - const prettierConfig = prettierManualConfig || (options.config ? await getPrettierConfigResolved(filePath, prettierConfigNames) : {}); + const prettierConfig = prettierManualConfig + ? prettierManualConfig(filePath) + : options.config + ? await getPrettierConfigResolved(filePath, prettierConfigNames) + : {}; const formatOptions = { ...editorConfig, ...prettierConfig, ...options.formatOptions }; return formatOptions; }; diff --git a/src/types.ts b/src/types.ts index d809838..e80600e 100644 --- a/src/types.ts +++ b/src/types.ts @@ -82,6 +82,8 @@ type Prettier = typeof import("./prettier_serial.js"); type PrettierConfig = FormatOptions; +type PrettierConfigResolver = (filePath: string) => PrettierConfig; + type PrettierConfigWithOverrides = PrettierConfig & { overrides?: { filesPositive: string[]; @@ -108,6 +110,7 @@ export type { PluginsOptions, Prettier, PrettierConfig, + PrettierConfigResolver, PrettierConfigWithOverrides, PrettierPlugin, PromiseMaybe,