@prabink/react-node-cli
Version:
React Node Application Generator & Helpers To Serve React Build, Push to github & Export industry level react project structure
474 lines (453 loc) • 14.6 kB
JavaScript
const program = require("commander");
const shell = require("shelljs");
const path = require("path");
const fs = require("fs");
const { prompt } = require("inquirer");
const packageInfo = require("./package.json");
var settings = require("./settings.json");
const utils = require("./utils");
const CWD = process.cwd();
/**
* Parent Directory in client side to hold all reference code
*/
const referenceCodeDirectoryName = ".react-node-cli";
const settingsJsonFileName = "settings.json";
const colorReference = {
Reset: "\x1b[0m",
Bright: "\x1b[1m",
Dim: "\x1b[2m",
Underscore: "\x1b[4m",
Blink: "\x1b[5m",
Reverse: "\x1b[7m",
Hidden: "\x1b[8m",
FgBlack: "\x1b[30m",
FgRed: "\x1b[31m",
FgGreen: "\x1b[32m",
FgYellow: "\x1b[33m",
FgBlue: "\x1b[34m",
FgMagenta: "\x1b[35m",
FgCyan: "\x1b[36m",
FgWhite: "\x1b[37m",
BgBlack: "\x1b[40m",
BgRed: "\x1b[41m",
BgGreen: "\x1b[42m",
BgYellow: "\x1b[43m",
BgBlue: "\x1b[44m",
BgMagenta: "\x1b[45m",
BgCyan: "\x1b[46m",
BgWhite: "\x1b[47m",
};
program
.version(packageInfo.version)
.description("React Node Full Stack Application Generator & Helper");
// Generate Command
program
.command("generate")
.alias("g")
.description("Generate node application to serve react build")
.action(async () => {
/**
* This directory will have core node js code to server react build
*/
const nodeCodeDirectoryName = `${referenceCodeDirectoryName}/node`;
const clentSettingJsonFile = path.join(
process.cwd(),
referenceCodeDirectoryName,
settingsJsonFileName
);
try {
const { isCorrectDirectory } = await prompt([
{
type: "confirm",
name: "isCorrectDirectory",
message: `Make sure you are inside the react project root directory`,
},
]);
if (!isCorrectDirectory) {
console.log(
colorReference.FgRed,
"Sorry: You must be in root directoy of react project",
colorReference.Reset
);
process.exit();
}
// making release directory and copying core node code inside it to serve react build
console.log(
colorReference.FgCyan,
"Your current working directory: ",
CWD,
colorReference.Reset
);
//Storing reference code initially
if (
!fs.existsSync(nodeCodeDirectoryName) ||
!fs.existsSync(referenceCodeDirectoryName)
) {
shell.rm("-rf", referenceCodeDirectoryName);
shell.mkdir("-p", nodeCodeDirectoryName);
shell.cp(
"-R",
`${__dirname}/node/*`,
`${__dirname}/node/.*`,
nodeCodeDirectoryName
);
shell.cp(
"-R",
`${__dirname}/settings.json`,
referenceCodeDirectoryName
);
await prompt([
{
name: "continue",
message: `Make sure to change your node code if needed inside ${nodeCodeDirectoryName} folder before procedding. eg PORT , CORS etc: Press enter to continue`,
},
]);
}
//Storing seetings file
if (
fs.existsSync(referenceCodeDirectoryName) &&
!fs.existsSync(clentSettingJsonFile)
) {
shell.cp(
"-R",
`${__dirname}/settings.json`,
referenceCodeDirectoryName
);
}
const clientSettings = await utils
.readJsonFile(clentSettingJsonFile)
.catch(() => {});
if (clientSettings) settings = clientSettings;
var releaseFolderName = settings.generate.releaseFolderName;
var reactBuildFolderName = settings.generate.reactBuildFolderName;
const {
runBuildCommand,
userInputReleaseOutputDirectory,
userInputReactBuildDirectory,
deployToGit,
} = await prompt([
{
type: "confirm",
name: "runBuildCommand",
default: true,
message: `Do you want to run react build command ?`,
},
{
type: "input",
name: "userInputReleaseOutputDirectory",
message: `Enter your prefer react node release output directory: [default:${releaseFolderName}]: `,
},
{
type: "input",
name: "userInputReactBuildDirectory",
message: `Enter your prefer react build directory to serve: [default:${reactBuildFolderName}]: `,
},
{
type: "confirm",
name: "deployToGit",
default: true,
message: `Do you want to deploy your release output directory to github branch using git ?`,
},
]);
//Copy all fresh/modified code from code node folder to release folder
if (
userInputReleaseOutputDirectory &&
userInputReleaseOutputDirectory !== releaseFolderName
) {
releaseFolderName = userInputReleaseOutputDirectory;
//Store in user setting too
settings.generate.releaseFolderName = releaseFolderName;
}
if (
userInputReactBuildDirectory &&
userInputReactBuildDirectory !== reactBuildFolderName
) {
reactBuildFolderName = userInputReactBuildDirectory;
//Store in user setting too
settings.generate.reactBuildFolderName = reactBuildFolderName;
}
const releaseDir = path.join(CWD, releaseFolderName);
const buildDirPath = path.join(CWD, reactBuildFolderName);
if (runBuildCommand) {
console.log(
colorReference.FgMagenta,
"....Making React Build, This will take some time....",
colorReference.Reset
);
// making react build
if (shell.exec("npm run build").code !== 0) {
console.log(
colorReference.FgRed,
"Error: React build failed",
colorReference.Reset
);
shell.exit(1);
}
}
if (!fs.existsSync(buildDirPath)) {
console.log(
colorReference.FgRed,
`Error: Given folder does not exists [${buildDirPath}]`,
colorReference.Reset
);
shell.exit(1);
}
// copying build to release directory
shell.rm("-rf", releaseDir);
shell.mkdir(releaseDir);
shell.cp(
"-R",
`${nodeCodeDirectoryName}/*`,
`${nodeCodeDirectoryName}/.*`,
`${buildDirPath}/.`,
releaseDir
);
//[gitignire-operation] Add release out folder to gitignore which is auto generaterd
const gitIgnireFilePath = `${CWD}/.gitignore`;
const stringToAdd = `\n\n#Added By React-Node-Cli\n${releaseFolderName}\n`;
if (fs.existsSync(gitIgnireFilePath)) {
const data = fs.readFileSync(gitIgnireFilePath);
// Only add if it is not added previously
if (data.indexOf(stringToAdd) < 0) {
fs.appendFileSync(gitIgnireFilePath, stringToAdd);
}
}
//Only deploy to git if user wants to deploy
if (deployToGit) {
await workingOnGit(releaseDir);
}
// Save Settings into a setting file
await utils
.writeJsonFile(settings, clentSettingJsonFile)
.catch((err) => {});
console.info(
colorReference.FgGreen,
`Completed: To run locally: $cd ${releaseFolderName} && npm start`,
colorReference.Reset
);
process.exit();
} catch (error) {
console.log(
colorReference.FgRed,
"Error: error executing command ",
error,
colorReference.Reset
);
}
});
// Generate Starter Structure Command
program
.command("export")
.alias("exp")
.description("Export React Starter Structure")
.action(async () => {
const exportTemplates = [
{
name: "Export react structure only",
outFolderName: "exported_react_structure_only",
path: "/templates/react_structure_only",
},
{
name: "Export react structure with materialUI & Mobx",
outFolderName: "exported_react_mui_mobx",
path: "/templates/react_mui_mobx",
},
{
name: "Export react structure with materialUI & Redux",
outFolderName: "exported_react_mui_redux",
path: "/templates/react_mui_redux",
},
];
try {
console.log(
colorReference.FgCyan,
"Your current working directory: ",
CWD,
colorReference.Reset
);
const DEFAULT_EXTRACT_FOLDER_NAME = "/exported_react_structure";
var folderToExport = DEFAULT_EXTRACT_FOLDER_NAME;
const { exportType } = await prompt([
{
type: "list",
name: "exportType",
message: "What you want to export? (Use arrow keys)",
choices: exportTemplates.map((i) => i.name),
},
]);
const templateType = exportTemplates.find((i) => i.name == exportType);
folderToExport = templateType.outFolderName;
const { extractFolderName } = await prompt([
{
type: "input",
name: "extractFolderName",
message: `Enter your prefer folder name to export: ${colorReference.FgGreen}[default:${folderToExport}]${colorReference.Reset}, If it is your fresh project, simply enter directory as ${colorReference.FgGreen}/${colorReference.Reset}: `,
},
]);
if (extractFolderName) folderToExport = extractFolderName;
const { aggreed } = await prompt([
{
type: "confirm",
name: "aggreed",
default: false,
message: `This will replace all code that you have in ${colorReference.FgGreen}[${folderToExport}]${colorReference.Reset} directory and it is can not be Undone. Do you aggreed of this ?`,
},
]);
if (!aggreed) {
console.log(
colorReference.BgRed,
"Exited: Reason not aggreed",
colorReference.Reset
);
process.exit();
}
//Export Path: where to export
const exportPath = path.join(CWD, folderToExport);
//Template Path: from where to export
const templatePath = path.join(__dirname, templateType.path);
shell.mkdir("-p", exportPath);
shell.cp("-R", `${templatePath}/*`, `${templatePath}/.*`, exportPath);
const dirs = fs.readdirSync(templatePath);
console.log(
colorReference.FgGreen,
`Completed:: Exported Directories: ${JSON.stringify(
dirs
)} inside ${exportPath}`,
colorReference.Reset
);
console.log(
colorReference.BgRed,
`Please make sure to install all required packages to work with this structure by running command:${colorReference.FgGreen} $ npm install ${colorReference.Reset}`,
colorReference.Reset
);
process.exit();
} catch (error) {
console.log(
colorReference.FgRed,
"Error: error executing command ",
error,
colorReference.Reset
);
process.exit(1);
}
});
// Deploy any folder to github branch
program
.command("git_deploy")
.alias("gdeploy")
.description("Deploy any folder to github branch")
.action(async () => {
try {
const { userInputTargetFolderName } = await prompt([
{
type: "input",
name: "userInputTargetFolderName",
message: `Enter your prefer folderName to deploy in github: `,
},
]);
const userInputTargetFolderPath = path.join(
CWD,
userInputTargetFolderName
);
if (
!userInputTargetFolderName ||
!fs.existsSync(userInputTargetFolderPath)
) {
console.log(
colorReference.FgRed,
`Error: Given folder does not exists or invalid folder, please provide valid foldername`,
colorReference.Reset
);
shell.exit(1);
}
await workingOnGit(userInputTargetFolderPath);
console.info(colorReference.FgGreen, `Completed`, colorReference.Reset);
process.exit();
} catch (error) {
console.log(
colorReference.FgRed,
"Error: error executing command ",
error,
colorReference.Reset
);
}
});
program.parse(process.argv);
/**
* This will deploy the given folder to github branch
* @param {string} folderToDeploy
* @returns
*/
async function workingOnGit(folderToDeploy) {
console.log(
colorReference.FgMagenta,
"....Working on git....",
colorReference.Reset
);
// checking git and working on git
if (!shell.which("git")) {
console.log(
colorReference.FgRed,
"Sorry, this script requires git",
colorReference.Reset
);
shell.exit(1);
}
// Findout git origin
const CURRENT_GIT_ORIGIN_RESULT = shell.exec(
"git config --get remote.origin.url"
);
if (CURRENT_GIT_ORIGIN_RESULT.code !== 0) {
console.log(
colorReference.FgRed,
"Error: Please make sure you have git initialized and added the remote origin, Try again later..",
colorReference.Reset
);
shell.exit(1);
}
const CURRENT_GIT_ORIGIN = CURRENT_GIT_ORIGIN_RESULT.stdout.replace(
/(\n|\r)/g,
""
);
console.log(
colorReference.FgCyan,
"Your current git origin: ",
CURRENT_GIT_ORIGIN,
colorReference.Reset
);
// switching to release directory to work on git operation
shell.cd(folderToDeploy);
// initialized git
shell.exec("git init");
// Set remote origin
shell.exec(`git remote add origin "${CURRENT_GIT_ORIGIN}"`);
// Get prefer branch name and commit message
const DEFAULT_BRANCH_NAME = "frontend-release";
const DEFAULT_COMMIT_MESSAGE = "release with build";
const { branchName, commitMessage } = await prompt([
{
type: "input",
name: "branchName",
message: `Enter your prefer branch name: [default:${DEFAULT_BRANCH_NAME}]: `,
},
{
type: "input",
name: "commitMessage",
message: `Enter your commit message: [default:${DEFAULT_COMMIT_MESSAGE}]: `,
},
]);
const BRANCH_NAME = branchName || DEFAULT_BRANCH_NAME;
const COMMIT_MESSAGE = commitMessage || DEFAULT_COMMIT_MESSAGE;
// Do basic git operation to push to prefer branch
shell.exec(`git checkout -b "${BRANCH_NAME}"`);
shell.exec(`git add .`);
shell.exec(`git commit -m "${COMMIT_MESSAGE}"`);
shell.exec(`git push -f origin "${BRANCH_NAME}"`);
console.info(
colorReference.FgGreen,
`${folderToDeploy} is successfully Deployed to git 😀 ,please check on your ${BRANCH_NAME} branch in github(${CURRENT_GIT_ORIGIN})`,
colorReference.Reset
);
return { CURRENT_GIT_ORIGIN, BRANCH_NAME, COMMIT_MESSAGE };
}