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

Babel core not importable with Node.js native ESM support #11247

Closed
1 task
kriskowal opened this issue Mar 12, 2020 · 9 comments
Closed
1 task

Babel core not importable with Node.js native ESM support #11247

kriskowal opened this issue Mar 12, 2020 · 9 comments
Labels
i: question outdated A closed issue/PR that is archived due to age. Recommended to make a new issue

Comments

@kriskowal
Copy link

Bug Report

  • I would like to work on a fix!

Current Behavior

Babel version 7.8.7

import * as babelCore from '@babel/core' from an ESM in Node.js’s experimental support for ESMs produces an object with a transformSync accessor but the accessor returns undefined. This occurs when the containing package.json has "type": "module" set or when the file has the .mjs extension. The same module works fine when executed with node -r esm: babelCore.transformSync is a function. The same also works fine when required with CommonJS conventions.

Unexpected behavior for Node.js ESM support when file has .mjs extension:

// node test.mjs
import * as babelCore from "@babel/core";
console.log(babelCore.transformSync);
// (node:25847) ExperimentalWarning: The ESM module loader is experimental.
// undefined

Expected behavior: log a defined function object.

// [Function: spec]

Unexpected behavior for Node.js ESM support when file has a .js extension and module type in package.json:

// package.json
{
  "type": "module"
}
// node test.js
import * as babelCore from "@babel/core";
console.log(babelCore.transformSync);
// (node:25847) ExperimentalWarning: The ESM module loader is experimental.
// undefined

As expected, the .mjs extension or module type in package.json are required to use native import.

// node test.js
import * as babelCore from "@babel/core";
console.log(babelCore.transformSync);
// (node:25839) Warning: To load an ES module, set "type": "module" in the package.json or use the .mjs extension.
// /Users/kris/ses/temp2.js:1
// import * as babelCore from "@babel/core";
// ^^^^^^

As expected, node -r esm does access a defined function.

// node -r esm test.js
import * as babelCore from "@babel/core";
console.log(babelCore.transformSync);
// [Function: sync]

As expected, node works fine when importing as CommonJS.

// node test.js
const babelCore = require("@babel/core");
console.log(babelCore.transformSync);
// [Function: sync]

It is possible that this behavior is due to aberrant behavior of Node.js’s ESM implementation. It is likely that this is a symptom of an underlying problem.

@babel-bot
Copy link
Collaborator

Hey @kriskowal! 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."

@kriskowal
Copy link
Author

@erights recommended that I call upon @jdalton or @bmeck for help with this issue. Time permitting, I’ll attempt to isolate this further. I’m not familiar enough with ESM or Babel at this point to tell whether this is an issue that lies more squarely with Babel or Node.

@bmeck
Copy link
Contributor

bmeck commented Mar 12, 2020

This is a known issue per how CJS files are only able to generate default exports in order to conform to the ECMAScript specification and binding behavior of ESM. Babel ships a CJS build to package managers and is why this behavior exists. esm contains a variety of conveniences that hook into a variety of transpilation patterns such as the __esModule:true behavior of not creating synthetic module namespaces and otherwise picking properties to form namespaces otherwise.

@kriskowal
Copy link
Author

@bmeck, I read your statement as “This is not a Node.js bug. Using Babel as it is written today depends on crutches of ESM that will vanish soon. Babel will need to change to accommodate Node.js.” Is this correct? Can you propose a rule that Babel would need to follow to straddle CJS and ESM, like “Avoid default exports.”?

@bmeck
Copy link
Contributor

bmeck commented Mar 12, 2020

@kriskowal I wouldn't phrase it as such since I don't actually think this is a bug but an integration problem by the nature of the transpilation; note that babel file in question is CJS and CJS does not export named bindings in Node. I wouldn't state that importing CJS is broken on Node's side, nor would I state that babel creating CJS from ESM input is broken. They just don't mix / no solutions have been able to be found. There are lots of issues exactly about this topic, from things on Node trackers, at TC39, and many more. In general mixing the transpiled ecosystem and spec ESM simply doesn't work as it stands. So, once you cross the transpilation barrier things must adhere to their compilation target expectations (in the case of @babel/core that is CJS). I think babel could output ESM instead of CJS like rollup does, but even then more integration issues likely remain.

@nicolo-ribaudo
Copy link
Member

We might decide to change this in Babel 8, but in the meantime use import babelCore from "@babel/core".

@kriskowal
Copy link
Author

@nicolo-ribaudo Thank you! That is a fine work-around. Feel free to close if you do not require this task to track.

@nicolo-ribaudo
Copy link
Member

I'll defer the decision to @JLHwung, since he recently started investigating Node.js's new export maps which are related to this.

@JLHwung
Copy link
Contributor

JLHwung commented Mar 12, 2020

I agree on what Bradley has said before: @babel/core is shipped in commonjs, if you are looking at the entry file, it is like

exports.transformSync = require("./transform").transformSync

The ESM named import import { transformSync } from "@babel/core" is never expected to work with commonjs exports.transformSync.

The esm will transform the commonjs exports to analogous esm exports so node -r esm works in your case.

We are likely to ship dual cjs/esm builds of babel on Babel 9, which is expected in next year. So in the future it should be valid to do

import * as babelCore from "@babel/core" // the esm entry is imported

At this moment the Node.js ES Module API is still experimental and Babel 8 is on the horizon, it is risky to ship esm builds for Babel 8. Before that please use

import babelCore from "@babel/core"

since @babel/core, although written in ESM, is actually shipped in commonjs format.

I am gonna close that because the current situation is expected.

@JLHwung JLHwung closed this as completed Mar 12, 2020
@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 Jun 12, 2020
@github-actions github-actions bot locked as resolved and limited conversation to collaborators Jun 12, 2020
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
i: question outdated A closed issue/PR that is archived due to age. Recommended to make a new issue
Projects
None yet
Development

No branches or pull requests

5 participants