@ryanatkn/gro 0.119.0 → 0.120.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (104) hide show
  1. package/README.md +3 -2
  2. package/dist/changelog.d.ts +1 -0
  3. package/dist/changeset.task.js +4 -5
  4. package/dist/clean_fs.d.ts +1 -2
  5. package/dist/clean_fs.js +3 -2
  6. package/dist/cli.d.ts +1 -2
  7. package/dist/cli.js +2 -2
  8. package/dist/config.d.ts +14 -1
  9. package/dist/config.js +17 -5
  10. package/dist/config.test.js +50 -1
  11. package/dist/deploy.task.js +8 -7
  12. package/dist/docs/README.gen.md.js +3 -3
  13. package/dist/docs/config.md +22 -6
  14. package/dist/docs/gen.md +2 -1
  15. package/dist/docs/task.md +4 -3
  16. package/dist/docs/tasks.gen.md.js +17 -10
  17. package/dist/docs/tasks.md +1 -0
  18. package/dist/env.d.ts +2 -3
  19. package/dist/env.js +2 -2
  20. package/dist/esbuild_helpers.d.ts +2 -1
  21. package/dist/esbuild_plugin_external_worker.d.ts +0 -1
  22. package/dist/esbuild_plugin_external_worker.js +7 -7
  23. package/dist/esbuild_plugin_svelte.d.ts +0 -1
  24. package/dist/esbuild_plugin_sveltekit_local_imports.js +2 -2
  25. package/dist/format_directory.d.ts +1 -0
  26. package/dist/fs.d.ts +1 -3
  27. package/dist/fs.js +3 -13
  28. package/dist/fs.test.js +5 -5
  29. package/dist/gen.d.ts +66 -13
  30. package/dist/gen.js +77 -9
  31. package/dist/gen.task.d.ts +3 -0
  32. package/dist/gen.task.js +24 -19
  33. package/dist/gen.test.js +48 -35
  34. package/dist/git.d.ts +0 -1
  35. package/dist/git.js +3 -3
  36. package/dist/gro_helpers.js +4 -4
  37. package/dist/gro_plugin_gen.js +7 -7
  38. package/dist/gro_plugin_server.d.ts +2 -2
  39. package/dist/gro_plugin_server.js +5 -5
  40. package/dist/gro_plugin_sveltekit_app.js +8 -8
  41. package/dist/hash.d.ts +1 -2
  42. package/dist/input_path.d.ts +34 -16
  43. package/dist/input_path.js +119 -67
  44. package/dist/input_path.test.js +150 -46
  45. package/dist/invoke_task.d.ts +2 -1
  46. package/dist/invoke_task.js +33 -79
  47. package/dist/loader.d.ts +0 -1
  48. package/dist/loader.js +4 -4
  49. package/dist/modules.d.ts +17 -36
  50. package/dist/modules.js +29 -68
  51. package/dist/modules.test.js +19 -143
  52. package/dist/package.d.ts +11 -20
  53. package/dist/package.js +61 -64
  54. package/dist/package_json.d.ts +1 -0
  55. package/dist/package_json.js +1 -1
  56. package/dist/package_meta.d.ts +1 -1
  57. package/dist/path.d.ts +12 -8
  58. package/dist/path.js +0 -6
  59. package/dist/paths.d.ts +7 -12
  60. package/dist/paths.js +11 -34
  61. package/dist/paths.test.js +17 -15
  62. package/dist/plugin.d.ts +1 -1
  63. package/dist/publish.task.js +3 -3
  64. package/dist/resolve.task.d.ts +11 -0
  65. package/dist/resolve.task.js +24 -0
  66. package/dist/resolve_node_specifier.d.ts +2 -2
  67. package/dist/resolve_node_specifier.js +3 -3
  68. package/dist/resolve_specifier.d.ts +2 -1
  69. package/dist/resolve_specifier.js +16 -16
  70. package/dist/resolve_specifier.test.js +9 -9
  71. package/dist/run.task.js +2 -2
  72. package/dist/run_gen.d.ts +4 -4
  73. package/dist/run_gen.js +9 -16
  74. package/dist/run_gen.test.js +10 -15
  75. package/dist/run_task.d.ts +2 -1
  76. package/dist/run_task.js +2 -0
  77. package/dist/search_fs.d.ts +20 -7
  78. package/dist/search_fs.js +40 -18
  79. package/dist/search_fs.test.js +9 -11
  80. package/dist/src_json.js +2 -2
  81. package/dist/sveltekit_config.d.ts +0 -1
  82. package/dist/sveltekit_helpers.js +4 -4
  83. package/dist/sveltekit_shim_app.d.ts +1 -1
  84. package/dist/sveltekit_shim_app_forms.d.ts +0 -1
  85. package/dist/sveltekit_shim_app_navigation.d.ts +0 -1
  86. package/dist/sveltekit_shim_app_paths.d.ts +0 -1
  87. package/dist/sveltekit_shim_app_stores.d.ts +0 -1
  88. package/dist/sveltekit_shim_env.d.ts +1 -1
  89. package/dist/task.d.ts +65 -1
  90. package/dist/task.js +85 -9
  91. package/dist/task.test.js +26 -6
  92. package/dist/task_logging.d.ts +3 -6
  93. package/dist/task_logging.js +18 -36
  94. package/dist/watch_dir.d.ts +2 -2
  95. package/dist/watch_dir.js +14 -16
  96. package/package.json +18 -23
  97. package/dist/gen_module.d.ts +0 -34
  98. package/dist/gen_module.js +0 -32
  99. package/dist/gen_module.test.d.ts +0 -1
  100. package/dist/gen_module.test.js +0 -30
  101. package/dist/task_module.d.ts +0 -15
  102. package/dist/task_module.js +0 -18
  103. package/dist/task_module.test.d.ts +0 -1
  104. package/dist/task_module.test.js +0 -67
