@ryanatkn/gro 0.155.0 → 0.157.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 (46) hide show
  1. package/dist/constants.d.ts +5 -0
  2. package/dist/constants.d.ts.map +1 -1
  3. package/dist/constants.js +5 -0
  4. package/dist/esbuild_plugin_svelte.d.ts.map +1 -1
  5. package/dist/esbuild_plugin_svelte.js +1 -2
  6. package/dist/gro_plugin_sveltekit_app.js +1 -1
  7. package/dist/loader.d.ts.map +1 -1
  8. package/dist/loader.js +33 -27
  9. package/dist/package.d.ts +44 -13
  10. package/dist/package.d.ts.map +1 -1
  11. package/dist/package.gen.js +1 -1
  12. package/dist/package.js +42 -21
  13. package/dist/parse_exports.d.ts +20 -0
  14. package/dist/parse_exports.d.ts.map +1 -0
  15. package/dist/parse_exports.js +65 -0
  16. package/dist/parse_exports_context.d.ts +21 -0
  17. package/dist/parse_exports_context.d.ts.map +1 -0
  18. package/dist/parse_exports_context.js +332 -0
  19. package/dist/parse_imports.d.ts.map +1 -1
  20. package/dist/parse_imports.js +1 -2
  21. package/dist/paths.d.ts +8 -0
  22. package/dist/paths.d.ts.map +1 -1
  23. package/dist/paths.js +6 -3
  24. package/dist/src_json.d.ts +68 -66
  25. package/dist/src_json.d.ts.map +1 -1
  26. package/dist/src_json.js +58 -55
  27. package/dist/test_helpers.d.ts +21 -0
  28. package/dist/test_helpers.d.ts.map +1 -0
  29. package/dist/test_helpers.js +122 -0
  30. package/package.json +20 -12
  31. package/src/lib/constants.ts +5 -0
  32. package/src/lib/esbuild_plugin_svelte.ts +1 -2
  33. package/src/lib/gro_plugin_sveltekit_app.ts +1 -1
  34. package/src/lib/loader.ts +38 -23
  35. package/src/lib/package.gen.ts +1 -1
  36. package/src/lib/package.ts +42 -21
  37. package/src/lib/parse_exports.ts +108 -0
  38. package/src/lib/parse_exports_context.ts +394 -0
  39. package/src/lib/parse_imports.ts +1 -2
  40. package/src/lib/paths.ts +13 -3
  41. package/src/lib/src_json.ts +80 -68
  42. package/src/lib/test_helpers.ts +159 -0
  43. package/dist/svelte_helpers.d.ts +0 -3
  44. package/dist/svelte_helpers.d.ts.map +0 -1
  45. package/dist/svelte_helpers.js +0 -2
  46. package/src/lib/svelte_helpers.ts +0 -2
