lint-staged 11.1.1 → 11.1.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 +4 -26
- package/lib/generateTasks.js +24 -26
- package/lib/index.js +62 -92
- package/lib/makeCmdTasks.js +2 -2
- package/lib/messages.js +18 -1
- package/lib/validateBraces.js +71 -0
- package/lib/validateConfig.js +71 -56
- package/package.json +1 -2
- package/lib/formatConfig.js +0 -7
package/README.md
CHANGED
|
@@ -445,36 +445,14 @@ For example, here is `jest` running on all `.js` files with the `NODE_ENV` varia
|
|
|
445
445
|
|
|
446
446
|
</details>
|
|
447
447
|
|
|
448
|
-
### Automatically fix code style with `prettier` for any format
|
|
448
|
+
### Automatically fix code style with `prettier` for any format Prettier supports
|
|
449
449
|
|
|
450
450
|
<details>
|
|
451
451
|
<summary>Click to expand</summary>
|
|
452
452
|
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
$ npm i --save-dev micromatch
|
|
457
|
-
```
|
|
458
|
-
|
|
459
|
-
```js
|
|
460
|
-
// lint-staged.config.js
|
|
461
|
-
const micromatch = require('micromatch')
|
|
462
|
-
const prettier = require('prettier')
|
|
463
|
-
|
|
464
|
-
const prettierSupportedExtensions = prettier
|
|
465
|
-
.getSupportInfo()
|
|
466
|
-
.languages.map(({ extensions }) => extensions)
|
|
467
|
-
.flat()
|
|
468
|
-
const addQuotes = (a) => `"${a}"`
|
|
469
|
-
|
|
470
|
-
module.exports = (allStagedFiles) => {
|
|
471
|
-
const prettierFiles = micromatch(
|
|
472
|
-
allStagedFiles,
|
|
473
|
-
prettierSupportedExtensions.map((extension) => `**/*${extension}`)
|
|
474
|
-
)
|
|
475
|
-
return prettierFiles.length > 0
|
|
476
|
-
? [`prettier --write ${prettierFiles.map(addQuotes).join(' ')}`]
|
|
477
|
-
: []
|
|
453
|
+
```json
|
|
454
|
+
{
|
|
455
|
+
"*": "prettier --ignore-unknown --write"
|
|
478
456
|
}
|
|
479
457
|
```
|
|
480
458
|
|
package/lib/generateTasks.js
CHANGED
|
@@ -16,39 +16,35 @@ const debug = require('debug')('lint-staged:gen-tasks')
|
|
|
16
16
|
* @param {boolean} [options.files] - Staged filepaths
|
|
17
17
|
* @param {boolean} [options.relative] - Whether filepaths to should be relative to gitDir
|
|
18
18
|
*/
|
|
19
|
-
|
|
20
|
-
config,
|
|
21
|
-
cwd = process.cwd(),
|
|
22
|
-
gitDir,
|
|
23
|
-
files,
|
|
24
|
-
relative = false,
|
|
25
|
-
}) {
|
|
19
|
+
const generateTasks = ({ config, cwd = process.cwd(), gitDir, files, relative = false }) => {
|
|
26
20
|
debug('Generating linter tasks')
|
|
27
21
|
|
|
28
22
|
const absoluteFiles = files.map((file) => normalize(path.resolve(gitDir, file)))
|
|
29
23
|
const relativeFiles = absoluteFiles.map((file) => normalize(path.relative(cwd, file)))
|
|
30
24
|
|
|
31
|
-
return Object.entries(config).map(([
|
|
25
|
+
return Object.entries(config).map(([rawPattern, commands]) => {
|
|
26
|
+
let pattern = rawPattern
|
|
27
|
+
|
|
32
28
|
const isParentDirPattern = pattern.startsWith('../')
|
|
33
29
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
30
|
+
// Only worry about children of the CWD unless the pattern explicitly
|
|
31
|
+
// specifies that it concerns a parent directory.
|
|
32
|
+
const filteredFiles = relativeFiles.filter((file) => {
|
|
33
|
+
if (isParentDirPattern) return true
|
|
34
|
+
return !file.startsWith('..') && !path.isAbsolute(file)
|
|
35
|
+
})
|
|
36
|
+
|
|
37
|
+
const matches = micromatch(filteredFiles, pattern, {
|
|
38
|
+
cwd,
|
|
39
|
+
dot: true,
|
|
40
|
+
// If the pattern doesn't look like a path, enable `matchBase` to
|
|
41
|
+
// match against filenames in every directory. This makes `*.js`
|
|
42
|
+
// match both `test.js` and `subdirectory/test.js`.
|
|
43
|
+
matchBase: !pattern.includes('/'),
|
|
44
|
+
strictBrackets: true,
|
|
45
|
+
})
|
|
46
|
+
|
|
47
|
+
const fileList = matches.map((file) => normalize(relative ? file : path.resolve(cwd, file)))
|
|
52
48
|
|
|
53
49
|
const task = { pattern, commands, fileList }
|
|
54
50
|
debug('Generated task: \n%O', task)
|
|
@@ -56,3 +52,5 @@ module.exports = function generateTasks({
|
|
|
56
52
|
return task
|
|
57
53
|
})
|
|
58
54
|
}
|
|
55
|
+
|
|
56
|
+
module.exports = generateTasks
|
package/lib/index.js
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
|
-
const dedent = require('dedent')
|
|
4
3
|
const { cosmiconfig } = require('cosmiconfig')
|
|
5
4
|
const debugLog = require('debug')('lint-staged')
|
|
6
5
|
const stringifyObject = require('stringify-object')
|
|
@@ -13,9 +12,7 @@ const {
|
|
|
13
12
|
ConfigNotFoundError,
|
|
14
13
|
GetBackupStashError,
|
|
15
14
|
GitError,
|
|
16
|
-
InvalidOptionsError,
|
|
17
15
|
} = require('./symbols')
|
|
18
|
-
const formatConfig = require('./formatConfig')
|
|
19
16
|
const validateConfig = require('./validateConfig')
|
|
20
17
|
const validateOptions = require('./validateOptions')
|
|
21
18
|
|
|
@@ -85,105 +82,78 @@ const lintStaged = async (
|
|
|
85
82
|
} = {},
|
|
86
83
|
logger = console
|
|
87
84
|
) => {
|
|
88
|
-
|
|
89
|
-
await validateOptions({ shell }, logger)
|
|
85
|
+
await validateOptions({ shell }, logger)
|
|
90
86
|
|
|
91
|
-
|
|
87
|
+
debugLog('Loading config using `cosmiconfig`')
|
|
92
88
|
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
89
|
+
const resolved = configObject
|
|
90
|
+
? { config: configObject, filepath: '(input)' }
|
|
91
|
+
: await loadConfig(configPath)
|
|
96
92
|
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
93
|
+
if (resolved == null) {
|
|
94
|
+
logger.error(`${ConfigNotFoundError.message}.`)
|
|
95
|
+
throw ConfigNotFoundError
|
|
96
|
+
}
|
|
100
97
|
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
// resolved.config is the parsed configuration object
|
|
104
|
-
// resolved.filepath is the path to the config file that was found
|
|
105
|
-
const formattedConfig = formatConfig(resolved.config)
|
|
106
|
-
const config = validateConfig(formattedConfig)
|
|
107
|
-
|
|
108
|
-
if (debug) {
|
|
109
|
-
// Log using logger to be able to test through `consolemock`.
|
|
110
|
-
logger.log('Running lint-staged with the following config:')
|
|
111
|
-
logger.log(stringifyObject(config, { indent: ' ' }))
|
|
112
|
-
} else {
|
|
113
|
-
// We might not be in debug mode but `DEBUG=lint-staged*` could have
|
|
114
|
-
// been set.
|
|
115
|
-
debugLog('lint-staged config:\n%O', config)
|
|
116
|
-
}
|
|
98
|
+
debugLog('Successfully loaded config from `%s`:\n%O', resolved.filepath, resolved.config)
|
|
117
99
|
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
try {
|
|
123
|
-
const ctx = await runAll(
|
|
124
|
-
{
|
|
125
|
-
allowEmpty,
|
|
126
|
-
concurrent,
|
|
127
|
-
config,
|
|
128
|
-
cwd,
|
|
129
|
-
debug,
|
|
130
|
-
maxArgLength,
|
|
131
|
-
quiet,
|
|
132
|
-
relative,
|
|
133
|
-
shell,
|
|
134
|
-
stash,
|
|
135
|
-
verbose,
|
|
136
|
-
},
|
|
137
|
-
logger
|
|
138
|
-
)
|
|
139
|
-
debugLog('Tasks were executed successfully!')
|
|
140
|
-
printTaskOutput(ctx, logger)
|
|
141
|
-
return true
|
|
142
|
-
} catch (runAllError) {
|
|
143
|
-
if (runAllError && runAllError.ctx && runAllError.ctx.errors) {
|
|
144
|
-
const { ctx } = runAllError
|
|
145
|
-
if (ctx.errors.has(ApplyEmptyCommitError)) {
|
|
146
|
-
logger.warn(PREVENTED_EMPTY_COMMIT)
|
|
147
|
-
} else if (ctx.errors.has(GitError) && !ctx.errors.has(GetBackupStashError)) {
|
|
148
|
-
logger.error(GIT_ERROR)
|
|
149
|
-
if (ctx.shouldBackup) {
|
|
150
|
-
// No sense to show this if the backup stash itself is missing.
|
|
151
|
-
logger.error(RESTORE_STASH_EXAMPLE)
|
|
152
|
-
}
|
|
153
|
-
}
|
|
100
|
+
// resolved.config is the parsed configuration object
|
|
101
|
+
// resolved.filepath is the path to the config file that was found
|
|
102
|
+
const config = validateConfig(resolved.config, logger)
|
|
154
103
|
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
104
|
+
if (debug) {
|
|
105
|
+
// Log using logger to be able to test through `consolemock`.
|
|
106
|
+
logger.log('Running lint-staged with the following config:')
|
|
107
|
+
logger.log(stringifyObject(config, { indent: ' ' }))
|
|
108
|
+
} else {
|
|
109
|
+
// We might not be in debug mode but `DEBUG=lint-staged*` could have
|
|
110
|
+
// been set.
|
|
111
|
+
debugLog('lint-staged config:\n%O', config)
|
|
112
|
+
}
|
|
158
113
|
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
} catch (lintStagedError) {
|
|
163
|
-
/** throw early because `validateOptions` options contains own logging */
|
|
164
|
-
if (lintStagedError === InvalidOptionsError) {
|
|
165
|
-
throw InvalidOptionsError
|
|
166
|
-
}
|
|
114
|
+
// Unset GIT_LITERAL_PATHSPECS to not mess with path interpretation
|
|
115
|
+
debugLog('Unset GIT_LITERAL_PATHSPECS (was `%s`)', process.env.GIT_LITERAL_PATHSPECS)
|
|
116
|
+
delete process.env.GIT_LITERAL_PATHSPECS
|
|
167
117
|
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
118
|
+
try {
|
|
119
|
+
const ctx = await runAll(
|
|
120
|
+
{
|
|
121
|
+
allowEmpty,
|
|
122
|
+
concurrent,
|
|
123
|
+
config,
|
|
124
|
+
cwd,
|
|
125
|
+
debug,
|
|
126
|
+
maxArgLength,
|
|
127
|
+
quiet,
|
|
128
|
+
relative,
|
|
129
|
+
shell,
|
|
130
|
+
stash,
|
|
131
|
+
verbose,
|
|
132
|
+
},
|
|
133
|
+
logger
|
|
134
|
+
)
|
|
135
|
+
debugLog('Tasks were executed successfully!')
|
|
136
|
+
printTaskOutput(ctx, logger)
|
|
137
|
+
return true
|
|
138
|
+
} catch (runAllError) {
|
|
139
|
+
if (runAllError && runAllError.ctx && runAllError.ctx.errors) {
|
|
140
|
+
const { ctx } = runAllError
|
|
141
|
+
if (ctx.errors.has(ApplyEmptyCommitError)) {
|
|
142
|
+
logger.warn(PREVENTED_EMPTY_COMMIT)
|
|
143
|
+
} else if (ctx.errors.has(GitError) && !ctx.errors.has(GetBackupStashError)) {
|
|
144
|
+
logger.error(GIT_ERROR)
|
|
145
|
+
if (ctx.shouldBackup) {
|
|
146
|
+
// No sense to show this if the backup stash itself is missing.
|
|
147
|
+
logger.error(RESTORE_STASH_EXAMPLE)
|
|
148
|
+
}
|
|
149
|
+
}
|
|
175
150
|
|
|
176
|
-
|
|
177
|
-
|
|
151
|
+
printTaskOutput(ctx, logger)
|
|
152
|
+
return false
|
|
178
153
|
}
|
|
179
|
-
|
|
180
|
-
//
|
|
181
|
-
|
|
182
|
-
Please make sure you have created it correctly.
|
|
183
|
-
See https://github.com/okonet/lint-staged#configuration.
|
|
184
|
-
`)
|
|
185
|
-
|
|
186
|
-
throw lintStagedError
|
|
154
|
+
|
|
155
|
+
// Probably a compilation error in the config js file. Pass it up to the outer error handler for logging.
|
|
156
|
+
throw runAllError
|
|
187
157
|
}
|
|
188
158
|
}
|
|
189
159
|
|
package/lib/makeCmdTasks.js
CHANGED
|
@@ -3,8 +3,8 @@
|
|
|
3
3
|
const cliTruncate = require('cli-truncate')
|
|
4
4
|
const debug = require('debug')('lint-staged:make-cmd-tasks')
|
|
5
5
|
|
|
6
|
+
const { configurationError } = require('./messages')
|
|
6
7
|
const resolveTaskFn = require('./resolveTaskFn')
|
|
7
|
-
const { createError } = require('./validateConfig')
|
|
8
8
|
|
|
9
9
|
const STDOUT_COLUMNS_DEFAULT = 80
|
|
10
10
|
|
|
@@ -51,7 +51,7 @@ const makeCmdTasks = async ({ commands, files, gitDir, renderer, shell, verbose
|
|
|
51
51
|
// Do the validation here instead of `validateConfig` to skip evaluating the function multiple times
|
|
52
52
|
if (isFn && typeof command !== 'string') {
|
|
53
53
|
throw new Error(
|
|
54
|
-
|
|
54
|
+
configurationError(
|
|
55
55
|
'[Function]',
|
|
56
56
|
'Function task should return a string or an array of strings',
|
|
57
57
|
resolved
|
package/lib/messages.js
CHANGED
|
@@ -2,11 +2,26 @@
|
|
|
2
2
|
|
|
3
3
|
const chalk = require('chalk')
|
|
4
4
|
const { error, info, warning } = require('log-symbols')
|
|
5
|
+
const format = require('stringify-object')
|
|
6
|
+
|
|
7
|
+
const configurationError = (opt, helpMsg, value) =>
|
|
8
|
+
`${chalk.redBright(`${error} Validation Error:`)}
|
|
9
|
+
|
|
10
|
+
Invalid value for '${chalk.bold(opt)}': ${chalk.bold(
|
|
11
|
+
format(value, { inlineCharacterLimit: Number.POSITIVE_INFINITY })
|
|
12
|
+
)}
|
|
13
|
+
|
|
14
|
+
${helpMsg}`
|
|
5
15
|
|
|
6
16
|
const NOT_GIT_REPO = chalk.redBright(`${error} Current directory is not a git directory!`)
|
|
7
17
|
|
|
8
18
|
const FAILED_GET_STAGED_FILES = chalk.redBright(`${error} Failed to get staged files!`)
|
|
9
19
|
|
|
20
|
+
const incorrectBraces = (before, after) => `${warning} ${chalk.yellow(
|
|
21
|
+
`Detected incorrect braces with only single value: \`${before}\`. Reformatted as: \`${after}\``
|
|
22
|
+
)}
|
|
23
|
+
`
|
|
24
|
+
|
|
10
25
|
const NO_STAGED_FILES = `${info} No staged files found.`
|
|
11
26
|
|
|
12
27
|
const NO_TASKS = `${info} No staged files match any configured task.`
|
|
@@ -29,7 +44,7 @@ const GIT_ERROR = `\n ${error} ${chalk.red(`lint-staged failed due to a git err
|
|
|
29
44
|
|
|
30
45
|
const invalidOption = (name, value, message) => `${chalk.redBright(`${error} Validation Error:`)}
|
|
31
46
|
|
|
32
|
-
Invalid value for option ${chalk.bold(name)}: ${chalk.bold(value)}
|
|
47
|
+
Invalid value for option '${chalk.bold(name)}': ${chalk.bold(value)}
|
|
33
48
|
|
|
34
49
|
${message}
|
|
35
50
|
|
|
@@ -51,9 +66,11 @@ const CONFIG_STDIN_ERROR = 'Error: Could not read config from stdin.'
|
|
|
51
66
|
|
|
52
67
|
module.exports = {
|
|
53
68
|
CONFIG_STDIN_ERROR,
|
|
69
|
+
configurationError,
|
|
54
70
|
DEPRECATED_GIT_ADD,
|
|
55
71
|
FAILED_GET_STAGED_FILES,
|
|
56
72
|
GIT_ERROR,
|
|
73
|
+
incorrectBraces,
|
|
57
74
|
invalidOption,
|
|
58
75
|
NO_STAGED_FILES,
|
|
59
76
|
NO_TASKS,
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
const { incorrectBraces } = require('./messages')
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* A correctly-formed brace expansion must contain unquoted opening and closing braces,
|
|
5
|
+
* and at least one unquoted comma or a valid sequence expression.
|
|
6
|
+
* Any incorrectly formed brace expansion is left unchanged.
|
|
7
|
+
*
|
|
8
|
+
* @see https://www.gnu.org/software/bash/manual/html_node/Brace-Expansion.html
|
|
9
|
+
*
|
|
10
|
+
* Lint-staged uses `micromatch` for brace expansion, and its behavior is to treat
|
|
11
|
+
* invalid brace expansions as literal strings, which means they (typically) do not match
|
|
12
|
+
* anything.
|
|
13
|
+
*
|
|
14
|
+
* This RegExp tries to match most cases of invalid brace expansions, so that they can be
|
|
15
|
+
* detected, warned about, and re-formatted by removing the braces and thus hopefully
|
|
16
|
+
* matching the files as intended by the user. The only real fix is to remove the incorrect
|
|
17
|
+
* braces from user configuration, but this is left to the user (after seeing the warning).
|
|
18
|
+
*
|
|
19
|
+
* @example <caption>Globs with brace expansions</caption>
|
|
20
|
+
* - *.{js,tx} // expanded as *.js, *.ts
|
|
21
|
+
* - *.{{j,t}s,css} // expanded as *.js, *.ts, *.css
|
|
22
|
+
* - file_{1..10}.css // expanded as file_1.css, file_2.css, …, file_10.css
|
|
23
|
+
*
|
|
24
|
+
* @example <caption>Globs with incorrect brace expansions</caption>
|
|
25
|
+
* - *.{js} // should just be *.js
|
|
26
|
+
* - *.{js,{ts}} // should just be *.{js,ts}
|
|
27
|
+
* - *.\{js\} // escaped braces, so they're treated literally
|
|
28
|
+
* - *.${js} // dollar-sign inhibits expansion, so treated literally
|
|
29
|
+
* - *.{js\,ts} // the comma is escaped, so treated literally
|
|
30
|
+
*/
|
|
31
|
+
const BRACES_REGEXP = /(?<![\\$])({)(?:(?!(?<!\\),|\.\.|\{|\}).)*?(?<!\\)(})/g
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* @param {string} pattern
|
|
35
|
+
* @returns {string}
|
|
36
|
+
*/
|
|
37
|
+
const withoutIncorrectBraces = (pattern) => {
|
|
38
|
+
let output = `${pattern}`
|
|
39
|
+
let match = null
|
|
40
|
+
|
|
41
|
+
while ((match = BRACES_REGEXP.exec(pattern))) {
|
|
42
|
+
const fullMatch = match[0]
|
|
43
|
+
const withoutBraces = fullMatch.replace(/{/, '').replace(/}/, '')
|
|
44
|
+
output = output.replace(fullMatch, withoutBraces)
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
return output
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Validate and remove incorrect brace expansions from glob pattern.
|
|
52
|
+
* For example `*.{js}` is incorrect because it doesn't contain a `,` or `..`,
|
|
53
|
+
* and will be reformatted as `*.js`.
|
|
54
|
+
*
|
|
55
|
+
* @param {string} pattern the glob pattern
|
|
56
|
+
* @param {*} logger
|
|
57
|
+
* @returns {string}
|
|
58
|
+
*/
|
|
59
|
+
const validateBraces = (pattern, logger) => {
|
|
60
|
+
const fixedPattern = withoutIncorrectBraces(pattern)
|
|
61
|
+
|
|
62
|
+
if (fixedPattern !== pattern) {
|
|
63
|
+
logger.warn(incorrectBraces(pattern, fixedPattern))
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
return fixedPattern
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
module.exports = validateBraces
|
|
70
|
+
|
|
71
|
+
module.exports.BRACES_REGEXP = BRACES_REGEXP
|
package/lib/validateConfig.js
CHANGED
|
@@ -2,11 +2,11 @@
|
|
|
2
2
|
|
|
3
3
|
'use strict'
|
|
4
4
|
|
|
5
|
-
const chalk = require('chalk')
|
|
6
|
-
const format = require('stringify-object')
|
|
7
|
-
|
|
8
5
|
const debug = require('debug')('lint-staged:cfg')
|
|
9
6
|
|
|
7
|
+
const { configurationError } = require('./messages')
|
|
8
|
+
const validateBraces = require('./validateBraces')
|
|
9
|
+
|
|
10
10
|
const TEST_DEPRECATED_KEYS = new Map([
|
|
11
11
|
['concurrent', (key) => typeof key === 'boolean'],
|
|
12
12
|
['chunkSize', (key) => typeof key === 'number'],
|
|
@@ -18,76 +18,91 @@ const TEST_DEPRECATED_KEYS = new Map([
|
|
|
18
18
|
['relative', (key) => typeof key === 'boolean'],
|
|
19
19
|
])
|
|
20
20
|
|
|
21
|
-
const formatError = (helpMsg) => `● Validation Error:
|
|
22
|
-
|
|
23
|
-
${helpMsg}
|
|
24
|
-
|
|
25
|
-
Please refer to https://github.com/okonet/lint-staged#configuration for more information...`
|
|
26
|
-
|
|
27
|
-
const createError = (opt, helpMsg, value) =>
|
|
28
|
-
formatError(`Invalid value for '${chalk.bold(opt)}'.
|
|
29
|
-
|
|
30
|
-
${helpMsg}.
|
|
31
|
-
|
|
32
|
-
Configured value is: ${chalk.bold(
|
|
33
|
-
format(value, { inlineCharacterLimit: Number.POSITIVE_INFINITY })
|
|
34
|
-
)}`)
|
|
35
|
-
|
|
36
21
|
/**
|
|
37
22
|
* Runs config validation. Throws error if the config is not valid.
|
|
38
23
|
* @param config {Object}
|
|
39
24
|
* @returns config {Object}
|
|
40
25
|
*/
|
|
41
|
-
|
|
26
|
+
const validateConfig = (config, logger) => {
|
|
42
27
|
debug('Validating config')
|
|
43
28
|
|
|
44
|
-
|
|
29
|
+
if (!config || (typeof config !== 'object' && typeof config !== 'function')) {
|
|
30
|
+
throw new Error('Configuration should be an object or a function!')
|
|
31
|
+
}
|
|
45
32
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
33
|
+
/**
|
|
34
|
+
* Function configurations receive all staged files as their argument.
|
|
35
|
+
* They are not further validated here to make sure the function gets
|
|
36
|
+
* evaluated only once.
|
|
37
|
+
*
|
|
38
|
+
* @see makeCmdTasks
|
|
39
|
+
*/
|
|
40
|
+
if (typeof config === 'function') {
|
|
41
|
+
return { '*': config }
|
|
42
|
+
}
|
|
50
43
|
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
44
|
+
if (Object.entries(config).length === 0) {
|
|
45
|
+
throw new Error('Configuration should not be empty!')
|
|
46
|
+
}
|
|
54
47
|
|
|
55
|
-
|
|
56
|
-
if (TEST_DEPRECATED_KEYS.has(pattern)) {
|
|
57
|
-
const testFn = TEST_DEPRECATED_KEYS.get(pattern)
|
|
58
|
-
if (testFn(task)) {
|
|
59
|
-
errors.push(
|
|
60
|
-
createError(
|
|
61
|
-
pattern,
|
|
62
|
-
'Advanced configuration has been deprecated. For more info, please visit: https://github.com/okonet/lint-staged',
|
|
63
|
-
task
|
|
64
|
-
)
|
|
65
|
-
)
|
|
66
|
-
}
|
|
67
|
-
}
|
|
48
|
+
const errors = []
|
|
68
49
|
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
50
|
+
/**
|
|
51
|
+
* Create a new validated config because the keys (patterns) might change.
|
|
52
|
+
* Since the Object.reduce method already loops through each entry in the config,
|
|
53
|
+
* it can be used for validating the values at the same time.
|
|
54
|
+
*/
|
|
55
|
+
const validatedConfig = Object.entries(config).reduce((collection, [pattern, task]) => {
|
|
56
|
+
/** Versions < 9 had more complex configuration options that are no longer supported. */
|
|
57
|
+
if (TEST_DEPRECATED_KEYS.has(pattern)) {
|
|
58
|
+
const testFn = TEST_DEPRECATED_KEYS.get(pattern)
|
|
59
|
+
if (testFn(task)) {
|
|
75
60
|
errors.push(
|
|
76
|
-
|
|
77
|
-
pattern,
|
|
78
|
-
'Should be a string, a function, or an array of strings and functions',
|
|
79
|
-
task
|
|
80
|
-
)
|
|
61
|
+
configurationError(pattern, 'Advanced configuration has been deprecated.', task)
|
|
81
62
|
)
|
|
82
63
|
}
|
|
83
|
-
|
|
84
|
-
|
|
64
|
+
|
|
65
|
+
/** Return early for deprecated keys to skip validating their (deprecated) values */
|
|
66
|
+
return collection
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
if (
|
|
70
|
+
(!Array.isArray(task) ||
|
|
71
|
+
task.some((item) => typeof item !== 'string' && typeof item !== 'function')) &&
|
|
72
|
+
typeof task !== 'string' &&
|
|
73
|
+
typeof task !== 'function'
|
|
74
|
+
) {
|
|
75
|
+
errors.push(
|
|
76
|
+
configurationError(
|
|
77
|
+
pattern,
|
|
78
|
+
'Should be a string, a function, or an array of strings and functions.',
|
|
79
|
+
task
|
|
80
|
+
)
|
|
81
|
+
)
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* A typical configuration error is using invalid brace expansion, like `*.{js}`.
|
|
86
|
+
* These are automatically fixed and warned about.
|
|
87
|
+
*/
|
|
88
|
+
const fixedPattern = validateBraces(pattern, logger)
|
|
89
|
+
|
|
90
|
+
return { ...collection, [fixedPattern]: task }
|
|
91
|
+
}, {})
|
|
85
92
|
|
|
86
93
|
if (errors.length) {
|
|
87
|
-
|
|
94
|
+
const message = errors.join('\n\n')
|
|
95
|
+
|
|
96
|
+
logger.error(`Could not parse lint-staged config.
|
|
97
|
+
|
|
98
|
+
${message}
|
|
99
|
+
|
|
100
|
+
See https://github.com/okonet/lint-staged#configuration.`)
|
|
101
|
+
|
|
102
|
+
throw new Error(message)
|
|
88
103
|
}
|
|
89
104
|
|
|
90
|
-
return
|
|
105
|
+
return validatedConfig
|
|
91
106
|
}
|
|
92
107
|
|
|
93
|
-
module.exports
|
|
108
|
+
module.exports = validateConfig
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "lint-staged",
|
|
3
|
-
"version": "11.1.
|
|
3
|
+
"version": "11.1.2",
|
|
4
4
|
"description": "Lint files staged by git",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": "https://github.com/okonet/lint-staged",
|
|
@@ -32,7 +32,6 @@
|
|
|
32
32
|
"commander": "^7.2.0",
|
|
33
33
|
"cosmiconfig": "^7.0.0",
|
|
34
34
|
"debug": "^4.3.1",
|
|
35
|
-
"dedent": "^0.7.0",
|
|
36
35
|
"enquirer": "^2.3.6",
|
|
37
36
|
"execa": "^5.0.0",
|
|
38
37
|
"listr2": "^3.8.2",
|