@prodisco/search-libs 0.1.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 (99) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +379 -0
  3. package/dist/__tests__/extractor.test.d.ts +5 -0
  4. package/dist/__tests__/extractor.test.d.ts.map +1 -0
  5. package/dist/__tests__/extractor.test.js +452 -0
  6. package/dist/__tests__/extractor.test.js.map +1 -0
  7. package/dist/__tests__/library-indexer.test.d.ts +5 -0
  8. package/dist/__tests__/library-indexer.test.d.ts.map +1 -0
  9. package/dist/__tests__/library-indexer.test.js +611 -0
  10. package/dist/__tests__/library-indexer.test.js.map +1 -0
  11. package/dist/__tests__/schema.test.d.ts +5 -0
  12. package/dist/__tests__/schema.test.d.ts.map +1 -0
  13. package/dist/__tests__/schema.test.js +231 -0
  14. package/dist/__tests__/schema.test.js.map +1 -0
  15. package/dist/__tests__/script-parser.test.d.ts +5 -0
  16. package/dist/__tests__/script-parser.test.d.ts.map +1 -0
  17. package/dist/__tests__/script-parser.test.js +178 -0
  18. package/dist/__tests__/script-parser.test.js.map +1 -0
  19. package/dist/__tests__/search-engine.test.d.ts +5 -0
  20. package/dist/__tests__/search-engine.test.d.ts.map +1 -0
  21. package/dist/__tests__/search-engine.test.js +497 -0
  22. package/dist/__tests__/search-engine.test.js.map +1 -0
  23. package/dist/extractor/ast-parser.d.ts +48 -0
  24. package/dist/extractor/ast-parser.d.ts.map +1 -0
  25. package/dist/extractor/ast-parser.js +118 -0
  26. package/dist/extractor/ast-parser.js.map +1 -0
  27. package/dist/extractor/function-extractor.d.ts +20 -0
  28. package/dist/extractor/function-extractor.d.ts.map +1 -0
  29. package/dist/extractor/function-extractor.js +169 -0
  30. package/dist/extractor/function-extractor.js.map +1 -0
  31. package/dist/extractor/index.d.ts +22 -0
  32. package/dist/extractor/index.d.ts.map +1 -0
  33. package/dist/extractor/index.js +194 -0
  34. package/dist/extractor/index.js.map +1 -0
  35. package/dist/extractor/method-extractor.d.ts +30 -0
  36. package/dist/extractor/method-extractor.d.ts.map +1 -0
  37. package/dist/extractor/method-extractor.js +163 -0
  38. package/dist/extractor/method-extractor.js.map +1 -0
  39. package/dist/extractor/package-resolver.d.ts +77 -0
  40. package/dist/extractor/package-resolver.d.ts.map +1 -0
  41. package/dist/extractor/package-resolver.js +766 -0
  42. package/dist/extractor/package-resolver.js.map +1 -0
  43. package/dist/extractor/type-extractor.d.ts +15 -0
  44. package/dist/extractor/type-extractor.d.ts.map +1 -0
  45. package/dist/extractor/type-extractor.js +206 -0
  46. package/dist/extractor/type-extractor.js.map +1 -0
  47. package/dist/extractor/types.d.ts +116 -0
  48. package/dist/extractor/types.d.ts.map +1 -0
  49. package/dist/extractor/types.js +5 -0
  50. package/dist/extractor/types.js.map +1 -0
  51. package/dist/index.d.ts +18 -0
  52. package/dist/index.d.ts.map +1 -0
  53. package/dist/index.js +21 -0
  54. package/dist/index.js.map +1 -0
  55. package/dist/library-indexer.d.ts +104 -0
  56. package/dist/library-indexer.d.ts.map +1 -0
  57. package/dist/library-indexer.js +295 -0
  58. package/dist/library-indexer.js.map +1 -0
  59. package/dist/schema/base-schema.d.ts +63 -0
  60. package/dist/schema/base-schema.d.ts.map +1 -0
  61. package/dist/schema/base-schema.js +63 -0
  62. package/dist/schema/base-schema.js.map +1 -0
  63. package/dist/schema/index.d.ts +6 -0
  64. package/dist/schema/index.d.ts.map +1 -0
  65. package/dist/schema/index.js +6 -0
  66. package/dist/schema/index.js.map +1 -0
  67. package/dist/schema/schema-builder.d.ts +47 -0
  68. package/dist/schema/schema-builder.d.ts.map +1 -0
  69. package/dist/schema/schema-builder.js +236 -0
  70. package/dist/schema/schema-builder.js.map +1 -0
  71. package/dist/script/index.d.ts +6 -0
  72. package/dist/script/index.d.ts.map +1 -0
  73. package/dist/script/index.js +5 -0
  74. package/dist/script/index.js.map +1 -0
  75. package/dist/script/script-parser.d.ts +18 -0
  76. package/dist/script/script-parser.d.ts.map +1 -0
  77. package/dist/script/script-parser.js +246 -0
  78. package/dist/script/script-parser.js.map +1 -0
  79. package/dist/script/types.d.ts +32 -0
  80. package/dist/script/types.d.ts.map +1 -0
  81. package/dist/script/types.js +5 -0
  82. package/dist/script/types.js.map +1 -0
  83. package/dist/search/index.d.ts +7 -0
  84. package/dist/search/index.d.ts.map +1 -0
  85. package/dist/search/index.js +7 -0
  86. package/dist/search/index.js.map +1 -0
  87. package/dist/search/query-builder.d.ts +59 -0
  88. package/dist/search/query-builder.d.ts.map +1 -0
  89. package/dist/search/query-builder.js +103 -0
  90. package/dist/search/query-builder.js.map +1 -0
  91. package/dist/search/result-formatter.d.ts +61 -0
  92. package/dist/search/result-formatter.d.ts.map +1 -0
  93. package/dist/search/result-formatter.js +170 -0
  94. package/dist/search/result-formatter.js.map +1 -0
  95. package/dist/search/search-engine.d.ts +105 -0
  96. package/dist/search/search-engine.d.ts.map +1 -0
  97. package/dist/search/search-engine.js +245 -0
  98. package/dist/search/search-engine.js.map +1 -0
  99. package/package.json +41 -0
