-
-
Notifications
You must be signed in to change notification settings - Fork 32.2k
Description
- Version:
v14.4.0
- Platform: Windows 10
- Subsystem: Windows
What steps will reproduce the bug?
git clone [email protected]:MicahZoltu/dual-module-package-repro.git
cd dual-module-package-repro/app-package
npm install
node index.mjs
# notice it works
node index.cjs
# notice it does not work
Then remove "type": "module"
from library-package/package.json
and repeat the process:
npm install
node index.mjs
# notice it doesn't work
node index.cjs
# notice it does work
How often does it reproduce? Is there a required condition?
Always.
What is the expected behavior?
The ability to ship an NPM package that can be used by either CJS users (any version of NodeJS) or ESM users (running NodeJS 14+)
What do you see instead?
I can either define type: module
and it will work as an ESM, or I can not define it or set it to CommonJS and it will work as a CJS module, but I cannot define the package.json such that the package works in both environments.
Additional information
The error indicates that NodeJS is correctly following the exports
path and locating the right module in each situation, however it proceeds to load the module using a loader picked by the presence of the type
property in package.json
. My expectation is that if it loads via the exports: { import: ... }
entrypoint then that entire call stack from there down should be ESM, and if it loads via exports: { require: ... }
then the entire call stack from there down should be CJS.
Error when type: module
is not set:
dual-module-package-repro\library-package\index-esm.js:1
export const apple = 'apple'
^^^^^^
SyntaxError: Unexpected token 'export'
at wrapSafe (internal/modules/cjs/loader.js:1116:16)
Error when type: module
is set:
internal/modules/cjs/loader.js:1216
throw new ERR_REQUIRE_ESM(filename, parentPath, packageJsonPath);
^
Error [ERR_REQUIRE_ESM]: Must use import to load ES Module: ...\dual-module-package-repro\library-package\index-cjs.js
require() of ES modules is not supported.
require() of ...\dual-module-package-repro\library-package\index-cjs.js from ...\dual-module-package-repro\app-package\index.cjs is an ES module file as it is a .js file whose nearest parent package.json contains "type": "module" which defines all .js files in that package scope as ES modules.
Instead rename index-cjs.js to end in .cjs, change the requiring code to use import(), or remove "type": "module" from ...\dual-module-package-repro\library-package\package.json.
at Object.Module._extensions..js (internal/modules/cjs/loader.js:1216:13)
The documentation makes it sound like NodeJS 14 supports packages that can be used as either ESM or CJS, but thus far I have not figured out how to actually accomplish this.
I believe this specific problem could be worked around by renaming files to .cjs
and .mjs
in the library-package
. However, in the real world this solution is far less tenable because I'm compiling from TypeScript which does not do import rewrites during emit (and they maintain a very strong position that they have no intent to ever change this policy). This means that I cannot have TypeScript emit files where the import statements have different extensions depending on the module type being targeted. So in order to have everything be mjs
in the ESM build output and cjs
in the CJS build output I would need to run my code through a transformer that rewrites all import statements (both static and dynamic) and also rename all of the files to have extensions by output folder.