lint-staged 15.4.3 → 15.5.1

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 CHANGED
@@ -876,7 +876,7 @@ export default {
876
876
  }
877
877
  ```
878
878
 
879
- To support backwards-compatibility, monorepo features require multiple _lint-staged_ configuration files present in the git repo. If you still want to run _lint-staged_ in only one of the packages in a monorepo, you can either add an "empty" _lint-staged_ configuration to the root of the repo (so that there's two configs in total), or alternatively run _lint-staged_ with the `--cwd` option pointing to your package directory (for example, `lint-staged --cwd packages/frontend`).
879
+ To support backwards-compatibility, monorepo features require multiple _lint-staged_ configuration files present in the git repo. If you still want to run _lint-staged_ in only one of the packages in a monorepo, you can use the `--cwd` option (for example, `lint-staged --cwd packages/frontend`).
880
880
 
881
881
  </details>
882
882
 
@@ -909,15 +909,15 @@ all changed files between two different branches. If you want to run _lint-stage
909
909
  Try out the `git diff` command until you are satisfied with the result, for example:
910
910
 
911
911
  ```
912
- git diff --diff-filter=ACMR --name-only master...my-branch
912
+ git diff --diff-filter=ACMR --name-only main...my-branch
913
913
  ```
914
914
 
915
- This will print a list of _added_, _changed_, _modified_, and _renamed_ files between `master` and `my-branch`.
915
+ This will print a list of _added_, _changed_, _modified_, and _renamed_ files between `main` and `my-branch`.
916
916
 
917
917
  You can then run lint-staged against the same files with:
918
918
 
919
919
  ```