@@ -0,0 +1,122 @@
1
+ import { existsSync, readFileSync, writeFileSync } from 'node:fs';
2
+ import { join } from 'node:path';
3
+ import ts from 'typescript';
4
+ export const SOME_PUBLIC_ENV_VAR_NAME = 'PUBLIC_SOME_PUBLIC_ENV_VAR';
5
+ export const SOME_PUBLIC_ENV_VAR_VALUE = 'SOME_PUBLIC_ENV_VAR';
6
+ const name_equals = SOME_PUBLIC_ENV_VAR_NAME + '=';
7
+ const line = name_equals + SOME_PUBLIC_ENV_VAR_VALUE;
8
+ let inited = false;
9
+ /**
10
+ * Hacky global helper to init the test env.
11
+ *
12
+ * @returns boolean indicating if the env file was created or not
13
+ */
14
+ export const init_test_env = (dir = process.cwd(), env_filename = '.env') => {
15
+ if (inited)
16
+ return false;
17
+ inited = true;
18
+ const env_file = join(dir, env_filename);
19
+ if (!existsSync(env_file)) {
20
+ writeFileSync(env_file, line + '\n', 'utf8');
21
+ return true;
22
+ }
23
+ const contents = readFileSync(env_file, 'utf8');
24
+ const lines = contents.split('\n');
25
+ if (lines.includes(line)) {
26
+ return false; // already exists
27
+ }
28
+ let new_contents;
29
+ const found_index = lines.findIndex((l) => l.startsWith(name_equals));
30
+ if (found_index === -1) {
31
+ // if the line does not exist, add it
32
+ new_contents = contents + (contents.endsWith('\n') ? '' : '\n') + line + '\n';
33
+ }
34
+ else {
35
+ // if the line exists but with a different value, replace it
36
+ new_contents = contents.replace(new RegExp(`${SOME_PUBLIC_ENV_VAR_NAME}=.*`), line);
37
+ }
38
+ writeFileSync(env_file, new_contents, 'utf8');
39
+ return true;
40
+ };
41
+ /**
42
+ * Creates a TypeScript environment for testing.
43
+ * Change to `typescript-go` when it's more ready.
44
+ * @see https://github.com/microsoft/typescript-go?tab=readme-ov-file#what-works-so-far
45
+ */
46
+ export const create_ts_test_env = (source_code, dir = process.cwd(), virtual_files = {}) => {
47
+ // Create a virtual file path for testing
48
+ const file_path = join(dir, 'virtual_test_file.ts');
49
+ // Create a compiler host with custom module resolution
50
+ const host = ts.createCompilerHost({});
51
+ const original_get_source_file = host.getSourceFile.bind(host);
52
+ // Override getSourceFile to return our test files
53
+ host.getSourceFile = (fileName, languageVersion) => {
54
+ if (fileName === file_path) {
55
+ return ts.createSourceFile(fileName, source_code, languageVersion);
56
+ }
57
+ // Check if we have a virtual file for this path
58
+ for (const [virtual_path, content] of Object.entries(virtual_files)) {
59
+ const full_path = join(dir, virtual_path);
60
+ if (fileName === full_path) {
61
+ return ts.createSourceFile(fileName, content, languageVersion);
62
+ }
63
+ }
64
+ return original_get_source_file(fileName, languageVersion);
65
+ };
66
+ // TODO simplify?
67
+ // Add custom module resolution using resolveModuleNameLiterals
68
+ host.resolveModuleNameLiterals = (module_literals, containing_file, _redirected_reference, options) => {
69
+ return module_literals.map((module_literal) => {
70
+ const module_name = module_literal.text;
71
+ // Handle relative imports that might be in our virtual files
72
+ if (module_name.startsWith('./') || module_name.startsWith('../')) {
73
+ const module_path = join(containing_file, '..', module_name);
74
+ // Normalize the path handling for the virtual files
75
+ for (const virtual_path of Object.keys(virtual_files)) {
76
+ const full_path = join(dir, virtual_path);
77
+ const normalized_module_path = module_path.replace(/\.ts$/, '') + '.ts';
78
+ if (normalized_module_path === full_path) {
79
+ return {
80
+ resolvedModule: {
81
+ resolvedFileName: full_path,
82
+ isExternalLibraryImport: false,
83
+ extension: ts.Extension.Ts,
84
+ },
85
+ };
86
+ }
87
+ }
88
+ }
89
+ // If it's our main file
90
+ if (join(dir, module_name) === file_path) {
91
+ return {
92
+ resolvedModule: {
93
+ resolvedFileName: file_path,
94
+ isExternalLibraryImport: false,
95
+ extension: ts.Extension.Ts,
96
+ },
97
+ };
98
+ }
99
+ // For non-virtual modules, try standard resolution
100
+ return ts.resolveModuleName(module_name, containing_file, options, host);
101
+ });
102
+ };
103
+ // Include all virtual files in the program files list
104
+ const program_files = [file_path, ...Object.keys(virtual_files).map((path) => join(dir, path))];
105
+ // TODO get from tsconfig?
106
+ // Create program options
107
+ const compiler_options = {
108
+ target: ts.ScriptTarget.ESNext,
109
+ module: ts.ModuleKind.ESNext,
110
+ moduleResolution: ts.ModuleResolutionKind.NodeNext,
111
+ verbatimModuleSyntax: true,
112
+ isolatedModules: true,
113
+ };
114
+ // Create a program with our virtual files
115
+ const program = ts.createProgram(program_files, compiler_options, host);
116
+ const source_file = program.getSourceFile(file_path);
117
+ const checker = program.getTypeChecker();
118
+ // Get the exports from the source file
119
+ const symbol = checker.getSymbolAtLocation(source_file);
120
+ const exports = symbol ? checker.getExportsOfModule(symbol) : [];
121
+ return { source_file, checker, program, exports };
122
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ryanatkn/gro",
3
- "version": "0.155.0",
3
+ "version": "0.157.0",
4
4
  "description": "task runner and toolkit extending SvelteKit",
5
5
  "motto": "generate, run, optimize",
6
6
  "glyph": "🌰",
@@ -54,17 +54,17 @@
54
54
  "dotenv": "^16.5.0",
55
55
  "esm-env": "^1.2.2",
56
56
  "mri": "^1.2.0",
57
- "oxc-parser": "^0.67.0",
57
+ "oxc-parser": "^0.68.1",
58
58
  "prettier": "^3.5.3",
59
59
  "prettier-plugin-svelte": "^3.3.3",
60
60
  "ts-blank-space": "^0.6.1",
61
- "ts-morph": "^25.0.1",
62
61
  "tslib": "^2.8.1",
63
- "zod": "^3.24.3"
62
+ "zod": "^3.24.4"
64
63
  },
