lint-staged 11.2.4 → 11.3.0-beta.2
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 +5 -6
- package/bin/lint-staged.js +24 -16
- package/lib/gitWorkflow.js +52 -137
- package/lib/index.js +11 -27
- package/lib/messages.js +10 -12
- package/lib/resolveGitRepo.js +2 -2
- package/lib/runAll.js +17 -16
- package/lib/state.js +20 -18
- package/lib/symbols.js +0 -4
- package/lib/unlink.js +22 -0
- package/lib/validateOptions.js +5 -1
- package/package.json +2 -3
- package/lib/file.js +0 -63
package/README.md
CHANGED
|
@@ -65,8 +65,7 @@ Options:
|
|
|
65
65
|
(default: false)
|
|
66
66
|
-c, --config [path] path to configuration file, or - to read from stdin
|
|
67
67
|
-d, --debug print additional debug information (default: false)
|
|
68
|
-
--no-
|
|
69
|
-
errors
|
|
68
|
+
--no-reset do not reset changes in case of errors
|
|
70
69
|
-p, --concurrent <parallel tasks> the number of tasks to run concurrently, or false to run
|
|
71
70
|
tasks serially (default: true)
|
|
72
71
|
-q, --quiet disable lint-staged’s own console output (default: false)
|
|
@@ -87,7 +86,7 @@ Options:
|
|
|
87
86
|
- `false`: Run all tasks serially
|
|
88
87
|
- `true` (default) : _Infinite_ concurrency. Runs as many tasks in parallel as possible.
|
|
89
88
|
- `{number}`: Run the specified number of tasks in parallel, where `1` is equivalent to `false`.
|
|
90
|
-
- **`--no-
|
|
89
|
+
- **`--no-reset`**: 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.
|
|
91
90
|
- **`--quiet`**: Supress all CLI output, except from tasks.
|
|
92
91
|
- **`--relative`**: Pass filepaths relative to `process.cwd()` (where `lint-staged` runs) to tasks. Default is `false`.
|
|
93
92
|
- **`--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"`.
|
|
@@ -105,7 +104,7 @@ Starting with v3.1 you can now use different ways of configuring lint-staged:
|
|
|
105
104
|
- `lint-staged.config.js`, `.lintstagedrc.js`, or `.lintstagedrc.cjs` file in JS format
|
|
106
105
|
- Pass a configuration file using the `--config` or `-c` flag
|
|
107
106
|
|
|
108
|
-
See [
|
|
107
|
+
See [cosmiconfig](https://github.com/davidtheclark/cosmiconfig) for more details on what formats are supported.
|
|
109
108
|
|
|
110
109
|
Configuration should be an object where each value is a command to run and its key is a glob pattern to use for this command. This package uses [micromatch](https://github.com/micromatch/micromatch) for glob patterns.
|
|
111
110
|
|
|
@@ -565,8 +564,8 @@ const success = await lintStaged({
|
|
|
565
564
|
maxArgLength: null,
|
|
566
565
|
quiet: false,
|
|
567
566
|
relative: false,
|
|
567
|
+
reset: true,
|
|
568
568
|
shell: false
|
|
569
|
-
stash: true,
|
|
570
569
|
verbose: false
|
|
571
570
|
})
|
|
572
571
|
```
|
|
@@ -583,8 +582,8 @@ const success = await lintStaged({
|
|
|
583
582
|
maxArgLength: null,
|
|
584
583
|
quiet: false,
|
|
585
584
|
relative: false,
|
|
585
|
+
reset: true,
|
|
586
586
|
shell: false,
|
|
587
|
-
stash: true,
|
|
588
587
|
verbose: false,
|
|
589
588
|
})
|
|
590
589
|
```
|
package/bin/lint-staged.js
CHANGED
|
@@ -22,19 +22,22 @@ require('please-upgrade-node')(
|
|
|
22
22
|
})
|
|
23
23
|
)
|
|
24
24
|
|
|
25
|
-
const
|
|
25
|
+
const { Command, Option } = require('commander')
|
|
26
26
|
const debugLib = require('debug')
|
|
27
27
|
const lintStaged = require('../lib')
|
|
28
28
|
const { CONFIG_STDIN_ERROR } = require('../lib/messages')
|
|
29
29
|
|
|
30
30
|
const debug = debugLib('lint-staged:bin')
|
|
31
31
|
|
|
32
|
-
|
|
33
|
-
|
|
32
|
+
const program = new Command('lint-staged')
|
|
33
|
+
|
|
34
|
+
program.version(pkg.version)
|
|
35
|
+
|
|
36
|
+
program
|
|
34
37
|
.option('--allow-empty', 'allow empty commits when tasks revert all staged changes', false)
|
|
35
38
|
.option('-c, --config [path]', 'path to configuration file, or - to read from stdin')
|
|
36
39
|
.option('-d, --debug', 'print additional debug information', false)
|
|
37
|
-
.option('--no-
|
|
40
|
+
.option('--no-reset', 'do not reset changes in case of errors', false)
|
|
38
41
|
.option(
|
|
39
42
|
'-p, --concurrent <parallel tasks>',
|
|
40
43
|
'the number of tasks to run concurrently, or false to run tasks serially',
|
|
@@ -48,9 +51,13 @@ cmdline
|
|
|
48
51
|
'show task output even when tasks succeed; by default only failed output is shown',
|
|
49
52
|
false
|
|
50
53
|
)
|
|
51
|
-
.parse(process.argv)
|
|
52
54
|
|
|
53
|
-
|
|
55
|
+
// Added for backwards-compatibility
|
|
56
|
+
program.addOption(new Option('--no-stash').hideHelp())
|
|
57
|
+
|
|
58
|
+
program.parse(process.argv)
|
|
59
|
+
|
|
60
|
+
if (program.debug) {
|
|
54
61
|
debugLib.enable('lint-staged*')
|
|
55
62
|
}
|
|
56
63
|
|
|
@@ -74,19 +81,20 @@ const getMaxArgLength = () => {
|
|
|
74
81
|
}
|
|
75
82
|
}
|
|
76
83
|
|
|
77
|
-
const
|
|
84
|
+
const programOpts = program.opts()
|
|
78
85
|
|
|
79
86
|
const options = {
|
|
80
|
-
allowEmpty: !!
|
|
81
|
-
concurrent: JSON.parse(
|
|
82
|
-
configPath:
|
|
83
|
-
debug: !!
|
|
87
|
+
allowEmpty: !!programOpts.allowEmpty,
|
|
88
|
+
concurrent: JSON.parse(programOpts.concurrent),
|
|
89
|
+
configPath: programOpts.config,
|
|
90
|
+
debug: !!programOpts.debug,
|
|
84
91
|
maxArgLength: getMaxArgLength() / 2,
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
shell:
|
|
89
|
-
|
|
92
|
+
quiet: !!programOpts.quiet,
|
|
93
|
+
relative: !!programOpts.relative,
|
|
94
|
+
reset: !!programOpts.reset, // commander inverts `no-<x>` flags to `!x`
|
|
95
|
+
shell: programOpts.shell /* Either a boolean or a string pointing to the shell */,
|
|
96
|
+
stash: programOpts.stash /** kept for backwards-compatibility */,
|
|
97
|
+
verbose: !!programOpts.verbose,
|
|
90
98
|
}
|
|
91
99
|
|
|
92
100
|
debug('Options parsed from command-line:', options)
|
package/lib/gitWorkflow.js
CHANGED
|
@@ -1,19 +1,21 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
3
|
const debug = require('debug')('lint-staged:git')
|
|
4
|
+
const fs = require('fs')
|
|
4
5
|
const path = require('path')
|
|
6
|
+
const { promisify } = require('util')
|
|
5
7
|
|
|
6
8
|
const execGit = require('./execGit')
|
|
7
|
-
const { readFile, unlink, writeFile } = require('./file')
|
|
8
9
|
const {
|
|
9
10
|
GitError,
|
|
10
|
-
RestoreOriginalStateError,
|
|
11
11
|
ApplyEmptyCommitError,
|
|
12
|
-
GetBackupStashError,
|
|
13
12
|
HideUnstagedChangesError,
|
|
14
|
-
|
|
13
|
+
RestoreOriginalStateError,
|
|
15
14
|
RestoreUnstagedChangesError,
|
|
16
15
|
} = require('./symbols')
|
|
16
|
+
const unlink = require('./unlink')
|
|
17
|
+
|
|
18
|
+
const fsReadFile = promisify(fs.readFile)
|
|
17
19
|
|
|
18
20
|
const MERGE_HEAD = 'MERGE_HEAD'
|
|
19
21
|
const MERGE_MODE = 'MERGE_MODE'
|
|
@@ -41,9 +43,8 @@ const processRenames = (files, includeRenameFrom = true) =>
|
|
|
41
43
|
return flattened
|
|
42
44
|
}, [])
|
|
43
45
|
|
|
44
|
-
const STASH = 'lint-staged automatic backup'
|
|
45
|
-
|
|
46
46
|
const PATCH_UNSTAGED = 'lint-staged_unstaged.patch'
|
|
47
|
+
const PATCH_PARTIAL = 'lint-staged_partial.patch'
|
|
47
48
|
|
|
48
49
|
const GIT_DIFF_ARGS = [
|
|
49
50
|
'--binary', // support binary files
|
|
@@ -90,69 +91,6 @@ class GitWorkflow {
|
|
|
90
91
|
return path.resolve(this.gitConfigDir, `./${filename}`)
|
|
91
92
|
}
|
|
92
93
|
|
|
93
|
-
/**
|
|
94
|
-
* Get name of backup stash
|
|
95
|
-
*/
|
|
96
|
-
async getBackupStash(ctx) {
|
|
97
|
-
const stashes = await this.execGit(['stash', 'list'])
|
|
98
|
-
const index = stashes.split('\n').findIndex((line) => line.includes(STASH))
|
|
99
|
-
if (index === -1) {
|
|
100
|
-
ctx.errors.add(GetBackupStashError)
|
|
101
|
-
throw new Error('lint-staged automatic backup is missing!')
|
|
102
|
-
}
|
|
103
|
-
return `refs/stash@{${index}}`
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
/**
|
|
107
|
-
* Get a list of unstaged deleted files
|
|
108
|
-
*/
|
|
109
|
-
async getDeletedFiles() {
|
|
110
|
-
debug('Getting deleted files...')
|
|
111
|
-
const lsFiles = await this.execGit(['ls-files', '--deleted'])
|
|
112
|
-
const deletedFiles = lsFiles
|
|
113
|
-
.split('\n')
|
|
114
|
-
.filter(Boolean)
|
|
115
|
-
.map((file) => path.resolve(this.gitDir, file))
|
|
116
|
-
debug('Found deleted files:', deletedFiles)
|
|
117
|
-
return deletedFiles
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
/**
|
|
121
|
-
* Save meta information about ongoing git merge
|
|
122
|
-
*/
|
|
123
|
-
async backupMergeStatus() {
|
|
124
|
-
debug('Backing up merge state...')
|
|
125
|
-
await Promise.all([
|
|
126
|
-
readFile(this.mergeHeadFilename).then((buffer) => (this.mergeHeadBuffer = buffer)),
|
|
127
|
-
readFile(this.mergeModeFilename).then((buffer) => (this.mergeModeBuffer = buffer)),
|
|
128
|
-
readFile(this.mergeMsgFilename).then((buffer) => (this.mergeMsgBuffer = buffer)),
|
|
129
|
-
])
|
|
130
|
-
debug('Done backing up merge state!')
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
/**
|
|
134
|
-
* Restore meta information about ongoing git merge
|
|
135
|
-
*/
|
|
136
|
-
async restoreMergeStatus(ctx) {
|
|
137
|
-
debug('Restoring merge state...')
|
|
138
|
-
try {
|
|
139
|
-
await Promise.all([
|
|
140
|
-
this.mergeHeadBuffer && writeFile(this.mergeHeadFilename, this.mergeHeadBuffer),
|
|
141
|
-
this.mergeModeBuffer && writeFile(this.mergeModeFilename, this.mergeModeBuffer),
|
|
142
|
-
this.mergeMsgBuffer && writeFile(this.mergeMsgFilename, this.mergeMsgBuffer),
|
|
143
|
-
])
|
|
144
|
-
debug('Done restoring merge state!')
|
|
145
|
-
} catch (error) {
|
|
146
|
-
debug('Failed restoring merge state with error:')
|
|
147
|
-
debug(error)
|
|
148
|
-
handleError(
|
|
149
|
-
new Error('Merge state could not be restored due to an error!'),
|
|
150
|
-
ctx,
|
|
151
|
-
RestoreMergeStatusError
|
|
152
|
-
)
|
|
153
|
-
}
|
|
154
|
-
}
|
|
155
|
-
|
|
156
94
|
/**
|
|
157
95
|
* Get a list of all files with both staged and unstaged modifications.
|
|
158
96
|
* Renames have special treatment, since the single status line includes
|
|
@@ -184,44 +122,28 @@ class GitWorkflow {
|
|
|
184
122
|
}
|
|
185
123
|
|
|
186
124
|
/**
|
|
187
|
-
* Create a diff of partially staged files
|
|
125
|
+
* Create a diff of partially staged files.
|
|
188
126
|
*/
|
|
189
127
|
async prepare(ctx) {
|
|
190
128
|
try {
|
|
191
129
|
debug('Backing up original state...')
|
|
192
130
|
|
|
131
|
+
const unstagedPatch = this.getHiddenFilepath(PATCH_UNSTAGED)
|
|
132
|
+
await this.execGit(['diff', ...GIT_DIFF_ARGS, '--output', unstagedPatch])
|
|
133
|
+
|
|
193
134
|
// Get a list of files with bot staged and unstaged changes.
|
|
194
135
|
// Unstaged changes to these files should be hidden before the tasks run.
|
|
195
136
|
this.partiallyStagedFiles = await this.getPartiallyStagedFiles()
|
|
196
137
|
|
|
197
138
|
if (this.partiallyStagedFiles) {
|
|
198
139
|
ctx.hasPartiallyStagedFiles = true
|
|
199
|
-
const
|
|
140
|
+
const partialPatch = this.getHiddenFilepath(PATCH_PARTIAL)
|
|
200
141
|
const files = processRenames(this.partiallyStagedFiles)
|
|
201
|
-
await this.execGit(['diff', ...GIT_DIFF_ARGS, '--output',
|
|
142
|
+
await this.execGit(['diff', ...GIT_DIFF_ARGS, '--output', partialPatch, '--', ...files])
|
|
202
143
|
} else {
|
|
203
144
|
ctx.hasPartiallyStagedFiles = false
|
|
204
145
|
}
|
|
205
146
|
|
|
206
|
-
/**
|
|
207
|
-
* If backup stash should be skipped, no need to continue
|
|
208
|
-
*/
|
|
209
|
-
if (!ctx.shouldBackup) return
|
|
210
|
-
|
|
211
|
-
// When backup is enabled, the revert will clear ongoing merge status.
|
|
212
|
-
await this.backupMergeStatus()
|
|
213
|
-
|
|
214
|
-
// Get a list of unstaged deleted files, because certain bugs might cause them to reappear:
|
|
215
|
-
// - in git versions =< 2.13.0 the `git stash --keep-index` option resurrects deleted files
|
|
216
|
-
// - git stash can't infer RD or MD states correctly, and will lose the deletion
|
|
217
|
-
this.deletedFiles = await this.getDeletedFiles()
|
|
218
|
-
|
|
219
|
-
// Save stash of all staged files.
|
|
220
|
-
// The `stash create` command creates a dangling commit without removing any files,
|
|
221
|
-
// and `stash store` saves it as an actual stash.
|
|
222
|
-
const hash = await this.execGit(['stash', 'create'])
|
|
223
|
-
await this.execGit(['stash', 'store', '--quiet', '--message', STASH, hash])
|
|
224
|
-
|
|
225
147
|
debug('Done backing up original state!')
|
|
226
148
|
} catch (error) {
|
|
227
149
|
handleError(error, ctx)
|
|
@@ -244,13 +166,19 @@ class GitWorkflow {
|
|
|
244
166
|
}
|
|
245
167
|
}
|
|
246
168
|
|
|
247
|
-
/**
|
|
248
|
-
* Applies back task modifications, and unstaged changes hidden in the stash.
|
|
249
|
-
* In case of a merge-conflict retry with 3-way merge.
|
|
250
|
-
*/
|
|
169
|
+
/** Add all task modifications to index for files that were staged before running. */
|
|
251
170
|
async applyModifications(ctx) {
|
|
252
171
|
debug('Adding task modifications to index...')
|
|
253
172
|
|
|
173
|
+
if (ctx.hasInitialCommit) {
|
|
174
|
+
const stagedFilesAfterAdd = await this.execGit(['diff', 'HEAD'])
|
|
175
|
+
if (!stagedFilesAfterAdd && !this.allowEmpty) {
|
|
176
|
+
// Tasks reverted all staged changes and the commit would be empty
|
|
177
|
+
// Throw error to stop commit unless `--allow-empty` was used
|
|
178
|
+
handleError(new Error('Prevented an empty git commit!'), ctx, ApplyEmptyCommitError)
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
|
|
254
182
|
// `matchedFileChunks` includes staged files that lint-staged originally detected and matched against a task.
|
|
255
183
|
// Add only these files so any 3rd-party edits to other files won't be included in the commit.
|
|
256
184
|
// These additions per chunk are run "serially" to prevent race conditions.
|
|
@@ -260,12 +188,25 @@ class GitWorkflow {
|
|
|
260
188
|
}
|
|
261
189
|
|
|
262
190
|
debug('Done adding task modifications to index!')
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
/**
|
|
194
|
+
* Restore original HEAD state in case of errors
|
|
195
|
+
*/
|
|
196
|
+
async restoreOriginalState(ctx) {
|
|
197
|
+
try {
|
|
198
|
+
debug('Restoring original state...')
|
|
199
|
+
await this.execGit(['checkout', '--force', '--', '.'])
|
|
263
200
|
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
201
|
+
const unstagedPatch = this.getHiddenFilepath(PATCH_UNSTAGED)
|
|
202
|
+
const hasContents = !!(await fsReadFile(unstagedPatch)).toString()
|
|
203
|
+
if (hasContents) {
|
|
204
|
+
await this.execGit(['apply', ...GIT_APPLY_ARGS, unstagedPatch])
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
debug('Done restoring original state!')
|
|
208
|
+
} catch (error) {
|
|
209
|
+
handleError(error, ctx, RestoreOriginalStateError)
|
|
269
210
|
}
|
|
270
211
|
}
|
|
271
212
|
|
|
@@ -274,18 +215,18 @@ class GitWorkflow {
|
|
|
274
215
|
* this is probably because of conflicts between new task modifications.
|
|
275
216
|
* 3-way merge usually fixes this, and in case it doesn't we should just give up and throw.
|
|
276
217
|
*/
|
|
277
|
-
async
|
|
218
|
+
async restorePartialChanges(ctx) {
|
|
278
219
|
debug('Restoring unstaged changes...')
|
|
279
|
-
const
|
|
220
|
+
const partialPatch = this.getHiddenFilepath(PATCH_PARTIAL)
|
|
280
221
|
try {
|
|
281
|
-
await this.execGit(['apply', ...GIT_APPLY_ARGS,
|
|
222
|
+
await this.execGit(['apply', ...GIT_APPLY_ARGS, partialPatch])
|
|
282
223
|
} catch (applyError) {
|
|
283
224
|
debug('Error while restoring changes:')
|
|
284
225
|
debug(applyError)
|
|
285
226
|
debug('Retrying with 3-way merge')
|
|
286
227
|
try {
|
|
287
228
|
// Retry with a 3-way merge if normal apply fails
|
|
288
|
-
await this.execGit(['apply', ...GIT_APPLY_ARGS, '--3way',
|
|
229
|
+
await this.execGit(['apply', ...GIT_APPLY_ARGS, '--3way', partialPatch])
|
|
289
230
|
} catch (threeWayApplyError) {
|
|
290
231
|
debug('Error while restoring unstaged changes using 3-way merge:')
|
|
291
232
|
debug(threeWayApplyError)
|
|
@@ -299,40 +240,14 @@ class GitWorkflow {
|
|
|
299
240
|
}
|
|
300
241
|
|
|
301
242
|
/**
|
|
302
|
-
*
|
|
303
|
-
|
|
304
|
-
async restoreOriginalState(ctx) {
|
|
305
|
-
try {
|
|
306
|
-
debug('Restoring original state...')
|
|
307
|
-
await this.execGit(['reset', '--hard', 'HEAD'])
|
|
308
|
-
await this.execGit(['stash', 'apply', '--quiet', '--index', await this.getBackupStash(ctx)])
|
|
309
|
-
|
|
310
|
-
// Restore meta information about ongoing git merge
|
|
311
|
-
await this.restoreMergeStatus(ctx)
|
|
312
|
-
|
|
313
|
-
// If stashing resurrected deleted files, clean them out
|
|
314
|
-
await Promise.all(this.deletedFiles.map((file) => unlink(file)))
|
|
315
|
-
|
|
316
|
-
// Clean out patch
|
|
317
|
-
await unlink(this.getHiddenFilepath(PATCH_UNSTAGED))
|
|
318
|
-
|
|
319
|
-
debug('Done restoring original state!')
|
|
320
|
-
} catch (error) {
|
|
321
|
-
handleError(error, ctx, RestoreOriginalStateError)
|
|
322
|
-
}
|
|
323
|
-
}
|
|
324
|
-
|
|
325
|
-
/**
|
|
326
|
-
* Drop the created stashes after everything has run
|
|
243
|
+
* Drop the created diff file after everything has run.
|
|
244
|
+
* Won't throw if the file has already been deleted (theoretically).
|
|
327
245
|
*/
|
|
328
|
-
async cleanup(
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
} catch (error) {
|
|
334
|
-
handleError(error, ctx)
|
|
335
|
-
}
|
|
246
|
+
async cleanup() {
|
|
247
|
+
debug('Removing temp files...')
|
|
248
|
+
await unlink(this.getHiddenFilepath(PATCH_UNSTAGED))
|
|
249
|
+
await unlink(this.getHiddenFilepath(PATCH_PARTIAL))
|
|
250
|
+
debug('Done removing temp files!')
|
|
336
251
|
}
|
|
337
252
|
}
|
|
338
253
|
|
package/lib/index.js
CHANGED
|
@@ -1,19 +1,13 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
|
-
const {
|
|
4
|
-
const { yaml } = require('js-yaml')
|
|
3
|
+
const { cosmiconfig } = require('cosmiconfig')
|
|
5
4
|
const debugLog = require('debug')('lint-staged')
|
|
6
5
|
const stringifyObject = require('stringify-object')
|
|
7
6
|
|
|
8
|
-
const { PREVENTED_EMPTY_COMMIT, GIT_ERROR
|
|
7
|
+
const { PREVENTED_EMPTY_COMMIT, GIT_ERROR } = require('./messages')
|
|
9
8
|
const printTaskOutput = require('./printTaskOutput')
|
|
10
9
|
const runAll = require('./runAll')
|
|
11
|
-
const {
|
|
12
|
-
ApplyEmptyCommitError,
|
|
13
|
-
ConfigNotFoundError,
|
|
14
|
-
GetBackupStashError,
|
|
15
|
-
GitError,
|
|
16
|
-
} = require('./symbols')
|
|
10
|
+
const { ApplyEmptyCommitError, ConfigNotFoundError, GitError } = require('./symbols')
|
|
17
11
|
const validateConfig = require('./validateConfig')
|
|
18
12
|
const validateOptions = require('./validateOptions')
|
|
19
13
|
|
|
@@ -25,10 +19,8 @@ const resolveConfig = (configPath) => {
|
|
|
25
19
|
}
|
|
26
20
|
}
|
|
27
21
|
|
|
28
|
-
const jsYamlLoad = (filepath, content) => yaml.load(content)
|
|
29
|
-
|
|
30
22
|
const loadConfig = (configPath) => {
|
|
31
|
-
const explorer =
|
|
23
|
+
const explorer = cosmiconfig('lint-staged', {
|
|
32
24
|
searchPlaces: [
|
|
33
25
|
'package.json',
|
|
34
26
|
'.lintstagedrc',
|
|
@@ -40,11 +32,6 @@ const loadConfig = (configPath) => {
|
|
|
40
32
|
'lint-staged.config.js',
|
|
41
33
|
'lint-staged.config.cjs',
|
|
42
34
|
],
|
|
43
|
-
loaders: {
|
|
44
|
-
'.yml': jsYamlLoad,
|
|
45
|
-
'.yaml': jsYamlLoad,
|
|
46
|
-
noExt: jsYamlLoad,
|
|
47
|
-
},
|
|
48
35
|
})
|
|
49
36
|
|
|
50
37
|
return configPath ? explorer.load(resolveConfig(configPath)) : explorer.search()
|
|
@@ -66,8 +53,8 @@ const loadConfig = (configPath) => {
|
|
|
66
53
|
* @param {number} [options.maxArgLength] - Maximum argument string length
|
|
67
54
|
* @param {boolean} [options.quiet] - Disable lint-staged’s own console output
|
|
68
55
|
* @param {boolean} [options.relative] - Pass relative filepaths to tasks
|
|
56
|
+
* @param {boolean} [options.reset] - Reset changes in case of errors
|
|
69
57
|
* @param {boolean|string} [options.shell] - Skip parsing of tasks for better shell support
|
|
70
|
-
* @param {boolean} [options.stash] - Enable the backup stash, and revert in case of errors
|
|
71
58
|
* @param {boolean} [options.verbose] - Show task output even when tasks succeed; by default only failed output is shown
|
|
72
59
|
* @param {Logger} [logger]
|
|
73
60
|
*
|
|
@@ -84,15 +71,16 @@ const lintStaged = async (
|
|
|
84
71
|
maxArgLength,
|
|
85
72
|
quiet = false,
|
|
86
73
|
relative = false,
|
|
74
|
+
reset = true,
|
|
87
75
|
shell = false,
|
|
88
|
-
stash
|
|
76
|
+
stash, // kept for backwards-compatibility
|
|
89
77
|
verbose = false,
|
|
90
78
|
} = {},
|
|
91
79
|
logger = console
|
|
92
80
|
) => {
|
|
93
|
-
await validateOptions({ shell }, logger)
|
|
81
|
+
await validateOptions({ shell, stash }, logger)
|
|
94
82
|
|
|
95
|
-
debugLog('Loading config using `
|
|
83
|
+
debugLog('Loading config using `cosmiconfig`')
|
|
96
84
|
|
|
97
85
|
const resolved = configObject
|
|
98
86
|
? { config: configObject, filepath: '(input)' }
|
|
@@ -134,8 +122,8 @@ const lintStaged = async (
|
|
|
134
122
|
maxArgLength,
|
|
135
123
|
quiet,
|
|
136
124
|
relative,
|
|
125
|
+
reset: reset || !!stash,
|
|
137
126
|
shell,
|
|
138
|
-
stash,
|
|
139
127
|
verbose,
|
|
140
128
|
},
|
|
141
129
|
logger
|
|
@@ -148,12 +136,8 @@ const lintStaged = async (
|
|
|
148
136
|
const { ctx } = runAllError
|
|
149
137
|
if (ctx.errors.has(ApplyEmptyCommitError)) {
|
|
150
138
|
logger.warn(PREVENTED_EMPTY_COMMIT)
|
|
151
|
-
} else if (ctx.errors.has(GitError)
|
|
139
|
+
} else if (ctx.errors.has(GitError)) {
|
|
152
140
|
logger.error(GIT_ERROR)
|
|
153
|
-
if (ctx.shouldBackup) {
|
|
154
|
-
// No sense to show this if the backup stash itself is missing.
|
|
155
|
-
logger.error(RESTORE_STASH_EXAMPLE)
|
|
156
|
-
}
|
|
157
141
|
}
|
|
158
142
|
|
|
159
143
|
printTaskOutput(ctx, logger)
|
package/lib/messages.js
CHANGED
|
@@ -28,9 +28,9 @@ const NO_STAGED_FILES = `${info} No staged files found.`
|
|
|
28
28
|
|
|
29
29
|
const NO_TASKS = `${info} No staged files match any configured task.`
|
|
30
30
|
|
|
31
|
-
const
|
|
32
|
-
const reason = hasInitialCommit ? '`--no-
|
|
33
|
-
return yellow(`${warning}
|
|
31
|
+
const skippingReset = (hasInitialCommit) => {
|
|
32
|
+
const reason = hasInitialCommit ? '`--no-reset` was used' : 'there’s no initial commit yet'
|
|
33
|
+
return yellow(`${warning} Changes won't be reset because ${reason}.\n`)
|
|
34
34
|
}
|
|
35
35
|
|
|
36
36
|
const DEPRECATED_GIT_ADD = yellow(
|
|
@@ -38,6 +38,11 @@ const DEPRECATED_GIT_ADD = yellow(
|
|
|
38
38
|
`
|
|
39
39
|
)
|
|
40
40
|
|
|
41
|
+
const DEPRECATED_NO_STASH = yellow(
|
|
42
|
+
`${warning} The \`--no-stash\` option has been renamed to \`--no-reset\`.
|
|
43
|
+
`
|
|
44
|
+
)
|
|
45
|
+
|
|
41
46
|
const TASK_ERROR = 'Skipped because of errors from tasks.'
|
|
42
47
|
|
|
43
48
|
const SKIPPED_GIT_ERROR = 'Skipped because of previous git error.'
|
|
@@ -57,19 +62,13 @@ const PREVENTED_EMPTY_COMMIT = `
|
|
|
57
62
|
Use the --allow-empty option to continue, or check your task configuration`)}
|
|
58
63
|
`
|
|
59
64
|
|
|
60
|
-
const RESTORE_STASH_EXAMPLE = ` Any lost modifications can be restored from a git stash:
|
|
61
|
-
|
|
62
|
-
> git stash list
|
|
63
|
-
stash@{0}: automatic lint-staged backup
|
|
64
|
-
> git stash apply --index stash@{0}
|
|
65
|
-
`
|
|
66
|
-
|
|
67
65
|
const CONFIG_STDIN_ERROR = 'Error: Could not read config from stdin.'
|
|
68
66
|
|
|
69
67
|
module.exports = {
|
|
70
68
|
CONFIG_STDIN_ERROR,
|
|
71
69
|
configurationError,
|
|
72
70
|
DEPRECATED_GIT_ADD,
|
|
71
|
+
DEPRECATED_NO_STASH,
|
|
73
72
|
FAILED_GET_STAGED_FILES,
|
|
74
73
|
GIT_ERROR,
|
|
75
74
|
incorrectBraces,
|
|
@@ -78,8 +77,7 @@ module.exports = {
|
|
|
78
77
|
NO_TASKS,
|
|
79
78
|
NOT_GIT_REPO,
|
|
80
79
|
PREVENTED_EMPTY_COMMIT,
|
|
81
|
-
RESTORE_STASH_EXAMPLE,
|
|
82
80
|
SKIPPED_GIT_ERROR,
|
|
83
|
-
|
|
81
|
+
skippingReset,
|
|
84
82
|
TASK_ERROR,
|
|
85
83
|
}
|
package/lib/resolveGitRepo.js
CHANGED
|
@@ -7,9 +7,9 @@ const path = require('path')
|
|
|
7
7
|
const { promisify } = require('util')
|
|
8
8
|
|
|
9
9
|
const execGit = require('./execGit')
|
|
10
|
-
const { readFile } = require('./file')
|
|
11
10
|
|
|
12
11
|
const fsLstat = promisify(fs.lstat)
|
|
12
|
+
const fsReadFile = promisify(fs.readFile)
|
|
13
13
|
|
|
14
14
|
/**
|
|
15
15
|
* Resolve path to the .git directory, with special handling for
|
|
@@ -21,7 +21,7 @@ const resolveGitConfigDir = async (gitDir) => {
|
|
|
21
21
|
// If .git is a directory, use it
|
|
22
22
|
if (stats.isDirectory()) return defaultDir
|
|
23
23
|
// Otherwise .git is a file containing path to real location
|
|
24
|
-
const file = (await
|
|
24
|
+
const file = (await fsReadFile(defaultDir)).toString()
|
|
25
25
|
return path.resolve(gitDir, file.replace(/^gitdir: /, '')).trim()
|
|
26
26
|
}
|
|
27
27
|
|
package/lib/runAll.js
CHANGED
|
@@ -19,7 +19,7 @@ const {
|
|
|
19
19
|
NO_STAGED_FILES,
|
|
20
20
|
NO_TASKS,
|
|
21
21
|
SKIPPED_GIT_ERROR,
|
|
22
|
-
|
|
22
|
+
skippingReset,
|
|
23
23
|
} = require('./messages')
|
|
24
24
|
const resolveGitRepo = require('./resolveGitRepo')
|
|
25
25
|
const {
|
|
@@ -30,7 +30,7 @@ const {
|
|
|
30
30
|
hasPartiallyStagedFiles,
|
|
31
31
|
restoreOriginalStateEnabled,
|
|
32
32
|
restoreOriginalStateSkipped,
|
|
33
|
-
|
|
33
|
+
restorePartialChangesSkipped,
|
|
34
34
|
} = require('./state')
|
|
35
35
|
const { GitRepoError, GetStagedFilesError, GitError } = require('./symbols')
|
|
36
36
|
|
|
@@ -48,8 +48,8 @@ const createError = (ctx) => Object.assign(new Error('lint-staged failed'), { ct
|
|
|
48
48
|
* @param {number} [options.maxArgLength] - Maximum argument string length
|
|
49
49
|
* @param {boolean} [options.quiet] - Disable lint-staged’s own console output
|
|
50
50
|
* @param {boolean} [options.relative] - Pass relative filepaths to tasks
|
|
51
|
+
* @param {boolean} [options.reset] - Reset changes in case of errors
|
|
51
52
|
* @param {boolean} [options.shell] - Skip parsing of tasks for better shell support
|
|
52
|
-
* @param {boolean} [options.stash] - Enable the backup stash, and revert in case of errors
|
|
53
53
|
* @param {boolean} [options.verbose] - Show task output even when tasks succeed; by default only failed output is shown
|
|
54
54
|
* @param {Logger} logger
|
|
55
55
|
* @returns {Promise}
|
|
@@ -64,8 +64,8 @@ const runAll = async (
|
|
|
64
64
|
maxArgLength,
|
|
65
65
|
quiet = false,
|
|
66
66
|
relative = false,
|
|
67
|
+
reset = true,
|
|
67
68
|
shell = false,
|
|
68
|
-
stash = true,
|
|
69
69
|
verbose = false,
|
|
70
70
|
},
|
|
71
71
|
logger = console
|
|
@@ -87,10 +87,11 @@ const runAll = async (
|
|
|
87
87
|
.then(() => true)
|
|
88
88
|
.catch(() => false)
|
|
89
89
|
|
|
90
|
-
// Lint-staged should
|
|
91
|
-
ctx.
|
|
92
|
-
|
|
93
|
-
|
|
90
|
+
// Lint-staged should reset changes when errors happen only when there's an initial commit
|
|
91
|
+
ctx.hasInitialCommit = hasInitialCommit
|
|
92
|
+
ctx.shouldReset = hasInitialCommit && reset
|
|
93
|
+
if (!ctx.shouldReset) {
|
|
94
|
+
logger.warn(skippingReset(hasInitialCommit))
|
|
94
95
|
}
|
|
95
96
|
|
|
96
97
|
const files = await getStagedFiles({ cwd: gitDir })
|
|
@@ -225,20 +226,20 @@ const runAll = async (
|
|
|
225
226
|
skip: applyModificationsSkipped,
|
|
226
227
|
},
|
|
227
228
|
{
|
|
228
|
-
title: '
|
|
229
|
-
task: (ctx) => git.restoreUnstagedChanges(ctx),
|
|
230
|
-
enabled: hasPartiallyStagedFiles,
|
|
231
|
-
skip: restoreUnstagedChangesSkipped,
|
|
232
|
-
},
|
|
233
|
-
{
|
|
234
|
-
title: 'Reverting to original state because of errors...',
|
|
229
|
+
title: 'Reverting because of errors...',
|
|
235
230
|
task: (ctx) => git.restoreOriginalState(ctx),
|
|
236
231
|
enabled: restoreOriginalStateEnabled,
|
|
237
232
|
skip: restoreOriginalStateSkipped,
|
|
238
233
|
},
|
|
234
|
+
{
|
|
235
|
+
title: 'Restoring partial changes...',
|
|
236
|
+
task: (ctx) => git.restorePartialChanges(ctx),
|
|
237
|
+
enabled: hasPartiallyStagedFiles,
|
|
238
|
+
skip: restorePartialChangesSkipped,
|
|
239
|
+
},
|
|
239
240
|
{
|
|
240
241
|
title: 'Cleaning up...',
|
|
241
|
-
task: (
|
|
242
|
+
task: () => git.cleanup(),
|
|
242
243
|
enabled: cleanupEnabled,
|
|
243
244
|
skip: cleanupSkipped,
|
|
244
245
|
},
|
package/lib/state.js
CHANGED
|
@@ -4,14 +4,13 @@ const { GIT_ERROR, TASK_ERROR } = require('./messages')
|
|
|
4
4
|
const {
|
|
5
5
|
ApplyEmptyCommitError,
|
|
6
6
|
TaskError,
|
|
7
|
-
RestoreOriginalStateError,
|
|
8
7
|
GitError,
|
|
9
8
|
RestoreUnstagedChangesError,
|
|
10
9
|
} = require('./symbols')
|
|
11
10
|
|
|
12
11
|
const getInitialState = ({ quiet = false } = {}) => ({
|
|
13
12
|
hasPartiallyStagedFiles: null,
|
|
14
|
-
|
|
13
|
+
shouldReset: null,
|
|
15
14
|
errors: new Set([]),
|
|
16
15
|
output: [],
|
|
17
16
|
quiet,
|
|
@@ -21,33 +20,40 @@ const hasPartiallyStagedFiles = (ctx) => ctx.hasPartiallyStagedFiles
|
|
|
21
20
|
|
|
22
21
|
const applyModificationsSkipped = (ctx) => {
|
|
23
22
|
// Always apply back unstaged modifications when skipping backup
|
|
24
|
-
if (!ctx.
|
|
23
|
+
if (!ctx.shouldReset) return false
|
|
24
|
+
|
|
25
25
|
// Should be skipped in case of git errors
|
|
26
26
|
if (ctx.errors.has(GitError)) {
|
|
27
27
|
return GIT_ERROR
|
|
28
28
|
}
|
|
29
|
+
|
|
29
30
|
// Should be skipped when tasks fail
|
|
30
31
|
if (ctx.errors.has(TaskError)) {
|
|
31
32
|
return TASK_ERROR
|
|
32
33
|
}
|
|
33
34
|
}
|
|
34
35
|
|
|
35
|
-
const
|
|
36
|
+
const restorePartialChangesSkipped = (ctx) => {
|
|
37
|
+
// Should be skipped when entire state has already been restored
|
|
38
|
+
if (restoreOriginalStateEnabled(ctx)) {
|
|
39
|
+
return TASK_ERROR
|
|
40
|
+
}
|
|
41
|
+
|
|
36
42
|
// Should be skipped in case of git errors
|
|
37
43
|
if (ctx.errors.has(GitError)) {
|
|
38
44
|
return GIT_ERROR
|
|
39
45
|
}
|
|
40
|
-
|
|
41
|
-
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
const restoreOriginalStateEnabled = (ctx) => {
|
|
49
|
+
if (ctx.shouldReset && ctx.errors.has(TaskError)) {
|
|
42
50
|
return TASK_ERROR
|
|
43
51
|
}
|
|
44
|
-
}
|
|
45
52
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
ctx.errors.has(RestoreUnstagedChangesError))
|
|
53
|
+
if (ctx.errors.has(ApplyEmptyCommitError)) {
|
|
54
|
+
return true
|
|
55
|
+
}
|
|
56
|
+
}
|
|
51
57
|
|
|
52
58
|
const restoreOriginalStateSkipped = (ctx) => {
|
|
53
59
|
// Should be skipped in case of unknown git errors
|
|
@@ -60,7 +66,7 @@ const restoreOriginalStateSkipped = (ctx) => {
|
|
|
60
66
|
}
|
|
61
67
|
}
|
|
62
68
|
|
|
63
|
-
const cleanupEnabled = (ctx) => ctx.
|
|
69
|
+
const cleanupEnabled = (ctx) => ctx.shouldReset
|
|
64
70
|
|
|
65
71
|
const cleanupSkipped = (ctx) => {
|
|
66
72
|
// Should be skipped in case of unknown git errors
|
|
@@ -71,17 +77,13 @@ const cleanupSkipped = (ctx) => {
|
|
|
71
77
|
) {
|
|
72
78
|
return GIT_ERROR
|
|
73
79
|
}
|
|
74
|
-
// Should be skipped when reverting to original state fails
|
|
75
|
-
if (ctx.errors.has(RestoreOriginalStateError)) {
|
|
76
|
-
return GIT_ERROR
|
|
77
|
-
}
|
|
78
80
|
}
|
|
79
81
|
|
|
80
82
|
module.exports = {
|
|
81
83
|
getInitialState,
|
|
82
84
|
hasPartiallyStagedFiles,
|
|
83
85
|
applyModificationsSkipped,
|
|
84
|
-
|
|
86
|
+
restorePartialChangesSkipped,
|
|
85
87
|
restoreOriginalStateEnabled,
|
|
86
88
|
restoreOriginalStateSkipped,
|
|
87
89
|
cleanupEnabled,
|
package/lib/symbols.js
CHANGED
|
@@ -2,28 +2,24 @@
|
|
|
2
2
|
|
|
3
3
|
const ApplyEmptyCommitError = Symbol('ApplyEmptyCommitError')
|
|
4
4
|
const ConfigNotFoundError = new Error('Config could not be found')
|
|
5
|
-
const GetBackupStashError = Symbol('GetBackupStashError')
|
|
6
5
|
const GetStagedFilesError = Symbol('GetStagedFilesError')
|
|
7
6
|
const GitError = Symbol('GitError')
|
|
8
7
|
const GitRepoError = Symbol('GitRepoError')
|
|
9
8
|
const HideUnstagedChangesError = Symbol('HideUnstagedChangesError')
|
|
10
9
|
const InvalidOptionsError = new Error('Invalid Options')
|
|
11
10
|
const RestoreMergeStatusError = Symbol('RestoreMergeStatusError')
|
|
12
|
-
const RestoreOriginalStateError = Symbol('RestoreOriginalStateError')
|
|
13
11
|
const RestoreUnstagedChangesError = Symbol('RestoreUnstagedChangesError')
|
|
14
12
|
const TaskError = Symbol('TaskError')
|
|
15
13
|
|
|
16
14
|
module.exports = {
|
|
17
15
|
ApplyEmptyCommitError,
|
|
18
16
|
ConfigNotFoundError,
|
|
19
|
-
GetBackupStashError,
|
|
20
17
|
GetStagedFilesError,
|
|
21
18
|
GitError,
|
|
22
19
|
GitRepoError,
|
|
23
20
|
InvalidOptionsError,
|
|
24
21
|
HideUnstagedChangesError,
|
|
25
22
|
RestoreMergeStatusError,
|
|
26
|
-
RestoreOriginalStateError,
|
|
27
23
|
RestoreUnstagedChangesError,
|
|
28
24
|
TaskError,
|
|
29
25
|
}
|
package/lib/unlink.js
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const debug = require('debug')('lint-staged:file')
|
|
4
|
+
const fs = require('fs')
|
|
5
|
+
const { promisify } = require('util')
|
|
6
|
+
|
|
7
|
+
const fsUnlink = promisify(fs.unlink)
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Remove a file if it exists
|
|
11
|
+
* @param {String} filename
|
|
12
|
+
*/
|
|
13
|
+
const unlink = async (filename) => {
|
|
14
|
+
debug('Removing file `%s`', filename)
|
|
15
|
+
try {
|
|
16
|
+
await fsUnlink(filename)
|
|
17
|
+
} catch (error) {
|
|
18
|
+
debug("File `%s` doesn't exist, ignoring...", filename)
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
module.exports = unlink
|
package/lib/validateOptions.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
const { promises: fs, constants } = require('fs')
|
|
2
2
|
|
|
3
|
-
const { invalidOption } = require('./messages')
|
|
3
|
+
const { DEPRECATED_NO_STASH, invalidOption } = require('./messages')
|
|
4
4
|
const { InvalidOptionsError } = require('./symbols')
|
|
5
5
|
|
|
6
6
|
const debug = require('debug')('lint-staged:options')
|
|
@@ -25,6 +25,10 @@ const validateOptions = async (options = {}, logger) => {
|
|
|
25
25
|
}
|
|
26
26
|
}
|
|
27
27
|
|
|
28
|
+
if (typeof options.stash === 'boolean') {
|
|
29
|
+
logger.warn(DEPRECATED_NO_STASH)
|
|
30
|
+
}
|
|
31
|
+
|
|
28
32
|
debug('Validated options!')
|
|
29
33
|
}
|
|
30
34
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "lint-staged",
|
|
3
|
-
"version": "11.2
|
|
3
|
+
"version": "11.3.0-beta.2",
|
|
4
4
|
"description": "Lint files staged by git",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": "https://github.com/okonet/lint-staged",
|
|
@@ -30,11 +30,10 @@
|
|
|
30
30
|
"cli-truncate": "2.1.0",
|
|
31
31
|
"colorette": "^1.4.0",
|
|
32
32
|
"commander": "^8.2.0",
|
|
33
|
+
"cosmiconfig": "^7.0.1",
|
|
33
34
|
"debug": "^4.3.2",
|
|
34
35
|
"enquirer": "^2.3.6",
|
|
35
36
|
"execa": "^5.1.1",
|
|
36
|
-
"js-yaml": "^4.1.0",
|
|
37
|
-
"lilconfig": "^2.0.3",
|
|
38
37
|
"listr2": "^3.12.2",
|
|
39
38
|
"micromatch": "^4.0.4",
|
|
40
39
|
"normalize-path": "^3.0.0",
|
package/lib/file.js
DELETED
|
@@ -1,63 +0,0 @@
|
|
|
1
|
-
'use strict'
|
|
2
|
-
|
|
3
|
-
const debug = require('debug')('lint-staged:file')
|
|
4
|
-
const fs = require('fs')
|
|
5
|
-
const { promisify } = require('util')
|
|
6
|
-
|
|
7
|
-
const fsReadFile = promisify(fs.readFile)
|
|
8
|
-
const fsUnlink = promisify(fs.unlink)
|
|
9
|
-
const fsWriteFile = promisify(fs.writeFile)
|
|
10
|
-
|
|
11
|
-
/**
|
|
12
|
-
* Read contents of a file to buffer
|
|
13
|
-
* @param {String} filename
|
|
14
|
-
* @param {Boolean} [ignoreENOENT=true] — Whether to throw if the file doesn't exist
|
|
15
|
-
* @returns {Promise<Buffer>}
|
|
16
|
-
*/
|
|
17
|
-
const readFile = async (filename, ignoreENOENT = true) => {
|
|
18
|
-
debug('Reading file `%s`', filename)
|
|
19
|
-
try {
|
|
20
|
-
return await fsReadFile(filename)
|
|
21
|
-
} catch (error) {
|
|
22
|
-
if (ignoreENOENT && error.code === 'ENOENT') {
|
|
23
|
-
debug("File `%s` doesn't exist, ignoring...", filename)
|
|
24
|
-
return null // no-op file doesn't exist
|
|
25
|
-
} else {
|
|
26
|
-
throw error
|
|
27
|
-
}
|
|
28
|
-
}
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
/**
|
|
32
|
-
* Remove a file
|
|
33
|
-
* @param {String} filename
|
|
34
|
-
* @param {Boolean} [ignoreENOENT=true] — Whether to throw if the file doesn't exist
|
|
35
|
-
*/
|
|
36
|
-
const unlink = async (filename, ignoreENOENT = true) => {
|
|
37
|
-
debug('Removing file `%s`', filename)
|
|
38
|
-
try {
|
|
39
|
-
await fsUnlink(filename)
|
|
40
|
-
} catch (error) {
|
|
41
|
-
if (ignoreENOENT && error.code === 'ENOENT') {
|
|
42
|
-
debug("File `%s` doesn't exist, ignoring...", filename)
|
|
43
|
-
} else {
|
|
44
|
-
throw error
|
|
45
|
-
}
|
|
46
|
-
}
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
/**
|
|
50
|
-
* Write buffer to file
|
|
51
|
-
* @param {String} filename
|
|
52
|
-
* @param {Buffer} buffer
|
|
53
|
-
*/
|
|
54
|
-
const writeFile = async (filename, buffer) => {
|
|
55
|
-
debug('Writing file `%s`', filename)
|
|
56
|
-
await fsWriteFile(filename, buffer)
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
module.exports = {
|
|
60
|
-
readFile,
|
|
61
|
-
unlink,
|
|
62
|
-
writeFile,
|
|
63
|
-
}
|