esbuild icon indicating copy to clipboard operation
esbuild copied to clipboard

Barrel files (containing only exports) are considered CommonJS

Open zofiag opened this issue 3 years ago • 3 comments

Background

There's a common pattern to point main or module to a barrel file, which contains exports from other files. An example dependency @aws-sdk/client-s3 has a module entry which points to es module version of their code. This file contains only export * entries. Even though it's an es modules version, Node still treats it as a CommonJS and throws an error.

Reproduction repository

https://github.com/zofiag/esm node version: 16.14.2

npm install
npm run run

Expected result: No errors, node can run built files (dist/index.js).

Actual result: node_modules/@aws-sdk/client-s3/dist-es/index.js is considered CommonJS even though it is not. That file contains export * entries that point to other es modules files.

file:///.../esm/dist/index.js:2
import { S3Client } from "../node_modules/@aws-sdk/client-s3/dist-es/index.js";
         ^^^^^^^^
SyntaxError: Named export 'S3Client' not found. The requested module '../node_modules/@aws-sdk/client-s3/dist-es/index.js' is a CommonJS module, which may not support all module.exports as named exports.
CommonJS modules can always be imported via the default export, for example using:

import pkg from '../node_modules/@aws-sdk/client-s3/dist-es/index.js';
const { S3Client } = pkg;

    at ModuleJob._instantiate (node:internal/modules/esm/module_job:127:21)
    at async ModuleJob.run (node:internal/modules/esm/module_job:191:5)
    at async Promise.all (index 0)
    at async ESMLoader.import (node:internal/modules/esm/loader:337:24)
    at async loadESM (node:internal/process/esm_loader:88:5)
    at async handleMainPromise (node:internal/modules/run_main:61:12)
Error - exit Error: exit code 1
    at ChildProcess.<anonymous> (file:///.../esm/build.js:35:37)
    at ChildProcess.emit (node:events:520:28)
    at Process.ChildProcess._handle.onexit (node:internal/child_process:291:12)

Notes

It seems to be more of a Node issue, but I couldn't find any useful info about it. Hope someone can help!

zofiag avatar Apr 07 '22 15:04 zofiag

I believe the interpretation as CommonJS may be due to the use of external: ["./node_modules/*"]. I was able to get your code to work by making @aws-sdk/client-s3 external explicitly. See also #1958. I'm planning to try fixing this in the next breaking change release by changing the --external feature to no longer rewrite external paths. I haven't made this change yet because it's a breaking change.

evanw avatar Apr 07 '22 15:04 evanw

Thanks for the answer! I did try making @aws-sdk/client-s3 external explicitly and then dist/index.js is basically the same as index.js file. Which means that if I then run node index.js or node dist/index.js the CommonJS version of @aws-sdk/client-s3 is used instead of the esm one (@aws-sdk/client-s3/package.json has main pointing to CommonJS version).

  "main": "./dist-cjs/index.js",
  "types": "./dist-types/index.d.ts",
  "module": "./dist-es/index.js",

Using CommonJS version doesn't throw any errors. But if we enforce using esm version then we end up with the error I posted originally (SyntaxError: Named export 'S3Client' not found. The requested module '../node_modules/@aws-sdk/client-s3/dist-es/index.js' is a CommonJS module...).

Do you know a way of making esbuild/nodeuse esm version of a dependency (module entry from package.json) without throwing the above error?

zofiag avatar Apr 08 '22 07:04 zofiag

I believe the interpretation as CommonJS may be due to the use of external: ["./node_modules/*"]. I was able to get your code to work by making @aws-sdk/client-s3 external explicitly. See also #1958. I'm planning to try fixing this in the next breaking change release by changing the --external feature to no longer rewrite external paths. I haven't made this change yet because it's a breaking change.

@evanw how does external make the modules load as commonjs? i'm trying to make a esm module with esbuild. When using this module in an app, it is trying to load dependent modules as commonjs.

is there any temporary workaround for this?

ShravanSunder avatar May 31 '22 13:05 ShravanSunder