@practical/create
Version:
Practical CLI
172 lines (155 loc) • 6.23 kB
JavaScript
import inquirer from "inquirer";
import {exec} from 'child_process'
import fs from 'fs'
const isUuid = data => {
return /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(data.trim());
}
const hasPackage = fs.existsSync("package.json");
const changePackageJsonName = name => {
const packageJSON = name + '/package.json';
fs.readFile(packageJSON, 'utf8', function (err,data) {
console.info("Setup package.json");
if (err) {
return console.log(err);
}
const result = data.replace('@practical/base', name );
fs.writeFile(packageJSON, result, 'utf8', function (err) {
if (err) return console.log(err);
console.info("Practical Template");
});
});
}
inquirer.prompt([
{
name: 'license',
type: 'input',
message: 'Welcome to Practical! 🎉🥳🙌 \n –> Please enter your @practical License!',
validate: async (input) => {
if (!input) {
return 'License is required';
} else if (!isUuid(input)) {
return 'License format is not correct';
}
return true;
}
},
{
name: 'domain',
type: 'input',
message: 'Add your Project Domain (format: example.com)',
validate: async (input) => {
if (!input) {
return 'Domain name is required';
}
return true;
}
},
{
name: 'presets',
type: "checkbox",
message: 'Choose your CMS Adapters!',
default: ['yml'],
choices: [
{name: 'YML - static data', value:'yml', short: 'YML'},
{name: 'Sanity Studio (sanity.io)', value:'sanity', short:'Sanity'},
{name: 'Storyblok Headless CMS (storyblok.com)', value:'storyblok', short:'Storyblok'},
{name: 'Wordpress Headless API', value:'wordpress', short:'Wordpress'},
],
validate: input => {
if (!input.length) { return 'Please choose an adapter!';}
return true;
},
},
{
name: 'projectName',
type: 'input',
message: 'Enter your Project name',
validate: async (input) => {
if (!input) {
return 'Project name is required';
}
/*** https://regexr.com/7qie6 */
const nameRegex = /^(@[a-z0-9-][a-z0-9-_]*\/)?[a-z0-9-][a-z0-9-_]*$/
if (!nameRegex.test(input)) {
return "The name must be lowercase and one word, and may contain hyphens and underscores."
}
return true;
}
}
]).then((answers) => {
/** create the .npmrc file */
const execPath = process.env.npm_execpath || '';
const isYarn = execPath.includes('yarn');
const isBun = execPath.includes('bun');
const configFile = isYarn ? '.yarnrc.yml' : (isBun ? 'bunfig.toml' : '.npmrc' )
const pkgRegistry = "practical-at.nodejs.pub"
const yarnFile = `npmScopes:
practical-at:
npmRegistryServer: https://${pkgRegistry}
npmAuthToken: ${answers.license.trim()}:${answers.domain}
`
const npmrcFile = `@practical-at:registry=https://${pkgRegistry}/
//${pkgRegistry}/:_authToken=${answers.license.trim()}:${answers.domain}
`
const bunfigToml = `[install.scopes]
"@practical-at" = { token = "${answers.license.trim()}:${answers.domain}", url = "https://${pkgRegistry}/" }`
const options = answers.presets.join(' ');
const projectName = answers.projectName.trim();
const base = answers.presets.length === 1 ? 'base ': '';
const command = `${(!isYarn && !isBun) ? 'node' : ''} ${execPath ? `"${execPath}"`:''} ${isBun ? '' :'create'} @practical-at${isBun ? '/create' :''}@latest folder ${projectName} ${base + options} -y`
const fileContents = isYarn ? yarnFile : ( isBun ? bunfigToml : npmrcFile);
try {
if (!fs.existsSync(projectName)) {
fs.mkdirSync(projectName);
}
} catch (err) {
console.error(err);
}
/* temp json file for windows */
!hasPackage && fs.writeFileSync('package.json', "{}");
fs.writeFile(configFile, fileContents, (err) => {
if(err) {return console.error(err);}
console.info("Get @practical Templates: \n", command);
/** Get practical Templates via Package manager */
exec(command, (error, stdout, stderr) => {
if (error) {
if (stderr.includes('E422') || stderr.includes('Unprocessable Entity')) {
console.error('❌ Unknown Licence. Please check if Project domain is correct and is paired with correct license.');
} else {
console.error('❌ Error:', error.message);
}
}
else {
/** clean up */
fs.copyFileSync(configFile, projectName+"/"+configFile)
!hasPackage && fs.rmSync("./package.json");
fs.rmSync(configFile);
console.info(stdout? stdout : stderr);
changePackageJsonName(projectName)
console.info("@practical setup -- done")
}
});
});
});
// Graceful Exit Handler
const gracefulExit = () => {
console.log('\n');
console.log(' ┌─────────────────────────────────────┐');
console.log(' │ Thanks for using Practical! │');
console.log(' │ See you next time! 👋 │');
console.log(' └─────────────────────────────────────┘\n');
process.exit(0);
};
// Handle CTRL+C gracefully
process.on('SIGINT', gracefulExit);
process.on('SIGTERM', gracefulExit);
// Catch inquirer exit errors and handle them gracefully
process.on('uncaughtException', (error) => {
if (error.name === 'ExitPromptError' || error.message.includes('User force closed')) {
gracefulExit();
} else {
console.error('An unexpected error occurred:', error.message);
process.exit(1);
}
});