@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.
- package/LICENSE +21 -0
- package/README.md +379 -0
- package/dist/__tests__/extractor.test.d.ts +5 -0
- package/dist/__tests__/extractor.test.d.ts.map +1 -0
- package/dist/__tests__/extractor.test.js +452 -0
- package/dist/__tests__/extractor.test.js.map +1 -0
- package/dist/__tests__/library-indexer.test.d.ts +5 -0
- package/dist/__tests__/library-indexer.test.d.ts.map +1 -0
- package/dist/__tests__/library-indexer.test.js +611 -0
- package/dist/__tests__/library-indexer.test.js.map +1 -0
- package/dist/__tests__/schema.test.d.ts +5 -0
- package/dist/__tests__/schema.test.d.ts.map +1 -0
- package/dist/__tests__/schema.test.js +231 -0
- package/dist/__tests__/schema.test.js.map +1 -0
- package/dist/__tests__/script-parser.test.d.ts +5 -0
- package/dist/__tests__/script-parser.test.d.ts.map +1 -0
- package/dist/__tests__/script-parser.test.js +178 -0
- package/dist/__tests__/script-parser.test.js.map +1 -0
- package/dist/__tests__/search-engine.test.d.ts +5 -0
- package/dist/__tests__/search-engine.test.d.ts.map +1 -0
- package/dist/__tests__/search-engine.test.js +497 -0
- package/dist/__tests__/search-engine.test.js.map +1 -0
- package/dist/extractor/ast-parser.d.ts +48 -0
- package/dist/extractor/ast-parser.d.ts.map +1 -0
- package/dist/extractor/ast-parser.js +118 -0
- package/dist/extractor/ast-parser.js.map +1 -0
- package/dist/extractor/function-extractor.d.ts +20 -0
- package/dist/extractor/function-extractor.d.ts.map +1 -0
- package/dist/extractor/function-extractor.js +169 -0
- package/dist/extractor/function-extractor.js.map +1 -0
- package/dist/extractor/index.d.ts +22 -0
- package/dist/extractor/index.d.ts.map +1 -0
- package/dist/extractor/index.js +194 -0
- package/dist/extractor/index.js.map +1 -0
- package/dist/extractor/method-extractor.d.ts +30 -0
- package/dist/extractor/method-extractor.d.ts.map +1 -0
- package/dist/extractor/method-extractor.js +163 -0
- package/dist/extractor/method-extractor.js.map +1 -0
- package/dist/extractor/package-resolver.d.ts +77 -0
- package/dist/extractor/package-resolver.d.ts.map +1 -0
- package/dist/extractor/package-resolver.js +766 -0
- package/dist/extractor/package-resolver.js.map +1 -0
- package/dist/extractor/type-extractor.d.ts +15 -0
- package/dist/extractor/type-extractor.d.ts.map +1 -0
- package/dist/extractor/type-extractor.js +206 -0
- package/dist/extractor/type-extractor.js.map +1 -0
- package/dist/extractor/types.d.ts +116 -0
- package/dist/extractor/types.d.ts.map +1 -0
- package/dist/extractor/types.js +5 -0
- package/dist/extractor/types.js.map +1 -0
- package/dist/index.d.ts +18 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +21 -0
- package/dist/index.js.map +1 -0
- package/dist/library-indexer.d.ts +104 -0
- package/dist/library-indexer.d.ts.map +1 -0
- package/dist/library-indexer.js +295 -0
- package/dist/library-indexer.js.map +1 -0
- package/dist/schema/base-schema.d.ts +63 -0
- package/dist/schema/base-schema.d.ts.map +1 -0
- package/dist/schema/base-schema.js +63 -0
- package/dist/schema/base-schema.js.map +1 -0
- package/dist/schema/index.d.ts +6 -0
- package/dist/schema/index.d.ts.map +1 -0
- package/dist/schema/index.js +6 -0
- package/dist/schema/index.js.map +1 -0
- package/dist/schema/schema-builder.d.ts +47 -0
- package/dist/schema/schema-builder.d.ts.map +1 -0
- package/dist/schema/schema-builder.js +236 -0
- package/dist/schema/schema-builder.js.map +1 -0
- package/dist/script/index.d.ts +6 -0
- package/dist/script/index.d.ts.map +1 -0
- package/dist/script/index.js +5 -0
- package/dist/script/index.js.map +1 -0
- package/dist/script/script-parser.d.ts +18 -0
- package/dist/script/script-parser.d.ts.map +1 -0
- package/dist/script/script-parser.js +246 -0
- package/dist/script/script-parser.js.map +1 -0
- package/dist/script/types.d.ts +32 -0
- package/dist/script/types.d.ts.map +1 -0
- package/dist/script/types.js +5 -0
- package/dist/script/types.js.map +1 -0
- package/dist/search/index.d.ts +7 -0
- package/dist/search/index.d.ts.map +1 -0
- package/dist/search/index.js +7 -0
- package/dist/search/index.js.map +1 -0
- package/dist/search/query-builder.d.ts +59 -0
- package/dist/search/query-builder.d.ts.map +1 -0
- package/dist/search/query-builder.js +103 -0
- package/dist/search/query-builder.js.map +1 -0
- package/dist/search/result-formatter.d.ts +61 -0
- package/dist/search/result-formatter.d.ts.map +1 -0
- package/dist/search/result-formatter.js +170 -0
- package/dist/search/result-formatter.js.map +1 -0
- package/dist/search/search-engine.d.ts +105 -0
- package/dist/search/search-engine.d.ts.map +1 -0
- package/dist/search/search-engine.js +245 -0
- package/dist/search/search-engine.js.map +1 -0
- 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
|