@hominis/fireforge 0.18.8 → 0.18.10

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.
Files changed (35) hide show
  1. package/dist/src/commands/lint.d.ts +36 -0
  2. package/dist/src/commands/lint.js +61 -1
  3. package/dist/src/commands/patch/index.d.ts +5 -3
  4. package/dist/src/commands/patch/index.js +8 -4
  5. package/dist/src/commands/patch/lint-ignore.d.ts +8 -0
  6. package/dist/src/commands/patch/lint-ignore.js +8 -4
  7. package/dist/src/commands/patch/rename.d.ts +36 -0
  8. package/dist/src/commands/patch/rename.js +244 -0
  9. package/dist/src/commands/test.js +50 -3
  10. package/dist/src/core/ast-utils.d.ts +5 -1
  11. package/dist/src/core/ast-utils.js +10 -3
  12. package/dist/src/core/build-audit-resolve.d.ts +5 -3
  13. package/dist/src/core/build-audit-resolve.js +12 -3
  14. package/dist/src/core/config-paths.d.ts +1 -1
  15. package/dist/src/core/config-paths.js +1 -0
  16. package/dist/src/core/config-validate.js +4 -0
  17. package/dist/src/core/license-headers.d.ts +5 -0
  18. package/dist/src/core/license-headers.js +46 -5
  19. package/dist/src/core/marionette-port.d.ts +29 -0
  20. package/dist/src/core/marionette-port.js +82 -0
  21. package/dist/src/core/patch-export.d.ts +10 -0
  22. package/dist/src/core/patch-export.js +8 -2
  23. package/dist/src/core/patch-lint-chrome-jsdoc.d.ts +47 -0
  24. package/dist/src/core/patch-lint-chrome-jsdoc.js +87 -0
  25. package/dist/src/core/patch-lint-cross.js +6 -1
  26. package/dist/src/core/patch-lint-jsdoc.d.ts +37 -0
  27. package/dist/src/core/patch-lint-jsdoc.js +24 -3
  28. package/dist/src/core/patch-lint-ownership.d.ts +21 -3
  29. package/dist/src/core/patch-lint-ownership.js +45 -18
  30. package/dist/src/core/patch-lint.d.ts +7 -2
  31. package/dist/src/core/patch-lint.js +24 -6
  32. package/dist/src/types/commands/index.d.ts +1 -1
  33. package/dist/src/types/commands/options.d.ts +36 -0
  34. package/dist/src/types/config.d.ts +12 -1
  35. package/package.json +1 -1
@@ -1,13 +1,41 @@
1
1
  // SPDX-License-Identifier: EUPL-1.2
2
2
  /**
3
- * Patch ownership resolution for `.sys.mjs` files.
3
+ * Patch ownership resolution for JS-shaped files.
4
4
  *
5
5
  * A file is "patch-owned" when it was created by the project's patch
6
6
  * queue rather than being an upstream Firefox file that happens to be
7
- * modified. This module computes the set of patch-owned `.sys.mjs`
8
- * paths so lint rules can scope enforcement to project code only.
7
+ * modified. This module computes the set of patch-owned paths so lint
8
+ * rules can scope enforcement to project code only.
9
+ *
10
+ * The two resolvers are kept separate (one per extension predicate)
11
+ * because the downstream rules differ: `.sys.mjs` files go through
12
+ * `runCheckJs` (TypeScript checkJs) and the export-walker JSDoc rule;
13
+ * chrome subscripts (`.js` non-`.sys.mjs`) only get the script-walker
14
+ * JSDoc rule. Mixing them in a single set would silently broaden
15
+ * `runCheckJs` to chrome subscripts, which it is not designed for.
9
16
  */
10
17
  import { collectNewFileCreatorsByPath } from './patch-lint-cross.js';
