@ryanatkn/gro 0.135.2 → 0.135.3

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 (60) hide show
  1. package/dist/check.task.d.ts +2 -2
  2. package/dist/filer.d.ts +29 -0
  3. package/dist/filer.d.ts.map +1 -0
  4. package/dist/filer.js +165 -0
  5. package/dist/gen.task.d.ts +2 -2
  6. package/dist/gro.config.default.d.ts.map +1 -1
  7. package/dist/gro.config.default.js +2 -3
  8. package/dist/gro_plugin_gen.d.ts +9 -2
  9. package/dist/gro_plugin_gen.d.ts.map +1 -1
  10. package/dist/gro_plugin_gen.js +67 -37
  11. package/dist/gro_plugin_sveltekit_library.d.ts.map +1 -1
  12. package/dist/gro_plugin_sveltekit_library.js +4 -16
  13. package/dist/invoke_task.d.ts +2 -1
  14. package/dist/invoke_task.d.ts.map +1 -1
  15. package/dist/invoke_task.js +4 -2
  16. package/dist/loader.d.ts.map +1 -1
  17. package/dist/loader.js +3 -9
  18. package/dist/package.d.ts +22 -0
  19. package/dist/package.d.ts.map +1 -1
  20. package/dist/package.js +39 -13
  21. package/dist/parse_imports.d.ts +6 -0
  22. package/dist/parse_imports.d.ts.map +1 -0
  23. package/dist/parse_imports.js +29 -0
  24. package/dist/path_constants.d.ts +1 -0
  25. package/dist/path_constants.d.ts.map +1 -1
  26. package/dist/path_constants.js +1 -0
  27. package/dist/paths.d.ts +2 -1
  28. package/dist/paths.d.ts.map +1 -1
  29. package/dist/paths.js +3 -1
  30. package/dist/run_task.d.ts +2 -1
  31. package/dist/run_task.d.ts.map +1 -1
  32. package/dist/run_task.js +4 -3
  33. package/dist/sveltekit_config.d.ts +1 -1
  34. package/dist/sveltekit_config.d.ts.map +1 -1
  35. package/dist/sveltekit_config.js +1 -1
  36. package/dist/sveltekit_helpers.d.ts +6 -0
  37. package/dist/sveltekit_helpers.d.ts.map +1 -1
  38. package/dist/sveltekit_helpers.js +32 -0
  39. package/dist/sync.task.d.ts +2 -2
  40. package/dist/task.d.ts +2 -0
  41. package/dist/task.d.ts.map +1 -1
  42. package/dist/watch_dir.d.ts +1 -1
  43. package/dist/watch_dir.d.ts.map +1 -1
  44. package/dist/watch_dir.js +14 -12
  45. package/package.json +19 -11
  46. package/src/lib/filer.ts +212 -0
  47. package/src/lib/gro.config.default.ts +2 -3
  48. package/src/lib/gro_plugin_gen.ts +90 -43
  49. package/src/lib/gro_plugin_sveltekit_library.ts +4 -20
  50. package/src/lib/invoke_task.ts +6 -1
  51. package/src/lib/loader.ts +3 -10
  52. package/src/lib/package.ts +39 -13
  53. package/src/lib/parse_imports.ts +42 -0
  54. package/src/lib/path_constants.ts +1 -0
  55. package/src/lib/paths.ts +8 -1
  56. package/src/lib/run_task.ts +5 -2
  57. package/src/lib/sveltekit_config.ts +2 -2
  58. package/src/lib/sveltekit_helpers.ts +46 -0
  59. package/src/lib/task.ts +2 -0
  60. package/src/lib/watch_dir.ts +15 -14
