create-kalp-boilerplate
Version:
A Kalp CLI tool to generate a React boilerplate
124 lines (110 loc) • 3.66 kB
JavaScript
import { Command } from 'commander';
import { exec } from 'child_process';
import { fileURLToPath } from 'url';
import fs from 'fs';
import ora from 'ora';
import path from 'path';
import util from 'util';
const execPromise = util.promisify(exec); // Promisify exec for async/await
const program = new Command();
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
program
.name('create-kalp-boilerplate')
.version('1.0.0')
.description('A Kalp CLI tool to generate React boilerplate')
.argument('<app-name>', 'Name of your application')
.option('--auth', 'Add authentication functionality')
.option('--mfd-config', 'Add configuration for microfrontends')
.action(async (appName, options) => {
const spinner = ora(`Creating ${appName}...`).start();
const repoUrl = options.mfdConfig
? 'https://github.com/sachinp2e/react-microfrontend-boilerplate.git'
: 'https://github.com/sachinp2e/react-boilerplate-ts.git';
const targetPath = path.resolve(process.cwd(), appName);
try {
await cloneRepo(repoUrl, appName, spinner);
await postCloneSetup(targetPath, options);
spinner.succeed(`App "${appName}" created successfully!`);
console.log(`\nNavigate to your app:\n cd ${appName}`);
console.log(`\nInstall dependencies:\n npm install`);
} catch (error) {
handleError(spinner, error);
}
});
program.parse(process.argv);
/**
* Clones a GitHub repository into the specified directory.
*/
async function cloneRepo(repoUrl, appName, spinner) {
try {
await execPromise(`git clone ${repoUrl} ${appName}`);
} catch (error) {
throw new Error(
`Failed to clone the boilerplate: ${error.stderr || error.message}`
);
}
}
/**
* Handles post-clone setup like authentication and microfrontend config.
*/
async function postCloneSetup(targetPath, options) {
if (options.auth) {
addAuthFiles(targetPath);
}
}
/**
* Adds authentication files to the boilerplate.
*/
function addAuthFiles(targetPath) {
const spinner = ora('Adding authentication functionality...').start();
const authFilePath = path.resolve(
__dirname,
'../templates/auth/apiClient.ts'
);
const destPath = path.join(
targetPath,
'src/services/api',
'axios-instance.ts'
);
const authSlicePath = path.resolve(
__dirname,
'../templates/auth/redux/User/authSlice.ts'
);
const authSliceDestPath = path.resolve(
targetPath,
'src/store/slices',
'authSlice.ts'
);
const authConfigPath = path.resolve(__dirname, '../templates/auth/config.ts');
const authConfigDestPath = path.resolve(
targetPath,
'src/services/api',
'config.ts'
);
const utilsPath = path.resolve(__dirname, '../templates/auth/utils.ts');
const utilsDestPath = path.resolve(targetPath, 'src', 'helpers', 'utils.ts');
try {
fs.copyFileSync(authFilePath, destPath);
fs.copyFileSync(authSlicePath, authSliceDestPath);
fs.copyFileSync(authConfigPath, authConfigDestPath);
const destDir = path.dirname(utilsDestPath);
// Ensure the directory exists (create it if necessary)
if (!fs.existsSync(destDir)) {
fs.mkdirSync(destDir, { recursive: true }); // Recursive creates nested directories if needed
}
// Copy the file
fs.copyFileSync(utilsPath, utilsDestPath);
spinner.succeed('Authentication functionality added successfully!');
} catch (error) {
handleError(spinner, error);
}
}
/**
* Handles errors and logs them with the spinner.
*/
function handleError(spinner, error) {
spinner.fail(error.message);
process.exit(1);
}