bundling in ESM with chunk splitting is confusing an ESM dependency with a commonJS dependency
I'm doing a bundling with splitting in ESM I have a lazy loading of the dependency react-slick, This one is using an export way that mx commonJs and ESM
In the bundling it is undestood as a commonJs and wrapped with __commonJs but the export was already having a "default". It result with an export like this { default: { default: ReactSlick component } }
See below code
esbuild config:
const buildResult = await esbuild.build({
entryPoints: ['src/index.ts'],
outdir: 'dist',
write: false,
bundle: true,
splitting: true,
minify: false,
format: 'esm',
sourcemap: true,
external: ['react', 'react-dom'],
plugins: [
sassPlugin()
],
loader: {
'.svg': 'file',
'.woff2': 'file',
},
assetNames: 'assets/[name]-[hash]'
});
parent file:
const Slider = lazy(() => import('react-slick'));
build result:
var Slider3 = lazy7(() => import("./lib-CCBX26FZ.js"));
react-slice/lib/index.js
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports["default"] = void 0;
var _slider = _interopRequireDefault(require("./slider"));
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { "default": obj }; }
var _default = exports["default"] = _slider["default"];
chunk result (lib-CCBX26FZ.js):
// ../../../node_modules/react-slick/lib/index.js
var require_lib = __commonJS({
"../../../node_modules/react-slick/lib/index.js"(exports) {
Object.defineProperty(exports, "__esModule", {
value: true
});
exports["default"] = void 0;
var _slider = _interopRequireDefault(require_slider());
function _interopRequireDefault(obj) {
return obj && obj.__esModule ? obj : { "default": obj };
}
var _default = exports["default"] = _slider["default"];
}
});
export default require_lib();
//# sourceMappingURL=lib-CCBX26FZ.js.map
_interopRequireDefault is injecting the __esModule: true to explain it is an ESM export
require_lib() here returns {__esModule: true, default: ƒ}. And we do export default of this, that adds another default level.
The __commonJs function might detect it to remove the "default" level.
Although some of @paztis's word wasn't correct, the main issue regards to using splitting on a commonjs entry point (which was created by a dynamic import).
esbuild only handles code splitting on ESM entries. So if one entry is in commonjs, it is converted to ESM first. How does it convert? esbuild simply wraps the cjs code in a callback and assigns the whole module.exports to the ESM's default export. That is to say, a commonjs entry could only have only one export in ESM context (which is known as the Node.js behavior).
_interopRequireDefault is injecting the __esModule: true to explain it is an ESM export
-
The
__esModuleannotation means that if possible, the bundler should treatmodule.exportsas the same meaning as an es-module's namespace object. This is known as the babel behavior.But it isn't work in this case because the whole commonjs module is the entrypoint instead of being bundled in some chunk. You can think of re-exporting every named exports from a random commonjs code, which requires a static analyze on the code.
-
_interopRequireDefaultis simply a helper function to extract thedefaultexport from either a normal commonjs module or a module that adopts the babel style exports. This is the trick to simulate an es-module when there's no native ESM support.
There're some further steps could take:
-
For esbuild, since the code splitting function is still experimental, it is fine to live with such small issues. But if any improvement could make, it would be:
-
If a commonjs module is the only one default export in an ESM chunk:
export default require_lib()...then extract the
defaultexport like babel:export default __toESM(require_lib()).default
-
-
For users, you can either turn off the code splitting, or manually extract the exports by using a wrapper when you already know the module is in commonjs and you only want some names from it, example.
-
For library authors, just move to ESM!
Here i'm a consumer of a 3rd part library. I'm not responsable of their code or their lazy imports. I also don't want to ask them to do code change while their code is correct because esbuild cannot process them.
I want code splitting otherwise all lazy mecanism became useless.
I want esbuild to support thèse side case by itself without any modification on my side, like webpack do for example. It is just a security they can add on hier side
Le mar. 25 mars 2025, 13:58, hyrious @.***> a écrit :
Although some of @paztis https://github.com/paztis's word wasn't correct, the main issue regards to using splitting on a commonjs entry point (which was created by a dynamic import).
esbuild only handles code splitting on ESM entries. So if one entry is in commonjs, it is converted to ESM first. How does it convert? esbuild simply wraps the cjs code in a callback and assigns the whole module.exports to the ESM's default export. That is to say, a commonjs entry could only have only one export in ESM context (which is known as the Node.js behavior https://esbuild.github.io/content-types/#default-interop).
_interopRequireDefault is injecting the __esModule: true to explain it is an ESM export
The __esModule annotation means that if possible, the bundler should treat module.exports as the same meaning as an es-module's namespace object. This is known as the babel behavior.
But it isn't work in this case because the whole commonjs module is the entrypoint instead of being bundled in some chunk. You can think of re-exporting every named exports from a random commonjs code, which requires a static analyze on the code. 2.
_interopRequireDefault is simply a helper function to extract the default export from either a normal commonjs module or a module that adopts the babel style exports. This is the trick to simulate an es-module when there's no native ESM support.
There're some further steps could take:
For esbuild, since the code splitting function is still experimental, it is fine to live with such small issues. But if any improvement could make, it would be:
If a commonjs module is the only one default export in an ESM chunk: export default require_lib() ...then extract the default export like babel: export default __toESM(require_lib()).default -For users, you can either turn off the code splitting, or manually extract the exports by using a wrapper when you already know the module is in commonjs and you only want some names from it, example https://esbuild.github.io/try/#YgAwLjI1LjEALS1idW5kbGUgLS1mb3JtYXQ9ZXNtIC0tc3BsaXR0aW5nIC0tb3V0ZGlyPS4AZQBlbnRyeS5qcwBsYXp5KCgpID0+IGltcG9ydCgnLi9saWItd3JhcHBlcicpKQAAbGliLmpzAGV4cG9ydHMuX19lc01vZHVsZSA9IHRydWUKZXhwb3J0cy5kZWZhdWx0ID0gNDIAAGxpYi13cmFwcGVyLmpzAGltcG9ydCBsaWIgZnJvbSAnLi9saWInCmV4cG9ydCBkZWZhdWx0IGxpYg .
For library authors, just move to ESM!
— Reply to this email directly, view it on GitHub https://github.com/evanw/esbuild/issues/4118#issuecomment-2751177473, or unsubscribe https://github.com/notifications/unsubscribe-auth/ABIKMMIBRA7L6TFREQVM5CD2WFHIDAVCNFSM6AAAAABZM4RVX2VHI2DSMVQWIX3LMV43OSLTON2WKQ3PNVWWK3TUHMZDONJRGE3TONBXGM . You are receiving this because you were mentioned.Message ID: @.***> [image: hyrious]hyrious left a comment (evanw/esbuild#4118) https://github.com/evanw/esbuild/issues/4118#issuecomment-2751177473
Although some of @paztis https://github.com/paztis's word wasn't correct, the main issue regards to using splitting on a commonjs entry point (which was created by a dynamic import).
esbuild only handles code splitting on ESM entries. So if one entry is in commonjs, it is converted to ESM first. How does it convert? esbuild simply wraps the cjs code in a callback and assigns the whole module.exports to the ESM's default export. That is to say, a commonjs entry could only have only one export in ESM context (which is known as the Node.js behavior https://esbuild.github.io/content-types/#default-interop).
_interopRequireDefault is injecting the __esModule: true to explain it is an ESM export
The __esModule annotation means that if possible, the bundler should treat module.exports as the same meaning as an es-module's namespace object. This is known as the babel behavior.
But it isn't work in this case because the whole commonjs module is the entrypoint instead of being bundled in some chunk. You can think of re-exporting every named exports from a random commonjs code, which requires a static analyze on the code. 2.
_interopRequireDefault is simply a helper function to extract the default export from either a normal commonjs module or a module that adopts the babel style exports. This is the trick to simulate an es-module when there's no native ESM support.
There're some further steps could take:
For esbuild, since the code splitting function is still experimental, it is fine to live with such small issues. But if any improvement could make, it would be:
If a commonjs module is the only one default export in an ESM chunk: export default require_lib() ...then extract the default export like babel: export default __toESM(require_lib()).default -For users, you can either turn off the code splitting, or manually extract the exports by using a wrapper when you already know the module is in commonjs and you only want some names from it, example https://esbuild.github.io/try/#YgAwLjI1LjEALS1idW5kbGUgLS1mb3JtYXQ9ZXNtIC0tc3BsaXR0aW5nIC0tb3V0ZGlyPS4AZQBlbnRyeS5qcwBsYXp5KCgpID0+IGltcG9ydCgnLi9saWItd3JhcHBlcicpKQAAbGliLmpzAGV4cG9ydHMuX19lc01vZHVsZSA9IHRydWUKZXhwb3J0cy5kZWZhdWx0ID0gNDIAAGxpYi13cmFwcGVyLmpzAGltcG9ydCBsaWIgZnJvbSAnLi9saWInCmV4cG9ydCBkZWZhdWx0IGxpYg .
For library authors, just move to ESM!
— Reply to this email directly, view it on GitHub https://github.com/evanw/esbuild/issues/4118#issuecomment-2751177473, or unsubscribe https://github.com/notifications/unsubscribe-auth/ABIKMMIBRA7L6TFREQVM5CD2WFHIDAVCNFSM6AAAAABZM4RVX2VHI2DSMVQWIX3LMV43OSLTON2WKQ3PNVWWK3TUHMZDONJRGE3TONBXGM . You are receiving this because you were mentioned.Message ID: @.***>
I have same error
use @langchain/community/document_loaders/fs/docx
code=>langchain=>import('mammoth')(dynamic import commonjs package)
mammoth export with {default:xxx} but import without default
In fact, there are currently many packages that mix esm/cjs imports, and I think a solution should be developed