@@ -0,0 +1,212 @@
1
+ import {EMPTY_OBJECT} from '@ryanatkn/belt/object.js';
2
+ import {existsSync, readFileSync} from 'node:fs';
3
+ import {dirname, resolve} from 'node:path';
4
+ import type {Omit_Strict} from '@ryanatkn/belt/types.js';
5
+ import {wait} from '@ryanatkn/belt/async.js';
6
+
7
+ import type {Path_Id} from './path.js';
8
+ import {
9
+ watch_dir,
10
+ type Watch_Node_Fs,
11
+ type Watcher_Change,
12
+ type Options as Watch_Dir_Options,
13
+ type Watcher_Change_Callback,
14
+ } from './watch_dir.js';
15
+ import {default_file_filter, paths} from './paths.js';
16
+ import {parse_imports} from './parse_imports.js';
17
+ import {resolve_specifier} from './resolve_specifier.js';
18
+ import {default_sveltekit_config} from './sveltekit_config.js';
19
+ import {map_sveltekit_aliases} from './sveltekit_helpers.js';
20
+ import {Unreachable_Error} from '@ryanatkn/belt/error.js';
21
+
22
+ const aliases = Object.entries(default_sveltekit_config.alias);
23
+
24
+ export interface Source_File {
25
+ id: Path_Id;
26
+ /**
27
+ * `null` contents means it doesn't exist.
28
+ * We create the file in memory to track its dependents regardless of its existence on disk.
29
+ */
30
+ contents: string | null;
31
+ dependents: Map<Path_Id, Source_File>;
32
+ dependencies: Map<Path_Id, Source_File>;
33
+ }
34
+
35
+ export type Cleanup_Watch = () => Promise<void>;
36
+
37
+ export type On_Filer_Change = (change: Watcher_Change, source_file: Source_File) => void;
38
+
39
+ export interface Options {
40
+ watch_dir?: typeof watch_dir;
41
+ watch_dir_options?: Partial<Omit_Strict<Watch_Dir_Options, 'on_change'>>;
42
+ }
43
+
44
+ export class Filer {
45
+ files: Map<Path_Id, Source_File> = new Map();
46
+
47
+ #watch_dir: typeof watch_dir;
48
+ #watch_dir_options: Partial<Watch_Dir_Options>;
49
+
50
+ constructor(options: Options = EMPTY_OBJECT) {
51
+ this.#watch_dir = options.watch_dir ?? watch_dir;
52
+ this.#watch_dir_options = options.watch_dir_options ?? EMPTY_OBJECT;
53
+ }
54
+
55
+ #watching: Watch_Node_Fs | undefined;
56
+ #listeners: Set<On_Filer_Change> = new Set();
57
+
58
+ #ready = false;
59
+
60
+ get_by_id = (id: Path_Id): Source_File | undefined => {
61
+ return this.files.get(id);
62
+ };
63
+
64
+ get_or_create = (id: Path_Id): Source_File => {
65
+ const existing = this.get_by_id(id);
66
+ if (existing) return existing;
67
+ const file: Source_File = {
68
+ id,
69
+ contents: null,
70
+ dependents: new Map(),
71
+ dependencies: new Map(),
72
+ };
73
+ this.files.set(id, file);
74
+ return file;
75
+ };
76
+
77
+ #update(id: Path_Id): Source_File {
78
+ const file = this.get_or_create(id);
79
+ const new_contents = existsSync(id) ? readFileSync(id, 'utf8') : null;
80
+ const contents_changed = file.contents !== new_contents;
81
+ file.contents = new_contents;
82
+
83
+ if (contents_changed) {
84
+ const dir = dirname(file.id);
85
+
86
+ const dependencies_before = new Set(file.dependencies.keys());
87
+ const dependencies_removed = new Set(dependencies_before);
88
+
89
+ const imported = file.contents ? parse_imports(file.id, file.contents) : [];
90
+ for (const specifier of imported) {
91
+ // TODO logic is duplicated from loader
92
+ const path = map_sveltekit_aliases(specifier, aliases);
93
+
94
+ // The specifier `path` has now been mapped to its final form, so we can inspect it.
95
+ if (path[0] === '.' || path[0] === '/') {
96
+ const {path_id} = resolve_specifier(path, dir);
97
+ dependencies_removed.delete(path_id);
98
+ if (!dependencies_before.has(path_id)) {
99
+ const d = this.get_or_create(path_id);
100
+ file.dependencies.set(d.id, d);
101
+ d.dependents.set(file.id, file);
102
+ }
103
+ }
104
+ }
105
+
106
+ // update any removed dependencies
107
+ for (const dependency_removed of dependencies_removed) {
108
+ const deleted1 = file.dependencies.delete(dependency_removed);
109
+ if (!deleted1) throw Error('expected to delete1 ' + file.id); // TODO @many delete if correct
110
+ const dependency_removed_file = this.get_or_create(dependency_removed);
111
+ const deleted2 = dependency_removed_file.dependents.delete(file.id);
112
+ if (!deleted2) throw Error('expected to delete2 ' + file.id); // TODO @many delete if correct
113
+ }
114
+ }
115
+
116
+ return file;
117
+ }
118
+
119
+ #remove(id: Path_Id): Source_File | undefined {
120
+ const file = this.get_by_id(id);
121
+ if (!file) return; // this is safe because the object would exist if any other file referenced it as a dependency or dependent
122
+
123
+ file.contents = null; // clear contents in case it gets re-added later, we want the change to be detected
124
+
125
+ let found = false;
126
+ for (const d of this.files.values()) {
127
+ if (d.dependencies.has(file.id)) {
128
+ found = true;
129
+ break;
130
+ }
131
+ }
132
+ if (!found) this.files.delete(id);
133
+
134
+ return file;
135
+ }
136
+
137
+ #notify_listener(listener: On_Filer_Change): void {
138
+ if (!this.#ready) return;
139
+ for (const source_file of this.files.values()) {
140
+ listener({type: 'add', path: source_file.id, is_directory: false}, source_file);
141
+ }
142
+ }
143
+
144
+ #notify_change(change: Watcher_Change, source_file: Source_File): void {
145
+ if (!this.#ready) return;
146
+ for (const listener of this.#listeners) {
147
+ listener(change, source_file);
148
+ }
149
+ }
150
+
151
+ async #add_listener(listener: On_Filer_Change): Promise<void> {
152
+ this.#listeners.add(listener);
153
+ if (this.#watching) {
154
+ // if already watching, call the listener for all existing files after init
155
+ await this.#watching.init();
156
+ await wait(); // wait a tick to ensure the `this.#ready` value is updated below first
157
+ this.#notify_listener(listener);
158
+ return;
159
+ }
160
+ this.#watching = this.#watch_dir({
161
+ filter: (path, is_directory) => (is_directory ? true : default_file_filter(path)),
162
+ ...this.#watch_dir_options,
163
+ dir: resolve(this.#watch_dir_options.dir ?? paths.source),
164
+ on_change: this.#on_change,
165
+ });
166
+ await this.#watching.init();
167
+ this.#ready = true;
168
+ this.#notify_listener(listener);
169
+ }
170
+
171
+ async #remove_listener(listener: On_Filer_Change): Promise<void> {
172
+ this.#listeners.delete(listener);
173
+ if (this.#listeners.size === 0) {
174
+ await this.close(); // TODO is this right? should `watch` be async?
175
+ }
176
+ }
177
+
178
+ #on_change: Watcher_Change_Callback = (change) => {
179
+ if (change.is_directory) return;
180
+ let source_file: Source_File | undefined;
181
+ switch (change.type) {
182
+ case 'add':
183
+ case 'update': {
184
+ source_file = this.#update(change.path);
185
+ break;
186
+ }
187
+ case 'delete': {
188
+ source_file = this.#remove(change.path);
189
+ break;
190
+ }
191
+ default:
192
+ throw new Unreachable_Error(change.type);
193
+ }
194
+ if (source_file) {
195
+ this.#notify_change(change, source_file);
196
+ }
197
+ };
198
+
199
+ async watch(listener: On_Filer_Change): Promise<Cleanup_Watch> {
200
+ await this.#add_listener(listener);
201
+ return () => this.#remove_listener(listener);
202
+ }
203
+
204
+ async close(): Promise<void> {
205
+ this.#ready = false;
206
+ this.#listeners.clear();
207
+ if (this.#watching) {
208
+ await this.#watching.close();
209
+ this.#watching = undefined;
210
+ }
211
+ }
212
+ }
@@ -3,6 +3,7 @@ import {gro_plugin_sveltekit_library} from './gro_plugin_sveltekit_library.js';
3
3
  import {has_server, gro_plugin_server} from './gro_plugin_server.js';
