@hominis/fireforge 0.18.9 → 0.18.11
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/README.md +20 -7
- package/dist/src/commands/doctor.js +1 -1
- package/dist/src/commands/furnace/index.js +1 -1
- package/dist/src/commands/lint.d.ts +36 -0
- package/dist/src/commands/lint.js +61 -1
- package/dist/src/commands/manifest.js +2 -0
- package/dist/src/commands/package.js +1 -1
- package/dist/src/commands/patch/index.d.ts +5 -3
- package/dist/src/commands/patch/index.js +8 -4
- package/dist/src/commands/patch/lint-ignore.d.ts +8 -0
- package/dist/src/commands/patch/lint-ignore.js +8 -4
- package/dist/src/commands/patch/rename.d.ts +36 -0
- package/dist/src/commands/patch/rename.js +244 -0
- 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 +5 -0
- package/dist/src/core/config-validate.js +64 -0
- package/dist/src/core/license-headers.d.ts +5 -0
- package/dist/src/core/license-headers.js +46 -5
- 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-export.d.ts +10 -0
- package/dist/src/core/patch-export.js +8 -2
- package/dist/src/core/patch-lint-checkjs.d.ts +14 -2
- package/dist/src/core/patch-lint-checkjs.js +40 -73
- package/dist/src/core/patch-lint-cross.js +6 -1
- package/dist/src/core/patch-lint.js +6 -4
- 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/index.d.ts +1 -1
- package/dist/src/types/commands/options.d.ts +29 -1
- package/dist/src/types/config.d.ts +33 -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
|
@@ -53,6 +53,39 @@ export function getLicenseHeader(license, style) {
|
|
|
53
53
|
return lines.map((l) => `# ${l}`).join('\n');
|
|
54
54
|
}
|
|
55
55
|
}
|
|
56
|
+
/**
|
|
57
|
+
* Single-line `/* ... *\/` block comments containing either an Emacs
|
|
58
|
+
* file-mode marker (`-*-`) or a vim modeline (`vim:`) — Mozilla's
|
|
59
|
+
* canonical first-line editor directives that legitimately precede the
|
|
60
|
+
* license header in many Firefox source files.
|
|
61
|
+
*
|
|
62
|
+
* Restricted to single-line blocks so a multi-line license header never
|
|
63
|
+
* gets accidentally consumed.
|
|
64
|
+
*/
|
|
65
|
+
const EDITOR_DIRECTIVE_BLOCK_COMMENT = /^[ \t]*\/\*[^\r\n]*?(?:-\*-|\bvim:)[^\r\n]*?\*\/[ \t]*\r?\n?/;
|
|
66
|
+
/**
|
|
67
|
+
* Strips any leading run of editor-directive block comments and blank
|
|
68
|
+
* lines, returning the remaining content.
|
|
69
|
+
*
|
|
70
|
+
* Mozilla's coding convention places editor directives like
|
|
71
|
+
* `/* -*- Mode: javascript; ... -*- *\/` and `/* vim: set ... *\/` on
|
|
72
|
+
* lines 1–2, with the canonical license header following on lines 3+.
|
|
73
|
+
* The raw `content.startsWith(...)` check used by {@link hasAnyLicenseHeader}
|
|
74
|
+
* never matches in that shape; this helper lets the caller test the
|
|
75
|
+
* post-directive prefix as a fallback.
|
|
76
|
+
*
|
|
77
|
+
* @param content - File content to strip
|
|
78
|
+
*/
|
|
79
|
+
function stripLeadingEditorDirectives(content) {
|
|
80
|
+
let result = content;
|
|
81
|
+
let prev;
|
|
82
|
+
do {
|
|
83
|
+
prev = result;
|
|
84
|
+
result = result.replace(/^[ \t]*\r?\n/, '');
|
|
85
|
+
result = result.replace(EDITOR_DIRECTIVE_BLOCK_COMMENT, '');
|
|
86
|
+
} while (result !== prev);
|
|
87
|
+
return result;
|
|
88
|
+
}
|
|
56
89
|
/**
|
|
57
90
|
* Returns true if `content` starts with any known license header for the
|
|
58
91
|
* given comment style.
|
|
@@ -65,16 +98,24 @@ export function getLicenseHeader(license, style) {
|
|
|
65
98
|
* standard MPL header — operators were forced to `--skip-lint` over a real
|
|
66
99
|
* false positive.
|
|
67
100
|
*
|
|
101
|
+
* Editor-directive block comments (`/* -*- ... -*- *\/`, `/* vim: ... *\/`)
|
|
102
|
+
* leading the file are tolerated — Mozilla's canonical layout puts those
|
|
103
|
+
* on lines 1–2 with the MPL header on lines 3+, which the raw
|
|
104
|
+
* `startsWith` check would otherwise miss.
|
|
105
|
+
*
|
|
68
106
|
* @param content - File content to check
|
|
69
107
|
* @param style - Comment syntax of the file
|
|
70
108
|
*/
|
|
71
109
|
export function hasAnyLicenseHeader(content, style) {
|
|
110
|
+
const candidates = [content, stripLeadingEditorDirectives(content)];
|
|
72
111
|
const licenses = Object.keys(HEADER_LINES);
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
112
|
+
for (const candidate of candidates) {
|
|
113
|
+
if (licenses.some((license) => candidate.startsWith(getLicenseHeader(license, style)))) {
|
|
114
|
+
return true;
|
|
115
|
+
}
|
|
116
|
+
if (style === 'js' && candidate.startsWith(getLicenseHeader('MPL-2.0', 'css'))) {
|
|
117
|
+
return true;
|
|
118
|
+
}
|
|
78
119
|
}
|
|
79
120
|
return false;
|
|
80
121
|
}
|
|
@@ -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)) {
|
|
@@ -5,6 +5,16 @@ import type { PatchCategory, PatchesManifest, PatchInfo, PatchMetadata } from '.
|
|
|
5
5
|
* @returns Next patch number (e.g., "005" for 4 existing patches)
|
|
6
6
|
*/
|
|
7
7
|
export declare function getNextPatchNumber(patchesDir: string): Promise<string>;
|
|
8
|
+
/**
|
|
9
|
+
* Sanitizes a human-readable name into a filename slug.
|
|
10
|
+
*
|
|
11
|
+
* Exported so `patch rename` can produce a filename slug from its
|
|
12
|
+
* `--to <new-name>` argument using the exact same convention `export`
|
|
13
|
+
* uses, without duplicating the lowercase + non-alnum collapse + length
|
|
14
|
+
* cap rules. Drift between the two would let an operator rename a patch
|
|
15
|
+
* to a slug `export` could never reach.
|
|
16
|
+
*/
|
|
17
|
+
export declare function sanitizeName(name: string): string;
|
|
8
18
|
/**
|
|
9
19
|
* Generates the next patch filename with category.
|
|
10
20
|
* @param patchesDir - Path to the patches directory
|
|
@@ -25,9 +25,15 @@ export async function getNextPatchNumber(patchesDir) {
|
|
|
25
25
|
return String(nextNumber).padStart(Math.max(3, String(nextNumber).length), '0');
|
|
26
26
|
}
|
|
27
27
|
/**
|
|
28
|
-
* Sanitizes a
|
|
28
|
+
* Sanitizes a human-readable name into a filename slug.
|
|
29
|
+
*
|
|
30
|
+
* Exported so `patch rename` can produce a filename slug from its
|
|
31
|
+
* `--to <new-name>` argument using the exact same convention `export`
|
|
32
|
+
* uses, without duplicating the lowercase + non-alnum collapse + length
|
|
33
|
+
* cap rules. Drift between the two would let an operator rename a patch
|
|
34
|
+
* to a slug `export` could never reach.
|
|
29
35
|
*/
|
|
30
|
-
function sanitizeName(name) {
|
|
36
|
+
export function sanitizeName(name) {
|
|
31
37
|
return name
|
|
32
38
|
.toLowerCase()
|
|
33
39
|
.replace(/[^a-z0-9]+/g, '-')
|
|
@@ -8,7 +8,10 @@
|
|
|
8
8
|
* how to fix it.
|
|
9
9
|
*
|
|
10
10
|
* Separated from `patch-lint.ts` to keep both files within the
|
|
11
|
-
* project's per-file line budget.
|
|
11
|
+
* project's per-file line budget. The shim itself and the suppressed
|
|
12
|
+
* diagnostic code list now live in `typecheck-shim.ts` and are shared
|
|
13
|
+
* with the whole-project `fireforge typecheck` command — keeping a
|
|
14
|
+
* single source of truth for the Firefox-globals coverage.
|
|
12
15
|
*/
|
|
13
16
|
import type { PatchLintIssue } from '../types/commands/index.js';
|
|
14
17
|
/**
|
|
@@ -16,6 +19,15 @@ import type { PatchLintIssue } from '../types/commands/index.js';
|
|
|
16
19
|
*
|
|
17
20
|
* @param repoDir - Absolute path to the engine (repository) directory
|
|
18
21
|
* @param patchOwnedFiles - Set of patch-owned `.sys.mjs` file paths (relative to repoDir)
|
|
22
|
+
* @param extraShimPath - Optional project-relative path to an additional
|
|
23
|
+
* `.d.ts` file whose contents are concatenated to the built-in
|
|
24
|
+
* Firefox-globals shim. Sourced from `patchLint.checkJsExtraShim`.
|
|
25
|
+
* Resolved against `projectRoot` (one level up from `repoDir` is the
|
|
26
|
+
* wrong root — patches sit inside `engine/` while the shim lives at
|
|
27
|
+
* the project root, so the caller passes both).
|
|
28
|
+
* @param projectRoot - Absolute project root for resolving `extraShimPath`.
|
|
29
|
+
* Defaults to `repoDir` for back-compat with callers that don't
|
|
30
|
+
* pass an extra shim (no resolution actually happens in that case).
|
|
19
31
|
* @returns Array of lint issues from TS diagnostics
|
|
20
32
|
*/
|
|
21
|
-
export declare function runCheckJs(repoDir: string, patchOwnedFiles: Set<string
|
|
33
|
+
export declare function runCheckJs(repoDir: string, patchOwnedFiles: Set<string>, extraShimPath?: string, projectRoot?: string): Promise<PatchLintIssue[]>;
|
|
@@ -9,80 +9,15 @@
|
|
|
9
9
|
* how to fix it.
|
|
10
10
|
*
|
|
11
11
|
* Separated from `patch-lint.ts` to keep both files within the
|
|
12
|
-
* project's per-file line budget.
|
|
12
|
+
* project's per-file line budget. The shim itself and the suppressed
|
|
13
|
+
* diagnostic code list now live in `typecheck-shim.ts` and are shared
|
|
14
|
+
* with the whole-project `fireforge typecheck` command — keeping a
|
|
15
|
+
* single source of truth for the Firefox-globals coverage.
|
|
13
16
|
*/
|
|
14
17
|
import { resolve } from 'node:path';
|
|
15
18
|
import { pathExists } from '../utils/fs.js';
|
|
16
19
|
import { verbose } from '../utils/logger.js';
|
|
17
|
-
|
|
18
|
-
// Firefox globals shim
|
|
19
|
-
// ---------------------------------------------------------------------------
|
|
20
|
-
const SHIM_FILENAME = '__fireforge_firefox_globals.d.ts';
|
|
21
|
-
/**
|
|
22
|
-
* Minimal `.d.ts` shim for Firefox privileged-scope globals.
|
|
23
|
-
*
|
|
24
|
-
* Firefox source is plain JS — no TypeScript allowed. The shim lets
|
|
25
|
-
* `checkJs` run without reporting "cannot find name" for the most
|
|
26
|
-
* common Mozilla APIs. Types are intentionally loose (`any`) because
|
|
27
|
-
* full Firefox type coverage is out of scope.
|
|
28
|
-
*
|
|
29
|
-
* Notable patterns that require shimming:
|
|
30
|
-
* - `const lazy = {};` + `ChromeUtils.defineESModuleGetters(lazy, { ... })`
|
|
31
|
-
* populates `lazy` at runtime; we declare it as `Record<string, any>`.
|
|
32
|
-
* - `Services.obs`, `Services.prefs`, etc. are XPCOM service accessors.
|
|
33
|
-
* - `Ci`, `Cc`, `Cr`, `Cu` are XPCOM component shortcuts.
|
|
34
|
-
* - Browser chrome globals like `gBrowser`, `gURLBar` are common in
|
|
35
|
-
* content scripts wired via `browser.js`.
|
|
36
|
-
*/
|
|
37
|
-
const FIREFOX_GLOBALS_SHIM = `
|
|
38
|
-
declare var Services: any;
|
|
39
|
-
declare var ChromeUtils: {
|
|
40
|
-
defineESModuleGetters(target: any, modules: Record<string, string>): void;
|
|
41
|
-
importESModule(specifier: string): any;
|
|
42
|
-
import(specifier: string): any;
|
|
43
|
-
defineModuleGetter(target: any, name: string, specifier: string): void;
|
|
44
|
-
generateQI(interfaces: any[]): Function;
|
|
45
|
-
isClassInfo(obj: any): boolean;
|
|
46
|
-
};
|
|
47
|
-
declare var Cu: any;
|
|
48
|
-
declare var Ci: any;
|
|
49
|
-
declare var Cc: any;
|
|
50
|
-
declare var Cr: any;
|
|
51
|
-
declare var Components: any;
|
|
52
|
-
declare var XPCOMUtils: any;
|
|
53
|
-
declare var lazy: Record<string, any>;
|
|
54
|
-
declare var PathUtils: any;
|
|
55
|
-
declare var IOUtils: any;
|
|
56
|
-
declare var FileUtils: any;
|
|
57
|
-
declare var gBrowser: any;
|
|
58
|
-
declare var gURLBar: any;
|
|
59
|
-
declare var gNavigatorBundle: any;
|
|
60
|
-
declare var AppConstants: any;
|
|
61
|
-
`;
|
|
62
|
-
// ---------------------------------------------------------------------------
|
|
63
|
-
// Diagnostic filtering
|
|
64
|
-
// ---------------------------------------------------------------------------
|
|
65
|
-
/**
|
|
66
|
-
* TS diagnostic codes to suppress because they are inherent to
|
|
67
|
-
* checking Firefox JS files outside of Mozilla's own build system.
|
|
68
|
-
*
|
|
69
|
-
* Firefox uses `resource://` and `chrome://` URL schemes for module
|
|
70
|
-
* imports. TypeScript's module resolver cannot follow these, so every
|
|
71
|
-
* import from an upstream Firefox module produces a spurious
|
|
72
|
-
* "Cannot find module" error. Filtering these out is essential to
|
|
73
|
-
* keep the checkJs pass usable — otherwise every file with an import
|
|
74
|
-
* would be buried in false positives.
|
|
75
|
-
*/
|
|
76
|
-
const SUPPRESSED_DIAGNOSTIC_CODES = new Set([
|
|
77
|
-
2307, // Cannot find module '{0}' or its corresponding type declarations.
|
|
78
|
-
2306, // File '{0}' is not a module.
|
|
79
|
-
2305, // Module '{0}' has no exported member '{1}'.
|
|
80
|
-
2792, // Cannot find module '{0}'. Did you mean to set the 'moduleResolution' option...
|
|
81
|
-
2304, // Cannot find name '{0}'. (for globals we missed in the shim)
|
|
82
|
-
2552, // Cannot find name '{0}'. Did you mean '{1}'?
|
|
83
|
-
2580, // Cannot find name '{0}'. Do you need to install type definitions...
|
|
84
|
-
7016, // Could not find a declaration file for module '{0}'.
|
|
85
|
-
]);
|
|
20
|
+
import { composeShimSource, SHIM_FILENAME, SUPPRESSED_DIAGNOSTIC_CODES } from './typecheck-shim.js';
|
|
86
21
|
// ---------------------------------------------------------------------------
|
|
87
22
|
// Public API
|
|
88
23
|
// ---------------------------------------------------------------------------
|
|
@@ -91,9 +26,18 @@ const SUPPRESSED_DIAGNOSTIC_CODES = new Set([
|
|
|
91
26
|
*
|
|
92
27
|
* @param repoDir - Absolute path to the engine (repository) directory
|
|
93
28
|
* @param patchOwnedFiles - Set of patch-owned `.sys.mjs` file paths (relative to repoDir)
|
|
29
|
+
* @param extraShimPath - Optional project-relative path to an additional
|
|
30
|
+
* `.d.ts` file whose contents are concatenated to the built-in
|
|
31
|
+
* Firefox-globals shim. Sourced from `patchLint.checkJsExtraShim`.
|
|
32
|
+
* Resolved against `projectRoot` (one level up from `repoDir` is the
|
|
33
|
+
* wrong root — patches sit inside `engine/` while the shim lives at
|
|
34
|
+
* the project root, so the caller passes both).
|
|
35
|
+
* @param projectRoot - Absolute project root for resolving `extraShimPath`.
|
|
36
|
+
* Defaults to `repoDir` for back-compat with callers that don't
|
|
37
|
+
* pass an extra shim (no resolution actually happens in that case).
|
|
94
38
|
* @returns Array of lint issues from TS diagnostics
|
|
95
39
|
*/
|
|
96
|
-
export async function runCheckJs(repoDir, patchOwnedFiles) {
|
|
40
|
+
export async function runCheckJs(repoDir, patchOwnedFiles, extraShimPath, projectRoot) {
|
|
97
41
|
if (patchOwnedFiles.size === 0)
|
|
98
42
|
return [];
|
|
99
43
|
// Dynamic import — typescript stays as a dev dependency
|
|
@@ -124,6 +68,29 @@ export async function runCheckJs(repoDir, patchOwnedFiles) {
|
|
|
124
68
|
}
|
|
125
69
|
if (rootFiles.length === 0)
|
|
126
70
|
return [];
|
|
71
|
+
// Compose the shim. `extraShimPath` is project-relative (validated
|
|
72
|
+
// by config-validate); resolve it against `projectRoot`. When the
|
|
73
|
+
// caller passes neither, fall back to `repoDir` — the only way the
|
|
74
|
+
// shim path is ever read in that case is when extraShimPath is
|
|
75
|
+
// also undefined, so the resolution target is irrelevant.
|
|
76
|
+
let shimSource;
|
|
77
|
+
try {
|
|
78
|
+
const composed = await composeShimSource(projectRoot ?? repoDir, extraShimPath);
|
|
79
|
+
shimSource = composed.source;
|
|
80
|
+
if (composed.extraShimAppended) {
|
|
81
|
+
verbose(`checkJs: extra shim ${extraShimPath ?? ''} appended to Firefox globals shim`);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
catch (err) {
|
|
85
|
+
return [
|
|
86
|
+
{
|
|
87
|
+
file: extraShimPath ?? '(checkJs)',
|
|
88
|
+
check: 'checkjs-type-error',
|
|
89
|
+
message: err instanceof Error ? err.message : String(err),
|
|
90
|
+
severity: 'error',
|
|
91
|
+
},
|
|
92
|
+
];
|
|
93
|
+
}
|
|
127
94
|
const shimPath = resolve(repoDir, SHIM_FILENAME);
|
|
128
95
|
rootFiles.push(shimPath);
|
|
129
96
|
const options = {
|
|
@@ -154,7 +121,7 @@ export async function runCheckJs(repoDir, patchOwnedFiles) {
|
|
|
154
121
|
...defaultHost,
|
|
155
122
|
getSourceFile(fileName, languageVersion, onError) {
|
|
156
123
|
if (fileName === shimPath) {
|
|
157
|
-
return ts.createSourceFile(fileName,
|
|
124
|
+
return ts.createSourceFile(fileName, shimSource, languageVersion, true);
|
|
158
125
|
}
|
|
159
126
|
if (ownedAbsolute.has(fileName)) {
|
|
160
127
|
return defaultHost.getSourceFile(fileName, languageVersion, onError);
|
|
@@ -177,7 +144,7 @@ export async function runCheckJs(repoDir, patchOwnedFiles) {
|
|
|
177
144
|
},
|
|
178
145
|
readFile(fileName) {
|
|
179
146
|
if (fileName === shimPath)
|
|
180
|
-
return
|
|
147
|
+
return shimSource;
|
|
181
148
|
return defaultHost.readFile(fileName);
|
|
182
149
|
},
|
|
183
150
|
};
|
|
@@ -396,6 +396,10 @@ export function lintPatchQueueForwardImports(ctx) {
|
|
|
396
396
|
.map((o) => `${o.filename}:${o.fullPath}`)
|
|
397
397
|
.sort((a, b) => a.localeCompare(b))
|
|
398
398
|
.join(',');
|
|
399
|
+
// Lowest ordinal that lands AFTER every later-ordered creator —
|
|
400
|
+
// turns the operator's "guess and re-run" loop into a single shot
|
|
401
|
+
// when the only fix is reordering.
|
|
402
|
+
const suggestedOrder = Math.max(...laterOwners.map((o) => o.order)) + 1;
|
|
399
403
|
issues.push({
|
|
400
404
|
file: sitePath,
|
|
401
405
|
check: 'forward-import',
|
|
@@ -404,7 +408,8 @@ export function lintPatchQueueForwardImports(ctx) {
|
|
|
404
408
|
`but the matching new file is created by a later patch: ${ownersSummary}. ` +
|
|
405
409
|
'Reorder the patches so the dependency is created first, move the import ' +
|
|
406
410
|
'into the later patch, or mark the import with ' +
|
|
407
|
-
`"// ${FORWARD_IMPORT_IGNORE_MARKER}" if the basename collision is a false positive
|
|
411
|
+
`"// ${FORWARD_IMPORT_IGNORE_MARKER}" if the basename collision is a false positive. ` +
|
|
412
|
+
`Closest legal ordinal that satisfies this dependency: ${suggestedOrder}.`,
|
|
408
413
|
severity: 'error',
|
|
409
414
|
});
|
|
410
415
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// SPDX-License-Identifier: EUPL-1.2
|
|
2
|
-
import { join } from 'node:path';
|
|
2
|
+
import { dirname, join } from 'node:path';
|
|
3
3
|
import { toError } from '../utils/errors.js';
|
|
4
4
|
import { pathExists, readText } from '../utils/fs.js';
|
|
5
5
|
import { verbose } from '../utils/logger.js';
|
|
@@ -737,10 +737,12 @@ export async function lintExportedPatch(repoDir, affectedFiles, diffContent, con
|
|
|
737
737
|
...jsIssues,
|
|
738
738
|
...modCommentIssues,
|
|
739
739
|
];
|
|
740
|
-
// Optional checkJs pass — only when explicitly enabled in config
|
|
740
|
+
// Optional checkJs pass — only when explicitly enabled in config.
|
|
741
|
+
// `checkJsExtraShim` is project-relative; resolve against the
|
|
742
|
+
// project root (dirname(engine) by getProjectPaths convention).
|
|
741
743
|
if (config.patchLint?.checkJs) {
|
|
742
|
-
const
|
|
743
|
-
issues.push(...
|
|
744
|
+
const extraShim = config.patchLint.checkJsExtraShim;
|
|
745
|
+
issues.push(...(await runCheckJs(repoDir, patchOwnedFiles, extraShim, dirname(repoDir))));
|
|
744
746
|
}
|
|
745
747
|
// Filter out ignored checks last so every rule still runs (keeps the
|
|
746
748
|
// implementation uniform) but suppressed rules do not surface. We do not
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared TypeScript-checking constants used by both the patch-lint
|
|
3
|
+
* `checkJs` pass (`patch-lint-checkjs.ts`) and the whole-project
|
|
4
|
+
* `fireforge typecheck` command (`typecheck.ts`).
|
|
5
|
+
*
|
|
6
|
+
* Centralised so both flows agree on the same Firefox-globals shim
|
|
7
|
+
* and the same set of suppressed diagnostic codes — drift between the
|
|
8
|
+
* two would mean a patch could lint clean under `fireforge lint` but
|
|
9
|
+
* still fail `fireforge typecheck`, or vice versa, for reasons the
|
|
10
|
+
* operator could not infer from the rule names.
|
|
11
|
+
*/
|
|
12
|
+
/** Filename used for the synthetic Firefox-globals shim source file. */
|
|
13
|
+
export declare const SHIM_FILENAME = "__fireforge_firefox_globals.d.ts";
|
|
14
|
+
/**
|
|
15
|
+
* Minimal `.d.ts` shim for Firefox privileged-scope globals.
|
|
16
|
+
*
|
|
17
|
+
* Firefox source is plain JS — no TypeScript allowed. The shim lets
|
|
18
|
+
* TS-driven type checking run without reporting "cannot find name"
|
|
19
|
+
* for the most common Mozilla APIs. Types are intentionally loose
|
|
20
|
+
* (`any`) because full Firefox type coverage is out of scope.
|
|
21
|
+
*
|
|
22
|
+
* Notable patterns that require shimming:
|
|
23
|
+
* - `const lazy = {};` + `ChromeUtils.defineESModuleGetters(lazy, { ... })`
|
|
24
|
+
* populates `lazy` at runtime; we declare it as `Record<string, any>`.
|
|
25
|
+
* - `Services.obs`, `Services.prefs`, etc. are XPCOM service accessors.
|
|
26
|
+
* - `Ci`, `Cc`, `Cr`, `Cu` are XPCOM component shortcuts.
|
|
27
|
+
* - Browser chrome globals like `gBrowser`, `gURLBar` are common in
|
|
28
|
+
* content scripts wired via `browser.js`.
|
|
29
|
+
*/
|
|
30
|
+
export declare const FIREFOX_GLOBALS_SHIM = "\ndeclare var Services: any;\ndeclare var ChromeUtils: {\n defineESModuleGetters(target: any, modules: Record<string, string>): void;\n importESModule(specifier: string): any;\n import(specifier: string): any;\n defineModuleGetter(target: any, name: string, specifier: string): void;\n generateQI(interfaces: any[]): Function;\n isClassInfo(obj: any): boolean;\n};\ndeclare var Cu: any;\ndeclare var Ci: any;\ndeclare var Cc: any;\ndeclare var Cr: any;\ndeclare var Components: any;\ndeclare var XPCOMUtils: any;\ndeclare var lazy: Record<string, any>;\ndeclare var PathUtils: any;\ndeclare var IOUtils: any;\ndeclare var FileUtils: any;\ndeclare var gBrowser: any;\ndeclare var gURLBar: any;\ndeclare var gNavigatorBundle: any;\ndeclare var AppConstants: any;\n";
|
|
31
|
+
/**
|
|
32
|
+
* TS diagnostic codes suppressed by both the patch-lint checkJs pass
|
|
33
|
+
* and the whole-project typecheck command. Each is a known false
|
|
34
|
+
* positive that arises from checking Firefox JS outside Mozilla's own
|
|
35
|
+
* build system: the resolver can't follow `resource://`/`chrome://`
|
|
36
|
+
* URLs and the global shim is intentionally narrow.
|
|
37
|
+
*
|
|
38
|
+
* Widening this set should be deliberate and per-code — silently
|
|
39
|
+
* suppressing more codes hides real type errors. The same set is used
|
|
40
|
+
* by both flows so a patch can't pass one and fail the other for a
|
|
41
|
+
* reason the operator couldn't infer from the docs.
|
|
42
|
+
*/
|
|
43
|
+
export declare const SUPPRESSED_DIAGNOSTIC_CODES: ReadonlySet<number>;
|
|
44
|
+
/**
|
|
45
|
+
* Result of {@link composeShimSource}: the source body to feed into
|
|
46
|
+
* the TS host plus a flag indicating whether the user-supplied extra
|
|
47
|
+
* shim was actually appended (used for verbose logging).
|
|
48
|
+
*/
|
|
49
|
+
export interface ComposedShim {
|
|
50
|
+
source: string;
|
|
51
|
+
extraShimAppended: boolean;
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Composes the synthetic shim source by concatenating the built-in
|
|
55
|
+
* Firefox globals shim with the contents of an optional user-supplied
|
|
56
|
+
* `.d.ts` file. The user file is appended verbatim — the augment
|
|
57
|
+
* direction is intentional (declarations later in concat order
|
|
58
|
+
* augment earlier ones), so a project that wants to refine `Services`
|
|
59
|
+
* with a more specific type can do so by declaring it in the extra
|
|
60
|
+
* shim.
|
|
61
|
+
*
|
|
62
|
+
* Missing extra-shim files raise a clear error rather than failing
|
|
63
|
+
* silently with a confusing "type not found" downstream — this is the
|
|
64
|
+
* one config-driven path where a user typo in `fireforge.json`
|
|
65
|
+
* produces a runtime error, so it needs to be unmistakable.
|
|
66
|
+
*
|
|
67
|
+
* @param projectRoot - Absolute project root, used to resolve the relative shim path
|
|
68
|
+
* @param extraShimPath - Optional project-relative path to an extra `.d.ts`
|
|
69
|
+
*/
|
|
70
|
+
export declare function composeShimSource(projectRoot: string, extraShimPath: string | undefined): Promise<ComposedShim>;
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
// SPDX-License-Identifier: EUPL-1.2
|
|
2
|
+
/**
|
|
3
|
+
* Shared TypeScript-checking constants used by both the patch-lint
|
|
4
|
+
* `checkJs` pass (`patch-lint-checkjs.ts`) and the whole-project
|
|
5
|
+
* `fireforge typecheck` command (`typecheck.ts`).
|
|
6
|
+
*
|
|
7
|
+
* Centralised so both flows agree on the same Firefox-globals shim
|
|
8
|
+
* and the same set of suppressed diagnostic codes — drift between the
|
|
9
|
+
* two would mean a patch could lint clean under `fireforge lint` but
|
|
10
|
+
* still fail `fireforge typecheck`, or vice versa, for reasons the
|
|
11
|
+
* operator could not infer from the rule names.
|
|
12
|
+
*/
|
|
13
|
+
import { resolve } from 'node:path';
|
|
14
|
+
import { pathExists, readText } from '../utils/fs.js';
|
|
15
|
+
/** Filename used for the synthetic Firefox-globals shim source file. */
|
|
16
|
+
export const SHIM_FILENAME = '__fireforge_firefox_globals.d.ts';
|
|
17
|
+
/**
|
|
18
|
+
* Minimal `.d.ts` shim for Firefox privileged-scope globals.
|
|
19
|
+
*
|
|
20
|
+
* Firefox source is plain JS — no TypeScript allowed. The shim lets
|
|
21
|
+
* TS-driven type checking run without reporting "cannot find name"
|
|
22
|
+
* for the most common Mozilla APIs. Types are intentionally loose
|
|
23
|
+
* (`any`) because full Firefox type coverage is out of scope.
|
|
24
|
+
*
|
|
25
|
+
* Notable patterns that require shimming:
|
|
26
|
+
* - `const lazy = {};` + `ChromeUtils.defineESModuleGetters(lazy, { ... })`
|
|
27
|
+
* populates `lazy` at runtime; we declare it as `Record<string, any>`.
|
|
28
|
+
* - `Services.obs`, `Services.prefs`, etc. are XPCOM service accessors.
|
|
29
|
+
* - `Ci`, `Cc`, `Cr`, `Cu` are XPCOM component shortcuts.
|
|
30
|
+
* - Browser chrome globals like `gBrowser`, `gURLBar` are common in
|
|
31
|
+
* content scripts wired via `browser.js`.
|
|
32
|
+
*/
|
|
33
|
+
export const FIREFOX_GLOBALS_SHIM = `
|
|
34
|
+
declare var Services: any;
|
|
35
|
+
declare var ChromeUtils: {
|
|
36
|
+
defineESModuleGetters(target: any, modules: Record<string, string>): void;
|
|
37
|
+
importESModule(specifier: string): any;
|
|
38
|
+
import(specifier: string): any;
|
|
39
|
+
defineModuleGetter(target: any, name: string, specifier: string): void;
|
|
40
|
+
generateQI(interfaces: any[]): Function;
|
|
41
|
+
isClassInfo(obj: any): boolean;
|
|
42
|
+
};
|
|
43
|
+
declare var Cu: any;
|
|
44
|
+
declare var Ci: any;
|
|
45
|
+
declare var Cc: any;
|
|
46
|
+
declare var Cr: any;
|
|
47
|
+
declare var Components: any;
|
|
48
|
+
declare var XPCOMUtils: any;
|
|
49
|
+
declare var lazy: Record<string, any>;
|
|
50
|
+
declare var PathUtils: any;
|
|
51
|
+
declare var IOUtils: any;
|
|
52
|
+
declare var FileUtils: any;
|
|
53
|
+
declare var gBrowser: any;
|
|
54
|
+
declare var gURLBar: any;
|
|
55
|
+
declare var gNavigatorBundle: any;
|
|
56
|
+
declare var AppConstants: any;
|
|
57
|
+
`;
|
|
58
|
+
/**
|
|
59
|
+
* TS diagnostic codes suppressed by both the patch-lint checkJs pass
|
|
60
|
+
* and the whole-project typecheck command. Each is a known false
|
|
61
|
+
* positive that arises from checking Firefox JS outside Mozilla's own
|
|
62
|
+
* build system: the resolver can't follow `resource://`/`chrome://`
|
|
63
|
+
* URLs and the global shim is intentionally narrow.
|
|
64
|
+
*
|
|
65
|
+
* Widening this set should be deliberate and per-code — silently
|
|
66
|
+
* suppressing more codes hides real type errors. The same set is used
|
|
67
|
+
* by both flows so a patch can't pass one and fail the other for a
|
|
68
|
+
* reason the operator couldn't infer from the docs.
|
|
69
|
+
*/
|
|
70
|
+
export const SUPPRESSED_DIAGNOSTIC_CODES = new Set([
|
|
71
|
+
2307, // Cannot find module '{0}' or its corresponding type declarations.
|
|
72
|
+
2306, // File '{0}' is not a module.
|
|
73
|
+
2305, // Module '{0}' has no exported member '{1}'.
|
|
74
|
+
2792, // Cannot find module '{0}'. Did you mean to set the 'moduleResolution' option...
|
|
75
|
+
2304, // Cannot find name '{0}'. (for globals we missed in the shim)
|
|
76
|
+
2552, // Cannot find name '{0}'. Did you mean '{1}'?
|
|
77
|
+
2580, // Cannot find name '{0}'. Do you need to install type definitions...
|
|
78
|
+
7016, // Could not find a declaration file for module '{0}'.
|
|
79
|
+
]);
|
|
80
|
+
/**
|
|
81
|
+
* Composes the synthetic shim source by concatenating the built-in
|
|
82
|
+
* Firefox globals shim with the contents of an optional user-supplied
|
|
83
|
+
* `.d.ts` file. The user file is appended verbatim — the augment
|
|
84
|
+
* direction is intentional (declarations later in concat order
|
|
85
|
+
* augment earlier ones), so a project that wants to refine `Services`
|
|
86
|
+
* with a more specific type can do so by declaring it in the extra
|
|
87
|
+
* shim.
|
|
88
|
+
*
|
|
89
|
+
* Missing extra-shim files raise a clear error rather than failing
|
|
90
|
+
* silently with a confusing "type not found" downstream — this is the
|
|
91
|
+
* one config-driven path where a user typo in `fireforge.json`
|
|
92
|
+
* produces a runtime error, so it needs to be unmistakable.
|
|
93
|
+
*
|
|
94
|
+
* @param projectRoot - Absolute project root, used to resolve the relative shim path
|
|
95
|
+
* @param extraShimPath - Optional project-relative path to an extra `.d.ts`
|
|
96
|
+
*/
|
|
97
|
+
export async function composeShimSource(projectRoot, extraShimPath) {
|
|
98
|
+
if (!extraShimPath) {
|
|
99
|
+
return { source: FIREFOX_GLOBALS_SHIM, extraShimAppended: false };
|
|
100
|
+
}
|
|
101
|
+
const absoluteShim = resolve(projectRoot, extraShimPath);
|
|
102
|
+
if (!(await pathExists(absoluteShim))) {
|
|
103
|
+
throw new Error(`Extra TypeScript shim not found: ${extraShimPath} (resolved to ${absoluteShim}). ` +
|
|
104
|
+
'Check the path in fireforge.json or create the file.');
|
|
105
|
+
}
|
|
106
|
+
const extra = await readText(absoluteShim);
|
|
107
|
+
return {
|
|
108
|
+
source: `${FIREFOX_GLOBALS_SHIM}\n// ── extraShim: ${extraShimPath} ──\n${extra}`,
|
|
109
|
+
extraShimAppended: true,
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
//# sourceMappingURL=typecheck-shim.js.map
|