Skip to content

Commit 77cb1fa

Browse files
committed
feature #746 Added Vue3 support (weaverryan)
This PR was squashed before being merged into the master branch. Discussion ---------- Added Vue3 support Hi! Vue3 is now beta. I think Encore should support Vue2 and Vue 3... at least for awhile. The changes needed to make Webpack work with v3 are not that many: 1) Make sure the user installs `vue/compiler-sfc` instead of `vue-template-compiler` 2) require `VueLoaderPlugin` in a slightly different way 3) Add a slightly different "alias" for vue You can choose the version with a new `version` option to `enableVue()`. Otherwise, it will configure itself correctly if you have vue2 or vue3 installed, or it will (finally) recommend the vue2 install instructions. When v3 comes out, we would change the default recommendation to be v3. But... getting the code (and test suite) to work with both vue2 and vue3 is quite tricky :). Commits ------- 40f8ae8 bumping to use vue 2.5.0 to avoid test inconsistency 5abb25d forcing exact low version 06782ae Merge branch 'master' into vue3-support e51165d correctly clearing logger before asserting 449a0af removing vue3 alias 33d2ac5 fixing bad vue-beta version 24f7652 Upgrading to Webpack 4.22.0 which has a fix for ordering problems 43f66c9 lint fixes 35e508f Forcing vue installation to include @3 or @2 version constraint on yarn add command d49352b changing order of entrypoints files d7903e4 updating test hashes 7d2917e fixing missing return bc182bd fixing lock file 63cfd1a fixing wrong path change in tests 08509da removing vue3 and jsx support for now c344809 Fixing missing-loader for vue-loader 16 85d42cd working around a bug in vue-loader by putting module styles first c9f83bc fixing bad test path 6f83868 updating lock file e702197 hack to test with vue3 and vue2 e376be2 various test fixes 8d07790 WIP Vue3 support
2 parents 2658151 + 40f8ae8 commit 77cb1fa

File tree

34 files changed

+673
-147
lines changed

34 files changed

+673
-147
lines changed

fixtures/vuejs-css-modules/App.vue

Lines changed: 30 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,36 @@
11
<template>
2-
<div id="app" class="red large justified lowercase block" :class="[$css.italic, $scss.bold, $less.underline, $stylus.rtl, $postcss.hidden]"></div>
2+
<div class="red large justified lowercase block" :class="[$css.italic, $scss.bold, $less.underline, $stylus.rtl, $postcss.hidden]"></div>
33
</template>
44

5+
<style module="$css">
6+
.italic {
7+
font-style: italic;
8+
}
9+
</style>
10+
11+
<style lang="scss" module="$scss">
12+
.bold {
13+
font-weight: bold;
14+
}
15+
</style>
16+
17+
<style lang="less" module="$less">
18+
.underline {
19+
text-decoration: underline;
20+
}
21+
</style>
22+
23+
<style lang="styl" module="$stylus">
24+
.rtl
25+
direction: rtl;
26+
</style>
27+
28+
<style lang="postcss" module="$postcss">
29+
.hidden {
30+
visibility: hidden;
31+
}
32+
</style>
33+
534
<style>
635
.red {
736
color: red;
@@ -30,33 +59,3 @@
3059
display: block;
3160
}
3261
</style>
33-
34-
35-
<style module="$css">
36-
.italic {
37-
font-style: italic;
38-
}
39-
</style>
40-
41-
<style lang="scss" module="$scss">
42-
.bold {
43-
font-weight: bold;
44-
}
45-
</style>
46-
47-
<style lang="less" module="$less">
48-
.underline {
49-
text-decoration: underline;
50-
}
51-
</style>
52-
53-
<style lang="styl" module="$stylus">
54-
.rtl
55-
direction: rtl;
56-
</style>
57-
58-
<style lang="postcss" module="$postcss">
59-
.hidden {
60-
visibility: hidden;
61-
}
62-
</style>

fixtures/vuejs-css-modules/main_v3.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
import { createApp } from 'vue';
2+
import App from './App'
3+
4+
createApp(App).mount('#app');
File renamed without changes.
File renamed without changes.

fixtures/vuejs/main_v3.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
import { createApp } from 'vue';
2+
import App from './App'
3+
4+
createApp(App).mount('#app');

