generate-spec.js
1 #!/usr/bin/env node 2 /** 3 * Copyright 2025 Alibaba Group Holding Ltd. 4 * 5 * Licensed under the Apache License, Version 2.0 (the "License"); 6 * you may not use this file except in compliance with the License. 7 * You may obtain a copy of the License at 8 * 9 * http://www.apache.org/licenses/LICENSE-2.0 10 * 11 * Unless required by applicable law or agreed to in writing, software 12 * distributed under the License is distributed on an "AS IS" BASIS, 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 * See the License for the specific language governing permissions and 15 * limitations under the License. 16 */ 17 18 19 /** 20 * Generate spec-inline.js from sandbox-lifecycle.yml 21 * 22 * Usage: 23 * node scripts/spec-doc/generate-spec.js 24 * node scripts/spec-doc/generate-spec.js --output docs/public/api/spec-inline.js 25 * 26 * This script: 27 * 1. Reads specs/sandbox-lifecycle.yml 28 * 2. Escapes backticks 29 * 3. Wraps in JavaScript template literal 30 * 4. Writes to docs/public/api/spec-inline.js (by default) 31 */ 32 33 const fs = require('fs'); 34 const path = require('path'); 35 36 // Find project root 37 function findProjectRoot() { 38 let dir = __dirname; 39 while (dir !== path.dirname(dir)) { 40 if (fs.existsSync(path.join(dir, 'specs', 'sandbox-lifecycle.yml'))) { 41 return dir; 42 } 43 dir = path.dirname(dir); 44 } 45 throw new Error('Could not find project root (sandbox-lifecycle.yml not found)'); 46 } 47 48 function parseOutputPathArg(projectRoot) { 49 const outputFlagIndex = process.argv.indexOf('--output'); 50 if (outputFlagIndex === -1) { 51 return path.join(projectRoot, 'docs', 'public', 'api', 'spec-inline.js'); 52 } 53 const outputValue = process.argv[outputFlagIndex + 1]; 54 if (!outputValue) { 55 throw new Error('Missing value for --output'); 56 } 57 if (path.isAbsolute(outputValue)) { 58 return outputValue; 59 } 60 return path.join(projectRoot, outputValue); 61 } 62 63 function main() { 64 try { 65 const projectRoot = findProjectRoot(); 66 const yamlPath = path.join(projectRoot, 'specs', 'sandbox-lifecycle.yml'); 67 const outputPath = parseOutputPathArg(projectRoot); 68 69 // Validate input file exists 70 if (!fs.existsSync(yamlPath)) { 71 throw new Error(`YAML file not found: ${yamlPath}`); 72 } 73 74 console.log('Generating spec-inline.js...'); 75 console.log(` Input: ${yamlPath}`); 76 console.log(` Output: ${outputPath}`); 77 fs.mkdirSync(path.dirname(outputPath), { recursive: true }); 78 79 80 // Read YAML 81 const yamlContent = fs.readFileSync(yamlPath, 'utf-8'); 82 const yamlSize = Math.round(yamlContent.length / 1024); 83 84 // Escape backticks for template literal 85 const escapedYaml = yamlContent.replace(/`/g, '\\`'); 86 87 // Generate JavaScript 88 const jsContent = `const OPENAPI_SPEC_YAML = \`${escapedYaml}\`;`; 89 const jsSize = Math.round(jsContent.length / 1024); 90 91 // Write output 92 fs.writeFileSync(outputPath, jsContent, 'utf-8'); 93 94 console.log('\nSuccessfully generated spec-inline.js'); 95 console.log(` YAML size: ${yamlSize} KB`); 96 console.log(` JS size: ${jsSize} KB`); 97 console.log(` Compression ratio: ${((jsSize / yamlSize) * 100).toFixed(1)}%`); 98 99 // Verify 100 const generated = fs.readFileSync(outputPath, 'utf-8'); 101 if (generated.startsWith('const OPENAPI_SPEC_YAML = `')) { 102 console.log('\nFile validated successfully'); 103 process.exit(0); 104 } else { 105 throw new Error('Generated file validation failed'); 106 } 107 } catch (error) { 108 console.error(`\nError: ${error.message}`); 109 console.error(error.stack); 110 process.exit(1); 111 } 112 } 113 114 // Run 115 main();