@hominis/fireforge 0.11.2 → 0.12.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.
Files changed (34) hide show
  1. package/CHANGELOG.md +24 -0
  2. package/README.md +54 -1
  3. package/dist/src/commands/export-shared.d.ts +2 -1
  4. package/dist/src/commands/export-shared.js +3 -2
  5. package/dist/src/commands/furnace/create.js +1 -1
  6. package/dist/src/commands/furnace/deploy.js +1 -1
  7. package/dist/src/commands/furnace/override.js +1 -1
  8. package/dist/src/commands/furnace/refresh.js +1 -1
  9. package/dist/src/commands/furnace/remove.js +1 -1
  10. package/dist/src/commands/furnace/rename.js +1 -1
  11. package/dist/src/commands/furnace/scan.js +1 -1
  12. package/dist/src/commands/lint.js +8 -3
  13. package/dist/src/core/ast-utils.d.ts +10 -0
  14. package/dist/src/core/ast-utils.js +18 -0
  15. package/dist/src/core/config-paths.d.ts +2 -2
  16. package/dist/src/core/config-paths.js +3 -0
  17. package/dist/src/core/config-validate.js +21 -3
  18. package/dist/src/core/file-lock.js +39 -2
  19. package/dist/src/core/furnace-apply.js +2 -1
  20. package/dist/src/core/furnace-config.js +6 -2
  21. package/dist/src/core/patch-apply.js +26 -4
  22. package/dist/src/core/patch-lint-checkjs.d.ts +21 -0
  23. package/dist/src/core/patch-lint-checkjs.js +225 -0
  24. package/dist/src/core/patch-lint-cross.d.ts +1 -0
  25. package/dist/src/core/patch-lint-cross.js +7 -0
  26. package/dist/src/core/patch-lint-jsdoc.d.ts +21 -0
  27. package/dist/src/core/patch-lint-jsdoc.js +259 -0
  28. package/dist/src/core/patch-lint-ownership.d.ts +25 -0
  29. package/dist/src/core/patch-lint-ownership.js +43 -0
  30. package/dist/src/core/patch-lint.d.ts +7 -2
  31. package/dist/src/core/patch-lint.js +30 -30
  32. package/dist/src/types/config.d.ts +9 -0
  33. package/dist/src/utils/paths.js +3 -1
  34. package/package.json +1 -1
@@ -6,7 +6,10 @@ import { verbose } from '../utils/logger.js';
6
6
  import { hasRawCssColors, stripJsComments } from '../utils/regex.js';
7
7
  import { loadFurnaceConfig } from './furnace-config.js';
8
8
  import { getLicenseHeader, hasAnyLicenseHeader } from './license-headers.js';
9
+ import { runCheckJs } from './patch-lint-checkjs.js';
9
10
  import { detectNewFilesInDiff, extractAddedLinesPerFile } from './patch-lint-diff.js';
11
+ import { validateExportJsDoc } from './patch-lint-jsdoc.js';
12
+ import { resolvePatchOwnedSysMjs } from './patch-lint-ownership.js';
10
13
  // ---------------------------------------------------------------------------
11
14
  // Cross-patch lint re-exports
12
15
  // ---------------------------------------------------------------------------
@@ -16,8 +19,11 @@ import { detectNewFilesInDiff, extractAddedLinesPerFile } from './patch-lint-dif
16
19
  // `patch-lint-cross.ts` so the per-patch and cross-patch rule bodies can
17
20
  // each stay within the project's per-file line budget. Re-export the
18
21
  // public surface so callers continue to import from a single module.
22
+ export { runCheckJs } from './patch-lint-checkjs.js';
19
23
  export { buildPatchQueueContext, collectNewFileCreatorsByPath, extractImportSpecifiers, extractImportSpecifiersWithLines, findForwardImportIgnoreLines, FORWARD_IMPORT_IGNORE_MARKER, isForwardImportableFile, lintPatchQueue, lintPatchQueueDuplicateCreations, lintPatchQueueForwardImports, } from './patch-lint-cross.js';
