Skip to content

Commit b171eb4

Browse files
committed
feature #401 Allow to configure the JS exclude rules through configureBabel (Lyrkan)
This PR was squashed before being merged into the master branch (closes #401). Discussion ---------- Allow to configure the JS exclude rules through configureBabel This PR closes #342 by allowing to configure the js/jsx loaders' exclude rule by using `configureBabel()`. For instance: ```js Encore.configureBabel( () => {}, { exclude: /foo/ } ); ``` Note that it doesn't change the default behavior that excludes `node_modules` and `bower_components`. **Edit:** Also adds an `include_node_modules` option to use the default `exclude` rule but only include some Node modules (can't be used if the `exclude` option is also set): ```js Encore.configureBabel( () => {}, { include_node_modules: ['foo', 'bar', 'baz'] } ); ``` Commits ------- a07238c Add an example of calling configureBabel with the "include_node_modules" option 714a732 Add "include_node_modules" option to configureBabel 6c8f073 Allow to configure the JS exclude rules through configureBabel
2 parents 94e65e7 + a07238c commit b171eb4

File tree

5 files changed

+159
-4
lines changed

5 files changed

+159
-4
lines changed

index.js

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -716,13 +716,36 @@ class Encore {
716716
*
717717
* Encore.configureBabel(function(babelConfig) {
718718
* // change the babelConfig
719+
* }, {
720+
* // set optional Encore-specific options, for instance:
721+
*
722+
* // change the rule that determines which files
723+
* // won't be processed by Babel
724+
* exclude: /bower_components/
725+
*
726+
* // ...or keep the default rule but only allow
727+
* // *some* Node modules to be processed by Babel
728+
* include_node_modules: ['foundation-sites']
719729
* });
720730
*
731+
* Supported options:
732+
* * {Condition} exclude (default=/(node_modules|bower_components)/)
733+
* A Webpack Condition passed to the JS/JSX rule that
734+
* determines which files and folders should not be
735+
* processed by Babel (https://webpack.js.org/configuration/module/#condition).
736+
* Cannot be used if the "include_node_modules" option is
737+
* also set.
738+
* * {string[]} include_node_modules
739+
* If set that option will include the given Node modules to
740+
* the files that are processed by Babel. Cannot be used if
741+
* the "exclude" option is also set.
742+
*
721743
* @param {function} callback
744+
* @param {object} encoreOptions
722745
* @returns {Encore}
723746
*/
724-
configureBabel(callback) {
725-
webpackConfig.configureBabel(callback);
747+
configureBabel(callback, encoreOptions = {}) {
748+
webpackConfig.configureBabel(callback, encoreOptions);
726749

727750
return this;
728751
}

lib/WebpackConfig.js

Lines changed: 41 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,9 @@ class WebpackConfig {
8080
images: false,
8181
fonts: false
8282
};
83+
this.babelOptions = {
84+
exclude: /(node_modules|bower_components)/
85+
};
8386

8487
// Features/Loaders options callbacks
8588
this.postCssLoaderOptionsCallback = () => {};
@@ -302,7 +305,7 @@ class WebpackConfig {
302305
this.useSourceMaps = enabled;
303306
}
304307

305-
configureBabel(callback) {
308+
configureBabel(callback, options = {}) {
306309
if (typeof callback !== 'function') {
307310
throw new Error('Argument 1 to configureBabel() must be a callback function.');
308311
}
@@ -312,6 +315,43 @@ class WebpackConfig {
312315
}
313316

314317
this.babelConfigurationCallback = callback;
318+
319+
for (const optionKey of Object.keys(options)) {
320+
if (optionKey === 'include_node_modules') {
321+
if (Object.keys(options).includes('exclude')) {
322+
throw new Error('"include_node_modules" and "exclude" options can\'t be used together when calling configureBabel().');
323+
}
324+
325+
if (!Array.isArray(options[optionKey])) {
326+
throw new Error('Option "include_node_modules" passed to configureBabel() must be an Array.');
327+
}
328+
329+
this.babelOptions['exclude'] = (filePath) => {
330+
// Don't exclude modules outside of node_modules/bower_components
331+
if (!/(node_modules|bower_components)/.test(filePath)) {
332+
return false;
333+
}
334+
335+
// Don't exclude whitelisted Node modules
336+
const whitelistedModules = options[optionKey].map(
337+
module => path.join('node_modules', module) + path.sep
338+
);
339+
340+
for (const modulePath of whitelistedModules) {
341+
if (filePath.includes(modulePath)) {
342+
return false;
343+
}
344+
}
345+
346+
// Exclude other modules
347+
return true;
348+
};
349+
} else if (!(optionKey in this.babelOptions)) {
350+
throw new Error(`Invalid option "${optionKey}" passed to configureBabel(). Valid keys are ${Object.keys(this.babelOptions).join(', ')}`);
351+
} else {
352+
this.babelOptions[optionKey] = options[optionKey];
353+
}
354+
}
315355
}
316356

317357
configureCssLoader(callback) {

lib/config-generator.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -197,7 +197,7 @@ class ConfigGenerator {
197197
{
198198
// match .js and .jsx
199199
test: /\.jsx?$/,
200-
exclude: /(node_modules|bower_components)/,
200+
exclude: this.webpackConfig.babelOptions.exclude,
201201
use: babelLoaderUtil.getLoaders(this.webpackConfig)
202202
},
203203
{

test/WebpackConfig.js

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -471,6 +471,45 @@ describe('WebpackConfig object', () => {
471471
const testCallback = () => {};
472472
config.configureBabel(testCallback);
473473
expect(config.babelConfigurationCallback).to.equal(testCallback);
474+
expect(String(config.babelOptions.exclude)).to.equal(String(/(node_modules|bower_components)/));
475+
});
476+
477+
it('Calling with "exclude" option', () => {
478+
const config = createConfig();
479+
config.configureBabel(() => {}, { exclude: 'foo' });
480+
481+
expect(config.babelOptions.exclude).to.equal('foo');
482+
});
483+
484+
it('Calling with "include_node_modules" option', () => {
485+
const config = createConfig();
486+
config.configureBabel(() => {}, { include_node_modules: ['foo', 'bar'] });
487+
488+
expect(config.babelOptions.exclude).to.be.a('Function');
489+
490+
const includedPaths = [
491+
path.join('test', 'lib', 'index.js'),
492+
path.join('test', 'node_modules', 'foo', 'index.js'),
493+
path.join('test', 'node_modules', 'foo', 'lib', 'index.js'),
494+
path.join('test', 'node_modules', 'bar', 'lib', 'index.js'),
495+
path.join('test', 'node_modules', 'baz', 'node_modules', 'foo', 'index.js'),
496+
];
497+
498+
const excludedPaths = [
499+
path.join('test', 'bower_components', 'foo', 'index.js'),
500+
path.join('test', 'bower_components', 'bar', 'index.js'),
501+
path.join('test', 'bower_components', 'baz', 'index.js'),
502+
path.join('test', 'node_modules', 'baz', 'lib', 'index.js'),
503+
path.join('test', 'node_modules', 'baz', 'lib', 'foo', 'index.js')
504+
];
505+
506+
for (const filePath of includedPaths) {
507+
expect(config.babelOptions.exclude(filePath)).to.equal(false);
508+
}
509+
510+
for (const filePath of excludedPaths) {
511+
expect(config.babelOptions.exclude(filePath)).to.equal(true);
512+
}
474513
});
475514

476515
it('Calling with non-callback throws an error', () => {
@@ -489,6 +528,30 @@ describe('WebpackConfig object', () => {
489528
config.configureBabel(() => {});
490529
}).to.throw('configureBabel() cannot be called because your app already has Babel configuration');
491530
});
531+
532+
it('Pass invalid config', () => {
533+
const config = createConfig();
534+
535+
expect(() => {
536+
config.configureBabel(() => {}, { fake_option: 'foo' });
537+
}).to.throw('Invalid option "fake_option" passed to configureBabel()');
538+
});
539+
540+
it('Calling with both "include_node_modules" and "exclude" options', () => {
541+
const config = createConfig();
542+
543+
expect(() => {
544+
config.configureBabel(() => {}, { exclude: 'foo', include_node_modules: ['bar', 'baz'] });
545+
}).to.throw('can\'t be used together');
546+
});
547+
548+
it('Calling with an invalid "include_node_modules" option value', () => {
549+
const config = createConfig();
550+
551+
expect(() => {
552+
config.configureBabel(() => {}, { include_node_modules: 'foo' });
553+
}).to.throw('must be an Array');
554+
});
492555
});
493556

494557
describe('configureCssLoader', () => {

test/config-generator.js

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -826,6 +826,35 @@ describe('The config-generator function', () => {
826826
});
827827
});
828828

829+
describe('Test configureBabel()', () => {
830+
it('without configureBabel()', () => {
831+
const config = createConfig();
832+
config.outputPath = '/tmp/output/public-path';
833+
config.publicPath = '/public-path';
834+
config.addEntry('main', './main');
835+
836+
const actualConfig = configGenerator(config);
837+
838+
const jsRule = findRule(/\.jsx?$/, actualConfig.module.rules);
839+
expect(String(jsRule.exclude)).to.equal(String(/(node_modules|bower_components)/));
840+
});
841+
842+
it('with configureBabel() and a different exclude rule', () => {
843+
const config = createConfig();
844+
config.outputPath = '/tmp/output/public-path';
845+
config.publicPath = '/public-path';
846+
config.addEntry('main', './main');
847+
config.configureBabel(() => {}, {
848+
exclude: /foo/
849+
});
850+
851+
const actualConfig = configGenerator(config);
852+
853+
const jsRule = findRule(/\.jsx?$/, actualConfig.module.rules);
854+
expect(String(jsRule.exclude)).to.equal(String(/foo/));
855+
});
856+
});
857+
829858
describe('Test shouldSplitEntryChunks', () => {
830859
it('Not production', () => {
831860
const config = createConfig();

0 commit comments

Comments
 (0)