18
+ /**
19
+ * Returns the set of patch-owned files matching `predicate`. Internal
20
+ * helper shared by the per-extension resolvers below.
21
+ */
22
+ function resolveOwned(currentNewFiles, patchQueueCtx, predicate) {
23
+ const owned = new Set();
24
+ for (const file of currentNewFiles) {
25
+ if (predicate(file)) {
26
+ owned.add(file);
27
+ }
28
+ }
29
+ if (patchQueueCtx) {
30
+ const creators = collectNewFileCreatorsByPath(patchQueueCtx);
31
+ for (const [file, owners] of creators) {
32
+ if (predicate(file) && owners.length > 0) {
33
+ owned.add(file);
34
+ }
35
+ }
36
+ }
37
+ return owned;
38
+ }
11
39
  /**
12
40
  * Returns the set of file paths that are patch-owned `.sys.mjs` files.
13
41
  *
@@ -24,20 +52,19 @@ import { collectNewFileCreatorsByPath } from './patch-lint-cross.js';
24
52
  * @returns Set of patch-owned `.sys.mjs` file paths
25
53
  */
26
54
  export function resolvePatchOwnedSysMjs(currentNewFiles, patchQueueCtx) {
27
- const owned = new Set();
28
- for (const file of currentNewFiles) {
29
- if (file.endsWith('.sys.mjs')) {
30
- owned.add(file);
31
- }
32
- }
33
- if (patchQueueCtx) {
34
- const creators = collectNewFileCreatorsByPath(patchQueueCtx);
35
- for (const [file, owners] of creators) {
36
- if (file.endsWith('.sys.mjs') && owners.length > 0) {
37
- owned.add(file);
38
- }
39
- }
40
- }
41
- return owned;
55
+ return resolveOwned(currentNewFiles, patchQueueCtx, (file) => file.endsWith('.sys.mjs'));
56
+ }
57
+ /**
58
+ * Returns the set of file paths that are patch-owned chrome subscripts
59
+ * (`.js` files that are not `.sys.mjs` modules — typically
60
+ * `browser/base/content/<binaryName>*.js` and similar). Same ownership
61
+ * semantics as {@link resolvePatchOwnedSysMjs}.
62
+ *
63
+ * @param currentNewFiles - Files newly created in the current diff
64
+ * @param patchQueueCtx - Optional cross-patch context for queue-wide ownership
65
+ * @returns Set of patch-owned chrome-subscript file paths
66
+ */
67
+ export function resolvePatchOwnedChromeScripts(currentNewFiles, patchQueueCtx) {
68
+ return resolveOwned(currentNewFiles, patchQueueCtx, (file) => file.endsWith('.js') && !file.endsWith('.sys.mjs'));
42
69
  }
43
70
  //# sourceMappingURL=patch-lint-ownership.js.map
@@ -5,7 +5,7 @@ export { runCheckJs } from './patch-lint-checkjs.js';
5
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
6
  export { buildModifiedFileAdditionsFromDiff, detectNewFilesInDiff } from './patch-lint-diff.js';
7
7
  export { type JsDocCheck, type JsDocIssue, validateExportJsDoc } from './patch-lint-jsdoc.js';
8
- export { resolvePatchOwnedSysMjs } from './patch-lint-ownership.js';
8
+ export { resolvePatchOwnedChromeScripts, resolvePatchOwnedSysMjs } from './patch-lint-ownership.js';
9
9
  /**
10
10
  * Counts the total lines in a unified diff and the number of non-binary
11
11
  * text lines, so binary hunks do not inflate patch size checks.
@@ -55,9 +55,14 @@ export declare function lintNewFileHeaders(repoDir: string, newFiles: string[],
55
55
  * @param newFiles - Set of files that are newly created in this patch
56
56
  * @param config - Project configuration
57
57
  * @param patchOwnedFiles - Optional set of patch-owned `.sys.mjs` paths for scoped JSDoc enforcement
58
+ * @param patchOwnedChromeScripts - Optional set of patch-owned chrome
59
+ * subscripts (`.js` non-`.sys.mjs`) for scoped chrome-script JSDoc
60
+ * enforcement. Built by {@link resolvePatchOwnedChromeScripts}; passed
61
+ * separately from `patchOwnedFiles` so the `runCheckJs` consumer (which
62
+ * only accepts `.sys.mjs`) is not silently broadened.
58
63
  * @returns Array of lint issues
59
64
  */
