lint-staged 16.2.6 → 16.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 +4 -0
- package/lib/colors.js +4 -4
- package/lib/execGit.js +20 -17
- package/lib/getAbortController.js +18 -0
- package/lib/getFunctionTask.js +2 -2
- package/lib/getSpawnedTask.js +90 -114
- package/lib/getSpawnedTasks.js +3 -0
- package/lib/killSubprocesses.js +42 -0
- package/lib/runAll.js +4 -13
- package/lib/state.js +0 -3
- package/lib/validateBraces.js +7 -3
- package/package.json +17 -17
package/README.md
CHANGED
|
@@ -34,6 +34,10 @@ $ git commit
|
|
|
34
34
|
|
|
35
35
|
</details>
|
|
36
36
|
|
|
37
|
+
> [!Tip]
|
|
38
|
+
> Do you only want to check staged files for errors, but not edit them automatically?
|
|
39
|
+
> You might be interested in this simpler shell script: [`lint-staged.sh`](https://github.com/lint-staged/lint-staged.sh).
|
|
40
|
+
|
|
37
41
|
## Table of Contents
|
|
38
42
|
|
|
39
43
|
- [Why](#why)
|
package/lib/colors.js
CHANGED
|
@@ -2,14 +2,14 @@ import nodeTty from 'node:tty'
|
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* @example NO_COLOR
|
|
5
|
-
* @
|
|
6
|
-
* @
|
|
5
|
+
* @example NO_COLOR=1
|
|
6
|
+
* @example NO_COLOR=true
|
|
7
7
|
*/
|
|
8
8
|
const TRUTHRY_ENV_VAR_VALUES = ['', '1', 'true']
|
|
9
9
|
|
|
10
10
|
/**
|
|
11
|
-
* @
|
|
12
|
-
* @
|
|
11
|
+
* @example FORCE_COLOR=0
|
|
12
|
+
* @example FORCE_COLOR=false
|
|
13
13
|
*/
|
|
14
14
|
const FALSY_ENV_VAR_VALUES = ['0', 'false']
|
|
15
15
|
|
package/lib/execGit.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { exec } from 'tinyexec'
|
|
2
2
|
|
|
3
3
|
import { createDebug } from './debug.js'
|
|
4
4
|
|
|
@@ -13,22 +13,25 @@ const NO_SUBMODULE_RECURSE = ['-c', 'submodule.recurse=false']
|
|
|
13
13
|
// exported for tests
|
|
14
14
|
export const GIT_GLOBAL_OPTIONS = [...NO_SUBMODULE_RECURSE]
|
|
15
15
|
|
|
16
|
-
/** @type {(cmd: string[], options?:
|
|
16
|
+
/** @type {(cmd: string[], options?: { cwd?: string }) => Promise<string>} */
|
|
17
17
|
export const execGit = async (cmd, options) => {
|
|
18
|
-
debugLog('Running git command', cmd)
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
18
|
+
debugLog('Running git command:', cmd)
|
|
19
|
+
const result = exec('git', [...NO_SUBMODULE_RECURSE, ...cmd], {
|
|
20
|
+
nodeOptions: {
|
|
21
|
+
cwd: options?.cwd,
|
|
22
|
+
stdio: ['ignore'],
|
|
23
|
+
},
|
|
24
|
+
})
|
|
25
|
+
|
|
26
|
+
let output = ''
|
|
27
|
+
for await (const line of result) {
|
|
28
|
+
output += line + '\n'
|
|
29
|
+
}
|
|
30
|
+
output = output.trimEnd()
|
|
31
|
+
|
|
32
|
+
if (result.exitCode > 0) {
|
|
33
|
+
throw new Error(output, { cause: result })
|
|
33
34
|
}
|
|
35
|
+
|
|
36
|
+
return output
|
|
34
37
|
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
export const Signal = {
|
|
2
|
+
SIGINT: 'SIGINT',
|
|
3
|
+
SIGKILL: 'SIGKILL',
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Get an AbortController used to cancel running tasks on failure/interruption.
|
|
8
|
+
* @returns AbortController
|
|
9
|
+
*/
|
|
10
|
+
export const getAbortController = (nodeProcess = process) => {
|
|
11
|
+
const abortController = new AbortController()
|
|
12
|
+
|
|
13
|
+
nodeProcess.on(Signal.SIGINT, () => {
|
|
14
|
+
abortController.abort(Signal.SIGINT)
|
|
15
|
+
})
|
|
16
|
+
|
|
17
|
+
return abortController
|
|
18
|
+
}
|
package/lib/getFunctionTask.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { createDebug } from './debug.js'
|
|
2
|
-
import {
|
|
2
|
+
import { createTaskError } from './getSpawnedTask.js'
|
|
3
3
|
|
|
4
4
|
const debugLog = createDebug('lint-staged:getFunctionTasks')
|
|
5
5
|
|
|
@@ -24,7 +24,7 @@ export const getFunctionTask = async (command, files) => {
|
|
|
24
24
|
try {
|
|
25
25
|
await command.task(files.map((file) => file.filepath))
|
|
26
26
|
} catch (e) {
|
|
27
|
-
throw
|
|
27
|
+
throw createTaskError(command.title, e, ctx)
|
|
28
28
|
}
|
|
29
29
|
}
|
|
30
30
|
|
package/lib/getSpawnedTask.js
CHANGED
|
@@ -1,106 +1,42 @@
|
|
|
1
|
-
import spawn, { SubprocessError } from 'nano-spawn'
|
|
2
|
-
import pidtree from 'pidtree'
|
|
3
1
|
import { parseArgsStringToArgv } from 'string-argv'
|
|
2
|
+
import { exec } from 'tinyexec'
|
|
4
3
|
|
|
5
4
|
import { blackBright, red } from './colors.js'
|
|
6
5
|
import { createDebug } from './debug.js'
|
|
7
6
|
import { error, info } from './figures.js'
|
|
7
|
+
import { Signal } from './getAbortController.js'
|
|
8
|
+
import { killSubProcesses } from './killSubprocesses.js'
|
|
8
9
|
import { getInitialState } from './state.js'
|
|
9
10
|
import { TaskError } from './symbols.js'
|
|
10
11
|
|
|
11
|
-
const TASK_ERROR = 'lint-staged:taskError'
|
|
12
|
-
|
|
13
12
|
const debugLog = createDebug('lint-staged:getSpawnedTask')
|
|
14
13
|
|
|
15
|
-
/** @type {(error: import('nano-spawn').SubprocessError) => string} */
|
|
16
|
-
const getTag = (error) => {
|
|
17
|
-
return error.signalName ?? 'FAILED'
|
|
18
|
-
}
|
|
19
|
-
|
|
20
14
|
/**
|
|
21
15
|
* Handle task console output.
|
|
22
16
|
*
|
|
23
17
|
* @param {string} command
|
|
24
|
-
* @param {
|
|
25
|
-
* @param {
|
|
26
|
-
* @
|
|
18
|
+
* @param {string} output
|
|
19
|
+
* @param {ReturnType<typeof getInitialState>} ctx context
|
|
20
|
+
* @param {keyof typeof Signal | undefined} signal
|
|
21
|
+
* @param {import('tinyexec').Result} [errorResult]
|
|
27
22
|
*/
|
|
28
|
-
const
|
|
29
|
-
if (
|
|
30
|
-
const outputTitle =
|
|
31
|
-
|
|
32
|
-
|
|
23
|
+
const handleTaskOutput = (command, output, ctx, signal, errorResult) => {
|
|
24
|
+
if (output) {
|
|
25
|
+
const outputTitle = errorResult ? red(`${error} ${command}:`) : `${info} ${command}:`
|
|
26
|
+
ctx.output.push([...(ctx.quiet ? [] : ['', outputTitle]), output].join('\n'))
|
|
27
|
+
return
|
|
33
28
|
}
|
|
34
29
|
|
|
35
|
-
if (ctx.quiet)
|
|
36
|
-
|
|
37
|
-
if (result instanceof SubprocessError) {
|
|
38
|
-
ctx.output.push(red(`\n${error} ${command} failed to spawn:`), result.message, result.cause)
|
|
39
|
-
} else if (isError) {
|
|
40
|
-
// Show generic error when task had no output
|
|
41
|
-
const tag = getTag(result)
|
|
42
|
-
const message = red(`\n${error} ${command} failed without output (${tag}).`)
|
|
43
|
-
ctx.output.push(message)
|
|
30
|
+
if (ctx.quiet) {
|
|
31
|
+
return
|
|
44
32
|
}
|
|
45
|
-
}
|
|
46
33
|
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
try {
|
|
55
|
-
childProcess = await subprocess.nodeChildProcess
|
|
56
|
-
} catch {
|
|
57
|
-
/** ignore internal nano-spawn errors, if child process isn't available it can't be killed */
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
if (childProcess?.pid !== undefined) {
|
|
61
|
-
try {
|
|
62
|
-
for (const childPid of await pidtree(childProcess.pid)) {
|
|
63
|
-
try {
|
|
64
|
-
process.kill(childPid, 'SIGKILL')
|
|
65
|
-
} catch (error) {
|
|
66
|
-
debugLog(`Failed to kill process with pid "%d": %o`, childPid, error)
|
|
67
|
-
}
|
|
68
|
-
}
|
|
69
|
-
} catch (error) {
|
|
70
|
-
// Suppress "No matching pid found" error. This probably means
|
|
71
|
-
// the process already died before executing.
|
|
72
|
-
debugLog(`Failed to list child processes of pid "%d": %o`, childProcess.pid, error)
|
|
73
|
-
}
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
// The child process is terminated separately in order to get the `KILLED` status.
|
|
77
|
-
childProcess?.kill('SIGKILL')
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
/**
|
|
81
|
-
* Interrupts the execution of the subprocess that we spawned if
|
|
82
|
-
* another task adds an error to the context.
|
|
83
|
-
*
|
|
84
|
-
* @param {Object} ctx
|
|
85
|
-
* @param {import('nano-spawn').Subprocess} subprocess
|
|
86
|
-
* @returns {() => Promise<void>} Function that clears the interval that
|
|
87
|
-
* checks the context.
|
|
88
|
-
*/
|
|
89
|
-
const interruptExecutionOnError = (ctx, subprocess) => {
|
|
90
|
-
let killPromise
|
|
91
|
-
|
|
92
|
-
const errorListener = async () => {
|
|
93
|
-
killPromise = killSubprocess(subprocess)
|
|
94
|
-
await killPromise
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
ctx.events.on(TASK_ERROR, errorListener, { once: true })
|
|
98
|
-
|
|
99
|
-
return async () => {
|
|
100
|
-
ctx.events.off(TASK_ERROR, errorListener)
|
|
101
|
-
if (killPromise) {
|
|
102
|
-
await killPromise
|
|
103
|
-
}
|
|
34
|
+
if (signal === 'SIGINT') {
|
|
35
|
+
ctx.output.push(red(`\n${error} Task interrupted: ${command}`))
|
|
36
|
+
} else if (signal === 'SIGKILL') {
|
|
37
|
+
ctx.output.push(red(`\n${error} Task killed: ${command}`))
|
|
38
|
+
} else if (errorResult) {
|
|
39
|
+
ctx.output.push(red(`\n${error} Task failed to spawn: ${command}`), signal)
|
|
104
40
|
}
|
|
105
41
|
}
|
|
106
42
|
|
|
@@ -108,25 +44,21 @@ const interruptExecutionOnError = (ctx, subprocess) => {
|
|
|
108
44
|
* Create a error output depending on process result.
|
|
109
45
|
*
|
|
110
46
|
* @param {string} command
|
|
111
|
-
* @param {import('
|
|
112
|
-
* @param {
|
|
47
|
+
* @param {import('tinyexec').Result} result
|
|
48
|
+
* @param {ReturnType<typeof getInitialState>} ctx context
|
|
49
|
+
* @param {keyof typeof Signal | undefined} signal
|
|
113
50
|
* @returns {Error}
|
|
114
51
|
*/
|
|
115
|
-
export const
|
|
52
|
+
export const createTaskError = (command, result, ctx, signal = 'FAILED') => {
|
|
116
53
|
ctx.errors.add(TaskError)
|
|
117
|
-
|
|
118
|
-
// https://nodejs.org/api/events.html#error-events
|
|
119
|
-
ctx.events.emit(TASK_ERROR, TaskError)
|
|
120
|
-
|
|
121
|
-
handleOutput(command, error, ctx, true)
|
|
122
|
-
const tag = getTag(error)
|
|
123
|
-
return new Error(`${red(command)} ${blackBright(`[${tag}]`)}`)
|
|
54
|
+
return new Error(`${red(command)} ${blackBright(`[${signal}]`)}`, { cause: result })
|
|
124
55
|
}
|
|
125
56
|
|
|
126
57
|
/**
|
|
127
58
|
* Returns the task function for the linter.
|
|
128
59
|
*
|
|
129
60
|
* @param {Object} options
|
|
61
|
+
* @param {AbortController} options.abortController
|
|
130
62
|
* @param {boolean} [options.color]
|
|
131
63
|
* @param {string} options.command — Linter task
|
|
132
64
|
* @param {string} [options.continueOnError]
|
|
@@ -138,6 +70,7 @@ export const makeErr = (command, error, ctx) => {
|
|
|
138
70
|
* @returns {() => Promise<Array<string>>}
|
|
139
71
|
*/
|
|
140
72
|
export const getSpawnedTask = ({
|
|
73
|
+
abortController,
|
|
141
74
|
color,
|
|
142
75
|
command,
|
|
143
76
|
continueOnError = false,
|
|
@@ -151,35 +84,78 @@ export const getSpawnedTask = ({
|
|
|
151
84
|
debugLog('cmd:', cmd)
|
|
152
85
|
debugLog('args:', args)
|
|
153
86
|
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
87
|
+
/** @type {import('tinyexec').Options}*/
|
|
88
|
+
const tinyExecOptions = {
|
|
89
|
+
nodeOptions: {
|
|
90
|
+
// Only use topLevelDir as CWD if we are using the git binary
|
|
91
|
+
// e.g `npm` should run tasks in the actual CWD
|
|
92
|
+
cwd: /^git(\.exe)?/i.test(cmd) ? topLevelDir : cwd,
|
|
93
|
+
detached: true,
|
|
94
|
+
env: color ? { FORCE_COLOR: 'true' } : { NO_COLOR: 'true' },
|
|
95
|
+
stdio: ['ignore'],
|
|
96
|
+
},
|
|
161
97
|
}
|
|
162
98
|
|
|
163
|
-
debugLog('
|
|
99
|
+
debugLog('Tinyexec options:', tinyExecOptions)
|
|
164
100
|
|
|
101
|
+
/** @param {ReturnType<typeof getInitialState>} ctx context */
|
|
165
102
|
return async (ctx = getInitialState()) => {
|
|
166
|
-
|
|
103
|
+
const result = exec(cmd, isFn ? args : args.concat(files), tinyExecOptions)
|
|
167
104
|
|
|
105
|
+
const taskFailed = () => result.exitCode > 0 || result.process?.signalCode
|
|
106
|
+
|
|
107
|
+
/** @type {keyof typeof Signal | undefined} */
|
|
108
|
+
let signal
|
|
109
|
+
|
|
110
|
+
abortController.signal.addEventListener(
|
|
111
|
+
'abort',
|
|
112
|
+
async () => {
|
|
113
|
+
if (taskFailed() || !result.process) return
|
|
114
|
+
|
|
115
|
+
signal = abortController.signal.reason
|
|
116
|
+
const pid = result.process.pid
|
|
117
|
+
result.process.kill(abortController.signal.reason)
|
|
118
|
+
await killSubProcesses(pid)
|
|
119
|
+
},
|
|
120
|
+
{ once: true }
|
|
121
|
+
)
|
|
122
|
+
|
|
123
|
+
let output = ''
|
|
168
124
|
try {
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
quitInterruptCheck = interruptExecutionOnError(ctx, subprocess)
|
|
172
|
-
}
|
|
173
|
-
const result = await subprocess
|
|
174
|
-
if (verbose) {
|
|
175
|
-
handleOutput(command, result, ctx)
|
|
125
|
+
for await (const line of result) {
|
|
126
|
+
output += line + '\n'
|
|
176
127
|
}
|
|
177
128
|
} catch (error) {
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
129
|
+
/** Probably failed to spawn (ENOENT) */
|
|
130
|
+
const errorSignal = (error instanceof Error && error.code) || 'FAILED'
|
|
131
|
+
|
|
132
|
+
if (continueOnError !== true) {
|
|
133
|
+
/** Other tasks should be killed */
|
|
134
|
+
abortController.abort(Signal.SIGKILL)
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
handleTaskOutput(command, output, ctx, errorSignal, result)
|
|
138
|
+
throw createTaskError(command, result, ctx, errorSignal)
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
output = output.trimEnd()
|
|
142
|
+
|
|
143
|
+
if (taskFailed()) {
|
|
144
|
+
if (continueOnError !== true) {
|
|
145
|
+
/** Other tasks should be killed */
|
|
146
|
+
abortController.abort(Signal.SIGKILL)
|
|
182
147
|
}
|
|
148
|
+
|
|
149
|
+
if (result.process?.pid) {
|
|
150
|
+
await killSubProcesses(result.process.pid)
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
handleTaskOutput(command, output, ctx, signal, result)
|
|
154
|
+
throw createTaskError(command, result, ctx, result.process?.signalCode ?? signal)
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
if (verbose) {
|
|
158
|
+
handleTaskOutput(command, output, ctx, signal)
|
|
183
159
|
}
|
|
184
160
|
}
|
|
185
161
|
}
|
package/lib/getSpawnedTasks.js
CHANGED
|
@@ -8,6 +8,7 @@ const debugLog = createDebug('lint-staged:getSpawnedTasks')
|
|
|
8
8
|
* Creates and returns an array of listr tasks which map to the given commands.
|
|
9
9
|
*
|
|
10
10
|
* @param {object} options
|
|
11
|
+
* @param {AbortController} options.abortController
|
|
11
12
|
* @param {boolean} [options.color]
|
|
12
13
|
* @param {Array<string|Function>|string|Function} options.commands
|
|
13
14
|
* @param {string} options.continueOnError
|
|
@@ -17,6 +18,7 @@ const debugLog = createDebug('lint-staged:getSpawnedTasks')
|
|
|
17
18
|
* @param {Boolean} verbose
|
|
18
19
|
*/
|
|
19
20
|
export const getSpawnedTasks = async ({
|
|
21
|
+
abortController,
|
|
20
22
|
color,
|
|
21
23
|
commands,
|
|
22
24
|
continueOnError,
|
|
@@ -55,6 +57,7 @@ export const getSpawnedTasks = async ({
|
|
|
55
57
|
}
|
|
56
58
|
|
|
57
59
|
const task = getSpawnedTask({
|
|
60
|
+
abortController,
|
|
58
61
|
color,
|
|
59
62
|
command,
|
|
60
63
|
continueOnError,
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { exec } from 'node:child_process'
|
|
2
|
+
import { promisify } from 'node:util'
|
|
3
|
+
|
|
4
|
+
const execAsync = promisify(exec)
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* End process by pid, forcefully, including child processes
|
|
8
|
+
*
|
|
9
|
+
* @see {@link https://learn.microsoft.com/en-us/windows-server/administration/windows-commands/taskkill}
|
|
10
|
+
*
|
|
11
|
+
* @param {number} pid
|
|
12
|
+
*/
|
|
13
|
+
const killWin32Subprocesses = async (pid) => {
|
|
14
|
+
await execAsync(`taskkill /pid ${pid} /T /F`)
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Kill all processes in the group by using negative pid
|
|
19
|
+
*
|
|
20
|
+
* @see {@link https://pubs.opengroup.org/onlinepubs/9699919799/functions/kill.html}
|
|
21
|
+
*
|
|
22
|
+
* @param {number} pid
|
|
23
|
+
*/
|
|
24
|
+
const killUnixSubprocesses = async (pid) => {
|
|
25
|
+
process.kill(-pid, 'SIGKILL')
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* @param {number} pid
|
|
30
|
+
* @param {boolean} [isWin32]
|
|
31
|
+
*/
|
|
32
|
+
export const killSubProcesses = async (pid, isWin32 = process.platform === 'win32') => {
|
|
33
|
+
try {
|
|
34
|
+
if (isWin32) {
|
|
35
|
+
await killWin32Subprocesses(pid)
|
|
36
|
+
} else {
|
|
37
|
+
await killUnixSubprocesses(pid)
|
|
38
|
+
}
|
|
39
|
+
} catch {
|
|
40
|
+
/** ignore errors */
|
|
41
|
+
}
|
|
42
|
+
}
|
package/lib/runAll.js
CHANGED
|
@@ -9,6 +9,7 @@ import { blackBright } from './colors.js'
|
|
|
9
9
|
import { createDebug } from './debug.js'
|
|
10
10
|
import { execGit } from './execGit.js'
|
|
11
11
|
import { generateTasks } from './generateTasks.js'
|
|
12
|
+
import { getAbortController } from './getAbortController.js'
|
|
12
13
|
import { getFunctionTask, isFunctionTask } from './getFunctionTask.js'
|
|
13
14
|
import { getRenderer } from './getRenderer.js'
|
|
14
15
|
import { getSpawnedTasks } from './getSpawnedTasks.js'
|
|
@@ -187,20 +188,14 @@ export const runAll = async (
|
|
|
187
188
|
...getRenderer({ color, debug, quiet }, logger),
|
|
188
189
|
}
|
|
189
190
|
|
|
190
|
-
/**
|
|
191
|
-
* This is used to set max event listener count to the total number
|
|
192
|
-
* of generated tasks. The event listener is used to keep track of
|
|
193
|
-
* the interrupt signal and kill all tasks when it happens. See the
|
|
194
|
-
* `interruptExecutionOnError` in `getSpawnedTask`.
|
|
195
|
-
*/
|
|
196
|
-
let listrTaskCount = 0
|
|
197
|
-
|
|
198
191
|
const listrTasks = []
|
|
199
192
|
|
|
200
193
|
// Set of all staged files that matched a task glob. Values in a set are unique.
|
|
201
194
|
/** @type {Set<import('./getStagedFiles.js').StagedFile>} */
|
|
202
195
|
const matchedFiles = new Set()
|
|
203
196
|
|
|
197
|
+
const abortController = getAbortController()
|
|
198
|
+
|
|
204
199
|
for (const [configPath, { config, files }] of Object.entries(filesByConfig)) {
|
|
205
200
|
const configName = configPath ? normalizePath(path.relative(cwd, configPath)) : 'Config object'
|
|
206
201
|
|
|
@@ -222,6 +217,7 @@ export const runAll = async (
|
|
|
222
217
|
(isFunctionTask(task.commands)
|
|
223
218
|
? getFunctionTask(task.commands, task.fileList)
|
|
224
219
|
: getSpawnedTasks({
|
|
220
|
+
abortController,
|
|
225
221
|
color,
|
|
226
222
|
commands: task.commands,
|
|
227
223
|
continueOnError,
|
|
@@ -273,8 +269,6 @@ export const runAll = async (
|
|
|
273
269
|
)
|
|
274
270
|
)
|
|
275
271
|
|
|
276
|
-
listrTaskCount += chunkListrTasks.length
|
|
277
|
-
|
|
278
272
|
listrTasks.push({
|
|
279
273
|
title:
|
|
280
274
|
`${configName}${blackBright(` — ${files.length} ${files.length > 1 ? 'files' : 'file'}`)}` +
|
|
@@ -368,9 +362,6 @@ export const runAll = async (
|
|
|
368
362
|
listrOptions
|
|
369
363
|
)
|
|
370
364
|
|
|
371
|
-
debugLog('Set max event listeners to the number of tasks: %i', listrTaskCount)
|
|
372
|
-
ctx.events.setMaxListeners(listrTaskCount)
|
|
373
|
-
|
|
374
365
|
await runner.run()
|
|
375
366
|
|
|
376
367
|
if (ctx.errors.size > 0) {
|
package/lib/state.js
CHANGED
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
import EventEmitter from 'events'
|
|
2
|
-
|
|
3
1
|
import { GIT_ERROR, TASK_ERROR } from './messages.js'
|
|
4
2
|
import {
|
|
5
3
|
FailOnChangesError,
|
|
@@ -18,7 +16,6 @@ export const getInitialState = ({
|
|
|
18
16
|
} = {}) => ({
|
|
19
17
|
backupHash: null,
|
|
20
18
|
errors: new Set([]),
|
|
21
|
-
events: new EventEmitter(),
|
|
22
19
|
shouldFailOnChanges: failOnChanges,
|
|
23
20
|
hasFilesToHide: null,
|
|
24
21
|
output: [],
|
package/lib/validateBraces.js
CHANGED
|
@@ -28,7 +28,8 @@ 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 getIncorrectBracesRegexp = () =>
|
|
32
|
+
/(?<![\\$])({)(?:(?!(?<!\\),|\.\.|\{|\}).)*?(?<!\\)(})/g
|
|
32
33
|
|
|
33
34
|
/**
|
|
34
35
|
* @param {string} pattern
|
|
@@ -36,12 +37,15 @@ export const INCORRECT_BRACES_REGEXP = /(?<![\\$])({)(?:(?!(?<!\\),|\.\.|\{|\}).
|
|
|
36
37
|
*/
|
|
37
38
|
const stripIncorrectBraces = (pattern) => {
|
|
38
39
|
let output = `${pattern}`
|
|
39
|
-
|
|
40
|
+
const regexp = getIncorrectBracesRegexp()
|
|
41
|
+
let match = regexp.exec(output)
|
|
40
42
|
|
|
41
|
-
while (
|
|
43
|
+
while (match) {
|
|
42
44
|
const fullMatch = match[0]
|
|
43
45
|
const withoutBraces = fullMatch.replace(/{/, '').replace(/}/, '')
|
|
44
46
|
output = output.replace(fullMatch, withoutBraces)
|
|
47
|
+
regexp.lastIndex = 0 // restart iteration
|
|
48
|
+
match = regexp.exec(output)
|
|
45
49
|
}
|
|
46
50
|
|
|
47
51
|
return output
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "lint-staged",
|
|
3
|
-
"version": "16.
|
|
3
|
+
"version": "16.3.0",
|
|
4
4
|
"description": "Lint files staged by git",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -48,35 +48,35 @@
|
|
|
48
48
|
"tag": "npx changeset tag"
|
|
49
49
|
},
|
|
50
50
|
"dependencies": {
|
|
51
|
-
"commander": "^14.0.
|
|
51
|
+
"commander": "^14.0.3",
|
|
52
52
|
"listr2": "^9.0.5",
|
|
53
53
|
"micromatch": "^4.0.8",
|
|
54
54
|
"nano-spawn": "^2.0.0",
|
|
55
|
-
"pidtree": "^0.6.0",
|
|
56
55
|
"string-argv": "^0.3.2",
|
|
57
|
-
"
|
|
56
|
+
"tinyexec": "^1.0.2",
|
|
57
|
+
"yaml": "^2.8.2"
|
|
58
58
|
},
|
|
59
59
|
"devDependencies": {
|
|
60
|
-
"@changesets/changelog-github": "0.5.
|
|
61
|
-
"@changesets/cli": "2.29.
|
|
62
|
-
"@commitlint/cli": "20.
|
|
63
|
-
"@commitlint/config-conventional": "20.
|
|
64
|
-
"@eslint/js": "
|
|
65
|
-
"@vitest/coverage-v8": "4.0.
|
|
66
|
-
"@vitest/eslint-plugin": "1.
|
|
60
|
+
"@changesets/changelog-github": "0.5.2",
|
|
61
|
+
"@changesets/cli": "2.29.8",
|
|
62
|
+
"@commitlint/cli": "20.4.2",
|
|
63
|
+
"@commitlint/config-conventional": "20.4.2",
|
|
64
|
+
"@eslint/js": "10.0.1",
|
|
65
|
+
"@vitest/coverage-v8": "4.0.18",
|
|
66
|
+
"@vitest/eslint-plugin": "1.6.9",
|
|
67
67
|
"consolemock": "1.1.0",
|
|
68
68
|
"cross-env": "10.1.0",
|
|
69
|
-
"eslint": "
|
|
69
|
+
"eslint": "10.0.2",
|
|
70
70
|
"eslint-config-prettier": "10.1.8",
|
|
71
|
-
"eslint-plugin-n": "17.
|
|
72
|
-
"eslint-plugin-prettier": "5.5.
|
|
71
|
+
"eslint-plugin-n": "17.24.0",
|
|
72
|
+
"eslint-plugin-prettier": "5.5.5",
|
|
73
73
|
"eslint-plugin-simple-import-sort": "12.1.1",
|
|
74
74
|
"husky": "9.1.7",
|
|
75
75
|
"mock-stdin": "1.0.0",
|
|
76
|
-
"prettier": "3.
|
|
77
|
-
"semver": "7.7.
|
|
76
|
+
"prettier": "3.8.1",
|
|
77
|
+
"semver": "7.7.4",
|
|
78
78
|
"typescript": "5.9.3",
|
|
79
|
-
"vitest": "4.0.
|
|
79
|
+
"vitest": "4.0.18"
|
|
80
80
|
},
|
|
81
81
|
"keywords": [
|
|
82
82
|
"lint",
|