UNPKG

@ordojs/cli

Version:

Command-line interface for OrdoJS framework

348 lines (331 loc) 12.2 kB
/** * @fileoverview OrdoJS CLI - Netlify deployment adapter * Adapter for deploying to Netlify */ import path from 'path'; import { AssetOptimizer, OptimizationResults } from '../../asset-optimizer.js'; import { mkdir, writeFile } from '../../fs.js'; import { logger } from '../../logger.js'; import { DeploymentAdapter, DeploymentConfig, DeploymentResult } from '../adapter-interface.js'; /** * Netlify deployment adapter */ export class NetlifyAdapter { /** * Adapter name */ name = 'netlify'; /** * Adapter description */ description = 'Deploy to Netlify'; /** * Validate Netlify deployment configuration * @param config Deployment configuration * @returns Validation result */ validateConfig(config) { const errors = []; // Check required fields if (!config.outputDir) { errors.push('outputDir is required'); } // Validate Netlify-specific settings const netlifyConfig = config; if (netlifyConfig.settings) { // Validate site name if provided if (netlifyConfig.settings.siteName && !/^[a-z0-9-]+$/.test(netlifyConfig.settings.siteName)) { errors.push('Site name must contain only lowercase letters, numbers, and hyphens'); } } return { valid: errors.length === 0, errors: errors.length > 0 ? errors : undefined }; } /** * Prepare Netlify deployment * @param config Deployment configuration * @returns Deployment result */ async prepareDeployment(config) { const netlifyConfig = config; const generatedFiles = []; try { // Create netlify.toml configuration const netlifyTomlContent = this.generateNetlifyConfig(netlifyConfig); const netlifyTomlPath = path.join(config.outputDir, 'netlify.toml'); await writeFile(netlifyTomlPath, netlifyTomlContent); generatedFiles.push({ path: netlifyTomlPath, content: netlifyTomlContent }); // Create build output directory if it doesn't exist await mkdir(config.outputDir, { recursive: true }); // Generate _redirects file if (config.redirects && config.redirects.length > 0) { const redirectsContent = this.generateRedirectsFile(config.redirects); const redirectsPath = path.join(config.outputDir, '_redirects'); await writeFile(redirectsPath, redirectsContent); generatedFiles.push({ path: redirectsPath, content: redirectsContent }); } // Generate _headers file if (config.headers && config.headers.length > 0) { const headersContent = this.generateHeadersFile(config.headers); const headersPath = path.join(config.outputDir, '_headers'); await writeFile(headersPath, headersContent); generatedFiles.push({ path: headersPath, content: headersContent }); } // Generate server functions if needed if (config.includeServerFunctions) { await this.generateServerFunctions(config); } // Optimize assets for Netlify const optimizationResults = await this.optimizeForDeployment(config, config.outputDir); return { success: true, generatedFiles, instructions: this.getDeploymentInstructions(netlifyConfig), optimizationResults }; } catch (error) { logger.error(`Netlify deployment preparation failed: ${error instanceof Error ? error.message : String(error)}`); return { success: false, error: `Netlify deployment preparation failed: ${error instanceof Error ? error.message : String(error)}`, generatedFiles, instructions: 'An error occurred during Netlify deployment preparation. Please check the logs for details.' }; } } /** * Generate Netlify configuration * @param config Netlify configuration * @returns Netlify configuration TOML */ generateNetlifyConfig(config) { const siteName = config.settings?.siteName || 'ordojs-app'; const publishDir = config.outputDir || 'dist'; const functionsDir = config.includeServerFunctions ? 'functions' : undefined; let toml = `# Netlify configuration for OrdoJS [build] publish = "${publishDir}" command = "ordojs build --production" ${functionsDir ? ` functions = "${functionsDir}"` : ''} [dev] framework = "ordojs" command = "ordojs dev" port = 8888 targetPort = 3000 publish = "${publishDir}" ${functionsDir ? ` functions = "${functionsDir}"` : ''} `; // Add environment variables if (config.env && Object.keys(config.env).length > 0) { toml += '[build.environment]\n'; for (const [key, value] of Object.entries(config.env)) { toml += ` ${key} = "${value}"\n`; } toml += '\n'; } // Add headers if (config.headers && config.headers.length > 0) { for (const header of config.headers) { toml += `[[headers]]\n`; toml += ` for = "${header.source}"\n`; toml += ` [headers.values]\n`; for (const h of header.headers) { toml += ` ${h.key} = "${h.value}"\n`; } toml += '\n'; } } // Add redirects if (config.redirects && config.redirects.length > 0) { for (const redirect of config.redirects) { toml += `[[redirects]]\n`; toml += ` from = "${redirect.source}"\n`; toml += ` to = "${redirect.destination}"\n`; toml += ` status = ${redirect.permanent ? 301 : 302}\n`; toml += ` force = true\n\n`; } } // Add plugins toml += `[plugins]\n`; if (config.settings?.useAnalytics) { toml += ` package = "@netlify/plugin-analytics"\n\n`; } // Add site settings toml += `[build.processing]\n`; toml += ` skip_processing = false\n`; toml += `[build.processing.css]\n`; toml += ` bundle = true\n`; toml += ` minify = true\n`; toml += `[build.processing.js]\n`; toml += ` bundle = true\n`; toml += ` minify = true\n`; toml += `[build.processing.html]\n`; toml += ` pretty_urls = true\n`; toml += `[build.processing.images]\n`; toml += ` compress = true\n`; return toml; } /** * Generate Netlify _redirects file * @param redirects Redirects configuration * @returns Redirects file content */ generateRedirectsFile(redirects) { return redirects.map(redirect => { return `${redirect.source} ${redirect.destination} ${redirect.permanent ? 301 : 302}`; }).join('\n'); } /** * Generate Netlify _headers file * @param headers Headers configuration * @returns Headers file content */ generateHeadersFile(headers) { let content = ''; for (const header of headers) { content += `${header.source}\n`; for (const h of header.headers) { content += ` ${h.key}: ${h.value}\n`; } content += '\n'; } return content; } /** * Generate server functions for Netlify * @param config Deployment configuration */ async generateServerFunctions(config) { // Create functions directory const functionsDir = path.join(config.outputDir, 'functions'); await mkdir(functionsDir, { recursive: true }); // Generate example function const exampleFunctionContent = ` exports.handler = async function(event, context) { return { statusCode: 200, body: JSON.stringify({ message: 'Hello from OrdoJS on Netlify!', timestamp: new Date().toISOString() }) }; }; `; await writeFile(path.join(functionsDir, 'hello.js'), exampleFunctionContent); } /** * Get deployment instructions * @param config Netlify configuration * @returns Deployment instructions */ getDeploymentInstructions(config) { return ` # Netlify Deployment Instructions Your project has been prepared for deployment to Netlify. ## Prerequisites 1. Install the Netlify CLI: \`\`\` npm install -g netlify-cli \`\`\` 2. Login to Netlify: \`\`\` netlify login \`\`\` ## Deployment To deploy your application to Netlify, run: \`\`\` cd ${config.outputDir} netlify deploy --prod \`\`\` ## Configuration The following files have been generated: - \`netlify.toml\`: Netlify configuration file ${config.redirects && config.redirects.length > 0 ? '- `_redirects`: URL redirects configuration\n' : ''} ${config.headers && config.headers.length > 0 ? '- `_headers`: HTTP headers configuration\n' : ''} ${config.includeServerFunctions ? '- `functions/`: Netlify Functions directory\n' : ''} You can customize these files to adjust your deployment settings. ## Environment Variables ${config.env && Object.keys(config.env).length > 0 ? 'The following environment variables have been configured:\n\n' + Object.entries(config.env).map(([key, value]) => `- ${key}: ${value}`).join('\n') : 'No environment variables have been configured. You can add them in the Netlify dashboard or in the netlify.toml file.'} ## Custom Domain ${config.domain ? `Your application will be available at: ${config.domain.name}` : 'You can configure a custom domain in the Netlify dashboard after deployment.'} `; } /** * Optimize assets for Netlify deployment * @param config Deployment configuration * @param outputDir Output directory * @returns Optimization results */ async optimizeForDeployment(config, outputDir) { logger.info('Optimizing assets for Netlify deployment...'); // Initialize asset optimizer with Netlify-specific options const optimizer = new AssetOptimizer({ minifyJs: true, minifyCss: true, brotli: true, gzip: true, sizeReport: true, terserOptions: { compress: { passes: 2, drop_console: true, drop_debugger: true }, mangle: true, format: { comments: false } } }); // Optimize all assets in the output directory const optimizationResults = await optimizer.optimizeDirectory(outputDir); // Generate and save size report const sizeReport = optimizer.generateSizeReport(optimizationResults); await writeFile(path.join(outputDir, 'size-report.txt'), sizeReport); return optimizationResults; } /** * Get Netlify-specific environment variables * @param config Deployment configuration * @returns Environment variables */ getEnvironmentVariables(config) { const env = { NETLIFY: 'true', CONTEXT: 'production', NODE_ENV: 'production', ...config.env }; // Add Netlify-specific environment variables if (config.domain) { env.URL = `https://${config.domain.name}`; } return env; } /** * Get Netlify deployment command * @param config Deployment configuration * @returns Deployment command */ getDeployCommand(config) { return `cd ${config.outputDir} && netlify deploy --prod`; } } //# sourceMappingURL=netlify-adapter.js.map