@node-cli/bundlecheck 1.4.1 → 1.5.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.
@@ -0,0 +1,330 @@
1
+ import fs from "node:fs";
2
+ import path from "node:path";
3
+ /**
4
+ * Resolve a relative import path to an absolute file path.
5
+ */ function resolveImportPath(basePath, importPath) {
6
+ // Remove quotes and trim.
7
+ let cleanPath = importPath.replace(/['"]/g, "").trim();
8
+ // Only handle relative imports.
9
+ if (!cleanPath.startsWith(".")) {
10
+ return null;
11
+ }
12
+ /**
13
+ * Strip .js/.mjs extension if present (TypeScript uses .js in imports but
14
+ * .d.ts for types).
15
+ */ cleanPath = cleanPath.replace(/\.(m?js)$/, "");
16
+ const baseDir = path.dirname(basePath);
17
+ const resolved = path.resolve(baseDir, cleanPath);
18
+ // Try different extensions for the resolved path.
19
+ const extensions = [
20
+ ".d.ts",
21
+ ".d.mts",
22
+ ".ts",
23
+ ""
24
+ ];
25
+ for (const ext of extensions){
26
+ const tryPath = resolved + ext;
27
+ if (fs.existsSync(tryPath) && fs.statSync(tryPath).isFile()) {
28
+ return tryPath;
29
+ }
30
+ }
31
+ // Try index files in directory.
32
+ for (const ext of [
33
+ ".d.ts",
34
+ ".d.mts",
35
+ ".ts"
36
+ ]){
37
+ const indexPath = path.join(resolved, `index${ext}`);
38
+ if (fs.existsSync(indexPath) && fs.statSync(indexPath).isFile()) {
39
+ return indexPath;
40
+ }
41
+ }
42
+ return null;
43
+ }
44
+ /**
45
+ * Parse a TypeScript declaration file (.d.ts) to extract named exports.
46
+ * Recursively follows re-exports up to a maximum depth.
47
+ */ function parseDeclarationFile(filePath, content, visited = new Set(), depth = 0) {
48
+ const MAX_DEPTH = 10;
49
+ const MAX_FILES = 500;
50
+ if (depth > MAX_DEPTH || visited.size > MAX_FILES) {
51
+ return [];
52
+ }
53
+ // Avoid circular imports.
54
+ const normalizedPath = path.resolve(filePath);
55
+ if (visited.has(normalizedPath)) {
56
+ return [];
57
+ }
58
+ visited.add(normalizedPath);
59
+ const exports = [];
60
+ const seenNames = new Set();
61
+ const addExport = (name, kind)=>{
62
+ // Skip internal/private exports and TypeScript utility types.
63
+ if (name.startsWith("_") || name === "default" || seenNames.has(name)) {
64
+ return;
65
+ }
66
+ seenNames.add(name);
67
+ exports.push({
68
+ name,
69
+ kind
70
+ });
71
+ };
72
+ // Pattern for: export * from './path'.
73
+ const reExportAllPattern = /export\s+\*\s+from\s+['"]([^'"]+)['"]/g;
74
+ // Pattern for: export type { Name, Name2 } from './path' (type-only exports).
75
+ const reExportTypePattern = /export\s+type\s+\{([^}]+)\}\s+from\s+['"]([^'"]+)['"]/g;
76
+ /**
77
+ * Pattern for: export { Name, Name2 } from './path' (value exports - NOT
78
+ * type).
79
+ */ const reExportValuePattern = /export\s+\{([^}]+)\}\s+from\s+['"]([^'"]+)['"]/g;
80
+ // Pattern for: export declare function Name.
81
+ const functionPattern = /export\s+declare\s+function\s+(\w+)/g;
82
+ // Pattern for: export declare class Name.
83
+ const classPattern = /export\s+declare\s+class\s+(\w+)/g;
84
+ // Pattern for: export declare const Name.
85
+ const constPattern = /export\s+declare\s+const\s+(\w+)/g;
86
+ // Pattern for: export declare type Name.
87
+ const typePattern = /export\s+declare\s+type\s+(\w+)/g;
88
+ // Pattern for: export type Name = ... (without declare).
89
+ const typePattern2 = /export\s+type\s+(\w+)\s*[=<]/g;
90
+ // Pattern for: export type { Name } (local re-export without from).
91
+ const typeExportPattern = /export\s+type\s+\{([^}]+)\}\s*;/g;
92
+ // Pattern for: export interface Name.
93
+ const interfacePattern = /export\s+(?:declare\s+)?interface\s+(\w+)/g;
94
+ // Pattern for: export declare enum Name.
95
+ const enumPattern = /export\s+declare\s+enum\s+(\w+)/g;
96
+ // Pattern for: export { Name, Name2 } (without from - local exports).
97
+ const namedExportPattern = /export\s+\{([^}]+)\}\s*;/g;
98
+ // Pattern for: export declare const Name: ... (component style).
99
+ const componentPattern = /export\s+declare\s+const\s+(\w+)\s*:/g;
100
+ // Pattern for: export function Name (without declare).
101
+ const functionPattern2 = /export\s+function\s+(\w+)/g;
102
+ // Pattern for: export class Name (without declare).
103
+ const classPattern2 = /export\s+class\s+(\w+)/g;
104
+ // Pattern for: export const Name (without declare).
105
+ const constPattern2 = /export\s+const\s+(\w+)/g;
106
+ // Helper to extract all matches from a regex.
107
+ const extractMatches = (pattern, text)=>{
108
+ const matches = [];
109
+ let result = pattern.exec(text);
110
+ while(result !== null){
111
+ matches.push(result);
112
+ result = pattern.exec(text);
113
+ }
114
+ return matches;
115
+ };
116
+ // Handle: export * from './path' - recursively parse.
117
+ for (const match of extractMatches(reExportAllPattern, content)){
118
+ const importPath = match[1];
119
+ const resolvedPath = resolveImportPath(filePath, importPath);
120
+ if (resolvedPath) {
121
+ try {
122
+ const importedContent = fs.readFileSync(resolvedPath, "utf-8");
123
+ const importedExports = parseDeclarationFile(resolvedPath, importedContent, visited, depth + 1);
124
+ for (const exp of importedExports){
125
+ addExport(exp.name, exp.kind);
126
+ }
127
+ } catch {
128
+ // Ignore read errors.
129
+ }
130
+ }
131
+ }
132
+ // Handle: export type { Name } from './path' (type-only exports).
133
+ for (const match of extractMatches(reExportTypePattern, content)){
134
+ const exportList = match[1];
135
+ const names = exportList.split(",").map((s)=>{
136
+ const trimmed = s.trim();
137
+ // Handle "Name as Alias".
138
+ const asMatch = trimmed.match(/(\w+)\s+as\s+(\w+)/);
139
+ if (asMatch) {
140
+ return asMatch[2];
141
+ }
142
+ return trimmed;
143
+ });
144
+ for (const name of names){
145
+ if (name && /^\w+$/.test(name)) {
146
+ addExport(name, "type");
147
+ }
148
+ }
149
+ }
150
+ // Handle: export { Name } from './path' (value exports).
151
+ for (const match of extractMatches(reExportValuePattern, content)){
152
+ // Skip if this is actually a type export (already handled above).
153
+ const fullMatch = match[0];
154
+ if (fullMatch.includes("export type")) {
155
+ continue;
156
+ }
157
+ const exportList = match[1];
158
+ const names = exportList.split(",").map((s)=>{
159
+ const trimmed = s.trim();
160
+ // Handle "Name as Alias".
161
+ const asMatch = trimmed.match(/(\w+)\s+as\s+(\w+)/);
162
+ if (asMatch) {
163
+ return asMatch[2];
164
+ }
165
+ // Handle "type Name" syntax within the braces.
166
+ const typeMatch = trimmed.match(/type\s+(\w+)/);
167
+ if (typeMatch) {
168
+ /**
169
+ * This is a type being re-exported, skip it (will be handled by type
170
+ * pattern).
171
+ */ return null;
172
+ }
173
+ return trimmed;
174
+ });
175
+ for (const name of names){
176
+ if (name && /^\w+$/.test(name)) {
177
+ addExport(name, "unknown");
178
+ }
179
+ }
180
+ }
181
+ // Extract functions.
182
+ for (const match of extractMatches(functionPattern, content)){
183
+ addExport(match[1], "function");
184
+ }
185
+ for (const match of extractMatches(functionPattern2, content)){
186
+ addExport(match[1], "function");
187
+ }
188
+ // Extract classes.
189
+ for (const match of extractMatches(classPattern, content)){
190
+ addExport(match[1], "class");
191
+ }
192
+ for (const match of extractMatches(classPattern2, content)){
193
+ addExport(match[1], "class");
194
+ }
195
+ // Extract const (includes React components).
196
+ for (const match of extractMatches(constPattern, content)){
197
+ addExport(match[1], "const");
198
+ }
199
+ for (const match of extractMatches(constPattern2, content)){
200
+ addExport(match[1], "const");
201
+ }
202
+ // Also catch component-style declarations.
203
+ for (const match of extractMatches(componentPattern, content)){
204
+ addExport(match[1], "const");
205
+ }
206
+ // Extract types (declare type).
207
+ for (const match of extractMatches(typePattern, content)){
208
+ addExport(match[1], "type");
209
+ }
210
+ // Extract types (export type X =).
211
+ for (const match of extractMatches(typePattern2, content)){
212
+ addExport(match[1], "type");
213
+ }
214
+ // Extract type exports (export type { X }).
215
+ for (const match of extractMatches(typeExportPattern, content)){
216
+ const exportList = match[1];
217
+ const names = exportList.split(",").map((s)=>s.trim());
218
+ for (const name of names){
219
+ if (name && /^\w+$/.test(name)) {
220
+ addExport(name, "type");
221
+ }
222
+ }
223
+ }
224
+ // Extract interfaces.
225
+ for (const match of extractMatches(interfacePattern, content)){
226
+ addExport(match[1], "interface");
227
+ }
228
+ // Extract enums.
229
+ for (const match of extractMatches(enumPattern, content)){
230
+ addExport(match[1], "enum");
231
+ }
232
+ /**
233
+ * Extract named exports from export { ... } (without from - these are local
234
+ * exports).
235
+ */ for (const match of extractMatches(namedExportPattern, content)){
236
+ const exportList = match[1];
237
+ // Skip if this looks like a type export (already handled above).
238
+ if (content.substring(match.index - 5, match.index).includes("type")) {
239
+ continue;
240
+ }
241
+ const names = exportList.split(",").map((s)=>{
242
+ const trimmed = s.trim();
243
+ const asMatch = trimmed.match(/(\w+)\s+as\s+(\w+)/);
244
+ if (asMatch) {
245
+ return asMatch[2];
246
+ }
247
+ const typeMatch = trimmed.match(/type\s+(\w+)/);
248
+ if (typeMatch) {
249
+ return typeMatch[1];
250
+ }
251
+ return trimmed;
252
+ });
253
+ for (const name of names){
254
+ if (name && /^\w+$/.test(name)) {
255
+ addExport(name, "unknown");
256
+ }
257
+ }
258
+ }
259
+ return exports;
260
+ }
261
+ /**
262
+ * Get named exports from an installed package by parsing its type definitions.
263
+ *
264
+ * @param tmpDir - The temporary directory where the package is installed
265
+ * @param packageName - The package name (e.g., "@mantine/core")
266
+ * @returns ParsedExports containing the list of exports and count
267
+ *
268
+ */ export function getNamedExports(tmpDir, packageName) {
269
+ const packageDir = path.join(tmpDir, "node_modules", packageName);
270
+ const emptyResult = {
271
+ exports: [],
272
+ count: 0,
273
+ runtimeExports: [],
274
+ runtimeCount: 0
275
+ };
276
+ // Try to read package.json to find the types entry.
277
+ const pkgJsonPath = path.join(packageDir, "package.json");
278
+ if (!fs.existsSync(pkgJsonPath)) {
279
+ return emptyResult;
280
+ }
281
+ let typesEntry;
282
+ try {
283
+ const pkgJson = JSON.parse(fs.readFileSync(pkgJsonPath, "utf-8"));
284
+ // Look for types in order of preference.
285
+ typesEntry = pkgJson.types || pkgJson.typings || pkgJson.exports?.["."]?.types || typeof pkgJson.exports?.["."] === "object" && pkgJson.exports["."].types;
286
+ // If no types field, try common patterns.
287
+ if (!typesEntry) {
288
+ const commonPaths = [
289
+ "dist/index.d.ts",
290
+ "lib/index.d.ts",
291
+ "index.d.ts",
292
+ "types/index.d.ts"
293
+ ];
294
+ for (const tryPath of commonPaths){
295
+ if (fs.existsSync(path.join(packageDir, tryPath))) {
296
+ typesEntry = tryPath;
297
+ break;
298
+ }
299
+ }
300
+ }
301
+ } catch {
302
+ return emptyResult;
303
+ }
304
+ if (!typesEntry) {
305
+ return emptyResult;
306
+ }
307
+ // Read and parse the types file.
308
+ const typesPath = path.join(packageDir, typesEntry);
309
+ if (!fs.existsSync(typesPath)) {
310
+ return emptyResult;
311
+ }
312
+ try {
313
+ const content = fs.readFileSync(typesPath, "utf-8");
314
+ const exports = parseDeclarationFile(typesPath, content);
315
+ // Sort exports by name for consistent output.
316
+ exports.sort((a, b)=>a.name.localeCompare(b.name));
317
+ // Filter to runtime-only exports (exclude types and interfaces).
318
+ const runtimeExports = exports.filter((e)=>e.kind !== "type" && e.kind !== "interface");
319
+ return {
320
+ exports,
321
+ count: exports.length,
322
+ runtimeExports,
323
+ runtimeCount: runtimeExports.length
324
+ };
325
+ } catch {
326
+ return emptyResult;
327
+ }
328
+ }
329
+
330
+ //# sourceMappingURL=exports.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/exports.ts"],"sourcesContent":["import fs from \"node:fs\";\nimport path from \"node:path\";\n\n/**\n * Represents a named export from a package.\n */\nexport type NamedExport = {\n\t/**\n\t * The export name (e.g., \"Button\", \"useState\").\n\t */\n\tname: string;\n\t/**\n\t * The type of export.\n\t */\n\tkind:\n\t\t| \"function\"\n\t\t| \"class\"\n\t\t| \"const\"\n\t\t| \"type\"\n\t\t| \"interface\"\n\t\t| \"enum\"\n\t\t| \"unknown\";\n};\n\n/**\n * Result from parsing package exports.\n */\nexport type ParsedExports = {\n\t/**\n\t * Array of all named exports found in the package (including types).\n\t */\n\texports: NamedExport[];\n\t/**\n\t * Total count of all named exports (including types).\n\t */\n\tcount: number;\n\t/**\n\t * Array of runtime exports only (excluding types and interfaces).\n\t */\n\truntimeExports: NamedExport[];\n\t/**\n\t * Count of runtime exports only (functions, classes, const, enums).\n\t */\n\truntimeCount: number;\n};\n\n/**\n * Resolve a relative import path to an absolute file path.\n */\nfunction resolveImportPath(\n\tbasePath: string,\n\timportPath: string,\n): string | null {\n\t// Remove quotes and trim.\n\tlet cleanPath = importPath.replace(/['\"]/g, \"\").trim();\n\n\t// Only handle relative imports.\n\tif (!cleanPath.startsWith(\".\")) {\n\t\treturn null;\n\t}\n\n\t/**\n\t * Strip .js/.mjs extension if present (TypeScript uses .js in imports but\n\t * .d.ts for types).\n\t */\n\tcleanPath = cleanPath.replace(/\\.(m?js)$/, \"\");\n\n\tconst baseDir = path.dirname(basePath);\n\tconst resolved = path.resolve(baseDir, cleanPath);\n\n\t// Try different extensions for the resolved path.\n\tconst extensions = [\".d.ts\", \".d.mts\", \".ts\", \"\"];\n\tfor (const ext of extensions) {\n\t\tconst tryPath = resolved + ext;\n\t\tif (fs.existsSync(tryPath) && fs.statSync(tryPath).isFile()) {\n\t\t\treturn tryPath;\n\t\t}\n\t}\n\n\t// Try index files in directory.\n\tfor (const ext of [\".d.ts\", \".d.mts\", \".ts\"]) {\n\t\tconst indexPath = path.join(resolved, `index${ext}`);\n\t\tif (fs.existsSync(indexPath) && fs.statSync(indexPath).isFile()) {\n\t\t\treturn indexPath;\n\t\t}\n\t}\n\n\treturn null;\n}\n\n/**\n * Parse a TypeScript declaration file (.d.ts) to extract named exports.\n * Recursively follows re-exports up to a maximum depth.\n */\nfunction parseDeclarationFile(\n\tfilePath: string,\n\tcontent: string,\n\tvisited: Set<string> = new Set(),\n\tdepth = 0,\n): NamedExport[] {\n\tconst MAX_DEPTH = 10;\n\tconst MAX_FILES = 500;\n\n\tif (depth > MAX_DEPTH || visited.size > MAX_FILES) {\n\t\treturn [];\n\t}\n\n\t// Avoid circular imports.\n\tconst normalizedPath = path.resolve(filePath);\n\tif (visited.has(normalizedPath)) {\n\t\treturn [];\n\t}\n\tvisited.add(normalizedPath);\n\n\tconst exports: NamedExport[] = [];\n\tconst seenNames = new Set<string>();\n\n\tconst addExport = (name: string, kind: NamedExport[\"kind\"]) => {\n\t\t// Skip internal/private exports and TypeScript utility types.\n\t\tif (name.startsWith(\"_\") || name === \"default\" || seenNames.has(name)) {\n\t\t\treturn;\n\t\t}\n\t\tseenNames.add(name);\n\t\texports.push({ name, kind });\n\t};\n\n\t// Pattern for: export * from './path'.\n\tconst reExportAllPattern = /export\\s+\\*\\s+from\\s+['\"]([^'\"]+)['\"]/g;\n\n\t// Pattern for: export type { Name, Name2 } from './path' (type-only exports).\n\tconst reExportTypePattern =\n\t\t/export\\s+type\\s+\\{([^}]+)\\}\\s+from\\s+['\"]([^'\"]+)['\"]/g;\n\n\t/**\n\t * Pattern for: export { Name, Name2 } from './path' (value exports - NOT\n\t * type).\n\t */\n\tconst reExportValuePattern =\n\t\t/export\\s+\\{([^}]+)\\}\\s+from\\s+['\"]([^'\"]+)['\"]/g;\n\n\t// Pattern for: export declare function Name.\n\tconst functionPattern = /export\\s+declare\\s+function\\s+(\\w+)/g;\n\t// Pattern for: export declare class Name.\n\tconst classPattern = /export\\s+declare\\s+class\\s+(\\w+)/g;\n\t// Pattern for: export declare const Name.\n\tconst constPattern = /export\\s+declare\\s+const\\s+(\\w+)/g;\n\t// Pattern for: export declare type Name.\n\tconst typePattern = /export\\s+declare\\s+type\\s+(\\w+)/g;\n\t// Pattern for: export type Name = ... (without declare).\n\tconst typePattern2 = /export\\s+type\\s+(\\w+)\\s*[=<]/g;\n\t// Pattern for: export type { Name } (local re-export without from).\n\tconst typeExportPattern = /export\\s+type\\s+\\{([^}]+)\\}\\s*;/g;\n\t// Pattern for: export interface Name.\n\tconst interfacePattern = /export\\s+(?:declare\\s+)?interface\\s+(\\w+)/g;\n\t// Pattern for: export declare enum Name.\n\tconst enumPattern = /export\\s+declare\\s+enum\\s+(\\w+)/g;\n\t// Pattern for: export { Name, Name2 } (without from - local exports).\n\tconst namedExportPattern = /export\\s+\\{([^}]+)\\}\\s*;/g;\n\t// Pattern for: export declare const Name: ... (component style).\n\tconst componentPattern = /export\\s+declare\\s+const\\s+(\\w+)\\s*:/g;\n\t// Pattern for: export function Name (without declare).\n\tconst functionPattern2 = /export\\s+function\\s+(\\w+)/g;\n\t// Pattern for: export class Name (without declare).\n\tconst classPattern2 = /export\\s+class\\s+(\\w+)/g;\n\t// Pattern for: export const Name (without declare).\n\tconst constPattern2 = /export\\s+const\\s+(\\w+)/g;\n\n\t// Helper to extract all matches from a regex.\n\tconst extractMatches = (pattern: RegExp, text: string): RegExpExecArray[] => {\n\t\tconst matches: RegExpExecArray[] = [];\n\t\tlet result = pattern.exec(text);\n\t\twhile (result !== null) {\n\t\t\tmatches.push(result);\n\t\t\tresult = pattern.exec(text);\n\t\t}\n\t\treturn matches;\n\t};\n\n\t// Handle: export * from './path' - recursively parse.\n\tfor (const match of extractMatches(reExportAllPattern, content)) {\n\t\tconst importPath = match[1];\n\t\tconst resolvedPath = resolveImportPath(filePath, importPath);\n\t\tif (resolvedPath) {\n\t\t\ttry {\n\t\t\t\tconst importedContent = fs.readFileSync(resolvedPath, \"utf-8\");\n\t\t\t\tconst importedExports = parseDeclarationFile(\n\t\t\t\t\tresolvedPath,\n\t\t\t\t\timportedContent,\n\t\t\t\t\tvisited,\n\t\t\t\t\tdepth + 1,\n\t\t\t\t);\n\t\t\t\tfor (const exp of importedExports) {\n\t\t\t\t\taddExport(exp.name, exp.kind);\n\t\t\t\t}\n\t\t\t} catch {\n\t\t\t\t// Ignore read errors.\n\t\t\t}\n\t\t}\n\t}\n\n\t// Handle: export type { Name } from './path' (type-only exports).\n\tfor (const match of extractMatches(reExportTypePattern, content)) {\n\t\tconst exportList = match[1];\n\t\tconst names = exportList.split(\",\").map((s) => {\n\t\t\tconst trimmed = s.trim();\n\t\t\t// Handle \"Name as Alias\".\n\t\t\tconst asMatch = trimmed.match(/(\\w+)\\s+as\\s+(\\w+)/);\n\t\t\tif (asMatch) {\n\t\t\t\treturn asMatch[2];\n\t\t\t}\n\t\t\treturn trimmed;\n\t\t});\n\n\t\tfor (const name of names) {\n\t\t\tif (name && /^\\w+$/.test(name)) {\n\t\t\t\taddExport(name, \"type\");\n\t\t\t}\n\t\t}\n\t}\n\n\t// Handle: export { Name } from './path' (value exports).\n\tfor (const match of extractMatches(reExportValuePattern, content)) {\n\t\t// Skip if this is actually a type export (already handled above).\n\t\tconst fullMatch = match[0];\n\t\tif (fullMatch.includes(\"export type\")) {\n\t\t\tcontinue;\n\t\t}\n\n\t\tconst exportList = match[1];\n\t\tconst names = exportList.split(\",\").map((s) => {\n\t\t\tconst trimmed = s.trim();\n\t\t\t// Handle \"Name as Alias\".\n\t\t\tconst asMatch = trimmed.match(/(\\w+)\\s+as\\s+(\\w+)/);\n\t\t\tif (asMatch) {\n\t\t\t\treturn asMatch[2];\n\t\t\t}\n\t\t\t// Handle \"type Name\" syntax within the braces.\n\t\t\tconst typeMatch = trimmed.match(/type\\s+(\\w+)/);\n\t\t\tif (typeMatch) {\n\t\t\t\t/**\n\t\t\t\t * This is a type being re-exported, skip it (will be handled by type\n\t\t\t\t * pattern).\n\t\t\t\t */\n\t\t\t\treturn null;\n\t\t\t}\n\t\t\treturn trimmed;\n\t\t});\n\n\t\tfor (const name of names) {\n\t\t\tif (name && /^\\w+$/.test(name)) {\n\t\t\t\taddExport(name, \"unknown\");\n\t\t\t}\n\t\t}\n\t}\n\n\t// Extract functions.\n\tfor (const match of extractMatches(functionPattern, content)) {\n\t\taddExport(match[1], \"function\");\n\t}\n\tfor (const match of extractMatches(functionPattern2, content)) {\n\t\taddExport(match[1], \"function\");\n\t}\n\n\t// Extract classes.\n\tfor (const match of extractMatches(classPattern, content)) {\n\t\taddExport(match[1], \"class\");\n\t}\n\tfor (const match of extractMatches(classPattern2, content)) {\n\t\taddExport(match[1], \"class\");\n\t}\n\n\t// Extract const (includes React components).\n\tfor (const match of extractMatches(constPattern, content)) {\n\t\taddExport(match[1], \"const\");\n\t}\n\tfor (const match of extractMatches(constPattern2, content)) {\n\t\taddExport(match[1], \"const\");\n\t}\n\n\t// Also catch component-style declarations.\n\tfor (const match of extractMatches(componentPattern, content)) {\n\t\taddExport(match[1], \"const\");\n\t}\n\n\t// Extract types (declare type).\n\tfor (const match of extractMatches(typePattern, content)) {\n\t\taddExport(match[1], \"type\");\n\t}\n\n\t// Extract types (export type X =).\n\tfor (const match of extractMatches(typePattern2, content)) {\n\t\taddExport(match[1], \"type\");\n\t}\n\n\t// Extract type exports (export type { X }).\n\tfor (const match of extractMatches(typeExportPattern, content)) {\n\t\tconst exportList = match[1];\n\t\tconst names = exportList.split(\",\").map((s) => s.trim());\n\t\tfor (const name of names) {\n\t\t\tif (name && /^\\w+$/.test(name)) {\n\t\t\t\taddExport(name, \"type\");\n\t\t\t}\n\t\t}\n\t}\n\n\t// Extract interfaces.\n\tfor (const match of extractMatches(interfacePattern, content)) {\n\t\taddExport(match[1], \"interface\");\n\t}\n\n\t// Extract enums.\n\tfor (const match of extractMatches(enumPattern, content)) {\n\t\taddExport(match[1], \"enum\");\n\t}\n\n\t/**\n\t * Extract named exports from export { ... } (without from - these are local\n\t * exports).\n\t */\n\tfor (const match of extractMatches(namedExportPattern, content)) {\n\t\tconst exportList = match[1];\n\t\t// Skip if this looks like a type export (already handled above).\n\t\tif (content.substring(match.index - 5, match.index).includes(\"type\")) {\n\t\t\tcontinue;\n\t\t}\n\t\tconst names = exportList.split(\",\").map((s) => {\n\t\t\tconst trimmed = s.trim();\n\t\t\tconst asMatch = trimmed.match(/(\\w+)\\s+as\\s+(\\w+)/);\n\t\t\tif (asMatch) {\n\t\t\t\treturn asMatch[2];\n\t\t\t}\n\t\t\tconst typeMatch = trimmed.match(/type\\s+(\\w+)/);\n\t\t\tif (typeMatch) {\n\t\t\t\treturn typeMatch[1];\n\t\t\t}\n\t\t\treturn trimmed;\n\t\t});\n\n\t\tfor (const name of names) {\n\t\t\tif (name && /^\\w+$/.test(name)) {\n\t\t\t\taddExport(name, \"unknown\");\n\t\t\t}\n\t\t}\n\t}\n\n\treturn exports;\n}\n\n/**\n * Get named exports from an installed package by parsing its type definitions.\n *\n * @param tmpDir - The temporary directory where the package is installed\n * @param packageName - The package name (e.g., \"@mantine/core\")\n * @returns ParsedExports containing the list of exports and count\n *\n */\nexport function getNamedExports(\n\ttmpDir: string,\n\tpackageName: string,\n): ParsedExports {\n\tconst packageDir = path.join(tmpDir, \"node_modules\", packageName);\n\n\tconst emptyResult: ParsedExports = {\n\t\texports: [],\n\t\tcount: 0,\n\t\truntimeExports: [],\n\t\truntimeCount: 0,\n\t};\n\n\t// Try to read package.json to find the types entry.\n\tconst pkgJsonPath = path.join(packageDir, \"package.json\");\n\tif (!fs.existsSync(pkgJsonPath)) {\n\t\treturn emptyResult;\n\t}\n\n\tlet typesEntry: string | undefined;\n\ttry {\n\t\tconst pkgJson = JSON.parse(fs.readFileSync(pkgJsonPath, \"utf-8\"));\n\n\t\t// Look for types in order of preference.\n\t\ttypesEntry =\n\t\t\tpkgJson.types ||\n\t\t\tpkgJson.typings ||\n\t\t\tpkgJson.exports?.[\".\"]?.types ||\n\t\t\t(typeof pkgJson.exports?.[\".\"] === \"object\" &&\n\t\t\t\tpkgJson.exports[\".\"].types);\n\n\t\t// If no types field, try common patterns.\n\t\tif (!typesEntry) {\n\t\t\tconst commonPaths = [\n\t\t\t\t\"dist/index.d.ts\",\n\t\t\t\t\"lib/index.d.ts\",\n\t\t\t\t\"index.d.ts\",\n\t\t\t\t\"types/index.d.ts\",\n\t\t\t];\n\t\t\tfor (const tryPath of commonPaths) {\n\t\t\t\tif (fs.existsSync(path.join(packageDir, tryPath))) {\n\t\t\t\t\ttypesEntry = tryPath;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t} catch {\n\t\treturn emptyResult;\n\t}\n\n\tif (!typesEntry) {\n\t\treturn emptyResult;\n\t}\n\n\t// Read and parse the types file.\n\tconst typesPath = path.join(packageDir, typesEntry);\n\tif (!fs.existsSync(typesPath)) {\n\t\treturn emptyResult;\n\t}\n\n\ttry {\n\t\tconst content = fs.readFileSync(typesPath, \"utf-8\");\n\t\tconst exports = parseDeclarationFile(typesPath, content);\n\n\t\t// Sort exports by name for consistent output.\n\t\texports.sort((a, b) => a.name.localeCompare(b.name));\n\n\t\t// Filter to runtime-only exports (exclude types and interfaces).\n\t\tconst runtimeExports = exports.filter(\n\t\t\t(e) => e.kind !== \"type\" && e.kind !== \"interface\",\n\t\t);\n\n\t\treturn {\n\t\t\texports,\n\t\t\tcount: exports.length,\n\t\t\truntimeExports,\n\t\t\truntimeCount: runtimeExports.length,\n\t\t};\n\t} catch {\n\t\treturn emptyResult;\n\t}\n}\n"],"names":["fs","path","resolveImportPath","basePath","importPath","cleanPath","replace","trim","startsWith","baseDir","dirname","resolved","resolve","extensions","ext","tryPath","existsSync","statSync","isFile","indexPath","join","parseDeclarationFile","filePath","content","visited","Set","depth","MAX_DEPTH","MAX_FILES","size","normalizedPath","has","add","exports","seenNames","addExport","name","kind","push","reExportAllPattern","reExportTypePattern","reExportValuePattern","functionPattern","classPattern","constPattern","typePattern","typePattern2","typeExportPattern","interfacePattern","enumPattern","namedExportPattern","componentPattern","functionPattern2","classPattern2","constPattern2","extractMatches","pattern","text","matches","result","exec","match","resolvedPath","importedContent","readFileSync","importedExports","exp","exportList","names","split","map","s","trimmed","asMatch","test","fullMatch","includes","typeMatch","substring","index","getNamedExports","tmpDir","packageName","packageDir","emptyResult","count","runtimeExports","runtimeCount","pkgJsonPath","typesEntry","pkgJson","JSON","parse","types","typings","commonPaths","typesPath","sort","a","b","localeCompare","filter","e","length"],"mappings":"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"}
package/dist/index.d.ts CHANGED
@@ -27,7 +27,7 @@ export type GetBundleStatsOptions = {
27
27
  */
28
28
  external?: string[];
29
29
  /**
30
- * Bundle everything including default externals (react, react-dom).
30
+ * Bundle everything including dependencies that would normally be external.
31
31
  */
32
32
  noExternal?: boolean;
33
33
  /**
@@ -100,6 +100,11 @@ export type BundleStats = {
100
100
  * Whether the result was retrieved from cache.
101
101
  */
102
102
  fromCache: boolean;
103
+ /**
104
+ * Total number of named exports in the package (when analyzing entire
105
+ * package).
106
+ */
107
+ namedExportCount: number;
103
108
  };