65
64
  "peerDependencies": {
66
65
  "esbuild": "^0.25",
67
- "svelte": "^5"
66
+ "svelte": "^5",
67
+ "typescript": "^5"
68
68
  },
69
69
  "devDependencies": {
70
70
  "@changesets/changelog-git": "^0.2.1",
@@ -78,12 +78,12 @@
78
78
  "@sveltejs/vite-plugin-svelte": "^5.0.3",
79
79
  "@types/node": "^22.15.3",
80
80
  "esbuild": "^0.25.3",
81
- "eslint": "^9.25.1",
81
+ "eslint": "9.25.1",
82
82
  "eslint-plugin-svelte": "^3.5.1",
83
83
  "svelte": "^5.28.2",
84
- "svelte-check": "^4.1.6",
84
+ "svelte-check": "^4.1.7",
85
85
  "typescript": "^5.8.3",
86
- "typescript-eslint": "^8.31.1",
86
+ "typescript-eslint": "^8.32.0",
87
87
  "uvu": "^0.5.6"
88
88
  },
89
89
  "optionalDependencies": {
@@ -325,6 +325,14 @@
325
325
  "types": "./dist/package.d.ts",
326
326
  "default": "./dist/package.js"
327
327
  },
328
+ "./parse_exports_context.js": {
329
+ "types": "./dist/parse_exports_context.d.ts",
330
+ "default": "./dist/parse_exports_context.js"
331
+ },
332
+ "./parse_exports.js": {
333
+ "types": "./dist/parse_exports.d.ts",
334
+ "default": "./dist/parse_exports.js"
335
+ },
328
336
  "./parse_imports.js": {
329
337
  "types": "./dist/parse_imports.d.ts",
330
338
  "default": "./dist/parse_imports.js"
@@ -389,10 +397,6 @@
389
397
  "types": "./dist/svelte_config.d.ts",
390
398
  "default": "./dist/svelte_config.js"
391
399
  },
392
- "./svelte_helpers.js": {
393
- "types": "./dist/svelte_helpers.d.ts",
394
- "default": "./dist/svelte_helpers.js"
395
- },
396
400
  "./sveltekit_helpers.js": {
397
401
  "types": "./dist/sveltekit_helpers.d.ts",
398
402
  "default": "./dist/sveltekit_helpers.js"
@@ -437,6 +441,10 @@
437
441
  "types": "./dist/task.d.ts",
438
442
  "default": "./dist/task.js"
439
443
  },
