@ryanatkn/gro 0.129.3 → 0.129.5

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 (124) hide show
  1. package/dist/package.js +4 -4
  2. package/package.json +4 -3
  3. package/src/lib/args.test.ts +59 -0
  4. package/src/lib/args.ts +169 -0
  5. package/src/lib/build.task.ts +37 -0
  6. package/src/lib/changelog.test.ts +138 -0
  7. package/src/lib/changelog.ts +69 -0
  8. package/src/lib/changeset.task.ts +206 -0
  9. package/src/lib/changeset_helpers.ts +13 -0
  10. package/src/lib/check.task.ts +90 -0
  11. package/src/lib/clean.task.ts +46 -0
  12. package/src/lib/clean_fs.ts +54 -0
  13. package/src/lib/cli.ts +97 -0
  14. package/src/lib/commit.task.ts +33 -0
  15. package/src/lib/config.test.ts +71 -0
  16. package/src/lib/config.ts +161 -0
  17. package/src/lib/deploy.task.ts +243 -0
  18. package/src/lib/dev.task.ts +43 -0
  19. package/src/lib/docs/README.gen.md.ts +63 -0
  20. package/src/lib/docs/README.md +20 -0
  21. package/src/lib/docs/build.md +41 -0
  22. package/src/lib/docs/config.md +213 -0
  23. package/src/lib/docs/deploy.md +32 -0
  24. package/src/lib/docs/dev.md +40 -0
  25. package/src/lib/docs/gen.md +269 -0
  26. package/src/lib/docs/gro_plugin_sveltekit_app.md +113 -0
  27. package/src/lib/docs/package_json.md +33 -0
  28. package/src/lib/docs/plugin.md +50 -0
  29. package/src/lib/docs/publish.md +137 -0
  30. package/src/lib/docs/task.md +391 -0
  31. package/src/lib/docs/tasks.gen.md.ts +90 -0
  32. package/src/lib/docs/tasks.md +37 -0
  33. package/src/lib/docs/test.md +52 -0
  34. package/src/lib/env.ts +75 -0
  35. package/src/lib/esbuild_helpers.ts +50 -0
  36. package/src/lib/esbuild_plugin_external_worker.ts +92 -0
  37. package/src/lib/esbuild_plugin_svelte.test.ts +88 -0
  38. package/src/lib/esbuild_plugin_svelte.ts +108 -0
  39. package/src/lib/esbuild_plugin_sveltekit_local_imports.ts +31 -0
  40. package/src/lib/esbuild_plugin_sveltekit_shim_alias.ts +25 -0
  41. package/src/lib/esbuild_plugin_sveltekit_shim_app.ts +41 -0
  42. package/src/lib/esbuild_plugin_sveltekit_shim_env.ts +46 -0
  43. package/src/lib/format.task.ts +30 -0
  44. package/src/lib/format_directory.ts +55 -0
  45. package/src/lib/format_file.test.ts +20 -0
  46. package/src/lib/format_file.ts +49 -0
  47. package/src/lib/fs.ts +18 -0
  48. package/src/lib/gen.task.ts +134 -0
  49. package/src/lib/gen.test.ts +306 -0
  50. package/src/lib/gen.ts +360 -0
  51. package/src/lib/git.test.ts +34 -0
  52. package/src/lib/git.ts +297 -0
  53. package/src/lib/github.ts +46 -0
  54. package/src/lib/gro.config.default.ts +34 -0
  55. package/src/lib/gro.ts +25 -0
  56. package/src/lib/gro_helpers.ts +101 -0
  57. package/src/lib/gro_plugin_gen.ts +95 -0
  58. package/src/lib/gro_plugin_server.ts +288 -0
  59. package/src/lib/gro_plugin_sveltekit_app.ts +257 -0
  60. package/src/lib/gro_plugin_sveltekit_library.ts +74 -0
  61. package/src/lib/hash.test.ts +33 -0
  62. package/src/lib/hash.ts +19 -0
  63. package/src/lib/index.ts +4 -0
  64. package/src/lib/input_path.test.ts +230 -0
  65. package/src/lib/input_path.ts +255 -0
  66. package/src/lib/invoke.ts +27 -0
  67. package/src/lib/invoke_task.ts +116 -0
  68. package/src/lib/lint.task.ts +38 -0
  69. package/src/lib/loader.test.ts +49 -0
  70. package/src/lib/loader.ts +226 -0
  71. package/src/lib/module.test.ts +46 -0
  72. package/src/lib/module.ts +13 -0
  73. package/src/lib/modules.test.ts +63 -0
  74. package/src/lib/modules.ts +112 -0
  75. package/src/lib/package.gen.ts +33 -0
  76. package/src/lib/package.ts +998 -0
  77. package/src/lib/package_json.test.ts +101 -0
  78. package/src/lib/package_json.ts +330 -0
  79. package/src/lib/package_meta.ts +86 -0
  80. package/src/lib/path.ts +23 -0
  81. package/src/lib/path_constants.ts +30 -0
  82. package/src/lib/paths.test.ts +77 -0
  83. package/src/lib/paths.ts +101 -0
  84. package/src/lib/plugin.test.ts +57 -0
  85. package/src/lib/plugin.ts +113 -0
  86. package/src/lib/publish.task.ts +194 -0
  87. package/src/lib/register.ts +3 -0
  88. package/src/lib/reinstall.task.ts +42 -0
  89. package/src/lib/release.task.ts +21 -0
  90. package/src/lib/resolve.task.ts +43 -0
  91. package/src/lib/resolve_node_specifier.test.ts +31 -0
  92. package/src/lib/resolve_node_specifier.ts +55 -0
  93. package/src/lib/resolve_specifier.test.ts +76 -0
  94. package/src/lib/resolve_specifier.ts +61 -0
  95. package/src/lib/run.task.ts +41 -0
  96. package/src/lib/run_gen.test.ts +196 -0
  97. package/src/lib/run_gen.ts +95 -0
  98. package/src/lib/run_task.test.ts +86 -0
  99. package/src/lib/run_task.ts +75 -0
  100. package/src/lib/search_fs.test.ts +56 -0
  101. package/src/lib/search_fs.ts +93 -0
  102. package/src/lib/src_json.test.ts +49 -0
  103. package/src/lib/src_json.ts +153 -0
  104. package/src/lib/svelte_helpers.ts +2 -0
  105. package/src/lib/sveltekit_config.ts +101 -0
  106. package/src/lib/sveltekit_config_global.ts +6 -0
  107. package/src/lib/sveltekit_helpers.ts +132 -0
  108. package/src/lib/sveltekit_shim_app.ts +42 -0
  109. package/src/lib/sveltekit_shim_app_environment.ts +14 -0
  110. package/src/lib/sveltekit_shim_app_forms.ts +20 -0
  111. package/src/lib/sveltekit_shim_app_navigation.ts +23 -0
  112. package/src/lib/sveltekit_shim_app_paths.ts +16 -0
  113. package/src/lib/sveltekit_shim_app_stores.ts +25 -0
  114. package/src/lib/sveltekit_shim_env.ts +45 -0
  115. package/src/lib/sync.task.ts +47 -0
  116. package/src/lib/task.test.ts +84 -0
  117. package/src/lib/task.ts +235 -0
  118. package/src/lib/task_logging.ts +180 -0
  119. package/src/lib/test.task.ts +50 -0
  120. package/src/lib/throttle.test.ts +52 -0
  121. package/src/lib/throttle.ts +63 -0
  122. package/src/lib/typecheck.task.ts +57 -0
  123. package/src/lib/upgrade.task.ts +108 -0
  124. package/src/lib/watch_dir.ts +88 -0
