@hominis/fireforge 0.31.0 → 0.32.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 +11 -0
- package/dist/src/commands/export-all.js +4 -1
- package/dist/src/commands/export-shared.js +10 -1
- package/dist/src/commands/export.js +5 -1
- package/dist/src/commands/lint-per-patch.d.ts +2 -0
- package/dist/src/commands/lint-per-patch.js +206 -44
- package/dist/src/commands/lint.js +100 -7
- package/dist/src/commands/re-export-files.js +4 -1
- package/dist/src/commands/re-export.js +8 -1
- package/dist/src/commands/test-run.d.ts +10 -0
- package/dist/src/commands/test-run.js +13 -4
- package/dist/src/commands/test.js +46 -7
- package/dist/src/core/config-validate.js +26 -0
- package/dist/src/core/furnace-jsconfig.js +22 -2
- package/dist/src/core/git-base.d.ts +15 -0
- package/dist/src/core/git-base.js +32 -0
- package/dist/src/core/git-diff.d.ts +8 -0
- package/dist/src/core/git-diff.js +224 -59
- package/dist/src/core/git-file-ops.d.ts +39 -0
- package/dist/src/core/git-file-ops.js +82 -1
- package/dist/src/core/mach.d.ts +17 -0
- package/dist/src/core/mach.js +21 -0
- package/dist/src/core/patch-lint-checkjs.d.ts +75 -21
- package/dist/src/core/patch-lint-checkjs.js +213 -67
- package/dist/src/core/patch-lint-css.d.ts +23 -0
- package/dist/src/core/patch-lint-css.js +172 -0
- package/dist/src/core/patch-lint.d.ts +34 -11
- package/dist/src/core/patch-lint.js +19 -163
- package/dist/src/core/test-xpcshell-retry.d.ts +9 -2
- package/dist/src/core/test-xpcshell-retry.js +9 -4
- package/dist/src/core/typecheck-shim.d.ts +3 -1
- package/dist/src/core/typecheck-shim.js +43 -3
- package/dist/src/types/commands/options.d.ts +17 -0
- package/dist/src/types/config.d.ts +11 -2
- package/package.json +1 -1
|
@@ -1,13 +1,11 @@
|
|
|
1
1
|
// SPDX-License-Identifier: EUPL-1.2
|
|
2
2
|
import { dirname, join } from 'node:path';
|
|
3
|
-
import { toError } from '../utils/errors.js';
|
|
4
3
|
import { pathExists, readText } from '../utils/fs.js';
|
|
5
|
-
import {
|
|
6
|
-
import { hasRawCssColors, stripJsComments } from '../utils/regex.js';
|
|
7
|
-
import { loadFurnaceConfig } from './furnace-config.js';
|
|
4
|
+
import { stripJsComments } from '../utils/regex.js';
|
|
8
5
|
import { containsUpstreamLicenseText, getLicenseHeader, hasAnyLicenseHeader, hasAnyLicenseHeaderAnyStyle, } from './license-headers.js';
|
|
9
6
|
import { invokePatchLintCheckJs } from './patch-lint-checkjs.js';
|
|
10
7
|
import { lintChromeScriptJsDocForFile } from './patch-lint-chrome-jsdoc.js';
|
|
8
|
+
import { lintPatchedCss } from './patch-lint-css.js';
|
|
11
9
|
import { detectNewFilesInDiff, extractAddedLinesPerFile } from './patch-lint-diff.js';
|
|
12
10
|
import { AGGREGATE_PATCH_FILE } from './patch-lint-diff-tag.js';
|
|
13
11
|
import { hasRelativeImport } from './patch-lint-imports.js';
|
|
@@ -25,6 +23,10 @@ import { resolvePatchOwnedChromeScripts, resolvePatchOwnedSysMjs } from './patch
|
|
|
25
23
|
// public surface from `patch-lint-reexports.ts` so callers continue to
|
|
26
24
|
// import from a single module.
|
|
27
25
|
export * from './patch-lint-reexports.js';
|
|
26
|
+
// The CSS rule bodies live in `patch-lint-css.ts` (same per-file-budget
|
|
27
|
+
// split as the other rule families); re-export the imported binding so
|
|
28
|
+
// callers and tests keep importing `lintPatchedCss` from this module.
|
|
29
|
+
export { lintPatchedCss };
|
|
28
30
|
// ---------------------------------------------------------------------------
|
|
29
31
|
// Helpers
|
|
30
32
|
// ---------------------------------------------------------------------------
|
|
@@ -205,158 +207,6 @@ export function commentStyleForFile(file) {
|
|
|
205
207
|
return 'js';
|
|
206
208
|
return null;
|
|
207
209
|
}
|
|
208
|
-
/**
|
|
209
|
-
* Loads the furnace token-prefix lint inputs gracefully — returns
|
|
210
|
-
* undefined (skipping the token-prefix check) when furnace.json cannot
|
|
211
|
-
* be loaded or no tokenPrefix is configured.
|
|
212
|
-
*/
|
|
213
|
-
async function loadCssTokenContext(repoDir) {
|
|
214
|
-
try {
|
|
215
|
-
const root = join(repoDir, '..');
|
|
216
|
-
const furnaceConfig = await loadFurnaceConfig(root);
|
|
217
|
-
if (furnaceConfig.tokenPrefix) {
|
|
218
|
-
return {
|
|
219
|
-
tokenPrefix: furnaceConfig.tokenPrefix,
|
|
220
|
-
tokenAllowlist: new Set(furnaceConfig.tokenAllowlist ?? []),
|
|
221
|
-
runtimeVariables: new Set(furnaceConfig.runtimeVariables ?? []),
|
|
222
|
-
};
|
|
223
|
-
}
|
|
224
|
-
}
|
|
225
|
-
catch (error) {
|
|
226
|
-
verbose(`Skipping furnace token-prefix lint hints because furnace.json could not be loaded: ${toError(error).message}`);
|
|
227
|
-
}
|
|
228
|
-
return undefined;
|
|
229
|
-
}
|
|
230
|
-
/**
|
|
231
|
-
* Raw-color check for one patched CSS file, scoped to introduced lines
|
|
232
|
-
* when diff context is available. Pushes onto `issues`.
|
|
233
|
-
*/
|
|
234
|
-
function checkRawColorValues(file, rawCss, addedLinesByFile, config, issues) {
|
|
235
|
-
// Check only introduced raw color values when diff context is available.
|
|
236
|
-
// Skip files on the raw-color allowlist (exact path or basename match) and
|
|
237
|
-
// auto-exempt files under `browser/branding/` — those are the fork's
|
|
238
|
-
// visual identity assets (app-about dialogs, installer pages, branded
|
|
239
|
-
// CSS copied from Firefox's `unofficial` template) and belong to the
|
|
240
|
-
// design-decision layer the design-token system does not govern.
|
|
241
|
-
// Without this auto-exemption, every first-time setup's copied CSS
|
|
242
|
-
// failed `raw-color-value` with no actionable fix other than manually
|
|
243
|
-
// listing each path in `rawColorAllowlist`.
|
|
244
|
-
const allowlist = config?.patchLint?.rawColorAllowlist;
|
|
245
|
-
const isAllowlisted = allowlist?.some((entry) => file === entry || file.endsWith('/' + entry));
|
|
246
|
-
const isBranding = file.startsWith('browser/branding/');
|
|
247
|
-
if (!isAllowlisted && !isBranding) {
|
|
248
|
-
// Strip lines with inline fireforge-ignore: raw-color-value suppression.
|
|
249
|
-
// Check against rawCss (before comment stripping) so the CSS comment marker is still present.
|
|
250
|
-
const sourceForSuppression = addedLinesByFile
|
|
251
|
-
? (addedLinesByFile.get(file) ?? []).join('\n')
|
|
252
|
-
: rawCss;
|
|
253
|
-
const suppressedContent = sourceForSuppression
|
|
254
|
-
.split('\n')
|
|
255
|
-
.filter((line) => !line.includes('fireforge-ignore: raw-color-value'))
|
|
256
|
-
.join('\n')
|
|
257
|
-
.replace(/\/\*[\s\S]*?\*\//g, '');
|
|
258
|
-
if (hasRawCssColors(suppressedContent)) {
|
|
259
|
-
issues.push({
|
|
260
|
-
file,
|
|
261
|
-
check: 'raw-color-value',
|
|
262
|
-
message: 'Raw color value found. Use CSS custom properties (var(--...)) for design token consistency.',
|
|
263
|
-
severity: 'error',
|
|
264
|
-
});
|
|
265
|
-
}
|
|
266
|
-
}
|
|
267
|
-
}
|
|
268
|
-
/**
|
|
269
|
-
* Token-prefix check for one patched CSS file: flags `var(--x)` references
|
|
270
|
-
* that match neither the configured prefix, the allowlist, the runtime
|
|
271
|
-
* variables, nor a same-file declaration. Pushes onto `issues`.
|
|
272
|
-
*/
|
|
273
|
-
function checkTokenPrefixViolations(file, cssContent, addedLinesByFile, tokenContext, issues) {
|
|
274
|
-
// Check for non-tokenized custom properties. A variable that is both
|
|
275
|
-
// declared and consumed inside the same file is auto-exempted as a
|
|
276
|
-
// runtime state channel (see furnace.json → runtimeVariables).
|
|
277
|
-
//
|
|
278
|
-
// When diff context is available, scope the `var(...)` scan to
|
|
279
|
-
// added/modified lines only. `cssContent` (full-file) is still the
|
|
280
|
-
// source of `localDeclarations` so vars declared anywhere in the file
|
|
281
|
-
// are recognised as same-file refs regardless of where the consuming
|
|
282
|
-
// `var(...)` appears. Before this scoping change, a small edit to a
|
|
283
|
-
// Furnace override of a stock component (e.g. moz-card) produced a
|
|
284
|
-
// `token-prefix-violation` for every stock `var(--moz-card-*)` the
|
|
285
|
-
// upstream file already carried, because the scanner saw the full
|
|
286
|
-
// applied file and flagged each inherited reference as if the fork
|
|
287
|
-
// had introduced it.
|
|
288
|
-
if (tokenContext) {
|
|
289
|
-
const declarationPattern = /(?:^|[{;,\s])(--[\w-]+)\s*:/g;
|
|
290
|
-
const localDeclarations = new Set();
|
|
291
|
-
let declMatch;
|
|
292
|
-
while ((declMatch = declarationPattern.exec(cssContent)) !== null) {
|
|
293
|
-
const name = declMatch[1];
|
|
294
|
-
if (name)
|
|
295
|
-
localDeclarations.add(name);
|
|
296
|
-
}
|
|
297
|
-
const prefixScanSource = addedLinesByFile
|
|
298
|
-
? (addedLinesByFile.get(file) ?? []).join('\n').replace(/\/\*[\s\S]*?\*\//g, '')
|
|
299
|
-
: cssContent;
|
|
300
|
-
if (prefixScanSource.length > 0) {
|
|
301
|
-
const varPattern = /var\(\s*(--[\w-]+)/g;
|
|
302
|
-
const flaggedProps = new Set();
|
|
303
|
-
let match;
|
|
304
|
-
while ((match = varPattern.exec(prefixScanSource)) !== null) {
|
|
305
|
-
const prop = match[1];
|
|
306
|
-
if (!prop)
|
|
307
|
-
continue;
|
|
308
|
-
if (prop.startsWith(tokenContext.tokenPrefix))
|
|
309
|
-
continue;
|
|
310
|
-
if (tokenContext.tokenAllowlist.has(prop))
|
|
311
|
-
continue;
|
|
312
|
-
if (tokenContext.runtimeVariables.has(prop))
|
|
313
|
-
continue;
|
|
314
|
-
if (localDeclarations.has(prop))
|
|
315
|
-
continue;
|
|
316
|
-
// De-duplicate per (file, prop) pair so the same introduced var
|
|
317
|
-
// used five times in the added hunk doesn't produce five
|
|
318
|
-
// identical issue entries.
|
|
319
|
-
if (flaggedProps.has(prop))
|
|
320
|
-
continue;
|
|
321
|
-
flaggedProps.add(prop);
|
|
322
|
-
issues.push({
|
|
323
|
-
file,
|
|
324
|
-
check: 'token-prefix-violation',
|
|
325
|
-
message: `CSS references var(${prop}) which does not match the required token prefix "${tokenContext.tokenPrefix}". Use a design token, add to tokenAllowlist, or (for runtime state channels) list the variable in runtimeVariables.`,
|
|
326
|
-
severity: 'error',
|
|
327
|
-
});
|
|
328
|
-
}
|
|
329
|
-
}
|
|
330
|
-
}
|
|
331
|
-
}
|
|
332
|
-
/**
|
|
333
|
-
* Lints patched CSS files for introduced raw color values and non-tokenized
|
|
334
|
-
* custom properties.
|
|
335
|
-
*
|
|
336
|
-
* @param repoDir - Absolute path to the engine (repository) directory
|
|
337
|
-
* @param affectedFiles - File paths (relative to repoDir) affected by the patch
|
|
338
|
-
* @param diffContent - Optional unified diff used to scope raw color checks to introduced lines
|
|
339
|
-
* @returns Array of lint issues found
|
|
340
|
-
*/
|
|
341
|
-
export async function lintPatchedCss(repoDir, affectedFiles, diffContent, config) {
|
|
342
|
-
const cssFiles = affectedFiles.filter((f) => f.endsWith('.css'));
|
|
343
|
-
if (cssFiles.length === 0)
|
|
344
|
-
return [];
|
|
345
|
-
const tokenContext = await loadCssTokenContext(repoDir);
|
|
346
|
-
const issues = [];
|
|
347
|
-
const addedLinesByFile = diffContent ? extractAddedLinesPerFile(diffContent) : undefined;
|
|
348
|
-
for (const file of cssFiles) {
|
|
349
|
-
const filePath = join(repoDir, file);
|
|
350
|
-
if (!(await pathExists(filePath)))
|
|
351
|
-
continue;
|
|
352
|
-
const rawCss = await readText(filePath);
|
|
353
|
-
// Strip block comments before scanning
|
|
354
|
-
const cssContent = rawCss.replace(/\/\*[\s\S]*?\*\//g, '');
|
|
355
|
-
checkRawColorValues(file, rawCss, addedLinesByFile, config, issues);
|
|
356
|
-
checkTokenPrefixViolations(file, cssContent, addedLinesByFile, tokenContext, issues);
|
|
357
|
-
}
|
|
358
|
-
return issues;
|
|
359
|
-
}
|
|
360
210
|
// ---------------------------------------------------------------------------
|
|
361
211
|
// License header lint
|
|
362
212
|
// ---------------------------------------------------------------------------
|
|
@@ -695,9 +545,6 @@ export async function lintModifiedFileHeaders(repoDir, affectedFiles, newFiles)
|
|
|
695
545
|
}
|
|
696
546
|
return issues;
|
|
697
547
|
}
|
|
698
|
-
// ---------------------------------------------------------------------------
|
|
699
|
-
// Orchestrator
|
|
700
|
-
// ---------------------------------------------------------------------------
|
|
701
548
|
/**
|
|
702
549
|
* Runs all patch lint checks and returns combined issues.
|
|
703
550
|
*
|
|
@@ -717,9 +564,11 @@ export async function lintModifiedFileHeaders(repoDir, affectedFiles, newFiles)
|
|
|
717
564
|
* per-patch manifest context (re-export, per-patch lint) should
|
|
718
565
|
* pass this; aggregate-mode callers without a specific patch
|
|
719
566
|
* context skip it and fall through to auto-detection.
|
|
567
|
+
* @param options - Optional behaviour switches; see
|
|
568
|
+
* {@link LintExportedPatchOptions}.
|
|
720
569
|
* @returns Array of all lint issues found
|
|
721
570
|
*/
|
|
722
|
-
export async function lintExportedPatch(repoDir, affectedFiles, diffContent, config, patchQueueCtx, ignoreChecks, patchTier) {
|
|
571
|
+
export async function lintExportedPatch(repoDir, affectedFiles, diffContent, config, patchQueueCtx, ignoreChecks, patchTier, options) {
|
|
723
572
|
const newFiles = detectNewFilesInDiff(diffContent);
|
|
724
573
|
const { textLines: lineCount } = countNonBinaryDiffLines(diffContent);
|
|
725
574
|
const patchOwnedFiles = resolvePatchOwnedSysMjs(newFiles, patchQueueCtx);
|
|
@@ -731,7 +580,9 @@ export async function lintExportedPatch(repoDir, affectedFiles, diffContent, con
|
|
|
731
580
|
lintModifiedFileHeaders(repoDir, affectedFiles, newFiles),
|
|
732
581
|
]);
|
|
733
582
|
const modCommentIssues = lintModificationComments(diffContent, config);
|
|
734
|
-
const sizeIssues =
|
|
583
|
+
const sizeIssues = options?.skipPatchSize
|
|
584
|
+
? []
|
|
585
|
+
: lintPatchSize(affectedFiles, lineCount, patchTier);
|
|
735
586
|
const issues = [
|
|
736
587
|
...sizeIssues,
|
|
737
588
|
...cssIssues,
|
|
@@ -740,8 +591,13 @@ export async function lintExportedPatch(repoDir, affectedFiles, diffContent, con
|
|
|
740
591
|
...jsIssues,
|
|
741
592
|
...modCommentIssues,
|
|
742
593
|
];
|
|
743
|
-
if (
|
|
744
|
-
|
|
594
|
+
if (options?.precomputedCheckJs) {
|
|
595
|
+
// Per-patch lint built one queue-wide program and already attributed
|
|
596
|
+
// this patch's findings — append them instead of rebuilding the program.
|
|
597
|
+
issues.push(...options.precomputedCheckJs);
|
|
598
|
+
}
|
|
599
|
+
else if (config.patchLint?.checkJs) {
|
|
600
|
+
issues.push(...(await invokePatchLintCheckJs(repoDir, patchOwnedFiles, config.patchLint, dirname(repoDir), options?.checkJsReportScope)));
|
|
745
601
|
}
|
|
746
602
|
// Filter out ignored checks last so every rule still runs (keeps the
|
|
747
603
|
// implementation uniform) but suppressed rules do not surface. We do not
|
|
@@ -3,5 +3,12 @@ export interface XpcshellRetryClassification {
|
|
|
3
3
|
xpcshell: readonly string[];
|
|
4
4
|
nonXpcshell: readonly string[];
|
|
5
5
|
}
|
|
6
|
-
/**
|
|
7
|
-
export
|
|
6
|
+
/** Dispatches a (possibly suite-specific) mach test run, mirroring `testWithOutput`. */
|
|
7
|
+
export type TestDispatch = (engineDir: string, testPaths: string[], args: string[], env?: Record<string, string>) => Promise<MachCommandResult>;
|
|
8
|
+
/**
|
|
9
|
+
* Removes a stale xpcshell install symlink and retries the focused mach test
|
|
10
|
+
* once. The retry uses the same `dispatch` (suite-specific or generic) the
|
|
11
|
+
* caller is already running on, so an xpcshell-suite run repairs and re-runs
|
|
12
|
+
* via `mach xpcshell-test` rather than falling back to the generic command.
|
|
13
|
+
*/
|
|
14
|
+
export declare function retryAfterXpcshellSymlinkRepair(engineDir: string, objDir: string | undefined, result: MachCommandResult, classification: XpcshellRetryClassification, normalizedPaths: string[], extraArgs: string[], env?: Record<string, string>, dispatch?: TestDispatch): Promise<MachCommandResult>;
|
|
@@ -1,16 +1,21 @@
|
|
|
1
1
|
// SPDX-License-Identifier: EUPL-1.2
|
|
2
2
|
import { testWithOutput } from './mach.js';
|
|
3
3
|
import { tryRepairStaleXpcshellTestSymlink } from './test-stale-symlink.js';
|
|
4
|
-
/**
|
|
5
|
-
|
|
4
|
+
/**
|
|
5
|
+
* Removes a stale xpcshell install symlink and retries the focused mach test
|
|
6
|
+
* once. The retry uses the same `dispatch` (suite-specific or generic) the
|
|
7
|
+
* caller is already running on, so an xpcshell-suite run repairs and re-runs
|
|
8
|
+
* via `mach xpcshell-test` rather than falling back to the generic command.
|
|
9
|
+
*/
|
|
10
|
+
export async function retryAfterXpcshellSymlinkRepair(engineDir, objDir, result, classification, normalizedPaths, extraArgs, env, dispatch = testWithOutput) {
|
|
6
11
|
if (result.exitCode !== 0 &&
|
|
7
12
|
classification.xpcshell.length > 0 &&
|
|
8
13
|
classification.nonXpcshell.length === 0) {
|
|
9
14
|
const repaired = await tryRepairStaleXpcshellTestSymlink(engineDir, objDir, `${result.stdout}\n${result.stderr}`);
|
|
10
15
|
if (repaired) {
|
|
11
16
|
return env
|
|
12
|
-
?
|
|
13
|
-
:
|
|
17
|
+
? dispatch(engineDir, normalizedPaths, extraArgs, env)
|
|
18
|
+
: dispatch(engineDir, normalizedPaths, extraArgs);
|
|
14
19
|
}
|
|
15
20
|
}
|
|
16
21
|
return result;
|
|
@@ -40,7 +40,9 @@ export interface ComposedShim {
|
|
|
40
40
|
* direction is intentional (declarations later in concat order
|
|
41
41
|
* augment earlier ones), so a project that wants to refine `Services`
|
|
42
42
|
* with a more specific type can do so by declaring it in the extra
|
|
43
|
-
* shim.
|
|
43
|
+
* shim. Any triple-slash `/// <reference path="…">` directives inside the
|
|
44
|
+
* extra shim are inlined (resolved against the extra shim's own directory)
|
|
45
|
+
* so they are not silently dropped at the synthetic shim path.
|
|
44
46
|
*
|
|
45
47
|
* Missing extra-shim files raise a clear error rather than failing
|
|
46
48
|
* silently with a confusing "type not found" downstream — this is the
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
* still fail `fireforge typecheck`, or vice versa, for reasons the
|
|
11
11
|
* operator could not infer from the rule names.
|
|
12
12
|
*/
|
|
13
|
-
import { resolve } from 'node:path';
|
|
13
|
+
import { dirname, resolve } from 'node:path';
|
|
14
14
|
import { pathExists, readText } from '../utils/fs.js';
|
|
15
15
|
/** Filename used for the synthetic Firefox-globals shim source file. */
|
|
16
16
|
export const SHIM_FILENAME = '__fireforge_firefox_globals.d.ts';
|
|
@@ -110,6 +110,43 @@ export const SUPPRESSED_DIAGNOSTIC_CODES = new Set([
|
|
|
110
110
|
2580, // Cannot find name '{0}'. Do you need to install type definitions...
|
|
111
111
|
7016, // Could not find a declaration file for module '{0}'.
|
|
112
112
|
]);
|
|
113
|
+
/** Matches a lone triple-slash `/// <reference path="…" />` directive line. */
|
|
114
|
+
const TRIPLE_SLASH_REFERENCE = /^\s*\/\/\/\s*<reference\s+path\s*=\s*["']([^"']+)["']\s*\/?>\s*$/;
|
|
115
|
+
/**
|
|
116
|
+
* Inlines triple-slash `/// <reference path="…">` directives in shim source.
|
|
117
|
+
*
|
|
118
|
+
* Both shim consumers feed the text to the compiler at a *synthetic* path
|
|
119
|
+
* (an in-memory source file, not the extra shim's real location), so TS
|
|
120
|
+
* resolves a relative `/// <reference>` against that synthetic directory and
|
|
121
|
+
* silently drops it. Inlining the referenced file's contents (recursively,
|
|
122
|
+
* resolved against the *referencing* file's directory, deduped by absolute
|
|
123
|
+
* path) makes the directives self-contained so their declarations survive.
|
|
124
|
+
*
|
|
125
|
+
* @param source - Shim source possibly containing reference directives
|
|
126
|
+
* @param baseDir - Directory the directives' relative paths resolve against
|
|
127
|
+
* @param seen - Absolute paths already inlined (cycle / duplicate guard)
|
|
128
|
+
*/
|
|
129
|
+
async function inlineTripleSlashReferences(source, baseDir, seen) {
|
|
130
|
+
const out = [];
|
|
131
|
+
for (const line of source.split('\n')) {
|
|
132
|
+
const match = TRIPLE_SLASH_REFERENCE.exec(line);
|
|
133
|
+
if (!match?.[1]) {
|
|
134
|
+
out.push(line);
|
|
135
|
+
continue;
|
|
136
|
+
}
|
|
137
|
+
const absolute = resolve(baseDir, match[1]);
|
|
138
|
+
if (seen.has(absolute))
|
|
139
|
+
continue;
|
|
140
|
+
seen.add(absolute);
|
|
141
|
+
if (!(await pathExists(absolute))) {
|
|
142
|
+
out.push(`// (fireforge: unresolved /// <reference path="${match[1]}">)`);
|
|
143
|
+
continue;
|
|
144
|
+
}
|
|
145
|
+
const referenced = await readText(absolute);
|
|
146
|
+
out.push(await inlineTripleSlashReferences(referenced, dirname(absolute), seen));
|
|
147
|
+
}
|
|
148
|
+
return out.join('\n');
|
|
149
|
+
}
|
|
113
150
|
/**
|
|
114
151
|
* Composes the synthetic shim source by concatenating the built-in
|
|
115
152
|
* Firefox globals shim with the contents of an optional user-supplied
|
|
@@ -117,7 +154,9 @@ export const SUPPRESSED_DIAGNOSTIC_CODES = new Set([
|
|
|
117
154
|
* direction is intentional (declarations later in concat order
|
|
118
155
|
* augment earlier ones), so a project that wants to refine `Services`
|
|
119
156
|
* with a more specific type can do so by declaring it in the extra
|
|
120
|
-
* shim.
|
|
157
|
+
* shim. Any triple-slash `/// <reference path="…">` directives inside the
|
|
158
|
+
* extra shim are inlined (resolved against the extra shim's own directory)
|
|
159
|
+
* so they are not silently dropped at the synthetic shim path.
|
|
121
160
|
*
|
|
122
161
|
* Missing extra-shim files raise a clear error rather than failing
|
|
123
162
|
* silently with a confusing "type not found" downstream — this is the
|
|
@@ -137,8 +176,9 @@ export async function composeShimSource(projectRoot, extraShimPath) {
|
|
|
137
176
|
'Check the path in fireforge.json or create the file.');
|
|
138
177
|
}
|
|
139
178
|
const extra = await readText(absoluteShim);
|
|
179
|
+
const inlinedExtra = await inlineTripleSlashReferences(extra, dirname(absoluteShim), new Set([absoluteShim]));
|
|
140
180
|
return {
|
|
141
|
-
source: `${FIREFOX_GLOBALS_SHIM}\n// ── extraShim: ${extraShimPath} ──\n${
|
|
181
|
+
source: `${FIREFOX_GLOBALS_SHIM}\n// ── extraShim: ${extraShimPath} ──\n${inlinedExtra}`,
|
|
142
182
|
extraShimAppended: true,
|
|
143
183
|
};
|
|
144
184
|
}
|
|
@@ -428,6 +428,15 @@ export interface TestOptions {
|
|
|
428
428
|
* command layer.
|
|
429
429
|
*/
|
|
430
430
|
harnessRetries?: number;
|
|
431
|
+
/**
|
|
432
|
+
* Force dispatch through the generic `mach test` command instead of the
|
|
433
|
+
* suite-specific `mach xpcshell-test` / `mach mochitest` commands a
|
|
434
|
+
* single-suite run auto-selects. Escape hatch for the rare case where a
|
|
435
|
+
* suite-specific command misbehaves; on a healthy host the generic command
|
|
436
|
+
* is equivalent. The default (auto suite dispatch) skips the mozlog
|
|
437
|
+
* resource monitor that crashes `mach test` on a broken host (E1).
|
|
438
|
+
*/
|
|
439
|
+
genericMachTest?: boolean;
|
|
431
440
|
/**
|
|
432
441
|
* Commander negation flag for `--no-shard`. When false, multiple test
|
|
433
442
|
* paths run in one combined mach invocation; by default they shard into
|
|
@@ -774,6 +783,14 @@ export interface LintCommandOptions {
|
|
|
774
783
|
* scope contracts are different.
|
|
775
784
|
*/
|
|
776
785
|
perPatch?: boolean;
|
|
786
|
+
/**
|
|
787
|
+
* Restrict `--per-patch` to a named subset of the queue (by filename,
|
|
788
|
+
* filename ± `.patch`, or manifest `name`). Lets a change that touches a
|
|
789
|
+
* handful of patches run the per-patch gate over just those instead of
|
|
790
|
+
* the full ~90-patch queue. Only valid with {@link perPatch}; queue-level
|
|
791
|
+
* findings (policy, cross-patch) are scoped to files the subset touches.
|
|
792
|
+
*/
|
|
793
|
+
patches?: string[];
|
|
777
794
|
/**
|
|
778
795
|
* Maximum warning count tolerated before lint exits non-zero. Mirrors
|
|
779
796
|
* ESLint's `--max-warnings` shape for release gates that want advisory
|
|
@@ -145,8 +145,15 @@ export type PatchLintSeverityGate = 'off' | 'warning' | 'error';
|
|
|
145
145
|
/**
|
|
146
146
|
* Allowlisted TypeScript `compilerOptions` overrides for the patch
|
|
147
147
|
* `checkJs` pass when {@link PatchLintConfig.checkJsStrict} is true.
|
|
148
|
-
* Merged after the strict preset
|
|
149
|
-
*
|
|
148
|
+
* Merged after the strict preset.
|
|
149
|
+
*
|
|
150
|
+
* Boolean flags tighten the strict preset. The optional `paths` mapping
|
|
151
|
+
* (each pattern may carry a single `*`) lets patch-owned modules be typed
|
|
152
|
+
* from their real sources — e.g. `"resource:///modules/foo/*": ["./*"]` —
|
|
153
|
+
* resolved host-side against the engine directory, so no `baseUrl` is set
|
|
154
|
+
* (TS5090-safe) and no hand-generated ambient stub shim is needed. Other
|
|
155
|
+
* options (`rootDir`, etc.) stay disallowed: they would fight the
|
|
156
|
+
* synthetic program.
|
|
150
157
|
*/
|
|
151
158
|
export interface PatchLintCheckJsCompilerOptions {
|
|
152
159
|
strictNullChecks?: boolean;
|
|
@@ -157,6 +164,8 @@ export interface PatchLintCheckJsCompilerOptions {
|
|
|
157
164
|
strictPropertyInitialization?: boolean;
|
|
158
165
|
noUnusedLocals?: boolean;
|
|
159
166
|
noUnusedParameters?: boolean;
|
|
167
|
+
/** Module-resolution `paths` mapping (pattern → targets, engine-relative). */
|
|
168
|
+
paths?: Record<string, string[]>;
|
|
160
169
|
}
|
|
161
170
|
/**
|
|
162
171
|
* Configuration for patch lint rules.
|