@depup/lint-staged 16.3.2-depup.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/lib/runAll.js ADDED
@@ -0,0 +1,372 @@
1
+ /** @typedef {import('./index').Logger} Logger */
2
+
3
+ import path from 'node:path'
4
+
5
+ import { Listr } from 'listr2'
6
+
7
+ import { chunkFiles } from './chunkFiles.js'
8
+ import { blackBright } from './colors.js'
9
+ import { createDebug } from './debug.js'
10
+ import { execGit } from './execGit.js'
11
+ import { generateTasks } from './generateTasks.js'
12
+ import { getAbortController } from './getAbortController.js'
13
+ import { getFunctionTask, isFunctionTask } from './getFunctionTask.js'
14
+ import { getRenderer } from './getRenderer.js'
15
+ import { getSpawnedTasks } from './getSpawnedTasks.js'
16
+ import { getStagedFiles } from './getStagedFiles.js'
17
+ import { GitWorkflow } from './gitWorkflow.js'
18
+ import { groupFilesByConfig } from './groupFilesByConfig.js'
19
+ import {
20
+ DEPRECATED_GIT_ADD,
21
+ FAILED_GET_STAGED_FILES,
22
+ NO_STAGED_FILES,
23
+ NO_TASKS,
24
+ NOT_GIT_REPO,
25
+ SKIPPED_GIT_ERROR,
26
+ SKIPPING_HIDE_PARTIALLY_CHANGED,
27
+ skippingBackup,
28
+ } from './messages.js'
29
+ import { normalizePath } from './normalizePath.js'
30
+ import { resolveGitRepo } from './resolveGitRepo.js'
31
+ import { searchConfigs } from './searchConfigs.js'
32
+ import {
33
+ applyModificationsSkipped,
34
+ cleanupEnabled,
35
+ cleanupSkipped,
36
+ getInitialState,
37
+ restoreOriginalStateEnabled,
38
+ restoreOriginalStateSkipped,
39
+ restoreUnstagedChangesSkipped,
40
+ shouldHidePartiallyStagedFiles,
41
+ shouldRestoreUnstagedChanges,
42
+ } from './state.js'
43
+ import { ConfigNotFoundError, GetStagedFilesError, GitError, GitRepoError } from './symbols.js'
44
+
45
+ const debugLog = createDebug('lint-staged:runAll')
46
+
47
+ /**
48
+ * @param {ReturnType<typeof getInitialState>} ctx context
49
+ * @param {unknown} cause error cause
50
+ */
51
+ const createError = (ctx, cause) =>
52
+ Object.assign(new Error('lint-staged failed', { cause }), { ctx })
53
+
54
+ /**
55
+ * Executes all tasks and either resolves or rejects the promise
56
+ *
57
+ * @param {object} options
58
+ * @param {boolean} [options.allowEmpty] - Allow empty commits when tasks revert all staged changes
59
+ * @param {boolean} [options.color] - Enable or disable ANSI color codes in output.
60
+ * @param {boolean | number} [options.concurrent] - The number of tasks to run concurrently, or false to run tasks serially
61
+ * @param {Object} [options.configObject] - Explicit config object from the js API
62
+ * @param {string} [options.configPath] - Explicit path to a config file
63
+ * @param {boolean} [options.continueOnError] - Run all tasks to completion even if one fails
64
+ * @param {string} [options.cwd] - Current working directory
65
+ * @param {boolean} [options.debug] - Enable debug mode
66
+ * @param {string} [options.diff] - Override the default "--staged" flag of "git diff" to get list of files
67
+ * @param {string} [options.diffFilter] - Override the default "--diff-filter=ACMR" flag of "git diff" to get list of files
68
+ * @param {boolean} [options.failOnChanges] - Fail with exit code 1 when tasks modify tracked files
69
+ * @param {boolean} [options.hidePartiallyStaged] - Whether to hide unstaged changes from partially staged files before running tasks
70
+ * @param {boolean} [options.hideUnstaged] - Whether to hide all unstaged changes before running tasks
71
+ * @param {number} [options.maxArgLength] - Maximum argument string length
72
+ * @param {boolean} [options.quiet] - Disable lint-staged's own console output
73
+ * @param {boolean} [options.relative] - Pass relative filepaths to tasks
74
+ * @param {boolean} [options.revert] - revert to original state in case of errors
75
+ * @param {boolean} [options.stash] - Enable the backup stash, and revert in case of errors
76
+ * @param {boolean} [options.verbose] - Show task output even when tasks succeed; by default only failed output is shown
77
+ * @param {Logger} logger
78
+ * @returns {Promise}
79
+ */
80
+ export const runAll = async (
81
+ {
82
+ allowEmpty = false,
83
+ color = false,
84
+ concurrent = true,
85
+ configObject,
86
+ configPath,
87
+ continueOnError = false,
88
+ cwd,
89
+ debug = false,
90
+ diff,
91
+ diffFilter,
92
+ failOnChanges = false,
93
+ hideUnstaged = false,
94
+ hidePartiallyStaged = !hideUnstaged,
95
+ maxArgLength,
96
+ quiet = false,
97
+ relative = false,
98
+ // Stashing should be disabled by default when the `diff` option is used
99
+ stash = diff === undefined,
100
+ // Cannot revert to original state without stash
101
+ revert = stash,
102
+ verbose = false,
103
+ },
104
+ logger = console
105
+ ) => {
106
+ debugLog('Running all linter scripts...')
107
+
108
+ // Resolve relative CWD option
109
+ const hasExplicitCwd = !!cwd
110
+ cwd = hasExplicitCwd ? path.resolve(cwd) : process.cwd()
111
+ debugLog('Using working directory `%s`', cwd)
112
+
113
+ const ctx = getInitialState({
114
+ failOnChanges,
115
+ hidePartiallyStaged,
116
+ hideUnstaged,
117
+ quiet,
118
+ revert,
119
+ })
120
+
121
+ const { topLevelDir, gitConfigDir } = await resolveGitRepo(cwd)
122
+ if (!topLevelDir) {
123
+ if (!quiet) ctx.output.push(NOT_GIT_REPO)
124
+ ctx.errors.add(GitRepoError)
125
+ throw createError(ctx, GitRepoError)
126
+ }
127
+
128
+ // Test whether we have any commits or not.
129
+ // Stashing must be disabled with no initial commit.
130
+ const hasInitialCommit = await execGit(['log', '-1'], { cwd: topLevelDir })
131
+ .then(() => true)
132
+ .catch(() => false)
133
+
134
+ // Lint-staged will create a backup stash only when there's an initial commit,
135
+ // and when using the default list of staged files by default
136
+ ctx.shouldBackup = hasInitialCommit && stash
137
+ if (!ctx.shouldBackup && !quiet) {
138
+ logger.warn(skippingBackup(hasInitialCommit, diff))
139
+ }
140
+
141
+ if (!ctx.shouldHidePartiallyStaged && !ctx.shouldHideUnstaged && !quiet) {
142
+ logger.warn(SKIPPING_HIDE_PARTIALLY_CHANGED)
143
+ }
144
+
145
+ // Run staged files retrieval and config search in parallel since they're independent
146
+ const [stagedFiles, foundConfigs] = await Promise.all([
147
+ getStagedFiles({ cwd: topLevelDir, diff, diffFilter }),
148
+ searchConfigs({ configObject, configPath, cwd, topLevelDir }, logger),
149
+ ])
150
+
151
+ if (!stagedFiles) {
152
+ if (!quiet) ctx.output.push(FAILED_GET_STAGED_FILES)
153
+ ctx.errors.add(GetStagedFilesError)
154
+ throw createError(ctx, GetStagedFilesError)
155
+ }
156
+ debugLog('Loaded list of staged files in git:\n%O', stagedFiles)
157
+
158
+ // If there are no files avoid executing any lint-staged logic
159
+ if (stagedFiles.length === 0) {
160
+ if (!quiet) ctx.output.push(NO_STAGED_FILES)
161
+ return ctx
162
+ }
163
+
164
+ const numberOfConfigs = Object.keys(foundConfigs).length
165
+
166
+ // Throw if no configurations were found
167
+ if (numberOfConfigs === 0) {
168
+ ctx.errors.add(ConfigNotFoundError)
169
+ throw createError(ctx, ConfigNotFoundError)
170
+ }
171
+
172
+ const filesByConfig = await groupFilesByConfig({
173
+ configs: foundConfigs,
174
+ files: stagedFiles,
175
+ singleConfigMode: configObject || configPath !== undefined,
176
+ })
177
+
178
+ const hasMultipleConfigs = numberOfConfigs > 1
179
+
180
+ // lint-staged 10 will automatically add modifications to index
181
+ // Warn user when their command includes `git add`
182
+ let hasDeprecatedGitAdd = false
183
+
184
+ const listrOptions = {
185
+ ctx,
186
+ exitOnError: false,
187
+ registerSignalListeners: false,
188
+ ...getRenderer({ color, debug, quiet }, logger),
189
+ }
190
+
191
+ const listrTasks = []
192
+
193
+ // Set of all staged files that matched a task glob. Values in a set are unique.
194
+ /** @type {Set<import('./getStagedFiles.js').StagedFile>} */
195
+ const matchedFiles = new Set()
196
+
197
+ const abortController = getAbortController()
198
+
199
+ for (const [configPath, { config, files }] of Object.entries(filesByConfig)) {
200
+ const configName = configPath ? normalizePath(path.relative(cwd, configPath)) : 'Config object'
201
+
202
+ const stagedFileChunks = chunkFiles({ baseDir: topLevelDir, files, maxArgLength, relative })
203
+
204
+ // Use actual cwd if it's specified, or there's only a single config file.
205
+ // Otherwise use the directory of the config file for each config group,
206
+ // to make sure tasks are separated from each other.
207
+ const groupCwd = hasMultipleConfigs && !hasExplicitCwd ? path.dirname(configPath) : cwd
208
+
209
+ const chunkCount = stagedFileChunks.length
210
+ if (chunkCount > 1) {
211
+ debugLog('Chunked staged files from `%s` into %d part', configPath, chunkCount)
212
+ }
213
+
214
+ for (const [index, files] of stagedFileChunks.entries()) {
215
+ const chunkListrTasks = await Promise.all(
216
+ generateTasks({ config, cwd: groupCwd, files, relative }).map((task) =>
217
+ (isFunctionTask(task.commands)
218
+ ? getFunctionTask(task.commands, task.fileList)
219
+ : getSpawnedTasks({
220
+ abortController,
221
+ color,
222
+ commands: task.commands,
223
+ continueOnError,
224
+ cwd: groupCwd,
225
+ files: task.fileList,
226
+ topLevelDir,
227
+ verbose,
228
+ })
229
+ ).then((subTasks) => {
230
+ // Add files from task to match set
231
+ task.fileList.forEach((file) => {
232
+ // Make sure relative files are normalized to the
233
+ // group cwd, because other there might be identical
234
+ // relative filenames in the entire set.
235
+ const normalizedFile = path.isAbsolute(file.filepath)
236
+ ? file
237
+ : {
238
+ filepath: normalizePath(path.join(groupCwd, file.filepath)),
239
+ status: file.status,
240
+ }
241
+
242
+ matchedFiles.add(normalizedFile)
243
+ })
244
+
245
+ hasDeprecatedGitAdd =
246
+ hasDeprecatedGitAdd || subTasks.some((subTask) => subTask.command === 'git add')
247
+
248
+ const fileCount = task.fileList.length
249
+
250
+ return {
251
+ title: `${task.pattern}${blackBright(
252
+ ` — ${fileCount} ${fileCount === 1 ? 'file' : 'files'}`
253
+ )}`,
254
+ task: async (ctx, task) =>
255
+ task.newListr(
256
+ subTasks,
257
+ // Subtasks should not run in parallel, and should exit on error
258
+ { concurrent: false, exitOnError: !continueOnError }
259
+ ),
260
+ skip: () => {
261
+ // Skip task when no files matched
262
+ if (fileCount === 0) {
263
+ return `${task.pattern}${blackBright(' — no files')}`
264
+ }
265
+ return false
266
+ },
267
+ }
268
+ })
269
+ )
270
+ )
271
+
272
+ listrTasks.push({
273
+ title:
274
+ `${configName}${blackBright(` — ${files.length} ${files.length > 1 ? 'files' : 'file'}`)}` +
275
+ (chunkCount > 1 ? blackBright(` (chunk ${index + 1}/${chunkCount})...`) : ''),
276
+ task: (ctx, task) =>
277
+ task.newListr(chunkListrTasks, { concurrent, exitOnError: !continueOnError }),
278
+ skip: () => {
279
+ // Skip if the first step (backup) failed
280
+ if (ctx.errors.has(GitError)) return SKIPPED_GIT_ERROR
281
+ // Skip chunk when no every task is skipped (due to no matches)
282
+ if (chunkListrTasks.every((task) => task.skip())) {
283
+ return `${configName}${blackBright(' — no tasks to run')}`
284
+ }
285
+ return false
286
+ },
287
+ })
288
+ }
289
+ }
290
+
291
+ if (hasDeprecatedGitAdd && !quiet) {
292
+ logger.warn(DEPRECATED_GIT_ADD)
293
+ }
294
+
295
+ // If all of the configured tasks should be skipped
296
+ // avoid executing any lint-staged logic
297
+ if (listrTasks.every((task) => task.skip())) {
298
+ if (!quiet) ctx.output.push(NO_TASKS)
299
+ return ctx
300
+ }
301
+
302
+ // Chunk matched files for better Windows compatibility
303
+ /** @type {import('./getStagedFiles.js').StagedFile[][]} */
304
+ const matchedFileChunks = chunkFiles({
305
+ // matched files are relative to `cwd`, not `topLevelDir`, when `relative` is used
306
+ baseDir: cwd,
307
+ files: Array.from(matchedFiles),
308
+ maxArgLength,
309
+ relative: false,
310
+ })
311
+
312
+ const git = new GitWorkflow({
313
+ allowEmpty,
314
+ diff,
315
+ diffFilter,
316
+ failOnChanges,
317
+ gitConfigDir,
318
+ matchedFileChunks,
319
+ topLevelDir,
320
+ })
321
+
322
+ const runner = new Listr(
323
+ [
324
+ {
325
+ title: ctx.shouldBackup ? 'Backing up original state...' : 'Preparing lint-staged...',
326
+ task: (ctx, task) => git.prepare(ctx, task),
327
+ },
328
+ {
329
+ title: 'Hiding unstaged changes to partially staged files...',
330
+ task: (ctx) => git.hidePartiallyStagedChanges(ctx),
331
+ enabled: shouldHidePartiallyStagedFiles,
332
+ },
333
+ {
334
+ title: `Running tasks for ${diff ? 'changed' : 'staged'} files...`,
335
+ task: (ctx, task) => git.runTasks(ctx, task, { listrTasks, concurrent }),
336
+ skip: () => listrTasks.every((task) => task.skip()),
337
+ },
338
+ {
339
+ title: 'Applying modifications from tasks...',
340
+ task: (ctx) => git.applyModifications(ctx),
341
+ skip: applyModificationsSkipped,
342
+ },
343
+ {
344
+ title: 'Restoring unstaged changes...',
345
+ task: (ctx) => git.restoreUnstagedChanges(ctx),
346
+ enabled: shouldRestoreUnstagedChanges,
347
+ skip: restoreUnstagedChangesSkipped,
348
+ },
349
+ {
350
+ title: 'Reverting to original state because of errors...',
351
+ task: (ctx) => git.restoreOriginalState(ctx),
352
+ enabled: restoreOriginalStateEnabled,
353
+ skip: restoreOriginalStateSkipped,
354
+ },
355
+ {
356
+ title: 'Cleaning up temporary files...',
357
+ task: (ctx) => git.cleanup(ctx),
358
+ enabled: cleanupEnabled,
359
+ skip: cleanupSkipped,
360
+ },
361
+ ],
362
+ listrOptions
363
+ )
364
+
365
+ await runner.run()
366
+
367
+ if (ctx.errors.size > 0) {
368
+ throw createError(ctx)
369
+ }
370
+
371
+ return ctx
372
+ }
@@ -0,0 +1,186 @@
1
+ /** @typedef {import('./index').Logger} Logger */
2
+
3
+ import fs, { constants } from 'node:fs/promises'
4
+ import path from 'node:path'
5
+
6
+ import { CONFIG_FILE_NAMES } from './configFiles.js'
7
+ import { createDebug } from './debug.js'
8
+ import { execGit } from './execGit.js'
9
+ import { loadConfig } from './loadConfig.js'
10
+ import { normalizePath } from './normalizePath.js'
11
+ import { parseGitZOutput } from './parseGitZOutput.js'
12
+ import { validateConfig } from './validateConfig.js'
13
+
14
+ const debugLog = createDebug('lint-staged:searchConfigs')
15
+
16
+ const EXEC_GIT = ['ls-files', '-z', '--full-name', '-t']
17
+
18
+ const CONFIG_PATHSPEC = CONFIG_FILE_NAMES.map((f) => `:(glob)**/${f}`)
19
+
20
+ const numberOfLevels = (file) => file.split('/').length
21
+
22
+ const sortAlphabetically = (a, b) => a.localeCompare(b)
23
+
24
+ const sortDeepestParth = (a, b) => (numberOfLevels(a) > numberOfLevels(b) ? -1 : 1)
25
+
26
+ /**
27
+ * Get all possible config files from git
28
+ *
29
+ * @param {object} options
30
+ * @param {string} options.cwd
31
+ * @param {string} options.topLevelDir
32
+ * @returns {Promise<string[]>}
33
+ */
34
+ const listConfigFilesFromGit = async ({ cwd, topLevelDir }) =>
35
+ execGit(
36
+ [
37
+ ...EXEC_GIT,
38
+ '--cached', // show all tracked files
39
+ '--others', // show untracked files
40
+ '--exclude-standard', // apply standard git exclusions (.gitignore, etc.)
41
+ '--',
42
+ ...CONFIG_PATHSPEC,
43
+ ],
44
+ { cwd }
45
+ )
46
+ .then(parseGitZOutput)
47
+ .then((lines) => {
48
+ const possibleConfigFiles = lines.flatMap((line) => {
49
+ /**
50
+ * Leave out lines starting with "S " to ignore not-checked-out files in a sparse repo.
51
+ * The "S" status means a tracked file that is "skip-worktree"
52
+ * @see https://git-scm.com/docs/git-ls-files#Documentation/git-ls-files.txt--t
53
+ */
54
+ if (line.startsWith('S ')) {
55
+ return []
56
+ }
57
+
58
+ const relativePath = line.replace(/^[HSMRCK?U] /, '')
59
+ const absolutePath = normalizePath(path.join(topLevelDir, relativePath))
60
+ return [absolutePath]
61
+ })
62
+
63
+ debugLog('Found possible config files from git:', possibleConfigFiles)
64
+
65
+ return possibleConfigFiles
66
+ })
67
+
68
+ /**
69
+ * Get all possible config files from filesystem, starting from `cwd` and
70
+ * moving upwards if nothing is found.
71
+ *
72
+ * @param {object} options
73
+ * @param {string} options.cwd
74
+ * @param {string[]} [possibleConfigFiles]
75
+ * @returns {Promise<string[]>}
76
+ */
77
+ export const listConfigFilesFromFs = async ({ cwd }) => {
78
+ debugLog('Listing possible configs from filesystem starting from "%s"...', cwd)
79
+
80
+ const results = await Promise.allSettled(
81
+ CONFIG_FILE_NAMES.map(async (f) => {
82
+ const filepath = path.join(cwd, f)
83
+ await fs.access(filepath, constants.F_OK)
84
+ return filepath
85
+ })
86
+ )
87
+
88
+ const possibleConfigFiles = results.flatMap((r) =>
89
+ r.status === 'fulfilled' ? [normalizePath(r.value)] : []
90
+ )
91
+
92
+ if (possibleConfigFiles.length > 0) {
93
+ debugLog('Found possible config files from filesystem:', possibleConfigFiles)
94
+ return possibleConfigFiles
95
+ }
96
+
97
+ const parentDir = path.dirname(cwd)
98
+ if (parentDir === cwd) {
99
+ return [] /** Root-level / */
100
+ }
101
+
102
+ return listConfigFilesFromFs({ cwd: parentDir })
103
+ }
104
+
105
+ /**
106
+ * Search all config files from the git repository, preferring those inside `cwd`.
107
+ *
108
+ * @param {object} options
109
+ * @param {Object} [options.configObject] - Explicit config object from the js API
110
+ * @param {string} [options.configPath] - Explicit path to a config file
111
+ * @param {string} [options.cwd] - Current working directory
112
+ * @param {string} [options.topLevelDir] - Top-level directory of the git repo
113
+ * @param {Logger} logger
114
+ *
115
+ * @returns {Promise<{ [key: string]: { config: *, files: string[] } }>} found configs with filepath as key, and config as value
116
+ */
117
+ export const searchConfigs = async (
118
+ { configObject, configPath, cwd = process.cwd(), topLevelDir = cwd },
119
+ logger
120
+ ) => {
121
+ debugLog('Searching for configuration files...')
122
+
123
+ // Return explicit config object from js API
124
+ if (configObject) {
125
+ debugLog('Using single direct configuration object...')
126
+
127
+ return { '': validateConfig(configObject, 'config object', logger) }
128
+ }
129
+
130
+ // Use only explicit config path instead of discovering multiple
131
+ if (configPath) {
132
+ debugLog('Using single configuration path...')
133
+
134
+ const { config, filepath } = await loadConfig(configPath, logger)
135
+
136
+ if (!config) return {}
137
+ return { [configPath]: validateConfig(config, filepath, logger) }
138
+ }
139
+
140
+ const possibleConfigFiles = new Set()
141
+
142
+ const addToSet = (files) => {
143
+ files.forEach((f) => {
144
+ possibleConfigFiles.add(f)
145
+ })
146
+ }
147
+
148
+ await Promise.all([
149
+ listConfigFilesFromGit({ cwd, topLevelDir }).then(addToSet),
150
+ listConfigFilesFromFs({ cwd }).then(addToSet),
151
+ ])
152
+
153
+ /** Create object with key as config file, and value as null */
154
+ const configs = Array.from(possibleConfigFiles)
155
+ .sort(sortAlphabetically)
156
+ .sort(sortDeepestParth)
157
+ .reduce((acc, configPath) => Object.assign(acc, { [configPath]: null }), {})
158
+
159
+ /** Load and validate all configs to the above object */
160
+ await Promise.all(
161
+ Object.keys(configs).map((configPath) =>
162
+ loadConfig(configPath, logger).then(({ config, filepath }) => {
163
+ if (config) {
164
+ if (configPath !== filepath) {
165
+ debugLog('Config file "%s" resolved to "%s"', configPath, filepath)
166
+ }
167
+
168
+ configs[configPath] = validateConfig(config, filepath, logger)
169
+ }
170
+ })
171
+ )
172
+ )
173
+
174
+ /** Get validated configs from the above object, without any `null` values (not found) */
175
+ const foundConfigs = Object.entries(configs).reduce((acc, [key, value]) => {
176
+ if (value) {
177
+ Object.assign(acc, { [key]: value })
178
+ }
179
+
180
+ return acc
181
+ }, {})
182
+
183
+ debugLog('Found %d config files', Object.keys(foundConfigs).length)
184
+
185
+ return foundConfigs
186
+ }
package/lib/state.js ADDED
@@ -0,0 +1,102 @@
1
+ import { GIT_ERROR, TASK_ERROR } from './messages.js'
2
+ import {
3
+ FailOnChangesError,
4
+ GitError,
5
+ RestoreOriginalStateError,
6
+ RestoreUnstagedChangesError,
7
+ TaskError,
8
+ } from './symbols.js'
9
+
10
+ export const getInitialState = ({
11
+ failOnChanges = false,
12
+ hideUnstaged = false,
13
+ hidePartiallyStaged = !hideUnstaged,
14
+ quiet = false,
15
+ revert = true,
16
+ } = {}) => ({
17
+ backupHash: null,
18
+ errors: new Set([]),
19
+ shouldFailOnChanges: failOnChanges,
20
+ hasFilesToHide: null,
21
+ output: [],
22
+ quiet,
23
+ shouldBackup: null,
24
+ shouldHidePartiallyStaged: hidePartiallyStaged,
25
+ shouldHideUnstaged: hideUnstaged,
26
+ shouldRevert: revert,
27
+ unstagedDiffSha256: null,
28
+ unstagedPatch: null,
29
+ })
30
+
31
+ export const shouldHidePartiallyStagedFiles = (ctx) =>
32
+ ctx.shouldHidePartiallyStaged && ctx.hasFilesToHide
33
+
34
+ export const shouldRestoreUnstagedChanges = (ctx) =>
35
+ (ctx.shouldHideUnstaged || ctx.shouldHidePartiallyStaged) && ctx.hasFilesToHide
36
+
37
+ export const applyModificationsSkipped = (ctx) => {
38
+ // Always apply back unstaged modifications when skipping revert or backup
39
+ if (!ctx.shouldRevert || !ctx.shouldBackup) return false
40
+
41
+ // Should be skipped in case of git errors
42
+ if (ctx.errors.has(GitError)) {
43
+ return GIT_ERROR
44
+ }
45
+
46
+ // Should be skipped when tasks fail
47
+ if (ctx.errors.has(TaskError)) {
48
+ return TASK_ERROR
49
+ }
50
+ }
51
+
52
+ export const restoreUnstagedChangesSkipped = (ctx) => {
53
+ // Should be skipped in case of git errors
54
+ if (ctx.errors.has(GitError)) {
55
+ return GIT_ERROR
56
+ }
57
+
58
+ // When complete reverting to original state is skipped,
59
+ // we can still restore unstaged changes to make it easier
60
+ // to do manually.
61
+ if (!ctx.shouldRevert) {
62
+ false
63
+ }
64
+
65
+ // Should be skipped when tasks fail
66
+ if (ctx.errors.has(TaskError)) {
67
+ return TASK_ERROR
68
+ }
69
+ }
70
+
71
+ export const restoreOriginalStateEnabled = (ctx) =>
72
+ !!ctx.shouldRevert &&
73
+ !!ctx.shouldBackup &&
74
+ (ctx.errors.has(FailOnChangesError) ||
75
+ ctx.errors.has(TaskError) ||
76
+ ctx.errors.has(RestoreUnstagedChangesError))
77
+
78
+ export const restoreOriginalStateSkipped = (ctx) => {
79
+ // Should be skipped in case of unknown git errors
80
+ if (ctx.errors.has(GitError) && !ctx.errors.has(RestoreUnstagedChangesError)) {
81
+ return GIT_ERROR
82
+ }
83
+ }
84
+
85
+ export const cleanupEnabled = (ctx) => ctx.shouldBackup
86
+
87
+ export const cleanupSkipped = (ctx) => {
88
+ // "--fail-on-changes" was used, so we shouldn't drop the backup stash
89
+ if (ctx.errors.has(FailOnChangesError) && !ctx.shouldRevert) {
90
+ return true
91
+ }
92
+
93
+ // Should be skipped in case of unknown git errors
94
+ if (restoreOriginalStateSkipped(ctx)) {
95
+ return GIT_ERROR
96
+ }
97
+
98
+ // Should be skipped when reverting to original state fails
99
+ if (ctx.errors.has(RestoreOriginalStateError)) {
100
+ return GIT_ERROR
101
+ }
102
+ }
package/lib/symbols.js ADDED
@@ -0,0 +1,29 @@
1
+ export const ApplyEmptyCommitError = Symbol('ApplyEmptyCommitError')
2
+
3
+ export const ConfigNotFoundError = new Error('Configuration could not be found')
4
+
5
+ export const ConfigFormatError = new Error('Configuration should be an object or a function')
6
+
7
+ export const ConfigEmptyError = new Error('Configuration should not be empty')
8
+
9
+ export const GetBackupStashError = Symbol('GetBackupStashError')
10
+
11
+ export const GetStagedFilesError = Symbol('GetStagedFilesError')
12
+
13
+ export const GitError = Symbol('GitError')
14
+
15
+ export const GitRepoError = Symbol('GitRepoError')
16
+
17
+ export const HideUnstagedChangesError = Symbol('HideUnstagedChangesError')
18
+
19
+ export const InvalidOptionsError = new Error('Invalid Options')
20
+
21
+ export const RestoreMergeStatusError = Symbol('RestoreMergeStatusError')
22
+
23
+ export const RestoreOriginalStateError = Symbol('RestoreOriginalStateError')
24
+
25
+ export const RestoreUnstagedChangesError = Symbol('RestoreUnstagedChangesError')
26
+
27
+ export const TaskError = Symbol('TaskError')
28
+
29
+ export const FailOnChangesError = Symbol('FailOnChangesError')