diff --git a/index.js b/index.js index f0325126..e7a95d53 100644 --- a/index.js +++ b/index.js @@ -785,6 +785,11 @@ class Encore { * * Encore.configureBabel(function(babelConfig) { * // change the babelConfig + * // if you use an external Babel configuration + * // this callback will NOT be used. In this case + * // you can pass false as the first parameter to + * // still be able to use some of the options below + * // without a warning. * }, { * // set optional Encore-specific options, for instance: * @@ -806,12 +811,16 @@ class Encore { * A Webpack Condition passed to the JS/JSX rule that * determines which files and folders should not be * processed by Babel (https://webpack.js.org/configuration/module/#condition). - * Cannot be used if the "include_node_modules" option is + * Can be used even if you have an external Babel configuration + * (a .babelrc file for instance) + * Cannot be used if the "includeNodeModules" option is * also set. * * {string[]} includeNodeModules * If set that option will include the given Node modules to - * the files that are processed by Babel. Cannot be used if - * the "exclude" option is also set. + * the files that are processed by Babel. + * Can be used even if you have an external Babel configuration + * (a .babelrc file for instance). + * Cannot be used if the "exclude" option is also set * * {'usage'|'entry'|false} useBuiltIns (default='entry') * Set the "useBuiltIns" option of @babel/preset-env that changes * how it handles polyfills (https://babeljs.io/docs/en/babel-preset-env#usebuiltins) @@ -820,8 +829,11 @@ class Encore { * by individual polyfills. Using it with 'usage' will try to * automatically detect which polyfills are needed for each file and * add them accordingly. + * Cannot be used if you have an external Babel configuration (a .babelrc + * file for instance). In this case you can set the option directly into + * that configuration file. * - * @param {function} callback + * @param {function|false} callback * @param {object} encoreOptions * @returns {Encore} */ diff --git a/lib/WebpackConfig.js b/lib/WebpackConfig.js index 6c0d9f5f..5d5b26bd 100644 --- a/lib/WebpackConfig.js +++ b/lib/WebpackConfig.js @@ -329,15 +329,22 @@ class WebpackConfig { } configureBabel(callback, options = {}) { - if (typeof callback !== 'function') { - throw new Error('Argument 1 to configureBabel() must be a callback function.'); - } + if (callback) { + if (typeof callback !== 'function') { + throw new Error('Argument 1 to configureBabel() must be a callback function or null.'); + } - if (this.doesBabelRcFileExist()) { - throw new Error('configureBabel() cannot be called because your app already has Babel configuration (a `.babelrc` file, `.babelrc.js` file or `babel` key in `package.json`). Either put all of your Babel configuration in that file, or delete it and use this function.'); + if (this.doesBabelRcFileExist()) { + logger.warning('The "callback" argument of configureBabel() will not be used because your app already provides an external Babel configuration (a ".babelrc" file, ".babelrc.js" file or "babel" key in "package.json"). Use null as a first argument to remove that warning.'); + } } - this.babelConfigurationCallback = callback; + this.babelConfigurationCallback = callback || (() => {}); + + // Whitelist some options that can be used even if there + // is an external Babel config. The other ones won't be + // applied and a warning message will be displayed instead. + const allowedOptionsWithExternalConfig = ['includeNodeModules', 'exclude']; for (const optionKey of Object.keys(options)) { let normalizedOptionKey = optionKey; @@ -346,6 +353,11 @@ class WebpackConfig { normalizedOptionKey = 'includeNodeModules'; } + if (this.doesBabelRcFileExist() && !allowedOptionsWithExternalConfig.includes(normalizedOptionKey)) { + logger.warning(`The "${normalizedOptionKey}" option of configureBabel() will not be used because your app already provides an external Babel configuration (a ".babelrc" file, ".babelrc.js" file or "babel" key in "package.json").`); + continue; + } + if (normalizedOptionKey === 'includeNodeModules') { if (Object.keys(options).includes('exclude')) { throw new Error('"includeNodeModules" and "exclude" options can\'t be used together when calling configureBabel().'); diff --git a/test/WebpackConfig.js b/test/WebpackConfig.js index 6d2f1ffc..64835ea3 100644 --- a/test/WebpackConfig.js +++ b/test/WebpackConfig.js @@ -538,6 +538,15 @@ describe('WebpackConfig object', () => { }); describe('configureBabel', () => { + beforeEach(() => { + logger.reset(); + logger.quiet(); + }); + + afterEach(() => { + logger.quiet(false); + }); + it('Calling method sets it', () => { const config = createConfig(); const testCallback = () => {}; @@ -599,13 +608,31 @@ describe('WebpackConfig object', () => { }).to.throw('must be a callback function'); }); - it('Calling when .babelrc is present throws an exception', () => { + it('Calling with a callback when .babelrc is present displays a warning', () => { const config = createConfig(); config.runtimeConfig.babelRcFileExists = true; + config.configureBabel(() => {}); - expect(() => { - config.configureBabel(() => {}); - }).to.throw('configureBabel() cannot be called because your app already has Babel configuration'); + const warnings = logger.getMessages().warning; + expect(warnings).to.have.lengthOf(1); + expect(warnings[0]).to.contain('your app already provides an external Babel configuration'); + }); + + it('Calling with a whitelisted option when .babelrc is present works fine', () => { + const config = createConfig(); + config.runtimeConfig.babelRcFileExists = true; + config.configureBabel(false, { includeNodeModules: ['foo'] }); + expect(logger.getMessages().warning).to.be.empty; + }); + + it('Calling with a non-whitelisted option when .babelrc is present displays a warning', () => { + const config = createConfig(); + config.runtimeConfig.babelRcFileExists = true; + config.configureBabel(false, { useBuiltIns: 'foo' }); + + const warnings = logger.getMessages().warning; + expect(warnings).to.have.lengthOf(1); + expect(warnings[0]).to.contain('your app already provides an external Babel configuration'); }); it('Pass invalid config', () => {