@node-cli/bundlecheck 1.4.0 → 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.
- package/README.md +58 -7
- package/dist/bundlecheck.js +10 -1
- package/dist/bundlecheck.js.map +1 -1
- package/dist/bundler.d.ts +19 -2
- package/dist/bundler.js +179 -30
- package/dist/bundler.js.map +1 -1
- package/dist/cache.js +29 -6
- package/dist/cache.js.map +1 -1
- package/dist/defaults.d.ts +10 -0
- package/dist/defaults.js +18 -1
- package/dist/defaults.js.map +1 -1
- package/dist/exports-installer.d.ts +19 -0
- package/dist/exports-installer.js +115 -0
- package/dist/exports-installer.js.map +1 -0
- package/dist/exports.d.ts +43 -0
- package/dist/exports.js +330 -0
- package/dist/exports.js.map +1 -0
- package/dist/index.d.ts +398 -3
- package/dist/index.js +64 -7
- package/dist/index.js.map +1 -1
- package/dist/parse.js +2 -2
- package/dist/parse.js.map +1 -1
- package/package.json +3 -4
package/dist/bundler.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/bundler.ts"],"sourcesContent":["import { execSync } from \"node:child_process\";\nimport fs from \"node:fs\";\nimport os from \"node:os\";\nimport path from \"node:path\";\nimport { promisify } from \"node:util\";\nimport zlib from \"node:zlib\";\nimport * as esbuild from \"esbuild\";\nimport { DEFAULT_EXTERNALS } from \"./defaults.js\";\n\nconst gzipAsync = promisify(zlib.gzip);\n\n/**\n * Escape special regex characters in a string.\n */\nfunction escapeRegExp(str: string): string {\n\treturn str.replace(/[.*+?^${}()|[\\]\\\\]/g, \"\\\\$&\");\n}\n\nexport type ParsedPackage = {\n\tname: string;\n\tversion: string;\n\tsubpath?: string;\n};\n\n/**\n * Parse a package specifier to extract name, version, and subpath.\n * Handles:\n * - @scope/package@1.0.0\n * - @scope/package/subpath@1.0.0\n * - @scope/package/subpath\n * - package/subpath@1.0.0\n */\nexport function parsePackageSpecifier(specifier: string): ParsedPackage {\n\tlet workingSpec = specifier;\n\tlet version = \"latest\";\n\n\t// Handle scoped packages (@scope/name...)\n\tif (workingSpec.startsWith(\"@\")) {\n\t\t// Find the second @ which would separate version.\n\t\tconst secondAtIndex = workingSpec.indexOf(\"@\", 1);\n\t\tif (secondAtIndex !== -1) {\n\t\t\tversion = workingSpec.substring(secondAtIndex + 1);\n\t\t\tworkingSpec = workingSpec.substring(0, secondAtIndex);\n\t\t}\n\n\t\t/**\n\t\t * Now workingSpec is like @scope/name or @scope/name/subpath Split by / and\n\t\t * check if there are more than 2 parts.\n\t\t */\n\t\tconst parts = workingSpec.split(\"/\");\n\t\tif (parts.length > 2) {\n\t\t\t// Has subpath: @scope/name/subpath/more.\n\t\t\tconst name = `${parts[0]}/${parts[1]}`;\n\t\t\tconst subpath = parts.slice(2).join(\"/\");\n\t\t\treturn { name, version, subpath };\n\t\t}\n\t\t// No subpath: @scope/name.\n\t\treturn { name: workingSpec, version };\n\t}\n\n\t// Handle non-scoped packages (name@version or name/subpath@version).\n\tconst atIndex = workingSpec.indexOf(\"@\");\n\tif (atIndex !== -1) {\n\t\tversion = workingSpec.substring(atIndex + 1);\n\t\tworkingSpec = workingSpec.substring(0, atIndex);\n\t}\n\n\t// Check for subpath in non-scoped packages.\n\tconst slashIndex = workingSpec.indexOf(\"/\");\n\tif (slashIndex !== -1) {\n\t\tconst name = workingSpec.substring(0, slashIndex);\n\t\tconst subpath = workingSpec.substring(slashIndex + 1);\n\t\treturn { name, version, subpath };\n\t}\n\n\treturn { name: workingSpec, version };\n}\n\nexport type BundleOptions = {\n\tpackageName: string;\n\texports?: string[];\n\tadditionalExternals?: string[];\n\tnoExternal?: boolean;\n\tgzipLevel?: number;\n\tregistry?: string;\n\t/**\n\t * Target platform. If undefined, auto-detects from package.json engines.\n\t */\n\tplatform?: \"browser\" | \"node\";\n};\n\nexport type BundleResult = {\n\tpackageName: string;\n\tpackageVersion: string;\n\texports: string[];\n\trawSize: number;\n\t/**\n\t * Gzip size in bytes, or null for node platform (gzip not applicable).\n\t */\n\tgzipSize: number | null;\n\tgzipLevel: number;\n\texternals: string[];\n\tdependencies: string[];\n\tplatform: \"browser\" | \"node\";\n};\n\n/**\n * Format bytes to human-readable string.\n */\nexport function formatBytes(bytes: number): string {\n\tif (bytes === 0) {\n\t\treturn \"0 B\";\n\t}\n\tconst k = 1024;\n\tconst sizes = [\"B\", \"kB\", \"MB\", \"GB\"];\n\tconst i = Math.floor(Math.log(bytes) / Math.log(k));\n\treturn `${Number.parseFloat((bytes / k ** i).toFixed(2))} ${sizes[i]}`;\n}\n\n/**\n * Create a temporary directory for bundling.\n */\nfunction createTempDir(): string {\n\tconst tmpDir = path.join(os.tmpdir(), `bundlecheck-${Date.now()}`);\n\tfs.mkdirSync(tmpDir, { recursive: true });\n\treturn tmpDir;\n}\n\n/**\n * Clean up temporary directory.\n */\nfunction cleanupTempDir(tmpDir: string): void {\n\ttry {\n\t\tfs.rmSync(tmpDir, { recursive: true, force: true });\n\t} catch {\n\t\t// Ignore cleanup errors.\n\t}\n}\n\n/**\n * Check if pnpm is available.\n */\nfunction isPnpmAvailable(): boolean {\n\ttry {\n\t\texecSync(\"pnpm --version\", { stdio: \"pipe\" });\n\t\treturn true;\n\t} catch {\n\t\treturn false;\n\t}\n}\n\n// Cache the result of pnpm availability check.\nlet usePnpm: boolean | null = null;\n\n/**\n * Validate and sanitize a registry URL to prevent command injection.\n * @param registry - The registry URL to validate\n * @returns The sanitized URL or undefined if invalid\n * @throws Error if the URL is invalid or contains potentially malicious characters\n */\nfunction validateRegistryUrl(registry: string): string {\n\t// Parse as URL to validate format.\n\tlet url: URL;\n\ttry {\n\t\turl = new URL(registry);\n\t} catch {\n\t\tthrow new Error(\n\t\t\t`Invalid registry URL: ${registry}. Must be a valid URL (e.g., https://registry.example.com)`,\n\t\t);\n\t}\n\n\t// Only allow http and https protocols.\n\tif (url.protocol !== \"http:\" && url.protocol !== \"https:\") {\n\t\tthrow new Error(\n\t\t\t`Invalid registry URL protocol: ${url.protocol}. Only http: and https: are allowed`,\n\t\t);\n\t}\n\n\t// Return the sanitized URL (URL constructor normalizes it).\n\treturn url.toString();\n}\n\n/**\n * Get the install command (pnpm preferred, npm fallback).\n * @param registry - Optional custom npm registry URL\n */\nfunction getInstallCommand(registry?: string): string {\n\tif (usePnpm === null) {\n\t\tusePnpm = isPnpmAvailable();\n\t}\n\n\tlet registryArg = \"\";\n\tif (registry) {\n\t\t// Validate and sanitize the registry URL to prevent command injection.\n\t\tconst sanitizedRegistry = validateRegistryUrl(registry);\n\t\t// Quote the URL to handle any special characters safely.\n\t\tregistryArg = ` --registry \"${sanitizedRegistry}\"`;\n\t}\n\n\tif (usePnpm) {\n\t\treturn `pnpm install --ignore-scripts --no-frozen-lockfile${registryArg}`;\n\t}\n\treturn `npm install --legacy-peer-deps --ignore-scripts${registryArg}`;\n}\n\nexport type EntryContentOptions = {\n\tpackageName: string;\n\tsubpath?: string;\n\texports?: string[];\n\tallSubpaths?: string[];\n\texportToSubpath?: Map<string, string>;\n};\n\n/**\n * Generate the entry file content based on package, subpath, and exports.\n */\nfunction generateEntryContent(options: EntryContentOptions): string {\n\tconst { packageName, subpath, exports, allSubpaths, exportToSubpath } =\n\t\toptions;\n\n\t// If we have exports mapped to different subpaths.\n\tif (exportToSubpath && exportToSubpath.size > 0) {\n\t\t// Group exports by subpath.\n\t\tconst subpathToExports = new Map<string, string[]>();\n\t\tfor (const [exportName, sp] of exportToSubpath) {\n\t\t\tconst existing = subpathToExports.get(sp) || [];\n\t\t\texisting.push(exportName);\n\t\t\tsubpathToExports.set(sp, existing);\n\t\t}\n\n\t\t// Generate imports for each subpath.\n\t\tconst lines: string[] = [];\n\t\tconst allExportNames: string[] = [];\n\n\t\tfor (const [sp, exportNames] of subpathToExports) {\n\t\t\tconst importPath = `${packageName}/${sp}`;\n\t\t\tconst names = exportNames.join(\", \");\n\t\t\tlines.push(`import { ${names} } from \"${importPath}\";`);\n\t\t\tallExportNames.push(...exportNames);\n\t\t}\n\n\t\tlines.push(`export { ${allExportNames.join(\", \")} };`);\n\t\treturn lines.join(\"\\n\") + \"\\n\";\n\t}\n\n\t// If we have specific exports to import.\n\tif (exports && exports.length > 0) {\n\t\t// Determine the import path.\n\t\tconst importPath = subpath ? `${packageName}/${subpath}` : packageName;\n\t\tconst importNames = exports.join(\", \");\n\t\treturn `import { ${importNames} } from \"${importPath}\";\\nexport { ${importNames} };\\n`;\n\t}\n\n\t// If we have a specific subpath (but no specific exports).\n\tif (subpath) {\n\t\tconst importPath = `${packageName}/${subpath}`;\n\t\treturn `import * as pkg from \"${importPath}\";\\nexport default pkg;\\n`;\n\t}\n\n\t// If package has subpath exports only (no main entry), import all subpaths.\n\tif (allSubpaths && allSubpaths.length > 0) {\n\t\tconst imports = allSubpaths\n\t\t\t.map(\n\t\t\t\t(sp, i) =>\n\t\t\t\t\t`import * as sub${i} from \"${packageName}/${sp}\";\\nexport { sub${i} };`,\n\t\t\t)\n\t\t\t.join(\"\\n\");\n\t\treturn imports + \"\\n\";\n\t}\n\n\t// Default: import everything from main entry.\n\treturn `import * as pkg from \"${packageName}\";\\nexport default pkg;\\n`;\n}\n\n/**\n * Get externals list based on options.\n */\nexport function getExternals(\n\tpackageName: string,\n\texternals?: string[],\n\tnoExternal?: boolean,\n): string[] {\n\tif (noExternal) {\n\t\treturn [];\n\t}\n\n\t// Start with default externals (react, react-dom).\n\tlet result = [...DEFAULT_EXTERNALS];\n\n\t// If checking react or react-dom themselves, don't mark them as external.\n\tif (packageName === \"react\") {\n\t\tresult = result.filter((e) => e !== \"react\");\n\t} else if (packageName === \"react-dom\") {\n\t\tresult = result.filter((e) => e !== \"react-dom\");\n\t}\n\n\t// Add any additional externals.\n\tif (externals && externals.length > 0) {\n\t\tresult = [...new Set([...result, ...externals])];\n\t}\n\n\treturn result;\n}\n\nexport type PackageExports = Record<\n\tstring,\n\tstring | { import?: string; types?: string }\n>;\n\nexport type PackageInfo = {\n\tversion: string;\n\tdependencies: Record<string, string>;\n\tpeerDependencies: Record<string, string>;\n\texports: PackageExports | null;\n\thasMainEntry: boolean;\n\tengines: Record<string, string> | null;\n};\n\n/**\n * Get version, dependencies, peer dependencies, and exports from an installed\n * package.\n */\nfunction getPackageInfo(tmpDir: string, packageName: string): PackageInfo {\n\ttry {\n\t\t// Handle scoped packages - the package name in node_modules.\n\t\tconst pkgJsonPath = path.join(\n\t\t\ttmpDir,\n\t\t\t\"node_modules\",\n\t\t\tpackageName,\n\t\t\t\"package.json\",\n\t\t);\n\t\tif (fs.existsSync(pkgJsonPath)) {\n\t\t\tconst pkgJson = JSON.parse(fs.readFileSync(pkgJsonPath, \"utf-8\"));\n\n\t\t\t// Check if package has a main entry point.\n\t\t\tconst hasMainEntry = Boolean(\n\t\t\t\tpkgJson.main ||\n\t\t\t\t\tpkgJson.module ||\n\t\t\t\t\tpkgJson.exports?.[\".\"] ||\n\t\t\t\t\tpkgJson.exports?.[\"./index\"] ||\n\t\t\t\t\t(!pkgJson.exports && !pkgJson.main && !pkgJson.module),\n\t\t\t);\n\n\t\t\treturn {\n\t\t\t\tversion: pkgJson.version || \"unknown\",\n\t\t\t\tdependencies: pkgJson.dependencies || {},\n\t\t\t\tpeerDependencies: pkgJson.peerDependencies || {},\n\t\t\t\texports: pkgJson.exports || null,\n\t\t\t\thasMainEntry,\n\t\t\t\tengines: pkgJson.engines || null,\n\t\t\t};\n\t\t}\n\t} catch {\n\t\t// Ignore errors reading package info.\n\t}\n\treturn {\n\t\tversion: \"unknown\",\n\t\tdependencies: {},\n\t\tpeerDependencies: {},\n\t\texports: null,\n\t\thasMainEntry: true,\n\t\tengines: null,\n\t};\n}\n\n/**\n * Extract subpath export names from package exports field Returns array of\n * subpaths like [\"header\", \"body\", \"datagrid\"].\n */\nfunction getSubpathExports(exports: PackageExports | null): string[] {\n\tif (!exports) {\n\t\treturn [];\n\t}\n\n\tconst subpaths: string[] = [];\n\tfor (const key of Object.keys(exports)) {\n\t\t// Skip the main entry point and package.json.\n\t\tif (key === \".\" || key === \"./package.json\") {\n\t\t\tcontinue;\n\t\t}\n\t\t// Remove leading \"./\" to get subpath name.\n\t\tif (key.startsWith(\"./\")) {\n\t\t\tsubpaths.push(key.substring(2));\n\t\t}\n\t}\n\treturn subpaths;\n}\n\n/**\n * Result of finding subpaths for exports.\n */\ntype SubpathMapping = {\n\t// Single subpath if all exports are from the same subpath.\n\tsingleSubpath?: string;\n\t// Map of export name to subpath for multiple subpaths.\n\texportToSubpath?: Map<string, string>;\n};\n\n/**\n * Find which subpath(s) export the given component names Reads type definition\n * files or JS files to find the exports.\n */\nfunction findSubpathsForExports(\n\ttmpDir: string,\n\tpackageName: string,\n\texports: PackageExports,\n\tcomponentNames: string[],\n): SubpathMapping {\n\tconst packageDir = path.join(tmpDir, \"node_modules\", packageName);\n\tconst exportToSubpath = new Map<string, string>();\n\n\tfor (const [subpathKey, subpathValue] of Object.entries(exports)) {\n\t\t// Skip main entry and package.json.\n\t\tif (subpathKey === \".\" || subpathKey === \"./package.json\") {\n\t\t\tcontinue;\n\t\t}\n\n\t\t// Get the types or import path.\n\t\tlet filePath: string | undefined;\n\t\tif (typeof subpathValue === \"object\" && subpathValue !== null) {\n\t\t\t// Prefer types file for more accurate export detection.\n\t\t\tfilePath = subpathValue.types || subpathValue.import;\n\t\t} else if (typeof subpathValue === \"string\") {\n\t\t\tfilePath = subpathValue;\n\t\t}\n\n\t\tif (!filePath) {\n\t\t\tcontinue;\n\t\t}\n\n\t\t// Resolve the file path.\n\t\tconst fullPath = path.join(packageDir, filePath);\n\n\t\ttry {\n\t\t\tif (fs.existsSync(fullPath)) {\n\t\t\t\tconst content = fs.readFileSync(fullPath, \"utf-8\");\n\t\t\t\tconst subpath = subpathKey.startsWith(\"./\")\n\t\t\t\t\t? subpathKey.substring(2)\n\t\t\t\t\t: subpathKey;\n\n\t\t\t\t// Check each component name.\n\t\t\t\tfor (const name of componentNames) {\n\t\t\t\t\t// Skip if already found.\n\t\t\t\t\tif (exportToSubpath.has(name)) {\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\n\t\t\t\t\t// Escape regex special characters in the name to prevent injection.\n\t\t\t\t\tconst escapedName = escapeRegExp(name);\n\n\t\t\t\t\t// Look for various export patterns.\n\t\t\t\t\tconst patterns = [\n\t\t\t\t\t\tnew RegExp(`export\\\\s*\\\\{[^}]*\\\\b${escapedName}\\\\b[^}]*\\\\}`, \"m\"),\n\t\t\t\t\t\tnew RegExp(\n\t\t\t\t\t\t\t`export\\\\s+declare\\\\s+(?:const|function|class)\\\\s+${escapedName}\\\\b`,\n\t\t\t\t\t\t\t\"m\",\n\t\t\t\t\t\t),\n\t\t\t\t\t\tnew RegExp(\n\t\t\t\t\t\t\t`export\\\\s+(?:const|function|class)\\\\s+${escapedName}\\\\b`,\n\t\t\t\t\t\t\t\"m\",\n\t\t\t\t\t\t),\n\t\t\t\t\t];\n\n\t\t\t\t\tif (patterns.some((pattern) => pattern.test(content))) {\n\t\t\t\t\t\texportToSubpath.set(name, subpath);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t} catch {\n\t\t\t// Ignore read errors, continue to next subpath.\n\t\t}\n\t}\n\n\t// Check if all exports were found.\n\tif (exportToSubpath.size !== componentNames.length) {\n\t\treturn {}; // Not all exports found\n\t}\n\n\t// Check if all exports are from the same subpath.\n\tconst subpaths = new Set(exportToSubpath.values());\n\tif (subpaths.size === 1) {\n\t\treturn { singleSubpath: [...subpaths][0] };\n\t}\n\n\t// Multiple subpaths needed.\n\treturn { exportToSubpath };\n}\n\n/**\n * Check the bundle size of an npm package.\n */\nexport async function checkBundleSize(\n\toptions: BundleOptions,\n): Promise<BundleResult> {\n\tconst {\n\t\tpackageName: packageSpecifier,\n\t\texports,\n\t\tadditionalExternals,\n\t\tnoExternal,\n\t\tgzipLevel = 5,\n\t\tregistry,\n\t\tplatform: explicitPlatform,\n\t} = options;\n\n\t// Parse the package specifier to extract name, version, and subpath.\n\tconst {\n\t\tname: packageName,\n\t\tversion: requestedVersion,\n\t\tsubpath,\n\t} = parsePackageSpecifier(packageSpecifier);\n\n\tconst tmpDir = createTempDir();\n\n\ttry {\n\t\t// Create initial package.json.\n\t\tconst packageJson: {\n\t\t\tname: string;\n\t\t\tversion: string;\n\t\t\ttype: string;\n\t\t\tdependencies: Record<string, string>;\n\t\t} = {\n\t\t\tname: \"bundlecheck-temp\",\n\t\t\tversion: \"1.0.0\",\n\t\t\ttype: \"module\",\n\t\t\tdependencies: {\n\t\t\t\t[packageName]: requestedVersion,\n\t\t\t},\n\t\t};\n\n\t\tfs.writeFileSync(\n\t\t\tpath.join(tmpDir, \"package.json\"),\n\t\t\tJSON.stringify(packageJson, null, 2),\n\t\t);\n\n\t\t// Install the main package (try pnpm first, fallback to npm).\n\t\tconst installCmd = getInstallCommand(registry);\n\t\texecSync(installCmd, {\n\t\t\tcwd: tmpDir,\n\t\t\tstdio: \"pipe\",\n\t\t});\n\n\t\t/**\n\t\t * Get package info (version, dependencies, peer dependencies, exports,\n\t\t * engines).\n\t\t */\n\t\tconst pkgInfo = getPackageInfo(tmpDir, packageName);\n\t\tconst peerDepKeys = Object.keys(pkgInfo.peerDependencies);\n\n\t\t/**\n\t\t * Determine platform: use explicit value if provided, otherwise auto-detect\n\t\t * from engines.\n\t\t */\n\t\tlet platform: \"browser\" | \"node\" = \"browser\";\n\t\tif (explicitPlatform) {\n\t\t\tplatform = explicitPlatform;\n\t\t} else if (pkgInfo.engines?.node && !pkgInfo.engines?.browser) {\n\t\t\t// Package specifies node engine without browser - likely a Node.js package.\n\t\t\tplatform = \"node\";\n\t\t}\n\n\t\t// Collect all dependency names (prod + peer).\n\t\tconst allDependencies = [\n\t\t\t...new Set([...Object.keys(pkgInfo.dependencies), ...peerDepKeys]),\n\t\t].sort();\n\n\t\tif (peerDepKeys.length > 0) {\n\t\t\t// Add peer dependencies to package.json.\n\t\t\tfor (const dep of peerDepKeys) {\n\t\t\t\t// Use the version range from peer dependencies.\n\t\t\t\tpackageJson.dependencies[dep] = pkgInfo.peerDependencies[dep];\n\t\t\t}\n\n\t\t\t// Update package.json and reinstall.\n\t\t\tfs.writeFileSync(\n\t\t\t\tpath.join(tmpDir, \"package.json\"),\n\t\t\t\tJSON.stringify(packageJson, null, 2),\n\t\t\t);\n\n\t\t\texecSync(installCmd, {\n\t\t\t\tcwd: tmpDir,\n\t\t\t\tstdio: \"pipe\",\n\t\t\t});\n\t\t}\n\n\t\t/**\n\t\t * Determine if we need to use all subpath exports or find the right\n\t\t * subpath(s).\n\t\t */\n\t\tlet allSubpaths: string[] | undefined;\n\t\tlet resolvedSubpath = subpath;\n\t\tlet exportToSubpath: Map<string, string> | undefined;\n\n\t\tif (!subpath && !pkgInfo.hasMainEntry && pkgInfo.exports) {\n\t\t\tif (exports && exports.length > 0) {\n\t\t\t\t// User specified exports but no subpath - try to find the right subpath(s).\n\t\t\t\tconst mapping = findSubpathsForExports(\n\t\t\t\t\ttmpDir,\n\t\t\t\t\tpackageName,\n\t\t\t\t\tpkgInfo.exports,\n\t\t\t\t\texports,\n\t\t\t\t);\n\n\t\t\t\tif (mapping.singleSubpath) {\n\t\t\t\t\t// All exports from the same subpath.\n\t\t\t\t\tresolvedSubpath = mapping.singleSubpath;\n\t\t\t\t} else if (mapping.exportToSubpath) {\n\t\t\t\t\t// Exports from multiple subpaths.\n\t\t\t\t\texportToSubpath = mapping.exportToSubpath;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// If still no subpath resolved and no mapping, bundle all subpaths.\n\t\t\tif (!resolvedSubpath && !exportToSubpath) {\n\t\t\t\tallSubpaths = getSubpathExports(pkgInfo.exports);\n\t\t\t}\n\t\t}\n\n\t\t// Create entry file with appropriate content.\n\t\tconst entryContent = generateEntryContent({\n\t\t\tpackageName,\n\t\t\tsubpath: resolvedSubpath,\n\t\t\texports,\n\t\t\tallSubpaths,\n\t\t\texportToSubpath,\n\t\t});\n\t\tconst entryFile = path.join(tmpDir, \"entry.js\");\n\t\tfs.writeFileSync(entryFile, entryContent);\n\n\t\t// Get externals.\n\t\tconst externals = getExternals(\n\t\t\tpackageName,\n\t\t\tadditionalExternals,\n\t\t\tnoExternal,\n\t\t);\n\n\t\t// Bundle with esbuild.\n\t\tconst result = await esbuild.build({\n\t\t\tentryPoints: [entryFile],\n\t\t\tbundle: true,\n\t\t\twrite: false,\n\t\t\tformat: \"esm\",\n\t\t\tplatform,\n\t\t\ttarget: \"es2020\",\n\t\t\tminify: true,\n\t\t\ttreeShaking: true,\n\t\t\texternal: externals,\n\t\t\tmetafile: true,\n\t\t});\n\n\t\t// Get raw size.\n\t\tconst bundleContent = result.outputFiles[0].contents;\n\t\tconst rawSize = bundleContent.length;\n\n\t\t// Gzip the bundle (only for browser platform - not relevant for Node.js).\n\t\tlet gzipSize: number | null = null;\n\t\tif (platform === \"browser\") {\n\t\t\tconst gzipped = await gzipAsync(Buffer.from(bundleContent), {\n\t\t\t\tlevel: gzipLevel,\n\t\t\t});\n\t\t\tgzipSize = gzipped.length;\n\t\t}\n\n\t\t// Determine the display name.\n\t\tlet displayName = packageName;\n\t\tif (resolvedSubpath) {\n\t\t\tdisplayName = `${packageName}/${resolvedSubpath}`;\n\t\t} else if (exportToSubpath && exportToSubpath.size > 0) {\n\t\t\t// Multiple subpaths - show them all.\n\t\t\tconst uniqueSubpaths = [...new Set(exportToSubpath.values())].sort();\n\t\t\tdisplayName = `${packageName}/{${uniqueSubpaths.join(\", \")}}`;\n\t\t}\n\n\t\treturn {\n\t\t\tpackageName: displayName,\n\t\t\tpackageVersion: pkgInfo.version,\n\t\t\texports: exports || [],\n\t\t\trawSize,\n\t\t\tgzipSize,\n\t\t\tgzipLevel,\n\t\t\texternals,\n\t\t\tdependencies: allDependencies,\n\t\t\tplatform,\n\t\t};\n\t} finally {\n\t\tcleanupTempDir(tmpDir);\n\t}\n}\n"],"names":["execSync","fs","os","path","promisify","zlib","esbuild","DEFAULT_EXTERNALS","gzipAsync","gzip","escapeRegExp","str","replace","parsePackageSpecifier","specifier","workingSpec","version","startsWith","secondAtIndex","indexOf","substring","parts","split","length","name","subpath","slice","join","atIndex","slashIndex","formatBytes","bytes","k","sizes","i","Math","floor","log","Number","parseFloat","toFixed","createTempDir","tmpDir","tmpdir","Date","now","mkdirSync","recursive","cleanupTempDir","rmSync","force","isPnpmAvailable","stdio","usePnpm","validateRegistryUrl","registry","url","URL","Error","protocol","toString","getInstallCommand","registryArg","sanitizedRegistry","generateEntryContent","options","packageName","exports","allSubpaths","exportToSubpath","size","subpathToExports","Map","exportName","sp","existing","get","push","set","lines","allExportNames","exportNames","importPath","names","importNames","imports","map","getExternals","externals","noExternal","result","filter","e","Set","getPackageInfo","pkgJsonPath","existsSync","pkgJson","JSON","parse","readFileSync","hasMainEntry","Boolean","main","module","dependencies","peerDependencies","engines","getSubpathExports","subpaths","key","Object","keys","findSubpathsForExports","componentNames","packageDir","subpathKey","subpathValue","entries","filePath","types","import","fullPath","content","has","escapedName","patterns","RegExp","some","pattern","test","values","singleSubpath","checkBundleSize","packageSpecifier","additionalExternals","gzipLevel","platform","explicitPlatform","requestedVersion","packageJson","type","writeFileSync","stringify","installCmd","cwd","pkgInfo","peerDepKeys","node","browser","allDependencies","sort","dep","resolvedSubpath","mapping","entryContent","entryFile","build","entryPoints","bundle","write","format","target","minify","treeShaking","external","metafile","bundleContent","outputFiles","contents","rawSize","gzipSize","gzipped","Buffer","from","level","displayName","uniqueSubpaths","packageVersion"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["../src/bundler.ts"],"sourcesContent":["import { execSync } from \"node:child_process\";\nimport fs from \"node:fs\";\nimport os from \"node:os\";\nimport path from \"node:path\";\nimport { promisify } from \"node:util\";\nimport zlib from \"node:zlib\";\nimport * as esbuild from \"esbuild\";\nimport { DEFAULT_EXTERNALS, EXTERNAL_SUBPATHS } from \"./defaults.js\";\nimport { getNamedExports } from \"./exports.js\";\n\nconst gzipAsync = promisify(zlib.gzip);\n\n/**\n * Escape special regex characters in a string.\n */\nfunction escapeRegExp(str: string): string {\n\treturn str.replace(/[.*+?^${}()|[\\]\\\\]/g, \"\\\\$&\");\n}\n\nexport type ParsedPackage = {\n\tname: string;\n\tversion: string;\n\tsubpath?: string;\n};\n\n/**\n * Parse a package specifier to extract name, version, and subpath.\n * Handles:\n * - @scope/package@1.0.0\n * - @scope/package/subpath@1.0.0\n * - @scope/package/subpath\n * - package/subpath@1.0.0\n */\nexport function parsePackageSpecifier(specifier: string): ParsedPackage {\n\tlet workingSpec = specifier;\n\tlet version = \"latest\";\n\n\t// Handle scoped packages (@scope/name...)\n\tif (workingSpec.startsWith(\"@\")) {\n\t\t// Find the second @ which would separate version.\n\t\tconst secondAtIndex = workingSpec.indexOf(\"@\", 1);\n\t\tif (secondAtIndex !== -1) {\n\t\t\tversion = workingSpec.substring(secondAtIndex + 1);\n\t\t\tworkingSpec = workingSpec.substring(0, secondAtIndex);\n\t\t}\n\n\t\t/**\n\t\t * Now workingSpec is like @scope/name or @scope/name/subpath Split by / and\n\t\t * check if there are more than 2 parts.\n\t\t */\n\t\tconst parts = workingSpec.split(\"/\");\n\t\tif (parts.length > 2) {\n\t\t\t// Has subpath: @scope/name/subpath/more.\n\t\t\tconst name = `${parts[0]}/${parts[1]}`;\n\t\t\tconst subpath = parts.slice(2).join(\"/\");\n\t\t\treturn { name, version, subpath };\n\t\t}\n\t\t// No subpath: @scope/name.\n\t\treturn { name: workingSpec, version };\n\t}\n\n\t// Handle non-scoped packages (name@version or name/subpath@version).\n\tconst atIndex = workingSpec.indexOf(\"@\");\n\tif (atIndex !== -1) {\n\t\tversion = workingSpec.substring(atIndex + 1);\n\t\tworkingSpec = workingSpec.substring(0, atIndex);\n\t}\n\n\t// Check for subpath in non-scoped packages.\n\tconst slashIndex = workingSpec.indexOf(\"/\");\n\tif (slashIndex !== -1) {\n\t\tconst name = workingSpec.substring(0, slashIndex);\n\t\tconst subpath = workingSpec.substring(slashIndex + 1);\n\t\treturn { name, version, subpath };\n\t}\n\n\treturn { name: workingSpec, version };\n}\n\nexport type BundleOptions = {\n\tpackageName: string;\n\texports?: string[];\n\tadditionalExternals?: string[];\n\tnoExternal?: boolean;\n\tgzipLevel?: number;\n\tregistry?: string;\n\t/**\n\t * Target platform. If undefined, auto-detects from package.json engines.\n\t */\n\tplatform?: \"browser\" | \"node\";\n};\n\nexport type BundleResult = {\n\tpackageName: string;\n\tpackageVersion: string;\n\texports: string[];\n\trawSize: number;\n\t/**\n\t * Gzip size in bytes, or null for node platform (gzip not applicable).\n\t */\n\tgzipSize: number | null;\n\tgzipLevel: number;\n\texternals: string[];\n\tdependencies: string[];\n\tplatform: \"browser\" | \"node\";\n\t/**\n\t * Total number of named exports in the package (when analyzing entire\n\t * package).\n\t */\n\tnamedExportCount: number;\n};\n\n/**\n * Format bytes to human-readable string.\n */\nexport function formatBytes(bytes: number): string {\n\tif (bytes === 0) {\n\t\treturn \"0 B\";\n\t}\n\tconst k = 1024;\n\tconst sizes = [\"B\", \"kB\", \"MB\", \"GB\"];\n\tconst i = Math.floor(Math.log(bytes) / Math.log(k));\n\treturn `${Number.parseFloat((bytes / k ** i).toFixed(2))} ${sizes[i]}`;\n}\n\n/**\n * Create a temporary directory for bundling.\n */\nfunction createTempDir(): string {\n\tconst tmpDir = path.join(os.tmpdir(), `bundlecheck-${Date.now()}`);\n\tfs.mkdirSync(tmpDir, { recursive: true });\n\treturn tmpDir;\n}\n\n/**\n * Clean up temporary directory.\n */\nfunction cleanupTempDir(tmpDir: string): void {\n\ttry {\n\t\tfs.rmSync(tmpDir, { recursive: true, force: true });\n\t} catch {\n\t\t// Ignore cleanup errors.\n\t}\n}\n\n/**\n * Check if pnpm is available.\n */\nfunction isPnpmAvailable(): boolean {\n\ttry {\n\t\texecSync(\"pnpm --version\", { stdio: \"pipe\" });\n\t\treturn true;\n\t} catch {\n\t\treturn false;\n\t}\n}\n\n// Cache the result of pnpm availability check.\nlet usePnpm: boolean | null = null;\n\n/**\n * Validate and sanitize a registry URL to prevent command injection.\n * @param registry - The registry URL to validate\n * @returns The sanitized URL or undefined if invalid\n * @throws Error if the URL is invalid or contains potentially malicious characters\n */\nfunction validateRegistryUrl(registry: string): string {\n\t// Parse as URL to validate format.\n\tlet url: URL;\n\ttry {\n\t\turl = new URL(registry);\n\t} catch {\n\t\tthrow new Error(\n\t\t\t`Invalid registry URL: ${registry}. Must be a valid URL (e.g., https://registry.example.com)`,\n\t\t);\n\t}\n\n\t// Only allow http and https protocols.\n\tif (url.protocol !== \"http:\" && url.protocol !== \"https:\") {\n\t\tthrow new Error(\n\t\t\t`Invalid registry URL protocol: ${url.protocol}. Only http: and https: are allowed`,\n\t\t);\n\t}\n\n\t// Return the sanitized URL (URL constructor normalizes it).\n\treturn url.toString();\n}\n\n/**\n * Get the install command (pnpm preferred, npm fallback).\n * @param registry - Optional custom npm registry URL\n */\nfunction getInstallCommand(registry?: string): string {\n\tif (usePnpm === null) {\n\t\tusePnpm = isPnpmAvailable();\n\t}\n\n\tlet registryArg = \"\";\n\tif (registry) {\n\t\t// Validate and sanitize the registry URL to prevent command injection.\n\t\tconst sanitizedRegistry = validateRegistryUrl(registry);\n\t\t// Quote the URL to handle any special characters safely.\n\t\tregistryArg = ` --registry \"${sanitizedRegistry}\"`;\n\t}\n\n\tif (usePnpm) {\n\t\treturn `pnpm install --ignore-scripts --no-frozen-lockfile${registryArg}`;\n\t}\n\treturn `npm install --legacy-peer-deps --ignore-scripts${registryArg}`;\n}\n\nexport type EntryContentOptions = {\n\tpackageName: string;\n\tsubpath?: string;\n\texports?: string[];\n\tallSubpaths?: string[];\n\texportToSubpath?: Map<string, string>;\n};\n\n/**\n * Generate the entry file content based on package, subpath, and exports.\n */\nfunction generateEntryContent(options: EntryContentOptions): string {\n\tconst { packageName, subpath, exports, allSubpaths, exportToSubpath } =\n\t\toptions;\n\n\t// If we have exports mapped to different subpaths.\n\tif (exportToSubpath && exportToSubpath.size > 0) {\n\t\t// Group exports by subpath.\n\t\tconst subpathToExports = new Map<string, string[]>();\n\t\tfor (const [exportName, sp] of exportToSubpath) {\n\t\t\tconst existing = subpathToExports.get(sp) || [];\n\t\t\texisting.push(exportName);\n\t\t\tsubpathToExports.set(sp, existing);\n\t\t}\n\n\t\t// Generate imports for each subpath.\n\t\tconst lines: string[] = [];\n\t\tconst allExportNames: string[] = [];\n\n\t\tfor (const [sp, exportNames] of subpathToExports) {\n\t\t\tconst importPath = `${packageName}/${sp}`;\n\t\t\tconst names = exportNames.join(\", \");\n\t\t\tlines.push(`import { ${names} } from \"${importPath}\";`);\n\t\t\tallExportNames.push(...exportNames);\n\t\t}\n\n\t\tlines.push(`export { ${allExportNames.join(\", \")} };`);\n\t\treturn lines.join(\"\\n\") + \"\\n\";\n\t}\n\n\t// If we have specific exports to import.\n\tif (exports && exports.length > 0) {\n\t\t// Determine the import path.\n\t\tconst importPath = subpath ? `${packageName}/${subpath}` : packageName;\n\t\tconst importNames = exports.join(\", \");\n\t\treturn `import { ${importNames} } from \"${importPath}\";\\nexport { ${importNames} };\\n`;\n\t}\n\n\t// If we have a specific subpath (but no specific exports).\n\tif (subpath) {\n\t\tconst importPath = `${packageName}/${subpath}`;\n\t\treturn `import * as pkg from \"${importPath}\";\\nexport default pkg;\\n`;\n\t}\n\n\t// If package has subpath exports only (no main entry), import all subpaths.\n\tif (allSubpaths && allSubpaths.length > 0) {\n\t\tconst imports = allSubpaths\n\t\t\t.map(\n\t\t\t\t(sp, i) =>\n\t\t\t\t\t`import * as sub${i} from \"${packageName}/${sp}\";\\nexport { sub${i} };`,\n\t\t\t)\n\t\t\t.join(\"\\n\");\n\t\treturn imports + \"\\n\";\n\t}\n\n\t// Default: import everything from main entry.\n\treturn `import * as pkg from \"${packageName}\";\\nexport default pkg;\\n`;\n}\n\n/**\n * Check if an error is an esbuild BuildFailure. BuildFailure has an `errors`\n * array with structured error objects.\n */\nfunction isEsbuildBuildFailure(\n\terror: unknown,\n): error is { errors: Array<{ text: string; location?: unknown }> } {\n\treturn (\n\t\ttypeof error === \"object\" &&\n\t\terror !== null &&\n\t\t\"errors\" in error &&\n\t\tArray.isArray((error as { errors: unknown }).errors)\n\t);\n}\n\n/**\n * Extract unresolved module paths from an esbuild BuildFailure error. Returns\n * an object indicating which react-related modules failed to resolve.\n *\n * Uses esbuild's structured error objects (errors array) for reliable parsing.\n * Falls back to string matching if the error format is unexpected.\n *\n * Tested against esbuild 0.27.x error format.\n *\n */\nfunction parseUnresolvedModules(error: unknown): {\n\thasUnresolvedReact: boolean;\n\thasUnresolvedReactDom: boolean;\n} {\n\tconst result = { hasUnresolvedReact: false, hasUnresolvedReactDom: false };\n\n\t/**\n\t * Pattern to extract module path from \"Could not resolve X\" errors. Matches:\n\t * Could not resolve \"module-name\" or Could not resolve 'module-name'.\n\t */\n\tconst resolveErrorPattern = /Could not resolve [\"']([^\"']+)[\"']/;\n\n\tif (isEsbuildBuildFailure(error)) {\n\t\t// Use structured error objects from esbuild BuildFailure.\n\t\tfor (const err of error.errors) {\n\t\t\tconst match = resolveErrorPattern.exec(err.text);\n\t\t\tif (match) {\n\t\t\t\tconst modulePath = match[1];\n\t\t\t\t// Check if it's a react or react-dom import (including subpaths).\n\t\t\t\tif (modulePath === \"react\" || modulePath.startsWith(\"react/\")) {\n\t\t\t\t\tresult.hasUnresolvedReact = true;\n\t\t\t\t}\n\t\t\t\tif (modulePath === \"react-dom\" || modulePath.startsWith(\"react-dom/\")) {\n\t\t\t\t\tresult.hasUnresolvedReactDom = true;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t} else {\n\t\t// Fallback: parse error message string (less reliable).\n\t\tconst errorMessage = String(error);\n\t\tconst matches = errorMessage.matchAll(\n\t\t\t/Could not resolve [\"']([^\"']+)[\"']/g,\n\t\t);\n\t\tfor (const match of matches) {\n\t\t\tconst modulePath = match[1];\n\t\t\tif (modulePath === \"react\" || modulePath.startsWith(\"react/\")) {\n\t\t\t\tresult.hasUnresolvedReact = true;\n\t\t\t}\n\t\t\tif (modulePath === \"react-dom\" || modulePath.startsWith(\"react-dom/\")) {\n\t\t\t\tresult.hasUnresolvedReactDom = true;\n\t\t\t}\n\t\t}\n\t}\n\n\treturn result;\n}\n\n/**\n * Get externals list based on options and package dependencies. react and\n * react-dom are only marked as external if they are declared in the package's\n * dependencies or peerDependencies.\n *\n * Returns the base package names (e.g., [\"react\", \"react-dom\"]) for display\n * purposes.\n *\n */\nexport function getExternals(\n\tpackageName: string,\n\texternals?: string[],\n\tnoExternal?: boolean,\n\tpackageDependencies?: string[],\n): string[] {\n\tif (noExternal) {\n\t\treturn [];\n\t}\n\n\t/**\n\t * Start with empty result - we'll only add react/react-dom if they're in\n\t * package deps.\n\t */\n\tlet result: string[] = [];\n\n\t/**\n\t * Only include react/react-dom if they're in the package's dependencies or\n\t * peerDependencies.\n\t */\n\tif (packageDependencies && packageDependencies.length > 0) {\n\t\tfor (const dep of DEFAULT_EXTERNALS) {\n\t\t\t// Don't mark as external if we're checking the package itself.\n\t\t\tif (dep === packageName) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tif (packageDependencies.includes(dep)) {\n\t\t\t\tresult.push(dep);\n\t\t\t}\n\t\t}\n\t}\n\n\t// Add any additional externals.\n\tif (externals && externals.length > 0) {\n\t\tresult = [...new Set([...result, ...externals])];\n\t}\n\n\treturn result;\n}\n\n/**\n * Expand externals to include subpaths for esbuild. For example, \"react\"\n * expands to [\"react\", \"react/jsx-runtime\", \"react/jsx-dev-runtime\"]. This is\n * needed because esbuild doesn't automatically externalize subpaths.\n */\nexport function expandExternalsForEsbuild(externals: string[]): string[] {\n\tconst expanded: string[] = [];\n\n\tfor (const ext of externals) {\n\t\texpanded.push(ext);\n\t\t// Add subpaths if this is a known package with subpath exports.\n\t\tconst subpaths = EXTERNAL_SUBPATHS[ext];\n\t\tif (subpaths) {\n\t\t\texpanded.push(...subpaths);\n\t\t}\n\t}\n\n\treturn [...new Set(expanded)];\n}\n\nexport type PackageExports = Record<\n\tstring,\n\tstring | { import?: string; types?: string }\n>;\n\nexport type PackageInfo = {\n\tversion: string;\n\tdependencies: Record<string, string>;\n\tpeerDependencies: Record<string, string>;\n\texports: PackageExports | null;\n\thasMainEntry: boolean;\n\tengines: Record<string, string> | null;\n};\n\n/**\n * Get version, dependencies, peer dependencies, and exports from an installed\n * package.\n */\nfunction getPackageInfo(tmpDir: string, packageName: string): PackageInfo {\n\ttry {\n\t\t// Handle scoped packages - the package name in node_modules.\n\t\tconst pkgJsonPath = path.join(\n\t\t\ttmpDir,\n\t\t\t\"node_modules\",\n\t\t\tpackageName,\n\t\t\t\"package.json\",\n\t\t);\n\t\tif (fs.existsSync(pkgJsonPath)) {\n\t\t\tconst pkgJson = JSON.parse(fs.readFileSync(pkgJsonPath, \"utf-8\"));\n\n\t\t\t// Check if package has a main entry point.\n\t\t\tconst hasMainEntry = Boolean(\n\t\t\t\tpkgJson.main ||\n\t\t\t\t\tpkgJson.module ||\n\t\t\t\t\tpkgJson.exports?.[\".\"] ||\n\t\t\t\t\tpkgJson.exports?.[\"./index\"] ||\n\t\t\t\t\t(!pkgJson.exports && !pkgJson.main && !pkgJson.module),\n\t\t\t);\n\n\t\t\treturn {\n\t\t\t\tversion: pkgJson.version || \"unknown\",\n\t\t\t\tdependencies: pkgJson.dependencies || {},\n\t\t\t\tpeerDependencies: pkgJson.peerDependencies || {},\n\t\t\t\texports: pkgJson.exports || null,\n\t\t\t\thasMainEntry,\n\t\t\t\tengines: pkgJson.engines || null,\n\t\t\t};\n\t\t}\n\t} catch {\n\t\t// Ignore errors reading package info.\n\t}\n\treturn {\n\t\tversion: \"unknown\",\n\t\tdependencies: {},\n\t\tpeerDependencies: {},\n\t\texports: null,\n\t\thasMainEntry: true,\n\t\tengines: null,\n\t};\n}\n\n/**\n * Extract subpath export names from package exports field Returns array of\n * subpaths like [\"header\", \"body\", \"datagrid\"].\n */\nfunction getSubpathExports(exports: PackageExports | null): string[] {\n\tif (!exports) {\n\t\treturn [];\n\t}\n\n\tconst subpaths: string[] = [];\n\tfor (const key of Object.keys(exports)) {\n\t\t// Skip the main entry point and package.json.\n\t\tif (key === \".\" || key === \"./package.json\") {\n\t\t\tcontinue;\n\t\t}\n\t\t// Remove leading \"./\" to get subpath name.\n\t\tif (key.startsWith(\"./\")) {\n\t\t\tsubpaths.push(key.substring(2));\n\t\t}\n\t}\n\treturn subpaths;\n}\n\n/**\n * Result of finding subpaths for exports.\n */\ntype SubpathMapping = {\n\t// Single subpath if all exports are from the same subpath.\n\tsingleSubpath?: string;\n\t// Map of export name to subpath for multiple subpaths.\n\texportToSubpath?: Map<string, string>;\n};\n\n/**\n * Find which subpath(s) export the given component names Reads type definition\n * files or JS files to find the exports.\n */\nfunction findSubpathsForExports(\n\ttmpDir: string,\n\tpackageName: string,\n\texports: PackageExports,\n\tcomponentNames: string[],\n): SubpathMapping {\n\tconst packageDir = path.join(tmpDir, \"node_modules\", packageName);\n\tconst exportToSubpath = new Map<string, string>();\n\n\tfor (const [subpathKey, subpathValue] of Object.entries(exports)) {\n\t\t// Skip main entry and package.json.\n\t\tif (subpathKey === \".\" || subpathKey === \"./package.json\") {\n\t\t\tcontinue;\n\t\t}\n\n\t\t// Get the types or import path.\n\t\tlet filePath: string | undefined;\n\t\tif (typeof subpathValue === \"object\" && subpathValue !== null) {\n\t\t\t// Prefer types file for more accurate export detection.\n\t\t\tfilePath = subpathValue.types || subpathValue.import;\n\t\t} else if (typeof subpathValue === \"string\") {\n\t\t\tfilePath = subpathValue;\n\t\t}\n\n\t\tif (!filePath) {\n\t\t\tcontinue;\n\t\t}\n\n\t\t// Resolve the file path.\n\t\tconst fullPath = path.join(packageDir, filePath);\n\n\t\ttry {\n\t\t\tif (fs.existsSync(fullPath)) {\n\t\t\t\tconst content = fs.readFileSync(fullPath, \"utf-8\");\n\t\t\t\tconst subpath = subpathKey.startsWith(\"./\")\n\t\t\t\t\t? subpathKey.substring(2)\n\t\t\t\t\t: subpathKey;\n\n\t\t\t\t// Check each component name.\n\t\t\t\tfor (const name of componentNames) {\n\t\t\t\t\t// Skip if already found.\n\t\t\t\t\tif (exportToSubpath.has(name)) {\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\n\t\t\t\t\t// Escape regex special characters in the name to prevent injection.\n\t\t\t\t\tconst escapedName = escapeRegExp(name);\n\n\t\t\t\t\t// Look for various export patterns.\n\t\t\t\t\tconst patterns = [\n\t\t\t\t\t\tnew RegExp(`export\\\\s*\\\\{[^}]*\\\\b${escapedName}\\\\b[^}]*\\\\}`, \"m\"),\n\t\t\t\t\t\tnew RegExp(\n\t\t\t\t\t\t\t`export\\\\s+declare\\\\s+(?:const|function|class)\\\\s+${escapedName}\\\\b`,\n\t\t\t\t\t\t\t\"m\",\n\t\t\t\t\t\t),\n\t\t\t\t\t\tnew RegExp(\n\t\t\t\t\t\t\t`export\\\\s+(?:const|function|class)\\\\s+${escapedName}\\\\b`,\n\t\t\t\t\t\t\t\"m\",\n\t\t\t\t\t\t),\n\t\t\t\t\t];\n\n\t\t\t\t\tif (patterns.some((pattern) => pattern.test(content))) {\n\t\t\t\t\t\texportToSubpath.set(name, subpath);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t} catch {\n\t\t\t// Ignore read errors, continue to next subpath.\n\t\t}\n\t}\n\n\t// Check if all exports were found.\n\tif (exportToSubpath.size !== componentNames.length) {\n\t\treturn {}; // Not all exports found\n\t}\n\n\t// Check if all exports are from the same subpath.\n\tconst subpaths = new Set(exportToSubpath.values());\n\tif (subpaths.size === 1) {\n\t\treturn { singleSubpath: [...subpaths][0] };\n\t}\n\n\t// Multiple subpaths needed.\n\treturn { exportToSubpath };\n}\n\n/**\n * Check the bundle size of an npm package.\n */\nexport async function checkBundleSize(\n\toptions: BundleOptions,\n): Promise<BundleResult> {\n\tconst {\n\t\tpackageName: packageSpecifier,\n\t\texports,\n\t\tadditionalExternals,\n\t\tnoExternal,\n\t\tgzipLevel = 5,\n\t\tregistry,\n\t\tplatform: explicitPlatform,\n\t} = options;\n\n\t// Parse the package specifier to extract name, version, and subpath.\n\tconst {\n\t\tname: packageName,\n\t\tversion: requestedVersion,\n\t\tsubpath,\n\t} = parsePackageSpecifier(packageSpecifier);\n\n\tconst tmpDir = createTempDir();\n\n\ttry {\n\t\t// Create initial package.json.\n\t\tconst packageJson: {\n\t\t\tname: string;\n\t\t\tversion: string;\n\t\t\ttype: string;\n\t\t\tdependencies: Record<string, string>;\n\t\t} = {\n\t\t\tname: \"bundlecheck-temp\",\n\t\t\tversion: \"1.0.0\",\n\t\t\ttype: \"module\",\n\t\t\tdependencies: {\n\t\t\t\t[packageName]: requestedVersion,\n\t\t\t},\n\t\t};\n\n\t\tfs.writeFileSync(\n\t\t\tpath.join(tmpDir, \"package.json\"),\n\t\t\tJSON.stringify(packageJson, null, 2),\n\t\t);\n\n\t\t// Install the main package (try pnpm first, fallback to npm).\n\t\tconst installCmd = getInstallCommand(registry);\n\t\texecSync(installCmd, {\n\t\t\tcwd: tmpDir,\n\t\t\tstdio: \"pipe\",\n\t\t});\n\n\t\t/**\n\t\t * Get package info (version, dependencies, peer dependencies, exports,\n\t\t * engines).\n\t\t */\n\t\tconst pkgInfo = getPackageInfo(tmpDir, packageName);\n\t\tconst peerDepKeys = Object.keys(pkgInfo.peerDependencies);\n\n\t\t/**\n\t\t * Determine platform: use explicit value if provided, otherwise auto-detect\n\t\t * from engines.\n\t\t */\n\t\tlet platform: \"browser\" | \"node\" = \"browser\";\n\t\tif (explicitPlatform) {\n\t\t\tplatform = explicitPlatform;\n\t\t} else if (pkgInfo.engines?.node && !pkgInfo.engines?.browser) {\n\t\t\t// Package specifies node engine without browser - likely a Node.js package.\n\t\t\tplatform = \"node\";\n\t\t}\n\n\t\t// Collect all dependency names (prod + peer).\n\t\tconst allDependencies = [\n\t\t\t...new Set([...Object.keys(pkgInfo.dependencies), ...peerDepKeys]),\n\t\t].sort();\n\n\t\tif (peerDepKeys.length > 0) {\n\t\t\t// Add peer dependencies to package.json.\n\t\t\tfor (const dep of peerDepKeys) {\n\t\t\t\t// Use the version range from peer dependencies.\n\t\t\t\tpackageJson.dependencies[dep] = pkgInfo.peerDependencies[dep];\n\t\t\t}\n\n\t\t\t// Update package.json and reinstall.\n\t\t\tfs.writeFileSync(\n\t\t\t\tpath.join(tmpDir, \"package.json\"),\n\t\t\t\tJSON.stringify(packageJson, null, 2),\n\t\t\t);\n\n\t\t\texecSync(installCmd, {\n\t\t\t\tcwd: tmpDir,\n\t\t\t\tstdio: \"pipe\",\n\t\t\t});\n\t\t}\n\n\t\t/**\n\t\t * Determine if we need to use all subpath exports or find the right\n\t\t * subpath(s).\n\t\t */\n\t\tlet allSubpaths: string[] | undefined;\n\t\tlet resolvedSubpath = subpath;\n\t\tlet exportToSubpath: Map<string, string> | undefined;\n\n\t\tif (!subpath && !pkgInfo.hasMainEntry && pkgInfo.exports) {\n\t\t\tif (exports && exports.length > 0) {\n\t\t\t\t// User specified exports but no subpath - try to find the right subpath(s).\n\t\t\t\tconst mapping = findSubpathsForExports(\n\t\t\t\t\ttmpDir,\n\t\t\t\t\tpackageName,\n\t\t\t\t\tpkgInfo.exports,\n\t\t\t\t\texports,\n\t\t\t\t);\n\n\t\t\t\tif (mapping.singleSubpath) {\n\t\t\t\t\t// All exports from the same subpath.\n\t\t\t\t\tresolvedSubpath = mapping.singleSubpath;\n\t\t\t\t} else if (mapping.exportToSubpath) {\n\t\t\t\t\t// Exports from multiple subpaths.\n\t\t\t\t\texportToSubpath = mapping.exportToSubpath;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// If still no subpath resolved and no mapping, bundle all subpaths.\n\t\t\tif (!resolvedSubpath && !exportToSubpath) {\n\t\t\t\tallSubpaths = getSubpathExports(pkgInfo.exports);\n\t\t\t}\n\t\t}\n\n\t\t// Create entry file with appropriate content.\n\t\tconst entryContent = generateEntryContent({\n\t\t\tpackageName,\n\t\t\tsubpath: resolvedSubpath,\n\t\t\texports,\n\t\t\tallSubpaths,\n\t\t\texportToSubpath,\n\t\t});\n\t\tconst entryFile = path.join(tmpDir, \"entry.js\");\n\t\tfs.writeFileSync(entryFile, entryContent);\n\n\t\t/**\n\t\t * Get externals based on package dependencies. Only include react/react-dom\n\t\t * if they're in the package's dependencies or peerDependencies.\n\t\t */\n\t\tlet externals = getExternals(\n\t\t\tpackageName,\n\t\t\tadditionalExternals,\n\t\t\tnoExternal,\n\t\t\tallDependencies,\n\t\t);\n\n\t\t/**\n\t\t * Expand externals to include subpaths for esbuild.\n\t\t * e.g., \"react\" -> [\"react\", \"react/jsx-runtime\", \"react/jsx-dev-runtime\"]\n\t\t */\n\t\tlet esbuildExternals = expandExternalsForEsbuild(externals);\n\n\t\t/**\n\t\t * Bundle with esbuild. We use a two-phase approach:\n\t\t * 1. First attempt with logLevel: \"silent\" to suppress errors\n\t\t * 2. If it fails due to unresolved react imports, auto-add react to externals and retry\n\t\t * This handles packages that don't properly declare react as a peer\n\t\t * dependency.\n\t\t */\n\t\tlet result: esbuild.BuildResult<{ write: false; metafile: true }>;\n\t\ttry {\n\t\t\tresult = await esbuild.build({\n\t\t\t\tentryPoints: [entryFile],\n\t\t\t\tbundle: true,\n\t\t\t\twrite: false,\n\t\t\t\tformat: \"esm\",\n\t\t\t\tplatform,\n\t\t\t\ttarget: \"es2020\",\n\t\t\t\tminify: true,\n\t\t\t\ttreeShaking: true,\n\t\t\t\texternal: esbuildExternals,\n\t\t\t\tmetafile: true,\n\t\t\t\tlogLevel: \"silent\", // Suppress errors on first attempt (will retry if needed)\n\t\t\t});\n\t\t} catch (error) {\n\t\t\t/**\n\t\t\t * Parse unresolved module errors using esbuild's structured error format.\n\t\t\t * This handles packages that don't properly declare react as a peer\n\t\t\t * dependency.\n\t\t\t */\n\t\t\tconst { hasUnresolvedReact, hasUnresolvedReactDom } =\n\t\t\t\tparseUnresolvedModules(error);\n\n\t\t\tif (!noExternal && (hasUnresolvedReact || hasUnresolvedReactDom)) {\n\t\t\t\t// Auto-add react and/or react-dom to externals and retry.\n\t\t\t\tconst autoExternals: string[] = [];\n\t\t\t\tif (hasUnresolvedReact && !externals.includes(\"react\")) {\n\t\t\t\t\tautoExternals.push(\"react\");\n\t\t\t\t}\n\t\t\t\tif (hasUnresolvedReactDom && !externals.includes(\"react-dom\")) {\n\t\t\t\t\tautoExternals.push(\"react-dom\");\n\t\t\t\t}\n\n\t\t\t\tif (autoExternals.length > 0) {\n\t\t\t\t\texternals = [...externals, ...autoExternals];\n\t\t\t\t\tesbuildExternals = expandExternalsForEsbuild(externals);\n\n\t\t\t\t\tresult = await esbuild.build({\n\t\t\t\t\t\tentryPoints: [entryFile],\n\t\t\t\t\t\tbundle: true,\n\t\t\t\t\t\twrite: false,\n\t\t\t\t\t\tformat: \"esm\",\n\t\t\t\t\t\tplatform,\n\t\t\t\t\t\ttarget: \"es2020\",\n\t\t\t\t\t\tminify: true,\n\t\t\t\t\t\ttreeShaking: true,\n\t\t\t\t\t\texternal: esbuildExternals,\n\t\t\t\t\t\tmetafile: true,\n\t\t\t\t\t});\n\t\t\t\t} else {\n\t\t\t\t\tthrow error;\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tthrow error;\n\t\t\t}\n\t\t}\n\n\t\t// Get raw size.\n\t\tconst bundleContent = result.outputFiles[0].contents;\n\t\tconst rawSize = bundleContent.length;\n\n\t\t// Gzip the bundle (only for browser platform - not relevant for Node.js).\n\t\tlet gzipSize: number | null = null;\n\t\tif (platform === \"browser\") {\n\t\t\tconst gzipped = await gzipAsync(Buffer.from(bundleContent), {\n\t\t\t\tlevel: gzipLevel,\n\t\t\t});\n\t\t\tgzipSize = gzipped.length;\n\t\t}\n\n\t\t// Determine the display name.\n\t\tlet displayName = packageName;\n\t\tif (resolvedSubpath) {\n\t\t\tdisplayName = `${packageName}/${resolvedSubpath}`;\n\t\t} else if (exportToSubpath && exportToSubpath.size > 0) {\n\t\t\t// Multiple subpaths - show them all.\n\t\t\tconst uniqueSubpaths = [...new Set(exportToSubpath.values())].sort();\n\t\t\tdisplayName = `${packageName}/{${uniqueSubpaths.join(\", \")}}`;\n\t\t}\n\n\t\t/**\n\t\t * Get named export count from the package's type definitions. Use\n\t\t * runtimeCount to exclude type-only exports (types, interfaces).\n\t\t */\n\t\tconst { runtimeCount: namedExportCount } = getNamedExports(\n\t\t\ttmpDir,\n\t\t\tpackageName,\n\t\t);\n\n\t\treturn {\n\t\t\tpackageName: displayName,\n\t\t\tpackageVersion: pkgInfo.version,\n\t\t\texports: exports || [],\n\t\t\trawSize,\n\t\t\tgzipSize,\n\t\t\tgzipLevel,\n\t\t\texternals,\n\t\t\tdependencies: allDependencies,\n\t\t\tplatform,\n\t\t\tnamedExportCount,\n\t\t};\n\t} finally {\n\t\tcleanupTempDir(tmpDir);\n\t}\n}\n"],"names":["execSync","fs","os","path","promisify","zlib","esbuild","DEFAULT_EXTERNALS","EXTERNAL_SUBPATHS","getNamedExports","gzipAsync","gzip","escapeRegExp","str","replace","parsePackageSpecifier","specifier","workingSpec","version","startsWith","secondAtIndex","indexOf","substring","parts","split","length","name","subpath","slice","join","atIndex","slashIndex","formatBytes","bytes","k","sizes","i","Math","floor","log","Number","parseFloat","toFixed","createTempDir","tmpDir","tmpdir","Date","now","mkdirSync","recursive","cleanupTempDir","rmSync","force","isPnpmAvailable","stdio","usePnpm","validateRegistryUrl","registry","url","URL","Error","protocol","toString","getInstallCommand","registryArg","sanitizedRegistry","generateEntryContent","options","packageName","exports","allSubpaths","exportToSubpath","size","subpathToExports","Map","exportName","sp","existing","get","push","set","lines","allExportNames","exportNames","importPath","names","importNames","imports","map","isEsbuildBuildFailure","error","Array","isArray","errors","parseUnresolvedModules","result","hasUnresolvedReact","hasUnresolvedReactDom","resolveErrorPattern","err","match","exec","text","modulePath","errorMessage","String","matches","matchAll","getExternals","externals","noExternal","packageDependencies","dep","includes","Set","expandExternalsForEsbuild","expanded","ext","subpaths","getPackageInfo","pkgJsonPath","existsSync","pkgJson","JSON","parse","readFileSync","hasMainEntry","Boolean","main","module","dependencies","peerDependencies","engines","getSubpathExports","key","Object","keys","findSubpathsForExports","componentNames","packageDir","subpathKey","subpathValue","entries","filePath","types","import","fullPath","content","has","escapedName","patterns","RegExp","some","pattern","test","values","singleSubpath","checkBundleSize","packageSpecifier","additionalExternals","gzipLevel","platform","explicitPlatform","requestedVersion","packageJson","type","writeFileSync","stringify","installCmd","cwd","pkgInfo","peerDepKeys","node","browser","allDependencies","sort","resolvedSubpath","mapping","entryContent","entryFile","esbuildExternals","build","entryPoints","bundle","write","format","target","minify","treeShaking","external","metafile","logLevel","autoExternals","bundleContent","outputFiles","contents","rawSize","gzipSize","gzipped","Buffer","from","level","displayName","uniqueSubpaths","runtimeCount","namedExportCount","packageVersion"],"mappings":"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"}
|
package/dist/cache.js
CHANGED
|
@@ -47,11 +47,29 @@ let db = null;
|
|
|
47
47
|
dependencies TEXT NOT NULL DEFAULT '[]',
|
|
48
48
|
display_name TEXT NOT NULL,
|
|
49
49
|
created_at INTEGER NOT NULL,
|
|
50
|
+
named_export_count INTEGER DEFAULT 0,
|
|
51
|
+
actual_externals TEXT NOT NULL DEFAULT '',
|
|
50
52
|
UNIQUE(package_name, version, exports, platform, gzip_level, externals, no_external)
|
|
51
53
|
);
|
|
52
54
|
|
|
53
55
|
CREATE INDEX IF NOT EXISTS idx_created_at ON bundle_cache(created_at);
|
|
54
56
|
`);
|
|
57
|
+
/**
|
|
58
|
+
* Migration: Add named_export_count column if it doesn't exist (for existing
|
|
59
|
+
* databases).
|
|
60
|
+
*/ try {
|
|
61
|
+
db.exec(`ALTER TABLE bundle_cache ADD COLUMN named_export_count INTEGER DEFAULT 0`);
|
|
62
|
+
} catch {
|
|
63
|
+
// Column already exists, ignore error.
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Migration: Add actual_externals column if it doesn't exist (for existing
|
|
67
|
+
* databases).
|
|
68
|
+
*/ try {
|
|
69
|
+
db.exec(`ALTER TABLE bundle_cache ADD COLUMN actual_externals TEXT NOT NULL DEFAULT ''`);
|
|
70
|
+
} catch {
|
|
71
|
+
// Column already exists, ignore error.
|
|
72
|
+
}
|
|
55
73
|
return db;
|
|
56
74
|
}
|
|
57
75
|
/**
|
|
@@ -104,7 +122,11 @@ let db = null;
|
|
|
104
122
|
// If JSON is corrupted, use empty array.
|
|
105
123
|
dependencies = [];
|
|
106
124
|
}
|
|
107
|
-
|
|
125
|
+
/**
|
|
126
|
+
* Convert row to BundleResult. Use actual_externals (computed externals
|
|
127
|
+
* including auto-detected react/react-dom) if available, otherwise fall back
|
|
128
|
+
* to externals (for old cache entries).
|
|
129
|
+
*/ const externalsStr = row.actual_externals || row.externals;
|
|
108
130
|
return {
|
|
109
131
|
packageName: row.display_name,
|
|
110
132
|
packageVersion: row.version,
|
|
@@ -112,9 +134,10 @@ let db = null;
|
|
|
112
134
|
rawSize: row.raw_size,
|
|
113
135
|
gzipSize: row.gzip_size,
|
|
114
136
|
gzipLevel: row.gzip_level,
|
|
115
|
-
externals:
|
|
137
|
+
externals: externalsStr ? externalsStr.split(",").filter(Boolean) : [],
|
|
116
138
|
dependencies,
|
|
117
|
-
platform: row.platform
|
|
139
|
+
platform: row.platform,
|
|
140
|
+
namedExportCount: row.named_export_count ?? 0
|
|
118
141
|
};
|
|
119
142
|
} catch {
|
|
120
143
|
/**
|
|
@@ -135,10 +158,10 @@ let db = null;
|
|
|
135
158
|
*/ const stmt = database.prepare(`
|
|
136
159
|
INSERT OR REPLACE INTO bundle_cache (
|
|
137
160
|
package_name, version, exports, platform, gzip_level, externals, no_external,
|
|
138
|
-
raw_size, gzip_size, dependencies, display_name, created_at
|
|
139
|
-
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
161
|
+
raw_size, gzip_size, dependencies, display_name, created_at, named_export_count, actual_externals
|
|
162
|
+
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
140
163
|
`);
|
|
141
|
-
stmt.run(key.packageName, key.version, key.exports, key.platform, key.gzipLevel, key.externals, key.noExternal, result.rawSize, result.gzipSize, JSON.stringify(result.dependencies), result.packageName, Date.now());
|
|
164
|
+
stmt.run(key.packageName, key.version, key.exports, key.platform, key.gzipLevel, key.externals, key.noExternal, result.rawSize, result.gzipSize, JSON.stringify(result.dependencies), result.packageName, Date.now(), result.namedExportCount, result.externals.slice().sort().join(","));
|
|
142
165
|
// Enforce max entries (LRU-style eviction based on created_at).
|
|
143
166
|
enforceMaxEntries(database);
|
|
144
167
|
} catch {
|
package/dist/cache.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/cache.ts"],"sourcesContent":["import fs from \"node:fs\";\nimport os from \"node:os\";\nimport path from \"node:path\";\nimport Database from \"better-sqlite3\";\nimport type { BundleResult } from \"./bundler.js\";\n\nconst MAX_CACHE_ENTRIES = 1000;\n\n/**\n * Get the cache directory path (computed lazily to support testing).\n */\nfunction getCacheDir(): string {\n\treturn path.join(os.homedir(), \".bundlecheck\");\n}\n\n/**\n * Get the cache database path (computed lazily to support testing).\n */\nfunction getCacheDbPath(): string {\n\treturn path.join(getCacheDir(), \"cache.db\");\n}\n\nexport type CacheKey = {\n\tpackageName: string;\n\tversion: string;\n\texports: string;\n\t/**\n\t * Platform: \"browser\", \"node\", or \"auto\" (for auto-detection).\n\t */\n\tplatform: \"browser\" | \"node\" | \"auto\";\n\tgzipLevel: number;\n\texternals: string;\n\tnoExternal: number;\n};\n\nexport type CachedBundleResult = BundleResult;\n\ntype CacheRow = {\n\tid: number;\n\tpackage_name: string;\n\tversion: string;\n\texports: string;\n\tplatform: string;\n\tgzip_level: number;\n\texternals: string;\n\tno_external: number;\n\traw_size: number;\n\tgzip_size: number | null;\n\tdependencies: string;\n\tdisplay_name: string;\n\tcreated_at: number;\n};\n\nlet db: Database.Database | null = null;\n\n/**\n * Initialize the cache database with proper pragmas and schema.\n */\nexport function initCache(): Database.Database {\n\tif (db) {\n\t\treturn db;\n\t}\n\n\t// Ensure cache directory exists.\n\tconst cacheDir = getCacheDir();\n\tif (!fs.existsSync(cacheDir)) {\n\t\tfs.mkdirSync(cacheDir, { recursive: true });\n\t}\n\n\tdb = new Database(getCacheDbPath());\n\n\t// Apply pragmas for better CLI performance.\n\tdb.pragma(\"journal_mode = WAL\");\n\tdb.pragma(\"foreign_keys = ON\");\n\n\t// Create table if not exists.\n\tdb.exec(`\n\t\tCREATE TABLE IF NOT EXISTS bundle_cache (\n\t\t\tid INTEGER PRIMARY KEY AUTOINCREMENT,\n\t\t\tpackage_name TEXT NOT NULL,\n\t\t\tversion TEXT NOT NULL,\n\t\t\texports TEXT NOT NULL DEFAULT '',\n\t\t\tplatform TEXT NOT NULL,\n\t\t\tgzip_level INTEGER NOT NULL,\n\t\t\texternals TEXT NOT NULL DEFAULT '',\n\t\t\tno_external INTEGER NOT NULL DEFAULT 0,\n\t\t\traw_size INTEGER NOT NULL,\n\t\t\tgzip_size INTEGER,\n\t\t\tdependencies TEXT NOT NULL DEFAULT '[]',\n\t\t\tdisplay_name TEXT NOT NULL,\n\t\t\tcreated_at INTEGER NOT NULL,\n\t\t\tUNIQUE(package_name, version, exports, platform, gzip_level, externals, no_external)\n\t\t);\n\n\t\tCREATE INDEX IF NOT EXISTS idx_created_at ON bundle_cache(created_at);\n\t`);\n\n\treturn db;\n}\n\n/**\n * Normalize cache key options to a consistent format Sorts exports and\n * externals to ensure consistent cache hits.\n *\n * NOTE: platform can be \"browser\", \"node\", or \"auto\" (when user doesn't specify).\n * Using \"auto\" in the cache key ensures that auto-detected results are cached\n * separately from explicitly specified platform results.\n *\n */\nexport function normalizeCacheKey(options: {\n\tpackageName: string;\n\tversion: string;\n\texports?: string[];\n\t/**\n\t * Platform: \"browser\", \"node\", or undefined (treated as \"auto\").\n\t */\n\tplatform: \"browser\" | \"node\" | undefined;\n\tgzipLevel: number;\n\texternals: string[];\n\tnoExternal: boolean;\n}): CacheKey {\n\treturn {\n\t\tpackageName: options.packageName,\n\t\tversion: options.version,\n\t\texports: (options.exports || []).slice().sort().join(\",\"),\n\t\t/**\n\t\t * Use \"auto\" when platform is undefined to distinguish from explicit\n\t\t * platform.\n\t\t */\n\t\tplatform: options.platform ?? \"auto\",\n\t\tgzipLevel: options.gzipLevel,\n\t\texternals: options.externals.slice().sort().join(\",\"),\n\t\tnoExternal: options.noExternal ? 1 : 0,\n\t};\n}\n\n/**\n * Get a cached result if it exists Returns null if not found or if an error\n * occurs (graceful degradation).\n */\nexport function getCachedResult(key: CacheKey): CachedBundleResult | null {\n\ttry {\n\t\tconst database = initCache();\n\n\t\tconst stmt = database.prepare<\n\t\t\t[string, string, string, string, number, string, number]\n\t\t>(`\n\t\t\tSELECT * FROM bundle_cache\n\t\t\tWHERE package_name = ?\n\t\t\tAND version = ?\n\t\t\tAND exports = ?\n\t\t\tAND platform = ?\n\t\t\tAND gzip_level = ?\n\t\t\tAND externals = ?\n\t\t\tAND no_external = ?\n\t\t`);\n\n\t\tconst row = stmt.get(\n\t\t\tkey.packageName,\n\t\t\tkey.version,\n\t\t\tkey.exports,\n\t\t\tkey.platform,\n\t\t\tkey.gzipLevel,\n\t\t\tkey.externals,\n\t\t\tkey.noExternal,\n\t\t) as CacheRow | undefined;\n\n\t\tif (!row) {\n\t\t\treturn null;\n\t\t}\n\n\t\t// Parse dependencies with error handling for corrupted data.\n\t\tlet dependencies: string[] = [];\n\t\ttry {\n\t\t\tdependencies = JSON.parse(row.dependencies) as string[];\n\t\t} catch {\n\t\t\t// If JSON is corrupted, use empty array.\n\t\t\tdependencies = [];\n\t\t}\n\n\t\t// Convert row to BundleResult.\n\t\treturn {\n\t\t\tpackageName: row.display_name,\n\t\t\tpackageVersion: row.version,\n\t\t\texports: row.exports ? row.exports.split(\",\").filter(Boolean) : [],\n\t\t\trawSize: row.raw_size,\n\t\t\tgzipSize: row.gzip_size,\n\t\t\tgzipLevel: row.gzip_level,\n\t\t\texternals: row.externals ? row.externals.split(\",\").filter(Boolean) : [],\n\t\t\tdependencies,\n\t\t\tplatform: row.platform as \"browser\" | \"node\",\n\t\t};\n\t} catch {\n\t\t/**\n\t\t * On any database error, return null (graceful degradation - continue without\n\t\t * cache).\n\t\t */\n\t\treturn null;\n\t}\n}\n\n/**\n * Store a result in the cache Silently fails on errors (graceful degradation -\n * CLI continues without caching).\n */\nexport function setCachedResult(key: CacheKey, result: BundleResult): void {\n\ttry {\n\t\tconst database = initCache();\n\n\t\t/**\n\t\t * Use INSERT OR REPLACE to handle duplicates.\n\t\t * NOTE: This updates created_at on replace, making this LRU-style eviction.\n\t\t */\n\t\tconst stmt = database.prepare(`\n\t\t\tINSERT OR REPLACE INTO bundle_cache (\n\t\t\t\tpackage_name, version, exports, platform, gzip_level, externals, no_external,\n\t\t\t\traw_size, gzip_size, dependencies, display_name, created_at\n\t\t\t) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)\n\t\t`);\n\n\t\tstmt.run(\n\t\t\tkey.packageName,\n\t\t\tkey.version,\n\t\t\tkey.exports,\n\t\t\tkey.platform,\n\t\t\tkey.gzipLevel,\n\t\t\tkey.externals,\n\t\t\tkey.noExternal,\n\t\t\tresult.rawSize,\n\t\t\tresult.gzipSize,\n\t\t\tJSON.stringify(result.dependencies),\n\t\t\tresult.packageName,\n\t\t\tDate.now(),\n\t\t);\n\n\t\t// Enforce max entries (LRU-style eviction based on created_at).\n\t\tenforceMaxEntries(database);\n\t} catch {\n\t\t// On any database error, silently continue (graceful degradation).\n\t}\n}\n\n/**\n * Enforce maximum cache entries by removing entries with oldest timestamps Uses\n * LRU-style eviction: entries that are re-checked get updated timestamps and\n * move to the end of the eviction queue.\n */\nfunction enforceMaxEntries(database: Database.Database): void {\n\tconst countResult = database\n\t\t.prepare(\"SELECT COUNT(*) as count FROM bundle_cache\")\n\t\t.get() as { count: number };\n\n\tif (countResult.count > MAX_CACHE_ENTRIES) {\n\t\tconst toDelete = countResult.count - MAX_CACHE_ENTRIES;\n\t\tdatabase\n\t\t\t.prepare(\n\t\t\t\t`\n\t\t\tDELETE FROM bundle_cache\n\t\t\tWHERE id IN (\n\t\t\t\tSELECT id FROM bundle_cache\n\t\t\t\tORDER BY created_at ASC\n\t\t\t\tLIMIT ?\n\t\t\t)\n\t\t`,\n\t\t\t)\n\t\t\t.run(toDelete);\n\t}\n}\n\n/**\n * Clear all cache entries Silently fails on errors.\n */\nexport function clearCache(): void {\n\ttry {\n\t\tconst database = initCache();\n\t\tdatabase.prepare(\"DELETE FROM bundle_cache\").run();\n\t} catch {\n\t\t// Silently ignore errors.\n\t}\n}\n\n/**\n * Get the number of cached entries Returns 0 on error.\n */\nexport function getCacheCount(): number {\n\ttry {\n\t\tconst database = initCache();\n\t\tconst result = database\n\t\t\t.prepare(\"SELECT COUNT(*) as count FROM bundle_cache\")\n\t\t\t.get() as { count: number };\n\t\treturn result.count;\n\t} catch {\n\t\treturn 0;\n\t}\n}\n\n/**\n * Close the database connection (useful for testing).\n */\nexport function closeCache(): void {\n\tif (db) {\n\t\tdb.close();\n\t\tdb = null;\n\t}\n}\n"],"names":["fs","os","path","Database","MAX_CACHE_ENTRIES","getCacheDir","join","homedir","getCacheDbPath","db","initCache","cacheDir","existsSync","mkdirSync","recursive","pragma","exec","normalizeCacheKey","options","packageName","version","exports","slice","sort","platform","gzipLevel","externals","noExternal","getCachedResult","key","database","stmt","prepare","row","get","dependencies","JSON","parse","display_name","packageVersion","split","filter","Boolean","rawSize","raw_size","gzipSize","gzip_size","gzip_level","setCachedResult","result","run","stringify","Date","now","enforceMaxEntries","countResult","count","toDelete","clearCache","getCacheCount","closeCache","close"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["../src/cache.ts"],"sourcesContent":["import fs from \"node:fs\";\nimport os from \"node:os\";\nimport path from \"node:path\";\nimport Database from \"better-sqlite3\";\nimport type { BundleResult } from \"./bundler.js\";\n\nconst MAX_CACHE_ENTRIES = 1000;\n\n/**\n * Get the cache directory path (computed lazily to support testing).\n */\nfunction getCacheDir(): string {\n\treturn path.join(os.homedir(), \".bundlecheck\");\n}\n\n/**\n * Get the cache database path (computed lazily to support testing).\n */\nfunction getCacheDbPath(): string {\n\treturn path.join(getCacheDir(), \"cache.db\");\n}\n\nexport type CacheKey = {\n\tpackageName: string;\n\tversion: string;\n\texports: string;\n\t/**\n\t * Platform: \"browser\", \"node\", or \"auto\" (for auto-detection).\n\t */\n\tplatform: \"browser\" | \"node\" | \"auto\";\n\tgzipLevel: number;\n\texternals: string;\n\tnoExternal: number;\n};\n\nexport type CachedBundleResult = BundleResult;\n\ntype CacheRow = {\n\tid: number;\n\tpackage_name: string;\n\tversion: string;\n\texports: string;\n\tplatform: string;\n\tgzip_level: number;\n\texternals: string;\n\tno_external: number;\n\traw_size: number;\n\tgzip_size: number | null;\n\tdependencies: string;\n\tdisplay_name: string;\n\tcreated_at: number;\n\tnamed_export_count: number | null;\n\tactual_externals: string;\n};\n\nlet db: Database.Database | null = null;\n\n/**\n * Initialize the cache database with proper pragmas and schema.\n */\nexport function initCache(): Database.Database {\n\tif (db) {\n\t\treturn db;\n\t}\n\n\t// Ensure cache directory exists.\n\tconst cacheDir = getCacheDir();\n\tif (!fs.existsSync(cacheDir)) {\n\t\tfs.mkdirSync(cacheDir, { recursive: true });\n\t}\n\n\tdb = new Database(getCacheDbPath());\n\n\t// Apply pragmas for better CLI performance.\n\tdb.pragma(\"journal_mode = WAL\");\n\tdb.pragma(\"foreign_keys = ON\");\n\n\t// Create table if not exists.\n\tdb.exec(`\n\t\tCREATE TABLE IF NOT EXISTS bundle_cache (\n\t\t\tid INTEGER PRIMARY KEY AUTOINCREMENT,\n\t\t\tpackage_name TEXT NOT NULL,\n\t\t\tversion TEXT NOT NULL,\n\t\t\texports TEXT NOT NULL DEFAULT '',\n\t\t\tplatform TEXT NOT NULL,\n\t\t\tgzip_level INTEGER NOT NULL,\n\t\t\texternals TEXT NOT NULL DEFAULT '',\n\t\t\tno_external INTEGER NOT NULL DEFAULT 0,\n\t\t\traw_size INTEGER NOT NULL,\n\t\t\tgzip_size INTEGER,\n\t\t\tdependencies TEXT NOT NULL DEFAULT '[]',\n\t\t\tdisplay_name TEXT NOT NULL,\n\t\t\tcreated_at INTEGER NOT NULL,\n\t\t\tnamed_export_count INTEGER DEFAULT 0,\n\t\t\tactual_externals TEXT NOT NULL DEFAULT '',\n\t\t\tUNIQUE(package_name, version, exports, platform, gzip_level, externals, no_external)\n\t\t);\n\n\t\tCREATE INDEX IF NOT EXISTS idx_created_at ON bundle_cache(created_at);\n\t`);\n\n\t/**\n\t * Migration: Add named_export_count column if it doesn't exist (for existing\n\t * databases).\n\t */\n\ttry {\n\t\tdb.exec(\n\t\t\t`ALTER TABLE bundle_cache ADD COLUMN named_export_count INTEGER DEFAULT 0`,\n\t\t);\n\t} catch {\n\t\t// Column already exists, ignore error.\n\t}\n\n\t/**\n\t * Migration: Add actual_externals column if it doesn't exist (for existing\n\t * databases).\n\t */\n\ttry {\n\t\tdb.exec(\n\t\t\t`ALTER TABLE bundle_cache ADD COLUMN actual_externals TEXT NOT NULL DEFAULT ''`,\n\t\t);\n\t} catch {\n\t\t// Column already exists, ignore error.\n\t}\n\n\treturn db;\n}\n\n/**\n * Normalize cache key options to a consistent format Sorts exports and\n * externals to ensure consistent cache hits.\n *\n * NOTE: platform can be \"browser\", \"node\", or \"auto\" (when user doesn't specify).\n * Using \"auto\" in the cache key ensures that auto-detected results are cached\n * separately from explicitly specified platform results.\n *\n */\nexport function normalizeCacheKey(options: {\n\tpackageName: string;\n\tversion: string;\n\texports?: string[];\n\t/**\n\t * Platform: \"browser\", \"node\", or undefined (treated as \"auto\").\n\t */\n\tplatform: \"browser\" | \"node\" | undefined;\n\tgzipLevel: number;\n\texternals: string[];\n\tnoExternal: boolean;\n}): CacheKey {\n\treturn {\n\t\tpackageName: options.packageName,\n\t\tversion: options.version,\n\t\texports: (options.exports || []).slice().sort().join(\",\"),\n\t\t/**\n\t\t * Use \"auto\" when platform is undefined to distinguish from explicit\n\t\t * platform.\n\t\t */\n\t\tplatform: options.platform ?? \"auto\",\n\t\tgzipLevel: options.gzipLevel,\n\t\texternals: options.externals.slice().sort().join(\",\"),\n\t\tnoExternal: options.noExternal ? 1 : 0,\n\t};\n}\n\n/**\n * Get a cached result if it exists Returns null if not found or if an error\n * occurs (graceful degradation).\n */\nexport function getCachedResult(key: CacheKey): CachedBundleResult | null {\n\ttry {\n\t\tconst database = initCache();\n\n\t\tconst stmt = database.prepare<\n\t\t\t[string, string, string, string, number, string, number]\n\t\t>(`\n\t\t\tSELECT * FROM bundle_cache\n\t\t\tWHERE package_name = ?\n\t\t\tAND version = ?\n\t\t\tAND exports = ?\n\t\t\tAND platform = ?\n\t\t\tAND gzip_level = ?\n\t\t\tAND externals = ?\n\t\t\tAND no_external = ?\n\t\t`);\n\n\t\tconst row = stmt.get(\n\t\t\tkey.packageName,\n\t\t\tkey.version,\n\t\t\tkey.exports,\n\t\t\tkey.platform,\n\t\t\tkey.gzipLevel,\n\t\t\tkey.externals,\n\t\t\tkey.noExternal,\n\t\t) as CacheRow | undefined;\n\n\t\tif (!row) {\n\t\t\treturn null;\n\t\t}\n\n\t\t// Parse dependencies with error handling for corrupted data.\n\t\tlet dependencies: string[] = [];\n\t\ttry {\n\t\t\tdependencies = JSON.parse(row.dependencies) as string[];\n\t\t} catch {\n\t\t\t// If JSON is corrupted, use empty array.\n\t\t\tdependencies = [];\n\t\t}\n\n\t\t/**\n\t\t * Convert row to BundleResult. Use actual_externals (computed externals\n\t\t * including auto-detected react/react-dom) if available, otherwise fall back\n\t\t * to externals (for old cache entries).\n\t\t */\n\t\tconst externalsStr = row.actual_externals || row.externals;\n\t\treturn {\n\t\t\tpackageName: row.display_name,\n\t\t\tpackageVersion: row.version,\n\t\t\texports: row.exports ? row.exports.split(\",\").filter(Boolean) : [],\n\t\t\trawSize: row.raw_size,\n\t\t\tgzipSize: row.gzip_size,\n\t\t\tgzipLevel: row.gzip_level,\n\t\t\texternals: externalsStr ? externalsStr.split(\",\").filter(Boolean) : [],\n\t\t\tdependencies,\n\t\t\tplatform: row.platform as \"browser\" | \"node\",\n\t\t\tnamedExportCount: row.named_export_count ?? 0,\n\t\t};\n\t} catch {\n\t\t/**\n\t\t * On any database error, return null (graceful degradation - continue without\n\t\t * cache).\n\t\t */\n\t\treturn null;\n\t}\n}\n\n/**\n * Store a result in the cache Silently fails on errors (graceful degradation -\n * CLI continues without caching).\n */\nexport function setCachedResult(key: CacheKey, result: BundleResult): void {\n\ttry {\n\t\tconst database = initCache();\n\n\t\t/**\n\t\t * Use INSERT OR REPLACE to handle duplicates.\n\t\t * NOTE: This updates created_at on replace, making this LRU-style eviction.\n\t\t */\n\t\tconst stmt = database.prepare(`\n\t\t\tINSERT OR REPLACE INTO bundle_cache (\n\t\t\t\tpackage_name, version, exports, platform, gzip_level, externals, no_external,\n\t\t\t\traw_size, gzip_size, dependencies, display_name, created_at, named_export_count, actual_externals\n\t\t\t) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)\n\t\t`);\n\n\t\tstmt.run(\n\t\t\tkey.packageName,\n\t\t\tkey.version,\n\t\t\tkey.exports,\n\t\t\tkey.platform,\n\t\t\tkey.gzipLevel,\n\t\t\tkey.externals,\n\t\t\tkey.noExternal,\n\t\t\tresult.rawSize,\n\t\t\tresult.gzipSize,\n\t\t\tJSON.stringify(result.dependencies),\n\t\t\tresult.packageName,\n\t\t\tDate.now(),\n\t\t\tresult.namedExportCount,\n\t\t\tresult.externals.slice().sort().join(\",\"),\n\t\t);\n\n\t\t// Enforce max entries (LRU-style eviction based on created_at).\n\t\tenforceMaxEntries(database);\n\t} catch {\n\t\t// On any database error, silently continue (graceful degradation).\n\t}\n}\n\n/**\n * Enforce maximum cache entries by removing entries with oldest timestamps Uses\n * LRU-style eviction: entries that are re-checked get updated timestamps and\n * move to the end of the eviction queue.\n */\nfunction enforceMaxEntries(database: Database.Database): void {\n\tconst countResult = database\n\t\t.prepare(\"SELECT COUNT(*) as count FROM bundle_cache\")\n\t\t.get() as { count: number };\n\n\tif (countResult.count > MAX_CACHE_ENTRIES) {\n\t\tconst toDelete = countResult.count - MAX_CACHE_ENTRIES;\n\t\tdatabase\n\t\t\t.prepare(\n\t\t\t\t`\n\t\t\tDELETE FROM bundle_cache\n\t\t\tWHERE id IN (\n\t\t\t\tSELECT id FROM bundle_cache\n\t\t\t\tORDER BY created_at ASC\n\t\t\t\tLIMIT ?\n\t\t\t)\n\t\t`,\n\t\t\t)\n\t\t\t.run(toDelete);\n\t}\n}\n\n/**\n * Clear all cache entries Silently fails on errors.\n */\nexport function clearCache(): void {\n\ttry {\n\t\tconst database = initCache();\n\t\tdatabase.prepare(\"DELETE FROM bundle_cache\").run();\n\t} catch {\n\t\t// Silently ignore errors.\n\t}\n}\n\n/**\n * Get the number of cached entries Returns 0 on error.\n */\nexport function getCacheCount(): number {\n\ttry {\n\t\tconst database = initCache();\n\t\tconst result = database\n\t\t\t.prepare(\"SELECT COUNT(*) as count FROM bundle_cache\")\n\t\t\t.get() as { count: number };\n\t\treturn result.count;\n\t} catch {\n\t\treturn 0;\n\t}\n}\n\n/**\n * Close the database connection (useful for testing).\n */\nexport function closeCache(): void {\n\tif (db) {\n\t\tdb.close();\n\t\tdb = null;\n\t}\n}\n"],"names":["fs","os","path","Database","MAX_CACHE_ENTRIES","getCacheDir","join","homedir","getCacheDbPath","db","initCache","cacheDir","existsSync","mkdirSync","recursive","pragma","exec","normalizeCacheKey","options","packageName","version","exports","slice","sort","platform","gzipLevel","externals","noExternal","getCachedResult","key","database","stmt","prepare","row","get","dependencies","JSON","parse","externalsStr","actual_externals","display_name","packageVersion","split","filter","Boolean","rawSize","raw_size","gzipSize","gzip_size","gzip_level","namedExportCount","named_export_count","setCachedResult","result","run","stringify","Date","now","enforceMaxEntries","countResult","count","toDelete","clearCache","getCacheCount","closeCache","close"],"mappings":"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"}
|
package/dist/defaults.d.ts
CHANGED
|
@@ -20,5 +20,15 @@ export declare function normalizePlatform(platform: string | undefined): "browse
|
|
|
20
20
|
*/
|
|
21
21
|
export declare function isValidPlatform(platform: string | undefined): boolean;
|
|
22
22
|
export declare const TREND_VERSION_COUNT = 5;
|
|
23
|
+
/**
|
|
24
|
+
* Base packages to auto-detect for externalization. These are checked against
|
|
25
|
+
* the package's dependencies/peerDependencies.
|
|
26
|
+
*/
|
|
23
27
|
export declare const DEFAULT_EXTERNALS: string[];
|
|
28
|
+
/**
|
|
29
|
+
* Subpath externals to add when a base package is detected. When "react" is in
|
|
30
|
+
* dependencies, we also need to externalize "react/jsx-runtime" etc. because
|
|
31
|
+
* esbuild doesn't automatically externalize subpaths.
|
|
32
|
+
*/
|
|
33
|
+
export declare const EXTERNAL_SUBPATHS: Record<string, string[]>;
|
|
24
34
|
export declare const DEFAULT_REGISTRY = "https://registry.npmjs.org";
|
package/dist/defaults.js
CHANGED
|
@@ -71,10 +71,27 @@
|
|
|
71
71
|
return validValues.includes(normalized);
|
|
72
72
|
}
|
|
73
73
|
export const TREND_VERSION_COUNT = 5;
|
|
74
|
-
|
|
74
|
+
/**
|
|
75
|
+
* Base packages to auto-detect for externalization. These are checked against
|
|
76
|
+
* the package's dependencies/peerDependencies.
|
|
77
|
+
*/ export const DEFAULT_EXTERNALS = [
|
|
75
78
|
"react",
|
|
76
79
|
"react-dom"
|
|
77
80
|
];
|
|
81
|
+
/**
|
|
82
|
+
* Subpath externals to add when a base package is detected. When "react" is in
|
|
83
|
+
* dependencies, we also need to externalize "react/jsx-runtime" etc. because
|
|
84
|
+
* esbuild doesn't automatically externalize subpaths.
|
|
85
|
+
*/ export const EXTERNAL_SUBPATHS = {
|
|
86
|
+
react: [
|
|
87
|
+
"react/jsx-runtime",
|
|
88
|
+
"react/jsx-dev-runtime"
|
|
89
|
+
],
|
|
90
|
+
"react-dom": [
|
|
91
|
+
"react-dom/client",
|
|
92
|
+
"react-dom/server"
|
|
93
|
+
]
|
|
94
|
+
};
|
|
78
95
|
export const DEFAULT_REGISTRY = "https://registry.npmjs.org";
|
|
79
96
|
|
|
80
97
|
//# sourceMappingURL=defaults.js.map
|
package/dist/defaults.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/defaults.ts"],"sourcesContent":["/* istanbul ignore file */\n\nexport const defaultFlags = {\n\tboring: false,\n\tgzipLevel: 5,\n\texternal: \"\",\n\tnoExternal: false,\n\tversions: false,\n\tregistry: \"\",\n\tplatform: \"auto\",\n\tforce: false,\n};\n\n/**\n * Normalize platform aliases to canonical esbuild platform values.\n * - \"auto\" → undefined (triggers auto-detection based on package engines)\n * - Browser aliases: \"browser\", \"web\", \"desktop\", \"client\" → \"browser\"\n * - Node aliases: \"node\", \"server\", \"nodejs\", \"backend\" → \"node\"\n */\nexport function normalizePlatform(\n\tplatform: string | undefined,\n): \"browser\" | \"node\" | undefined {\n\tif (!platform) {\n\t\treturn undefined;\n\t}\n\n\tconst normalized = platform.toLowerCase().trim();\n\n\t// Auto-detect from package.json engines.\n\tif (normalized === \"auto\") {\n\t\treturn undefined;\n\t}\n\n\t// Node aliases.\n\tif ([\"node\", \"server\", \"nodejs\", \"backend\"].includes(normalized)) {\n\t\treturn \"node\";\n\t}\n\n\t// Browser aliases.\n\tif ([\"browser\", \"web\", \"desktop\", \"client\"].includes(normalized)) {\n\t\treturn \"browser\";\n\t}\n\n\t// Invalid value - will be caught by validation.\n\treturn normalized as \"browser\" | \"node\";\n}\n\n/**\n * Check if a platform value is valid (either canonical or alias).\n */\nexport function isValidPlatform(platform: string | undefined): boolean {\n\tif (platform === undefined) {\n\t\treturn true;\n\t}\n\n\tconst normalized = platform.toLowerCase().trim();\n\n\t// Empty string after trim is invalid.\n\tif (normalized === \"\") {\n\t\treturn false;\n\t}\n\tconst validValues = [\n\t\t// Auto-detect.\n\t\t\"auto\",\n\t\t// Browser.\n\t\t\"browser\",\n\t\t\"web\",\n\t\t\"desktop\",\n\t\t\"client\",\n\t\t// Node.\n\t\t\"node\",\n\t\t\"server\",\n\t\t\"nodejs\",\n\t\t\"backend\",\n\t];\n\n\treturn validValues.includes(normalized);\n}\n\nexport const TREND_VERSION_COUNT = 5;\n\nexport const DEFAULT_EXTERNALS = [\"react\", \"react-dom\"];\n\nexport const DEFAULT_REGISTRY = \"https://registry.npmjs.org\";\n"],"names":["defaultFlags","boring","gzipLevel","external","noExternal","versions","registry","platform","force","normalizePlatform","undefined","normalized","toLowerCase","trim","includes","isValidPlatform","validValues","TREND_VERSION_COUNT","DEFAULT_EXTERNALS","DEFAULT_REGISTRY"],"mappings":"AAAA,wBAAwB,GAExB,OAAO,MAAMA,eAAe;IAC3BC,QAAQ;IACRC,WAAW;IACXC,UAAU;IACVC,YAAY;IACZC,UAAU;IACVC,UAAU;IACVC,UAAU;IACVC,OAAO;AACR,EAAE;AAEF;;;;;CAKC,GACD,OAAO,SAASC,kBACfF,QAA4B;IAE5B,IAAI,CAACA,UAAU;QACd,OAAOG;IACR;IAEA,MAAMC,aAAaJ,SAASK,WAAW,GAAGC,IAAI;IAE9C,yCAAyC;IACzC,IAAIF,eAAe,QAAQ;QAC1B,OAAOD;IACR;IAEA,gBAAgB;IAChB,IAAI;QAAC;QAAQ;QAAU;QAAU;KAAU,CAACI,QAAQ,CAACH,aAAa;QACjE,OAAO;IACR;IAEA,mBAAmB;IACnB,IAAI;QAAC;QAAW;QAAO;QAAW;KAAS,CAACG,QAAQ,CAACH,aAAa;QACjE,OAAO;IACR;IAEA,gDAAgD;IAChD,OAAOA;AACR;AAEA;;CAEC,GACD,OAAO,SAASI,gBAAgBR,QAA4B;IAC3D,IAAIA,aAAaG,WAAW;QAC3B,OAAO;IACR;IAEA,MAAMC,aAAaJ,SAASK,WAAW,GAAGC,IAAI;IAE9C,sCAAsC;IACtC,IAAIF,eAAe,IAAI;QACtB,OAAO;IACR;IACA,MAAMK,cAAc;QACnB,eAAe;QACf;QACA,WAAW;QACX;QACA;QACA;QACA;QACA,QAAQ;QACR;QACA;QACA;QACA;KACA;IAED,OAAOA,YAAYF,QAAQ,CAACH;AAC7B;AAEA,OAAO,MAAMM,sBAAsB,EAAE;AAErC,OAAO,MAAMC,oBAAoB;IAAC;IAAS;CAAY,CAAC;AAExD,OAAO,MAAMC,mBAAmB,6BAA6B"}
|
|
1
|
+
{"version":3,"sources":["../src/defaults.ts"],"sourcesContent":["/* istanbul ignore file */\n\nexport const defaultFlags = {\n\tboring: false,\n\tgzipLevel: 5,\n\texternal: \"\",\n\tnoExternal: false,\n\tversions: false,\n\tregistry: \"\",\n\tplatform: \"auto\",\n\tforce: false,\n};\n\n/**\n * Normalize platform aliases to canonical esbuild platform values.\n * - \"auto\" → undefined (triggers auto-detection based on package engines)\n * - Browser aliases: \"browser\", \"web\", \"desktop\", \"client\" → \"browser\"\n * - Node aliases: \"node\", \"server\", \"nodejs\", \"backend\" → \"node\"\n */\nexport function normalizePlatform(\n\tplatform: string | undefined,\n): \"browser\" | \"node\" | undefined {\n\tif (!platform) {\n\t\treturn undefined;\n\t}\n\n\tconst normalized = platform.toLowerCase().trim();\n\n\t// Auto-detect from package.json engines.\n\tif (normalized === \"auto\") {\n\t\treturn undefined;\n\t}\n\n\t// Node aliases.\n\tif ([\"node\", \"server\", \"nodejs\", \"backend\"].includes(normalized)) {\n\t\treturn \"node\";\n\t}\n\n\t// Browser aliases.\n\tif ([\"browser\", \"web\", \"desktop\", \"client\"].includes(normalized)) {\n\t\treturn \"browser\";\n\t}\n\n\t// Invalid value - will be caught by validation.\n\treturn normalized as \"browser\" | \"node\";\n}\n\n/**\n * Check if a platform value is valid (either canonical or alias).\n */\nexport function isValidPlatform(platform: string | undefined): boolean {\n\tif (platform === undefined) {\n\t\treturn true;\n\t}\n\n\tconst normalized = platform.toLowerCase().trim();\n\n\t// Empty string after trim is invalid.\n\tif (normalized === \"\") {\n\t\treturn false;\n\t}\n\tconst validValues = [\n\t\t// Auto-detect.\n\t\t\"auto\",\n\t\t// Browser.\n\t\t\"browser\",\n\t\t\"web\",\n\t\t\"desktop\",\n\t\t\"client\",\n\t\t// Node.\n\t\t\"node\",\n\t\t\"server\",\n\t\t\"nodejs\",\n\t\t\"backend\",\n\t];\n\n\treturn validValues.includes(normalized);\n}\n\nexport const TREND_VERSION_COUNT = 5;\n\n/**\n * Base packages to auto-detect for externalization. These are checked against\n * the package's dependencies/peerDependencies.\n */\nexport const DEFAULT_EXTERNALS = [\"react\", \"react-dom\"];\n\n/**\n * Subpath externals to add when a base package is detected. When \"react\" is in\n * dependencies, we also need to externalize \"react/jsx-runtime\" etc. because\n * esbuild doesn't automatically externalize subpaths.\n */\nexport const EXTERNAL_SUBPATHS: Record<string, string[]> = {\n\treact: [\"react/jsx-runtime\", \"react/jsx-dev-runtime\"],\n\t\"react-dom\": [\"react-dom/client\", \"react-dom/server\"],\n};\n\nexport const DEFAULT_REGISTRY = \"https://registry.npmjs.org\";\n"],"names":["defaultFlags","boring","gzipLevel","external","noExternal","versions","registry","platform","force","normalizePlatform","undefined","normalized","toLowerCase","trim","includes","isValidPlatform","validValues","TREND_VERSION_COUNT","DEFAULT_EXTERNALS","EXTERNAL_SUBPATHS","react","DEFAULT_REGISTRY"],"mappings":"AAAA,wBAAwB,GAExB,OAAO,MAAMA,eAAe;IAC3BC,QAAQ;IACRC,WAAW;IACXC,UAAU;IACVC,YAAY;IACZC,UAAU;IACVC,UAAU;IACVC,UAAU;IACVC,OAAO;AACR,EAAE;AAEF;;;;;CAKC,GACD,OAAO,SAASC,kBACfF,QAA4B;IAE5B,IAAI,CAACA,UAAU;QACd,OAAOG;IACR;IAEA,MAAMC,aAAaJ,SAASK,WAAW,GAAGC,IAAI;IAE9C,yCAAyC;IACzC,IAAIF,eAAe,QAAQ;QAC1B,OAAOD;IACR;IAEA,gBAAgB;IAChB,IAAI;QAAC;QAAQ;QAAU;QAAU;KAAU,CAACI,QAAQ,CAACH,aAAa;QACjE,OAAO;IACR;IAEA,mBAAmB;IACnB,IAAI;QAAC;QAAW;QAAO;QAAW;KAAS,CAACG,QAAQ,CAACH,aAAa;QACjE,OAAO;IACR;IAEA,gDAAgD;IAChD,OAAOA;AACR;AAEA;;CAEC,GACD,OAAO,SAASI,gBAAgBR,QAA4B;IAC3D,IAAIA,aAAaG,WAAW;QAC3B,OAAO;IACR;IAEA,MAAMC,aAAaJ,SAASK,WAAW,GAAGC,IAAI;IAE9C,sCAAsC;IACtC,IAAIF,eAAe,IAAI;QACtB,OAAO;IACR;IACA,MAAMK,cAAc;QACnB,eAAe;QACf;QACA,WAAW;QACX;QACA;QACA;QACA;QACA,QAAQ;QACR;QACA;QACA;QACA;KACA;IAED,OAAOA,YAAYF,QAAQ,CAACH;AAC7B;AAEA,OAAO,MAAMM,sBAAsB,EAAE;AAErC;;;CAGC,GACD,OAAO,MAAMC,oBAAoB;IAAC;IAAS;CAAY,CAAC;AAExD;;;;CAIC,GACD,OAAO,MAAMC,oBAA8C;IAC1DC,OAAO;QAAC;QAAqB;KAAwB;IACrD,aAAa;QAAC;QAAoB;KAAmB;AACtD,EAAE;AAEF,OAAO,MAAMC,mBAAmB,6BAA6B"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { type NamedExport } from "./exports.js";
|
|
2
|
+
export type InstallPackageOptions = {
|
|
3
|
+
packageName: string;
|
|
4
|
+
version: string;
|
|
5
|
+
registry?: string;
|
|
6
|
+
};
|
|
7
|
+
export type InstallPackageResult = {
|
|
8
|
+
version: string;
|
|
9
|
+
exports: NamedExport[];
|
|
10
|
+
runtimeExports: NamedExport[];
|
|
11
|
+
};
|
|
12
|
+
/**
|
|
13
|
+
* Install a package temporarily and extract its named exports.
|
|
14
|
+
*/
|
|
15
|
+
export declare function installPackage(options: InstallPackageOptions): Promise<InstallPackageResult>;
|
|
16
|
+
/**
|
|
17
|
+
* Alias for backward compatibility.
|
|
18
|
+
*/
|
|
19
|
+
export declare const getPackageInfoAndExports: typeof installPackage;
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
import { execSync } from "node:child_process";
|
|
2
|
+
import fs from "node:fs";
|
|
3
|
+
import os from "node:os";
|
|
4
|
+
import path from "node:path";
|
|
5
|
+
import { getNamedExports } from "./exports.js";
|
|
6
|
+
/**
|
|
7
|
+
* Check if pnpm is available.
|
|
8
|
+
*/ function isPnpmAvailable() {
|
|
9
|
+
try {
|
|
10
|
+
execSync("pnpm --version", {
|
|
11
|
+
stdio: "pipe"
|
|
12
|
+
});
|
|
13
|
+
return true;
|
|
14
|
+
} catch {
|
|
15
|
+
return false;
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
// Cache the result of pnpm availability check.
|
|
19
|
+
let usePnpm = null;
|
|
20
|
+
/**
|
|
21
|
+
* Validate and sanitize a registry URL to prevent command injection.
|
|
22
|
+
*/ function validateRegistryUrl(registry) {
|
|
23
|
+
let url;
|
|
24
|
+
try {
|
|
25
|
+
url = new URL(registry);
|
|
26
|
+
} catch {
|
|
27
|
+
throw new Error(`Invalid registry URL: ${registry}. Must be a valid URL (e.g., https://registry.example.com)`);
|
|
28
|
+
}
|
|
29
|
+
if (url.protocol !== "http:" && url.protocol !== "https:") {
|
|
30
|
+
throw new Error(`Invalid registry URL protocol: ${url.protocol}. Only http: and https: are allowed`);
|
|
31
|
+
}
|
|
32
|
+
return url.toString();
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Get the install command (pnpm preferred, npm fallback).
|
|
36
|
+
*/ function getInstallCommand(registry) {
|
|
37
|
+
if (usePnpm === null) {
|
|
38
|
+
usePnpm = isPnpmAvailable();
|
|
39
|
+
}
|
|
40
|
+
let registryArg = "";
|
|
41
|
+
if (registry) {
|
|
42
|
+
const sanitizedRegistry = validateRegistryUrl(registry);
|
|
43
|
+
registryArg = ` --registry "${sanitizedRegistry}"`;
|
|
44
|
+
}
|
|
45
|
+
if (usePnpm) {
|
|
46
|
+
return `pnpm install --ignore-scripts --no-frozen-lockfile${registryArg}`;
|
|
47
|
+
}
|
|
48
|
+
return `npm install --legacy-peer-deps --ignore-scripts${registryArg}`;
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Create a temporary directory for installation.
|
|
52
|
+
*/ function createTempDir() {
|
|
53
|
+
const tmpDir = path.join(os.tmpdir(), `bundlecheck-exports-${Date.now()}`);
|
|
54
|
+
fs.mkdirSync(tmpDir, {
|
|
55
|
+
recursive: true
|
|
56
|
+
});
|
|
57
|
+
return tmpDir;
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Clean up temporary directory.
|
|
61
|
+
*/ function cleanupTempDir(tmpDir) {
|
|
62
|
+
try {
|
|
63
|
+
fs.rmSync(tmpDir, {
|
|
64
|
+
recursive: true,
|
|
65
|
+
force: true
|
|
66
|
+
});
|
|
67
|
+
} catch {
|
|
68
|
+
// Ignore cleanup errors.
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Install a package temporarily and extract its named exports.
|
|
73
|
+
*/ export async function installPackage(options) {
|
|
74
|
+
const { packageName, version, registry } = options;
|
|
75
|
+
const tmpDir = createTempDir();
|
|
76
|
+
try {
|
|
77
|
+
// Create minimal package.json.
|
|
78
|
+
const packageJson = {
|
|
79
|
+
name: "bundlecheck-exports-temp",
|
|
80
|
+
version: "1.0.0",
|
|
81
|
+
type: "module",
|
|
82
|
+
dependencies: {
|
|
83
|
+
[packageName]: version
|
|
84
|
+
}
|
|
85
|
+
};
|
|
86
|
+
fs.writeFileSync(path.join(tmpDir, "package.json"), JSON.stringify(packageJson, null, 2));
|
|
87
|
+
// Install the package.
|
|
88
|
+
const installCmd = getInstallCommand(registry);
|
|
89
|
+
execSync(installCmd, {
|
|
90
|
+
cwd: tmpDir,
|
|
91
|
+
stdio: "pipe"
|
|
92
|
+
});
|
|
93
|
+
// Read the installed version.
|
|
94
|
+
const pkgJsonPath = path.join(tmpDir, "node_modules", packageName, "package.json");
|
|
95
|
+
let installedVersion = version;
|
|
96
|
+
if (fs.existsSync(pkgJsonPath)) {
|
|
97
|
+
const pkgJson = JSON.parse(fs.readFileSync(pkgJsonPath, "utf-8"));
|
|
98
|
+
installedVersion = pkgJson.version || version;
|
|
99
|
+
}
|
|
100
|
+
// Get named exports.
|
|
101
|
+
const { exports, runtimeExports } = getNamedExports(tmpDir, packageName);
|
|
102
|
+
return {
|
|
103
|
+
version: installedVersion,
|
|
104
|
+
exports,
|
|
105
|
+
runtimeExports
|
|
106
|
+
};
|
|
107
|
+
} finally{
|
|
108
|
+
cleanupTempDir(tmpDir);
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
/**
|
|
112
|
+
* Alias for backward compatibility.
|
|
113
|
+
*/ export const getPackageInfoAndExports = installPackage;
|
|
114
|
+
|
|
115
|
+
//# sourceMappingURL=exports-installer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/exports-installer.ts"],"sourcesContent":["import { execSync } from \"node:child_process\";\nimport fs from \"node:fs\";\nimport os from \"node:os\";\nimport path from \"node:path\";\nimport { getNamedExports, type NamedExport } from \"./exports.js\";\n\n/**\n * Check if pnpm is available.\n */\nfunction isPnpmAvailable(): boolean {\n\ttry {\n\t\texecSync(\"pnpm --version\", { stdio: \"pipe\" });\n\t\treturn true;\n\t} catch {\n\t\treturn false;\n\t}\n}\n\n// Cache the result of pnpm availability check.\nlet usePnpm: boolean | null = null;\n\n/**\n * Validate and sanitize a registry URL to prevent command injection.\n */\nfunction validateRegistryUrl(registry: string): string {\n\tlet url: URL;\n\ttry {\n\t\turl = new URL(registry);\n\t} catch {\n\t\tthrow new Error(\n\t\t\t`Invalid registry URL: ${registry}. Must be a valid URL (e.g., https://registry.example.com)`,\n\t\t);\n\t}\n\n\tif (url.protocol !== \"http:\" && url.protocol !== \"https:\") {\n\t\tthrow new Error(\n\t\t\t`Invalid registry URL protocol: ${url.protocol}. Only http: and https: are allowed`,\n\t\t);\n\t}\n\n\treturn url.toString();\n}\n\n/**\n * Get the install command (pnpm preferred, npm fallback).\n */\nfunction getInstallCommand(registry?: string): string {\n\tif (usePnpm === null) {\n\t\tusePnpm = isPnpmAvailable();\n\t}\n\n\tlet registryArg = \"\";\n\tif (registry) {\n\t\tconst sanitizedRegistry = validateRegistryUrl(registry);\n\t\tregistryArg = ` --registry \"${sanitizedRegistry}\"`;\n\t}\n\n\tif (usePnpm) {\n\t\treturn `pnpm install --ignore-scripts --no-frozen-lockfile${registryArg}`;\n\t}\n\treturn `npm install --legacy-peer-deps --ignore-scripts${registryArg}`;\n}\n\n/**\n * Create a temporary directory for installation.\n */\nfunction createTempDir(): string {\n\tconst tmpDir = path.join(os.tmpdir(), `bundlecheck-exports-${Date.now()}`);\n\tfs.mkdirSync(tmpDir, { recursive: true });\n\treturn tmpDir;\n}\n\n/**\n * Clean up temporary directory.\n */\nfunction cleanupTempDir(tmpDir: string): void {\n\ttry {\n\t\tfs.rmSync(tmpDir, { recursive: true, force: true });\n\t} catch {\n\t\t// Ignore cleanup errors.\n\t}\n}\n\nexport type InstallPackageOptions = {\n\tpackageName: string;\n\tversion: string;\n\tregistry?: string;\n};\n\nexport type InstallPackageResult = {\n\tversion: string;\n\texports: NamedExport[];\n\truntimeExports: NamedExport[];\n};\n\n/**\n * Install a package temporarily and extract its named exports.\n */\nexport async function installPackage(\n\toptions: InstallPackageOptions,\n): Promise<InstallPackageResult> {\n\tconst { packageName, version, registry } = options;\n\tconst tmpDir = createTempDir();\n\n\ttry {\n\t\t// Create minimal package.json.\n\t\tconst packageJson = {\n\t\t\tname: \"bundlecheck-exports-temp\",\n\t\t\tversion: \"1.0.0\",\n\t\t\ttype: \"module\",\n\t\t\tdependencies: {\n\t\t\t\t[packageName]: version,\n\t\t\t},\n\t\t};\n\n\t\tfs.writeFileSync(\n\t\t\tpath.join(tmpDir, \"package.json\"),\n\t\t\tJSON.stringify(packageJson, null, 2),\n\t\t);\n\n\t\t// Install the package.\n\t\tconst installCmd = getInstallCommand(registry);\n\t\texecSync(installCmd, {\n\t\t\tcwd: tmpDir,\n\t\t\tstdio: \"pipe\",\n\t\t});\n\n\t\t// Read the installed version.\n\t\tconst pkgJsonPath = path.join(\n\t\t\ttmpDir,\n\t\t\t\"node_modules\",\n\t\t\tpackageName,\n\t\t\t\"package.json\",\n\t\t);\n\t\tlet installedVersion = version;\n\t\tif (fs.existsSync(pkgJsonPath)) {\n\t\t\tconst pkgJson = JSON.parse(fs.readFileSync(pkgJsonPath, \"utf-8\"));\n\t\t\tinstalledVersion = pkgJson.version || version;\n\t\t}\n\n\t\t// Get named exports.\n\t\tconst { exports, runtimeExports } = getNamedExports(tmpDir, packageName);\n\n\t\treturn {\n\t\t\tversion: installedVersion,\n\t\t\texports,\n\t\t\truntimeExports,\n\t\t};\n\t} finally {\n\t\tcleanupTempDir(tmpDir);\n\t}\n}\n\n/**\n * Alias for backward compatibility.\n */\nexport const getPackageInfoAndExports = installPackage;\n"],"names":["execSync","fs","os","path","getNamedExports","isPnpmAvailable","stdio","usePnpm","validateRegistryUrl","registry","url","URL","Error","protocol","toString","getInstallCommand","registryArg","sanitizedRegistry","createTempDir","tmpDir","join","tmpdir","Date","now","mkdirSync","recursive","cleanupTempDir","rmSync","force","installPackage","options","packageName","version","packageJson","name","type","dependencies","writeFileSync","JSON","stringify","installCmd","cwd","pkgJsonPath","installedVersion","existsSync","pkgJson","parse","readFileSync","exports","runtimeExports","getPackageInfoAndExports"],"mappings":"AAAA,SAASA,QAAQ,QAAQ,qBAAqB;AAC9C,OAAOC,QAAQ,UAAU;AACzB,OAAOC,QAAQ,UAAU;AACzB,OAAOC,UAAU,YAAY;AAC7B,SAASC,eAAe,QAA0B,eAAe;AAEjE;;CAEC,GACD,SAASC;IACR,IAAI;QACHL,SAAS,kBAAkB;YAAEM,OAAO;QAAO;QAC3C,OAAO;IACR,EAAE,OAAM;QACP,OAAO;IACR;AACD;AAEA,+CAA+C;AAC/C,IAAIC,UAA0B;AAE9B;;CAEC,GACD,SAASC,oBAAoBC,QAAgB;IAC5C,IAAIC;IACJ,IAAI;QACHA,MAAM,IAAIC,IAAIF;IACf,EAAE,OAAM;QACP,MAAM,IAAIG,MACT,CAAC,sBAAsB,EAAEH,SAAS,0DAA0D,CAAC;IAE/F;IAEA,IAAIC,IAAIG,QAAQ,KAAK,WAAWH,IAAIG,QAAQ,KAAK,UAAU;QAC1D,MAAM,IAAID,MACT,CAAC,+BAA+B,EAAEF,IAAIG,QAAQ,CAAC,mCAAmC,CAAC;IAErF;IAEA,OAAOH,IAAII,QAAQ;AACpB;AAEA;;CAEC,GACD,SAASC,kBAAkBN,QAAiB;IAC3C,IAAIF,YAAY,MAAM;QACrBA,UAAUF;IACX;IAEA,IAAIW,cAAc;IAClB,IAAIP,UAAU;QACb,MAAMQ,oBAAoBT,oBAAoBC;QAC9CO,cAAc,CAAC,aAAa,EAAEC,kBAAkB,CAAC,CAAC;IACnD;IAEA,IAAIV,SAAS;QACZ,OAAO,CAAC,kDAAkD,EAAES,aAAa;IAC1E;IACA,OAAO,CAAC,+CAA+C,EAAEA,aAAa;AACvE;AAEA;;CAEC,GACD,SAASE;IACR,MAAMC,SAAShB,KAAKiB,IAAI,CAAClB,GAAGmB,MAAM,IAAI,CAAC,oBAAoB,EAAEC,KAAKC,GAAG,IAAI;IACzEtB,GAAGuB,SAAS,CAACL,QAAQ;QAAEM,WAAW;IAAK;IACvC,OAAON;AACR;AAEA;;CAEC,GACD,SAASO,eAAeP,MAAc;IACrC,IAAI;QACHlB,GAAG0B,MAAM,CAACR,QAAQ;YAAEM,WAAW;YAAMG,OAAO;QAAK;IAClD,EAAE,OAAM;IACP,yBAAyB;IAC1B;AACD;AAcA;;CAEC,GACD,OAAO,eAAeC,eACrBC,OAA8B;IAE9B,MAAM,EAAEC,WAAW,EAAEC,OAAO,EAAEvB,QAAQ,EAAE,GAAGqB;IAC3C,MAAMX,SAASD;IAEf,IAAI;QACH,+BAA+B;QAC/B,MAAMe,cAAc;YACnBC,MAAM;YACNF,SAAS;YACTG,MAAM;YACNC,cAAc;gBACb,CAACL,YAAY,EAAEC;YAChB;QACD;QAEA/B,GAAGoC,aAAa,CACflC,KAAKiB,IAAI,CAACD,QAAQ,iBAClBmB,KAAKC,SAAS,CAACN,aAAa,MAAM;QAGnC,uBAAuB;QACvB,MAAMO,aAAazB,kBAAkBN;QACrCT,SAASwC,YAAY;YACpBC,KAAKtB;YACLb,OAAO;QACR;QAEA,8BAA8B;QAC9B,MAAMoC,cAAcvC,KAAKiB,IAAI,CAC5BD,QACA,gBACAY,aACA;QAED,IAAIY,mBAAmBX;QACvB,IAAI/B,GAAG2C,UAAU,CAACF,cAAc;YAC/B,MAAMG,UAAUP,KAAKQ,KAAK,CAAC7C,GAAG8C,YAAY,CAACL,aAAa;YACxDC,mBAAmBE,QAAQb,OAAO,IAAIA;QACvC;QAEA,qBAAqB;QACrB,MAAM,EAAEgB,OAAO,EAAEC,cAAc,EAAE,GAAG7C,gBAAgBe,QAAQY;QAE5D,OAAO;YACNC,SAASW;YACTK;YACAC;QACD;IACD,SAAU;QACTvB,eAAeP;IAChB;AACD;AAEA;;CAEC,GACD,OAAO,MAAM+B,2BAA2BrB,eAAe"}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Represents a named export from a package.
|
|
3
|
+
*/
|
|
4
|
+
export type NamedExport = {
|
|
5
|
+
/**
|
|
6
|
+
* The export name (e.g., "Button", "useState").
|
|
7
|
+
*/
|
|
8
|
+
name: string;
|
|
9
|
+
/**
|
|
10
|
+
* The type of export.
|
|
11
|
+
*/
|
|
12
|
+
kind: "function" | "class" | "const" | "type" | "interface" | "enum" | "unknown";
|
|
13
|
+
};
|
|
14
|
+
/**
|
|
15
|
+
* Result from parsing package exports.
|
|
16
|
+
*/
|
|
17
|
+
export type ParsedExports = {
|
|
18
|
+
/**
|
|
19
|
+
* Array of all named exports found in the package (including types).
|
|
20
|
+
*/
|
|
21
|
+
exports: NamedExport[];
|
|
22
|
+
/**
|
|
23
|
+
* Total count of all named exports (including types).
|
|
24
|
+
*/
|
|
25
|
+
count: number;
|
|
26
|
+
/**
|
|
27
|
+
* Array of runtime exports only (excluding types and interfaces).
|
|
28
|
+
*/
|
|
29
|
+
runtimeExports: NamedExport[];
|
|
30
|
+
/**
|
|
31
|
+
* Count of runtime exports only (functions, classes, const, enums).
|
|
32
|
+
*/
|
|
33
|
+
runtimeCount: number;
|
|
34
|
+
};
|
|
35
|
+
/**
|
|
36
|
+
* Get named exports from an installed package by parsing its type definitions.
|
|
37
|
+
*
|
|
38
|
+
* @param tmpDir - The temporary directory where the package is installed
|
|
39
|
+
* @param packageName - The package name (e.g., "@mantine/core")
|
|
40
|
+
* @returns ParsedExports containing the list of exports and count
|
|
41
|
+
*
|
|
42
|
+
*/
|
|
43
|
+
export declare function getNamedExports(tmpDir: string, packageName: string): ParsedExports;
|