@ryanatkn/gro 0.156.0 → 0.157.1

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 +6 -0
  2. package/dist/constants.d.ts.map +1 -1
  3. package/dist/constants.js +6 -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 +1 -2
  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 +44 -22
  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 +21 -13
  31. package/src/lib/constants.ts +6 -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 +6 -2
  35. package/src/lib/package.gen.ts +1 -1
  36. package/src/lib/package.ts +44 -22
  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.156.0",
3
+ "version": "0.157.1",
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,16 +78,16 @@
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": {
90
- "@ryanatkn/moss": ">=0.27.0 <0.29.0"
90
+ "@ryanatkn/moss": ">=0.27.0 <0.30.0"
91
91
  },
92
92
  "prettier": {
93
93
  "plugins": [
@@ -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';
@@ -31,6 +34,9 @@ export const TSCONFIG_FILENAME = 'tsconfig.json';
31
34
 
32
35
  export const TS_MATCHER = /\.(ts|tsx|mts|cts)$/;
33
36
  export const JS_MATCHER = /\.(js|jsx|mjs|cjs)$/;
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
34
40
  /** Extracts the script content from Svelte files. */
35
41
  export const SVELTE_SCRIPT_MATCHER = /<script(?:\s+[^>]*)?>([\s\S]*?)<\/script>/gim; // TODO maybe this shouldnt be global? or make a getter?
36
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 {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
 
@@ -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.156.0',
8
+ version: '0.157.1',
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,15 +69,15 @@ 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
- optionalDependencies: {'@ryanatkn/moss': '>=0.27.0 <0.29.0'},
80
+ optionalDependencies: {'@ryanatkn/moss': '>=0.27.0 <0.30.0'},
82
81
  prettier: {
83
82
  plugins: ['prettier-plugin-svelte'],
84
83
  useTabs: true,
@@ -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.156.0',
268
+ version: '0.157.1',
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: [
@@ -392,6 +393,9 @@ export const src_json = {
392
393
  {name: 'TSCONFIG_FILENAME', kind: 'variable'},
393
394
  {name: 'TS_MATCHER', kind: 'variable'},
394
395
  {name: 'JS_MATCHER', kind: 'variable'},
396
+ {name: 'JSON_MATCHER', kind: 'variable'},
397
+ {name: 'SVELTE_MATCHER', kind: 'variable'},
398
+ {name: 'SVELTE_RUNES_MATCHER', kind: 'variable'},
395
399
  {name: 'SVELTE_SCRIPT_MATCHER', kind: 'variable'},
396
400
  {name: 'EVERYTHING_MATCHER', kind: 'variable'},
397
401
  {name: 'JS_CLI_DEFAULT', kind: 'variable'},
@@ -736,6 +740,21 @@ export const src_json = {
736
740
  {name: 'src_json', kind: 'variable'},
737
741
  ],
738
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
+ },
739
758
  './parse_imports.js': {
740
759
  path: 'parse_imports.ts',
741
760
  declarations: [
@@ -855,6 +874,7 @@ export const src_json = {
855
874
  './src_json.js': {
856
875
  path: 'src_json.ts',
857
876
  declarations: [
877
+ {name: 'Src_Module_Declaration_Kind', kind: 'variable'},
858
878
  {name: 'Src_Module_Declaration', kind: 'variable'},
859
879
  {name: 'Src_Module', kind: 'variable'},
860
880
  {name: 'Src_Modules', kind: 'variable'},
@@ -875,13 +895,6 @@ export const src_json = {
875
895
  {name: 'default_svelte_config', kind: 'variable'},
876
896
  ],
877
897
  },
878
- './svelte_helpers.js': {
879
- path: 'svelte_helpers.ts',
880
- declarations: [
881
- {name: 'SVELTE_MATCHER', kind: 'variable'},
882
- {name: 'SVELTE_RUNES_MATCHER', kind: 'variable'},
883
- ],
884
- },
885
898
  './sveltekit_helpers.js': {
886
899
  path: 'sveltekit_helpers.ts',
887
900
  declarations: [
@@ -1003,6 +1016,15 @@ export const src_json = {
1003
1016
  {name: 'validate_task_module', kind: 'function'},
1004
1017
  ],
1005
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
+ },
1006
1028
  './test.task.js': {
1007
1029
  path: 'test.task.ts',
1008
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
+ import type {Logger} from '@ryanatkn/belt/log.js';
5
+
6
+ import type {Path_Id} from './path.ts';
7
+ import {TS_MATCHER} from './constants.ts';
8
+ import {Parse_Exports_Context} from './parse_exports_context.ts';
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
+ };