104
109
  /**
105
110
  * Options for getting bundle size trend across versions.
@@ -122,7 +127,7 @@ export type GetBundleTrendOptions = {
122
127
  */
123
128
  external?: string[];
124
129
  /**
125
- * Bundle everything including default externals (react, react-dom).
130
+ * Bundle everything including dependencies that would normally be external.
126
131
  */
127
132
  noExternal?: boolean;
128
133
  /**
@@ -303,6 +308,80 @@ export declare function getBundleTrend(options: GetBundleTrendOptions): Promise<
303
308
  *
304
309
  */
305
310
  export declare function getPackageVersions(options: GetPackageVersionsOptions): Promise<PackageVersions>;
311
+ /**
312
+ * Options for getting package exports.
313
+ */
314
+ export type GetPackageExportsOptions = {
315
+ /**
316
+ * Package name with optional version (e.g., "@mantine/core" or
317
+ * "@mantine/core@7.0.0").
318
+ */
319
+ package: string;
320
+ /**
321
+ * Custom npm registry URL.
322
+ */
323
+ registry?: string;
324
+ };
325
+ /**
326
+ * A named export from a package.
327
+ */
328
+ export type PackageExport = {
329
+ /**
330
+ * The export name (e.g., "Button", "useState").
331
+ */
332
+ name: string;
333
+ /**
334
+ * The type of export.
335
+ */
336
+ kind: "function" | "class" | "const" | "type" | "interface" | "enum" | "unknown";
337
+ };
338
+ /**
339
+ * Result from getPackageExports.
340
+ */
341
+ export type PackageExports = {
342
+ /**
343
+ * Package name.
344
+ */
345
+ packageName: string;
346
+ /**
347
+ * Resolved package version.
348
+ */
349
+ packageVersion: string;
350
+ /**
351
+ * Array of all named exports found in the package (including types).
352
+ */
353
+ exports: PackageExport[];
354
+ /**
355
+ * Total count of all named exports (including types).
356
+ */
357
+ count: number;
358
+ /**
359
+ * Array of runtime exports only (excluding types and interfaces). These are
360
+ * the exports that can actually be imported at runtime.
361
+ */
362
+ runtimeExports: PackageExport[];
363
+ /**
364
+ * Count of runtime exports only (functions, classes, const, enums).
365
+ */
366
+ runtimeCount: number;
367
+ };
368
+ /**
369
+ * Get the named exports of an npm package by analyzing its type definitions.
370
+ *
371
+ * @example
372
+ * ```js
373
+ * import { getPackageExports } from "@node-cli/bundlecheck";
374
+ *
375
+ * const { exports, count } = await getPackageExports({
376
+ * package: "@mantine/core@7.0.0",
377
+ * });
378
+ *
379
+ * console.log(`Found ${count} exports`);
380
+ * console.log(exports.map(e => e.name)); // ["Accordion", "ActionIcon", "Alert", ...]
381
+ * ```
382
+ *
383
+ */
384
+ export declare function getPackageExports(options: GetPackageExportsOptions): Promise<PackageExports>;
306
385
  /**
307
386
  * =============================================================================
308
387
  * Re-exports for advanced usage
package/dist/index.js CHANGED
@@ -3,7 +3,7 @@
3
3
  *
4
4
  * Programmatic interface for analyzing npm package bundle sizes.
5
5
  *
6
- */ import { checkBundleSize, formatBytes, getExternals, parsePackageSpecifier } from "./bundler.js";
6
+ */ import { checkBundleSize, formatBytes, parsePackageSpecifier } from "./bundler.js";
7
7
  import { getCachedResult, normalizeCacheKey, setCachedResult } from "./cache.js";
