@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
|
@@ -20,8 +20,8 @@ function multiLineBlockNameMatches(blockLine, category) {
|
|
|
20
20
|
return blockLine.replace(/^\s*\*\s*/, '').trim() === category;
|
|
21
21
|
}
|
|
22
22
|
/**
|
|
23
|
-
* Body of the first `/* … *\/` comment on `line`, or `
|
|
24
|
-
* carries no closed block comment.
|
|
23
|
+
* Body of the first `/* … *\/` comment on `line`, or `undefined` when the
|
|
24
|
+
* line carries no closed block comment.
|
|
25
25
|
*
|
|
26
26
|
* Deliberately index arithmetic rather than a regex: every regex spelling of
|
|
27
27
|
* "banner comment" this module used to carry was super-linear on a line that
|
|
@@ -32,10 +32,10 @@ function multiLineBlockNameMatches(blockLine, category) {
|
|
|
32
32
|
function blockCommentBody(line) {
|
|
33
33
|
const open = line.indexOf('/*');
|
|
34
34
|
if (open === -1)
|
|
35
|
-
return
|
|
35
|
+
return undefined;
|
|
36
36
|
const close = line.indexOf('*/', open + 2);
|
|
37
37
|
if (close === -1)
|
|
38
|
-
return
|
|
38
|
+
return undefined;
|
|
39
39
|
return line.slice(open + 2, close);
|
|
40
40
|
}
|
|
41
41
|
/**
|
|
@@ -49,14 +49,15 @@ function isSingleLineBannerLine(line) {
|
|
|
49
49
|
return body !== undefined && body.length >= 2 && body.startsWith('=') && body.endsWith('=');
|
|
50
50
|
}
|
|
51
51
|
/**
|
|
52
|
-
* Name declared by a single-line banner (`/* = Foo = *\/` → `Foo`), or
|
|
53
|
-
* when the line is not a named banner. The `=` runs on both sides
|
|
54
|
-
* required; a decorative all-`=` rule carries no name and yields
|
|
52
|
+
* Name declared by a single-line banner (`/* = Foo = *\/` → `Foo`), or
|
|
53
|
+
* `undefined` when the line is not a named banner. The `=` runs on both sides
|
|
54
|
+
* are required; a decorative all-`=` rule carries no name and yields
|
|
55
|
+
* `undefined`.
|
|
55
56
|
*/
|
|
56
57
|
function singleLineBannerName(line) {
|
|
57
58
|
const body = blockCommentBody(line);
|
|
58
|
-
if (body ===
|
|
59
|
-
return
|
|
59
|
+
if (body === undefined)
|
|
60
|
+
return undefined;
|
|
60
61
|
let start = 0;
|
|
61
62
|
let end = body.length;
|
|
62
63
|
while (start < end && /\s/.test(body[start] ?? ''))
|
|
@@ -65,16 +66,16 @@ function singleLineBannerName(line) {
|
|
|
65
66
|
while (start < end && body[start] === '=')
|
|
66
67
|
start++;
|
|
67
68
|
if (start === leadingEqStart)
|
|
68
|
-
return
|
|
69
|
+
return undefined;
|
|
69
70
|
while (end > start && /\s/.test(body[end - 1] ?? ''))
|
|
70
71
|
end--;
|
|
71
72
|
const trailingEqEnd = end;
|
|
72
73
|
while (end > start && body[end - 1] === '=')
|
|
73
74
|
end--;
|
|
74
75
|
if (end === trailingEqEnd)
|
|
75
|
-
return
|
|
76
|
+
return undefined;
|
|
76
77
|
const name = body.slice(start, end).trim();
|
|
77
|
-
return name.length > 0 ? name :
|
|
78
|
+
return name.length > 0 ? name : undefined;
|
|
78
79
|
}
|
|
79
80
|
/**
|
|
80
81
|
* True when `lines` contain a category header (single-line or multi-line
|
|
@@ -84,8 +85,7 @@ function singleLineBannerName(line) {
|
|
|
84
85
|
*/
|
|
85
86
|
export function categoryHeaderExists(lines, category) {
|
|
86
87
|
const singleLinePattern = singleLineBannerPattern(category);
|
|
87
|
-
for (
|
|
88
|
-
const line = lines[i] ?? '';
|
|
88
|
+
for (const [i, line] of lines.entries()) {
|
|
89
89
|
if (singleLinePattern.test(line)) {
|
|
90
90
|
return true;
|
|
91
91
|
}
|
|
@@ -115,29 +115,44 @@ export function categoryHeaderExists(lines, category) {
|
|
|
115
115
|
* This helper exists as a pure inspector; it never throws on malformed
|
|
116
116
|
* headers and silently skips shapes it cannot parse.
|
|
117
117
|
*/
|
|
118
|
+
/**
|
|
119
|
+
* Category name declared by the banner STARTING at `index`, or `undefined`
|
|
120
|
+
* when that line opens no banner.
|
|
121
|
+
*
|
|
122
|
+
* The single reader for both banner shapes, so the inventory walk, the
|
|
123
|
+
* "available categories" error body and the section finder cannot drift on
|
|
124
|
+
* what counts as a header.
|
|
125
|
+
*
|
|
126
|
+
* @param lines - Tokens CSS split into lines
|
|
127
|
+
* @param index - 0-based line index to test
|
|
128
|
+
* @returns The declared category name, or undefined
|
|
129
|
+
*/
|
|
130
|
+
export function categoryBannerNameAt(lines, index) {
|
|
131
|
+
const line = lines[index] ?? '';
|
|
132
|
+
const extracted = singleLineBannerName(line);
|
|
133
|
+
if (extracted !== undefined)
|
|
134
|
+
return extracted;
|
|
135
|
+
if (/^\s*\/\*\s*=+/.test(line) && !/\*\//.test(line)) {
|
|
136
|
+
for (let j = index + 1; j < Math.min(index + 6, lines.length); j++) {
|
|
137
|
+
const blockLine = lines[j] ?? '';
|
|
138
|
+
if (/\*\//.test(blockLine))
|
|
139
|
+
break;
|
|
140
|
+
const trimmed = blockLine.replace(/^\s*\*\s*/, '').trim();
|
|
141
|
+
if (trimmed.length === 0)
|
|
142
|
+
continue;
|
|
143
|
+
if (/^=+$/.test(trimmed))
|
|
144
|
+
continue;
|
|
145
|
+
return trimmed;
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
return undefined;
|
|
149
|
+
}
|
|
118
150
|
function discoverCategoryHeaders(lines) {
|
|
119
151
|
const categories = new Set();
|
|
120
152
|
for (let i = 0; i < lines.length; i++) {
|
|
121
|
-
const
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
categories.add(extracted);
|
|
125
|
-
continue;
|
|
126
|
-
}
|
|
127
|
-
if (/^\s*\/\*\s*=+/.test(line) && !/\*\//.test(line)) {
|
|
128
|
-
for (let j = i + 1; j < Math.min(i + 6, lines.length); j++) {
|
|
129
|
-
const blockLine = lines[j] ?? '';
|
|
130
|
-
if (/\*\//.test(blockLine))
|
|
131
|
-
break;
|
|
132
|
-
const trimmed = blockLine.replace(/^\s*\*\s*/, '').trim();
|
|
133
|
-
if (trimmed.length === 0)
|
|
134
|
-
continue;
|
|
135
|
-
if (/^=+$/.test(trimmed))
|
|
136
|
-
continue;
|
|
137
|
-
categories.add(trimmed);
|
|
138
|
-
break;
|
|
139
|
-
}
|
|
140
|
-
}
|
|
153
|
+
const name = categoryBannerNameAt(lines, i);
|
|
154
|
+
if (name !== undefined)
|
|
155
|
+
categories.add(name);
|
|
141
156
|
}
|
|
142
157
|
return [...categories];
|
|
143
158
|
}
|
|
@@ -177,7 +192,7 @@ export async function assertTokenCategoryExists(engineDir, tokensCssPath, catego
|
|
|
177
192
|
*/
|
|
178
193
|
export function declareCategoryBanner(lines, category, tokensCssPath) {
|
|
179
194
|
const bounds = findBaseRootBounds(lines);
|
|
180
|
-
if (bounds ===
|
|
195
|
+
if (bounds === undefined) {
|
|
181
196
|
throw new GeneralError(`Cannot create category "${category}": no :root block found in ${tokensCssPath}. ` +
|
|
182
197
|
'Run "fireforge furnace init --force" to re-scaffold the tokens CSS file.');
|
|
183
198
|
}
|
|
@@ -190,8 +205,7 @@ export function declareCategoryBanner(lines, category, tokensCssPath) {
|
|
|
190
205
|
export function findCategorySection(lines, category, tokensCssPath) {
|
|
191
206
|
const singleLinePattern = singleLineBannerPattern(category);
|
|
192
207
|
let categoryLine = -1;
|
|
193
|
-
for (
|
|
194
|
-
const line = lines[i] ?? '';
|
|
208
|
+
for (const [i, line] of lines.entries()) {
|
|
195
209
|
// Check single-line format: /* = Category = */
|
|
196
210
|
if (singleLinePattern.test(line)) {
|
|
197
211
|
categoryLine = i;
|
|
@@ -245,16 +259,10 @@ export function findCategorySection(lines, category, tokensCssPath) {
|
|
|
245
259
|
return { categoryLine, sectionEnd };
|
|
246
260
|
}
|
|
247
261
|
/** 0-based open/close line indices of the base `:root {` block. */
|
|
248
|
-
function findBaseRootBounds(lines) {
|
|
249
|
-
|
|
250
|
-
for (let i = 0; i < lines.length; i++) {
|
|
251
|
-
if (/:root\s*\{/.test(lines[i] ?? '')) {
|
|
252
|
-
open = i;
|
|
253
|
-
break;
|
|
254
|
-
}
|
|
255
|
-
}
|
|
262
|
+
export function findBaseRootBounds(lines) {
|
|
263
|
+
const open = lines.findIndex((line) => /:root\s*\{/.test(line));
|
|
256
264
|
if (open === -1)
|
|
257
|
-
return
|
|
265
|
+
return undefined;
|
|
258
266
|
for (let i = open + 1; i < lines.length; i++) {
|
|
259
267
|
if (/^\s*\}/.test(lines[i] ?? '')) {
|
|
260
268
|
return { open, close: i };
|
|
@@ -263,7 +271,7 @@ function findBaseRootBounds(lines) {
|
|
|
263
271
|
return { open, close: -1 };
|
|
264
272
|
}
|
|
265
273
|
/** Masks block-comment content per line so declarations inside comments never match. */
|
|
266
|
-
function maskCommentLines(lines) {
|
|
274
|
+
export function maskCommentLines(lines) {
|
|
267
275
|
const masked = lines.join('\n').replace(/\/\*[\s\S]*?\*\//g, (m) => m.replace(/[^\n]/g, ' '));
|
|
268
276
|
return masked.split('\n');
|
|
269
277
|
}
|
|
@@ -276,7 +284,7 @@ function maskCommentLines(lines) {
|
|
|
276
284
|
*/
|
|
277
285
|
export function findTokenDeclarationInRoot(lines, tokenName) {
|
|
278
286
|
const bounds = findBaseRootBounds(lines);
|
|
279
|
-
if (bounds ===
|
|
287
|
+
if (bounds === undefined || bounds.close === -1)
|
|
280
288
|
return undefined;
|
|
281
289
|
const masked = maskCommentLines(lines);
|
|
282
290
|
const declPattern = new RegExp(`^\\s*${escapeRegex(tokenName)}\\s*:`);
|
|
@@ -87,13 +87,7 @@ export function stripBlockCommentsInLines(lines) {
|
|
|
87
87
|
*/
|
|
88
88
|
export function findDarkRootInsertionIndex(lines) {
|
|
89
89
|
const stripped = stripBlockCommentsInLines(lines);
|
|
90
|
-
|
|
91
|
-
for (let i = 0; i < stripped.length; i++) {
|
|
92
|
-
if (/prefers-color-scheme:\s*dark/.test(stripped[i] ?? '')) {
|
|
93
|
-
darkMediaLine = i;
|
|
94
|
-
break;
|
|
95
|
-
}
|
|
96
|
-
}
|
|
90
|
+
const darkMediaLine = stripped.findIndex((line) => /prefers-color-scheme:\s*dark/.test(line));
|
|
97
91
|
if (darkMediaLine === -1)
|
|
98
92
|
return null;
|
|
99
93
|
// Walk the comment-stripped lines after the @media header and find
|
|
@@ -172,13 +166,7 @@ export function findBlockCloseIndex(stripped, startLine) {
|
|
|
172
166
|
*/
|
|
173
167
|
export function findDarkMediaCloseIndex(lines) {
|
|
174
168
|
const stripped = stripBlockCommentsInLines(lines);
|
|
175
|
-
|
|
176
|
-
for (let i = 0; i < stripped.length; i++) {
|
|
177
|
-
if (/prefers-color-scheme:\s*dark/.test(stripped[i] ?? '')) {
|
|
178
|
-
darkMediaLine = i;
|
|
179
|
-
break;
|
|
180
|
-
}
|
|
181
|
-
}
|
|
169
|
+
const darkMediaLine = stripped.findIndex((line) => /prefers-color-scheme:\s*dark/.test(line));
|
|
182
170
|
if (darkMediaLine === -1)
|
|
183
171
|
return -1;
|
|
184
172
|
return findBlockCloseIndex(stripped, darkMediaLine);
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
/** One token declaration inside the base `:root` block. */
|
|
2
|
+
export interface TokenInventoryEntry {
|
|
3
|
+
/** Custom-property name, including the leading `--`. */
|
|
4
|
+
name: string;
|
|
5
|
+
/** 1-based line number in the tokens CSS file. */
|
|
6
|
+
line: number;
|
|
7
|
+
/** Declared value, trimmed, without the trailing `;`. */
|
|
8
|
+
value: string;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Tokens grouped under the banner that precedes them, in file order.
|
|
12
|
+
* `category` is null for declarations that sit above the first banner —
|
|
13
|
+
* a real state in a hand-edited file, and one the report must show rather
|
|
14
|
+
* than silently drop.
|
|
15
|
+
*/
|
|
16
|
+
export interface TokenCategoryInventory {
|
|
17
|
+
category: string | null;
|
|
18
|
+
tokens: TokenInventoryEntry[];
|
|
19
|
+
}
|
|
20
|
+
/** One block that declares a given token, with the value it declares. */
|
|
21
|
+
export interface TokenBlockValue {
|
|
22
|
+
/**
|
|
23
|
+
* Selector trail of the declaring block, outermost first, joined with
|
|
24
|
+
* ` > ` — for example `:root` or
|
|
25
|
+
* `@media (prefers-color-scheme: dark) > :root`.
|
|
26
|
+
*/
|
|
27
|
+
block: string;
|
|
28
|
+
value: string;
|
|
29
|
+
/** 1-based line number of the declaration. */
|
|
30
|
+
line: number;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Collects every base-`:root` token declaration grouped by the category
|
|
34
|
+
* banner above it, in document order.
|
|
35
|
+
*
|
|
36
|
+
* Only the base block is walked: the dark `@media` and `:root[variant]`
|
|
37
|
+
* companions mirror declarations rather than owning them, so listing them
|
|
38
|
+
* would report the same token two or three times under no category at all.
|
|
39
|
+
*
|
|
40
|
+
* @param lines - Tokens CSS split into lines
|
|
41
|
+
* @returns Categories in file order, each with its tokens in file order
|
|
42
|
+
*/
|
|
43
|
+
export declare function collectTokenInventory(lines: string[]): TokenCategoryInventory[];
|
|
44
|
+
/**
|
|
45
|
+
* Every declaration of `tokenName`, in every block of the file, labelled by
|
|
46
|
+
* the selector trail of the block that declares it.
|
|
47
|
+
*
|
|
48
|
+
* Deliberately not restricted to the base `:root`: the question `token show`
|
|
49
|
+
* answers is "what does this token resolve to", and a token whose dark
|
|
50
|
+
* override differs from its base value is precisely the case worth seeing.
|
|
51
|
+
*
|
|
52
|
+
* @param lines - Tokens CSS split into lines
|
|
53
|
+
* @param tokenName - Custom-property name including the leading `--`
|
|
54
|
+
* @returns Declarations in file order
|
|
55
|
+
*/
|
|
56
|
+
export declare function collectTokenBlockValues(lines: string[], tokenName: string): TokenBlockValue[];
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
// SPDX-License-Identifier: EUPL-1.2
|
|
2
|
+
/**
|
|
3
|
+
* Read-only inventory of the tokens CSS file: which categories exist, which
|
|
4
|
+
* tokens each declares, and what a single token resolves to in every theme
|
|
5
|
+
* block that mentions it.
|
|
6
|
+
*
|
|
7
|
+
* `token add` requires `--category` and, until now, nothing reported the
|
|
8
|
+
* categories a project actually has — so the only way to name one correctly
|
|
9
|
+
* was to hand-parse a neighbouring `= Category =` banner out of the file.
|
|
10
|
+
* This module is that report. It deliberately reuses the banner and
|
|
11
|
+
* `:root`-bounds primitives from `token-category.ts` rather than parsing
|
|
12
|
+
* tokens CSS a second way: two readers that disagree on what a banner is
|
|
13
|
+
* would put `token list` and `token add` in different sections of the same
|
|
14
|
+
* file.
|
|
15
|
+
*/
|
|
16
|
+
import { categoryBannerNameAt, findBaseRootBounds, maskCommentLines } from './token-category.js';
|
|
17
|
+
/** Matches a custom-property declaration and captures its name and value. */
|
|
18
|
+
const DECLARATION_PATTERN = /^\s*(--[A-Za-z0-9_-]+)\s*:\s*([^;]*);?/;
|
|
19
|
+
/**
|
|
20
|
+
* Collects every base-`:root` token declaration grouped by the category
|
|
21
|
+
* banner above it, in document order.
|
|
22
|
+
*
|
|
23
|
+
* Only the base block is walked: the dark `@media` and `:root[variant]`
|
|
24
|
+
* companions mirror declarations rather than owning them, so listing them
|
|
25
|
+
* would report the same token two or three times under no category at all.
|
|
26
|
+
*
|
|
27
|
+
* @param lines - Tokens CSS split into lines
|
|
28
|
+
* @returns Categories in file order, each with its tokens in file order
|
|
29
|
+
*/
|
|
30
|
+
export function collectTokenInventory(lines) {
|
|
31
|
+
const bounds = findBaseRootBounds(lines);
|
|
32
|
+
if (bounds === undefined || bounds.close === -1)
|
|
33
|
+
return [];
|
|
34
|
+
const masked = maskCommentLines(lines);
|
|
35
|
+
const groups = [];
|
|
36
|
+
let current;
|
|
37
|
+
for (let i = bounds.open + 1; i < bounds.close; i++) {
|
|
38
|
+
// Banners are read from the RAW lines: `maskCommentLines` blanks comment
|
|
39
|
+
// bodies, which is exactly what a banner is.
|
|
40
|
+
const banner = categoryBannerNameAt(lines, i);
|
|
41
|
+
if (banner !== undefined) {
|
|
42
|
+
current = { category: banner, tokens: [] };
|
|
43
|
+
groups.push(current);
|
|
44
|
+
continue;
|
|
45
|
+
}
|
|
46
|
+
const match = DECLARATION_PATTERN.exec(masked[i] ?? '');
|
|
47
|
+
if (!match?.[1])
|
|
48
|
+
continue;
|
|
49
|
+
if (current === undefined) {
|
|
50
|
+
current = { category: null, tokens: [] };
|
|
51
|
+
groups.push(current);
|
|
52
|
+
}
|
|
53
|
+
current.tokens.push({ name: match[1], line: i + 1, value: (match[2] ?? '').trim() });
|
|
54
|
+
}
|
|
55
|
+
// A banner with no declarations under it is still a category `token add
|
|
56
|
+
// --category` accepts, so empty groups are kept.
|
|
57
|
+
return groups;
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Every declaration of `tokenName`, in every block of the file, labelled by
|
|
61
|
+
* the selector trail of the block that declares it.
|
|
62
|
+
*
|
|
63
|
+
* Deliberately not restricted to the base `:root`: the question `token show`
|
|
64
|
+
* answers is "what does this token resolve to", and a token whose dark
|
|
65
|
+
* override differs from its base value is precisely the case worth seeing.
|
|
66
|
+
*
|
|
67
|
+
* @param lines - Tokens CSS split into lines
|
|
68
|
+
* @param tokenName - Custom-property name including the leading `--`
|
|
69
|
+
* @returns Declarations in file order
|
|
70
|
+
*/
|
|
71
|
+
export function collectTokenBlockValues(lines, tokenName) {
|
|
72
|
+
const masked = maskCommentLines(lines);
|
|
73
|
+
const trail = [];
|
|
74
|
+
const found = [];
|
|
75
|
+
for (let i = 0; i < masked.length; i++) {
|
|
76
|
+
const line = masked[i] ?? '';
|
|
77
|
+
const match = DECLARATION_PATTERN.exec(line);
|
|
78
|
+
if (match?.[1] === tokenName) {
|
|
79
|
+
found.push({
|
|
80
|
+
block: trail.length > 0 ? trail.join(' > ') : '(file scope)',
|
|
81
|
+
value: (match[2] ?? '').trim(),
|
|
82
|
+
line: i + 1,
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
// Brace tracking after the declaration test, so a one-line block cannot
|
|
86
|
+
// swallow the declaration it opens on. Selector text is whatever
|
|
87
|
+
// precedes the `{`; an empty selector (a continuation line) still needs
|
|
88
|
+
// a stack entry or the closing `}` pops the wrong one.
|
|
89
|
+
for (const char of line) {
|
|
90
|
+
if (char === '{') {
|
|
91
|
+
trail.push(selectorBefore(line, trail.length));
|
|
92
|
+
}
|
|
93
|
+
else if (char === '}') {
|
|
94
|
+
trail.pop();
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
return found;
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* Selector text introducing the `depth`-th open brace on `line`. Falls back
|
|
102
|
+
* to a positional label so the trail stays well-formed on a minified or
|
|
103
|
+
* continuation line that opens a block with no visible selector.
|
|
104
|
+
*/
|
|
105
|
+
function selectorBefore(line, depth) {
|
|
106
|
+
const open = line.indexOf('{');
|
|
107
|
+
const text = open === -1 ? '' : line.slice(0, open).trim();
|
|
108
|
+
return text.length > 0 ? text : `(block ${String(depth + 1)})`;
|
|
109
|
+
}
|
|
@@ -10,7 +10,7 @@ export declare const TOKEN_MODES: readonly ["auto", "static", "override"];
|
|
|
10
10
|
/** Dark mode behaviour for a token. */
|
|
11
11
|
export type TokenMode = (typeof TOKEN_MODES)[number];
|
|
12
12
|
/** Narrows an arbitrary string to a {@link TokenMode}. */
|
|
13
|
-
export declare const isTokenMode: (value: string) => value is "
|
|
13
|
+
export declare const isTokenMode: (value: string) => value is "auto" | "override" | "static";
|
|
14
14
|
/**
|
|
15
15
|
* Options for adding a token.
|
|
16
16
|
*/
|
|
@@ -177,7 +177,7 @@ function evaluateBaseTokenIdempotency(lines, options, tokensCssPath) {
|
|
|
177
177
|
? 'remove the stray declaration first'
|
|
178
178
|
: `pass --category "${existing.category}" to target the existing declaration, or remove it first`;
|
|
179
179
|
throw new GeneralError(`Token "${options.tokenName}" is already declared outside category "${options.category}" ` +
|
|
180
|
-
`(${tokensCssPath}:${
|
|
180
|
+
`(${tokensCssPath}:${existing.line}${sectionSuffix}). ` +
|
|
181
181
|
`A token belongs to exactly one category — ${remedy}.`);
|
|
182
182
|
}
|
|
183
183
|
/**
|
|
@@ -32,29 +32,29 @@ const ATTRIBUTE_FRAGMENT_PATTERN = /^\[[a-zA-Z][a-zA-Z0-9_-]*(?:=(?:"[a-zA-Z0-9_
|
|
|
32
32
|
const QUALIFIER_PATTERN = /^:[a-zA-Z][a-zA-Z0-9-]*(?:\(([^()]*)\))?$/;
|
|
33
33
|
/**
|
|
34
34
|
* Splits a run of consecutive attribute fragments into its groups, or
|
|
35
|
-
* returns `
|
|
35
|
+
* returns `undefined` when the run is not exactly that — a group that does not
|
|
36
36
|
* open with `[`, an unclosed group, an empty run, or a group whose name or
|
|
37
37
|
* value is not identifier-safe.
|
|
38
38
|
*
|
|
39
39
|
* @param run - The `[…][…]` text with no `:root` prefix
|
|
40
|
-
* @returns The individual fragments, or
|
|
40
|
+
* @returns The individual fragments, or undefined when the run is not valid
|
|
41
41
|
*/
|
|
42
42
|
function splitAttributeGroups(run) {
|
|
43
43
|
const groups = [];
|
|
44
44
|
let cursor = 0;
|
|
45
45
|
while (cursor < run.length) {
|
|
46
46
|
if (run[cursor] !== '[')
|
|
47
|
-
return
|
|
47
|
+
return undefined;
|
|
48
48
|
const close = run.indexOf(']', cursor);
|
|
49
49
|
if (close === -1)
|
|
50
|
-
return
|
|
50
|
+
return undefined;
|
|
51
51
|
const group = run.slice(cursor, close + 1);
|
|
52
52
|
if (!ATTRIBUTE_FRAGMENT_PATTERN.test(group))
|
|
53
|
-
return
|
|
53
|
+
return undefined;
|
|
54
54
|
groups.push(group);
|
|
55
55
|
cursor = close + 1;
|
|
56
56
|
}
|
|
57
|
-
return groups.length > 0 ? groups :
|
|
57
|
+
return groups.length > 0 ? groups : undefined;
|
|
58
58
|
}
|
|
59
59
|
/** Rewrites `=value` / `='value'` to the double-quoted Mozilla convention. */
|
|
60
60
|
function normalizeAttributeFragment(group) {
|
|
@@ -96,10 +96,10 @@ export function validateVariantSelector(raw) {
|
|
|
96
96
|
// The parse must have consumed the WHOLE fragment: anything it declined to
|
|
97
97
|
// read (a stray space, a trailing combinator) is text the matcher would
|
|
98
98
|
// not see either, so accepting it would author a block nothing can find.
|
|
99
|
-
if (parts ===
|
|
99
|
+
if (parts === undefined || `${parts.attributes}${parts.qualifier}` !== `:root${value}`)
|
|
100
100
|
return reject;
|
|
101
101
|
const groups = splitAttributeGroups(parts.attributes.slice(':root'.length));
|
|
102
|
-
if (groups ===
|
|
102
|
+
if (groups === undefined)
|
|
103
103
|
return reject;
|
|
104
104
|
let qualifier = '';
|
|
105
105
|
if (parts.qualifier !== '') {
|
|
@@ -112,7 +112,7 @@ export function validateVariantSelector(raw) {
|
|
|
112
112
|
}
|
|
113
113
|
else {
|
|
114
114
|
const innerGroups = splitAttributeGroups(inner);
|
|
115
|
-
if (innerGroups ===
|
|
115
|
+
if (innerGroups === undefined)
|
|
116
116
|
return reject;
|
|
117
117
|
const name = parts.qualifier.slice(0, parts.qualifier.indexOf('('));
|
|
118
118
|
qualifier = `${name}(${innerGroups.map(normalizeAttributeFragment).join('')})`;
|
|
@@ -131,7 +131,7 @@ function canonicalSelector(selector) {
|
|
|
131
131
|
}
|
|
132
132
|
/**
|
|
133
133
|
* Splits the `:root[…]` selector on `line` into its attribute run and any
|
|
134
|
-
* trailing qualifier, or `
|
|
134
|
+
* trailing qualifier, or `undefined` when the line carries none.
|
|
135
135
|
*
|
|
136
136
|
* Index arithmetic rather than `/:root\[[^{]*\]/`: `[^{]` also matches `]`,
|
|
137
137
|
* so that pattern backtracks quadratically on a line repeating `:root[`
|
|
@@ -155,7 +155,7 @@ function canonicalSelector(selector) {
|
|
|
155
155
|
function rootAttributeSelector(line) {
|
|
156
156
|
const start = line.indexOf(':root[');
|
|
157
157
|
if (start === -1)
|
|
158
|
-
return
|
|
158
|
+
return undefined;
|
|
159
159
|
const brace = line.indexOf('{', start);
|
|
160
160
|
const limit = brace === -1 ? line.length : brace;
|
|
161
161
|
let cursor = start + ':root'.length;
|
|
@@ -168,13 +168,13 @@ function rootAttributeSelector(line) {
|
|
|
168
168
|
end = cursor;
|
|
169
169
|
}
|
|
170
170
|
if (end === start + ':root'.length)
|
|
171
|
-
return
|
|
171
|
+
return undefined;
|
|
172
172
|
return { attributes: line.slice(start, end), qualifier: line.slice(end, limit).trim() };
|
|
173
173
|
}
|
|
174
174
|
/**
|
|
175
175
|
* Finds the top-level `:root<variant>` block whose attribute selector
|
|
176
176
|
* matches `variant` (compared canonically). Returns the opening-brace and
|
|
177
|
-
* closing-brace line indices, or `
|
|
177
|
+
* closing-brace line indices, or `undefined` when no such block exists.
|
|
178
178
|
*
|
|
179
179
|
* Scans a comment-stripped mirror of `lines` so braces inside comments do
|
|
180
180
|
* not offset the depth counter; the returned indices line up with the
|
|
@@ -189,10 +189,9 @@ function findVariantBlock(lines, variant) {
|
|
|
189
189
|
const requested = rootAttributeSelector(`:root${variant} {`);
|
|
190
190
|
const want = canonicalSelector(requested?.attributes ?? `:root${variant}`);
|
|
191
191
|
const wantQualifier = canonicalSelector(requested?.qualifier ?? '');
|
|
192
|
-
for (
|
|
193
|
-
const line = stripped[i] ?? '';
|
|
192
|
+
for (const [i, line] of stripped.entries()) {
|
|
194
193
|
const selector = rootAttributeSelector(line);
|
|
195
|
-
if (selector ===
|
|
194
|
+
if (selector === undefined || canonicalSelector(selector.attributes) !== want)
|
|
196
195
|
continue;
|
|
197
196
|
// An UNqualified request still matches a qualified block: that is the
|
|
198
197
|
// 0.44.2 behaviour `variantBlockQualifier` exists to report, since
|
|
@@ -219,7 +218,7 @@ function findVariantBlock(lines, variant) {
|
|
|
219
218
|
continue;
|
|
220
219
|
return { open: openLine, close, qualifier: selector.qualifier };
|
|
221
220
|
}
|
|
222
|
-
return
|
|
221
|
+
return undefined;
|
|
223
222
|
}
|
|
224
223
|
/**
|
|
225
224
|
* Returns the line index at which a brand-new `:root<variant>` block should
|
|
@@ -229,8 +228,8 @@ function findVariantBlock(lines, variant) {
|
|
|
229
228
|
*/
|
|
230
229
|
function findVariantBlockInsertionPoint(lines) {
|
|
231
230
|
const stripped = stripBlockCommentsInLines(lines);
|
|
232
|
-
for (
|
|
233
|
-
if (/^\s*:root\s*\{/.test(
|
|
231
|
+
for (const [i, line] of stripped.entries()) {
|
|
232
|
+
if (/^\s*:root\s*\{/.test(line)) {
|
|
234
233
|
const close = findBlockCloseIndex(stripped, i);
|
|
235
234
|
if (close !== -1)
|
|
236
235
|
return close + 1;
|
|
@@ -245,7 +244,7 @@ function findVariantBlockInsertionPoint(lines) {
|
|
|
245
244
|
* the block, which is only wanted for the explicit `--variant` flow.
|
|
246
245
|
*/
|
|
247
246
|
export function variantBlockExists(lines, variant) {
|
|
248
|
-
return findVariantBlock(lines, variant) !==
|
|
247
|
+
return findVariantBlock(lines, variant) !== undefined;
|
|
249
248
|
}
|
|
250
249
|
/**
|
|
251
250
|
* The pseudo-class qualifier on the matched `:root<variant>` block, or
|
|
@@ -300,8 +299,8 @@ export function findVariantBlockDeclaration(lines, variant, tokenName) {
|
|
|
300
299
|
.join('\n')
|
|
301
300
|
.replace(/\/\*[\s\S]*?\*\//g, (match) => match.replace(/[^\n]/g, ' '))
|
|
302
301
|
.split('\n');
|
|
303
|
-
for (
|
|
304
|
-
if (
|
|
302
|
+
for (const [i, line] of stripped.entries()) {
|
|
303
|
+
if (line.includes(`${tokenName}:`))
|
|
305
304
|
return { line: block.open + i + 1 };
|
|
306
305
|
}
|
|
307
306
|
return undefined;
|
|
@@ -121,7 +121,7 @@ export function formatMajorVersionHopNotice(previousVersion, newVersion) {
|
|
|
121
121
|
return undefined;
|
|
122
122
|
if (previousMajor === newMajor)
|
|
123
123
|
return undefined;
|
|
124
|
-
return (`Firefox major version changed (${
|
|
124
|
+
return (`Firefox major version changed (${previousMajor} → ${newMajor}): ` +
|
|
125
125
|
'upstream toolchain minimums (cbindgen, Rust, …) may have moved with it. ' +
|
|
126
126
|
'Consider running "fireforge bootstrap" before the next build.');
|
|
127
127
|
}
|
|
@@ -2,8 +2,6 @@
|
|
|
2
2
|
export type CloneExecutor = (command: string, args: string[]) => Promise<void>;
|
|
3
3
|
/** How this host can materialise a tree. */
|
|
4
4
|
export type CowCapability = 'clonefile' | 'reflink' | 'none';
|
|
5
|
-
/** Per-platform `cp` argv prefix for a CoW copy of one entry. */
|
|
6
|
-
export declare function cowCopyArgs(capability: Exclude<CowCapability, 'none'>): string[];
|
|
7
5
|
/**
|
|
8
6
|
* Probes CoW support in `dir` by cloning a tiny probe file there.
|
|
9
7
|
* Non-darwin/linux platforms report 'none' without probing.
|
|
@@ -20,11 +20,11 @@ import { exec } from '../utils/process.js';
|
|
|
20
20
|
const defaultExecutor = async (command, args) => {
|
|
21
21
|
const result = await exec(command, args);
|
|
22
22
|
if (result.exitCode !== 0) {
|
|
23
|
-
throw new Error(`${command} ${args.join(' ')} failed (exit ${
|
|
23
|
+
throw new Error(`${command} ${args.join(' ')} failed (exit ${result.exitCode}): ${result.stderr}`);
|
|
24
24
|
}
|
|
25
25
|
};
|
|
26
26
|
/** Per-platform `cp` argv prefix for a CoW copy of one entry. */
|
|
27
|
-
|
|
27
|
+
function cowCopyArgs(capability) {
|
|
28
28
|
return capability === 'clonefile' ? ['-c', '-R', '-p'] : ['--reflink=always', '-a'];
|
|
29
29
|
}
|
|
30
30
|
/**
|
|
@@ -19,6 +19,7 @@ import { GeneralError } from '../errors/base.js';
|
|
|
19
19
|
import { mapWithConcurrency } from '../utils/concurrency.js';
|
|
20
20
|
import { getNodeErrorCode, isProcessAlive, toError } from '../utils/errors.js';
|
|
21
21
|
import { pathExists, readJson, writeJson } from '../utils/fs.js';
|
|
22
|
+
import { sha256Hex } from '../utils/hash.js';
|
|
22
23
|
import { verbose, warn } from '../utils/logger.js';
|
|
23
24
|
import { isObject } from '../utils/validation.js';
|
|
24
25
|
import { getBuildBaselinePath } from './build-baseline.js';
|
|
@@ -128,7 +129,7 @@ export async function readTreeMarker(root) {
|
|
|
128
129
|
const code = getNodeErrorCode(error);
|
|
129
130
|
if (code === 'ENOENT' || code === 'ENOTDIR')
|
|
130
131
|
return { kind: 'absent' };
|
|
131
|
-
verbose(`Unreadable tree marker at ${markerPath}: ${
|
|
132
|
+
verbose(`Unreadable tree marker at ${markerPath}: ${toError(error).message}`);
|
|
132
133
|
return { kind: 'corrupt', reason: `the marker could not be read (${toError(error).message})` };
|
|
133
134
|
}
|
|
134
135
|
}
|
|
@@ -178,9 +179,7 @@ export async function computePrimaryFingerprint(primaryRoot) {
|
|
|
178
179
|
}
|
|
179
180
|
let engineFingerprint;
|
|
180
181
|
try {
|
|
181
|
-
engineFingerprint =
|
|
182
|
-
.update(await getAllDiff(engineDir))
|
|
183
|
-
.digest('hex');
|
|
182
|
+
engineFingerprint = sha256Hex(await getAllDiff(engineDir));
|
|
184
183
|
}
|
|
185
184
|
catch {
|
|
186
185
|
engineFingerprint = null;
|
|
@@ -461,7 +460,7 @@ export async function removeTree(primaryRoot, name, options = {}) {
|
|
|
461
460
|
]) {
|
|
462
461
|
const state = await inspectTreeLock(lockPath);
|
|
463
462
|
if (state.kind === 'held') {
|
|
464
|
-
throw new GeneralError(`Refusing to remove tree "${name}": a live process (pid ${
|
|
463
|
+
throw new GeneralError(`Refusing to remove tree "${name}": a live process (pid ${state.pid}) holds ${lockPath}. ` +
|
|
465
464
|
'Wait for it to finish, then retry.');
|
|
466
465
|
}
|
|
467
466
|
if (state.kind === 'unknown' && options.force !== true) {
|
|
@@ -144,7 +144,7 @@ export async function runTypecheck(projectRoot, cfg) {
|
|
|
144
144
|
*/
|
|
145
145
|
function resolveProjectExtraShim(cfg, projectPath) {
|
|
146
146
|
const overrides = cfg.projectOverrides;
|
|
147
|
-
if (overrides && Object.
|
|
147
|
+
if (overrides && Object.hasOwn(overrides, projectPath)) {
|
|
148
148
|
const value = overrides[projectPath];
|
|
149
149
|
return value === null ? undefined : value;
|
|
150
150
|
}
|
|
@@ -298,7 +298,7 @@ async function runTypecheckForProject(ts, projectRoot, projectPath, shimSource,
|
|
|
298
298
|
}
|
|
299
299
|
issues.push(diagnosticToIssue(ts, diag, absConfig, projectPath));
|
|
300
300
|
}
|
|
301
|
-
verbose(`typecheck: ${projectPath} — analyzed ${
|
|
301
|
+
verbose(`typecheck: ${projectPath} — analyzed ${parsed.fileNames.length} file(s), found ${issues.length} issue(s)`);
|
|
302
302
|
return {
|
|
303
303
|
project: projectPath,
|
|
304
304
|
issues,
|