4
4
  import {gro_plugin_sveltekit_app} from './gro_plugin_sveltekit_app.js';
5
5
  import {has_sveltekit_app, has_sveltekit_library} from './sveltekit_helpers.js';
6
+ import {gro_plugin_gen} from './gro_plugin_gen.js';
6
7
 
7
8
  /**
8
9
  * This is the default config that's passed to `gro.config.ts`
@@ -23,9 +24,7 @@ const config: Create_Gro_Config = async (cfg) => {
23
24
  has_sveltekit_app_result.ok
24
25
  ? gro_plugin_sveltekit_app({host_target: has_server_result.ok ? 'node' : 'github_pages'})
25
26
  : null,
26
- // TODO replace with an esbuild plugin, see the module for more
27
- // import {gro_plugin_gen} from './gro_plugin_gen.js';
28
- // gro_plugin_gen(),
27
+ gro_plugin_gen(),
29
28
  ];
30
29
 
31
30
  return cfg;
@@ -1,13 +1,14 @@
1
- // TODO this became unused with https://github.com/ryanatkn/gro/pull/382
2
- // because we no longer have a normal system build - replace with an esbuild plugin
3
- // @ts-nocheck
1
+ import {EMPTY_OBJECT} from '@ryanatkn/belt/object.js';
4
2
 
5
- import type {Plugin, Plugin_Context} from './plugin.js';
3
+ import type {Plugin} from './plugin.js';
6
4
  import type {Args} from './args.js';
7
- import {path_id_to_base_path} from './paths.js';
5
+ import {paths} from './paths.js';
8
6
  import {find_genfiles, is_gen_path} from './gen.js';
9
- import {filter_dependents} from './build/source_file.js';
10
7
  import {throttle} from './throttle.js';
8
+ import {spawn_cli} from './cli.js';
9
+ import type {File_Filter, Path_Id} from './path.js';
10
+ import type {Cleanup_Watch, Source_File} from './filer.js';
11
+ import {Unreachable_Error} from '@ryanatkn/belt/error.js';
11
12
 
12
13
  const FLUSH_DEBOUNCE_DELAY = 500;
13
14
 
@@ -15,14 +16,28 @@ export interface Task_Args extends Args {
15
16
  watch?: boolean;
16
17
  }
17
18
 
18
- export const plugin = (): Plugin<Plugin_Context<Task_Args>> => {
19
+ export interface Options {
20
+ root_dirs?: string[];
21
+ flush_debounce_delay?: number;
22
+ }
23
+
24
+ export const gro_plugin_gen = ({
25
+ root_dirs = [paths.source],
26
+ flush_debounce_delay = FLUSH_DEBOUNCE_DELAY,
27
+ }: Options = EMPTY_OBJECT): Plugin => {
28
+ const input_path = paths.source; // TODO option?
19
29
  let generating = false;
20
30
  let regen = false;
21
- let on_filer_build: ((e: Filer_Events['build']) => void) | undefined;
31
+ let flushing_timeout: NodeJS.Timeout | undefined;
22
32
  const queued_files: Set<string> = new Set();
23
- const queue_gen = (gen_file_name: string) => {
24
- queued_files.add(gen_file_name);
25
- void flush_gen_queue();
33
+ const queue_gen = (gen_file_id: string) => {
34
+ queued_files.add(gen_file_id);
35
+ if (flushing_timeout === undefined) {
36
+ flushing_timeout = setTimeout(() => {
37
+ flushing_timeout = undefined;
38
+ void flush_gen_queue();
39
+ }); // the timeout batches synchronously
40
+ }
26
41
  };
27
42
  const flush_gen_queue = throttle(async () => {
28
43
  // hacky way to avoid concurrent `gro gen` calls
@@ -39,57 +54,89 @@ export const plugin = (): Plugin<Plugin_Context<Task_Args>> => {
39
54
  regen = false;
40
55
  void flush_gen_queue();
41
56
  }
42
- }, FLUSH_DEBOUNCE_DELAY);
57
+ }, flush_debounce_delay);
43
58
  const gen = (files: string[] = []) => spawn_cli('gro', ['gen', ...files]);
44
59
 
60
+ let cleanup: Cleanup_Watch | undefined;
61
+
45
62
  return {
46
63
  name: 'gro_plugin_gen',
47
- setup: async ({args: {watch}, dev, log, config}) => {
64
+ setup: async ({watch, dev, log, config, filer}) => {
48
65
  // For production builds, we assume `gen` is already fresh,
49
66
  // which should be checked by CI via `gro check` which calls `gro gen --check`.
50
67
  if (!dev) return;
51
68
 
52
- // Run `gen`, first checking if there are any modules to avoid a console error.
53
- // Some parts of the build may have already happened,
54
- // making us miss `build` events for gen dependencies,
55
- // so we run `gen` here even if it's usually wasteful.
56
- const found = find_genfiles([paths.source], root_dirs, config);
57
- if (found.ok && found.value.resolved_input_files.size > 0) {
58
- await gen();
59
- }
60
-
61
69
  // Do we need to just generate everything once and exit?
62
70
  // TODO could we have an esbuild context here? problem is watching the right files, maybe a plugin that tracks deps
63
- if (!filer || !watch) {
71
+ if (!watch) {
64
72
  log.info('generating and exiting early');
73
+
74
+ // Run `gen`, first checking if there are any modules to avoid a console error.
75
+ // Some parts of the build may have already happened,
76
+ // making us miss `build` events for gen dependencies,
77
+ // so we run `gen` here even if it's usually wasteful.
78
+ const found = find_genfiles([input_path], root_dirs, config);
79
+ if (found.ok && found.value.resolved_input_files.length > 0) {
80
+ await gen();
81
+ }
65
82
  return;
66
83
  }
67
84
 
68
85
  // When a file builds, check it and its tree of dependents
69
86
  // for any `.gen.` files that need to run.
70
- on_filer_build = ({source_file, build_config}) => {
71
- // TODO how to handle this now? the loader traces deps for us with `parentPath`,
72
- // but we probably want to make this an esbuild plugin instead
73
- // if (build_config.name !== 'system') return;
74
- if (is_gen_path(source_file.id)) {
75
- queue_gen(path_id_to_base_path(source_file.id));
76
- }
77
- const dependent_gen_file_ids = filter_dependents(
78
- source_file,
79
- build_config,
80
- filer.find_by_id as any, // cast because we can assume they're all `SourceFile`s
81
- is_gen_path,
82
- );
83
- for (const dependent_gen_file_id of dependent_gen_file_ids) {
84
- queue_gen(path_id_to_base_path(dependent_gen_file_id));
87
+ cleanup = await filer.watch((change, source_file) => {
88
+ switch (change.type) {
89
+ case 'add':
90
+ case 'update': {
91
+ // TODO how to handle this now? the loader traces deps for us with `parentPath`,
92
+ // but we probably want to make this an esbuild plugin instead
93
+ if (is_gen_path(source_file.id)) {
94
+ queue_gen(source_file.id);
95
+ }
96
+ const dependent_gen_file_ids = filter_dependents(
97
+ source_file,
98
+ filer.get_by_id,
99
+ is_gen_path,
100
+ );
101
+ for (const dependent_gen_file_id of dependent_gen_file_ids) {
102
+ queue_gen(dependent_gen_file_id);
103
+ }
104
+ break;
105
+ }
106
+ case 'delete': {
107
+ // TODO delete the generated file(s)? option?
108
+ break;
109
+ }
110
+ default:
111
+ throw new Unreachable_Error(change.type);
85
112
  }
86
- };
87
- filer.on('build', on_filer_build);
113
+ });
88
114
  },
89
- teardown: ({filer}) => {
90
- if (on_filer_build && filer) {
91
- filer.off('build', on_filer_build);
115
+ teardown: async () => {
116
+ if (cleanup !== undefined) {
117
+ await cleanup();
118
+ cleanup = undefined;
92
119
  }
93
120
  },
94
121
  };
95
122
  };
123
+
124
+ export const filter_dependents = (
125
+ source_file: Source_File,
126
+ get_by_id: (id: Path_Id) => Source_File | undefined,
127
+ filter?: File_Filter,
128
+ results: Set<string> = new Set(),
129
+ searched: Set<string> = new Set(),
130
+ ): Set<string> => {
131
+ const {dependents} = source_file;
132
+ for (const dependent_id of dependents.keys()) {
133
+ if (searched.has(dependent_id)) continue;
134
+ searched.add(dependent_id);
135
+ if (!filter || filter(dependent_id)) {
136
+ results.add(dependent_id);
137
+ }
138
+ const dependent_source_File = get_by_id(dependent_id)!;
139
+ filter_dependents(dependent_source_File, get_by_id, filter, results, searched);
140
+ }
141
+ return results;
142
+ };
@@ -3,11 +3,9 @@ import {print_spawn_result, spawn} from '@ryanatkn/belt/process.js';
3
3
  import type {Plugin} from './plugin.js';
4
4
  import {Task_Error} from './task.js';
5
5
  import {load_package_json} from './package_json.js';
6
- import {serialize_args, to_forwarded_args} from './args.js';
7
- import {find_cli, spawn_cli} from './cli.js';
8
6
  import {
9
7
  SVELTE_PACKAGE_CLI,
10
- has_sveltekit_library,
8
+ run_svelte_package,
11
9
  type Svelte_Package_Options,
12
10
  } from './sveltekit_helpers.js';
13
11
 
@@ -30,24 +28,10 @@ export const gro_plugin_sveltekit_library = ({
30
28
  }: Options = {}): Plugin => {
31
29
  return {
32
30
  name: 'gro_plugin_sveltekit_library',
33
- setup: async ({log}) => {
34
- const has_sveltekit_library_result = has_sveltekit_library();
35
- if (!has_sveltekit_library_result.ok) {
36
- throw new Task_Error(
37
- 'Failed to find SvelteKit library: ' + has_sveltekit_library_result.message,
38
- );
39
- }
40
- const found_svelte_package_cli = find_cli(svelte_package_cli);
41
- if (found_svelte_package_cli?.kind !== 'local') {
42
- throw new Task_Error(
43
- `Failed to find SvelteKit packaging CLI \`${svelte_package_cli}\`, do you need to run \`npm i\`?`,
44
- );
31
+ setup: async ({dev, log}) => {
32
+ if (!dev) {
33
+ await run_svelte_package(svelte_package_options, svelte_package_cli, log);
45
34
  }
46
- const serialized_args = serialize_args({
47
- ...svelte_package_options,
48
- ...to_forwarded_args(svelte_package_cli),
49
- });
50
- await spawn_cli(found_svelte_package_cli, serialized_args, log);
51
35
  },
52
36
  adapt: async ({log, timings}) => {
53
37
  const package_json = load_package_json();
@@ -10,6 +10,7 @@ import {find_tasks, load_tasks, Silent_Error} from './task.js';
10
10
  import {load_gro_package_json} from './package_json.js';
11
11
  import {log_tasks, log_error_reasons} from './task_logging.js';
12
12
  import type {Gro_Config} from './gro_config.js';
13
+ import {Filer} from './filer.js';
13
14
 
14
15
  /**
15
16
  * Invokes Gro tasks by name using the filesystem as the source.
@@ -37,11 +38,14 @@ export const invoke_task = async (
37
38
  task_name: Raw_Input_Path,
38
39
  args: Args | undefined,
39
40
  config: Gro_Config,
40
- initial_timings: Timings | null = null,
41
+ initial_filer?: Filer,
42
+ initial_timings?: Timings | null,
41
43
  ): Promise<void> => {
42
44
  const log = new System_Logger(print_log_label(task_name || 'gro'));
43
45
  log.info('invoking', task_name ? st('cyan', task_name) : 'gro');
44
46
 
47
+ const filer = initial_filer ?? new Filer();
48
+
45
49
  const timings = initial_timings ?? new Timings();
46
50
 
47
51
  const total_timing = create_stopwatch();
@@ -104,6 +108,7 @@ export const invoke_task = async (
104
108
  {...args, ...to_forwarded_args(`gro ${task.name}`)},
105
109
  invoke_task,
106
110
  config,
111
+ filer,
107
112
  timings,
108
113
  );
109
114
  timing_to_run_task();
package/src/lib/loader.ts CHANGED
@@ -22,6 +22,7 @@ import {to_define_import_meta_env, default_ts_transform_options} from './esbuild
22
22
  import {resolve_specifier} from './resolve_specifier.js';
23
23
  import {resolve_node_specifier} from './resolve_node_specifier.js';
24
24
  import type {Package_Json} from './package_json.js';
25
+ import {map_sveltekit_aliases} from './sveltekit_helpers.js';
25
26
 
26
27
  /*
27
28
 
@@ -73,7 +74,7 @@ const ts_transform_options: esbuild.TransformOptions = {
73
74
  sourcemap: 'inline',
74
75
  };
75
76
 
76
- const aliases = Object.entries({$lib: 'src/lib', ...alias});
77
+ const aliases = Object.entries(alias);
77
78
 
78
79
  const RAW_MATCHER = /(%3Fraw|\.css|\.svg)$/; // TODO others? configurable?
79
80
  const ENV_MATCHER = /src\/lib\/\$env\/(static|dynamic)\/(public|private)$/;
@@ -197,15 +198,7 @@ export const resolve: ResolveHook = async (specifier, context, nextResolve) => {
197
198
  return nextResolve(shimmed, context);
198
199
  }
199
200
 
200
- let path = specifier;
201
-
202
- // Map the path with the SvelteKit aliases.
203
- for (const [from, to] of aliases) {
204
- if (path.startsWith(from)) {
205
- path = join(dir, to, path.substring(from.length));
206
- break;
207
- }
208
- }
201
+ const path = map_sveltekit_aliases(specifier, aliases);
209
202
 
210
203
  // The specifier `path` has now been mapped to its final form, so we can inspect it.
211
204
  if (path[0] !== '.' && path[0] !== '/') {
@@ -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.135.2',
8
+ version: '0.135.3',
9
9
  description: 'task runner and toolkit extending SvelteKit',
10
10
  motto: 'generate, run, optimize',
11
11
  glyph: '🌰',
@@ -51,7 +51,7 @@ export const package_json = {
51
51
  'esm-env': '^1.0.0',
52
52
  mri: '^1.2.0',
53
53
  prettier: '^3.3.3',
54
- 'prettier-plugin-svelte': '^3.2.6',
54
+ 'prettier-plugin-svelte': '^3.2.7',
55
55
  'ts-morph': '^23.0.0',
56
56
  tslib: '^2.7.0',
57
57
  zod: '^3.23.8',
@@ -60,22 +60,22 @@ export const package_json = {
60
60
  devDependencies: {
61
61
  '@changesets/changelog-git': '^0.2.0',
62
62
  '@changesets/types': '^6.0.0',
63
- '@ryanatkn/eslint-config': '^0.5.3',
64
- '@ryanatkn/fuz': '^0.127.0',
63
+ '@ryanatkn/eslint-config': '^0.5.4',
64
+ '@ryanatkn/fuz': '^0.129.0',
65
65
  '@ryanatkn/moss': '^0.16.1',
66
66
  '@sveltejs/adapter-static': '^3.0.5',
67
- '@sveltejs/kit': '^2.5.28',
67
+ '@sveltejs/kit': '^2.6.1',
68
68
  '@sveltejs/package': '^2.3.5',
69
69
  '@sveltejs/vite-plugin-svelte': '^3.1.2',
70
70
  '@types/fs-extra': '^11.0.4',
71
- '@types/node': '^22.5.5',
71
+ '@types/node': '^22.7.4',
72
72
  esbuild: '^0.21.5',
73
- eslint: '^9.11.0',
74
- 'eslint-plugin-svelte': '^2.44.0',
75
- svelte: '^5.0.0-next.257',
76
- 'svelte-check': '^4.0.2',
73
+ eslint: '^9.11.1',
74
+ 'eslint-plugin-svelte': '^2.44.1',
75
+ svelte: '^5.0.0-next.260',
76
+ 'svelte-check': '^4.0.4',
77
77
  typescript: '^5.6.2',
78
- 'typescript-eslint': '^8.6.0',
78
+ 'typescript-eslint': '^8.7.0',
79
79
  uvu: '^0.5.6',
80
80
  },
81
81
  prettier: {
@@ -138,6 +138,7 @@ export const package_json = {
138
138
  types: './dist/esbuild_plugin_sveltekit_shim_env.d.ts',
139
139
  default: './dist/esbuild_plugin_sveltekit_shim_env.js',
140
140
  },
141
+ './filer.js': {types: './dist/filer.d.ts', default: './dist/filer.js'},
141
142
  './format_directory.js': {
142
143
  types: './dist/format_directory.d.ts',
143
144
  default: './dist/format_directory.js',
@@ -184,6 +185,7 @@ export const package_json = {
184
185
  './package_meta.js': {types: './dist/package_meta.d.ts', default: './dist/package_meta.js'},
185
186
  './package.gen.js': {types: './dist/package.gen.d.ts', default: './dist/package.gen.js'},
186
187
  './package.js': {types: './dist/package.d.ts', default: './dist/package.js'},
188
+ './parse_imports.js': {types: './dist/parse_imports.d.ts', default: './dist/parse_imports.js'},
187
189
  './path_constants.js': {
188
190
  types: './dist/path_constants.d.ts',
189
191
  default: './dist/path_constants.js',
@@ -268,7 +270,7 @@ export const package_json = {
268
270
 
269
271
  export const src_json = {
270
272
  name: '@ryanatkn/gro',
271
- version: '0.135.2',
273
+ version: '0.135.3',
272
274
  modules: {
273
275
  '.': {
274
276
  path: 'index.ts',
@@ -435,6 +437,16 @@ export const src_json = {
435
437
  {name: 'esbuild_plugin_sveltekit_shim_env', kind: 'function'},
436
438
  ],
437
439
  },
440
+ './filer.js': {
441
+ path: 'filer.ts',
442
+ declarations: [
443
+ {name: 'Source_File', kind: 'type'},
444
+ {name: 'Cleanup_Watch', kind: 'type'},
445
+ {name: 'On_Filer_Change', kind: 'type'},
446
+ {name: 'Options', kind: 'type'},
447
+ {name: 'Filer', kind: 'class'},
448
+ ],
449
+ },
438
450
  './format_directory.js': {
439
451
  path: 'format_directory.ts',
440
452
  declarations: [{name: 'format_directory', kind: 'function'}],
@@ -552,7 +564,9 @@ export const src_json = {
552
564
  path: 'gro_plugin_gen.ts',
553
565
  declarations: [
554
566
  {name: 'Task_Args', kind: 'type'},
555
- {name: 'plugin', kind: 'function'},
567
+ {name: 'Options', kind: 'type'},
568
+ {name: 'gro_plugin_gen', kind: 'function'},
569
+ {name: 'filter_dependents', kind: 'function'},
556
570
  ],
557
571
  },
558
572
  './gro_plugin_server.js': {
@@ -684,6 +698,14 @@ export const src_json = {
684
698
  {name: 'src_json', kind: 'variable'},
685
699
  ],
686
700
  },
701
+ './parse_imports.js': {
702
+ path: 'parse_imports.ts',
703
+ declarations: [
704
+ {name: 'init_lexer', kind: 'function'},
705
+ {name: 'Import_Specifier', kind: 'type'},
706
+ {name: 'parse_imports', kind: 'function'},
707
+ ],
708
+ },
687
709
  './path_constants.js': {
688
710
  path: 'path_constants.ts',
689
711
  declarations: [
@@ -709,6 +731,7 @@ export const src_json = {
709
731
  {name: 'GIT_DIRNAME', kind: 'variable'},
710
732
  {name: 'TSCONFIG_FILENAME', kind: 'variable'},
711
733
  {name: 'TS_MATCHER', kind: 'variable'},
734
+ {name: 'JS_MATCHER', kind: 'variable'},
712
735
  {name: 'JSON_MATCHER', kind: 'variable'},
713
736
  {name: 'EVERYTHING_MATCHER', kind: 'variable'},
714
737
  ],
@@ -745,6 +768,7 @@ export const src_json = {
745
768
  {name: 'IS_THIS_GRO', kind: 'variable'},
746
769
  {name: 'gro_paths', kind: 'variable'},
747
770
  {name: 'GRO_DIST_DIR', kind: 'variable'},
771
+ {name: 'default_file_filter', kind: 'function'},
748
772
  ],
749
773
  },
750
774
  './plugin.js': {
@@ -870,6 +894,8 @@ export const src_json = {
870
894
  {name: 'sveltekit_sync_if_available', kind: 'function'},
871
895
  {name: 'sveltekit_sync_if_obviously_needed', kind: 'function'},
872
896
  {name: 'Svelte_Package_Options', kind: 'type'},
897
+ {name: 'run_svelte_package', kind: 'function'},
898
+ {name: 'map_sveltekit_aliases', kind: 'function'},
873
899
  ],
874
900
  },
875
901
  './sveltekit_shim_app_environment.js': {