Skip to content

ESM runtime helper files should have the .mjs extension #8462

@jaydenseric

Description

@jaydenseric

Bug Report

Current behavior

Config

{
  presets: [
    [
      '@babel/env',
      {
        targets: {
          node: '6.10',
          browsers: '>1%'
        },
        modules: false,
        shippedProposals: true,
        loose: true
      }
    ]
  ],
  plugins: [
    ['@babel/transform-runtime', { useESModules: true }]
  ]
}

Input code

import React from 'react'

class Demo extends React.Component {}

Outputs:

import _inheritsLoose from '@babel/runtime/helpers/esm/inheritsLoose'
import React from 'react'

var Demo = (function(_React$Component) {
  _inheritsLoose(Demo, _React$Component)

  function Demo() {
    return _React$Component.apply(this, arguments) || this
  }

  return Demo
})(React.Component)

When run as native ESM in Node.js with --experimental-modules enabled:

node --experimental-modules lib/demo.mjs

@babel/runtime/helpers/esm/inheritsLoose resolves to node_modules/@babel/runtime/helpers/esm/inheritsLoose.js. Because Node.js can only run native ESM in files with the .mjs extension, this results in a runtime error:

$ node --experimental-modules lib/demo.mjs
(node:97782) ExperimentalWarning: The ESM module loader is experimental.
/Users/jaydenseric/Sites/demo/node_modules/@babel/runtime/helpers/esm/inheritsLoose.js:1
(function (exports, require, module, __filename, __dirname) { export default function _inheritsLoose(subClass, superClass) {
                                                              ^^^^^^

SyntaxError: Unexpected token export
    at new Script (vm.js:74:7)
    at createScript (vm.js:246:10)
    at Proxy.runInThisContext (vm.js:298:10)
    at Module._compile (internal/modules/cjs/loader.js:657:28)
    at Object.Module._extensions..js (internal/modules/cjs/loader.js:700:10)
    at Module.load (internal/modules/cjs/loader.js:599:32)
    at tryModuleLoad (internal/modules/cjs/loader.js:538:12)
    at Function.Module._load (internal/modules/cjs/loader.js:530:3)
    at createDynamicModule (internal/modules/esm/translators.js:56:15)
    at setExecutor (internal/modules/esm/create_dynamic_module.js:50:23)

Expected behavior/code

The ESM helper import should resolve a helper file with a .mjs extension.

Environment

  • Babel version(s): v7.0.0-rc.1
  • Node/npm version: Node.js v10.8.0 / npm v6.3.0
  • OS: macOS v10.13.6
  • Monorepo: No
  • How you are using Babel: CLI

Possible Solution

Allow Babel helpers to be consumed in native CJS or ESM Node.js environments by publishing sibling .js (CJS) and .mjs (ESM) files. They should be imported using the same path so the Node.js import resolution algorithm will select the appropriate file for the environment:

- import _inheritsLoose from '@babel/runtime/helpers/esm/inheritsLoose'
+ import _inheritsLoose from '@babel/runtime/helpers/inheritsLoose'

Webpack and other tools have adopted the same resolution algorithm; Webpack actually prefers .mjs files for ESM.

Additional context/Screenshots

I would like to be able to generate an ESM build, that imports ESM helpers, that can run as native ESM in Node.js with --experimental-modules enabled.

Metadata

Metadata

Assignees

No one assigned

    Labels

    area: helpersoutdatedA closed issue/PR that is archived due to age. Recommended to make a new issue

    Type

    No type

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions