Skip to content

Commit d7df254

Browse files
committed
Refactoring loaders into re-usable modules
1 parent 6043b7c commit d7df254

File tree

6 files changed

+199
-100
lines changed

6 files changed

+199
-100
lines changed

lib/config-generator.js

Lines changed: 13 additions & 95 deletions
Original file line numberDiff line numberDiff line change
@@ -14,14 +14,17 @@ const ExtractTextPlugin = require('extract-text-webpack-plugin');
1414
const ManifestPlugin = require('./webpack/webpack-manifest-plugin');
1515
const DeleteUnusedEntriesJSPlugin = require('./webpack/delete-unused-entries-js-plugin');
1616
const AssetOutputDisplayPlugin = require('./friendly-errors/asset-output-display-plugin');
17-
const loaderFeatures = require('./loader-features');
1817
const CleanWebpackPlugin = require('clean-webpack-plugin');
1918
const WebpackChunkHash = require('webpack-chunk-hash');
2019
const FriendlyErrorsWebpackPlugin = require('friendly-errors-webpack-plugin');
2120
const missingLoaderTransformer = require('./friendly-errors/transformers/missing-loader');
2221
const missingLoaderFormatter = require('./friendly-errors/formatters/missing-loader');
2322
const missingPostCssConfigTransformer = require('./friendly-errors/transformers/missing-postcss-config');
2423
const missingPostCssConfigFormatter = require('./friendly-errors/formatters/missing-postcss-config');
24+
const cssLoaders = require('./loaders/css');
25+
const sassLoaders = require('./loaders/sass');
26+
const lessLoaders = require('./loaders/less');
27+
const babelLoaders = require('./loaders/babel');
2528

