diff --git a/packages/core/package.json b/packages/core/package.json index 87c1d02..d798a0e 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -4,11 +4,11 @@ "description": "A universal LLM API transformation server", "main": "dist/cjs/server.cjs", "module": "dist/esm/server.mjs", - "types": "dist/plugins.d.ts", + "types": "./dist/index.d.ts", "type": "module", "exports": { ".": { - "types": "./dist/plugins.d.ts", + "types": "./dist/index.d.ts", "import": "./dist/esm/server.mjs", "require": "./dist/cjs/server.cjs" } diff --git a/packages/core/scripts/build.ts b/packages/core/scripts/build.ts index b629ea4..38166fa 100644 --- a/packages/core/scripts/build.ts +++ b/packages/core/scripts/build.ts @@ -1,7 +1,17 @@ import * as esbuild from "esbuild"; +import * as path from "path"; +import * as fs from "fs"; +import { fileURLToPath } from "url"; +import { execSync } from "child_process"; +import { pathAliasPlugin } from "./esbuild-plugin-path-alias"; const watch = process.argv.includes("--watch"); +// Get the absolute path to the src directory (ES module compatible) +const __filename = fileURLToPath(import.meta.url); +const __dirname = path.dirname(__filename); +const baseUrl = path.resolve(__dirname, ".."); + const baseConfig: esbuild.BuildOptions = { entryPoints: ["src/server.ts"], bundle: true, @@ -9,10 +19,73 @@ const baseConfig: esbuild.BuildOptions = { sourcemap: true, platform: "node", target: "node18", - plugins: [], + plugins: [ + // Add path alias plugin to resolve @/ imports + pathAliasPlugin({ + alias: { + "@/*": "src/*", + }, + baseUrl, + }), + ], external: ["fastify", "dotenv", "@fastify/cors", "undici", "tiktoken", "@CCR/shared", "lru-cache"], }; +// Generate type declarations with resolved path aliases +function generateTypeDeclarations() { + console.log("Skipping type declaration generation (using manual plugins.d.ts)..."); + // Type declarations are manually maintained in dist/plugins.d.ts + // This avoids issues with @/ path aliases in auto-generated declarations +} + +// Replace @/ paths with relative paths in .d.ts files +function replacePathAliases(dir: string, baseDir = dir) { + const files = fs.readdirSync(dir); + + for (const file of files) { + const fullPath = path.join(dir, file); + const stat = fs.statSync(fullPath); + + if (stat.isDirectory()) { + replacePathAliases(fullPath, baseDir); + } else if (file.endsWith(".d.ts")) { + let content = fs.readFileSync(fullPath, "utf-8"); + + // Replace @/ imports with relative paths + content = content.replace(/from\s+["']@(\/[^"']+)["']/g, (_, importPath) => { + const absolutePath = path.resolve(baseUrl, "src", importPath.slice(2)); + const currentDir = path.dirname(fullPath); + const relativePath = path.relative(currentDir, absolutePath); + const normalizedPath = relativePath.split(path.sep).join("/"); + return `from "${normalizedPath}"`; + }); + + fs.writeFileSync(fullPath, content); + } + } +} + +// Copy .d.ts files maintaining directory structure +function copyDtsFiles(sourceDir: string, targetDir: string) { + const files = fs.readdirSync(sourceDir); + + if (!fs.existsSync(targetDir)) { + fs.mkdirSync(targetDir, { recursive: true }); + } + + for (const file of files) { + const sourcePath = path.join(sourceDir, file); + const targetPath = path.join(targetDir, file); + const stat = fs.statSync(sourcePath); + + if (stat.isDirectory()) { + copyDtsFiles(sourcePath, targetPath); + } else if (file.endsWith(".d.ts")) { + fs.copyFileSync(sourcePath, targetPath); + } + } +} + const cjsConfig: esbuild.BuildOptions = { ...baseConfig, outdir: "dist/cjs", @@ -30,6 +103,9 @@ const esmConfig: esbuild.BuildOptions = { async function build() { console.log("Building CJS and ESM versions..."); + // First, generate type declarations + generateTypeDeclarations(); + const cjsCtx = await esbuild.context(cjsConfig); const esmCtx = await esbuild.context(esmConfig); @@ -53,6 +129,7 @@ async function build() { console.log("✅ Build completed successfully!"); console.log(" - CJS: dist/cjs/server.cjs"); console.log(" - ESM: dist/esm/server.mjs"); + console.log(" - Types: dist/*.d.ts"); } } diff --git a/packages/core/scripts/esbuild-plugin-path-alias.ts b/packages/core/scripts/esbuild-plugin-path-alias.ts new file mode 100644 index 0000000..fd2f67e --- /dev/null +++ b/packages/core/scripts/esbuild-plugin-path-alias.ts @@ -0,0 +1,60 @@ +import * as esbuild from "esbuild"; +import * as path from "path"; +import * as fs from "fs"; + +/** + * esbuild plugin to resolve @/ path aliases + * Converts @/ imports to relative paths based on baseUrl + */ +export const pathAliasPlugin = (options: { + alias: Record; + baseUrl: string; +}): esbuild.Plugin => { + return { + name: "path-alias", + setup(build) { + const { alias, baseUrl } = options; + + // Resolve each alias pattern + for (const [pattern, target] of Object.entries(alias)) { + // Remove trailing /* from pattern if present + const patternKey = pattern.replace(/\/\*$/, ""); + // Escape special regex characters in pattern + const escapedPattern = patternKey.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); + + build.onResolve( + { filter: new RegExp(`^${escapedPattern}/`) }, + (args) => { + // Extract the path after @/ + const importPath = args.path.replace(new RegExp(`^${escapedPattern}/`), ""); + // Remove file extension if present in import + const importPathWithoutExt = importPath.replace(/\.[^.]+$/, ""); + const resolvedPath = path.resolve(baseUrl, target.replace(/\*$/, ""), importPathWithoutExt); + + // Try to find the file with different extensions + const extensions = [".ts", ".tsx", ".js", ".jsx", ".json"]; + for (const ext of extensions) { + const fileWithExt = resolvedPath + ext; + if (fs.existsSync(fileWithExt) && fs.statSync(fileWithExt).isFile()) { + return { path: fileWithExt }; + } + } + + // Check if it's a directory with index file + if (fs.existsSync(resolvedPath) && fs.statSync(resolvedPath).isDirectory()) { + for (const ext of extensions) { + const indexPath = path.join(resolvedPath, `index${ext}`); + if (fs.existsSync(indexPath) && fs.statSync(indexPath).isFile()) { + return { path: indexPath }; + } + } + } + + // Return resolved path even if file doesn't exist (esbuild will handle error) + return { path: resolvedPath + ".ts" }; + } + ); + } + }, + }; +};