lint-staged 11.3.0-beta.2 → 12.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 +10 -5
- package/bin/lint-staged.js +31 -45
- package/lib/chunkFiles.js +10 -9
- package/lib/execGit.js +7 -9
- package/lib/figures.js +6 -8
- package/lib/file.js +53 -0
- package/lib/generateTasks.js +8 -10
- package/lib/getRenderer.js +1 -5
- package/lib/getStagedFiles.js +2 -4
- package/lib/gitWorkflow.js +156 -72
- package/lib/index.js +28 -20
- package/lib/makeCmdTasks.js +7 -9
- package/lib/messages.js +27 -45
- package/lib/printTaskOutput.js +1 -5
- package/lib/resolveGitRepo.js +12 -18
- package/lib/resolveTaskFn.js +13 -13
- package/lib/runAll.js +34 -37
- package/lib/state.js +25 -40
- package/lib/symbols.js +23 -25
- package/lib/validateBraces.js +3 -7
- package/lib/validateConfig.js +9 -11
- package/lib/validateOptions.js +9 -13
- package/package.json +22 -33
- package/lib/unlink.js +0 -22
package/lib/gitWorkflow.js
CHANGED
|
@@ -1,21 +1,20 @@
|
|
|
1
|
-
|
|
1
|
+
import path from 'path'
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
const fs = require('fs')
|
|
5
|
-
const path = require('path')
|
|
6
|
-
const { promisify } = require('util')
|
|
3
|
+
import debug from 'debug'
|
|
7
4
|
|
|
8
|
-
|
|
9
|
-
|
|
5
|
+
import { execGit } from './execGit.js'
|
|
6
|
+
import { readFile, unlink, writeFile } from './file.js'
|
|
7
|
+
import {
|
|
10
8
|
GitError,
|
|
9
|
+
RestoreOriginalStateError,
|
|
11
10
|
ApplyEmptyCommitError,
|
|
11
|
+
GetBackupStashError,
|
|
12
12
|
HideUnstagedChangesError,
|
|
13
|
-
|
|
13
|
+
RestoreMergeStatusError,
|
|
14
14
|
RestoreUnstagedChangesError,
|
|
15
|
-
}
|
|
16
|
-
const unlink = require('./unlink')
|
|
15
|
+
} from './symbols.js'
|
|
17
16
|
|
|
18
|
-
const
|
|
17
|
+
const debugLog = debug('lint-staged:GitWorkflow')
|
|
19
18
|
|
|
20
19
|
const MERGE_HEAD = 'MERGE_HEAD'
|
|
21
20
|
const MERGE_MODE = 'MERGE_MODE'
|
|
@@ -43,8 +42,9 @@ const processRenames = (files, includeRenameFrom = true) =>
|
|
|
43
42
|
return flattened
|
|
44
43
|
}, [])
|
|
45
44
|
|
|
45
|
+
const STASH = 'lint-staged automatic backup'
|
|
46
|
+
|
|
46
47
|
const PATCH_UNSTAGED = 'lint-staged_unstaged.patch'
|
|
47
|
-
const PATCH_PARTIAL = 'lint-staged_partial.patch'
|
|
48
48
|
|
|
49
49
|
const GIT_DIFF_ARGS = [
|
|
50
50
|
'--binary', // support binary files
|
|
@@ -64,7 +64,7 @@ const handleError = (error, ctx, symbol) => {
|
|
|
64
64
|
throw error
|
|
65
65
|
}
|
|
66
66
|
|
|
67
|
-
class GitWorkflow {
|
|
67
|
+
export class GitWorkflow {
|
|
68
68
|
constructor({ allowEmpty, gitConfigDir, gitDir, matchedFileChunks }) {
|
|
69
69
|
this.execGit = (args, options = {}) => execGit(args, { ...options, cwd: gitDir })
|
|
70
70
|
this.deletedFiles = []
|
|
@@ -91,13 +91,76 @@ class GitWorkflow {
|
|
|
91
91
|
return path.resolve(this.gitConfigDir, `./${filename}`)
|
|
92
92
|
}
|
|
93
93
|
|
|
94
|
+
/**
|
|
95
|
+
* Get name of backup stash
|
|
96
|
+
*/
|
|
97
|
+
async getBackupStash(ctx) {
|
|
98
|
+
const stashes = await this.execGit(['stash', 'list'])
|
|
99
|
+
const index = stashes.split('\n').findIndex((line) => line.includes(STASH))
|
|
100
|
+
if (index === -1) {
|
|
101
|
+
ctx.errors.add(GetBackupStashError)
|
|
102
|
+
throw new Error('lint-staged automatic backup is missing!')
|
|
103
|
+
}
|
|
104
|
+
return `refs/stash@{${index}}`
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* Get a list of unstaged deleted files
|
|
109
|
+
*/
|
|
110
|
+
async getDeletedFiles() {
|
|
111
|
+
debugLog('Getting deleted files...')
|
|
112
|
+
const lsFiles = await this.execGit(['ls-files', '--deleted'])
|
|
113
|
+
const deletedFiles = lsFiles
|
|
114
|
+
.split('\n')
|
|
115
|
+
.filter(Boolean)
|
|
116
|
+
.map((file) => path.resolve(this.gitDir, file))
|
|
117
|
+
debugLog('Found deleted files:', deletedFiles)
|
|
118
|
+
return deletedFiles
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* Save meta information about ongoing git merge
|
|
123
|
+
*/
|
|
124
|
+
async backupMergeStatus() {
|
|
125
|
+
debugLog('Backing up merge state...')
|
|
126
|
+
await Promise.all([
|
|
127
|
+
readFile(this.mergeHeadFilename).then((buffer) => (this.mergeHeadBuffer = buffer)),
|
|
128
|
+
readFile(this.mergeModeFilename).then((buffer) => (this.mergeModeBuffer = buffer)),
|
|
129
|
+
readFile(this.mergeMsgFilename).then((buffer) => (this.mergeMsgBuffer = buffer)),
|
|
130
|
+
])
|
|
131
|
+
debugLog('Done backing up merge state!')
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* Restore meta information about ongoing git merge
|
|
136
|
+
*/
|
|
137
|
+
async restoreMergeStatus(ctx) {
|
|
138
|
+
debugLog('Restoring merge state...')
|
|
139
|
+
try {
|
|
140
|
+
await Promise.all([
|
|
141
|
+
this.mergeHeadBuffer && writeFile(this.mergeHeadFilename, this.mergeHeadBuffer),
|
|
142
|
+
this.mergeModeBuffer && writeFile(this.mergeModeFilename, this.mergeModeBuffer),
|
|
143
|
+
this.mergeMsgBuffer && writeFile(this.mergeMsgFilename, this.mergeMsgBuffer),
|
|
144
|
+
])
|
|
145
|
+
debugLog('Done restoring merge state!')
|
|
146
|
+
} catch (error) {
|
|
147
|
+
debugLog('Failed restoring merge state with error:')
|
|
148
|
+
debugLog(error)
|
|
149
|
+
handleError(
|
|
150
|
+
new Error('Merge state could not be restored due to an error!'),
|
|
151
|
+
ctx,
|
|
152
|
+
RestoreMergeStatusError
|
|
153
|
+
)
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
|
|
94
157
|
/**
|
|
95
158
|
* Get a list of all files with both staged and unstaged modifications.
|
|
96
159
|
* Renames have special treatment, since the single status line includes
|
|
97
160
|
* both the "from" and "to" filenames, where "from" is no longer on disk.
|
|
98
161
|
*/
|
|
99
162
|
async getPartiallyStagedFiles() {
|
|
100
|
-
|
|
163
|
+
debugLog('Getting partially staged files...')
|
|
101
164
|
const status = await this.execGit(['status', '-z'])
|
|
102
165
|
/**
|
|
103
166
|
* See https://git-scm.com/docs/git-status#_short_format
|
|
@@ -117,19 +180,16 @@ class GitWorkflow {
|
|
|
117
180
|
})
|
|
118
181
|
.map((line) => line.substr(3)) // Remove first three letters (index, workingTree, and a whitespace)
|
|
119
182
|
.filter(Boolean) // Filter empty string
|
|
120
|
-
|
|
183
|
+
debugLog('Found partially staged files:', partiallyStaged)
|
|
121
184
|
return partiallyStaged.length ? partiallyStaged : null
|
|
122
185
|
}
|
|
123
186
|
|
|
124
187
|
/**
|
|
125
|
-
* Create a diff of partially staged files.
|
|
188
|
+
* Create a diff of partially staged files and backup stash if enabled.
|
|
126
189
|
*/
|
|
127
190
|
async prepare(ctx) {
|
|
128
191
|
try {
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
const unstagedPatch = this.getHiddenFilepath(PATCH_UNSTAGED)
|
|
132
|
-
await this.execGit(['diff', ...GIT_DIFF_ARGS, '--output', unstagedPatch])
|
|
192
|
+
debugLog('Backing up original state...')
|
|
133
193
|
|
|
134
194
|
// Get a list of files with bot staged and unstaged changes.
|
|
135
195
|
// Unstaged changes to these files should be hidden before the tasks run.
|
|
@@ -137,14 +197,33 @@ class GitWorkflow {
|
|
|
137
197
|
|
|
138
198
|
if (this.partiallyStagedFiles) {
|
|
139
199
|
ctx.hasPartiallyStagedFiles = true
|
|
140
|
-
const
|
|
200
|
+
const unstagedPatch = this.getHiddenFilepath(PATCH_UNSTAGED)
|
|
141
201
|
const files = processRenames(this.partiallyStagedFiles)
|
|
142
|
-
await this.execGit(['diff', ...GIT_DIFF_ARGS, '--output',
|
|
202
|
+
await this.execGit(['diff', ...GIT_DIFF_ARGS, '--output', unstagedPatch, '--', ...files])
|
|
143
203
|
} else {
|
|
144
204
|
ctx.hasPartiallyStagedFiles = false
|
|
145
205
|
}
|
|
146
206
|
|
|
147
|
-
|
|
207
|
+
/**
|
|
208
|
+
* If backup stash should be skipped, no need to continue
|
|
209
|
+
*/
|
|
210
|
+
if (!ctx.shouldBackup) return
|
|
211
|
+
|
|
212
|
+
// When backup is enabled, the revert will clear ongoing merge status.
|
|
213
|
+
await this.backupMergeStatus()
|
|
214
|
+
|
|
215
|
+
// Get a list of unstaged deleted files, because certain bugs might cause them to reappear:
|
|
216
|
+
// - in git versions =< 2.13.0 the `git stash --keep-index` option resurrects deleted files
|
|
217
|
+
// - git stash can't infer RD or MD states correctly, and will lose the deletion
|
|
218
|
+
this.deletedFiles = await this.getDeletedFiles()
|
|
219
|
+
|
|
220
|
+
// Save stash of all staged files.
|
|
221
|
+
// The `stash create` command creates a dangling commit without removing any files,
|
|
222
|
+
// and `stash store` saves it as an actual stash.
|
|
223
|
+
const hash = await this.execGit(['stash', 'create'])
|
|
224
|
+
await this.execGit(['stash', 'store', '--quiet', '--message', STASH, hash])
|
|
225
|
+
|
|
226
|
+
debugLog('Done backing up original state!')
|
|
148
227
|
} catch (error) {
|
|
149
228
|
handleError(error, ctx)
|
|
150
229
|
}
|
|
@@ -166,18 +245,12 @@ class GitWorkflow {
|
|
|
166
245
|
}
|
|
167
246
|
}
|
|
168
247
|
|
|
169
|
-
/**
|
|
248
|
+
/**
|
|
249
|
+
* Applies back task modifications, and unstaged changes hidden in the stash.
|
|
250
|
+
* In case of a merge-conflict retry with 3-way merge.
|
|
251
|
+
*/
|
|
170
252
|
async applyModifications(ctx) {
|
|
171
|
-
|
|
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
|
-
}
|
|
253
|
+
debugLog('Adding task modifications to index...')
|
|
181
254
|
|
|
182
255
|
// `matchedFileChunks` includes staged files that lint-staged originally detected and matched against a task.
|
|
183
256
|
// Add only these files so any 3rd-party edits to other files won't be included in the commit.
|
|
@@ -187,26 +260,13 @@ class GitWorkflow {
|
|
|
187
260
|
await this.execGit(['add', '--', ...files])
|
|
188
261
|
}
|
|
189
262
|
|
|
190
|
-
|
|
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', '--', '.'])
|
|
200
|
-
|
|
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
|
-
}
|
|
263
|
+
debugLog('Done adding task modifications to index!')
|
|
206
264
|
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
265
|
+
const stagedFilesAfterAdd = await this.execGit(['diff', '--name-only', '--cached'])
|
|
266
|
+
if (!stagedFilesAfterAdd && !this.allowEmpty) {
|
|
267
|
+
// Tasks reverted all staged changes and the commit would be empty
|
|
268
|
+
// Throw error to stop commit unless `--allow-empty` was used
|
|
269
|
+
handleError(new Error('Prevented an empty git commit!'), ctx, ApplyEmptyCommitError)
|
|
210
270
|
}
|
|
211
271
|
}
|
|
212
272
|
|
|
@@ -215,21 +275,21 @@ class GitWorkflow {
|
|
|
215
275
|
* this is probably because of conflicts between new task modifications.
|
|
216
276
|
* 3-way merge usually fixes this, and in case it doesn't we should just give up and throw.
|
|
217
277
|
*/
|
|
218
|
-
async
|
|
219
|
-
|
|
220
|
-
const
|
|
278
|
+
async restoreUnstagedChanges(ctx) {
|
|
279
|
+
debugLog('Restoring unstaged changes...')
|
|
280
|
+
const unstagedPatch = this.getHiddenFilepath(PATCH_UNSTAGED)
|
|
221
281
|
try {
|
|
222
|
-
await this.execGit(['apply', ...GIT_APPLY_ARGS,
|
|
282
|
+
await this.execGit(['apply', ...GIT_APPLY_ARGS, unstagedPatch])
|
|
223
283
|
} catch (applyError) {
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
284
|
+
debugLog('Error while restoring changes:')
|
|
285
|
+
debugLog(applyError)
|
|
286
|
+
debugLog('Retrying with 3-way merge')
|
|
227
287
|
try {
|
|
228
288
|
// Retry with a 3-way merge if normal apply fails
|
|
229
|
-
await this.execGit(['apply', ...GIT_APPLY_ARGS, '--3way',
|
|
289
|
+
await this.execGit(['apply', ...GIT_APPLY_ARGS, '--3way', unstagedPatch])
|
|
230
290
|
} catch (threeWayApplyError) {
|
|
231
|
-
|
|
232
|
-
|
|
291
|
+
debugLog('Error while restoring unstaged changes using 3-way merge:')
|
|
292
|
+
debugLog(threeWayApplyError)
|
|
233
293
|
handleError(
|
|
234
294
|
new Error('Unstaged changes could not be restored due to a merge conflict!'),
|
|
235
295
|
ctx,
|
|
@@ -240,15 +300,39 @@ class GitWorkflow {
|
|
|
240
300
|
}
|
|
241
301
|
|
|
242
302
|
/**
|
|
243
|
-
*
|
|
244
|
-
|
|
303
|
+
* Restore original HEAD state in case of errors
|
|
304
|
+
*/
|
|
305
|
+
async restoreOriginalState(ctx) {
|
|
306
|
+
try {
|
|
307
|
+
debugLog('Restoring original state...')
|
|
308
|
+
await this.execGit(['reset', '--hard', 'HEAD'])
|
|
309
|
+
await this.execGit(['stash', 'apply', '--quiet', '--index', await this.getBackupStash(ctx)])
|
|
310
|
+
|
|
311
|
+
// Restore meta information about ongoing git merge
|
|
312
|
+
await this.restoreMergeStatus(ctx)
|
|
313
|
+
|
|
314
|
+
// If stashing resurrected deleted files, clean them out
|
|
315
|
+
await Promise.all(this.deletedFiles.map((file) => unlink(file)))
|
|
316
|
+
|
|
317
|
+
// Clean out patch
|
|
318
|
+
await unlink(this.getHiddenFilepath(PATCH_UNSTAGED))
|
|
319
|
+
|
|
320
|
+
debugLog('Done restoring original state!')
|
|
321
|
+
} catch (error) {
|
|
322
|
+
handleError(error, ctx, RestoreOriginalStateError)
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
/**
|
|
327
|
+
* Drop the created stashes after everything has run
|
|
245
328
|
*/
|
|
246
|
-
async cleanup() {
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
329
|
+
async cleanup(ctx) {
|
|
330
|
+
try {
|
|
331
|
+
debugLog('Dropping backup stash...')
|
|
332
|
+
await this.execGit(['stash', 'drop', '--quiet', await this.getBackupStash(ctx)])
|
|
333
|
+
debugLog('Done dropping backup stash!')
|
|
334
|
+
} catch (error) {
|
|
335
|
+
handleError(error, ctx)
|
|
336
|
+
}
|
|
251
337
|
}
|
|
252
338
|
}
|
|
253
|
-
|
|
254
|
-
module.exports = GitWorkflow
|
package/lib/index.js
CHANGED
|
@@ -1,15 +1,20 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
1
|
+
import { cosmiconfig } from 'cosmiconfig'
|
|
2
|
+
import debug from 'debug'
|
|
3
|
+
import inspect from 'object-inspect'
|
|
4
|
+
|
|
5
|
+
import { PREVENTED_EMPTY_COMMIT, GIT_ERROR, RESTORE_STASH_EXAMPLE } from './messages.js'
|
|
6
|
+
import { printTaskOutput } from './printTaskOutput.js'
|
|
7
|
+
import { runAll } from './runAll.js'
|
|
8
|
+
import {
|
|
9
|
+
ApplyEmptyCommitError,
|
|
10
|
+
ConfigNotFoundError,
|
|
11
|
+
GetBackupStashError,
|
|
12
|
+
GitError,
|
|
13
|
+
} from './symbols.js'
|
|
14
|
+
import { validateConfig } from './validateConfig.js'
|
|
15
|
+
import { validateOptions } from './validateOptions.js'
|
|
16
|
+
|
|
17
|
+
const debugLog = debug('lint-staged')
|
|
13
18
|
|
|
14
19
|
const resolveConfig = (configPath) => {
|
|
15
20
|
try {
|
|
@@ -53,8 +58,8 @@ const loadConfig = (configPath) => {
|
|
|
53
58
|
* @param {number} [options.maxArgLength] - Maximum argument string length
|
|
54
59
|
* @param {boolean} [options.quiet] - Disable lint-staged’s own console output
|
|
55
60
|
* @param {boolean} [options.relative] - Pass relative filepaths to tasks
|
|
56
|
-
* @param {boolean} [options.reset] - Reset changes in case of errors
|
|
57
61
|
* @param {boolean|string} [options.shell] - Skip parsing of tasks for better shell support
|
|
62
|
+
* @param {boolean} [options.stash] - Enable the backup stash, and revert in case of errors
|
|
58
63
|
* @param {boolean} [options.verbose] - Show task output even when tasks succeed; by default only failed output is shown
|
|
59
64
|
* @param {Logger} [logger]
|
|
60
65
|
*
|
|
@@ -71,14 +76,13 @@ const lintStaged = async (
|
|
|
71
76
|
maxArgLength,
|
|
72
77
|
quiet = false,
|
|
73
78
|
relative = false,
|
|
74
|
-
reset = true,
|
|
75
79
|
shell = false,
|
|
76
|
-
stash
|
|
80
|
+
stash = true,
|
|
77
81
|
verbose = false,
|
|
78
82
|
} = {},
|
|
79
83
|
logger = console
|
|
80
84
|
) => {
|
|
81
|
-
await validateOptions({ shell
|
|
85
|
+
await validateOptions({ shell }, logger)
|
|
82
86
|
|
|
83
87
|
debugLog('Loading config using `cosmiconfig`')
|
|
84
88
|
|
|
@@ -100,7 +104,7 @@ const lintStaged = async (
|
|
|
100
104
|
if (debug) {
|
|
101
105
|
// Log using logger to be able to test through `consolemock`.
|
|
102
106
|
logger.log('Running lint-staged with the following config:')
|
|
103
|
-
logger.log(
|
|
107
|
+
logger.log(inspect(config, { indent: 2 }))
|
|
104
108
|
} else {
|
|
105
109
|
// We might not be in debug mode but `DEBUG=lint-staged*` could have
|
|
106
110
|
// been set.
|
|
@@ -122,8 +126,8 @@ const lintStaged = async (
|
|
|
122
126
|
maxArgLength,
|
|
123
127
|
quiet,
|
|
124
128
|
relative,
|
|
125
|
-
reset: reset || !!stash,
|
|
126
129
|
shell,
|
|
130
|
+
stash,
|
|
127
131
|
verbose,
|
|
128
132
|
},
|
|
129
133
|
logger
|
|
@@ -136,8 +140,12 @@ const lintStaged = async (
|
|
|
136
140
|
const { ctx } = runAllError
|
|
137
141
|
if (ctx.errors.has(ApplyEmptyCommitError)) {
|
|
138
142
|
logger.warn(PREVENTED_EMPTY_COMMIT)
|
|
139
|
-
} else if (ctx.errors.has(GitError)) {
|
|
143
|
+
} else if (ctx.errors.has(GitError) && !ctx.errors.has(GetBackupStashError)) {
|
|
140
144
|
logger.error(GIT_ERROR)
|
|
145
|
+
if (ctx.shouldBackup) {
|
|
146
|
+
// No sense to show this if the backup stash itself is missing.
|
|
147
|
+
logger.error(RESTORE_STASH_EXAMPLE)
|
|
148
|
+
}
|
|
141
149
|
}
|
|
142
150
|
|
|
143
151
|
printTaskOutput(ctx, logger)
|
|
@@ -149,4 +157,4 @@ const lintStaged = async (
|
|
|
149
157
|
}
|
|
150
158
|
}
|
|
151
159
|
|
|
152
|
-
|
|
160
|
+
export default lintStaged
|
package/lib/makeCmdTasks.js
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
|
|
1
|
+
import cliTruncate from 'cli-truncate'
|
|
2
|
+
import debug from 'debug'
|
|
2
3
|
|
|
3
|
-
|
|
4
|
-
|
|
4
|
+
import { configurationError } from './messages.js'
|
|
5
|
+
import { resolveTaskFn } from './resolveTaskFn.js'
|
|
5
6
|
|
|
6
|
-
const
|
|
7
|
-
const resolveTaskFn = require('./resolveTaskFn')
|
|
7
|
+
const debugLog = debug('lint-staged:makeCmdTasks')
|
|
8
8
|
|
|
9
9
|
const STDOUT_COLUMNS_DEFAULT = 80
|
|
10
10
|
|
|
@@ -34,8 +34,8 @@ const getTitleLength = (renderer, columns = process.stdout.columns) => {
|
|
|
34
34
|
* @param {Boolean} shell
|
|
35
35
|
* @param {Boolean} verbose
|
|
36
36
|
*/
|
|
37
|
-
const makeCmdTasks = async ({ commands, files, gitDir, renderer, shell, verbose }) => {
|
|
38
|
-
|
|
37
|
+
export const makeCmdTasks = async ({ commands, files, gitDir, renderer, shell, verbose }) => {
|
|
38
|
+
debugLog('Creating listr tasks for commands %o', commands)
|
|
39
39
|
const commandArray = Array.isArray(commands) ? commands : [commands]
|
|
40
40
|
const cmdTasks = []
|
|
41
41
|
|
|
@@ -68,5 +68,3 @@ const makeCmdTasks = async ({ commands, files, gitDir, renderer, shell, verbose
|
|
|
68
68
|
|
|
69
69
|
return cmdTasks
|
|
70
70
|
}
|
|
71
|
-
|
|
72
|
-
module.exports = makeCmdTasks
|
package/lib/messages.js
CHANGED
|
@@ -1,55 +1,48 @@
|
|
|
1
|
-
|
|
1
|
+
import { redBright, bold, yellow } from 'colorette'
|
|
2
|
+
import inspect from 'object-inspect'
|
|
2
3
|
|
|
3
|
-
|
|
4
|
-
const format = require('stringify-object')
|
|
4
|
+
import { error, info, warning } from './figures.js'
|
|
5
5
|
|
|
6
|
-
const
|
|
7
|
-
|
|
8
|
-
const configurationError = (opt, helpMsg, value) =>
|
|
6
|
+
export const configurationError = (opt, helpMsg, value) =>
|
|
9
7
|
`${redBright(`${error} Validation Error:`)}
|
|
10
8
|
|
|
11
9
|
Invalid value for '${bold(opt)}': ${bold(
|
|
12
|
-
|
|
10
|
+
inspect(value, { inlineCharacterLimit: Number.POSITIVE_INFINITY })
|
|
13
11
|
)}
|
|
14
12
|
|
|
15
13
|
${helpMsg}`
|
|
16
14
|
|
|
17
|
-
const NOT_GIT_REPO = redBright(`${error} Current directory is not a git directory!`)
|
|
15
|
+
export const NOT_GIT_REPO = redBright(`${error} Current directory is not a git directory!`)
|
|
18
16
|
|
|
19
|
-
const FAILED_GET_STAGED_FILES = redBright(`${error} Failed to get staged files!`)
|
|
17
|
+
export const FAILED_GET_STAGED_FILES = redBright(`${error} Failed to get staged files!`)
|
|
20
18
|
|
|
21
|
-
const incorrectBraces = (before, after) =>
|
|
19
|
+
export const incorrectBraces = (before, after) =>
|
|
22
20
|
yellow(
|
|
23
21
|
`${warning} Detected incorrect braces with only single value: \`${before}\`. Reformatted as: \`${after}\`
|
|
24
22
|
`
|
|
25
23
|
)
|
|
26
24
|
|
|
27
|
-
const NO_STAGED_FILES = `${info} No staged files found.`
|
|
25
|
+
export const NO_STAGED_FILES = `${info} No staged files found.`
|
|
28
26
|
|
|
29
|
-
const NO_TASKS = `${info} No staged files match any configured task.`
|
|
27
|
+
export const NO_TASKS = `${info} No staged files match any configured task.`
|
|
30
28
|
|
|
31
|
-
const
|
|
32
|
-
const reason = hasInitialCommit ? '`--no-
|
|
33
|
-
return yellow(`${warning}
|
|
29
|
+
export const skippingBackup = (hasInitialCommit) => {
|
|
30
|
+
const reason = hasInitialCommit ? '`--no-stash` was used' : 'there’s no initial commit yet'
|
|
31
|
+
return yellow(`${warning} Skipping backup because ${reason}.\n`)
|
|
34
32
|
}
|
|
35
33
|
|
|
36
|
-
const DEPRECATED_GIT_ADD = yellow(
|
|
34
|
+
export const DEPRECATED_GIT_ADD = yellow(
|
|
37
35
|
`${warning} Some of your tasks use \`git add\` command. Please remove it from the config since all modifications made by tasks will be automatically added to the git commit index.
|
|
38
36
|
`
|
|
39
37
|
)
|
|
40
38
|
|
|
41
|
-
const
|
|
42
|
-
`${warning} The \`--no-stash\` option has been renamed to \`--no-reset\`.
|
|
43
|
-
`
|
|
44
|
-
)
|
|
45
|
-
|
|
46
|
-
const TASK_ERROR = 'Skipped because of errors from tasks.'
|
|
39
|
+
export const TASK_ERROR = 'Skipped because of errors from tasks.'
|
|
47
40
|
|
|
48
|
-
const SKIPPED_GIT_ERROR = 'Skipped because of previous git error.'
|
|
41
|
+
export const SKIPPED_GIT_ERROR = 'Skipped because of previous git error.'
|
|
49
42
|
|
|
50
|
-
const GIT_ERROR = `\n ${redBright(`${error} lint-staged failed due to a git error.`)}`
|
|
43
|
+
export const GIT_ERROR = `\n ${redBright(`${error} lint-staged failed due to a git error.`)}`
|
|
51
44
|
|
|
52
|
-
const invalidOption = (name, value, message) => `${redBright(`${error} Validation Error:`)}
|
|
45
|
+
export const invalidOption = (name, value, message) => `${redBright(`${error} Validation Error:`)}
|
|
53
46
|
|
|
54
47
|
Invalid value for option '${bold(name)}': ${bold(value)}
|
|
55
48
|
|
|
@@ -57,27 +50,16 @@ const invalidOption = (name, value, message) => `${redBright(`${error} Validatio
|
|
|
57
50
|
|
|
58
51
|
See https://github.com/okonet/lint-staged#command-line-flags`
|
|
59
52
|
|
|
60
|
-
const PREVENTED_EMPTY_COMMIT = `
|
|
53
|
+
export const PREVENTED_EMPTY_COMMIT = `
|
|
61
54
|
${yellow(`${warning} lint-staged prevented an empty git commit.
|
|
62
55
|
Use the --allow-empty option to continue, or check your task configuration`)}
|
|
63
56
|
`
|
|
64
57
|
|
|
65
|
-
const
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
GIT_ERROR,
|
|
74
|
-
incorrectBraces,
|
|
75
|
-
invalidOption,
|
|
76
|
-
NO_STAGED_FILES,
|
|
77
|
-
NO_TASKS,
|
|
78
|
-
NOT_GIT_REPO,
|
|
79
|
-
PREVENTED_EMPTY_COMMIT,
|
|
80
|
-
SKIPPED_GIT_ERROR,
|
|
81
|
-
skippingReset,
|
|
82
|
-
TASK_ERROR,
|
|
83
|
-
}
|
|
58
|
+
export const RESTORE_STASH_EXAMPLE = ` Any lost modifications can be restored from a git stash:
|
|
59
|
+
|
|
60
|
+
> git stash list
|
|
61
|
+
stash@{0}: automatic lint-staged backup
|
|
62
|
+
> git stash apply --index stash@{0}
|
|
63
|
+
`
|
|
64
|
+
|
|
65
|
+
export const CONFIG_STDIN_ERROR = 'Error: Could not read config from stdin.'
|
package/lib/printTaskOutput.js
CHANGED
|
@@ -1,16 +1,12 @@
|
|
|
1
|
-
'use strict'
|
|
2
|
-
|
|
3
1
|
/**
|
|
4
2
|
* Handle logging of listr `ctx.output` to the specified `logger`
|
|
5
3
|
* @param {Object} ctx - The listr initial state
|
|
6
4
|
* @param {Object} logger - The logger
|
|
7
5
|
*/
|
|
8
|
-
const printTaskOutput = (ctx = {}, logger) => {
|
|
6
|
+
export const printTaskOutput = (ctx = {}, logger) => {
|
|
9
7
|
if (!Array.isArray(ctx.output)) return
|
|
10
8
|
const log = ctx.errors && ctx.errors.size > 0 ? logger.error : logger.log
|
|
11
9
|
for (const line of ctx.output) {
|
|
12
10
|
log(line)
|
|
13
11
|
}
|
|
14
12
|
}
|
|
15
|
-
|
|
16
|
-
module.exports = printTaskOutput
|
package/lib/resolveGitRepo.js
CHANGED
|
@@ -1,15 +1,13 @@
|
|
|
1
|
-
|
|
1
|
+
import { promises as fs } from 'fs'
|
|
2
|
+
import path from 'path'
|
|
2
3
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
const fs = require('fs')
|
|
6
|
-
const path = require('path')
|
|
7
|
-
const { promisify } = require('util')
|
|
4
|
+
import debug from 'debug'
|
|
5
|
+
import normalize from 'normalize-path'
|
|
8
6
|
|
|
9
|
-
|
|
7
|
+
import { execGit } from './execGit.js'
|
|
8
|
+
import { readFile } from './file.js'
|
|
10
9
|
|
|
11
|
-
const
|
|
12
|
-
const fsReadFile = promisify(fs.readFile)
|
|
10
|
+
const debugLog = debug('lint-staged:resolveGitRepo')
|
|
13
11
|
|
|
14
12
|
/**
|
|
15
13
|
* Resolve path to the .git directory, with special handling for
|
|
@@ -17,15 +15,16 @@ const fsReadFile = promisify(fs.readFile)
|
|
|
17
15
|
*/
|
|
18
16
|
const resolveGitConfigDir = async (gitDir) => {
|
|
19
17
|
const defaultDir = normalize(path.join(gitDir, '.git'))
|
|
20
|
-
const stats = await
|
|
18
|
+
const stats = await fs.lstat(defaultDir)
|
|
21
19
|
// If .git is a directory, use it
|
|
22
20
|
if (stats.isDirectory()) return defaultDir
|
|
23
21
|
// Otherwise .git is a file containing path to real location
|
|
24
|
-
const file = (await
|
|
22
|
+
const file = (await readFile(defaultDir)).toString()
|
|
25
23
|
return path.resolve(gitDir, file.replace(/^gitdir: /, '')).trim()
|
|
26
24
|
}
|
|
27
25
|
|
|
28
|
-
|
|
26
|
+
// exported for test
|
|
27
|
+
export const determineGitDir = (cwd, relativeDir) => {
|
|
29
28
|
// if relative dir and cwd have different endings normalize it
|
|
30
29
|
// this happens under windows, where normalize is unable to normalize git's output
|
|
31
30
|
if (relativeDir && relativeDir.endsWith(path.sep)) {
|
|
@@ -43,7 +42,7 @@ const determineGitDir = (cwd, relativeDir) => {
|
|
|
43
42
|
/**
|
|
44
43
|
* Resolve git directory and possible submodule paths
|
|
45
44
|
*/
|
|
46
|
-
const resolveGitRepo = async (cwd = process.cwd()) => {
|
|
45
|
+
export const resolveGitRepo = async (cwd = process.cwd()) => {
|
|
47
46
|
try {
|
|
48
47
|
debugLog('Resolving git repo from `%s`', cwd)
|
|
49
48
|
|
|
@@ -68,8 +67,3 @@ const resolveGitRepo = async (cwd = process.cwd()) => {
|
|
|
68
67
|
return { error, gitDir: null, gitConfigDir: null }
|
|
69
68
|
}
|
|
70
69
|
}
|
|
71
|
-
|
|
72
|
-
module.exports = resolveGitRepo
|
|
73
|
-
|
|
74
|
-
// exported for test
|
|
75
|
-
module.exports.determineGitDir = determineGitDir
|