@ryanatkn/gro 0.141.0 → 0.142.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 (57) hide show
  1. package/dist/changeset.task.d.ts.map +1 -1
  2. package/dist/changeset.task.js +2 -2
  3. package/dist/filer.d.ts +7 -0
  4. package/dist/filer.d.ts.map +1 -1
  5. package/dist/filer.js +28 -29
  6. package/dist/gro_config.d.ts +5 -0
  7. package/dist/gro_config.d.ts.map +1 -1
  8. package/dist/gro_config.js +3 -1
  9. package/dist/gro_plugin_sveltekit_library.js +4 -4
  10. package/dist/loader.d.ts.map +1 -1
  11. package/dist/loader.js +22 -16
  12. package/dist/moss_helpers.d.ts.map +1 -1
  13. package/dist/moss_helpers.js +2 -1
  14. package/dist/package.d.ts +326 -164
  15. package/dist/package.d.ts.map +1 -1
  16. package/dist/package.js +15 -15
  17. package/dist/package_json.d.ts +9 -6
  18. package/dist/package_json.d.ts.map +1 -1
  19. package/dist/package_json.js +19 -4
  20. package/dist/parse_imports.d.ts.map +1 -1
  21. package/dist/parse_imports.js +4 -2
  22. package/dist/publish.task.d.ts.map +1 -1
  23. package/dist/publish.task.js +3 -8
  24. package/dist/reinstall.task.js +4 -4
  25. package/dist/resolve_node_specifier.d.ts +7 -1
  26. package/dist/resolve_node_specifier.d.ts.map +1 -1
  27. package/dist/resolve_node_specifier.js +77 -13
  28. package/dist/resolve_specifier.d.ts +2 -6
  29. package/dist/resolve_specifier.d.ts.map +1 -1
  30. package/dist/resolve_specifier.js +2 -6
  31. package/dist/src_json.d.ts +39 -3
  32. package/dist/src_json.d.ts.map +1 -1
  33. package/dist/sveltekit_config.d.ts +1 -1
  34. package/dist/sveltekit_config.d.ts.map +1 -1
  35. package/dist/sync.task.js +1 -1
  36. package/dist/upgrade.task.d.ts.map +1 -1
  37. package/dist/upgrade.task.js +4 -2
  38. package/dist/watch_dir.d.ts.map +1 -1
  39. package/dist/watch_dir.js +5 -5
  40. package/package.json +14 -14
  41. package/src/lib/changeset.task.ts +2 -1
  42. package/src/lib/filer.ts +37 -27
  43. package/src/lib/gro_config.ts +8 -0
  44. package/src/lib/gro_plugin_sveltekit_library.ts +4 -4
  45. package/src/lib/loader.ts +24 -16
  46. package/src/lib/moss_helpers.ts +2 -1
  47. package/src/lib/package.ts +15 -15
  48. package/src/lib/package_json.ts +21 -4
  49. package/src/lib/parse_imports.ts +4 -2
  50. package/src/lib/publish.task.ts +3 -9
  51. package/src/lib/reinstall.task.ts +4 -4
  52. package/src/lib/resolve_node_specifier.ts +99 -17
  53. package/src/lib/resolve_specifier.ts +2 -6
  54. package/src/lib/sveltekit_config.ts +1 -1
  55. package/src/lib/sync.task.ts +1 -1
  56. package/src/lib/upgrade.task.ts +4 -2
  57. package/src/lib/watch_dir.ts +6 -6
package/dist/watch_dir.js CHANGED
@@ -1,6 +1,7 @@
1
1
  import { watch } from 'chokidar';
2
2
  import { relative } from 'node:path';
3
3
  import { statSync } from 'node:fs';
4
+ import { create_deferred } from '@ryanatkn/belt/async.js';
4
5
  /**
5
6
  * Watch for changes on the filesystem using chokidar.
6
7
  */
