@ryanatkn/gro 0.141.1 → 0.142.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 (54) hide show
  1. package/dist/changeset.task.d.ts.map +1 -1
  2. package/dist/changeset.task.js +2 -2
  3. package/dist/filer.d.ts +7 -0
  4. package/dist/filer.d.ts.map +1 -1
  5. package/dist/filer.js +28 -29
  6. package/dist/gro_config.d.ts +5 -0
  7. package/dist/gro_config.d.ts.map +1 -1
  8. package/dist/gro_config.js +3 -1
  9. package/dist/gro_plugin_sveltekit_library.js +4 -4
  10. package/dist/loader.d.ts.map +1 -1
  11. package/dist/loader.js +22 -16
  12. package/dist/moss_helpers.d.ts.map +1 -1
  13. package/dist/moss_helpers.js +2 -1
  14. package/dist/package.d.ts +326 -164
  15. package/dist/package.d.ts.map +1 -1
  16. package/dist/package.js +15 -15
  17. package/dist/package_json.d.ts +9 -6
  18. package/dist/package_json.d.ts.map +1 -1
  19. package/dist/package_json.js +19 -4
  20. package/dist/publish.task.d.ts.map +1 -1
  21. package/dist/publish.task.js +3 -8
  22. package/dist/reinstall.task.js +4 -4
  23. package/dist/resolve_node_specifier.d.ts +7 -1
  24. package/dist/resolve_node_specifier.d.ts.map +1 -1
  25. package/dist/resolve_node_specifier.js +77 -13
  26. package/dist/resolve_specifier.d.ts +2 -6
  27. package/dist/resolve_specifier.d.ts.map +1 -1
  28. package/dist/resolve_specifier.js +2 -6
  29. package/dist/src_json.d.ts +39 -3
  30. package/dist/src_json.d.ts.map +1 -1
  31. package/dist/sveltekit_config.d.ts +1 -1
  32. package/dist/sveltekit_config.d.ts.map +1 -1
  33. package/dist/sync.task.js +1 -1
  34. package/dist/upgrade.task.d.ts.map +1 -1
  35. package/dist/upgrade.task.js +4 -2
  36. package/dist/watch_dir.d.ts.map +1 -1
  37. package/dist/watch_dir.js +5 -5
  38. package/package.json +14 -14
  39. package/src/lib/changeset.task.ts +2 -1
  40. package/src/lib/filer.ts +37 -27
  41. package/src/lib/gro_config.ts +8 -0
  42. package/src/lib/gro_plugin_sveltekit_library.ts +4 -4
  43. package/src/lib/loader.ts +24 -16
  44. package/src/lib/moss_helpers.ts +2 -1
  45. package/src/lib/package.ts +15 -15
  46. package/src/lib/package_json.ts +21 -4
  47. package/src/lib/publish.task.ts +3 -9
  48. package/src/lib/reinstall.task.ts +4 -4
  49. package/src/lib/resolve_node_specifier.ts +99 -17
  50. package/src/lib/resolve_specifier.ts +2 -6
  51. package/src/lib/sveltekit_config.ts +1 -1
  52. package/src/lib/sync.task.ts +1 -1
  53. package/src/lib/upgrade.task.ts +4 -2
  54. package/src/lib/watch_dir.ts +6 -6
@@ -1,21 +1,32 @@
1
1
  import {join} from 'node:path';
2
+ import {existsSync} from 'node:fs';
3
+ import {DEV} from 'esm-env';
2
4
 
3
- import {Package_Json, load_package_json} from './package_json.js';
5
+ import {Package_Json, Package_Json_Exports, load_package_json} from './package_json.js';
4
6
  import {paths} from './paths.js';
5
7
  import {NODE_MODULES_DIRNAME} from './path_constants.js';
6
8
  import type {Resolved_Specifier} from './resolve_specifier.js';
7
9
 
