Initial commit
This commit is contained in:
259
skills/inquirer-patterns/templates/nodejs/autocomplete-prompt.js
Normal file
259
skills/inquirer-patterns/templates/nodejs/autocomplete-prompt.js
Normal file
@@ -0,0 +1,259 @@
|
||||
/**
|
||||
* Autocomplete Prompt Template
|
||||
*
|
||||
* Use for: Large option lists with search
|
||||
* Features: Type-ahead, fuzzy matching, suggestions
|
||||
*
|
||||
* Note: Requires inquirer-autocomplete-prompt plugin
|
||||
* Install: npm install inquirer-autocomplete-prompt
|
||||
*/
|
||||
|
||||
import inquirer from 'inquirer';
|
||||
import inquirerAutocomplete from 'inquirer-autocomplete-prompt';
|
||||
|
||||
// Register the autocomplete prompt type
|
||||
inquirer.registerPrompt('autocomplete', inquirerAutocomplete);
|
||||
|
||||
// Example: Countries list for autocomplete
|
||||
const countries = [
|
||||
'Afghanistan', 'Albania', 'Algeria', 'Andorra', 'Angola',
|
||||
'Argentina', 'Armenia', 'Australia', 'Austria', 'Azerbaijan',
|
||||
'Bahamas', 'Bahrain', 'Bangladesh', 'Barbados', 'Belarus',
|
||||
'Belgium', 'Belize', 'Benin', 'Bhutan', 'Bolivia',
|
||||
'Brazil', 'Brunei', 'Bulgaria', 'Burkina Faso', 'Burundi',
|
||||
'Cambodia', 'Cameroon', 'Canada', 'Cape Verde', 'Chad',
|
||||
'Chile', 'China', 'Colombia', 'Comoros', 'Congo',
|
||||
'Costa Rica', 'Croatia', 'Cuba', 'Cyprus', 'Czech Republic',
|
||||
'Denmark', 'Djibouti', 'Dominica', 'Dominican Republic',
|
||||
'Ecuador', 'Egypt', 'El Salvador', 'Estonia', 'Ethiopia',
|
||||
'Fiji', 'Finland', 'France', 'Gabon', 'Gambia',
|
||||
'Georgia', 'Germany', 'Ghana', 'Greece', 'Grenada',
|
||||
'Guatemala', 'Guinea', 'Guyana', 'Haiti', 'Honduras',
|
||||
'Hungary', 'Iceland', 'India', 'Indonesia', 'Iran',
|
||||
'Iraq', 'Ireland', 'Israel', 'Italy', 'Jamaica',
|
||||
'Japan', 'Jordan', 'Kazakhstan', 'Kenya', 'Kuwait',
|
||||
'Laos', 'Latvia', 'Lebanon', 'Lesotho', 'Liberia',
|
||||
'Libya', 'Lithuania', 'Luxembourg', 'Madagascar', 'Malawi',
|
||||
'Malaysia', 'Maldives', 'Mali', 'Malta', 'Mexico',
|
||||
'Moldova', 'Monaco', 'Mongolia', 'Morocco', 'Mozambique',
|
||||
'Myanmar', 'Namibia', 'Nepal', 'Netherlands', 'New Zealand',
|
||||
'Nicaragua', 'Niger', 'Nigeria', 'Norway', 'Oman',
|
||||
'Pakistan', 'Panama', 'Paraguay', 'Peru', 'Philippines',
|
||||
'Poland', 'Portugal', 'Qatar', 'Romania', 'Russia',
|
||||
'Rwanda', 'Saudi Arabia', 'Senegal', 'Serbia', 'Singapore',
|
||||
'Slovakia', 'Slovenia', 'Somalia', 'South Africa', 'South Korea',
|
||||
'Spain', 'Sri Lanka', 'Sudan', 'Sweden', 'Switzerland',
|
||||
'Syria', 'Taiwan', 'Tanzania', 'Thailand', 'Togo',
|
||||
'Tunisia', 'Turkey', 'Uganda', 'Ukraine', 'United Arab Emirates',
|
||||
'United Kingdom', 'United States', 'Uruguay', 'Uzbekistan',
|
||||
'Venezuela', 'Vietnam', 'Yemen', 'Zambia', 'Zimbabwe'
|
||||
];
|
||||
|
||||
// Example: NPM packages for autocomplete
|
||||
const popularPackages = [
|
||||
'express', 'react', 'vue', 'angular', 'next', 'nuxt',
|
||||
'axios', 'lodash', 'moment', 'dayjs', 'uuid', 'dotenv',
|
||||
'typescript', 'eslint', 'prettier', 'jest', 'mocha', 'chai',
|
||||
'webpack', 'vite', 'rollup', 'babel', 'esbuild',
|
||||
'socket.io', 'redis', 'mongodb', 'mongoose', 'sequelize',
|
||||
'prisma', 'typeorm', 'knex', 'pg', 'mysql2',
|
||||
'bcrypt', 'jsonwebtoken', 'passport', 'helmet', 'cors',
|
||||
'multer', 'sharp', 'puppeteer', 'playwright', 'cheerio'
|
||||
];
|
||||
|
||||
// Fuzzy search function
|
||||
function fuzzySearch(input, choices) {
|
||||
if (!input) return choices;
|
||||
|
||||
const searchTerm = input.toLowerCase();
|
||||
return choices.filter(choice => {
|
||||
const item = typeof choice === 'string' ? choice : choice.name;
|
||||
return item.toLowerCase().includes(searchTerm);
|
||||
});
|
||||
}
|
||||
|
||||
async function autocompletePromptExample() {
|
||||
const answers = await inquirer.prompt([
|
||||
{
|
||||
type: 'autocomplete',
|
||||
name: 'country',
|
||||
message: 'Select your country:',
|
||||
source: (answersSoFar, input) => {
|
||||
return Promise.resolve(fuzzySearch(input, countries));
|
||||
},
|
||||
pageSize: 10
|
||||
},
|
||||
{
|
||||
type: 'autocomplete',
|
||||
name: 'package',
|
||||
message: 'Search for an npm package:',
|
||||
source: (answersSoFar, input) => {
|
||||
const filtered = fuzzySearch(input, popularPackages);
|
||||
return Promise.resolve(filtered);
|
||||
},
|
||||
pageSize: 8,
|
||||
validate: (input) => {
|
||||
return input.length > 0 || 'Please select a package';
|
||||
}
|
||||
},
|
||||
{
|
||||
type: 'autocomplete',
|
||||
name: 'city',
|
||||
message: 'Select city:',
|
||||
source: async (answersSoFar, input) => {
|
||||
// Example: Cities based on selected country
|
||||
const citiesByCountry = {
|
||||
'United States': ['New York', 'Los Angeles', 'Chicago', 'Houston', 'Phoenix'],
|
||||
'United Kingdom': ['London', 'Manchester', 'Birmingham', 'Glasgow', 'Liverpool'],
|
||||
'Canada': ['Toronto', 'Vancouver', 'Montreal', 'Calgary', 'Ottawa'],
|
||||
'Australia': ['Sydney', 'Melbourne', 'Brisbane', 'Perth', 'Adelaide'],
|
||||
'Germany': ['Berlin', 'Munich', 'Hamburg', 'Frankfurt', 'Cologne']
|
||||
};
|
||||
|
||||
const cities = citiesByCountry[answersSoFar.country] || ['Capital City', 'Major City'];
|
||||
return fuzzySearch(input, cities);
|
||||
},
|
||||
when: (answers) => ['United States', 'United Kingdom', 'Canada', 'Australia', 'Germany'].includes(answers.country)
|
||||
}
|
||||
]);
|
||||
|
||||
console.log('\n✅ Selections:');
|
||||
console.log(JSON.stringify(answers, null, 2));
|
||||
|
||||
return answers;
|
||||
}
|
||||
|
||||
// Example: Framework/Library search
|
||||
async function frameworkSearchExample() {
|
||||
const frameworks = [
|
||||
{ name: 'React - UI library by Facebook', value: 'react' },
|
||||
{ name: 'Vue.js - Progressive JavaScript framework', value: 'vue' },
|
||||
{ name: 'Angular - Platform for building web apps', value: 'angular' },
|
||||
{ name: 'Svelte - Cybernetically enhanced web apps', value: 'svelte' },
|
||||
{ name: 'Next.js - React framework with SSR', value: 'next' },
|
||||
{ name: 'Nuxt.js - Vue.js framework with SSR', value: 'nuxt' },
|
||||
{ name: 'Remix - Full stack web framework', value: 'remix' },
|
||||
{ name: 'SvelteKit - Svelte framework', value: 'sveltekit' },
|
||||
{ name: 'Express - Fast Node.js web framework', value: 'express' },
|
||||
{ name: 'Fastify - Fast and low overhead web framework', value: 'fastify' },
|
||||
{ name: 'NestJS - Progressive Node.js framework', value: 'nestjs' },
|
||||
{ name: 'Koa - Expressive middleware for Node.js', value: 'koa' }
|
||||
];
|
||||
|
||||
const answer = await inquirer.prompt([
|
||||
{
|
||||
type: 'autocomplete',
|
||||
name: 'framework',
|
||||
message: 'Search for a framework:',
|
||||
source: (answersSoFar, input) => {
|
||||
const filtered = fuzzySearch(input, frameworks);
|
||||
return Promise.resolve(filtered);
|
||||
},
|
||||
pageSize: 10
|
||||
}
|
||||
]);
|
||||
|
||||
console.log(`\n✅ Selected: ${answer.framework}`);
|
||||
return answer;
|
||||
}
|
||||
|
||||
// Example: Command search with categories
|
||||
async function commandSearchExample() {
|
||||
const commands = [
|
||||
{ name: '📦 install - Install dependencies', value: 'install' },
|
||||
{ name: '🚀 start - Start development server', value: 'start' },
|
||||
{ name: '🏗️ build - Build for production', value: 'build' },
|
||||
{ name: '🧪 test - Run tests', value: 'test' },
|
||||
{ name: '🔍 lint - Check code quality', value: 'lint' },
|
||||
{ name: '✨ format - Format code', value: 'format' },
|
||||
{ name: '📝 generate - Generate files', value: 'generate' },
|
||||
{ name: '🔄 update - Update dependencies', value: 'update' },
|
||||
{ name: '🧹 clean - Clean build artifacts', value: 'clean' },
|
||||
{ name: '🚢 deploy - Deploy application', value: 'deploy' },
|
||||
{ name: '📊 analyze - Analyze bundle size', value: 'analyze' },
|
||||
{ name: '🐛 debug - Start debugger', value: 'debug' }
|
||||
];
|
||||
|
||||
const answer = await inquirer.prompt([
|
||||
{
|
||||
type: 'autocomplete',
|
||||
name: 'command',
|
||||
message: 'Search for a command:',
|
||||
source: (answersSoFar, input) => {
|
||||
return Promise.resolve(fuzzySearch(input, commands));
|
||||
},
|
||||
pageSize: 12
|
||||
}
|
||||
]);
|
||||
|
||||
console.log(`\n✅ Running: ${answer.command}`);
|
||||
return answer;
|
||||
}
|
||||
|
||||
// Example: Dynamic API search (simulated)
|
||||
async function apiSearchExample() {
|
||||
console.log('\n🔍 API Endpoint Search\n');
|
||||
|
||||
const answer = await inquirer.prompt([
|
||||
{
|
||||
type: 'autocomplete',
|
||||
name: 'endpoint',
|
||||
message: 'Search API endpoints:',
|
||||
source: async (answersSoFar, input) => {
|
||||
// Simulate API call
|
||||
await new Promise(resolve => setTimeout(resolve, 100));
|
||||
|
||||
const endpoints = [
|
||||
{ name: 'GET /users - List all users', value: '/users' },
|
||||
{ name: 'GET /users/:id - Get user by ID', value: '/users/:id' },
|
||||
{ name: 'POST /users - Create new user', value: '/users' },
|
||||
{ name: 'PUT /users/:id - Update user', value: '/users/:id' },
|
||||
{ name: 'DELETE /users/:id - Delete user', value: '/users/:id' },
|
||||
{ name: 'GET /posts - List all posts', value: '/posts' },
|
||||
{ name: 'GET /posts/:id - Get post by ID', value: '/posts/:id' },
|
||||
{ name: 'POST /posts - Create new post', value: '/posts' },
|
||||
{ name: 'GET /comments - List comments', value: '/comments' },
|
||||
{ name: 'POST /auth/login - User login', value: '/auth/login' },
|
||||
{ name: 'POST /auth/register - User registration', value: '/auth/register' },
|
||||
{ name: 'POST /auth/logout - User logout', value: '/auth/logout' }
|
||||
];
|
||||
|
||||
return fuzzySearch(input, endpoints);
|
||||
},
|
||||
pageSize: 10
|
||||
}
|
||||
]);
|
||||
|
||||
console.log(`\n✅ Selected endpoint: ${answer.endpoint}`);
|
||||
return answer;
|
||||
}
|
||||
|
||||
// Run if executed directly
|
||||
if (import.meta.url === `file://${process.argv[1]}`) {
|
||||
(async () => {
|
||||
console.log('=== Autocomplete Examples ===\n');
|
||||
|
||||
console.log('1. Country & Package Selection');
|
||||
await autocompletePromptExample();
|
||||
|
||||
console.log('\n2. Framework Search');
|
||||
await frameworkSearchExample();
|
||||
|
||||
console.log('\n3. Command Search');
|
||||
await commandSearchExample();
|
||||
|
||||
console.log('\n4. API Endpoint Search');
|
||||
await apiSearchExample();
|
||||
|
||||
process.exit(0);
|
||||
})().catch((error) => {
|
||||
if (error.isTtyError) {
|
||||
console.error('❌ Prompt could not be rendered in this environment');
|
||||
} else {
|
||||
console.error('❌ User interrupted prompt');
|
||||
}
|
||||
process.exit(1);
|
||||
});
|
||||
}
|
||||
|
||||
export { autocompletePromptExample, frameworkSearchExample, commandSearchExample, apiSearchExample };
|
||||
140
skills/inquirer-patterns/templates/nodejs/checkbox-prompt.js
Normal file
140
skills/inquirer-patterns/templates/nodejs/checkbox-prompt.js
Normal file
@@ -0,0 +1,140 @@
|
||||
/**
|
||||
* Checkbox Prompt Template
|
||||
*
|
||||
* Use for: Multiple selections from options
|
||||
* Features: Space to toggle, Enter to confirm
|
||||
*/
|
||||
|
||||
import inquirer from 'inquirer';
|
||||
|
||||
async function checkboxPromptExample() {
|
||||
const answers = await inquirer.prompt([
|
||||
{
|
||||
type: 'checkbox',
|
||||
name: 'features',
|
||||
message: 'Select features to include:',
|
||||
choices: [
|
||||
'Authentication',
|
||||
'Authorization',
|
||||
'Database Integration',
|
||||
'API Documentation',
|
||||
'Testing Suite',
|
||||
'CI/CD Pipeline',
|
||||
'Monitoring',
|
||||
'Logging'
|
||||
],
|
||||
validate: (choices) => {
|
||||
if (choices.length === 0) {
|
||||
return 'You must select at least one feature';
|
||||
}
|
||||
return true;
|
||||
}
|
||||
},
|
||||
{
|
||||
type: 'checkbox',
|
||||
name: 'tools',
|
||||
message: 'Select development tools:',
|
||||
choices: [
|
||||
{ name: 'ESLint (Linting)', value: 'eslint', checked: true },
|
||||
{ name: 'Prettier (Formatting)', value: 'prettier', checked: true },
|
||||
{ name: 'Jest (Testing)', value: 'jest' },
|
||||
{ name: 'Husky (Git Hooks)', value: 'husky' },
|
||||
{ name: 'TypeDoc (Documentation)', value: 'typedoc' },
|
||||
{ name: 'Webpack (Bundling)', value: 'webpack' }
|
||||
]
|
||||
},
|
||||
{
|
||||
type: 'checkbox',
|
||||
name: 'plugins',
|
||||
message: 'Select plugins to install:',
|
||||
choices: [
|
||||
new inquirer.Separator('=== Essential ==='),
|
||||
{ name: 'dotenv - Environment variables', value: 'dotenv', checked: true },
|
||||
{ name: 'axios - HTTP client', value: 'axios', checked: true },
|
||||
new inquirer.Separator('=== Utilities ==='),
|
||||
{ name: 'lodash - Utility functions', value: 'lodash' },
|
||||
{ name: 'dayjs - Date manipulation', value: 'dayjs' },
|
||||
{ name: 'uuid - Unique IDs', value: 'uuid' },
|
||||
new inquirer.Separator('=== Validation ==='),
|
||||
{ name: 'joi - Schema validation', value: 'joi' },
|
||||
{ name: 'zod - TypeScript-first validation', value: 'zod' },
|
||||
new inquirer.Separator('=== Advanced ==='),
|
||||
{ name: 'bull - Job queues', value: 'bull' },
|
||||
{ name: 'socket.io - WebSockets', value: 'socket.io' }
|
||||
],
|
||||
pageSize: 15,
|
||||
validate: (choices) => {
|
||||
if (choices.length > 10) {
|
||||
return 'Please select no more than 10 plugins to avoid bloat';
|
||||
}
|
||||
return true;
|
||||
}
|
||||
},
|
||||
{
|
||||
type: 'checkbox',
|
||||
name: 'permissions',
|
||||
message: 'Grant the following permissions:',
|
||||
choices: [
|
||||
{ name: '📁 Read files', value: 'read', checked: true },
|
||||
{ name: '✏️ Write files', value: 'write' },
|
||||
{ name: '🗑️ Delete files', value: 'delete' },
|
||||
{ name: '🌐 Network access', value: 'network', checked: true },
|
||||
{ name: '🖥️ System commands', value: 'system' },
|
||||
{ name: '🔒 Keychain access', value: 'keychain' }
|
||||
],
|
||||
validate: (choices) => {
|
||||
if (choices.includes('delete') && !choices.includes('write')) {
|
||||
return 'Delete permission requires write permission';
|
||||
}
|
||||
return true;
|
||||
}
|
||||
},
|
||||
{
|
||||
type: 'checkbox',
|
||||
name: 'environments',
|
||||
message: 'Select deployment environments:',
|
||||
choices: [
|
||||
{ name: 'Development', value: 'dev', checked: true },
|
||||
{ name: 'Staging', value: 'staging' },
|
||||
{ name: 'Production', value: 'prod' },
|
||||
{ name: 'Testing', value: 'test', checked: true }
|
||||
],
|
||||
validate: (choices) => {
|
||||
if (!choices.includes('dev')) {
|
||||
return 'Development environment is required';
|
||||
}
|
||||
if (choices.includes('prod') && !choices.includes('staging')) {
|
||||
return 'Staging environment is recommended before production';
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
]);
|
||||
|
||||
console.log('\n✅ Selected options:');
|
||||
console.log(JSON.stringify(answers, null, 2));
|
||||
|
||||
// Example: Process selections
|
||||
console.log('\n📦 Installing selected features...');
|
||||
answers.features.forEach(feature => {
|
||||
console.log(` - ${feature}`);
|
||||
});
|
||||
|
||||
return answers;
|
||||
}
|
||||
|
||||
// Run if executed directly
|
||||
if (import.meta.url === `file://${process.argv[1]}`) {
|
||||
checkboxPromptExample()
|
||||
.then(() => process.exit(0))
|
||||
.catch((error) => {
|
||||
if (error.isTtyError) {
|
||||
console.error('❌ Prompt could not be rendered in this environment');
|
||||
} else {
|
||||
console.error('❌ User interrupted prompt');
|
||||
}
|
||||
process.exit(1);
|
||||
});
|
||||
}
|
||||
|
||||
export { checkboxPromptExample };
|
||||
@@ -0,0 +1,456 @@
|
||||
/**
|
||||
* Comprehensive CLI Example
|
||||
*
|
||||
* Complete project initialization wizard combining all prompt types:
|
||||
* - Text input with validation
|
||||
* - List selections
|
||||
* - Checkbox selections
|
||||
* - Password input
|
||||
* - Autocomplete (optional)
|
||||
* - Conditional logic
|
||||
*/
|
||||
|
||||
import inquirer from 'inquirer';
|
||||
|
||||
async function projectInitWizard() {
|
||||
console.log(`
|
||||
╔═══════════════════════════════════════════════════════════╗
|
||||
║ ║
|
||||
║ 🚀 Project Initialization Wizard 🚀 ║
|
||||
║ ║
|
||||
╚═══════════════════════════════════════════════════════════╝
|
||||
`);
|
||||
|
||||
const config = await inquirer.prompt([
|
||||
// === PROJECT BASICS ===
|
||||
{
|
||||
type: 'input',
|
||||
name: 'projectName',
|
||||
message: '📦 Project name:',
|
||||
validate: (input) => {
|
||||
if (input.length === 0) return 'Project name is required';
|
||||
if (!/^[a-z0-9-_]+$/.test(input)) {
|
||||
return 'Use lowercase letters, numbers, hyphens, and underscores only';
|
||||
}
|
||||
if (input.length < 3) return 'Project name must be at least 3 characters';
|
||||
return true;
|
||||
},
|
||||
transformer: (input) => input.toLowerCase()
|
||||
},
|
||||
{
|
||||
type: 'input',
|
||||
name: 'description',
|
||||
message: '📝 Project description:',
|
||||
validate: (input) => input.length > 0 || 'Description is required'
|
||||
},
|
||||
{
|
||||
type: 'input',
|
||||
name: 'version',
|
||||
message: '🏷️ Initial version:',
|
||||
default: '0.1.0',
|
||||
validate: (input) => {
|
||||
return /^\d+\.\d+\.\d+$/.test(input) || 'Use semantic versioning (e.g., 0.1.0)';
|
||||
}
|
||||
},
|
||||
{
|
||||
type: 'input',
|
||||
name: 'author',
|
||||
message: '👤 Author name:',
|
||||
default: process.env.USER || ''
|
||||
},
|
||||
{
|
||||
type: 'input',
|
||||
name: 'email',
|
||||
message: '📧 Author email:',
|
||||
validate: (input) => {
|
||||
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
||||
return emailRegex.test(input) || 'Invalid email address';
|
||||
}
|
||||
},
|
||||
{
|
||||
type: 'list',
|
||||
name: 'license',
|
||||
message: '📜 License:',
|
||||
choices: ['MIT', 'Apache-2.0', 'GPL-3.0', 'BSD-3-Clause', 'ISC', 'Unlicensed'],
|
||||
default: 'MIT'
|
||||
},
|
||||
|
||||
// === TECHNOLOGY STACK ===
|
||||
{
|
||||
type: 'list',
|
||||
name: 'projectType',
|
||||
message: '🛠️ Project type:',
|
||||
choices: [
|
||||
'Web Application',
|
||||
'CLI Tool',
|
||||
'API/Backend',
|
||||
'Library/Package',
|
||||
'Desktop Application',
|
||||
'Mobile Application'
|
||||
]
|
||||
},
|
||||
{
|
||||
type: 'list',
|
||||
name: 'language',
|
||||
message: '💻 Programming language:',
|
||||
choices: [
|
||||
{ name: 'TypeScript (Recommended)', value: 'typescript', short: 'TS' },
|
||||
{ name: 'JavaScript', value: 'javascript', short: 'JS' },
|
||||
{ name: 'Python', value: 'python', short: 'Py' },
|
||||
{ name: 'Go', value: 'go', short: 'Go' },
|
||||
{ name: 'Rust', value: 'rust', short: 'Rust' }
|
||||
]
|
||||
},
|
||||
{
|
||||
type: 'list',
|
||||
name: 'framework',
|
||||
message: '🎨 Framework/Runtime:',
|
||||
choices: (answers) => {
|
||||
const frameworks = {
|
||||
typescript: ['Next.js', 'Remix', 'Nest.js', 'Express', 'Fastify', 'Node.js'],
|
||||
javascript: ['React', 'Vue', 'Svelte', 'Express', 'Fastify', 'Node.js'],
|
||||
python: ['FastAPI', 'Django', 'Flask', 'Tornado', 'Sanic'],
|
||||
go: ['Gin', 'Echo', 'Fiber', 'Standard library'],
|
||||
rust: ['Actix', 'Rocket', 'Axum', 'Warp']
|
||||
};
|
||||
return frameworks[answers.language] || ['None'];
|
||||
}
|
||||
},
|
||||
|
||||
// === FEATURES & TOOLS ===
|
||||
{
|
||||
type: 'checkbox',
|
||||
name: 'features',
|
||||
message: '✨ Select features:',
|
||||
choices: (answers) => {
|
||||
const baseFeatures = [
|
||||
{ name: 'Environment variables (.env)', value: 'env', checked: true },
|
||||
{ name: 'Configuration management', value: 'config', checked: true },
|
||||
{ name: 'Logging', value: 'logging', checked: true },
|
||||
{ name: 'Error handling', value: 'error-handling', checked: true }
|
||||
];
|
||||
|
||||
if (answers.projectType === 'Web Application' || answers.projectType === 'API/Backend') {
|
||||
baseFeatures.push(
|
||||
{ name: 'Authentication', value: 'auth' },
|
||||
{ name: 'Database integration', value: 'database' },
|
||||
{ name: 'API documentation', value: 'api-docs' },
|
||||
{ name: 'CORS handling', value: 'cors' }
|
||||
);
|
||||
}
|
||||
|
||||
if (answers.projectType === 'CLI Tool') {
|
||||
baseFeatures.push(
|
||||
{ name: 'Command-line arguments parser', value: 'cli-parser', checked: true },
|
||||
{ name: 'Interactive prompts', value: 'prompts', checked: true },
|
||||
{ name: 'Progress bars', value: 'progress' }
|
||||
);
|
||||
}
|
||||
|
||||
return baseFeatures;
|
||||
},
|
||||
validate: (choices) => choices.length > 0 || 'Select at least one feature'
|
||||
},
|
||||
{
|
||||
type: 'checkbox',
|
||||
name: 'devTools',
|
||||
message: '🔧 Development tools:',
|
||||
choices: (answers) => {
|
||||
const tools = [];
|
||||
|
||||
if (['typescript', 'javascript'].includes(answers.language)) {
|
||||
tools.push(
|
||||
{ name: 'ESLint - Linting', value: 'eslint', checked: true },
|
||||
{ name: 'Prettier - Code formatting', value: 'prettier', checked: true },
|
||||
{ name: 'Husky - Git hooks', value: 'husky' },
|
||||
{ name: 'Jest - Testing framework', value: 'jest', checked: true },
|
||||
{ name: 'TypeDoc/JSDoc - Documentation', value: 'docs' }
|
||||
);
|
||||
} else if (answers.language === 'python') {
|
||||
tools.push(
|
||||
{ name: 'Black - Code formatting', value: 'black', checked: true },
|
||||
{ name: 'Flake8 - Linting', value: 'flake8', checked: true },
|
||||
{ name: 'mypy - Type checking', value: 'mypy' },
|
||||
{ name: 'pytest - Testing framework', value: 'pytest', checked: true },
|
||||
{ name: 'Sphinx - Documentation', value: 'sphinx' }
|
||||
);
|
||||
}
|
||||
|
||||
return tools;
|
||||
},
|
||||
default: ['eslint', 'prettier', 'jest']
|
||||
},
|
||||
|
||||
// === DATABASE CONFIGURATION ===
|
||||
{
|
||||
type: 'confirm',
|
||||
name: 'useDatabase',
|
||||
message: '🗄️ Use database?',
|
||||
default: (answers) => {
|
||||
return answers.features.includes('database') ||
|
||||
['Web Application', 'API/Backend'].includes(answers.projectType);
|
||||
},
|
||||
when: (answers) => ['Web Application', 'API/Backend', 'CLI Tool'].includes(answers.projectType)
|
||||
},
|
||||
{
|
||||
type: 'list',
|
||||
name: 'databaseType',
|
||||
message: '📊 Database type:',
|
||||
choices: [
|
||||
{ name: '🐘 PostgreSQL (Relational)', value: 'postgresql' },
|
||||
{ name: '🐬 MySQL (Relational)', value: 'mysql' },
|
||||
{ name: '🍃 MongoDB (Document)', value: 'mongodb' },
|
||||
{ name: '⚡ Redis (Key-Value)', value: 'redis' },
|
||||
{ name: '📁 SQLite (Embedded)', value: 'sqlite' },
|
||||
{ name: '🔥 Supabase (PostgreSQL + APIs)', value: 'supabase' }
|
||||
],
|
||||
when: (answers) => answers.useDatabase
|
||||
},
|
||||
{
|
||||
type: 'list',
|
||||
name: 'databaseORM',
|
||||
message: '🔗 ORM/Database client:',
|
||||
choices: (answers) => {
|
||||
const orms = {
|
||||
typescript: {
|
||||
postgresql: ['Prisma', 'TypeORM', 'Kysely', 'Drizzle'],
|
||||
mysql: ['Prisma', 'TypeORM', 'Kysely', 'Drizzle'],
|
||||
mongodb: ['Mongoose', 'Prisma', 'TypeORM'],
|
||||
sqlite: ['Prisma', 'TypeORM', 'Better-SQLite3'],
|
||||
supabase: ['Supabase Client', 'Prisma']
|
||||
},
|
||||
python: {
|
||||
postgresql: ['SQLAlchemy', 'Django ORM', 'Tortoise ORM'],
|
||||
mysql: ['SQLAlchemy', 'Django ORM', 'Tortoise ORM'],
|
||||
mongodb: ['Motor', 'PyMongo', 'MongoEngine'],
|
||||
sqlite: ['SQLAlchemy', 'Django ORM']
|
||||
}
|
||||
};
|
||||
|
||||
const lang = answers.language;
|
||||
const db = answers.databaseType;
|
||||
return orms[lang]?.[db] || ['None'];
|
||||
},
|
||||
when: (answers) => answers.useDatabase && answers.databaseType !== 'redis'
|
||||
},
|
||||
|
||||
// === TESTING CONFIGURATION ===
|
||||
{
|
||||
type: 'confirm',
|
||||
name: 'setupTesting',
|
||||
message: '🧪 Setup testing?',
|
||||
default: true
|
||||
},
|
||||
{
|
||||
type: 'checkbox',
|
||||
name: 'testTypes',
|
||||
message: '🔬 Test types:',
|
||||
choices: [
|
||||
{ name: 'Unit tests', value: 'unit', checked: true },
|
||||
{ name: 'Integration tests', value: 'integration', checked: true },
|
||||
{ name: 'E2E tests', value: 'e2e' },
|
||||
{ name: 'Performance tests', value: 'performance' }
|
||||
],
|
||||
when: (answers) => answers.setupTesting
|
||||
},
|
||||
|
||||
// === CI/CD CONFIGURATION ===
|
||||
{
|
||||
type: 'confirm',
|
||||
name: 'setupCICD',
|
||||
message: '⚙️ Setup CI/CD?',
|
||||
default: true
|
||||
},
|
||||
{
|
||||
type: 'list',
|
||||
name: 'cicdProvider',
|
||||
message: '🔄 CI/CD provider:',
|
||||
choices: ['GitHub Actions', 'GitLab CI', 'CircleCI', 'Jenkins', 'None'],
|
||||
when: (answers) => answers.setupCICD
|
||||
},
|
||||
|
||||
// === DEPLOYMENT CONFIGURATION ===
|
||||
{
|
||||
type: 'confirm',
|
||||
name: 'setupDeployment',
|
||||
message: '🚀 Setup deployment?',
|
||||
default: (answers) => answers.projectType !== 'Library/Package'
|
||||
},
|
||||
{
|
||||
type: 'list',
|
||||
name: 'deploymentPlatform',
|
||||
message: '☁️ Deployment platform:',
|
||||
choices: (answers) => {
|
||||
if (answers.projectType === 'Web Application') {
|
||||
return ['Vercel', 'Netlify', 'AWS', 'Google Cloud', 'Azure', 'Self-hosted'];
|
||||
} else if (answers.projectType === 'API/Backend') {
|
||||
return ['AWS', 'Google Cloud', 'Azure', 'DigitalOcean', 'Heroku', 'Self-hosted'];
|
||||
} else if (answers.projectType === 'CLI Tool') {
|
||||
return ['npm', 'PyPI', 'Homebrew', 'Binary releases', 'Docker'];
|
||||
}
|
||||
return ['AWS', 'Google Cloud', 'Azure', 'Self-hosted'];
|
||||
},
|
||||
when: (answers) => answers.setupDeployment
|
||||
},
|
||||
{
|
||||
type: 'confirm',
|
||||
name: 'useDocker',
|
||||
message: '🐳 Use Docker?',
|
||||
default: true,
|
||||
when: (answers) => {
|
||||
return answers.setupDeployment &&
|
||||
!['Vercel', 'Netlify'].includes(answers.deploymentPlatform);
|
||||
}
|
||||
},
|
||||
|
||||
// === MONITORING & OBSERVABILITY ===
|
||||
{
|
||||
type: 'confirm',
|
||||
name: 'setupMonitoring',
|
||||
message: '📊 Setup monitoring & observability?',
|
||||
default: (answers) => answers.projectType !== 'Library/Package'
|
||||
},
|
||||
{
|
||||
type: 'checkbox',
|
||||
name: 'monitoringTools',
|
||||
message: '📈 Monitoring tools:',
|
||||
choices: [
|
||||
{ name: 'Sentry - Error tracking', value: 'sentry' },
|
||||
{ name: 'DataDog - Full observability', value: 'datadog' },
|
||||
{ name: 'Prometheus - Metrics', value: 'prometheus' },
|
||||
{ name: 'Grafana - Dashboards', value: 'grafana' },
|
||||
{ name: 'New Relic - APM', value: 'newrelic' }
|
||||
],
|
||||
when: (answers) => answers.setupMonitoring
|
||||
},
|
||||
|
||||
// === DOCUMENTATION ===
|
||||
{
|
||||
type: 'confirm',
|
||||
name: 'generateDocs',
|
||||
message: '📚 Generate documentation?',
|
||||
default: true
|
||||
},
|
||||
{
|
||||
type: 'checkbox',
|
||||
name: 'docTypes',
|
||||
message: '📖 Documentation types:',
|
||||
choices: [
|
||||
{ name: 'README.md', value: 'readme', checked: true },
|
||||
{ name: 'API documentation', value: 'api', checked: true },
|
||||
{ name: 'Contributing guidelines', value: 'contributing' },
|
||||
{ name: 'Code of conduct', value: 'coc' },
|
||||
{ name: 'Changelog', value: 'changelog', checked: true }
|
||||
],
|
||||
when: (answers) => answers.generateDocs
|
||||
},
|
||||
|
||||
// === SECURITY ===
|
||||
{
|
||||
type: 'confirm',
|
||||
name: 'securitySetup',
|
||||
message: '🔒 Setup security features?',
|
||||
default: true,
|
||||
when: (answers) => ['Web Application', 'API/Backend'].includes(answers.projectType)
|
||||
},
|
||||
{
|
||||
type: 'checkbox',
|
||||
name: 'securityFeatures',
|
||||
message: '🛡️ Security features:',
|
||||
choices: [
|
||||
{ name: 'Dependency scanning', value: 'dep-scan', checked: true },
|
||||
{ name: 'Secret scanning', value: 'secret-scan', checked: true },
|
||||
{ name: 'HTTPS enforcement', value: 'https' },
|
||||
{ name: 'Rate limiting', value: 'rate-limit' },
|
||||
{ name: 'Input validation', value: 'validation', checked: true },
|
||||
{ name: 'Security headers', value: 'headers' }
|
||||
],
|
||||
when: (answers) => answers.securitySetup
|
||||
},
|
||||
|
||||
// === FINAL CONFIRMATION ===
|
||||
{
|
||||
type: 'confirm',
|
||||
name: 'confirm',
|
||||
message: '✅ Initialize project with these settings?',
|
||||
default: true
|
||||
}
|
||||
]);
|
||||
|
||||
if (!config.confirm) {
|
||||
console.log('\n❌ Project initialization cancelled.\n');
|
||||
return null;
|
||||
}
|
||||
|
||||
// Display configuration summary
|
||||
console.log('\n' + '═'.repeat(60));
|
||||
console.log('📋 PROJECT CONFIGURATION SUMMARY');
|
||||
console.log('═'.repeat(60) + '\n');
|
||||
|
||||
console.log(`📦 Project: ${config.projectName} v${config.version}`);
|
||||
console.log(`📝 Description: ${config.description}`);
|
||||
console.log(`👤 Author: ${config.author} <${config.email}>`);
|
||||
console.log(`📜 License: ${config.license}\n`);
|
||||
|
||||
console.log(`💻 Language: ${config.language}`);
|
||||
console.log(`🎨 Framework: ${config.framework}`);
|
||||
console.log(`🛠️ Type: ${config.projectType}\n`);
|
||||
|
||||
if (config.useDatabase) {
|
||||
console.log(`🗄️ Database: ${config.databaseType}`);
|
||||
if (config.databaseORM) {
|
||||
console.log(`🔗 ORM: ${config.databaseORM}\n`);
|
||||
}
|
||||
}
|
||||
|
||||
if (config.features.length > 0) {
|
||||
console.log(`✨ Features: ${config.features.join(', ')}`);
|
||||
}
|
||||
|
||||
if (config.devTools.length > 0) {
|
||||
console.log(`🔧 Dev Tools: ${config.devTools.join(', ')}\n`);
|
||||
}
|
||||
|
||||
if (config.setupDeployment) {
|
||||
console.log(`🚀 Deployment: ${config.deploymentPlatform}`);
|
||||
if (config.useDocker) console.log(`🐳 Docker: Enabled`);
|
||||
}
|
||||
|
||||
if (config.setupCICD) {
|
||||
console.log(`⚙️ CI/CD: ${config.cicdProvider}`);
|
||||
}
|
||||
|
||||
console.log('\n' + '═'.repeat(60) + '\n');
|
||||
|
||||
console.log('🎉 Configuration complete! Initializing project...\n');
|
||||
|
||||
// Here you would actually create the project files
|
||||
// This is just a demonstration
|
||||
|
||||
return config;
|
||||
}
|
||||
|
||||
// Run if executed directly
|
||||
if (import.meta.url === `file://${process.argv[1]}`) {
|
||||
projectInitWizard()
|
||||
.then((config) => {
|
||||
if (config) {
|
||||
console.log('✅ Project initialized successfully!\n');
|
||||
console.log('Next steps:');
|
||||
console.log(` 1. cd ${config.projectName}`);
|
||||
console.log(' 2. Install dependencies');
|
||||
console.log(' 3. Start development');
|
||||
}
|
||||
process.exit(0);
|
||||
})
|
||||
.catch((error) => {
|
||||
if (error.isTtyError) {
|
||||
console.error('❌ Prompt could not be rendered in this environment');
|
||||
} else {
|
||||
console.error('❌ User interrupted prompt');
|
||||
}
|
||||
process.exit(1);
|
||||
});
|
||||
}
|
||||
|
||||
export { projectInitWizard };
|
||||
460
skills/inquirer-patterns/templates/nodejs/conditional-prompt.js
Normal file
460
skills/inquirer-patterns/templates/nodejs/conditional-prompt.js
Normal file
@@ -0,0 +1,460 @@
|
||||
/**
|
||||
* Conditional Prompt Template
|
||||
*
|
||||
* Use for: Dynamic forms based on previous answers
|
||||
* Features: Skip logic, dependent questions, branching
|
||||
*/
|
||||
|
||||
import inquirer from 'inquirer';
|
||||
|
||||
async function conditionalPromptExample() {
|
||||
const answers = await inquirer.prompt([
|
||||
{
|
||||
type: 'confirm',
|
||||
name: 'useDatabase',
|
||||
message: 'Do you want to use a database?',
|
||||
default: true
|
||||
},
|
||||
{
|
||||
type: 'list',
|
||||
name: 'databaseType',
|
||||
message: 'Select database type:',
|
||||
choices: ['PostgreSQL', 'MySQL', 'MongoDB', 'SQLite', 'Redis'],
|
||||
when: (answers) => answers.useDatabase
|
||||
},
|
||||
{
|
||||
type: 'input',
|
||||
name: 'databaseHost',
|
||||
message: 'Database host:',
|
||||
default: 'localhost',
|
||||
when: (answers) => {
|
||||
return answers.useDatabase && answers.databaseType !== 'SQLite';
|
||||
}
|
||||
},
|
||||
{
|
||||
type: 'input',
|
||||
name: 'databasePort',
|
||||
message: 'Database port:',
|
||||
default: (answers) => {
|
||||
const ports = {
|
||||
'PostgreSQL': '5432',
|
||||
'MySQL': '3306',
|
||||
'MongoDB': '27017',
|
||||
'Redis': '6379'
|
||||
};
|
||||
return ports[answers.databaseType] || '5432';
|
||||
},
|
||||
when: (answers) => {
|
||||
return answers.useDatabase && answers.databaseType !== 'SQLite';
|
||||
}
|
||||
},
|
||||
{
|
||||
type: 'input',
|
||||
name: 'databaseName',
|
||||
message: 'Database name:',
|
||||
when: (answers) => answers.useDatabase,
|
||||
validate: (input) => input.length > 0 || 'Database name required'
|
||||
},
|
||||
{
|
||||
type: 'confirm',
|
||||
name: 'useAuthentication',
|
||||
message: 'Do you want to use authentication?',
|
||||
default: true,
|
||||
when: (answers) => answers.useDatabase
|
||||
},
|
||||
{
|
||||
type: 'input',
|
||||
name: 'databaseUsername',
|
||||
message: 'Database username:',
|
||||
when: (answers) => answers.useDatabase && answers.useAuthentication,
|
||||
validate: (input) => input.length > 0 || 'Username required'
|
||||
},
|
||||
{
|
||||
type: 'password',
|
||||
name: 'databasePassword',
|
||||
message: 'Database password:',
|
||||
mask: '*',
|
||||
when: (answers) => answers.useDatabase && answers.useAuthentication
|
||||
},
|
||||
{
|
||||
type: 'confirm',
|
||||
name: 'useSSL',
|
||||
message: 'Use SSL connection?',
|
||||
default: false,
|
||||
when: (answers) => {
|
||||
return answers.useDatabase &&
|
||||
answers.databaseType !== 'SQLite' &&
|
||||
answers.databaseHost !== 'localhost';
|
||||
}
|
||||
},
|
||||
{
|
||||
type: 'input',
|
||||
name: 'sslCertPath',
|
||||
message: 'Path to SSL certificate:',
|
||||
when: (answers) => answers.useSSL,
|
||||
validate: (input) => input.length > 0 || 'SSL certificate path required'
|
||||
}
|
||||
]);
|
||||
|
||||
console.log('\n✅ Configuration:');
|
||||
console.log(JSON.stringify(answers, null, 2));
|
||||
|
||||
return answers;
|
||||
}
|
||||
|
||||
// Example: Deployment configuration wizard
|
||||
async function deploymentWizard() {
|
||||
console.log('\n🚀 Deployment Configuration Wizard\n');
|
||||
|
||||
const config = await inquirer.prompt([
|
||||
{
|
||||
type: 'list',
|
||||
name: 'environment',
|
||||
message: 'Select deployment environment:',
|
||||
choices: ['Development', 'Staging', 'Production']
|
||||
},
|
||||
{
|
||||
type: 'confirm',
|
||||
name: 'useDocker',
|
||||
message: 'Deploy using Docker?',
|
||||
default: true
|
||||
},
|
||||
{
|
||||
type: 'input',
|
||||
name: 'dockerImage',
|
||||
message: 'Docker image name:',
|
||||
when: (answers) => answers.useDocker,
|
||||
default: 'myapp:latest',
|
||||
validate: (input) => /^[a-z0-9-_/:.]+$/.test(input) || 'Invalid Docker image name'
|
||||
},
|
||||
{
|
||||
type: 'list',
|
||||
name: 'registry',
|
||||
message: 'Container registry:',
|
||||
choices: ['Docker Hub', 'GitHub Container Registry', 'AWS ECR', 'Google Artifact Registry'],
|
||||
when: (answers) => answers.useDocker
|
||||
},
|
||||
{
|
||||
type: 'list',
|
||||
name: 'platform',
|
||||
message: 'Deployment platform:',
|
||||
choices: ['AWS', 'Google Cloud', 'Azure', 'DigitalOcean', 'Vercel', 'Netlify', 'Self-hosted']
|
||||
},
|
||||
{
|
||||
type: 'list',
|
||||
name: 'awsService',
|
||||
message: 'AWS service:',
|
||||
choices: ['ECS', 'EKS', 'Lambda', 'Elastic Beanstalk', 'EC2'],
|
||||
when: (answers) => answers.platform === 'AWS'
|
||||
},
|
||||
{
|
||||
type: 'list',
|
||||
name: 'gcpService',
|
||||
message: 'Google Cloud service:',
|
||||
choices: ['Cloud Run', 'GKE', 'App Engine', 'Compute Engine'],
|
||||
when: (answers) => answers.platform === 'Google Cloud'
|
||||
},
|
||||
{
|
||||
type: 'confirm',
|
||||
name: 'autoScale',
|
||||
message: 'Enable auto-scaling?',
|
||||
default: true,
|
||||
when: (answers) => {
|
||||
const scalableServices = ['ECS', 'EKS', 'Cloud Run', 'GKE'];
|
||||
return scalableServices.includes(answers.awsService) ||
|
||||
scalableServices.includes(answers.gcpService);
|
||||
}
|
||||
},
|
||||
{
|
||||
type: 'input',
|
||||
name: 'minInstances',
|
||||
message: 'Minimum instances:',
|
||||
default: '1',
|
||||
when: (answers) => answers.autoScale,
|
||||
validate: (input) => {
|
||||
const num = parseInt(input);
|
||||
return num > 0 || 'Must be at least 1';
|
||||
}
|
||||
},
|
||||
{
|
||||
type: 'input',
|
||||
name: 'maxInstances',
|
||||
message: 'Maximum instances:',
|
||||
default: '10',
|
||||
when: (answers) => answers.autoScale,
|
||||
validate: (input, answers) => {
|
||||
const num = parseInt(input);
|
||||
const min = parseInt(answers.minInstances);
|
||||
return num >= min || `Must be at least ${min}`;
|
||||
}
|
||||
},
|
||||
{
|
||||
type: 'confirm',
|
||||
name: 'useCDN',
|
||||
message: 'Use CDN for static assets?',
|
||||
default: true,
|
||||
when: (answers) => answers.environment === 'Production'
|
||||
},
|
||||
{
|
||||
type: 'list',
|
||||
name: 'cdnProvider',
|
||||
message: 'CDN provider:',
|
||||
choices: ['CloudFlare', 'AWS CloudFront', 'Google Cloud CDN', 'Azure CDN'],
|
||||
when: (answers) => answers.useCDN
|
||||
},
|
||||
{
|
||||
type: 'confirm',
|
||||
name: 'setupMonitoring',
|
||||
message: 'Setup monitoring?',
|
||||
default: true
|
||||
},
|
||||
{
|
||||
type: 'checkbox',
|
||||
name: 'monitoringTools',
|
||||
message: 'Select monitoring tools:',
|
||||
choices: ['Prometheus', 'Grafana', 'Datadog', 'New Relic', 'Sentry'],
|
||||
when: (answers) => answers.setupMonitoring,
|
||||
validate: (choices) => choices.length > 0 || 'Select at least one tool'
|
||||
}
|
||||
]);
|
||||
|
||||
console.log('\n✅ Deployment configuration complete!');
|
||||
console.log(JSON.stringify(config, null, 2));
|
||||
|
||||
return config;
|
||||
}
|
||||
|
||||
// Example: Feature flag configuration
|
||||
async function featureFlagWizard() {
|
||||
console.log('\n🎛️ Feature Flag Configuration\n');
|
||||
|
||||
const config = await inquirer.prompt([
|
||||
{
|
||||
type: 'input',
|
||||
name: 'featureName',
|
||||
message: 'Feature name:',
|
||||
validate: (input) => /^[a-z-_]+$/.test(input) || 'Use lowercase, hyphens, underscores only'
|
||||
},
|
||||
{
|
||||
type: 'confirm',
|
||||
name: 'enabledByDefault',
|
||||
message: 'Enabled by default?',
|
||||
default: false
|
||||
},
|
||||
{
|
||||
type: 'list',
|
||||
name: 'rolloutStrategy',
|
||||
message: 'Rollout strategy:',
|
||||
choices: [
|
||||
'All users',
|
||||
'Percentage rollout',
|
||||
'User targeting',
|
||||
'Beta users only',
|
||||
'Manual control'
|
||||
]
|
||||
},
|
||||
{
|
||||
type: 'input',
|
||||
name: 'rolloutPercentage',
|
||||
message: 'Rollout percentage (0-100):',
|
||||
when: (answers) => answers.rolloutStrategy === 'Percentage rollout',
|
||||
default: '10',
|
||||
validate: (input) => {
|
||||
const num = parseInt(input);
|
||||
return (num >= 0 && num <= 100) || 'Must be between 0 and 100';
|
||||
}
|
||||
},
|
||||
{
|
||||
type: 'checkbox',
|
||||
name: 'targetUserGroups',
|
||||
message: 'Target user groups:',
|
||||
choices: ['Beta testers', 'Premium users', 'Internal team', 'Early adopters', 'Specific regions'],
|
||||
when: (answers) => answers.rolloutStrategy === 'User targeting',
|
||||
validate: (choices) => choices.length > 0 || 'Select at least one group'
|
||||
},
|
||||
{
|
||||
type: 'checkbox',
|
||||
name: 'targetRegions',
|
||||
message: 'Target regions:',
|
||||
choices: ['North America', 'Europe', 'Asia Pacific', 'South America', 'Africa'],
|
||||
when: (answers) => {
|
||||
return answers.rolloutStrategy === 'User targeting' &&
|
||||
answers.targetUserGroups.includes('Specific regions');
|
||||
}
|
||||
},
|
||||
{
|
||||
type: 'confirm',
|
||||
name: 'enableMetrics',
|
||||
message: 'Track feature usage metrics?',
|
||||
default: true
|
||||
},
|
||||
{
|
||||
type: 'checkbox',
|
||||
name: 'metrics',
|
||||
message: 'Select metrics to track:',
|
||||
choices: [
|
||||
'Usage count',
|
||||
'User adoption rate',
|
||||
'Performance impact',
|
||||
'Error rate',
|
||||
'User feedback'
|
||||
],
|
||||
when: (answers) => answers.enableMetrics
|
||||
},
|
||||
{
|
||||
type: 'confirm',
|
||||
name: 'addExpirationDate',
|
||||
message: 'Set feature flag expiration?',
|
||||
default: false
|
||||
},
|
||||
{
|
||||
type: 'input',
|
||||
name: 'expirationDate',
|
||||
message: 'Expiration date (YYYY-MM-DD):',
|
||||
when: (answers) => answers.addExpirationDate,
|
||||
validate: (input) => {
|
||||
const date = new Date(input);
|
||||
if (isNaN(date.getTime())) return 'Invalid date format';
|
||||
if (date < new Date()) return 'Date must be in the future';
|
||||
return true;
|
||||
}
|
||||
}
|
||||
]);
|
||||
|
||||
console.log('\n✅ Feature flag configured!');
|
||||
console.log(JSON.stringify(config, null, 2));
|
||||
|
||||
return config;
|
||||
}
|
||||
|
||||
// Example: CI/CD pipeline setup
|
||||
async function cicdPipelineWizard() {
|
||||
console.log('\n⚙️ CI/CD Pipeline Configuration\n');
|
||||
|
||||
const config = await inquirer.prompt([
|
||||
{
|
||||
type: 'list',
|
||||
name: 'provider',
|
||||
message: 'CI/CD provider:',
|
||||
choices: ['GitHub Actions', 'GitLab CI', 'CircleCI', 'Jenkins', 'Travis CI']
|
||||
},
|
||||
{
|
||||
type: 'checkbox',
|
||||
name: 'triggers',
|
||||
message: 'Pipeline triggers:',
|
||||
choices: [
|
||||
'Push to main/master',
|
||||
'Pull request',
|
||||
'Tag creation',
|
||||
'Manual trigger',
|
||||
'Scheduled (cron)'
|
||||
],
|
||||
default: ['Push to main/master', 'Pull request']
|
||||
},
|
||||
{
|
||||
type: 'input',
|
||||
name: 'cronSchedule',
|
||||
message: 'Cron schedule:',
|
||||
when: (answers) => answers.triggers.includes('Scheduled (cron)'),
|
||||
default: '0 2 * * *',
|
||||
validate: (input) => {
|
||||
// Basic cron validation
|
||||
const parts = input.split(' ');
|
||||
return parts.length === 5 || 'Invalid cron format (5 parts required)';
|
||||
}
|
||||
},
|
||||
{
|
||||
type: 'checkbox',
|
||||
name: 'stages',
|
||||
message: 'Pipeline stages:',
|
||||
choices: ['Build', 'Test', 'Lint', 'Security scan', 'Deploy'],
|
||||
default: ['Build', 'Test', 'Deploy'],
|
||||
validate: (choices) => choices.length > 0 || 'Select at least one stage'
|
||||
},
|
||||
{
|
||||
type: 'checkbox',
|
||||
name: 'testTypes',
|
||||
message: 'Test types to run:',
|
||||
choices: ['Unit tests', 'Integration tests', 'E2E tests', 'Performance tests'],
|
||||
when: (answers) => answers.stages.includes('Test')
|
||||
},
|
||||
{
|
||||
type: 'checkbox',
|
||||
name: 'securityTools',
|
||||
message: 'Security scanning tools:',
|
||||
choices: ['Snyk', 'Dependabot', 'SonarQube', 'OWASP Dependency Check'],
|
||||
when: (answers) => answers.stages.includes('Security scan')
|
||||
},
|
||||
{
|
||||
type: 'checkbox',
|
||||
name: 'deployEnvironments',
|
||||
message: 'Deployment environments:',
|
||||
choices: ['Development', 'Staging', 'Production'],
|
||||
when: (answers) => answers.stages.includes('Deploy'),
|
||||
default: ['Staging', 'Production']
|
||||
},
|
||||
{
|
||||
type: 'confirm',
|
||||
name: 'requireApproval',
|
||||
message: 'Require manual approval for production?',
|
||||
default: true,
|
||||
when: (answers) => {
|
||||
return answers.stages.includes('Deploy') &&
|
||||
answers.deployEnvironments?.includes('Production');
|
||||
}
|
||||
},
|
||||
{
|
||||
type: 'confirm',
|
||||
name: 'enableNotifications',
|
||||
message: 'Enable build notifications?',
|
||||
default: true
|
||||
},
|
||||
{
|
||||
type: 'checkbox',
|
||||
name: 'notificationChannels',
|
||||
message: 'Notification channels:',
|
||||
choices: ['Email', 'Slack', 'Discord', 'Microsoft Teams'],
|
||||
when: (answers) => answers.enableNotifications
|
||||
}
|
||||
]);
|
||||
|
||||
console.log('\n✅ CI/CD pipeline configured!');
|
||||
console.log(JSON.stringify(config, null, 2));
|
||||
|
||||
return config;
|
||||
}
|
||||
|
||||
// Run if executed directly
|
||||
if (import.meta.url === `file://${process.argv[1]}`) {
|
||||
(async () => {
|
||||
console.log('=== Conditional Prompt Examples ===\n');
|
||||
|
||||
console.log('1. Database Configuration');
|
||||
await conditionalPromptExample();
|
||||
|
||||
console.log('\n2. Deployment Wizard');
|
||||
await deploymentWizard();
|
||||
|
||||
console.log('\n3. Feature Flag Configuration');
|
||||
await featureFlagWizard();
|
||||
|
||||
console.log('\n4. CI/CD Pipeline Setup');
|
||||
await cicdPipelineWizard();
|
||||
|
||||
process.exit(0);
|
||||
})().catch((error) => {
|
||||
if (error.isTtyError) {
|
||||
console.error('❌ Prompt could not be rendered in this environment');
|
||||
} else {
|
||||
console.error('❌ User interrupted prompt');
|
||||
}
|
||||
process.exit(1);
|
||||
});
|
||||
}
|
||||
|
||||
export {
|
||||
conditionalPromptExample,
|
||||
deploymentWizard,
|
||||
featureFlagWizard,
|
||||
cicdPipelineWizard
|
||||
};
|
||||
104
skills/inquirer-patterns/templates/nodejs/list-prompt.js
Normal file
104
skills/inquirer-patterns/templates/nodejs/list-prompt.js
Normal file
@@ -0,0 +1,104 @@
|
||||
/**
|
||||
* List Selection Prompt Template
|
||||
*
|
||||
* Use for: Single choice from predefined options
|
||||
* Features: Arrow key navigation, search filtering
|
||||
*/
|
||||
|
||||
import inquirer from 'inquirer';
|
||||
|
||||
async function listPromptExample() {
|
||||
const answers = await inquirer.prompt([
|
||||
{
|
||||
type: 'list',
|
||||
name: 'framework',
|
||||
message: 'Choose your preferred framework:',
|
||||
choices: [
|
||||
'React',
|
||||
'Vue',
|
||||
'Angular',
|
||||
'Svelte',
|
||||
'Next.js',
|
||||
'Nuxt.js'
|
||||
],
|
||||
default: 'React'
|
||||
},
|
||||
{
|
||||
type: 'list',
|
||||
name: 'language',
|
||||
message: 'Choose programming language:',
|
||||
choices: [
|
||||
{ name: 'JavaScript', value: 'js' },
|
||||
{ name: 'TypeScript', value: 'ts' },
|
||||
{ name: 'Python', value: 'py' },
|
||||
{ name: 'Ruby', value: 'rb' },
|
||||
{ name: 'Go', value: 'go' }
|
||||
],
|
||||
default: 'ts'
|
||||
},
|
||||
{
|
||||
type: 'list',
|
||||
name: 'packageManager',
|
||||
message: 'Choose package manager:',
|
||||
choices: [
|
||||
{ name: 'npm (Node Package Manager)', value: 'npm', short: 'npm' },
|
||||
{ name: 'yarn (Fast, reliable package manager)', value: 'yarn', short: 'yarn' },
|
||||
{ name: 'pnpm (Fast, disk space efficient)', value: 'pnpm', short: 'pnpm' },
|
||||
{ name: 'bun (All-in-one toolkit)', value: 'bun', short: 'bun' }
|
||||
]
|
||||
},
|
||||
{
|
||||
type: 'list',
|
||||
name: 'environment',
|
||||
message: 'Select deployment environment:',
|
||||
choices: [
|
||||
new inquirer.Separator('--- Cloud Platforms ---'),
|
||||
'AWS',
|
||||
'Google Cloud',
|
||||
'Azure',
|
||||
new inquirer.Separator('--- Serverless ---'),
|
||||
'Vercel',
|
||||
'Netlify',
|
||||
'Cloudflare Workers',
|
||||
new inquirer.Separator('--- Self-hosted ---'),
|
||||
'Docker',
|
||||
'Kubernetes'
|
||||
]
|
||||
},
|
||||
{
|
||||
type: 'list',
|
||||
name: 'database',
|
||||
message: 'Choose database:',
|
||||
choices: [
|
||||
{ name: '🐘 PostgreSQL (Relational)', value: 'postgresql' },
|
||||
{ name: '🐬 MySQL (Relational)', value: 'mysql' },
|
||||
{ name: '🍃 MongoDB (Document)', value: 'mongodb' },
|
||||
{ name: '⚡ Redis (Key-Value)', value: 'redis' },
|
||||
{ name: '📊 SQLite (Embedded)', value: 'sqlite' },
|
||||
{ name: '🔥 Supabase (PostgreSQL + APIs)', value: 'supabase' }
|
||||
],
|
||||
pageSize: 10
|
||||
}
|
||||
]);
|
||||
|
||||
console.log('\n✅ Selections:');
|
||||
console.log(JSON.stringify(answers, null, 2));
|
||||
|
||||
return answers;
|
||||
}
|
||||
|
||||
// Run if executed directly
|
||||
if (import.meta.url === `file://${process.argv[1]}`) {
|
||||
listPromptExample()
|
||||
.then(() => process.exit(0))
|
||||
.catch((error) => {
|
||||
if (error.isTtyError) {
|
||||
console.error('❌ Prompt could not be rendered in this environment');
|
||||
} else {
|
||||
console.error('❌ User interrupted prompt');
|
||||
}
|
||||
process.exit(1);
|
||||
});
|
||||
}
|
||||
|
||||
export { listPromptExample };
|
||||
208
skills/inquirer-patterns/templates/nodejs/password-prompt.js
Normal file
208
skills/inquirer-patterns/templates/nodejs/password-prompt.js
Normal file
@@ -0,0 +1,208 @@
|
||||
/**
|
||||
* Password Prompt Template
|
||||
*
|
||||
* Use for: Sensitive input (credentials, tokens)
|
||||
* Features: Hidden input, confirmation, validation
|
||||
*/
|
||||
|
||||
import inquirer from 'inquirer';
|
||||
|
||||
async function passwordPromptExample() {
|
||||
const answers = await inquirer.prompt([
|
||||
{
|
||||
type: 'password',
|
||||
name: 'password',
|
||||
message: 'Enter your password:',
|
||||
mask: '*',
|
||||
validate: (input) => {
|
||||
if (input.length < 8) {
|
||||
return 'Password must be at least 8 characters';
|
||||
}
|
||||
if (!/[A-Z]/.test(input)) {
|
||||
return 'Password must contain at least one uppercase letter';
|
||||
}
|
||||
if (!/[a-z]/.test(input)) {
|
||||
return 'Password must contain at least one lowercase letter';
|
||||
}
|
||||
if (!/[0-9]/.test(input)) {
|
||||
return 'Password must contain at least one number';
|
||||
}
|
||||
if (!/[!@#$%^&*(),.?":{}|<>]/.test(input)) {
|
||||
return 'Password must contain at least one special character';
|
||||
}
|
||||
return true;
|
||||
}
|
||||
},
|
||||
{
|
||||
type: 'password',
|
||||
name: 'confirmPassword',
|
||||
message: 'Confirm your password:',
|
||||
mask: '*',
|
||||
validate: (input, answers) => {
|
||||
if (input !== answers.password) {
|
||||
return 'Passwords do not match';
|
||||
}
|
||||
return true;
|
||||
}
|
||||
},
|
||||
{
|
||||
type: 'password',
|
||||
name: 'apiKey',
|
||||
message: 'Enter your API key:',
|
||||
mask: '•',
|
||||
validate: (input) => {
|
||||
if (input.length === 0) {
|
||||
return 'API key is required';
|
||||
}
|
||||
// Example: Validate API key format (e.g., sk-...)
|
||||
if (!input.startsWith('sk-') && !input.startsWith('pk-')) {
|
||||
return 'API key must start with "sk-" or "pk-"';
|
||||
}
|
||||
if (input.length < 32) {
|
||||
return 'API key appears to be too short';
|
||||
}
|
||||
return true;
|
||||
}
|
||||
},
|
||||
{
|
||||
type: 'password',
|
||||
name: 'oldPassword',
|
||||
message: 'Enter your old password (for password change):',
|
||||
mask: '*',
|
||||
when: (answers) => {
|
||||
// Only ask for old password if changing password
|
||||
return answers.password && answers.confirmPassword;
|
||||
},
|
||||
validate: (input) => {
|
||||
if (input.length === 0) {
|
||||
return 'Old password is required';
|
||||
}
|
||||
// In real app, you'd verify against stored password
|
||||
return true;
|
||||
}
|
||||
},
|
||||
{
|
||||
type: 'password',
|
||||
name: 'encryptionKey',
|
||||
message: 'Enter encryption key (optional):',
|
||||
mask: '#',
|
||||
validate: (input) => {
|
||||
if (input.length === 0) return true; // Optional
|
||||
if (input.length < 16) {
|
||||
return 'Encryption key must be at least 16 characters';
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
]);
|
||||
|
||||
// Don't log actual passwords!
|
||||
console.log('\n✅ Credentials received (not displayed for security)');
|
||||
console.log('Password strength:', calculatePasswordStrength(answers.password));
|
||||
console.log('API key format:', answers.apiKey.substring(0, 6) + '...');
|
||||
|
||||
// In real app, you'd:
|
||||
// - Hash the password before storing
|
||||
// - Encrypt the API key
|
||||
// - Store securely (not in plain text)
|
||||
|
||||
return {
|
||||
passwordHash: hashPassword(answers.password),
|
||||
apiKeyEncrypted: encryptApiKey(answers.apiKey)
|
||||
};
|
||||
}
|
||||
|
||||
// Helper function to calculate password strength
|
||||
function calculatePasswordStrength(password) {
|
||||
let strength = 0;
|
||||
|
||||
if (password.length >= 8) strength++;
|
||||
if (password.length >= 12) strength++;
|
||||
if (/[a-z]/.test(password)) strength++;
|
||||
if (/[A-Z]/.test(password)) strength++;
|
||||
if (/[0-9]/.test(password)) strength++;
|
||||
if (/[!@#$%^&*(),.?":{}|<>]/.test(password)) strength++;
|
||||
|
||||
const levels = ['Very Weak', 'Weak', 'Fair', 'Good', 'Strong', 'Very Strong'];
|
||||
return levels[Math.min(strength, levels.length - 1)];
|
||||
}
|
||||
|
||||
// Placeholder for password hashing (use bcrypt in production)
|
||||
function hashPassword(password) {
|
||||
return `[HASHED:${password.length}_chars]`;
|
||||
}
|
||||
|
||||
// Placeholder for API key encryption (use proper encryption in production)
|
||||
function encryptApiKey(apiKey) {
|
||||
return `[ENCRYPTED:${apiKey.substring(0, 6)}...]`;
|
||||
}
|
||||
|
||||
// Example: Secure credential storage
|
||||
async function securePasswordExample() {
|
||||
console.log('\n🔐 Secure Password Setup\n');
|
||||
|
||||
const credentials = await inquirer.prompt([
|
||||
{
|
||||
type: 'input',
|
||||
name: 'username',
|
||||
message: 'Username:',
|
||||
validate: (input) => input.length > 0 || 'Username required'
|
||||
},
|
||||
{
|
||||
type: 'password',
|
||||
name: 'password',
|
||||
message: 'Password:',
|
||||
mask: '*',
|
||||
validate: (input) => {
|
||||
const issues = [];
|
||||
if (input.length < 12) issues.push('at least 12 characters');
|
||||
if (!/[A-Z]/.test(input)) issues.push('an uppercase letter');
|
||||
if (!/[a-z]/.test(input)) issues.push('a lowercase letter');
|
||||
if (!/[0-9]/.test(input)) issues.push('a number');
|
||||
if (!/[!@#$%^&*]/.test(input)) issues.push('a special character (!@#$%^&*)');
|
||||
|
||||
if (issues.length > 0) {
|
||||
return `Password must contain: ${issues.join(', ')}`;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
},
|
||||
{
|
||||
type: 'password',
|
||||
name: 'confirm',
|
||||
message: 'Confirm password:',
|
||||
mask: '*',
|
||||
validate: (input, answers) => {
|
||||
return input === answers.password || 'Passwords do not match';
|
||||
}
|
||||
},
|
||||
{
|
||||
type: 'confirm',
|
||||
name: 'remember',
|
||||
message: 'Remember credentials? (stored securely)',
|
||||
default: false
|
||||
}
|
||||
]);
|
||||
|
||||
console.log(`\n✅ Account created for: ${credentials.username}`);
|
||||
console.log(`🔒 Password strength: ${calculatePasswordStrength(credentials.password)}`);
|
||||
|
||||
return credentials;
|
||||
}
|
||||
|
||||
// Run if executed directly
|
||||
if (import.meta.url === `file://${process.argv[1]}`) {
|
||||
passwordPromptExample()
|
||||
.then(() => securePasswordExample())
|
||||
.then(() => process.exit(0))
|
||||
.catch((error) => {
|
||||
if (error.isTtyError) {
|
||||
console.error('❌ Prompt could not be rendered in this environment');
|
||||
} else {
|
||||
console.error('❌ User interrupted prompt');
|
||||
}
|
||||
process.exit(1);
|
||||
});
|
||||
}
|
||||
|
||||
export { passwordPromptExample, securePasswordExample };
|
||||
105
skills/inquirer-patterns/templates/nodejs/text-prompt.js
Normal file
105
skills/inquirer-patterns/templates/nodejs/text-prompt.js
Normal file
@@ -0,0 +1,105 @@
|
||||
/**
|
||||
* Text Input Prompt Template
|
||||
*
|
||||
* Use for: Names, emails, URLs, paths, free-form text
|
||||
* Features: Validation, default values, transform
|
||||
*/
|
||||
|
||||
import inquirer from 'inquirer';
|
||||
|
||||
async function textPromptExample() {
|
||||
const answers = await inquirer.prompt([
|
||||
{
|
||||
type: 'input',
|
||||
name: 'username',
|
||||
message: 'Enter your username:',
|
||||
default: '',
|
||||
validate: (input) => {
|
||||
if (input.length === 0) {
|
||||
return 'Username is required';
|
||||
}
|
||||
if (input.length < 3) {
|
||||
return 'Username must be at least 3 characters';
|
||||
}
|
||||
if (!/^[a-zA-Z0-9_-]+$/.test(input)) {
|
||||
return 'Username can only contain letters, numbers, hyphens, and underscores';
|
||||
}
|
||||
return true;
|
||||
},
|
||||
transformer: (input) => {
|
||||
return input.toLowerCase();
|
||||
}
|
||||
},
|
||||
{
|
||||
type: 'input',
|
||||
name: 'email',
|
||||
message: 'Enter your email:',
|
||||
validate: (input) => {
|
||||
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
||||
return emailRegex.test(input) || 'Invalid email address';
|
||||
}
|
||||
},
|
||||
{
|
||||
type: 'input',
|
||||
name: 'website',
|
||||
message: 'Enter your website (optional):',
|
||||
default: '',
|
||||
validate: (input) => {
|
||||
if (input.length === 0) return true; // Optional field
|
||||
const urlRegex = /^https?:\/\/.+/;
|
||||
return urlRegex.test(input) || 'Must be a valid URL (http:// or https://)';
|
||||
}
|
||||
},
|
||||
{
|
||||
type: 'input',
|
||||
name: 'age',
|
||||
message: 'Enter your age:',
|
||||
validate: (input) => {
|
||||
const age = parseInt(input);
|
||||
if (isNaN(age)) {
|
||||
return 'Please enter a valid number';
|
||||
}
|
||||
if (age < 18) {
|
||||
return 'You must be at least 18 years old';
|
||||
}
|
||||
if (age > 120) {
|
||||
return 'Please enter a realistic age';
|
||||
}
|
||||
return true;
|
||||
},
|
||||
filter: (input) => parseInt(input)
|
||||
},
|
||||
{
|
||||
type: 'input',
|
||||
name: 'bio',
|
||||
message: 'Enter a short bio:',
|
||||
validate: (input) => {
|
||||
if (input.length > 200) {
|
||||
return 'Bio must be 200 characters or less';
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
]);
|
||||
|
||||
console.log('\n✅ Answers received:');
|
||||
console.log(JSON.stringify(answers, null, 2));
|
||||
|
||||
return answers;
|
||||
}
|
||||
|
||||
// Run if executed directly
|
||||
if (import.meta.url === `file://${process.argv[1]}`) {
|
||||
textPromptExample()
|
||||
.then(() => process.exit(0))
|
||||
.catch((error) => {
|
||||
if (error.isTtyError) {
|
||||
console.error('❌ Prompt could not be rendered in this environment');
|
||||
} else {
|
||||
console.error('❌ User interrupted prompt');
|
||||
}
|
||||
process.exit(1);
|
||||
});
|
||||
}
|
||||
|
||||
export { textPromptExample };
|
||||
Reference in New Issue
Block a user