@hominis/fireforge 0.49.2 → 0.50.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 +40 -1
- package/README.md +1 -1
- package/dist/bin/fireforge.js +6 -0
- package/dist/build-info.json +4 -4
- package/dist/src/commands/build-prelock.d.ts +20 -0
- package/dist/src/commands/build-prelock.js +50 -0
- package/dist/src/commands/build.d.ts +3 -1
- package/dist/src/commands/build.js +13 -6
- package/dist/src/commands/check-report.d.ts +28 -0
- package/dist/src/commands/check-report.js +78 -0
- package/dist/src/commands/check-run.d.ts +28 -0
- package/dist/src/commands/check-run.js +104 -0
- package/dist/src/commands/check-steps.d.ts +67 -0
- package/dist/src/commands/check-steps.js +175 -0
- package/dist/src/commands/check.d.ts +32 -0
- package/dist/src/commands/check.js +160 -0
- package/dist/src/commands/config.js +5 -6
- package/dist/src/commands/doctor-engine-eslint.js +6 -1
- package/dist/src/commands/doctor-source-pin.js +4 -1
- package/dist/src/commands/export-adopt-check.d.ts +38 -0
- package/dist/src/commands/export-adopt-check.js +98 -0
- package/dist/src/commands/export-flow.js +29 -14
- package/dist/src/commands/export-placement-gate.d.ts +6 -2
- package/dist/src/commands/export-shared.d.ts +2 -1
- package/dist/src/commands/export-staged-registration.d.ts +46 -0
- package/dist/src/commands/export-staged-registration.js +91 -0
- package/dist/src/commands/forward-import-remedy.d.ts +32 -0
- package/dist/src/commands/forward-import-remedy.js +63 -0
- package/dist/src/commands/furnace/apply.js +3 -3
- package/dist/src/commands/furnace/chrome-doc-remove.d.ts +2 -1
- package/dist/src/commands/furnace/chrome-doc-remove.js +2 -2
- package/dist/src/commands/furnace/chrome-doc.d.ts +2 -1
- package/dist/src/commands/furnace/chrome-doc.js +2 -2
- package/dist/src/commands/furnace/create.js +2 -2
- package/dist/src/commands/furnace/deploy-named.d.ts +48 -0
- package/dist/src/commands/furnace/deploy-named.js +107 -0
- package/dist/src/commands/furnace/deploy.d.ts +3 -2
- package/dist/src/commands/furnace/deploy.js +50 -86
- package/dist/src/commands/furnace/diff.js +26 -3
- package/dist/src/commands/furnace/index.js +66 -41
- package/dist/src/commands/furnace/init.js +13 -6
- package/dist/src/commands/furnace/override.js +4 -2
- package/dist/src/commands/furnace/preview.js +4 -3
- package/dist/src/commands/furnace/refresh.js +2 -2
- package/dist/src/commands/furnace/remove.js +4 -3
- package/dist/src/commands/furnace/rename-dry-run.d.ts +29 -0
- package/dist/src/commands/furnace/rename-dry-run.js +127 -0
- package/dist/src/commands/furnace/rename-helpers.d.ts +7 -0
- package/dist/src/commands/furnace/rename-helpers.js +15 -1
- package/dist/src/commands/furnace/rename-registrations.d.ts +41 -0
- package/dist/src/commands/furnace/rename-registrations.js +93 -0
- package/dist/src/commands/furnace/rename.d.ts +2 -1
- package/dist/src/commands/furnace/rename.js +28 -66
- package/dist/src/commands/furnace/scan.d.ts +9 -4
- package/dist/src/commands/furnace/scan.js +11 -9
- package/dist/src/commands/furnace/sync.js +3 -2
- package/dist/src/commands/furnace/validate-report.d.ts +45 -0
- package/dist/src/commands/furnace/validate-report.js +91 -0
- package/dist/src/commands/furnace/validate.d.ts +1 -1
- package/dist/src/commands/furnace/validate.js +127 -114
- package/dist/src/commands/furnace/validation-output.js +14 -3
- package/dist/src/commands/lint-per-patch.js +31 -60
- package/dist/src/commands/lint-report.d.ts +8 -4
- package/dist/src/commands/lint-report.js +15 -5
- package/dist/src/commands/lint.js +15 -34
- package/dist/src/commands/manifest.js +2 -0
- package/dist/src/commands/patch/delete-gap-notice.js +3 -1
- package/dist/src/commands/patch/delete.d.ts +1 -1
- package/dist/src/commands/patch/delete.js +13 -98
- package/dist/src/commands/patch/index.d.ts +1 -1
- package/dist/src/commands/patch/index.js +4 -2
- package/dist/src/commands/patch/lint-ignore.d.ts +5 -1
- package/dist/src/commands/patch/lint-ignore.js +65 -28
- package/dist/src/commands/patch/move-files-create.js +26 -17
- package/dist/src/commands/patch/move-files-into.js +5 -3
- package/dist/src/commands/patch/move-files.js +3 -1
- package/dist/src/commands/patch/removal-dependents.d.ts +63 -0
- package/dist/src/commands/patch/removal-dependents.js +133 -0
- package/dist/src/commands/patch/rename.js +7 -0
- package/dist/src/commands/patch/retire.d.ts +34 -0
- package/dist/src/commands/patch/retire.js +128 -0
- package/dist/src/commands/patch/split-plan.d.ts +7 -7
- package/dist/src/commands/patch/split-plan.js +45 -28
- package/dist/src/commands/patch/split-staged.d.ts +108 -0
- package/dist/src/commands/patch/split-staged.js +202 -0
- package/dist/src/commands/patch/split.d.ts +5 -0
- package/dist/src/commands/patch/split.js +51 -15
- package/dist/src/commands/patch/staged-dependency-batch.d.ts +48 -0
- package/dist/src/commands/patch/staged-dependency-batch.js +176 -0
- package/dist/src/commands/patch/staged-dependency-entries.d.ts +36 -0
- package/dist/src/commands/patch/staged-dependency-entries.js +52 -0
- package/dist/src/commands/patch/staged-dependency-from.d.ts +14 -0
- package/dist/src/commands/patch/staged-dependency-from.js +108 -0
- package/dist/src/commands/patch/staged-dependency.js +14 -41
- package/dist/src/commands/re-export-adjacent-registrations.d.ts +20 -0
- package/dist/src/commands/re-export-adjacent-registrations.js +75 -0
- package/dist/src/commands/re-export-adjacent.d.ts +45 -2
- package/dist/src/commands/re-export-adjacent.js +156 -37
- package/dist/src/commands/re-export-drift.d.ts +36 -8
- package/dist/src/commands/re-export-drift.js +73 -49
- package/dist/src/commands/re-export-files.js +11 -8
- package/dist/src/commands/re-export-lint.d.ts +2 -1
- package/dist/src/commands/re-export-lint.js +23 -10
- package/dist/src/commands/re-export-options.js +45 -4
- package/dist/src/commands/re-export-preflight.d.ts +23 -0
- package/dist/src/commands/re-export-preflight.js +160 -0
- package/dist/src/commands/re-export-register.js +42 -17
- package/dist/src/commands/re-export-removed.d.ts +12 -1
- package/dist/src/commands/re-export-removed.js +41 -11
- package/dist/src/commands/re-export-scan-owner.d.ts +44 -0
- package/dist/src/commands/re-export-scan-owner.js +116 -0
- package/dist/src/commands/re-export-scan.d.ts +17 -9
- package/dist/src/commands/re-export-scan.js +56 -23
- package/dist/src/commands/re-export-single.js +22 -6
- package/dist/src/commands/re-export-summary.d.ts +28 -0
- package/dist/src/commands/re-export-summary.js +36 -0
- package/dist/src/commands/re-export.js +37 -24
- package/dist/src/commands/rebase/index.js +17 -17
- package/dist/src/commands/run.js +16 -2
- package/dist/src/commands/status-check.d.ts +55 -16
- package/dist/src/commands/status-check.js +92 -37
- package/dist/src/commands/status-json.d.ts +22 -9
- package/dist/src/commands/status-json.js +47 -32
- package/dist/src/commands/status-output.js +3 -1
- package/dist/src/commands/status-scope.d.ts +37 -0
- package/dist/src/commands/status-scope.js +54 -0
- package/dist/src/commands/status.js +66 -23
- package/dist/src/commands/test-browser-preflight.js +19 -3
- package/dist/src/commands/test-build-furnace.d.ts +21 -0
- package/dist/src/commands/test-build-furnace.js +48 -0
- package/dist/src/commands/test-build-phase.js +47 -41
- package/dist/src/commands/test-checkjs.js +7 -0
- package/dist/src/commands/test-diagnose.js +7 -0
- package/dist/src/commands/test-modes.d.ts +16 -1
- package/dist/src/commands/test-modes.js +24 -1
- package/dist/src/commands/test-multi-verdict.d.ts +61 -0
- package/dist/src/commands/test-multi-verdict.js +111 -0
- package/dist/src/commands/test-orchestrate.d.ts +43 -0
- package/dist/src/commands/test-orchestrate.js +222 -0
- package/dist/src/commands/test-perf-scope.d.ts +29 -0
- package/dist/src/commands/test-perf-scope.js +83 -0
- package/dist/src/commands/test-prelock.d.ts +51 -0
- package/dist/src/commands/test-prelock.js +177 -0
- package/dist/src/commands/test-register.js +54 -10
- package/dist/src/commands/test-run.js +4 -1
- package/dist/src/commands/test-stale-gate.js +57 -27
- package/dist/src/commands/test-verdict.d.ts +10 -1
- package/dist/src/commands/test-verdict.js +28 -1
- package/dist/src/commands/test.d.ts +6 -1
- package/dist/src/commands/test.js +66 -60
- package/dist/src/commands/token-lint.d.ts +14 -0
- package/dist/src/commands/token-lint.js +111 -0
- package/dist/src/commands/token.js +10 -0
- package/dist/src/commands/verify-scope.d.ts +28 -0
- package/dist/src/commands/verify-scope.js +67 -0
- package/dist/src/commands/verify.d.ts +10 -2
- package/dist/src/commands/verify.js +59 -35
- package/dist/src/core/build-baseline-types.d.ts +46 -0
- package/dist/src/core/build-baseline-validate.js +19 -0
- package/dist/src/core/build-baseline.d.ts +5 -1
- package/dist/src/core/build-baseline.js +3 -1
- package/dist/src/core/build-overwrite-guard.d.ts +50 -0
- package/dist/src/core/build-overwrite-guard.js +84 -4
- package/dist/src/core/build-prepare.d.ts +7 -0
- package/dist/src/core/build-prepare.js +14 -8
- package/dist/src/core/build-run-record.d.ts +74 -0
- package/dist/src/core/build-run-record.js +136 -0
- package/dist/src/core/checkjs-support-heads.d.ts +41 -0
- package/dist/src/core/checkjs-support-heads.js +104 -0
- package/dist/src/core/config-paths.d.ts +2 -2
- package/dist/src/core/config-paths.js +8 -0
- package/dist/src/core/config-validate-check.d.ts +8 -0
- package/dist/src/core/config-validate-check.js +143 -0
- package/dist/src/core/config-validate-file-size.d.ts +9 -6
- package/dist/src/core/config-validate-file-size.js +91 -2
- package/dist/src/core/config-validate-history.d.ts +11 -0
- package/dist/src/core/config-validate-history.js +38 -0
- package/dist/src/core/config-validate-patch-policy.js +100 -0
- package/dist/src/core/config-validate-test-toolchains.js +7 -0
- package/dist/src/core/config-validate-token-scales.d.ts +6 -0
- package/dist/src/core/config-validate-token-scales.js +50 -0
- package/dist/src/core/config-validate.js +14 -5
- package/dist/src/core/config.d.ts +5 -0
- package/dist/src/core/config.js +16 -0
- package/dist/src/core/coverage-dispatch.d.ts +24 -0
- package/dist/src/core/coverage-dispatch.js +44 -0
- package/dist/src/core/coverage-extend.d.ts +70 -45
- package/dist/src/core/coverage-extend.js +199 -144
- package/dist/src/core/coverage-records.d.ts +97 -0
- package/dist/src/core/coverage-records.js +350 -0
- package/dist/src/core/destructive.d.ts +7 -4
- package/dist/src/core/destructive.js +10 -10
- package/dist/src/core/engine-session-lock.d.ts +10 -1
- package/dist/src/core/engine-session-lock.js +28 -8
- package/dist/src/core/export-moved-code-shape.d.ts +49 -11
- package/dist/src/core/export-moved-code-shape.js +135 -44
- package/dist/src/core/file-lock-queue.d.ts +67 -0
- package/dist/src/core/file-lock-queue.js +221 -0
- package/dist/src/core/file-lock.d.ts +4 -25
- package/dist/src/core/file-lock.js +105 -138
- package/dist/src/core/furnace-apply-output.d.ts +10 -0
- package/dist/src/core/furnace-apply-output.js +31 -1
- package/dist/src/core/furnace-apply.js +12 -2
- package/dist/src/core/furnace-composed-source.d.ts +76 -0
- package/dist/src/core/furnace-composed-source.js +167 -0
- package/dist/src/core/furnace-config-validate-exceptions.d.ts +9 -0
- package/dist/src/core/furnace-config-validate-exceptions.js +71 -0
- package/dist/src/core/furnace-config-validate.js +8 -0
- package/dist/src/core/furnace-css-fragments.d.ts +10 -0
- package/dist/src/core/furnace-css-fragments.js +26 -0
- package/dist/src/core/furnace-operation.d.ts +3 -3
- package/dist/src/core/furnace-registration-remove.d.ts +22 -1
- package/dist/src/core/furnace-registration-remove.js +53 -17
- package/dist/src/core/furnace-registration-rename.d.ts +26 -0
- package/dist/src/core/furnace-registration-rename.js +77 -0
- package/dist/src/core/furnace-registration.d.ts +14 -4
- package/dist/src/core/furnace-registration.js +44 -10
- package/dist/src/core/furnace-staleness.d.ts +7 -0
- package/dist/src/core/furnace-staleness.js +34 -24
- package/dist/src/core/furnace-validate-accessibility.d.ts +4 -2
- package/dist/src/core/furnace-validate-accessibility.js +10 -1
- package/dist/src/core/furnace-validate-exceptions.d.ts +40 -0
- package/dist/src/core/furnace-validate-exceptions.js +43 -0
- package/dist/src/core/furnace-validate-icon-button.d.ts +7 -0
- package/dist/src/core/furnace-validate-icon-button.js +49 -0
- package/dist/src/core/furnace-validate.js +1 -1
- package/dist/src/core/harness-port-fallback.d.ts +60 -0
- package/dist/src/core/harness-port-fallback.js +96 -0
- package/dist/src/core/history-log-file.d.ts +61 -0
- package/dist/src/core/history-log-file.js +157 -0
- package/dist/src/core/history-log.d.ts +2 -1
- package/dist/src/core/history-log.js +2 -1
- package/dist/src/core/json-preserving-edit.d.ts +22 -0
- package/dist/src/core/json-preserving-edit.js +199 -0
- package/dist/src/core/mach.js +8 -1
- package/dist/src/core/negative-control-diff.d.ts +39 -0
- package/dist/src/core/negative-control-diff.js +136 -0
- package/dist/src/core/negative-control.d.ts +138 -0
- package/dist/src/core/negative-control.js +356 -0
- package/dist/src/core/objdir-install-manifests.d.ts +73 -0
- package/dist/src/core/objdir-install-manifests.js +226 -0
- package/dist/src/core/patch-export-update.d.ts +2 -0
- package/dist/src/core/patch-export-update.js +1 -0
- package/dist/src/core/patch-export.js +3 -4
- package/dist/src/core/patch-lint-checkjs-any-import.d.ts +37 -0
- package/dist/src/core/patch-lint-checkjs-any-import.js +61 -0
- package/dist/src/core/patch-lint-checkjs.js +8 -11
- package/dist/src/core/patch-lint-code-lines.d.ts +59 -0
- package/dist/src/core/patch-lint-code-lines.js +105 -0
- package/dist/src/core/patch-lint-cross.js +12 -4
- package/dist/src/core/patch-lint-css.d.ts +8 -2
- package/dist/src/core/patch-lint-css.js +10 -4
- package/dist/src/core/patch-lint-file-size.d.ts +12 -5
- package/dist/src/core/patch-lint-file-size.js +22 -11
- package/dist/src/core/patch-lint-forward-registration-extended.d.ts +18 -0
- package/dist/src/core/patch-lint-forward-registration-extended.js +35 -10
- package/dist/src/core/patch-lint-forward-registration.js +10 -0
- package/dist/src/core/patch-lint-ignore.d.ts +152 -0
- package/dist/src/core/patch-lint-ignore.js +299 -0
- package/dist/src/core/patch-lint-size-thresholds.d.ts +52 -0
- package/dist/src/core/patch-lint-size-thresholds.js +110 -0
- package/dist/src/core/patch-lint-size.d.ts +117 -0
- package/dist/src/core/patch-lint-size.js +323 -0
- package/dist/src/core/patch-lint-staged-registration.d.ts +18 -1
- package/dist/src/core/patch-lint-staged-registration.js +20 -0
- package/dist/src/core/patch-lint-token-scale.d.ts +39 -0
- package/dist/src/core/patch-lint-token-scale.js +109 -0
- package/dist/src/core/patch-lint.d.ts +11 -83
- package/dist/src/core/patch-lint.js +64 -286
- package/dist/src/core/patch-lock.js +7 -6
- package/dist/src/core/patch-manifest-consistency.js +7 -6
- package/dist/src/core/patch-manifest-io.d.ts +6 -3
- package/dist/src/core/patch-manifest-io.js +19 -8
- package/dist/src/core/patch-manifest-validate.js +34 -2
- package/dist/src/core/patch-order-legal.d.ts +25 -0
- package/dist/src/core/patch-order-legal.js +83 -0
- package/dist/src/core/patch-policy-content.d.ts +40 -0
- package/dist/src/core/patch-policy-content.js +216 -0
- package/dist/src/core/patch-policy.d.ts +16 -3
- package/dist/src/core/patch-policy.js +16 -9
- package/dist/src/core/patch-retired.d.ts +35 -0
- package/dist/src/core/patch-retired.js +54 -0
- package/dist/src/core/patch-subset.d.ts +16 -0
- package/dist/src/core/patch-subset.js +66 -0
- package/dist/src/core/preflight-wait.d.ts +20 -0
- package/dist/src/core/preflight-wait.js +38 -0
- package/dist/src/core/rebase-from-version.d.ts +46 -0
- package/dist/src/core/rebase-from-version.js +71 -0
- package/dist/src/core/run-log.d.ts +20 -0
- package/dist/src/core/run-log.js +89 -3
- package/dist/src/core/run-profile.d.ts +7 -0
- package/dist/src/core/run-profile.js +7 -1
- package/dist/src/core/smoke-patterns.d.ts +5 -0
- package/dist/src/core/smoke-patterns.js +13 -0
- package/dist/src/core/test-harness-crash.d.ts +18 -1
- package/dist/src/core/test-harness-crash.js +6 -4
- package/dist/src/core/test-harness-signature.d.ts +17 -0
- package/dist/src/core/test-harness-signature.js +4 -0
- package/dist/src/core/test-run-completeness.d.ts +57 -0
- package/dist/src/core/test-run-completeness.js +144 -0
- package/dist/src/core/test-stale-check.d.ts +27 -1
- package/dist/src/core/test-stale-check.js +39 -49
- package/dist/src/core/test-stale-exempt.d.ts +40 -0
- package/dist/src/core/test-stale-exempt.js +70 -0
- package/dist/src/core/token-lint.d.ts +50 -0
- package/dist/src/core/token-lint.js +126 -0
- package/dist/src/core/tree-guard.js +3 -0
- package/dist/src/core/typecheck-shim.d.ts +1 -1
- package/dist/src/core/typecheck-shim.js +90 -2
- package/dist/src/index.d.ts +8 -3
- package/dist/src/index.js +3 -1
- package/dist/src/types/commands/index.d.ts +2 -2
- package/dist/src/types/commands/options.d.ts +148 -21
- package/dist/src/types/commands/patches.d.ts +75 -1
- package/dist/src/types/config.d.ts +192 -0
- package/dist/src/types/config.js +9 -0
- package/dist/src/types/furnace.d.ts +43 -1
- package/dist/src/types/furnace.js +7 -0
- package/dist/src/types/index.d.ts +1 -1
- package/dist/src/utils/options.d.ts +34 -6
- package/dist/src/utils/options.js +86 -5
- package/dist/src/utils/path-glob.d.ts +15 -0
- package/dist/src/utils/path-glob.js +47 -0
- package/dist/src/utils/regex.d.ts +15 -3
- package/dist/src/utils/regex.js +37 -16
- package/docs/configuration.md +282 -9
- package/docs/exit-codes.md +18 -1
- package/docs/furnace.md +97 -3
- package/docs/lifecycle-invariants.md +52 -19
- package/docs/machine-output.md +74 -12
- package/docs/patch-workflow.md +297 -22
- package/docs/run-logs.md +20 -1
- package/docs/testing.md +276 -20
- package/package.json +1 -1
|
@@ -22,9 +22,11 @@ import { assertConfirmationAvailable, confirmDestructive } from '../../core/dest
|
|
|
22
22
|
import { appendHistoryBestEffort } from '../../core/history-log.js';
|
|
23
23
|
import { formatPatchNotFoundError } from '../../core/patch-identifier-suggest.js';
|
|
24
24
|
import { buildPatchQueueContext } from '../../core/patch-lint.js';
|
|
25
|
+
import { buildLintIgnoreChecks } from '../../core/patch-lint-ignore.js';
|
|
25
26
|
import { withPatchDirectoryLock } from '../../core/patch-lock.js';
|
|
26
27
|
import { loadPatchesManifest, renumberPatchesInManifest, resolvePatchIdentifier, savePatchesManifest, validatePatchesManifest, } from '../../core/patch-manifest.js';
|
|
27
28
|
import { enforcePatchPolicy } from '../../core/patch-policy.js';
|
|
29
|
+
import { manifestWithRetiredSlots } from '../../core/patch-retired.js';
|
|
28
30
|
import { GeneralError, InvalidArgumentError } from '../../errors/base.js';
|
|
29
31
|
import { toError } from '../../utils/errors.js';
|
|
30
32
|
import { readText, removeFile, writeText } from '../../utils/fs.js';
|
|
@@ -34,7 +36,32 @@ import { normalizePatchDisplayName, parsePositiveIntegerFlag } from '../../utils
|
|
|
34
36
|
import { proceedAfterDecision } from '../destructive-decision.js';
|
|
35
37
|
import { placementPlansEqual, resolvePlacementPlan } from '../export-flow.js';
|
|
36
38
|
import { runPatchLint } from '../export-shared.js';
|
|
37
|
-
import { assertSourceOwnsFiles, buildNewPatchMetadata, buildSplitDiff, buildSplitSummary,
|
|
39
|
+
import { assertSourceOwnsFiles, buildNewPatchMetadata, buildSplitDiff, buildSplitSummary, mergeStagedForwardImports, projectSplitManifest, runProjectedSplitLint, } from './split-plan.js';
|
|
40
|
+
import { describeStagedMaintenance, emptyStagedMaintenance, findOwnerRewriteHolders, rewriteSplitOwners, withoutDroppedDeclarations, withoutRelocatedDeclarations, } from './split-staged.js';
|
|
41
|
+
/**
|
|
42
|
+
* History payload for the declarations a split re-homed or dropped, omitted
|
|
43
|
+
* when it touched none so unaffected records keep their old shape.
|
|
44
|
+
*/
|
|
45
|
+
function stagedMaintenanceHistory(plan) {
|
|
46
|
+
const { relocated, dropped } = plan.stagedMaintenance;
|
|
47
|
+
const relocatedCount = (relocated.forwardImports?.length ?? 0) + (relocated.registrations?.length ?? 0);
|
|
48
|
+
return {
|
|
49
|
+
...(relocatedCount > 0 ? { relocatedDeclarations: relocated } : {}),
|
|
50
|
+
...(dropped.length > 0 ? { droppedDeclarations: dropped } : {}),
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Prints the owner rewrites and the declarations the move re-homed or
|
|
55
|
+
* dropped, after a committed split or `move-files --create`.
|
|
56
|
+
*/
|
|
57
|
+
export function reportSplitStagedChanges(plan) {
|
|
58
|
+
if (plan.ownerRewrites.length > 0) {
|
|
59
|
+
info(`Re-pointed staged-dependency owners in: ${plan.ownerRewrites.join(', ')}`);
|
|
60
|
+
}
|
|
61
|
+
for (const line of describeStagedMaintenance(plan.stagedMaintenance, plan.source.filename, plan.placement.newFilename)) {
|
|
62
|
+
info(line);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
38
65
|
/**
|
|
39
66
|
* Commits a confirmed split under the patch directory lock: renumber →
|
|
40
67
|
* write new patch body → write shrunken source body → single manifest
|
|
@@ -84,13 +111,17 @@ export async function commitPatchSplit(patchesDir, plan, newMetadata, options, c
|
|
|
84
111
|
if (decls?.length) {
|
|
85
112
|
withOwners = mergeStagedForwardImports(withOwners, decls);
|
|
86
113
|
}
|
|
114
|
+
withOwners = withoutDroppedDeclarations(withOwners, plan.stagedMaintenance.dropped);
|
|
87
115
|
if (patch.filename !== effectiveSourceFilename)
|
|
88
116
|
return withOwners;
|
|
89
|
-
return {
|
|
117
|
+
return {
|
|
118
|
+
...withoutRelocatedDeclarations(withOwners, movedSet),
|
|
119
|
+
filesAffected: plan.remainingFiles,
|
|
120
|
+
};
|
|
90
121
|
});
|
|
91
122
|
updatedPatches.push(newMetadata);
|
|
92
123
|
updatedPatches.sort((a, b) => a.order - b.order || a.filename.localeCompare(b.filename));
|
|
93
|
-
const updated = validatePatchesManifest(
|
|
124
|
+
const updated = validatePatchesManifest(manifestWithRetiredSlots(fresh, updatedPatches));
|
|
94
125
|
await savePatchesManifest(patchesDir, updated);
|
|
95
126
|
await appendHistoryBestEffort(patchesDir, {
|
|
96
127
|
operation: 'patch-split',
|
|
@@ -100,6 +131,7 @@ export async function commitPatchSplit(patchesDir, plan, newMetadata, options, c
|
|
|
100
131
|
order: plan.placement.insertionOrder,
|
|
101
132
|
files: plan.movedFiles,
|
|
102
133
|
ownerRewrites: plan.ownerRewrites,
|
|
134
|
+
...stagedMaintenanceHistory(plan),
|
|
103
135
|
renames: [...plan.placement.renameMap.entries()].map(([from, entry]) => ({
|
|
104
136
|
from,
|
|
105
137
|
to: entry.newFilename,
|
|
@@ -212,16 +244,27 @@ export async function patchSplitCommand(projectRoot, sourceId, options) {
|
|
|
212
244
|
name: displayName,
|
|
213
245
|
description: options.description ?? '',
|
|
214
246
|
ownerRewrites: findOwnerRewriteHolders(manifest.patches, source.filename, movedSet),
|
|
215
|
-
// Populated by runProjectedSplitLint below (forward edges into the new
|
|
247
|
+
// Populated by runProjectedSplitLint below (forward edges into the new
|
|
248
|
+
// patch, and the declarations the move re-homes or makes unused).
|
|
216
249
|
stagedDependencyAdditions: new Map(),
|
|
250
|
+
stagedMaintenance: emptyStagedMaintenance(),
|
|
217
251
|
};
|
|
252
|
+
// Policy first: the projected manifest (orders, ranges, the gap rule)
|
|
253
|
+
// does not depend on the lint below, so a refused --order fails before
|
|
254
|
+
// the per-patch and projected lint passes instead of after them.
|
|
255
|
+
enforcePatchPolicy({
|
|
256
|
+
config,
|
|
257
|
+
manifest: projectSplitManifest(manifest, plan, buildNewPatchMetadata(plan, config)),
|
|
258
|
+
command: 'patch split',
|
|
259
|
+
forceUnsafe: options.forceUnsafe === true,
|
|
260
|
+
});
|
|
218
261
|
// Per-patch lint both projected bodies, threading the source patch's
|
|
219
262
|
// tier/lintIgnore so an intentional-advisory patch can still split.
|
|
220
263
|
// The whole-queue context (built once, with the config for committed-gate
|
|
221
264
|
// parity) resolves cross-patch imports and sibling head.js harness roots
|
|
222
265
|
// instead of linting each projected body blind.
|
|
223
266
|
const patchQueueCtx = await buildPatchQueueContext(paths.patches, config);
|
|
224
|
-
const ignoreChecks =
|
|
267
|
+
const ignoreChecks = buildLintIgnoreChecks(source.lintIgnore);
|
|
225
268
|
await runPatchLint({
|
|
226
269
|
engineDir: paths.engine,
|
|
227
270
|
filesAffected: remainingFiles,
|
|
@@ -242,15 +285,10 @@ export async function patchSplitCommand(projectRoot, sourceId, options) {
|
|
|
242
285
|
ignoreChecks,
|
|
243
286
|
patchTier: source.tier,
|
|
244
287
|
});
|
|
245
|
-
const { conflicts, stagedDependencyAdditions } = runProjectedSplitLint(plan, patchQueueCtx);
|
|
288
|
+
const { conflicts, stagedDependencyAdditions, stagedMaintenance } = runProjectedSplitLint(plan, patchQueueCtx);
|
|
246
289
|
plan.stagedDependencyAdditions = stagedDependencyAdditions;
|
|
290
|
+
plan.stagedMaintenance = stagedMaintenance;
|
|
247
291
|
const newMetadata = buildNewPatchMetadata(plan, config);
|
|
248
|
-
enforcePatchPolicy({
|
|
249
|
-
config,
|
|
250
|
-
manifest: projectSplitManifest(manifest, plan, newMetadata),
|
|
251
|
-
command: 'patch split',
|
|
252
|
-
forceUnsafe: options.forceUnsafe === true,
|
|
253
|
-
});
|
|
254
292
|
const decision = await confirmDestructive({
|
|
255
293
|
operation: 'patch-split',
|
|
256
294
|
title: `Split ${plan.movedFiles.length} file(s) out of ${source.filename} into ${placement.newFilename}`,
|
|
@@ -264,9 +302,7 @@ export async function patchSplitCommand(projectRoot, sourceId, options) {
|
|
|
264
302
|
return;
|
|
265
303
|
await commitPatchSplit(paths.patches, plan, newMetadata, options, config);
|
|
266
304
|
success(`Split ${source.filename}: ${placement.newFilename} now owns ${plan.movedFiles.length} file(s)`);
|
|
267
|
-
|
|
268
|
-
info(`Re-pointed staged-dependency owners in: ${plan.ownerRewrites.join(', ')}`);
|
|
269
|
-
}
|
|
305
|
+
reportSplitStagedChanges(plan);
|
|
270
306
|
outro('Split complete');
|
|
271
307
|
}
|
|
272
308
|
/**
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Batch input and history delta for `patch staged-dependency`.
|
|
3
|
+
*
|
|
4
|
+
* `--from <json-file>` lets one `--add` call declare many edges under one
|
|
5
|
+
* lock hold. Turning on `patchLint.forwardRegistration: "extended"` on a real
|
|
6
|
+
* queue surfaced hundreds of undeclared registrations, each printed with its
|
|
7
|
+
* own command, and one call per edge was the only way to declare them. The
|
|
8
|
+
* file is either a `lint --per-patch --report` JSON (its findings carry the
|
|
9
|
+
* `stagedDeclarations` that discharge them) or a plain array of
|
|
10
|
+
* declarations.
|
|
11
|
+
*
|
|
12
|
+
* The history record of every call holds only the delta (`added` /
|
|
13
|
+
* `removed`). Recording the patch's whole list before and after grew the
|
|
14
|
+
* log quadratically with the list: 849 single-edge calls took one consumer's
|
|
15
|
+
* log from 36 MB to 184 MB.
|
|
16
|
+
*/
|
|
17
|
+
import type { PatchStagedDependencies, PatchStagedForwardImport, PatchStagedRegistration } from '../../types/commands/index.js';
|
|
18
|
+
/** Declarations read from a `--from` file for one target patch. */
|
|
19
|
+
export interface StagedDependencyBatch {
|
|
20
|
+
forwardImports: PatchStagedForwardImport[];
|
|
21
|
+
registrations: PatchStagedRegistration[];
|
|
22
|
+
/** Entries naming a different patch, left out. */
|
|
23
|
+
skippedForOtherPatches: number;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Reads the declarations a `--from` file supplies for `targetFilename`.
|
|
27
|
+
*
|
|
28
|
+
* Every entry takes `file`, `creates`, optional `owner` and `reason`, and
|
|
29
|
+
* either `specifier` (an import) or `line` (a registration); `kind` may
|
|
30
|
+
* name the shape explicitly. An entry with a `patch` field naming another
|
|
31
|
+
* patch is skipped and counted, so one report can be applied patch by
|
|
32
|
+
* patch. `defaultReason` (the `--reason` flag) fills entries without one.
|
|
33
|
+
*
|
|
34
|
+
* @param path - The JSON file
|
|
35
|
+
* @param targetFilename - Filename of the patch being edited
|
|
36
|
+
* @param defaultReason - Reason for entries that carry none
|
|
37
|
+
*/
|
|
38
|
+
export declare function readStagedDependencyBatch(path: string, targetFilename: string, defaultReason?: string): Promise<StagedDependencyBatch>;
|
|
39
|
+
/**
|
|
40
|
+
* The history delta between two committed `stagedDependencies` values:
|
|
41
|
+
* the declarations the call added and the ones it removed. Each side is
|
|
42
|
+
* omitted when empty. Older log records carry `before`/`after` instead;
|
|
43
|
+
* a reader should accept either.
|
|
44
|
+
*/
|
|
45
|
+
export declare function stagedDependencyDelta(before: PatchStagedDependencies | undefined, after: PatchStagedDependencies | undefined): {
|
|
46
|
+
added?: PatchStagedDependencies;
|
|
47
|
+
removed?: PatchStagedDependencies;
|
|
48
|
+
};
|
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
// SPDX-License-Identifier: EUPL-1.2
|
|
2
|
+
/**
|
|
3
|
+
* Batch input and history delta for `patch staged-dependency`.
|
|
4
|
+
*
|
|
5
|
+
* `--from <json-file>` lets one `--add` call declare many edges under one
|
|
6
|
+
* lock hold. Turning on `patchLint.forwardRegistration: "extended"` on a real
|
|
7
|
+
* queue surfaced hundreds of undeclared registrations, each printed with its
|
|
8
|
+
* own command, and one call per edge was the only way to declare them. The
|
|
9
|
+
* file is either a `lint --per-patch --report` JSON (its findings carry the
|
|
10
|
+
* `stagedDeclarations` that discharge them) or a plain array of
|
|
11
|
+
* declarations.
|
|
12
|
+
*
|
|
13
|
+
* The history record of every call holds only the delta (`added` /
|
|
14
|
+
* `removed`). Recording the patch's whole list before and after grew the
|
|
15
|
+
* log quadratically with the list: 849 single-edge calls took one consumer's
|
|
16
|
+
* log from 36 MB to 184 MB.
|
|
17
|
+
*/
|
|
18
|
+
import { InvalidArgumentError } from '../../errors/base.js';
|
|
19
|
+
import { toError } from '../../utils/errors.js';
|
|
20
|
+
import { readJson } from '../../utils/fs.js';
|
|
21
|
+
import { isArray, isObject } from '../../utils/validation.js';
|
|
22
|
+
function optionalString(record, key, label) {
|
|
23
|
+
const value = record[key];
|
|
24
|
+
if (value === undefined)
|
|
25
|
+
return undefined;
|
|
26
|
+
if (typeof value !== 'string' || value.length === 0) {
|
|
27
|
+
throw new InvalidArgumentError(`${label}.${key} must be a non-empty string.`, '--from');
|
|
28
|
+
}
|
|
29
|
+
return value;
|
|
30
|
+
}
|
|
31
|
+
function requiredString(record, key, label) {
|
|
32
|
+
const value = optionalString(record, key, label);
|
|
33
|
+
if (value === undefined) {
|
|
34
|
+
throw new InvalidArgumentError(`${label} is missing "${key}".`, '--from');
|
|
35
|
+
}
|
|
36
|
+
return value;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Collects the declaration records a parsed file holds: a lint report's
|
|
40
|
+
* `stagedDeclarations` (from `queueIssues` and every patch's `issues`), or
|
|
41
|
+
* the elements of a plain array.
|
|
42
|
+
*/
|
|
43
|
+
function collectRecords(data) {
|
|
44
|
+
if (isArray(data)) {
|
|
45
|
+
return data.map((entry, index) => {
|
|
46
|
+
if (!isObject(entry)) {
|
|
47
|
+
throw new InvalidArgumentError(`--from entry [${index}] must be an object.`, '--from');
|
|
48
|
+
}
|
|
49
|
+
return { record: entry, label: `--from entry [${index}]` };
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
if (!isObject(data)) {
|
|
53
|
+
throw new InvalidArgumentError('--from must hold a JSON array of declarations or a "lint --per-patch --report" document.', '--from');
|
|
54
|
+
}
|
|
55
|
+
const issues = [];
|
|
56
|
+
if (isArray(data['queueIssues']))
|
|
57
|
+
issues.push(...data['queueIssues']);
|
|
58
|
+
if (isArray(data['patches'])) {
|
|
59
|
+
for (const patch of data['patches']) {
|
|
60
|
+
if (isObject(patch) && isArray(patch['issues']))
|
|
61
|
+
issues.push(...patch['issues']);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
const records = [];
|
|
65
|
+
for (const issue of issues) {
|
|
66
|
+
if (!isObject(issue) || !isArray(issue['stagedDeclarations']))
|
|
67
|
+
continue;
|
|
68
|
+
for (const declaration of issue['stagedDeclarations']) {
|
|
69
|
+
if (isObject(declaration)) {
|
|
70
|
+
records.push({
|
|
71
|
+
record: declaration,
|
|
72
|
+
label: `stagedDeclarations of [${String(issue['check'])}] ${String(issue['file'])}`,
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
return records;
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Reads the declarations a `--from` file supplies for `targetFilename`.
|
|
81
|
+
*
|
|
82
|
+
* Every entry takes `file`, `creates`, optional `owner` and `reason`, and
|
|
83
|
+
* either `specifier` (an import) or `line` (a registration); `kind` may
|
|
84
|
+
* name the shape explicitly. An entry with a `patch` field naming another
|
|
85
|
+
* patch is skipped and counted, so one report can be applied patch by
|
|
86
|
+
* patch. `defaultReason` (the `--reason` flag) fills entries without one.
|
|
87
|
+
*
|
|
88
|
+
* @param path - The JSON file
|
|
89
|
+
* @param targetFilename - Filename of the patch being edited
|
|
90
|
+
* @param defaultReason - Reason for entries that carry none
|
|
91
|
+
*/
|
|
92
|
+
export async function readStagedDependencyBatch(path, targetFilename, defaultReason) {
|
|
93
|
+
let data;
|
|
94
|
+
try {
|
|
95
|
+
data = await readJson(path);
|
|
96
|
+
}
|
|
97
|
+
catch (error) {
|
|
98
|
+
throw new InvalidArgumentError(`--from ${path} could not be read as JSON: ${toError(error).message}`, '--from');
|
|
99
|
+
}
|
|
100
|
+
const batch = {
|
|
101
|
+
forwardImports: [],
|
|
102
|
+
registrations: [],
|
|
103
|
+
skippedForOtherPatches: 0,
|
|
104
|
+
};
|
|
105
|
+
for (const { record, label } of collectRecords(data)) {
|
|
106
|
+
const patch = optionalString(record, 'patch', label);
|
|
107
|
+
if (patch !== undefined && patch !== targetFilename) {
|
|
108
|
+
batch.skippedForOtherPatches += 1;
|
|
109
|
+
continue;
|
|
110
|
+
}
|
|
111
|
+
const specifier = optionalString(record, 'specifier', label);
|
|
112
|
+
const line = optionalString(record, 'line', label);
|
|
113
|
+
const kind = record['kind'] ?? (line !== undefined ? 'registration' : 'import');
|
|
114
|
+
if (kind !== 'import' && kind !== 'registration') {
|
|
115
|
+
throw new InvalidArgumentError(`${label}.kind must be "import" or "registration".`, '--from');
|
|
116
|
+
}
|
|
117
|
+
const file = requiredString(record, 'file', label);
|
|
118
|
+
const creates = requiredString(record, 'creates', label);
|
|
119
|
+
const owner = optionalString(record, 'owner', label);
|
|
120
|
+
const reason = optionalString(record, 'reason', label) ?? defaultReason;
|
|
121
|
+
const extras = {
|
|
122
|
+
...(owner !== undefined ? { owner } : {}),
|
|
123
|
+
...(reason !== undefined ? { reason } : {}),
|
|
124
|
+
};
|
|
125
|
+
if (kind === 'import') {
|
|
126
|
+
if (specifier === undefined || line !== undefined) {
|
|
127
|
+
throw new InvalidArgumentError(`${label} is an import declaration and needs "specifier" (not "line").`, '--from');
|
|
128
|
+
}
|
|
129
|
+
batch.forwardImports.push({ file, specifier, creates, ...extras });
|
|
130
|
+
}
|
|
131
|
+
else {
|
|
132
|
+
if (line === undefined || specifier !== undefined) {
|
|
133
|
+
throw new InvalidArgumentError(`${label} is a registration declaration and needs "line" (not "specifier").`, '--from');
|
|
134
|
+
}
|
|
135
|
+
batch.registrations.push({ file, line, creates, ...extras });
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
return batch;
|
|
139
|
+
}
|
|
140
|
+
function entryKey(entry) {
|
|
141
|
+
return JSON.stringify(entry);
|
|
142
|
+
}
|
|
143
|
+
function difference(left, right) {
|
|
144
|
+
const keys = new Set((right ?? []).map(entryKey));
|
|
145
|
+
return (left ?? []).filter((entry) => !keys.has(entryKey(entry)));
|
|
146
|
+
}
|
|
147
|
+
function nonEmpty(staged) {
|
|
148
|
+
const forwardImports = staged.forwardImports ?? [];
|
|
149
|
+
const registrations = staged.registrations ?? [];
|
|
150
|
+
if (forwardImports.length === 0 && registrations.length === 0)
|
|
151
|
+
return undefined;
|
|
152
|
+
return {
|
|
153
|
+
...(forwardImports.length > 0 ? { forwardImports } : {}),
|
|
154
|
+
...(registrations.length > 0 ? { registrations } : {}),
|
|
155
|
+
};
|
|
156
|
+
}
|
|
157
|
+
/**
|
|
158
|
+
* The history delta between two committed `stagedDependencies` values:
|
|
159
|
+
* the declarations the call added and the ones it removed. Each side is
|
|
160
|
+
* omitted when empty. Older log records carry `before`/`after` instead;
|
|
161
|
+
* a reader should accept either.
|
|
162
|
+
*/
|
|
163
|
+
export function stagedDependencyDelta(before, after) {
|
|
164
|
+
const added = nonEmpty({
|
|
165
|
+
forwardImports: difference(after?.forwardImports, before?.forwardImports),
|
|
166
|
+
registrations: difference(after?.registrations, before?.registrations),
|
|
167
|
+
});
|
|
168
|
+
const removed = nonEmpty({
|
|
169
|
+
forwardImports: difference(before?.forwardImports, after?.forwardImports),
|
|
170
|
+
registrations: difference(before?.registrations, after?.registrations),
|
|
171
|
+
});
|
|
172
|
+
return {
|
|
173
|
+
...(added !== undefined ? { added } : {}),
|
|
174
|
+
...(removed !== undefined ? { removed } : {}),
|
|
175
|
+
};
|
|
176
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Kind-neutral views and list operations over staged-dependency
|
|
3
|
+
* declarations, shared by `patch staged-dependency` and its `--from` batch
|
|
4
|
+
* path. Split out of `staged-dependency.ts` for the per-file line budget.
|
|
5
|
+
*/
|
|
6
|
+
import type { PatchStagedDependencies, PatchStagedForwardImport, PatchStagedRegistration } from '../../types/commands/index.js';
|
|
7
|
+
/** The command's mutation mode. */
|
|
8
|
+
export type StagedDependencyMode = 'add' | 'remove' | 'clear';
|
|
9
|
+
/** The declaration shape (`--kind`). */
|
|
10
|
+
export type StagedDependencyKind = 'import' | 'registration';
|
|
11
|
+
/**
|
|
12
|
+
* One staged-dependency entry of either kind. The `matcher` field is the
|
|
13
|
+
* kind-specific middle term: the import specifier for import entries, the
|
|
14
|
+
* registration line for registration entries. Key/removal/label logic is
|
|
15
|
+
* shared across kinds through this shape.
|
|
16
|
+
*/
|
|
17
|
+
export interface StagedEntryView {
|
|
18
|
+
file: string;
|
|
19
|
+
matcher: string;
|
|
20
|
+
creates: string;
|
|
21
|
+
owner?: string;
|
|
22
|
+
reason?: string;
|
|
23
|
+
}
|
|
24
|
+
/** View of an import declaration. */
|
|
25
|
+
export declare function importView(dependency: PatchStagedForwardImport): StagedEntryView;
|
|
26
|
+
/** View of a registration declaration. */
|
|
27
|
+
export declare function registrationView(dependency: PatchStagedRegistration): StagedEntryView;
|
|
28
|
+
/** One-line operator label for a declaration. */
|
|
29
|
+
export declare function dependencyLabel(kind: StagedDependencyKind, view: StagedEntryView): string;
|
|
30
|
+
/**
|
|
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.
|
|
33
|
+
*/
|
|
34
|
+
export declare function applyMode<T>(existing: readonly T[], mode: StagedDependencyMode, target: StagedEntryView | undefined, toView: (entry: T) => StagedEntryView, addition: T | undefined): T[];
|
|
35
|
+
/** Number of declarations of both kinds. */
|
|
36
|
+
export declare function countStagedEntries(staged: PatchStagedDependencies | undefined): number;
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
// SPDX-License-Identifier: EUPL-1.2
|
|
2
|
+
/**
|
|
3
|
+
* Kind-neutral views and list operations over staged-dependency
|
|
4
|
+
* declarations, shared by `patch staged-dependency` and its `--from` batch
|
|
5
|
+
* path. Split out of `staged-dependency.ts` for the per-file line budget.
|
|
6
|
+
*/
|
|
7
|
+
/** View of an import declaration. */
|
|
8
|
+
export function importView(dependency) {
|
|
9
|
+
return { ...dependency, matcher: dependency.specifier };
|
|
10
|
+
}
|
|
11
|
+
/** View of a registration declaration. */
|
|
12
|
+
export function registrationView(dependency) {
|
|
13
|
+
return { ...dependency, matcher: dependency.line };
|
|
14
|
+
}
|
|
15
|
+
function dependencyKey(view) {
|
|
16
|
+
return [view.file, view.matcher, view.creates, view.owner ?? ''].join('\0');
|
|
17
|
+
}
|
|
18
|
+
/** One-line operator label for a declaration. */
|
|
19
|
+
export function dependencyLabel(kind, view) {
|
|
20
|
+
const owner = view.owner ? ` owner=${view.owner}` : '';
|
|
21
|
+
const reason = view.reason ? ` reason="${view.reason}"` : '';
|
|
22
|
+
if (kind === 'import') {
|
|
23
|
+
return `${view.file} imports "${view.matcher}" from ${view.creates}${owner}${reason}`;
|
|
24
|
+
}
|
|
25
|
+
return `${view.file} registers "${view.matcher}" for ${view.creates}${owner}${reason}`;
|
|
26
|
+
}
|
|
27
|
+
function matchesTarget(view, target) {
|
|
28
|
+
return (view.file === target.file &&
|
|
29
|
+
view.matcher === target.matcher &&
|
|
30
|
+
view.creates === target.creates &&
|
|
31
|
+
(target.owner === undefined || view.owner === target.owner));
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
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.
|
|
36
|
+
*/
|
|
37
|
+
export function applyMode(existing, mode, target, toView, addition) {
|
|
38
|
+
if (mode === 'clear')
|
|
39
|
+
return [];
|
|
40
|
+
if (!target)
|
|
41
|
+
return [...existing];
|
|
42
|
+
if (mode === 'remove')
|
|
43
|
+
return existing.filter((entry) => !matchesTarget(toView(entry), target));
|
|
44
|
+
const seen = new Set(existing.map((entry) => dependencyKey(toView(entry))));
|
|
45
|
+
if (seen.has(dependencyKey(target)) || addition === undefined)
|
|
46
|
+
return [...existing];
|
|
47
|
+
return [...existing, addition];
|
|
48
|
+
}
|
|
49
|
+
/** Number of declarations of both kinds. */
|
|
50
|
+
export function countStagedEntries(staged) {
|
|
51
|
+
return (staged?.forwardImports?.length ?? 0) + (staged?.registrations?.length ?? 0);
|
|
52
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `patch staged-dependency --add --from <json-file>`: adds every
|
|
3
|
+
* declaration a file supplies for one patch in a single manifest update
|
|
4
|
+
* under one lock hold. The file format and the history delta live in
|
|
5
|
+
* `staged-dependency-batch.ts`.
|
|
6
|
+
*/
|
|
7
|
+
import type { PatchStagedDependencyOptions } from '../../types/commands/index.js';
|
|
8
|
+
import { type StagedDependencyMode } from './staged-dependency-entries.js';
|
|
9
|
+
/**
|
|
10
|
+
* `--add --from <json-file>`: every declaration the file supplies for the
|
|
11
|
+
* target patch, validated up front and written in one manifest update
|
|
12
|
+
* under one lock hold, with one delta history record.
|
|
13
|
+
*/
|
|
14
|
+
export declare function runBatchAdd(projectRoot: string, identifier: string, mode: StagedDependencyMode, options: PatchStagedDependencyOptions): Promise<void>;
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
// SPDX-License-Identifier: EUPL-1.2
|
|
2
|
+
/**
|
|
3
|
+
* `patch staged-dependency --add --from <json-file>`: adds every
|
|
4
|
+
* declaration a file supplies for one patch in a single manifest update
|
|
5
|
+
* under one lock hold. The file format and the history delta live in
|
|
6
|
+
* `staged-dependency-batch.ts`.
|
|
7
|
+
*/
|
|
8
|
+
import { appendHistoryBestEffort } from '../../core/history-log.js';
|
|
9
|
+
import { mutatePatchMetadata } from '../../core/patch-export.js';
|
|
10
|
+
import { GeneralError, InvalidArgumentError } from '../../errors/base.js';
|
|
11
|
+
import { info, outro } from '../../utils/logger.js';
|
|
12
|
+
import { resolveWaitLockSeconds } from '../../utils/options.js';
|
|
13
|
+
import { requirePatchQueue, requirePatchTarget } from './patch-context.js';
|
|
14
|
+
import { readStagedDependencyBatch, stagedDependencyDelta, } from './staged-dependency-batch.js';
|
|
15
|
+
import { applyMode, countStagedEntries, importView, registrationView, } from './staged-dependency-entries.js';
|
|
16
|
+
import { validateStagedDependencyAdd } from './staged-dependency-validate.js';
|
|
17
|
+
/** Flags that describe one declaration and so cannot combine with `--from`. */
|
|
18
|
+
const SINGLE_DECLARATION_FLAGS = [
|
|
19
|
+
['--kind', 'kind'],
|
|
20
|
+
['--file', 'file'],
|
|
21
|
+
['--specifier', 'specifier'],
|
|
22
|
+
['--line', 'line'],
|
|
23
|
+
['--creates', 'creates'],
|
|
24
|
+
['--owner', 'owner'],
|
|
25
|
+
];
|
|
26
|
+
/** Adds every `--from` declaration not already present, keyed like a single `--add`. */
|
|
27
|
+
function mergeBatch(staged, batch) {
|
|
28
|
+
let forwardImports = [...(staged?.forwardImports ?? [])];
|
|
29
|
+
for (const entry of batch.forwardImports) {
|
|
30
|
+
forwardImports = applyMode(forwardImports, 'add', importView(entry), importView, entry);
|
|
31
|
+
}
|
|
32
|
+
let registrations = [...(staged?.registrations ?? [])];
|
|
33
|
+
for (const entry of batch.registrations) {
|
|
34
|
+
registrations = applyMode(registrations, 'add', registrationView(entry), registrationView, entry);
|
|
35
|
+
}
|
|
36
|
+
return {
|
|
37
|
+
...(forwardImports.length > 0 ? { forwardImports } : {}),
|
|
38
|
+
...(registrations.length > 0 ? { registrations } : {}),
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* `--add --from <json-file>`: every declaration the file supplies for the
|
|
43
|
+
* target patch, validated up front and written in one manifest update
|
|
44
|
+
* under one lock hold, with one delta history record.
|
|
45
|
+
*/
|
|
46
|
+
export async function runBatchAdd(projectRoot, identifier, mode, options) {
|
|
47
|
+
const from = options.from ?? '';
|
|
48
|
+
if (mode !== 'add') {
|
|
49
|
+
throw new InvalidArgumentError('--from only applies to --add.', '--from');
|
|
50
|
+
}
|
|
51
|
+
const clashing = SINGLE_DECLARATION_FLAGS.filter(([, key]) => options[key] !== undefined);
|
|
52
|
+
if (clashing.length > 0) {
|
|
53
|
+
throw new InvalidArgumentError(`--from supplies the declarations itself; drop ${clashing.map(([flag]) => flag).join(', ')}.`, '--from');
|
|
54
|
+
}
|
|
55
|
+
const { paths, manifest } = await requirePatchQueue(projectRoot);
|
|
56
|
+
const targetPatch = requirePatchTarget(identifier, manifest.patches);
|
|
57
|
+
const batch = await readStagedDependencyBatch(from, targetPatch.filename, options.reason);
|
|
58
|
+
for (const entry of [...batch.forwardImports, ...batch.registrations]) {
|
|
59
|
+
validateStagedDependencyAdd(entry, manifest.patches);
|
|
60
|
+
}
|
|
61
|
+
const supplied = batch.forwardImports.length + batch.registrations.length;
|
|
62
|
+
if (batch.skippedForOtherPatches > 0) {
|
|
63
|
+
info(`Skipped ${batch.skippedForOtherPatches} declaration(s) in ${from} that name another patch.`);
|
|
64
|
+
}
|
|
65
|
+
if (supplied === 0) {
|
|
66
|
+
info(`${from} holds no declarations for ${targetPatch.filename}; nothing to add.`);
|
|
67
|
+
outro('Patch staged-dependency complete');
|
|
68
|
+
return;
|
|
69
|
+
}
|
|
70
|
+
const existing = targetPatch.stagedDependencies;
|
|
71
|
+
const projectedCount = countStagedEntries(mergeBatch(existing, batch));
|
|
72
|
+
const describe = (beforeCount, afterCount) => `added ${afterCount - beforeCount} of ${supplied} staged declaration(s) from ${from}` +
|
|
73
|
+
(supplied > afterCount - beforeCount
|
|
74
|
+
? ` (${supplied - (afterCount - beforeCount)} already present or repeated)`
|
|
75
|
+
: '');
|
|
76
|
+
if (options.dryRun === true) {
|
|
77
|
+
info(`[dry-run] ${targetPatch.filename}: ${describe(countStagedEntries(existing), projectedCount)}.`);
|
|
78
|
+
outro('Dry run complete — no changes made');
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
81
|
+
const result = await mutatePatchMetadata(paths.patches, targetPatch.filename, (current) => {
|
|
82
|
+
const after = mergeBatch(current.stagedDependencies, batch);
|
|
83
|
+
if (countStagedEntries(after) === 0)
|
|
84
|
+
return { unset: ['stagedDependencies'] };
|
|
85
|
+
return { set: { stagedDependencies: after } };
|
|
86
|
+
}, {
|
|
87
|
+
waitLockSeconds: resolveWaitLockSeconds(options.waitLock),
|
|
88
|
+
command: 'patch staged-dependency',
|
|
89
|
+
});
|
|
90
|
+
if (!result) {
|
|
91
|
+
throw new GeneralError(`Patch ${targetPatch.filename} disappeared from the manifest during the update. Re-run after investigating.`);
|
|
92
|
+
}
|
|
93
|
+
const before = result.before.stagedDependencies;
|
|
94
|
+
const after = result.after.stagedDependencies;
|
|
95
|
+
info(`${targetPatch.filename}: ${describe(countStagedEntries(before), countStagedEntries(after))}.`);
|
|
96
|
+
await appendHistoryBestEffort(paths.patches, {
|
|
97
|
+
operation: 'patch-staged-dependency',
|
|
98
|
+
args: {
|
|
99
|
+
filename: targetPatch.filename,
|
|
100
|
+
mode,
|
|
101
|
+
from,
|
|
102
|
+
...stagedDependencyDelta(before, after),
|
|
103
|
+
},
|
|
104
|
+
...(options.yes === true ? { yes: true } : {}),
|
|
105
|
+
result: 'ok',
|
|
106
|
+
}, `patch staged-dependency committed (${targetPatch.filename})`);
|
|
107
|
+
outro('Patch staged-dependency complete');
|
|
108
|
+
}
|