UNPKG

generator-confit

Version:

Yeoman generator for creating the development process, tools and a sample project for current-generation web applications

215 lines (185 loc) 7.4 kB
'use strict'; const _ = require('lodash'); _.mixin(require('lodash-deep')); const scriptHasShortcut = {start: true, stop: true, test: true, restart: true}; module.exports = { addNpmTasks, addPackageJsonConfig, setNpmDevDependenciesFromArray, setNpmDependenciesFromArray, readPackageJson, writePackageJson, }; /** * Adds NPM tasks from an array of task descriptions * * @param {Object[]} arrayOfTaskDefinitions Array of task definitions * @param {string} isUnsupportedMessage A message to display if the task is not supported * @this generator */ function addNpmTasks(arrayOfTaskDefinitions, isUnsupportedMessage) { let templateData = this.getStandardTemplateData(); let self = this; (arrayOfTaskDefinitions || []).forEach((item) => { // The item.tasks can contain EJS template. Convert them now as they will appear only in the package.json, not in the readme doc let resolvedTasks = item.tasks.map((taskStr) => self.renderEJS(taskStr, templateData)); // Verify whether the task is supported or not. If it is not supported, show the unsupported message instead. defineNpmTask.call(this, item.name, isUnsupportedMessage ? ['echo ' + isUnsupportedMessage] : resolvedTasks); if (item.description) { // console.log('>> ', this.name, item.name); // If the description or features contain EJS templates, addReadmeDoc() will convert them this.addReadmeDoc('buildTask.' + this.name + '.' + item.name, { command: 'npm ' + (scriptHasShortcut[item.name] ? '' : 'run ') + item.name, description: isUnsupportedMessage || item.description, features: isUnsupportedMessage ? [] : item.features || [], }); } }); } /** * Defines an NPM Script task. Uses npm-run-all when there are multiple tasks to run. * Adds readme info for each script * If the scriptName is 'start', 'stop', 'test' or 'restart', the command is `npm <scriptName>`, * otherwise it is `npm run <scriptName>`. * * @param {string} scriptName The name of the script in the `scripts` section of package.json * @param {Object[]} taskArray A list of tasks to execute (can contain a single task) * @this generator */ function defineNpmTask(scriptName, taskArray) { let packageJson = readPackageJson.call(this); let runner = taskArray.length > 1 ? 'npm-run-all ' : ''; _.set(packageJson, 'scripts.' + scriptName, runner + taskArray.join(' ')); writePackageJson.call(this, packageJson); } /** * Adds a key-value to ANY block in package.json * * @param {Object[]} arrayOfKeyValues [{package.json.path.key: value}, {k: v}, ...]. * @this generator */ function addPackageJsonConfig(arrayOfKeyValues) { let values = arrayOfKeyValues || []; if (values.length === 0) { return; } let packageJson = readPackageJson.call(this); let templateData = this.getStandardTemplateData(); values.forEach((item) => { let newItem = this.forEJSInObj(item, templateData); // Parse any EJS templates in the object let key = _.keys(newItem)[0]; _.set(packageJson, key, newItem[key]); }); writePackageJson.call(this, packageJson); } const MINIMUM_VALID_PACKAGE_JSON = { description: '', name: '', repository: { url: '', }, }; /** * * @return {Object|{description: string, name: string}} Package JSON object * @this generator */ function readPackageJson() { return _.merge({}, MINIMUM_VALID_PACKAGE_JSON, this.fs.readJSON(this.destinationPath('package.json'))); } /** * * @param {Object} data Object to store inside package.json * @this generator */ function writePackageJson(data) { this.fs.writeJSON(this.destinationPath('package.json'), data); } /** * Set NPM dependencies from an array * @param {Object[]} packages Array of packages * @this generator */ function setNpmDependenciesFromArray(packages) { let ctx = {me: this, config: this.getGlobalConfig()}; setDependenciesFromArray.call(ctx, packages, 'dependencies'); } /** * Set NPM dev-dependencies from an array * @param {Object[]} packages Array of packages * @this generator */ function setNpmDevDependenciesFromArray(packages) { let ctx = {me: this, config: this.getGlobalConfig()}; setDependenciesFromArray.call(ctx, packages, 'devDependencies'); } /** * Adds dependencies based on some optional criteria. * In order to eval() the criteria, this method is called with a different context: * `this = {me: (the parent this), config: this.getGlobalConfig()}`. This allows the * `eval(pkg.criteria)` to contain references to `config...` and work correctly. * * See setNpmDevDependenciesFromArray() for an example of this. * * @param {Object[]} packages The array can contain arrays, so flatten it first. * A package has a name and a version. If the name contains a '/' and does not start * with a '@', the string before the '/' is used as the name. * NPM introduced scoped packages, which look like '@scope/package'. So don't split on '/' * for scoped packages. * The package could also be a relative file reference, starting with './'. Ignore these. * @param {string} optKey Defaults to 'dependencies', but can also be 'devDependencies' * @this generator */ function setDependenciesFromArray(packages, optKey) { let packagesToAdd = (packages || []).map((pkg) => { if (pkg.criteria) { // console.log('eval', pkg.criteria, eval(pkg.criteria)); pkg.isAddDep = eval(pkg.criteria); // eslint-disable-line no-eval } return pkg; }); _.flattenDeep(packagesToAdd).forEach((pkg) => { let obj = {}; // Only add packages that are NPM packages (which have name properties), not relativePath files (starting with './') if (pkg.name) { obj[getBasePackageName(pkg)] = pkg.version; setNpmDependencies.call(this.me, obj, pkg.isAddDep, optKey); } }); } /** * Add/remove dependencies to the package.json file * @param {Object} deps NPM package names * @param {boolean} isAddDep Whether to add or remove the dependency * @param {string} optKey Defaults to 'dependencies', but can also be 'devDependencies' * @this generator */ function setNpmDependencies(deps, isAddDep, optKey) { let packageJson = readPackageJson.call(this); let key = optKey || 'dependencies'; let addDep = isAddDep === undefined ? true : Boolean(isAddDep); // Create the key in case it doesn't exist if (!packageJson[key]) { packageJson[key] = {}; } if (addDep) { packageJson[key] = _.assign(packageJson[key], deps); } else { // Remove the dependencies packageJson[key] = _.omit(packageJson[key], _.keys(deps)); } writePackageJson.call(this, packageJson); } /** * Returns the base package name for a package definition. * Sometimes the package definition looks like this: baseName/subPackageName * To install the package, we just need th baseName fragment of the package name. * * @param {Object} pkg Package name object {name: 'string/optionalPath', ...} * @return {string} Base package name */ function getBasePackageName(pkg) { let isScopedPackage = pkg.name.indexOf('@') === 0; let pkgIndexOfSlash = pkg.name.indexOf('/'); return !isScopedPackage && pkgIndexOfSlash > -1 ? pkg.name.substr(0, pkgIndexOfSlash) : pkg.name; }