Barrel files (containing only exports) are considered CommonJS
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!
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.
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?
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-s3external explicitly. See also #1958. I'm planning to try fixing this in the next breaking change release by changing the--externalfeature 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?