8
8
  import { normalizePlatform, TREND_VERSION_COUNT } from "./defaults.js";
9
9
  import { analyzeTrend, selectTrendVersions } from "./trend.js";
@@ -42,16 +42,18 @@ import { fetchPackageVersions as fetchVersions } from "./versions.js";
42
42
  });
43
43
  resolvedVersion = tags.latest || requestedVersion;
44
44
  }
45
- // Compute externals for cache key.
46
- const externals = getExternals(baseName, additionalExternals, noExternal);
47
- // Build cache key.
48
- const cacheKey = normalizeCacheKey({
45
+ /**
46
+ * Build cache key.
47
+ * NOTE: We use additionalExternals here, not computed externals, because the default externals (react, react-dom) are determined at bundle time based on the package's dependencies.
48
+ * The cache key captures the user's intent (additionalExternals + noExternal
49
+ * flag), and the actual externals are stored in the cached result.
50
+ */ const cacheKey = normalizeCacheKey({
49
51
  packageName: baseName,
50
52
  version: resolvedVersion,
51
53
  exports: exportsList,
52
54
  platform,
53
55
  gzipLevel,
54
- externals,
56
+ externals: additionalExternals || [],
55
57
  noExternal: noExternal ?? false
56
58
  });
57
59
  // Check cache (unless force is set).
@@ -190,6 +192,60 @@ import { fetchPackageVersions as fetchVersions } from "./versions.js";
190
192
  tags: result.tags
191
193
  };
192
194
  }
