@hominis/fireforge 0.44.4 → 0.46.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 (378) hide show
  1. package/CHANGELOG.md +120 -0
  2. package/dist/bin/fireforge.js +6 -0
  3. package/dist/build-info.json +4 -4
  4. package/dist/src/cli-usage-error.d.ts +46 -0
  5. package/dist/src/cli-usage-error.js +81 -0
  6. package/dist/src/cli.d.ts +5 -1
  7. package/dist/src/cli.js +20 -21
  8. package/dist/src/commands/bootstrap.js +1 -2
  9. package/dist/src/commands/build.js +15 -6
  10. package/dist/src/commands/destructive-decision.d.ts +19 -0
  11. package/dist/src/commands/destructive-decision.js +31 -0
  12. package/dist/src/commands/doctor-check-core.d.ts +0 -6
  13. package/dist/src/commands/doctor-check-core.js +0 -8
  14. package/dist/src/commands/doctor-orphaned-harness.d.ts +0 -6
  15. package/dist/src/commands/doctor-orphaned-harness.js +4 -12
  16. package/dist/src/commands/doctor-post-rebase-audit.js +1 -1
  17. package/dist/src/commands/doctor-source-pin.d.ts +29 -0
  18. package/dist/src/commands/doctor-source-pin.js +81 -0
  19. package/dist/src/commands/doctor.d.ts +2 -0
  20. package/dist/src/commands/doctor.js +9 -8
  21. package/dist/src/commands/download.js +35 -24
  22. package/dist/src/commands/export-all.js +21 -4
  23. package/dist/src/commands/export-flow.js +15 -60
  24. package/dist/src/commands/export-placement-conflicts.js +3 -3
  25. package/dist/src/commands/export-placement-gate.js +2 -8
  26. package/dist/src/commands/export-placement-policy.js +3 -5
  27. package/dist/src/commands/export-shared.d.ts +42 -23
  28. package/dist/src/commands/export-shared.js +4 -19
  29. package/dist/src/commands/export.js +10 -1
  30. package/dist/src/commands/furnace/authoring-config.d.ts +13 -0
  31. package/dist/src/commands/furnace/authoring-config.js +19 -0
  32. package/dist/src/commands/furnace/chrome-doc-remove.js +1 -1
  33. package/dist/src/commands/furnace/create-browser-test.js +3 -8
  34. package/dist/src/commands/furnace/create-dry-run.js +2 -5
  35. package/dist/src/commands/furnace/create-templates.d.ts +17 -1
  36. package/dist/src/commands/furnace/create-templates.js +2 -1
  37. package/dist/src/commands/furnace/create.js +25 -19
  38. package/dist/src/commands/furnace/deploy.js +11 -2
  39. package/dist/src/commands/furnace/init.js +2 -1
  40. package/dist/src/commands/furnace/override.js +28 -19
  41. package/dist/src/commands/furnace/refresh.js +9 -1
  42. package/dist/src/commands/furnace/remove-mutations.d.ts +6 -7
  43. package/dist/src/commands/furnace/remove-mutations.js +0 -1
  44. package/dist/src/commands/furnace/remove.js +2 -6
  45. package/dist/src/commands/furnace/rename-browser-test.js +1 -7
  46. package/dist/src/commands/furnace/rename.js +29 -29
  47. package/dist/src/commands/furnace/status.js +12 -9
  48. package/dist/src/commands/furnace/test-file-name.d.ts +28 -0
  49. package/dist/src/commands/furnace/test-file-name.js +39 -0
  50. package/dist/src/commands/furnace/validation-output.d.ts +21 -9
  51. package/dist/src/commands/furnace/validation-output.js +3 -8
  52. package/dist/src/commands/import.js +2 -9
  53. package/dist/src/commands/lint-per-patch.d.ts +2 -2
  54. package/dist/src/commands/lint-per-patch.js +19 -4
  55. package/dist/src/commands/lint-per-run-checkjs.d.ts +2 -2
  56. package/dist/src/commands/lint.d.ts +2 -1
  57. package/dist/src/commands/lint.js +1 -1
  58. package/dist/src/commands/manifest.js +1 -1
  59. package/dist/src/commands/package.js +2 -4
  60. package/dist/src/commands/patch/compact.js +14 -15
  61. package/dist/src/commands/patch/delete.js +15 -25
  62. package/dist/src/commands/patch/entry-projection.d.ts +13 -0
  63. package/dist/src/commands/patch/entry-projection.js +24 -0
  64. package/dist/src/commands/patch/lint-ignore.d.ts +0 -8
  65. package/dist/src/commands/patch/lint-ignore.js +19 -29
  66. package/dist/src/commands/patch/move-files-create.js +24 -10
  67. package/dist/src/commands/patch/move-files-into.js +38 -37
  68. package/dist/src/commands/patch/move-files.js +4 -13
  69. package/dist/src/commands/patch/rename.js +27 -41
  70. package/dist/src/commands/patch/reorder.js +25 -38
  71. package/dist/src/commands/patch/split-plan.d.ts +4 -5
  72. package/dist/src/commands/patch/split-plan.js +4 -11
  73. package/dist/src/commands/patch/split.js +45 -43
  74. package/dist/src/commands/patch/staged-dependency.js +16 -22
  75. package/dist/src/commands/patch/tier.js +15 -25
  76. package/dist/src/commands/patch-rename-map.d.ts +19 -0
  77. package/dist/src/commands/patch-rename-map.js +36 -0
  78. package/dist/src/commands/pending-resolution.d.ts +15 -0
  79. package/dist/src/commands/pending-resolution.js +25 -0
  80. package/dist/src/commands/re-export-adjacent.d.ts +32 -2
  81. package/dist/src/commands/re-export-adjacent.js +51 -10
  82. package/dist/src/commands/re-export-bulk-scan.js +6 -10
  83. package/dist/src/commands/re-export-drift.d.ts +8 -0
  84. package/dist/src/commands/re-export-drift.js +65 -4
  85. package/dist/src/commands/re-export-files.d.ts +2 -3
  86. package/dist/src/commands/re-export-files.js +38 -32
  87. package/dist/src/commands/re-export-lint.d.ts +3 -4
  88. package/dist/src/commands/re-export-lint.js +9 -2
  89. package/dist/src/commands/re-export-options.js +19 -5
  90. package/dist/src/commands/re-export-register.js +5 -1
  91. package/dist/src/commands/re-export-scan.js +8 -8
  92. package/dist/src/commands/re-export-single.d.ts +22 -3
  93. package/dist/src/commands/re-export-single.js +19 -11
  94. package/dist/src/commands/re-export.js +44 -27
  95. package/dist/src/commands/rebase/abort.js +3 -8
  96. package/dist/src/commands/rebase/continue.js +12 -12
  97. package/dist/src/commands/rebase/index.js +2 -2
  98. package/dist/src/commands/rebase/patch-loop.d.ts +2 -2
  99. package/dist/src/commands/rebase/patch-loop.js +2 -7
  100. package/dist/src/commands/resolve.js +18 -10
  101. package/dist/src/commands/run.d.ts +0 -13
  102. package/dist/src/commands/run.js +12 -25
  103. package/dist/src/commands/setup-support.d.ts +16 -4
  104. package/dist/src/commands/setup-support.js +16 -16
  105. package/dist/src/commands/setup.js +3 -9
  106. package/dist/src/commands/source.js +8 -1
  107. package/dist/src/commands/status-check.js +2 -2
  108. package/dist/src/commands/status-output.js +2 -2
  109. package/dist/src/commands/status.js +3 -4
  110. package/dist/src/commands/test-build-phase.d.ts +2 -2
  111. package/dist/src/commands/test-build-phase.js +33 -8
  112. package/dist/src/commands/test-diagnose.js +14 -5
  113. package/dist/src/commands/test-modes.d.ts +2 -2
  114. package/dist/src/commands/test-modes.js +0 -1
  115. package/dist/src/commands/test-register.js +6 -3
  116. package/dist/src/commands/test-run.js +29 -21
  117. package/dist/src/commands/test-stale-gate.js +8 -5
  118. package/dist/src/commands/test-verdict.d.ts +16 -2
  119. package/dist/src/commands/test-verdict.js +18 -3
  120. package/dist/src/commands/test.js +51 -7
  121. package/dist/src/commands/token-list.d.ts +26 -0
  122. package/dist/src/commands/token-list.js +153 -0
  123. package/dist/src/commands/token.js +17 -1
  124. package/dist/src/commands/tree.js +7 -2
  125. package/dist/src/commands/typecheck.js +9 -9
  126. package/dist/src/commands/wire.js +4 -3
  127. package/dist/src/core/branding.d.ts +0 -7
  128. package/dist/src/core/branding.js +3 -2
  129. package/dist/src/core/browser-wire.js +6 -3
  130. package/dist/src/core/build-audit-platform.d.ts +29 -0
  131. package/dist/src/core/build-audit-platform.js +140 -0
  132. package/dist/src/core/build-audit-registration.js +3 -2
  133. package/dist/src/core/build-audit-skip.d.ts +57 -0
  134. package/dist/src/core/build-audit-skip.js +112 -0
  135. package/dist/src/core/build-audit.d.ts +15 -2
  136. package/dist/src/core/build-audit.js +154 -12
  137. package/dist/src/core/build-baseline.d.ts +45 -28
  138. package/dist/src/core/build-baseline.js +6 -29
  139. package/dist/src/core/build-jar-escalation.d.ts +64 -0
  140. package/dist/src/core/build-jar-escalation.js +128 -0
  141. package/dist/src/core/build-overwrite-guard.js +1 -1
  142. package/dist/src/core/build-prepare.d.ts +5 -2
  143. package/dist/src/core/build-prepare.js +34 -6
  144. package/dist/src/core/config-paths.d.ts +2 -6
  145. package/dist/src/core/config-paths.js +4 -2
  146. package/dist/src/core/config-state.d.ts +0 -6
  147. package/dist/src/core/config-state.js +1 -19
  148. package/dist/src/core/config-validate-build-audit.d.ts +8 -0
  149. package/dist/src/core/config-validate-build-audit.js +80 -0
  150. package/dist/src/core/config-validate-file-size.d.ts +12 -0
  151. package/dist/src/core/config-validate-file-size.js +65 -0
  152. package/dist/src/core/config-validate-patch-policy.d.ts +2 -2
  153. package/dist/src/core/config-validate-patch-policy.js +3 -3
  154. package/dist/src/core/config-validate-test-toolchains.d.ts +2 -2
  155. package/dist/src/core/config-validate-test-toolchains.js +4 -5
  156. package/dist/src/core/config-validate.js +20 -3
  157. package/dist/src/core/config.d.ts +2 -2
  158. package/dist/src/core/config.js +11 -15
  159. package/dist/src/core/coverage-extend.js +4 -4
  160. package/dist/src/core/discard-baseline.js +7 -7
  161. package/dist/src/core/engine-precondition.d.ts +1 -9
  162. package/dist/src/core/engine-precondition.js +3 -8
  163. package/dist/src/core/engine-session-lock.js +13 -17
  164. package/dist/src/core/export-moved-code-shape.js +2 -2
  165. package/dist/src/core/file-lock-owner.d.ts +103 -0
  166. package/dist/src/core/file-lock-owner.js +259 -0
  167. package/dist/src/core/file-lock.d.ts +2 -7
  168. package/dist/src/core/file-lock.js +40 -91
  169. package/dist/src/core/firefox-cache.d.ts +13 -1
  170. package/dist/src/core/firefox-cache.js +59 -34
  171. package/dist/src/core/firefox-download.d.ts +15 -0
  172. package/dist/src/core/firefox-download.js +37 -21
  173. package/dist/src/core/firefox-extract.d.ts +6 -5
  174. package/dist/src/core/firefox-extract.js +36 -18
  175. package/dist/src/core/firefox.d.ts +30 -32
  176. package/dist/src/core/firefox.js +5 -36
  177. package/dist/src/core/furnace-apply-dry-run.d.ts +1 -6
  178. package/dist/src/core/furnace-apply-dry-run.js +1 -7
  179. package/dist/src/core/furnace-apply-helpers.d.ts +0 -11
  180. package/dist/src/core/furnace-apply-helpers.js +8 -19
  181. package/dist/src/core/furnace-apply-overwrite-warn.js +4 -2
  182. package/dist/src/core/furnace-apply.d.ts +1 -1
  183. package/dist/src/core/furnace-apply.js +30 -10
  184. package/dist/src/core/furnace-config.d.ts +1 -1
  185. package/dist/src/core/furnace-config.js +23 -36
  186. package/dist/src/core/furnace-constants.js +3 -2
  187. package/dist/src/core/furnace-dir-entry.d.ts +20 -0
  188. package/dist/src/core/furnace-dir-entry.js +21 -0
  189. package/dist/src/core/furnace-operation.d.ts +2 -0
  190. package/dist/src/core/furnace-operation.js +3 -1
  191. package/dist/src/core/furnace-refresh.d.ts +18 -6
  192. package/dist/src/core/furnace-refresh.js +3 -6
  193. package/dist/src/core/furnace-registration-ast.d.ts +0 -1
  194. package/dist/src/core/furnace-registration-ast.js +0 -3
  195. package/dist/src/core/furnace-registration.d.ts +2 -1
  196. package/dist/src/core/furnace-registration.js +22 -29
  197. package/dist/src/core/furnace-scanner.js +3 -6
  198. package/dist/src/core/furnace-state-persist.d.ts +2 -2
  199. package/dist/src/core/furnace-validate-registration.js +5 -7
  200. package/dist/src/core/git-base.d.ts +3 -1
  201. package/dist/src/core/git-base.js +25 -3
  202. package/dist/src/core/git-diff-new-file.d.ts +40 -0
  203. package/dist/src/core/git-diff-new-file.js +119 -0
  204. package/dist/src/core/git-diff.d.ts +9 -9
  205. package/dist/src/core/git-diff.js +88 -157
  206. package/dist/src/core/git-file-ops.d.ts +0 -7
  207. package/dist/src/core/git-file-ops.js +4 -28
  208. package/dist/src/core/git-readonly-index.d.ts +0 -2
  209. package/dist/src/core/git-readonly-index.js +0 -4
  210. package/dist/src/core/git-status.d.ts +7 -0
  211. package/dist/src/core/git-status.js +21 -9
  212. package/dist/src/core/git.d.ts +0 -6
  213. package/dist/src/core/git.js +14 -43
  214. package/dist/src/core/harness-orphans.d.ts +73 -0
  215. package/dist/src/core/harness-orphans.js +187 -0
  216. package/dist/src/core/history-log.d.ts +19 -0
  217. package/dist/src/core/history-log.js +29 -0
  218. package/dist/src/core/mach-error-hints.d.ts +0 -5
  219. package/dist/src/core/mach-error-hints.js +1 -1
  220. package/dist/src/core/mach-known-noise-filter.d.ts +25 -3
  221. package/dist/src/core/mach-known-noise-filter.js +31 -10
  222. package/dist/src/core/mach-mozconfig.js +2 -1
  223. package/dist/src/core/mach-python.d.ts +2 -0
  224. package/dist/src/core/mach-python.js +12 -7
  225. package/dist/src/core/mach-resource-shim.d.ts +13 -1
  226. package/dist/src/core/mach-resource-shim.js +112 -9
  227. package/dist/src/core/mach.d.ts +46 -53
  228. package/dist/src/core/mach.js +31 -83
  229. package/dist/src/core/marionette-port.d.ts +0 -17
  230. package/dist/src/core/marionette-port.js +12 -58
  231. package/dist/src/core/marionette-preflight.js +9 -27
  232. package/dist/src/core/mochitest-server-port.js +3 -3
  233. package/dist/src/core/moz-manifest-rules.js +37 -33
  234. package/dist/src/core/patch-apply-fuzz.js +4 -4
  235. package/dist/src/core/patch-apply.d.ts +4 -5
  236. package/dist/src/core/patch-apply.js +55 -32
  237. package/dist/src/core/patch-export-coverage.d.ts +0 -9
  238. package/dist/src/core/patch-export-coverage.js +1 -30
  239. package/dist/src/core/patch-export-update.d.ts +18 -1
  240. package/dist/src/core/patch-export-update.js +4 -3
  241. package/dist/src/core/patch-export.d.ts +11 -21
  242. package/dist/src/core/patch-export.js +44 -66
  243. package/dist/src/core/patch-files.d.ts +0 -4
  244. package/dist/src/core/patch-files.js +0 -11
  245. package/dist/src/core/patch-lint-binary.d.ts +6 -12
  246. package/dist/src/core/patch-lint-cache.d.ts +17 -1
  247. package/dist/src/core/patch-lint-cache.js +3 -6
  248. package/dist/src/core/patch-lint-checkjs.d.ts +33 -16
  249. package/dist/src/core/patch-lint-checkjs.js +43 -25
  250. package/dist/src/core/patch-lint-creators.d.ts +2 -6
  251. package/dist/src/core/patch-lint-cross.d.ts +16 -8
  252. package/dist/src/core/patch-lint-cross.js +49 -92
  253. package/dist/src/core/patch-lint-css.js +10 -6
  254. package/dist/src/core/patch-lint-file-size.d.ts +52 -0
  255. package/dist/src/core/patch-lint-file-size.js +53 -0
  256. package/dist/src/core/patch-lint-forward-registration.d.ts +58 -0
  257. package/dist/src/core/patch-lint-forward-registration.js +210 -0
  258. package/dist/src/core/patch-lint-jsdoc.js +36 -36
  259. package/dist/src/core/patch-lint-module-registration.d.ts +2 -8
  260. package/dist/src/core/patch-lint-prettier.d.ts +36 -0
  261. package/dist/src/core/patch-lint-prettier.js +126 -0
  262. package/dist/src/core/patch-lint-queue-types.d.ts +60 -0
  263. package/dist/src/core/patch-lint-queue-types.js +12 -0
  264. package/dist/src/core/patch-lint-staged-registration.d.ts +61 -0
  265. package/dist/src/core/patch-lint-staged-registration.js +90 -0
  266. package/dist/src/core/patch-lint-unmanaged-companion.d.ts +106 -0
  267. package/dist/src/core/patch-lint-unmanaged-companion.js +214 -0
  268. package/dist/src/core/patch-lint.d.ts +14 -9
  269. package/dist/src/core/patch-lint.js +27 -27
  270. package/dist/src/core/patch-lock.js +3 -3
  271. package/dist/src/core/patch-manifest-io.js +8 -7
  272. package/dist/src/core/patch-parse.d.ts +19 -8
  273. package/dist/src/core/patch-parse.js +25 -0
  274. package/dist/src/core/patch-policy.js +15 -27
  275. package/dist/src/core/patch-source-metadata.d.ts +1 -3
  276. package/dist/src/core/patch-source-metadata.js +0 -4
  277. package/dist/src/core/patch-transform.d.ts +21 -8
  278. package/dist/src/core/patch-transform.js +38 -10
  279. package/dist/src/core/process-cpu-time.js +1 -1
  280. package/dist/src/core/rebase-session.d.ts +2 -8
  281. package/dist/src/core/rebase-session.js +5 -17
  282. package/dist/src/core/register-browser-content.js +3 -2
  283. package/dist/src/core/register-browser-test.js +2 -1
  284. package/dist/src/core/register-module.js +2 -1
  285. package/dist/src/core/register-scaffold.js +5 -4
  286. package/dist/src/core/register-shared-css.js +2 -1
  287. package/dist/src/core/run-log-redact.d.ts +38 -0
  288. package/dist/src/core/run-log-redact.js +69 -0
  289. package/dist/src/core/run-log.d.ts +13 -2
  290. package/dist/src/core/run-log.js +67 -18
  291. package/dist/src/core/signal-critical.js +2 -4
  292. package/dist/src/core/smoke-patterns.d.ts +0 -1
  293. package/dist/src/core/smoke-patterns.js +18 -11
  294. package/dist/src/core/state-file.d.ts +12 -1
  295. package/dist/src/core/state-file.js +14 -1
  296. package/dist/src/core/status-classify.js +2 -1
  297. package/dist/src/core/test-changed-prefs.js +26 -11
  298. package/dist/src/core/test-harness-crash.js +61 -48
  299. package/dist/src/core/test-harness-verdict-notes.d.ts +55 -0
  300. package/dist/src/core/test-harness-verdict-notes.js +48 -0
  301. package/dist/src/core/test-stale-check.d.ts +23 -8
  302. package/dist/src/core/test-stale-check.js +77 -4
  303. package/dist/src/core/test-stall-triage.js +37 -19
  304. package/dist/src/core/test-xpcshell-retry.d.ts +26 -7
  305. package/dist/src/core/test-xpcshell-retry.js +14 -11
  306. package/dist/src/core/token-category.d.ts +33 -0
  307. package/dist/src/core/token-category.js +56 -48
  308. package/dist/src/core/token-dark-mode.js +2 -14
  309. package/dist/src/core/token-inventory.d.ts +56 -0
  310. package/dist/src/core/token-inventory.js +109 -0
  311. package/dist/src/core/token-manager.d.ts +1 -1
  312. package/dist/src/core/token-manager.js +1 -1
  313. package/dist/src/core/token-variant.js +21 -22
  314. package/dist/src/core/toolchain-preflight.js +1 -1
  315. package/dist/src/core/tree-cow.d.ts +0 -2
  316. package/dist/src/core/tree-cow.js +2 -2
  317. package/dist/src/core/tree-store.js +4 -5
  318. package/dist/src/core/typecheck.js +2 -2
  319. package/dist/src/core/wire-dom-fragment.js +6 -17
  320. package/dist/src/core/wire-utils.js +5 -17
  321. package/dist/src/core/xpcshell-appdir.js +2 -4
  322. package/dist/src/errors/base.d.ts +53 -1
  323. package/dist/src/errors/base.js +79 -11
  324. package/dist/src/errors/build.d.ts +28 -0
  325. package/dist/src/errors/build.js +51 -10
  326. package/dist/src/errors/config.d.ts +0 -14
  327. package/dist/src/errors/config.js +13 -34
  328. package/dist/src/errors/download.d.ts +13 -0
  329. package/dist/src/errors/download.js +53 -24
  330. package/dist/src/errors/furnace.js +8 -9
  331. package/dist/src/errors/git.d.ts +8 -8
  332. package/dist/src/errors/git.js +20 -30
  333. package/dist/src/errors/patch.js +8 -10
  334. package/dist/src/errors/rebase.js +7 -7
  335. package/dist/src/types/commands/options.d.ts +96 -123
  336. package/dist/src/types/commands/patches.d.ts +17 -6
  337. package/dist/src/types/config.d.ts +75 -2
  338. package/dist/src/types/index.d.ts +0 -1
  339. package/dist/src/utils/assert.d.ts +3 -15
  340. package/dist/src/utils/assert.js +1 -18
  341. package/dist/src/utils/build-info.d.ts +1 -1
  342. package/dist/src/utils/child-env.d.ts +2 -1
  343. package/dist/src/utils/elapsed.js +10 -1
  344. package/dist/src/utils/fs.d.ts +4 -2
  345. package/dist/src/utils/fs.js +11 -14
  346. package/dist/src/utils/hash.d.ts +12 -0
  347. package/dist/src/utils/hash.js +16 -0
  348. package/dist/src/utils/line-dispatch.d.ts +15 -0
  349. package/dist/src/utils/line-dispatch.js +48 -0
  350. package/dist/src/utils/options.d.ts +1 -7
  351. package/dist/src/utils/options.js +6 -7
  352. package/dist/src/utils/package-root.js +3 -12
  353. package/dist/src/utils/platform.d.ts +0 -4
  354. package/dist/src/utils/process-group.d.ts +2 -1
  355. package/dist/src/utils/process-group.js +20 -19
  356. package/dist/src/utils/process.d.ts +40 -26
  357. package/dist/src/utils/process.js +115 -166
  358. package/dist/src/utils/ps-duration.js +3 -3
  359. package/dist/src/utils/sleep.d.ts +26 -0
  360. package/dist/src/utils/sleep.js +31 -0
  361. package/dist/src/utils/validation.d.ts +2 -38
  362. package/dist/src/utils/validation.js +7 -16
  363. package/docs/configuration.md +136 -1
  364. package/docs/exit-codes.md +34 -20
  365. package/docs/lifecycle-invariants.md +18 -12
  366. package/docs/machine-output.md +31 -8
  367. package/docs/patch-workflow.md +2 -1
  368. package/docs/run-logs.md +44 -4
  369. package/docs/testing.md +70 -11
  370. package/package.json +8 -7
  371. package/dist/src/commands/rebase.d.ts +0 -4
  372. package/dist/src/commands/rebase.js +0 -5
  373. package/dist/src/core/patch-artifact-normalize.d.ts +0 -21
  374. package/dist/src/core/patch-artifact-normalize.js +0 -24
  375. package/dist/src/core/patch-lint-reexports.d.ts +0 -7
  376. package/dist/src/core/patch-lint-reexports.js +0 -8
  377. package/dist/src/core/wire-targets.d.ts +0 -7
  378. package/dist/src/core/wire-targets.js +0 -8
