@ryanatkn/gro 0.119.1 → 0.120.1
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 +3 -2
- package/dist/changelog.d.ts +1 -0
- package/dist/changeset.task.js +4 -5
- package/dist/clean_fs.d.ts +1 -2
- package/dist/clean_fs.js +3 -2
- package/dist/cli.d.ts +1 -2
- package/dist/cli.js +2 -2
- package/dist/config.d.ts +14 -1
- package/dist/config.js +17 -5
- package/dist/config.test.js +50 -1
- package/dist/deploy.task.js +8 -7
- package/dist/docs/README.gen.md.js +3 -3
- package/dist/docs/config.md +22 -6
- package/dist/docs/gen.md +2 -1
- package/dist/docs/task.md +4 -3
- package/dist/docs/tasks.gen.md.js +17 -10
- package/dist/docs/tasks.md +1 -0
- package/dist/env.d.ts +2 -3
- package/dist/env.js +2 -2
- package/dist/esbuild_helpers.d.ts +2 -1
- package/dist/esbuild_plugin_external_worker.d.ts +0 -1
- package/dist/esbuild_plugin_external_worker.js +7 -7
- package/dist/esbuild_plugin_svelte.d.ts +0 -1
- package/dist/esbuild_plugin_sveltekit_local_imports.js +2 -2
- package/dist/format_directory.d.ts +1 -0
- package/dist/fs.d.ts +1 -3
- package/dist/fs.js +3 -13
- package/dist/fs.test.js +5 -5
- package/dist/gen.d.ts +66 -13
- package/dist/gen.js +78 -9
- package/dist/gen.task.d.ts +3 -0
- package/dist/gen.task.js +24 -19
- package/dist/gen.test.js +48 -35
- package/dist/git.d.ts +0 -1
- package/dist/git.js +3 -3
- package/dist/gro_helpers.js +4 -4
- package/dist/gro_plugin_gen.js +7 -7
- package/dist/gro_plugin_server.d.ts +2 -2
- package/dist/gro_plugin_server.js +5 -5
- package/dist/gro_plugin_sveltekit_app.js +8 -8
- package/dist/hash.d.ts +1 -2
- package/dist/input_path.d.ts +34 -16
- package/dist/input_path.js +119 -67
- package/dist/input_path.test.js +150 -46
- package/dist/invoke_task.d.ts +2 -1
- package/dist/invoke_task.js +33 -79
- package/dist/loader.d.ts +0 -1
- package/dist/loader.js +4 -4
- package/dist/modules.d.ts +17 -36
- package/dist/modules.js +29 -68
- package/dist/modules.test.js +19 -143
- package/dist/package.d.ts +11 -20
- package/dist/package.js +61 -64
- package/dist/package_json.d.ts +1 -0
- package/dist/package_json.js +1 -1
- package/dist/package_meta.d.ts +1 -1
- package/dist/path.d.ts +12 -8
- package/dist/path.js +0 -6
- package/dist/paths.d.ts +7 -12
- package/dist/paths.js +11 -34
- package/dist/paths.test.js +17 -15
- package/dist/plugin.d.ts +1 -1
- package/dist/publish.task.js +3 -3
- package/dist/resolve.task.d.ts +11 -0
- package/dist/resolve.task.js +24 -0
- package/dist/resolve_node_specifier.d.ts +2 -2
- package/dist/resolve_node_specifier.js +3 -3
- package/dist/resolve_specifier.d.ts +2 -1
- package/dist/resolve_specifier.js +16 -16
- package/dist/resolve_specifier.test.js +9 -9
- package/dist/run.task.js +2 -2
- package/dist/run_gen.d.ts +4 -4
- package/dist/run_gen.js +9 -16
- package/dist/run_gen.test.js +10 -15
- package/dist/run_task.d.ts +2 -1
- package/dist/run_task.js +2 -0
- package/dist/search_fs.d.ts +20 -7
- package/dist/search_fs.js +40 -18
- package/dist/search_fs.test.js +9 -11
- package/dist/src_json.js +2 -2
- package/dist/sveltekit_config.d.ts +0 -1
- package/dist/sveltekit_helpers.js +4 -4
- package/dist/sveltekit_shim_app.d.ts +1 -1
- package/dist/sveltekit_shim_app_forms.d.ts +0 -1
- package/dist/sveltekit_shim_app_navigation.d.ts +0 -1
- package/dist/sveltekit_shim_app_paths.d.ts +0 -1
- package/dist/sveltekit_shim_app_stores.d.ts +0 -1
- package/dist/sveltekit_shim_env.d.ts +1 -1
- package/dist/task.d.ts +65 -1
- package/dist/task.js +86 -13
- package/dist/task.test.js +25 -11
- package/dist/task_logging.d.ts +3 -6
- package/dist/task_logging.js +18 -36
- package/dist/watch_dir.d.ts +2 -2
- package/dist/watch_dir.js +14 -16
- package/package.json +18 -23
- package/dist/gen_module.d.ts +0 -34
- package/dist/gen_module.js +0 -32
- package/dist/gen_module.test.d.ts +0 -1
- package/dist/gen_module.test.js +0 -30
- package/dist/task_module.d.ts +0 -15
- package/dist/task_module.js +0 -18
- package/dist/task_module.test.d.ts +0 -1
- package/dist/task_module.test.js +0 -67
package/dist/fs.js
CHANGED
|
@@ -1,19 +1,9 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { rm } from 'node:fs/promises';
|
|
2
|
+
import { readdirSync } from 'node:fs';
|
|
2
3
|
import { join } from 'node:path';
|
|
3
|
-
export const exists = async (path) => {
|
|
4
|
-
try {
|
|
5
|
-
await access(path, constants.F_OK);
|
|
6
|
-
return true;
|
|
7
|
-
}
|
|
8
|
-
catch (err) {
|
|
9
|
-
if (err.code === 'ENOENT')
|
|
10
|
-
return false;
|
|
11
|
-
throw err;
|
|
12
|
-
}
|
|
13
|
-
};
|
|
14
4
|
/**
|
|
15
5
|
* Empties a directory with an optional `filter`.
|
|
16
6
|
*/
|
|
17
7
|
export const empty_dir = async (dir, filter, options) => {
|
|
18
|
-
await Promise.all((
|
|
8
|
+
await Promise.all(readdirSync(dir).map((path) => filter && !filter(path) ? null : rm(join(dir, path), { ...options, recursive: true })));
|
|
19
9
|
};
|
package/dist/fs.test.js
CHANGED
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
import { test } from 'uvu';
|
|
2
2
|
import * as assert from 'uvu/assert';
|
|
3
|
-
import {
|
|
3
|
+
import { existsSync } from 'node:fs';
|
|
4
4
|
test('file exists', async () => {
|
|
5
|
-
assert.ok(
|
|
5
|
+
assert.ok(existsSync('./src/lib/fs.ts'));
|
|
6
6
|
});
|
|
7
7
|
test('file does not exist', async () => {
|
|
8
|
-
assert.ok(!(
|
|
8
|
+
assert.ok(!existsSync('./src/lib/existsssss.ts'));
|
|
9
9
|
});
|
|
10
10
|
test('directory exists', async () => {
|
|
11
|
-
assert.ok(
|
|
11
|
+
assert.ok(existsSync('./src/lib'));
|
|
12
12
|
});
|
|
13
13
|
test('directory does not exist', async () => {
|
|
14
|
-
assert.ok(!(
|
|
14
|
+
assert.ok(!existsSync('./src/libbbbbbbb'));
|
|
15
15
|
});
|
|
16
16
|
test.run();
|
package/dist/gen.d.ts
CHANGED
|
@@ -1,14 +1,22 @@
|
|
|
1
1
|
import type { Logger } from '@ryanatkn/belt/log.js';
|
|
2
2
|
import { z } from 'zod';
|
|
3
|
+
import type { Result } from '@ryanatkn/belt/result.js';
|
|
4
|
+
import type { Path_Id } from './path.js';
|
|
3
5
|
import type { Gro_Config } from './config.js';
|
|
6
|
+
import type { Parsed_Sveltekit_Config } from './sveltekit_config.js';
|
|
7
|
+
import { type Load_Modules_Failure, type Module_Meta } from './modules.js';
|
|
8
|
+
import { Input_Path, type Resolved_Input_File, type Resolved_Input_Path } from './input_path.js';
|
|
9
|
+
export declare const GEN_FILE_PATTERN_TEXT = "gen";
|
|
10
|
+
export declare const GEN_FILE_PATTERN: string;
|
|
11
|
+
export declare const is_gen_path: (path: string) => boolean;
|
|
4
12
|
export type Gen_Result = {
|
|
5
|
-
origin_id:
|
|
13
|
+
origin_id: Path_Id;
|
|
6
14
|
files: Gen_File[];
|
|
7
15
|
};
|
|
8
16
|
export interface Gen_File {
|
|
9
|
-
id:
|
|
17
|
+
id: Path_Id;
|
|
10
18
|
content: string;
|
|
11
|
-
origin_id:
|
|
19
|
+
origin_id: Path_Id;
|
|
12
20
|
format: boolean;
|
|
13
21
|
}
|
|
14
22
|
export interface Gen {
|
|
@@ -16,10 +24,11 @@ export interface Gen {
|
|
|
16
24
|
}
|
|
17
25
|
export interface Gen_Context {
|
|
18
26
|
config: Gro_Config;
|
|
27
|
+
sveltekit_config: Parsed_Sveltekit_Config;
|
|
19
28
|
/**
|
|
20
29
|
* Same as `import.meta.url` but in path form.
|
|
21
30
|
*/
|
|
22
|
-
origin_id:
|
|
31
|
+
origin_id: Path_Id;
|
|
23
32
|
log: Logger;
|
|
24
33
|
}
|
|
25
34
|
export type Raw_Gen_Result = string | Raw_Gen_File | null | Raw_Gen_Result[];
|
|
@@ -37,28 +46,28 @@ export declare const Gen_Config: z.ZodObject<{
|
|
|
37
46
|
}>;
|
|
38
47
|
export type Gen_Config = z.infer<typeof Gen_Config>;
|
|
39
48
|
export type Gen_Results = {
|
|
40
|
-
results:
|
|
41
|
-
successes:
|
|
42
|
-
failures:
|
|
49
|
+
results: Genfile_Module_Result[];
|
|
50
|
+
successes: Genfile_Module_Result_Success[];
|
|
51
|
+
failures: Genfile_Module_Result_Failure[];
|
|
43
52
|
input_count: number;
|
|
44
53
|
output_count: number;
|
|
45
54
|
elapsed: number;
|
|
46
55
|
};
|
|
47
|
-
export type
|
|
48
|
-
export type
|
|
56
|
+
export type Genfile_Module_Result = Genfile_Module_Result_Success | Genfile_Module_Result_Failure;
|
|
57
|
+
export type Genfile_Module_Result_Success = {
|
|
49
58
|
ok: true;
|
|
50
|
-
id:
|
|
59
|
+
id: Path_Id;
|
|
51
60
|
files: Gen_File[];
|
|
52
61
|
elapsed: number;
|
|
53
62
|
};
|
|
54
|
-
export type
|
|
63
|
+
export type Genfile_Module_Result_Failure = {
|
|
55
64
|
ok: false;
|
|
56
|
-
id:
|
|
65
|
+
id: Path_Id;
|
|
57
66
|
reason: string;
|
|
58
67
|
error: Error;
|
|
59
68
|
elapsed: number;
|
|
60
69
|
};
|
|
61
|
-
export declare const to_gen_result: (origin_id:
|
|
70
|
+
export declare const to_gen_result: (origin_id: Path_Id, raw_result: Raw_Gen_Result) => Gen_Result;
|
|
62
71
|
export declare const to_output_file_name: (filename: string) => string;
|
|
63
72
|
export type Analyzed_Gen_Result = {
|
|
64
73
|
file: Gen_File;
|
|
@@ -74,3 +83,47 @@ export type Analyzed_Gen_Result = {
|
|
|
74
83
|
export declare const analyze_gen_results: (gen_results: Gen_Results) => Promise<Analyzed_Gen_Result[]>;
|
|
75
84
|
export declare const analyze_gen_result: (file: Gen_File) => Promise<Analyzed_Gen_Result>;
|
|
76
85
|
export declare const write_gen_results: (gen_results: Gen_Results, analyzed_gen_results: Analyzed_Gen_Result[], log: Logger) => Promise<void>;
|
|
86
|
+
export interface Found_Genfiles {
|
|
87
|
+
resolved_input_files: Resolved_Input_File[];
|
|
88
|
+
resolved_input_files_by_input_path: Map<Input_Path, Resolved_Input_File[]>;
|
|
89
|
+
resolved_input_files_by_root_dir: Map<Path_Id, Resolved_Input_File[]>;
|
|
90
|
+
resolved_input_paths: Resolved_Input_Path[];
|
|
91
|
+
resolved_input_paths_by_input_path: Map<Input_Path, Resolved_Input_Path[]>;
|
|
92
|
+
}
|
|
93
|
+
export type Find_Genfiles_Result = Result<{
|
|
94
|
+
value: Found_Genfiles;
|
|
95
|
+
}, Find_Genfiles_Failure>;
|
|
96
|
+
export type Find_Genfiles_Failure = {
|
|
97
|
+
type: 'unmapped_input_paths';
|
|
98
|
+
unmapped_input_paths: Input_Path[];
|
|
99
|
+
resolved_input_paths: Resolved_Input_Path[];
|
|
100
|
+
resolved_input_paths_by_input_path: Map<Input_Path, Resolved_Input_Path[]>;
|
|
101
|
+
reasons: string[];
|
|
102
|
+
} | {
|
|
103
|
+
type: 'input_directories_with_no_files';
|
|
104
|
+
input_directories_with_no_files: Resolved_Input_Path[];
|
|
105
|
+
resolved_input_files: Resolved_Input_File[];
|
|
106
|
+
resolved_input_files_by_input_path: Map<Input_Path, Resolved_Input_File[]>;
|
|
107
|
+
resolved_input_files_by_root_dir: Map<Path_Id, Resolved_Input_File[]>;
|
|
108
|
+
resolved_input_paths: Resolved_Input_Path[];
|
|
109
|
+
resolved_input_paths_by_input_path: Map<Input_Path, Resolved_Input_Path[]>;
|
|
110
|
+
reasons: string[];
|
|
111
|
+
};
|
|
112
|
+
/**
|
|
113
|
+
* Finds modules from input paths. (see `src/lib/input_path.ts` for more)
|
|
114
|
+
*/
|
|
115
|
+
export declare const find_genfiles: (input_paths: Input_Path[], root_dirs: Path_Id[], config: Gro_Config, timings?: any) => Promise<Find_Genfiles_Result>;
|
|
116
|
+
export interface Genfile_Module {
|
|
117
|
+
gen: Gen;
|
|
118
|
+
}
|
|
119
|
+
export type Genfile_Module_Meta = Module_Meta<Genfile_Module>;
|
|
120
|
+
export interface Loaded_Genfiles {
|
|
121
|
+
modules: Genfile_Module_Meta[];
|
|
122
|
+
found_genfiles: Found_Genfiles;
|
|
123
|
+
}
|
|
124
|
+
export type Load_Genfiles_Result = Result<{
|
|
125
|
+
value: Loaded_Genfiles;
|
|
126
|
+
}, Load_Genfiles_Failure>;
|
|
127
|
+
export type Load_Genfiles_Failure = Load_Modules_Failure<Genfile_Module_Meta>;
|
|
128
|
+
export declare const load_genfiles: (found_genfiles: Found_Genfiles, timings?: any) => Promise<Load_Genfiles_Result>;
|
|
129
|
+
export declare const validate_gen_module: (mod: Record<string, any>) => mod is Genfile_Module;
|
package/dist/gen.js
CHANGED
|
@@ -1,9 +1,15 @@
|
|
|
1
1
|
import { join, basename, dirname, isAbsolute } from 'node:path';
|
|
2
2
|
import { mkdir, readFile, writeFile } from 'node:fs/promises';
|
|
3
3
|
import { z } from 'zod';
|
|
4
|
-
import {
|
|
4
|
+
import { red } from 'kleur/colors';
|
|
5
|
+
import { existsSync } from 'node:fs';
|
|
5
6
|
import { print_path } from './paths.js';
|
|
6
|
-
import {
|
|
7
|
+
import { load_modules } from './modules.js';
|
|
8
|
+
import { Input_Path, resolve_input_files, resolve_input_paths, } from './input_path.js';
|
|
9
|
+
import { search_fs } from './search_fs.js';
|
|
10
|
+
export const GEN_FILE_PATTERN_TEXT = 'gen';
|
|
11
|
+
export const GEN_FILE_PATTERN = '.' + GEN_FILE_PATTERN_TEXT + '.';
|
|
12
|
+
export const is_gen_path = (path) => path.includes(GEN_FILE_PATTERN);
|
|
7
13
|
export const Gen_Config = z.object({
|
|
8
14
|
imports: z.record(z.string(), z.string()).default({}),
|
|
9
15
|
});
|
|
@@ -47,19 +53,18 @@ const to_output_file_id = (origin_id, raw_file_name) => {
|
|
|
47
53
|
return output_file_id;
|
|
48
54
|
};
|
|
49
55
|
export const to_output_file_name = (filename) => {
|
|
50
|
-
const { pattern, text } = gen_module_meta[to_gen_module_type(filename)];
|
|
51
56
|
const parts = filename.split('.');
|
|
52
|
-
const gen_pattern_index = parts.indexOf(
|
|
57
|
+
const gen_pattern_index = parts.indexOf(GEN_FILE_PATTERN_TEXT);
|
|
53
58
|
if (gen_pattern_index === -1) {
|
|
54
|
-
throw Error(`Invalid gen file name - '${
|
|
59
|
+
throw Error(`Invalid gen file name - '${GEN_FILE_PATTERN_TEXT}' not found in '${filename}'`);
|
|
55
60
|
}
|
|
56
|
-
if (gen_pattern_index !== parts.lastIndexOf(
|
|
57
|
-
throw Error(`Invalid gen file name - multiple instances of '${
|
|
61
|
+
if (gen_pattern_index !== parts.lastIndexOf(GEN_FILE_PATTERN_TEXT)) {
|
|
62
|
+
throw Error(`Invalid gen file name - multiple instances of '${GEN_FILE_PATTERN_TEXT}' found in '${filename}'`);
|
|
58
63
|
}
|
|
59
64
|
if (gen_pattern_index < parts.length - 3) {
|
|
60
65
|
// This check is technically unneccessary,
|
|
61
66
|
// but ensures a consistent file naming convention.
|
|
62
|
-
throw Error(`Invalid gen file name - only one additional extension is allowed to follow '${
|
|
67
|
+
throw Error(`Invalid gen file name - only one additional extension is allowed to follow '${GEN_FILE_PATTERN}' in '${filename}'`);
|
|
63
68
|
}
|
|
64
69
|
const final_parts = [];
|
|
65
70
|
const has_different_ext = gen_pattern_index === parts.length - 3;
|
|
@@ -86,7 +91,7 @@ export const analyze_gen_results = (gen_results) => Promise.all(gen_results.succ
|
|
|
86
91
|
.map((result) => result.files.map((file) => analyze_gen_result(file)))
|
|
87
92
|
.flat());
|
|
88
93
|
export const analyze_gen_result = async (file) => {
|
|
89
|
-
if (!(
|
|
94
|
+
if (!existsSync(file.id)) {
|
|
90
95
|
return {
|
|
91
96
|
file,
|
|
92
97
|
existing_content: null,
|
|
@@ -124,3 +129,67 @@ export const write_gen_results = async (gen_results, analyzed_gen_results, log)
|
|
|
124
129
|
}))
|
|
125
130
|
.flat());
|
|
126
131
|
};
|
|
132
|
+
/**
|
|
133
|
+
* Finds modules from input paths. (see `src/lib/input_path.ts` for more)
|
|
134
|
+
*/
|
|
135
|
+
export const find_genfiles = async (input_paths, root_dirs, config, timings) => {
|
|
136
|
+
const extensions = [GEN_FILE_PATTERN];
|
|
137
|
+
// Check which extension variation works - if it's a directory, prefer others first!
|
|
138
|
+
const timing_to_resolve_input_paths = timings?.start('resolve input paths');
|
|
139
|
+
const { resolved_input_paths, resolved_input_paths_by_input_path, unmapped_input_paths } = await resolve_input_paths(input_paths, root_dirs, extensions);
|
|
140
|
+
timing_to_resolve_input_paths?.();
|
|
141
|
+
// Error if any input path could not be mapped.
|
|
142
|
+
if (unmapped_input_paths.length) {
|
|
143
|
+
return {
|
|
144
|
+
ok: false,
|
|
145
|
+
type: 'unmapped_input_paths',
|
|
146
|
+
unmapped_input_paths,
|
|
147
|
+
resolved_input_paths,
|
|
148
|
+
resolved_input_paths_by_input_path,
|
|
149
|
+
reasons: unmapped_input_paths.map((input_path) => red(`Input path ${print_path(input_path)} cannot be mapped to a file or directory.`)),
|
|
150
|
+
};
|
|
151
|
+
}
|
|
152
|
+
// Find all of the files for any directories.
|
|
153
|
+
const timing_to_search_fs = timings?.start('find files');
|
|
154
|
+
const { resolved_input_files, resolved_input_files_by_input_path, resolved_input_files_by_root_dir, input_directories_with_no_files, } = await resolve_input_files(resolved_input_paths, (id, options) => search_fs(id, {
|
|
155
|
+
...options,
|
|
156
|
+
filter: config.search_filters,
|
|
157
|
+
file_filter: (p) => extensions.some((e) => p.includes(e)),
|
|
158
|
+
}));
|
|
159
|
+
timing_to_search_fs?.();
|
|
160
|
+
// Error if any input path has no files. (means we have an empty directory)
|
|
161
|
+
if (input_directories_with_no_files.length) {
|
|
162
|
+
return {
|
|
163
|
+
ok: false,
|
|
164
|
+
type: 'input_directories_with_no_files',
|
|
165
|
+
input_directories_with_no_files,
|
|
166
|
+
resolved_input_files,
|
|
167
|
+
resolved_input_files_by_input_path,
|
|
168
|
+
resolved_input_files_by_root_dir,
|
|
169
|
+
resolved_input_paths,
|
|
170
|
+
resolved_input_paths_by_input_path,
|
|
171
|
+
reasons: input_directories_with_no_files.map(({ input_path }) => red(`Input directory contains no matching files: ${print_path(input_path)}`)),
|
|
172
|
+
};
|
|
173
|
+
}
|
|
174
|
+
return {
|
|
175
|
+
ok: true,
|
|
176
|
+
value: {
|
|
177
|
+
resolved_input_files,
|
|
178
|
+
resolved_input_files_by_input_path,
|
|
179
|
+
resolved_input_files_by_root_dir,
|
|
180
|
+
resolved_input_paths,
|
|
181
|
+
resolved_input_paths_by_input_path,
|
|
182
|
+
},
|
|
183
|
+
};
|
|
184
|
+
};
|
|
185
|
+
export const load_genfiles = async (found_genfiles, timings) => {
|
|
186
|
+
const loaded_modules = await load_modules(found_genfiles.resolved_input_files, validate_gen_module, (resolved_input_file, mod) => ({ id: resolved_input_file.id, mod }), timings);
|
|
187
|
+
if (!loaded_modules.ok) {
|
|
188
|
+
return loaded_modules;
|
|
189
|
+
}
|
|
190
|
+
return {
|
|
191
|
+
ok: true,
|
|
192
|
+
value: { modules: loaded_modules.modules, found_genfiles },
|
|
193
|
+
};
|
|
194
|
+
};
|
|
195
|
+
export const validate_gen_module = (mod) => typeof mod?.gen === 'function';
|
package/dist/gen.task.d.ts
CHANGED
|
@@ -2,13 +2,16 @@ import { z } from 'zod';
|
|
|
2
2
|
import { type Task } from './task.js';
|
|
3
3
|
export declare const Args: z.ZodObject<{
|
|
4
4
|
_: z.ZodDefault<z.ZodArray<z.ZodString, "many">>;
|
|
5
|
+
root_dirs: z.ZodDefault<z.ZodArray<z.ZodString, "many">>;
|
|
5
6
|
check: z.ZodDefault<z.ZodBoolean>;
|
|
6
7
|
}, "strict", z.ZodTypeAny, {
|
|
7
8
|
_: string[];
|
|
8
9
|
check: boolean;
|
|
10
|
+
root_dirs: string[];
|
|
9
11
|
}, {
|
|
10
12
|
_?: string[] | undefined;
|
|
11
13
|
check?: boolean | undefined;
|
|
14
|
+
root_dirs?: string[] | undefined;
|
|
12
15
|
}>;
|
|
13
16
|
export type Args = z.infer<typeof Args>;
|
|
14
17
|
export declare const task: Task<Args>;
|
package/dist/gen.task.js
CHANGED
|
@@ -4,16 +4,18 @@ import { plural } from '@ryanatkn/belt/string.js';
|
|
|
4
4
|
import { z } from 'zod';
|
|
5
5
|
import { Task_Error } from './task.js';
|
|
6
6
|
import { run_gen } from './run_gen.js';
|
|
7
|
-
import { load_gen_module, find_gen_modules } from './gen_module.js';
|
|
8
7
|
import { Raw_Input_Path, to_input_paths } from './input_path.js';
|
|
9
|
-
import { load_modules } from './modules.js';
|
|
10
8
|
import { format_file } from './format_file.js';
|
|
11
|
-
import {
|
|
9
|
+
import { print_path } from './paths.js';
|
|
12
10
|
import { log_error_reasons } from './task_logging.js';
|
|
13
|
-
import { write_gen_results, analyze_gen_results } from './gen.js';
|
|
11
|
+
import { write_gen_results, analyze_gen_results, find_genfiles, load_genfiles } from './gen.js';
|
|
12
|
+
import { SOURCE_DIRNAME } from './path_constants.js';
|
|
14
13
|
export const Args = z
|
|
15
14
|
.object({
|
|
16
|
-
_: z.array(Raw_Input_Path, { description: 'paths to generate' }).default([]),
|
|
15
|
+
_: z.array(Raw_Input_Path, { description: 'input paths to generate' }).default([SOURCE_DIRNAME]),
|
|
16
|
+
root_dirs: z
|
|
17
|
+
.array(z.string(), { description: 'root directories to resolve input paths against' }) // TODO `Path_Id` schema
|
|
18
|
+
.default([process.cwd()]),
|
|
17
19
|
check: z
|
|
18
20
|
.boolean({ description: 'exit with a nonzero code if any files need to be generated' })
|
|
19
21
|
.default(false),
|
|
@@ -25,29 +27,32 @@ export const task = {
|
|
|
25
27
|
summary: 'run code generation scripts',
|
|
26
28
|
Args,
|
|
27
29
|
run: async ({ args, log, timings, config }) => {
|
|
28
|
-
const { _: raw_input_paths, check } = args;
|
|
29
|
-
const input_paths =
|
|
30
|
+
const { _: raw_input_paths, root_dirs, check } = args;
|
|
31
|
+
const input_paths = to_input_paths(raw_input_paths);
|
|
30
32
|
// load all of the gen modules
|
|
31
|
-
const
|
|
32
|
-
if (!
|
|
33
|
-
if (
|
|
34
|
-
|
|
33
|
+
const found = await find_genfiles(input_paths, root_dirs, config, timings);
|
|
34
|
+
if (!found.ok) {
|
|
35
|
+
if (found.type === 'input_directories_with_no_files') {
|
|
36
|
+
// 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`
|
|
37
|
+
log.info('no gen modules found in ' + input_paths.join(', '));
|
|
35
38
|
return;
|
|
36
39
|
}
|
|
37
40
|
else {
|
|
38
|
-
log_error_reasons(log,
|
|
41
|
+
log_error_reasons(log, found.reasons);
|
|
39
42
|
throw new Task_Error('Failed to find gen modules.');
|
|
40
43
|
}
|
|
41
44
|
}
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
45
|
+
const found_genfiles = found.value;
|
|
46
|
+
log.info('gen files', found_genfiles.resolved_input_files.map((f) => f.id));
|
|
47
|
+
const loaded = await load_genfiles(found_genfiles, timings);
|
|
48
|
+
if (!loaded.ok) {
|
|
49
|
+
log_error_reasons(log, loaded.reasons);
|
|
46
50
|
throw new Task_Error('Failed to load gen modules.');
|
|
47
51
|
}
|
|
52
|
+
const loaded_genfiles = loaded.value;
|
|
48
53
|
// run `gen` on each of the modules
|
|
49
54
|
const timing_to_generate_code = timings.start('generate code'); // TODO this ignores `gen_results.elapsed` - should it return `Timings` instead?
|
|
50
|
-
const gen_results = await run_gen(
|
|
55
|
+
const gen_results = await run_gen(loaded_genfiles.modules, config, log, timings, format_file);
|
|
51
56
|
timing_to_generate_code();
|
|
52
57
|
const fail_count = gen_results.failures.length;
|
|
53
58
|
const analyzed_gen_results = await analyze_gen_results(gen_results);
|
|
@@ -82,11 +87,11 @@ export const task = {
|
|
|
82
87
|
// TODO these final printed results could be improved showing a breakdown per file id
|
|
83
88
|
const new_count = analyzed_gen_results.filter((r) => r.is_new).length;
|
|
84
89
|
const changed_count = analyzed_gen_results.filter((r) => r.has_changed).length;
|
|
85
|
-
const
|
|
90
|
+
const unchanged_count = analyzed_gen_results.filter((r) => !r.is_new && !r.has_changed).length;
|
|
86
91
|
let log_result = green('gen results:');
|
|
87
92
|
log_result += `\n\t${new_count} ` + gray('new');
|
|
88
93
|
log_result += `\n\t${changed_count} ` + gray('changed');
|
|
89
|
-
log_result += `\n\t${
|
|
94
|
+
log_result += `\n\t${unchanged_count} ` + gray('unchanged');
|
|
90
95
|
for (const result of gen_results.results) {
|
|
91
96
|
log_result += `\n\t${result.ok ? green('✓') : red('🞩')} ${result.ok ? result.files.length : 0} ${gray('in')} ${print_ms(result.elapsed)} ${gray('←')} ${print_path(result.id)}`;
|
|
92
97
|
}
|
package/dist/gen.test.js
CHANGED
|
@@ -1,32 +1,32 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { test } from 'uvu';
|
|
2
2
|
import * as assert from 'uvu/assert';
|
|
3
3
|
import { resolve } from 'node:path';
|
|
4
|
-
import { to_gen_result } from './gen.js';
|
|
4
|
+
import { to_gen_result, find_genfiles, validate_gen_module } from './gen.js';
|
|
5
|
+
import { paths } from './paths.js';
|
|
6
|
+
import { create_empty_config } from './config.js';
|
|
5
7
|
const origin_id = resolve('src/foo.gen.ts');
|
|
6
|
-
|
|
7
|
-
const test__to_gen_result = suite('to_gen_result');
|
|
8
|
-
test__to_gen_result('plain string', () => {
|
|
8
|
+
test('to_gen_result plain string', () => {
|
|
9
9
|
assert.equal(to_gen_result(origin_id, '/**/'), {
|
|
10
10
|
origin_id,
|
|
11
11
|
files: [{ id: resolve('src/foo.ts'), content: '/**/', origin_id, format: true }],
|
|
12
12
|
});
|
|
13
13
|
});
|
|
14
|
-
|
|
14
|
+
test('to_gen_result object with a content string', () => {
|
|
15
15
|
assert.equal(to_gen_result(origin_id, { content: '/**/' }), {
|
|
16
16
|
origin_id,
|
|
17
17
|
files: [{ id: resolve('src/foo.ts'), content: '/**/', origin_id, format: true }],
|
|
18
18
|
});
|
|
19
19
|
});
|
|
20
|
-
|
|
20
|
+
test('to_gen_result fail with an unresolved id', () => {
|
|
21
21
|
assert.throws(() => to_gen_result('src/foo.ts', { content: '/**/' }));
|
|
22
22
|
});
|
|
23
|
-
|
|
23
|
+
test('to_gen_result fail with a build id', () => {
|
|
24
24
|
assert.throws(() => to_gen_result(resolve('.gro/foo.js'), { content: '/**/' }));
|
|
25
25
|
});
|
|
26
|
-
|
|
26
|
+
test('to_gen_result fail with an empty id', () => {
|
|
27
27
|
assert.throws(() => to_gen_result('', { content: '/**/' }));
|
|
28
28
|
});
|
|
29
|
-
|
|
29
|
+
test('to_gen_result custom file name', () => {
|
|
30
30
|
assert.equal(to_gen_result(origin_id, {
|
|
31
31
|
filename: 'fooz.ts',
|
|
32
32
|
content: '/**/',
|
|
@@ -35,7 +35,7 @@ test__to_gen_result('custom file name', () => {
|
|
|
35
35
|
files: [{ id: resolve('src/fooz.ts'), content: '/**/', origin_id, format: true }],
|
|
36
36
|
});
|
|
37
37
|
});
|
|
38
|
-
|
|
38
|
+
test('to_gen_result custom file name that matches the default file name', () => {
|
|
39
39
|
assert.equal(to_gen_result(origin_id, {
|
|
40
40
|
filename: 'foo.ts',
|
|
41
41
|
content: '/**/',
|
|
@@ -44,7 +44,7 @@ test__to_gen_result('custom file name that matches the default file name', () =>
|
|
|
44
44
|
files: [{ id: resolve('src/foo.ts'), content: '/**/', origin_id, format: true }],
|
|
45
45
|
});
|
|
46
46
|
});
|
|
47
|
-
|
|
47
|
+
test('to_gen_result fail when custom file name explicitly matches the origin', () => {
|
|
48
48
|
assert.throws(() => {
|
|
49
49
|
to_gen_result(origin_id, {
|
|
50
50
|
filename: 'foo.gen.ts',
|
|
@@ -52,15 +52,15 @@ test__to_gen_result('fail when custom file name explicitly matches the origin',
|
|
|
52
52
|
});
|
|
53
53
|
});
|
|
54
54
|
});
|
|
55
|
-
|
|
55
|
+
test('to_gen_result fail when file name implicitly matches the origin', () => {
|
|
56
56
|
assert.throws(() => {
|
|
57
57
|
to_gen_result(resolve('src/foo.ts'), { content: '/**/' });
|
|
58
58
|
});
|
|
59
59
|
});
|
|
60
|
-
|
|
60
|
+
test('to_gen_result fail with an empty file name', () => {
|
|
61
61
|
assert.throws(() => to_gen_result(origin_id, { filename: '', content: '/**/' }));
|
|
62
62
|
});
|
|
63
|
-
|
|
63
|
+
test('to_gen_result additional file name parts', () => {
|
|
64
64
|
assert.equal(to_gen_result(resolve('src/foo.bar.gen.ts'), { content: '/**/' }), {
|
|
65
65
|
origin_id: resolve('src/foo.bar.gen.ts'),
|
|
66
66
|
files: [
|
|
@@ -73,7 +73,7 @@ test__to_gen_result('additional file name parts', () => {
|
|
|
73
73
|
],
|
|
74
74
|
});
|
|
75
75
|
});
|
|
76
|
-
|
|
76
|
+
test('to_gen_result js', () => {
|
|
77
77
|
assert.equal(to_gen_result(origin_id, {
|
|
78
78
|
filename: 'foo.js',
|
|
79
79
|
content: '/**/',
|
|
@@ -82,7 +82,7 @@ test__to_gen_result('js', () => {
|
|
|
82
82
|
files: [{ id: resolve('src/foo.js'), content: '/**/', origin_id, format: true }],
|
|
83
83
|
});
|
|
84
84
|
});
|
|
85
|
-
|
|
85
|
+
test('to_gen_result implicit custom file extension', () => {
|
|
86
86
|
assert.equal(to_gen_result(resolve('src/foo.gen.json.ts'), '[/**/]'), {
|
|
87
87
|
origin_id: resolve('src/foo.gen.json.ts'),
|
|
88
88
|
files: [
|
|
@@ -95,7 +95,7 @@ test__to_gen_result('implicit custom file extension', () => {
|
|
|
95
95
|
],
|
|
96
96
|
});
|
|
97
97
|
});
|
|
98
|
-
|
|
98
|
+
test('to_gen_result implicit empty file extension', () => {
|
|
99
99
|
assert.equal(to_gen_result(resolve('src/foo.gen..ts'), '[/**/]'), {
|
|
100
100
|
origin_id: resolve('src/foo.gen..ts'),
|
|
101
101
|
files: [
|
|
@@ -108,7 +108,7 @@ test__to_gen_result('implicit empty file extension', () => {
|
|
|
108
108
|
],
|
|
109
109
|
});
|
|
110
110
|
});
|
|
111
|
-
|
|
111
|
+
test('to_gen_result implicit custom file extension with additional file name parts', () => {
|
|
112
112
|
assert.equal(to_gen_result(resolve('src/foo.bar.gen.json.ts'), { content: '[/**/]' }), {
|
|
113
113
|
origin_id: resolve('src/foo.bar.gen.json.ts'),
|
|
114
114
|
files: [
|
|
@@ -121,7 +121,7 @@ test__to_gen_result('implicit custom file extension with additional file name pa
|
|
|
121
121
|
],
|
|
122
122
|
});
|
|
123
123
|
});
|
|
124
|
-
|
|
124
|
+
test('to_gen_result implicit custom file extension with many dots in between', () => {
|
|
125
125
|
assert.equal(to_gen_result(resolve('src/foo...gen.ts'), '[/**/]'), {
|
|
126
126
|
origin_id: resolve('src/foo...gen.ts'),
|
|
127
127
|
files: [
|
|
@@ -134,36 +134,36 @@ test__to_gen_result('implicit custom file extension with many dots in between',
|
|
|
134
134
|
],
|
|
135
135
|
});
|
|
136
136
|
});
|
|
137
|
-
|
|
137
|
+
test('to_gen_result fail with two parts following the .gen. pattern in the file name', () => {
|
|
138
138
|
// This just ensures consistent file names - maybe loosen the restriction?
|
|
139
139
|
// You can still implicitly name files like this,
|
|
140
140
|
// but you have to move ".bar" before ".gen".
|
|
141
141
|
assert.throws(() => to_gen_result(resolve('src/foo.gen.bar.json.ts'), '/**/'));
|
|
142
142
|
});
|
|
143
|
-
|
|
143
|
+
test('to_gen_result fail implicit file extension ending with a dot', () => {
|
|
144
144
|
// This just ensures consistent file names - maybe loosen the restriction?
|
|
145
145
|
// This one is more restrictive than the above,
|
|
146
146
|
// because to have a file ending with a dot
|
|
147
147
|
// you have to use an explicit file name.
|
|
148
148
|
assert.throws(() => to_gen_result(resolve('src/foo.gen...ts'), '[/**/]'));
|
|
149
149
|
});
|
|
150
|
-
|
|
150
|
+
test('to_gen_result fail without a .gen. pattern in the file name', () => {
|
|
151
151
|
assert.throws(() => {
|
|
152
152
|
to_gen_result(resolve('src/foo.ts'), '/**/');
|
|
153
153
|
});
|
|
154
154
|
});
|
|
155
|
-
|
|
155
|
+
test('to_gen_result fail without a .gen. pattern in a file name that has multiple other patterns', () => {
|
|
156
156
|
assert.throws(() => {
|
|
157
157
|
to_gen_result(resolve('src/foo.bar.baz.ts'), '/**/');
|
|
158
158
|
});
|
|
159
159
|
});
|
|
160
|
-
|
|
160
|
+
test('to_gen_result fail with two .gen. patterns in the file name', () => {
|
|
161
161
|
assert.throws(() => to_gen_result(resolve('src/lib/gen.gen.ts'), '/**/'));
|
|
162
162
|
assert.throws(() => to_gen_result(resolve('src/foo.gen.gen.ts'), '/**/'));
|
|
163
163
|
assert.throws(() => to_gen_result(resolve('src/foo.gen.bar.gen.ts'), '/**/'));
|
|
164
164
|
assert.throws(() => to_gen_result(resolve('src/foo.gen.bar.gen.baz.ts'), '/**/'));
|
|
165
165
|
});
|
|
166
|
-
|
|
166
|
+
test('to_gen_result explicit custom file extension', () => {
|
|
167
167
|
assert.equal(to_gen_result(origin_id, {
|
|
168
168
|
filename: 'foo.json',
|
|
169
169
|
content: '[/**/]',
|
|
@@ -172,7 +172,7 @@ test__to_gen_result('explicit custom file extension', () => {
|
|
|
172
172
|
files: [{ id: resolve('src/foo.json'), content: '[/**/]', origin_id, format: true }],
|
|
173
173
|
});
|
|
174
174
|
});
|
|
175
|
-
|
|
175
|
+
test('to_gen_result explicit custom empty file extension', () => {
|
|
176
176
|
assert.equal(to_gen_result(origin_id, {
|
|
177
177
|
filename: 'foo',
|
|
178
178
|
content: '[/**/]',
|
|
@@ -181,7 +181,7 @@ test__to_gen_result('explicit custom empty file extension', () => {
|
|
|
181
181
|
files: [{ id: resolve('src/foo'), content: '[/**/]', origin_id, format: true }],
|
|
182
182
|
});
|
|
183
183
|
});
|
|
184
|
-
|
|
184
|
+
test('to_gen_result explicit custom file extension ending with a dot', () => {
|
|
185
185
|
assert.equal(to_gen_result(origin_id, {
|
|
186
186
|
filename: 'foo.',
|
|
187
187
|
content: '[/**/]',
|
|
@@ -190,7 +190,7 @@ test__to_gen_result('explicit custom file extension ending with a dot', () => {
|
|
|
190
190
|
files: [{ id: resolve('src/foo.'), content: '[/**/]', origin_id, format: true }],
|
|
191
191
|
});
|
|
192
192
|
});
|
|
193
|
-
|
|
193
|
+
test('to_gen_result simple array of raw files', () => {
|
|
194
194
|
assert.equal(to_gen_result(origin_id, [{ content: '/*1*/' }, { filename: 'foo2.ts', content: '/*2*/' }]), {
|
|
195
195
|
origin_id,
|
|
196
196
|
files: [
|
|
@@ -199,7 +199,7 @@ test__to_gen_result('simple array of raw files', () => {
|
|
|
199
199
|
],
|
|
200
200
|
});
|
|
201
201
|
});
|
|
202
|
-
|
|
202
|
+
test('to_gen_result complex array of raw files', () => {
|
|
203
203
|
assert.equal(to_gen_result(origin_id, [
|
|
204
204
|
{ content: '/*1*/' },
|
|
205
205
|
{ filename: 'foo2.ts', content: '/*2*/' },
|
|
@@ -217,12 +217,12 @@ test__to_gen_result('complex array of raw files', () => {
|
|
|
217
217
|
],
|
|
218
218
|
});
|
|
219
219
|
});
|
|
220
|
-
|
|
220
|
+
test('to_gen_result fail with duplicate names because of omissions', () => {
|
|
221
221
|
assert.throws(() => {
|
|
222
222
|
to_gen_result(origin_id, [{ content: '/*1*/' }, { content: '/*2*/' }]);
|
|
223
223
|
});
|
|
224
224
|
});
|
|
225
|
-
|
|
225
|
+
test('to_gen_result fail with duplicate explicit names', () => {
|
|
226
226
|
assert.throws(() => {
|
|
227
227
|
to_gen_result(origin_id, [
|
|
228
228
|
{ filename: 'foo.ts', content: '/*1*/' },
|
|
@@ -230,10 +230,23 @@ test__to_gen_result('fail with duplicate explicit names', () => {
|
|
|
230
230
|
]);
|
|
231
231
|
});
|
|
232
232
|
});
|
|
233
|
-
|
|
233
|
+
test('to_gen_result fail with duplicate explicit and implicit names', () => {
|
|
234
234
|
assert.throws(() => {
|
|
235
235
|
to_gen_result(origin_id, [{ content: '/*1*/' }, { filename: 'foo.ts', content: '/*2*/' }]);
|
|
236
236
|
});
|
|
237
237
|
});
|
|
238
|
-
|
|
239
|
-
|
|
238
|
+
test('validate_gen_module basic behavior', () => {
|
|
239
|
+
assert.ok(validate_gen_module({ gen: Function.prototype }));
|
|
240
|
+
assert.ok(!validate_gen_module({ gen: {} }));
|
|
241
|
+
assert.ok(!validate_gen_module({ task: { run: {} } }));
|
|
242
|
+
assert.ok(!validate_gen_module(undefined));
|
|
243
|
+
assert.ok(!validate_gen_module(null));
|
|
244
|
+
assert.ok(!validate_gen_module(false));
|
|
245
|
+
});
|
|
246
|
+
test('find_genfiles_result finds gen modules in a directory', async () => {
|
|
247
|
+
const find_genfiles_result = await find_genfiles(['docs'], [paths.lib], create_empty_config());
|
|
248
|
+
assert.ok(find_genfiles_result.ok);
|
|
249
|
+
assert.ok(find_genfiles_result.value.resolved_input_paths.length);
|
|
250
|
+
assert.ok(find_genfiles_result.value.resolved_input_paths_by_input_path.size);
|
|
251
|
+
});
|
|
252
|
+
test.run();
|
package/dist/git.d.ts
CHANGED