920
- npx lint-staged --diff="master...my-branch"
920
+ npx lint-staged --diff="main...my-branch"
921
921
  ```
922
922
 
923
923
  </details>
@@ -1013,3 +1013,98 @@ ESLint v8.51.0 introduced [`--no-warn-ignored` CLI flag](https://eslint.org/docs
1013
1013
  </details>
1014
1014
 
1015
1015
  </details>
1016
+
1017
+ ### How can I resolve TypeScript (`tsc`) ignoring `tsconfig.json` when `lint-staged` runs via Husky hooks?
1018
+
1019
+ <details>
1020
+ <summary>Click to expand</summary>
1021
+
1022
+ When running `lint-staged` via Husky hooks, TypeScript may ignore `tsconfig.json`, leading to errors like:
1023
+
1024
+ > **TS17004:** Cannot use JSX unless the '--jsx' flag is provided.
1025
+ > **TS1056:** Accessors are only available when targeting ECMAScript 5 and higher.
1026
+
1027
+ See issue [#825](https://github.com/okonet/lint-staged/issues/825) for more details.
1028
+
1029
+ #### Root Cause
1030
+
1031
+ <details>
1032
+ <summary>Click to expand</summary>
1033
+
1034
+ 1. `lint-staged` automatically passes matched staged files as arguments to commands.
1035
+ 2. Certain input files can cause TypeScript to ignore `tsconfig.json`. For more details, see this TypeScript issue: [Allow tsconfig.json when input files are specified](https://github.com/microsoft/TypeScript/issues/27379).
1036
+
1037
+ </details>
1038
+
1039
+ #### Workaround 1: Use a [function signature](https://github.com/lint-staged/lint-staged?tab=readme-ov-file#example-run-tsc-on-changes-to-typescript-files-but-do-not-pass-any-filename-arguments) for the `tsc` command
1040
+
1041
+ <details>
1042
+ <summary>Click to expand</summary>
1043
+
1044
+ As suggested by @antoinerousseau in [#825 (comment)](https://github.com/lint-staged/lint-staged/issues/825#issuecomment-620018284), using a function prevents `lint-staged` from appending file arguments:
1045
+
1046
+ **Before:**
1047
+
1048
+ ```js
1049
+ // package.json
1050
+
1051
+ "lint-staged": {
1052
+ "*.{ts,tsx}":[
1053
+ "tsc --noEmit",
1054
+ "prettier --write"
1055
+ ]
1056
+ }
1057
+ ```
1058
+
1059
+ **After:**
1060
+
1061
+ ```js
1062
+ // lint-staged.config.js
1063
+ module.exports = {
1064
+ "*.{ts,tsx}": [
1065
+ () => "tsc --noEmit",
1066
+ "prettier --write"
1067
+ ],
1068
+ }
1069
+ ```
1070
+
1071
+ </details>
1072
+
1073
+ #### Workaround 2: Take the `sh` or `bash` to wrap the `tsc` command
1074
+
1075
+ <details>
1076
+ <summary>Click to expand</summary>
1077
+
1078
+ As suggested by @sombreroEnPuntas in [#825 (comment)](https://github.com/lint-staged/lint-staged/issues/825#issuecomment-674575655), wrapping `tsc` in a shell command prevents `lint-staged` from modifying its arguments:
1079
+
1080
+ **Before:**
1081
+
1082
+ ```js
1083
+ // package.json
1084
+
1085
+ "lint-staged": {
1086
+ "*.{ts,tsx}":[
1087
+ "tsc --noEmit",
1088
+ "prettier --write"
1089
+ ]
1090
+ }
1091
+ ```
1092
+
1093
+ **After:**
1094
+
1095
+ ```js
1096
+ // package.json
1097
+
1098
+ "lint-staged": {
1099
+ "*.{ts,tsx}":[
1100
+ "bash -c 'tsc --noEmit'"
1101
+ "prettier --write"
1102
+ ]
1103
+ }
1104
+ ```
1105
+
1106
+ **Note:** This approach may have cross-platform compatibility issues.
1107
+
1108
+ </details>
1109
+
1110
+ </details>
@@ -1,4 +1,5 @@
1
- export function getDiffCommand(diff, diffFilter) {
1
+ /** @type {(diff?: string, diffFilter?: string) => string[]} */
2
+ export const getDiffCommand = (diff, diffFilter) => {
2
3
  /**
3
4
  * Docs for --diff-filter option:
4
5
  * @see https://git-scm.com/docs/git-diff#Documentation/git-diff.txt---diff-filterACDMRTUXB82308203
@@ -12,7 +13,7 @@ export function getDiffCommand(diff, diffFilter) {
12
13
  * Docs for -z option:
13
14
  * @see https://git-scm.com/docs/git-diff#Documentation/git-diff.txt--z
14
15
  */
15
- const diffCommand = ['diff', '--name-only', '-z', `--diff-filter=${diffFilterArg}`, ...diffArgs]
16
+ const diffCommand = ['diff', `--diff-filter=${diffFilterArg}`, ...diffArgs]
16
17
 
17
18
  return diffCommand
18
19
  }
@@ -5,21 +5,51 @@ import { getDiffCommand } from './getDiffCommand.js'
5
5
  import { normalizePath } from './normalizePath.js'
6
6
  import { parseGitZOutput } from './parseGitZOutput.js'
7
7
 
8
- const listSubmoduleRoots = async ({ cwd }) => {
9
- const lines = await execGit(['submodule', '--quiet', 'foreach', 'echo $displaypath'], { cwd })
10
- return lines.split('\n').map((file) => normalizePath(path.resolve(cwd, file)))
11
- }
12
-
13
8
  export const getStagedFiles = async ({ cwd = process.cwd(), diff, diffFilter } = {}) => {
14
9
  try {
15
- const lines = await execGit(getDiffCommand(diff, diffFilter), { cwd })
16
- if (!lines) return []
10
+ /**
11
+ * With the raw output lines look like:
12
+ *
13
+ * :000000 100644 0000000 780ccd3\u0000A\u0000.gitmodules\u0000
14
+ * :000000 160000 0000000 1bb568e\u0000A\u0000submodule\u0000
15
+ *
16
+ * @see https://git-scm.com/docs/git-diff#_raw_output_format
17
+ */
18
+ const output = await execGit([...getDiffCommand(diff, diffFilter), '--raw', '-z'], { cwd })
19
+
20
+ if (!output) return []
21
+
22
+ /**
23
+ * Split from all colons and remove the first one, after which lines will look like:
24
+ *
25
+ * 000000 100644 0000000 780ccd3 A\u0000.gitmodules\u0000
26
+ * 000000 160000 0000000 47e5cff A\u0000submodule\u0000
27
+ *
28
+ * where '\u0000' is the NUL character from '-z' option. After that we
29
+ * parse the lines by splitting from NUL, and then split the first
30
+ * part from space. This yields us enough info both filter out submodule
31
+ * roots and get the filename.
32
+ */
33
+ return output
34
+ .split(':')
35
+ .slice(1)
36
+ .map(parseGitZOutput)
37
+ .flatMap(([info, src, dst]) => {
38
+ const [, dstMode, , , ,] = info.split(' ')
39
+
40
+ /**
41
+ * Filter out submodule root directory. "160000" is the object mode for submodules.
42
+ * @see https://github.com/git/git/blob/485f5f863615e670fd97ae40af744e14072cfe18/object.h#L114-L120
43
+ */
44
+ if (dstMode === '160000') {
45
+ return []
46
+ }
17
47
 
18
- const submodules = await listSubmoduleRoots({ cwd })
48
+ /** "dst" exists when moving files, otherwise it's undefined and only "src" exists */
49
+ const filename = dst ?? src
19
50
 
20
- return parseGitZOutput(lines)
21
- .map((file) => normalizePath(path.resolve(cwd, file)))
22
- .filter((file) => !submodules.includes(file))
51
+ return [normalizePath(path.resolve(cwd, filename))]
52
+ })
23
53
  } catch {
24
54
  return null
25
55
  }
@@ -278,7 +278,12 @@ export class GitWorkflow {
278
278
 
279
279
  debugLog('Done adding task modifications to index!')
280
280
 
281
- const stagedFilesAfterAdd = await this.execGit(getDiffCommand(this.diff, this.diffFilter))
281
+ const stagedFilesAfterAdd = await this.execGit([
282
+ ...getDiffCommand(this.diff, this.diffFilter),
283
+ '--name-only',
284
+ '-z',
285
+ ])
286
+
282
287
  if (!stagedFilesAfterAdd && !this.allowEmpty) {
283
288
  // Tasks reverted all staged changes and the commit would be empty
284
289
  // Throw error to stop commit unless `--allow-empty` was used
package/lib/state.js CHANGED
@@ -2,7 +2,6 @@ import EventEmitter from 'events'
2
2
 
3
3
  import { GIT_ERROR, TASK_ERROR } from './messages.js'
4
4
  import {
5
- ApplyEmptyCommitError,
6
5
  GitError,
7
6
  RestoreOriginalStateError,
8
7
  RestoreUnstagedChangesError,
@@ -49,18 +48,11 @@ export const restoreUnstagedChangesSkipped = (ctx) => {
49
48
  }
50
49
 
51
50
  export const restoreOriginalStateEnabled = (ctx) =>
52
- ctx.shouldBackup &&
53
- (ctx.errors.has(TaskError) ||
54
- ctx.errors.has(ApplyEmptyCommitError) ||
55
- ctx.errors.has(RestoreUnstagedChangesError))
51
+ ctx.shouldBackup && (ctx.errors.has(TaskError) || ctx.errors.has(RestoreUnstagedChangesError))
56
52
 
57
53
  export const restoreOriginalStateSkipped = (ctx) => {
58
54
  // Should be skipped in case of unknown git errors
59
- if (
60
- ctx.errors.has(GitError) &&
61
- !ctx.errors.has(ApplyEmptyCommitError) &&
62
- !ctx.errors.has(RestoreUnstagedChangesError)
63
- ) {
55
+ if (ctx.errors.has(GitError) && !ctx.errors.has(RestoreUnstagedChangesError)) {
64
56
  return GIT_ERROR
65
57
  }
66
58
  }
package/package.json CHANGED
@@ -1,9 +1,16 @@
1
1
  {
2
2
  "name": "lint-staged",
3
- "version": "15.4.3",
3
+ "version": "15.5.1",
4
4
  "description": "Lint files staged by git",
5
5
  "license": "MIT",
6
- "repository": "https://github.com/lint-staged/lint-staged",
6
+ "repository": {
7
+ "type": "git",
8
+ "url": "git+https://github.com/lint-staged/lint-staged.git"
9
+ },
10
+ "homepage": "https://github.com/lint-staged/lint-staged#readme",
11
+ "bugs": {
12
+ "url": "https://github.com/lint-staged/lint-staged/issues"
13
+ },
7
14
  "author": "Andrey Okonetchnikov <andrey@okonet.ru>",
8
15
  "maintainers": [
9
16
  "Lufty Wiranda <lufty.wiranda@gmail.com>",
@@ -17,16 +24,14 @@
17
24
  "node": ">=18.12.0"
18
25
  },
19
26
  "type": "module",
20
- "bin": "./bin/lint-staged.js",
27
+ "bin": {
28
+ "lint-staged": "bin/lint-staged.js"
29
+ },
21
30
  "exports": {
22
- ".": {
23
- "default": "./lib/index.js",
24
- "types": "./lib/types.d.ts"
25
- },
31
+ ".": "./lib/index.js",
26
32
  "./bin": "./bin/lint-staged.js",
27
33
  "./package.json": "./package.json"
28
34
  },
29
- "types": "lib/types.d.ts",
30
35
  "files": [
31
36
  "bin",
32
37
  "lib"
@@ -35,7 +40,7 @@
35
40
  "lint": "eslint .",
36
41
  "test": "cross-env NODE_OPTIONS=--experimental-vm-modules npx jest --coverage",
37
42
  "test:watch": "npm run test -- --watch",
38
- "typecheck": "tsc --noEmit --strict test/unit/types.ts",
43
+ "typecheck": "tsc --noEmit --strict test/types/index.ts",
39
44
  "version": "npx changeset version",
40
45
  "postversion": "npm i --package-lock-only && git commit -am \"chore(changeset): release\"",
41
46
  "tag": "npx changeset tag"
@@ -53,26 +58,26 @@
53
58
  "yaml": "^2.7.0"
54
59
  },
55
60
  "devDependencies": {
56
- "@changesets/changelog-github": "0.5.0",
57
- "@changesets/cli": "2.27.11",
58
- "@commitlint/cli": "19.6.1",
59
- "@commitlint/config-conventional": "19.6.0",
60
- "@eslint/js": "9.18.0",
61
+ "@changesets/changelog-github": "0.5.1",
62
+ "@changesets/cli": "2.28.1",
63
+ "@commitlint/cli": "19.8.0",
64
+ "@commitlint/config-conventional": "19.8.0",
65
+ "@eslint/js": "9.22.0",
61
66
  "consolemock": "1.1.0",
62
67
  "cross-env": "7.0.3",
63
- "eslint": "9.18.0",
64
- "eslint-config-prettier": "10.0.1",
68
+ "eslint": "9.22.0",
69
+ "eslint-config-prettier": "10.1.1",
65
70
  "eslint-plugin-jest": "28.11.0",
66
- "eslint-plugin-n": "17.15.1",
71
+ "eslint-plugin-n": "17.16.2",
67
72
  "eslint-plugin-prettier": "5.2.3",
68
73
  "eslint-plugin-simple-import-sort": "12.1.1",
69
74
  "husky": "9.1.7",
70
75
  "jest": "29.7.0",
71
- "jest-snapshot-serializer-ansi": "2.1.0",
76
+ "jest-snapshot-serializer-ansi": "2.2.1",
72
77
  "mock-stdin": "1.0.0",
73
- "prettier": "3.4.2",
74
- "semver": "7.6.3",
75
- "typescript": "5.7.3"
78
+ "prettier": "3.5.3",
79
+ "semver": "7.7.1",
80
+ "typescript": "5.8.2"
76
81
  },
77
82
  "keywords": [
78
83
  "lint",
File without changes