src/util/scaffold/project.js
/**
* @module project.js
* @author Joe Groseclose <@benderTheCrime>
* @date 8/16/2015
*/
// System Modules
// Do not alias this as the commands mirror the global `confirm` and `prompt`
import {default as promptly} from 'promptly';
import fs from 'fs';
import util from 'util';
import chalk, {
bold,
green
} from 'chalk';
import $LogProvider from 'angie-log';
// Angie Modules
import {$StringUtil} from '../Util';
const p = process,
breen = (v) => bold(green(v));
/**
* @desc $$createProject is the function called when the CLI attempts to create
* a project from the command line. This scaffolds the main folder in the
* specified location or no folder and then folders for commonly used providers
* and the Angie config file (AngieFile.json).
*
* The CLI function to create a project will ask the user a series of questions.
* The result of these questions will be passed to the AngieFile.json:
* - By default the `development` option is set to true
* - By default, the `databases` object has one sqlite3 database, but this
* database is not instantiated
* - User will be prompted for caching of static assets
* - User will be prompted for default script file attachment
*
* This function will gracefully exit the process if successful and exit with
* errors if unsuccessful.
* @since 0.0.1
* @todo If any more CL arguments are required, you must abstract confirm/prompt
* as functions
* @param {object} args A list of arugments passed from the CLI parser
* @param {string} args.name The name of the project being created. This must
* consist of letters, dashes, & underscores
* @param {string} args.location [param=process.cwd()] The location in which the
* inner files of the project are created. CWD if no location is specified.
* @access private
*/
export default function $$createProject(args = {}) {
// Parse the passed arguments object
const name = args.name,
location = $StringUtil.removeTrailingLeadingSlashes(args.location);
let mkDir,
mkDirFiles,
mkSub;
// The process must exit if there is no passed name, or the name passed is
// in an incorrect format
if (!name) {
throw new $$ProjectCreationError('No project name specified');
} else if (
/[0-9$%\^&*\)\(\<\>\\\/\.\,\`\?\+\!\~#@=\}\{\|\]\[\'\"\:\;]+/i.test(name)
) {
// Being super specific about which characters are OK and which are not
throw new $$ProjectCreationError(
'Invalid project name: must be all letters, dashes, & underscores'
);
}
// Make sure that we're creating the project in the right spot
mkDir = location ? (location === '.' ? '' : location) : `${p.cwd()}/${name}`;
mkDirFiles = mkDir ? `${mkDir}/` : '';
mkSub = `${mkDirFiles}src`.replace(/\/{2}/g, '/');
try {
// We cannot create a dir if the argument is empty
if (mkDir) {
fs.mkdirSync(mkDir);
}
fs.mkdirSync(mkSub);
// Create provider folders
[
'constants',
'configs',
'services',
'factories',
'controllers',
'directives'
].forEach(function(v) {
fs.mkdirSync(`${mkSub}/${v}`);
});
// Create static folders
[
'test',
'static',
'templates'
].forEach(function(v) {
fs.mkdirSync(`${mkDirFiles}${v}`);
});
} catch(e) {
throw new $$ProjectCreationError(e);
} finally {
// This is where we create our AngieFile, we can pick certain values with
// which we can populate our config:
// cacheStaticAssets
let staticCache = false,
// Default JS to be loaded with all HTML files
defaultAppJavaScriptFilename;
// Wrap the prompts in a Promise
new Promise(function(resolve, reject) {
promptly.confirm(
`${breen('Do you want Angie to cache static assets?')} :`,
function(e, v) {
if (e) {
reject(e);
}
resolve(v);
}
);
}).then(function(v) {
staticCache = !!v;
}).then(function() {
// Ask what the default JS filename should be
return new Promise(function(resolve) {
return promptly.prompt(
`${breen(
'What would you like to call the "default" ' +
'loaded script file ' +
`(${bold(chalk.white('default is'))} ` +
`${chalk.cyan('application.js')})?`
)} :`,
{
default: 'application.js',
validator: function(v) {
if (v && v.indexOf('.js') === -1) {
throw new Error(
bold(chalk.red(
'Input must be a valid ".js" file.'
))
);
}
return v.replace(/\/|\\/g, '');
}
},
function(e, v) {
resolve(v);
}
);
});
}).then(function(v) {
defaultAppJavaScriptFilename = v;
}).then(function() {
// Read our AngieFile template and reproduce in the target directory
let template = fs.readFileSync(
`${__dirname}/../../templates/json/AngieFile.template.json`,
'utf8'
);
template = util.format(
template,
name,
name,
staticCache,
defaultAppJavaScriptFilename
);
fs.writeFileSync(
`${mkDirFiles}AngieFile.json`,
template,
'utf8'
);
fs.writeFileSync(
`${mkDirFiles}static/${defaultAppJavaScriptFilename}`,
''
);
$LogProvider.info('Project successfully created');
p.exit(0);
});
}
}
class $$ProjectCreationError extends Error {
constructor(e) {
$LogProvider.error(e);
super(e);
}
}