60
- export declare function lintPatchedJs(repoDir: string, affectedFiles: string[], newFiles: Set<string>, config: FireForgeConfig, patchOwnedFiles?: Set<string>): Promise<PatchLintIssue[]>;
65
+ export declare function lintPatchedJs(repoDir: string, affectedFiles: string[], newFiles: Set<string>, config: FireForgeConfig, patchOwnedFiles?: Set<string>, patchOwnedChromeScripts?: Set<string>): Promise<PatchLintIssue[]>;
61
66
  /**
62
67
  * Checks that modifications to existing (non-new) JS/MJS files include at
63
68
  * least one `// BINARYNAME:` comment in the added lines.
@@ -7,10 +7,11 @@ 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
9
  import { runCheckJs } from './patch-lint-checkjs.js';
10
+ import { lintChromeScriptJsDocForFile } from './patch-lint-chrome-jsdoc.js';
10
11
  import { detectNewFilesInDiff, extractAddedLinesPerFile } from './patch-lint-diff.js';
11
12
  import { AGGREGATE_PATCH_FILE } from './patch-lint-diff-tag.js';
12
13
  import { validateExportJsDoc } from './patch-lint-jsdoc.js';
13
- import { resolvePatchOwnedSysMjs } from './patch-lint-ownership.js';
14
+ import { resolvePatchOwnedChromeScripts, resolvePatchOwnedSysMjs } from './patch-lint-ownership.js';
14
15
  // ---------------------------------------------------------------------------
15
16
  // Cross-patch lint re-exports
16
17
  // ---------------------------------------------------------------------------
@@ -24,7 +25,7 @@ export { runCheckJs } from './patch-lint-checkjs.js';
24
25
  export { buildPatchQueueContext, collectNewFileCreatorsByPath, extractImportSpecifiers, extractImportSpecifiersWithLines, findForwardImportIgnoreLines, FORWARD_IMPORT_IGNORE_MARKER, isForwardImportableFile, lintPatchQueue, lintPatchQueueDuplicateCreations, lintPatchQueueForwardImports, } from './patch-lint-cross.js';
25
26
  export { buildModifiedFileAdditionsFromDiff, detectNewFilesInDiff } from './patch-lint-diff.js';
26
27
  export { validateExportJsDoc } from './patch-lint-jsdoc.js';
27
- export { resolvePatchOwnedSysMjs } from './patch-lint-ownership.js';
28
+ export { resolvePatchOwnedChromeScripts, resolvePatchOwnedSysMjs } from './patch-lint-ownership.js';
28
29
  // ---------------------------------------------------------------------------
29
30
  // Helpers
30
31
  // ---------------------------------------------------------------------------
@@ -403,9 +404,14 @@ export async function lintNewFileHeaders(repoDir, newFiles, config) {
403
404
  * @param newFiles - Set of files that are newly created in this patch
404
405
  * @param config - Project configuration
405
406
  * @param patchOwnedFiles - Optional set of patch-owned `.sys.mjs` paths for scoped JSDoc enforcement
407
+ * @param patchOwnedChromeScripts - Optional set of patch-owned chrome
408
+ * subscripts (`.js` non-`.sys.mjs`) for scoped chrome-script JSDoc
409
+ * enforcement. Built by {@link resolvePatchOwnedChromeScripts}; passed
410
+ * separately from `patchOwnedFiles` so the `runCheckJs` consumer (which
411
+ * only accepts `.sys.mjs`) is not silently broadened.
406
412
  * @returns Array of lint issues
407
413
  */
