Working on new packages structure

This commit is contained in:
Ronan Berder
2025-05-08 09:06:32 +08:00
parent 3780eb80ee
commit 39a7d58e50
7 changed files with 309 additions and 121 deletions
+104 -103
View File
@@ -1,24 +1,23 @@
#!/usr/bin/env node
console.log('CLI starting...');
import { Command } from 'commander';
import inquirer from 'inquirer';
import fs from 'fs-extra';
import path from 'path';
console.log('Imports loaded');
import { fileURLToPath } from 'url';
const program = new Command();
// Attempt to read version from the CLI's own package.json
let packageVersion = 'unknown';
try {
const cliScriptDir = path.dirname(new URL(import.meta.url).pathname);
const packageJsonPath = path.resolve(cliScriptDir, 'package.json');
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');
const packageJsonContent = fs.readFileSync(packageJsonPath, 'utf8');
packageVersion = JSON.parse(packageJsonContent).version;
} catch (error) {
console.warn('Could not read package version:', error.message);
console.warn('Could not read CLI package version:', error.message);
}
const DEFAULT_CONFIG = {
@@ -26,28 +25,28 @@ const DEFAULT_CONFIG = {
templateDest: './components/basecoat',
scriptDest: './static/js/basecoat'
};
let config = { ...DEFAULT_CONFIG };
let config = { ...DEFAULT_CONFIG }; // User-specific config, potentially updated by prompts
// Discovers available components from the CLI's bundled assets.
async function getAvailableComponents() {
try {
const packageRoot = path.resolve(path.dirname(new URL(import.meta.url).pathname), '..', '..');
console.log('Package root:', packageRoot);
const jsSourceDir = path.join(packageRoot, 'src', 'js');
console.log('JS source dir:', jsSourceDir);
const cliScriptRunningDir = path.dirname(fileURLToPath(import.meta.url));
const assetsDir = path.resolve(cliScriptRunningDir, 'assets');
const jsSourceDir = path.join(assetsDir, 'js');
const jsFiles = await fs.readdir(jsSourceDir);
console.log('Found JS files:', jsFiles);
return jsFiles
.filter(file => file.endsWith('.js'))
.map(file => path.basename(file, '.js'));
} catch (error) {
console.error("Error reading component source directories:", error);
console.error("Error reading component source directories from CLI assets:", error);
return [];
}
}
// Ensures necessary configuration (template engine, destination paths) is set, prompting the user if needed.
async function ensureConfiguration() {
console.log('\nChecking configuration...');
if (!config.templateEngine) {
const engineChoice = await inquirer.prompt([
{
@@ -60,113 +59,61 @@ async function ensureConfiguration() {
config.templateEngine = engineChoice.engine;
}
const pathChoices = await inquirer.prompt([
{
type: 'input',
name: 'templateDest',
message: 'Where should template files be placed?',
default: './',
},
{
type: 'input',
name: 'scriptDest',
message: 'Where should script files be placed?',
default: './',
}
]);
config.templateDest = pathChoices.templateDest;
config.scriptDest = pathChoices.scriptDest;
const pathPrompts = [];
if (!config.templateDest || config.templateDest === DEFAULT_CONFIG.templateDest) {
pathPrompts.push({
type: 'input',
name: 'templateDest',
message: 'Where should template files be placed?',
default: DEFAULT_CONFIG.templateDest,
});
}
if (!config.scriptDest || config.scriptDest === DEFAULT_CONFIG.scriptDest) {
pathPrompts.push({
type: 'input',
name: 'scriptDest',
message: 'Where should script files be placed?',
default: DEFAULT_CONFIG.scriptDest,
});
}
console.log(`Configuration set: Engine=${config.templateEngine}, Templates=${config.templateDest}, Scripts=${config.scriptDest}`);
if (pathPrompts.length > 0) {
const pathChoices = await inquirer.prompt(pathPrompts);
if (pathChoices.templateDest) config.templateDest = pathChoices.templateDest;
if (pathChoices.scriptDest) config.scriptDest = pathChoices.scriptDest;
}
}
console.log('Basecoat CLI');
program
.name('basecoat')
.description('Add Basecoat components to your project')
.version(packageVersion)
.command('add')
.description('Add one or more components to your project')
.argument('[components...]', 'Names of components to add')
.action(async (componentsArg) => {
let componentsToAdd = componentsArg;
try {
if (!componentsToAdd || componentsToAdd.length === 0) {
const availableComponents = await getAvailableComponents();
if (availableComponents.length === 0) {
console.error('Error: Could not find any components.');
return;
}
const answers = await inquirer.prompt([
{
type: 'checkbox',
name: 'selectedComponents',
message: 'Which component(s) would you like to add?',
choices: availableComponents,
validate: (input) => input.length > 0 ? true : 'Please select at least one component.'
}
]);
componentsToAdd = answers.selectedComponents;
}
if (!componentsToAdd || componentsToAdd.length === 0) {
console.log('No components selected. Exiting.');
return;
}
console.log(`Attempting to add: ${componentsToAdd.join(', ')}`);
await ensureConfiguration();
for (const componentName of componentsToAdd) {
await addComponent(componentName);
}
console.log('\nComponent addition process finished.');
} catch (error) {
if (error.isTtyError || error.constructor.name === 'ExitPromptError') {
console.log('\nOperation cancelled.');
} else {
console.error('\nAn unexpected error occurred:', error);
}
process.exit(1);
}
});
// Copies a component's template and script files to the user's project.
async function addComponent(componentName) {
console.log(`\nProcessing component: ${componentName}...`);
// 1. Determine source paths (relative to package root)
const cliScriptDir = path.dirname(new URL(import.meta.url).pathname);
const packageRoot = path.resolve(cliScriptDir, '..', '..');
const cliScriptRunningDir = path.dirname(fileURLToPath(import.meta.url));
const assetsDir = path.resolve(cliScriptRunningDir, 'assets');
const templateExt = config.templateEngine === 'jinja' ? '.html.jinja' : '.njk';
const templateSource = path.join(packageRoot, 'src', config.templateEngine, `${componentName}${templateExt}`);
const scriptSource = path.join(packageRoot, 'src', 'js', `${componentName}.js`);
const templateSource = path.join(assetsDir, config.templateEngine, `${componentName}${templateExt}`);
const scriptSource = path.join(assetsDir, 'js', `${componentName}.js`);
// 2. Determine destination paths (relative to user's current working directory)
const templateDestPath = path.resolve(process.cwd(), config.templateDest, `${componentName}${templateExt}`);
const scriptDestPath = path.resolve(process.cwd(), config.scriptDest, `${componentName}.js`);
try {
// 3. Check if source files exist
const templateExists = await fs.pathExists(templateSource);
const scriptExists = await fs.pathExists(scriptSource);
if (!templateExists || !scriptExists) {
console.error(` Error: Source files for component '${componentName}' not found. Searched:`);
if (!templateExists) console.error(` - ${templateSource}`);
if (!scriptExists) console.error(` - ${scriptSource}`);
return; // Skip this component
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
}
if (!scriptExists) {
console.error(` Error: Script file for component '${componentName}' not found in CLI assets. Searched: ${scriptSource}`);
}
if (!templateExists || !scriptExists) return; // Skip this component if sources are missing
// 4. Ensure destination directories exist
await fs.ensureDir(path.dirname(templateDestPath));
await fs.ensureDir(path.dirname(scriptDestPath));
// 5. Copy files
await fs.copyFile(templateSource, templateDestPath);
console.log(` -> Copied template to: ${templateDestPath}`);
await fs.copyFile(scriptSource, scriptDestPath);
@@ -177,4 +124,58 @@ async function addComponent(componentName) {
}
}
// Setup main CLI program
program
.name('basecoat')
.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) => {
let componentsToAdd = componentsArg;
try {
if (!componentsToAdd || componentsToAdd.length === 0) {
const availableComponents = await getAvailableComponents();
if (availableComponents.length === 0) {
console.error('Error: No components available in the CLI. Build might be corrupted.');
return;
}
const answers = await inquirer.prompt([
{
type: 'checkbox',
name: 'selectedComponents',
message: 'Which component(s) would you like to add?',
choices: availableComponents,
validate: (input) => input.length > 0 ? true : 'Please select at least one component.'
}
]);
componentsToAdd = answers.selectedComponents;
}
if (!componentsToAdd || componentsToAdd.length === 0) {
console.log('No components selected. Exiting.');
return;
}
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
}
});
program.parse(process.argv);
+37 -6
View File
@@ -1,14 +1,45 @@
{
"name": "@hunvreus/basecoat-cli",
"name": "basecoat-cli",
"version": "0.1.0",
"description": "CLI for Basecoat UI components",
"description": "Add Basecoat components to your project",
"author": "hunvreus",
"license": "MIT",
"type": "module",
"bin": {
"basecoat": "./index.js"
"basecoat": "./dist/index.js"
},
"files": [
"dist/"
],
"dependencies": {
"commander": "^13.1.0",
"inquirer": "^12.6.0",
"fs-extra": "^11.3.0"
}
"fs-extra": "^11.3.0",
"inquirer": "^12.6.0"
},
"keywords": [
"cli",
"components",
"component library",
"component system",
"ui",
"ui kit",
"shadcn",
"shadcn/ui",
"tailwind",
"tailwindcss",
"css",
"html",
"jinja",
"nunjucks",
"alpinejs"
],
"repository": {
"type": "git",
"url": "git+https://github.com/hunvreus/basecoat.git",
"directory": "packages/cli"
},
"bugs": {
"url": "https://github.com/hunvreus/basecoat/issues"
},
"homepage": "https://basecoatui.com/installation#install-cli"
}