package/dist/fs.js CHANGED
@@ -1,19 +1,9 @@
1
- import { access, constants, readdir, rm } from 'node:fs/promises';
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((await readdir(dir)).map((path) => filter && !filter(path) ? null : rm(join(dir, path), { ...options, recursive: true })));
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 { exists } from './fs.js';
3
+ import { existsSync } from 'node:fs';
4
4
  test('file exists', async () => {
5
- assert.ok(await exists('./src/lib/fs.ts'));
5
+ assert.ok(existsSync('./src/lib/fs.ts'));
6
6
  });
7
7
  test('file does not exist', async () => {
8
- assert.ok(!(await exists('./src/lib/existsssss.ts')));
8
+ assert.ok(!existsSync('./src/lib/existsssss.ts'));
9
9
  });
10
10
  test('directory exists', async () => {
11
- assert.ok(await exists('./src/lib'));
11
+ assert.ok(existsSync('./src/lib'));
12
12
  });
13
13
  test('directory does not exist', async () => {
14
- assert.ok(!(await exists('./src/libbbbbbbb')));
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: string;
13
+ origin_id: Path_Id;
6
14
  files: Gen_File[];
7
15
  };
8
16
  export interface Gen_File {
9
- id: string;
17
+ id: Path_Id;
10
18
  content: string;
11
- origin_id: string;
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: string;
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: Gen_Module_Result[];
41
- successes: Gen_Module_Result_Success[];
42
- failures: Gen_Module_Result_Failure[];
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 Gen_Module_Result = Gen_Module_Result_Success | Gen_Module_Result_Failure;
48
- export type Gen_Module_Result_Success = {
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: string;
59
+ id: Path_Id;
51
60
  files: Gen_File[];
52
61
  elapsed: number;
53
62
  };
54
- export type Gen_Module_Result_Failure = {
63
+ export type Genfile_Module_Result_Failure = {
55
64
  ok: false;
56
- id: string;
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: Flavored<string, "Source_Id">, raw_result: Raw_Gen_Result) => Gen_Result;
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 { gen_module_meta, to_gen_module_type } from './gen_module.js';
4
+ import { red } from 'kleur/colors';
5
+ import { existsSync } from 'node:fs';
5
6
  import { print_path } from './paths.js';
6
- import { exists } from './fs.js';
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(text);
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 - '${text}' not found in '${filename}'`);
59
+ throw Error(`Invalid gen file name - '${GEN_FILE_PATTERN_TEXT}' not found in '${filename}'`);
55
60
  }
56
- if (gen_pattern_index !== parts.lastIndexOf(text)) {
57
- throw Error(`Invalid gen file name - multiple instances of '${text}' found in '${filename}'`);
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 '${pattern}' in '${filename}'`);
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 (!(await exists(file.id))) {
94
+ if (!existsSync(file.id)) {
90
95
  return {
91
96
  file,
92
97
  existing_content: null,
@@ -124,3 +129,66 @@ 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) => search_fs(id, {
155
+ filter: config.search_filters,
156
+ file_filter: (p) => extensions.some((e) => p.includes(e)),
157
+ }));
158
+ timing_to_search_fs?.();
159
+ // Error if any input path has no files. (means we have an empty directory)
160
+ if (input_directories_with_no_files.length) {
161
+ return {
162
+ ok: false,
163
+ type: 'input_directories_with_no_files',
164
+ input_directories_with_no_files,
165
+ resolved_input_files,
166
+ resolved_input_files_by_input_path,
167
+ resolved_input_files_by_root_dir,
168
+ resolved_input_paths,
169
+ resolved_input_paths_by_input_path,
170
+ reasons: input_directories_with_no_files.map(({ input_path }) => red(`Input directory contains no matching files: ${print_path(input_path)}`)),
171
+ };
172
+ }
173
+ return {
174
+ ok: true,
175
+ value: {
176
+ resolved_input_files,
177
+ resolved_input_files_by_input_path,
178
+ resolved_input_files_by_root_dir,
179
+ resolved_input_paths,
180
+ resolved_input_paths_by_input_path,
181
+ },
182
+ };
183
+ };
184
+ export const load_genfiles = async (found_genfiles, timings) => {
185
+ 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);
186
+ if (!loaded_modules.ok) {
187
+ return loaded_modules;
188
+ }
189
+ return {
190
+ ok: true,
191
+ value: { modules: loaded_modules.modules, found_genfiles },
192
+ };
193
+ };
194
+ export const validate_gen_module = (mod) => typeof mod?.gen === 'function';
@@ -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 { paths, print_path } from './paths.js';
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 = raw_input_paths.length ? to_input_paths(raw_input_paths) : [paths.source];
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 find_modules_result = await find_gen_modules(input_paths);
32
- if (!find_modules_result.ok) {
33
- if (find_modules_result.type === 'input_directories_with_no_files') {
34
- log.info('no gen modules found');
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, find_modules_result.reasons);
41
+ log_error_reasons(log, found.reasons);
39
42
  throw new Task_Error('Failed to find gen modules.');
40
43
  }
41
44
  }
