lint-staged 13.2.3 → 13.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +10 -8
- package/bin/lint-staged.js +9 -13
- package/lib/chunkFiles.js +3 -2
- package/lib/generateTasks.js +4 -3
- package/lib/getRenderer.js +50 -14
- package/lib/getStagedFiles.js +2 -3
- package/lib/makeCmdTasks.js +2 -23
- package/lib/messages.js +3 -4
- package/lib/normalizePath.js +50 -0
- package/lib/resolveGitRepo.js +7 -7
- package/lib/runAll.js +4 -5
- package/lib/searchConfigs.js +3 -3
- package/lib/validateBraces.js +30 -6
- package/lib/validateConfig.js +3 -2
- package/package.json +25 -29
package/README.md
CHANGED
|
@@ -58,7 +58,7 @@ To install _lint-staged_ in the recommended way, you need to:
|
|
|
58
58
|
1. Install some linters, like [ESLint](https://eslint.org) or [Prettier](https://prettier.io)
|
|
59
59
|
1. Configure _lint-staged_ to run linters and other tasks:
|
|
60
60
|
- for example: `{ "*.js": "eslint" }` to run ESLint for all staged JS files
|
|
61
|
-
- See [Configuration](#
|
|
61
|
+
- See [Configuration](#configuration) for more info
|
|
62
62
|
|
|
63
63
|
Don't forget to commit changes to `package.json` and `.husky` to share this setup with your team!
|
|
64
64
|
|
|
@@ -72,6 +72,10 @@ See [Releases](https://github.com/okonet/lint-staged/releases).
|
|
|
72
72
|
|
|
73
73
|
### Migration
|
|
74
74
|
|
|
75
|
+
#### v14
|
|
76
|
+
|
|
77
|
+
- Since `v14.0.0` _lint-staged_ no longer supports Node.js 14. Please upgrade your Node.js version to at least `16.14.0`.
|
|
78
|
+
|
|
75
79
|
#### v13
|
|
76
80
|
|
|
77
81
|
- Since `v13.0.0` _lint-staged_ no longer supports Node.js 12. Please upgrade your Node.js version to at least `14.13.1`, or `16.0.0` onward.
|
|
@@ -218,11 +222,11 @@ If necessary, you can limit the concurrency using `--concurrent <number>` or dis
|
|
|
218
222
|
Linter commands work on a subset of all staged files, defined by a _glob pattern_. lint-staged uses [micromatch](https://github.com/micromatch/micromatch) for matching files with the following rules:
|
|
219
223
|
|
|
220
224
|
- If the glob pattern contains no slashes (`/`), micromatch's `matchBase` option will enabled, so globs match a file's basename regardless of directory:
|
|
221
|
-
-
|
|
222
|
-
-
|
|
225
|
+
- `"*.js"` will match all JS files, like `/test.js` and `/foo/bar/test.js`
|
|
226
|
+
- `"!(*test).js"` will match all JS files, except those ending in `test.js`, so `foo.js` but not `foo.test.js`
|
|
223
227
|
- If the glob pattern does contain a slash (`/`), it will match for paths as well:
|
|
224
|
-
-
|
|
225
|
-
-
|
|
228
|
+
- `"./*.js"` will match all JS files in the git repo root, so `/test.js` but not `/foo/bar/test.js`
|
|
229
|
+
- `"foo/**/*.js"` will match all JS files inside the `/foo` directory, so `/foo/bar/test.js` but not `/test.js`
|
|
226
230
|
|
|
227
231
|
When matching, lint-staged will do the following
|
|
228
232
|
|
|
@@ -624,9 +628,7 @@ See more on [this blog post](https://medium.com/@tomchentw/imagemin-lint-staged-
|
|
|
624
628
|
const path = require('path')
|
|
625
629
|
|
|
626
630
|
const buildEslintCommand = (filenames) =>
|
|
627
|
-
`next lint --fix --file ${filenames
|
|
628
|
-
.map((f) => path.relative(process.cwd(), f))
|
|
629
|
-
.join(' --file ')}`
|
|
631
|
+
`next lint --fix --file ${filenames.map((f) => path.relative(process.cwd(), f)).join(' --file ')}`
|
|
630
632
|
|
|
631
633
|
module.exports = {
|
|
632
634
|
'*.{js,jsx,ts,tsx}': [buildEslintCommand],
|
package/bin/lint-staged.js
CHANGED
|
@@ -1,8 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
|
-
import fs from 'node:fs'
|
|
4
|
-
import path from 'node:path'
|
|
5
|
-
import { fileURLToPath } from 'node:url'
|
|
3
|
+
import fs from 'node:fs/promises'
|
|
6
4
|
|
|
7
5
|
import { supportsColor } from 'chalk'
|
|
8
6
|
import { Option, program } from 'commander'
|
|
@@ -19,8 +17,7 @@ if (supportsColor) {
|
|
|
19
17
|
// Do not terminate main Listr process on SIGINT
|
|
20
18
|
process.on('SIGINT', () => {})
|
|
21
19
|
|
|
22
|
-
const
|
|
23
|
-
const packageJson = JSON.parse(fs.readFileSync(packageJsonPath))
|
|
20
|
+
const packageJson = JSON.parse(await fs.readFile(new URL('../package.json', import.meta.url)))
|
|
24
21
|
const version = packageJson.version
|
|
25
22
|
|
|
26
23
|
const debugLog = debug('lint-staged:bin')
|
|
@@ -113,7 +110,7 @@ debugLog('Options parsed from command-line:', options)
|
|
|
113
110
|
if (options.configPath === '-') {
|
|
114
111
|
delete options.configPath
|
|
115
112
|
try {
|
|
116
|
-
options.config = fs.
|
|
113
|
+
options.config = await fs.readFile(process.stdin.fd, 'utf8').toString().trim()
|
|
117
114
|
} catch {
|
|
118
115
|
console.error(CONFIG_STDIN_ERROR)
|
|
119
116
|
process.exit(1)
|
|
@@ -126,10 +123,9 @@ if (options.configPath === '-') {
|
|
|
126
123
|
}
|
|
127
124
|
}
|
|
128
125
|
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
.
|
|
134
|
-
|
|
135
|
-
})
|
|
126
|
+
try {
|
|
127
|
+
const passed = await lintStaged(options)
|
|
128
|
+
process.exitCode = passed ? 0 : 1
|
|
129
|
+
} catch {
|
|
130
|
+
process.exitCode = 1
|
|
131
|
+
}
|
package/lib/chunkFiles.js
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import path from 'node:path'
|
|
2
2
|
|
|
3
3
|
import debug from 'debug'
|
|
4
|
-
|
|
4
|
+
|
|
5
|
+
import { normalizePath } from './normalizePath.js'
|
|
5
6
|
|
|
6
7
|
const debugLog = debug('lint-staged:chunkFiles')
|
|
7
8
|
|
|
@@ -35,7 +36,7 @@ const chunkArray = (arr, chunkCount) => {
|
|
|
35
36
|
*/
|
|
36
37
|
export const chunkFiles = ({ files, baseDir, maxArgLength = null, relative = false }) => {
|
|
37
38
|
const normalizedFiles = files.map((file) =>
|
|
38
|
-
|
|
39
|
+
normalizePath(relative || !baseDir ? file : path.resolve(baseDir, file))
|
|
39
40
|
)
|
|
40
41
|
|
|
41
42
|
if (!maxArgLength) {
|
package/lib/generateTasks.js
CHANGED
|
@@ -2,7 +2,8 @@ import path from 'node:path'
|
|
|
2
2
|
|
|
3
3
|
import debug from 'debug'
|
|
4
4
|
import micromatch from 'micromatch'
|
|
5
|
-
|
|
5
|
+
|
|
6
|
+
import { normalizePath } from './normalizePath.js'
|
|
6
7
|
|
|
7
8
|
const debugLog = debug('lint-staged:generateTasks')
|
|
8
9
|
|
|
@@ -19,7 +20,7 @@ const debugLog = debug('lint-staged:generateTasks')
|
|
|
19
20
|
export const generateTasks = ({ config, cwd = process.cwd(), files, relative = false }) => {
|
|
20
21
|
debugLog('Generating linter tasks')
|
|
21
22
|
|
|
22
|
-
const relativeFiles = files.map((file) =>
|
|
23
|
+
const relativeFiles = files.map((file) => normalizePath(path.relative(cwd, file)))
|
|
23
24
|
|
|
24
25
|
return Object.entries(config).map(([pattern, commands]) => {
|
|
25
26
|
const isParentDirPattern = pattern.startsWith('../')
|
|
@@ -42,7 +43,7 @@ export const generateTasks = ({ config, cwd = process.cwd(), files, relative = f
|
|
|
42
43
|
strictBrackets: true,
|
|
43
44
|
})
|
|
44
45
|
|
|
45
|
-
const fileList = matches.map((file) =>
|
|
46
|
+
const fileList = matches.map((file) => normalizePath(relative ? file : path.resolve(cwd, file)))
|
|
46
47
|
|
|
47
48
|
const task = { pattern, commands, fileList }
|
|
48
49
|
debugLog('Generated task: \n%O', task)
|
package/lib/getRenderer.js
CHANGED
|
@@ -1,28 +1,64 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
import { EOL } from 'node:os'
|
|
2
|
+
import { Writable } from 'node:stream'
|
|
3
|
+
|
|
4
|
+
import { ListrLogger, ProcessOutput } from 'listr2'
|
|
5
|
+
|
|
6
|
+
const EOLRegex = new RegExp(EOL + '$')
|
|
7
|
+
|
|
8
|
+
const bindLogger = (consoleLogMethod) =>
|
|
9
|
+
new Writable({
|
|
10
|
+
write: function (chunk, encoding, next) {
|
|
11
|
+
consoleLogMethod(chunk.toString().replace(EOLRegex, ''))
|
|
12
|
+
next()
|
|
13
|
+
},
|
|
14
|
+
})
|
|
15
|
+
|
|
16
|
+
const getMainRendererOptions = ({ debug, quiet }, logger, env) => {
|
|
17
|
+
if (quiet) {
|
|
18
|
+
return {
|
|
19
|
+
renderer: 'silent',
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
if (env.NODE_ENV === 'test') {
|
|
24
|
+
return {
|
|
25
|
+
renderer: 'test',
|
|
26
|
+
rendererOptions: {
|
|
27
|
+
logger: new ListrLogger({
|
|
28
|
+
processOutput: new ProcessOutput(bindLogger(logger.log), bindLogger(logger.error)),
|
|
29
|
+
}),
|
|
30
|
+
},
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
3
34
|
// Better support for dumb terminals: https://en.wikipedia.org/wiki/Computer_terminal#Dumb_terminals
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
}
|
|
35
|
+
if (debug || env.TERM === 'dumb') {
|
|
36
|
+
return {
|
|
37
|
+
renderer: 'verbose',
|
|
38
|
+
}
|
|
39
|
+
}
|
|
8
40
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
41
|
+
return {
|
|
42
|
+
renderer: 'update',
|
|
43
|
+
rendererOptions: {
|
|
44
|
+
formatOutput: 'truncate',
|
|
45
|
+
},
|
|
12
46
|
}
|
|
47
|
+
}
|
|
13
48
|
|
|
14
|
-
|
|
15
|
-
if (Number(FORCE_COLOR) > 0) {
|
|
49
|
+
const getFallbackRenderer = ({ renderer }, { FORCE_COLOR }) => {
|
|
50
|
+
if (renderer === 'silent' || renderer === 'test' || Number(FORCE_COLOR) > 0) {
|
|
16
51
|
return renderer
|
|
17
52
|
}
|
|
18
53
|
|
|
19
54
|
return 'verbose'
|
|
20
55
|
}
|
|
21
56
|
|
|
22
|
-
export const getRenderer = (options, env = process.env) => {
|
|
23
|
-
const mainRendererOptions = getMainRendererOptions(options, env)
|
|
57
|
+
export const getRenderer = (options, logger, env = process.env) => {
|
|
58
|
+
const mainRendererOptions = getMainRendererOptions(options, logger, env)
|
|
59
|
+
|
|
24
60
|
return {
|
|
25
61
|
...mainRendererOptions,
|
|
26
|
-
|
|
62
|
+
fallbackRenderer: getFallbackRenderer(mainRendererOptions, env),
|
|
27
63
|
}
|
|
28
64
|
}
|
package/lib/getStagedFiles.js
CHANGED
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
import path from 'node:path'
|
|
2
2
|
|
|
3
|
-
import normalize from 'normalize-path'
|
|
4
|
-
|
|
5
3
|
import { execGit } from './execGit.js'
|
|
6
4
|
import { getDiffCommand } from './getDiffCommand.js'
|
|
5
|
+
import { normalizePath } from './normalizePath.js'
|
|
7
6
|
import { parseGitZOutput } from './parseGitZOutput.js'
|
|
8
7
|
|
|
9
8
|
export const getStagedFiles = async ({ cwd = process.cwd(), diff, diffFilter } = {}) => {
|
|
@@ -11,7 +10,7 @@ export const getStagedFiles = async ({ cwd = process.cwd(), diff, diffFilter } =
|
|
|
11
10
|
const lines = await execGit(getDiffCommand(diff, diffFilter), { cwd })
|
|
12
11
|
if (!lines) return []
|
|
13
12
|
|
|
14
|
-
return parseGitZOutput(lines).map((file) =>
|
|
13
|
+
return parseGitZOutput(lines).map((file) => normalizePath(path.resolve(cwd, file)))
|
|
15
14
|
} catch {
|
|
16
15
|
return null
|
|
17
16
|
}
|
package/lib/makeCmdTasks.js
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import cliTruncate from 'cli-truncate'
|
|
2
1
|
import debug from 'debug'
|
|
3
2
|
|
|
4
3
|
import { configurationError } from './messages.js'
|
|
@@ -6,23 +5,6 @@ import { resolveTaskFn } from './resolveTaskFn.js'
|
|
|
6
5
|
|
|
7
6
|
const debugLog = debug('lint-staged:makeCmdTasks')
|
|
8
7
|
|
|
9
|
-
const STDOUT_COLUMNS_DEFAULT = 80
|
|
10
|
-
|
|
11
|
-
const listrPrefixLength = {
|
|
12
|
-
update: ` X `.length, // indented task title where X is a checkmark or a cross (failure)
|
|
13
|
-
verbose: `[STARTED] `.length, // verbose renderer uses 7-letter STARTED/SUCCESS prefixes
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
/**
|
|
17
|
-
* Get length of title based on the number of available columns prefix length
|
|
18
|
-
* @param {string} renderer The name of the Listr renderer
|
|
19
|
-
* @returns {number}
|
|
20
|
-
*/
|
|
21
|
-
const getTitleLength = (renderer, columns = process.stdout.columns) => {
|
|
22
|
-
const prefixLength = listrPrefixLength[renderer] || 0
|
|
23
|
-
return (columns || STDOUT_COLUMNS_DEFAULT) - prefixLength
|
|
24
|
-
}
|
|
25
|
-
|
|
26
8
|
/**
|
|
27
9
|
* Creates and returns an array of listr tasks which map to the given commands.
|
|
28
10
|
*
|
|
@@ -31,11 +13,10 @@ const getTitleLength = (renderer, columns = process.stdout.columns) => {
|
|
|
31
13
|
* @param {string} options.cwd
|
|
32
14
|
* @param {Array<string>} options.files
|
|
33
15
|
* @param {string} options.gitDir
|
|
34
|
-
* @param {string} options.renderer
|
|
35
16
|
* @param {Boolean} shell
|
|
36
17
|
* @param {Boolean} verbose
|
|
37
18
|
*/
|
|
38
|
-
export const makeCmdTasks = async ({ commands, cwd, files, gitDir,
|
|
19
|
+
export const makeCmdTasks = async ({ commands, cwd, files, gitDir, shell, verbose }) => {
|
|
39
20
|
debugLog('Creating listr tasks for commands %o', commands)
|
|
40
21
|
const commandArray = Array.isArray(commands) ? commands : [commands]
|
|
41
22
|
const cmdTasks = []
|
|
@@ -60,10 +41,8 @@ export const makeCmdTasks = async ({ commands, cwd, files, gitDir, renderer, she
|
|
|
60
41
|
)
|
|
61
42
|
}
|
|
62
43
|
|
|
63
|
-
// Truncate title to single line based on renderer
|
|
64
|
-
const title = cliTruncate(command, getTitleLength(renderer))
|
|
65
44
|
const task = resolveTaskFn({ command, cwd, files, gitDir, isFn, shell, verbose })
|
|
66
|
-
cmdTasks.push({ title, command, task })
|
|
45
|
+
cmdTasks.push({ title: command, command, task })
|
|
67
46
|
}
|
|
68
47
|
}
|
|
69
48
|
|
package/lib/messages.js
CHANGED
|
@@ -1,14 +1,13 @@
|
|
|
1
|
+
import { inspect } from 'node:util'
|
|
2
|
+
|
|
1
3
|
import chalk from 'chalk'
|
|
2
|
-
import inspect from 'object-inspect'
|
|
3
4
|
|
|
4
5
|
import { error, info, warning } from './figures.js'
|
|
5
6
|
|
|
6
7
|
export const configurationError = (opt, helpMsg, value) =>
|
|
7
8
|
`${chalk.redBright(`${error} Validation Error:`)}
|
|
8
9
|
|
|
9
|
-
Invalid value for '${chalk.bold(opt)}': ${chalk.bold(
|
|
10
|
-
inspect(value, { inlineCharacterLimit: Number.POSITIVE_INFINITY })
|
|
11
|
-
)}
|
|
10
|
+
Invalid value for '${chalk.bold(opt)}': ${chalk.bold(inspect(value))}
|
|
12
11
|
|
|
13
12
|
${helpMsg}`
|
|
14
13
|
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Reimplementation of "normalize-path"
|
|
3
|
+
* @see https://github.com/jonschlinkert/normalize-path/blob/52c3a95ebebc2d98c1ad7606cbafa7e658656899/index.js
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
/*!
|
|
7
|
+
* normalize-path <https://github.com/jonschlinkert/normalize-path>
|
|
8
|
+
*
|
|
9
|
+
* Copyright (c) 2014-2018, Jon Schlinkert.
|
|
10
|
+
* Released under the MIT License.
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
import path from 'node:path'
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* A file starting with \\?\
|
|
17
|
+
* @see https://learn.microsoft.com/en-us/windows/win32/fileio/naming-a-file#win32-file-namespaces
|
|
18
|
+
*/
|
|
19
|
+
const WIN32_FILE_NS = '\\\\?\\'
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* A file starting with \\.\
|
|
23
|
+
* @see https://learn.microsoft.com/en-us/windows/win32/fileio/naming-a-file#win32-file-namespaces
|
|
24
|
+
*/
|
|
25
|
+
const WIN32_DEVICE_NS = '\\\\.\\'
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Normalize input file path to use POSIX separators
|
|
29
|
+
* @param {String} input
|
|
30
|
+
* @returns String
|
|
31
|
+
*/
|
|
32
|
+
export const normalizePath = (input) => {
|
|
33
|
+
if (input === path.posix.sep || input === path.win32.sep) {
|
|
34
|
+
return path.posix.sep
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
let normalized = input.split(/[/\\]+/).join(path.posix.sep)
|
|
38
|
+
|
|
39
|
+
/** Handle win32 Namespaced paths by changing e.g. \\.\ to //./ */
|
|
40
|
+
if (input.startsWith(WIN32_FILE_NS) || input.startsWith(WIN32_DEVICE_NS)) {
|
|
41
|
+
normalized = normalized.replace(/^\/(\.|\?)/, '//$1')
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/** Remove trailing slash */
|
|
45
|
+
if (normalized.endsWith(path.posix.sep)) {
|
|
46
|
+
normalized = normalized.slice(0, -1)
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
return normalized
|
|
50
|
+
}
|
package/lib/resolveGitRepo.js
CHANGED
|
@@ -2,10 +2,10 @@ import fs from 'node:fs/promises'
|
|
|
2
2
|
import path from 'node:path'
|
|
3
3
|
|
|
4
4
|
import debug from 'debug'
|
|
5
|
-
import normalize from 'normalize-path'
|
|
6
5
|
|
|
7
6
|
import { execGit } from './execGit.js'
|
|
8
7
|
import { readFile } from './file.js'
|
|
8
|
+
import { normalizePath } from './normalizePath.js'
|
|
9
9
|
|
|
10
10
|
const debugLog = debug('lint-staged:resolveGitRepo')
|
|
11
11
|
|
|
@@ -15,7 +15,7 @@ const debugLog = debug('lint-staged:resolveGitRepo')
|
|
|
15
15
|
*/
|
|
16
16
|
const resolveGitConfigDir = async (gitDir) => {
|
|
17
17
|
// Get the real path in case it's a symlink
|
|
18
|
-
const defaultDir =
|
|
18
|
+
const defaultDir = normalizePath(await fs.realpath(path.join(gitDir, '.git')))
|
|
19
19
|
const stats = await fs.lstat(defaultDir)
|
|
20
20
|
// If .git is a directory, use it
|
|
21
21
|
if (stats.isDirectory()) return defaultDir
|
|
@@ -33,10 +33,10 @@ export const determineGitDir = (cwd, relativeDir) => {
|
|
|
33
33
|
}
|
|
34
34
|
if (relativeDir) {
|
|
35
35
|
// the current working dir is inside the git top-level directory
|
|
36
|
-
return
|
|
36
|
+
return normalizePath(cwd.substring(0, cwd.lastIndexOf(relativeDir)))
|
|
37
37
|
} else {
|
|
38
38
|
// the current working dir is the top-level git directory
|
|
39
|
-
return
|
|
39
|
+
return normalizePath(cwd)
|
|
40
40
|
}
|
|
41
41
|
}
|
|
42
42
|
|
|
@@ -55,9 +55,9 @@ export const resolveGitRepo = async (cwd = process.cwd()) => {
|
|
|
55
55
|
|
|
56
56
|
// read the path of the current directory relative to the top-level directory
|
|
57
57
|
// don't read the toplevel directly, it will lead to an posix conform path on non posix systems (cygwin)
|
|
58
|
-
const gitRel =
|
|
59
|
-
const gitDir = determineGitDir(
|
|
60
|
-
const gitConfigDir =
|
|
58
|
+
const gitRel = normalizePath(await execGit(['rev-parse', '--show-prefix'], { cwd }))
|
|
59
|
+
const gitDir = determineGitDir(normalizePath(cwd), gitRel)
|
|
60
|
+
const gitConfigDir = normalizePath(await resolveGitConfigDir(gitDir))
|
|
61
61
|
|
|
62
62
|
debugLog('Resolved git directory to be `%s`', gitDir)
|
|
63
63
|
debugLog('Resolved git config directory to be `%s`', gitConfigDir)
|
package/lib/runAll.js
CHANGED
|
@@ -5,7 +5,6 @@ import path from 'node:path'
|
|
|
5
5
|
import chalk from 'chalk'
|
|
6
6
|
import debug from 'debug'
|
|
7
7
|
import { Listr } from 'listr2'
|
|
8
|
-
import normalize from 'normalize-path'
|
|
9
8
|
|
|
10
9
|
import { chunkFiles } from './chunkFiles.js'
|
|
11
10
|
import { execGit } from './execGit.js'
|
|
@@ -24,6 +23,7 @@ import {
|
|
|
24
23
|
SKIPPED_GIT_ERROR,
|
|
25
24
|
skippingBackup,
|
|
26
25
|
} from './messages.js'
|
|
26
|
+
import { normalizePath } from './normalizePath.js'
|
|
27
27
|
import { resolveGitRepo } from './resolveGitRepo.js'
|
|
28
28
|
import {
|
|
29
29
|
applyModificationsSkipped,
|
|
@@ -151,7 +151,7 @@ export const runAll = async (
|
|
|
151
151
|
ctx,
|
|
152
152
|
exitOnError: false,
|
|
153
153
|
registerSignalListeners: false,
|
|
154
|
-
...getRenderer({ debug, quiet }),
|
|
154
|
+
...getRenderer({ debug, quiet }, logger),
|
|
155
155
|
}
|
|
156
156
|
|
|
157
157
|
const listrTasks = []
|
|
@@ -160,7 +160,7 @@ export const runAll = async (
|
|
|
160
160
|
const matchedFiles = new Set()
|
|
161
161
|
|
|
162
162
|
for (const [configPath, { config, files }] of Object.entries(filesByConfig)) {
|
|
163
|
-
const configName = configPath ?
|
|
163
|
+
const configName = configPath ? normalizePath(path.relative(cwd, configPath)) : 'Config object'
|
|
164
164
|
|
|
165
165
|
const stagedFileChunks = chunkFiles({ baseDir: gitDir, files, maxArgLength, relative })
|
|
166
166
|
|
|
@@ -182,7 +182,6 @@ export const runAll = async (
|
|
|
182
182
|
cwd: groupCwd,
|
|
183
183
|
files: task.fileList,
|
|
184
184
|
gitDir,
|
|
185
|
-
renderer: listrOptions.renderer,
|
|
186
185
|
shell,
|
|
187
186
|
verbose,
|
|
188
187
|
}).then((subTasks) => {
|
|
@@ -193,7 +192,7 @@ export const runAll = async (
|
|
|
193
192
|
// relative filenames in the entire set.
|
|
194
193
|
const normalizedFile = path.isAbsolute(file)
|
|
195
194
|
? file
|
|
196
|
-
:
|
|
195
|
+
: normalizePath(path.join(groupCwd, file))
|
|
197
196
|
|
|
198
197
|
matchedFiles.add(normalizedFile)
|
|
199
198
|
})
|
package/lib/searchConfigs.js
CHANGED
|
@@ -3,10 +3,10 @@
|
|
|
3
3
|
import path from 'node:path'
|
|
4
4
|
|
|
5
5
|
import debug from 'debug'
|
|
6
|
-
import normalize from 'normalize-path'
|
|
7
6
|
|
|
8
7
|
import { execGit } from './execGit.js'
|
|
9
8
|
import { loadConfig, searchPlaces } from './loadConfig.js'
|
|
9
|
+
import { normalizePath } from './normalizePath.js'
|
|
10
10
|
import { parseGitZOutput } from './parseGitZOutput.js'
|
|
11
11
|
import { validateConfig } from './validateConfig.js'
|
|
12
12
|
|
|
@@ -21,7 +21,7 @@ const numberOfLevels = (file) => file.split('/').length
|
|
|
21
21
|
|
|
22
22
|
const sortDeepestParth = (a, b) => (numberOfLevels(a) > numberOfLevels(b) ? -1 : 1)
|
|
23
23
|
|
|
24
|
-
const isInsideDirectory = (dir) => (file) => file.startsWith(
|
|
24
|
+
const isInsideDirectory = (dir) => (file) => file.startsWith(normalizePath(dir))
|
|
25
25
|
|
|
26
26
|
/**
|
|
27
27
|
* Search all config files from the git repository, preferring those inside `cwd`.
|
|
@@ -68,7 +68,7 @@ export const searchConfigs = async (
|
|
|
68
68
|
|
|
69
69
|
/** Sort possible config files so that deepest is first */
|
|
70
70
|
const possibleConfigFiles = [...cachedFiles, ...otherFiles]
|
|
71
|
-
.map((file) =>
|
|
71
|
+
.map((file) => normalizePath(path.join(gitDir, file)))
|
|
72
72
|
.filter(isInsideDirectory(cwd))
|
|
73
73
|
.sort(sortDeepestParth)
|
|
74
74
|
|
package/lib/validateBraces.js
CHANGED
|
@@ -17,8 +17,8 @@ import { incorrectBraces } from './messages.js'
|
|
|
17
17
|
* braces from user configuration, but this is left to the user (after seeing the warning).
|
|
18
18
|
*
|
|
19
19
|
* @example <caption>Globs with brace expansions</caption>
|
|
20
|
-
* - *.{js,tx}
|
|
21
|
-
* - *.{{j,t}s,css}
|
|
20
|
+
* - *.{js,tx} // expanded as *.js, *.ts
|
|
21
|
+
* - *.{{j,t}s,css} // expanded as *.js, *.ts, *.css
|
|
22
22
|
* - file_{1..10}.css // expanded as file_1.css, file_2.css, …, file_10.css
|
|
23
23
|
*
|
|
24
24
|
* @example <caption>Globs with incorrect brace expansions</caption>
|
|
@@ -28,17 +28,17 @@ import { incorrectBraces } from './messages.js'
|
|
|
28
28
|
* - *.${js} // dollar-sign inhibits expansion, so treated literally
|
|
29
29
|
* - *.{js\,ts} // the comma is escaped, so treated literally
|
|
30
30
|
*/
|
|
31
|
-
export const
|
|
31
|
+
export const INCORRECT_BRACES_REGEXP = /(?<![\\$])({)(?:(?!(?<!\\),|\.\.|\{|\}).)*?(?<!\\)(})/g
|
|
32
32
|
|
|
33
33
|
/**
|
|
34
34
|
* @param {string} pattern
|
|
35
35
|
* @returns {string}
|
|
36
36
|
*/
|
|
37
|
-
const
|
|
37
|
+
const stripIncorrectBraces = (pattern) => {
|
|
38
38
|
let output = `${pattern}`
|
|
39
39
|
let match = null
|
|
40
40
|
|
|
41
|
-
while ((match =
|
|
41
|
+
while ((match = INCORRECT_BRACES_REGEXP.exec(pattern))) {
|
|
42
42
|
const fullMatch = match[0]
|
|
43
43
|
const withoutBraces = fullMatch.replace(/{/, '').replace(/}/, '')
|
|
44
44
|
output = output.replace(fullMatch, withoutBraces)
|
|
@@ -47,6 +47,30 @@ const withoutIncorrectBraces = (pattern) => {
|
|
|
47
47
|
return output
|
|
48
48
|
}
|
|
49
49
|
|
|
50
|
+
/**
|
|
51
|
+
* This RegExp matches "duplicate" opening and closing braces, without any other braces
|
|
52
|
+
* in between, where the duplication is redundant and should be removed.
|
|
53
|
+
*
|
|
54
|
+
* @example *.{{js,ts}} // should just be *.{js,ts}
|
|
55
|
+
*/
|
|
56
|
+
export const DOUBLE_BRACES_REGEXP = /{{[^}{]*}}/
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* @param {string} pattern
|
|
60
|
+
* @returns {string}
|
|
61
|
+
*/
|
|
62
|
+
const stripDoubleBraces = (pattern) => {
|
|
63
|
+
let output = `${pattern}`
|
|
64
|
+
const match = DOUBLE_BRACES_REGEXP.exec(pattern)?.[0]
|
|
65
|
+
|
|
66
|
+
if (match) {
|
|
67
|
+
const withoutBraces = match.replace('{{', '{').replace('}}', '}')
|
|
68
|
+
output = output.replace(match, withoutBraces)
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
return output
|
|
72
|
+
}
|
|
73
|
+
|
|
50
74
|
/**
|
|
51
75
|
* Validate and remove incorrect brace expansions from glob pattern.
|
|
52
76
|
* For example `*.{js}` is incorrect because it doesn't contain a `,` or `..`,
|
|
@@ -57,7 +81,7 @@ const withoutIncorrectBraces = (pattern) => {
|
|
|
57
81
|
* @returns {string}
|
|
58
82
|
*/
|
|
59
83
|
export const validateBraces = (pattern, logger) => {
|
|
60
|
-
const fixedPattern =
|
|
84
|
+
const fixedPattern = stripDoubleBraces(stripIncorrectBraces(pattern))
|
|
61
85
|
|
|
62
86
|
if (fixedPattern !== pattern) {
|
|
63
87
|
logger.warn(incorrectBraces(pattern, fixedPattern))
|
package/lib/validateConfig.js
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
/** @typedef {import('./index').Logger} Logger */
|
|
2
2
|
|
|
3
|
+
import { inspect } from 'node:util'
|
|
4
|
+
|
|
3
5
|
import debug from 'debug'
|
|
4
|
-
import inspect from 'object-inspect'
|
|
5
6
|
|
|
6
7
|
import { configurationError } from './messages.js'
|
|
7
8
|
import { ConfigEmptyError, ConfigFormatError } from './symbols.js'
|
|
@@ -109,7 +110,7 @@ See https://github.com/okonet/lint-staged#configuration.`)
|
|
|
109
110
|
}
|
|
110
111
|
|
|
111
112
|
debugLog('Validated config from `%s`:', configPath)
|
|
112
|
-
debugLog(inspect(config, {
|
|
113
|
+
debugLog(inspect(config, { compact: false }))
|
|
113
114
|
|
|
114
115
|
return validatedConfig
|
|
115
116
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "lint-staged",
|
|
3
|
-
"version": "13.
|
|
3
|
+
"version": "13.3.0",
|
|
4
4
|
"description": "Lint files staged by git",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": "https://github.com/okonet/lint-staged",
|
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
"url": "https://opencollective.com/lint-staged"
|
|
15
15
|
},
|
|
16
16
|
"engines": {
|
|
17
|
-
"node": "^14.
|
|
17
|
+
"node": "^16.14.0 || >=18.0.0"
|
|
18
18
|
},
|
|
19
19
|
"type": "module",
|
|
20
20
|
"bin": "./bin/lint-staged.js",
|
|
@@ -33,37 +33,33 @@
|
|
|
33
33
|
"test:watch": "jest --watch"
|
|
34
34
|
},
|
|
35
35
|
"dependencies": {
|
|
36
|
-
"chalk": "5.
|
|
37
|
-
"
|
|
38
|
-
"
|
|
39
|
-
"
|
|
40
|
-
"execa": "^7.0.0",
|
|
36
|
+
"chalk": "5.3.0",
|
|
37
|
+
"commander": "11.0.0",
|
|
38
|
+
"debug": "4.3.4",
|
|
39
|
+
"execa": "7.2.0",
|
|
41
40
|
"lilconfig": "2.1.0",
|
|
42
|
-
"listr2": "
|
|
43
|
-
"micromatch": "
|
|
44
|
-
"
|
|
45
|
-
"
|
|
46
|
-
"
|
|
47
|
-
"string-argv": "^0.3.1",
|
|
48
|
-
"yaml": "^2.2.2"
|
|
41
|
+
"listr2": "6.6.1",
|
|
42
|
+
"micromatch": "4.0.5",
|
|
43
|
+
"pidtree": "0.6.0",
|
|
44
|
+
"string-argv": "0.3.2",
|
|
45
|
+
"yaml": "2.3.1"
|
|
49
46
|
},
|
|
50
47
|
"devDependencies": {
|
|
51
|
-
"@babel/core": "
|
|
52
|
-
"@babel/eslint-parser": "
|
|
53
|
-
"@babel/preset-env": "
|
|
54
|
-
"babel-jest": "
|
|
48
|
+
"@babel/core": "7.22.10",
|
|
49
|
+
"@babel/eslint-parser": "7.22.10",
|
|
50
|
+
"@babel/preset-env": "7.22.10",
|
|
51
|
+
"babel-jest": "29.6.2",
|
|
55
52
|
"babel-plugin-transform-imports": "2.0.0",
|
|
56
|
-
"consolemock": "
|
|
57
|
-
"eslint": "
|
|
58
|
-
"eslint-config-prettier": "
|
|
59
|
-
"eslint-plugin-import": "
|
|
60
|
-
"eslint-plugin-node": "
|
|
61
|
-
"eslint-plugin-prettier": "
|
|
62
|
-
"
|
|
63
|
-
"
|
|
64
|
-
"jest": "
|
|
65
|
-
"
|
|
66
|
-
"prettier": "^2.8.4"
|
|
53
|
+
"consolemock": "1.1.0",
|
|
54
|
+
"eslint": "8.46.0",
|
|
55
|
+
"eslint-config-prettier": "9.0.0",
|
|
56
|
+
"eslint-plugin-import": "2.28.0",
|
|
57
|
+
"eslint-plugin-node": "11.1.0",
|
|
58
|
+
"eslint-plugin-prettier": "5.0.0",
|
|
59
|
+
"husky": "8.0.3",
|
|
60
|
+
"jest": "29.6.2",
|
|
61
|
+
"jest-snapshot-serializer-ansi": "2.1.0",
|
|
62
|
+
"prettier": "3.0.1"
|
|
67
63
|
},
|
|
68
64
|
"keywords": [
|
|
69
65
|
"lint",
|