Skip to content

Support for ESM style Babel config files, calling Babel in async fixed? #894

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
kroko opened this issue Mar 5, 2021 · 16 comments
Closed

Comments

@kroko
Copy link

kroko commented Mar 5, 2021

I'm submitting a bug report

Node.js version:
14.16.0

Webpack Version:
5.24.3

Babel Core Version:
7.13.8

Babel Loader Version:
8.2.2

Please tell us about your environment:
macOS 10.15.7

Current behavior:
I was using CJS for all _build config related source_, currently I am changing everything to ESM.
I am not changing package.json to "type": "module" though, simply renaming all files to .mjs and changing the syntax.
So far so good (all webpack configs, helper scripts migrated, everything works).

Now, when renaming .babelrc.js to .babelrc.mjs and changing syntax, webpack throws well defined error.

Parsing error: /path/to/.babelrc.mjs: Error while loading config - You appear to be using a native ECMAScript module configuration file, which is only supported when running Babel asynchronously

Here are contents as a reference

.babelrc.js

// module.exports = { // uncomment for CJS

export default { // uncomment for MJS

  "presets": [
    [
      "@babel/preset-env",
      {
        // https://babeljs.io/docs/en/babel-preset-env.html#modules
        "modules": false,
        // https://babeljs.io/docs/en/babel-preset-env.html#usebuiltins
        "useBuiltIns": "usage",
        // https://babeljs.io/docs/en/babel-preset-env.html#corejs
        // https://github.com/zloirock/core-js#babelpreset-env
        "corejs": '3.8',
        // https://babeljs.io/docs/en/babel-preset-env.html#forcealltransforms
        "forceAllTransforms": false,
        // https://babeljs.io/docs/en/babel-preset-env.html#ignorebrowserslistconfig
        "ignoreBrowserslistConfig": false,
        // https://babeljs.io/docs/en/babel-preset-env.html#debug
        "debug": true
      }
    ],
    [
      "@babel/preset-react",
      {
        development: process.env.BABEL_ENV === "development"
      }
    ]
  ],
  "plugins": process.env.BABEL_ENV === "development"
    ? [
        "react-refresh/babel",
        "@babel/plugin-syntax-jsx"
      ]
    : [
        "@babel/plugin-syntax-jsx"
      ]
};

and JS related module section in webpack config

// ...

    {
      test: /\.(js|mjs|ts)x?$/,
      exclude: [/node_modules/, /bower_components/, /preflight\.js$/],
      use: [
        {
          loader: 'babel-loader',
          options: {
            cacheDirectory: true,
            cacheCompression: false
          }
        }
      ]
    },

// ...

Expected/desired behavior:

Babel documentation about Supported file extensions say that ECMAScript modules are supported

