@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/README.md CHANGED
@@ -76,7 +76,7 @@ It includes:
76
76
 
77
77
  ## install
78
78
 
79
- > depends on node >=20.10
79
+ > depends on node >=22.3
80
80
 
81
81
  Typical usage installs [@ryanatkn/gro](https://www.npmjs.com/package/@ryanatkn/gro)
82
82
  as a dev dependency:
@@ -107,7 +107,7 @@ gro # prints available tasks - defers to any local gro installation
107
107
  Run a task: gro [name]
108
108
  View help: gro [name] --help
109
109
 
110
- 18 tasks in gro:
110
+ 19 tasks in gro:
111
111
 
112
112
  build build the project
113
113
  changeset call changeset with gro patterns
@@ -122,6 +122,7 @@ lint run eslint
122
122
  publish bump version, publish to npm, and git push
123
123
  reinstall refreshes package-lock.json with the latest and cleanest deps
124
124
  release publish and deploy
125
+ resolve run `gro gen`, update `package.json`, and optionally `npm i` to sync up
125
126
  run execute a file with the loader, like `node` but works for TypeScript
126
127
  sync run `gro gen`, update `package.json`, and optionally `npm i` to sync up
127
128
  test run tests with uvu
@@ -1,3 +1,4 @@
1
+ import type { Fetch_Value_Cache } from '@ryanatkn/belt/fetch.js';
1
2
  /**
2
3
  * Updates a changelog produced by `@changesets/changelog-git` with better links and formatting.
3
4
  * It's similar to `@changesets/changelog-github` but doesn't require a token for light usage.
@@ -3,9 +3,8 @@ import { spawn } from '@ryanatkn/belt/process.js';
3
3
  import { red, blue } from 'kleur/colors';
4
4
  import { readFile, writeFile } from 'node:fs/promises';
5
5
  import { join } from 'node:path';
6
- import { readdir } from 'node:fs/promises';
6
+ import { existsSync, readdirSync } from 'node:fs';
7
7
  import { Task_Error } from './task.js';
8
- import { exists } from './fs.js';
9
8
  import { load_package_json } from './package_json.js';
10
9
  import { find_cli, spawn_cli } from './cli.js';
11
10
  import { Git_Origin, git_check_fully_staged_workspace, git_push_to_create } from './git.js';
@@ -67,7 +66,7 @@ export const task = {
67
66
  throw new Task_Error('Failed to find SvelteKit library: ' + has_sveltekit_library_result.message);
68
67
  }
69
68
  const path = join(dir, 'config.json');
70
- const inited = await exists(path);
69
+ const inited = existsSync(path);
71
70
  if (!inited) {
72
71
  await spawn_cli('changeset', ['init']);
73
72
  const access = access_arg ?? package_json.private ? RESTRICTED_ACCESS : PUBLIC_ACCESS;
@@ -109,9 +108,9 @@ export const task = {
109
108
  * @see https://github.com/changesets/changesets/pull/1121
110
109
  */
111
110
  const create_changeset_adder = async (repo_name, dir, message, bump) => {
112
- const filenames_before = await readdir(dir);
111
+ const filenames_before = readdirSync(dir);
113
112
  return async () => {
114
- const filenames_after = await readdir(dir);
113
+ const filenames_after = readdirSync(dir);
115
114
  const filenames_added = filenames_after.filter((p) => !filenames_before.includes(p));
116
115
  if (!filenames_added.length) {
117
116
  throw Error('expected to find a new changeset file');
@@ -1,5 +1,4 @@
1
- /// <reference types="node" />
2
- import type { RmOptions } from 'node:fs';
1
+ import { type RmOptions } from 'node:fs';
3
2
  export declare const clean_fs: ({ build, build_dev, build_dist, sveltekit, nodemodules, }: {
4
3
  build?: boolean;
5
4
  build_dev?: boolean;
package/dist/clean_fs.js CHANGED
@@ -1,4 +1,5 @@
1
- import { rm, readdir } from 'node:fs/promises';
1
+ import { rm } from 'node:fs/promises';
2
+ import { readdirSync } from 'node:fs';
2
3
  import { paths } from './paths.js';
3
4
  import { NODE_MODULES_DIRNAME, GRO_DIST_PREFIX, SVELTEKIT_DEV_DIRNAME, SVELTEKIT_BUILD_DIRNAME, SVELTEKIT_VITE_CACHE_PATH, SVELTEKIT_DIST_DIRNAME, } from './path_constants.js';
4
5
  export const clean_fs = async ({ build = false, build_dev = false, build_dist = false, sveltekit = false, nodemodules = false, }, rm_options = { force: true, recursive: true }) => {
@@ -10,7 +11,7 @@ export const clean_fs = async ({ build = false, build_dev = false, build_dist =
10
11
  promises.push(rm(paths.build_dev, rm_options));
11
12
  }
12
13
  if (build || build_dist) {
13
- const paths = (await readdir('.')).filter((p) => p.startsWith(GRO_DIST_PREFIX));
14
+ const paths = readdirSync('.').filter((p) => p.startsWith(GRO_DIST_PREFIX));
14
15
  for (const path of paths) {
15
16
  promises.push(rm(path, rm_options));
16
17
  }
package/dist/cli.d.ts CHANGED
@@ -1,10 +1,9 @@
1
- /// <reference types="node" />
2
1
  import type { SpawnOptions } from 'node:child_process';
3
2
  import { type Spawn_Result } from '@ryanatkn/belt/process.js';
4
3
  /**
5
4
  * Looks for the CLI `name`, first local to the cwd and then globally.
6
5
  */
7
- export declare const find_cli: (name: string) => Promise<'local' | 'global' | null>;
6
+ export declare const find_cli: (name: string) => Promise<"local" | "global" | null>;
8
7
  /**
9
8
  * Calls the CLI `name` if available, first local to the cwd and then globally.
10
9
  */
package/dist/cli.js CHANGED
@@ -1,12 +1,12 @@
1
1
  import { spawn, spawn_out } from '@ryanatkn/belt/process.js';
2
2
  import { join } from 'node:path';
3
- import { exists } from './fs.js';
3
+ import { existsSync } from 'node:fs';
4
4
  import { NODE_MODULES_DIRNAME } from './path_constants.js';
5
5
  /**
6
6
  * Looks for the CLI `name`, first local to the cwd and then globally.
7
7
  */
8
8
  export const find_cli = async (name) => {
9
- if (await exists(join(NODE_MODULES_DIRNAME, `.bin/${name}`))) {
9
+ if (existsSync(join(NODE_MODULES_DIRNAME, `.bin/${name}`))) {
10
10
  return 'local';
11
11
  }
12
12
  const { stdout } = await spawn_out('which', [name]);
package/dist/config.d.ts CHANGED
@@ -1,5 +1,6 @@
1
1
  import type { Create_Config_Plugins } from './plugin.js';
2
2
  import type { Map_Package_Json } from './package_json.js';
3
+ import type { Path_Filter } from './path.js';
3
4
  export interface Gro_Config {
4
5
  plugins: Create_Config_Plugins;
5
6
  /**
@@ -12,12 +13,24 @@ export interface Gro_Config {
12
13
  * The root directories to search for tasks given implicit relative input paths.
13
14
  * Defaults to `./src/lib`, then the cwd, then the Gro package dist.
14
15
  */
15
- task_root_paths: string[];
16
+ task_root_dirs: string[];
17
+ /**
18
+ * When searching the filsystem for tasks and genfiles,
19
+ * directories and files are included if they pass all of these filters.
20
+ */
21
+ search_filters: Path_Filter | Path_Filter[] | null;
16
22
  }
17
23
  export interface Create_Gro_Config {
18
24
  (base_config: Gro_Config): Gro_Config | Promise<Gro_Config>;
19
25
  }
20
26
  export declare const create_empty_config: () => Gro_Config;
27
+ /**
28
+ * The regexp used by default to exclude directories and files
29
+ * when searching the filesystem for tasks and genfiles.
30
+ * Customize via `search_filters` in the `Gro_Config`.
31
+ * See the test cases for the exact behavior.
32
+ */
33
+ export declare const DEFAULT_SEARCH_EXCLUDER: RegExp;
21
34
  export declare const DEFAULT_EXPORTS_EXCLUDER: RegExp;
22
35
  export interface Gro_Config_Module {
23
36
  readonly default: Gro_Config | Create_Gro_Config;
package/dist/config.js CHANGED
@@ -1,14 +1,26 @@
1
1
  import { join, resolve } from 'node:path';
2
+ import { existsSync } from 'node:fs';
2
3
  import { GRO_DIST_DIR, IS_THIS_GRO, paths } from './paths.js';
3
- import { GRO_CONFIG_PATH } from './path_constants.js';
4
+ import { GRO_CONFIG_PATH, NODE_MODULES_DIRNAME, SERVER_DIST_PATH, SVELTEKIT_BUILD_DIRNAME, SVELTEKIT_DIST_DIRNAME, } from './path_constants.js';
4
5
  import create_default_config from './gro.config.default.js';
5
- import { exists } from './fs.js';
6
6
  export const create_empty_config = () => ({
7
7
  plugins: () => [],
8
8
  // TODO maybe disable if no SvelteKit `lib` directory? or other detection to improve defaults
9
9
  map_package_json: default_map_package_json,
10
- task_root_paths: [paths.lib, paths.root, IS_THIS_GRO ? null : GRO_DIST_DIR].filter(Boolean),
10
+ task_root_dirs: [
11
+ paths.lib,
12
+ IS_THIS_GRO ? null : paths.root,
13
+ IS_THIS_GRO ? null : GRO_DIST_DIR,
14
+ ].filter(Boolean),
15
+ search_filters: [(id) => !DEFAULT_SEARCH_EXCLUDER.test(id)],
11
16
  });
17
+ /**
18
+ * The regexp used by default to exclude directories and files
19
+ * when searching the filesystem for tasks and genfiles.
20
+ * Customize via `search_filters` in the `Gro_Config`.
21
+ * See the test cases for the exact behavior.
22
+ */
23
+ export const DEFAULT_SEARCH_EXCLUDER = new RegExp(`((^|/)\\.[^/]+|/?${NODE_MODULES_DIRNAME}|(^|/)${SVELTEKIT_BUILD_DIRNAME}|(?<!(^|/)gro/)${SVELTEKIT_DIST_DIRNAME}|(^|/)${SERVER_DIST_PATH})($|/)`, 'u');
12
24
  const default_map_package_json = async (package_json) => {
13
25
  if (package_json.exports) {
14
26
  package_json.exports = Object.fromEntries(Object.entries(package_json.exports).filter(([k]) => !DEFAULT_EXPORTS_EXCLUDER.test(k)));
@@ -20,7 +32,7 @@ export const load_config = async (dir = paths.root) => {
20
32
  const default_config = await create_default_config(create_empty_config());
21
33
  const config_path = join(dir, GRO_CONFIG_PATH);
22
34
  let config;
23
- if (await exists(config_path)) {
35
+ if (existsSync(config_path)) {
24
36
  const config_module = await import(config_path);
25
37
  validate_config_module(config_module, config_path);
26
38
  config =
@@ -37,7 +49,7 @@ export const load_config = async (dir = paths.root) => {
37
49
  // Mutates `config` with cleaned up values.
38
50
  const normalize_config = (config) => {
39
51
  // TODO any validation?
40
- config.task_root_paths = config.task_root_paths.map((p) => resolve(p));
52
+ config.task_root_dirs = config.task_root_dirs.map((p) => resolve(p));
41
53
  };
42
54
  export const validate_config_module = (config_module, config_path) => {
43
55
  const config = config_module.default;
@@ -1,8 +1,57 @@
1
1
  import { test } from 'uvu';
2
2
  import * as assert from 'uvu/assert';
3
- import { load_config } from './config.js';
3
+ import { DEFAULT_SEARCH_EXCLUDER, load_config } from './config.js';
4
4
  test('load_config', async () => {
5
5
  const config = await load_config();
6
6
  assert.ok(config);
7
7
  });
8
+ test('DEFAULT_SEARCH_EXCLUDER', () => {
9
+ const assert_excludes = (dirname) => {
10
+ assert.ok(DEFAULT_SEARCH_EXCLUDER.test(`a/${dirname}/c`), 'should exclude: ' + dirname);
11
+ assert.ok(DEFAULT_SEARCH_EXCLUDER.test(`a/${dirname}/c/d.js`), 'should exclude: ' + dirname);
12
+ assert.ok(DEFAULT_SEARCH_EXCLUDER.test(`a/${dirname}/`), 'should exclude: ' + dirname);
13
+ assert.ok(DEFAULT_SEARCH_EXCLUDER.test(`a/${dirname}`), 'should exclude: ' + dirname);
14
+ assert.ok(DEFAULT_SEARCH_EXCLUDER.test(`/a/${dirname}/c`), 'should exclude: ' + dirname);
15
+ assert.ok(DEFAULT_SEARCH_EXCLUDER.test(`/a/${dirname}/c/d.js`), 'should exclude: ' + dirname);
16
+ assert.ok(DEFAULT_SEARCH_EXCLUDER.test(`/a/${dirname}/`), 'should exclude: ' + dirname);
17
+ assert.ok(DEFAULT_SEARCH_EXCLUDER.test(`/a/${dirname}`), 'should exclude: ' + dirname);
18
+ assert.ok(DEFAULT_SEARCH_EXCLUDER.test(`/${dirname}/a`), 'should exclude: ' + dirname);
19
+ assert.ok(DEFAULT_SEARCH_EXCLUDER.test(`/${dirname}/a/b.js`), 'should exclude: ' + dirname);
20
+ assert.ok(DEFAULT_SEARCH_EXCLUDER.test(`/${dirname}/`), 'should exclude: ' + dirname);
21
+ assert.ok(DEFAULT_SEARCH_EXCLUDER.test(`/${dirname}`), 'should exclude: ' + dirname);
22
+ assert.ok(DEFAULT_SEARCH_EXCLUDER.test(`./${dirname}/a`), 'should exclude: ' + dirname);
23
+ assert.ok(DEFAULT_SEARCH_EXCLUDER.test(`./${dirname}/a/b.js`), 'should exclude: ' + dirname);
24
+ assert.ok(DEFAULT_SEARCH_EXCLUDER.test(`./${dirname}/`), 'should exclude: ' + dirname);
25
+ assert.ok(DEFAULT_SEARCH_EXCLUDER.test(`./${dirname}`), 'should exclude: ' + dirname);
26
+ assert.ok(DEFAULT_SEARCH_EXCLUDER.test(`${dirname}/a`), 'should exclude: ' + dirname);
27
+ assert.ok(DEFAULT_SEARCH_EXCLUDER.test(`${dirname}/a/b.js`), 'should exclude: ' + dirname);
28
+ assert.ok(DEFAULT_SEARCH_EXCLUDER.test(`${dirname}/`), 'should exclude: ' + dirname);
29
+ assert.ok(DEFAULT_SEARCH_EXCLUDER.test(`${dirname}`), 'should exclude: ' + dirname);
30
+ };
31
+ assert_excludes('node_modules');
32
+ assert_excludes('dist');
33
+ assert_excludes('build');
34
+ assert_excludes('.git');
35
+ assert_excludes('.gro');
36
+ assert_excludes('.svelte-kit');
37
+ assert.ok(!DEFAULT_SEARCH_EXCLUDER.test('nodemodules'));
38
+ assert.ok(!DEFAULT_SEARCH_EXCLUDER.test('a/b/c'));
39
+ assert.ok(!DEFAULT_SEARCH_EXCLUDER.test('/a/b/c'));
40
+ assert.ok(!DEFAULT_SEARCH_EXCLUDER.test('/a/b/c.js'));
41
+ assert.ok(!DEFAULT_SEARCH_EXCLUDER.test('/a/b/c.d.js'));
42
+ assert.ok(!DEFAULT_SEARCH_EXCLUDER.test('./a/b/c'));
43
+ assert.ok(!DEFAULT_SEARCH_EXCLUDER.test('./a/b/c.d.js'));
44
+ // Special exception for `gro/dist/`:
45
+ assert.ok(DEFAULT_SEARCH_EXCLUDER.test('/home/not_gro/dist/a.task.js'));
46
+ assert.ok(DEFAULT_SEARCH_EXCLUDER.test('/home/grodist/a.task.js'));
47
+ assert.ok(DEFAULT_SEARCH_EXCLUDER.test('not_gro/dist/a.task.js'));
48
+ assert.ok(DEFAULT_SEARCH_EXCLUDER.test('not_dist/a.task.js'));
49
+ assert.ok(DEFAULT_SEARCH_EXCLUDER.test('grodist/a.task.js'));
50
+ assert.ok(!DEFAULT_SEARCH_EXCLUDER.test('/home/gro/dist/a.task.js'));
51
+ assert.ok(!DEFAULT_SEARCH_EXCLUDER.test('gro/dist/a.task.js'));
52
+ assert.ok(!DEFAULT_SEARCH_EXCLUDER.test('./gro/dist/a.task.js'));
53
+ // But not `gro/build/` and others because they're not usecases:
54
+ assert.ok(DEFAULT_SEARCH_EXCLUDER.test('/home/gro/build/a.task.js'));
55
+ assert.ok(DEFAULT_SEARCH_EXCLUDER.test('/home/gro/node_modules/a.task.js'));
56
+ });
8
57
  test.run();
@@ -2,12 +2,13 @@ import { spawn } from '@ryanatkn/belt/process.js';
2
2
  import { print_error } from '@ryanatkn/belt/print.js';
3
3
  import { green, red } from 'kleur/colors';
4
4
  import { z } from 'zod';
5
- import { cp, mkdir, readdir, rm } from 'node:fs/promises';
5
+ import { cp, mkdir, rm } from 'node:fs/promises';
6
6
  import { join, resolve } from 'node:path';
7
+ import { existsSync, readdirSync } from 'node:fs';
7
8
  import { Task_Error } from './task.js';
8
9
  import { print_path } from './paths.js';
9
10
  import { GRO_DIRNAME, GIT_DIRNAME, SVELTEKIT_BUILD_DIRNAME } from './path_constants.js';
10
- import { empty_dir, exists } from './fs.js';
11
+ import { empty_dir } from './fs.js';
11
12
  import { git_check_clean_workspace, git_checkout, git_local_branch_exists, git_remote_branch_exists, Git_Origin, Git_Branch, git_delete_local_branch, git_push_to_create, git_reset_branch_to_first_commit, git_pull, git_fetch, git_check_setting_pull_rebase, git_clone_locally, git_current_branch_name, } from './git.js';
12
13
  // docs at ./docs/deploy.md
13
14
  // terminal command for testing:
@@ -96,7 +97,7 @@ export const task = {
96
97
  // First, check if the deploy dir exists, and if so, attempt to sync it.
97
98
  // If anything goes wrong, delete the directory and we'll initialize it
98
99
  // using the same code path as if it didn't exist in the first place.
99
- if (await exists(resolved_deploy_dir)) {
100
+ if (existsSync(resolved_deploy_dir)) {
100
101
  if (target !== (await git_current_branch_name(target_spawn_options))) {
101
102
  // We're in a bad state because the target branch has changed,
102
103
  // so delete the directory and continue as if it wasn't there.
@@ -114,7 +115,7 @@ export const task = {
114
115
  }
115
116
  // Second, initialize the deploy dir if needed.
116
117
  // It may not exist, or it may have been deleted after failing to sync above.
117
- if (!(await exists(resolved_deploy_dir))) {
118
+ if (!existsSync(resolved_deploy_dir)) {
118
119
  const local_deploy_branch_exists = await git_local_branch_exists(target);
119
120
  await git_fetch(origin, ('+' + target + ':' + target)); // fetch+merge and allow non-fastforward updates with the +
120
121
  await git_clone_locally(origin, target, dir, resolved_deploy_dir);
@@ -131,7 +132,7 @@ export const task = {
131
132
  else {
132
133
  // Remote target branch does not exist, so start from scratch
133
134
  // Delete the deploy dir and recreate it
134
- if (await exists(resolved_deploy_dir)) {
135
+ if (existsSync(resolved_deploy_dir)) {
135
136
  await rm(resolved_deploy_dir, { recursive: true });
136
137
  await mkdir(resolved_deploy_dir, { recursive: true });
137
138
  }
@@ -160,7 +161,7 @@ export const task = {
160
161
  if (build) {
161
162
  await invoke_task('build');
162
163
  }
163
- if (!(await exists(build_dir))) {
164
+ if (!existsSync(build_dir)) {
164
165
  log.error(red('directory to deploy does not exist after building:'), build_dir);
165
166
  return;
166
167
  }
@@ -173,7 +174,7 @@ export const task = {
173
174
  throw new Task_Error(`Deploy safely canceled due to build failure. See the error above.`);
174
175
  }
175
176
  // Copy the build
176
- await Promise.all((await readdir(build_dir)).map((path) => cp(join(build_dir, path), join(resolved_deploy_dir, path), { recursive: true })));
177
+ await Promise.all(readdirSync(build_dir).map((path) => cp(join(build_dir, path), join(resolved_deploy_dir, path), { recursive: true })));
177
178
  // At this point, `dist/` is ready to be committed and deployed!
178
179
  if (dry) {
179
180
  log.info(green('dry deploy complete:'), 'files at', print_path(resolved_deploy_dir));
@@ -2,7 +2,7 @@ import { dirname, relative, basename } from 'node:path';
2
2
  import { parse_path_parts, parse_path_segments } from '@ryanatkn/belt/path.js';
3
3
  import { strip_start } from '@ryanatkn/belt/string.js';
4
4
  import { to_output_file_name } from '../gen.js';
5
- import { paths, base_path_to_source_id } from '../paths.js';
5
+ import { paths, base_path_to_path_id } from '../paths.js';
6
6
  import { search_fs } from '../search_fs.js';
7
7
  // TODO look at `tasks.gen.md.ts` to refactor and generalize
8
8
  // TODO show nested structure, not a flat list
@@ -24,7 +24,7 @@ export const gen = async ({ origin_id }) => {
24
24
  const root_link = `[${root_path}](/../..)`;
25
25
  const doc_files = await search_fs(origin_dir);
26
26
  const doc_paths = [];
27
- for (const path of doc_files.keys()) {
27
+ for (const { path } of doc_files) {
28
28
  if (path === output_file_name || !path.endsWith('.md')) {
29
29
  continue;
30
30
  }
@@ -37,7 +37,7 @@ export const gen = async ({ origin_id }) => {
37
37
  const segment = parse_path_segments(relative_path_part).at(-1);
38
38
  return is_index_file && relative_path_part === relative_dir
39
39
  ? segment
40
- : `[${segment}](${relative(origin_dir, base_path_to_source_id(relative_path_part)) || './'})`;
40
+ : `[${segment}](${relative(origin_dir, base_path_to_path_id(relative_path_part)) || './'})`;
41
41
  });
42
42
  const breadcrumbs = '> <sub>' + [root_link, ...path_parts, output_file_name].join(' / ') + '</sub>';
43
43
  // TODO render the footer with the origin_id
@@ -44,7 +44,8 @@ export interface Create_Gro_Config {
44
44
  export interface Gro_Config {
45
45
  plugins: Create_Config_Plugins;
46
46
  map_package_json: Map_Package_Json | null;
47
- task_root_paths: string[];
47
+ task_root_dirs: string[];
48
+ search_filters: Path_Filter | Path_Filter[] | null;
48
49
  }
49
50
  ```
50
51
 
@@ -55,7 +56,11 @@ import type {Create_Gro_Config} from '@ryanatkn/gro';
55
56
  import {gro_plugin_sveltekit_app} from '@ryanatkn/gro/gro_plugin_sveltekit_app.js';
56
57
 
57
58
  const config: Create_Gro_Config = async (cfg) => {
58
- // example setting your own plugins:
59
+ // `cfg`, which is equal to `create_empty_config()`,
60
+ // can be mutated or you can return your own.
61
+ // A return value is required to avoid potential errors and reduce ambiguity.
62
+
63
+ // example setting your own plugins):
59
64
  cfg.plugins = async () => [
60
65
  gro_plugin_sveltekit_app(),
61
66
  (await import('./src/custom_plugin.js')).plugin(),
@@ -90,7 +95,8 @@ const config = create_empty_config();
90
95
 
91
96
  // config.plugins = ...;
92
97
  // config.map_package_json = ...;
93
- // config.task_root_paths = ...;
98
+ // config.task_root_dirs = ...;
99
+ // config.search_filters = ...;
94
100
 
95
101
  export default config;
96
102
  ```
@@ -176,12 +182,22 @@ export interface Map_Package_Json {
176
182
  }
177
183
  ```
178
184
 
179
- ## `task_root_paths`
185
+ ## `task_root_dirs`
180
186
 
181
- The Gro config option `task_root_paths` allows customizing Gro's task resolution.
187
+ The Gro config option `task_root_dirs` allows customizing Gro's task resolution.
182
188
  When calling `gro [input_path]`, absolute and explicitly relative paths starting with `.`
183
189
  are resolved according to normal filesystem rules,
184
190
  but non-explicit input paths, like `foo`, are resolved by searching
185
- through `task_root_paths` in order until a matching file or directory is found on the filesystem.
191
+ through `task_root_dirs` in order until a matching file or directory is found on the filesystem.
186
192
 
187
193
  The default task paths are `./src/lib`, then `.`, and then Gro's dist directory.
194
+
195
+ ## `search_filters`
196
+
197
+ The Gro config option `search_filters` allows customizing
198
+ how Gro searches for tasks and genfiles on the filesystem.
199
+ Directories and files are included if they pass all of these filters.
200
+
201
+ By default, it uses the `DEFAULT_SEARCH_EXCLUDER` to exclude
202
+ dot-prefixed directories, node_modules,
203
+ and the build and dist directories for SvelteKit and Gro.
package/dist/docs/gen.md CHANGED
@@ -109,10 +109,11 @@ The `Gen` function receives one argument, the `Gen_Context` object:
109
109
  ```ts
110
110
  export interface Gen_Context {
111
111
  config: Gro_Config;
112
+ sveltekit_config: Parsed_Sveltekit_Config;
112
113
  /**
113
114
  * Same as `import.meta.url` but in path form.
114
115
  */
115
- origin_id: string;
116
+ origin_id: Path_Id;
116
117
  log: Logger;
117
118
  }
118
119
  // export const gen: Gen = ({config, origin_id, log}) => {
package/dist/docs/task.md CHANGED
@@ -28,7 +28,7 @@ and defers composition to the user in regular TypeScript modules.
28
28
  - Gro automatically discovers all `*.task.ts|js` files
29
29
  in its configurable directory, so creating a new task
30
30
  is as simple as [creating a new file](#define-a-task), no config needed
31
- (defaults to `src/lib`, see the config option [`task_root_paths`](./config.md#task_root_paths))
31
+ (defaults to `src/lib`, see the config option [`task_root_dirs`](./config.md#task_root_dirs))
32
32
  - to view [the available tasks](https://github.com/ryanatkn/gro/blob/main/src/lib/docs/tasks.md)
33
33
  run `gro` with no arguments
34
34
  - task definitions are just objects with an async `run` function and some optional properties,
@@ -59,10 +59,10 @@ As a developer, it's nice to be able to reuse TypeScript modules in every contex
59
59
  $ gro
60
60
  ```
61
61
 
62
- The [config](./config.md) option [task_root_paths](./config.md#task_root_paths)
62
+ The [config](./config.md) option [task_root_dirs](./config.md#task_root_dirs)
63
63
  tells Gro where to search for tasks.
64
64
 
65
- > Currently, only the first directory specified in `task_root_paths` that's found on the filesystem
65
+ > Currently, only the first directory specified in `task_root_dirs` that's found on the filesystem
66
66
  > will be used to automatically discover tasks, like when running `gro` without args.
67
67
  > Please open an issue if you would like to see Gro be able to discover
68
68
  > tasks in more than one directory - it will take some reworking of internals
@@ -162,6 +162,7 @@ import type {Task_Context} from '@ryanatkn/gro';
162
162
  export interface Task_Context<T_Args = object> {
163
163
  args: T_Args;
164
164
  config: Gro_Config;
165
+ sveltekit_config: Parsed_Sveltekit_Config;
165
166
  log: Logger;
166
167
  timings: Timings;
167
168
  invoke_task: (task_name: string, args?: Args, config?: Gro_Config) => Promise<void>;
@@ -2,25 +2,32 @@ import { dirname, relative, basename } from 'node:path';
2
2
  import { parse_path_parts, parse_path_segments } from '@ryanatkn/belt/path.js';
3
3
  import { strip_start } from '@ryanatkn/belt/string.js';
4
4
  import { to_output_file_name } from '../gen.js';
5
- import { paths, base_path_to_source_id } from '../paths.js';
6
- import { load_task_modules } from '../task_module.js';
5
+ import { paths, base_path_to_path_id } from '../paths.js';
7
6
  import { log_error_reasons } from '../task_logging.js';
7
+ import { find_tasks, load_tasks, Task_Error } from '../task.js';
8
8
  // This is the first simple implementation of Gro's automated docs.
9
9
  // It combines Gro's gen and task systems
10
- // to generate a markdown file describing all of the project's tasks.
10
+ // to generate a markdown file with a summary of all of Gro's tasks.
11
11
  // Other projects that use Gro should be able to import this module
12
12
  // or other otherwise get frictionless access to this specific use case,
13
13
  // and they should be able to extend or customize it to any degree.
14
14
  // TODO display more info about each task, including a summary and params
15
15
  // TODO needs some cleanup and better APIs - paths are confusing and verbose!
16
16
  // TODO add backlinks to every document that links to this one
17
- export const gen = async ({ config, origin_id, log }) => {
18
- const result = await load_task_modules([paths.lib], config.task_root_paths);
19
- if (!result.ok) {
20
- log_error_reasons(log, result.reasons);
21
- throw new Error(result.type);
17
+ export const gen = async ({ origin_id, log, config }) => {
18
+ const found = await find_tasks(['.'], [paths.lib], config);
19
+ if (!found.ok) {
20
+ log_error_reasons(log, found.reasons);
21
+ throw new Task_Error(`Failed to generate task docs: ${found.type}`);
22
22
  }
23
- const tasks = result.modules;
23
+ const found_tasks = found.value;
24
+ const loaded = await load_tasks(found_tasks);
25
+ if (!loaded.ok) {
26
+ log_error_reasons(log, loaded.reasons);
27
+ throw new Task_Error(`Failed to generate task docs: ${loaded.type}`);
28
+ }
29
+ const loaded_tasks = loaded.value;
30
+ const tasks = loaded_tasks.modules;
24
31
  const root_path = parse_path_segments(paths.root).at(-1);
25
32
  const origin_dir = dirname(origin_id);
26
33
  const origin_base = basename(origin_id);
@@ -33,7 +40,7 @@ export const gen = async ({ config, origin_id, log }) => {
33
40
  const root_link = `[${root_path}](/../..)`;
34
41
  // TODO do we want to use absolute paths instead of relative paths,
35
42
  // because GitHub works with them and it simplifies the code?
36
- const path_parts = parse_path_parts(relative_dir).map((relative_path_part) => `[${parse_path_segments(relative_path_part).at(-1)}](${relative(origin_dir, base_path_to_source_id(relative_path_part)) || './'})`);
43
+ const path_parts = parse_path_parts(relative_dir).map((relative_path_part) => `[${parse_path_segments(relative_path_part).at(-1)}](${relative(origin_dir, base_path_to_path_id(relative_path_part)) || './'})`);
37
44
  const breadcrumbs = '> <sub>' + [root_link, ...path_parts, output_file_name].join(' / ') + '</sub>';
38
45
  // TODO render the footer with the origin_id
39
46
  return `# tasks
@@ -19,6 +19,7 @@ What is a `Task`? See [`task.md`](./task.md).
19
19
  - [publish](../publish.task.ts) - bump version, publish to npm, and git push
20
20
  - [reinstall](../reinstall.task.ts) - refreshes package-lock.json with the latest and cleanest deps
21
21
  - [release](../release.task.ts) - publish and deploy
22
+ - [resolve](../resolve.task.ts) - diagnostic that logs the info resolved from the filesystem for the given input paths
22
23
  - [run](../run.task.ts) - execute a file with the loader, like `node` but works for TypeScript
23
24
  - [sync](../sync.task.ts) - run `gro gen`, update `package.json`, and optionally `npm i` to sync up
24
25
  - [test](../test.task.ts) - run tests with uvu
package/dist/env.d.ts CHANGED
@@ -1,6 +1,5 @@
1
- /// <reference types="node" />
2
- export declare const load_env: (dev: boolean, visibility: 'public' | 'private', public_prefix: string, private_prefix: string, env_dir?: string, env_files?: string[], ambient_env?: NodeJS.ProcessEnv) => Promise<Record<string, string>>;
3
- export declare const merge_envs: (envs: Array<Record<string, string | undefined>>, visibility: 'public' | 'private', public_prefix: string, private_prefix: string) => Record<string, string>;
1
+ export declare const load_env: (dev: boolean, visibility: "public" | "private", public_prefix: string, private_prefix: string, env_dir?: string, env_files?: string[], ambient_env?: NodeJS.ProcessEnv) => Promise<Record<string, string>>;
2
+ export declare const merge_envs: (envs: Array<Record<string, string | undefined>>, visibility: "public" | "private", public_prefix: string, private_prefix: string) => Record<string, string>;
4
3
  export declare const is_private_env: (key: string, public_prefix: string, private_prefix: string) => boolean;
5
4
  export declare const is_public_env: (key: string, public_prefix: string, private_prefix: string) => boolean;
6
5
  /**
package/dist/env.js CHANGED
@@ -1,7 +1,7 @@
1
1
  import dotenv from 'dotenv';
2
2
  import { readFile } from 'node:fs/promises';
3
3
  import { resolve } from 'node:path';
4
- import { exists } from './fs.js';
4
+ import { existsSync } from 'node:fs';
5
5
  export const load_env = async (dev, visibility, public_prefix, private_prefix, env_dir, env_files = ['.env', '.env.' + (dev ? 'development' : 'production')], ambient_env = process.env) => {
6
6
  const envs = await Promise.all(env_files
7
7
  .map(async (path) => (await load(env_dir === undefined ? path : resolve(env_dir, path))))
@@ -10,7 +10,7 @@ export const load_env = async (dev, visibility, public_prefix, private_prefix, e
10
10
  return merge_envs(envs, visibility, public_prefix, private_prefix);
11
11
  };
12
12
  const load = async (path) => {
13
- if (!(await exists(path)))
13
+ if (!existsSync(path))
14
14
  return undefined;
15
15
  const loaded = await readFile(path, 'utf8');
16
16
  return dotenv.parse(loaded);
@@ -1,3 +1,4 @@
1
+ import type { Logger } from '@ryanatkn/belt/log.js';
1
2
  import type * as esbuild from 'esbuild';
2
3
  import type { Parsed_Sveltekit_Config } from './sveltekit_config.js';
3
4
  export declare const print_build_result: (log: Logger, build_result: esbuild.BuildResult) => void;
@@ -10,5 +11,5 @@ export declare const print_build_result: (log: Logger, build_result: esbuild.Bui
10
11
  * @param mode
11
12
  * @returns
12
13
  */
13
- export declare const to_define_import_meta_env: (dev: boolean, base_url: Parsed_Sveltekit_Config['base_url'], ssr?: boolean, mode?: string) => Record<string, string>;
14
+ export declare const to_define_import_meta_env: (dev: boolean, base_url: Parsed_Sveltekit_Config["base_url"], ssr?: boolean, mode?: string) => Record<string, string>;
14
15
  export declare const ts_transform_options: esbuild.TransformOptions;
@@ -1,4 +1,3 @@
1
- /// <reference types="svelte" />
2
1
  import * as esbuild from 'esbuild';
3
2
  import type { Logger } from '@ryanatkn/belt/log.js';
4
3
  import type { CompileOptions, PreprocessorGroup, ModuleCompileOptions } from 'svelte/compiler';
@@ -11,11 +11,11 @@ export const esbuild_plugin_external_worker = ({ dev, build_options, dir = proce
11
11
  name: 'external_worker',
12
12
  setup: (build) => {
13
13
  const builds = new Map();
14
- const build_worker = async (source_id) => {
15
- if (builds.has(source_id))
16
- return builds.get(source_id);
14
+ const build_worker = async (path_id) => {
15
+ if (builds.has(path_id))
16
+ return builds.get(path_id);
17
17
  const building = esbuild.build({
18
- entryPoints: [source_id],
18
+ entryPoints: [path_id],
19
19
  plugins: [
20
20
  esbuild_plugin_sveltekit_shim_app({ dev, base_url, assets_url }),
21
21
  esbuild_plugin_sveltekit_shim_env({
@@ -38,13 +38,13 @@ export const esbuild_plugin_external_worker = ({ dev, build_options, dir = proce
38
38
  define: to_define_import_meta_env(dev, base_url),
39
39
  ...build_options,
40
40
  });
41
- builds.set(source_id, building);
41
+ builds.set(path_id, building);
42
42
  return building;
43
43
  };
44
44
  build.onResolve({ filter: /\.worker(|\.js|\.ts)$/u }, async ({ path, resolveDir }) => {
45
45
  const parsed = await resolve_specifier(path, resolveDir);
46
- const { specifier, source_id, namespace } = parsed;
47
- const build_result = await build_worker(source_id);
46
+ const { specifier, path_id, namespace } = parsed;
47
+ const build_result = await build_worker(path_id);
48
48
  if (log)
49
49
  print_build_result(log, build_result);
50
50
  return { path: './' + basename(specifier), external: true, namespace };
@@ -1,4 +1,3 @@
1
- /// <reference types="svelte" />
2
1
  import type * as esbuild from 'esbuild';
3
2
  import { type CompileOptions, type ModuleCompileOptions, type PreprocessorGroup } from 'svelte/compiler';
4
3
  export interface Options {
@@ -13,8 +13,8 @@ export const esbuild_plugin_sveltekit_local_imports = () => ({
13
13
  const { path, importer } = args;
14
14
  if (!importer)
15
15
  return { path };
16
- const { source_id, namespace } = await resolve_specifier(path, dirname(importer));
17
- return { path: source_id, namespace }; // `namespace` may be `undefined`, but esbuild needs the absolute path for json etc
16
+ const { path_id, namespace } = await resolve_specifier(path, dirname(importer));
17
+ return { path: path_id, namespace }; // `namespace` may be `undefined`, but esbuild needs the absolute path for json etc
18
18
  });
19
19
  build.onLoad({ filter: /.*/u, namespace: 'sveltekit_local_imports_ts' }, async ({ path }) => ({
20
20
  contents: await readFile(path),
@@ -1,4 +1,5 @@
1
1
  import type { Spawn_Result } from '@ryanatkn/belt/process.js';
2
+ import type { Logger } from '@ryanatkn/belt/log.js';
2
3
  /**
3
4
  * Formats a directory on the filesystem.
4
5
  * If the source directory is given, it also formats all of the root directory files.
package/dist/fs.d.ts CHANGED
@@ -1,6 +1,4 @@
1
- /// <reference types="node" />
2
- import type { RmOptions } from 'node:fs';
3
- export declare const exists: (path: string) => Promise<boolean>;
1
+ import { type RmOptions } from 'node:fs';
4
2
  /**
5
3
  * Empties a directory with an optional `filter`.
6
4
  */