408
- export async function lintPatchedJs(repoDir, affectedFiles, newFiles, config, patchOwnedFiles) {
414
+ export async function lintPatchedJs(repoDir, affectedFiles, newFiles, config, patchOwnedFiles, patchOwnedChromeScripts) {
409
415
  const jsFiles = affectedFiles.filter(isJsFile);
410
416
  if (jsFiles.length === 0)
411
417
  return [];
@@ -476,9 +482,20 @@ export async function lintPatchedJs(repoDir, affectedFiles, newFiles, config, pa
476
482
  });
477
483
  }
478
484
  }
479
- // 3b. Assertion floor for patch-introduced browser-chrome tests
485
+ // 3a. JSDoc on top-level declarations in patch-owned chrome subscripts.
486
+ // Dispatched out-of-line to keep this file under the per-file line
487
+ // budget. See `patch-lint-chrome-jsdoc.ts` for the rule body.
488
+ const isChromeOwned = file.endsWith('.js') &&
489
+ !isSysMjs &&
490
+ (patchOwnedChromeScripts ? patchOwnedChromeScripts.has(file) : isNew);
491
+ const chromeMode = config.patchLint?.chromeScriptJsDoc;
492
+ issues.push(...lintChromeScriptJsDocForFile(file, content, isChromeOwned, chromeMode));
493
+ // 3b. Assertion floor for browser-chrome tests touched by this patch
494
+ // (covers both newly introduced files and modified upstream tests —
495
+ // a patch that strips the last `Assert.equal` from an existing
496
+ // browser_*.js silently passed under the old `isNew` gate).
480
497
  const assertionFloor = config.patchLint?.testAssertionFloor;
