diff --git a/.eslintignore b/.eslintignore
index ba322b37b4..08f4631091 100644
--- a/.eslintignore
+++ b/.eslintignore
@@ -1,5 +1,6 @@
node_modules
template
+template-vue3
packages/test
temp
entry-wc.js
diff --git a/package.json b/package.json
index cf7a657079..acf5742a47 100644
--- a/package.json
+++ b/package.json
@@ -91,7 +91,6 @@
},
"resolutions": {
"puppeteer": "1.11.0",
- "vue": "^2.6.11",
"vue-template-compiler": "^2.6.11",
"vue-server-renderer": "^2.6.11"
}
diff --git a/packages/@vue/babel-preset-app/README.md b/packages/@vue/babel-preset-app/README.md
index 839a0cc405..aed82da6e5 100644
--- a/packages/@vue/babel-preset-app/README.md
+++ b/packages/@vue/babel-preset-app/README.md
@@ -88,7 +88,7 @@ Use this option when you have 3rd party dependencies that are not processed by B
- Default: `true`.
-Set to `false` to disable JSX support. Or you can toggle [@vue/babel-preset-jsx](https://github.com/vuejs/jsx/tree/dev/packages/babel-preset-jsx) features here.
+Set to `false` to disable JSX support. Or you can toggle [@vue/babel-preset-jsx](https://github.com/vuejs/jsx/tree/dev/packages/babel-preset-jsx) (or [@ant-design-vue/babel-plugin-jsx](https://github.com/vueComponent/jsx) for Vue 3 projects) features here.
### loose
diff --git a/packages/@vue/babel-preset-app/index.js b/packages/@vue/babel-preset-app/index.js
index ab8722f75c..79fa8eadd8 100644
--- a/packages/@vue/babel-preset-app/index.js
+++ b/packages/@vue/babel-preset-app/index.js
@@ -112,7 +112,22 @@ module.exports = (context, options = {}) => {
// JSX
if (options.jsx !== false) {
- presets.push([require('@vue/babel-preset-jsx'), typeof options.jsx === 'object' ? options.jsx : {}])
+ let jsxOptions = {}
+ if (typeof options.jsx === 'object') {
+ jsxOptions = options.jsx
+ }
+
+ let vueVersion = 2
+ try {
+ const Vue = require('vue')
+ vueVersion = semver.major(Vue.version)
+ } catch (e) {}
+
+ if (vueVersion === 2) {
+ presets.push([require('@vue/babel-preset-jsx'), jsxOptions])
+ } else if (vueVersion === 3) {
+ plugins.push([require('@ant-design-vue/babel-plugin-jsx'), jsxOptions])
+ }
}
const runtimePath = path.dirname(require.resolve('@babel/runtime/package.json'))
diff --git a/packages/@vue/babel-preset-app/package.json b/packages/@vue/babel-preset-app/package.json
index 7cd1999597..22003ebc1d 100644
--- a/packages/@vue/babel-preset-app/package.json
+++ b/packages/@vue/babel-preset-app/package.json
@@ -22,6 +22,7 @@
},
"homepage": "https://github.com/vuejs/vue-cli/tree/dev/packages/@vue/babel-preset-app#readme",
"dependencies": {
+ "@ant-design-vue/babel-plugin-jsx": "^1.0.0-0",
"@babel/core": "^7.9.6",
"@babel/helper-compilation-targets": "^7.9.6",
"@babel/helper-module-imports": "^7.8.3",
@@ -40,11 +41,15 @@
},
"peerDependencies": {
"@babel/core": "*",
- "core-js": "^3"
+ "core-js": "^3",
+ "vue": "^2 || ^3.0.0-0"
},
"peerDependenciesMeta": {
"core-js": {
"optional": true
+ },
+ "vue": {
+ "optional": true
}
}
}
diff --git a/packages/@vue/cli-plugin-babel/__tests__/babelMigrator.spec.js b/packages/@vue/cli-plugin-babel/__tests__/babelMigrator.spec.js
index 42414a9c9a..697aa21d07 100644
--- a/packages/@vue/cli-plugin-babel/__tests__/babelMigrator.spec.js
+++ b/packages/@vue/cli-plugin-babel/__tests__/babelMigrator.spec.js
@@ -2,9 +2,6 @@ const create = require('@vue/cli-test-utils/createUpgradableProject')
const { logs } = require('@vue/cli-shared-utils')
jest.setTimeout(300000)
-beforeEach(() => {
- process.env.VUE_CLI_TEST_DO_INSTALL_PLUGIN = true
-})
test('upgrade: plugin-babel v3.5', async () => {
const project = await create('plugin-babel-legacy', {
diff --git a/packages/@vue/cli-plugin-babel/__tests__/babel-runtime.spec.js b/packages/@vue/cli-plugin-babel/__tests__/babelRuntime.spec.js
similarity index 100%
rename from packages/@vue/cli-plugin-babel/__tests__/babel-runtime.spec.js
rename to packages/@vue/cli-plugin-babel/__tests__/babelRuntime.spec.js
diff --git a/packages/@vue/cli-plugin-babel/__tests__/transpileDependencies.spec.js b/packages/@vue/cli-plugin-babel/__tests__/transpileDependencies.spec.js
index fcd392038d..3f3dba1ba1 100644
--- a/packages/@vue/cli-plugin-babel/__tests__/transpileDependencies.spec.js
+++ b/packages/@vue/cli-plugin-babel/__tests__/transpileDependencies.spec.js
@@ -63,6 +63,11 @@ beforeAll(async () => {
)
})
+afterAll(async () => {
+ // avoid the non-existent made-up deps interfere with other tests
+ await project.rm('package.json')
+})
+
test('dep from node_modules should not been transpiled', async () => {
await project.run('vue-cli-service build')
expect(await readVendorFile()).toMatch('() => "__TEST__"')
diff --git a/packages/@vue/cli-plugin-eslint/__tests__/eslintMigrator.spec.js b/packages/@vue/cli-plugin-eslint/__tests__/eslintMigrator.spec.js
index 6cb2877a47..de39574006 100644
--- a/packages/@vue/cli-plugin-eslint/__tests__/eslintMigrator.spec.js
+++ b/packages/@vue/cli-plugin-eslint/__tests__/eslintMigrator.spec.js
@@ -1,10 +1,6 @@
jest.setTimeout(300000)
jest.mock('inquirer')
-beforeEach(() => {
- process.env.VUE_CLI_TEST_DO_INSTALL_PLUGIN = true
-})
-
const create = require('@vue/cli-test-utils/createUpgradableProject')
const { expectPrompts } = require('inquirer')
diff --git a/packages/@vue/cli-plugin-eslint/__tests__/eslintVue3.spec.js b/packages/@vue/cli-plugin-eslint/__tests__/eslintVue3.spec.js
new file mode 100644
index 0000000000..cefbd9eb68
--- /dev/null
+++ b/packages/@vue/cli-plugin-eslint/__tests__/eslintVue3.spec.js
@@ -0,0 +1,53 @@
+jest.setTimeout(300000)
+
+const generateWithPlugin = require('@vue/cli-test-utils/generateWithPlugin')
+const createOutside = require('@vue/cli-test-utils/createUpgradableProject')
+
+test('Vue 3 base', async () => {
+ const { pkg } = await generateWithPlugin([
+ {
+ id: '@vue/cli-service',
+ apply: require('@vue/cli-service/generator'),
+ options: {
+ vueVersion: '3'
+ }
+ },
+ {
+ id: '@vue/cli-plugineslint',
+ apply: require('../generator'),
+ options: {}
+ }
+ ])
+
+ expect(pkg.scripts.lint).toBeTruthy()
+ expect(pkg.eslintConfig.extends).toEqual([
+ 'plugin:vue/vue3-essential', 'eslint:recommended'
+ ])
+})
+
+test('Should allow fragments in Vue 3 projects', async () => {
+ const { write, run } = await createOutside('eslint-vue3-fragment', {
+ vueVersion: '3',
+ plugins: {
+ '@vue/cli-plugin-eslint': {}
+ }
+ })
+ await write('src/App.vue', `
+
+
+
+
+
+`)
+
+ await run('vue-cli-service lint')
+})
diff --git a/packages/@vue/cli-plugin-eslint/eslintDeps.js b/packages/@vue/cli-plugin-eslint/eslintDeps.js
index 057908e885..ca5ce62387 100644
--- a/packages/@vue/cli-plugin-eslint/eslintDeps.js
+++ b/packages/@vue/cli-plugin-eslint/eslintDeps.js
@@ -28,9 +28,13 @@ const DEPS_MAP = {
exports.DEPS_MAP = DEPS_MAP
-exports.getDeps = function (api, preset) {
+exports.getDeps = function (api, preset, rootOptions = {}) {
const deps = Object.assign({}, DEPS_MAP.base, DEPS_MAP[preset])
+ if (rootOptions.vueVersion === '3') {
+ Object.assign(deps, { 'eslint-plugin-vue': '^7.0.0-0' })
+ }
+
if (api.hasPlugin('typescript')) {
Object.assign(deps, DEPS_MAP.typescript)
}
diff --git a/packages/@vue/cli-plugin-eslint/eslintOptions.js b/packages/@vue/cli-plugin-eslint/eslintOptions.js
index b3bce290cc..c6aea69c3b 100644
--- a/packages/@vue/cli-plugin-eslint/eslintOptions.js
+++ b/packages/@vue/cli-plugin-eslint/eslintOptions.js
@@ -1,4 +1,4 @@
-exports.config = (api, preset) => {
+exports.config = (api, preset, rootOptions = {}) => {
const config = {
root: true,
env: { node: true },
@@ -40,6 +40,15 @@ exports.config = (api, preset) => {
}
}
+ if (rootOptions.vueVersion === '3') {
+ const updateConfig = cfg =>
+ cfg.replace(
+ /plugin:vue\/(essential|recommended|strongly-recommended)/gi,
+ 'plugin:vue/vue3-$1'
+ )
+ config.extends = config.extends.map(updateConfig)
+ }
+
return config
}
diff --git a/packages/@vue/cli-plugin-eslint/generator/index.js b/packages/@vue/cli-plugin-eslint/generator/index.js
index 042c4fdf56..d9d3694256 100644
--- a/packages/@vue/cli-plugin-eslint/generator/index.js
+++ b/packages/@vue/cli-plugin-eslint/generator/index.js
@@ -1,9 +1,9 @@
const fs = require('fs')
const path = require('path')
-module.exports = (api, { config, lintOn = [] }, _, invoking) => {
- const eslintConfig = require('../eslintOptions').config(api, config)
- const devDependencies = require('../eslintDeps').getDeps(api, config)
+module.exports = (api, { config, lintOn = [] }, rootOptions, invoking) => {
+ const eslintConfig = require('../eslintOptions').config(api, config, rootOptions)
+ const devDependencies = require('../eslintDeps').getDeps(api, config, rootOptions)
const pkg = {
scripts: {
diff --git a/packages/@vue/cli-plugin-router/__tests__/routerGenerator.spec.js b/packages/@vue/cli-plugin-router/__tests__/routerGenerator.spec.js
index baac1337b6..2cf76c8207 100644
--- a/packages/@vue/cli-plugin-router/__tests__/routerGenerator.spec.js
+++ b/packages/@vue/cli-plugin-router/__tests__/routerGenerator.spec.js
@@ -62,3 +62,77 @@ test('use with Babel', async () => {
expect(pkg.dependencies).toHaveProperty('vue-router')
})
+
+test('use with Vue 3', async () => {
+ const { files, pkg } = await generateWithPlugin([
+ {
+ id: '@vue/cli-service',
+ apply: require('@vue/cli-service/generator'),
+ options: {
+ vueVersion: '3'
+ }
+ },
+ {
+ id: '@vue/cli-plugin-router',
+ apply: require('../generator'),
+ options: {}
+ }
+ ])
+
+ expect(files['src/router/index.js']).toBeTruthy()
+ expect(files['src/router/index.js']).toMatch('createRouter')
+ expect(files['src/router/index.js']).toMatch('history: createWebHashHistory()')
+
+ expect(files['src/main.js']).toMatch('.use(router)')
+
+ expect(pkg.dependencies).toHaveProperty('vue-router')
+ expect(pkg.dependencies['vue-router']).toMatch('^4')
+})
+
+test('Vue 3 + History Mode', async () => {
+ const { files } = await generateWithPlugin([
+ {
+ id: '@vue/cli-service',
+ apply: require('@vue/cli-service/generator'),
+ options: {
+ vueVersion: '3'
+ }
+ },
+ {
+ id: '@vue/cli-plugin-router',
+ apply: require('../generator'),
+ options: {
+ historyMode: true
+ }
+ }
+ ])
+
+ expect(files['src/router/index.js']).toMatch(/import {.*createWebHistory/)
+ expect(files['src/router/index.js']).toMatch('history: createWebHistory(process.env.BASE_URL)')
+})
+
+test('Vue 3 + TypeScript', async () => {
+ const { files } = await generateWithPlugin([
+ {
+ id: '@vue/cli-service',
+ apply: require('@vue/cli-service/generator'),
+ options: {
+ vueVersion: '3'
+ }
+ },
+ {
+ id: '@vue/cli-plugin-router',
+ apply: require('../generator'),
+ options: {}
+ },
+ {
+ id: '@vue/cli-plugin-typescript',
+ apply: require('@vue/cli-plugin-typescript/generator'),
+ options: {}
+ }
+ ])
+
+ expect(files['src/router/index.ts']).toBeTruthy()
+ expect(files['src/router/index.ts']).toMatch(/import {.*RouteRecordRaw/)
+ expect(files['src/router/index.ts']).toMatch('const routes: Array =')
+})
diff --git a/packages/@vue/cli-plugin-router/generator/index.js b/packages/@vue/cli-plugin-router/generator/index.js
index eecf8c1a5b..7f7bfb7ae7 100644
--- a/packages/@vue/cli-plugin-router/generator/index.js
+++ b/packages/@vue/cli-plugin-router/generator/index.js
@@ -1,12 +1,24 @@
-module.exports = (api, options = {}) => {
+module.exports = (api, options = {}, rootOptions = {}) => {
+ const isVue3 = (rootOptions.vueVersion === '3')
+
api.injectImports(api.entryFile, `import router from './router'`)
- api.injectRootOptions(api.entryFile, `router`)
- api.extendPackage({
- dependencies: {
- 'vue-router': '^3.2.0'
- }
- })
+ if (isVue3) {
+ api.transformScript(api.entryFile, require('./injectUseRouter'))
+ api.extendPackage({
+ dependencies: {
+ 'vue-router': '^4.0.0-0'
+ }
+ })
+ } else {
+ api.injectRootOptions(api.entryFile, `router`)
+
+ api.extendPackage({
+ dependencies: {
+ 'vue-router': '^3.2.0'
+ }
+ })
+ }
api.render('./template', {
historyMode: options.historyMode,
@@ -14,6 +26,14 @@ module.exports = (api, options = {}) => {
hasTypeScript: api.hasPlugin('typescript')
})
+ if (isVue3) {
+ api.render('./template-vue3', {
+ historyMode: options.historyMode,
+ doesCompile: api.hasPlugin('babel') || api.hasPlugin('typescript'),
+ hasTypeScript: api.hasPlugin('typescript')
+ })
+ }
+
if (api.invoking) {
if (api.hasPlugin('typescript')) {
/* eslint-disable-next-line node/no-extraneous-require */
diff --git a/packages/@vue/cli-plugin-router/generator/injectUseRouter.js b/packages/@vue/cli-plugin-router/generator/injectUseRouter.js
new file mode 100644
index 0000000000..5231512de2
--- /dev/null
+++ b/packages/@vue/cli-plugin-router/generator/injectUseRouter.js
@@ -0,0 +1,29 @@
+module.exports = (file, api) => {
+ const j = api.jscodeshift
+ const root = j(file.source)
+
+ const appRoots = root.find(j.CallExpression, (node) => {
+ if (j.Identifier.check(node.callee) && node.callee.name === 'createApp') {
+ return true
+ }
+
+ if (
+ j.MemberExpression.check(node.callee) &&
+ j.Identifier.check(node.callee.object) &&
+ node.callee.object.name === 'Vue' &&
+ j.Identifier.check(node.callee.property) &&
+ node.callee.property.name === 'createApp'
+ ) {
+ return true
+ }
+ })
+
+ appRoots.replaceWith(({ node: createAppCall }) => {
+ return j.callExpression(
+ j.memberExpression(createAppCall, j.identifier('use')),
+ [j.identifier('router')]
+ )
+ })
+
+ return root.toSource()
+}
diff --git a/packages/@vue/cli-plugin-router/generator/template-vue3/src/router/index.js b/packages/@vue/cli-plugin-router/generator/template-vue3/src/router/index.js
new file mode 100644
index 0000000000..8678e20202
--- /dev/null
+++ b/packages/@vue/cli-plugin-router/generator/template-vue3/src/router/index.js
@@ -0,0 +1,45 @@
+import { createRouter<%
+ if (historyMode) {
+ %>, createWebHistory<%
+ } else {
+ %>, createWebHashHistory<%
+ }
+
+ if (hasTypeScript) {
+ %>, RouteRecordRaw<%
+ }
+ %> } from 'vue-router'
+import Home from '../views/Home.vue'
+
+const routes<% if (hasTypeScript) { %>: Array<% } %> = [
+ {
+ path: '/',
+ name: 'Home',
+ component: Home
+ },
+ {
+ path: '/about',
+ name: 'About',
+ // route level code-splitting
+ // this generates a separate chunk (about.[hash].js) for this route
+ // which is lazy-loaded when the route is visited.
+ <%_ if (doesCompile) { _%>
+ component: () => import(/* webpackChunkName: "about" */ '../views/About.vue')
+ <%_ } else { _%>
+ component: function () {
+ return import(/* webpackChunkName: "about" */ '../views/About.vue')
+ }
+ <%_ } _%>
+ }
+]
+
+const router = createRouter({
+ <%_ if (historyMode) { _%>
+ history: createWebHistory(process.env.BASE_URL),
+ <%_ } else { _%>
+ history: createWebHashHistory(),
+ <%_ } _%>
+ routes
+})
+
+export default router
diff --git a/packages/@vue/cli-plugin-typescript/__tests__/tsPlugin.helper.js b/packages/@vue/cli-plugin-typescript/__tests__/tsPlugin.helper.js
index 51a5581c33..4584b8862a 100644
--- a/packages/@vue/cli-plugin-typescript/__tests__/tsPlugin.helper.js
+++ b/packages/@vue/cli-plugin-typescript/__tests__/tsPlugin.helper.js
@@ -4,15 +4,21 @@ const path = require('path')
const portfinder = require('portfinder')
const createServer = require('@vue/cli-test-utils/createServer')
const create = require('@vue/cli-test-utils/createTestProject')
+const createOutside = require('@vue/cli-test-utils/createUpgradableProject')
const serve = require('@vue/cli-test-utils/serveWithPuppeteer')
const launchPuppeteer = require('@vue/cli-test-utils/launchPuppeteer')
-exports.assertServe = async (name, options) => {
+exports.assertServe = async (name, options, outside = false) => {
test('serve', async () => {
- const project = await create(name, options)
+ let project
+ if (outside) {
+ project = await createOutside(name, options)
+ } else {
+ project = await create(name, options)
+ }
await serve(
- () => project.run('vue-cli-service serve'),
+ () => project.run('yarn serve'),
async ({ page, nextUpdate, helpers }) => {
const msg = `Welcome to Your Vue.js + TypeScript App`
expect(await helpers.getText('h1')).toMatch(msg)
@@ -40,12 +46,17 @@ exports.assertServe = async (name, options) => {
})
}
-exports.assertBuild = async (name, options, customAssert) => {
+exports.assertBuild = async (name, options, customAssert, outside = false) => {
let browser, server, page
test('build', async () => {
- const project = await create(name, options)
+ let project
+ if (outside) {
+ project = await createOutside(name, options)
+ } else {
+ project = await create(name, options)
+ }
- const { stdout } = await project.run('vue-cli-service build')
+ const { stdout } = await project.run('yarn build')
expect(stdout).toMatch('Build complete.')
const port = await portfinder.getPortPromise()
diff --git a/packages/@vue/cli-plugin-typescript/__tests__/tsPluginVue3.spec.js b/packages/@vue/cli-plugin-typescript/__tests__/tsPluginVue3.spec.js
new file mode 100644
index 0000000000..b89dde464f
--- /dev/null
+++ b/packages/@vue/cli-plugin-typescript/__tests__/tsPluginVue3.spec.js
@@ -0,0 +1,13 @@
+jest.setTimeout(30000)
+
+const { assertServe, assertBuild } = require('./tsPlugin.helper')
+
+const options = {
+ vueVersion: '3',
+ plugins: {
+ '@vue/cli-plugin-typescript': {}
+ }
+}
+
+assertServe('ts-vue-3-serve', options, true)
+assertBuild('ts-vue-3-build', options, undefined, true)
diff --git a/packages/@vue/cli-plugin-typescript/generator/index.js b/packages/@vue/cli-plugin-typescript/generator/index.js
index 1e579b77e4..d9257efe47 100644
--- a/packages/@vue/cli-plugin-typescript/generator/index.js
+++ b/packages/@vue/cli-plugin-typescript/generator/index.js
@@ -6,10 +6,11 @@ module.exports = (api, {
lintOn = [],
convertJsToTs,
allowJs
-}, _, invoking) => {
+}, rootOptions, invoking) => {
if (typeof lintOn === 'string') {
lintOn = lintOn.split(',')
}
+ const isVue3 = rootOptions && rootOptions.vueVersion === '3'
api.extendPackage({
devDependencies: {
@@ -18,12 +19,20 @@ module.exports = (api, {
})
if (classComponent) {
- api.extendPackage({
- dependencies: {
- 'vue-class-component': pluginDevDeps['vue-class-component'],
- 'vue-property-decorator': pluginDevDeps['vue-property-decorator']
- }
- })
+ if (isVue3) {
+ api.extendPackage({
+ dependencies: {
+ 'vue-class-component': '^8.0.0-0'
+ }
+ })
+ } else {
+ api.extendPackage({
+ dependencies: {
+ 'vue-class-component': pluginDevDeps['vue-class-component'],
+ 'vue-property-decorator': pluginDevDeps['vue-property-decorator']
+ }
+ })
+ }
}
if (tsLint) {
@@ -81,10 +90,17 @@ module.exports = (api, {
}
api.render('./template', {
- isTest: process.env.VUE_CLI_TEST || process.env.VUE_CLI_DEBUG,
hasMocha: api.hasPlugin('unit-mocha'),
hasJest: api.hasPlugin('unit-jest')
})
+ if (isVue3) {
+ api.render('./template-vue3')
+
+ // In Vue 3, TSX interface is defined in https://github.com/vuejs/vue-next/blob/master/packages/runtime-dom/types/jsx.d.ts
+ // So no need to manually add a shim.
+ api.render((files) => delete files['src/shims-tsx.d.ts'])
+ }
+
require('./convert')(api, { tsLint, convertJsToTs })
}
diff --git a/packages/@vue/cli-plugin-typescript/generator/template-vue3/src/App.vue b/packages/@vue/cli-plugin-typescript/generator/template-vue3/src/App.vue
new file mode 100644
index 0000000000..1d7b8ac41f
--- /dev/null
+++ b/packages/@vue/cli-plugin-typescript/generator/template-vue3/src/App.vue
@@ -0,0 +1,36 @@
+---
+extend: '@vue/cli-service/generator/template/src/App.vue'
+replace:
+ - !!js/regexp /Welcome to Your Vue\.js App/g
+ - !!js/regexp /
+<%# END_REPLACE %>
diff --git a/packages/@vue/cli-plugin-typescript/generator/template-vue3/src/components/HelloWorld.vue b/packages/@vue/cli-plugin-typescript/generator/template-vue3/src/components/HelloWorld.vue
new file mode 100644
index 0000000000..49f4011c10
--- /dev/null
+++ b/packages/@vue/cli-plugin-typescript/generator/template-vue3/src/components/HelloWorld.vue
@@ -0,0 +1,27 @@
+---
+extend: '@vue/cli-service/generator/template/src/components/HelloWorld.vue'
+replace: !!js/regexp /
diff --git a/packages/@vue/cli-plugin-typescript/generator/template-vue3/src/shims-vue.d.ts b/packages/@vue/cli-plugin-typescript/generator/template-vue3/src/shims-vue.d.ts
new file mode 100644
index 0000000000..32a1b5cd40
--- /dev/null
+++ b/packages/@vue/cli-plugin-typescript/generator/template-vue3/src/shims-vue.d.ts
@@ -0,0 +1,5 @@
+declare module '*.vue' {
+ import { defineComponent } from 'vue'
+ const component: ReturnType
+ export default component
+}
diff --git a/packages/@vue/cli-plugin-typescript/generator/template-vue3/src/views/Home.vue b/packages/@vue/cli-plugin-typescript/generator/template-vue3/src/views/Home.vue
new file mode 100644
index 0000000000..10729a6c17
--- /dev/null
+++ b/packages/@vue/cli-plugin-typescript/generator/template-vue3/src/views/Home.vue
@@ -0,0 +1,37 @@
+---
+extend: '@vue/cli-plugin-router/generator/template/src/views/Home.vue'
+when: "rootOptions.plugins && rootOptions.plugins['@vue/cli-plugin-router']"
+replace:
+ - !!js/regexp /Welcome to Your Vue\.js App/
+ - !!js/regexp /
+<%# END_REPLACE %>
diff --git a/packages/@vue/cli-plugin-unit-jest/__tests__/jestPlugin.spec.js b/packages/@vue/cli-plugin-unit-jest/__tests__/jestPlugin.spec.js
index 50548e7d42..618914c269 100644
--- a/packages/@vue/cli-plugin-unit-jest/__tests__/jestPlugin.spec.js
+++ b/packages/@vue/cli-plugin-unit-jest/__tests__/jestPlugin.spec.js
@@ -1,6 +1,7 @@
-jest.setTimeout(20000)
+jest.setTimeout(300000)
const create = require('@vue/cli-test-utils/createTestProject')
+const createOutside = require('@vue/cli-test-utils/createUpgradableProject')
test('should work', async () => {
const project = await create('unit-jest', {
@@ -125,3 +126,16 @@ test('should correctly configured eslint', async () => {
})
await project.run(`vue-cli-service lint`)
})
+
+test('should work with Vue 3', async () => {
+ const project = await createOutside('unit-jest-vue-3', {
+ vueVersion: '3',
+ plugins: {
+ '@vue/cli-plugin-babel': {},
+ '@vue/cli-plugin-unit-jest': {}
+ }
+ })
+ const pkg = JSON.parse(await project.read('package.json'))
+ expect(pkg.devDependencies['@vue/test-utils']).toMatch('^2')
+ await project.run(`vue-cli-service test:unit`)
+})
diff --git a/packages/@vue/cli-plugin-unit-jest/generator/index.js b/packages/@vue/cli-plugin-unit-jest/generator/index.js
index be9dfd94ab..220189b9a9 100644
--- a/packages/@vue/cli-plugin-unit-jest/generator/index.js
+++ b/packages/@vue/cli-plugin-unit-jest/generator/index.js
@@ -1,5 +1,8 @@
-module.exports = (api, _, __, invoking) => {
+module.exports = (api, options, rootOptions, invoking) => {
+ const isVue3 = rootOptions && rootOptions.vueVersion === '3'
+
api.render('./template', {
+ isVue3,
hasTS: api.hasPlugin('typescript')
})
@@ -8,7 +11,7 @@ module.exports = (api, _, __, invoking) => {
'test:unit': 'vue-cli-service test:unit'
},
devDependencies: {
- '@vue/test-utils': '^1.0.3'
+ '@vue/test-utils': isVue3 ? '^2.0.0-0' : '^1.0.3'
},
jest: {
preset: api.hasPlugin('babel')
@@ -17,6 +20,21 @@ module.exports = (api, _, __, invoking) => {
}
})
+ if (isVue3) {
+ api.extendPackage({
+ devDependencies: {
+ 'vue-jest': '^5.0.0-0',
+ // vue-jest 5.0.0-alpha.1 requires typescript to be present
+ 'typescript': '~3.9.3'
+ },
+ jest: {
+ transform: {
+ '^.+\\.vue$': 'vue-jest'
+ }
+ }
+ })
+ }
+
if (api.hasPlugin('eslint')) {
applyESLint(api)
}
diff --git a/packages/@vue/cli-plugin-unit-jest/generator/template/tests/unit/example.spec.js b/packages/@vue/cli-plugin-unit-jest/generator/template/tests/unit/example.spec.js
index aa88537261..3851de59a3 100644
--- a/packages/@vue/cli-plugin-unit-jest/generator/template/tests/unit/example.spec.js
+++ b/packages/@vue/cli-plugin-unit-jest/generator/template/tests/unit/example.spec.js
@@ -7,7 +7,11 @@ describe('HelloWorld.vue', () => {
it('renders props.msg when passed', () => {
const msg = 'new message'
const wrapper = shallowMount(HelloWorld, {
+ <%_ if (isVue3) { _%>
+ props: { msg }
+ <%_ } else { _%>
propsData: { msg }
+ <%_ } _%>
})
expect(wrapper.text()).toMatch(msg)
})
diff --git a/packages/@vue/cli-plugin-unit-jest/generator/template/tests/unit/example.spec.ts b/packages/@vue/cli-plugin-unit-jest/generator/template/tests/unit/example.spec.ts
index 5d56d9ae54..962d8ed7b8 100644
--- a/packages/@vue/cli-plugin-unit-jest/generator/template/tests/unit/example.spec.ts
+++ b/packages/@vue/cli-plugin-unit-jest/generator/template/tests/unit/example.spec.ts
@@ -7,7 +7,11 @@ describe('HelloWorld.vue', () => {
it('renders props.msg when passed', () => {
const msg = 'new message'
const wrapper = shallowMount(HelloWorld, {
+ <%_ if (isVue3) { _%>
+ props: { msg }
+ <%_ } else { _%>
propsData: { msg }
+ <%_ } _%>
})
expect(wrapper.text()).toMatch(msg)
})
diff --git a/packages/@vue/cli-plugin-unit-mocha/__tests__/mochaPlugin.spec.js b/packages/@vue/cli-plugin-unit-mocha/__tests__/mochaPlugin.spec.js
index fdca6b364d..2fb088a8bd 100644
--- a/packages/@vue/cli-plugin-unit-mocha/__tests__/mochaPlugin.spec.js
+++ b/packages/@vue/cli-plugin-unit-mocha/__tests__/mochaPlugin.spec.js
@@ -1,6 +1,7 @@
-jest.setTimeout(20000)
+jest.setTimeout(3000000)
const create = require('@vue/cli-test-utils/createTestProject')
+const createOutside = require('@vue/cli-test-utils/createUpgradableProject')
test('should work', async () => {
const project = await create('unit-mocha', {
@@ -11,3 +12,30 @@ test('should work', async () => {
})
await project.run(`vue-cli-service test:unit`)
})
+
+test('should work with Vue 3', async () => {
+ const project = await createOutside('unit-mocha-vue-3', {
+ vueVersion: '3',
+ plugins: {
+ '@vue/cli-plugin-babel': {},
+ '@vue/cli-plugin-unit-mocha': {}
+ }
+ })
+ const pkg = JSON.parse(await project.read('package.json'))
+ expect(pkg.devDependencies['@vue/test-utils']).toMatch('^2')
+ await project.run(`vue-cli-service test:unit`)
+})
+
+test('should work with Vue 3 + TS', async () => {
+ const project = await createOutside('unit-mocha-vue-3', {
+ vueVersion: '3',
+ plugins: {
+ '@vue/cli-plugin-babel': {},
+ '@vue/cli-plugin-typescript': {},
+ '@vue/cli-plugin-unit-mocha': {}
+ }
+ })
+ const pkg = JSON.parse(await project.read('package.json'))
+ expect(pkg.devDependencies['@vue/test-utils']).toMatch('^2')
+ await project.run(`vue-cli-service test:unit`)
+})
diff --git a/packages/@vue/cli-plugin-unit-mocha/generator/index.js b/packages/@vue/cli-plugin-unit-mocha/generator/index.js
index 88fb63dfbf..cadf62d8f8 100644
--- a/packages/@vue/cli-plugin-unit-mocha/generator/index.js
+++ b/packages/@vue/cli-plugin-unit-mocha/generator/index.js
@@ -1,11 +1,14 @@
-module.exports = (api, _, __, invoking) => {
+module.exports = (api, options, rootOptions, invoking) => {
+ const isVue3 = rootOptions && rootOptions.vueVersion === '3'
+
api.render('./template', {
+ isVue3,
hasTS: api.hasPlugin('typescript')
})
api.extendPackage({
devDependencies: {
- '@vue/test-utils': '^1.0.3',
+ '@vue/test-utils': isVue3 ? '^2.0.0-0' : '^1.0.3',
'chai': '^4.1.2'
},
scripts: {
diff --git a/packages/@vue/cli-plugin-unit-mocha/generator/template/tests/unit/example.spec.js b/packages/@vue/cli-plugin-unit-mocha/generator/template/tests/unit/example.spec.js
index 9231226366..12bbb8c152 100644
--- a/packages/@vue/cli-plugin-unit-mocha/generator/template/tests/unit/example.spec.js
+++ b/packages/@vue/cli-plugin-unit-mocha/generator/template/tests/unit/example.spec.js
@@ -8,7 +8,11 @@ describe('HelloWorld.vue', () => {
it('renders props.msg when passed', () => {
const msg = 'new message'
const wrapper = shallowMount(HelloWorld, {
+ <%_ if (isVue3) { _%>
+ props: { msg }
+ <%_ } else { _%>
propsData: { msg }
+ <%_ } _%>
})
expect(wrapper.text()).to.include(msg)
})
diff --git a/packages/@vue/cli-plugin-unit-mocha/generator/template/tests/unit/example.spec.ts b/packages/@vue/cli-plugin-unit-mocha/generator/template/tests/unit/example.spec.ts
index 16bb292f8d..b218903b4b 100644
--- a/packages/@vue/cli-plugin-unit-mocha/generator/template/tests/unit/example.spec.ts
+++ b/packages/@vue/cli-plugin-unit-mocha/generator/template/tests/unit/example.spec.ts
@@ -8,7 +8,11 @@ describe('HelloWorld.vue', () => {
it('renders props.msg when passed', () => {
const msg = 'new message'
const wrapper = shallowMount(HelloWorld, {
+ <%_ if (isVue3) { _%>
+ props: { msg }
+ <%_ } else { _%>
propsData: { msg }
+ <%_ } _%>
})
expect(wrapper.text()).to.include(msg)
})
diff --git a/packages/@vue/cli-plugin-unit-mocha/index.js b/packages/@vue/cli-plugin-unit-mocha/index.js
index aa981d6b60..cfdec0aa5f 100644
--- a/packages/@vue/cli-plugin-unit-mocha/index.js
+++ b/packages/@vue/cli-plugin-unit-mocha/index.js
@@ -6,15 +6,22 @@ module.exports = api => {
devtool: 'inline-cheap-module-source-map'
})
+ const { semver, loadModule } = require('@vue/cli-shared-utils')
+ const vue = loadModule('vue', api.service.context)
+ const isVue3 = (vue && semver.major(vue.version) === 3)
+
// when target === 'node', vue-loader will attempt to generate
// SSR-optimized code. We need to turn that off here.
- webpackConfig.module
+ // the `optimizeSSR` option is only available in vue-loader 15
+ if (!isVue3) {
+ webpackConfig.module
.rule('vue')
.use('vue-loader')
.tap(options => {
options.optimizeSSR = false
return options
})
+ }
}
})
diff --git a/packages/@vue/cli-plugin-vuex/__tests__/vuexGenerator.spec.js b/packages/@vue/cli-plugin-vuex/__tests__/vuexGenerator.spec.js
index f95317c267..f7a81fc047 100644
--- a/packages/@vue/cli-plugin-vuex/__tests__/vuexGenerator.spec.js
+++ b/packages/@vue/cli-plugin-vuex/__tests__/vuexGenerator.spec.js
@@ -12,3 +12,27 @@ test('base', async () => {
expect(pkg.dependencies).toHaveProperty('vuex')
})
+
+test('use with Vue 3', async () => {
+ const { files, pkg } = await generateWithPlugin([
+ {
+ id: '@vue/cli-service',
+ apply: require('@vue/cli-service/generator'),
+ options: {
+ vueVersion: '3'
+ }
+ },
+ {
+ id: 'vuex',
+ apply: require('../generator'),
+ options: {}
+ }
+ ])
+
+ expect(files['src/store/index.js']).toBeTruthy()
+ expect(files['src/store/index.js']).toMatch('import { createStore }')
+ expect(files['src/main.js']).toMatch('.use(store)')
+
+ expect(pkg.dependencies).toHaveProperty('vuex')
+ expect(pkg.dependencies.vuex).toMatch('^4')
+})
diff --git a/packages/@vue/cli-plugin-vuex/generator/index.js b/packages/@vue/cli-plugin-vuex/generator/index.js
index 267a3d9fe9..80cf1ea02f 100644
--- a/packages/@vue/cli-plugin-vuex/generator/index.js
+++ b/packages/@vue/cli-plugin-vuex/generator/index.js
@@ -1,15 +1,25 @@
-module.exports = (api, options = {}) => {
+module.exports = (api, options = {}, rootOptions = {}) => {
api.injectImports(api.entryFile, `import store from './store'`)
- api.injectRootOptions(api.entryFile, `store`)
- api.extendPackage({
- dependencies: {
- vuex: '^3.4.0'
- }
- })
+ if (rootOptions.vueVersion === '3') {
+ api.transformScript(api.entryFile, require('./injectUseStore'))
+ api.extendPackage({
+ dependencies: {
+ vuex: '^4.0.0-0'
+ }
+ })
+ api.render('./template-vue3', {})
+ } else {
+ api.injectRootOptions(api.entryFile, `store`)
- api.render('./template', {
- })
+ api.extendPackage({
+ dependencies: {
+ vuex: '^3.4.0'
+ }
+ })
+
+ api.render('./template', {})
+ }
if (api.invoking && api.hasPlugin('typescript')) {
/* eslint-disable-next-line node/no-extraneous-require */
diff --git a/packages/@vue/cli-plugin-vuex/generator/injectUseStore.js b/packages/@vue/cli-plugin-vuex/generator/injectUseStore.js
new file mode 100644
index 0000000000..41104e9510
--- /dev/null
+++ b/packages/@vue/cli-plugin-vuex/generator/injectUseStore.js
@@ -0,0 +1,29 @@
+module.exports = (file, api) => {
+ const j = api.jscodeshift
+ const root = j(file.source)
+
+ const appRoots = root.find(j.CallExpression, (node) => {
+ if (j.Identifier.check(node.callee) && node.callee.name === 'createApp') {
+ return true
+ }
+
+ if (
+ j.MemberExpression.check(node.callee) &&
+ j.Identifier.check(node.callee.object) &&
+ node.callee.object.name === 'Vue' &&
+ j.Identifier.check(node.callee.property) &&
+ node.callee.property.name === 'createApp'
+ ) {
+ return true
+ }
+ })
+
+ appRoots.replaceWith(({ node: createAppCall }) => {
+ return j.callExpression(
+ j.memberExpression(createAppCall, j.identifier('use')),
+ [j.identifier('store')]
+ )
+ })
+
+ return root.toSource()
+}
diff --git a/packages/@vue/cli-plugin-vuex/generator/template-vue3/src/store/index.js b/packages/@vue/cli-plugin-vuex/generator/template-vue3/src/store/index.js
new file mode 100644
index 0000000000..5f05f19391
--- /dev/null
+++ b/packages/@vue/cli-plugin-vuex/generator/template-vue3/src/store/index.js
@@ -0,0 +1,12 @@
+import { createStore } from 'vuex'
+
+export default createStore({
+ state: {
+ },
+ mutations: {
+ },
+ actions: {
+ },
+ modules: {
+ }
+})
diff --git a/packages/@vue/cli-service/__tests__/generator.spec.js b/packages/@vue/cli-service/__tests__/generator.spec.js
index 47537fc132..52d8bb082a 100644
--- a/packages/@vue/cli-service/__tests__/generator.spec.js
+++ b/packages/@vue/cli-service/__tests__/generator.spec.js
@@ -1,46 +1,49 @@
const generateWithPlugin = require('@vue/cli-test-utils/generateWithPlugin')
-test('sass (default)', async () => {
- const { pkg, files } = await generateWithPlugin([
+function generateWithOptions (options) {
+ return generateWithPlugin([
{
id: '@vue/cli-service',
apply: require('../generator'),
- options: {
- cssPreprocessor: 'sass'
- }
+ options
}
])
+}
+
+test('sass (default)', async () => {
+ const { pkg, files } = await await generateWithOptions({
+ cssPreprocessor: 'sass'
+ })
expect(files['src/App.vue']).toMatch('