fixtures/vuejs3-typescript/App.vue

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
<template>
2+
<div id="app">
3+
<img src="./assets/logo.png">
4+
<hello></hello>
5+
</div>
6+
</template>
7+
8+
<script lang="ts">
9+
import Hello from './components/Hello.vue'
10+
11+
class TestClassSyntax {
12+
13+
}
14+
15+
export default {
16+
name: 'app',
17+
components: {
18+
Hello
19+
}
20+
}
21+
</script>
22+
23+
<style>
24+
#app {
25+
font-family: 'Avenir', Helvetica, Arial, sans-serif;
26+
-webkit-font-smoothing: antialiased;
27+
-moz-osx-font-smoothing: grayscale;
28+
text-align: center;
29+
color: #2c3e50;
30+
margin-top: 60px;
31+
}
32+
</style>
33+
34+
<style lang="scss">
35+
#app {
36+
display: flex;
37+
color: #2c3e90;
38+
}
39+
</style>
40+
41+
<style lang="less">
42+
#app {
43+
margin-top: 40px;
44+
}
45+
</style>
6.69 KB
Loading
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
<template>
2+
<div class="hello">
3+
<h1>{{ msg }}</h1>
4+
<h2>Essential Links</h2>
5+
<ul>
6+
<li><a href="https://vuejs.org" target="_blank">Core Docs</a></li>
7+
<li><a href="https://forum.vuejs.org" target="_blank">Forum</a></li>
8+
<li><a href="https://gitter.im/vuejs/vue" target="_blank">Gitter Chat</a></li>
9+
<li><a href="https://twitter.com/vuejs" target="_blank">Twitter</a></li>
10+
<br>
11+
<li><a href="https://vuejs-templates.github.io/webpack/" target="_blank">Docs for This Template</a></li>
12+
</ul>
13+
<h2>Ecosystem</h2>
14+
<ul>
15+
<li><a href="https://router.vuejs.org/" target="_blank">vue-router</a></li>
16+
<li><a href="https://vuex.vuejs.org/" target="_blank">vuex</a></li>
17+
<li><a href="https://vue-loader.vuejs.org/" target="_blank">vue-loader</a></li>
18+
<li><a href="https://github.com/vuejs/awesome-vue" target="_blank">awesome-vue</a></li>
19+
</ul>
20+
</div>
21+
</template>
22+
23+
<script lang="ts">
24+
export default {
25+
name: 'hello',
26+
data () {
27+
return {
28+
msg: 'Welcome to Your Vue.js App'
29+
}
30+
}
31+
}
32+
</script>
33+
34+
<!-- Add "scoped" attribute to limit CSS to this component only -->
35+
<style scoped>
36+
h1, h2 {
37+
font-weight: normal;
38+
}
39+
40+
ul {
41+
list-style-type: none;
42+
padding: 0;
43+
}
44+
45+
li {
46+
display: inline-block;
47+
margin: 0 10px;
48+
}
49+
50+
a {
51+
color: #42b983;
52+
}
53+
</style>

fixtures/vuejs3-typescript/main.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
import { createApp } from 'vue';
2+
import App from './App.vue';
3+
4+
createApp(App).mount('#app');
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
{
2+
"compilerOptions": {
3+
"target": "es6",
4+
"strict": true,
5+
"module": "es2015",
6+
"moduleResolution": "node",
7+
"allowSyntheticDefaultImports": true
8+
},
9+
"include": [
10+
"./**/*.ts"
11+
]
12+
}

