Skip to content

Commit 2e1f9f7

Browse files
committed
Throw an error if the user tries to use (e.g.) lang="scss" in vue, but has not activated that loader
1 parent 0d5cdd5 commit 2e1f9f7

File tree

9 files changed

+181
-22
lines changed

9 files changed

+181
-22
lines changed

lib/config-generator.js

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@ const missingLoaderTransformer = require('./friendly-errors/transformers/missing
2222
const missingLoaderFormatter = require('./friendly-errors/formatters/missing-loader');
2323
const missingPostCssConfigTransformer = require('./friendly-errors/transformers/missing-postcss-config');
2424
const missingPostCssConfigFormatter = require('./friendly-errors/formatters/missing-postcss-config');
25+
const vueUnactivatedLoaderTransformer = require('./friendly-errors/transformers/vue-unactivated-loader-error');
26+
const vueUnactivatedLoaderFormatter = require('./friendly-errors/formatters/vue-unactivated-loader-error');
2527
const pathUtil = require('./config/path-util');
2628
const cssLoaderUtil = require('./loaders/css');
2729
const sassLoaderUtil = require('./loaders/sass');
@@ -316,11 +318,13 @@ class ConfigGenerator {
316318
clearConsole: false,
317319
additionalTransformers: [
318320
missingLoaderTransformer,
319-
missingPostCssConfigTransformer
321+
missingPostCssConfigTransformer,
322+
vueUnactivatedLoaderTransformer
320323
],
321324
additionalFormatters: [
322325
missingLoaderFormatter,
323-
missingPostCssConfigFormatter
326+
missingPostCssConfigFormatter,
327+
vueUnactivatedLoaderFormatter
324328
],
325329
compilationSuccessInfo: {
326330
messages: []
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
/*
2+
* This file is part of the Symfony package.
3+
*
4+
* (c) Fabien Potencier <[email protected]>
5+
*
6+
* For the full copyright and license information, please view the LICENSE
7+
* file that was distributed with this source code.
8+
*/
9+
10+
'use strict';
11+
12+
const chalk = require('chalk');
13+
14+
function formatErrors(errors) {
15+
if (errors.length === 0) {
16+
return [];
17+
}
18+
19+
let messages = [];
20+
// there will be an error for *every* file, but showing
21+
// the error over and over again is not helpful
22+
23+
messages.push(
24+
chalk.red('Vue processing failed:')
25+
);
26+
messages.push('');
27+
for (let error of errors) {
28+
messages.push(` * ${error.message}`);
29+
}
30+
31+
messages.push('');
32+
33+
return messages;
34+
}
35+
36+
function format(errors) {
37+
return formatErrors(errors.filter((e) => (
38+
e.type === 'vue-unactivated-loader-error'
39+
)));
40+
}
41+
42+
module.exports = format;
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
/*
2+
* This file is part of the Symfony package.
3+
*
4+
* (c) Fabien Potencier <[email protected]>
5+
*
6+
* For the full copyright and license information, please view the LICENSE
7+
* file that was distributed with this source code.
8+
*/
9+
10+
'use strict';
11+
12+
const TYPE = 'vue-unactivated-loader-error';
13+
14+
function isVueUnactivatedLoaderError(e) {
15+
if (e.name !== 'ModuleBuildError') {
16+
return false;
17+
}
18+
19+
if (e.message.indexOf('Cannot process lang=') === -1) {
20+
return false;
21+
}
22+
23+
return true;
24+
}
25+
26+
function transform(error) {
27+
if (!isVueUnactivatedLoaderError(error)) {
28+
return error;
29+
}
30+
31+
error = Object.assign({}, error);
32+
33+
error.type = TYPE;
34+
error.severity = 900;
35+
36+
return error;
37+
}
38+
39+
module.exports = transform;

lib/loaders/sass.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ module.exports = {
3737

3838
sassLoaders.push({
3939
loader: 'sass-loader',
40-
options: Object.assign(sassOptions, {
40+
options: Object.assign({}, sassOptions, {
4141
// needed by the resolve-url-loader
4242
sourceMap: (true === webpackConfig.sassOptions.resolve_url_loader) || webpackConfig.useSourceMaps
4343
}),

lib/loaders/vue-unactivated-loader.js

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
/*
2+
* This file is part of the Symfony package.
3+
*
4+
* (c) Fabien Potencier <[email protected]>
5+
*
6+
* For the full copyright and license information, please view the LICENSE
7+
* file that was distributed with this source code.
8+
*/
9+
10+
'use strict';
11+
12+
const loaderUtils = require('loader-utils');
13+
14+
/**
15+
* A "fake" loader that's set inside vue-loader for languages
16+
* when they are not activated in Encore.
17+
*
18+
* For example, if the user has not called enableSassLoader(),
19+
* then this loader is added, so that the user gets an error if
20+
* they try to use lang="scss" inside Vue.
21+
*
22+
* This is necessary because vue-loader *always* automatically
23+
* processes new lang values through a loader (e.g. lang="foo"
24+
* will automatically try to use a foo-loader). If we did *not*
25+
* register this as a loader for scss (for example), then the
26+
* user *would* still be able to use lang="scss"... but it would
27+
* not use our custom sass-loader configuration.
28+
*
29+
* @return {function}
30+
*/
31+
module.exports = function() {
32+
const options = loaderUtils.getOptions(this) || {};
33+
34+
// the vue-unactivated-loader-error transformer expects some of this language
35+
throw new Error(`Cannot process lang="${options.lang}" inside ${this.resourcePath}: the ${options.loaderName} is not activated. Call ${options.featureCommand} in webpack.config.js to enable it.`);
36+
};

lib/loaders/vue.js

Lines changed: 38 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -53,28 +53,22 @@ module.exports = {
5353
cssLoaderUtil.getLoaders(webpackConfig, true),
5454
true
5555
),
56+
};
5657

57-
/*
58-
* The optional loaders are always added here. Without these,
59-
* if the user forgets to enable an optional loader but uses
60-
* (for example) lang="scss" inside vue, it *will* work, becaus
61-
* vue-loader will automatically register the sass-loader. But,
62-
* it will completely skip our configuration (e.g. ExtractTextPlugin).
63-
*/
64-
65-
scss: extractText.extract(
58+
if (webpackConfig.useSassLoader) {
59+
loaders.scss = extractText.extract(
6660
webpackConfig,
6761
sassLoaderUtil.getLoaders(webpackConfig, {}, true),
6862
true
69-
),
63+
);
7064

7165
/*
7266
* indentedSyntax is required here for SASS. It's not required
7367
* when using the loaders normally, because the .sass extension
7468
* is detected and this option is activated automatically inside
7569
* sass-loader.
7670
*/
77-
sass: extractText.extract(
71+
loaders.sass = extractText.extract(
7872
webpackConfig,
7973
sassLoaderUtil.getLoaders(
8074
webpackConfig,
@@ -84,14 +78,43 @@ module.exports = {
8478
true
8579
),
8680
true
87-
),
81+
);
82+
} else {
83+
loaders.scss = {
84+
loader: require.resolve('./vue-unactivated-loader'),
85+
options: {
86+
lang: 'scss',
87+
loaderName: 'sass-loader',
88+
featureCommand: loaderFeatures.getLoaderFeatureMethod('sass')
89+
}
90+
};
8891

89-
less: extractText.extract(
92+
loaders.sass = {
93+
loader: require.resolve('./vue-unactivated-loader'),
94+
options: {
95+
lang: 'sass',
96+
loaderName: 'sass-loader',
97+
featureCommand: loaderFeatures.getLoaderFeatureMethod('sass')
98+
}
99+
};
100+
}
101+
102+
if (webpackConfig.useLessLoader) {
103+
loaders.less = extractText.extract(
90104
webpackConfig,
91105
lessLoaderUtil.getLoaders(webpackConfig, true),
92106
true
93-
)
94-
};
107+
);
108+
} else {
109+
loaders.scss = {
110+
loader: require.resolve('./vue-unactivated-loader'),
111+
options: {
112+
lang: 'less',
113+
loaderName: 'less-loader',
114+
featureCommand: loaderFeatures.getLoaderFeatureMethod('less')
115+
}
116+
};
117+
}
95118

96119
return [
97120
{

lib/test/setup.js

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ function createWebpackConfig(testAppDir, outputDirName = '', command, argv = {})
5959
return config;
6060
}
6161

62-
function runWebpack(webpackConfig, callback) {
62+
function runWebpack(webpackConfig, callback, allowCompilationError = false) {
6363
validator(webpackConfig);
6464
const compiler = webpack(configGenerator(webpackConfig));
6565
compiler.run((err, stats) => {
@@ -74,7 +74,7 @@ function runWebpack(webpackConfig, callback) {
7474

7575
const info = stats.toJson();
7676

77-
if (stats.hasErrors()) {
77+
if (stats.hasErrors() && !allowCompilationError) {
7878
console.error(info.errors);
7979

8080
throw new Error('Compilation error running webpack!');
@@ -84,7 +84,7 @@ function runWebpack(webpackConfig, callback) {
8484
console.warn(info.warnings);
8585
}
8686

87-
callback(assertUtil(webpackConfig));
87+
callback(assertUtil(webpackConfig), stats);
8888
});
8989
}
9090

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@
3636
"file-loader": "^0.10.1",
3737
"friendly-errors-webpack-plugin": "^1.6.1",
3838
"fs-extra": "^2.0.0",
39+
"loader-utils": "^1.1.0",
3940
"lodash": ">=3.5 <5",
4041
"pkg-up": "^1.0.0",
4142
"pretty-error": "^2.1.0",

test/functional.js

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -617,7 +617,7 @@ module.exports = {
617617
* That's because, in theory, you should be able to
618618
* run Encore from other directories, give you set
619619
* the context. However, in this case, the vue-loader
620-
* users load-postcss-config to load the postcss config,
620+
* uses load-postcss-config to load the postcss config,
621621
* but it uses process.cwd() to find it, instead of the
622622
* context. So, in this case, we *must* set the cwd()
623623
* to be the temp test directory.
@@ -637,6 +637,8 @@ module.exports = {
637637
config.setPublicPath('/build');
638638
config.addEntry('main', './vuejs/main');
639639
config.enableVueLoader();
640+
config.enableSassLoader();
641+
config.enableLessLoader();
640642
config.configureBabel(function(config) {
641643
config.presets = [
642644
['env', {
@@ -682,5 +684,17 @@ module.exports = {
682684
);
683685
});
684686
});
687+
688+
it('Vue.js error when using non-activated loaders', (done) => {
689+
const config = createWebpackConfig('www/build', 'dev');
690+
config.setPublicPath('/build');
691+
config.addEntry('main', './vuejs/main');
692+
config.enableVueLoader();
693+
694+
testSetup.runWebpack(config, (webpackAssert, stats) => {
695+
expect(stats.toJson().errors[0]).to.contain('Cannot process lang="less" inside');
696+
done();
697+
}, true);
698+
});
685699
});
686700
});

0 commit comments

Comments
 (0)