@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
|
@@ -243,6 +243,16 @@ export function lintPatchQueueForwardRegistrations(ctx) {
|
|
|
243
243
|
check: 'forward-registration',
|
|
244
244
|
patches: [found.patch],
|
|
245
245
|
fingerprint: `forward-registration|${found.patch}|${found.file}|${found.token}|${found.creates}|${found.owner}`,
|
|
246
|
+
stagedDeclarations: [
|
|
247
|
+
{
|
|
248
|
+
patch: found.patch,
|
|
249
|
+
kind: 'registration',
|
|
250
|
+
file: found.file,
|
|
251
|
+
line: found.line,
|
|
252
|
+
creates: found.creates,
|
|
253
|
+
owner: found.owner,
|
|
254
|
+
},
|
|
255
|
+
],
|
|
246
256
|
message: `${found.file} in ${found.patch} registers "${found.token}" ${KIND_PHRASES[found.kind]}, ` +
|
|
247
257
|
`but ${found.creates} is created by the later patch ${found.owner}. ` +
|
|
248
258
|
'Applying the queue up to this patch leaves the registration dangling. ' +
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `PatchMetadata.lintIgnore` entries and the waiver filter they drive.
|
|
3
|
+
*
|
|
4
|
+
* An entry is either a bare check ID, which suppresses that rule at any
|
|
5
|
+
* size, or a ceiling `{ rule, max, reason }`, which suppresses a size rule
|
|
6
|
+
* only while its measurement is at or below `max`. The bare form let a
|
|
7
|
+
* waived file grow without bound: nothing re-examined the waiver once it
|
|
8
|
+
* was written, so a 950-line file waived at review time was still waived at
|
|
9
|
+
* 2,000 lines. The ceiling makes growth past the reviewed size fire again,
|
|
10
|
+
* and lets a gate say when the waiver is no longer needed at all.
|
|
11
|
+
*
|
|
12
|
+
* Its own module so every caller that turns a manifest list into a filter
|
|
13
|
+
* (export, re-export, per-patch and aggregate lint, split, move-files)
|
|
14
|
+
* shares one reading of an entry, and so `patch-lint.ts` only calls into it.
|
|
15
|
+
*/
|
|
16
|
+
import type { LintIgnoreCeiling, LintIgnoreEntry, PatchLintIssue } from '../types/commands/index.js';
|
|
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 declare const LINT_IGNORE_CEILING_RULES: readonly string[];
|
|
23
|
+
/**
|
|
24
|
+
* A waiver filter: the bare check IDs as a set (so a plain `Set<string>`
|
|
25
|
+
* still satisfies the type), plus the ceilings keyed by rule.
|
|
26
|
+
*
|
|
27
|
+
* Bare IDs and ceilings are kept apart on purpose. A caller that only knows
|
|
28
|
+
* `has()` treats a ceiling-waived rule as not waived, which fails closed.
|
|
29
|
+
*/
|
|
30
|
+
export type LintIgnoreChecks = ReadonlySet<string> & {
|
|
31
|
+
readonly ceilings?: ReadonlyMap<string, LintIgnoreCeiling>;
|
|
32
|
+
};
|
|
33
|
+
/** Narrows an entry to the ceiling form. */
|
|
34
|
+
export declare function isLintIgnoreCeiling(entry: LintIgnoreEntry): entry is LintIgnoreCeiling;
|
|
35
|
+
/** The check ID an entry names, whatever its form. */
|
|
36
|
+
export declare function lintIgnoreRule(entry: LintIgnoreEntry): string;
|
|
37
|
+
/** One-word rendering for summaries and dry runs: `file-too-large<=1200`. */
|
|
38
|
+
export declare function formatLintIgnoreEntry(entry: LintIgnoreEntry): string;
|
|
39
|
+
/** Renders a list for messages, `(empty)` when there is nothing to show. */
|
|
40
|
+
export declare function formatLintIgnoreList(entries: readonly LintIgnoreEntry[]): string;
|
|
41
|
+
/**
|
|
42
|
+
* Validates a raw `lintIgnore` value from `patches.json`.
|
|
43
|
+
*
|
|
44
|
+
* Bare strings keep their historical leniency (any ID, duplicates collapse
|
|
45
|
+
* later). A ceiling must name a measured size rule, carry a positive
|
|
46
|
+
* integer `max` and a non-empty `reason`, and must be the only entry for
|
|
47
|
+
* its rule: a bare ID beside a ceiling for the same rule would make the
|
|
48
|
+
* ceiling dead, and two ceilings would leave the effective one ambiguous.
|
|
49
|
+
*
|
|
50
|
+
* @param raw - The value under `lintIgnore`
|
|
51
|
+
* @param label - Field label for error messages, e.g. `patches[3].lintIgnore`
|
|
52
|
+
* @returns The validated entries
|
|
53
|
+
* @throws Error naming the offending element
|
|
54
|
+
*/
|
|
55
|
+
export declare function parseLintIgnoreEntries(raw: unknown, label: string): LintIgnoreEntry[];
|
|
56
|
+
/**
|
|
57
|
+
* Builds the waiver filter for a patch's entries.
|
|
58
|
+
*
|
|
59
|
+
* @param entries - `PatchMetadata.lintIgnore`, possibly merged with CLI flags
|
|
60
|
+
* @returns The filter, or undefined when there is nothing to waive
|
|
61
|
+
*/
|
|
62
|
+
export declare function buildLintIgnoreChecks(entries: readonly LintIgnoreEntry[] | undefined): LintIgnoreChecks | undefined;
|
|
63
|
+
/**
|
|
64
|
+
* Canonical string form of a filter for the per-patch lint cache's waiver
|
|
65
|
+
* comparison: bare IDs as themselves, ceilings as `rule<=max`.
|
|
66
|
+
*/
|
|
67
|
+
export declare function lintIgnoreCacheTokens(checks: LintIgnoreChecks | undefined): string[];
|
|
68
|
+
/**
|
|
69
|
+
* Unions CLI-flag check IDs into an existing list, the append-only
|
|
70
|
+
* semantics `export` / `re-export --lint-ignore` have always had. A bare ID
|
|
71
|
+
* whose rule already has an entry (bare or ceiling) is a no-op: widening a
|
|
72
|
+
* reviewed ceiling to an unbounded waiver is `patch lint-ignore`'s job, where
|
|
73
|
+
* it is confirmed explicitly.
|
|
74
|
+
*
|
|
75
|
+
* @returns The merged list, or undefined when it is empty
|
|
76
|
+
*/
|
|
77
|
+
export declare function mergeLintIgnoreIds(existing: readonly LintIgnoreEntry[] | undefined, ids: readonly string[] | undefined): LintIgnoreEntry[] | undefined;
|
|
78
|
+
/**
|
|
79
|
+
* Adds or replaces entries by rule, the `patch lint-ignore --add` semantics:
|
|
80
|
+
* an explicit `--add <rule>` turns a ceiling into a bare waiver, and
|
|
81
|
+
* `--add <rule> --max <n>` sets or moves the ceiling.
|
|
82
|
+
*
|
|
83
|
+
* @returns The new list, or undefined when it is empty
|
|
84
|
+
*/
|
|
85
|
+
export declare function upsertLintIgnoreEntries(existing: readonly LintIgnoreEntry[], additions: readonly LintIgnoreEntry[]): LintIgnoreEntry[] | undefined;
|
|
86
|
+
/** Removes every entry naming one of `rules`. Undefined when nothing is left. */
|
|
87
|
+
export declare function removeLintIgnoreRules(existing: readonly LintIgnoreEntry[], rules: readonly string[]): LintIgnoreEntry[] | undefined;
|
|
88
|
+
/** Entries present in `after` but not (identically) in `before`. */
|
|
89
|
+
export declare function addedLintIgnoreEntries(before: readonly LintIgnoreEntry[], after: readonly LintIgnoreEntry[]): LintIgnoreEntry[];
|
|
90
|
+
/**
|
|
91
|
+
* Splits issues into the ones that still surface and the ones a waiver
|
|
92
|
+
* drops.
|
|
93
|
+
*
|
|
94
|
+
* A bare ID drops every finding of its rule. A ceiling drops a finding only
|
|
95
|
+
* when it carries a measurement at or below `max`. Past the ceiling the
|
|
96
|
+
* finding surfaces at its own severity with the ceiling named, and a
|
|
97
|
+
* finding with no measurement surfaces too (fail closed).
|
|
98
|
+
*/
|
|
99
|
+
export declare function partitionLintIgnoredIssues(issues: readonly PatchLintIssue[], checks: LintIgnoreChecks | undefined): {
|
|
100
|
+
kept: PatchLintIssue[];
|
|
101
|
+
suppressed: PatchLintIssue[];
|
|
102
|
+
};
|
|
103
|
+
/** True when `checks` waives `issue` (see {@link partitionLintIgnoredIssues}). */
|
|
104
|
+
export declare function isLintIgnored(issue: PatchLintIssue, checks: LintIgnoreChecks | undefined): boolean;
|
|
105
|
+
/**
|
|
106
|
+
* Drops issues that a patch owning `issue.file` (via `filesAffected`)
|
|
107
|
+
* waives. Used where findings carry no patch scope of their own (aggregate
|
|
108
|
+
* `lint`). Each owner keeps its own filter, so a ceiling stays bounded by
|
|
109
|
+
* the patch that declared it instead of merging into a file-wide union; an
|
|
110
|
+
* issue is dropped when any owner waives it.
|
|
111
|
+
*/
|
|
112
|
+
export declare function filterLintIgnoredByOwners(issues: PatchLintIssue[], owners: ReadonlyArray<{
|
|
113
|
+
lintIgnore?: LintIgnoreEntry[];
|
|
114
|
+
filesAffected: readonly string[];
|
|
115
|
+
} | null | undefined>): PatchLintIssue[];
|
|
116
|
+
/** Where a ceiling stands against the patch's current measurements. */
|
|
117
|
+
export type LintIgnoreCeilingState = 'within' | 'exceeded' | 'removable';
|
|
118
|
+
/** One ceiling's standing, as `lint --per-patch --report` records it. */
|
|
119
|
+
export interface LintIgnoreCeilingStatus {
|
|
120
|
+
rule: string;
|
|
121
|
+
max: number;
|
|
122
|
+
reason: string;
|
|
123
|
+
/** Largest current measurement for the rule, null when none was taken. */
|
|
124
|
+
measurement: number | null;
|
|
125
|
+
/**
|
|
126
|
+
* `exceeded`: a finding measured past `max` and surfaced.
|
|
127
|
+
* `removable`: the rule no longer reaches its own warning band, so the
|
|
128
|
+
* waiver changes no gate and can be dropped.
|
|
129
|
+
* `within`: the waiver is doing its job.
|
|
130
|
+
*/
|
|
131
|
+
state: LintIgnoreCeilingState;
|
|
132
|
+
}
|
|
133
|
+
/**
|
|
134
|
+
* Evaluates each ceiling on a patch against the findings its lint produced
|
|
135
|
+
* (both surfaced and suppressed).
|
|
136
|
+
*
|
|
137
|
+
* @param entries - The patch's `lintIgnore`
|
|
138
|
+
* @param issues - Every finding of the patch's lint, surfaced and suppressed
|
|
139
|
+
* @param patchMeasurements - Patch-level measurements used when a
|
|
140
|
+
* patch-size rule did not fire at all, so the report still shows the
|
|
141
|
+
* current number
|
|
142
|
+
*/
|
|
143
|
+
export declare function evaluateLintIgnoreCeilings(entries: readonly LintIgnoreEntry[] | undefined, issues: readonly PatchLintIssue[], patchMeasurements: {
|
|
144
|
+
lines: number;
|
|
145
|
+
files: number;
|
|
146
|
+
}): LintIgnoreCeilingStatus[];
|
|
147
|
+
/**
|
|
148
|
+
* Notice-severity findings for ceilings that are no longer needed, so the
|
|
149
|
+
* waiver's removal is prompted by the tool that enforces it rather than
|
|
150
|
+
* discovered by hand.
|
|
151
|
+
*/
|
|
152
|
+
export declare function removableCeilingIssues(patchFilename: string, statuses: readonly LintIgnoreCeilingStatus[]): PatchLintIssue[];
|
|
@@ -0,0 +1,299 @@
|
|
|
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
|
+
/** Narrows an entry to the ceiling form. */
|
|
28
|
+
export function isLintIgnoreCeiling(entry) {
|
|
29
|
+
return typeof entry !== 'string';
|
|
30
|
+
}
|
|
31
|
+
/** The check ID an entry names, whatever its form. */
|
|
32
|
+
export function lintIgnoreRule(entry) {
|
|
33
|
+
return isLintIgnoreCeiling(entry) ? entry.rule : entry;
|
|
34
|
+
}
|
|
35
|
+
/** One-word rendering for summaries and dry runs: `file-too-large<=1200`. */
|
|
36
|
+
export function formatLintIgnoreEntry(entry) {
|
|
37
|
+
return isLintIgnoreCeiling(entry) ? `${entry.rule}<=${String(entry.max)}` : entry;
|
|
38
|
+
}
|
|
39
|
+
/** Renders a list for messages, `(empty)` when there is nothing to show. */
|
|
40
|
+
export function formatLintIgnoreList(entries) {
|
|
41
|
+
return entries.length > 0 ? entries.map(formatLintIgnoreEntry).join(', ') : '(empty)';
|
|
42
|
+
}
|
|
43
|
+
function sameEntry(a, b) {
|
|
44
|
+
if (!isLintIgnoreCeiling(a) || !isLintIgnoreCeiling(b))
|
|
45
|
+
return a === b;
|
|
46
|
+
return a.rule === b.rule && a.max === b.max && a.reason === b.reason;
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Validates a raw `lintIgnore` value from `patches.json`.
|
|
50
|
+
*
|
|
51
|
+
* Bare strings keep their historical leniency (any ID, duplicates collapse
|
|
52
|
+
* later). A ceiling must name a measured size rule, carry a positive
|
|
53
|
+
* integer `max` and a non-empty `reason`, and must be the only entry for
|
|
54
|
+
* its rule: a bare ID beside a ceiling for the same rule would make the
|
|
55
|
+
* ceiling dead, and two ceilings would leave the effective one ambiguous.
|
|
56
|
+
*
|
|
57
|
+
* @param raw - The value under `lintIgnore`
|
|
58
|
+
* @param label - Field label for error messages, e.g. `patches[3].lintIgnore`
|
|
59
|
+
* @returns The validated entries
|
|
60
|
+
* @throws Error naming the offending element
|
|
61
|
+
*/
|
|
62
|
+
export function parseLintIgnoreEntries(raw, label) {
|
|
63
|
+
if (!Array.isArray(raw)) {
|
|
64
|
+
throw new Error(`${label} must be an array`);
|
|
65
|
+
}
|
|
66
|
+
const entries = [];
|
|
67
|
+
const ceilingRules = new Set();
|
|
68
|
+
for (const [index, item] of raw.entries()) {
|
|
69
|
+
const itemLabel = `${label}[${String(index)}]`;
|
|
70
|
+
if (typeof item === 'string') {
|
|
71
|
+
entries.push(item);
|
|
72
|
+
continue;
|
|
73
|
+
}
|
|
74
|
+
entries.push(parseCeiling(item, itemLabel));
|
|
75
|
+
}
|
|
76
|
+
for (const entry of entries) {
|
|
77
|
+
if (!isLintIgnoreCeiling(entry))
|
|
78
|
+
continue;
|
|
79
|
+
if (ceilingRules.has(entry.rule) || entries.includes(entry.rule)) {
|
|
80
|
+
throw new Error(`${label} names "${entry.rule}" more than once; keep a single entry (bare or ceiling) per rule`);
|
|
81
|
+
}
|
|
82
|
+
ceilingRules.add(entry.rule);
|
|
83
|
+
}
|
|
84
|
+
return entries;
|
|
85
|
+
}
|
|
86
|
+
function parseCeiling(item, label) {
|
|
87
|
+
if (item === null || typeof item !== 'object' || Array.isArray(item)) {
|
|
88
|
+
throw new Error(`${label} must be a check ID string or a { rule, max, reason } object`);
|
|
89
|
+
}
|
|
90
|
+
const record = item;
|
|
91
|
+
const unknownKeys = Object.keys(record).filter((key) => key !== 'rule' && key !== 'max' && key !== 'reason');
|
|
92
|
+
if (unknownKeys.length > 0) {
|
|
93
|
+
throw new Error(`${label} has unknown field(s): ${unknownKeys.join(', ')}`);
|
|
94
|
+
}
|
|
95
|
+
const { rule, max, reason } = record;
|
|
96
|
+
if (typeof rule !== 'string' || !LINT_IGNORE_CEILING_RULES.includes(rule)) {
|
|
97
|
+
throw new Error(`${label}.rule must be one of the measured size rules: ${LINT_IGNORE_CEILING_RULES.join(', ')}`);
|
|
98
|
+
}
|
|
99
|
+
if (typeof max !== 'number' || !Number.isInteger(max) || max <= 0) {
|
|
100
|
+
throw new Error(`${label}.max must be a positive integer`);
|
|
101
|
+
}
|
|
102
|
+
if (typeof reason !== 'string' || reason.trim() === '') {
|
|
103
|
+
throw new Error(`${label}.reason must be a non-empty string`);
|
|
104
|
+
}
|
|
105
|
+
return { rule, max, reason };
|
|
106
|
+
}
|
|
107
|
+
/**
|
|
108
|
+
* Builds the waiver filter for a patch's entries.
|
|
109
|
+
*
|
|
110
|
+
* @param entries - `PatchMetadata.lintIgnore`, possibly merged with CLI flags
|
|
111
|
+
* @returns The filter, or undefined when there is nothing to waive
|
|
112
|
+
*/
|
|
113
|
+
export function buildLintIgnoreChecks(entries) {
|
|
114
|
+
if (entries === undefined || entries.length === 0)
|
|
115
|
+
return undefined;
|
|
116
|
+
const plain = new Set();
|
|
117
|
+
const ceilings = new Map();
|
|
118
|
+
for (const entry of entries) {
|
|
119
|
+
if (isLintIgnoreCeiling(entry))
|
|
120
|
+
ceilings.set(entry.rule, entry);
|
|
121
|
+
else
|
|
122
|
+
plain.add(entry);
|
|
123
|
+
}
|
|
124
|
+
return ceilings.size > 0 ? Object.assign(plain, { ceilings }) : plain;
|
|
125
|
+
}
|
|
126
|
+
/**
|
|
127
|
+
* Canonical string form of a filter for the per-patch lint cache's waiver
|
|
128
|
+
* comparison: bare IDs as themselves, ceilings as `rule<=max`.
|
|
129
|
+
*/
|
|
130
|
+
export function lintIgnoreCacheTokens(checks) {
|
|
131
|
+
if (checks === undefined)
|
|
132
|
+
return [];
|
|
133
|
+
const ceilings = [...(checks.ceilings?.values() ?? [])].map(formatLintIgnoreEntry);
|
|
134
|
+
return [...checks, ...ceilings];
|
|
135
|
+
}
|
|
136
|
+
/**
|
|
137
|
+
* Unions CLI-flag check IDs into an existing list, the append-only
|
|
138
|
+
* semantics `export` / `re-export --lint-ignore` have always had. A bare ID
|
|
139
|
+
* whose rule already has an entry (bare or ceiling) is a no-op: widening a
|
|
140
|
+
* reviewed ceiling to an unbounded waiver is `patch lint-ignore`'s job, where
|
|
141
|
+
* it is confirmed explicitly.
|
|
142
|
+
*
|
|
143
|
+
* @returns The merged list, or undefined when it is empty
|
|
144
|
+
*/
|
|
145
|
+
export function mergeLintIgnoreIds(existing, ids) {
|
|
146
|
+
const merged = [];
|
|
147
|
+
const rules = new Set();
|
|
148
|
+
for (const entry of [...(existing ?? []), ...(ids ?? [])]) {
|
|
149
|
+
const rule = lintIgnoreRule(entry);
|
|
150
|
+
if (rules.has(rule))
|
|
151
|
+
continue;
|
|
152
|
+
rules.add(rule);
|
|
153
|
+
merged.push(entry);
|
|
154
|
+
}
|
|
155
|
+
return merged.length > 0 ? merged : undefined;
|
|
156
|
+
}
|
|
157
|
+
/**
|
|
158
|
+
* Adds or replaces entries by rule, the `patch lint-ignore --add` semantics:
|
|
159
|
+
* an explicit `--add <rule>` turns a ceiling into a bare waiver, and
|
|
160
|
+
* `--add <rule> --max <n>` sets or moves the ceiling.
|
|
161
|
+
*
|
|
162
|
+
* @returns The new list, or undefined when it is empty
|
|
163
|
+
*/
|
|
164
|
+
export function upsertLintIgnoreEntries(existing, additions) {
|
|
165
|
+
const next = [...existing];
|
|
166
|
+
for (const addition of additions) {
|
|
167
|
+
const rule = lintIgnoreRule(addition);
|
|
168
|
+
const index = next.findIndex((entry) => lintIgnoreRule(entry) === rule);
|
|
169
|
+
if (index === -1)
|
|
170
|
+
next.push(addition);
|
|
171
|
+
else
|
|
172
|
+
next[index] = addition;
|
|
173
|
+
}
|
|
174
|
+
return mergeLintIgnoreIds(next, undefined);
|
|
175
|
+
}
|
|
176
|
+
/** Removes every entry naming one of `rules`. Undefined when nothing is left. */
|
|
177
|
+
export function removeLintIgnoreRules(existing, rules) {
|
|
178
|
+
const drop = new Set(rules);
|
|
179
|
+
const remaining = existing.filter((entry) => !drop.has(lintIgnoreRule(entry)));
|
|
180
|
+
return remaining.length > 0 ? remaining : undefined;
|
|
181
|
+
}
|
|
182
|
+
/** Entries present in `after` but not (identically) in `before`. */
|
|
183
|
+
export function addedLintIgnoreEntries(before, after) {
|
|
184
|
+
return after.filter((entry) => !before.some((prior) => sameEntry(prior, entry)));
|
|
185
|
+
}
|
|
186
|
+
/**
|
|
187
|
+
* Splits issues into the ones that still surface and the ones a waiver
|
|
188
|
+
* drops.
|
|
189
|
+
*
|
|
190
|
+
* A bare ID drops every finding of its rule. A ceiling drops a finding only
|
|
191
|
+
* when it carries a measurement at or below `max`. Past the ceiling the
|
|
192
|
+
* finding surfaces at its own severity with the ceiling named, and a
|
|
193
|
+
* finding with no measurement surfaces too (fail closed).
|
|
194
|
+
*/
|
|
195
|
+
export function partitionLintIgnoredIssues(issues, checks) {
|
|
196
|
+
const kept = [];
|
|
197
|
+
const suppressed = [];
|
|
198
|
+
for (const issue of issues) {
|
|
199
|
+
if (checks?.has(issue.check) === true) {
|
|
200
|
+
suppressed.push(issue);
|
|
201
|
+
continue;
|
|
202
|
+
}
|
|
203
|
+
const ceiling = checks?.ceilings?.get(issue.check);
|
|
204
|
+
if (ceiling === undefined) {
|
|
205
|
+
kept.push(issue);
|
|
206
|
+
continue;
|
|
207
|
+
}
|
|
208
|
+
if (issue.measurement !== undefined && issue.measurement <= ceiling.max) {
|
|
209
|
+
suppressed.push(issue);
|
|
210
|
+
continue;
|
|
211
|
+
}
|
|
212
|
+
kept.push({ ...issue, message: `${issue.message} ${describeExceededCeiling(ceiling)}` });
|
|
213
|
+
}
|
|
214
|
+
return { kept, suppressed };
|
|
215
|
+
}
|
|
216
|
+
function describeExceededCeiling(ceiling) {
|
|
217
|
+
return (`This exceeds the patch's lintIgnore ceiling for ${ceiling.rule} ` +
|
|
218
|
+
`(max ${String(ceiling.max)}: ${ceiling.reason}).`);
|
|
219
|
+
}
|
|
220
|
+
/** True when `checks` waives `issue` (see {@link partitionLintIgnoredIssues}). */
|
|
221
|
+
export function isLintIgnored(issue, checks) {
|
|
222
|
+
return partitionLintIgnoredIssues([issue], checks).suppressed.length === 1;
|
|
223
|
+
}
|
|
224
|
+
/**
|
|
225
|
+
* Drops issues that a patch owning `issue.file` (via `filesAffected`)
|
|
226
|
+
* waives. Used where findings carry no patch scope of their own (aggregate
|
|
227
|
+
* `lint`). Each owner keeps its own filter, so a ceiling stays bounded by
|
|
228
|
+
* the patch that declared it instead of merging into a file-wide union; an
|
|
229
|
+
* issue is dropped when any owner waives it.
|
|
230
|
+
*/
|
|
231
|
+
export function filterLintIgnoredByOwners(issues, owners) {
|
|
232
|
+
const byFile = new Map();
|
|
233
|
+
for (const owner of owners) {
|
|
234
|
+
const checks = buildLintIgnoreChecks(owner?.lintIgnore);
|
|
235
|
+
if (!checks)
|
|
236
|
+
continue;
|
|
237
|
+
for (const file of owner?.filesAffected ?? []) {
|
|
238
|
+
byFile.set(file, [...(byFile.get(file) ?? []), checks]);
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
if (byFile.size === 0)
|
|
242
|
+
return issues;
|
|
243
|
+
return issues.filter((issue) => !(byFile.get(issue.file) ?? []).some((checks) => isLintIgnored(issue, checks)));
|
|
244
|
+
}
|
|
245
|
+
/**
|
|
246
|
+
* Evaluates each ceiling on a patch against the findings its lint produced
|
|
247
|
+
* (both surfaced and suppressed).
|
|
248
|
+
*
|
|
249
|
+
* @param entries - The patch's `lintIgnore`
|
|
250
|
+
* @param issues - Every finding of the patch's lint, surfaced and suppressed
|
|
251
|
+
* @param patchMeasurements - Patch-level measurements used when a
|
|
252
|
+
* patch-size rule did not fire at all, so the report still shows the
|
|
253
|
+
* current number
|
|
254
|
+
*/
|
|
255
|
+
export function evaluateLintIgnoreCeilings(entries, issues, patchMeasurements) {
|
|
256
|
+
const statuses = [];
|
|
257
|
+
for (const entry of entries ?? []) {
|
|
258
|
+
if (!isLintIgnoreCeiling(entry))
|
|
259
|
+
continue;
|
|
260
|
+
const findings = issues.filter((issue) => issue.check === entry.rule);
|
|
261
|
+
const measured = findings
|
|
262
|
+
.map((issue) => issue.measurement)
|
|
263
|
+
.filter((value) => value !== undefined);
|
|
264
|
+
const fallback = entry.rule === 'large-patch-lines'
|
|
265
|
+
? patchMeasurements.lines
|
|
266
|
+
: entry.rule === 'large-patch-files'
|
|
267
|
+
? patchMeasurements.files
|
|
268
|
+
: null;
|
|
269
|
+
const measurement = measured.length > 0 ? Math.max(...measured) : fallback;
|
|
270
|
+
const gating = findings.some((issue) => issue.severity !== 'notice');
|
|
271
|
+
const state = measured.some((value) => value > entry.max)
|
|
272
|
+
? 'exceeded'
|
|
273
|
+
: gating
|
|
274
|
+
? 'within'
|
|
275
|
+
: 'removable';
|
|
276
|
+
statuses.push({ rule: entry.rule, max: entry.max, reason: entry.reason, measurement, state });
|
|
277
|
+
}
|
|
278
|
+
return statuses;
|
|
279
|
+
}
|
|
280
|
+
/**
|
|
281
|
+
* Notice-severity findings for ceilings that are no longer needed, so the
|
|
282
|
+
* waiver's removal is prompted by the tool that enforces it rather than
|
|
283
|
+
* discovered by hand.
|
|
284
|
+
*/
|
|
285
|
+
export function removableCeilingIssues(patchFilename, statuses) {
|
|
286
|
+
return statuses
|
|
287
|
+
.filter((status) => status.state === 'removable')
|
|
288
|
+
.map((status) => {
|
|
289
|
+
const now = status.measurement === null ? 'below its notice band' : `at ${String(status.measurement)}`;
|
|
290
|
+
return {
|
|
291
|
+
file: patchFilename,
|
|
292
|
+
check: 'lint-ignore-removable',
|
|
293
|
+
message: `lintIgnore ceiling for ${status.rule} (max ${String(status.max)}) is no longer needed: ` +
|
|
294
|
+
`the measurement is ${now}, under the rule's own warning threshold. ` +
|
|
295
|
+
`Remove it with "fireforge patch lint-ignore ${patchFilename} --remove ${status.rule}".`,
|
|
296
|
+
severity: 'notice',
|
|
297
|
+
};
|
|
298
|
+
});
|
|
299
|
+
}
|
|
@@ -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
|
+
}
|