@ryanatkn/gro 0.141.1 → 0.143.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 (114) hide show
  1. package/dist/build.task.js +1 -1
  2. package/dist/changeset.task.d.ts.map +1 -1
  3. package/dist/changeset.task.js +4 -4
  4. package/dist/check.task.d.ts.map +1 -1
  5. package/dist/check.task.js +4 -2
  6. package/dist/clean_fs.js +1 -1
  7. package/dist/cli.js +1 -1
  8. package/dist/{path_constants.d.ts → constants.d.ts} +4 -1
  9. package/dist/constants.d.ts.map +1 -0
  10. package/dist/{path_constants.js → constants.js} +3 -0
  11. package/dist/deploy.task.js +1 -1
  12. package/dist/dev.task.js +1 -1
  13. package/dist/esbuild_plugin_svelte.js +1 -1
  14. package/dist/esbuild_plugin_sveltekit_local_imports.js +1 -1
  15. package/dist/esbuild_plugin_sveltekit_shim_app.js +1 -1
  16. package/dist/esbuild_plugin_sveltekit_shim_env.js +1 -1
  17. package/dist/filer.d.ts +7 -0
  18. package/dist/filer.d.ts.map +1 -1
  19. package/dist/filer.js +28 -29
  20. package/dist/format.task.d.ts.map +1 -1
  21. package/dist/format.task.js +2 -2
  22. package/dist/format_directory.d.ts +1 -1
  23. package/dist/format_directory.d.ts.map +1 -1
  24. package/dist/format_directory.js +5 -6
  25. package/dist/gen.task.js +1 -1
  26. package/dist/gro_config.d.ts +12 -2
  27. package/dist/gro_config.d.ts.map +1 -1
  28. package/dist/gro_config.js +10 -6
  29. package/dist/gro_helpers.d.ts +1 -1
  30. package/dist/gro_helpers.d.ts.map +1 -1
  31. package/dist/gro_helpers.js +3 -3
  32. package/dist/gro_plugin_server.js +4 -4
  33. package/dist/gro_plugin_sveltekit_app.d.ts.map +1 -1
  34. package/dist/gro_plugin_sveltekit_app.js +5 -5
  35. package/dist/gro_plugin_sveltekit_library.js +7 -7
  36. package/dist/loader.d.ts.map +1 -1
  37. package/dist/loader.js +23 -17
  38. package/dist/module.js +1 -1
  39. package/dist/moss_helpers.d.ts +1 -1
  40. package/dist/moss_helpers.d.ts.map +1 -1
  41. package/dist/moss_helpers.js +4 -3
  42. package/dist/package.d.ts +334 -172
  43. package/dist/package.d.ts.map +1 -1
  44. package/dist/package.js +51 -51
  45. package/dist/package_json.d.ts +9 -6
  46. package/dist/package_json.d.ts.map +1 -1
  47. package/dist/package_json.js +21 -6
  48. package/dist/package_meta.d.ts +1 -1
  49. package/dist/parse_imports.js +1 -1
  50. package/dist/paths.js +1 -1
  51. package/dist/publish.task.d.ts.map +1 -1
  52. package/dist/publish.task.js +7 -20
  53. package/dist/reinstall.task.js +11 -11
  54. package/dist/resolve_node_specifier.d.ts +7 -1
  55. package/dist/resolve_node_specifier.d.ts.map +1 -1
  56. package/dist/resolve_node_specifier.js +78 -14
  57. package/dist/resolve_specifier.d.ts +2 -6
  58. package/dist/resolve_specifier.d.ts.map +1 -1
  59. package/dist/resolve_specifier.js +2 -6
  60. package/dist/run_task.js +1 -1
  61. package/dist/src_json.d.ts +39 -3
  62. package/dist/src_json.d.ts.map +1 -1
  63. package/dist/sveltekit_config.d.ts +1 -1
  64. package/dist/sveltekit_config.d.ts.map +1 -1
  65. package/dist/sveltekit_config.js +1 -1
  66. package/dist/sveltekit_helpers.d.ts +3 -3
  67. package/dist/sveltekit_helpers.d.ts.map +1 -1
  68. package/dist/sveltekit_helpers.js +7 -7
  69. package/dist/sync.task.js +5 -5
  70. package/dist/upgrade.task.d.ts.map +1 -1
  71. package/dist/upgrade.task.js +4 -2
  72. package/dist/watch_dir.d.ts.map +1 -1
  73. package/dist/watch_dir.js +5 -5
  74. package/package.json +18 -18
  75. package/src/lib/build.task.ts +1 -1
  76. package/src/lib/changeset.task.ts +4 -3
  77. package/src/lib/check.task.ts +4 -2
  78. package/src/lib/clean_fs.ts +1 -1
  79. package/src/lib/cli.ts +1 -1
  80. package/src/lib/{path_constants.ts → constants.ts} +4 -0
  81. package/src/lib/deploy.task.ts +1 -1
  82. package/src/lib/dev.task.ts +1 -1
  83. package/src/lib/esbuild_plugin_svelte.ts +1 -1
  84. package/src/lib/esbuild_plugin_sveltekit_local_imports.ts +1 -1
  85. package/src/lib/esbuild_plugin_sveltekit_shim_app.ts +1 -1
  86. package/src/lib/esbuild_plugin_sveltekit_shim_env.ts +1 -1
  87. package/src/lib/filer.ts +37 -27
  88. package/src/lib/format.task.ts +10 -2
  89. package/src/lib/format_directory.ts +10 -9
  90. package/src/lib/gen.task.ts +1 -1
  91. package/src/lib/gro_config.ts +23 -5
  92. package/src/lib/gro_helpers.ts +3 -2
  93. package/src/lib/gro_plugin_server.ts +4 -4
  94. package/src/lib/gro_plugin_sveltekit_app.ts +7 -5
  95. package/src/lib/gro_plugin_sveltekit_library.ts +7 -7
  96. package/src/lib/loader.ts +25 -17
  97. package/src/lib/module.ts +1 -1
  98. package/src/lib/moss_helpers.ts +4 -2
  99. package/src/lib/package.ts +51 -51
  100. package/src/lib/package_json.ts +23 -6
  101. package/src/lib/package_meta.ts +1 -1
  102. package/src/lib/parse_imports.ts +1 -1
  103. package/src/lib/paths.ts +1 -1
  104. package/src/lib/publish.task.ts +7 -22
  105. package/src/lib/reinstall.task.ts +11 -11
  106. package/src/lib/resolve_node_specifier.ts +100 -18
  107. package/src/lib/resolve_specifier.ts +2 -6
  108. package/src/lib/run_task.ts +1 -1
  109. package/src/lib/sveltekit_config.ts +2 -2
  110. package/src/lib/sveltekit_helpers.ts +7 -4
  111. package/src/lib/sync.task.ts +5 -5
  112. package/src/lib/upgrade.task.ts +4 -2
  113. package/src/lib/watch_dir.ts +6 -6
  114. package/dist/path_constants.d.ts.map +0 -1