195
+ /**
196
+ * Get the named exports of an npm package by analyzing its type definitions.
197
+ *
198
+ * @example
199
+ * ```js
200
+ * import { getPackageExports } from "@node-cli/bundlecheck";
201
+ *
202
+ * const { exports, count } = await getPackageExports({
203
+ * package: "@mantine/core@7.0.0",
204
+ * });
205
+ *
206
+ * console.log(`Found ${count} exports`);
207
+ * console.log(exports.map(e => e.name)); // ["Accordion", "ActionIcon", "Alert", ...]
208
+ * ```
209
+ *
210
+ */ export async function getPackageExports(options) {
211
+ const { package: packageName, registry } = options;
212
+ /**
213
+ * Import the install utilities from bundler (we'll use a minimal bundle
214
+ * check).
215
+ */ const { name: baseName, version: requestedVersion } = parsePackageSpecifier(packageName);
216
+ // Resolve "latest" to actual version.
217
+ let resolvedVersion = requestedVersion;
218
+ if (requestedVersion === "latest") {
219
+ const { tags } = await fetchVersions({
220
+ packageName: baseName,
221
+ registry
222
+ });
223
+ resolvedVersion = tags.latest || requestedVersion;
224
+ }
225
+ /**
226
+ * Use a minimal bundle check to install the package and get exports We'll
227
+ * leverage the existing infrastructure.
228
+ */ const { installPackage } = await import("./exports-installer.js");
229
+ const { version, exports, runtimeExports } = await installPackage({
230
+ packageName: baseName,
231
+ version: resolvedVersion,
232
+ registry
233
+ });
234
+ return {
235
+ packageName: baseName,
236
+ packageVersion: version,
237
+ exports: exports.map((e)=>({
238
+ name: e.name,
239
+ kind: e.kind
240
+ })),
241
+ count: exports.length,
242
+ runtimeExports: runtimeExports.map((e)=>({
243
+ name: e.name,
244
+ kind: e.kind
245
+ })),
246
+ runtimeCount: runtimeExports.length
247
+ };
248
+ }
193
249
  /**
194
250
  * =============================================================================
195
251
  * Re-exports for advanced usage
@@ -222,7 +278,8 @@ import { fetchPackageVersions as fetchVersions } from "./versions.js";
222
278
  platform: result.platform,
223
279
  rawSizeFormatted: formatBytes(result.rawSize),
224
280
  gzipSizeFormatted: result.gzipSize !== null ? formatBytes(result.gzipSize) : null,
225
- fromCache
281
+ fromCache,
282
+ namedExportCount: result.namedExportCount
226
283
  };
227
284
  }
228
285