@hominis/fireforge 0.52.0 → 0.54.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/CHANGELOG.md +45 -0
- package/dist/build-info.json +4 -4
- package/dist/src/commands/bootstrap.js +2 -3
- package/dist/src/commands/build-prelock.d.ts +3 -3
- package/dist/src/commands/build-prelock.js +3 -3
- package/dist/src/commands/build.js +4 -0
- package/dist/src/commands/declarations-report.d.ts +9 -3
- package/dist/src/commands/declarations-report.js +18 -11
- package/dist/src/commands/discard.js +3 -3
- package/dist/src/commands/doctor-furnace-config-sync.js +4 -4
- package/dist/src/commands/doctor-furnace.js +5 -5
- package/dist/src/commands/export-adopt-check.d.ts +5 -6
- package/dist/src/commands/export-adopt-check.js +5 -6
- package/dist/src/commands/export-flow.js +6 -9
- package/dist/src/commands/export-staged-registration.d.ts +4 -4
- package/dist/src/commands/export-staged-registration.js +4 -4
- package/dist/src/commands/forward-import-remedy.d.ts +2 -3
- package/dist/src/commands/forward-import-remedy.js +2 -3
- package/dist/src/commands/furnace/create-dry-run.d.ts +2 -2
- package/dist/src/commands/furnace/deploy-fragment-includers.d.ts +2 -2
- package/dist/src/commands/furnace/deploy-fragment-includers.js +2 -2
- package/dist/src/commands/furnace/index.js +2 -2
- package/dist/src/commands/lint.js +4 -4
- package/dist/src/commands/patch/compact.js +1 -1
- package/dist/src/commands/patch/lint-ignore-from.d.ts +3 -4
- package/dist/src/commands/patch/lint-ignore-from.js +3 -4
- package/dist/src/commands/patch/move-files-adopt.d.ts +33 -0
- package/dist/src/commands/patch/move-files-adopt.js +65 -0
- package/dist/src/commands/patch/move-files-create.js +41 -20
- package/dist/src/commands/patch/move-files-into.d.ts +18 -0
- package/dist/src/commands/patch/move-files-into.js +125 -60
- package/dist/src/commands/patch/move-files-retire.d.ts +39 -0
- package/dist/src/commands/patch/move-files-retire.js +91 -0
- package/dist/src/commands/patch/move-files.d.ts +3 -0
- package/dist/src/commands/patch/move-files.js +42 -12
- package/dist/src/commands/patch/move-size-tier.d.ts +27 -0
- package/dist/src/commands/patch/move-size-tier.js +66 -0
- package/dist/src/commands/patch/rename.js +1 -1
- package/dist/src/commands/patch/reorder-projection.d.ts +17 -0
- package/dist/src/commands/patch/reorder-projection.js +49 -0
- package/dist/src/commands/patch/reorder-shift.d.ts +30 -0
- package/dist/src/commands/patch/reorder-shift.js +307 -0
- package/dist/src/commands/patch/reorder.d.ts +9 -15
- package/dist/src/commands/patch/reorder.js +52 -50
- package/dist/src/commands/patch/split-plan.d.ts +31 -3
- package/dist/src/commands/patch/split-plan.js +36 -12
- package/dist/src/commands/patch/split-staged.d.ts +2 -2
- package/dist/src/commands/patch/split-staged.js +2 -2
- package/dist/src/commands/patch/split.js +18 -5
- package/dist/src/commands/patch/staged-dependency-entries.d.ts +9 -1
- package/dist/src/commands/patch/staged-dependency-entries.js +32 -4
- package/dist/src/commands/patch/staged-dependency-from.js +15 -9
- package/dist/src/commands/patch/staged-dependency-remove.d.ts +26 -4
- package/dist/src/commands/patch/staged-dependency-remove.js +60 -23
- package/dist/src/commands/patch/staged-dependency.js +35 -24
- package/dist/src/commands/patch/tier.js +2 -2
- package/dist/src/commands/patch-rename-map.d.ts +14 -3
- package/dist/src/commands/patch-rename-map.js +21 -3
- package/dist/src/commands/re-export-adjacent.d.ts +4 -5
- package/dist/src/commands/re-export-lint.d.ts +4 -3
- package/dist/src/commands/re-export-lint.js +4 -3
- package/dist/src/commands/re-export-options.js +8 -2
- package/dist/src/commands/re-export-preflight.js +1 -2
- package/dist/src/commands/re-export-purity-scope.d.ts +1 -1
- package/dist/src/commands/re-export-purity-scope.js +13 -7
- package/dist/src/commands/re-export-retired-declarations.d.ts +22 -0
- package/dist/src/commands/re-export-retired-declarations.js +113 -0
- package/dist/src/commands/re-export-single.js +2 -2
- package/dist/src/commands/re-export-stale-registrations.d.ts +27 -6
- package/dist/src/commands/re-export-stale-registrations.js +65 -21
- package/dist/src/commands/re-export.js +4 -0
- package/dist/src/commands/rebase/continue.js +3 -3
- package/dist/src/commands/register.js +5 -4
- package/dist/src/commands/run.js +11 -4
- package/dist/src/commands/status.js +1 -1
- package/dist/src/commands/test-build-furnace.d.ts +1 -2
- package/dist/src/commands/test-build-furnace.js +1 -2
- package/dist/src/commands/test-build-phase.d.ts +8 -0
- package/dist/src/commands/test-build-phase.js +42 -7
- package/dist/src/commands/test-control-restore.d.ts +40 -0
- package/dist/src/commands/test-control-restore.js +86 -0
- package/dist/src/commands/test-dispatch.js +68 -7
- package/dist/src/commands/test-multi-verdict.js +1 -0
- package/dist/src/commands/test-orchestrate.d.ts +6 -0
- package/dist/src/commands/test-orchestrate.js +32 -1
- package/dist/src/commands/test-perf-scope.d.ts +1 -1
- package/dist/src/commands/test-perf-scope.js +1 -1
- package/dist/src/commands/test-prelock.d.ts +18 -8
- package/dist/src/commands/test-prelock.js +42 -14
- package/dist/src/commands/test-register.js +4 -3
- package/dist/src/commands/test-results.d.ts +10 -1
- package/dist/src/commands/test-results.js +20 -3
- package/dist/src/commands/test-run.d.ts +6 -0
- package/dist/src/commands/test-run.js +23 -4
- package/dist/src/commands/test-shards.d.ts +7 -3
- package/dist/src/commands/test-shards.js +24 -7
- package/dist/src/commands/test-stale-gate.d.ts +6 -1
- package/dist/src/commands/test-stale-gate.js +40 -2
- package/dist/src/commands/test-verdict.d.ts +5 -3
- package/dist/src/commands/test.js +14 -2
- package/dist/src/commands/token.js +2 -2
- package/dist/src/commands/verify.js +1 -1
- package/dist/src/commands/watch.js +1 -1
- package/dist/src/commands/wire.js +1 -1
- package/dist/src/core/build-audit-resolve.d.ts +4 -5
- package/dist/src/core/build-audit-resolve.js +4 -5
- package/dist/src/core/build-audit-skip.d.ts +2 -2
- package/dist/src/core/build-audit.d.ts +3 -4
- package/dist/src/core/build-baseline-types.d.ts +24 -3
- package/dist/src/core/build-baseline-validate.d.ts +30 -1
- package/dist/src/core/build-baseline-validate.js +127 -35
- package/dist/src/core/build-baseline.d.ts +8 -2
- package/dist/src/core/build-baseline.js +25 -16
- package/dist/src/core/build-jar-escalation.d.ts +4 -4
- package/dist/src/core/build-jar-escalation.js +4 -4
- package/dist/src/core/build-prepare.js +4 -4
- package/dist/src/core/config-validate.js +4 -4
- package/dist/src/core/coverage-extend.d.ts +5 -3
- package/dist/src/core/coverage-extend.js +8 -3
- package/dist/src/core/coverage-records.d.ts +4 -5
- package/dist/src/core/coverage-records.js +4 -5
- package/dist/src/core/destructive.d.ts +2 -2
- package/dist/src/core/destructive.js +2 -2
- package/dist/src/core/discard-baseline.d.ts +2 -2
- package/dist/src/core/discard-baseline.js +3 -3
- package/dist/src/core/engine-session-lock.d.ts +1 -1
- package/dist/src/core/engine-session-lock.js +1 -1
- package/dist/src/core/file-lock-owner.d.ts +5 -5
- package/dist/src/core/file-lock-owner.js +5 -5
- package/dist/src/core/file-lock-queue.js +6 -8
- package/dist/src/core/file-lock-shared.d.ts +4 -4
- package/dist/src/core/file-lock-shared.js +4 -4
- package/dist/src/core/file-lock.js +7 -10
- package/dist/src/core/firefox.js +2 -2
- package/dist/src/core/furnace-apply-overwrite-warn.d.ts +1 -1
- package/dist/src/core/furnace-apply-overwrite-warn.js +1 -1
- package/dist/src/core/furnace-apply.js +3 -3
- package/dist/src/core/furnace-operation.js +5 -5
- package/dist/src/core/furnace-registration-jar-inplace.js +5 -6
- package/dist/src/core/furnace-registration-rename.d.ts +5 -5
- package/dist/src/core/furnace-registration-rename.js +5 -5
- package/dist/src/core/furnace-registration-validate.d.ts +2 -1
- package/dist/src/core/furnace-registration-validate.js +2 -1
- package/dist/src/core/furnace-registration.js +2 -2
- package/dist/src/core/git-base.js +4 -3
- package/dist/src/core/git-diff.js +1 -1
- package/dist/src/core/git.js +1 -1
- package/dist/src/core/harness-orphans.d.ts +6 -6
- package/dist/src/core/harness-orphans.js +6 -6
- package/dist/src/core/harness-port-reservation.d.ts +60 -0
- package/dist/src/core/harness-port-reservation.js +122 -0
- package/dist/src/core/history-log-file.d.ts +9 -9
- package/dist/src/core/history-log-file.js +15 -15
- package/dist/src/core/history-log.d.ts +3 -3
- package/dist/src/core/history-log.js +3 -3
- package/dist/src/core/launchable-build.d.ts +39 -0
- package/dist/src/core/launchable-build.js +85 -0
- package/dist/src/core/marionette-port.js +1 -3
- package/dist/src/core/moz-manifest-rules.js +3 -2
- package/dist/src/core/negative-control-compiled.d.ts +13 -3
- package/dist/src/core/negative-control-compiled.js +27 -5
- package/dist/src/core/negative-control-diff.js +31 -6
- package/dist/src/core/parser-fallback.d.ts +5 -5
- package/dist/src/core/parser-fallback.js +5 -5
- package/dist/src/core/patch-identifier-suggest.d.ts +6 -0
- package/dist/src/core/patch-identifier-suggest.js +11 -4
- package/dist/src/core/patch-lint-checkjs.d.ts +1 -1
- package/dist/src/core/patch-lint-checkjs.js +1 -1
- package/dist/src/core/patch-lint-css.js +2 -2
- package/dist/src/core/patch-lint-file-size.d.ts +5 -7
- package/dist/src/core/patch-lint-file-size.js +5 -7
- package/dist/src/core/patch-lint-ignore.d.ts +3 -3
- package/dist/src/core/patch-lint-ignore.js +3 -3
- package/dist/src/core/patch-lint-prettier.d.ts +4 -6
- package/dist/src/core/patch-lint-prettier.js +4 -6
- package/dist/src/core/patch-lint-projection.d.ts +1 -1
- package/dist/src/core/patch-lint-projection.js +1 -1
- package/dist/src/core/patch-lint-queue-types.d.ts +2 -2
- package/dist/src/core/patch-lint-queue-types.js +2 -2
- package/dist/src/core/patch-manifest-consistency.d.ts +2 -2
- package/dist/src/core/patch-manifest-consistency.js +2 -2
- package/dist/src/core/patch-manifest-io.d.ts +5 -1
- package/dist/src/core/patch-manifest-io.js +12 -10
- package/dist/src/core/patch-manifest-key-order.d.ts +3 -4
- package/dist/src/core/patch-manifest-key-order.js +3 -4
- package/dist/src/core/patch-parse.d.ts +3 -3
- package/dist/src/core/patch-parse.js +3 -3
- package/dist/src/core/patch-policy.d.ts +1 -1
- package/dist/src/core/patch-policy.js +1 -1
- package/dist/src/core/patch-source-metadata.d.ts +1 -1
- package/dist/src/core/patch-source-metadata.js +1 -1
- package/dist/src/core/patch-subset.d.ts +5 -3
- package/dist/src/core/patch-subset.js +8 -7
- package/dist/src/core/patch-transform.d.ts +2 -2
- package/dist/src/core/patch-transform.js +2 -2
- package/dist/src/core/rebase-session.js +4 -6
- package/dist/src/core/register-xpcshell-test.d.ts +28 -7
- package/dist/src/core/register-xpcshell-test.js +76 -27
- package/dist/src/core/run-log.d.ts +8 -8
- package/dist/src/core/run-log.js +8 -8
- package/dist/src/core/status-classify.d.ts +1 -2
- package/dist/src/core/status-classify.js +1 -2
- package/dist/src/core/test-harness-crash.d.ts +5 -3
- package/dist/src/core/test-harness-crash.js +13 -14
- package/dist/src/core/test-harness-infra.d.ts +32 -0
- package/dist/src/core/test-harness-infra.js +69 -0
- package/dist/src/core/test-harness-signature.d.ts +6 -0
- package/dist/src/core/test-stale-check.d.ts +38 -0
- package/dist/src/core/test-stale-check.js +82 -1
- package/dist/src/core/test-stall-triage.js +5 -7
- package/dist/src/core/token-category.js +2 -2
- package/dist/src/core/token-inventory.js +3 -3
- package/dist/src/core/token-manager.d.ts +2 -2
- package/dist/src/core/token-manager.js +5 -7
- package/dist/src/core/token-variant.d.ts +6 -6
- package/dist/src/core/token-variant.js +7 -7
- package/dist/src/core/typecheck-shim.js +1 -1
- package/dist/src/core/wire-destroy.d.ts +1 -1
- package/dist/src/core/wire-destroy.js +1 -1
- package/dist/src/core/wire-dom-fragment.js +1 -1
- package/dist/src/core/wire-init.d.ts +1 -1
- package/dist/src/core/wire-init.js +1 -1
- package/dist/src/core/wire-subscript.d.ts +1 -1
- package/dist/src/core/wire-subscript.js +1 -1
- package/dist/src/core/wire-utils.d.ts +2 -2
- package/dist/src/core/wire-utils.js +3 -3
- package/dist/src/errors/base.d.ts +1 -1
- package/dist/src/errors/base.js +1 -1
- package/dist/src/errors/rebase.d.ts +1 -1
- package/dist/src/errors/rebase.js +1 -1
- package/dist/src/types/commands/options.d.ts +38 -3
- package/dist/src/types/config.d.ts +5 -7
- package/dist/src/utils/options.js +2 -2
- package/dist/src/utils/process.js +2 -2
- package/dist/src/utils/regex.d.ts +2 -2
- package/dist/src/utils/regex.js +3 -3
- package/dist/src/utils/sleep.d.ts +5 -6
- package/dist/src/utils/sleep.js +5 -6
- package/dist/src/utils/validation.js +2 -2
- package/docs/configuration.md +28 -23
- package/docs/exit-codes.md +8 -11
- package/docs/furnace.md +3 -3
- package/docs/lifecycle-invariants.md +36 -45
- package/docs/machine-output.md +41 -24
- package/docs/patch-workflow.md +140 -29
- package/docs/run-logs.md +8 -9
- package/docs/testing.md +114 -48
- package/package.json +1 -1
|
@@ -21,6 +21,22 @@ import { warn } from '../../utils/logger.js';
|
|
|
21
21
|
import {} from '../export-flow.js';
|
|
22
22
|
import { projectEntryBody } from './entry-projection.js';
|
|
23
23
|
import { describeStagedMaintenance, findDeclarationsMadeUnused, keptRelocatedDeclarations, relocatedSourceDeclarations, rewriteSplitOwners, withoutRelocatedDeclarations, } from './split-staged.js';
|
|
24
|
+
/**
|
|
25
|
+
* The `reason` a move records on a forward declaration it writes, so the
|
|
26
|
+
* declaration explains itself in `patches.json` and `staged-dependency
|
|
27
|
+
* --add --from` accepts it without a hand-written `--reason`.
|
|
28
|
+
*
|
|
29
|
+
* @param source - Patch the files left
|
|
30
|
+
* @param destination - Patch the files moved into
|
|
31
|
+
* @param operation - The command that moved them
|
|
32
|
+
*/
|
|
33
|
+
export function moveDeclarationReason(source, destination, operation) {
|
|
34
|
+
return `${source} → ${destination} by ${operation}`;
|
|
35
|
+
}
|
|
36
|
+
/** The {@link moveDeclarationReason} of a split plan. */
|
|
37
|
+
export function splitDeclarationReason(plan) {
|
|
38
|
+
return moveDeclarationReason(plan.source.filename, plan.placement.newFilename, plan.operation ?? 'patch split');
|
|
39
|
+
}
|
|
24
40
|
/**
|
|
25
41
|
* Refuses the split unless the source patch currently owns every requested
|
|
26
42
|
* file. Splitting out a file the source does not own would produce a new patch
|
|
@@ -118,7 +134,7 @@ function buildProjectedSplitEntries(baseCtx, plan) {
|
|
|
118
134
|
* them once declared. Auto-declaring keeps the two in lock-step and lets a
|
|
119
135
|
* sound split read as sound.
|
|
120
136
|
*/
|
|
121
|
-
function computeSplitStagedDependencyAdditions(projectedEntries, newFilename) {
|
|
137
|
+
function computeSplitStagedDependencyAdditions(projectedEntries, newFilename, reason) {
|
|
122
138
|
const additions = new Map();
|
|
123
139
|
for (const edge of collectForwardImportEdges({ entries: projectedEntries })) {
|
|
124
140
|
if (edge.owner !== newFilename)
|
|
@@ -128,6 +144,7 @@ function computeSplitStagedDependencyAdditions(projectedEntries, newFilename) {
|
|
|
128
144
|
specifier: edge.specifier,
|
|
129
145
|
creates: edge.creates,
|
|
130
146
|
owner: newFilename,
|
|
147
|
+
reason,
|
|
131
148
|
};
|
|
132
149
|
const list = additions.get(edge.entry) ?? [];
|
|
133
150
|
const dup = list.some((d) => d.file === decl.file &&
|
|
@@ -179,15 +196,14 @@ export function runProjectedSplitLint(plan, baseCtx) {
|
|
|
179
196
|
const projectedEntries = buildProjectedSplitEntries(baseCtx, plan);
|
|
180
197
|
// Discover and auto-declare the forward edges this split introduces into
|
|
181
198
|
// the new patch, then inject them before linting so they resolve.
|
|
182
|
-
const stagedDependencyAdditions = computeSplitStagedDependencyAdditions(projectedEntries, plan.placement.newFilename);
|
|
199
|
+
const stagedDependencyAdditions = computeSplitStagedDependencyAdditions(projectedEntries, plan.placement.newFilename, splitDeclarationReason(plan));
|
|
183
200
|
injectStagedDependencyAdditions(projectedEntries, stagedDependencyAdditions);
|
|
184
201
|
const baselineAll = lintPatchQueue(baseCtx);
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
});
|
|
202
|
+
// Keep every queue-wide lint setting of the baseline (patch policy,
|
|
203
|
+
// forward-registration scope, token scales), so
|
|
204
|
+
// computeProjectedLintRegressions compares symmetric rule sets and a
|
|
205
|
+
// test-manifest suite entry is projected as the committed gate sees it.
|
|
206
|
+
const projectedAll = lintPatchQueue({ ...baseCtx, entries: projectedEntries });
|
|
191
207
|
const baselineIssues = baselineAll.filter((i) => i.severity === 'error');
|
|
192
208
|
const projectedIssues = projectedAll.filter((i) => i.severity === 'error');
|
|
193
209
|
const oldNameByNew = new Map([...plan.placement.renameMap].map(([oldName, entry]) => [entry.newFilename, oldName]));
|
|
@@ -257,6 +273,12 @@ export function buildSplitSummary(plan) {
|
|
|
257
273
|
const summary = [
|
|
258
274
|
`split ${plan.source.filename}`,
|
|
259
275
|
`moved files (${plan.movedFiles.length}): ${plan.movedFiles.join(', ')}`,
|
|
276
|
+
...(plan.adoptedFiles?.length
|
|
277
|
+
? [
|
|
278
|
+
`of which adopted from the worktree (no patch owned them) (${plan.adoptedFiles.length}): ` +
|
|
279
|
+
plan.adoptedFiles.join(', '),
|
|
280
|
+
]
|
|
281
|
+
: []),
|
|
260
282
|
`source keeps (${plan.remainingFiles.length}): ${plan.remainingFiles.join(', ')}`,
|
|
261
283
|
`new patch: ${plan.placement.newFilename} (order ${plan.placement.insertionOrder})`,
|
|
262
284
|
];
|
|
@@ -274,9 +296,11 @@ export function buildSplitSummary(plan) {
|
|
|
274
296
|
}
|
|
275
297
|
/**
|
|
276
298
|
* Maps a declaration the split projection proposes back onto the queue as
|
|
277
|
-
* it stands: renumbered patches take their current names
|
|
278
|
-
*
|
|
279
|
-
*
|
|
299
|
+
* it stands: renumbered patches take their current names. An owner that is
|
|
300
|
+
* the patch the split would create keeps the planned filename, so the
|
|
301
|
+
* report declares the owner the rerun creates (`staged-dependency --add`
|
|
302
|
+
* accepts an owner the queue does not hold yet, and the move then lands
|
|
303
|
+
* with the declaration already right).
|
|
280
304
|
*/
|
|
281
305
|
export function splitDeclarationRenamer(plan) {
|
|
282
306
|
const oldNameByNew = new Map([...plan.placement.renameMap].map(([oldName, entry]) => [entry.newFilename, oldName]));
|
|
@@ -284,6 +308,6 @@ export function splitDeclarationRenamer(plan) {
|
|
|
284
308
|
return ({ owner, ...declaration }) => ({
|
|
285
309
|
...declaration,
|
|
286
310
|
patch: current(declaration.patch),
|
|
287
|
-
|
|
311
|
+
owner: owner === plan.placement.newFilename ? owner : current(owner),
|
|
288
312
|
});
|
|
289
313
|
}
|
|
@@ -14,8 +14,8 @@
|
|
|
14
14
|
* the imported file landed in one patch) is dropped
|
|
15
15
|
* ({@link findDeclarationsMadeUnused}).
|
|
16
16
|
*
|
|
17
|
-
* Without this the move
|
|
18
|
-
* per-patch lint to report `queue-dependency-creates-unowned` and
|
|
17
|
+
* Without this the move would write the bodies and leave the queue audit
|
|
18
|
+
* and the per-patch lint to report `queue-dependency-creates-unowned` and
|
|
19
19
|
* `staged-dependency-unused` afterwards, one `--remove`/`--add` pair per
|
|
20
20
|
* entry to repair by hand.
|
|
21
21
|
*/
|
|
@@ -15,8 +15,8 @@
|
|
|
15
15
|
* the imported file landed in one patch) is dropped
|
|
16
16
|
* ({@link findDeclarationsMadeUnused}).
|
|
17
17
|
*
|
|
18
|
-
* Without this the move
|
|
19
|
-
* per-patch lint to report `queue-dependency-creates-unowned` and
|
|
18
|
+
* Without this the move would write the bodies and leave the queue audit
|
|
19
|
+
* and the per-patch lint to report `queue-dependency-creates-unowned` and
|
|
20
20
|
* `staged-dependency-unused` afterwards, one `--remove`/`--add` pair per
|
|
21
21
|
* entry to repair by hand.
|
|
22
22
|
*/
|
|
@@ -36,8 +36,10 @@ import { normalizePatchDisplayName, parsePositiveIntegerFlag } from '../../utils
|
|
|
36
36
|
import { withDeclarationsReport } from '../declarations-report.js';
|
|
37
37
|
import { proceedAfterDecision } from '../destructive-decision.js';
|
|
38
38
|
import { placementPlansEqual, resolvePlacementPlan } from '../export-flow.js';
|
|
39
|
-
import {
|
|
40
|
-
import {
|
|
39
|
+
import { writeRenameMapFile } from '../patch-rename-map.js';
|
|
40
|
+
import { assertAdoptedStillUnowned } from './move-files-adopt.js';
|
|
41
|
+
import { runMoveSideLint } from './move-size-tier.js';
|
|
42
|
+
import { assertSourceOwnsFiles, buildNewPatchMetadata, buildSplitDiff, buildSplitSummary, mergeStagedForwardImports, projectSplitManifest, runProjectedSplitLint, splitDeclarationReason, splitDeclarationRenamer, } from './split-plan.js';
|
|
41
43
|
import { describeStagedMaintenance, emptyStagedMaintenance, findOwnerRewriteHolders, rewriteSplitOwners, withoutDroppedDeclarations, withoutRelocatedDeclarations, } from './split-staged.js';
|
|
42
44
|
/**
|
|
43
45
|
* History payload for the declarations a split re-homed or dropped, omitted
|
|
@@ -79,6 +81,7 @@ export async function commitPatchSplit(patchesDir, plan, newMetadata, options, c
|
|
|
79
81
|
if (!current || current.filesAffected.join('\n') !== plan.source.filesAffected.join('\n')) {
|
|
80
82
|
throw new InvalidArgumentError('Patch queue changed while waiting for split confirmation. Re-run the command.', 'patch split');
|
|
81
83
|
}
|
|
84
|
+
assertAdoptedStillUnowned(manifest.patches, plan.adoptedFiles, 'patch split');
|
|
82
85
|
const currentPlacement = await resolvePlacementPlan(patchesDir, plan.placementOptions, plan.category, plan.name, config);
|
|
83
86
|
if (!placementPlansEqual(currentPlacement, plan.placement)) {
|
|
84
87
|
throw new InvalidArgumentError('Patch queue changed while waiting for split confirmation. Re-run the command.', 'patch split');
|
|
@@ -266,9 +269,11 @@ export async function patchSplitCommand(projectRoot, sourceId, options) {
|
|
|
266
269
|
// instead of linting each projected body blind.
|
|
267
270
|
const patchQueueCtx = await buildPatchQueueContext(paths.patches, config);
|
|
268
271
|
const ignoreChecks = buildLintIgnoreChecks(source.lintIgnore);
|
|
269
|
-
await
|
|
272
|
+
await runMoveSideLint({
|
|
270
273
|
engineDir: paths.engine,
|
|
271
274
|
filesAffected: remainingFiles,
|
|
275
|
+
filesBefore: source.filesAffected,
|
|
276
|
+
role: 'source',
|
|
272
277
|
diffContent: remainingDiff,
|
|
273
278
|
config,
|
|
274
279
|
skipLint: options.skipLint,
|
|
@@ -276,9 +281,10 @@ export async function patchSplitCommand(projectRoot, sourceId, options) {
|
|
|
276
281
|
ignoreChecks,
|
|
277
282
|
patchTier: source.tier,
|
|
278
283
|
});
|
|
279
|
-
await
|
|
284
|
+
await runMoveSideLint({
|
|
280
285
|
engineDir: paths.engine,
|
|
281
286
|
filesAffected: movedFiles,
|
|
287
|
+
role: 'new patch',
|
|
282
288
|
diffContent: movedDiff,
|
|
283
289
|
config,
|
|
284
290
|
skipLint: options.skipLint,
|
|
@@ -294,6 +300,7 @@ export async function patchSplitCommand(projectRoot, sourceId, options) {
|
|
|
294
300
|
regressions: projected.regressions,
|
|
295
301
|
out: options.declarationsOut,
|
|
296
302
|
rename: splitDeclarationRenamer(plan),
|
|
303
|
+
reason: splitDeclarationReason(plan),
|
|
297
304
|
});
|
|
298
305
|
const newMetadata = buildNewPatchMetadata(plan, config);
|
|
299
306
|
const decision = await confirmDestructive({
|
|
@@ -308,6 +315,10 @@ export async function patchSplitCommand(projectRoot, sourceId, options) {
|
|
|
308
315
|
if (!proceedAfterDecision(decision, 'Split cancelled'))
|
|
309
316
|
return;
|
|
310
317
|
await commitPatchSplit(paths.patches, plan, newMetadata, options, config);
|
|
318
|
+
if (options.mapOut !== undefined) {
|
|
319
|
+
const written = await writeRenameMapFile(options.mapOut, plan.placement.renameMap);
|
|
320
|
+
info(`Wrote the old-to-new filename map (${plan.placement.renameMap.size} rename(s)) to ${written}`);
|
|
321
|
+
}
|
|
311
322
|
success(`Split ${source.filename}: ${placement.newFilename} now owns ${plan.movedFiles.length} file(s)`);
|
|
312
323
|
reportSplitStagedChanges(plan);
|
|
313
324
|
outro('Split complete');
|
|
@@ -331,7 +342,8 @@ export function registerPatchSplit(parent, context) {
|
|
|
331
342
|
.option('-y, --yes', 'Skip confirmation prompt (required for non-TTY)')
|
|
332
343
|
.option('--force-unsafe', 'Bypass projected-lint refusals')
|
|
333
344
|
.option('--skip-lint', 'Skip per-patch lint of the projected bodies')
|
|
334
|
-
.option('--declarations-out <path>', 'Where a refusal writes its one --from-ready staged-declaration report (default: .fireforge/declarations/<timestamp>.json)')
|
|
345
|
+
.option('--declarations-out <path>', 'Where a refusal writes its one --from-ready staged-declaration report (default: .fireforge/declarations/<timestamp>.json)')
|
|
346
|
+
.option('--map-out <file>', 'Write the old-to-new filename map of the patches the split renumbers as JSON');
|
|
335
347
|
addWaitLockOption(command).action(withErrorHandling(async (sourceId, options) => {
|
|
336
348
|
await patchSplitCommand(getProjectRoot(), sourceId, {
|
|
337
349
|
files: options.files,
|
|
@@ -347,6 +359,7 @@ export function registerPatchSplit(parent, context) {
|
|
|
347
359
|
forceUnsafe: options.forceUnsafe,
|
|
348
360
|
skipLint: options.skipLint,
|
|
349
361
|
declarationsOut: options.declarationsOut,
|
|
362
|
+
mapOut: options.mapOut,
|
|
350
363
|
waitLock: options.waitLock,
|
|
351
364
|
}),
|
|
352
365
|
});
|
|
@@ -29,8 +29,16 @@ export declare function registrationView(dependency: PatchStagedRegistration): S
|
|
|
29
29
|
export declare function dependencyLabel(kind: StagedDependencyKind, view: StagedEntryView): string;
|
|
30
30
|
/**
|
|
31
31
|
* Applies one add/remove/clear to a declaration list: `add` appends unless
|
|
32
|
-
* an entry with the same file, matcher, creates and owner exists.
|
|
32
|
+
* an entry with the same file, matcher, creates and owner exists. A
|
|
33
|
+
* matching entry whose `reason` differs from the addition's takes
|
|
34
|
+
* the new reason in place, keeping its position in the list; an addition
|
|
35
|
+
* without a reason leaves the existing one alone.
|
|
33
36
|
*/
|
|
34
37
|
export declare function applyMode<T>(existing: readonly T[], mode: StagedDependencyMode, target: StagedEntryView | undefined, toView: (entry: T) => StagedEntryView, addition: T | undefined): T[];
|
|
38
|
+
/**
|
|
39
|
+
* Number of declarations in `after` whose key matches one in `before` but
|
|
40
|
+
* whose `reason` differs: the in-place reason updates of an `--add`.
|
|
41
|
+
*/
|
|
42
|
+
export declare function countReasonUpdates(before: PatchStagedDependencies | undefined, after: PatchStagedDependencies | undefined): number;
|
|
35
43
|
/** Number of declarations of both kinds. */
|
|
36
44
|
export declare function countStagedEntries(staged: PatchStagedDependencies | undefined): number;
|
|
@@ -32,7 +32,10 @@ function matchesTarget(view, target) {
|
|
|
32
32
|
}
|
|
33
33
|
/**
|
|
34
34
|
* Applies one add/remove/clear to a declaration list: `add` appends unless
|
|
35
|
-
* an entry with the same file, matcher, creates and owner exists.
|
|
35
|
+
* an entry with the same file, matcher, creates and owner exists. A
|
|
36
|
+
* matching entry whose `reason` differs from the addition's takes
|
|
37
|
+
* the new reason in place, keeping its position in the list; an addition
|
|
38
|
+
* without a reason leaves the existing one alone.
|
|
36
39
|
*/
|
|
37
40
|
export function applyMode(existing, mode, target, toView, addition) {
|
|
38
41
|
if (mode === 'clear')
|
|
@@ -41,10 +44,35 @@ export function applyMode(existing, mode, target, toView, addition) {
|
|
|
41
44
|
return [...existing];
|
|
42
45
|
if (mode === 'remove')
|
|
43
46
|
return existing.filter((entry) => !matchesTarget(toView(entry), target));
|
|
44
|
-
|
|
45
|
-
if (seen.has(dependencyKey(target)) || addition === undefined)
|
|
47
|
+
if (addition === undefined)
|
|
46
48
|
return [...existing];
|
|
47
|
-
|
|
49
|
+
const key = dependencyKey(target);
|
|
50
|
+
const index = existing.findIndex((entry) => dependencyKey(toView(entry)) === key);
|
|
51
|
+
if (index === -1)
|
|
52
|
+
return [...existing, addition];
|
|
53
|
+
const current = existing[index];
|
|
54
|
+
if (target.reason === undefined || toView(current).reason === target.reason) {
|
|
55
|
+
return [...existing];
|
|
56
|
+
}
|
|
57
|
+
const next = [...existing];
|
|
58
|
+
next[index] = addition;
|
|
59
|
+
return next;
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Number of declarations in `after` whose key matches one in `before` but
|
|
63
|
+
* whose `reason` differs: the in-place reason updates of an `--add`.
|
|
64
|
+
*/
|
|
65
|
+
export function countReasonUpdates(before, after) {
|
|
66
|
+
const updates = (left, right, toView) => {
|
|
67
|
+
const reasons = new Map((left ?? []).map((entry) => [dependencyKey(toView(entry)), toView(entry).reason]));
|
|
68
|
+
return (right ?? []).filter((entry) => {
|
|
69
|
+
const view = toView(entry);
|
|
70
|
+
const key = dependencyKey(view);
|
|
71
|
+
return reasons.has(key) && reasons.get(key) !== view.reason;
|
|
72
|
+
}).length;
|
|
73
|
+
};
|
|
74
|
+
return (updates(before?.forwardImports, after?.forwardImports, importView) +
|
|
75
|
+
updates(before?.registrations, after?.registrations, registrationView));
|
|
48
76
|
}
|
|
49
77
|
/** Number of declarations of both kinds. */
|
|
50
78
|
export function countStagedEntries(staged) {
|
|
@@ -12,7 +12,7 @@ import { info, outro } from '../../utils/logger.js';
|
|
|
12
12
|
import { resolveWaitLockSeconds } from '../../utils/options.js';
|
|
13
13
|
import { requirePatchQueue, requirePatchTarget } from './patch-context.js';
|
|
14
14
|
import { readStagedDependencyBatch, stagedDependencyDelta, } from './staged-dependency-batch.js';
|
|
15
|
-
import { applyMode, countStagedEntries, importView, registrationView, } from './staged-dependency-entries.js';
|
|
15
|
+
import { applyMode, countReasonUpdates, countStagedEntries, importView, registrationView, } from './staged-dependency-entries.js';
|
|
16
16
|
import { validateStagedDependencyAdd } from './staged-dependency-validate.js';
|
|
17
17
|
/** Flags that describe one declaration and so cannot combine with `--from`. */
|
|
18
18
|
const SINGLE_DECLARATION_FLAGS = [
|
|
@@ -23,7 +23,10 @@ const SINGLE_DECLARATION_FLAGS = [
|
|
|
23
23
|
['--creates', 'creates'],
|
|
24
24
|
['--owner', 'owner'],
|
|
25
25
|
];
|
|
26
|
-
/**
|
|
26
|
+
/**
|
|
27
|
+
* Adds every `--from` declaration not already present, keyed like a single
|
|
28
|
+
* `--add`; a present one whose `reason` differs takes the new reason in place.
|
|
29
|
+
*/
|
|
27
30
|
function mergeBatch(staged, batch) {
|
|
28
31
|
let forwardImports = [...(staged?.forwardImports ?? [])];
|
|
29
32
|
for (const entry of batch.forwardImports) {
|
|
@@ -68,13 +71,16 @@ export async function runBatchAdd(projectRoot, identifier, mode, options) {
|
|
|
68
71
|
return;
|
|
69
72
|
}
|
|
70
73
|
const existing = targetPatch.stagedDependencies;
|
|
71
|
-
const
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
74
|
+
const describe = (before, after) => {
|
|
75
|
+
const added = countStagedEntries(after) - countStagedEntries(before);
|
|
76
|
+
const updated = countReasonUpdates(before, after);
|
|
77
|
+
const unchanged = supplied - added - updated;
|
|
78
|
+
return (`added ${added} of ${supplied} staged declaration(s) from ${from}` +
|
|
79
|
+
(updated > 0 ? `, updated the reason of ${updated}` : '') +
|
|
80
|
+
(unchanged > 0 ? ` (${unchanged} already present or repeated)` : ''));
|
|
81
|
+
};
|
|
76
82
|
if (options.dryRun === true) {
|
|
77
|
-
info(`[dry-run] ${targetPatch.filename}: ${describe(
|
|
83
|
+
info(`[dry-run] ${targetPatch.filename}: ${describe(existing, mergeBatch(existing, batch))}.`);
|
|
78
84
|
outro('Dry run complete — no changes made');
|
|
79
85
|
return;
|
|
80
86
|
}
|
|
@@ -92,7 +98,7 @@ export async function runBatchAdd(projectRoot, identifier, mode, options) {
|
|
|
92
98
|
}
|
|
93
99
|
const before = result.before.stagedDependencies;
|
|
94
100
|
const after = result.after.stagedDependencies;
|
|
95
|
-
info(`${targetPatch.filename}: ${describe(
|
|
101
|
+
info(`${targetPatch.filename}: ${describe(before, after)}.`);
|
|
96
102
|
await appendHistoryBestEffort(paths.patches, {
|
|
97
103
|
operation: 'patch-staged-dependency',
|
|
98
104
|
args: {
|
|
@@ -4,7 +4,10 @@
|
|
|
4
4
|
*
|
|
5
5
|
* A removal names its entries by any of `--file`, `--specifier`/`--line`,
|
|
6
6
|
* `--creates` and `--owner`, and must give at least `--creates` or the
|
|
7
|
-
* `--file` + matcher pair.
|
|
7
|
+
* `--file` + matcher pair. Without `--kind` the matcher picks the kind
|
|
8
|
+
* (`--specifier` an import, `--line` a registration), and a selection with
|
|
9
|
+
* neither searches both kinds, so `--creates` alone removes every
|
|
10
|
+
* declaration for that path. Registration lines compare whitespace-
|
|
8
11
|
* insensitively (runs of whitespace collapse, ends trimmed), the way the
|
|
9
12
|
* lint compares them, so a line copied from a patch hunk matches the stored
|
|
10
13
|
* one whatever its padding. A selection that matches nothing is refused
|
|
@@ -12,6 +15,24 @@
|
|
|
12
15
|
*/
|
|
13
16
|
import type { PatchStagedDependencies, PatchStagedDependencyOptions } from '../../types/commands/index.js';
|
|
14
17
|
import { type StagedDependencyKind, type StagedEntryView } from './staged-dependency-entries.js';
|
|
18
|
+
/** One stored declaration together with its kind. */
|
|
19
|
+
export interface KindedEntryView {
|
|
20
|
+
kind: StagedDependencyKind;
|
|
21
|
+
view: StagedEntryView;
|
|
22
|
+
}
|
|
23
|
+
/** Singular noun for a declaration of the given kinds. */
|
|
24
|
+
export declare function declarationNoun(kinds: readonly StagedDependencyKind[]): string;
|
|
25
|
+
/**
|
|
26
|
+
* The kinds a `--remove` searches. `--kind` wins when given (and is
|
|
27
|
+
* validated by the caller); otherwise `--specifier` selects imports,
|
|
28
|
+
* `--line` registrations, and a selection with neither matcher searches
|
|
29
|
+
* both kinds.
|
|
30
|
+
*
|
|
31
|
+
* @param options - Command options
|
|
32
|
+
* @returns The kinds to search, or undefined when `--kind` was given
|
|
33
|
+
* @throws InvalidArgumentError when `--specifier` and `--line` are both given without `--kind`
|
|
34
|
+
*/
|
|
35
|
+
export declare function inferRemovalKinds(options: PatchStagedDependencyOptions): readonly StagedDependencyKind[] | undefined;
|
|
15
36
|
/**
|
|
16
37
|
* Resolves the staged entries a `--remove` invocation names. The returned
|
|
17
38
|
* views are the stored entries themselves, so the removal matches them
|
|
@@ -20,12 +41,13 @@ import { type StagedDependencyKind, type StagedEntryView } from './staged-depend
|
|
|
20
41
|
* - `--file` + matcher identifying one entry infers `--creates`.
|
|
21
42
|
* - `--file` + matcher identifying several entries without `--creates`
|
|
22
43
|
* refuses with the candidate list.
|
|
23
|
-
* - `--creates` alone selects every declaration for that created path
|
|
44
|
+
* - `--creates` alone selects every declaration for that created path, of
|
|
45
|
+
* every kind searched.
|
|
24
46
|
*
|
|
25
|
-
* @param
|
|
47
|
+
* @param kinds - Declaration kinds to search
|
|
26
48
|
* @param options - Command options
|
|
27
49
|
* @param staged - The target patch's current staged dependencies
|
|
28
50
|
* @returns The matching stored entries (never empty)
|
|
29
51
|
* @throws InvalidArgumentError when the selectors are insufficient or match nothing
|
|
30
52
|
*/
|
|
31
|
-
export declare function selectRemovalEntries(
|
|
53
|
+
export declare function selectRemovalEntries(kinds: readonly StagedDependencyKind[], options: PatchStagedDependencyOptions, staged: PatchStagedDependencies | undefined): KindedEntryView[];
|
|
@@ -5,7 +5,10 @@
|
|
|
5
5
|
*
|
|
6
6
|
* A removal names its entries by any of `--file`, `--specifier`/`--line`,
|
|
7
7
|
* `--creates` and `--owner`, and must give at least `--creates` or the
|
|
8
|
-
* `--file` + matcher pair.
|
|
8
|
+
* `--file` + matcher pair. Without `--kind` the matcher picks the kind
|
|
9
|
+
* (`--specifier` an import, `--line` a registration), and a selection with
|
|
10
|
+
* neither searches both kinds, so `--creates` alone removes every
|
|
11
|
+
* declaration for that path. Registration lines compare whitespace-
|
|
9
12
|
* insensitively (runs of whitespace collapse, ends trimmed), the way the
|
|
10
13
|
* lint compares them, so a line copied from a patch hunk matches the stored
|
|
11
14
|
* one whatever its padding. A selection that matches nothing is refused
|
|
@@ -18,10 +21,38 @@ import { dependencyLabel, importView, registrationView, } from './staged-depende
|
|
|
18
21
|
function normalizeRegistrationLine(line) {
|
|
19
22
|
return line.trim().replace(/\s+/g, ' ');
|
|
20
23
|
}
|
|
21
|
-
function stagedViews(
|
|
22
|
-
return kind === 'import'
|
|
23
|
-
? (staged?.forwardImports ?? []).map(importView)
|
|
24
|
-
: (staged?.registrations ?? []).map(registrationView);
|
|
24
|
+
function stagedViews(kinds, staged) {
|
|
25
|
+
return kinds.flatMap((kind) => kind === 'import'
|
|
26
|
+
? (staged?.forwardImports ?? []).map((entry) => ({ kind, view: importView(entry) }))
|
|
27
|
+
: (staged?.registrations ?? []).map((entry) => ({ kind, view: registrationView(entry) })));
|
|
28
|
+
}
|
|
29
|
+
/** Singular noun for a declaration of the given kinds. */
|
|
30
|
+
export function declarationNoun(kinds) {
|
|
31
|
+
if (kinds.length !== 1)
|
|
32
|
+
return 'staged declaration';
|
|
33
|
+
return kinds[0] === 'import' ? 'staged forward-import' : 'staged registration';
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* The kinds a `--remove` searches. `--kind` wins when given (and is
|
|
37
|
+
* validated by the caller); otherwise `--specifier` selects imports,
|
|
38
|
+
* `--line` registrations, and a selection with neither matcher searches
|
|
39
|
+
* both kinds.
|
|
40
|
+
*
|
|
41
|
+
* @param options - Command options
|
|
42
|
+
* @returns The kinds to search, or undefined when `--kind` was given
|
|
43
|
+
* @throws InvalidArgumentError when `--specifier` and `--line` are both given without `--kind`
|
|
44
|
+
*/
|
|
45
|
+
export function inferRemovalKinds(options) {
|
|
46
|
+
if (options.kind !== undefined)
|
|
47
|
+
return undefined;
|
|
48
|
+
if (options.specifier !== undefined && options.line !== undefined) {
|
|
49
|
+
throw new InvalidArgumentError('--specifier names an import and --line a registration; pass one of them, or --kind.', '--specifier/--line');
|
|
50
|
+
}
|
|
51
|
+
if (options.specifier !== undefined)
|
|
52
|
+
return ['import'];
|
|
53
|
+
if (options.line !== undefined)
|
|
54
|
+
return ['registration'];
|
|
55
|
+
return ['import', 'registration'];
|
|
25
56
|
}
|
|
26
57
|
/**
|
|
27
58
|
* Resolves the staged entries a `--remove` invocation names. The returned
|
|
@@ -31,47 +62,53 @@ function stagedViews(kind, staged) {
|
|
|
31
62
|
* - `--file` + matcher identifying one entry infers `--creates`.
|
|
32
63
|
* - `--file` + matcher identifying several entries without `--creates`
|
|
33
64
|
* refuses with the candidate list.
|
|
34
|
-
* - `--creates` alone selects every declaration for that created path
|
|
65
|
+
* - `--creates` alone selects every declaration for that created path, of
|
|
66
|
+
* every kind searched.
|
|
35
67
|
*
|
|
36
|
-
* @param
|
|
68
|
+
* @param kinds - Declaration kinds to search
|
|
37
69
|
* @param options - Command options
|
|
38
70
|
* @param staged - The target patch's current staged dependencies
|
|
39
71
|
* @returns The matching stored entries (never empty)
|
|
40
72
|
* @throws InvalidArgumentError when the selectors are insufficient or match nothing
|
|
41
73
|
*/
|
|
42
|
-
export function selectRemovalEntries(
|
|
43
|
-
const
|
|
44
|
-
const
|
|
74
|
+
export function selectRemovalEntries(kinds, options, staged) {
|
|
75
|
+
const single = kinds.length === 1 ? kinds[0] : undefined;
|
|
76
|
+
const matcherFlag = single === 'import'
|
|
77
|
+
? '--specifier'
|
|
78
|
+
: single === 'registration'
|
|
79
|
+
? '--line'
|
|
80
|
+
: '--specifier or --line';
|
|
81
|
+
const matcher = single === 'import' ? options.specifier : single === 'registration' ? options.line : undefined;
|
|
45
82
|
const hasPair = Boolean(options.file) && Boolean(matcher);
|
|
46
83
|
if (!options.creates && !hasPair) {
|
|
47
|
-
throw new InvalidArgumentError(`--remove${kind === 'registration' ? ' --kind registration' : ''} requires --creates, ` +
|
|
84
|
+
throw new InvalidArgumentError(`--remove${options.kind === 'registration' ? ' --kind registration' : ''} requires --creates, ` +
|
|
48
85
|
`or --file with ${matcherFlag}; --creates alone removes every declaration for that path.`, matcher && !options.file
|
|
49
86
|
? '--file'
|
|
50
87
|
: options.file && !matcher
|
|
51
88
|
? matcherFlag
|
|
52
89
|
: `--creates or --file + ${matcherFlag}`);
|
|
53
90
|
}
|
|
54
|
-
const
|
|
55
|
-
const
|
|
56
|
-
|
|
57
|
-
|
|
91
|
+
const views = stagedViews(kinds, staged);
|
|
92
|
+
const candidates = views.filter(({ kind, view }) => (!options.file || view.file === options.file) &&
|
|
93
|
+
(matcher === undefined ||
|
|
94
|
+
comparableMatcher(kind, view.matcher) === comparableMatcher(kind, matcher)) &&
|
|
58
95
|
(!options.creates || view.creates === options.creates) &&
|
|
59
96
|
(options.owner === undefined || view.owner === options.owner));
|
|
97
|
+
const label = ({ kind, view }) => ` - ${dependencyLabel(kind, view)}`;
|
|
60
98
|
if (candidates.length === 0) {
|
|
61
|
-
const noun =
|
|
99
|
+
const noun = declarationNoun(kinds);
|
|
62
100
|
const existing = views.length === 0
|
|
63
101
|
? `This patch has no ${noun}s.`
|
|
64
|
-
: `This patch's ${noun}s:\n${views.map(
|
|
102
|
+
: `This patch's ${noun}s:\n${views.map(label).join('\n')}`;
|
|
65
103
|
throw new InvalidArgumentError(`--remove matched no ${noun}. ${existing}`, '--remove');
|
|
66
104
|
}
|
|
67
105
|
if (candidates.length > 1 && !options.creates) {
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
throw new GeneralError(`--remove matches ${candidates.length} ${noun} on this patch; pass --creates to pick one:\n${list}`);
|
|
106
|
+
throw new GeneralError(`--remove matches ${candidates.length} ${declarationNoun(kinds)}s on this patch; ` +
|
|
107
|
+
`pass --creates to pick one:\n${candidates.map(label).join('\n')}`);
|
|
71
108
|
}
|
|
72
|
-
const
|
|
73
|
-
if (
|
|
74
|
-
info(`Inferred --creates ${
|
|
109
|
+
const only = candidates.length === 1 ? candidates[0] : undefined;
|
|
110
|
+
if (only !== undefined && !options.creates) {
|
|
111
|
+
info(`Inferred --creates ${only.view.creates} from the single matching staged entry.`);
|
|
75
112
|
}
|
|
76
113
|
return candidates;
|
|
77
114
|
}
|