lint-staged 12.3.7 → 13.0.2
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 +84 -10
- package/bin/lint-staged.js +79 -59
- package/lib/chunkFiles.js +1 -1
- package/lib/dynamicImport.js +1 -1
- package/lib/execGit.js +1 -1
- package/lib/file.js +1 -1
- package/lib/generateTasks.js +3 -4
- package/lib/getDiffCommand.js +18 -0
- package/lib/getStagedFiles.js +4 -8
- package/lib/gitWorkflow.js +6 -4
- package/lib/groupFilesByConfig.js +55 -0
- package/lib/index.js +58 -22
- package/lib/messages.js +10 -2
- package/lib/parseGitZOutput.js +4 -2
- package/lib/printTaskOutput.js +1 -1
- package/lib/resolveConfig.js +1 -1
- package/lib/resolveGitRepo.js +2 -2
- package/lib/resolveTaskFn.js +43 -18
- package/lib/runAll.js +42 -28
- package/lib/searchConfigs.js +78 -24
- package/lib/state.js +3 -0
- package/lib/validateOptions.js +3 -2
- package/package.json +24 -24
- package/lib/getConfigGroups.js +0 -105
package/lib/parseGitZOutput.js
CHANGED
package/lib/printTaskOutput.js
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
*/
|
|
6
6
|
export const printTaskOutput = (ctx = {}, logger) => {
|
|
7
7
|
if (!Array.isArray(ctx.output)) return
|
|
8
|
-
const log = ctx.errors
|
|
8
|
+
const log = ctx.errors?.size > 0 ? logger.error : logger.log
|
|
9
9
|
for (const line of ctx.output) {
|
|
10
10
|
log(line)
|
|
11
11
|
}
|
package/lib/resolveConfig.js
CHANGED
package/lib/resolveGitRepo.js
CHANGED
package/lib/resolveTaskFn.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { redBright, dim } from 'colorette'
|
|
2
|
-
import execa from 'execa'
|
|
2
|
+
import { execa, execaCommand } from 'execa'
|
|
3
3
|
import debug from 'debug'
|
|
4
4
|
import { parseArgsStringToArgv } from 'string-argv'
|
|
5
5
|
import pidTree from 'pidtree'
|
|
@@ -8,7 +8,7 @@ import { error, info } from './figures.js'
|
|
|
8
8
|
import { getInitialState } from './state.js'
|
|
9
9
|
import { TaskError } from './symbols.js'
|
|
10
10
|
|
|
11
|
-
const
|
|
11
|
+
const TASK_ERROR = 'lint-staged:taskError'
|
|
12
12
|
|
|
13
13
|
const debugLog = debug('lint-staged:resolveTaskFn')
|
|
14
14
|
|
|
@@ -46,31 +46,52 @@ const handleOutput = (command, result, ctx, isError = false) => {
|
|
|
46
46
|
}
|
|
47
47
|
}
|
|
48
48
|
|
|
49
|
+
/**
|
|
50
|
+
* Kill an execa process along with all its child processes.
|
|
51
|
+
* @param {execa.ExecaChildProcess<string>} execaProcess
|
|
52
|
+
*/
|
|
53
|
+
const killExecaProcess = async (execaProcess) => {
|
|
54
|
+
try {
|
|
55
|
+
const childPids = await pidTree(execaProcess.pid)
|
|
56
|
+
for (const childPid of childPids) {
|
|
57
|
+
try {
|
|
58
|
+
process.kill(childPid)
|
|
59
|
+
} catch (error) {
|
|
60
|
+
debugLog(`Failed to kill process with pid "%d": %o`, childPid, error)
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
} catch (error) {
|
|
64
|
+
// Suppress "No matching pid found" error. This probably means
|
|
65
|
+
// the process already died before executing.
|
|
66
|
+
debugLog(`Failed to kill process with pid "%d": %o`, execaProcess.pid, error)
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
// The execa process is killed separately in order to get the `KILLED` status.
|
|
70
|
+
execaProcess.kill()
|
|
71
|
+
}
|
|
72
|
+
|
|
49
73
|
/**
|
|
50
74
|
* Interrupts the execution of the execa process that we spawned if
|
|
51
75
|
* another task adds an error to the context.
|
|
52
76
|
*
|
|
53
77
|
* @param {Object} ctx
|
|
54
78
|
* @param {execa.ExecaChildProcess<string>} execaChildProcess
|
|
55
|
-
* @returns {
|
|
79
|
+
* @returns {() => Promise<void>} Function that clears the interval that
|
|
56
80
|
* checks the context.
|
|
57
81
|
*/
|
|
58
82
|
const interruptExecutionOnError = (ctx, execaChildProcess) => {
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
// The execa process is killed separately in order
|
|
65
|
-
// to get the `KILLED` status.
|
|
66
|
-
execaChildProcess.kill()
|
|
67
|
-
}
|
|
83
|
+
let killPromise
|
|
84
|
+
|
|
85
|
+
const errorListener = async () => {
|
|
86
|
+
killPromise = killExecaProcess(execaChildProcess)
|
|
87
|
+
await killPromise
|
|
68
88
|
}
|
|
69
89
|
|
|
70
|
-
|
|
90
|
+
ctx.events.on(TASK_ERROR, errorListener, { once: true })
|
|
71
91
|
|
|
72
|
-
return () => {
|
|
73
|
-
|
|
92
|
+
return async () => {
|
|
93
|
+
ctx.events.off(TASK_ERROR, errorListener)
|
|
94
|
+
await killPromise
|
|
74
95
|
}
|
|
75
96
|
}
|
|
76
97
|
|
|
@@ -89,6 +110,10 @@ const interruptExecutionOnError = (ctx, execaChildProcess) => {
|
|
|
89
110
|
*/
|
|
90
111
|
const makeErr = (command, result, ctx) => {
|
|
91
112
|
ctx.errors.add(TaskError)
|
|
113
|
+
|
|
114
|
+
// https://nodejs.org/api/events.html#error-events
|
|
115
|
+
ctx.events.emit(TASK_ERROR, TaskError)
|
|
116
|
+
|
|
92
117
|
handleOutput(command, result, ctx, true)
|
|
93
118
|
const tag = getTag(result)
|
|
94
119
|
return new Error(`${redBright(command)} ${dim(`[${tag}]`)}`)
|
|
@@ -105,7 +130,7 @@ const makeErr = (command, result, ctx) => {
|
|
|
105
130
|
* @param {Array<string>} options.files — Filepaths to run the linter task against
|
|
106
131
|
* @param {Boolean} [options.shell] — Whether to skip parsing linter task for better shell support
|
|
107
132
|
* @param {Boolean} [options.verbose] — Always show task verbose
|
|
108
|
-
* @returns {
|
|
133
|
+
* @returns {() => Promise<Array<string>>}
|
|
109
134
|
*/
|
|
110
135
|
export const resolveTaskFn = ({
|
|
111
136
|
command,
|
|
@@ -133,12 +158,12 @@ export const resolveTaskFn = ({
|
|
|
133
158
|
|
|
134
159
|
return async (ctx = getInitialState()) => {
|
|
135
160
|
const execaChildProcess = shell
|
|
136
|
-
?
|
|
161
|
+
? execaCommand(isFn ? command : `${command} ${files.join(' ')}`, execaOptions)
|
|
137
162
|
: execa(cmd, isFn ? args : args.concat(files), execaOptions)
|
|
138
163
|
|
|
139
164
|
const quitInterruptCheck = interruptExecutionOnError(ctx, execaChildProcess)
|
|
140
165
|
const result = await execaChildProcess
|
|
141
|
-
quitInterruptCheck()
|
|
166
|
+
await quitInterruptCheck()
|
|
142
167
|
|
|
143
168
|
if (result.failed || result.killed || result.signal != null) {
|
|
144
169
|
throw makeErr(command, result, ctx)
|
package/lib/runAll.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/** @typedef {import('./index').Logger} Logger */
|
|
2
2
|
|
|
3
|
-
import path from 'path'
|
|
3
|
+
import path from 'node:path'
|
|
4
4
|
|
|
5
5
|
import { dim } from 'colorette'
|
|
6
6
|
import debug from 'debug'
|
|
@@ -10,10 +10,10 @@ import normalize from 'normalize-path'
|
|
|
10
10
|
import { chunkFiles } from './chunkFiles.js'
|
|
11
11
|
import { execGit } from './execGit.js'
|
|
12
12
|
import { generateTasks } from './generateTasks.js'
|
|
13
|
-
import { getConfigGroups } from './getConfigGroups.js'
|
|
14
13
|
import { getRenderer } from './getRenderer.js'
|
|
15
14
|
import { getStagedFiles } from './getStagedFiles.js'
|
|
16
15
|
import { GitWorkflow } from './gitWorkflow.js'
|
|
16
|
+
import { groupFilesByConfig } from './groupFilesByConfig.js'
|
|
17
17
|
import { makeCmdTasks } from './makeCmdTasks.js'
|
|
18
18
|
import {
|
|
19
19
|
DEPRECATED_GIT_ADD,
|
|
@@ -52,6 +52,8 @@ const createError = (ctx) => Object.assign(new Error('lint-staged failed'), { ct
|
|
|
52
52
|
* @param {string} [options.configPath] - Explicit path to a config file
|
|
53
53
|
* @param {string} [options.cwd] - Current working directory
|
|
54
54
|
* @param {boolean} [options.debug] - Enable debug mode
|
|
55
|
+
* @param {string} [options.diff] - Override the default "--staged" flag of "git diff" to get list of files
|
|
56
|
+
* @param {string} [options.diffFilter] - Override the default "--diff-filter=ACMR" flag of "git diff" to get list of files
|
|
55
57
|
* @param {number} [options.maxArgLength] - Maximum argument string length
|
|
56
58
|
* @param {boolean} [options.quiet] - Disable lint-staged’s own console output
|
|
57
59
|
* @param {boolean} [options.relative] - Pass relative filepaths to tasks
|
|
@@ -69,6 +71,8 @@ export const runAll = async (
|
|
|
69
71
|
configPath,
|
|
70
72
|
cwd,
|
|
71
73
|
debug = false,
|
|
74
|
+
diff,
|
|
75
|
+
diffFilter,
|
|
72
76
|
maxArgLength,
|
|
73
77
|
quiet = false,
|
|
74
78
|
relative = false,
|
|
@@ -100,13 +104,14 @@ export const runAll = async (
|
|
|
100
104
|
.then(() => true)
|
|
101
105
|
.catch(() => false)
|
|
102
106
|
|
|
103
|
-
// Lint-staged should create a backup stash only when there's an initial commit
|
|
104
|
-
|
|
107
|
+
// Lint-staged should create a backup stash only when there's an initial commit,
|
|
108
|
+
// and when using the default list of staged files
|
|
109
|
+
ctx.shouldBackup = hasInitialCommit && stash && diff === undefined
|
|
105
110
|
if (!ctx.shouldBackup) {
|
|
106
|
-
logger.warn(skippingBackup(hasInitialCommit))
|
|
111
|
+
logger.warn(skippingBackup(hasInitialCommit, diff))
|
|
107
112
|
}
|
|
108
113
|
|
|
109
|
-
const files = await getStagedFiles({ cwd: gitDir })
|
|
114
|
+
const files = await getStagedFiles({ cwd: gitDir, diff, diffFilter })
|
|
110
115
|
if (!files) {
|
|
111
116
|
if (!quiet) ctx.output.push(FAILED_GET_STAGED_FILES)
|
|
112
117
|
ctx.errors.add(GetStagedFilesError)
|
|
@@ -120,11 +125,8 @@ export const runAll = async (
|
|
|
120
125
|
return ctx
|
|
121
126
|
}
|
|
122
127
|
|
|
123
|
-
const
|
|
124
|
-
|
|
125
|
-
const hasExplicitConfig = configObject || configPath
|
|
126
|
-
const foundConfigs = hasExplicitConfig ? null : await searchConfigs(gitDir, logger)
|
|
127
|
-
const numberOfConfigs = hasExplicitConfig ? 1 : Object.keys(foundConfigs).length
|
|
128
|
+
const foundConfigs = await searchConfigs({ configObject, configPath, cwd, gitDir }, logger)
|
|
129
|
+
const numberOfConfigs = Object.keys(foundConfigs).length
|
|
128
130
|
|
|
129
131
|
// Throw if no configurations were found
|
|
130
132
|
if (numberOfConfigs === 0) {
|
|
@@ -132,7 +134,11 @@ export const runAll = async (
|
|
|
132
134
|
throw createError(ctx, ConfigNotFoundError)
|
|
133
135
|
}
|
|
134
136
|
|
|
135
|
-
|
|
137
|
+
const filesByConfig = await groupFilesByConfig({
|
|
138
|
+
configs: foundConfigs,
|
|
139
|
+
files,
|
|
140
|
+
singleConfigMode: configObject || configPath !== undefined,
|
|
141
|
+
})
|
|
136
142
|
|
|
137
143
|
const hasMultipleConfigs = numberOfConfigs > 1
|
|
138
144
|
|
|
@@ -152,8 +158,9 @@ export const runAll = async (
|
|
|
152
158
|
// Set of all staged files that matched a task glob. Values in a set are unique.
|
|
153
159
|
const matchedFiles = new Set()
|
|
154
160
|
|
|
155
|
-
for (const [configPath, { config, files }] of Object.entries(
|
|
156
|
-
const
|
|
161
|
+
for (const [configPath, { config, files }] of Object.entries(filesByConfig)) {
|
|
162
|
+
const configName = configPath ? normalize(path.relative(cwd, configPath)) : 'Config object'
|
|
163
|
+
|
|
157
164
|
const stagedFileChunks = chunkFiles({ baseDir: gitDir, files, maxArgLength, relative })
|
|
158
165
|
|
|
159
166
|
// Use actual cwd if it's specified, or there's only a single config file.
|
|
@@ -196,15 +203,15 @@ export const runAll = async (
|
|
|
196
203
|
const fileCount = task.fileList.length
|
|
197
204
|
|
|
198
205
|
return {
|
|
199
|
-
title: `${task.pattern}${dim(
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
exitOnError: true
|
|
207
|
-
|
|
206
|
+
title: `${task.pattern}${dim(
|
|
207
|
+
` — ${fileCount} ${fileCount === 1 ? 'file' : 'files'}`
|
|
208
|
+
)}`,
|
|
209
|
+
task: async (ctx, task) =>
|
|
210
|
+
task.newListr(
|
|
211
|
+
subTasks,
|
|
212
|
+
// Subtasks should not run in parallel, and should exit on error
|
|
213
|
+
{ concurrent: false, exitOnError: true }
|
|
214
|
+
),
|
|
208
215
|
skip: () => {
|
|
209
216
|
// Skip task when no files matched
|
|
210
217
|
if (fileCount === 0) {
|
|
@@ -219,15 +226,15 @@ export const runAll = async (
|
|
|
219
226
|
|
|
220
227
|
listrTasks.push({
|
|
221
228
|
title:
|
|
222
|
-
`${
|
|
229
|
+
`${configName}${dim(` — ${files.length} ${files.length > 1 ? 'files' : 'file'}`)}` +
|
|
223
230
|
(chunkCount > 1 ? dim(` (chunk ${index + 1}/${chunkCount})...`) : ''),
|
|
224
|
-
task: () =>
|
|
231
|
+
task: (ctx, task) => task.newListr(chunkListrTasks, { concurrent, exitOnError: true }),
|
|
225
232
|
skip: () => {
|
|
226
233
|
// Skip if the first step (backup) failed
|
|
227
234
|
if (ctx.errors.has(GitError)) return SKIPPED_GIT_ERROR
|
|
228
235
|
// Skip chunk when no every task is skipped (due to no matches)
|
|
229
236
|
if (chunkListrTasks.every((task) => task.skip())) {
|
|
230
|
-
return `${
|
|
237
|
+
return `${configName}${dim(' — no tasks to run')}`
|
|
231
238
|
}
|
|
232
239
|
return false
|
|
233
240
|
},
|
|
@@ -255,7 +262,14 @@ export const runAll = async (
|
|
|
255
262
|
relative: false,
|
|
256
263
|
})
|
|
257
264
|
|
|
258
|
-
const git = new GitWorkflow({
|
|
265
|
+
const git = new GitWorkflow({
|
|
266
|
+
allowEmpty,
|
|
267
|
+
gitConfigDir,
|
|
268
|
+
gitDir,
|
|
269
|
+
matchedFileChunks,
|
|
270
|
+
diff,
|
|
271
|
+
diffFilter,
|
|
272
|
+
})
|
|
259
273
|
|
|
260
274
|
const runner = new Listr(
|
|
261
275
|
[
|
|
@@ -270,7 +284,7 @@ export const runAll = async (
|
|
|
270
284
|
},
|
|
271
285
|
{
|
|
272
286
|
title: `Running tasks for staged files...`,
|
|
273
|
-
task: () =>
|
|
287
|
+
task: (ctx, task) => task.newListr(listrTasks, { concurrent }),
|
|
274
288
|
skip: () => listrTasks.every((task) => task.skip()),
|
|
275
289
|
},
|
|
276
290
|
{
|
package/lib/searchConfigs.js
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
/** @typedef {import('./index').Logger} Logger */
|
|
2
2
|
|
|
3
|
-
import
|
|
3
|
+
import path from 'node:path'
|
|
4
4
|
|
|
5
|
+
import debug from 'debug'
|
|
5
6
|
import normalize from 'normalize-path'
|
|
6
7
|
|
|
7
8
|
import { execGit } from './execGit.js'
|
|
@@ -9,38 +10,70 @@ import { loadConfig, searchPlaces } from './loadConfig.js'
|
|
|
9
10
|
import { parseGitZOutput } from './parseGitZOutput.js'
|
|
10
11
|
import { validateConfig } from './validateConfig.js'
|
|
11
12
|
|
|
13
|
+
const debugLog = debug('lint-staged:searchConfigs')
|
|
14
|
+
|
|
12
15
|
const EXEC_GIT = ['ls-files', '-z', '--full-name']
|
|
13
16
|
|
|
14
|
-
const filterPossibleConfigFiles = (
|
|
17
|
+
const filterPossibleConfigFiles = (files) =>
|
|
18
|
+
files.filter((file) => searchPlaces.includes(path.basename(file)))
|
|
15
19
|
|
|
16
20
|
const numberOfLevels = (file) => file.split('/').length
|
|
17
21
|
|
|
18
22
|
const sortDeepestParth = (a, b) => (numberOfLevels(a) > numberOfLevels(b) ? -1 : 1)
|
|
19
23
|
|
|
24
|
+
const isInsideDirectory = (dir) => (file) => file.startsWith(normalize(dir))
|
|
25
|
+
|
|
20
26
|
/**
|
|
21
|
-
* Search all config files from the git repository
|
|
27
|
+
* Search all config files from the git repository, preferring those inside `cwd`.
|
|
22
28
|
*
|
|
23
|
-
* @param {
|
|
29
|
+
* @param {object} options
|
|
30
|
+
* @param {Object} [options.configObject] - Explicit config object from the js API
|
|
31
|
+
* @param {string} [options.configPath] - Explicit path to a config file
|
|
32
|
+
* @param {string} [options.cwd] - Current working directory
|
|
24
33
|
* @param {Logger} logger
|
|
25
|
-
*
|
|
34
|
+
*
|
|
35
|
+
* @returns {Promise<{ [key: string]: { config: *, files: string[] } }>} found configs with filepath as key, and config as value
|
|
26
36
|
*/
|
|
27
|
-
export const searchConfigs = async (
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
)
|
|
37
|
+
export const searchConfigs = async (
|
|
38
|
+
{ configObject, configPath, cwd = process.cwd(), gitDir = cwd },
|
|
39
|
+
logger
|
|
40
|
+
) => {
|
|
41
|
+
debugLog('Searching for configuration files...')
|
|
42
|
+
|
|
43
|
+
// Return explicit config object from js API
|
|
44
|
+
if (configObject) {
|
|
45
|
+
debugLog('Using single direct configuration object...')
|
|
46
|
+
|
|
47
|
+
return { '': validateConfig(configObject, 'config object', logger) }
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// Use only explicit config path instead of discovering multiple
|
|
51
|
+
if (configPath) {
|
|
52
|
+
debugLog('Using single configuration path...')
|
|
53
|
+
|
|
54
|
+
const { config, filepath } = await loadConfig({ configPath }, logger)
|
|
32
55
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
56
|
+
if (!config) return {}
|
|
57
|
+
return { [configPath]: validateConfig(config, filepath, logger) }
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
const [cachedFiles, otherFiles] = await Promise.all([
|
|
61
|
+
/** Get all possible config files known to git */
|
|
62
|
+
execGit(EXEC_GIT, { cwd: gitDir }).then(parseGitZOutput).then(filterPossibleConfigFiles),
|
|
63
|
+
/** Get all possible config files from uncommitted files */
|
|
64
|
+
execGit([...EXEC_GIT, '--others', '--exclude-standard'], { cwd: gitDir })
|
|
65
|
+
.then(parseGitZOutput)
|
|
66
|
+
.then(filterPossibleConfigFiles),
|
|
67
|
+
])
|
|
37
68
|
|
|
38
69
|
/** Sort possible config files so that deepest is first */
|
|
39
70
|
const possibleConfigFiles = [...cachedFiles, ...otherFiles]
|
|
40
|
-
.map((file) => join(gitDir, file))
|
|
41
|
-
.
|
|
71
|
+
.map((file) => normalize(path.join(gitDir, file)))
|
|
72
|
+
.filter(isInsideDirectory(cwd))
|
|
42
73
|
.sort(sortDeepestParth)
|
|
43
74
|
|
|
75
|
+
debugLog('Found possible config files:', possibleConfigFiles)
|
|
76
|
+
|
|
44
77
|
/** Create object with key as config file, and value as null */
|
|
45
78
|
const configs = possibleConfigFiles.reduce(
|
|
46
79
|
(acc, configPath) => Object.assign(acc, { [configPath]: null }),
|
|
@@ -49,15 +82,17 @@ export const searchConfigs = async (gitDir = process.cwd(), logger) => {
|
|
|
49
82
|
|
|
50
83
|
/** Load and validate all configs to the above object */
|
|
51
84
|
await Promise.all(
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
configs[filepath] = validateConfig(config, filepath, logger)
|
|
85
|
+
Object.keys(configs).map((configPath) =>
|
|
86
|
+
loadConfig({ configPath }, logger).then(({ config, filepath }) => {
|
|
87
|
+
if (config) {
|
|
88
|
+
if (configPath !== filepath) {
|
|
89
|
+
debugLog('Config file "%s" resolved to "%s"', configPath, filepath)
|
|
58
90
|
}
|
|
59
|
-
|
|
60
|
-
|
|
91
|
+
|
|
92
|
+
configs[configPath] = validateConfig(config, filepath, logger)
|
|
93
|
+
}
|
|
94
|
+
})
|
|
95
|
+
)
|
|
61
96
|
)
|
|
62
97
|
|
|
63
98
|
/** Get validated configs from the above object, without any `null` values (not found) */
|
|
@@ -65,5 +100,24 @@ export const searchConfigs = async (gitDir = process.cwd(), logger) => {
|
|
|
65
100
|
.filter(([, value]) => !!value)
|
|
66
101
|
.reduce((acc, [key, value]) => ({ ...acc, [key]: value }), {})
|
|
67
102
|
|
|
103
|
+
/**
|
|
104
|
+
* Try to find a single config from parent directories
|
|
105
|
+
* to match old behavior before monorepo support
|
|
106
|
+
*/
|
|
107
|
+
if (!Object.keys(foundConfigs).length) {
|
|
108
|
+
debugLog('Could not find config files inside "%s"', cwd)
|
|
109
|
+
|
|
110
|
+
const { config, filepath } = await loadConfig({ cwd }, logger)
|
|
111
|
+
if (config) {
|
|
112
|
+
debugLog('Found parent configuration file from "%s"', filepath)
|
|
113
|
+
|
|
114
|
+
foundConfigs[filepath] = validateConfig(config, filepath, logger)
|
|
115
|
+
} else {
|
|
116
|
+
debugLog('Could not find parent configuration files from "%s"', cwd)
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
debugLog('Found %d config files', Object.keys(foundConfigs).length)
|
|
121
|
+
|
|
68
122
|
return foundConfigs
|
|
69
123
|
}
|
package/lib/state.js
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import EventEmitter from 'events'
|
|
2
|
+
|
|
1
3
|
import { GIT_ERROR, TASK_ERROR } from './messages.js'
|
|
2
4
|
import {
|
|
3
5
|
ApplyEmptyCommitError,
|
|
@@ -11,6 +13,7 @@ export const getInitialState = ({ quiet = false } = {}) => ({
|
|
|
11
13
|
hasPartiallyStagedFiles: null,
|
|
12
14
|
shouldBackup: null,
|
|
13
15
|
errors: new Set([]),
|
|
16
|
+
events: new EventEmitter(),
|
|
14
17
|
output: [],
|
|
15
18
|
quiet,
|
|
16
19
|
})
|
package/lib/validateOptions.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "lint-staged",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "13.0.2",
|
|
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": "^
|
|
17
|
+
"node": "^14.13.1 || >=16.0.0"
|
|
18
18
|
},
|
|
19
19
|
"type": "module",
|
|
20
20
|
"bin": "./bin/lint-staged.js",
|
|
@@ -33,36 +33,36 @@
|
|
|
33
33
|
},
|
|
34
34
|
"dependencies": {
|
|
35
35
|
"cli-truncate": "^3.1.0",
|
|
36
|
-
"colorette": "^2.0.
|
|
37
|
-
"commander": "^
|
|
38
|
-
"debug": "^4.3.
|
|
39
|
-
"execa": "^
|
|
40
|
-
"lilconfig": "2.0.
|
|
41
|
-
"listr2": "^4.0.
|
|
42
|
-
"micromatch": "^4.0.
|
|
36
|
+
"colorette": "^2.0.17",
|
|
37
|
+
"commander": "^9.3.0",
|
|
38
|
+
"debug": "^4.3.4",
|
|
39
|
+
"execa": "^6.1.0",
|
|
40
|
+
"lilconfig": "2.0.5",
|
|
41
|
+
"listr2": "^4.0.5",
|
|
42
|
+
"micromatch": "^4.0.5",
|
|
43
43
|
"normalize-path": "^3.0.0",
|
|
44
|
-
"object-inspect": "^1.12.
|
|
45
|
-
"pidtree": "^0.
|
|
44
|
+
"object-inspect": "^1.12.2",
|
|
45
|
+
"pidtree": "^0.6.0",
|
|
46
46
|
"string-argv": "^0.3.1",
|
|
47
|
-
"
|
|
48
|
-
"yaml": "^1.10.2"
|
|
47
|
+
"yaml": "^2.1.1"
|
|
49
48
|
},
|
|
50
49
|
"devDependencies": {
|
|
51
|
-
"@babel/core": "^7.
|
|
52
|
-
"@babel/eslint-parser": "^7.
|
|
53
|
-
"@babel/preset-env": "^7.
|
|
54
|
-
"babel-jest": "^
|
|
50
|
+
"@babel/core": "^7.18.2",
|
|
51
|
+
"@babel/eslint-parser": "^7.18.2",
|
|
52
|
+
"@babel/preset-env": "^7.18.2",
|
|
53
|
+
"babel-jest": "^28.1.1",
|
|
54
|
+
"babel-plugin-transform-imports": "2.0.0",
|
|
55
55
|
"consolemock": "^1.1.0",
|
|
56
|
-
"eslint": "^8.
|
|
57
|
-
"eslint-config-prettier": "^8.
|
|
58
|
-
"eslint-plugin-import": "^2.
|
|
56
|
+
"eslint": "^8.17.0",
|
|
57
|
+
"eslint-config-prettier": "^8.5.0",
|
|
58
|
+
"eslint-plugin-import": "^2.26.0",
|
|
59
59
|
"eslint-plugin-node": "^11.1.0",
|
|
60
60
|
"eslint-plugin-prettier": "^4.0.0",
|
|
61
|
-
"fs-extra": "^10.
|
|
62
|
-
"husky": "^
|
|
63
|
-
"jest": "^
|
|
61
|
+
"fs-extra": "^10.1.0",
|
|
62
|
+
"husky": "^8.0.1",
|
|
63
|
+
"jest": "^28.1.1",
|
|
64
64
|
"jest-snapshot-serializer-ansi": "^1.0.0",
|
|
65
|
-
"prettier": "^2.
|
|
65
|
+
"prettier": "^2.6.2"
|
|
66
66
|
},
|
|
67
67
|
"keywords": [
|
|
68
68
|
"lint",
|
package/lib/getConfigGroups.js
DELETED
|
@@ -1,105 +0,0 @@
|
|
|
1
|
-
/** @typedef {import('./index').Logger} Logger */
|
|
2
|
-
|
|
3
|
-
import path from 'path'
|
|
4
|
-
|
|
5
|
-
import debug from 'debug'
|
|
6
|
-
import objectInspect from 'object-inspect'
|
|
7
|
-
|
|
8
|
-
import { loadConfig } from './loadConfig.js'
|
|
9
|
-
import { ConfigNotFoundError } from './symbols.js'
|
|
10
|
-
import { validateConfig } from './validateConfig.js'
|
|
11
|
-
|
|
12
|
-
const debugLog = debug('lint-staged:getConfigGroups')
|
|
13
|
-
|
|
14
|
-
/**
|
|
15
|
-
* Return matched files grouped by their configuration.
|
|
16
|
-
*
|
|
17
|
-
* @param {object} options
|
|
18
|
-
* @param {Object} [options.configObject] - Explicit config object from the js API
|
|
19
|
-
* @param {string} [options.configPath] - Explicit path to a config file
|
|
20
|
-
* @param {string} [options.cwd] - Current working directory
|
|
21
|
-
* @param {string} [options.files] - List of staged files
|
|
22
|
-
* @param {Logger} logger
|
|
23
|
-
*/
|
|
24
|
-
export const getConfigGroups = async (
|
|
25
|
-
{ configObject, configPath, cwd, files },
|
|
26
|
-
logger = console
|
|
27
|
-
) => {
|
|
28
|
-
debugLog('Grouping configuration files...')
|
|
29
|
-
|
|
30
|
-
// Return explicit config object from js API
|
|
31
|
-
if (configObject) {
|
|
32
|
-
debugLog('Using single direct configuration object...')
|
|
33
|
-
|
|
34
|
-
const config = validateConfig(configObject, 'config object', logger)
|
|
35
|
-
return { '': { config, files } }
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
// Use only explicit config path instead of discovering multiple
|
|
39
|
-
if (configPath) {
|
|
40
|
-
debugLog('Using single configuration path...')
|
|
41
|
-
|
|
42
|
-
const { config, filepath } = await loadConfig({ configPath }, logger)
|
|
43
|
-
|
|
44
|
-
if (!config) {
|
|
45
|
-
logger.error(`${ConfigNotFoundError.message}.`)
|
|
46
|
-
throw ConfigNotFoundError
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
const validatedConfig = validateConfig(config, filepath, logger)
|
|
50
|
-
return { [configPath]: { config: validatedConfig, files } }
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
debugLog('Grouping staged files by their directories...')
|
|
54
|
-
|
|
55
|
-
// Group files by their base directory
|
|
56
|
-
const filesByDir = files.reduce((acc, file) => {
|
|
57
|
-
const dir = path.normalize(path.dirname(file))
|
|
58
|
-
|
|
59
|
-
if (dir in acc) {
|
|
60
|
-
acc[dir].push(file)
|
|
61
|
-
} else {
|
|
62
|
-
acc[dir] = [file]
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
return acc
|
|
66
|
-
}, {})
|
|
67
|
-
|
|
68
|
-
debugLog('Grouped staged files into %d directories:', Object.keys(filesByDir).length)
|
|
69
|
-
debugLog(objectInspect(filesByDir, { indent: 2 }))
|
|
70
|
-
|
|
71
|
-
// Group files by their discovered config
|
|
72
|
-
// { '.lintstagedrc.json': { config: {...}, files: [...] } }
|
|
73
|
-
const configGroups = {}
|
|
74
|
-
|
|
75
|
-
debugLog('Searching config files...')
|
|
76
|
-
|
|
77
|
-
const searchConfig = async (cwd, files = []) => {
|
|
78
|
-
const { config, filepath } = await loadConfig({ cwd }, logger)
|
|
79
|
-
if (!config) {
|
|
80
|
-
debugLog('Found no config from "%s"!', cwd)
|
|
81
|
-
return
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
if (filepath in configGroups) {
|
|
85
|
-
debugLog('Found existing config "%s" from "%s"!', filepath, cwd)
|
|
86
|
-
// Re-use cached config and skip validation
|
|
87
|
-
configGroups[filepath].files.push(...files)
|
|
88
|
-
} else {
|
|
89
|
-
debugLog('Found new config "%s" from "%s"!', filepath, cwd)
|
|
90
|
-
|
|
91
|
-
const validatedConfig = validateConfig(config, filepath, logger)
|
|
92
|
-
configGroups[filepath] = { config: validatedConfig, files }
|
|
93
|
-
}
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
// Start by searching from cwd
|
|
97
|
-
await searchConfig(cwd)
|
|
98
|
-
|
|
99
|
-
// Discover configs from the base directory of each file
|
|
100
|
-
await Promise.all(Object.entries(filesByDir).map(([dir, files]) => searchConfig(dir, files)))
|
|
101
|
-
|
|
102
|
-
debugLog('Grouped staged files into %d groups!', Object.keys(configGroups).length)
|
|
103
|
-
|
|
104
|
-
return configGroups
|
|
105
|
-
}
|