@ryanatkn/gro 0.124.0 → 0.125.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (47) hide show
  1. package/README.md +1 -1
  2. package/dist/args.js +2 -2
  3. package/dist/changeset.task.d.ts +3 -2
  4. package/dist/changeset.task.js +13 -18
  5. package/dist/changeset_helpers.d.ts +8 -0
  6. package/dist/changeset_helpers.js +7 -0
  7. package/dist/check.task.js +1 -1
  8. package/dist/cli.d.ts +26 -5
  9. package/dist/cli.js +49 -13
  10. package/dist/config.d.ts +29 -5
  11. package/dist/config.js +31 -20
  12. package/dist/deploy.task.js +7 -10
  13. package/dist/docs/config.md +15 -5
  14. package/dist/esbuild_helpers.js +1 -1
  15. package/dist/format_directory.d.ts +2 -1
  16. package/dist/format_directory.js +7 -7
  17. package/dist/gen.js +1 -1
  18. package/dist/gen.task.js +1 -1
  19. package/dist/github.d.ts +2 -2
  20. package/dist/gro_plugin_sveltekit_app.d.ts +5 -1
  21. package/dist/gro_plugin_sveltekit_app.js +13 -12
  22. package/dist/gro_plugin_sveltekit_library.d.ts +7 -1
  23. package/dist/gro_plugin_sveltekit_library.js +7 -7
  24. package/dist/index.d.ts +1 -1
  25. package/dist/invoke_task.js +1 -1
  26. package/dist/lint.task.d.ts +3 -0
  27. package/dist/lint.task.js +9 -6
  28. package/dist/package.d.ts +94 -84
  29. package/dist/package.js +117 -92
  30. package/dist/package_json.d.ts +71 -68
  31. package/dist/package_json.js +52 -25
  32. package/dist/path.d.ts +3 -0
  33. package/dist/paths.js +1 -1
  34. package/dist/publish.task.d.ts +3 -0
  35. package/dist/publish.task.js +10 -7
  36. package/dist/run.task.js +1 -1
  37. package/dist/run_gen.js +1 -1
  38. package/dist/run_task.js +1 -1
  39. package/dist/search_fs.d.ts +2 -2
  40. package/dist/sveltekit_helpers.d.ts +5 -2
  41. package/dist/sveltekit_helpers.js +13 -10
  42. package/dist/task.js +1 -1
  43. package/dist/task_logging.js +1 -1
  44. package/dist/test.task.js +2 -3
  45. package/dist/typecheck.task.d.ts +10 -1
  46. package/dist/typecheck.task.js +27 -17
  47. package/package.json +175 -172
package/README.md CHANGED
@@ -83,7 +83,7 @@ as a dev dependency:
83
83
 
84
84
  ```bash
85
85
  npm i -D @ryanatkn/gro
86
- npx gro
86
+ npx gro # note the package is namespaced, don't install `gro`
87
87
  ```
88
88
 
89
89
  It's handy to install globally too:
package/dist/args.js CHANGED
@@ -1,4 +1,4 @@
1
- import { magenta } from 'kleur/colors';
1
+ import { gray, magenta } from '@ryanatkn/belt/styletext.js';
2
2
  import mri from 'mri';
