lint-staged 17.0.8 → 17.1.1
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 +14 -28
- package/bin/lint-staged.js +5 -5
- package/lib/chunkFiles.js +2 -2
- package/lib/cli.js +17 -12
- package/lib/colors.js +26 -8
- package/lib/debug.js +2 -2
- package/lib/figures.js +9 -5
- package/lib/getFunctionTask.js +13 -4
- package/lib/getSpawnedTask.js +9 -9
- package/lib/getSpawnedTasks.js +3 -6
- package/lib/gitWorkflow.js +188 -112
- package/lib/index.js +23 -20
- package/lib/messages.js +36 -29
- package/lib/parseGitZOutput.js +1 -1
- package/lib/printTaskOutput.js +2 -2
- package/lib/runAll.js +103 -103
- package/lib/runParallelTasks.js +112 -0
- package/lib/state.js +14 -9
- package/lib/validateOptions.js +30 -3
- package/package.json +20 -25
- package/lib/getRenderer.js +0 -63
package/lib/messages.js
CHANGED
|
@@ -1,30 +1,32 @@
|
|
|
1
1
|
import { inspect } from 'node:util'
|
|
2
2
|
|
|
3
3
|
import { bold, red, yellow } from './colors.js'
|
|
4
|
-
import
|
|
4
|
+
import * as figures from './figures.js'
|
|
5
5
|
|
|
6
6
|
export const configurationError = (opt, helpMsg, value) =>
|
|
7
|
-
`${red(`${error} Validation Error:`)}
|
|
7
|
+
`${red(`${figures.error()} Validation Error:`)}
|
|
8
8
|
|
|
9
9
|
Invalid value for '${bold(opt)}': ${bold(inspect(value))}
|
|
10
10
|
|
|
11
11
|
${helpMsg}`
|
|
12
12
|
|
|
13
|
-
export const
|
|
13
|
+
export const notGitRepo = () => red(`${figures.error()} Current directory is not a git directory!`)
|
|
14
14
|
|
|
15
|
-
export const
|
|
15
|
+
export const failedGetStagedFiles = () => red(`${figures.error()} Failed to get staged files!`)
|
|
16
16
|
|
|
17
17
|
export const incorrectBraces = (before, after) =>
|
|
18
18
|
yellow(
|
|
19
|
-
`${warning} Detected incorrect braces with only single value: \`${before}\`. Reformatted as: \`${after}\`
|
|
19
|
+
`${figures.warning()} Detected incorrect braces with only single value: \`${before}\`. Reformatted as: \`${after}\`
|
|
20
20
|
`
|
|
21
21
|
)
|
|
22
22
|
|
|
23
|
-
export const
|
|
23
|
+
export const noConfiguration = () =>
|
|
24
|
+
`${figures.error()} lint-staged could not find any valid configuration.`
|
|
24
25
|
|
|
25
|
-
export const
|
|
26
|
+
export const noStagedFiles = () => `${figures.info()} lint-staged could not find any staged files.`
|
|
26
27
|
|
|
27
|
-
export const
|
|
28
|
+
export const noTasks = () =>
|
|
29
|
+
`${figures.info()} lint-staged could not find any staged files matching configured tasks.`
|
|
28
30
|
|
|
29
31
|
export const skippingBackup = (hasInitialCommit, diff) => {
|
|
30
32
|
const reason =
|
|
@@ -33,27 +35,33 @@ export const skippingBackup = (hasInitialCommit, diff) => {
|
|
|
33
35
|
: (hasInitialCommit ? '`--no-stash` was used' : 'there’s no initial commit yet') +
|
|
34
36
|
'. This might result in data loss'
|
|
35
37
|
|
|
36
|
-
return yellow(`${warning} Skipping backup because ${reason}.\n`)
|
|
38
|
+
return yellow(`${figures.warning()} Skipping backup because ${reason}.\n`)
|
|
37
39
|
}
|
|
38
40
|
|
|
39
|
-
export const
|
|
40
|
-
|
|
41
|
-
)
|
|
41
|
+
export const skippingHidePartiallyChanged = () =>
|
|
42
|
+
yellow(
|
|
43
|
+
`${figures.warning()} Skipping hiding unstaged changes from partially staged files because \`--no-hide-partially-staged\` was used.\n`
|
|
44
|
+
)
|
|
42
45
|
|
|
43
|
-
export const
|
|
44
|
-
|
|
46
|
+
export const deprecatedGitAdd = () =>
|
|
47
|
+
yellow(
|
|
48
|
+
`${figures.warning()} Some of your tasks use \`git add\` command. Please remove it from the config since all modifications made by tasks will be automatically added to the git commit index.
|
|
45
49
|
`
|
|
46
|
-
)
|
|
47
|
-
|
|
48
|
-
export const TASK_ERROR = 'Skipped because of errors from tasks.'
|
|
50
|
+
)
|
|
49
51
|
|
|
50
|
-
export const
|
|
52
|
+
export const taskError = () => 'Skipped because of errors from tasks.'
|
|
51
53
|
|
|
52
|
-
export const
|
|
54
|
+
export const preventedTaskModifications = () =>
|
|
55
|
+
`\n${figures.error()} lint-staged failed because \`--fail-on-changes\` was used.`
|
|
53
56
|
|
|
54
|
-
export const
|
|
57
|
+
export const gitError = () =>
|
|
58
|
+
`\n ${red(`${figures.error()} lint-staged failed due to a git error.`)}`
|
|
55
59
|
|
|
56
|
-
export const invalidOption = (
|
|
60
|
+
export const invalidOption = (
|
|
61
|
+
name,
|
|
62
|
+
value,
|
|
63
|
+
message
|
|
64
|
+
) => `${red(`${figures.error()} Validation Error:`)}
|
|
57
65
|
|
|
58
66
|
Invalid value for option '${bold(name)}': ${bold(value)}
|
|
59
67
|
|
|
@@ -61,8 +69,8 @@ export const invalidOption = (name, value, message) => `${red(`${error} Validati
|
|
|
61
69
|
|
|
62
70
|
See https://github.com/lint-staged/lint-staged#command-line-flags`
|
|
63
71
|
|
|
64
|
-
export const
|
|
65
|
-
${yellow(`${warning} lint-staged prevented an empty git commit.
|
|
72
|
+
export const preventedEmptyCommit = () => `
|
|
73
|
+
${yellow(`${figures.warning()} lint-staged prevented an empty git commit.
|
|
66
74
|
Use the --allow-empty option to continue, or check your task configuration`)}
|
|
67
75
|
`
|
|
68
76
|
|
|
@@ -75,23 +83,22 @@ export const restoreStashExample = (
|
|
|
75
83
|
> git apply --index ${hash}
|
|
76
84
|
`
|
|
77
85
|
|
|
78
|
-
export const CONFIG_STDIN_ERROR = red(`${error} Failed to read config from stdin.`)
|
|
79
|
-
|
|
80
86
|
export const failedToLoadConfig = (filepath) =>
|
|
81
|
-
red(`${error} Failed to read config from file "${filepath}".`)
|
|
87
|
+
red(`${figures.error()} Failed to read config from file "${filepath}".`)
|
|
82
88
|
|
|
83
89
|
export const failedToParseConfig = (
|
|
84
90
|
filepath,
|
|
85
91
|
error
|
|
86
|
-
) => `${red(`${error} Failed to parse config from file "${filepath}".`)}
|
|
92
|
+
) => `${red(`${figures.error()} Failed to parse config from file "${filepath}".`)}
|
|
87
93
|
|
|
88
94
|
${error}
|
|
89
95
|
|
|
90
96
|
See https://github.com/lint-staged/lint-staged#configuration.`
|
|
91
97
|
|
|
92
|
-
export const
|
|
98
|
+
export const unstagedChangesBackupStashLocation = () =>
|
|
99
|
+
'Unstaged changes have been kept back in a patch file:'
|
|
93
100
|
|
|
94
101
|
export const minGitVersionRequired = (expected) =>
|
|
95
|
-
red(`${error} lint-staged requires at least Git version ${bold(expected)}.
|
|
102
|
+
red(`${figures.error()} lint-staged requires at least Git version ${bold(expected)}.
|
|
96
103
|
|
|
97
104
|
Please update Git: https://git-scm.com/downloads`)
|
package/lib/parseGitZOutput.js
CHANGED
package/lib/printTaskOutput.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Handle logging of
|
|
3
|
-
* @param {Object} ctx - The
|
|
2
|
+
* Handle logging of task `ctx.output` to the specified `logger`
|
|
3
|
+
* @param {Object} ctx - The context
|
|
4
4
|
* @param {Object} logger - The logger
|
|
5
5
|
*/
|
|
6
6
|
export const printTaskOutput = (ctx = {}, logger) => {
|
package/lib/runAll.js
CHANGED
|
@@ -2,29 +2,26 @@
|
|
|
2
2
|
|
|
3
3
|
import path from 'node:path'
|
|
4
4
|
|
|
5
|
-
import { Listr } from 'listr2'
|
|
6
|
-
|
|
7
5
|
import { chunkFiles } from './chunkFiles.js'
|
|
8
6
|
import { dim } from './colors.js'
|
|
9
7
|
import { createDebug } from './debug.js'
|
|
10
8
|
import { execGit } from './execGit.js'
|
|
9
|
+
import * as figures from './figures.js'
|
|
11
10
|
import { generateTasks } from './generateTasks.js'
|
|
12
11
|
import { getAbortController } from './getAbortController.js'
|
|
13
12
|
import { getFunctionTask, isFunctionTask } from './getFunctionTask.js'
|
|
14
|
-
import { getRenderer } from './getRenderer.js'
|
|
15
13
|
import { getSpawnedTasks } from './getSpawnedTasks.js'
|
|
16
14
|
import { getStagedFiles } from './getStagedFiles.js'
|
|
17
15
|
import { GitWorkflow } from './gitWorkflow.js'
|
|
18
16
|
import { groupFilesByConfig } from './groupFilesByConfig.js'
|
|
19
17
|
import {
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
SKIPPED_GIT_ERROR,
|
|
26
|
-
SKIPPING_HIDE_PARTIALLY_CHANGED,
|
|
18
|
+
deprecatedGitAdd,
|
|
19
|
+
failedGetStagedFiles,
|
|
20
|
+
noStagedFiles,
|
|
21
|
+
noTasks,
|
|
22
|
+
notGitRepo,
|
|
27
23
|
skippingBackup,
|
|
24
|
+
skippingHidePartiallyChanged,
|
|
28
25
|
} from './messages.js'
|
|
29
26
|
import { normalizePath } from './normalizePath.js'
|
|
30
27
|
import { resolveGitRepo } from './resolveGitRepo.js'
|
|
@@ -41,10 +38,24 @@ import {
|
|
|
41
38
|
shouldRestoreUntrackedFiles,
|
|
42
39
|
updateIndexSkipped,
|
|
43
40
|
} from './state.js'
|
|
44
|
-
import { ConfigNotFoundError, GetStagedFilesError,
|
|
41
|
+
import { ConfigNotFoundError, GetStagedFilesError, GitRepoError } from './symbols.js'
|
|
45
42
|
|
|
46
43
|
const debugLog = createDebug('lint-staged:runAll')
|
|
47
44
|
|
|
45
|
+
/**
|
|
46
|
+
* @param {Logger} logger
|
|
47
|
+
* @param {boolean} quiet
|
|
48
|
+
* @returns {Logger}
|
|
49
|
+
*/
|
|
50
|
+
const configureLogger = (logger, quiet) => {
|
|
51
|
+
if (quiet) {
|
|
52
|
+
const noop = () => {}
|
|
53
|
+
return { error: logger.error, log: noop, warn: noop }
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
return logger
|
|
57
|
+
}
|
|
58
|
+
|
|
48
59
|
/**
|
|
49
60
|
* @param {ReturnType<typeof getInitialState>} ctx context
|
|
50
61
|
* @param {unknown} cause error cause
|
|
@@ -57,13 +68,11 @@ const createError = (ctx, cause) =>
|
|
|
57
68
|
*
|
|
58
69
|
* @param {object} options
|
|
59
70
|
* @param {boolean} [options.allowEmpty] - Allow empty commits when tasks revert all staged changes
|
|
60
|
-
* @param {boolean} [options.color] - Enable or disable ANSI color codes in output.
|
|
61
71
|
* @param {boolean | number} [options.concurrent] - The number of tasks to run concurrently, or false to run tasks serially
|
|
62
72
|
* @param {Object} [options.configObject] - Explicit config object from the js API
|
|
63
73
|
* @param {string} [options.configPath] - Explicit path to a config file
|
|
64
74
|
* @param {boolean} [options.continueOnError] - Run all tasks to completion even if one fails
|
|
65
75
|
* @param {string} [options.cwd] - Current working directory
|
|
66
|
-
* @param {boolean} [options.debug] - Enable debug mode
|
|
67
76
|
* @param {string} [options.diff] - Override the default "--staged" flag of "git diff" to get list of files
|
|
68
77
|
* @param {string} [options.diffFilter] - Override the default "--diff-filter=ACMR" flag of "git diff" to get list of files
|
|
69
78
|
* @param {boolean} [options.failOnChanges] - Fail with exit code 1 when tasks modify tracked files
|
|
@@ -81,13 +90,11 @@ const createError = (ctx, cause) =>
|
|
|
81
90
|
export const runAll = async (
|
|
82
91
|
{
|
|
83
92
|
allowEmpty = false,
|
|
84
|
-
color = false,
|
|
85
93
|
concurrent = true,
|
|
86
94
|
configObject,
|
|
87
95
|
configPath,
|
|
88
96
|
continueOnError = false,
|
|
89
97
|
cwd,
|
|
90
|
-
debug = false,
|
|
91
98
|
diff,
|
|
92
99
|
diffFilter,
|
|
93
100
|
failOnChanges = false,
|
|
@@ -105,6 +112,8 @@ export const runAll = async (
|
|
|
105
112
|
},
|
|
106
113
|
logger = console
|
|
107
114
|
) => {
|
|
115
|
+
const thisLogger = configureLogger(logger, quiet)
|
|
116
|
+
|
|
108
117
|
debugLog('Running all linter scripts...')
|
|
109
118
|
|
|
110
119
|
// Resolve relative CWD option
|
|
@@ -123,7 +132,7 @@ export const runAll = async (
|
|
|
123
132
|
|
|
124
133
|
const { topLevelDir, gitConfigDir } = await resolveGitRepo(cwd)
|
|
125
134
|
if (!topLevelDir) {
|
|
126
|
-
if (!quiet) ctx.output.push(
|
|
135
|
+
if (!quiet) ctx.output.push(notGitRepo())
|
|
127
136
|
ctx.errors.add(GitRepoError)
|
|
128
137
|
throw createError(ctx, GitRepoError)
|
|
129
138
|
}
|
|
@@ -137,22 +146,22 @@ export const runAll = async (
|
|
|
137
146
|
// Lint-staged will create a backup stash only when there's an initial commit,
|
|
138
147
|
// and when using the default list of staged files by default
|
|
139
148
|
ctx.shouldBackup = hasInitialCommit && stash
|
|
140
|
-
if (!ctx.shouldBackup
|
|
141
|
-
|
|
149
|
+
if (!ctx.shouldBackup) {
|
|
150
|
+
thisLogger.warn(skippingBackup(hasInitialCommit, diff))
|
|
142
151
|
}
|
|
143
152
|
|
|
144
|
-
if (!ctx.shouldHidePartiallyStaged && !ctx.shouldHideUnstaged && !ctx.shouldHideAll
|
|
145
|
-
|
|
153
|
+
if (!ctx.shouldHidePartiallyStaged && !ctx.shouldHideUnstaged && !ctx.shouldHideAll) {
|
|
154
|
+
thisLogger.warn(skippingHidePartiallyChanged())
|
|
146
155
|
}
|
|
147
156
|
|
|
148
157
|
// Run staged files retrieval and config search in parallel since they're independent
|
|
149
158
|
const [stagedFiles, foundConfigs] = await Promise.all([
|
|
150
159
|
getStagedFiles({ cwd: topLevelDir, diff, diffFilter }),
|
|
151
|
-
searchConfigs({ configObject, configPath, cwd, topLevelDir },
|
|
160
|
+
searchConfigs({ configObject, configPath, cwd, topLevelDir }, thisLogger),
|
|
152
161
|
])
|
|
153
162
|
|
|
154
163
|
if (!stagedFiles) {
|
|
155
|
-
if (!quiet) ctx.output.push(
|
|
164
|
+
if (!quiet) ctx.output.push(failedGetStagedFiles())
|
|
156
165
|
ctx.errors.add(GetStagedFilesError)
|
|
157
166
|
throw createError(ctx, GetStagedFilesError)
|
|
158
167
|
}
|
|
@@ -160,7 +169,7 @@ export const runAll = async (
|
|
|
160
169
|
|
|
161
170
|
// If there are no files avoid executing any lint-staged logic
|
|
162
171
|
if (stagedFiles.length === 0) {
|
|
163
|
-
if (!quiet) ctx.output.push(
|
|
172
|
+
if (!quiet) ctx.output.push(noStagedFiles())
|
|
164
173
|
return ctx
|
|
165
174
|
}
|
|
166
175
|
|
|
@@ -184,14 +193,7 @@ export const runAll = async (
|
|
|
184
193
|
// Warn user when their command includes `git add`
|
|
185
194
|
let hasDeprecatedGitAdd = false
|
|
186
195
|
|
|
187
|
-
const
|
|
188
|
-
ctx,
|
|
189
|
-
exitOnError: false,
|
|
190
|
-
registerSignalListeners: false,
|
|
191
|
-
...getRenderer({ color, debug, quiet }, logger),
|
|
192
|
-
}
|
|
193
|
-
|
|
194
|
-
const listrTasks = []
|
|
196
|
+
const tasks = []
|
|
195
197
|
|
|
196
198
|
// Set of all staged files that matched a task glob. Values in a set are unique.
|
|
197
199
|
/** @type {Set<import('./getStagedFiles.js').StagedFile>} */
|
|
@@ -215,13 +217,17 @@ export const runAll = async (
|
|
|
215
217
|
}
|
|
216
218
|
|
|
217
219
|
for (const [index, files] of stagedFileChunks.entries()) {
|
|
218
|
-
const
|
|
220
|
+
const taskChunk = await Promise.all(
|
|
219
221
|
generateTasks({ config, cwd: groupCwd, files, relative }).map((task) =>
|
|
220
222
|
(isFunctionTask(task.commands)
|
|
221
|
-
? getFunctionTask(
|
|
223
|
+
? getFunctionTask({
|
|
224
|
+
abortController,
|
|
225
|
+
command: task.commands,
|
|
226
|
+
continueOnError,
|
|
227
|
+
files: task.fileList,
|
|
228
|
+
})
|
|
222
229
|
: getSpawnedTasks({
|
|
223
230
|
abortController,
|
|
224
|
-
color,
|
|
225
231
|
commands: task.commands,
|
|
226
232
|
continueOnError,
|
|
227
233
|
cwd: groupCwd,
|
|
@@ -246,7 +252,7 @@ export const runAll = async (
|
|
|
246
252
|
})
|
|
247
253
|
|
|
248
254
|
hasDeprecatedGitAdd =
|
|
249
|
-
hasDeprecatedGitAdd || subTasks.some((subTask) => subTask.
|
|
255
|
+
hasDeprecatedGitAdd || subTasks.some((subTask) => subTask.title.includes('git add'))
|
|
250
256
|
|
|
251
257
|
const fileCount = task.fileList.length
|
|
252
258
|
|
|
@@ -254,17 +260,13 @@ export const runAll = async (
|
|
|
254
260
|
title: `${task.pattern}${dim(
|
|
255
261
|
` — ${fileCount} ${fileCount === 1 ? 'file' : 'files'}`
|
|
256
262
|
)}`,
|
|
257
|
-
task:
|
|
258
|
-
task.newListr(
|
|
259
|
-
subTasks,
|
|
260
|
-
// Subtasks should not run in parallel, and should exit on error
|
|
261
|
-
{ concurrent: false, exitOnError: !continueOnError }
|
|
262
|
-
),
|
|
263
|
+
task: subTasks,
|
|
263
264
|
skip: () => {
|
|
264
265
|
// Skip task when no files matched
|
|
265
266
|
if (fileCount === 0) {
|
|
266
267
|
return `${task.pattern}${dim(' — no files')}`
|
|
267
268
|
}
|
|
269
|
+
|
|
268
270
|
return false
|
|
269
271
|
},
|
|
270
272
|
}
|
|
@@ -272,33 +274,29 @@ export const runAll = async (
|
|
|
272
274
|
)
|
|
273
275
|
)
|
|
274
276
|
|
|
275
|
-
|
|
277
|
+
tasks.push({
|
|
276
278
|
title:
|
|
277
279
|
`${configName}${dim(` — ${files.length} ${files.length > 1 ? 'files' : 'file'}`)}` +
|
|
278
280
|
(chunkCount > 1 ? dim(` (chunk ${index + 1}/${chunkCount})...`) : ''),
|
|
279
|
-
task:
|
|
280
|
-
task.newListr(chunkListrTasks, { concurrent, exitOnError: !continueOnError }),
|
|
281
|
+
task: taskChunk,
|
|
281
282
|
skip: () => {
|
|
282
|
-
// Skip if the first step (backup) failed
|
|
283
|
-
if (ctx.errors.has(GitError)) return SKIPPED_GIT_ERROR
|
|
284
283
|
// Skip chunk when no every task is skipped (due to no matches)
|
|
285
|
-
if (
|
|
284
|
+
if (taskChunk.every((task) => task.skip())) {
|
|
286
285
|
return `${configName}${dim(' — no tasks to run')}`
|
|
287
286
|
}
|
|
287
|
+
|
|
288
288
|
return false
|
|
289
289
|
},
|
|
290
290
|
})
|
|
291
291
|
}
|
|
292
292
|
}
|
|
293
293
|
|
|
294
|
-
if (hasDeprecatedGitAdd
|
|
295
|
-
|
|
294
|
+
if (hasDeprecatedGitAdd) {
|
|
295
|
+
thisLogger.warn(deprecatedGitAdd())
|
|
296
296
|
}
|
|
297
297
|
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
if (listrTasks.every((task) => task.skip())) {
|
|
301
|
-
if (!quiet) ctx.output.push(NO_TASKS)
|
|
298
|
+
if (tasks.every((task) => task.skip())) {
|
|
299
|
+
if (!quiet) ctx.output.push(noTasks())
|
|
302
300
|
return ctx
|
|
303
301
|
}
|
|
304
302
|
|
|
@@ -318,60 +316,62 @@ export const runAll = async (
|
|
|
318
316
|
diffFilter,
|
|
319
317
|
failOnChanges,
|
|
320
318
|
gitConfigDir,
|
|
319
|
+
logger: thisLogger,
|
|
321
320
|
matchedFileChunks,
|
|
322
321
|
topLevelDir,
|
|
323
322
|
})
|
|
324
323
|
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
}
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
324
|
+
await git.prepare(ctx)
|
|
325
|
+
|
|
326
|
+
if (shouldHidePartiallyStagedFiles(ctx)) {
|
|
327
|
+
await git.hidePartiallyStagedChanges(ctx)
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
if (abortController.signal.aborted || ctx.errors.size > 0) {
|
|
331
|
+
thisLogger.log(`${figures.cancelled()} ${dim(`Skipped running tasks…`)}`)
|
|
332
|
+
} else {
|
|
333
|
+
await git.runTasks(ctx, tasks, { abortController, concurrent })
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
if (updateIndexSkipped(ctx)) {
|
|
337
|
+
thisLogger.log(`${figures.cancelled()} ${dim('Skipped staging changes from tasks…')}`)
|
|
338
|
+
} else {
|
|
339
|
+
await git.updateIndex(ctx)
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
if (shouldRestoreUnstagedChanges(ctx)) {
|
|
343
|
+
if (restoreUnstagedChangesSkipped(ctx)) {
|
|
344
|
+
thisLogger.log(`${figures.cancelled()} ${dim('Skipped restoring unstaged changes…')}`)
|
|
345
|
+
} else {
|
|
346
|
+
await git.restoreUnstagedChanges(ctx)
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
if (shouldRestoreUntrackedFiles(ctx)) {
|
|
351
|
+
if (restoreUnstagedChangesSkipped(ctx)) {
|
|
352
|
+
thisLogger.log(`${figures.cancelled()} ${dim('Skipped restoring untracked files…')}`)
|
|
353
|
+
} else {
|
|
354
|
+
await git.restoreUntrackedFiles(ctx)
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
if (restoreOriginalStateEnabled(ctx)) {
|
|
359
|
+
if (restoreOriginalStateSkipped(ctx)) {
|
|
360
|
+
thisLogger.log(
|
|
361
|
+
`${figures.cancelled()} ${dim('Skipped reverting to original state because of errors…')}`
|
|
362
|
+
)
|
|
363
|
+
} else {
|
|
364
|
+
await git.restoreOriginalState(ctx)
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
if (cleanupEnabled(ctx)) {
|
|
369
|
+
if (cleanupSkipped(ctx)) {
|
|
370
|
+
thisLogger.log(`${figures.cancelled()} ${dim('Skipped cleaning up temporary files…')}`)
|
|
371
|
+
} else {
|
|
372
|
+
await git.cleanup(ctx)
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
375
|
|
|
376
376
|
if (ctx.errors.size > 0) {
|
|
377
377
|
throw createError(ctx)
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
import * as colors from './colors.js'
|
|
2
|
+
import { createDebug } from './debug.js'
|
|
3
|
+
import * as figures from './figures.js'
|
|
4
|
+
|
|
5
|
+
const debugLog = createDebug('lint-staged:runParallelTasks')
|
|
6
|
+
|
|
7
|
+
/** @param {Number | boolean} options.concurrent Boolean value for whether to run concurrently */
|
|
8
|
+
export const parseConcurrency = (concurrent) => {
|
|
9
|
+
if (concurrent === true || concurrent === Infinity) {
|
|
10
|
+
return Infinity
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
if (concurrent === false || concurrent < 1 || !Number.isInteger(concurrent)) {
|
|
14
|
+
return 1
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
return concurrent
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* @param {Object} ctx
|
|
22
|
+
* @param {Array} tasks
|
|
23
|
+
* @param {Object} options
|
|
24
|
+
* @param {AbortController} abortController
|
|
25
|
+
* @param {Number | boolean} [options.concurrent=true] Boolean value for whether to run concurrently,
|
|
26
|
+
* or a number value controls the number of concurrent executions
|
|
27
|
+
*/
|
|
28
|
+
export const runParallelTasks = async (
|
|
29
|
+
ctx,
|
|
30
|
+
tasks,
|
|
31
|
+
{ abortController, concurrent = true, logger }
|
|
32
|
+
) => {
|
|
33
|
+
const concurrency = parseConcurrency(concurrent)
|
|
34
|
+
|
|
35
|
+
debugLog('Running parallel tasks with concurrency:', concurrency)
|
|
36
|
+
|
|
37
|
+
const indent = tasks.length > 1 ? ' ' : ' '
|
|
38
|
+
const allPerGlobTasks = []
|
|
39
|
+
|
|
40
|
+
// the array of actual tasks is split per config and per glob
|
|
41
|
+
for (const perConfig of tasks) {
|
|
42
|
+
if (perConfig.skip()) {
|
|
43
|
+
debugLog('Skipped configuration:', perConfig.title)
|
|
44
|
+
} else {
|
|
45
|
+
debugLog('Running configuration:', perConfig.title)
|
|
46
|
+
|
|
47
|
+
if (tasks.length > 1) {
|
|
48
|
+
// display name of config only when there are multiple
|
|
49
|
+
logger?.log(colors.dim(`${indent}${perConfig.title}`))
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
for (const perGlob of perConfig.task) {
|
|
54
|
+
if (perGlob.skip()) {
|
|
55
|
+
debugLog('Skipped glob from configuration:', perConfig.title, perGlob.title)
|
|
56
|
+
continue
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
if (perConfig.skip()) {
|
|
60
|
+
continue // entire group is skipped, but debug logs still show above
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
debugLog('Running glob:', perGlob.title)
|
|
64
|
+
logger?.log(colors.dim(`${indent} ${perGlob.title}`))
|
|
65
|
+
|
|
66
|
+
for (const task of perGlob.task) {
|
|
67
|
+
debugLog('Running task:', task.title)
|
|
68
|
+
logger?.log(colors.dim(`${indent} ${figures.wip()} ${task.title}`))
|
|
69
|
+
}
|
|
70
|
+
// per-glob tasks run serially
|
|
71
|
+
allPerGlobTasks.push(perGlob.task)
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
logger?.log('') // empty line before all tasks
|
|
76
|
+
|
|
77
|
+
const runSequential = async (tasks) => {
|
|
78
|
+
for (const { title, task } of tasks) {
|
|
79
|
+
if (abortController.signal.aborted) {
|
|
80
|
+
debugLog('Skipped task because aborted:', title)
|
|
81
|
+
// Log all aborted tasks without actually running them
|
|
82
|
+
logger?.warn(`${figures.cancelled()} ${title}`)
|
|
83
|
+
continue
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
try {
|
|
87
|
+
debugLog('Running task:', title)
|
|
88
|
+
await task(ctx)
|
|
89
|
+
debugLog('Done running task:', title)
|
|
90
|
+
logger?.log(`${figures.done()} ${title}`)
|
|
91
|
+
} catch {
|
|
92
|
+
debugLog('Failed to run task:', title)
|
|
93
|
+
logger?.error(colors.red(`${figures.error()} ${title}`))
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
let next = 0
|
|
99
|
+
const worker = async () => {
|
|
100
|
+
while (next < allPerGlobTasks.length) {
|
|
101
|
+
const perGlobTasks = allPerGlobTasks[next++]
|
|
102
|
+
await runSequential(perGlobTasks)
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
const length = Math.min(allPerGlobTasks.length, concurrency)
|
|
107
|
+
debugLog('Running workers with concurrency:', length)
|
|
108
|
+
await Promise.all(Array.from({ length }, worker))
|
|
109
|
+
debugLog('Done running workers')
|
|
110
|
+
|
|
111
|
+
logger?.log('') // empty line after all tasks
|
|
112
|
+
}
|