UNPKG

emerald-templates

Version:

Intelligent Template Generation & Project Management

180 lines (169 loc) 6.48 kB
const { join, basename, dirname } = require('path') const chalk = require('chalk') const { promisify } = require('util') const exec = promisify(require('child_process').exec) const rimraf = require('delete').promise const { mkdir, cleanup, track } = require('temp') const { pathExists, ensureDir } = require('fs-extra') const getConfiguration = require('../functions/getConfiguration') const directoryExists = require('directory-exists') const resolvePath = require('../functions/resolvePath') const saveGlobalConfig = require('../functions/saveGlobalConfig') const processOutputFolder = require('../functions/processOutputFolder') const copyTemplate = require('../functions/copyTemplate') const findTemplateFolder = require('../functions/findTemplateFolder') const askQuestion = require('../functions/askQuestion') const askYesOrNo = require('../functions/askYesOrNo') //const displayList = require('../functions/displayList') const getEmeraldConfig = require('../functions/getEmeraldConfig') const getProjectStore = require('../functions/getProjectStore') const gitPull = require('../functions/gitPull') const pathSpacingRegex = /[\s-]+/g const remoteRegex = /^(git|http(?:s)?):\/\// const validPreexistingOptions = ['overwrite', 'erase', 'stop', 'available'] async function createProject(templateFolder, outputFolder, options) { const config = (process.env.EMERALD_CONFIG = getConfiguration()) let { launchCommand } = config let tempDir = null if (remoteRegex.test(templateFolder)) { // Remote Repo track(true) const url = new URL(templateFolder) console.log('Cloning the repository to use as a template') tempDir = await mkdir('remote-template') await exec(`git clone "${url}" target`, { cwd: tempDir }) templateFolder = join(tempDir, 'target') } const templateConfig = await findTemplateFolder(templateFolder) if (tempDir === null) { // Update the template console.log('Checking for updates') await gitPull(templateConfig.path) } if (templateConfig === null) throw new Error('Could not find the template: ' + templateFolder) process.env.TEMPLATE_FOLDER = templateConfig.path if (Array.isArray(outputFolder)) outputFolder = outputFolder[0] if (typeof outputFolder != 'string') throw new Error('You must specify the output folder') let outputFolderPath = resolvePath(outputFolder, process.cwd()) // strip the spaces outputFolderPath = join( dirname(outputFolderPath), basename(outputFolderPath).trim().toLowerCase().replace(pathSpacingRegex, '-') ) const parentDirectory = join(outputFolderPath, '..') await ensureDir(parentDirectory) const silent = !!options.silent if (!silent) console.log( chalk.green('Creating a new project at ') + chalk.cyan('"' + outputFolderPath + '"') ) process.env.OUTPUT_FOLDER = outputFolderPath const exists = await directoryExists(outputFolderPath) let overwriteMode = null if (exists) { while (!validPreexistingOptions.includes(overwriteMode)) overwriteMode = ( await askQuestion( 'That folder already exists, how would you like to proceed?\nOptions: \n- ' + validPreexistingOptions.join(', ') + '\n> ' ) ) .toLowerCase() .trim() if (overwriteMode === 'erase') { if (options.force !== true) { const answer = await askYesOrNo( // eslint-disable-next-line quotes "Are you sure you'd like to erase the entire project? (yes/no)\n> " ) if (answer === false) { if (!silent) console.log('Exiting...') process.exit(0) } } await rimraf(outputFolderPath) } else if (overwriteMode === 'available') { // Do Nothing } else if (overwriteMode === 'stop') { throw new Error('Stop requested') } } if (!silent) console.log('Copying The Template') await copyTemplate(templateConfig.path, outputFolderPath, { overwrite: overwriteMode === 'overwrite' }) if (!silent) console.log('Generating the default emerald config') const projectConfig = await getEmeraldConfig(outputFolderPath, { generateDefaultConfig: true, defaultOptions: { sources: [templateConfig.path] } }) // Generate the default .emerald-config.json if (!silent) { console.log( chalk.green('The project has been named ') + chalk.cyan('"' + projectConfig.name + '"') ) console.log('Handling any scripts, links, etc') } global.PROJECT_STORE = getProjectStore(outputFolderPath, projectConfig) await processOutputFolder(outputFolderPath, templateConfig.path, { silent }) let packageJSON = null try { packageJSON = require(join(outputFolderPath, 'package.json')) } catch (error) { if (!silent) console.log('Could not find or access the package.json') } if (!options.noInstall && config.automaticallyInstallDependencies !== false && packageJSON) { if (!silent) console.log('Installing Dependencies') await exec('npm install', { cwd: outputFolderPath }) } if (config.automaticallyInitializeGitRepo === true) { if (!silent) console.log('Initializing Git Repository') if (!(await pathExists(join(outputFolderPath, '.git')))) await exec('git init .', { cwd: outputFolderPath }) } if (options.noLaunch !== true && typeof launchCommand == 'string') { launchCommand = launchCommand.trim() if (launchCommand.length > 0) { if (!silent) console.log('Running Launch Command') await exec(launchCommand, { cwd: outputFolderPath, shell: true }) } else { console.warn('Invalid Launch Command') } } const { projectFolders } = config if (!projectFolders.includes(outputFolderPath)) { config.projectFolders = projectFolders.concat([outputFolderPath]) saveGlobalConfig(config) } if (tempDir !== null) { await cleanup() track(false) } if (!silent) console.log(chalk.green('Project Generated Successfully!')) } module.exports = { handler: createProject, aliases: ['generate', 'gen', 'g', 'clone', 'create'], allowBonusArgs: true, spreadArgs: true, args: { templateFolder: { format: String, argsPosition: 0, prompt: 'Which template would you like to use?', required: true }, outputFolder: { format: String, argsPosition: 1, prompt: 'What would you like to name the project?', required: true }, noLaunch: { format: Boolean, default: false } } }