@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
|
@@ -8,14 +8,42 @@
|
|
|
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.
|
|
15
|
+
* `patchLint.checkJsStrict` only tightens `strict` / `noImplicitAny`
|
|
16
|
+
* and optional allowlisted `checkJsCompilerOptions`; it does not change
|
|
17
|
+
* shim composition or suppressed diagnostic codes.
|
|
12
18
|
*/
|
|
13
19
|
import type { PatchLintIssue } from '../types/commands/index.js';
|
|
20
|
+
import type { PatchLintCheckJsCompilerOptions, PatchLintConfig } from '../types/config.js';
|
|
14
21
|
/**
|
|
15
22
|
* Runs TypeScript's checkJs pass on patch-owned `.sys.mjs` files.
|
|
16
23
|
*
|
|
17
24
|
* @param repoDir - Absolute path to the engine (repository) directory
|
|
18
25
|
* @param patchOwnedFiles - Set of patch-owned `.sys.mjs` file paths (relative to repoDir)
|
|
26
|
+
* @param extraShimPath - Optional project-relative path to an additional
|
|
27
|
+
* `.d.ts` file whose contents are concatenated to the built-in
|
|
28
|
+
* Firefox-globals shim. Sourced from `patchLint.checkJsExtraShim`.
|
|
29
|
+
* Resolved against `projectRoot` (one level up from `repoDir` is the
|
|
30
|
+
* wrong root — patches sit inside `engine/` while the shim lives at
|
|
31
|
+
* the project root, so the caller passes both).
|
|
32
|
+
* @param projectRoot - Absolute project root for resolving `extraShimPath`.
|
|
33
|
+
* Defaults to `repoDir` for back-compat with callers that don't
|
|
34
|
+
* pass an extra shim (no resolution actually happens in that case).
|
|
35
|
+
* @param mode - When `strict` is true, enables `strict` and `noImplicitAny`
|
|
36
|
+
* (CI-style). Optional `compilerOptions` merges allowlisted boolean
|
|
37
|
+
* overrides after that preset (from `patchLint.checkJsCompilerOptions`).
|
|
38
|
+
* Omitted or `{ strict: false }` preserves the historical loose preset.
|
|
19
39
|
* @returns Array of lint issues from TS diagnostics
|
|
20
40
|
*/
|
|
21
|
-
export declare function runCheckJs(repoDir: string, patchOwnedFiles: Set<string
|
|
41
|
+
export declare function runCheckJs(repoDir: string, patchOwnedFiles: Set<string>, extraShimPath?: string, projectRoot?: string, mode?: {
|
|
42
|
+
strict: boolean;
|
|
43
|
+
compilerOptions?: PatchLintCheckJsCompilerOptions;
|
|
44
|
+
}): Promise<PatchLintIssue[]>;
|
|
45
|
+
/**
|
|
46
|
+
* Invokes {@link runCheckJs} for a `patchLint` block with `checkJs: true`.
|
|
47
|
+
* `projectRoot` is the FireForge project root (`dirname(engine)`).
|
|
48
|
+
*/
|
|
49
|
+
export declare function invokePatchLintCheckJs(repoDir: string, patchOwnedFiles: Set<string>, patchLint: PatchLintConfig, projectRoot: string): Promise<PatchLintIssue[]>;
|
|
@@ -9,80 +9,18 @@
|
|
|
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.
|
|
16
|
+
* `patchLint.checkJsStrict` only tightens `strict` / `noImplicitAny`
|
|
17
|
+
* and optional allowlisted `checkJsCompilerOptions`; it does not change
|
|
18
|
+
* shim composition or suppressed diagnostic codes.
|
|
13
19
|
*/
|
|
14
20
|
import { resolve } from 'node:path';
|
|
15
21
|
import { pathExists } from '../utils/fs.js';
|
|
16
22
|
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
|
-
]);
|
|
23
|
+
import { composeShimSource, SHIM_FILENAME, SUPPRESSED_DIAGNOSTIC_CODES } from './typecheck-shim.js';
|
|
86
24
|
// ---------------------------------------------------------------------------
|
|
87
25
|
// Public API
|
|
88
26
|
// ---------------------------------------------------------------------------
|
|
@@ -91,9 +29,22 @@ const SUPPRESSED_DIAGNOSTIC_CODES = new Set([
|
|
|
91
29
|
*
|
|
92
30
|
* @param repoDir - Absolute path to the engine (repository) directory
|
|
93
31
|
* @param patchOwnedFiles - Set of patch-owned `.sys.mjs` file paths (relative to repoDir)
|
|
32
|
+
* @param extraShimPath - Optional project-relative path to an additional
|
|
33
|
+
* `.d.ts` file whose contents are concatenated to the built-in
|
|
34
|
+
* Firefox-globals shim. Sourced from `patchLint.checkJsExtraShim`.
|
|
35
|
+
* Resolved against `projectRoot` (one level up from `repoDir` is the
|
|
36
|
+
* wrong root — patches sit inside `engine/` while the shim lives at
|
|
37
|
+
* the project root, so the caller passes both).
|
|
38
|
+
* @param projectRoot - Absolute project root for resolving `extraShimPath`.
|
|
39
|
+
* Defaults to `repoDir` for back-compat with callers that don't
|
|
40
|
+
* pass an extra shim (no resolution actually happens in that case).
|
|
41
|
+
* @param mode - When `strict` is true, enables `strict` and `noImplicitAny`
|
|
42
|
+
* (CI-style). Optional `compilerOptions` merges allowlisted boolean
|
|
43
|
+
* overrides after that preset (from `patchLint.checkJsCompilerOptions`).
|
|
44
|
+
* Omitted or `{ strict: false }` preserves the historical loose preset.
|
|
94
45
|
* @returns Array of lint issues from TS diagnostics
|
|
95
46
|
*/
|
|
96
|
-
export async function runCheckJs(repoDir, patchOwnedFiles) {
|
|
47
|
+
export async function runCheckJs(repoDir, patchOwnedFiles, extraShimPath, projectRoot, mode) {
|
|
97
48
|
if (patchOwnedFiles.size === 0)
|
|
98
49
|
return [];
|
|
99
50
|
// Dynamic import — typescript stays as a dev dependency
|
|
@@ -124,13 +75,53 @@ export async function runCheckJs(repoDir, patchOwnedFiles) {
|
|
|
124
75
|
}
|
|
125
76
|
if (rootFiles.length === 0)
|
|
126
77
|
return [];
|
|
78
|
+
// Compose the shim. `extraShimPath` is project-relative (validated
|
|
79
|
+
// by config-validate); resolve it against `projectRoot`. When the
|
|
80
|
+
// caller passes neither, fall back to `repoDir` — the only way the
|
|
81
|
+
// shim path is ever read in that case is when extraShimPath is
|
|
82
|
+
// also undefined, so the resolution target is irrelevant.
|
|
83
|
+
let shimSource;
|
|
84
|
+
try {
|
|
85
|
+
const composed = await composeShimSource(projectRoot ?? repoDir, extraShimPath);
|
|
86
|
+
shimSource = composed.source;
|
|
87
|
+
if (composed.extraShimAppended) {
|
|
88
|
+
verbose(`checkJs: extra shim ${extraShimPath ?? ''} appended to Firefox globals shim`);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
catch (err) {
|
|
92
|
+
return [
|
|
93
|
+
{
|
|
94
|
+
file: extraShimPath ?? '(checkJs)',
|
|
95
|
+
check: 'checkjs-type-error',
|
|
96
|
+
message: err instanceof Error ? err.message : String(err),
|
|
97
|
+
severity: 'error',
|
|
98
|
+
},
|
|
99
|
+
];
|
|
100
|
+
}
|
|
127
101
|
const shimPath = resolve(repoDir, SHIM_FILENAME);
|
|
128
102
|
rootFiles.push(shimPath);
|
|
103
|
+
const strict = mode?.strict === true;
|
|
104
|
+
const strictness = strict
|
|
105
|
+
? { strict: true, noImplicitAny: true }
|
|
106
|
+
: {
|
|
107
|
+
// Loose default — implicit `any` is allowed unless `patchLint.checkJsStrict`.
|
|
108
|
+
strict: false,
|
|
109
|
+
noImplicitAny: false,
|
|
110
|
+
};
|
|
111
|
+
const overrides = {};
|
|
112
|
+
const co = mode?.compilerOptions;
|
|
113
|
+
if (co) {
|
|
114
|
+
for (const key of Object.keys(co)) {
|
|
115
|
+
const v = co[key];
|
|
116
|
+
if (v !== undefined) {
|
|
117
|
+
overrides[key] = v;
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
}
|
|
129
121
|
const options = {
|
|
130
122
|
allowJs: true,
|
|
131
123
|
checkJs: true,
|
|
132
124
|
noEmit: true,
|
|
133
|
-
strict: false,
|
|
134
125
|
target: ts.ScriptTarget.ESNext,
|
|
135
126
|
module: ts.ModuleKind.ESNext,
|
|
136
127
|
moduleResolution: ts.ModuleResolutionKind.Bundler,
|
|
@@ -141,10 +132,8 @@ export async function runCheckJs(repoDir, patchOwnedFiles) {
|
|
|
141
132
|
// resource:// and chrome:// import, flooding the output with
|
|
142
133
|
// "Cannot find module" errors for upstream Firefox modules.
|
|
143
134
|
noResolve: true,
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
// errors defeats the purpose of a focused check.
|
|
147
|
-
noImplicitAny: false,
|
|
135
|
+
...strictness,
|
|
136
|
+
...overrides,
|
|
148
137
|
};
|
|
149
138
|
// Custom compiler host: reads patch-owned files from disk, returns
|
|
150
139
|
// the shim for the shim path, and returns empty content for
|
|
@@ -154,7 +143,7 @@ export async function runCheckJs(repoDir, patchOwnedFiles) {
|
|
|
154
143
|
...defaultHost,
|
|
155
144
|
getSourceFile(fileName, languageVersion, onError) {
|
|
156
145
|
if (fileName === shimPath) {
|
|
157
|
-
return ts.createSourceFile(fileName,
|
|
146
|
+
return ts.createSourceFile(fileName, shimSource, languageVersion, true);
|
|
158
147
|
}
|
|
159
148
|
if (ownedAbsolute.has(fileName)) {
|
|
160
149
|
return defaultHost.getSourceFile(fileName, languageVersion, onError);
|
|
@@ -177,7 +166,7 @@ export async function runCheckJs(repoDir, patchOwnedFiles) {
|
|
|
177
166
|
},
|
|
178
167
|
readFile(fileName) {
|
|
179
168
|
if (fileName === shimPath)
|
|
180
|
-
return
|
|
169
|
+
return shimSource;
|
|
181
170
|
return defaultHost.readFile(fileName);
|
|
182
171
|
},
|
|
183
172
|
};
|
|
@@ -222,4 +211,15 @@ export async function runCheckJs(repoDir, patchOwnedFiles) {
|
|
|
222
211
|
verbose(`checkJs: analyzed ${rootFiles.length - 1} file(s), found ${issues.length} issue(s)`);
|
|
223
212
|
return issues;
|
|
224
213
|
}
|
|
214
|
+
/**
|
|
215
|
+
* Invokes {@link runCheckJs} for a `patchLint` block with `checkJs: true`.
|
|
216
|
+
* `projectRoot` is the FireForge project root (`dirname(engine)`).
|
|
217
|
+
*/
|
|
218
|
+
export async function invokePatchLintCheckJs(repoDir, patchOwnedFiles, patchLint, projectRoot) {
|
|
219
|
+
const strict = patchLint.checkJsStrict === true;
|
|
220
|
+
const mode = strict && patchLint.checkJsCompilerOptions
|
|
221
|
+
? { strict, compilerOptions: patchLint.checkJsCompilerOptions }
|
|
222
|
+
: { strict };
|
|
223
|
+
return runCheckJs(repoDir, patchOwnedFiles, patchLint.checkJsExtraShim, projectRoot, mode);
|
|
224
|
+
}
|
|
225
225
|
//# sourceMappingURL=patch-lint-checkjs.js.map
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Public re-exports for {@link ./patch-lint.ts}. Split out so the
|
|
3
|
+
* orchestrator stays within the ESLint `max-lines` budget.
|
|
4
|
+
*/
|
|
5
|
+
export { runCheckJs } from './patch-lint-checkjs.js';
|
|
6
|
+
export { buildPatchQueueContext, collectNewFileCreatorsByPath, type ExtractedSpecifier, extractImportSpecifiers, extractImportSpecifiersWithLines, findForwardImportIgnoreLines, FORWARD_IMPORT_IGNORE_MARKER, isForwardImportableFile, lintPatchQueue, lintPatchQueueDuplicateCreations, lintPatchQueueForwardImports, type PatchQueueContext, type PatchQueueEntry, } from './patch-lint-cross.js';
|
|
7
|
+
export { buildModifiedFileAdditionsFromDiff, detectNewFilesInDiff } from './patch-lint-diff.js';
|
|
8
|
+
export { type JsDocCheck, type JsDocIssue, validateExportJsDoc } from './patch-lint-jsdoc.js';
|
|
9
|
+
export { resolvePatchOwnedChromeScripts, resolvePatchOwnedSysMjs } from './patch-lint-ownership.js';
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
// SPDX-License-Identifier: EUPL-1.2
|
|
2
|
+
/**
|
|
3
|
+
* Public re-exports for {@link ./patch-lint.ts}. Split out so the
|
|
4
|
+
* orchestrator stays within the ESLint `max-lines` budget.
|
|
5
|
+
*/
|
|
6
|
+
export { runCheckJs } from './patch-lint-checkjs.js';
|
|
7
|
+
export { buildPatchQueueContext, collectNewFileCreatorsByPath, extractImportSpecifiers, extractImportSpecifiersWithLines, findForwardImportIgnoreLines, FORWARD_IMPORT_IGNORE_MARKER, isForwardImportableFile, lintPatchQueue, lintPatchQueueDuplicateCreations, lintPatchQueueForwardImports, } from './patch-lint-cross.js';
|
|
8
|
+
export { buildModifiedFileAdditionsFromDiff, detectNewFilesInDiff } from './patch-lint-diff.js';
|
|
9
|
+
export { validateExportJsDoc } from './patch-lint-jsdoc.js';
|
|
10
|
+
export { resolvePatchOwnedChromeScripts, resolvePatchOwnedSysMjs } from './patch-lint-ownership.js';
|
|
11
|
+
//# sourceMappingURL=patch-lint-reexports.js.map
|
|
@@ -1,11 +1,7 @@
|
|
|
1
1
|
import type { PatchLintIssue } from '../types/commands/index.js';
|
|
2
2
|
import type { FireForgeConfig } from '../types/config.js';
|
|
3
3
|
import { type CommentStyle } from './license-headers.js';
|
|
4
|
-
export
|
|
5
|
-
export { buildPatchQueueContext, collectNewFileCreatorsByPath, type ExtractedSpecifier, extractImportSpecifiers, extractImportSpecifiersWithLines, findForwardImportIgnoreLines, FORWARD_IMPORT_IGNORE_MARKER, isForwardImportableFile, lintPatchQueue, lintPatchQueueDuplicateCreations, lintPatchQueueForwardImports, type PatchQueueContext, type PatchQueueEntry, } from './patch-lint-cross.js';
|
|
6
|
-
export { buildModifiedFileAdditionsFromDiff, detectNewFilesInDiff } from './patch-lint-diff.js';
|
|
7
|
-
export { type JsDocCheck, type JsDocIssue, validateExportJsDoc } from './patch-lint-jsdoc.js';
|
|
8
|
-
export { resolvePatchOwnedChromeScripts, resolvePatchOwnedSysMjs } from './patch-lint-ownership.js';
|
|
4
|
+
export * from './patch-lint-reexports.js';
|
|
9
5
|
/**
|
|
10
6
|
* Counts the total lines in a unified diff and the number of non-binary
|
|
11
7
|
* text lines, so binary hunks do not inflate patch size checks.
|
|
@@ -1,12 +1,12 @@
|
|
|
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';
|
|
6
6
|
import { hasRawCssColors, stripJsComments } from '../utils/regex.js';
|
|
7
7
|
import { loadFurnaceConfig } from './furnace-config.js';
|
|
8
8
|
import { containsUpstreamLicenseText, getLicenseHeader, hasAnyLicenseHeader, hasAnyLicenseHeaderAnyStyle, } from './license-headers.js';
|
|
9
|
-
import {
|
|
9
|
+
import { invokePatchLintCheckJs } from './patch-lint-checkjs.js';
|
|
10
10
|
import { lintChromeScriptJsDocForFile } from './patch-lint-chrome-jsdoc.js';
|
|
11
11
|
import { detectNewFilesInDiff, extractAddedLinesPerFile } from './patch-lint-diff.js';
|
|
12
12
|
import { AGGREGATE_PATCH_FILE } from './patch-lint-diff-tag.js';
|
|
@@ -20,12 +20,9 @@ import { resolvePatchOwnedChromeScripts, resolvePatchOwnedSysMjs } from './patch
|
|
|
20
20
|
// creation and forward-import rules, ignore marker) lives in
|
|
21
21
|
// `patch-lint-cross.ts` so the per-patch and cross-patch rule bodies can
|
|
22
22
|
// each stay within the project's per-file line budget. Re-export the
|
|
23
|
-
// public surface so callers continue to
|
|
24
|
-
|
|
25
|
-
export
|
|
26
|
-
export { buildModifiedFileAdditionsFromDiff, detectNewFilesInDiff } from './patch-lint-diff.js';
|
|
27
|
-
export { validateExportJsDoc } from './patch-lint-jsdoc.js';
|
|
28
|
-
export { resolvePatchOwnedChromeScripts, resolvePatchOwnedSysMjs } from './patch-lint-ownership.js';
|
|
23
|
+
// public surface from `patch-lint-reexports.ts` so callers continue to
|
|
24
|
+
// import from a single module.
|
|
25
|
+
export * from './patch-lint-reexports.js';
|
|
29
26
|
// ---------------------------------------------------------------------------
|
|
30
27
|
// Helpers
|
|
31
28
|
// ---------------------------------------------------------------------------
|
|
@@ -737,10 +734,8 @@ export async function lintExportedPatch(repoDir, affectedFiles, diffContent, con
|
|
|
737
734
|
...jsIssues,
|
|
738
735
|
...modCommentIssues,
|
|
739
736
|
];
|
|
740
|
-
// Optional checkJs pass — only when explicitly enabled in config
|
|
741
737
|
if (config.patchLint?.checkJs) {
|
|
742
|
-
|
|
743
|
-
issues.push(...checkJsIssues);
|
|
738
|
+
issues.push(...(await invokePatchLintCheckJs(repoDir, patchOwnedFiles, config.patchLint, dirname(repoDir))));
|
|
744
739
|
}
|
|
745
740
|
// Filter out ignored checks last so every rule still runs (keeps the
|
|
746
741
|
// 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
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Whole-project TypeScript type checking driven by user-supplied
|
|
3
|
+
* `jsconfig.json` paths. The engine behind the `fireforge typecheck`
|
|
4
|
+
* command.
|
|
5
|
+
*
|
|
6
|
+
* Distinct from `patch-lint-checkjs.ts` (patch-hygiene): patchLint is
|
|
7
|
+
* scoped, fireforge-controlled, and runs as part of `fireforge lint`;
|
|
8
|
+
* this command runs whole projects with user-controlled compiler
|
|
9
|
+
* options and is intended as a CI gate. The two share the Firefox
|
|
10
|
+
* globals shim and suppressed-code list (via `typecheck-shim.ts`) so
|
|
11
|
+
* a file that lints clean cannot fail typecheck for a reason the
|
|
12
|
+
* operator could not have inferred from the docs.
|
|
13
|
+
*
|
|
14
|
+
* Loads the TypeScript compiler API via dynamic import so it is only
|
|
15
|
+
* required when `typecheck.projects` is configured. TypeScript
|
|
16
|
+
* remains a dev-dependency — if a user invokes `fireforge typecheck`
|
|
17
|
+
* without installing it, the function returns a clear error pointing
|
|
18
|
+
* at `npm install typescript`.
|
|
19
|
+
*/
|
|
20
|
+
import type { TypecheckConfig } from '../types/config.js';
|
|
21
|
+
import type { TypecheckProjectResult } from '../types/typecheck.js';
|
|
22
|
+
/**
|
|
23
|
+
* Sentinel string surfaced as a `TypecheckIssue.message` when the
|
|
24
|
+
* project's jsconfig sets `checkJs: false`. Exported so tests can
|
|
25
|
+
* pin the contract — operators rely on it to spot opted-out projects
|
|
26
|
+
* in CI logs.
|
|
27
|
+
*/
|
|
28
|
+
export declare const CHECK_JS_DISABLED_NOTICE = "Project sets \"checkJs: false\" \u2014 skipping (override the jsconfig to enable typecheck).";
|
|
29
|
+
/**
|
|
30
|
+
* Runs `fireforge typecheck` against every project listed in `cfg.projects`.
|
|
31
|
+
*
|
|
32
|
+
* Per-project flow:
|
|
33
|
+
* 1. Read the jsconfig via the TS API. JSON parse / config-spec
|
|
34
|
+
* errors are surfaced as issues, not thrown — a single broken
|
|
35
|
+
* project must not abort the rest of the run.
|
|
36
|
+
* 2. Compute final compiler options from the user's options. We
|
|
37
|
+
* only force `noEmit: true` (this is a typecheck, not a build);
|
|
38
|
+
* we honour `strict`, `target`, `lib`, `module`, `paths`,
|
|
39
|
+
* `include`, `exclude`. `allowJs` and `checkJs` default to
|
|
40
|
+
* `true` only when the user has not set them — if the user set
|
|
41
|
+
* `checkJs: false` we treat that as an explicit opt-out and
|
|
42
|
+
* skip the project with a single notice (see {@link CHECK_JS_DISABLED_NOTICE}).
|
|
43
|
+
* 3. Inject the synthetic shim (built-in + optional extraShim) as
|
|
44
|
+
* a virtual root file that the custom CompilerHost serves.
|
|
45
|
+
* 4. Build the program; collect semantic, syntactic, options, and
|
|
46
|
+
* global diagnostics. The patchLint flow only reads semantic +
|
|
47
|
+
* syntactic — fine for hygiene, but a CI gate needs the full
|
|
48
|
+
* set so misconfigured `lib`/`paths` entries fail loudly.
|
|
49
|
+
* 5. Drop diagnostics whose code is in `SUPPRESSED_DIAGNOSTIC_CODES`
|
|
50
|
+
* and any diagnostic originating in the synthetic shim itself.
|
|
51
|
+
*
|
|
52
|
+
* @param projectRoot - Absolute project root. All paths in `cfg` are resolved against it.
|
|
53
|
+
* @param cfg - Validated `typecheck` block from `fireforge.json`.
|
|
54
|
+
* @returns One {@link TypecheckProjectResult} per entry in `cfg.projects`,
|
|
55
|
+
* in declared order. The CLI is responsible for printing the issues
|
|
56
|
+
* and computing the exit code.
|
|
57
|
+
*/
|
|
58
|
+
export declare function runTypecheck(projectRoot: string, cfg: TypecheckConfig): Promise<TypecheckProjectResult[]>;
|
|
59
|
+
/**
|
|
60
|
+
* Converts an absolute path to a path relative to the project root,
|
|
61
|
+
* for display in CLI output. Falls back to the absolute path when
|
|
62
|
+
* the path lies outside the root (e.g. a `node_modules` symlinked
|
|
63
|
+
* from elsewhere). Exposed for tests and the CLI.
|
|
64
|
+
*/
|
|
65
|
+
export declare function relativeForDisplay(projectRoot: string, absoluteFile: string): string;
|