@fuzdev/gro 0.192.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 (323) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +283 -0
  3. package/dist/args.d.ts +37 -0
  4. package/dist/args.d.ts.map +1 -0
  5. package/dist/args.js +102 -0
  6. package/dist/build.task.d.ts +20 -0
  7. package/dist/build.task.d.ts.map +1 -0
  8. package/dist/build.task.js +119 -0
  9. package/dist/build_cache.d.ts +100 -0
  10. package/dist/build_cache.d.ts.map +1 -0
  11. package/dist/build_cache.js +299 -0
  12. package/dist/changelog.d.ts +11 -0
  13. package/dist/changelog.d.ts.map +1 -0
  14. package/dist/changelog.js +47 -0
  15. package/dist/changeset.task.d.ts +35 -0
  16. package/dist/changeset.task.d.ts.map +1 -0
  17. package/dist/changeset.task.js +151 -0
  18. package/dist/changeset_helpers.d.ts +17 -0
  19. package/dist/changeset_helpers.d.ts.map +1 -0
  20. package/dist/changeset_helpers.js +7 -0
  21. package/dist/check.task.d.ts +28 -0
  22. package/dist/check.task.d.ts.map +1 -0
  23. package/dist/check.task.js +104 -0
  24. package/dist/child_process_logging.d.ts +10 -0
  25. package/dist/child_process_logging.d.ts.map +1 -0
  26. package/dist/child_process_logging.js +26 -0
  27. package/dist/clean.task.d.ts +15 -0
  28. package/dist/clean.task.d.ts.map +1 -0
  29. package/dist/clean.task.js +40 -0
  30. package/dist/clean_fs.d.ts +9 -0
  31. package/dist/clean_fs.d.ts.map +1 -0
  32. package/dist/clean_fs.js +28 -0
  33. package/dist/cli.d.ts +34 -0
  34. package/dist/cli.d.ts.map +1 -0
  35. package/dist/cli.js +61 -0
  36. package/dist/commit.task.d.ts +11 -0
  37. package/dist/commit.task.d.ts.map +1 -0
  38. package/dist/commit.task.js +24 -0
  39. package/dist/constants.d.ts +46 -0
  40. package/dist/constants.d.ts.map +1 -0
  41. package/dist/constants.js +52 -0
  42. package/dist/deploy.task.d.ts +29 -0
  43. package/dist/deploy.task.d.ts.map +1 -0
  44. package/dist/deploy.task.js +217 -0
  45. package/dist/dev.task.d.ts +16 -0
  46. package/dist/dev.task.d.ts.map +1 -0
  47. package/dist/dev.task.js +44 -0
  48. package/dist/disknode.d.ts +23 -0
  49. package/dist/disknode.d.ts.map +1 -0
  50. package/dist/disknode.js +1 -0
  51. package/dist/env.d.ts +11 -0
  52. package/dist/env.d.ts.map +1 -0
  53. package/dist/env.js +49 -0
  54. package/dist/esbuild_helpers.d.ts +16 -0
  55. package/dist/esbuild_helpers.d.ts.map +1 -0
  56. package/dist/esbuild_helpers.js +36 -0
  57. package/dist/esbuild_plugin_external_worker.d.ts +23 -0
  58. package/dist/esbuild_plugin_external_worker.d.ts.map +1 -0
  59. package/dist/esbuild_plugin_external_worker.js +55 -0
  60. package/dist/esbuild_plugin_svelte.d.ts +15 -0
  61. package/dist/esbuild_plugin_svelte.d.ts.map +1 -0
  62. package/dist/esbuild_plugin_svelte.js +83 -0
  63. package/dist/esbuild_plugin_sveltekit_local_imports.d.ts +8 -0
  64. package/dist/esbuild_plugin_sveltekit_local_imports.d.ts.map +1 -0
  65. package/dist/esbuild_plugin_sveltekit_local_imports.js +30 -0
  66. package/dist/esbuild_plugin_sveltekit_shim_alias.d.ts +7 -0
  67. package/dist/esbuild_plugin_sveltekit_shim_alias.d.ts.map +1 -0
  68. package/dist/esbuild_plugin_sveltekit_shim_alias.js +18 -0
  69. package/dist/esbuild_plugin_sveltekit_shim_app.d.ts +9 -0
  70. package/dist/esbuild_plugin_sveltekit_shim_app.d.ts.map +1 -0
  71. package/dist/esbuild_plugin_sveltekit_shim_app.js +22 -0
  72. package/dist/esbuild_plugin_sveltekit_shim_env.d.ts +11 -0
  73. package/dist/esbuild_plugin_sveltekit_shim_env.d.ts.map +1 -0
  74. package/dist/esbuild_plugin_sveltekit_shim_env.js +18 -0
  75. package/dist/filer.d.ts +33 -0
  76. package/dist/filer.d.ts.map +1 -0
  77. package/dist/filer.js +385 -0
  78. package/dist/format.task.d.ts +11 -0
  79. package/dist/format.task.d.ts.map +1 -0
  80. package/dist/format.task.js +27 -0
  81. package/dist/format_directory.d.ts +13 -0
  82. package/dist/format_directory.d.ts.map +1 -0
  83. package/dist/format_directory.js +40 -0
  84. package/dist/format_file.d.ts +9 -0
  85. package/dist/format_file.d.ts.map +1 -0
  86. package/dist/format_file.js +42 -0
  87. package/dist/gen.d.ts +142 -0
  88. package/dist/gen.d.ts.map +1 -0
  89. package/dist/gen.js +199 -0
  90. package/dist/gen.task.d.ts +12 -0
  91. package/dist/gen.task.d.ts.map +1 -0
  92. package/dist/gen.task.js +149 -0
  93. package/dist/gen_helpers.d.ts +11 -0
  94. package/dist/gen_helpers.d.ts.map +1 -0
  95. package/dist/gen_helpers.js +76 -0
  96. package/dist/github.d.ts +19 -0
  97. package/dist/github.d.ts.map +1 -0
  98. package/dist/github.js +33 -0
  99. package/dist/gro.config.default.d.ts +13 -0
  100. package/dist/gro.config.default.d.ts.map +1 -0
  101. package/dist/gro.config.default.js +33 -0
  102. package/dist/gro.d.ts +3 -0
  103. package/dist/gro.d.ts.map +1 -0
  104. package/dist/gro.js +21 -0
  105. package/dist/gro_config.d.ts +115 -0
  106. package/dist/gro_config.d.ts.map +1 -0
  107. package/dist/gro_config.js +114 -0
  108. package/dist/gro_helpers.d.ts +49 -0
  109. package/dist/gro_helpers.d.ts.map +1 -0
  110. package/dist/gro_helpers.js +97 -0
  111. package/dist/gro_plugin_gen.d.ts +12 -0
  112. package/dist/gro_plugin_gen.d.ts.map +1 -0
  113. package/dist/gro_plugin_gen.js +101 -0
  114. package/dist/gro_plugin_server.d.ts +80 -0
  115. package/dist/gro_plugin_server.d.ts.map +1 -0
  116. package/dist/gro_plugin_server.js +167 -0
  117. package/dist/gro_plugin_sveltekit_app.d.ts +9 -0
  118. package/dist/gro_plugin_sveltekit_app.d.ts.map +1 -0
  119. package/dist/gro_plugin_sveltekit_app.js +42 -0
  120. package/dist/gro_plugin_sveltekit_library.d.ts +16 -0
  121. package/dist/gro_plugin_sveltekit_library.d.ts.map +1 -0
  122. package/dist/gro_plugin_sveltekit_library.js +34 -0
  123. package/dist/index.d.ts +9 -0
  124. package/dist/index.d.ts.map +1 -0
  125. package/dist/index.js +4 -0
  126. package/dist/input_path.d.ts +64 -0
  127. package/dist/input_path.d.ts.map +1 -0
  128. package/dist/input_path.js +199 -0
  129. package/dist/invoke.d.ts +2 -0
  130. package/dist/invoke.d.ts.map +1 -0
  131. package/dist/invoke.js +28 -0
  132. package/dist/invoke_task.d.ts +30 -0
  133. package/dist/invoke_task.d.ts.map +1 -0
  134. package/dist/invoke_task.js +104 -0
  135. package/dist/lint.task.d.ts +11 -0
  136. package/dist/lint.task.d.ts.map +1 -0
  137. package/dist/lint.task.js +32 -0
  138. package/dist/loader.d.ts +6 -0
  139. package/dist/loader.d.ts.map +1 -0
  140. package/dist/loader.js +192 -0
  141. package/dist/module.d.ts +4 -0
  142. package/dist/module.d.ts.map +1 -0
  143. package/dist/module.js +6 -0
  144. package/dist/modules.d.ts +36 -0
  145. package/dist/modules.d.ts.map +1 -0
  146. package/dist/modules.js +71 -0
  147. package/dist/package_json.d.ts +32 -0
  148. package/dist/package_json.d.ts.map +1 -0
  149. package/dist/package_json.js +178 -0
  150. package/dist/parse_exports.d.ts +20 -0
  151. package/dist/parse_exports.d.ts.map +1 -0
  152. package/dist/parse_exports.js +65 -0
  153. package/dist/parse_exports_context.d.ts +21 -0
  154. package/dist/parse_exports_context.d.ts.map +1 -0
  155. package/dist/parse_exports_context.js +332 -0
  156. package/dist/parse_imports.d.ts +5 -0
  157. package/dist/parse_imports.d.ts.map +1 -0
  158. package/dist/parse_imports.js +140 -0
  159. package/dist/paths.d.ts +41 -0
  160. package/dist/paths.d.ts.map +1 -0
  161. package/dist/paths.js +69 -0
  162. package/dist/plugin.d.ts +36 -0
  163. package/dist/plugin.d.ts.map +1 -0
  164. package/dist/plugin.js +78 -0
  165. package/dist/publish.task.d.ts +26 -0
  166. package/dist/publish.task.d.ts.map +1 -0
  167. package/dist/publish.task.js +176 -0
  168. package/dist/register.d.ts +2 -0
  169. package/dist/register.d.ts.map +1 -0
  170. package/dist/register.js +2 -0
  171. package/dist/reinstall.task.d.ts +8 -0
  172. package/dist/reinstall.task.d.ts.map +1 -0
  173. package/dist/reinstall.task.js +35 -0
  174. package/dist/release.task.d.ts +8 -0
  175. package/dist/release.task.d.ts.map +1 -0
  176. package/dist/release.task.js +20 -0
  177. package/dist/resolve.task.d.ts +11 -0
  178. package/dist/resolve.task.d.ts.map +1 -0
  179. package/dist/resolve.task.js +38 -0
  180. package/dist/resolve_specifier.d.ts +22 -0
  181. package/dist/resolve_specifier.d.ts.map +1 -0
  182. package/dist/resolve_specifier.js +57 -0
  183. package/dist/run.task.d.ts +16 -0
  184. package/dist/run.task.d.ts.map +1 -0
  185. package/dist/run.task.js +52 -0
  186. package/dist/run_gen.d.ts +10 -0
  187. package/dist/run_gen.d.ts.map +1 -0
  188. package/dist/run_gen.js +73 -0
  189. package/dist/run_task.d.ts +17 -0
  190. package/dist/run_task.d.ts.map +1 -0
  191. package/dist/run_task.js +45 -0
  192. package/dist/source_json.d.ts +7 -0
  193. package/dist/source_json.d.ts.map +1 -0
  194. package/dist/source_json.js +145 -0
  195. package/dist/svelte_config.d.ts +57 -0
  196. package/dist/svelte_config.d.ts.map +1 -0
  197. package/dist/svelte_config.js +81 -0
  198. package/dist/sveltekit_helpers.d.ts +75 -0
  199. package/dist/sveltekit_helpers.d.ts.map +1 -0
  200. package/dist/sveltekit_helpers.js +94 -0
  201. package/dist/sveltekit_shim_app.d.ts +11 -0
  202. package/dist/sveltekit_shim_app.d.ts.map +1 -0
  203. package/dist/sveltekit_shim_app.js +31 -0
  204. package/dist/sveltekit_shim_app_environment.d.ts +13 -0
  205. package/dist/sveltekit_shim_app_environment.d.ts.map +1 -0
  206. package/dist/sveltekit_shim_app_environment.js +14 -0
  207. package/dist/sveltekit_shim_app_forms.d.ts +5 -0
  208. package/dist/sveltekit_shim_app_forms.d.ts.map +1 -0
  209. package/dist/sveltekit_shim_app_forms.js +6 -0
  210. package/dist/sveltekit_shim_app_navigation.d.ts +10 -0
  211. package/dist/sveltekit_shim_app_navigation.d.ts.map +1 -0
  212. package/dist/sveltekit_shim_app_navigation.js +11 -0
  213. package/dist/sveltekit_shim_app_paths.d.ts +17 -0
  214. package/dist/sveltekit_shim_app_paths.d.ts.map +1 -0
  215. package/dist/sveltekit_shim_app_paths.js +10 -0
  216. package/dist/sveltekit_shim_app_state.d.ts +5 -0
  217. package/dist/sveltekit_shim_app_state.d.ts.map +1 -0
  218. package/dist/sveltekit_shim_app_state.js +26 -0
  219. package/dist/sveltekit_shim_env.d.ts +5 -0
  220. package/dist/sveltekit_shim_env.d.ts.map +1 -0
  221. package/dist/sveltekit_shim_env.js +23 -0
  222. package/dist/sync.task.d.ts +16 -0
  223. package/dist/sync.task.d.ts.map +1 -0
  224. package/dist/sync.task.js +39 -0
  225. package/dist/task.d.ts +98 -0
  226. package/dist/task.d.ts.map +1 -0
  227. package/dist/task.js +109 -0
  228. package/dist/task_logging.d.ts +6 -0
  229. package/dist/task_logging.d.ts.map +1 -0
  230. package/dist/task_logging.js +201 -0
  231. package/dist/test.task.d.ts +13 -0
  232. package/dist/test.task.d.ts.map +1 -0
  233. package/dist/test.task.js +53 -0
  234. package/dist/typecheck.task.d.ts +13 -0
  235. package/dist/typecheck.task.d.ts.map +1 -0
  236. package/dist/typecheck.task.js +68 -0
  237. package/dist/upgrade.task.d.ts +20 -0
  238. package/dist/upgrade.task.d.ts.map +1 -0
  239. package/dist/upgrade.task.js +111 -0
  240. package/dist/watch_dir.d.ts +36 -0
  241. package/dist/watch_dir.d.ts.map +1 -0
  242. package/dist/watch_dir.js +69 -0
  243. package/package.json +149 -0
  244. package/src/lib/args.ts +115 -0
  245. package/src/lib/build.task.ts +151 -0
  246. package/src/lib/build_cache.ts +378 -0
  247. package/src/lib/changelog.ts +69 -0
  248. package/src/lib/changeset.task.ts +228 -0
  249. package/src/lib/changeset_helpers.ts +14 -0
  250. package/src/lib/check.task.ts +132 -0
  251. package/src/lib/child_process_logging.ts +38 -0
  252. package/src/lib/clean.task.ts +48 -0
  253. package/src/lib/clean_fs.ts +54 -0
  254. package/src/lib/cli.ts +98 -0
  255. package/src/lib/commit.task.ts +34 -0
  256. package/src/lib/constants.ts +56 -0
  257. package/src/lib/deploy.task.ts +287 -0
  258. package/src/lib/dev.task.ts +52 -0
  259. package/src/lib/disknode.ts +26 -0
  260. package/src/lib/env.ts +78 -0
  261. package/src/lib/esbuild_helpers.ts +49 -0
  262. package/src/lib/esbuild_plugin_external_worker.ts +94 -0
  263. package/src/lib/esbuild_plugin_svelte.ts +134 -0
  264. package/src/lib/esbuild_plugin_sveltekit_local_imports.ts +38 -0
  265. package/src/lib/esbuild_plugin_sveltekit_shim_alias.ts +27 -0
  266. package/src/lib/esbuild_plugin_sveltekit_shim_app.ts +42 -0
  267. package/src/lib/esbuild_plugin_sveltekit_shim_env.ts +47 -0
  268. package/src/lib/filer.ts +458 -0
  269. package/src/lib/format.task.ts +44 -0
  270. package/src/lib/format_directory.ts +65 -0
  271. package/src/lib/format_file.ts +49 -0
  272. package/src/lib/gen.task.ts +206 -0
  273. package/src/lib/gen.ts +406 -0
  274. package/src/lib/gen_helpers.ts +131 -0
  275. package/src/lib/github.ts +46 -0
  276. package/src/lib/gro.config.default.ts +42 -0
  277. package/src/lib/gro.ts +29 -0
  278. package/src/lib/gro_config.ts +254 -0
  279. package/src/lib/gro_helpers.ts +108 -0
  280. package/src/lib/gro_plugin_gen.ts +149 -0
  281. package/src/lib/gro_plugin_server.ts +288 -0
  282. package/src/lib/gro_plugin_sveltekit_app.ts +58 -0
  283. package/src/lib/gro_plugin_sveltekit_library.ts +63 -0
  284. package/src/lib/index.ts +8 -0
  285. package/src/lib/input_path.ts +254 -0
  286. package/src/lib/invoke.ts +34 -0
  287. package/src/lib/invoke_task.ts +139 -0
  288. package/src/lib/lint.task.ts +39 -0
  289. package/src/lib/loader.ts +229 -0
  290. package/src/lib/module.ts +13 -0
  291. package/src/lib/modules.ts +117 -0
  292. package/src/lib/package_json.ts +255 -0
  293. package/src/lib/parse_exports.ts +100 -0
  294. package/src/lib/parse_exports_context.ts +395 -0
  295. package/src/lib/parse_imports.ts +180 -0
  296. package/src/lib/paths.ts +111 -0
  297. package/src/lib/plugin.ts +106 -0
  298. package/src/lib/publish.task.ts +228 -0
  299. package/src/lib/register.ts +3 -0
  300. package/src/lib/reinstall.task.ts +45 -0
  301. package/src/lib/release.task.ts +26 -0
  302. package/src/lib/resolve.task.ts +43 -0
  303. package/src/lib/resolve_specifier.ts +81 -0
  304. package/src/lib/run.task.ts +65 -0
  305. package/src/lib/run_gen.ts +110 -0
  306. package/src/lib/run_task.ts +82 -0
  307. package/src/lib/source_json.ts +183 -0
  308. package/src/lib/svelte_config.ts +140 -0
  309. package/src/lib/sveltekit_helpers.ts +193 -0
  310. package/src/lib/sveltekit_shim_app.ts +41 -0
  311. package/src/lib/sveltekit_shim_app_environment.ts +16 -0
  312. package/src/lib/sveltekit_shim_app_forms.ts +13 -0
  313. package/src/lib/sveltekit_shim_app_navigation.ts +23 -0
  314. package/src/lib/sveltekit_shim_app_paths.ts +26 -0
  315. package/src/lib/sveltekit_shim_app_state.ts +35 -0
  316. package/src/lib/sveltekit_shim_env.ts +45 -0
  317. package/src/lib/sync.task.ts +47 -0
  318. package/src/lib/task.ts +245 -0
  319. package/src/lib/task_logging.ts +255 -0
  320. package/src/lib/test.task.ts +63 -0
  321. package/src/lib/typecheck.task.ts +81 -0
  322. package/src/lib/upgrade.task.ts +148 -0
  323. package/src/lib/watch_dir.ts +115 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) Ryan Atkinson <mail@ryanatkn.com> <https://ryanatkn.com/>
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,283 @@
1
+ # gro <img src="static/logo.svg" alt="a pixelated green oak acorn with a glint of sun" width="32" height="32">
2
+
3
+ [<img src="static/logo.svg" alt="a pixelated green oak acorn with a glint of sun" align="right" width="192" height="192">](https://gro.fuzdev.com/)
4
+
5
+ > task runner and toolkit extending SvelteKit 🌰 generate, run, optimize
6
+
7
+ [`npm i -D @fuzdev/gro`](https://www.npmjs.com/package/@fuzdev/gro)
8
+
9
+ > ⚠️ I still use Gro heavily but I'm transitioning to Rust-based tooling
10
+ > with [Fuz](https://github.com/fuzdev) using [Deno](https://github.com/denoland) as a sidecar.
11
+ > I consider Gro deprecated but there should be a migration path.
12
+ > Please open issues if you need help.
13
+
14
+ > ⚠️[Windows won't be supported](https://github.com/fuzdev/gro/issues/319), I chose Bash instead.
15
+
16
+ Docs at [gro.fuzdev.com/docs](https://gro.fuzdev.com/docs) and [src/docs](./src/docs).
17
+
18
+ Need help or want to share thoughts? See the
19
+ [issues](https://github.com/fuzdev/gro/issues) and
20
+ [discussions](https://github.com/fuzdev/gro/discussions).
21
+
22
+ ## about
23
+
24
+ Gro is a task runner and toolkit
25
+ extending [SvelteKit](https://github.com/sveltejs/kit),
26
+ [Vite](https://github.com/vitejs/vite),
27
+ and [esbuild](https://github.com/evanw/esbuild)
28
+ for making web frontends, servers, and libraries with TypeScript.
29
+ It's a dev tool, not for production use.
30
+ It includes:
31
+
32
+ - [task runner](/src/docs/task.md) that uses the filesystem convention `*.task.ts`
33
+ - lots of [common builtin tasks](/src/docs/tasks.md) that users can easily override and compose
34
+ - tools and patterns for
35
+ [developing](/src/docs/dev.md),
36
+ [building](/src/docs/build.md),
37
+ [testing](/src/docs/test.md),
38
+ [deploying](/src/docs/deploy.md),
39
+ and [publishing](/src/docs/publish.md)
40
+ [SvelteKit](https://github.com/sveltejs/kit) apps, library packages, and Node servers
41
+ - integrated [TypeScript](https://github.com/microsoft/typescript),
42
+ [Svelte](https://github.com/sveltejs/svelte),
43
+ and [SvelteKit](https://github.com/sveltejs/kit)
44
+ - defers to SvelteKit and Vite for the frontend and
45
+ [`@sveltejs/package`](https://kit.svelte.dev/docs/packaging) for the library
46
+ - exposes all of its internals in `$lib`
47
+ - uses [Changesets](https://github.com/changesets/changesets) for versioning and changelogs
48
+ - provides a [Node loader](/src/lib/loader.ts) with a [register hook](/src/lib/register.ts)
49
+ - uses Node's type stripping and supports importing JSON, SvelteKit shims,
50
+ and SSR'd Svelte files in tests/tasks/scripts
51
+ - supports [SvelteKit module imports](https://kit.svelte.dev/docs/modules) for
52
+ `$lib`, `$env`, and `$app` in tasks, tests, Node servers,
53
+ and other code outside of the SvelteKit frontend,
54
+ so you can use SvelteKit patterns everywhere
55
+ (these are best-effort shims, not perfect)
56
+ - supports running TypeScript files directly without a task via `gro run a.ts`
57
+ or `node --import @fuzdev/gro/register.js a.ts`
58
+ - [configurable plugins](/src/docs/plugin.md) to support SvelteKit,
59
+ [auto-restarting Node servers](/src/lib/gro_plugin_server.ts),
60
+ and other external build processes
61
+ - see the [Gro config docs](/src/docs/config.md) and
62
+ [the default config](https://github.com/fuzdev/gro/blob/main/src/lib/gro.config.default.ts)
63
+ - see [`fuz_template`](https://github.com/fuz-dev/fuz_template)
64
+ for a simple starter project example, and
65
+ [`@feltjs/felt`](https://github.com/feltjs/felt) for a more complex example with custom tasks
66
+ - [testing](/src/docs/test.md) with [`vitest`](https://github.com/vitest-dev/vitest)
67
+ - codegen by convention with [`gen`](/src/docs/gen.md)
68
+ - linting with [ESLint](https://github.com/eslint/eslint)
69
+ (I also maintain [`@feltjs/eslint-config`](https://github.com/feltjs/eslint-config))
70
+ - formatting with [Prettier](https://github.com/prettier/prettier)
71
+
72
+ ## docs
73
+
74
+ - early API docs at [/docs/api](https://gro.fuzdev.com/docs/api)
75
+ - developing web frontends, servers, and libraries
76
+ - [config](/src/docs/config.md)
77
+ - [dev](/src/docs/dev.md)
78
+ - [build](/src/docs/build.md) for production
79
+ - [deploy](/src/docs/deploy.md) to a branch, like for GitHub pages
80
+ - [publish](/src/docs/publish.md) to npm
81
+ - [`Task`](/src/docs/task.md) runner
82
+ - builtin [tasks](/src/docs/tasks.md) list
83
+ - [testing](/src/docs/test.md) with [`vitest`](https://github.com/vitest-dev/vitest)
84
+ - [`gen`](/src/docs/gen.md) code generation
85
+ - [`public` package](/src/docs/package_json.md#public-packages) features (nonstandard)
86
+ - full [docs index](/src/docs#readme)
87
+
88
+ ## install
89
+
90
+ > depends on node >=20.12
91
+
92
+ Typical usage installs [@fuzdev/gro](https://www.npmjs.com/package/@fuzdev/gro)
93
+ as a dev dependency:
94
+
95
+ ```bash
96
+ npm i -D @fuzdev/gro
97
+ npx @fuzdev/gro # note the package is namespaced, don't install `gro`
98
+ ```
99
+
100
+ It's handy to install globally too:
101
+
102
+ ```bash
103
+ npm i -g @fuzdev/gro
104
+ gro
105
+ ```
106
+
107
+ ## usage
108
+
109
+ Gro has a task runner that discovers and runs TypeScript modules with the `.task.` subextension.
110
+ Running `gro` with no args prints the tasks
111
+ it finds in the current directory along with its builtin tasks:
112
+
113
+ ```bash
114
+ gro # prints available tasks - defers to any local gro installation
115
+ ```
116
+
117
+ ```
118
+ Run a task: gro [name]
119
+ View help: gro [name] --help
120
+
121
+ 19 tasks in gro:
122
+
123
+ build build the project
124
+ changeset call changeset with gro patterns
125
+ check check that everything is ready to commit
126
+ clean remove temporary dev and build files, and optionally prune git branches
127
+ commit commit and push to a new branch
128
+ deploy deploy to a branch
129
+ dev start SvelteKit and other dev plugins
130
+ format format source files
131
+ gen run code generation scripts
132
+ lint run eslint
133
+ publish bump version, publish to the configured registry, and git push
134
+ reinstall refreshes package-lock.json with the latest and cleanest deps
135
+ release publish and deploy
136
+ resolve diagnostic that logs resolved filesystem info for the given input paths
137
+ run execute a file with the loader, like `node` but works for TypeScript
138
+ sync run `gro gen`, update `package.json`, and optionally install packages to sync up
139
+ test run tests with vitest
140
+ typecheck run svelte-check or tsc on the project without emitting any files
141
+ upgrade upgrade deps
142
+ ```
143
+
144
+ To run tasks, Gro matches your CLI input against its filesystem conventions.
145
+ It tries to do the right thing, where right is helpful but not surprising,
146
+ with some magic but not too much:
147
+
148
+ ```bash
149
+ gro # displays all available tasks matching `src/lib/**/*.task.ts` and Gro's builtins
150
+ gro a # tries to run `src/lib/a.task.ts`, then `./a.task.ts`, then Gro's builtin if one exists
151
+ gro a --help # displays docs for the "a" task and its args, works for every task
152
+ gro some/dir # lists all tasks inside `src/lib/some/dir`
153
+ gro some/file # runs `src/lib/some/file.task.ts`
154
+ gro some/file.task.ts # same as above
155
+ ```
156
+
157
+ Gro can also run non-task TypeScript files directly
158
+ with [the `gro run` task](/src/lib/run.task.ts) or [register hook](/src/lib/register.ts):
159
+
160
+ ```bash
161
+ gro run foo.ts
162
+ node --import @fuzdev/gro/register.js foo.ts
163
+ ```
164
+
165
+ Or programmatically:
166
+
167
+ ```js
168
+ // myfile.js
169
+ import {register} from 'node:module';
170
+ register('@fuzdev/gro/loader.js', import.meta.url);
171
+ await import('./foo.ts');
172
+ ```
173
+
174
+ Gro has a number of builtin tasks that you can run with the CLI.
175
+ To learn more [see the task docs](/src/docs/task.md)
176
+ and [the generated task index](/src/docs/tasks.md).
177
+
178
+ ```bash
179
+ gro dev # start developing in watch mode
180
+ gro dev -- vite --port 3003 # forward args by separating sections with --
181
+ ```
182
+
183
+ ```bash
184
+ gro build # build everything for production
185
+ ```
186
+
187
+ [Testing](/src/docs/test.md) with [`vitest`](https://github.com/vitest-dev/vitest),
188
+ including shims for [SvelteKit modules](https://kit.svelte.dev/docs/modules):
189
+
190
+ ```bash
191
+ gro test # run all tests for `*.test.ts` files with `vitest`
192
+ gro test filepattern1 some.test another.test
193
+ gro test optional_pattern -t "optional search string for test name"
194
+ gro test -- vitest --forwarded_args 'to vitest'
195
+ ```
196
+
197
+ Check all the things:
198
+
199
+ ```bash
200
+ gro check # does all of the following:
201
+ gro typecheck # svelte-check with tsc fallback
202
+ gro test # run tests
203
+ gro gen --check # ensure generated files are current
204
+ gro format --check # ensure everything is formatted
205
+ gro lint # eslint
206
+ ```
207
+
208
+ For a usage example see [the `check.yml` CI config](.github/workflows/check.yml).
209
+
210
+ Formatting with [`prettier`](https://github.com/prettier/prettier):
211
+
212
+ ```bash
213
+ gro format # format all of the source files using Prettier
214
+ gro format --check # check that all source files are formatted
215
+ ```
216
+
217
+ Codegen with [`gen`](/src/docs/gen.md):
218
+
219
+ ```bash
220
+ gro gen # run codegen for all `*.gen.*` files
221
+ gro gen --check # error if any generated files are new or different
222
+ ```
223
+
224
+ To deploy: (also see [`src/docs/deploy.md`](/src/docs/deploy.md))
225
+
226
+ ```bash
227
+ gro deploy # build and push to the `deploy` branch
228
+ ```
229
+
230
+ To publish: (also see [`src/docs/publish.md`](/src/docs/publish.md))
231
+
232
+ ```bash
233
+ gro publish # flush changeset to changelog, bump version, publish to npm, and git push
234
+ ```
235
+
236
+ More:
237
+
238
+ ```bash
239
+ gro clean # delete all build artifacts from the filesystem
240
+ gro clean --sveltekit --nodemodules --git # also deletes dirs and prunes git branches
241
+ gro upgrade excluded-dep-1 excluded-dep-2 # npm updates to the latest everything
242
+ gro --version # print the Gro version
243
+ ```
244
+
245
+ For more see [the tasks index](/src/docs/tasks.md),
246
+ [the task feature docs](/src/docs/task.md), and [the docs index](/src/docs/README.md).
247
+
248
+ ## develop
249
+
250
+ ```bash
251
+ npm i
252
+ npm run bootstrap # build and link `gro` without itself - needed only once
253
+ gro build # same as `npm run bootstrap` when the `gro` CLI is available
254
+ gro test # make sure everything looks good - same as `npm test`
255
+ gro test some.test another.test
256
+
257
+ # use your development version of `gro` locally in another project:
258
+ gro build # updates the `gro` CLI, same as `npm run bootstrap`
259
+ cd ../otherproject
260
+ npm link ../gro # from `otherproject/`
261
+ gro build # from `../gro` on changes
262
+ ```
263
+
264
+ ## credits 🐢<sub>🐢</sub><sub><sub>🐢</sub></sub>
265
+
266
+ Gro builds on
267
+ [TypeScript](https://github.com/microsoft/TypeScript) ∙
268
+ [Svelte](https://github.com/sveltejs/svelte) ∙
269
+ [SvelteKit](https://github.com/sveltejs/kit) ∙
270
+ [Vite](https://github.com/vitejs/vite) ∙
271
+ [esbuild](https://github.com/evanw/esbuild) ∙
272
+ [Vitest](https://github.com/vitest-dev/vitest) ∙
273
+ [chokidar](https://github.com/paulmillr/chokidar) ∙
274
+ [zod](https://github.com/colinhacks/zod) ∙
275
+ [@fuzdev/fuz_util](https://github.com/fuzdev/fuz_util) ∙
276
+ [ESLint](https://github.com/eslint/eslint) ∙
277
+ [Prettier](https://github.com/prettier/prettier) ∙
278
+ [svelte-check](https://github.com/sveltejs/language-tools/tree/master/packages/svelte-check) &
279
+ [more](package.json)
280
+
281
+ ## license [🐦](https://wikipedia.org/wiki/Free_and_open-source_software)
282
+
283
+ [MIT](LICENSE)
package/dist/args.d.ts ADDED
@@ -0,0 +1,37 @@
1
+ import { type Args } from '@fuzdev/fuz_util/args.js';
2
+ /**
3
+ * Parses `task_name` and `args` from `process.argv`,
4
+ * ignoring anything after any `--`.
5
+ */
6
+ export declare const to_task_args: (argv?: string[]) => {
7
+ task_name: string;
8
+ args: Args;
9
+ };
10
+ /**
11
+ * Gets the array of raw string args starting with the first `--`, if any.
12
+ */
13
+ export declare const to_raw_rest_args: (argv?: string[]) => Array<string>;
14
+ /**
15
+ * Parses `process.argv` for the specified `command`, so given
16
+ * `gro taskname arg1 --arg2 -- eslint eslintarg1 --eslintarg2 -- tsc --tscarg1 --tscarg2`
17
+ * the `command` `'eslint'` returns `eslintarg1 --eslintarg2`
18
+ * and `'tsc'` returns `--tscarg1` and `--tscarg2`.
19
+ */
20
+ export declare const to_forwarded_args: (command: string, raw_rest_args?: Array<string>, cache?: Record<string, Args | undefined>) => Args;
21
+ export declare const to_forwarded_args_by_command: (raw_rest_args?: string[]) => Record<string, Args | undefined>;
22
+ /**
23
+ * Gets all args after the first `--` without assuming a command name.
24
+ * This is useful for tasks that want to forward args directly to a tool
25
+ * without requiring users to specify the tool name explicitly.
26
+ * Optionally strips a specific command name if present for backward compatibility.
27
+ * @example
28
+ * ```ts
29
+ * // `gro test -- --watch` → {watch: true}
30
+ * // `gro test -- foo.test.ts` → {_: ['foo.test.ts']}
31
+ * // `gro test -- vitest --watch` with command_to_strip='vitest' → {watch: true}
32
+ * to_implicit_forwarded_args('vitest')
33
+ * ```
34
+ */
35
+ export declare const to_implicit_forwarded_args: (command_to_strip?: string, raw_rest_args?: string[]) => Args;
36
+ export declare const print_command_args: (serialized_args: Array<string>) => string;
37
+ //# sourceMappingURL=args.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"args.d.ts","sourceRoot":"../src/lib/","sources":["../src/lib/args.ts"],"names":[],"mappings":"AACA,OAAO,EAAa,KAAK,IAAI,EAAC,MAAM,0BAA0B,CAAC;AAE/D;;;GAGG;AACH,eAAO,MAAM,YAAY,GAAI,eAAmB,KAAG;IAAC,SAAS,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,IAAI,CAAA;CAMhF,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,gBAAgB,GAAI,eAAmB,KAAG,KAAK,CAAC,MAAM,CAGlE,CAAC;AAEF;;;;;GAKG;AACH,eAAO,MAAM,iBAAiB,GAC7B,SAAS,MAAM,EACf,gBAAgB,KAAK,CAAC,MAAM,CAAC,EAC7B,wCAAmD,KACjD,IAA4B,CAAC;AAEhC,eAAO,MAAM,4BAA4B,GACxC,wBAAkC,KAChC,MAAM,CAAC,MAAM,EAAE,IAAI,GAAG,SAAS,CAuCjC,CAAC;AAEF;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,0BAA0B,GACtC,mBAAmB,MAAM,EACzB,wBAAkC,KAChC,IAaF,CAAC;AAEF,eAAO,MAAM,kBAAkB,GAAI,iBAAiB,KAAK,CAAC,MAAM,CAAC,KAAG,MAK1C,CAAC"}
package/dist/args.js ADDED
@@ -0,0 +1,102 @@
1
+ import { styleText as st } from 'node:util';
2
+ import { argv_parse } from '@fuzdev/fuz_util/args.js';
3
+ /**
4
+ * Parses `task_name` and `args` from `process.argv`,
5
+ * ignoring anything after any `--`.
6
+ */
7
+ export const to_task_args = (argv = process.argv) => {
8
+ const forwarded_index = argv.indexOf('--');
9
+ const args = argv_parse(forwarded_index === -1 ? argv.slice(2) : argv.slice(2, forwarded_index));
10
+ const task_name = args._.shift() ?? '';
11
+ if (!args._.length)
12
+ delete args._; // enable schema defaults
13
+ return { task_name, args };
14
+ };
15
+ /**
16
+ * Gets the array of raw string args starting with the first `--`, if any.
17
+ */
18
+ export const to_raw_rest_args = (argv = process.argv) => {
19
+ const forwarded_index = argv.indexOf('--');
20
+ return forwarded_index === -1 ? [] : argv.slice(forwarded_index);
21
+ };
22
+ /**
23
+ * Parses `process.argv` for the specified `command`, so given
24
+ * `gro taskname arg1 --arg2 -- eslint eslintarg1 --eslintarg2 -- tsc --tscarg1 --tscarg2`
25
+ * the `command` `'eslint'` returns `eslintarg1 --eslintarg2`
26
+ * and `'tsc'` returns `--tscarg1` and `--tscarg2`.
27
+ */
28
+ export const to_forwarded_args = (command, raw_rest_args, cache = to_forwarded_args_by_command(raw_rest_args)) => cache[command] ?? {};
29
+ export const to_forwarded_args_by_command = (raw_rest_args = to_raw_rest_args()) => {
30
+ // Parse each segment of `argv` separated by `--`.
31
+ const argvs = [];
32
+ let arr;
33
+ for (const arg of raw_rest_args) {
34
+ if (arg === '--') {
35
+ if (arr?.length)
36
+ argvs.push(arr);
37
+ arr = [];
38
+ }
39
+ else if (!arr) {
40
+ continue;
41
+ }
42
+ else if (arg) {
43
+ arr.push(arg);
44
+ }
45
+ }
46
+ if (arr?.length)
47
+ argvs.push(arr);
48
+ // Add each segment of parsed `argv` keyed by the first rest arg,
49
+ // which is assumed to be the CLI command that gets forwarded the args.
50
+ const forwarded_args_by_command = {};
51
+ for (const argv of argvs) {
52
+ const args = argv_parse(argv);
53
+ let command = args._.shift();
54
+ if (!command) {
55
+ // Skip sections without a command name - these are handled by `to_implicit_forwarded_args`.
56
+ // This allows `gro run script.ts -- --help` to pass `--help` to the script.
57
+ continue;
58
+ }
59
+ // Gro commands get combined with their task name.
60
+ if (command === 'gro') {
61
+ if (!args._.length) {
62
+ throw Error(`Malformed args following a \`--\`. Expected gro taskname: \`${argv.join(' ')}\``);
63
+ }
64
+ command += ' ' + args._.shift();
65
+ }
66
+ if (!args._.length)
67
+ delete args._;
68
+ forwarded_args_by_command[command] = args;
69
+ }
70
+ return forwarded_args_by_command;
71
+ };
72
+ /**
73
+ * Gets all args after the first `--` without assuming a command name.
74
+ * This is useful for tasks that want to forward args directly to a tool
75
+ * without requiring users to specify the tool name explicitly.
76
+ * Optionally strips a specific command name if present for backward compatibility.
77
+ * @example
78
+ * ```ts
79
+ * // `gro test -- --watch` → {watch: true}
80
+ * // `gro test -- foo.test.ts` → {_: ['foo.test.ts']}
81
+ * // `gro test -- vitest --watch` with command_to_strip='vitest' → {watch: true}
82
+ * to_implicit_forwarded_args('vitest')
83
+ * ```
84
+ */
85
+ export const to_implicit_forwarded_args = (command_to_strip, raw_rest_args = to_raw_rest_args()) => {
86
+ const start = raw_rest_args.indexOf('--');
87
+ if (start === -1)
88
+ return {};
89
+ let argv = raw_rest_args.slice(start + 1);
90
+ if (command_to_strip && argv[0] === command_to_strip) {
91
+ argv = argv.slice(1);
92
+ }
93
+ const args = argv_parse(argv);
94
+ if (!args._.length)
95
+ delete args._;
96
+ return args;
97
+ };
98
+ export const print_command_args = (serialized_args) => st('gray', '[') +
99
+ st('magenta', 'running command') +
100
+ st('gray', ']') +
101
+ ' ' +
102
+ serialized_args.join(' ');
@@ -0,0 +1,20 @@
1
+ import { z } from 'zod';
2
+ import { type Task } from './task.ts';
3
+ /** @nodocs */
4
+ export declare const Args: z.ZodObject<{
5
+ sync: z.ZodDefault<z.ZodBoolean>;
6
+ 'no-sync': z.ZodDefault<z.ZodBoolean>;
7
+ gen: z.ZodDefault<z.ZodBoolean>;
8
+ 'no-gen': z.ZodDefault<z.ZodBoolean>;
9
+ install: z.ZodDefault<z.ZodBoolean>;
10
+ 'no-install': z.ZodDefault<z.ZodBoolean>;
11
+ force_build: z.ZodDefault<z.ZodBoolean>;
12
+ }, z.core.$strict>;
13
+ export type Args = z.infer<typeof Args>;
14
+ /**
15
+ * Length of git commit hash when displayed in logs (standard git convention).
16
+ */
17
+ export declare const GIT_SHORT_HASH_LENGTH = 7;
18
+ /** @nodocs */
19
+ export declare const task: Task<Args>;
20
+ //# sourceMappingURL=build.task.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"build.task.d.ts","sourceRoot":"../src/lib/","sources":["../src/lib/build.task.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,CAAC,EAAC,MAAM,KAAK,CAAC;AAOtB,OAAO,EAAY,KAAK,IAAI,EAAC,MAAM,WAAW,CAAC;AAW/C,cAAc;AACd,eAAO,MAAM,IAAI;;;;;;;;kBAcf,CAAC;AACH,MAAM,MAAM,IAAI,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,IAAI,CAAC,CAAC;AAExC;;GAEG;AACH,eAAO,MAAM,qBAAqB,IAAI,CAAC;AASvC,cAAc;AACd,eAAO,MAAM,IAAI,EAAE,IAAI,CAAC,IAAI,CAqG3B,CAAC"}
@@ -0,0 +1,119 @@
1
+ import { z } from 'zod';
2
+ import { styleText as st } from 'node:util';
3
+ import { git_check_clean_workspace, git_current_commit_hash } from '@fuzdev/fuz_util/git.js';
4
+ import { rm } from 'node:fs/promises';
5
+ import { join } from 'node:path';
6
+ import { fs_exists } from '@fuzdev/fuz_util/fs.js';
7
+ import { TaskError } from "./task.js";
8
+ import { Plugins } from "./plugin.js";
9
+ import { clean_fs } from "./clean_fs.js";
10
+ import { is_build_cache_valid, create_build_cache_metadata, save_build_cache_metadata, discover_build_output_dirs, } from "./build_cache.js";
11
+ import { paths } from "./paths.js";
12
+ /** @nodocs */
13
+ export const Args = z.strictObject({
14
+ sync: z.boolean().meta({ description: 'dual of no-sync' }).default(true),
15
+ 'no-sync': z.boolean().meta({ description: 'opt out of gro sync' }).default(false),
16
+ gen: z.boolean().meta({ description: 'dual of no-gen' }).default(true),
17
+ 'no-gen': z.boolean().meta({ description: 'opt out of gro gen' }).default(false),
18
+ install: z.boolean().meta({ description: 'dual of no-install' }).default(true),
19
+ 'no-install': z // convenience, same as `gro build -- gro sync --no-install` but the latter takes precedence
20
+ .boolean()
21
+ .meta({ description: 'opt out of installing packages before building' })
22
+ .default(false),
23
+ force_build: z
24
+ .boolean()
25
+ .meta({ description: 'force a fresh build, ignoring the cache' })
26
+ .default(false),
27
+ });
28
+ /**
29
+ * Length of git commit hash when displayed in logs (standard git convention).
30
+ */
31
+ export const GIT_SHORT_HASH_LENGTH = 7;
32
+ /**
33
+ * Formats a git commit hash for display in logs.
34
+ * Returns '[none]' if hash is null (e.g., not in a git repo).
35
+ */
36
+ const format_commit_hash = (hash) => hash?.slice(0, GIT_SHORT_HASH_LENGTH) ?? '[none]';
37
+ /** @nodocs */
38
+ export const task = {
39
+ summary: 'build the project',
40
+ Args,
41
+ run: async (ctx) => {
42
+ const { args, invoke_task, log, config } = ctx;
43
+ const { sync, gen, install, force_build } = args;
44
+ if (sync || install) {
45
+ if (!sync)
46
+ log.warn('sync is false but install is true, so ignoring the sync option');
47
+ await invoke_task('sync', { install, gen: false });
48
+ }
49
+ if (gen) {
50
+ await invoke_task('gen');
51
+ }
52
+ // Batch git calls upfront for performance (spawning processes is expensive)
53
+ const [workspace_status, initial_commit] = await Promise.all([
54
+ git_check_clean_workspace(),
55
+ git_current_commit_hash(),
56
+ ]);
57
+ const workspace_dirty = !!workspace_status;
58
+ // Discover build output directories once to avoid redundant filesystem scans
59
+ let build_dirs;
60
+ // Check build cache unless force_build is set or workspace is dirty
61
+ if (!workspace_dirty && !force_build) {
62
+ const cache_valid = await is_build_cache_valid(config, log, initial_commit);
63
+ if (cache_valid) {
64
+ log.info(st('cyan', 'skipping build, cache is valid'), st('dim', '(use --force_build to rebuild)'));
65
+ return;
66
+ }
67
+ }
68
+ else if (workspace_dirty) {
69
+ // IMPORTANT: When workspace is dirty, we delete cache AND all outputs to prevent stale state.
70
+ // Rationale: Uncommitted changes could be reverted, leaving cached outputs from reverted code.
71
+ // This conservative approach prioritizes safety over convenience during development.
72
+ const cache_path = join(paths.build, 'build.json');
73
+ if (await fs_exists(cache_path)) {
74
+ await rm(cache_path, { force: true });
75
+ }
76
+ // Delete all build output directories
77
+ build_dirs = await discover_build_output_dirs();
78
+ await Promise.all(build_dirs.map((dir) => rm(dir, { recursive: true, force: true })));
79
+ log.info(st('yellow', 'workspace has uncommitted changes - skipping build cache'));
80
+ // Skip clean_fs - already manually cleaned cache and all build outputs above
81
+ }
82
+ else {
83
+ log.info(st('yellow', 'forcing fresh build, ignoring cache'));
84
+ }
85
+ // Clean build outputs (skip if workspace was dirty - already cleaned manually above)
86
+ if (!workspace_dirty) {
87
+ await clean_fs({ build_dist: true });
88
+ }
89
+ const plugins = await Plugins.create({ ...ctx, dev: false, watch: false });
90
+ await plugins.setup();
91
+ await plugins.adapt();
92
+ await plugins.teardown();
93
+ // Verify workspace didn't become dirty during build
94
+ const final_workspace_status = await git_check_clean_workspace();
95
+ if (final_workspace_status !== workspace_status) {
96
+ // Workspace state changed during build - this indicates a problem
97
+ throw new TaskError('Build process modified tracked files or created untracked files.\n\n' +
98
+ 'Git status after build:\n' +
99
+ final_workspace_status +
100
+ '\n\n' +
101
+ 'Builds should only write to output directories (build/, dist/, etc.).\n' +
102
+ 'This usually indicates a plugin or build step is incorrectly modifying source files.');
103
+ }
104
+ // Save build cache metadata after successful build (only if workspace is clean)
105
+ if (!workspace_dirty) {
106
+ // Race condition protection: verify git commit didn't change during build
107
+ const current_commit = await git_current_commit_hash();
108
+ if (current_commit !== initial_commit) {
109
+ log.warn(st('yellow', 'git commit changed during build'), st('dim', `(${format_commit_hash(initial_commit)} → ${format_commit_hash(current_commit)})`), '- cache not saved');
110
+ }
111
+ else {
112
+ // Commit is stable - safe to save cache
113
+ const metadata = await create_build_cache_metadata(config, log, initial_commit, build_dirs);
114
+ await save_build_cache_metadata(metadata, log);
115
+ log.debug('Build cache metadata saved');
116
+ }
117
+ }
118
+ },
119
+ };