@@ -0,0 +1,766 @@
1
+ /**
2
+ * Resolve npm packages and find their .d.ts files
3
+ */
4
+ import { existsSync, readFileSync, readdirSync, statSync } from 'fs';
5
+ import { join, dirname, extname, isAbsolute } from 'path';
6
+ import * as ts from 'typescript';
7
+ /**
8
+ * Resolve a package and find all its .d.ts files
9
+ */
10
+ export function resolvePackage(packageName, basePath = process.cwd()) {
11
+ const nodeModulesPath = join(basePath, 'node_modules');
12
+ // Handle scoped packages (e.g., @kubernetes/client-node)
13
+ const packagePath = packageName.startsWith('@')
14
+ ? join(nodeModulesPath, ...packageName.split('/'))
15
+ : join(nodeModulesPath, packageName);
16
+ if (!existsSync(packagePath)) {
17
+ return null;
18
+ }
19
+ const packageJsonPath = join(packagePath, 'package.json');
20
+ const packageJson = getPackageJson(packageJsonPath);
21
+ if (!packageJson) {
22
+ return null;
23
+ }
24
+ // Find main .d.ts file
25
+ const mainDts = findMainDts(packagePath, packageJson);
26
+ // Find types directory
27
+ const typesDir = findTypesDir(packagePath, packageJson);
28
+ // Find all .d.ts files
29
+ let allDtsFiles = findDtsFiles(packagePath, typesDir);
30
+ // Ensure mainDts is included in allDtsFiles if it exists
31
+ if (mainDts && !allDtsFiles.includes(mainDts)) {
32
+ allDtsFiles = [mainDts, ...allDtsFiles];
33
+ }
34
+ // Build export info from main .d.ts (alias map + public exports)
35
+ let exportAliases;
36
+ let publicExports;
37
+ if (mainDts) {
38
+ const exportInfo = buildExportInfo(mainDts);
39
+ exportAliases = exportInfo.aliasMap;
40
+ publicExports = exportInfo.publicExports;
41
+ }
42
+ return {
43
+ packageName,
44
+ packagePath,
45
+ mainDts,
46
+ typesDir,
47
+ allDtsFiles,
48
+ exportAliases,
49
+ publicExports,
50
+ };
51
+ }
52
+ /**
53
+ * Find the main ESM JavaScript entry file for a package.
54
+ *
55
+ * This is intended as a fallback for packages that ship no `.d.ts` files.
56
+ * Scope: ESM only (`.js` with `"type":"module"` or `.mjs`). CommonJS (`.cjs`)
57
+ * is intentionally out of scope.
58
+ */
59
+ export function findMainEsmJs(packagePath, packageJson) {
60
+ const pkgType = typeof packageJson.type === 'string' ? packageJson.type : undefined;
61
+ const isTypeModule = pkgType === 'module';
62
+ // 1) Prefer conditional exports root entry: exports["."] (import -> default)
63
+ const exportsField = packageJson.exports;
64
+ const rootExport = exportsField && typeof exportsField === 'object'
65
+ ? exportsField['.'] ?? exportsField
66
+ : exportsField;
67
+ const exportCandidate = pickConditionalExportPath(rootExport, { prefer: ['import', 'default'] }) ??
68
+ // Some packages put the path directly at exports["."] as a string
69
+ (typeof rootExport === 'string' ? rootExport : undefined);
70
+ if (exportCandidate) {
71
+ const resolved = resolvePackageEsmFile(packagePath, exportCandidate, {
72
+ allowJsExtension: true,
73
+ allowMjsExtension: true,
74
+ // Any path coming from `exports` for ESM import/default is treated as ESM-capable.
75
+ requireTypeModuleForJs: false,
76
+ isTypeModule,
77
+ });
78
+ if (resolved)
79
+ return resolved;
80
+ }
81
+ // 2) Try "module" field (commonly ESM build)
82
+ const moduleField = packageJson.module;
83
+ if (typeof moduleField === 'string') {
84
+ const resolved = resolvePackageEsmFile(packagePath, moduleField, {
85
+ allowJsExtension: true,
86
+ allowMjsExtension: true,
87
+ requireTypeModuleForJs: false,
88
+ isTypeModule,
89
+ });
90
+ if (resolved)
91
+ return resolved;
92
+ }
93
+ // 3) Try "main" field (only if it looks like ESM)
94
+ const mainField = packageJson.main;
95
+ if (typeof mainField === 'string') {
96
+ const resolved = resolvePackageEsmFile(packagePath, mainField, {
97
+ allowJsExtension: true,
98
+ allowMjsExtension: true,
99
+ // If `main` points at `.js`, only treat it as ESM when package.json is type:module.
100
+ requireTypeModuleForJs: true,
101
+ isTypeModule,
102
+ });
103
+ if (resolved)
104
+ return resolved;
105
+ }
106
+ // 4) Common patterns
107
+ const patterns = [
108
+ 'dist/index.js',
109
+ 'dist/index.mjs',
110
+ 'lib/index.js',
111
+ 'lib/index.mjs',
112
+ 'index.js',
113
+ 'index.mjs',
114
+ ];
115
+ for (const pattern of patterns) {
116
+ const resolved = resolvePackageEsmFile(packagePath, pattern, {
117
+ allowJsExtension: true,
118
+ allowMjsExtension: true,
119
+ requireTypeModuleForJs: true,
120
+ isTypeModule,
121
+ });
122
+ if (resolved)
123
+ return resolved;
124
+ }
125
+ return undefined;
126
+ }
127
+ function resolvePackageEsmFile(packagePath, packageRelativePath, options) {
128
+ // Disallow absolute paths in package.json to avoid escaping the package folder.
129
+ if (isAbsolute(packageRelativePath))
130
+ return undefined;
131
+ // Normalize leading "./"
132
+ const rel = packageRelativePath.startsWith('./')
133
+ ? packageRelativePath.slice(2)
134
+ : packageRelativePath;
135
+ const base = join(packagePath, rel);
136
+ // Direct file match
137
+ const direct = resolveEsmFileCandidate(base, options);
138
+ if (direct)
139
+ return direct;
140
+ // If extensionless, try common ESM extensions + index files
141
+ if (!extname(base)) {
142
+ const extCandidates = [];
143
+ if (options.allowJsExtension)
144
+ extCandidates.push(`${base}.js`);
145
+ if (options.allowMjsExtension)
146
+ extCandidates.push(`${base}.mjs`);
147
+ for (const c of extCandidates) {
148
+ const resolved = resolveEsmFileCandidate(c, options);
149
+ if (resolved)
150
+ return resolved;
151
+ }
152
+ const indexCandidates = [];
153
+ if (options.allowJsExtension)
154
+ indexCandidates.push(join(base, 'index.js'));
155
+ if (options.allowMjsExtension)
156
+ indexCandidates.push(join(base, 'index.mjs'));
157
+ for (const c of indexCandidates) {
158
+ const resolved = resolveEsmFileCandidate(c, options);
159
+ if (resolved)
160
+ return resolved;
161
+ }
162
+ }
163
+ return undefined;
164
+ }
165
+ function resolveEsmFileCandidate(filePath, options) {
166
+ if (!existsSync(filePath))
167
+ return undefined;
168
+ try {
169
+ const stat = statSync(filePath);
170
+ if (!stat.isFile())
171
+ return undefined;
172
+ }
173
+ catch {
174
+ return undefined;
175
+ }
176
+ const ext = extname(filePath).toLowerCase();
177
+ if (ext === '.cjs')
178
+ return undefined;
179
+ if (ext === '.mjs')
180
+ return filePath;
181
+ if (ext === '.js') {
182
+ if (options.requireTypeModuleForJs && !options.isTypeModule) {
183
+ return undefined;
184
+ }
185
+ return filePath;
186
+ }
187
+ // Unsupported extension for JS fallback
188
+ return undefined;
189
+ }
190
+ function pickConditionalExportPath(value, options) {
191
+ if (!value)
192
+ return undefined;
193
+ if (typeof value === 'string')
194
+ return value;
195
+ if (typeof value !== 'object')
196
+ return undefined;
197
+ const obj = value;
198
+ // Prefer known condition keys first.
199
+ for (const key of options.prefer) {
200
+ const v = obj[key];
201
+ if (key === 'types')
202
+ continue;
203
+ if (typeof v === 'string')
204
+ return v;
205
+ const nested = pickConditionalExportPath(v, options);
206
+ if (nested)
207
+ return nested;
208
+ }
209
+ // As a fallback, search any nested condition (but skip require/types).
210
+ for (const [key, v] of Object.entries(obj)) {
211
+ if (key === 'types' || key === 'require')
212
+ continue;
213
+ if (typeof v === 'string')
214
+ return v;
215
+ const nested = pickConditionalExportPath(v, options);
216
+ if (nested)
217
+ return nested;
218
+ }
219
+ return undefined;
220
+ }
221
+ /**
222
+ * Read and parse package.json
223
+ */
224
+ export function getPackageJson(packageJsonPath) {
225
+ if (!existsSync(packageJsonPath)) {
226
+ return null;
227
+ }
228
+ try {
229
+ const content = readFileSync(packageJsonPath, 'utf-8');
230
+ return JSON.parse(content);
231
+ }
232
+ catch {
233
+ return null;
234
+ }
235
+ }
236
+ /**
237
+ * Find the main .d.ts file for a package
238
+ */
239
+ function findMainDts(packagePath, packageJson) {
240
+ // Check "types" or "typings" field
241
+ const typesField = (packageJson.types || packageJson.typings);
242
+ if (typesField) {
243
+ const typesPath = join(packagePath, typesField);
244
+ if (existsSync(typesPath)) {
245
+ return typesPath;
246
+ }
247
+ }
248
+ // Check "exports" field for types
249
+ const exports = packageJson.exports;
250
+ if (exports && typeof exports === 'object') {
251
+ // Check root export
252
+ const rootExport = exports['.'];
253
+ if (rootExport && typeof rootExport === 'object') {
254
+ const typesPath = rootExport.types;
255
+ if (typesPath) {
256
+ const fullPath = join(packagePath, typesPath);
257
+ if (existsSync(fullPath)) {
258
+ return fullPath;
259
+ }
260
+ }
261
+ }
262
+ }
263
+ // Try common patterns
264
+ const patterns = [
265
+ 'dist/index.d.ts',
266
+ 'lib/index.d.ts',
267
+ 'index.d.ts',
268
+ 'types/index.d.ts',
269
+ ];
270
+ for (const pattern of patterns) {
271
+ const fullPath = join(packagePath, pattern);
272
+ if (existsSync(fullPath)) {
273
+ return fullPath;
274
+ }
275
+ }
276
+ return undefined;
277
+ }
278
+ /**
279
+ * Find the types directory for a package
280
+ */
281
+ function findTypesDir(packagePath, packageJson) {
282
+ // Check if there's a types directory
283
+ const patterns = ['types', 'typings', 'dist', 'lib'];
284
+ for (const pattern of patterns) {
285
+ const dirPath = join(packagePath, pattern);
286
+ if (existsSync(dirPath) && statSync(dirPath).isDirectory()) {
287
+ // Check if it contains .d.ts files
288
+ const files = readdirSync(dirPath);
289
+ if (files.some((f) => f.endsWith('.d.ts'))) {
290
+ return dirPath;
291
+ }
292
+ }
293
+ }
294
+ // For K8s client, check dist/gen
295
+ const k8sGenPath = join(packagePath, 'dist', 'gen');
296
+ if (existsSync(k8sGenPath)) {
297
+ return k8sGenPath;
298
+ }
299
+ return undefined;
300
+ }
301
+ /**
302
+ * Find all .d.ts files in a package
303
+ */
304
+ export function findDtsFiles(packagePath, typesDir, maxDepth = 5) {
305
+ const files = [];
306
+ const visited = new Set();
307
+ function walkDir(dir, depth) {
308
+ if (depth > maxDepth || visited.has(dir)) {
309
+ return;
310
+ }
311
+ visited.add(dir);
312
+ if (!existsSync(dir)) {
313
+ return;
314
+ }
315
+ try {
316
+ const entries = readdirSync(dir);
317
+ for (const entry of entries) {
318
+ // Skip node_modules and hidden directories
319
+ if (entry === 'node_modules' || entry.startsWith('.')) {
320
+ continue;
321
+ }
322
+ const fullPath = join(dir, entry);
323
+ try {
324
+ const stat = statSync(fullPath);
325
+ if (stat.isDirectory()) {
326
+ walkDir(fullPath, depth + 1);
327
+ }
328
+ else if (entry.endsWith('.d.ts')) {
329
+ // Skip test files and internal files
330
+ if (!entry.includes('.test.') &&
331
+ !entry.includes('.spec.') &&
332
+ !entry.includes('__')) {
333
+ files.push(fullPath);
334
+ }
335
+ }
336
+ }
337
+ catch {
338
+ // Skip files we can't access
339
+ }
340
+ }
341
+ }
342
+ catch {
343
+ // Skip directories we can't read
344
+ }
345
+ }
346
+ // Start from types directory if available, otherwise from package root
347
+ if (typesDir) {
348
+ walkDir(typesDir, 0);
349
+ }
350
+ // Also check dist directory if not already covered
351
+ const distPath = join(packagePath, 'dist');
352
+ if (!typesDir?.startsWith(distPath) && existsSync(distPath)) {
353
+ walkDir(distPath, 0);
354
+ }
355
+ // Check lib directory
356
+ const libPath = join(packagePath, 'lib');
357
+ if (!typesDir?.startsWith(libPath) && existsSync(libPath)) {
358
+ walkDir(libPath, 0);
359
+ }
360
+ // Check src directory (some packages like simple-statistics store .d.ts files here)
361
+ const srcPath = join(packagePath, 'src');
362
+ if (!typesDir?.startsWith(srcPath) && existsSync(srcPath)) {
363
+ walkDir(srcPath, 0);
364
+ }
365
+ return files;
366
+ }
367
+ /**
368
+ * Get all exportable types from a package's main entry point
369
+ */
370
+ export function getPackageExports(packagePath, packageJson) {
371
+ const exports = [];
372
+ // Check "exports" field
373
+ const exportsField = packageJson.exports;
374
+ if (exportsField && typeof exportsField === 'object') {
375
+ for (const key of Object.keys(exportsField)) {
376
+ if (key !== '.' && !key.startsWith('./')) {
377
+ continue;
378
+ }
379
+ exports.push(key === '.' ? 'default' : key.slice(2));
380
+ }
381
+ }
382
+ return exports;
383
+ }
384
+ /**
385
+ * Parse exports from a package's entry point to determine:
386
+ * 1. Which names are publicly exported (visible to users)
387
+ * 2. Which internal names map to which public aliases
388
+ *
389
+ * Parses export statements like:
390
+ * export { ObjectCoreV1Api as CoreV1Api } from './gen/api/coreV1Api';
391
+ * export { SomeClass } from './module'; // No alias, SomeClass is public
392
+ * export class PublicClass { } // Directly exported class
393
+ *
394
+ * Recursively follows `export * from './module'` re-exports.
395
+ */
396
+ export function buildExportInfo(mainDtsPath) {
397
+ const aliasMap = new Map();
398
+ const publicExports = new Set();
399
+ const visitedFiles = new Set();
400
+ function processFile(filePath) {
401
+ // Avoid infinite loops
402
+ if (visitedFiles.has(filePath)) {
403
+ return;
404
+ }
405
+ visitedFiles.add(filePath);
406
+ if (!existsSync(filePath)) {
407
+ return;
408
+ }
409
+ try {
410
+ const sourceCode = readFileSync(filePath, 'utf-8');
411
+ const sourceFile = ts.createSourceFile(filePath, sourceCode, ts.ScriptTarget.Latest, true);
412
+ const fileDir = dirname(filePath);
413
+ // Walk through all statements looking for export declarations
414
+ function visit(node) {
415
+ // Handle: export { InternalName as PublicName } from '...';
416
+ // Handle: export { Name } from '...'; (no alias)
417
+ if (ts.isExportDeclaration(node)) {
418
+ if (node.exportClause && ts.isNamedExports(node.exportClause)) {
419
+ for (const element of node.exportClause.elements) {
420
+ const exportedName = element.name.text;
421
+ if (element.propertyName) {
422
+ // Aliased export: export { Internal as Public }
423
+ const internalName = element.propertyName.text;
424
+ aliasMap.set(internalName, exportedName);
425
+ publicExports.add(exportedName);
426
+ }
427
+ else {
428
+ // Direct export: export { Name }
429
+ publicExports.add(exportedName);
430
+ }
431
+ }
432
+ }
433
+ // Handle: export * from './module';
434
+ // Recursively follow re-exports
435
+ if (!node.exportClause && node.moduleSpecifier) {
436
+ const moduleSpec = node.moduleSpecifier.text;
437
+ if (moduleSpec.startsWith('.')) {
438
+ // Resolve relative path
439
+ let resolvedPath = join(fileDir, moduleSpec);
440
+ // Try with .d.ts extension
441
+ if (!resolvedPath.endsWith('.d.ts')) {
442
+ if (resolvedPath.endsWith('.js')) {
443
+ resolvedPath = resolvedPath.replace(/\.js$/, '.d.ts');
444
+ }
445
+ else {
446
+ resolvedPath = resolvedPath + '.d.ts';
447
+ }
448
+ }
449
+ // Also try index.d.ts if file doesn't exist
450
+ if (!existsSync(resolvedPath)) {
451
+ const indexPath = join(fileDir, moduleSpec, 'index.d.ts');
452
+ if (existsSync(indexPath)) {
453
+ resolvedPath = indexPath;
454
+ }
455
+ }
456
+ processFile(resolvedPath);
457
+ }
458
+ }
459
+ }
460
+ // Handle: export class ClassName { }
461
+ if (ts.isClassDeclaration(node) && node.name) {
462
+ const hasExportModifier = node.modifiers?.some((m) => m.kind === ts.SyntaxKind.ExportKeyword);
463
+ if (hasExportModifier) {
464
+ publicExports.add(node.name.text);
465
+ }
466
+ }
467
+ // Handle: export interface InterfaceName { }
468
+ if (ts.isInterfaceDeclaration(node) && node.name) {
469
+ const hasExportModifier = node.modifiers?.some((m) => m.kind === ts.SyntaxKind.ExportKeyword);
470
+ if (hasExportModifier) {
471
+ publicExports.add(node.name.text);
472
+ }
473
+ }
474
+ // Handle: export function functionName() { }
475
+ if (ts.isFunctionDeclaration(node) && node.name) {
476
+ const hasExportModifier = node.modifiers?.some((m) => m.kind === ts.SyntaxKind.ExportKeyword);
477
+ if (hasExportModifier) {
478
+ publicExports.add(node.name.text);
479
+ }
480
+ }
481
+ ts.forEachChild(node, visit);
482
+ }
483
+ visit(sourceFile);
484
+ }
485
+ catch {
486
+ // Silently ignore parsing errors
487
+ }
488
+ }
489
+ processFile(mainDtsPath);
490
+ return { aliasMap, publicExports };
491
+ }
492
+ /**
493
+ * Build the public export surface for an ESM entry file.
494
+ *
495
+ * This follows only *relative* static re-exports (`./...`). Re-exports from
496
+ * dependencies are intentionally ignored.
497
+ */
498
+ export function buildEsmExportSurface(entryFile) {
499
+ const visitedFiles = new Set();
500
+ const exportCache = new Map();
501
+ const inProgress = new Set();
502
+ function resolveModuleExports(filePath) {
503
+ if (exportCache.has(filePath)) {
504
+ return exportCache.get(filePath);
505
+ }
506
+ if (inProgress.has(filePath)) {
507
+ // Cycle detected; treat as empty to avoid infinite recursion.
508
+ return new Map();
509
+ }
510
+ inProgress.add(filePath);
511
+ visitedFiles.add(filePath);
512
+ const exports = new Map();
513
+ if (!existsSync(filePath)) {
514
+ exportCache.set(filePath, exports);
515
+ inProgress.delete(filePath);
516
+ return exports;
517
+ }
518
+ let sourceCode = '';
519
+ try {
520
+ sourceCode = readFileSync(filePath, 'utf-8');
521
+ }
522
+ catch {
523
+ exportCache.set(filePath, exports);
524
+ inProgress.delete(filePath);
525
+ return exports;
526
+ }
527
+ const sourceFile = ts.createSourceFile(filePath, sourceCode, ts.ScriptTarget.Latest, true);
528
+ // Track relative imports so we can resolve "import { foo } from './x.js'; export { foo }"
529
+ const importMap = new Map();
530
+ for (const stmt of sourceFile.statements) {
531
+ if (!ts.isImportDeclaration(stmt) || !stmt.moduleSpecifier)
532
+ continue;
533
+ if (!ts.isStringLiteral(stmt.moduleSpecifier))
534
+ continue;
535
+ const moduleSpec = stmt.moduleSpecifier.text;
536
+ const resolved = resolveRelativeEsmModule(filePath, moduleSpec);
537
+ if (!resolved)
538
+ continue;
539
+ const clause = stmt.importClause;
540
+ if (!clause)
541
+ continue;
542
+ // Default import: import foo from './x.js'
543
+ if (clause.name) {
544
+ importMap.set(clause.name.text, {
545
+ sourceFile: resolved,
546
+ importedName: 'default',
547
+ });
548
+ }
549
+ const named = clause.namedBindings;
550
+ if (named && ts.isNamedImports(named)) {
551
+ for (const el of named.elements) {
552
+ const localName = el.name.text;
553
+ const importedName = el.propertyName?.text ?? el.name.text;
554
+ importMap.set(localName, { sourceFile: resolved, importedName });
555
+ }
556
+ }
557
+ }
558
+ function addExport(exportName, target) {
559
+ exports.set(exportName, target);
560
+ }
561
+ for (const stmt of sourceFile.statements) {
562
+ // export { ... } [from '...']
563
+ if (ts.isExportDeclaration(stmt)) {
564
+ const moduleSpec = stmt.moduleSpecifier && ts.isStringLiteral(stmt.moduleSpecifier)
565
+ ? stmt.moduleSpecifier.text
566
+ : undefined;
567
+ if (moduleSpec && moduleSpec.startsWith('.')) {
568
+ const resolved = resolveRelativeEsmModule(filePath, moduleSpec);
569
+ if (!resolved)
570
+ continue;
571
+ const targetExports = resolveModuleExports(resolved);
572
+ // export { a, b as c } from './x.js'
573
+ if (stmt.exportClause && ts.isNamedExports(stmt.exportClause)) {
574
+ for (const el of stmt.exportClause.elements) {
575
+ const exportedName = el.name.text;
576
+ const requestedName = el.propertyName?.text ?? el.name.text;
577
+ const target = targetExports.get(requestedName);
578
+ if (target) {
579
+ addExport(exportedName, target);
580
+ }
581
+ }
582
+ }
583
+ // export * from './x.js'
584
+ if (!stmt.exportClause) {
585
+ for (const [name, target] of targetExports) {
586
+ // export * does not re-export default
587
+ if (name === 'default')
588
+ continue;
589
+ if (!exports.has(name)) {
590
+ addExport(name, target);
591
+ }
592
+ }
593
+ }
594
+ continue;
595
+ }
596
+ // export { a, b as c } (local or imported bindings)
597
+ if (stmt.exportClause && ts.isNamedExports(stmt.exportClause)) {
598
+ for (const el of stmt.exportClause.elements) {
599
+ const exportedName = el.name.text;
600
+ const localName = el.propertyName?.text ?? el.name.text;
601
+ const imported = importMap.get(localName);
602
+ if (imported && imported.importedName !== '*') {
603
+ const targetExports = resolveModuleExports(imported.sourceFile);
604
+ const target = targetExports.get(imported.importedName);
605
+ if (target) {
606
+ addExport(exportedName, target);
607
+ }
608
+ continue;
609
+ }
610
+ addExport(exportedName, { originFile: filePath, originName: localName });
611
+ }
612
+ }
613
+ }
614
+ // export default <expr>;
615
+ if (ts.isExportAssignment(stmt)) {
616
+ if (stmt.isExportEquals)
617
+ continue;
618
+ if (ts.isIdentifier(stmt.expression)) {
619
+ const localName = stmt.expression.text;
620
+ const imported = importMap.get(localName);
621
+ if (imported && imported.importedName !== '*') {
622
+ const targetExports = resolveModuleExports(imported.sourceFile);
623
+ const target = targetExports.get(imported.importedName);
624
+ if (target) {
625
+ addExport('default', target);
626
+ }
627
+ }
628
+ else {
629
+ addExport('default', { originFile: filePath, originName: localName });
630
+ }
631
+ }
632
+ }
633
+ // export function foo() {} / export default function foo() {}
634
+ if (ts.isFunctionDeclaration(stmt) && stmt.name) {
635
+ const isExported = stmt.modifiers?.some((m) => m.kind === ts.SyntaxKind.ExportKeyword);
636
+ if (!isExported)
637
+ continue;
638
+ const isDefault = stmt.modifiers?.some((m) => m.kind === ts.SyntaxKind.DefaultKeyword);
639
+ if (isDefault) {
640
+ addExport('default', { originFile: filePath, originName: stmt.name.text });
641
+ }
642
+ else {
643
+ addExport(stmt.name.text, { originFile: filePath, originName: stmt.name.text });
644
+ }
645
+ }
646
+ // export class Foo {} / export default class Foo {}
647
+ if (ts.isClassDeclaration(stmt) && stmt.name) {
648
+ const isExported = stmt.modifiers?.some((m) => m.kind === ts.SyntaxKind.ExportKeyword);
649
+ if (!isExported)
650
+ continue;
651
+ const isDefault = stmt.modifiers?.some((m) => m.kind === ts.SyntaxKind.DefaultKeyword);
652
+ if (isDefault) {
653
+ addExport('default', { originFile: filePath, originName: stmt.name.text });
654
+ }
655
+ else {
656
+ addExport(stmt.name.text, { originFile: filePath, originName: stmt.name.text });
657
+ }
658
+ }
659
+ // export const foo = ...
660
+ if (ts.isVariableStatement(stmt)) {
661
+ const isExported = stmt.modifiers?.some((m) => m.kind === ts.SyntaxKind.ExportKeyword);
662
+ if (!isExported)
663
+ continue;
664
+ for (const decl of stmt.declarationList.declarations) {
665
+ if (ts.isIdentifier(decl.name)) {
666
+ addExport(decl.name.text, { originFile: filePath, originName: decl.name.text });
667
+ }
668
+ }
669
+ }
670
+ }
671
+ exportCache.set(filePath, exports);
672
+ inProgress.delete(filePath);
673
+ return exports;
674
+ }
675
+ const entryExports = resolveModuleExports(entryFile);
676
+ const exportAllowlistByFile = new Map();
677
+ const aliasMapByFile = new Map();
678
+ const publicExports = new Set();
679
+ for (const [publicName, target] of entryExports) {
680
+ publicExports.add(publicName);
681
+ const { originFile, originName } = target;
682
+ let allow = exportAllowlistByFile.get(originFile);
683
+ if (!allow) {
684
+ allow = new Set();
685
+ exportAllowlistByFile.set(originFile, allow);
686
+ }
687
+ allow.add(originName);
688
+ if (originName !== publicName) {
689
+ let aliases = aliasMapByFile.get(originFile);
690
+ if (!aliases) {
691
+ aliases = new Map();
692
+ aliasMapByFile.set(originFile, aliases);
693
+ }
694
+ aliases.set(originName, publicName);
695
+ }
696
+ }
697
+ return {
698
+ entryFile,
699
+ filesToParse: Array.from(visitedFiles),
700
+ exportAllowlistByFile,
701
+ aliasMapByFile,
702
+ publicExports,
703
+ };
704
+ }
705
+ function resolveRelativeEsmModule(fromFilePath, moduleSpec) {
706
+ if (!moduleSpec.startsWith('.'))
707
+ return null;
708
+ const base = join(dirname(fromFilePath), moduleSpec);
709
+ // Direct path
710
+ const direct = resolveRelativeEsmCandidate(base);
711
+ if (direct)
712
+ return direct;
713
+ // Extensionless: try .js/.mjs and index files
714
+ if (!extname(base)) {
715
+ const js = resolveRelativeEsmCandidate(`${base}.js`);
716
+ if (js)
717
+ return js;
718
+ const mjs = resolveRelativeEsmCandidate(`${base}.mjs`);
719
+ if (mjs)
720
+ return mjs;
721
+ const idxJs = resolveRelativeEsmCandidate(join(base, 'index.js'));
722
+ if (idxJs)
723
+ return idxJs;
724
+ const idxMjs = resolveRelativeEsmCandidate(join(base, 'index.mjs'));
725
+ if (idxMjs)
726
+ return idxMjs;
727
+ }
728
+ else {
729
+ // If spec points to .js but only .mjs exists (or vice versa), try swapping.
730
+ const ext = extname(base).toLowerCase();
731
+ if (ext === '.js') {
732
+ const swap = resolveRelativeEsmCandidate(base.replace(/\.js$/i, '.mjs'));
733
+ if (swap)
734
+ return swap;
735
+ }
736
+ if (ext === '.mjs') {
737
+ const swap = resolveRelativeEsmCandidate(base.replace(/\.mjs$/i, '.js'));
738
+ if (swap)
739
+ return swap;
740
+ }
741
+ }
742
+ return null;
743
+ }
744
+ function resolveRelativeEsmCandidate(filePath) {
745
+ if (!existsSync(filePath))
746
+ return null;
747
+ try {
748
+ const stat = statSync(filePath);
749
+ if (!stat.isFile())
750
+ return null;
751
+ }
752
+ catch {
753
+ return null;
754
+ }
755
+ const ext = extname(filePath).toLowerCase();
756
+ if (ext !== '.js' && ext !== '.mjs')
757
+ return null;
758
+ return filePath;
759
+ }
760
+ /**
761
+ * @deprecated Use buildExportInfo instead
762
+ */
763
+ export function buildExportAliasMap(mainDtsPath) {
764
+ return buildExportInfo(mainDtsPath).aliasMap;
765
+ }
766
+ //# sourceMappingURL=package-resolver.js.map