@nuggetwise/cli
Version:
Magic Nuggetwise CLI for Cursor IDE integration
195 lines (161 loc) • 5.43 kB
text/typescript
import { exec } from 'child_process';
import { promisify } from 'util';
import fs from 'fs-extra';
import path from 'path';
import { BuildResult, PreviewResult } from '../types/index.js';
const execAsync = promisify(exec);
export class PreviewManager {
private workspace: string;
private buildTool: 'next' | 'vite' | 'esbuild';
constructor(workspace: string, buildTool: 'next' | 'vite' | 'esbuild' = 'next') {
this.workspace = workspace;
this.buildTool = buildTool;
}
async buildProject(): Promise<BuildResult> {
const startTime = Date.now();
try {
console.log(`Building project with ${this.buildTool}...`);
let buildCommand: string;
let outputDir: string;
switch (this.buildTool) {
case 'next':
buildCommand = 'npm run build';
outputDir = '.next';
break;
case 'vite':
buildCommand = 'npm run build';
outputDir = 'dist';
break;
case 'esbuild':
buildCommand = 'npx esbuild src/**/*.{ts,tsx} --bundle --outdir=dist --format=esm --target=es2020';
outputDir = 'dist';
break;
default:
throw new Error(`Unsupported build tool: ${this.buildTool}`);
}
const { stdout, stderr } = await execAsync(buildCommand, {
cwd: this.workspace,
timeout: 60000 // 60 second timeout
});
const buildTime = Date.now() - startTime;
if (stderr && !stderr.includes('warning')) {
return {
success: false,
error: stderr,
buildTime
};
}
// Verify build output exists
const outputPath = path.join(this.workspace, outputDir);
if (!await fs.pathExists(outputPath)) {
return {
success: false,
error: `Build output not found at ${outputDir}`,
buildTime
};
}
return {
success: true,
output: stdout,
buildTime
};
} catch (error: any) {
const buildTime = Date.now() - startTime;
return {
success: false,
error: error.message || 'Build failed',
buildTime
};
}
}
async deployPreview(): Promise<PreviewResult> {
try {
console.log('Deploying preview to Vercel...');
// Check if Vercel CLI is available
try {
await execAsync('vercel --version');
} catch (error) {
throw new Error('Vercel CLI not found. Please install with: npm i -g vercel');
}
// Deploy to Vercel
const { stdout, stderr } = await execAsync('vercel deploy --prebuilt --prod false --yes', {
cwd: this.workspace,
timeout: 120000 // 2 minute timeout
});
if (stderr && !stderr.includes('warning')) {
return {
success: false,
error: stderr
};
}
// Extract deployment URL from output
const urlMatch = stdout.match(/https:\/\/[^\s]+/);
const deploymentIdMatch = stdout.match(/Deployment ID: ([^\s]+)/);
if (!urlMatch) {
return {
success: false,
error: 'Could not extract deployment URL from Vercel output'
};
}
return {
success: true,
url: urlMatch[0],
deploymentId: deploymentIdMatch?.[1]
};
} catch (error: any) {
return {
success: false,
error: error.message || 'Deployment failed'
};
}
}
async buildAndDeploy(): Promise<PreviewResult> {
// First build the project
const buildResult = await this.buildProject();
if (!buildResult.success) {
return {
success: false,
error: `Build failed: ${buildResult.error}`
};
}
console.log(`Build completed in ${buildResult.buildTime}ms`);
// Then deploy the preview
return await this.deployPreview();
}
async detectBuildTool(): Promise<'next' | 'vite' | 'esbuild'> {
// Check for Next.js
if (await fs.pathExists(path.join(this.workspace, 'next.config.js')) ||
await fs.pathExists(path.join(this.workspace, 'next.config.ts'))) {
return 'next';
}
// Check for Vite
if (await fs.pathExists(path.join(this.workspace, 'vite.config.js')) ||
await fs.pathExists(path.join(this.workspace, 'vite.config.ts'))) {
return 'vite';
}
// Check package.json for build scripts
const packageJsonPath = path.join(this.workspace, 'package.json');
if (await fs.pathExists(packageJsonPath)) {
const packageJson = await fs.readJson(packageJsonPath);
const scripts = packageJson.scripts || {};
if (scripts.build && scripts.build.includes('next')) {
return 'next';
}
if (scripts.build && scripts.build.includes('vite')) {
return 'vite';
}
}
// Default to esbuild
return 'esbuild';
}
async setupBuildEnvironment(): Promise<void> {
// Install dependencies if node_modules doesn't exist
const nodeModulesPath = path.join(this.workspace, 'node_modules');
if (!await fs.pathExists(nodeModulesPath)) {
console.log('Installing dependencies...');
await execAsync('npm install', { cwd: this.workspace });
}
// Update build tool detection
this.buildTool = await this.detectBuildTool();
}
}