@hominis/fireforge 0.9.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 (316) hide show
  1. package/CHANGELOG.md +7 -0
  2. package/LICENSE.md +294 -0
  3. package/README.md +435 -0
  4. package/dist/bin/fireforge.d.ts +10 -0
  5. package/dist/bin/fireforge.js +29 -0
  6. package/dist/src/cli.d.ts +33 -0
  7. package/dist/src/cli.js +180 -0
  8. package/dist/src/commands/bootstrap.d.ts +9 -0
  9. package/dist/src/commands/bootstrap.js +73 -0
  10. package/dist/src/commands/build.d.ts +11 -0
  11. package/dist/src/commands/build.js +102 -0
  12. package/dist/src/commands/config.d.ts +13 -0
  13. package/dist/src/commands/config.js +135 -0
  14. package/dist/src/commands/discard.d.ts +12 -0
  15. package/dist/src/commands/discard.js +84 -0
  16. package/dist/src/commands/doctor.d.ts +18 -0
  17. package/dist/src/commands/doctor.js +356 -0
  18. package/dist/src/commands/download.d.ts +11 -0
  19. package/dist/src/commands/download.js +127 -0
  20. package/dist/src/commands/export-all.d.ts +11 -0
  21. package/dist/src/commands/export-all.js +122 -0
  22. package/dist/src/commands/export-shared.d.ts +48 -0
  23. package/dist/src/commands/export-shared.js +208 -0
  24. package/dist/src/commands/export.d.ts +13 -0
  25. package/dist/src/commands/export.js +178 -0
  26. package/dist/src/commands/furnace/apply.d.ts +7 -0
  27. package/dist/src/commands/furnace/apply.js +80 -0
  28. package/dist/src/commands/furnace/create.d.ts +8 -0
  29. package/dist/src/commands/furnace/create.js +377 -0
  30. package/dist/src/commands/furnace/deploy.d.ts +8 -0
  31. package/dist/src/commands/furnace/deploy.js +338 -0
  32. package/dist/src/commands/furnace/diff.d.ts +7 -0
  33. package/dist/src/commands/furnace/diff.js +119 -0
  34. package/dist/src/commands/furnace/index.d.ts +16 -0
  35. package/dist/src/commands/furnace/index.js +121 -0
  36. package/dist/src/commands/furnace/list.d.ts +5 -0
  37. package/dist/src/commands/furnace/list.js +65 -0
  38. package/dist/src/commands/furnace/override.d.ts +8 -0
  39. package/dist/src/commands/furnace/override.js +188 -0
  40. package/dist/src/commands/furnace/preview.d.ts +7 -0
  41. package/dist/src/commands/furnace/preview.js +96 -0
  42. package/dist/src/commands/furnace/remove.d.ts +8 -0
  43. package/dist/src/commands/furnace/remove.js +159 -0
  44. package/dist/src/commands/furnace/scan.d.ts +5 -0
  45. package/dist/src/commands/furnace/scan.js +112 -0
  46. package/dist/src/commands/furnace/status.d.ts +7 -0
  47. package/dist/src/commands/furnace/status.js +137 -0
  48. package/dist/src/commands/furnace/validate.d.ts +6 -0
  49. package/dist/src/commands/furnace/validate.js +91 -0
  50. package/dist/src/commands/furnace/validation-output.d.ts +7 -0
  51. package/dist/src/commands/furnace/validation-output.js +22 -0
  52. package/dist/src/commands/import.d.ts +11 -0
  53. package/dist/src/commands/import.js +241 -0
  54. package/dist/src/commands/lint.d.ts +10 -0
  55. package/dist/src/commands/lint.js +118 -0
  56. package/dist/src/commands/package.d.ts +11 -0
  57. package/dist/src/commands/package.js +80 -0
  58. package/dist/src/commands/re-export.d.ts +12 -0
  59. package/dist/src/commands/re-export.js +242 -0
  60. package/dist/src/commands/rebase/abort.d.ts +7 -0
  61. package/dist/src/commands/rebase/abort.js +49 -0
  62. package/dist/src/commands/rebase/confirm.d.ts +18 -0
  63. package/dist/src/commands/rebase/confirm.js +33 -0
  64. package/dist/src/commands/rebase/continue.d.ts +7 -0
  65. package/dist/src/commands/rebase/continue.js +81 -0
  66. package/dist/src/commands/rebase/index.d.ts +22 -0
  67. package/dist/src/commands/rebase/index.js +127 -0
  68. package/dist/src/commands/rebase/patch-loop.d.ts +9 -0
  69. package/dist/src/commands/rebase/patch-loop.js +135 -0
  70. package/dist/src/commands/rebase/summary.d.ts +12 -0
  71. package/dist/src/commands/rebase/summary.js +43 -0
  72. package/dist/src/commands/rebase.d.ts +4 -0
  73. package/dist/src/commands/rebase.js +6 -0
  74. package/dist/src/commands/register.d.ts +13 -0
  75. package/dist/src/commands/register.js +67 -0
  76. package/dist/src/commands/reset.d.ts +11 -0
  77. package/dist/src/commands/reset.js +83 -0
  78. package/dist/src/commands/resolve.d.ts +9 -0
  79. package/dist/src/commands/resolve.js +124 -0
  80. package/dist/src/commands/run.d.ts +9 -0
  81. package/dist/src/commands/run.js +91 -0
  82. package/dist/src/commands/setup-support.d.ts +23 -0
  83. package/dist/src/commands/setup-support.js +310 -0
  84. package/dist/src/commands/setup.d.ts +11 -0
  85. package/dist/src/commands/setup.js +94 -0
  86. package/dist/src/commands/status.d.ts +11 -0
  87. package/dist/src/commands/status.js +268 -0
  88. package/dist/src/commands/test.d.ts +12 -0
  89. package/dist/src/commands/test.js +182 -0
  90. package/dist/src/commands/token-coverage.d.ts +5 -0
  91. package/dist/src/commands/token-coverage.js +57 -0
  92. package/dist/src/commands/token.d.ts +14 -0
  93. package/dist/src/commands/token.js +121 -0
  94. package/dist/src/commands/watch.d.ts +9 -0
  95. package/dist/src/commands/watch.js +112 -0
  96. package/dist/src/commands/wire.d.ts +13 -0
  97. package/dist/src/commands/wire.js +149 -0
  98. package/dist/src/core/ast-utils.d.ts +47 -0
  99. package/dist/src/core/ast-utils.js +57 -0
  100. package/dist/src/core/brand-validation.d.ts +7 -0
  101. package/dist/src/core/brand-validation.js +15 -0
  102. package/dist/src/core/branding.d.ts +49 -0
  103. package/dist/src/core/branding.js +229 -0
  104. package/dist/src/core/browser-wire.d.ts +40 -0
  105. package/dist/src/core/browser-wire.js +66 -0
  106. package/dist/src/core/build-prepare.d.ts +25 -0
  107. package/dist/src/core/build-prepare.js +93 -0
  108. package/dist/src/core/config-mutate.d.ts +15 -0
  109. package/dist/src/core/config-mutate.js +51 -0
  110. package/dist/src/core/config-paths.d.ts +28 -0
  111. package/dist/src/core/config-paths.js +65 -0
  112. package/dist/src/core/config-state.d.ts +28 -0
  113. package/dist/src/core/config-state.js +152 -0
  114. package/dist/src/core/config-validate.d.ts +11 -0
  115. package/dist/src/core/config-validate.js +141 -0
  116. package/dist/src/core/config.d.ts +39 -0
  117. package/dist/src/core/config.js +70 -0
  118. package/dist/src/core/file-lock.d.ts +11 -0
  119. package/dist/src/core/file-lock.js +80 -0
  120. package/dist/src/core/firefox-archive.d.ts +40 -0
  121. package/dist/src/core/firefox-archive.js +63 -0
  122. package/dist/src/core/firefox-cache.d.ts +23 -0
  123. package/dist/src/core/firefox-cache.js +134 -0
  124. package/dist/src/core/firefox-download.d.ts +21 -0
  125. package/dist/src/core/firefox-download.js +129 -0
  126. package/dist/src/core/firefox-extract.d.ts +21 -0
  127. package/dist/src/core/firefox-extract.js +53 -0
  128. package/dist/src/core/firefox.d.ts +34 -0
  129. package/dist/src/core/firefox.js +78 -0
  130. package/dist/src/core/furnace-apply-helpers.d.ts +21 -0
  131. package/dist/src/core/furnace-apply-helpers.js +244 -0
  132. package/dist/src/core/furnace-apply.d.ts +16 -0
  133. package/dist/src/core/furnace-apply.js +147 -0
  134. package/dist/src/core/furnace-config.d.ts +94 -0
  135. package/dist/src/core/furnace-config.js +372 -0
  136. package/dist/src/core/furnace-constants.d.ts +4 -0
  137. package/dist/src/core/furnace-constants.js +6 -0
  138. package/dist/src/core/furnace-registration-ast.d.ts +24 -0
  139. package/dist/src/core/furnace-registration-ast.js +218 -0
  140. package/dist/src/core/furnace-registration-remove.d.ts +14 -0
  141. package/dist/src/core/furnace-registration-remove.js +89 -0
  142. package/dist/src/core/furnace-registration-validate.d.ts +20 -0
  143. package/dist/src/core/furnace-registration-validate.js +40 -0
  144. package/dist/src/core/furnace-registration.d.ts +29 -0
  145. package/dist/src/core/furnace-registration.js +96 -0
  146. package/dist/src/core/furnace-rollback.d.ts +20 -0
  147. package/dist/src/core/furnace-rollback.js +66 -0
  148. package/dist/src/core/furnace-scanner.d.ts +40 -0
  149. package/dist/src/core/furnace-scanner.js +143 -0
  150. package/dist/src/core/furnace-stories.d.ts +37 -0
  151. package/dist/src/core/furnace-stories.js +185 -0
  152. package/dist/src/core/furnace-validate-accessibility.d.ts +6 -0
  153. package/dist/src/core/furnace-validate-accessibility.js +32 -0
  154. package/dist/src/core/furnace-validate-checks.d.ts +4 -0
  155. package/dist/src/core/furnace-validate-checks.js +7 -0
  156. package/dist/src/core/furnace-validate-compatibility.d.ts +6 -0
  157. package/dist/src/core/furnace-validate-compatibility.js +57 -0
  158. package/dist/src/core/furnace-validate-helpers.d.ts +28 -0
  159. package/dist/src/core/furnace-validate-helpers.js +129 -0
  160. package/dist/src/core/furnace-validate-registration.d.ts +37 -0
  161. package/dist/src/core/furnace-validate-registration.js +220 -0
  162. package/dist/src/core/furnace-validate-structure.d.ts +6 -0
  163. package/dist/src/core/furnace-validate-structure.js +66 -0
  164. package/dist/src/core/furnace-validate.d.ts +16 -0
  165. package/dist/src/core/furnace-validate.js +103 -0
  166. package/dist/src/core/git-base.d.ts +47 -0
  167. package/dist/src/core/git-base.js +50 -0
  168. package/dist/src/core/git-diff.d.ts +63 -0
  169. package/dist/src/core/git-diff.js +246 -0
  170. package/dist/src/core/git-file-ops.d.ts +65 -0
  171. package/dist/src/core/git-file-ops.js +141 -0
  172. package/dist/src/core/git-status.d.ts +65 -0
  173. package/dist/src/core/git-status.js +163 -0
  174. package/dist/src/core/git.d.ts +113 -0
  175. package/dist/src/core/git.js +363 -0
  176. package/dist/src/core/license-headers.d.ts +36 -0
  177. package/dist/src/core/license-headers.js +83 -0
  178. package/dist/src/core/mach-build-artifacts.d.ts +29 -0
  179. package/dist/src/core/mach-build-artifacts.js +117 -0
  180. package/dist/src/core/mach-mozconfig.d.ts +17 -0
  181. package/dist/src/core/mach-mozconfig.js +50 -0
  182. package/dist/src/core/mach-python.d.ts +16 -0
  183. package/dist/src/core/mach-python.js +126 -0
  184. package/dist/src/core/mach.d.ts +106 -0
  185. package/dist/src/core/mach.js +166 -0
  186. package/dist/src/core/manifest-helpers.d.ts +25 -0
  187. package/dist/src/core/manifest-helpers.js +96 -0
  188. package/dist/src/core/manifest-register.d.ts +30 -0
  189. package/dist/src/core/manifest-register.js +65 -0
  190. package/dist/src/core/manifest-rules.d.ts +39 -0
  191. package/dist/src/core/manifest-rules.js +151 -0
  192. package/dist/src/core/manifest-tokenizers.d.ts +34 -0
  193. package/dist/src/core/manifest-tokenizers.js +84 -0
  194. package/dist/src/core/parser-fallback.d.ts +36 -0
  195. package/dist/src/core/parser-fallback.js +43 -0
  196. package/dist/src/core/patch-apply-fuzz.d.ts +29 -0
  197. package/dist/src/core/patch-apply-fuzz.js +70 -0
  198. package/dist/src/core/patch-apply.d.ts +46 -0
  199. package/dist/src/core/patch-apply.js +235 -0
  200. package/dist/src/core/patch-export.d.ts +99 -0
  201. package/dist/src/core/patch-export.js +314 -0
  202. package/dist/src/core/patch-files.d.ts +11 -0
  203. package/dist/src/core/patch-files.js +51 -0
  204. package/dist/src/core/patch-lint.d.ts +72 -0
  205. package/dist/src/core/patch-lint.js +403 -0
  206. package/dist/src/core/patch-lock.d.ts +8 -0
  207. package/dist/src/core/patch-lock.js +29 -0
  208. package/dist/src/core/patch-manifest-consistency.d.ts +24 -0
  209. package/dist/src/core/patch-manifest-consistency.js +135 -0
  210. package/dist/src/core/patch-manifest-io.d.ts +36 -0
  211. package/dist/src/core/patch-manifest-io.js +77 -0
  212. package/dist/src/core/patch-manifest-query.d.ts +48 -0
  213. package/dist/src/core/patch-manifest-query.js +124 -0
  214. package/dist/src/core/patch-manifest-validate.d.ts +22 -0
  215. package/dist/src/core/patch-manifest-validate.js +72 -0
  216. package/dist/src/core/patch-manifest.d.ts +11 -0
  217. package/dist/src/core/patch-manifest.js +12 -0
  218. package/dist/src/core/patch-parse.d.ts +43 -0
  219. package/dist/src/core/patch-parse.js +143 -0
  220. package/dist/src/core/patch-transform.d.ts +21 -0
  221. package/dist/src/core/patch-transform.js +138 -0
  222. package/dist/src/core/rebase-session.d.ts +47 -0
  223. package/dist/src/core/rebase-session.js +65 -0
  224. package/dist/src/core/register-browser-content.d.ts +11 -0
  225. package/dist/src/core/register-browser-content.js +116 -0
  226. package/dist/src/core/register-module.d.ts +11 -0
  227. package/dist/src/core/register-module.js +76 -0
  228. package/dist/src/core/register-shared-css.d.ts +11 -0
  229. package/dist/src/core/register-shared-css.js +117 -0
  230. package/dist/src/core/register-test-manifest.d.ts +18 -0
  231. package/dist/src/core/register-test-manifest.js +99 -0
  232. package/dist/src/core/state-file.d.ts +4 -0
  233. package/dist/src/core/state-file.js +25 -0
  234. package/dist/src/core/token-coverage.d.ts +12 -0
  235. package/dist/src/core/token-coverage.js +74 -0
  236. package/dist/src/core/token-manager.d.ts +55 -0
  237. package/dist/src/core/token-manager.js +387 -0
  238. package/dist/src/core/wire-destroy.d.ts +21 -0
  239. package/dist/src/core/wire-destroy.js +103 -0
  240. package/dist/src/core/wire-dom-fragment.d.ts +23 -0
  241. package/dist/src/core/wire-dom-fragment.js +129 -0
  242. package/dist/src/core/wire-init.d.ts +23 -0
  243. package/dist/src/core/wire-init.js +201 -0
  244. package/dist/src/core/wire-subscript.d.ts +20 -0
  245. package/dist/src/core/wire-subscript.js +134 -0
  246. package/dist/src/core/wire-targets.d.ts +7 -0
  247. package/dist/src/core/wire-targets.js +9 -0
  248. package/dist/src/core/wire-utils.d.ts +88 -0
  249. package/dist/src/core/wire-utils.js +279 -0
  250. package/dist/src/errors/base.d.ts +60 -0
  251. package/dist/src/errors/base.js +87 -0
  252. package/dist/src/errors/build.d.ts +52 -0
  253. package/dist/src/errors/build.js +114 -0
  254. package/dist/src/errors/codes.d.ts +29 -0
  255. package/dist/src/errors/codes.js +30 -0
  256. package/dist/src/errors/config.d.ts +31 -0
  257. package/dist/src/errors/config.js +61 -0
  258. package/dist/src/errors/download.d.ts +42 -0
  259. package/dist/src/errors/download.js +95 -0
  260. package/dist/src/errors/furnace.d.ts +10 -0
  261. package/dist/src/errors/furnace.js +22 -0
  262. package/dist/src/errors/git.d.ts +41 -0
  263. package/dist/src/errors/git.js +99 -0
  264. package/dist/src/errors/patch.d.ts +10 -0
  265. package/dist/src/errors/patch.js +26 -0
  266. package/dist/src/errors/rebase.d.ts +20 -0
  267. package/dist/src/errors/rebase.js +30 -0
  268. package/dist/src/index.d.ts +21 -0
  269. package/dist/src/index.js +21 -0
  270. package/dist/src/types/cli.d.ts +14 -0
  271. package/dist/src/types/cli.js +2 -0
  272. package/dist/src/types/commands/index.d.ts +6 -0
  273. package/dist/src/types/commands/index.js +6 -0
  274. package/dist/src/types/commands/options.d.ts +239 -0
  275. package/dist/src/types/commands/options.js +6 -0
  276. package/dist/src/types/commands/patches.d.ts +89 -0
  277. package/dist/src/types/commands/patches.js +6 -0
  278. package/dist/src/types/commands/project.d.ts +71 -0
  279. package/dist/src/types/commands/project.js +6 -0
  280. package/dist/src/types/config.d.ts +101 -0
  281. package/dist/src/types/config.js +2 -0
  282. package/dist/src/types/furnace.d.ts +158 -0
  283. package/dist/src/types/furnace.js +2 -0
  284. package/dist/src/types/index.d.ts +6 -0
  285. package/dist/src/types/index.js +6 -0
  286. package/dist/src/utils/errors.d.ts +2 -0
  287. package/dist/src/utils/errors.js +15 -0
  288. package/dist/src/utils/fs.d.ts +72 -0
  289. package/dist/src/utils/fs.js +179 -0
  290. package/dist/src/utils/logger.d.ts +58 -0
  291. package/dist/src/utils/logger.js +120 -0
  292. package/dist/src/utils/options.d.ts +8 -0
  293. package/dist/src/utils/options.js +16 -0
  294. package/dist/src/utils/package-root.d.ts +10 -0
  295. package/dist/src/utils/package-root.js +53 -0
  296. package/dist/src/utils/parse.d.ts +110 -0
  297. package/dist/src/utils/parse.js +200 -0
  298. package/dist/src/utils/paths.d.ts +10 -0
  299. package/dist/src/utils/paths.js +43 -0
  300. package/dist/src/utils/platform.d.ts +38 -0
  301. package/dist/src/utils/platform.js +56 -0
  302. package/dist/src/utils/process.d.ts +80 -0
  303. package/dist/src/utils/process.js +188 -0
  304. package/dist/src/utils/regex.d.ts +24 -0
  305. package/dist/src/utils/regex.js +40 -0
  306. package/dist/src/utils/validation.d.ts +133 -0
  307. package/dist/src/utils/validation.js +250 -0
  308. package/package.json +106 -0
  309. package/templates/configs/common.mozconfig +24 -0
  310. package/templates/configs/darwin.mozconfig +10 -0
  311. package/templates/configs/linux.mozconfig +12 -0
  312. package/templates/configs/win32.mozconfig +14 -0
  313. package/templates/licenses/0BSD.md +14 -0
  314. package/templates/licenses/EUPL-1.2.md +294 -0
  315. package/templates/licenses/GPL-2.0-or-later.md +339 -0
  316. package/templates/licenses/MPL-2.0.md +383 -0
