Skip to content

Commit

Permalink
fix: do not try to load configuration from files that are not checked…
Browse files Browse the repository at this point in the history
… out
  • Loading branch information
iiroj committed Feb 3, 2024
1 parent 999dcce commit fdcdad4
Show file tree
Hide file tree
Showing 3 changed files with 38 additions and 10 deletions.
5 changes: 5 additions & 0 deletions .changeset/wet-cups-jog.md
@@ -0,0 +1,5 @@
---
'lint-staged': patch
---

_Lint-staged_ no longer tries to load configuration from files that are not checked out. This might happen when using sparse-checkout.
23 changes: 14 additions & 9 deletions lib/searchConfigs.js
Expand Up @@ -13,10 +13,9 @@ import { CONFIG_FILE_NAMES } from './configFiles.js'

const debugLog = debug('lint-staged:searchConfigs')

const EXEC_GIT = ['ls-files', '-z', '--full-name']
const EXEC_GIT = ['ls-files', '-z', '--full-name', '-t']

const filterPossibleConfigFiles = (files) =>
files.filter((file) => CONFIG_FILE_NAMES.includes(path.basename(file)))
const filterPossibleConfigFiles = (file) => CONFIG_FILE_NAMES.includes(path.basename(file))

const numberOfLevels = (file) => file.split('/').length

Expand Down Expand Up @@ -58,17 +57,23 @@ export const searchConfigs = async (
return { [configPath]: validateConfig(config, filepath, logger) }
}

const [cachedFiles, otherFiles] = await Promise.all([
const [cachedFilesWithStatus, otherFilesWithStatus] = await Promise.all([
/** Get all possible config files known to git */
execGit(EXEC_GIT, { cwd: gitDir }).then(parseGitZOutput).then(filterPossibleConfigFiles),
execGit(EXEC_GIT, { cwd: gitDir }).then(parseGitZOutput),
/** Get all possible config files from uncommitted files */
execGit([...EXEC_GIT, '--others', '--exclude-standard'], { cwd: gitDir })
.then(parseGitZOutput)
.then(filterPossibleConfigFiles),
execGit([...EXEC_GIT, '--others', '--exclude-standard'], { cwd: gitDir }).then(parseGitZOutput),
])

/** Sort possible config files so that deepest is first */
const possibleConfigFiles = [...cachedFiles, ...otherFiles]
const possibleConfigFiles = [...cachedFilesWithStatus, ...otherFilesWithStatus]
.flatMap(
/**
* Leave out lines starting with "S " to ignore not-checked-out files in a sparse repo.
* The "S" status means a tracked file that is "skip-worktree"
* @see https://git-scm.com/docs/git-ls-files#Documentation/git-ls-files.txt--t
*/ (line) => (line.startsWith('S ') ? [] : [line.replace(/^[HSMRCK?U] /, '')])
)
.filter(filterPossibleConfigFiles)
.map((file) => normalizePath(path.join(gitDir, file)))
.filter(isInsideDirectory(cwd))
.sort(sortDeepestParth)
Expand Down
20 changes: 19 additions & 1 deletion test/unit/searchConfigs.spec.js
Expand Up @@ -101,7 +101,7 @@ describe('searchConfigs', () => {
const config = { '*.js': 'eslint' }

execGit.mockResolvedValueOnce(
`.lintstagedrc.json\u0000even/deeper/.lintstagedrc.json\u0000deeper/.lintstagedrc.json\u0000`
`H .lintstagedrc.json\u0000H even/deeper/.lintstagedrc.json\u0000H deeper/.lintstagedrc.json\u0000`
)

const topLevelConfig = normalizePath(path.join(process.cwd(), '.lintstagedrc.json'))
Expand All @@ -118,4 +118,22 @@ describe('searchConfigs', () => {

expect(Object.keys(configs)).toEqual([evenDeeperConfig, deeperConfig, topLevelConfig])
})

it('should ignore config files skipped from the worktree (sparse checkout)', async () => {
const config = { '*.js': 'eslint' }

execGit.mockResolvedValueOnce(`H .lintstagedrc.json\u0000S skipped/.lintstagedrc.json\u0000`)

const topLevelConfig = normalizePath(path.join(process.cwd(), '.lintstagedrc.json'))
const skippedConfig = normalizePath(path.join(process.cwd(), 'skipped/.lintstagedrc.json'))

loadConfig.mockResolvedValueOnce({ config, filepath: topLevelConfig })

// Mock will return config for skipped file, but it should not be read
loadConfig.mockResolvedValueOnce({ config, filepath: skippedConfig })

const configs = await searchConfigs({})

expect(Object.keys(configs)).toEqual([topLevelConfig])
})
})

0 comments on commit fdcdad4

Please sign in to comment.