lint-staged 12.4.2 → 13.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/README.md CHANGED
@@ -64,6 +64,10 @@ See [Releases](https://github.com/okonet/lint-staged/releases).
64
64
 
65
65
  ### Migration
66
66
 
67
+ #### v13
68
+
69
+ - 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.
70
+
67
71
  #### v12
68
72
 
69
73
  - Since `v12.0.0` _lint-staged_ is a pure ESM module, so make sure your Node.js version is at least `12.20.0`, `14.13.1`, or `16.0.0`. Read more about ESM modules from the official [Node.js Documentation site here](https://nodejs.org/api/esm.html#introduction).
@@ -92,6 +96,10 @@ Options:
92
96
  -c, --config [path] path to configuration file, or - to read from stdin
93
97
  --cwd [path] run all tasks in specific directory, instead of the current
94
98
  -d, --debug print additional debug information (default: false)
99
+ --diff [string] override the default "--staged" flag of "git diff" to get list of files. Implies
100
+ "--no-stash".
101
+ --diff-filter [string] override the default "--diff-filter=ACMR" flag of "git diff" to get list of files
102
+ --max-arg-length [number] maximum length of the command-line argument string (default: 0)
95
103
  --no-stash disable the backup stash, and do not revert in case of errors
96
104
  -q, --quiet disable lint-staged’s own console output (default: false)
97
105
  -r, --relative pass relative filepaths to tasks (default: false)
@@ -111,6 +119,9 @@ Options:
111
119
  - **`--debug`**: Run in debug mode. When set, it does the following:
112
120
  - uses [debug](https://github.com/visionmedia/debug) internally to log additional information about staged files, commands being executed, location of binaries, etc. Debug logs, which are automatically enabled by passing the flag, can also be enabled by setting the environment variable `$DEBUG` to `lint-staged*`.
113
121
  - uses [`verbose` renderer](https://github.com/SamVerschueren/listr-verbose-renderer) for `listr`; this causes serial, uncoloured output to the terminal, instead of the default (beautified, dynamic) output.
122
+ - **`--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).
123
+ - **`--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).
124
+ - **`--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.
114
125
  - **`--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.
115
126
  - **`--quiet`**: Supress all CLI output, except from tasks.
116
127
  - **`--relative`**: Pass filepaths relative to `process.cwd()` (where `lint-staged` runs) to tasks. Default is `false`.
@@ -594,9 +605,9 @@ const success = await lintStaged({
594
605
  maxArgLength: null,
595
606
  quiet: false,
596
607
  relative: false,
597
- shell: false
608
+ shell: false,
598
609
  stash: true,
599
- verbose: false
610
+ verbose: false,
600
611
  })
601
612
  ```
602
613
 
@@ -713,6 +724,30 @@ Example repo: [sudo-suhas/lint-staged-django-react-demo](https://github.com/sudo
713
724
 
714
725
  </details>
715
726
 
727
+ ### Can I run `lint-staged` in CI, or when there are no staged files?
728
+
729
+ <details>
730
+ <summary>Click to expand</summary>
731
+
732
+ Lint-staged will by default run against files staged in git, and should be run during the git pre-commit hook, for example. It's also possible to override this default behaviour and run against files in a specific diff, for example
733
+ all changed files between two different branches. If you want to run _lint-staged_ in the CI, maybe you can set it up to compare the branch in a _Pull Request_/_Merge Request_ to the target branch.
734
+
735
+ Try out the `git diff` command until you are satisfied with the result, for example:
736
+
737
+ ```
738
+ git diff --diff-filter=ACMR --name-only master...my-branch
739
+ ```
740
+
741
+ This will print a list of _added_, _changed_, _modified_, and _renamed_ files between `master` and `my-branch`.
742
+
743
+ You can then run lint-staged against the same files with:
744
+
745
+ ```
746
+ npx lint-staged --diff="master...my-branch"
747
+ ```
748
+
749
+ </details>
750
+
716
751
  ### Can I use `lint-staged` with `ng lint`
717
752
 
718
753
  <details>
@@ -1,19 +1,19 @@
1
1
  #!/usr/bin/env node
2
2
 
3
- import fs from 'fs'
4
- import path from 'path'
5
- import { fileURLToPath } from 'url'
3
+ import fs from 'node:fs'
4
+ import path from 'node:path'
5
+ import { fileURLToPath } from 'node:url'
6
6
 
7
- import cmdline from 'commander'
7
+ import { isColorSupported } from 'colorette'
8
+ import { Option, program } from 'commander'
8
9
  import debug from 'debug'
9
- import supportsColor from 'supports-color'
10
10
 
11
11
  import lintStaged from '../lib/index.js'
12
12
  import { CONFIG_STDIN_ERROR } from '../lib/messages.js'
13
13
 
14
14
  // Force colors for packages that depend on https://www.npmjs.com/package/supports-color
15
- if (supportsColor.stdout) {
16
- process.env.FORCE_COLOR = supportsColor.stdout.level.toString()
15
+ if (isColorSupported) {
16
+ process.env.FORCE_COLOR = '1'
17
17
  }
18
18
 
19
19
  // Do not terminate main Listr process on SIGINT
@@ -23,50 +23,87 @@ const packageJsonPath = path.join(fileURLToPath(import.meta.url), '../../package
23
23
  const packageJson = JSON.parse(fs.readFileSync(packageJsonPath))
24
24
  const version = packageJson.version
25
25
 
26
- cmdline
27
- .version(version)
28
- .option('--allow-empty', 'allow empty commits when tasks revert all staged changes', false)
29
- .option(
30
- '-p, --concurrent <number|boolean>',
31
- 'the number of tasks to run concurrently, or false for serial',
32
- true
26
+ const debugLog = debug('lint-staged:bin')
27
+ debugLog('Running `lint-staged@%s`', version)
28
+
29
+ const cli = program.version(version)
30
+
31
+ cli.option('--allow-empty', 'allow empty commits when tasks revert all staged changes', false)
32
+
33
+ cli.option(
34
+ '-p, --concurrent <number|boolean>',
35
+ 'the number of tasks to run concurrently, or false for serial',
36
+ true
37
+ )
38
+
39
+ cli.option('-c, --config [path]', 'path to configuration file, or - to read from stdin')
40
+
41
+ cli.option('--cwd [path]', 'run all tasks in specific directory, instead of the current')
42
+
43
+ cli.option('-d, --debug', 'print additional debug information', false)
44
+
45
+ cli.option(
46
+ '--diff [string]',
47
+ 'override the default "--staged" flag of "git diff" to get list of files. Implies "--no-stash".'
48
+ )
49
+
50
+ cli.option(
51
+ '--diff-filter [string]',
52
+ 'override the default "--diff-filter=ACMR" flag of "git diff" to get list of files'
53
+ )
54
+
55
+ cli.option('--max-arg-length [number]', 'maximum length of the command-line argument string', 0)
56
+
57
+ /**
58
+ * We don't want to show the `--stash` flag because it's on by default, and only show the
59
+ * negatable flag `--no-stash` in stead. There seems to be a bug in Commander.js where
60
+ * configuring only the latter won't actually set the default value.
61
+ */
62
+ cli
63
+ .addOption(
64
+ new Option('--stash', 'enable the backup stash, and revert in case of errors')
65
+ .default(true)
66
+ .hideHelp()
33
67
  )
34
- .option('-c, --config [path]', 'path to configuration file, or - to read from stdin')
35
- .option('--cwd [path]', 'run all tasks in specific directory, instead of the current')
36
- .option('-d, --debug', 'print additional debug information', false)
37
- .option('--max-arg-length [number]', 'maximum length of the command-line argument string', 0)
38
- .option('--no-stash', 'disable the backup stash, and do not revert in case of errors', false)
39
- .option('-q, --quiet', 'disable lint-staged’s own console output', false)
40
- .option('-r, --relative', 'pass relative filepaths to tasks', false)
41
- .option('-x, --shell [path]', 'skip parsing of tasks for better shell support', false)
42
- .option(
43
- '-v, --verbose',
44
- 'show task output even when tasks succeed; by default only failed output is shown',
45
- false
68
+ .addOption(
69
+ new Option(
70
+ '--no-stash',
71
+ 'disable the backup stash, and do not revert in case of errors'
72
+ ).default(false)
46
73
  )
47
- .parse(process.argv)
48
74
 
49
- const cmdlineOptions = cmdline.opts()
75
+ cli.option('-q, --quiet', 'disable lint-staged’s own console output', false)
76
+
77
+ cli.option('-r, --relative', 'pass relative filepaths to tasks', false)
78
+
79
+ cli.option('-x, --shell [path]', 'skip parsing of tasks for better shell support', false)
80
+
81
+ cli.option(
82
+ '-v, --verbose',
83
+ 'show task output even when tasks succeed; by default only failed output is shown',
84
+ false
85
+ )
50
86
 
51
- if (cmdlineOptions.debug) {
87
+ const cliOptions = cli.parse(process.argv).opts()
88
+
89
+ if (cliOptions.debug) {
52
90
  debug.enable('lint-staged*')
53
91
  }
54
92
 
55
- const debugLog = debug('lint-staged:bin')
56
- debugLog('Running `lint-staged@%s`', version)
57
-
58
93
  const options = {
59
- allowEmpty: !!cmdlineOptions.allowEmpty,
60
- concurrent: JSON.parse(cmdlineOptions.concurrent),
61
- configPath: cmdlineOptions.config,
62
- cwd: cmdlineOptions.cwd,
63
- debug: !!cmdlineOptions.debug,
64
- maxArgLength: cmdlineOptions.maxArgLength || undefined,
65
- quiet: !!cmdlineOptions.quiet,
66
- relative: !!cmdlineOptions.relative,
67
- shell: cmdlineOptions.shell /* Either a boolean or a string pointing to the shell */,
68
- stash: !!cmdlineOptions.stash, // commander inverts `no-<x>` flags to `!x`
69
- verbose: !!cmdlineOptions.verbose,
94
+ allowEmpty: !!cliOptions.allowEmpty,
95
+ concurrent: JSON.parse(cliOptions.concurrent),
96
+ configPath: cliOptions.config,
97
+ cwd: cliOptions.cwd,
98
+ debug: !!cliOptions.debug,
99
+ diff: cliOptions.diff,
100
+ diffFilter: cliOptions.diffFilter,
101
+ maxArgLength: cliOptions.maxArgLength || undefined,
102
+ quiet: !!cliOptions.quiet,
103
+ relative: !!cliOptions.relative,
104
+ shell: cliOptions.shell /* Either a boolean or a string pointing to the shell */,
105
+ stash: !!cliOptions.stash, // commander inverts `no-<x>` flags to `!x`
106
+ verbose: !!cliOptions.verbose,
70
107
  }
71
108
 
72
109
  debugLog('Options parsed from command-line:', options)
package/lib/chunkFiles.js CHANGED
@@ -1,4 +1,4 @@
1
- import path from 'path'
1
+ import path from 'node:path'
2
2
 
3
3
  import debug from 'debug'
4
4
  import normalize from 'normalize-path'
@@ -1,3 +1,3 @@
1
- import { pathToFileURL } from 'url'
1
+ import { pathToFileURL } from 'node:url'
2
2
 
3
3
  export const dynamicImport = (path) => import(pathToFileURL(path)).then((module) => module.default)
package/lib/execGit.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import debug from 'debug'
2
- import execa from 'execa'
2
+ import { execa } from 'execa'
3
3
 
4
4
  const debugLog = debug('lint-staged:execGit')
5
5
 
package/lib/file.js CHANGED
@@ -1,4 +1,4 @@
1
- import { promises as fs } from 'fs'
1
+ import fs from 'node:fs/promises'
2
2
 
3
3
  import debug from 'debug'
4
4
 
@@ -1,4 +1,4 @@
1
- import path from 'path'
1
+ import path from 'node:path'
2
2
 
3
3
  import debug from 'debug'
4
4
  import micromatch from 'micromatch'
@@ -1,18 +1,29 @@
1
- import path from 'path'
1
+ import path from 'node:path'
2
2
 
3
3
  import normalize from 'normalize-path'
4
4
 
5
5
  import { execGit } from './execGit.js'
6
6
  import { parseGitZOutput } from './parseGitZOutput.js'
7
7
 
8
- export const getStagedFiles = async ({ cwd = process.cwd() } = {}) => {
8
+ export const getStagedFiles = async ({ cwd = process.cwd(), diff, diffFilter } = {}) => {
9
9
  try {
10
- // Docs for --diff-filter option: https://git-scm.com/docs/git-diff#Documentation/git-diff.txt---diff-filterACDMRTUXB82308203
11
- // Docs for -z option: https://git-scm.com/docs/git-diff#Documentation/git-diff.txt--z
12
- const lines = await execGit(['diff', '--staged', '--diff-filter=ACMR', '--name-only', '-z'], {
13
- cwd,
14
- })
10
+ /**
11
+ * Docs for --diff-filter option:
12
+ * @see https://git-scm.com/docs/git-diff#Documentation/git-diff.txt---diff-filterACDMRTUXB82308203
13
+ */
14
+ const diffFilterArg = diffFilter !== undefined ? diffFilter.trim() : 'ACMR'
15
15
 
16
+ /** Use `--diff branch1...branch2` or `--diff="branch1 branch2", or fall back to default staged files */
17
+ const diffArgs = diff !== undefined ? diff.trim().split(' ') : ['--staged']
18
+
19
+ /**
20
+ * Docs for -z option:
21
+ * @see https://git-scm.com/docs/git-diff#Documentation/git-diff.txt--z
22
+ */
23
+ const lines = await execGit(
24
+ ['diff', '--name-only', '-z', `--diff-filter=${diffFilterArg}`, ...diffArgs],
25
+ { cwd }
26
+ )
16
27
  if (!lines) return []
17
28
 
18
29
  return parseGitZOutput(lines).map((file) => normalize(path.resolve(cwd, file)))
@@ -1,4 +1,4 @@
1
- import path from 'path'
1
+ import path from 'node:path'
2
2
 
3
3
  import debug from 'debug'
4
4
 
@@ -1,23 +1,19 @@
1
- import path from 'path'
1
+ import path from 'node:path'
2
2
 
3
3
  import debug from 'debug'
4
4
 
5
- import { ConfigObjectSymbol } from './searchConfigs.js'
6
-
7
5
  const debugLog = debug('lint-staged:groupFilesByConfig')
8
6
 
9
- export const groupFilesByConfig = async ({ configs, files }) => {
7
+ export const groupFilesByConfig = async ({ configs, files, singleConfigMode }) => {
10
8
  debugLog('Grouping %d files by %d configurations', files.length, Object.keys(configs).length)
11
9
 
12
10
  const filesSet = new Set(files)
13
11
  const filesByConfig = {}
14
12
 
15
13
  /** Configs are sorted deepest first by `searchConfigs` */
16
- for (const filepath of Reflect.ownKeys(configs)) {
17
- const config = configs[filepath]
18
-
19
- /** When passed an explicit config object via the Node.js API, skip logic */
20
- if (filepath === ConfigObjectSymbol) {
14
+ for (const [filepath, config] of Object.entries(configs)) {
15
+ /** When passed an explicit config object via the Node.js API‚ or an explicit path, skip logic */
16
+ if (singleConfigMode) {
21
17
  filesByConfig[filepath] = { config, files }
22
18
  break
23
19
  }
package/lib/index.js CHANGED
@@ -49,6 +49,8 @@ const getMaxArgLength = () => {
49
49
  * @param {string} [options.configPath] - Path to configuration file
50
50
  * @param {Object} [options.cwd] - Current working directory
51
51
  * @param {boolean} [options.debug] - Enable debug mode
52
+ * @param {string} [options.diff] - Override the default "--staged" flag of "git diff" to get list of files
53
+ * @param {string} [options.diffFilter] - Override the default "--diff-filter=ACMR" flag of "git diff" to get list of files
52
54
  * @param {number} [options.maxArgLength] - Maximum argument string length
53
55
  * @param {boolean} [options.quiet] - Disable lint-staged’s own console output
54
56
  * @param {boolean} [options.relative] - Pass relative filepaths to tasks
@@ -67,6 +69,8 @@ const lintStaged = async (
67
69
  configPath,
68
70
  cwd,
69
71
  debug = false,
72
+ diff,
73
+ diffFilter,
70
74
  maxArgLength = getMaxArgLength() / 2,
71
75
  quiet = false,
72
76
  relative = false,
@@ -82,29 +86,30 @@ const lintStaged = async (
82
86
  debugLog('Unset GIT_LITERAL_PATHSPECS (was `%s`)', process.env.GIT_LITERAL_PATHSPECS)
83
87
  delete process.env.GIT_LITERAL_PATHSPECS
84
88
 
89
+ const options = {
90
+ allowEmpty,
91
+ concurrent,
92
+ configObject,
93
+ configPath,
94
+ cwd,
95
+ debug,
96
+ diff,
97
+ diffFilter,
98
+ maxArgLength,
99
+ quiet,
100
+ relative,
101
+ shell,
102
+ stash,
103
+ verbose,
104
+ }
105
+
85
106
  try {
86
- const ctx = await runAll(
87
- {
88
- allowEmpty,
89
- concurrent,
90
- configObject,
91
- configPath,
92
- cwd,
93
- debug,
94
- maxArgLength,
95
- quiet,
96
- relative,
97
- shell,
98
- stash,
99
- verbose,
100
- },
101
- logger
102
- )
107
+ const ctx = await runAll(options, logger)
103
108
  debugLog('Tasks were executed successfully!')
104
109
  printTaskOutput(ctx, logger)
105
110
  return true
106
111
  } catch (runAllError) {
107
- if (runAllError && runAllError.ctx && runAllError.ctx.errors) {
112
+ if (runAllError?.ctx?.errors) {
108
113
  const { ctx } = runAllError
109
114
 
110
115
  if (ctx.errors.has(ConfigNotFoundError)) {
package/lib/messages.js CHANGED
@@ -28,8 +28,14 @@ export const NO_STAGED_FILES = `${info} No staged files found.`
28
28
 
29
29
  export const NO_TASKS = `${info} No staged files match any configured task.`
30
30
 
31
- export const skippingBackup = (hasInitialCommit) => {
32
- const reason = hasInitialCommit ? '`--no-stash` was used' : 'there’s no initial commit yet'
31
+ export const skippingBackup = (hasInitialCommit, diff) => {
32
+ const reason =
33
+ diff !== undefined
34
+ ? '`--diff` was used'
35
+ : hasInitialCommit
36
+ ? '`--no-stash` was used'
37
+ : 'there’s no initial commit yet'
38
+
33
39
  return yellow(`${warning} Skipping backup because ${reason}.\n`)
34
40
  }
35
41
 
@@ -5,7 +5,7 @@
5
5
  */
6
6
  export const printTaskOutput = (ctx = {}, logger) => {
7
7
  if (!Array.isArray(ctx.output)) return
8
- const log = ctx.errors && ctx.errors.size > 0 ? logger.error : logger.log
8
+ const log = ctx.errors?.size > 0 ? logger.error : logger.log
9
9
  for (const line of ctx.output) {
10
10
  log(line)
11
11
  }
@@ -1,4 +1,4 @@
1
- import { createRequire } from 'module'
1
+ import { createRequire } from 'node:module'
2
2
 
3
3
  /**
4
4
  * require() does not exist for ESM, so we must create it to use require.resolve().
@@ -1,5 +1,5 @@
1
- import { promises as fs } from 'fs'
2
- import path from 'path'
1
+ import fs from 'node:fs/promises'
2
+ import path from 'node:path'
3
3
 
4
4
  import debug from 'debug'
5
5
  import normalize from 'normalize-path'
@@ -1,5 +1,5 @@
1
1
  import { redBright, dim } from 'colorette'
2
- import execa from 'execa'
2
+ import { execa, execaCommand } from 'execa'
3
3
  import debug from 'debug'
4
4
  import { parseArgsStringToArgv } from 'string-argv'
5
5
  import pidTree from 'pidtree'
@@ -139,7 +139,7 @@ export const resolveTaskFn = ({
139
139
 
140
140
  return async (ctx = getInitialState()) => {
141
141
  const execaChildProcess = shell
142
- ? execa.command(isFn ? command : `${command} ${files.join(' ')}`, execaOptions)
142
+ ? execaCommand(isFn ? command : `${command} ${files.join(' ')}`, execaOptions)
143
143
  : execa(cmd, isFn ? args : args.concat(files), execaOptions)
144
144
 
145
145
  const quitInterruptCheck = interruptExecutionOnError(ctx, execaChildProcess)
package/lib/runAll.js CHANGED
@@ -1,6 +1,6 @@
1
1
  /** @typedef {import('./index').Logger} Logger */
2
2
 
3
- import path from 'path'
3
+ import path from 'node:path'
4
4
 
5
5
  import { dim } from 'colorette'
6
6
  import debug from 'debug'
@@ -36,7 +36,7 @@ import {
36
36
  restoreUnstagedChangesSkipped,
37
37
  } from './state.js'
38
38
  import { GitRepoError, GetStagedFilesError, GitError, ConfigNotFoundError } from './symbols.js'
39
- import { ConfigObjectSymbol, searchConfigs } from './searchConfigs.js'
39
+ import { searchConfigs } from './searchConfigs.js'
40
40
 
41
41
  const debugLog = debug('lint-staged:runAll')
42
42
 
@@ -52,6 +52,8 @@ const createError = (ctx) => Object.assign(new Error('lint-staged failed'), { ct
52
52
  * @param {string} [options.configPath] - Explicit path to a config file
53
53
  * @param {string} [options.cwd] - Current working directory
54
54
  * @param {boolean} [options.debug] - Enable debug mode
55
+ * @param {string} [options.diff] - Override the default "--staged" flag of "git diff" to get list of files
56
+ * @param {string} [options.diffFilter] - Override the default "--diff-filter=ACMR" flag of "git diff" to get list of files
55
57
  * @param {number} [options.maxArgLength] - Maximum argument string length
56
58
  * @param {boolean} [options.quiet] - Disable lint-staged’s own console output
57
59
  * @param {boolean} [options.relative] - Pass relative filepaths to tasks
@@ -69,6 +71,8 @@ export const runAll = async (
69
71
  configPath,
70
72
  cwd,
71
73
  debug = false,
74
+ diff,
75
+ diffFilter,
72
76
  maxArgLength,
73
77
  quiet = false,
74
78
  relative = false,
@@ -100,13 +104,14 @@ export const runAll = async (
100
104
  .then(() => true)
101
105
  .catch(() => false)
102
106
 
103
- // Lint-staged should create a backup stash only when there's an initial commit
104
- ctx.shouldBackup = hasInitialCommit && stash
107
+ // Lint-staged should create a backup stash only when there's an initial commit,
108
+ // and when using the default list of staged files
109
+ ctx.shouldBackup = hasInitialCommit && stash && diff === undefined
105
110
  if (!ctx.shouldBackup) {
106
- logger.warn(skippingBackup(hasInitialCommit))
111
+ logger.warn(skippingBackup(hasInitialCommit, diff))
107
112
  }
108
113
 
109
- const files = await getStagedFiles({ cwd: gitDir })
114
+ const files = await getStagedFiles({ cwd: gitDir, diff, diffFilter })
110
115
  if (!files) {
111
116
  if (!quiet) ctx.output.push(FAILED_GET_STAGED_FILES)
112
117
  ctx.errors.add(GetStagedFilesError)
@@ -121,7 +126,7 @@ export const runAll = async (
121
126
  }
122
127
 
123
128
  const foundConfigs = await searchConfigs({ configObject, configPath, cwd, gitDir }, logger)
124
- const numberOfConfigs = Reflect.ownKeys(foundConfigs).length
129
+ const numberOfConfigs = Object.keys(foundConfigs).length
125
130
 
126
131
  // Throw if no configurations were found
127
132
  if (numberOfConfigs === 0) {
@@ -129,7 +134,11 @@ export const runAll = async (
129
134
  throw createError(ctx, ConfigNotFoundError)
130
135
  }
131
136
 
132
- const filesByConfig = await groupFilesByConfig({ configs: foundConfigs, files })
137
+ const filesByConfig = await groupFilesByConfig({
138
+ configs: foundConfigs,
139
+ files,
140
+ singleConfigMode: configObject || configPath !== undefined,
141
+ })
133
142
 
134
143
  const hasMultipleConfigs = numberOfConfigs > 1
135
144
 
@@ -149,13 +158,8 @@ export const runAll = async (
149
158
  // Set of all staged files that matched a task glob. Values in a set are unique.
150
159
  const matchedFiles = new Set()
151
160
 
152
- for (const configPath of Reflect.ownKeys(filesByConfig)) {
153
- const { config, files } = filesByConfig[configPath]
154
-
155
- const configName =
156
- configPath === ConfigObjectSymbol
157
- ? 'Config object'
158
- : normalize(path.relative(cwd, configPath))
161
+ for (const [configPath, { config, files }] of Object.entries(filesByConfig)) {
162
+ const configName = configPath ? normalize(path.relative(cwd, configPath)) : 'Config object'
159
163
 
160
164
  const stagedFileChunks = chunkFiles({ baseDir: gitDir, files, maxArgLength, relative })
161
165
 
@@ -1,6 +1,6 @@
1
1
  /** @typedef {import('./index').Logger} Logger */
2
2
 
3
- import { basename, join } from 'path'
3
+ import path from 'node:path'
4
4
 
5
5
  import debug from 'debug'
6
6
  import normalize from 'normalize-path'
@@ -15,7 +15,7 @@ const debugLog = debug('lint-staged:searchConfigs')
15
15
  const EXEC_GIT = ['ls-files', '-z', '--full-name']
16
16
 
17
17
  const filterPossibleConfigFiles = (files) =>
18
- files.filter((file) => searchPlaces.includes(basename(file)))
18
+ files.filter((file) => searchPlaces.includes(path.basename(file)))
19
19
 
20
20
  const numberOfLevels = (file) => file.split('/').length
21
21
 
@@ -23,8 +23,6 @@ const sortDeepestParth = (a, b) => (numberOfLevels(a) > numberOfLevels(b) ? -1 :
23
23
 
24
24
  const isInsideDirectory = (dir) => (file) => file.startsWith(normalize(dir))
25
25
 
26
- export const ConfigObjectSymbol = Symbol()
27
-
28
26
  /**
29
27
  * Search all config files from the git repository, preferring those inside `cwd`.
30
28
  *
@@ -46,7 +44,7 @@ export const searchConfigs = async (
46
44
  if (configObject) {
47
45
  debugLog('Using single direct configuration object...')
48
46
 
49
- return { [ConfigObjectSymbol]: validateConfig(configObject, 'config object', logger) }
47
+ return { '': validateConfig(configObject, 'config object', logger) }
50
48
  }
51
49
 
52
50
  // Use only explicit config path instead of discovering multiple
@@ -70,7 +68,7 @@ export const searchConfigs = async (
70
68
 
71
69
  /** Sort possible config files so that deepest is first */
72
70
  const possibleConfigFiles = [...cachedFiles, ...otherFiles]
73
- .map((file) => normalize(join(gitDir, file)))
71
+ .map((file) => normalize(path.join(gitDir, file)))
74
72
  .filter(isInsideDirectory(cwd))
75
73
  .sort(sortDeepestParth)
76
74
 
@@ -1,5 +1,6 @@
1
- import { constants, promises as fs } from 'fs'
2
- import path from 'path'
1
+ import { constants } from 'node:fs'
2
+ import fs from 'node:fs/promises'
3
+ import path from 'node:path'
3
4
 
4
5
  import debug from 'debug'
5
6
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lint-staged",
3
- "version": "12.4.2",
3
+ "version": "13.0.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": "^12.20.0 || ^14.13.1 || >=16.0.0"
17
+ "node": "^14.13.1 || >=16.0.0"
18
18
  },
19
19
  "type": "module",
20
20
  "bin": "./bin/lint-staged.js",
@@ -34,35 +34,35 @@
34
34
  "dependencies": {
35
35
  "cli-truncate": "^3.1.0",
36
36
  "colorette": "^2.0.16",
37
- "commander": "^8.3.0",
38
- "debug": "^4.3.3",
39
- "execa": "^5.1.1",
40
- "lilconfig": "2.0.4",
41
- "listr2": "^4.0.1",
42
- "micromatch": "^4.0.4",
37
+ "commander": "^9.3.0",
38
+ "debug": "^4.3.4",
39
+ "execa": "^6.1.0",
40
+ "lilconfig": "2.0.5",
41
+ "listr2": "^4.0.5",
42
+ "micromatch": "^4.0.5",
43
43
  "normalize-path": "^3.0.0",
44
- "object-inspect": "^1.12.0",
44
+ "object-inspect": "^1.12.2",
45
45
  "pidtree": "^0.5.0",
46
46
  "string-argv": "^0.3.1",
47
- "supports-color": "^9.2.1",
48
- "yaml": "^1.10.2"
47
+ "yaml": "^2.1.1"
49
48
  },
50
49
  "devDependencies": {
51
- "@babel/core": "^7.16.12",
52
- "@babel/eslint-parser": "^7.16.5",
53
- "@babel/preset-env": "^7.16.11",
54
- "babel-jest": "^27.4.6",
50
+ "@babel/core": "^7.18.2",
51
+ "@babel/eslint-parser": "^7.18.2",
52
+ "@babel/preset-env": "^7.18.2",
53
+ "babel-jest": "^28.1.0",
54
+ "babel-plugin-transform-imports": "2.0.0",
55
55
  "consolemock": "^1.1.0",
56
- "eslint": "^8.7.0",
57
- "eslint-config-prettier": "^8.3.0",
58
- "eslint-plugin-import": "^2.25.4",
56
+ "eslint": "^8.16.0",
57
+ "eslint-config-prettier": "^8.5.0",
58
+ "eslint-plugin-import": "^2.26.0",
59
59
  "eslint-plugin-node": "^11.1.0",
60
60
  "eslint-plugin-prettier": "^4.0.0",
61
- "fs-extra": "^10.0.0",
62
- "husky": "^7.0.4",
63
- "jest": "^27.4.7",
61
+ "fs-extra": "^10.1.0",
62
+ "husky": "^8.0.1",
63
+ "jest": "^28.1.0",
64
64
  "jest-snapshot-serializer-ansi": "^1.0.0",
65
- "prettier": "^2.5.1"
65
+ "prettier": "^2.6.2"
66
66
  },
67
67
  "keywords": [
68
68
  "lint",