@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
@@ -0,0 +1,254 @@
1
+ import {dirname, isAbsolute, join, resolve} from 'node:path';
2
+ import {stat} from 'node:fs/promises';
3
+ import {fs_exists, fs_search} from '@fuzdev/fuz_util/fs.js';
4
+ import {strip_start} from '@fuzdev/fuz_util/string.js';
5
+ import {z} from 'zod';
6
+ import type {Flavored} from '@fuzdev/fuz_util/types.js';
7
+ import type {PathInfo, PathId, ResolvedPath} from '@fuzdev/fuz_util/path.js';
8
+
9
+ import {GRO_PACKAGE_DIR, GRO_DIST_DIR} from './paths.ts';
10
+ import {TASK_FILE_SUFFIX_JS} from './task.ts';
11
+
12
+ // TODO Flavored doesn't work when used in schemas, use Zod brand instead? problem is ergonomics
13
+ export const InputPath = z.string();
14
+ export type InputPath = Flavored<z.infer<typeof InputPath>, 'InputPath'>;
15
+
16
+ export const RawInputPath = z.string();
17
+ export type RawInputPath = Flavored<z.infer<typeof RawInputPath>, 'RawInputPath'>;
18
+
19
+ /**
20
+ * Raw input paths are paths that users provide to Gro to reference files for tasks and gen.
21
+ *
22
+ * A raw input path can be to a file or directory in the following forms:
23
+ *
24
+ * - an absolute path, preserved
25
+ * - an explicit relative path, e.g. `./src/foo`, resolved to `root_path` which defaults to the cwd
26
+ * - an implicit relative path, e.g. `src/foo`, preserved
27
+ * - an implicit relative path prefixed with `gro/`, transformed to absolute in the Gro directory
28
+ *
29
+ * Thus, input paths are either absolute or implicitly relative.
30
+ */
31
+ export const to_input_path = (
32
+ raw_input_path: RawInputPath,
33
+ root_path = process.cwd(), // TODO @many isn't passed in anywhere, maybe hoist to `invoke_task` and others
34
+ ): InputPath => {
35
+ if (raw_input_path.startsWith(GRO_PACKAGE_DIR)) {
36
+ return GRO_DIST_DIR + strip_start(raw_input_path, GRO_PACKAGE_DIR);
37
+ } else if (raw_input_path[0] === '.') {
38
+ return resolve(root_path, raw_input_path);
39
+ }
40
+ return raw_input_path as InputPath;
41
+ };
42
+
43
+ export const to_input_paths = (
44
+ raw_input_paths: Array<RawInputPath>,
45
+ root_path?: string, // TODO @many isn't passed in anywhere, maybe hoist to `invoke_task` and others
46
+ ): Array<InputPath> => raw_input_paths.map((p) => to_input_path(p, root_path));
47
+
48
+ export interface PossiblePath {
49
+ id: PathId;
50
+ input_path: InputPath;
51
+ root_dir: PathId;
52
+ }
53
+
54
+ /**
55
+ * Gets a list of possible source ids for each input path with `extensions`,
56
+ * duplicating each under `root_dirs`, without checking the filesystem.
57
+ */
58
+ export const get_possible_paths = async (
59
+ input_path: InputPath,
60
+ root_dirs: Array<PathId>,
61
+ extensions: Array<string>,
62
+ ): Promise<Array<PossiblePath>> => {
63
+ const possible_paths: Set<PossiblePath> = new Set();
64
+
65
+ const add_possible_paths = (path: string, root_dir: PathId) => {
66
+ // Specifically for paths to the Gro package dist, optimize by only looking for `.task.js`.
67
+ if (path.startsWith(GRO_DIST_DIR)) {
68
+ possible_paths.add({
69
+ id: (path.endsWith('/') || path.endsWith(TASK_FILE_SUFFIX_JS)
70
+ ? path
71
+ : path + TASK_FILE_SUFFIX_JS) as PathId,
72
+ input_path,
73
+ root_dir,
74
+ });
75
+ } else {
76
+ possible_paths.add({id: path as PathId, input_path, root_dir});
77
+ if (!path.endsWith('/') && !extensions.some((e) => path.endsWith(e))) {
78
+ for (const extension of extensions) {
79
+ possible_paths.add({id: path + extension, input_path, root_dir});
80
+ }
81
+ }
82
+ }
83
+ };
84
+
85
+ if (isAbsolute(input_path)) {
86
+ const exists = await fs_exists(input_path);
87
+ const is_directory = exists && (await stat(input_path)).isDirectory();
88
+ add_possible_paths(input_path, is_directory ? input_path : dirname(input_path));
89
+ } else {
90
+ for (const root_dir of root_dirs) {
91
+ add_possible_paths(join(root_dir, input_path), root_dir);
92
+ }
93
+ }
94
+ return Array.from(possible_paths);
95
+ };
96
+
97
+ export interface ResolvedInputPath {
98
+ input_path: InputPath;
99
+ id: PathId;
100
+ is_directory: boolean;
101
+ root_dir: PathId;
102
+ }
103
+
104
+ export interface ResolvedInputFile {
105
+ id: PathId;
106
+ input_path: InputPath;
107
+ resolved_input_path: ResolvedInputPath;
108
+ }
109
+
110
+ export interface ResolvedInputPaths {
111
+ resolved_input_paths: Array<ResolvedInputPath>;
112
+ possible_paths_by_input_path: Map<InputPath, Array<PossiblePath>>;
113
+ unmapped_input_paths: Array<InputPath>;
114
+ }
115
+
116
+ /**
117
+ * Gets the path data for each input path, checking the filesystem for the possibilities
118
+ * and stopping at the first existing file or falling back to the first existing directory.
119
+ * If none is found for an input path, it's added to `unmapped_input_paths`.
120
+ */
121
+ export const resolve_input_paths = async (
122
+ input_paths: Array<InputPath>,
123
+ root_dirs: Array<PathId>,
124
+ extensions: Array<string>,
125
+ ): Promise<ResolvedInputPaths> => {
126
+ const resolved_input_paths: Array<ResolvedInputPath> = [];
127
+ const possible_paths_by_input_path: Map<InputPath, Array<PossiblePath>> = new Map();
128
+ const unmapped_input_paths: Array<InputPath> = [];
129
+ for (const input_path of input_paths) {
130
+ let found_file: [PathInfo, PossiblePath] | null = null;
131
+ let found_dirs: Array<[PathInfo, PossiblePath]> | null = null;
132
+ // eslint-disable-next-line no-await-in-loop
133
+ const possible_paths = await get_possible_paths(input_path, root_dirs, extensions);
134
+ possible_paths_by_input_path.set(input_path, possible_paths);
135
+
136
+ // Find the first existing file path or fallback to the first directory path.
137
+ for (const possible_path of possible_paths) {
138
+ // eslint-disable-next-line no-await-in-loop
139
+ if (!(await fs_exists(possible_path.id))) continue;
140
+ // eslint-disable-next-line no-await-in-loop
141
+ const stats = await stat(possible_path.id);
142
+ if (stats.isDirectory()) {
143
+ found_dirs ??= [];
144
+ found_dirs.push([{id: possible_path.id, is_directory: stats.isDirectory()}, possible_path]);
145
+ } else {
146
+ found_file = [{id: possible_path.id, is_directory: stats.isDirectory()}, possible_path];
147
+ break;
148
+ }
149
+ }
150
+ if (found_file) {
151
+ resolved_input_paths.push({
152
+ input_path,
153
+ id: found_file[0].id,
154
+ is_directory: found_file[0].is_directory,
155
+ root_dir: found_file[1].root_dir,
156
+ });
157
+ } else if (found_dirs) {
158
+ for (const found_dir of found_dirs) {
159
+ resolved_input_paths.push({
160
+ input_path,
161
+ id: found_dir[0].id,
162
+ is_directory: found_dir[0].is_directory,
163
+ root_dir: found_dir[1].root_dir,
164
+ });
165
+ }
166
+ } else {
167
+ unmapped_input_paths.push(input_path);
168
+ }
169
+ }
170
+ return {
171
+ resolved_input_paths,
172
+ possible_paths_by_input_path,
173
+ unmapped_input_paths,
174
+ };
175
+ };
176
+
177
+ export interface ResolvedInputFiles {
178
+ resolved_input_files: Array<ResolvedInputFile>;
179
+ resolved_input_files_by_root_dir: Map<PathId, Array<ResolvedInputFile>>;
180
+ input_directories_with_no_files: Array<InputPath>;
181
+ }
182
+
183
+ /**
184
+ * Finds all of the matching files for the given input paths.
185
+ * De-dupes source ids.
186
+ */
187
+ export const resolve_input_files = async (
188
+ resolved_input_paths: Array<ResolvedInputPath>,
189
+ search: (dir: string) => Promise<Array<ResolvedPath>> = fs_search,
190
+ ): Promise<ResolvedInputFiles> => {
191
+ const resolved_input_files: Array<ResolvedInputFile> = [];
192
+ // Add all input paths initially, and remove each when resolved to a file.
193
+ const existing_path_ids: Set<PathId> = new Set();
194
+
195
+ let remaining = resolved_input_paths.slice();
196
+ const handle_found = (input_path: InputPath, id: PathId) => {
197
+ remaining = remaining.filter(
198
+ (r) => !(r.id === id || r.input_path === input_path || r.input_path === id), // `r.input_path === id` may be unnecessary
199
+ );
200
+ };
201
+
202
+ // TODO parallelize but would need to de-dupe and retain order
203
+ for (const resolved_input_path of resolved_input_paths) {
204
+ const {input_path, id, is_directory} = resolved_input_path;
205
+ if (is_directory) {
206
+ // Handle input paths that resolve to directories.
207
+ // eslint-disable-next-line no-await-in-loop
208
+ const files = await search(id);
209
+ if (!files.length) continue;
210
+ const path_ids: Array<PathId> = [];
211
+ for (const {path, is_directory} of files) {
212
+ if (is_directory) continue;
213
+ const path_id = join(id, path);
214
+ if (!existing_path_ids.has(path_id)) {
215
+ existing_path_ids.add(path_id);
216
+ path_ids.push(path_id);
217
+ }
218
+ handle_found(input_path, path_id);
219
+ }
220
+ if (!path_ids.length) continue;
221
+ const resolved_input_files_for_input_path: Array<ResolvedInputFile> = [];
222
+ for (const path_id of path_ids) {
223
+ const resolved_input_file: ResolvedInputFile = {
224
+ id: path_id,
225
+ input_path,
226
+ resolved_input_path,
227
+ };
228
+ resolved_input_files.push(resolved_input_file);
229
+ resolved_input_files_for_input_path.push(resolved_input_file);
230
+ }
231
+ } else {
232
+ if (!existing_path_ids.has(id)) {
233
+ // Handle input paths that resolve to files.
234
+ existing_path_ids.add(id);
235
+ const resolved_input_file: ResolvedInputFile = {id, input_path, resolved_input_path};
236
+ resolved_input_files.push(resolved_input_file);
237
+ }
238
+ handle_found(input_path, id);
239
+ }
240
+ }
241
+ return {
242
+ resolved_input_files,
243
+ resolved_input_files_by_root_dir: resolved_input_files.reduce((map, resolved_input_file) => {
244
+ const {root_dir} = resolved_input_file.resolved_input_path;
245
+ if (map.has(root_dir)) {
246
+ map.get(root_dir)!.push(resolved_input_file);
247
+ } else {
248
+ map.set(root_dir, [resolved_input_file]);
249
+ }
250
+ return map;
251
+ }, new Map<PathId, Array<ResolvedInputFile>>()),
252
+ input_directories_with_no_files: remaining.map((r) => r.input_path),
253
+ };
254
+ };
@@ -0,0 +1,34 @@
1
+ import {attach_process_error_handler} from '@fuzdev/fuz_util/process.js';
2
+ import {configure_print_colors} from '@fuzdev/fuz_util/print.js';
3
+
4
+ import {invoke_task} from './invoke_task.ts';
5
+ import {to_task_args} from './args.ts';
6
+ import {load_gro_config} from './gro_config.ts';
7
+ import {sveltekit_sync_if_obviously_needed} from './sveltekit_helpers.ts';
8
+
9
+ /*
10
+
11
+ This module invokes the Gro CLI which in turn invokes tasks.
12
+ Tasks are the CLI's primary concept.
13
+ To learn more about them, see `src/docs/task.md`.
14
+
15
+ When the CLI is invoked it passes the first CLI arg as `task_name` to `invoke_task`,
16
+ and the rest of the args are forwarded to the task's `run` function.
17
+
18
+ */
19
+
20
+ // handle uncaught errors
21
+ attach_process_error_handler({
22
+ to_error_label: (err) => (err.constructor.name === 'TaskError' ? 'TaskError' : null),
23
+ map_error_text: (err) => (err.constructor.name === 'SilentError' ? '' : null),
24
+ });
25
+
26
+ if (!process.env.NO_COLOR) {
27
+ const {styleText} = await import('node:util');
28
+ configure_print_colors(styleText);
29
+ }
30
+
31
+ await sveltekit_sync_if_obviously_needed();
32
+
33
+ const {task_name, args} = to_task_args();
34
+ await invoke_task(task_name, args, await load_gro_config());
@@ -0,0 +1,139 @@
1
+ import type {Args} from '@fuzdev/fuz_util/args.js';
2
+ import {Logger} from '@fuzdev/fuz_util/log.js';
3
+ import {print_ms, print_timings} from '@fuzdev/fuz_util/print.js';
4
+ import {create_stopwatch, Timings} from '@fuzdev/fuz_util/timings.js';
5
+ import {styleText as st} from 'node:util';
6
+
7
+ import {to_forwarded_args} from './args.ts';
8
+ import {run_task} from './run_task.ts';
9
+ import {to_input_path, RawInputPath} from './input_path.ts';
10
+ import {find_tasks, load_tasks, SilentError} from './task.ts';
11
+ import {package_json_load_for_gro} from './package_json.ts';
12
+ import {log_tasks, log_error_reasons} from './task_logging.ts';
13
+ import type {GroConfig} from './gro_config.ts';
14
+ import {Filer} from './filer.ts';
15
+
16
+ /**
17
+ * Invokes Gro tasks by name using the filesystem as the source.
18
+ *
19
+ * When a task is invoked,
20
+ * Gro first searches for tasks in the current working directory.
21
+ * and falls back to searching Gro's directory, if the two are different.
22
+ * See `src/lib/input_path.ts` for info about what "task_name" can refer to.
23
+ * If it matches a directory, all of the tasks within it are logged,
24
+ * both in the current working directory and Gro.
25
+ *
26
+ * This code is particularly hairy because
27
+ * we're accepting a wide range of user input
28
+ * and trying to do the right thing.
29
+ * Precise error messages are especially difficult and
30
+ * there are some subtle differences in the complex logical branches.
31
+ * The comments describe each condition.
32
+ *
33
+ * @param task_name - The name of the task to invoke.
34
+ * @param args - The CLI args to pass to the task.
35
+ * @param config - The Gro configuration.
36
+ * @param initial_timings - The timings to use for the top-level task, `null` for composed tasks.
37
+ */
38
+ export const invoke_task = async (
39
+ task_name: RawInputPath,
40
+ args: Args | undefined,
41
+ config: GroConfig,
42
+ initial_filer?: Filer,
43
+ initial_timings?: Timings | null,
44
+ parent_log?: Logger,
45
+ ): Promise<void> => {
46
+ // Create child logger if parent exists, otherwise root logger
47
+ const log_label = task_name || 'gro';
48
+ const log = parent_log ? parent_log.child(log_label) : new Logger(log_label);
49
+ log.info('invoking', task_name ? st('cyan', task_name) : 'gro');
50
+
51
+ // track if we created the filer
52
+ const owns_filer = !initial_filer;
53
+ const filer = initial_filer ?? new Filer({log: log.child('filer'), ...config.filer_options});
54
+
55
+ const owns_timings = !initial_timings;
56
+ const timings = initial_timings ?? new Timings();
57
+
58
+ const total_timing = create_stopwatch();
59
+ const finish = async () => {
60
+ // cleanup filer only if we created it and it was initialized
61
+ if (owns_filer && filer.inited) {
62
+ await filer.close();
63
+ }
64
+
65
+ if (owns_timings) return; // kinda weird, print timings only for the top-level task
66
+ print_timings(timings, log);
67
+ log.info(`🕒 ${print_ms(total_timing())}`);
68
+ };
69
+
70
+ // Check if the caller just wants to see the version.
71
+ if (!task_name && (args?.version || args?.v)) {
72
+ const gro_package_json = await package_json_load_for_gro();
73
+ log.info(`${st('gray', 'v')}${st('cyan', gro_package_json.version)}`);
74
+ await finish();
75
+ return;
76
+ }
77
+
78
+ // Resolve the input path for the provided task name.
79
+ const input_path = to_input_path(task_name);
80
+
81
+ const {task_root_dirs} = config;
82
+
83
+ // Find the task or directory specified by the `input_path`.
84
+ // Fall back to searching the Gro directory as well.
85
+ const found = await find_tasks([input_path], task_root_dirs, config);
86
+ if (!found.ok) {
87
+ log_error_reasons(log, found.reasons);
88
+ throw new SilentError();
89
+ }
90
+
91
+ // Found a match either in the current working directory or Gro's directory.
92
+ const found_tasks = found.value;
93
+ const {resolved_input_files} = found_tasks;
94
+
95
+ // Load the task module.
96
+ const loaded = await load_tasks(found_tasks);
97
+ if (!loaded.ok) {
98
+ log_error_reasons(log, loaded.reasons);
99
+ throw new SilentError();
100
+ }
101
+ const loaded_tasks = loaded.value;
102
+
103
+ if (
104
+ resolved_input_files.length > 1 ||
105
+ resolved_input_files[0]!.resolved_input_path.is_directory
106
+ ) {
107
+ // The input path matches a directory. Log the tasks but don't run them.
108
+ log_tasks(log, loaded_tasks);
109
+ await finish();
110
+ return;
111
+ }
112
+
113
+ // The input path matches a file that's presumable a task, so load and run it.
114
+ if (loaded_tasks.modules.length !== 1) throw Error('expected one loaded task'); // run only one task at a time
115
+ const task = loaded_tasks.modules[0]!;
116
+ log.info(
117
+ `→ ${st('cyan', task.name)} ${(task.mod.task.summary && st('gray', task.mod.task.summary)) ?? ''}`,
118
+ );
119
+
120
+ const timing_to_run_task = timings.start('run task ' + task_name);
121
+ const result = await run_task(
122
+ task,
123
+ {...args, ...to_forwarded_args(`gro ${task.name}`)},
124
+ invoke_task,
125
+ config,
126
+ filer,
127
+ log,
128
+ timings,
129
+ );
130
+ timing_to_run_task();
131
+ if (!result.ok) {
132
+ log.info(`${st('red', '🞩')} ${st('cyan', task.name)}`);
133
+ log_error_reasons(log, [result.reason]);
134
+ throw result.error;
135
+ }
136
+ log.info(`✓ ${st('cyan', task.name)}`);
137
+
138
+ await finish();
139
+ };
@@ -0,0 +1,39 @@
1
+ import {args_serialize} from '@fuzdev/fuz_util/args.js';
2
+ import {print_spawn_result} from '@fuzdev/fuz_util/process.js';
3
+ import {z} from 'zod';
4
+
5
+ import {to_forwarded_args} from './args.ts';
6
+ import {find_cli, spawn_cli} from './cli.ts';
7
+ import {TaskError, type Task} from './task.ts';
8
+
9
+ const ESLINT_CLI = 'eslint';
10
+
11
+ /** @nodocs */
12
+ export const Args = z.strictObject({
13
+ _: z.array(z.string()).meta({description: 'paths to serve'}).default([]),
14
+ eslint_cli: z.string().meta({description: 'the ESLint CLI to use'}).default(ESLINT_CLI),
15
+ });
16
+ export type Args = z.infer<typeof Args>;
17
+
18
+ /** @nodocs */
19
+ export const task: Task<Args> = {
20
+ summary: 'run eslint',
21
+ Args,
22
+ run: async ({log, args}): Promise<void> => {
23
+ const {_, eslint_cli} = args;
24
+
25
+ const found_eslint_cli = await find_cli(eslint_cli);
26
+ if (!found_eslint_cli) {
27
+ // TODO maybe make this an option?
28
+ log.info('ESLint is not installed; skipping linting');
29
+ return;
30
+ }
31
+
32
+ const forwarded_args = {_, 'max-warnings': 0, ...to_forwarded_args(eslint_cli)};
33
+ const serialized_args = args_serialize(forwarded_args);
34
+ const eslintResult = await spawn_cli(found_eslint_cli, serialized_args, log);
35
+ if (!eslintResult?.ok) {
36
+ throw new TaskError(`ESLint found some problems. ${print_spawn_result(eslintResult!)}`);
37
+ }
38
+ },
39
+ };
@@ -0,0 +1,229 @@
1
+ import {compile, compileModule, preprocess} from 'svelte/compiler';
2
+ import {fileURLToPath, pathToFileURL} from 'node:url';
3
+ import {dirname, join} from 'node:path';
4
+ import type {LoadHook, ResolveHook} from 'node:module';
5
+ import {readFileSync} from 'node:fs';
6
+ import ts_blank_space from 'ts-blank-space';
7
+
8
+ import {render_env_shim_module} from './sveltekit_shim_env.ts';
9
+ import {
10
+ render_sveltekit_shim_app_environment,
11
+ render_sveltekit_shim_app_paths,
12
+ SVELTEKIT_SHIM_APP_ENVIRONMENT_MATCHER,
13
+ SVELTEKIT_SHIM_APP_PATHS_MATCHER,
14
+ sveltekit_shim_app_specifiers,
15
+ } from './sveltekit_shim_app.ts';
16
+ import {default_svelte_config} from './svelte_config.ts';
17
+ import {paths} from './paths.ts';
18
+ import {TS_MATCHER, SVELTE_MATCHER, SVELTE_RUNES_MATCHER} from './constants.ts';
19
+ import {resolve_specifier} from './resolve_specifier.ts';
20
+ import {map_sveltekit_aliases} from './sveltekit_helpers.ts';
21
+
22
+ // TODO get out of the loader business, starting with https://nodejs.org/api/typescript.html#type-stripping
23
+
24
+ /*
25
+
26
+ Usage via `$lib/register.ts`:
27
+
28
+ ```bash
29
+ node --import @fuzdev/gro/register.js foo.ts
30
+ ```
31
+
32
+ Usage via `$lib/run.task.ts`:
33
+
34
+ ```bash
35
+ gro run foo.ts
36
+ ```
37
+
38
+ Direct usage without register (see also `$lib/gro.ts`):
39
+
40
+ ```bash
41
+ node --import 'data:text/javascript,import {register} from "node:module"; import {pathToFileURL} from "node:url"; register("@fuzdev/gro/loader.js", pathToFileURL("./"));' --experimental-import-meta-resolve --experimental-strip-types' foo.ts
42
+ ```
43
+
44
+ TODO how to improve that gnarly import line? was originally designed for the now-deprecated `--loader`
45
+
46
+ */
47
+
48
+ // TODO sourcemaps for the svelte preprocessors
49
+ // TODO `import.meta.resolve` wasn't available in loaders when this was first implemented, but might be now
50
+
51
+ // dev is always true in the loader
52
+ const dev = true;
53
+
54
+ const dir = paths.root;
55
+
56
+ const {
57
+ alias,
58
+ base_url,
59
+ assets_url,
60
+ env_dir,
61
+ private_prefix,
62
+ public_prefix,
63
+ svelte_compile_options,
64
+ svelte_compile_module_options,
65
+ svelte_preprocessors,
66
+ } = default_svelte_config;
67
+
68
+ const aliases = Object.entries(alias);
69
+
70
+ const RAW_MATCHER = /(%3Fraw|\.css|\.svg)$/; // TODO others? configurable?
71
+
72
+ /** @nodocs */
73
+ export const load: LoadHook = async (url, context, nextLoad) => {
74
+ // console.log(`url`, url);
75
+ if (SVELTEKIT_SHIM_APP_PATHS_MATCHER.test(url)) {
76
+ // SvelteKit `$app/paths` shim
77
+ return {
78
+ format: 'module',
79
+ shortCircuit: true,
80
+ source: render_sveltekit_shim_app_paths(base_url, assets_url),
81
+ };
82
+ } else if (SVELTEKIT_SHIM_APP_ENVIRONMENT_MATCHER.test(url)) {
83
+ // SvelteKit `$app/environment` shim
84
+ return {
85
+ format: 'module',
86
+ shortCircuit: true,
87
+ source: render_sveltekit_shim_app_environment(dev),
88
+ };
89
+ } else if (SVELTE_RUNES_MATCHER.test(url)) {
90
+ // Svelte runes in js/ts, `.svelte.ts`
91
+ const filename = fileURLToPath(url);
92
+ const loaded = await nextLoad(url, {...context, format: 'module-typescript'});
93
+ const raw_source = loaded.source?.toString(); // eslint-disable-line @typescript-eslint/no-base-to-string
94
+ if (raw_source == null) throw Error(`Failed to load ${url}`);
95
+ // TODO should be nice if we could use Node's builtin amaro transform, but I couldn't find a way after digging into the source, AFAICT it's internal and not exposed
96
+ const source = ts_blank_space(raw_source); // TODO was using oxc-transform and probably should, but this doesn't require sourcemaps, and it's still alpha as of May 2025
97
+ const transformed = compileModule(source, {
98
+ ...svelte_compile_module_options,
99
+ dev,
100
+ filename,
101
+ });
102
+ return {format: 'module', shortCircuit: true, source: transformed.js.code};
103
+ } else if (TS_MATCHER.test(url)) {
104
+ // ts but not `.svelte.ts`
105
+ return nextLoad(url, {...context, format: 'module-typescript'});
106
+ } else if (SVELTE_MATCHER.test(url)) {
107
+ // Svelte, `.svelte`
108
+ const loaded = await nextLoad(url, {...context, format: 'module'});
109
+ const raw_source = loaded.source!.toString(); // eslint-disable-line @typescript-eslint/no-base-to-string
110
+ const filename = fileURLToPath(url);
111
+ const preprocessed = svelte_preprocessors // TODO @many use sourcemaps (and diagnostics?)
112
+ ? await preprocess(raw_source, svelte_preprocessors, {filename})
113
+ : null;
114
+ const source = preprocessed?.code ?? raw_source;
115
+ const transformed = compile(source, {...svelte_compile_options, dev, filename});
116
+ return {format: 'module', shortCircuit: true, source: transformed.js.code};
117
+ } else if (context.importAttributes.type === 'json') {
118
+ // json - any file extension
119
+ // TODO probably follow esbuild and also export every top-level property for objects from the module for good treeshaking - https://esbuild.github.io/content-types/#json (type generation?)
120
+ // TODO why is removing the importAttributes needed? can't pass no context either -
121
+ // error: `Module "file:///home/user/dev/repo/foo.json" is not of type "json"`
122
+ const loaded = await nextLoad(url, {...context, importAttributes: undefined});
123
+ const raw_source = loaded.source?.toString(); // eslint-disable-line @typescript-eslint/no-base-to-string
124
+ if (raw_source == null) throw Error(`Failed to load ${url}`);
125
+ const source = `export default ` + raw_source;
126
+ return {format: 'module', shortCircuit: true, source};
127
+ } else if (RAW_MATCHER.test(url)) {
128
+ // raw text imports like `?raw`, `.css`, `.svg`
129
+ const filename = fileURLToPath(url.endsWith('%3Fraw') ? url.substring(0, url.length - 6) : url);
130
+ const raw_source = readFileSync(filename, 'utf8');
131
+ const source =
132
+ 'export default `' + raw_source.replaceAll('\\', '\\\\').replaceAll('`', '\\`') + '`;';
133
+ return {format: 'module', shortCircuit: true, source};
134
+ } else {
135
+ // SvelteKit `$env`
136
+ // TODO use `format` from the resolve hook to speed this up and make it simpler
137
+ if (context.format === 'sveltekit-env') {
138
+ let mode: 'static' | 'dynamic';
139
+ let visibility: 'public' | 'private';
140
+ switch (context.importAttributes.virtual) {
141
+ case '$env/static/public': {
142
+ mode = 'static';
143
+ visibility = 'public';
144
+ break;
145
+ }
146
+ case '$env/static/private': {
147
+ mode = 'static';
148
+ visibility = 'private';
149
+ break;
150
+ }
151
+ case '$env/dynamic/public': {
152
+ mode = 'dynamic';
153
+ visibility = 'public';
154
+ break;
155
+ }
156
+ case '$env/dynamic/private': {
157
+ mode = 'dynamic';
158
+ visibility = 'private';
159
+ break;
160
+ }
161
+ default: {
162
+ throw Error(`Unknown $env import: ${context.importAttributes.virtual}`);
163
+ }
164
+ }
165
+ const source = render_env_shim_module(
166
+ dev,
167
+ mode,
168
+ visibility,
169
+ public_prefix,
170
+ private_prefix,
171
+ env_dir,
172
+ );
173
+ return {format: 'module', shortCircuit: true, source};
174
+ }
175
+ }
176
+
177
+ // fallback to default behavior
178
+ return nextLoad(url, context);
179
+ };
180
+
181
+ /** @nodocs */
182
+ export const resolve: ResolveHook = async (specifier, context, nextResolve) => {
183
+ let s = specifier;
184
+
185
+ // Support SvelteKit `$env` imports
186
+ if (
187
+ s === '$env/static/public' ||
188
+ s === '$env/static/private' ||
189
+ s === '$env/dynamic/public' ||
190
+ s === '$env/dynamic/private'
191
+ ) {
192
+ // The returned `url` is validated before `load` is called,
193
+ // so we need a slightly roundabout strategy to pass through the specifier for virtual files.
194
+ return {
195
+ url: pathToFileURL(join(dir, 'src/lib', s)).href,
196
+ format: 'sveltekit-env',
197
+ importAttributes: {virtual: s}, // TODO idk I'm just making this up
198
+ shortCircuit: true,
199
+ };
200
+ }
201
+
202
+ // Support SvelteKit `$app` imports, including from node_modules
203
+ const shimmed = sveltekit_shim_app_specifiers.get(s);
204
+ if (shimmed !== undefined) {
205
+ return nextResolve(shimmed, context);
206
+ }
207
+
208
+ // Apply SvelteKit aliases (handles self-referencing packages like @fuzdev/fuz_util -> src/lib)
209
+ s = map_sveltekit_aliases(s, aliases);
210
+
211
+ // Bare specifiers (not starting with . or /) use Node's default resolution
212
+ if (s[0] !== '.' && s[0] !== '/') {
213
+ return nextResolve(s, context);
214
+ }
215
+
216
+ // Resolve paths using Vite conventions
217
+ const parent_url = context.parentURL;
218
+ if (!parent_url) {
219
+ return nextResolve(s, context);
220
+ }
221
+
222
+ const resolved = await resolve_specifier(s, dirname(fileURLToPath(parent_url)));
223
+
224
+ return {
225
+ url: pathToFileURL(resolved.path_id_with_querystring).href,
226
+ format: 'module',
227
+ shortCircuit: true,
228
+ };
229
+ };