20
24
  export { buildModifiedFileAdditionsFromDiff, detectNewFilesInDiff } from './patch-lint-diff.js';
25
+ export { validateExportJsDoc } from './patch-lint-jsdoc.js';
26
+ export { resolvePatchOwnedSysMjs } from './patch-lint-ownership.js';
21
27
  // ---------------------------------------------------------------------------
22
28
  // Helpers
23
29
  // ---------------------------------------------------------------------------
@@ -156,9 +162,10 @@ export async function lintNewFileHeaders(repoDir, newFiles, config) {
156
162
  * @param affectedFiles - File paths (relative to repoDir)
157
163
  * @param newFiles - Set of files that are newly created in this patch
158
164
  * @param config - Project configuration
165
+ * @param patchOwnedFiles - Optional set of patch-owned `.sys.mjs` paths for scoped JSDoc enforcement
159
166
  * @returns Array of lint issues
160
167
  */
161
- export async function lintPatchedJs(repoDir, affectedFiles, newFiles, config) {
168
+ export async function lintPatchedJs(repoDir, affectedFiles, newFiles, config, patchOwnedFiles) {
162
169
  const jsFiles = affectedFiles.filter(isJsFile);
163
170
  if (jsFiles.length === 0)
164
171
  return [];
@@ -195,32 +202,17 @@ export async function lintPatchedJs(repoDir, affectedFiles, newFiles, config) {
195
202
  });
196
203
  }
197
204
  }