@@ -0,0 +1,101 @@
1
+ import {test} from 'uvu';
2
+ import * as assert from 'uvu/assert';
3
+
4
+ import {
5
+ Package_Json,
6
+ load_package_json,
7
+ parse_repo_url,
8
+ serialize_package_json,
9
+ to_package_exports,
10
+ } from './package_json.js';
11
+
12
+ test('load_package_json', () => {
13
+ const package_json = load_package_json();
14
+ assert.ok(package_json);
15
+ const parsed = Package_Json.parse(package_json);
16
+ assert.ok(parsed);
17
+ serialize_package_json(package_json);
18
+ });
19
+
20
+ test('load_package_json with cache', () => {
21
+ const cache = {};
22
+ const package_json1 = load_package_json(undefined, cache);
23
+ assert.ok(package_json1);
24
+ assert.is(Object.keys(cache).length, 1);
25
+ const package_json2 = load_package_json(undefined, cache);
26
+ assert.is(Object.keys(cache).length, 1);
27
+ assert.is(package_json1, package_json2);
28
+ });
29
+
30
+ test('Package_Json.parse', () => {
31
+ Package_Json.parse({name: 'abc', version: '123'});
32
+ });
33
+
34
+ test('Package_Json.parse fails with bad data', () => {
35
+ let err;
36
+ try {
37
+ Package_Json.parse({version: '123'});
38
+ } catch (_err) {
39
+ err = _err;
40
+ }
41
+ assert.ok(err);
42
+ });
43
+
44
+ test('serialize_package_json', () => {
45
+ serialize_package_json({name: 'abc', version: '123'});
46
+ });
47
+
48
+ test('serialize_package_json fails with bad data', () => {
49
+ let err;
50
+ try {
51
+ serialize_package_json({version: '123'} as any);
52
+ } catch (_err) {
53
+ err = _err;
54
+ }
55
+ assert.ok(err);
56
+ });
57
+
58
+ test('to_package_exports', () => {
59
+ assert.equal(to_package_exports(['a/b.ts']), {
60
+ './package.json': './package.json',
61
+ './a/b.js': {
62
+ default: './dist/a/b.js',
63
+ types: './dist/a/b.d.ts',
64
+ },
65
+ });
66
+ assert.equal(
67
+ to_package_exports([
68
+ 'a/b/Some_Test_Svelte.svelte',
69
+ 'a/b/some_test_ts.ts',
70
+ 'a/b/some_test_json.json',
71
+ 'index.ts',
72
+ ]),
73
+ {
74
+ '.': {
75
+ default: './dist/index.js',
76
+ types: './dist/index.d.ts',
77
+ },
78
+ './package.json': './package.json',
79
+ './a/b/some_test_json.json': {
80
+ default: './dist/a/b/some_test_json.json',
81
+ },
82
+ './a/b/Some_Test_Svelte.svelte': {
83
+ svelte: './dist/a/b/Some_Test_Svelte.svelte',
84
+ default: './dist/a/b/Some_Test_Svelte.svelte',
85
+ types: './dist/a/b/Some_Test_Svelte.svelte.d.ts',
86
+ },
87
+ './a/b/some_test_ts.js': {
88
+ default: './dist/a/b/some_test_ts.js',
89
+ types: './dist/a/b/some_test_ts.d.ts',
90
+ },
91
+ },
92
+ );
93
+ });
94
+
95
+ test('parse_repo_url', () => {
96
+ const parsed = parse_repo_url(load_package_json());
97
+ assert.is(parsed?.owner, 'ryanatkn');
98
+ assert.is(parsed?.repo, 'gro');
99
+ });
100
+
101
+ test.run();
@@ -0,0 +1,330 @@
1
+ import {z} from 'zod';
2
+ import {join} from 'node:path';
3
+ import {readFileSync, writeFileSync} from 'node:fs';
4
+ import {count_graphemes, plural, strip_end} from '@ryanatkn/belt/string.js';
5
+ import type {Logger} from '@ryanatkn/belt/log.js';
6
+ import type {Flavored} from '@ryanatkn/belt/types.js';
7
+ import {red} from '@ryanatkn/belt/styletext.js';
8
+
9
+ import {paths, gro_paths, IS_THIS_GRO, replace_extension} from './paths.js';
10
+ import {SVELTEKIT_DIST_DIRNAME} from './path_constants.js';
11
+ import {search_fs} from './search_fs.js';
12
+ import {has_sveltekit_library} from './sveltekit_helpers.js';
13
+ import {GITHUB_REPO_MATCHER} from './github.js';
14
+
15
+ // TODO @multiple belongs elsewhere
16
+ export const Url = z.string();
17
+ export type Url = Flavored<z.infer<typeof Url>, 'Url'>;
18
+
19
+ // TODO @multiple belongs elsewhere
20
+ export const Email = z.string();
21
+ export type Email = Flavored<z.infer<typeof Email>, 'Email'>;
22
+
23
+ // TODO move this where?
24
+ export const transform_empty_object_to_undefined = (val: any): any => {
25
+ if (val && Object.keys(val).length === 0) {
26
+ return;
27
+ }
28
+ return val;
29
+ };
30
+
31
+ export const Package_Json_Repository = z.union([
32
+ z.string(),
33
+ z
34
+ .object({
35
+ type: z.string(),
36
+ url: Url,
37
+ directory: z.string().optional(),
38
+ })
39
+ .passthrough(),
40
+ ]);
41
+ export type Package_Json_Repository = z.infer<typeof Package_Json_Repository>;
42
+
43
+ export const Package_Json_Author = z.union([
44
+ z.string(),
45
+ z
46
+ .object({
47
+ name: z.string(),
48
+ email: Email.optional(),
49
+ url: Url.optional(),
50
+ })
51
+ .passthrough(),
52
+ ]);
53
+ export type Package_Json_Author = z.infer<typeof Package_Json_Author>;
54
+
55
+ export const Package_Json_Funding = z.union([
56
+ z.string(),
57
+ z
58
+ .object({
59
+ type: z.string(),
60
+ url: Url,
61
+ })
62
+ .passthrough(),
63
+ ]);
64
+ export type Package_Json_Funding = z.infer<typeof Package_Json_Funding>;
65
+
66
+ export const Package_Json_Exports = z.record(
67
+ z.union([z.string(), z.record(z.string())]).optional(),
68
+ );
69
+ export type Package_Json_Exports = z.infer<typeof Package_Json_Exports>;
70
+
71
+ /**
72
+ * @see https://docs.npmjs.com/cli/v10/configuring-npm/package-json
73
+ */
74
+ export const Package_Json = z
75
+ .object({
76
+ // according to the npm docs, `name` and `version` are the only required properties
77
+ name: z.string(),
78
+ version: z.string(),
79
+ private: z.boolean({description: 'disallow npm publish'}).optional(),
80
+ public: z
81
+ .boolean({
82
+ description:
83
+ 'a Gro extension that enables publishing `.well-known/package.json` and `.well-known/src`',
84
+ })
85
+ .optional(),
86
+ description: z.string().optional(),
87
+ motto: z
88
+ .string({description: "a Gro extension that's a short phrase that represents this project"})
89
+ .optional(),
90
+ glyph: z
91
+ .string({
92
+ description:
93
+ "a Gro extension that's a single unicode character that represents this project",
94
+ })
95
+ .refine((v) => count_graphemes(v) === 1, 'must be a single unicode character')
96
+ .optional(),
97
+ logo: z
98
+ .string({
99
+ description:
100
+ "a Gro extension that's a link relative to the `homepage` to an image that represents this project",
101
+ })
102
+ .optional(),
103
+ logo_alt: z
104
+ .string({description: "a Gro extension that's the alt text for the `logo`"})
105
+ .optional(),
106
+ license: z.string().optional(),
107
+ scripts: z.record(z.string()).optional(),
108
+ homepage: Url.optional(),
109
+ author: z.union([z.string(), Package_Json_Author.optional()]),
110
+ repository: z.union([z.string(), Url, Package_Json_Repository]).optional(),
111
+ contributors: z.array(z.union([z.string(), Package_Json_Author])).optional(),
112
+ bugs: z
113
+ .union([z.string(), z.object({url: Url.optional(), email: Email.optional()}).passthrough()])
114
+ .optional(),
115
+ funding: z
116
+ .union([Url, Package_Json_Funding, z.array(z.union([Url, Package_Json_Funding]))])
117
+ .optional(),
118
+ keywords: z.array(z.string()).optional(),
119
+
120
+ type: z.string().optional(),
121
+ engines: z.record(z.string()).optional(),
122
+ os: z.array(z.string()).optional(),
123
+ cpu: z.array(z.string()).optional(),
124
+
125
+ dependencies: z.record(z.string()).optional(),
126
+ devDependencies: z.record(z.string()).optional(),
127
+ peerDependencies: z.record(z.string()).optional(),
128
+ peerDependenciesMeta: z.record(z.record(z.string())).optional(),
129
+ optionalDependencies: z.record(z.string()).optional(),
130
+
131
+ bin: z.record(z.string()).optional(),
132
+ sideEffects: z.array(z.string()).optional(),
133
+ files: z.array(z.string()).optional(),
134
+ exports: Package_Json_Exports.transform(transform_empty_object_to_undefined).optional(),
135
+ })
136
+ .passthrough();
137
+ export type Package_Json = z.infer<typeof Package_Json>;
138
+
139
+ export type Map_Package_Json = (
140
+ package_json: Package_Json,
141
+ ) => Package_Json | null | Promise<Package_Json | null>;
142
+
143
+ export const EMPTY_PACKAGE_JSON: Package_Json = {name: '', version: ''};
144
+
145
+ export const load_package_json = (
146
+ dir = IS_THIS_GRO ? gro_paths.root : paths.root,
147
+ cache?: Record<string, Package_Json>,
148
+ ): Package_Json => {
149
+ let package_json: Package_Json;
150
+ if (cache && dir in cache) {
151
+ return cache[dir];
152
+ }
153
+ try {
154
+ package_json = JSON.parse(load_package_json_contents(dir));
155
+ } catch (_err) {
156
+ return EMPTY_PACKAGE_JSON;
157
+ }
158
+ package_json = parse_package_json(Package_Json, package_json);
159
+ if (cache) cache[dir] = package_json;
160
+ return package_json;
161
+ };
162
+
163
+ export const sync_package_json = async (
164
+ map_package_json: Map_Package_Json,
165
+ log: Logger,
166
+ check = false,
167
+ dir = paths.root,
168
+ exports_dir = paths.lib,
169
+ ): Promise<{package_json: Package_Json | null; changed: boolean}> => {
170
+ const exported_files = search_fs(exports_dir);
171
+ const exported_paths = exported_files.map((f) => f.path);
172
+ const updated = await update_package_json(
173
+ dir,
174
+ async (package_json) => {
175
+ if (has_sveltekit_library(package_json).ok) {
176
+ const exports = to_package_exports(exported_paths);
177
+ package_json.exports = exports;
178
+ }
179
+ const mapped = await map_package_json(package_json);
180
+ return mapped ? parse_package_json(Package_Json, mapped) : mapped;
181
+ },
182
+ !check,
183
+ );
184
+
185
+ const exports_count =
186
+ updated.changed && updated.package_json?.exports
187
+ ? Object.keys(updated.package_json.exports).length
188
+ : 0;
189
+ log.info(
190
+ updated.changed
191
+ ? `updated package.json exports with ${exports_count} total export${plural(exports_count)}`
192
+ : 'no changes to exports in package.json',
193
+ );
194
+
195
+ return updated;
196
+ };
197
+
198
+ export const load_gro_package_json = (): Package_Json => load_package_json(gro_paths.root);
199
+
200
+ // TODO probably make this nullable and make callers handle failures
201
+ const load_package_json_contents = (dir: string): string =>
202
+ readFileSync(join(dir, 'package.json'), 'utf8');
203
+
204
+ export const write_package_json = (serialized_package_json: string): void => {
205
+ writeFileSync(join(paths.root, 'package.json'), serialized_package_json);
206
+ };
207
+
208
+ export const serialize_package_json = (package_json: Package_Json): string =>
209
+ JSON.stringify(parse_package_json(Package_Json, package_json), null, 2) + '\n';
210
+
211
+ /**
212
+ * Updates package.json. Writes to the filesystem only when contents change.
213
+ */
214
+ export const update_package_json = async (
215
+ dir = paths.root,
216
+ update: (package_json: Package_Json) => Package_Json | null | Promise<Package_Json | null>,
217
+ write = true,
218
+ ): Promise<{package_json: Package_Json | null; changed: boolean}> => {
219
+ const original_contents = load_package_json_contents(dir);
220
+ const original = JSON.parse(original_contents);
221
+ const updated = await update(original);
222
+ if (updated === null) {
223
+ return {package_json: original, changed: false};
224
+ }
225
+ const updated_contents = serialize_package_json(updated);
226
+ if (updated_contents === original_contents) {
227
+ return {package_json: original, changed: false};
228
+ }
229
+ if (write) write_package_json(updated_contents);
230
+ return {package_json: updated, changed: true};
231
+ };
232
+
233
+ const is_index = (path: string): boolean => path === 'index.ts' || path === 'index.js';
234
+
235
+ export const to_package_exports = (paths: string[]): Package_Json_Exports => {
236
+ const sorted = paths
237
+ .slice()
238
+ .sort((a, b) => (is_index(a) ? -1 : is_index(b) ? 1 : a.localeCompare(b)));
239
+ // Add the package.json after the index, if one exists.
240
+ // Including the `./` here ensures we don't conflict with any potential `$lib/package.json`.
241
+ const final_sorted = is_index(sorted[0])
242
+ ? [sorted[0]].concat('./package.json', sorted.slice(1))
243
+ : ['./package.json'].concat(sorted);
244
+ const exports: Package_Json_Exports = {};
245
+ for (const path of final_sorted) {
246
+ if (path === './package.json') {
247
+ exports['./package.json'] = './package.json';
248
+ } else if (path.endsWith('.json.d.ts')) {
249
+ const json_path = path.substring(0, path.length - 5);
250
+ exports['./' + json_path] = {
251
+ types: IMPORT_PREFIX + path,
252
+ default: IMPORT_PREFIX + json_path, // assuming a matching json file
253
+ };
254
+ } else if (path.endsWith('.ts') && !path.endsWith('.d.ts')) {
255
+ const js_path = replace_extension(path, '.js');
256
+ const key = is_index(path) ? '.' : './' + js_path;
257
+ exports[key] = {
258
+ types: IMPORT_PREFIX + replace_extension(path, '.d.ts'),
259
+ default: IMPORT_PREFIX + js_path,
260
+ };
261
+ } else if (path.endsWith('.js')) {
262
+ const key = is_index(path) ? '.' : './' + path;
263
+ exports[key] = {
264
+ types: IMPORT_PREFIX + replace_extension(path, '.d.ts'), // assuming JSDoc types
265
+ default: IMPORT_PREFIX + path,
266
+ };
267
+ } else if (path.endsWith('.svelte')) {
268
+ exports['./' + path] = {
269
+ types: IMPORT_PREFIX + path + '.d.ts',
270
+ svelte: IMPORT_PREFIX + path,
271
+ default: IMPORT_PREFIX + path, // needed for loader imports
272
+ };
273
+ } else {
274
+ exports['./' + path] = {
275
+ default: IMPORT_PREFIX + path,
276
+ };
277
+ }
278
+ }
279
+ return parse_or_throw_formatted_error('package.json#exports', Package_Json_Exports, exports);
280
+ };
281
+
282
+ const IMPORT_PREFIX = './' + SVELTEKIT_DIST_DIRNAME + '/';
283
+
284
+ export const parse_repo_url = (
285
+ package_json: Package_Json,
286
+ ): {owner: string; repo: string} | undefined => {
287
+ const {repository} = package_json;
288
+ const repo_url = repository
289
+ ? typeof repository === 'string'
290
+ ? repository
291
+ : repository.url
292
+ : undefined;
293
+ if (!repo_url) {
294
+ return;
295
+ }
296
+ const parsed_repo_url = GITHUB_REPO_MATCHER.exec(strip_end(strip_end(repo_url, '/'), '.git'));
297
+ if (!parsed_repo_url) {
298
+ return;
299
+ }
300
+ const [, owner, repo] = parsed_repo_url;
301
+ return {owner, repo};
302
+ };
303
+
304
+ /**
305
+ * Parses a `Package_Json` object but preserves the order of the original keys.
306
+ */
307
+ const parse_package_json = (schema: typeof Package_Json, value: any): Package_Json => {
308
+ const parsed = parse_or_throw_formatted_error('package.json', schema, value);
309
+ const keys = Object.keys(value);
310
+ return Object.fromEntries(
311
+ Object.entries(parsed).sort(([a], [b]) => keys.indexOf(a) - keys.indexOf(b)),
312
+ ) as any;
313
+ };
314
+
315
+ // TODO maybe extract to zod helpers? see also everything in `task_logging.ts`
316
+ const parse_or_throw_formatted_error = <T extends z.ZodTypeAny>(
317
+ name: string,
318
+ schema: T,
319
+ value: any,
320
+ ): z.infer<T> => {
321
+ const parsed = schema.safeParse(value);
322
+ if (!parsed.success) {
323
+ let msg = red(`Failed to parse ${name}:\n`);
324
+ for (const issue of parsed.error.issues) {
325
+ msg += red(`\n\t"${issue.path}" ${issue.message}\n`);
326
+ }
327
+ throw Error(msg);
328
+ }
329
+ return parsed.data;
330
+ };
@@ -0,0 +1,86 @@
1
+ import {strip_start, strip_end} from '@ryanatkn/belt/string.js';
2
+
3
+ import type {Package_Json, Url} from './package_json.js';
4
+ import type {Src_Json} from './src_json.js';
5
+
6
+ // TODO needs refactoring, more clarity
7
+ export interface Package_Meta {
8
+ url: Url;
9
+ package_json: Package_Json;
10
+ src_json: Src_Json;
11
+ name: string; // '@ryanatkn/fuz_library';
12
+ repo_name: string; // fuz_library
13
+ repo_url: Url | null; // 'https://github.com/ryanatkn/fuz';
14
+ /**
15
+ * the is the github user/org, not npm
16
+ */
17
+ owner_name: string | null; // 'fuz-dev';
18
+ homepage_url: Url | null; // 'https://www.fuz.dev/';
19
+ npm_url: Url | null; // 'https://npmjs.com/package/@ryanatkn/fuz_library';
20
+ changelog_url: Url | null;
21
+ published: boolean;
22
+ }
23
+
24
+ export const parse_package_meta = (
25
+ url: Url,
26
+ package_json: Package_Json,
27
+ src_json: Src_Json,
28
+ ): Package_Meta => {
29
+ const {name} = package_json;
30
+
31
+ // TODO hacky
32
+ const parse_repo = (r: string | null | undefined) => {
33
+ if (!r) return null;
34
+ return strip_end(strip_start(strip_end(r, '.git'), 'git+'), '/');
35
+ };
36
+
37
+ const repo_url = parse_repo(
38
+ package_json.repository
39
+ ? typeof package_json.repository === 'string'
40
+ ? package_json.repository
41
+ : package_json.repository.url
42
+ : null,
43
+ );
44
+
45
+ const homepage_url = package_json.homepage ?? null;
46
+
47
+ const published =
48
+ !package_json.private && !!package_json.exports && package_json.version !== '0.0.1';
49
+
50
+ // TODO generic registries
51
+ const npm_url = published ? 'https://www.npmjs.com/package/' + package_json.name : null;
52
+
53
+ const changelog_url = published && repo_url ? repo_url + '/blob/main/CHANGELOG.md' : null;
54
+
55
+ const repo_name = parse_repo_name(name);
56
+
57
+ const owner_name = repo_url ? strip_start(repo_url, 'https://github.com/').split('/')[0] : null;
58
+
59
+ return {
60
+ url,
61
+ package_json,
62
+ src_json,
63
+ name,
64
+ repo_name,
65
+ repo_url,
66
+ owner_name,
67
+ homepage_url,
68
+ npm_url,
69
+ changelog_url,
70
+ published,
71
+ };
72
+ };
73
+
74
+ // TODO proper parsing
75
+ export const parse_repo_name = (name: string): string =>
76
+ name[0] === '@' ? name.split('/')[1] : name;
77
+
78
+ export const parse_org_url = (pkg: Package_Meta): string | null => {
79
+ const {repo_name, repo_url} = pkg;
80
+ if (!repo_url) return null;
81
+ const suffix = '/' + repo_name;
82
+ if (repo_url.endsWith(suffix)) {
83
+ return strip_end(repo_url, suffix);
84
+ }
85
+ return null;
86
+ };
@@ -0,0 +1,23 @@
1
+ import {fileURLToPath} from 'node:url';
2
+ import type {Flavored} from '@ryanatkn/belt/types.js';
3
+
4
+ /**
5
+ * An absolute path on the filesystem. Named "id" to be consistent with Rollup.
6
+ */
7
+ export type Path_Id = Flavored<string, 'Path_Id'>;
8
+
9
+ export interface Path_Info {
10
+ id: Path_Id;
11
+ is_directory: boolean;
12
+ }
13
+
14
+ export interface Resolved_Path extends Path_Info {
15
+ path: string;
16
+ }
17
+
18
+ export type Path_Filter = (path: string, is_directory: boolean) => boolean;
19
+
20
+ export type File_Filter = (path: string) => boolean;
21
+
22
+ export const to_file_path = (path_or_url: string | URL): string =>
23
+ typeof path_or_url === 'string' ? path_or_url : fileURLToPath(path_or_url.href);
@@ -0,0 +1,30 @@
1
+ /*
2
+
3
+ This module is intended to have no dependencies to avoid over-imports in the CLI and loader.
4
+ If any of these become customizable from SvelteKit or Gro's configs, move them to `./paths.ts`.
5
+
6
+ */
7
+
8
+ // TODO the slashes here are kinda gross - do we want to maintain the convention to have the trailing slash in most usage?
9
+
10
+ export const SOURCE_DIRNAME = 'src';
11
+ export const GRO_DIRNAME = '.gro';
12
+ export const GRO_DIST_PREFIX = 'dist_'; //
13
+ export const SERVER_DIST_PATH = 'dist_server'; // TODO should all of these be `_PATH` or should this be `DIRNAME`? also, add `_PLUGIN` to this name?
14
+ export const GRO_DEV_DIRNAME = GRO_DIRNAME + '/dev';
15
+ export const SOURCE_DIR = SOURCE_DIRNAME + '/';
16
+ export const GRO_DIR = GRO_DIRNAME + '/';
17
+ export const GRO_DEV_DIR = GRO_DEV_DIRNAME + '/';
18
+ export const GRO_CONFIG_PATH = 'gro.config.ts';
19
+ export const README_FILENAME = 'README.md';
20
+ export const SVELTEKIT_CONFIG_FILENAME = 'svelte.config.js';
21
+ export const VITE_CONFIG_FILENAME = 'vite.config.ts';
22
+ export const NODE_MODULES_DIRNAME = 'node_modules';
23
+ export const LOCKFILE_FILENAME = 'package-lock.json';
24
+ export const SVELTEKIT_DEV_DIRNAME = '.svelte-kit'; // TODO use Svelte config value `outDir`
25
+ export const SVELTEKIT_BUILD_DIRNAME = 'build';
26
+ export const SVELTEKIT_DIST_DIRNAME = 'dist';
27
+ export const SVELTEKIT_VITE_CACHE_PATH = NODE_MODULES_DIRNAME + '/.vite';
28
+ export const GITHUB_DIRNAME = '.github';
29
+ export const GIT_DIRNAME = '.git';
30
+ export const TSCONFIG_FILENAME = 'tsconfig.json';
@@ -0,0 +1,77 @@
1
+ import {suite} from 'uvu';
2
+ import * as assert from 'uvu/assert';
3
+ import {resolve, join} from 'node:path';
4
+
5
+ import {
6
+ create_paths,
7
+ paths,
8
+ gro_paths,
9
+ is_gro_id,
10
+ to_root_path,
11
+ path_id_to_base_path,
12
+ base_path_to_path_id,
13
+ } from './paths.js';
14
+
15
+ /* test__create_paths */
16
+ const test__create_paths = suite('create_paths');
17
+
18
+ test__create_paths('basic behavior', () => {
19
+ const root = resolve('../fake');
20
+ const p = create_paths(root);
21
+ assert.is(p.root, join(root, '/'));
22
+ assert.is(p.source, join(root, 'src/'));
23
+ });
24
+
25
+ test__create_paths('paths object has the same identity as the gro_paths object', () => {
26
+ assert.is(paths, gro_paths); // because we're testing inside the Gro project
27
+ });
28
+
29
+ test__create_paths.run();
30
+ /* test__create_paths */
31
+
32
+ /* test__is_gro_id */
33
+ const test__is_gro_id = suite('is_gro_id');
34
+
35
+ test__is_gro_id('basic behavior', () => {
36
+ assert.ok(is_gro_id(resolve(paths.source)));
37
+ assert.ok(!is_gro_id(resolve('../fake/src')));
38
+ });
39
+
40
+ test__is_gro_id.run();
41
+ /* test__is_gro_id */
42
+
43
+ /* test__to_root_path */
44
+ const test__to_root_path = suite('to_root_path');
45
+
46
+ test__to_root_path('basic behavior', () => {
47
+ assert.is(to_root_path(resolve('foo/bar')), 'foo/bar');
48
+ assert.is(to_root_path(resolve('./')), './');
49
+ assert.is(to_root_path(resolve('./')), './');
50
+ });
51
+
52
+ test__to_root_path.run();
53
+ /* test__to_root_path */
54
+
55
+ /* test__path_id_to_base_path */
56
+ const test__path_id_to_base_path = suite('path_id_to_base_path');
57
+
58
+ test__path_id_to_base_path('basic behavior', () => {
59
+ assert.is(path_id_to_base_path(resolve('src/foo/bar/baz.ts')), 'foo/bar/baz.ts');
60
+ });
61
+
62
+ test__path_id_to_base_path.run();
63
+ /* test__path_id_to_base_path */
64
+
65
+ /* test__base_path_to_path_id */
66
+ const test__base_path_to_path_id = suite('base_path_to_path_id');
67
+
68
+ test__base_path_to_path_id('basic behavior', () => {
69
+ assert.is(base_path_to_path_id('foo/bar/baz.ts'), resolve('src/foo/bar/baz.ts'));
70
+ });
71
+
72
+ test__base_path_to_path_id('does not change extension', () => {
73
+ assert.is(base_path_to_path_id('foo/bar/baz.js'), resolve('src/foo/bar/baz.js'));
74
+ });
75
+
76
+ test__base_path_to_path_id.run();
77
+ /* test__base_path_to_path_id */