lint-staged 17.0.5 → 17.0.7
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 +17 -0
- package/lib/execGit.js +0 -1
- package/lib/getSpawnedTask.js +0 -1
- package/lib/gitWorkflow.js +47 -20
- package/lib/runAll.js +12 -1
- package/package.json +15 -11
package/README.md
CHANGED
|
@@ -772,6 +772,23 @@ module.exports = {
|
|
|
772
772
|
|
|
773
773
|
## Frequently Asked Questions
|
|
774
774
|
|
|
775
|
+
### How does `lint-staged`'s stashing work?
|
|
776
|
+
|
|
777
|
+
<details>
|
|
778
|
+
<summary>Click to expand</summary>
|
|
779
|
+
|
|
780
|
+
When running `lint-staged` with the default configuration, the following happens:
|
|
781
|
+
|
|
782
|
+
1. The entire original state is backed up in a git stash using `git stash create` and `git stash store`. This leaves all files in the worktree by default — the regular `git stash` command would also remove them. This most probably ignores any untracked files, which is the default behavior of `git stash`.
|
|
783
|
+
1. If some file is "_partially staged_", meaning there's both staged and unstaged changes in the same file, lint-staged will additionally save all of these in a patch file, basically like `git diff --patch >> .git/lint-staged_unstaged.patch`. After this, the unstaged changes to these files are removed from the worktree so that tasks only see the staged changes
|
|
784
|
+
1. After running tasks, any new modifications to the originally staged files are added to the index
|
|
785
|
+
1. After this, any originally unstaged changes to "_partially staged_" files are restored by applying the patch file from step 2.
|
|
786
|
+
1. In case of any errors, the state is reset with `git reset` and the original state restored from the git stash created in step 1.
|
|
787
|
+
|
|
788
|
+
If the process is interrupted at any point, it should be possible to restore changes from the git stash created in step 1. because it's only dropped in the last step, after successfully completing all previous steps in the process.
|
|
789
|
+
|
|
790
|
+
</details>
|
|
791
|
+
|
|
775
792
|
### The output of commit hook looks weird (no colors, duplicate lines, verbose output on Windows, …)
|
|
776
793
|
|
|
777
794
|
<details>
|
package/lib/execGit.js
CHANGED
package/lib/getSpawnedTask.js
CHANGED
package/lib/gitWorkflow.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import crypto from 'node:crypto'
|
|
2
|
+
import fs from 'node:fs/promises'
|
|
2
3
|
import path from 'node:path'
|
|
3
4
|
|
|
4
5
|
import { createDebug } from './debug.js'
|
|
@@ -80,13 +81,23 @@ export class GitWorkflow {
|
|
|
80
81
|
/**
|
|
81
82
|
* @param {Object} opts
|
|
82
83
|
*/
|
|
83
|
-
constructor({
|
|
84
|
+
constructor({
|
|
85
|
+
allowEmpty,
|
|
86
|
+
diff,
|
|
87
|
+
diffFilter,
|
|
88
|
+
failOnChanges,
|
|
89
|
+
gitConfigDir,
|
|
90
|
+
matchedFileChunks,
|
|
91
|
+
topLevelDir,
|
|
92
|
+
}) {
|
|
84
93
|
this.execGit = (args, options = {}) => execGit(args, { ...options, cwd: topLevelDir })
|
|
85
94
|
this.allowEmpty = allowEmpty
|
|
86
95
|
this.diff = diff
|
|
87
96
|
this.diffFilter = diffFilter
|
|
88
97
|
this.gitConfigDir = gitConfigDir
|
|
89
98
|
this.failOnChanges = !!failOnChanges
|
|
99
|
+
/** @type {import('./getStagedFiles.js').StagedFile[][]} */
|
|
100
|
+
this.matchedFileChunks = matchedFileChunks
|
|
90
101
|
this.topLevelDir = topLevelDir
|
|
91
102
|
|
|
92
103
|
/**
|
|
@@ -299,30 +310,46 @@ export class GitWorkflow {
|
|
|
299
310
|
? normalizePath(process.env.GIT_INDEX_FILE)
|
|
300
311
|
: process.env.GIT_INDEX_FILE
|
|
301
312
|
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
313
|
+
/** Needs to be run serially because of locking Git operation */
|
|
314
|
+
for (const files of this.matchedFileChunks) {
|
|
315
|
+
const accessCheckedFiles = await Promise.allSettled(
|
|
316
|
+
files.map(async (f) => {
|
|
317
|
+
if (f.status === 'D') {
|
|
318
|
+
await fs.access(f.filepath)
|
|
319
|
+
return f.filepath // File is no longer deleted and can be added
|
|
320
|
+
} else {
|
|
321
|
+
return f.filepath
|
|
322
|
+
}
|
|
323
|
+
})
|
|
324
|
+
)
|
|
305
325
|
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
await this.execGit(['rev-parse', '--path-format=absolute', '--git-path', 'index.lock'])
|
|
326
|
+
const addableFiles = accessCheckedFiles.flatMap((r) =>
|
|
327
|
+
r.status === 'fulfilled' ? [r.value] : []
|
|
309
328
|
)
|
|
310
329
|
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
* default index, otherwise there will be leftover diff after committing
|
|
315
|
-
*/
|
|
316
|
-
if (activeIndexFile !== defaultIndexLock) {
|
|
317
|
-
debugLog('Updating default Git index lock again: %s', defaultIndexLock)
|
|
330
|
+
debugLog('Updating active Git index: %s', activeIndexFile)
|
|
331
|
+
await this.execGit(['add', '--', ...addableFiles])
|
|
332
|
+
debugLog('Done updating Git index: %s', activeIndexFile)
|
|
318
333
|
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
})
|
|
334
|
+
if (activeIndexFile?.endsWith('.lock')) {
|
|
335
|
+
const defaultIndexLock = normalizePath(
|
|
336
|
+
await this.execGit(['rev-parse', '--path-format=absolute', '--git-path', 'index.lock'])
|
|
337
|
+
)
|
|
324
338
|
|
|
325
|
-
|
|
339
|
+
/**
|
|
340
|
+
* If the active index file is a non-default lockfile, we are committing with a pathspec
|
|
341
|
+
* without having explicitly run `git add`. In this case we need to also update the
|
|
342
|
+
* default index, otherwise there will be leftover diff after committing
|
|
343
|
+
*/
|
|
344
|
+
if (activeIndexFile !== defaultIndexLock) {
|
|
345
|
+
debugLog('Updating default Git index again: %s', defaultIndexLock)
|
|
346
|
+
await this.execGit(['add', '--', ...addableFiles], {
|
|
347
|
+
env: {
|
|
348
|
+
GIT_INDEX_FILE: defaultIndexLock,
|
|
349
|
+
},
|
|
350
|
+
})
|
|
351
|
+
debugLog('Done updating default Git index lock: %s', defaultIndexLock)
|
|
352
|
+
}
|
|
326
353
|
}
|
|
327
354
|
}
|
|
328
355
|
|
package/lib/runAll.js
CHANGED
|
@@ -302,12 +302,23 @@ export const runAll = async (
|
|
|
302
302
|
return ctx
|
|
303
303
|
}
|
|
304
304
|
|
|
305
|
+
// Chunk matched files for better Windows compatibility
|
|
306
|
+
/** @type {import('./getStagedFiles.js').StagedFile[][]} */
|
|
307
|
+
const matchedFileChunks = chunkFiles({
|
|
308
|
+
// matched files are relative to `cwd`, not `topLevelDir`, when `relative` is used
|
|
309
|
+
baseDir: cwd,
|
|
310
|
+
files: Array.from(matchedFiles),
|
|
311
|
+
maxArgLength,
|
|
312
|
+
relative: false,
|
|
313
|
+
})
|
|
314
|
+
|
|
305
315
|
const git = new GitWorkflow({
|
|
306
316
|
allowEmpty,
|
|
307
317
|
diff,
|
|
308
318
|
diffFilter,
|
|
309
319
|
failOnChanges,
|
|
310
320
|
gitConfigDir,
|
|
321
|
+
matchedFileChunks,
|
|
311
322
|
topLevelDir,
|
|
312
323
|
})
|
|
313
324
|
|
|
@@ -328,7 +339,7 @@ export const runAll = async (
|
|
|
328
339
|
skip: () => listrTasks.every((task) => task.skip()),
|
|
329
340
|
},
|
|
330
341
|
{
|
|
331
|
-
title: '
|
|
342
|
+
title: 'Staging changes from tasks...',
|
|
332
343
|
task: (ctx) => git.updateIndex(ctx),
|
|
333
344
|
skip: updateIndexSkipped,
|
|
334
345
|
},
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "lint-staged",
|
|
3
|
-
"version": "17.0.
|
|
3
|
+
"version": "17.0.7",
|
|
4
4
|
"description": "Lint files staged by git",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -18,6 +18,10 @@
|
|
|
18
18
|
"funding": {
|
|
19
19
|
"url": "https://opencollective.com/lint-staged"
|
|
20
20
|
},
|
|
21
|
+
"publishConfig": {
|
|
22
|
+
"provenance": true,
|
|
23
|
+
"access": "public"
|
|
24
|
+
},
|
|
21
25
|
"engines": {
|
|
22
26
|
"node": ">=22.22.1"
|
|
23
27
|
},
|
|
@@ -51,31 +55,31 @@
|
|
|
51
55
|
"listr2": "^10.2.1",
|
|
52
56
|
"picomatch": "^4.0.4",
|
|
53
57
|
"string-argv": "^0.3.2",
|
|
54
|
-
"tinyexec": "^1.
|
|
58
|
+
"tinyexec": "^1.2.4"
|
|
55
59
|
},
|
|
56
60
|
"optionalDependencies": {
|
|
57
|
-
"yaml": "^2.
|
|
61
|
+
"yaml": "^2.9.0"
|
|
58
62
|
},
|
|
59
63
|
"devDependencies": {
|
|
60
64
|
"@changesets/changelog-github": "0.7.0",
|
|
61
65
|
"@changesets/cli": "2.31.0",
|
|
62
|
-
"@commitlint/cli": "21.0.
|
|
63
|
-
"@commitlint/config-conventional": "21.0.
|
|
66
|
+
"@commitlint/cli": "21.0.2",
|
|
67
|
+
"@commitlint/config-conventional": "21.0.2",
|
|
64
68
|
"@eslint/js": "10.0.1",
|
|
65
|
-
"@vitest/coverage-istanbul": "4.1.
|
|
66
|
-
"@vitest/eslint-plugin": "1.6.
|
|
69
|
+
"@vitest/coverage-istanbul": "4.1.7",
|
|
70
|
+
"@vitest/eslint-plugin": "1.6.18",
|
|
67
71
|
"consolemock": "1.1.0",
|
|
68
72
|
"cross-env": "10.1.0",
|
|
69
|
-
"eslint": "10.
|
|
73
|
+
"eslint": "10.4.1",
|
|
70
74
|
"eslint-config-prettier": "10.1.8",
|
|
71
75
|
"eslint-plugin-n": "18.0.1",
|
|
72
|
-
"eslint-plugin-prettier": "5.5.
|
|
76
|
+
"eslint-plugin-prettier": "5.5.6",
|
|
73
77
|
"eslint-plugin-simple-import-sort": "13.0.0",
|
|
74
78
|
"husky": "9.1.7",
|
|
75
79
|
"mock-stdin": "1.0.0",
|
|
76
80
|
"prettier": "3.8.3",
|
|
77
|
-
"semver": "7.8.
|
|
78
|
-
"vitest": "4.1.
|
|
81
|
+
"semver": "7.8.1",
|
|
82
|
+
"vitest": "4.1.7"
|
|
79
83
|
},
|
|
80
84
|
"keywords": [
|
|
81
85
|
"lint",
|