babel.config.mjs and .babelrc.mjs use native ECMAScript modules. They are supported by Node.js 13.2+ (or older versions via the --experimental-modules flag). Please remember that native ECMAScript modules are asynchronous (that's why import() always returns a promise!): for this reason, .mjs config files will throw when calling Babel synchronously. They have been supported since v7.8.0.

Now, it nicely mentions why the error happens.

Expected that babel-loader supports ESM moving forward.

If the current behavior is a bug, please provide the steps to reproduce and if possible a minimal demo of the problem along with a gist/jsbin of your webpack configuration.

See above - just change any existing project babel.config.js or .babelrc.js to use .mjs and fix syntax. Probably change webpack config .mjs and change syntax too.

What is the expected behavior?

It should work. At some point 😄 , as ESM is way to go.

What is the motivation / use case for changing the behavior?

ESM everything.
Sure, there are few config files within build config where ESM is still not possible yet, such as .eslintrc.js that explicitly states that ESLint does not support ESM configuration at this time. But Babel explicitly states it can.

I see this pull #825 , but...

Thanks!

@JLHwung
Copy link
Contributor

JLHwung commented Mar 5, 2021

I can not reproduce this issue locally, can you share a reproduction repo?

@nicolo-ribaudo
Copy link
Member

nicolo-ribaudo commented Mar 5, 2021

Also make sure you are not accidentally using an old Babel or babel-loader version, run yarn dedupe (Yarn 2) / npx yarn-deduplicate (Yarn 1) / npm dedupe (Npm)

@UsamaHameed
Copy link

I hate to hijack this issue because my situation seems the opposite but the error is the same.

I have a babel config written as an ESM and I see the following error:

ERROR in ./src/main.js
Module build failed (from ./node_modules/babel-loader/lib/index.js):
Error: path/to/babel.config.js: Error while loading config - You appear to be using a native ECMAScript module configuration file, which is only supported when running Babel asynchronously.
    at loadCjsOrMjsDefault (path/to/node_modules/@babel/core/lib/config/files/module-types.js:59:13)
    at loadCjsOrMjsDefault.next (<anonymous>)
    at readConfigJS (path/to/node_modules/@babel/core/lib/config/files/configuration.js:200:47)
    at readConfigJS.next (<anonymous>)
    at Function.<anonymous> (path/to/node_modules/@babel/core/lib/gensync-utils/async.js:26:3)
    at Generator.next (<anonymous>)
    at evaluateSync (path/to/gensync/index.js:251:28)
    at Function.sync (path/to/node_modules/gensync/index.js:89:14)
    at sync (path/to/node_modules/@babel/core/lib/gensync-utils/async.js:66:25)
    at sync (path/to/node_modules/gensync/index.js:182:19)

When I rename my babel.config.js file to babel.config.cjs and make it CJS as well, it works but not with ESM. Is my configuration missing something to make ESM work?

babel.config.js

export default {
  presets: [
    ['@babel/preset-env', {
      modules: false,
      targets: { browsers: ['last 2 versions', 'safari >= 7'] },
    },
    ],
    '@babel/preset-react',
  ],
  plugins: [
    'syntax-trailing-function-commas',
    'import-glob',
    ['@babel/plugin-transform-runtime', {
      regenerator: true,
    }],
    'transform-class-properties',
    'react-hot-loader/babel',
    '@babel/plugin-proposal-optional-chaining',
    '@babel/plugin-proposal-nullish-coalescing-operator',
  ],
  env: {
    test: {
      presets: [
        '@babel/preset-env',
        '@babel/preset-react',
      ],
      plugins: [
        // '@babel/plugin-transform-modules-commonjs',
        ['@babel/plugin-transform-runtime', {
          regenerator: true,
        }],
        'transform-class-properties',
      ],
    },
  },
};

@nicolo-ribaudo
Copy link
Member

It seems to be the same problem.
Could you edit path/to/node_modules/@babel/core/lib/config/files/module-types.js, add Error.stackTraceLimit = Infinity at the beginning of that file, and post the longer stack trace that it will print?

@UsamaHameed
Copy link

UsamaHameed commented Mar 5, 2021

Here you go:

ERROR in ./src/main.js
Module build failed (from ./node_modules/babel-loader/lib/index.js):
Error: /path/to/babel.config.js: Error while loading config - You appear to be using a native ECMAScript module configuration file, which is only supported when running Babel asynchronously.
    at loadCjsOrMjsDefault (/path/to/node_modules/@babel/core/lib/config/files/module-types.js:60:13)
    at loadCjsOrMjsDefault.next (<anonymous>)
    at readConfigJS (/path/to/node_modules/@babel/core/lib/config/files/configuration.js:200:47)
    at readConfigJS.next (<anonymous>)
    at Function.<anonymous> (/path/to/node_modules/@babel/core/lib/gensync-utils/async.js:26:3)
    at Generator.next (<anonymous>)
    at evaluateSync (/path/to/node_modules/gensync/index.js:251:28)
    at Function.sync (/path/to/node_modules/gensync/index.js:89:14)
    at sync (/path/to/node_modules/@babel/core/lib/gensync-utils/async.js:66:25)
    at sync (/path/to/node_modules/gensync/index.js:182:19)
    at onFirstPause (/path/to/node_modules/gensync/index.js:210:24)
    at Generator.next (<anonymous>)
    at cachedFunction (/path/to/node_modules/@babel/core/lib/config/caching.js:68:46)
    at cachedFunction.next (<anonymous>)
    at evaluateSync (/path/to/node_modules/gensync/index.js:251:28)
    at /path/to/node_modules/gensync/index.js:31:34
    at Array.map (<anonymous>)
    at Function.sync (/path/to/node_modules/gensync/index.js:31:22)
    at Function.all (/path/to/node_modules/gensync/index.js:210:24)
    at Generator.next (<anonymous>)
    at loadOneConfig (/path/to/node_modules/@babel/core/lib/config/files/configuration.js:131:45)
    at loadOneConfig.next (<anonymous>)
    at buildRootChain (/path/to/node_modules/@babel/core/lib/config/config-chain.js:84:51)
    at buildRootChain.next (<anonymous>)
    at loadPrivatePartialConfig (/path/to/node_modules/@babel/core/lib/config/partial.js:103:62)
    at loadPrivatePartialConfig.next (<anonymous>)
    at Function.<anonymous> (/path/to/node_modules/@babel/core/lib/config/partial.js:148:25)
    at Generator.next (<anonymous>)
    at evaluateSync (/path/to/node_modules/gensync/index.js:251:28)
    at Function.sync (/path/to/node_modules/gensync/index.js:89:14)
    at Object.<anonymous> (/path/to/node_modules/@babel/core/lib/config/index.js:47:61)
    at Object.<anonymous> (/path/to/node_modules/babel-loader/lib/index.js:151:26)
    at Generator.next (<anonymous>)
    at asyncGeneratorStep (/path/to/node_modules/babel-loader/lib/index.js:3:103)
    at _next (/path/to/node_modules/babel-loader/lib/index.js:5:194)
    at /path/to/node_modules/babel-loader/lib/index.js:5:364
    at new Promise (<anonymous>)
    at Object.<anonymous> (/path/to/node_modules/babel-loader/lib/index.js:5:97)
    at Object._loader (/path/to/node_modules/babel-loader/lib/index.js:231:18)
    at Object.loader (/path/to/node_modules/babel-loader/lib/index.js:64:18)
    at Object.<anonymous> (/path/to/node_modules/babel-loader/lib/index.js:59:12)

Thanks for your help 👍

@JLHwung
Copy link
Contributor

JLHwung commented Mar 5, 2021

We should use transformAsync and fallback to the promisified version:

const transform = promisify(babel.transform);

@nicolo-ribaudo
Copy link
Member

@JLHwung babel.transform uses a callback, so it should still work asynchronously anyway 🤔

@kroko
Copy link
Author

kroko commented Mar 6, 2021

I can not reproduce this issue locally, can you share a reproduction repo?

Here you go https://github.com/kroko/bugrepro-babel-loader-esm-01

@nicolo-ribaudo
Copy link
Member

@kroko Thanks for the reproduction, I found two problems:

  1. You are using the ESLint webpack plugin and @babel/eslint-parser. ESLint doesn't support async parsers, so Babel throws when it needs to load a .mjs file.
  2. Even if I disable the ESLint plugin, I still get this error (with Node.js 15):
    Module build failed (from ./node_modules/babel-loader/lib/index.js):
    TypeError: /home/nicolo/Documenti/dev/babel-bugs/bugrepro-babel-loader-esm-01/app-renderer/.babelrc.mjs: Error while loading config - Invalid host defined options
    
    Unfortunately this error is completely unuseful, but if I switch to Node.js 14 I get
    TypeError [ERR_VM_DYNAMIC_IMPORT_CALLBACK_MISSING]: A dynamic import callback was not specified.
      at exports.importModuleDynamicallyCallback (internal/process/esm_loader.js:34:9)
      at import_ (/home/nicolo/Documenti/dev/babel-bugs/bugrepro-babel-loader-esm-01/node_modules/@babel/core/lib/config/files/import.js:9:3)
    

This means that something (webpack-cli maybe, or webpack itself) is running babel-loader inside a Node.js vm script, and it's disabling import() calls because it does not provide the importModuleDynamically option to vm.Script.

@kroko
Copy link
Author

kroko commented Mar 6, 2021

@nicolo-ribaudo thanks for your time and insights. I guess I'll keep this ticket open as a passive tracker for if/when things change in ESLint / webpack / webpack-cli (?)

@keenwon
Copy link

keenwon commented Oct 17, 2021

I have the same issues

ERROR in ./src/index.js
Module build failed (from ./node_modules/babel-loader/lib/index.js):
TypeError: /home/keenwon/Code/test/babel.config.mjs: Error while loading config - Invalid host defined options
    at import_ (/home/keenwon/Code/test/node_modules/@babel/core/lib/config/files/import.js:9:3)
    at /home/keenwon/Code/test/node_modules/@babel/core/lib/config/files/module-types.js:100:26
    at Generator.next (<anonymous>)
    at asyncGeneratorStep (/home/keenwon/Code/test/node_modules/@babel/core/lib/config/files/module-types.js:40:103)
    at _next (/home/keenwon/Code/test/node_modules/@babel/core/lib/config/files/module-types.js:42:194)
    at /home/keenwon/Code/test/node_modules/@babel/core/lib/config/files/module-types.js:42:364
    at new Promise (<anonymous>)
    at /home/keenwon/Code/test/node_modules/@babel/core/lib/config/files/module-types.js:42:97
    at _loadMjsDefault (/home/keenwon/Code/test/node_modules/@babel/core/lib/config/files/module-types.js:103:26)
    at loadMjsDefault (/home/keenwon/Code/test/node_modules/@babel/core/lib/config/files/module-types.js:91:26)

webpack 5.58.2 compiled with 1 error in 649 ms

https://github.com/webpack/webpack/issues/14494

repo: https://github.com/keenwon/test

undergroundwires added a commit to undergroundwires/privacy.sexy that referenced this issue Aug 17, 2023
Configure project to use ES6 modules to enable top-level await
capabilities. This change helps project to align well with modern JS
standards.

- Set `type` to `module` in `package.json`.
- Use import/export syntax in Cypress configuration files.
- Rename configurations files that do not support modules to use
  the `.cjs` extension:
  - `vue.config.js` to `vue.config.cjs` (vuejs/vue-cli#4477).
  - `babel.config.js` to `babel.config.cjs (babel/babel-loader#894)
  - `.eslintrc.js` to `.eslintrc.cjs` (eslint/eslint#13440,
    eslint/eslint#14137)
  - `postcss.config.js` to `postcss.config.cjs` (postcss/postcss#1771)
undergroundwires added a commit to undergroundwires/privacy.sexy that referenced this issue Aug 17, 2023
Configure project to use ES6 modules to enable top-level await
capabilities. This change helps project to align well with modern JS
standards.

- Set `type` to `module` in `package.json`.
- Use import/export syntax in Cypress configuration files.
- Rename configurations files that do not support modules to use
  the `.cjs` extension:
  - `vue.config.js` to `vue.config.cjs` (vuejs/vue-cli#4477).
  - `babel.config.js` to `babel.config.cjs (babel/babel-loader#894)
  - `.eslintrc.js` to `.eslintrc.cjs` (eslint/eslint#13440,
    eslint/eslint#14137)
  - `postcss.config.js` to `postcss.config.cjs` (postcss/postcss#1771)
- Provide a workaround for Vue CLI & Mocha ES6 modules conflict in
  Vue configuration file (vuejs#7417).
undergroundwires added a commit to undergroundwires/privacy.sexy that referenced this issue Aug 17, 2023
Configure project to use ES6 modules to enable top-level await
capabilities. This change helps project to align well with modern JS
standards.

- Set `type` to `module` in `package.json`.
- Use import/export syntax in Cypress configuration files.
- Rename configurations files that do not support modules to use
  the `.cjs` extension:
  - `vue.config.js` to `vue.config.cjs` (vuejs/vue-cli#4477).
  - `babel.config.js` to `babel.config.cjs (babel/babel-loader#894)
  - `.eslintrc.js` to `.eslintrc.cjs` (eslint/eslint#13440,
    eslint/eslint#14137)
  - `postcss.config.js` to `postcss.config.cjs` (postcss/postcss#1771)
- Provide a workaround for Vue CLI & Mocha ES6 modules conflict in
  Vue configuration file (vuejs/vue-cli#7417).
LarrMarburger added a commit to LarrMarburger/privacy.sexy that referenced this issue Nov 16, 2023
Configure project to use ES6 modules to enable top-level await
capabilities. This change helps project to align well with modern JS
standards.

- Set `type` to `module` in `package.json`.
- Use import/export syntax in Cypress configuration files.
- Rename configurations files that do not support modules to use
  the `.cjs` extension:
  - `vue.config.js` to `vue.config.cjs` (vuejs/vue-cli#4477).
  - `babel.config.js` to `babel.config.cjs (babel/babel-loader#894)
  - `.eslintrc.js` to `.eslintrc.cjs` (eslint/eslint#13440,
    eslint/eslint#14137)
  - `postcss.config.js` to `postcss.config.cjs` (postcss/postcss#1771)
- Provide a workaround for Vue CLI & Mocha ES6 modules conflict in
  Vue configuration file (vuejs/vue-cli#7417).
@yoyo837
Copy link

yoyo837 commented May 6, 2025

It's been 4 years, is babel/babel#13352 the latest news? Can anyone update the information of this issue please?

@JLHwung
Copy link
Contributor

JLHwung commented May 6, 2025

Babel-loader v10 supports ESM Babel config:

test("should load ESM config files", async () => {
const config = Object.assign({}, globalConfig, {
entry: path.join(__dirname, "fixtures/constant.js"),
output: {
path: context.directory,
},
module: {
rules: [
{
test: /\.js$/,
loader: babelLoader,
exclude: /node_modules/,
options: {
// Use relative path starting with a dot to satisfy module loader.
// https://github.com/nodejs/node/issues/31710
// File urls doesn't work with current [email protected] package.
extends: (
"." +
path.sep +
path.relative(
process.cwd(),
path.resolve(__dirname, "fixtures/babelrc.mjs"),
)
).replace(/\\/g, "/"),
babelrc: false,
},
},
],
},
});
const stats = await webpackAsync(config);
// Node supports ESM without a flag starting from 12.13.0 and 13.2.0.
if (satisfies(process.version, `^12.13.0 || >=13.2.0`)) {
assert.deepEqual(
stats.compilation.errors.map(e => e.message),
[],
);
} else {
assert.strictEqual(stats.compilation.errors.length, 1);
const moduleBuildError = stats.compilation.errors[0];
const babelLoaderError = moduleBuildError.error;
assert.ok(babelLoaderError instanceof Error);
// Error messages are slightly different between versions:
// "modules aren't supported" or "modules not supported".
assert.match(babelLoaderError.message, /supported/i);
}
assert.deepEqual(stats.compilation.warnings, []);
});

If you spot an error like "Error while loading config - You appear to be using a native ECMAScript module configuration file, which is only supported when running Babel asynchronously.", please ensure you are using the latest version of every tool, and it does not help, please open a new issue and provide a reproducible repo.

@JLHwung JLHwung closed this as completed May 6, 2025
@yoyo837
Copy link

yoyo837 commented May 8, 2025

Sorry, I didn't notice that it is babel-loader repo, I jumped here from another babel repo issue. I encountered this issue reported under eslint 9 (esm flat config) + @babel/eslint-parser + babel.config.js (esm config).

@JLHwung
Copy link
Contributor

JLHwung commented May 8, 2025

eslint 9 (esm flat config) + @babel/eslint-parser + babel.config.js (esm config).

The ESM config support in @babel/eslint-parser is experimental, you can opt-in by

// eslint.config.js
import babelExperimentalParser from "@babel/eslint-parser/experimental-worker";

export default [
{
  languageOptions: {
      parser: babelExperimentalParser,
  }
}]

@yoyo837
Copy link

yoyo837 commented May 8, 2025

@JLHwung That's a pleasant surprise, thanks for your reply, I will try it.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

6 participants