Skip to content

Commit 877ab4c

Browse files
committed
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.
1 parent 7d36ef7 commit 877ab4c

File tree

3 files changed

+51
-13
lines changed

3 files changed

+51
-13
lines changed

lib/generateTasks.js

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,9 +21,7 @@ export const generateTasks = ({ config, cwd = process.cwd(), files, relative = f
2121

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

24-
return Object.entries(config).map(([rawPattern, commands]) => {
25-
let pattern = rawPattern
26-
24+
return Object.entries(config).map(([pattern, commands]) => {
2725
const isParentDirPattern = pattern.startsWith('../')
2826

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

lib/groupFilesByConfig.js

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -30,18 +30,24 @@ export const groupFilesByConfig = async ({ configs, files }) => {
3030
return relative && !relative.startsWith('..') && !path.isAbsolute(relative)
3131
}
3232

33-
const scopedFiles = new Set()
33+
/** This config should match all files since it has a parent glob */
34+
const includeAllFiles = Object.keys(config).some((glob) => glob.startsWith('..'))
35+
36+
const scopedFiles = new Set(includeAllFiles ? filesSet : undefined)
3437

3538
/**
36-
* If file is inside the config file's directory, assign it to that configuration
37-
* and remove it from the set. This means only one configuration can match a file.
39+
* Without a parent glob, if file is inside the config file's directory,
40+
* assign it to that configuration.
3841
*/
39-
filesSet.forEach((file) => {
40-
if (isInsideDir(file)) {
41-
scopedFiles.add(file)
42-
}
43-
})
42+
if (!includeAllFiles) {
43+
filesSet.forEach((file) => {
44+
if (isInsideDir(file)) {
45+
scopedFiles.add(file)
46+
}
47+
})
48+
}
4449

50+
/** Files should only match a single config */
4551
scopedFiles.forEach((file) => {
4652
filesSet.delete(file)
4753
})

test/integration.test.js

Lines changed: 35 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1217,7 +1217,7 @@ describe('lint-staged', () => {
12171217
await gitCommit({ shell: true, cwd: path.join(cwd, 'deeper') })
12181218

12191219
// 'file.js' was ignored
1220-
expect(await readFile('file.js')).toMatch('')
1220+
expect(await readFile('file.js')).toEqual('')
12211221

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

12311231
// 'a/very/deep/file/path/file.js' was ignored
1232-
expect(await readFile('a/very/deep/file/path/file.js')).toMatch('')
1232+
expect(await readFile('a/very/deep/file/path/file.js')).toEqual('')
1233+
})
1234+
1235+
it('should support parent globs', async () => {
1236+
// Add some empty files
1237+
await writeFile('file.js', '')
1238+
await writeFile('deeper/file.js', '')
1239+
await writeFile('deeper/even/file.js', '')
1240+
await writeFile('deeper/even/deeper/file.js', '')
1241+
await writeFile('a/very/deep/file/path/file.js', '')
1242+
1243+
// Include single-level parent glob in deeper config
1244+
await writeFile(
1245+
'deeper/even/.lintstagedrc.cjs',
1246+
`module.exports = { '../*.js': (files) => files.map((f) => \`echo level-2 > \${f}\`) }`
1247+
)
1248+
1249+
// Stage all files
1250+
await execGit(['add', '.'])
1251+
1252+
// Run lint-staged with `--shell` so that tasks do their thing
1253+
// Run in 'deeper/' so that root config is ignored
1254+
await gitCommit({ shell: true, cwd: path.join(cwd, 'deeper/even') })
1255+
1256+
// Two levels above, no match
1257+
expect(await readFile('file.js')).toEqual('')
1258+
1259+
// One level above, match
1260+
expect(await readFile('deeper/file.js')).toMatch('level-2')
1261+
1262+
// Not directly in the above-level, no match
1263+
expect(await readFile('deeper/even/file.js')).toEqual('')
1264+
expect(await readFile('deeper/even/deeper/file.js')).toEqual('')
1265+
expect(await readFile('a/very/deep/file/path/file.js')).toEqual('')
12331266
})
12341267

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

0 commit comments

Comments
 (0)