@ryanatkn/gro 0.179.0 → 0.180.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.
- package/dist/build.task.d.ts +2 -0
- package/dist/build.task.d.ts.map +1 -1
- package/dist/build.task.js +14 -10
- package/dist/build_cache.d.ts +3 -3
- package/dist/build_cache.d.ts.map +1 -1
- package/dist/build_cache.js +53 -43
- package/dist/changeset.task.js +9 -9
- package/dist/clean_fs.d.ts +1 -1
- package/dist/clean_fs.d.ts.map +1 -1
- package/dist/clean_fs.js +3 -4
- package/dist/cli.d.ts +4 -4
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +11 -12
- package/dist/deploy.task.d.ts +7 -0
- package/dist/deploy.task.d.ts.map +1 -1
- package/dist/deploy.task.js +27 -14
- package/dist/esbuild_plugin_external_worker.js +1 -1
- package/dist/esbuild_plugin_svelte.js +4 -4
- package/dist/esbuild_plugin_sveltekit_local_imports.js +2 -2
- package/dist/filer.d.ts.map +1 -1
- package/dist/filer.js +103 -52
- package/dist/format_file.js +1 -1
- package/dist/gen.d.ts +1 -1
- package/dist/gen.d.ts.map +1 -1
- package/dist/gen.js +28 -22
- package/dist/gen.task.js +1 -1
- package/dist/gro.config.default.js +1 -1
- package/dist/gro_config.js +2 -2
- package/dist/gro_plugin_gen.js +1 -1
- package/dist/gro_plugin_server.js +2 -2
- package/dist/gro_plugin_sveltekit_app.d.ts.map +1 -1
- package/dist/gro_plugin_sveltekit_app.js +40 -36
- package/dist/gro_plugin_sveltekit_library.js +2 -1
- package/dist/input_path.d.ts +3 -3
- package/dist/input_path.d.ts.map +1 -1
- package/dist/input_path.js +16 -14
- package/dist/invoke_task.js +2 -2
- package/dist/lint.task.js +1 -1
- package/dist/loader.js +1 -1
- package/dist/modules.js +2 -2
- package/dist/package_json.d.ts +4 -4
- package/dist/package_json.d.ts.map +1 -1
- package/dist/package_json.js +12 -14
- package/dist/publish.task.js +6 -6
- package/dist/release.task.js +1 -1
- package/dist/resolve.task.js +2 -2
- package/dist/resolve_specifier.d.ts +1 -1
- package/dist/resolve_specifier.d.ts.map +1 -1
- package/dist/resolve_specifier.js +5 -4
- package/dist/run.task.js +2 -2
- package/dist/run_gen.d.ts.map +1 -1
- package/dist/run_gen.js +9 -8
- package/dist/run_task.js +4 -4
- package/dist/source_json.d.ts +2 -2
- package/dist/source_json.d.ts.map +1 -1
- package/dist/source_json.js +16 -15
- package/dist/sveltekit_helpers.js +3 -3
- package/dist/sveltekit_shim_env.js +1 -1
- package/dist/task.d.ts +1 -1
- package/dist/task.d.ts.map +1 -1
- package/dist/task.js +4 -4
- package/dist/test.task.d.ts.map +1 -1
- package/dist/test.task.js +5 -4
- package/dist/typecheck.task.js +3 -3
- package/dist/upgrade.task.js +4 -4
- package/package.json +5 -5
- package/src/lib/build.task.ts +15 -10
- package/src/lib/build_cache.ts +79 -63
- package/src/lib/changeset.task.ts +10 -10
- package/src/lib/clean_fs.ts +4 -4
- package/src/lib/cli.ts +15 -14
- package/src/lib/deploy.task.ts +30 -13
- package/src/lib/esbuild_plugin_external_worker.ts +1 -1
- package/src/lib/esbuild_plugin_svelte.ts +4 -4
- package/src/lib/esbuild_plugin_sveltekit_local_imports.ts +2 -2
- package/src/lib/filer.ts +111 -52
- package/src/lib/format_file.ts +1 -1
- package/src/lib/gen.task.ts +1 -1
- package/src/lib/gen.ts +52 -46
- package/src/lib/gro.config.default.ts +1 -1
- package/src/lib/gro_config.ts +2 -2
- package/src/lib/gro_plugin_gen.ts +1 -1
- package/src/lib/gro_plugin_server.ts +2 -2
- package/src/lib/gro_plugin_sveltekit_app.ts +43 -39
- package/src/lib/gro_plugin_sveltekit_library.ts +2 -2
- package/src/lib/input_path.ts +20 -21
- package/src/lib/invoke_task.ts +2 -2
- package/src/lib/lint.task.ts +1 -1
- package/src/lib/loader.ts +1 -1
- package/src/lib/modules.ts +2 -2
- package/src/lib/package_json.ts +16 -20
- package/src/lib/publish.task.ts +6 -6
- package/src/lib/release.task.ts +1 -1
- package/src/lib/resolve.task.ts +2 -2
- package/src/lib/resolve_specifier.ts +8 -4
- package/src/lib/run.task.ts +2 -2
- package/src/lib/run_gen.ts +15 -10
- package/src/lib/run_task.ts +4 -4
- package/src/lib/source_json.ts +22 -18
- package/src/lib/sveltekit_helpers.ts +3 -3
- package/src/lib/task.ts +11 -9
- package/src/lib/test.task.ts +4 -3
- package/src/lib/typecheck.task.ts +3 -3
- package/src/lib/upgrade.task.ts +4 -4
- package/dist/search_fs.d.ts +0 -26
- package/dist/search_fs.d.ts.map +0 -1
- package/dist/search_fs.js +0 -52
- package/src/lib/search_fs.ts +0 -100
package/src/lib/filer.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import {EMPTY_OBJECT} from '@ryanatkn/belt/object.js';
|
|
2
|
-
import {
|
|
2
|
+
import {readFile, stat} from 'node:fs/promises';
|
|
3
3
|
import {dirname, resolve} from 'node:path';
|
|
4
4
|
import type {OmitStrict} from '@ryanatkn/belt/types.js';
|
|
5
5
|
import {isBuiltin} from 'node:module';
|
|
@@ -51,6 +51,9 @@ export class Filer {
|
|
|
51
51
|
#initing: Promise<void> | undefined;
|
|
52
52
|
#closing: Promise<void> | undefined;
|
|
53
53
|
|
|
54
|
+
#change_queue: Array<WatcherChange> = [];
|
|
55
|
+
#processing_promise: Promise<void> | null = null;
|
|
56
|
+
|
|
54
57
|
constructor(options: FilerOptions = EMPTY_OBJECT) {
|
|
55
58
|
this.#watch_dir = options.watch_dir ?? watch_dir;
|
|
56
59
|
this.#watch_dir_options = options.watch_dir_options ?? EMPTY_OBJECT;
|
|
@@ -82,9 +85,11 @@ export class Filer {
|
|
|
82
85
|
dependencies: new Map(),
|
|
83
86
|
};
|
|
84
87
|
this.files.set(id, file);
|
|
85
|
-
//
|
|
88
|
+
// Defer external file change notification to avoid reentrancy during queue processing
|
|
86
89
|
if (file.external) {
|
|
87
|
-
|
|
90
|
+
queueMicrotask(() => {
|
|
91
|
+
this.#on_change({type: 'add', path: file.id, is_directory: false});
|
|
92
|
+
});
|
|
88
93
|
}
|
|
89
94
|
return file;
|
|
90
95
|
};
|
|
@@ -117,10 +122,10 @@ export class Filer {
|
|
|
117
122
|
this.#initing = this.#init();
|
|
118
123
|
try {
|
|
119
124
|
await this.#initing;
|
|
120
|
-
} catch (
|
|
125
|
+
} catch (error) {
|
|
121
126
|
// use shared cleanup logic
|
|
122
127
|
this.#cleanup();
|
|
123
|
-
throw
|
|
128
|
+
throw error;
|
|
124
129
|
} finally {
|
|
125
130
|
this.#initing = undefined;
|
|
126
131
|
}
|
|
@@ -136,6 +141,9 @@ export class Filer {
|
|
|
136
141
|
try {
|
|
137
142
|
await watcher.init();
|
|
138
143
|
|
|
144
|
+
// Wait for any queued changes from init to be processed
|
|
145
|
+
await this.#drain_queue();
|
|
146
|
+
|
|
139
147
|
// check if close() was called during init
|
|
140
148
|
if (this.#closing) {
|
|
141
149
|
await watcher.close();
|
|
@@ -144,14 +152,14 @@ export class Filer {
|
|
|
144
152
|
|
|
145
153
|
// only set after successful init and not closing
|
|
146
154
|
this.#watching = watcher;
|
|
147
|
-
} catch (
|
|
155
|
+
} catch (error) {
|
|
148
156
|
// clean up watcher on error, but don't let close error mask init error
|
|
149
157
|
try {
|
|
150
158
|
await watcher.close();
|
|
151
159
|
} catch {
|
|
152
160
|
// ignore close errors - init error is more important
|
|
153
161
|
}
|
|
154
|
-
throw
|
|
162
|
+
throw error;
|
|
155
163
|
}
|
|
156
164
|
}
|
|
157
165
|
|
|
@@ -169,6 +177,8 @@ export class Filer {
|
|
|
169
177
|
this.#listeners.clear();
|
|
170
178
|
this.files.clear();
|
|
171
179
|
this.#watching = undefined;
|
|
180
|
+
this.#change_queue = [];
|
|
181
|
+
this.#processing_promise = null;
|
|
172
182
|
// #initing is handled in finally block of init()
|
|
173
183
|
}
|
|
174
184
|
|
|
@@ -214,20 +224,22 @@ export class Filer {
|
|
|
214
224
|
this.#cleanup();
|
|
215
225
|
}
|
|
216
226
|
|
|
217
|
-
#update(id: PathId): Disknode | null {
|
|
227
|
+
async #update(id: PathId): Promise<Disknode | null> {
|
|
218
228
|
const file = this.get_or_create(id);
|
|
219
229
|
|
|
220
|
-
let stats: ReturnType<typeof
|
|
230
|
+
let stats: Awaited<ReturnType<typeof stat>> | null = null;
|
|
221
231
|
let new_contents: string | null = null; // TODO need to lazily load contents, probably turn `Disknode` into a class
|
|
222
232
|
|
|
223
233
|
try {
|
|
224
|
-
stats =
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
234
|
+
[stats, new_contents] = await Promise.all([stat(id), readFile(id, 'utf8')]);
|
|
235
|
+
} catch (error) {
|
|
236
|
+
const code = (error as NodeJS.ErrnoException).code;
|
|
237
|
+
// Treat file as deleted/inaccessible for common error codes
|
|
238
|
+
if (code === 'ENOENT' || code === 'EACCES' || code === 'EPERM') {
|
|
239
|
+
// File doesn't exist or is inaccessible, treat as deleted
|
|
240
|
+
} else {
|
|
241
|
+
throw error;
|
|
229
242
|
}
|
|
230
|
-
// ENOENT: file doesn't exist, treat as deleted
|
|
231
243
|
}
|
|
232
244
|
|
|
233
245
|
const old_mtime = file.mtime;
|
|
@@ -245,7 +257,14 @@ export class Filer {
|
|
|
245
257
|
const dependencies_before = new Set(file.dependencies.keys());
|
|
246
258
|
const dependencies_removed = new Set(dependencies_before);
|
|
247
259
|
|
|
248
|
-
|
|
260
|
+
let imported: Array<string> = [];
|
|
261
|
+
if (file.contents) {
|
|
262
|
+
try {
|
|
263
|
+
imported = parse_imports(file.id, file.contents);
|
|
264
|
+
} catch (error) {
|
|
265
|
+
this.#log?.error('[filer] Failed to parse imports', file.id, error);
|
|
266
|
+
}
|
|
267
|
+
}
|
|
249
268
|
for (const specifier of imported) {
|
|
250
269
|
if (SVELTEKIT_GLOBAL_SPECIFIER.test(specifier)) continue;
|
|
251
270
|
const path = map_sveltekit_aliases(specifier, aliases);
|
|
@@ -253,7 +272,7 @@ export class Filer {
|
|
|
253
272
|
let path_id;
|
|
254
273
|
// TODO can we replace `resolve_specifier` with `import.meta.resolve` completely now outside of esbuild plugins?
|
|
255
274
|
if (path[0] === '.' || path[0] === '/') {
|
|
256
|
-
const resolved = resolve_specifier(path, dir);
|
|
275
|
+
const resolved = await resolve_specifier(path, dir); // eslint-disable-line no-await-in-loop
|
|
257
276
|
path_id = resolved.path_id;
|
|
258
277
|
} else {
|
|
259
278
|
if (isBuiltin(path)) continue;
|
|
@@ -304,8 +323,8 @@ export class Filer {
|
|
|
304
323
|
for (const disknode of this.files.values()) {
|
|
305
324
|
try {
|
|
306
325
|
listener({type: 'add', path: disknode.id, is_directory: false}, disknode);
|
|
307
|
-
} catch (
|
|
308
|
-
this.#log?.error('[filer] Listener error during sync:',
|
|
326
|
+
} catch (error) {
|
|
327
|
+
this.#log?.error('[filer] Listener error during sync:', error);
|
|
309
328
|
}
|
|
310
329
|
}
|
|
311
330
|
}
|
|
@@ -314,8 +333,8 @@ export class Filer {
|
|
|
314
333
|
for (const listener of this.#listeners) {
|
|
315
334
|
try {
|
|
316
335
|
listener(change, disknode);
|
|
317
|
-
} catch (
|
|
318
|
-
this.#log?.error('[filer] Listener error during change notification:',
|
|
336
|
+
} catch (error) {
|
|
337
|
+
this.#log?.error('[filer] Listener error during change notification:', error);
|
|
319
338
|
}
|
|
320
339
|
}
|
|
321
340
|
}
|
|
@@ -335,26 +354,61 @@ export class Filer {
|
|
|
335
354
|
// keep watching active even with no listeners, only close() tears down
|
|
336
355
|
}
|
|
337
356
|
|
|
338
|
-
#
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
357
|
+
async #drain_queue(): Promise<void> {
|
|
358
|
+
// Wait for queue to be empty and no active processing
|
|
359
|
+
while (this.#change_queue.length > 0 || this.#processing_promise) {
|
|
360
|
+
await this.#process_queue(); // eslint-disable-line no-await-in-loop
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
async #process_queue(): Promise<void> {
|
|
365
|
+
// If already processing, return the existing promise
|
|
366
|
+
if (this.#processing_promise) return this.#processing_promise;
|
|
367
|
+
|
|
368
|
+
// Create and track the processing promise
|
|
369
|
+
this.#processing_promise = this.#do_process_queue();
|
|
370
|
+
|
|
371
|
+
try {
|
|
372
|
+
await this.#processing_promise;
|
|
373
|
+
} finally {
|
|
374
|
+
this.#processing_promise = null;
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
async #do_process_queue(): Promise<void> {
|
|
379
|
+
while (this.#change_queue.length > 0) {
|
|
380
|
+
const change = this.#change_queue.shift()!;
|
|
381
|
+
|
|
382
|
+
if (this.#closing) continue; // ignore changes during close
|
|
383
|
+
if (change.is_directory) continue; // TODO manage directories?
|
|
384
|
+
|
|
385
|
+
let disknode: Disknode | null;
|
|
386
|
+
switch (change.type) {
|
|
387
|
+
case 'add':
|
|
388
|
+
case 'update': {
|
|
389
|
+
disknode = await this.#update(change.path); // eslint-disable-line no-await-in-loop
|
|
390
|
+
break;
|
|
391
|
+
}
|
|
392
|
+
case 'delete': {
|
|
393
|
+
disknode = this.#remove(change.path);
|
|
394
|
+
break;
|
|
395
|
+
}
|
|
396
|
+
default:
|
|
397
|
+
throw new UnreachableError(change.type);
|
|
347
398
|
}
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
399
|
+
|
|
400
|
+
if (disknode && this.#listeners.size > 0) {
|
|
401
|
+
this.#notify_change(change, disknode);
|
|
351
402
|
}
|
|
352
|
-
default:
|
|
353
|
-
throw new UnreachableError(change.type);
|
|
354
|
-
}
|
|
355
|
-
if (disknode && this.#listeners.size > 0) {
|
|
356
|
-
this.#notify_change(change, disknode);
|
|
357
403
|
}
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
#on_change: WatcherChangeCallback = (change) => {
|
|
407
|
+
// Enqueue the change (sync callback from chokidar)
|
|
408
|
+
this.#change_queue.push(change);
|
|
409
|
+
|
|
410
|
+
// Start processing if not already running
|
|
411
|
+
void this.#process_queue();
|
|
358
412
|
};
|
|
359
413
|
|
|
360
414
|
#is_external(id: PathId): boolean {
|
|
@@ -372,21 +426,26 @@ export const filter_dependents = (
|
|
|
372
426
|
searched: Set<PathId> = new Set(),
|
|
373
427
|
log?: Logger,
|
|
374
428
|
): Set<PathId> => {
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
);
|
|
387
|
-
|
|
429
|
+
// Use iterative approach to avoid stack overflow on deep dependency trees
|
|
430
|
+
const stack = [disknode];
|
|
431
|
+
|
|
432
|
+
while (stack.length > 0) {
|
|
433
|
+
const current = stack.pop()!;
|
|
434
|
+
for (const dependent_id of current.dependents.keys()) {
|
|
435
|
+
if (searched.has(dependent_id)) continue;
|
|
436
|
+
searched.add(dependent_id);
|
|
437
|
+
if (!filter || filter(dependent_id)) {
|
|
438
|
+
results.add(dependent_id);
|
|
439
|
+
}
|
|
440
|
+
const dependent_disknode = get_by_id(dependent_id);
|
|
441
|
+
if (!dependent_disknode) {
|
|
442
|
+
log?.warn(
|
|
443
|
+
`[filer.filter_dependents] dependent source file ${dependent_id} not found for ${current.id}`,
|
|
444
|
+
);
|
|
445
|
+
continue;
|
|
446
|
+
}
|
|
447
|
+
stack.push(dependent_disknode);
|
|
388
448
|
}
|
|
389
|
-
filter_dependents(dependent_disknode, get_by_id, filter, results, searched);
|
|
390
449
|
}
|
|
391
450
|
return results;
|
|
392
451
|
};
|
package/src/lib/format_file.ts
CHANGED
|
@@ -19,7 +19,7 @@ export const format_file = async (
|
|
|
19
19
|
const final_base_options =
|
|
20
20
|
base_options !== undefined
|
|
21
21
|
? base_options
|
|
22
|
-
: (cached_base_options = load_package_json().prettier as any);
|
|
22
|
+
: (cached_base_options = (await load_package_json()).prettier as any);
|
|
23
23
|
let final_options = options;
|
|
24
24
|
if (options.filepath && !options.parser) {
|
|
25
25
|
const {filepath, ...rest} = options;
|
package/src/lib/gen.task.ts
CHANGED
|
@@ -43,7 +43,7 @@ export const task: Task<Args> = {
|
|
|
43
43
|
const input_paths = to_input_paths(raw_input_paths);
|
|
44
44
|
|
|
45
45
|
// load all of the gen modules
|
|
46
|
-
const found = find_genfiles(input_paths, root_dirs, config, timings);
|
|
46
|
+
const found = await find_genfiles(input_paths, root_dirs, config, timings);
|
|
47
47
|
if (!found.ok) {
|
|
48
48
|
if (found.type === 'input_directories_with_no_files') {
|
|
49
49
|
// TODO maybe let this error like the normal case, but only call `gro gen` if we find gen files? problem is the args would need to be hoisted to callers like `gro sync`
|
package/src/lib/gen.ts
CHANGED
|
@@ -4,8 +4,9 @@ import {mkdir, readFile, writeFile} from 'node:fs/promises';
|
|
|
4
4
|
import type {Result} from '@ryanatkn/belt/result.js';
|
|
5
5
|
import type {Timings} from '@ryanatkn/belt/timings.js';
|
|
6
6
|
import {styleText as st} from 'node:util';
|
|
7
|
-
import {existsSync} from 'node:fs';
|
|
8
7
|
import type {PathId} from '@ryanatkn/belt/path.js';
|
|
8
|
+
import {map_concurrent} from '@ryanatkn/belt/async.js';
|
|
9
|
+
import {fs_search} from '@ryanatkn/belt/fs.js';
|
|
9
10
|
|
|
10
11
|
import {print_path} from './paths.ts';
|
|
11
12
|
import type {GroConfig} from './gro_config.ts';
|
|
@@ -18,7 +19,6 @@ import {
|
|
|
18
19
|
type ResolvedInputFile,
|
|
19
20
|
type ResolvedInputPath,
|
|
20
21
|
} from './input_path.ts';
|
|
21
|
-
import {search_fs} from './search_fs.ts';
|
|
22
22
|
import type {Filer} from './filer.ts';
|
|
23
23
|
import type {InvokeTask} from './task.ts';
|
|
24
24
|
|
|
@@ -210,23 +210,28 @@ export type AnalyzedGenResult =
|
|
|
210
210
|
has_changed: true;
|
|
211
211
|
};
|
|
212
212
|
|
|
213
|
-
export const analyze_gen_results = (
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
213
|
+
export const analyze_gen_results = async (
|
|
214
|
+
gen_results: GenResults,
|
|
215
|
+
): Promise<Array<AnalyzedGenResult>> => {
|
|
216
|
+
const files = gen_results.successes.flatMap((result) => result.files);
|
|
217
|
+
return map_concurrent(files, (file) => analyze_gen_result(file), 10);
|
|
218
|
+
};
|
|
219
219
|
|
|
220
220
|
export const analyze_gen_result = async (file: GenFile): Promise<AnalyzedGenResult> => {
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
221
|
+
let existing_content: string;
|
|
222
|
+
try {
|
|
223
|
+
existing_content = await readFile(file.id, 'utf8');
|
|
224
|
+
} catch (error) {
|
|
225
|
+
if (error.code === 'ENOENT') {
|
|
226
|
+
return {
|
|
227
|
+
file,
|
|
228
|
+
existing_content: null,
|
|
229
|
+
is_new: true,
|
|
230
|
+
has_changed: true,
|
|
231
|
+
};
|
|
232
|
+
}
|
|
233
|
+
throw error;
|
|
228
234
|
}
|
|
229
|
-
const existing_content = await readFile(file.id, 'utf8');
|
|
230
235
|
return {
|
|
231
236
|
file,
|
|
232
237
|
existing_content,
|
|
@@ -240,26 +245,25 @@ export const write_gen_results = async (
|
|
|
240
245
|
analyzed_gen_results: Array<AnalyzedGenResult>,
|
|
241
246
|
log: Logger,
|
|
242
247
|
): Promise<void> => {
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
.flat(),
|
|
248
|
+
const files = gen_results.successes.flatMap((result) => result.files);
|
|
249
|
+
await map_concurrent(
|
|
250
|
+
files,
|
|
251
|
+
async (file) => {
|
|
252
|
+
const analyzed = analyzed_gen_results.find((r) => r.file.id === file.id);
|
|
253
|
+
if (!analyzed) throw Error('Expected to find analyzed result: ' + file.id);
|
|
254
|
+
const log_args = [print_path(file.id), 'generated from', print_path(file.origin_id)];
|
|
255
|
+
if (analyzed.is_new) {
|
|
256
|
+
log.info('writing new', ...log_args);
|
|
257
|
+
await mkdir(dirname(file.id), {recursive: true});
|
|
258
|
+
await writeFile(file.id, file.content);
|
|
259
|
+
} else if (analyzed.has_changed) {
|
|
260
|
+
log.info('writing changed', ...log_args);
|
|
261
|
+
await writeFile(file.id, file.content);
|
|
262
|
+
} else {
|
|
263
|
+
log.info('skipping unchanged', ...log_args);
|
|
264
|
+
}
|
|
265
|
+
},
|
|
266
|
+
10,
|
|
263
267
|
);
|
|
264
268
|
};
|
|
265
269
|
|
|
@@ -289,17 +293,17 @@ export type FindGenfilesFailure =
|
|
|
289
293
|
/**
|
|
290
294
|
* Finds modules from input paths. (see `src/lib/input_path.ts` for more)
|
|
291
295
|
*/
|
|
292
|
-
export const find_genfiles = (
|
|
296
|
+
export const find_genfiles = async (
|
|
293
297
|
input_paths: Array<InputPath>,
|
|
294
298
|
root_dirs: Array<PathId>,
|
|
295
299
|
config: GroConfig,
|
|
296
300
|
timings?: Timings,
|
|
297
|
-
): FindGenfilesResult => {
|
|
301
|
+
): Promise<FindGenfilesResult> => {
|
|
298
302
|
const extensions: Array<string> = [GEN_FILE_PATTERN];
|
|
299
303
|
|
|
300
304
|
// Check which extension variation works - if it's a directory, prefer others first!
|
|
301
305
|
const timing_to_resolve_input_paths = timings?.start('resolve input paths');
|
|
302
|
-
const {resolved_input_paths, unmapped_input_paths} = resolve_input_paths(
|
|
306
|
+
const {resolved_input_paths, unmapped_input_paths} = await resolve_input_paths(
|
|
303
307
|
input_paths,
|
|
304
308
|
root_dirs,
|
|
305
309
|
extensions,
|
|
@@ -320,15 +324,17 @@ export const find_genfiles = (
|
|
|
320
324
|
}
|
|
321
325
|
|
|
322
326
|
// Find all of the files for any directories.
|
|
323
|
-
const
|
|
327
|
+
const timing_to_fs_search = timings?.start('find files');
|
|
324
328
|
const {resolved_input_files, resolved_input_files_by_root_dir, input_directories_with_no_files} =
|
|
325
|
-
resolve_input_files(
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
329
|
+
await resolve_input_files(
|
|
330
|
+
resolved_input_paths,
|
|
331
|
+
async (id) =>
|
|
332
|
+
await fs_search(id, {
|
|
333
|
+
filter: config.search_filters,
|
|
334
|
+
file_filter: (p) => extensions.some((e) => p.includes(e)),
|
|
335
|
+
}),
|
|
330
336
|
);
|
|
331
|
-
|
|
337
|
+
timing_to_fs_search?.();
|
|
332
338
|
|
|
333
339
|
// Error if any input path has no files. (means we have an empty directory)
|
|
334
340
|
if (input_directories_with_no_files.length) {
|
|
@@ -18,7 +18,7 @@ import {load_package_json} from './package_json.ts';
|
|
|
18
18
|
* - if `src/lib/server/server.ts`, assumes a Node server - needs config
|
|
19
19
|
*/
|
|
20
20
|
const config: CreateGroConfig = async (cfg, svelte_config) => {
|
|
21
|
-
const package_json = load_package_json(); // TODO gets wastefully loaded by some plugins, maybe put in plugin/task context? how does that interact with `map_package_json`?
|
|
21
|
+
const package_json = await load_package_json(); // TODO gets wastefully loaded by some plugins, maybe put in plugin/task context? how does that interact with `map_package_json`?
|
|
22
22
|
|
|
23
23
|
const [has_server_result, has_sveltekit_library_result, has_sveltekit_app_result] =
|
|
24
24
|
await Promise.all([
|
package/src/lib/gro_config.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import {join, resolve} from 'node:path';
|
|
2
|
-
import {
|
|
2
|
+
import {fs_exists} from '@ryanatkn/belt/fs.js';
|
|
3
3
|
import {identity} from '@ryanatkn/belt/function.js';
|
|
4
4
|
import type {PathFilter, PathId} from '@ryanatkn/belt/path.js';
|
|
5
5
|
import {json_stringify_deterministic} from '@ryanatkn/belt/json.js';
|
|
@@ -208,7 +208,7 @@ export const load_gro_config = async (dir = paths.root): Promise<GroConfig> => {
|
|
|
208
208
|
);
|
|
209
209
|
|
|
210
210
|
const config_path = join(dir, GRO_CONFIG_FILENAME);
|
|
211
|
-
if (!
|
|
211
|
+
if (!(await fs_exists(config_path))) {
|
|
212
212
|
// No user config file found, so return the default.
|
|
213
213
|
return default_config;
|
|
214
214
|
}
|
|
@@ -48,7 +48,7 @@ export const gro_plugin_gen = ({
|
|
|
48
48
|
// Some parts of the build may have already happened,
|
|
49
49
|
// making us miss `build` events for gen dependencies,
|
|
50
50
|
// so we run a full `gen` here even if it's usually wasteful.
|
|
51
|
-
const found = find_genfiles(input_paths, root_dirs, config);
|
|
51
|
+
const found = await find_genfiles(input_paths, root_dirs, config);
|
|
52
52
|
if (found.ok && found.value.resolved_input_files.length > 0) {
|
|
53
53
|
await gen();
|
|
54
54
|
}
|
|
@@ -217,8 +217,8 @@ export const gro_plugin_server = ({
|
|
|
217
217
|
let build_result;
|
|
218
218
|
try {
|
|
219
219
|
build_result = await build_ctx!.rebuild();
|
|
220
|
-
} catch (
|
|
221
|
-
log.error('[gro_plugin_server] build failed',
|
|
220
|
+
} catch (error) {
|
|
221
|
+
log.error('[gro_plugin_server] build failed', error);
|
|
222
222
|
return;
|
|
223
223
|
}
|
|
224
224
|
const {metafile} = build_result;
|