UNPKG

zoro-cli

Version:

https://github.com/vuejs/vue-cli

192 lines (157 loc) 5.08 kB
const debug = require('zoro-cli-util/debug')('cli-generator'); const debugVerbose = require('zoro-cli-util/debug')('cliverbose-generator-files'); const { removeFiles, writeFiles } = require('zoro-cli-util/fs'); const { log, startSpinner, stopSpinner } = require('zoro-cli-util/logger'); const sortObject = require('zoro-cli-util/sortObject'); const path = require('path'); const fs = require('fs'); const ejs = require('ejs'); const GeneratorAPI = require('./GeneratorAPI'); const Types = require('./types'); class Generator { constructor({ context = process.cwd(), pkg = {}, files = {}, plugins = [], rootOptions = {}, args = {} }) { this.context = context; // this.originalPkg = Object.assign({}, pkg) this.pkg = pkg; this.plugins = plugins; this.rootOptions = rootOptions; debug.key('rootOptions'); debug(rootOptions); this.fileMiddlewares = []; this.files = files; this.filesToRemove = []; this.pkgKeyOrder = []; this.scriptsKeyOrder = []; this.postProcessFilesCbs = []; this.completeCbs = []; this.args = args; } async generate() { await this.applyPlugins(); // wait for file resolve await this.resolveFiles(); // write file tree to disk await this.writeFiles(); // run complete cbs if any, for example lint log('⚓ Running completion hooks...'); /* eslint-disable no-restricted-syntax, no-await-in-loop */ for (const cb of this.completeCbs) { await cb(); } /* eslint-enable no-restricted-syntax, no-await-in-loop */ } async applyPlugins() { const { rootOptions, pkg, args } = this; /* eslint-disable no-restricted-syntax, no-await-in-loop */ for (const { id, apply, options = {} } of this.plugins) { if (apply) { const api = new GeneratorAPI({ id, generator: this, options, rootOptions, pkg }); await apply({ api, options, rootOptions, Types, pkg, args }); } } /* eslint-enable no-restricted-syntax, no-await-in-loop */ } async resolveFiles() { /* eslint-disable no-restricted-syntax, no-await-in-loop */ for (const { type, middleware } of this.fileMiddlewares) { if (type === 'add') { const files = await middleware({ render: ejs.render, files: this.files }); Object.assign(this.files, files); } else if (type === 'remove') { if (Array.isArray(middleware)) { this.filesToRemove.push(...middleware); } else { this.filesToRemove.push(middleware); } } } for (const postProcess of this.postProcessFilesCbs) { await postProcess({ files: this.files }); } /* eslint-enable no-restricted-syntax, no-await-in-loop */ // set package.json this.sortPkg(); this.files['package.json'] = JSON.stringify(this.pkg, null, 2) + '\n'; debug.key('filenames'); debug(Object.keys(this.files)); debugVerbose(this.files); } sortPkg() { // ensure package.json keys has readable order this.pkg.dependencies = sortObject(this.pkg.dependencies); this.pkg.devDependencies = sortObject(this.pkg.devDependencies); this.pkg.scripts = sortObject(this.pkg.scripts, this.scriptsKeyOrder, ':'); this.pkg = sortObject(this.pkg, this.pkgKeyOrder); } async writeFiles() { startSpinner('generating files'); const belongToFolder = (folder, filepath) => { // 如果是 . 文件, 那么要覆盖 const firstChar = path.basename(filepath).charAt(0); if (firstChar === '_' || firstChar === '.') return false; // 特殊情况要覆盖 const specialCases = ['src/assets/template', 'src/style/layout/html-body.css', 'src/style/layout/html-body-mobile.css']; const isSpecial = specialCases.some(sepcialCase => filepath.indexOf(sepcialCase) !== -1); if (isSpecial) return false; // 属于要忽略的文件夹 return filepath.indexOf(folder) === 0 || filepath.indexOf(`./${folder}`) === 0; }; /* eslint-disable no-restricted-syntax, no-await-in-loop */ // skip the following folders for (const folder of ['src', 'code', 'pages', 'components', 'common', 'tests', 'test', 'types']) { if (fs.existsSync(`./${folder}`)) { log(); log(`skip override ${folder}`); this.filesToRemove = this.filesToRemove.filter(filepath => !belongToFolder(folder, filepath)); Object.keys(this.files).forEach(filepath => { if (belongToFolder(folder, filepath)) { delete this.files[filepath]; } }); } } /* eslint-enable no-restricted-syntax, no-await-in-loop */ await removeFiles(this.context, this.filesToRemove); await writeFiles(this.context, this.files); stopSpinner(false); log(`files generated to ${this.context}`); } } module.exports = Generator;