Skip to content

ESM that imports CJS can't be transpiled for both CJS and native ESM environments #7294

@jaydenseric

Description

@jaydenseric

Bug…

This issue arises when attempting to create a package that:

  • Transpiles both ESM (.mjs) for recent versions of Node.js run with --experimental-modules enabled, and CJS (.js) as a fallback.
  • Imports a CJS package that contains exports.__esModule.

When imported CJS in .mjs, Node.js provides a single default export representing the value of module.exports, so the syntax to use in source is:

import graphql from 'graphql'
console.log(graphql.GraphQLScalarType) // Class

This syntax survives transpilation when using preset-env with modules: false for the .mjs target and works ok when running --experimental-modules. When transpiled with modules: 'commonjs' for the .js target, it creates syntax like this:

'use strict'

var _graphql = _interopRequireDefault(require('graphql'))

function _interopRequireDefault(obj) {
  return obj && obj.__esModule ? obj : { default: obj }
}

console.log(_graphql.default.GraphQLScalarType) // Undefined

_graphql.default.GraphQLScalarType becomes causes an error as _graphql.default is undefined, because _interopRequireDefault found __esModule in the imported module and didn't wrap the exports under default.

Trying either of these approaches syntax in the source results in a functional .js target:

import { GraphQLScalarType } from 'graphql'
console.log(GraphQLScalarType)
import * as graphql from 'graphql'
console.log(graphql.GraphQLScalarType)

But breaks the .mjs target, because with --experimental-modules CJS can only be imported using default syntax.

The only workaround that seems to work in the source for both CJS and ESM targets:

import graphqlDefault, * as graphqlExports from 'graphql'

const graphql = graphqlDefault || graphqlExports

console.log(graphql.GraphQLScalarType)

Babel/Babylon Configuration (.babelrc, package.json, cli command)

.babelrc.js:

module.exports = {
  presets: [
    [
      '@babel/env',
      {
        targets: { node: '6.10' },
        shippedProposals: true,
        modules: process.env.MODULE ? false : 'commonjs',
        useBuiltIns: 'usage'
      }
    ]
  ]
}

package.json scripts:

{
  "scripts": {
    "build": "npm run build:js && npm run build:mjs",
    "build:js": "babel src --out-dir lib",
    "build:mjs": "MODULE=true babel src --out-dir lib --keep-file-extension"
  }
}

Context

I have been struggling to find an elegent solution to solve jaydenseric/graphql-upload#40. apollo-upload-server is run by users in both traditional CJS environments and with native ESM using --experimental-modules.

Your Environment

software version(s)
Babel 7.0.0-beta.38
node 8.9.3
npm 5.6.0
Operating System macOS

Metadata

Metadata

Assignees

No one assigned

    Labels

    outdatedA closed issue/PR that is archived due to age. Recommended to make a new issue

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions