@ryanatkn/gro 0.161.2 → 0.162.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/README.md +8 -7
- package/dist/build.task.d.ts.map +1 -1
- package/dist/build.task.js +4 -2
- package/dist/check.task.d.ts +0 -1
- package/dist/check.task.d.ts.map +1 -1
- package/dist/check.task.js +8 -8
- package/dist/child_process_logging.js +1 -1
- package/dist/constants.d.ts +10 -1
- package/dist/constants.d.ts.map +1 -1
- package/dist/constants.js +12 -3
- package/dist/dev.task.d.ts +0 -1
- package/dist/dev.task.d.ts.map +1 -1
- package/dist/dev.task.js +5 -7
- package/dist/disknode.d.ts +18 -0
- package/dist/disknode.d.ts.map +1 -0
- package/dist/disknode.js +1 -0
- package/dist/esbuild_plugin_sveltekit_shim_env.d.ts.map +1 -1
- package/dist/esbuild_plugin_sveltekit_shim_env.js +1 -2
- package/dist/filer.d.ts +6 -21
- package/dist/filer.d.ts.map +1 -1
- package/dist/filer.js +11 -6
- package/dist/format_directory.js +2 -2
- package/dist/gen.task.d.ts.map +1 -1
- package/dist/gen.task.js +56 -12
- package/dist/git.js +3 -3
- package/dist/gro.config.default.d.ts.map +1 -1
- package/dist/gro.config.default.js +2 -1
- package/dist/gro_config.d.ts +1 -1
- package/dist/gro_config.d.ts.map +1 -1
- package/dist/gro_config.js +5 -5
- package/dist/gro_plugin_gen.d.ts.map +1 -1
- package/dist/gro_plugin_gen.js +7 -6
- package/dist/gro_plugin_server.d.ts.map +1 -1
- package/dist/gro_plugin_server.js +16 -25
- package/dist/gro_plugin_sveltekit_app.d.ts.map +1 -1
- package/dist/gro_plugin_sveltekit_app.js +1 -2
- package/dist/gro_plugin_sveltekit_library.d.ts.map +1 -1
- package/dist/gro_plugin_sveltekit_library.js +2 -1
- package/dist/invoke.js +6 -4
- package/dist/package.d.ts.map +1 -1
- package/dist/package.gen.d.ts.map +1 -1
- package/dist/package.gen.js +4 -0
- package/dist/package.js +41 -30
- package/dist/package_json.d.ts +4 -3
- package/dist/package_json.d.ts.map +1 -1
- package/dist/package_json.js +12 -10
- package/dist/parse_imports.js +2 -2
- package/dist/paths.js +2 -2
- package/dist/reinstall.task.js +1 -1
- package/dist/resolve_specifier.d.ts.map +1 -1
- package/dist/src_json.d.ts.map +1 -1
- package/dist/src_json.js +3 -1
- package/dist/svelte_config.d.ts.map +1 -1
- package/dist/svelte_config.js +2 -0
- package/dist/sveltekit_helpers.d.ts +0 -7
- package/dist/sveltekit_helpers.d.ts.map +1 -1
- package/dist/sveltekit_helpers.js +1 -8
- package/dist/sync.task.d.ts +0 -1
- package/dist/sync.task.d.ts.map +1 -1
- package/dist/sync.task.js +1 -2
- package/dist/task.d.ts.map +1 -1
- package/dist/test.task.d.ts +2 -3
- package/dist/test.task.d.ts.map +1 -1
- package/dist/test.task.js +31 -30
- package/dist/test_helpers.d.ts +1 -0
- package/dist/test_helpers.d.ts.map +1 -1
- package/dist/test_helpers.js +1 -0
- package/dist/typecheck.task.d.ts.map +1 -1
- package/dist/typecheck.task.js +2 -1
- package/dist/upgrade.task.d.ts.map +1 -1
- package/dist/upgrade.task.js +2 -1
- package/package.json +34 -19
- package/src/lib/build.task.ts +3 -2
- package/src/lib/check.task.ts +7 -8
- package/src/lib/child_process_logging.ts +1 -1
- package/src/lib/constants.ts +12 -3
- package/src/lib/dev.task.ts +4 -7
- package/src/lib/disknode.ts +21 -0
- package/src/lib/esbuild_plugin_sveltekit_shim_env.ts +1 -2
- package/src/lib/filer.ts +23 -35
- package/src/lib/format_directory.ts +2 -2
- package/src/lib/gen.task.ts +83 -23
- package/src/lib/git.ts +3 -3
- package/src/lib/gro.config.default.ts +2 -1
- package/src/lib/gro_config.ts +11 -7
- package/src/lib/gro_plugin_gen.ts +8 -6
- package/src/lib/gro_plugin_server.ts +18 -24
- package/src/lib/gro_plugin_sveltekit_app.ts +1 -2
- package/src/lib/gro_plugin_sveltekit_library.ts +2 -5
- package/src/lib/invoke.ts +6 -4
- package/src/lib/package.gen.ts +4 -0
- package/src/lib/package.ts +41 -30
- package/src/lib/package_json.ts +11 -9
- package/src/lib/parse_imports.ts +2 -2
- package/src/lib/paths.ts +2 -2
- package/src/lib/reinstall.task.ts +1 -1
- package/src/lib/resolve_specifier.ts +2 -0
- package/src/lib/src_json.ts +3 -1
- package/src/lib/svelte_config.ts +3 -0
- package/src/lib/sveltekit_helpers.ts +7 -13
- package/src/lib/sync.task.ts +1 -2
- package/src/lib/task.ts +0 -2
- package/src/lib/test.task.ts +31 -32
- package/src/lib/test_helpers.ts +2 -0
- package/src/lib/typecheck.task.ts +2 -1
- package/src/lib/upgrade.task.ts +2 -1
package/src/lib/filer.ts
CHANGED
|
@@ -21,31 +21,15 @@ import {paths} from './paths.ts';
|
|
|
21
21
|
import {parse_imports} from './parse_imports.ts';
|
|
22
22
|
import {resolve_specifier} from './resolve_specifier.ts';
|
|
23
23
|
import {default_svelte_config} from './svelte_config.ts';
|
|
24
|
-
import {map_sveltekit_aliases
|
|
24
|
+
import {map_sveltekit_aliases} from './sveltekit_helpers.ts';
|
|
25
|
+
import {SVELTEKIT_GLOBAL_SPECIFIER} from './constants.ts';
|
|
26
|
+
import type {Disknode} from './disknode.ts';
|
|
25
27
|
|
|
26
28
|
const aliases = Object.entries(default_svelte_config.alias);
|
|
27
29
|
|
|
28
|
-
export interface Source_File {
|
|
29
|
-
id: Path_Id;
|
|
30
|
-
// TODO figure out the best API that makes this lazy
|
|
31
|
-
/**
|
|
32
|
-
* `null` contents means it doesn't exist.
|
|
33
|
-
* We create the file in memory to track its dependents regardless of its existence on disk.
|
|
34
|
-
*/
|
|
35
|
-
contents: string | null;
|
|
36
|
-
/**
|
|
37
|
-
* Is the source file outside of the `root_dir` or excluded by `watch_dir_options.filter`?
|
|
38
|
-
*/
|
|
39
|
-
external: boolean;
|
|
40
|
-
ctime: number | null;
|
|
41
|
-
mtime: number | null;
|
|
42
|
-
dependents: Map<Path_Id, Source_File>;
|
|
43
|
-
dependencies: Map<Path_Id, Source_File>;
|
|
44
|
-
}
|
|
45
|
-
|
|
46
30
|
export type Cleanup_Watch = () => Promise<void>;
|
|
47
31
|
|
|
48
|
-
export type On_Filer_Change = (change: Watcher_Change, source_file:
|
|
32
|
+
export type On_Filer_Change = (change: Watcher_Change, source_file: Disknode) => void;
|
|
49
33
|
|
|
50
34
|
export interface Filer_Options {
|
|
51
35
|
watch_dir?: typeof watch_dir;
|
|
@@ -57,7 +41,7 @@ export interface Filer_Options {
|
|
|
57
41
|
export class Filer {
|
|
58
42
|
readonly root_dir: Path_Id;
|
|
59
43
|
|
|
60
|
-
readonly files: Map<Path_Id,
|
|
44
|
+
readonly files: Map<Path_Id, Disknode> = new Map();
|
|
61
45
|
|
|
62
46
|
#watch_dir: typeof watch_dir;
|
|
63
47
|
#watch_dir_options: Partial<Watch_Dir_Options>;
|
|
@@ -68,6 +52,10 @@ export class Filer {
|
|
|
68
52
|
this.#watch_dir = options.watch_dir ?? watch_dir;
|
|
69
53
|
this.#watch_dir_options = options.watch_dir_options ?? EMPTY_OBJECT;
|
|
70
54
|
this.root_dir = resolve(options.watch_dir_options?.dir ?? paths.source);
|
|
55
|
+
// TODO for package.json maybe another array of files/dirs to watch to invalidate everything?
|
|
56
|
+
// or instead of that, think of taking an array of config objects that can specify invalidation rules,
|
|
57
|
+
// so package.json would be configured differently than ./src, and we could add a default with
|
|
58
|
+
// package.json/gro.config.ts/tsconfig.json/svelte.config.js/vite.config.ts to invalidate everything
|
|
71
59
|
this.#log = options.log;
|
|
72
60
|
}
|
|
73
61
|
|
|
@@ -76,14 +64,14 @@ export class Filer {
|
|
|
76
64
|
|
|
77
65
|
#ready = false;
|
|
78
66
|
|
|
79
|
-
get_by_id = (id: Path_Id):
|
|
67
|
+
get_by_id = (id: Path_Id): Disknode | undefined => {
|
|
80
68
|
return this.files.get(id);
|
|
81
69
|
};
|
|
82
70
|
|
|
83
|
-
get_or_create = (id: Path_Id):
|
|
71
|
+
get_or_create = (id: Path_Id): Disknode => {
|
|
84
72
|
const existing = this.get_by_id(id);
|
|
85
73
|
if (existing) return existing;
|
|
86
|
-
const file:
|
|
74
|
+
const file: Disknode = {
|
|
87
75
|
id,
|
|
88
76
|
contents: null,
|
|
89
77
|
external: this.#is_external(id), // TODO maybe filter externals by default? the user needs to configure the filer then
|
|
@@ -100,14 +88,14 @@ export class Filer {
|
|
|
100
88
|
return file;
|
|
101
89
|
};
|
|
102
90
|
|
|
103
|
-
#update(id: Path_Id):
|
|
91
|
+
#update(id: Path_Id): Disknode | null {
|
|
104
92
|
const file = this.get_or_create(id);
|
|
105
93
|
|
|
106
94
|
const stats = existsSync(id) ? statSync(id) : null;
|
|
107
95
|
file.ctime = stats?.ctimeMs ?? null;
|
|
108
96
|
file.mtime = stats?.mtimeMs ?? null;
|
|
109
97
|
|
|
110
|
-
const new_contents = stats ? readFileSync(id, 'utf8') : null; // TODO need to lazily load contents, probably turn `
|
|
98
|
+
const new_contents = stats ? readFileSync(id, 'utf8') : null; // TODO need to lazily load contents, probably turn `Disknode` into a class
|
|
111
99
|
|
|
112
100
|
if (file.contents === new_contents) {
|
|
113
101
|
return null;
|
|
@@ -159,7 +147,7 @@ export class Filer {
|
|
|
159
147
|
return file;
|
|
160
148
|
}
|
|
161
149
|
|
|
162
|
-
#remove(id: Path_Id):
|
|
150
|
+
#remove(id: Path_Id): Disknode | null {
|
|
163
151
|
const file = this.get_by_id(id);
|
|
164
152
|
if (!file) return null; // this is safe because the object would exist if any other file referenced it as a dependency or dependent
|
|
165
153
|
|
|
@@ -177,14 +165,14 @@ export class Filer {
|
|
|
177
165
|
return file;
|
|
178
166
|
}
|
|
179
167
|
|
|
180
|
-
#
|
|
168
|
+
#sync_listener_with_files(listener: On_Filer_Change): void {
|
|
181
169
|
if (!this.#ready) return;
|
|
182
170
|
for (const source_file of this.files.values()) {
|
|
183
171
|
listener({type: 'add', path: source_file.id, is_directory: false}, source_file);
|
|
184
172
|
}
|
|
185
173
|
}
|
|
186
174
|
|
|
187
|
-
#notify_change(change: Watcher_Change, source_file:
|
|
175
|
+
#notify_change(change: Watcher_Change, source_file: Disknode): void {
|
|
188
176
|
if (!this.#ready) return;
|
|
189
177
|
for (const listener of this.#listeners) {
|
|
190
178
|
listener(change, source_file);
|
|
@@ -197,7 +185,7 @@ export class Filer {
|
|
|
197
185
|
// if already watching, call the listener for all existing files after init
|
|
198
186
|
await this.#watching.init();
|
|
199
187
|
await wait(); // wait a tick to ensure the `this.#ready` value is updated below first
|
|
200
|
-
this.#
|
|
188
|
+
this.#sync_listener_with_files(listener);
|
|
201
189
|
return;
|
|
202
190
|
}
|
|
203
191
|
this.#watching = this.#watch_dir({
|
|
@@ -207,7 +195,7 @@ export class Filer {
|
|
|
207
195
|
});
|
|
208
196
|
await this.#watching.init();
|
|
209
197
|
this.#ready = true;
|
|
210
|
-
this.#
|
|
198
|
+
this.#sync_listener_with_files(listener);
|
|
211
199
|
}
|
|
212
200
|
|
|
213
201
|
async #remove_listener(listener: On_Filer_Change): Promise<void> {
|
|
@@ -219,7 +207,7 @@ export class Filer {
|
|
|
219
207
|
|
|
220
208
|
#on_change: Watcher_Change_Callback = (change) => {
|
|
221
209
|
if (change.is_directory) return; // TODO manage directories?
|
|
222
|
-
let source_file:
|
|
210
|
+
let source_file: Disknode | null;
|
|
223
211
|
switch (change.type) {
|
|
224
212
|
case 'add':
|
|
225
213
|
case 'update': {
|
|
@@ -258,10 +246,10 @@ export class Filer {
|
|
|
258
246
|
}
|
|
259
247
|
}
|
|
260
248
|
|
|
261
|
-
// TODO maybe `
|
|
249
|
+
// TODO maybe `Disknode` class?
|
|
262
250
|
export const filter_dependents = (
|
|
263
|
-
source_file:
|
|
264
|
-
get_by_id: (id: Path_Id) =>
|
|
251
|
+
source_file: Disknode,
|
|
252
|
+
get_by_id: (id: Path_Id) => Disknode | undefined,
|
|
265
253
|
filter?: File_Filter,
|
|
266
254
|
results: Set<string> = new Set(),
|
|
267
255
|
searched: Set<string> = new Set(),
|
|
@@ -8,7 +8,7 @@ import {
|
|
|
8
8
|
SVELTE_CONFIG_FILENAME,
|
|
9
9
|
VITE_CONFIG_FILENAME,
|
|
10
10
|
TSCONFIG_FILENAME,
|
|
11
|
-
|
|
11
|
+
GRO_CONFIG_FILENAME,
|
|
12
12
|
PM_CLI_DEFAULT,
|
|
13
13
|
PRETTIER_CLI_DEFAULT,
|
|
14
14
|
} from './constants.ts';
|
|
@@ -18,7 +18,7 @@ import {spawn_cli, to_cli_name, type Cli} from './cli.ts';
|
|
|
18
18
|
const EXTENSIONS_DEFAULT = 'ts,js,json,svelte,html,css,md,yml';
|
|
19
19
|
const ROOT_PATHS_DEFAULT = `${[
|
|
20
20
|
README_FILENAME,
|
|
21
|
-
|
|
21
|
+
GRO_CONFIG_FILENAME,
|
|
22
22
|
SVELTE_CONFIG_FILENAME,
|
|
23
23
|
VITE_CONFIG_FILENAME,
|
|
24
24
|
TSCONFIG_FILENAME,
|
package/src/lib/gen.task.ts
CHANGED
|
@@ -9,7 +9,14 @@ import {Raw_Input_Path, to_input_paths} from './input_path.ts';
|
|
|
9
9
|
import {format_file} from './format_file.ts';
|
|
10
10
|
import {print_path} from './paths.ts';
|
|
11
11
|
import {log_error_reasons} from './task_logging.ts';
|
|
12
|
-
import {
|
|
12
|
+
import {
|
|
13
|
+
write_gen_results,
|
|
14
|
+
analyze_gen_results,
|
|
15
|
+
find_genfiles,
|
|
16
|
+
load_genfiles,
|
|
17
|
+
type Analyzed_Gen_Result,
|
|
18
|
+
type Gen_Results,
|
|
19
|
+
} from './gen.ts';
|
|
13
20
|
import {SOURCE_DIRNAME} from './constants.ts';
|
|
14
21
|
|
|
15
22
|
export const Args = z.strictObject({
|
|
@@ -106,31 +113,16 @@ export const task: Task<Args> = {
|
|
|
106
113
|
timing_to_output_results();
|
|
107
114
|
}
|
|
108
115
|
|
|
109
|
-
//
|
|
116
|
+
// collect and format output with summary
|
|
117
|
+
const output_lines = collect_output_lines(gen_results, analyzed_gen_results);
|
|
110
118
|
const new_count = analyzed_gen_results.filter((r) => r.is_new).length;
|
|
111
|
-
const changed_count = analyzed_gen_results.filter((r) => r.has_changed).length;
|
|
119
|
+
const changed_count = analyzed_gen_results.filter((r) => r.has_changed && !r.is_new).length;
|
|
112
120
|
const unchanged_count = analyzed_gen_results.filter((r) => !r.is_new && !r.has_changed).length;
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
log_result += `\n\t${changed_count} ` + st('gray', 'changed');
|
|
116
|
-
log_result += `\n\t${unchanged_count} ` + st('gray', 'unchanged');
|
|
117
|
-
for (const result of gen_results.results) {
|
|
118
|
-
log_result += `\n\t${result.ok ? st('green', '✓') : st('red', '🞩')} ${
|
|
119
|
-
result.ok ? result.files.length : 0
|
|
120
|
-
} ${st('gray', 'in')} ${print_ms(result.elapsed)} ${st('gray', '←')} ${print_path(
|
|
121
|
-
result.id,
|
|
122
|
-
)} ${st('gray', '→')} ${
|
|
123
|
-
result.ok ? result.files.map((f) => print_path(f.id)).join(', ') : print_error(result.error)
|
|
124
|
-
}`;
|
|
125
|
-
}
|
|
126
|
-
log.info(log_result);
|
|
121
|
+
const error_count = gen_results.failures.length;
|
|
122
|
+
|
|
127
123
|
log.info(
|
|
128
|
-
|
|
129
|
-
'green',
|
|
130
|
-
`generated ${gen_results.output_count} file${plural(gen_results.output_count)} from ${
|
|
131
|
-
gen_results.successes.length
|
|
132
|
-
} input file${plural(gen_results.successes.length)}`,
|
|
133
|
-
),
|
|
124
|
+
format_gen_output(output_lines) +
|
|
125
|
+
`\n\n\t${new_count} ${st(new_count > 0 ? 'green' : 'gray', 'new')}, ${changed_count} ${st(changed_count > 0 ? 'cyan' : 'gray', 'changed')}, ${unchanged_count} ${st('gray', 'unchanged')}${error_count ? `, ${error_count} ${st('red', 'error' + plural(error_count))}` : ''} from ${gen_results.input_count} input file${plural(gen_results.input_count)}`,
|
|
134
126
|
);
|
|
135
127
|
|
|
136
128
|
if (fail_count) {
|
|
@@ -141,3 +133,71 @@ export const task: Task<Args> = {
|
|
|
141
133
|
}
|
|
142
134
|
},
|
|
143
135
|
};
|
|
136
|
+
|
|
137
|
+
interface Gen_Status {
|
|
138
|
+
symbol: string;
|
|
139
|
+
color: Parameters<typeof st>[0];
|
|
140
|
+
text: string;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
const format_gen_status = (analyzed: Analyzed_Gen_Result | undefined): Gen_Status => {
|
|
144
|
+
if (!analyzed) return {symbol: '?', color: 'gray', text: 'unknown'};
|
|
145
|
+
if (analyzed.is_new) return {symbol: '●', color: 'green', text: 'new'};
|
|
146
|
+
if (analyzed.has_changed) return {symbol: '◐', color: 'cyan', text: 'changed'};
|
|
147
|
+
return {symbol: '○', color: 'gray', text: 'unchanged'};
|
|
148
|
+
};
|
|
149
|
+
|
|
150
|
+
interface Output_Line {
|
|
151
|
+
status: Gen_Status;
|
|
152
|
+
elapsed: string;
|
|
153
|
+
source: string;
|
|
154
|
+
target: string;
|
|
155
|
+
is_error: boolean;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
const collect_output_lines = (
|
|
159
|
+
gen_results: Gen_Results,
|
|
160
|
+
analyzed_gen_results: Array<Analyzed_Gen_Result>,
|
|
161
|
+
): Array<Output_Line> => {
|
|
162
|
+
const output_lines: Array<Output_Line> = [];
|
|
163
|
+
|
|
164
|
+
for (const result of gen_results.results) {
|
|
165
|
+
if (result.ok) {
|
|
166
|
+
for (const file of result.files) {
|
|
167
|
+
const analyzed = analyzed_gen_results.find((a) => a.file.id === file.id);
|
|
168
|
+
output_lines.push({
|
|
169
|
+
status: format_gen_status(analyzed),
|
|
170
|
+
elapsed: print_ms(result.elapsed),
|
|
171
|
+
source: print_path(result.id),
|
|
172
|
+
target: print_path(file.id),
|
|
173
|
+
is_error: false,
|
|
174
|
+
});
|
|
175
|
+
}
|
|
176
|
+
} else {
|
|
177
|
+
output_lines.push({
|
|
178
|
+
status: {symbol: '🞩', color: 'red', text: 'error'},
|
|
179
|
+
elapsed: print_ms(result.elapsed),
|
|
180
|
+
source: print_path(result.id),
|
|
181
|
+
target: st('red', result.error.stack || result.error.message || 'error'),
|
|
182
|
+
is_error: true,
|
|
183
|
+
});
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
return output_lines;
|
|
188
|
+
};
|
|
189
|
+
|
|
190
|
+
const format_gen_output = (output_lines: Array<Output_Line>): string => {
|
|
191
|
+
// calculate column widths for alignment
|
|
192
|
+
const max_elapsed_length = Math.max(...output_lines.map((l) => l.elapsed.length));
|
|
193
|
+
const max_source_length = Math.max(...output_lines.map((l) => l.source.length));
|
|
194
|
+
|
|
195
|
+
// format the output lines
|
|
196
|
+
let log_result = 'gen results:';
|
|
197
|
+
for (const line of output_lines) {
|
|
198
|
+
const elapsed_text = line.elapsed.padStart(max_elapsed_length);
|
|
199
|
+
const source_text = line.source.padEnd(max_source_length);
|
|
200
|
+
log_result += `\n\t${st(line.status.color, line.status.symbol)} ${elapsed_text} ${source_text} → ${line.target}`;
|
|
201
|
+
}
|
|
202
|
+
return log_result;
|
|
203
|
+
};
|
package/src/lib/git.ts
CHANGED
|
@@ -20,7 +20,7 @@ export type Git_Branch = Flavored<string, 'Git_Branch'>;
|
|
|
20
20
|
export const git_current_branch_name = async (options?: SpawnOptions): Promise<Git_Branch> => {
|
|
21
21
|
const {stdout} = await spawn_out('git', ['rev-parse', '--abbrev-ref', 'HEAD'], options);
|
|
22
22
|
if (!stdout) throw Error('git_current_branch_name failed');
|
|
23
|
-
const branch_name = stdout.
|
|
23
|
+
const branch_name = stdout.trim() as Git_Branch;
|
|
24
24
|
return branch_name;
|
|
25
25
|
};
|
|
26
26
|
|
|
@@ -251,7 +251,7 @@ export const git_current_commit_hash = async (
|
|
|
251
251
|
const final_branch = branch ?? (await git_current_branch_name(options));
|
|
252
252
|
const {stdout} = await spawn_out('git', ['show-ref', '-s', final_branch], options);
|
|
253
253
|
if (!stdout) return null; // TODO hack for CI
|
|
254
|
-
return stdout.
|
|
254
|
+
return stdout.split('\n')[0].trim();
|
|
255
255
|
};
|
|
256
256
|
|
|
257
257
|
/**
|
|
@@ -266,7 +266,7 @@ export const git_current_branch_first_commit_hash = async (
|
|
|
266
266
|
options,
|
|
267
267
|
);
|
|
268
268
|
if (!stdout) throw Error('git_current_branch_first_commit_hash failed');
|
|
269
|
-
return stdout.
|
|
269
|
+
return stdout.trim();
|
|
270
270
|
};
|
|
271
271
|
|
|
272
272
|
/**
|
|
@@ -33,7 +33,8 @@ const config: Create_Gro_Config = async (cfg, svelte_config) => {
|
|
|
33
33
|
|
|
34
34
|
const local_moss_plugin_path = find_first_existing_file([
|
|
35
35
|
'./src/lib/gro_plugin_moss.ts',
|
|
36
|
-
'./src/
|
|
36
|
+
'./src/gro_plugin_moss.ts',
|
|
37
|
+
'./src/routes/gro_plugin_moss.ts', // TODO probably remove this
|
|
37
38
|
]);
|
|
38
39
|
|
|
39
40
|
// put things that generate files before SvelteKit so it can see them
|
package/src/lib/gro_config.ts
CHANGED
|
@@ -3,7 +3,7 @@ import {existsSync} from 'node:fs';
|
|
|
3
3
|
|
|
4
4
|
import {GRO_DIST_DIR, IS_THIS_GRO, paths} from './paths.ts';
|
|
5
5
|
import {
|
|
6
|
-
|
|
6
|
+
GRO_CONFIG_FILENAME,
|
|
7
7
|
JS_CLI_DEFAULT,
|
|
8
8
|
NODE_MODULES_DIRNAME,
|
|
9
9
|
PM_CLI_DEFAULT,
|
|
@@ -121,8 +121,9 @@ export const EXPORTS_EXCLUDER_DEFAULT = /(\.md|\.(test|ignore)\.|\/(test|fixture
|
|
|
121
121
|
* Transforms a `Raw_Gro_Config` to the more strict `Gro_Config`.
|
|
122
122
|
* This allows users to provide a more relaxed config.
|
|
123
123
|
*/
|
|
124
|
-
export const
|
|
124
|
+
export const cook_gro_config = (raw_config: Raw_Gro_Config): Gro_Config => {
|
|
125
125
|
const empty_config = create_empty_gro_config();
|
|
126
|
+
|
|
126
127
|
// All of the raw config properties are optional,
|
|
127
128
|
// so fall back to the empty values when `undefined`.
|
|
128
129
|
const {
|
|
@@ -133,6 +134,7 @@ export const normalize_gro_config = (raw_config: Raw_Gro_Config): Gro_Config =>
|
|
|
133
134
|
js_cli = empty_config.js_cli,
|
|
134
135
|
pm_cli = empty_config.pm_cli,
|
|
135
136
|
} = raw_config;
|
|
137
|
+
|
|
136
138
|
return {
|
|
137
139
|
plugins,
|
|
138
140
|
map_package_json,
|
|
@@ -152,18 +154,20 @@ export interface Gro_Config_Module {
|
|
|
152
154
|
}
|
|
153
155
|
|
|
154
156
|
export const load_gro_config = async (dir = paths.root): Promise<Gro_Config> => {
|
|
155
|
-
const default_config =
|
|
156
|
-
|
|
157
|
-
);
|
|
158
|
-
const config_path = join(dir, GRO_CONFIG_PATH);
|
|
157
|
+
const default_config = cook_gro_config(await create_default_config(create_empty_gro_config()));
|
|
158
|
+
|
|
159
|
+
const config_path = join(dir, GRO_CONFIG_FILENAME);
|
|
159
160
|
if (!existsSync(config_path)) {
|
|
160
161
|
// No user config file found, so return the default.
|
|
161
162
|
return default_config;
|
|
162
163
|
}
|
|
164
|
+
|
|
163
165
|
// Import the user's `gro.config.ts`.
|
|
164
166
|
const config_module = await import(config_path);
|
|
167
|
+
|
|
165
168
|
validate_gro_config_module(config_module, config_path);
|
|
166
|
-
|
|
169
|
+
|
|
170
|
+
return cook_gro_config(
|
|
167
171
|
typeof config_module.default === 'function'
|
|
168
172
|
? await config_module.default(default_config)
|
|
169
173
|
: config_module.default,
|
|
@@ -45,9 +45,11 @@ export const gro_plugin_gen = ({
|
|
|
45
45
|
},
|
|
46
46
|
{delay: flush_debounce_delay},
|
|
47
47
|
);
|
|
48
|
+
|
|
49
|
+
// TODO do this in-process - will it cause caching issues with the current impl?
|
|
48
50
|
const gen = (files: Array<string> = []) => spawn_cli('gro', ['gen', ...files]);
|
|
49
51
|
|
|
50
|
-
let
|
|
52
|
+
let cleanup_watch: Cleanup_Watch | undefined;
|
|
51
53
|
|
|
52
54
|
return {
|
|
53
55
|
name: 'gro_plugin_gen',
|
|
@@ -73,7 +75,7 @@ export const gro_plugin_gen = ({
|
|
|
73
75
|
|
|
74
76
|
// When a file builds, check it and its tree of dependents
|
|
75
77
|
// for any `.gen.` files that need to run.
|
|
76
|
-
|
|
78
|
+
cleanup_watch = await filer.watch((change, source_file) => {
|
|
77
79
|
if (source_file.external) return;
|
|
78
80
|
switch (change.type) {
|
|
79
81
|
case 'add':
|
|
@@ -95,7 +97,7 @@ export const gro_plugin_gen = ({
|
|
|
95
97
|
break;
|
|
96
98
|
}
|
|
97
99
|
case 'delete': {
|
|
98
|
-
// TODO delete the generated file(s)? option?
|
|
100
|
+
// TODO delete the generated file(s)? option? because it may be surprising
|
|
99
101
|
break;
|
|
100
102
|
}
|
|
101
103
|
default:
|
|
@@ -104,9 +106,9 @@ export const gro_plugin_gen = ({
|
|
|
104
106
|
});
|
|
105
107
|
},
|
|
106
108
|
teardown: async () => {
|
|
107
|
-
if (
|
|
108
|
-
await
|
|
109
|
-
|
|
109
|
+
if (cleanup_watch) {
|
|
110
|
+
await cleanup_watch();
|
|
111
|
+
cleanup_watch = undefined;
|
|
110
112
|
}
|
|
111
113
|
},
|
|
112
114
|
};
|
|
@@ -12,7 +12,6 @@ import type {Plugin} from './plugin.ts';
|
|
|
12
12
|
import {base_path_to_path_id, LIB_DIRNAME, paths} from './paths.ts';
|
|
13
13
|
import type {Path_Id} from './path.ts';
|
|
14
14
|
import {GRO_DEV_DIRNAME, SERVER_DIST_PATH} from './constants.ts';
|
|
15
|
-
import {watch_dir, type Watch_Node_Fs} from './watch_dir.ts';
|
|
16
15
|
import {parse_svelte_config, default_svelte_config} from './svelte_config.ts';
|
|
17
16
|
import {esbuild_plugin_sveltekit_shim_app} from './esbuild_plugin_sveltekit_shim_app.ts';
|
|
18
17
|
import {esbuild_plugin_sveltekit_shim_env} from './esbuild_plugin_sveltekit_shim_env.ts';
|
|
@@ -122,14 +121,14 @@ export const gro_plugin_server = ({
|
|
|
122
121
|
cli_command,
|
|
123
122
|
run, // `dev` default is not available in this scope
|
|
124
123
|
}: Gro_Plugin_Server_Options = {}): Plugin => {
|
|
125
|
-
let build_ctx: esbuild.BuildContext |
|
|
126
|
-
let
|
|
127
|
-
let server_process: Restartable_Process |
|
|
128
|
-
let deps: Set<Path_Id> |
|
|
124
|
+
let build_ctx: esbuild.BuildContext | undefined;
|
|
125
|
+
let cleanup_watch: (() => void) | undefined;
|
|
126
|
+
let server_process: Restartable_Process | undefined;
|
|
127
|
+
let deps: Set<Path_Id> | undefined;
|
|
129
128
|
|
|
130
129
|
return {
|
|
131
130
|
name: 'gro_plugin_server',
|
|
132
|
-
setup: async ({dev, watch, timings, log, config}) => {
|
|
131
|
+
setup: async ({dev, watch, timings, log, config, filer}) => {
|
|
133
132
|
const parsed_svelte_config =
|
|
134
133
|
!svelte_config && strip_end(dir, '/') === process.cwd()
|
|
135
134
|
? default_svelte_config
|
|
@@ -231,22 +230,13 @@ export const gro_plugin_server = ({
|
|
|
231
230
|
|
|
232
231
|
await rebuild();
|
|
233
232
|
|
|
234
|
-
// uses chokidar instead of esbuild's watcher for efficiency
|
|
235
233
|
if (watch) {
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
watcher = watch_dir({
|
|
242
|
-
dir: paths.lib,
|
|
243
|
-
on_change: (change) => {
|
|
244
|
-
if (!watcher_ready || !deps?.has(change.path)) return;
|
|
245
|
-
void rebuild();
|
|
246
|
-
},
|
|
234
|
+
cleanup_watch = await filer.watch((change) => {
|
|
235
|
+
if (!deps?.has(change.path)) {
|
|
236
|
+
return;
|
|
237
|
+
}
|
|
238
|
+
void rebuild();
|
|
247
239
|
});
|
|
248
|
-
await watcher.init();
|
|
249
|
-
watcher_ready = true;
|
|
250
240
|
}
|
|
251
241
|
|
|
252
242
|
if (!existsSync(server_outpath)) {
|
|
@@ -263,16 +253,20 @@ export const gro_plugin_server = ({
|
|
|
263
253
|
}
|
|
264
254
|
},
|
|
265
255
|
teardown: async () => {
|
|
256
|
+
if (cleanup_watch) {
|
|
257
|
+
cleanup_watch();
|
|
258
|
+
cleanup_watch = undefined;
|
|
259
|
+
}
|
|
260
|
+
|
|
266
261
|
if (server_process) {
|
|
267
262
|
const s = server_process; // avoid possible issue where a build is in progress, don't want to issue a restart, could be fixed upstream in `spawn_restartable_process`
|
|
268
|
-
server_process =
|
|
263
|
+
server_process = undefined;
|
|
269
264
|
await s.kill();
|
|
270
265
|
}
|
|
271
|
-
|
|
272
|
-
await watcher.close();
|
|
273
|
-
}
|
|
266
|
+
|
|
274
267
|
if (build_ctx) {
|
|
275
268
|
await build_ctx.dispose();
|
|
269
|
+
build_ctx = undefined;
|
|
276
270
|
}
|
|
277
271
|
},
|
|
278
272
|
};
|
|
@@ -10,8 +10,7 @@ import {find_cli, spawn_cli, spawn_cli_process} from './cli.ts';
|
|
|
10
10
|
import {type Map_Src_Json, serialize_src_json, create_src_json} from './src_json.ts';
|
|
11
11
|
import {EXPORTS_EXCLUDER_DEFAULT} from './gro_config.ts';
|
|
12
12
|
import {default_svelte_config} from './svelte_config.ts';
|
|
13
|
-
import {SOURCE_DIRNAME} from './constants.ts';
|
|
14
|
-
import {VITE_CLI} from './sveltekit_helpers.ts';
|
|
13
|
+
import {SOURCE_DIRNAME, VITE_CLI} from './constants.ts';
|
|
15
14
|
|
|
16
15
|
export interface Gro_Plugin_Sveltekit_App_Options {
|
|
17
16
|
/**
|
|
@@ -3,11 +3,8 @@ import {print_spawn_result, spawn} from '@ryanatkn/belt/process.js';
|
|
|
3
3
|
import type {Plugin} from './plugin.ts';
|
|
4
4
|
import {Task_Error} from './task.ts';
|
|
5
5
|
import {load_package_json} from './package_json.ts';
|
|
6
|
-
import {
|
|
7
|
-
|
|
8
|
-
run_svelte_package,
|
|
9
|
-
type Svelte_Package_Options,
|
|
10
|
-
} from './sveltekit_helpers.ts';
|
|
6
|
+
import {run_svelte_package, type Svelte_Package_Options} from './sveltekit_helpers.ts';
|
|
7
|
+
import {SVELTE_PACKAGE_CLI} from './constants.ts';
|
|
11
8
|
|
|
12
9
|
export interface Gro_Plugin_Sveltekit_Library_Options {
|
|
13
10
|
/**
|
package/src/lib/invoke.ts
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import {attach_process_error_handlers} from '@ryanatkn/belt/process.js';
|
|
2
2
|
import {configure_log_colors} from '@ryanatkn/belt/log.js';
|
|
3
|
-
import {
|
|
4
|
-
import {styleText} from 'node:util';
|
|
3
|
+
import {configure_print_colors} from '@ryanatkn/belt/print.js';
|
|
5
4
|
|
|
6
5
|
import {invoke_task} from './invoke_task.ts';
|
|
7
6
|
import {to_task_args} from './args.ts';
|
|
@@ -25,8 +24,11 @@ attach_process_error_handlers(
|
|
|
25
24
|
(err) => (err.constructor.name === 'Silent_Error' ? '' : null),
|
|
26
25
|
);
|
|
27
26
|
|
|
28
|
-
|
|
29
|
-
|
|
27
|
+
if (!process.env.NO_COLOR) {
|
|
28
|
+
const {styleText} = await import('node:util');
|
|
29
|
+
configure_log_colors(styleText);
|
|
30
|
+
configure_print_colors(styleText);
|
|
31
|
+
}
|
|
30
32
|
|
|
31
33
|
await sveltekit_sync_if_obviously_needed();
|
|
32
34
|
|
package/src/lib/package.gen.ts
CHANGED
|
@@ -13,6 +13,10 @@ import type {Gen} from './gen.ts';
|
|
|
13
13
|
*/
|
|
14
14
|
export const gen: Gen = ({origin_path}) => {
|
|
15
15
|
const package_json = load_package_json();
|
|
16
|
+
// TODO this can error with bad json data, do we need to update the package.json here first?
|
|
17
|
+
// similarly do this in other places too?
|
|
18
|
+
// part of the problem is that the package_json actually has a dependency on each of its exports and their deps,
|
|
19
|
+
// but only when they're added/removed right? see gro_plugin_gen for details
|
|
16
20
|
const src_json = create_src_json(package_json, undefined);
|
|
17
21
|
|
|
18
22
|
const is_this_belt = package_json.name === '@ryanatkn/belt'; // TODO more robust?
|