Webpack-encore: Can't modify babel rule to exclude bootstrap in node_module

Created on 9 Apr 2019  路  12Comments  路  Source: symfony/webpack-encore

Hi,

Last year I found a solution to allow Babel to process files in node_modules using something like this:

const webpackConfig = Encore.getWebpackConfig();
const babelLoader = webpackConfig.module.rules.find(rule => { 
  if (rule.use && rule.use[0]) {
    const firstUse = rule.use[0];
    return firstUse.loader === 'babel-loader';
  } 
  return false;
});
babelLoader.exclude = /node_modules\/(?!bootstrap\/).*/;
module.exports = webpackConfig;

But in the recent versions, when I run yarn run encore production I get the files from Bootstrap without being transpiled, which not happens in my regular files.

I am using the file babelrc to configure Babel:

{
    "presets": [
      [
        "@babel/preset-env",
        {
          "debug": true,
          "useBuiltIns": "usage",
          "corejs": 3,
          "targets": "> 0.25%, not dead"
        }
      ]
    ]
  }

The weird thing is that if I configure Babel through webpack.config.js:

.configureBabel(function(babelConfig) { }, { include_node_modules: ['bootstrap'] })

it works perfect. So I don't know the difference between the two solutions. Is there any implementation detail under this case that causes the first solution not valid (as I said, last year it works well)?

All 12 comments

Something more... if I copy the babel config from file babel.js to .babelrc:

{
  "presets": [
    [
      "@babel/preset-env",
      {
        "targets": {},
        "modules": false,
        "forceAllTransforms": true,
        "useBuiltIns": "entry"
      }
    ]
  ],
  "plugins": ["@babel/plugin-syntax-dynamic-import"]
}

It doesn't work neither. I have tried to replicate the logic of option include_node_modules in my webpack.config.js:

const webpackConfig = Encore.getWebpackConfig();
const babelLoader = webpackConfig.module.rules.find(rule => { 
  if (rule.use && rule.use[0]) {
    const firstUse = rule.use[0];
    return firstUse.loader === 'babel-loader';
  } 
  return false;
});
babelLoader.exclude = (filePath) => {
  if (!/(node_modules|bower_components)/.test(filePath)) {
      return false;
  }
  const whitelistedModules = ['bootstrap'].map(
      module => path.join('node_modules', module) + path.sep
  );

  for (const modulePath of whitelistedModules) {
      if (filePath.includes(modulePath)) {
          console.log('hi');
          return false;
      }
  }
  return true;
};
module.exports = webpackConfig;

The callback is executed when run yarn run encore production and returns false as expected but I still gets the spread operator in the resulting file that contains the Bootstrap code. However, if I use:

.configureBabel(function(babelConfig) {}, {
   include_node_modules: ['bootstrap']
})

it works as expected. I can't find the difference between the two methods so I think there is something in the middle that breaks my alternative when using the .babelrc file.

Note that in the latest version of Encore, you can now use the include_node_modules (well, its new name is includeNodeModules but include_node_modules still works with a deprecation warning) even when using an external babel config, by passing null as the first argument to configureBabel.

Yeah, I know! But my question is about the difference in the two methods because it seems that using .babelrc file doesn't perform the same result as using the configureBabel method

Hi @ger86,

I can't see any reason why it wouldn't work since the exclude rule is not handled by Babel but by Webpack itself.

I did a quick test with the following files and it seems to be fine:

// webpack.config.js
const Encore = require('@symfony/webpack-encore');

Encore
  .disableSingleRuntimeChunk()
  .setOutputPath('build/')
  .setPublicPath('/')
  .cleanupOutputBeforeBuild()
  .addEntry('main', './src/index.js')
;

const config = Encore.getWebpackConfig();

const babelLoader = config.module.rules.find(
  rule =>
    rule.use &&
    rule.use[0] &&
    rule.use[0].loader === 'babel-loader'
);

babelLoader.exclude = /node_modules\/(?!bootstrap\/).*/;

module.exports = config;
// src/index.js
require('bootstrap/js/src/popover.js');

Without the babelLoader.exclude = ... line:

$ fgrep -Rnil '...' ./build | wc -l
1

With it:

$ fgrep -Rnil '...' ./build | wc -l
0

Would you be able to share a repository that could show this issue?

Sure, I will post a repository with the failing code this weekend. Thanks

Hi,
I have created a repo: https://github.com/ger86/webpackencore-babel with my config and the steps to reproduce in the Readme.md.

As you will see, when there is a .babelrc file, you get the spread operator in the resulting file.
I would love to know the explanation of what is happening because I have been unable to find it after many hours.

Thanks

@ger86 I think that the exclude rule isn't actually the issue.

No matter which one of the config listed in your webpack.config.jsyou are using, Babel do process Bootstrap's files.

However, it doesn't do it with the same config than the one used for your files:

  • without the .babelrc file it uses the configuration provided by Encore, which applies project-wide
  • with the .babelrc you are using a file-relative configuration which only applies to the current package... and not to Bootstrap (Babel would use its .babelrc if it had one).

As noted on the following page a babel.config.js file should work better in your case: https://babeljs.io/docs/en/configuration#what-s-your-use-case

Hi @ger86,

Just a simple question, what's your need to transpile Bootstrap source code, when you can only include what you need of Bootstrap thanks to the files in js/dist.
The files in the js/dist are already transpiled separately

import 'bootstrap/js/dist/dropdown'

@Johann-S Not sure how useful it would be (if at all), but one possible use-case would be to use their own targets instead of the ones used during Bootstrap's release process.

@ger86 I think that the exclude rule isn't actually the issue.

No matter which one of the config listed in your webpack.config.jsyou are using, Babel do process Bootstrap's files.

However, it doesn't do it with the same config than the one used for _your_ files:

  • without the .babelrc file it uses the configuration provided by Encore, which applies project-wide
  • with the .babelrc you are using a file-relative configuration which only applies to the current package... and not to Bootstrap (Babel would use its .babelrc if it had one).

As noted on the following page a babel.config.js file should work better in your case: https://babeljs.io/docs/en/configuration#what-s-your-use-case

Ahhh, maybe that is the "problem" what causes that behaviour. I don't know about another .babelrc. Maybe we can complete the docs explaining that in order to help other users that have the same problem

Hi @ger86,

Just a simple question, what's your need to transpile Bootstrap source code, when you can only include what you need of Bootstrap thanks to the files in js/dist.
The files in the js/dist are already transpiled separately

import 'bootstrap/js/dist/dropdown'

Yes, because when Bootstrap 4 was released the files in dist was not ES6 modules, so I can't use in that way..

@ger86 according to what you said, you should think about updating your Bootstrap version because we released several security fixes and you'll be able to use the dist files

Was this page helpful?
0 / 5 - 0 ratings

Related issues

Growiel picture Growiel  路  4Comments

MatthD picture MatthD  路  4Comments

fanchyfanch picture fanchyfanch  路  3Comments

EliuTimana picture EliuTimana  路  4Comments

weaverryan picture weaverryan  路  4Comments