42
- log.info('gen files', Array.from(find_modules_result.source_ids_by_input_path.values()).flat());
43
- const load_modules_result = await load_modules(find_modules_result.source_ids_by_input_path, load_gen_module);
44
- if (!load_modules_result.ok) {
45
- log_error_reasons(log, load_modules_result.reasons);
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(load_modules_result.modules, config, log, timings, format_file);
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 skipped_count = analyzed_gen_results.filter((r) => !r.is_new && !r.has_changed).length;
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${skipped_count} ` + gray('skipped');
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 { suite } from 'uvu';
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
- /* test__to_gen_result */
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
- test__to_gen_result('object with a content string', () => {
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
- test__to_gen_result('fail with an unresolved id', () => {
20
+ test('to_gen_result fail with an unresolved id', () => {
21
21
  assert.throws(() => to_gen_result('src/foo.ts', { content: '/**/' }));
22
22
  });
23
- test__to_gen_result('fail with a build id', () => {
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
- test__to_gen_result('fail with an empty id', () => {
26
+ test('to_gen_result fail with an empty id', () => {
27
27
  assert.throws(() => to_gen_result('', { content: '/**/' }));
28
28
  });
29
- test__to_gen_result('custom file name', () => {
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
- test__to_gen_result('custom file name that matches the default file name', () => {
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
- test__to_gen_result('fail when custom file name explicitly matches the origin', () => {
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
- test__to_gen_result('fail when file name implicitly matches the origin', () => {
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
- test__to_gen_result('fail with an empty file name', () => {
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
- test__to_gen_result('additional file name parts', () => {
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
- test__to_gen_result('js', () => {
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
- test__to_gen_result('implicit custom file extension', () => {
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
- test__to_gen_result('implicit empty file extension', () => {
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
- test__to_gen_result('implicit custom file extension with additional file name parts', () => {
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
- test__to_gen_result('implicit custom file extension with many dots in between', () => {
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
- test__to_gen_result('fail with two parts following the .gen. pattern in the file name', () => {
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
- test__to_gen_result('fail implicit file extension ending with a dot', () => {
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
- test__to_gen_result('fail without a .gen. pattern in the file name', () => {
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
- test__to_gen_result('fail without a .gen. pattern in a file name that has multiple other patterns', () => {
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
- test__to_gen_result('fail with two .gen. patterns in the file name', () => {
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
- test__to_gen_result('explicit custom file extension', () => {
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
- test__to_gen_result('explicit custom empty file extension', () => {
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
- test__to_gen_result('explicit custom file extension ending with a dot', () => {
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
- test__to_gen_result('simple array of raw files', () => {
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
- test__to_gen_result('complex array of raw files', () => {
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
- test__to_gen_result('fail with duplicate names because of omissions', () => {
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
- test__to_gen_result('fail with duplicate explicit names', () => {
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
- test__to_gen_result('fail with duplicate explicit and implicit names', () => {
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
- test__to_gen_result.run();
239
- /* test__to_gen_result */
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
@@ -1,4 +1,3 @@
1
- /// <reference types="node" />
2
1
  import type { SpawnOptions } from 'child_process';
3
2
  import { z } from 'zod';
4
3
  export declare const Git_Origin: z.ZodBranded<z.ZodString, "Git_Origin">;