Skip to content

Vue.js Loader #49

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 7 commits into from
Jun 29, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
50 changes: 50 additions & 0 deletions fixtures/vuejs/App.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
<template>
<div id="app">
<img src="./assets/logo.png">
<hello></hello>
</div>
</template>

<script>
import Hello from './components/Hello'

class TestClassSyntax {

}

export default {
name: 'app',
components: {
Hello
}
}
</script>

<style>
#app {
font-family: 'Avenir', Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-align: center;
color: #2c3e50;
margin-top: 60px;
}
</style>

<style lang="scss">
#app {
display: flex;
color: #2c3e90;
}
</style>

<style lang="sass">
#app
color: #2c3e90
</style>

<style lang="less">
#app {
margin-top: 40px;
}
</style>
Binary file added fixtures/vuejs/assets/logo.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
53 changes: 53 additions & 0 deletions fixtures/vuejs/components/Hello.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
<template>
<div class="hello">
<h1>{{ msg }}</h1>
<h2>Essential Links</h2>
<ul>
<li><a href="https://vuejs.org" target="_blank">Core Docs</a></li>
<li><a href="https://forum.vuejs.org" target="_blank">Forum</a></li>
<li><a href="https://gitter.im/vuejs/vue" target="_blank">Gitter Chat</a></li>
<li><a href="https://twitter.com/vuejs" target="_blank">Twitter</a></li>
<br>
<li><a href="http://vuejs-templates.github.io/webpack/" target="_blank">Docs for This Template</a></li>
</ul>
<h2>Ecosystem</h2>
<ul>
<li><a href="http://router.vuejs.org/" target="_blank">vue-router</a></li>
<li><a href="http://vuex.vuejs.org/" target="_blank">vuex</a></li>
<li><a href="http://vue-loader.vuejs.org/" target="_blank">vue-loader</a></li>
<li><a href="https://github.com/vuejs/awesome-vue" target="_blank">awesome-vue</a></li>
</ul>
</div>
</template>

<script>
export default {
name: 'hello',
data () {
return {
msg: 'Welcome to Your Vue.js App'
}
}
}
</script>

<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped>
h1, h2 {
font-weight: normal;
}

ul {
list-style-type: none;
padding: 0;
}

li {
display: inline-block;
margin: 0 10px;
}

a {
color: #42b983;
}
</style>
8 changes: 8 additions & 0 deletions fixtures/vuejs/main.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import Vue from 'vue'
import App from './App'

new Vue({
el: '#app',
template: '<App/>',
components: { App }
})
25 changes: 24 additions & 1 deletion index.js
Original file line number Diff line number Diff line change
Expand Up @@ -330,7 +330,8 @@ module.exports = {
},

/**
* If enabled, the react preset is added to Babel:
* If enabled, the react preset is added to Babel.
*
* https://babeljs.io/docs/plugins/preset-react/
*
* @returns {exports}
Expand All @@ -341,6 +342,28 @@ module.exports = {
return this;
},

/**
* If enabled, the Vue.js loader is enabled.
*
* https://github.com/vuejs/vue-loader
*
* Encore.enableVueLoader();
*
* // or configure the vue-loader options
* // https://vue-loader.vuejs.org/en/configurations/advanced.html
* Encore.enableVueLoader(function(options) {
* options.preLoaders = { ... }
* });
*
* @param {function} vueLoaderOptionsCallback
* @returns {exports}
*/
enableVueLoader(vueLoaderOptionsCallback = () => {}) {
webpackConfig.enableVueLoader(vueLoaderOptionsCallback);

return this;
},

