@craftled/comp
Version:
A CLI tool to initialize a component app
192 lines (166 loc) ⢠5.12 kB
JavaScript
const { execSync } = require('child_process');
const readline = require('readline');
const path = require('path');
const fs = require('fs');
// Function to check if a command exists
function commandExists(command) {
try {
execSync(`command -v ${command}`, { stdio: 'ignore' });
return true;
} catch (error) {
return false;
}
}
// Function to prompt the user for input
function promptUser(query) {
const rl = readline.createInterface({
input: process.stdin,
output: process.stdout
});
return new Promise((resolve) => {
rl.question(query, (answer) => {
rl.close();
resolve(answer);
});
});
}
// Determine the best runner to use, prioritizing: bunx > pnpm dlx > npx
let runner;
if (commandExists('bunx')) {
runner = 'bunx';
} else if (commandExists('pnpm')) {
runner = 'pnpm dlx';
} else {
runner = 'npx';
}
async function initCommand() {
try {
// Prompt the user for the project name
const projectName = await promptUser(
'Enter project \x1b[36mfolder name\x1b[0m: '
);
if (!projectName || projectName.trim() === '') {
console.error(
'Error: Project folder name cannot be empty.'
);
process.exit(1);
}
// Define the commands to run
const createCommand = `${runner} create-next-app@latest ${projectName} --ts --tailwind --eslint --app --no-src-dir --no-import-alias --use-bun --turbopack --empty`;
const initCommand = `${runner} shadcn init`;
const addCommand = `${runner} shadcn add https://comp-registry.vercel.app/r/new.json --overwrite`;
// Step 1: Create the Next.js project
console.log(`Running: \x1b[36m${createCommand}\x1b[0m`);
execSync(createCommand, { stdio: 'inherit' });
// Step 2: Navigate into the project directory
console.log(
`Navigating into project directory: \x1b[36m${projectName}\x1b[0m`
);
process.chdir(projectName);
// Step 3: Initialize shadcn/ui
console.log(`Running: \x1b[36m${initCommand}\x1b[0m`);
execSync(initCommand, { stdio: 'inherit' });
// Step 4: Add components
console.log(`Running: \x1b[36m${addCommand}\x1b[0m`);
execSync(addCommand, { stdio: 'inherit' });
// Step 5: Inform the user how to proceed
console.log(
`\nš Project \x1b[36m${projectName}\x1b[0m set up successfully!`
);
console.log(
`To start working on your project, navigate into the directory:`
);
console.log(` \x1b[36mcd ${projectName}\x1b[0m`);
console.log(
`Then, install dependencies and start the development server:`
);
console.log(` \x1b[36mbun i\x1b[0m`);
console.log(` \x1b[36mbun run dev\x1b[0m`);
} catch (error) {
console.error(
`Error executing command with ${runner}:`,
error.message
);
process.exit(1);
}
}
async function addCommand(componentName) {
try {
if (!componentName) {
console.error('Error: Component name is required.');
console.log('Usage: comp add <component-name>');
process.exit(1);
}
// Check if we're in a Next.js project
if (!fs.existsSync('./package.json')) {
console.error(
'Error: Not in a project directory. Run this command from your project root.'
);
process.exit(1);
}
const packageJson = JSON.parse(
fs.readFileSync('./package.json', 'utf8')
);
if (
!packageJson.dependencies ||
!packageJson.dependencies.next
) {
console.warn(
"Warning: This doesn't appear to be a Next.js project."
);
}
// Add the component
const addComponentCommand = `${runner} shadcn add https://comp-registry.vercel.app/r/${componentName}.json --overwrite`;
console.log(
`Adding component: \x1b[36m${componentName}\x1b[0m`
);
console.log(
`Running: \x1b[36m${addComponentCommand}\x1b[0m`
);
execSync(addComponentCommand, { stdio: 'inherit' });
console.log(
`\nā
Component \x1b[36m${componentName}\x1b[0m added successfully!`
);
} catch (error) {
console.error(`Error adding component:`, error.message);
process.exit(1);
}
}
async function listCommand() {
// list manually all possible components
console.log('Available components:');
console.log(
' - \x1b[36mpayload\x1b[0m - Add Payload CMS to your project'
);
}
// Main function to parse arguments and execute commands
async function main() {
const args = process.argv.slice(2);
const command = args[0];
switch (command) {
case 'init':
await initCommand();
break;
case 'add':
await addCommand(args[1]);
break;
case 'list':
await listCommand();
break;
default:
console.log('Usage:');
console.log(
' @craftled/comp init - Initialize a new app'
);
console.log(
' @craftled/comp add <component-name> - Add a component to your project'
);
console.log(
' @craftled/comp list - List available components'
);
process.exit(0);
}
}
// Run the main function
main();