lint-staged 13.0.0 → 13.0.3
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 +47 -8
- package/lib/getDiffCommand.js +18 -0
- package/lib/getStagedFiles.js +2 -17
- package/lib/gitWorkflow.js +16 -4
- package/lib/resolveTaskFn.js +40 -21
- package/lib/runAll.js +19 -12
- package/lib/state.js +3 -0
- package/package.json +6 -6
package/README.md
CHANGED
|
@@ -2,6 +2,10 @@
|
|
|
2
2
|
|
|
3
3
|
Run linters against staged git files and don't let :poop: slip into your code base!
|
|
4
4
|
|
|
5
|
+
```bash
|
|
6
|
+
npm install --save-dev lint-staged # requires further setup
|
|
7
|
+
```
|
|
8
|
+
|
|
5
9
|
```
|
|
6
10
|
$ git commit
|
|
7
11
|
|
|
@@ -44,13 +48,17 @@ This project contains a script that will run arbitrary shell tasks with a list o
|
|
|
44
48
|
|
|
45
49
|
## Installation and setup
|
|
46
50
|
|
|
47
|
-
|
|
51
|
+
To install _lint-staged_ in the recommended way, you need to:
|
|
48
52
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
53
|
+
1. Install _lint-staged_ itself:
|
|
54
|
+
- `npm install --save-dev lint-staged`
|
|
55
|
+
1. Set up the `pre-commit` git hook to run _lint-staged_
|
|
56
|
+
- [Husky](https://github.com/typicode/husky) is a popular choice for configuring git hooks
|
|
57
|
+
- Read more about git hooks [here](https://git-scm.com/book/en/v2/Customizing-Git-Git-Hooks)
|
|
58
|
+
1. Install some linters, like [ESLint](https://eslint.org) or [Prettier](https://prettier.io)
|
|
59
|
+
1. Configure _lint-staged_ to run linters and other tasks:
|
|
60
|
+
- for example: `{ "*.js": "eslint" }` to run ESLint for all staged JS files
|
|
61
|
+
- See [Configuration](#Configuration) for more info
|
|
54
62
|
|
|
55
63
|
Don't forget to commit changes to `package.json` and `.husky` to share this setup with your team!
|
|
56
64
|
|
|
@@ -110,7 +118,7 @@ Options:
|
|
|
110
118
|
```
|
|
111
119
|
|
|
112
120
|
- **`--allow-empty`**: By default, when linter tasks undo all staged changes, lint-staged will exit with an error and abort the commit. Use this flag to allow creating empty git commits.
|
|
113
|
-
- **`--concurrent [number|boolean]`**: Controls the concurrency of tasks being run by lint-staged. **NOTE**: This does NOT affect the concurrency of subtasks (they will always be run sequentially). Possible values are:
|
|
121
|
+
- **`--concurrent [number|boolean]`**: Controls the [concurrency of tasks](#task-concurrency) being run by lint-staged. **NOTE**: This does NOT affect the concurrency of subtasks (they will always be run sequentially). Possible values are:
|
|
114
122
|
- `false`: Run all tasks serially
|
|
115
123
|
- `true` (default) : _Infinite_ concurrency. Runs as many tasks in parallel as possible.
|
|
116
124
|
- `{number}`: Run the specified number of tasks in parallel, where `1` is equivalent to `false`.
|
|
@@ -130,7 +138,7 @@ Options:
|
|
|
130
138
|
|
|
131
139
|
## Configuration
|
|
132
140
|
|
|
133
|
-
|
|
141
|
+
_Lint-staged_ can be configured in many ways:
|
|
134
142
|
|
|
135
143
|
- `lint-staged` object in your `package.json`
|
|
136
144
|
- `.lintstagedrc` file in JSON or YML format, or you can be explicit with the file extension:
|
|
@@ -173,6 +181,37 @@ So, considering you did `git add file1.ext file2.ext`, lint-staged will run the
|
|
|
173
181
|
|
|
174
182
|
`your-cmd file1.ext file2.ext`
|
|
175
183
|
|
|
184
|
+
### Task concurrency
|
|
185
|
+
|
|
186
|
+
By default _lint-staged_ will run configured tasks concurrently. This means that for every glob, all the commands will be started at the same time. With the following config, both `eslint` and `prettier` will run at the same time:
|
|
187
|
+
|
|
188
|
+
```json
|
|
189
|
+
{
|
|
190
|
+
"*.ts": "eslint",
|
|
191
|
+
"*.md": "prettier --list-different"
|
|
192
|
+
}
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
This is typically not a problem since the globs do not overlap, and the commands do not make changes to the files, but only report possible errors (aborting the git commit). If you want to run multiple commands for the same set of files, you can use the array syntax to make sure commands are run in order. In the following example, `prettier` will run for both globs, and in addition `eslint` will run for `*.ts` files _after_ it. Both sets of commands (for each glob) are still started at the same time (but do not overlap).
|
|
196
|
+
|
|
197
|
+
```json
|
|
198
|
+
{
|
|
199
|
+
"*.ts": ["prettier --list-different", "eslint"],
|
|
200
|
+
"*.md": "prettier --list-different"
|
|
201
|
+
}
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
Pay extra attention when the configured globs overlap, and tasks make edits to files. For example, in this configuration `prettier` and `eslint` might try to make changes to the same `*.ts` file at the same time, causing a _race condition_:
|
|
205
|
+
|
|
206
|
+
```json
|
|
207
|
+
{
|
|
208
|
+
"*": "prettier --write",
|
|
209
|
+
"*.ts": "eslint --fix"
|
|
210
|
+
}
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
If necessary, you can limit the concurrency using `--concurrent <number>` or disable it entirely with `--concurrent false`.
|
|
214
|
+
|
|
176
215
|
## Filtering files
|
|
177
216
|
|
|
178
217
|
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:
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
export function getDiffCommand(diff, diffFilter) {
|
|
2
|
+
/**
|
|
3
|
+
* Docs for --diff-filter option:
|
|
4
|
+
* @see https://git-scm.com/docs/git-diff#Documentation/git-diff.txt---diff-filterACDMRTUXB82308203
|
|
5
|
+
*/
|
|
6
|
+
const diffFilterArg = diffFilter !== undefined ? diffFilter.trim() : 'ACMR'
|
|
7
|
+
|
|
8
|
+
/** Use `--diff branch1...branch2` or `--diff="branch1 branch2", or fall back to default staged files */
|
|
9
|
+
const diffArgs = diff !== undefined ? diff.trim().split(' ') : ['--staged']
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Docs for -z option:
|
|
13
|
+
* @see https://git-scm.com/docs/git-diff#Documentation/git-diff.txt--z
|
|
14
|
+
*/
|
|
15
|
+
const diffCommand = ['diff', '--name-only', '-z', `--diff-filter=${diffFilterArg}`, ...diffArgs]
|
|
16
|
+
|
|
17
|
+
return diffCommand
|
|
18
|
+
}
|
package/lib/getStagedFiles.js
CHANGED
|
@@ -3,27 +3,12 @@ import path from 'node:path'
|
|
|
3
3
|
import normalize from 'normalize-path'
|
|
4
4
|
|
|
5
5
|
import { execGit } from './execGit.js'
|
|
6
|
+
import { getDiffCommand } from './getDiffCommand.js'
|
|
6
7
|
import { parseGitZOutput } from './parseGitZOutput.js'
|
|
7
8
|
|
|
8
9
|
export const getStagedFiles = async ({ cwd = process.cwd(), diff, diffFilter } = {}) => {
|
|
9
10
|
try {
|
|
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
|
-
|
|
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
|
-
)
|
|
11
|
+
const lines = await execGit(getDiffCommand(diff, diffFilter), { cwd })
|
|
27
12
|
if (!lines) return []
|
|
28
13
|
|
|
29
14
|
return parseGitZOutput(lines).map((file) => normalize(path.resolve(cwd, file)))
|
package/lib/gitWorkflow.js
CHANGED
|
@@ -4,6 +4,7 @@ import debug from 'debug'
|
|
|
4
4
|
|
|
5
5
|
import { execGit } from './execGit.js'
|
|
6
6
|
import { readFile, unlink, writeFile } from './file.js'
|
|
7
|
+
import { getDiffCommand } from './getDiffCommand.js'
|
|
7
8
|
import {
|
|
8
9
|
GitError,
|
|
9
10
|
RestoreOriginalStateError,
|
|
@@ -42,7 +43,7 @@ const processRenames = (files, includeRenameFrom = true) =>
|
|
|
42
43
|
return flattened
|
|
43
44
|
}, [])
|
|
44
45
|
|
|
45
|
-
const STASH = 'lint-staged automatic backup'
|
|
46
|
+
export const STASH = 'lint-staged automatic backup'
|
|
46
47
|
|
|
47
48
|
const PATCH_UNSTAGED = 'lint-staged_unstaged.patch'
|
|
48
49
|
|
|
@@ -65,12 +66,13 @@ const handleError = (error, ctx, symbol) => {
|
|
|
65
66
|
}
|
|
66
67
|
|
|
67
68
|
export class GitWorkflow {
|
|
68
|
-
constructor({ allowEmpty, gitConfigDir, gitDir, matchedFileChunks }) {
|
|
69
|
+
constructor({ allowEmpty, gitConfigDir, gitDir, matchedFileChunks, diff, diffFilter }) {
|
|
69
70
|
this.execGit = (args, options = {}) => execGit(args, { ...options, cwd: gitDir })
|
|
70
71
|
this.deletedFiles = []
|
|
71
72
|
this.gitConfigDir = gitConfigDir
|
|
72
73
|
this.gitDir = gitDir
|
|
73
|
-
this.
|
|
74
|
+
this.diff = diff
|
|
75
|
+
this.diffFilter = diffFilter
|
|
74
76
|
this.allowEmpty = allowEmpty
|
|
75
77
|
this.matchedFileChunks = matchedFileChunks
|
|
76
78
|
|
|
@@ -101,6 +103,16 @@ export class GitWorkflow {
|
|
|
101
103
|
ctx.errors.add(GetBackupStashError)
|
|
102
104
|
throw new Error('lint-staged automatic backup is missing!')
|
|
103
105
|
}
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* https://github.com/okonet/lint-staged/issues/1121
|
|
109
|
+
* Detect MSYS in login shell mode and escape braces
|
|
110
|
+
* to prevent interpolation
|
|
111
|
+
*/
|
|
112
|
+
if (!!process.env.MSYSTEM && !!process.env.LOGINSHELL) {
|
|
113
|
+
return `refs/stash@\\{${index}\\}`
|
|
114
|
+
}
|
|
115
|
+
|
|
104
116
|
return `refs/stash@{${index}}`
|
|
105
117
|
}
|
|
106
118
|
|
|
@@ -262,7 +274,7 @@ export class GitWorkflow {
|
|
|
262
274
|
|
|
263
275
|
debugLog('Done adding task modifications to index!')
|
|
264
276
|
|
|
265
|
-
const stagedFilesAfterAdd = await this.execGit(
|
|
277
|
+
const stagedFilesAfterAdd = await this.execGit(getDiffCommand(this.diff, this.diffFilter))
|
|
266
278
|
if (!stagedFilesAfterAdd && !this.allowEmpty) {
|
|
267
279
|
// Tasks reverted all staged changes and the commit would be empty
|
|
268
280
|
// Throw error to stop commit unless `--allow-empty` was used
|
package/lib/resolveTaskFn.js
CHANGED
|
@@ -8,7 +8,7 @@ import { error, info } from './figures.js'
|
|
|
8
8
|
import { getInitialState } from './state.js'
|
|
9
9
|
import { TaskError } from './symbols.js'
|
|
10
10
|
|
|
11
|
-
const
|
|
11
|
+
const TASK_ERROR = 'lint-staged:taskError'
|
|
12
12
|
|
|
13
13
|
const debugLog = debug('lint-staged:resolveTaskFn')
|
|
14
14
|
|
|
@@ -46,37 +46,52 @@ const handleOutput = (command, result, ctx, isError = false) => {
|
|
|
46
46
|
}
|
|
47
47
|
}
|
|
48
48
|
|
|
49
|
+
/**
|
|
50
|
+
* Kill an execa process along with all its child processes.
|
|
51
|
+
* @param {execa.ExecaChildProcess<string>} execaProcess
|
|
52
|
+
*/
|
|
53
|
+
const killExecaProcess = async (execaProcess) => {
|
|
54
|
+
try {
|
|
55
|
+
const childPids = await pidTree(execaProcess.pid)
|
|
56
|
+
for (const childPid of childPids) {
|
|
57
|
+
try {
|
|
58
|
+
process.kill(childPid)
|
|
59
|
+
} catch (error) {
|
|
60
|
+
debugLog(`Failed to kill process with pid "%d": %o`, childPid, error)
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
} catch (error) {
|
|
64
|
+
// Suppress "No matching pid found" error. This probably means
|
|
65
|
+
// the process already died before executing.
|
|
66
|
+
debugLog(`Failed to kill process with pid "%d": %o`, execaProcess.pid, error)
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
// The execa process is killed separately in order to get the `KILLED` status.
|
|
70
|
+
execaProcess.kill()
|
|
71
|
+
}
|
|
72
|
+
|
|
49
73
|
/**
|
|
50
74
|
* Interrupts the execution of the execa process that we spawned if
|
|
51
75
|
* another task adds an error to the context.
|
|
52
76
|
*
|
|
53
77
|
* @param {Object} ctx
|
|
54
78
|
* @param {execa.ExecaChildProcess<string>} execaChildProcess
|
|
55
|
-
* @returns {
|
|
79
|
+
* @returns {() => Promise<void>} Function that clears the interval that
|
|
56
80
|
* checks the context.
|
|
57
81
|
*/
|
|
58
82
|
const interruptExecutionOnError = (ctx, execaChildProcess) => {
|
|
59
|
-
let
|
|
60
|
-
|
|
61
|
-
async function loop() {
|
|
62
|
-
if (ctx.errors.size > 0) {
|
|
63
|
-
clearInterval(loopIntervalId)
|
|
64
|
-
|
|
65
|
-
const childPids = await pidTree(execaChildProcess.pid)
|
|
66
|
-
for (const pid of childPids) {
|
|
67
|
-
process.kill(pid)
|
|
68
|
-
}
|
|
83
|
+
let killPromise
|
|
69
84
|
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
}
|
|
85
|
+
const errorListener = async () => {
|
|
86
|
+
killPromise = killExecaProcess(execaChildProcess)
|
|
87
|
+
await killPromise
|
|
74
88
|
}
|
|
75
89
|
|
|
76
|
-
|
|
90
|
+
ctx.events.on(TASK_ERROR, errorListener, { once: true })
|
|
77
91
|
|
|
78
|
-
return () => {
|
|
79
|
-
|
|
92
|
+
return async () => {
|
|
93
|
+
ctx.events.off(TASK_ERROR, errorListener)
|
|
94
|
+
await killPromise
|
|
80
95
|
}
|
|
81
96
|
}
|
|
82
97
|
|
|
@@ -95,6 +110,10 @@ const interruptExecutionOnError = (ctx, execaChildProcess) => {
|
|
|
95
110
|
*/
|
|
96
111
|
const makeErr = (command, result, ctx) => {
|
|
97
112
|
ctx.errors.add(TaskError)
|
|
113
|
+
|
|
114
|
+
// https://nodejs.org/api/events.html#error-events
|
|
115
|
+
ctx.events.emit(TASK_ERROR, TaskError)
|
|
116
|
+
|
|
98
117
|
handleOutput(command, result, ctx, true)
|
|
99
118
|
const tag = getTag(result)
|
|
100
119
|
return new Error(`${redBright(command)} ${dim(`[${tag}]`)}`)
|
|
@@ -111,7 +130,7 @@ const makeErr = (command, result, ctx) => {
|
|
|
111
130
|
* @param {Array<string>} options.files — Filepaths to run the linter task against
|
|
112
131
|
* @param {Boolean} [options.shell] — Whether to skip parsing linter task for better shell support
|
|
113
132
|
* @param {Boolean} [options.verbose] — Always show task verbose
|
|
114
|
-
* @returns {
|
|
133
|
+
* @returns {() => Promise<Array<string>>}
|
|
115
134
|
*/
|
|
116
135
|
export const resolveTaskFn = ({
|
|
117
136
|
command,
|
|
@@ -144,7 +163,7 @@ export const resolveTaskFn = ({
|
|
|
144
163
|
|
|
145
164
|
const quitInterruptCheck = interruptExecutionOnError(ctx, execaChildProcess)
|
|
146
165
|
const result = await execaChildProcess
|
|
147
|
-
quitInterruptCheck()
|
|
166
|
+
await quitInterruptCheck()
|
|
148
167
|
|
|
149
168
|
if (result.failed || result.killed || result.signal != null) {
|
|
150
169
|
throw makeErr(command, result, ctx)
|
package/lib/runAll.js
CHANGED
|
@@ -203,15 +203,15 @@ export const runAll = async (
|
|
|
203
203
|
const fileCount = task.fileList.length
|
|
204
204
|
|
|
205
205
|
return {
|
|
206
|
-
title: `${task.pattern}${dim(
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
exitOnError: true
|
|
214
|
-
|
|
206
|
+
title: `${task.pattern}${dim(
|
|
207
|
+
` — ${fileCount} ${fileCount === 1 ? 'file' : 'files'}`
|
|
208
|
+
)}`,
|
|
209
|
+
task: async (ctx, task) =>
|
|
210
|
+
task.newListr(
|
|
211
|
+
subTasks,
|
|
212
|
+
// Subtasks should not run in parallel, and should exit on error
|
|
213
|
+
{ concurrent: false, exitOnError: true }
|
|
214
|
+
),
|
|
215
215
|
skip: () => {
|
|
216
216
|
// Skip task when no files matched
|
|
217
217
|
if (fileCount === 0) {
|
|
@@ -228,7 +228,7 @@ export const runAll = async (
|
|
|
228
228
|
title:
|
|
229
229
|
`${configName}${dim(` — ${files.length} ${files.length > 1 ? 'files' : 'file'}`)}` +
|
|
230
230
|
(chunkCount > 1 ? dim(` (chunk ${index + 1}/${chunkCount})...`) : ''),
|
|
231
|
-
task: () =>
|
|
231
|
+
task: (ctx, task) => task.newListr(chunkListrTasks, { concurrent, exitOnError: true }),
|
|
232
232
|
skip: () => {
|
|
233
233
|
// Skip if the first step (backup) failed
|
|
234
234
|
if (ctx.errors.has(GitError)) return SKIPPED_GIT_ERROR
|
|
@@ -262,7 +262,14 @@ export const runAll = async (
|
|
|
262
262
|
relative: false,
|
|
263
263
|
})
|
|
264
264
|
|
|
265
|
-
const git = new GitWorkflow({
|
|
265
|
+
const git = new GitWorkflow({
|
|
266
|
+
allowEmpty,
|
|
267
|
+
gitConfigDir,
|
|
268
|
+
gitDir,
|
|
269
|
+
matchedFileChunks,
|
|
270
|
+
diff,
|
|
271
|
+
diffFilter,
|
|
272
|
+
})
|
|
266
273
|
|
|
267
274
|
const runner = new Listr(
|
|
268
275
|
[
|
|
@@ -277,7 +284,7 @@ export const runAll = async (
|
|
|
277
284
|
},
|
|
278
285
|
{
|
|
279
286
|
title: `Running tasks for staged files...`,
|
|
280
|
-
task: () =>
|
|
287
|
+
task: (ctx, task) => task.newListr(listrTasks, { concurrent }),
|
|
281
288
|
skip: () => listrTasks.every((task) => task.skip()),
|
|
282
289
|
},
|
|
283
290
|
{
|
package/lib/state.js
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import EventEmitter from 'events'
|
|
2
|
+
|
|
1
3
|
import { GIT_ERROR, TASK_ERROR } from './messages.js'
|
|
2
4
|
import {
|
|
3
5
|
ApplyEmptyCommitError,
|
|
@@ -11,6 +13,7 @@ export const getInitialState = ({ quiet = false } = {}) => ({
|
|
|
11
13
|
hasPartiallyStagedFiles: null,
|
|
12
14
|
shouldBackup: null,
|
|
13
15
|
errors: new Set([]),
|
|
16
|
+
events: new EventEmitter(),
|
|
14
17
|
output: [],
|
|
15
18
|
quiet,
|
|
16
19
|
})
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "lint-staged",
|
|
3
|
-
"version": "13.0.
|
|
3
|
+
"version": "13.0.3",
|
|
4
4
|
"description": "Lint files staged by git",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": "https://github.com/okonet/lint-staged",
|
|
@@ -33,7 +33,7 @@
|
|
|
33
33
|
},
|
|
34
34
|
"dependencies": {
|
|
35
35
|
"cli-truncate": "^3.1.0",
|
|
36
|
-
"colorette": "^2.0.
|
|
36
|
+
"colorette": "^2.0.17",
|
|
37
37
|
"commander": "^9.3.0",
|
|
38
38
|
"debug": "^4.3.4",
|
|
39
39
|
"execa": "^6.1.0",
|
|
@@ -42,7 +42,7 @@
|
|
|
42
42
|
"micromatch": "^4.0.5",
|
|
43
43
|
"normalize-path": "^3.0.0",
|
|
44
44
|
"object-inspect": "^1.12.2",
|
|
45
|
-
"pidtree": "^0.
|
|
45
|
+
"pidtree": "^0.6.0",
|
|
46
46
|
"string-argv": "^0.3.1",
|
|
47
47
|
"yaml": "^2.1.1"
|
|
48
48
|
},
|
|
@@ -50,17 +50,17 @@
|
|
|
50
50
|
"@babel/core": "^7.18.2",
|
|
51
51
|
"@babel/eslint-parser": "^7.18.2",
|
|
52
52
|
"@babel/preset-env": "^7.18.2",
|
|
53
|
-
"babel-jest": "^28.1.
|
|
53
|
+
"babel-jest": "^28.1.1",
|
|
54
54
|
"babel-plugin-transform-imports": "2.0.0",
|
|
55
55
|
"consolemock": "^1.1.0",
|
|
56
|
-
"eslint": "^8.
|
|
56
|
+
"eslint": "^8.17.0",
|
|
57
57
|
"eslint-config-prettier": "^8.5.0",
|
|
58
58
|
"eslint-plugin-import": "^2.26.0",
|
|
59
59
|
"eslint-plugin-node": "^11.1.0",
|
|
60
60
|
"eslint-plugin-prettier": "^4.0.0",
|
|
61
61
|
"fs-extra": "^10.1.0",
|
|
62
62
|
"husky": "^8.0.1",
|
|
63
|
-
"jest": "^28.1.
|
|
63
|
+
"jest": "^28.1.1",
|
|
64
64
|
"jest-snapshot-serializer-ansi": "^1.0.0",
|
|
65
65
|
"prettier": "^2.6.2"
|
|
66
66
|
},
|