3
3
  /**
4
4
  * Parses user input args with a Zod schema.
@@ -129,4 +129,4 @@ export const to_forwarded_args_by_command = (raw_rest_args = to_raw_rest_args())
129
129
  }
130
130
  return forwarded_args_by_command;
131
131
  };
132
- export const print_command_args = (serialized_args) => magenta('running command: ') + serialized_args.join(' ');
132
+ export const print_command_args = (serialized_args) => gray('[') + magenta('running command') + gray(']') + ' ' + serialized_args.join(' ');
@@ -1,7 +1,5 @@
1
1
  import { z } from 'zod';
2
2
  import { type Task } from './task.js';
3
- export declare const Changeset_Bump: z.ZodEnum<["patch", "minor", "major"]>;
4
- export type Changeset_Bump = z.infer<typeof Changeset_Bump>;
5
3
  export declare const Args: z.ZodObject<{
6
4
  /**
7
5
  * The optional rest args get joined with a space to form the `message`.
@@ -15,6 +13,7 @@ export declare const Args: z.ZodObject<{
15
13
  install: z.ZodDefault<z.ZodBoolean>;
16
14
  'no-install': z.ZodDefault<z.ZodBoolean>;
17
15
  origin: z.ZodDefault<z.ZodBranded<z.ZodString, "Git_Origin">>;
16
+ changeset_cli: z.ZodDefault<z.ZodString>;
18
17
  }, "strict", z.ZodTypeAny, {
19
18
  _: string[];
20
19
  dir: string;
@@ -24,6 +23,7 @@ export declare const Args: z.ZodObject<{
24
23
  minor: boolean;
25
24
  major: boolean;
26
25
  changelog: string;
26
+ changeset_cli: string;
27
27
  access?: "public" | "restricted" | undefined;
28
28
  }, {
29
29
  _?: string[] | undefined;
@@ -35,6 +35,7 @@ export declare const Args: z.ZodObject<{
35
35
  major?: boolean | undefined;
36
36
  access?: "public" | "restricted" | undefined;
37
37
  changelog?: string | undefined;
38
+ changeset_cli?: string | undefined;
38
39
  }>;
39
40
  export type Args = z.infer<typeof Args>;
40
41
  /**
@@ -1,6 +1,6 @@
1
1
  import { z } from 'zod';
2
2
  import { spawn } from '@ryanatkn/belt/process.js';
3
- import { red, blue } from 'kleur/colors';
3
+ import { red, blue } from '@ryanatkn/belt/styletext.js';
4
4
  import { readFile, writeFile } from 'node:fs/promises';
5
5
  import { join } from 'node:path';
6
6
  import { existsSync, readdirSync } from 'node:fs';
@@ -9,10 +9,7 @@ import { load_package_json } from './package_json.js';
9
9
  import { find_cli, spawn_cli } from './cli.js';
10
10
  import { Git_Origin, git_check_fully_staged_workspace, git_push_to_create } from './git.js';
11
11
  import { has_sveltekit_library } from './sveltekit_helpers.js';
12
- const RESTRICTED_ACCESS = 'restricted';
13
- const PUBLIC_ACCESS = 'public';
14
- const CHANGESET_DIR = '.changeset';
15
- export const Changeset_Bump = z.enum(['patch', 'minor', 'major']);
12
+ import { CHANGESET_CLI, CHANGESET_DIR, Changeset_Access, Changeset_Bump, CHANGESET_PUBLIC_ACCESS, CHANGESET_RESTRICTED_ACCESS, } from './changeset_helpers.js';
16
13
  export const Args = z
17
14
  .object({
18
15
  /**
@@ -22,11 +19,7 @@ export const Args = z
22
19
  minor: z.boolean({ description: 'bump the minor version' }).default(false),
23
20
  major: z.boolean({ description: 'bump the major version' }).default(false),
24
21
  dir: z.string({ description: 'changeset dir' }).default(CHANGESET_DIR),
25
- access: z
26
- .enum([RESTRICTED_ACCESS, PUBLIC_ACCESS], {
27
- description: `changeset 'access' config value, the default depends on package.json#private`,
28
- })
29
- .optional(),
22
+ access: Changeset_Access.describe("changeset 'access' config value, the default depends on package.json#private").optional(),
30
23
  changelog: z
31
24
  .string({ description: 'changeset "changelog" config value' })
32
25
  .default('@changesets/changelog-git'),
@@ -35,6 +28,7 @@ export const Args = z
35
28
  .boolean({ description: 'opt out of npm installing the changelog package' })
36
29
  .default(false),
37
30
  origin: Git_Origin.describe('git origin to deploy to').default('origin'),
31
+ changeset_cli: z.string({ description: 'the changeset CLI to use' }).default(CHANGESET_CLI),
38
32
  })
39
33
  .strict();
40
34
  /**
@@ -50,14 +44,15 @@ export const task = {
50
44
  summary: 'call changeset with gro patterns',
51
45
  Args,
52
46
  run: async (ctx) => {
53
- const { invoke_task, args: { _, minor, major, dir, access: access_arg, changelog, install, origin }, log, } = ctx;
47
+ const { invoke_task, args: { _, minor, major, dir, access: access_arg, changelog, install, origin, changeset_cli }, log, } = ctx;
54
48
  const message = _.join(' ');
55
49
  if (!message && (minor || major))
56
50
  throw new Task_Error('cannot bump version without a message');
57
51
  if (minor && major)
58
52
  throw new Task_Error('cannot bump both minor and major');
59
53
  const bump = minor ? 'minor' : major ? 'major' : 'patch';
60
- if (!(await find_cli('changeset'))) {
54
+ const found_changeset_cli = await find_cli(changeset_cli);
55
+ if (!found_changeset_cli) {
61
56
  throw new Task_Error('changeset command not found: install @changesets/cli locally or globally');
62
57
  }
63
58
  const package_json = await load_package_json();
@@ -68,11 +63,11 @@ export const task = {
68
63
  const path = join(dir, 'config.json');
69
64
  const inited = existsSync(path);
70
65
  if (!inited) {
71
- await spawn_cli('changeset', ['init']);
72
- const access = access_arg ?? package_json.private ? RESTRICTED_ACCESS : PUBLIC_ACCESS;
73
- const access_color = access === RESTRICTED_ACCESS ? blue : red;
66
+ await spawn_cli(found_changeset_cli, ['init'], log);
67
+ const access = access_arg ?? package_json.private ? CHANGESET_RESTRICTED_ACCESS : CHANGESET_PUBLIC_ACCESS;
68
+ const access_color = access === CHANGESET_RESTRICTED_ACCESS ? blue : red;
74
69
  log.info('initing changeset with ' + access_color(access) + ' access');
75
- if (access !== RESTRICTED_ACCESS) {
70
+ if (access !== CHANGESET_RESTRICTED_ACCESS) {
76
71
  await update_changeset_config(path, (config) => {
77
72
  const updated = { ...config };
78
73
  updated.access = access;
@@ -90,7 +85,7 @@ export const task = {
90
85
  if (message) {
91
86
  // TODO see the helper below, simplify this to CLI flags when support is added to Changesets
92
87
  const changeset_adder = await create_changeset_adder(package_json.name, dir, message, bump);
93
- await spawn_cli('changeset', ['add', '--empty']);
88
+ await spawn_cli(found_changeset_cli, ['add', '--empty'], log);
94
89
  await changeset_adder();
95
90
  if (!(await git_check_fully_staged_workspace())) {
96
91
  await spawn('git', ['commit', '-m', message]);
@@ -98,7 +93,7 @@ export const task = {
98
93
  }
99
94
  }
100
95
  else {
101
- await spawn_cli('changeset');
96
+ await spawn_cli(found_changeset_cli, [], log);
102
97
  await spawn('git', ['add', dir]);
103
98
  }
104
99
  },
@@ -0,0 +1,8 @@
1
+ import { z } from 'zod';
2
+ export declare const CHANGESET_RESTRICTED_ACCESS = "restricted";
3
+ export declare const CHANGESET_PUBLIC_ACCESS = "public";
4
+ export declare const Changeset_Access: z.ZodEnum<["restricted", "public"]>;
5
+ export declare const CHANGESET_CLI = "changeset";
6
+ export declare const CHANGESET_DIR = ".changeset";
7
+ export declare const Changeset_Bump: z.ZodEnum<["patch", "minor", "major"]>;
8
+ export type Changeset_Bump = z.infer<typeof Changeset_Bump>;
@@ -0,0 +1,7 @@
1
+ import { z } from 'zod';
2
+ export const CHANGESET_RESTRICTED_ACCESS = 'restricted';
3
+ export const CHANGESET_PUBLIC_ACCESS = 'public';
4
+ export const Changeset_Access = z.enum([CHANGESET_RESTRICTED_ACCESS, CHANGESET_PUBLIC_ACCESS]);
5
+ export const CHANGESET_CLI = 'changeset';
6
+ export const CHANGESET_DIR = '.changeset';
7
+ export const Changeset_Bump = z.enum(['patch', 'minor', 'major']);
@@ -1,6 +1,6 @@
1
1
  import { z } from 'zod';
2
2
  import { spawn } from '@ryanatkn/belt/process.js';
3
- import { red } from 'kleur/colors';
3
+ import { red } from '@ryanatkn/belt/styletext.js';
4
4
  import { Task_Error } from './task.js';
5
5
  import { git_check_clean_workspace } from './git.js';
6
6
  import { sync_package_json } from './package_json.js';
package/dist/cli.d.ts CHANGED
@@ -1,10 +1,31 @@
1
1
  import type { SpawnOptions } from 'node:child_process';
2
- import { type Spawn_Result } from '@ryanatkn/belt/process.js';
2
+ import { type Spawn_Result, type Spawned_Process } from '@ryanatkn/belt/process.js';
3
+ import type { Path_Id } from './path.js';
4
+ export type Cli = {
5
+ kind: 'local';
6
+ name: string;
7
+ id: Path_Id;
8
+ } | {
9
+ kind: 'global';
10
+ name: string;
11
+ id: Path_Id;
12
+ };
3
13
  /**
4
- * Looks for the CLI `name`, first local to the cwd and then globally.
14
+ * Searches the filesystem for the CLI `name`, first local to the cwd and then globally.
15
+ * @returns `null` if not found locally or globally
5
16
  */
