@hominis/fireforge 0.38.0 → 0.39.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 +20 -0
- package/dist/src/commands/build.js +8 -3
- package/dist/src/commands/discard.js +75 -50
- package/dist/src/commands/export-flow.d.ts +1 -1
- package/dist/src/commands/export-flow.js +7 -4
- package/dist/src/commands/export-placement-gate.js +1 -1
- package/dist/src/commands/export-shared.js +17 -2
- package/dist/src/commands/import.js +5 -0
- package/dist/src/commands/lint-per-patch.js +15 -5
- package/dist/src/commands/lint.js +1 -1
- package/dist/src/commands/patch/lint-ignore.js +20 -1
- package/dist/src/commands/patch/move-files-into.d.ts +47 -0
- package/dist/src/commands/patch/move-files-into.js +245 -0
- package/dist/src/commands/patch/move-files.js +27 -13
- package/dist/src/commands/patch/rename.js +4 -1
- package/dist/src/commands/reset.js +5 -0
- package/dist/src/commands/status-output.d.ts +9 -0
- package/dist/src/commands/status-output.js +33 -1
- package/dist/src/commands/status.js +17 -3
- package/dist/src/commands/test-diagnose.d.ts +1 -1
- package/dist/src/commands/test-diagnose.js +9 -3
- package/dist/src/commands/test-modes.d.ts +21 -0
- package/dist/src/commands/test-modes.js +42 -0
- package/dist/src/commands/test-run.d.ts +2 -0
- package/dist/src/commands/test-run.js +8 -2
- package/dist/src/commands/test.js +38 -32
- package/dist/src/core/build-baseline-types.d.ts +7 -0
- package/dist/src/core/build-baseline.d.ts +1 -1
- package/dist/src/core/build-baseline.js +2 -1
- package/dist/src/core/build-prepare.d.ts +9 -0
- package/dist/src/core/build-prepare.js +24 -0
- package/dist/src/core/config-validate.js +8 -0
- package/dist/src/core/discard-baseline.d.ts +60 -0
- package/dist/src/core/discard-baseline.js +232 -0
- package/dist/src/core/license-headers.d.ts +12 -0
- package/dist/src/core/license-headers.js +27 -0
- package/dist/src/core/patch-export.d.ts +10 -1
- package/dist/src/core/patch-export.js +12 -2
- package/dist/src/core/patch-lint-checkjs.d.ts +6 -1
- package/dist/src/core/patch-lint-checkjs.js +30 -14
- package/dist/src/core/patch-lint-cross.d.ts +9 -1
- package/dist/src/core/patch-lint-cross.js +69 -9
- package/dist/src/core/patch-lint-mozbuild.d.ts +18 -0
- package/dist/src/core/patch-lint-mozbuild.js +104 -0
- package/dist/src/core/patch-lint.js +4 -1
- package/dist/src/core/patch-policy.d.ts +14 -1
- package/dist/src/core/patch-policy.js +13 -2
- package/dist/src/core/test-harness-crash.d.ts +10 -0
- package/dist/src/core/test-harness-crash.js +16 -0
- package/dist/src/core/test-stale-check.d.ts +15 -0
- package/dist/src/core/test-stale-check.js +37 -1
- package/dist/src/core/token-manager.js +39 -2
- package/dist/src/core/token-variant.d.ts +7 -0
- package/dist/src/core/token-variant.js +9 -0
- package/dist/src/core/typecheck-shim.d.ts +13 -0
- package/dist/src/core/typecheck-shim.js +17 -2
- package/dist/src/core/typecheck.js +14 -3
- package/dist/src/types/commands/options.d.ts +12 -0
- package/dist/src/types/config.d.ts +15 -0
- package/package.json +1 -1
|
@@ -4,14 +4,14 @@ import { GeneralError, InvalidArgumentError } from '../errors/base.js';
|
|
|
4
4
|
import { FurnaceError } from '../errors/furnace.js';
|
|
5
5
|
import { toError } from '../utils/errors.js';
|
|
6
6
|
import { pathExists, readText, writeText } from '../utils/fs.js';
|
|
7
|
-
import { warn } from '../utils/logger.js';
|
|
7
|
+
import { info, warn } from '../utils/logger.js';
|
|
8
8
|
import { escapeRegex } from '../utils/regex.js';
|
|
9
9
|
import { validateTokenName } from '../utils/validation.js';
|
|
10
10
|
import { getProjectPaths, loadConfig } from './config.js';
|
|
11
11
|
import { loadFurnaceConfig } from './furnace-config.js';
|
|
12
12
|
import { findDarkMediaCloseIndex, findDarkRootInsertionIndex } from './token-dark-mode.js';
|
|
13
13
|
import { addTokenToDocs } from './token-docs.js';
|
|
14
|
-
import { insertVariantDeclaration, validateVariantSelector, variantBlockHasToken, } from './token-variant.js';
|
|
14
|
+
import { insertVariantDeclaration, validateVariantSelector, variantBlockExists, variantBlockHasToken, } from './token-variant.js';
|
|
15
15
|
/** Returns the token CSS path relative to engine root for a given binary name. */
|
|
16
16
|
export function getTokensCssPath(binaryName) {
|
|
17
17
|
return `browser/themes/shared/${binaryName}-tokens.css`;
|
|
@@ -389,6 +389,39 @@ function findCategorySection(lines, category, tokensCssPath) {
|
|
|
389
389
|
}
|
|
390
390
|
return { categoryLine, sectionEnd };
|
|
391
391
|
}
|
|
392
|
+
/**
|
|
393
|
+
* Explicit theme-attribute selectors the override path keeps in sync with
|
|
394
|
+
* the `@media (prefers-color-scheme: dark)` block. Consumer scaffolds that
|
|
395
|
+
* pair the media query with `:root[data-theme="dark"]` /
|
|
396
|
+
* `:root[data-theme="light"]` blocks (viewer-toggle theming) require every
|
|
397
|
+
* themed list to declare identical token sets, so an override add that only
|
|
398
|
+
* wrote the media block was a guaranteed half-finished edit (FORGE F8).
|
|
399
|
+
*/
|
|
400
|
+
const THEME_ATTRIBUTE_VARIANTS = [
|
|
401
|
+
{ variant: '[data-theme="dark"]', pick: 'dark' },
|
|
402
|
+
{ variant: '[data-theme="light"]', pick: 'light' },
|
|
403
|
+
];
|
|
404
|
+
/**
|
|
405
|
+
* Mirrors an override's light/dark values into existing top-level
|
|
406
|
+
* `:root[data-theme="dark"]` / `:root[data-theme="light"]` blocks. Blocks
|
|
407
|
+
* that do not exist are left alone (scaffolded files have none; behavior is
|
|
408
|
+
* unchanged there). Idempotent per block. Returns the selectors written.
|
|
409
|
+
*/
|
|
410
|
+
function insertThemeAttributeOverrides(lines, options) {
|
|
411
|
+
if (options.mode !== 'override' || !options.darkValue)
|
|
412
|
+
return [];
|
|
413
|
+
const written = [];
|
|
414
|
+
for (const { variant, pick } of THEME_ATTRIBUTE_VARIANTS) {
|
|
415
|
+
if (!variantBlockExists(lines, variant))
|
|
416
|
+
continue;
|
|
417
|
+
if (variantBlockHasToken(lines, variant, options.tokenName))
|
|
418
|
+
continue;
|
|
419
|
+
const value = pick === 'dark' ? options.darkValue : options.value;
|
|
420
|
+
insertVariantDeclaration(lines, variant, ` ${options.tokenName}: ${value};`);
|
|
421
|
+
written.push(`:root${variant}`);
|
|
422
|
+
}
|
|
423
|
+
return written;
|
|
424
|
+
}
|
|
392
425
|
function insertDarkModeOverride(lines, options) {
|
|
393
426
|
if (options.mode !== 'override' || !options.darkValue)
|
|
394
427
|
return;
|
|
@@ -451,6 +484,10 @@ async function addTokenToCSS(engineDir, options, tokensCssPath) {
|
|
|
451
484
|
}
|
|
452
485
|
lines.splice(insertIndex, 0, ...insertLines);
|
|
453
486
|
insertDarkModeOverride(lines, options);
|
|
487
|
+
const themeBlocksWritten = insertThemeAttributeOverrides(lines, options);
|
|
488
|
+
if (themeBlocksWritten.length > 0) {
|
|
489
|
+
info(`Override also written to ${themeBlocksWritten.join(' and ')}.`);
|
|
490
|
+
}
|
|
454
491
|
content = lines.join('\n');
|
|
455
492
|
await writeText(filePath, content);
|
|
456
493
|
return { added: true, categoryCreated };
|
|
@@ -29,6 +29,13 @@ export type VariantValidation = {
|
|
|
29
29
|
* @param raw - Raw `--variant` value from the CLI / programmatic caller.
|
|
30
30
|
*/
|
|
31
31
|
export declare function validateVariantSelector(raw: unknown): VariantValidation;
|
|
32
|
+
/**
|
|
33
|
+
* True when a top-level `:root<variant>` block exists in `lines`. Used by
|
|
34
|
+
* the override path to write theme-attribute blocks only when the scaffold
|
|
35
|
+
* already declares them — `insertVariantDeclaration` would otherwise create
|
|
36
|
+
* the block, which is only wanted for the explicit `--variant` flow.
|
|
37
|
+
*/
|
|
38
|
+
export declare function variantBlockExists(lines: string[], variant: string): boolean;
|
|
32
39
|
/** True when the `:root<variant>` block already declares `tokenName`. */
|
|
33
40
|
export declare function variantBlockHasToken(lines: string[], variant: string, tokenName: string): boolean;
|
|
34
41
|
/**
|
|
@@ -107,6 +107,15 @@ function findVariantBlockInsertionPoint(lines) {
|
|
|
107
107
|
}
|
|
108
108
|
return lines.length;
|
|
109
109
|
}
|
|
110
|
+
/**
|
|
111
|
+
* True when a top-level `:root<variant>` block exists in `lines`. Used by
|
|
112
|
+
* the override path to write theme-attribute blocks only when the scaffold
|
|
113
|
+
* already declares them — `insertVariantDeclaration` would otherwise create
|
|
114
|
+
* the block, which is only wanted for the explicit `--variant` flow.
|
|
115
|
+
*/
|
|
116
|
+
export function variantBlockExists(lines, variant) {
|
|
117
|
+
return findVariantBlock(lines, variant) !== null;
|
|
118
|
+
}
|
|
110
119
|
/** True when the `:root<variant>` block already declares `tokenName`. */
|
|
111
120
|
export function variantBlockHasToken(lines, variant, tokenName) {
|
|
112
121
|
const block = findVariantBlock(lines, variant);
|
|
@@ -24,6 +24,19 @@ export declare const SHIM_FILENAME = "__fireforge_firefox_globals.d.ts";
|
|
|
24
24
|
* reason the operator couldn't infer from the docs.
|
|
25
25
|
*/
|
|
26
26
|
export declare const SUPPRESSED_DIAGNOSTIC_CODES: ReadonlySet<number>;
|
|
27
|
+
/**
|
|
28
|
+
* Undefined-free-identifier codes, split out of
|
|
29
|
+
* {@link SUPPRESSED_DIAGNOSTIC_CODES} (FORGE F12). Unconditional
|
|
30
|
+
* suppression let a module reference a name with no import or declaration
|
|
31
|
+
* anywhere and still typecheck with 0 errors — the failure then surfaced
|
|
32
|
+
* as a runtime `ReferenceError`. Both flows now report these at a
|
|
33
|
+
* configurable severity (default `'warning'`: visible without breaking
|
|
34
|
+
* gates; genuine shim gaps are silenced by adding the global to
|
|
35
|
+
* `extraShim`, or per-run via the `'off'` setting).
|
|
36
|
+
*/
|
|
37
|
+
export declare const UNDEFINED_IDENTIFIER_CODES: ReadonlySet<number>;
|
|
38
|
+
/** Hint appended to reported undefined-identifier diagnostics. */
|
|
39
|
+
export declare const UNDEFINED_IDENTIFIER_HINT: string;
|
|
27
40
|
/**
|
|
28
41
|
* Result of {@link composeShimSource}: the source body to feed into
|
|
29
42
|
* the TS host plus a flag indicating whether the user-supplied extra
|
|
@@ -146,11 +146,26 @@ export const SUPPRESSED_DIAGNOSTIC_CODES = new Set([
|
|
|
146
146
|
2306, // File '{0}' is not a module.
|
|
147
147
|
2305, // Module '{0}' has no exported member '{1}'.
|
|
148
148
|
2792, // Cannot find module '{0}'. Did you mean to set the 'moduleResolution' option...
|
|
149
|
-
2304, // Cannot find name '{0}'. (for globals we missed in the shim)
|
|
150
|
-
2552, // Cannot find name '{0}'. Did you mean '{1}'?
|
|
151
149
|
2580, // Cannot find name '{0}'. Do you need to install type definitions...
|
|
152
150
|
7016, // Could not find a declaration file for module '{0}'.
|
|
153
151
|
]);
|
|
152
|
+
/**
|
|
153
|
+
* Undefined-free-identifier codes, split out of
|
|
154
|
+
* {@link SUPPRESSED_DIAGNOSTIC_CODES} (FORGE F12). Unconditional
|
|
155
|
+
* suppression let a module reference a name with no import or declaration
|
|
156
|
+
* anywhere and still typecheck with 0 errors — the failure then surfaced
|
|
157
|
+
* as a runtime `ReferenceError`. Both flows now report these at a
|
|
158
|
+
* configurable severity (default `'warning'`: visible without breaking
|
|
159
|
+
* gates; genuine shim gaps are silenced by adding the global to
|
|
160
|
+
* `extraShim`, or per-run via the `'off'` setting).
|
|
161
|
+
*/
|
|
162
|
+
export const UNDEFINED_IDENTIFIER_CODES = new Set([
|
|
163
|
+
2304, // Cannot find name '{0}'.
|
|
164
|
+
2552, // Cannot find name '{0}'. Did you mean '{1}'?
|
|
165
|
+
]);
|
|
166
|
+
/** Hint appended to reported undefined-identifier diagnostics. */
|
|
167
|
+
export const UNDEFINED_IDENTIFIER_HINT = '(undefined identifier — import or declare it, add the global to the extra shim, ' +
|
|
168
|
+
'or tune the "undefinedIdentifiers" setting)';
|
|
154
169
|
/** Matches a lone triple-slash `/// <reference path="…" />` directive line. */
|
|
155
170
|
const TRIPLE_SLASH_REFERENCE = /^\s*\/\/\/\s*<reference\s+path\s*=\s*["']([^"']+)["']\s*\/?>\s*$/;
|
|
156
171
|
/**
|
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
import { dirname, isAbsolute, relative, resolve } from 'node:path';
|
|
22
22
|
import { pathExists } from '../utils/fs.js';
|
|
23
23
|
import { verbose } from '../utils/logger.js';
|
|
24
|
-
import { composeShimSource, SHIM_FILENAME, SUPPRESSED_DIAGNOSTIC_CODES } from './typecheck-shim.js';
|
|
24
|
+
import { composeShimSource, SHIM_FILENAME, SUPPRESSED_DIAGNOSTIC_CODES, UNDEFINED_IDENTIFIER_CODES, UNDEFINED_IDENTIFIER_HINT, } from './typecheck-shim.js';
|
|
25
25
|
/**
|
|
26
26
|
* Sentinel string surfaced as a `TypecheckIssue.message` when the
|
|
27
27
|
* project's jsconfig sets `checkJs: false`. Exported so tests can
|
|
@@ -131,7 +131,7 @@ export async function runTypecheck(projectRoot, cfg) {
|
|
|
131
131
|
});
|
|
132
132
|
continue;
|
|
133
133
|
}
|
|
134
|
-
results.push(await runTypecheckForProject(ts, projectRoot, projectPath, shimSource));
|
|
134
|
+
results.push(await runTypecheckForProject(ts, projectRoot, projectPath, shimSource, cfg.undefinedIdentifiers ?? 'warning'));
|
|
135
135
|
}
|
|
136
136
|
return results;
|
|
137
137
|
}
|
|
@@ -149,7 +149,7 @@ function resolveProjectExtraShim(cfg, projectPath) {
|
|
|
149
149
|
return cfg.extraShim;
|
|
150
150
|
}
|
|
151
151
|
/** Runs typecheck for a single jsconfig path, isolating its failures. */
|
|
152
|
-
async function runTypecheckForProject(ts, projectRoot, projectPath, shimSource) {
|
|
152
|
+
async function runTypecheckForProject(ts, projectRoot, projectPath, shimSource, undefinedIdentifiers = 'warning') {
|
|
153
153
|
const absConfig = resolve(projectRoot, projectPath);
|
|
154
154
|
if (!(await pathExists(absConfig))) {
|
|
155
155
|
return {
|
|
@@ -268,6 +268,17 @@ async function runTypecheckForProject(ts, projectRoot, projectPath, shimSource)
|
|
|
268
268
|
// can't act on them and they would clutter CI logs.
|
|
269
269
|
if (diag.file?.fileName === shimPath)
|
|
270
270
|
continue;
|
|
271
|
+
if (UNDEFINED_IDENTIFIER_CODES.has(diag.code)) {
|
|
272
|
+
if (undefinedIdentifiers === 'off')
|
|
273
|
+
continue;
|
|
274
|
+
const issue = diagnosticToIssue(ts, diag, absConfig, projectPath);
|
|
275
|
+
issues.push({
|
|
276
|
+
...issue,
|
|
277
|
+
category: undefinedIdentifiers,
|
|
278
|
+
message: `${issue.message} ${UNDEFINED_IDENTIFIER_HINT}`,
|
|
279
|
+
});
|
|
280
|
+
continue;
|
|
281
|
+
}
|
|
271
282
|
issues.push(diagnosticToIssue(ts, diag, absConfig, projectPath));
|
|
272
283
|
}
|
|
273
284
|
verbose(`typecheck: ${projectPath} — analyzed ${String(parsed.fileNames.length)} file(s), found ${String(issues.length)} issue(s)`);
|
|
@@ -160,6 +160,12 @@ export interface ResetOptions {
|
|
|
160
160
|
* Options for the discard command.
|
|
161
161
|
*/
|
|
162
162
|
export interface DiscardOptions {
|
|
163
|
+
/**
|
|
164
|
+
* Restore to pristine upstream (HEAD) instead of the patch-applied
|
|
165
|
+
* baseline; deletes patch-created files (the pre-0.39.0 semantics —
|
|
166
|
+
* FORGE F1).
|
|
167
|
+
*/
|
|
168
|
+
toUpstream?: boolean;
|
|
163
169
|
/** Show what would be discarded without doing it */
|
|
164
170
|
dryRun?: boolean;
|
|
165
171
|
/** Skip confirmation prompt */
|
|
@@ -795,6 +801,12 @@ export interface StatusOptions {
|
|
|
795
801
|
* patch as an ownership conflict.
|
|
796
802
|
*/
|
|
797
803
|
ownership?: boolean;
|
|
804
|
+
/**
|
|
805
|
+
* Print the recorded test-packaging coverage of the last build baseline
|
|
806
|
+
* (scope, timestamp, recording invocation) and exit. Read-only — the
|
|
807
|
+
* counterpart to the out-of-coverage test refusal (FORGE F11).
|
|
808
|
+
*/
|
|
809
|
+
testCoverage?: boolean;
|
|
798
810
|
/** Output machine-readable JSON instead of human-readable text. */
|
|
799
811
|
json?: boolean;
|
|
800
812
|
}
|
|
@@ -181,6 +181,14 @@ export interface TypecheckConfig {
|
|
|
181
181
|
* mismatch). A narrowed project sets `null` here to stay clean.
|
|
182
182
|
*/
|
|
183
183
|
projectOverrides?: Record<string, string | null>;
|
|
184
|
+
/**
|
|
185
|
+
* How to report undefined free identifiers (TS2304/TS2552). These were
|
|
186
|
+
* historically suppressed as shim-gap noise, which also hid genuine
|
|
187
|
+
* missing imports until they crashed at runtime (FORGE F12). Default
|
|
188
|
+
* `'warning'`: visible without failing the gate. `'error'` makes them
|
|
189
|
+
* blocking; `'off'` restores the historical suppression.
|
|
190
|
+
*/
|
|
191
|
+
undefinedIdentifiers?: PatchLintSeverityGate;
|
|
184
192
|
}
|
|
185
193
|
/**
|
|
186
194
|
* Wire command configuration.
|
|
@@ -262,6 +270,13 @@ export interface PatchLintConfig {
|
|
|
262
270
|
* 'off'.
|
|
263
271
|
*/
|
|
264
272
|
chromeScriptJsDoc?: PatchLintSeverityGate;
|
|
273
|
+
/**
|
|
274
|
+
* How the checkJs pass reports undefined free identifiers
|
|
275
|
+
* (TS2304/TS2552). Same semantics as `typecheck.undefinedIdentifiers`
|
|
276
|
+
* (FORGE F12); the two flows share the suppression policy so a patch
|
|
277
|
+
* cannot pass one and fail the other. Default: 'warning'.
|
|
278
|
+
*/
|
|
279
|
+
undefinedIdentifiers?: PatchLintSeverityGate;
|
|
265
280
|
}
|
|
266
281
|
/**
|
|
267
282
|
* Build mode for mach.
|