package/src/lib/filer.ts CHANGED
@@ -18,6 +18,8 @@ import {resolve_specifier} from './resolve_specifier.js';
18
18
  import {default_sveltekit_config} from './sveltekit_config.js';
19
19
  import {map_sveltekit_aliases} from './sveltekit_helpers.js';
20
20
  import {Unreachable_Error} from '@ryanatkn/belt/error.js';
21
+ import {resolve_node_specifier} from './resolve_node_specifier.js';
22
+ import type {Package_Json} from './package_json.js';
21
23
  // TODO see below
22
24
  // import {resolve_node_specifier} from './resolve_node_specifier.js';
23
25
 
@@ -31,6 +33,11 @@ export interface Source_File {
31
33
  * We create the file in memory to track its dependents regardless of its existence on disk.
32
34
  */
33
35
  contents: string | null;
36
+ /**
37
+ * Is the source file outside of the `root_dir` or excluded by `watch_dir_options.filter`?
38
+ */
39
+ external: boolean;
40
+ ctime: number | null;
34
41
  mtime: number | null;
35
42
  dependents: Map<Path_Id, Source_File>;
36
43
  dependencies: Map<Path_Id, Source_File>;
@@ -43,6 +50,7 @@ export type On_Filer_Change = (change: Watcher_Change, source_file: Source_File)
43
50
  export interface Options {
44
51
  watch_dir?: typeof watch_dir;
45
52
  watch_dir_options?: Partial<Omit_Strict<Watch_Dir_Options, 'on_change'>>;
53
+ package_json_cache?: Record<string, Package_Json>;
46
54
  }
47
55
 
48
56
  export class Filer {
@@ -53,10 +61,13 @@ export class Filer {
53
61
  #watch_dir: typeof watch_dir;
54
62
  #watch_dir_options: Partial<Watch_Dir_Options>;
55
63
 
64
+ #package_json_cache: Record<string, Package_Json>;
65
+
56
66
  constructor(options: Options = EMPTY_OBJECT) {
57
67
  this.#watch_dir = options.watch_dir ?? watch_dir;
58
68
  this.#watch_dir_options = options.watch_dir_options ?? EMPTY_OBJECT;
59
69
  this.root_dir = resolve(options.watch_dir_options?.dir ?? paths.source);
70
+ this.#package_json_cache = options.package_json_cache ?? {};
60
71
  }
61
72
 
62
73
  #watching: Watch_Node_Fs | undefined;
@@ -74,11 +85,17 @@ export class Filer {
74
85
  const file: Source_File = {
75
86
  id,
76
87
  contents: null,
88
+ external: this.#is_external(id), // TODO maybe filter externals by default? the user needs to configure the filer then
89
+ ctime: null,
77
90
  mtime: null,
78
91
  dependents: new Map(),
79
92
  dependencies: new Map(),
80
93
  };
81
94
  this.files.set(id, file);
95
+ // TODO this may need to be batched/deferred
96
+ if (file.external) {
97
+ this.#on_change({type: 'add', path: file.id, is_directory: false});
98
+ }
82
99
  return file;
83
100
  };
84
101
 
@@ -86,8 +103,7 @@ export class Filer {
86
103
  const file = this.get_or_create(id);
87
104
 
88
105
  const stats = existsSync(id) ? statSync(id) : null;
89
- // const mtime_prev = file.mtime;
90
- // const mtime_changed = mtime_prev !== (stats?.mtimeMs ?? null);
106
+ file.ctime = stats?.ctimeMs ?? null;
91
107
  file.mtime = stats?.mtimeMs ?? null;
92
108
 
93
109
  const new_contents = stats ? readFileSync(id, 'utf8') : null;
@@ -109,36 +125,25 @@ export class Filer {
109
125
  const path = map_sveltekit_aliases(specifier, aliases);
110
126
 
111
127
  // The specifier `path` has now been mapped to its final form, so we can inspect it.
112
- if (path[0] === '.' || path[0] === '/') {
113
- const {path_id} = resolve_specifier(path, dir);
114
- dependencies_removed.delete(path_id);
115
- if (!dependencies_before.has(path_id)) {
116
- const d = this.get_or_create(path_id);
117
- file.dependencies.set(d.id, d);
118
- d.dependents.set(file.id, file);
119
- }
128
+ const resolved =
129
+ path[0] === '.' || path[0] === '/'
130
+ ? resolve_specifier(path, dir)
131
+ : resolve_node_specifier(path, undefined, file.id, this.#package_json_cache, false);
132
+ if (!resolved) continue; // ignore any missing imports like Node identifiers
133
+ const {path_id} = resolved;
134
+ dependencies_removed.delete(path_id);
135
+ if (!dependencies_before.has(path_id)) {
136
+ const d = this.get_or_create(path_id);
137
+ file.dependencies.set(d.id, d);
138
+ d.dependents.set(file.id, file);
120
139
  }
121
- // TODO this doesn't work
122
- // const resolved =
123
- // path[0] === '.' || path[0] === '/'
124
- // ? resolve_specifier(path, dir)
125
- // : resolve_node_specifier(path, dir);
126
- // const {path_id} = resolved;
127
- // dependencies_removed.delete(path_id);
128
- // if (!dependencies_before.has(path_id)) {
129
- // const d = this.get_or_create(path_id);
130
- // file.dependencies.set(d.id, d);
131
- // d.dependents.set(file.id, file);
132
- // }
133
140
  }
134
141
 
135
142
  // update any removed dependencies
136
143
  for (const dependency_removed of dependencies_removed) {
137
- const deleted1 = file.dependencies.delete(dependency_removed);
138
- if (!deleted1) throw Error('expected to delete1 ' + file.id); // TODO @many delete if correct
144
+ file.dependencies.delete(dependency_removed);
139
145
  const dependency_removed_file = this.get_or_create(dependency_removed);
140
- const deleted2 = dependency_removed_file.dependents.delete(file.id);
141
- if (!deleted2) throw Error('expected to delete2 ' + file.id); // TODO @many delete if correct
146
+ dependency_removed_file.dependents.delete(file.id);
142
147
  }
143
148
 
144
149
  return file;
@@ -203,7 +208,7 @@ export class Filer {
203
208
  }
204
209
 
205
210
  #on_change: Watcher_Change_Callback = (change) => {
206
- if (change.is_directory) return;
211
+ if (change.is_directory) return; // TODO manage directories?
207
212
  let source_file: Source_File | null;
208
213
  switch (change.type) {
209
214
  case 'add':
@@ -236,4 +241,9 @@ export class Filer {
236
241
  this.#watching = undefined;
237
242
  }
238
243
  }
244
+
245
+ #is_external(id: string): boolean {
246
+ const {filter} = this.#watch_dir_options;
247
+ return !id.startsWith(this.root_dir + '/') || (!!filter && !filter(id, false));
248
+ }
239
249
  }
@@ -17,10 +17,18 @@ export type Args = z.infer<typeof Args>;
17
17
  export const task: Task<Args> = {
18
18
  summary: 'format source files',
19
19
  Args,
20
- run: async ({args, log}) => {
20
+ run: async ({args, log, config}) => {
21
21
  const {check} = args;
22
22
  // TODO forward prettier args
23
- const format_result = await format_directory(log, paths.source, check);
23
+ const format_result = await format_directory(
24
+ log,
25
+ paths.source,
26
+ check,
27
+ undefined,
28
+ undefined,
29
+ undefined,
30
+ config.pm_cli,
31
+ );
24
32
  if (!format_result.ok) {
25
33
  throw new Task_Error(
26
34
  `Failed ${check ? 'formatting check' : 'to format'}. ${print_spawn_result(format_result)}`,
@@ -9,14 +9,14 @@ import {
9
9
  VITE_CONFIG_FILENAME,
10
10
  TSCONFIG_FILENAME,
11
11
  GRO_CONFIG_PATH,
12
- } from './path_constants.js';
12
+ PM_CLI_DEFAULT,
13
+ PRETTIER_CLI_DEFAULT,
14
+ } from './constants.js';
13
15
  import {serialize_args, to_forwarded_args} from './args.js';
14
16
  import {spawn_cli, to_cli_name, type Cli} from './cli.js';
15
17
 
16
- const PRETTIER_CLI = 'prettier';
17
-
18
- const DEFAULT_EXTENSIONS = 'ts,js,json,svelte,html,css,md,yml';
19
- const DEFAULT_ROOT_PATHS = `${[
18
+ const EXTENSIONS_DEFAULT = 'ts,js,json,svelte,html,css,md,yml';
19
+ const ROOT_PATHS_DEFAULT = `${[
20
20
  README_FILENAME,
21
21
  GRO_CONFIG_PATH,
22
22
  SVELTEKIT_CONFIG_FILENAME,
@@ -35,9 +35,10 @@ export const format_directory = async (
35
35
  log: Logger,
36
36
  dir: string,
37
37
  check = false,
38
- extensions = DEFAULT_EXTENSIONS,
39
- root_paths = DEFAULT_ROOT_PATHS,
40
- prettier_cli: string | Cli = PRETTIER_CLI,
38
+ extensions = EXTENSIONS_DEFAULT,
39
+ root_paths = ROOT_PATHS_DEFAULT,
40
+ prettier_cli: string | Cli = PRETTIER_CLI_DEFAULT,
41
+ pm_cli: string = PM_CLI_DEFAULT,
41
42
  ): Promise<Spawn_Result> => {
42
43
  const forwarded_args = to_forwarded_args(to_cli_name(prettier_cli));
43
44
  forwarded_args[check ? 'check' : 'write'] = true;
@@ -49,7 +50,7 @@ export const format_directory = async (
49
50
  const spawned = await spawn_cli(prettier_cli, serialized_args, log);
50
51
  if (!spawned)
51
52
  throw new Error(
52
- `failed to find \`${to_cli_name(prettier_cli)}\` CLI locally or globally, do you need to run \`npm i\`?`,
53
+ `failed to find \`${to_cli_name(prettier_cli)}\` CLI locally or globally, do you need to run \`${pm_cli} install\`?`,
53
54
  );
54
55
  return spawned;
55
56
  };
@@ -10,7 +10,7 @@ import {format_file} from './format_file.js';
10
10
  import {print_path} from './paths.js';
11
11
  import {log_error_reasons} from './task_logging.js';
12
12
  import {write_gen_results, analyze_gen_results, find_genfiles, load_genfiles} from './gen.js';
13
- import {SOURCE_DIRNAME} from './path_constants.js';
13
+ import {SOURCE_DIRNAME} from './constants.js';
14
14
 
15
15
  export const Args = z
16
16
  .object({
@@ -4,11 +4,13 @@ import {existsSync} from 'node:fs';
4
4
  import {GRO_DIST_DIR, IS_THIS_GRO, paths} from './paths.js';
5
5
  import {
6
6
  GRO_CONFIG_PATH,
7
+ JS_CLI_DEFAULT,
7
8
  NODE_MODULES_DIRNAME,
9
+ PM_CLI_DEFAULT,
8
10
  SERVER_DIST_PATH,
9
11
  SVELTEKIT_BUILD_DIRNAME,
10
12
  SVELTEKIT_DIST_DIRNAME,
11
- } from './path_constants.js';
13
+ } from './constants.js';
12
14
  import create_default_config from './gro.config.default.js';
13
15
  import type {Create_Config_Plugins} from './plugin.js';
14
16
  import type {Map_Package_Json} from './package_json.js';
@@ -40,6 +42,14 @@ export interface Gro_Config {
40
42
  * directories and files are included if they pass all of these filters.
41
43
  */
42
44
  search_filters: Path_Filter[];
45
+ /**
46
+ * The CLI to use that's compatible with `node`.
47
+ */
48
+ js_cli: string;
49
+ /**
50
+ * The CLI to use that's compatible with `npm install` and `npm link`. Defaults to `'npm'`.
51
+ */
52
+ pm_cli: string;
43
53
  }
44
54
 
45
55
  /**
@@ -52,6 +62,8 @@ export interface Raw_Gro_Config {
52
62
  map_package_json?: Map_Package_Json | null;
53
63
  task_root_dirs?: string[];
54
64
  search_filters?: Path_Filter | Path_Filter[] | null;
65
+ js_cli?: string;
66
+ pm_cli?: string;
55
67
  }
56
68
 
57
69
  export type Create_Gro_Config = (
@@ -67,7 +79,9 @@ export const create_empty_gro_config = (): Gro_Config => ({
67
79
  IS_THIS_GRO ? null : paths.root,
68
80
  IS_THIS_GRO ? null : GRO_DIST_DIR,
69
81
  ].filter((v) => v !== null),
70
- search_filters: [(id) => !DEFAULT_SEARCH_EXCLUDER.test(id)],
82
+ search_filters: [(id) => !SEARCH_EXCLUDER_DEFAULT.test(id)],
83
+ js_cli: JS_CLI_DEFAULT,
84
+ pm_cli: PM_CLI_DEFAULT,
71
85
  });
72
86
 
73
87
  /**
@@ -76,7 +90,7 @@ export const create_empty_gro_config = (): Gro_Config => ({
76
90
  * Customize via `search_filters` in the `Gro_Config`.
77
91
  * See the test cases for the exact behavior.
78
92
  */
79
- export const DEFAULT_SEARCH_EXCLUDER = new RegExp(
93
+ export const SEARCH_EXCLUDER_DEFAULT = new RegExp(
80
94
  `(${
81
95
  '(^|/)\\.[^/]+' + // exclude all `.`-prefixed directories
82
96
  // TODO probably change to `pkg.name` instead of this catch-all (also `gro` below)
@@ -91,13 +105,13 @@ export const DEFAULT_SEARCH_EXCLUDER = new RegExp(
91
105
  const default_map_package_json: Map_Package_Json = (package_json) => {
92
106
  if (package_json.exports) {
93
107
  package_json.exports = Object.fromEntries(
94
- Object.entries(package_json.exports).filter(([k]) => !DEFAULT_EXPORTS_EXCLUDER.test(k)),
108
+ Object.entries(package_json.exports).filter(([k]) => !EXPORTS_EXCLUDER_DEFAULT.test(k)),
95
109
  );
96
110
  }
97
111
  return package_json;
98
112
  };
99
113
 
100
- export const DEFAULT_EXPORTS_EXCLUDER = /(\.md|\.(test|ignore)\.|\/(test|fixtures|ignore)\/)/;
114
+ export const EXPORTS_EXCLUDER_DEFAULT = /(\.md|\.(test|ignore)\.|\/(test|fixtures|ignore)\/)/;
101
115
 
102
116
  /**
103
117
  * Transforms a `Raw_Gro_Config` to the more strict `Gro_Config`.
@@ -112,6 +126,8 @@ export const normalize_gro_config = (raw_config: Raw_Gro_Config): Gro_Config =>
112
126
  map_package_json = empty_config.map_package_json,
113
127
  task_root_dirs = empty_config.task_root_dirs,
114
128
  search_filters = empty_config.search_filters,
129
+ js_cli = empty_config.js_cli,
130
+ pm_cli = empty_config.pm_cli,
115
131
  } = raw_config;
116
132
  return {
117
133
  plugins,
@@ -122,6 +138,8 @@ export const normalize_gro_config = (raw_config: Raw_Gro_Config): Gro_Config =>
122
138
  : search_filters
123
139
  ? [search_filters]
124
140
  : [],
141
+ js_cli,
142
+ pm_cli,
125
143
  };
126
144
  };
127
145
 
@@ -3,7 +3,7 @@ import {join, resolve} from 'node:path';
3
3
  import {fileURLToPath} from 'node:url';
4
4
  import {spawn, type Spawn_Result} from '@ryanatkn/belt/process.js';
5
5
 
6
- import {NODE_MODULES_DIRNAME, SVELTEKIT_DIST_DIRNAME} from './path_constants.js';
6
+ import {JS_CLI_DEFAULT, NODE_MODULES_DIRNAME, SVELTEKIT_DIST_DIRNAME} from './constants.js';
7
7
 
8
8
  /*
9
9
 
@@ -84,6 +84,7 @@ export const spawn_with_loader = (
84
84
  loader_path: string,
85
85
  invoke_path: string,
86
86
  argv: string[],
87
+ js_cli = JS_CLI_DEFAULT, // TODO source from config when possible
87
88
  ): Promise<Spawn_Result> => {
88
89
  const args = [
89
90
  '--import',
@@ -99,5 +100,5 @@ export const spawn_with_loader = (
99
100
  args.push('-C', 'development'); // same as `--conditions`
100
101
  }
101
102
  args.push(invoke_path, ...argv);
102
- return spawn('node', args);
103
+ return spawn(js_cli, args);
103
104
  };
@@ -11,7 +11,7 @@ import {throttle} from '@ryanatkn/belt/throttle.js';
11
11
  import type {Plugin} from './plugin.js';
12
12
  import {base_path_to_path_id, LIB_DIRNAME, paths} from './paths.js';
13
13
  import type {Path_Id} from './path.js';
14
- import {GRO_DEV_DIRNAME, SERVER_DIST_PATH} from './path_constants.js';
14
+ import {GRO_DEV_DIRNAME, SERVER_DIST_PATH} from './constants.js';
15
15
  import {watch_dir, type Watch_Node_Fs} from './watch_dir.js';
16
16
  import {init_sveltekit_config, default_sveltekit_config} from './sveltekit_config.js';
17
17
  import {esbuild_plugin_sveltekit_shim_app} from './esbuild_plugin_sveltekit_shim_app.js';
@@ -119,7 +119,7 @@ export const gro_plugin_server = ({
119
119
  target = 'esnext',
120
120
  esbuild_build_options = identity,
121
121
  rebuild_throttle_delay = 1000,
122
- cli_command = 'node',
122
+ cli_command,
123
123
  run, // `dev` default is not available in this scope
124
124
  }: Options = {}): Plugin => {
125
125
  let build_ctx: esbuild.BuildContext | null = null;
@@ -129,7 +129,7 @@ export const gro_plugin_server = ({
129
129
 
130
130
  return {
131
131
  name: 'gro_plugin_server',
132
- setup: async ({dev, watch, timings, log}) => {
132
+ setup: async ({dev, watch, timings, log, config}) => {
133
133
  const parsed_sveltekit_config =
134
134
  !sveltekit_config && strip_end(dir, '/') === process.cwd()
135
135
  ? default_sveltekit_config
@@ -256,7 +256,7 @@ export const gro_plugin_server = ({
256
256
  cli_args.push('-C', 'development'); // same as `--conditions`
257
257
  }
258
258
  cli_args.push(server_outpath);
259
- server_process = spawn_restartable_process(cli_command, cli_args);
259
+ server_process = spawn_restartable_process(cli_command ?? config.js_cli, cli_args);
260
260
  }
261
261
  },
262
262
  teardown: async () => {
@@ -8,9 +8,9 @@ import {serialize_package_json, type Map_Package_Json, load_package_json} from '
8
8
  import {Task_Error} from './task.js';
9
9
  import {find_cli, spawn_cli, spawn_cli_process} from './cli.js';
10
10
  import {type Map_Src_Json, serialize_src_json, create_src_json} from './src_json.js';
11
- import {DEFAULT_EXPORTS_EXCLUDER} from './gro_config.js';
11
+ import {EXPORTS_EXCLUDER_DEFAULT} from './gro_config.js';
12
12
  import {default_sveltekit_config} from './sveltekit_config.js';
13
- import {SOURCE_DIRNAME} from './path_constants.js';
13
+ import {SOURCE_DIRNAME} from './constants.js';
14
14
  import {VITE_CLI} from './sveltekit_helpers.js';
15
15
 
16
16
  export interface Options {
@@ -57,10 +57,12 @@ export const gro_plugin_sveltekit_app = ({
57
57
  let sveltekit_process: Spawned_Process | undefined = undefined;
58
58
  return {
59
59
  name: 'gro_plugin_sveltekit_app',
60
- setup: async ({dev, watch, log}) => {
60
+ setup: async ({dev, watch, log, config}) => {
61
61
  const found_vite_cli = find_cli(vite_cli);
62
62
  if (!found_vite_cli)
63
- throw new Error(`Failed to find Vite CLI \`${vite_cli}\`, do you need to run \`npm i\`?`);
63
+ throw new Error(
64
+ `Failed to find Vite CLI \`${vite_cli}\`, do you need to run \`${config.pm_cli} i\`?`,
65
+ );
64
66
  if (dev) {
65
67
  // `vite dev` in development mode
66
68
  if (watch) {
@@ -123,7 +125,7 @@ export const gro_plugin_sveltekit_app = ({
123
125
  assets_path,
124
126
  '.well-known',
125
127
  well_known_src_files === true
126
- ? (file_path) => !DEFAULT_EXPORTS_EXCLUDER.test(file_path)
128
+ ? (file_path) => !EXPORTS_EXCLUDER_DEFAULT.test(file_path)
127
129
  : well_known_src_files,
128
130
  )
129
131
  : null,
@@ -28,17 +28,17 @@ export const gro_plugin_sveltekit_library = ({
28
28
  }: Options = {}): Plugin => {
29
29
  return {
30
30
  name: 'gro_plugin_sveltekit_library',
31
- setup: async ({dev, log}) => {
31
+ setup: async ({dev, log, config}) => {
32
32
  if (!dev) {
33
- await run_svelte_package(svelte_package_options, svelte_package_cli, log);
33
+ await run_svelte_package(svelte_package_options, svelte_package_cli, log, config.pm_cli);
34
34
  }
35
35
  },
36
- adapt: async ({log, timings}) => {
36
+ adapt: async ({log, timings, config}) => {
37
37
  const package_json = load_package_json();
38
38
 
39
- // `npm link`
39
+ // link the CLI binaries if they exist
40
40
  if (package_json.bin) {
41
- const timing_to_npm_link = timings.start('npm link');
41
+ const timing_to_link = timings.start(`${config.pm_cli} link`);
42
42
  await Promise.all(
43
43
  Object.values(package_json.bin).map(async (bin_path) => {
44
44
  const chmod_result = await spawn('chmod', ['+x', bin_path]);
@@ -47,11 +47,11 @@ export const gro_plugin_sveltekit_library = ({
47
47
  }),
48
48
  );
49
49
  log.info(`linking`);
50
- const link_result = await spawn('npm', ['link', '-f']); // TODO don't use `-f` unless necessary or at all?
50
+ const link_result = await spawn(config.pm_cli, ['link', '-f']); // TODO don't use `-f` unless necessary or at all?
51
51
  if (!link_result.ok) {
52
52
  throw new Task_Error(`Failed to link. ${print_spawn_result(link_result)}`);
53
53
  }
54
- timing_to_npm_link();
54
+ timing_to_link();
55
55
  }
56
56
  },
57
57
  };
package/src/lib/loader.ts CHANGED
@@ -16,8 +16,8 @@ import {
16
16
  } from './sveltekit_shim_app.js';
17
17
  import {default_sveltekit_config} from './sveltekit_config.js';
18
18
  import {SVELTE_MATCHER, SVELTE_RUNES_MATCHER} from './svelte_helpers.js';
19
- import {paths} from './paths.js';
20
- import {JSON_MATCHER, NODE_MODULES_DIRNAME, TS_MATCHER} from './path_constants.js';
19
+ import {IS_THIS_GRO, paths} from './paths.js';
20
+ import {JSON_MATCHER, NODE_MODULES_DIRNAME, TS_MATCHER} from './constants.js';
21
21
  import {to_define_import_meta_env, default_ts_transform_options} from './esbuild_helpers.js';
22
22
  import {resolve_specifier} from './resolve_specifier.js';
23
23
  import {resolve_node_specifier} from './resolve_node_specifier.js';
@@ -173,51 +173,59 @@ export const load: LoadHook = async (url, context, nextLoad) => {
173
173
  };
174
174
 
175
175
  export const resolve: ResolveHook = async (specifier, context, nextResolve) => {
176
+ let s = specifier;
177
+
178
+ // Support SvelteKit `$env` imports
176
179
  if (
177
- specifier === '$env/static/public' ||
178
- specifier === '$env/static/private' ||
179
- specifier === '$env/dynamic/public' ||
180
- specifier === '$env/dynamic/private'
180
+ s === '$env/static/public' ||
181
+ s === '$env/static/private' ||
182
+ s === '$env/dynamic/public' ||
183
+ s === '$env/dynamic/private'
181
184
  ) {
182
185
  // The returned `url` is validated before `load` is called,
183
186
  // so we need a slightly roundabout strategy to pass through the specifier for virtual files.
184
187
  return {
185
- url: pathToFileURL(join(dir, 'src/lib', specifier)).href,
188
+ url: pathToFileURL(join(dir, 'src/lib', s)).href,
186
189
  format: 'module',
187
190
  shortCircuit: true,
188
191
  };
189
192
  }
190
193
 
194
+ // Special case for Gro's dependencies that import into Gro.
195
+ // Without this, we'd need to add a dev dep to Gro for Gro, which causes problems.
196
+ if (IS_THIS_GRO && s.startsWith('@ryanatkn/gro')) {
197
+ s = join(dir, 'dist', s.substring(13));
198
+ }
199
+
191
200
  const parent_url = context.parentURL;
192
201
  if (!parent_url || NODE_MODULES_MATCHER.test(parent_url)) {
193
- return nextResolve(specifier, context);
202
+ return nextResolve(s, context);
194
203
  }
195
204
 
196
- const shimmed = sveltekit_shim_app_specifiers.get(specifier);
205
+ const shimmed = sveltekit_shim_app_specifiers.get(s);
197
206
  if (shimmed !== undefined) {
198
207
  return nextResolve(shimmed, context);
199
208
  }
200
209
 
201
- const path = map_sveltekit_aliases(specifier, aliases);
210
+ s = map_sveltekit_aliases(s, aliases);
202
211
 
203
- // The specifier `path` has now been mapped to its final form, so we can inspect it.
204
- if (path[0] !== '.' && path[0] !== '/') {
212
+ // The specifier has now been mapped to its final form, so we can inspect it.
213
+ if (s[0] !== '.' && s[0] !== '/') {
205
214
  // Resolve to `node_modules`.
206
- if (SVELTE_MATCHER.test(path) || JSON_MATCHER.test(path)) {
215
+ if (SVELTE_MATCHER.test(s) || JSON_MATCHER.test(s)) {
207
216
  // Match the behavior of Vite and esbuild for Svelte and JSON imports.
208
- // TODO `.ts` too
209
- const resolved = resolve_node_specifier(path, dir, parent_url, package_json_cache);
217
+ const resolved = resolve_node_specifier(s, dir, parent_url, package_json_cache)!; // `node:` specifiers shouldn't reach this point, so the assertion is safe
210
218
  return {
211
219
  url: pathToFileURL(resolved.path_id_with_querystring).href,
212
220
  format: 'module',
213
221
  shortCircuit: true,
214
222
  };
215
223
  } else {
216
- return nextResolve(path, context);
224
+ return nextResolve(s, context);
217
225
  }
218
226
  }
219
227
 
220
- const resolved = resolve_specifier(path, dirname(fileURLToPath(parent_url)));
228
+ const resolved = resolve_specifier(s, dirname(fileURLToPath(parent_url)));
221
229
 
222
230
  return {
223
231
  url: pathToFileURL(resolved.path_id_with_querystring).href,
package/src/lib/module.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import {LIB_DIRNAME} from './paths.js';
2
- import {SOURCE_DIR, SOURCE_DIRNAME} from './path_constants.js';
2
+ import {SOURCE_DIR, SOURCE_DIRNAME} from './constants.js';
3
3
 
4
4
  export const MODULE_PATH_SRC_PREFIX = SOURCE_DIR;
5
5
  export const MODULE_PATH_LIB_PREFIX = `$${LIB_DIRNAME}/`;
@@ -3,6 +3,7 @@ import {existsSync} from 'node:fs';
3
3
  import {resolve} from 'node:path';
4
4
 
5
5
  import {has_dep, type Package_Json} from './package_json.js';
6
+ import {NODE_MODULES_DIRNAME, PM_CLI_DEFAULT} from './constants.js';
6
7
 
7
8
  export const MOSS_PACKAGE_DEP_NAME = '@ryanatkn/moss';
8
9
 
@@ -10,13 +11,14 @@ export const MOSS_PACKAGE_DEP_NAME = '@ryanatkn/moss';
10
11
  export const load_moss_plugin = async (
11
12
  package_json?: Package_Json,
12
13
  dep_name = MOSS_PACKAGE_DEP_NAME,
13
- plugin_path = `node_modules/${dep_name}/dist/gro_plugin_moss.js`, // TODO maybe lookup from its `package_json.exports`? kinda unnecessary
14
+ plugin_path = `${NODE_MODULES_DIRNAME}/${dep_name}/dist/gro_plugin_moss.js`, // TODO maybe lookup from its `package_json.exports`? kinda unnecessary
14
15
  local_plugin_path = 'src/lib/gro_plugin_moss.ts',
16
+ pm_cli = PM_CLI_DEFAULT, // TODO source from config when possible, is just needed for error messages
15
17
  ): Promise<Result<{gro_plugin_moss: any}, {message: string}>> => {
16
18
  if (!has_dep(dep_name, package_json)) {
17
19
  return {
18
20
  ok: false,
19
- message: `no dependency found in package.json for ${dep_name}, install it with \`npm i -D ${dep_name}\``,
21
+ message: `no dependency found in package.json for ${dep_name}, install it with \`${pm_cli} install -D ${dep_name}\``,
20
22
  };
21
23
  }
22
24