From 4afcda5addade65ef847e3c5b0c4a38db80d020b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Iiro=20J=C3=A4ppinen?= Date: Thu, 20 Jan 2022 18:09:27 +0200 Subject: [PATCH] fix: always search config from `cwd` first --- lib/getConfigGroups.js | 41 ++++++++++++++++++++---------------- lib/runAll.js | 2 +- test/getConfigGroups.spec.js | 4 ++++ test/integration.test.js | 29 +++++++++++++++++++++++++ 4 files changed, 57 insertions(+), 19 deletions(-) diff --git a/lib/getConfigGroups.js b/lib/getConfigGroups.js index c692b84b3..0923b2edc 100644 --- a/lib/getConfigGroups.js +++ b/lib/getConfigGroups.js @@ -13,9 +13,13 @@ import { validateConfig } from './validateConfig.js' * @param {Object} [options.configObject] - Explicit config object from the js API * @param {string} [options.configPath] - Explicit path to a config file * @param {string} [options.cwd] - Current working directory + * @param {string} [options.files] - List of staged files * @param {Logger} logger */ -export const getConfigGroups = async ({ configObject, configPath, files }, logger = console) => { +export const getConfigGroups = async ( + { configObject, configPath, cwd, files }, + logger = console +) => { // Return explicit config object from js API if (configObject) { const config = validateConfig(configObject, 'config object', logger) @@ -52,23 +56,24 @@ export const getConfigGroups = async ({ configObject, configPath, files }, logge // { '.lintstagedrc.json': { config: {...}, files: [...] } } const configGroups = {} - await Promise.all( - Object.entries(filesByDir).map(([dir, files]) => { - // Discover config from the base directory of the file - return loadConfig({ cwd: dir }, logger).then(({ config, filepath }) => { - if (!config) return - - if (filepath in configGroups) { - // Re-use cached config and skip validation - configGroups[filepath].files.push(...files) - return - } - - const validatedConfig = validateConfig(config, filepath, logger) - configGroups[filepath] = { config: validatedConfig, files } - }) - }) - ) + const searchConfig = async (cwd, files = []) => { + const { config, filepath } = await loadConfig({ cwd }, logger) + if (!config) return + + if (filepath in configGroups) { + // Re-use cached config and skip validation + configGroups[filepath].files.push(...files) + } else { + const validatedConfig = validateConfig(config, filepath, logger) + configGroups[filepath] = { config: validatedConfig, files } + } + } + + // Start by searching from cwd + await searchConfig(cwd) + + // Discover configs from the base directory of each file + await Promise.all(Object.entries(filesByDir).map(([dir, files]) => searchConfig(dir, files))) // Throw if no configurations were found if (Object.keys(configGroups).length === 0) { diff --git a/lib/runAll.js b/lib/runAll.js index dfe7997c1..eebce3d10 100644 --- a/lib/runAll.js +++ b/lib/runAll.js @@ -114,7 +114,7 @@ export const runAll = async ( return ctx } - const configGroups = await getConfigGroups({ configObject, configPath, files }, logger) + const configGroups = await getConfigGroups({ configObject, configPath, cwd, files }, logger) // lint-staged 10 will automatically add modifications to index // Warn user when their command includes `git add` diff --git a/test/getConfigGroups.spec.js b/test/getConfigGroups.spec.js index 3607659f0..6f1ceddd9 100644 --- a/test/getConfigGroups.spec.js +++ b/test/getConfigGroups.spec.js @@ -37,6 +37,8 @@ describe('getConfigGroups', () => { }) it('should find config files for all staged files', async () => { + // Base cwd + loadConfig.mockResolvedValueOnce({ config, filepath: '/.lintstagedrc.json' }) // '/foo.js' and '/bar.js' loadConfig.mockResolvedValueOnce({ config, filepath: '/.lintstagedrc.json' }) // '/deeper/foo.js' @@ -55,6 +57,8 @@ describe('getConfigGroups', () => { }) it('should find config for one file, and not care about other', async () => { + // Base cwd + loadConfig.mockResolvedValueOnce({}) // '/foo.js' loadConfig.mockResolvedValueOnce({}) // '/deeper/foo.js' diff --git a/test/integration.test.js b/test/integration.test.js index cefe6e388..8234ebdef 100644 --- a/test/integration.test.js +++ b/test/integration.test.js @@ -1147,6 +1147,35 @@ describe('lint-staged', () => { // 'a/very/deep/file/path/file.js' matched '.lintstagedrc.json' expect(await readFile('a/very/deep/file/path/file.js')).toMatch('level-0') }) + + it('should not care about staged file outside current cwd with another staged file', async () => { + await writeFile('file.js', testJsFileUgly) + await writeFile('deeper/file.js', testJsFileUgly) + await writeFile('deeper/.lintstagedrc.json', JSON.stringify(fixJsConfig.config)) + await execGit(['add', '.']) + + // Run lint-staged in "deeper/"" + expect(await gitCommit({ cwd: path.join(cwd, 'deeper') })).resolves + + // File inside deeper/ was fixed + expect(await readFile('deeper/file.js')).toEqual(testJsFilePretty) + // ...but file outside was not + expect(await readFile('file.js')).toEqual(testJsFileUgly) + }) + + it('should not care about staged file outside current cwd without any other staged files', async () => { + await writeFile('file.js', testJsFileUgly) + await writeFile('deeper/.lintstagedrc.json', JSON.stringify(fixJsConfig.config)) + await execGit(['add', '.']) + + // Run lint-staged in "deeper/"" + expect(await gitCommit({ cwd: path.join(cwd, 'deeper') })).resolves + + expect(console.printHistory()).toMatch('No staged files match any configured task') + + // File outside deeper/ was not fixed + expect(await readFile('file.js')).toEqual(testJsFileUgly) + }) }) describe('lintStaged', () => {