@@ -0,0 +1,153 @@
1
+ // SPDX-License-Identifier: EUPL-1.2
2
+ /**
3
+ * `token list` and `token show` — the read half of design-token management.
4
+ *
5
+ * `token add` requires `--category` and refuses an unknown one, but nothing
6
+ * reported the categories a project has, so naming one meant hand-parsing a
7
+ * neighbouring `= Category =` banner out of the tokens CSS. Both commands
8
+ * read through `token-inventory.ts`, which is built on the same banner and
9
+ * `:root`-bounds primitives `token add` and `token coverage` use — a second
10
+ * tokens-CSS parser would let the report and the writer disagree about
11
+ * which section a token lives in.
12
+ *
13
+ * Helper module: no registrar, called from `token.ts`.
14
+ */
15
+ import { join } from 'node:path';
16
+ import { getProjectPaths, loadConfig } from '../core/config.js';
17
+ import { collectTokenBlockValues, collectTokenInventory, } from '../core/token-inventory.js';
18
+ import { getTokensCssPath } from '../core/token-manager.js';
19
+ import { GeneralError } from '../errors/base.js';
20
+ import { toError } from '../utils/errors.js';
21
+ import { pathExists, readText } from '../utils/fs.js';
22
+ import { info, intro, outro, setMachineOutputMode, setStdoutSealed } from '../utils/logger.js';
23
+ import { emitMachineError, MACHINE_OUTPUT_SCHEMA_VERSION } from '../utils/machine-output.js';
24
+ /** Reads the project's tokens CSS as lines, refusing when it is absent. */
25
+ async function readTokensCssLines(projectRoot) {
26
+ const paths = getProjectPaths(projectRoot);
27
+ const config = await loadConfig(projectRoot);
28
+ const tokensCssPath = getTokensCssPath(config.binaryName);
29
+ const filePath = join(paths.engine, tokensCssPath);
30
+ if (!(await pathExists(filePath))) {
31
+ throw new GeneralError(`Token CSS file not found: ${tokensCssPath}. Run "fireforge furnace init" to seed it.`);
32
+ }
33
+ return { lines: (await readText(filePath)).split('\n'), tokensCssPath };
34
+ }
35
+ /** Applies the `--category` filter, refusing a name the file does not carry. */
36
+ function filterByCategory(groups, category, tokensCssPath) {
37
+ const matched = groups.filter((group) => group.category === category);
38
+ if (matched.length > 0)
39
+ return matched;
40
+ // Same "name the alternatives" contract `token add` gives for an unknown
41
+ // category — a filter that silently prints nothing reads as "this
42
+ // category is empty", which is a different fact.
43
+ const available = groups
44
+ .map((group) => group.category)
45
+ .filter((name) => name !== null);
46
+ const suffix = available.length > 0
47
+ ? `Available categories: ${available.map((name) => `"${name}"`).join(', ')}.`
48
+ : 'The file declares no category banners.';
49
+ throw new GeneralError(`Category "${category}" not found in ${tokensCssPath}.\n\n${suffix}`);
50
+ }
51
+ /**
52
+ * Lists the categories declared in the tokens CSS with their token names,
53
+ * in file order.
54
+ *
55
+ * @param projectRoot - Root directory of the project
56
+ * @param options - Filter and output-mode options
57
+ */
58
+ export async function tokenListCommand(projectRoot, options = {}) {
59
+ if (options.json === true) {
60
+ // stdout belongs exclusively to the payload from here on; a later
61
+ // diagnostic (including withErrorHandling's) routes to stderr.
62
+ setMachineOutputMode(true);
63
+ try {
64
+ const { lines, tokensCssPath } = await readTokensCssLines(projectRoot);
65
+ const all = collectTokenInventory(lines);
66
+ const groups = options.category === undefined
67
+ ? all
68
+ : filterByCategory(all, options.category, tokensCssPath);
69
+ process.stdout.write(`${JSON.stringify({
70
+ schemaVersion: MACHINE_OUTPUT_SCHEMA_VERSION,
71
+ tokensCssPath,
72
+ categories: groups.map((group) => ({
73
+ category: group.category,
74
+ tokens: group.tokens.map((token) => ({
75
+ name: token.name,
76
+ line: token.line,
77
+ value: token.value,
78
+ })),
79
+ })),
80
+ }, null, 2)}\n`);
81
+ setStdoutSealed(true);
82
+ }
83
+ catch (error) {
84
+ emitMachineError('token-list-failed', toError(error).message);
85
+ }
86
+ finally {
87
+ setMachineOutputMode(false);
88
+ }
89
+ return;
90
+ }
91
+ const { lines, tokensCssPath } = await readTokensCssLines(projectRoot);
92
+ const all = collectTokenInventory(lines);
93
+ const groups = options.category === undefined ? all : filterByCategory(all, options.category, tokensCssPath);
94
+ intro('Design Tokens');
95
+ info(tokensCssPath);
96
+ if (groups.length === 0) {
97
+ outro('No token declarations found in the :root block');
98
+ return;
99
+ }
100
+ let total = 0;
101
+ for (const group of groups) {
102
+ info(` ${group.category ?? '(no category)'} — ${String(group.tokens.length)} token(s)`);
103
+ for (const token of group.tokens) {
104
+ info(` ${token.name}`);
105
+ }
106
+ total += group.tokens.length;
107
+ }
108
+ outro(`${String(groups.length)} category/categories, ${String(total)} token(s)`);
109
+ }
110
+ /**
111
+ * Reports one token: the category that owns it and the value it takes in
112
+ * every block that declares it.
113
+ *
114
+ * @param projectRoot - Root directory of the project
115
+ * @param tokenName - Custom-property name, with or without the leading `--`
116
+ * @param options - Output-mode options
117
+ */
118
+ export async function tokenShowCommand(projectRoot, tokenName, options = {}) {
119
+ const emit = async () => {
120
+ const { lines, tokensCssPath } = await readTokensCssLines(projectRoot);
121
+ const name = tokenName.startsWith('--') ? tokenName : `--${tokenName}`;
122
+ const declarations = collectTokenBlockValues(lines, name);
123
+ if (declarations.length === 0) {
124
+ throw new GeneralError(`Token "${name}" is not declared in ${tokensCssPath}. ` +
125
+ 'Run "fireforge token list" to see the declared tokens.');
126
+ }
127
+ const owning = collectTokenInventory(lines).find((group) => group.tokens.some((token) => token.name === name));
128
+ return { tokensCssPath, name, category: owning?.category ?? null, declarations };
129
+ };
130
+ if (options.json === true) {
131
+ setMachineOutputMode(true);
132
+ try {
133
+ const report = await emit();
134
+ process.stdout.write(`${JSON.stringify({ schemaVersion: MACHINE_OUTPUT_SCHEMA_VERSION, ...report }, null, 2)}\n`);
135
+ setStdoutSealed(true);
136
+ }
137
+ catch (error) {
138
+ emitMachineError('token-show-failed', toError(error).message);
139
+ }
140
+ finally {
141
+ setMachineOutputMode(false);
142
+ }
143
+ return;
144
+ }
145
+ const report = await emit();
146
+ intro('Design Token');
147
+ info(`${report.name} — category: ${report.category ?? '(no category)'}`);
148
+ info(report.tokensCssPath);
149
+ for (const declaration of report.declarations) {
150
+ info(` ${declaration.block}: ${declaration.value} (line ${String(declaration.line)})`);
151
+ }
152
+ outro(`${String(report.declarations.length)} declaration(s)`);
153
+ }
@@ -10,6 +10,7 @@ import { info, intro, outro, success, warn } from '../utils/logger.js';
10
10
  import { pickDefined } from '../utils/options.js';
