Files
basecoat/packages/cli/index.js
T

193 lines
7.1 KiB
JavaScript
Raw Permalink Normal View History

2025-05-01 20:53:03 +08:00
#!/usr/bin/env node
import { Command } from 'commander';
2025-05-02 18:27:45 +08:00
import inquirer from 'inquirer';
import fs from 'fs-extra';
import path from 'path';
2025-05-08 09:06:32 +08:00
import { fileURLToPath } from 'url';
2025-05-08 00:22:34 +08:00
2025-05-01 20:53:03 +08:00
const program = new Command();
2025-05-08 09:06:32 +08:00
// Attempt to read version from the CLI's own package.json
2025-05-02 18:27:45 +08:00
let packageVersion = 'unknown';
try {
2025-05-08 09:06:32 +08:00
const cliScriptRunningDir = path.dirname(fileURLToPath(import.meta.url)); // .../packages/cli/dist
const cliPackageRootDir = path.resolve(cliScriptRunningDir, '..'); // .../packages/cli
const packageJsonPath = path.join(cliPackageRootDir, 'package.json');
2025-05-02 18:27:45 +08:00
const packageJsonContent = fs.readFileSync(packageJsonPath, 'utf8');
packageVersion = JSON.parse(packageJsonContent).version;
} catch (error) {
2025-05-08 09:06:32 +08:00
console.warn('Could not read CLI package version:', error.message);
2025-05-02 18:27:45 +08:00
}
const DEFAULT_CONFIG = {
templateEngine: null,
templateDest: './components/basecoat',
scriptDest: './static/js/basecoat'
};
2025-05-08 09:06:32 +08:00
let config = { ...DEFAULT_CONFIG }; // User-specific config, potentially updated by prompts
2025-05-02 18:27:45 +08:00
2025-05-08 09:06:32 +08:00
// Discovers available components from the CLI's bundled assets.
2025-05-02 18:27:45 +08:00
async function getAvailableComponents() {
try {
2025-05-08 09:06:32 +08:00
const cliScriptRunningDir = path.dirname(fileURLToPath(import.meta.url));
const assetsDir = path.resolve(cliScriptRunningDir, 'assets');
const jsSourceDir = path.join(assetsDir, 'js');
2025-05-02 18:27:45 +08:00
const jsFiles = await fs.readdir(jsSourceDir);
2025-05-08 09:06:32 +08:00
2025-05-02 18:27:45 +08:00
return jsFiles
.filter(file => file.endsWith('.js'))
.map(file => path.basename(file, '.js'));
} catch (error) {
2025-05-08 09:06:32 +08:00
console.error("Error reading component source directories from CLI assets:", error);
2025-05-02 18:27:45 +08:00
return [];
}
}
2025-05-08 09:06:32 +08:00
// Ensures necessary configuration (template engine, destination paths) is set, prompting the user if needed.
2025-05-02 18:27:45 +08:00
async function ensureConfiguration() {
if (!config.templateEngine) {
const engineChoice = await inquirer.prompt([
{
type: 'list',
name: 'engine',
message: 'Which template engine are you using?',
choices: ['nunjucks', 'jinja'],
}
]);
config.templateEngine = engineChoice.engine;
}
2025-05-08 09:06:32 +08:00
const pathPrompts = [];
if (!config.templateDest || config.templateDest === DEFAULT_CONFIG.templateDest) {
pathPrompts.push({
2025-05-16 02:51:05 -07:00
type: 'input',
name: 'templateDest',
message: 'Where should template files be placed?',
2025-05-08 09:06:32 +08:00
default: DEFAULT_CONFIG.templateDest,
});
}
if (!config.scriptDest || config.scriptDest === DEFAULT_CONFIG.scriptDest) {
pathPrompts.push({
2025-05-16 02:51:05 -07:00
type: 'input',
name: 'scriptDest',
message: 'Where should script files be placed?',
2025-05-08 09:06:32 +08:00
default: DEFAULT_CONFIG.scriptDest,
});
}
2025-05-02 18:27:45 +08:00
2025-05-08 09:06:32 +08:00
if (pathPrompts.length > 0) {
const pathChoices = await inquirer.prompt(pathPrompts);
if (pathChoices.templateDest) config.templateDest = pathChoices.templateDest;
if (pathChoices.scriptDest) config.scriptDest = pathChoices.scriptDest;
}
2025-05-02 18:27:45 +08:00
}
2025-05-08 09:06:32 +08:00
// Copies a component's template and script files to the user's project.
2025-05-01 20:53:03 +08:00
async function addComponent(componentName) {
2025-05-02 18:27:45 +08:00
console.log(`\nProcessing component: ${componentName}...`);
2025-05-01 20:53:03 +08:00
2025-05-08 09:06:32 +08:00
const cliScriptRunningDir = path.dirname(fileURLToPath(import.meta.url));
const assetsDir = path.resolve(cliScriptRunningDir, 'assets');
2025-05-02 18:27:45 +08:00
const templateExt = config.templateEngine === 'jinja' ? '.html.jinja' : '.njk';
2025-05-08 09:06:32 +08:00
const templateSource = path.join(assetsDir, config.templateEngine, `${componentName}${templateExt}`);
const scriptSource = path.join(assetsDir, 'js', `${componentName}.js`);
2025-05-01 20:53:03 +08:00
2025-05-02 18:27:45 +08:00
const templateDestPath = path.resolve(process.cwd(), config.templateDest, `${componentName}${templateExt}`);
const scriptDestPath = path.resolve(process.cwd(), config.scriptDest, `${componentName}.js`);
try {
const templateExists = await fs.pathExists(templateSource);
const scriptExists = await fs.pathExists(scriptSource);
2025-05-08 09:06:32 +08:00
if (!templateExists) {
console.error(` Error: Template file for component '${componentName}' not found in CLI assets. Searched: ${templateSource}`);
// Optionally, list available template engines or files for that component if helpful
2025-05-02 18:27:45 +08:00
}
2025-05-08 09:06:32 +08:00
if (!scriptExists) {
console.error(` Error: Script file for component '${componentName}' not found in CLI assets. Searched: ${scriptSource}`);
2025-05-16 02:51:05 -07:00
}
2025-05-08 09:06:32 +08:00
if (!templateExists || !scriptExists) return; // Skip this component if sources are missing
2025-05-02 18:27:45 +08:00
await fs.ensureDir(path.dirname(templateDestPath));
await fs.ensureDir(path.dirname(scriptDestPath));
await fs.copyFile(templateSource, templateDestPath);
console.log(` -> Copied template to: ${templateDestPath}`);
await fs.copyFile(scriptSource, scriptDestPath);
console.log(` -> Copied script to: ${scriptDestPath}`);
} catch (error) {
console.error(` Error processing component ${componentName}:`, error);
}
2025-05-01 20:53:03 +08:00
}
2025-05-08 09:06:32 +08:00
// Setup main CLI program
program
2025-05-08 09:36:08 +08:00
.name('basecoat-cli')
2025-05-08 09:06:32 +08:00
.description('Add Basecoat components to your project')
.version(packageVersion);
// Define the 'add' command
program
.command('add')
.description('Add one or more Basecoat components to your project')
.argument('[components...]', 'Names of components to add (e.g., dialog select)')
.action(async (componentsArg) => {
2025-05-08 09:36:08 +08:00
let componentsToAdd = []; // Initialize as an empty array
2025-05-08 09:06:32 +08:00
try {
2025-05-08 09:36:08 +08:00
if (!componentsArg || componentsArg.length === 0) {
2025-05-08 09:06:32 +08:00
const availableComponents = await getAvailableComponents();
if (availableComponents.length === 0) {
console.error('Error: No components available in the CLI. Build might be corrupted.');
return;
}
2025-05-08 09:36:08 +08:00
const allComponentsChoice = 'All components';
const choices = [allComponentsChoice, ...availableComponents];
2025-05-08 09:06:32 +08:00
const answers = await inquirer.prompt([
{
2025-05-08 09:36:08 +08:00
type: 'list', // Changed from checkbox to list
name: 'selectedComponent', // Changed name for clarity
2025-05-08 09:06:32 +08:00
message: 'Which component(s) would you like to add?',
2025-05-08 09:36:08 +08:00
choices: choices,
2025-05-08 09:06:32 +08:00
}
]);
2025-05-08 09:36:08 +08:00
if (answers.selectedComponent === allComponentsChoice) {
componentsToAdd = availableComponents; // Add all
} else {
componentsToAdd = [answers.selectedComponent]; // Add the single selected one
}
} else {
// If components were provided as arguments, use them directly
// (This part of the logic remains, ensuring componentsArg is an array)
componentsToAdd = Array.isArray(componentsArg) ? componentsArg : [componentsArg];
2025-05-08 09:06:32 +08:00
}
if (!componentsToAdd || componentsToAdd.length === 0) {
console.log('No components selected. Exiting.');
return;
}
2025-05-08 09:36:08 +08:00
2025-05-08 09:06:32 +08:00
await ensureConfiguration(); // Confirm/gather destination paths
for (const componentName of componentsToAdd) {
await addComponent(componentName);
}
console.log('\nComponent addition process finished.');
} catch (error) {
if (error.isTtyError || error.constructor?.name === 'ExitPromptError') { // Handle inquirer cancellation
console.log('\nOperation cancelled by user.');
} else {
console.error('\nAn unexpected error occurred during the add command:', error);
}
process.exit(1); // Exit with error code for unexpected errors
}
});
2025-05-01 20:53:03 +08:00
program.parse(process.argv);