6
- export declare const find_cli: (name: string) => Promise<"local" | "global" | null>;
17
+ export declare const find_cli: (name: string, cwd?: string | URL) => Promise<Cli | null>;
7
18
  /**
8
- * Calls the CLI `name` if available, first local to the cwd and then globally.
19
+ * Spawns a CLI if available using Belt's `spawn`.
20
+ * If a string is provided for `name_or_cli`, it checks first local to the cwd and then globally.
21
+ * @returns `undefined` if no CLI is found, or the spawn result
9
22
  */
10
- export declare const spawn_cli: (name: string, args?: any[], options?: SpawnOptions | undefined) => Promise<Spawn_Result | undefined>;
23
+ export declare const spawn_cli: (name_or_cli: string | Cli, args?: string[], log?: any, options?: SpawnOptions | undefined) => Promise<Spawn_Result | undefined>;
24
+ /**
25
+ * Spawns a CLI if available using Belt's `spawn_process`.
26
+ * If a string is provided for `name_or_cli`, it checks first local to the cwd and then globally.
27
+ * @returns `undefined` if no CLI is found, or the spawn result
28
+ */
29
+ export declare const spawn_cli_process: (name_or_cli: string | Cli, args?: string[], log?: any, options?: SpawnOptions | undefined) => Promise<Spawned_Process | undefined>;
30
+ export declare const resolve_cli: (name_or_cli: string | Cli, args: string[] | undefined, cwd: string | URL | undefined, log?: any) => Promise<Cli | undefined>;
31
+ export declare const to_cli_name: (cli: string | Cli) => string;
package/dist/cli.js CHANGED
@@ -1,25 +1,61 @@
1
- import { spawn, spawn_out } from '@ryanatkn/belt/process.js';
1
+ import { spawn, spawn_out, spawn_process, } from '@ryanatkn/belt/process.js';
2
2
  import { join } from 'node:path';
