diff --git a/.changeset/selfish-pillows-remember.md b/.changeset/selfish-pillows-remember.md new file mode 100644 index 000000000..4e02cbc09 --- /dev/null +++ b/.changeset/selfish-pillows-remember.md @@ -0,0 +1,5 @@ +--- +'lint-staged': patch +--- + +To improve performance, only use `lilconfig` when searching for config files outside the git repo. In the regular case, _lint-staged_ finds the config files from the Git index and loads them directly. diff --git a/lib/loadConfig.js b/lib/loadConfig.js index b9a1ddcc3..b04d3b2f5 100644 --- a/lib/loadConfig.js +++ b/lib/loadConfig.js @@ -1,9 +1,9 @@ /** @typedef {import('./index').Logger} Logger */ +import fs from 'node:fs/promises' import path from 'node:path' import debug from 'debug' -import { lilconfig } from 'lilconfig' import YAML from 'yaml' import { dynamicImport } from './dynamicImport.js' @@ -37,8 +37,10 @@ export const searchPlaces = [ ] const jsonParse = (filePath, content) => { + const isPackageFile = PACKAGE_JSON_FILE.includes(path.basename(filePath)) try { - return JSON.parse(content) + const json = JSON.parse(content) + return isPackageFile ? json[CONFIG_NAME] : json } catch (error) { if (path.basename(filePath) === PACKAGE_JSON_FILE) { debugLog('Ignoring invalid package file `%s` with content:\n%s', filePath, content) @@ -64,6 +66,8 @@ const yamlParse = (filePath, content) => { } } +const NO_EXT = 'noExt' + /** * `lilconfig` doesn't support yaml files by default, * so we add custom loaders for those. Files without @@ -77,10 +81,31 @@ const loaders = { '.cjs': dynamicImport, '.yaml': yamlParse, '.yml': yamlParse, - noExt: yamlParse, + [NO_EXT]: yamlParse, +} + +const readFile = async (filepath) => { + const absolutePath = path.resolve(filepath) + const file = await fs.readFile(absolutePath) + return await file.toString() } -const explorer = lilconfig(CONFIG_NAME, { searchPlaces, loaders }) +const loadConfigByExt = async (filepath) => { + filepath = path.resolve(filepath) + const ext = path.extname(filepath) || NO_EXT + const loader = loaders[ext] + + /** + * No need to read file contents when loader only takes in the filepath argument + * and reads itself; this is for `lilconfig` compatibility + */ + const content = loader.length > 1 ? await readFile(filepath) : undefined + + return { + config: await loader(filepath, content), + filepath, + } +} /** * @param {object} options @@ -89,20 +114,22 @@ const explorer = lilconfig(CONFIG_NAME, { searchPlaces, loaders }) */ export const loadConfig = async ({ configPath, cwd }, logger) => { try { + let result + if (configPath) { debugLog('Loading configuration from `%s`...', configPath) + result = await loadConfigByExt(resolveConfig(configPath)) } else { debugLog('Searching for configuration from `%s`...', cwd) + const { lilconfig } = await import('lilconfig') + const explorer = lilconfig(CONFIG_NAME, { searchPlaces, loaders }) + result = await explorer.search(cwd) } - const result = await (configPath - ? explorer.load(resolveConfig(configPath)) - : explorer.search(cwd)) - if (!result) return {} // config is a promise when using the `dynamicImport` loader - const config = await result.config + const config = (await result.config) ?? null const filepath = result.filepath debugLog('Successfully loaded config from `%s`:\n%O', filepath, config) diff --git a/test/unit/loadConfig.spec.js b/test/unit/loadConfig.spec.js index 13b22e94f..53fe92546 100644 --- a/test/unit/loadConfig.spec.js +++ b/test/unit/loadConfig.spec.js @@ -286,7 +286,7 @@ describe('loadConfig', () => { const { config } = await loadConfig({ configPath: configFile }, logger) - expect(config).toBeUndefined() + expect(config).toBeNull() await fs.rm(configFile) })