11
11
  import { normalizeTokenName } from '../utils/validation.js';
12
12
  import { tokenCoverageCommand } from './token-coverage.js';
13
+ import { tokenListCommand, tokenShowCommand } from './token-list.js';
13
14
  async function normalizeTokenNameForProject(projectRoot, rawTokenName) {
14
15
  if (rawTokenName.startsWith('--')) {
15
16
  return normalizeTokenName(rawTokenName);
@@ -52,7 +53,7 @@ function reportSkippedToken(tokenName, result, options) {
52
53
  ? `:root${options.variant}`
53
54
  : `category "${result.skippedExisting?.category ?? options.category ?? ''}"`;
54
55
  const where = result.skippedExisting
55
- ? ` in ${scope} (line ${String(result.skippedExisting.line)}), unchanged`
56
+ ? ` in ${scope} (line ${result.skippedExisting.line}), unchanged`
56
57
  : '';
57
58
  info(`Token ${tokenName} already exists${where} (skipped)`);
58
59
  }
@@ -201,6 +202,21 @@ export function registerToken(program, { getProjectRoot, withErrorHandling }) {
201
202
  }),
202
203
  });
203
204
  }));
205
+ token
206
+ .command('list')
207
+ .description('List the design-token categories declared in the tokens CSS with their token names, in file order')
208
+ .option('--category <name>', 'Restrict the report to one category')
209
+ .option('--json', 'Emit the machine-readable envelope on stdout (see docs/machine-output.md)')
210
+ .action(withErrorHandling(async (options) => {
211
+ await tokenListCommand(getProjectRoot(), pickDefined({ category: options.category, json: options.json }));
212
+ }));
213
+ token
214
+ .command('show <token-name>')
215
+ .description('Show one token: its category, the value it takes in every declaring block, and where it is defined. The leading `--` is optional; with it, use the `--` separator first (e.g. `fireforge token show -- --my-token`).')
216
+ .option('--json', 'Emit the machine-readable envelope on stdout (see docs/machine-output.md)')
217
+ .action(withErrorHandling(async (tokenName, options) => {
218
+ await tokenShowCommand(getProjectRoot(), tokenName, pickDefined({ json: options.json }));
219
+ }));
204
220
  token
