@hominis/fireforge 0.49.2 → 0.51.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 +87 -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 +41 -0
- package/dist/src/commands/check-report.js +91 -0
- package/dist/src/commands/check-run.d.ts +33 -0
- package/dist/src/commands/check-run.js +112 -0
- package/dist/src/commands/check-steps.d.ts +75 -0
- package/dist/src/commands/check-steps.js +200 -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/declarations-report.d.ts +43 -0
- package/dist/src/commands/declarations-report.js +76 -0
- 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-placement-gate.js +8 -0
- 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 +98 -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 +151 -113
- package/dist/src/commands/furnace/validation-output.js +14 -3
- package/dist/src/commands/lint-per-patch.js +36 -61
- package/dist/src/commands/lint-per-run-checkjs.js +2 -2
- package/dist/src/commands/lint-report.d.ts +8 -4
- package/dist/src/commands/lint-report.js +20 -5
- package/dist/src/commands/lint.js +15 -34
- package/dist/src/commands/manifest.js +4 -0
- package/dist/src/commands/park.d.ts +22 -0
- package/dist/src/commands/park.js +232 -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 +24 -104
- package/dist/src/commands/patch/index.d.ts +3 -3
- package/dist/src/commands/patch/index.js +10 -4
- package/dist/src/commands/patch/lint-ignore-from.d.ts +44 -0
- package/dist/src/commands/patch/lint-ignore-from.js +142 -0
- package/dist/src/commands/patch/lint-ignore.d.ts +5 -1
- package/dist/src/commands/patch/lint-ignore.js +78 -31
- package/dist/src/commands/patch/move-files-create.js +34 -18
- package/dist/src/commands/patch/move-files-into.js +15 -7
- package/dist/src/commands/patch/move-files.js +6 -2
- package/dist/src/commands/patch/removal-created-files.d.ts +48 -0
- package/dist/src/commands/patch/removal-created-files.js +83 -0
- 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 +20 -1
- package/dist/src/commands/patch/reserve.d.ts +33 -0
- package/dist/src/commands/patch/reserve.js +152 -0
- package/dist/src/commands/patch/retire.d.ts +34 -0
- package/dist/src/commands/patch/retire.js +133 -0
- package/dist/src/commands/patch/split-plan.d.ts +17 -8
- package/dist/src/commands/patch/split-plan.js +60 -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 +62 -17
- package/dist/src/commands/patch/stage.d.ts +34 -0
- package/dist/src/commands/patch/stage.js +153 -0
- 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-remove.d.ts +31 -0
- package/dist/src/commands/patch/staged-dependency-remove.js +80 -0
- package/dist/src/commands/patch/staged-dependency.js +54 -139
- 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 +50 -2
- package/dist/src/commands/re-export-adjacent.js +160 -40
- package/dist/src/commands/re-export-bulk-scan.d.ts +2 -1
- package/dist/src/commands/re-export-bulk-scan.js +22 -7
- 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-hunks.d.ts +35 -0
- package/dist/src/commands/re-export-hunks.js +220 -0
- 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 +74 -6
- 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-purity-scope.d.ts +18 -0
- package/dist/src/commands/re-export-purity-scope.js +131 -0
- package/dist/src/commands/re-export-register.js +56 -17
- package/dist/src/commands/re-export-removed.d.ts +13 -2
- package/dist/src/commands/re-export-removed.js +57 -12
- 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 +19 -9
- package/dist/src/commands/re-export-scan.js +60 -24
- package/dist/src/commands/re-export-single.js +122 -46
- package/dist/src/commands/re-export-stale-registrations.d.ts +18 -0
- package/dist/src/commands/re-export-stale-registrations.js +62 -0
- 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 +71 -57
- 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 +22 -0
- package/dist/src/commands/test-build-furnace.js +54 -0
- package/dist/src/commands/test-build-phase.js +64 -41
- package/dist/src/commands/test-checkjs.js +10 -2
- package/dist/src/commands/test-diagnose.js +7 -0
- package/dist/src/commands/test-dispatch.d.ts +39 -0
- package/dist/src/commands/test-dispatch.js +211 -0
- package/dist/src/commands/test-furnace-reach.d.ts +37 -0
- package/dist/src/commands/test-furnace-reach.js +132 -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 +52 -0
- package/dist/src/commands/test-orchestrate.js +240 -0
- package/dist/src/commands/test-perf-scope.d.ts +50 -0
- package/dist/src/commands/test-perf-scope.js +138 -0
- package/dist/src/commands/test-prelock.d.ts +51 -0
- package/dist/src/commands/test-prelock.js +273 -0
- package/dist/src/commands/test-register.js +69 -10
- package/dist/src/commands/test-results.d.ts +62 -0
- package/dist/src/commands/test-results.js +62 -0
- package/dist/src/commands/test-run.d.ts +13 -55
- package/dist/src/commands/test-run.js +74 -99
- package/dist/src/commands/test-shards.d.ts +110 -0
- package/dist/src/commands/test-shards.js +255 -0
- package/dist/src/commands/test-stale-gate.d.ts +5 -1
- package/dist/src/commands/test-stale-gate.js +60 -28
- package/dist/src/commands/test-verdict.d.ts +11 -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 +120 -124
- package/dist/src/commands/token-lint.d.ts +14 -0
- package/dist/src/commands/token-lint.js +151 -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 +67 -36
- 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 +13 -0
- package/dist/src/core/build-prepare.js +42 -10
- 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 +10 -0
- package/dist/src/core/config-validate-check.d.ts +8 -0
- package/dist/src/core/config-validate-check.js +219 -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 +21 -0
- package/dist/src/core/config-validate-token-scales.d.ts +9 -0
- package/dist/src/core/config-validate-token-scales.js +92 -0
- package/dist/src/core/config-validate.js +20 -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 +108 -0
- package/dist/src/core/coverage-records.js +384 -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 +16 -1
- package/dist/src/core/engine-session-lock.js +38 -10
- 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-shared.d.ts +57 -0
- package/dist/src/core/file-lock-shared.js +117 -0
- package/dist/src/core/file-lock.d.ts +13 -25
- package/dist/src/core/file-lock.js +163 -140
- 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 +22 -12
- 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 +16 -0
- package/dist/src/core/furnace-config-validate-exceptions.js +102 -0
- package/dist/src/core/furnace-config-validate.js +14 -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-jar-inplace.d.ts +12 -0
- package/dist/src/core/furnace-registration-jar-inplace.js +80 -0
- 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 +8 -4
- package/dist/src/core/furnace-validate-accessibility.js +71 -28
- package/dist/src/core/furnace-validate-exceptions.d.ts +48 -0
- package/dist/src/core/furnace-validate-exceptions.js +80 -0
- package/dist/src/core/furnace-validate-icon-button.d.ts +7 -0
- package/dist/src/core/furnace-validate-icon-button.js +78 -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 +164 -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/line-prefixer.d.ts +18 -0
- package/dist/src/core/line-prefixer.js +34 -0
- package/dist/src/core/mach.d.ts +7 -0
- package/dist/src/core/mach.js +32 -10
- package/dist/src/core/module-resolution-preflight.d.ts +19 -3
- package/dist/src/core/module-resolution-preflight.js +24 -6
- package/dist/src/core/negative-control-compiled.d.ts +27 -0
- package/dist/src/core/negative-control-compiled.js +73 -0
- package/dist/src/core/negative-control-diff.d.ts +51 -0
- package/dist/src/core/negative-control-diff.js +179 -0
- package/dist/src/core/negative-control.d.ts +150 -0
- package/dist/src/core/negative-control.js +399 -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/park-store.d.ts +63 -0
- package/dist/src/core/park-store.js +216 -0
- package/dist/src/core/patch-export-update.d.ts +6 -0
- package/dist/src/core/patch-export-update.js +13 -3
- package/dist/src/core/patch-export.d.ts +3 -2
- package/dist/src/core/patch-export.js +22 -16
- package/dist/src/core/patch-files.js +26 -8
- package/dist/src/core/patch-lint-checkjs-any-import.d.ts +39 -0
- package/dist/src/core/patch-lint-checkjs-any-import.js +63 -0
- package/dist/src/core/patch-lint-checkjs-diagnostics.d.ts +17 -0
- package/dist/src/core/patch-lint-checkjs-diagnostics.js +32 -0
- package/dist/src/core/patch-lint-checkjs.d.ts +10 -1
- package/dist/src/core/patch-lint-checkjs.js +20 -13
- 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.d.ts +3 -1
- package/dist/src/core/patch-lint-cross.js +17 -4
- package/dist/src/core/patch-lint-css.d.ts +8 -2
- package/dist/src/core/patch-lint-css.js +24 -14
- 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 +23 -0
- package/dist/src/core/patch-lint-forward-registration-extended.js +36 -11
- package/dist/src/core/patch-lint-forward-registration.js +10 -0
- package/dist/src/core/patch-lint-ignore.d.ts +196 -0
- package/dist/src/core/patch-lint-ignore.js +429 -0
- package/dist/src/core/patch-lint-ownership.d.ts +10 -0
- package/dist/src/core/patch-lint-ownership.js +26 -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 +331 -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 +57 -0
- package/dist/src/core/patch-lint-token-scale.js +286 -0
- package/dist/src/core/patch-lint.d.ts +11 -83
- package/dist/src/core/patch-lint.js +66 -288
- 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 +27 -9
- package/dist/src/core/patch-manifest-key-order.d.ts +24 -0
- package/dist/src/core/patch-manifest-key-order.js +61 -0
- package/dist/src/core/patch-manifest-stage.d.ts +46 -0
- package/dist/src/core/patch-manifest-stage.js +96 -0
- package/dist/src/core/patch-manifest-validate.js +61 -3
- package/dist/src/core/patch-order-legal.d.ts +26 -0
- package/dist/src/core/patch-order-legal.js +88 -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 +22 -5
- package/dist/src/core/patch-policy.js +26 -12
- package/dist/src/core/patch-retired.d.ts +57 -0
- package/dist/src/core/patch-retired.js +92 -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 +26 -0
- package/dist/src/core/run-log.js +106 -4
- 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 +19 -1
- package/dist/src/core/test-harness-crash.js +8 -9
- package/dist/src/core/test-harness-signature.d.ts +33 -0
- package/dist/src/core/test-harness-signature.js +23 -1
- package/dist/src/core/test-raw-log.d.ts +66 -0
- package/dist/src/core/test-raw-log.js +122 -0
- package/dist/src/core/test-run-completeness.d.ts +90 -0
- package/dist/src/core/test-run-completeness.js +184 -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/test-xpcshell-retry.d.ts +2 -0
- package/dist/src/core/test-xpcshell-retry.js +2 -1
- package/dist/src/core/token-lint.d.ts +53 -0
- package/dist/src/core/token-lint.js +131 -0
- package/dist/src/core/token-sibling-sheets.d.ts +46 -0
- package/dist/src/core/token-sibling-sheets.js +119 -0
- package/dist/src/core/tree-guard.js +5 -0
- package/dist/src/core/typecheck-shim.d.ts +6 -2
- package/dist/src/core/typecheck-shim.js +205 -4
- package/dist/src/core/typecheck.js +3 -1
- package/dist/src/index.d.ts +14 -3
- package/dist/src/index.js +6 -1
- package/dist/src/types/commands/index.d.ts +2 -2
- package/dist/src/types/commands/options.d.ts +212 -24
- package/dist/src/types/commands/patches.d.ts +118 -1
- package/dist/src/types/config.d.ts +264 -0
- package/dist/src/types/config.js +9 -0
- package/dist/src/types/furnace.d.ts +63 -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 +36 -7
- package/dist/src/utils/options.js +94 -8
- 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 +369 -11
- package/docs/exit-codes.md +18 -1
- package/docs/furnace.md +139 -3
- package/docs/lifecycle-invariants.md +65 -30
- package/docs/machine-output.md +120 -12
- package/docs/patch-workflow.md +458 -22
- package/docs/run-logs.md +20 -1
- package/docs/testing.md +346 -21
- package/package.json +3 -3
|
@@ -0,0 +1,429 @@
|
|
|
1
|
+
// SPDX-License-Identifier: EUPL-1.2
|
|
2
|
+
/**
|
|
3
|
+
* `PatchMetadata.lintIgnore` entries and the waiver filter they drive.
|
|
4
|
+
*
|
|
5
|
+
* An entry is either a bare check ID, which suppresses that rule at any
|
|
6
|
+
* size, or a ceiling `{ rule, max, reason }`, which suppresses a size rule
|
|
7
|
+
* only while its measurement is at or below `max`. The bare form let a
|
|
8
|
+
* waived file grow without bound: nothing re-examined the waiver once it
|
|
9
|
+
* was written, so a 950-line file waived at review time was still waived at
|
|
10
|
+
* 2,000 lines. The ceiling makes growth past the reviewed size fire again,
|
|
11
|
+
* and lets a gate say when the waiver is no longer needed at all.
|
|
12
|
+
*
|
|
13
|
+
* Its own module so every caller that turns a manifest list into a filter
|
|
14
|
+
* (export, re-export, per-patch and aggregate lint, split, move-files)
|
|
15
|
+
* shares one reading of an entry, and so `patch-lint.ts` only calls into it.
|
|
16
|
+
*/
|
|
17
|
+
/**
|
|
18
|
+
* The size rules whose findings carry a `measurement`, and therefore the
|
|
19
|
+
* only rules a ceiling can bound. A ceiling on any other rule could never
|
|
20
|
+
* suppress anything, so the manifest validator refuses it.
|
|
21
|
+
*/
|
|
22
|
+
export const LINT_IGNORE_CEILING_RULES = [
|
|
23
|
+
'file-too-large',
|
|
24
|
+
'large-patch-lines',
|
|
25
|
+
'large-patch-files',
|
|
26
|
+
];
|
|
27
|
+
/** The only rule whose findings carry a {@link PatchSizeShare}. */
|
|
28
|
+
const SHARE_RULE = 'large-patch-lines';
|
|
29
|
+
const SHARES = ['product', 'test'];
|
|
30
|
+
/**
|
|
31
|
+
* The key a ceiling is stored under: the rule, plus `#<share>` for a
|
|
32
|
+
* share-scoped ceiling, so one rule can carry a ceiling per share.
|
|
33
|
+
*/
|
|
34
|
+
function ceilingKey(rule, share) {
|
|
35
|
+
return share === undefined ? rule : `${rule}#${share}`;
|
|
36
|
+
}
|
|
37
|
+
/** Narrows an entry to the ceiling form. */
|
|
38
|
+
export function isLintIgnoreCeiling(entry) {
|
|
39
|
+
return typeof entry !== 'string';
|
|
40
|
+
}
|
|
41
|
+
/** The check ID an entry names, whatever its form. */
|
|
42
|
+
export function lintIgnoreRule(entry) {
|
|
43
|
+
return isLintIgnoreCeiling(entry) ? entry.rule : entry;
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* One-word rendering for summaries and dry runs: `file-too-large<=1200`,
|
|
47
|
+
* `large-patch-lines[test]<=900`.
|
|
48
|
+
*/
|
|
49
|
+
export function formatLintIgnoreEntry(entry) {
|
|
50
|
+
if (!isLintIgnoreCeiling(entry))
|
|
51
|
+
return entry;
|
|
52
|
+
const share = entry.share === undefined ? '' : `[${entry.share}]`;
|
|
53
|
+
return `${entry.rule}${share}<=${String(entry.max)}`;
|
|
54
|
+
}
|
|
55
|
+
/** The identity an entry is upserted and deduplicated by. */
|
|
56
|
+
function entryKey(entry) {
|
|
57
|
+
return isLintIgnoreCeiling(entry) ? ceilingKey(entry.rule, entry.share) : entry;
|
|
58
|
+
}
|
|
59
|
+
/** Renders a list for messages, `(empty)` when there is nothing to show. */
|
|
60
|
+
export function formatLintIgnoreList(entries) {
|
|
61
|
+
return entries.length > 0 ? entries.map(formatLintIgnoreEntry).join(', ') : '(empty)';
|
|
62
|
+
}
|
|
63
|
+
function sameEntry(a, b) {
|
|
64
|
+
if (!isLintIgnoreCeiling(a) || !isLintIgnoreCeiling(b))
|
|
65
|
+
return a === b;
|
|
66
|
+
return (a.rule === b.rule &&
|
|
67
|
+
a.max === b.max &&
|
|
68
|
+
a.reason === b.reason &&
|
|
69
|
+
a.share === b.share &&
|
|
70
|
+
JSON.stringify(a.meta) === JSON.stringify(b.meta));
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Validates a raw `lintIgnore` value from `patches.json`.
|
|
74
|
+
*
|
|
75
|
+
* Bare strings keep their historical leniency (any ID, duplicates collapse
|
|
76
|
+
* later). A ceiling must name a measured size rule, carry a positive
|
|
77
|
+
* integer `max` and a non-empty `reason`, and must be the only entry for
|
|
78
|
+
* its rule: a bare ID beside a ceiling for the same rule would make the
|
|
79
|
+
* ceiling dead, and two ceilings would leave the effective one ambiguous.
|
|
80
|
+
* The one exception is `large-patch-lines`, which may carry one ceiling
|
|
81
|
+
* per `share` instead of a single share-less one. `meta` must be a JSON
|
|
82
|
+
* object and is kept verbatim.
|
|
83
|
+
*
|
|
84
|
+
* @param raw - The value under `lintIgnore`
|
|
85
|
+
* @param label - Field label for error messages, e.g. `patches[3].lintIgnore`
|
|
86
|
+
* @returns The validated entries
|
|
87
|
+
* @throws Error naming the offending element
|
|
88
|
+
*/
|
|
89
|
+
export function parseLintIgnoreEntries(raw, label) {
|
|
90
|
+
if (!Array.isArray(raw)) {
|
|
91
|
+
throw new Error(`${label} must be an array`);
|
|
92
|
+
}
|
|
93
|
+
const entries = [];
|
|
94
|
+
const ceilingRules = new Set();
|
|
95
|
+
for (const [index, item] of raw.entries()) {
|
|
96
|
+
const itemLabel = `${label}[${String(index)}]`;
|
|
97
|
+
if (typeof item === 'string') {
|
|
98
|
+
entries.push(item);
|
|
99
|
+
continue;
|
|
100
|
+
}
|
|
101
|
+
entries.push(parseCeiling(item, itemLabel));
|
|
102
|
+
}
|
|
103
|
+
const sharedRules = new Set();
|
|
104
|
+
for (const entry of entries) {
|
|
105
|
+
if (!isLintIgnoreCeiling(entry))
|
|
106
|
+
continue;
|
|
107
|
+
const key = ceilingKey(entry.rule, entry.share);
|
|
108
|
+
const clash = ceilingRules.has(key) ||
|
|
109
|
+
entries.includes(entry.rule) ||
|
|
110
|
+
(entry.share === undefined ? sharedRules.has(entry.rule) : ceilingRules.has(entry.rule));
|
|
111
|
+
if (clash) {
|
|
112
|
+
throw new Error(`${label} names "${entry.rule}" more than once; keep a single entry (bare or ceiling) ` +
|
|
113
|
+
'per rule, or one ceiling per share');
|
|
114
|
+
}
|
|
115
|
+
ceilingRules.add(key);
|
|
116
|
+
if (entry.share !== undefined)
|
|
117
|
+
sharedRules.add(entry.rule);
|
|
118
|
+
}
|
|
119
|
+
return entries;
|
|
120
|
+
}
|
|
121
|
+
function parseCeiling(item, label) {
|
|
122
|
+
if (item === null || typeof item !== 'object' || Array.isArray(item)) {
|
|
123
|
+
throw new Error(`${label} must be a check ID string or a { rule, max, reason } object`);
|
|
124
|
+
}
|
|
125
|
+
const record = item;
|
|
126
|
+
const unknownKeys = Object.keys(record).filter((key) => !CEILING_KEYS.has(key));
|
|
127
|
+
if (unknownKeys.length > 0) {
|
|
128
|
+
throw new Error(`${label} has unknown field(s): ${unknownKeys.join(', ')}`);
|
|
129
|
+
}
|
|
130
|
+
const { rule, max, reason, share, meta } = record;
|
|
131
|
+
if (typeof rule !== 'string' || !LINT_IGNORE_CEILING_RULES.includes(rule)) {
|
|
132
|
+
throw new Error(`${label}.rule must be one of the measured size rules: ${LINT_IGNORE_CEILING_RULES.join(', ')}`);
|
|
133
|
+
}
|
|
134
|
+
if (typeof max !== 'number' || !Number.isInteger(max) || max <= 0) {
|
|
135
|
+
throw new Error(`${label}.max must be a positive integer`);
|
|
136
|
+
}
|
|
137
|
+
if (typeof reason !== 'string' || reason.trim() === '') {
|
|
138
|
+
throw new Error(`${label}.reason must be a non-empty string`);
|
|
139
|
+
}
|
|
140
|
+
const ceiling = { rule, max, reason };
|
|
141
|
+
if (share !== undefined) {
|
|
142
|
+
if (rule !== SHARE_RULE || !SHARES.includes(share)) {
|
|
143
|
+
throw new Error(`${label}.share must be "product" or "test", and only on ${SHARE_RULE}`);
|
|
144
|
+
}
|
|
145
|
+
ceiling.share = share;
|
|
146
|
+
}
|
|
147
|
+
if (meta !== undefined) {
|
|
148
|
+
if (meta === null || typeof meta !== 'object' || Array.isArray(meta)) {
|
|
149
|
+
throw new Error(`${label}.meta must be a JSON object`);
|
|
150
|
+
}
|
|
151
|
+
ceiling.meta = meta;
|
|
152
|
+
}
|
|
153
|
+
return ceiling;
|
|
154
|
+
}
|
|
155
|
+
const CEILING_KEYS = new Set(['rule', 'max', 'reason', 'share', 'meta']);
|
|
156
|
+
/**
|
|
157
|
+
* Builds the waiver filter for a patch's entries.
|
|
158
|
+
*
|
|
159
|
+
* @param entries - `PatchMetadata.lintIgnore`, possibly merged with CLI flags
|
|
160
|
+
* @returns The filter, or undefined when there is nothing to waive
|
|
161
|
+
*/
|
|
162
|
+
export function buildLintIgnoreChecks(entries) {
|
|
163
|
+
if (entries === undefined || entries.length === 0)
|
|
164
|
+
return undefined;
|
|
165
|
+
const plain = new Set();
|
|
166
|
+
const ceilings = new Map();
|
|
167
|
+
for (const entry of entries) {
|
|
168
|
+
if (isLintIgnoreCeiling(entry))
|
|
169
|
+
ceilings.set(ceilingKey(entry.rule, entry.share), entry);
|
|
170
|
+
else
|
|
171
|
+
plain.add(entry);
|
|
172
|
+
}
|
|
173
|
+
return ceilings.size > 0 ? Object.assign(plain, { ceilings }) : plain;
|
|
174
|
+
}
|
|
175
|
+
/**
|
|
176
|
+
* Canonical string form of a filter for the per-patch lint cache's waiver
|
|
177
|
+
* comparison: bare IDs as themselves, ceilings as `rule<=max`.
|
|
178
|
+
*/
|
|
179
|
+
export function lintIgnoreCacheTokens(checks) {
|
|
180
|
+
if (checks === undefined)
|
|
181
|
+
return [];
|
|
182
|
+
const ceilings = [...(checks.ceilings?.values() ?? [])].map(formatLintIgnoreEntry);
|
|
183
|
+
return [...checks, ...ceilings];
|
|
184
|
+
}
|
|
185
|
+
/**
|
|
186
|
+
* Unions CLI-flag check IDs into an existing list, the append-only
|
|
187
|
+
* semantics `export` / `re-export --lint-ignore` have always had. A bare ID
|
|
188
|
+
* whose rule already has an entry (bare or ceiling) is a no-op: widening a
|
|
189
|
+
* reviewed ceiling to an unbounded waiver is `patch lint-ignore`'s job, where
|
|
190
|
+
* it is confirmed explicitly. Ceilings deduplicate by rule and share, so a
|
|
191
|
+
* rule's per-share ceilings all survive.
|
|
192
|
+
*
|
|
193
|
+
* @returns The merged list, or undefined when it is empty
|
|
194
|
+
*/
|
|
195
|
+
export function mergeLintIgnoreIds(existing, ids) {
|
|
196
|
+
const merged = [];
|
|
197
|
+
const keys = new Set();
|
|
198
|
+
const rules = new Set();
|
|
199
|
+
for (const entry of [...(existing ?? []), ...(ids ?? [])]) {
|
|
200
|
+
const blocked = isLintIgnoreCeiling(entry) ? keys.has(entryKey(entry)) : rules.has(entry);
|
|
201
|
+
if (blocked)
|
|
202
|
+
continue;
|
|
203
|
+
keys.add(entryKey(entry));
|
|
204
|
+
rules.add(lintIgnoreRule(entry));
|
|
205
|
+
merged.push(entry);
|
|
206
|
+
}
|
|
207
|
+
return merged.length > 0 ? merged : undefined;
|
|
208
|
+
}
|
|
209
|
+
/**
|
|
210
|
+
* Adds or replaces entries, the `patch lint-ignore --add` semantics: an
|
|
211
|
+
* explicit `--add <rule>` turns a rule's ceilings into a bare waiver,
|
|
212
|
+
* `--add <rule> --max <n>` sets or moves the rule's ceiling, and a
|
|
213
|
+
* share-scoped ceiling replaces only its own share (plus any bare or
|
|
214
|
+
* share-less entry for the rule, which it would contradict).
|
|
215
|
+
*
|
|
216
|
+
* @returns The new list, or undefined when it is empty
|
|
217
|
+
*/
|
|
218
|
+
export function upsertLintIgnoreEntries(existing, additions) {
|
|
219
|
+
let next = [...existing];
|
|
220
|
+
for (const addition of additions) {
|
|
221
|
+
const rule = lintIgnoreRule(addition);
|
|
222
|
+
const scoped = isLintIgnoreCeiling(addition) && addition.share !== undefined;
|
|
223
|
+
const replaced = (entry) => lintIgnoreRule(entry) === rule &&
|
|
224
|
+
(!scoped ||
|
|
225
|
+
!isLintIgnoreCeiling(entry) ||
|
|
226
|
+
entry.share === undefined ||
|
|
227
|
+
entryKey(entry) === entryKey(addition));
|
|
228
|
+
const index = next.findIndex(replaced);
|
|
229
|
+
const kept = next.filter((entry) => !replaced(entry));
|
|
230
|
+
// The addition takes the first replaced entry's place, so a moved
|
|
231
|
+
// ceiling keeps its position in the list.
|
|
232
|
+
next =
|
|
233
|
+
index === -1
|
|
234
|
+
? [...kept, addition]
|
|
235
|
+
: [...kept.slice(0, index), addition, ...kept.slice(index)];
|
|
236
|
+
}
|
|
237
|
+
return mergeLintIgnoreIds(next, undefined);
|
|
238
|
+
}
|
|
239
|
+
/** Removes every entry naming one of `rules`. Undefined when nothing is left. */
|
|
240
|
+
export function removeLintIgnoreRules(existing, rules) {
|
|
241
|
+
const drop = new Set(rules);
|
|
242
|
+
const remaining = existing.filter((entry) => !drop.has(lintIgnoreRule(entry)));
|
|
243
|
+
return remaining.length > 0 ? remaining : undefined;
|
|
244
|
+
}
|
|
245
|
+
/** Entries present in `after` but not (identically) in `before`. */
|
|
246
|
+
export function addedLintIgnoreEntries(before, after) {
|
|
247
|
+
return after.filter((entry) => !before.some((prior) => sameEntry(prior, entry)));
|
|
248
|
+
}
|
|
249
|
+
/**
|
|
250
|
+
* Splits issues into the ones that still surface and the ones a waiver
|
|
251
|
+
* drops.
|
|
252
|
+
*
|
|
253
|
+
* A bare ID drops every finding of its rule. A ceiling drops a finding only
|
|
254
|
+
* when it carries a measurement at or below `max`. Past the ceiling the
|
|
255
|
+
* finding surfaces at its own severity with the ceiling named, and a
|
|
256
|
+
* finding with no measurement surfaces too (fail closed).
|
|
257
|
+
*/
|
|
258
|
+
export function partitionLintIgnoredIssues(issues, checks) {
|
|
259
|
+
const kept = [];
|
|
260
|
+
const suppressed = [];
|
|
261
|
+
for (const issue of issues) {
|
|
262
|
+
if (checks?.has(issue.check) === true) {
|
|
263
|
+
suppressed.push(issue);
|
|
264
|
+
continue;
|
|
265
|
+
}
|
|
266
|
+
const ceiling = ceilingFor(issue, checks);
|
|
267
|
+
if (ceiling === undefined) {
|
|
268
|
+
kept.push(issue);
|
|
269
|
+
continue;
|
|
270
|
+
}
|
|
271
|
+
if (issue.measurement !== undefined && issue.measurement <= ceiling.max) {
|
|
272
|
+
suppressed.push(issue);
|
|
273
|
+
continue;
|
|
274
|
+
}
|
|
275
|
+
// A notice never gates, so a ceiling does not bound it: one past `max`
|
|
276
|
+
// surfaces as the plain notice it is, not as an exceeded waiver.
|
|
277
|
+
if (issue.severity === 'notice') {
|
|
278
|
+
kept.push(issue);
|
|
279
|
+
continue;
|
|
280
|
+
}
|
|
281
|
+
kept.push({ ...issue, message: `${issue.message} ${describeExceededCeiling(ceiling)}` });
|
|
282
|
+
}
|
|
283
|
+
return { kept, suppressed };
|
|
284
|
+
}
|
|
285
|
+
/** The ceiling that governs `issue`: its share's, else the rule's share-less one. */
|
|
286
|
+
function ceilingFor(issue, checks) {
|
|
287
|
+
const ceilings = checks?.ceilings;
|
|
288
|
+
if (ceilings === undefined)
|
|
289
|
+
return undefined;
|
|
290
|
+
if (issue.share !== undefined) {
|
|
291
|
+
const scoped = ceilings.get(ceilingKey(issue.check, issue.share));
|
|
292
|
+
if (scoped !== undefined)
|
|
293
|
+
return scoped;
|
|
294
|
+
}
|
|
295
|
+
return ceilings.get(issue.check);
|
|
296
|
+
}
|
|
297
|
+
function describeExceededCeiling(ceiling) {
|
|
298
|
+
const share = ceiling.share === undefined ? '' : ` (${ceiling.share} share)`;
|
|
299
|
+
return (`This exceeds the patch's lintIgnore ceiling for ${ceiling.rule}${share} ` +
|
|
300
|
+
`(max ${String(ceiling.max)}: ${ceiling.reason}).`);
|
|
301
|
+
}
|
|
302
|
+
/** True when `checks` waives `issue` (see {@link partitionLintIgnoredIssues}). */
|
|
303
|
+
export function isLintIgnored(issue, checks) {
|
|
304
|
+
return partitionLintIgnoredIssues([issue], checks).suppressed.length === 1;
|
|
305
|
+
}
|
|
306
|
+
/**
|
|
307
|
+
* Drops issues that a patch owning `issue.file` (via `filesAffected`)
|
|
308
|
+
* waives. Used where findings carry no patch scope of their own (aggregate
|
|
309
|
+
* `lint`). Each owner keeps its own filter, so a ceiling stays bounded by
|
|
310
|
+
* the patch that declared it instead of merging into a file-wide union; an
|
|
311
|
+
* issue is dropped when any owner waives it.
|
|
312
|
+
*/
|
|
313
|
+
export function filterLintIgnoredByOwners(issues, owners) {
|
|
314
|
+
const byFile = new Map();
|
|
315
|
+
for (const owner of owners) {
|
|
316
|
+
const checks = buildLintIgnoreChecks(owner?.lintIgnore);
|
|
317
|
+
if (!checks)
|
|
318
|
+
continue;
|
|
319
|
+
for (const file of owner?.filesAffected ?? []) {
|
|
320
|
+
byFile.set(file, [...(byFile.get(file) ?? []), checks]);
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
if (byFile.size === 0)
|
|
324
|
+
return issues;
|
|
325
|
+
return issues.filter((issue) => !(byFile.get(issue.file) ?? []).some((checks) => isLintIgnored(issue, checks)));
|
|
326
|
+
}
|
|
327
|
+
function judgeWaiver(rule, max, share, issues, patchMeasurements) {
|
|
328
|
+
const findings = issues.filter((issue) => issue.check === rule && (share === undefined || issue.share === share));
|
|
329
|
+
const measured = findings
|
|
330
|
+
.map((issue) => issue.measurement)
|
|
331
|
+
.filter((value) => value !== undefined);
|
|
332
|
+
// A share-scoped ceiling has no patch-level fallback: the total counts
|
|
333
|
+
// both shares, which is not what it bounds.
|
|
334
|
+
const fallback = share !== undefined
|
|
335
|
+
? null
|
|
336
|
+
: rule === 'large-patch-lines'
|
|
337
|
+
? patchMeasurements.lines
|
|
338
|
+
: rule === 'large-patch-files'
|
|
339
|
+
? patchMeasurements.files
|
|
340
|
+
: null;
|
|
341
|
+
const measurement = measured.length > 0 ? Math.max(...measured) : fallback;
|
|
342
|
+
const gating = findings.filter((issue) => issue.severity !== 'notice');
|
|
343
|
+
const exceeded = max !== null &&
|
|
344
|
+
gating.some((issue) => issue.measurement !== undefined && issue.measurement > max);
|
|
345
|
+
const state = exceeded
|
|
346
|
+
? 'exceeded'
|
|
347
|
+
: gating.length > 0
|
|
348
|
+
? 'within'
|
|
349
|
+
: 'removable';
|
|
350
|
+
return { measurement, state };
|
|
351
|
+
}
|
|
352
|
+
/**
|
|
353
|
+
* Evaluates each ceiling on a patch against the findings its lint produced
|
|
354
|
+
* (both surfaced and suppressed).
|
|
355
|
+
*
|
|
356
|
+
* @param entries - The patch's `lintIgnore`
|
|
357
|
+
* @param issues - Every finding of the patch's lint, surfaced and suppressed
|
|
358
|
+
* @param patchMeasurements - Patch-level measurements used when a
|
|
359
|
+
* patch-size rule did not fire at all, so the report still shows the
|
|
360
|
+
* current number
|
|
361
|
+
*/
|
|
362
|
+
export function evaluateLintIgnoreCeilings(entries, issues, patchMeasurements) {
|
|
363
|
+
const statuses = [];
|
|
364
|
+
for (const entry of entries ?? []) {
|
|
365
|
+
if (!isLintIgnoreCeiling(entry))
|
|
366
|
+
continue;
|
|
367
|
+
statuses.push({
|
|
368
|
+
rule: entry.rule,
|
|
369
|
+
max: entry.max,
|
|
370
|
+
reason: entry.reason,
|
|
371
|
+
...(entry.share !== undefined ? { share: entry.share } : {}),
|
|
372
|
+
...(entry.meta !== undefined ? { meta: entry.meta } : {}),
|
|
373
|
+
...judgeWaiver(entry.rule, entry.max, entry.share, issues, patchMeasurements),
|
|
374
|
+
});
|
|
375
|
+
}
|
|
376
|
+
return statuses;
|
|
377
|
+
}
|
|
378
|
+
/**
|
|
379
|
+
* Evaluates every waiver on a patch, bare IDs included, so a waiver audit
|
|
380
|
+
* reads each one's current measurement from the report instead of
|
|
381
|
+
* re-measuring bare waivers from `suppressedIssues`.
|
|
382
|
+
*
|
|
383
|
+
* @param entries - The patch's `lintIgnore`
|
|
384
|
+
* @param issues - Every finding of the patch's lint, surfaced and suppressed
|
|
385
|
+
* @param patchMeasurements - See {@link evaluateLintIgnoreCeilings}
|
|
386
|
+
*/
|
|
387
|
+
export function evaluateLintIgnoreWaivers(entries, issues, patchMeasurements) {
|
|
388
|
+
return (entries ?? []).map((entry) => {
|
|
389
|
+
if (!isLintIgnoreCeiling(entry)) {
|
|
390
|
+
return {
|
|
391
|
+
kind: 'bare',
|
|
392
|
+
rule: entry,
|
|
393
|
+
max: null,
|
|
394
|
+
reason: null,
|
|
395
|
+
...judgeWaiver(entry, null, undefined, issues, patchMeasurements),
|
|
396
|
+
};
|
|
397
|
+
}
|
|
398
|
+
return {
|
|
399
|
+
kind: 'ceiling',
|
|
400
|
+
rule: entry.rule,
|
|
401
|
+
max: entry.max,
|
|
402
|
+
reason: entry.reason,
|
|
403
|
+
...(entry.share !== undefined ? { share: entry.share } : {}),
|
|
404
|
+
...(entry.meta !== undefined ? { meta: entry.meta } : {}),
|
|
405
|
+
...judgeWaiver(entry.rule, entry.max, entry.share, issues, patchMeasurements),
|
|
406
|
+
};
|
|
407
|
+
});
|
|
408
|
+
}
|
|
409
|
+
/**
|
|
410
|
+
* Notice-severity findings for ceilings that are no longer needed, so the
|
|
411
|
+
* waiver's removal is prompted by the tool that enforces it rather than
|
|
412
|
+
* discovered by hand.
|
|
413
|
+
*/
|
|
414
|
+
export function removableCeilingIssues(patchFilename, statuses) {
|
|
415
|
+
return statuses
|
|
416
|
+
.filter((status) => status.state === 'removable')
|
|
417
|
+
.map((status) => {
|
|
418
|
+
const now = status.measurement === null ? 'below its notice band' : `at ${String(status.measurement)}`;
|
|
419
|
+
const share = status.share === undefined ? '' : `, ${status.share} share`;
|
|
420
|
+
return {
|
|
421
|
+
file: patchFilename,
|
|
422
|
+
check: 'lint-ignore-removable',
|
|
423
|
+
message: `lintIgnore ceiling for ${status.rule} (max ${String(status.max)}${share}) is no longer needed: ` +
|
|
424
|
+
`the measurement is ${now}, under the rule's own warning threshold. ` +
|
|
425
|
+
`Remove it with "fireforge patch lint-ignore ${patchFilename} --remove ${status.rule}".`,
|
|
426
|
+
severity: 'notice',
|
|
427
|
+
};
|
|
428
|
+
});
|
|
429
|
+
}
|
|
@@ -51,6 +51,16 @@ export declare function resolvePatchOwnedChromeScripts(currentNewFiles: Set<stri
|
|
|
51
51
|
* `patch-lint-ownership.test.ts`.
|
|
52
52
|
*/
|
|
53
53
|
export declare function isTestScriptFile(file: string): boolean;
|
|
54
|
+
/**
|
|
55
|
+
* For each test script, the ES modules (`.mjs`, `.sys.mjs`) the queue
|
|
56
|
+
* creates up to and including the test's own patch: what its imports can
|
|
57
|
+
* resolve to in a per-test checkJs program. A test no patch touches sees
|
|
58
|
+
* every module the queue creates.
|
|
59
|
+
*
|
|
60
|
+
* @param patchQueueCtx - The queue, in apply order
|
|
61
|
+
* @returns Lookup from a test script path to its resolvable modules
|
|
62
|
+
*/
|
|
63
|
+
export declare function resolvableModulesForTests(patchQueueCtx: PatchQueueContext): (testFile: string) => ReadonlySet<string>;
|
|
54
64
|
/**
|
|
55
65
|
* Returns the set of patch-owned test `.js` files. Same
|
|
56
66
|
* ownership semantics as {@link resolvePatchOwnedSysMjs}. Consumed by the
|
|
@@ -84,6 +84,32 @@ export function isTestScriptFile(file) {
|
|
|
84
84
|
const basename = file.split('/').pop() ?? '';
|
|
85
85
|
return /^(?:browser_|test_|xpcshell_).*\.js$/.test(basename);
|
|
86
86
|
}
|
|
87
|
+
/**
|
|
88
|
+
* For each test script, the ES modules (`.mjs`, `.sys.mjs`) the queue
|
|
89
|
+
* creates up to and including the test's own patch: what its imports can
|
|
90
|
+
* resolve to in a per-test checkJs program. A test no patch touches sees
|
|
91
|
+
* every module the queue creates.
|
|
92
|
+
*
|
|
93
|
+
* @param patchQueueCtx - The queue, in apply order
|
|
94
|
+
* @returns Lookup from a test script path to its resolvable modules
|
|
95
|
+
*/
|
|
96
|
+
export function resolvableModulesForTests(patchQueueCtx) {
|
|
97
|
+
const byTest = new Map();
|
|
98
|
+
const upTo = new Set();
|
|
99
|
+
for (const entry of patchQueueCtx.entries) {
|
|
100
|
+
for (const file of entry.newFiles.keys()) {
|
|
101
|
+
if (file.endsWith('.mjs') && !isTestScriptFile(file))
|
|
102
|
+
upTo.add(file);
|
|
103
|
+
}
|
|
104
|
+
const snapshot = new Set(upTo);
|
|
105
|
+
for (const file of [...entry.newFiles.keys(), ...entry.modifiedFileAdditions.keys()]) {
|
|
106
|
+
if (isTestScriptFile(file) && !byTest.has(file))
|
|
107
|
+
byTest.set(file, snapshot);
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
const all = upTo;
|
|
111
|
+
return (testFile) => byTest.get(testFile) ?? all;
|
|
112
|
+
}
|
|
87
113
|
/**
|
|
88
114
|
* Returns the set of patch-owned test `.js` files. Same
|
|
89
115
|
* ownership semantics as {@link resolvePatchOwnedSysMjs}. Consumed by the
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The patch-size thresholds (`large-patch-lines`, `large-patch-files`) and
|
|
3
|
+
* their merge with `patchLint.patchSizeThresholds`.
|
|
4
|
+
*
|
|
5
|
+
* Its own dependency-free module for the same reason as
|
|
6
|
+
* `patch-lint-file-size.ts`: the config validator checks an override
|
|
7
|
+
* against the merged result, and importing the rule bodies from config
|
|
8
|
+
* parsing would draw an import cycle.
|
|
9
|
+
*/
|
|
10
|
+
import type { PatchLintPatchSizeThresholds } from '../types/config.js';
|
|
11
|
+
import type { ResolvedFileSizeTier } from './patch-lint-file-size.js';
|
|
12
|
+
/** The tiers a patch is measured against. */
|
|
13
|
+
export type PatchSizeTierName = 'general' | 'test' | 'branding';
|
|
14
|
+
/**
|
|
15
|
+
* A resolved `large-patch-files` triple. `error` is `null` when no error
|
|
16
|
+
* band is configured. `notice` equal to `warning` means no notice band.
|
|
17
|
+
*/
|
|
18
|
+
export interface ResolvedPatchFilesTier {
|
|
19
|
+
notice: number;
|
|
20
|
+
warning: number;
|
|
21
|
+
error: number | null;
|
|
22
|
+
}
|
|
23
|
+
/** Line and file-count thresholds governing one patch-size tier. */
|
|
24
|
+
export interface PatchSizeThresholds {
|
|
25
|
+
/** Line counts at which `large-patch-lines` notice/warning/error issues fire. */
|
|
26
|
+
lines: ResolvedFileSizeTier;
|
|
27
|
+
/** File counts at which `large-patch-files` notice/warning/error issues fire. */
|
|
28
|
+
files: ResolvedPatchFilesTier;
|
|
29
|
+
/**
|
|
30
|
+
* The `files.warning` threshold, kept for consumers written before the
|
|
31
|
+
* files triple existed.
|
|
32
|
+
*/
|
|
33
|
+
maxFiles: number;
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Merges the configured overrides over the defaults for one tier.
|
|
37
|
+
*
|
|
38
|
+
* @param tier - The tier to resolve
|
|
39
|
+
* @param overrides - The `patchLint.patchSizeThresholds` block, if any
|
|
40
|
+
* @returns The effective thresholds
|
|
41
|
+
*/
|
|
42
|
+
export declare function resolvePatchSizeThresholds(tier: PatchSizeTierName, overrides: PatchLintPatchSizeThresholds | undefined): PatchSizeThresholds;
|
|
43
|
+
/**
|
|
44
|
+
* True when `file` matches one of the `patchLint.patchSizeRegistrationFiles`
|
|
45
|
+
* globs. A glob without `/` matches the basename, one with `/` the whole
|
|
46
|
+
* engine-relative path. `*` and `?` stay within a segment, `**` crosses
|
|
47
|
+
* segments.
|
|
48
|
+
*
|
|
49
|
+
* @param file - Engine-relative POSIX path
|
|
50
|
+
* @param globs - The configured globs
|
|
51
|
+
*/
|
|
52
|
+
export declare function isPatchSizeRegistrationFile(file: string, globs: readonly string[]): boolean;
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
// SPDX-License-Identifier: EUPL-1.2
|
|
2
|
+
/**
|
|
3
|
+
* The patch-size thresholds (`large-patch-lines`, `large-patch-files`) and
|
|
4
|
+
* their merge with `patchLint.patchSizeThresholds`.
|
|
5
|
+
*
|
|
6
|
+
* Its own dependency-free module for the same reason as
|
|
7
|
+
* `patch-lint-file-size.ts`: the config validator checks an override
|
|
8
|
+
* against the merged result, and importing the rule bodies from config
|
|
9
|
+
* parsing would draw an import cycle.
|
|
10
|
+
*/
|
|
11
|
+
import { escapeRegex } from '../utils/regex.js';
|
|
12
|
+
/**
|
|
13
|
+
* Built-in line-count bands per tier.
|
|
14
|
+
*
|
|
15
|
+
* Branding patches have a legitimate reason to be large: they include
|
|
16
|
+
* every locale's `brand.ftl`, copied upstream CSS/PNG assets, and the
|
|
17
|
+
* fork-specific `configure.sh` / `brand.properties` under a single
|
|
18
|
+
* `browser/branding/<name>/` subtree. The tier is calibrated against a
|
|
19
|
+
* fresh-fork branding export, which lands around 15-16k lines (localized
|
|
20
|
+
* brand.ftl across many locales + SVG path data + copied upstream CSS).
|
|
21
|
+
* That is the minimum branding diff, so it must surface as a `notice`
|
|
22
|
+
* rather than a `warning`: the warning band sits above it with ~13%
|
|
23
|
+
* headroom, and the error band at roughly 2× the baseline, where
|
|
24
|
+
* non-branding work is probably bundled in.
|
|
25
|
+
*
|
|
26
|
+
* Permissive thresholds are safe because the *gate* into this tier is
|
|
27
|
+
* narrow: auto-detect requires every file under `browser/branding/` plus a
|
|
28
|
+
* tight registration allowlist, or an explicit `PatchMetadata.tier:
|
|
29
|
+
* "branding"` opt-in. A non-branding patch cannot accidentally land here.
|
|
30
|
+
*/
|
|
31
|
+
const DEFAULT_PATCH_LINE_THRESHOLDS = {
|
|
32
|
+
general: { notice: 800, warning: 1500, error: 3000 },
|
|
33
|
+
test: { notice: 1500, warning: 3000, error: 6000 },
|
|
34
|
+
branding: { notice: 8000, warning: 18000, error: 30000 },
|
|
35
|
+
};
|
|
36
|
+
/**
|
|
37
|
+
* Built-in `large-patch-files` warning band per tier. There is no notice or
|
|
38
|
+
* error band by default: file count expresses scope, not blast radius.
|
|
39
|
+
*
|
|
40
|
+
* The branding tier sits well above the typical floor because branding
|
|
41
|
+
* patches inherently span many files: PNG/ICO icon assets in 7+ sizes, MSIX
|
|
42
|
+
* manifests, channel-specific configs, locale `.ftl` files, Windows/macOS
|
|
43
|
+
* launcher resources. A fresh-fork branding bundle is around 56 files, so 60
|
|
44
|
+
* leaves headroom for additional channels/locales while still firing on a
|
|
45
|
+
* genuinely bloated patch. The test tier matches general because a
|
|
46
|
+
* test-only patch rarely touches many files.
|
|
47
|
+
*/
|
|
48
|
+
const DEFAULT_PATCH_FILES_WARNING = {
|
|
49
|
+
general: 5,
|
|
50
|
+
test: 5,
|
|
51
|
+
branding: 60,
|
|
52
|
+
};
|
|
53
|
+
/**
|
|
54
|
+
* Merges the configured overrides over the defaults for one tier.
|
|
55
|
+
*
|
|
56
|
+
* @param tier - The tier to resolve
|
|
57
|
+
* @param overrides - The `patchLint.patchSizeThresholds` block, if any
|
|
58
|
+
* @returns The effective thresholds
|
|
59
|
+
*/
|
|
60
|
+
export function resolvePatchSizeThresholds(tier, overrides) {
|
|
61
|
+
const configured = overrides?.[tier];
|
|
62
|
+
const lines = { ...DEFAULT_PATCH_LINE_THRESHOLDS[tier], ...configured?.lines };
|
|
63
|
+
const files = resolveFilesTier(DEFAULT_PATCH_FILES_WARNING[tier], configured?.files);
|
|
64
|
+
return { lines, files, maxFiles: files.warning };
|
|
65
|
+
}
|
|
66
|
+
function resolveFilesTier(defaultWarning, configured) {
|
|
67
|
+
const warning = configured?.warning ?? defaultWarning;
|
|
68
|
+
return {
|
|
69
|
+
notice: configured?.notice ?? warning,
|
|
70
|
+
warning,
|
|
71
|
+
error: configured?.error ?? null,
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* True when `file` matches one of the `patchLint.patchSizeRegistrationFiles`
|
|
76
|
+
* globs. A glob without `/` matches the basename, one with `/` the whole
|
|
77
|
+
* engine-relative path. `*` and `?` stay within a segment, `**` crosses
|
|
78
|
+
* segments.
|
|
79
|
+
*
|
|
80
|
+
* @param file - Engine-relative POSIX path
|
|
81
|
+
* @param globs - The configured globs
|
|
82
|
+
*/
|
|
83
|
+
export function isPatchSizeRegistrationFile(file, globs) {
|
|
84
|
+
if (globs.length === 0)
|
|
85
|
+
return false;
|
|
86
|
+
const basename = file.slice(file.lastIndexOf('/') + 1);
|
|
87
|
+
return globs.some((glob) => globToRegExp(glob).test(glob.includes('/') ? file : basename));
|
|
88
|
+
}
|
|
89
|
+
function globToRegExp(glob) {
|
|
90
|
+
let source = '';
|
|
91
|
+
for (let i = 0; i < glob.length; i++) {
|
|
92
|
+
const char = glob.charAt(i);
|
|
93
|
+
if (char === '*' && glob.charAt(i + 1) === '*') {
|
|
94
|
+
// `**/` matches zero or more whole segments, a bare `**` anything.
|
|
95
|
+
const slash = glob.charAt(i + 2) === '/';
|
|
96
|
+
source += slash ? '(?:.*/)?' : '.*';
|
|
97
|
+
i += slash ? 2 : 1;
|
|
98
|
+
}
|
|
99
|
+
else if (char === '*') {
|
|
100
|
+
source += '[^/]*';
|
|
101
|
+
}
|
|
102
|
+
else if (char === '?') {
|
|
103
|
+
source += '[^/]';
|
|
104
|
+
}
|
|
105
|
+
else {
|
|
106
|
+
source += escapeRegex(char);
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
return new RegExp(`^${source}$`);
|
|
110
|
+
}
|