@hominis/fireforge 0.49.1 → 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 +41 -0
- 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-templates.d.ts +8 -3
- package/dist/src/commands/furnace/create-templates.js +8 -23
- 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 +2 -2
|
@@ -12,19 +12,25 @@
|
|
|
12
12
|
* re-export's engine read + diff regeneration roundtrip is wasted.
|
|
13
13
|
*
|
|
14
14
|
* Modes are mutually exclusive: exactly one of `--add`, `--remove`, or
|
|
15
|
-
* `--clear` must be supplied per invocation.
|
|
15
|
+
* `--clear` must be supplied per invocation. `--add <rule> --max <n>
|
|
16
|
+
* --reason <text>` writes a size waiver with a ceiling (see
|
|
17
|
+
* `core/patch-lint-ignore.ts`) instead of a bare check ID; an `--add` for a
|
|
18
|
+
* rule that already has an entry replaces it, so the same command moves a
|
|
19
|
+
* ceiling or widens it back to a bare waiver. The read-modify-write
|
|
16
20
|
* happens inside the patch directory lock via {@link mutatePatchMetadata}
|
|
17
21
|
* so a concurrent writer cannot interleave between the read and the
|
|
18
22
|
* write, which matters when an operator scripts repeated invocations or
|
|
19
23
|
* runs `--add` and `--remove` back-to-back.
|
|
20
24
|
*/
|
|
21
|
-
import { Command } from 'commander';
|
|
25
|
+
import { Command, Option } from 'commander';
|
|
22
26
|
import { confirmDestructive } from '../../core/destructive.js';
|
|
23
27
|
import { appendHistoryBestEffort } from '../../core/history-log.js';
|
|
24
28
|
import { mutatePatchMetadata } from '../../core/patch-export.js';
|
|
29
|
+
import { addedLintIgnoreEntries, formatLintIgnoreEntry, formatLintIgnoreList, LINT_IGNORE_CEILING_RULES, lintIgnoreRule, removeLintIgnoreRules, upsertLintIgnoreEntries, } from '../../core/patch-lint-ignore.js';
|
|
25
30
|
import { GeneralError, InvalidArgumentError } from '../../errors/base.js';
|
|
26
31
|
import { info, intro, outro, warn } from '../../utils/logger.js';
|
|
27
|
-
import { addWaitLockOption, resolveWaitLockSeconds, stringListOption, } from '../../utils/options.js';
|
|
32
|
+
import { addWaitLockOption, commanderArgParser, resolveWaitLockSeconds, stringListOption, } from '../../utils/options.js';
|
|
33
|
+
import { parsePositiveIntegerFlag } from '../../utils/validation.js';
|
|
28
34
|
import { proceedAfterDecision } from '../destructive-decision.js';
|
|
29
35
|
import { requirePatchQueue, requirePatchTarget } from './patch-context.js';
|
|
30
36
|
/**
|
|
@@ -44,19 +50,10 @@ const LINT_IGNORE_REVIEW_WARNING = "This lint waiver is subject to the project's
|
|
|
44
50
|
* present" contract).
|
|
45
51
|
*/
|
|
46
52
|
function applyMode(existing, mode, values) {
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
const merged = [...existingSet];
|
|
52
|
-
return merged.length > 0 ? merged : undefined;
|
|
53
|
-
}
|
|
54
|
-
if (mode === 'remove') {
|
|
55
|
-
for (const v of values)
|
|
56
|
-
existingSet.delete(v);
|
|
57
|
-
const remaining = [...existingSet];
|
|
58
|
-
return remaining.length > 0 ? remaining : undefined;
|
|
59
|
-
}
|
|
53
|
+
if (mode === 'add')
|
|
54
|
+
return upsertLintIgnoreEntries(existing, values);
|
|
55
|
+
if (mode === 'remove')
|
|
56
|
+
return removeLintIgnoreRules(existing, values.map(lintIgnoreRule));
|
|
60
57
|
// mode === 'clear'
|
|
61
58
|
return undefined;
|
|
62
59
|
}
|
|
@@ -66,29 +63,62 @@ function applyMode(existing, mode, values) {
|
|
|
66
63
|
* message format directly without mocking the logger transport.
|
|
67
64
|
*/
|
|
68
65
|
function describeChange(before, after, mode, values) {
|
|
69
|
-
const beforeSet = new Set(before);
|
|
70
|
-
const afterSet = new Set(after);
|
|
71
66
|
if (mode === 'clear') {
|
|
72
67
|
return before.length === 0
|
|
73
68
|
? 'lintIgnore was already empty — no change'
|
|
74
|
-
: `lintIgnore cleared (was ${before
|
|
69
|
+
: `lintIgnore cleared (was ${formatLintIgnoreList(before)})`;
|
|
75
70
|
}
|
|
76
|
-
const currentLabel = before.length > 0 ? `[${before
|
|
71
|
+
const currentLabel = before.length > 0 ? `[${formatLintIgnoreList(before)}]` : '(empty)';
|
|
77
72
|
if (mode === 'add') {
|
|
78
|
-
const added =
|
|
73
|
+
const added = addedLintIgnoreEntries(before, after);
|
|
79
74
|
if (added.length === 0) {
|
|
80
75
|
// Surface the existing list so a no-op `--add` does not require a
|
|
81
76
|
// follow-up `patches.json` read to confirm what was already present.
|
|
82
77
|
return `lintIgnore unchanged (current: ${currentLabel}; all requested IDs already present)`;
|
|
83
78
|
}
|
|
84
|
-
return `lintIgnore += ${added
|
|
79
|
+
return `lintIgnore += ${formatLintIgnoreList(added)} → ${formatLintIgnoreList(after)}`;
|
|
85
80
|
}
|
|
86
81
|
// mode === 'remove'
|
|
87
|
-
const
|
|
82
|
+
const requested = new Set(values.map(lintIgnoreRule));
|
|
83
|
+
const removed = before.filter((entry) => requested.has(lintIgnoreRule(entry)));
|
|
88
84
|
if (removed.length === 0) {
|
|
89
85
|
return `lintIgnore unchanged (current: ${currentLabel}; none of the requested IDs were present)`;
|
|
90
86
|
}
|
|
91
|
-
return `lintIgnore −= ${removed
|
|
87
|
+
return `lintIgnore −= ${formatLintIgnoreList(removed)} → ${formatLintIgnoreList(after)}`;
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Turns the mode's IDs into manifest entries, applying `--max`/`--reason`
|
|
91
|
+
* to `--add`. Refuses a ceiling flag without `--add`, a `--max` without a
|
|
92
|
+
* `--reason` (and the reverse), and a ceiling on a rule that carries no
|
|
93
|
+
* measurement, so a waiver that could never suppress anything is not
|
|
94
|
+
* written.
|
|
95
|
+
*/
|
|
96
|
+
function resolveRequestedEntries(mode, options) {
|
|
97
|
+
const hasMax = options.max !== undefined;
|
|
98
|
+
const hasReason = options.reason !== undefined;
|
|
99
|
+
if ((hasMax || hasReason) && mode !== 'add') {
|
|
100
|
+
throw new InvalidArgumentError('--max and --reason only apply to --add.', hasMax ? '--max' : '--reason');
|
|
101
|
+
}
|
|
102
|
+
if (hasMax !== hasReason) {
|
|
103
|
+
throw new InvalidArgumentError('A lintIgnore ceiling needs both --max <n> and --reason <text>.', hasMax ? '--reason' : '--max');
|
|
104
|
+
}
|
|
105
|
+
if (mode === 'remove')
|
|
106
|
+
return [...(options.remove ?? [])];
|
|
107
|
+
if (mode !== 'add')
|
|
108
|
+
return [];
|
|
109
|
+
const ids = options.add ?? [];
|
|
110
|
+
if (options.max === undefined || options.reason === undefined)
|
|
111
|
+
return [...ids];
|
|
112
|
+
if (options.reason.trim() === '') {
|
|
113
|
+
throw new InvalidArgumentError('--reason must not be empty.', '--reason');
|
|
114
|
+
}
|
|
115
|
+
const unmeasured = ids.filter((id) => !LINT_IGNORE_CEILING_RULES.includes(id));
|
|
116
|
+
if (unmeasured.length > 0) {
|
|
117
|
+
throw new InvalidArgumentError(`--max only applies to the measured size rules (${LINT_IGNORE_CEILING_RULES.join(', ')}); ` +
|
|
118
|
+
`got ${unmeasured.join(', ')}.`, '--max');
|
|
119
|
+
}
|
|
120
|
+
const { max, reason } = options;
|
|
121
|
+
return ids.map((rule) => ({ rule, max, reason }));
|
|
92
122
|
}
|
|
93
123
|
/**
|
|
94
124
|
* Runs the `patch lint-ignore` command: reads the patch's existing
|
|
@@ -116,7 +146,7 @@ export async function patchLintIgnoreCommand(projectRoot, identifier, options =
|
|
|
116
146
|
throw new InvalidArgumentError('Specify --add <id>, --remove <id>, or --clear.', 'patch lint-ignore');
|
|
117
147
|
}
|
|
118
148
|
const mode = adding ? 'add' : removing ? 'remove' : 'clear';
|
|
119
|
-
const values =
|
|
149
|
+
const values = resolveRequestedEntries(mode, options);
|
|
120
150
|
const { paths, manifest } = await requirePatchQueue(projectRoot);
|
|
121
151
|
const target = requirePatchTarget(identifier, manifest.patches);
|
|
122
152
|
// Destructive-operation contract (docs/lifecycle-invariants.md): manifest
|
|
@@ -126,7 +156,7 @@ export async function patchLintIgnoreCommand(projectRoot, identifier, options =
|
|
|
126
156
|
// of mutation an operator should approve consciously.
|
|
127
157
|
const currentList = target.lintIgnore ?? [];
|
|
128
158
|
const projectedList = applyMode(currentList, mode, values) ?? [];
|
|
129
|
-
const addsNewIds = mode === 'add' && projectedList.length >
|
|
159
|
+
const addsNewIds = mode === 'add' && addedLintIgnoreEntries(currentList, projectedList).length > 0;
|
|
130
160
|
const projectedSummary = describeChange(currentList, projectedList, mode, values);
|
|
131
161
|
const decision = await confirmDestructive({
|
|
132
162
|
operation: 'patch-lint-ignore',
|
|
@@ -158,7 +188,7 @@ export async function patchLintIgnoreCommand(projectRoot, identifier, options =
|
|
|
158
188
|
const existing = result.before.lintIgnore ?? [];
|
|
159
189
|
const projected = result.after.lintIgnore ?? [];
|
|
160
190
|
info(`${target.filename}: ${describeChange(existing, projected, mode, values)}.`);
|
|
161
|
-
if (mode === 'add' && projected.length >
|
|
191
|
+
if (mode === 'add' && addedLintIgnoreEntries(existing, projected).length > 0) {
|
|
162
192
|
warn(LINT_IGNORE_REVIEW_WARNING);
|
|
163
193
|
}
|
|
164
194
|
await appendHistoryBestEffort(paths.patches, {
|
|
@@ -166,7 +196,7 @@ export async function patchLintIgnoreCommand(projectRoot, identifier, options =
|
|
|
166
196
|
args: {
|
|
167
197
|
filename: target.filename,
|
|
168
198
|
mode,
|
|
169
|
-
values:
|
|
199
|
+
values: values.map(formatLintIgnoreEntry),
|
|
170
200
|
before: existing,
|
|
171
201
|
after: projected,
|
|
172
202
|
},
|
|
@@ -189,6 +219,9 @@ export function registerPatchLintIgnore(parent, context) {
|
|
|
189
219
|
.option('--add <check-id>', 'Lint check ID to add to the patch lintIgnore list (repeatable)', ...stringListOption())
|
|
190
220
|
.option('--remove <check-id>', 'Lint check ID to remove from the patch lintIgnore list (repeatable)', ...stringListOption())
|
|
191
221
|
.option('--clear', 'Drop the lintIgnore field entirely')
|
|
222
|
+
.addOption(new Option('--max <n>', 'With --add: write a size waiver with a ceiling, suppressing the rule only while its ' +
|
|
223
|
+
'measurement is at most <n> (file-too-large, large-patch-lines, large-patch-files)').argParser(commanderArgParser((v) => parsePositiveIntegerFlag('--max', v))))
|
|
224
|
+
.option('--reason <text>', 'With --add --max: why the ceiling exists (required with --max)')
|
|
192
225
|
.option('--dry-run', 'Show what would change without writing')
|
|
193
226
|
.option('-y, --yes', 'Skip confirmation prompt (required for non-TTY)');
|
|
194
227
|
addWaitLockOption(command).action(withErrorHandling(async (name, options) => {
|
|
@@ -203,6 +236,10 @@ export function registerPatchLintIgnore(parent, context) {
|
|
|
203
236
|
normalized.remove = options.remove;
|
|
204
237
|
if (options.clear === true)
|
|
205
238
|
normalized.clear = true;
|
|
239
|
+
if (options.max !== undefined)
|
|
240
|
+
normalized.max = options.max;
|
|
241
|
+
if (options.reason !== undefined)
|
|
242
|
+
normalized.reason = options.reason;
|
|
206
243
|
if (options.dryRun === true)
|
|
207
244
|
normalized.dryRun = true;
|
|
208
245
|
if (options.yes === true)
|
|
@@ -14,6 +14,7 @@ import { getProjectPaths, loadConfig } from '../../core/config.js';
|
|
|
14
14
|
import { assertConfirmationAvailable, confirmDestructive } from '../../core/destructive.js';
|
|
15
15
|
import { formatPatchNotFoundError } from '../../core/patch-identifier-suggest.js';
|
|
16
16
|
import { buildPatchQueueContext } from '../../core/patch-lint.js';
|
|
17
|
+
import { buildLintIgnoreChecks } from '../../core/patch-lint-ignore.js';
|
|
17
18
|
import { loadPatchesManifest, resolvePatchIdentifier } from '../../core/patch-manifest.js';
|
|
18
19
|
import { formatPatchOrder } from '../../core/patch-parse.js';
|
|
19
20
|
import { enforcePatchPolicy } from '../../core/patch-policy.js';
|
|
@@ -23,8 +24,9 @@ import { normalizePatchDisplayName } from '../../utils/validation.js';
|
|
|
23
24
|
import { proceedAfterDecision } from '../destructive-decision.js';
|
|
24
25
|
import { resolvePlacementPlan } from '../export-flow.js';
|
|
25
26
|
import { runPatchLint } from '../export-shared.js';
|
|
26
|
-
import { commitPatchSplit } from './split.js';
|
|
27
|
-
import { assertSourceOwnsFiles, buildNewPatchMetadata, buildSplitDiff, buildSplitSummary,
|
|
27
|
+
import { commitPatchSplit, reportSplitStagedChanges } from './split.js';
|
|
28
|
+
import { assertSourceOwnsFiles, buildNewPatchMetadata, buildSplitDiff, buildSplitSummary, projectSplitManifest, runProjectedSplitLint, } from './split-plan.js';
|
|
29
|
+
import { emptyStagedMaintenance, findOwnerRewriteHolders } from './split-staged.js';
|
|
28
30
|
/**
|
|
29
31
|
* Runs `patch move-files --create --order <n>`: plans the create+move as a
|
|
30
32
|
* split, lints the projection, confirms, and commits transactionally.
|
|
@@ -85,9 +87,26 @@ export async function patchMoveFilesCreateCommand(projectRoot, fromIdentifier, n
|
|
|
85
87
|
name: displayName,
|
|
86
88
|
description: options.description ?? '',
|
|
87
89
|
ownerRewrites: findOwnerRewriteHolders(manifest.patches, source.filename, movedSet),
|
|
88
|
-
// Populated by runProjectedSplitLint below (forward edges into the new
|
|
90
|
+
// Populated by runProjectedSplitLint below (forward edges into the new
|
|
91
|
+
// patch, and the declarations the move re-homes or makes unused).
|
|
89
92
|
stagedDependencyAdditions: new Map(),
|
|
93
|
+
stagedMaintenance: emptyStagedMaintenance(),
|
|
90
94
|
};
|
|
95
|
+
// Policy first: the projected manifest (orders, ranges, the gap rule)
|
|
96
|
+
// does not depend on the lint below, so a refused --order fails before
|
|
97
|
+
// the per-patch and projected lint passes instead of after them.
|
|
98
|
+
enforcePatchPolicy({
|
|
99
|
+
config,
|
|
100
|
+
manifest: projectSplitManifest(manifest, plan, buildNewPatchMetadata(plan, config)),
|
|
101
|
+
command: 'patch move-files --create',
|
|
102
|
+
forceUnsafe: options.forceUnsafe === true,
|
|
103
|
+
hints: {
|
|
104
|
+
'description-required': 'Pass --description "<text>" (or -d) on this command to set the created patch\'s description.',
|
|
105
|
+
'numeric-gap': 'With patchPolicy.allowGaps: false, orders are contiguous within a range: --order must ' +
|
|
106
|
+
"fill an existing gap or a retired slot, or take the number right after the range's last " +
|
|
107
|
+
'patch. A number reserved past a gap cannot be used until the numbers before it exist.',
|
|
108
|
+
},
|
|
109
|
+
});
|
|
91
110
|
// Per-patch lint both projected bodies, threading the source patch's
|
|
92
111
|
// tier/lintIgnore so an intentional-advisory patch can still move files.
|
|
93
112
|
// The whole-queue context (built once, with the config so it carries the
|
|
@@ -96,7 +115,7 @@ export async function patchMoveFilesCreateCommand(projectRoot, fromIdentifier, n
|
|
|
96
115
|
// resolve exactly as they will after the move lands. Without it the
|
|
97
116
|
// projection lint is blind.
|
|
98
117
|
const patchQueueCtx = await buildPatchQueueContext(paths.patches, config);
|
|
99
|
-
const ignoreChecks =
|
|
118
|
+
const ignoreChecks = buildLintIgnoreChecks(source.lintIgnore);
|
|
100
119
|
await runPatchLint({
|
|
101
120
|
engineDir: paths.engine,
|
|
102
121
|
filesAffected: remainingFiles,
|
|
@@ -117,18 +136,10 @@ export async function patchMoveFilesCreateCommand(projectRoot, fromIdentifier, n
|
|
|
117
136
|
ignoreChecks,
|
|
118
137
|
patchTier: source.tier,
|
|
119
138
|
});
|
|
120
|
-
const { conflicts, stagedDependencyAdditions } = runProjectedSplitLint(plan, patchQueueCtx);
|
|
139
|
+
const { conflicts, stagedDependencyAdditions, stagedMaintenance } = runProjectedSplitLint(plan, patchQueueCtx);
|
|
121
140
|
plan.stagedDependencyAdditions = stagedDependencyAdditions;
|
|
141
|
+
plan.stagedMaintenance = stagedMaintenance;
|
|
122
142
|
const newMetadata = buildNewPatchMetadata(plan, config);
|
|
123
|
-
enforcePatchPolicy({
|
|
124
|
-
config,
|
|
125
|
-
manifest: projectSplitManifest(manifest, plan, newMetadata),
|
|
126
|
-
command: 'patch move-files --create',
|
|
127
|
-
forceUnsafe: options.forceUnsafe === true,
|
|
128
|
-
hints: {
|
|
129
|
-
'description-required': 'Pass --description "<text>" (or -d) on this command to set the created patch\'s description.',
|
|
130
|
-
},
|
|
131
|
-
});
|
|
132
143
|
const decision = await confirmDestructive({
|
|
133
144
|
operation: 'patch-move-files-create',
|
|
134
145
|
title: `Create ${placement.newFilename} (order ${placement.insertionOrder}) and move ${movedFiles.length} file(s) out of ${source.filename}`,
|
|
@@ -143,8 +154,6 @@ export async function patchMoveFilesCreateCommand(projectRoot, fromIdentifier, n
|
|
|
143
154
|
await commitPatchSplit(paths.patches, plan, newMetadata, options, config);
|
|
144
155
|
success(`Created ${placement.newFilename} (order ${formatPatchOrder(placement.insertionOrder)}) ` +
|
|
145
156
|
`and moved ${plan.movedFiles.length} file(s) out of ${source.filename}`);
|
|
146
|
-
|
|
147
|
-
info(`Re-pointed staged-dependency owners in: ${plan.ownerRewrites.join(', ')}`);
|
|
148
|
-
}
|
|
157
|
+
reportSplitStagedChanges(plan);
|
|
149
158
|
outro('Move complete');
|
|
150
159
|
}
|
|
@@ -18,6 +18,7 @@ import { join } from 'node:path';
|
|
|
18
18
|
import { assertConfirmationAvailable, confirmDestructive, } from '../../core/destructive.js';
|
|
19
19
|
import { appendHistoryBestEffort } from '../../core/history-log.js';
|
|
20
20
|
import { buildPatchQueueContext, collectForwardImportEdges, formatPatchLintIssue, lintPatchQueue, } from '../../core/patch-lint.js';
|
|
21
|
+
import { buildLintIgnoreChecks } from '../../core/patch-lint-ignore.js';
|
|
21
22
|
import { computeProjectedLintRegressions } from '../../core/patch-lint-projection.js';
|
|
22
23
|
import { withPatchDirectoryLock } from '../../core/patch-lock.js';
|
|
23
24
|
import { loadPatchesManifest, savePatchesManifest, validatePatchesManifest, } from '../../core/patch-manifest.js';
|
|
@@ -29,7 +30,8 @@ import { resolveWaitLockSeconds } from '../../utils/options.js';
|
|
|
29
30
|
import { proceedAfterDecision } from '../destructive-decision.js';
|
|
30
31
|
import { runPatchLint } from '../export-shared.js';
|
|
31
32
|
import { projectEntryBody } from './entry-projection.js';
|
|
32
|
-
import { buildSplitDiff,
|
|
33
|
+
import { buildSplitDiff, mergeStagedForwardImports } from './split-plan.js';
|
|
34
|
+
import { findOwnerRewriteHolders, rewriteSplitOwners } from './split-staged.js';
|
|
33
35
|
/**
|
|
34
36
|
* Projects the post-move queue through cross-patch lint, auto-declaring the
|
|
35
37
|
* forward edges the move introduces into the target (an importer that used
|
|
@@ -198,7 +200,7 @@ export async function runMoveFilesInto(args) {
|
|
|
198
200
|
config: args.config,
|
|
199
201
|
skipLint: options.skipLint,
|
|
200
202
|
patchQueueCtx,
|
|
201
|
-
ignoreChecks:
|
|
203
|
+
ignoreChecks: buildLintIgnoreChecks(source.lintIgnore),
|
|
202
204
|
patchTier: source.tier,
|
|
203
205
|
});
|
|
204
206
|
await runPatchLint({
|
|
@@ -208,7 +210,7 @@ export async function runMoveFilesInto(args) {
|
|
|
208
210
|
config: args.config,
|
|
209
211
|
skipLint: options.skipLint,
|
|
210
212
|
patchQueueCtx,
|
|
211
|
-
ignoreChecks:
|
|
213
|
+
ignoreChecks: buildLintIgnoreChecks(target.lintIgnore),
|
|
212
214
|
patchTier: target.tier,
|
|
213
215
|
});
|
|
214
216
|
const manifest = await loadPatchesManifest(patchesDir);
|
|
@@ -164,7 +164,9 @@ export function registerPatchMoveFiles(parent, context) {
|
|
|
164
164
|
'or create the target patch and move the files in one transaction with --create --order <n>.')
|
|
165
165
|
.option('--file <path>', 'File path relative to engine/ to move (repeatable)', ...stringListOption())
|
|
166
166
|
.option('--create', 'Create <to> as a new patch and move the files into it (requires --order)')
|
|
167
|
-
.option('--order <n>', 'Exact sparse order for the created patch (only valid with --create)
|
|
167
|
+
.option('--order <n>', 'Exact sparse order for the created patch (only valid with --create). With ' +
|
|
168
|
+
'patchPolicy.allowGaps: false, orders are contiguous within a range: pick a gap, a ' +
|
|
169
|
+
"retired slot, or the number after the range's last patch; checked before any lint runs", commanderArgParser((raw) => parsePositiveIntegerFlag('--order', raw)))
|
|
168
170
|
.option('--category <category>', "Category for the created patch (default: the source patch's)")
|
|
169
171
|
.option('-d, --description <desc>', 'Description for the created patch')
|
|
170
172
|
.option('--dry-run', 'Show what would happen without writing')
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The dependency scan `patch delete` and `patch retire` share: which later
|
|
3
|
+
* patches import a file the removed patch creates, and which staged
|
|
4
|
+
* declarations name it as their owner. Both commands take the patch out of
|
|
5
|
+
* the queue, so both must refuse and warn on exactly the same evidence.
|
|
6
|
+
*/
|
|
7
|
+
import type { ConflictReport } from '../../core/destructive.js';
|
|
8
|
+
import { type PatchQueueContext } from '../../core/patch-lint.js';
|
|
9
|
+
import type { PatchMetadata } from '../../types/commands/index.js';
|
|
10
|
+
/**
|
|
11
|
+
* Lists every later patch site that imports a file `target` creates.
|
|
12
|
+
*
|
|
13
|
+
* Both source-site maps are covered: `newFiles` (files the later patch
|
|
14
|
+
* creates) and `modifiedFileAdditions` (added lines against files that
|
|
15
|
+
* already exist). Scanning only newFiles misses a later patch adding
|
|
16
|
+
* `import "./TargetHelper.sys.mjs"` to an existing file. Uses the shared
|
|
17
|
+
* specifier extractor so dynamic `import()` and
|
|
18
|
+
* `ChromeUtils.defineESModuleGetters` are picked up: the forward-import lint
|
|
19
|
+
* rule already covers those forms, and removal safety must match the same
|
|
20
|
+
* set or it silently drops dependencies.
|
|
21
|
+
*
|
|
22
|
+
* @param baseCtx - Queue context built before the removal
|
|
23
|
+
* @param target - The patch about to leave the queue
|
|
24
|
+
* @param command - Names for the "queue changed" refusal, e.g.
|
|
25
|
+
* `{ noun: 'deletion', verb: 'delete' }`
|
|
26
|
+
* @returns One description per dependent patch
|
|
27
|
+
*/
|
|
28
|
+
export declare function collectRemovalDependents(baseCtx: PatchQueueContext, target: PatchMetadata, command: {
|
|
29
|
+
noun: string;
|
|
30
|
+
verb: string;
|
|
31
|
+
}): string[];
|
|
32
|
+
/**
|
|
33
|
+
* Warns once per patch whose staged forward-import names `target` as its
|
|
34
|
+
* owner. The dangling reference also surfaces via cross-patch lint later,
|
|
35
|
+
* but warning here puts the exact cleanup command in front of the operator
|
|
36
|
+
* at decision time.
|
|
37
|
+
*
|
|
38
|
+
* @param baseCtx - Queue context built before the removal
|
|
39
|
+
* @param target - The patch about to leave the queue
|
|
40
|
+
* @param verb - Command name used in the warning ("delete", "retire")
|
|
41
|
+
*/
|
|
42
|
+
export declare function warnDanglingStagedOwners(baseCtx: PatchQueueContext, target: PatchMetadata, verb: string): void;
|
|
43
|
+
/**
|
|
44
|
+
* The structural conflict a removal with dependents raises.
|
|
45
|
+
*
|
|
46
|
+
* The wording spells out the runtime impact: `git apply` does not resolve
|
|
47
|
+
* imports and succeeds even when a later patch imports a file the target
|
|
48
|
+
* created, so the queue re-imports cleanly. The breakage surfaces at
|
|
49
|
+
* browser startup when `ChromeUtils.importESModule` cannot locate the
|
|
50
|
+
* deleted module. Operators who plan to re-introduce the imported files
|
|
51
|
+
* (rename, refactor) need to know this is the impact model rather than a
|
|
52
|
+
* patch-application failure.
|
|
53
|
+
*
|
|
54
|
+
* @param dependents - Result of {@link collectRemovalDependents}
|
|
55
|
+
* @param target - The patch about to leave the queue
|
|
56
|
+
*/
|
|
57
|
+
export declare function removalConflict(dependents: readonly string[], target: PatchMetadata): ConflictReport | null;
|
|
58
|
+
/**
|
|
59
|
+
* Snapshot of the queue inputs a removal's dependency scan read, compared
|
|
60
|
+
* again under the lock so a change between the prompt and the commit
|
|
61
|
+
* refuses instead of committing an unreviewed removal.
|
|
62
|
+
*/
|
|
63
|
+
export declare function queueSnapshot(context: PatchQueueContext): string;
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
// SPDX-License-Identifier: EUPL-1.2
|
|
2
|
+
/**
|
|
3
|
+
* The dependency scan `patch delete` and `patch retire` share: which later
|
|
4
|
+
* patches import a file the removed patch creates, and which staged
|
|
5
|
+
* declarations name it as their owner. Both commands take the patch out of
|
|
6
|
+
* the queue, so both must refuse and warn on exactly the same evidence.
|
|
7
|
+
*/
|
|
8
|
+
import { basename } from 'node:path';
|
|
9
|
+
import { extractImportSpecifiersWithLines, findForwardImportIgnoreLines, isForwardImportableFile, } from '../../core/patch-lint.js';
|
|
10
|
+
import { GeneralError } from '../../errors/base.js';
|
|
11
|
+
import { warn } from '../../utils/logger.js';
|
|
12
|
+
/**
|
|
13
|
+
* Lists every later patch site that imports a file `target` creates.
|
|
14
|
+
*
|
|
15
|
+
* Both source-site maps are covered: `newFiles` (files the later patch
|
|
16
|
+
* creates) and `modifiedFileAdditions` (added lines against files that
|
|
17
|
+
* already exist). Scanning only newFiles misses a later patch adding
|
|
18
|
+
* `import "./TargetHelper.sys.mjs"` to an existing file. Uses the shared
|
|
19
|
+
* specifier extractor so dynamic `import()` and
|
|
20
|
+
* `ChromeUtils.defineESModuleGetters` are picked up: the forward-import lint
|
|
21
|
+
* rule already covers those forms, and removal safety must match the same
|
|
22
|
+
* set or it silently drops dependencies.
|
|
23
|
+
*
|
|
24
|
+
* @param baseCtx - Queue context built before the removal
|
|
25
|
+
* @param target - The patch about to leave the queue
|
|
26
|
+
* @param command - Names for the "queue changed" refusal, e.g.
|
|
27
|
+
* `{ noun: 'deletion', verb: 'delete' }`
|
|
28
|
+
* @returns One description per dependent patch
|
|
29
|
+
*/
|
|
30
|
+
export function collectRemovalDependents(baseCtx, target, command) {
|
|
31
|
+
const targetEntry = baseCtx.entries.find((e) => e.filename === target.filename);
|
|
32
|
+
if (!targetEntry || JSON.stringify(targetEntry.metadata) !== JSON.stringify(target)) {
|
|
33
|
+
throw new GeneralError(`Patch queue changed while preparing ${command.noun}. Re-run patch ${command.verb} to review the current target.`);
|
|
34
|
+
}
|
|
35
|
+
const targetNewFileLeaves = new Set();
|
|
36
|
+
for (const fullPath of targetEntry.newFiles.keys()) {
|
|
37
|
+
targetNewFileLeaves.add(basename(fullPath));
|
|
38
|
+
}
|
|
39
|
+
const dependents = [];
|
|
40
|
+
const scanSite = (entryFilename, sitePath, content) => {
|
|
41
|
+
if (!isForwardImportableFile(sitePath))
|
|
42
|
+
return false;
|
|
43
|
+
const ignoredLines = findForwardImportIgnoreLines(content);
|
|
44
|
+
const specifiers = extractImportSpecifiersWithLines(content);
|
|
45
|
+
for (const { specifier, line } of specifiers) {
|
|
46
|
+
if (ignoredLines.has(line))
|
|
47
|
+
continue;
|
|
48
|
+
const cleaned = specifier.split(/[?#]/)[0] ?? specifier;
|
|
49
|
+
const leaf = basename(cleaned);
|
|
50
|
+
if (!leaf || !isForwardImportableFile(leaf))
|
|
51
|
+
continue;
|
|
52
|
+
if (targetNewFileLeaves.has(leaf)) {
|
|
53
|
+
dependents.push(`${entryFilename} (${sitePath}) imports "${specifier}" which would be deleted`);
|
|
54
|
+
return true;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
return false;
|
|
58
|
+
};
|
|
59
|
+
for (const entry of baseCtx.entries) {
|
|
60
|
+
if (entry.filename === target.filename)
|
|
61
|
+
continue;
|
|
62
|
+
if (entry.order < target.order)
|
|
63
|
+
continue;
|
|
64
|
+
let matched = false;
|
|
65
|
+
for (const [newFile, content] of entry.newFiles) {
|
|
66
|
+
if (scanSite(entry.filename, newFile, content)) {
|
|
67
|
+
matched = true;
|
|
68
|
+
break;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
if (matched)
|
|
72
|
+
continue;
|
|
73
|
+
for (const [modifiedPath, addedContent] of entry.modifiedFileAdditions) {
|
|
74
|
+
if (scanSite(entry.filename, modifiedPath, addedContent))
|
|
75
|
+
break;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
return dependents;
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Warns once per patch whose staged forward-import names `target` as its
|
|
82
|
+
* owner. The dangling reference also surfaces via cross-patch lint later,
|
|
83
|
+
* but warning here puts the exact cleanup command in front of the operator
|
|
84
|
+
* at decision time.
|
|
85
|
+
*
|
|
86
|
+
* @param baseCtx - Queue context built before the removal
|
|
87
|
+
* @param target - The patch about to leave the queue
|
|
88
|
+
* @param verb - Command name used in the warning ("delete", "retire")
|
|
89
|
+
*/
|
|
90
|
+
export function warnDanglingStagedOwners(baseCtx, target, verb) {
|
|
91
|
+
const holders = baseCtx.entries.filter((entry) => entry.filename !== target.filename &&
|
|
92
|
+
(entry.metadata?.stagedDependencies?.forwardImports ?? []).some((fi) => fi.owner === target.filename));
|
|
93
|
+
for (const holder of holders) {
|
|
94
|
+
warn(`${holder.filename} declares a staged dependency with owner ${target.filename}; ` +
|
|
95
|
+
`after the ${verb}, update it via "fireforge patch staged-dependency ${holder.filename} --remove ..." ` +
|
|
96
|
+
'or re-point the owner at the patch that will create the file.');
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* The structural conflict a removal with dependents raises.
|
|
101
|
+
*
|
|
102
|
+
* The wording spells out the runtime impact: `git apply` does not resolve
|
|
103
|
+
* imports and succeeds even when a later patch imports a file the target
|
|
104
|
+
* created, so the queue re-imports cleanly. The breakage surfaces at
|
|
105
|
+
* browser startup when `ChromeUtils.importESModule` cannot locate the
|
|
106
|
+
* deleted module. Operators who plan to re-introduce the imported files
|
|
107
|
+
* (rename, refactor) need to know this is the impact model rather than a
|
|
108
|
+
* patch-application failure.
|
|
109
|
+
*
|
|
110
|
+
* @param dependents - Result of {@link collectRemovalDependents}
|
|
111
|
+
* @param target - The patch about to leave the queue
|
|
112
|
+
*/
|
|
113
|
+
export function removalConflict(dependents, target) {
|
|
114
|
+
if (dependents.length === 0)
|
|
115
|
+
return null;
|
|
116
|
+
return {
|
|
117
|
+
reason: `${dependents.length} later patch(es) contain import statements that reference files created by ${target.filename}. Patch application itself will still succeed, but runtime imports will fail at browser startup until those files are re-introduced.`,
|
|
118
|
+
details: [...dependents],
|
|
119
|
+
};
|
|
120
|
+
}
|
|
121
|
+
/**
|
|
122
|
+
* Snapshot of the queue inputs a removal's dependency scan read, compared
|
|
123
|
+
* again under the lock so a change between the prompt and the commit
|
|
124
|
+
* refuses instead of committing an unreviewed removal.
|
|
125
|
+
*/
|
|
126
|
+
export function queueSnapshot(context) {
|
|
127
|
+
return JSON.stringify(context.entries.map(({ filename, order, diff, metadata }) => ({
|
|
128
|
+
filename,
|
|
129
|
+
order,
|
|
130
|
+
diff,
|
|
131
|
+
metadata,
|
|
132
|
+
})));
|
|
133
|
+
}
|
|
@@ -26,6 +26,7 @@ import { buildPatchQueueContext, formatPatchLintIssue, lintPatchQueue, } from '.
|
|
|
26
26
|
import { withPatchDirectoryLock } from '../../core/patch-lock.js';
|
|
27
27
|
import { loadPatchesManifest, rewriteStagedDependencyOwners, savePatchesManifest, } from '../../core/patch-manifest.js';
|
|
28
28
|
import { buildProjectedManifest, enforcePatchPolicy, getPatchPolicyCategories, isCategoryAllowedByConfig, } from '../../core/patch-policy.js';
|
|
29
|
+
import { carryRetiredSlots } from '../../core/patch-retired.js';
|
|
29
30
|
import { GeneralError, InvalidArgumentError } from '../../errors/base.js';
|
|
30
31
|
import { toError } from '../../utils/errors.js';
|
|
31
32
|
import { pathExists } from '../../utils/fs.js';
|
|
@@ -115,6 +116,12 @@ async function commitRenameUnderLock(input) {
|
|
|
115
116
|
...(descriptionChanging ? { description: newDescription ?? '' } : {}),
|
|
116
117
|
};
|
|
117
118
|
}
|
|
119
|
+
// A rename onto a retired order fills that slot.
|
|
120
|
+
const retired = carryRetiredSlots(fresh, fresh.patches);
|
|
121
|
+
if (retired === undefined)
|
|
122
|
+
delete fresh.retired;
|
|
123
|
+
else
|
|
124
|
+
fresh.retired = retired;
|
|
118
125
|
enforcePatchPolicy({
|
|
119
126
|
config: input.config,
|
|
120
127
|
manifest: buildProjectedManifest(fresh, fresh.patches),
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `fireforge patch retire <name>`: removes a patch from the queue and keeps
|
|
3
|
+
* its order number spent as a retired slot in `patches.json#retired`.
|
|
4
|
+
*
|
|
5
|
+
* The case it exists for: a rename leaves a patch owning only files that no
|
|
6
|
+
* longer exist. `patch delete` opens a numeric gap that
|
|
7
|
+
* `patchPolicy.allowGaps: false` refuses, and `patch compact` renumbers
|
|
8
|
+
* every later patch, including ones a peer session has in flight. Retiring
|
|
9
|
+
* removes the patch file and row like a delete, and records the order as a
|
|
10
|
+
* placeholder the gap check counts as occupied. Nothing else reads the
|
|
11
|
+
* placeholder (see `core/patch-retired.ts`), and a later
|
|
12
|
+
* `export --order <n>` that takes the number fills it.
|
|
13
|
+
*
|
|
14
|
+
* Shares the delete's dependency refusal, dry-run, confirmation and history
|
|
15
|
+
* contract, so the two differ only in what happens to the order number.
|
|
16
|
+
*/
|
|
17
|
+
import { Command } from 'commander';
|
|
18
|
+
import type { CommandContext } from '../../types/cli.js';
|
|
19
|
+
import type { PatchRetireOptions } from '../../types/commands/index.js';
|
|
20
|
+
/**
|
|
21
|
+
* Runs the `patch retire` command.
|
|
22
|
+
*
|
|
23
|
+
* @param projectRoot - Project root directory
|
|
24
|
+
* @param identifier - Patch filename, ordinal, or manifest name to retire
|
|
25
|
+
* @param options - Command options
|
|
26
|
+
*/
|
|
27
|
+
export declare function patchRetireCommand(projectRoot: string, identifier: string, options?: PatchRetireOptions): Promise<void>;
|
|
28
|
+
/**
|
|
29
|
+
* Registers the `patch retire` subcommand on the `patch` parent.
|
|
30
|
+
*
|
|
31
|
+
* @param parent - Parent Commander command
|
|
32
|
+
* @param context - Shared CLI registration context
|
|
33
|
+
*/
|
|
34
|
+
export declare function registerPatchRetire(parent: Command, context: CommandContext): void;
|