UNPKG

onepoint-new-app

Version:

Easily generate a new fully-equiped React project, optionally with Express & MongoDB.

194 lines (159 loc) 5.93 kB
#!/usr/bin/env node // Avoid Node complaining about unhandled rejection errors. process.on("unhandledRejection", err => console.error(err)); // Node built-in modules. const path = require("path"); // External modules. const fs = require("fs-extra"); const chalk = require("chalk"); const cla = require("command-line-args"); // File creators. const dotEnv = require("./file-creators/dotEnv"); const packageJson = require("./file-creators/packageJson"); const webpackConfig = require("./file-creators/webpackConfig"); const publicIndex = require("./file-creators/publicIndex"); const portValidator = require("./modules/portValidator"); // Custom modules. const run = require("./modules/run"); const safeToCreateDir = require("./modules/safeToCreateDir"); const adjustPkgJson = require("./modules/adjustPkgJson"); const uuid = require("uuid"); // Other. const cwd = fs.realpathSync(process.cwd()); // http://bit.ly/2YYe9R8 - because symlinks. const dir = text => path.resolve(__dirname, text); // Option definitions. const optionDefinitions = [ // Main argument. // Will be validated in the `parseArgs` fxn by the `validate-npm-package-name` pkg. { name: "appName", type: String, defaultOption: true }, { name: "port", alias: "p", type: val => portValidator(val, 4000), defaultValue: 4000 } ]; // Let's go! Push the first dominoe. letsGo(); async function letsGo() { let options = cla(optionDefinitions, { partial: true }); console.log("@debug-options", options); options = { ...options, appDir: `${cwd}/${options.appName}`, appId: uuid.v1() }; createProjectDirectory(options); createFiles(options); installDependencies(options); } // STEP 3 function createProjectDirectory(options) { const { appName, appDir } = options; const greenDir = chalk.green(`${cwd}/`); const boldName = chalk.green.bold(appName); safeToCreateDir(options) || process.exit(); console.log(`\nCreating a new app in ${greenDir}${boldName}.`); // Create the project directory if it doesn't already exist. fs.mkdirpSync(appDir); } // STEP 4 function createFiles(options) { const { appDir } = options; // `.env` fs.writeFileSync(`${appDir}/.env`, dotEnv(options), "utf8"); // `.gitignore` fs.copySync(dir("files/gitignore.txt"), `${appDir}/.gitignore`); // `.babelrc` fs.copySync(dir("files/babelrc.txt"), `${appDir}/.babelrc`); // `package.json` fs.writeFileSync(`${appDir}/package.json`, packageJson(options), "utf8"); // `webpack.config.js` fs.writeFileSync(`${appDir}/webpack.config.js`, webpackConfig({}), "utf8"); const excludedFiles = [".gitkeep"].filter(Boolean); const filter2 = { filter: file => excludedFiles.every(f => !file.includes(f)) }; // `src` directory tree. fs.copySync(dir("./files/src"), `${appDir}/src`, filter2); fs.writeFileSync( `${appDir}/src/env.js`, `export default ${JSON.stringify(options)};`, "utf8" ); // `public` directory tree. fs.mkdirpSync(`${appDir}/public`); fs.writeFileSync(`${appDir}/public/index.html`, publicIndex(options), "utf8"); } // STEP 5 async function installDependencies(options) { const { appName, appDir, server, offline, mongo, force, noInstall } = options; const forceOffline = offline ? " --offline" : ""; // http://bit.ly/2Z2Ht9c const cache = offline ? " cache" : ""; // Change into the projects directory. process.chdir(`${cwd}/${appName}`); // Install the dependencies. if (!noInstall) { offline && console.log(`\nIt looks like you're offline or have a bad connection.`); console.log(`Installing project dependencies via npm${cache}...\n`); try { run(`npm i${forceOffline}`); } catch (e) { console.log( `\n${chalk.yellow("An error occurred during the npm installation.")}` ); // Cleanup what was created *only* if we didn't force install. if (!force) { process.chdir(cwd); run(`rm -rf ${cwd}/${appName}`); console.log("Created directories and files have been removed."); process.exit(1); } else { console.log("Refusing to remove created directories and files"); console.log("since they were created in a pre-existing location:"); console.log(` ${chalk.cyan(cwd)}`); } } } // Adjust the package.json dependencies to show their installed version. // E.x. - "react": "^16" => "react": "^16.6.1" !noInstall && adjustPkgJson(appDir); // Initialize git. try { run("git init", true); // Don't display stdout. console.log("Initialized a git repository.\n"); } catch (e) { console.log( `Tried to initialize a new ${chalk.bold("git")} repository but couldn't.` ); console.log(`Do you have have ${chalk.bold("git")} installed?\n`); } noInstall && console.log( "No dependecies intalled. `package.json` will not reflect specific versions." ); // Display the final message. const cyanDir = chalk.cyan(appDir); const boldName = chalk.bold(appName); const serverMsg = server ? "and Express servers" : "server"; console.log(`\nSuccess! Created ${boldName} at ${cyanDir}.`); console.log(`Inside that directory you can run several commands:\n`); console.log(` ${chalk.cyan("npm start")}`); console.log(` Starts the development ${serverMsg}.\n`); console.log(` ${chalk.cyan("npm run build")}`); console.log(` Bundles the app into static files for production.\n`); if (server) { console.log(` ${chalk.cyan("npm run local")}`); console.log( ` Starts only the Express server (no development server).\n` ); } if (mongo) { console.log("To see tips & tasks related to MongoDB in production:"); console.log(` ${chalk.cyan("cna --mongoHelp")}\n`); } console.log(`\nGet started by typing:\n`); console.log(` ${chalk.cyan("cd")} ${appName}`); console.log(` ${chalk.cyan("npm start")}\n`); }