lint-staged 9.2.5 β 9.4.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 +39 -1
- package/package.json +1 -1
- package/src/index.js +3 -2
- package/src/makeCmdTasks.js +24 -11
- package/src/resolveTaskFn.js +12 -19
- package/src/runAll.js +1 -1
package/README.md
CHANGED
|
@@ -2,6 +2,30 @@
|
|
|
2
2
|
|
|
3
3
|
Run linters against staged git files and don't let :poop: slip into your code base!
|
|
4
4
|
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## π§ Help test `lint-staged@next`!
|
|
8
|
+
|
|
9
|
+
Version 10 of `lint-staged` is coming with changes that help it run faster on large git repositories and prevent loss of data during errors. Please help test the `next` version and report any inconsistencies in our [GitHub Issues](https://github.com/okonet/lint-staged/issues):
|
|
10
|
+
|
|
11
|
+
**Using npm**
|
|
12
|
+
|
|
13
|
+
npm install --save-dev lint-staged@next
|
|
14
|
+
|
|
15
|
+
**Using yarn**
|
|
16
|
+
|
|
17
|
+
yarn add -D lint-staged@next
|
|
18
|
+
|
|
19
|
+
### Notable changes
|
|
20
|
+
|
|
21
|
+
- A git stash is created before running any tasks, so in case of errors any lost changes can be restored easily (and automatically unless lint-staged itself crashes)
|
|
22
|
+
- Instead of write-tree/read-tree, `lint-staged@next` uses git stashes to hide unstaged changes while running tasks against staged files
|
|
23
|
+
- This results in a performance increase of up to 45x on very large repositories
|
|
24
|
+
- The behaviour of committing modifications during tasks (eg. `prettier --write && git add`) is different. The current version creates a diff of these modifications, and applies it against the original state, silently ignoring any errors. The `next` version leaves modifications of staged files as-is, and then restores all hidden unstaged changes as patch. If applying the patch fails due to a merge conflict (because tasks have modified the same lines), a 3-way merge will be retried. If this also fails, the entire commit will fail and the original state will be restored.
|
|
25
|
+
- **TL;DR** the `next` version will never skip committing any changes by tasks (due to a merge conflict), but might fail in very complex situations where unstaged changes cannot be restored cleanly. If this happens to you, we are very interested in a repeatable test scenario.
|
|
26
|
+
|
|
27
|
+
---
|
|
28
|
+
|
|
5
29
|
[](https://asciinema.org/a/199934)
|
|
6
30
|
|
|
7
31
|
## Why
|
|
@@ -109,7 +133,7 @@ Linter commands work on a subset of all staged files, defined by a _glob pattern
|
|
|
109
133
|
* **`"!(*test).js"`**. will match all JS files, except those ending in `test.js`, so `foo.js` but not `foo.test.js`
|
|
110
134
|
* If the glob pattern does contain a slash (`/`), it will match for paths as well:
|
|
111
135
|
* **`"/*.js"`** will match all JS files in the git repo root, so `/test.js` but not `/foo/bar/test.js`
|
|
112
|
-
* **`"foo
|
|
136
|
+
* **`"foo/**/*.js"`** will match all JS files inside the`/foo`directory, so`/foo/bar/test.js`but not`/test.js`
|
|
113
137
|
|
|
114
138
|
When matching, `lint-staged` will do the following
|
|
115
139
|
|
|
@@ -400,6 +424,20 @@ const success = await lintStaged({
|
|
|
400
424
|
})
|
|
401
425
|
```
|
|
402
426
|
|
|
427
|
+
You can also pass config directly with `config` option:
|
|
428
|
+
|
|
429
|
+
|
|
430
|
+
```js
|
|
431
|
+
const success = await lintStaged({
|
|
432
|
+
config: {
|
|
433
|
+
'*.js': 'eslint --fix'
|
|
434
|
+
},
|
|
435
|
+
shell: false,
|
|
436
|
+
quiet: false,
|
|
437
|
+
debug: false
|
|
438
|
+
})
|
|
439
|
+
```
|
|
440
|
+
|
|
403
441
|
### Using with JetBrains IDEs _(WebStorm, PyCharm, IntelliJ IDEA, RubyMine, etc.)_
|
|
404
442
|
|
|
405
443
|
_**Update**_: The latest version of JetBrains IDEs now support running hooks as you would expect.
|
package/package.json
CHANGED
package/src/index.js
CHANGED
|
@@ -43,6 +43,7 @@ function loadConfig(configPath) {
|
|
|
43
43
|
*
|
|
44
44
|
* @param {object} options
|
|
45
45
|
* @param {string} [options.configPath] - Path to configuration file
|
|
46
|
+
* @param {object} [options.config] - Object with configuration for programmatic API
|
|
46
47
|
* @param {boolean} [options.relative] - Pass relative filepaths to tasks
|
|
47
48
|
* @param {boolean} [options.shell] - Skip parsing of tasks for better shell support
|
|
48
49
|
* @param {boolean} [options.quiet] - Disable lint-stagedβs own console output
|
|
@@ -52,12 +53,12 @@ function loadConfig(configPath) {
|
|
|
52
53
|
* @returns {Promise<boolean>} Promise of whether the linting passed or failed
|
|
53
54
|
*/
|
|
54
55
|
module.exports = function lintStaged(
|
|
55
|
-
{ configPath, relative = false, shell = false, quiet = false, debug = false } = {},
|
|
56
|
+
{ configPath, config, relative = false, shell = false, quiet = false, debug = false } = {},
|
|
56
57
|
logger = console
|
|
57
58
|
) {
|
|
58
59
|
debugLog('Loading config using `cosmiconfig`')
|
|
59
60
|
|
|
60
|
-
return loadConfig(configPath)
|
|
61
|
+
return (config ? Promise.resolve({ config, filepath: '(input)' }) : loadConfig(configPath))
|
|
61
62
|
.then(result => {
|
|
62
63
|
if (result == null) throw errConfigNotFound
|
|
63
64
|
|
package/src/makeCmdTasks.js
CHANGED
|
@@ -8,27 +8,40 @@ const debug = require('debug')('lint-staged:make-cmd-tasks')
|
|
|
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 {Array<string|Function>|string|Function}
|
|
12
|
-
* @param {string}
|
|
13
|
-
* @param {
|
|
11
|
+
* @param {Array<string|Function>|string|Function} options.commands
|
|
12
|
+
* @param {Array<string>} options.files
|
|
13
|
+
* @param {string} options.gitDir
|
|
14
14
|
* @param {Boolean} shell
|
|
15
15
|
*/
|
|
16
|
-
module.exports = async function makeCmdTasks({ commands,
|
|
16
|
+
module.exports = async function makeCmdTasks({ commands, files, gitDir, shell }) {
|
|
17
17
|
debug('Creating listr tasks for commands %o', commands)
|
|
18
18
|
const commandsArray = Array.isArray(commands) ? commands : [commands]
|
|
19
19
|
|
|
20
20
|
return commandsArray.reduce((tasks, command) => {
|
|
21
|
-
//
|
|
21
|
+
// command function may return array of commands that already include `stagedFiles`
|
|
22
22
|
const isFn = typeof command === 'function'
|
|
23
|
-
const resolved = isFn ? command(
|
|
24
|
-
const
|
|
23
|
+
const resolved = isFn ? command(files) : command
|
|
24
|
+
const commands = Array.isArray(resolved) ? resolved : [resolved] // Wrap non-array command as array
|
|
25
25
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
26
|
+
// Function command should not be used as the task title as-is
|
|
27
|
+
// because the resolved string it might be very long
|
|
28
|
+
// Create a matching command array with [file] in place of file names
|
|
29
|
+
let mockCommands
|
|
30
|
+
if (isFn) {
|
|
31
|
+
const mockFileList = Array(files.length).fill('[file]')
|
|
32
|
+
const resolved = command(mockFileList)
|
|
33
|
+
mockCommands = Array.isArray(resolved) ? resolved : [resolved]
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
commands.forEach((command, i) => {
|
|
37
|
+
let title = isFn ? '[Function]' : command
|
|
38
|
+
if (isFn && mockCommands[i]) {
|
|
39
|
+
// If command is a function, use the matching mock command as title,
|
|
40
|
+
// but since might include multiple [file] arguments, shorten to one
|
|
41
|
+
title = mockCommands[i].replace(/\[file\].*\[file\]/, '[file]')
|
|
30
42
|
}
|
|
31
43
|
|
|
44
|
+
const task = { title, task: resolveTaskFn({ gitDir, isFn, command, files, shell }) }
|
|
32
45
|
tasks.push(task)
|
|
33
46
|
})
|
|
34
47
|
|
package/src/resolveTaskFn.js
CHANGED
|
@@ -77,27 +77,20 @@ function makeErr(linter, result, context = {}) {
|
|
|
77
77
|
* if the OS is Windows.
|
|
78
78
|
*
|
|
79
79
|
* @param {Object} options
|
|
80
|
-
* @param {
|
|
81
|
-
* @param {
|
|
82
|
-
* @param {
|
|
83
|
-
* @param {Array<string>}
|
|
80
|
+
* @param {string} options.command β Linter task
|
|
81
|
+
* @param {String} options.gitDir - Current git repo path
|
|
82
|
+
* @param {Boolean} options.isFn - Whether the linter task is a function
|
|
83
|
+
* @param {Array<string>} options.pathsToLint β Filepaths to run the linter task against
|
|
84
84
|
* @param {Boolean} [options.relative] β Whether the filepaths should be relative
|
|
85
85
|
* @param {Boolean} [options.shell] β Whether to skip parsing linter task for better shell support
|
|
86
86
|
* @returns {function(): Promise<Array<string>>}
|
|
87
87
|
*/
|
|
88
|
-
module.exports = function resolveTaskFn({
|
|
89
|
-
gitDir,
|
|
90
|
-
isFn,
|
|
91
|
-
linter,
|
|
92
|
-
pathsToLint,
|
|
93
|
-
relative,
|
|
94
|
-
shell = false
|
|
95
|
-
}) {
|
|
88
|
+
module.exports = function resolveTaskFn({ command, files, gitDir, isFn, relative, shell = false }) {
|
|
96
89
|
const execaOptions = { preferLocal: true, reject: false, shell }
|
|
97
90
|
|
|
98
91
|
if (relative) {
|
|
99
92
|
execaOptions.cwd = process.cwd()
|
|
100
|
-
} else if (/^git(\.exe)?/i.test(
|
|
93
|
+
} else if (/^git(\.exe)?/i.test(command) && gitDir !== process.cwd()) {
|
|
101
94
|
// Only use gitDir as CWD if we are using the git binary
|
|
102
95
|
// e.g `npm` should run tasks in the actual CWD
|
|
103
96
|
execaOptions.cwd = gitDir
|
|
@@ -109,20 +102,20 @@ module.exports = function resolveTaskFn({
|
|
|
109
102
|
if (shell) {
|
|
110
103
|
execaOptions.shell = true
|
|
111
104
|
// If `shell`, passed command shouldn't be parsed
|
|
112
|
-
// If `linter` is a function, command already includes `
|
|
113
|
-
cmd = isFn ?
|
|
105
|
+
// If `linter` is a function, command already includes `files`.
|
|
106
|
+
cmd = isFn ? command : `${command} ${files.join(' ')}`
|
|
114
107
|
} else {
|
|
115
|
-
const [parsedCmd, ...parsedArgs] = stringArgv.parseArgsStringToArgv(
|
|
108
|
+
const [parsedCmd, ...parsedArgs] = stringArgv.parseArgsStringToArgv(command)
|
|
116
109
|
cmd = parsedCmd
|
|
117
|
-
args = isFn ? parsedArgs : parsedArgs.concat(
|
|
110
|
+
args = isFn ? parsedArgs : parsedArgs.concat(files)
|
|
118
111
|
}
|
|
119
112
|
|
|
120
113
|
return ctx =>
|
|
121
114
|
execLinter(cmd, args, execaOptions).then(result => {
|
|
122
115
|
if (result.failed || result.killed || result.signal != null) {
|
|
123
|
-
throw makeErr(
|
|
116
|
+
throw makeErr(command, result, ctx)
|
|
124
117
|
}
|
|
125
118
|
|
|
126
|
-
return successMsg(
|
|
119
|
+
return successMsg(command)
|
|
127
120
|
})
|
|
128
121
|
}
|
package/src/runAll.js
CHANGED
|
@@ -73,7 +73,7 @@ https://github.com/okonet/lint-staged#using-js-functions-to-customize-linter-com
|
|
|
73
73
|
title: `Running tasks for ${task.pattern}`,
|
|
74
74
|
task: async () =>
|
|
75
75
|
new Listr(
|
|
76
|
-
await makeCmdTasks({ commands: task.commands,
|
|
76
|
+
await makeCmdTasks({ commands: task.commands, files: task.fileList, gitDir, shell }),
|
|
77
77
|
{
|
|
78
78
|
// In sub-tasks we don't want to run concurrently
|
|
79
79
|
// and we want to abort on errors
|