@hominis/fireforge 0.15.2 → 0.15.4
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 +30 -1
- package/README.md +55 -1
- package/dist/src/commands/build.js +29 -2
- package/dist/src/commands/doctor-furnace.js +8 -1
- package/dist/src/commands/furnace/chrome-doc-templates.d.ts +49 -0
- package/dist/src/commands/furnace/chrome-doc-templates.js +151 -0
- package/dist/src/commands/furnace/chrome-doc.d.ts +34 -0
- package/dist/src/commands/furnace/chrome-doc.js +168 -0
- package/dist/src/commands/furnace/create-mochikit.d.ts +30 -0
- package/dist/src/commands/furnace/create-mochikit.js +70 -0
- package/dist/src/commands/furnace/create-templates.d.ts +32 -0
- package/dist/src/commands/furnace/create-templates.js +69 -0
- package/dist/src/commands/furnace/create.d.ts +17 -0
- package/dist/src/commands/furnace/create.js +54 -16
- package/dist/src/commands/furnace/index.d.ts +2 -1
- package/dist/src/commands/furnace/index.js +20 -3
- package/dist/src/commands/lint.d.ts +13 -1
- package/dist/src/commands/lint.js +33 -7
- package/dist/src/commands/wire.js +59 -6
- package/dist/src/core/browser-wire.d.ts +8 -0
- package/dist/src/core/browser-wire.js +2 -2
- package/dist/src/core/build-audit.d.ts +46 -0
- package/dist/src/core/build-audit.js +251 -0
- package/dist/src/core/build-baseline.d.ts +59 -0
- package/dist/src/core/build-baseline.js +83 -0
- package/dist/src/core/build-prepare.d.ts +20 -1
- package/dist/src/core/build-prepare.js +89 -4
- package/dist/src/core/furnace-operation.d.ts +2 -1
- package/dist/src/core/furnace-operation.js +13 -7
- package/dist/src/core/mach-error-hints.d.ts +29 -0
- package/dist/src/core/mach-error-hints.js +43 -0
- package/dist/src/core/mach.d.ts +5 -2
- package/dist/src/core/mach.js +31 -4
- package/dist/src/core/patch-lint-diff-tag.d.ts +33 -0
- package/dist/src/core/patch-lint-diff-tag.js +83 -0
- package/dist/src/core/wire-dom-fragment.d.ts +16 -4
- package/dist/src/core/wire-dom-fragment.js +32 -17
- package/dist/src/types/commands/options.d.ts +22 -0
- package/dist/src/types/commands/patches.d.ts +9 -0
- package/dist/src/types/furnace.d.ts +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -36,12 +36,41 @@
|
|
|
36
36
|
|
|
37
37
|
- New `furnace create --xpcshell` flag scaffolds an xpcshell test harness alongside (or instead of) the browser-chrome mochitest that `--with-tests` already produces. xpcshell runs headless without a `tabbrowser`, so storage-layer / observer-driven / module-loading code on forks that do not mount the upstream browser chrome (no `openLinkIn` → `URILoadingHelper`) can be covered without the harness complaining about a missing tab strip. Writes `test_<name>_module_loads.js` and an `xpcshell.toml` manifest into `engine/browser/base/content/test/<binary-name>-xpcshell/<component-name>/`. Registration in `XPCSHELL_TESTS_MANIFESTS` is left to the operator — the moz.build that should own the entry depends on where the component lives.
|
|
38
38
|
|
|
39
|
+
### Build audit
|
|
40
|
+
|
|
41
|
+
- `fireforge build` now runs a warn-only post-build dist-tree audit after a successful mach build. The audit diffs engine-relative paths touched since the last successful build (stored as `.fireforge/last-build.json`) against the dist bundle, and warns per file that is packageable-by-convention (`.js`/`.mjs`/`.css`/`.ftl`/`.xhtml`/`app/profile/…`) but has no matching artifact, or whose dist mtime is older than the source. Surfaces the class of bug where a new pref file or widget is edited but never registered in `moz.build` / `jar.mn` / `package-manifest.in` — the 2026-04-18 `hominis.js` pref-file incident is the motivating case. The audit is warn-only and never fails a successful build.
|
|
42
|
+
- Ends every build with a `Packaged: N updated, M stale, K missing, S skipped` summary so operators can distinguish a fast-because-incremental build from a fast-because-silently-skipped one without a post-build `find`.
|
|
43
|
+
- `fireforge build` auto-runs `mach configure` before the mach build step when any `moz.build`, `moz.configure`, or `Makefile.in` changed since the last successful build. Prevents the stale-backend trap where an incremental build skips work against a recursive-make backend that no longer matches the source tree. Emits a `Backend config changed; running mach configure first...` banner so the extra step is visible, and continues the build even if configure exits non-zero.
|
|
44
|
+
- Mach build failures with known-cryptic mozbuild errors now print actionable hints. First entry in the table: `mozbuild.preprocessor.Preprocessor.Error: no preprocessor directives found` prints `Use JS_PREFERENCE_FILES instead, or add at least one #filter / #expand directive to the file.` The hint table lives in `src/core/mach-error-hints.ts` and is extensible — new cryptic errors we diagnose get one-line hints added without touching the build wrapper.
|
|
45
|
+
|
|
46
|
+
### Lint
|
|
47
|
+
|
|
48
|
+
- `fireforge lint --since <git-rev>` tags each lint issue as `[introduced]` (file touched in the diff since `<git-rev>`) or `[cumulative]` (pre-existing patch-state drift). Output gains the tag prefix and the summary line splits counts (e.g. `Lint: 2 introduced error(s), 0 introduced warning(s); 5 cumulative error(s), 1 cumulative warning(s)`). Exit code semantics are unchanged — an introduced OR cumulative error still fails lint — but triage of "is the diff I just produced clean?" no longer requires mentally subtracting pre-existing noise from every report. Without `--since` the output is unchanged.
|
|
49
|
+
|
|
50
|
+
### Fork chrome-doc assumptions
|
|
51
|
+
|
|
52
|
+
- `fireforge doctor`'s `Furnace engine paths` check now reads `furnace.json.tokenHostDocuments` instead of hardcoding `browser/base/content/browser.xhtml`. Forks that replaced the stock chrome document were emitting a permanent "browser.xhtml missing" warning every doctor run; reusing the same field the `missing-token-link` validator already consumes means a fork configures chrome-doc paths once and both checks agree. Defaults to `['browser/base/content/browser.xhtml']` when unset — behaviour is unchanged for forks that ship the stock chrome document.
|
|
53
|
+
- `fireforge wire --dom` no longer hardcodes `browser/base/content/browser.xhtml` as the target chrome document. The target now resolves in this order: explicit `--target <path>` flag → first entry of `furnace.json.tokenHostDocuments` → upstream `browser/base/content/browser.xhtml`. Forks that replaced browser.xhtml were getting a cryptic "could not find insertion point in browser.xhtml" error that read like a FireForge bug; the resolved path now propagates into the dry-run plan, the success message, and the insertion-failure error so the actual target is surfaced. When the resolved target does not exist on disk, wire fails up-front with a pointer to `tokenHostDocuments` / `--target` rather than blowing up in the AST pass. The `addDomFragment` core now computes the `#include` directive relative to the target's own directory instead of a hardcoded `browser/base/content/`, so a target that lives elsewhere in the engine tree gets a correctly resolved include path.
|
|
54
|
+
|
|
55
|
+
### Furnace chrome-doc
|
|
56
|
+
|
|
57
|
+
- New `furnace chrome-doc create <name>` subcommand scaffolds a top-level chrome document (xhtml + js + css + ftl) plus the three jar.mn registrations (`browser/base/jar.mn`, `browser/themes/shared/jar.inc.mn`, `browser/locales/jar.mn`). Default emits titlebar-buttonbox markup and a `windowtype="navigator:browser"` shell; `--no-titlebar` produces a frameless overlay with the macOS `.titlebar-button { display: none }` carve-out. Mirrors the workflow for custom elements (`furnace create`) so hand-authoring mistakes — the `*` preprocessor flag, the startup-topic observer, the platform titlebar inheritance — are eliminated. All writes go through a rollback journal under the signal-handler pathway: a Ctrl+C mid-scaffold restores every touched file.
|
|
58
|
+
|
|
59
|
+
### Furnace create
|
|
60
|
+
|
|
61
|
+
- New `--test-style <mochikit|browser-chrome|xpcshell>` option for `furnace create --with-tests`. `mochikit` (the new default when `--with-tests` is set alone) scaffolds a `chrome://mochikit/...` test under `engine/toolkit/content/tests/widgets/test_<tag>.html` that loads the component module directly and smoke-asserts `customElements.whenDefined(tag)`. Runs today against forks whose top-level chrome document lacks a `tabbrowser` (the class of bug that forced `--xpcshell` for storage-layer code) because the harness doesn't traverse `URILoadingHelper.openLinkIn`. `browser-chrome` is the former default and requires a working tabbrowser; `xpcshell` is equivalent to `--xpcshell`. Backwards-compat: `--xpcshell` alone still works; conflicting flag combinations are rejected up-front.
|
|
62
|
+
|
|
63
|
+
### Run / signals
|
|
64
|
+
|
|
65
|
+
- `fireforge run` (and every other command that never registers a furnace mutation) no longer prints the alarming `Received SIGTERM; rolling back in-flight furnace mutations…` line when the CLI receives SIGINT or SIGTERM. The global signal handler now gates the rollback banner on the presence of at least one live mutation in the registry — plain launches exit silently with code 130/143 as they always did. A new regression test asserts `warn` is not called when no operations are registered.
|
|
66
|
+
|
|
39
67
|
### Internal
|
|
40
68
|
|
|
41
69
|
- Extracted `furnace-apply-ftl.ts`, `furnace-config-tokens.ts`, and `create-templates.ts` to keep apply / config / scaffolding files under the per-file LOC budget after the new features landed. `parseStringArray` is now exported from `furnace-config.ts` for cross-module reuse.
|
|
42
70
|
- New `src/core/marionette-preflight.ts` owns the `--doctor` probe and its teardown semantics.
|
|
43
71
|
- Test mocks for `furnace-registration.js` now cover the new `addLocaleFtlJarMnEntry` / `removeLocaleFtlJarMnEntry` exports; `config.js` mocks in apply-batch tests now cover `loadConfig` because the apply path reads `markerComment` from fireforge.json.
|
|
44
|
-
- Repo-wide scrub of fork-example mentions (`hominis.xhtml`, `HOMINIS` marker-comment examples, fixture tag names) in favour of a generic `mybrowser` / `MYBROWSER` placeholder. FireForge reads as fork-agnostic in docs and fixtures; the npm identity (`@hominis/fireforge`) is unchanged.
|
|
72
|
+
- Repo-wide scrub of fork-example mentions (`hominis.xhtml`, `HOMINIS` marker-comment examples, fixture tag names) in favour of a generic `mybrowser` / `MYBROWSER` placeholder. FireForge reads as fork-agnostic in docs and fixtures; the npm identity (`@hominis/fireforge`) is unchanged. Closes a v0.15.0 slip-through (one `@hominis/fireforge` reference remained in `src/core/furnace-operation.ts` as a generic example alongside the npm-identity occurrences; the code example is now fork-neutral).
|
|
73
|
+
- New modules landed under coverage-gate protection: `src/core/mach-error-hints.ts`, `src/core/build-audit.ts`, `src/core/build-baseline.ts`, `src/core/patch-lint-diff-tag.ts`, `src/commands/furnace/chrome-doc.ts`, `src/commands/furnace/chrome-doc-templates.ts`, `src/commands/furnace/create-mochikit.ts`. Per-module thresholds added to `scripts/check-coverage-thresholds.mjs`.
|
|
45
74
|
|
|
46
75
|
## 0.14.0
|
|
47
76
|
|
package/README.md
CHANGED
|
@@ -293,6 +293,7 @@ There are three component types:
|
|
|
293
293
|
fireforge furnace scan # discover components in the engine
|
|
294
294
|
fireforge furnace override moz-button -t css-only # fork with CSS-only restyle
|
|
295
295
|
fireforge furnace create moz-my-widget # scaffold a new component
|
|
296
|
+
fireforge furnace chrome-doc create mybrowser # scaffold a top-level chrome document
|
|
296
297
|
fireforge furnace deploy # apply to engine/ + validate
|
|
297
298
|
fireforge furnace status # workspace vs engine drift
|
|
298
299
|
fireforge furnace diff moz-button # unified diff against baseline
|
|
@@ -300,6 +301,39 @@ fireforge furnace diff moz-button # unified diff against baseli
|
|
|
300
301
|
|
|
301
302
|
`furnace deploy` validates components before applying. As always, errors block, warnings are advisory. `fireforge build` and `fireforge test --build` run apply automatically — when apply wrote files during a build, the build prints a `Furnace: source → engine sync wrote N component(s) …` banner naming every component that was synced, so it is obvious whether engine/ was freshly updated. Use `fireforge doctor --repair-furnace` if the engine gets out of sync.
|
|
302
303
|
|
|
304
|
+
### Scaffolding top-level chrome documents
|
|
305
|
+
|
|
306
|
+
Custom elements live under `toolkit/content/widgets`, but a fork's top-level chrome document (`browser.xhtml` equivalents like `mybrowser.xhtml`, `about:*` panels, onboarding flows) lives at `browser/base/content/` and needs jar.mn, jar.inc.mn, and locales/jar.mn entries to reach the packaged bundle. `furnace chrome-doc create <name>` handles that boilerplate:
|
|
307
|
+
|
|
308
|
+
```bash
|
|
309
|
+
fireforge furnace chrome-doc create mybrowser # full chrome (titlebar + windowtype)
|
|
310
|
+
fireforge furnace chrome-doc create overlay --no-titlebar # frameless overlay
|
|
311
|
+
```
|
|
312
|
+
|
|
313
|
+
The command writes:
|
|
314
|
+
|
|
315
|
+
- `engine/browser/base/content/<name>.xhtml` — XHTML shell, optional titlebar-buttonbox, Fluent `<link>`.
|
|
316
|
+
- `engine/browser/base/content/<name>.js` — startup-topic observer fired on first idle.
|
|
317
|
+
- `engine/browser/themes/shared/<name>-chrome.css` — scoped CSS; emits the macOS `.titlebar-button { display: none }` carve-out under `--no-titlebar`.
|
|
318
|
+
- `engine/browser/locales/en-US/browser/<name>.ftl` — Fluent stub keyed on `<name>-window-title`.
|
|
319
|
+
- Appends the corresponding `jar.mn` / `jar.inc.mn` / `locales/jar.mn` entries.
|
|
320
|
+
|
|
321
|
+
Writes are transactional: a SIGINT mid-scaffold rolls back every touched file. Requires an existing engine — run `fireforge download` first.
|
|
322
|
+
|
|
323
|
+
### Picking a test harness for `furnace create`
|
|
324
|
+
|
|
325
|
+
`furnace create --with-tests` defaults to a MochiKit test at `engine/toolkit/content/tests/widgets/test_<tag>.html`. MochiKit tests load the component module via `chrome://global/` and don't need a `tabbrowser`, so they run against any fork — including bespoke chrome documents (`mybrowser.xhtml`-class) that deliberately omit the upstream browser chrome.
|
|
326
|
+
|
|
327
|
+
Three styles are available via `--test-style`:
|
|
328
|
+
|
|
329
|
+
| Style | When to use |
|
|
330
|
+
| ---------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
331
|
+
| `mochikit` | Default. Pure-UI custom elements. Runs today against non-tabbrowser chrome. Emits `test_<tag>.html` under `toolkit/content/tests/widgets/`. |
|
|
332
|
+
| `browser-chrome` | Element talks to the browser window (open URLs, manipulate tabs). Requires a working `tabbrowser` in the chrome document. Emits the `browser_<bin>_<tag>.js` scaffold and registers it in `browser/base/moz.build`. |
|
|
333
|
+
| `xpcshell` | Storage-layer, observer-driven, or ESM-loading code. Headless, no tabbrowser. Emits `test_<name>_module_loads.js` + `xpcshell.toml` (registration in `XPCSHELL_TESTS_MANIFESTS` is left to the operator). |
|
|
334
|
+
|
|
335
|
+
`--xpcshell` is preserved as an alias for `--test-style=xpcshell`; conflicting flag combinations (`--xpcshell --test-style=mochikit`) are rejected.
|
|
336
|
+
|
|
303
337
|
## Additional Commands
|
|
304
338
|
|
|
305
339
|
The commands below cover project configuration, patch queue management, build packaging and development utilities. Run `fireforge <command> --help` for full option details.
|
|
@@ -348,6 +382,26 @@ fireforge watch
|
|
|
348
382
|
fireforge token --name "--my-color" --value "light-dark(#fff, #000)"
|
|
349
383
|
```
|
|
350
384
|
|
|
385
|
+
### Diff-scoped lint (`lint --since`)
|
|
386
|
+
|
|
387
|
+
`fireforge lint --since <git-rev>` tags each issue as `[introduced]` or `[cumulative]` based on whether its file changed since `<git-rev>`:
|
|
388
|
+
|
|
389
|
+
```bash
|
|
390
|
+
fireforge lint --since HEAD~1 # just the current commit
|
|
391
|
+
fireforge lint --since main # everything since main
|
|
392
|
+
fireforge lint --since abc1234 # since a specific SHA
|
|
393
|
+
```
|
|
394
|
+
|
|
395
|
+
The summary line splits counts — e.g. `Lint: 2 introduced error(s), 0 introduced warning(s); 5 cumulative error(s), 1 cumulative warning(s)` — so triage of "did my diff introduce any of these?" is a one-glance check on a large patch series. Exit code still fails on any error (introduced or cumulative); the flag is purely a display / audit tool. Without `--since`, output is unchanged.
|
|
396
|
+
|
|
397
|
+
### Post-build audit and auto-configure
|
|
398
|
+
|
|
399
|
+
`fireforge build` is a transactional step: after a successful mach build it audits the dist bundle against engine-relative paths touched since the last successful build, and warns per file that is packageable-by-convention (`.js`/`.mjs`/`.css`/`.ftl`/`.xhtml`/`app/profile/…`) but has no matching artifact or whose dist mtime is older than the source. Ends every build with a `Packaged: N updated, M stale, K missing, S skipped` summary. The audit is warn-only — it never fails a build that mach reported green.
|
|
400
|
+
|
|
401
|
+
The build also auto-runs `mach configure` before the mach build step when any `moz.build`, `moz.configure`, or `Makefile.in` changed since the last successful build. Prevents incremental builds from silently skipping work against a stale recursive-make backend. Emits a `Backend config changed; running mach configure first...` banner when it fires.
|
|
402
|
+
|
|
403
|
+
Mach build failures with known-cryptic mozbuild errors now print actionable hints. Example: a `JS_PREFERENCE_PP_FILES` entry with no `#filter` / `#expand` directives now prints `Hint: ...use JS_PREFERENCE_FILES instead, or add at least one #filter / #expand directive to the file.` alongside the raw mach traceback.
|
|
404
|
+
|
|
351
405
|
## Configuration
|
|
352
406
|
|
|
353
407
|
`fireforge.json` at your project root:
|
|
@@ -375,7 +429,7 @@ fireforge token --name "--my-color" --value "light-dark(#fff, #000)"
|
|
|
375
429
|
|
|
376
430
|
**`markerComment`** (optional). Appended as a ` // <marker>:` suffix to every line FireForge writes into upstream Firefox source files (starting with `customElements.js`). Keeps fork modifications discoverable and makes re-apply idempotent without hand-tagging entries after each `furnace apply`. Reject list: empty strings, leading/trailing whitespace, newlines, `*/` (would close an enclosing block comment), control characters.
|
|
377
431
|
|
|
378
|
-
**`furnace.json.tokenHostDocuments`** (optional). List of chrome XHTML documents the `missing-token-link` validator scans for the tokens CSS link. Forks with a second chrome host (e.g. `mybrowser.xhtml` alongside `browser.xhtml`) should list every document that may own the link — the rule fires only when NONE of them link the tokens CSS. Defaults to `["browser/base/content/browser.xhtml"]` when omitted.
|
|
432
|
+
**`furnace.json.tokenHostDocuments`** (optional). List of chrome XHTML documents the `missing-token-link` validator scans for the tokens CSS link. Forks with a second chrome host (e.g. `mybrowser.xhtml` alongside `browser.xhtml`) should list every document that may own the link — the rule fires only when NONE of them link the tokens CSS. Defaults to `["browser/base/content/browser.xhtml"]` when omitted. `fireforge doctor`'s engine-paths probe reads the same field when confirming the chrome document exists on disk, and `fireforge wire --dom` uses the first entry as the default target for its `#include` directive (override per-invocation with `--target <path>`). Forks that fully replaced `browser.xhtml` with a custom top-level chrome document configure this field once and both checks agree.
|
|
379
433
|
|
|
380
434
|
### `furnace create --localized` for `MozLitElement`
|
|
381
435
|
|
|
@@ -1,11 +1,14 @@
|
|
|
1
1
|
// SPDX-License-Identifier: EUPL-1.2
|
|
2
2
|
import { InvalidArgumentError as CommanderInvalidArgumentError } from 'commander';
|
|
3
3
|
import { validateBrandOverride } from '../core/brand-validation.js';
|
|
4
|
+
import { auditBuildArtifacts } from '../core/build-audit.js';
|
|
5
|
+
import { readBuildBaseline, writeBuildBaseline } from '../core/build-baseline.js';
|
|
4
6
|
import { prepareBuildEnvironment } from '../core/build-prepare.js';
|
|
5
7
|
import { getProjectPaths, loadConfig } from '../core/config.js';
|
|
6
8
|
import { build, buildArtifactMismatchMessage, buildUI, hasBuildArtifacts } from '../core/mach.js';
|
|
7
9
|
import { GeneralError } from '../errors/base.js';
|
|
8
10
|
import { AmbiguousBuildArtifactsError, BuildError } from '../errors/build.js';
|
|
11
|
+
import { toError } from '../utils/errors.js';
|
|
9
12
|
import { checkDiskSpace, pathExists } from '../utils/fs.js';
|
|
10
13
|
import { error, info, intro, outro, verbose, warn } from '../utils/logger.js';
|
|
11
14
|
import { pickDefined } from '../utils/options.js';
|
|
@@ -60,8 +63,13 @@ export async function buildCommand(projectRoot, options) {
|
|
|
60
63
|
// Future: Load brand-specific config from fireforge.json brands section
|
|
61
64
|
info(`Brand: ${options.brand}`);
|
|
62
65
|
}
|
|
63
|
-
//
|
|
64
|
-
|
|
66
|
+
// Read the previous build baseline BEFORE prepareBuildEnvironment so the
|
|
67
|
+
// auto-configure step there can detect moz.build-family changes since the
|
|
68
|
+
// last successful build. The post-build audit below reuses the same
|
|
69
|
+
// baseline to diff engine changes against dist artifacts.
|
|
70
|
+
const previousBaseline = await readBuildBaseline(projectRoot);
|
|
71
|
+
// Shared pre-flight: branding, Furnace, mozconfig, auto-configure
|
|
72
|
+
await prepareBuildEnvironment(projectRoot, paths, config, { previousBaseline });
|
|
65
73
|
const jobs = resolveJobCount(options, config.build?.jobs);
|
|
66
74
|
// Run build
|
|
67
75
|
info(`Starting ${buildType.toLowerCase()} build...`);
|
|
@@ -90,6 +98,25 @@ export async function buildCommand(projectRoot, options) {
|
|
|
90
98
|
error(`Build failed after ${timeStr}`);
|
|
91
99
|
throw new BuildError(`Build failed with exit code ${exitCode}`, options.ui ? 'mach build faster' : 'mach build');
|
|
92
100
|
}
|
|
101
|
+
// Warn-only post-build audit: surfaces silent packaging drops (files
|
|
102
|
+
// edited in engine/ but never registered for packaging) against the
|
|
103
|
+
// previous-build baseline. Never fails the build; the worst case is a
|
|
104
|
+
// warning an operator chooses to investigate.
|
|
105
|
+
try {
|
|
106
|
+
await auditBuildArtifacts(projectRoot, paths.engine, previousBaseline);
|
|
107
|
+
}
|
|
108
|
+
catch (auditError) {
|
|
109
|
+
verbose(`Audit skipped: ${toError(auditError).message}`);
|
|
110
|
+
}
|
|
111
|
+
// Record a fresh baseline only on clean success so the next run audits
|
|
112
|
+
// against this build's HEAD. A failed build keeps the prior baseline so
|
|
113
|
+
// the next attempt still catches long-standing packaging drops.
|
|
114
|
+
try {
|
|
115
|
+
await writeBuildBaseline(projectRoot, paths.engine, config.binaryName);
|
|
116
|
+
}
|
|
117
|
+
catch (baselineError) {
|
|
118
|
+
verbose(`Could not persist build baseline: ${toError(baselineError).message}`);
|
|
119
|
+
}
|
|
93
120
|
outro(`Build completed in ${timeStr}!`);
|
|
94
121
|
}
|
|
95
122
|
/** Registers the build command on the CLI program. */
|
|
@@ -319,12 +319,19 @@ const furnaceEnginePathsCheck = {
|
|
|
319
319
|
dependsOn: ['Furnace configuration'],
|
|
320
320
|
skipIf: (ctx) => !ctx.furnaceConfigExists || !ctx.furnaceConfig || !ctx.engineExists,
|
|
321
321
|
run: async (ctx) => {
|
|
322
|
+
// Forks that replace browser.xhtml with a custom top-level chrome
|
|
323
|
+
// document enumerate their chrome docs in furnace.json.tokenHostDocuments.
|
|
324
|
+
// Reuse the same field for the engine-path probe so those forks stop
|
|
325
|
+
// seeing a permanent "browser.xhtml missing" warning.
|
|
326
|
+
const hostDocs = ctx.furnaceConfig?.tokenHostDocuments && ctx.furnaceConfig.tokenHostDocuments.length > 0
|
|
327
|
+
? ctx.furnaceConfig.tokenHostDocuments
|
|
328
|
+
: ['browser/base/content/browser.xhtml'];
|
|
322
329
|
const expectedPaths = [
|
|
323
330
|
CUSTOM_ELEMENTS_JS,
|
|
324
331
|
JAR_MN,
|
|
325
332
|
'toolkit/content/widgets',
|
|
326
333
|
resolveFtlDir(ctx.furnaceConfig?.ftlBasePath),
|
|
327
|
-
|
|
334
|
+
...hostDocs,
|
|
328
335
|
];
|
|
329
336
|
const missing = [];
|
|
330
337
|
for (const relative of expectedPaths) {
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* File-content templates for `fireforge furnace chrome-doc create`.
|
|
3
|
+
* Extracted so the command entrypoint stays under the per-file LOC budget
|
|
4
|
+
* and each template can be exercised in isolation.
|
|
5
|
+
*
|
|
6
|
+
* The templates here mirror the shape of top-level chrome documents in
|
|
7
|
+
* upstream Firefox (browser.xhtml, privatebrowsing/aboutPrivateBrowsing.html,
|
|
8
|
+
* etc.) minus the fork-specific wiring. A fork author fills in the body.
|
|
9
|
+
*/
|
|
10
|
+
/**
|
|
11
|
+
* XHTML shell for a top-level chrome document.
|
|
12
|
+
*
|
|
13
|
+
* The emitted document:
|
|
14
|
+
* - Declares `windowtype="navigator:browser"` when `withTitlebar` is true
|
|
15
|
+
* so chrome-wide stylesheets that target the browser window still apply.
|
|
16
|
+
* - Emits a titlebar-buttonbox placeholder when `withTitlebar` is true so
|
|
17
|
+
* platform-native window controls render.
|
|
18
|
+
* - Links the per-document CSS at `chrome://browser/content/<name>-chrome.css`
|
|
19
|
+
* and the Fluent bundle `browser/<name>.ftl`.
|
|
20
|
+
*/
|
|
21
|
+
export declare function generateChromeDocXhtml(name: string, withTitlebar: boolean, license: string): string;
|
|
22
|
+
/**
|
|
23
|
+
* ESM bootstrap script for the chrome document.
|
|
24
|
+
*
|
|
25
|
+
* The generated script fires a startup observer topic on the first idle
|
|
26
|
+
* callback so other chrome code can wait on the document being ready.
|
|
27
|
+
* Mirrors the pattern FireForge-built forks use to coordinate per-window
|
|
28
|
+
* init across multiple top-level documents.
|
|
29
|
+
*/
|
|
30
|
+
export declare function generateChromeDocJs(name: string, licenseHeader: string): string;
|
|
31
|
+
/**
|
|
32
|
+
* Scoped CSS for a chrome document. When `withTitlebar` is false the
|
|
33
|
+
* macOS `.titlebar-button { display: none }` carve-out is emitted so
|
|
34
|
+
* frameless overlay-style documents don't inherit the platform window
|
|
35
|
+
* controls that `global.css` applies by default.
|
|
36
|
+
*/
|
|
37
|
+
export declare function generateChromeDocCss(name: string, withTitlebar: boolean, licenseHeader: string): string;
|
|
38
|
+
/** Fluent stub — one placeholder message keyed to the window title. */
|
|
39
|
+
export declare function generateChromeDocFtl(name: string, licenseHeader: string): string;
|
|
40
|
+
/**
|
|
41
|
+
* Single-line jar.mn entry that registers an xhtml + js pair under
|
|
42
|
+
* `content/browser/`. Emits the `*` preprocessor flag so both files flow
|
|
43
|
+
* through `#filter substitution` for FireForge brand-name substitution.
|
|
44
|
+
*/
|
|
45
|
+
export declare function jarMnEntriesForChromeDoc(name: string): string[];
|
|
46
|
+
/** jar.inc.mn entry that registers the scoped CSS under `content/browser/`. */
|
|
47
|
+
export declare function jarIncMnEntryForChromeDoc(name: string): string;
|
|
48
|
+
/** locales/jar.mn entry that registers the `.ftl` under the browser locale bundle. */
|
|
49
|
+
export declare function localeJarMnEntryForChromeDoc(name: string): string;
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
// SPDX-License-Identifier: EUPL-1.2
|
|
2
|
+
/**
|
|
3
|
+
* File-content templates for `fireforge furnace chrome-doc create`.
|
|
4
|
+
* Extracted so the command entrypoint stays under the per-file LOC budget
|
|
5
|
+
* and each template can be exercised in isolation.
|
|
6
|
+
*
|
|
7
|
+
* The templates here mirror the shape of top-level chrome documents in
|
|
8
|
+
* upstream Firefox (browser.xhtml, privatebrowsing/aboutPrivateBrowsing.html,
|
|
9
|
+
* etc.) minus the fork-specific wiring. A fork author fills in the body.
|
|
10
|
+
*/
|
|
11
|
+
/**
|
|
12
|
+
* XHTML shell for a top-level chrome document.
|
|
13
|
+
*
|
|
14
|
+
* The emitted document:
|
|
15
|
+
* - Declares `windowtype="navigator:browser"` when `withTitlebar` is true
|
|
16
|
+
* so chrome-wide stylesheets that target the browser window still apply.
|
|
17
|
+
* - Emits a titlebar-buttonbox placeholder when `withTitlebar` is true so
|
|
18
|
+
* platform-native window controls render.
|
|
19
|
+
* - Links the per-document CSS at `chrome://browser/content/<name>-chrome.css`
|
|
20
|
+
* and the Fluent bundle `browser/<name>.ftl`.
|
|
21
|
+
*/
|
|
22
|
+
export function generateChromeDocXhtml(name, withTitlebar, license) {
|
|
23
|
+
const windowAttr = withTitlebar ? ' windowtype="navigator:browser"' : '';
|
|
24
|
+
const titlebarMarkup = withTitlebar
|
|
25
|
+
? `
|
|
26
|
+
<hbox class="titlebar-buttonbox-container">
|
|
27
|
+
<hbox class="titlebar-buttonbox"></hbox>
|
|
28
|
+
</hbox>`
|
|
29
|
+
: '';
|
|
30
|
+
return `<?xml version="1.0"?>
|
|
31
|
+
<!-- SPDX-License-Identifier: ${license} -->
|
|
32
|
+
<!DOCTYPE window>
|
|
33
|
+
<window
|
|
34
|
+
xmlns="http://www.w3.org/1999/xhtml"
|
|
35
|
+
xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
|
|
36
|
+
id="${name}-window"${windowAttr}
|
|
37
|
+
data-l10n-id="${name}-window-title"
|
|
38
|
+
role="application">
|
|
39
|
+
<head>
|
|
40
|
+
<meta charset="utf-8" />
|
|
41
|
+
<title data-l10n-id="${name}-window-title"></title>
|
|
42
|
+
<link rel="localization" href="browser/${name}.ftl" />
|
|
43
|
+
<link rel="stylesheet" href="chrome://global/skin/global.css" />
|
|
44
|
+
<link rel="stylesheet" href="chrome://browser/content/${name}-chrome.css" />
|
|
45
|
+
<script src="chrome://browser/content/${name}.js"></script>
|
|
46
|
+
</head>
|
|
47
|
+
<body>${titlebarMarkup}
|
|
48
|
+
<main id="${name}-main"></main>
|
|
49
|
+
</body>
|
|
50
|
+
</window>
|
|
51
|
+
`;
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* ESM bootstrap script for the chrome document.
|
|
55
|
+
*
|
|
56
|
+
* The generated script fires a startup observer topic on the first idle
|
|
57
|
+
* callback so other chrome code can wait on the document being ready.
|
|
58
|
+
* Mirrors the pattern FireForge-built forks use to coordinate per-window
|
|
59
|
+
* init across multiple top-level documents.
|
|
60
|
+
*/
|
|
61
|
+
export function generateChromeDocJs(name, licenseHeader) {
|
|
62
|
+
const topic = `${name}-startup`;
|
|
63
|
+
return `${licenseHeader}
|
|
64
|
+
|
|
65
|
+
"use strict";
|
|
66
|
+
|
|
67
|
+
// Fire a startup topic on first idle so subscribers can defer their own
|
|
68
|
+
// init until this document is ready. Observers see the document element as
|
|
69
|
+
// the subject; use \`aSubject instanceof Ci.nsIDOMWindow\` if you need the
|
|
70
|
+
// containing window instead.
|
|
71
|
+
window.addEventListener(
|
|
72
|
+
"DOMContentLoaded",
|
|
73
|
+
() => {
|
|
74
|
+
window.requestIdleCallback?.(() => {
|
|
75
|
+
try {
|
|
76
|
+
Services.obs.notifyObservers(document.documentElement, "${topic}");
|
|
77
|
+
} catch (error) {
|
|
78
|
+
// Observer notifications should never block document init — log
|
|
79
|
+
// but don't throw so a missing observer service (headless / test
|
|
80
|
+
// harness) still leaves the document usable.
|
|
81
|
+
console.error("Failed to fire ${topic} observer:", error);
|
|
82
|
+
}
|
|
83
|
+
});
|
|
84
|
+
},
|
|
85
|
+
{ once: true }
|
|
86
|
+
);
|
|
87
|
+
`;
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Scoped CSS for a chrome document. When `withTitlebar` is false the
|
|
91
|
+
* macOS `.titlebar-button { display: none }` carve-out is emitted so
|
|
92
|
+
* frameless overlay-style documents don't inherit the platform window
|
|
93
|
+
* controls that `global.css` applies by default.
|
|
94
|
+
*/
|
|
95
|
+
export function generateChromeDocCss(name, withTitlebar, licenseHeader) {
|
|
96
|
+
const titlebarOverrides = withTitlebar
|
|
97
|
+
? ''
|
|
98
|
+
: `
|
|
99
|
+
|
|
100
|
+
/* Frameless overlay — suppress the platform titlebar buttons that
|
|
101
|
+
global.css inherits on macOS. Without this carve-out the traffic-light
|
|
102
|
+
controls render even though the document has no titlebar-buttonbox. */
|
|
103
|
+
:root[windowtype="navigator:browser"] .titlebar-button {
|
|
104
|
+
display: none;
|
|
105
|
+
}
|
|
106
|
+
`;
|
|
107
|
+
return `${licenseHeader}
|
|
108
|
+
|
|
109
|
+
:root {
|
|
110
|
+
--${name}-padding: 16px;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
#${name}-window {
|
|
114
|
+
display: flex;
|
|
115
|
+
flex-direction: column;
|
|
116
|
+
min-height: 100vh;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
#${name}-main {
|
|
120
|
+
flex: 1;
|
|
121
|
+
padding: var(--${name}-padding);
|
|
122
|
+
}${titlebarOverrides}
|
|
123
|
+
`;
|
|
124
|
+
}
|
|
125
|
+
/** Fluent stub — one placeholder message keyed to the window title. */
|
|
126
|
+
export function generateChromeDocFtl(name, licenseHeader) {
|
|
127
|
+
return `${licenseHeader}
|
|
128
|
+
|
|
129
|
+
${name}-window-title = ${name}
|
|
130
|
+
`;
|
|
131
|
+
}
|
|
132
|
+
/**
|
|
133
|
+
* Single-line jar.mn entry that registers an xhtml + js pair under
|
|
134
|
+
* `content/browser/`. Emits the `*` preprocessor flag so both files flow
|
|
135
|
+
* through `#filter substitution` for FireForge brand-name substitution.
|
|
136
|
+
*/
|
|
137
|
+
export function jarMnEntriesForChromeDoc(name) {
|
|
138
|
+
return [
|
|
139
|
+
`* content/browser/${name}.xhtml (content/${name}.xhtml)`,
|
|
140
|
+
` content/browser/${name}.js (content/${name}.js)`,
|
|
141
|
+
];
|
|
142
|
+
}
|
|
143
|
+
/** jar.inc.mn entry that registers the scoped CSS under `content/browser/`. */
|
|
144
|
+
export function jarIncMnEntryForChromeDoc(name) {
|
|
145
|
+
return ` content/browser/${name}-chrome.css (shared/${name}-chrome.css)`;
|
|
146
|
+
}
|
|
147
|
+
/** locales/jar.mn entry that registers the `.ftl` under the browser locale bundle. */
|
|
148
|
+
export function localeJarMnEntryForChromeDoc(name) {
|
|
149
|
+
return ` locale/browser/${name}.ftl (%${name}.ftl)`;
|
|
150
|
+
}
|
|
151
|
+
//# sourceMappingURL=chrome-doc-templates.js.map
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `fireforge furnace chrome-doc create <name>` — scaffolds a top-level
|
|
3
|
+
* chrome document (xhtml + js + css + ftl + jar.mn registrations).
|
|
4
|
+
*
|
|
5
|
+
* Motivation: `furnace create` covers custom elements under
|
|
6
|
+
* `toolkit/content/widgets/`, but top-level chrome documents (the
|
|
7
|
+
* `mybrowser.xhtml`-class entry points a fork adds alongside or instead
|
|
8
|
+
* of `browser.xhtml`) are today hand-authored with error-prone jar.mn +
|
|
9
|
+
* jar.inc.mn + locales/jar.mn glue. The `*` preprocessor flag, the
|
|
10
|
+
* macOS titlebar-button carve-out, the startup-topic observer, and the
|
|
11
|
+
* Fluent linkage each have silent-break failure modes.
|
|
12
|
+
*
|
|
13
|
+
* This command writes the four source files and appends three jar.mn
|
|
14
|
+
* entries under a rollback journal identical in shape to `furnace create`.
|
|
15
|
+
* A SIGINT mid-scaffold restores every touched file; a successful run
|
|
16
|
+
* leaves the tree ready for `fireforge build`.
|
|
17
|
+
*/
|
|
18
|
+
/** Options for `furnace chrome-doc create`. */
|
|
19
|
+
export interface FurnaceChromeDocCreateOptions {
|
|
20
|
+
/**
|
|
21
|
+
* Emit the titlebar-buttonbox markup and leave platform window controls
|
|
22
|
+
* visible. Defaults to `true` because the common case is a full chrome
|
|
23
|
+
* document (not a frameless overlay). Frameless callers pass
|
|
24
|
+
* `--no-titlebar`.
|
|
25
|
+
*/
|
|
26
|
+
titlebar?: boolean;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Runs `furnace chrome-doc create <name>`.
|
|
30
|
+
* @param projectRoot Root directory of the project.
|
|
31
|
+
* @param name Chrome-doc name (e.g. `mybrowser`, `aboutonboarding`).
|
|
32
|
+
* @param options CLI-provided options.
|
|
33
|
+
*/
|
|
34
|
+
export declare function furnaceChromeDocCreateCommand(projectRoot: string, name: string, options?: FurnaceChromeDocCreateOptions): Promise<void>;
|
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
// SPDX-License-Identifier: EUPL-1.2
|
|
2
|
+
/**
|
|
3
|
+
* `fireforge furnace chrome-doc create <name>` — scaffolds a top-level
|
|
4
|
+
* chrome document (xhtml + js + css + ftl + jar.mn registrations).
|
|
5
|
+
*
|
|
6
|
+
* Motivation: `furnace create` covers custom elements under
|
|
7
|
+
* `toolkit/content/widgets/`, but top-level chrome documents (the
|
|
8
|
+
* `mybrowser.xhtml`-class entry points a fork adds alongside or instead
|
|
9
|
+
* of `browser.xhtml`) are today hand-authored with error-prone jar.mn +
|
|
10
|
+
* jar.inc.mn + locales/jar.mn glue. The `*` preprocessor flag, the
|
|
11
|
+
* macOS titlebar-button carve-out, the startup-topic observer, and the
|
|
12
|
+
* Fluent linkage each have silent-break failure modes.
|
|
13
|
+
*
|
|
14
|
+
* This command writes the four source files and appends three jar.mn
|
|
15
|
+
* entries under a rollback journal identical in shape to `furnace create`.
|
|
16
|
+
* A SIGINT mid-scaffold restores every touched file; a successful run
|
|
17
|
+
* leaves the tree ready for `fireforge build`.
|
|
18
|
+
*/
|
|
19
|
+
import { join } from 'node:path';
|
|
20
|
+
import { loadConfig } from '../../core/config.js';
|
|
21
|
+
import { runFurnaceMutation } from '../../core/furnace-operation.js';
|
|
22
|
+
import { createRollbackJournal, recordCreatedDir, restoreRollbackJournalOrThrow, snapshotFile, } from '../../core/furnace-rollback.js';
|
|
23
|
+
import { DEFAULT_LICENSE, getLicenseHeader } from '../../core/license-headers.js';
|
|
24
|
+
import { InvalidArgumentError } from '../../errors/base.js';
|
|
25
|
+
import { FurnaceError } from '../../errors/furnace.js';
|
|
26
|
+
import { pathExists, readText, writeText } from '../../utils/fs.js';
|
|
27
|
+
import { intro, note, outro } from '../../utils/logger.js';
|
|
28
|
+
import { generateChromeDocCss, generateChromeDocFtl, generateChromeDocJs, generateChromeDocXhtml, jarIncMnEntryForChromeDoc, jarMnEntriesForChromeDoc, localeJarMnEntryForChromeDoc, } from './chrome-doc-templates.js';
|
|
29
|
+
/** Chrome-doc name shape: lowercase ASCII, optional hyphens, no leading digit. */
|
|
30
|
+
const CHROME_DOC_NAME_PATTERN = /^[a-z][a-z0-9]*(?:-[a-z0-9]+)*$/;
|
|
31
|
+
/**
|
|
32
|
+
* Validates a chrome-doc name. Lowercase ASCII, optional hyphens, no
|
|
33
|
+
* leading digit — the name is used verbatim in CSS selectors, jar.mn
|
|
34
|
+
* entries, FTL keys, and file basenames, so anything outside that
|
|
35
|
+
* character set would break at least one downstream consumer.
|
|
36
|
+
* @param name Chrome-doc name (file basename without extension).
|
|
37
|
+
* @throws InvalidArgumentError when the name is unusable.
|
|
38
|
+
*/
|
|
39
|
+
function validateChromeDocName(name) {
|
|
40
|
+
if (!name.trim()) {
|
|
41
|
+
throw new InvalidArgumentError('Chrome-doc name is required', 'name');
|
|
42
|
+
}
|
|
43
|
+
if (!CHROME_DOC_NAME_PATTERN.test(name)) {
|
|
44
|
+
throw new InvalidArgumentError('Chrome-doc name must be lowercase ASCII, may contain hyphens, and must not start with a digit (e.g. mybrowser, about-onboarding).', 'name');
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Appends a line to a jar.mn-style file when that exact line is not
|
|
49
|
+
* already present. Captures the pre-write contents in the journal so a
|
|
50
|
+
* mid-run interruption restores the file to its original state.
|
|
51
|
+
*/
|
|
52
|
+
async function appendJarEntryIfAbsent(filePath, entry, journal) {
|
|
53
|
+
if (!(await pathExists(filePath))) {
|
|
54
|
+
// Target jar.mn doesn't exist in this tree layout. We do NOT create it
|
|
55
|
+
// — a fork that moved the jar file needs the operator to choose a
|
|
56
|
+
// placement. The command surfaces this as a FurnaceError so the user
|
|
57
|
+
// can investigate rather than silently writing to a non-canonical path.
|
|
58
|
+
throw new FurnaceError(`Required jar file ${filePath} does not exist; cannot register chrome-doc entry. Check that the fork's engine layout matches the expected browser/ and locales/ tree.`);
|
|
59
|
+
}
|
|
60
|
+
const existing = await readText(filePath);
|
|
61
|
+
if (existing.includes(entry)) {
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
64
|
+
await snapshotFile(journal, filePath);
|
|
65
|
+
const withEntry = existing.trimEnd() + '\n' + entry + '\n';
|
|
66
|
+
await writeText(filePath, withEntry);
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Writes the xhtml/js/css/ftl source files plus the three jar.mn
|
|
70
|
+
* registrations under a rollback journal. Any interruption leaves the
|
|
71
|
+
* tree in its pre-command state.
|
|
72
|
+
*/
|
|
73
|
+
async function performChromeDocMutations(args) {
|
|
74
|
+
const journal = createRollbackJournal();
|
|
75
|
+
args.operationContext.registerJournal(journal);
|
|
76
|
+
// XHTML uses an inline XML comment since getLicenseHeader has no XML
|
|
77
|
+
// style — the SPDX convention is a single-line comment at the top.
|
|
78
|
+
const jsHeader = getLicenseHeader(args.license, 'js');
|
|
79
|
+
const cssHeader = getLicenseHeader(args.license, 'css');
|
|
80
|
+
const ftlHeader = getLicenseHeader(args.license, 'hash');
|
|
81
|
+
const written = [];
|
|
82
|
+
try {
|
|
83
|
+
const contentDir = join(args.engineDir, 'browser/base/content');
|
|
84
|
+
const sharedThemeDir = join(args.engineDir, 'browser/themes/shared');
|
|
85
|
+
const localeDir = join(args.engineDir, 'browser/locales/en-US/browser');
|
|
86
|
+
for (const dir of [contentDir, sharedThemeDir, localeDir]) {
|
|
87
|
+
if (!(await pathExists(dir))) {
|
|
88
|
+
recordCreatedDir(journal, dir);
|
|
89
|
+
const { ensureDir } = await import('../../utils/fs.js');
|
|
90
|
+
await ensureDir(dir);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
const xhtmlPath = join(contentDir, `${args.name}.xhtml`);
|
|
94
|
+
if (await pathExists(xhtmlPath)) {
|
|
95
|
+
throw new FurnaceError(`${args.name}.xhtml already exists at ${xhtmlPath}. Remove it or choose a different name.`);
|
|
96
|
+
}
|
|
97
|
+
await snapshotFile(journal, xhtmlPath);
|
|
98
|
+
await writeText(xhtmlPath, generateChromeDocXhtml(args.name, args.withTitlebar, args.license));
|
|
99
|
+
written.push(`browser/base/content/${args.name}.xhtml`);
|
|
100
|
+
const jsPath = join(contentDir, `${args.name}.js`);
|
|
101
|
+
await snapshotFile(journal, jsPath);
|
|
102
|
+
await writeText(jsPath, generateChromeDocJs(args.name, jsHeader));
|
|
103
|
+
written.push(`browser/base/content/${args.name}.js`);
|
|
104
|
+
const cssPath = join(sharedThemeDir, `${args.name}-chrome.css`);
|
|
105
|
+
await snapshotFile(journal, cssPath);
|
|
106
|
+
await writeText(cssPath, generateChromeDocCss(args.name, args.withTitlebar, cssHeader));
|
|
107
|
+
written.push(`browser/themes/shared/${args.name}-chrome.css`);
|
|
108
|
+
const ftlPath = join(localeDir, `${args.name}.ftl`);
|
|
109
|
+
await snapshotFile(journal, ftlPath);
|
|
110
|
+
await writeText(ftlPath, generateChromeDocFtl(args.name, ftlHeader));
|
|
111
|
+
written.push(`browser/locales/en-US/browser/${args.name}.ftl`);
|
|
112
|
+
// jar.mn registrations — XHTML + JS go through the `*` preprocessor
|
|
113
|
+
// for brand substitution, CSS goes through jar.inc.mn, FTL through
|
|
114
|
+
// the locale jar.
|
|
115
|
+
const jarMnPath = join(args.engineDir, 'browser/base/jar.mn');
|
|
116
|
+
for (const entry of jarMnEntriesForChromeDoc(args.name)) {
|
|
117
|
+
await appendJarEntryIfAbsent(jarMnPath, entry, journal);
|
|
118
|
+
}
|
|
119
|
+
written.push('browser/base/jar.mn');
|
|
120
|
+
const jarIncMnPath = join(args.engineDir, 'browser/themes/shared/jar.inc.mn');
|
|
121
|
+
await appendJarEntryIfAbsent(jarIncMnPath, jarIncMnEntryForChromeDoc(args.name), journal);
|
|
122
|
+
written.push('browser/themes/shared/jar.inc.mn');
|
|
123
|
+
const localeJarMnPath = join(args.engineDir, 'browser/locales/jar.mn');
|
|
124
|
+
await appendJarEntryIfAbsent(localeJarMnPath, localeJarMnEntryForChromeDoc(args.name), journal);
|
|
125
|
+
written.push('browser/locales/jar.mn');
|
|
126
|
+
}
|
|
127
|
+
catch (error) {
|
|
128
|
+
await restoreRollbackJournalOrThrow(journal, `Failed to scaffold chrome-doc "${args.name}"`);
|
|
129
|
+
throw error;
|
|
130
|
+
}
|
|
131
|
+
return written;
|
|
132
|
+
}
|
|
133
|
+
/**
|
|
134
|
+
* Runs `furnace chrome-doc create <name>`.
|
|
135
|
+
* @param projectRoot Root directory of the project.
|
|
136
|
+
* @param name Chrome-doc name (e.g. `mybrowser`, `aboutonboarding`).
|
|
137
|
+
* @param options CLI-provided options.
|
|
138
|
+
*/
|
|
139
|
+
export async function furnaceChromeDocCreateCommand(projectRoot, name, options = {}) {
|
|
140
|
+
intro('Furnace chrome-doc create');
|
|
141
|
+
validateChromeDocName(name);
|
|
142
|
+
const forgeConfig = await loadConfig(projectRoot);
|
|
143
|
+
const license = forgeConfig.license ?? DEFAULT_LICENSE;
|
|
144
|
+
const engineDir = join(projectRoot, 'engine');
|
|
145
|
+
if (!(await pathExists(engineDir))) {
|
|
146
|
+
throw new FurnaceError('Engine directory not found. Run "fireforge download" first to scaffold a chrome-doc.');
|
|
147
|
+
}
|
|
148
|
+
const withTitlebar = options.titlebar ?? true;
|
|
149
|
+
const written = await runFurnaceMutation(projectRoot, 'chrome-doc-rollback', (ctx) => performChromeDocMutations({
|
|
150
|
+
name,
|
|
151
|
+
license,
|
|
152
|
+
engineDir,
|
|
153
|
+
withTitlebar,
|
|
154
|
+
operationContext: ctx,
|
|
155
|
+
}));
|
|
156
|
+
note([
|
|
157
|
+
`Chrome document "${name}" scaffolded:`,
|
|
158
|
+
...written.map((f) => ` engine/${f}`),
|
|
159
|
+
'',
|
|
160
|
+
'Next steps:',
|
|
161
|
+
` 1. Edit engine/browser/base/content/${name}.xhtml and fill in the body.`,
|
|
162
|
+
` 2. Localize strings in engine/browser/locales/en-US/browser/${name}.ftl.`,
|
|
163
|
+
` 3. Run "fireforge build" to validate packaging (post-build audit will flag`,
|
|
164
|
+
' any entry whose file does not land in the dist bundle).',
|
|
165
|
+
].join('\n'), name);
|
|
166
|
+
outro('Chrome document created');
|
|
167
|
+
}
|
|
168
|
+
//# sourceMappingURL=chrome-doc.js.map
|