@ryanatkn/gro 0.135.2 → 0.136.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 (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
@@ -22,6 +22,7 @@ export declare const Args: z.ZodObject<{
22
22
  sync: boolean;
23
23
  package_json: boolean;
24
24
  format: boolean;
25
+ gen: boolean;
25
26
  'no-sync': boolean;
26
27
  install: boolean;
27
28
  'no-install': boolean;
@@ -29,7 +30,6 @@ export declare const Args: z.ZodObject<{
29
30
  'no-typecheck': boolean;
30
31
  test: boolean;
31
32
  'no-test': boolean;
32
- gen: boolean;
33
33
  'no-gen': boolean;
34
34
  'no-format': boolean;
35
35
  'no-package_json': boolean;
@@ -40,6 +40,7 @@ export declare const Args: z.ZodObject<{
40
40
  sync?: boolean | undefined;
41
41
  package_json?: boolean | undefined;
42
42
  format?: boolean | undefined;
43
+ gen?: boolean | undefined;
43
44
  'no-sync'?: boolean | undefined;
44
45
  install?: boolean | undefined;
45
46
  'no-install'?: boolean | undefined;
@@ -47,7 +48,6 @@ export declare const Args: z.ZodObject<{
47
48
  'no-typecheck'?: boolean | undefined;
48
49
  test?: boolean | undefined;
49
50
  'no-test'?: boolean | undefined;
50
- gen?: boolean | undefined;
51
51
  'no-gen'?: boolean | undefined;
52
52
  'no-format'?: boolean | undefined;
53
53
  'no-package_json'?: boolean | undefined;
@@ -0,0 +1,29 @@
1
+ import type { Omit_Strict } from '@ryanatkn/belt/types.js';
2
+ import type { Path_Id } from './path.js';
3
+ import { watch_dir, type Watcher_Change, type Options as Watch_Dir_Options } from './watch_dir.js';
4
+ export interface Source_File {
5
+ id: Path_Id;
6
+ /**
7
+ * `null` contents means it doesn't exist.
8
+ * We create the file in memory to track its dependents regardless of its existence on disk.
9
+ */
10
+ contents: string | null;
11
+ dependents: Map<Path_Id, Source_File>;
12
+ dependencies: Map<Path_Id, Source_File>;
13
+ }
14
+ export type Cleanup_Watch = () => Promise<void>;
15
+ export type On_Filer_Change = (change: Watcher_Change, source_file: Source_File) => void;
16
+ export interface Options {
17
+ watch_dir?: typeof watch_dir;
18
+ watch_dir_options?: Partial<Omit_Strict<Watch_Dir_Options, 'on_change'>>;
19
+ }
20
+ export declare class Filer {
21
+ #private;
22
+ files: Map<Path_Id, Source_File>;
23
+ constructor(options?: Options);
24
+ get_by_id: (id: Path_Id) => Source_File | undefined;
25
+ get_or_create: (id: Path_Id) => Source_File;
26
+ watch(listener: On_Filer_Change): Promise<Cleanup_Watch>;
27
+ close(): Promise<void>;
28
+ }
29
+ //# sourceMappingURL=filer.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"filer.d.ts","sourceRoot":"../src/lib/","sources":["filer.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAC,WAAW,EAAC,MAAM,yBAAyB,CAAC;AAGzD,OAAO,KAAK,EAAC,OAAO,EAAC,MAAM,WAAW,CAAC;AACvC,OAAO,EACN,SAAS,EAET,KAAK,cAAc,EACnB,KAAK,OAAO,IAAI,iBAAiB,EAEjC,MAAM,gBAAgB,CAAC;AAUxB,MAAM,WAAW,WAAW;IAC3B,EAAE,EAAE,OAAO,CAAC;IACZ;;;OAGG;IACH,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,UAAU,EAAE,GAAG,CAAC,OAAO,EAAE,WAAW,CAAC,CAAC;IACtC,YAAY,EAAE,GAAG,CAAC,OAAO,EAAE,WAAW,CAAC,CAAC;CACxC;AAED,MAAM,MAAM,aAAa,GAAG,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;AAEhD,MAAM,MAAM,eAAe,GAAG,CAAC,MAAM,EAAE,cAAc,EAAE,WAAW,EAAE,WAAW,KAAK,IAAI,CAAC;AAEzF,MAAM,WAAW,OAAO;IACvB,SAAS,CAAC,EAAE,OAAO,SAAS,CAAC;IAC7B,iBAAiB,CAAC,EAAE,OAAO,CAAC,WAAW,CAAC,iBAAiB,EAAE,WAAW,CAAC,CAAC,CAAC;CACzE;AAED,qBAAa,KAAK;;IACjB,KAAK,EAAE,GAAG,CAAC,OAAO,EAAE,WAAW,CAAC,CAAa;gBAKjC,OAAO,GAAE,OAAsB;IAU3C,SAAS,OAAQ,OAAO,KAAG,WAAW,GAAG,SAAS,CAEhD;IAEF,aAAa,OAAQ,OAAO,KAAG,WAAW,CAWxC;IA4HI,KAAK,CAAC,QAAQ,EAAE,eAAe,GAAG,OAAO,CAAC,aAAa,CAAC;IAKxD,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;CAQ5B"}
package/dist/filer.js ADDED
@@ -0,0 +1,165 @@
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 { wait } from '@ryanatkn/belt/async.js';
5
+ import { watch_dir, } from './watch_dir.js';
6
+ import { default_file_filter, paths } from './paths.js';
7
+ import { parse_imports } from './parse_imports.js';
8
+ import { resolve_specifier } from './resolve_specifier.js';
9
+ import { default_sveltekit_config } from './sveltekit_config.js';
10
+ import { map_sveltekit_aliases } from './sveltekit_helpers.js';
11
+ import { Unreachable_Error } from '@ryanatkn/belt/error.js';
12
+ const aliases = Object.entries(default_sveltekit_config.alias);
13
+ export class Filer {
14
+ files = new Map();
15
+ #watch_dir;
16
+ #watch_dir_options;
17
+ constructor(options = EMPTY_OBJECT) {
18
+ this.#watch_dir = options.watch_dir ?? watch_dir;
19
+ this.#watch_dir_options = options.watch_dir_options ?? EMPTY_OBJECT;
20
+ }
21
+ #watching;
22
+ #listeners = new Set();
23
+ #ready = false;
24
+ get_by_id = (id) => {
25
+ return this.files.get(id);
26
+ };
27
+ get_or_create = (id) => {
28
+ const existing = this.get_by_id(id);
29
+ if (existing)
30
+ return existing;
31
+ const file = {
32
+ id,
33
+ contents: null,
34
+ dependents: new Map(),
35
+ dependencies: new Map(),
36
+ };
37
+ this.files.set(id, file);
38
+ return file;
39
+ };
40
+ #update(id) {
41
+ const file = this.get_or_create(id);
42
+ const new_contents = existsSync(id) ? readFileSync(id, 'utf8') : null;
43
+ const contents_changed = file.contents !== new_contents;
44
+ file.contents = new_contents;
45
+ if (contents_changed) {
46
+ const dir = dirname(file.id);
47
+ const dependencies_before = new Set(file.dependencies.keys());
48
+ const dependencies_removed = new Set(dependencies_before);
49
+ const imported = file.contents ? parse_imports(file.id, file.contents) : [];
50
+ for (const specifier of imported) {
51
+ // TODO logic is duplicated from loader
52
+ const path = map_sveltekit_aliases(specifier, aliases);
53
+ // The specifier `path` has now been mapped to its final form, so we can inspect it.
54
+ if (path[0] === '.' || path[0] === '/') {
55
+ const { path_id } = resolve_specifier(path, dir);
56
+ dependencies_removed.delete(path_id);
57
+ if (!dependencies_before.has(path_id)) {
58
+ const d = this.get_or_create(path_id);
59
+ file.dependencies.set(d.id, d);
60
+ d.dependents.set(file.id, file);
61
+ }
62
+ }
63
+ }
64
+ // update any removed dependencies
65
+ for (const dependency_removed of dependencies_removed) {
66
+ const deleted1 = file.dependencies.delete(dependency_removed);
67
+ if (!deleted1)
68
+ throw Error('expected to delete1 ' + file.id); // TODO @many delete if correct
69
+ const dependency_removed_file = this.get_or_create(dependency_removed);
70
+ const deleted2 = dependency_removed_file.dependents.delete(file.id);
71
+ if (!deleted2)
72
+ throw Error('expected to delete2 ' + file.id); // TODO @many delete if correct
73
+ }
74
+ }
75
+ return file;
76
+ }
77
+ #remove(id) {
78
+ const file = this.get_by_id(id);
79
+ if (!file)
80
+ return; // this is safe because the object would exist if any other file referenced it as a dependency or dependent
81
+ file.contents = null; // clear contents in case it gets re-added later, we want the change to be detected
82
+ let found = false;
83
+ for (const d of this.files.values()) {
84
+ if (d.dependencies.has(file.id)) {
85
+ found = true;
86
+ break;
87
+ }
88
+ }
89
+ if (!found)
90
+ this.files.delete(id);
91
+ return file;
92
+ }
93
+ #notify_listener(listener) {
94
+ if (!this.#ready)
95
+ return;
96
+ for (const source_file of this.files.values()) {
97
+ listener({ type: 'add', path: source_file.id, is_directory: false }, source_file);
98
+ }
99
+ }
100
+ #notify_change(change, source_file) {
101
+ if (!this.#ready)
102
+ return;
103
+ for (const listener of this.#listeners) {
104
+ listener(change, source_file);
105
+ }
106
+ }
107
+ async #add_listener(listener) {
108
+ this.#listeners.add(listener);
109
+ if (this.#watching) {
110
+ // if already watching, call the listener for all existing files after init
111
+ await this.#watching.init();
112
+ await wait(); // wait a tick to ensure the `this.#ready` value is updated below first
113
+ this.#notify_listener(listener);
114
+ return;
115
+ }
116
+ this.#watching = this.#watch_dir({
117
+ filter: (path, is_directory) => (is_directory ? true : default_file_filter(path)),
118
+ ...this.#watch_dir_options,
119
+ dir: resolve(this.#watch_dir_options.dir ?? paths.source),
120
+ on_change: this.#on_change,
121
+ });
122
+ await this.#watching.init();
123
+ this.#ready = true;
124
+ this.#notify_listener(listener);
125
+ }
126
+ async #remove_listener(listener) {
127
+ this.#listeners.delete(listener);
128
+ if (this.#listeners.size === 0) {
129
+ await this.close(); // TODO is this right? should `watch` be async?
130
+ }
131
+ }
132
+ #on_change = (change) => {
133
+ if (change.is_directory)
134
+ return;
135
+ let source_file;
136
+ switch (change.type) {
137
+ case 'add':
138
+ case 'update': {
139
+ source_file = this.#update(change.path);
140
+ break;
141
+ }
142
+ case 'delete': {
143
+ source_file = this.#remove(change.path);
144
+ break;
145
+ }
146
+ default:
147
+ throw new Unreachable_Error(change.type);
148
+ }
149
+ if (source_file) {
150
+ this.#notify_change(change, source_file);
151
+ }
152
+ };
153
+ async watch(listener) {
154
+ await this.#add_listener(listener);
155
+ return () => this.#remove_listener(listener);
156
+ }
157
+ async close() {
158
+ this.#ready = false;
159
+ this.#listeners.clear();
160
+ if (this.#watching) {
161
+ await this.#watching.close();
162
+ this.#watching = undefined;
163
+ }
164
+ }
165
+ }
@@ -6,12 +6,12 @@ export declare const Args: z.ZodObject<{
6
6
  check: z.ZodDefault<z.ZodBoolean>;
7
7
  }, "strict", z.ZodTypeAny, {
8
8
  _: string[];
9
- check: boolean;
10
9
  root_dirs: string[];
10
+ check: boolean;
11
11
  }, {
12
12
  _?: string[] | undefined;
13
- check?: boolean | undefined;
14
13
  root_dirs?: string[] | undefined;
14
+ check?: boolean | undefined;
15
15
  }>;
