UNPKG

@controlla/cli

Version:

Command line interface for rapid Controlla projects development

407 lines (358 loc) 10.8 kB
// const path = require('path') const chalk = require('chalk') const debug = require('debug') const inquirer = require('inquirer') const EventEmitter = require('events') const path = require('path') const krnos = require('@krnos/kronos') const getGitUser = require('./util/git-user') const shouldBackend = require('./util/shouldBackend') const isFlutter = require('./util/isFlutter') const { installDeps, installComposerDeps, installFlutterDeps, injectedScripts } = require('./util/installDeps') const { clearConsole } = require('./util/clearConsole') const { saveOptions, loadOptions } = require('./options') const { log, warn, hasGit, hasProjectGit, hasYarn, hasPnpm3OrLater, logWithSpinner, stopSpinner, execa } = require('@vue/cli-shared-utils') module.exports = class Creator extends EventEmitter { constructor (name, context) { super() this.name = name this.context = process.env.VUE_CLI_CONTEXT = context const { presetPrompt } = this.resolveIntroPrompts() this.presetPrompt = presetPrompt this.outroPrompts = this.resolveOutroPrompts() this.injectedPrompts = [] this.createCompleteCbs = [] this.run = this.run.bind(this) // const promptAPI = new PromptModuleAPI(this) // promptModules.forEach(m => m(promptAPI)) } async create (cliOptions = {}, preset = null) { const isTestOrDebug = process.env.CONTROLLA_CLI_TEST || process.env.CONTROLLA_CLI_DEBUG const { run, name, context, createCompleteCbs } = this let repository = '' if (!preset) { if (cliOptions.preset) { preset = cliOptions.preset } else { const props = await this.resolvePrompts() preset = props.preset repository = props.repository } } const packageManager = ( cliOptions.packageManager || loadOptions().packageManager || (hasYarn() ? 'yarn' : null) || (hasPnpm3OrLater() ? 'pnpm' : 'npm') ) const description = loadOptions().description const author = getGitUser() const isShouldBackend = await shouldBackend(preset) const flutterCommand = await isFlutter(preset) await clearConsole() logWithSpinner('✨', `Creating project in ${chalk.yellow(context)}.`) this.emit('creation', { event: 'creating' }) const src = path.join(path.dirname(require.resolve('../templates')), preset) await krnos.generate(name, description, author, src, context, isShouldBackend, preset, err => { if (err) console.log(err) stopSpinner() }) // intilaize git repository before installing deps // so that vue-cli-service can setup git hooks. const shouldInitGit = this.shouldInitGit(cliOptions) if (shouldInitGit) { logWithSpinner('🗃', 'Initializing git repository...') this.emit('creation', { event: 'git-init' }) await run('git init') } log('') // const frontendPath = isShouldBackend ? preset === 'saas' ? `${context}/central` : `${context}/frontend` : context // // install plugins // stopSpinner() // log('🚀 Installing CLI plugins. This might take a while...') // log() // this.emit('creation', { event: 'plugins-install' }) // if (isTestOrDebug) { // // in development, avoid installation process // await require('./util/setupDevProject')(context) // } else { // await installDeps(frontendPath, packageManager, cliOptions.registry) // } if (preset === 'saas') { await installDeps(`${context}/tenant`, packageManager, cliOptions.registry) } // install composer if (isShouldBackend) { log() log('🚀 Installing additional dependencies...') this.emit('creation', { event: 'plugins-install' }) log() await installDeps(context, packageManager, cliOptions.registry) } // install composer if (isShouldBackend) { log() log('🚀 Installing Composer dependencies...') this.emit('creation', { event: 'composer-install' }) log() await installComposerDeps(context, 'composer') } // Injected additional scripts if (isShouldBackend) { log() log('📦 Injected additional scripts...') this.emit('creation', { event: 'injected-scripts' }) log() await injectedScripts(context, 'composer') } // run complete cbs if any (injected by generators) log() logWithSpinner('⚓', 'Running completion hooks...') this.emit('creation', { event: 'completion-hooks' }) await execa("find . -name '.controllaignore' -exec rename 's/controlla/git/' -- {} +", { cwd: this.context, shell: true }) // if (isShouldBackend) { // log() // logWithSpinner('🚧', 'Creating virtualhost...') // this.emit('creation', { event: 'creating-virtualhost' }) // await execa.command(`sudo sed -i -e "s/127.0.0.1\tlocalhost/127.0.0.1\tlocalhost\n127.0.0.1\t${name}.test/g" /etc/hosts`, { cwd: context }).then(_ => { // stopSpinner() // }) // } for (const cb of createCompleteCbs) { await cb() } // log instructions stopSpinner() log() // commit initial state let gitCommitFailed = false if (shouldInitGit) { await run('git', ['remote', 'add', 'origin', repository]) await run('git add -A') if (isTestOrDebug) { await run('git', ['config', 'user.name', 'test']) await run('git', ['config', 'user.email', 'test@test.com']) } const msg = typeof cliOptions.git === 'string' ? cliOptions.git : 'chore: Init Project :tada:' try { await run('git', ['commit', '-m', msg, '--no-verify']) } catch (e) { log(`Error: ${e}`) gitCommitFailed = true } } log() stopSpinner() log() if (shouldInitGit) { logWithSpinner('⚓', 'Create DEV branch...') this.emit('creation', { event: 'create-branch' }) await run('git', ['checkout', '-b', 'DEV']) } stopSpinner() // generate README.md log('') logWithSpinner('📄', 'Generating README.md...') stopSpinner() // install flutter dependencies if (flutterCommand) { log() log('🚀 Installing flutter dependencies...') this.emit('creation', { event: 'plugins-install' }) log() await installFlutterDeps(context, 'flutter') } log('') log(`🎉 Successfully created project ${chalk.yellow(name)}.`) if (!cliOptions.skipGetStarted) { log('👉 Get started with the following commands:\n\n' + (this.context === process.cwd() ? '' : chalk.cyan(` ${chalk.gray('$')} cd ${name}\n`)) + chalk.cyan( ` ${chalk.gray('$')} ${ flutterCommand ? 'flutter run' : packageManager === 'yarn' ? 'yarn serve' : packageManager === 'pnpm' ? 'pnpm run serve' : 'npm run serve' }` ) ) } log() this.emit('creation', { event: 'done' }) if (gitCommitFailed) { warn( 'Skipped git commit due to missing username and email in git config.\n' + 'You will need to perform the initial commit yourself.\n' ) } // generator.printExitLogs() } run (command, args) { if (!args) { [command, ...args] = command.split(/\s+/) } return execa(command, args, { cwd: this.context }) } async resolvePrompts (answers = null) { // prompt if (!answers) { await clearConsole(true) answers = await inquirer.prompt(this.resolveFinalPrompts()) } debug('vue-cli:answers')(answers) if (answers.packageManager) { saveOptions({ packageManager: answers.packageManager }) } saveOptions({ description: answers.description }) return answers } resolveIntroPrompts () { const presetPrompt = { name: 'preset', type: 'list', message: 'Please pick a preset:', choices: [ { name: 'ERP Project', value: 'erp' }, { name: 'Flutter Project', value: 'flutter' }, { name: 'Flutter Desktop Project', value: 'flutter(desktop)' }, { name: 'Electron Project (clear)', value: 'electron(clear)' }, { name: 'Saas Project', value: 'saas' }, { name: 'CRM Project', value: 'crm' }, { name: 'CMS Project', value: 'cms' }, { name: 'Landing Project', value: 'landing' }, { name: 'Package Project', value: 'package' }, { name: 'Plugin Project', value: 'plugin' } ] } return { presetPrompt } } resolveOutroPrompts () { const outroPrompts = [] // ask for packageManager once if (hasYarn() || hasPnpm3OrLater()) { const packageManagerChoices = [] packageManagerChoices.push({ name: 'Use NPM', value: 'npm', short: 'NPM' }) if (hasYarn()) { packageManagerChoices.push({ name: 'Use Yarn', value: 'yarn', short: 'Yarn' }) } if (hasPnpm3OrLater()) { packageManagerChoices.push({ name: 'Use PNPM', value: 'pnpm', short: 'PNPM' }) } outroPrompts.push({ name: 'packageManager', type: 'list', message: 'Pick the package manager to use when installing dependencies:', choices: packageManagerChoices }) } outroPrompts.push({ name: 'description', type: 'string', required: false, message: 'Project description', default: 'An vue project' }) outroPrompts.push({ name: 'repository', type: 'input', required: true, message: 'Set remote repository', validate: (value) => { if (value.length) { return true } else { return 'Please enter the remote repository' } } }) return outroPrompts } resolveFinalPrompts () { const prompts = [ this.presetPrompt, ...this.injectedPrompts, ...this.outroPrompts ] debug('vue-cli:prompts')(prompts) return prompts } shouldInitGit (cliOptions) { if (!hasGit()) { return false } // --git if (cliOptions.forceGit) { return true } // --no-git if (cliOptions.git === false || cliOptions.git === 'false') { return false } // default: true unless already in a git repo return !hasProjectGit(this.context) } }