UNPKG

@pulzar/cli

Version:

Ultimate command-line interface for Pulzar framework - scaffolding, development server, building, testing, code generation, health diagnostics, security auditing, and deployment tools for modern Node.js applications

273 lines 9.21 kB
import { logger } from "../utils/logger"; export async function buildCommand(options) { try { logger.info("Starting build process...", { options }); // 1. Check if pulzar.config.ts exists const configPath = await findConfigFile(); if (!configPath) { logger.error("No pulzar.config.ts found. Run 'pulzar init' first."); process.exit(1); } logger.info("Found configuration file", { path: configPath }); // 2. Load configuration const config = await loadConfig(configPath); // 3. Setup output directory await setupOutputDirectory(options.out); // 4. Build TypeScript files await buildTypeScript(config, options); // 5. Generate DI container if needed await generateDIContainer(config); // 6. Bundle with esbuild if specified if (options.edge || config.edge) { await buildForEdge(config, options); } else { await bundleApplication(config, options); } // 7. Copy static files await copyStaticFiles(config, options.out); // 8. Generate package.json for output await generateOutputPackageJson(config, options); logger.info("Build completed successfully", { outputDir: options.out, edge: options.edge, minified: options.minify, }); } catch (error) { logger.error("Build failed", { error }); process.exit(1); } } async function findConfigFile() { const possiblePaths = [ "pulzar.config.ts", "pulzar.config.js", "src/pulzar.config.ts", "src/pulzar.config.js", ]; for (const path of possiblePaths) { try { const fs = await import("fs/promises"); await fs.access(path); return path; } catch { continue; } } return null; } async function loadConfig(configPath) { try { // Dynamic import of config file const config = await import(process.cwd() + "/" + configPath); return config.default || config; } catch (error) { logger.error("Failed to load configuration", { configPath, error }); return { entry: "src/main.ts", outDir: "dist", target: "node", minify: false, bundle: true, }; } } async function setupOutputDirectory(outDir) { try { const fs = await import("fs/promises"); const path = await import("path"); const absoluteOut = path.resolve(outDir); // Create output directory await fs.mkdir(absoluteOut, { recursive: true }); // Clean existing files const files = await fs.readdir(absoluteOut); for (const file of files) { const filePath = path.join(absoluteOut, file); const stat = await fs.stat(filePath); if (stat.isDirectory()) { await fs.rmdir(filePath, { recursive: true }); } else { await fs.unlink(filePath); } } logger.info("Output directory prepared", { path: absoluteOut }); } catch (error) { logger.error("Failed to setup output directory", { outDir, error }); throw error; } } async function buildTypeScript(config, options) { try { // Try to use esbuild for fast compilation const esbuild = await tryImport("esbuild"); if (esbuild) { const buildOptions = { entryPoints: [config.entry || "src/main.ts"], outdir: options.out, platform: config.target === "edge" ? "neutral" : "node", target: config.target === "edge" ? "es2022" : "node18", format: "esm", bundle: config.bundle !== false, minify: options.minify, sourcemap: !options.minify, external: config.external || [], define: { "process.env.NODE_ENV": `"${process.env.NODE_ENV || "production"}"`, }, }; const result = await esbuild.build(buildOptions); if (result.errors.length > 0) { throw new Error(`Build errors: ${result.errors.map((e) => e.text).join(", ")}`); } logger.info("TypeScript compilation completed", { entryPoints: buildOptions.entryPoints.length, platform: buildOptions.platform, }); } else { // Fallback to tsc if esbuild not available logger.warn("esbuild not available, using tsc fallback"); await buildWithTSC(config, options); } } catch (error) { logger.error("TypeScript compilation failed", { error }); throw error; } } async function buildWithTSC(config, options) { const { spawn } = await import("child_process"); const path = await import("path"); return new Promise((resolve, reject) => { const tscPath = path.join(process.cwd(), "node_modules", ".bin", "tsc"); const args = [ "--outDir", options.out, "--target", "ES2022", "--module", "ESNext", "--moduleResolution", "node", "--esModuleInterop", "--allowSyntheticDefaultImports", "--strict", "--skipLibCheck", ]; const tsc = spawn(tscPath, args, { stdio: "inherit" }); tsc.on("close", (code) => { if (code === 0) { resolve(); } else { reject(new Error(`tsc exited with code ${code}`)); } }); tsc.on("error", reject); }); } async function generateDIContainer(config) { try { // Import and run DI build command const { buildDI } = await import("./build-di"); await buildDI({ sourceDir: config.src || "src", outputFile: config.diOut || "src/generated/di-container.ts", watch: false, }); logger.info("DI container generated"); } catch (error) { logger.warn("Failed to generate DI container", { error }); // Non-fatal error } } async function buildForEdge(config, options) { logger.info("Building for edge runtime..."); // Edge-specific optimizations const esbuild = await tryImport("esbuild"); if (esbuild) { await esbuild.build({ entryPoints: [config.entry || "src/main.ts"], outfile: `${options.out}/index.js`, platform: "neutral", target: "es2022", format: "esm", bundle: true, minify: true, treeShaking: true, external: [], define: { "process.env.NODE_ENV": '"production"', "process.env.EDGE_RUNTIME": '"1"', }, banner: { js: "// Pulzar Edge Runtime Build", }, }); logger.info("Edge runtime build completed"); } else { logger.warn("esbuild required for edge builds"); } } async function bundleApplication(config, options) { if (!config.bundle) { logger.info("Bundling disabled, skipping..."); return; } logger.info("Bundling application..."); // Application bundling logic would go here } async function copyStaticFiles(config, outDir) { const staticDirs = config.static || ["public", "assets"]; const fs = await import("fs/promises"); const path = await import("path"); for (const staticDir of staticDirs) { try { await fs.access(staticDir); const dest = path.join(outDir, path.basename(staticDir)); await fs.cp(staticDir, dest, { recursive: true }); logger.info("Static files copied", { from: staticDir, to: dest }); } catch { // Static directory doesn't exist, skip } } } async function generateOutputPackageJson(config, options) { const fs = await import("fs/promises"); const path = await import("path"); try { // Read existing package.json const packageJson = JSON.parse(await fs.readFile("package.json", "utf-8")); // Create output package.json const outputPackage = { name: packageJson.name, version: packageJson.version, type: "module", main: "index.js", dependencies: config.bundleDependencies ? {} : packageJson.dependencies, engines: packageJson.engines, }; await fs.writeFile(path.join(options.out, "package.json"), JSON.stringify(outputPackage, null, 2)); logger.info("Output package.json generated"); } catch (error) { logger.warn("Failed to generate output package.json", { error }); } } async function tryImport(moduleName) { try { return await import(moduleName); } catch { return null; } } //# sourceMappingURL=build.js.map