@@ -10,9 +11,8 @@ export const watch_dir = ({ dir, on_change, filter, absolute = true, chokidar, }
10
11
  return {
11
12
  init: async () => {
12
13
  if (initing)
13
- return initing;
14
- let resolve;
15
- initing = new Promise((r) => (resolve = r)); // TODO `create_deferred`?// cwd: chokidar?.cwd ?? process.cwd()
14
+ return initing.promise;
15
+ initing = create_deferred();
16
16
  watcher = watch(dir, { ...chokidar });
17
17
  watcher.on('add', (path) => {
18
18
  const final_path = absolute ? path : relative(dir, path);
@@ -46,8 +46,8 @@ export const watch_dir = ({ dir, on_change, filter, absolute = true, chokidar, }
46
46
  on_change({ type: 'delete', path: final_path, is_directory: true });
47
47
  });
48
48
  // wait until ready
49
- watcher.once('ready', () => resolve());
50
- await initing;
49
+ watcher.once('ready', () => initing?.resolve());
50
+ await initing.promise;
51
51
  },
52
52
  close: async () => {
53
53
  initing = undefined;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ryanatkn/gro",
3
- "version": "0.141.0",
3
+ "version": "0.142.0",
4
4
  "description": "task runner and toolkit extending SvelteKit",
5
5
  "motto": "generate, run, optimize",
6
6
  "glyph": "🌰",
@@ -58,7 +58,7 @@
58
58
  "prettier": "^3.3.3",
59
59
  "prettier-plugin-svelte": "^3.2.7",
60
60
  "ts-morph": "^24.0.0",
61
- "tslib": "^2.7.0",
61
+ "tslib": "^2.8.0",
62
62
  "zod": "^3.23.8"
63
63
  },
64
64
  "peerDependencies": {
@@ -69,21 +69,21 @@
69
69
  "@changesets/changelog-git": "^0.2.0",
70
70
  "@changesets/types": "^6.0.0",
71
71
  "@ryanatkn/eslint-config": "^0.5.5",
72
- "@ryanatkn/fuz": "^0.129.2",
73
- "@ryanatkn/moss": "^0.18.0",
74
- "@sveltejs/adapter-static": "^3.0.5",
75
- "@sveltejs/kit": "^2.6.4",
76
- "@sveltejs/package": "^2.3.5",
77
- "@sveltejs/vite-plugin-svelte": "^4.0.0-next.6",
72
+ "@ryanatkn/fuz": "^0.129.5",
73
+ "@ryanatkn/moss": "^0.18.2",
74
+ "@sveltejs/adapter-static": "^3.0.6",
75
+ "@sveltejs/kit": "^2.7.3",
76
+ "@sveltejs/package": "^2.3.7",
77
+ "@sveltejs/vite-plugin-svelte": "^4.0.0",
78
78
  "@types/fs-extra": "^11.0.4",
79
- "@types/node": "^22.7.5",
79
+ "@types/node": "^22.8.1",
80
80
  "esbuild": "^0.21.5",
81
- "eslint": "^9.12.0",
82
- "eslint-plugin-svelte": "^2.44.1",
83
- "svelte": "^5.0.0-next.264",
84
- "svelte-check": "^4.0.4",
81
+ "eslint": "^9.13.0",
82
+ "eslint-plugin-svelte": "^2.46.0",
83
+ "svelte": "^5.1.3",
84
+ "svelte-check": "^4.0.5",
85
85
  "typescript": "^5.6.3",
86
- "typescript-eslint": "^8.8.1",
86
+ "typescript-eslint": "^8.11.0",
87
87
  "uvu": "^0.5.6"
88
88
  },
89
89
  "prettier": {
@@ -63,6 +63,7 @@ export const task: Task<Args> = {
63
63
  args: {_, minor, major, dir, access: access_arg, changelog, dep, origin, changeset_cli},
64
64
  log,
65
65
  sveltekit_config,
66
+ config,
66
67
  } = ctx;
67
68
 
68
69
  const message = _.join(' ');
@@ -114,7 +115,7 @@ export const task: Task<Args> = {
114
115
  await spawn('git', ['add', dir]);
115
116
 
116
117
  if (dep) {
117
- await spawn('npm', ['i', '-D', changelog]);
118
+ await spawn(config.pm_cli, ['i', '-D', changelog]);
118
119
  }
119
120
  }
120
121
 
package/src/lib/filer.ts CHANGED
@@ -18,6 +18,8 @@ import {resolve_specifier} from './resolve_specifier.js';
18
18
  import {default_sveltekit_config} from './sveltekit_config.js';
19
19
  import {map_sveltekit_aliases} from './sveltekit_helpers.js';
20
20
  import {Unreachable_Error} from '@ryanatkn/belt/error.js';
21
+ import {resolve_node_specifier} from './resolve_node_specifier.js';
22
+ import type {Package_Json} from './package_json.js';
21
23
  // TODO see below
22
24
  // import {resolve_node_specifier} from './resolve_node_specifier.js';
23
25
 
@@ -31,6 +33,11 @@ export interface Source_File {
31
33
  * We create the file in memory to track its dependents regardless of its existence on disk.
32
34
  */
33
35
  contents: string | null;
36
+ /**
37
+ * Is the source file outside of the `root_dir` or excluded by `watch_dir_options.filter`?
38
+ */
39
+ external: boolean;
40
+ ctime: number | null;
34
41
  mtime: number | null;
35
42
  dependents: Map<Path_Id, Source_File>;
36
43
  dependencies: Map<Path_Id, Source_File>;
@@ -43,6 +50,7 @@ export type On_Filer_Change = (change: Watcher_Change, source_file: Source_File)
43
50
  export interface Options {
44
51
  watch_dir?: typeof watch_dir;
45
52
  watch_dir_options?: Partial<Omit_Strict<Watch_Dir_Options, 'on_change'>>;
53
+ package_json_cache?: Record<string, Package_Json>;
46
54
  }
47
55
 
48
56
  export class Filer {
@@ -53,10 +61,13 @@ export class Filer {
53
61
  #watch_dir: typeof watch_dir;
54
62
  #watch_dir_options: Partial<Watch_Dir_Options>;
55
63
 
64
+ #package_json_cache: Record<string, Package_Json>;
65
+
56
66
  constructor(options: Options = EMPTY_OBJECT) {
57
67
  this.#watch_dir = options.watch_dir ?? watch_dir;
58
68
  this.#watch_dir_options = options.watch_dir_options ?? EMPTY_OBJECT;
59
69
  this.root_dir = resolve(options.watch_dir_options?.dir ?? paths.source);
70
+ this.#package_json_cache = options.package_json_cache ?? {};
60
71
  }
61
72
 
62
73
  #watching: Watch_Node_Fs | undefined;
@@ -74,11 +85,17 @@ export class Filer {
74
85
  const file: Source_File = {
75
86
  id,
76
87
  contents: null,
88
+ external: this.#is_external(id), // TODO maybe filter externals by default? the user needs to configure the filer then
89
+ ctime: null,
77
90
  mtime: null,
78
91
  dependents: new Map(),
79
92
  dependencies: new Map(),
80
93
  };
81
94
  this.files.set(id, file);
95
+ // TODO this may need to be batched/deferred
96
+ if (file.external) {
97
+ this.#on_change({type: 'add', path: file.id, is_directory: false});
98
+ }
82
99
  return file;
83
100
  };
84
101
 
@@ -86,8 +103,7 @@ export class Filer {
86
103
  const file = this.get_or_create(id);
87
104
 
88
105
  const stats = existsSync(id) ? statSync(id) : null;
89
- // const mtime_prev = file.mtime;
90
- // const mtime_changed = mtime_prev !== (stats?.mtimeMs ?? null);
106
+ file.ctime = stats?.ctimeMs ?? null;
91
107
  file.mtime = stats?.mtimeMs ?? null;
92
108
 
93
109
  const new_contents = stats ? readFileSync(id, 'utf8') : null;
@@ -109,36 +125,25 @@ export class Filer {
109
125
  const path = map_sveltekit_aliases(specifier, aliases);
110
126
 
111
127
  // The specifier `path` has now been mapped to its final form, so we can inspect it.
112
- if (path[0] === '.' || path[0] === '/') {
113
- const {path_id} = resolve_specifier(path, dir);
114
- dependencies_removed.delete(path_id);
115
- if (!dependencies_before.has(path_id)) {
116
- const d = this.get_or_create(path_id);
117
- file.dependencies.set(d.id, d);
118
- d.dependents.set(file.id, file);
119
- }
128
+ const resolved =
129
+ path[0] === '.' || path[0] === '/'
130
+ ? resolve_specifier(path, dir)
131
+ : resolve_node_specifier(path, undefined, file.id, this.#package_json_cache, false);
132
+ if (!resolved) continue; // ignore any missing imports like Node identifiers
133
+ const {path_id} = resolved;
134
+ dependencies_removed.delete(path_id);
135
+ if (!dependencies_before.has(path_id)) {
136
+ const d = this.get_or_create(path_id);
137
+ file.dependencies.set(d.id, d);
138
+ d.dependents.set(file.id, file);
120
139
  }
121
- // TODO this doesn't work
122
- // const resolved =
123
- // path[0] === '.' || path[0] === '/'
124
- // ? resolve_specifier(path, dir)
125
- // : resolve_node_specifier(path, dir);
126
- // const {path_id} = resolved;
127
- // dependencies_removed.delete(path_id);
128
- // if (!dependencies_before.has(path_id)) {
129
- // const d = this.get_or_create(path_id);
130
- // file.dependencies.set(d.id, d);
131
- // d.dependents.set(file.id, file);
132
- // }
133
140
  }
134
141
 
135
142
  // update any removed dependencies
136
143
  for (const dependency_removed of dependencies_removed) {
137
- const deleted1 = file.dependencies.delete(dependency_removed);
138
- if (!deleted1) throw Error('expected to delete1 ' + file.id); // TODO @many delete if correct
144
+ file.dependencies.delete(dependency_removed);
139
145
  const dependency_removed_file = this.get_or_create(dependency_removed);
140
- const deleted2 = dependency_removed_file.dependents.delete(file.id);
141
- if (!deleted2) throw Error('expected to delete2 ' + file.id); // TODO @many delete if correct
146
+ dependency_removed_file.dependents.delete(file.id);
142
147
  }
143
148
 
144
149
  return file;
@@ -203,7 +208,7 @@ export class Filer {
203
208
  }
204
209
 
205
210
  #on_change: Watcher_Change_Callback = (change) => {
206
- if (change.is_directory) return;
211
+ if (change.is_directory) return; // TODO manage directories?
207
212
  let source_file: Source_File | null;
208
213
  switch (change.type) {
209
214
  case 'add':
@@ -236,4 +241,9 @@ export class Filer {
236
241
  this.#watching = undefined;
237
242
  }
238
243
  }
244
+
245
+ #is_external(id: string): boolean {
246
+ const {filter} = this.#watch_dir_options;
247
+ return !id.startsWith(this.root_dir + '/') || (!!filter && !filter(id, false));
248
+ }
239
249
  }
@@ -40,6 +40,10 @@ export interface Gro_Config {
40
40
  * directories and files are included if they pass all of these filters.
41
41
  */
42
42
  search_filters: Path_Filter[];
43
+ /**
44
+ * The CLI to use that's compatible with `npm install` and `npm link`. Defaults to `'npm'`.
45
+ */
46
+ pm_cli: string;
43
47
  }
44
48
 
45
49
  /**
@@ -52,6 +56,7 @@ export interface Raw_Gro_Config {
52
56
  map_package_json?: Map_Package_Json | null;
53
57
  task_root_dirs?: string[];
54
58
  search_filters?: Path_Filter | Path_Filter[] | null;
59
+ pm_cli?: string;
55
60
  }
56
61
 
57
62
  export type Create_Gro_Config = (
@@ -68,6 +73,7 @@ export const create_empty_gro_config = (): Gro_Config => ({
68
73
  IS_THIS_GRO ? null : GRO_DIST_DIR,
69
74
  ].filter((v) => v !== null),
70
75
  search_filters: [(id) => !DEFAULT_SEARCH_EXCLUDER.test(id)],
76
+ pm_cli: 'npm',
71
77
  });
72
78
 
73
79
  /**
@@ -112,6 +118,7 @@ export const normalize_gro_config = (raw_config: Raw_Gro_Config): Gro_Config =>
112
118
  map_package_json = empty_config.map_package_json,
113
119
  task_root_dirs = empty_config.task_root_dirs,
114
120
  search_filters = empty_config.search_filters,
121
+ pm_cli = empty_config.pm_cli,
115
122
  } = raw_config;
116
123
  return {
117
124
  plugins,
@@ -122,6 +129,7 @@ export const normalize_gro_config = (raw_config: Raw_Gro_Config): Gro_Config =>
122
129
  : search_filters
123
130
  ? [search_filters]
124
131
  : [],
132
+ pm_cli,
125
133
  };
126
134
  };
127
135
 
@@ -33,12 +33,12 @@ export const gro_plugin_sveltekit_library = ({
33
33
  await run_svelte_package(svelte_package_options, svelte_package_cli, log);
34
34
  }
35
35
  },
36
- adapt: async ({log, timings}) => {
36
+ adapt: async ({log, timings, config}) => {
37
37
  const package_json = load_package_json();
38
38
 
39
39
  // `npm link`
40
40
  if (package_json.bin) {
41
- const timing_to_npm_link = timings.start('npm link');
41
+ const timing_to_link = timings.start(`${config.pm_cli} link`);
42
42
  await Promise.all(
43
43
  Object.values(package_json.bin).map(async (bin_path) => {
44
44
  const chmod_result = await spawn('chmod', ['+x', bin_path]);
@@ -47,11 +47,11 @@ export const gro_plugin_sveltekit_library = ({
47
47
  }),
48
48
  );
49
49
  log.info(`linking`);
50
- const link_result = await spawn('npm', ['link', '-f']); // TODO don't use `-f` unless necessary or at all?
50
+ const link_result = await spawn(config.pm_cli, ['link', '-f']); // TODO don't use `-f` unless necessary or at all?
51
51
  if (!link_result.ok) {
52
52
  throw new Task_Error(`Failed to link. ${print_spawn_result(link_result)}`);
53
53
  }
54
- timing_to_npm_link();
54
+ timing_to_link();
55
55
  }
56
56
  },
57
57
  };
package/src/lib/loader.ts CHANGED
@@ -16,7 +16,7 @@ import {
16
16
  } from './sveltekit_shim_app.js';
17
17
  import {default_sveltekit_config} from './sveltekit_config.js';
18
18
  import {SVELTE_MATCHER, SVELTE_RUNES_MATCHER} from './svelte_helpers.js';
19
- import {paths} from './paths.js';
19
+ import {IS_THIS_GRO, paths} from './paths.js';
20
20
  import {JSON_MATCHER, NODE_MODULES_DIRNAME, TS_MATCHER} from './path_constants.js';
21
21
  import {to_define_import_meta_env, default_ts_transform_options} from './esbuild_helpers.js';
22
22
  import {resolve_specifier} from './resolve_specifier.js';
@@ -173,51 +173,59 @@ export const load: LoadHook = async (url, context, nextLoad) => {
173
173
  };
174
174
 
175
175
  export const resolve: ResolveHook = async (specifier, context, nextResolve) => {
176
+ let s = specifier;
177
+
178
+ // Support SvelteKit `$env` imports
176
179
  if (
177
- specifier === '$env/static/public' ||
178
- specifier === '$env/static/private' ||
179
- specifier === '$env/dynamic/public' ||
180
- specifier === '$env/dynamic/private'
180
+ s === '$env/static/public' ||
181
+ s === '$env/static/private' ||
182
+ s === '$env/dynamic/public' ||
183
+ s === '$env/dynamic/private'
181
184
  ) {
182
185
  // The returned `url` is validated before `load` is called,
183
186
  // so we need a slightly roundabout strategy to pass through the specifier for virtual files.
184
187
  return {
185
- url: pathToFileURL(join(dir, 'src/lib', specifier)).href,
188
+ url: pathToFileURL(join(dir, 'src/lib', s)).href,
186
189
  format: 'module',
187
190
  shortCircuit: true,
188
191
  };
189
192
  }
190
193
 
194
+ // Special case for Gro's dependencies that import into Gro.
195
+ // Without this, we'd need to add a dev dep to Gro for Gro, which causes problems.
196
+ if (IS_THIS_GRO && s.startsWith('@ryanatkn/gro')) {
197
+ s = join(dir, 'dist', s.substring(13));
198
+ }
199
+
191
200
  const parent_url = context.parentURL;
192
201
  if (!parent_url || NODE_MODULES_MATCHER.test(parent_url)) {
193
- return nextResolve(specifier, context);
202
+ return nextResolve(s, context);
194
203
  }
195
204
 
196
- const shimmed = sveltekit_shim_app_specifiers.get(specifier);
205
+ const shimmed = sveltekit_shim_app_specifiers.get(s);
197
206
  if (shimmed !== undefined) {
198
207
  return nextResolve(shimmed, context);
199
208
  }
200
209
 
201
- const path = map_sveltekit_aliases(specifier, aliases);
210
+ s = map_sveltekit_aliases(s, aliases);
202
211
 
203
- // The specifier `path` has now been mapped to its final form, so we can inspect it.
204
- if (path[0] !== '.' && path[0] !== '/') {
212
+ // The specifier has now been mapped to its final form, so we can inspect it.
213
+ if (s[0] !== '.' && s[0] !== '/') {
205
214
  // Resolve to `node_modules`.
206
- if (SVELTE_MATCHER.test(path) || JSON_MATCHER.test(path)) {
215
+ if (SVELTE_MATCHER.test(s) || JSON_MATCHER.test(s)) {
207
216
  // Match the behavior of Vite and esbuild for Svelte and JSON imports.
208
- // TODO `.ts` too
209
- const resolved = resolve_node_specifier(path, dir, parent_url, package_json_cache);
217
+ const resolved = resolve_node_specifier(s, dir, parent_url, package_json_cache)!; // `node:` specifiers shouldn't reach this point, so the assertion is safe
210
218
  return {
211
219
  url: pathToFileURL(resolved.path_id_with_querystring).href,
212
220
  format: 'module',
213
221
  shortCircuit: true,
214
222
  };
215
223
  } else {
216
- return nextResolve(path, context);
224
+ return nextResolve(s, context);
217
225
  }
218
226
  }
219
227
 
220
- const resolved = resolve_specifier(path, dirname(fileURLToPath(parent_url)));
228
+ const resolved = resolve_specifier(s, dirname(fileURLToPath(parent_url)));
221
229
 
222
230
  return {
223
231
  url: pathToFileURL(resolved.path_id_with_querystring).href,
@@ -3,6 +3,7 @@ import {existsSync} from 'node:fs';
3
3
  import {resolve} from 'node:path';
4
4
 
5
5
  import {has_dep, type Package_Json} from './package_json.js';
6
+ import {NODE_MODULES_DIRNAME} from './path_constants.js';
6
7
 
7
8
  export const MOSS_PACKAGE_DEP_NAME = '@ryanatkn/moss';
8
9
 
@@ -10,7 +11,7 @@ export const MOSS_PACKAGE_DEP_NAME = '@ryanatkn/moss';
10
11
  export const load_moss_plugin = async (
11
12
  package_json?: Package_Json,
12
13
  dep_name = MOSS_PACKAGE_DEP_NAME,
13
- plugin_path = `node_modules/${dep_name}/dist/gro_plugin_moss.js`, // TODO maybe lookup from its `package_json.exports`? kinda unnecessary
14
+ plugin_path = `${NODE_MODULES_DIRNAME}/${dep_name}/dist/gro_plugin_moss.js`, // TODO maybe lookup from its `package_json.exports`? kinda unnecessary
14
15
  local_plugin_path = 'src/lib/gro_plugin_moss.ts',
15
16
  ): Promise<Result<{gro_plugin_moss: any}, {message: string}>> => {
16
17
  if (!has_dep(dep_name, package_json)) {
@@ -5,7 +5,7 @@ import type {Src_Json} from './src_json.js';
5
5
 
6
6
  export const package_json = {
7
7
  name: '@ryanatkn/gro',
8
- version: '0.141.0',
8
+ version: '0.142.0',
9
9
  description: 'task runner and toolkit extending SvelteKit',
10
10
  motto: 'generate, run, optimize',
11
11
  glyph: '🌰',
@@ -53,7 +53,7 @@ export const package_json = {
53
53
  prettier: '^3.3.3',
54
54
  'prettier-plugin-svelte': '^3.2.7',
55
55
  'ts-morph': '^24.0.0',
56
- tslib: '^2.7.0',
56
+ tslib: '^2.8.0',
57
57
  zod: '^3.23.8',
58
58
  },
59
59
  peerDependencies: {esbuild: '^0.21.0', svelte: '^5.0.0-next.0'},
@@ -61,21 +61,21 @@ export const package_json = {
61
61
  '@changesets/changelog-git': '^0.2.0',
62
62
  '@changesets/types': '^6.0.0',
63
63
  '@ryanatkn/eslint-config': '^0.5.5',
64
- '@ryanatkn/fuz': '^0.129.2',
65
- '@ryanatkn/moss': '^0.18.0',
66
- '@sveltejs/adapter-static': '^3.0.5',
67
- '@sveltejs/kit': '^2.6.4',
68
- '@sveltejs/package': '^2.3.5',
69
- '@sveltejs/vite-plugin-svelte': '^4.0.0-next.6',
64
+ '@ryanatkn/fuz': '^0.129.5',
65
+ '@ryanatkn/moss': '^0.18.2',
66
+ '@sveltejs/adapter-static': '^3.0.6',
67
+ '@sveltejs/kit': '^2.7.3',
68
+ '@sveltejs/package': '^2.3.7',
69
+ '@sveltejs/vite-plugin-svelte': '^4.0.0',
70
70
  '@types/fs-extra': '^11.0.4',
71
- '@types/node': '^22.7.5',
71
+ '@types/node': '^22.8.1',
72
72
  esbuild: '^0.21.5',
73
- eslint: '^9.12.0',
74
- 'eslint-plugin-svelte': '^2.44.1',
75
- svelte: '^5.0.0-next.264',
76
- 'svelte-check': '^4.0.4',
73
+ eslint: '^9.13.0',
74
+ 'eslint-plugin-svelte': '^2.46.0',
75
+ svelte: '^5.1.3',
76
+ 'svelte-check': '^4.0.5',
77
77
  typescript: '^5.6.3',
78
- 'typescript-eslint': '^8.8.1',
78
+ 'typescript-eslint': '^8.11.0',
79
79
  uvu: '^0.5.6',
80
80
  },
81
81
  prettier: {
@@ -270,7 +270,7 @@ export const package_json = {
270
270
 
271
271
  export const src_json = {
272
272
  name: '@ryanatkn/gro',
273
- version: '0.141.0',
273
+ version: '0.142.0',
274
274
  modules: {
275
275
  '.': {
276
276
  path: 'index.ts',
@@ -21,7 +21,7 @@ export const Email = z.string();
21
21
  export type Email = Flavored<z.infer<typeof Email>, 'Email'>;
22
22
 
23
23
  // TODO move this where?
24
- export const transform_empty_object_to_undefined = (val: any): any => {
24
+ export const transform_empty_object_to_undefined = <T>(val: T): T | undefined => {
25
25
  if (val && Object.keys(val).length === 0) {
26
26
  return;
27
27
  }
@@ -63,8 +63,19 @@ export const Package_Json_Funding = z.union([
63
63
  ]);
64
64
  export type Package_Json_Funding = z.infer<typeof Package_Json_Funding>;
65
65
 
66
+ // exports: {
67
+ // './': './index.js',
68
+ // './record': {default: './record.js'},
69
+ // './export_condition': {default: {development: './ec1', default: './ec2.js'}},
70
+ // }
66
71
  export const Package_Json_Exports = z.record(
67
- z.union([z.string(), z.record(z.string())]).optional(),
72
+ z
73
+ .union([
74
+ z.string(),
75
+ z.record(z.string().optional()),
76
+ z.record(z.record(z.string().optional()).optional()),
77
+ ])
78
+ .optional(),
68
79
  );
69
80
  export type Package_Json_Exports = z.infer<typeof Package_Json_Exports>;
70
81
 
@@ -131,6 +142,7 @@ export const Package_Json = z
131
142
  bin: z.record(z.string()).optional(),
132
143
  sideEffects: z.array(z.string()).optional(),
133
144
  files: z.array(z.string()).optional(),
145
+ main: z.string().optional(),
134
146
  exports: Package_Json_Exports.transform(transform_empty_object_to_undefined).optional(),
135
147
  })
136
148
  .passthrough();
@@ -145,6 +157,7 @@ export const EMPTY_PACKAGE_JSON: Package_Json = {name: '', version: ''};
145
157
  export const load_package_json = (
146
158
  dir = IS_THIS_GRO ? gro_paths.root : paths.root,
147
159
  cache?: Record<string, Package_Json>,
160
+ parse = true, // TODO pass `false` here in more places, especially anything perf-sensitive like work on startup
148
161
  ): Package_Json => {
149
162
  let package_json: Package_Json;
150
163
  if (cache && dir in cache) {
@@ -155,8 +168,12 @@ export const load_package_json = (
155
168
  } catch (_err) {
156
169
  return EMPTY_PACKAGE_JSON;
157
170
  }
158
- package_json = parse_package_json(Package_Json, package_json);
159
- if (cache) cache[dir] = package_json;
171
+ if (parse) {
172
+ package_json = parse_package_json(Package_Json, package_json);
173
+ }
174
+ if (cache) {
175
+ cache[dir] = package_json;
176
+ }
160
177
  return package_json;
161
178
  };
162
179
 
@@ -2,6 +2,8 @@ import {init, parse} from 'es-module-lexer';
2
2
  import type {Flavored} from '@ryanatkn/belt/types.js';
3
3
 
4
4
  import type {Path_Id} from './path.js';
5
+ import {SVELTE_MATCHER} from './svelte_helpers.js';
6
+ import {JS_MATCHER, TS_MATCHER} from './path_constants.js';
5
7
 
6
8
  export const init_lexer = (): Promise<void> => init;
7
9
 
@@ -29,12 +31,12 @@ export const parse_imports = (
29
31
  }
30
32
  };
31
33
 
32
- if (id.endsWith('.svelte')) {
34
+ if (SVELTE_MATCHER.test(id)) {
33
35
  const matches = contents.matchAll(script_matcher);
34
36
  for (const m of matches) {
35
37
  parse_from(m[1]);
36
38
  }
37
- } else {
39
+ } else if (TS_MATCHER.test(id) || JS_MATCHER.test(id)) {
38
40
  parse_from(contents);
39
41
  }
40
42
 
@@ -6,7 +6,6 @@ import {existsSync} from 'node:fs';
6
6
  import {Task_Error, type Task} from './task.js';
7
7
  import {load_package_json, parse_repo_url} from './package_json.js';
8
8
  import {find_cli, spawn_cli} from './cli.js';
9
- import {IS_THIS_GRO} from './paths.js';
10
9
  import {has_sveltekit_library} from './sveltekit_helpers.js';
11
10
  import {update_changelog} from './changelog.js';
12
11
  import {load_from_env} from './env.js';
@@ -84,11 +83,6 @@ export const task: Task<Args> = {
84
83
  );
85
84
  }
86
85
 
87
- // TODO hacky, ensures Gro bootstraps itself
88
- if (IS_THIS_GRO) {
89
- await spawn('npm', ['run', 'build']);
90
- }
91
-
92
86
  const changelog_exists = existsSync(changelog);
93
87
 
94
88
  const found_changeset_cli = find_cli(changeset_cli);
@@ -139,9 +133,9 @@ export const task: Task<Args> = {
139
133
 
140
134
  // This is the first line that alters the repo.
141
135
 
142
- const npmVersionResult = await spawn_cli(found_changeset_cli, ['version'], log);
143
- if (!npmVersionResult?.ok) {
144
- throw Error('npm version failed: no commits were made: see the error above');
136
+ const changeset_ersion_result = await spawn_cli(found_changeset_cli, ['version'], log);
137
+ if (!changeset_ersion_result?.ok) {
138
+ throw Error('changeset version failed: no commits were made: see the error above');
145
139
  }
146
140
 
147
141
  if (!preserve_changelog) {
@@ -11,9 +11,9 @@ export type Args = z.infer<typeof Args>;
11
11
  export const task: Task<Args> = {
12
12
  summary: `refreshes ${LOCKFILE_FILENAME} with the latest and cleanest deps`,
13
13
  Args,
14
- run: async ({log}): Promise<void> => {
14
+ run: async ({log, config}): Promise<void> => {
15
15
  log.info('running the initial npm install');
16
- const initial_install_result = await spawn('npm', ['i']);
16
+ const initial_install_result = await spawn(config.pm_cli, ['i']);
17
17
  if (!initial_install_result.ok) {
18
18
  throw new Task_Error('Failed initial npm install');
19
19
  }
@@ -23,7 +23,7 @@ export const task: Task<Args> = {
23
23
  log.info(
24
24
  `running npm install after deleting ${LOCKFILE_FILENAME} and ${NODE_MODULES_DIRNAME}, this can take a while...`,
25
25
  );
26
- const second_install_result = await spawn('npm', ['i']);
26
+ const second_install_result = await spawn(config.pm_cli, ['i']);
27
27
  if (!second_install_result.ok) {
28
28
  throw new Task_Error(
29
29
  `Failed npm install after deleting ${LOCKFILE_FILENAME} and ${NODE_MODULES_DIRNAME}`,
@@ -34,7 +34,7 @@ export const task: Task<Args> = {
34
34
  // like esbuild's many packages for each platform.
35
35
  await rm(LOCKFILE_FILENAME);
36
36
  log.info(`running npm install one last time to clean ${LOCKFILE_FILENAME}`);
37
- const final_install_result = await spawn('npm', ['i']);
37
+ const final_install_result = await spawn(config.pm_cli, ['i']);
38
38
  if (!final_install_result.ok) {
39
39
  throw new Task_Error('Failed npm install');
40
40
  }