Skip to content
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

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

Closed
jaydenseric opened this issue Aug 13, 2018 · 10 comments · Fixed by #10748, #10549 or #10853
Closed

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

jaydenseric opened this issue Aug 13, 2018 · 10 comments · Fixed by #10748, #10549 or #10853
Labels
area: helpers outdated A closed issue/PR that is archived due to age. Recommended to make a new issue
Milestone

Comments

@jaydenseric
Copy link

jaydenseric commented Aug 13, 2018

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.

@babel-bot
Copy link
Collaborator

Hey @jaydenseric! We really appreciate you taking the time to report an issue. The collaborators
on this project attempt to help as many people as possible, but we're a limited number of volunteers,
so it's possible this won't be addressed swiftly.

If you need any help, or just have general Babel or JavaScript questions, we have a vibrant Slack
community that typically always has someone willing to help. You can sign-up here
for an invite.

@loganfsmyth
Copy link
Member

Since Node's support for .mjs files is still quite experimental, I'm very hesitant to adopt it into the runtime. Once we do, it will mean that any package built and published to npm with a dependency on @babel/runtime could be more at risk of breakage should Node change it's behavior, and separately it means that Node itself could have a harder time landing their implementation because it they'd be more at risk of breaking published packages.

@nicolo-ribaudo
Copy link
Member

Node's ESM implementation is now unflagged.

@nicolo-ribaudo
Copy link
Member

This should now work when used with node 13.2, which is the first version supporting unflagged es modules.

@jaydenseric
Copy link
Author

Should this issue should be reopened, as the previous work that closed it has been reverted? It’s a Babel v8 checklist item that’s still todo: #10746.

@JLHwung JLHwung reopened this Feb 26, 2020
@jaydenseric
Copy link
Author

What is the best workaround for those of us that can't wait until Babel v8 and need a solution now?

The option removeImportsExtension planned for v7.8 didn't seem to eventuate.

If there is no better way, I'm going to write a plugin that rewrites every import specifier starting with @babel/runtime/helpers/ to end with an appropriate .js/.mjs file extension.

@jaydenseric
Copy link
Author

The plugin won't be able to add the required .mjs extensions to import specifiers within the helper modules though, so I guess that rules out using ESM via ['@babel/transform-runtime', { useESModules: true }] 😢

@jaydenseric
Copy link
Author

Here is what I came up with; it really complicates the .babelrc.js:

function babelPluginAddBabelRuntimeFileExtensions({ types }) {
  return {
    visitor: {
      'ImportDeclaration|ExportNamedDeclaration'(path) {
        if (
          path.node.source &&
          path.node.source.value.startsWith('@babel/runtime/helpers/') &&
          !path.node.source.value.endsWith('.js')
        )
          path
            .get('source')
            .replaceWith(types.stringLiteral(`${path.node.source.value}.js`))
      }
    }
  }
}

const plugins = [
  '@babel/transform-runtime',
  'transform-require-extensions'
]

if (process.env.BABEL_ESM)
  plugins.push(babelPluginAddBabelRuntimeFileExtensions)

module.exports = {
  plugins,
  presets: [
    [
      '@babel/env',
      {
        modules: process.env.BABEL_ESM ? false : 'cjs',
        shippedProposals: true,
        loose: true
      }
    ]
  ]
}

Publishing this hack of a plugin is a hassle, as is copy pasting it across a bunch of packages. A proper fix in Babel would be awesome 🙏

jaydenseric added a commit to jaydenseric/graphql-react that referenced this issue Mar 22, 2020
Includes a workaround for babel/babel#8462 .
@jaydenseric
Copy link
Author

To workaround this issue I've published babel-plugin-transform-runtime-file-extensions, a Babel plugin that adds file extensions to Babel runtime import specifiers and require paths for Node.js ESM compatibility.

@JLHwung
Copy link
Contributor

JLHwung commented Oct 14, 2020

Fixed in @babel/runtime@7.12.0.

lokshunhung added a commit to pinnacle0/frontend-libraries that referenced this issue Oct 27, 2020
babel/babel#8462

Webpack fails to load @babel/runtime helper functions (required by antd, rc-*) due to the import specifier not being fully resolved (missing .js, .mjs extension)

This is fixed in @babel/runtime 7.12.0 but antd does not require the latest @babel/runtime by default

So a peer dependency of @babel/runtime@>=7.12.1 (currently latest) is added
lokshunhung added a commit to pinnacle0/frontend-libraries that referenced this issue Nov 23, 2020
babel/babel#8462

Webpack fails to load @babel/runtime helper functions (required by antd, rc-*) due to the import specifier not being fully resolved (missing .js, .mjs extension)

This is fixed in @babel/runtime 7.12.0 but antd does not require the latest @babel/runtime by default

So a peer dependency of @babel/runtime@>=7.12.1 (currently latest) is added
lokshunhung added a commit to pinnacle0/frontend-libraries that referenced this issue Nov 23, 2020
babel/babel#8462

Webpack fails to load @babel/runtime helper functions (required by antd, rc-*) due to the import specifier not being fully resolved (missing .js, .mjs extension)

This is fixed in @babel/runtime 7.12.0 but antd does not require the latest @babel/runtime by default

So a peer dependency of @babel/runtime@>=7.12.1 (currently latest) is added
@github-actions github-actions bot added the outdated A closed issue/PR that is archived due to age. Recommended to make a new issue label Jan 14, 2021
@github-actions github-actions bot locked as resolved and limited conversation to collaborators Jan 14, 2021
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
area: helpers outdated A closed issue/PR that is archived due to age. Recommended to make a new issue
Projects
None yet
5 participants