24

I'm having a hard time trying to configure Babel to transpile code that IE11 can understand, specifically arrow functions. Running npx webpack --mode=development with my configuration doesn't convert the arrow functions in my code: in the eval() statement in the generated code, I can see that all the instances have gone unconverted.

Unlike in the console output quoted in this question, there's no mention in mine of "Using targets" or "Using presets". Whether that's something to do with using npx webpack rather than npm run build I don't know.

Here's the Babel part of my package.json:

{
  // name, version etc. snipped
  "devDependencies": {
    "@babel/core": "^7.1.2",
    "@babel/plugin-transform-async-to-generator": "^7.1.0",
    "@babel/plugin-transform-es2015-arrow-functions": "^6.22.0",
    "@babel/plugin-transform-es2015-modules-commonjs": "^6.26.2",
    "@babel/preset-env": "^7.1.0",
    "ajv": "^6.5.4",
    "copy-webpack-plugin": "^4.5.2",
    "eslint-plugin-jest": "^21.24.1",
    "jest": "^23.6.0",
    "jest-dom": "^2.0.4",
    "webpack": "^4.20.2",
    "webpack-cli": "^3.1.2"
  },
  "babel": {
    "presets": [
      [
        "@babel/preset-env",
        {
          "targets": {
            "ie": "11"
          }
        }
      ]
    ],
    "env": {
      "development": {
        "plugins": [
          "transform-es2015-arrow-functions",
          "transform-es2015-modules-commonjs"
        ]
      },
      "test": {
        "plugins": [
          "transform-es2015-arrow-functions",
          "transform-es2015-modules-commonjs"
        ]
      }
    }
  }
}

My webpack.config.js looks like:

const CopyWebpackPlugin = require("copy-webpack-plugin");
const path = require("path");

module.exports = {
    entry: "./src/thing.js",
    optimization: {
        minimize: false
    },
    output: {
        filename: "thing.js",
        path: path.resolve(__dirname, "dist")
    },
    plugins: [
        new CopyWebpackPlugin([
            // snipped
        ])
    ]
};

I arrived at this point from reading other questions here about Babel configurations, and the babel-preset-env docs and also the very skimpy babel-plugin-transform-es2015-arrow-functions docs. The answers to this very similar question (no accepted answer) don't mention that plugin at all, and one suggests using a polyfill, which seems to involve loading a library in your actual code rather than at this stage?

I'm very new to working with Webpack in general and don't understand what the difference is between "env" (which gets mentioned in a lot of questions) and "@babel/preset-env". Or really what the former implies in general; if you click on "env" in the docs navigation, it takes you to the page for @babel/preset-env.

What am I doing wrong?

