@hominis/fireforge 0.18.10 → 0.19.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 +6 -0
- package/README.md +21 -8
- package/dist/src/commands/doctor.js +1 -1
- package/dist/src/commands/furnace/index.js +1 -1
- package/dist/src/commands/manifest.js +2 -0
- package/dist/src/commands/package.js +1 -1
- package/dist/src/commands/test.js +8 -8
- package/dist/src/commands/typecheck.d.ts +52 -0
- package/dist/src/commands/typecheck.js +115 -0
- package/dist/src/core/config-paths.d.ts +2 -2
- package/dist/src/core/config-paths.js +7 -0
- package/dist/src/core/config-validate.js +111 -0
- package/dist/src/core/mach-build-artifacts.d.ts +2 -2
- package/dist/src/core/mach-build-artifacts.js +2 -2
- package/dist/src/core/mach-error-hints.js +7 -8
- package/dist/src/core/marionette-port.js +4 -4
- package/dist/src/core/patch-lint-checkjs.d.ts +30 -2
- package/dist/src/core/patch-lint-checkjs.js +78 -78
- package/dist/src/core/patch-lint-reexports.d.ts +9 -0
- package/dist/src/core/patch-lint-reexports.js +11 -0
- package/dist/src/core/patch-lint.d.ts +1 -5
- package/dist/src/core/patch-lint.js +6 -11
- package/dist/src/core/typecheck-shim.d.ts +70 -0
- package/dist/src/core/typecheck-shim.js +112 -0
- package/dist/src/core/typecheck.d.ts +65 -0
- package/dist/src/core/typecheck.js +302 -0
- package/dist/src/core/xpcshell-appdir.d.ts +2 -2
- package/dist/src/core/xpcshell-appdir.js +2 -2
- package/dist/src/types/commands/options.d.ts +1 -1
- package/dist/src/types/config.d.ts +61 -0
- package/dist/src/types/furnace.d.ts +1 -1
- package/dist/src/types/typecheck.d.ts +51 -0
- package/dist/src/types/typecheck.js +15 -0
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.19.0
|
|
4
|
+
|
|
5
|
+
### Features
|
|
6
|
+
|
|
7
|
+
- **`patchLint.checkJsStrict` and `patchLint.checkJsCompilerOptions`.** The patch-lint `checkJs` pass now defaults to the historical loose preset (`strict: false`, `noImplicitAny: false`). Set `"patchLint": { "checkJs": true, "checkJsStrict": true }` to enforce `strict` and `noImplicitAny` on patch-owned `.sys.mjs` so implicit-any parameters surface as `checkjs-type-error`, aligning with strict whole-project checkJs without changing module resolution (`noResolve` and `resource://` suppression unchanged). Optional `checkJsCompilerOptions` (requires `checkJsStrict`) merges allowlisted boolean compiler overrides — for example `{ "strictNullChecks": false }` — after the strict preset for gradual adoption. The Firefox globals shim and `SUPPRESSED_DIAGNOSTIC_CODES` remain shared with `fireforge typecheck` via `typecheck-shim.ts`.
|
|
8
|
+
|
|
3
9
|
## 0.18.0
|
|
4
10
|
|
|
5
11
|
### Compatibility
|
package/README.md
CHANGED
|
@@ -24,7 +24,7 @@ Inspired by [fern.js](https://github.com/ghostery/user-agent-desktop) and [Melon
|
|
|
24
24
|
|
|
25
25
|
- **Design token management** Track CSS custom property coverage across your modified files.
|
|
26
26
|
|
|
27
|
-
- **Quality checks** `fireforge lint` catches fork-specific issues (raw colours, missing licence headers, relative imports, large patches, cross-patch ordering problems) before you export. `fireforge verify` runs a read-only integrity check over the whole patch queue. `fireforge doctor` diagnoses project health including Furnace component validation.
|
|
27
|
+
- **Quality checks** `fireforge lint` catches fork-specific issues (raw colours, missing licence headers, relative imports, large patches, cross-patch ordering problems) before you export. `fireforge typecheck` runs CI-grade JS type checking against project-supplied jsconfig.json files (web components, chrome scripts) — separate from `lint`'s patch-hygiene checkJs pass. `fireforge verify` runs a read-only integrity check over the whole patch queue. `fireforge doctor` diagnoses project health including Furnace component validation.
|
|
28
28
|
|
|
29
29
|
- **Built and validated against real Firefox code** Developed by editing a real Firefox ESR codebase, learning from existing patch tools, observing the breakages and edge cases that surfaced and turning those findings into a realistic test suite. In-repo tests are thus grounded in actual development scenarios. Yes, we mock quite a bit, but when building a tool that modifies a separate code base, I think it's a solid compromise for the time being. Full end-to-end runs are currently run locally, as they require about 30 GB of disk and significant compute for multiple full builds. Full end-to-end via Github Actions will be added soonishlyTM.
|
|
30
30
|
|
|
@@ -57,7 +57,7 @@ Your project now has `fireforge.json`, an `engine/` directory with Firefox sourc
|
|
|
57
57
|
|
|
58
58
|
#### Known upstream build issues
|
|
59
59
|
|
|
60
|
-
- **macOS 15 (Darwin 25+) — `gecko-profiler` bindgen error `cannot find type _CharT in this scope`.** An Apple toolchain update changed `std::__CharT_pointer` to `_CharT_pointer` in the libc++ headers Firefox's bindgen walks, so `toolkit/library/rust/target-objects` fails during `mach build` even on a clean `fireforge bootstrap`. This is an upstream Firefox issue, not a FireForge bug. Two workarounds: pin Xcode's command line tools to a pre-September-2025 release via `xcode-select --install` / [Apple developer downloads](https://developer.apple.com/download/all/), or apply a one-line bindgen-basic-string-workaround patch (
|
|
60
|
+
- **macOS 15 (Darwin 25+) — `gecko-profiler` bindgen error `cannot find type _CharT in this scope`.** An Apple toolchain update changed `std::__CharT_pointer` to `_CharT_pointer` in the libc++ headers Firefox's bindgen walks, so `toolkit/library/rust/target-objects` fails during `mach build` even on a clean `fireforge bootstrap`. This is an upstream Firefox issue, not a FireForge bug. Two workarounds: pin Xcode's command line tools to a pre-September-2025 release via `xcode-select --install` / [Apple developer downloads](https://developer.apple.com/download/all/), or apply a one-line bindgen-basic-string-workaround patch (a downstream consumer may ship one in its patch queue). If you interrupt the resulting `fireforge build` and re-run `fireforge doctor`, the download/engine state is unaffected — the failure is isolated to the Rust compile phase.
|
|
61
61
|
|
|
62
62
|
### Workflow Overview
|
|
63
63
|
|
|
@@ -241,7 +241,7 @@ By default, a standalone `fireforge lint` (no arguments) lints the **aggregate**
|
|
|
241
241
|
| `missing-jsdoc` | Exports in patch-owned `.sys.mjs` | error |
|
|
242
242
|
| `jsdoc-param-mismatch` | Exports in patch-owned `.sys.mjs` | error |
|
|
243
243
|
| `jsdoc-missing-returns` | Exports in patch-owned `.sys.mjs` | error |
|
|
244
|
-
| `checkjs-type-error` | Patch-owned `.sys.mjs` (opt-in)
|
|
244
|
+
| `checkjs-type-error` | Patch-owned `.sys.mjs` (opt-in `patchLint.checkJs`) | error |
|
|
245
245
|
| `missing-jsdoc-class-method` | Class-method exports in patch-owned `.sys.mjs` (opt-in) | configurable |
|
|
246
246
|
| `jsdoc-class-method-param-mismatch` | Class-method exports in patch-owned `.sys.mjs` (opt-in) | configurable |
|
|
247
247
|
| `jsdoc-class-method-missing-returns` | Class-method exports in patch-owned `.sys.mjs` (opt-in) | configurable |
|
|
@@ -255,7 +255,20 @@ By default, a standalone `fireforge lint` (no arguments) lints the **aggregate**
|
|
|
255
255
|
|
|
256
256
|
**JSDoc validation** uses AST-based analysis (Acorn) to validate exported APIs in patch-owned `.sys.mjs` files. A file is "patch-owned" if it was newly created by the current diff or by an existing patch in the queue. Functions must document every `@param` (names must match) and include `@returns` when the function returns a value. Exported constants and classes require a JSDoc block.
|
|
257
257
|
|
|
258
|
-
**Optional `checkJs` pass.** Enable a TypeScript-esque bastardization of type checking for patch-owned `.sys.mjs` files by adding `"patchLint": { "checkJs": true }` to `fireforge.json`. This uses the TypeScript compiler API with `allowJs + checkJs + noEmit`, scoped only to patch-owned files. Firefox globals (`Services`, `ChromeUtils`, `lazy`, etc.) are shimmed automatically. Module-resolution errors from Firefox's `resource://` and `chrome://` URL schemes are suppressed since TypeScript cannot follow these.
|
|
258
|
+
**Optional `checkJs` pass.** Enable a TypeScript-esque bastardization of type checking for patch-owned `.sys.mjs` files by adding `"patchLint": { "checkJs": true }` to `fireforge.json`. This uses the TypeScript compiler API with `allowJs + checkJs + noEmit`, scoped only to patch-owned files. Firefox globals (`Services`, `ChromeUtils`, `lazy`, etc.) are shimmed automatically. Module-resolution errors from Firefox's `resource://` and `chrome://` URL schemes are suppressed since TypeScript cannot follow these. By default the pass uses a **loose** compiler preset (`strict: false`, `noImplicitAny: false`) so implicit `any` from untyped parameters does not flood the output. Set `"patchLint": { "checkJsStrict": true }` (requires `checkJs: true`) to enable `strict` and `noImplicitAny` for parity with strict whole-project checkJs — implicit-any parameters then surface as `checkjs-type-error`. Optional `"patchLint": { "checkJsCompilerOptions": { "strictNullChecks": false } }` (requires `checkJsStrict: true`) merges **allowlisted boolean** strict flags after that preset so forks can tighten `noImplicitAny` while relaxing e.g. null checks. The same built-in shim and the same eight suppressed diagnostic codes as `fireforge typecheck` apply; only explicit strictness differs. Projects that need to extend the built-in shim (e.g. for `MozLitElement`, `MozXULElement`, or fork-specific component bases) can point at an additional `.d.ts` via `"patchLint": { "checkJsExtraShim": "tools/types/<fork>-globals.d.ts" }`; the file is concatenated to the built-in shim — augment, don't redeclare.
|
|
259
|
+
|
|
260
|
+
**Whole-project type checking — `fireforge typecheck`.** `patchLint.checkJs` is patch-hygiene: scoped to patch-owned `.sys.mjs`, suppresses module-resolution noise, and runs every time `fireforge lint` runs. With `checkJsStrict`, that pass can match a strict jsconfig's `noImplicitAny` behaviour without replacing full-project resolution. `fireforge typecheck` remains the CI-grade complement: it runs whole projects you point at via `typecheck.projects` in `fireforge.json`, honours each jsconfig's strictness/include/exclude/`paths`, and is intended as a CI gate. The two are complementary; the recommended setup is `fireforge lint` on every patch export and `fireforge typecheck` on CI for the project-level baseline.
|
|
261
|
+
|
|
262
|
+
```jsonc
|
|
263
|
+
{
|
|
264
|
+
"typecheck": {
|
|
265
|
+
"projects": ["components/custom/jsconfig.json", "engine/browser/base/content/jsconfig.json"],
|
|
266
|
+
"extraShim": "tools/types/<fork>-globals.d.ts",
|
|
267
|
+
},
|
|
268
|
+
}
|
|
269
|
+
```
|
|
270
|
+
|
|
271
|
+
The command resolves each `projects` entry through TypeScript's own config parser (`readConfigFile` + `parseJsonConfigFileContent`), so `paths` mappings, `include`/`exclude` globs, and `lib` settings all behave the same as `tsc --noEmit -p <path>`. FireForge forces `noEmit: true` and defaults `allowJs`/`checkJs` to `true` only when the user has not set them — explicit `"checkJs": false` in a jsconfig is honoured (one notice, no diagnostics) so the IDE-noise opt-out remains an opt-out. The same `FIREFOX_GLOBALS_SHIM` and the same eight suppressed diagnostic codes (2304 / 2305 / 2306 / 2307 / 2552 / 2580 / 2792 / 7016 — module-resolution + global-name noise) apply, so a file that lints clean cannot fail typecheck for an inferable-only-from-source reason. Pass `--project <path>` for a one-off run against a single jsconfig (replaces the configured list, preserves `extraShim`). Exits non-zero on any error-severity diagnostic; warnings print but do not fail. TypeScript stays a dev-dependency — install it (`npm i -D typescript`) before running. The command does not honour `--since` or any patch-diff filter: it is whole-project by design.
|
|
259
272
|
|
|
260
273
|
**Optional `jsdocClassMethods` enforcement.** Set `"patchLint": { "jsdocClassMethods": "warning" | "error" }` in `fireforge.json` to extend JSDoc validation to class-method exports inside patch-owned `.sys.mjs` files. Every public method (instance and static), parameter-bearing constructor, getter, and setter must carry a leading JSDoc block; `@param` names must match the parameter list, and `@returns` is required when a method returns a value (getters and setters are exempt from `@returns`). Methods whose name starts with `_` or `#`, methods carrying `@private` or `@internal` in their JSDoc, and zero-parameter constructors are exempt. Defaults to `"off"`, so upgrading is a no-op until the knob is set.
|
|
261
274
|
|
|
@@ -667,10 +680,10 @@ The summary block reports two allowlist counters so operators can tell whether a
|
|
|
667
680
|
A feature with multiple components (e.g. an eight-component dock) typically wants one shared `.ftl` per feature rather than eight per-component stubs. `furnace create <tag> --localized --shared-ftl <chrome-uri>` participates in an existing feature-scoped bundle:
|
|
668
681
|
|
|
669
682
|
```bash
|
|
670
|
-
fireforge furnace create
|
|
683
|
+
fireforge furnace create mybrowser-dock-button --localized --shared-ftl browser/mybrowser-dock.ftl
|
|
671
684
|
```
|
|
672
685
|
|
|
673
|
-
The generated `.mjs` calls `insertFTLIfNeeded("browser/
|
|
686
|
+
The generated `.mjs` calls `insertFTLIfNeeded("browser/mybrowser-dock.ftl")` instead of the per-component path. No `<tag>.ftl` stub is written. The `furnace.json` `custom` entry carries a new `sharedFtl` field so apply, validate, and remove all honour the participation:
|
|
674
687
|
|
|
675
688
|
- `furnace apply` does not copy a per-component `.ftl` into the FTL tree nor add a locale `jar.mn` entry — the shared file is registered by whoever owns the feature bundle.
|
|
676
689
|
- `furnace remove` early-returns from the locale `jar.mn` cleanup, so dropping our component's reference does not orphan the bundle for every other participant.
|
|
@@ -685,9 +698,9 @@ The generated `.mjs` calls `insertFTLIfNeeded("browser/hominis-dock.ftl")` inste
|
|
|
685
698
|
When the wrapped inner element is hand-authored or is a non-stock `moz-*` widget that does not appear in `composes`, the explicit `keyboardCovered: true` field on the component's `furnace.json` entry forces the same skip:
|
|
686
699
|
|
|
687
700
|
```json
|
|
688
|
-
"
|
|
701
|
+
"mybrowser-dock-button": {
|
|
689
702
|
"description": "Dock button wrapper",
|
|
690
|
-
"targetPath": "components/custom/
|
|
703
|
+
"targetPath": "components/custom/mybrowser-dock-button",
|
|
691
704
|
"register": true,
|
|
692
705
|
"localized": false,
|
|
693
706
|
"composes": ["moz-button"],
|
|
@@ -337,7 +337,7 @@ const DOCTOR_CHECKS = [
|
|
|
337
337
|
for (const filename of repaired.recoveredFilenames) {
|
|
338
338
|
// 2026-04-24 eval Finding 6: the repair path used to tell the
|
|
339
339
|
// operator to hand-edit patches.json, which contradicts the
|
|
340
|
-
// README +
|
|
340
|
+
// README + downstream docs that treat the manifest as
|
|
341
341
|
// FireForge-owned. Point at the existing `re-export` /
|
|
342
342
|
// `export` workflow instead so the fix stays inside the tool:
|
|
343
343
|
// re-exporting the same files with an explicit `--description`
|
|
@@ -81,7 +81,7 @@ function registerFurnaceInfoCommands(furnace, context) {
|
|
|
81
81
|
return value;
|
|
82
82
|
})
|
|
83
83
|
.option('--compose <tags>', 'Record stock tags composed internally (metadata only, comma-separated)', (val) => val.split(',').map((s) => s.trim()))
|
|
84
|
-
.option('--shared-ftl <path>', 'Participate in an existing feature-scoped .ftl at this path (e.g. "browser/
|
|
84
|
+
.option('--shared-ftl <path>', 'Participate in an existing feature-scoped .ftl at this path (e.g. "browser/mybrowser-dock.ftl"); skips the per-component .ftl scaffold (implies --localized)')
|
|
85
85
|
.option('--dry-run', 'Show the planned file set and furnace.json changes without writing')
|
|
86
86
|
.option('--allow-prefix-mismatch', 'Create the component even when its name does not start with the configured `componentPrefix` in furnace.json. Without this flag the command refuses to write anything on a prefix mismatch.')
|
|
87
87
|
.action(withErrorHandling(async (name, options) => {
|
|
@@ -21,6 +21,7 @@ import { registerSetup } from './setup.js';
|
|
|
21
21
|
import { registerStatus } from './status.js';
|
|
22
22
|
import { registerTest } from './test.js';
|
|
23
23
|
import { registerToken } from './token.js';
|
|
24
|
+
import { registerTypecheck } from './typecheck.js';
|
|
24
25
|
import { registerVerify } from './verify.js';
|
|
25
26
|
import { registerWatch } from './watch.js';
|
|
26
27
|
import { registerWire } from './wire.js';
|
|
@@ -53,6 +54,7 @@ export const COMMAND_MANIFEST = [
|
|
|
53
54
|
{ name: 'wire', group: 'workflow', register: registerWire },
|
|
54
55
|
{ name: 'token', group: 'components', register: registerToken },
|
|
55
56
|
{ name: 'lint', group: 'diagnostics', register: registerLint },
|
|
57
|
+
{ name: 'typecheck', group: 'diagnostics', register: registerTypecheck },
|
|
56
58
|
{ name: 'verify', group: 'diagnostics', register: registerVerify },
|
|
57
59
|
{ name: 'furnace', group: 'components', register: registerFurnace },
|
|
58
60
|
];
|
|
@@ -57,7 +57,7 @@ export async function packageCommand(projectRoot, options) {
|
|
|
57
57
|
// only, so a targeted hint translator could not see the failure text.
|
|
58
58
|
// The captured stderr is fed through `explainMachError` below so
|
|
59
59
|
// recognised failure modes (notably the `packager.py` NoneType trip
|
|
60
|
-
// the evaluator hit on `
|
|
60
|
+
// the evaluator hit on `mybrowser/`) get an actionable hint prepended
|
|
61
61
|
// to the raw mach output the operator already saw.
|
|
62
62
|
result = await machPackageCapture(paths.engine);
|
|
63
63
|
}
|
|
@@ -41,7 +41,7 @@ function hasStaleBuildArtifactsSignal(output) {
|
|
|
41
41
|
// that are always a stale-artifact symptom. The earlier pattern also
|
|
42
42
|
// matched `resource:///modules/distribution.sys.mjs`, which surfaced on
|
|
43
43
|
// real packaging / module-resolution failures too (e.g. a fork's
|
|
44
|
-
// `
|
|
44
|
+
// `MyBrowserStore.sys.mjs` missing from the installed app dir after a
|
|
45
45
|
// successful build). That false-positive pushed operators toward
|
|
46
46
|
// "rebuild" advice for what was actually a module-registration issue.
|
|
47
47
|
return (/chrome:\/\/branding\/locale\/brand\.properties/i.test(output) ||
|
|
@@ -49,8 +49,8 @@ function hasStaleBuildArtifactsSignal(output) {
|
|
|
49
49
|
}
|
|
50
50
|
/**
|
|
51
51
|
* Fork-module-not-registered signal. 2026-04-21 eval Finding #14:
|
|
52
|
-
* a
|
|
53
|
-
*
|
|
52
|
+
* a fork's test failed with `Failed to load resource:///modules/mybrowser/
|
|
53
|
+
* MyBrowserStore.sys.mjs`. The branding pattern happened to also match
|
|
54
54
|
* because the test harness printed a branding warning during its
|
|
55
55
|
* teardown, and the stale-build branch won by precedence — telling the
|
|
56
56
|
* operator to rebuild when the real fix is to register the module in
|
|
@@ -126,8 +126,8 @@ function handleNonZeroTestExit(result, normalizedPaths, appdirInjectionAttempted
|
|
|
126
126
|
throw new GeneralError(buildUnknownTestMessage(normalizedPaths));
|
|
127
127
|
}
|
|
128
128
|
// Fork-owned module load failures must beat the branding stale-build
|
|
129
|
-
// branch: 2026-04-21 eval (Finding #14) saw a
|
|
130
|
-
// `Failed to load resource:///modules/
|
|
129
|
+
// branch: 2026-04-21 eval (Finding #14) saw a fork's test fail with
|
|
130
|
+
// `Failed to load resource:///modules/mybrowser/MyBrowserStore.sys.mjs`
|
|
131
131
|
// while the harness teardown printed a branding warning that the old
|
|
132
132
|
// stale-build pattern matched, so the operator was told to rebuild
|
|
133
133
|
// when the real fix is to register the missing module.
|
|
@@ -140,7 +140,7 @@ function handleNonZeroTestExit(result, normalizedPaths, appdirInjectionAttempted
|
|
|
140
140
|
// But the stale-build check is now narrower — it no longer matches
|
|
141
141
|
// `resource:///modules/distribution.sys.mjs` alone, which was producing
|
|
142
142
|
// false-positive rebuild advice on fork-custom module-load failures
|
|
143
|
-
// (the eval saw this for `
|
|
143
|
+
// (the eval saw this for `MyBrowserStore.sys.mjs`). Cases that once
|
|
144
144
|
// landed on `distribution.sys.mjs` fall through to xpcshell-appdir,
|
|
145
145
|
// which is the more useful diagnosis in practice for `Failed to load
|
|
146
146
|
// resource:///modules/…`.
|
|
@@ -253,13 +253,13 @@ export async function testCommand(projectRoot, testPaths, options = {}) {
|
|
|
253
253
|
: undefined;
|
|
254
254
|
const effectivePort = options.marionettePort ?? forwardedPort;
|
|
255
255
|
// Stale-browser probe: an interrupted earlier test run can leave a
|
|
256
|
-
// Firefox/ForgeFresh/
|
|
256
|
+
// Firefox/ForgeFresh/fork instance listening on the Marionette
|
|
257
257
|
// control port, which breaks the next mach test launch with a
|
|
258
258
|
// bind error that points nowhere near the real cause. Raise a
|
|
259
259
|
// targeted refusal up front instead of letting mach surface the
|
|
260
260
|
// generic bind failure. 2026-04-21 eval (Finding #20): a stale
|
|
261
261
|
// `-marionette` process from `fresh/` poisoned a later test run in
|
|
262
|
-
// the sibling `
|
|
262
|
+
// the sibling `mybrowser/` workspace.
|
|
263
263
|
await assertMarionettePortAvailable(effectivePort, { binaryName: projectConfig.binaryName });
|
|
264
264
|
// `--doctor` runs a short marionette handshake probe. When test paths are
|
|
265
265
|
// supplied the probe gates the mach test invocation (a FAIL bails out). When
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `fireforge typecheck` — whole-project TypeScript type checking
|
|
3
|
+
* driven by user-supplied jsconfig.json paths.
|
|
4
|
+
*
|
|
5
|
+
* Distinct from `patchLint.checkJs`: that pass is patch-hygiene
|
|
6
|
+
* (scoped to patch-owned `.sys.mjs`, run automatically by
|
|
7
|
+
* `fireforge lint`); this command is CI-grade — it runs whole
|
|
8
|
+
* projects with the user's own compiler options and is intended as
|
|
9
|
+
* a CI gate. The two share their Firefox-globals shim and the same
|
|
10
|
+
* suppressed-diagnostic set so a file that lints clean cannot fail
|
|
11
|
+
* typecheck for a reason the operator could not have inferred from
|
|
12
|
+
* the docs.
|
|
13
|
+
*
|
|
14
|
+
* Exits non-zero on any error-severity diagnostic. Warnings print
|
|
15
|
+
* but do not fail. Designed for CI use.
|
|
16
|
+
*/
|
|
17
|
+
import { Command } from 'commander';
|
|
18
|
+
import type { CommandContext } from '../types/cli.js';
|
|
19
|
+
import type { TypecheckConfig } from '../types/config.js';
|
|
20
|
+
import type { TypecheckProjectResult } from '../types/typecheck.js';
|
|
21
|
+
/** Command-line options Commander forwards from `fireforge typecheck`. */
|
|
22
|
+
export interface TypecheckCommandOptions {
|
|
23
|
+
/**
|
|
24
|
+
* Override `typecheck.projects` with a single jsconfig.json path
|
|
25
|
+
* for one-off verification. Replaces (does not augment) the config
|
|
26
|
+
* — useful to re-run a single project after fixing one of its
|
|
27
|
+
* issues without waiting for the full set.
|
|
28
|
+
*/
|
|
29
|
+
project?: string;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Resolves the project list to type-check. `--project` wins over
|
|
33
|
+
* config; if neither is set, throws a clear error pointing at both
|
|
34
|
+
* paths to a fix (add the config field or pass --project).
|
|
35
|
+
*/
|
|
36
|
+
export declare function resolveTypecheckProjects(configTypecheck: TypecheckConfig | undefined, override: string | undefined): TypecheckConfig;
|
|
37
|
+
/**
|
|
38
|
+
* Top-level entry point invoked by the registered Commander action.
|
|
39
|
+
* Loads config, resolves projects, runs typecheck, prints the result,
|
|
40
|
+
* and throws `GeneralError` to set a non-zero exit on errors.
|
|
41
|
+
*/
|
|
42
|
+
export declare function typecheckCommand(projectRoot: string, options: TypecheckCommandOptions): Promise<void>;
|
|
43
|
+
/**
|
|
44
|
+
* Prints all issues, computes the per-project + total counts, and
|
|
45
|
+
* throws on errors. Extracted so it can be exercised directly by
|
|
46
|
+
* the CLI test without spawning a child process.
|
|
47
|
+
*/
|
|
48
|
+
export declare function reportResults(projectRoot: string, results: ReadonlyArray<TypecheckProjectResult>): void;
|
|
49
|
+
/**
|
|
50
|
+
* Registers the `typecheck` command on the CLI program.
|
|
51
|
+
*/
|
|
52
|
+
export declare function registerTypecheck(program: Command, { getProjectRoot, withErrorHandling }: CommandContext): void;
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
// SPDX-License-Identifier: EUPL-1.2
|
|
2
|
+
/**
|
|
3
|
+
* `fireforge typecheck` — whole-project TypeScript type checking
|
|
4
|
+
* driven by user-supplied jsconfig.json paths.
|
|
5
|
+
*
|
|
6
|
+
* Distinct from `patchLint.checkJs`: that pass is patch-hygiene
|
|
7
|
+
* (scoped to patch-owned `.sys.mjs`, run automatically by
|
|
8
|
+
* `fireforge lint`); this command is CI-grade — it runs whole
|
|
9
|
+
* projects with the user's own compiler options and is intended as
|
|
10
|
+
* a CI gate. The two share their Firefox-globals shim and the same
|
|
11
|
+
* suppressed-diagnostic set so a file that lints clean cannot fail
|
|
12
|
+
* typecheck for a reason the operator could not have inferred from
|
|
13
|
+
* the docs.
|
|
14
|
+
*
|
|
15
|
+
* Exits non-zero on any error-severity diagnostic. Warnings print
|
|
16
|
+
* but do not fail. Designed for CI use.
|
|
17
|
+
*/
|
|
18
|
+
import { getProjectPaths, loadConfig } from '../core/config.js';
|
|
19
|
+
import { relativeForDisplay, runTypecheck } from '../core/typecheck.js';
|
|
20
|
+
import { GeneralError } from '../errors/base.js';
|
|
21
|
+
import { info, intro, outro, success, warn } from '../utils/logger.js';
|
|
22
|
+
/**
|
|
23
|
+
* Resolves the project list to type-check. `--project` wins over
|
|
24
|
+
* config; if neither is set, throws a clear error pointing at both
|
|
25
|
+
* paths to a fix (add the config field or pass --project).
|
|
26
|
+
*/
|
|
27
|
+
export function resolveTypecheckProjects(configTypecheck, override) {
|
|
28
|
+
if (override !== undefined) {
|
|
29
|
+
if (override.trim() === '') {
|
|
30
|
+
throw new GeneralError('--project requires a non-empty path');
|
|
31
|
+
}
|
|
32
|
+
return {
|
|
33
|
+
projects: [override],
|
|
34
|
+
...(configTypecheck?.extraShim !== undefined ? { extraShim: configTypecheck.extraShim } : {}),
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
if (!configTypecheck) {
|
|
38
|
+
throw new GeneralError('No typecheck configuration found. Add a "typecheck": { "projects": [...] } block to ' +
|
|
39
|
+
'fireforge.json, or pass --project <path> for a one-off run.');
|
|
40
|
+
}
|
|
41
|
+
return configTypecheck;
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Formats a single issue for CLI display. `[<project>] <file>:<line>:<col> TS<code> <message>`
|
|
45
|
+
* matches the format `tsc -p` produces with `--pretty false`, so output
|
|
46
|
+
* piped into editor jump-lists works without per-tool tweaks.
|
|
47
|
+
*/
|
|
48
|
+
function formatIssue(projectRoot, issue) {
|
|
49
|
+
const file = relativeForDisplay(projectRoot, issue.file);
|
|
50
|
+
const codeLabel = issue.code > 0 ? ` TS${String(issue.code)}` : '';
|
|
51
|
+
return `[${issue.project}] ${file}:${String(issue.line)}:${String(issue.column)}${codeLabel} ${issue.message}`;
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Top-level entry point invoked by the registered Commander action.
|
|
55
|
+
* Loads config, resolves projects, runs typecheck, prints the result,
|
|
56
|
+
* and throws `GeneralError` to set a non-zero exit on errors.
|
|
57
|
+
*/
|
|
58
|
+
export async function typecheckCommand(projectRoot, options) {
|
|
59
|
+
intro('FireForge typecheck');
|
|
60
|
+
// Validate project is initialised. `loadConfig` throws on missing
|
|
61
|
+
// fireforge.json — withErrorHandling at the CLI layer renders the
|
|
62
|
+
// resulting `ConfigNotFoundError` cleanly, so we don't need to
|
|
63
|
+
// re-wrap.
|
|
64
|
+
getProjectPaths(projectRoot);
|
|
65
|
+
const config = await loadConfig(projectRoot);
|
|
66
|
+
const cfg = resolveTypecheckProjects(config.typecheck, options.project);
|
|
67
|
+
info(`Running typecheck across ${String(cfg.projects.length)} project(s): ${cfg.projects.join(', ')}`);
|
|
68
|
+
const results = await runTypecheck(projectRoot, cfg);
|
|
69
|
+
reportResults(projectRoot, results);
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Prints all issues, computes the per-project + total counts, and
|
|
73
|
+
* throws on errors. Extracted so it can be exercised directly by
|
|
74
|
+
* the CLI test without spawning a child process.
|
|
75
|
+
*/
|
|
76
|
+
export function reportResults(projectRoot, results) {
|
|
77
|
+
let totalErrors = 0;
|
|
78
|
+
let totalWarnings = 0;
|
|
79
|
+
for (const result of results) {
|
|
80
|
+
const errors = result.issues.filter((i) => i.category === 'error');
|
|
81
|
+
const warnings = result.issues.filter((i) => i.category === 'warning');
|
|
82
|
+
totalErrors += errors.length;
|
|
83
|
+
totalWarnings += warnings.length;
|
|
84
|
+
for (const issue of warnings)
|
|
85
|
+
warn(formatIssue(projectRoot, issue));
|
|
86
|
+
for (const issue of errors)
|
|
87
|
+
warn(formatIssue(projectRoot, issue));
|
|
88
|
+
}
|
|
89
|
+
const summary = `Typecheck: ${String(totalErrors)} error(s), ${String(totalWarnings)} warning(s) across ${String(results.length)} project(s)`;
|
|
90
|
+
if (totalErrors === 0) {
|
|
91
|
+
success(summary);
|
|
92
|
+
outro(totalWarnings > 0 ? 'Typecheck passed with warnings' : 'Typecheck passed');
|
|
93
|
+
return;
|
|
94
|
+
}
|
|
95
|
+
info(summary);
|
|
96
|
+
outro('Typecheck failed');
|
|
97
|
+
throw new GeneralError(`Typecheck found ${String(totalErrors)} error(s) across ${String(results.length)} project(s).`);
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* Registers the `typecheck` command on the CLI program.
|
|
101
|
+
*/
|
|
102
|
+
export function registerTypecheck(program, { getProjectRoot, withErrorHandling }) {
|
|
103
|
+
program
|
|
104
|
+
.command('typecheck')
|
|
105
|
+
.description('Run TypeScript type checking against project-owned jsconfig.json files (CI-grade, whole-project)')
|
|
106
|
+
.option('--project <path>', 'Override typecheck.projects with a single jsconfig.json path (one-off run)')
|
|
107
|
+
.action(withErrorHandling(async (options) => {
|
|
108
|
+
const opts = {};
|
|
109
|
+
if (options.project !== undefined) {
|
|
110
|
+
opts.project = options.project;
|
|
111
|
+
}
|
|
112
|
+
await typecheckCommand(getProjectRoot(), opts);
|
|
113
|
+
}));
|
|
114
|
+
}
|
|
115
|
+
//# sourceMappingURL=typecheck.js.map
|
|
@@ -17,9 +17,9 @@ export declare const CONFIGS_DIR = "configs";
|
|
|
17
17
|
/** Name of the source directory */
|
|
18
18
|
export declare const SRC_DIR = "src";
|
|
19
19
|
/** Supported top-level fireforge.json keys backed by the current schema. */
|
|
20
|
-
export declare const SUPPORTED_CONFIG_ROOT_KEYS: readonly ["name", "vendor", "appId", "binaryName", "firefox", "build", "license", "wire", "patchLint", "markerComment"];
|
|
20
|
+
export declare const SUPPORTED_CONFIG_ROOT_KEYS: readonly ["name", "vendor", "appId", "binaryName", "firefox", "build", "license", "wire", "patchLint", "typecheck", "markerComment"];
|
|
21
21
|
/** Supported config paths that can be read or set without --force. */
|
|
22
|
-
export declare const SUPPORTED_CONFIG_PATHS: readonly ["name", "vendor", "appId", "binaryName", "license", "firefox", "firefox.version", "firefox.product", "build", "build.jobs", "wire", "wire.subscriptDir", "patchLint", "patchLint.checkJs", "patchLint.rawColorAllowlist", "patchLint.jsdocClassMethods", "patchLint.testAssertionFloor", "patchLint.chromeScriptJsDoc", "markerComment"];
|
|
22
|
+
export declare const SUPPORTED_CONFIG_PATHS: readonly ["name", "vendor", "appId", "binaryName", "license", "firefox", "firefox.version", "firefox.product", "build", "build.jobs", "wire", "wire.subscriptDir", "patchLint", "patchLint.checkJs", "patchLint.checkJsStrict", "patchLint.checkJsCompilerOptions", "patchLint.checkJsExtraShim", "patchLint.rawColorAllowlist", "patchLint.jsdocClassMethods", "patchLint.testAssertionFloor", "patchLint.chromeScriptJsDoc", "typecheck", "typecheck.projects", "typecheck.extraShim", "markerComment"];
|
|
23
23
|
/**
|
|
24
24
|
* Gets all project paths based on a root directory.
|
|
25
25
|
* @param root - Root directory of the project
|
|
@@ -28,6 +28,7 @@ export const SUPPORTED_CONFIG_ROOT_KEYS = [
|
|
|
28
28
|
'license',
|
|
29
29
|
'wire',
|
|
30
30
|
'patchLint',
|
|
31
|
+
'typecheck',
|
|
31
32
|
'markerComment',
|
|
32
33
|
];
|
|
33
34
|
/** Supported config paths that can be read or set without --force. */
|
|
@@ -46,10 +47,16 @@ export const SUPPORTED_CONFIG_PATHS = [
|
|
|
46
47
|
'wire.subscriptDir',
|
|
47
48
|
'patchLint',
|
|
48
49
|
'patchLint.checkJs',
|
|
50
|
+
'patchLint.checkJsStrict',
|
|
51
|
+
'patchLint.checkJsCompilerOptions',
|
|
52
|
+
'patchLint.checkJsExtraShim',
|
|
49
53
|
'patchLint.rawColorAllowlist',
|
|
50
54
|
'patchLint.jsdocClassMethods',
|
|
51
55
|
'patchLint.testAssertionFloor',
|
|
52
56
|
'patchLint.chromeScriptJsDoc',
|
|
57
|
+
'typecheck',
|
|
58
|
+
'typecheck.projects',
|
|
59
|
+
'typecheck.extraShim',
|
|
53
60
|
'markerComment',
|
|
54
61
|
];
|
|
55
62
|
/**
|
|
@@ -131,6 +131,11 @@ export function validateConfig(data) {
|
|
|
131
131
|
if (patchLintRec) {
|
|
132
132
|
config.patchLint = parsePatchLintBlock(patchLintRec);
|
|
133
133
|
}
|
|
134
|
+
// Typecheck (top-level, distinct from patchLint — see TypecheckConfig docs).
|
|
135
|
+
const typecheckRec = optionalConfigObject(rec, 'typecheck');
|
|
136
|
+
if (typecheckRec) {
|
|
137
|
+
config.typecheck = parseTypecheckBlock(typecheckRec);
|
|
138
|
+
}
|
|
134
139
|
// Warn on unknown root keys
|
|
135
140
|
const knownRootKeys = new Set(SUPPORTED_CONFIG_ROOT_KEYS);
|
|
136
141
|
for (const key of rec.keys()) {
|
|
@@ -196,6 +201,36 @@ function optionalConfigObject(rec, key) {
|
|
|
196
201
|
}
|
|
197
202
|
}
|
|
198
203
|
const SEVERITY_GATE_VALUES = ['off', 'warning', 'error'];
|
|
204
|
+
/** Allowlisted keys for `patchLint.checkJsCompilerOptions` (boolean overrides only). */
|
|
205
|
+
const PATCH_LINT_CHECKJS_COMPILER_OPTION_KEYS = [
|
|
206
|
+
'strictNullChecks',
|
|
207
|
+
'strictFunctionTypes',
|
|
208
|
+
'strictBindCallApply',
|
|
209
|
+
'noImplicitThis',
|
|
210
|
+
'useUnknownInCatchVariables',
|
|
211
|
+
'strictPropertyInitialization',
|
|
212
|
+
'noUnusedLocals',
|
|
213
|
+
'noUnusedParameters',
|
|
214
|
+
];
|
|
215
|
+
function parsePatchLintCheckJsCompilerOptions(raw) {
|
|
216
|
+
if (typeof raw !== 'object' || raw === null || Array.isArray(raw)) {
|
|
217
|
+
throw new ConfigError('Config field "patchLint.checkJsCompilerOptions" must be a plain object');
|
|
218
|
+
}
|
|
219
|
+
const rec = raw;
|
|
220
|
+
const allowed = new Set(PATCH_LINT_CHECKJS_COMPILER_OPTION_KEYS);
|
|
221
|
+
const out = {};
|
|
222
|
+
for (const key of Object.keys(rec)) {
|
|
223
|
+
if (!allowed.has(key)) {
|
|
224
|
+
throw new ConfigError(`Config field "patchLint.checkJsCompilerOptions" has unknown key "${key}"`);
|
|
225
|
+
}
|
|
226
|
+
const val = rec[key];
|
|
227
|
+
if (typeof val !== 'boolean') {
|
|
228
|
+
throw new ConfigError(`Config field "patchLint.checkJsCompilerOptions.${key}" must be a boolean`);
|
|
229
|
+
}
|
|
230
|
+
out[key] = val;
|
|
231
|
+
}
|
|
232
|
+
return out;
|
|
233
|
+
}
|
|
199
234
|
function parseSeverityGate(raw, label) {
|
|
200
235
|
if (raw === undefined)
|
|
201
236
|
return undefined;
|
|
@@ -213,6 +248,21 @@ function parsePatchLintBlock(rec) {
|
|
|
213
248
|
}
|
|
214
249
|
out.checkJs = checkJs;
|
|
215
250
|
}
|
|
251
|
+
const checkJsStrict = rec.raw('checkJsStrict');
|
|
252
|
+
if (checkJsStrict !== undefined) {
|
|
253
|
+
if (typeof checkJsStrict !== 'boolean') {
|
|
254
|
+
throw new ConfigError('Config field "patchLint.checkJsStrict" must be a boolean');
|
|
255
|
+
}
|
|
256
|
+
out.checkJsStrict = checkJsStrict;
|
|
257
|
+
}
|
|
258
|
+
const checkJsCompilerOptionsRaw = rec.raw('checkJsCompilerOptions');
|
|
259
|
+
if (checkJsCompilerOptionsRaw !== undefined) {
|
|
260
|
+
out.checkJsCompilerOptions = parsePatchLintCheckJsCompilerOptions(checkJsCompilerOptionsRaw);
|
|
261
|
+
}
|
|
262
|
+
const checkJsExtraShim = rec.raw('checkJsExtraShim');
|
|
263
|
+
if (checkJsExtraShim !== undefined) {
|
|
264
|
+
out.checkJsExtraShim = parseShimPath(checkJsExtraShim, 'patchLint.checkJsExtraShim');
|
|
265
|
+
}
|
|
216
266
|
const rawColorAllowlist = rec.raw('rawColorAllowlist');
|
|
217
267
|
if (rawColorAllowlist !== undefined) {
|
|
218
268
|
if (!Array.isArray(rawColorAllowlist) ||
|
|
@@ -233,6 +283,67 @@ function parsePatchLintBlock(rec) {
|
|
|
233
283
|
if (chromeScriptJsDoc !== undefined) {
|
|
234
284
|
out.chromeScriptJsDoc = chromeScriptJsDoc;
|
|
235
285
|
}
|
|
286
|
+
if (out.checkJsStrict === true && out.checkJs !== true) {
|
|
287
|
+
throw new ConfigError('Config field "patchLint.checkJsStrict" requires "patchLint.checkJs": true');
|
|
288
|
+
}
|
|
289
|
+
if (out.checkJsCompilerOptions !== undefined && out.checkJsStrict !== true) {
|
|
290
|
+
throw new ConfigError('Config field "patchLint.checkJsCompilerOptions" requires "patchLint.checkJsStrict": true');
|
|
291
|
+
}
|
|
292
|
+
return out;
|
|
293
|
+
}
|
|
294
|
+
/**
|
|
295
|
+
* Validates a path field that should point at a project-relative `.d.ts`
|
|
296
|
+
* file. Shared between `patchLint.checkJsExtraShim` and
|
|
297
|
+
* `typecheck.extraShim` so both fields reject the same absolute-path /
|
|
298
|
+
* traversal / empty-string inputs with consistent error messages. The
|
|
299
|
+
* file's existence is intentionally not checked here — that lives at
|
|
300
|
+
* the engine layer where a missing file produces a typed runtime error
|
|
301
|
+
* pointing at the actual command, rather than blocking config reads
|
|
302
|
+
* for unrelated commands.
|
|
303
|
+
*/
|
|
304
|
+
function parseShimPath(raw, label) {
|
|
305
|
+
if (typeof raw !== 'string' || raw.trim() === '') {
|
|
306
|
+
throw new ConfigError(`Config field "${label}" must be a non-empty string`);
|
|
307
|
+
}
|
|
308
|
+
if (!isContainedRelativePath(raw)) {
|
|
309
|
+
throw new ConfigError(`Config field "${label}" must be a project-relative path`);
|
|
310
|
+
}
|
|
311
|
+
return raw;
|
|
312
|
+
}
|
|
313
|
+
/**
|
|
314
|
+
* Validates the optional top-level `typecheck` block. Empty `projects`
|
|
315
|
+
* is rejected because a silent no-op for `fireforge typecheck` is a
|
|
316
|
+
* footgun — operators set the block expecting it to do something. Each
|
|
317
|
+
* project path must be a contained relative path so `--project` / CLI
|
|
318
|
+
* scripts can't escape the project root.
|
|
319
|
+
*/
|
|
320
|
+
function parseTypecheckBlock(rec) {
|
|
321
|
+
const projectsRaw = rec.raw('projects');
|
|
322
|
+
if (projectsRaw === undefined) {
|
|
323
|
+
throw new ConfigError('Config field "typecheck.projects" is required when "typecheck" is set');
|
|
324
|
+
}
|
|
325
|
+
if (!Array.isArray(projectsRaw)) {
|
|
326
|
+
throw new ConfigError('Config field "typecheck.projects" must be an array of strings');
|
|
327
|
+
}
|
|
328
|
+
if (projectsRaw.length === 0) {
|
|
329
|
+
throw new ConfigError('Config field "typecheck.projects" must not be empty');
|
|
330
|
+
}
|
|
331
|
+
const projects = [];
|
|
332
|
+
for (let i = 0; i < projectsRaw.length; i++) {
|
|
333
|
+
const entry = projectsRaw[i];
|
|
334
|
+
if (typeof entry !== 'string' || entry.trim() === '') {
|
|
335
|
+
throw new ConfigError(`Config field "typecheck.projects[${String(i)}]" must be a non-empty string`);
|
|
336
|
+
}
|
|
337
|
+
if (!isContainedRelativePath(entry)) {
|
|
338
|
+
throw new ConfigError(`Config field "typecheck.projects[${String(i)}]" must be a project-relative path`);
|
|
339
|
+
}
|
|
340
|
+
projects.push(entry);
|
|
341
|
+
}
|
|
342
|
+
const out = { projects };
|
|
343
|
+
const extraShim = rec.raw('extraShim');
|
|
344
|
+
if (extraShim !== undefined) {
|
|
345
|
+
out.extraShim = parseShimPath(extraShim, 'typecheck.extraShim');
|
|
346
|
+
}
|
|
236
347
|
return out;
|
|
237
348
|
}
|
|
238
349
|
//# sourceMappingURL=config-validate.js.map
|
|
@@ -50,8 +50,8 @@ export interface RunnableBundleCheck {
|
|
|
50
50
|
*
|
|
51
51
|
* Platform layout:
|
|
52
52
|
* - macOS: `<objDir>/dist/*.app/Contents/MacOS/<binaryName>` (the `.app`
|
|
53
|
-
* display casing can differ from `binaryName` — e.g. `
|
|
54
|
-
* binary `
|
|
53
|
+
* display casing can differ from `binaryName` — e.g. `MyBrowser.app` for
|
|
54
|
+
* binary `mybrowser`, so we enumerate the `*.app` bundles rather than
|
|
55
55
|
* compute the name.
|
|
56
56
|
* - Linux: `<objDir>/dist/bin/<binaryName>`.
|
|
57
57
|
* - Windows: `<objDir>/dist/bin/<binaryName>.exe`.
|
|
@@ -108,8 +108,8 @@ export async function hasBuildArtifacts(engineDir) {
|
|
|
108
108
|
*
|
|
109
109
|
* Platform layout:
|
|
110
110
|
* - macOS: `<objDir>/dist/*.app/Contents/MacOS/<binaryName>` (the `.app`
|
|
111
|
-
* display casing can differ from `binaryName` — e.g. `
|
|
112
|
-
* binary `
|
|
111
|
+
* display casing can differ from `binaryName` — e.g. `MyBrowser.app` for
|
|
112
|
+
* binary `mybrowser`, so we enumerate the `*.app` bundles rather than
|
|
113
113
|
* compute the name.
|
|
114
114
|
* - Linux: `<objDir>/dist/bin/<binaryName>`.
|
|
115
115
|
* - Windows: `<objDir>/dist/bin/<binaryName>.exe`.
|
|
@@ -40,19 +40,18 @@ export const MACH_ERROR_HINTS = [
|
|
|
40
40
|
// inside gecko-profiler's generated `bindings.rs`, but `_CharT` is
|
|
41
41
|
// not in scope where the alias lands — so the Rust compile fails
|
|
42
42
|
// with "cannot find type `_CharT`". The symptom is obscure and the
|
|
43
|
-
// fix is external:
|
|
44
|
-
// `990-infra-bindgen-basic-string-workaround.patch
|
|
45
|
-
//
|
|
46
|
-
//
|
|
47
|
-
//
|
|
48
|
-
// failure.
|
|
43
|
+
// fix is external: a downstream consumer's patch queue may ship
|
|
44
|
+
// `990-infra-bindgen-basic-string-workaround.patch`, which strips
|
|
45
|
+
// the offending alias line post-generation. This hint surfaces the
|
|
46
|
+
// workaround pointer alongside the raw bindgen output so operators
|
|
47
|
+
// don't have to reverse-engineer the failure.
|
|
49
48
|
pattern: /cannot find type `_CharT` in this scope[\s\S]*?gecko-profiler-|gecko-profiler-[\s\S]*?cannot find type `_CharT` in this scope/,
|
|
50
49
|
hint: 'The Rust compile failed on a bindgen-generated `basic_string___self_view` alias in ' +
|
|
51
50
|
'gecko-profiler/bindings.rs. This is an upstream bindgen output bug against some ' +
|
|
52
51
|
'macOS libc++ SDK versions and needs a post-generation patch to strip the alias. ' +
|
|
53
52
|
'The known-working workaround is the `990-infra-bindgen-basic-string-workaround.patch` ' +
|
|
54
|
-
"
|
|
55
|
-
'then re-run "fireforge import" + "fireforge build". If
|
|
53
|
+
"shipped by some downstream patch queues — import the equivalent into your fork's patches/, " +
|
|
54
|
+
'then re-run "fireforge import" + "fireforge build". If your fork does not carry such a patch, ' +
|
|
56
55
|
'apply the following post-process to the generated file before the Rust compile: ' +
|
|
57
56
|
'remove any `pub type basic_string___self_view = …<_CharT>;` line from ' +
|
|
58
57
|
'`<objdir>/release/build/gecko-profiler-*/out/gecko/bindings.rs`.',
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
* Marionette port probe.
|
|
4
4
|
*
|
|
5
5
|
* Gecko's Marionette control channel binds `127.0.0.1:2828` when a
|
|
6
|
-
* Firefox / ForgeFresh /
|
|
6
|
+
* Firefox / ForgeFresh / fork instance is launched with
|
|
7
7
|
* `-marionette`. The `fireforge test` harness spawns the browser with
|
|
8
8
|
* that flag, so any test run needs the port to be free at start.
|
|
9
9
|
*
|
|
@@ -42,7 +42,7 @@ const BROWSER_BASENAMES = new Set([
|
|
|
42
42
|
'firefox-bin',
|
|
43
43
|
'firefox-esr',
|
|
44
44
|
'forgefresh',
|
|
45
|
-
'
|
|
45
|
+
'mybrowser',
|
|
46
46
|
'thunderbird',
|
|
47
47
|
]);
|
|
48
48
|
/**
|
|
@@ -53,8 +53,8 @@ const BROWSER_BASENAMES = new Set([
|
|
|
53
53
|
* warning.
|
|
54
54
|
*
|
|
55
55
|
* Includes the operator-provided `binaryName` from `fireforge.json`
|
|
56
|
-
* so a fork that ships under a custom name (e.g.
|
|
57
|
-
* `
|
|
56
|
+
* so a fork that ships under a custom name (e.g.
|
|
57
|
+
* `mybrowser-nightly`) is still recognised as a browser.
|
|
58
58
|
*/
|
|
59
59
|
function isBrowserHolder(holder, binaryName) {
|
|
60
60
|
if (/\s-marionette(?:\s|$)/.test(holder.commandLine)) {
|