From e4a74c05f4502dd4ec70495c3130ff08ab088e05 Mon Sep 17 00:00:00 2001 From: fisker Cheung Date: Thu, 16 Nov 2023 01:05:57 +0800 Subject: [PATCH] Fix argument passed to `lilconfig.search()` (#15363) --- changelog_unreleased/api/15363.md | 19 ++++++++++++ src/cli/format.js | 4 ++- src/cli/options/get-options-for-file.js | 4 +-- src/config/resolve-config.js | 23 +++++++++----- .../__snapshots__/config-invalid.js.snap | 4 +-- .../__snapshots__/config-resolution.js.snap | 2 +- .../__tests__/config-resolution.js | 31 ++++++++++++++++++- .../cli/config/config-position/.prettierrc | 1 + .../config-position/directory/.prettierrc | 1 + .../directory/file-indirectory.js | 2 ++ .../cli/config/config-position/file.js | 2 ++ 11 files changed, 79 insertions(+), 14 deletions(-) create mode 100644 changelog_unreleased/api/15363.md create mode 100644 tests/integration/cli/config/config-position/.prettierrc create mode 100644 tests/integration/cli/config/config-position/directory/.prettierrc create mode 100644 tests/integration/cli/config/config-position/directory/file-indirectory.js create mode 100644 tests/integration/cli/config/config-position/file.js diff --git a/changelog_unreleased/api/15363.md b/changelog_unreleased/api/15363.md new file mode 100644 index 000000000..4c180935c --- /dev/null +++ b/changelog_unreleased/api/15363.md @@ -0,0 +1,19 @@ +#### Fix config file search (#15363 by @fisker) + +Previously, we start search for config files from the filePath as a directory, if it happened to be a directory and contains config file, it will be used by mistake. + +```text +├─ .prettierrc +└─ test.js (A directory) + └─ index.min.js +``` + +```js +// Prettier stable +await prettier.resolveConfigFile(new URL("./test.js", import.meta.url)); +// /test.js/.prettierrc + +// Prettier main +await prettier.resolveConfigFile(new URL("./test.js", import.meta.url)); +// /.prettierrc +``` diff --git a/src/cli/format.js b/src/cli/format.js index 9b4d6a2d2..69eb0ef59 100644 --- a/src/cli/format.js +++ b/src/cli/format.js @@ -248,6 +248,8 @@ async function formatStdin(context) { try { const input = await getStdin(); + // TODO[@fisker]: Exit if no input. + // `prettier --config-precedence cli-override` let isFileIgnored = false; if (filepath) { @@ -262,7 +264,7 @@ async function formatStdin(context) { const options = await getOptionsForFile( context, - filepath ? path.resolve(process.cwd(), filepath) : process.cwd(), + filepath ? path.resolve(filepath) : undefined, ); if (await listDifferent(context, input, options, "(stdin)")) { diff --git a/src/cli/options/get-options-for-file.js b/src/cli/options/get-options-for-file.js index 0d0fdbf3f..7098b95b3 100644 --- a/src/cli/options/get-options-for-file.js +++ b/src/cli/options/get-options-for-file.js @@ -77,7 +77,8 @@ async function getOptionsOrDie(context, filePath) { return options; } catch (error) { context.logger.error( - `Invalid configuration for file "${filePath}":\n` + error.message, + `Invalid configuration${filePath ? ` for file "${filePath}"` : ""}:\n` + + error.message, ); process.exit(2); } @@ -103,7 +104,6 @@ function applyConfigPrecedence(context, options) { async function getOptionsForFile(context, filepath) { const options = await getOptionsOrDie(context, filepath); - const hasPlugins = options?.plugins; if (hasPlugins) { await context.pushContextPlugins(options.plugins); diff --git a/src/config/resolve-config.js b/src/config/resolve-config.js index 54e3d9806..4c15de5f9 100644 --- a/src/config/resolve-config.js +++ b/src/config/resolve-config.js @@ -30,9 +30,17 @@ function loadPrettierConfig(filePath, options) { const { load, search } = getPrettierConfigExplorer({ cache: Boolean(useCache), }); - return configPath - ? load(configPath) - : search(filePath ? path.resolve(filePath) : undefined); + + if (configPath) { + return load(configPath); + } + + if (!filePath) { + return search(); + } + + const dirname = path.dirname(path.resolve(filePath)); + return search(dirname); } async function resolveConfig(fileUrlOrPath, options) { @@ -65,10 +73,11 @@ async function resolveConfig(fileUrlOrPath, options) { } async function resolveConfigFile(fileUrlOrPath) { - const { search } = getPrettierConfigExplorer({ cache: false }); - const result = await search( - fileUrlOrPath ? path.resolve(toPath(fileUrlOrPath)) : undefined, - ); + const { search } = getPrettierConfigExplorerWithoutCache(); + const dirname = fileUrlOrPath + ? path.dirname(path.resolve(toPath(fileUrlOrPath))) + : undefined; + const result = await search(dirname); return result?.filepath ?? null; } diff --git a/tests/integration/__tests__/__snapshots__/config-invalid.js.snap b/tests/integration/__tests__/__snapshots__/config-invalid.js.snap index a56e9379e..9b78f11b4 100644 --- a/tests/integration/__tests__/__snapshots__/config-invalid.js.snap +++ b/tests/integration/__tests__/__snapshots__/config-invalid.js.snap @@ -22,7 +22,7 @@ exports[`show warning with unknown option (stdout) 1`] = `""`; exports[`show warning with unknown option (write) 1`] = `[]`; exports[`throw error for unsupported extension (stderr) 1`] = ` -"[error] Invalid configuration for file "/config/invalid": +"[error] Invalid configuration: [error] No loader specified for extension ".unsupported"" `; @@ -62,7 +62,7 @@ exports[`throw error with invalid config precedence option (configPrecedence) (s exports[`throw error with invalid config precedence option (configPrecedence) (write) 1`] = `[]`; exports[`throw error with invalid config target (directory) (stderr) 1`] = ` -"[error] Invalid configuration for file "/config/invalid": +"[error] Invalid configuration: [error] EISDIR: illegal operation on a directory, read" `; diff --git a/tests/integration/__tests__/__snapshots__/config-resolution.js.snap b/tests/integration/__tests__/__snapshots__/config-resolution.js.snap index 9dfe1054f..ccabbd11a 100644 --- a/tests/integration/__tests__/__snapshots__/config-resolution.js.snap +++ b/tests/integration/__tests__/__snapshots__/config-resolution.js.snap @@ -10,7 +10,7 @@ exports[`accepts configuration from --config (stdout) 1`] = ` exports[`accepts configuration from --config (write) 1`] = `[]`; -exports[`prints error message when no file found with --find-config-path (stderr) 1`] = `"[error] Can not find configure file for ".."."`; +exports[`prints error message when no file found with --find-config-path (stderr) 1`] = `"[error] Can not find configure file for "../--non-exits-filename--"."`; exports[`prints error message when no file found with --find-config-path (write) 1`] = `[]`; diff --git a/tests/integration/__tests__/config-resolution.js b/tests/integration/__tests__/config-resolution.js index a29a4247e..72a2203a3 100644 --- a/tests/integration/__tests__/config-resolution.js +++ b/tests/integration/__tests__/config-resolution.js @@ -74,7 +74,7 @@ describe("prints error message when no file found with --find-config-path", () = "--end-of-line", "lf", "--find-config-path", - "..", + "../--non-exits-filename--", ]).test({ stdout: "", status: 1, @@ -389,3 +389,32 @@ test("API resolveConfig accepts path or URL", async () => { expect(resultByPath).toMatchObject(expectedResult); expect(resultByRelativePath).toMatchObject(expectedResult); }); + +test("Search from directory, not treat file as directory", async () => { + // CLI + const getConfigFileByCli = async (file) => { + const { stdout: configFile } = await runCli("cli/config/config-position/", [ + "--find-config-path", + file, + ]); + return configFile; + }; + + expect(await getConfigFileByCli("file.js")).toBe(".prettierrc"); + expect(await getConfigFileByCli("directory/file-in-child-directory.js")).toBe( + "directory/.prettierrc", + ); + + // Api + const directory = new URL("../cli/config/config-position/", import.meta.url); + const getConfigFileByApi = async (file) => { + const configFile = await prettier.resolveConfigFile( + new URL(file, directory), + ); + return url.pathToFileURL(configFile).href.slice(directory.href.length); + }; + expect(await getConfigFileByApi("file.js")).toBe(".prettierrc"); + expect(await getConfigFileByApi("directory/file-in-child-directory.js")).toBe( + "directory/.prettierrc", + ); +}); diff --git a/tests/integration/cli/config/config-position/.prettierrc b/tests/integration/cli/config/config-position/.prettierrc new file mode 100644 index 000000000..0967ef424 --- /dev/null +++ b/tests/integration/cli/config/config-position/.prettierrc @@ -0,0 +1 @@ +{} diff --git a/tests/integration/cli/config/config-position/directory/.prettierrc b/tests/integration/cli/config/config-position/directory/.prettierrc new file mode 100644 index 000000000..0967ef424 --- /dev/null +++ b/tests/integration/cli/config/config-position/directory/.prettierrc @@ -0,0 +1 @@ +{} diff --git a/tests/integration/cli/config/config-position/directory/file-indirectory.js b/tests/integration/cli/config/config-position/directory/file-indirectory.js new file mode 100644 index 000000000..a1f5e65d4 --- /dev/null +++ b/tests/integration/cli/config/config-position/directory/file-indirectory.js @@ -0,0 +1,2 @@ +Hello +.prettier() diff --git a/tests/integration/cli/config/config-position/file.js b/tests/integration/cli/config/config-position/file.js new file mode 100644 index 000000000..a1f5e65d4 --- /dev/null +++ b/tests/integration/cli/config/config-position/file.js @@ -0,0 +1,2 @@ +Hello +.prettier()