@hominis/fireforge 0.43.1 → 0.44.1
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 +153 -0
- package/README.md +3 -3
- package/dist/bin/fireforge.d.ts +4 -9
- package/dist/bin/fireforge.js +42 -40
- package/dist/build-info.json +4 -4
- package/dist/src/cli.js +74 -8
- package/dist/src/commands/bootstrap.js +17 -20
- package/dist/src/commands/build.js +25 -32
- package/dist/src/commands/config.js +34 -41
- package/dist/src/commands/discard.js +8 -9
- package/dist/src/commands/doctor-check-core.d.ts +13 -18
- package/dist/src/commands/doctor-check-core.js +8 -35
- package/dist/src/commands/doctor-external-toolchains.js +2 -2
- package/dist/src/commands/doctor-furnace-config-sync.d.ts +18 -0
- package/dist/src/commands/{doctor-furnace-manifest-sync.js → doctor-furnace-config-sync.js} +27 -26
- package/dist/src/commands/doctor-furnace-jar.d.ts +7 -9
- package/dist/src/commands/doctor-furnace-jar.js +7 -9
- package/dist/src/commands/doctor-furnace.js +21 -24
- package/dist/src/commands/doctor-orphaned-harness.d.ts +16 -20
- package/dist/src/commands/doctor-orphaned-harness.js +18 -44
- package/dist/src/commands/doctor-patch-manifest.d.ts +8 -0
- package/dist/src/commands/doctor-patch-manifest.js +155 -0
- package/dist/src/commands/doctor-post-rebase-audit.d.ts +2 -0
- package/dist/src/commands/{doctor/post-rebase-audit.js → doctor-post-rebase-audit.js} +3 -3
- package/dist/src/commands/doctor-working-tree.d.ts +10 -12
- package/dist/src/commands/doctor-working-tree.js +11 -18
- package/dist/src/commands/doctor.d.ts +1 -1
- package/dist/src/commands/doctor.js +64 -96
- package/dist/src/commands/download.js +32 -48
- package/dist/src/commands/export-all.js +29 -33
- package/dist/src/commands/export-flow.js +6 -6
- package/dist/src/commands/export-placement-conflicts.js +10 -9
- package/dist/src/commands/export-placement-gate.js +5 -5
- package/dist/src/commands/export-shared.d.ts +30 -33
- package/dist/src/commands/export-shared.js +45 -43
- package/dist/src/commands/export.js +21 -27
- package/dist/src/commands/furnace/apply.js +14 -23
- package/dist/src/commands/furnace/chrome-doc-remove.js +14 -21
- package/dist/src/commands/furnace/chrome-doc-templates.d.ts +23 -23
- package/dist/src/commands/furnace/chrome-doc-templates.js +23 -23
- package/dist/src/commands/furnace/chrome-doc-tests.js +26 -29
- package/dist/src/commands/furnace/chrome-doc.d.ts +16 -17
- package/dist/src/commands/furnace/chrome-doc.js +31 -41
- package/dist/src/commands/furnace/create-browser-test.d.ts +4 -4
- package/dist/src/commands/furnace/create-browser-test.js +8 -8
- package/dist/src/commands/furnace/create-mochikit.d.ts +6 -7
- package/dist/src/commands/furnace/create-mochikit.js +8 -9
- package/dist/src/commands/furnace/create-readback.d.ts +12 -13
- package/dist/src/commands/furnace/create-readback.js +12 -13
- package/dist/src/commands/furnace/create-templates.d.ts +22 -28
- package/dist/src/commands/furnace/create-templates.js +35 -30
- package/dist/src/commands/furnace/create-xpcshell.js +2 -2
- package/dist/src/commands/furnace/create.js +33 -23
- package/dist/src/commands/furnace/deploy.js +16 -24
- package/dist/src/commands/furnace/diff.js +15 -10
- package/dist/src/commands/furnace/init.js +33 -36
- package/dist/src/commands/furnace/override.js +23 -25
- package/dist/src/commands/furnace/preview.js +47 -67
- package/dist/src/commands/furnace/refresh.js +12 -16
- package/dist/src/commands/furnace/remove-mutations.d.ts +3 -6
- package/dist/src/commands/furnace/remove-mutations.js +14 -17
- package/dist/src/commands/furnace/remove-state.js +1 -1
- package/dist/src/commands/furnace/remove.js +33 -38
- package/dist/src/commands/furnace/rename-xpcshell.d.ts +8 -11
- package/dist/src/commands/furnace/rename-xpcshell.js +8 -11
- package/dist/src/commands/furnace/rename.js +59 -69
- package/dist/src/commands/furnace/scan.js +19 -33
- package/dist/src/commands/furnace/status.js +12 -9
- package/dist/src/commands/furnace/sync.js +9 -16
- package/dist/src/commands/furnace/validate.js +20 -20
- package/dist/src/commands/import.js +48 -37
- package/dist/src/commands/lint-per-patch.js +15 -10
- package/dist/src/commands/lint-per-run-checkjs.d.ts +13 -13
- package/dist/src/commands/lint-per-run-checkjs.js +13 -13
- package/dist/src/commands/lint-report.js +6 -7
- package/dist/src/commands/lint.d.ts +15 -16
- package/dist/src/commands/lint.js +86 -102
- package/dist/src/commands/package.js +6 -11
- package/dist/src/commands/patch/compact.js +1 -1
- package/dist/src/commands/patch/delete.js +24 -33
- package/dist/src/commands/patch/index.d.ts +5 -6
- package/dist/src/commands/patch/index.js +10 -12
- package/dist/src/commands/patch/lint-ignore.js +14 -13
- package/dist/src/commands/patch/move-files-create.d.ts +6 -6
- package/dist/src/commands/patch/move-files-create.js +8 -8
- package/dist/src/commands/patch/move-files-into.d.ts +6 -6
- package/dist/src/commands/patch/move-files-into.js +8 -8
- package/dist/src/commands/patch/patch-context.d.ts +2 -3
- package/dist/src/commands/patch/patch-context.js +2 -3
- package/dist/src/commands/patch/rename.d.ts +10 -11
- package/dist/src/commands/patch/rename.js +11 -12
- package/dist/src/commands/patch/reorder.js +8 -9
- package/dist/src/commands/patch/split-plan.js +1 -1
- package/dist/src/commands/patch/split.d.ts +8 -10
- package/dist/src/commands/patch/split.js +9 -11
- package/dist/src/commands/patch/staged-dependency-validate.js +8 -8
- package/dist/src/commands/patch/staged-dependency.js +7 -3
- package/dist/src/commands/patch/tier.d.ts +9 -9
- package/dist/src/commands/patch/tier.js +18 -15
- package/dist/src/commands/re-export-adjacent.js +9 -8
- package/dist/src/commands/re-export-bulk-scan.d.ts +9 -9
- package/dist/src/commands/re-export-bulk-scan.js +22 -21
- package/dist/src/commands/re-export-drift.js +10 -10
- package/dist/src/commands/re-export-files.d.ts +6 -7
- package/dist/src/commands/re-export-files.js +34 -44
- package/dist/src/commands/re-export-lint.d.ts +10 -13
- package/dist/src/commands/re-export-lint.js +16 -14
- package/dist/src/commands/re-export-options.d.ts +6 -6
- package/dist/src/commands/re-export-options.js +6 -6
- package/dist/src/commands/re-export-register.d.ts +4 -10
- package/dist/src/commands/re-export-register.js +5 -11
- package/dist/src/commands/re-export-scan.js +1 -1
- package/dist/src/commands/re-export-single.d.ts +3 -3
- package/dist/src/commands/re-export-single.js +33 -25
- package/dist/src/commands/re-export.js +13 -21
- package/dist/src/commands/rebase/abort.js +3 -3
- package/dist/src/commands/rebase/confirm.js +2 -1
- package/dist/src/commands/rebase/continue.d.ts +1 -1
- package/dist/src/commands/rebase/continue.js +14 -9
- package/dist/src/commands/rebase/index.js +11 -11
- package/dist/src/commands/rebase/patch-loop.d.ts +7 -1
- package/dist/src/commands/rebase/patch-loop.js +60 -39
- package/dist/src/commands/rebase/summary.js +1 -1
- package/dist/src/commands/register.js +5 -7
- package/dist/src/commands/reset.js +2 -1
- package/dist/src/commands/resolve.d.ts +16 -13
- package/dist/src/commands/resolve.js +30 -33
- package/dist/src/commands/run.js +33 -35
- package/dist/src/commands/setup-support.js +15 -19
- package/dist/src/commands/setup.js +13 -9
- package/dist/src/commands/status-check.d.ts +6 -6
- package/dist/src/commands/status-json.d.ts +6 -6
- package/dist/src/commands/status-json.js +5 -0
- package/dist/src/commands/status-output.d.ts +5 -5
- package/dist/src/commands/status-output.js +6 -6
- package/dist/src/commands/status-ownership.d.ts +16 -14
- package/dist/src/commands/status-ownership.js +16 -14
- package/dist/src/commands/status.js +42 -34
- package/dist/src/commands/test-build-phase.js +26 -13
- package/dist/src/commands/test-diagnose.js +44 -44
- package/dist/src/commands/test-register.js +1 -0
- package/dist/src/commands/test-run.d.ts +14 -16
- package/dist/src/commands/test-run.js +21 -13
- package/dist/src/commands/test-stale-gate.d.ts +15 -16
- package/dist/src/commands/test-stale-gate.js +15 -16
- package/dist/src/commands/test-verdict.d.ts +20 -3
- package/dist/src/commands/test-verdict.js +30 -0
- package/dist/src/commands/test.js +83 -76
- package/dist/src/commands/token-coverage.js +3 -4
- package/dist/src/commands/token.js +16 -18
- package/dist/src/commands/tree.js +47 -31
- package/dist/src/commands/typecheck.d.ts +10 -12
- package/dist/src/commands/typecheck.js +24 -27
- package/dist/src/commands/verify.js +13 -15
- package/dist/src/commands/watch.js +21 -23
- package/dist/src/commands/wire.js +52 -62
- package/dist/src/core/ast-utils.d.ts +8 -7
- package/dist/src/core/ast-utils.js +8 -7
- package/dist/src/core/branding.d.ts +9 -14
- package/dist/src/core/branding.js +7 -11
- package/dist/src/core/browser-wire.d.ts +1 -1
- package/dist/src/core/browser-wire.js +14 -19
- package/dist/src/core/build-audit-platform.js +25 -33
- package/dist/src/core/build-audit-registration.js +17 -25
- package/dist/src/core/build-audit-resolve.d.ts +15 -17
- package/dist/src/core/build-audit-resolve.js +39 -36
- package/dist/src/core/build-audit-transforms.js +18 -19
- package/dist/src/core/build-audit.js +28 -42
- package/dist/src/core/build-baseline-types.d.ts +29 -35
- package/dist/src/core/build-baseline.d.ts +5 -4
- package/dist/src/core/build-baseline.js +14 -14
- package/dist/src/core/build-overwrite-guard.d.ts +7 -14
- package/dist/src/core/build-overwrite-guard.js +17 -27
- package/dist/src/core/build-prepare.d.ts +2 -2
- package/dist/src/core/build-prepare.js +8 -9
- package/dist/src/core/config-mutate.js +20 -19
- package/dist/src/core/config-state.js +12 -4
- package/dist/src/core/config-validate.js +8 -5
- package/dist/src/core/config.d.ts +5 -8
- package/dist/src/core/config.js +7 -16
- package/dist/src/core/coverage-extend.d.ts +15 -13
- package/dist/src/core/coverage-extend.js +15 -13
- package/dist/src/core/destructive.d.ts +31 -9
- package/dist/src/core/destructive.js +29 -26
- package/dist/src/core/diff-hunks.js +10 -12
- package/dist/src/core/discard-baseline.d.ts +8 -8
- package/dist/src/core/discard-baseline.js +16 -15
- package/dist/src/core/display-state.d.ts +12 -14
- package/dist/src/core/display-state.js +12 -14
- package/dist/src/core/engine-changes.d.ts +2 -4
- package/dist/src/core/engine-changes.js +2 -4
- package/dist/src/core/engine-precondition.d.ts +28 -13
- package/dist/src/core/engine-precondition.js +25 -10
- package/dist/src/core/engine-session-lock.d.ts +47 -21
- package/dist/src/core/engine-session-lock.js +168 -33
- package/dist/src/core/export-moved-code-shape.d.ts +10 -10
- package/dist/src/core/export-moved-code-shape.js +10 -10
- package/dist/src/core/file-lock.d.ts +62 -5
- package/dist/src/core/file-lock.js +196 -59
- package/dist/src/core/firefox-cache.js +1 -3
- package/dist/src/core/firefox-extract.js +10 -14
- package/dist/src/core/firefox.d.ts +7 -5
- package/dist/src/core/firefox.js +4 -5
- package/dist/src/core/furnace-apply-dry-run.d.ts +2 -2
- package/dist/src/core/furnace-apply-dry-run.js +3 -2
- package/dist/src/core/furnace-apply-ftl.d.ts +7 -9
- package/dist/src/core/furnace-apply-ftl.js +16 -13
- package/dist/src/core/furnace-apply-helpers.d.ts +7 -7
- package/dist/src/core/furnace-apply-helpers.js +30 -21
- package/dist/src/core/furnace-apply-overwrite-warn.d.ts +8 -9
- package/dist/src/core/furnace-apply-overwrite-warn.js +8 -9
- package/dist/src/core/furnace-apply-undeploy-actions.d.ts +3 -4
- package/dist/src/core/furnace-apply-undeploy-actions.js +3 -4
- package/dist/src/core/furnace-apply.d.ts +5 -8
- package/dist/src/core/furnace-apply.js +19 -24
- package/dist/src/core/furnace-checksum-utils.d.ts +14 -0
- package/dist/src/core/furnace-checksum-utils.js +16 -0
- package/dist/src/core/furnace-config.d.ts +21 -30
- package/dist/src/core/furnace-config.js +32 -54
- package/dist/src/core/furnace-constants.d.ts +8 -0
- package/dist/src/core/furnace-constants.js +10 -2
- package/dist/src/core/furnace-css-fragments.d.ts +5 -5
- package/dist/src/core/furnace-css-fragments.js +5 -5
- package/dist/src/core/furnace-jsconfig.d.ts +11 -12
- package/dist/src/core/furnace-jsconfig.js +11 -12
- package/dist/src/core/furnace-marker.d.ts +7 -8
- package/dist/src/core/furnace-marker.js +7 -8
- package/dist/src/core/furnace-operation.d.ts +31 -0
- package/dist/src/core/furnace-operation.js +54 -17
- package/dist/src/core/furnace-precondition.d.ts +61 -0
- package/dist/src/core/furnace-precondition.js +67 -0
- package/dist/src/core/furnace-registration-remove.d.ts +5 -6
- package/dist/src/core/furnace-registration-remove.js +6 -7
- package/dist/src/core/furnace-registration-validate.d.ts +13 -0
- package/dist/src/core/furnace-registration-validate.js +20 -1
- package/dist/src/core/furnace-registration.d.ts +5 -6
- package/dist/src/core/furnace-registration.js +10 -12
- package/dist/src/core/furnace-rollback.js +7 -7
- package/dist/src/core/furnace-scanner.d.ts +1 -0
- package/dist/src/core/furnace-scanner.js +1 -2
- package/dist/src/core/furnace-stale-export.d.ts +10 -10
- package/dist/src/core/furnace-stale-export.js +10 -10
- package/dist/src/core/furnace-state-persist.d.ts +6 -8
- package/dist/src/core/furnace-state-persist.js +6 -8
- package/dist/src/core/furnace-validate-accessibility.d.ts +22 -0
- package/dist/src/core/furnace-validate-accessibility.js +310 -1
- package/dist/src/core/furnace-validate-compatibility.js +141 -1
- package/dist/src/core/furnace-validate-helpers.d.ts +5 -116
- package/dist/src/core/furnace-validate-helpers.js +0 -491
- package/dist/src/core/furnace-validate-registration.d.ts +7 -0
- package/dist/src/core/furnace-validate-registration.js +115 -41
- package/dist/src/core/furnace-validate.d.ts +16 -0
- package/dist/src/core/furnace-validate.js +188 -145
- package/dist/src/core/furnace-version-drift.d.ts +10 -11
- package/dist/src/core/furnace-version-drift.js +10 -11
- package/dist/src/core/git-base.d.ts +21 -9
- package/dist/src/core/git-base.js +24 -9
- package/dist/src/core/git-diff.d.ts +20 -9
- package/dist/src/core/git-diff.js +148 -63
- package/dist/src/core/git-file-ops.d.ts +19 -19
- package/dist/src/core/git-file-ops.js +19 -19
- package/dist/src/core/git-readonly-index.d.ts +53 -19
- package/dist/src/core/git-readonly-index.js +75 -31
- package/dist/src/core/git-status.d.ts +5 -6
- package/dist/src/core/git-status.js +5 -6
- package/dist/src/core/git.d.ts +9 -10
- package/dist/src/core/git.js +57 -71
- package/dist/src/core/license-headers.d.ts +17 -23
- package/dist/src/core/license-headers.js +17 -23
- package/dist/src/core/mach-build-artifacts.d.ts +9 -9
- package/dist/src/core/mach-build-artifacts.js +57 -19
- package/dist/src/core/mach-error-hints.js +42 -29
- package/dist/src/core/mach-known-noise-filter.d.ts +15 -16
- package/dist/src/core/mach-known-noise-filter.js +18 -20
- package/dist/src/core/mach-mozconfig.d.ts +14 -0
- package/dist/src/core/mach-mozconfig.js +30 -0
- package/dist/src/core/mach-resource-shim.d.ts +43 -58
- package/dist/src/core/mach-resource-shim.js +50 -67
- package/dist/src/core/mach.d.ts +37 -41
- package/dist/src/core/mach.js +50 -51
- package/dist/src/core/marionette-port.d.ts +36 -1
- package/dist/src/core/marionette-port.js +96 -31
- package/dist/src/core/marionette-preflight.d.ts +8 -12
- package/dist/src/core/marionette-preflight.js +86 -52
- package/dist/src/core/mochitest-server-port.d.ts +42 -0
- package/dist/src/core/mochitest-server-port.js +119 -0
- package/dist/src/core/module-resolution-preflight.d.ts +9 -11
- package/dist/src/core/module-resolution-preflight.js +9 -11
- package/dist/src/core/moz-manifest-helpers.d.ts +79 -0
- package/dist/src/core/moz-manifest-helpers.js +163 -0
- package/dist/src/core/{manifest-register.d.ts → moz-manifest-register.d.ts} +1 -1
- package/dist/src/core/{manifest-register.js → moz-manifest-register.js} +13 -28
- package/dist/src/core/{manifest-rules.d.ts → moz-manifest-rules.d.ts} +10 -1
- package/dist/src/core/{manifest-rules.js → moz-manifest-rules.js} +31 -24
- package/dist/src/core/{manifest-tokenizers.d.ts → moz-manifest-tokenizers.d.ts} +3 -5
- package/dist/src/core/{manifest-tokenizers.js → moz-manifest-tokenizers.js} +9 -12
- package/dist/src/core/ownership-table.d.ts +14 -16
- package/dist/src/core/ownership-table.js +14 -16
- package/dist/src/core/parser-fallback.d.ts +15 -1
- package/dist/src/core/parser-fallback.js +22 -3
- package/dist/src/core/patch-apply-fuzz.d.ts +10 -12
- package/dist/src/core/patch-apply-fuzz.js +15 -18
- package/dist/src/core/patch-apply.d.ts +13 -34
- package/dist/src/core/patch-apply.js +16 -76
- package/dist/src/core/patch-artifact-normalize.d.ts +13 -15
- package/dist/src/core/patch-artifact-normalize.js +13 -15
- package/dist/src/core/patch-export-metadata.d.ts +12 -2
- package/dist/src/core/patch-export-metadata.js +13 -4
- package/dist/src/core/patch-export-update.d.ts +13 -1
- package/dist/src/core/patch-export-update.js +25 -8
- package/dist/src/core/patch-export.d.ts +15 -8
- package/dist/src/core/patch-export.js +10 -11
- package/dist/src/core/patch-files.d.ts +5 -6
- package/dist/src/core/patch-files.js +5 -6
- package/dist/src/core/patch-identifier-suggest.d.ts +7 -12
- package/dist/src/core/patch-identifier-suggest.js +14 -19
- package/dist/src/core/patch-lint-binary.d.ts +51 -0
- package/dist/src/core/patch-lint-binary.js +39 -0
- package/dist/src/core/{lint-cache.d.ts → patch-lint-cache.d.ts} +5 -5
- package/dist/src/core/{lint-cache.js → patch-lint-cache.js} +21 -23
- package/dist/src/core/patch-lint-checkjs.d.ts +16 -16
- package/dist/src/core/patch-lint-checkjs.js +41 -20
- package/dist/src/core/patch-lint-chrome-jsdoc.d.ts +6 -9
- package/dist/src/core/patch-lint-chrome-jsdoc.js +10 -14
- package/dist/src/core/patch-lint-creators.d.ts +8 -8
- package/dist/src/core/patch-lint-creators.js +8 -8
- package/dist/src/core/patch-lint-cross.d.ts +17 -20
- package/dist/src/core/patch-lint-cross.js +16 -16
- package/dist/src/core/patch-lint-css.d.ts +3 -5
- package/dist/src/core/patch-lint-css.js +17 -22
- package/dist/src/core/patch-lint-diff-tag.d.ts +14 -16
- package/dist/src/core/patch-lint-diff-tag.js +14 -16
- package/dist/src/core/patch-lint-jsdoc.d.ts +7 -8
- package/dist/src/core/patch-lint-jsdoc.js +26 -19
- package/dist/src/core/patch-lint-mozbuild.js +20 -20
- package/dist/src/core/patch-lint-observer.d.ts +7 -7
- package/dist/src/core/patch-lint-observer.js +7 -7
- package/dist/src/core/patch-lint.d.ts +38 -29
- package/dist/src/core/patch-lint.js +89 -121
- package/dist/src/core/patch-lock.d.ts +1 -1
- package/dist/src/core/patch-lock.js +45 -9
- package/dist/src/core/patch-manifest-consistency.d.ts +57 -6
- package/dist/src/core/patch-manifest-consistency.js +111 -40
- package/dist/src/core/patch-manifest-files-affected.d.ts +43 -0
- package/dist/src/core/patch-manifest-files-affected.js +77 -0
- package/dist/src/core/patch-manifest-io.js +14 -18
- package/dist/src/core/patch-manifest-resolve.d.ts +9 -12
- package/dist/src/core/patch-manifest-resolve.js +9 -12
- package/dist/src/core/patch-manifest-validate.d.ts +6 -1
- package/dist/src/core/patch-manifest-validate.js +25 -13
- package/dist/src/core/patch-manifest.d.ts +2 -1
- package/dist/src/core/patch-manifest.js +2 -1
- package/dist/src/core/patch-parse.d.ts +24 -13
- package/dist/src/core/patch-parse.js +19 -14
- package/dist/src/core/patch-policy.d.ts +2 -2
- package/dist/src/core/patch-policy.js +2 -2
- package/dist/src/core/patch-registration-refs.d.ts +7 -8
- package/dist/src/core/patch-registration-refs.js +9 -9
- package/dist/src/core/process-cpu-time.d.ts +12 -0
- package/dist/src/core/process-cpu-time.js +52 -0
- package/dist/src/core/rebase-session.d.ts +38 -26
- package/dist/src/core/rebase-session.js +55 -28
- package/dist/src/core/register-browser-content.js +9 -74
- package/dist/src/core/{register-test-manifest.js → register-browser-test.js} +7 -42
- package/dist/src/core/register-module.js +10 -43
- package/dist/src/core/register-scaffold.d.ts +8 -8
- package/dist/src/core/register-scaffold.js +10 -10
- package/dist/src/core/register-shared-css.d.ts +9 -12
- package/dist/src/core/register-shared-css.js +17 -87
- package/dist/src/core/register-xpcshell-test.d.ts +6 -7
- package/dist/src/core/register-xpcshell-test.js +7 -8
- package/dist/src/core/shared-ftl.js +12 -4
- package/dist/src/core/state-file.d.ts +45 -0
- package/dist/src/core/state-file.js +34 -0
- package/dist/src/core/status-binary.d.ts +18 -11
- package/dist/src/core/status-binary.js +19 -12
- package/dist/src/core/status-classify.d.ts +30 -36
- package/dist/src/core/status-classify.js +21 -26
- package/dist/src/core/test-changed-prefs.d.ts +30 -0
- package/dist/src/core/test-changed-prefs.js +78 -0
- package/dist/src/core/test-harness-crash.d.ts +25 -63
- package/dist/src/core/test-harness-crash.js +121 -142
- package/dist/src/core/test-harness-signature.d.ts +18 -0
- package/dist/src/core/test-harness-signature.js +13 -0
- package/dist/src/core/test-path-scope.d.ts +12 -15
- package/dist/src/core/test-path-scope.js +15 -18
- package/dist/src/core/test-stale-check.d.ts +17 -17
- package/dist/src/core/test-stale-check.js +50 -43
- package/dist/src/core/test-stall-triage.d.ts +51 -0
- package/dist/src/core/test-stall-triage.js +126 -0
- package/dist/src/core/token-category.js +66 -14
- package/dist/src/core/token-coverage.js +9 -9
- package/dist/src/core/token-dark-mode.d.ts +9 -11
- package/dist/src/core/token-dark-mode.js +29 -13
- package/dist/src/core/token-docs.d.ts +2 -0
- package/dist/src/core/token-docs.js +3 -1
- package/dist/src/core/token-manager.d.ts +10 -2
- package/dist/src/core/token-manager.js +17 -8
- package/dist/src/core/token-scaffold.d.ts +7 -7
- package/dist/src/core/token-scaffold.js +7 -7
- package/dist/src/core/token-variant.d.ts +4 -4
- package/dist/src/core/token-variant.js +28 -6
- package/dist/src/core/toolchain-preflight.d.ts +20 -21
- package/dist/src/core/toolchain-preflight.js +28 -31
- package/dist/src/core/tree-guard.js +12 -2
- package/dist/src/core/tree-store.d.ts +41 -27
- package/dist/src/core/tree-store.js +68 -43
- package/dist/src/core/typecheck-shim.d.ts +23 -24
- package/dist/src/core/typecheck-shim.js +23 -24
- package/dist/src/core/typecheck.js +15 -9
- package/dist/src/core/wire-destroy.d.ts +3 -3
- package/dist/src/core/wire-destroy.js +7 -15
- package/dist/src/core/wire-dom-fragment.d.ts +9 -17
- package/dist/src/core/wire-dom-fragment.js +11 -44
- package/dist/src/core/wire-init.d.ts +2 -3
- package/dist/src/core/wire-init.js +8 -19
- package/dist/src/core/wire-subscript.d.ts +2 -3
- package/dist/src/core/wire-subscript.js +4 -13
- package/dist/src/core/wire-utils.d.ts +7 -8
- package/dist/src/core/wire-utils.js +7 -8
- package/dist/src/core/xpcshell-appdir.d.ts +38 -45
- package/dist/src/core/xpcshell-appdir.js +38 -45
- package/dist/src/errors/base.d.ts +37 -16
- package/dist/src/errors/base.js +37 -16
- package/dist/src/errors/build.d.ts +13 -1
- package/dist/src/errors/build.js +14 -2
- package/dist/src/errors/codes.d.ts +16 -0
- package/dist/src/errors/codes.js +16 -0
- package/dist/src/errors/furnace.d.ts +4 -4
- package/dist/src/errors/git.d.ts +6 -10
- package/dist/src/errors/git.js +6 -10
- package/dist/src/errors/patch.d.ts +7 -7
- package/dist/src/errors/patch.js +7 -7
- package/dist/src/errors/rebase.d.ts +4 -4
- package/dist/src/errors/rebase.js +4 -4
- package/dist/src/index.d.ts +7 -1
- package/dist/src/index.js +5 -0
- package/dist/src/types/commands/index.d.ts +2 -2
- package/dist/src/types/commands/options.d.ts +111 -70
- package/dist/src/types/commands/patches.d.ts +24 -34
- package/dist/src/types/commands/project.d.ts +5 -34
- package/dist/src/types/config.d.ts +25 -23
- package/dist/src/types/config.js +6 -1
- package/dist/src/types/furnace.d.ts +37 -13
- package/dist/src/types/furnace.js +19 -1
- package/dist/src/types/index.d.ts +3 -3
- package/dist/src/utils/build-info.d.ts +7 -8
- package/dist/src/utils/build-info.js +12 -11
- package/dist/src/utils/errors.d.ts +6 -8
- package/dist/src/utils/errors.js +6 -8
- package/dist/src/utils/logger.d.ts +27 -10
- package/dist/src/utils/logger.js +28 -16
- package/dist/src/utils/machine-output.d.ts +12 -0
- package/dist/src/utils/machine-output.js +33 -0
- package/dist/src/utils/options.d.ts +31 -14
- package/dist/src/utils/options.js +86 -30
- package/dist/src/utils/paths.d.ts +10 -8
- package/dist/src/utils/paths.js +10 -8
- package/dist/src/utils/platform.d.ts +0 -25
- package/dist/src/utils/platform.js +1 -42
- package/dist/src/utils/process-group.d.ts +9 -4
- package/dist/src/utils/process-group.js +19 -5
- package/dist/src/utils/process.d.ts +42 -7
- package/dist/src/utils/process.js +44 -17
- package/dist/src/utils/ps-duration.d.ts +31 -0
- package/dist/src/utils/ps-duration.js +70 -0
- package/dist/src/utils/regex.d.ts +9 -10
- package/dist/src/utils/regex.js +9 -10
- package/dist/src/utils/validation.d.ts +58 -28
- package/dist/src/utils/validation.js +60 -33
- package/package.json +3 -3
- package/dist/src/commands/doctor/post-rebase-audit.d.ts +0 -2
- package/dist/src/commands/doctor-furnace-manifest-sync.d.ts +0 -18
- package/dist/src/core/manifest-helpers.d.ts +0 -38
- package/dist/src/core/manifest-helpers.js +0 -120
- /package/dist/src/core/{lint-projection.d.ts → patch-lint-projection.d.ts} +0 -0
- /package/dist/src/core/{lint-projection.js → patch-lint-projection.js} +0 -0
- /package/dist/src/core/{register-test-manifest.d.ts → register-browser-test.d.ts} +0 -0
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
// SPDX-License-Identifier: EUPL-1.2
|
|
2
2
|
import { join } from 'node:path';
|
|
3
3
|
import { pathExists, readText } from '../utils/fs.js';
|
|
4
|
-
import {
|
|
4
|
+
import { createIssue } from './furnace-validate-helpers.js';
|
|
5
5
|
/**
|
|
6
6
|
* Validates accessibility patterns in a component's .mjs file.
|
|
7
7
|
* Checks for ARIA roles, keyboard handlers, l10n, and focus delegation.
|
|
8
8
|
*
|
|
9
|
+
* @param componentDir - Directory holding the component's sources
|
|
10
|
+
* @param tagName - Custom element tag name, used to locate `<tagName>.mjs`
|
|
9
11
|
* @param customConfig - When the component is custom, its matching entry
|
|
10
12
|
* from `furnace.json`. Used to skip the `no-keyboard-handler` warning
|
|
11
13
|
* when the component declares keyboard coverage either explicitly
|
|
@@ -55,3 +57,310 @@ export async function validateAccessibility(componentDir, tagName, customConfig)
|
|
|
55
57
|
}
|
|
56
58
|
return issues;
|
|
57
59
|
}
|
|
60
|
+
/** Detects whether template or script content assigns an ARIA role. */
|
|
61
|
+
function hasAriaRole(content) {
|
|
62
|
+
return (/role\s*=\s*["']/.test(content) ||
|
|
63
|
+
/\.role\s*=/.test(content) ||
|
|
64
|
+
/setAttribute\(\s*["']role["']/.test(content));
|
|
65
|
+
}
|
|
66
|
+
/** Detects generic elements being used as custom interactive controls. */
|
|
67
|
+
function hasGenericInteractiveElement(content) {
|
|
68
|
+
return /<(div|span)\b(?=[^>]*(?:@click|@key(?:down|press|up)|\btabindex\s*=|\.onclick\s*=))/i.test(content);
|
|
69
|
+
}
|
|
70
|
+
/** Detects a positive tabindex value, which disrupts natural tab order. */
|
|
71
|
+
function hasPositiveTabindex(content) {
|
|
72
|
+
const match = content.match(/tabindex\s*=\s*["']?(\d+)/g);
|
|
73
|
+
if (!match)
|
|
74
|
+
return false;
|
|
75
|
+
return match.some((m) => {
|
|
76
|
+
const value = /(\d+)/.exec(m)?.[1];
|
|
77
|
+
return value !== undefined && parseInt(value, 10) > 0;
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Collects `[start, end)` spans of `<label>…</label>` elements whose content
|
|
82
|
+
* includes actual label text. Labels cannot nest per HTML, so the non-greedy
|
|
83
|
+
* inner match is sound. A span qualifies only when the inner content minus
|
|
84
|
+
* tags still contains non-whitespace — an empty or tag-only wrapper provides
|
|
85
|
+
* no accessible name. A `${…}` Lit binding counts as label text: dynamic
|
|
86
|
+
* (usually localized) text is a legitimate accessible name.
|
|
87
|
+
*/
|
|
88
|
+
function collectLabelledSpans(content) {
|
|
89
|
+
const spans = [];
|
|
90
|
+
const labelPattern = /<label\b[^>]*>([\s\S]*?)<\/label>/gi;
|
|
91
|
+
let labelMatch;
|
|
92
|
+
while ((labelMatch = labelPattern.exec(content)) !== null) {
|
|
93
|
+
const inner = labelMatch[1] ?? '';
|
|
94
|
+
if (/\S/.test(inner.replace(/<[^>]*>/g, ' '))) {
|
|
95
|
+
spans.push({ start: labelMatch.index, end: labelMatch.index + labelMatch[0].length });
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
return spans;
|
|
99
|
+
}
|
|
100
|
+
/** Detects form inputs without associated labels. */
|
|
101
|
+
function hasUnlabelledFormInput(content) {
|
|
102
|
+
// Look for <input> or <select> or <textarea> without aria-label, aria-labelledby, or id
|
|
103
|
+
// (id implies an external <label for="..."> could exist). A control wrapped
|
|
104
|
+
// in a <label> that carries actual text is implicitly associated and is
|
|
105
|
+
// exempt as well.
|
|
106
|
+
const labelledSpans = collectLabelledSpans(content);
|
|
107
|
+
const inputPattern = /<(input|select|textarea)\b([^>]*)>/gi;
|
|
108
|
+
let inputMatch;
|
|
109
|
+
while ((inputMatch = inputPattern.exec(content)) !== null) {
|
|
110
|
+
const attrs = inputMatch[2] ?? '';
|
|
111
|
+
if (/aria-label\s*=/.test(attrs) ||
|
|
112
|
+
/aria-labelledby\s*=/.test(attrs) ||
|
|
113
|
+
/\bid\s*=/.test(attrs) ||
|
|
114
|
+
/type\s*=\s*["']hidden["']/i.test(attrs)) {
|
|
115
|
+
continue;
|
|
116
|
+
}
|
|
117
|
+
const index = inputMatch.index;
|
|
118
|
+
if (labelledSpans.some((span) => index >= span.start && index < span.end)) {
|
|
119
|
+
continue;
|
|
120
|
+
}
|
|
121
|
+
return true;
|
|
122
|
+
}
|
|
123
|
+
return false;
|
|
124
|
+
}
|
|
125
|
+
/** Detects Lit-style template click handlers. */
|
|
126
|
+
function hasTemplateClickHandler(content) {
|
|
127
|
+
return /@click\s*=\s*\$\{/.test(content);
|
|
128
|
+
}
|
|
129
|
+
/** Detects Lit-style template keyboard handlers. */
|
|
130
|
+
function hasTemplateKeyboardHandler(content) {
|
|
131
|
+
return /@key(down|press|up)\s*=\s*\$\{/.test(content);
|
|
132
|
+
}
|
|
133
|
+
/**
|
|
134
|
+
* Native HTML elements that dispatch `click` on Enter and Space via the
|
|
135
|
+
* platform. Attaching `@click` to these is NOT a keyboard-a11y bug because
|
|
136
|
+
* the browser already handles the keyboard activation path — a duplicate
|
|
137
|
+
* `@keydown`/`@keypress` handler would usually double-fire the action.
|
|
138
|
+
*
|
|
139
|
+
* `<a>` is accepted only when an `href` attribute is present; bare `<a>` is
|
|
140
|
+
* non-interactive and is treated as synthetic.
|
|
141
|
+
*/
|
|
142
|
+
const NATIVE_CLICK_INTERACTIVE_TAGS = new Set([
|
|
143
|
+
'button',
|
|
144
|
+
'input',
|
|
145
|
+
'select',
|
|
146
|
+
'textarea',
|
|
147
|
+
'summary',
|
|
148
|
+
'details',
|
|
149
|
+
// Mozilla widgets that extend the native pattern and keep Enter/Space activation.
|
|
150
|
+
'moz-button',
|
|
151
|
+
'moz-toggle',
|
|
152
|
+
'moz-checkbox',
|
|
153
|
+
'moz-radio',
|
|
154
|
+
'moz-radio-group',
|
|
155
|
+
'moz-menulist',
|
|
156
|
+
]);
|
|
157
|
+
/**
|
|
158
|
+
* Returns true when `customConfig` declares that the component's keyboard
|
|
159
|
+
* activation path is covered by a wrapped native-interactive inner element,
|
|
160
|
+
* either through an explicit opt-out (`keyboardCovered: true`) or by
|
|
161
|
+
* composing a tag that lives in {@link NATIVE_CLICK_INTERACTIVE_TAGS}.
|
|
162
|
+
*
|
|
163
|
+
* Uses `.some` rather than `.every` so that a wrapper composing e.g.
|
|
164
|
+
* `['moz-button', 'my-tooltip']` still skips the warning: the keyboard
|
|
165
|
+
* activation path flows through the button, even if other composed
|
|
166
|
+
* children are synthetic.
|
|
167
|
+
*/
|
|
168
|
+
function isKeyboardCoveredByComposition(customConfig) {
|
|
169
|
+
if (!customConfig)
|
|
170
|
+
return false;
|
|
171
|
+
if (customConfig.keyboardCovered === true)
|
|
172
|
+
return true;
|
|
173
|
+
const composes = customConfig.composes ?? [];
|
|
174
|
+
return composes.some((tag) => NATIVE_CLICK_INTERACTIVE_TAGS.has(tag));
|
|
175
|
+
}
|
|
176
|
+
/**
|
|
177
|
+
* Returns true when `content` has at least one `@click=${...}` handler on a
|
|
178
|
+
* *synthetic* interactive element (e.g. `<div @click>`), which lacks native
|
|
179
|
+
* keyboard activation and therefore needs an explicit key handler for
|
|
180
|
+
* Enter/Space. Returns false when every `@click` handler sits on a native
|
|
181
|
+
* interactive element — those already fire `click` on keyboard activation.
|
|
182
|
+
*/
|
|
183
|
+
function hasTemplateClickOnSyntheticInteractive(content) {
|
|
184
|
+
const pattern = /@click\s*=\s*\$\{/g;
|
|
185
|
+
let match;
|
|
186
|
+
while ((match = pattern.exec(content)) !== null) {
|
|
187
|
+
if (isClickOnSyntheticInteractive(content, match.index)) {
|
|
188
|
+
return true;
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
return false;
|
|
192
|
+
}
|
|
193
|
+
/**
|
|
194
|
+
* Given the offset of an `@click=${...}` occurrence, walks backwards to find
|
|
195
|
+
* the opening `<tag` that owns it and decides whether that tag is a native
|
|
196
|
+
* interactive element (no warning needed) or a synthetic one (warning needed).
|
|
197
|
+
*/
|
|
198
|
+
function isClickOnSyntheticInteractive(content, clickIndex) {
|
|
199
|
+
// Find the nearest preceding `<` that starts a tag (skip `</` closers).
|
|
200
|
+
let i = clickIndex - 1;
|
|
201
|
+
let tagOpenIndex = -1;
|
|
202
|
+
while (i >= 0) {
|
|
203
|
+
if (content[i] === '<' && content[i + 1] !== '/') {
|
|
204
|
+
tagOpenIndex = i;
|
|
205
|
+
break;
|
|
206
|
+
}
|
|
207
|
+
// A `>` before an unclosed `<` means we're outside the attribute list,
|
|
208
|
+
// which shouldn't happen for a well-formed template but we defensively
|
|
209
|
+
// treat it as synthetic to preserve the prior-behaviour warning.
|
|
210
|
+
if (content[i] === '>') {
|
|
211
|
+
return true;
|
|
212
|
+
}
|
|
213
|
+
i--;
|
|
214
|
+
}
|
|
215
|
+
if (tagOpenIndex < 0)
|
|
216
|
+
return true;
|
|
217
|
+
const tagMatch = /^<([a-zA-Z][a-zA-Z0-9-]*)/.exec(content.slice(tagOpenIndex));
|
|
218
|
+
if (!tagMatch?.[1])
|
|
219
|
+
return true;
|
|
220
|
+
const tagName = tagMatch[1].toLowerCase();
|
|
221
|
+
if (tagName === 'a') {
|
|
222
|
+
// Bare <a> (no href) is non-interactive; require a keyboard handler.
|
|
223
|
+
// Look forward from the tag open for the closing `>` and scan the
|
|
224
|
+
// attribute text in between for an href attribute.
|
|
225
|
+
const tagEnd = content.indexOf('>', tagOpenIndex);
|
|
226
|
+
if (tagEnd < 0)
|
|
227
|
+
return true;
|
|
228
|
+
const attrs = content.slice(tagOpenIndex, tagEnd);
|
|
229
|
+
return !/\shref\s*=/.test(attrs);
|
|
230
|
+
}
|
|
231
|
+
return !NATIVE_CLICK_INTERACTIVE_TAGS.has(tagName);
|
|
232
|
+
}
|
|
233
|
+
/** ASCII punctuation that reads as decoration rather than localizable text. */
|
|
234
|
+
const SYMBOL_ONLY_ASCII = '+-*=<>|/\\^~@#&!?%';
|
|
235
|
+
/**
|
|
236
|
+
* Reports whether `text` is decoration (arrows, maths, emoji, bare
|
|
237
|
+
* punctuation) rather than user-facing prose that needs localizing.
|
|
238
|
+
*
|
|
239
|
+
* The letter/number/mark test comes first and is script-agnostic — a rule
|
|
240
|
+
* keyed on `code > 0xff` would classify every code point above U+00FF as a
|
|
241
|
+
* symbol, exempting all CJK, Cyrillic, Greek, Arabic, Hebrew, Devanagari and
|
|
242
|
+
* Thai text from a **localization** validator.
|
|
243
|
+
*
|
|
244
|
+
* Combining marks are deliberately NOT counted as text: the check is an
|
|
245
|
+
* `every()`, so a script's base letters already disqualify the string, and
|
|
246
|
+
* treating marks as text would flag emoji carrying a variation selector
|
|
247
|
+
* (U+FE0F is category Mn — `⚙️` is U+2699 + U+FE0F).
|
|
248
|
+
*
|
|
249
|
+
* High code points that are not letters — emoji, arrows, CJK punctuation —
|
|
250
|
+
* stay exempt, which is the rule's intent.
|
|
251
|
+
*
|
|
252
|
+
* A handful of emoji are ALSO Unicode letters, and the letter test alone
|
|
253
|
+
* gets them wrong: U+2139 (`ℹ`, the base of the common `ℹ️`) is category Ll,
|
|
254
|
+
* so `\p{L}` would classify an info badge as prose needing localization.
|
|
255
|
+
* Non-ASCII code points carrying `Emoji` are therefore read as symbols
|
|
256
|
+
* before the letter test. The ASCII guard matters: `\p{Emoji}` also covers
|
|
257
|
+
* the digits `0`-`9`, `#` and `*`, which must keep their classification.
|
|
258
|
+
*/
|
|
259
|
+
function isSymbolOnlyText(text) {
|
|
260
|
+
return Array.from(text).every((character) => {
|
|
261
|
+
const code = character.codePointAt(0) ?? 0;
|
|
262
|
+
if (code > 0x7f && /\p{Emoji}/u.test(character))
|
|
263
|
+
return true;
|
|
264
|
+
if (/[\p{L}\p{N}]/u.test(character))
|
|
265
|
+
return false;
|
|
266
|
+
return code > 0xff || SYMBOL_ONLY_ASCII.includes(character);
|
|
267
|
+
});
|
|
268
|
+
}
|
|
269
|
+
function isWithinLocalizedElement(content, matchIndex) {
|
|
270
|
+
const contentBefore = content.slice(0, matchIndex + 1);
|
|
271
|
+
const lastTagOpen = contentBefore.lastIndexOf('<');
|
|
272
|
+
if (lastTagOpen === -1) {
|
|
273
|
+
return false;
|
|
274
|
+
}
|
|
275
|
+
const tagContent = contentBefore.slice(lastTagOpen, matchIndex + 1);
|
|
276
|
+
return /data-l10n-id\s*=/.test(tagContent);
|
|
277
|
+
}
|
|
278
|
+
/**
|
|
279
|
+
* Detects hardcoded user-visible template text that should usually be
|
|
280
|
+
* localized.
|
|
281
|
+
*
|
|
282
|
+
* Scoped to three positive contexts rather than scanning the whole file,
|
|
283
|
+
* because a bare `>…<` regex catches JS comparisons (`if (x > 0 && y <
|
|
284
|
+
* 100)`), diagnostic strings (`console.error("Failed <id> lookup")`), and
|
|
285
|
+
* identifier literals that are never shown to a user. Only matches that
|
|
286
|
+
* actually enter a UI render path count:
|
|
287
|
+
*
|
|
288
|
+
* 1. Content inside a Lit `` html`…` `` tagged template literal.
|
|
289
|
+
* 2. The string literal on the RHS of `.textContent = "…"` or
|
|
290
|
+
* `.innerHTML = "…"`.
|
|
291
|
+
* 3. The string literal assigned to an XUL-widget `label=`,
|
|
292
|
+
* `title=`, or `tooltiptext=` attribute when constructing DOM in JS.
|
|
293
|
+
*
|
|
294
|
+
* A file-wide `// furnace-ignore: hardcoded-text` comment suppresses all
|
|
295
|
+
* findings (matches the pre-existing escape hatch).
|
|
296
|
+
*/
|
|
297
|
+
export function containsHardcodedTemplateText(content) {
|
|
298
|
+
if (/furnace-ignore:\s*hardcoded-text/.test(content)) {
|
|
299
|
+
return false;
|
|
300
|
+
}
|
|
301
|
+
return (hasFlaggedTextInLitTemplates(content) ||
|
|
302
|
+
hasFlaggedTextInDomAssignment(content) ||
|
|
303
|
+
hasFlaggedTextInXulAttribute(content));
|
|
304
|
+
}
|
|
305
|
+
function isFlaggableText(text) {
|
|
306
|
+
const trimmed = text.trim();
|
|
307
|
+
if (!trimmed)
|
|
308
|
+
return false;
|
|
309
|
+
if (/\$\{/.test(trimmed))
|
|
310
|
+
return false;
|
|
311
|
+
if (Array.from(trimmed).length <= 1)
|
|
312
|
+
return false;
|
|
313
|
+
if (isSymbolOnlyText(trimmed))
|
|
314
|
+
return false;
|
|
315
|
+
return true;
|
|
316
|
+
}
|
|
317
|
+
function hasFlaggedTextInLitTemplates(content) {
|
|
318
|
+
// Match `html\`…\`` regions, anchored on a non-identifier char before `html`
|
|
319
|
+
// so substrings like `otherhtml` do not spuriously open a template.
|
|
320
|
+
const htmlPattern = /(?:^|[^a-zA-Z0-9_$])html`([\s\S]*?)`/g;
|
|
321
|
+
let litMatch;
|
|
322
|
+
while ((litMatch = htmlPattern.exec(content)) !== null) {
|
|
323
|
+
const region = litMatch[1] ?? '';
|
|
324
|
+
const textPattern = />([^<$\s][^<$]*)</g;
|
|
325
|
+
let textMatch;
|
|
326
|
+
while ((textMatch = textPattern.exec(region)) !== null) {
|
|
327
|
+
const text = textMatch[1] ?? '';
|
|
328
|
+
if (!isFlaggableText(text))
|
|
329
|
+
continue;
|
|
330
|
+
if (isWithinLocalizedElement(region, textMatch.index))
|
|
331
|
+
continue;
|
|
332
|
+
return true;
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
return false;
|
|
336
|
+
}
|
|
337
|
+
function hasFlaggedTextInDomAssignment(content) {
|
|
338
|
+
// `<expr>.textContent = "abc"` and `<expr>.innerHTML = "abc"` — these are
|
|
339
|
+
// user-visible render paths. Template-literal RHS is excluded (usually
|
|
340
|
+
// dynamic), matching the `${` guard used elsewhere in this helper.
|
|
341
|
+
const assignPattern = /\.(?:textContent|innerHTML)\s*=\s*(["'])((?:\\.|(?!\1).)*)\1/g;
|
|
342
|
+
let match;
|
|
343
|
+
while ((match = assignPattern.exec(content)) !== null) {
|
|
344
|
+
const text = match[2] ?? '';
|
|
345
|
+
if (isFlaggableText(text))
|
|
346
|
+
return true;
|
|
347
|
+
}
|
|
348
|
+
return false;
|
|
349
|
+
}
|
|
350
|
+
function hasFlaggedTextInXulAttribute(content) {
|
|
351
|
+
// Assignments like `node.setAttribute("label", "Save")` or JS-built XUL
|
|
352
|
+
// attributes `label="…"` / `title="…"` / `tooltiptext="…"` in template
|
|
353
|
+
// literals outside Lit blocks. Covers DOM built via createXULElement.
|
|
354
|
+
const setAttrPattern = /setAttribute\s*\(\s*["'](?:label|title|tooltiptext)["']\s*,\s*(["'])((?:\\.|(?!\1).)*)\1/g;
|
|
355
|
+
let setAttrMatch;
|
|
356
|
+
while ((setAttrMatch = setAttrPattern.exec(content)) !== null) {
|
|
357
|
+
const text = setAttrMatch[2] ?? '';
|
|
358
|
+
if (isFlaggableText(text))
|
|
359
|
+
return true;
|
|
360
|
+
}
|
|
361
|
+
return false;
|
|
362
|
+
}
|
|
363
|
+
/** Detects whether a component opts into shadow-root focus delegation. */
|
|
364
|
+
function hasDelegatesFocusEnabled(content) {
|
|
365
|
+
return /shadowRootOptions[\s\S]*?delegatesFocus\s*:\s*true/.test(content);
|
|
366
|
+
}
|
|
@@ -2,7 +2,8 @@
|
|
|
2
2
|
import { join } from 'node:path';
|
|
3
3
|
import { pathExists, readText } from '../utils/fs.js';
|
|
4
4
|
import { escapeRegex, hasRawCssColors } from '../utils/regex.js';
|
|
5
|
-
import {
|
|
5
|
+
import { getProjectPaths } from './config.js';
|
|
6
|
+
import { createIssue } from './furnace-validate-helpers.js';
|
|
6
7
|
async function validateMjsCompatibility(mjsPath, tagName, isLibrary) {
|
|
7
8
|
if (!(await pathExists(mjsPath)))
|
|
8
9
|
return [];
|
|
@@ -153,3 +154,142 @@ export async function validateCompatibility(componentDir, tagName, type, config,
|
|
|
153
154
|
}
|
|
154
155
|
return issues;
|
|
155
156
|
}
|
|
157
|
+
/** Removes CSS block comments before running simple string-based checks. */
|
|
158
|
+
function stripCssBlockComments(content) {
|
|
159
|
+
return content.replace(/\/\*[\s\S]*?\*\//g, '');
|
|
160
|
+
}
|
|
161
|
+
/** Detects relative ES module imports in a component module file. */
|
|
162
|
+
function hasRelativeModuleImport(mjsContent) {
|
|
163
|
+
return (/^\s*import\s.*from\s+["']\.\.?\//m.test(mjsContent) ||
|
|
164
|
+
/^\s*import\s+["']\.\.?\//m.test(mjsContent));
|
|
165
|
+
}
|
|
166
|
+
/** Detects whether a module defines a custom element at runtime. */
|
|
167
|
+
function hasCustomElementDefineCall(mjsContent) {
|
|
168
|
+
return /customElements\.define\s*\(/.test(mjsContent);
|
|
169
|
+
}
|
|
170
|
+
/**
|
|
171
|
+
* Detects whether the module's `customElements.define(...)` call includes a
|
|
172
|
+
* literal `extends:` option (third argument). That shape is the marker for
|
|
173
|
+
* a customized built-in element — the class extends a specific
|
|
174
|
+
* `HTMLxxxElement` rather than the autonomous `MozLitElement` path.
|
|
175
|
+
*
|
|
176
|
+
* Firefox's own widgets use this pattern for toolkit anchors (e.g.
|
|
177
|
+
* `moz-support-link` extends `HTMLAnchorElement` with
|
|
178
|
+
* `customElements.define("moz-support-link", ..., { extends: "a" })`), and
|
|
179
|
+
* the validator's `not-moz-lit-element` check must allow them through or
|
|
180
|
+
* `furnace override` of a valid upstream component fails its own
|
|
181
|
+
* `furnace validate` pass with nothing the operator can fix.
|
|
182
|
+
*/
|
|
183
|
+
function hasCustomElementExtendsOption(mjsContent) {
|
|
184
|
+
// Match `customElements.define(..., { ..., extends: "..." })`. Tolerant of
|
|
185
|
+
// whitespace, line breaks, and other object properties. The `[^)]*` stops
|
|
186
|
+
// the inner greedy match at the closing define call paren so a later
|
|
187
|
+
// `define` call on a different custom element in the same module does
|
|
188
|
+
// not bleed its options in.
|
|
189
|
+
return /customElements\.define\s*\([^)]*\bextends\s*:\s*["'`]/.test(mjsContent);
|
|
190
|
+
}
|
|
191
|
+
/**
|
|
192
|
+
* Checks whether a declared component class extends a valid element base.
|
|
193
|
+
*
|
|
194
|
+
* Two shapes are accepted:
|
|
195
|
+
*
|
|
196
|
+
* 1. Autonomous custom element: `class X extends MozLitElement` — the
|
|
197
|
+
* default FireForge pattern for fork-authored components and most
|
|
198
|
+
* toolkit widgets.
|
|
199
|
+
* 2. Customized built-in: `class X extends HTML<Something>Element` paired
|
|
200
|
+
* with a `customElements.define(..., ..., { extends: "<tagname>" })`
|
|
201
|
+
* call. Firefox's `moz-support-link`, `moz-button-group` tabbing
|
|
202
|
+
* widgets, and a handful of other toolkit components use this form;
|
|
203
|
+
* `furnace override` of those components writes the original source
|
|
204
|
+
* verbatim and the validator must not reject them.
|
|
205
|
+
*
|
|
206
|
+
* A class that extends a plain `HTMLElement` WITHOUT a `extends:` option
|
|
207
|
+
* is still rejected — that's the legitimate `not-moz-lit-element` case
|
|
208
|
+
* the rule was originally designed to catch.
|
|
209
|
+
*/
|
|
210
|
+
function classExtendsMozLitElement(mjsContent) {
|
|
211
|
+
const hasClassDeclaration = /class\s+\w+\s+extends\s+/.test(mjsContent);
|
|
212
|
+
if (!hasClassDeclaration) {
|
|
213
|
+
// No class declaration — skip this check since the component may use a
|
|
214
|
+
// different pattern (e.g. function-based). Other validators will catch
|
|
215
|
+
// structural issues.
|
|
216
|
+
return true;
|
|
217
|
+
}
|
|
218
|
+
if (/class\s+\w+\s+extends\s+MozLitElement\b/.test(mjsContent)) {
|
|
219
|
+
return true;
|
|
220
|
+
}
|
|
221
|
+
// Customized built-in: extend a specific HTMLxxxElement AND the define
|
|
222
|
+
// call carries an `extends:` option. Both halves are required — a class
|
|
223
|
+
// that extends `HTMLAnchorElement` without the matching define option is
|
|
224
|
+
// almost certainly an author mistake, and a define call with `extends:`
|
|
225
|
+
// without a matching class is unreachable.
|
|
226
|
+
const extendsCustomizedBuiltin = /class\s+\w+\s+extends\s+HTML[A-Z]\w*Element\b/.test(mjsContent);
|
|
227
|
+
if (extendsCustomizedBuiltin && hasCustomElementExtendsOption(mjsContent)) {
|
|
228
|
+
return true;
|
|
229
|
+
}
|
|
230
|
+
return false;
|
|
231
|
+
}
|
|
232
|
+
/** Collects CSS custom property references used via var(--token-name). */
|
|
233
|
+
function collectCssVariableReferences(cssContent) {
|
|
234
|
+
const referencedVariables = [];
|
|
235
|
+
const variablePattern = /var\(\s*(--[\w-]+)/g;
|
|
236
|
+
let match;
|
|
237
|
+
while ((match = variablePattern.exec(cssContent)) !== null) {
|
|
238
|
+
const variableName = match[1];
|
|
239
|
+
if (variableName) {
|
|
240
|
+
referencedVariables.push(variableName);
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
return referencedVariables;
|
|
244
|
+
}
|
|
245
|
+
/**
|
|
246
|
+
* Collects CSS custom property *declarations* — names appearing on the
|
|
247
|
+
* left-hand side of a `--name:` declaration. Used to auto-exempt
|
|
248
|
+
* component-local runtime variables from the token-prefix check: if the
|
|
249
|
+
* component both declares and consumes a variable in its own CSS file, it
|
|
250
|
+
* is a local runtime channel, not a design-token reference.
|
|
251
|
+
*
|
|
252
|
+
* The anchor `(?:^|[{;,\s])` rules out `var(--name)` occurrences (which are
|
|
253
|
+
* always preceded by `(`), so references are not mistaken for declarations.
|
|
254
|
+
*/
|
|
255
|
+
function collectCssVariableDeclarations(cssContent) {
|
|
256
|
+
const declared = new Set();
|
|
257
|
+
const pattern = /(?:^|[{;,\s])(--[\w-]+)\s*:/g;
|
|
258
|
+
let match;
|
|
259
|
+
while ((match = pattern.exec(cssContent)) !== null) {
|
|
260
|
+
const name = match[1];
|
|
261
|
+
if (name)
|
|
262
|
+
declared.add(name);
|
|
263
|
+
}
|
|
264
|
+
return declared;
|
|
265
|
+
}
|
|
266
|
+
async function collectInheritedOverrideVariables(tagName, config, root) {
|
|
267
|
+
const inheritedVariables = new Set();
|
|
268
|
+
const basePath = config.overrides[tagName]?.basePath;
|
|
269
|
+
if (!basePath) {
|
|
270
|
+
return inheritedVariables;
|
|
271
|
+
}
|
|
272
|
+
const { engine: engineDir } = getProjectPaths(root);
|
|
273
|
+
const originalCssPath = join(engineDir, basePath, `${tagName}.css`);
|
|
274
|
+
if (!(await pathExists(originalCssPath))) {
|
|
275
|
+
return inheritedVariables;
|
|
276
|
+
}
|
|
277
|
+
const originalCssContent = stripCssBlockComments(await readText(originalCssPath));
|
|
278
|
+
for (const variableName of collectCssVariableReferences(originalCssContent)) {
|
|
279
|
+
inheritedVariables.add(variableName);
|
|
280
|
+
}
|
|
281
|
+
return inheritedVariables;
|
|
282
|
+
}
|
|
283
|
+
/** Builds token-validation context from the config allowlist and inherited override CSS. */
|
|
284
|
+
async function getTokenPrefixContext(tagName, type, config, root) {
|
|
285
|
+
const allowlist = new Set(config.tokenAllowlist ?? []);
|
|
286
|
+
const runtimeVariables = new Set(config.runtimeVariables ?? []);
|
|
287
|
+
if (type !== 'override' || !root) {
|
|
288
|
+
return { allowlist, inheritedOverrideVars: new Set(), runtimeVariables };
|
|
289
|
+
}
|
|
290
|
+
return {
|
|
291
|
+
allowlist,
|
|
292
|
+
inheritedOverrideVars: await collectInheritedOverrideVariables(tagName, config, root),
|
|
293
|
+
runtimeVariables,
|
|
294
|
+
};
|
|
295
|
+
}
|
|
@@ -1,118 +1,7 @@
|
|
|
1
|
-
import type { ComponentType, CustomComponentConfig, FurnaceConfig, ValidationIssue } from '../types/furnace.js';
|
|
2
|
-
/** Creates a normalized validation issue object. */
|
|
3
|
-
export declare function createIssue(component: string, severity: ValidationIssue['severity'], check: ValidationIssue['check'], message: string): ValidationIssue;
|
|
4
|
-
/** Detects whether template or script content assigns an ARIA role. */
|
|
5
|
-
export declare function hasAriaRole(content: string): boolean;
|
|
6
|
-
/** Detects generic elements being used as custom interactive controls. */
|
|
7
|
-
export declare function hasGenericInteractiveElement(content: string): boolean;
|
|
8
|
-
/** Detects a positive tabindex value, which disrupts natural tab order. */
|
|
9
|
-
export declare function hasPositiveTabindex(content: string): boolean;
|
|
10
|
-
/** Detects form inputs without associated labels. */
|
|
11
|
-
export declare function hasUnlabelledFormInput(content: string): boolean;
|
|
12
|
-
/** Detects Lit-style template click handlers. */
|
|
13
|
-
export declare function hasTemplateClickHandler(content: string): boolean;
|
|
14
|
-
/** Detects Lit-style template keyboard handlers. */
|
|
15
|
-
export declare function hasTemplateKeyboardHandler(content: string): boolean;
|
|
16
|
-
/**
|
|
17
|
-
* Returns true when `customConfig` declares that the component's keyboard
|
|
18
|
-
* activation path is covered by a wrapped native-interactive inner element,
|
|
19
|
-
* either through an explicit opt-out (`keyboardCovered: true`) or by
|
|
20
|
-
* composing a tag that lives in {@link NATIVE_CLICK_INTERACTIVE_TAGS}.
|
|
21
|
-
*
|
|
22
|
-
* Uses `.some` rather than `.every` so that a wrapper composing e.g.
|
|
23
|
-
* `['moz-button', 'my-tooltip']` still skips the warning: the keyboard
|
|
24
|
-
* activation path flows through the button, even if other composed
|
|
25
|
-
* children are synthetic.
|
|
26
|
-
*/
|
|
27
|
-
export declare function isKeyboardCoveredByComposition(customConfig: CustomComponentConfig | undefined): boolean;
|
|
28
|
-
/**
|
|
29
|
-
* Returns true when `content` has at least one `@click=${...}` handler on a
|
|
30
|
-
* *synthetic* interactive element (e.g. `<div @click>`), which lacks native
|
|
31
|
-
* keyboard activation and therefore needs an explicit key handler for
|
|
32
|
-
* Enter/Space. Returns false when every `@click` handler sits on a native
|
|
33
|
-
* interactive element — those already fire `click` on keyboard activation.
|
|
34
|
-
*/
|
|
35
|
-
export declare function hasTemplateClickOnSyntheticInteractive(content: string): boolean;
|
|
36
|
-
/**
|
|
37
|
-
* Detects hardcoded user-visible template text that should usually be
|
|
38
|
-
* localized.
|
|
39
|
-
*
|
|
40
|
-
* Scoped to three positive contexts rather than scanning the whole file,
|
|
41
|
-
* because a bare `>…<` regex catches JS comparisons (`if (x > 0 && y <
|
|
42
|
-
* 100)`), diagnostic strings (`console.error("Failed <id> lookup")`), and
|
|
43
|
-
* identifier literals that are never shown to a user. Only matches that
|
|
44
|
-
* actually enter a UI render path count:
|
|
45
|
-
*
|
|
46
|
-
* 1. Content inside a Lit `` html`…` `` tagged template literal.
|
|
47
|
-
* 2. The string literal on the RHS of `.textContent = "…"` or
|
|
48
|
-
* `.innerHTML = "…"`.
|
|
49
|
-
* 3. The string literal assigned to an XUL-widget `label=`,
|
|
50
|
-
* `title=`, or `tooltiptext=` attribute when constructing DOM in JS.
|
|
51
|
-
*
|
|
52
|
-
* A file-wide `// furnace-ignore: hardcoded-text` comment suppresses all
|
|
53
|
-
* findings (matches the pre-existing escape hatch).
|
|
54
|
-
*/
|
|
55
|
-
export declare function containsHardcodedTemplateText(content: string): boolean;
|
|
56
|
-
/** Detects whether a component opts into shadow-root focus delegation. */
|
|
57
|
-
export declare function hasDelegatesFocusEnabled(content: string): boolean;
|
|
58
|
-
/** Removes CSS block comments before running simple string-based checks. */
|
|
59
|
-
export declare function stripCssBlockComments(content: string): string;
|
|
60
|
-
/** Detects relative ES module imports in a component module file. */
|
|
61
|
-
export declare function hasRelativeModuleImport(mjsContent: string): boolean;
|
|
62
|
-
/** Detects whether a module defines a custom element at runtime. */
|
|
63
|
-
export declare function hasCustomElementDefineCall(mjsContent: string): boolean;
|
|
64
|
-
/**
|
|
65
|
-
* Checks whether a declared component class extends a valid element base.
|
|
66
|
-
*
|
|
67
|
-
* Two shapes are accepted:
|
|
68
|
-
*
|
|
69
|
-
* 1. Autonomous custom element: `class X extends MozLitElement` — the
|
|
70
|
-
* default FireForge pattern for fork-authored components and most
|
|
71
|
-
* toolkit widgets.
|
|
72
|
-
* 2. Customized built-in: `class X extends HTML<Something>Element` paired
|
|
73
|
-
* with a `customElements.define(..., ..., { extends: "<tagname>" })`
|
|
74
|
-
* call. Firefox's `moz-support-link`, `moz-button-group` tabbing
|
|
75
|
-
* widgets, and a handful of other toolkit components use this form;
|
|
76
|
-
* `furnace override` of those components writes the original source
|
|
77
|
-
* verbatim and the validator must not reject them.
|
|
78
|
-
*
|
|
79
|
-
* A class that extends a plain `HTMLElement` WITHOUT a `extends:` option
|
|
80
|
-
* is still rejected — that's the legitimate `not-moz-lit-element` case
|
|
81
|
-
* the rule was originally designed to catch.
|
|
82
|
-
*/
|
|
83
|
-
export declare function classExtendsMozLitElement(mjsContent: string): boolean;
|
|
84
|
-
/** Collects CSS custom property references used via var(--token-name). */
|
|
85
|
-
export declare function collectCssVariableReferences(cssContent: string): string[];
|
|
86
1
|
/**
|
|
87
|
-
*
|
|
88
|
-
*
|
|
89
|
-
* component-local runtime variables from the token-prefix check: if the
|
|
90
|
-
* component both declares and consumes a variable in its own CSS file, it
|
|
91
|
-
* is a local runtime channel, not a design-token reference.
|
|
92
|
-
*
|
|
93
|
-
* The anchor `(?:^|[{;,\s])` rules out `var(--name)` occurrences (which are
|
|
94
|
-
* always preceded by `(`), so references are not mistaken for declarations.
|
|
2
|
+
* The one genuinely shared helper across the furnace validators. Every other
|
|
3
|
+
* helper lives with the single validator that uses it.
|
|
95
4
|
*/
|
|
96
|
-
|
|
97
|
-
/**
|
|
98
|
-
export declare function
|
|
99
|
-
allowlist: Set<string>;
|
|
100
|
-
inheritedOverrideVars: Set<string>;
|
|
101
|
-
runtimeVariables: Set<string>;
|
|
102
|
-
}>;
|
|
103
|
-
/**
|
|
104
|
-
* Flags engine-side files that a previous deploy of `tagName` left behind
|
|
105
|
-
* after their workspace source was renamed or removed (field report D1).
|
|
106
|
-
*
|
|
107
|
-
* Detection keys on the furnace state file: every `appliedChecksums` entry
|
|
108
|
-
* under `custom/<tagName>/` whose workspace source no longer exists but
|
|
109
|
-
* whose engine target is still present is an orphan — the next deploy will
|
|
110
|
-
* prune it, but until then jar.mn and the deployed directory disagree with
|
|
111
|
-
* the workspace, and a re-export could capture the stale state into a patch.
|
|
112
|
-
*
|
|
113
|
-
* Custom components only: override undeploys restore the upstream baseline
|
|
114
|
-
* rather than deleting files, so "orphan" has no meaning there.
|
|
115
|
-
*/
|
|
116
|
-
export declare function findOrphanedEngineFiles(root: string, config: FurnaceConfig, tagName: string, state: {
|
|
117
|
-
appliedChecksums?: Record<string, string>;
|
|
118
|
-
}, ftlDir: string): Promise<ValidationIssue[]>;
|
|
5
|
+
import type { ValidationIssue } from '../types/furnace.js';
|
|
6
|
+
/** Creates a normalized validation issue object. */
|
|
7
|
+
export declare function createIssue(component: string, severity: ValidationIssue['severity'], check: ValidationIssue['check'], message: string): ValidationIssue;
|