@hominis/fireforge 0.49.2 → 0.50.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +40 -1
- package/README.md +1 -1
- package/dist/bin/fireforge.js +6 -0
- package/dist/build-info.json +4 -4
- package/dist/src/commands/build-prelock.d.ts +20 -0
- package/dist/src/commands/build-prelock.js +50 -0
- package/dist/src/commands/build.d.ts +3 -1
- package/dist/src/commands/build.js +13 -6
- package/dist/src/commands/check-report.d.ts +28 -0
- package/dist/src/commands/check-report.js +78 -0
- package/dist/src/commands/check-run.d.ts +28 -0
- package/dist/src/commands/check-run.js +104 -0
- package/dist/src/commands/check-steps.d.ts +67 -0
- package/dist/src/commands/check-steps.js +175 -0
- package/dist/src/commands/check.d.ts +32 -0
- package/dist/src/commands/check.js +160 -0
- package/dist/src/commands/config.js +5 -6
- package/dist/src/commands/doctor-engine-eslint.js +6 -1
- package/dist/src/commands/doctor-source-pin.js +4 -1
- package/dist/src/commands/export-adopt-check.d.ts +38 -0
- package/dist/src/commands/export-adopt-check.js +98 -0
- package/dist/src/commands/export-flow.js +29 -14
- package/dist/src/commands/export-placement-gate.d.ts +6 -2
- package/dist/src/commands/export-shared.d.ts +2 -1
- package/dist/src/commands/export-staged-registration.d.ts +46 -0
- package/dist/src/commands/export-staged-registration.js +91 -0
- package/dist/src/commands/forward-import-remedy.d.ts +32 -0
- package/dist/src/commands/forward-import-remedy.js +63 -0
- package/dist/src/commands/furnace/apply.js +3 -3
- package/dist/src/commands/furnace/chrome-doc-remove.d.ts +2 -1
- package/dist/src/commands/furnace/chrome-doc-remove.js +2 -2
- package/dist/src/commands/furnace/chrome-doc.d.ts +2 -1
- package/dist/src/commands/furnace/chrome-doc.js +2 -2
- package/dist/src/commands/furnace/create.js +2 -2
- package/dist/src/commands/furnace/deploy-named.d.ts +48 -0
- package/dist/src/commands/furnace/deploy-named.js +107 -0
- package/dist/src/commands/furnace/deploy.d.ts +3 -2
- package/dist/src/commands/furnace/deploy.js +50 -86
- package/dist/src/commands/furnace/diff.js +26 -3
- package/dist/src/commands/furnace/index.js +66 -41
- package/dist/src/commands/furnace/init.js +13 -6
- package/dist/src/commands/furnace/override.js +4 -2
- package/dist/src/commands/furnace/preview.js +4 -3
- package/dist/src/commands/furnace/refresh.js +2 -2
- package/dist/src/commands/furnace/remove.js +4 -3
- package/dist/src/commands/furnace/rename-dry-run.d.ts +29 -0
- package/dist/src/commands/furnace/rename-dry-run.js +127 -0
- package/dist/src/commands/furnace/rename-helpers.d.ts +7 -0
- package/dist/src/commands/furnace/rename-helpers.js +15 -1
- package/dist/src/commands/furnace/rename-registrations.d.ts +41 -0
- package/dist/src/commands/furnace/rename-registrations.js +93 -0
- package/dist/src/commands/furnace/rename.d.ts +2 -1
- package/dist/src/commands/furnace/rename.js +28 -66
- package/dist/src/commands/furnace/scan.d.ts +9 -4
- package/dist/src/commands/furnace/scan.js +11 -9
- package/dist/src/commands/furnace/sync.js +3 -2
- package/dist/src/commands/furnace/validate-report.d.ts +45 -0
- package/dist/src/commands/furnace/validate-report.js +91 -0
- package/dist/src/commands/furnace/validate.d.ts +1 -1
- package/dist/src/commands/furnace/validate.js +127 -114
- package/dist/src/commands/furnace/validation-output.js +14 -3
- package/dist/src/commands/lint-per-patch.js +31 -60
- package/dist/src/commands/lint-report.d.ts +8 -4
- package/dist/src/commands/lint-report.js +15 -5
- package/dist/src/commands/lint.js +15 -34
- package/dist/src/commands/manifest.js +2 -0
- package/dist/src/commands/patch/delete-gap-notice.js +3 -1
- package/dist/src/commands/patch/delete.d.ts +1 -1
- package/dist/src/commands/patch/delete.js +13 -98
- package/dist/src/commands/patch/index.d.ts +1 -1
- package/dist/src/commands/patch/index.js +4 -2
- package/dist/src/commands/patch/lint-ignore.d.ts +5 -1
- package/dist/src/commands/patch/lint-ignore.js +65 -28
- package/dist/src/commands/patch/move-files-create.js +26 -17
- package/dist/src/commands/patch/move-files-into.js +5 -3
- package/dist/src/commands/patch/move-files.js +3 -1
- package/dist/src/commands/patch/removal-dependents.d.ts +63 -0
- package/dist/src/commands/patch/removal-dependents.js +133 -0
- package/dist/src/commands/patch/rename.js +7 -0
- package/dist/src/commands/patch/retire.d.ts +34 -0
- package/dist/src/commands/patch/retire.js +128 -0
- package/dist/src/commands/patch/split-plan.d.ts +7 -7
- package/dist/src/commands/patch/split-plan.js +45 -28
- package/dist/src/commands/patch/split-staged.d.ts +108 -0
- package/dist/src/commands/patch/split-staged.js +202 -0
- package/dist/src/commands/patch/split.d.ts +5 -0
- package/dist/src/commands/patch/split.js +51 -15
- package/dist/src/commands/patch/staged-dependency-batch.d.ts +48 -0
- package/dist/src/commands/patch/staged-dependency-batch.js +176 -0
- package/dist/src/commands/patch/staged-dependency-entries.d.ts +36 -0
- package/dist/src/commands/patch/staged-dependency-entries.js +52 -0
- package/dist/src/commands/patch/staged-dependency-from.d.ts +14 -0
- package/dist/src/commands/patch/staged-dependency-from.js +108 -0
- package/dist/src/commands/patch/staged-dependency.js +14 -41
- package/dist/src/commands/re-export-adjacent-registrations.d.ts +20 -0
- package/dist/src/commands/re-export-adjacent-registrations.js +75 -0
- package/dist/src/commands/re-export-adjacent.d.ts +45 -2
- package/dist/src/commands/re-export-adjacent.js +156 -37
- package/dist/src/commands/re-export-drift.d.ts +36 -8
- package/dist/src/commands/re-export-drift.js +73 -49
- package/dist/src/commands/re-export-files.js +11 -8
- package/dist/src/commands/re-export-lint.d.ts +2 -1
- package/dist/src/commands/re-export-lint.js +23 -10
- package/dist/src/commands/re-export-options.js +45 -4
- package/dist/src/commands/re-export-preflight.d.ts +23 -0
- package/dist/src/commands/re-export-preflight.js +160 -0
- package/dist/src/commands/re-export-register.js +42 -17
- package/dist/src/commands/re-export-removed.d.ts +12 -1
- package/dist/src/commands/re-export-removed.js +41 -11
- package/dist/src/commands/re-export-scan-owner.d.ts +44 -0
- package/dist/src/commands/re-export-scan-owner.js +116 -0
- package/dist/src/commands/re-export-scan.d.ts +17 -9
- package/dist/src/commands/re-export-scan.js +56 -23
- package/dist/src/commands/re-export-single.js +22 -6
- package/dist/src/commands/re-export-summary.d.ts +28 -0
- package/dist/src/commands/re-export-summary.js +36 -0
- package/dist/src/commands/re-export.js +37 -24
- package/dist/src/commands/rebase/index.js +17 -17
- package/dist/src/commands/run.js +16 -2
- package/dist/src/commands/status-check.d.ts +55 -16
- package/dist/src/commands/status-check.js +92 -37
- package/dist/src/commands/status-json.d.ts +22 -9
- package/dist/src/commands/status-json.js +47 -32
- package/dist/src/commands/status-output.js +3 -1
- package/dist/src/commands/status-scope.d.ts +37 -0
- package/dist/src/commands/status-scope.js +54 -0
- package/dist/src/commands/status.js +66 -23
- package/dist/src/commands/test-browser-preflight.js +19 -3
- package/dist/src/commands/test-build-furnace.d.ts +21 -0
- package/dist/src/commands/test-build-furnace.js +48 -0
- package/dist/src/commands/test-build-phase.js +47 -41
- package/dist/src/commands/test-checkjs.js +7 -0
- package/dist/src/commands/test-diagnose.js +7 -0
- package/dist/src/commands/test-modes.d.ts +16 -1
- package/dist/src/commands/test-modes.js +24 -1
- package/dist/src/commands/test-multi-verdict.d.ts +61 -0
- package/dist/src/commands/test-multi-verdict.js +111 -0
- package/dist/src/commands/test-orchestrate.d.ts +43 -0
- package/dist/src/commands/test-orchestrate.js +222 -0
- package/dist/src/commands/test-perf-scope.d.ts +29 -0
- package/dist/src/commands/test-perf-scope.js +83 -0
- package/dist/src/commands/test-prelock.d.ts +51 -0
- package/dist/src/commands/test-prelock.js +177 -0
- package/dist/src/commands/test-register.js +54 -10
- package/dist/src/commands/test-run.js +4 -1
- package/dist/src/commands/test-stale-gate.js +57 -27
- package/dist/src/commands/test-verdict.d.ts +10 -1
- package/dist/src/commands/test-verdict.js +28 -1
- package/dist/src/commands/test.d.ts +6 -1
- package/dist/src/commands/test.js +66 -60
- package/dist/src/commands/token-lint.d.ts +14 -0
- package/dist/src/commands/token-lint.js +111 -0
- package/dist/src/commands/token.js +10 -0
- package/dist/src/commands/verify-scope.d.ts +28 -0
- package/dist/src/commands/verify-scope.js +67 -0
- package/dist/src/commands/verify.d.ts +10 -2
- package/dist/src/commands/verify.js +59 -35
- package/dist/src/core/build-baseline-types.d.ts +46 -0
- package/dist/src/core/build-baseline-validate.js +19 -0
- package/dist/src/core/build-baseline.d.ts +5 -1
- package/dist/src/core/build-baseline.js +3 -1
- package/dist/src/core/build-overwrite-guard.d.ts +50 -0
- package/dist/src/core/build-overwrite-guard.js +84 -4
- package/dist/src/core/build-prepare.d.ts +7 -0
- package/dist/src/core/build-prepare.js +14 -8
- package/dist/src/core/build-run-record.d.ts +74 -0
- package/dist/src/core/build-run-record.js +136 -0
- package/dist/src/core/checkjs-support-heads.d.ts +41 -0
- package/dist/src/core/checkjs-support-heads.js +104 -0
- package/dist/src/core/config-paths.d.ts +2 -2
- package/dist/src/core/config-paths.js +8 -0
- package/dist/src/core/config-validate-check.d.ts +8 -0
- package/dist/src/core/config-validate-check.js +143 -0
- package/dist/src/core/config-validate-file-size.d.ts +9 -6
- package/dist/src/core/config-validate-file-size.js +91 -2
- package/dist/src/core/config-validate-history.d.ts +11 -0
- package/dist/src/core/config-validate-history.js +38 -0
- package/dist/src/core/config-validate-patch-policy.js +100 -0
- package/dist/src/core/config-validate-test-toolchains.js +7 -0
- package/dist/src/core/config-validate-token-scales.d.ts +6 -0
- package/dist/src/core/config-validate-token-scales.js +50 -0
- package/dist/src/core/config-validate.js +14 -5
- package/dist/src/core/config.d.ts +5 -0
- package/dist/src/core/config.js +16 -0
- package/dist/src/core/coverage-dispatch.d.ts +24 -0
- package/dist/src/core/coverage-dispatch.js +44 -0
- package/dist/src/core/coverage-extend.d.ts +70 -45
- package/dist/src/core/coverage-extend.js +199 -144
- package/dist/src/core/coverage-records.d.ts +97 -0
- package/dist/src/core/coverage-records.js +350 -0
- package/dist/src/core/destructive.d.ts +7 -4
- package/dist/src/core/destructive.js +10 -10
- package/dist/src/core/engine-session-lock.d.ts +10 -1
- package/dist/src/core/engine-session-lock.js +28 -8
- package/dist/src/core/export-moved-code-shape.d.ts +49 -11
- package/dist/src/core/export-moved-code-shape.js +135 -44
- package/dist/src/core/file-lock-queue.d.ts +67 -0
- package/dist/src/core/file-lock-queue.js +221 -0
- package/dist/src/core/file-lock.d.ts +4 -25
- package/dist/src/core/file-lock.js +105 -138
- package/dist/src/core/furnace-apply-output.d.ts +10 -0
- package/dist/src/core/furnace-apply-output.js +31 -1
- package/dist/src/core/furnace-apply.js +12 -2
- package/dist/src/core/furnace-composed-source.d.ts +76 -0
- package/dist/src/core/furnace-composed-source.js +167 -0
- package/dist/src/core/furnace-config-validate-exceptions.d.ts +9 -0
- package/dist/src/core/furnace-config-validate-exceptions.js +71 -0
- package/dist/src/core/furnace-config-validate.js +8 -0
- package/dist/src/core/furnace-css-fragments.d.ts +10 -0
- package/dist/src/core/furnace-css-fragments.js +26 -0
- package/dist/src/core/furnace-operation.d.ts +3 -3
- package/dist/src/core/furnace-registration-remove.d.ts +22 -1
- package/dist/src/core/furnace-registration-remove.js +53 -17
- package/dist/src/core/furnace-registration-rename.d.ts +26 -0
- package/dist/src/core/furnace-registration-rename.js +77 -0
- package/dist/src/core/furnace-registration.d.ts +14 -4
- package/dist/src/core/furnace-registration.js +44 -10
- package/dist/src/core/furnace-staleness.d.ts +7 -0
- package/dist/src/core/furnace-staleness.js +34 -24
- package/dist/src/core/furnace-validate-accessibility.d.ts +4 -2
- package/dist/src/core/furnace-validate-accessibility.js +10 -1
- package/dist/src/core/furnace-validate-exceptions.d.ts +40 -0
- package/dist/src/core/furnace-validate-exceptions.js +43 -0
- package/dist/src/core/furnace-validate-icon-button.d.ts +7 -0
- package/dist/src/core/furnace-validate-icon-button.js +49 -0
- package/dist/src/core/furnace-validate.js +1 -1
- package/dist/src/core/harness-port-fallback.d.ts +60 -0
- package/dist/src/core/harness-port-fallback.js +96 -0
- package/dist/src/core/history-log-file.d.ts +61 -0
- package/dist/src/core/history-log-file.js +157 -0
- package/dist/src/core/history-log.d.ts +2 -1
- package/dist/src/core/history-log.js +2 -1
- package/dist/src/core/json-preserving-edit.d.ts +22 -0
- package/dist/src/core/json-preserving-edit.js +199 -0
- package/dist/src/core/mach.js +8 -1
- package/dist/src/core/negative-control-diff.d.ts +39 -0
- package/dist/src/core/negative-control-diff.js +136 -0
- package/dist/src/core/negative-control.d.ts +138 -0
- package/dist/src/core/negative-control.js +356 -0
- package/dist/src/core/objdir-install-manifests.d.ts +73 -0
- package/dist/src/core/objdir-install-manifests.js +226 -0
- package/dist/src/core/patch-export-update.d.ts +2 -0
- package/dist/src/core/patch-export-update.js +1 -0
- package/dist/src/core/patch-export.js +3 -4
- package/dist/src/core/patch-lint-checkjs-any-import.d.ts +37 -0
- package/dist/src/core/patch-lint-checkjs-any-import.js +61 -0
- package/dist/src/core/patch-lint-checkjs.js +8 -11
- package/dist/src/core/patch-lint-code-lines.d.ts +59 -0
- package/dist/src/core/patch-lint-code-lines.js +105 -0
- package/dist/src/core/patch-lint-cross.js +12 -4
- package/dist/src/core/patch-lint-css.d.ts +8 -2
- package/dist/src/core/patch-lint-css.js +10 -4
- package/dist/src/core/patch-lint-file-size.d.ts +12 -5
- package/dist/src/core/patch-lint-file-size.js +22 -11
- package/dist/src/core/patch-lint-forward-registration-extended.d.ts +18 -0
- package/dist/src/core/patch-lint-forward-registration-extended.js +35 -10
- package/dist/src/core/patch-lint-forward-registration.js +10 -0
- package/dist/src/core/patch-lint-ignore.d.ts +152 -0
- package/dist/src/core/patch-lint-ignore.js +299 -0
- package/dist/src/core/patch-lint-size-thresholds.d.ts +52 -0
- package/dist/src/core/patch-lint-size-thresholds.js +110 -0
- package/dist/src/core/patch-lint-size.d.ts +117 -0
- package/dist/src/core/patch-lint-size.js +323 -0
- package/dist/src/core/patch-lint-staged-registration.d.ts +18 -1
- package/dist/src/core/patch-lint-staged-registration.js +20 -0
- package/dist/src/core/patch-lint-token-scale.d.ts +39 -0
- package/dist/src/core/patch-lint-token-scale.js +109 -0
- package/dist/src/core/patch-lint.d.ts +11 -83
- package/dist/src/core/patch-lint.js +64 -286
- package/dist/src/core/patch-lock.js +7 -6
- package/dist/src/core/patch-manifest-consistency.js +7 -6
- package/dist/src/core/patch-manifest-io.d.ts +6 -3
- package/dist/src/core/patch-manifest-io.js +19 -8
- package/dist/src/core/patch-manifest-validate.js +34 -2
- package/dist/src/core/patch-order-legal.d.ts +25 -0
- package/dist/src/core/patch-order-legal.js +83 -0
- package/dist/src/core/patch-policy-content.d.ts +40 -0
- package/dist/src/core/patch-policy-content.js +216 -0
- package/dist/src/core/patch-policy.d.ts +16 -3
- package/dist/src/core/patch-policy.js +16 -9
- package/dist/src/core/patch-retired.d.ts +35 -0
- package/dist/src/core/patch-retired.js +54 -0
- package/dist/src/core/patch-subset.d.ts +16 -0
- package/dist/src/core/patch-subset.js +66 -0
- package/dist/src/core/preflight-wait.d.ts +20 -0
- package/dist/src/core/preflight-wait.js +38 -0
- package/dist/src/core/rebase-from-version.d.ts +46 -0
- package/dist/src/core/rebase-from-version.js +71 -0
- package/dist/src/core/run-log.d.ts +20 -0
- package/dist/src/core/run-log.js +89 -3
- package/dist/src/core/run-profile.d.ts +7 -0
- package/dist/src/core/run-profile.js +7 -1
- package/dist/src/core/smoke-patterns.d.ts +5 -0
- package/dist/src/core/smoke-patterns.js +13 -0
- package/dist/src/core/test-harness-crash.d.ts +18 -1
- package/dist/src/core/test-harness-crash.js +6 -4
- package/dist/src/core/test-harness-signature.d.ts +17 -0
- package/dist/src/core/test-harness-signature.js +4 -0
- package/dist/src/core/test-run-completeness.d.ts +57 -0
- package/dist/src/core/test-run-completeness.js +144 -0
- package/dist/src/core/test-stale-check.d.ts +27 -1
- package/dist/src/core/test-stale-check.js +39 -49
- package/dist/src/core/test-stale-exempt.d.ts +40 -0
- package/dist/src/core/test-stale-exempt.js +70 -0
- package/dist/src/core/token-lint.d.ts +50 -0
- package/dist/src/core/token-lint.js +126 -0
- package/dist/src/core/tree-guard.js +3 -0
- package/dist/src/core/typecheck-shim.d.ts +1 -1
- package/dist/src/core/typecheck-shim.js +90 -2
- package/dist/src/index.d.ts +8 -3
- package/dist/src/index.js +3 -1
- package/dist/src/types/commands/index.d.ts +2 -2
- package/dist/src/types/commands/options.d.ts +148 -21
- package/dist/src/types/commands/patches.d.ts +75 -1
- package/dist/src/types/config.d.ts +192 -0
- package/dist/src/types/config.js +9 -0
- package/dist/src/types/furnace.d.ts +43 -1
- package/dist/src/types/furnace.js +7 -0
- package/dist/src/types/index.d.ts +1 -1
- package/dist/src/utils/options.d.ts +34 -6
- package/dist/src/utils/options.js +86 -5
- package/dist/src/utils/path-glob.d.ts +15 -0
- package/dist/src/utils/path-glob.js +47 -0
- package/dist/src/utils/regex.d.ts +15 -3
- package/dist/src/utils/regex.js +37 -16
- package/docs/configuration.md +282 -9
- package/docs/exit-codes.md +18 -1
- package/docs/furnace.md +97 -3
- package/docs/lifecycle-invariants.md +52 -19
- package/docs/machine-output.md +74 -12
- package/docs/patch-workflow.md +297 -22
- package/docs/run-logs.md +20 -1
- package/docs/testing.md +276 -20
- package/package.json +1 -1
|
@@ -2,29 +2,19 @@ import type { PatchLintIssue } from '../types/commands/index.js';
|
|
|
2
2
|
import type { FireForgeConfig, ProjectLicense } from '../types/config.js';
|
|
3
3
|
import { type CommentStyle } from './license-headers.js';
|
|
4
4
|
import { lintPatchedCss } from './patch-lint-css.js';
|
|
5
|
+
import { type LintIgnoreChecks } from './patch-lint-ignore.js';
|
|
6
|
+
import { type PatchSizeMeasurement } from './patch-lint-size.js';
|
|
5
7
|
export * from './patch-lint-cross.js';
|
|
6
8
|
export { buildModifiedFileAdditionsFromDiff, detectNewFilesInDiff } from './patch-lint-diff.js';
|
|
7
9
|
export { lintPatchedCss };
|
|
10
|
+
export { countNonBinaryDiffLines, getPatchSizeThresholds, isTestFile, lintPatchSize, measurePatchSize, type PatchSizeMeasurement, type PatchSizeTierDecision, resolvePatchSizeTier, } from './patch-lint-size.js';
|
|
11
|
+
export type { PatchSizeThresholds } from './patch-lint-size-thresholds.js';
|
|
8
12
|
/**
|
|
9
13
|
* Canonical `[check] file: message` rendering of a patch-lint issue. It is
|
|
10
14
|
* the one string shape every reporter and refusal message uses, so operators
|
|
11
15
|
* can grep one format across lint output and refusal details.
|
|
12
16
|
*/
|
|
13
17
|
export declare function formatPatchLintIssue(issue: Pick<PatchLintIssue, 'check' | 'file' | 'message'>): string;
|
|
14
|
-
/**
|
|
15
|
-
* Counts the total lines in a unified diff and the number of non-binary
|
|
16
|
-
* text lines, so binary hunks do not inflate patch size checks.
|
|
17
|
-
*
|
|
18
|
-
* Line counting agrees with `wc -l`: a trailing newline does not add a
|
|
19
|
-
* phantom empty line to either `total` or the binary-hunk accounting.
|
|
20
|
-
*
|
|
21
|
-
* @param diffContent - Raw unified diff string
|
|
22
|
-
* @returns Object with `total` line count and `textLines` (total minus binary hunk lines)
|
|
23
|
-
*/
|
|
24
|
-
export declare function countNonBinaryDiffLines(diffContent: string): {
|
|
25
|
-
total: number;
|
|
26
|
-
textLines: number;
|
|
27
|
-
};
|
|
28
18
|
/**
|
|
29
19
|
* The files the `file-too-large` rule measures for a patch: the JS files
|
|
30
20
|
* (`.js`, `.mjs`, `.sys.mjs`, …) the patch creates. A modified file is
|
|
@@ -36,12 +26,6 @@ export declare function countNonBinaryDiffLines(diffContent: string): {
|
|
|
36
26
|
* @returns Engine-relative paths the rule would size-check
|
|
37
27
|
*/
|
|
38
28
|
export declare function filesMeasuredByFileTooLarge(patchText: string): Set<string>;
|
|
39
|
-
/**
|
|
40
|
-
* Returns true if the file path looks like a test file.
|
|
41
|
-
* Matches paths containing `/test/` or filenames starting with
|
|
42
|
-
* `browser_`, `test_`, or `xpcshell_` (all `.js`).
|
|
43
|
-
*/
|
|
44
|
-
export declare function isTestFile(file: string): boolean;
|
|
45
29
|
/** True when `strippedContent` contains at least one assertion call. */
|
|
46
30
|
export declare function hasBrowserChromeAssertion(strippedContent: string): boolean;
|
|
47
31
|
/**
|
|
@@ -115,68 +99,6 @@ export declare function lintPatchedJs(repoDir: string, affectedFiles: string[],
|
|
|
115
99
|
* @returns Array of lint issues
|
|
116
100
|
*/
|
|
117
101
|
export declare function lintModificationComments(diffContent: string, config: FireForgeConfig): PatchLintIssue[];
|
|
118
|
-
/**
|
|
119
|
-
* Describes which tier `resolvePatchSizeTier` selected and why.
|
|
120
|
-
* Consumers that want to surface the tier choice to the operator
|
|
121
|
-
* (e.g. a one-line `info()` when branding thresholds kick in) read
|
|
122
|
-
* this alongside the issues array from `lintPatchSize`.
|
|
123
|
-
*/
|
|
124
|
-
export type PatchSizeTierDecision = {
|
|
125
|
-
tier: 'general';
|
|
126
|
-
} | {
|
|
127
|
-
tier: 'test';
|
|
128
|
-
} | {
|
|
129
|
-
tier: 'branding';
|
|
130
|
-
source: 'auto' | 'explicit';
|
|
131
|
-
};
|
|
132
|
-
/**
|
|
133
|
-
* Decides which `large-patch-lines` threshold tier applies to a patch.
|
|
134
|
-
* Exported so `runPatchLint` and the per-patch `lint` command can
|
|
135
|
-
* surface the tier choice to the operator *without* depending on
|
|
136
|
-
* `lintPatchSize`'s internal return shape. The rule itself stays a
|
|
137
|
-
* pure issues-array API, and the decision is computed separately for
|
|
138
|
-
* the sole purpose of reporting.
|
|
139
|
-
*
|
|
140
|
-
* Precedence: test > branding (explicit) > branding (auto) > general.
|
|
141
|
-
* The test tier beats branding because a table-driven regression test
|
|
142
|
-
* is legitimately large independent of whether the patch also claims
|
|
143
|
-
* branding shape, and the test-tier thresholds are already more
|
|
144
|
-
* permissive than branding, so "tests beat branding" is the
|
|
145
|
-
* defensive-for-tests choice.
|
|
146
|
-
*/
|
|
147
|
-
export declare function resolvePatchSizeTier(filesAffected: ReadonlyArray<string>, patchTier?: 'branding'): PatchSizeTierDecision;
|
|
148
|
-
/** Line and file-count thresholds governing one patch-size tier. */
|
|
149
|
-
export interface PatchSizeThresholds {
|
|
150
|
-
/** Added-line counts at which notice/warning/error issues fire. */
|
|
151
|
-
lines: {
|
|
152
|
-
notice: number;
|
|
153
|
-
warning: number;
|
|
154
|
-
error: number;
|
|
155
|
-
};
|
|
156
|
-
/** Maximum number of files a patch may touch before an issue fires. */
|
|
157
|
-
maxFiles: number;
|
|
158
|
-
}
|
|
159
|
-
/**
|
|
160
|
-
* Read-only view of the size thresholds a tier enforces.
|
|
161
|
-
* Public-API companion to {@link resolvePatchSizeTier} /
|
|
162
|
-
* {@link countNonBinaryDiffLines} so consumers can build waiver-freshness
|
|
163
|
-
* checks against the same numbers `large-patch-lines` /
|
|
164
|
-
* `large-patch-files` fire on instead of mirroring them.
|
|
165
|
-
*/
|
|
166
|
-
export declare function getPatchSizeThresholds(tier: 'general' | 'test' | 'branding'): PatchSizeThresholds;
|
|
167
|
-
/**
|
|
168
|
-
* Checks patch size and emits advisory warnings.
|
|
169
|
-
*
|
|
170
|
-
* @param filesAffected - Files touched by the patch
|
|
171
|
-
* @param lineCount - Non-binary line count of the unified diff
|
|
172
|
-
* @param patchTier - Optional explicit tier override declared on
|
|
173
|
-
* `PatchMetadata.tier`. When `"branding"`, forces the branding
|
|
174
|
-
* thresholds regardless of `filesAffected`. Tests still win over
|
|
175
|
-
* branding (precedence `test > branding > general`) because the
|
|
176
|
-
* test-tier thresholds are already more permissive and an all-tests
|
|
177
|
-
* patch that is also branding-shaped is vanishingly rare.
|
|
178
|
-
*/
|
|
179
|
-
export declare function lintPatchSize(filesAffected: string[], lineCount: number, patchTier?: 'branding'): PatchLintIssue[];
|
|
180
102
|
/**
|
|
181
103
|
* Checks that modified (non-new) files with a supported extension still
|
|
182
104
|
* start with a recognized license header.
|
|
@@ -204,7 +126,7 @@ export interface LintExportedPatchOptions {
|
|
|
204
126
|
* of a specific rule without reaching for the blunt `--skip-lint` hammer.
|
|
205
127
|
* Not mutated.
|
|
206
128
|
*/
|
|
207
|
-
ignoreChecks?:
|
|
129
|
+
ignoreChecks?: LintIgnoreChecks;
|
|
208
130
|
/**
|
|
209
131
|
* Explicit tier override, threaded from `PatchMetadata.tier`. When
|
|
210
132
|
* `"branding"` forces the branding thresholds on the
|
|
@@ -246,6 +168,12 @@ export interface LintExportedPatchOptions {
|
|
|
246
168
|
* and the `--report` JSON) instead of losing it entirely.
|
|
247
169
|
*/
|
|
248
170
|
onSuppressed?: (suppressed: PatchLintIssue[]) => void;
|
|
171
|
+
/**
|
|
172
|
+
* The patch's {@link measurePatchSize} result, when the caller already
|
|
173
|
+
* measured it for its own report or cache row. Absent, the size rules
|
|
174
|
+
* measure the diff themselves.
|
|
175
|
+
*/
|
|
176
|
+
patchSizeMeasurement?: PatchSizeMeasurement;
|
|
249
177
|
}
|
|
250
178
|
/**
|
|
251
179
|
* Runs all patch lint checks and returns combined issues.
|
|
@@ -5,16 +5,18 @@ import { stripJsComments } from '../utils/regex.js';
|
|
|
5
5
|
import { containsUpstreamLicenseText, getLicenseHeader, hasAnyLicenseHeader, hasAnyLicenseHeaderAnyStyle, hasUpstreamMplBlockHeader, } from './license-headers.js';
|
|
6
6
|
import { invokePatchLintCheckJs, runCheckJsTestFilesGrouped } from './patch-lint-checkjs.js';
|
|
7
7
|
import { lintChromeScriptJsDocForFile } from './patch-lint-chrome-jsdoc.js';
|
|
8
|
+
import { countCodeLines, countRawLines } from './patch-lint-code-lines.js';
|
|
8
9
|
import { lintPatchedCss } from './patch-lint-css.js';
|
|
9
10
|
import { detectNewFilesInDiff, extractAddedLinesPerFile } from './patch-lint-diff.js';
|
|
10
|
-
import { AGGREGATE_PATCH_FILE } from './patch-lint-diff-tag.js';
|
|
11
11
|
import { formatFileTooLargeMessage, resolveFileSizeThresholds } from './patch-lint-file-size.js';
|
|
12
|
+
import { partitionLintIgnoredIssues } from './patch-lint-ignore.js';
|
|
12
13
|
import { hasRelativeImport } from './patch-lint-imports.js';
|
|
13
14
|
import { validateExportJsDoc } from './patch-lint-jsdoc.js';
|
|
14
15
|
import { lintMozBuildSortedLists } from './patch-lint-mozbuild.js';
|
|
15
16
|
import { lintObserverTopics } from './patch-lint-observer.js';
|
|
16
17
|
import { resolvePatchOwnedChromeScripts, resolvePatchOwnedSysMjs, resolvePatchOwnedTestScripts, } from './patch-lint-ownership.js';
|
|
17
18
|
import { invokePatchLintPrettier } from './patch-lint-prettier.js';
|
|
19
|
+
import { isTestFile, lintPatchSize, measurePatchSize, } from './patch-lint-size.js';
|
|
18
20
|
// ---------------------------------------------------------------------------
|
|
19
21
|
// Cross-patch lint re-exports
|
|
20
22
|
// ---------------------------------------------------------------------------
|
|
@@ -30,23 +32,12 @@ export { buildModifiedFileAdditionsFromDiff, detectNewFilesInDiff } from './patc
|
|
|
30
32
|
// split as the other rule families). Re-export the imported binding so
|
|
31
33
|
// callers and tests keep importing `lintPatchedCss` from this module.
|
|
32
34
|
export { lintPatchedCss };
|
|
35
|
+
// Same split for the patch-size rules and their measurement.
|
|
36
|
+
export { countNonBinaryDiffLines, getPatchSizeThresholds, isTestFile, lintPatchSize, measurePatchSize, resolvePatchSizeTier, } from './patch-lint-size.js';
|
|
33
37
|
// ---------------------------------------------------------------------------
|
|
34
38
|
// Helpers
|
|
35
39
|
// ---------------------------------------------------------------------------
|
|
36
40
|
const JS_EXTENSIONS = ['.js', '.mjs', '.jsm'];
|
|
37
|
-
/**
|
|
38
|
-
* Counts the lines of `content` the way `wc -l` reports them: a trailing
|
|
39
|
-
* newline terminates the last line rather than starting an empty extra one.
|
|
40
|
-
* A naive `split('\n').length` over-counts by one for any content ending in
|
|
41
|
-
* `\n` (the common case), making the size rules fire one line early against
|
|
42
|
-
* the operator's own `wc -l` measurement.
|
|
43
|
-
*/
|
|
44
|
-
function countContentLines(content) {
|
|
45
|
-
const lines = content.split('\n');
|
|
46
|
-
if (lines[lines.length - 1] === '')
|
|
47
|
-
lines.pop();
|
|
48
|
-
return lines.length;
|
|
49
|
-
}
|
|
50
41
|
/**
|
|
51
42
|
* Canonical `[check] file: message` rendering of a patch-lint issue. It is
|
|
52
43
|
* the one string shape every reporter and refusal message uses, so operators
|
|
@@ -55,133 +46,6 @@ function countContentLines(content) {
|
|
|
55
46
|
export function formatPatchLintIssue(issue) {
|
|
56
47
|
return `[${issue.check}] ${issue.file}: ${issue.message}`;
|
|
57
48
|
}
|
|
58
|
-
/**
|
|
59
|
-
* Counts the total lines in a unified diff and the number of non-binary
|
|
60
|
-
* text lines, so binary hunks do not inflate patch size checks.
|
|
61
|
-
*
|
|
62
|
-
* Line counting agrees with `wc -l`: a trailing newline does not add a
|
|
63
|
-
* phantom empty line to either `total` or the binary-hunk accounting.
|
|
64
|
-
*
|
|
65
|
-
* @param diffContent - Raw unified diff string
|
|
66
|
-
* @returns Object with `total` line count and `textLines` (total minus binary hunk lines)
|
|
67
|
-
*/
|
|
68
|
-
export function countNonBinaryDiffLines(diffContent) {
|
|
69
|
-
const lines = diffContent.split('\n');
|
|
70
|
-
if (lines[lines.length - 1] === '')
|
|
71
|
-
lines.pop();
|
|
72
|
-
const total = lines.length;
|
|
73
|
-
let binaryLines = 0;
|
|
74
|
-
let inBinaryHunk = false;
|
|
75
|
-
for (const line of lines) {
|
|
76
|
-
if (line === 'GIT binary patch' || line.startsWith('GIT binary patch')) {
|
|
77
|
-
inBinaryHunk = true;
|
|
78
|
-
}
|
|
79
|
-
else if (line.startsWith('diff --git ')) {
|
|
80
|
-
inBinaryHunk = false;
|
|
81
|
-
}
|
|
82
|
-
if (inBinaryHunk) {
|
|
83
|
-
binaryLines++;
|
|
84
|
-
}
|
|
85
|
-
}
|
|
86
|
-
return { total, textLines: total - binaryLines };
|
|
87
|
-
}
|
|
88
|
-
const PATCH_LINE_THRESHOLDS = {
|
|
89
|
-
general: { notice: 800, warning: 1500, error: 3000 },
|
|
90
|
-
test: { notice: 1500, warning: 3000, error: 6000 },
|
|
91
|
-
/**
|
|
92
|
-
* Branding patches have a legitimate reason to be large: they include
|
|
93
|
-
* every locale's `brand.ftl`, copied upstream CSS/PNG assets, and the
|
|
94
|
-
* fork-specific `configure.sh` / `brand.properties` under a single
|
|
95
|
-
* `browser/branding/<name>/` subtree.
|
|
96
|
-
*
|
|
97
|
-
* Calibrated against a fresh-fork branding export, which lands around
|
|
98
|
-
* 15-16k lines (localized brand.ftl across many locales + SVG path data +
|
|
99
|
-
* copied upstream CSS). That is the minimum branding diff, so it must
|
|
100
|
-
* surface as a soft `notice` rather than a `warning`: the warning band
|
|
101
|
-
* sits above it with ~13% headroom, and the error band at roughly 2× the
|
|
102
|
-
* baseline, where non-branding work is probably bundled in.
|
|
103
|
-
*
|
|
104
|
-
* Permissive thresholds are safe because the *gate* into this tier is
|
|
105
|
-
* narrow: auto-detect requires every file under `browser/branding/` plus a
|
|
106
|
-
* tight registration allowlist, or an explicit `PatchMetadata.tier:
|
|
107
|
-
* "branding"` opt-in. A non-branding patch cannot accidentally land here.
|
|
108
|
-
*/
|
|
109
|
-
branding: { notice: 8000, warning: 18000, error: 30000 },
|
|
110
|
-
};
|
|
111
|
-
/**
|
|
112
|
-
* File-count thresholds for the `large-patch-files` rule, mirroring the tier
|
|
113
|
-
* shape of {@link PATCH_LINE_THRESHOLDS}. A single warning-only threshold
|
|
114
|
-
* per tier is intentional: file count expresses scope, not blast radius,
|
|
115
|
-
* and there is no error band that would block export.
|
|
116
|
-
*
|
|
117
|
-
* The branding tier sits well above the typical floor because branding
|
|
118
|
-
* patches inherently span many files: PNG/ICO icon assets in 7+ sizes, MSIX
|
|
119
|
-
* manifests, channel-specific configs, locale `.ftl` files, Windows/macOS
|
|
120
|
-
* launcher resources. A fresh-fork branding bundle is around 56 files, so 60
|
|
121
|
-
* leaves headroom for additional channels/locales while still firing on a
|
|
122
|
-
* genuinely bloated patch.
|
|
123
|
-
*
|
|
124
|
-
* Test tier matches general because a test-only patch rarely touches many
|
|
125
|
-
* files (a single regression test usually adds 1-3 fixtures). The elevation
|
|
126
|
-
* in {@link PATCH_LINE_THRESHOLDS.test} addresses big table-driven test
|
|
127
|
-
* bodies, not file fan-out.
|
|
128
|
-
*/
|
|
129
|
-
const PATCH_FILES_THRESHOLDS = {
|
|
130
|
-
general: 5,
|
|
131
|
-
test: 5,
|
|
132
|
-
branding: 60,
|
|
133
|
-
};
|
|
134
|
-
/**
|
|
135
|
-
* Shared remedy sentence for the over-budget findings. Both places an
|
|
136
|
-
* operator stands when a patch outgrows its budget must name the sanctioned
|
|
137
|
-
* one-transaction way out, or they reach for the wrong tools first. One
|
|
138
|
-
* constant, four emission sites, so the wording cannot drift.
|
|
139
|
-
*/
|
|
140
|
-
const SPLIT_HINT = 'Consider splitting into smaller, focused patches — ' +
|
|
141
|
-
'"fireforge patch move-files <from> <to> --file <path> --create --order <n>" ' +
|
|
142
|
-
'moves files into a new patch in one transaction.';
|
|
143
|
-
/**
|
|
144
|
-
* Fixed allowlist of non-branding sibling paths that real-world Firefox
|
|
145
|
-
* branding patches legitimately need to touch to register the new branding
|
|
146
|
-
* flavor with the top-level configure. A predicate requiring every file to
|
|
147
|
-
* live under `browser/branding/` drops a branding patch that also touches
|
|
148
|
-
* `browser/moz.configure` (the canonical registration point) into the
|
|
149
|
-
* general lint tier.
|
|
150
|
-
*
|
|
151
|
-
* Narrow on purpose: add entries only when a genuine branding patch
|
|
152
|
-
* cannot be expressed without a specific registration sibling. Pinned
|
|
153
|
-
* against ESR 140.x conventions at time of writing.
|
|
154
|
-
*/
|
|
155
|
-
const BRANDING_REGISTRATION_FILES = new Set([
|
|
156
|
-
'browser/moz.configure',
|
|
157
|
-
'browser/confvars.sh',
|
|
158
|
-
]);
|
|
159
|
-
/**
|
|
160
|
-
* Returns true when a patch qualifies for the branding threshold tier:
|
|
161
|
-
* every file lives either under `browser/branding/` or in the narrow
|
|
162
|
-
* registration allowlist, and the patch contains at least one file
|
|
163
|
-
* under `browser/branding/` (guard against a config-only patch
|
|
164
|
-
* accidentally qualifying as branding).
|
|
165
|
-
*
|
|
166
|
-
* Used by `lintPatchSize` to pick the branding threshold tier. The
|
|
167
|
-
* explicit `tier: "branding"` field on `PatchMetadata` bypasses this
|
|
168
|
-
* heuristic and forces the branding tier directly.
|
|
169
|
-
*/
|
|
170
|
-
function isBrandingOnlyPatch(files) {
|
|
171
|
-
if (files.length === 0)
|
|
172
|
-
return false;
|
|
173
|
-
let hasBrandingFile = false;
|
|
174
|
-
for (const file of files) {
|
|
175
|
-
if (file.startsWith('browser/branding/')) {
|
|
176
|
-
hasBrandingFile = true;
|
|
177
|
-
continue;
|
|
178
|
-
}
|
|
179
|
-
if (BRANDING_REGISTRATION_FILES.has(file))
|
|
180
|
-
continue;
|
|
181
|
-
return false;
|
|
182
|
-
}
|
|
183
|
-
return hasBrandingFile;
|
|
184
|
-
}
|
|
185
49
|
/**
|
|
186
50
|
* Returns true if the filename looks like a JS/MJS/JSM file.
|
|
187
51
|
* Handles `.sys.mjs` as well.
|
|
@@ -207,15 +71,57 @@ export function filesMeasuredByFileTooLarge(patchText) {
|
|
|
207
71
|
return measuredByFileTooLarge(detectNewFilesInDiff(patchText));
|
|
208
72
|
}
|
|
209
73
|
/**
|
|
210
|
-
*
|
|
211
|
-
*
|
|
212
|
-
*
|
|
74
|
+
* The `file-too-large` rule for one measured file, in the configured
|
|
75
|
+
* `patchLint.fileSizeMetric`. Under `"code"`, a file acorn cannot tokenize
|
|
76
|
+
* is measured in raw lines (the upper bound) and a `file-size-unparsed`
|
|
77
|
+
* notice names it, so the fallback is visible instead of silently lenient.
|
|
213
78
|
*/
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
79
|
+
function lintFileSize(file, content, config) {
|
|
80
|
+
const issues = [];
|
|
81
|
+
let lineCount = countRawLines(content);
|
|
82
|
+
let unit = 'lines';
|
|
83
|
+
if (config.patchLint?.fileSizeMetric === 'code') {
|
|
84
|
+
const counted = countCodeLines(content);
|
|
85
|
+
if (counted.parsed) {
|
|
86
|
+
lineCount = counted.lines;
|
|
87
|
+
unit = 'code lines';
|
|
88
|
+
}
|
|
89
|
+
else {
|
|
90
|
+
issues.push({
|
|
91
|
+
file,
|
|
92
|
+
check: 'file-size-unparsed',
|
|
93
|
+
message: `Could not parse the file as a module or a script, so file-too-large counted its ` +
|
|
94
|
+
`${lineCount} raw lines (the upper bound) instead of code lines.`,
|
|
95
|
+
severity: 'notice',
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
const isTest = isTestFile(file);
|
|
100
|
+
const thresholds = resolveFileSizeThresholds(config.patchLint?.fileSizeThresholds, isTest);
|
|
101
|
+
const band = lineCount > thresholds.error
|
|
102
|
+
? 'error'
|
|
103
|
+
: lineCount > thresholds.warning
|
|
104
|
+
? 'warning'
|
|
105
|
+
: lineCount > thresholds.notice
|
|
106
|
+
? 'notice'
|
|
107
|
+
: undefined;
|
|
108
|
+
if (band !== undefined) {
|
|
109
|
+
issues.push({
|
|
110
|
+
file,
|
|
111
|
+
check: 'file-too-large',
|
|
112
|
+
message: formatFileTooLargeMessage({
|
|
113
|
+
label: isTest ? 'Test file' : 'New file',
|
|
114
|
+
lineCount,
|
|
115
|
+
unit,
|
|
116
|
+
thresholds,
|
|
117
|
+
band,
|
|
118
|
+
verb: isTest ? 'splitting' : 'decomposing',
|
|
119
|
+
}),
|
|
120
|
+
severity: band,
|
|
121
|
+
measurement: lineCount,
|
|
122
|
+
});
|
|
123
|
+
}
|
|
124
|
+
return issues;
|
|
219
125
|
}
|
|
220
126
|
/**
|
|
221
127
|
* Narrower scope than `isTestFile`: only browser-chrome test files
|
|
@@ -424,26 +330,7 @@ export async function lintPatchedJs(repoDir, affectedFiles, newFiles, config, pa
|
|
|
424
330
|
}
|
|
425
331
|
// 2. File size check (new JS files only; see filesMeasuredByFileTooLarge)
|
|
426
332
|
if (sizeMeasured.has(file)) {
|
|
427
|
-
|
|
428
|
-
const isTest = isTestFile(file);
|
|
429
|
-
const thresholds = resolveFileSizeThresholds(config.patchLint?.fileSizeThresholds, isTest);
|
|
430
|
-
const label = isTest ? 'Test file' : 'New file';
|
|
431
|
-
const verb = isTest ? 'splitting' : 'decomposing';
|
|
432
|
-
const band = lineCount > thresholds.error
|
|
433
|
-
? 'error'
|
|
434
|
-
: lineCount > thresholds.warning
|
|
435
|
-
? 'warning'
|
|
436
|
-
: lineCount > thresholds.notice
|
|
437
|
-
? 'notice'
|
|
438
|
-
: undefined;
|
|
439
|
-
if (band !== undefined) {
|
|
440
|
-
issues.push({
|
|
441
|
-
file,
|
|
442
|
-
check: 'file-too-large',
|
|
443
|
-
message: formatFileTooLargeMessage({ label, lineCount, thresholds, band, verb }),
|
|
444
|
-
severity: band,
|
|
445
|
-
});
|
|
446
|
-
}
|
|
333
|
+
issues.push(...lintFileSize(file, content, config));
|
|
447
334
|
}
|
|
448
335
|
// 3. JSDoc on exports (patch-owned .sys.mjs files)
|
|
449
336
|
const isOwned = patchOwnedFiles ? patchOwnedFiles.has(file) : isNew;
|
|
@@ -522,114 +409,6 @@ export function lintModificationComments(diffContent, config) {
|
|
|
522
409
|
}
|
|
523
410
|
return issues;
|
|
524
411
|
}
|
|
525
|
-
/**
|
|
526
|
-
* Decides which `large-patch-lines` threshold tier applies to a patch.
|
|
527
|
-
* Exported so `runPatchLint` and the per-patch `lint` command can
|
|
528
|
-
* surface the tier choice to the operator *without* depending on
|
|
529
|
-
* `lintPatchSize`'s internal return shape. The rule itself stays a
|
|
530
|
-
* pure issues-array API, and the decision is computed separately for
|
|
531
|
-
* the sole purpose of reporting.
|
|
532
|
-
*
|
|
533
|
-
* Precedence: test > branding (explicit) > branding (auto) > general.
|
|
534
|
-
* The test tier beats branding because a table-driven regression test
|
|
535
|
-
* is legitimately large independent of whether the patch also claims
|
|
536
|
-
* branding shape, and the test-tier thresholds are already more
|
|
537
|
-
* permissive than branding, so "tests beat branding" is the
|
|
538
|
-
* defensive-for-tests choice.
|
|
539
|
-
*/
|
|
540
|
-
export function resolvePatchSizeTier(filesAffected, patchTier) {
|
|
541
|
-
const allTests = filesAffected.length > 0 && filesAffected.every(isTestFile);
|
|
542
|
-
if (allTests)
|
|
543
|
-
return { tier: 'test' };
|
|
544
|
-
if (patchTier === 'branding')
|
|
545
|
-
return { tier: 'branding', source: 'explicit' };
|
|
546
|
-
if (isBrandingOnlyPatch(filesAffected))
|
|
547
|
-
return { tier: 'branding', source: 'auto' };
|
|
548
|
-
return { tier: 'general' };
|
|
549
|
-
}
|
|
550
|
-
/**
|
|
551
|
-
* Read-only view of the size thresholds a tier enforces.
|
|
552
|
-
* Public-API companion to {@link resolvePatchSizeTier} /
|
|
553
|
-
* {@link countNonBinaryDiffLines} so consumers can build waiver-freshness
|
|
554
|
-
* checks against the same numbers `large-patch-lines` /
|
|
555
|
-
* `large-patch-files` fire on instead of mirroring them.
|
|
556
|
-
*/
|
|
557
|
-
export function getPatchSizeThresholds(tier) {
|
|
558
|
-
return {
|
|
559
|
-
lines: { ...PATCH_LINE_THRESHOLDS[tier] },
|
|
560
|
-
maxFiles: PATCH_FILES_THRESHOLDS[tier],
|
|
561
|
-
};
|
|
562
|
-
}
|
|
563
|
-
/**
|
|
564
|
-
* Checks patch size and emits advisory warnings.
|
|
565
|
-
*
|
|
566
|
-
* @param filesAffected - Files touched by the patch
|
|
567
|
-
* @param lineCount - Non-binary line count of the unified diff
|
|
568
|
-
* @param patchTier - Optional explicit tier override declared on
|
|
569
|
-
* `PatchMetadata.tier`. When `"branding"`, forces the branding
|
|
570
|
-
* thresholds regardless of `filesAffected`. Tests still win over
|
|
571
|
-
* branding (precedence `test > branding > general`) because the
|
|
572
|
-
* test-tier thresholds are already more permissive and an all-tests
|
|
573
|
-
* patch that is also branding-shaped is vanishingly rare.
|
|
574
|
-
*/
|
|
575
|
-
export function lintPatchSize(filesAffected, lineCount, patchTier) {
|
|
576
|
-
const issues = [];
|
|
577
|
-
// Tier selection: test > branding > general. Tests keep their elevated
|
|
578
|
-
// thresholds because a big regression test is legitimate (table-driven
|
|
579
|
-
// harnesses run into the thousands of lines). Branding patches get their
|
|
580
|
-
// own tier so a first export of setup-generated branding does not fire the
|
|
581
|
-
// general hard limit. See `PATCH_LINE_THRESHOLDS.branding` and
|
|
582
|
-
// `PATCH_FILES_THRESHOLDS.branding` above. An explicit `patchTier` opt-in
|
|
583
|
-
// forces branding even when `isBrandingOnlyPatch` cannot reach the patch's
|
|
584
|
-
// actual shape (a branding patch that also touches a non-allowlisted
|
|
585
|
-
// sibling like a vendor-specific icon resource). Both checks read off the
|
|
586
|
-
// same decision so the file-count and line-count rules cannot disagree
|
|
587
|
-
// about which tier applies.
|
|
588
|
-
const decision = resolvePatchSizeTier(filesAffected, patchTier);
|
|
589
|
-
const fileThreshold = decision.tier === 'test'
|
|
590
|
-
? PATCH_FILES_THRESHOLDS.test
|
|
591
|
-
: decision.tier === 'branding'
|
|
592
|
-
? PATCH_FILES_THRESHOLDS.branding
|
|
593
|
-
: PATCH_FILES_THRESHOLDS.general;
|
|
594
|
-
const lineThresholds = decision.tier === 'test'
|
|
595
|
-
? PATCH_LINE_THRESHOLDS.test
|
|
596
|
-
: decision.tier === 'branding'
|
|
597
|
-
? PATCH_LINE_THRESHOLDS.branding
|
|
598
|
-
: PATCH_LINE_THRESHOLDS.general;
|
|
599
|
-
if (filesAffected.length > fileThreshold) {
|
|
600
|
-
issues.push({
|
|
601
|
-
file: AGGREGATE_PATCH_FILE,
|
|
602
|
-
check: 'large-patch-files',
|
|
603
|
-
message: `Patch affects ${filesAffected.length} files (recommended: ≤${fileThreshold}). ${SPLIT_HINT}`,
|
|
604
|
-
severity: 'warning',
|
|
605
|
-
});
|
|
606
|
-
}
|
|
607
|
-
if (lineCount > lineThresholds.error) {
|
|
608
|
-
issues.push({
|
|
609
|
-
file: AGGREGATE_PATCH_FILE,
|
|
610
|
-
check: 'large-patch-lines',
|
|
611
|
-
message: `Patch is ${lineCount} lines (hard limit: ${lineThresholds.error}). ${SPLIT_HINT}`,
|
|
612
|
-
severity: 'error',
|
|
613
|
-
});
|
|
614
|
-
}
|
|
615
|
-
else if (lineCount > lineThresholds.warning) {
|
|
616
|
-
issues.push({
|
|
617
|
-
file: AGGREGATE_PATCH_FILE,
|
|
618
|
-
check: 'large-patch-lines',
|
|
619
|
-
message: `Patch is ${lineCount} lines (soft limit: ${lineThresholds.warning}, hard limit: ${lineThresholds.error}). ${SPLIT_HINT}`,
|
|
620
|
-
severity: 'warning',
|
|
621
|
-
});
|
|
622
|
-
}
|
|
623
|
-
else if (lineCount > lineThresholds.notice) {
|
|
624
|
-
issues.push({
|
|
625
|
-
file: AGGREGATE_PATCH_FILE,
|
|
626
|
-
check: 'large-patch-lines',
|
|
627
|
-
message: `Patch is ${lineCount} lines (soft limit: ${lineThresholds.warning}, hard limit: ${lineThresholds.error}). ${SPLIT_HINT}`,
|
|
628
|
-
severity: 'notice',
|
|
629
|
-
});
|
|
630
|
-
}
|
|
631
|
-
return issues;
|
|
632
|
-
}
|
|
633
412
|
// ---------------------------------------------------------------------------
|
|
634
413
|
// Modified file header lint
|
|
635
414
|
// ---------------------------------------------------------------------------
|
|
@@ -683,7 +462,6 @@ export async function lintModifiedFileHeaders(repoDir, affectedFiles, newFiles)
|
|
|
683
462
|
export async function lintExportedPatch(repoDir, affectedFiles, diffContent, config, options) {
|
|
684
463
|
const { patchQueueCtx, ignoreChecks, patchTier } = options ?? {};
|
|
685
464
|
const newFiles = detectNewFilesInDiff(diffContent);
|
|
686
|
-
const { textLines: lineCount } = countNonBinaryDiffLines(diffContent);
|
|
687
465
|
const patchOwnedFiles = resolvePatchOwnedSysMjs(newFiles, patchQueueCtx);
|
|
688
466
|
const patchOwnedChromeScripts = resolvePatchOwnedChromeScripts(newFiles, patchQueueCtx);
|
|
689
467
|
const [cssIssues, headerIssues, jsIssues, modifiedHeaderIssues, mozBuildIssues] = await Promise.all([
|
|
@@ -696,7 +474,8 @@ export async function lintExportedPatch(repoDir, affectedFiles, diffContent, con
|
|
|
696
474
|
const modCommentIssues = lintModificationComments(diffContent, config);
|
|
697
475
|
const sizeIssues = options?.skipPatchSize
|
|
698
476
|
? []
|
|
699
|
-
: lintPatchSize(affectedFiles,
|
|
477
|
+
: lintPatchSize(affectedFiles, options?.patchSizeMeasurement ??
|
|
478
|
+
(await measurePatchSize(repoDir, diffContent, affectedFiles, config)), patchTier, config);
|
|
700
479
|
const issues = [
|
|
701
480
|
...sizeIssues,
|
|
702
481
|
...cssIssues,
|
|
@@ -740,12 +519,11 @@ export async function lintExportedPatch(repoDir, affectedFiles, diffContent, con
|
|
|
740
519
|
// reclassify severities: an ignored error simply drops, mirroring how
|
|
741
520
|
// inline `fireforge-ignore: <check>` markers work in the CSS and
|
|
742
521
|
// forward-import rules.
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
return issues.filter((issue) => !ignoreChecks.has(issue.check));
|
|
522
|
+
// A ceiling entry (see `patch-lint-ignore.ts`) drops a size finding only
|
|
523
|
+
// while its measurement stays at or below the ceiling.
|
|
524
|
+
const { kept, suppressed } = partitionLintIgnoredIssues(issues, ignoreChecks);
|
|
525
|
+
if (suppressed.length > 0) {
|
|
526
|
+
options?.onSuppressed?.(suppressed);
|
|
749
527
|
}
|
|
750
|
-
return
|
|
528
|
+
return kept;
|
|
751
529
|
}
|
|
@@ -8,20 +8,21 @@ import { PatchError } from '../errors/patch.js';
|
|
|
8
8
|
import { assert } from '../utils/assert.js';
|
|
9
9
|
import { toError } from '../utils/errors.js';
|
|
10
10
|
import { info } from '../utils/logger.js';
|
|
11
|
-
import { readLockStatus, withFileLock } from './file-lock.js';
|
|
11
|
+
import { formatLockQueuePosition, readLockStatus, withFileLock } from './file-lock.js';
|
|
12
12
|
const PATCH_DIRECTORY_LOCK = '.fireforge-patches.lock';
|
|
13
13
|
/**
|
|
14
14
|
* Formats the periodic operator-facing waiting line printed while
|
|
15
15
|
* `--wait-lock` polls a contended patch directory lock. Mirrors the engine
|
|
16
16
|
* session lock's progress line so operators read one shape for both locks.
|
|
17
17
|
*/
|
|
18
|
-
function formatWaitProgressLine(waitedMs, timeoutMs, holder) {
|
|
18
|
+
function formatWaitProgressLine(waitedMs, timeoutMs, holder, queue) {
|
|
19
19
|
const progress = `${Math.round(waitedMs / 1000)}s of up to ${Math.round(timeoutMs / 1000)}s`;
|
|
20
|
+
const position = formatLockQueuePosition(queue);
|
|
20
21
|
if (holder === undefined) {
|
|
21
|
-
return `Waiting for the FireForge patch directory lock — ${progress}
|
|
22
|
+
return `Waiting for the FireForge patch directory lock — ${progress}.${position}`;
|
|
22
23
|
}
|
|
23
24
|
const details = holder.metadata.length > 0 ? ` (${holder.metadata.join(', ')})` : '';
|
|
24
|
-
return `Waiting for the FireForge patch directory lock held by PID ${holder.pid}${details} — ${progress}
|
|
25
|
+
return `Waiting for the FireForge patch directory lock held by PID ${holder.pid}${details} — ${progress}.${position}`;
|
|
25
26
|
}
|
|
26
27
|
/**
|
|
27
28
|
* Asserts that this process is inside {@link withPatchDirectoryLock} for the
|
|
@@ -80,8 +81,8 @@ export async function withPatchDirectoryLock(patchesDir, operation, options = {}
|
|
|
80
81
|
pollMs: 100,
|
|
81
82
|
pollMaxMs: 2000,
|
|
82
83
|
waitProgressMs: 5000,
|
|
83
|
-
onWaitProgress: ({ waitedMs, timeoutMs, holder }) => {
|
|
84
|
-
info(formatWaitProgressLine(waitedMs, timeoutMs, holder));
|
|
84
|
+
onWaitProgress: ({ waitedMs, timeoutMs, holder, queue }) => {
|
|
85
|
+
info(formatWaitProgressLine(waitedMs, timeoutMs, holder, queue));
|
|
85
86
|
},
|
|
86
87
|
}
|
|
87
88
|
: {}),
|
|
@@ -8,6 +8,7 @@ import { discoverPatches, getAllTargetFilesFromPatch } from './patch-files.js';
|
|
|
8
8
|
import { withPatchDirectoryLock } from './patch-lock.js';
|
|
9
9
|
import { loadPatchesManifestState, PATCHES_MANIFEST, savePatchesManifest, } from './patch-manifest-io.js';
|
|
10
10
|
import { inferPatchMetadataFromFilename } from './patch-manifest-validate.js';
|
|
11
|
+
import { manifestWithRetiredSlots } from './patch-retired.js';
|
|
11
12
|
/**
|
|
12
13
|
* Validates that patches.json and the patch directory describe the same patch set.
|
|
13
14
|
* @param patchesDir - Path to the patches directory
|
|
@@ -216,16 +217,16 @@ async function rebuildPatchesManifestUnderLock(patchesDir, fallbackSourceEsrVers
|
|
|
216
217
|
};
|
|
217
218
|
// Copy the arrays rather than aliasing the loaded manifest's, so a caller
|
|
218
219
|
// mutating the rebuilt entry cannot reach back into `manifestState`.
|
|
219
|
-
if (existing?.lintIgnore !== undefined)
|
|
220
|
-
rebuilt.lintIgnore =
|
|
220
|
+
if (existing?.lintIgnore !== undefined) {
|
|
221
|
+
rebuilt.lintIgnore = existing.lintIgnore.map((entry) => typeof entry === 'string' ? entry : { ...entry });
|
|
222
|
+
}
|
|
221
223
|
rebuiltPatches.push(rebuilt);
|
|
222
224
|
existingEntries.delete(patch.filename);
|
|
223
225
|
}
|
|
224
226
|
rebuiltPatches.sort((left, right) => left.order - right.order || left.filename.localeCompare(right.filename));
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
};
|
|
227
|
+
// Retired slots have no patch file to recover them from, so they ride
|
|
228
|
+
// along from the loaded manifest like the per-row fields above.
|
|
229
|
+
const rebuiltManifest = manifestWithRetiredSlots(manifestState.manifest, rebuiltPatches);
|
|
229
230
|
// Whatever is left in the map had a manifest row but no patch file, so the
|
|
230
231
|
// rebuild drops it. That is the correct outcome, since the file is the
|
|
231
232
|
// source of truth, but dropping rows without naming them is
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
* inconsistent manifests.
|
|
8
8
|
*/
|
|
9
9
|
import { FireForgeError } from '../errors/base.js';
|
|
10
|
-
import type { PatchesManifest, PatchMetadata } from '../types/commands/index.js';
|
|
10
|
+
import type { PatchesManifest, PatchMetadata, PatchRetiredSlot } from '../types/commands/index.js';
|
|
11
11
|
import type { JsonObject } from '../types/json.js';
|
|
12
12
|
/** Filename for the patches manifest */
|
|
13
13
|
export declare const PATCHES_MANIFEST = "patches.json";
|
|
@@ -89,10 +89,11 @@ export declare function addPatchToManifest(patchesDir: string, metadata: PatchMe
|
|
|
89
89
|
*
|
|
90
90
|
* @param patchesDir - Path to the patches directory
|
|
91
91
|
* @param filename - Filename of the patch to remove from the manifest
|
|
92
|
+
* @param retiredSlot - Optional placeholder to record for the removed order
|
|
92
93
|
* @returns True when the manifest was written (i.e. an entry was removed),
|
|
93
94
|
* false when no matching entry existed
|
|
94
95
|
*/
|
|
95
|
-
export declare function removePatchFromManifest(patchesDir: string, filename: string): Promise<boolean>;
|
|
96
|
+
export declare function removePatchFromManifest(patchesDir: string, filename: string, retiredSlot?: PatchRetiredSlot): Promise<boolean>;
|
|
96
97
|
/**
|
|
97
98
|
* A single rename step in a {@link renumberPatchesInManifest} plan.
|
|
98
99
|
*/
|
|
@@ -188,5 +189,7 @@ export declare class PatchDeleteRollbackError extends FireForgeError {
|
|
|
188
189
|
*
|
|
189
190
|
* @param patchesDir - Path to the patches directory
|
|
190
191
|
* @param filename - Patch filename to delete
|
|
192
|
+
* @param retiredSlot - When set (`patch retire`), the placeholder written in
|
|
193
|
+
* the same manifest update that removes the row
|
|
191
194
|
*/
|
|
192
|
-
export declare function removePatchFileAndManifest(patchesDir: string, filename: string): Promise<void>;
|
|
195
|
+
export declare function removePatchFileAndManifest(patchesDir: string, filename: string, retiredSlot?: PatchRetiredSlot): Promise<void>;
|