lint-staged 12.3.3 → 12.3.6
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 +11 -0
- package/lib/getConfigGroups.js +0 -7
- package/lib/getStagedFiles.js +2 -9
- package/lib/loadConfig.js +1 -1
- package/lib/parseGitZOutput.js +9 -0
- package/lib/resolveTaskFn.js +38 -3
- package/lib/runAll.js +15 -2
- package/lib/searchConfigs.js +69 -0
- package/package.json +6 -2
package/README.md
CHANGED
|
@@ -713,6 +713,17 @@ Example repo: [sudo-suhas/lint-staged-django-react-demo](https://github.com/sudo
|
|
|
713
713
|
|
|
714
714
|
</details>
|
|
715
715
|
|
|
716
|
+
### Can I use `lint-staged` with `ng lint`
|
|
717
|
+
|
|
718
|
+
<details>
|
|
719
|
+
<summary>Click to expand</summary>
|
|
720
|
+
|
|
721
|
+
You should not use `ng lint` through _lint-staged_, because it's designed to lint an entire project. Instead, you can add `ng lint` to your git pre-commit hook the same way as you would run lint-staged.
|
|
722
|
+
|
|
723
|
+
See issue [!951](https://github.com/okonet/lint-staged/issues/951) for more details and possible workarounds.
|
|
724
|
+
|
|
725
|
+
</details>
|
|
726
|
+
|
|
716
727
|
### How can I ignore files from `.eslintignore`?
|
|
717
728
|
|
|
718
729
|
<details>
|
package/lib/getConfigGroups.js
CHANGED
|
@@ -99,13 +99,6 @@ export const getConfigGroups = async (
|
|
|
99
99
|
// Discover configs from the base directory of each file
|
|
100
100
|
await Promise.all(Object.entries(filesByDir).map(([dir, files]) => searchConfig(dir, files)))
|
|
101
101
|
|
|
102
|
-
// Throw if no configurations were found
|
|
103
|
-
if (Object.keys(configGroups).length === 0) {
|
|
104
|
-
debugLog('Found no config groups!')
|
|
105
|
-
logger.error(`${ConfigNotFoundError.message}.`)
|
|
106
|
-
throw ConfigNotFoundError
|
|
107
|
-
}
|
|
108
|
-
|
|
109
102
|
debugLog('Grouped staged files into %d groups!', Object.keys(configGroups).length)
|
|
110
103
|
|
|
111
104
|
return configGroups
|
package/lib/getStagedFiles.js
CHANGED
|
@@ -3,6 +3,7 @@ import path from 'path'
|
|
|
3
3
|
import normalize from 'normalize-path'
|
|
4
4
|
|
|
5
5
|
import { execGit } from './execGit.js'
|
|
6
|
+
import { parseGitZOutput } from './parseGitZOutput.js'
|
|
6
7
|
|
|
7
8
|
export const getStagedFiles = async ({ cwd = process.cwd() } = {}) => {
|
|
8
9
|
try {
|
|
@@ -14,15 +15,7 @@ export const getStagedFiles = async ({ cwd = process.cwd() } = {}) => {
|
|
|
14
15
|
|
|
15
16
|
if (!lines) return []
|
|
16
17
|
|
|
17
|
-
|
|
18
|
-
// remove the last occurrence of `\u0000` before splitting
|
|
19
|
-
return (
|
|
20
|
-
lines
|
|
21
|
-
// eslint-disable-next-line no-control-regex
|
|
22
|
-
.replace(/\u0000$/, '')
|
|
23
|
-
.split('\u0000')
|
|
24
|
-
.map((file) => normalize(path.resolve(cwd, file)))
|
|
25
|
-
)
|
|
18
|
+
return parseGitZOutput(lines).map((file) => normalize(path.resolve(cwd, file)))
|
|
26
19
|
} catch {
|
|
27
20
|
return null
|
|
28
21
|
}
|
package/lib/loadConfig.js
CHANGED
|
@@ -13,7 +13,7 @@ const debugLog = debug('lint-staged:loadConfig')
|
|
|
13
13
|
* The list of files `lint-staged` will read configuration
|
|
14
14
|
* from, in the declared order.
|
|
15
15
|
*/
|
|
16
|
-
const searchPlaces = [
|
|
16
|
+
export const searchPlaces = [
|
|
17
17
|
'package.json',
|
|
18
18
|
'.lintstagedrc',
|
|
19
19
|
'.lintstagedrc.json',
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Return array of strings split from the output of `git <something> -z`.
|
|
3
|
+
* With `-z`, git prints `fileA\u0000fileB\u0000fileC\u0000` so we need to
|
|
4
|
+
* remove the last occurrence of `\u0000` before splitting
|
|
5
|
+
*/
|
|
6
|
+
export const parseGitZOutput = (input) =>
|
|
7
|
+
input
|
|
8
|
+
.replace(/\u0000$/, '') // eslint-disable-line no-control-regex
|
|
9
|
+
.split('\u0000')
|
package/lib/resolveTaskFn.js
CHANGED
|
@@ -2,14 +2,17 @@ import { redBright, dim } from 'colorette'
|
|
|
2
2
|
import execa from 'execa'
|
|
3
3
|
import debug from 'debug'
|
|
4
4
|
import { parseArgsStringToArgv } from 'string-argv'
|
|
5
|
+
import pidTree from 'pidtree'
|
|
5
6
|
|
|
6
7
|
import { error, info } from './figures.js'
|
|
7
8
|
import { getInitialState } from './state.js'
|
|
8
9
|
import { TaskError } from './symbols.js'
|
|
9
10
|
|
|
11
|
+
const ERROR_CHECK_INTERVAL = 200
|
|
12
|
+
|
|
10
13
|
const debugLog = debug('lint-staged:resolveTaskFn')
|
|
11
14
|
|
|
12
|
-
const getTag = ({ code, killed, signal }) =>
|
|
15
|
+
const getTag = ({ code, killed, signal }) => (killed && 'KILLED') || signal || code || 'FAILED'
|
|
13
16
|
|
|
14
17
|
/**
|
|
15
18
|
* Handle task console output.
|
|
@@ -43,6 +46,34 @@ const handleOutput = (command, result, ctx, isError = false) => {
|
|
|
43
46
|
}
|
|
44
47
|
}
|
|
45
48
|
|
|
49
|
+
/**
|
|
50
|
+
* Interrupts the execution of the execa process that we spawned if
|
|
51
|
+
* another task adds an error to the context.
|
|
52
|
+
*
|
|
53
|
+
* @param {Object} ctx
|
|
54
|
+
* @param {execa.ExecaChildProcess<string>} execaChildProcess
|
|
55
|
+
* @returns {function(): void} Function that clears the interval that
|
|
56
|
+
* checks the context.
|
|
57
|
+
*/
|
|
58
|
+
const interruptExecutionOnError = (ctx, execaChildProcess) => {
|
|
59
|
+
async function loop() {
|
|
60
|
+
if (ctx.errors.size > 0) {
|
|
61
|
+
const ids = await pidTree(execaChildProcess.pid)
|
|
62
|
+
ids.forEach(process.kill)
|
|
63
|
+
|
|
64
|
+
// The execa process is killed separately in order
|
|
65
|
+
// to get the `KILLED` status.
|
|
66
|
+
execaChildProcess.kill()
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
const loopIntervalId = setInterval(loop, ERROR_CHECK_INTERVAL)
|
|
71
|
+
|
|
72
|
+
return () => {
|
|
73
|
+
clearInterval(loopIntervalId)
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
46
77
|
/**
|
|
47
78
|
* Create a error output dependding on process result.
|
|
48
79
|
*
|
|
@@ -101,9 +132,13 @@ export const resolveTaskFn = ({
|
|
|
101
132
|
debugLog('execaOptions:', execaOptions)
|
|
102
133
|
|
|
103
134
|
return async (ctx = getInitialState()) => {
|
|
104
|
-
const
|
|
135
|
+
const execaChildProcess = shell
|
|
105
136
|
? execa.command(isFn ? command : `${command} ${files.join(' ')}`, execaOptions)
|
|
106
|
-
: execa(cmd, isFn ? args : args.concat(files), execaOptions)
|
|
137
|
+
: execa(cmd, isFn ? args : args.concat(files), execaOptions)
|
|
138
|
+
|
|
139
|
+
const quitInterruptCheck = interruptExecutionOnError(ctx, execaChildProcess)
|
|
140
|
+
const result = await execaChildProcess
|
|
141
|
+
quitInterruptCheck()
|
|
107
142
|
|
|
108
143
|
if (result.failed || result.killed || result.signal != null) {
|
|
109
144
|
throw makeErr(command, result, ctx)
|
package/lib/runAll.js
CHANGED
|
@@ -35,7 +35,8 @@ import {
|
|
|
35
35
|
restoreOriginalStateSkipped,
|
|
36
36
|
restoreUnstagedChangesSkipped,
|
|
37
37
|
} from './state.js'
|
|
38
|
-
import { GitRepoError, GetStagedFilesError, GitError } from './symbols.js'
|
|
38
|
+
import { GitRepoError, GetStagedFilesError, GitError, ConfigNotFoundError } from './symbols.js'
|
|
39
|
+
import { searchConfigs } from './searchConfigs.js'
|
|
39
40
|
|
|
40
41
|
const debugLog = debug('lint-staged:runAll')
|
|
41
42
|
|
|
@@ -121,7 +122,19 @@ export const runAll = async (
|
|
|
121
122
|
|
|
122
123
|
const configGroups = await getConfigGroups({ configObject, configPath, cwd, files }, logger)
|
|
123
124
|
|
|
124
|
-
const
|
|
125
|
+
const hasExplicitConfig = configObject || configPath
|
|
126
|
+
const foundConfigs = hasExplicitConfig ? null : await searchConfigs(gitDir, logger)
|
|
127
|
+
const numberOfConfigs = hasExplicitConfig ? 1 : Object.keys(foundConfigs).length
|
|
128
|
+
|
|
129
|
+
// Throw if no configurations were found
|
|
130
|
+
if (numberOfConfigs === 0) {
|
|
131
|
+
ctx.errors.add(ConfigNotFoundError)
|
|
132
|
+
throw createError(ctx, ConfigNotFoundError)
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
debugLog('Found %d configs:\n%O', numberOfConfigs, foundConfigs)
|
|
136
|
+
|
|
137
|
+
const hasMultipleConfigs = numberOfConfigs > 1
|
|
125
138
|
|
|
126
139
|
// lint-staged 10 will automatically add modifications to index
|
|
127
140
|
// Warn user when their command includes `git add`
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
/** @typedef {import('./index').Logger} Logger */
|
|
2
|
+
|
|
3
|
+
import { basename, join } from 'path'
|
|
4
|
+
|
|
5
|
+
import normalize from 'normalize-path'
|
|
6
|
+
|
|
7
|
+
import { execGit } from './execGit.js'
|
|
8
|
+
import { loadConfig, searchPlaces } from './loadConfig.js'
|
|
9
|
+
import { parseGitZOutput } from './parseGitZOutput.js'
|
|
10
|
+
import { validateConfig } from './validateConfig.js'
|
|
11
|
+
|
|
12
|
+
const EXEC_GIT = ['ls-files', '-z', '--full-name']
|
|
13
|
+
|
|
14
|
+
const filterPossibleConfigFiles = (file) => searchPlaces.includes(basename(file))
|
|
15
|
+
|
|
16
|
+
const numberOfLevels = (file) => file.split('/').length
|
|
17
|
+
|
|
18
|
+
const sortDeepestParth = (a, b) => (numberOfLevels(a) > numberOfLevels(b) ? -1 : 1)
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Search all config files from the git repository
|
|
22
|
+
*
|
|
23
|
+
* @param {string} gitDir
|
|
24
|
+
* @param {Logger} logger
|
|
25
|
+
* @returns {Promise<{ [key: string]: * }>} found configs with filepath as key, and config as value
|
|
26
|
+
*/
|
|
27
|
+
export const searchConfigs = async (gitDir = process.cwd(), logger) => {
|
|
28
|
+
/** Get all possible config files known to git */
|
|
29
|
+
const cachedFiles = parseGitZOutput(await execGit(EXEC_GIT, { cwd: gitDir })).filter(
|
|
30
|
+
filterPossibleConfigFiles
|
|
31
|
+
)
|
|
32
|
+
|
|
33
|
+
/** Get all possible config files from uncommitted files */
|
|
34
|
+
const otherFiles = parseGitZOutput(
|
|
35
|
+
await execGit([...EXEC_GIT, '--others', '--exclude-standard'], { cwd: gitDir })
|
|
36
|
+
).filter(filterPossibleConfigFiles)
|
|
37
|
+
|
|
38
|
+
/** Sort possible config files so that deepest is first */
|
|
39
|
+
const possibleConfigFiles = [...cachedFiles, ...otherFiles]
|
|
40
|
+
.map((file) => join(gitDir, file))
|
|
41
|
+
.map((file) => normalize(file))
|
|
42
|
+
.sort(sortDeepestParth)
|
|
43
|
+
|
|
44
|
+
/** Create object with key as config file, and value as null */
|
|
45
|
+
const configs = possibleConfigFiles.reduce(
|
|
46
|
+
(acc, configPath) => Object.assign(acc, { [configPath]: null }),
|
|
47
|
+
{}
|
|
48
|
+
)
|
|
49
|
+
|
|
50
|
+
/** Load and validate all configs to the above object */
|
|
51
|
+
await Promise.all(
|
|
52
|
+
possibleConfigFiles
|
|
53
|
+
.map((configPath) => loadConfig({ configPath }, logger))
|
|
54
|
+
.map((promise) =>
|
|
55
|
+
promise.then(({ config, filepath }) => {
|
|
56
|
+
if (config) {
|
|
57
|
+
configs[filepath] = validateConfig(config, filepath, logger)
|
|
58
|
+
}
|
|
59
|
+
})
|
|
60
|
+
)
|
|
61
|
+
)
|
|
62
|
+
|
|
63
|
+
/** Get validated configs from the above object, without any `null` values (not found) */
|
|
64
|
+
const foundConfigs = Object.entries(configs)
|
|
65
|
+
.filter(([, value]) => !!value)
|
|
66
|
+
.reduce((acc, [key, value]) => ({ ...acc, [key]: value }), {})
|
|
67
|
+
|
|
68
|
+
return foundConfigs
|
|
69
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "lint-staged",
|
|
3
|
-
"version": "12.3.
|
|
3
|
+
"version": "12.3.6",
|
|
4
4
|
"description": "Lint files staged by git",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": "https://github.com/okonet/lint-staged",
|
|
@@ -18,7 +18,10 @@
|
|
|
18
18
|
},
|
|
19
19
|
"type": "module",
|
|
20
20
|
"bin": "./bin/lint-staged.js",
|
|
21
|
-
"exports":
|
|
21
|
+
"exports": {
|
|
22
|
+
".": "./lib/index.js",
|
|
23
|
+
"./package.json": "./package.json"
|
|
24
|
+
},
|
|
22
25
|
"files": [
|
|
23
26
|
"bin",
|
|
24
27
|
"lib"
|
|
@@ -39,6 +42,7 @@
|
|
|
39
42
|
"micromatch": "^4.0.4",
|
|
40
43
|
"normalize-path": "^3.0.0",
|
|
41
44
|
"object-inspect": "^1.12.0",
|
|
45
|
+
"pidtree": "^0.5.0",
|
|
42
46
|
"string-argv": "^0.3.1",
|
|
43
47
|
"supports-color": "^9.2.1",
|
|
44
48
|
"yaml": "^1.10.2"
|