@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
473 lines (472 loc) • 17.6 kB
JavaScript
"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;
};
})();
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
var _a;
Object.defineProperty(exports, "__esModule", { value: true });
exports.InteractivePrompter = void 0;
exports.createPrompter = createPrompter;
exports.getGlobalPrompter = getGlobalPrompter;
exports.setGlobalPrompter = setGlobalPrompter;
const prompts_1 = __importDefault(require("prompts"));
const chalk_1 = __importDefault(require("chalk"));
const fs = __importStar(require("fs-extra"));
const path = __importStar(require("path"));
class InteractivePrompter {
constructor(options = {}) {
this.history = [];
this.responseCache = new Map();
this.suggestions = new Map();
this.theme = options.theme || this.getDefaultTheme();
// Load suggestions
this.loadSuggestions();
// Override prompts for theming
this.setupPromptTheme();
}
getDefaultTheme() {
return {
prefix: chalk_1.default.cyan('?'),
suffix: chalk_1.default.gray(':'),
separator: chalk_1.default.gray('›'),
errorPrefix: chalk_1.default.red('✖'),
successPrefix: chalk_1.default.green('✓'),
warningPrefix: chalk_1.default.yellow('⚠'),
infoPrefix: chalk_1.default.blue('ℹ')
};
}
setupPromptTheme() {
prompts_1.default.override({
onCancel: () => {
console.log(chalk_1.default.red('\n✖ Operation cancelled'));
process.exit(1);
}
});
}
loadSuggestions() {
// Load framework suggestions
this.suggestions.set('framework', [
{ title: 'React', value: 'react', description: 'Popular library for building user interfaces' },
{ title: 'Vue', value: 'vue', description: 'Progressive framework for building UIs' },
{ title: 'Svelte', value: 'svelte', description: 'Compile-time optimized framework' },
{ title: 'Angular', value: 'angular', description: 'Platform for building mobile and desktop apps' },
{ title: 'Vanilla', value: 'vanilla', description: 'Plain JavaScript without frameworks' }
]);
// Load package manager suggestions
this.suggestions.set('packageManager', [
{ title: 'pnpm', value: 'pnpm', description: 'Fast, disk space efficient package manager' },
{ title: 'npm', value: 'npm', description: 'Default Node.js package manager' },
{ title: 'yarn', value: 'yarn', description: 'Fast, reliable, and secure dependency management' },
{ title: 'bun', value: 'bun', description: 'Incredibly fast JavaScript runtime and package manager' }
]);
// Load template suggestions
this.suggestions.set('template', [
{ title: 'Basic', value: 'basic', description: 'Simple project structure' },
{ title: 'E-commerce', value: 'ecommerce', description: 'Online store template' },
{ title: 'Dashboard', value: 'dashboard', description: 'Analytics dashboard template' },
{ title: 'SaaS', value: 'saas', description: 'Software as a Service template' },
{ title: 'Blog', value: 'blog', description: 'Content management template' }
]);
// Load common project names
this.suggestions.set('projectName', [
{ title: 'my-app', value: 'my-app', description: 'Generic application name' },
{ title: 'frontend-app', value: 'frontend-app', description: 'Frontend application' },
{ title: 'web-platform', value: 'web-platform', description: 'Web platform project' },
{ title: 'micro-frontend', value: 'micro-frontend', description: 'Microfrontend application' }
]);
}
async prompt(config, options = {}) {
const startTime = Date.now();
const configs = Array.isArray(config) ? config : [config];
// Handle non-interactive mode
if (options.nonInteractive || process.env.CI) {
return this.handleNonInteractive(configs, options);
}
// Prepare prompts with enhancements
const enhancedConfigs = configs.map(cfg => this.enhancePromptConfig(cfg, options));
try {
// Execute prompts
const response = await (0, prompts_1.default)(enhancedConfigs, {
onCancel: () => {
console.log(chalk_1.default.red('\n✖ Operation cancelled'));
process.exit(1);
}
});
// Record history
this.recordHistory(configs, response, Date.now() - startTime);
// Save responses if requested
if (options.saveResponses) {
await this.saveResponses(response, options.responseFile);
}
// Cache responses
for (const [key, value] of Object.entries(response)) {
this.responseCache.set(key, value);
}
return response;
}
catch (error) {
console.error(chalk_1.default.red(`\n${this.theme.errorPrefix} Prompt error: ${error.message}`));
throw error;
}
}
enhancePromptConfig(config, options) {
const enhanced = {
...config,
name: config.name,
message: `${this.theme.prefix} ${config.message}${this.theme.suffix}`
};
// Add choices with descriptions
if (config.choices) {
enhanced.choices = config.choices.map(choice => ({
...choice,
title: choice.description ?
`${choice.title} ${chalk_1.default.gray('- ' + choice.description)}` :
choice.title
}));
}
// Enhanced validation
if (config.validate) {
enhanced.validate = async (value) => {
if (options.skipValidation)
return true;
const result = await config.validate(value);
if (typeof result === 'string') {
return `${this.theme.errorPrefix} ${result}`;
}
return result;
};
}
// Enhanced autocomplete
if (config.type === 'autocomplete') {
enhanced.suggest = async (input) => {
// Use custom suggest function if provided
if (config.suggest) {
return await config.suggest(input);
}
// Use built-in suggestions
const suggestions = this.suggestions.get(config.name) || [];
return suggestions.filter(suggestion => suggestion.title.toLowerCase().includes(input.toLowerCase()) ||
suggestion.value.toLowerCase().includes(input.toLowerCase()));
};
enhanced.limit = config.limit || 10;
}
// Add hints and warnings
if (config.hint) {
enhanced.message += chalk_1.default.gray(` (${config.hint})`);
}
if (config.warn) {
enhanced.message += chalk_1.default.yellow(` ⚠ ${config.warn}`);
}
// Default values from cache or options
if (!enhanced.initial) {
enhanced.initial = options.defaults?.[config.name] ||
this.responseCache.get(config.name) ||
config.initial;
}
return enhanced;
}
handleNonInteractive(configs, options) {
const response = {};
for (const config of configs) {
const defaultValue = options.defaults?.[config.name] ||
this.responseCache.get(config.name) ||
config.initial;
if (defaultValue !== undefined) {
response[config.name] = defaultValue;
}
else {
throw new Error(`No default value provided for required prompt '${config.name}' in non-interactive mode`);
}
}
return response;
}
recordHistory(configs, response, duration) {
const historyEntry = {
timestamp: new Date(),
prompts: configs.map(config => ({
name: config.name,
value: response[config.name],
duration: duration / configs.length // Approximate per-prompt duration
}))
};
this.history.push(historyEntry);
// Keep only last 50 entries
if (this.history.length > 50) {
this.history = this.history.slice(-50);
}
}
async saveResponses(response, filename) {
try {
const saveFile = filename || path.join(process.cwd(), '.re-shell', 'prompt-responses.json');
const dir = path.dirname(saveFile);
await fs.ensureDir(dir);
let existingData = {};
if (await fs.pathExists(saveFile)) {
existingData = await fs.readJson(saveFile);
}
const updatedData = {
...existingData,
...response,
lastUpdated: new Date().toISOString()
};
await fs.writeJson(saveFile, updatedData, { spaces: 2 });
}
catch (error) {
// Don't throw on save errors
console.warn(chalk_1.default.yellow(`${this.theme.warningPrefix} Failed to save responses: ${error}`));
}
}
// Built-in prompt templates
async promptProjectSetup() {
return this.prompt([
{
type: 'text',
name: 'name',
message: 'Project name',
initial: 'my-app',
validate: (value) => {
if (!value.trim())
return 'Project name is required';
if (!/^[a-z0-9-_]+$/.test(value)) {
return 'Project name must contain only lowercase letters, numbers, hyphens, and underscores';
}
return true;
},
hint: 'lowercase, no spaces'
},
{
type: 'autocomplete',
name: 'framework',
message: 'Choose framework',
choices: this.suggestions.get('framework')
},
{
type: 'confirm',
name: 'typescript',
message: 'Use TypeScript',
initial: true
},
{
type: 'select',
name: 'packageManager',
message: 'Package manager',
choices: this.suggestions.get('packageManager'),
initial: 0
},
{
type: 'select',
name: 'template',
message: 'Project template',
choices: this.suggestions.get('template')
},
{
type: 'confirm',
name: 'installDependencies',
message: 'Install dependencies now',
initial: true
}
]);
}
async promptMicrofrontendConfig() {
return this.prompt([
{
type: 'text',
name: 'name',
message: 'Microfrontend name',
validate: (value) => {
if (!value.trim())
return 'Name is required';
if (!/^[a-z0-9-]+$/.test(value)) {
return 'Name must contain only lowercase letters, numbers, and hyphens';
}
return true;
}
},
{
type: 'autocomplete',
name: 'framework',
message: 'Framework',
choices: this.suggestions.get('framework')
},
{
type: 'number',
name: 'port',
message: 'Development port',
initial: 3001,
min: 1000,
max: 65535,
validate: (value) => {
if (value < 1000 || value > 65535) {
return 'Port must be between 1000 and 65535';
}
return true;
}
},
{
type: 'list',
name: 'exposed',
message: 'Exposed modules (comma-separated)',
initial: './App',
separator: ','
},
{
type: 'confirm',
name: 'typescript',
message: 'Use TypeScript',
initial: true
}
]);
}
async promptConfigurationUpdate() {
return this.prompt([
{
type: 'confirm',
name: 'updateGlobal',
message: 'Update global configuration',
initial: false
},
{
type: 'confirm',
name: 'updateProject',
message: 'Update project configuration',
initial: true
},
{
type: 'confirm',
name: 'backupExisting',
message: 'Backup existing configuration',
initial: true
}
]);
}
// Utility methods
getCachedResponse(name) {
return this.responseCache.get(name);
}
setCachedResponse(name, value) {
this.responseCache.set(name, value);
}
clearCache() {
this.responseCache.clear();
}
getHistory() {
return [...this.history];
}
clearHistory() {
this.history = [];
}
addSuggestion(category, suggestion) {
const existing = this.suggestions.get(category) || [];
existing.push(suggestion);
this.suggestions.set(category, existing);
}
getSuggestions(category) {
return this.suggestions.get(category) || [];
}
}
exports.InteractivePrompter = InteractivePrompter;
_a = InteractivePrompter;
// Validation helpers
InteractivePrompter.validators = {
required: (message = 'This field is required') => (value) => {
if (value === undefined || value === null || value === '') {
return message;
}
return true;
},
minLength: (min, message) => (value) => {
if (value.length < min) {
return message || `Must be at least ${min} characters`;
}
return true;
},
maxLength: (max, message) => (value) => {
if (value.length > max) {
return message || `Must be no more than ${max} characters`;
}
return true;
},
pattern: (pattern, message) => (value) => {
if (!pattern.test(value)) {
return message || 'Invalid format';
}
return true;
},
email: (message = 'Invalid email format') => (value) => {
const emailPattern = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
if (!emailPattern.test(value)) {
return message;
}
return true;
},
url: (message = 'Invalid URL format') => (value) => {
try {
new URL(value);
return true;
}
catch {
return message;
}
},
port: (message = 'Invalid port number') => (value) => {
if (value < 1 || value > 65535 || !Number.isInteger(value)) {
return message;
}
return true;
},
combine: (...validators) => async (value) => {
for (const validator of validators) {
const result = await validator(value);
if (result !== true) {
return result;
}
}
return true;
}
};
// Global instance
let globalPrompter = null;
function createPrompter(options) {
return new InteractivePrompter(options);
}
function getGlobalPrompter() {
if (!globalPrompter) {
globalPrompter = new InteractivePrompter();
}
return globalPrompter;
}
function setGlobalPrompter(prompter) {
globalPrompter = prompter;
}