481
- if (assertionFloor && assertionFloor !== 'off' && isNew && isBrowserChromeTestFile(file)) {
498
+ if (assertionFloor && assertionFloor !== 'off' && isBrowserChromeTestFile(file)) {
482
499
  const ASSERTION_TOKENS = ['Assert.', 'ok(', 'is(', 'isnot(', 'isDeeply('];
483
500
  const hasAssertion = ASSERTION_TOKENS.some((tok) => strippedContent.includes(tok));
484
501
  if (!hasAssertion) {
@@ -703,10 +720,11 @@ export async function lintExportedPatch(repoDir, affectedFiles, diffContent, con
703
720
  const newFiles = detectNewFilesInDiff(diffContent);
704
721
  const { textLines: lineCount } = countNonBinaryDiffLines(diffContent);
705
722
  const patchOwnedFiles = resolvePatchOwnedSysMjs(newFiles, patchQueueCtx);
723
+ const patchOwnedChromeScripts = resolvePatchOwnedChromeScripts(newFiles, patchQueueCtx);
706
724
  const [cssIssues, headerIssues, jsIssues, modifiedHeaderIssues] = await Promise.all([
707
725
  lintPatchedCss(repoDir, affectedFiles, diffContent, config),
708
726
  lintNewFileHeaders(repoDir, [...newFiles], config),
709
- lintPatchedJs(repoDir, affectedFiles, newFiles, config, patchOwnedFiles),
727
+ lintPatchedJs(repoDir, affectedFiles, newFiles, config, patchOwnedFiles, patchOwnedChromeScripts),
710
728
  lintModifiedFileHeaders(repoDir, affectedFiles, newFiles),
711
729
  ]);
712
730
  const modCommentIssues = lintModificationComments(diffContent, config);
@@ -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, PatchReorderOptions, PatchTierOptions, RebaseOptions, ReExportOptions, RegisterOptions, ResetOptions, RunOptions, SetupOptions, 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, PackageOptions, PatchCompactOptions, PatchDeleteOptions, PatchLintIgnoreOptions, PatchRenameOptions, PatchReorderOptions, PatchTierOptions, RebaseOptions, ReExportOptions, RegisterOptions, ResetOptions, RunOptions, SetupOptions, StatusOptions, TestOptions, TokenAddOptions, WireOptions, } from './options.js';
5
5
  export type { ImportSummary, PatchCategory, PatchesManifest, PatchInfo, PatchLintIssue, PatchMetadata, PatchResult, } from './patches.js';
6
6
  export type { DoctorCheck, ProjectStatus, TokenCoverageFileEntry, TokenCoverageReport, } from './project.js';
@@ -316,6 +316,14 @@ export interface TestOptions {
316
316
  * values appear after `--headless` if both are set.
317
317
  */
318
318
  machArg?: string[];
319
+ /**
320
+ * Override the Marionette control port (default 2828) used by the
321
+ * stale-browser probe, the `--doctor` preflight, and the auto-forwarded
322
+ * `--setpref=marionette.port=<n>` arg passed to mach. Set this when a
323
+ * stale process holds the default port and `kill` is not an option, or
324
+ * when a CI runner reserves a different port for parallel test runs.
325
+ */
326
+ marionettePort?: number;
319
327
  }
320
328
  /**
321
329
  * Options for the furnace apply command.
@@ -493,6 +501,34 @@ export interface PatchDeleteOptions {
493
501
  /** Bypass the hard refusal when later patches depend on the target. */
494
502
  forceUnsafe?: boolean;
495
503
  }
504
+ /**
505
+ * Options for the `fireforge patch rename` subcommand. Updates the
506
+ * patch's filename, manifest `name`, and (optionally) `description`
507
+ * atomically without rewriting the `.patch` file body. Companion to
508
+ * `re-export --files` for the case where the body is already correct
509
+ * but the patch's identity (filename + description) describes a
510
+ * pre-shrink scope; before this verb existed the only workaround was
511
+ * `delete` + re-export, which briefly dropped the patch from the queue.
512
+ */
513
+ export interface PatchRenameOptions {
514
+ /**
515
+ * New human-readable name. Sanitised the same way `export --name`
516
+ * sanitises into the filename slug (lowercase, non-alphanumerics
517
+ * collapsed to `-`, length-capped). The patch's `name` field stores
518
+ * the raw value; the filename uses the sanitised slug.
519
+ */
520
+ to?: string;
521
+ /**
522
+ * Replacement description. Omit to leave the description unchanged
523
+ * (intentional — operators frequently want to relabel the slug
524
+ * without touching the description).
525
+ */
526
+ description?: string;
527
+ /** Print the planned change without writing. */
528
+ dryRun?: boolean;
529
+ /** Skip the confirmation prompt (required for non-TTY). */
530
+ yes?: boolean;
531
+ }
496
532
  /**
497
533
  * Options for the patch reorder command.
498
534
  */
@@ -74,8 +74,19 @@ export interface PatchLintConfig {
74
74
  rawColorAllowlist?: string[];
75
75
  /** Enforce JSDoc on class-method exports in patch-owned .sys.mjs files. Default: 'off'. */
76
76
  jsdocClassMethods?: PatchLintSeverityGate;
77
- /** Require ≥1 assertion in patch-introduced browser_*.js test files. Default: 'off'. */
77
+ /** Require ≥1 assertion in any patch-touched browser_*.js test file (new or modified). Default: 'off'. */
78
78
  testAssertionFloor?: PatchLintSeverityGate;
79
+ /**
80
+ * Enforce JSDoc on top-level classes (and their methods) and functions
81
+ * in patch-owned chrome subscripts (`.js` files loaded via
82
+ * `Services.scriptloader.loadSubScript`, e.g.
83
+ * `browser/base/content/<binaryName>*.js`). Distinct from
84
+ * `jsdocClassMethods` because chrome subscripts are parsed as scripts,
85
+ * not ES modules — using one flag for both would silently disable the
86
+ * rule when a chrome subscript was fed to the module parser. Default:
87
+ * 'off'.
88
+ */
89
+ chromeScriptJsDoc?: PatchLintSeverityGate;
79
90
  }
80
91
  /**
81
92
  * Build mode for mach.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hominis/fireforge",
3
- "version": "0.18.8",
3
+ "version": "0.18.10",
4
4
  "description": "FireForge — a build tool for customizing Firefox",
5
5
  "type": "module",
6
6
  "main": "./dist/src/index.js",