3
3
  import { existsSync } from 'node:fs';
4
+ import { fileURLToPath } from 'node:url';
4
5
  import { NODE_MODULES_DIRNAME } from './path_constants.js';
6
+ import { print_command_args } from './args.js';
5
7
  /**
6
- * Looks for the CLI `name`, first local to the cwd and then globally.
8
+ * Searches the filesystem for the CLI `name`, first local to the cwd and then globally.
9
+ * @returns `null` if not found locally or globally
7
10
  */
8
- export const find_cli = async (name) => {
9
- if (existsSync(join(NODE_MODULES_DIRNAME, `.bin/${name}`))) {
10
- return 'local';
11
+ export const find_cli = async (name, cwd = process.cwd()) => {
12
+ const final_cwd = typeof cwd === 'string' ? cwd : fileURLToPath(cwd);
13
+ const local_id = join(final_cwd, NODE_MODULES_DIRNAME, `.bin/${name}`);
14
+ if (existsSync(local_id)) {
15
+ return { name, id: local_id, kind: 'local' };
11
16
  }
12
17
  const { stdout } = await spawn_out('which', [name]);
13
- return stdout === null ? null : 'global';
18
+ const global_id = stdout?.trim();
19
+ if (!global_id)
20
+ return null;
21
+ return { name, id: global_id, kind: 'global' };
14
22
  };
15
23
  /**
16
- * Calls the CLI `name` if available, first local to the cwd and then globally.
24
+ * Spawns a CLI if available using Belt's `spawn`.
25
+ * If a string is provided for `name_or_cli`, it checks first local to the cwd and then globally.
26
+ * @returns `undefined` if no CLI is found, or the spawn result
17
27
  */
18
- export const spawn_cli = async (name, args = [], options) => {
19
- const found = await find_cli(name);
20
- if (!found)
28
+ export const spawn_cli = async (name_or_cli, args = [], log, options) => {
29
+ const cli = await resolve_cli(name_or_cli, args, options?.cwd, log);
30
+ if (!cli)
21
31
  return;
22
- const command = found === 'local' ? 'npx' : name;
23
- const final_args = found === 'local' ? [name].concat(args) : args;
24
- return spawn(command, final_args, options);
32
+ return spawn(cli.id, args, options);
25
33
  };
34
+ /**
35
+ * Spawns a CLI if available using Belt's `spawn_process`.
36
+ * If a string is provided for `name_or_cli`, it checks first local to the cwd and then globally.
37
+ * @returns `undefined` if no CLI is found, or the spawn result
38
+ */
39
+ export const spawn_cli_process = async (name_or_cli, args = [], log, options) => {
40
+ const cli = await resolve_cli(name_or_cli, args, options?.cwd, log);
41
+ if (!cli)
42
+ return;
43
+ return spawn_process(cli.id, args, options);
44
+ };
45
+ export const resolve_cli = async (name_or_cli, args = [], cwd, log) => {
46
+ let final_cli;
47
+ if (typeof name_or_cli === 'string') {
48
+ const found = await find_cli(name_or_cli, cwd);
49
+ if (!found)
50
+ return;
51
+ final_cli = found;
52
+ }
53
+ else {
54
+ final_cli = name_or_cli;
55
+ }
56
+ if (log) {
57
+ log.info(print_command_args([final_cli.name].concat(args)));
58
+ }
59
+ return final_cli;
60
+ };
61
+ export const to_cli_name = (cli) => typeof cli === 'string' ? cli : cli.name;
package/dist/config.d.ts CHANGED
@@ -1,7 +1,15 @@
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
+ import type { Path_Filter, Path_Id } from './path.js';
4
+ /**
5
+ * The config that users can extend via `gro.config.ts`.
6
+ * This is exposed to users in places like tasks and genfiles.
7
+ * @see https://github.com/ryanatkn/gro/blob/main/src/lib/docs/config.md
8
+ */
4
9
  export interface Gro_Config {
10
+ /**
11
+ * @see https://github.com/ryanatkn/gro/blob/main/src/lib/docs/plugin.md
12
+ */
5
13
  plugins: Create_Config_Plugins;
6
14
  /**
7
15
  * Maps the project's `package.json` before writing it to the filesystem.
@@ -13,15 +21,26 @@ export interface Gro_Config {
13
21
  * The root directories to search for tasks given implicit relative input paths.
14
22
  * Defaults to `./src/lib`, then the cwd, then the Gro package dist.
15
23
  */
16
- task_root_dirs: string[];
24
+ task_root_dirs: Path_Id[];
17
25
  /**
18
26
  * When searching the filsystem for tasks and genfiles,
19
27
  * directories and files are included if they pass all of these filters.
20
28
  */
21
- search_filters: Path_Filter | Path_Filter[] | null;
29
+ search_filters: Path_Filter[];
30
+ }
31
+ /**
32
+ * The relaxed variant of `Gro_Config` that users can provide via `gro.config.ts`.
33
+ * Superset of `Gro_Config`.
34
+ * @see https://github.com/ryanatkn/gro/blob/main/src/lib/docs/config.md
35
+ */
36
+ export interface Raw_Gro_Config {
37
+ plugins?: Create_Config_Plugins;
38
+ map_package_json?: Map_Package_Json | null;
39
+ task_root_dirs?: string[];
40
+ search_filters?: Path_Filter | Path_Filter[] | null;
22
41
  }
23
42
  export interface Create_Gro_Config {
24
- (base_config: Gro_Config): Gro_Config | Promise<Gro_Config>;
43
+ (base_config: Gro_Config): Raw_Gro_Config | Promise<Raw_Gro_Config>;
25
44
  }
26
45
  export declare const create_empty_config: () => Gro_Config;
27
46
  /**
@@ -32,8 +51,13 @@ export declare const create_empty_config: () => Gro_Config;
32
51
  */
33
52
  export declare const DEFAULT_SEARCH_EXCLUDER: RegExp;
34
53
  export declare const DEFAULT_EXPORTS_EXCLUDER: RegExp;
54
+ /**
55
+ * Transforms a `Raw_Gro_Config` to the more strict `Gro_Config`.
56
+ * This allows users to provide a more relaxed config.
57
+ */
58
+ export declare const normalize_config: (raw_config: Raw_Gro_Config) => Gro_Config;
35
59
  export interface Gro_Config_Module {
36
- readonly default: Gro_Config | Create_Gro_Config;
60
+ readonly default: Raw_Gro_Config | Create_Gro_Config;
37
61
  }
38
62
  export declare const load_config: (dir?: string) => Promise<Gro_Config>;
39
63
  export declare const validate_config_module: (config_module: any, config_path: string) => asserts config_module is Gro_Config_Module;
package/dist/config.js CHANGED
@@ -5,9 +5,9 @@ import { GRO_CONFIG_PATH, NODE_MODULES_DIRNAME, SERVER_DIST_PATH, SVELTEKIT_BUIL
5
5
  import create_default_config from './gro.config.default.js';
6
6
  export const create_empty_config = () => ({
7
7
  plugins: () => [],
8
- // TODO maybe disable if no SvelteKit `lib` directory? or other detection to improve defaults
9
8
  map_package_json: default_map_package_json,
10
9
  task_root_dirs: [
10
+ // TODO maybe disable if no SvelteKit `lib` directory? or other detection to improve defaults
11
11
  paths.lib,
12
12
  IS_THIS_GRO ? null : paths.root,
13
13
  IS_THIS_GRO ? null : GRO_DIST_DIR,
@@ -34,28 +34,39 @@ const default_map_package_json = async (package_json) => {
34
34
  return package_json;
35
35
  };
36
36
  export const DEFAULT_EXPORTS_EXCLUDER = /(\.md|\.(test|ignore)\.|\/(test|fixtures|ignore)\/)/u;
37
+ /**
38
+ * Transforms a `Raw_Gro_Config` to the more strict `Gro_Config`.
39
+ * This allows users to provide a more relaxed config.
40
+ */
41
+ export const normalize_config = (raw_config) => {
42
+ const empty_config = create_empty_config();
43
+ // All of the raw config properties are optional,
44
+ // so fall back to the empty values when `undefined`.
45
+ const { plugins = empty_config.plugins, map_package_json = empty_config.map_package_json, task_root_dirs = empty_config.task_root_dirs, search_filters = empty_config.search_filters, } = raw_config;
46
+ return {
47
+ plugins,
48
+ map_package_json,
49
+ task_root_dirs: task_root_dirs.map((p) => resolve(p)),
50
+ search_filters: Array.isArray(search_filters)
51
+ ? search_filters
52
+ : search_filters
53
+ ? [search_filters]
54
+ : [],
55
+ };
56
+ };
37
57
  export const load_config = async (dir = paths.root) => {
38
- const default_config = await create_default_config(create_empty_config());
58
+ const default_config = normalize_config(await create_default_config(create_empty_config()));
39
59
  const config_path = join(dir, GRO_CONFIG_PATH);
40
- let config;
41
- if (existsSync(config_path)) {
42
- const config_module = await import(config_path);
43
- validate_config_module(config_module, config_path);
44
- config =
45
- typeof config_module.default === 'function'
46
- ? await config_module.default(default_config)
47
- : config_module.default;
48
- normalize_config(config);
60
+ if (!existsSync(config_path)) {
61
+ // No user config file found, so return the default.
62
+ return default_config;
49
63
  }
50
- else {
51
- config = default_config;
52
- }
53
- return config;
54
- };
55
- // Mutates `config` with cleaned up values.
56
- const normalize_config = (config) => {
57
- // TODO any validation?
58
- config.task_root_dirs = config.task_root_dirs.map((p) => resolve(p));
64
+ // Import the user's `gro.config.ts`.
65
+ const config_module = await import(config_path);
66
+ validate_config_module(config_module, config_path);
67
+ return normalize_config(typeof config_module.default === 'function'
68
+ ? await config_module.default(default_config)
69
+ : config_module.default);
59
70
  };
60
71
  export const validate_config_module = (config_module, config_path) => {
61
72
  const config = config_module.default;
@@ -1,6 +1,6 @@
1
1
  import { spawn } from '@ryanatkn/belt/process.js';
2
2
  import { print_error } from '@ryanatkn/belt/print.js';
3
- import { green, red } from 'kleur/colors';
3
+ import { green, red } from '@ryanatkn/belt/styletext.js';
4
4
  import { z } from 'zod';
5
5
  import { cp, mkdir, rm } from 'node:fs/promises';
6
6
  import { join, resolve } from 'node:path';
@@ -16,7 +16,6 @@ import { git_check_clean_workspace, git_checkout, git_local_branch_exists, git_r
16
16
  // TODO customize
17
17
  const dir = process.cwd();
18
18
  const INITIAL_FILE_PATH = '.gitkeep';
19
- const INITIAL_FILE_CONTENTS = '';
20
19
  const DEPLOY_DIR = GRO_DIRNAME + '/deploy';
21
20
  const SOURCE_BRANCH = 'main';
22
21
  const TARGET_BRANCH = 'deploy';
@@ -143,14 +142,12 @@ export const task = {
143
142
  // Create the target branch locally and remotely.
144
143
  // This is more complex to avoid churning the cwd.
145
144
  await git_clone_locally(origin, source, dir, resolved_deploy_dir);
146
- await spawn(`git checkout --orphan ${target} && ` +
147
- // TODO there's definitely a better way to do this
148
- `git rm -rf . && ` +
149
- `echo "${INITIAL_FILE_CONTENTS}" >> ${INITIAL_FILE_PATH} && ` +
150
- `git add ${INITIAL_FILE_PATH} && ` +
151
- `git commit -m "init"`, [],
152
- // Use `shell: true` because the above is unwieldy with standard command construction
153
- { ...target_spawn_options, shell: true });
145
+ await spawn('git', ['checkout', '--orphan', target], target_spawn_options);
146
+ // TODO there's definitely a better way to do this
147
+ await spawn('git', ['rm', '-rf', '.'], target_spawn_options);
148
+ await spawn('touch', [INITIAL_FILE_PATH], target_spawn_options);
149
+ await spawn('git', ['add', INITIAL_FILE_PATH], target_spawn_options);
150
+ await spawn('git', ['commit', '-m', 'init'], target_spawn_options);
154
151
  await git_push_to_create(origin, target, target_spawn_options);
155
152
  await git_delete_local_branch(source, target_spawn_options);
156
153
  }
@@ -38,14 +38,23 @@ The default export of a Gro config is `Gro_Config | Create_Gro_Config`:
38
38
 
39
39
  ```ts
40
40
  export interface Create_Gro_Config {
41
- (base_config: Gro_Config): Gro_Config | Promise<Gro_Config>;
41
+ (base_config: Gro_Config): Raw_Gro_Config | Promise<Raw_Gro_Config>;
42
42
  }
43
43
 
44
+ // The strict variant that's used internally and exposed to users in tasks and elsewhere.
44
45
  export interface Gro_Config {
45
46
  plugins: Create_Config_Plugins;
46
47
  map_package_json: Map_Package_Json | null;
47
- task_root_dirs: string[];
48
- search_filters: Path_Filter | Path_Filter[] | null;
48
+ task_root_dirs: Path_Id[];
49
+ search_filters: Path_Filter[];
50
+ }
51
+
52
+ // The relaxed variant that users can provide. Superset of `Gro_Config`.
53
+ export interface Raw_Gro_Config {
54
+ plugins?: Create_Config_Plugins;
55
+ map_package_json?: Map_Package_Json | null;
56
+ task_root_dirs?: string[];
57
+ search_filters?: Path_Filter | Path_Filter[] | null;
49
58
  }
50
59
  ```
51
60
 
@@ -56,7 +65,7 @@ import type {Create_Gro_Config} from '@ryanatkn/gro';
56
65
  import {gro_plugin_sveltekit_app} from '@ryanatkn/gro/gro_plugin_sveltekit_app.js';
57
66
 
58
67
  const config: Create_Gro_Config = async (cfg) => {
59
- // `cfg`, which is equal to `create_empty_config()`,
68
+ // `cfg`, which has type `Gro_Config` and is equal to `create_empty_config()`,
60
69
  // can be mutated or you can return your own.
61
70
  // A return value is required to avoid potential errors and reduce ambiguity.
62
71
 
@@ -80,7 +89,8 @@ const config: Create_Gro_Config = async (cfg) => {
80
89
  );
81
90
  return updated_plugins.concat(create_some_custom_plugin());
82
91
  };
83
- return cfg;
92
+
93
+ return cfg; // return type is `Raw_Gro_Config`, which is a relaxed superset of `Gro_Config`
84
94
  };
85
95
 
86
96
  export default config;
@@ -1,4 +1,4 @@
1
- import { yellow, red } from 'kleur/colors';
1
+ import { yellow, red } from '@ryanatkn/belt/styletext.js';
2
2
  export const print_build_result = (log, build_result) => {
3
3
  for (const error of build_result.errors) {
4
4
  log.error(red('esbuild error'), error);
@@ -1,9 +1,10 @@
1
1
  import type { Spawn_Result } from '@ryanatkn/belt/process.js';
2
2
  import type { Logger } from '@ryanatkn/belt/log.js';
3
+ import { type Cli } from './cli.js';
3
4
  /**
4
5
  * Formats a directory on the filesystem.
5
6
  * If the source directory is given, it also formats all of the root directory files.
6
7
  * This is separated from `./format_file` to avoid importing all of the `prettier` code
7
8
  * inside modules that import this one. (which has a nontrivial cost)
8
9
  */
9
- export declare const format_directory: (log: Logger, dir: string, check?: boolean, extensions?: string, root_paths?: string) => Promise<Spawn_Result>;
10
+ export declare const format_directory: (log: Logger, dir: string, check?: boolean, extensions?: string, root_paths?: string, prettier_cli?: string | Cli) => Promise<Spawn_Result>;
@@ -1,7 +1,8 @@
1
1
  import { paths } from './paths.js';
2
2
  import { GITHUB_DIRNAME, README_FILENAME, SVELTEKIT_CONFIG_FILENAME, VITE_CONFIG_FILENAME, TSCONFIG_FILENAME, GRO_CONFIG_PATH, } from './path_constants.js';
3
- import { print_command_args, serialize_args, to_forwarded_args } from './args.js';
4
- import { spawn_cli } from './cli.js';
3
+ import { serialize_args, to_forwarded_args } from './args.js';
4
+ import { spawn_cli, to_cli_name } from './cli.js';
5
+ const PRETTIER_CLI = 'prettier';
5
6
  const DEFAULT_EXTENSIONS = 'ts,js,json,svelte,html,css,md,yml';
6
7
  const DEFAULT_ROOT_PATHS = `${[
7
8
  README_FILENAME,
@@ -17,17 +18,16 @@ const DEFAULT_ROOT_PATHS = `${[
17
18
  * This is separated from `./format_file` to avoid importing all of the `prettier` code
18
19
  * inside modules that import this one. (which has a nontrivial cost)
19
20
  */
20
- export const format_directory = async (log, dir, check = false, extensions = DEFAULT_EXTENSIONS, root_paths = DEFAULT_ROOT_PATHS) => {
21
- const forwarded_args = to_forwarded_args('prettier');
21
+ export const format_directory = async (log, dir, check = false, extensions = DEFAULT_EXTENSIONS, root_paths = DEFAULT_ROOT_PATHS, prettier_cli = PRETTIER_CLI) => {
22
+ const forwarded_args = to_forwarded_args(to_cli_name(prettier_cli));
22
23
  forwarded_args[check ? 'check' : 'write'] = true;
23
24
  const serialized_args = serialize_args(forwarded_args);
24
25
  serialized_args.push(`${dir}**/*.{${extensions}}`);
25
26
  if (dir === paths.source) {
26
27
  serialized_args.push(`${paths.root}{${root_paths}}`);
27
28
  }
28
- log.info(print_command_args(serialized_args));
29
- const spawned = await spawn_cli('prettier', serialized_args);
29
+ const spawned = await spawn_cli(prettier_cli, serialized_args, log);
30
30
  if (!spawned)
31
- throw new Error('failed to find `prettier` CLI locally or globally, do you need to run `npm i`?');
31
+ throw new Error(`failed to find \`${to_cli_name(prettier_cli)}\` CLI locally or globally, do you need to run \`npm i\`?`);
32
32
  return spawned;
33
33
  };
package/dist/gen.js CHANGED
@@ -1,7 +1,7 @@
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 { red } from 'kleur/colors';
4
+ import { red } from '@ryanatkn/belt/styletext.js';
5
5
  import { existsSync } from 'node:fs';
6
6
  import { print_path } from './paths.js';
7
7
  import { load_modules } from './modules.js';
package/dist/gen.task.js CHANGED
@@ -1,4 +1,4 @@
1
- import { red, green, gray } from 'kleur/colors';
1
+ import { red, green, gray } from '@ryanatkn/belt/styletext.js';
2
2
  import { print_ms, print_error } from '@ryanatkn/belt/print.js';
3
3
  import { plural } from '@ryanatkn/belt/string.js';
4
4
  import { z } from 'zod';
package/dist/github.d.ts CHANGED
@@ -13,16 +13,16 @@ export declare const Github_Pull_Request: z.ZodObject<{
13
13
  }>;
14
14
  }, "strip", z.ZodTypeAny, {
15
15
  number: number;
16
- url: string;
17
16
  id: number;
17
+ url: string;
18
18
  html_url: string;
19
19
  user: {
20
20
  login: string;
21
21
  };
22
22
  }, {
23
23
  number: number;
24
- url: string;
25
24
  id: number;
25
+ url: string;
26
26
  html_url: string;
27
27
  user: {
28
28
  login: string;
@@ -22,9 +22,13 @@ export interface Options {
22
22
  * Pass a function to customize which files get copied.
23
23
  */
24
24
  well_known_src_files?: boolean | Copy_File_Filter;
25
+ /**
26
+ * The Vite CLI to use.
27
+ */
28
+ vite_cli?: string;
25
29
  }
26
30
  export type Host_Target = 'github_pages' | 'static' | 'node';
27
31
  export interface Copy_File_Filter {
28
32
  (file_path: string): boolean | Promise<boolean>;
29
33
  }
30
- export declare const gro_plugin_sveltekit_app: ({ host_target, well_known_package_json, well_known_src_json, well_known_src_files, }?: Options) => Plugin<Plugin_Context>;
34
+ export declare const gro_plugin_sveltekit_app: ({ host_target, well_known_package_json, well_known_src_json, well_known_src_files, vite_cli, }?: Options) => Plugin<Plugin_Context>;