#!/usr/bin/env node /** * JQHTML Official CLI Compiler * * Compiles .jqhtml templates to JavaScript with multiple output formats * Uses unified compiler module for single source of truth */ import fs from 'fs'; import path from 'path'; import { fileURLToPath } from 'url'; import { compileTemplate } from '../dist/index.js'; import { readFileSync } from 'fs'; const __filename = fileURLToPath(import.meta.url); const __dirname = path.dirname(__filename); // Get package version const packagePath = path.join(path.dirname(__dirname), 'package.json'); const packageJson = JSON.parse(readFileSync(packagePath, 'utf-8')); const VERSION = packageJson.version; // Parse command line arguments function parseArgs(argv) { const args = { input: null, output: null, format: 'iife', sourcemap: false, statusJson: false, help: false, version: false }; for (let i = 2; i < argv.length; i++) { const arg = argv[i]; if (arg === '--help' || arg === '-h') { args.help = true; } else if (arg === '--version' || arg === '-v') { args.version = true; } else if (arg === '--output' || arg === '-o') { args.output = argv[++i]; if (!args.output) { throw new Error('--output requires a file path'); } } else if (arg === '--format') { args.format = argv[++i]; if (!['iife', 'esm', 'cjs', 'umd'].includes(args.format)) { throw new Error(`Invalid format: ${args.format}. Must be one of: iife, esm, cjs, umd`); } } else if (arg === '--sourcemap') { args.sourcemap = true; } else if (arg === '--status-json') { args.statusJson = true; } else if (!arg.startsWith('-')) { args.input = arg; } } return args; } // Show help message function showHelp() { console.log(` JQHTML CLI Compiler v${VERSION} Usage: jqhtml-compile [options] Options: --output, -o Write output to file instead of stdout --format Output format: iife|esm|cjs|umd (default: iife) --sourcemap Include inline sourcemap --status-json Output errors as JSON for programmatic parsing --version, -v Show version number --help, -h Show this help message Examples: jqhtml compile component.jqhtml jqhtml compile component.jqhtml --output component.js jqhtml compile component.jqhtml --format esm --sourcemap jqhtml compile component.jqhtml -o dist/component.js --format umd Output formats: iife - Self-executing function that registers with window.jqhtml esm - ES module export cjs - CommonJS module.exports umd - Universal module (works as AMD, CommonJS, or global) `); } // Main compilation function async function compile(inputFile, options) { try { // Read input file const inputPath = path.resolve(inputFile); if (!fs.existsSync(inputPath)) { throw new Error(`Input file not found: ${inputPath}`); } const source = fs.readFileSync(inputPath, 'utf-8'); const filename = path.basename(inputPath); // Use unified compiler const compiled = compileTemplate(source, filename, { format: options.format, sourcemap: options.sourcemap, version: VERSION }); const formattedCode = compiled.code; if (options.statusJson) { // Output as JSON for programmatic consumption const result = { status: 'success', result: formattedCode }; if (options.output) { result.outputFile = options.output; } console.log(JSON.stringify(result)); } else if (options.output) { // Write to output file const outputPath = path.resolve(options.output); const outputDir = path.dirname(outputPath); // Create output directory if it doesn't exist if (!fs.existsSync(outputDir)) { fs.mkdirSync(outputDir, { recursive: true }); } fs.writeFileSync(outputPath, formattedCode, 'utf-8'); console.log(`✓ Compiled ${inputFile} → ${options.output}`); } else { // Output the compiled code to stdout without trailing newline process.stdout.write(formattedCode); } return 0; // Success } catch (error) { if (options.statusJson) { // Output error as JSON const errorObj = { status: 'error', error: { type: error.constructor.name, message: error.message, file: error.filename || null, line: error.line || null, column: error.column || null, context: error.context || null, suggestion: error.suggestion || null } }; console.log(JSON.stringify(errorObj)); } else { // Output error to stderr console.error(`Error: ${error.message}`); if (error.line) { console.error(` at line ${error.line}, column ${error.column || 0}`); } if (error.context) { console.error(` context: ${error.context}`); } if (error.suggestion) { console.error(` suggestion: ${error.suggestion}`); } } return 1; // Error } } // Main entry point async function main() { try { const args = parseArgs(process.argv); if (args.help) { showHelp(); process.exit(0); } if (args.version) { console.log(`v${VERSION}`); process.exit(0); } if (!args.input) { if (args.statusJson) { console.log(JSON.stringify({ status: 'error', error: { type: 'ArgumentError', message: 'No input file specified' } })); } else { console.error('Error: No input file specified'); console.error('Usage: jqhtml compile [options]'); console.error('Use --help for more information'); } process.exit(1); } const exitCode = await compile(args.input, args); process.exit(exitCode); } catch (error) { console.error('Fatal error:', error.message); process.exit(1); } } // Run the CLI main();