@hominis/fireforge 0.45.0 → 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 (359) hide show
  1. package/CHANGELOG.md +80 -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.d.ts +2 -0
  18. package/dist/src/commands/doctor.js +7 -8
  19. package/dist/src/commands/download.js +35 -24
  20. package/dist/src/commands/export-all.js +21 -4
  21. package/dist/src/commands/export-flow.js +15 -45
  22. package/dist/src/commands/export-placement-conflicts.js +3 -3
  23. package/dist/src/commands/export-placement-gate.js +2 -8
  24. package/dist/src/commands/export-placement-policy.js +3 -5
  25. package/dist/src/commands/export-shared.d.ts +42 -23
  26. package/dist/src/commands/export-shared.js +4 -19
  27. package/dist/src/commands/export.js +10 -1
  28. package/dist/src/commands/furnace/authoring-config.d.ts +13 -0
  29. package/dist/src/commands/furnace/authoring-config.js +19 -0
  30. package/dist/src/commands/furnace/chrome-doc-remove.js +1 -1
  31. package/dist/src/commands/furnace/create-browser-test.js +3 -8
  32. package/dist/src/commands/furnace/create-dry-run.js +2 -5
  33. package/dist/src/commands/furnace/create-templates.d.ts +17 -1
  34. package/dist/src/commands/furnace/create-templates.js +2 -1
  35. package/dist/src/commands/furnace/create.js +25 -19
  36. package/dist/src/commands/furnace/deploy.js +11 -2
  37. package/dist/src/commands/furnace/init.js +2 -1
  38. package/dist/src/commands/furnace/override.js +28 -19
  39. package/dist/src/commands/furnace/refresh.js +9 -1
  40. package/dist/src/commands/furnace/remove-mutations.d.ts +6 -7
  41. package/dist/src/commands/furnace/remove-mutations.js +0 -1
  42. package/dist/src/commands/furnace/remove.js +2 -6
  43. package/dist/src/commands/furnace/rename-browser-test.js +1 -7
  44. package/dist/src/commands/furnace/rename.js +29 -29
  45. package/dist/src/commands/furnace/status.js +12 -9
  46. package/dist/src/commands/furnace/test-file-name.d.ts +28 -0
  47. package/dist/src/commands/furnace/test-file-name.js +39 -0
  48. package/dist/src/commands/furnace/validation-output.d.ts +21 -9
  49. package/dist/src/commands/furnace/validation-output.js +3 -8
  50. package/dist/src/commands/import.js +2 -9
  51. package/dist/src/commands/lint-per-patch.d.ts +2 -2
  52. package/dist/src/commands/lint-per-patch.js +19 -4
  53. package/dist/src/commands/lint-per-run-checkjs.d.ts +2 -2
  54. package/dist/src/commands/lint.d.ts +2 -1
  55. package/dist/src/commands/lint.js +1 -1
  56. package/dist/src/commands/manifest.js +1 -1
  57. package/dist/src/commands/package.js +2 -4
  58. package/dist/src/commands/patch/compact.js +14 -15
  59. package/dist/src/commands/patch/delete.js +15 -25
  60. package/dist/src/commands/patch/entry-projection.d.ts +13 -0
  61. package/dist/src/commands/patch/entry-projection.js +24 -0
  62. package/dist/src/commands/patch/lint-ignore.d.ts +0 -8
  63. package/dist/src/commands/patch/lint-ignore.js +19 -29
  64. package/dist/src/commands/patch/move-files-create.js +24 -10
  65. package/dist/src/commands/patch/move-files-into.js +38 -34
  66. package/dist/src/commands/patch/move-files.js +4 -13
  67. package/dist/src/commands/patch/rename.js +27 -41
  68. package/dist/src/commands/patch/reorder.js +25 -38
  69. package/dist/src/commands/patch/split-plan.d.ts +4 -5
  70. package/dist/src/commands/patch/split-plan.js +4 -8
  71. package/dist/src/commands/patch/split.js +45 -43
  72. package/dist/src/commands/patch/staged-dependency.js +16 -22
  73. package/dist/src/commands/patch/tier.js +15 -25
  74. package/dist/src/commands/patch-rename-map.d.ts +19 -0
  75. package/dist/src/commands/patch-rename-map.js +36 -0
  76. package/dist/src/commands/pending-resolution.d.ts +15 -0
  77. package/dist/src/commands/pending-resolution.js +25 -0
  78. package/dist/src/commands/re-export-adjacent.d.ts +24 -2
  79. package/dist/src/commands/re-export-adjacent.js +32 -16
  80. package/dist/src/commands/re-export-bulk-scan.js +6 -10
  81. package/dist/src/commands/re-export-drift.js +4 -4
  82. package/dist/src/commands/re-export-files.d.ts +2 -3
  83. package/dist/src/commands/re-export-files.js +38 -29
  84. package/dist/src/commands/re-export-lint.d.ts +3 -4
  85. package/dist/src/commands/re-export-lint.js +9 -2
  86. package/dist/src/commands/re-export-scan.js +5 -2
  87. package/dist/src/commands/re-export-single.d.ts +22 -3
  88. package/dist/src/commands/re-export-single.js +19 -11
  89. package/dist/src/commands/re-export.js +28 -11
  90. package/dist/src/commands/rebase/abort.js +3 -8
  91. package/dist/src/commands/rebase/continue.js +12 -12
  92. package/dist/src/commands/rebase/index.js +2 -2
  93. package/dist/src/commands/rebase/patch-loop.d.ts +2 -2
  94. package/dist/src/commands/rebase/patch-loop.js +2 -7
  95. package/dist/src/commands/resolve.js +18 -10
  96. package/dist/src/commands/run.d.ts +0 -13
  97. package/dist/src/commands/run.js +12 -25
  98. package/dist/src/commands/setup-support.d.ts +16 -4
  99. package/dist/src/commands/setup-support.js +16 -16
  100. package/dist/src/commands/setup.js +3 -9
  101. package/dist/src/commands/source.js +8 -1
  102. package/dist/src/commands/status-check.js +2 -2
  103. package/dist/src/commands/status-output.js +2 -2
  104. package/dist/src/commands/status.js +3 -4
  105. package/dist/src/commands/test-build-phase.d.ts +2 -2
  106. package/dist/src/commands/test-build-phase.js +31 -7
  107. package/dist/src/commands/test-modes.d.ts +2 -2
  108. package/dist/src/commands/test-modes.js +0 -1
  109. package/dist/src/commands/test-register.js +5 -3
  110. package/dist/src/commands/test-run.js +25 -18
  111. package/dist/src/commands/test-stale-gate.js +4 -4
  112. package/dist/src/commands/test-verdict.d.ts +16 -2
  113. package/dist/src/commands/test-verdict.js +18 -3
  114. package/dist/src/commands/test.js +42 -5
  115. package/dist/src/commands/token-list.d.ts +26 -0
  116. package/dist/src/commands/token-list.js +153 -0
  117. package/dist/src/commands/token.js +17 -1
  118. package/dist/src/commands/tree.js +7 -2
  119. package/dist/src/commands/typecheck.js +9 -9
  120. package/dist/src/commands/wire.js +4 -3
  121. package/dist/src/core/branding.d.ts +0 -7
  122. package/dist/src/core/branding.js +3 -2
  123. package/dist/src/core/browser-wire.js +6 -3
  124. package/dist/src/core/build-audit-platform.d.ts +29 -0
  125. package/dist/src/core/build-audit-platform.js +140 -0
  126. package/dist/src/core/build-audit-registration.js +3 -2
  127. package/dist/src/core/build-audit-skip.d.ts +57 -0
  128. package/dist/src/core/build-audit-skip.js +112 -0
  129. package/dist/src/core/build-audit.d.ts +15 -2
  130. package/dist/src/core/build-audit.js +110 -10
  131. package/dist/src/core/build-baseline.d.ts +45 -28
  132. package/dist/src/core/build-baseline.js +6 -29
  133. package/dist/src/core/build-jar-escalation.js +3 -2
  134. package/dist/src/core/build-overwrite-guard.js +1 -1
  135. package/dist/src/core/build-prepare.d.ts +0 -8
  136. package/dist/src/core/build-prepare.js +7 -12
  137. package/dist/src/core/config-paths.d.ts +2 -6
  138. package/dist/src/core/config-paths.js +4 -2
  139. package/dist/src/core/config-state.d.ts +0 -6
  140. package/dist/src/core/config-state.js +1 -19
  141. package/dist/src/core/config-validate-build-audit.d.ts +8 -0
  142. package/dist/src/core/config-validate-build-audit.js +80 -0
  143. package/dist/src/core/config-validate-file-size.js +1 -1
  144. package/dist/src/core/config-validate-patch-policy.d.ts +2 -2
  145. package/dist/src/core/config-validate-patch-policy.js +3 -3
  146. package/dist/src/core/config-validate-test-toolchains.d.ts +2 -2
  147. package/dist/src/core/config-validate-test-toolchains.js +4 -5
  148. package/dist/src/core/config-validate.js +14 -3
  149. package/dist/src/core/config.d.ts +2 -2
  150. package/dist/src/core/config.js +11 -15
  151. package/dist/src/core/coverage-extend.js +4 -4
  152. package/dist/src/core/discard-baseline.js +7 -7
  153. package/dist/src/core/engine-precondition.d.ts +1 -9
  154. package/dist/src/core/engine-precondition.js +3 -8
  155. package/dist/src/core/engine-session-lock.js +13 -17
  156. package/dist/src/core/export-moved-code-shape.js +2 -2
  157. package/dist/src/core/file-lock-owner.d.ts +103 -0
  158. package/dist/src/core/file-lock-owner.js +259 -0
  159. package/dist/src/core/file-lock.d.ts +2 -7
  160. package/dist/src/core/file-lock.js +40 -91
  161. package/dist/src/core/firefox-cache.d.ts +13 -1
  162. package/dist/src/core/firefox-cache.js +59 -34
  163. package/dist/src/core/firefox-download.d.ts +15 -0
  164. package/dist/src/core/firefox-download.js +37 -21
  165. package/dist/src/core/firefox-extract.d.ts +6 -5
  166. package/dist/src/core/firefox-extract.js +36 -18
  167. package/dist/src/core/firefox.d.ts +30 -32
  168. package/dist/src/core/firefox.js +5 -36
  169. package/dist/src/core/furnace-apply-dry-run.d.ts +1 -6
  170. package/dist/src/core/furnace-apply-dry-run.js +1 -7
  171. package/dist/src/core/furnace-apply-helpers.d.ts +0 -11
  172. package/dist/src/core/furnace-apply-helpers.js +8 -19
  173. package/dist/src/core/furnace-apply-overwrite-warn.js +4 -2
  174. package/dist/src/core/furnace-apply.d.ts +1 -1
  175. package/dist/src/core/furnace-apply.js +30 -10
  176. package/dist/src/core/furnace-config.d.ts +1 -1
  177. package/dist/src/core/furnace-config.js +23 -36
  178. package/dist/src/core/furnace-constants.js +3 -2
  179. package/dist/src/core/furnace-dir-entry.d.ts +20 -0
  180. package/dist/src/core/furnace-dir-entry.js +21 -0
  181. package/dist/src/core/furnace-operation.d.ts +2 -0
  182. package/dist/src/core/furnace-operation.js +3 -1
  183. package/dist/src/core/furnace-refresh.d.ts +18 -6
  184. package/dist/src/core/furnace-refresh.js +3 -6
  185. package/dist/src/core/furnace-registration-ast.d.ts +0 -1
  186. package/dist/src/core/furnace-registration-ast.js +0 -3
  187. package/dist/src/core/furnace-registration.d.ts +2 -1
  188. package/dist/src/core/furnace-registration.js +22 -29
  189. package/dist/src/core/furnace-scanner.js +3 -6
  190. package/dist/src/core/furnace-state-persist.d.ts +2 -2
  191. package/dist/src/core/furnace-validate-registration.js +5 -7
  192. package/dist/src/core/git-base.d.ts +3 -1
  193. package/dist/src/core/git-base.js +25 -3
  194. package/dist/src/core/git-diff-new-file.d.ts +40 -0
  195. package/dist/src/core/git-diff-new-file.js +119 -0
  196. package/dist/src/core/git-diff.d.ts +9 -9
  197. package/dist/src/core/git-diff.js +88 -157
  198. package/dist/src/core/git-file-ops.d.ts +0 -7
  199. package/dist/src/core/git-file-ops.js +4 -28
  200. package/dist/src/core/git-readonly-index.d.ts +0 -2
  201. package/dist/src/core/git-readonly-index.js +0 -4
  202. package/dist/src/core/git-status.d.ts +7 -0
  203. package/dist/src/core/git-status.js +21 -9
  204. package/dist/src/core/git.d.ts +0 -6
  205. package/dist/src/core/git.js +14 -43
  206. package/dist/src/core/harness-orphans.js +8 -7
  207. package/dist/src/core/history-log.d.ts +19 -0
  208. package/dist/src/core/history-log.js +29 -0
  209. package/dist/src/core/mach-error-hints.d.ts +0 -5
  210. package/dist/src/core/mach-error-hints.js +1 -1
  211. package/dist/src/core/mach-known-noise-filter.d.ts +4 -3
  212. package/dist/src/core/mach-known-noise-filter.js +7 -9
  213. package/dist/src/core/mach-mozconfig.js +2 -1
  214. package/dist/src/core/mach-python.d.ts +2 -0
  215. package/dist/src/core/mach-python.js +12 -7
  216. package/dist/src/core/mach-resource-shim.d.ts +13 -1
  217. package/dist/src/core/mach-resource-shim.js +112 -9
  218. package/dist/src/core/mach.d.ts +39 -53
  219. package/dist/src/core/mach.js +13 -81
  220. package/dist/src/core/marionette-port.d.ts +0 -17
  221. package/dist/src/core/marionette-port.js +12 -58
  222. package/dist/src/core/marionette-preflight.js +9 -27
  223. package/dist/src/core/mochitest-server-port.js +3 -3
  224. package/dist/src/core/moz-manifest-rules.js +37 -33
  225. package/dist/src/core/patch-apply-fuzz.js +4 -4
  226. package/dist/src/core/patch-apply.d.ts +4 -5
  227. package/dist/src/core/patch-apply.js +6 -12
  228. package/dist/src/core/patch-export-coverage.d.ts +0 -9
  229. package/dist/src/core/patch-export-coverage.js +1 -30
  230. package/dist/src/core/patch-export-update.d.ts +18 -1
  231. package/dist/src/core/patch-export-update.js +4 -3
  232. package/dist/src/core/patch-export.d.ts +11 -21
  233. package/dist/src/core/patch-export.js +44 -66
  234. package/dist/src/core/patch-files.d.ts +0 -4
  235. package/dist/src/core/patch-files.js +0 -11
  236. package/dist/src/core/patch-lint-binary.d.ts +6 -12
  237. package/dist/src/core/patch-lint-cache.d.ts +17 -1
  238. package/dist/src/core/patch-lint-cache.js +3 -6
  239. package/dist/src/core/patch-lint-checkjs.d.ts +33 -16
  240. package/dist/src/core/patch-lint-checkjs.js +37 -24
  241. package/dist/src/core/patch-lint-creators.d.ts +2 -6
  242. package/dist/src/core/patch-lint-cross.d.ts +16 -8
  243. package/dist/src/core/patch-lint-cross.js +49 -92
  244. package/dist/src/core/patch-lint-css.js +10 -6
  245. package/dist/src/core/patch-lint-file-size.js +3 -3
  246. package/dist/src/core/patch-lint-forward-registration.d.ts +58 -0
  247. package/dist/src/core/patch-lint-forward-registration.js +210 -0
  248. package/dist/src/core/patch-lint-jsdoc.js +36 -36
  249. package/dist/src/core/patch-lint-module-registration.d.ts +2 -8
  250. package/dist/src/core/patch-lint-prettier.js +1 -1
  251. package/dist/src/core/patch-lint-queue-types.d.ts +60 -0
  252. package/dist/src/core/patch-lint-queue-types.js +12 -0
  253. package/dist/src/core/patch-lint-staged-registration.d.ts +61 -0
  254. package/dist/src/core/patch-lint-staged-registration.js +90 -0
  255. package/dist/src/core/patch-lint.d.ts +14 -9
  256. package/dist/src/core/patch-lint.js +4 -3
  257. package/dist/src/core/patch-lock.js +3 -3
  258. package/dist/src/core/patch-manifest-io.js +8 -7
  259. package/dist/src/core/patch-parse.d.ts +19 -8
  260. package/dist/src/core/patch-parse.js +25 -0
  261. package/dist/src/core/patch-policy.js +15 -27
  262. package/dist/src/core/patch-source-metadata.d.ts +1 -3
  263. package/dist/src/core/patch-source-metadata.js +0 -4
  264. package/dist/src/core/patch-transform.d.ts +0 -8
  265. package/dist/src/core/patch-transform.js +0 -10
  266. package/dist/src/core/process-cpu-time.js +1 -1
  267. package/dist/src/core/rebase-session.d.ts +2 -8
  268. package/dist/src/core/rebase-session.js +5 -17
  269. package/dist/src/core/register-browser-content.js +3 -2
  270. package/dist/src/core/register-browser-test.js +2 -1
  271. package/dist/src/core/register-module.js +2 -1
  272. package/dist/src/core/register-scaffold.js +5 -4
  273. package/dist/src/core/register-shared-css.js +2 -1
  274. package/dist/src/core/run-log-redact.d.ts +38 -0
  275. package/dist/src/core/run-log-redact.js +69 -0
  276. package/dist/src/core/run-log.d.ts +13 -2
  277. package/dist/src/core/run-log.js +67 -18
  278. package/dist/src/core/signal-critical.js +2 -4
  279. package/dist/src/core/smoke-patterns.d.ts +0 -1
  280. package/dist/src/core/smoke-patterns.js +6 -11
  281. package/dist/src/core/state-file.d.ts +12 -1
  282. package/dist/src/core/state-file.js +14 -1
  283. package/dist/src/core/status-classify.js +2 -1
  284. package/dist/src/core/test-changed-prefs.js +26 -11
  285. package/dist/src/core/test-harness-crash.js +2 -2
  286. package/dist/src/core/test-stale-check.d.ts +0 -2
  287. package/dist/src/core/test-stale-check.js +2 -2
  288. package/dist/src/core/test-xpcshell-retry.d.ts +26 -7
  289. package/dist/src/core/test-xpcshell-retry.js +14 -11
  290. package/dist/src/core/token-category.d.ts +33 -0
  291. package/dist/src/core/token-category.js +56 -48
  292. package/dist/src/core/token-dark-mode.js +2 -14
  293. package/dist/src/core/token-inventory.d.ts +56 -0
  294. package/dist/src/core/token-inventory.js +109 -0
  295. package/dist/src/core/token-manager.d.ts +1 -1
  296. package/dist/src/core/token-manager.js +1 -1
  297. package/dist/src/core/token-variant.js +21 -22
  298. package/dist/src/core/toolchain-preflight.js +1 -1
  299. package/dist/src/core/tree-cow.d.ts +0 -2
  300. package/dist/src/core/tree-cow.js +2 -2
  301. package/dist/src/core/tree-store.js +4 -5
  302. package/dist/src/core/typecheck.js +2 -2
  303. package/dist/src/core/wire-dom-fragment.js +6 -17
  304. package/dist/src/core/wire-utils.js +5 -17
  305. package/dist/src/core/xpcshell-appdir.js +2 -4
  306. package/dist/src/errors/base.d.ts +53 -1
  307. package/dist/src/errors/base.js +79 -11
  308. package/dist/src/errors/build.js +20 -28
  309. package/dist/src/errors/config.d.ts +0 -14
  310. package/dist/src/errors/config.js +13 -34
  311. package/dist/src/errors/download.d.ts +13 -0
  312. package/dist/src/errors/download.js +53 -24
  313. package/dist/src/errors/furnace.js +8 -9
  314. package/dist/src/errors/git.d.ts +8 -8
  315. package/dist/src/errors/git.js +20 -30
  316. package/dist/src/errors/patch.js +8 -10
  317. package/dist/src/errors/rebase.js +7 -7
  318. package/dist/src/types/commands/options.d.ts +77 -123
  319. package/dist/src/types/config.d.ts +34 -2
  320. package/dist/src/types/index.d.ts +0 -1
  321. package/dist/src/utils/assert.d.ts +3 -15
  322. package/dist/src/utils/assert.js +1 -18
  323. package/dist/src/utils/build-info.d.ts +1 -1
  324. package/dist/src/utils/child-env.d.ts +2 -1
  325. package/dist/src/utils/elapsed.js +10 -1
  326. package/dist/src/utils/fs.js +8 -13
  327. package/dist/src/utils/hash.d.ts +12 -0
  328. package/dist/src/utils/hash.js +16 -0
  329. package/dist/src/utils/line-dispatch.d.ts +15 -0
  330. package/dist/src/utils/line-dispatch.js +48 -0
  331. package/dist/src/utils/options.d.ts +1 -7
  332. package/dist/src/utils/options.js +6 -7
  333. package/dist/src/utils/package-root.js +3 -12
  334. package/dist/src/utils/platform.d.ts +0 -4
  335. package/dist/src/utils/process-group.d.ts +2 -1
  336. package/dist/src/utils/process-group.js +20 -19
  337. package/dist/src/utils/process.d.ts +40 -26
  338. package/dist/src/utils/process.js +115 -166
  339. package/dist/src/utils/ps-duration.js +3 -3
  340. package/dist/src/utils/sleep.d.ts +26 -0
  341. package/dist/src/utils/sleep.js +31 -0
  342. package/dist/src/utils/validation.d.ts +2 -38
  343. package/dist/src/utils/validation.js +7 -16
  344. package/docs/configuration.md +89 -1
  345. package/docs/exit-codes.md +31 -19
  346. package/docs/lifecycle-invariants.md +18 -12
  347. package/docs/machine-output.md +31 -8
  348. package/docs/patch-workflow.md +2 -1
  349. package/docs/run-logs.md +44 -4
  350. package/docs/testing.md +8 -1
  351. package/package.json +8 -7
  352. package/dist/src/commands/rebase.d.ts +0 -4
  353. package/dist/src/commands/rebase.js +0 -5
  354. package/dist/src/core/patch-artifact-normalize.d.ts +0 -21
  355. package/dist/src/core/patch-artifact-normalize.js +0 -24
  356. package/dist/src/core/patch-lint-reexports.d.ts +0 -7
  357. package/dist/src/core/patch-lint-reexports.js +0 -8
  358. package/dist/src/core/wire-targets.d.ts +0 -7
  359. package/dist/src/core/wire-targets.js +0 -8