16
16
  export type Args = z.infer<typeof Args>;
17
17
  export declare const task: Task<Args>;
@@ -1 +1 @@
1
- {"version":3,"file":"gro.config.default.d.ts","sourceRoot":"../src/lib/","sources":["gro.config.default.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAC,iBAAiB,EAAC,MAAM,iBAAiB,CAAC;AAMvD;;;;;;;;GAQG;AACH,QAAA,MAAM,MAAM,EAAE,iBAgBb,CAAC;AAEF,eAAe,MAAM,CAAC"}
1
+ {"version":3,"file":"gro.config.default.d.ts","sourceRoot":"../src/lib/","sources":["gro.config.default.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAC,iBAAiB,EAAC,MAAM,iBAAiB,CAAC;AAOvD;;;;;;;;GAQG;AACH,QAAA,MAAM,MAAM,EAAE,iBAcb,CAAC;AAEF,eAAe,MAAM,CAAC"}
@@ -2,6 +2,7 @@ import { gro_plugin_sveltekit_library } from './gro_plugin_sveltekit_library.js'
2
2
  import { has_server, gro_plugin_server } from './gro_plugin_server.js';
3
3
  import { gro_plugin_sveltekit_app } from './gro_plugin_sveltekit_app.js';
4
4
  import { has_sveltekit_app, has_sveltekit_library } from './sveltekit_helpers.js';
5
+ import { gro_plugin_gen } from './gro_plugin_gen.js';
5
6
  /**
6
7
  * This is the default config that's passed to `gro.config.ts`
7
8
  * if it exists in the current project, and if not, this is the final config.
@@ -19,9 +20,7 @@ const config = async (cfg) => {
19
20
  has_sveltekit_app_result.ok
20
21
  ? gro_plugin_sveltekit_app({ host_target: has_server_result.ok ? 'node' : 'github_pages' })
21
22
  : null,
22
- // TODO replace with an esbuild plugin, see the module for more
23
- // import {gro_plugin_gen} from './gro_plugin_gen.js';
24
- // gro_plugin_gen(),
23
+ gro_plugin_gen(),
25
24
  ];
26
25
  return cfg;
27
26
  };
@@ -1,7 +1,14 @@
1
- import type { Plugin, Plugin_Context } from './plugin.js';
1
+ import type { Plugin } from './plugin.js';
2
2
  import type { Args } from './args.js';
3
+ import type { File_Filter, Path_Id } from './path.js';
4
+ import type { Source_File } from './filer.js';
3
5
  export interface Task_Args extends Args {
4
6
  watch?: boolean;
5
7
  }
6
- export declare const plugin: () => Plugin<Plugin_Context<Task_Args>>;
8
+ export interface Options {
9
+ root_dirs?: string[];
10
+ flush_debounce_delay?: number;
11
+ }
12
+ export declare const gro_plugin_gen: ({ root_dirs, flush_debounce_delay, }?: Options) => Plugin;
13
+ export declare const filter_dependents: (source_file: Source_File, get_by_id: (id: Path_Id) => Source_File | undefined, filter?: File_Filter, results?: Set<string>, searched?: Set<string>) => Set<string>;
7
14
  //# sourceMappingURL=gro_plugin_gen.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"gro_plugin_gen.d.ts","sourceRoot":"../src/lib/","sources":["gro_plugin_gen.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAC,MAAM,EAAE,cAAc,EAAC,MAAM,aAAa,CAAC;AACxD,OAAO,KAAK,EAAC,IAAI,EAAC,MAAM,WAAW,CAAC;AAQpC,MAAM,WAAW,SAAU,SAAQ,IAAI;IACtC,KAAK,CAAC,EAAE,OAAO,CAAC;CAChB;AAED,eAAO,MAAM,MAAM,QAAO,MAAM,CAAC,cAAc,CAAC,SAAS,CAAC,CA6EzD,CAAC"}
1
+ {"version":3,"file":"gro_plugin_gen.d.ts","sourceRoot":"../src/lib/","sources":["gro_plugin_gen.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAC,MAAM,EAAC,MAAM,aAAa,CAAC;AACxC,OAAO,KAAK,EAAC,IAAI,EAAC,MAAM,WAAW,CAAC;AAKpC,OAAO,KAAK,EAAC,WAAW,EAAE,OAAO,EAAC,MAAM,WAAW,CAAC;AACpD,OAAO,KAAK,EAAgB,WAAW,EAAC,MAAM,YAAY,CAAC;AAK3D,MAAM,WAAW,SAAU,SAAQ,IAAI;IACtC,KAAK,CAAC,EAAE,OAAO,CAAC;CAChB;AAED,MAAM,WAAW,OAAO;IACvB,SAAS,CAAC,EAAE,MAAM,EAAE,CAAC;IACrB,oBAAoB,CAAC,EAAE,MAAM,CAAC;CAC9B;AAED,eAAO,MAAM,cAAc,0CAGxB,OAAO,KAAkB,MA+F3B,CAAC;AAEF,eAAO,MAAM,iBAAiB,gBAChB,WAAW,aACb,CAAC,EAAE,EAAE,OAAO,KAAK,WAAW,GAAG,SAAS,WAC1C,WAAW,YACX,GAAG,CAAC,MAAM,CAAC,aACV,GAAG,CAAC,MAAM,CAAC,KACnB,GAAG,CAAC,MAAM,CAYZ,CAAC"}
@@ -1,19 +1,24 @@
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
4
- import { path_id_to_base_path } from './paths.js';
1
+ import { EMPTY_OBJECT } from '@ryanatkn/belt/object.js';
2
+ import { paths } from './paths.js';
5
3
  import { find_genfiles, is_gen_path } from './gen.js';
6
- import { filter_dependents } from './build/source_file.js';
7
4
  import { throttle } from './throttle.js';
5
+ import { spawn_cli } from './cli.js';
6
+ import { Unreachable_Error } from '@ryanatkn/belt/error.js';
8
7
  const FLUSH_DEBOUNCE_DELAY = 500;
9
- export const plugin = () => {
8
+ export const gro_plugin_gen = ({ root_dirs = [paths.source], flush_debounce_delay = FLUSH_DEBOUNCE_DELAY, } = EMPTY_OBJECT) => {
9
+ const input_path = paths.source; // TODO option?
10
10
  let generating = false;
11
11
  let regen = false;
12
- let on_filer_build;
12
+ let flushing_timeout;
13
13
  const queued_files = new Set();
14
- const queue_gen = (gen_file_name) => {
15
- queued_files.add(gen_file_name);
16
- void flush_gen_queue();
14
+ const queue_gen = (gen_file_id) => {
15
+ queued_files.add(gen_file_id);
16
+ if (flushing_timeout === undefined) {
17
+ flushing_timeout = setTimeout(() => {
18
+ flushing_timeout = undefined;
19
+ void flush_gen_queue();
20
+ }); // the timeout batches synchronously
21
+ }
17
22
  };
18
23
  const flush_gen_queue = throttle(async () => {
19
24
  // hacky way to avoid concurrent `gro gen` calls
@@ -30,50 +35,75 @@ export const plugin = () => {
30
35
  regen = false;
31
36
  void flush_gen_queue();
32
37
  }
33
- }, FLUSH_DEBOUNCE_DELAY);
38
+ }, flush_debounce_delay);
34
39
  const gen = (files = []) => spawn_cli('gro', ['gen', ...files]);
40
+ let cleanup;
35
41
  return {
36
42
  name: 'gro_plugin_gen',
37
- setup: async ({ args: { watch }, dev, log, config }) => {
43
+ setup: async ({ watch, dev, log, config, filer }) => {
38
44
  // For production builds, we assume `gen` is already fresh,
39
45
  // which should be checked by CI via `gro check` which calls `gro gen --check`.
40
46
  if (!dev)
41
47
  return;
42
- // Run `gen`, first checking if there are any modules to avoid a console error.
43
- // Some parts of the build may have already happened,
44
- // making us miss `build` events for gen dependencies,
45
- // so we run `gen` here even if it's usually wasteful.
46
- const found = find_genfiles([paths.source], root_dirs, config);
47
- if (found.ok && found.value.resolved_input_files.size > 0) {
48
- await gen();
49
- }
50
48
  // Do we need to just generate everything once and exit?
51
49
  // TODO could we have an esbuild context here? problem is watching the right files, maybe a plugin that tracks deps
52
- if (!filer || !watch) {
50
+ if (!watch) {
53
51
  log.info('generating and exiting early');
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([input_path], root_dirs, config);
57
+ if (found.ok && found.value.resolved_input_files.length > 0) {
58
+ await gen();
59
+ }
54
60
  return;
55
61
  }
56
62
  // When a file builds, check it and its tree of dependents
57
63
  // for any `.gen.` files that need to run.
58
- on_filer_build = ({ source_file, build_config }) => {
59
- // TODO how to handle this now? the loader traces deps for us with `parentPath`,
60
- // but we probably want to make this an esbuild plugin instead
61
- // if (build_config.name !== 'system') return;
62
- if (is_gen_path(source_file.id)) {
63
- queue_gen(path_id_to_base_path(source_file.id));
64
+ cleanup = await filer.watch((change, source_file) => {
65
+ switch (change.type) {
66
+ case 'add':
67
+ case 'update': {
68
+ // TODO how to handle this now? the loader traces deps for us with `parentPath`,
69
+ // but we probably want to make this an esbuild plugin instead
70
+ if (is_gen_path(source_file.id)) {
71
+ queue_gen(source_file.id);
72
+ }
73
+ const dependent_gen_file_ids = filter_dependents(source_file, filer.get_by_id, is_gen_path);
74
+ for (const dependent_gen_file_id of dependent_gen_file_ids) {
75
+ queue_gen(dependent_gen_file_id);
76
+ }
77
+ break;
78
+ }
79
+ case 'delete': {
80
+ // TODO delete the generated file(s)? option?
81
+ break;
82
+ }
83
+ default:
84
+ throw new Unreachable_Error(change.type);
64
85
  }
65
- const dependent_gen_file_ids = filter_dependents(source_file, build_config, filer.find_by_id, // cast because we can assume they're all `SourceFile`s
66
- is_gen_path);
67
- for (const dependent_gen_file_id of dependent_gen_file_ids) {
68
- queue_gen(path_id_to_base_path(dependent_gen_file_id));
69
- }
70
- };
71
- filer.on('build', on_filer_build);
86
+ });
72
87
  },
73
- teardown: ({ filer }) => {
74
- if (on_filer_build && filer) {
75
- filer.off('build', on_filer_build);
88
+ teardown: async () => {
89
+ if (cleanup !== undefined) {
90
+ await cleanup();
91
+ cleanup = undefined;
76
92
  }
77
93
  },
78
94
  };
79
95
  };
96
+ export const filter_dependents = (source_file, get_by_id, filter, results = new Set(), searched = new Set()) => {
97
+ const { dependents } = source_file;
98
+ for (const dependent_id of dependents.keys()) {
99
+ if (searched.has(dependent_id))
100
+ continue;
101
+ searched.add(dependent_id);
102
+ if (!filter || filter(dependent_id)) {
103
+ results.add(dependent_id);
104
+ }
105
+ const dependent_source_File = get_by_id(dependent_id);
106
+ filter_dependents(dependent_source_File, get_by_id, filter, results, searched);
107
+ }
108
+ return results;
109
+ };
@@ -1 +1 @@
1
- {"version":3,"file":"gro_plugin_sveltekit_library.d.ts","sourceRoot":"../src/lib/","sources":["gro_plugin_sveltekit_library.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAC,MAAM,EAAC,MAAM,aAAa,CAAC;AAKxC,OAAO,EAGN,KAAK,sBAAsB,EAC3B,MAAM,wBAAwB,CAAC;AAEhC,MAAM,WAAW,OAAO;IACvB;;;OAGG;IACH,sBAAsB,CAAC,EAAE,sBAAsB,CAAC;IAChD;;;OAGG;IACH,kBAAkB,CAAC,EAAE,MAAM,CAAC;CAC5B;AAED,eAAO,MAAM,4BAA4B,qDAGtC,OAAO,KAAQ,MA4CjB,CAAC"}
1
+ {"version":3,"file":"gro_plugin_sveltekit_library.d.ts","sourceRoot":"../src/lib/","sources":["gro_plugin_sveltekit_library.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAC,MAAM,EAAC,MAAM,aAAa,CAAC;AAGxC,OAAO,EAGN,KAAK,sBAAsB,EAC3B,MAAM,wBAAwB,CAAC;AAEhC,MAAM,WAAW,OAAO;IACvB;;;OAGG;IACH,sBAAsB,CAAC,EAAE,sBAAsB,CAAC;IAChD;;;OAGG;IACH,kBAAkB,CAAC,EAAE,MAAM,CAAC;CAC5B;AAED,eAAO,MAAM,4BAA4B,qDAGtC,OAAO,KAAQ,MA8BjB,CAAC"}
@@ -1,26 +1,14 @@
1
1
  import { print_spawn_result, spawn } from '@ryanatkn/belt/process.js';
2
2
  import { Task_Error } from './task.js';
3
3
  import { load_package_json } from './package_json.js';
4
- import { serialize_args, to_forwarded_args } from './args.js';
5
- import { find_cli, spawn_cli } from './cli.js';
6
- import { SVELTE_PACKAGE_CLI, has_sveltekit_library, } from './sveltekit_helpers.js';
4
+ import { SVELTE_PACKAGE_CLI, run_svelte_package, } from './sveltekit_helpers.js';
7
5
  export const gro_plugin_sveltekit_library = ({ svelte_package_options, svelte_package_cli = SVELTE_PACKAGE_CLI, } = {}) => {
8
6
  return {
9
7
  name: 'gro_plugin_sveltekit_library',
10
- setup: async ({ log }) => {
11
- const has_sveltekit_library_result = has_sveltekit_library();
12
- if (!has_sveltekit_library_result.ok) {
13
- throw new Task_Error('Failed to find SvelteKit library: ' + has_sveltekit_library_result.message);
8
+ setup: async ({ dev, log }) => {
9
+ if (!dev) {
10
+ await run_svelte_package(svelte_package_options, svelte_package_cli, log);
14
11
  }
15
- const found_svelte_package_cli = find_cli(svelte_package_cli);
16
- if (found_svelte_package_cli?.kind !== 'local') {
17
- throw new Task_Error(`Failed to find SvelteKit packaging CLI \`${svelte_package_cli}\`, do you need to run \`npm i\`?`);
18
- }
19
- const serialized_args = serialize_args({
20
- ...svelte_package_options,
21
- ...to_forwarded_args(svelte_package_cli),
22
- });
23
- await spawn_cli(found_svelte_package_cli, serialized_args, log);
24
12
  },
25
13
  adapt: async ({ log, timings }) => {
26
14
  const package_json = load_package_json();
@@ -2,6 +2,7 @@ import { Timings } from '@ryanatkn/belt/timings.js';
2
2
  import { type Args } from './args.js';
3
3
  import { Raw_Input_Path } from './input_path.js';
4
4
  import type { Gro_Config } from './gro_config.js';
5
+ import { Filer } from './filer.js';
5
6
  /**
6
7
  * Invokes Gro tasks by name using the filesystem as the source.
7
8
  *
@@ -24,5 +25,5 @@ import type { Gro_Config } from './gro_config.js';
24
25
  * @param config - The Gro configuration.
25
26
  * @param initial_timings - The timings to use for the top-level task, `null` for composed tasks.
26
27
  */
27
- export declare const invoke_task: (task_name: Raw_Input_Path, args: Args | undefined, config: Gro_Config, initial_timings?: Timings | null) => Promise<void>;
28
+ export declare const invoke_task: (task_name: Raw_Input_Path, args: Args | undefined, config: Gro_Config, initial_filer?: Filer, initial_timings?: Timings | null) => Promise<void>;
28
29
  //# sourceMappingURL=invoke_task.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"invoke_task.d.ts","sourceRoot":"../src/lib/","sources":["invoke_task.ts"],"names":[],"mappings":"AAEA,OAAO,EAAmB,OAAO,EAAC,MAAM,2BAA2B,CAAC;AAGpE,OAAO,EAAoB,KAAK,IAAI,EAAC,MAAM,WAAW,CAAC;AAEvD,OAAO,EAAgB,cAAc,EAAC,MAAM,iBAAiB,CAAC;AAI9D,OAAO,KAAK,EAAC,UAAU,EAAC,MAAM,iBAAiB,CAAC;AAEhD;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,eAAO,MAAM,WAAW,cACZ,cAAc,QACnB,IAAI,GAAG,SAAS,UACd,UAAU,oBACD,OAAO,GAAG,IAAI,KAC7B,OAAO,CAAC,IAAI,CA6Ed,CAAC"}
1
+ {"version":3,"file":"invoke_task.d.ts","sourceRoot":"../src/lib/","sources":["invoke_task.ts"],"names":[],"mappings":"AAEA,OAAO,EAAmB,OAAO,EAAC,MAAM,2BAA2B,CAAC;AAGpE,OAAO,EAAoB,KAAK,IAAI,EAAC,MAAM,WAAW,CAAC;AAEvD,OAAO,EAAgB,cAAc,EAAC,MAAM,iBAAiB,CAAC;AAI9D,OAAO,KAAK,EAAC,UAAU,EAAC,MAAM,iBAAiB,CAAC;AAChD,OAAO,EAAC,KAAK,EAAC,MAAM,YAAY,CAAC;AAEjC;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,eAAO,MAAM,WAAW,cACZ,cAAc,QACnB,IAAI,GAAG,SAAS,UACd,UAAU,kBACF,KAAK,oBACH,OAAO,GAAG,IAAI,KAC9B,OAAO,CAAC,IAAI,CAgFd,CAAC"}
@@ -8,6 +8,7 @@ import { to_input_path, Raw_Input_Path } from './input_path.js';
8
8
  import { find_tasks, load_tasks, Silent_Error } from './task.js';
9
9
  import { load_gro_package_json } from './package_json.js';
10
10
  import { log_tasks, log_error_reasons } from './task_logging.js';
11
+ import { Filer } from './filer.js';
11
12
  /**
12
13
  * Invokes Gro tasks by name using the filesystem as the source.
13
14
  *
@@ -30,9 +31,10 @@ import { log_tasks, log_error_reasons } from './task_logging.js';
30
31
  * @param config - The Gro configuration.
31
32
  * @param initial_timings - The timings to use for the top-level task, `null` for composed tasks.
32
33
  */
33
- export const invoke_task = async (task_name, args, config, initial_timings = null) => {
34
+ export const invoke_task = async (task_name, args, config, initial_filer, initial_timings) => {
34
35
  const log = new System_Logger(print_log_label(task_name || 'gro'));
35
36
  log.info('invoking', task_name ? st('cyan', task_name) : 'gro');
37
+ const filer = initial_filer ?? new Filer();
36
38
  const timings = initial_timings ?? new Timings();
37
39
  const total_timing = create_stopwatch();
38
40
  const finish = () => {
@@ -80,7 +82,7 @@ export const invoke_task = async (task_name, args, config, initial_timings = nul
80
82
  const task = loaded_tasks.modules[0];
81
83
  log.info(`→ ${st('cyan', task.name)} ${(task.mod.task.summary && st('gray', task.mod.task.summary)) ?? ''}`);
82
84
  const timing_to_run_task = timings.start('run task ' + task_name);
83
- const result = await run_task(task, { ...args, ...to_forwarded_args(`gro ${task.name}`) }, invoke_task, config, timings);
85
+ const result = await run_task(task, { ...args, ...to_forwarded_args(`gro ${task.name}`) }, invoke_task, config, filer, timings);
84
86
  timing_to_run_task();
85
87
  if (!result.ok) {
86
88
  log.info(`${st('red', '🞩')} ${st('cyan', task.name)}`);
@@ -1 +1 @@
1
- {"version":3,"file":"loader.d.ts","sourceRoot":"../src/lib/","sources":["loader.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAC,QAAQ,EAAE,WAAW,EAAC,MAAM,aAAa,CAAC;AA+EvD,eAAO,MAAM,IAAI,EAAE,QAwFlB,CAAC;AAEF,eAAO,MAAM,OAAO,EAAE,WA4DrB,CAAC"}
1
+ {"version":3,"file":"loader.d.ts","sourceRoot":"../src/lib/","sources":["loader.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAC,QAAQ,EAAE,WAAW,EAAC,MAAM,aAAa,CAAC;AAgFvD,eAAO,MAAM,IAAI,EAAE,QAwFlB,CAAC;AAEF,eAAO,MAAM,OAAO,EAAE,WAoDrB,CAAC"}
package/dist/loader.js CHANGED
@@ -13,6 +13,7 @@ import { JSON_MATCHER, NODE_MODULES_DIRNAME, TS_MATCHER } from './path_constants
13
13
  import { to_define_import_meta_env, default_ts_transform_options } from './esbuild_helpers.js';
14
14
  import { resolve_specifier } from './resolve_specifier.js';
15
15
  import { resolve_node_specifier } from './resolve_node_specifier.js';
16
+ import { map_sveltekit_aliases } from './sveltekit_helpers.js';
16
17
  /*
17
18
 
18
19
  Usage via `$lib/register.ts`:
@@ -47,7 +48,7 @@ const ts_transform_options = {
47
48
  define: to_define_import_meta_env(dev, base_url),
48
49
  sourcemap: 'inline',
49
50
  };
50
- const aliases = Object.entries({ $lib: 'src/lib', ...alias });
51
+ const aliases = Object.entries(alias);
51
52
  const RAW_MATCHER = /(%3Fraw|\.css|\.svg)$/; // TODO others? configurable?
52
53
  const ENV_MATCHER = /src\/lib\/\$env\/(static|dynamic)\/(public|private)$/;
53
54
  const NODE_MODULES_MATCHER = new RegExp(escape_regexp('/' + NODE_MODULES_DIRNAME + '/'), 'u');
@@ -151,14 +152,7 @@ export const resolve = async (specifier, context, nextResolve) => {
151
152
  if (shimmed !== undefined) {
152
153
  return nextResolve(shimmed, context);
153
154
  }
154
- let path = specifier;
155
- // Map the path with the SvelteKit aliases.
156
- for (const [from, to] of aliases) {
157
- if (path.startsWith(from)) {
158
- path = join(dir, to, path.substring(from.length));
159
- break;
160
- }
161
- }
155
+ const path = map_sveltekit_aliases(specifier, aliases);
162
156
  // The specifier `path` has now been mapped to its final form, so we can inspect it.
163
157
  if (path[0] !== '.' && path[0] !== '/') {
164
158
  // Resolve to `node_modules`.
package/dist/package.d.ts CHANGED
@@ -177,6 +177,10 @@ export declare const package_json: {
177
177
  types: string;
178
178
  default: string;
179
179
  };
180
+ './filer.js': {
181
+ types: string;
182
+ default: string;
183
+ };
180
184
  './format_directory.js': {
181
185
  types: string;
182
186
  default: string;
@@ -289,6 +293,10 @@ export declare const package_json: {
289
293
  types: string;
290
294
  default: string;
291
295
  };
296
+ './parse_imports.js': {
297
+ types: string;
298
+ default: string;
299
+ };
292
300
  './path_constants.js': {
293
301
  types: string;
294
302
  default: string;
@@ -582,6 +590,13 @@ export declare const src_json: {
582
590
  kind: string;
583
591
  }[];
584
592
  };
593
+ './filer.js': {
594
+ path: string;
595
+ declarations: {
596
+ name: string;
597
+ kind: string;
598
+ }[];
599
+ };
585
600
  './format_directory.js': {
586
601
  path: string;
587
602
  declarations: {
@@ -772,6 +787,13 @@ export declare const src_json: {
772
787
  kind: string;
773
788
  }[];
774
789
  };
790
+ './parse_imports.js': {
791
+ path: string;
792
+ declarations: {
793
+ name: string;
794
+ kind: string;
795
+ }[];
796
+ };
775
797
  './path_constants.js': {
776
798
  path: string;
777
799
  declarations: {
@@ -1 +1 @@
1
- {"version":3,"file":"package.d.ts","sourceRoot":"../src/lib/","sources":["package.ts"],"names":[],"mappings":"AAKA,eAAO,MAAM,YAAY;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAqQD,CAAC;AAEzB,eAAO,MAAM,QAAQ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAsuBD,CAAC"}
1
+ {"version":3,"file":"package.d.ts","sourceRoot":"../src/lib/","sources":["package.ts"],"names":[],"mappings":"AAKA,eAAO,MAAM,YAAY;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAuQD,CAAC;AAEzB,eAAO,MAAM,QAAQ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA8vBD,CAAC"}