From 5f0956bc30ea88ca7faf2f9f38ba60a86cd61605 Mon Sep 17 00:00:00 2001 From: fisker Cheung Date: Tue, 5 Dec 2023 15:00:43 +0800 Subject: [PATCH] Implement our own config explorer (#15663) --- changelog_unreleased/api/15663.md | 3 + docs/api.md | 2 +- docs/configuration.md | 2 +- package.json | 1 - scripts/build/config.js | 5 - src/common/mockable.js | 4 +- src/config/get-prettier-config-explorer.js | 110 ---------------- .../config-searcher.js | 119 ++++++++++++++++++ src/config/prettier-config-explorer/index.js | 64 ++++++++++ .../prettier-config-explorer/load-config.js | 48 +++++++ .../load-external-config.js | 10 +- .../prettier-config-explorer/loaders.js | 68 ++++++++++ src/config/resolve-config.js | 48 +++---- src/utils/is-file.js | 22 ++++ .../__snapshots__/config-invalid.js.snap | 2 +- tests/integration/__tests__/config-invalid.js | 29 +++++ tests/integration/__tests__/mockable.js | 45 ------- tests/integration/cli-worker.js | 8 +- .../broken-package-json/.prettierignore | 2 + .../invalid/broken-package-json/.prettierrc | 1 + yarn.lock | 8 -- 21 files changed, 393 insertions(+), 208 deletions(-) create mode 100644 changelog_unreleased/api/15663.md delete mode 100644 src/config/get-prettier-config-explorer.js create mode 100644 src/config/prettier-config-explorer/config-searcher.js create mode 100644 src/config/prettier-config-explorer/index.js create mode 100644 src/config/prettier-config-explorer/load-config.js rename src/config/{ => prettier-config-explorer}/load-external-config.js (65%) create mode 100644 src/config/prettier-config-explorer/loaders.js create mode 100644 src/utils/is-file.js create mode 100644 tests/integration/cli/config/invalid/broken-package-json/.prettierignore create mode 100644 tests/integration/cli/config/invalid/broken-package-json/.prettierrc diff --git a/changelog_unreleased/api/15663.md b/changelog_unreleased/api/15663.md new file mode 100644 index 000000000..29aac10bf --- /dev/null +++ b/changelog_unreleased/api/15663.md @@ -0,0 +1,3 @@ +#### Improve config file search (#15663 by @fisker) + +The Prettier config file search performance has been improved by more effective cache strategy. diff --git a/docs/api.md b/docs/api.md index af40bde7e..d0fbf560c 100644 --- a/docs/api.md +++ b/docs/api.md @@ -66,7 +66,7 @@ If `options.editorconfig` is `true` and an [`.editorconfig` file](https://editor The promise will be rejected if there was an error parsing the configuration file. -The search starts at `process.cwd()`, or at `fileUrlOrPath` if provided. Please see the [lilconfig docs](https://github.com/antonk52/lilconfig) for details on how the resolving works. +The search starts at `process.cwd()`, or at the directory of `fileUrlOrPath` if provided. ```js const configFile = await prettier.resolveConfigFile(filePath); diff --git a/docs/configuration.md b/docs/configuration.md index eee65eb8e..5929585c9 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -3,7 +3,7 @@ id: configuration title: Configuration File --- -Prettier uses [lilconfig](https://github.com/antonk52/lilconfig) for configuration file support. This means you can configure Prettier via (in order of precedence): +You can configure Prettier via (in order of precedence): - A `"prettier"` key in your `package.json` file. - A `.prettierrc` file written in JSON or YAML. diff --git a/package.json b/package.json index ac919c108..2f9a4edc9 100644 --- a/package.json +++ b/package.json @@ -75,7 +75,6 @@ "js-yaml": "4.1.0", "json5": "2.2.3", "leven": "4.0.0", - "lilconfig": "2.1.0", "lines-and-columns": "2.0.4", "linguist-languages": "7.27.0", "mem": "9.0.2", diff --git a/scripts/build/config.js b/scripts/build/config.js index 505201621..8e67bba58 100644 --- a/scripts/build/config.js +++ b/scripts/build/config.js @@ -610,11 +610,6 @@ const nodejsFiles = [ }), path: require.resolve("@babel/code-frame"), }, - { - module: require.resolve("lilconfig"), - find: "exports.lilconfigSync = lilconfigSync;", - replacement: "", - }, ], addDefaultExport: true, }, diff --git a/src/common/mockable.js b/src/common/mockable.js index 57583c2f2..dc6d38692 100644 --- a/src/common/mockable.js +++ b/src/common/mockable.js @@ -1,5 +1,4 @@ import fs from "node:fs/promises"; -import { lilconfig } from "lilconfig"; import getStdin from "get-stdin"; import { isCI } from "ci-info"; @@ -8,7 +7,8 @@ function writeFormattedFile(file, data) { } const mockable = { - lilconfig, + // eslint-disable-next-line unicorn/no-useless-undefined + getPrettierConfigSearchStopDirectory: () => undefined, getStdin, isCI: () => isCI, writeFormattedFile, diff --git a/src/config/get-prettier-config-explorer.js b/src/config/get-prettier-config-explorer.js deleted file mode 100644 index 5d4bf25eb..000000000 --- a/src/config/get-prettier-config-explorer.js +++ /dev/null @@ -1,110 +0,0 @@ -import { pathToFileURL } from "node:url"; -import parseToml from "@iarna/toml/parse-async.js"; -import parseJson5 from "json5/lib/parse.js"; -import yaml from "js-yaml"; -import parseJson from "parse-json"; -import mockable from "../common/mockable.js"; -import loadExternalConfig from "./load-external-config.js"; - -const searchPlaces = [ - "package.json", - ".prettierrc", - ".prettierrc.json", - ".prettierrc.yaml", - ".prettierrc.yml", - ".prettierrc.json5", - ".prettierrc.js", - ".prettierrc.mjs", - ".prettierrc.cjs", - "prettier.config.js", - "prettier.config.mjs", - "prettier.config.cjs", - ".prettierrc.toml", -]; - -async function loadJs(filepath /*, content*/) { - const module = await import(pathToFileURL(filepath).href); - return module.default; -} - -function loadYaml(filepath, content) { - try { - return yaml.load(content); - } catch (/** @type {any} */ error) { - error.message = `YAML Error in ${filepath}:\n${error.message}`; - throw error; - } -} - -const loaders = { - async ".toml"(filePath, content) { - try { - return await parseToml(content); - } catch (/** @type {any} */ error) { - error.message = `TOML Error in ${filePath}:\n${error.message}`; - throw error; - } - }, - ".json5"(filePath, content) { - try { - return parseJson5(content); - } catch (/** @type {any} */ error) { - error.message = `JSON5 Error in ${filePath}:\n${error.message}`; - throw error; - } - }, - ".json"(filePath, content) { - try { - return parseJson(content); - } catch (/** @type {any} */ error) { - error.message = `JSON Error in ${filePath}:\n${error.message}`; - throw error; - } - }, - ".js": loadJs, - ".mjs": loadJs, - ".yaml": loadYaml, - ".yml": loadYaml, - noExt: loadYaml, -}; - -async function transform(result) { - if (!result?.config) { - return result; - } - - let { config, filepath } = result; - - /* - We support external config - - ```json - { - "prettier": "my-prettier-config-package-or-file" - } - ``` - */ - if (typeof config === "string") { - config = await loadExternalConfig(config, filepath); - result.config = config; - } - - if (typeof config !== "object") { - throw new TypeError( - "Config is only allowed to be an object, " + - `but received ${typeof config} in "${filepath}"`, - ); - } - - delete config.$schema; - return result; -} - -/** - * @return {ReturnType} - */ -function getExplorer() { - return mockable.lilconfig("prettier", { searchPlaces, loaders, transform }); -} - -export default getExplorer; diff --git a/src/config/prettier-config-explorer/config-searcher.js b/src/config/prettier-config-explorer/config-searcher.js new file mode 100644 index 000000000..a73a015b8 --- /dev/null +++ b/src/config/prettier-config-explorer/config-searcher.js @@ -0,0 +1,119 @@ +import path from "node:path"; +import iterateDirectoryUp from "iterate-directory-up"; +import isFile from "../../utils/is-file.js"; +import { loadConfigFromPackageJson } from "./loaders.js"; + +const CONFIG_FILE_NAMES = [ + "package.json", + ".prettierrc", + ".prettierrc.json", + ".prettierrc.yaml", + ".prettierrc.yml", + ".prettierrc.json5", + ".prettierrc.js", + ".prettierrc.mjs", + ".prettierrc.cjs", + "prettier.config.js", + "prettier.config.mjs", + "prettier.config.cjs", + ".prettierrc.toml", +]; + +async function isPackageJsonFileWithPrettierConfig(file) { + try { + return Boolean(await loadConfigFromPackageJson(file)); + } catch { + return false; + } +} + +async function searchConfigInDirectory(directory) { + for (const fileName of CONFIG_FILE_NAMES) { + const file = path.join(directory, fileName); + + if (!(await isFile(file))) { + continue; + } + + if ( + fileName !== "package.json" || + (await isPackageJsonFileWithPrettierConfig(file)) + ) { + return file; + } + } +} + +function createCachedSearchConfigInDirectory() { + const cache = new Map(); + + return async function (fileOrDirectory) { + if (cache.has(fileOrDirectory)) { + return cache.get(fileOrDirectory); + } + + const promise = searchConfigInDirectory(fileOrDirectory); + cache.set(fileOrDirectory, promise); + const result = await promise; + cache.set(fileOrDirectory, result); + return result; + }; +} + +/** @type {Map} */ // @ts-expect-error -- intentionally not add the `get` method +const noopMap = { has: () => false, set() {} }; +class ConfigSearcher { + #shouldCache; + #stopDirectory; + #searchedFilesStore; + #searchConfigInDirectory; + constructor({ stopDirectory, shouldCache }) { + this.#shouldCache = shouldCache; + this.#stopDirectory = stopDirectory; + this.#searchConfigInDirectory = shouldCache + ? createCachedSearchConfigInDirectory() + : searchConfigInDirectory; + this.#searchedFilesStore = shouldCache ? new Map() : noopMap; + } + + async #searchConfigInDirectories(startDirectory) { + const store = this.#searchedFilesStore; + for (const directory of iterateDirectoryUp( + startDirectory, + this.#stopDirectory, + )) { + // The parent directory may already searched + if (store.has(directory)) { + return store.get(directory); + } + + const file = await this.#searchConfigInDirectory(directory); + if (file) { + return file; + } + } + } + + async search(startDirectory) { + const store = this.#searchedFilesStore; + if (store.has(startDirectory)) { + return store.get(startDirectory); + } + + const configFile = await this.#searchConfigInDirectories(startDirectory); + + // Directories between startDirectory and configFile directory should has the same result + if (this.#shouldCache) { + for (const directory of iterateDirectoryUp( + startDirectory, + configFile ? path.dirname(configFile) : startDirectory, + )) { + store.set(directory, configFile); + } + } + + return configFile; + } +} + +export default ConfigSearcher; diff --git a/src/config/prettier-config-explorer/index.js b/src/config/prettier-config-explorer/index.js new file mode 100644 index 000000000..d8f1fa3f6 --- /dev/null +++ b/src/config/prettier-config-explorer/index.js @@ -0,0 +1,64 @@ +import path from "node:path"; +import mockable from "../../common/mockable.js"; +import loadConfigWithoutCache from "./load-config.js"; +import ConfigSearcher from "./config-searcher.js"; + +const loadCache = new Map(); +const searchCache = new Map(); +function clearCache() { + loadCache.clear(); + searchCache.clear(); +} + +/** + * @param {string} configFile + * @param {{shouldCache?: boolean}} param1 + * @returns {Promise>} + */ +function loadConfig(configFile, { shouldCache }) { + configFile = path.resolve(configFile); + if (!shouldCache || !loadCache.has(configFile)) { + const promise = loadConfigWithoutCache(configFile); + // Even if `shouldCache` is false, we still cache it, so we can use it later + loadCache.set(configFile, promise); + return promise; + } + + return loadCache.get(configFile); +} + +/** + * @param {{shouldCache?: boolean, stopDirectory?: string}} param0 + */ +function getSearchFunction({ shouldCache, stopDirectory }) { + stopDirectory = stopDirectory ? path.resolve(stopDirectory) : undefined; + const searchCacheKey = JSON.stringify({ shouldCache, stopDirectory }); + + if (!searchCache.has(searchCacheKey)) { + const searcher = new ConfigSearcher({ shouldCache, stopDirectory }); + const searchFunction = searcher.search.bind(searcher); + + searchCache.set(searchCacheKey, searchFunction); + } + + return searchCache.get(searchCacheKey); +} + +/** + * @param {string} startDirectory + * @param {{shouldCache?: boolean, stopDirectory?: string}} options + * @returns {Promise} + */ +function searchConfig(startDirectory, options = {}) { + startDirectory = startDirectory + ? path.resolve(startDirectory) + : process.cwd(); + + options.stopDirectory = mockable.getPrettierConfigSearchStopDirectory(); + + const search = getSearchFunction(options); + + return search(startDirectory); +} + +export { searchConfig, loadConfig, clearCache }; diff --git a/src/config/prettier-config-explorer/load-config.js b/src/config/prettier-config-explorer/load-config.js new file mode 100644 index 000000000..50658e62c --- /dev/null +++ b/src/config/prettier-config-explorer/load-config.js @@ -0,0 +1,48 @@ +import path from "node:path"; +import loadExternalConfig from "./load-external-config.js"; +import loaders, { loadConfigFromPackageJson } from "./loaders.js"; + +async function loadConfig(configFile) { + const { base: fileName, ext: extension } = path.parse(configFile); + const load = + fileName === "package.json" + ? loadConfigFromPackageJson + : loaders[extension]; + + if (!load) { + throw new Error( + `No loader specified for extension "${extension || "noExt"}"`, + ); + } + + let config = await load(configFile); + + if (!config) { + return; + } + + /* + We support external config + + ```json + { + "prettier": "my-prettier-config-package-or-file" + } + ``` + */ + if (typeof config === "string") { + config = await loadExternalConfig(config, configFile); + } + + if (typeof config !== "object") { + throw new TypeError( + "Config is only allowed to be an object, " + + `but received ${typeof config} in "${configFile}"`, + ); + } + + delete config.$schema; + return config; +} + +export default loadConfig; diff --git a/src/config/load-external-config.js b/src/config/prettier-config-explorer/load-external-config.js similarity index 65% rename from src/config/load-external-config.js rename to src/config/prettier-config-explorer/load-external-config.js index 96988a08c..2b40eddd4 100644 --- a/src/config/load-external-config.js +++ b/src/config/prettier-config-explorer/load-external-config.js @@ -1,12 +1,12 @@ -import requireFromFile from "../utils/require-from-file.js"; -import importFromFile from "../utils/import-from-file.js"; +import requireFromFile from "../../utils/require-from-file.js"; +import importFromFile from "../../utils/import-from-file.js"; const requireErrorCodesShouldBeIgnored = new Set([ "MODULE_NOT_FOUND", "ERR_REQUIRE_ESM", "ERR_PACKAGE_PATH_NOT_EXPORTED", ]); -async function loadExternalConfig(config, filepath) { +async function loadExternalConfig(externalConfig, configFile) { /* Try `require()` first, this is how it works in Prettier v2. Kept this because the external config path or package may can't load with `import()`: @@ -15,14 +15,14 @@ async function loadExternalConfig(config, filepath) { 3. is a dirname with index.js inside */ try { - return requireFromFile(config, filepath); + return requireFromFile(externalConfig, configFile); } catch (/** @type {any} */ error) { if (!requireErrorCodesShouldBeIgnored.has(error?.code)) { throw error; } } - const module = await importFromFile(config, filepath); + const module = await importFromFile(externalConfig, configFile); return module.default; } diff --git a/src/config/prettier-config-explorer/loaders.js b/src/config/prettier-config-explorer/loaders.js new file mode 100644 index 000000000..2f663e333 --- /dev/null +++ b/src/config/prettier-config-explorer/loaders.js @@ -0,0 +1,68 @@ +import { pathToFileURL } from "node:url"; +import parseToml from "@iarna/toml/parse-async.js"; +import parseJson5 from "json5/lib/parse.js"; +import jsYaml from "js-yaml"; +import parseJson from "parse-json"; +import readFile from "../../utils/read-file.js"; + +async function readJson(file) { + const content = await readFile(file); + try { + return parseJson(content); + } catch (/** @type {any} */ error) { + error.message = `JSON Error in ${file}:\n${error.message}`; + throw error; + } +} + +async function loadJs(file) { + const module = await import(pathToFileURL(file).href); + return module.default; +} + +async function loadConfigFromPackageJson(file) { + const { prettier } = await readJson(file); + return prettier; +} + +async function loadYaml(file) { + const content = await readFile(file); + try { + return jsYaml.load(content); + } catch (/** @type {any} */ error) { + error.message = `YAML Error in ${file}:\n${error.message}`; + throw error; + } +} + +const loaders = { + async ".toml"(file) { + const content = await readFile(file); + try { + return await parseToml(content); + } catch (/** @type {any} */ error) { + error.message = `TOML Error in ${file}:\n${error.message}`; + throw error; + } + }, + async ".json5"(file) { + const content = await readFile(file); + try { + return parseJson5(content); + } catch (/** @type {any} */ error) { + error.message = `JSON5 Error in ${file}:\n${error.message}`; + throw error; + } + }, + ".json": readJson, + ".js": loadJs, + ".mjs": loadJs, + ".cjs": loadJs, + ".yaml": loadYaml, + ".yml": loadYaml, + // No extension + "": loadYaml, +}; + +export default loaders; +export { readJson, loadConfigFromPackageJson }; diff --git a/src/config/resolve-config.js b/src/config/resolve-config.js index 4c15de5f9..707007d89 100644 --- a/src/config/resolve-config.js +++ b/src/config/resolve-config.js @@ -3,15 +3,16 @@ import micromatch from "micromatch"; import mem, { memClear } from "mem"; import { toPath } from "url-or-path"; import partition from "../utils/partition.js"; +import { + searchConfig as searchPrettierConfig, + loadConfig as loadPrettierConfigFile, + clearCache as clearPrettierConfigCache, +} from "./prettier-config-explorer/index.js"; import loadEditorConfigWithoutCache from "./resolve-editorconfig.js"; -import getPrettierConfigExplorerWithoutCache from "./get-prettier-config-explorer.js"; -const getPrettierConfigExplorer = mem(getPrettierConfigExplorerWithoutCache, { - cacheKey: ([options]) => options.cache, -}); const memoizedLoadEditorConfig = mem(loadEditorConfigWithoutCache); function clearCache() { - memClear(getPrettierConfigExplorer); + clearPrettierConfigCache(); memClear(memoizedLoadEditorConfig); } @@ -25,22 +26,24 @@ function loadEditorConfig(filePath, options) { )(filePath); } -function loadPrettierConfig(filePath, options) { - const { useCache, config: configPath } = options; - const { load, search } = getPrettierConfigExplorer({ - cache: Boolean(useCache), - }); +async function loadPrettierConfig(filePath, options) { + const shouldCache = options.useCache; + let configFile = options.config; - if (configPath) { - return load(configPath); + if (!configFile) { + const directory = filePath + ? path.dirname(path.resolve(filePath)) + : undefined; + configFile = await searchPrettierConfig(directory, { shouldCache }); } - if (!filePath) { - return search(); + if (!configFile) { + return; } - const dirname = path.dirname(path.resolve(filePath)); - return search(dirname); + const config = await loadPrettierConfigFile(configFile, { shouldCache }); + + return { config, configFile }; } async function resolveConfig(fileUrlOrPath, options) { @@ -64,7 +67,7 @@ async function resolveConfig(fileUrlOrPath, options) { if (Array.isArray(merged.plugins)) { merged.plugins = merged.plugins.map((value) => typeof value === "string" && value.startsWith(".") // relative path - ? path.resolve(path.dirname(result.filepath), value) + ? path.resolve(path.dirname(result.configFile), value) : value, ); } @@ -73,19 +76,18 @@ async function resolveConfig(fileUrlOrPath, options) { } async function resolveConfigFile(fileUrlOrPath) { - const { search } = getPrettierConfigExplorerWithoutCache(); - const dirname = fileUrlOrPath + const directory = fileUrlOrPath ? path.dirname(path.resolve(toPath(fileUrlOrPath))) : undefined; - const result = await search(dirname); - return result?.filepath ?? null; + const result = await searchPrettierConfig(directory, { shouldCache: false }); + return result ?? null; } function mergeOverrides(configResult, filePath) { - const { config, filepath: configPath } = configResult || {}; + const { config, configFile } = configResult || {}; const { overrides, ...options } = config || {}; if (filePath && overrides) { - const relativeFilePath = path.relative(path.dirname(configPath), filePath); + const relativeFilePath = path.relative(path.dirname(configFile), filePath); for (const override of overrides) { if ( pathMatchesGlobs( diff --git a/src/utils/is-file.js b/src/utils/is-file.js new file mode 100644 index 000000000..d9526826a --- /dev/null +++ b/src/utils/is-file.js @@ -0,0 +1,22 @@ +import fs from "node:fs/promises"; +import { toPath } from "url-or-path"; + +/** + * @param {string | URL} file + * @param {{allowSymlinks: boolean}} [options] + * @returns {Promise} + */ +async function isFile(file, options) { + const allowSymlinks = options?.allowSymlinks ?? true; + + let stats; + try { + stats = await (allowSymlinks ? fs.stat : fs.lstat)(toPath(file)); + } catch { + return false; + } + + return stats.isFile(); +} + +export default isFile; diff --git a/tests/integration/__tests__/__snapshots__/config-invalid.js.snap b/tests/integration/__tests__/__snapshots__/config-invalid.js.snap index 9b78f11b4..105c5d0cf 100644 --- a/tests/integration/__tests__/__snapshots__/config-invalid.js.snap +++ b/tests/integration/__tests__/__snapshots__/config-invalid.js.snap @@ -63,7 +63,7 @@ exports[`throw error with invalid config precedence option (configPrecedence) (w exports[`throw error with invalid config target (directory) (stderr) 1`] = ` "[error] Invalid configuration: -[error] EISDIR: illegal operation on a directory, read" +[error] Unable to read '/config/invalid/folder/.prettierrc': EISDIR: illegal operation on a directory, read" `; exports[`throw error with invalid config target (directory) (stdout) 1`] = `""`; diff --git a/tests/integration/__tests__/config-invalid.js b/tests/integration/__tests__/config-invalid.js index 7cf19705a..4becfe576 100644 --- a/tests/integration/__tests__/config-invalid.js +++ b/tests/integration/__tests__/config-invalid.js @@ -1,3 +1,4 @@ +import fs from "node:fs/promises"; import outdent from "outdent"; import jestPathSerializer from "../path-serializer.js"; @@ -178,3 +179,31 @@ describe("Invalid config value", () => { stderr: "", }); }); + +// Can't put a invalid `package.json` file in the test dir +test("Invalid package.json", async () => { + const packageJsonFile = new URL( + "../cli/config/invalid/broken-package-json/package.json", + import.meta.url, + ); + + try { + await fs.writeFile(packageJsonFile, '{"prettier":{}}'); + const { stdout: configFileForValidPackageJson } = await runCli( + "cli/config/invalid/broken-package-json", + ["--find-config-path", "foo.js"], + ); + + expect(configFileForValidPackageJson).toBe("package.json"); + + await fs.writeFile(packageJsonFile, '{"prettier":{'); + const { stdout: configFileForInvalidPackageJson } = await runCli( + "cli/config/invalid/broken-package-json", + ["--find-config-path", "foo.js"], + ); + + expect(configFileForInvalidPackageJson).toBe(".prettierrc"); + } finally { + await fs.rm(packageJsonFile, { force: true }); + } +}); diff --git a/tests/integration/__tests__/mockable.js b/tests/integration/__tests__/mockable.js index d3eaf48e2..68b5f53ea 100644 --- a/tests/integration/__tests__/mockable.js +++ b/tests/integration/__tests__/mockable.js @@ -1,52 +1,7 @@ -import path from "node:path"; -import createEsmUtils from "esm-utils"; import prettier from "../../config/prettier-entry.js"; -const { __dirname } = createEsmUtils(import.meta); const { mockable } = prettier.__debug; -// This don't has to be the same result as `prettier.resolveConfig`, -// Because we are testing with default `lilconfigOptions` -describe("lilconfig", () => { - const { lilconfig } = mockable; - - const configs = [ - { - title: "prettier.config.cjs", - dirname: path.join(__dirname, "../cli/config/js/"), - file: path.join(__dirname, "../cli/config/js/prettier.config.cjs"), - value: { - endOfLine: "auto", - tabWidth: 8, - }, - }, - { - title: "package.json", - dirname: path.join(__dirname, "../cli/config/package/"), - file: path.join(__dirname, "../cli/config/package/package.json"), - value: { - tabWidth: 3, - overrides: [ - { - files: "*.ts", - options: { - tabWidth: 5, - }, - }, - ], - }, - }, - ]; - - for (const { title, dirname, file, value } of configs) { - test(`async version ${title}`, async () => { - const { config, filepath } = await lilconfig("prettier").search(dirname); - expect(config).toEqual(value); - expect(filepath).toBe(file); - }); - } -}); - test("isCI", () => { expect(typeof mockable.isCI()).toBe("boolean"); }); diff --git a/tests/integration/cli-worker.js b/tests/integration/cli-worker.js index c4b9405fb..583569fab 100644 --- a/tests/integration/cli-worker.js +++ b/tests/integration/cli-worker.js @@ -3,7 +3,6 @@ import fs from "node:fs"; import path from "node:path"; import url from "node:url"; import readline from "node:readline"; -import { lilconfig } from "lilconfig"; import { prettierCli, prettierMainEntry } from "./env.js"; const normalizeToPosix = @@ -65,11 +64,8 @@ async function run() { // eslint-disable-next-line require-await mockable.getStdin = async () => options.input || ""; mockable.isCI = () => Boolean(options.ci); - mockable.lilconfig = (moduleName, options) => - lilconfig(moduleName, { - ...options, - stopDir: url.fileURLToPath(new URL("./cli", import.meta.url)), - }); + mockable.getPrettierConfigSearchStopDirectory = () => + url.fileURLToPath(new URL("./cli", import.meta.url)); // eslint-disable-next-line require-await mockable.writeFormattedFile = async (filename, content) => { filename = normalizeToPosix(path.relative(process.cwd(), filename)); diff --git a/tests/integration/cli/config/invalid/broken-package-json/.prettierignore b/tests/integration/cli/config/invalid/broken-package-json/.prettierignore new file mode 100644 index 000000000..396a8fc23 --- /dev/null +++ b/tests/integration/cli/config/invalid/broken-package-json/.prettierignore @@ -0,0 +1,2 @@ +# will be created by test +package.json diff --git a/tests/integration/cli/config/invalid/broken-package-json/.prettierrc b/tests/integration/cli/config/invalid/broken-package-json/.prettierrc new file mode 100644 index 000000000..0967ef424 --- /dev/null +++ b/tests/integration/cli/config/invalid/broken-package-json/.prettierrc @@ -0,0 +1 @@ +{} diff --git a/yarn.lock b/yarn.lock index f719621cc..6258c6fdd 100644 --- a/yarn.lock +++ b/yarn.lock @@ -6214,13 +6214,6 @@ __metadata: languageName: node linkType: hard -"lilconfig@npm:2.1.0": - version: 2.1.0 - resolution: "lilconfig@npm:2.1.0" - checksum: 8549bb352b8192375fed4a74694cd61ad293904eee33f9d4866c2192865c44c4eb35d10782966242634e0cbc1e91fe62b1247f148dc5514918e3a966da7ea117 - languageName: node - linkType: hard - "lines-and-columns@npm:2.0.4, lines-and-columns@npm:^2.0.3": version: 2.0.4 resolution: "lines-and-columns@npm:2.0.4" @@ -7371,7 +7364,6 @@ __metadata: js-yaml: 4.1.0 json5: 2.2.3 leven: 4.0.0 - lilconfig: 2.1.0 lines-and-columns: 2.0.4 linguist-languages: 7.27.0 magic-string: 0.30.5