198
- // 3. JSDoc on exports (new .sys.mjs files only)
199
- if (isNew && isSysMjs) {
200
- const lines = content.split('\n');
201
- for (let i = 0; i < lines.length; i++) {
202
- const line = lines[i] ?? '';
203
- if (/^export\s+(function|class|const|let|var)\s/.test(line)) {
204
- // Walk backwards to find JSDoc
205
- let hasJsDoc = false;
206
- for (let j = i - 1; j >= 0; j--) {
207
- const prev = (lines[j] ?? '').trim();
208
- if (prev === '')
209
- continue;
210
- if (prev.endsWith('*/')) {
211
- hasJsDoc = true;
212
- }
213
- break;
214
- }
215
- if (!hasJsDoc) {
216
- issues.push({
217
- file,
218
- check: 'missing-jsdoc',
219
- message: `Export at line ${i + 1} is missing a JSDoc comment with @param/@returns.`,
220
- severity: 'warning',
221
- });
222
- }
223
- }
205
+ // 3. JSDoc on exports (patch-owned .sys.mjs files)
206
+ const isOwned = patchOwnedFiles ? patchOwnedFiles.has(file) : isNew;
207
+ if (isOwned && isSysMjs) {
208
+ const jsdocIssues = validateExportJsDoc(content);
209
+ for (const jsdocIssue of jsdocIssues) {
210
+ issues.push({
211
+ file,
212
+ check: jsdocIssue.check,
213
+ message: jsdocIssue.message,
214
+ severity: 'error',
215
+ });
224
216
  }
225
217
  }
226
218
  // 4. Observer topic naming
@@ -346,20 +338,22 @@ export async function lintModifiedFileHeaders(repoDir, affectedFiles, newFiles)
346
338
  * @param affectedFiles - File paths (relative to repoDir) affected by the patch
347
339
  * @param diffContent - Raw unified diff string
348
340
  * @param config - Project configuration
341
+ * @param patchQueueCtx - Optional cross-patch context for ownership resolution
349
342
  * @returns Array of all lint issues found
350
343
  */
351
- export async function lintExportedPatch(repoDir, affectedFiles, diffContent, config) {
344
+ export async function lintExportedPatch(repoDir, affectedFiles, diffContent, config, patchQueueCtx) {
352
345
  const newFiles = detectNewFilesInDiff(diffContent);
353
346
  const lineCount = diffContent.split('\n').length;
347
+ const patchOwnedFiles = resolvePatchOwnedSysMjs(newFiles, patchQueueCtx);
354
348
  const [cssIssues, headerIssues, jsIssues, modifiedHeaderIssues] = await Promise.all([
355
349
  lintPatchedCss(repoDir, affectedFiles, diffContent),
356
350
  lintNewFileHeaders(repoDir, [...newFiles], config),
357
- lintPatchedJs(repoDir, affectedFiles, newFiles, config),
351
+ lintPatchedJs(repoDir, affectedFiles, newFiles, config, patchOwnedFiles),
358
352
  lintModifiedFileHeaders(repoDir, affectedFiles, newFiles),
359
353
  ]);
360
354
  const modCommentIssues = lintModificationComments(diffContent, config);
361
355
  const sizeIssues = lintPatchSize(affectedFiles, lineCount);
362
- return [
356
+ const issues = [
363
357
  ...sizeIssues,
364
358
  ...cssIssues,
365
359
  ...headerIssues,
@@ -367,5 +361,11 @@ export async function lintExportedPatch(repoDir, affectedFiles, diffContent, con
367
361
  ...jsIssues,
368
362
  ...modCommentIssues,
369
363
  ];
364
+ // Optional checkJs pass — only when explicitly enabled in config
365
+ if (config.patchLint?.checkJs) {
366
+ const checkJsIssues = await runCheckJs(repoDir, patchOwnedFiles);
367
+ issues.push(...checkJsIssues);
368
+ }
369
+ return issues;
370
370
  }
371
371
  //# sourceMappingURL=patch-lint.js.map
@@ -42,6 +42,8 @@ export interface FireForgeConfig {
42
42
  license?: ProjectLicense;
43
43
  /** Wire command configuration */
44
44
  wire?: WireConfig;
45
+ /** Patch lint configuration */
46
+ patchLint?: PatchLintConfig;
45
47
  }
46
48
  /**
47
49
  * Wire command configuration.
@@ -50,6 +52,13 @@ export interface WireConfig {
50
52
  /** Subscript directory relative to engine/. Default: "browser/base/content" */
51
53
  subscriptDir?: string;
52
54
  }
55
+ /**
56
+ * Configuration for patch lint rules.
57
+ */
58
+ export interface PatchLintConfig {
59
+ /** Enable TypeScript checkJs pass on patch-owned .sys.mjs files */
60
+ checkJs?: boolean;
61
+ }
53
62
  /**
54
63
  * Build mode for mach.
55
64
  */
@@ -12,6 +12,8 @@ export function isExplicitAbsolutePath(path) {
12
12
  }
13
13
  /** Resolves a candidate path and returns whether it stays within the given root. */
14
14
  export function isPathInsideRoot(root, candidate) {
15
+ if (candidate.includes('\0'))
16
+ return false;
15
17
  const resolvedRoot = resolve(root);
16
18
  const resolvedCandidate = isExplicitAbsolutePath(candidate)
17
19
  ? resolve(candidate)
@@ -24,7 +26,7 @@ export function isPathInsideRoot(root, candidate) {
24
26
  }
25
27
  /** Checks whether a relative path stays contained within an arbitrary root. */
26
28
  export function isContainedRelativePath(path) {
27
- if (isExplicitAbsolutePath(path)) {
29
+ if (isExplicitAbsolutePath(path) || path.includes('\0')) {
28
30
  return false;
29
31
  }
30
32
  return isPathInsideRoot(RELATIVE_PATH_ROOT, path);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hominis/fireforge",
3
- "version": "0.11.2",
3
+ "version": "0.12.0",
4
4
  "description": "FireForge — a build tool for customizing Firefox",
5
5
  "type": "module",
6
6
  "main": "./dist/src/index.js",