UNPKG

zoro-cli

Version:

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

285 lines (241 loc) 7.76 kB
function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); if (enumerableOnly) symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; }); keys.push.apply(keys, symbols); } return keys; } function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i] != null ? arguments[i] : {}; if (i % 2) { ownKeys(Object(source), true).forEach(function (key) { _defineProperty(target, key, source[key]); }); } else if (Object.getOwnPropertyDescriptors) { Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)); } else { ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } } return target; } function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; } const { isString, isObject, isFunction } = require('zoro-cli-util/is'); const { renderArray, renderDir, renderObject } = require('zoro-cli-util/renderFile'); const mergeScripts = require('zoro-cli-util/mergeScripts'); const path = require('path'); const { union, mergeWith } = require('lodash'); const Types = require('./types'); function extractCallDir() { // extract api.render() callsite file location using error stack const obj = {}; Error.captureStackTrace(obj); const callSite = obj.stack.split('\n')[3]; const fileName = callSite.match(/\s\((.*):\d+:\d+\)$/)[1]; return path.dirname(fileName); } class GeneratorAPI { constructor({ id, generator, options = {}, rootOptions = {}, pkg = {} } = {}) { this.id = id; this.generator = generator; this.options = options; this.rootOptions = rootOptions; this.pkg = pkg; } /** * Resolves the data when rendering templates. * * @private */ _resolveData(additionalData) { return _objectSpread({ options: this.options, rootOptions: this.rootOptions, Types, pkg: this.pkg }, additionalData); } /** * Inject a file processing middleware. * * @private * @param {FileMiddleware} middleware - A middleware function that receives the * virtual files tree object, and an ejs render function. Can be async. */ _injectFileAddMiddleware(middleware) { this.generator.fileMiddlewares.push({ type: 'add', middleware }); } _injectFileRemoveMiddleware(middleware) { this.generator.fileMiddlewares.push({ type: 'remove', middleware }); } /** * Resolve path for a project. * * @param {string} _path - Relative path from project root * @return {string} The resolved absolute path. */ resolve(_path) { return path.resolve(this.generator.context, _path); } /** * Extend the package.json of the project. * Nested fields are deep-merged unless `{ merge: false }` is passed. * Also resolves dependency conflicts between plugins. * Tool configuration fields may be extracted into standalone files before * files are written to disk. * * @param {object | () => object} fields - Fields to merge. */ extendPackage(fields, options = {}) { const { overrideObj, overrideArr } = options; const pkg = this.generator.pkg; const toMerge = isFunction(fields) ? fields(pkg) : fields; Object.keys(toMerge).forEach(key => { const value = toMerge[key]; const existing = pkg[key]; if (!existing) { pkg[key] = value; } else if (Array.isArray(existing) && Array.isArray(value)) { if (overrideArr) { pkg[key] = value; } else { pkg[key] = union(existing, value); } } else if (isObject(value)) { if (overrideObj) { pkg[key] = value; } else if (key === 'scripts') { pkg[key] = mergeScripts(existing, value); } else { pkg[key] = mergeWith({}, existing, value, (destValue, srcValue) => { if (Array.isArray(destValue) || Array.isArray(srcValue)) { if (overrideArr) { return srcValue; } return union(destValue, srcValue); } if (destValue !== undefined && srcValue === undefined) { // 如果是 ''/null/undefined, 不覆盖 return destValue; } return undefined; }); } } else { pkg[key] = value; } }); } removeDependencies(names = []) { names.forEach(name => { const { pkg } = this.generator; if (pkg.dependencies) { delete pkg.dependencies[name]; } if (pkg.devDependencies) { delete pkg.devDependencies[name]; } }); } /** * Render template files into the virtual files tree object. * * @param {string | object | FileMiddleware} source - * Can be one of: * - relative path to caller directory; * - Object of { dir: '', arr: [] }; * - dir is relative path to caller directory * - arr is an array of paths relative to dir * - Object hash of { sourceTemplate: targetFile } mappings; * - a custom file middleware function. * @param {object} [additionalData] - additional data available to templates. * @param {object} [ejsOptions] - options for ejs. */ render(source, additionalData = {}, ejsOptions = {}) { const baseDir = extractCallDir(); const data = this._resolveData(additionalData); if (isString(source)) { this._injectFileAddMiddleware(() => renderDir({ dir: path.join(baseDir, source), data, ejsOptions })); } else if (isObject(source)) { if (Array.isArray(source.arr)) { const { arr, dir = '' } = source; this._injectFileAddMiddleware(() => renderArray({ dir: path.join(baseDir, dir), arr, data, ejsOptions })); } else { this._injectFileAddMiddleware(() => renderObject({ dir: baseDir, obj: source, data, ejsOptions })); } } else if (isFunction(source === 'function')) { this._injectFileAddMiddleware(source); } } remove(source) { this._injectFileRemoveMiddleware(source); } setScriptsKeyOrder(keyOrder) { this.generator.scriptsKeyOrder = keyOrder; } setPkgKeyOrder(keyOrder) { this.generator.pkgKeyOrder = keyOrder; } _spliceKeyOrder(property, keyOrderMap) { const arr = this.generator[property] || []; Object.keys(keyOrderMap).forEach(key => { const index = arr.indexOf(key); if (index !== -1) { arr.splice(index + 1, 0, ...keyOrderMap[key]); } else { arr.push(...keyOrderMap[key]); } }); this.generator[property] = arr; } spliceScriptsKeyOrder(keyOrderMap) { this._spliceKeyOrder('scriptsKeyOrder', keyOrderMap); } splicePkgKeyOrder(keyOrderMap) { this._spliceKeyOrder('pkgKeyOrder', keyOrderMap); } /** * Push a file middleware that will be applied after all normal file * middelwares have been applied. * * @param {FileMiddleware} cb */ postProcessFiles(cb) { this.generator.postProcessFilesCbs.push(cb); } /** * Push a callback to be called when the files have been written to disk. * * @param {function} cb */ onCreateComplete(cb) { this.generator.completeCbs.push(cb); } } module.exports = GeneratorAPI;