Danziger
Danziger

Reputation: 21161

Webpack and Babel not transpiling a dependency inside node_modules that breaks IE 11 and Edge due to ES6's spread syntax

I have a project that uses @mdx-js/runtime and it completely breaks on IE 11 or Edge (Edge 44.18362.449.0):

SCRIPT1028: SCRIPT1028: Expected identifier, string or number

It actually breaks because of the spread syntax here:

const allNodes = sortedNodes.map(({ start: _, ...node }, i) => { 

This line of code comes from remark-mdx, which is a dependency of @mdx-js/runtime, particularly this file and line: https://github.com/mdx-js/mdx/blob/master/packages/remark-mdx/extract-imports-and-exports.js#L66

I've been trying to get Webpack and Babel to transform that file so that the spread is no longer there:

Browserlist:

If I run npx browserslist I can see IE 11 is there.

"browserslist": [
    "> 0.5%",
    "last 2 version",
    "Firefox ESR",
    "not dead",
    "iOS >= 9"
]

.babelrc:

I've tried disabling loose mode and adding @babel/plugin-transform-spread and @babel/plugin-proposal-object-rest-spread, but didn't fix the issue.

  {
    "presets": [[
        "@babel/preset-env", {
            "useBuiltIns": "usage",
            "loose": false, // Was true before
            "modules": "auto",
            "corejs": 3
        }],
        "@babel/preset-react",
        "@babel/preset-typescript"
    ],

    "plugins": [
        ["@babel/plugin-proposal-decorators", {
            "legacy": true
        }],
        ["@babel/plugin-proposal-class-properties", {
            "loose": true
        }],
        "@babel/plugin-transform-spread", // Just added
        "@babel/plugin-proposal-object-rest-spread", // Just added
        "@babel/plugin-proposal-optional-chaining",
        "@babel/plugin-proposal-nullish-coalescing-operator",
        "react-hot-loader/babel"
    ]
}

webpack.config.js:

Here I tried explicitly including remark-mdx and @mdx-js/runtime and also removing the exclude property or changing it to /node_modules\/(?!(remark-mdx|@mdx-js\/runtime)\/).*/, but nothing seems to work:

  {
    test: /\.(j|t)sx?$/,
    include: [
      path.resolve(__dirname, 'src'),
      // Tried explicitly adding these 2:
      path.resolve('node_modules/remark-mdx'),
      path.resolve('node_modules/@mdx-js/runtime'),
    ],
    // Tried removing `exclude` or not excluding those 2 packages:
    // exclude: /node_modules/,
    // exclude: /node_modules\/(?!(remark-mdx|@mdx-js\/runtime)\/).*/,
    use: [{
      loader: 'babel-loader',
      options: {
        cacheDirectory: true,
        babelrc: true,
      }
    }],
  }

I'm using Babel 7 an Webpack 4.

Upvotes: 8

Views: 10625

Answers (1)

Danziger
Danziger

Reputation: 21161

Ok, it turns out to process files inside node_modules you need to use babel.config.js rather than .babelrc as explained here and here:

What's your use case?

  • You are using a monorepo?
  • You want to compile node_modules?

    babel.config.json is for you!

  • You have a configuration that only applies to a single part of your project?

    .babelrc.json is for you!

Also, if you are using a monorepo you need to set rootMode: 'upward' so that Babel can find your new config file, as explained here.

Webpack's babel-loader config should look something like this:

  {
    test: /\.(j|t)sx?$/,
    include: [
      path.resolve(__dirname, 'src'),
      // No need to add @mdx-js/runtime, just add the problematic package:
      path.resolve('node_modules/remark-mdx'),
    ],
    // You also need to exclude it from the exclusions:
    exclude: /node_modules\/(?!(remark-mdx)\/).*/,
    use: [{
      loader: 'babel-loader',
      options: {
        cacheDirectory: true,
        // And replace .babelrc with babel.config.json...
        babelrc: false,
        // ...which might also mean you need this if you are using a monorepo:
        rootMode: 'upward',
      }
    }],
  }

After this change the file was being processed, but I was getting a different error:

Uncaught TypeError: Cannot assign to read only property 'exports' of object '#<Object>'

In this case, I had to add sourceType: "unambiguous" to babel.config.json, as remark-mdx was using CommonJS modules rather than ES6 ones. You can add this at the root of your babel.config.json file or inside overrides, so that it only targets packages inside node_modules.

For more on why this happens, see How do I use babel's `useBuiltIns: 'usage'` option on the vendors bundle?.

Babel's babel.config.json ended up looking like this:

{
    "presets": [[
        "@babel/preset-env", {
            "useBuiltIns": "usage",
            "loose": true,
            "modules": "auto",
            "corejs": 3
        }],
        "@babel/preset-react",
        "@babel/preset-typescript"
    ],

    "plugins": [
        ["@babel/plugin-proposal-decorators", {
            "legacy": true
        }],
        ["@babel/plugin-proposal-class-properties", {
            "loose": true
        }],
        "@babel/plugin-proposal-optional-chaining",
        "@babel/plugin-proposal-nullish-coalescing-operator",
        "react-hot-loader/babel"
    ],

    "overrides": [{
        "test": "./node_modules",
        "sourceType": "unambiguous"
    }]
}

Upvotes: 23

Related Questions