444
+ "./test_helpers.js": {
445
+ "types": "./dist/test_helpers.d.ts",
446
+ "default": "./dist/test_helpers.js"
447
+ },
440
448
  "./test.task.js": {
441
449
  "types": "./dist/test.task.d.ts",
442
450
  "default": "./dist/test.task.js"
@@ -12,8 +12,11 @@ export const GRO_DIRNAME = '.gro';
12
12
  export const GRO_DIST_PREFIX = 'dist_'; //
13
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
14
  export const GRO_DEV_DIRNAME = GRO_DIRNAME + '/dev';
15
+ /** @trailing_slash */
15
16
  export const SOURCE_DIR = SOURCE_DIRNAME + '/';
17
+ /** @trailing_slash */
16
18
  export const GRO_DIR = GRO_DIRNAME + '/';
19
+ /** @trailing_slash */
17
20
  export const GRO_DEV_DIR = GRO_DEV_DIRNAME + '/';
18
21
  export const GRO_CONFIG_PATH = 'gro.config.ts';
19
22
  export const README_FILENAME = 'README.md';
@@ -32,6 +35,8 @@ export const TSCONFIG_FILENAME = 'tsconfig.json';
32
35
  export const TS_MATCHER = /\.(ts|tsx|mts|cts)$/;
33
36
  export const JS_MATCHER = /\.(js|jsx|mjs|cjs)$/;
34
37
  export const JSON_MATCHER = /\.json$/;
38
+ export const SVELTE_MATCHER = /\.svelte$/;
39
+ export const SVELTE_RUNES_MATCHER = /\.svelte\.(js|ts)$/; // TODO probably let `.svelte.` appear anywhere - https://github.com/sveltejs/svelte/issues/11536
35
40
  /** Extracts the script content from Svelte files. */
36
41
  export const SVELTE_SCRIPT_MATCHER = /<script(?:\s+[^>]*)?>([\s\S]*?)<\/script>/gim; // TODO maybe this shouldnt be global? or make a getter?
37
42
  export const EVERYTHING_MATCHER = /.*/;
@@ -10,14 +10,13 @@ import {
10
10
  import {readFile} from 'node:fs/promises';
11
11
  import {relative} from 'node:path';
12
12
 
13
- import {SVELTE_MATCHER, SVELTE_RUNES_MATCHER} from './svelte_helpers.ts';
14
13
  import {to_define_import_meta_env, default_ts_transform_options} from './esbuild_helpers.ts';
15
14
  import {
16
15
  default_svelte_config,
17
16
  to_default_compile_module_options,
18
17
  type Parsed_Svelte_Config,
19
18
  } from './svelte_config.ts';
20
- import {TS_MATCHER} from './constants.ts';
19
+ import {TS_MATCHER, SVELTE_MATCHER, SVELTE_RUNES_MATCHER} from './constants.ts';
21
20
 
22
21
  export interface Esbuild_Plugin_Svelte_Options {
23
22
  dev: boolean;
@@ -92,7 +92,7 @@ export const gro_plugin_sveltekit_app = ({
92
92
 
93
93
  // `.well-known/src.json` and `.well-known/src/`
94
94
  const final_package_json = mapped_package_json ?? package_json;
95
- const src_json = create_src_json(final_package_json);
95
+ const src_json = create_src_json(final_package_json, undefined);
96
96
  if (well_known_src_json === undefined) {
97
97
  well_known_src_json = final_package_json.public; // eslint-disable-line no-param-reassign
98
98
  }
package/src/lib/loader.ts CHANGED
@@ -15,9 +15,13 @@ import {
15
15
  sveltekit_shim_app_specifiers,
16
16
  } from './sveltekit_shim_app.ts';
17
17
  import {default_svelte_config} from './svelte_config.ts';
18
- import {SVELTE_MATCHER, SVELTE_RUNES_MATCHER} from './svelte_helpers.ts';
19
18
  import {IS_THIS_GRO, paths} from './paths.ts';
20
- import {JSON_MATCHER, NODE_MODULES_DIRNAME, TS_MATCHER} from './constants.ts';
19
+ import {
20
+ NODE_MODULES_DIRNAME,
21
+ TS_MATCHER,
22
+ SVELTE_MATCHER,
23
+ SVELTE_RUNES_MATCHER,
24
+ } from './constants.ts';
21
25
  import {resolve_specifier} from './resolve_specifier.ts';
22
26
  import {map_sveltekit_aliases} from './sveltekit_helpers.ts';
23
27
 
@@ -93,7 +97,7 @@ export const load: LoadHook = async (url, context, nextLoad) => {
93
97
  const filename = fileURLToPath(url);
94
98
  const loaded = await nextLoad(url, {...context, format: 'module-typescript'});
95
99
  const raw_source = loaded.source?.toString(); // eslint-disable-line @typescript-eslint/no-base-to-string
96
- if (raw_source == null) throw new Error(`Failed to load ${url}`);
100
+ if (raw_source == null) throw Error(`Failed to load ${url}`);
97
101
  // TODO should be nice if we could use Node's builtin amaro transform, but I couldn't find a way after digging into the source, AFAICT it's internal and not exposed
98
102
  const source = ts_blank_space(raw_source); // TODO was using oxc-transform and probably should, but this doesn't require sourcemaps, and it's still alpha as of May 2025
99
103
  const transformed = compileModule(source, {
@@ -116,13 +120,14 @@ export const load: LoadHook = async (url, context, nextLoad) => {
116
120
  const source = preprocessed?.code ?? raw_source;
117
121
  const transformed = compile(source, {...svelte_compile_options, dev, filename});
118
122
  return {format: 'module', shortCircuit: true, source: transformed.js.code};
119
- } else if (JSON_MATCHER.test(url)) {
120
- // TODO probably require import attrs: `JSON_MATCHER.test(url) && context.importAttributes.type === 'json'`
121
- // json
123
+ } else if (context.importAttributes.type === 'json') {
124
+ // json - any file extension
122
125
  // TODO probably follow esbuild and also export every top-level property for objects from the module for good treeshaking - https://esbuild.github.io/content-types/#json (type generation?)
123
- const loaded = await nextLoad(url, context);
126
+ // TODO why is removing the importAttributes needed? can't pass no context either -
127
+ // error: `Module "file:///home/user/dev/repo/foo.json" is not of type "json"`
128
+ const loaded = await nextLoad(url, {...context, importAttributes: undefined});
124
129
  const raw_source = loaded.source?.toString(); // eslint-disable-line @typescript-eslint/no-base-to-string
125
- if (raw_source == null) throw new Error(`Failed to load ${url}`);
130
+ if (raw_source == null) throw Error(`Failed to load ${url}`);
126
131
  const source = `export default ` + raw_source;
127
132
  return {format: 'module', shortCircuit: true, source};
128
133
  } else if (RAW_MATCHER.test(url)) {
@@ -138,20 +143,30 @@ export const load: LoadHook = async (url, context, nextLoad) => {
138
143
  if (context.format === 'sveltekit-env') {
139
144
  let mode: 'static' | 'dynamic';
140
145
  let visibility: 'public' | 'private';
141
- if (context.importAttributes.virtual === '$env/static/public') {
142
- mode = 'static';
143
- visibility = 'public';
144
- } else if (context.importAttributes.virtual === '$env/static/private') {
145
- mode = 'static';
146
- visibility = 'private';
147
- } else if (context.importAttributes.virtual === '$env/dynamic/public') {
148
- mode = 'dynamic';
149
- visibility = 'public';
150
- } else if (context.importAttributes.virtual === '$env/dynamic/private') {
151
- mode = 'dynamic';
152
- visibility = 'private';
153
- } else {
154
- throw new Error(`Unknown $env import: ${context.importAttributes.virtual}`);
146
+ switch (context.importAttributes.virtual) {
147
+ case '$env/static/public': {
148
+ mode = 'static';
149
+ visibility = 'public';
150
+ break;
151
+ }
152
+ case '$env/static/private': {
153
+ mode = 'static';
154
+ visibility = 'private';
155
+ break;
156
+ }
157
+ case '$env/dynamic/public': {
158
+ mode = 'dynamic';
159
+ visibility = 'public';
160
+ break;
161
+ }
162
+ case '$env/dynamic/private': {
163
+ mode = 'dynamic';
164
+ visibility = 'private';
165
+ break;
166
+ }
167
+ default: {
168
+ throw Error(`Unknown $env import: ${context.importAttributes.virtual}`);
169
+ }
155
170
  }
156
171
  const source = render_env_shim_module(
157
172
  dev,
@@ -162,7 +177,7 @@ export const load: LoadHook = async (url, context, nextLoad) => {
162
177
  env_dir,
163
178
  );
164
179
  return {format: 'module', shortCircuit: true, source};
165
- } // else fallback
180
+ }
166
181
  }
167
182
 
168
183
  // fallback to default behavior
@@ -14,7 +14,7 @@ import {create_src_json} from './src_json.ts';
14
14
  */
15
15
  export const gen: Gen = ({origin_path}) => {
16
16
  const package_json = load_package_json();
17
- const src_json = create_src_json(package_json);
17
+ const src_json = create_src_json(package_json, undefined);
18
18
 
19
19
  return `
20
20
  // generated by ${origin_path}
@@ -5,7 +5,7 @@ import type {Src_Json} from './src_json.ts';
5
5
 
6
6
  export const package_json = {
7
7
  name: '@ryanatkn/gro',
8
- version: '0.155.0',
8
+ version: '0.157.0',
9
9
  description: 'task runner and toolkit extending SvelteKit',
10
10
  motto: 'generate, run, optimize',
11
11
  glyph: '🌰',
@@ -49,15 +49,14 @@ export const package_json = {
49
49
  dotenv: '^16.5.0',
50
50
  'esm-env': '^1.2.2',
51
51
  mri: '^1.2.0',
52
- 'oxc-parser': '^0.67.0',
52
+ 'oxc-parser': '^0.68.1',
53
53
  prettier: '^3.5.3',
54
54
  'prettier-plugin-svelte': '^3.3.3',
55
55
  'ts-blank-space': '^0.6.1',
56
- 'ts-morph': '^25.0.1',
57
56
  tslib: '^2.8.1',
58
- zod: '^3.24.3',
57
+ zod: '^3.24.4',
59
58
  },
60
- peerDependencies: {esbuild: '^0.25', svelte: '^5'},
59
+ peerDependencies: {esbuild: '^0.25', svelte: '^5', typescript: '^5'},
61
60
  devDependencies: {
62
61
  '@changesets/changelog-git': '^0.2.1',
63
62
  '@changesets/types': '^6.1.0',
@@ -70,12 +69,12 @@ export const package_json = {
70
69
  '@sveltejs/vite-plugin-svelte': '^5.0.3',
71
70
  '@types/node': '^22.15.3',
72
71
  esbuild: '^0.25.3',
73
- eslint: '^9.25.1',
72
+ eslint: '9.25.1',
74
73
  'eslint-plugin-svelte': '^3.5.1',
75
74
  svelte: '^5.28.2',
76
- 'svelte-check': '^4.1.6',
75
+ 'svelte-check': '^4.1.7',
77
76
  typescript: '^5.8.3',
78
- 'typescript-eslint': '^8.31.1',
77
+ 'typescript-eslint': '^8.32.0',
79
78
  uvu: '^0.5.6',
80
79
  },
81
80
  optionalDependencies: {'@ryanatkn/moss': '>=0.27.0 <0.29.0'},
@@ -191,6 +190,11 @@ export const package_json = {
191
190
  './package_meta.js': {types: './dist/package_meta.d.ts', default: './dist/package_meta.js'},
192
191
  './package.gen.js': {types: './dist/package.gen.d.ts', default: './dist/package.gen.js'},
193
192
  './package.js': {types: './dist/package.d.ts', default: './dist/package.js'},
193
+ './parse_exports_context.js': {
194
+ types: './dist/parse_exports_context.d.ts',
195
+ default: './dist/parse_exports_context.js',
196
+ },
197
+ './parse_exports.js': {types: './dist/parse_exports.d.ts', default: './dist/parse_exports.js'},
194
198
  './parse_imports.js': {types: './dist/parse_imports.d.ts', default: './dist/parse_imports.js'},
195
199
  './path.js': {types: './dist/path.d.ts', default: './dist/path.js'},
196
200
  './paths.js': {types: './dist/paths.d.ts', default: './dist/paths.js'},
@@ -213,10 +217,6 @@ export const package_json = {
213
217
  './search_fs.js': {types: './dist/search_fs.d.ts', default: './dist/search_fs.js'},
214
218
  './src_json.js': {types: './dist/src_json.d.ts', default: './dist/src_json.js'},
215
219
  './svelte_config.js': {types: './dist/svelte_config.d.ts', default: './dist/svelte_config.js'},
216
- './svelte_helpers.js': {
217
- types: './dist/svelte_helpers.d.ts',
218
- default: './dist/svelte_helpers.js',
219
- },
220
220
  './sveltekit_helpers.js': {
221
221
  types: './dist/sveltekit_helpers.d.ts',
222
222
  default: './dist/sveltekit_helpers.js',
@@ -252,6 +252,7 @@ export const package_json = {
252
252
  './sync.task.js': {types: './dist/sync.task.d.ts', default: './dist/sync.task.js'},
253
253
  './task_logging.js': {types: './dist/task_logging.d.ts', default: './dist/task_logging.js'},
254
254
  './task.js': {types: './dist/task.d.ts', default: './dist/task.js'},
255
+ './test_helpers.js': {types: './dist/test_helpers.d.ts', default: './dist/test_helpers.js'},
255
256
  './test.task.js': {types: './dist/test.task.d.ts', default: './dist/test.task.js'},
256
257
  './typecheck.task.js': {
257
258
  types: './dist/typecheck.task.d.ts',
@@ -264,7 +265,7 @@ export const package_json = {
264
265
 
265
266
  export const src_json = {
266
267
  name: '@ryanatkn/gro',
267
- version: '0.155.0',
268
+ version: '0.157.0',
268
269
  modules: {
269
270
  '.': {
270
271
  path: 'index.ts',
@@ -281,7 +282,7 @@ export const src_json = {
281
282
  {name: 'Task_Error', kind: 'class'},
282
283
  ],
283
284
  },
284
- './package.json': {path: 'package.json', declarations: []},
285
+ './package.json': {path: 'package.json', declarations: [{name: 'default', kind: 'json'}]},
285
286
  './args.js': {
286
287
  path: 'args.ts',
287
288
  declarations: [
@@ -393,6 +394,8 @@ export const src_json = {
393
394
  {name: 'TS_MATCHER', kind: 'variable'},
394
395
  {name: 'JS_MATCHER', kind: 'variable'},
395
396
  {name: 'JSON_MATCHER', kind: 'variable'},
397
+ {name: 'SVELTE_MATCHER', kind: 'variable'},
398
+ {name: 'SVELTE_RUNES_MATCHER', kind: 'variable'},
396
399
  {name: 'SVELTE_SCRIPT_MATCHER', kind: 'variable'},
397
400
  {name: 'EVERYTHING_MATCHER', kind: 'variable'},
398
401
  {name: 'JS_CLI_DEFAULT', kind: 'variable'},
@@ -737,6 +740,21 @@ export const src_json = {
737
740
  {name: 'src_json', kind: 'variable'},
738
741
  ],
739
742
  },
743
+ './parse_exports_context.js': {
744
+ path: 'parse_exports_context.ts',
745
+ declarations: [{name: 'Parse_Exports_Context', kind: 'class'}],
746
+ },
747
+ './parse_exports.js': {
748
+ path: 'parse_exports.ts',
749
+ declarations: [
750
+ {name: 'Declaration_Kind', kind: 'type'},
751
+ {name: 'Declaration', kind: 'type'},
752
+ {name: 'Export_Declaration', kind: 'type'},
753
+ {name: 'parse_exports', kind: 'function'},
754
+ {name: 'infer_declarations_from_file_type', kind: 'function'},
755
+ {name: 'process_ts_exports', kind: 'function'},
756
+ ],
757
+ },
740
758
  './parse_imports.js': {
741
759
  path: 'parse_imports.ts',
742
760
  declarations: [
@@ -856,6 +874,7 @@ export const src_json = {
856
874
  './src_json.js': {
857
875
  path: 'src_json.ts',
858
876
  declarations: [
877
+ {name: 'Src_Module_Declaration_Kind', kind: 'variable'},
859
878
  {name: 'Src_Module_Declaration', kind: 'variable'},
860
879
  {name: 'Src_Module', kind: 'variable'},
861
880
  {name: 'Src_Modules', kind: 'variable'},
@@ -876,13 +895,6 @@ export const src_json = {
876
895
  {name: 'default_svelte_config', kind: 'variable'},
877
896
  ],
878
897
  },
879
- './svelte_helpers.js': {
880
- path: 'svelte_helpers.ts',
881
- declarations: [
882
- {name: 'SVELTE_MATCHER', kind: 'variable'},
883
- {name: 'SVELTE_RUNES_MATCHER', kind: 'variable'},
884
- ],
885
- },
886
898
  './sveltekit_helpers.js': {
887
899
  path: 'sveltekit_helpers.ts',
888
900
  declarations: [
@@ -1004,6 +1016,15 @@ export const src_json = {
1004
1016
  {name: 'validate_task_module', kind: 'function'},
1005
1017
  ],
1006
1018
  },
1019
+ './test_helpers.js': {
1020
+ path: 'test_helpers.ts',
1021
+ declarations: [
1022
+ {name: 'SOME_PUBLIC_ENV_VAR_NAME', kind: 'variable'},
1023
+ {name: 'SOME_PUBLIC_ENV_VAR_VALUE', kind: 'variable'},
1024
+ {name: 'init_test_env', kind: 'function'},
1025
+ {name: 'create_ts_test_env', kind: 'function'},
1026
+ ],
1027
+ },
1007
1028
  './test.task.js': {
1008
1029
  path: 'test.task.ts',
1009
1030
  declarations: [
@@ -0,0 +1,108 @@
1
+ import ts from 'typescript';
2
+ import {extname} from 'node:path';
3
+ import type {Flavored} from '@ryanatkn/belt/types.js';
4
+
5
+ import type {Path_Id} from './path.ts';
6
+ import {TS_MATCHER} from './constants.ts';
7
+ import {Parse_Exports_Context} from './parse_exports_context.ts';
8
+ import type {Logger} from '@ryanatkn/belt/log.js';
9
+
10
+ export type Declaration_Kind =
11
+ | 'type'
12
+ | 'function'
13
+ | 'variable' // TODO maybe expand this to have literals/primitives?
14
+ | 'class'
15
+ | 'component'
16
+ | 'json'
17
+ | 'css';
18
+
19
+ export interface Declaration {
20
+ name: string;
21
+ kind: Declaration_Kind | null;
22
+ }
23
+
24
+ export type Export_Declaration = Flavored<Declaration, 'Export_Declaration'>;
25
+
26
+ /**
27
+ * Parse exports from a file based on its file type and content.
28
+ */
29
+ export const parse_exports = (
30
+ id: Path_Id,
31
+ program?: ts.Program,
32
+ declarations: Array<Export_Declaration> = [],
33
+ log?: Logger,
34
+ ): Array<Export_Declaration> => {
35
+ // First, infer declarations based on file extension
36
+ infer_declarations_from_file_type(id, declarations);
37
+
38
+ // For TypeScript files with program, perform detailed export analysis
39
+ if (TS_MATCHER.test(id) && program) {
40
+ const source_file = program.getSourceFile(id);
41
+ if (!source_file) return declarations;
42
+
43
+ const checker = program.getTypeChecker();
44
+
45
+ // Get the exports of the source file (module)
46
+ const symbol = checker.getSymbolAtLocation(source_file);
47
+ if (!symbol) return declarations;
48
+
49
+ // Get the module exports
50
+ const exports = checker.getExportsOfModule(symbol);
51
+
52
+ // Process TypeScript declarations
53
+ const export_context = new Parse_Exports_Context(program, log);
54
+ export_context.analyze_source_file(source_file);
55
+ export_context.process_exports(source_file, exports, declarations);
56
+ }
57
+
58
+ return declarations;
59
+ };
60
+
61
+ // TODO temporary until proper type inference
62
+ export const infer_declarations_from_file_type = (
63
+ file_path: Path_Id,
64
+ declarations: Array<Export_Declaration> = [],
65
+ ): Array<Export_Declaration> => {
66
+ const extension = extname(file_path).toLowerCase();
67
+
68
+ switch (extension) {
69
+ case '.svelte': {
70
+ declarations.push({
71
+ name: 'default',
72
+ kind: 'component',
73
+ });
74
+ break;
75
+ }
76
+ case '.css': {
77
+ declarations.push({
78
+ name: 'default',
79
+ kind: 'css',
80
+ });
81
+ break;
82
+ }
83
+ case '.json': {
84
+ declarations.push({
85
+ name: 'default',
86
+ kind: 'json',
87
+ });
88
+ break;
89
+ }
90
+ }
91
+
92
+ return declarations;
93
+ };
94
+
95
+ /**
96
+ * Process TypeScript exports, identifying their kinds.
97
+ */
98
+ export const process_ts_exports = (
99
+ source_file: ts.SourceFile,
100
+ program: ts.Program,
101
+ exports: Array<ts.Symbol>,
102
+ declarations: Array<Export_Declaration> = [],
103
+ log?: Logger,
104
+ ): Array<Export_Declaration> => {
105
+ const export_context = new Parse_Exports_Context(program, log);
106
+ export_context.analyze_source_file(source_file);
107
+ return export_context.process_exports(source_file, exports, declarations);
108
+ };