@@ -0,0 +1,112 @@
1
+ import { getProjectPaths, loadConfig } from '../core/config.js';
2
+ import { buildArtifactMismatchMessage, generateMozconfig, hasBuildArtifacts, watchWithOutput, } from '../core/mach.js';
3
+ import { GeneralError } from '../errors/base.js';
4
+ import { AmbiguousBuildArtifactsError, BuildError } from '../errors/build.js';
5
+ import { pathExists } from '../utils/fs.js';
6
+ import { info, intro, outro, spinner } from '../utils/logger.js';
7
+ import { executableExists } from '../utils/process.js';
8
+ /**
9
+ * Builds remediation guidance for objdirs configured before watchman was available.
10
+ * @returns User-facing configure-time watchman guidance
11
+ */
12
+ function buildWatchmanConfigureTimeMessage() {
13
+ return ('Watch mode cannot use the current obj-* build because watchman was not available when Firefox was configured.\n\n' +
14
+ 'Install watchman, delete the current obj-* directory, run "fireforge build" again, then retry "fireforge watch".');
15
+ }
16
+ /**
17
+ * Builds the generic unsupported-watch failure message.
18
+ * @param exitCode - Exit code returned by `mach watch`
19
+ * @returns User-facing failure guidance
20
+ */
21
+ function buildUnsupportedWatchMessage(exitCode) {
22
+ return (`Watch failed with exit code ${exitCode}. Check the output above for details.\n\n` +
23
+ 'Common causes:\n' +
24
+ ' - watchman is not installed or not in PATH right now\n' +
25
+ ' - watchman was installed only after the current obj-* directory was configured; delete obj-* and rebuild\n' +
26
+ ' - mach watch is unsupported in the current objdir or build environment');
27
+ }
28
+ /**
29
+ * Detects the Firefox-side output produced when watchman was missing at configure time.
30
+ * @param output - Combined stdout and stderr from the watch run
31
+ * @returns True when the output matches the configure-time watchman failure mode
32
+ */
33
+ function hasConfigureTimeWatchmanFailure(output) {
34
+ return (/watchman/i.test(output) &&
35
+ /(configure time|configured|configuration time|when (?:this|the current) build was configured)/i.test(output));
36
+ }
37
+ /**
38
+ * Runs the watch command for auto-rebuilding.
39
+ * @param projectRoot - Root directory of the project
40
+ */
41
+ export async function watchCommand(projectRoot) {
42
+ intro('FireForge Watch');
43
+ // Load configuration
44
+ const config = await loadConfig(projectRoot);
45
+ const paths = getProjectPaths(projectRoot);
46
+ // Check if engine exists
47
+ if (!(await pathExists(paths.engine))) {
48
+ throw new GeneralError('Firefox source not found. Run "fireforge download" first.');
49
+ }
50
+ if (!(await executableExists('watchman'))) {
51
+ throw new GeneralError('Watch mode requires watchman to be installed and available in PATH.\n\n' +
52
+ 'Install watchman first, then rerun "fireforge watch".');
53
+ }
54
+ // Check for build artifacts before starting watch
55
+ const buildCheck = await hasBuildArtifacts(paths.engine);
56
+ if (buildCheck.ambiguous && buildCheck.objDirs && buildCheck.objDirs.length > 0) {
57
+ throw new AmbiguousBuildArtifactsError(buildCheck.objDirs);
58
+ }
59
+ // Reject copied or relocated obj-* dirs whose mozinfo metadata (topsrcdir,
60
+ // topobjdir, mozconfig) still points at a different source tree. Running mach
61
+ // watch against stale metadata produces confusing build errors.
62
+ const mismatchMessage = buildArtifactMismatchMessage(paths.engine, buildCheck, 'Watch mode');
63
+ if (mismatchMessage) {
64
+ throw new GeneralError(mismatchMessage);
65
+ }
66
+ if (!buildCheck.exists) {
67
+ const detail = buildCheck.objDir
68
+ ? `Build artifacts incomplete in ${buildCheck.objDir}/`
69
+ : 'No build artifacts found (obj-*/ directory missing)';
70
+ throw new GeneralError(`Watch mode requires a completed build. ${detail}\n\n` +
71
+ "Run 'fireforge build' first to create the initial build, then run 'fireforge watch'.");
72
+ }
73
+ info(`Using build artifacts from ${buildCheck.objDir}/`);
74
+ // Generate mozconfig (in case it's not up to date)
75
+ const mozconfigSpinner = spinner('Generating mozconfig...');
76
+ try {
77
+ await generateMozconfig(paths.configs, paths.engine, config);
78
+ mozconfigSpinner.stop('mozconfig generated');
79
+ }
80
+ catch (error) {
81
+ mozconfigSpinner.error('Failed to generate mozconfig');
82
+ throw error;
83
+ }
84
+ info('Starting watch mode...');
85
+ info('Press Ctrl+C to stop\n');
86
+ let result;
87
+ try {
88
+ result = await watchWithOutput(paths.engine);
89
+ }
90
+ catch (error) {
91
+ throw new BuildError('Watch process failed to start', 'mach watch', error instanceof Error ? error : undefined);
92
+ }
93
+ if (result.exitCode !== 0 && result.exitCode !== 130) {
94
+ const combinedOutput = `${result.stdout}\n${result.stderr}`;
95
+ if (hasConfigureTimeWatchmanFailure(combinedOutput)) {
96
+ throw new GeneralError(buildWatchmanConfigureTimeMessage());
97
+ }
98
+ // 130 is SIGINT (Ctrl+C), which is expected
99
+ throw new BuildError(buildUnsupportedWatchMessage(result.exitCode), 'mach watch');
100
+ }
101
+ outro('Watch mode stopped');
102
+ }
103
+ /** Registers the watch command on the CLI program. */
104
+ export function registerWatch(program, { getProjectRoot, withErrorHandling }) {
105
+ program
106
+ .command('watch')
107
+ .description('Watch for changes and auto-rebuild')
108
+ .action(withErrorHandling(async () => {
109
+ await watchCommand(getProjectRoot());
110
+ }));
111
+ }
112
+ //# sourceMappingURL=watch.js.map
@@ -0,0 +1,13 @@
1
+ import { Command } from 'commander';
2
+ import type { CommandContext } from '../types/cli.js';
3
+ import type { WireOptions } from '../types/commands/index.js';
4
+ /**
5
+ * Wires a chrome subscript into the browser.
6
+ *
7
+ * @param projectRoot - Root directory of the project
8
+ * @param name - Subscript name (without .js extension)
9
+ * @param options - Command options
10
+ */
11
+ export declare function wireCommand(projectRoot: string, name: string, options?: WireOptions): Promise<void>;
12
+ /** Registers the wire command on the CLI program. */
13
+ export declare function registerWire(program: Command, { getProjectRoot, withErrorHandling }: CommandContext): void;
@@ -0,0 +1,149 @@
1
+ // SPDX-License-Identifier: EUPL-1.2
2
+ import { join, relative } from 'node:path';
3
+ import { DEFAULT_BROWSER_SUBSCRIPT_DIR, wireSubscript } from '../core/browser-wire.js';
4
+ import { getProjectPaths, loadConfig } from '../core/config.js';
5
+ import { consumeParserFallbackEvents } from '../core/parser-fallback.js';
6
+ import { InvalidArgumentError } from '../errors/base.js';
7
+ import { toError } from '../utils/errors.js';
8
+ import { pathExists } from '../utils/fs.js';
9
+ import { info, intro, outro, success, warn } from '../utils/logger.js';
10
+ import { pickDefined } from '../utils/options.js';
11
+ import { isContainedRelativePath, isPathInsideRoot, toRootRelativePath } from '../utils/paths.js';
12
+ const BROWSER_BASE_DIR = 'browser/base';
13
+ function printWireDryRun(engineDir, name, subscriptDir, domFilePath, options) {
14
+ info('[dry-run] Would wire subscript:');
15
+ info(` source: ${subscriptDir}/${name}.js`);
16
+ info(` browser-main.js: loadSubScript("chrome://browser/content/${name}.js")`);
17
+ if (options.init) {
18
+ info(` browser-init.js: ${options.init}`);
19
+ }
20
+ if (options.destroy) {
21
+ info(` browser-init.js onUnload(): ${options.destroy}`);
22
+ }
23
+ if (domFilePath) {
24
+ const includePath = relative(join(engineDir, subscriptDir), join(engineDir, domFilePath)).replace(/\\/g, '/');
25
+ info(` browser.xhtml: #include ${includePath}`);
26
+ }
27
+ const relPath = relative(join(engineDir, BROWSER_BASE_DIR), join(engineDir, subscriptDir)).replace(/\\/g, '/');
28
+ info(` jar.mn: content/browser/${name}.js (${relPath}/${name}.js)`);
29
+ outro('Dry run complete');
30
+ }
31
+ /**
32
+ * Wires a chrome subscript into the browser.
33
+ *
34
+ * @param projectRoot - Root directory of the project
35
+ * @param name - Subscript name (without .js extension)
36
+ * @param options - Command options
37
+ */
38
+ export async function wireCommand(projectRoot, name, options = {}) {
39
+ intro('Wire');
40
+ consumeParserFallbackEvents();
41
+ // Resolve subscript directory: CLI flag > fireforge.json > default
42
+ let subscriptDir = DEFAULT_BROWSER_SUBSCRIPT_DIR;
43
+ try {
44
+ const config = await loadConfig(projectRoot);
45
+ if (config.wire?.subscriptDir) {
46
+ subscriptDir = config.wire.subscriptDir;
47
+ }
48
+ }
49
+ catch (error) {
50
+ warn(`Using default wire.subscriptDir because fireforge.json could not be loaded: ${toError(error).message}`);
51
+ }
52
+ if (options.subscriptDir) {
53
+ if (!isContainedRelativePath(options.subscriptDir)) {
54
+ throw new InvalidArgumentError(`Subscript directory must stay within engine/: ${options.subscriptDir}`, 'subscriptDir');
55
+ }
56
+ subscriptDir = options.subscriptDir;
57
+ }
58
+ // Validate DOM fragment file exists and compute path relative to engine root
59
+ let domFilePath;
60
+ if (options.dom) {
61
+ const paths = getProjectPaths(projectRoot);
62
+ if (!(await pathExists(options.dom))) {
63
+ throw new InvalidArgumentError(`DOM fragment file not found: ${options.dom}`, 'dom');
64
+ }
65
+ if (!isPathInsideRoot(paths.engine, options.dom)) {
66
+ throw new InvalidArgumentError(`DOM fragment file must stay within engine/: ${options.dom}`, 'dom');
67
+ }
68
+ domFilePath = toRootRelativePath(paths.engine, options.dom);
69
+ }
70
+ // Verify the subscript file exists in engine/ (skip for dry-run)
71
+ if (!options.dryRun) {
72
+ const paths = getProjectPaths(projectRoot);
73
+ const subscriptPath = join(paths.engine, subscriptDir, `${name}.js`);
74
+ if (!(await pathExists(subscriptPath))) {
75
+ throw new InvalidArgumentError(`Subscript file not found: ${subscriptDir}/${name}.js\n` +
76
+ 'Create the file in engine/ before wiring.', 'name');
77
+ }
78
+ }
79
+ if (options.dryRun) {
80
+ printWireDryRun(getProjectPaths(projectRoot).engine, name, subscriptDir, domFilePath, options);
81
+ return;
82
+ }
83
+ const result = await wireSubscript(projectRoot, name, {
84
+ ...(options.init !== undefined ? { init: options.init } : {}),
85
+ ...(options.destroy !== undefined ? { destroy: options.destroy } : {}),
86
+ ...(domFilePath !== undefined ? { domFilePath } : {}),
87
+ ...(options.after !== undefined ? { after: options.after } : {}),
88
+ ...(subscriptDir !== DEFAULT_BROWSER_SUBSCRIPT_DIR ? { subscriptDir } : {}),
89
+ dryRun: false,
90
+ });
91
+ const parserFallbacks = consumeParserFallbackEvents();
92
+ if (parserFallbacks.length > 0) {
93
+ const contexts = [...new Set(parserFallbacks.map((event) => event.context))];
94
+ info(`Legacy parser fallback was used for ${contexts.length} file${contexts.length === 1 ? '' : 's'}: ${contexts.join(', ')}`);
95
+ }
96
+ if (result.subscriptAdded) {
97
+ success(`Added loadSubScript for ${name}.js to browser-main.js`);
98
+ }
99
+ else {
100
+ info(`${name}.js already registered in browser-main.js (skipped)`);
101
+ }
102
+ if (options.init) {
103
+ if (result.initAdded) {
104
+ success(`Added init expression to browser-init.js onLoad()`);
105
+ }
106
+ else {
107
+ info(`Init expression already present in browser-init.js (skipped)`);
108
+ }
109
+ }
110
+ if (options.destroy) {
111
+ if (result.destroyAdded) {
112
+ success(`Added destroy expression to browser-init.js onUnload()`);
113
+ }
114
+ else {
115
+ info(`Destroy expression already present in browser-init.js (skipped)`);
116
+ }
117
+ }
118
+ if (domFilePath) {
119
+ if (result.domInserted) {
120
+ success(`Inserted #include directive into browser.xhtml`);
121
+ }
122
+ else {
123
+ info(`#include directive already present in browser.xhtml (skipped)`);
124
+ }
125
+ }
126
+ if (result.jarMnResult.skipped) {
127
+ info(`${name}.js already registered in jar.mn (skipped)`);
128
+ }
129
+ else {
130
+ success(`Registered ${name}.js in ${result.jarMnResult.manifest}`);
131
+ }
132
+ outro('Wiring complete');
133
+ }
134
+ /** Registers the wire command on the CLI program. */
135
+ export function registerWire(program, { getProjectRoot, withErrorHandling }) {
136
+ program
137
+ .command('wire <name>')
138
+ .description('Wire a chrome subscript into the browser')
139
+ .option('--init <expression>', 'Init expression for browser-init.js onLoad()')
140
+ .option('--destroy <expression>', 'Destroy expression for browser-init.js onUnload()')
141
+ .option('--dom <file>', 'XHTML fragment file to insert into browser.xhtml')
142
+ .option('--dry-run', 'Show what would be changed without writing')
143
+ .option('--after <name>', 'Insert init block after the block for this name')
144
+ .option('--subscript-dir <dir>', 'Subscript directory relative to engine/ (default: browser/base/content)')
145
+ .action(withErrorHandling(async (name, options) => {
146
+ await wireCommand(getProjectRoot(), name, pickDefined(options));
147
+ }));
148
+ }
149
+ //# sourceMappingURL=wire.js.map
@@ -0,0 +1,47 @@
1
+ import type * as estree from 'estree';
2
+ import { walk } from 'estree-walker';
3
+ /**
4
+ * An ESTree node augmented with acorn's character-offset positions.
5
+ * At runtime `acorn.parse` produces objects that carry both the ESTree
6
+ * shape *and* `start`/`end` indices, but the type system doesn't know that.
7
+ * This intersection type bridges the gap so we can safely use both APIs.
8
+ */
9
+ export type AcornESTreeNode<T extends estree.Node = estree.Node> = T & {
10
+ start: number;
11
+ end: number;
12
+ };
13
+ /**
14
+ * Parse JavaScript source as a **script** (not an ES module).
15
+ * All Mozilla chrome JS files (`browser-main.js`, `browser-init.js`,
16
+ * `customElements.js`, etc.) are scripts that run in a privileged scope.
17
+ */
18
+ export declare function parseScript(content: string): AcornESTreeNode<estree.Program>;
19
+ /**
20
+ * Convenience cast from `acorn.Node` (or the generic ESTree union returned
21
+ * by estree-walker callbacks) to a positioned, narrowly-typed node.
22
+ */
23
+ export declare function asEstree<T extends estree.Node>(node: estree.Node): AcornESTreeNode<T>;
24
+ /**
25
+ * Type-safe wrapper around estree-walker's `walk()` that bridges the
26
+ * acorn→estree type gap. Centralises the single `as unknown as` cast so
27
+ * callers don't need it.
28
+ */
29
+ export declare function walkAST(ast: AcornESTreeNode<estree.Program>, visitors: Parameters<typeof walk>[1]): ReturnType<typeof walk>;
30
+ /**
31
+ * Read backward from `position` to the preceding newline (or start of
32
+ * string) and return the leading whitespace. This is the "visual indent"
33
+ * of whatever token begins at `position`.
34
+ *
35
+ * ```
36
+ * " try {\n foo();\n }"
37
+ * ^ detectIndent(…, 26) → " "
38
+ * ```
39
+ */
40
+ export declare function detectIndent(content: string, position: number): string;
41
+ /**
42
+ * Extract the raw source text for a node's range.
43
+ */
44
+ export declare function getNodeSource(content: string, node: {
45
+ start: number;
46
+ end: number;
47
+ }): string;
@@ -0,0 +1,57 @@
1
+ // SPDX-License-Identifier: EUPL-1.2
2
+ import * as acorn from 'acorn';
3
+ import { walk } from 'estree-walker';
4
+ /**
5
+ * Parse JavaScript source as a **script** (not an ES module).
6
+ * All Mozilla chrome JS files (`browser-main.js`, `browser-init.js`,
7
+ * `customElements.js`, etc.) are scripts that run in a privileged scope.
8
+ */
9
+ export function parseScript(content) {
10
+ return acorn.parse(content, {
11
+ sourceType: 'script',
12
+ ecmaVersion: 'latest',
13
+ });
14
+ }
15
+ /**
16
+ * Convenience cast from `acorn.Node` (or the generic ESTree union returned
17
+ * by estree-walker callbacks) to a positioned, narrowly-typed node.
18
+ */
19
+ export function asEstree(node) {
20
+ return node;
21
+ }
22
+ /**
23
+ * Type-safe wrapper around estree-walker's `walk()` that bridges the
24
+ * acorn→estree type gap. Centralises the single `as unknown as` cast so
25
+ * callers don't need it.
26
+ */
27
+ export function walkAST(ast, visitors) {
28
+ return walk(ast, visitors);
29
+ }
30
+ /**
31
+ * Read backward from `position` to the preceding newline (or start of
32
+ * string) and return the leading whitespace. This is the "visual indent"
33
+ * of whatever token begins at `position`.
34
+ *
35
+ * ```
36
+ * " try {\n foo();\n }"
37
+ * ^ detectIndent(…, 26) → " "
38
+ * ```
39
+ */
40
+ export function detectIndent(content, position) {
41
+ let i = position - 1;
42
+ while (i >= 0 && content[i] !== '\n') {
43
+ i--;
44
+ }
45
+ // i is now at the newline (or -1 for start-of-string)
46
+ const lineStart = i + 1;
47
+ const slice = content.slice(lineStart, position);
48
+ const match = /^(\s*)/.exec(slice);
49
+ return match?.[1] ?? '';
50
+ }
51
+ /**
52
+ * Extract the raw source text for a node's range.
53
+ */
54
+ export function getNodeSource(content, node) {
55
+ return content.slice(node.start, node.end);
56
+ }
57
+ //# sourceMappingURL=ast-utils.js.map
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Validates that a brand override matches the configured brand.
3
+ * @param configuredBrand - The brand configured in fireforge.json
4
+ * @param requestedBrand - The brand requested via CLI flag
5
+ * @throws InvalidArgumentError if the brands don't match
6
+ */
7
+ export declare function validateBrandOverride(configuredBrand: string, requestedBrand?: string): void;
@@ -0,0 +1,15 @@
1
+ // SPDX-License-Identifier: EUPL-1.2
2
+ import { InvalidArgumentError } from '../errors/base.js';
3
+ /**
4
+ * Validates that a brand override matches the configured brand.
5
+ * @param configuredBrand - The brand configured in fireforge.json
6
+ * @param requestedBrand - The brand requested via CLI flag
7
+ * @throws InvalidArgumentError if the brands don't match
8
+ */
9
+ export function validateBrandOverride(configuredBrand, requestedBrand) {
10
+ if (!requestedBrand || requestedBrand === configuredBrand) {
11
+ return;
12
+ }
13
+ throw new InvalidArgumentError(`Brand override "${requestedBrand}" is not supported yet. FireForge currently operates only on the configured brand "${configuredBrand}".`, 'brand');
14
+ }
15
+ //# sourceMappingURL=brand-validation.js.map
@@ -0,0 +1,49 @@
1
+ import { FireForgeError } from '../errors/base.js';
2
+ /**
3
+ * Error thrown when branding operations fail.
4
+ */
5
+ export declare class BrandingError extends FireForgeError {
6
+ readonly code: 6;
7
+ get userMessage(): string;
8
+ }
9
+ /**
10
+ * Full branding configuration.
11
+ */
12
+ export interface BrandingConfig {
13
+ /** Display name (e.g., "MyBrowser") */
14
+ name: string;
15
+ /** Vendor name (e.g., "My Company") */
16
+ vendor: string;
17
+ /** Application ID in reverse-domain format (e.g., "org.mybrowser.browser") */
18
+ appId: string;
19
+ /** Binary/branding directory name (e.g., "mybrowser") */
20
+ binaryName: string;
21
+ }
22
+ /**
23
+ * Sets up the custom branding directory for the browser.
24
+ *
25
+ * This creates a branding directory based on Firefox's unofficial branding,
26
+ * with customized values for:
27
+ * - configure.sh: MOZ_APP_DISPLAYNAME, MOZ_MACBUNDLE_ID
28
+ * - brand.properties: brandShorterName, brandShortName, brandFullName
29
+ * - brand.ftl: -brand-shorter-name, -brand-short-name, etc.
30
+ *
31
+ * @param engineDir - Path to the engine directory
32
+ * @param config - Branding configuration
33
+ */
34
+ export declare function setupBranding(engineDir: string, config: BrandingConfig): Promise<void>;
35
+ /**
36
+ * Checks if branding has been set up for the given configuration.
37
+ *
38
+ * @param engineDir - Path to the engine directory
39
+ * @param config - Branding configuration to check for
40
+ * @returns true if branding is already set up
41
+ */
42
+ export declare function isBrandingSetup(engineDir: string, config: BrandingConfig): Promise<boolean>;
43
+ /**
44
+ * Checks whether a file path belongs to the tool-managed branding directory.
45
+ * @param file - File path (relative to engine root)
46
+ * @param binaryName - The configured binary name (used as branding directory name)
47
+ * @returns true if the path is managed by branding tooling
48
+ */
49
+ export declare function isBrandingManagedPath(file: string, binaryName: string): boolean;