Files
root f6fac6c4bc Fix bin/publish: copy docs.dist from project root
Fix bin/publish: use correct .env path for rspade_system
Fix bin/publish script: prevent grep exit code 1 from terminating script

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-21 02:08:33 +00:00

218 lines
5.9 KiB
JavaScript
Executable File

#!/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 <input.jqhtml> [options]
Options:
--output, -o <file> Write output to file instead of stdout
--format <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 <input.jqhtml> [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();