diff --git a/docs/dev-guide/ui-api.md b/docs/dev-guide/ui-api.md index 4ee32902fe..a742100d9e 100644 --- a/docs/dev-guide/ui-api.md +++ b/docs/dev-guide/ui-api.md @@ -1274,7 +1274,7 @@ const ROUTER = 'vue-router-add' api.onViewOpen(({ view }) => { if (view.id === 'vue-project-plugins') { - if (!api.hasPlugin('vue-router')) { + if (!api.hasPlugin('router')) { api.addSuggestion({ id: ROUTER, type: 'action', @@ -1282,7 +1282,7 @@ api.onViewOpen(({ view }) => { message: 'org.vue.cli-service.suggestions.vue-router-add.message', link: 'https://router.vuejs.org/', async handler () { - await install(api, 'vue-router') + await install(api, 'router') } }) } diff --git a/docs/guide/plugins-and-presets.md b/docs/guide/plugins-and-presets.md index 0ebee9f122..ae0de69711 100644 --- a/docs/guide/plugins-and-presets.md +++ b/docs/guide/plugins-and-presets.md @@ -52,13 +52,6 @@ You can pass generator options to the installed plugin (this will skip the promp vue add @vue/eslint --config airbnb --lintOn save ``` -`vue-router` and `vuex` are special cases - they do not have their own plugins, but you can add them nonetheless: - -``` bash -vue add router -vue add vuex -``` - If a plugin is already installed, you can skip the installation and only invoke its generator with the `vue invoke` command. The command takes the same arguments as `vue add`. ::: tip @@ -112,7 +105,6 @@ Here's an example preset: ``` json { "useConfigFiles": true, - "router": true, "vuex": true, "cssPreprocessor": "sass", "plugins": { @@ -120,7 +112,8 @@ Here's an example preset: "@vue/cli-plugin-eslint": { "config": "airbnb", "lintOn": ["save", "commit"] - } + }, + "@vue/cli-plugin-router": {} } } ``` diff --git a/docs/ru/dev-guide/ui-api.md b/docs/ru/dev-guide/ui-api.md index 6560eb3c28..91d657e756 100644 --- a/docs/ru/dev-guide/ui-api.md +++ b/docs/ru/dev-guide/ui-api.md @@ -1274,7 +1274,7 @@ const ROUTER = 'vue-router-add' api.onViewOpen(({ view }) => { if (view.id === 'vue-project-plugins') { - if (!api.hasPlugin('vue-router')) { + if (!api.hasPlugin('router')) { api.addSuggestion({ id: ROUTER, type: 'action', @@ -1282,7 +1282,7 @@ api.onViewOpen(({ view }) => { message: 'org.vue.cli-service.suggestions.vue-router-add.message', link: 'https://router.vuejs.org/', async handler () { - await install(api, 'vue-router') + await install(api, 'router') } }) } diff --git a/docs/ru/guide/plugins-and-presets.md b/docs/ru/guide/plugins-and-presets.md index 2233469f7a..7bd8b63618 100644 --- a/docs/ru/guide/plugins-and-presets.md +++ b/docs/ru/guide/plugins-and-presets.md @@ -52,13 +52,6 @@ vue add @foo/bar vue add @vue/eslint --config airbnb --lintOn save ``` -Добавление `vue-router` и `vuex` — особый случай, у них нет собственных плагинов, но вы тем не менее можете их добавить: - -``` bash -vue add router -vue add vuex -``` - Если плагин уже установлен, вы можете пропустить установку и только вызвать его генератор с помощью команды `vue invoke`. Команда принимает такие же аргументы, как и `vue add`. ::: tip Совет @@ -112,7 +105,6 @@ vue add vuex ``` json { "useConfigFiles": true, - "router": true, "vuex": true, "cssPreprocessor": "sass", "plugins": { @@ -120,7 +112,8 @@ vue add vuex "@vue/cli-plugin-eslint": { "config": "airbnb", "lintOn": ["save", "commit"] - } + }, + "@vue/cli-plugin-router": {} } } ``` diff --git a/docs/zh/guide/plugins-and-presets.md b/docs/zh/guide/plugins-and-presets.md index 48b0edc0f7..db6b8754c5 100644 --- a/docs/zh/guide/plugins-and-presets.md +++ b/docs/zh/guide/plugins-and-presets.md @@ -52,13 +52,6 @@ vue add @foo/bar vue add @vue/eslint --config airbnb --lintOn save ``` -`vue-router` 和 `vuex` 的情况比较特殊——它们并没有自己的插件,但是你仍然可以这样添加它们: - -``` bash -vue add router -vue add vuex -``` - 如果一个插件已经被安装,你可以使用 `vue invoke` 命令跳过安装过程,只调用它的生成器。这个命令会接受和 `vue add` 相同的参数。 ::: tip 提示 @@ -112,7 +105,6 @@ vue add vuex ``` json { "useConfigFiles": true, - "router": true, "vuex": true, "cssPreprocessor": "sass", "plugins": { @@ -120,7 +112,8 @@ vue add vuex "@vue/cli-plugin-eslint": { "config": "airbnb", "lintOn": ["save", "commit"] - } + }, + "@vue/cli-plugin-router": {} } } ``` diff --git a/packages/@vue/cli-plugin-router/.npmignore b/packages/@vue/cli-plugin-router/.npmignore new file mode 100644 index 0000000000..e0b178a189 --- /dev/null +++ b/packages/@vue/cli-plugin-router/.npmignore @@ -0,0 +1,2 @@ +__tests__ +__mocks__ diff --git a/packages/@vue/cli-plugin-router/README.md b/packages/@vue/cli-plugin-router/README.md new file mode 100644 index 0000000000..05786ca865 --- /dev/null +++ b/packages/@vue/cli-plugin-router/README.md @@ -0,0 +1,9 @@ +# @vue/cli-plugin-router + +> router plugin for vue-cli + +## Installing in an Already Created Project + +``` sh +vue add @vue/router +``` diff --git a/packages/@vue/cli-plugin-router/__tests__/routerGenerator.spec.js b/packages/@vue/cli-plugin-router/__tests__/routerGenerator.spec.js new file mode 100644 index 0000000000..baac1337b6 --- /dev/null +++ b/packages/@vue/cli-plugin-router/__tests__/routerGenerator.spec.js @@ -0,0 +1,64 @@ +const generateWithPlugin = require('@vue/cli-test-utils/generateWithPlugin') + +test('base', async () => { + const { files, pkg } = await generateWithPlugin({ + id: 'router', + apply: require('../generator'), + options: {} + }) + + expect(files['src/router/index.js']).toBeTruthy() + expect(files['src/router/index.js']).not.toMatch('history') + expect(files['src/views/About.vue']).toBeTruthy() + expect(files['src/views/Home.vue']).toBeTruthy() + expect(files['src/App.vue']).toMatch('Home') + expect(files['src/App.vue']).not.toMatch(' - <%_ } _%> -<%_ } else { _%> - -<%_ } _%> -<%_ if (!rootOptions.bare) { _%> -<%_ if (rootOptions.cssPreprocessor !== 'stylus') { _%> +<%_ if (rootOptions.cssPreprocessor !== 'stylus') { _%> @@ -91,19 +48,7 @@ export default { -moz-osx-font-smoothing grayscale text-align center color #2c3e50 - <%_ if (!rootOptions.router) { _%> margin-top 60px - <%_ } _%> -<%_ if (rootOptions.router) { _%> - -#nav - padding 30px - a - font-weight bold - color #2c3e50 - &.router-link-exact-active - color #42b983 -<%_ } _%> <%_ } _%> <%_ } _%> diff --git a/packages/@vue/cli-service/lib/PluginAPI.js b/packages/@vue/cli-service/lib/PluginAPI.js index d3672d4ed1..39ebe76c28 100644 --- a/packages/@vue/cli-service/lib/PluginAPI.js +++ b/packages/@vue/cli-service/lib/PluginAPI.js @@ -64,8 +64,7 @@ class PluginAPI { * @return {boolean} */ hasPlugin (id) { - if (id === 'router') id = 'vue-router' - if (['vue-router', 'vuex'].includes(id)) { + if (['vuex'].includes(id)) { const pkg = this.service.pkg return ((pkg.dependencies && pkg.dependencies[id]) || (pkg.devDependencies && pkg.devDependencies[id])) } diff --git a/packages/@vue/cli-ui/apollo-server/api/PluginApi.js b/packages/@vue/cli-ui/apollo-server/api/PluginApi.js index 4c26ff6c86..e5ecbf2044 100644 --- a/packages/@vue/cli-ui/apollo-server/api/PluginApi.js +++ b/packages/@vue/cli-ui/apollo-server/api/PluginApi.js @@ -397,8 +397,7 @@ class PluginApi { * @param {string} id Plugin id or short id */ hasPlugin (id) { - if (id === 'router') id = 'vue-router' - if (['vue-router', 'vuex'].includes(id)) { + if (['vuex'].includes(id)) { const pkg = folders.readPackage(this.cwd, this.context, true) return ((pkg.dependencies && pkg.dependencies[id]) || (pkg.devDependencies && pkg.devDependencies[id])) } diff --git a/packages/@vue/cli-ui/tests/e2e/specs/g2-plugins.js b/packages/@vue/cli-ui/tests/e2e/specs/g2-plugins.js index ad36b135b2..8bd290edcf 100644 --- a/packages/@vue/cli-ui/tests/e2e/specs/g2-plugins.js +++ b/packages/@vue/cli-ui/tests/e2e/specs/g2-plugins.js @@ -1,7 +1,7 @@ describe('Plugins', () => { it('Should display the plugins', () => { cy.visit('/plugins') - cy.get('.project-plugin-item').should('have.length', 4) + cy.get('.project-plugin-item').should('have.length', 5) }) it('Should add a plugin', () => { @@ -26,6 +26,6 @@ describe('Plugins', () => { .should('be.visible') .should('not.have.class', 'disabled') .click() - cy.get('.project-plugin-item').should('have.length', 4) + cy.get('.project-plugin-item').should('have.length', 5) }) }) diff --git a/packages/@vue/cli-ui/ui-defaults/suggestions.js b/packages/@vue/cli-ui/ui-defaults/suggestions.js index 8378ec3449..ae27af4081 100644 --- a/packages/@vue/cli-ui/ui-defaults/suggestions.js +++ b/packages/@vue/cli-ui/ui-defaults/suggestions.js @@ -8,7 +8,7 @@ const VUE_CONFIG_OPEN = 'org.vue.vue-config-open' module.exports = api => { api.onViewOpen(({ view }) => { if (view.id === 'vue-project-plugins') { - if (!api.hasPlugin('vue-router')) { + if (!api.hasPlugin('router')) { api.addSuggestion({ id: ROUTER, type: 'action', @@ -16,7 +16,7 @@ module.exports = api => { message: 'org.vue.cli-service.suggestions.vue-router-add.message', link: 'https://router.vuejs.org/', async handler () { - await install(api, 'vue-router') + await install(api, 'router') } }) } @@ -73,16 +73,19 @@ async function install (api, id) { progress: -1 }) - const name = id === 'vue-router' ? 'router' : id const context = api.getCwd() let error try { - await invoke.runGenerator(context, { - id: `core:${name}`, - apply: loadModule(`@vue/cli-service/generator/${name}`, context) - }) + if (id === 'router') { + await invoke(id, {}, context) + } else { + await invoke.runGenerator(context, { + id: `core:${id}`, + apply: loadModule(`@vue/cli-service/generator/${id}`, context) + }) + } } catch (e) { error = e } diff --git a/packages/@vue/cli/lib/Creator.js b/packages/@vue/cli/lib/Creator.js index 7782b59cf6..e8db9c0774 100644 --- a/packages/@vue/cli/lib/Creator.js +++ b/packages/@vue/cli/lib/Creator.js @@ -92,10 +92,20 @@ module.exports = class Creator extends EventEmitter { preset.plugins['@vue/cli-service'] = Object.assign({ projectName: name }, preset) + if (cliOptions.bare) { preset.plugins['@vue/cli-service'].bare = true } + // legacy support for router + if (preset.router) { + preset.plugins['@vue/cli-plugin-router'] = {} + + if (preset.routerHistoryMode) { + preset.plugins['@vue/cli-plugin-router'].historyMode = true + } + } + const packageManager = ( cliOptions.packageManager || loadOptions().packageManager || diff --git a/packages/@vue/cli/lib/Generator.js b/packages/@vue/cli/lib/Generator.js index 87386bea07..ecd826a87e 100644 --- a/packages/@vue/cli/lib/Generator.js +++ b/packages/@vue/cli/lib/Generator.js @@ -243,8 +243,7 @@ module.exports = class Generator { } hasPlugin (_id) { - if (_id === 'router') _id = 'vue-router' - if (['vue-router', 'vuex'].includes(_id)) { + if (['vuex'].includes(_id)) { const pkg = this.pkg return ((pkg.dependencies && pkg.dependencies[_id]) || (pkg.devDependencies && pkg.devDependencies[_id])) } diff --git a/packages/@vue/cli/lib/add.js b/packages/@vue/cli/lib/add.js index 848a843cd2..4d8b147921 100644 --- a/packages/@vue/cli/lib/add.js +++ b/packages/@vue/cli/lib/add.js @@ -14,9 +14,6 @@ const { async function add (pluginName, options = {}, context = process.cwd()) { // special internal "plugins" - if (/^(@vue\/)?router$/.test(pluginName)) { - return addRouter(context) - } if (/^(@vue\/)?vuex$/.test(pluginName)) { return addVuex(context) } @@ -41,20 +38,6 @@ async function add (pluginName, options = {}, context = process.cwd()) { } } -async function addRouter (context) { - const inquirer = require('inquirer') - const options = await inquirer.prompt([{ - name: 'routerHistoryMode', - type: 'confirm', - message: `Use history mode for router? ${chalk.yellow(`(Requires proper server setup for index fallback in production)`)}` - }]) - invoke.runGenerator(context, { - id: 'core:router', - apply: loadModule('@vue/cli-service/generator/router', context), - options - }) -} - async function addVuex (context) { invoke.runGenerator(context, { id: 'core:vuex', diff --git a/packages/@vue/cli/lib/options.js b/packages/@vue/cli/lib/options.js index 197414cde1..e2f26e729a 100644 --- a/packages/@vue/cli/lib/options.js +++ b/packages/@vue/cli/lib/options.js @@ -10,6 +10,7 @@ const rcPath = exports.rcPath = getRcPath('.vuerc') const presetSchema = createSchema(joi => joi.object().keys({ bare: joi.boolean(), useConfigFiles: joi.boolean(), + // TODO: Use warn for router once @hapi/joi v16 releases router: joi.boolean(), routerHistoryMode: joi.boolean(), vuex: joi.boolean(), @@ -31,7 +32,6 @@ exports.validatePreset = preset => validate(preset, presetSchema, msg => { }) exports.defaultPreset = { - router: false, vuex: false, useConfigFiles: false, cssPreprocessor: undefined, diff --git a/packages/@vue/cli/lib/promptModules/__tests__/router.spec.js b/packages/@vue/cli/lib/promptModules/__tests__/router.spec.js index 932ebafafa..9f9918177f 100644 --- a/packages/@vue/cli/lib/promptModules/__tests__/router.spec.js +++ b/packages/@vue/cli/lib/promptModules/__tests__/router.spec.js @@ -19,9 +19,11 @@ test('router', async () => { ] const expectedOptions = { - router: true, - routerHistoryMode: true, - plugins: {} + plugins: { + '@vue/cli-plugin-router': { + historyMode: true + } + } } await assertPromptModule( diff --git a/packages/@vue/cli/lib/promptModules/router.js b/packages/@vue/cli/lib/promptModules/router.js index f64b6ec4f6..2cdc1d239c 100644 --- a/packages/@vue/cli/lib/promptModules/router.js +++ b/packages/@vue/cli/lib/promptModules/router.js @@ -9,7 +9,7 @@ module.exports = cli => { }) cli.injectPrompt({ - name: 'routerHistoryMode', + name: 'historyMode', when: answers => answers.features.includes('router'), type: 'confirm', message: `Use history mode for router? ${chalk.yellow(`(Requires proper server setup for index fallback in production)`)}`, @@ -19,8 +19,9 @@ module.exports = cli => { cli.onPromptComplete((answers, options) => { if (answers.features.includes('router')) { - options.router = true - options.routerHistoryMode = answers.routerHistoryMode + options.plugins['@vue/cli-plugin-router'] = { + historyMode: answers.historyMode + } } }) } diff --git a/packages/@vue/cli/lib/util/features.js b/packages/@vue/cli/lib/util/features.js index e64e69df04..916952ba01 100644 --- a/packages/@vue/cli/lib/util/features.js +++ b/packages/@vue/cli/lib/util/features.js @@ -4,7 +4,7 @@ const { toShortPluginId } = require('@vue/cli-shared-utils') exports.getFeatures = (preset) => { const features = [] if (preset.router) { - features.push('vue-router') + features.push('router') } if (preset.vuex) { features.push('vuex')