From 5f60f14ee6989b6271ff14b2047e5041b35ebdcd Mon Sep 17 00:00:00 2001 From: Pavan Kumar Sunkara Date: Sat, 13 Jul 2019 00:44:08 +0200 Subject: [PATCH 1/6] refactor(cli): rename onCreateComplete to afterInvoke --- packages/@vue/cli/__tests__/Generator.spec.js | 19 ++++++++++++++++++- packages/@vue/cli/lib/Creator.js | 8 ++++---- packages/@vue/cli/lib/Generator.js | 4 ++-- packages/@vue/cli/lib/GeneratorAPI.js | 6 +++++- packages/@vue/cli/lib/add.js | 10 ++-------- packages/@vue/cli/lib/invoke.js | 8 ++++---- 6 files changed, 35 insertions(+), 20 deletions(-) diff --git a/packages/@vue/cli/__tests__/Generator.spec.js b/packages/@vue/cli/__tests__/Generator.spec.js index 02119f0682..263aa4a792 100644 --- a/packages/@vue/cli/__tests__/Generator.spec.js +++ b/packages/@vue/cli/__tests__/Generator.spec.js @@ -448,7 +448,24 @@ test('api: onCreateComplete', () => { } } ], - completeCbs: cbs + afterInvokeCbs: cbs + }) + expect(cbs).toContain(fn) +}) + +test('api: afterInvoke', () => { + const fn = () => {} + const cbs = [] + new Generator('/', { + plugins: [ + { + id: 'test', + apply: api => { + api.afterInvoke(fn) + } + } + ], + afterInvokeCbs: cbs }) expect(cbs).toContain(fn) }) diff --git a/packages/@vue/cli/lib/Creator.js b/packages/@vue/cli/lib/Creator.js index 9a0cd6d906..6ffb31973c 100644 --- a/packages/@vue/cli/lib/Creator.js +++ b/packages/@vue/cli/lib/Creator.js @@ -54,7 +54,7 @@ module.exports = class Creator extends EventEmitter { this.outroPrompts = this.resolveOutroPrompts() this.injectedPrompts = [] this.promptCompleteCbs = [] - this.createCompleteCbs = [] + this.afterInvokeCbs = [] this.run = this.run.bind(this) @@ -64,7 +64,7 @@ module.exports = class Creator extends EventEmitter { async create (cliOptions = {}, preset = null) { const isTestOrDebug = process.env.VUE_CLI_TEST || process.env.VUE_CLI_DEBUG - const { run, name, context, createCompleteCbs } = this + const { run, name, context, afterInvokeCbs } = this if (!preset) { if (cliOptions.preset) { @@ -187,7 +187,7 @@ module.exports = class Creator extends EventEmitter { const generator = new Generator(context, { pkg, plugins, - completeCbs: createCompleteCbs + afterInvokeCbs }) await generator.generate({ extractConfigFiles: preset.useConfigFiles @@ -204,7 +204,7 @@ module.exports = class Creator extends EventEmitter { // run complete cbs if any (injected by generators) logWithSpinner('⚓', `Running completion hooks...`) this.emit('creation', { event: 'completion-hooks' }) - for (const cb of createCompleteCbs) { + for (const cb of afterInvokeCbs) { await cb() } diff --git a/packages/@vue/cli/lib/Generator.js b/packages/@vue/cli/lib/Generator.js index 0955b212de..3aac56553d 100644 --- a/packages/@vue/cli/lib/Generator.js +++ b/packages/@vue/cli/lib/Generator.js @@ -69,7 +69,7 @@ module.exports = class Generator { constructor (context, { pkg = {}, plugins = [], - completeCbs = [], + afterInvokeCbs = [], files = {}, invoking = false } = {}) { @@ -79,7 +79,7 @@ module.exports = class Generator { this.pkg = Object.assign({}, pkg) this.imports = {} this.rootOptions = {} - this.completeCbs = completeCbs + this.afterInvokeCbs = afterInvokeCbs this.configTransforms = {} this.defaultConfigTransforms = defaultConfigTransforms this.reservedConfigTransforms = reservedConfigTransforms diff --git a/packages/@vue/cli/lib/GeneratorAPI.js b/packages/@vue/cli/lib/GeneratorAPI.js index 91cd4d0992..e8c3f9f72a 100644 --- a/packages/@vue/cli/lib/GeneratorAPI.js +++ b/packages/@vue/cli/lib/GeneratorAPI.js @@ -280,7 +280,11 @@ class GeneratorAPI { * @param {function} cb */ onCreateComplete (cb) { - this.generator.completeCbs.push(cb) + this.afterInvoke(cb) + } + + afterInvoke (cb) { + this.generator.afterInvokeCbs.push(cb) } /** diff --git a/packages/@vue/cli/lib/add.js b/packages/@vue/cli/lib/add.js index e77e2fd188..251fd9d1ca 100644 --- a/packages/@vue/cli/lib/add.js +++ b/packages/@vue/cli/lib/add.js @@ -5,8 +5,7 @@ const PackageManager = require('./util/ProjectPackageManager') const { log, error, - resolvePluginId, - resolveModule + resolvePluginId } = require('@vue/cli-shared-utils') const confirmIfGitDirty = require('./util/confirmIfGitDirty') @@ -27,12 +26,7 @@ async function add (pluginName, options = {}, context = process.cwd()) { log(`${chalk.green('✔')} Successfully installed plugin: ${chalk.cyan(packageName)}`) log() - const generatorPath = resolveModule(`${packageName}/generator`, context) - if (generatorPath) { - invoke(pluginName, options, context) - } else { - log(`Plugin ${packageName} does not have a generator to invoke`) - } + invoke(pluginName, options, context) } module.exports = (...args) => { diff --git a/packages/@vue/cli/lib/invoke.js b/packages/@vue/cli/lib/invoke.js index 87aeaa9956..d7a6e6fe1d 100644 --- a/packages/@vue/cli/lib/invoke.js +++ b/packages/@vue/cli/lib/invoke.js @@ -103,12 +103,12 @@ async function invoke (pluginName, options = {}, context = process.cwd()) { async function runGenerator (context, plugin, pkg = getPkg(context)) { const isTestOrDebug = process.env.VUE_CLI_TEST || process.env.VUE_CLI_DEBUG - const createCompleteCbs = [] + const afterInvokeCbs = [] const generator = new Generator(context, { pkg, plugins: [plugin], files: await readFiles(context), - completeCbs: createCompleteCbs, + afterInvokeCbs, invoking: true }) @@ -132,9 +132,9 @@ async function runGenerator (context, plugin, pkg = getPkg(context)) { await pm.install() } - if (createCompleteCbs.length) { + if (afterInvokeCbs.length) { logWithSpinner('⚓', `Running completion hooks...`) - for (const cb of createCompleteCbs) { + for (const cb of afterInvokeCbs) { await cb() } stopSpinner() From ad459d98aeeeb608bfe78b821a4b2758fb4aca25 Mon Sep 17 00:00:00 2001 From: Pavan Kumar Sunkara Date: Thu, 23 Aug 2018 11:17:11 +0200 Subject: [PATCH 2/6] feat(cli): added afterAnyInvoke hook support --- packages/@vue/cli/__tests__/Generator.spec.js | 21 +++++++++++++ packages/@vue/cli/lib/Creator.js | 9 ++++-- packages/@vue/cli/lib/Generator.js | 30 ++++++++++++++++--- packages/@vue/cli/lib/GeneratorAPI.js | 10 +++++++ packages/@vue/cli/lib/invoke.js | 20 +++++++++++++ 5 files changed, 84 insertions(+), 6 deletions(-) diff --git a/packages/@vue/cli/__tests__/Generator.spec.js b/packages/@vue/cli/__tests__/Generator.spec.js index 263aa4a792..09f0a18029 100644 --- a/packages/@vue/cli/__tests__/Generator.spec.js +++ b/packages/@vue/cli/__tests__/Generator.spec.js @@ -470,6 +470,27 @@ test('api: afterInvoke', () => { expect(cbs).toContain(fn) }) +test('api: afterAnyInvoke', () => { + const fn = () => {} + const cbs = [] + + const apply = () => {} + apply.hooks = api => { + api.afterAnyInvoke(fn) + } + + new Generator('/', { + plugins: [ + { + id: 'test', + apply + } + ], + afterAnyInvokeCbs: cbs + }) + expect(cbs).toContain(fn) +}) + test('api: resolve', () => { new Generator('/foo/bar', { plugins: [ { diff --git a/packages/@vue/cli/lib/Creator.js b/packages/@vue/cli/lib/Creator.js index 6ffb31973c..a2897f27ca 100644 --- a/packages/@vue/cli/lib/Creator.js +++ b/packages/@vue/cli/lib/Creator.js @@ -55,6 +55,7 @@ module.exports = class Creator extends EventEmitter { this.injectedPrompts = [] this.promptCompleteCbs = [] this.afterInvokeCbs = [] + this.afterAnyInvokeCbs = [] this.run = this.run.bind(this) @@ -64,7 +65,7 @@ module.exports = class Creator extends EventEmitter { async create (cliOptions = {}, preset = null) { const isTestOrDebug = process.env.VUE_CLI_TEST || process.env.VUE_CLI_DEBUG - const { run, name, context, afterInvokeCbs } = this + const { run, name, context, afterInvokeCbs, afterAnyInvokeCbs } = this if (!preset) { if (cliOptions.preset) { @@ -187,7 +188,8 @@ module.exports = class Creator extends EventEmitter { const generator = new Generator(context, { pkg, plugins, - afterInvokeCbs + afterInvokeCbs, + afterAnyInvokeCbs }) await generator.generate({ extractConfigFiles: preset.useConfigFiles @@ -207,6 +209,9 @@ module.exports = class Creator extends EventEmitter { for (const cb of afterInvokeCbs) { await cb() } + for (const cb of afterAnyInvokeCbs) { + await cb() + } // generate README.md stopSpinner() diff --git a/packages/@vue/cli/lib/Generator.js b/packages/@vue/cli/lib/Generator.js index 3aac56553d..fce0b1a09f 100644 --- a/packages/@vue/cli/lib/Generator.js +++ b/packages/@vue/cli/lib/Generator.js @@ -6,7 +6,7 @@ const writeFileTree = require('./util/writeFileTree') const inferRootOptions = require('./util/inferRootOptions') const normalizeFilePaths = require('./util/normalizeFilePaths') const runCodemod = require('./util/runCodemod') -const { toShortPluginId, matchesPluginId } = require('@vue/cli-shared-utils') +const { toShortPluginId, matchesPluginId, loadModule } = require('@vue/cli-shared-utils') const ConfigTransform = require('./ConfigTransform') const logger = require('@vue/cli-shared-utils/lib/logger') @@ -69,17 +69,21 @@ module.exports = class Generator { constructor (context, { pkg = {}, plugins = [], + otherPlugins = [], afterInvokeCbs = [], + afterAnyInvokeCbs = [], files = {}, invoking = false } = {}) { this.context = context this.plugins = plugins + this.otherPlugins = otherPlugins this.originalPkg = pkg this.pkg = Object.assign({}, pkg) this.imports = {} this.rootOptions = {} - this.afterInvokeCbs = afterInvokeCbs + this.afterInvokeCbs = [] + this.afterAnyInvokeCbs = afterAnyInvokeCbs this.configTransforms = {} this.defaultConfigTransforms = defaultConfigTransforms this.reservedConfigTransforms = reservedConfigTransforms @@ -93,14 +97,33 @@ module.exports = class Generator { // exit messages this.exitLogs = [] + const pluginIds = plugins.map(p => p.id) const cliService = plugins.find(p => p.id === '@vue/cli-service') const rootOptions = cliService ? cliService.options : inferRootOptions(pkg) + + // apply hooks from all plugins + otherPlugins.forEach(id => { + const api = new GeneratorAPI(id, this, {}, rootOptions) + const pluginGenerator = loadModule(`${id}/generator`, context) + if (pluginGenerator && pluginGenerator.hooks) { + pluginGenerator.hooks(api, {}, rootOptions, pluginIds) + } + }) + + // reset nonAny hooks + this.afterInvokeCbs = afterInvokeCbs + this.postProcessFilesCbs = [] + // apply generators from plugins plugins.forEach(({ id, apply, options }) => { const api = new GeneratorAPI(id, this, options, rootOptions) apply(api, options, rootOptions, invoking) + + if (apply.hooks) { + apply.hooks(api, options, rootOptions, pluginIds) + } }) } @@ -245,8 +268,7 @@ module.exports = class Generator { hasPlugin (_id) { return [ ...this.plugins.map(p => p.id), - ...Object.keys(this.pkg.devDependencies || {}), - ...Object.keys(this.pkg.dependencies || {}) + ...this.otherPlugins ].some(id => matchesPluginId(_id, id)) } diff --git a/packages/@vue/cli/lib/GeneratorAPI.js b/packages/@vue/cli/lib/GeneratorAPI.js index e8c3f9f72a..ba34a5abd9 100644 --- a/packages/@vue/cli/lib/GeneratorAPI.js +++ b/packages/@vue/cli/lib/GeneratorAPI.js @@ -287,6 +287,16 @@ class GeneratorAPI { this.generator.afterInvokeCbs.push(cb) } + /** + * Push a callback to be called when the files have been written to disk + * from non invoked plugins + * + * @param {function} cb + */ + afterAnyInvoke (cb) { + this.generator.afterAnyInvokeCbs.push(cb) + } + /** * Add a message to be printed when the generator exits (after any other standard messages). * diff --git a/packages/@vue/cli/lib/invoke.js b/packages/@vue/cli/lib/invoke.js index d7a6e6fe1d..27810b2d3a 100644 --- a/packages/@vue/cli/lib/invoke.js +++ b/packages/@vue/cli/lib/invoke.js @@ -9,6 +9,7 @@ const { hasProjectGit, logWithSpinner, stopSpinner, + isPlugin, resolvePluginId, loadModule } = require('@vue/cli-shared-utils') @@ -104,11 +105,21 @@ async function invoke (pluginName, options = {}, context = process.cwd()) { async function runGenerator (context, plugin, pkg = getPkg(context)) { const isTestOrDebug = process.env.VUE_CLI_TEST || process.env.VUE_CLI_DEBUG const afterInvokeCbs = [] + const afterAnyInvokeCbs = [] + + // load all the other plugins + const otherPlugins = Object.keys(pkg.dependencies) + .concat(Object.keys(pkg.devDependencies)) + .filter(isPlugin) + .filter(id => id !== plugin.id) + const generator = new Generator(context, { pkg, plugins: [plugin], + otherPlugins, files: await readFiles(context), afterInvokeCbs, + afterAnyInvokeCbs, invoking: true }) @@ -141,6 +152,15 @@ async function runGenerator (context, plugin, pkg = getPkg(context)) { log() } + if (afterAnyInvokeCbs.length) { + logWithSpinner('⚓', `Running completion hooks from other plugins...`) + for (const cb of afterAnyInvokeCbs) { + await cb() + } + stopSpinner() + log() + } + log(`${chalk.green('✔')} Successfully invoked generator for plugin: ${chalk.cyan(plugin.id)}`) if (!process.env.VUE_CLI_TEST && hasProjectGit(context)) { const { stdout } = await execa('git', [ From 76d49e09580cd342338eb84e5ec20baeda1a26d5 Mon Sep 17 00:00:00 2001 From: Pavan Kumar Sunkara Date: Sat, 13 Jul 2019 10:26:44 +0200 Subject: [PATCH 3/6] refactor(eslint): added afterAnyInvoke hook to eslint --- packages/@vue/cli-plugin-eslint/generator/index.js | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/packages/@vue/cli-plugin-eslint/generator/index.js b/packages/@vue/cli-plugin-eslint/generator/index.js index 8a4b10e23d..b34d49ab7e 100644 --- a/packages/@vue/cli-plugin-eslint/generator/index.js +++ b/packages/@vue/cli-plugin-eslint/generator/index.js @@ -2,6 +2,9 @@ const fs = require('fs') const path = require('path') module.exports = (api, { config, lintOn = [] }, _, invoking) => { + api.assertCliVersion('^4.0.0-alpha.4') + api.assertCliServiceVersion('^4.0.0-alpha.4') + if (typeof lintOn === 'string') { lintOn = lintOn.split(',') } @@ -97,13 +100,13 @@ module.exports = (api, { config, lintOn = [] }, _, invoking) => { require('@vue/cli-plugin-unit-jest/generator').applyESLint(api) } } +} +module.exports.hooks = (api) => { // lint & fix after create to ensure files adhere to chosen config - if (config && config !== 'base') { - api.onCreateComplete(() => { - require('../lint')({ silent: true }, api) - }) - } + api.afterAnyInvoke(() => { + require('../lint')({ silent: true }, api) + }) } const applyTS = module.exports.applyTS = api => { From 0bc61c355ff459e65cf1d9cbcef11cc79e44d9f7 Mon Sep 17 00:00:00 2001 From: Pavan Kumar Sunkara Date: Sat, 13 Jul 2019 12:08:53 +0200 Subject: [PATCH 4/6] feat(cli): added version argument to hasPlugin --- docs/dev-guide/generator-api.md | 4 ++-- packages/@vue/cli/lib/Generator.js | 31 +++++++++++++++++++++------ packages/@vue/cli/lib/GeneratorAPI.js | 5 +++-- packages/@vue/cli/lib/invoke.js | 8 ------- 4 files changed, 30 insertions(+), 18 deletions(-) diff --git a/docs/dev-guide/generator-api.md b/docs/dev-guide/generator-api.md index 20b7b9b4b2..4dfa51cc6b 100644 --- a/docs/dev-guide/generator-api.md +++ b/docs/dev-guide/generator-api.md @@ -54,12 +54,13 @@ Resolve a path for the current project - **Arguments** - `{string} id` - plugin id, can omit the (@vue/|vue-|@scope/vue)-cli-plugin- prefix + - `{string} version` - semver version range, optional - **Returns** - `{boolean}` - **Usage**: -Check if the project has a plugin with given id +Check if the project has a plugin with given id. If version range is given, then the plugin version should satisfy it ## addConfigTransform @@ -177,4 +178,3 @@ Get the entry file taking into account typescript. - **Usage**: Checks if the plugin is being invoked. - diff --git a/packages/@vue/cli/lib/Generator.js b/packages/@vue/cli/lib/Generator.js index fce0b1a09f..fe1e6c1844 100644 --- a/packages/@vue/cli/lib/Generator.js +++ b/packages/@vue/cli/lib/Generator.js @@ -1,12 +1,14 @@ const ejs = require('ejs') const debug = require('debug') +const semver = require('semver') const GeneratorAPI = require('./GeneratorAPI') +const PackageManager = require('./util/ProjectPackageManager') const sortObject = require('./util/sortObject') const writeFileTree = require('./util/writeFileTree') const inferRootOptions = require('./util/inferRootOptions') const normalizeFilePaths = require('./util/normalizeFilePaths') const runCodemod = require('./util/runCodemod') -const { toShortPluginId, matchesPluginId, loadModule } = require('@vue/cli-shared-utils') +const { toShortPluginId, matchesPluginId, loadModule, isPlugin } = require('@vue/cli-shared-utils') const ConfigTransform = require('./ConfigTransform') const logger = require('@vue/cli-shared-utils/lib/logger') @@ -69,7 +71,6 @@ module.exports = class Generator { constructor (context, { pkg = {}, plugins = [], - otherPlugins = [], afterInvokeCbs = [], afterAnyInvokeCbs = [], files = {}, @@ -77,9 +78,9 @@ module.exports = class Generator { } = {}) { this.context = context this.plugins = plugins - this.otherPlugins = otherPlugins this.originalPkg = pkg this.pkg = Object.assign({}, pkg) + this.pm = new PackageManager({ context }) this.imports = {} this.rootOptions = {} this.afterInvokeCbs = [] @@ -98,13 +99,20 @@ module.exports = class Generator { this.exitLogs = [] const pluginIds = plugins.map(p => p.id) + + // load all the other plugins + this.otherPlugins = Object.keys(this.pkg.dependencies || {}) + .concat(Object.keys(this.pkg.devDependencies || {})) + .filter(isPlugin) + .filter(id => pluginIds.indexOf(id) === -1) + const cliService = plugins.find(p => p.id === '@vue/cli-service') const rootOptions = cliService ? cliService.options : inferRootOptions(pkg) // apply hooks from all plugins - otherPlugins.forEach(id => { + this.otherPlugins.forEach(id => { const api = new GeneratorAPI(id, this, {}, rootOptions) const pluginGenerator = loadModule(`${id}/generator`, context) if (pluginGenerator && pluginGenerator.hooks) { @@ -265,11 +273,22 @@ module.exports = class Generator { debug('vue:cli-files')(this.files) } - hasPlugin (_id) { + hasPlugin (_id, _version) { return [ ...this.plugins.map(p => p.id), ...this.otherPlugins - ].some(id => matchesPluginId(_id, id)) + ].some(id => { + if (!matchesPluginId(_id, id)) { + return false + } + + if (!_version) { + return true + } + + const version = this.pm.getInstalledVersion(id) + return semver.satisfies(version, _version) + }) } printExitLogs () { diff --git a/packages/@vue/cli/lib/GeneratorAPI.js b/packages/@vue/cli/lib/GeneratorAPI.js index ba34a5abd9..1261676d35 100644 --- a/packages/@vue/cli/lib/GeneratorAPI.js +++ b/packages/@vue/cli/lib/GeneratorAPI.js @@ -133,10 +133,11 @@ class GeneratorAPI { * Check if the project has a given plugin. * * @param {string} id - Plugin id, can omit the (@vue/|vue-|@scope/vue)-cli-plugin- prefix + * @param {string} version - Plugin version. Defaults to '' * @return {boolean} */ - hasPlugin (id) { - return this.generator.hasPlugin(id) + hasPlugin (id, version) { + return this.generator.hasPlugin(id, version) } /** diff --git a/packages/@vue/cli/lib/invoke.js b/packages/@vue/cli/lib/invoke.js index 27810b2d3a..9a226df647 100644 --- a/packages/@vue/cli/lib/invoke.js +++ b/packages/@vue/cli/lib/invoke.js @@ -9,7 +9,6 @@ const { hasProjectGit, logWithSpinner, stopSpinner, - isPlugin, resolvePluginId, loadModule } = require('@vue/cli-shared-utils') @@ -107,16 +106,9 @@ async function runGenerator (context, plugin, pkg = getPkg(context)) { const afterInvokeCbs = [] const afterAnyInvokeCbs = [] - // load all the other plugins - const otherPlugins = Object.keys(pkg.dependencies) - .concat(Object.keys(pkg.devDependencies)) - .filter(isPlugin) - .filter(id => id !== plugin.id) - const generator = new Generator(context, { pkg, plugins: [plugin], - otherPlugins, files: await readFiles(context), afterInvokeCbs, afterAnyInvokeCbs, From fa2ef5db3642af313b0d1a9ff050c259b261fcd0 Mon Sep 17 00:00:00 2001 From: Pavan Kumar Sunkara Date: Sat, 13 Jul 2019 12:22:36 +0200 Subject: [PATCH 5/6] docs(cli): update docs about hooks --- docs/dev-guide/plugin-dev.md | 54 ++++++++++++++++++++---------------- 1 file changed, 30 insertions(+), 24 deletions(-) diff --git a/docs/dev-guide/plugin-dev.md b/docs/dev-guide/plugin-dev.md index 8dbbda2229..e064bf9d97 100644 --- a/docs/dev-guide/plugin-dev.md +++ b/docs/dev-guide/plugin-dev.md @@ -241,51 +241,57 @@ Let's consider the case where we have created a `router.js` file via [templating api.injectImports(api.entryFile, `import router from './router'`) ``` -Now, when we have a router imported, we can inject this router to the Vue instance in the main file. We will use `onCreateComplete` hook which is to be called when the files have been written to disk. +Now, when we have a router imported, we can inject this router to the Vue instance in the main file. We will use `afterInvoke` hook which is to be called when the files have been written to disk. First, we need to read main file content with Node `fs` module (which provides an API for interacting with the file system) and split this content on lines: ```js // generator/index.js -api.onCreateComplete(() => { - const fs = require('fs') - const contentMain = fs.readFileSync(api.entryFile, { encoding: 'utf-8' }) - const lines = contentMain.split(/\r?\n/g) -}) +module.exports.hooks = (api) => { + api.afterInvoke(() => { + const fs = require('fs') + const contentMain = fs.readFileSync(api.entryFile, { encoding: 'utf-8' }) + const lines = contentMain.split(/\r?\n/g) + }) +} ``` Then we should to find the string containing `render` word (it's usually a part of Vue instance) and add our `router` as a next string: -```js{8-9} +```js{9-10} // generator/index.js -api.onCreateComplete(() => { - const fs = require('fs') - const contentMain = fs.readFileSync(api.entryFile, { encoding: 'utf-8' }) - const lines = contentMain.split(/\r?\n/g) +module.exports.hooks = (api) => { + api.afterInvoke(() => { + const fs = require('fs') + const contentMain = fs.readFileSync(api.entryFile, { encoding: 'utf-8' }) + const lines = contentMain.split(/\r?\n/g) - const renderIndex = lines.findIndex(line => line.match(/render/)) - lines[renderIndex] += `\n router,` -}) + const renderIndex = lines.findIndex(line => line.match(/render/)) + lines[renderIndex] += `\n router,` + }) +} ``` Finally, you need to write the content back to the main file: -```js{2,11} +```js{12-13} // generator/index.js -api.onCreateComplete(() => { - const { EOL } = require('os') - const fs = require('fs') - const contentMain = fs.readFileSync(api.entryFile, { encoding: 'utf-8' }) - const lines = contentMain.split(/\r?\n/g) +module.exports.hooks = (api) => { + api.afterInvoke(() => { + const { EOL } = require('os') + const fs = require('fs') + const contentMain = fs.readFileSync(api.entryFile, { encoding: 'utf-8' }) + const lines = contentMain.split(/\r?\n/g) - const renderIndex = lines.findIndex(line => line.match(/render/)) - lines[renderIndex] += `${EOL} router,` + const renderIndex = lines.findIndex(line => line.match(/render/)) + lines[renderIndex] += `${EOL} router,` - fs.writeFileSync(api.entryFile, lines.join(EOL), { encoding: 'utf-8' }) -}) + fs.writeFileSync(api.entryFile, lines.join(EOL), { encoding: 'utf-8' }) + }) +} ``` ## Service Plugin From dd9294887afb64f9e583e2385d63a02f2aecb5c4 Mon Sep 17 00:00:00 2001 From: Pavan Kumar Sunkara Date: Mon, 15 Jul 2019 23:56:14 +0200 Subject: [PATCH 6/6] fix(cli): address review comments --- packages/@vue/cli/__tests__/Generator.spec.js | 21 ------------------- packages/@vue/cli/lib/Generator.js | 18 +++++++++++----- packages/@vue/cli/lib/invoke.js | 8 +------ 3 files changed, 14 insertions(+), 33 deletions(-) diff --git a/packages/@vue/cli/__tests__/Generator.spec.js b/packages/@vue/cli/__tests__/Generator.spec.js index 09f0a18029..263aa4a792 100644 --- a/packages/@vue/cli/__tests__/Generator.spec.js +++ b/packages/@vue/cli/__tests__/Generator.spec.js @@ -470,27 +470,6 @@ test('api: afterInvoke', () => { expect(cbs).toContain(fn) }) -test('api: afterAnyInvoke', () => { - const fn = () => {} - const cbs = [] - - const apply = () => {} - apply.hooks = api => { - api.afterAnyInvoke(fn) - } - - new Generator('/', { - plugins: [ - { - id: 'test', - apply - } - ], - afterAnyInvokeCbs: cbs - }) - expect(cbs).toContain(fn) -}) - test('api: resolve', () => { new Generator('/foo/bar', { plugins: [ { diff --git a/packages/@vue/cli/lib/Generator.js b/packages/@vue/cli/lib/Generator.js index fe1e6c1844..b99dcebd28 100644 --- a/packages/@vue/cli/lib/Generator.js +++ b/packages/@vue/cli/lib/Generator.js @@ -101,10 +101,9 @@ module.exports = class Generator { const pluginIds = plugins.map(p => p.id) // load all the other plugins - this.otherPlugins = Object.keys(this.pkg.dependencies || {}) + this.allPlugins = Object.keys(this.pkg.dependencies || {}) .concat(Object.keys(this.pkg.devDependencies || {})) .filter(isPlugin) - .filter(id => pluginIds.indexOf(id) === -1) const cliService = plugins.find(p => p.id === '@vue/cli-service') const rootOptions = cliService @@ -112,16 +111,22 @@ module.exports = class Generator { : inferRootOptions(pkg) // apply hooks from all plugins - this.otherPlugins.forEach(id => { + this.allPlugins.forEach(id => { const api = new GeneratorAPI(id, this, {}, rootOptions) const pluginGenerator = loadModule(`${id}/generator`, context) + if (pluginGenerator && pluginGenerator.hooks) { pluginGenerator.hooks(api, {}, rootOptions, pluginIds) } }) - // reset nonAny hooks + // We are doing save/load to make the hook order deterministic + // save "any" hooks + const afterAnyInvokeCbsFromPlugins = this.afterAnyInvokeCbs + + // reset hooks this.afterInvokeCbs = afterInvokeCbs + this.afterAnyInvokeCbs = [] this.postProcessFilesCbs = [] // apply generators from plugins @@ -133,6 +138,9 @@ module.exports = class Generator { apply.hooks(api, options, rootOptions, pluginIds) } }) + + // load "any" hooks + this.afterAnyInvokeCbs = afterAnyInvokeCbsFromPlugins } async generate ({ @@ -276,7 +284,7 @@ module.exports = class Generator { hasPlugin (_id, _version) { return [ ...this.plugins.map(p => p.id), - ...this.otherPlugins + ...this.allPlugins ].some(id => { if (!matchesPluginId(_id, id)) { return false diff --git a/packages/@vue/cli/lib/invoke.js b/packages/@vue/cli/lib/invoke.js index 9a226df647..7c4ac836fd 100644 --- a/packages/@vue/cli/lib/invoke.js +++ b/packages/@vue/cli/lib/invoke.js @@ -135,17 +135,11 @@ async function runGenerator (context, plugin, pkg = getPkg(context)) { await pm.install() } - if (afterInvokeCbs.length) { + if (afterInvokeCbs.length || afterAnyInvokeCbs.length) { logWithSpinner('⚓', `Running completion hooks...`) for (const cb of afterInvokeCbs) { await cb() } - stopSpinner() - log() - } - - if (afterAnyInvokeCbs.length) { - logWithSpinner('⚓', `Running completion hooks from other plugins...`) for (const cb of afterAnyInvokeCbs) { await cb() }