Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
fix: restore functionality of parent globs for a single configuration…
… file

Parent globs like `../*.js` were broken when introducing support
for multiple configuration files in version 12.2.0. This is because
lint-staged incorrectly grouped all files inside a single
configuration's base directory to belong to that config, meaning
files can only match a single configuration.

This has now been fixed so that when a configuration has a
parent glob, it will receive all staged files. Currently this means
there can only be a single configuration file containing a parent
glob, because all files will belong to that config.

This aims to restore the previous behaviour, where only a
single configuration file was supported. It shouldn't be necessary
to use parent globs anymore, because they can be replaced by
using multiple configuration files instead.
  • Loading branch information
iiroj committed Apr 20, 2022
1 parent 7d36ef7 commit 877ab4c
Show file tree
Hide file tree
Showing 3 changed files with 51 additions and 13 deletions.
5 changes: 2 additions & 3 deletions lib/generateTasks.js
Expand Up @@ -21,9 +21,7 @@ export const generateTasks = ({ config, cwd = process.cwd(), files, relative = f

const relativeFiles = files.map((file) => normalize(path.relative(cwd, file)))

return Object.entries(config).map(([rawPattern, commands]) => {
let pattern = rawPattern

return Object.entries(config).map(([pattern, commands]) => {
const isParentDirPattern = pattern.startsWith('../')

// Only worry about children of the CWD unless the pattern explicitly
Expand All @@ -40,6 +38,7 @@ export const generateTasks = ({ config, cwd = process.cwd(), files, relative = f
// match against filenames in every directory. This makes `*.js`
// match both `test.js` and `subdirectory/test.js`.
matchBase: !pattern.includes('/'),
posixSlashes: true,
strictBrackets: true,
})

Expand Down
22 changes: 14 additions & 8 deletions lib/groupFilesByConfig.js
Expand Up @@ -30,18 +30,24 @@ export const groupFilesByConfig = async ({ configs, files }) => {
return relative && !relative.startsWith('..') && !path.isAbsolute(relative)
}

const scopedFiles = new Set()
/** This config should match all files since it has a parent glob */
const includeAllFiles = Object.keys(config).some((glob) => glob.startsWith('..'))

const scopedFiles = new Set(includeAllFiles ? filesSet : undefined)

/**
* If file is inside the config file's directory, assign it to that configuration
* and remove it from the set. This means only one configuration can match a file.
* Without a parent glob, if file is inside the config file's directory,
* assign it to that configuration.
*/
filesSet.forEach((file) => {
if (isInsideDir(file)) {
scopedFiles.add(file)
}
})
if (!includeAllFiles) {
filesSet.forEach((file) => {
if (isInsideDir(file)) {
scopedFiles.add(file)
}
})
}

/** Files should only match a single config */
scopedFiles.forEach((file) => {
filesSet.delete(file)
})
Expand Down
37 changes: 35 additions & 2 deletions test/integration.test.js
Expand Up @@ -1217,7 +1217,7 @@ describe('lint-staged', () => {
await gitCommit({ shell: true, cwd: path.join(cwd, 'deeper') })

// 'file.js' was ignored
expect(await readFile('file.js')).toMatch('')
expect(await readFile('file.js')).toEqual('')

// 'deeper/file.js' matched 'deeper/.lintstagedrc.json'
expect(await readFile('deeper/file.js')).toMatch('level-1')
Expand All @@ -1229,7 +1229,40 @@ describe('lint-staged', () => {
expect(await readFile('deeper/even/deeper/file.js')).toMatch('level-2')

// 'a/very/deep/file/path/file.js' was ignored
expect(await readFile('a/very/deep/file/path/file.js')).toMatch('')
expect(await readFile('a/very/deep/file/path/file.js')).toEqual('')
})

it('should support parent globs', async () => {
// Add some empty files
await writeFile('file.js', '')
await writeFile('deeper/file.js', '')
await writeFile('deeper/even/file.js', '')
await writeFile('deeper/even/deeper/file.js', '')
await writeFile('a/very/deep/file/path/file.js', '')

// Include single-level parent glob in deeper config
await writeFile(
'deeper/even/.lintstagedrc.cjs',
`module.exports = { '../*.js': (files) => files.map((f) => \`echo level-2 > \${f}\`) }`
)

// Stage all files
await execGit(['add', '.'])

// Run lint-staged with `--shell` so that tasks do their thing
// Run in 'deeper/' so that root config is ignored
await gitCommit({ shell: true, cwd: path.join(cwd, 'deeper/even') })

// Two levels above, no match
expect(await readFile('file.js')).toEqual('')

// One level above, match
expect(await readFile('deeper/file.js')).toMatch('level-2')

// Not directly in the above-level, no match
expect(await readFile('deeper/even/file.js')).toEqual('')
expect(await readFile('deeper/even/deeper/file.js')).toEqual('')
expect(await readFile('a/very/deep/file/path/file.js')).toEqual('')
})

it('should not care about staged file outside current cwd with another staged file', async () => {
Expand Down

0 comments on commit 877ab4c

Please sign in to comment.