@ryanatkn/gro 0.135.1 → 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.
- package/dist/check.task.d.ts +2 -2
- package/dist/filer.d.ts +29 -0
- package/dist/filer.d.ts.map +1 -0
- package/dist/filer.js +165 -0
- package/dist/gen.task.d.ts +2 -2
- package/dist/gro.config.default.d.ts.map +1 -1
- package/dist/gro.config.default.js +2 -3
- package/dist/gro_plugin_gen.d.ts +9 -2
- package/dist/gro_plugin_gen.d.ts.map +1 -1
- package/dist/gro_plugin_gen.js +67 -37
- package/dist/gro_plugin_sveltekit_library.d.ts.map +1 -1
- package/dist/gro_plugin_sveltekit_library.js +4 -16
- package/dist/invoke_task.d.ts +2 -1
- package/dist/invoke_task.d.ts.map +1 -1
- package/dist/invoke_task.js +4 -2
- package/dist/loader.d.ts.map +1 -1
- package/dist/loader.js +3 -9
- package/dist/package.d.ts +22 -0
- package/dist/package.d.ts.map +1 -1
- package/dist/package.js +39 -13
- package/dist/parse_imports.d.ts +6 -0
- package/dist/parse_imports.d.ts.map +1 -0
- package/dist/parse_imports.js +29 -0
- package/dist/path_constants.d.ts +1 -0
- package/dist/path_constants.d.ts.map +1 -1
- package/dist/path_constants.js +1 -0
- package/dist/paths.d.ts +2 -1
- package/dist/paths.d.ts.map +1 -1
- package/dist/paths.js +3 -1
- package/dist/run_task.d.ts +2 -1
- package/dist/run_task.d.ts.map +1 -1
- package/dist/run_task.js +4 -3
- package/dist/search_fs.d.ts +2 -2
- package/dist/search_fs.js +11 -11
- package/dist/sveltekit_config.d.ts +1 -1
- package/dist/sveltekit_config.d.ts.map +1 -1
- package/dist/sveltekit_config.js +1 -1
- package/dist/sveltekit_helpers.d.ts +6 -0
- package/dist/sveltekit_helpers.d.ts.map +1 -1
- package/dist/sveltekit_helpers.js +32 -0
- package/dist/sync.task.d.ts +2 -2
- package/dist/task.d.ts +2 -0
- package/dist/task.d.ts.map +1 -1
- package/dist/watch_dir.d.ts +1 -1
- package/dist/watch_dir.d.ts.map +1 -1
- package/dist/watch_dir.js +14 -12
- package/package.json +19 -11
- package/src/lib/filer.ts +212 -0
- package/src/lib/gro.config.default.ts +2 -3
- package/src/lib/gro_plugin_gen.ts +90 -43
- package/src/lib/gro_plugin_sveltekit_library.ts +4 -20
- package/src/lib/invoke_task.ts +6 -1
- package/src/lib/loader.ts +3 -10
- package/src/lib/package.ts +39 -13
- package/src/lib/parse_imports.ts +42 -0
- package/src/lib/path_constants.ts +1 -0
- package/src/lib/paths.ts +8 -1
- package/src/lib/run_task.ts +5 -2
- package/src/lib/search_fs.ts +11 -12
- package/src/lib/sveltekit_config.ts +2 -2
- package/src/lib/sveltekit_helpers.ts +46 -0
- package/src/lib/task.ts +2 -0
- package/src/lib/watch_dir.ts +15 -14
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ryanatkn/gro",
|
|
3
|
-
"version": "0.135.
|
|
3
|
+
"version": "0.135.3",
|
|
4
4
|
"description": "task runner and toolkit extending SvelteKit",
|
|
5
5
|
"motto": "generate, run, optimize",
|
|
6
6
|
"glyph": "🌰",
|
|
@@ -56,7 +56,7 @@
|
|
|
56
56
|
"esm-env": "^1.0.0",
|
|
57
57
|
"mri": "^1.2.0",
|
|
58
58
|
"prettier": "^3.3.3",
|
|
59
|
-
"prettier-plugin-svelte": "^3.2.
|
|
59
|
+
"prettier-plugin-svelte": "^3.2.7",
|
|
60
60
|
"ts-morph": "^23.0.0",
|
|
61
61
|
"tslib": "^2.7.0",
|
|
62
62
|
"zod": "^3.23.8"
|
|
@@ -68,22 +68,22 @@
|
|
|
68
68
|
"devDependencies": {
|
|
69
69
|
"@changesets/changelog-git": "^0.2.0",
|
|
70
70
|
"@changesets/types": "^6.0.0",
|
|
71
|
-
"@ryanatkn/eslint-config": "^0.5.
|
|
72
|
-
"@ryanatkn/fuz": "^0.
|
|
71
|
+
"@ryanatkn/eslint-config": "^0.5.4",
|
|
72
|
+
"@ryanatkn/fuz": "^0.129.0",
|
|
73
73
|
"@ryanatkn/moss": "^0.16.1",
|
|
74
74
|
"@sveltejs/adapter-static": "^3.0.5",
|
|
75
|
-
"@sveltejs/kit": "^2.
|
|
75
|
+
"@sveltejs/kit": "^2.6.1",
|
|
76
76
|
"@sveltejs/package": "^2.3.5",
|
|
77
77
|
"@sveltejs/vite-plugin-svelte": "^3.1.2",
|
|
78
78
|
"@types/fs-extra": "^11.0.4",
|
|
79
|
-
"@types/node": "^22.
|
|
79
|
+
"@types/node": "^22.7.4",
|
|
80
80
|
"esbuild": "^0.21.5",
|
|
81
|
-
"eslint": "^9.11.
|
|
82
|
-
"eslint-plugin-svelte": "^2.44.
|
|
83
|
-
"svelte": "^5.0.0-next.
|
|
84
|
-
"svelte-check": "^4.0.
|
|
81
|
+
"eslint": "^9.11.1",
|
|
82
|
+
"eslint-plugin-svelte": "^2.44.1",
|
|
83
|
+
"svelte": "^5.0.0-next.260",
|
|
84
|
+
"svelte-check": "^4.0.4",
|
|
85
85
|
"typescript": "^5.6.2",
|
|
86
|
-
"typescript-eslint": "^8.
|
|
86
|
+
"typescript-eslint": "^8.7.0",
|
|
87
87
|
"uvu": "^0.5.6"
|
|
88
88
|
},
|
|
89
89
|
"prettier": {
|
|
@@ -198,6 +198,10 @@
|
|
|
198
198
|
"types": "./dist/esbuild_plugin_sveltekit_shim_env.d.ts",
|
|
199
199
|
"default": "./dist/esbuild_plugin_sveltekit_shim_env.js"
|
|
200
200
|
},
|
|
201
|
+
"./filer.js": {
|
|
202
|
+
"types": "./dist/filer.d.ts",
|
|
203
|
+
"default": "./dist/filer.js"
|
|
204
|
+
},
|
|
201
205
|
"./format_directory.js": {
|
|
202
206
|
"types": "./dist/format_directory.d.ts",
|
|
203
207
|
"default": "./dist/format_directory.js"
|
|
@@ -310,6 +314,10 @@
|
|
|
310
314
|
"types": "./dist/package.d.ts",
|
|
311
315
|
"default": "./dist/package.js"
|
|
312
316
|
},
|
|
317
|
+
"./parse_imports.js": {
|
|
318
|
+
"types": "./dist/parse_imports.d.ts",
|
|
319
|
+
"default": "./dist/parse_imports.js"
|
|
320
|
+
},
|
|
313
321
|
"./path_constants.js": {
|
|
314
322
|
"types": "./dist/path_constants.d.ts",
|
|
315
323
|
"default": "./dist/path_constants.js"
|
package/src/lib/filer.ts
ADDED
|
@@ -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
|
-
|
|
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
|
-
|
|
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
|
|
3
|
+
import type {Plugin} from './plugin.js';
|
|
6
4
|
import type {Args} from './args.js';
|
|
7
|
-
import {
|
|
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
|
|
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
|
|
31
|
+
let flushing_timeout: NodeJS.Timeout | undefined;
|
|
22
32
|
const queued_files: Set<string> = new Set();
|
|
23
|
-
const queue_gen = (
|
|
24
|
-
queued_files.add(
|
|
25
|
-
|
|
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
|
-
},
|
|
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 ({
|
|
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 (!
|
|
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
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
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: (
|
|
90
|
-
if (
|
|
91
|
-
|
|
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
|
-
|
|
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
|
-
|
|
35
|
-
|
|
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();
|
package/src/lib/invoke_task.ts
CHANGED
|
@@ -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
|
-
|
|
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(
|
|
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
|
-
|
|
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] !== '/') {
|