lint-staged 17.0.8 → 17.1.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 +14 -24
- package/bin/lint-staged.js +5 -5
- package/lib/colors.js +26 -8
- package/lib/debug.js +2 -2
- package/lib/figures.js +9 -5
- package/lib/getFunctionTask.js +13 -4
- package/lib/getSpawnedTask.js +9 -9
- package/lib/getSpawnedTasks.js +3 -6
- package/lib/gitWorkflow.js +188 -112
- package/lib/index.js +14 -12
- package/lib/messages.js +36 -29
- package/lib/parseGitZOutput.js +1 -1
- package/lib/printTaskOutput.js +2 -2
- package/lib/runAll.js +103 -103
- package/lib/runParallelTasks.js +112 -0
- package/lib/state.js +14 -9
- package/lib/validateOptions.js +9 -0
- package/package.json +20 -25
- package/lib/getRenderer.js +0 -63
package/lib/gitWorkflow.js
CHANGED
|
@@ -2,12 +2,15 @@ import crypto from 'node:crypto'
|
|
|
2
2
|
import fs from 'node:fs/promises'
|
|
3
3
|
import path from 'node:path'
|
|
4
4
|
|
|
5
|
+
import { dim } from './colors.js'
|
|
5
6
|
import { createDebug } from './debug.js'
|
|
6
7
|
import { execGit } from './execGit.js'
|
|
8
|
+
import * as figures from './figures.js'
|
|
7
9
|
import { readFile, unlink, writeFile } from './file.js'
|
|
8
10
|
import { getDiffCommand } from './getDiffCommand.js'
|
|
9
11
|
import { normalizePath } from './normalizePath.js'
|
|
10
12
|
import { parseGitZOutput } from './parseGitZOutput.js'
|
|
13
|
+
import { runParallelTasks } from './runParallelTasks.js'
|
|
11
14
|
import {
|
|
12
15
|
ApplyEmptyCommitError,
|
|
13
16
|
FailOnChangesError,
|
|
@@ -17,6 +20,7 @@ import {
|
|
|
17
20
|
RestoreMergeStatusError,
|
|
18
21
|
RestoreOriginalStateError,
|
|
19
22
|
RestoreUnstagedChangesError,
|
|
23
|
+
TaskError,
|
|
20
24
|
} from './symbols.js'
|
|
21
25
|
|
|
22
26
|
const debugLog = createDebug('lint-staged:GitWorkflow')
|
|
@@ -27,7 +31,7 @@ const MERGE_MSG = 'MERGE_MSG'
|
|
|
27
31
|
|
|
28
32
|
// In git status machine output, renames are presented as `to`NUL`from`
|
|
29
33
|
// When diffing, both need to be taken into account, but in some cases on the `to`.
|
|
30
|
-
//
|
|
34
|
+
// oxlint-disable-next-line no-control-regex
|
|
31
35
|
const RENAME = /\x00/
|
|
32
36
|
|
|
33
37
|
/**
|
|
@@ -63,12 +67,6 @@ const GIT_DIFF_ARGS = [
|
|
|
63
67
|
]
|
|
64
68
|
const GIT_APPLY_ARGS = ['-v', '--whitespace=nowarn', '--recount', '--unidiff-zero']
|
|
65
69
|
|
|
66
|
-
const handleError = (error, ctx, symbol) => {
|
|
67
|
-
ctx.errors.add(GitError)
|
|
68
|
-
if (symbol) ctx.errors.add(symbol)
|
|
69
|
-
throw error
|
|
70
|
-
}
|
|
71
|
-
|
|
72
70
|
const calculateSha256 = (input) => crypto.createHash('sha256').update(input, 'utf-8').digest('hex')
|
|
73
71
|
|
|
74
72
|
/**
|
|
@@ -87,6 +85,7 @@ export class GitWorkflow {
|
|
|
87
85
|
diffFilter,
|
|
88
86
|
failOnChanges,
|
|
89
87
|
gitConfigDir,
|
|
88
|
+
logger,
|
|
90
89
|
matchedFileChunks,
|
|
91
90
|
topLevelDir,
|
|
92
91
|
}) {
|
|
@@ -96,6 +95,7 @@ export class GitWorkflow {
|
|
|
96
95
|
this.diffFilter = diffFilter
|
|
97
96
|
this.gitConfigDir = gitConfigDir
|
|
98
97
|
this.failOnChanges = !!failOnChanges
|
|
98
|
+
this.logger = logger
|
|
99
99
|
/** @type {import('./getStagedFiles.js').StagedFile[][]} */
|
|
100
100
|
this.matchedFileChunks = matchedFileChunks
|
|
101
101
|
this.topLevelDir = topLevelDir
|
|
@@ -130,6 +130,7 @@ export class GitWorkflow {
|
|
|
130
130
|
|
|
131
131
|
if (index === -1) {
|
|
132
132
|
ctx.errors.add(GetBackupStashError)
|
|
133
|
+
this.logger.error(`${figures.error()} lint-staged automatic backup is missing!`)
|
|
133
134
|
throw new Error('lint-staged automatic backup is missing!')
|
|
134
135
|
}
|
|
135
136
|
|
|
@@ -162,13 +163,13 @@ export class GitWorkflow {
|
|
|
162
163
|
])
|
|
163
164
|
debugLog('Done restoring merge state!')
|
|
164
165
|
} catch (error) {
|
|
166
|
+
ctx.errors.add(GitError)
|
|
167
|
+
ctx.errors.add(RestoreMergeStatusError)
|
|
168
|
+
|
|
165
169
|
debugLog('Failed restoring merge state with error:')
|
|
166
170
|
debugLog(error)
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
ctx,
|
|
170
|
-
RestoreMergeStatusError
|
|
171
|
-
)
|
|
171
|
+
|
|
172
|
+
throw error
|
|
172
173
|
}
|
|
173
174
|
}
|
|
174
175
|
|
|
@@ -190,7 +191,7 @@ export class GitWorkflow {
|
|
|
190
191
|
* (e.g. `to`\0`from`)
|
|
191
192
|
*/
|
|
192
193
|
const unstagedFiles = status
|
|
193
|
-
//
|
|
194
|
+
// oxlint-disable-next-line no-control-regex
|
|
194
195
|
.split(/\x00(?=[ AMDRCU?!]{2} |$)/)
|
|
195
196
|
.filter((line) => {
|
|
196
197
|
const [index, workingTree] = line
|
|
@@ -212,10 +213,14 @@ export class GitWorkflow {
|
|
|
212
213
|
/**
|
|
213
214
|
* Create a diff of unstaged or partially staged files and backup stash if enabled.
|
|
214
215
|
*/
|
|
215
|
-
async prepare(ctx
|
|
216
|
-
|
|
217
|
-
|
|
216
|
+
async prepare(ctx) {
|
|
217
|
+
this.logger.log(
|
|
218
|
+
dim(
|
|
219
|
+
`${figures.wip()} ${ctx.shouldBackup ? 'Backing up original state…' : 'Preparing lint-staged…'}`
|
|
220
|
+
)
|
|
221
|
+
)
|
|
218
222
|
|
|
223
|
+
try {
|
|
219
224
|
if (ctx.shouldBackup) {
|
|
220
225
|
// When backup is enabled, the revert will clear ongoing merge status.
|
|
221
226
|
await this.backupMergeStatus()
|
|
@@ -257,114 +262,175 @@ export class GitWorkflow {
|
|
|
257
262
|
ctx.backupHash = await this.execGit(['rev-parse', '--short', stashHash])
|
|
258
263
|
await this.execGit(['stash', 'store', '--quiet', '--message', STASH, ctx.backupHash])
|
|
259
264
|
}
|
|
260
|
-
|
|
261
|
-
task.title = `Backed up original state in git stash (${ctx.backupHash})`
|
|
262
|
-
debugLog(task.title)
|
|
263
265
|
}
|
|
266
|
+
|
|
267
|
+
this.logger.log(
|
|
268
|
+
`${figures.done()} ${
|
|
269
|
+
ctx.shouldBackup
|
|
270
|
+
? `Done backing up original state (${ctx.backupHash})!`
|
|
271
|
+
: 'Done preparing lint-staged!'
|
|
272
|
+
}`
|
|
273
|
+
)
|
|
264
274
|
} catch (error) {
|
|
265
|
-
|
|
275
|
+
ctx.errors.add(GitError)
|
|
276
|
+
|
|
277
|
+
const errorMessage = ctx.shouldBackup
|
|
278
|
+
? 'Failed to back up original state!'
|
|
279
|
+
: 'Failed to prepare lint-staged!'
|
|
280
|
+
|
|
281
|
+
this.logger.error(`${figures.error()} ${errorMessage}`)
|
|
282
|
+
debugLog(error)
|
|
266
283
|
}
|
|
267
284
|
}
|
|
268
285
|
|
|
269
286
|
async hidePartiallyStagedChanges(ctx) {
|
|
287
|
+
this.logger.log(dim(`${figures.wip()} Hiding unstaged changes to partially staged files…`))
|
|
288
|
+
|
|
270
289
|
try {
|
|
271
290
|
const files = processRenames(this.unstagedFiles, false)
|
|
272
291
|
await this.execGit(['restore', '--worktree', '--', ...files])
|
|
292
|
+
this.logger.log(`${figures.done()} Done hiding unstaged changes to partially staged files!`)
|
|
273
293
|
} catch (error) {
|
|
274
294
|
/**
|
|
275
295
|
* `git checkout --force` doesn't throw errors, so it shouldn't be possible to get here.
|
|
276
|
-
* If this does fail, the handleError method will set ctx.gitError and lint-staged will fail.
|
|
277
296
|
*/
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
}
|
|
297
|
+
ctx.errors.add(GitError)
|
|
298
|
+
ctx.errors.add(HideUnstagedChangesError)
|
|
281
299
|
|
|
282
|
-
|
|
283
|
-
if (ctx.shouldFailOnChanges) {
|
|
284
|
-
debugLog(
|
|
285
|
-
'Calculating SHA-256 hash of unstaged changes because "--fail-on-changes" was used...'
|
|
286
|
-
)
|
|
287
|
-
const diff = await this.execGit(['diff', '--patch', '--unified=0'])
|
|
288
|
-
ctx.unstagedDiffSha256 = calculateSha256(diff)
|
|
289
|
-
debugLog('SHA-256 hash of unstaged changes is %s', ctx.unstagedDiffSha256)
|
|
290
|
-
}
|
|
300
|
+
const errorMessage = `${figures.error()} Failed to hude unstaged changes to partially staged files!`
|
|
291
301
|
|
|
292
|
-
|
|
302
|
+
this.logger.error(errorMessage)
|
|
303
|
+
debugLog(error)
|
|
304
|
+
}
|
|
293
305
|
}
|
|
294
306
|
|
|
295
|
-
|
|
296
|
-
|
|
307
|
+
async runTasks(ctx, tasks, { abortController, concurrent }) {
|
|
308
|
+
this.logger.log(
|
|
309
|
+
dim(`${figures.wip()} Running tasks for ${this.diff ? 'changed' : 'staged'} files…`)
|
|
310
|
+
)
|
|
311
|
+
|
|
297
312
|
if (ctx.shouldFailOnChanges) {
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
313
|
+
try {
|
|
314
|
+
debugLog(
|
|
315
|
+
'Calculating SHA-256 hash of unstaged changes because "--fail-on-changes" was used...'
|
|
316
|
+
)
|
|
317
|
+
const diff = await this.execGit(['diff', '--patch', '--unified=0'])
|
|
318
|
+
ctx.unstagedDiffSha256 = calculateSha256(diff)
|
|
319
|
+
debugLog('SHA-256 hash of unstaged changes is %s', ctx.unstagedDiffSha256)
|
|
320
|
+
} catch (error) {
|
|
321
|
+
ctx.errors.add(GitError)
|
|
322
|
+
this.logger.error('Failed to calculate SHA-256 hash of unstaged changes!')
|
|
323
|
+
debugLog(error)
|
|
324
|
+
return
|
|
307
325
|
}
|
|
308
326
|
}
|
|
309
327
|
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
:
|
|
314
|
-
|
|
315
|
-
/** Needs to be run serially because of locking Git operation */
|
|
316
|
-
for (const files of this.matchedFileChunks) {
|
|
317
|
-
const accessCheckedFiles = await Promise.allSettled(
|
|
318
|
-
files.map(async (f) => {
|
|
319
|
-
if (f.status === 'D') {
|
|
320
|
-
await fs.access(f.filepath)
|
|
321
|
-
return f.filepath // File is no longer deleted and can be added
|
|
322
|
-
} else {
|
|
323
|
-
return f.filepath
|
|
324
|
-
}
|
|
325
|
-
})
|
|
326
|
-
)
|
|
328
|
+
const failureMessage = `${figures.error()} Failed to run tasks for ${this.diff ? 'changed' : 'staged'} files!`
|
|
329
|
+
|
|
330
|
+
try {
|
|
331
|
+
await runParallelTasks(ctx, tasks, { abortController, concurrent, logger: this.logger })
|
|
327
332
|
|
|
328
|
-
|
|
329
|
-
|
|
333
|
+
this.logger.log(
|
|
334
|
+
ctx.errors.has(TaskError)
|
|
335
|
+
? failureMessage
|
|
336
|
+
: `${figures.done()} Done running tasks for ${this.diff ? 'changed' : 'staged'} files!`
|
|
330
337
|
)
|
|
338
|
+
} catch (error) {
|
|
339
|
+
ctx.errors.add(TaskError)
|
|
340
|
+
this.logger.error(failureMessage)
|
|
341
|
+
debugLog(error)
|
|
342
|
+
}
|
|
343
|
+
}
|
|
331
344
|
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
345
|
+
/** Update Git index again for the originally staged files to stage task modifications. */
|
|
346
|
+
async updateIndex(ctx) {
|
|
347
|
+
this.logger.log(dim(`${figures.wip()} Staging changes from tasks…`))
|
|
335
348
|
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
349
|
+
try {
|
|
350
|
+
if (ctx.shouldFailOnChanges) {
|
|
351
|
+
debugLog(
|
|
352
|
+
'Calculating SHA-256 hash of changes after tasks because "--fail-on-changes" was used...'
|
|
339
353
|
)
|
|
354
|
+
const diff = await this.execGit(['diff', '--patch', '--unified=0'])
|
|
355
|
+
const diffSha256 = calculateSha256(diff)
|
|
356
|
+
debugLog('SHA-256 hash of changes after tasks is %s', diffSha256)
|
|
357
|
+
|
|
358
|
+
if (ctx.unstagedDiffSha256 !== diffSha256) {
|
|
359
|
+
ctx.errors.add(FailOnChangesError)
|
|
360
|
+
this.logger.error(
|
|
361
|
+
`${figures.error()} Tasks modified files and --fail-on-changes was used!`
|
|
362
|
+
)
|
|
363
|
+
return
|
|
364
|
+
}
|
|
365
|
+
}
|
|
340
366
|
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
367
|
+
// This looks confusing but the intent is to only normalize truthy values
|
|
368
|
+
const activeIndexFile = process.env.GIT_INDEX_FILE
|
|
369
|
+
? normalizePath(process.env.GIT_INDEX_FILE)
|
|
370
|
+
: process.env.GIT_INDEX_FILE
|
|
371
|
+
|
|
372
|
+
/** Needs to be run serially because of locking Git operation */
|
|
373
|
+
for (const files of this.matchedFileChunks) {
|
|
374
|
+
const accessCheckedFiles = await Promise.allSettled(
|
|
375
|
+
files.map(async (f) => {
|
|
376
|
+
if (f.status === 'D') {
|
|
377
|
+
await fs.access(f.filepath)
|
|
378
|
+
return f.filepath // File is no longer deleted and can be added
|
|
379
|
+
} else {
|
|
380
|
+
return f.filepath
|
|
381
|
+
}
|
|
352
382
|
})
|
|
353
|
-
|
|
383
|
+
)
|
|
384
|
+
|
|
385
|
+
const addableFiles = accessCheckedFiles.flatMap((r) =>
|
|
386
|
+
r.status === 'fulfilled' ? [r.value] : []
|
|
387
|
+
)
|
|
388
|
+
|
|
389
|
+
debugLog('Updating active Git index: %s', activeIndexFile)
|
|
390
|
+
await this.execGit(['add', '--', ...addableFiles])
|
|
391
|
+
debugLog('Done updating Git index: %s', activeIndexFile)
|
|
392
|
+
|
|
393
|
+
if (activeIndexFile?.endsWith('.lock')) {
|
|
394
|
+
const defaultIndexLock = normalizePath(
|
|
395
|
+
await this.execGit(['rev-parse', '--path-format=absolute', '--git-path', 'index.lock'])
|
|
396
|
+
)
|
|
397
|
+
|
|
398
|
+
/**
|
|
399
|
+
* If the active index file is a non-default lockfile, we are committing with a pathspec
|
|
400
|
+
* without having explicitly run `git add`. In this case we need to also update the
|
|
401
|
+
* default index, otherwise there will be leftover diff after committing
|
|
402
|
+
*/
|
|
403
|
+
if (activeIndexFile !== defaultIndexLock) {
|
|
404
|
+
debugLog('Updating default Git index again: %s', defaultIndexLock)
|
|
405
|
+
await this.execGit(['add', '--', ...addableFiles], {
|
|
406
|
+
env: {
|
|
407
|
+
GIT_INDEX_FILE: defaultIndexLock,
|
|
408
|
+
},
|
|
409
|
+
})
|
|
410
|
+
debugLog('Done updating default Git index lock: %s', defaultIndexLock)
|
|
411
|
+
}
|
|
354
412
|
}
|
|
355
413
|
}
|
|
356
|
-
}
|
|
357
414
|
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
415
|
+
const stagedFilesAfterAdd = await this.execGit([
|
|
416
|
+
...getDiffCommand(this.diff, this.diffFilter),
|
|
417
|
+
'--name-only',
|
|
418
|
+
'-z',
|
|
419
|
+
])
|
|
420
|
+
|
|
421
|
+
if (!stagedFilesAfterAdd && !this.allowEmpty) {
|
|
422
|
+
// Tasks reverted all staged changes and the commit would be empty
|
|
423
|
+
// Stop commit unless `--allow-empty` was used
|
|
424
|
+
ctx.errors.add(ApplyEmptyCommitError)
|
|
425
|
+
this.logger.error(`${figures.error()} Prevented an empty git commit!`)
|
|
426
|
+
} else {
|
|
427
|
+
this.logger.log(`${figures.done()} Done staging changes from tasks!`)
|
|
428
|
+
}
|
|
429
|
+
} catch (error) {
|
|
430
|
+
ctx.errors.add(GitError)
|
|
363
431
|
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
// Throw error to stop commit unless `--allow-empty` was used
|
|
367
|
-
handleError(new Error('Prevented an empty git commit!'), ctx, ApplyEmptyCommitError)
|
|
432
|
+
this.logger.log(`${figures.error()} Failed to stage changes from tasks!`)
|
|
433
|
+
debugLog(error)
|
|
368
434
|
}
|
|
369
435
|
}
|
|
370
436
|
|
|
@@ -374,8 +440,10 @@ export class GitWorkflow {
|
|
|
374
440
|
* 3-way merge usually fixes this, and in case it doesn't we should just give up and throw.
|
|
375
441
|
*/
|
|
376
442
|
async restoreUnstagedChanges(ctx) {
|
|
377
|
-
|
|
443
|
+
this.logger.log(dim(`${figures.wip()} Restoring unstaged changes…`))
|
|
444
|
+
|
|
378
445
|
const unstagedPatch = this.getHiddenFilepath(PATCH_UNSTAGED)
|
|
446
|
+
|
|
379
447
|
try {
|
|
380
448
|
await this.execGit(['apply', ...GIT_APPLY_ARGS, unstagedPatch])
|
|
381
449
|
} catch (applyError) {
|
|
@@ -386,20 +454,19 @@ export class GitWorkflow {
|
|
|
386
454
|
try {
|
|
387
455
|
await this.execGit(['apply', ...GIT_APPLY_ARGS, '--3way', unstagedPatch])
|
|
388
456
|
} catch (threeWayApplyError) {
|
|
389
|
-
|
|
457
|
+
ctx.errors.add(GitError)
|
|
458
|
+
ctx.errors.add(RestoreUnstagedChangesError)
|
|
459
|
+
|
|
460
|
+
this.logger.error(`${figures.error()} Failed to restore unstaged changes!`)
|
|
390
461
|
debugLog(threeWayApplyError)
|
|
391
|
-
handleError(
|
|
392
|
-
new Error('Unstaged changes could not be restored due to a merge conflict!'),
|
|
393
|
-
ctx,
|
|
394
|
-
RestoreUnstagedChangesError
|
|
395
|
-
)
|
|
396
462
|
}
|
|
397
463
|
}
|
|
398
464
|
}
|
|
399
465
|
|
|
400
466
|
async restoreUntrackedFiles(ctx) {
|
|
467
|
+
this.logger.log(dim(`${figures.wip()} Restoring untracked files…`))
|
|
468
|
+
|
|
401
469
|
try {
|
|
402
|
-
debugLog('Restoring untracked files...')
|
|
403
470
|
const backupStash = await this.getBackupStash(ctx)
|
|
404
471
|
const untrackedFiles = await this.execGit([
|
|
405
472
|
'stash',
|
|
@@ -419,17 +486,17 @@ export class GitWorkflow {
|
|
|
419
486
|
'--',
|
|
420
487
|
...untrackedFiles.map(normalizePath),
|
|
421
488
|
])
|
|
489
|
+
|
|
490
|
+
this.logger.log(`${figures.done()} ${'Done restoring untracked files!'}`)
|
|
422
491
|
} else {
|
|
423
|
-
|
|
492
|
+
this.logger.log(`${figures.cancelled()} ${dim('No untracked files to restore!')}`)
|
|
424
493
|
}
|
|
425
494
|
} catch (restoreUntrackedFilesError) {
|
|
426
|
-
|
|
495
|
+
ctx.errors.add(GitError)
|
|
496
|
+
ctx.errors.add(RestoreUnstagedChangesError)
|
|
497
|
+
|
|
498
|
+
this.logger.error(`${figures.error()} ${'Failed to restore untracked files!'}`)
|
|
427
499
|
debugLog(restoreUntrackedFilesError)
|
|
428
|
-
handleError(
|
|
429
|
-
new Error('Untracked files could not be restored!'),
|
|
430
|
-
ctx,
|
|
431
|
-
RestoreUnstagedChangesError
|
|
432
|
-
)
|
|
433
500
|
}
|
|
434
501
|
}
|
|
435
502
|
|
|
@@ -437,6 +504,8 @@ export class GitWorkflow {
|
|
|
437
504
|
* Restore original HEAD state in case of errors
|
|
438
505
|
*/
|
|
439
506
|
async restoreOriginalState(ctx) {
|
|
507
|
+
this.logger.log(dim(`${figures.wip()} Reverting to original state because of errors…`))
|
|
508
|
+
|
|
440
509
|
try {
|
|
441
510
|
debugLog('Restoring original state...')
|
|
442
511
|
await this.execGit(['reset', '--hard', 'HEAD'])
|
|
@@ -447,9 +516,13 @@ export class GitWorkflow {
|
|
|
447
516
|
// Clean out patch
|
|
448
517
|
await unlink(this.getHiddenFilepath(PATCH_UNSTAGED))
|
|
449
518
|
|
|
450
|
-
|
|
519
|
+
this.logger.log(`${figures.done()} Done reverting to original state!`)
|
|
451
520
|
} catch (error) {
|
|
452
|
-
|
|
521
|
+
ctx.errors.add(GitError)
|
|
522
|
+
ctx.errors.add(RestoreOriginalStateError)
|
|
523
|
+
|
|
524
|
+
this.logger.error(`${figures.error()} Failed to revert to original state!`)
|
|
525
|
+
debugLog(error)
|
|
453
526
|
}
|
|
454
527
|
}
|
|
455
528
|
|
|
@@ -457,12 +530,15 @@ export class GitWorkflow {
|
|
|
457
530
|
* Drop the created stashes after everything has run
|
|
458
531
|
*/
|
|
459
532
|
async cleanup(ctx) {
|
|
533
|
+
this.logger.log(dim(`${figures.wip()} Cleaning up temporary files…`))
|
|
534
|
+
|
|
460
535
|
try {
|
|
461
|
-
debugLog('Dropping backup stash...')
|
|
462
536
|
await this.execGit(['stash', 'drop', '--quiet', await this.getBackupStash(ctx)])
|
|
463
|
-
|
|
537
|
+
this.logger.log(`${figures.done()} Done cleaning up temporary files!`)
|
|
464
538
|
} catch (error) {
|
|
465
|
-
|
|
539
|
+
ctx.errors.add(GitError)
|
|
540
|
+
this.logger.error(`${figures.error()} Failed to clean up temporary files!`)
|
|
541
|
+
debugLog(error)
|
|
466
542
|
}
|
|
467
543
|
}
|
|
468
544
|
}
|
package/lib/index.js
CHANGED
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
import { assertGitVersion, MIN_GIT_VERSION } from './assertGitVersion.js'
|
|
2
|
-
import {
|
|
2
|
+
import { enableColors } from './colors.js'
|
|
3
3
|
import { createDebug, enableDebug } from './debug.js'
|
|
4
4
|
import { execGit } from './execGit.js'
|
|
5
5
|
import {
|
|
6
|
-
|
|
6
|
+
gitError,
|
|
7
7
|
minGitVersionRequired,
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
8
|
+
noConfiguration,
|
|
9
|
+
preventedEmptyCommit,
|
|
10
|
+
preventedTaskModifications,
|
|
11
11
|
restoreStashExample,
|
|
12
|
-
|
|
12
|
+
unstagedChangesBackupStashLocation,
|
|
13
13
|
} from './messages.js'
|
|
14
14
|
import { printTaskOutput } from './printTaskOutput.js'
|
|
15
15
|
import { runAll } from './runAll.js'
|
|
@@ -78,7 +78,7 @@ const getMaxArgLength = () => {
|
|
|
78
78
|
const lintStaged = async (
|
|
79
79
|
{
|
|
80
80
|
allowEmpty = false,
|
|
81
|
-
color =
|
|
81
|
+
color = !!process.stdout.hasColors?.(),
|
|
82
82
|
concurrent = true,
|
|
83
83
|
config: configObject,
|
|
84
84
|
configPath,
|
|
@@ -102,6 +102,8 @@ const lintStaged = async (
|
|
|
102
102
|
} = {},
|
|
103
103
|
logger = console
|
|
104
104
|
) => {
|
|
105
|
+
enableColors(color)
|
|
106
|
+
|
|
105
107
|
// Seemingly enable debug twice (also done in bin), so that it also works when using the Node.js API
|
|
106
108
|
if (debug) {
|
|
107
109
|
enableDebug(logger)
|
|
@@ -160,20 +162,20 @@ const lintStaged = async (
|
|
|
160
162
|
const { ctx } = runAllError
|
|
161
163
|
|
|
162
164
|
if (ctx.errors.has(ConfigNotFoundError)) {
|
|
163
|
-
logger.error(
|
|
165
|
+
logger.error(noConfiguration())
|
|
164
166
|
} else if (ctx.errors.has(ApplyEmptyCommitError)) {
|
|
165
|
-
logger.warn(
|
|
167
|
+
logger.warn(preventedEmptyCommit())
|
|
166
168
|
} else if (ctx.errors.has(FailOnChangesError)) {
|
|
167
|
-
logger.warn(
|
|
169
|
+
logger.warn(preventedTaskModifications() + '\n')
|
|
168
170
|
logger.warn(restoreStashExample(ctx.backupHash))
|
|
169
171
|
} else if (ctx.errors.has(RestoreUnstagedChangesError)) {
|
|
170
|
-
logger.warn(
|
|
172
|
+
logger.warn(unstagedChangesBackupStashLocation())
|
|
171
173
|
logger.warn(ctx.unstagedPatch)
|
|
172
174
|
} else if (
|
|
173
175
|
(ctx.errors.has(GitError) || cleanupSkipped(ctx)) &&
|
|
174
176
|
!ctx.errors.has(GetBackupStashError)
|
|
175
177
|
) {
|
|
176
|
-
logger.error(
|
|
178
|
+
logger.error(gitError())
|
|
177
179
|
if (ctx.shouldBackup) {
|
|
178
180
|
// No sense to show this if the backup stash itself is missing.
|
|
179
181
|
logger.error(restoreStashExample(ctx.backupHash) + '\n')
|
package/lib/messages.js
CHANGED
|
@@ -1,30 +1,32 @@
|
|
|
1
1
|
import { inspect } from 'node:util'
|
|
2
2
|
|
|
3
3
|
import { bold, red, yellow } from './colors.js'
|
|
4
|
-
import
|
|
4
|
+
import * as figures from './figures.js'
|
|
5
5
|
|
|
6
6
|
export const configurationError = (opt, helpMsg, value) =>
|
|
7
|
-
`${red(`${error} Validation Error:`)}
|
|
7
|
+
`${red(`${figures.error()} Validation Error:`)}
|
|
8
8
|
|
|
9
9
|
Invalid value for '${bold(opt)}': ${bold(inspect(value))}
|
|
10
10
|
|
|
11
11
|
${helpMsg}`
|
|
12
12
|
|
|
13
|
-
export const
|
|
13
|
+
export const notGitRepo = () => red(`${figures.error()} Current directory is not a git directory!`)
|
|
14
14
|
|
|
15
|
-
export const
|
|
15
|
+
export const failedGetStagedFiles = () => red(`${figures.error()} Failed to get staged files!`)
|
|
16
16
|
|
|
17
17
|
export const incorrectBraces = (before, after) =>
|
|
18
18
|
yellow(
|
|
19
|
-
`${warning} Detected incorrect braces with only single value: \`${before}\`. Reformatted as: \`${after}\`
|
|
19
|
+
`${figures.warning()} Detected incorrect braces with only single value: \`${before}\`. Reformatted as: \`${after}\`
|
|
20
20
|
`
|
|
21
21
|
)
|
|
22
22
|
|
|
23
|
-
export const
|
|
23
|
+
export const noConfiguration = () =>
|
|
24
|
+
`${figures.error()} lint-staged could not find any valid configuration.`
|
|
24
25
|
|
|
25
|
-
export const
|
|
26
|
+
export const noStagedFiles = () => `${figures.info()} lint-staged could not find any staged files.`
|
|
26
27
|
|
|
27
|
-
export const
|
|
28
|
+
export const noTasks = () =>
|
|
29
|
+
`${figures.info()} lint-staged could not find any staged files matching configured tasks.`
|
|
28
30
|
|
|
29
31
|
export const skippingBackup = (hasInitialCommit, diff) => {
|
|
30
32
|
const reason =
|
|
@@ -33,27 +35,33 @@ export const skippingBackup = (hasInitialCommit, diff) => {
|
|
|
33
35
|
: (hasInitialCommit ? '`--no-stash` was used' : 'there’s no initial commit yet') +
|
|
34
36
|
'. This might result in data loss'
|
|
35
37
|
|
|
36
|
-
return yellow(`${warning} Skipping backup because ${reason}.\n`)
|
|
38
|
+
return yellow(`${figures.warning()} Skipping backup because ${reason}.\n`)
|
|
37
39
|
}
|
|
38
40
|
|
|
39
|
-
export const
|
|
40
|
-
|
|
41
|
-
)
|
|
41
|
+
export const skippingHidePartiallyChanged = () =>
|
|
42
|
+
yellow(
|
|
43
|
+
`${figures.warning()} Skipping hiding unstaged changes from partially staged files because \`--no-hide-partially-staged\` was used.\n`
|
|
44
|
+
)
|
|
42
45
|
|
|
43
|
-
export const
|
|
44
|
-
|
|
46
|
+
export const deprecatedGitAdd = () =>
|
|
47
|
+
yellow(
|
|
48
|
+
`${figures.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.
|
|
45
49
|
`
|
|
46
|
-
)
|
|
47
|
-
|
|
48
|
-
export const TASK_ERROR = 'Skipped because of errors from tasks.'
|
|
50
|
+
)
|
|
49
51
|
|
|
50
|
-
export const
|
|
52
|
+
export const taskError = () => 'Skipped because of errors from tasks.'
|
|
51
53
|
|
|
52
|
-
export const
|
|
54
|
+
export const preventedTaskModifications = () =>
|
|
55
|
+
`\n${figures.error()} lint-staged failed because \`--fail-on-changes\` was used.`
|
|
53
56
|
|
|
54
|
-
export const
|
|
57
|
+
export const gitError = () =>
|
|
58
|
+
`\n ${red(`${figures.error()} lint-staged failed due to a git error.`)}`
|
|
55
59
|
|
|
56
|
-
export const invalidOption = (
|
|
60
|
+
export const invalidOption = (
|
|
61
|
+
name,
|
|
62
|
+
value,
|
|
63
|
+
message
|
|
64
|
+
) => `${red(`${figures.error()} Validation Error:`)}
|
|
57
65
|
|
|
58
66
|
Invalid value for option '${bold(name)}': ${bold(value)}
|
|
59
67
|
|
|
@@ -61,8 +69,8 @@ export const invalidOption = (name, value, message) => `${red(`${error} Validati
|
|
|
61
69
|
|
|
62
70
|
See https://github.com/lint-staged/lint-staged#command-line-flags`
|
|
63
71
|
|
|
64
|
-
export const
|
|
65
|
-
${yellow(`${warning} lint-staged prevented an empty git commit.
|
|
72
|
+
export const preventedEmptyCommit = () => `
|
|
73
|
+
${yellow(`${figures.warning()} lint-staged prevented an empty git commit.
|
|
66
74
|
Use the --allow-empty option to continue, or check your task configuration`)}
|
|
67
75
|
`
|
|
68
76
|
|
|
@@ -75,23 +83,22 @@ export const restoreStashExample = (
|
|
|
75
83
|
> git apply --index ${hash}
|
|
76
84
|
`
|
|
77
85
|
|
|
78
|
-
export const CONFIG_STDIN_ERROR = red(`${error} Failed to read config from stdin.`)
|
|
79
|
-
|
|
80
86
|
export const failedToLoadConfig = (filepath) =>
|
|
81
|
-
red(`${error} Failed to read config from file "${filepath}".`)
|
|
87
|
+
red(`${figures.error()} Failed to read config from file "${filepath}".`)
|
|
82
88
|
|
|
83
89
|
export const failedToParseConfig = (
|
|
84
90
|
filepath,
|
|
85
91
|
error
|
|
86
|
-
) => `${red(`${error} Failed to parse config from file "${filepath}".`)}
|
|
92
|
+
) => `${red(`${figures.error()} Failed to parse config from file "${filepath}".`)}
|
|
87
93
|
|
|
88
94
|
${error}
|
|
89
95
|
|
|
90
96
|
See https://github.com/lint-staged/lint-staged#configuration.`
|
|
91
97
|
|
|
92
|
-
export const
|
|
98
|
+
export const unstagedChangesBackupStashLocation = () =>
|
|
99
|
+
'Unstaged changes have been kept back in a patch file:'
|
|
93
100
|
|
|
94
101
|
export const minGitVersionRequired = (expected) =>
|
|
95
|
-
red(`${error} lint-staged requires at least Git version ${bold(expected)}.
|
|
102
|
+
red(`${figures.error()} lint-staged requires at least Git version ${bold(expected)}.
|
|
96
103
|
|
|
97
104
|
Please update Git: https://git-scm.com/downloads`)
|