Scott Martin
  • 1,260
  • 2
  • 17
  • 27
  • 2
    Are you using https://github.com/babel/babel-loader? As is, it looks like you've installed Babel, but not actually enabled it in Webpack. – loganfsmyth Oct 15 '18 at 21:51
  • 2
    @loganfsmyth I'm extremely happy to report that no, I wasn't, and that fixed it. If you submit that as an answer I'll mark it as accepted. – Scott Martin Oct 16 '18 at 10:37
  • 1
    I'm going to vent a little... it's really irritating that there's no explanation of that in the Babel docs. It's only mentioned [in the sentence](https://babeljs.io/docs/en/options#primary-options) "Users of Babel's integrations, like babel-loader or @babel/register are unlikely to use these." I know now that if you're coming at this via the Webpack docs loaders are explained, but landing at the Babel site I (a learner) hit a brick wall. It would have saved a bunch of frustrating hours if there had been a link to [the Webpack loaders docs](https://webpack.js.org/concepts/#loaders). – Scott Martin Oct 16 '18 at 11:05
  • 2
    We have a "Setup" top-level page: https://babeljs.io/setup. The "Webpack" section goes over the steps for using `babel-loader`. – loganfsmyth Oct 16 '18 at 15:28
  • 1
    Unfortunately that page isn't learner-friendly at all. Now that you've mentioned, it I can see a "Webpack" button located after a dozen other things that I've never heard of - I didn't get to see that yesterday because I immediately followed the large and unexplained hint "try CLI". That scrolled me down to the bottom of the page to a block of instructions. I got to part 4, which links to various other docs, and from there it's a web of pages that don't mention babel-loader. There was nothing to suggest I should go back up to the button list to look for Webpack configuration info. – Scott Martin Oct 16 '18 at 15:48
  • 1
    Thanks, that's useful feedback. That page hasn't been updated in a while, there almost certainly other things we can do to make it more beginner-friendly. – loganfsmyth Oct 17 '18 at 04:00

5 Answers5

15

If you are using Webpack 5, you need to specify the features that you want to transpile in the ouput.environment configuration, as explained here: https://webpack.js.org/configuration/output/#outputenvironment.

output: {
  // ... other configs
  environment: {
    arrowFunction: false,
    bigIntLiteral: false,
    const: false,
    destructuring: false,
    dynamicImport: false,
    forOf: false,
    module: false,
  },
}

EDIT 08/10/22

While doing some refactoring of my webpack configuration, I figured out that the arrows had stopped transpiling (or maybe I didn't test extensively when giving the original answer).

Two things weren't setup correctly: the target key was missing, and the babel configuration had a wrong value.

For supporting legacy browsers, the target key needs to be set like this:

target: ['web', 'es5']

While in the Babel configuration, I had added 'not dead' in the browserslist configuration under targets in the Babel loader, so since IE11 is now technically dead, this configuration removed the trasnpilation of the arrow function.

So if you still need to transpile the arrow function, this would be the relevant part in the Babel configuration.

module: {
  rules: [
    {
      test: /\.m?js$/,
      use: {
        loader: 'babel-loader',
        options: {
          presets: [
            [
              '@babel/preset-env',
              {
                useBuiltIns: 'entry',
                targets: {
                  // be sure not to add 'not dead' here 
                  browsers: ['> 1%', 'last 2 versions', 'Firefox ESR']
                },
                corejs: {
                  version: 3,
                  proposals: false,
                },
              },
            ],
          ],
          plugins: [['@babel/plugin-transform-runtime', { corejs: 3 }]],
        },
      },
    },
  ],
},
Giorgio Tempesta
  • 1,816
  • 24
  • 32
  • 1
    @ScottMartin I don't know, it's probably since Webpack 5. I removed Laravel Mix which was using Webpack 4 and switched to Webpack 5 at the same time, so I had to add this configuration to make it work. – Giorgio Tempesta Jul 28 '21 at 15:50
  • 1
    Ah, you've nailed it. Looking at the original project that spawned this question, it's still using Webpack 4. I'll update my answer with reference to yours. – Scott Martin Jul 28 '21 at 16:16
  • 3
    It turns out there's now a [`target` setting for this that accepts `browserslist` syntax](https://webpack.js.org/configuration/target/), which obviously can line up with your Babel config. Great. – Scott Martin Jul 28 '21 at 17:30
8

In addition to loganfsmyth's answer that solved the problem, I want to note for any other beginners reading this that I made life easier for myself afterward by moving the Babel configuration out of package.json into a .babelrc.

I also discovered that the plugins I needed, such as the one I mentioned above, babel-plugin-transform-es2015-arrow-functions, have newer versions with a different naming scheme - for that example, @babel/plugin-transform-arrow-functions. The documentation pages for the old versions don't mention that.

The module part of my webpack.config.js now looks like:

module: {
    rules: [
        {
            test: /\.m?js$/,
            exclude: /node_modules/,
            use: {
                loader: "babel-loader"
            }
        }
    ]
}

My .babelrc looks like:

{
    "presets": [
        [
            "@babel/preset-env",
            {
                "targets": {
                    "ie": "11"
                },
                "useBuiltIns": "entry"
            }
        ]
    ],
    "plugins": [
        "@babel/transform-async-to-generator",
        "@babel/transform-arrow-functions",
        "@babel/transform-modules-commonjs"
    ],
    "env": {
        "development": {},
        "test": {},
        "production": {}
    }
}

Update 2021: As of Webpack version 5, it outputs ES6 code by default. If you need that not to happen, you need to add a configuration setting. See Giorgio Tempesta's answer.

Scott Martin
  • 1,260
  • 2
  • 17
  • 27
  • 1
    I did same but still getting error in IE11 for arrow function. Any idea? – Yogen Darji Oct 31 '18 at 11:16
  • Hi Yogen, can you paste the error message that you're getting? – Scott Martin Oct 31 '18 at 13:28
  • 2
    IE11 `"SCRIPT1002 syntax error"` I found if I remove arrow function => then it works fine. – Yogen Darji Oct 31 '18 at 13:51
  • Did you `npm install @babel/transform-arrow-functions`? But, it would be better for you to ask a question of your own rather than trying to get help in the comments here. You could link back to this answer in your question if you want. – Scott Martin Oct 31 '18 at 15:34
  • 1
    Why the duplicated config though? It should be enough to define presets and plugins in either webpack.config.js _or_ .babelrc, no? – Hein Haraldson Berg Jul 31 '19 at 07:49
  • @HeinHaraldsonBerg: According to the [Webpack documentation](https://webpack.js.org/concepts/#plugins) you have to `require` the plugins that you're intending to use. According to the [Babel documentation](https://babeljs.io/docs/en/options#plugins) you have to specify a list of the plugins you're intending to use. So it seems that you need both. ¯\\_(ツ)_/¯ – Scott Martin Jul 31 '19 at 16:24
  • Looking at my npm dependencies, `@babel/preset-env` (7.12.10) in fact depends on `@babel/plugin-transform-arrow-functions`. So I am a bit perplexed: if it depends on it during installation, why does it not activate it during compilation? Why else list it as a dependency? Is there perhaps some setting that will tell `preset-env` to turn on these plugins? – corwin.amber Dec 15 '20 at 21:44
  • 2 years later, I've found [this guide to Babel and Webpack](https://www.thebasement.be/working-with-babel-7-and-webpack/), which says: "The docs are a bit unclear on this one, but if you set your @babel/preset-env-options in the .babelrc-file you don’t have to define them in your Webpack configuration. If you do so, the Webpack configuration will overwrite the options in your .babelrc." Would have saved me a lot of frowning if they'd just bothered to say so in those docs. – Scott Martin Apr 07 '21 at 16:31
7

Babel itself is a transformation library, but on its own it will not integrate into any specific tooling. To use Babel with Webpack, you'll want to install the babel-loader package and configure it in your Webpack config using something along the lines of

module: {
  rules: [
    {
      test: /\.m?js$/,
      exclude: /node_modules/,
      use: {
        loader: 'babel-loader',
      }
    }
  ]
}
loganfsmyth
  • 156,129
  • 30
  • 331
  • 251
2

2022 Update

If you have this problem now, it could be because browerslist treats IE 11 as dead since version 4.21.0. The solution to access IE 11 anyway is to add ie 11 either as the last entry in browserslist or at least after not dead.

Normally you don't see browserslist in the package.json, but if you use npm ls browserslist you can see which dependency uses browserslist and which version of it.

See also: https://github.com/browserslist/browserslist/issues/699

RiZKiT
  • 2,107
  • 28
  • 23
1

I had the same problem. Turned out it wasn't all of my code that had arrow functions, but only one single library. I went inside my built bundle, and searched for code with arrow functions (=>). Then I searched and copied some unique-looking names around it and managed to find the source of it searching for it in all files within node_modules. In my case it turned out that the code with arrow functions came from fetch polyfill called unfetch. I'm not sure why it didn't get transpiled by the plugin, but I switched it to "whatwg-fetch" and it worked just fine - no arrow functions in bundle anymore. You could try the same technique to discover what causes it in your case.

Matt Leonowicz
  • 564
  • 7
  • 15