UNPKG

@xec-sh/cli

Version:

Xec: The Universal Shell for TypeScript

270 lines 10.1 kB
import path from 'path'; import chalk from 'chalk'; import fs from 'fs/promises'; import { $ } from '@xec-sh/core'; import * as clack from '@clack/prompts'; import { TaskManager } from '../config/task-manager.js'; import { ConfigurationManager } from '../config/configuration-manager.js'; import { BaseCommand } from '../utils/command-base.js'; import { ScriptLoader } from '../utils/script-loader.js'; export class RunCommand extends BaseCommand { constructor() { super({ name: 'run', description: 'Run an Xec script or task', arguments: '[fileOrTask]', aliases: ['r'], options: [ { flags: '-e, --eval <code>', description: 'Evaluate code' }, { flags: '--repl', description: 'Start interactive REPL' }, { flags: '--typescript', description: 'Enable TypeScript support' }, { flags: '--watch', description: 'Watch for file changes' }, { flags: '--runtime <runtime>', description: 'Specify runtime: auto, node, bun, deno (default: auto)' }, { flags: '--no-universal', description: 'Disable universal loader (legacy mode)' } ], examples: [ { command: 'xec run script.js', description: 'Run a JavaScript file' }, { command: 'xec run script.ts', description: 'Run a TypeScript file' }, { command: 'xec run build', description: 'Run a task named "build"' }, { command: 'xec run -e "console.log(\'Hello\')"', description: 'Evaluate inline code' }, { command: 'xec run --repl', description: 'Start interactive REPL' } ] }); this.scriptLoader = new ScriptLoader({ verbose: process.env['XEC_DEBUG'] === 'true', cache: true, preferredCDN: 'esm.sh' }); } create() { const command = super.create(); command.option('-p, --param <key=value...>', 'Task parameters (can be used multiple times)', (value, previous = []) => { previous.push(value); return previous; }, []); command.allowUnknownOption(true); return command; } async execute(args) { const fileOrTask = args[0]; const options = args[args.length - 1]; const scriptArgs = args.slice(1, args.length - 1); if (options.repl) { await this.startRepl(options); } else if (options.eval) { await this.evalCode(options.eval, scriptArgs, options); } else if (fileOrTask) { const isFile = fileOrTask.includes('.') || fileOrTask.includes('/') || fileOrTask.includes('\\'); if (isFile) { await this.runScript(fileOrTask, scriptArgs, options); } else { await this.runTask(fileOrTask, options); } } else { clack.log.error('No script file or task specified'); clack.log.info('Usage: xec run <file> [args...]'); clack.log.info(' xec run <task> [options]'); clack.log.info(' xec run -e <code>'); clack.log.info(' xec run --repl'); throw new Error('No script file or task specified'); } } async runScript(scriptPath, args, options) { const execOptions = { verbose: this.options.verbose || process.env['XEC_DEBUG'] === 'true', quiet: this.options.quiet, typescript: options.typescript, watch: options.watch, context: { args, argv: [process.argv[0] || 'node', scriptPath, ...args], __filename: path.resolve(scriptPath), __dirname: path.dirname(path.resolve(scriptPath)), }, target: { type: 'local', name: 'local', config: {} }, targetEngine: $ }; const result = await this.scriptLoader.executeScript(scriptPath, execOptions); if (!result.success && result.error) { if (result.error.message.includes('runtime requested but not available')) { clack.log.error(result.error.message); const runtime = options.runtime || 'auto'; if (runtime !== 'auto') { clack.log.info('\nTo use a specific runtime, ensure it is installed and run xec with it:'); clack.log.info(` ${chalk.cyan(`${runtime} xec run ${scriptPath}`)}`); } } else { throw result.error; } } } async evalCode(code, args, options) { const execOptions = { verbose: this.options.verbose || process.env['XEC_DEBUG'] === 'true', quiet: this.options.quiet, typescript: options.typescript, context: { args, argv: ['xec', '<eval>', ...args], __filename: '<eval>', __dirname: process.cwd(), }, target: { type: 'local', name: 'local', config: {} }, targetEngine: $ }; const result = await this.scriptLoader.evaluateCode(code, execOptions); if (!result.success && result.error) { throw result.error; } } async startRepl(options) { const execOptions = { verbose: this.options.verbose || process.env['XEC_DEBUG'] === 'true', quiet: this.options.quiet, typescript: options.typescript, target: { type: 'local', name: 'local', config: {} }, targetEngine: $ }; await this.scriptLoader.startRepl(execOptions); } async runTask(taskName, options) { const configManager = new ConfigurationManager({ projectRoot: process.cwd(), }); const taskManager = new TaskManager({ configManager, debug: this.options.verbose || process.env['XEC_DEBUG'] === 'true', dryRun: false, }); await taskManager.load(); if (!await taskManager.exists(taskName)) { try { await fs.access(taskName); return await this.runScript(taskName, [], options); } catch { clack.log.error(`Task '${taskName}' not found`); clack.log.info(chalk.dim('\nRun "xec tasks" to see available tasks')); throw new Error(`Task '${taskName}' not found`); } } const params = {}; if (options.param) { for (const param of options.param) { const [key, ...valueParts] = param.split('='); const value = valueParts.join('='); if (!key || !value) { clack.log.error(`Invalid parameter format: ${param}`); clack.log.info(chalk.dim('Use --param key=value')); throw new Error(`Invalid parameter format: ${param}`); } let parsedValue = value; if (value === 'true') parsedValue = true; else if (value === 'false') parsedValue = false; else if (!isNaN(Number(value))) parsedValue = Number(value); else if (value.startsWith('[') || value.startsWith('{')) { try { parsedValue = JSON.parse(value); } catch { } } params[key] = parsedValue; } } if (!this.options.quiet) { clack.log.info(`Running task: ${chalk.cyan(taskName)}`); if (Object.keys(params).length > 0) { clack.log.info(chalk.dim('Parameters:')); for (const [key, value] of Object.entries(params)) { clack.log.info(chalk.dim(` ${key}: ${JSON.stringify(value)}`)); } } } const result = await taskManager.run(taskName, params); if (!result.success) { clack.log.error(`Task '${taskName}' failed`); if (result.error) { clack.log.error(result.error.message); } throw new Error(`Task '${taskName}' failed`); } if (!this.options.quiet) { clack.log.success(`Task '${taskName}' completed successfully`); } } } export async function runScript(scriptPath, args, options) { const command = new RunCommand(); return command['runScript'](scriptPath, args, options); } export async function evalCode(code, args, options) { const command = new RunCommand(); return command['evalCode'](code, args, options); } export async function startRepl(options) { const command = new RunCommand(); return command['startRepl'](options); } export async function runTask(taskName, options) { const command = new RunCommand(); return command['runTask'](taskName, options); } export default function command(program) { const cmd = new RunCommand(); program.addCommand(cmd.create()); } //# sourceMappingURL=run.js.map