10
+ /**
11
+ * Like `resolve_specifier` but for Node specifiers,
12
+ * typically those that aren't relative or absolute.
13
+ * Optionally return `null` instead of throwing by setting
14
+ * `throw_on_missing_package` to `false`.
15
+ */
8
16
  export const resolve_node_specifier = (
9
17
  specifier: string,
10
18
  dir = paths.root,
11
- parent_url?: string,
19
+ parent_path?: string,
12
20
  cache?: Record<string, Package_Json>,
13
- exports_key = specifier.endsWith('.svelte') ? 'svelte' : 'default',
14
- ): Resolved_Specifier => {
21
+ throw_on_missing_package = true,
22
+ // TODO this needs to use `--conditions`/`-C` to determine the correct key
23
+ exports_condition = DEV ? 'development' : 'default',
24
+ ): Resolved_Specifier | null => {
15
25
  const raw = specifier.endsWith('?raw');
16
26
  const mapped_specifier = raw ? specifier.substring(0, specifier.length - 4) : specifier;
17
27
 
18
- let idx!: number;
28
+ // Parse the specifier
29
+ let idx: number = -1;
19
30
  if (mapped_specifier[0] === '@') {
20
31
  // get the index of the second `/`
21
32
  let count = 0;
@@ -29,21 +40,47 @@ export const resolve_node_specifier = (
29
40
  } else {
30
41
  idx = mapped_specifier.indexOf('/');
31
42
  }
32
- const name = mapped_specifier.substring(0, idx);
33
- const path = mapped_specifier.substring(idx + 1);
43
+ const pkg_name = idx === -1 ? mapped_specifier : mapped_specifier.substring(0, idx);
44
+ const module_path = idx === -1 ? '' : mapped_specifier.substring(idx + 1);
45
+ const subpath = module_path ? './' + module_path : '.';
46
+ const package_dir = join(dir, NODE_MODULES_DIRNAME, pkg_name);
34
47
 
35
- const subpath = './' + path;
36
- const package_dir = join(dir, NODE_MODULES_DIRNAME, name);
37
- const package_json = load_package_json(package_dir, cache);
38
- const exported = package_json.exports?.[subpath];
48
+ if (!existsSync(package_dir)) {
49
+ if (throw_on_missing_package) {
50
+ throw Error(
51
+ `Package not found at ${package_dir} for specifier ${specifier}, you may need to npm install or fix the path` +
52
+ (parent_path ? ` imported from ${parent_path}` : ''),
53
+ );
54
+ } else {
55
+ return null;
56
+ }
57
+ }
58
+
59
+ const package_json = load_package_json(package_dir, cache, false);
60
+ const {exported, exports_key} = resolve_subpath(package_json, specifier, subpath);
39
61
  if (!exported) {
40
- // same error message as Node
41
- throw Error(
42
- `[ERR_PACKAGE_PATH_NOT_EXPORTED]: Package subpath '${subpath}' is not defined by "exports" in ${package_dir}/package.json` +
43
- (parent_url ? ` imported from ${parent_url}` : ''),
44
- );
62
+ if (throw_on_missing_package) {
63
+ // same error message as Node
64
+ throw Error(
65
+ `[ERR_PACKAGE_PATH_NOT_EXPORTED]: Package subpath '${subpath}' is not defined by 'exports' in ${package_dir}/package.json` +
66
+ (parent_path ? ` imported from ${parent_path}` : ''),
67
+ );
68
+ } else {
69
+ return null;
70
+ }
45
71
  }
46
- const path_id = join(package_dir, exported[exports_key]);
72
+ const exported_value = resolve_exported_value(exported, exports_key, exports_condition);
73
+ if (exported_value === undefined) {
74
+ if (throw_on_missing_package) {
75
+ throw Error(
76
+ `Package subpath '${subpath}' does not define the key '${exports_key}' in 'exports' in ${package_dir}/package.json` +
77
+ (parent_path ? ` imported from ${parent_path}` : ''),
78
+ );
79
+ } else {
80
+ return null;
81
+ }
82
+ }
83
+ const path_id = join(package_dir, exported_value);
47
84
 
48
85
  return {
49
86
  path_id,
@@ -54,3 +91,48 @@ export const resolve_node_specifier = (
54
91
  namespace: undefined,
55
92
  };
56
93
  };
94
+
95
+ /**
96
+ * Resolves the subpath of a package.json `exports` field based on the `specifier`.
97
+ */
98
+ const resolve_subpath = (
99
+ package_json: Package_Json,
100
+ specifier: string,
101
+ subpath: string,
102
+ ): {exported: Package_Json_Exports[string]; exports_key: string} => {
103
+ const exports_key = specifier.endsWith('.svelte') ? 'svelte' : 'default';
104
+
105
+ const exported =
106
+ subpath === '.' && !package_json.exports
107
+ ? {[exports_key]: package_json.main}
108
+ : package_json.exports?.[subpath];
109
+
110
+ return {exported, exports_key};
111
+ };
112
+
113
+ /**
114
+ * Resolves the exported value based on the exports key and condition.
115
+ */
116
+ const resolve_exported_value = (
117
+ exported: Exclude<Package_Json_Exports[string], undefined>,
118
+ exports_key: string,
119
+ exports_condition: string,
120
+ ): string | undefined => {
121
+ let exported_value = typeof exported === 'string' ? exported : exported[exports_key];
122
+
123
+ // TODO best effort fallback, support `default` but fall back to `import` or `node` as the exports key.
124
+ if (exported_value === undefined && typeof exported !== 'string' && exports_key === 'default') {
125
+ exported_value = exported.import ?? exported.node;
126
+ }
127
+
128
+ // Possibly resolve to conditional exports.
129
+ exported_value =
130
+ exported_value === undefined || typeof exported_value === 'string'
131
+ ? exported_value
132
+ : (exported_value[exports_condition] ??
133
+ exported_value.default ??
134
+ exported_value.import ??
135
+ exported_value.node); // TODO this fallback has corner case bugs for off-spec exports
136
+
137
+ return exported_value;
138
+ };
@@ -20,13 +20,9 @@ export interface Resolved_Specifier {
20
20
  }
21
21
 
22
22
  /**
23
- * Maps a `path` import specifier relative to the `importer`,
23
+ * Maps an import `specifier` relative to `dir`,
24
24
  * and infer the correct extension following Vite conventions.
25
- * If no `.js` file is found for the `path` on the filesystem, it assumes `.ts`.
26
- * @param specifier
27
- * @param dir - if defined, enables relative importers like from esbuild plugins
28
- * @param passthrough_extensions - used to support specifiers that have no file extention, which Vite supports, so we do our best effort
29
- * @returns
25
+ * If no `.js` file is found for the specifier on the filesystem, it assumes `.ts`.
30
26
  */
31
27
  export const resolve_specifier = (specifier: string, dir: string): Resolved_Specifier => {
32
28
  const raw = specifier.endsWith('?raw');
@@ -57,7 +57,7 @@ export interface Parsed_Sveltekit_Config {
57
57
  private_prefix: string | undefined;
58
58
  public_prefix: string | undefined;
59
59
  svelte_compile_options: CompileOptions;
60
- svelte_compile_module_options: CompileOptions;
60
+ svelte_compile_module_options: ModuleCompileOptions;
61
61
  svelte_preprocessors: PreprocessorGroup | PreprocessorGroup[] | undefined;
62
62
  }
63
63
 
@@ -26,7 +26,7 @@ export const task: Task<Args> = {
26
26
  const {sveltekit, package_json, gen, install} = args;
27
27
 
28
28
  if (install) {
29
- const result = await spawn('npm', ['i']);
29
+ const result = await spawn(config.pm_cli, ['i']);
30
30
  if (!result.ok) {
31
31
  throw new Task_Error('Failed npm install');
32
32
  }
@@ -5,6 +5,7 @@ import {Task_Error, type Task} from './task.js';
5
5
  import {extract_deps, load_package_json, type Package_Json_Dep} from './package_json.js';
6
6
  import {Git_Origin, git_pull} from './git.js';
7
7
  import {spawn_cli} from './cli.js';
8
+ import {serialize_args, to_forwarded_args} from './args.js';
8
9
 
9
10
  export const Args = z
10
11
  .object({
@@ -27,7 +28,7 @@ export type Args = z.infer<typeof Args>;
27
28
  export const task: Task<Args> = {
28
29
  summary: 'upgrade deps',
29
30
  Args,
30
- run: async ({args, log}): Promise<void> => {
31
+ run: async ({args, log, config}): Promise<void> => {
31
32
  const {_, only, origin, force, pull, dry} = args;
32
33
 
33
34
  if (_.length && only.length) {
@@ -65,7 +66,8 @@ export const task: Task<Args> = {
65
66
  if (force) {
66
67
  install_args.push('--force');
67
68
  }
68
- await spawn('npm', install_args);
69
+ install_args.push(...serialize_args(to_forwarded_args(config.pm_cli)));
70
+ await spawn(config.pm_cli, install_args);
69
71
 
70
72
  // Sync in a new process to pick up any changes after installing, avoiding some errors.
71
73
  await spawn_cli('gro', ['sync', '--no-install']); // don't install because we do above
@@ -1,6 +1,7 @@
1
1
  import {watch, type ChokidarOptions, type FSWatcher} from 'chokidar';
2
2
  import {relative} from 'node:path';
3
3
  import {statSync} from 'node:fs';
4
+ import {create_deferred, type Deferred} from '@ryanatkn/belt/async.js';
4
5
 
5
6
  import type {Path_Filter} from './path.js';
6
7
 
@@ -42,13 +43,12 @@ export const watch_dir = ({
42
43
  chokidar,
43
44
  }: Options): Watch_Node_Fs => {
44
45
  let watcher: FSWatcher | undefined;
45
- let initing: Promise<void> | undefined;
46
+ let initing: Deferred<void> | undefined;
46
47
 
47
48
  return {
48
49
  init: async () => {
49
- if (initing) return initing;
50
- let resolve: any;
51
- initing = new Promise((r) => (resolve = r)); // TODO `create_deferred`?// cwd: chokidar?.cwd ?? process.cwd()
50
+ if (initing) return initing.promise;
51
+ initing = create_deferred();
52
52
  watcher = watch(dir, {...chokidar});
53
53
  watcher.on('add', (path) => {
54
54
  const final_path = absolute ? path : relative(dir, path);
@@ -77,8 +77,8 @@ export const watch_dir = ({
77
77
  on_change({type: 'delete', path: final_path, is_directory: true});
78
78
  });
79
79
  // wait until ready
80
- watcher.once('ready', () => resolve());
81
- await initing;
80
+ watcher.once('ready', () => initing?.resolve());
81
+ await initing.promise;
82
82
  },
83
83
  close: async () => {
84
84
  initing = undefined;