@hominis/fireforge 0.30.0 → 0.31.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 +26 -1
- package/README.md +22 -5
- package/dist/src/commands/export-all.js +5 -15
- package/dist/src/commands/export-flow.d.ts +6 -0
- package/dist/src/commands/export-flow.js +6 -1
- package/dist/src/commands/export-placement-gate.d.ts +38 -0
- package/dist/src/commands/export-placement-gate.js +105 -0
- package/dist/src/commands/export-shared.d.ts +28 -0
- package/dist/src/commands/export-shared.js +36 -0
- package/dist/src/commands/export.js +47 -112
- package/dist/src/commands/furnace/chrome-doc-templates.d.ts +0 -13
- package/dist/src/commands/furnace/chrome-doc-templates.js +1 -1
- package/dist/src/commands/furnace/create-dry-run.d.ts +1 -1
- package/dist/src/commands/furnace/create.d.ts +1 -2
- package/dist/src/commands/furnace/deploy.js +36 -114
- package/dist/src/commands/furnace/refresh.js +52 -32
- package/dist/src/commands/furnace/sync.js +2 -0
- package/dist/src/commands/import.js +108 -73
- package/dist/src/commands/lint-per-patch.d.ts +1 -1
- package/dist/src/commands/lint-per-patch.js +119 -78
- package/dist/src/commands/lint.d.ts +1 -58
- package/dist/src/commands/lint.js +96 -84
- package/dist/src/commands/patch/compact.d.ts +5 -2
- package/dist/src/commands/patch/compact.js +85 -25
- package/dist/src/commands/patch/delete.js +17 -17
- package/dist/src/commands/patch/index.js +2 -0
- package/dist/src/commands/patch/lint-ignore.js +3 -16
- package/dist/src/commands/patch/move-files.js +2 -0
- package/dist/src/commands/patch/patch-context.d.ts +41 -0
- package/dist/src/commands/patch/patch-context.js +53 -0
- package/dist/src/commands/patch/rename.js +10 -15
- package/dist/src/commands/patch/reorder.d.ts +0 -2
- package/dist/src/commands/patch/reorder.js +18 -19
- package/dist/src/commands/patch/split-plan.d.ts +66 -0
- package/dist/src/commands/patch/split-plan.js +178 -0
- package/dist/src/commands/patch/split.d.ts +30 -0
- package/dist/src/commands/patch/split.js +283 -0
- package/dist/src/commands/patch/staged-dependency.d.ts +1 -7
- package/dist/src/commands/patch/staged-dependency.js +4 -17
- package/dist/src/commands/patch/tier.js +4 -17
- package/dist/src/commands/re-export-scan.js +8 -1
- package/dist/src/commands/rebase/summary.d.ts +1 -5
- package/dist/src/commands/rebase/summary.js +1 -1
- package/dist/src/commands/status-output.js +77 -68
- package/dist/src/commands/test-diagnose.d.ts +23 -0
- package/dist/src/commands/test-diagnose.js +210 -0
- package/dist/src/commands/test-run.d.ts +58 -0
- package/dist/src/commands/test-run.js +88 -0
- package/dist/src/commands/test.js +169 -257
- package/dist/src/commands/token.js +15 -1
- package/dist/src/commands/wire.js +109 -78
- package/dist/src/core/build-audit.d.ts +1 -1
- package/dist/src/core/build-audit.js +2 -46
- package/dist/src/core/build-baseline-types.d.ts +38 -0
- package/dist/src/core/build-baseline-types.js +10 -0
- package/dist/src/core/build-baseline.d.ts +1 -31
- package/dist/src/core/build-prepare.d.ts +1 -1
- package/dist/src/core/build-prepare.js +2 -45
- package/dist/src/core/config-paths.d.ts +0 -8
- package/dist/src/core/config-paths.js +4 -4
- package/dist/src/core/config-state.d.ts +0 -6
- package/dist/src/core/config-state.js +1 -1
- package/dist/src/core/config-validate-patch-policy.js +12 -13
- package/dist/src/core/config-validate.js +48 -28
- package/dist/src/core/engine-changes.d.ts +24 -0
- package/dist/src/core/engine-changes.js +64 -0
- package/dist/src/core/firefox-cache.d.ts +0 -5
- package/dist/src/core/firefox-cache.js +1 -1
- package/dist/src/core/firefox-download.d.ts +0 -6
- package/dist/src/core/firefox-download.js +1 -1
- package/dist/src/core/furnace-apply-helpers.d.ts +1 -8
- package/dist/src/core/furnace-apply-helpers.js +11 -20
- package/dist/src/core/furnace-apply.d.ts +1 -1
- package/dist/src/core/furnace-apply.js +1 -1
- package/dist/src/core/furnace-checksum-utils.d.ts +7 -0
- package/dist/src/core/furnace-checksum-utils.js +15 -0
- package/dist/src/core/furnace-config-validate.d.ts +31 -0
- package/dist/src/core/furnace-config-validate.js +133 -0
- package/dist/src/core/furnace-config.d.ts +4 -32
- package/dist/src/core/furnace-config.js +15 -111
- package/dist/src/core/furnace-constants.d.ts +0 -10
- package/dist/src/core/furnace-constants.js +2 -2
- package/dist/src/core/furnace-css-fragments.d.ts +79 -0
- package/dist/src/core/furnace-css-fragments.js +243 -0
- package/dist/src/core/furnace-jsconfig.d.ts +63 -0
- package/dist/src/core/furnace-jsconfig.js +171 -0
- package/dist/src/core/furnace-validate-helpers.d.ts +16 -14
- package/dist/src/core/furnace-validate-helpers.js +40 -1
- package/dist/src/core/furnace-validate-registration.js +16 -1
- package/dist/src/core/furnace-validate.js +54 -2
- package/dist/src/core/git-file-ops.d.ts +0 -12
- package/dist/src/core/git-file-ops.js +2 -2
- package/dist/src/core/lint-cache.d.ts +3 -13
- package/dist/src/core/lint-cache.js +11 -5
- package/dist/src/core/mach.d.ts +5 -1
- package/dist/src/core/mach.js +6 -2
- package/dist/src/core/manifest-register.d.ts +5 -16
- package/dist/src/core/manifest-register.js +3 -1
- package/dist/src/core/patch-lint-checkjs.js +53 -7
- package/dist/src/core/patch-lint-jsdoc.js +63 -4
- package/dist/src/core/patch-lint-observer.d.ts +37 -0
- package/dist/src/core/patch-lint-observer.js +168 -0
- package/dist/src/core/patch-lint.js +132 -125
- package/dist/src/core/patch-manifest-io.d.ts +16 -0
- package/dist/src/core/patch-manifest-io.js +44 -2
- package/dist/src/core/patch-manifest-validate.d.ts +1 -8
- package/dist/src/core/patch-manifest-validate.js +1 -1
- package/dist/src/core/patch-manifest.d.ts +1 -1
- package/dist/src/core/patch-manifest.js +1 -1
- package/dist/src/core/patch-policy.d.ts +0 -4
- package/dist/src/core/patch-policy.js +10 -4
- package/dist/src/core/register-browser-content.d.ts +1 -1
- package/dist/src/core/register-module.d.ts +1 -1
- package/dist/src/core/register-result.d.ts +21 -0
- package/dist/src/core/register-result.js +9 -0
- package/dist/src/core/register-shared-css.d.ts +1 -1
- package/dist/src/core/register-test-manifest.d.ts +1 -1
- package/dist/src/core/test-harness-crash.d.ts +61 -0
- package/dist/src/core/test-harness-crash.js +140 -0
- package/dist/src/core/test-stale-check.d.ts +1 -1
- package/dist/src/core/test-stale-check.js +2 -46
- package/dist/src/core/test-xpcshell-retry.d.ts +1 -1
- package/dist/src/core/test-xpcshell-retry.js +4 -2
- package/dist/src/core/token-dark-mode.js +14 -26
- package/dist/src/core/token-manager.d.ts +4 -0
- package/dist/src/core/token-manager.js +70 -16
- package/dist/src/core/typecheck-shim.d.ts +0 -21
- package/dist/src/core/typecheck-shim.js +26 -4
- package/dist/src/core/wire-utils.js +37 -44
- package/dist/src/types/commands/index.d.ts +1 -1
- package/dist/src/types/commands/options.d.ts +105 -0
- package/dist/src/types/furnace.d.ts +12 -1
- package/dist/src/utils/elapsed.d.ts +0 -2
- package/dist/src/utils/elapsed.js +1 -1
- package/dist/src/utils/fs.d.ts +0 -5
- package/dist/src/utils/fs.js +1 -1
- package/dist/src/utils/regex.d.ts +0 -6
- package/dist/src/utils/regex.js +3 -3
- package/dist/src/utils/validation.d.ts +0 -8
- package/dist/src/utils/validation.js +2 -2
- package/package.json +6 -4
|
@@ -69,39 +69,54 @@ function validateDarkValue(options) {
|
|
|
69
69
|
throw new InvalidArgumentError('Override mode requires --dark-value to be specified.', 'darkValue');
|
|
70
70
|
}
|
|
71
71
|
}
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
const lines = content.split('\n');
|
|
72
|
+
/**
|
|
73
|
+
* True when `lines` contain a category header (single-line or multi-line
|
|
74
|
+
* banner shape) naming `category`. Shared by the pre-add assertion and the
|
|
75
|
+
* in-memory banner creation path so both agree on what "exists" means.
|
|
76
|
+
*/
|
|
77
|
+
function categoryHeaderExists(lines, category) {
|
|
79
78
|
const escapedCategory = escapeRegex(category);
|
|
80
79
|
const singleLinePattern = new RegExp(`\\/\\*\\s*=.*${escapedCategory}.*=\\s*\\*\\/`);
|
|
81
80
|
for (let i = 0; i < lines.length; i++) {
|
|
82
81
|
const line = lines[i] ?? '';
|
|
83
82
|
if (singleLinePattern.test(line)) {
|
|
84
|
-
return;
|
|
83
|
+
return true;
|
|
85
84
|
}
|
|
86
85
|
if (/^\s*\/\*\s*=+/.test(line) && !/\*\//.test(line)) {
|
|
87
86
|
for (let j = i + 1; j < Math.min(i + 6, lines.length); j++) {
|
|
88
87
|
const blockLine = lines[j] ?? '';
|
|
89
88
|
if (new RegExp(escapedCategory).test(blockLine)) {
|
|
90
|
-
return;
|
|
89
|
+
return true;
|
|
91
90
|
}
|
|
92
91
|
if (/\*\//.test(blockLine))
|
|
93
92
|
break;
|
|
94
93
|
}
|
|
95
94
|
}
|
|
96
95
|
}
|
|
96
|
+
return false;
|
|
97
|
+
}
|
|
98
|
+
async function assertTokenCategoryExists(engineDir, tokensCssPath, category, createCategory = false) {
|
|
99
|
+
const filePath = join(engineDir, tokensCssPath);
|
|
100
|
+
if (!(await pathExists(filePath))) {
|
|
101
|
+
throw new GeneralError(`Token CSS file not found: ${tokensCssPath}`);
|
|
102
|
+
}
|
|
103
|
+
const content = await readText(filePath);
|
|
104
|
+
const lines = content.split('\n');
|
|
105
|
+
if (categoryHeaderExists(lines, category))
|
|
106
|
+
return;
|
|
107
|
+
// The write path declares the banner in the same edit as the token
|
|
108
|
+
// insertion, so a missing category is fine when creation was requested.
|
|
109
|
+
if (createCategory)
|
|
110
|
+
return;
|
|
97
111
|
const discoveredCategories = discoverCategoryHeaders(lines);
|
|
98
112
|
const available = discoveredCategories.length > 0
|
|
99
113
|
? `Available categories in the file: ${discoveredCategories.map((name) => `"${name}"`).join(', ')}.`
|
|
100
|
-
: 'The file currently has no category headers.
|
|
114
|
+
: 'The file currently has no category headers.';
|
|
101
115
|
throw new GeneralError(`Category "${category}" not found in ${tokensCssPath}.\n\n` +
|
|
102
116
|
`${available}\n\n` +
|
|
103
117
|
'Categories are declared by comment headers. Single-line shape: /* = My Category = */. ' +
|
|
104
|
-
'Multi-line shape: /* =============\\n * My Category\\n * =============
|
|
118
|
+
'Multi-line shape: /* =============\\n * My Category\\n * ============= */.\n\n' +
|
|
119
|
+
'Re-run with --create-category to declare the banner and insert the token in one step.');
|
|
105
120
|
}
|
|
106
121
|
/**
|
|
107
122
|
* Scans a tokens CSS file for category header comments and returns the
|
|
@@ -156,7 +171,7 @@ export async function validateTokenAdd(root, options) {
|
|
|
156
171
|
validateTokenNameSyntax(options.tokenName);
|
|
157
172
|
await validateTokenPrefix(root, options);
|
|
158
173
|
validateDarkValue(options);
|
|
159
|
-
await assertTokenCategoryExists(engineDir, tokensCssPath, options.category);
|
|
174
|
+
await assertTokenCategoryExists(engineDir, tokensCssPath, options.category, options.createCategory === true);
|
|
160
175
|
}
|
|
161
176
|
/**
|
|
162
177
|
* Adds a design token to the CSS file and documentation.
|
|
@@ -185,7 +200,7 @@ export async function addToken(root, options) {
|
|
|
185
200
|
};
|
|
186
201
|
}
|
|
187
202
|
// --- CSS file ---
|
|
188
|
-
const cssAdded = await addTokenToCSS(engineDir, options, tokensCssPath);
|
|
203
|
+
const { added: cssAdded, categoryCreated } = await addTokenToCSS(engineDir, options, tokensCssPath);
|
|
189
204
|
if (!cssAdded) {
|
|
190
205
|
return {
|
|
191
206
|
cssAdded: false,
|
|
@@ -203,8 +218,39 @@ export async function addToken(root, options) {
|
|
|
203
218
|
unmappedAdded: docsResult.unmappedAdded,
|
|
204
219
|
countUpdated: docsResult.countUpdated,
|
|
205
220
|
skipped: false,
|
|
221
|
+
categoryCreated,
|
|
206
222
|
};
|
|
207
223
|
}
|
|
224
|
+
/**
|
|
225
|
+
* Splices a new single-line category banner ("= Name =" comment shape, the
|
|
226
|
+
* same format `discoverCategoryHeaders` recognises) just before the closing
|
|
227
|
+
* brace of the `:root` block, making the new category the last section.
|
|
228
|
+
* Mutates `lines` in place.
|
|
229
|
+
*/
|
|
230
|
+
function declareCategoryBanner(lines, category, tokensCssPath) {
|
|
231
|
+
let rootOpen = -1;
|
|
232
|
+
for (let i = 0; i < lines.length; i++) {
|
|
233
|
+
if (/:root\s*\{/.test(lines[i] ?? '')) {
|
|
234
|
+
rootOpen = i;
|
|
235
|
+
break;
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
if (rootOpen === -1) {
|
|
239
|
+
throw new GeneralError(`Cannot create category "${category}": no :root block found in ${tokensCssPath}. ` +
|
|
240
|
+
'Run "fireforge furnace init --force" to re-scaffold the tokens CSS file.');
|
|
241
|
+
}
|
|
242
|
+
let rootClose = -1;
|
|
243
|
+
for (let i = rootOpen + 1; i < lines.length; i++) {
|
|
244
|
+
if (/^\s*\}/.test(lines[i] ?? '')) {
|
|
245
|
+
rootClose = i;
|
|
246
|
+
break;
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
if (rootClose === -1) {
|
|
250
|
+
throw new GeneralError(`Cannot create category "${category}": the :root block in ${tokensCssPath} never closes.`);
|
|
251
|
+
}
|
|
252
|
+
lines.splice(rootClose, 0, '', ` /* = ${category} = */`);
|
|
253
|
+
}
|
|
208
254
|
function findCategorySection(lines, category, tokensCssPath) {
|
|
209
255
|
const escapedCategory = escapeRegex(category);
|
|
210
256
|
const singleLinePattern = new RegExp(`\\/\\*\\s*=.*${escapedCategory}.*=\\s*\\*\\/`);
|
|
@@ -296,15 +342,23 @@ function insertDarkModeOverride(lines, options) {
|
|
|
296
342
|
*/
|
|
297
343
|
async function addTokenToCSS(engineDir, options, tokensCssPath) {
|
|
298
344
|
const filePath = join(engineDir, tokensCssPath);
|
|
299
|
-
await assertTokenCategoryExists(engineDir, tokensCssPath, options.category);
|
|
345
|
+
await assertTokenCategoryExists(engineDir, tokensCssPath, options.category, options.createCategory === true);
|
|
300
346
|
let content = await readText(filePath);
|
|
301
347
|
// Idempotency check — strip CSS block comments so we don't match inside them
|
|
302
348
|
const stripped = content.replace(/\/\*[\s\S]*?\*\//g, '');
|
|
303
349
|
if (stripped.includes(options.tokenName + ':')) {
|
|
304
|
-
return false;
|
|
350
|
+
return { added: false, categoryCreated: false };
|
|
305
351
|
}
|
|
306
352
|
const lines = content.split('\n');
|
|
307
353
|
const annotation = getModeAnnotation(options.mode, options.value);
|
|
354
|
+
// Declare a missing category banner in the same in-memory edit as the
|
|
355
|
+
// token insertion — the file is written exactly once, so a failure
|
|
356
|
+
// between "banner declared" and "token inserted" cannot occur.
|
|
357
|
+
let categoryCreated = false;
|
|
358
|
+
if (options.createCategory === true && !categoryHeaderExists(lines, options.category)) {
|
|
359
|
+
declareCategoryBanner(lines, options.category, tokensCssPath);
|
|
360
|
+
categoryCreated = true;
|
|
361
|
+
}
|
|
308
362
|
const { categoryLine, sectionEnd } = findCategorySection(lines, options.category, tokensCssPath);
|
|
309
363
|
// Build the insertion lines
|
|
310
364
|
const insertLines = [];
|
|
@@ -325,7 +379,7 @@ async function addTokenToCSS(engineDir, options, tokensCssPath) {
|
|
|
325
379
|
insertDarkModeOverride(lines, options);
|
|
326
380
|
content = lines.join('\n');
|
|
327
381
|
await writeText(filePath, content);
|
|
328
|
-
return true;
|
|
382
|
+
return { added: true, categoryCreated };
|
|
329
383
|
}
|
|
330
384
|
/**
|
|
331
385
|
* Strips surrounding backticks from a cell, if present. Token cells are
|
|
@@ -11,27 +11,6 @@
|
|
|
11
11
|
*/
|
|
12
12
|
/** Filename used for the synthetic Firefox-globals shim source file. */
|
|
13
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
|
-
* - Dynamic `import("resource:-…")` / `import("chrome:-…")` under patch
|
|
30
|
-
* checkJs: the compiler sees empty stubs (`noResolve`); without URL
|
|
31
|
-
* ambient modules namespaces degrade to unusable typings. Wildcards
|
|
32
|
-
* keep Firefox URL imports pragmatically loose, same posture as globals.
|
|
33
|
-
*/
|
|
34
|
-
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\n// Shorthand ambient modules \u2014 exports from matching URL imports are loosely typed,\n// avoiding noResolve empty-graph namespaces. (Named member access broke when we tried\n// export= Record under moduleResolution Bundler.)\ndeclare module 'resource:*';\ndeclare module 'chrome:*';\n\n";
|
|
35
14
|
/**
|
|
36
15
|
* TS diagnostic codes suppressed by both the patch-lint checkJs pass
|
|
37
16
|
* and the whole-project typecheck command. Each is a known false
|
|
@@ -30,18 +30,21 @@ export const SHIM_FILENAME = '__fireforge_firefox_globals.d.ts';
|
|
|
30
30
|
* - Browser chrome globals like `gBrowser`, `gURLBar` are common in
|
|
31
31
|
* content scripts wired via `browser.js`.
|
|
32
32
|
* - Dynamic `import("resource:-…")` / `import("chrome:-…")` under patch
|
|
33
|
-
* checkJs:
|
|
34
|
-
*
|
|
35
|
-
*
|
|
33
|
+
* checkJs: imports of *patch-owned* modules resolve to their real
|
|
34
|
+
* sources (see `patch-lint-checkjs.ts`); everything else fails host
|
|
35
|
+
* resolution and lands on these URL ambient wildcards, keeping
|
|
36
|
+
* upstream Firefox imports pragmatically loose, same posture as globals.
|
|
36
37
|
*/
|
|
37
|
-
|
|
38
|
+
const FIREFOX_GLOBALS_SHIM = `
|
|
38
39
|
declare var Services: any;
|
|
39
40
|
declare var ChromeUtils: {
|
|
40
41
|
defineESModuleGetters(target: any, modules: Record<string, string>): void;
|
|
41
42
|
importESModule(specifier: string): any;
|
|
42
43
|
import(specifier: string): any;
|
|
43
44
|
defineModuleGetter(target: any, name: string, specifier: string): void;
|
|
45
|
+
defineLazyGetter(target: any, name: string, getter: () => any): void;
|
|
44
46
|
generateQI(interfaces: any[]): Function;
|
|
47
|
+
getClassName(obj: any, unwrap?: boolean): string;
|
|
45
48
|
isClassInfo(obj: any): boolean;
|
|
46
49
|
};
|
|
47
50
|
declare var Cu: any;
|
|
@@ -58,6 +61,25 @@ declare var gBrowser: any;
|
|
|
58
61
|
declare var gURLBar: any;
|
|
59
62
|
declare var gNavigatorBundle: any;
|
|
60
63
|
declare var AppConstants: any;
|
|
64
|
+
// Fluent localization — a stable chrome global. Members stay loose (any),
|
|
65
|
+
// but the constructor shape is declared so "new Localization([...])" and
|
|
66
|
+
// "new Localization([...], true)" typecheck without a local cast.
|
|
67
|
+
declare var Localization: {
|
|
68
|
+
new (
|
|
69
|
+
resourceIds: Array<string | { path: string; optional?: boolean }>,
|
|
70
|
+
sync?: boolean
|
|
71
|
+
): {
|
|
72
|
+
formatValue(id: string, args?: Record<string, unknown>): any;
|
|
73
|
+
formatValues(keys: any[]): any;
|
|
74
|
+
formatMessages(keys: any[]): any;
|
|
75
|
+
formatValueSync(id: string, args?: Record<string, unknown>): any;
|
|
76
|
+
formatValuesSync(keys: any[]): any;
|
|
77
|
+
formatMessagesSync(keys: any[]): any;
|
|
78
|
+
addResourceIds(ids: Array<string | { path: string; optional?: boolean }>): void;
|
|
79
|
+
removeResourceIds(ids: string[]): number;
|
|
80
|
+
setAsync(): void;
|
|
81
|
+
};
|
|
82
|
+
};
|
|
61
83
|
|
|
62
84
|
// Shorthand ambient modules — exports from matching URL imports are loosely typed,
|
|
63
85
|
// avoiding noResolve empty-graph namespaces. (Named member access broke when we tried
|
|
@@ -60,9 +60,7 @@ export function coerceToCall(expression) {
|
|
|
60
60
|
*/
|
|
61
61
|
export function countBraceDepth(line, inBlockComment) {
|
|
62
62
|
let depth = 0;
|
|
63
|
-
let
|
|
64
|
-
let inDouble = false;
|
|
65
|
-
let inTemplate = false;
|
|
63
|
+
let quote = null;
|
|
66
64
|
let inLine = false;
|
|
67
65
|
let inBlock = inBlockComment;
|
|
68
66
|
for (let i = 0; i < line.length; i++) {
|
|
@@ -77,23 +75,13 @@ export function countBraceDepth(line, inBlockComment) {
|
|
|
77
75
|
}
|
|
78
76
|
continue;
|
|
79
77
|
}
|
|
80
|
-
if (ch === '\\' &&
|
|
78
|
+
if (ch === '\\' && quote !== null) {
|
|
81
79
|
i++;
|
|
82
80
|
continue;
|
|
83
81
|
}
|
|
84
|
-
if (
|
|
85
|
-
if (ch ===
|
|
86
|
-
|
|
87
|
-
continue;
|
|
88
|
-
}
|
|
89
|
-
if (inDouble) {
|
|
90
|
-
if (ch === '"')
|
|
91
|
-
inDouble = false;
|
|
92
|
-
continue;
|
|
93
|
-
}
|
|
94
|
-
if (inTemplate) {
|
|
95
|
-
if (ch === '`')
|
|
96
|
-
inTemplate = false;
|
|
82
|
+
if (quote !== null) {
|
|
83
|
+
if (ch === quote)
|
|
84
|
+
quote = null;
|
|
97
85
|
continue;
|
|
98
86
|
}
|
|
99
87
|
if (ch === '/' && next === '/') {
|
|
@@ -105,35 +93,12 @@ export function countBraceDepth(line, inBlockComment) {
|
|
|
105
93
|
i++;
|
|
106
94
|
continue;
|
|
107
95
|
}
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
if (ch === '/' && next !== undefined) {
|
|
111
|
-
const prev = i > 0 ? line[i - 1] : undefined;
|
|
112
|
-
if (prev === undefined || /[=(:,!|&?;~^{[\n+\-*%<>]/.test(prev)) {
|
|
113
|
-
// Skip to closing /
|
|
114
|
-
i++;
|
|
115
|
-
while (i < line.length) {
|
|
116
|
-
if (line[i] === '\\') {
|
|
117
|
-
i++; // skip escaped character
|
|
118
|
-
}
|
|
119
|
-
else if (line[i] === '/') {
|
|
120
|
-
break;
|
|
121
|
-
}
|
|
122
|
-
i++;
|
|
123
|
-
}
|
|
124
|
-
continue;
|
|
125
|
-
}
|
|
126
|
-
}
|
|
127
|
-
if (ch === "'") {
|
|
128
|
-
inSingle = true;
|
|
129
|
-
continue;
|
|
130
|
-
}
|
|
131
|
-
if (ch === '"') {
|
|
132
|
-
inDouble = true;
|
|
96
|
+
if (ch === '/' && next !== undefined && isRegexLiteralStart(line, i)) {
|
|
97
|
+
i = scanToRegexLiteralEnd(line, i);
|
|
133
98
|
continue;
|
|
134
99
|
}
|
|
135
|
-
if (ch === '`') {
|
|
136
|
-
|
|
100
|
+
if (ch === "'" || ch === '"' || ch === '`') {
|
|
101
|
+
quote = ch;
|
|
137
102
|
continue;
|
|
138
103
|
}
|
|
139
104
|
if (ch === '{')
|
|
@@ -143,6 +108,34 @@ export function countBraceDepth(line, inBlockComment) {
|
|
|
143
108
|
}
|
|
144
109
|
return { depth, inBlockComment: inBlock };
|
|
145
110
|
}
|
|
111
|
+
/**
|
|
112
|
+
* Regex-literal opener heuristic for the fallback scanner: a `/` is
|
|
113
|
+
* treated as starting a regex when it follows an operator or
|
|
114
|
+
* keyword-boundary character (or starts the line). See the misfire notes
|
|
115
|
+
* on {@link countBraceDepth}.
|
|
116
|
+
*/
|
|
117
|
+
function isRegexLiteralStart(line, i) {
|
|
118
|
+
const prev = i > 0 ? line[i - 1] : undefined;
|
|
119
|
+
return prev === undefined || /[=(:,!|&?;~^{[\n+\-*%<>]/.test(prev);
|
|
120
|
+
}
|
|
121
|
+
/**
|
|
122
|
+
* Scans from the opening `/` of a regex literal to its closing `/`
|
|
123
|
+
* (honouring escapes), returning the index of the closing slash — or the
|
|
124
|
+
* end of the line when the literal never closes.
|
|
125
|
+
*/
|
|
126
|
+
function scanToRegexLiteralEnd(line, start) {
|
|
127
|
+
let i = start + 1;
|
|
128
|
+
while (i < line.length) {
|
|
129
|
+
if (line[i] === '\\') {
|
|
130
|
+
i++; // skip escaped character
|
|
131
|
+
}
|
|
132
|
+
else if (line[i] === '/') {
|
|
133
|
+
break;
|
|
134
|
+
}
|
|
135
|
+
i++;
|
|
136
|
+
}
|
|
137
|
+
return i;
|
|
138
|
+
}
|
|
146
139
|
/**
|
|
147
140
|
* Extracts the class/object name from an expression like "MyComponent.init()".
|
|
148
141
|
*/
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Re-exports all command-related types from focused sub-modules.
|
|
3
3
|
*/
|
|
4
|
-
export type { BuildOptions, DiscardOptions, DoctorOptions, DownloadOptions, ExportOptions, FurnaceApplyOptions, FurnaceCreateOptions, FurnaceDeployOptions, FurnaceOverrideOptions, FurnacePreviewOptions, FurnaceRefreshOptions, FurnaceRemoveOptions, FurnaceSyncOptions, FurnaceValidateOptions, GlobalOptions, ImportOptions, PackageOptions, PatchCompactOptions, PatchDeleteOptions, PatchLintIgnoreOptions, PatchMoveFilesOptions, PatchRenameOptions, PatchReorderOptions, PatchStagedDependencyOptions, PatchTierOptions, RebaseOptions, ReExportOptions, RegisterOptions, ResetOptions, RunOptions, SetupOptions, SourceSetOptions, StatusOptions, TestOptions, TokenAddOptions, WireOptions, } from './options.js';
|
|
4
|
+
export type { BuildOptions, DiscardOptions, DoctorOptions, DownloadOptions, ExportOptions, FurnaceApplyOptions, FurnaceCreateOptions, FurnaceDeployOptions, FurnaceOverrideOptions, FurnacePreviewOptions, FurnaceRefreshOptions, FurnaceRemoveOptions, FurnaceSyncOptions, FurnaceValidateOptions, GlobalOptions, ImportOptions, LintCommandOptions, PackageOptions, PatchCompactOptions, PatchDeleteOptions, PatchLintIgnoreOptions, PatchMoveFilesOptions, PatchRenameOptions, PatchReorderOptions, PatchSplitOptions, PatchStagedDependencyOptions, PatchTierOptions, RebaseOptions, ReExportOptions, RegisterOptions, ResetOptions, RunOptions, SetupOptions, SourceSetOptions, StatusOptions, TestOptions, TokenAddOptions, WireOptions, } from './options.js';
|
|
5
5
|
export type { ImportSummary, PatchCategory, PatchesManifest, PatchInfo, PatchLintIssue, PatchMetadata, PatchResult, PatchStagedDependencies, PatchStagedForwardImport, } from './patches.js';
|
|
6
6
|
export type { DoctorCheck, ProjectStatus, TokenCoverageFileEntry, TokenCoverageReport, } from './project.js';
|
|
@@ -304,6 +304,33 @@ export interface PatchStagedDependencyOptions {
|
|
|
304
304
|
* It validates an ownership transfer and prints the explicit
|
|
305
305
|
* `re-export --files` commands needed to perform it.
|
|
306
306
|
*/
|
|
307
|
+
/**
|
|
308
|
+
* Options for the `patch split` command.
|
|
309
|
+
*/
|
|
310
|
+
export interface PatchSplitOptions {
|
|
311
|
+
/** Files to move out of the source patch (engine-relative). */
|
|
312
|
+
files: string[];
|
|
313
|
+
/** Name for the new patch (used in its filename slug). */
|
|
314
|
+
name: string;
|
|
315
|
+
/** Category for the new patch; defaults to the source patch's category. */
|
|
316
|
+
category?: string;
|
|
317
|
+
/** Description for the new patch. */
|
|
318
|
+
description?: string;
|
|
319
|
+
/** Exact sparse order for the new patch (mutually exclusive with before/after). */
|
|
320
|
+
order?: number;
|
|
321
|
+
/** Place the new patch before this patch identifier. */
|
|
322
|
+
before?: string;
|
|
323
|
+
/** Place the new patch after this patch identifier (default: the source). */
|
|
324
|
+
after?: string;
|
|
325
|
+
/** Preview without writing. */
|
|
326
|
+
dryRun?: boolean;
|
|
327
|
+
/** Skip interactive confirmation (required for non-TTY). */
|
|
328
|
+
yes?: boolean;
|
|
329
|
+
/** Bypass projected-lint refusals. */
|
|
330
|
+
forceUnsafe?: boolean;
|
|
331
|
+
/** Skip per-patch lint of the projected bodies. */
|
|
332
|
+
skipLint?: boolean;
|
|
333
|
+
}
|
|
307
334
|
export interface PatchMoveFilesOptions {
|
|
308
335
|
/** File paths relative to engine/ to move from the source patch to the target patch. */
|
|
309
336
|
file?: string[];
|
|
@@ -394,6 +421,25 @@ export interface TestOptions {
|
|
|
394
421
|
* stale process holds the default port or CI uses another port.
|
|
395
422
|
*/
|
|
396
423
|
marionettePort?: number;
|
|
424
|
+
/**
|
|
425
|
+
* Retry budget for recognized harness crashes (resource-monitor
|
|
426
|
+
* tracebacks, pre-test no-output hangs, post-green shutdown re-entry).
|
|
427
|
+
* 0 disables retries. Defaults to {@link DEFAULT_HARNESS_RETRIES} at the
|
|
428
|
+
* command layer.
|
|
429
|
+
*/
|
|
430
|
+
harnessRetries?: number;
|
|
431
|
+
/**
|
|
432
|
+
* Commander negation flag for `--no-shard`. When false, multiple test
|
|
433
|
+
* paths run in one combined mach invocation; by default they shard into
|
|
434
|
+
* sequential single-file harness runs with an aggregate report.
|
|
435
|
+
*/
|
|
436
|
+
shard?: boolean;
|
|
437
|
+
/**
|
|
438
|
+
* Project-relative (or absolute) artifact path published to the harness
|
|
439
|
+
* run as `<BINARYNAME>_PERF_SAMPLE_JSON`, for downstream perf-budget
|
|
440
|
+
* checkers that consume a sample artifact after the run.
|
|
441
|
+
*/
|
|
442
|
+
perfSamples?: string;
|
|
397
443
|
}
|
|
398
444
|
/**
|
|
399
445
|
* Options for the furnace apply command.
|
|
@@ -648,6 +694,8 @@ export interface TokenAddOptions {
|
|
|
648
694
|
description?: string;
|
|
649
695
|
darkValue?: string;
|
|
650
696
|
dryRun?: boolean;
|
|
697
|
+
/** Declare the category banner in the tokens CSS when it does not exist yet. */
|
|
698
|
+
createCategory?: boolean;
|
|
651
699
|
}
|
|
652
700
|
/**
|
|
653
701
|
* Options for the doctor command.
|
|
@@ -681,3 +729,60 @@ export interface GlobalOptions {
|
|
|
681
729
|
/** Enable verbose/debug output */
|
|
682
730
|
verbose?: boolean;
|
|
683
731
|
}
|
|
732
|
+
/** Options controlling how the lint command filters and tags its output. */
|
|
733
|
+
export interface LintCommandOptions {
|
|
734
|
+
/**
|
|
735
|
+
* When set, tag each issue as `introduced` or `cumulative` based on
|
|
736
|
+
* whether its file changed since this git revision (e.g. `HEAD`, a
|
|
737
|
+
* branch name, or a SHA). Issues are not filtered — the full set still
|
|
738
|
+
* prints — but a diff-scoped summary makes it trivial to see which
|
|
739
|
+
* errors the current task introduced.
|
|
740
|
+
*/
|
|
741
|
+
since?: string;
|
|
742
|
+
/**
|
|
743
|
+
* When set together with {@link since}, scope the exit code to issues
|
|
744
|
+
* tagged `introduced`. Cumulative pre-existing errors still print (so
|
|
745
|
+
* the operator can still see the full queue state) but do not fail
|
|
746
|
+
* lint. Motivating case: a branch whose diff is clean but whose repo
|
|
747
|
+
* already carries unrelated `raw-color` / license-header errors from
|
|
748
|
+
* older patches. Without this flag, CI treats the clean branch as
|
|
749
|
+
* failing; with it, a branch "breaks the build" only when its own diff
|
|
750
|
+
* introduced a new error.
|
|
751
|
+
*
|
|
752
|
+
* Requires {@link since}: without a revision to diff against there is
|
|
753
|
+
* no distinction between introduced and cumulative, so the flag is
|
|
754
|
+
* rejected up-front rather than silently ignored.
|
|
755
|
+
*/
|
|
756
|
+
onlyIntroduced?: boolean;
|
|
757
|
+
/**
|
|
758
|
+
* Lint each patch in the queue as its own isolated diff, rather than
|
|
759
|
+
* the aggregate `git diff HEAD` across all applied patches.
|
|
760
|
+
*
|
|
761
|
+
* Motivating case: running `fireforge lint` (no args) on a repo where
|
|
762
|
+
* `fireforge import` or `fireforge rebase` has just applied the full
|
|
763
|
+
* patch queue produces an aggregate diff (every patch's changes
|
|
764
|
+
* summed). The patch-size advisory rules (`large-patch-lines`,
|
|
765
|
+
* `large-patch-files`) then fire against the sum — e.g. "Patch is
|
|
766
|
+
* 37529 lines" on a queue of 22 individually-fine patches — which
|
|
767
|
+
* reads as a task-specific regression when it is really an artefact
|
|
768
|
+
* of the aggregation. `--per-patch` rescopes the diff to each patch's
|
|
769
|
+
* own `filesAffected`, honours the patch's own `lintIgnore`, and runs
|
|
770
|
+
* the cross-patch rules once over the whole queue so queue-level
|
|
771
|
+
* findings (duplicate creations, forward imports) still surface.
|
|
772
|
+
*
|
|
773
|
+
* Mutually exclusive with passing explicit file paths — the two
|
|
774
|
+
* scope contracts are different.
|
|
775
|
+
*/
|
|
776
|
+
perPatch?: boolean;
|
|
777
|
+
/**
|
|
778
|
+
* Maximum warning count tolerated before lint exits non-zero. Mirrors
|
|
779
|
+
* ESLint's `--max-warnings` shape for release gates that want advisory
|
|
780
|
+
* findings to become blocking without changing default CLI behavior.
|
|
781
|
+
*/
|
|
782
|
+
maxWarnings?: number;
|
|
783
|
+
/**
|
|
784
|
+
* Bypass per-patch lint cache reads and writes. Accepted in aggregate mode
|
|
785
|
+
* for CLI consistency, but only `--per-patch` currently uses the cache.
|
|
786
|
+
*/
|
|
787
|
+
noCache?: boolean;
|
|
788
|
+
}
|
|
@@ -140,6 +140,15 @@ export interface FurnaceConfig {
|
|
|
140
140
|
* Always includes `toolkit/content/widgets` by default.
|
|
141
141
|
*/
|
|
142
142
|
scanPaths?: string[];
|
|
143
|
+
/**
|
|
144
|
+
* Project-relative path to a consumer-owned jsconfig/tsconfig whose
|
|
145
|
+
* `compilerOptions.paths` entries for deployed component modules
|
|
146
|
+
* (`chrome://global/content/elements/<file>.mjs`) Furnace keeps in sync
|
|
147
|
+
* on deploy. Only entries under that chrome prefix that map into
|
|
148
|
+
* `components/custom/` are managed; everything else is preserved.
|
|
149
|
+
* Unset disables jsconfig maintenance.
|
|
150
|
+
*/
|
|
151
|
+
typecheckJsconfig?: string;
|
|
143
152
|
/** Stock components tracked for preview */
|
|
144
153
|
stock: string[];
|
|
145
154
|
/** Override components */
|
|
@@ -234,7 +243,7 @@ export interface ApplyResult {
|
|
|
234
243
|
*/
|
|
235
244
|
export interface DryRunAction {
|
|
236
245
|
component: string;
|
|
237
|
-
action: 'copy' | 'register-ce' | 'register-jar' | 'copy-ftl' | 'undeploy-remove' | 'undeploy-restore' | 'unregister-ce' | 'unregister-jar';
|
|
246
|
+
action: 'copy' | 'expand-fragments' | 'register-ce' | 'register-jar' | 'copy-ftl' | 'undeploy-remove' | 'undeploy-restore' | 'unregister-ce' | 'unregister-jar';
|
|
238
247
|
source?: string;
|
|
239
248
|
target?: string;
|
|
240
249
|
description: string;
|
|
@@ -277,3 +286,5 @@ export interface ValidationIssue {
|
|
|
277
286
|
/** Human-readable description of the issue */
|
|
278
287
|
message: string;
|
|
279
288
|
}
|
|
289
|
+
/** Resolved test-harness selection for a `furnace create` run. */
|
|
290
|
+
export type ResolvedTestStyle = 'mochikit' | 'browser-chrome' | 'xpcshell' | 'none';
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
// SPDX-License-Identifier: EUPL-1.2
|
|
2
2
|
/** Formats elapsed milliseconds for progress messages. */
|
|
3
|
-
|
|
3
|
+
function formatElapsed(ms) {
|
|
4
4
|
const totalSeconds = Math.max(0, Math.round(ms / 1000));
|
|
5
5
|
const minutes = Math.floor(totalSeconds / 60);
|
|
6
6
|
const seconds = totalSeconds % 60;
|
package/dist/src/utils/fs.d.ts
CHANGED
|
@@ -13,11 +13,6 @@ export declare function pathExistsStrict(path: string): Promise<boolean>;
|
|
|
13
13
|
* @param path - Directory path to ensure
|
|
14
14
|
*/
|
|
15
15
|
export declare function ensureDir(path: string): Promise<void>;
|
|
16
|
-
/**
|
|
17
|
-
* Ensures the parent directory of a file exists.
|
|
18
|
-
* @param filePath - Path to a file
|
|
19
|
-
*/
|
|
20
|
-
export declare function ensureParentDir(filePath: string): Promise<void>;
|
|
21
16
|
/**
|
|
22
17
|
* Removes a directory recursively.
|
|
23
18
|
* @param path - Directory path to remove
|
package/dist/src/utils/fs.js
CHANGED
|
@@ -52,7 +52,7 @@ export async function ensureDir(path) {
|
|
|
52
52
|
* Ensures the parent directory of a file exists.
|
|
53
53
|
* @param filePath - Path to a file
|
|
54
54
|
*/
|
|
55
|
-
|
|
55
|
+
async function ensureParentDir(filePath) {
|
|
56
56
|
const parent = dirname(filePath);
|
|
57
57
|
await ensureDir(parent);
|
|
58
58
|
}
|
|
@@ -2,12 +2,6 @@
|
|
|
2
2
|
* Escapes special regex characters in a string.
|
|
3
3
|
*/
|
|
4
4
|
export declare function escapeRegex(str: string): string;
|
|
5
|
-
/** Matches hex color values like #fff, #ff00ff, #ff00ff80 (longest-first alternation) */
|
|
6
|
-
export declare const CSS_HEX_COLOR: RegExp;
|
|
7
|
-
/** Matches rgb() or rgba() function calls */
|
|
8
|
-
export declare const CSS_RGB_COLOR: RegExp;
|
|
9
|
-
/** Matches hsl() or hsla() function calls */
|
|
10
|
-
export declare const CSS_HSL_COLOR: RegExp;
|
|
11
5
|
/**
|
|
12
6
|
* Returns true if the content contains any raw CSS color values (hex, rgb, hsl).
|
|
13
7
|
*/
|
package/dist/src/utils/regex.js
CHANGED
|
@@ -6,11 +6,11 @@ export function escapeRegex(str) {
|
|
|
6
6
|
return str.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
7
7
|
}
|
|
8
8
|
/** Matches hex color values like #fff, #ff00ff, #ff00ff80 (longest-first alternation) */
|
|
9
|
-
|
|
9
|
+
const CSS_HEX_COLOR = /#(?:[0-9a-fA-F]{8}|[0-9a-fA-F]{6}|[0-9a-fA-F]{3,4})\b/;
|
|
10
10
|
/** Matches rgb() or rgba() function calls */
|
|
11
|
-
|
|
11
|
+
const CSS_RGB_COLOR = /\brgba?\s*\(/;
|
|
12
12
|
/** Matches hsl() or hsla() function calls */
|
|
13
|
-
|
|
13
|
+
const CSS_HSL_COLOR = /\bhsla?\s*\(/;
|
|
14
14
|
// Global variants for counting (cached to avoid re-creation per call)
|
|
15
15
|
const CSS_HEX_COLOR_G = new RegExp(CSS_HEX_COLOR.source, 'g');
|
|
16
16
|
const CSS_RGB_COLOR_G = new RegExp(CSS_RGB_COLOR.source, 'g');
|
|
@@ -90,14 +90,6 @@ export declare const PATCH_CATEGORIES: readonly ["branding", "ui", "privacy", "s
|
|
|
90
90
|
* Validates a patch category string.
|
|
91
91
|
*/
|
|
92
92
|
export declare function isValidPatchCategory(category: string): category is (typeof PATCH_CATEGORIES)[number];
|
|
93
|
-
/**
|
|
94
|
-
* Checks whether a Firefox version string has an ESR suffix.
|
|
95
|
-
*/
|
|
96
|
-
export declare function isEsrVersion(version: string): boolean;
|
|
97
|
-
/**
|
|
98
|
-
* Checks whether a Firefox version string is a beta version (e.g. "147.0b1").
|
|
99
|
-
*/
|
|
100
|
-
export declare function isBetaVersion(version: string): boolean;
|
|
101
93
|
/**
|
|
102
94
|
* Infers the Firefox product type from a version string.
|
|
103
95
|
* Returns undefined if no clear inference can be made.
|
|
@@ -134,13 +134,13 @@ export function isValidPatchCategory(category) {
|
|
|
134
134
|
/**
|
|
135
135
|
* Checks whether a Firefox version string has an ESR suffix.
|
|
136
136
|
*/
|
|
137
|
-
|
|
137
|
+
function isEsrVersion(version) {
|
|
138
138
|
return /esr$/i.test(version);
|
|
139
139
|
}
|
|
140
140
|
/**
|
|
141
141
|
* Checks whether a Firefox version string is a beta version (e.g. "147.0b1").
|
|
142
142
|
*/
|
|
143
|
-
|
|
143
|
+
function isBetaVersion(version) {
|
|
144
144
|
return /b\d+$/.test(version);
|
|
145
145
|
}
|
|
146
146
|
/**
|