@ryanatkn/gro 0.164.1 → 0.165.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.
@@ -6,8 +6,16 @@ import type {Logger} from '@ryanatkn/belt/log.js';
6
6
  import {styleText as st} from 'node:util';
7
7
  import {Package_Json, Package_Json_Exports} from '@ryanatkn/belt/package_json.js';
8
8
 
9
- import {paths, gro_paths, IS_THIS_GRO, replace_extension} from './paths.ts';
10
- import {PACKAGE_JSON_FILENAME, SVELTEKIT_DIST_DIRNAME} from './constants.ts';
9
+ import {paths, gro_paths, IS_THIS_GRO} from './paths.ts';
10
+ import {
11
+ PACKAGE_JSON_FILENAME,
12
+ SVELTEKIT_DIST_DIRNAME,
13
+ TS_MATCHER,
14
+ JS_MATCHER,
15
+ SVELTE_MATCHER,
16
+ JSON_MATCHER,
17
+ CSS_MATCHER,
18
+ } from './constants.ts';
11
19
  import {search_fs} from './search_fs.ts';
12
20
  import {has_sveltekit_library} from './sveltekit_helpers.ts';
13
21
  import {GITHUB_REPO_MATCHER} from './github.ts';
@@ -55,8 +63,7 @@ export const sync_package_json = async (
55
63
  const updated = await update_package_json(
56
64
  async (package_json) => {
57
65
  if (has_sveltekit_library(package_json).ok) {
58
- const exports = to_package_exports(exported_paths);
59
- package_json.exports = exports;
66
+ package_json.exports = to_package_exports(exported_paths);
60
67
  }
61
68
  const mapped = await map_package_json(package_json);
62
69
  return mapped ? parse_package_json(Package_Json, mapped) : mapped;
@@ -115,53 +122,52 @@ export const update_package_json = async (
115
122
 
116
123
  const is_index = (path: string): boolean => path === 'index.ts' || path === 'index.js';
117
124
 
118
- // TODO support subpath patterns as the main concise way to do things
119
- // https://nodejs.org/api/packages.html#subpath-patterns
120
-
121
125
  export const to_package_exports = (paths: Array<string>): Package_Json_Exports => {
122
- const sorted = paths
123
- .slice()
124
- .sort((a, b) => (is_index(a) ? -1 : is_index(b) ? 1 : a.localeCompare(b)));
125
- // Add the package.json after the index, if one exists.
126
- // Including the `./` here ensures we don't conflict with any potential `$lib/package.json`.
127
- const final_sorted = is_index(sorted[0])
128
- ? [sorted[0]].concat('./package.json', sorted.slice(1))
129
- : ['./package.json'].concat(sorted);
130
- const exports: Package_Json_Exports = {};
131
- for (const path of final_sorted) {
132
- if (path === './package.json') {
133
- exports['./package.json'] = './package.json';
134
- } else if (path.endsWith('.json.d.ts')) {
135
- const json_path = path.substring(0, path.length - 5);
136
- exports['./' + json_path] = {
137
- types: IMPORT_PREFIX + path,
138
- default: IMPORT_PREFIX + json_path, // assuming a matching json file
139
- };
140
- } else if (path.endsWith('.ts') && !path.endsWith('.d.ts')) {
141
- const js_path = replace_extension(path, '.js');
142
- const key = is_index(path) ? '.' : './' + js_path;
143
- exports[key] = {
144
- types: IMPORT_PREFIX + replace_extension(path, '.d.ts'),
145
- default: IMPORT_PREFIX + js_path,
146
- };
147
- } else if (path.endsWith('.js')) {
148
- const key = is_index(path) ? '.' : './' + path;
149
- exports[key] = {
150
- types: IMPORT_PREFIX + replace_extension(path, '.d.ts'), // assuming JSDoc types
151
- default: IMPORT_PREFIX + path,
152
- };
153
- } else if (path.endsWith('.svelte')) {
154
- exports['./' + path] = {
155
- types: IMPORT_PREFIX + path + '.d.ts',
156
- svelte: IMPORT_PREFIX + path,
157
- default: IMPORT_PREFIX + path, // needed for loader imports
158
- };
159
- } else {
160
- exports['./' + path] = {
161
- default: IMPORT_PREFIX + path,
162
- };
163
- }
126
+ const has_index = paths.some(is_index);
127
+ const has_js = paths.some((p) => TS_MATCHER.test(p) || JS_MATCHER.test(p));
128
+ const has_svelte = paths.some((p) => SVELTE_MATCHER.test(p));
129
+ const has_json = paths.some((p) => JSON_MATCHER.test(p));
130
+ const has_css = paths.some((p) => CSS_MATCHER.test(p));
131
+
132
+ const exports: Package_Json_Exports = {
133
+ './package.json': './package.json',
134
+ };
135
+
136
+ if (has_index) {
137
+ exports['.'] = {
138
+ types: IMPORT_PREFIX + 'index.d.ts',
139
+ default: IMPORT_PREFIX + 'index.js',
140
+ };
164
141
  }
142
+
143
+ if (has_js) {
144
+ exports['./*.js'] = {
145
+ types: IMPORT_PREFIX + '*.d.ts',
146
+ default: IMPORT_PREFIX + '*.js',
147
+ };
148
+ }
149
+
150
+ if (has_svelte) {
151
+ exports['./*.svelte'] = {
152
+ types: IMPORT_PREFIX + '*.svelte.d.ts',
153
+ svelte: IMPORT_PREFIX + '*.svelte',
154
+ default: IMPORT_PREFIX + '*.svelte',
155
+ };
156
+ }
157
+
158
+ if (has_json) {
159
+ exports['./*.json'] = {
160
+ types: IMPORT_PREFIX + '*.json.d.ts',
161
+ default: IMPORT_PREFIX + '*.json',
162
+ };
163
+ }
164
+
165
+ if (has_css) {
166
+ exports['./*.css'] = {
167
+ default: IMPORT_PREFIX + '*.css',
168
+ };
169
+ }
170
+
165
171
  return parse_or_throw_formatted_error('package.json#exports', Package_Json_Exports, exports);
166
172
  };
167
173
 
@@ -1,4 +1,4 @@
1
- import {join, extname} from 'node:path';
1
+ import {join} from 'node:path';
2
2
  import {ensure_end, strip_start} from '@ryanatkn/belt/string.js';
3
3
  import {existsSync} from 'node:fs';
4
4
  import ts from 'typescript';
@@ -7,7 +7,8 @@ import {Src_Json, Src_Modules} from '@ryanatkn/belt/src_json.js';
7
7
 
8
8
  import {paths, replace_extension} from './paths.ts';
9
9
  import {parse_exports} from './parse_exports.ts';
10
- import {TS_MATCHER} from './constants.ts';
10
+ import {TS_MATCHER, SVELTE_MATCHER, JSON_MATCHER, CSS_MATCHER} from './constants.ts';
11
+ import {search_fs} from './search_fs.ts';
11
12
 
12
13
  export type Map_Src_Json = (src_json: Src_Json) => Src_Json | null | Promise<Src_Json | null>;
13
14
 
@@ -29,34 +30,7 @@ export const to_src_modules = (
29
30
  ): Src_Modules | undefined => {
30
31
  if (!exports) return;
31
32
 
32
- // Prepare a list of files to analyze
33
- const file_paths: Array<{export_key: string; file_path: string}> = [];
34
- for (const [k, _v] of Object.entries(exports)) {
35
- // Handle different file types
36
- const source_file_path =
37
- k === '.' || k === './'
38
- ? 'index.ts'
39
- : strip_start(k.endsWith('.js') ? replace_extension(k, '.ts') : k, './');
40
-
41
- const source_file_id = join(lib_path, source_file_path);
42
-
43
- // Check if file exists
44
- if (!existsSync(source_file_id)) {
45
- // Handle non-TypeScript files (Svelte, CSS, JSON)
46
- const extension = extname(source_file_id);
47
- if (extension === '.svelte' || extension === '.css' || extension === '.json') {
48
- file_paths.push({export_key: k, file_path: source_file_id});
49
- continue;
50
- }
51
-
52
- // TODO still an error for unknown files running `gro gen`, can it use the filer to invalidate correctly?
53
- throw Error(
54
- `Failed to infer source file from package.json export path ${k} - the inferred file ${source_file_id} does not exist`,
55
- );
56
- }
57
-
58
- file_paths.push({export_key: k, file_path: source_file_id});
59
- }
33
+ const file_paths = collect_file_paths(exports, lib_path);
60
34
 
61
35
  // Create a TypeScript program for all TypeScript files
62
36
  const ts_files = file_paths
@@ -98,3 +72,101 @@ export const to_src_modules = (
98
72
 
99
73
  return result;
100
74
  };
75
+
76
+ const collect_file_paths = (
77
+ exports: Package_Json_Exports,
78
+ lib_path: string,
79
+ ): Array<{export_key: string; file_path: string}> => {
80
+ const file_paths: Array<{export_key: string; file_path: string}> = [];
81
+
82
+ // Handle string exports (single default export)
83
+ if (typeof exports === 'string') {
84
+ const source_file = infer_source_from_export(exports, lib_path);
85
+ if (source_file) {
86
+ file_paths.push({export_key: '.', file_path: source_file});
87
+ } else {
88
+ throw Error(
89
+ `Failed to infer source file from package.json string export "${exports}" - no matching file found in ${lib_path}`,
90
+ );
91
+ }
92
+ return file_paths;
93
+ }
94
+
95
+ // Handle object exports
96
+ for (const k in exports) {
97
+ // Skip package.json
98
+ if (k === './package.json') continue;
99
+
100
+ // Check if this is a pattern export
101
+ if (k.includes('*')) {
102
+ // Handle pattern exports by finding matching files in lib
103
+ const matching_files = search_fs(lib_path, {
104
+ file_filter: (path) => {
105
+ const p = path.replace(ensure_end(lib_path, '/'), '');
106
+ // Only match files in the root directory (no subdirectories)
107
+ if (p.includes('/')) return false;
108
+
109
+ // Match based on the export pattern
110
+ if (k === './*.js') {
111
+ return TS_MATCHER.test(p) && !p.endsWith('.d.ts') && !p.endsWith('.test.ts');
112
+ } else if (k === './*.svelte') {
113
+ return SVELTE_MATCHER.test(p);
114
+ } else if (k === './*.json') {
115
+ return JSON_MATCHER.test(p);
116
+ } else if (k === './*.css') {
117
+ return CSS_MATCHER.test(p);
118
+ }
119
+ return false;
120
+ },
121
+ });
122
+
123
+ // Add each matching file
124
+ for (const file of matching_files) {
125
+ let export_path: string;
126
+ if (file.path.endsWith('.ts')) {
127
+ export_path = './' + file.path.replace('.ts', '.js');
128
+ } else {
129
+ export_path = './' + file.path;
130
+ }
131
+ file_paths.push({export_key: export_path, file_path: file.id});
132
+ }
133
+ } else {
134
+ // Handle explicit exports (non-patterns)
135
+ const source_file = infer_source_from_export(k, lib_path);
136
+ if (source_file) {
137
+ file_paths.push({export_key: k, file_path: source_file});
138
+ } else {
139
+ throw Error(
140
+ `Failed to infer source file from package.json export path ${k} - no matching file found in ${lib_path}`,
141
+ );
142
+ }
143
+ }
144
+ }
145
+
146
+ return file_paths;
147
+ };
148
+
149
+ const infer_source_from_export = (export_path: string, lib_path: string): string | null => {
150
+ // Handle index specially
151
+ if (export_path === '.' || export_path === './') {
152
+ const index_ts = join(lib_path, 'index.ts');
153
+ if (existsSync(index_ts)) return index_ts;
154
+ const index_js = join(lib_path, 'index.js');
155
+ if (existsSync(index_js)) return index_js;
156
+ return null;
157
+ }
158
+
159
+ const clean_path = strip_start(export_path, './');
160
+
161
+ // For .js exports, try .ts first
162
+ if (clean_path.endsWith('.js')) {
163
+ const ts_path = join(lib_path, replace_extension(clean_path, '.ts'));
164
+ if (existsSync(ts_path)) return ts_path;
165
+ }
166
+
167
+ // Try the exact path
168
+ const exact_path = join(lib_path, clean_path);
169
+ if (existsSync(exact_path)) return exact_path;
170
+
171
+ return null;
172
+ };