@hominis/fireforge 0.45.0 → 0.46.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 +80 -0
- package/dist/bin/fireforge.js +6 -0
- package/dist/build-info.json +4 -4
- package/dist/src/cli-usage-error.d.ts +46 -0
- package/dist/src/cli-usage-error.js +81 -0
- package/dist/src/cli.d.ts +5 -1
- package/dist/src/cli.js +20 -21
- package/dist/src/commands/bootstrap.js +1 -2
- package/dist/src/commands/build.js +15 -6
- package/dist/src/commands/destructive-decision.d.ts +19 -0
- package/dist/src/commands/destructive-decision.js +31 -0
- package/dist/src/commands/doctor-check-core.d.ts +0 -6
- package/dist/src/commands/doctor-check-core.js +0 -8
- package/dist/src/commands/doctor-orphaned-harness.d.ts +0 -6
- package/dist/src/commands/doctor-orphaned-harness.js +4 -12
- package/dist/src/commands/doctor-post-rebase-audit.js +1 -1
- package/dist/src/commands/doctor.d.ts +2 -0
- package/dist/src/commands/doctor.js +7 -8
- package/dist/src/commands/download.js +35 -24
- package/dist/src/commands/export-all.js +21 -4
- package/dist/src/commands/export-flow.js +15 -45
- package/dist/src/commands/export-placement-conflicts.js +3 -3
- package/dist/src/commands/export-placement-gate.js +2 -8
- package/dist/src/commands/export-placement-policy.js +3 -5
- package/dist/src/commands/export-shared.d.ts +42 -23
- package/dist/src/commands/export-shared.js +4 -19
- package/dist/src/commands/export.js +10 -1
- package/dist/src/commands/furnace/authoring-config.d.ts +13 -0
- package/dist/src/commands/furnace/authoring-config.js +19 -0
- package/dist/src/commands/furnace/chrome-doc-remove.js +1 -1
- package/dist/src/commands/furnace/create-browser-test.js +3 -8
- package/dist/src/commands/furnace/create-dry-run.js +2 -5
- package/dist/src/commands/furnace/create-templates.d.ts +17 -1
- package/dist/src/commands/furnace/create-templates.js +2 -1
- package/dist/src/commands/furnace/create.js +25 -19
- package/dist/src/commands/furnace/deploy.js +11 -2
- package/dist/src/commands/furnace/init.js +2 -1
- package/dist/src/commands/furnace/override.js +28 -19
- package/dist/src/commands/furnace/refresh.js +9 -1
- package/dist/src/commands/furnace/remove-mutations.d.ts +6 -7
- package/dist/src/commands/furnace/remove-mutations.js +0 -1
- package/dist/src/commands/furnace/remove.js +2 -6
- package/dist/src/commands/furnace/rename-browser-test.js +1 -7
- package/dist/src/commands/furnace/rename.js +29 -29
- package/dist/src/commands/furnace/status.js +12 -9
- package/dist/src/commands/furnace/test-file-name.d.ts +28 -0
- package/dist/src/commands/furnace/test-file-name.js +39 -0
- package/dist/src/commands/furnace/validation-output.d.ts +21 -9
- package/dist/src/commands/furnace/validation-output.js +3 -8
- package/dist/src/commands/import.js +2 -9
- package/dist/src/commands/lint-per-patch.d.ts +2 -2
- package/dist/src/commands/lint-per-patch.js +19 -4
- package/dist/src/commands/lint-per-run-checkjs.d.ts +2 -2
- package/dist/src/commands/lint.d.ts +2 -1
- package/dist/src/commands/lint.js +1 -1
- package/dist/src/commands/manifest.js +1 -1
- package/dist/src/commands/package.js +2 -4
- package/dist/src/commands/patch/compact.js +14 -15
- package/dist/src/commands/patch/delete.js +15 -25
- package/dist/src/commands/patch/entry-projection.d.ts +13 -0
- package/dist/src/commands/patch/entry-projection.js +24 -0
- package/dist/src/commands/patch/lint-ignore.d.ts +0 -8
- package/dist/src/commands/patch/lint-ignore.js +19 -29
- package/dist/src/commands/patch/move-files-create.js +24 -10
- package/dist/src/commands/patch/move-files-into.js +38 -34
- package/dist/src/commands/patch/move-files.js +4 -13
- package/dist/src/commands/patch/rename.js +27 -41
- package/dist/src/commands/patch/reorder.js +25 -38
- package/dist/src/commands/patch/split-plan.d.ts +4 -5
- package/dist/src/commands/patch/split-plan.js +4 -8
- package/dist/src/commands/patch/split.js +45 -43
- package/dist/src/commands/patch/staged-dependency.js +16 -22
- package/dist/src/commands/patch/tier.js +15 -25
- package/dist/src/commands/patch-rename-map.d.ts +19 -0
- package/dist/src/commands/patch-rename-map.js +36 -0
- package/dist/src/commands/pending-resolution.d.ts +15 -0
- package/dist/src/commands/pending-resolution.js +25 -0
- package/dist/src/commands/re-export-adjacent.d.ts +24 -2
- package/dist/src/commands/re-export-adjacent.js +32 -16
- package/dist/src/commands/re-export-bulk-scan.js +6 -10
- package/dist/src/commands/re-export-drift.js +4 -4
- package/dist/src/commands/re-export-files.d.ts +2 -3
- package/dist/src/commands/re-export-files.js +38 -29
- package/dist/src/commands/re-export-lint.d.ts +3 -4
- package/dist/src/commands/re-export-lint.js +9 -2
- package/dist/src/commands/re-export-scan.js +5 -2
- package/dist/src/commands/re-export-single.d.ts +22 -3
- package/dist/src/commands/re-export-single.js +19 -11
- package/dist/src/commands/re-export.js +28 -11
- package/dist/src/commands/rebase/abort.js +3 -8
- package/dist/src/commands/rebase/continue.js +12 -12
- package/dist/src/commands/rebase/index.js +2 -2
- package/dist/src/commands/rebase/patch-loop.d.ts +2 -2
- package/dist/src/commands/rebase/patch-loop.js +2 -7
- package/dist/src/commands/resolve.js +18 -10
- package/dist/src/commands/run.d.ts +0 -13
- package/dist/src/commands/run.js +12 -25
- package/dist/src/commands/setup-support.d.ts +16 -4
- package/dist/src/commands/setup-support.js +16 -16
- package/dist/src/commands/setup.js +3 -9
- package/dist/src/commands/source.js +8 -1
- package/dist/src/commands/status-check.js +2 -2
- package/dist/src/commands/status-output.js +2 -2
- package/dist/src/commands/status.js +3 -4
- package/dist/src/commands/test-build-phase.d.ts +2 -2
- package/dist/src/commands/test-build-phase.js +31 -7
- package/dist/src/commands/test-modes.d.ts +2 -2
- package/dist/src/commands/test-modes.js +0 -1
- package/dist/src/commands/test-register.js +5 -3
- package/dist/src/commands/test-run.js +25 -18
- package/dist/src/commands/test-stale-gate.js +4 -4
- package/dist/src/commands/test-verdict.d.ts +16 -2
- package/dist/src/commands/test-verdict.js +18 -3
- package/dist/src/commands/test.js +42 -5
- package/dist/src/commands/token-list.d.ts +26 -0
- package/dist/src/commands/token-list.js +153 -0
- package/dist/src/commands/token.js +17 -1
- package/dist/src/commands/tree.js +7 -2
- package/dist/src/commands/typecheck.js +9 -9
- package/dist/src/commands/wire.js +4 -3
- package/dist/src/core/branding.d.ts +0 -7
- package/dist/src/core/branding.js +3 -2
- package/dist/src/core/browser-wire.js +6 -3
- package/dist/src/core/build-audit-platform.d.ts +29 -0
- package/dist/src/core/build-audit-platform.js +140 -0
- package/dist/src/core/build-audit-registration.js +3 -2
- package/dist/src/core/build-audit-skip.d.ts +57 -0
- package/dist/src/core/build-audit-skip.js +112 -0
- package/dist/src/core/build-audit.d.ts +15 -2
- package/dist/src/core/build-audit.js +110 -10
- package/dist/src/core/build-baseline.d.ts +45 -28
- package/dist/src/core/build-baseline.js +6 -29
- package/dist/src/core/build-jar-escalation.js +3 -2
- package/dist/src/core/build-overwrite-guard.js +1 -1
- package/dist/src/core/build-prepare.d.ts +0 -8
- package/dist/src/core/build-prepare.js +7 -12
- package/dist/src/core/config-paths.d.ts +2 -6
- package/dist/src/core/config-paths.js +4 -2
- package/dist/src/core/config-state.d.ts +0 -6
- package/dist/src/core/config-state.js +1 -19
- package/dist/src/core/config-validate-build-audit.d.ts +8 -0
- package/dist/src/core/config-validate-build-audit.js +80 -0
- package/dist/src/core/config-validate-file-size.js +1 -1
- package/dist/src/core/config-validate-patch-policy.d.ts +2 -2
- package/dist/src/core/config-validate-patch-policy.js +3 -3
- package/dist/src/core/config-validate-test-toolchains.d.ts +2 -2
- package/dist/src/core/config-validate-test-toolchains.js +4 -5
- package/dist/src/core/config-validate.js +14 -3
- package/dist/src/core/config.d.ts +2 -2
- package/dist/src/core/config.js +11 -15
- package/dist/src/core/coverage-extend.js +4 -4
- package/dist/src/core/discard-baseline.js +7 -7
- package/dist/src/core/engine-precondition.d.ts +1 -9
- package/dist/src/core/engine-precondition.js +3 -8
- package/dist/src/core/engine-session-lock.js +13 -17
- package/dist/src/core/export-moved-code-shape.js +2 -2
- package/dist/src/core/file-lock-owner.d.ts +103 -0
- package/dist/src/core/file-lock-owner.js +259 -0
- package/dist/src/core/file-lock.d.ts +2 -7
- package/dist/src/core/file-lock.js +40 -91
- package/dist/src/core/firefox-cache.d.ts +13 -1
- package/dist/src/core/firefox-cache.js +59 -34
- package/dist/src/core/firefox-download.d.ts +15 -0
- package/dist/src/core/firefox-download.js +37 -21
- package/dist/src/core/firefox-extract.d.ts +6 -5
- package/dist/src/core/firefox-extract.js +36 -18
- package/dist/src/core/firefox.d.ts +30 -32
- package/dist/src/core/firefox.js +5 -36
- package/dist/src/core/furnace-apply-dry-run.d.ts +1 -6
- package/dist/src/core/furnace-apply-dry-run.js +1 -7
- package/dist/src/core/furnace-apply-helpers.d.ts +0 -11
- package/dist/src/core/furnace-apply-helpers.js +8 -19
- package/dist/src/core/furnace-apply-overwrite-warn.js +4 -2
- package/dist/src/core/furnace-apply.d.ts +1 -1
- package/dist/src/core/furnace-apply.js +30 -10
- package/dist/src/core/furnace-config.d.ts +1 -1
- package/dist/src/core/furnace-config.js +23 -36
- package/dist/src/core/furnace-constants.js +3 -2
- package/dist/src/core/furnace-dir-entry.d.ts +20 -0
- package/dist/src/core/furnace-dir-entry.js +21 -0
- package/dist/src/core/furnace-operation.d.ts +2 -0
- package/dist/src/core/furnace-operation.js +3 -1
- package/dist/src/core/furnace-refresh.d.ts +18 -6
- package/dist/src/core/furnace-refresh.js +3 -6
- package/dist/src/core/furnace-registration-ast.d.ts +0 -1
- package/dist/src/core/furnace-registration-ast.js +0 -3
- package/dist/src/core/furnace-registration.d.ts +2 -1
- package/dist/src/core/furnace-registration.js +22 -29
- package/dist/src/core/furnace-scanner.js +3 -6
- package/dist/src/core/furnace-state-persist.d.ts +2 -2
- package/dist/src/core/furnace-validate-registration.js +5 -7
- package/dist/src/core/git-base.d.ts +3 -1
- package/dist/src/core/git-base.js +25 -3
- package/dist/src/core/git-diff-new-file.d.ts +40 -0
- package/dist/src/core/git-diff-new-file.js +119 -0
- package/dist/src/core/git-diff.d.ts +9 -9
- package/dist/src/core/git-diff.js +88 -157
- package/dist/src/core/git-file-ops.d.ts +0 -7
- package/dist/src/core/git-file-ops.js +4 -28
- package/dist/src/core/git-readonly-index.d.ts +0 -2
- package/dist/src/core/git-readonly-index.js +0 -4
- package/dist/src/core/git-status.d.ts +7 -0
- package/dist/src/core/git-status.js +21 -9
- package/dist/src/core/git.d.ts +0 -6
- package/dist/src/core/git.js +14 -43
- package/dist/src/core/harness-orphans.js +8 -7
- package/dist/src/core/history-log.d.ts +19 -0
- package/dist/src/core/history-log.js +29 -0
- package/dist/src/core/mach-error-hints.d.ts +0 -5
- package/dist/src/core/mach-error-hints.js +1 -1
- package/dist/src/core/mach-known-noise-filter.d.ts +4 -3
- package/dist/src/core/mach-known-noise-filter.js +7 -9
- package/dist/src/core/mach-mozconfig.js +2 -1
- package/dist/src/core/mach-python.d.ts +2 -0
- package/dist/src/core/mach-python.js +12 -7
- package/dist/src/core/mach-resource-shim.d.ts +13 -1
- package/dist/src/core/mach-resource-shim.js +112 -9
- package/dist/src/core/mach.d.ts +39 -53
- package/dist/src/core/mach.js +13 -81
- package/dist/src/core/marionette-port.d.ts +0 -17
- package/dist/src/core/marionette-port.js +12 -58
- package/dist/src/core/marionette-preflight.js +9 -27
- package/dist/src/core/mochitest-server-port.js +3 -3
- package/dist/src/core/moz-manifest-rules.js +37 -33
- package/dist/src/core/patch-apply-fuzz.js +4 -4
- package/dist/src/core/patch-apply.d.ts +4 -5
- package/dist/src/core/patch-apply.js +6 -12
- package/dist/src/core/patch-export-coverage.d.ts +0 -9
- package/dist/src/core/patch-export-coverage.js +1 -30
- package/dist/src/core/patch-export-update.d.ts +18 -1
- package/dist/src/core/patch-export-update.js +4 -3
- package/dist/src/core/patch-export.d.ts +11 -21
- package/dist/src/core/patch-export.js +44 -66
- package/dist/src/core/patch-files.d.ts +0 -4
- package/dist/src/core/patch-files.js +0 -11
- package/dist/src/core/patch-lint-binary.d.ts +6 -12
- package/dist/src/core/patch-lint-cache.d.ts +17 -1
- package/dist/src/core/patch-lint-cache.js +3 -6
- package/dist/src/core/patch-lint-checkjs.d.ts +33 -16
- package/dist/src/core/patch-lint-checkjs.js +37 -24
- package/dist/src/core/patch-lint-creators.d.ts +2 -6
- package/dist/src/core/patch-lint-cross.d.ts +16 -8
- package/dist/src/core/patch-lint-cross.js +49 -92
- package/dist/src/core/patch-lint-css.js +10 -6
- package/dist/src/core/patch-lint-file-size.js +3 -3
- package/dist/src/core/patch-lint-forward-registration.d.ts +58 -0
- package/dist/src/core/patch-lint-forward-registration.js +210 -0
- package/dist/src/core/patch-lint-jsdoc.js +36 -36
- package/dist/src/core/patch-lint-module-registration.d.ts +2 -8
- package/dist/src/core/patch-lint-prettier.js +1 -1
- package/dist/src/core/patch-lint-queue-types.d.ts +60 -0
- package/dist/src/core/patch-lint-queue-types.js +12 -0
- package/dist/src/core/patch-lint-staged-registration.d.ts +61 -0
- package/dist/src/core/patch-lint-staged-registration.js +90 -0
- package/dist/src/core/patch-lint.d.ts +14 -9
- package/dist/src/core/patch-lint.js +4 -3
- package/dist/src/core/patch-lock.js +3 -3
- package/dist/src/core/patch-manifest-io.js +8 -7
- package/dist/src/core/patch-parse.d.ts +19 -8
- package/dist/src/core/patch-parse.js +25 -0
- package/dist/src/core/patch-policy.js +15 -27
- package/dist/src/core/patch-source-metadata.d.ts +1 -3
- package/dist/src/core/patch-source-metadata.js +0 -4
- package/dist/src/core/patch-transform.d.ts +0 -8
- package/dist/src/core/patch-transform.js +0 -10
- package/dist/src/core/process-cpu-time.js +1 -1
- package/dist/src/core/rebase-session.d.ts +2 -8
- package/dist/src/core/rebase-session.js +5 -17
- package/dist/src/core/register-browser-content.js +3 -2
- package/dist/src/core/register-browser-test.js +2 -1
- package/dist/src/core/register-module.js +2 -1
- package/dist/src/core/register-scaffold.js +5 -4
- package/dist/src/core/register-shared-css.js +2 -1
- package/dist/src/core/run-log-redact.d.ts +38 -0
- package/dist/src/core/run-log-redact.js +69 -0
- package/dist/src/core/run-log.d.ts +13 -2
- package/dist/src/core/run-log.js +67 -18
- package/dist/src/core/signal-critical.js +2 -4
- package/dist/src/core/smoke-patterns.d.ts +0 -1
- package/dist/src/core/smoke-patterns.js +6 -11
- package/dist/src/core/state-file.d.ts +12 -1
- package/dist/src/core/state-file.js +14 -1
- package/dist/src/core/status-classify.js +2 -1
- package/dist/src/core/test-changed-prefs.js +26 -11
- package/dist/src/core/test-harness-crash.js +2 -2
- package/dist/src/core/test-stale-check.d.ts +0 -2
- package/dist/src/core/test-stale-check.js +2 -2
- package/dist/src/core/test-xpcshell-retry.d.ts +26 -7
- package/dist/src/core/test-xpcshell-retry.js +14 -11
- package/dist/src/core/token-category.d.ts +33 -0
- package/dist/src/core/token-category.js +56 -48
- package/dist/src/core/token-dark-mode.js +2 -14
- package/dist/src/core/token-inventory.d.ts +56 -0
- package/dist/src/core/token-inventory.js +109 -0
- package/dist/src/core/token-manager.d.ts +1 -1
- package/dist/src/core/token-manager.js +1 -1
- package/dist/src/core/token-variant.js +21 -22
- package/dist/src/core/toolchain-preflight.js +1 -1
- package/dist/src/core/tree-cow.d.ts +0 -2
- package/dist/src/core/tree-cow.js +2 -2
- package/dist/src/core/tree-store.js +4 -5
- package/dist/src/core/typecheck.js +2 -2
- package/dist/src/core/wire-dom-fragment.js +6 -17
- package/dist/src/core/wire-utils.js +5 -17
- package/dist/src/core/xpcshell-appdir.js +2 -4
- package/dist/src/errors/base.d.ts +53 -1
- package/dist/src/errors/base.js +79 -11
- package/dist/src/errors/build.js +20 -28
- package/dist/src/errors/config.d.ts +0 -14
- package/dist/src/errors/config.js +13 -34
- package/dist/src/errors/download.d.ts +13 -0
- package/dist/src/errors/download.js +53 -24
- package/dist/src/errors/furnace.js +8 -9
- package/dist/src/errors/git.d.ts +8 -8
- package/dist/src/errors/git.js +20 -30
- package/dist/src/errors/patch.js +8 -10
- package/dist/src/errors/rebase.js +7 -7
- package/dist/src/types/commands/options.d.ts +77 -123
- package/dist/src/types/config.d.ts +34 -2
- package/dist/src/types/index.d.ts +0 -1
- package/dist/src/utils/assert.d.ts +3 -15
- package/dist/src/utils/assert.js +1 -18
- package/dist/src/utils/build-info.d.ts +1 -1
- package/dist/src/utils/child-env.d.ts +2 -1
- package/dist/src/utils/elapsed.js +10 -1
- package/dist/src/utils/fs.js +8 -13
- package/dist/src/utils/hash.d.ts +12 -0
- package/dist/src/utils/hash.js +16 -0
- package/dist/src/utils/line-dispatch.d.ts +15 -0
- package/dist/src/utils/line-dispatch.js +48 -0
- package/dist/src/utils/options.d.ts +1 -7
- package/dist/src/utils/options.js +6 -7
- package/dist/src/utils/package-root.js +3 -12
- package/dist/src/utils/platform.d.ts +0 -4
- package/dist/src/utils/process-group.d.ts +2 -1
- package/dist/src/utils/process-group.js +20 -19
- package/dist/src/utils/process.d.ts +40 -26
- package/dist/src/utils/process.js +115 -166
- package/dist/src/utils/ps-duration.js +3 -3
- package/dist/src/utils/sleep.d.ts +26 -0
- package/dist/src/utils/sleep.js +31 -0
- package/dist/src/utils/validation.d.ts +2 -38
- package/dist/src/utils/validation.js +7 -16
- package/docs/configuration.md +89 -1
- package/docs/exit-codes.md +31 -19
- package/docs/lifecycle-invariants.md +18 -12
- package/docs/machine-output.md +31 -8
- package/docs/patch-workflow.md +2 -1
- package/docs/run-logs.md +44 -4
- package/docs/testing.md +8 -1
- package/package.json +8 -7
- package/dist/src/commands/rebase.d.ts +0 -4
- package/dist/src/commands/rebase.js +0 -5
- package/dist/src/core/patch-artifact-normalize.d.ts +0 -21
- package/dist/src/core/patch-artifact-normalize.js +0 -24
- package/dist/src/core/patch-lint-reexports.d.ts +0 -7
- package/dist/src/core/patch-lint-reexports.js +0 -8
- package/dist/src/core/wire-targets.d.ts +0 -7
- package/dist/src/core/wire-targets.js +0 -8
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
// SPDX-License-Identifier: EUPL-1.2
|
|
2
|
+
/**
|
|
3
|
+
* Cross-patch lint rule: a test manifest that registers a file a
|
|
4
|
+
* LATER-ordered patch creates, without declaring the staged dependency.
|
|
5
|
+
*
|
|
6
|
+
* The import kind has had this arm since forward-import shipped; the
|
|
7
|
+
* registration kind never did. `--kind registration` metadata was only ever
|
|
8
|
+
* checked for STALENESS once declared, so a patch whose `xpcshell.toml`
|
|
9
|
+
* carried `support-files = ["fixtures/*.sqlite"]` for a fixture the next
|
|
10
|
+
* patch creates passed lint in both arms — declaring the dependency and
|
|
11
|
+
* removing the declaration produced identical silence, which is what made
|
|
12
|
+
* the gap invisible rather than merely absent.
|
|
13
|
+
*
|
|
14
|
+
* Scope is deliberately narrow: test-manifest `support-files` only. A
|
|
15
|
+
* jar.mn packaging line or an actor/customElements registration names its
|
|
16
|
+
* target through indirection the linter cannot resolve without the build
|
|
17
|
+
* system, and a rule that guesses there would refuse correct queues on a
|
|
18
|
+
* Firefox-sized tree. The staged-registration declaration remains the
|
|
19
|
+
* documented escape hatch for a deliberate stage.
|
|
20
|
+
*/
|
|
21
|
+
import { basename, dirname, posix } from 'node:path';
|
|
22
|
+
import { normalizePathSlashes } from '../utils/paths.js';
|
|
23
|
+
import { escapeRegex } from '../utils/regex.js';
|
|
24
|
+
import { isLaterOwner, quoteRegistrationLine } from './patch-lint-staged-registration.js';
|
|
25
|
+
/**
|
|
26
|
+
* Manifest basenames whose `support-files` key registers auxiliary files
|
|
27
|
+
* for a test suite. Closed and documented on purpose — a pattern like
|
|
28
|
+
* "anything .toml" would sweep in Cargo and taskcluster manifests, whose
|
|
29
|
+
* `support-files`-shaped keys mean something else entirely.
|
|
30
|
+
*/
|
|
31
|
+
const TEST_MANIFEST_BASENAMES = new Set([
|
|
32
|
+
'xpcshell.toml',
|
|
33
|
+
'xpcshell.ini',
|
|
34
|
+
'mochitest.toml',
|
|
35
|
+
'mochitest.ini',
|
|
36
|
+
'browser.toml',
|
|
37
|
+
'browser.ini',
|
|
38
|
+
'chrome.toml',
|
|
39
|
+
'chrome.ini',
|
|
40
|
+
'a11y.toml',
|
|
41
|
+
'a11y.ini',
|
|
42
|
+
]);
|
|
43
|
+
/** True for a path whose basename is a recognised test manifest. */
|
|
44
|
+
function isTestManifestPath(path) {
|
|
45
|
+
return TEST_MANIFEST_BASENAMES.has(basename(normalizePathSlashes(path)));
|
|
46
|
+
}
|
|
47
|
+
/** Matches the start of a `support-files` assignment. */
|
|
48
|
+
const SUPPORT_FILES_PATTERN = /^\s*support-files\s*=\s*(.*)$/;
|
|
49
|
+
/**
|
|
50
|
+
* Extracts the `support-files` entries declared in `content`, each paired
|
|
51
|
+
* with the manifest line it is written on.
|
|
52
|
+
*
|
|
53
|
+
* Handles the TOML array on one line and spread over several, and the
|
|
54
|
+
* whitespace-separated `.ini` spelling. Anything else — a computed value, a
|
|
55
|
+
* shape this does not recognise — yields nothing, because a rule that
|
|
56
|
+
* guesses at a manifest it cannot parse refuses correct queues.
|
|
57
|
+
*
|
|
58
|
+
* Quoted tokens are matched per physical line rather than over the whole
|
|
59
|
+
* accumulated array body, so a multi-line array attributes each element to
|
|
60
|
+
* its own line. Entries are deduplicated: a manifest that lists the same
|
|
61
|
+
* file under two per-test sections needs one declaration, not two.
|
|
62
|
+
*/
|
|
63
|
+
export function extractSupportFileEntries(content) {
|
|
64
|
+
const lines = content.split('\n');
|
|
65
|
+
const order = [];
|
|
66
|
+
const sourceLines = new Map();
|
|
67
|
+
const record = (entry, source) => {
|
|
68
|
+
let seen = sourceLines.get(entry);
|
|
69
|
+
if (seen === undefined) {
|
|
70
|
+
seen = [];
|
|
71
|
+
sourceLines.set(entry, seen);
|
|
72
|
+
order.push(entry);
|
|
73
|
+
}
|
|
74
|
+
const trimmed = source.trim();
|
|
75
|
+
if (!seen.includes(trimmed))
|
|
76
|
+
seen.push(trimmed);
|
|
77
|
+
};
|
|
78
|
+
for (let i = 0; i < lines.length; i++) {
|
|
79
|
+
const raw = lines[i] ?? '';
|
|
80
|
+
const match = SUPPORT_FILES_PATTERN.exec(raw);
|
|
81
|
+
if (!match)
|
|
82
|
+
continue;
|
|
83
|
+
const head = match[1] ?? '';
|
|
84
|
+
if (!head.trimStart().startsWith('[')) {
|
|
85
|
+
// `.ini` spelling: whitespace-separated bare paths on one line.
|
|
86
|
+
for (const token of head.split(/\s+/).filter((t) => t.length > 0))
|
|
87
|
+
record(token, raw);
|
|
88
|
+
continue;
|
|
89
|
+
}
|
|
90
|
+
// TOML array: walk the physical lines until the closing bracket,
|
|
91
|
+
// bounded so an unterminated array in a malformed manifest cannot walk
|
|
92
|
+
// the file.
|
|
93
|
+
let body = head;
|
|
94
|
+
let scanned = 0;
|
|
95
|
+
let current = raw;
|
|
96
|
+
for (;;) {
|
|
97
|
+
for (const quoted of current.matchAll(/["']([^"']+)["']/g)) {
|
|
98
|
+
if (quoted[1] !== undefined)
|
|
99
|
+
record(quoted[1], current);
|
|
100
|
+
}
|
|
101
|
+
if (body.includes(']') || i + 1 >= lines.length || scanned >= 200)
|
|
102
|
+
break;
|
|
103
|
+
i += 1;
|
|
104
|
+
scanned += 1;
|
|
105
|
+
current = lines[i] ?? '';
|
|
106
|
+
body += `\n${current}`;
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
return order.map((entry) => {
|
|
110
|
+
const seen = sourceLines.get(entry) ?? [];
|
|
111
|
+
return { entry, line: seen[0] ?? '', occurrences: seen.length };
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* Resolves one `support-files` entry, relative to its manifest's directory,
|
|
116
|
+
* into a matcher over engine-relative paths.
|
|
117
|
+
*
|
|
118
|
+
* Only a `*` inside a single path segment is expanded — the shape the
|
|
119
|
+
* downstream report uses (`fixtures/*.sqlite`). `**`, `!` exclusions and
|
|
120
|
+
* absolute `/`-rooted entries return undefined: they either cross directory
|
|
121
|
+
* boundaries or subtract, and a matcher that guessed at them would attribute
|
|
122
|
+
* a creation to the wrong manifest.
|
|
123
|
+
*/
|
|
124
|
+
export function buildSupportFileMatcher(manifestPath, entry) {
|
|
125
|
+
if (entry.startsWith('!') || entry.includes('**') || entry.startsWith('/'))
|
|
126
|
+
return undefined;
|
|
127
|
+
const resolved = posix.normalize(posix.join(normalizePathSlashes(dirname(manifestPath)), normalizePathSlashes(entry)));
|
|
128
|
+
if (resolved.startsWith('..'))
|
|
129
|
+
return undefined;
|
|
130
|
+
if (!resolved.includes('*')) {
|
|
131
|
+
return (candidate) => normalizePathSlashes(candidate) === resolved;
|
|
132
|
+
}
|
|
133
|
+
const pattern = new RegExp(`^${resolved
|
|
134
|
+
.split('*')
|
|
135
|
+
.map((part) => escapeRegex(part))
|
|
136
|
+
.join('[^/]*')}$`);
|
|
137
|
+
return (candidate) => pattern.test(normalizePathSlashes(candidate));
|
|
138
|
+
}
|
|
139
|
+
/** True when this patch already declares the staged registration. */
|
|
140
|
+
function isDeclared(entry, manifestPath, createdPath) {
|
|
141
|
+
return (entry.metadata?.stagedDependencies?.registrations ?? []).some((registration) => normalizePathSlashes(registration.file) === normalizePathSlashes(manifestPath) &&
|
|
142
|
+
normalizePathSlashes(registration.creates) === normalizePathSlashes(createdPath));
|
|
143
|
+
}
|
|
144
|
+
/**
|
|
145
|
+
* Flags every `support-files` registration a patch introduces that names a
|
|
146
|
+
* file only a later-ordered patch creates, unless the staged dependency is
|
|
147
|
+
* declared.
|
|
148
|
+
*
|
|
149
|
+
* @param ctx - Projected queue in application order
|
|
150
|
+
* @returns One issue per undeclared forward registration
|
|
151
|
+
*/
|
|
152
|
+
export function lintPatchQueueForwardRegistrations(ctx) {
|
|
153
|
+
const issues = [];
|
|
154
|
+
for (const entry of ctx.entries) {
|
|
155
|
+
// Both arms: a manifest this patch CREATES and one it MODIFIES can each
|
|
156
|
+
// introduce a registration line.
|
|
157
|
+
const manifests = [
|
|
158
|
+
...entry.newFiles,
|
|
159
|
+
...entry.modifiedFileAdditions,
|
|
160
|
+
].filter(([path]) => isTestManifestPath(path));
|
|
161
|
+
for (const [manifestPath, content] of manifests) {
|
|
162
|
+
for (const { entry: raw, line: addedLine, occurrences } of extractSupportFileEntries(content)) {
|
|
163
|
+
const matches = buildSupportFileMatcher(manifestPath, raw);
|
|
164
|
+
if (matches === undefined)
|
|
165
|
+
continue;
|
|
166
|
+
// `--line` is the line as the patch adds it, which is what the
|
|
167
|
+
// declared arm compares and what `--remove` matches on. Synthesising
|
|
168
|
+
// `support-files = ["<entry>"]` instead only ever matched a
|
|
169
|
+
// single-line single-entry array, so the pasted command left the
|
|
170
|
+
// declaring patch red with staged-dependency-unused.
|
|
171
|
+
const declaredLine = quoteRegistrationLine(addedLine);
|
|
172
|
+
const ambiguity = occurrences > 1
|
|
173
|
+
? ` (the entry is spelled out on ${occurrences} added lines here; ` +
|
|
174
|
+
'the first is quoted, and one declaration covers the file either way ' +
|
|
175
|
+
'because the declared check keys on --file and --creates.)'
|
|
176
|
+
: '';
|
|
177
|
+
for (const later of ctx.entries) {
|
|
178
|
+
// Same predicate the declared-registration arm validates against,
|
|
179
|
+
// tiebreak included: two spellings of "later" would let one rule
|
|
180
|
+
// flag a pair the other considers already satisfied.
|
|
181
|
+
if (!isLaterOwner(later, entry))
|
|
182
|
+
continue;
|
|
183
|
+
for (const created of later.createdFiles) {
|
|
184
|
+
if (!matches(created))
|
|
185
|
+
continue;
|
|
186
|
+
if (isDeclared(entry, manifestPath, created))
|
|
187
|
+
continue;
|
|
188
|
+
issues.push({
|
|
189
|
+
file: manifestPath,
|
|
190
|
+
check: 'forward-registration',
|
|
191
|
+
patches: [entry.filename],
|
|
192
|
+
fingerprint: `forward-registration|${entry.filename}|${manifestPath}|${raw}|${created}|${later.filename}`,
|
|
193
|
+
message: `${manifestPath} in ${entry.filename} registers "${raw}" as a support file, ` +
|
|
194
|
+
`but ${created} is created by the later patch ${later.filename}. ` +
|
|
195
|
+
'Applying the queue up to this patch leaves the registration dangling. ' +
|
|
196
|
+
'Reorder the patches so the file is created first, move the registration into ' +
|
|
197
|
+
'the later patch, or declare the intentional staged dependency with: ' +
|
|
198
|
+
`fireforge patch staged-dependency ${entry.filename} --add --kind registration ` +
|
|
199
|
+
`--file ${manifestPath} --line "${declaredLine}" ` +
|
|
200
|
+
`--creates ${created} --owner ${later.filename}` +
|
|
201
|
+
ambiguity,
|
|
202
|
+
severity: 'error',
|
|
203
|
+
});
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
return issues;
|
|
210
|
+
}
|
|
@@ -46,8 +46,8 @@ function findAttachedJsDoc(comments, declStart, source) {
|
|
|
46
46
|
* loses the param name. String literal types may contain braces too, so
|
|
47
47
|
* quoted runs are skipped verbatim.
|
|
48
48
|
*
|
|
49
|
-
* @returns Index just past the matching close brace, or
|
|
50
|
-
* expression never closes (malformed doc — caller skips the tag).
|
|
49
|
+
* @returns Index just past the matching close brace, or undefined when the
|
|
50
|
+
* type expression never closes (malformed doc — caller skips the tag).
|
|
51
51
|
*/
|
|
52
52
|
function skipBalancedTypeBraces(jsDoc, start) {
|
|
53
53
|
let depth = 0;
|
|
@@ -73,7 +73,7 @@ function skipBalancedTypeBraces(jsDoc, start) {
|
|
|
73
73
|
return i + 1;
|
|
74
74
|
}
|
|
75
75
|
}
|
|
76
|
-
return
|
|
76
|
+
return undefined;
|
|
77
77
|
}
|
|
78
78
|
function extractParamNames(jsDoc) {
|
|
79
79
|
const names = [];
|
|
@@ -87,7 +87,7 @@ function extractParamNames(jsDoc) {
|
|
|
87
87
|
// inline object types or Record<…> generics don't truncate the scan.
|
|
88
88
|
if (jsDoc[i] === '{') {
|
|
89
89
|
const afterType = skipBalancedTypeBraces(jsDoc, i);
|
|
90
|
-
if (afterType ===
|
|
90
|
+
if (afterType === undefined)
|
|
91
91
|
continue;
|
|
92
92
|
i = afterType;
|
|
93
93
|
while (i < jsDoc.length && /\s/.test(jsDoc[i] ?? ''))
|
|
@@ -124,6 +124,17 @@ function hasPrivateOrInternalTag(jsDoc) {
|
|
|
124
124
|
function functionReturnsValue(node) {
|
|
125
125
|
return walkForReturn(node.body);
|
|
126
126
|
}
|
|
127
|
+
/**
|
|
128
|
+
* Narrows an arbitrary child value reached by walking a node's own
|
|
129
|
+
* properties to an ESTree node. Acorn's AST holds nodes, arrays of nodes,
|
|
130
|
+
* and plain scalars in the same property positions, so the discriminating
|
|
131
|
+
* `type` string is the only structural signal available.
|
|
132
|
+
*/
|
|
133
|
+
function isEstreeNode(value) {
|
|
134
|
+
if (typeof value !== 'object' || value === null || !('type' in value))
|
|
135
|
+
return false;
|
|
136
|
+
return typeof value.type === 'string';
|
|
137
|
+
}
|
|
127
138
|
function walkForReturn(node) {
|
|
128
139
|
if (node.type === 'ReturnStatement') {
|
|
129
140
|
return node.argument != null;
|
|
@@ -139,20 +150,16 @@ function walkForReturn(node) {
|
|
|
139
150
|
for (const [key, val] of entries) {
|
|
140
151
|
if (key === 'type')
|
|
141
152
|
continue;
|
|
142
|
-
if (val
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
if (walkForReturn(child))
|
|
147
|
-
return true;
|
|
148
|
-
}
|
|
149
|
-
}
|
|
150
|
-
}
|
|
151
|
-
else if ('type' in val) {
|
|
152
|
-
if (walkForReturn(val))
|
|
153
|
+
if (Array.isArray(val)) {
|
|
154
|
+
const children = val;
|
|
155
|
+
for (const child of children) {
|
|
156
|
+
if (isEstreeNode(child) && walkForReturn(child))
|
|
153
157
|
return true;
|
|
154
158
|
}
|
|
155
159
|
}
|
|
160
|
+
else if (isEstreeNode(val) && walkForReturn(val)) {
|
|
161
|
+
return true;
|
|
162
|
+
}
|
|
156
163
|
}
|
|
157
164
|
return false;
|
|
158
165
|
}
|
|
@@ -183,12 +190,7 @@ function findLocalDeclaration(body, name) {
|
|
|
183
190
|
// Line number helpers
|
|
184
191
|
// ---------------------------------------------------------------------------
|
|
185
192
|
function lineAt(source, offset) {
|
|
186
|
-
|
|
187
|
-
for (let i = 0; i < offset && i < source.length; i++) {
|
|
188
|
-
if (source[i] === '\n')
|
|
189
|
-
line++;
|
|
190
|
-
}
|
|
191
|
-
return line;
|
|
193
|
+
return source.slice(0, offset).split('\n').length;
|
|
192
194
|
}
|
|
193
195
|
/**
|
|
194
196
|
* Validates @param name matching and @returns presence on a function-like
|
|
@@ -198,21 +200,19 @@ function lineAt(source, offset) {
|
|
|
198
200
|
*/
|
|
199
201
|
function validateParamsAndReturns(fnNode, jsDoc, issues, ctx) {
|
|
200
202
|
const docText = jsDoc.value;
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
});
|
|
215
|
-
}
|
|
203
|
+
const actualParams = fnNode.params
|
|
204
|
+
.map((p) => (p.type === 'Identifier' ? p.name : null))
|
|
205
|
+
.filter((n) => n !== null);
|
|
206
|
+
if (actualParams.length > 0) {
|
|
207
|
+
const docParams = extractParamNames(docText);
|
|
208
|
+
for (const param of actualParams) {
|
|
209
|
+
if (!docParams.includes(param)) {
|
|
210
|
+
issues.push({
|
|
211
|
+
line: ctx.line,
|
|
212
|
+
check: ctx.paramCheck,
|
|
213
|
+
message: `Exported ${ctx.label} at line ${ctx.line}: @param "${param}" is missing or misnamed in JSDoc.`,
|
|
214
|
+
...(ctx.severity ? { severity: ctx.severity } : {}),
|
|
215
|
+
});
|
|
216
216
|
}
|
|
217
217
|
}
|
|
218
218
|
}
|
|
@@ -1,12 +1,6 @@
|
|
|
1
1
|
import type { PatchLintIssue } from '../types/commands/index.js';
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
newFiles: ReadonlyMap<string, string>;
|
|
5
|
-
modifiedFileAdditions: ReadonlyMap<string, string>;
|
|
6
|
-
}
|
|
7
|
-
interface ModuleRegistrationQueueContext {
|
|
8
|
-
entries: readonly ModuleRegistrationQueueEntry[];
|
|
9
|
-
}
|
|
2
|
+
import type { PatchQueueRegistrationEntry, PatchQueueView } from './patch-lint-queue-types.js';
|
|
3
|
+
type ModuleRegistrationQueueContext = PatchQueueView<PatchQueueRegistrationEntry>;
|
|
10
4
|
/**
|
|
11
5
|
* Maps Firefox's common system-module resource URLs to their source
|
|
12
6
|
* suffix. Exported alongside {@link extractResourceModuleSpecifiers} so
|
|
@@ -94,7 +94,7 @@ export async function invokePatchLintPrettier(engineDir, projectRoot, files, gat
|
|
|
94
94
|
{
|
|
95
95
|
file: '(prettier)',
|
|
96
96
|
check: 'prettier-format',
|
|
97
|
-
message: `prettier exited ${
|
|
97
|
+
message: `prettier exited ${result.exitCode} without completing the check: ` +
|
|
98
98
|
output.trim().split('\n').slice(-5).join(' ').slice(0, 400),
|
|
99
99
|
severity,
|
|
100
100
|
},
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Structural views of the cross-patch lint queue context.
|
|
3
|
+
*
|
|
4
|
+
* A leaf module by design: it imports nothing, so the individual rule
|
|
5
|
+
* modules can describe the slice of `PatchQueueContext` they read without
|
|
6
|
+
* importing `patch-lint-cross.ts` (which imports them back). Previously each
|
|
7
|
+
* rule re-declared its own private `{ entries }` interface; sharing them here
|
|
8
|
+
* keeps the dependency edge one-way and `dpdm` clean while stating the shape
|
|
9
|
+
* once.
|
|
10
|
+
*/
|
|
11
|
+
/**
|
|
12
|
+
* The slice of a queue entry consumed by rules that read raw patch bodies
|
|
13
|
+
* (the new-file creators map, the binary-body check).
|
|
14
|
+
*/
|
|
15
|
+
export interface PatchQueueBodyEntry {
|
|
16
|
+
/** Filename on disk and in the manifest. */
|
|
17
|
+
filename: string;
|
|
18
|
+
/** Raw unified-diff content of the patch body. */
|
|
19
|
+
diff: string;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* The slice of a queue entry consumed by the module-registration rule: the
|
|
23
|
+
* per-path content maps rather than the raw diff.
|
|
24
|
+
*/
|
|
25
|
+
export interface PatchQueueRegistrationEntry {
|
|
26
|
+
/** Filename on disk and in the manifest. */
|
|
27
|
+
filename: string;
|
|
28
|
+
/** Newly-created file path → the content the patch would produce. */
|
|
29
|
+
newFiles: ReadonlyMap<string, string>;
|
|
30
|
+
/** Existing file path → the lines the patch adds to it. */
|
|
31
|
+
modifiedFileAdditions: ReadonlyMap<string, string>;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Structural queue view: the `entries` field of a `PatchQueueContext`,
|
|
35
|
+
* narrowed to whichever entry slice a rule needs.
|
|
36
|
+
*/
|
|
37
|
+
export interface PatchQueueView<TEntry> {
|
|
38
|
+
/** Entries in application order (lowest `order` first). */
|
|
39
|
+
entries: readonly TEntry[];
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* The slice consumed by the forward-registration rule: the same content maps
|
|
43
|
+
* the module-registration rule reads, plus the body-kind-agnostic creation
|
|
44
|
+
* set and the ordering/metadata needed to decide "later" and "declared".
|
|
45
|
+
*/
|
|
46
|
+
export interface PatchQueueForwardRegistrationEntry extends PatchQueueRegistrationEntry {
|
|
47
|
+
/** Order number from the manifest (or filename prefix fallback). */
|
|
48
|
+
order: number;
|
|
49
|
+
/** Every path this patch creates, whatever the body kind. */
|
|
50
|
+
createdFiles: ReadonlySet<string>;
|
|
51
|
+
/** Manifest metadata. Null when the patch file exists but has no entry. */
|
|
52
|
+
metadata: {
|
|
53
|
+
stagedDependencies?: {
|
|
54
|
+
registrations?: readonly {
|
|
55
|
+
file: string;
|
|
56
|
+
creates: string;
|
|
57
|
+
}[];
|
|
58
|
+
};
|
|
59
|
+
} | null;
|
|
60
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
// SPDX-License-Identifier: EUPL-1.2
|
|
2
|
+
/**
|
|
3
|
+
* Structural views of the cross-patch lint queue context.
|
|
4
|
+
*
|
|
5
|
+
* A leaf module by design: it imports nothing, so the individual rule
|
|
6
|
+
* modules can describe the slice of `PatchQueueContext` they read without
|
|
7
|
+
* importing `patch-lint-cross.ts` (which imports them back). Previously each
|
|
8
|
+
* rule re-declared its own private `{ entries }` interface; sharing them here
|
|
9
|
+
* keeps the dependency edge one-way and `dpdm` clean while stating the shape
|
|
10
|
+
* once.
|
|
11
|
+
*/
|
|
12
|
+
export {};
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import type { PatchStagedRegistration } from '../types/commands/index.js';
|
|
2
|
+
/** A patch that creates a given path, with its position in the queue. */
|
|
3
|
+
export interface NewFileOwner {
|
|
4
|
+
filename: string;
|
|
5
|
+
order: number;
|
|
6
|
+
fullPath: string;
|
|
7
|
+
}
|
|
8
|
+
/** The ordering fields `isLaterOwner` compares an owner against. */
|
|
9
|
+
export interface QueuePosition {
|
|
10
|
+
filename: string;
|
|
11
|
+
order: number;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Later-in-apply-order predicate shared by the forward-import scan, the
|
|
15
|
+
* forward-registration rule and the registration validation: strictly
|
|
16
|
+
* higher ordinal, or same ordinal with a lexicographically later filename
|
|
17
|
+
* (the apply loop's tiebreak).
|
|
18
|
+
*
|
|
19
|
+
* @param owner - The creating patch
|
|
20
|
+
* @param entry - The referencing patch
|
|
21
|
+
* @returns True when `owner` applies after `entry`
|
|
22
|
+
*/
|
|
23
|
+
export declare function isLaterOwner(owner: QueuePosition, entry: QueuePosition): boolean;
|
|
24
|
+
/** The slice of a queue entry the registration validation reads. */
|
|
25
|
+
export interface StagedRegistrationEntry extends QueuePosition {
|
|
26
|
+
newFiles: ReadonlyMap<string, string>;
|
|
27
|
+
modifiedFileAdditions: ReadonlyMap<string, string>;
|
|
28
|
+
}
|
|
29
|
+
/** Why a staged registration declaration failed validation. */
|
|
30
|
+
export type StagedRegistrationFailure = {
|
|
31
|
+
kind: 'line-absent';
|
|
32
|
+
} | {
|
|
33
|
+
kind: 'creates-unresolved';
|
|
34
|
+
} | {
|
|
35
|
+
kind: 'owner-mismatch';
|
|
36
|
+
creators: readonly NewFileOwner[];
|
|
37
|
+
};
|
|
38
|
+
/**
|
|
39
|
+
* Validates one registration-kind declaration against the patch content
|
|
40
|
+
* and the queue, mirroring {@link findMatchingStagedDependency}: the
|
|
41
|
+
* declared line must be added by this patch, `creates` must exactly match
|
|
42
|
+
* a file a LATER-ordered patch creates (an earlier-only creator means the
|
|
43
|
+
* dependency is already satisfied and the declaration is stale), and
|
|
44
|
+
* `owner`, when set, must name one of those creating patches. Returns
|
|
45
|
+
* undefined when valid.
|
|
46
|
+
*/
|
|
47
|
+
export declare function findStagedRegistrationFailure(entry: StagedRegistrationEntry, registration: PatchStagedRegistration, createdFileIndex: Map<string, NewFileOwner[]>): StagedRegistrationFailure | undefined;
|
|
48
|
+
/** Message tail for each {@link StagedRegistrationFailure} shape. */
|
|
49
|
+
export declare function describeRegistrationFailure(failure: StagedRegistrationFailure): string;
|
|
50
|
+
/**
|
|
51
|
+
* Renders `line` for the `--line "…"` argument of a paste-and-run
|
|
52
|
+
* `patch staged-dependency` command.
|
|
53
|
+
*
|
|
54
|
+
* A registration line is quoted for the shell in the messages that print
|
|
55
|
+
* the command, and a `support-files` element carries its own quotes
|
|
56
|
+
* (`"file_tiles_find.html",` is what a multi-line TOML array adds). Without
|
|
57
|
+
* escaping, the shell would eat them and the declaration would be written
|
|
58
|
+
* with a line the patch never adds — the exact mismatch that made the
|
|
59
|
+
* forward-registration remedy leave the declaring patch red.
|
|
60
|
+
*/
|
|
61
|
+
export declare function quoteRegistrationLine(line: string): string;
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
// SPDX-License-Identifier: EUPL-1.2
|
|
2
|
+
/**
|
|
3
|
+
* Validation of `--kind registration` staged-dependency declarations.
|
|
4
|
+
*
|
|
5
|
+
* Split out of `patch-lint-cross.ts` to keep that file inside the per-file
|
|
6
|
+
* line budget, and because the forward-registration rule needs the same
|
|
7
|
+
* apply-order predicate: two spellings of "later in the queue" would let the
|
|
8
|
+
* rule that FLAGS an undeclared registration and the rule that validates a
|
|
9
|
+
* DECLARED one disagree about the very same pair of patches.
|
|
10
|
+
*
|
|
11
|
+
* Exports helpers consumed by `patch-lint-cross.ts` and
|
|
12
|
+
* `patch-lint-forward-registration.ts`; no registrar, no rule of its own.
|
|
13
|
+
*/
|
|
14
|
+
import { basename } from 'node:path';
|
|
15
|
+
/**
|
|
16
|
+
* Later-in-apply-order predicate shared by the forward-import scan, the
|
|
17
|
+
* forward-registration rule and the registration validation: strictly
|
|
18
|
+
* higher ordinal, or same ordinal with a lexicographically later filename
|
|
19
|
+
* (the apply loop's tiebreak).
|
|
20
|
+
*
|
|
21
|
+
* @param owner - The creating patch
|
|
22
|
+
* @param entry - The referencing patch
|
|
23
|
+
* @returns True when `owner` applies after `entry`
|
|
24
|
+
*/
|
|
25
|
+
export function isLaterOwner(owner, entry) {
|
|
26
|
+
return (owner.order > entry.order || (owner.order === entry.order && owner.filename > entry.filename));
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Validates one registration-kind declaration against the patch content
|
|
30
|
+
* and the queue, mirroring {@link findMatchingStagedDependency}: the
|
|
31
|
+
* declared line must be added by this patch, `creates` must exactly match
|
|
32
|
+
* a file a LATER-ordered patch creates (an earlier-only creator means the
|
|
33
|
+
* dependency is already satisfied and the declaration is stale), and
|
|
34
|
+
* `owner`, when set, must name one of those creating patches. Returns
|
|
35
|
+
* undefined when valid.
|
|
36
|
+
*/
|
|
37
|
+
export function findStagedRegistrationFailure(entry, registration, createdFileIndex) {
|
|
38
|
+
if (!isRegistrationLinePresent(entry, registration))
|
|
39
|
+
return { kind: 'line-absent' };
|
|
40
|
+
const creators = (createdFileIndex.get(basename(registration.creates)) ?? []).filter((owner) => owner.fullPath === registration.creates && isLaterOwner(owner, entry));
|
|
41
|
+
if (creators.length === 0)
|
|
42
|
+
return { kind: 'creates-unresolved' };
|
|
43
|
+
if (registration.owner !== undefined &&
|
|
44
|
+
!creators.some((owner) => owner.filename === registration.owner)) {
|
|
45
|
+
return { kind: 'owner-mismatch', creators };
|
|
46
|
+
}
|
|
47
|
+
return undefined;
|
|
48
|
+
}
|
|
49
|
+
/** Message tail for each {@link StagedRegistrationFailure} shape. */
|
|
50
|
+
export function describeRegistrationFailure(failure) {
|
|
51
|
+
switch (failure.kind) {
|
|
52
|
+
case 'line-absent':
|
|
53
|
+
return 'the patch does not add that line.';
|
|
54
|
+
case 'creates-unresolved':
|
|
55
|
+
return ('no later-ordered patch creates that file — the declaration is stale, ' +
|
|
56
|
+
'or the queue order no longer stages it.');
|
|
57
|
+
case 'owner-mismatch':
|
|
58
|
+
return `that file is created by ${failure.creators.map((o) => o.filename).join(', ')}, not the declared owner.`;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* True when the declared registration/packaging line appears (trimmed)
|
|
63
|
+
* among the lines the patch introduces in the declaring file — either as a
|
|
64
|
+
* newly-created file's content or as added lines to an existing file
|
|
65
|
+
* (where jar.mn / customElements.js / actor-registration edits land).
|
|
66
|
+
*/
|
|
67
|
+
function isRegistrationLinePresent(entry, registration) {
|
|
68
|
+
const declared = registration.line.trim();
|
|
69
|
+
if (declared.length === 0)
|
|
70
|
+
return false;
|
|
71
|
+
const introduced = [
|
|
72
|
+
entry.newFiles.get(registration.file),
|
|
73
|
+
entry.modifiedFileAdditions.get(registration.file),
|
|
74
|
+
];
|
|
75
|
+
return introduced.some((content) => content !== undefined && content.split('\n').some((line) => line.trim() === declared));
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Renders `line` for the `--line "…"` argument of a paste-and-run
|
|
79
|
+
* `patch staged-dependency` command.
|
|
80
|
+
*
|
|
81
|
+
* A registration line is quoted for the shell in the messages that print
|
|
82
|
+
* the command, and a `support-files` element carries its own quotes
|
|
83
|
+
* (`"file_tiles_find.html",` is what a multi-line TOML array adds). Without
|
|
84
|
+
* escaping, the shell would eat them and the declaration would be written
|
|
85
|
+
* with a line the patch never adds — the exact mismatch that made the
|
|
86
|
+
* forward-registration remedy leave the declaring patch red.
|
|
87
|
+
*/
|
|
88
|
+
export function quoteRegistrationLine(line) {
|
|
89
|
+
return line.replace(/\\/g, '\\\\').replace(/"/g, '\\"');
|
|
90
|
+
}
|
|
@@ -2,7 +2,8 @@ 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
|
-
export * from './patch-lint-
|
|
5
|
+
export * from './patch-lint-cross.js';
|
|
6
|
+
export { buildModifiedFileAdditionsFromDiff, detectNewFilesInDiff } from './patch-lint-diff.js';
|
|
6
7
|
export { lintPatchedCss };
|
|
7
8
|
/**
|
|
8
9
|
* Canonical `[check] file: message` rendering of a patch-lint issue — the
|
|
@@ -132,6 +133,17 @@ export type PatchSizeTierDecision = {
|
|
|
132
133
|
* defensive-for-tests choice.
|
|
133
134
|
*/
|
|
134
135
|
export declare function resolvePatchSizeTier(filesAffected: ReadonlyArray<string>, patchTier?: 'branding'): PatchSizeTierDecision;
|
|
136
|
+
/** Line and file-count thresholds governing one patch-size tier. */
|
|
137
|
+
export interface PatchSizeThresholds {
|
|
138
|
+
/** Added-line counts at which notice/warning/error issues fire. */
|
|
139
|
+
lines: {
|
|
140
|
+
notice: number;
|
|
141
|
+
warning: number;
|
|
142
|
+
error: number;
|
|
143
|
+
};
|
|
144
|
+
/** Maximum number of files a patch may touch before an issue fires. */
|
|
145
|
+
maxFiles: number;
|
|
146
|
+
}
|
|
135
147
|
/**
|
|
136
148
|
* Read-only view of the size thresholds a tier enforces.
|
|
137
149
|
* Public-API companion to {@link resolvePatchSizeTier} /
|
|
@@ -139,14 +151,7 @@ export declare function resolvePatchSizeTier(filesAffected: ReadonlyArray<string
|
|
|
139
151
|
* checks against the SAME numbers `large-patch-lines` /
|
|
140
152
|
* `large-patch-files` fire on instead of mirroring them.
|
|
141
153
|
*/
|
|
142
|
-
export declare function getPatchSizeThresholds(tier: 'general' | 'test' | 'branding'):
|
|
143
|
-
lines: {
|
|
144
|
-
notice: number;
|
|
145
|
-
warning: number;
|
|
146
|
-
error: number;
|
|
147
|
-
};
|
|
148
|
-
maxFiles: number;
|
|
149
|
-
};
|
|
154
|
+
export declare function getPatchSizeThresholds(tier: 'general' | 'test' | 'branding'): PatchSizeThresholds;
|
|
150
155
|
/**
|
|
151
156
|
* Checks patch size and emits advisory warnings.
|
|
152
157
|
*
|
|
@@ -22,9 +22,10 @@ import { invokePatchLintPrettier } from './patch-lint-prettier.js';
|
|
|
22
22
|
// The cross-patch lint infrastructure (queue context builder,
|
|
23
23
|
// duplicate-creation and forward-import rules, ignore marker) lives in
|
|
24
24
|
// `patch-lint-cross.ts` so the per-patch and cross-patch rule bodies each
|
|
25
|
-
// stay within the per-file line budget.
|
|
26
|
-
//
|
|
27
|
-
export * from './patch-lint-
|
|
25
|
+
// stay within the per-file line budget. Its surface is re-exported here so
|
|
26
|
+
// callers keep importing from one module.
|
|
27
|
+
export * from './patch-lint-cross.js';
|
|
28
|
+
export { buildModifiedFileAdditionsFromDiff, detectNewFilesInDiff } from './patch-lint-diff.js';
|
|
28
29
|
// The CSS rule bodies live in `patch-lint-css.ts` (same per-file-budget
|
|
29
30
|
// split as the other rule families); re-export the imported binding so
|
|
30
31
|
// callers and tests keep importing `lintPatchedCss` from this module.
|