/ scripts / spec-doc / generate-spec.js
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();