@@ -16,6 +16,7 @@ import { toError } from '../../utils/errors.js';
16
16
  import { ensureDir, pathExists, readText, writeText } from '../../utils/fs.js';
17
17
  import { info, success, warn } from '../../utils/logger.js';
18
18
  import { stripEnginePrefix } from '../../utils/paths.js';
19
+ import { browserTestFileName, deriveTestStem } from './test-file-name.js';
19
20
  /**
20
21
  * Validates the `--test-dir` option against the resolved test style before
21
22
  * any writes. Mochikit lives in the upstream toolkit/content/tests/widgets
@@ -65,15 +66,9 @@ export function resolveTestDirOverride(raw) {
65
66
  * the harness will try to run.
66
67
  */
67
68
  export async function scaffoldTestFiles(componentName, license, forgeConfig, paths, journal, testDirOverride) {
68
- const strippedName = componentName.startsWith('moz-') ? componentName.slice(4) : componentName;
69
- // Avoid double-prefixing: strip binaryName prefix since the default test
70
- // dir name already uses it
71
69
  const binaryName = forgeConfig.binaryName;
72
- const withoutBinaryPrefix = strippedName.startsWith(binaryName + '-')
73
- ? strippedName.slice(binaryName.length + 1)
74
- : strippedName;
75
- const underscored = withoutBinaryPrefix.replace(/-/g, '_');
76
- const testFileName = `browser_${binaryName}_${underscored}.js`;
70
+ const underscored = deriveTestStem(componentName, binaryName);
71
+ const testFileName = browserTestFileName(componentName, binaryName);
77
72
  // --test-dir redirects the scaffold: the hardcoded
78
73
  // `.../test/<binaryName>/` target can collide with a test suite owned by a
79
74
  // different patch. The manifest-registration name is the path below
@@ -8,6 +8,7 @@
8
8
  * independently of the mutation plumbing.
9
9
  */
10
10
  import { BROWSER_TEST_SCAFFOLD_ROOT, resolveBrowserChromeTestDir, resolveXpcshellTestDir, } from '../../core/furnace-constants.js';
11
+ import { deriveTestStem } from './test-file-name.js';
11
12
  /**
12
13
  * Builds the test-section fragment of the dry-run plan for a given
13
14
  * harness choice. Kept separate from the top-level formatter so the
@@ -18,11 +19,7 @@ function formatTestSection(args) {
18
19
  const { testStyle, componentName, binaryName, testDir } = args;
19
20
  if (testStyle === 'none')
20
21
  return '';
21
- const strippedName = componentName.startsWith('moz-') ? componentName.slice(4) : componentName;
22
- const withoutBinaryPrefix = strippedName.startsWith(binaryName + '-')
23
- ? strippedName.slice(binaryName.length + 1)
24
- : strippedName;
25
- const underscored = withoutBinaryPrefix.replace(/-/g, '_');
22
+ const underscored = deriveTestStem(componentName, binaryName);
26
23
  if (testStyle === 'browser-chrome') {
27
24
  // Same resolver as scaffoldTestFiles: the registration name is the
28
25
  // path below browser/base/content/test/, which under --test-dir is
@@ -3,6 +3,22 @@
3
3
  * command entrypoint so the generator is unit-testable in isolation and the
4
4
  * command file stays under the per-file LOC budget.
5
5
  */
6
+ export interface GenerateMjsContentOptions {
7
+ /** Custom element tag name. */
8
+ name: string;
9
+ /** Generated component class name. */
10
+ className: string;
11
+ /** Human-readable component description. */
12
+ description: string;
13
+ /** Whether to emit the Fluent wiring. */
14
+ localized: boolean;
15
+ /** License header prepended to the module. */
16
+ header: string;
17
+ /** chrome:// sub-path for the generated FTL reference, when localized. */
18
+ ftlChromeSubPath: string | undefined;
19
+ /** Explicit shared FTL path, used verbatim when supplied. */
20
+ sharedFtl: string | undefined;
21
+ }
6
22
  /**
7
23
  * Generates the .mjs file content for a custom component.
8
24
  *
@@ -23,7 +39,7 @@
23
39
  * matching the locale jar.mn entry that `furnace apply` writes.
24
40
  * 3. `<name>.ftl` — fallback when no chrome sub-path was resolvable.
25
41
  */
26
- export declare function generateMjsContent(name: string, className: string, description: string, localized: boolean, header: string, ftlChromeSubPath: string | undefined, sharedFtl: string | undefined): string;
42
+ export declare function generateMjsContent(options: GenerateMjsContentOptions): string;
27
43
  /** Generates the .css file content for a custom component. */
28
44
  export declare function generateCssContent(header: string): string;
29
45
  /** Generates the .ftl file content for a custom component. */
@@ -24,7 +24,8 @@
24
24
  * matching the locale jar.mn entry that `furnace apply` writes.
25
25
  * 3. `<name>.ftl` — fallback when no chrome sub-path was resolvable.
26
26
  */
27
- export function generateMjsContent(name, className, description, localized, header, ftlChromeSubPath, sharedFtl) {
27
+ export function generateMjsContent(options) {
28
+ const { name, className, description, localized, header, ftlChromeSubPath, sharedFtl } = options;
28
29
  // A `*/` in the operator-supplied description would close the JSDoc block
29
30
  // early and leave the remainder as stray code. Escaped here rather than
30
31
  // rejected at input, because the same text is also rendered by
@@ -3,7 +3,7 @@ import { join } from 'node:path';
3
3
  import { text } from '@clack/prompts';
4
4
  import { getProjectPaths, loadConfig } from '../../core/config.js';
5
5
  import { stdioIsInteractive } from '../../core/destructive.js';
6
- import { createDefaultFurnaceConfig, furnaceConfigExists, getFurnacePaths, loadFurnaceConfig, writeFurnaceConfig, } from '../../core/furnace-config.js';
6
+ import { getFurnacePaths, writeFurnaceConfig } from '../../core/furnace-config.js';
7
7
  import { resolveFtlChromeSubPath, tagNameToClassName, WIDGETS_DIR, } from '../../core/furnace-constants.js';
8
8
  import { completeJournalRollback, runFurnaceMutation, } from '../../core/furnace-operation.js';
9
9
  import { CUSTOM_ELEMENT_TAG_PATTERN, CUSTOM_ELEMENT_TAG_RULES, describeTagNameProblem, } from '../../core/furnace-registration-validate.js';
@@ -15,6 +15,7 @@ import { InvalidArgumentError } from '../../errors/base.js';
15
15
  import { FurnaceError } from '../../errors/furnace.js';
16
16
  import { ensureDir, pathExists, writeText } from '../../utils/fs.js';
17
17
  import { cancel, intro, isCancel, note, outro } from '../../utils/logger.js';
18
+ import { loadAuthoringFurnaceConfig } from './authoring-config.js';
18
19
  import { resolveValidatedTestDir, scaffoldTestFiles } from './create-browser-test.js';
19
20
  import { formatDryRunPlan, formatSuccessNote } from './create-dry-run.js';
20
21
  import { resolveCreateFeatures } from './create-features.js';
@@ -23,12 +24,6 @@ import { assertCustomEntryPersisted } from './create-readback.js';
23
24
  import { generateCssContent, generateFtlContent, generateMjsContent } from './create-templates.js';
24
25
  import { validateCreateAgainstConfig } from './create-validation.js';
25
26
  import { scaffoldXpcshellTestFiles } from './create-xpcshell.js';
26
- async function loadAuthoringFurnaceConfig(projectRoot) {
27
- if (await furnaceConfigExists(projectRoot)) {
28
- return loadFurnaceConfig(projectRoot);
29
- }
30
- return createDefaultFurnaceConfig();
31
- }
32
27
  function knownComponentSet(config) {
33
28
  return new Set([
34
29
  ...config.stock,
@@ -64,24 +59,25 @@ function validateTagName(name) {
64
59
  }
65
60
  /**
66
61
  * Writes the scaffolded component source files to disk.
67
- * @param componentDir - Destination component directory
68
- * @param componentName - Custom element tag name
69
- * @param className - Generated component class name
70
- * @param description - Human-readable component description
71
- * @param localized - Whether to include a Fluent file
72
- * @param license - Project license used for generated headers
73
- * @param ftlChromeSubPath - chrome:// sub-path for the generated FTL reference, when localized
74
- * @param sharedFtl - Explicit shared FTL path from `--shared-ftl`, when supplied
75
- * @param journal - Optional rollback journal that snapshots files before writes
62
+ * @param options - See {@link WriteComponentFilesOptions}
76
63
  * @returns Relative filenames written for the component
77
64
  */
78
- async function writeComponentFiles(componentDir, componentName, className, description, localized, license, ftlChromeSubPath, sharedFtl, journal) {
65
+ async function writeComponentFiles(options) {
66
+ const { componentDir, componentName, className, description, localized, license, ftlChromeSubPath, sharedFtl, journal, } = options;
79
67
  await ensureDir(componentDir);
80
68
  const files = [`${componentName}.mjs`, `${componentName}.css`];
81
69
  const mjsPath = join(componentDir, `${componentName}.mjs`);
82
70
  if (journal)
83
71
  await snapshotFile(journal, mjsPath);
84
- const mjsContent = generateMjsContent(componentName, className, description, localized, getLicenseHeader(license, 'js'), ftlChromeSubPath, sharedFtl);
72
+ const mjsContent = generateMjsContent({
73
+ name: componentName,
74
+ className,
75
+ description,
76
+ localized,
77
+ header: getLicenseHeader(license, 'js'),
78
+ ftlChromeSubPath,
79
+ sharedFtl,
80
+ });
85
81
  await writeText(mjsPath, mjsContent);
86
82
  const cssPath = join(componentDir, `${componentName}.css`);
87
83
  if (journal)
@@ -165,7 +161,17 @@ async function performCreateMutations(args) {
165
161
  // so signal-driven rollback can clean it up even if writeComponentFiles
166
162
  // is interrupted mid-ensureDir.
167
163
  recordCreatedDir(journal, args.componentDir);
168
- files = await writeComponentFiles(args.componentDir, args.componentName, args.className, args.description, args.localized, args.license, resolveFtlChromeSubPath(freshConfig.ftlBasePath), args.sharedFtl, journal);
164
+ files = await writeComponentFiles({
165
+ componentDir: args.componentDir,
166
+ componentName: args.componentName,
167
+ className: args.className,
168
+ description: args.description,
169
+ localized: args.localized,
170
+ license: args.license,
171
+ ftlChromeSubPath: resolveFtlChromeSubPath(freshConfig.ftlBasePath),
172
+ sharedFtl: args.sharedFtl,
173
+ journal,
174
+ });
169
175
  const customEntry = {
170
176
  description: args.description,
171
177
  targetPath: `${WIDGETS_DIR}/${args.componentName}`,
@@ -2,7 +2,7 @@
2
2
  import { loadConfig } from '../../core/config.js';
3
3
  import { applyAllComponents } from '../../core/furnace-apply.js';
4
4
  import { logApplyResult } from '../../core/furnace-apply-output.js';
5
- import { getFurnacePaths, loadFurnaceConfig } from '../../core/furnace-config.js';
5
+ import { getFurnacePaths } from '../../core/furnace-config.js';
6
6
  import { reportJsconfigPathsSync } from '../../core/furnace-jsconfig.js';
7
7
  import { runFurnaceMutation, waitLockMutationOptions, } from '../../core/furnace-operation.js';
8
8
  import { assertFurnaceReady } from '../../core/furnace-precondition.js';
@@ -203,7 +203,16 @@ export async function furnaceDeployCommand(projectRoot, name, options = {}) {
203
203
  }
204
204
  const validateSpinner = spinner(isDryRun ? 'Validating (read-only)...' : 'Validating...');
205
205
  const failedComponents = getFailedComponentNames(result);
206
- const validation = await runDeployValidation(validateSpinner, name, config, furnacePaths, failedComponents, isDryRun, projectRoot, result.actions);
206
+ const validation = await runDeployValidation({
207
+ validateSpinner,
208
+ name,
209
+ config,
210
+ furnacePaths,
211
+ failedComponents,
212
+ isDryRun,
213
+ projectRoot,
214
+ dryRunActions: result.actions,
215
+ });
207
216
  if (validation.done)
208
217
  return;
209
218
  const { totalErrors, totalWarnings, componentCount, skippedValidationCount } = validation;
@@ -13,6 +13,7 @@ import { FurnaceError } from '../../errors/furnace.js';
13
13
  import { getNodeErrorCode, toError } from '../../utils/errors.js';
14
14
  import { ensureDir, pathExists, writeText } from '../../utils/fs.js';
15
15
  import { cancel, info, intro, isCancel, note, outro, success, warn } from '../../utils/logger.js';
16
+ import { normalizePathSlashes } from '../../utils/paths.js';
16
17
  /**
17
18
  * File extensions that are definitely FTL resources (not locale
18
19
  * directories). A value ending in one of these is almost certainly the
@@ -64,7 +65,7 @@ async function validateFtlBasePath(value, engineDir) {
64
65
  // Normalize with the POSIX rules regardless of host: `normalize` on Windows
65
66
  // emits backslashes, so a `../`-prefix test against the platform `normalize`
66
67
  // silently passes every traversal attempt there.
67
- const normalized = posix.normalize(value.replace(/\\/g, '/'));
68
+ const normalized = posix.normalize(normalizePathSlashes(value));
68
69
  if (normalized === '..' || normalized.startsWith('../')) {
69
70
  throw new FurnaceError(`ftlBasePath "${value}" must not escape the engine checkout via parent-directory segments.`);
70
71
  }
@@ -4,7 +4,7 @@ import { dirname, join } from 'node:path';
4
4
  import { select, text } from '@clack/prompts';
5
5
  import { getProjectPaths, loadConfig, loadState } from '../../core/config.js';
6
6
  import { stdioIsInteractive } from '../../core/destructive.js';
7
- import { createDefaultFurnaceConfig, furnaceConfigExists, getFurnacePaths, loadFurnaceConfig, writeFurnaceConfig, } from '../../core/furnace-config.js';
7
+ import { getFurnacePaths, writeFurnaceConfig } from '../../core/furnace-config.js';
8
8
  import { resolveFtlDir } from '../../core/furnace-constants.js';
9
9
  import { completeJournalRollback, runFurnaceMutation } from '../../core/furnace-operation.js';
10
10
  import { assertFurnaceEngineReady } from '../../core/furnace-precondition.js';
@@ -16,25 +16,14 @@ import { FurnaceError } from '../../errors/furnace.js';
16
16
  import { toError } from '../../utils/errors.js';
17
17
  import { copyFile, ensureDir, pathExists, writeJson } from '../../utils/fs.js';
18
18
  import { cancel, info, intro, isCancel, note, outro, warn } from '../../utils/logger.js';
19
- async function loadAuthoringFurnaceConfig(projectRoot) {
20
- if (await furnaceConfigExists(projectRoot)) {
21
- return loadFurnaceConfig(projectRoot);
22
- }
23
- return createDefaultFurnaceConfig();
24
- }
19
+ import { loadAuthoringFurnaceConfig } from './authoring-config.js';
25
20
  /**
26
21
  * Copies the source files needed for a new override into the workspace.
27
- * @param engineDir - Absolute path to the engine checkout
28
- * @param srcDir - Original component directory in the engine checkout
29
- * @param destDir - Destination override directory in the workspace
30
- * @param componentName - Custom element tag name being overridden
31
- * @param hasFTL - Whether the component ships a localized `.ftl` file
32
- * @param overrideType - Requested override mode
33
- * @param ftlDir - Engine-relative directory the shared `.ftl` deploys into
34
- * @param journal - Rollback journal every write is recorded in
22
+ * @param options - See {@link CopyOverrideFilesOptions}
35
23
  * @returns Filenames copied into the override directory
36
24
  */
37
- async function copyOverrideFiles(engineDir, srcDir, destDir, componentName, hasFTL, overrideType, ftlDir, journal) {
25
+ async function copyOverrideFiles(options) {
26
+ const { engineDir, srcDir, destDir, componentName, hasFTL, overrideType, ftlDir, journal } = options;
38
27
  await ensureDir(destDir);
39
28
  const entries = await readdir(srcDir, { withFileTypes: true });
40
29
  const copiedFiles = [];
@@ -93,7 +82,8 @@ async function copyOverrideFiles(engineDir, srcDir, destDir, componentName, hasF
93
82
  * cycles into a single surviving entry — otherwise both report success and
94
83
  * furnace.json keeps only the second writer's.
95
84
  */
96
- async function saveOverrideConfig(projectRoot, destDir, componentName, overrideType, description, details, firefoxVersion, journal, baseCommit) {
85
+ async function saveOverrideConfig(options) {
86
+ const { projectRoot, destDir, componentName, overrideType, description, details, firefoxVersion, journal, baseCommit, } = options;
97
87
  const overrideJson = {
98
88
  type: overrideType,
99
89
  description,
@@ -138,9 +128,28 @@ async function performOverrideMutations(args) {
138
128
  ctx.registerJournal(journal);
139
129
  recordCreatedDir(journal, args.destDir);
140
130
  try {
141
- const filesCopied = await copyOverrideFiles(args.engineDir, args.srcDir, args.destDir, args.componentName, args.details.hasFTL, args.overrideType, args.ftlDir, journal);
131
+ const filesCopied = await copyOverrideFiles({
132
+ engineDir: args.engineDir,
133
+ srcDir: args.srcDir,
134
+ destDir: args.destDir,
135
+ componentName: args.componentName,
136
+ hasFTL: args.details.hasFTL,
137
+ overrideType: args.overrideType,
138
+ ftlDir: args.ftlDir,
139
+ journal,
140
+ });
142
141
  await snapshotFile(journal, args.furnacePaths.furnaceConfig);
143
- await saveOverrideConfig(args.projectRoot, args.destDir, args.componentName, args.overrideType, args.description, args.details, args.firefoxVersion, journal, args.baseCommit);
142
+ await saveOverrideConfig({
143
+ projectRoot: args.projectRoot,
144
+ destDir: args.destDir,
145
+ componentName: args.componentName,
146
+ overrideType: args.overrideType,
147
+ description: args.description,
148
+ details: args.details,
149
+ firefoxVersion: args.firefoxVersion,
150
+ journal,
151
+ baseCommit: args.baseCommit,
152
+ });
144
153
  return filesCopied;
145
154
  }
146
155
  catch (error) {
@@ -126,7 +126,15 @@ async function refreshSingleOverride(projectRoot, name, options = {}) {
126
126
  for (const entry of overrideFiles) {
127
127
  const overridePath = join(overrideDir, entry.name);
128
128
  const engineRelPath = getOverrideEngineTargetPath(paths.engine, overrideConfig, entry.name, ftlDir).slice(paths.engine.length + 1);
129
- const result = await refreshOverrideFile(paths.engine, overridePath, engineRelPath, baseCommit, entry.name, dryRun, strategy);
129
+ const result = await refreshOverrideFile({
130
+ engineDir: paths.engine,
131
+ overridePath,
132
+ engineRelPath,
133
+ baseCommit,
134
+ fileName: entry.name,
135
+ dryRun,
136
+ strategy,
137
+ });
130
138
  fileResults.push(result);
131
139
  }
132
140
  // Update baseVersion and baseCommit on clean merge (not dry-run, no conflicts)
@@ -1,7 +1,6 @@
1
- import type { loadFurnaceConfig } from '../../core/furnace-config.js';
2
- import { getFurnacePaths } from '../../core/furnace-config.js';
1
+ import type { FurnacePaths } from '../../core/furnace-config.js';
3
2
  import type { RollbackJournal } from '../../core/furnace-rollback.js';
4
- import type { FurnaceState } from '../../types/furnace.js';
3
+ import type { FurnaceConfig, FurnaceState } from '../../types/furnace.js';
5
4
  /**
6
5
  * Engine-side and workspace-side removal for an OVERRIDE component.
7
6
  *
@@ -14,8 +13,8 @@ export declare function performOverrideRemovalMutations(args: {
14
13
  paths: {
15
14
  engine: string;
16
15
  };
17
- furnacePaths: ReturnType<typeof getFurnacePaths>;
18
- freshConfig: Awaited<ReturnType<typeof loadFurnaceConfig>>;
16
+ furnacePaths: FurnacePaths;
17
+ freshConfig: FurnaceConfig;
19
18
  freshState: FurnaceState;
20
19
  ftlDir: string;
21
20
  journal: RollbackJournal;
@@ -30,8 +29,8 @@ export declare function performCustomRemovalMutations(args: {
30
29
  paths: {
31
30
  engine: string;
32
31
  };
33
- furnacePaths: ReturnType<typeof getFurnacePaths>;
34
- freshConfig: Awaited<ReturnType<typeof loadFurnaceConfig>>;
32
+ furnacePaths: FurnacePaths;
33
+ freshConfig: FurnaceConfig;
35
34
  ftlDir: string;
36
35
  journal: RollbackJournal;
37
36
  }): Promise<void>;
@@ -16,7 +16,6 @@ import { join } from 'node:path';
16
16
  import { removeCustomFtlJarMnEntry } from '../../core/furnace-apply-ftl.js';
17
17
  import { getOverrideEngineTargetPath, isOverrideCopyCandidate, restoreOverrideFileToBaseline, } from '../../core/furnace-apply-helpers.js';
18
18
  import { extractComponentChecksums } from '../../core/furnace-checksum-utils.js';
19
- import { getFurnacePaths } from '../../core/furnace-config.js';
20
19
  import { removeCustomElementRegistration, removeJarMnEntries, } from '../../core/furnace-registration.js';
21
20
  import { snapshotDir, snapshotFile } from '../../core/furnace-rollback.js';
22
21
  import { isGitRepository } from '../../core/git.js';
@@ -16,6 +16,7 @@ import { pathExists, readText, removeDir, writeText } from '../../utils/fs.js';
16
16
  import { cancel, info, intro, isCancel, outro, warn } from '../../utils/logger.js';
17
17
  import { performCustomRemovalMutations, performOverrideRemovalMutations, } from './remove-mutations.js';
18
18
  import { dropChecksumsByPrefix } from './remove-state.js';
19
+ import { browserTestFileName } from './test-file-name.js';
19
20
  /**
20
21
  * Removes an entire TOML section (header + body lines) for a given test file.
21
22
  * Matches from `["filename"]` up to the next section header `[` or end-of-file,
@@ -94,12 +95,7 @@ async function cleanupCustomTestFiles(name, projectRoot, journal) {
94
95
  }
95
96
  const paths = getProjectPaths(projectRoot);
96
97
  const binaryName = forgeConfig.binaryName;
97
- const strippedName = name.startsWith('moz-') ? name.slice(4) : name;
98
- const withoutBinaryPrefix = strippedName.startsWith(binaryName + '-')
99
- ? strippedName.slice(binaryName.length + 1)
100
- : strippedName;
101
- const underscored = withoutBinaryPrefix.replace(/-/g, '_');
102
- const testFileName = `browser_${binaryName}_${underscored}.js`;
98
+ const testFileName = browserTestFileName(name, binaryName);
103
99
  const testDir = join(paths.engine, 'browser/base/content/test', binaryName);
104
100
  if (!(await pathExists(testDir)))
105
101
  return { partialFailures };
@@ -1,13 +1,7 @@
1
1
  // SPDX-License-Identifier: EUPL-1.2
2
2
  import { tagNameToClassName } from '../../core/furnace-constants.js';
3
3
  import { escapeRegex } from '../../utils/regex.js';
4
- function deriveTestStem(componentName, binaryName) {
5
- const strippedName = componentName.startsWith('moz-') ? componentName.slice(4) : componentName;
6
- const withoutBinaryPrefix = strippedName.startsWith(binaryName + '-')
7
- ? strippedName.slice(binaryName.length + 1)
8
- : strippedName;
9
- return withoutBinaryPrefix.replace(/-/g, '_');
10
- }
4
+ import { deriveTestStem } from './test-file-name.js';
11
5
  /** Rewrites scaffolded browser-chrome test literals after a component rename. */
12
6
  export function updateBrowserChromeTestContent(content, oldName, newName, binaryName) {
13
7
  const oldClassName = tagNameToClassName(oldName);
@@ -18,18 +18,7 @@ import { escapeRegex } from '../../utils/regex.js';
18
18
  import { updateBrowserChromeTestContent } from './rename-browser-test.js';
19
19
  import { rekeyStateChecksums, renameComponentFileName, updateConfigForCustomRename, updateConfigForOverrideRename, } from './rename-helpers.js';
20
20
  import { renameXpcshellTestFiles } from './rename-xpcshell.js';
21
- /**
22
- * Derives the test file name for a component, matching the convention used by
23
- * `furnace create --with-tests`.
24
- */
25
- function deriveTestFileName(componentName, binaryName) {
26
- const strippedName = componentName.startsWith('moz-') ? componentName.slice(4) : componentName;
27
- const withoutBinaryPrefix = strippedName.startsWith(binaryName + '-')
28
- ? strippedName.slice(binaryName.length + 1)
29
- : strippedName;
30
- const underscored = withoutBinaryPrefix.replace(/-/g, '_');
31
- return `browser_${binaryName}_${underscored}.js`;
32
- }
21
+ import { browserTestFileName } from './test-file-name.js';
33
22
  /**
34
23
  * Renames test files created by `furnace create --with-tests` in the engine
35
24
  * test directory. Best-effort: failures are logged as warnings but do not
@@ -44,8 +33,8 @@ async function renameTestFiles(engineDir, projectRoot, oldName, newName, journal
44
33
  return; // Cannot determine test paths without config.
45
34
  }
46
35
  const binaryName = forgeConfig.binaryName;
47
- const oldTestFileName = deriveTestFileName(oldName, binaryName);
48
- const newTestFileName = deriveTestFileName(newName, binaryName);
36
+ const oldTestFileName = browserTestFileName(oldName, binaryName);
37
+ const newTestFileName = browserTestFileName(newName, binaryName);
49
38
  const testDir = join(engineDir, 'browser/base/content/test', binaryName);
50
39
  if (!(await pathExists(testDir)))
51
40
  return;
@@ -259,7 +248,15 @@ async function performRenameMutations(args) {
259
248
  if (isCustom && config.custom[newName]?.register && (await pathExists(args.engineDir))) {
260
249
  const ftlDir = resolveFtlDir(config.ftlBasePath);
261
250
  const isLocalized = config.custom[newName].localized;
262
- await updateEngineRegistrations(args.engineDir, oldName, newName, newDir, ftlDir, isLocalized, journal);
251
+ await updateEngineRegistrations({
252
+ engineDir: args.engineDir,
253
+ oldName,
254
+ newName,
255
+ newDir,
256
+ ftlDir,
257
+ isLocalized,
258
+ journal,
259
+ });
263
260
  }
264
261
  // 4. Re-key furnace-state.json checksums from old name to new name
265
262
  await rekeyStateChecksums(args.projectRoot, componentType, oldName, newName);
@@ -323,7 +320,14 @@ async function performRenameMutations(args) {
323
320
  }
324
321
  });
325
322
  }
326
- async function updateEngineRegistrations(engineDir, oldName, newName, newDir, ftlDir, isLocalized, journal) {
323
+ /**
324
+ * Re-points the engine-side registrations (customElements.js, jar.mn, and
325
+ * the localized `.ftl` entries) from the old tag name to the new one.
326
+ *
327
+ * @param options - See {@link UpdateEngineRegistrationsOptions}
328
+ */
329
+ async function updateEngineRegistrations(options) {
330
+ const { engineDir, oldName, newName, newDir, ftlDir, isLocalized, journal } = options;
327
331
  const customElementsPath = join(engineDir, 'toolkit/content/customElements.js');
328
332
  const jarMnPath = join(engineDir, 'toolkit/content/jar.mn');
329
333
  if (await pathExists(customElementsPath)) {
@@ -396,15 +400,15 @@ export async function furnaceRenameCommand(projectRoot, oldName, newName) {
396
400
  if (newName in config.custom || newName in config.overrides || config.stock.includes(newName)) {
397
401
  throw new FurnaceError(`A component named "${newName}" already exists in furnace.json.`, newName);
398
402
  }
399
- const componentType = isCustom ? 'custom' : 'override';
400
- // `componentType` is the furnace-state key (singular: `custom` /
401
- // `override`); the on-disk directory label differs — custom components
402
- // live under `components/custom/` (singular) while overrides live under
403
- // `components/overrides/` (plural). Appending an `s` to `componentType`
404
- // produces the wrong label `components/customs/` for custom components
405
- // and is correct for overrides only by coincidence. `componentDirLabel`
406
- // centralises the pick so every operator-facing string names the
407
- // directory that actually exists on disk.
403
+ // The furnace-state key is singular (`custom` / `override`, derived
404
+ // inside performRenameMutations); the on-disk directory label differs —
405
+ // custom components live under `components/custom/` (singular) while
406
+ // overrides live under `components/overrides/` (plural). Appending an
407
+ // `s` to the state key produces the wrong label `components/customs/`
408
+ // for custom components and is correct for overrides only by
409
+ // coincidence. `componentDirLabel` centralises the pick so every
410
+ // operator-facing string names the directory that actually exists on
411
+ // disk.
408
412
  const componentDirLabel = isCustom ? 'custom' : 'overrides';
409
413
  const baseDir = isCustom ? furnacePaths.customDir : furnacePaths.overridesDir;
410
414
  const oldDir = join(baseDir, oldName);
@@ -419,11 +423,7 @@ export async function furnaceRenameCommand(projectRoot, oldName, newName) {
419
423
  projectRoot,
420
424
  oldName,
421
425
  newName,
422
- oldDir,
423
426
  newDir,
424
- isCustom,
425
- componentType,
426
- config,
427
427
  furnaceConfigPath: furnacePaths.furnaceConfig,
428
428
  furnacePaths,
429
429
  engineDir: paths.engine,
@@ -11,15 +11,10 @@ import { pathExists } from '../../utils/fs.js';
11
11
  import { info, intro, note, outro, warn } from '../../utils/logger.js';
12
12
  /**
13
13
  * Displays detailed status for a single Furnace component, including registration drift.
14
- * @param name - Component tag name to inspect
15
- * @param config - Loaded Furnace configuration
16
- * @param state - Loaded furnace state, for applied/engine checksums
17
- * @param projectRoot - Root directory of the project
18
- * @param paths - Resolved project paths
19
- * @param furnacePaths - Resolved Furnace-specific paths
20
- * @param ftlDir - Engine-relative directory localized components deploy their `.ftl` into
14
+ * @param options - See {@link DetailedComponentStatusOptions}
21
15
  */
22
- async function showDetailedComponentStatus(name, config, state, projectRoot, paths, furnacePaths, ftlDir) {
16
+ async function showDetailedComponentStatus(options) {
17
+ const { name, config, state, projectRoot, paths, furnacePaths, ftlDir } = options;
23
18
  const customConfig = config.custom[name];
24
19
  const overrideConfig = config.overrides[name];
25
20
  if (!customConfig && !overrideConfig && !config.stock.includes(name)) {
@@ -113,7 +108,15 @@ export async function furnaceStatusCommand(projectRoot, name) {
113
108
  const furnacePaths = getFurnacePaths(projectRoot);
114
109
  const ftlDir = resolveFtlDir(config.ftlBasePath);
115
110
  if (name) {
116
- await showDetailedComponentStatus(name, config, state, projectRoot, paths, furnacePaths, ftlDir);
111
+ await showDetailedComponentStatus({
112
+ name,
113
+ config,
114
+ state,
115
+ projectRoot,
116
+ paths,
117
+ furnacePaths,
118
+ ftlDir,
119
+ });
117
120
  return;
118
121
  }
119
122
  // Surface a pendingRepair marker before the normal summary so it cannot
@@ -0,0 +1,28 @@
1
+ /**
2
+ * Derives the underscored stem a component's generated test files are named
3
+ * after.
4
+ *
5
+ * Five modules (`create-browser-test`, `create-dry-run`, `remove`, `rename`,
6
+ * `rename-browser-test`) had each inlined this three-step transform. The steps
7
+ * are not arbitrary and must agree exactly, or `furnace rename` and
8
+ * `furnace remove` compute a different filename than `furnace create` wrote
9
+ * and silently leave the old test behind:
10
+ *
11
+ * 1. drop the upstream `moz-` custom-element prefix, which never appears in a
12
+ * test filename;
13
+ * 2. drop a leading `<binaryName>-`, because the test directory is already
14
+ * named after the binary and the prefix would be doubled;
15
+ * 3. underscore the remaining dashes, matching mozilla-central's
16
+ * `browser_*.js` convention.
17
+ * @param componentName - Custom-element name, e.g. `moz-mybrowser-sidebar`.
18
+ * @param binaryName - Configured binary name, e.g. `mybrowser`.
19
+ * @returns The stem, e.g. `sidebar`.
20
+ */
21
+ export declare function deriveTestStem(componentName: string, binaryName: string): string;
22
+ /**
23
+ * Builds the browser-chrome test filename for a component.
24
+ * @param componentName - Custom-element name.
25
+ * @param binaryName - Configured binary name.
26
+ * @returns The filename, e.g. `browser_mybrowser_sidebar.js`.
27
+ */
28
+ export declare function browserTestFileName(componentName: string, binaryName: string): string;
@@ -0,0 +1,39 @@
1
+ // SPDX-License-Identifier: EUPL-1.2
2
+ /**
3
+ * Derives the underscored stem a component's generated test files are named
4
+ * after.
5
+ *
6
+ * Five modules (`create-browser-test`, `create-dry-run`, `remove`, `rename`,
7
+ * `rename-browser-test`) had each inlined this three-step transform. The steps
8
+ * are not arbitrary and must agree exactly, or `furnace rename` and
9
+ * `furnace remove` compute a different filename than `furnace create` wrote
10
+ * and silently leave the old test behind:
11
+ *
12
+ * 1. drop the upstream `moz-` custom-element prefix, which never appears in a
13
+ * test filename;
14
+ * 2. drop a leading `<binaryName>-`, because the test directory is already
15
+ * named after the binary and the prefix would be doubled;
16
+ * 3. underscore the remaining dashes, matching mozilla-central's
17
+ * `browser_*.js` convention.
18
+ * @param componentName - Custom-element name, e.g. `moz-mybrowser-sidebar`.
19
+ * @param binaryName - Configured binary name, e.g. `mybrowser`.
20
+ * @returns The stem, e.g. `sidebar`.
21
+ */
22
+ export function deriveTestStem(componentName, binaryName) {
23
+ const withoutMozPrefix = componentName.startsWith('moz-')
24
+ ? componentName.slice(4)
25
+ : componentName;
26
+ const withoutBinaryPrefix = withoutMozPrefix.startsWith(`${binaryName}-`)
27
+ ? withoutMozPrefix.slice(binaryName.length + 1)
28
+ : withoutMozPrefix;
29
+ return withoutBinaryPrefix.replace(/-/g, '_');
30
+ }
31
+ /**
32
+ * Builds the browser-chrome test filename for a component.
33
+ * @param componentName - Custom-element name.
34
+ * @param binaryName - Configured binary name.
35
+ * @returns The filename, e.g. `browser_mybrowser_sidebar.js`.
36
+ */
37
+ export function browserTestFileName(componentName, binaryName) {
38
+ return `browser_${binaryName}_${deriveTestStem(componentName, binaryName)}.js`;
39
+ }