lint-staged 16.4.0 → 17.0.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/MIGRATION.md +20 -0
- package/README.md +35 -31
- package/bin/lint-staged.js +16 -135
- package/lib/assertGitVersion.js +48 -0
- package/lib/cli.js +242 -0
- package/lib/colors.js +8 -103
- package/lib/debug.js +3 -5
- package/lib/getRenderer.js +1 -1
- package/lib/getSpawnedTask.js +2 -2
- package/lib/gitWorkflow.js +55 -68
- package/lib/index.d.ts +5 -0
- package/lib/index.js +9 -1
- package/lib/messages.js +8 -2
- package/lib/runAll.js +23 -25
- package/lib/state.js +33 -17
- package/package.json +19 -19
package/MIGRATION.md
CHANGED
|
@@ -1,3 +1,23 @@
|
|
|
1
|
+
## v17
|
|
2
|
+
|
|
3
|
+
#### Node.js v20 is no longer supported.
|
|
4
|
+
|
|
5
|
+
The oldest supported Node.js version is now 22.22.1, which is the latest active v22 LTS version at the time of release. You can — of course — update to v24 or v25, or later.
|
|
6
|
+
|
|
7
|
+
#### The `yaml` dependency is now optional
|
|
8
|
+
|
|
9
|
+
The dependency `yaml` is now marked as optional and probably won't be installed by default. If you're using a YAML configuration file you should install the package separately:
|
|
10
|
+
|
|
11
|
+
```shell
|
|
12
|
+
npm install --development yaml
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
If you're using `.lintstagedrc` as the config file name (without a file extension), it will be treated as a YAML file. If the content is JSON, consider renaming it to `.lintstagedrc.json` to avoid needing to install `yaml`.
|
|
16
|
+
|
|
17
|
+
#### Git version needs to be at least 2.32.0
|
|
18
|
+
|
|
19
|
+
_Lint-staged_ now tries to verify the installed Git version is at least `2.32.0`, released in 2021. If you're using an even older Git version, you need to [upgrade](https://git-scm.com/install/mac) it before running _lint-staged_!
|
|
20
|
+
|
|
1
21
|
## v16
|
|
2
22
|
|
|
3
23
|
#### Updated Node.js version requirement
|
package/README.md
CHANGED
|
@@ -23,7 +23,7 @@ $ git commit
|
|
|
23
23
|
❯ *.js — 2 files
|
|
24
24
|
⠼ eslint --fix
|
|
25
25
|
↓ *.{json,md} — no files [SKIPPED]
|
|
26
|
-
◼
|
|
26
|
+
◼ Updating Git index again...
|
|
27
27
|
◼ Cleaning up temporary files...
|
|
28
28
|
```
|
|
29
29
|
|
|
@@ -104,34 +104,32 @@ For breaking changes, see [MIGRATION.md](./MIGRATION.md).
|
|
|
104
104
|
❯ npx lint-staged --help
|
|
105
105
|
Usage: lint-staged [options]
|
|
106
106
|
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
(default: false)
|
|
128
|
-
-h, --help display help for command
|
|
107
|
+
-h, --help display this help message
|
|
108
|
+
-V, --version display the current version number
|
|
109
|
+
--allow-empty allow empty commits when tasks revert all staged changes (default: false)
|
|
110
|
+
-p, --concurrent <number|boolean> the number of tasks to run concurrently, or false for serial (default: true)
|
|
111
|
+
-c, --config [path] path to configuration file, or - to read from stdin
|
|
112
|
+
--continue-on-error run all tasks to completion even if one fails (default: false)
|
|
113
|
+
--cwd [path] run all tasks in specific directory, instead of the current
|
|
114
|
+
-d, --debug print additional debug information (default: false)
|
|
115
|
+
--diff [string] override the default "--staged" flag of "git diff" to get list of files. Implies "--no-stash".
|
|
116
|
+
--diff-filter [string] override the default "--diff-filter=ACMR" flag of "git diff" to get list of files
|
|
117
|
+
--fail-on-changes fail with exit code 1 when tasks modify tracked files (default: false)
|
|
118
|
+
--no-hide-partially-staged hide unstaged changes from partially staged files (default: true)
|
|
119
|
+
--hide-unstaged hide all unstaged changes, instead of just partially staged (default: false)
|
|
120
|
+
--hide-all hide all unstaged changes and untracked files (default: false)
|
|
121
|
+
--max-arg-length [number] maximum length of the command-line argument string (default: 0)
|
|
122
|
+
-q, --quiet disable lint-staged's own console output (default: false)
|
|
123
|
+
-r, --relative pass relative filepaths to tasks (default: false)
|
|
124
|
+
--no-revert revert to original state in case of errors (default: true)
|
|
125
|
+
--no-stash enable the backup stash (default: true)
|
|
126
|
+
-v, --verbose show task output even when tasks succeed; by default only failed output is shown (default: false)
|
|
129
127
|
|
|
130
128
|
Any lost modifications can be restored from a git stash:
|
|
131
129
|
|
|
132
130
|
> git stash list --format="%h %s"
|
|
133
|
-
|
|
134
|
-
> git apply --index
|
|
131
|
+
<git-hash> On main: lint-staged automatic backup
|
|
132
|
+
> git apply --index <git-hash>
|
|
135
133
|
```
|
|
136
134
|
|
|
137
135
|
#### `--allow-empty`
|
|
@@ -152,7 +150,7 @@ Manually specify a path to a config file or npm package name. Note: when used, l
|
|
|
152
150
|
|
|
153
151
|
#### `--cwd [path]`
|
|
154
152
|
|
|
155
|
-
|
|
153
|
+
Change the working directory _lint-staged_ runs tasks in. Defaults to `process.cwd()` and the value can be absolute or relative to the default value.
|
|
156
154
|
|
|
157
155
|
#### `--debug`
|
|
158
156
|
|
|
@@ -192,7 +190,11 @@ By default, unstaged changes from partially staged files will be hidden and appl
|
|
|
192
190
|
|
|
193
191
|
#### `--hide-unstaged`
|
|
194
192
|
|
|
195
|
-
Use this option to hide all unstaged changes
|
|
193
|
+
Use this option to hide all unstaged changes in tracked files, instead of just those which are also partially staged, before running tasks. The changes will be applied back after running the tasks.
|
|
194
|
+
|
|
195
|
+
#### `--hide-all`
|
|
196
|
+
|
|
197
|
+
Add new option `--hide-all` for hiding all unstaged changes and untracked files, before running tasks. This makes it easier to run tools like [Knip](https://knip.dev) which check for unused code. Untracked files are included in the backup stash and restored automatically after running.
|
|
196
198
|
|
|
197
199
|
#### `--quiet`
|
|
198
200
|
|
|
@@ -229,7 +231,7 @@ _Lint-staged_ can be configured in many ways:
|
|
|
229
231
|
|
|
230
232
|
Configuration should be an object where each value is a **command** to run and its key is a glob pattern to use for this command. This package uses [picomatch](https://github.com/micromatch/picomatch) for glob patterns. JavaScript files can also export advanced configuration as a function. See [Using JS configuration files](#using-js-configuration-files) for more info.
|
|
231
233
|
|
|
232
|
-
You can also place multiple configuration files in different directories inside a project. For a given staged file, the closest configuration file will always be used. See ["How to use `lint-staged` in a multi-package monorepo?"](#how-to-use-lint-staged-in-a-multi-package-monorepo) for more info and an example.
|
|
234
|
+
You can also place multiple configuration files in different directories inside a project. For a given staged file, the closest configuration file will always be used and tasks will by default run in the directory of the config (for example, the directory of a specific package in a monorepo). See ["How to use `lint-staged` in a multi-package monorepo?"](#how-to-use-lint-staged-in-a-multi-package-monorepo) for more info and an example.
|
|
233
235
|
|
|
234
236
|
#### `package.json` example:
|
|
235
237
|
|
|
@@ -901,11 +903,11 @@ _Thanks to [this comment](https://youtrack.jetbrains.com/issue/IDEA-135454#comme
|
|
|
901
903
|
<details>
|
|
902
904
|
<summary>Click to expand</summary>
|
|
903
905
|
|
|
904
|
-
Install _lint-staged_ on the monorepo root level
|
|
906
|
+
Install _lint-staged_ on the monorepo root level and add separate configuration files in each package. _Lint-staged_ will find each config file and match staged files to the closest config. The directory of each config file will be used as the working directory for those tasks, unless `--cwd` option is used. This is almost the same as running multiple processes of _lint-staged_ in parallel for each config, but multiple parallel locking Git operations are avoided.
|
|
905
907
|
|
|
906
908
|
For example, in a monorepo with `packages/frontend/.lintstagedrc.json` and `packages/backend/.lintstagedrc.json`, a staged file inside `packages/frontend/` will only match that configuration, and not the one in `packages/backend/`.
|
|
907
909
|
|
|
908
|
-
**Note**: _lint-staged_
|
|
910
|
+
**Note**: _lint-staged_ does not merge config files, so if the closest config file to a staged file doesn't match it, the file will be ignored. For example:
|
|
909
911
|
|
|
910
912
|
```js
|
|
911
913
|
// ./.lintstagedrc.json
|
|
@@ -928,7 +930,9 @@ export default {
|
|
|
928
930
|
}
|
|
929
931
|
```
|
|
930
932
|
|
|
931
|
-
|
|
933
|
+
**Note**: If you want to run _lint-staged_ in only one package inside a monorepo, you can simply use the `--cwd` option (for example `lint-staged --cwd packages/frontend`).
|
|
934
|
+
|
|
935
|
+
**Note**: It is possible to run all tasks in the monorepo root by explicitly configuring `lint-staged --cwd="."`.
|
|
932
936
|
|
|
933
937
|
</details>
|
|
934
938
|
|
package/bin/lint-staged.js
CHANGED
|
@@ -2,152 +2,33 @@
|
|
|
2
2
|
|
|
3
3
|
import { userInfo } from 'node:os'
|
|
4
4
|
|
|
5
|
-
import {
|
|
6
|
-
|
|
5
|
+
import { getVersionNumber, parseCliOptions, printHelpText } from '../lib/cli.js'
|
|
7
6
|
import { createDebug, enableDebug } from '../lib/debug.js'
|
|
8
7
|
import lintStaged from '../lib/index.js'
|
|
9
|
-
import { CONFIG_STDIN_ERROR
|
|
8
|
+
import { CONFIG_STDIN_ERROR } from '../lib/messages.js'
|
|
10
9
|
import { readStdin } from '../lib/readStdin.js'
|
|
11
|
-
import { getVersion } from '../lib/version.js'
|
|
12
10
|
|
|
13
11
|
const debugLog = createDebug('lint-staged:bin')
|
|
14
12
|
|
|
15
13
|
// Do not terminate main Listr process on SIGINT
|
|
16
14
|
process.on('SIGINT', () => {})
|
|
17
15
|
|
|
18
|
-
|
|
19
|
-
.version(await getVersion())
|
|
20
|
-
|
|
21
|
-
/**
|
|
22
|
-
* This shouldn't be necessary for lint-staged, but add migration step just in case
|
|
23
|
-
* to preserve old behavior of "commander".
|
|
24
|
-
*
|
|
25
|
-
* @todo remove this in the major version
|
|
26
|
-
* @see https://github.com/tj/commander.js/releases/tag/v13.0.0
|
|
27
|
-
* */
|
|
28
|
-
.allowExcessArguments()
|
|
29
|
-
|
|
30
|
-
.addOption(
|
|
31
|
-
new Option('--allow-empty', 'allow empty commits when tasks revert all staged changes').default(
|
|
32
|
-
false
|
|
33
|
-
)
|
|
34
|
-
)
|
|
35
|
-
.addOption(
|
|
36
|
-
new Option(
|
|
37
|
-
'-p, --concurrent <number|boolean>',
|
|
38
|
-
'the number of tasks to run concurrently, or false for serial'
|
|
39
|
-
).default(true)
|
|
40
|
-
)
|
|
41
|
-
.addOption(
|
|
42
|
-
new Option('-c, --config [path]', 'path to configuration file, or - to read from stdin')
|
|
43
|
-
)
|
|
44
|
-
.addOption(
|
|
45
|
-
new Option('--cwd [path]', 'run all tasks in specific directory, instead of the current')
|
|
46
|
-
)
|
|
47
|
-
.addOption(new Option('-d, --debug', 'print additional debug information').default(false))
|
|
48
|
-
.addOption(
|
|
49
|
-
new Option(
|
|
50
|
-
'--diff [string]',
|
|
51
|
-
'override the default "--staged" flag of "git diff" to get list of files. Implies "--no-stash".'
|
|
52
|
-
).implies({ stash: false })
|
|
53
|
-
)
|
|
54
|
-
.addOption(
|
|
55
|
-
new Option(
|
|
56
|
-
'--diff-filter [string]',
|
|
57
|
-
'override the default "--diff-filter=ACMR" flag of "git diff" to get list of files'
|
|
58
|
-
)
|
|
59
|
-
)
|
|
60
|
-
.addOption(
|
|
61
|
-
new Option('--continue-on-error', 'run all tasks to completion even if one fails').default(
|
|
62
|
-
false
|
|
63
|
-
)
|
|
64
|
-
)
|
|
65
|
-
.addOption(
|
|
66
|
-
new Option('--fail-on-changes', 'fail with exit code 1 when tasks modify tracked files')
|
|
67
|
-
.default(false)
|
|
68
|
-
.implies({ revert: false })
|
|
69
|
-
)
|
|
70
|
-
.addOption(
|
|
71
|
-
new Option(
|
|
72
|
-
'--max-arg-length [number]',
|
|
73
|
-
'maximum length of the command-line argument string'
|
|
74
|
-
).default(0)
|
|
75
|
-
)
|
|
76
|
-
|
|
77
|
-
/**
|
|
78
|
-
* We don't want to show the `--revert` flag because it's on by default, and only show the
|
|
79
|
-
* negatable flag `--no-rever` instead. There seems to be a bug in Commander.js where
|
|
80
|
-
* configuring only the latter won't actually set the default value.
|
|
81
|
-
*/
|
|
82
|
-
.addOption(
|
|
83
|
-
new Option('--revert', 'revert to original state in case of errors').default(true).hideHelp()
|
|
84
|
-
)
|
|
85
|
-
.addOption(
|
|
86
|
-
new Option('--no-revert', 'do not revert to original state in case of errors.').default(false)
|
|
87
|
-
)
|
|
88
|
-
|
|
89
|
-
.addOption(new Option('--stash', 'enable the backup stash').default(true).hideHelp())
|
|
90
|
-
.addOption(
|
|
91
|
-
new Option('--no-stash', 'disable the backup stash. Implies "--no-revert".')
|
|
92
|
-
.default(false)
|
|
93
|
-
.implies({ revert: false })
|
|
94
|
-
)
|
|
95
|
-
|
|
96
|
-
.addOption(
|
|
97
|
-
new Option('--hide-partially-staged', 'hide unstaged changes from partially staged files')
|
|
98
|
-
.default(true)
|
|
99
|
-
.hideHelp()
|
|
100
|
-
)
|
|
101
|
-
.addOption(
|
|
102
|
-
new Option(
|
|
103
|
-
'--no-hide-partially-staged',
|
|
104
|
-
'disable hiding unstaged changes from partially staged files'
|
|
105
|
-
).default(false)
|
|
106
|
-
)
|
|
16
|
+
const cliOptions = parseCliOptions(process.argv)
|
|
107
17
|
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
)
|
|
113
|
-
|
|
114
|
-
.addOption(new Option('-q, --quiet', 'disable lint-staged’s own console output').default(false))
|
|
115
|
-
.addOption(new Option('-r, --relative', 'pass relative filepaths to tasks').default(false))
|
|
116
|
-
.addOption(
|
|
117
|
-
new Option(
|
|
118
|
-
'-v, --verbose',
|
|
119
|
-
'show task output even when tasks succeed; by default only failed output is shown'
|
|
120
|
-
).default(false)
|
|
121
|
-
)
|
|
122
|
-
|
|
123
|
-
.addHelpText('afterAll', '\n' + restoreStashExample())
|
|
18
|
+
if (cliOptions.version) {
|
|
19
|
+
console.log(await getVersionNumber())
|
|
20
|
+
process.exit(0)
|
|
21
|
+
}
|
|
124
22
|
|
|
125
|
-
|
|
23
|
+
if (cliOptions.help) {
|
|
24
|
+
console.log(await printHelpText())
|
|
25
|
+
process.exit(0)
|
|
26
|
+
}
|
|
126
27
|
|
|
127
28
|
if (cliOptions.debug) {
|
|
128
29
|
enableDebug()
|
|
129
30
|
}
|
|
130
31
|
|
|
131
|
-
const options = {
|
|
132
|
-
allowEmpty: !!cliOptions.allowEmpty,
|
|
133
|
-
concurrent: JSON.parse(cliOptions.concurrent),
|
|
134
|
-
configPath: cliOptions.config,
|
|
135
|
-
continueOnError: !!cliOptions.continueOnError,
|
|
136
|
-
cwd: cliOptions.cwd,
|
|
137
|
-
debug: !!cliOptions.debug,
|
|
138
|
-
diff: cliOptions.diff,
|
|
139
|
-
diffFilter: cliOptions.diffFilter,
|
|
140
|
-
failOnChanges: !!cliOptions.failOnChanges,
|
|
141
|
-
hidePartiallyStaged: !!cliOptions.hidePartiallyStaged, // commander inverts `no-<x>` flags to `!x`
|
|
142
|
-
hideUnstaged: !!cliOptions.hideUnstaged,
|
|
143
|
-
maxArgLength: cliOptions.maxArgLength || undefined,
|
|
144
|
-
quiet: !!cliOptions.quiet,
|
|
145
|
-
relative: !!cliOptions.relative,
|
|
146
|
-
revert: !!cliOptions.revert, // commander inverts `no-<x>` flags to `!x`
|
|
147
|
-
stash: !!cliOptions.stash, // commander inverts `no-<x>` flags to `!x`
|
|
148
|
-
verbose: !!cliOptions.verbose,
|
|
149
|
-
}
|
|
150
|
-
|
|
151
32
|
try {
|
|
152
33
|
const { shell } = userInfo()
|
|
153
34
|
debugLog('Using shell: %s', shell)
|
|
@@ -155,13 +36,13 @@ try {
|
|
|
155
36
|
debugLog('Could not determine current shell')
|
|
156
37
|
}
|
|
157
38
|
|
|
158
|
-
debugLog('Options parsed from command-line: %o',
|
|
39
|
+
debugLog('Options parsed from command-line: %o', cliOptions)
|
|
159
40
|
|
|
160
|
-
if (
|
|
161
|
-
delete
|
|
41
|
+
if (cliOptions.configPath === '-') {
|
|
42
|
+
delete cliOptions.configPath
|
|
162
43
|
try {
|
|
163
44
|
debugLog('Reading config from stdin')
|
|
164
|
-
|
|
45
|
+
cliOptions.config = JSON.parse(await readStdin())
|
|
165
46
|
} catch (error) {
|
|
166
47
|
debugLog(CONFIG_STDIN_ERROR, error)
|
|
167
48
|
console.error(CONFIG_STDIN_ERROR)
|
|
@@ -169,7 +50,7 @@ if (options.configPath === '-') {
|
|
|
169
50
|
}
|
|
170
51
|
}
|
|
171
52
|
|
|
172
|
-
const passed = await lintStaged(
|
|
53
|
+
const passed = await lintStaged(cliOptions)
|
|
173
54
|
if (!passed) {
|
|
174
55
|
process.exitCode = 1
|
|
175
56
|
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/** @see {@link https://github.com/git/git/blob/master/Documentation/RelNotes/2.32.0.adoc} */
|
|
2
|
+
export const MIN_GIT_VERSION = '2.32.0'
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* @param {string} gitVersionOutput the Git version command output
|
|
6
|
+
* @returns {string} the Git version number in format `<major>.<minor>.<patch>`
|
|
7
|
+
*/
|
|
8
|
+
const extractGitVersionNumber = (gitVersionOutput) => {
|
|
9
|
+
const match = gitVersionOutput.match(/git version\s+(\d+\.\d+\.\d+)/i)
|
|
10
|
+
return match?.[1]
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* @param {string} version the version number in format `<major>.<minor>.<patch>`
|
|
15
|
+
* @returns {[string, string, string]} the version number parsed as integers `[major, minor, patch]`
|
|
16
|
+
*/
|
|
17
|
+
const parseSemver = (version) => {
|
|
18
|
+
const match = /(\d+)\.(\d+)\.(\d+)/.exec(version)
|
|
19
|
+
return match?.slice(1, 4).map(Number)
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
*
|
|
24
|
+
* @param {string} actual the actual version number in format `<major>.<minor>.<patch>`
|
|
25
|
+
* @param {string} expected the expected version number in format `<major>.<minor>.<patch>`
|
|
26
|
+
* @returns {boolean} `true` when the actual version number is at least the expected version number
|
|
27
|
+
*/
|
|
28
|
+
export const satisfiesVersion = (actual, expected) => {
|
|
29
|
+
const a = parseSemver(actual)
|
|
30
|
+
const e = parseSemver(expected)
|
|
31
|
+
if (a[0] !== e[0]) return a[0] > e[0]
|
|
32
|
+
if (a[1] !== e[1]) return a[1] > e[1]
|
|
33
|
+
return a[2] >= e[2]
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
*
|
|
38
|
+
* @param {string} gitVersionOutput the Git version command output
|
|
39
|
+
* @returns {boolean} `true` when the Git version number is supported
|
|
40
|
+
*/
|
|
41
|
+
export const assertGitVersion = (gitVersionOutput) => {
|
|
42
|
+
try {
|
|
43
|
+
const version = extractGitVersionNumber(gitVersionOutput)
|
|
44
|
+
return satisfiesVersion(version, MIN_GIT_VERSION)
|
|
45
|
+
} catch {
|
|
46
|
+
return false
|
|
47
|
+
}
|
|
48
|
+
}
|
package/lib/cli.js
ADDED
|
@@ -0,0 +1,242 @@
|
|
|
1
|
+
import { readFile } from 'node:fs/promises'
|
|
2
|
+
import path from 'node:path'
|
|
3
|
+
import { fileURLToPath } from 'node:url'
|
|
4
|
+
import { parseArgs } from 'node:util'
|
|
5
|
+
|
|
6
|
+
import { restoreStashExample } from './messages.js'
|
|
7
|
+
|
|
8
|
+
const CLI_OPTIONS = [
|
|
9
|
+
{
|
|
10
|
+
short: 'h',
|
|
11
|
+
flag: 'help',
|
|
12
|
+
type: 'boolean',
|
|
13
|
+
description: 'display this help message',
|
|
14
|
+
},
|
|
15
|
+
{
|
|
16
|
+
short: 'V',
|
|
17
|
+
flag: 'version',
|
|
18
|
+
type: 'boolean',
|
|
19
|
+
description: 'display the current version number',
|
|
20
|
+
},
|
|
21
|
+
{
|
|
22
|
+
flag: 'allow-empty',
|
|
23
|
+
type: 'boolean',
|
|
24
|
+
description: 'allow empty commits when tasks revert all staged changes (default: false)',
|
|
25
|
+
},
|
|
26
|
+
{
|
|
27
|
+
short: 'p',
|
|
28
|
+
flag: 'concurrent',
|
|
29
|
+
positional: '<number|boolean>',
|
|
30
|
+
type: 'string',
|
|
31
|
+
description: 'the number of tasks to run concurrently, or false for serial (default: true)',
|
|
32
|
+
},
|
|
33
|
+
{
|
|
34
|
+
short: 'c',
|
|
35
|
+
flag: 'config',
|
|
36
|
+
positional: '[path]',
|
|
37
|
+
type: 'string',
|
|
38
|
+
description: 'path to configuration file, or - to read from stdin',
|
|
39
|
+
},
|
|
40
|
+
{
|
|
41
|
+
flag: 'continue-on-error',
|
|
42
|
+
type: 'boolean',
|
|
43
|
+
description: 'run all tasks to completion even if one fails (default: false)',
|
|
44
|
+
},
|
|
45
|
+
{
|
|
46
|
+
flag: 'cwd',
|
|
47
|
+
positional: '[path]',
|
|
48
|
+
type: 'string',
|
|
49
|
+
description: 'run all tasks in specific directory, instead of the current',
|
|
50
|
+
},
|
|
51
|
+
{
|
|
52
|
+
short: 'd',
|
|
53
|
+
flag: 'debug',
|
|
54
|
+
type: 'boolean',
|
|
55
|
+
description: 'print additional debug information (default: false)',
|
|
56
|
+
},
|
|
57
|
+
{
|
|
58
|
+
flag: 'diff',
|
|
59
|
+
positional: '[string]',
|
|
60
|
+
type: 'string',
|
|
61
|
+
description:
|
|
62
|
+
'override the default "--staged" flag of "git diff" to get list of files. Implies "--no-stash".',
|
|
63
|
+
},
|
|
64
|
+
{
|
|
65
|
+
flag: 'diff-filter',
|
|
66
|
+
positional: '[string]',
|
|
67
|
+
type: 'string',
|
|
68
|
+
description:
|
|
69
|
+
'override the default "--diff-filter=ACMR" flag of "git diff" to get list of files',
|
|
70
|
+
},
|
|
71
|
+
{
|
|
72
|
+
flag: 'fail-on-changes',
|
|
73
|
+
type: 'boolean',
|
|
74
|
+
description: 'fail with exit code 1 when tasks modify tracked files (default: false)',
|
|
75
|
+
},
|
|
76
|
+
{
|
|
77
|
+
negative: true,
|
|
78
|
+
flag: 'hide-partially-staged',
|
|
79
|
+
type: 'boolean',
|
|
80
|
+
description: 'hide unstaged changes from partially staged files (default: true)',
|
|
81
|
+
},
|
|
82
|
+
{
|
|
83
|
+
flag: 'hide-unstaged',
|
|
84
|
+
type: 'boolean',
|
|
85
|
+
description: 'hide all unstaged changes, instead of just partially staged (default: false)',
|
|
86
|
+
},
|
|
87
|
+
{
|
|
88
|
+
flag: 'hide-all',
|
|
89
|
+
type: 'boolean',
|
|
90
|
+
description: 'hide all unstaged changes and untracked files (default: false)',
|
|
91
|
+
},
|
|
92
|
+
{
|
|
93
|
+
flag: 'max-arg-length',
|
|
94
|
+
type: 'string', // Parsed with `parseInt()` below
|
|
95
|
+
positional: '[number]',
|
|
96
|
+
description: 'maximum length of the command-line argument string (default: 0)',
|
|
97
|
+
},
|
|
98
|
+
{
|
|
99
|
+
short: 'q',
|
|
100
|
+
flag: 'quiet',
|
|
101
|
+
type: 'boolean',
|
|
102
|
+
description: "disable lint-staged's own console output (default: false)",
|
|
103
|
+
},
|
|
104
|
+
{
|
|
105
|
+
short: 'r',
|
|
106
|
+
flag: 'relative',
|
|
107
|
+
type: 'boolean',
|
|
108
|
+
description: 'pass relative filepaths to tasks (default: false)',
|
|
109
|
+
},
|
|
110
|
+
{
|
|
111
|
+
negative: true,
|
|
112
|
+
flag: 'revert',
|
|
113
|
+
type: 'boolean',
|
|
114
|
+
description: 'revert to original state in case of errors (default: true)',
|
|
115
|
+
},
|
|
116
|
+
{
|
|
117
|
+
negative: true,
|
|
118
|
+
flag: 'stash',
|
|
119
|
+
type: 'boolean',
|
|
120
|
+
description: 'enable the backup stash (default: true)',
|
|
121
|
+
},
|
|
122
|
+
{
|
|
123
|
+
short: 'v',
|
|
124
|
+
flag: 'verbose',
|
|
125
|
+
type: 'boolean',
|
|
126
|
+
description:
|
|
127
|
+
'show task output even when tasks succeed; by default only failed output is shown (default: false)',
|
|
128
|
+
},
|
|
129
|
+
]
|
|
130
|
+
|
|
131
|
+
/** @param {string[]} argv */
|
|
132
|
+
export const parseCliOptions = (argv) => {
|
|
133
|
+
const options = CLI_OPTIONS.reduce((acc, current) => {
|
|
134
|
+
acc[current.flag] = { type: current.type }
|
|
135
|
+
if (current.short) acc[current.flag].short = current.short
|
|
136
|
+
return acc
|
|
137
|
+
}, {})
|
|
138
|
+
|
|
139
|
+
const { values } = parseArgs({
|
|
140
|
+
args: argv,
|
|
141
|
+
allowNegative: true,
|
|
142
|
+
allowPositionals: true,
|
|
143
|
+
options,
|
|
144
|
+
})
|
|
145
|
+
|
|
146
|
+
if (values.diff !== undefined && values.stash === undefined) {
|
|
147
|
+
/** Disable stashing by default when diffing specific value */
|
|
148
|
+
values.stash = false
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
if (values['fail-on-changes'] && values.revert === undefined) {
|
|
152
|
+
/** When using --fail-on-changes, default to not reverting on errors */
|
|
153
|
+
values.revert = false
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
if (values.stash === false && values.revert === undefined) {
|
|
157
|
+
/** Can't revert when using --no-stash */
|
|
158
|
+
values.revert = false
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
if (values['hide-unstaged'] === true) {
|
|
162
|
+
values['hide-partially-staged'] = false // becomes redundant
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
if (values['hide-all'] === true) {
|
|
166
|
+
values['hide-partially-staged'] = false // becomes redundant
|
|
167
|
+
values['hide-unstaged'] = false // becomes redundant
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
return {
|
|
171
|
+
allowEmpty: values['allow-empty'] ?? false,
|
|
172
|
+
concurrent: values.concurrent === undefined ? true : JSON.parse(values.concurrent),
|
|
173
|
+
configPath: values.config,
|
|
174
|
+
continueOnError: !!values['continue-on-error'],
|
|
175
|
+
cwd: values.cwd,
|
|
176
|
+
debug: !!values.debug,
|
|
177
|
+
diff: values.diff,
|
|
178
|
+
diffFilter: values['diff-filter'],
|
|
179
|
+
failOnChanges: !!values['fail-on-changes'],
|
|
180
|
+
help: !!values.help,
|
|
181
|
+
hidePartiallyStaged: values['hide-partially-staged'] ?? true,
|
|
182
|
+
hideUnstaged: !!values['hide-unstaged'],
|
|
183
|
+
hideAll: !!values['hide-all'],
|
|
184
|
+
maxArgLength: parseInt(values['max-arg-length'], 10),
|
|
185
|
+
quiet: !!values.quiet,
|
|
186
|
+
relative: !!values.relative,
|
|
187
|
+
revert: values.revert ?? true,
|
|
188
|
+
stash: values.stash ?? true,
|
|
189
|
+
verbose: !!values.verbose,
|
|
190
|
+
version: !!values.version,
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
export const getVersionNumber = async () => {
|
|
195
|
+
const dirname = path.dirname(fileURLToPath(import.meta.url))
|
|
196
|
+
const packageJsonFile = await readFile(path.join(dirname, '../package.json'), 'utf-8')
|
|
197
|
+
/** @type {import('../package.json')} */
|
|
198
|
+
const packageJson = JSON.parse(packageJsonFile)
|
|
199
|
+
|
|
200
|
+
return packageJson.version
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
const helpOptions = CLI_OPTIONS.map((option) => {
|
|
204
|
+
if (option.negative) {
|
|
205
|
+
/** @example `--no-stash` */
|
|
206
|
+
return [`--no-${option.flag}`, option.description]
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
/**
|
|
210
|
+
* @example `-V, --version
|
|
211
|
+
* or
|
|
212
|
+
* @example `--allow-empty`
|
|
213
|
+
*/
|
|
214
|
+
let arg = option.short ? `-${option.short}, --${option.flag}` : `--${option.flag}`
|
|
215
|
+
|
|
216
|
+
/** @example `--cwd [path]` */
|
|
217
|
+
if (option.positional) arg += ` ${option.positional}`
|
|
218
|
+
|
|
219
|
+
return [arg, option.description]
|
|
220
|
+
})
|
|
221
|
+
|
|
222
|
+
const createWrap = (width) => {
|
|
223
|
+
const regExp = new RegExp(`.{1,${width}}(\\s|$)`, 'g')
|
|
224
|
+
return (text) => text.match(regExp)?.map((s) => s.trimEnd())
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
export const printHelpText = async (width = process.stdout.columns ?? 80) => {
|
|
228
|
+
const output = ['Usage: lint-staged [options]', '']
|
|
229
|
+
|
|
230
|
+
const col1Width = Math.max(...helpOptions.map(([arg]) => arg.length)) + 2
|
|
231
|
+
const wrap = createWrap(width - col1Width)
|
|
232
|
+
|
|
233
|
+
for (const [arg, description] of helpOptions) {
|
|
234
|
+
const lines = wrap(description)
|
|
235
|
+
const pad = ' '.repeat(col1Width)
|
|
236
|
+
output.push(arg.padEnd(col1Width) + lines[0], ...lines.slice(1).map((line) => pad + line))
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
output.push('', restoreStashExample())
|
|
240
|
+
|
|
241
|
+
return output.join('\n')
|
|
242
|
+
}
|