@hominis/fireforge 0.45.0 → 0.46.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +80 -0
- package/dist/bin/fireforge.js +6 -0
- package/dist/build-info.json +4 -4
- package/dist/src/cli-usage-error.d.ts +46 -0
- package/dist/src/cli-usage-error.js +81 -0
- package/dist/src/cli.d.ts +5 -1
- package/dist/src/cli.js +20 -21
- package/dist/src/commands/bootstrap.js +1 -2
- package/dist/src/commands/build.js +15 -6
- package/dist/src/commands/destructive-decision.d.ts +19 -0
- package/dist/src/commands/destructive-decision.js +31 -0
- package/dist/src/commands/doctor-check-core.d.ts +0 -6
- package/dist/src/commands/doctor-check-core.js +0 -8
- package/dist/src/commands/doctor-orphaned-harness.d.ts +0 -6
- package/dist/src/commands/doctor-orphaned-harness.js +4 -12
- package/dist/src/commands/doctor-post-rebase-audit.js +1 -1
- package/dist/src/commands/doctor.d.ts +2 -0
- package/dist/src/commands/doctor.js +7 -8
- package/dist/src/commands/download.js +35 -24
- package/dist/src/commands/export-all.js +21 -4
- package/dist/src/commands/export-flow.js +15 -45
- package/dist/src/commands/export-placement-conflicts.js +3 -3
- package/dist/src/commands/export-placement-gate.js +2 -8
- package/dist/src/commands/export-placement-policy.js +3 -5
- package/dist/src/commands/export-shared.d.ts +42 -23
- package/dist/src/commands/export-shared.js +4 -19
- package/dist/src/commands/export.js +10 -1
- package/dist/src/commands/furnace/authoring-config.d.ts +13 -0
- package/dist/src/commands/furnace/authoring-config.js +19 -0
- package/dist/src/commands/furnace/chrome-doc-remove.js +1 -1
- package/dist/src/commands/furnace/create-browser-test.js +3 -8
- package/dist/src/commands/furnace/create-dry-run.js +2 -5
- package/dist/src/commands/furnace/create-templates.d.ts +17 -1
- package/dist/src/commands/furnace/create-templates.js +2 -1
- package/dist/src/commands/furnace/create.js +25 -19
- package/dist/src/commands/furnace/deploy.js +11 -2
- package/dist/src/commands/furnace/init.js +2 -1
- package/dist/src/commands/furnace/override.js +28 -19
- package/dist/src/commands/furnace/refresh.js +9 -1
- package/dist/src/commands/furnace/remove-mutations.d.ts +6 -7
- package/dist/src/commands/furnace/remove-mutations.js +0 -1
- package/dist/src/commands/furnace/remove.js +2 -6
- package/dist/src/commands/furnace/rename-browser-test.js +1 -7
- package/dist/src/commands/furnace/rename.js +29 -29
- package/dist/src/commands/furnace/status.js +12 -9
- package/dist/src/commands/furnace/test-file-name.d.ts +28 -0
- package/dist/src/commands/furnace/test-file-name.js +39 -0
- package/dist/src/commands/furnace/validation-output.d.ts +21 -9
- package/dist/src/commands/furnace/validation-output.js +3 -8
- package/dist/src/commands/import.js +2 -9
- package/dist/src/commands/lint-per-patch.d.ts +2 -2
- package/dist/src/commands/lint-per-patch.js +19 -4
- package/dist/src/commands/lint-per-run-checkjs.d.ts +2 -2
- package/dist/src/commands/lint.d.ts +2 -1
- package/dist/src/commands/lint.js +1 -1
- package/dist/src/commands/manifest.js +1 -1
- package/dist/src/commands/package.js +2 -4
- package/dist/src/commands/patch/compact.js +14 -15
- package/dist/src/commands/patch/delete.js +15 -25
- package/dist/src/commands/patch/entry-projection.d.ts +13 -0
- package/dist/src/commands/patch/entry-projection.js +24 -0
- package/dist/src/commands/patch/lint-ignore.d.ts +0 -8
- package/dist/src/commands/patch/lint-ignore.js +19 -29
- package/dist/src/commands/patch/move-files-create.js +24 -10
- package/dist/src/commands/patch/move-files-into.js +38 -34
- package/dist/src/commands/patch/move-files.js +4 -13
- package/dist/src/commands/patch/rename.js +27 -41
- package/dist/src/commands/patch/reorder.js +25 -38
- package/dist/src/commands/patch/split-plan.d.ts +4 -5
- package/dist/src/commands/patch/split-plan.js +4 -8
- package/dist/src/commands/patch/split.js +45 -43
- package/dist/src/commands/patch/staged-dependency.js +16 -22
- package/dist/src/commands/patch/tier.js +15 -25
- package/dist/src/commands/patch-rename-map.d.ts +19 -0
- package/dist/src/commands/patch-rename-map.js +36 -0
- package/dist/src/commands/pending-resolution.d.ts +15 -0
- package/dist/src/commands/pending-resolution.js +25 -0
- package/dist/src/commands/re-export-adjacent.d.ts +24 -2
- package/dist/src/commands/re-export-adjacent.js +32 -16
- package/dist/src/commands/re-export-bulk-scan.js +6 -10
- package/dist/src/commands/re-export-drift.js +4 -4
- package/dist/src/commands/re-export-files.d.ts +2 -3
- package/dist/src/commands/re-export-files.js +38 -29
- package/dist/src/commands/re-export-lint.d.ts +3 -4
- package/dist/src/commands/re-export-lint.js +9 -2
- package/dist/src/commands/re-export-scan.js +5 -2
- package/dist/src/commands/re-export-single.d.ts +22 -3
- package/dist/src/commands/re-export-single.js +19 -11
- package/dist/src/commands/re-export.js +28 -11
- package/dist/src/commands/rebase/abort.js +3 -8
- package/dist/src/commands/rebase/continue.js +12 -12
- package/dist/src/commands/rebase/index.js +2 -2
- package/dist/src/commands/rebase/patch-loop.d.ts +2 -2
- package/dist/src/commands/rebase/patch-loop.js +2 -7
- package/dist/src/commands/resolve.js +18 -10
- package/dist/src/commands/run.d.ts +0 -13
- package/dist/src/commands/run.js +12 -25
- package/dist/src/commands/setup-support.d.ts +16 -4
- package/dist/src/commands/setup-support.js +16 -16
- package/dist/src/commands/setup.js +3 -9
- package/dist/src/commands/source.js +8 -1
- package/dist/src/commands/status-check.js +2 -2
- package/dist/src/commands/status-output.js +2 -2
- package/dist/src/commands/status.js +3 -4
- package/dist/src/commands/test-build-phase.d.ts +2 -2
- package/dist/src/commands/test-build-phase.js +31 -7
- package/dist/src/commands/test-modes.d.ts +2 -2
- package/dist/src/commands/test-modes.js +0 -1
- package/dist/src/commands/test-register.js +5 -3
- package/dist/src/commands/test-run.js +25 -18
- package/dist/src/commands/test-stale-gate.js +4 -4
- package/dist/src/commands/test-verdict.d.ts +16 -2
- package/dist/src/commands/test-verdict.js +18 -3
- package/dist/src/commands/test.js +42 -5
- package/dist/src/commands/token-list.d.ts +26 -0
- package/dist/src/commands/token-list.js +153 -0
- package/dist/src/commands/token.js +17 -1
- package/dist/src/commands/tree.js +7 -2
- package/dist/src/commands/typecheck.js +9 -9
- package/dist/src/commands/wire.js +4 -3
- package/dist/src/core/branding.d.ts +0 -7
- package/dist/src/core/branding.js +3 -2
- package/dist/src/core/browser-wire.js +6 -3
- package/dist/src/core/build-audit-platform.d.ts +29 -0
- package/dist/src/core/build-audit-platform.js +140 -0
- package/dist/src/core/build-audit-registration.js +3 -2
- package/dist/src/core/build-audit-skip.d.ts +57 -0
- package/dist/src/core/build-audit-skip.js +112 -0
- package/dist/src/core/build-audit.d.ts +15 -2
- package/dist/src/core/build-audit.js +110 -10
- package/dist/src/core/build-baseline.d.ts +45 -28
- package/dist/src/core/build-baseline.js +6 -29
- package/dist/src/core/build-jar-escalation.js +3 -2
- package/dist/src/core/build-overwrite-guard.js +1 -1
- package/dist/src/core/build-prepare.d.ts +0 -8
- package/dist/src/core/build-prepare.js +7 -12
- package/dist/src/core/config-paths.d.ts +2 -6
- package/dist/src/core/config-paths.js +4 -2
- package/dist/src/core/config-state.d.ts +0 -6
- package/dist/src/core/config-state.js +1 -19
- package/dist/src/core/config-validate-build-audit.d.ts +8 -0
- package/dist/src/core/config-validate-build-audit.js +80 -0
- package/dist/src/core/config-validate-file-size.js +1 -1
- package/dist/src/core/config-validate-patch-policy.d.ts +2 -2
- package/dist/src/core/config-validate-patch-policy.js +3 -3
- package/dist/src/core/config-validate-test-toolchains.d.ts +2 -2
- package/dist/src/core/config-validate-test-toolchains.js +4 -5
- package/dist/src/core/config-validate.js +14 -3
- package/dist/src/core/config.d.ts +2 -2
- package/dist/src/core/config.js +11 -15
- package/dist/src/core/coverage-extend.js +4 -4
- package/dist/src/core/discard-baseline.js +7 -7
- package/dist/src/core/engine-precondition.d.ts +1 -9
- package/dist/src/core/engine-precondition.js +3 -8
- package/dist/src/core/engine-session-lock.js +13 -17
- package/dist/src/core/export-moved-code-shape.js +2 -2
- package/dist/src/core/file-lock-owner.d.ts +103 -0
- package/dist/src/core/file-lock-owner.js +259 -0
- package/dist/src/core/file-lock.d.ts +2 -7
- package/dist/src/core/file-lock.js +40 -91
- package/dist/src/core/firefox-cache.d.ts +13 -1
- package/dist/src/core/firefox-cache.js +59 -34
- package/dist/src/core/firefox-download.d.ts +15 -0
- package/dist/src/core/firefox-download.js +37 -21
- package/dist/src/core/firefox-extract.d.ts +6 -5
- package/dist/src/core/firefox-extract.js +36 -18
- package/dist/src/core/firefox.d.ts +30 -32
- package/dist/src/core/firefox.js +5 -36
- package/dist/src/core/furnace-apply-dry-run.d.ts +1 -6
- package/dist/src/core/furnace-apply-dry-run.js +1 -7
- package/dist/src/core/furnace-apply-helpers.d.ts +0 -11
- package/dist/src/core/furnace-apply-helpers.js +8 -19
- package/dist/src/core/furnace-apply-overwrite-warn.js +4 -2
- package/dist/src/core/furnace-apply.d.ts +1 -1
- package/dist/src/core/furnace-apply.js +30 -10
- package/dist/src/core/furnace-config.d.ts +1 -1
- package/dist/src/core/furnace-config.js +23 -36
- package/dist/src/core/furnace-constants.js +3 -2
- package/dist/src/core/furnace-dir-entry.d.ts +20 -0
- package/dist/src/core/furnace-dir-entry.js +21 -0
- package/dist/src/core/furnace-operation.d.ts +2 -0
- package/dist/src/core/furnace-operation.js +3 -1
- package/dist/src/core/furnace-refresh.d.ts +18 -6
- package/dist/src/core/furnace-refresh.js +3 -6
- package/dist/src/core/furnace-registration-ast.d.ts +0 -1
- package/dist/src/core/furnace-registration-ast.js +0 -3
- package/dist/src/core/furnace-registration.d.ts +2 -1
- package/dist/src/core/furnace-registration.js +22 -29
- package/dist/src/core/furnace-scanner.js +3 -6
- package/dist/src/core/furnace-state-persist.d.ts +2 -2
- package/dist/src/core/furnace-validate-registration.js +5 -7
- package/dist/src/core/git-base.d.ts +3 -1
- package/dist/src/core/git-base.js +25 -3
- package/dist/src/core/git-diff-new-file.d.ts +40 -0
- package/dist/src/core/git-diff-new-file.js +119 -0
- package/dist/src/core/git-diff.d.ts +9 -9
- package/dist/src/core/git-diff.js +88 -157
- package/dist/src/core/git-file-ops.d.ts +0 -7
- package/dist/src/core/git-file-ops.js +4 -28
- package/dist/src/core/git-readonly-index.d.ts +0 -2
- package/dist/src/core/git-readonly-index.js +0 -4
- package/dist/src/core/git-status.d.ts +7 -0
- package/dist/src/core/git-status.js +21 -9
- package/dist/src/core/git.d.ts +0 -6
- package/dist/src/core/git.js +14 -43
- package/dist/src/core/harness-orphans.js +8 -7
- package/dist/src/core/history-log.d.ts +19 -0
- package/dist/src/core/history-log.js +29 -0
- package/dist/src/core/mach-error-hints.d.ts +0 -5
- package/dist/src/core/mach-error-hints.js +1 -1
- package/dist/src/core/mach-known-noise-filter.d.ts +4 -3
- package/dist/src/core/mach-known-noise-filter.js +7 -9
- package/dist/src/core/mach-mozconfig.js +2 -1
- package/dist/src/core/mach-python.d.ts +2 -0
- package/dist/src/core/mach-python.js +12 -7
- package/dist/src/core/mach-resource-shim.d.ts +13 -1
- package/dist/src/core/mach-resource-shim.js +112 -9
- package/dist/src/core/mach.d.ts +39 -53
- package/dist/src/core/mach.js +13 -81
- package/dist/src/core/marionette-port.d.ts +0 -17
- package/dist/src/core/marionette-port.js +12 -58
- package/dist/src/core/marionette-preflight.js +9 -27
- package/dist/src/core/mochitest-server-port.js +3 -3
- package/dist/src/core/moz-manifest-rules.js +37 -33
- package/dist/src/core/patch-apply-fuzz.js +4 -4
- package/dist/src/core/patch-apply.d.ts +4 -5
- package/dist/src/core/patch-apply.js +6 -12
- package/dist/src/core/patch-export-coverage.d.ts +0 -9
- package/dist/src/core/patch-export-coverage.js +1 -30
- package/dist/src/core/patch-export-update.d.ts +18 -1
- package/dist/src/core/patch-export-update.js +4 -3
- package/dist/src/core/patch-export.d.ts +11 -21
- package/dist/src/core/patch-export.js +44 -66
- package/dist/src/core/patch-files.d.ts +0 -4
- package/dist/src/core/patch-files.js +0 -11
- package/dist/src/core/patch-lint-binary.d.ts +6 -12
- package/dist/src/core/patch-lint-cache.d.ts +17 -1
- package/dist/src/core/patch-lint-cache.js +3 -6
- package/dist/src/core/patch-lint-checkjs.d.ts +33 -16
- package/dist/src/core/patch-lint-checkjs.js +37 -24
- package/dist/src/core/patch-lint-creators.d.ts +2 -6
- package/dist/src/core/patch-lint-cross.d.ts +16 -8
- package/dist/src/core/patch-lint-cross.js +49 -92
- package/dist/src/core/patch-lint-css.js +10 -6
- package/dist/src/core/patch-lint-file-size.js +3 -3
- package/dist/src/core/patch-lint-forward-registration.d.ts +58 -0
- package/dist/src/core/patch-lint-forward-registration.js +210 -0
- package/dist/src/core/patch-lint-jsdoc.js +36 -36
- package/dist/src/core/patch-lint-module-registration.d.ts +2 -8
- package/dist/src/core/patch-lint-prettier.js +1 -1
- package/dist/src/core/patch-lint-queue-types.d.ts +60 -0
- package/dist/src/core/patch-lint-queue-types.js +12 -0
- package/dist/src/core/patch-lint-staged-registration.d.ts +61 -0
- package/dist/src/core/patch-lint-staged-registration.js +90 -0
- package/dist/src/core/patch-lint.d.ts +14 -9
- package/dist/src/core/patch-lint.js +4 -3
- package/dist/src/core/patch-lock.js +3 -3
- package/dist/src/core/patch-manifest-io.js +8 -7
- package/dist/src/core/patch-parse.d.ts +19 -8
- package/dist/src/core/patch-parse.js +25 -0
- package/dist/src/core/patch-policy.js +15 -27
- package/dist/src/core/patch-source-metadata.d.ts +1 -3
- package/dist/src/core/patch-source-metadata.js +0 -4
- package/dist/src/core/patch-transform.d.ts +0 -8
- package/dist/src/core/patch-transform.js +0 -10
- package/dist/src/core/process-cpu-time.js +1 -1
- package/dist/src/core/rebase-session.d.ts +2 -8
- package/dist/src/core/rebase-session.js +5 -17
- package/dist/src/core/register-browser-content.js +3 -2
- package/dist/src/core/register-browser-test.js +2 -1
- package/dist/src/core/register-module.js +2 -1
- package/dist/src/core/register-scaffold.js +5 -4
- package/dist/src/core/register-shared-css.js +2 -1
- package/dist/src/core/run-log-redact.d.ts +38 -0
- package/dist/src/core/run-log-redact.js +69 -0
- package/dist/src/core/run-log.d.ts +13 -2
- package/dist/src/core/run-log.js +67 -18
- package/dist/src/core/signal-critical.js +2 -4
- package/dist/src/core/smoke-patterns.d.ts +0 -1
- package/dist/src/core/smoke-patterns.js +6 -11
- package/dist/src/core/state-file.d.ts +12 -1
- package/dist/src/core/state-file.js +14 -1
- package/dist/src/core/status-classify.js +2 -1
- package/dist/src/core/test-changed-prefs.js +26 -11
- package/dist/src/core/test-harness-crash.js +2 -2
- package/dist/src/core/test-stale-check.d.ts +0 -2
- package/dist/src/core/test-stale-check.js +2 -2
- package/dist/src/core/test-xpcshell-retry.d.ts +26 -7
- package/dist/src/core/test-xpcshell-retry.js +14 -11
- package/dist/src/core/token-category.d.ts +33 -0
- package/dist/src/core/token-category.js +56 -48
- package/dist/src/core/token-dark-mode.js +2 -14
- package/dist/src/core/token-inventory.d.ts +56 -0
- package/dist/src/core/token-inventory.js +109 -0
- package/dist/src/core/token-manager.d.ts +1 -1
- package/dist/src/core/token-manager.js +1 -1
- package/dist/src/core/token-variant.js +21 -22
- package/dist/src/core/toolchain-preflight.js +1 -1
- package/dist/src/core/tree-cow.d.ts +0 -2
- package/dist/src/core/tree-cow.js +2 -2
- package/dist/src/core/tree-store.js +4 -5
- package/dist/src/core/typecheck.js +2 -2
- package/dist/src/core/wire-dom-fragment.js +6 -17
- package/dist/src/core/wire-utils.js +5 -17
- package/dist/src/core/xpcshell-appdir.js +2 -4
- package/dist/src/errors/base.d.ts +53 -1
- package/dist/src/errors/base.js +79 -11
- package/dist/src/errors/build.js +20 -28
- package/dist/src/errors/config.d.ts +0 -14
- package/dist/src/errors/config.js +13 -34
- package/dist/src/errors/download.d.ts +13 -0
- package/dist/src/errors/download.js +53 -24
- package/dist/src/errors/furnace.js +8 -9
- package/dist/src/errors/git.d.ts +8 -8
- package/dist/src/errors/git.js +20 -30
- package/dist/src/errors/patch.js +8 -10
- package/dist/src/errors/rebase.js +7 -7
- package/dist/src/types/commands/options.d.ts +77 -123
- package/dist/src/types/config.d.ts +34 -2
- package/dist/src/types/index.d.ts +0 -1
- package/dist/src/utils/assert.d.ts +3 -15
- package/dist/src/utils/assert.js +1 -18
- package/dist/src/utils/build-info.d.ts +1 -1
- package/dist/src/utils/child-env.d.ts +2 -1
- package/dist/src/utils/elapsed.js +10 -1
- package/dist/src/utils/fs.js +8 -13
- package/dist/src/utils/hash.d.ts +12 -0
- package/dist/src/utils/hash.js +16 -0
- package/dist/src/utils/line-dispatch.d.ts +15 -0
- package/dist/src/utils/line-dispatch.js +48 -0
- package/dist/src/utils/options.d.ts +1 -7
- package/dist/src/utils/options.js +6 -7
- package/dist/src/utils/package-root.js +3 -12
- package/dist/src/utils/platform.d.ts +0 -4
- package/dist/src/utils/process-group.d.ts +2 -1
- package/dist/src/utils/process-group.js +20 -19
- package/dist/src/utils/process.d.ts +40 -26
- package/dist/src/utils/process.js +115 -166
- package/dist/src/utils/ps-duration.js +3 -3
- package/dist/src/utils/sleep.d.ts +26 -0
- package/dist/src/utils/sleep.js +31 -0
- package/dist/src/utils/validation.d.ts +2 -38
- package/dist/src/utils/validation.js +7 -16
- package/docs/configuration.md +89 -1
- package/docs/exit-codes.md +31 -19
- package/docs/lifecycle-invariants.md +18 -12
- package/docs/machine-output.md +31 -8
- package/docs/patch-workflow.md +2 -1
- package/docs/run-logs.md +44 -4
- package/docs/testing.md +8 -1
- package/package.json +8 -7
- package/dist/src/commands/rebase.d.ts +0 -4
- package/dist/src/commands/rebase.js +0 -5
- package/dist/src/core/patch-artifact-normalize.d.ts +0 -21
- package/dist/src/core/patch-artifact-normalize.js +0 -24
- package/dist/src/core/patch-lint-reexports.d.ts +0 -7
- package/dist/src/core/patch-lint-reexports.js +0 -8
- package/dist/src/core/wire-targets.d.ts +0 -7
- package/dist/src/core/wire-targets.js +0 -8
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
// SPDX-License-Identifier: EUPL-1.2
|
|
2
|
+
/**
|
|
3
|
+
* `token list` and `token show` — the read half of design-token management.
|
|
4
|
+
*
|
|
5
|
+
* `token add` requires `--category` and refuses an unknown one, but nothing
|
|
6
|
+
* reported the categories a project has, so naming one meant hand-parsing a
|
|
7
|
+
* neighbouring `= Category =` banner out of the tokens CSS. Both commands
|
|
8
|
+
* read through `token-inventory.ts`, which is built on the same banner and
|
|
9
|
+
* `:root`-bounds primitives `token add` and `token coverage` use — a second
|
|
10
|
+
* tokens-CSS parser would let the report and the writer disagree about
|
|
11
|
+
* which section a token lives in.
|
|
12
|
+
*
|
|
13
|
+
* Helper module: no registrar, called from `token.ts`.
|
|
14
|
+
*/
|
|
15
|
+
import { join } from 'node:path';
|
|
16
|
+
import { getProjectPaths, loadConfig } from '../core/config.js';
|
|
17
|
+
import { collectTokenBlockValues, collectTokenInventory, } from '../core/token-inventory.js';
|
|
18
|
+
import { getTokensCssPath } from '../core/token-manager.js';
|
|
19
|
+
import { GeneralError } from '../errors/base.js';
|
|
20
|
+
import { toError } from '../utils/errors.js';
|
|
21
|
+
import { pathExists, readText } from '../utils/fs.js';
|
|
22
|
+
import { info, intro, outro, setMachineOutputMode, setStdoutSealed } from '../utils/logger.js';
|
|
23
|
+
import { emitMachineError, MACHINE_OUTPUT_SCHEMA_VERSION } from '../utils/machine-output.js';
|
|
24
|
+
/** Reads the project's tokens CSS as lines, refusing when it is absent. */
|
|
25
|
+
async function readTokensCssLines(projectRoot) {
|
|
26
|
+
const paths = getProjectPaths(projectRoot);
|
|
27
|
+
const config = await loadConfig(projectRoot);
|
|
28
|
+
const tokensCssPath = getTokensCssPath(config.binaryName);
|
|
29
|
+
const filePath = join(paths.engine, tokensCssPath);
|
|
30
|
+
if (!(await pathExists(filePath))) {
|
|
31
|
+
throw new GeneralError(`Token CSS file not found: ${tokensCssPath}. Run "fireforge furnace init" to seed it.`);
|
|
32
|
+
}
|
|
33
|
+
return { lines: (await readText(filePath)).split('\n'), tokensCssPath };
|
|
34
|
+
}
|
|
35
|
+
/** Applies the `--category` filter, refusing a name the file does not carry. */
|
|
36
|
+
function filterByCategory(groups, category, tokensCssPath) {
|
|
37
|
+
const matched = groups.filter((group) => group.category === category);
|
|
38
|
+
if (matched.length > 0)
|
|
39
|
+
return matched;
|
|
40
|
+
// Same "name the alternatives" contract `token add` gives for an unknown
|
|
41
|
+
// category — a filter that silently prints nothing reads as "this
|
|
42
|
+
// category is empty", which is a different fact.
|
|
43
|
+
const available = groups
|
|
44
|
+
.map((group) => group.category)
|
|
45
|
+
.filter((name) => name !== null);
|
|
46
|
+
const suffix = available.length > 0
|
|
47
|
+
? `Available categories: ${available.map((name) => `"${name}"`).join(', ')}.`
|
|
48
|
+
: 'The file declares no category banners.';
|
|
49
|
+
throw new GeneralError(`Category "${category}" not found in ${tokensCssPath}.\n\n${suffix}`);
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Lists the categories declared in the tokens CSS with their token names,
|
|
53
|
+
* in file order.
|
|
54
|
+
*
|
|
55
|
+
* @param projectRoot - Root directory of the project
|
|
56
|
+
* @param options - Filter and output-mode options
|
|
57
|
+
*/
|
|
58
|
+
export async function tokenListCommand(projectRoot, options = {}) {
|
|
59
|
+
if (options.json === true) {
|
|
60
|
+
// stdout belongs exclusively to the payload from here on; a later
|
|
61
|
+
// diagnostic (including withErrorHandling's) routes to stderr.
|
|
62
|
+
setMachineOutputMode(true);
|
|
63
|
+
try {
|
|
64
|
+
const { lines, tokensCssPath } = await readTokensCssLines(projectRoot);
|
|
65
|
+
const all = collectTokenInventory(lines);
|
|
66
|
+
const groups = options.category === undefined
|
|
67
|
+
? all
|
|
68
|
+
: filterByCategory(all, options.category, tokensCssPath);
|
|
69
|
+
process.stdout.write(`${JSON.stringify({
|
|
70
|
+
schemaVersion: MACHINE_OUTPUT_SCHEMA_VERSION,
|
|
71
|
+
tokensCssPath,
|
|
72
|
+
categories: groups.map((group) => ({
|
|
73
|
+
category: group.category,
|
|
74
|
+
tokens: group.tokens.map((token) => ({
|
|
75
|
+
name: token.name,
|
|
76
|
+
line: token.line,
|
|
77
|
+
value: token.value,
|
|
78
|
+
})),
|
|
79
|
+
})),
|
|
80
|
+
}, null, 2)}\n`);
|
|
81
|
+
setStdoutSealed(true);
|
|
82
|
+
}
|
|
83
|
+
catch (error) {
|
|
84
|
+
emitMachineError('token-list-failed', toError(error).message);
|
|
85
|
+
}
|
|
86
|
+
finally {
|
|
87
|
+
setMachineOutputMode(false);
|
|
88
|
+
}
|
|
89
|
+
return;
|
|
90
|
+
}
|
|
91
|
+
const { lines, tokensCssPath } = await readTokensCssLines(projectRoot);
|
|
92
|
+
const all = collectTokenInventory(lines);
|
|
93
|
+
const groups = options.category === undefined ? all : filterByCategory(all, options.category, tokensCssPath);
|
|
94
|
+
intro('Design Tokens');
|
|
95
|
+
info(tokensCssPath);
|
|
96
|
+
if (groups.length === 0) {
|
|
97
|
+
outro('No token declarations found in the :root block');
|
|
98
|
+
return;
|
|
99
|
+
}
|
|
100
|
+
let total = 0;
|
|
101
|
+
for (const group of groups) {
|
|
102
|
+
info(` ${group.category ?? '(no category)'} — ${String(group.tokens.length)} token(s)`);
|
|
103
|
+
for (const token of group.tokens) {
|
|
104
|
+
info(` ${token.name}`);
|
|
105
|
+
}
|
|
106
|
+
total += group.tokens.length;
|
|
107
|
+
}
|
|
108
|
+
outro(`${String(groups.length)} category/categories, ${String(total)} token(s)`);
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* Reports one token: the category that owns it and the value it takes in
|
|
112
|
+
* every block that declares it.
|
|
113
|
+
*
|
|
114
|
+
* @param projectRoot - Root directory of the project
|
|
115
|
+
* @param tokenName - Custom-property name, with or without the leading `--`
|
|
116
|
+
* @param options - Output-mode options
|
|
117
|
+
*/
|
|
118
|
+
export async function tokenShowCommand(projectRoot, tokenName, options = {}) {
|
|
119
|
+
const emit = async () => {
|
|
120
|
+
const { lines, tokensCssPath } = await readTokensCssLines(projectRoot);
|
|
121
|
+
const name = tokenName.startsWith('--') ? tokenName : `--${tokenName}`;
|
|
122
|
+
const declarations = collectTokenBlockValues(lines, name);
|
|
123
|
+
if (declarations.length === 0) {
|
|
124
|
+
throw new GeneralError(`Token "${name}" is not declared in ${tokensCssPath}. ` +
|
|
125
|
+
'Run "fireforge token list" to see the declared tokens.');
|
|
126
|
+
}
|
|
127
|
+
const owning = collectTokenInventory(lines).find((group) => group.tokens.some((token) => token.name === name));
|
|
128
|
+
return { tokensCssPath, name, category: owning?.category ?? null, declarations };
|
|
129
|
+
};
|
|
130
|
+
if (options.json === true) {
|
|
131
|
+
setMachineOutputMode(true);
|
|
132
|
+
try {
|
|
133
|
+
const report = await emit();
|
|
134
|
+
process.stdout.write(`${JSON.stringify({ schemaVersion: MACHINE_OUTPUT_SCHEMA_VERSION, ...report }, null, 2)}\n`);
|
|
135
|
+
setStdoutSealed(true);
|
|
136
|
+
}
|
|
137
|
+
catch (error) {
|
|
138
|
+
emitMachineError('token-show-failed', toError(error).message);
|
|
139
|
+
}
|
|
140
|
+
finally {
|
|
141
|
+
setMachineOutputMode(false);
|
|
142
|
+
}
|
|
143
|
+
return;
|
|
144
|
+
}
|
|
145
|
+
const report = await emit();
|
|
146
|
+
intro('Design Token');
|
|
147
|
+
info(`${report.name} — category: ${report.category ?? '(no category)'}`);
|
|
148
|
+
info(report.tokensCssPath);
|
|
149
|
+
for (const declaration of report.declarations) {
|
|
150
|
+
info(` ${declaration.block}: ${declaration.value} (line ${String(declaration.line)})`);
|
|
151
|
+
}
|
|
152
|
+
outro(`${String(report.declarations.length)} declaration(s)`);
|
|
153
|
+
}
|
|
@@ -10,6 +10,7 @@ import { info, intro, outro, success, warn } from '../utils/logger.js';
|
|
|
10
10
|
import { pickDefined } from '../utils/options.js';
|
|
11
11
|
import { normalizeTokenName } from '../utils/validation.js';
|
|
12
12
|
import { tokenCoverageCommand } from './token-coverage.js';
|
|
13
|
+
import { tokenListCommand, tokenShowCommand } from './token-list.js';
|
|
13
14
|
async function normalizeTokenNameForProject(projectRoot, rawTokenName) {
|
|
14
15
|
if (rawTokenName.startsWith('--')) {
|
|
15
16
|
return normalizeTokenName(rawTokenName);
|
|
@@ -52,7 +53,7 @@ function reportSkippedToken(tokenName, result, options) {
|
|
|
52
53
|
? `:root${options.variant}`
|
|
53
54
|
: `category "${result.skippedExisting?.category ?? options.category ?? ''}"`;
|
|
54
55
|
const where = result.skippedExisting
|
|
55
|
-
? ` in ${scope} (line ${
|
|
56
|
+
? ` in ${scope} (line ${result.skippedExisting.line}), unchanged`
|
|
56
57
|
: '';
|
|
57
58
|
info(`Token ${tokenName} already exists${where} (skipped)`);
|
|
58
59
|
}
|
|
@@ -201,6 +202,21 @@ export function registerToken(program, { getProjectRoot, withErrorHandling }) {
|
|
|
201
202
|
}),
|
|
202
203
|
});
|
|
203
204
|
}));
|
|
205
|
+
token
|
|
206
|
+
.command('list')
|
|
207
|
+
.description('List the design-token categories declared in the tokens CSS with their token names, in file order')
|
|
208
|
+
.option('--category <name>', 'Restrict the report to one category')
|
|
209
|
+
.option('--json', 'Emit the machine-readable envelope on stdout (see docs/machine-output.md)')
|
|
210
|
+
.action(withErrorHandling(async (options) => {
|
|
211
|
+
await tokenListCommand(getProjectRoot(), pickDefined({ category: options.category, json: options.json }));
|
|
212
|
+
}));
|
|
213
|
+
token
|
|
214
|
+
.command('show <token-name>')
|
|
215
|
+
.description('Show one token: its category, the value it takes in every declaring block, and where it is defined. The leading `--` is optional; with it, use the `--` separator first (e.g. `fireforge token show -- --my-token`).')
|
|
216
|
+
.option('--json', 'Emit the machine-readable envelope on stdout (see docs/machine-output.md)')
|
|
217
|
+
.action(withErrorHandling(async (tokenName, options) => {
|
|
218
|
+
await tokenShowCommand(getProjectRoot(), tokenName, pickDefined({ json: options.json }));
|
|
219
|
+
}));
|
|
204
220
|
token
|
|
205
221
|
.command('coverage')
|
|
206
222
|
.description('Measure design token usage across modified CSS files')
|
|
@@ -290,7 +290,7 @@ async function treeExecCommand(projectRoot, name, args) {
|
|
|
290
290
|
setStdoutSealed(true);
|
|
291
291
|
});
|
|
292
292
|
if (exitCode !== 0) {
|
|
293
|
-
throw new GeneralError(`tree exec: fireforge ${args.join(' ')} exited with code ${
|
|
293
|
+
throw new GeneralError(`tree exec: fireforge ${args.join(' ')} exited with code ${exitCode}.`);
|
|
294
294
|
}
|
|
295
295
|
}
|
|
296
296
|
/** Registers the tree command group on the CLI program. */
|
|
@@ -298,7 +298,12 @@ export function registerTree(program, context) {
|
|
|
298
298
|
const { getProjectRoot, withErrorHandling } = context;
|
|
299
299
|
const tree = program
|
|
300
300
|
.command('tree')
|
|
301
|
-
.description('Manage copy-on-write verification clones of this project for concurrent read-mostly work (lint, typecheck, status, verify, export/re-export --dry-run; build-less test with create --with-objdir). Mutation commands are refused inside a tree.')
|
|
301
|
+
.description('Manage copy-on-write verification clones of this project for concurrent read-mostly work (lint, typecheck, status, verify, export/re-export --dry-run; build-less test with create --with-objdir). Mutation commands are refused inside a tree.')
|
|
302
|
+
// No-subcommand contract shared with `patch`/`token`/`furnace`: help on
|
|
303
|
+
// stdout, exit 0 (see `handleParseError` for the groups without an action).
|
|
304
|
+
.action(() => {
|
|
305
|
+
tree.outputHelp();
|
|
306
|
+
});
|
|
302
307
|
addWaitLockOption(tree
|
|
303
308
|
.command('create <name>')
|
|
304
309
|
.description('Clone the project (engine included, obj-* excluded) into .fireforge/trees/<name> using filesystem copy-on-write (APFS clonefile / btrfs-XFS reflink). Refuses on filesystems without CoW support unless --force-copy explicitly accepts a full physical copy.')
|
|
@@ -57,8 +57,8 @@ export function resolveTypecheckProjects(configTypecheck, override) {
|
|
|
57
57
|
*/
|
|
58
58
|
function formatIssue(projectRoot, issue) {
|
|
59
59
|
const file = relativeForDisplay(projectRoot, issue.file);
|
|
60
|
-
const codeLabel = issue.code > 0 ? ` TS${
|
|
61
|
-
return `[${issue.project}] ${file}:${
|
|
60
|
+
const codeLabel = issue.code > 0 ? ` TS${issue.code}` : '';
|
|
61
|
+
return `[${issue.project}] ${file}:${issue.line}:${issue.column}${codeLabel} ${issue.message}`;
|
|
62
62
|
}
|
|
63
63
|
/**
|
|
64
64
|
* Top-level entry point invoked by the registered Commander action.
|
|
@@ -87,7 +87,7 @@ async function runTypecheckCommandBody(projectRoot, options) {
|
|
|
87
87
|
// session never touched. Run the same reconciler `furnace deploy`/`sync`
|
|
88
88
|
// use so typecheck checks against the current workspace.
|
|
89
89
|
await regenerateStaleGeneratedJsconfig(projectRoot);
|
|
90
|
-
info(`Running typecheck across ${
|
|
90
|
+
info(`Running typecheck across ${cfg.projects.length} project(s): ${cfg.projects.join(', ')}`);
|
|
91
91
|
const results = await runTypecheck(projectRoot, cfg);
|
|
92
92
|
// Fold the EXPORT-TIME authority into this pass. Per-patch lint runs
|
|
93
93
|
// checkJs over the queue's patch-owned modules in relative isolation and
|
|
@@ -139,8 +139,8 @@ async function regenerateStaleGeneratedJsconfig(projectRoot) {
|
|
|
139
139
|
if (!drift.changed)
|
|
140
140
|
return;
|
|
141
141
|
info(`Regenerating stale generated jsconfig ${furnaceConfig.typecheckJsconfig} before typecheck ` +
|
|
142
|
-
`(+${
|
|
143
|
-
`-${
|
|
142
|
+
`(+${drift.added.length} added, ~${drift.updated.length} updated, ` +
|
|
143
|
+
`-${drift.pruned.length} pruned).`);
|
|
144
144
|
await syncFurnaceJsconfigPaths(projectRoot, furnaceConfig);
|
|
145
145
|
}
|
|
146
146
|
/**
|
|
@@ -171,10 +171,10 @@ export function reportResults(projectRoot, results, patchLintIssues = []) {
|
|
|
171
171
|
for (const issue of errors)
|
|
172
172
|
warn(formatIssue(projectRoot, issue));
|
|
173
173
|
}
|
|
174
|
-
const summary = `Typecheck: ${
|
|
175
|
-
`${
|
|
174
|
+
const summary = `Typecheck: ${totalErrors} error(s), ${totalWarnings} warning(s) across ` +
|
|
175
|
+
`${results.length} project(s)` +
|
|
176
176
|
(patchLintIssues.length > 0
|
|
177
|
-
? ` (including ${
|
|
177
|
+
? ` (including ${patchLintIssues.length} per-patch checkJs finding(s))`
|
|
178
178
|
: '');
|
|
179
179
|
if (totalErrors === 0) {
|
|
180
180
|
success(summary);
|
|
@@ -183,7 +183,7 @@ export function reportResults(projectRoot, results, patchLintIssues = []) {
|
|
|
183
183
|
}
|
|
184
184
|
info(summary);
|
|
185
185
|
outro('Typecheck failed');
|
|
186
|
-
throw new GeneralError(`Typecheck found ${
|
|
186
|
+
throw new GeneralError(`Typecheck found ${totalErrors} error(s) across ${results.length} project(s).`);
|
|
187
187
|
}
|
|
188
188
|
/**
|
|
189
189
|
* Registers the `typecheck` command on the CLI program.
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// SPDX-License-Identifier: EUPL-1.2
|
|
2
2
|
import { join, relative } from 'node:path';
|
|
3
3
|
import { Command } from 'commander';
|
|
4
|
-
import { DEFAULT_BROWSER_SUBSCRIPT_DIR, wireSubscript } from '../core/browser-wire.js';
|
|
4
|
+
import { DEFAULT_BROWSER_SUBSCRIPT_DIR, wireSubscript, } from '../core/browser-wire.js';
|
|
5
5
|
import { getProjectPaths, loadConfig } from '../core/config.js';
|
|
6
6
|
import { furnaceConfigExists as checkFurnaceConfigExists, loadFurnaceConfig, } from '../core/furnace-config.js';
|
|
7
7
|
import { consumeParserFallbackEvents } from '../core/parser-fallback.js';
|
|
@@ -13,6 +13,7 @@ import { pathExists } from '../utils/fs.js';
|
|
|
13
13
|
import { info, intro, outro, success, warn } from '../utils/logger.js';
|
|
14
14
|
import { pickDefined } from '../utils/options.js';
|
|
15
15
|
import { isContainedRelativePath, isExplicitAbsolutePath, isPathInsideRoot, stripEnginePrefix, toRootRelativePath, } from '../utils/paths.js';
|
|
16
|
+
import { normalizePathSlashes } from '../utils/paths.js';
|
|
16
17
|
const BROWSER_BASE_DIR = 'browser/base';
|
|
17
18
|
function printWireDryRun(engineDir, name, subscriptDir, domFilePath, domTargetPath, options) {
|
|
18
19
|
info('[dry-run] Would wire subscript:');
|
|
@@ -28,10 +29,10 @@ function printWireDryRun(engineDir, name, subscriptDir, domFilePath, domTargetPa
|
|
|
28
29
|
info(` browser-init.js onUnload(): ${coerceToCall(options.destroy)}`);
|
|
29
30
|
}
|
|
30
31
|
if (domFilePath) {
|
|
31
|
-
const includePath = relative(join(engineDir, subscriptDir), join(engineDir, domFilePath))
|
|
32
|
+
const includePath = normalizePathSlashes(relative(join(engineDir, subscriptDir), join(engineDir, domFilePath)));
|
|
32
33
|
info(` ${domTargetPath}: #include ${includePath}`);
|
|
33
34
|
}
|
|
34
|
-
const relPath = relative(join(engineDir, BROWSER_BASE_DIR), join(engineDir, subscriptDir))
|
|
35
|
+
const relPath = normalizePathSlashes(relative(join(engineDir, BROWSER_BASE_DIR), join(engineDir, subscriptDir)));
|
|
35
36
|
info(` jar.mn: content/browser/${name}.js (${relPath}/${name}.js)`);
|
|
36
37
|
outro('Dry run complete');
|
|
37
38
|
}
|
|
@@ -1,12 +1,5 @@
|
|
|
1
1
|
import { FireForgeError } from '../errors/base.js';
|
|
2
2
|
import type { ProjectLicense } from '../types/config.js';
|
|
3
|
-
/**
|
|
4
|
-
* Error thrown when branding operations fail.
|
|
5
|
-
*/
|
|
6
|
-
export declare class BrandingError extends FireForgeError {
|
|
7
|
-
readonly code: 6;
|
|
8
|
-
get userMessage(): string;
|
|
9
|
-
}
|
|
10
3
|
/**
|
|
11
4
|
* Error thrown when the generated `mozconfig` references a `--with-branding`
|
|
12
5
|
* directory that does not match the branding tree FireForge set up. The
|
|
@@ -4,11 +4,12 @@ import { FireForgeError } from '../errors/base.js';
|
|
|
4
4
|
import { ExitCode } from '../errors/codes.js';
|
|
5
5
|
import { copyDir, pathExists, readText, writeTextIfChanged } from '../utils/fs.js';
|
|
6
6
|
import { warn } from '../utils/logger.js';
|
|
7
|
+
import { normalizePathSlashes } from '../utils/paths.js';
|
|
7
8
|
import { DEFAULT_LICENSE, getLicenseHeader } from './license-headers.js';
|
|
8
9
|
/**
|
|
9
10
|
* Error thrown when branding operations fail.
|
|
10
11
|
*/
|
|
11
|
-
|
|
12
|
+
class BrandingError extends FireForgeError {
|
|
12
13
|
code = ExitCode.PATCH_ERROR;
|
|
13
14
|
get userMessage() {
|
|
14
15
|
return `Branding Error: ${this.message}\n\nBranding is required to set MOZ_APP_VENDOR, MOZ_MACBUNDLE_ID, and other Firefox identity values.`;
|
|
@@ -361,7 +362,7 @@ export async function isBrandingSetup(engineDir, config) {
|
|
|
361
362
|
* @returns true if the path is managed by branding tooling
|
|
362
363
|
*/
|
|
363
364
|
export function isBrandingManagedPath(file, binaryName) {
|
|
364
|
-
const normalized = file
|
|
365
|
+
const normalized = normalizePathSlashes(file);
|
|
365
366
|
const brandingRoot = `browser/branding/${binaryName}`;
|
|
366
367
|
return (normalized === 'browser/moz.configure' ||
|
|
367
368
|
normalized === brandingRoot ||
|
|
@@ -4,11 +4,14 @@ import { GeneralError } from '../errors/base.js';
|
|
|
4
4
|
import { toError } from '../utils/errors.js';
|
|
5
5
|
import { verbose } from '../utils/logger.js';
|
|
6
6
|
import { toRootRelativePath } from '../utils/paths.js';
|
|
7
|
+
import { normalizePathSlashes } from '../utils/paths.js';
|
|
7
8
|
import { getProjectPaths, loadConfig } from './config.js';
|
|
8
9
|
import { assertSnapshotted, createRollbackJournal, restoreRollbackJournal, snapshotFile, } from './furnace-rollback.js';
|
|
9
10
|
import { registerBrowserContent } from './moz-manifest-register.js';
|
|
10
|
-
import {
|
|
11
|
-
import {
|
|
11
|
+
import { addDestroyToBrowserInit } from './wire-destroy.js';
|
|
12
|
+
import { addDomFragment, DEFAULT_DOM_TARGET } from './wire-dom-fragment.js';
|
|
13
|
+
import { addInitToBrowserInit } from './wire-init.js';
|
|
14
|
+
import { addSubscriptToBrowserMain } from './wire-subscript.js';
|
|
12
15
|
export const DEFAULT_BROWSER_SUBSCRIPT_DIR = 'browser/base/content';
|
|
13
16
|
const BROWSER_BASE_DIR = 'browser/base';
|
|
14
17
|
/**
|
|
@@ -25,7 +28,7 @@ export async function wireSubscript(root, name, options = {}) {
|
|
|
25
28
|
// Compute jar.mn source path relative to browser/base/
|
|
26
29
|
let jarMnSourcePath;
|
|
27
30
|
if (subscriptDir !== DEFAULT_BROWSER_SUBSCRIPT_DIR) {
|
|
28
|
-
const relPath = relative(join(engineDir, BROWSER_BASE_DIR), join(engineDir, subscriptDir))
|
|
31
|
+
const relPath = normalizePathSlashes(relative(join(engineDir, BROWSER_BASE_DIR), join(engineDir, subscriptDir)));
|
|
29
32
|
jarMnSourcePath = `${relPath}/${name}.js`;
|
|
30
33
|
}
|
|
31
34
|
if (options.dryRun) {
|
|
@@ -36,3 +36,32 @@ export declare function findEnclosingGate(content: string, basename: string): st
|
|
|
36
36
|
* @returns Detection result
|
|
37
37
|
*/
|
|
38
38
|
export declare function detectPlatformGate(engineDir: string, sourcePath: string): Promise<PlatformGateResult>;
|
|
39
|
+
/**
|
|
40
|
+
* Result of an ancestor `DIRS +=` gate lookup.
|
|
41
|
+
*/
|
|
42
|
+
export interface AncestorDirsGateResult {
|
|
43
|
+
/** True when an ancestor's `DIRS` entry is gated off on this host. */
|
|
44
|
+
gatedOff: boolean;
|
|
45
|
+
/** The gate expression that excluded the directory. */
|
|
46
|
+
gateExpression?: string;
|
|
47
|
+
/** Engine-relative path of the `moz.build` carrying the gate. */
|
|
48
|
+
gateFile?: string;
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Determines whether the source file's DIRECTORY is excluded from this
|
|
52
|
+
* build by a gate in an ANCESTOR `moz.build`.
|
|
53
|
+
*
|
|
54
|
+
* `detectPlatformGate` reads only the file's own nearest `moz.build`, which
|
|
55
|
+
* misses the common Firefox shape where the whole subtree is reached
|
|
56
|
+
* through a conditional `DIRS +=` one or more levels up: nothing in
|
|
57
|
+
* `toolkit/mozapps/defaultagent/moz.build` says "Windows only", yet none of
|
|
58
|
+
* it is built on macOS.
|
|
59
|
+
*
|
|
60
|
+
* Errs toward NOT skipping, like the rest of this module: an unreadable or
|
|
61
|
+
* oversized ancestor, or a directory no `DIRS` entry names, yields
|
|
62
|
+
* `gatedOff: false`.
|
|
63
|
+
*
|
|
64
|
+
* @param engineDir - Absolute path to the engine root
|
|
65
|
+
* @param sourcePath - Engine-relative POSIX path of the source file
|
|
66
|
+
*/
|
|
67
|
+
export declare function detectAncestorDirsGate(engineDir: string, sourcePath: string): Promise<AncestorDirsGateResult>;
|
|
@@ -28,6 +28,7 @@
|
|
|
28
28
|
* that should ship on this host) are not, so it errs toward NOT skipping
|
|
29
29
|
* when uncertain.
|
|
30
30
|
*/
|
|
31
|
+
import { stat } from 'node:fs/promises';
|
|
31
32
|
import { dirname, join } from 'node:path';
|
|
32
33
|
import { pathExists, readText } from '../utils/fs.js';
|
|
33
34
|
import { getPlatform } from '../utils/platform.js';
|
|
@@ -231,3 +232,142 @@ export async function detectPlatformGate(engineDir, sourcePath) {
|
|
|
231
232
|
}
|
|
232
233
|
return { gatedOff: false };
|
|
233
234
|
}
|
|
235
|
+
/**
|
|
236
|
+
* Maximum ancestor levels walked looking for a gating `DIRS` entry, and the
|
|
237
|
+
* largest `moz.build` read while doing it. The audit runs on every build, so
|
|
238
|
+
* a pathological tree must not be able to make it slow; beyond these bounds
|
|
239
|
+
* the walk gives up and the caller keeps today's behaviour (warn).
|
|
240
|
+
*/
|
|
241
|
+
const MAX_ANCESTOR_DEPTH = 12;
|
|
242
|
+
const MAX_MOZ_BUILD_BYTES = 256 * 1024;
|
|
243
|
+
/** Matches a line that begins a `DIRS` assignment. */
|
|
244
|
+
const DIRS_ASSIGNMENT_PATTERN = /^\s*DIRS\s*\+?=/;
|
|
245
|
+
/**
|
|
246
|
+
* Collects the FULL stack of `if CONFIG[…]:` expressions enclosing the
|
|
247
|
+
* `DIRS` entry whose value equals `relDir`.
|
|
248
|
+
*
|
|
249
|
+
* Two differences from {@link findEnclosingGate}, both load-bearing here:
|
|
250
|
+
* only quoted values inside a `DIRS` assignment are considered (an
|
|
251
|
+
* unrelated string literal elsewhere in the file must not gate a
|
|
252
|
+
* directory), and EVERY enclosing expression is returned rather than the
|
|
253
|
+
* innermost. Firefox nests these — `toolkit/moz.build` reaches
|
|
254
|
+
* `mozapps/defaultagent` inside `OS_ARCH == "WINNT"`, then a `CC_TYPE`
|
|
255
|
+
* test, then `MOZ_DEFAULT_BROWSER_AGENT` — and it is the OUTERMOST one that
|
|
256
|
+
* excludes the directory on a macOS host.
|
|
257
|
+
*
|
|
258
|
+
* @param content - `moz.build` file content
|
|
259
|
+
* @param relDir - Directory path relative to the `moz.build`, POSIX-separated
|
|
260
|
+
* @returns Enclosing gate expressions, outermost first; empty when ungated
|
|
261
|
+
* or when no `DIRS` entry reaches `relDir`
|
|
262
|
+
*/
|
|
263
|
+
function findDirsEntryGates(content, relDir) {
|
|
264
|
+
const lines = content.split('\n');
|
|
265
|
+
const stack = [];
|
|
266
|
+
let inDirs = false;
|
|
267
|
+
for (const line of lines) {
|
|
268
|
+
if (line.trim() === '')
|
|
269
|
+
continue;
|
|
270
|
+
const indent = (/^(\s*)/.exec(line)?.[1] ?? '').length;
|
|
271
|
+
while (stack.length > 0) {
|
|
272
|
+
const top = stack[stack.length - 1];
|
|
273
|
+
if (top && indent <= top.indent) {
|
|
274
|
+
stack.pop();
|
|
275
|
+
continue;
|
|
276
|
+
}
|
|
277
|
+
break;
|
|
278
|
+
}
|
|
279
|
+
const ifMatch = /^\s*if\s+(.+?):\s*(?:#.*)?$/.exec(line);
|
|
280
|
+
if (ifMatch?.[1]) {
|
|
281
|
+
stack.push({ indent, expression: ifMatch[1] });
|
|
282
|
+
inDirs = false;
|
|
283
|
+
continue;
|
|
284
|
+
}
|
|
285
|
+
if (DIRS_ASSIGNMENT_PATTERN.test(line)) {
|
|
286
|
+
// A `DIRS += [` list body continues on later lines; a single-line
|
|
287
|
+
// `DIRS += ["x"]` is fully handled by the match below.
|
|
288
|
+
inDirs = !line.includes(']');
|
|
289
|
+
if (dirsLineNames(line, relDir))
|
|
290
|
+
return stack.map((entry) => entry.expression);
|
|
291
|
+
continue;
|
|
292
|
+
}
|
|
293
|
+
if (inDirs) {
|
|
294
|
+
if (dirsLineNames(line, relDir))
|
|
295
|
+
return stack.map((entry) => entry.expression);
|
|
296
|
+
if (line.includes(']'))
|
|
297
|
+
inDirs = false;
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
return undefined;
|
|
301
|
+
}
|
|
302
|
+
/** True when `line` carries `relDir` as a quoted value. */
|
|
303
|
+
function dirsLineNames(line, relDir) {
|
|
304
|
+
const escaped = escapeRegex(relDir.replace(/\/+$/, ''));
|
|
305
|
+
return new RegExp(`["']${escaped}\\/?["']`).test(line);
|
|
306
|
+
}
|
|
307
|
+
/**
|
|
308
|
+
* Determines whether the source file's DIRECTORY is excluded from this
|
|
309
|
+
* build by a gate in an ANCESTOR `moz.build`.
|
|
310
|
+
*
|
|
311
|
+
* `detectPlatformGate` reads only the file's own nearest `moz.build`, which
|
|
312
|
+
* misses the common Firefox shape where the whole subtree is reached
|
|
313
|
+
* through a conditional `DIRS +=` one or more levels up: nothing in
|
|
314
|
+
* `toolkit/mozapps/defaultagent/moz.build` says "Windows only", yet none of
|
|
315
|
+
* it is built on macOS.
|
|
316
|
+
*
|
|
317
|
+
* Errs toward NOT skipping, like the rest of this module: an unreadable or
|
|
318
|
+
* oversized ancestor, or a directory no `DIRS` entry names, yields
|
|
319
|
+
* `gatedOff: false`.
|
|
320
|
+
*
|
|
321
|
+
* @param engineDir - Absolute path to the engine root
|
|
322
|
+
* @param sourcePath - Engine-relative POSIX path of the source file
|
|
323
|
+
*/
|
|
324
|
+
export async function detectAncestorDirsGate(engineDir, sourcePath) {
|
|
325
|
+
let host;
|
|
326
|
+
try {
|
|
327
|
+
host = getPlatform();
|
|
328
|
+
}
|
|
329
|
+
catch {
|
|
330
|
+
return { gatedOff: false };
|
|
331
|
+
}
|
|
332
|
+
const segments = sourcePath.split('/').filter(Boolean);
|
|
333
|
+
// Drop the basename: the walk is over DIRECTORIES.
|
|
334
|
+
segments.pop();
|
|
335
|
+
const root = engineDir.replace(/[/\\]+$/, '');
|
|
336
|
+
const depth = Math.min(segments.length, MAX_ANCESTOR_DEPTH);
|
|
337
|
+
// Start at the immediate parent of the file's directory and walk up, so
|
|
338
|
+
// the nearest gating ancestor is found first.
|
|
339
|
+
for (let cut = segments.length - 1; cut >= segments.length - depth; cut -= 1) {
|
|
340
|
+
const ancestorSegments = segments.slice(0, cut);
|
|
341
|
+
const relDir = segments.slice(cut).join('/');
|
|
342
|
+
if (relDir === '')
|
|
343
|
+
continue;
|
|
344
|
+
const mozBuild = join(root, ...ancestorSegments, 'moz.build');
|
|
345
|
+
if (!(await pathExists(mozBuild)))
|
|
346
|
+
continue;
|
|
347
|
+
let content;
|
|
348
|
+
try {
|
|
349
|
+
const stats = await stat(mozBuild);
|
|
350
|
+
if (stats.size > MAX_MOZ_BUILD_BYTES)
|
|
351
|
+
continue;
|
|
352
|
+
content = await readText(mozBuild);
|
|
353
|
+
}
|
|
354
|
+
catch {
|
|
355
|
+
continue;
|
|
356
|
+
}
|
|
357
|
+
const gates = findDirsEntryGates(content, relDir);
|
|
358
|
+
if (gates === undefined)
|
|
359
|
+
continue;
|
|
360
|
+
const offHost = gates.find((expression) => isGateOffHost(expression, host));
|
|
361
|
+
if (offHost !== undefined) {
|
|
362
|
+
return {
|
|
363
|
+
gatedOff: true,
|
|
364
|
+
gateExpression: offHost,
|
|
365
|
+
gateFile: [...ancestorSegments, 'moz.build'].join('/'),
|
|
366
|
+
};
|
|
367
|
+
}
|
|
368
|
+
// The directory IS reached from here and this host passes every gate;
|
|
369
|
+
// a further ancestor cannot un-reach it.
|
|
370
|
+
return { gatedOff: false };
|
|
371
|
+
}
|
|
372
|
+
return { gatedOff: false };
|
|
373
|
+
}
|
|
@@ -25,6 +25,7 @@
|
|
|
25
25
|
*/
|
|
26
26
|
import { basename, dirname, join, relative, sep } from 'node:path';
|
|
27
27
|
import { pathExists, readText } from '../utils/fs.js';
|
|
28
|
+
import { normalizePathSlashes } from '../utils/paths.js';
|
|
28
29
|
import { findAllByBasename } from './build-audit-resolve.js';
|
|
29
30
|
/** Ceiling on ancestor-directory hops when searching for an owning jar.mn. */
|
|
30
31
|
const MAX_JAR_MN_SCAN_DEPTH = 8;
|
|
@@ -65,7 +66,7 @@ export function parseJarMnEntry(line) {
|
|
|
65
66
|
* practice, so a first-match wins behaviour is adequate.
|
|
66
67
|
*/
|
|
67
68
|
export function findJarMnEntryForSource(content, relativeSource) {
|
|
68
|
-
const normalized = relativeSource
|
|
69
|
+
const normalized = normalizePathSlashes(relativeSource);
|
|
69
70
|
for (const line of content.split('\n')) {
|
|
70
71
|
const parsed = parseJarMnEntry(line);
|
|
71
72
|
if (!parsed)
|
|
@@ -121,7 +122,7 @@ export async function findRegisteredTarget(engineDir, source) {
|
|
|
121
122
|
* independent.
|
|
122
123
|
*/
|
|
123
124
|
function toPosix(path) {
|
|
124
|
-
return path
|
|
125
|
+
return normalizePathSlashes(path);
|
|
125
126
|
}
|
|
126
127
|
/**
|
|
127
128
|
* Probes the dist tree for the artifact registered against the given
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Why a changed engine path was not audited.
|
|
3
|
+
*
|
|
4
|
+
* The first four name skips that already existed as unstructured
|
|
5
|
+
* `verbose()` strings; the last four are the 0.46.0 classifiers.
|
|
6
|
+
*/
|
|
7
|
+
export type AuditSkipReason = 'not-packageable' | 'platform-gated' | 'platform-gated-ancestor' | 'tests-not-packaged' | 'source-unreadable' | 'branding-not-selected' | 'storybook-story' | 'declared-unpackaged';
|
|
8
|
+
/** Render order for the `Packaged:` summary's per-class breakdown. */
|
|
9
|
+
export declare const AUDIT_SKIP_REASONS: readonly AuditSkipReason[];
|
|
10
|
+
/** True for a Storybook story module. */
|
|
11
|
+
export declare function isStorybookStoryPath(sourcePath: string): boolean;
|
|
12
|
+
/**
|
|
13
|
+
* Reads the branding directory this objdir was configured with.
|
|
14
|
+
*
|
|
15
|
+
* FireForge generates `engine/mozconfig` and writes the
|
|
16
|
+
* `ac_add_options --with-branding=…` line into it, so the selected branding
|
|
17
|
+
* is knowable from the tree. Returns undefined when the mozconfig cannot be
|
|
18
|
+
* read or carries no directive — the caller must then keep warning, because
|
|
19
|
+
* a skip that cannot name its evidence is a masked warning.
|
|
20
|
+
*
|
|
21
|
+
* @param engineDir - Absolute path to the engine root
|
|
22
|
+
* @returns Engine-relative branding path, or undefined
|
|
23
|
+
*/
|
|
24
|
+
export declare function resolveSelectedBranding(engineDir: string): Promise<string | undefined>;
|
|
25
|
+
/**
|
|
26
|
+
* True when `sourcePath` lives under a branding tree that is NOT the one
|
|
27
|
+
* this objdir was configured with.
|
|
28
|
+
*
|
|
29
|
+
* An unselected branding directory is not an input to this build at all, so
|
|
30
|
+
* its files can never appear in `dist/`. Worse, the basename walker matched
|
|
31
|
+
* them against the SELECTED branding's same-named artifact and reported the
|
|
32
|
+
* unselected file as "newer than its packaged artifact" — a stale warning
|
|
33
|
+
* about a comparison that has no meaning.
|
|
34
|
+
*
|
|
35
|
+
* @param sourcePath - Engine-relative POSIX path
|
|
36
|
+
* @param selectedBranding - Engine-relative selected branding path
|
|
37
|
+
*/
|
|
38
|
+
export declare function isUnselectedBrandingPath(sourcePath: string, selectedBranding: string | undefined): boolean;
|
|
39
|
+
/** One `buildAudit.unpackaged` carve-out from `fireforge.json`. */
|
|
40
|
+
export interface UnpackagedDeclaration {
|
|
41
|
+
/** Engine-relative path or glob. */
|
|
42
|
+
path: string;
|
|
43
|
+
/** Why this file is deliberately never packaged. Required, non-empty. */
|
|
44
|
+
reason: string;
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Finds the declaration admitting `sourcePath`, if any.
|
|
48
|
+
*
|
|
49
|
+
* Exact match or a `*`-within-one-segment glob, matching the
|
|
50
|
+
* `support-files` matcher's deliberate narrowness: `**` and other
|
|
51
|
+
* cross-directory shapes are not accepted, because a carve-out that
|
|
52
|
+
* silently widens is exactly the failure this feature is warned against.
|
|
53
|
+
*
|
|
54
|
+
* @param sourcePath - Engine-relative POSIX path
|
|
55
|
+
* @param declarations - The project's `buildAudit.unpackaged` entries
|
|
56
|
+
*/
|
|
57
|
+
export declare function matchUnpackagedDeclaration(sourcePath: string, declarations: readonly UnpackagedDeclaration[]): UnpackagedDeclaration | undefined;
|