UNPKG

@re-shell/cli

Version:

Full-stack development platform uniting microservices and microfrontends. Build complete applications with .NET (ASP.NET Core Web API, Minimal API), Java (Spring Boot, Quarkus, Micronaut, Vert.x), Rust (Actix-Web, Warp, Rocket, Axum), Python (FastAPI, Dja

429 lines (417 loc) 15.7 kB
"use strict"; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || (function () { var ownKeys = function(o) { ownKeys = Object.getOwnPropertyNames || function (o) { var ar = []; for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; return ar; }; return ownKeys(o); }; return function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); __setModuleDefault(result, mod); return result; }; })(); Object.defineProperty(exports, "__esModule", { value: true }); exports.CompletionSystem = void 0; exports.createCompletionSystem = createCompletionSystem; exports.getGlobalCompletion = getGlobalCompletion; exports.setGlobalCompletion = setGlobalCompletion; const fs = __importStar(require("fs-extra")); const path = __importStar(require("path")); const os = __importStar(require("os")); class CompletionSystem { constructor(config = {}) { this.commands = new Map(); this.customProviders = new Map(); this.config = { enableFileCompletion: true, enableDirectoryCompletion: true, enableCommandCompletion: true, enableOptionCompletion: true, enableValueCompletion: true, customCompletions: {}, ...config }; this.setupBuiltinProviders(); } setupBuiltinProviders() { // Framework completion this.customProviders.set('framework', () => [ 'react', 'vue', 'svelte', 'angular', 'vanilla' ]); // Package manager completion this.customProviders.set('packageManager', () => [ 'npm', 'yarn', 'pnpm', 'bun' ]); // Template completion this.customProviders.set('template', () => [ 'basic', 'ecommerce', 'dashboard', 'saas', 'blog' ]); // Log level completion this.customProviders.set('logLevel', () => [ 'debug', 'info', 'warn', 'error', 'silent' ]); // Boolean completion this.customProviders.set('boolean', () => [ 'true', 'false' ]); // Environment completion this.customProviders.set('environment', () => [ 'development', 'staging', 'production', 'test' ]); // Port completion (common ports) this.customProviders.set('port', () => [ '3000', '3001', '3002', '4000', '5000', '8000', '8080', '9000' ]); } registerCommand(command) { this.commands.set(command.name, command); // Register aliases if (command.alias) { for (const alias of command.alias) { this.commands.set(alias, command); } } } unregisterCommand(name) { const command = this.commands.get(name); if (command) { this.commands.delete(name); // Remove aliases if (command.alias) { for (const alias of command.alias) { this.commands.delete(alias); } } } } registerProvider(name, provider) { this.customProviders.set(name, provider); } async complete(args, current, context) { const completions = []; // Complete command names if (args.length === 0 || (args.length === 1 && !current.startsWith('-'))) { if (this.config.enableCommandCompletion) { const commandCompletions = this.completeCommands(current); completions.push(...commandCompletions); } return completions; } const commandName = args[0]; const command = this.commands.get(commandName); if (!command) { return completions; } // Complete options if (current.startsWith('-')) { if (this.config.enableOptionCompletion) { const optionCompletions = this.completeOptions(command, current); completions.push(...optionCompletions); } return completions; } // Complete option values const lastArg = args[args.length - 1]; if (lastArg && lastArg.startsWith('-')) { if (this.config.enableValueCompletion) { const valueCompletions = await this.completeOptionValues(command, lastArg, current, context); completions.push(...valueCompletions); } return completions; } // Complete arguments const argumentCompletions = await this.completeArguments(command, args.slice(1), current, context); completions.push(...argumentCompletions); // Complete files and directories if (this.config.enableFileCompletion || this.config.enableDirectoryCompletion) { const pathCompletions = await this.completeFiles(current, context); completions.push(...pathCompletions); } return Array.from(new Set(completions)).sort(); } completeCommands(current) { const commandNames = Array.from(this.commands.keys()); return commandNames.filter(name => name.startsWith(current) && !this.commands.get(name)?.hidden); } completeOptions(command, current) { if (!command.options) return []; const completions = []; for (const option of command.options) { const flags = option.flag.split(',').map(f => f.trim()); for (const flag of flags) { if (flag.startsWith(current)) { completions.push(flag); } } } return completions; } async completeOptionValues(command, option, current, context) { if (!command.options) return []; // Find the option definition const optionDef = command.options.find(opt => { const flags = opt.flag.split(',').map(f => f.trim()); return flags.includes(option); }); if (!optionDef) return []; // Use choices if available if (optionDef.choices) { return optionDef.choices .filter(choice => choice.toString().startsWith(current)) .map(choice => choice.toString()); } // Use custom completion provider if available const providerName = this.getProviderForOption(option); if (providerName && this.customProviders.has(providerName)) { const provider = this.customProviders.get(providerName); const results = await provider(current, [option], context); return results.filter(result => result.startsWith(current)); } return []; } getProviderForOption(option) { const providerMap = { '--framework': 'framework', '-f': 'framework', '--package-manager': 'packageManager', '--pm': 'packageManager', '--template': 'template', '-t': 'template', '--log-level': 'logLevel', '--yes': 'boolean', '--no': 'boolean', '--typescript': 'boolean', '--ts': 'boolean', '--port': 'port', '-p': 'port', '--env': 'environment', '--environment': 'environment' }; return providerMap[option] || null; } async completeArguments(command, args, current, context) { if (!command.arguments) return []; const argIndex = args.length; const argumentDef = command.arguments[argIndex]; if (!argumentDef) return []; // Use custom completion if available const providerName = this.getProviderForArgument(command.name, argumentDef.name); if (providerName && this.customProviders.has(providerName)) { const provider = this.customProviders.get(providerName); return await provider(current, args, context); } return []; } getProviderForArgument(command, argument) { const providerMap = { init: { name: 'projectName' }, add: { name: 'microfrontendName' } }; return providerMap[command]?.[argument] || null; } async completeFiles(current, context) { try { const isDirectory = current.endsWith('/'); const basePath = isDirectory ? current : path.dirname(current); const filename = isDirectory ? '' : path.basename(current); const fullPath = path.resolve(context.cwd, basePath); if (!await fs.pathExists(fullPath)) { return []; } const entries = await fs.readdir(fullPath, { withFileTypes: true }); const completions = []; for (const entry of entries) { if (!entry.name.startsWith(filename)) continue; if (entry.isDirectory() && this.config.enableDirectoryCompletion) { const dirPath = path.join(basePath, entry.name); completions.push(dirPath + '/'); } else if (entry.isFile() && this.config.enableFileCompletion) { const filePath = path.join(basePath, entry.name); completions.push(filePath); } } return completions; } catch { return []; } } generateShellCompletions(programName) { return [ this.generateBashCompletion(programName), this.generateZshCompletion(programName), this.generateFishCompletion(programName), this.generatePowerShellCompletion(programName) ]; } generateBashCompletion(programName) { const script = `#!/bin/bash _${programName}_completions() { local cur prev words cword _init_completion || return # Call the completion endpoint local completions completions=$(${programName} __complete "\${COMP_WORDS[@]}" "\${COMP_CWORD}") if [[ $? -eq 0 ]]; then COMPREPLY=($(compgen -W "\${completions}" -- "\${cur}")) fi } complete -F _${programName}_completions ${programName}`; return { shell: 'bash', script, installPath: '/etc/bash_completion.d/' + programName, instructions: `To install bash completion: 1. Save the script to /etc/bash_completion.d/${programName} 2. Or add to your ~/.bashrc: source <(${programName} completion bash) 3. Restart your shell or run: source ~/.bashrc` }; } generateZshCompletion(programName) { const script = `#compdef ${programName} _${programName}() { local context state line local -a completions # Call the completion endpoint completions=($(${programName} __complete "\${words[@]}" "\${CURRENT}")) if [[ $? -eq 0 ]]; then _describe 'commands' completions fi } _${programName} "$@"`; return { shell: 'zsh', script, installPath: `${os.homedir()}/.zsh/completions/_${programName}`, instructions: `To install zsh completion: 1. Create directory: mkdir -p ~/.zsh/completions 2. Save the script to ~/.zsh/completions/_${programName} 3. Add to your ~/.zshrc: fpath=(~/.zsh/completions $fpath) autoload -U compinit && compinit 4. Restart your shell` }; } generateFishCompletion(programName) { const script = `function __${programName}_complete set -l completions (${programName} __complete (commandline -cp) (commandline -t)) if test $status -eq 0 printf '%s\\n' $completions end end complete -c ${programName} -f -a "(__${programName}_complete)"`; return { shell: 'fish', script, installPath: `${os.homedir()}/.config/fish/completions/${programName}.fish`, instructions: `To install fish completion: 1. Create directory: mkdir -p ~/.config/fish/completions 2. Save the script to ~/.config/fish/completions/${programName}.fish 3. Restart your shell` }; } generatePowerShellCompletion(programName) { const script = `Register-ArgumentCompleter -Native -CommandName ${programName} -ScriptBlock { param($wordToComplete, $commandAst, $cursorPosition) $completions = & ${programName} __complete $commandAst.CommandElements $completions | ForEach-Object { [System.Management.Automation.CompletionResult]::new($_, $_, 'ParameterValue', $_) } }`; return { shell: 'powershell', script, installPath: '$PROFILE', instructions: `To install PowerShell completion: 1. Add the script to your PowerShell profile 2. Or run: ${programName} completion powershell | Out-String | Invoke-Expression 3. Restart PowerShell` }; } async installCompletion(shell, programName) { const completions = this.generateShellCompletions(programName); const completion = completions.find(c => c.shell === shell); if (!completion) { throw new Error(`Unsupported shell: ${shell}`); } try { const installDir = path.dirname(completion.installPath); await fs.ensureDir(installDir); await fs.writeFile(completion.installPath, completion.script); console.log(`✓ Completion installed to ${completion.installPath}`); console.log('\nNext steps:'); console.log(completion.instructions); } catch (error) { throw new Error(`Failed to install completion: ${error.message}`); } } async handleCompletionRequest(args) { // Parse completion request const words = args.slice(0, -1); // All words except the current one const current = args[args.length - 1] || ''; const context = { command: words[0] || '', cwd: process.cwd(), env: process.env }; try { const completions = await this.complete(words.slice(1), current, context); console.log(completions.join('\n')); } catch (error) { // Silent fail for completion errors process.exit(1); } } } exports.CompletionSystem = CompletionSystem; // Global completion system let globalCompletion = null; function createCompletionSystem(config) { return new CompletionSystem(config); } function getGlobalCompletion() { if (!globalCompletion) { globalCompletion = new CompletionSystem(); } return globalCompletion; } function setGlobalCompletion(completion) { globalCompletion = completion; }