205
221
  .command('coverage')
206
222
  .description('Measure design token usage across modified CSS files')
@@ -290,7 +290,7 @@ async function treeExecCommand(projectRoot, name, args) {
290
290
  setStdoutSealed(true);
291
291
  });
292
292
  if (exitCode !== 0) {
293
- throw new GeneralError(`tree exec: fireforge ${args.join(' ')} exited with code ${String(exitCode)}.`);
293
+ throw new GeneralError(`tree exec: fireforge ${args.join(' ')} exited with code ${exitCode}.`);
294
294
  }
295
295
  }
296
296
  /** Registers the tree command group on the CLI program. */
@@ -298,7 +298,12 @@ export function registerTree(program, context) {
298
298
  const { getProjectRoot, withErrorHandling } = context;
299
299
  const tree = program
300
300
  .command('tree')
301
- .description('Manage copy-on-write verification clones of this project for concurrent read-mostly work (lint, typecheck, status, verify, export/re-export --dry-run; build-less test with create --with-objdir). Mutation commands are refused inside a tree.');
301
+ .description('Manage copy-on-write verification clones of this project for concurrent read-mostly work (lint, typecheck, status, verify, export/re-export --dry-run; build-less test with create --with-objdir). Mutation commands are refused inside a tree.')
302
+ // No-subcommand contract shared with `patch`/`token`/`furnace`: help on
303
+ // stdout, exit 0 (see `handleParseError` for the groups without an action).
304
+ .action(() => {
305
+ tree.outputHelp();
306
+ });
302
307
  addWaitLockOption(tree
303
308
  .command('create <name>')
304
309
  .description('Clone the project (engine included, obj-* excluded) into .fireforge/trees/<name> using filesystem copy-on-write (APFS clonefile / btrfs-XFS reflink). Refuses on filesystems without CoW support unless --force-copy explicitly accepts a full physical copy.')
@@ -57,8 +57,8 @@ export function resolveTypecheckProjects(configTypecheck, override) {
57
57
  */
58
58
  function formatIssue(projectRoot, issue) {
59
59
  const file = relativeForDisplay(projectRoot, issue.file);
60
- const codeLabel = issue.code > 0 ? ` TS${String(issue.code)}` : '';
61
- return `[${issue.project}] ${file}:${String(issue.line)}:${String(issue.column)}${codeLabel} ${issue.message}`;
60
+ const codeLabel = issue.code > 0 ? ` TS${issue.code}` : '';
61
+ return `[${issue.project}] ${file}:${issue.line}:${issue.column}${codeLabel} ${issue.message}`;
62
62
  }
63
63
  /**
64
64
  * Top-level entry point invoked by the registered Commander action.
@@ -87,7 +87,7 @@ async function runTypecheckCommandBody(projectRoot, options) {
87
87
  // session never touched. Run the same reconciler `furnace deploy`/`sync`
88
88
  // use so typecheck checks against the current workspace.
89
89
  await regenerateStaleGeneratedJsconfig(projectRoot);
90
- info(`Running typecheck across ${String(cfg.projects.length)} project(s): ${cfg.projects.join(', ')}`);
90
+ info(`Running typecheck across ${cfg.projects.length} project(s): ${cfg.projects.join(', ')}`);
91
91
  const results = await runTypecheck(projectRoot, cfg);
92
92
  // Fold the EXPORT-TIME authority into this pass. Per-patch lint runs
93
93
  // checkJs over the queue's patch-owned modules in relative isolation and
@@ -139,8 +139,8 @@ async function regenerateStaleGeneratedJsconfig(projectRoot) {
139
139
  if (!drift.changed)
140
140
  return;
141
141
  info(`Regenerating stale generated jsconfig ${furnaceConfig.typecheckJsconfig} before typecheck ` +
142
- `(+${String(drift.added.length)} added, ~${String(drift.updated.length)} updated, ` +
143
- `-${String(drift.pruned.length)} pruned).`);
142
+ `(+${drift.added.length} added, ~${drift.updated.length} updated, ` +
143
+ `-${drift.pruned.length} pruned).`);
144
144
  await syncFurnaceJsconfigPaths(projectRoot, furnaceConfig);
145
145
  }
146
146
  /**
@@ -171,10 +171,10 @@ export function reportResults(projectRoot, results, patchLintIssues = []) {
171
171
  for (const issue of errors)
172
172
  warn(formatIssue(projectRoot, issue));
173
173
  }
174
- const summary = `Typecheck: ${String(totalErrors)} error(s), ${String(totalWarnings)} warning(s) across ` +
175
- `${String(results.length)} project(s)` +
174
+ const summary = `Typecheck: ${totalErrors} error(s), ${totalWarnings} warning(s) across ` +
175
+ `${results.length} project(s)` +
176
176
  (patchLintIssues.length > 0
177
- ? ` (including ${String(patchLintIssues.length)} per-patch checkJs finding(s))`
177
+ ? ` (including ${patchLintIssues.length} per-patch checkJs finding(s))`
178
178
  : '');
179
179
  if (totalErrors === 0) {
180
180
  success(summary);
@@ -183,7 +183,7 @@ export function reportResults(projectRoot, results, patchLintIssues = []) {
183
183
  }
184
184
  info(summary);
185
185
  outro('Typecheck failed');
186
- throw new GeneralError(`Typecheck found ${String(totalErrors)} error(s) across ${String(results.length)} project(s).`);
186
+ throw new GeneralError(`Typecheck found ${totalErrors} error(s) across ${results.length} project(s).`);
187
187
  }
188
188
  /**
189
189
  * Registers the `typecheck` command on the CLI program.
@@ -1,7 +1,7 @@
1
1
  // SPDX-License-Identifier: EUPL-1.2
2
2
  import { join, relative } from 'node:path';
3
3
  import { Command } from 'commander';
4
- import { DEFAULT_BROWSER_SUBSCRIPT_DIR, wireSubscript } from '../core/browser-wire.js';
4
+ import { DEFAULT_BROWSER_SUBSCRIPT_DIR, wireSubscript, } from '../core/browser-wire.js';
5
5
  import { getProjectPaths, loadConfig } from '../core/config.js';
6
6
  import { furnaceConfigExists as checkFurnaceConfigExists, loadFurnaceConfig, } from '../core/furnace-config.js';
7
7
  import { consumeParserFallbackEvents } from '../core/parser-fallback.js';
@@ -13,6 +13,7 @@ import { pathExists } from '../utils/fs.js';
13
13
  import { info, intro, outro, success, warn } from '../utils/logger.js';
14
14
  import { pickDefined } from '../utils/options.js';
15
15
  import { isContainedRelativePath, isExplicitAbsolutePath, isPathInsideRoot, stripEnginePrefix, toRootRelativePath, } from '../utils/paths.js';
16
+ import { normalizePathSlashes } from '../utils/paths.js';
16
17
  const BROWSER_BASE_DIR = 'browser/base';
17
18
  function printWireDryRun(engineDir, name, subscriptDir, domFilePath, domTargetPath, options) {
18
19
  info('[dry-run] Would wire subscript:');
@@ -28,10 +29,10 @@ function printWireDryRun(engineDir, name, subscriptDir, domFilePath, domTargetPa
28
29
  info(` browser-init.js onUnload(): ${coerceToCall(options.destroy)}`);
29
30
  }
30
31
  if (domFilePath) {
31
- const includePath = relative(join(engineDir, subscriptDir), join(engineDir, domFilePath)).replace(/\\/g, '/');
32
+ const includePath = normalizePathSlashes(relative(join(engineDir, subscriptDir), join(engineDir, domFilePath)));
32
33
  info(` ${domTargetPath}: #include ${includePath}`);
33
34
  }
34
- const relPath = relative(join(engineDir, BROWSER_BASE_DIR), join(engineDir, subscriptDir)).replace(/\\/g, '/');
35
+ const relPath = normalizePathSlashes(relative(join(engineDir, BROWSER_BASE_DIR), join(engineDir, subscriptDir)));
35
36
  info(` jar.mn: content/browser/${name}.js (${relPath}/${name}.js)`);
36
37
  outro('Dry run complete');
37
38
  }
@@ -1,12 +1,5 @@
1
1
  import { FireForgeError } from '../errors/base.js';
2
2
  import type { ProjectLicense } from '../types/config.js';
3
- /**
4
- * Error thrown when branding operations fail.
5
- */
6
- export declare class BrandingError extends FireForgeError {
7
- readonly code: 6;
8
- get userMessage(): string;
9
- }
10
3
  /**
11
4
  * Error thrown when the generated `mozconfig` references a `--with-branding`
12
5
  * directory that does not match the branding tree FireForge set up. The
@@ -4,11 +4,12 @@ import { FireForgeError } from '../errors/base.js';
4
4
  import { ExitCode } from '../errors/codes.js';
5
5
  import { copyDir, pathExists, readText, writeTextIfChanged } from '../utils/fs.js';
6
6
  import { warn } from '../utils/logger.js';
7
+ import { normalizePathSlashes } from '../utils/paths.js';
7
8
  import { DEFAULT_LICENSE, getLicenseHeader } from './license-headers.js';
8
9
  /**
9
10
  * Error thrown when branding operations fail.
10
11
  */
11
- export class BrandingError extends FireForgeError {
12
+ class BrandingError extends FireForgeError {
12
13
  code = ExitCode.PATCH_ERROR;
13
14
  get userMessage() {
14
15
  return `Branding Error: ${this.message}\n\nBranding is required to set MOZ_APP_VENDOR, MOZ_MACBUNDLE_ID, and other Firefox identity values.`;
@@ -361,7 +362,7 @@ export async function isBrandingSetup(engineDir, config) {
361
362
  * @returns true if the path is managed by branding tooling
362
363
  */
363
364
  export function isBrandingManagedPath(file, binaryName) {
364
- const normalized = file.replace(/\\/g, '/');
365
+ const normalized = normalizePathSlashes(file);
365
366
  const brandingRoot = `browser/branding/${binaryName}`;
366
367
  return (normalized === 'browser/moz.configure' ||
367
368
  normalized === brandingRoot ||
@@ -4,11 +4,14 @@ import { GeneralError } from '../errors/base.js';
4
4
  import { toError } from '../utils/errors.js';
5
5
  import { verbose } from '../utils/logger.js';
6
6
  import { toRootRelativePath } from '../utils/paths.js';
7
+ import { normalizePathSlashes } from '../utils/paths.js';
7
8
  import { getProjectPaths, loadConfig } from './config.js';
8
9
  import { assertSnapshotted, createRollbackJournal, restoreRollbackJournal, snapshotFile, } from './furnace-rollback.js';
9
10
  import { registerBrowserContent } from './moz-manifest-register.js';
10
- import { DEFAULT_DOM_TARGET } from './wire-dom-fragment.js';
11
- import { addDestroyToBrowserInit, addDomFragment, addInitToBrowserInit, addSubscriptToBrowserMain, } from './wire-targets.js';
11
+ import { addDestroyToBrowserInit } from './wire-destroy.js';
12
+ import { addDomFragment, DEFAULT_DOM_TARGET } from './wire-dom-fragment.js';
13
+ import { addInitToBrowserInit } from './wire-init.js';
14
+ import { addSubscriptToBrowserMain } from './wire-subscript.js';
12
15
  export const DEFAULT_BROWSER_SUBSCRIPT_DIR = 'browser/base/content';
13
16
  const BROWSER_BASE_DIR = 'browser/base';
14
17
  /**
@@ -25,7 +28,7 @@ export async function wireSubscript(root, name, options = {}) {
25
28
  // Compute jar.mn source path relative to browser/base/
26
29
  let jarMnSourcePath;
27
30
  if (subscriptDir !== DEFAULT_BROWSER_SUBSCRIPT_DIR) {
28
- const relPath = relative(join(engineDir, BROWSER_BASE_DIR), join(engineDir, subscriptDir)).replace(/\\/g, '/');
31
+ const relPath = normalizePathSlashes(relative(join(engineDir, BROWSER_BASE_DIR), join(engineDir, subscriptDir)));
29
32
  jarMnSourcePath = `${relPath}/${name}.js`;
30
33
  }
31
34
  if (options.dryRun) {
@@ -36,3 +36,32 @@ export declare function findEnclosingGate(content: string, basename: string): st
36
36
  * @returns Detection result
37
37
  */
38
38
  export declare function detectPlatformGate(engineDir: string, sourcePath: string): Promise<PlatformGateResult>;
39
+ /**
40
+ * Result of an ancestor `DIRS +=` gate lookup.
41
+ */
42
+ export interface AncestorDirsGateResult {
43
+ /** True when an ancestor's `DIRS` entry is gated off on this host. */
44
+ gatedOff: boolean;
45
+ /** The gate expression that excluded the directory. */
46
+ gateExpression?: string;
47
+ /** Engine-relative path of the `moz.build` carrying the gate. */
48
+ gateFile?: string;
49
+ }
50
+ /**
51
+ * Determines whether the source file's DIRECTORY is excluded from this
52
+ * build by a gate in an ANCESTOR `moz.build`.
53
+ *
54
+ * `detectPlatformGate` reads only the file's own nearest `moz.build`, which
55
+ * misses the common Firefox shape where the whole subtree is reached
56
+ * through a conditional `DIRS +=` one or more levels up: nothing in
57
+ * `toolkit/mozapps/defaultagent/moz.build` says "Windows only", yet none of
58
+ * it is built on macOS.
59
+ *
60
+ * Errs toward NOT skipping, like the rest of this module: an unreadable or
61
+ * oversized ancestor, or a directory no `DIRS` entry names, yields
62
+ * `gatedOff: false`.
63
+ *
64
+ * @param engineDir - Absolute path to the engine root
65
+ * @param sourcePath - Engine-relative POSIX path of the source file
66
+ */
67
+ export declare function detectAncestorDirsGate(engineDir: string, sourcePath: string): Promise<AncestorDirsGateResult>;
@@ -28,6 +28,7 @@
28
28
  * that should ship on this host) are not, so it errs toward NOT skipping
29
29
  * when uncertain.
30
30
  */
31
+ import { stat } from 'node:fs/promises';
31
32
  import { dirname, join } from 'node:path';
32
33
  import { pathExists, readText } from '../utils/fs.js';
33
34
  import { getPlatform } from '../utils/platform.js';
@@ -231,3 +232,142 @@ export async function detectPlatformGate(engineDir, sourcePath) {
231
232
  }
232
233
  return { gatedOff: false };
233
234
  }
235
+ /**
236
+ * Maximum ancestor levels walked looking for a gating `DIRS` entry, and the
237
+ * largest `moz.build` read while doing it. The audit runs on every build, so
238
+ * a pathological tree must not be able to make it slow; beyond these bounds
239
+ * the walk gives up and the caller keeps today's behaviour (warn).
240
+ */
241
+ const MAX_ANCESTOR_DEPTH = 12;
242
+ const MAX_MOZ_BUILD_BYTES = 256 * 1024;
243
+ /** Matches a line that begins a `DIRS` assignment. */
244
+ const DIRS_ASSIGNMENT_PATTERN = /^\s*DIRS\s*\+?=/;
245
+ /**
246
+ * Collects the FULL stack of `if CONFIG[…]:` expressions enclosing the
247
+ * `DIRS` entry whose value equals `relDir`.
248
+ *
249
+ * Two differences from {@link findEnclosingGate}, both load-bearing here:
250
+ * only quoted values inside a `DIRS` assignment are considered (an
251
+ * unrelated string literal elsewhere in the file must not gate a
252
+ * directory), and EVERY enclosing expression is returned rather than the
253
+ * innermost. Firefox nests these — `toolkit/moz.build` reaches
254
+ * `mozapps/defaultagent` inside `OS_ARCH == "WINNT"`, then a `CC_TYPE`
255
+ * test, then `MOZ_DEFAULT_BROWSER_AGENT` — and it is the OUTERMOST one that
256
+ * excludes the directory on a macOS host.
257
+ *
258
+ * @param content - `moz.build` file content
259
+ * @param relDir - Directory path relative to the `moz.build`, POSIX-separated
260
+ * @returns Enclosing gate expressions, outermost first; empty when ungated
261
+ * or when no `DIRS` entry reaches `relDir`
262
+ */
263
+ function findDirsEntryGates(content, relDir) {
264
+ const lines = content.split('\n');
265
+ const stack = [];
266
+ let inDirs = false;
267
+ for (const line of lines) {
268
+ if (line.trim() === '')
269
+ continue;
270
+ const indent = (/^(\s*)/.exec(line)?.[1] ?? '').length;
271
+ while (stack.length > 0) {
272
+ const top = stack[stack.length - 1];
273
+ if (top && indent <= top.indent) {
274
+ stack.pop();
275
+ continue;
276
+ }
277
+ break;
278
+ }
279
+ const ifMatch = /^\s*if\s+(.+?):\s*(?:#.*)?$/.exec(line);
280
+ if (ifMatch?.[1]) {
281
+ stack.push({ indent, expression: ifMatch[1] });
282
+ inDirs = false;
283
+ continue;
284
+ }
285
+ if (DIRS_ASSIGNMENT_PATTERN.test(line)) {
286
+ // A `DIRS += [` list body continues on later lines; a single-line
287
+ // `DIRS += ["x"]` is fully handled by the match below.
288
+ inDirs = !line.includes(']');
289
+ if (dirsLineNames(line, relDir))
290
+ return stack.map((entry) => entry.expression);
291
+ continue;
292
+ }
293
+ if (inDirs) {
294
+ if (dirsLineNames(line, relDir))
295
+ return stack.map((entry) => entry.expression);
296
+ if (line.includes(']'))
297
+ inDirs = false;
298
+ }
299
+ }
300
+ return undefined;
301
+ }
302
+ /** True when `line` carries `relDir` as a quoted value. */
303
+ function dirsLineNames(line, relDir) {
304
+ const escaped = escapeRegex(relDir.replace(/\/+$/, ''));
305
+ return new RegExp(`["']${escaped}\\/?["']`).test(line);
306
+ }
307
+ /**
308
+ * Determines whether the source file's DIRECTORY is excluded from this
309
+ * build by a gate in an ANCESTOR `moz.build`.
310
+ *
311
+ * `detectPlatformGate` reads only the file's own nearest `moz.build`, which
312
+ * misses the common Firefox shape where the whole subtree is reached
313
+ * through a conditional `DIRS +=` one or more levels up: nothing in
314
+ * `toolkit/mozapps/defaultagent/moz.build` says "Windows only", yet none of
315
+ * it is built on macOS.
316
+ *
317
+ * Errs toward NOT skipping, like the rest of this module: an unreadable or
318
+ * oversized ancestor, or a directory no `DIRS` entry names, yields
319
+ * `gatedOff: false`.
320
+ *
321
+ * @param engineDir - Absolute path to the engine root
322
+ * @param sourcePath - Engine-relative POSIX path of the source file
323
+ */
324
+ export async function detectAncestorDirsGate(engineDir, sourcePath) {
325
+ let host;
326
+ try {
327
+ host = getPlatform();
328
+ }
329
+ catch {
330
+ return { gatedOff: false };
331
+ }
332
+ const segments = sourcePath.split('/').filter(Boolean);
333
+ // Drop the basename: the walk is over DIRECTORIES.
334
+ segments.pop();
335
+ const root = engineDir.replace(/[/\\]+$/, '');
336
+ const depth = Math.min(segments.length, MAX_ANCESTOR_DEPTH);
337
+ // Start at the immediate parent of the file's directory and walk up, so
338
+ // the nearest gating ancestor is found first.
339
+ for (let cut = segments.length - 1; cut >= segments.length - depth; cut -= 1) {
340
+ const ancestorSegments = segments.slice(0, cut);
341
+ const relDir = segments.slice(cut).join('/');
342
+ if (relDir === '')
343
+ continue;
344
+ const mozBuild = join(root, ...ancestorSegments, 'moz.build');
345
+ if (!(await pathExists(mozBuild)))
346
+ continue;
347
+ let content;
348
+ try {
349
+ const stats = await stat(mozBuild);
350
+ if (stats.size > MAX_MOZ_BUILD_BYTES)
351
+ continue;
352
+ content = await readText(mozBuild);
353
+ }
354
+ catch {
355
+ continue;
356
+ }
357
+ const gates = findDirsEntryGates(content, relDir);
358
+ if (gates === undefined)
359
+ continue;
360
+ const offHost = gates.find((expression) => isGateOffHost(expression, host));
361
+ if (offHost !== undefined) {
362
+ return {
363
+ gatedOff: true,
364
+ gateExpression: offHost,
365
+ gateFile: [...ancestorSegments, 'moz.build'].join('/'),
366
+ };
367
+ }
368
+ // The directory IS reached from here and this host passes every gate;
369
+ // a further ancestor cannot un-reach it.
370
+ return { gatedOff: false };
371
+ }
372
+ return { gatedOff: false };
373
+ }
@@ -25,6 +25,7 @@
25
25
  */
26
26
  import { basename, dirname, join, relative, sep } from 'node:path';
27
27
  import { pathExists, readText } from '../utils/fs.js';
28
+ import { normalizePathSlashes } from '../utils/paths.js';
28
29
  import { findAllByBasename } from './build-audit-resolve.js';
29
30
  /** Ceiling on ancestor-directory hops when searching for an owning jar.mn. */
30
31
  const MAX_JAR_MN_SCAN_DEPTH = 8;
@@ -65,7 +66,7 @@ export function parseJarMnEntry(line) {
65
66
  * practice, so a first-match wins behaviour is adequate.
66
67
  */
67
68
  export function findJarMnEntryForSource(content, relativeSource) {
68
- const normalized = relativeSource.replace(/\\/g, '/');
69
+ const normalized = normalizePathSlashes(relativeSource);
69
70
  for (const line of content.split('\n')) {
70
71
  const parsed = parseJarMnEntry(line);
71
72
  if (!parsed)
@@ -121,7 +122,7 @@ export async function findRegisteredTarget(engineDir, source) {
121
122
  * independent.
122
123
  */
123
124
  function toPosix(path) {
124
- return path.split(sep).join('/');
125
+ return normalizePathSlashes(path);
125
126
  }
126
127
  /**
127
128
  * Probes the dist tree for the artifact registered against the given
@@ -0,0 +1,57 @@
1
+ /**
2
+ * Why a changed engine path was not audited.
3
+ *
4
+ * The first four name skips that already existed as unstructured
5
+ * `verbose()` strings; the last four are the 0.46.0 classifiers.
6
+ */
7
+ export type AuditSkipReason = 'not-packageable' | 'platform-gated' | 'platform-gated-ancestor' | 'tests-not-packaged' | 'source-unreadable' | 'branding-not-selected' | 'storybook-story' | 'declared-unpackaged';
8
+ /** Render order for the `Packaged:` summary's per-class breakdown. */
9
+ export declare const AUDIT_SKIP_REASONS: readonly AuditSkipReason[];
10
+ /** True for a Storybook story module. */
11
+ export declare function isStorybookStoryPath(sourcePath: string): boolean;
12
+ /**
13
+ * Reads the branding directory this objdir was configured with.
14
+ *
15
+ * FireForge generates `engine/mozconfig` and writes the
16
+ * `ac_add_options --with-branding=…` line into it, so the selected branding
17
+ * is knowable from the tree. Returns undefined when the mozconfig cannot be
18
+ * read or carries no directive — the caller must then keep warning, because
19
+ * a skip that cannot name its evidence is a masked warning.
20
+ *
21
+ * @param engineDir - Absolute path to the engine root
22
+ * @returns Engine-relative branding path, or undefined
23
+ */
24
+ export declare function resolveSelectedBranding(engineDir: string): Promise<string | undefined>;
25
+ /**
26
+ * True when `sourcePath` lives under a branding tree that is NOT the one
27
+ * this objdir was configured with.
28
+ *
29
+ * An unselected branding directory is not an input to this build at all, so
30
+ * its files can never appear in `dist/`. Worse, the basename walker matched
31
+ * them against the SELECTED branding's same-named artifact and reported the
32
+ * unselected file as "newer than its packaged artifact" — a stale warning
33
+ * about a comparison that has no meaning.
34
+ *
35
+ * @param sourcePath - Engine-relative POSIX path
36
+ * @param selectedBranding - Engine-relative selected branding path
37
+ */
38
+ export declare function isUnselectedBrandingPath(sourcePath: string, selectedBranding: string | undefined): boolean;
39
+ /** One `buildAudit.unpackaged` carve-out from `fireforge.json`. */
40
+ export interface UnpackagedDeclaration {
41
+ /** Engine-relative path or glob. */
42
+ path: string;
43
+ /** Why this file is deliberately never packaged. Required, non-empty. */
44
+ reason: string;
45
+ }
46
+ /**
47
+ * Finds the declaration admitting `sourcePath`, if any.
48
+ *
49
+ * Exact match or a `*`-within-one-segment glob, matching the
50
+ * `support-files` matcher's deliberate narrowness: `**` and other
51
+ * cross-directory shapes are not accepted, because a carve-out that
52
+ * silently widens is exactly the failure this feature is warned against.
53
+ *
54
+ * @param sourcePath - Engine-relative POSIX path
55
+ * @param declarations - The project's `buildAudit.unpackaged` entries
56
+ */
57
+ export declare function matchUnpackagedDeclaration(sourcePath: string, declarations: readonly UnpackagedDeclaration[]): UnpackagedDeclaration | undefined;