@fuzdev/gro 0.192.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/LICENSE +21 -0
- package/README.md +283 -0
- package/dist/args.d.ts +37 -0
- package/dist/args.d.ts.map +1 -0
- package/dist/args.js +102 -0
- package/dist/build.task.d.ts +20 -0
- package/dist/build.task.d.ts.map +1 -0
- package/dist/build.task.js +119 -0
- package/dist/build_cache.d.ts +100 -0
- package/dist/build_cache.d.ts.map +1 -0
- package/dist/build_cache.js +299 -0
- package/dist/changelog.d.ts +11 -0
- package/dist/changelog.d.ts.map +1 -0
- package/dist/changelog.js +47 -0
- package/dist/changeset.task.d.ts +35 -0
- package/dist/changeset.task.d.ts.map +1 -0
- package/dist/changeset.task.js +151 -0
- package/dist/changeset_helpers.d.ts +17 -0
- package/dist/changeset_helpers.d.ts.map +1 -0
- package/dist/changeset_helpers.js +7 -0
- package/dist/check.task.d.ts +28 -0
- package/dist/check.task.d.ts.map +1 -0
- package/dist/check.task.js +104 -0
- package/dist/child_process_logging.d.ts +10 -0
- package/dist/child_process_logging.d.ts.map +1 -0
- package/dist/child_process_logging.js +26 -0
- package/dist/clean.task.d.ts +15 -0
- package/dist/clean.task.d.ts.map +1 -0
- package/dist/clean.task.js +40 -0
- package/dist/clean_fs.d.ts +9 -0
- package/dist/clean_fs.d.ts.map +1 -0
- package/dist/clean_fs.js +28 -0
- package/dist/cli.d.ts +34 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +61 -0
- package/dist/commit.task.d.ts +11 -0
- package/dist/commit.task.d.ts.map +1 -0
- package/dist/commit.task.js +24 -0
- package/dist/constants.d.ts +46 -0
- package/dist/constants.d.ts.map +1 -0
- package/dist/constants.js +52 -0
- package/dist/deploy.task.d.ts +29 -0
- package/dist/deploy.task.d.ts.map +1 -0
- package/dist/deploy.task.js +217 -0
- package/dist/dev.task.d.ts +16 -0
- package/dist/dev.task.d.ts.map +1 -0
- package/dist/dev.task.js +44 -0
- package/dist/disknode.d.ts +23 -0
- package/dist/disknode.d.ts.map +1 -0
- package/dist/disknode.js +1 -0
- package/dist/env.d.ts +11 -0
- package/dist/env.d.ts.map +1 -0
- package/dist/env.js +49 -0
- package/dist/esbuild_helpers.d.ts +16 -0
- package/dist/esbuild_helpers.d.ts.map +1 -0
- package/dist/esbuild_helpers.js +36 -0
- package/dist/esbuild_plugin_external_worker.d.ts +23 -0
- package/dist/esbuild_plugin_external_worker.d.ts.map +1 -0
- package/dist/esbuild_plugin_external_worker.js +55 -0
- package/dist/esbuild_plugin_svelte.d.ts +15 -0
- package/dist/esbuild_plugin_svelte.d.ts.map +1 -0
- package/dist/esbuild_plugin_svelte.js +83 -0
- package/dist/esbuild_plugin_sveltekit_local_imports.d.ts +8 -0
- package/dist/esbuild_plugin_sveltekit_local_imports.d.ts.map +1 -0
- package/dist/esbuild_plugin_sveltekit_local_imports.js +30 -0
- package/dist/esbuild_plugin_sveltekit_shim_alias.d.ts +7 -0
- package/dist/esbuild_plugin_sveltekit_shim_alias.d.ts.map +1 -0
- package/dist/esbuild_plugin_sveltekit_shim_alias.js +18 -0
- package/dist/esbuild_plugin_sveltekit_shim_app.d.ts +9 -0
- package/dist/esbuild_plugin_sveltekit_shim_app.d.ts.map +1 -0
- package/dist/esbuild_plugin_sveltekit_shim_app.js +22 -0
- package/dist/esbuild_plugin_sveltekit_shim_env.d.ts +11 -0
- package/dist/esbuild_plugin_sveltekit_shim_env.d.ts.map +1 -0
- package/dist/esbuild_plugin_sveltekit_shim_env.js +18 -0
- package/dist/filer.d.ts +33 -0
- package/dist/filer.d.ts.map +1 -0
- package/dist/filer.js +385 -0
- package/dist/format.task.d.ts +11 -0
- package/dist/format.task.d.ts.map +1 -0
- package/dist/format.task.js +27 -0
- package/dist/format_directory.d.ts +13 -0
- package/dist/format_directory.d.ts.map +1 -0
- package/dist/format_directory.js +40 -0
- package/dist/format_file.d.ts +9 -0
- package/dist/format_file.d.ts.map +1 -0
- package/dist/format_file.js +42 -0
- package/dist/gen.d.ts +142 -0
- package/dist/gen.d.ts.map +1 -0
- package/dist/gen.js +199 -0
- package/dist/gen.task.d.ts +12 -0
- package/dist/gen.task.d.ts.map +1 -0
- package/dist/gen.task.js +149 -0
- package/dist/gen_helpers.d.ts +11 -0
- package/dist/gen_helpers.d.ts.map +1 -0
- package/dist/gen_helpers.js +76 -0
- package/dist/github.d.ts +19 -0
- package/dist/github.d.ts.map +1 -0
- package/dist/github.js +33 -0
- package/dist/gro.config.default.d.ts +13 -0
- package/dist/gro.config.default.d.ts.map +1 -0
- package/dist/gro.config.default.js +33 -0
- package/dist/gro.d.ts +3 -0
- package/dist/gro.d.ts.map +1 -0
- package/dist/gro.js +21 -0
- package/dist/gro_config.d.ts +115 -0
- package/dist/gro_config.d.ts.map +1 -0
- package/dist/gro_config.js +114 -0
- package/dist/gro_helpers.d.ts +49 -0
- package/dist/gro_helpers.d.ts.map +1 -0
- package/dist/gro_helpers.js +97 -0
- package/dist/gro_plugin_gen.d.ts +12 -0
- package/dist/gro_plugin_gen.d.ts.map +1 -0
- package/dist/gro_plugin_gen.js +101 -0
- package/dist/gro_plugin_server.d.ts +80 -0
- package/dist/gro_plugin_server.d.ts.map +1 -0
- package/dist/gro_plugin_server.js +167 -0
- package/dist/gro_plugin_sveltekit_app.d.ts +9 -0
- package/dist/gro_plugin_sveltekit_app.d.ts.map +1 -0
- package/dist/gro_plugin_sveltekit_app.js +42 -0
- package/dist/gro_plugin_sveltekit_library.d.ts +16 -0
- package/dist/gro_plugin_sveltekit_library.d.ts.map +1 -0
- package/dist/gro_plugin_sveltekit_library.js +34 -0
- package/dist/index.d.ts +9 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +4 -0
- package/dist/input_path.d.ts +64 -0
- package/dist/input_path.d.ts.map +1 -0
- package/dist/input_path.js +199 -0
- package/dist/invoke.d.ts +2 -0
- package/dist/invoke.d.ts.map +1 -0
- package/dist/invoke.js +28 -0
- package/dist/invoke_task.d.ts +30 -0
- package/dist/invoke_task.d.ts.map +1 -0
- package/dist/invoke_task.js +104 -0
- package/dist/lint.task.d.ts +11 -0
- package/dist/lint.task.d.ts.map +1 -0
- package/dist/lint.task.js +32 -0
- package/dist/loader.d.ts +6 -0
- package/dist/loader.d.ts.map +1 -0
- package/dist/loader.js +192 -0
- package/dist/module.d.ts +4 -0
- package/dist/module.d.ts.map +1 -0
- package/dist/module.js +6 -0
- package/dist/modules.d.ts +36 -0
- package/dist/modules.d.ts.map +1 -0
- package/dist/modules.js +71 -0
- package/dist/package_json.d.ts +32 -0
- package/dist/package_json.d.ts.map +1 -0
- package/dist/package_json.js +178 -0
- package/dist/parse_exports.d.ts +20 -0
- package/dist/parse_exports.d.ts.map +1 -0
- package/dist/parse_exports.js +65 -0
- package/dist/parse_exports_context.d.ts +21 -0
- package/dist/parse_exports_context.d.ts.map +1 -0
- package/dist/parse_exports_context.js +332 -0
- package/dist/parse_imports.d.ts +5 -0
- package/dist/parse_imports.d.ts.map +1 -0
- package/dist/parse_imports.js +140 -0
- package/dist/paths.d.ts +41 -0
- package/dist/paths.d.ts.map +1 -0
- package/dist/paths.js +69 -0
- package/dist/plugin.d.ts +36 -0
- package/dist/plugin.d.ts.map +1 -0
- package/dist/plugin.js +78 -0
- package/dist/publish.task.d.ts +26 -0
- package/dist/publish.task.d.ts.map +1 -0
- package/dist/publish.task.js +176 -0
- package/dist/register.d.ts +2 -0
- package/dist/register.d.ts.map +1 -0
- package/dist/register.js +2 -0
- package/dist/reinstall.task.d.ts +8 -0
- package/dist/reinstall.task.d.ts.map +1 -0
- package/dist/reinstall.task.js +35 -0
- package/dist/release.task.d.ts +8 -0
- package/dist/release.task.d.ts.map +1 -0
- package/dist/release.task.js +20 -0
- package/dist/resolve.task.d.ts +11 -0
- package/dist/resolve.task.d.ts.map +1 -0
- package/dist/resolve.task.js +38 -0
- package/dist/resolve_specifier.d.ts +22 -0
- package/dist/resolve_specifier.d.ts.map +1 -0
- package/dist/resolve_specifier.js +57 -0
- package/dist/run.task.d.ts +16 -0
- package/dist/run.task.d.ts.map +1 -0
- package/dist/run.task.js +52 -0
- package/dist/run_gen.d.ts +10 -0
- package/dist/run_gen.d.ts.map +1 -0
- package/dist/run_gen.js +73 -0
- package/dist/run_task.d.ts +17 -0
- package/dist/run_task.d.ts.map +1 -0
- package/dist/run_task.js +45 -0
- package/dist/source_json.d.ts +7 -0
- package/dist/source_json.d.ts.map +1 -0
- package/dist/source_json.js +145 -0
- package/dist/svelte_config.d.ts +57 -0
- package/dist/svelte_config.d.ts.map +1 -0
- package/dist/svelte_config.js +81 -0
- package/dist/sveltekit_helpers.d.ts +75 -0
- package/dist/sveltekit_helpers.d.ts.map +1 -0
- package/dist/sveltekit_helpers.js +94 -0
- package/dist/sveltekit_shim_app.d.ts +11 -0
- package/dist/sveltekit_shim_app.d.ts.map +1 -0
- package/dist/sveltekit_shim_app.js +31 -0
- package/dist/sveltekit_shim_app_environment.d.ts +13 -0
- package/dist/sveltekit_shim_app_environment.d.ts.map +1 -0
- package/dist/sveltekit_shim_app_environment.js +14 -0
- package/dist/sveltekit_shim_app_forms.d.ts +5 -0
- package/dist/sveltekit_shim_app_forms.d.ts.map +1 -0
- package/dist/sveltekit_shim_app_forms.js +6 -0
- package/dist/sveltekit_shim_app_navigation.d.ts +10 -0
- package/dist/sveltekit_shim_app_navigation.d.ts.map +1 -0
- package/dist/sveltekit_shim_app_navigation.js +11 -0
- package/dist/sveltekit_shim_app_paths.d.ts +17 -0
- package/dist/sveltekit_shim_app_paths.d.ts.map +1 -0
- package/dist/sveltekit_shim_app_paths.js +10 -0
- package/dist/sveltekit_shim_app_state.d.ts +5 -0
- package/dist/sveltekit_shim_app_state.d.ts.map +1 -0
- package/dist/sveltekit_shim_app_state.js +26 -0
- package/dist/sveltekit_shim_env.d.ts +5 -0
- package/dist/sveltekit_shim_env.d.ts.map +1 -0
- package/dist/sveltekit_shim_env.js +23 -0
- package/dist/sync.task.d.ts +16 -0
- package/dist/sync.task.d.ts.map +1 -0
- package/dist/sync.task.js +39 -0
- package/dist/task.d.ts +98 -0
- package/dist/task.d.ts.map +1 -0
- package/dist/task.js +109 -0
- package/dist/task_logging.d.ts +6 -0
- package/dist/task_logging.d.ts.map +1 -0
- package/dist/task_logging.js +201 -0
- package/dist/test.task.d.ts +13 -0
- package/dist/test.task.d.ts.map +1 -0
- package/dist/test.task.js +53 -0
- package/dist/typecheck.task.d.ts +13 -0
- package/dist/typecheck.task.d.ts.map +1 -0
- package/dist/typecheck.task.js +68 -0
- package/dist/upgrade.task.d.ts +20 -0
- package/dist/upgrade.task.d.ts.map +1 -0
- package/dist/upgrade.task.js +111 -0
- package/dist/watch_dir.d.ts +36 -0
- package/dist/watch_dir.d.ts.map +1 -0
- package/dist/watch_dir.js +69 -0
- package/package.json +149 -0
- package/src/lib/args.ts +115 -0
- package/src/lib/build.task.ts +151 -0
- package/src/lib/build_cache.ts +378 -0
- package/src/lib/changelog.ts +69 -0
- package/src/lib/changeset.task.ts +228 -0
- package/src/lib/changeset_helpers.ts +14 -0
- package/src/lib/check.task.ts +132 -0
- package/src/lib/child_process_logging.ts +38 -0
- package/src/lib/clean.task.ts +48 -0
- package/src/lib/clean_fs.ts +54 -0
- package/src/lib/cli.ts +98 -0
- package/src/lib/commit.task.ts +34 -0
- package/src/lib/constants.ts +56 -0
- package/src/lib/deploy.task.ts +287 -0
- package/src/lib/dev.task.ts +52 -0
- package/src/lib/disknode.ts +26 -0
- package/src/lib/env.ts +78 -0
- package/src/lib/esbuild_helpers.ts +49 -0
- package/src/lib/esbuild_plugin_external_worker.ts +94 -0
- package/src/lib/esbuild_plugin_svelte.ts +134 -0
- package/src/lib/esbuild_plugin_sveltekit_local_imports.ts +38 -0
- package/src/lib/esbuild_plugin_sveltekit_shim_alias.ts +27 -0
- package/src/lib/esbuild_plugin_sveltekit_shim_app.ts +42 -0
- package/src/lib/esbuild_plugin_sveltekit_shim_env.ts +47 -0
- package/src/lib/filer.ts +458 -0
- package/src/lib/format.task.ts +44 -0
- package/src/lib/format_directory.ts +65 -0
- package/src/lib/format_file.ts +49 -0
- package/src/lib/gen.task.ts +206 -0
- package/src/lib/gen.ts +406 -0
- package/src/lib/gen_helpers.ts +131 -0
- package/src/lib/github.ts +46 -0
- package/src/lib/gro.config.default.ts +42 -0
- package/src/lib/gro.ts +29 -0
- package/src/lib/gro_config.ts +254 -0
- package/src/lib/gro_helpers.ts +108 -0
- package/src/lib/gro_plugin_gen.ts +149 -0
- package/src/lib/gro_plugin_server.ts +288 -0
- package/src/lib/gro_plugin_sveltekit_app.ts +58 -0
- package/src/lib/gro_plugin_sveltekit_library.ts +63 -0
- package/src/lib/index.ts +8 -0
- package/src/lib/input_path.ts +254 -0
- package/src/lib/invoke.ts +34 -0
- package/src/lib/invoke_task.ts +139 -0
- package/src/lib/lint.task.ts +39 -0
- package/src/lib/loader.ts +229 -0
- package/src/lib/module.ts +13 -0
- package/src/lib/modules.ts +117 -0
- package/src/lib/package_json.ts +255 -0
- package/src/lib/parse_exports.ts +100 -0
- package/src/lib/parse_exports_context.ts +395 -0
- package/src/lib/parse_imports.ts +180 -0
- package/src/lib/paths.ts +111 -0
- package/src/lib/plugin.ts +106 -0
- package/src/lib/publish.task.ts +228 -0
- package/src/lib/register.ts +3 -0
- package/src/lib/reinstall.task.ts +45 -0
- package/src/lib/release.task.ts +26 -0
- package/src/lib/resolve.task.ts +43 -0
- package/src/lib/resolve_specifier.ts +81 -0
- package/src/lib/run.task.ts +65 -0
- package/src/lib/run_gen.ts +110 -0
- package/src/lib/run_task.ts +82 -0
- package/src/lib/source_json.ts +183 -0
- package/src/lib/svelte_config.ts +140 -0
- package/src/lib/sveltekit_helpers.ts +193 -0
- package/src/lib/sveltekit_shim_app.ts +41 -0
- package/src/lib/sveltekit_shim_app_environment.ts +16 -0
- package/src/lib/sveltekit_shim_app_forms.ts +13 -0
- package/src/lib/sveltekit_shim_app_navigation.ts +23 -0
- package/src/lib/sveltekit_shim_app_paths.ts +26 -0
- package/src/lib/sveltekit_shim_app_state.ts +35 -0
- package/src/lib/sveltekit_shim_env.ts +45 -0
- package/src/lib/sync.task.ts +47 -0
- package/src/lib/task.ts +245 -0
- package/src/lib/task_logging.ts +255 -0
- package/src/lib/test.task.ts +63 -0
- package/src/lib/typecheck.task.ts +81 -0
- package/src/lib/upgrade.task.ts +148 -0
- package/src/lib/watch_dir.ts +115 -0
|
@@ -0,0 +1,255 @@
|
|
|
1
|
+
import type {ArgSchema} from '@fuzdev/fuz_util/args.js';
|
|
2
|
+
import type {Logger} from '@fuzdev/fuz_util/log.js';
|
|
3
|
+
import {print_value} from '@fuzdev/fuz_util/print.js';
|
|
4
|
+
import {plural} from '@fuzdev/fuz_util/string.js';
|
|
5
|
+
import {styleText as st} from 'node:util';
|
|
6
|
+
import {z} from 'zod';
|
|
7
|
+
import type {LoadedTasks, TaskModuleMeta} from './task.ts';
|
|
8
|
+
import {print_path} from './paths.ts';
|
|
9
|
+
|
|
10
|
+
export const log_tasks = (log: Logger, loaded_tasks: LoadedTasks, log_intro = true): void => {
|
|
11
|
+
const {modules, found_tasks} = loaded_tasks;
|
|
12
|
+
const {resolved_input_files_by_root_dir} = found_tasks;
|
|
13
|
+
|
|
14
|
+
const logged: Array<string> = [];
|
|
15
|
+
if (log_intro) {
|
|
16
|
+
logged.unshift(
|
|
17
|
+
`\n\n${st('gray', 'Run a task:')} gro [name]`,
|
|
18
|
+
`\n${st('gray', 'View help:')} gro [name] --help`,
|
|
19
|
+
);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
for (const [root_dir, resolved_input_files] of resolved_input_files_by_root_dir) {
|
|
23
|
+
const dir_label = print_path(root_dir);
|
|
24
|
+
if (!resolved_input_files.length) {
|
|
25
|
+
log.info(`No tasks found in ${dir_label}.`);
|
|
26
|
+
continue;
|
|
27
|
+
}
|
|
28
|
+
logged.push(
|
|
29
|
+
`${log_intro ? '\n\n' : ''}${resolved_input_files.length} task${plural(
|
|
30
|
+
resolved_input_files.length,
|
|
31
|
+
)} in ${dir_label}:\n`,
|
|
32
|
+
);
|
|
33
|
+
const longest_task_name = to_max_length(modules, (m) => m.name);
|
|
34
|
+
for (const resolved_input_file of resolved_input_files) {
|
|
35
|
+
const meta = modules.find((m) => m.id === resolved_input_file.id)!;
|
|
36
|
+
logged.push(
|
|
37
|
+
'\n' + st('cyan', meta.name.padEnd(longest_task_name)),
|
|
38
|
+
' ',
|
|
39
|
+
meta.mod.task.summary ?? '',
|
|
40
|
+
);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
log[log_intro ? 'info' : 'raw'](logged.join('') + '\n');
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
export const log_error_reasons = (log: Logger, reasons: Array<string>): void => {
|
|
47
|
+
for (const reason of reasons) {
|
|
48
|
+
log.error(st('red', reason));
|
|
49
|
+
}
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
const ARGS_PROPERTY_NAME = '[...args]';
|
|
53
|
+
|
|
54
|
+
export const log_task_help = (log: Logger, meta: TaskModuleMeta): void => {
|
|
55
|
+
const {
|
|
56
|
+
name,
|
|
57
|
+
mod: {task},
|
|
58
|
+
} = meta;
|
|
59
|
+
const logged: Array<string> = [];
|
|
60
|
+
logged.push(
|
|
61
|
+
st('cyan', name),
|
|
62
|
+
'help',
|
|
63
|
+
st('cyan', `\n\ngro ${name}`) + `: ${task.summary ?? '(no summary available)'}\n`,
|
|
64
|
+
);
|
|
65
|
+
if (task.Args) {
|
|
66
|
+
const properties = to_arg_properties(task.Args, meta, log);
|
|
67
|
+
// TODO hacky padding for some quick and dirty tables
|
|
68
|
+
const longest_task_name = Math.max(
|
|
69
|
+
ARGS_PROPERTY_NAME.length,
|
|
70
|
+
to_max_length(properties, (p) => p.name),
|
|
71
|
+
);
|
|
72
|
+
const longest_type = to_max_length(properties, (p) => p.schema.type);
|
|
73
|
+
const longest_default = to_max_length(properties, (p) => print_value(p.schema.default));
|
|
74
|
+
for (const property of properties) {
|
|
75
|
+
const name = property.name === '_' ? ARGS_PROPERTY_NAME : property.name;
|
|
76
|
+
logged.push(
|
|
77
|
+
`\n${st('green', name.padEnd(longest_task_name))} `,
|
|
78
|
+
st('gray', property.schema.type.padEnd(longest_type)) + ' ',
|
|
79
|
+
print_value(property.schema.default).padEnd(longest_default) + ' ',
|
|
80
|
+
property.schema.description || '(no description available)',
|
|
81
|
+
);
|
|
82
|
+
}
|
|
83
|
+
if (!properties.length) {
|
|
84
|
+
logged.push('\n' + st('gray', 'this task has no args'));
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
log.info(...logged, '\n');
|
|
88
|
+
};
|
|
89
|
+
|
|
90
|
+
// TODO rework all of this
|
|
91
|
+
// The following Zod helpers only need to support single-depth schemas for CLI args,
|
|
92
|
+
// but there's generic recursion to handle things like `ZodOptional` and `ZodDefault`.
|
|
93
|
+
|
|
94
|
+
interface ArgSchemaProperty {
|
|
95
|
+
name: string;
|
|
96
|
+
schema: ArgSchema;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// TODO this blocks many usecases like unions, and it's only implemented for CLI arg types, need better support for arbitrary schemas
|
|
100
|
+
const to_arg_properties = (
|
|
101
|
+
schema: z.ZodType,
|
|
102
|
+
meta: TaskModuleMeta,
|
|
103
|
+
log: Logger,
|
|
104
|
+
): Array<ArgSchemaProperty> => {
|
|
105
|
+
const {def} = schema;
|
|
106
|
+
|
|
107
|
+
// TODO overly restrictive, support optional objects and/or unions?
|
|
108
|
+
if (!('shape' in def)) {
|
|
109
|
+
log.error(`Expected Args for task "${meta.name}" to be an object schema but got ${def.type}`);
|
|
110
|
+
return [];
|
|
111
|
+
}
|
|
112
|
+
const shape = (def as z.core.$ZodObjectDef).shape;
|
|
113
|
+
|
|
114
|
+
const properties: Array<ArgSchemaProperty> = [];
|
|
115
|
+
for (const name in shape) {
|
|
116
|
+
if ('no-' + name in shape) continue;
|
|
117
|
+
const s = shape[name] as z.ZodType;
|
|
118
|
+
const schema: ArgSchema = {
|
|
119
|
+
type: to_args_schema_type(s),
|
|
120
|
+
description: to_args_schema_description(s) || '',
|
|
121
|
+
default: to_args_schema_default(s),
|
|
122
|
+
};
|
|
123
|
+
properties.push({name, schema});
|
|
124
|
+
}
|
|
125
|
+
return properties;
|
|
126
|
+
};
|
|
127
|
+
|
|
128
|
+
const to_max_length = <T>(items: Array<T>, toString: (item: T) => string) =>
|
|
129
|
+
items.reduce((max, m) => Math.max(toString(m).length, max), 0);
|
|
130
|
+
|
|
131
|
+
const to_args_schema_type = (schema: z.ZodType): ArgSchema['type'] => {
|
|
132
|
+
const {def} = schema._zod;
|
|
133
|
+
switch (def.type) {
|
|
134
|
+
case 'string':
|
|
135
|
+
return 'string';
|
|
136
|
+
case 'number':
|
|
137
|
+
return 'number';
|
|
138
|
+
case 'int':
|
|
139
|
+
return 'int';
|
|
140
|
+
case 'boolean':
|
|
141
|
+
return 'boolean';
|
|
142
|
+
case 'bigint':
|
|
143
|
+
return 'bigint';
|
|
144
|
+
case 'symbol':
|
|
145
|
+
return 'symbol';
|
|
146
|
+
case 'null':
|
|
147
|
+
return 'null';
|
|
148
|
+
case 'undefined':
|
|
149
|
+
return 'undefined';
|
|
150
|
+
case 'void':
|
|
151
|
+
return 'void';
|
|
152
|
+
case 'never':
|
|
153
|
+
return 'never';
|
|
154
|
+
case 'any':
|
|
155
|
+
return 'any';
|
|
156
|
+
case 'unknown':
|
|
157
|
+
return 'unknown';
|
|
158
|
+
case 'date':
|
|
159
|
+
return 'date';
|
|
160
|
+
case 'object':
|
|
161
|
+
return 'object';
|
|
162
|
+
case 'record':
|
|
163
|
+
return 'record';
|
|
164
|
+
case 'file':
|
|
165
|
+
return 'file';
|
|
166
|
+
case 'array':
|
|
167
|
+
// TODO other types, only handling a subset of CLI arg cases
|
|
168
|
+
return 'Array<string>';
|
|
169
|
+
case 'tuple':
|
|
170
|
+
return 'tuple';
|
|
171
|
+
case 'union':
|
|
172
|
+
// TODO fix, this is a hacky way to handle unions for CLI args
|
|
173
|
+
return 'string | Array<string>';
|
|
174
|
+
case 'intersection':
|
|
175
|
+
return 'intersection';
|
|
176
|
+
case 'map':
|
|
177
|
+
return 'map';
|
|
178
|
+
case 'set':
|
|
179
|
+
return 'set';
|
|
180
|
+
case 'enum':
|
|
181
|
+
return (schema as unknown as {options: Array<string>}).options
|
|
182
|
+
.map((v) => `'${v}'`)
|
|
183
|
+
.join(' | ');
|
|
184
|
+
case 'literal':
|
|
185
|
+
return (def as unknown as {values: Array<any>}).values.map((v) => print_value(v)).join(' | ');
|
|
186
|
+
case 'nullable': {
|
|
187
|
+
const subschema = to_subschema(def);
|
|
188
|
+
return subschema ? to_args_schema_type(subschema) + ' | null' : 'nullable';
|
|
189
|
+
}
|
|
190
|
+
case 'optional': {
|
|
191
|
+
const subschema = to_subschema(def);
|
|
192
|
+
return subschema ? to_args_schema_type(subschema) + ' | undefined' : 'optional';
|
|
193
|
+
}
|
|
194
|
+
case 'success':
|
|
195
|
+
return 'success';
|
|
196
|
+
case 'catch':
|
|
197
|
+
return 'catch';
|
|
198
|
+
case 'nan':
|
|
199
|
+
return 'NaN';
|
|
200
|
+
case 'readonly':
|
|
201
|
+
return 'readonly';
|
|
202
|
+
case 'template_literal':
|
|
203
|
+
return 'template_literal';
|
|
204
|
+
case 'promise':
|
|
205
|
+
return 'promise';
|
|
206
|
+
case 'lazy':
|
|
207
|
+
return 'lazy';
|
|
208
|
+
case 'custom':
|
|
209
|
+
return 'custom';
|
|
210
|
+
// Unwrap these:
|
|
211
|
+
// case 'nonoptional':
|
|
212
|
+
// case 'transform':
|
|
213
|
+
// case 'default':
|
|
214
|
+
// case 'prefault':
|
|
215
|
+
// case 'pipe':
|
|
216
|
+
default: {
|
|
217
|
+
const subschema = to_subschema(def);
|
|
218
|
+
return subschema ? to_args_schema_type(subschema) : def.type;
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
};
|
|
222
|
+
|
|
223
|
+
const to_args_schema_description = (schema: z.ZodType): string | null => {
|
|
224
|
+
const meta = schema.meta();
|
|
225
|
+
if (meta?.description) {
|
|
226
|
+
return meta.description;
|
|
227
|
+
}
|
|
228
|
+
const subschema = to_subschema(schema.def);
|
|
229
|
+
if (subschema) {
|
|
230
|
+
return to_args_schema_description(subschema);
|
|
231
|
+
}
|
|
232
|
+
return null;
|
|
233
|
+
};
|
|
234
|
+
|
|
235
|
+
const to_args_schema_default = (schema: z.ZodType): any => {
|
|
236
|
+
const {def} = schema._zod;
|
|
237
|
+
if ('defaultValue' in def) {
|
|
238
|
+
return def.defaultValue;
|
|
239
|
+
}
|
|
240
|
+
const subschema = to_subschema(def);
|
|
241
|
+
if (subschema) {
|
|
242
|
+
return to_args_schema_default(subschema);
|
|
243
|
+
}
|
|
244
|
+
};
|
|
245
|
+
|
|
246
|
+
const to_subschema = (def: z.core.$ZodTypeDef): z.ZodType | undefined => {
|
|
247
|
+
if ('innerType' in def) {
|
|
248
|
+
return def.innerType as any;
|
|
249
|
+
} else if ('in' in def) {
|
|
250
|
+
return def.in as any;
|
|
251
|
+
} else if ('schema' in def) {
|
|
252
|
+
return def.schema as any;
|
|
253
|
+
}
|
|
254
|
+
return undefined;
|
|
255
|
+
};
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import {args_serialize} from '@fuzdev/fuz_util/args.js';
|
|
2
|
+
import {spawn_result_to_message} from '@fuzdev/fuz_util/process.js';
|
|
3
|
+
import {spawn_cli} from '@fuzdev/gro/cli.js';
|
|
4
|
+
import {z} from 'zod';
|
|
5
|
+
|
|
6
|
+
import {to_implicit_forwarded_args} from './args.ts';
|
|
7
|
+
import {find_cli} from './cli.ts';
|
|
8
|
+
import {VITEST_CLI} from './constants.ts';
|
|
9
|
+
import {package_json_has_dependency, package_json_load} from './package_json.ts';
|
|
10
|
+
import {paths} from './paths.ts';
|
|
11
|
+
import {TaskError, type Task} from './task.ts';
|
|
12
|
+
|
|
13
|
+
/** @nodocs */
|
|
14
|
+
export const Args = z.strictObject({
|
|
15
|
+
_: z.array(z.string()).meta({description: 'file patterns to filter tests'}).default(['.test.']),
|
|
16
|
+
dir: z.string().meta({description: 'working directory for tests'}).default(paths.source),
|
|
17
|
+
fail_without_tests: z
|
|
18
|
+
.boolean()
|
|
19
|
+
.meta({description: 'opt out of `passWithNoTests`'})
|
|
20
|
+
.default(false),
|
|
21
|
+
t: z
|
|
22
|
+
.string()
|
|
23
|
+
.meta({description: 'search pattern for test names, same as vitest -t and --testNamePattern'})
|
|
24
|
+
.optional(),
|
|
25
|
+
});
|
|
26
|
+
export type Args = z.infer<typeof Args>;
|
|
27
|
+
|
|
28
|
+
/** @nodocs */
|
|
29
|
+
export const task: Task<Args> = {
|
|
30
|
+
summary: 'run tests with vitest',
|
|
31
|
+
Args,
|
|
32
|
+
run: async ({args}): Promise<void> => {
|
|
33
|
+
const {_: patterns, dir, fail_without_tests, t} = args;
|
|
34
|
+
|
|
35
|
+
const package_json = await package_json_load();
|
|
36
|
+
if (!package_json_has_dependency(VITEST_CLI, package_json)) {
|
|
37
|
+
throw new TaskError('no test runner found, install vitest');
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
if (!(await find_cli(VITEST_CLI))) {
|
|
41
|
+
throw new TaskError('vitest is a dependency but not installed; run `npm i`?');
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
const vitest_args = ['run', ...patterns];
|
|
45
|
+
if (dir) {
|
|
46
|
+
vitest_args.push('--dir', dir);
|
|
47
|
+
}
|
|
48
|
+
if (!fail_without_tests) {
|
|
49
|
+
vitest_args.push('--passWithNoTests');
|
|
50
|
+
}
|
|
51
|
+
if (t) {
|
|
52
|
+
vitest_args.push('-t', t);
|
|
53
|
+
}
|
|
54
|
+
vitest_args.push(...args_serialize(to_implicit_forwarded_args(VITEST_CLI)));
|
|
55
|
+
|
|
56
|
+
const spawned = await spawn_cli(VITEST_CLI, vitest_args);
|
|
57
|
+
if (!spawned?.ok) {
|
|
58
|
+
throw new TaskError(
|
|
59
|
+
`vitest failed: ${spawned ? spawn_result_to_message(spawned) : 'unknown error'}`,
|
|
60
|
+
);
|
|
61
|
+
}
|
|
62
|
+
},
|
|
63
|
+
};
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import {args_serialize} from '@fuzdev/fuz_util/args.js';
|
|
2
|
+
import {print_spawn_result} from '@fuzdev/fuz_util/process.js';
|
|
3
|
+
import {z} from 'zod';
|
|
4
|
+
|
|
5
|
+
import {to_forwarded_args} from './args.ts';
|
|
6
|
+
import {configure_colored_output_with_path_replacement} from './child_process_logging.ts';
|
|
7
|
+
import {find_cli, spawn_cli, spawn_cli_process} from './cli.ts';
|
|
8
|
+
import {SVELTE_CHECK_CLI} from './constants.ts';
|
|
9
|
+
import {paths} from './paths.ts';
|
|
10
|
+
import {sveltekit_sync_if_available} from './sveltekit_helpers.ts';
|
|
11
|
+
import {TaskError, type Task} from './task.ts';
|
|
12
|
+
|
|
13
|
+
/** @nodocs */
|
|
14
|
+
export const Args = z.strictObject({
|
|
15
|
+
svelte_check_cli: z
|
|
16
|
+
.string()
|
|
17
|
+
.meta({description: 'the svelte-check CLI to use'})
|
|
18
|
+
.default(SVELTE_CHECK_CLI),
|
|
19
|
+
typescript_cli: z
|
|
20
|
+
.string()
|
|
21
|
+
.meta({description: 'the TypeScript CLI to use as a fallback to svelte-check'})
|
|
22
|
+
.default('tsc'),
|
|
23
|
+
path_replacement: z
|
|
24
|
+
.string()
|
|
25
|
+
.meta({description: 'replacement string for current working directory in output'})
|
|
26
|
+
.default('.'),
|
|
27
|
+
cwd: z.string().meta({description: 'current working directory'}).default(paths.root),
|
|
28
|
+
});
|
|
29
|
+
export type Args = z.infer<typeof Args>;
|
|
30
|
+
|
|
31
|
+
/** @nodocs */
|
|
32
|
+
export const task: Task<Args> = {
|
|
33
|
+
summary: 'run svelte-check or tsc on the project without emitting any files',
|
|
34
|
+
Args,
|
|
35
|
+
run: async ({args, log}): Promise<void> => {
|
|
36
|
+
const {svelte_check_cli, typescript_cli, path_replacement, cwd} = args;
|
|
37
|
+
|
|
38
|
+
await sveltekit_sync_if_available();
|
|
39
|
+
|
|
40
|
+
// Prefer svelte-check if available.
|
|
41
|
+
const found_svelte_check_cli = await find_cli(svelte_check_cli);
|
|
42
|
+
if (found_svelte_check_cli) {
|
|
43
|
+
const serialized = args_serialize(to_forwarded_args(svelte_check_cli));
|
|
44
|
+
const spawned = await spawn_cli_process(found_svelte_check_cli, serialized, undefined, {
|
|
45
|
+
stdio: ['inherit', 'pipe', 'pipe'],
|
|
46
|
+
env: {...process.env, FORCE_COLOR: '1'}, // Needed for colors (maybe make an option)
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
const svelte_check_process = spawned?.child;
|
|
50
|
+
if (svelte_check_process) {
|
|
51
|
+
// Configure process output with path replacement while preserving colors
|
|
52
|
+
configure_colored_output_with_path_replacement(svelte_check_process, path_replacement, cwd);
|
|
53
|
+
|
|
54
|
+
const svelte_check_result = await spawned.closed;
|
|
55
|
+
|
|
56
|
+
if (!svelte_check_result.ok) {
|
|
57
|
+
throw new TaskError(`Failed to typecheck. ${print_spawn_result(svelte_check_result)}`);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
return;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// Fall back to tsc.
|
|
65
|
+
const found_typescript_cli = await find_cli(typescript_cli);
|
|
66
|
+
if (found_typescript_cli) {
|
|
67
|
+
const forwarded = to_forwarded_args(typescript_cli);
|
|
68
|
+
if (!forwarded.noEmit) forwarded.noEmit = true;
|
|
69
|
+
const serialized = args_serialize(forwarded);
|
|
70
|
+
const svelte_check_result = await spawn_cli(found_typescript_cli, serialized, log);
|
|
71
|
+
if (!svelte_check_result?.ok) {
|
|
72
|
+
throw new TaskError(`Failed to typecheck. ${print_spawn_result(svelte_check_result!)}`);
|
|
73
|
+
}
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
throw new TaskError(
|
|
78
|
+
`Failed to typecheck because neither \`${svelte_check_cli}\` nor \`${typescript_cli}\` was found`,
|
|
79
|
+
);
|
|
80
|
+
},
|
|
81
|
+
};
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
import {args_serialize} from '@fuzdev/fuz_util/args.js';
|
|
2
|
+
import {GitOrigin, git_pull} from '@fuzdev/fuz_util/git.js';
|
|
3
|
+
import {spawn} from '@fuzdev/fuz_util/process.js';
|
|
4
|
+
import {rm} from 'node:fs/promises';
|
|
5
|
+
import {z} from 'zod';
|
|
6
|
+
|
|
7
|
+
import {to_forwarded_args} from './args.ts';
|
|
8
|
+
import {spawn_cli} from './cli.ts';
|
|
9
|
+
import {NODE_MODULES_DIRNAME} from './constants.ts';
|
|
10
|
+
import {
|
|
11
|
+
package_json_extract_dependencies,
|
|
12
|
+
package_json_load,
|
|
13
|
+
type PackageJsonDep,
|
|
14
|
+
} from './package_json.ts';
|
|
15
|
+
import {TaskError, type Task} from './task.ts';
|
|
16
|
+
|
|
17
|
+
/** @nodocs */
|
|
18
|
+
export const Args = z.strictObject({
|
|
19
|
+
_: z
|
|
20
|
+
.array(z.string())
|
|
21
|
+
.meta({description: 'names of deps to exclude from the upgrade'})
|
|
22
|
+
.default([]),
|
|
23
|
+
only: z
|
|
24
|
+
.union([z.string(), z.array(z.string())])
|
|
25
|
+
.meta({
|
|
26
|
+
description: 'names of deps to include in the upgrade',
|
|
27
|
+
})
|
|
28
|
+
.default([])
|
|
29
|
+
.transform((v) => (Array.isArray(v) ? v : [v])),
|
|
30
|
+
origin: GitOrigin.describe('git origin to deploy to').default('origin'),
|
|
31
|
+
force: z.boolean().meta({description: 'if true, print out the planned upgrades'}).default(false),
|
|
32
|
+
pull: z.boolean().meta({description: 'dual of no-pull'}).default(true),
|
|
33
|
+
'no-pull': z.boolean().meta({description: 'opt out of git pull'}).default(false),
|
|
34
|
+
delete_node_modules: z
|
|
35
|
+
.boolean()
|
|
36
|
+
.meta({description: 'if true, deletes node_modules before upgrading'})
|
|
37
|
+
.default(false),
|
|
38
|
+
node_modules_path: z // TODO maybe configured globally instead
|
|
39
|
+
.string()
|
|
40
|
+
.meta({description: 'path to modules directory to delete'})
|
|
41
|
+
.default(NODE_MODULES_DIRNAME),
|
|
42
|
+
delete_lockfile: z
|
|
43
|
+
.boolean()
|
|
44
|
+
.meta({description: 'if true, deletes the lockfile before upgrading'})
|
|
45
|
+
.default(false),
|
|
46
|
+
lockfile_path: z
|
|
47
|
+
.string()
|
|
48
|
+
.meta({description: 'path to the lockfile to delete'})
|
|
49
|
+
.default('package-lock.json'),
|
|
50
|
+
dry: z.boolean().meta({description: 'if true, print out the planned upgrades'}).default(false),
|
|
51
|
+
});
|
|
52
|
+
export type Args = z.infer<typeof Args>;
|
|
53
|
+
|
|
54
|
+
/** @nodocs */
|
|
55
|
+
export const task: Task<Args> = {
|
|
56
|
+
summary: 'upgrade deps',
|
|
57
|
+
Args,
|
|
58
|
+
run: async ({args, log, config}): Promise<void> => {
|
|
59
|
+
const {
|
|
60
|
+
_,
|
|
61
|
+
only,
|
|
62
|
+
origin,
|
|
63
|
+
force,
|
|
64
|
+
pull,
|
|
65
|
+
delete_node_modules,
|
|
66
|
+
node_modules_path,
|
|
67
|
+
delete_lockfile,
|
|
68
|
+
lockfile_path,
|
|
69
|
+
dry,
|
|
70
|
+
} = args;
|
|
71
|
+
|
|
72
|
+
if (_.length && only.length) {
|
|
73
|
+
throw new TaskError('Cannot call `gro upgrade` with both rest args and --only.');
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// TODO maybe a different task that pulls and does other things, like `gro ready`
|
|
77
|
+
if (pull) {
|
|
78
|
+
await git_pull(origin);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
if (delete_node_modules) {
|
|
82
|
+
log.info(`deleting node_modules at `, node_modules_path);
|
|
83
|
+
await rm(node_modules_path, {recursive: true, force: true});
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
if (delete_lockfile) {
|
|
87
|
+
log.info(`deleting lockfile at`, lockfile_path);
|
|
88
|
+
await rm(lockfile_path, {force: true});
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
const package_json = await package_json_load();
|
|
92
|
+
|
|
93
|
+
const all_deps = package_json_extract_dependencies(package_json);
|
|
94
|
+
|
|
95
|
+
const deps = only.length
|
|
96
|
+
? all_deps.filter((d) => only.includes(d.name))
|
|
97
|
+
: all_deps.filter((d) => !_.includes(d.name));
|
|
98
|
+
|
|
99
|
+
if (only.length && only.length !== deps.length) {
|
|
100
|
+
throw new TaskError(
|
|
101
|
+
`Some deps to upgrade were not found: ${only.filter((o) => !deps.find((d) => d.name === o)).join(', ')}`,
|
|
102
|
+
);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
const upgrade_items = to_upgrade_items(deps);
|
|
106
|
+
|
|
107
|
+
log.info(`upgrading:`, upgrade_items.join(' '));
|
|
108
|
+
|
|
109
|
+
const install_args = ['install'].concat(upgrade_items);
|
|
110
|
+
if (dry) {
|
|
111
|
+
install_args.push('--dry-run');
|
|
112
|
+
log.info(`deps`, deps);
|
|
113
|
+
}
|
|
114
|
+
if (force) {
|
|
115
|
+
install_args.push('--force');
|
|
116
|
+
}
|
|
117
|
+
install_args.push(...args_serialize(to_forwarded_args(config.pm_cli)));
|
|
118
|
+
await spawn(config.pm_cli, install_args);
|
|
119
|
+
|
|
120
|
+
// TODO @many this relies on npm behavior that changed in v11
|
|
121
|
+
// If we deleted the lockfile or node modules, `npm install` again
|
|
122
|
+
// to fix a recurring npm bug getting the lockfile to its final state.
|
|
123
|
+
if (!dry && (delete_node_modules || delete_lockfile)) {
|
|
124
|
+
log.info(`installing again to fix npm lockfile bugs`);
|
|
125
|
+
await spawn(config.pm_cli, ['install']);
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
// Sync in a new process to pick up any changes after installing, avoiding some errors.
|
|
129
|
+
await spawn_cli('gro', ['sync']); // don't install because we do above
|
|
130
|
+
},
|
|
131
|
+
};
|
|
132
|
+
|
|
133
|
+
const EXACT_VERSION_MATCHER = /^..*@.+/;
|
|
134
|
+
const CUSTOM_TAG_MATCHER = /^[\^~><=]*.+-(.+)/;
|
|
135
|
+
|
|
136
|
+
// TODO hacky and limited
|
|
137
|
+
// TODO probably want to pass through exact deps as well, e.g. @foo/bar@1
|
|
138
|
+
const to_upgrade_items = (deps: Array<PackageJsonDep>): Array<string> =>
|
|
139
|
+
deps.map((dep) => {
|
|
140
|
+
if (EXACT_VERSION_MATCHER.test(dep.name)) {
|
|
141
|
+
return dep.name;
|
|
142
|
+
}
|
|
143
|
+
const custom_tag_matches = CUSTOM_TAG_MATCHER.exec(dep.version);
|
|
144
|
+
if (custom_tag_matches) {
|
|
145
|
+
return dep.name + '@' + custom_tag_matches[1]!.split('.')[0]; // I tried adding `\.?` to the end but doesn't work and I'm being lazy so I'm just splitting
|
|
146
|
+
}
|
|
147
|
+
return dep.name + '@latest';
|
|
148
|
+
});
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
import {watch, type ChokidarOptions, type FSWatcher, type Matcher} from 'chokidar';
|
|
2
|
+
import {relative} from 'node:path';
|
|
3
|
+
import {statSync} from 'node:fs';
|
|
4
|
+
import {create_deferred, type Deferred} from '@fuzdev/fuz_util/async.js';
|
|
5
|
+
import type {PathFilter} from '@fuzdev/fuz_util/path.js';
|
|
6
|
+
import {EMPTY_OBJECT} from '@fuzdev/fuz_util/object.js';
|
|
7
|
+
|
|
8
|
+
const TMP_FILE_PATTERN = /\.tmp\./;
|
|
9
|
+
|
|
10
|
+
// TODO pretty hacky
|
|
11
|
+
|
|
12
|
+
export interface WatchNodeFs {
|
|
13
|
+
init: () => Promise<void>;
|
|
14
|
+
close: () => Promise<void>;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export interface WatcherChange {
|
|
18
|
+
type: WatcherChangeType;
|
|
19
|
+
path: string;
|
|
20
|
+
is_directory: boolean;
|
|
21
|
+
}
|
|
22
|
+
export type WatcherChangeType = 'add' | 'update' | 'delete';
|
|
23
|
+
export type WatcherChangeCallback = (change: WatcherChange) => void;
|
|
24
|
+
|
|
25
|
+
export interface WatchDirOptions {
|
|
26
|
+
dir: string;
|
|
27
|
+
on_change: WatcherChangeCallback;
|
|
28
|
+
filter?: PathFilter | null | undefined;
|
|
29
|
+
chokidar?: ChokidarOptions;
|
|
30
|
+
/**
|
|
31
|
+
* When `false`, returns the `path` relative to `dir`.
|
|
32
|
+
* @default true
|
|
33
|
+
*/
|
|
34
|
+
absolute?: boolean;
|
|
35
|
+
/**
|
|
36
|
+
* Pattern to ignore files, merged into `chokidar.ignored` if also provided.
|
|
37
|
+
* - `undefined` (default) ignores files matching `.tmp.` pattern
|
|
38
|
+
* - `null` sets no default ignore pattern
|
|
39
|
+
* - or some custom pattern
|
|
40
|
+
*/
|
|
41
|
+
ignored?: Matcher | null;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Watch for changes on the filesystem using chokidar.
|
|
46
|
+
*/
|
|
47
|
+
export const watch_dir = ({
|
|
48
|
+
dir,
|
|
49
|
+
on_change,
|
|
50
|
+
filter,
|
|
51
|
+
absolute = true,
|
|
52
|
+
chokidar,
|
|
53
|
+
ignored = TMP_FILE_PATTERN,
|
|
54
|
+
}: WatchDirOptions): WatchNodeFs => {
|
|
55
|
+
let watcher: FSWatcher | undefined;
|
|
56
|
+
let initing: Deferred<void> | undefined;
|
|
57
|
+
|
|
58
|
+
return {
|
|
59
|
+
init: async () => {
|
|
60
|
+
if (initing) return initing.promise;
|
|
61
|
+
initing = create_deferred();
|
|
62
|
+
watcher = watch(dir, resolve_chokidar_options(chokidar, ignored));
|
|
63
|
+
watcher.on('add', (path) => {
|
|
64
|
+
const final_path = absolute ? path : relative(dir, path);
|
|
65
|
+
if (filter && !filter(final_path, false)) return;
|
|
66
|
+
on_change({type: 'add', path: final_path, is_directory: false});
|
|
67
|
+
});
|
|
68
|
+
watcher.on('addDir', (path) => {
|
|
69
|
+
const final_path = absolute ? path : relative(dir, path);
|
|
70
|
+
if (filter && !filter(final_path, true)) return;
|
|
71
|
+
on_change({type: 'add', path: final_path, is_directory: true});
|
|
72
|
+
});
|
|
73
|
+
watcher.on('change', (path, s) => {
|
|
74
|
+
const stats = s ?? statSync(path);
|
|
75
|
+
const final_path = absolute ? path : relative(dir, path);
|
|
76
|
+
if (filter && !filter(final_path, stats.isDirectory())) {
|
|
77
|
+
return;
|
|
78
|
+
}
|
|
79
|
+
on_change({type: 'update', path: final_path, is_directory: stats.isDirectory()});
|
|
80
|
+
});
|
|
81
|
+
watcher.on('unlink', (path) => {
|
|
82
|
+
const final_path = absolute ? path : relative(dir, path);
|
|
83
|
+
if (filter && !filter(final_path, false)) return;
|
|
84
|
+
on_change({type: 'delete', path: final_path, is_directory: false});
|
|
85
|
+
});
|
|
86
|
+
watcher.on('unlinkDir', (path) => {
|
|
87
|
+
const final_path = absolute ? path : relative(dir, path);
|
|
88
|
+
if (filter && !filter(final_path, true)) return;
|
|
89
|
+
on_change({type: 'delete', path: final_path, is_directory: true});
|
|
90
|
+
});
|
|
91
|
+
// wait until ready
|
|
92
|
+
watcher.once('ready', () => initing?.resolve());
|
|
93
|
+
await initing.promise;
|
|
94
|
+
},
|
|
95
|
+
close: async () => {
|
|
96
|
+
initing = undefined;
|
|
97
|
+
if (!watcher) return;
|
|
98
|
+
await watcher.close();
|
|
99
|
+
},
|
|
100
|
+
};
|
|
101
|
+
};
|
|
102
|
+
|
|
103
|
+
const resolve_chokidar_options = (
|
|
104
|
+
options: ChokidarOptions | undefined,
|
|
105
|
+
ignored: Matcher | null,
|
|
106
|
+
): ChokidarOptions | undefined => {
|
|
107
|
+
if (ignored === null) return options;
|
|
108
|
+
|
|
109
|
+
const {ignored: i, ...rest} = options ?? EMPTY_OBJECT;
|
|
110
|
+
|
|
111
|
+
const resolved_ignored =
|
|
112
|
+
i === undefined ? ignored : Array.isArray(i) ? [...i, ignored] : [i, ignored];
|
|
113
|
+
|
|
114
|
+
return {...rest, ignored: resolved_ignored};
|
|
115
|
+
};
|