From 08352811e08a4136edf8f1c931edc6ddf249fb6b Mon Sep 17 00:00:00 2001 From: Guillaume Chau Date: Fri, 13 Jul 2018 17:39:07 +0200 Subject: [PATCH] feat: local service plugins, closes #1841 --- docs/guide/plugins-and-presets.md | 16 +++++++++++ packages/@vue/cli-service/lib/Service.js | 28 ++++++++++++++++--- packages/@vue/cli-shared-utils/index.js | 1 + .../util => cli-shared-utils/lib}/module.js | 0 packages/@vue/cli-shared-utils/package.json | 1 + .../connectors/configurations.js | 3 +- .../apollo-server/connectors/dependencies.js | 3 +- .../apollo-server/connectors/plugins.js | 6 ++-- .../apollo-server/connectors/projects.js | 3 +- .../@vue/cli-ui/ui-defaults/suggestions.js | 2 +- packages/@vue/cli/lib/Creator.js | 4 +-- packages/@vue/cli/lib/add.js | 5 ++-- packages/@vue/cli/lib/invoke.js | 4 +-- .../@vue/cli/lib/util/configTransforms.js | 2 +- 14 files changed, 58 insertions(+), 20 deletions(-) rename packages/@vue/{cli/lib/util => cli-shared-utils/lib}/module.js (100%) diff --git a/docs/guide/plugins-and-presets.md b/docs/guide/plugins-and-presets.md index 4aab2b8b2..0b68ed343 100644 --- a/docs/guide/plugins-and-presets.md +++ b/docs/guide/plugins-and-presets.md @@ -75,6 +75,22 @@ For example, if you have a `.config/package.json` file: ``` ::: +### Project local plugin + +If you need access to the plugin API in your project and don't want to create a full plugin for it, you can use the `vuePlugins.service` option in your `package.json` file: + +```json +{ + "vuePlugins": { + "service": ["my-commands.js"] + } +} +``` + +Each file will need to export a function taking the plugin API as the first argument. For more information about the plugin API, check out the [Plugin Development Guide](../dev-guide/plugin-dev.md). + +You can also create a `vue-cli-ui.js` file that will behave like a UI plugin. For more information, read the [UI Plugin API](../dev-guide/ui-api.md). + ## Presets A Vue CLI preset is a JSON object that contains pre-defined options and plugins for creating a new project so that the user don't have to go through the prompts to select them. diff --git a/packages/@vue/cli-service/lib/Service.js b/packages/@vue/cli-service/lib/Service.js index bd68526bf..a813b5c2b 100644 --- a/packages/@vue/cli-service/lib/Service.js +++ b/packages/@vue/cli-service/lib/Service.js @@ -8,7 +8,7 @@ const Config = require('webpack-chain') const PluginAPI = require('./PluginAPI') const loadEnv = require('./util/loadEnv') const defaultsDeep = require('lodash.defaultsdeep') -const { warn, error, isPlugin } = require('@vue/cli-shared-utils') +const { warn, error, isPlugin, loadModule } = require('@vue/cli-shared-utils') const { defaults, validate } = require('./options') @@ -22,6 +22,9 @@ module.exports = class Service { this.webpackRawConfigFns = [] this.devServerConfigFns = [] this.commands = {} + // Folder containing the target package.json for plugins + this.pkgContext = context + // package.json containing the plugins this.pkg = this.resolvePkg(pkg) // If there are inline plugins, they will be used instead of those // found in package.json. @@ -42,7 +45,8 @@ module.exports = class Service { } else if (fs.existsSync(path.join(context, 'package.json'))) { const pkg = readPkg.sync({ cwd: context }) if (pkg.vuePlugins && pkg.vuePlugins.resolveFrom) { - return this.resolvePkg(null, path.resolve(context, pkg.vuePlugins.resolveFrom)) + this.pkgContext = path.resolve(context, pkg.vuePlugins.resolveFrom) + return this.resolvePkg(null, this.pkgContext) } return pkg } else { @@ -132,6 +136,8 @@ module.exports = class Service { apply: require(id) }) + let plugins + const builtInPlugins = [ './commands/serve', './commands/build', @@ -146,7 +152,7 @@ module.exports = class Service { ].map(idToPlugin) if (inlinePlugins) { - return useBuiltIn !== false + plugins = useBuiltIn !== false ? builtInPlugins.concat(inlinePlugins) : inlinePlugins } else { @@ -154,8 +160,22 @@ module.exports = class Service { .concat(Object.keys(this.pkg.dependencies || {})) .filter(isPlugin) .map(idToPlugin) - return builtInPlugins.concat(projectPlugins) + plugins = builtInPlugins.concat(projectPlugins) } + + // Local plugins + if (this.pkg.vuePlugins && this.pkg.vuePlugins.service) { + const files = this.pkg.vuePlugins.service + if (!Array.isArray(files)) { + throw new Error(`Invalid type for option 'vuePlugins.service', expected 'array' but got ${typeof files}.`) + } + plugins = plugins.concat(files.map(file => ({ + id: `local:${file}`, + apply: loadModule(file, this.pkgContext) + }))) + } + + return plugins } async run (name, args = {}, rawArgv = []) { diff --git a/packages/@vue/cli-shared-utils/index.js b/packages/@vue/cli-shared-utils/index.js index e895a028b..678ce7f40 100644 --- a/packages/@vue/cli-shared-utils/index.js +++ b/packages/@vue/cli-shared-utils/index.js @@ -3,6 +3,7 @@ 'exit', 'ipc', 'logger', + 'module', 'object', 'openBrowser', 'pluginResolution', diff --git a/packages/@vue/cli/lib/util/module.js b/packages/@vue/cli-shared-utils/lib/module.js similarity index 100% rename from packages/@vue/cli/lib/util/module.js rename to packages/@vue/cli-shared-utils/lib/module.js diff --git a/packages/@vue/cli-shared-utils/package.json b/packages/@vue/cli-shared-utils/package.json index 0f8eb197f..a372d9d51 100644 --- a/packages/@vue/cli-shared-utils/package.json +++ b/packages/@vue/cli-shared-utils/package.json @@ -27,6 +27,7 @@ "ora": "^2.1.0", "request": "^2.87.0", "request-promise-native": "^1.0.5", + "semver": "^5.5.0", "string.prototype.padstart": "^3.0.0" }, "publishConfig": { diff --git a/packages/@vue/cli-ui/apollo-server/connectors/configurations.js b/packages/@vue/cli-ui/apollo-server/connectors/configurations.js index 93aaf0c65..1d1dbca7b 100644 --- a/packages/@vue/cli-ui/apollo-server/connectors/configurations.js +++ b/packages/@vue/cli-ui/apollo-server/connectors/configurations.js @@ -9,9 +9,8 @@ const plugins = require('./plugins') const folders = require('./folders') const prompts = require('./prompts') // Utils -const { get, set, unset } = require('@vue/cli-shared-utils') +const { get, set, unset, loadModule } = require('@vue/cli-shared-utils') const { log } = require('../util/logger') -const { loadModule } = require('@vue/cli/lib/util/module') const extendJSConfig = require('@vue/cli/lib/util/extendJSConfig') const fileTypes = ['js', 'json', 'yaml'] diff --git a/packages/@vue/cli-ui/apollo-server/connectors/dependencies.js b/packages/@vue/cli-ui/apollo-server/connectors/dependencies.js index c6b337c56..3a0b14ab1 100644 --- a/packages/@vue/cli-ui/apollo-server/connectors/dependencies.js +++ b/packages/@vue/cli-ui/apollo-server/connectors/dependencies.js @@ -11,8 +11,7 @@ const logs = require('./logs') // Context const getContext = require('../context') // Utils -const { isPlugin, hasYarn } = require('@vue/cli-shared-utils') -const { resolveModule } = require('@vue/cli/lib/util/module') +const { isPlugin, hasYarn, resolveModule } = require('@vue/cli-shared-utils') const getPackageVersion = require('@vue/cli/lib/util/getPackageVersion') const { progress: installProgress, diff --git a/packages/@vue/cli-ui/apollo-server/connectors/plugins.js b/packages/@vue/cli-ui/apollo-server/connectors/plugins.js index 6bd8b06e0..6f545605f 100644 --- a/packages/@vue/cli-ui/apollo-server/connectors/plugins.js +++ b/packages/@vue/cli-ui/apollo-server/connectors/plugins.js @@ -24,9 +24,11 @@ const PluginApi = require('../api/PluginApi') const { isPlugin, isOfficialPlugin, - getPluginLink + getPluginLink, + resolveModule, + loadModule, + clearModule } = require('@vue/cli-shared-utils') -const { resolveModule, loadModule, clearModule } = require('@vue/cli/lib/util/module') const { progress: installProgress, installPackage, diff --git a/packages/@vue/cli-ui/apollo-server/connectors/projects.js b/packages/@vue/cli-ui/apollo-server/connectors/projects.js index 488ff9cb2..7d721fa21 100644 --- a/packages/@vue/cli-ui/apollo-server/connectors/projects.js +++ b/packages/@vue/cli-ui/apollo-server/connectors/projects.js @@ -5,9 +5,8 @@ const Creator = require('@vue/cli/lib/Creator') const { getPromptModules } = require('@vue/cli/lib/util/createTools') const { getFeatures } = require('@vue/cli/lib/util/features') const { defaults } = require('@vue/cli/lib/options') -const { toShortPluginId } = require('@vue/cli-shared-utils') +const { toShortPluginId, clearModule } = require('@vue/cli-shared-utils') const { progress: installProgress } = require('@vue/cli/lib/util/installDeps') -const { clearModule } = require('@vue/cli/lib/util/module') const parseGitConfig = require('parse-git-config') // Connectors const progress = require('./progress') diff --git a/packages/@vue/cli-ui/ui-defaults/suggestions.js b/packages/@vue/cli-ui/ui-defaults/suggestions.js index c6df58464..91cc8b51d 100644 --- a/packages/@vue/cli-ui/ui-defaults/suggestions.js +++ b/packages/@vue/cli-ui/ui-defaults/suggestions.js @@ -1,4 +1,4 @@ -const { loadModule } = require('@vue/cli/lib/util/module') +const { loadModule } = require('@vue/cli-shared-utils') const invoke = require('@vue/cli/lib/invoke') const ROUTER = 'org.vue.vue-router-add' diff --git a/packages/@vue/cli/lib/Creator.js b/packages/@vue/cli/lib/Creator.js index 1f7ed99fc..988cb1502 100644 --- a/packages/@vue/cli/lib/Creator.js +++ b/packages/@vue/cli/lib/Creator.js @@ -7,7 +7,6 @@ const inquirer = require('inquirer') const Generator = require('./Generator') const cloneDeep = require('lodash.clonedeep') const sortObject = require('./util/sortObject') -const { loadModule } = require('./util/module') const getVersions = require('./util/getVersions') const { installDeps } = require('./util/installDeps') const { clearConsole } = require('./util/clearConsole') @@ -34,7 +33,8 @@ const { hasYarn, logWithSpinner, stopSpinner, - exit + exit, + loadModule } = require('@vue/cli-shared-utils') const isManualMode = answers => answers.preset === '__manual__' diff --git a/packages/@vue/cli/lib/add.js b/packages/@vue/cli/lib/add.js index 08f54b23a..d3a2fd51b 100644 --- a/packages/@vue/cli/lib/add.js +++ b/packages/@vue/cli/lib/add.js @@ -2,13 +2,14 @@ const chalk = require('chalk') const invoke = require('./invoke') const { loadOptions } = require('./options') const { installPackage } = require('./util/installDeps') -const { resolveModule, loadModule } = require('./util/module') const { log, error, hasProjectYarn, stopSpinner, - resolvePluginId + resolvePluginId, + resolveModule, + loadModule } = require('@vue/cli-shared-utils') async function add (pluginName, options = {}, context = process.cwd()) { diff --git a/packages/@vue/cli/lib/invoke.js b/packages/@vue/cli/lib/invoke.js index cf5353269..86c630614 100644 --- a/packages/@vue/cli/lib/invoke.js +++ b/packages/@vue/cli/lib/invoke.js @@ -7,7 +7,6 @@ const inquirer = require('inquirer') const isBinary = require('isbinaryfile') const Generator = require('./Generator') const { loadOptions } = require('./options') -const { loadModule } = require('./util/module') const { installDeps } = require('./util/installDeps') const normalizeFilePaths = require('./util/normalizeFilePaths') const { @@ -17,7 +16,8 @@ const { hasProjectGit, logWithSpinner, stopSpinner, - resolvePluginId + resolvePluginId, + loadModule } = require('@vue/cli-shared-utils') async function readFiles (context) { diff --git a/packages/@vue/cli/lib/util/configTransforms.js b/packages/@vue/cli/lib/util/configTransforms.js index 6ebf8a615..79328292f 100644 --- a/packages/@vue/cli/lib/util/configTransforms.js +++ b/packages/@vue/cli/lib/util/configTransforms.js @@ -1,6 +1,6 @@ const extendJSConfig = require('./extendJSConfig') const stringifyJS = require('./stringifyJS') -const { loadModule } = require('./module') +const { loadModule } = require('@vue/cli-shared-utils') const merge = require('deepmerge') const isObject = val => val && typeof val === 'object'