lint-staged 13.2.2 → 13.3.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/README.md +11 -9
- package/bin/lint-staged.js +14 -16
- package/lib/chunkFiles.js +3 -2
- package/lib/generateTasks.js +4 -3
- package/lib/getRenderer.js +50 -14
- package/lib/getStagedFiles.js +2 -3
- package/lib/makeCmdTasks.js +2 -23
- package/lib/messages.js +3 -4
- package/lib/normalizePath.js +50 -0
- package/lib/resolveGitRepo.js +7 -7
- package/lib/runAll.js +4 -5
- package/lib/searchConfigs.js +3 -3
- package/lib/validateBraces.js +30 -6
- package/lib/validateConfig.js +3 -2
- package/package.json +25 -29
package/README.md
CHANGED
|
@@ -58,7 +58,7 @@ To install _lint-staged_ in the recommended way, you need to:
|
|
|
58
58
|
1. Install some linters, like [ESLint](https://eslint.org) or [Prettier](https://prettier.io)
|
|
59
59
|
1. Configure _lint-staged_ to run linters and other tasks:
|
|
60
60
|
- for example: `{ "*.js": "eslint" }` to run ESLint for all staged JS files
|
|
61
|
-
- See [Configuration](#
|
|
61
|
+
- See [Configuration](#configuration) for more info
|
|
62
62
|
|
|
63
63
|
Don't forget to commit changes to `package.json` and `.husky` to share this setup with your team!
|
|
64
64
|
|
|
@@ -72,6 +72,10 @@ See [Releases](https://github.com/okonet/lint-staged/releases).
|
|
|
72
72
|
|
|
73
73
|
### Migration
|
|
74
74
|
|
|
75
|
+
#### v14
|
|
76
|
+
|
|
77
|
+
- Since `v14.0.0` _lint-staged_ no longer supports Node.js 14. Please upgrade your Node.js version to at least `16.14.0`.
|
|
78
|
+
|
|
75
79
|
#### v13
|
|
76
80
|
|
|
77
81
|
- Since `v13.0.0` _lint-staged_ no longer supports Node.js 12. Please upgrade your Node.js version to at least `14.13.1`, or `16.0.0` onward.
|
|
@@ -131,7 +135,7 @@ Options:
|
|
|
131
135
|
- **`--diff`**: By default linters are filtered against all files staged in git, generated from `git diff --staged`. This option allows you to override the `--staged` flag with arbitrary revisions. For example to get a list of changed files between two branches, use `--diff="branch1...branch2"`. You can also read more from about [git diff](https://git-scm.com/docs/git-diff) and [gitrevisions](https://git-scm.com/docs/gitrevisions). This option also implies `--no-stash`.
|
|
132
136
|
- **`--diff-filter`**: By default only files that are _added_, _copied_, _modified_, or _renamed_ are included. Use this flag to override the default `ACMR` value with something else: _added_ (`A`), _copied_ (`C`), _deleted_ (`D`), _modified_ (`M`), _renamed_ (`R`), _type changed_ (`T`), _unmerged_ (`U`), _unknown_ (`X`), or _pairing broken_ (`B`). See also the `git diff` docs for [--diff-filter](https://git-scm.com/docs/git-diff#Documentation/git-diff.txt---diff-filterACDMRTUXB82308203).
|
|
133
137
|
- **`--max-arg-length`**: long commands (a lot of files) are automatically split into multiple chunks when it detects the current shell cannot handle them. Use this flag to override the maximum length of the generated command string.
|
|
134
|
-
- **`--no-stash`**: By default a backup stash will be created before running the tasks, and all task modifications will be reverted in case of an error. This option will disable creating the stash, and instead leave all modifications in the index when aborting the commit. Can be re-enabled with `--stash
|
|
138
|
+
- **`--no-stash`**: By default a backup stash will be created before running the tasks, and all task modifications will be reverted in case of an error. This option will disable creating the stash, and instead leave all modifications in the index when aborting the commit. Can be re-enabled with `--stash`.
|
|
135
139
|
- **`--quiet`**: Supress all CLI output, except from tasks.
|
|
136
140
|
- **`--relative`**: Pass filepaths relative to `process.cwd()` (where `lint-staged` runs) to tasks. Default is `false`.
|
|
137
141
|
- **`--shell`**: By default linter commands will be parsed for speed and security. This has the side-effect that regular shell scripts might not work as expected. You can skip parsing of commands with this option. To use a specific shell, use a path like `--shell "/bin/bash"`.
|
|
@@ -218,11 +222,11 @@ If necessary, you can limit the concurrency using `--concurrent <number>` or dis
|
|
|
218
222
|
Linter commands work on a subset of all staged files, defined by a _glob pattern_. lint-staged uses [micromatch](https://github.com/micromatch/micromatch) for matching files with the following rules:
|
|
219
223
|
|
|
220
224
|
- If the glob pattern contains no slashes (`/`), micromatch's `matchBase` option will enabled, so globs match a file's basename regardless of directory:
|
|
221
|
-
-
|
|
222
|
-
-
|
|
225
|
+
- `"*.js"` will match all JS files, like `/test.js` and `/foo/bar/test.js`
|
|
226
|
+
- `"!(*test).js"` will match all JS files, except those ending in `test.js`, so `foo.js` but not `foo.test.js`
|
|
223
227
|
- If the glob pattern does contain a slash (`/`), it will match for paths as well:
|
|
224
|
-
-
|
|
225
|
-
-
|
|
228
|
+
- `"./*.js"` will match all JS files in the git repo root, so `/test.js` but not `/foo/bar/test.js`
|
|
229
|
+
- `"foo/**/*.js"` will match all JS files inside the `/foo` directory, so `/foo/bar/test.js` but not `/test.js`
|
|
226
230
|
|
|
227
231
|
When matching, lint-staged will do the following
|
|
228
232
|
|
|
@@ -624,9 +628,7 @@ See more on [this blog post](https://medium.com/@tomchentw/imagemin-lint-staged-
|
|
|
624
628
|
const path = require('path')
|
|
625
629
|
|
|
626
630
|
const buildEslintCommand = (filenames) =>
|
|
627
|
-
`next lint --fix --file ${filenames
|
|
628
|
-
.map((f) => path.relative(process.cwd(), f))
|
|
629
|
-
.join(' --file ')}`
|
|
631
|
+
`next lint --fix --file ${filenames.map((f) => path.relative(process.cwd(), f)).join(' --file ')}`
|
|
630
632
|
|
|
631
633
|
module.exports = {
|
|
632
634
|
'*.{js,jsx,ts,tsx}': [buildEslintCommand],
|
package/bin/lint-staged.js
CHANGED
|
@@ -1,8 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
|
-
import fs from 'node:fs'
|
|
4
|
-
import path from 'node:path'
|
|
5
|
-
import { fileURLToPath } from 'node:url'
|
|
3
|
+
import fs from 'node:fs/promises'
|
|
6
4
|
|
|
7
5
|
import { supportsColor } from 'chalk'
|
|
8
6
|
import { Option, program } from 'commander'
|
|
@@ -19,8 +17,7 @@ if (supportsColor) {
|
|
|
19
17
|
// Do not terminate main Listr process on SIGINT
|
|
20
18
|
process.on('SIGINT', () => {})
|
|
21
19
|
|
|
22
|
-
const
|
|
23
|
-
const packageJson = JSON.parse(fs.readFileSync(packageJsonPath))
|
|
20
|
+
const packageJson = JSON.parse(await fs.readFile(new URL('../package.json', import.meta.url)))
|
|
24
21
|
const version = packageJson.version
|
|
25
22
|
|
|
26
23
|
const debugLog = debug('lint-staged:bin')
|
|
@@ -42,9 +39,11 @@ cli.option('--cwd [path]', 'run all tasks in specific directory, instead of the
|
|
|
42
39
|
|
|
43
40
|
cli.option('-d, --debug', 'print additional debug information', false)
|
|
44
41
|
|
|
45
|
-
cli.
|
|
46
|
-
|
|
47
|
-
|
|
42
|
+
cli.addOption(
|
|
43
|
+
new Option(
|
|
44
|
+
'--diff [string]',
|
|
45
|
+
'override the default "--staged" flag of "git diff" to get list of files. Implies "--no-stash".'
|
|
46
|
+
).implies({ stash: false })
|
|
48
47
|
)
|
|
49
48
|
|
|
50
49
|
cli.option(
|
|
@@ -111,7 +110,7 @@ debugLog('Options parsed from command-line:', options)
|
|
|
111
110
|
if (options.configPath === '-') {
|
|
112
111
|
delete options.configPath
|
|
113
112
|
try {
|
|
114
|
-
options.config = fs.
|
|
113
|
+
options.config = await fs.readFile(process.stdin.fd, 'utf8').toString().trim()
|
|
115
114
|
} catch {
|
|
116
115
|
console.error(CONFIG_STDIN_ERROR)
|
|
117
116
|
process.exit(1)
|
|
@@ -124,10 +123,9 @@ if (options.configPath === '-') {
|
|
|
124
123
|
}
|
|
125
124
|
}
|
|
126
125
|
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
.
|
|
132
|
-
|
|
133
|
-
})
|
|
126
|
+
try {
|
|
127
|
+
const passed = await lintStaged(options)
|
|
128
|
+
process.exitCode = passed ? 0 : 1
|
|
129
|
+
} catch {
|
|
130
|
+
process.exitCode = 1
|
|
131
|
+
}
|
package/lib/chunkFiles.js
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import path from 'node:path'
|
|
2
2
|
|
|
3
3
|
import debug from 'debug'
|
|
4
|
-
|
|
4
|
+
|
|
5
|
+
import { normalizePath } from './normalizePath.js'
|
|
5
6
|
|
|
6
7
|
const debugLog = debug('lint-staged:chunkFiles')
|
|
7
8
|
|
|
@@ -35,7 +36,7 @@ const chunkArray = (arr, chunkCount) => {
|
|
|
35
36
|
*/
|
|
36
37
|
export const chunkFiles = ({ files, baseDir, maxArgLength = null, relative = false }) => {
|
|
37
38
|
const normalizedFiles = files.map((file) =>
|
|
38
|
-
|
|
39
|
+
normalizePath(relative || !baseDir ? file : path.resolve(baseDir, file))
|
|
39
40
|
)
|
|
40
41
|
|
|
41
42
|
if (!maxArgLength) {
|
package/lib/generateTasks.js
CHANGED
|
@@ -2,7 +2,8 @@ import path from 'node:path'
|
|
|
2
2
|
|
|
3
3
|
import debug from 'debug'
|
|
4
4
|
import micromatch from 'micromatch'
|
|
5
|
-
|
|
5
|
+
|
|
6
|
+
import { normalizePath } from './normalizePath.js'
|
|
6
7
|
|
|
7
8
|
const debugLog = debug('lint-staged:generateTasks')
|
|
8
9
|
|
|
@@ -19,7 +20,7 @@ const debugLog = debug('lint-staged:generateTasks')
|
|
|
19
20
|
export const generateTasks = ({ config, cwd = process.cwd(), files, relative = false }) => {
|
|
20
21
|
debugLog('Generating linter tasks')
|
|
21
22
|
|
|
22
|
-
const relativeFiles = files.map((file) =>
|
|
23
|
+
const relativeFiles = files.map((file) => normalizePath(path.relative(cwd, file)))
|
|
23
24
|
|
|
24
25
|
return Object.entries(config).map(([pattern, commands]) => {
|
|
25
26
|
const isParentDirPattern = pattern.startsWith('../')
|
|
@@ -42,7 +43,7 @@ export const generateTasks = ({ config, cwd = process.cwd(), files, relative = f
|
|
|
42
43
|
strictBrackets: true,
|
|
43
44
|
})
|
|
44
45
|
|
|
45
|
-
const fileList = matches.map((file) =>
|
|
46
|
+
const fileList = matches.map((file) => normalizePath(relative ? file : path.resolve(cwd, file)))
|
|
46
47
|
|
|
47
48
|
const task = { pattern, commands, fileList }
|
|
48
49
|
debugLog('Generated task: \n%O', task)
|
package/lib/getRenderer.js
CHANGED
|
@@ -1,28 +1,64 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
import { EOL } from 'node:os'
|
|
2
|
+
import { Writable } from 'node:stream'
|
|
3
|
+
|
|
4
|
+
import { ListrLogger, ProcessOutput } from 'listr2'
|
|
5
|
+
|
|
6
|
+
const EOLRegex = new RegExp(EOL + '$')
|
|
7
|
+
|
|
8
|
+
const bindLogger = (consoleLogMethod) =>
|
|
9
|
+
new Writable({
|
|
10
|
+
write: function (chunk, encoding, next) {
|
|
11
|
+
consoleLogMethod(chunk.toString().replace(EOLRegex, ''))
|
|
12
|
+
next()
|
|
13
|
+
},
|
|
14
|
+
})
|
|
15
|
+
|
|
16
|
+
const getMainRendererOptions = ({ debug, quiet }, logger, env) => {
|
|
17
|
+
if (quiet) {
|
|
18
|
+
return {
|
|
19
|
+
renderer: 'silent',
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
if (env.NODE_ENV === 'test') {
|
|
24
|
+
return {
|
|
25
|
+
renderer: 'test',
|
|
26
|
+
rendererOptions: {
|
|
27
|
+
logger: new ListrLogger({
|
|
28
|
+
processOutput: new ProcessOutput(bindLogger(logger.log), bindLogger(logger.error)),
|
|
29
|
+
}),
|
|
30
|
+
},
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
3
34
|
// Better support for dumb terminals: https://en.wikipedia.org/wiki/Computer_terminal#Dumb_terminals
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
}
|
|
35
|
+
if (debug || env.TERM === 'dumb') {
|
|
36
|
+
return {
|
|
37
|
+
renderer: 'verbose',
|
|
38
|
+
}
|
|
39
|
+
}
|
|
8
40
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
41
|
+
return {
|
|
42
|
+
renderer: 'update',
|
|
43
|
+
rendererOptions: {
|
|
44
|
+
formatOutput: 'truncate',
|
|
45
|
+
},
|
|
12
46
|
}
|
|
47
|
+
}
|
|
13
48
|
|
|
14
|
-
|
|
15
|
-
if (Number(FORCE_COLOR) > 0) {
|
|
49
|
+
const getFallbackRenderer = ({ renderer }, { FORCE_COLOR }) => {
|
|
50
|
+
if (renderer === 'silent' || renderer === 'test' || Number(FORCE_COLOR) > 0) {
|
|
16
51
|
return renderer
|
|
17
52
|
}
|
|
18
53
|
|
|
19
54
|
return 'verbose'
|
|
20
55
|
}
|
|
21
56
|
|
|
22
|
-
export const getRenderer = (options, env = process.env) => {
|
|
23
|
-
const mainRendererOptions = getMainRendererOptions(options, env)
|
|
57
|
+
export const getRenderer = (options, logger, env = process.env) => {
|
|
58
|
+
const mainRendererOptions = getMainRendererOptions(options, logger, env)
|
|
59
|
+
|
|
24
60
|
return {
|
|
25
61
|
...mainRendererOptions,
|
|
26
|
-
|
|
62
|
+
fallbackRenderer: getFallbackRenderer(mainRendererOptions, env),
|
|
27
63
|
}
|
|
28
64
|
}
|
package/lib/getStagedFiles.js
CHANGED
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
import path from 'node:path'
|
|
2
2
|
|
|
3
|
-
import normalize from 'normalize-path'
|
|
4
|
-
|
|
5
3
|
import { execGit } from './execGit.js'
|
|
6
4
|
import { getDiffCommand } from './getDiffCommand.js'
|
|
5
|
+
import { normalizePath } from './normalizePath.js'
|
|
7
6
|
import { parseGitZOutput } from './parseGitZOutput.js'
|
|
8
7
|
|
|
9
8
|
export const getStagedFiles = async ({ cwd = process.cwd(), diff, diffFilter } = {}) => {
|
|
@@ -11,7 +10,7 @@ export const getStagedFiles = async ({ cwd = process.cwd(), diff, diffFilter } =
|
|
|
11
10
|
const lines = await execGit(getDiffCommand(diff, diffFilter), { cwd })
|
|
12
11
|
if (!lines) return []
|
|
13
12
|
|
|
14
|
-
return parseGitZOutput(lines).map((file) =>
|
|
13
|
+
return parseGitZOutput(lines).map((file) => normalizePath(path.resolve(cwd, file)))
|
|
15
14
|
} catch {
|
|
16
15
|
return null
|
|
17
16
|
}
|
package/lib/makeCmdTasks.js
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import cliTruncate from 'cli-truncate'
|
|
2
1
|
import debug from 'debug'
|
|
3
2
|
|
|
4
3
|
import { configurationError } from './messages.js'
|
|
@@ -6,23 +5,6 @@ import { resolveTaskFn } from './resolveTaskFn.js'
|
|
|
6
5
|
|
|
7
6
|
const debugLog = debug('lint-staged:makeCmdTasks')
|
|
8
7
|
|
|
9
|
-
const STDOUT_COLUMNS_DEFAULT = 80
|
|
10
|
-
|
|
11
|
-
const listrPrefixLength = {
|
|
12
|
-
update: ` X `.length, // indented task title where X is a checkmark or a cross (failure)
|
|
13
|
-
verbose: `[STARTED] `.length, // verbose renderer uses 7-letter STARTED/SUCCESS prefixes
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
/**
|
|
17
|
-
* Get length of title based on the number of available columns prefix length
|
|
18
|
-
* @param {string} renderer The name of the Listr renderer
|
|
19
|
-
* @returns {number}
|
|
20
|
-
*/
|
|
21
|
-
const getTitleLength = (renderer, columns = process.stdout.columns) => {
|
|
22
|
-
const prefixLength = listrPrefixLength[renderer] || 0
|
|
23
|
-
return (columns || STDOUT_COLUMNS_DEFAULT) - prefixLength
|
|
24
|
-
}
|
|
25
|
-
|
|
26
8
|
/**
|
|
27
9
|
* Creates and returns an array of listr tasks which map to the given commands.
|
|
28
10
|
*
|
|
@@ -31,11 +13,10 @@ const getTitleLength = (renderer, columns = process.stdout.columns) => {
|
|
|
31
13
|
* @param {string} options.cwd
|
|
32
14
|
* @param {Array<string>} options.files
|
|
33
15
|
* @param {string} options.gitDir
|
|
34
|
-
* @param {string} options.renderer
|
|
35
16
|
* @param {Boolean} shell
|
|
36
17
|
* @param {Boolean} verbose
|
|
37
18
|
*/
|
|
38
|
-
export const makeCmdTasks = async ({ commands, cwd, files, gitDir,
|
|
19
|
+
export const makeCmdTasks = async ({ commands, cwd, files, gitDir, shell, verbose }) => {
|
|
39
20
|
debugLog('Creating listr tasks for commands %o', commands)
|
|
40
21
|
const commandArray = Array.isArray(commands) ? commands : [commands]
|
|
41
22
|
const cmdTasks = []
|
|
@@ -60,10 +41,8 @@ export const makeCmdTasks = async ({ commands, cwd, files, gitDir, renderer, she
|
|
|
60
41
|
)
|
|
61
42
|
}
|
|
62
43
|
|
|
63
|
-
// Truncate title to single line based on renderer
|
|
64
|
-
const title = cliTruncate(command, getTitleLength(renderer))
|
|
65
44
|
const task = resolveTaskFn({ command, cwd, files, gitDir, isFn, shell, verbose })
|
|
66
|
-
cmdTasks.push({ title, command, task })
|
|
45
|
+
cmdTasks.push({ title: command, command, task })
|
|
67
46
|
}
|
|
68
47
|
}
|
|
69
48
|
|
package/lib/messages.js
CHANGED
|
@@ -1,14 +1,13 @@
|
|
|
1
|
+
import { inspect } from 'node:util'
|
|
2
|
+
|
|
1
3
|
import chalk from 'chalk'
|
|
2
|
-
import inspect from 'object-inspect'
|
|
3
4
|
|
|
4
5
|
import { error, info, warning } from './figures.js'
|
|
5
6
|
|
|
6
7
|
export const configurationError = (opt, helpMsg, value) =>
|
|
7
8
|
`${chalk.redBright(`${error} Validation Error:`)}
|
|
8
9
|
|
|
9
|
-
Invalid value for '${chalk.bold(opt)}': ${chalk.bold(
|
|
10
|
-
inspect(value, { inlineCharacterLimit: Number.POSITIVE_INFINITY })
|
|
11
|
-
)}
|
|
10
|
+
Invalid value for '${chalk.bold(opt)}': ${chalk.bold(inspect(value))}
|
|
12
11
|
|
|
13
12
|
${helpMsg}`
|
|
14
13
|
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Reimplementation of "normalize-path"
|
|
3
|
+
* @see https://github.com/jonschlinkert/normalize-path/blob/52c3a95ebebc2d98c1ad7606cbafa7e658656899/index.js
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
/*!
|
|
7
|
+
* normalize-path <https://github.com/jonschlinkert/normalize-path>
|
|
8
|
+
*
|
|
9
|
+
* Copyright (c) 2014-2018, Jon Schlinkert.
|
|
10
|
+
* Released under the MIT License.
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
import path from 'node:path'
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* A file starting with \\?\
|
|
17
|
+
* @see https://learn.microsoft.com/en-us/windows/win32/fileio/naming-a-file#win32-file-namespaces
|
|
18
|
+
*/
|
|
19
|
+
const WIN32_FILE_NS = '\\\\?\\'
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* A file starting with \\.\
|
|
23
|
+
* @see https://learn.microsoft.com/en-us/windows/win32/fileio/naming-a-file#win32-file-namespaces
|
|
24
|
+
*/
|
|
25
|
+
const WIN32_DEVICE_NS = '\\\\.\\'
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Normalize input file path to use POSIX separators
|
|
29
|
+
* @param {String} input
|
|
30
|
+
* @returns String
|
|
31
|
+
*/
|
|
32
|
+
export const normalizePath = (input) => {
|
|
33
|
+
if (input === path.posix.sep || input === path.win32.sep) {
|
|
34
|
+
return path.posix.sep
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
let normalized = input.split(/[/\\]+/).join(path.posix.sep)
|
|
38
|
+
|
|
39
|
+
/** Handle win32 Namespaced paths by changing e.g. \\.\ to //./ */
|
|
40
|
+
if (input.startsWith(WIN32_FILE_NS) || input.startsWith(WIN32_DEVICE_NS)) {
|
|
41
|
+
normalized = normalized.replace(/^\/(\.|\?)/, '//$1')
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/** Remove trailing slash */
|
|
45
|
+
if (normalized.endsWith(path.posix.sep)) {
|
|
46
|
+
normalized = normalized.slice(0, -1)
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
return normalized
|
|
50
|
+
}
|
package/lib/resolveGitRepo.js
CHANGED
|
@@ -2,10 +2,10 @@ import fs from 'node:fs/promises'
|
|
|
2
2
|
import path from 'node:path'
|
|
3
3
|
|
|
4
4
|
import debug from 'debug'
|
|
5
|
-
import normalize from 'normalize-path'
|
|
6
5
|
|
|
7
6
|
import { execGit } from './execGit.js'
|
|
8
7
|
import { readFile } from './file.js'
|
|
8
|
+
import { normalizePath } from './normalizePath.js'
|
|
9
9
|
|
|
10
10
|
const debugLog = debug('lint-staged:resolveGitRepo')
|
|
11
11
|
|
|
@@ -15,7 +15,7 @@ const debugLog = debug('lint-staged:resolveGitRepo')
|
|
|
15
15
|
*/
|
|
16
16
|
const resolveGitConfigDir = async (gitDir) => {
|
|
17
17
|
// Get the real path in case it's a symlink
|
|
18
|
-
const defaultDir =
|
|
18
|
+
const defaultDir = normalizePath(await fs.realpath(path.join(gitDir, '.git')))
|
|
19
19
|
const stats = await fs.lstat(defaultDir)
|
|
20
20
|
// If .git is a directory, use it
|
|
21
21
|
if (stats.isDirectory()) return defaultDir
|
|
@@ -33,10 +33,10 @@ export const determineGitDir = (cwd, relativeDir) => {
|
|
|
33
33
|
}
|
|
34
34
|
if (relativeDir) {
|
|
35
35
|
// the current working dir is inside the git top-level directory
|
|
36
|
-
return
|
|
36
|
+
return normalizePath(cwd.substring(0, cwd.lastIndexOf(relativeDir)))
|
|
37
37
|
} else {
|
|
38
38
|
// the current working dir is the top-level git directory
|
|
39
|
-
return
|
|
39
|
+
return normalizePath(cwd)
|
|
40
40
|
}
|
|
41
41
|
}
|
|
42
42
|
|
|
@@ -55,9 +55,9 @@ export const resolveGitRepo = async (cwd = process.cwd()) => {
|
|
|
55
55
|
|
|
56
56
|
// read the path of the current directory relative to the top-level directory
|
|
57
57
|
// don't read the toplevel directly, it will lead to an posix conform path on non posix systems (cygwin)
|
|
58
|
-
const gitRel =
|
|
59
|
-
const gitDir = determineGitDir(
|
|
60
|
-
const gitConfigDir =
|
|
58
|
+
const gitRel = normalizePath(await execGit(['rev-parse', '--show-prefix'], { cwd }))
|
|
59
|
+
const gitDir = determineGitDir(normalizePath(cwd), gitRel)
|
|
60
|
+
const gitConfigDir = normalizePath(await resolveGitConfigDir(gitDir))
|
|
61
61
|
|
|
62
62
|
debugLog('Resolved git directory to be `%s`', gitDir)
|
|
63
63
|
debugLog('Resolved git config directory to be `%s`', gitConfigDir)
|
package/lib/runAll.js
CHANGED
|
@@ -5,7 +5,6 @@ import path from 'node:path'
|
|
|
5
5
|
import chalk from 'chalk'
|
|
6
6
|
import debug from 'debug'
|
|
7
7
|
import { Listr } from 'listr2'
|
|
8
|
-
import normalize from 'normalize-path'
|
|
9
8
|
|
|
10
9
|
import { chunkFiles } from './chunkFiles.js'
|
|
11
10
|
import { execGit } from './execGit.js'
|
|
@@ -24,6 +23,7 @@ import {
|
|
|
24
23
|
SKIPPED_GIT_ERROR,
|
|
25
24
|
skippingBackup,
|
|
26
25
|
} from './messages.js'
|
|
26
|
+
import { normalizePath } from './normalizePath.js'
|
|
27
27
|
import { resolveGitRepo } from './resolveGitRepo.js'
|
|
28
28
|
import {
|
|
29
29
|
applyModificationsSkipped,
|
|
@@ -151,7 +151,7 @@ export const runAll = async (
|
|
|
151
151
|
ctx,
|
|
152
152
|
exitOnError: false,
|
|
153
153
|
registerSignalListeners: false,
|
|
154
|
-
...getRenderer({ debug, quiet }),
|
|
154
|
+
...getRenderer({ debug, quiet }, logger),
|
|
155
155
|
}
|
|
156
156
|
|
|
157
157
|
const listrTasks = []
|
|
@@ -160,7 +160,7 @@ export const runAll = async (
|
|
|
160
160
|
const matchedFiles = new Set()
|
|
161
161
|
|
|
162
162
|
for (const [configPath, { config, files }] of Object.entries(filesByConfig)) {
|
|
163
|
-
const configName = configPath ?
|
|
163
|
+
const configName = configPath ? normalizePath(path.relative(cwd, configPath)) : 'Config object'
|
|
164
164
|
|
|
165
165
|
const stagedFileChunks = chunkFiles({ baseDir: gitDir, files, maxArgLength, relative })
|
|
166
166
|
|
|
@@ -182,7 +182,6 @@ export const runAll = async (
|
|
|
182
182
|
cwd: groupCwd,
|
|
183
183
|
files: task.fileList,
|
|
184
184
|
gitDir,
|
|
185
|
-
renderer: listrOptions.renderer,
|
|
186
185
|
shell,
|
|
187
186
|
verbose,
|
|
188
187
|
}).then((subTasks) => {
|
|
@@ -193,7 +192,7 @@ export const runAll = async (
|
|
|
193
192
|
// relative filenames in the entire set.
|
|
194
193
|
const normalizedFile = path.isAbsolute(file)
|
|
195
194
|
? file
|
|
196
|
-
:
|
|
195
|
+
: normalizePath(path.join(groupCwd, file))
|
|
197
196
|
|
|
198
197
|
matchedFiles.add(normalizedFile)
|
|
199
198
|
})
|
package/lib/searchConfigs.js
CHANGED
|
@@ -3,10 +3,10 @@
|
|
|
3
3
|
import path from 'node:path'
|
|
4
4
|
|
|
5
5
|
import debug from 'debug'
|
|
6
|
-
import normalize from 'normalize-path'
|
|
7
6
|
|
|
8
7
|
import { execGit } from './execGit.js'
|
|
9
8
|
import { loadConfig, searchPlaces } from './loadConfig.js'
|
|
9
|
+
import { normalizePath } from './normalizePath.js'
|
|
10
10
|
import { parseGitZOutput } from './parseGitZOutput.js'
|
|
11
11
|
import { validateConfig } from './validateConfig.js'
|
|
12
12
|
|
|
@@ -21,7 +21,7 @@ const numberOfLevels = (file) => file.split('/').length
|
|
|
21
21
|
|
|
22
22
|
const sortDeepestParth = (a, b) => (numberOfLevels(a) > numberOfLevels(b) ? -1 : 1)
|
|
23
23
|
|
|
24
|
-
const isInsideDirectory = (dir) => (file) => file.startsWith(
|
|
24
|
+
const isInsideDirectory = (dir) => (file) => file.startsWith(normalizePath(dir))
|
|
25
25
|
|
|
26
26
|
/**
|
|
27
27
|
* Search all config files from the git repository, preferring those inside `cwd`.
|
|
@@ -68,7 +68,7 @@ export const searchConfigs = async (
|
|
|
68
68
|
|
|
69
69
|
/** Sort possible config files so that deepest is first */
|
|
70
70
|
const possibleConfigFiles = [...cachedFiles, ...otherFiles]
|
|
71
|
-
.map((file) =>
|
|
71
|
+
.map((file) => normalizePath(path.join(gitDir, file)))
|
|
72
72
|
.filter(isInsideDirectory(cwd))
|
|
73
73
|
.sort(sortDeepestParth)
|
|
74
74
|
|
package/lib/validateBraces.js
CHANGED
|
@@ -17,8 +17,8 @@ import { incorrectBraces } from './messages.js'
|
|
|
17
17
|
* braces from user configuration, but this is left to the user (after seeing the warning).
|
|
18
18
|
*
|
|
19
19
|
* @example <caption>Globs with brace expansions</caption>
|
|
20
|
-
* - *.{js,tx}
|
|
21
|
-
* - *.{{j,t}s,css}
|
|
20
|
+
* - *.{js,tx} // expanded as *.js, *.ts
|
|
21
|
+
* - *.{{j,t}s,css} // expanded as *.js, *.ts, *.css
|
|
22
22
|
* - file_{1..10}.css // expanded as file_1.css, file_2.css, …, file_10.css
|
|
23
23
|
*
|
|
24
24
|
* @example <caption>Globs with incorrect brace expansions</caption>
|
|
@@ -28,17 +28,17 @@ import { incorrectBraces } from './messages.js'
|
|
|
28
28
|
* - *.${js} // dollar-sign inhibits expansion, so treated literally
|
|
29
29
|
* - *.{js\,ts} // the comma is escaped, so treated literally
|
|
30
30
|
*/
|
|
31
|
-
export const
|
|
31
|
+
export const INCORRECT_BRACES_REGEXP = /(?<![\\$])({)(?:(?!(?<!\\),|\.\.|\{|\}).)*?(?<!\\)(})/g
|
|
32
32
|
|
|
33
33
|
/**
|
|
34
34
|
* @param {string} pattern
|
|
35
35
|
* @returns {string}
|
|
36
36
|
*/
|
|
37
|
-
const
|
|
37
|
+
const stripIncorrectBraces = (pattern) => {
|
|
38
38
|
let output = `${pattern}`
|
|
39
39
|
let match = null
|
|
40
40
|
|
|
41
|
-
while ((match =
|
|
41
|
+
while ((match = INCORRECT_BRACES_REGEXP.exec(pattern))) {
|
|
42
42
|
const fullMatch = match[0]
|
|
43
43
|
const withoutBraces = fullMatch.replace(/{/, '').replace(/}/, '')
|
|
44
44
|
output = output.replace(fullMatch, withoutBraces)
|
|
@@ -47,6 +47,30 @@ const withoutIncorrectBraces = (pattern) => {
|
|
|
47
47
|
return output
|
|
48
48
|
}
|
|
49
49
|
|
|
50
|
+
/**
|
|
51
|
+
* This RegExp matches "duplicate" opening and closing braces, without any other braces
|
|
52
|
+
* in between, where the duplication is redundant and should be removed.
|
|
53
|
+
*
|
|
54
|
+
* @example *.{{js,ts}} // should just be *.{js,ts}
|
|
55
|
+
*/
|
|
56
|
+
export const DOUBLE_BRACES_REGEXP = /{{[^}{]*}}/
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* @param {string} pattern
|
|
60
|
+
* @returns {string}
|
|
61
|
+
*/
|
|
62
|
+
const stripDoubleBraces = (pattern) => {
|
|
63
|
+
let output = `${pattern}`
|
|
64
|
+
const match = DOUBLE_BRACES_REGEXP.exec(pattern)?.[0]
|
|
65
|
+
|
|
66
|
+
if (match) {
|
|
67
|
+
const withoutBraces = match.replace('{{', '{').replace('}}', '}')
|
|
68
|
+
output = output.replace(match, withoutBraces)
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
return output
|
|
72
|
+
}
|
|
73
|
+
|
|
50
74
|
/**
|
|
51
75
|
* Validate and remove incorrect brace expansions from glob pattern.
|
|
52
76
|
* For example `*.{js}` is incorrect because it doesn't contain a `,` or `..`,
|
|
@@ -57,7 +81,7 @@ const withoutIncorrectBraces = (pattern) => {
|
|
|
57
81
|
* @returns {string}
|
|
58
82
|
*/
|
|
59
83
|
export const validateBraces = (pattern, logger) => {
|
|
60
|
-
const fixedPattern =
|
|
84
|
+
const fixedPattern = stripDoubleBraces(stripIncorrectBraces(pattern))
|
|
61
85
|
|
|
62
86
|
if (fixedPattern !== pattern) {
|
|
63
87
|
logger.warn(incorrectBraces(pattern, fixedPattern))
|
package/lib/validateConfig.js
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
/** @typedef {import('./index').Logger} Logger */
|
|
2
2
|
|
|
3
|
+
import { inspect } from 'node:util'
|
|
4
|
+
|
|
3
5
|
import debug from 'debug'
|
|
4
|
-
import inspect from 'object-inspect'
|
|
5
6
|
|
|
6
7
|
import { configurationError } from './messages.js'
|
|
7
8
|
import { ConfigEmptyError, ConfigFormatError } from './symbols.js'
|
|
@@ -109,7 +110,7 @@ See https://github.com/okonet/lint-staged#configuration.`)
|
|
|
109
110
|
}
|
|
110
111
|
|
|
111
112
|
debugLog('Validated config from `%s`:', configPath)
|
|
112
|
-
debugLog(inspect(config, {
|
|
113
|
+
debugLog(inspect(config, { compact: false }))
|
|
113
114
|
|
|
114
115
|
return validatedConfig
|
|
115
116
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "lint-staged",
|
|
3
|
-
"version": "13.
|
|
3
|
+
"version": "13.3.0",
|
|
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": "^14.
|
|
17
|
+
"node": "^16.14.0 || >=18.0.0"
|
|
18
18
|
},
|
|
19
19
|
"type": "module",
|
|
20
20
|
"bin": "./bin/lint-staged.js",
|
|
@@ -33,37 +33,33 @@
|
|
|
33
33
|
"test:watch": "jest --watch"
|
|
34
34
|
},
|
|
35
35
|
"dependencies": {
|
|
36
|
-
"chalk": "5.
|
|
37
|
-
"
|
|
38
|
-
"
|
|
39
|
-
"
|
|
40
|
-
"execa": "^7.0.0",
|
|
36
|
+
"chalk": "5.3.0",
|
|
37
|
+
"commander": "11.0.0",
|
|
38
|
+
"debug": "4.3.4",
|
|
39
|
+
"execa": "7.2.0",
|
|
41
40
|
"lilconfig": "2.1.0",
|
|
42
|
-
"listr2": "
|
|
43
|
-
"micromatch": "
|
|
44
|
-
"
|
|
45
|
-
"
|
|
46
|
-
"
|
|
47
|
-
"string-argv": "^0.3.1",
|
|
48
|
-
"yaml": "^2.2.2"
|
|
41
|
+
"listr2": "6.6.1",
|
|
42
|
+
"micromatch": "4.0.5",
|
|
43
|
+
"pidtree": "0.6.0",
|
|
44
|
+
"string-argv": "0.3.2",
|
|
45
|
+
"yaml": "2.3.1"
|
|
49
46
|
},
|
|
50
47
|
"devDependencies": {
|
|
51
|
-
"@babel/core": "
|
|
52
|
-
"@babel/eslint-parser": "
|
|
53
|
-
"@babel/preset-env": "
|
|
54
|
-
"babel-jest": "
|
|
48
|
+
"@babel/core": "7.22.10",
|
|
49
|
+
"@babel/eslint-parser": "7.22.10",
|
|
50
|
+
"@babel/preset-env": "7.22.10",
|
|
51
|
+
"babel-jest": "29.6.2",
|
|
55
52
|
"babel-plugin-transform-imports": "2.0.0",
|
|
56
|
-
"consolemock": "
|
|
57
|
-
"eslint": "
|
|
58
|
-
"eslint-config-prettier": "
|
|
59
|
-
"eslint-plugin-import": "
|
|
60
|
-
"eslint-plugin-node": "
|
|
61
|
-
"eslint-plugin-prettier": "
|
|
62
|
-
"
|
|
63
|
-
"
|
|
64
|
-
"jest": "
|
|
65
|
-
"
|
|
66
|
-
"prettier": "^2.8.4"
|
|
53
|
+
"consolemock": "1.1.0",
|
|
54
|
+
"eslint": "8.46.0",
|
|
55
|
+
"eslint-config-prettier": "9.0.0",
|
|
56
|
+
"eslint-plugin-import": "2.28.0",
|
|
57
|
+
"eslint-plugin-node": "11.1.0",
|
|
58
|
+
"eslint-plugin-prettier": "5.0.0",
|
|
59
|
+
"husky": "8.0.3",
|
|
60
|
+
"jest": "29.6.2",
|
|
61
|
+
"jest-snapshot-serializer-ansi": "2.1.0",
|
|
62
|
+
"prettier": "3.0.1"
|
|
67
63
|
},
|
|
68
64
|
"keywords": [
|
|
69
65
|
"lint",
|