lib/WebpackConfig.js

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,7 @@ class WebpackConfig {
136136
this.babelTypeScriptPresetOptions = {};
137137
this.vueOptions = {
138138
useJsx: false,
139+
version: null,
139140
};
140141
this.eslintOptions = {
141142
lintVue: false,
@@ -749,9 +750,21 @@ class WebpackConfig {
749750
if (!(key in this.vueOptions)) {
750751
throw new Error(`"${key}" is not a valid key for enableVueLoader(). Valid keys: ${Object.keys(this.vueOptions).join(', ')}.`);
751752
}
753+
754+
if (key === 'version') {
755+
const validVersions = [2, 3];
756+
if (!validVersions.includes(vueOptions.version)) {
757+
throw new Error(`"${vueOptions.version}" is not a valid value for the "version" option passed to enableVueLoader(). Valid versions are: ${validVersions.join(', ')}.`);
758+
}
759+
}
760+
761+
this.vueOptions[key] = vueOptions[key];
752762
}
753763

754-
this.vueOptions = vueOptions;
764+
// useJsx and vue 3 are not currently supported by Encore
765+
if (this.vueOptions.useJsx && this.vueOptions.version === 3) {
766+
throw new Error('Setting both "useJsx: true" and "version: 3" for enableVueLoader() is not currently supported. Please use version: 2 or disable useJsx.');
767+
}
755768
}
756769

757770
enableEslintLoader(eslintLoaderOptionsOrCallback = () => {}, eslintOptions = {}) {

lib/config-generator.js

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ const PluginPriorities = require('./plugins/plugin-priorities');
4343
const applyOptionsCallback = require('./utils/apply-options-callback');
4444
const sharedEntryTmpName = require('./utils/sharedEntryTmpName');
4545
const copyEntryTmpName = require('./utils/copyEntryTmpName');
46+
const getVueVersion = require('./utils/get-vue-version');
4647
const tmp = require('tmp');
4748
const fs = require('fs');
4849
const path = require('path');
@@ -101,7 +102,10 @@ class ConfigGenerator {
101102
};
102103

103104
if (this.webpackConfig.useVueLoader) {
104-
config.resolve.alias['vue$'] = 'vue/dist/vue.esm.js';
105+
const vueVersion = getVueVersion(this.webpackConfig);
106+
if (vueVersion === 2) {
107+
config.resolve.alias['vue$'] = 'vue/dist/vue.esm.js';
108+
}
105109
}
106110

107111
if (this.webpackConfig.usePreact && this.webpackConfig.preactOptions.preactCompat) {

lib/features.js

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -87,17 +87,28 @@ const features = {
8787
],
8888
description: 'process TypeScript files with Babel'
8989
},
90-
vue: {
90+
vue2: {
9191
method: 'enableVueLoader()',
9292
// vue is needed so the end-user can do things
9393
// vue-template-compiler is a peer dep of vue-loader
9494
packages: [
95-
{ name: 'vue' },
96-
{ name: 'vue-loader', enforce_version: true },
95+
{ name: 'vue', version: '^2.5' },
96+
{ name: 'vue-loader', version: '^15' },
9797
{ name: 'vue-template-compiler' }
9898
],
9999
description: 'load Vue files'
100100
},
101+
vue3: {
102+
method: 'enableVueLoader()',
103+
// vue is needed so the end-user can do things
104+
// @vue/compiler-sfc is an optional peer dep of vue-loader
105+
packages: [
106+
{ name: 'vue', enforce_version: true },
107+
{ name: 'vue-loader', enforce_version: true },
108+
{ name: '@vue/compiler-sfc' }
109+
],
110+
description: 'load Vue files'
111+
},
101112
'vue-jsx': {
102113
method: 'enableVueLoader()',
103114
packages: [

lib/friendly-errors/transformers/missing-loader.js

Lines changed: 25 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@
99

1010
'use strict';
1111

12+
const getVueVersion = require('../../utils/get-vue-version');
13+
1214
const TYPE = 'loader-not-enabled';
1315

1416
function isMissingLoaderError(e) {
@@ -24,14 +26,25 @@ function isMissingLoaderError(e) {
2426
}
2527

2628
function isErrorFromVueLoader(filename) {
27-
return filename.includes('??vue-loader-options');
29+
// vue2
30+
if (filename.includes('??vue-loader-options')) {
31+
return true;
32+
}
33+
34+
// vue3
35+
if (filename.includes('vue-loader/dist??')) {
36+
return true;
37+
}
38+
39+
return false;
2840
}
2941

3042
function getFileExtension(filename) {
3143
// ??vue-loader-options
3244
if (isErrorFromVueLoader(filename)) {
3345
// vue is strange, the "filename" is reported as something like
34-
// /path/to/project/node_modules/vue-loader/lib??vue-loader-options!./vuejs/App.vue?vue&type=style&index=1&lang=scss
46+
// vue2: /path/to/project/node_modules/vue-loader/lib??vue-loader-options!./vuejs/App.vue?vue&type=style&index=1&lang=scss
47+
// vue3: /path/to/project/node_modules/vue-loader/dist??ref--4-0!./vuejs/App.vue?vue&type=style&index=1&lang=scss
3548
const langPos = filename.indexOf('lang=') + 5;
3649
let endLangPos = filename.indexOf('&', langPos);
3750
if (endLangPos === -1) {
@@ -47,7 +60,7 @@ function getFileExtension(filename) {
4760
return split.pop();
4861
}
4962

50-
function transform(error) {
63+
function transform(error, webpackConfig) {
5164
if (!isMissingLoaderError(error)) {
5265
return error;
5366
}
@@ -68,7 +81,7 @@ function transform(error) {
6881
error.loaderName = 'react';
6982
break;
7083
case 'vue':
71-
error.loaderName = 'vue';
84+
error.loaderName = 'vue' + getVueVersion(webpackConfig);
7285
break;
7386
case 'tsx':
7487
case 'ts':
@@ -86,4 +99,11 @@ function transform(error) {
8699
return error;
87100
}
88101

89-
module.exports = transform;
102+
/*
103+
* Returns a factory to get the function.
104+
*/
105+
module.exports = function(webpackConfig) {
106+
return function(error) {
107+
return transform(error, webpackConfig);
108+
};
109+
};

lib/loaders/vue.js

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,14 +12,16 @@
1212
const WebpackConfig = require('../WebpackConfig'); //eslint-disable-line no-unused-vars
1313
const loaderFeatures = require('../features');
1414
const applyOptionsCallback = require('../utils/apply-options-callback');
15+
const getVueVersion = require('../utils/get-vue-version');
1516

1617
module.exports = {
1718
/**
1819
* @param {WebpackConfig} webpackConfig
1920
* @return {Array} of loaders to use for Vue files
2021
*/
2122
getLoaders(webpackConfig) {
22-
loaderFeatures.ensurePackagesExistAndAreCorrectVersion('vue');
23+
const vueVersion = getVueVersion(webpackConfig);
24+
loaderFeatures.ensurePackagesExistAndAreCorrectVersion('vue' + vueVersion);
2325

2426
const options = {};
2527

0 commit comments

Comments
 (0)