/**
* If enabled, the output directory is emptied between
* each build (to remove old files).
Expand Down
12 changes: 12 additions & 0 deletions lib/WebpackConfig.js
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,8 @@ class WebpackConfig {
this.providedVariables = {};
this.babelConfigurationCallback = function() {};
this.useReact = false;
this.useVueLoader = false;
this.vueLoaderOptionsCallback = () => {};
this.loaders = [];
}

Expand Down Expand Up @@ -222,6 +224,16 @@ class WebpackConfig {
this.useReact = true;
}

enableVueLoader(vueLoaderOptionsCallback = () => {}) {
this.useVueLoader = true;

if (typeof vueLoaderOptionsCallback !== 'function') {
throw new Error('Argument 1 to enableVueLoader() must be a callback function.');
}

this.vueLoaderOptionsCallback = vueLoaderOptionsCallback;
}

cleanupOutputBeforeBuild() {
this.cleanupOutput = true;
}
Expand Down
43 changes: 24 additions & 19 deletions lib/config-generator.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@

const webpack = require('webpack');
const ExtractTextPlugin = require('extract-text-webpack-plugin');
const extractText = require('./loaders/extract-text');
const ManifestPlugin = require('./webpack/webpack-manifest-plugin');
const DeleteUnusedEntriesJSPlugin = require('./webpack/delete-unused-entries-js-plugin');
const AssetOutputDisplayPlugin = require('./friendly-errors/asset-output-display-plugin');
Expand All @@ -21,11 +22,14 @@ const missingLoaderTransformer = require('./friendly-errors/transformers/missing
const missingLoaderFormatter = require('./friendly-errors/formatters/missing-loader');
const missingPostCssConfigTransformer = require('./friendly-errors/transformers/missing-postcss-config');
const missingPostCssConfigFormatter = require('./friendly-errors/formatters/missing-postcss-config');
const vueUnactivatedLoaderTransformer = require('./friendly-errors/transformers/vue-unactivated-loader-error');
const vueUnactivatedLoaderFormatter = require('./friendly-errors/formatters/vue-unactivated-loader-error');
const pathUtil = require('./config/path-util');
const cssLoaderUtil = require('./loaders/css');
const sassLoaderUtil = require('./loaders/sass');
const lessLoaderUtil = require('./loaders/less');
const babelLoaderUtil = require('./loaders/babel');
const vueLoaderUtil = require('./loaders/vue');

class ConfigGenerator {
/**
Expand Down Expand Up @@ -68,9 +72,14 @@ class ConfigGenerator {
config.stats = this.buildStatsConfig();

config.resolve = {
extensions: ['.js', '.jsx']
extensions: ['.js', '.jsx', '.vue'],
alias: {}
};

if (this.webpackConfig.useVueLoader) {
config.resolve.alias['vue$'] = 'vue/dist/vue.esm.js';
}

return config;
}

Expand Down Expand Up @@ -111,10 +120,7 @@ class ConfigGenerator {
},
{
test: /\.css$/,
use: ExtractTextPlugin.extract({
fallback: 'style-loader' + this.getSourceMapOption(),
use: cssLoaderUtil.getLoaders(this.webpackConfig)
})
use: extractText.extract(this.webpackConfig, cssLoaderUtil.getLoaders(this.webpackConfig, false))
},
{
test: /\.(png|jpg|jpeg|gif|ico|svg)$/,
Expand All @@ -137,20 +143,21 @@ class ConfigGenerator {
if (this.webpackConfig.useSassLoader) {
rules.push({
test: /\.s[ac]ss$/,
use: ExtractTextPlugin.extract({
fallback: 'style-loader' + this.getSourceMapOption(),
use: sassLoaderUtil.getLoaders(this.webpackConfig)
})
use: extractText.extract(this.webpackConfig, sassLoaderUtil.getLoaders(this.webpackConfig))
});
}

if (this.webpackConfig.useLessLoader) {
rules.push({
test: /\.less/,
use: ExtractTextPlugin.extract({
fallback: 'style-loader' + this.getSourceMapOption(),
use: lessLoaderUtil.getLoaders(this.webpackConfig)
})
use: extractText.extract(this.webpackConfig, lessLoaderUtil.getLoaders(this.webpackConfig))
});
}

if (this.webpackConfig.useVueLoader) {
rules.push({
test: /\.vue$/,
use: vueLoaderUtil.getLoaders(this.webpackConfig, this.webpackConfig.vueLoaderOptionsCallback)
});
}

Expand Down Expand Up @@ -313,11 +320,13 @@ class ConfigGenerator {
clearConsole: false,
additionalTransformers: [
missingLoaderTransformer,
missingPostCssConfigTransformer
missingPostCssConfigTransformer,
vueUnactivatedLoaderTransformer
],
additionalFormatters: [
missingLoaderFormatter,
missingPostCssConfigFormatter
missingPostCssConfigFormatter,
vueUnactivatedLoaderFormatter
],
compilationSuccessInfo: {
messages: []
Expand Down Expand Up @@ -377,10 +386,6 @@ class ConfigGenerator {
https: this.webpackConfig.useDevServerInHttps()
};
}

getSourceMapOption() {
return this.webpackConfig.useSourceMaps ? '?sourceMap' : '';
}
}

/**
Expand Down
42 changes: 42 additions & 0 deletions lib/friendly-errors/formatters/vue-unactivated-loader-error.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <[email protected]>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

'use strict';

const chalk = require('chalk');

function formatErrors(errors) {
if (errors.length === 0) {
return [];
}

let messages = [];
// there will be an error for *every* file, but showing
// the error over and over again is not helpful

messages.push(
chalk.red('Vue processing failed:')
);
messages.push('');
for (let error of errors) {
messages.push(` * ${error.message}`);
}

messages.push('');

return messages;
}

function format(errors) {
return formatErrors(errors.filter((e) => (
e.type === 'vue-unactivated-loader-error'
)));
}

module.exports = format;
39 changes: 39 additions & 0 deletions lib/friendly-errors/transformers/vue-unactivated-loader-error.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <[email protected]>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

'use strict';

const TYPE = 'vue-unactivated-loader-error';

function isVueUnactivatedLoaderError(e) {
if (e.name !== 'ModuleBuildError') {
return false;
}

if (e.message.indexOf('Cannot process lang=') === -1) {
return false;
}

return true;
}

function transform(error) {
if (!isVueUnactivatedLoaderError(error)) {
return error;
}

error = Object.assign({}, error);

error.type = TYPE;
error.severity = 900;

return error;
}

module.exports = transform;
7 changes: 7 additions & 0 deletions lib/loader-features.js
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,13 @@ const loaderFeatures = {
method: 'enableReactPreset()',
packages: ['babel-preset-react'],
description: 'process React JS files'
},
vue: {
method: 'enableVueLoader()',
// vue is needed so the end-user can do things
// vue-template-compiler is a peer dep of vue-loader
packages: ['vue', 'vue-loader', 'vue-template-compiler'],
description: 'load VUE files'
}
};

Expand Down
Loading