2629
class ConfigGenerator {
2730
/**
@@ -98,76 +101,18 @@ class ConfigGenerator {
98101
}
99102

100103
buildRulesConfig() {
101-
const cssLoaders = [
104+
let rules = [
102105
{
103-
loader: 'css-loader' + this.getSourceMapOption() + (this.webpackConfig.isProduction() ? '?minimize=1' : ''),
106+
// match .js and .jsx
107+
test: /\.jsx?$/,
108+
exclude: /(node_modules|bower_components)/,
109+
use: babelLoaders(this.webpackConfig)
104110
},
105-
];
106-
if (this.webpackConfig.usePostCssLoader) {
107-
loaderFeatures.ensureLoaderPackagesExist('postcss');
108-
109-
cssLoaders.push({
110-
loader: 'postcss-loader' + this.getSourceMapOption(),
111-
});
112-
}
113-
114-
let babelConfig = {
115-
// improves performance by caching babel compiles
116-
// we add this option ALWAYS
117-
// https://github.com/babel/babel-loader#options
118-
cacheDirectory: true
119-
};
120-
121-
// configure babel (unless the user is specifying .babelrc)
122-
// todo - add a sanity check for their babelrc contents
123-
if (!this.webpackConfig.doesBabelRcFileExist()) {
124-
Object.assign(babelConfig, {
125-
presets: [
126-
['env', {
127-
// modules don't need to be transformed - webpack will parse
128-
// the modules for us. This is a performance improvement
129-
// https://babeljs.io/docs/plugins/preset-env/#optionsmodules
130-
modules: false,
131-
targets: {
132-
browsers: '> 1%',
133-
uglify: true
134-
},
135-
useBuiltIns: true
136-
}]
137-
],
138-
});
139-
140-
if (this.webpackConfig.useReact) {
141-
loaderFeatures.ensureLoaderPackagesExist('react');
142-
143-
babelConfig.presets.push('react');
144-
}
145-
146-
// allow for babel config to be controlled
147-
this.webpackConfig.babelConfigurationCallback.apply(
148-
// use babelConfig as the this variable
149-
babelConfig,
150-
[babelConfig]
151-
);
152-
}
153-
154-
let rules = [];
155-
rules.push({
156-
// match .js and .jsx
157-
test: /\.jsx?$/,
158-
exclude: /(node_modules|bower_components)/,
159-
use: {
160-
loader: 'babel-loader',
161-
options: babelConfig
162-
}
163-
});
164-
165-
rules = rules.concat([
166111
{
167112
test: /\.css$/,
168113
use: ExtractTextPlugin.extract({
169114
fallback: 'style-loader' + this.getSourceMapOption(),
170-
use: cssLoaders
115+
use: cssLoaders(this.webpackConfig)
171116
})
172117
},
173118
{
@@ -186,51 +131,24 @@ class ConfigGenerator {
186131
publicPath: this.webpackConfig.getRealPublicPath()
187132
}
188133
},
189-
]);
134+
];
190135

191136
if (this.webpackConfig.useSassLoader) {
192-
loaderFeatures.ensureLoaderPackagesExist('sass');
193-
194-
const sassLoaders = [...cssLoaders];
195-
if (true === this.webpackConfig.sassOptions.resolve_url_loader) {
196-
// responsible for resolving SASS url() paths
197-
// without this, all url() paths must be relative to the
198-
// entry file, not the file that contains the url()
199-
sassLoaders.push({
200-
loader: 'resolve-url-loader' + this.getSourceMapOption(),
201-
});
202-
}
203-
204-
sassLoaders.push({
205-
loader: 'sass-loader',
206-
options: {
207-
// needed by the resolve-url-loader
208-
sourceMap: (true === this.webpackConfig.sassOptions.resolve_url_loader) || this.webpackConfig.useSourceMaps
209-
}
210-
});
211-
212137
rules.push({
213138
test: /\.s[ac]ss$/,
214139
use: ExtractTextPlugin.extract({
215140
fallback: 'style-loader' + this.getSourceMapOption(),
216-
use: sassLoaders
141+
use: sassLoaders(this.webpackConfig)
217142
})
218143
});
219144
}
220145

221146
if (this.webpackConfig.useLessLoader) {
222-
loaderFeatures.ensureLoaderPackagesExist('less');
223-
224147
rules.push({
225148
test: /\.less/,
226149
use: ExtractTextPlugin.extract({
227150
fallback: 'style-loader' + this.getSourceMapOption(),
228-
use: [
229-
...cssLoaders,
230-
{
231-
loader: 'less-loader' + this.getSourceMapOption()
232-
},
233-
]
151+
use: lessLoaders(this.webpackConfig)
234152
})
235153
});
236154
}

lib/loaders/babel.js

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
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 loaderFeatures = require('../loader-features');
13+
14+
/**
15+
* @param {WebpackConfig} webpackConfig
16+
* @return {Array} of loaders to use for Babel
17+
*/
18+
module.exports = function(webpackConfig) {
19+
let babelConfig = {
20+
// improves performance by caching babel compiles
21+
// we add this option ALWAYS
22+
// https://github.com/babel/babel-loader#options
23+
cacheDirectory: true
24+
};
25+
26+
// configure babel (unless the user is specifying .babelrc)
27+
// todo - add a sanity check for their babelrc contents
28+
if (!webpackConfig.doesBabelRcFileExist()) {
29+
Object.assign(babelConfig, {
30+
presets: [
31+
['env', {
32+
// modules don't need to be transformed - webpack will parse
33+
// the modules for us. This is a performance improvement
34+
// https://babeljs.io/docs/plugins/preset-env/#optionsmodules
35+
modules: false,
36+
targets: {
37+
browsers: '> 1%',
38+
uglify: true
39+
},
40+
useBuiltIns: true
41+
}]
42+
],
43+
});
44+
45+
if (webpackConfig.useReact) {
46+
loaderFeatures.ensureLoaderPackagesExist('react');
47+
48+
babelConfig.presets.push('react');
49+
}
50+
51+
// allow for babel config to be controlled
52+
webpackConfig.babelConfigurationCallback.apply(
53+
// use babelConfig as the this variable
54+
babelConfig,
55+
[babelConfig]
56+
);
57+
}
58+
59+
return [
60+
{
61+
loader: 'babel-loader',
62+
options: babelConfig
63+
}
64+
];
65+
};

lib/loaders/css.js

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
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 loaderFeatures = require('../loader-features');
13+
14+
/**
15+
* @param {WebpackConfig} webpackConfig
16+
* @return {Array} of loaders to use for CSS files
17+
*/
18+
module.exports = function(webpackConfig) {
19+
const cssLoaders = [
20+
{
21+
loader: 'css-loader',
22+
options: {
23+
minimize: webpackConfig.isProduction(),
24+
sourceMap: webpackConfig.useSourceMaps
25+
}
26+
},
27+
];
28+
29+
if (webpackConfig.usePostCssLoader) {
30+
loaderFeatures.ensureLoaderPackagesExist('postcss');
31+
32+
cssLoaders.push({
33+
loader: 'postcss-loader',
34+
options: {
35+
sourceMap: webpackConfig.useSourceMaps
36+
}
37+
});
38+
}
39+
40+
return cssLoaders;
41+
};

lib/loaders/less.js

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
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 loaderFeatures = require('../loader-features');
13+
const cssLoaders = require('./css');
14+
15+
/**
16+
* @param {WebpackConfig} webpackConfig
17+
* @return {Array} of loaders to use for Less files
18+
*/
19+
module.exports = function(webpackConfig) {
20+
loaderFeatures.ensureLoaderPackagesExist('less');
21+
22+
return [
23+
...cssLoaders(webpackConfig),
24+
{
25+
loader: 'less-loader',
26+
options: {
27+
sourceMap: webpackConfig.useSourceMaps
28+
}
29+
},
30+
];
31+
};

lib/loaders/sass.js

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
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 loaderFeatures = require('../loader-features');
13+
const cssLoaders = require('./css');
14+
15+
/**
16+
* @param {WebpackConfig} webpackConfig
17+
* @return {Array} of loaders to use for Sass files
18+
*/
19+
module.exports = function(webpackConfig) {
20+
loaderFeatures.ensureLoaderPackagesExist('sass');
21+
22+
const sassLoaders = [...cssLoaders(webpackConfig)];
23+
if (true === webpackConfig.sassOptions.resolve_url_loader) {
24+
// responsible for resolving SASS url() paths
25+
// without this, all url() paths must be relative to the
26+
// entry file, not the file that contains the url()
27+
sassLoaders.push({
28+
loader: 'resolve-url-loader',
29+
options: {
30+
sourceMap: webpackConfig.useSourceMaps
31+
}
32+
});
33+
}
34+
35+
sassLoaders.push({
36+
loader: 'sass-loader',
37+
options: {
38+
// needed by the resolve-url-loader
39+
sourceMap: (true === webpackConfig.sassOptions.resolve_url_loader) || webpackConfig.useSourceMaps
40+
}
41+
});
42+
43+
return sassLoaders;
44+
};

test/config-generator.js

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -360,7 +360,7 @@ describe('The config-generator function', () => {
360360
const jsRule = findRule(/\.jsx?$/, actualConfig.module.rules);
361361

362362
// check for the default env preset only
363-
expect(JSON.stringify(jsRule.use.options.presets)).contains('env');
363+
expect(JSON.stringify(jsRule.use[0].options.presets)).contains('env');
364364
});
365365

366366
it('If .babelrc is present, we pass *no* config', () => {
@@ -376,7 +376,7 @@ describe('The config-generator function', () => {
376376
const jsRule = findRule(/\.jsx?$/, actualConfig.module.rules);
377377

378378
// the options should only contain the cacheDirectory option
379-
expect(JSON.stringify(jsRule.use.options)).to.equal(JSON.stringify({ 'cacheDirectory': true }));
379+
expect(JSON.stringify(jsRule.use[0].options)).to.equal(JSON.stringify({ 'cacheDirectory': true }));
380380
});
381381

382382
it('configureBabel() passes babel options', () => {
@@ -392,7 +392,7 @@ describe('The config-generator function', () => {
392392

393393
const jsRule = findRule(/\.jsx?$/, actualConfig.module.rules);
394394

395-
expect(jsRule.use.options.presets).to.include('foo');
395+
expect(jsRule.use[0].options.presets).to.include('foo');
396396
});
397397

398398
it('enableReactPreset() passes react preset to babel', () => {
@@ -409,9 +409,9 @@ describe('The config-generator function', () => {
409409

410410
const jsRule = findRule(/\.jsx?$/, actualConfig.module.rules);
411411

412-
expect(jsRule.use.options.presets).to.include('react');
412+
expect(jsRule.use[0].options.presets).to.include('react');
413413
// foo is also still there, not overridden
414-
expect(jsRule.use.options.presets).to.include('foo');
414+
expect(jsRule.use[0].options.presets).to.include('foo');
415415
});
416416
});
417417

0 commit comments

Comments
 (0)