You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
All of these edge cases come into play when the shims end up in a different chunk than they're used in.
CJS shim edge cases
When outputting a ESM bundle with shims enabled:
__filename will never be correct as the import.meta.url will be the chunk it's exported from, not the one it's used in.
__dirname will be incorrect if the chunk containing the shims is in a different directory than the chunk importing the shims.
dist
├── a
│ └── index.js // <- __filename will be /dist/chunk-with-shims.js, not /dist/a/index.js
├── b
│ └── index.js // <- if the __dirname shims are imported in this chunk, it will be /dist, not /dist/b
├── c
│ └── index.js
├── chunk-with-shims.js
└── ...
Edge cases when using ESM shims
For the ESM shims, import.meta.url will never be correct if the shims are in a chunk.
dist
├── a
│ └── index.js
├── b
│ └── index.js
├── c
│ └── index.js
├── chunk-with-shims.js <- the importMetaUrl export uses __filename, which will always be dist/chunk-with-shims.js
└── ...
var _shimChunk = require('../chunk-with-shims.js');
var _fs = require('fs');
var _fs2 = _interopRequireDefault(_fs);
// This is adapted from https://nodejs.org/api/esm.html#importmetaurl
// _shimChunk.importMetaUrl will be the __filename of `../chunk-with-shims.js`, not this file
const buffer = _fs2.default.readFileSync(new URL('./data.proto', _shimChunk.importMetaUrl));
import * as url from 'node:url';
if (import.meta.url.startsWith('file:')) { // (A)
// import.meta.url will be wrong if the shim ends up in a chunk, and this
// will never run when called from a shell.
const modulePath = url.fileURLToPath(import.meta.url);
if (process.argv[1] === modulePath) { // (B)
// Main ESM module
}
}
This could be done with an ESBuild plugin, which modifies the output chunks after the build, or as a tsup plugin.
This is a pretty hacky plugin I whipped up to replace the tsup shims when building ESM bundles. Having poked around the code base, (and seeing how tsup plugins work), this likely breaks sourcemaps, but they aren't turned on in our project.
import { Options } from "tsup";
import * as path from "path";
import * as fs from "fs";
// https://stackoverflow.com/a/51399781
type ArrayElement<ArrayType extends readonly unknown[]> =
ArrayType extends readonly (infer ElementType)[] ? ElementType : never;
type EsbuildPlugin = ArrayElement<NonNullable<Options["esbuildPlugins"]>>;
const CjsShimPlugin: EsbuildPlugin = {
name: "cjs-shim-plugin",
setup(build) {
build.onEnd((result) => {
const filesNeedingShims =
result.outputFiles?.filter(
(file) => file.text.match(/__dirname|__filename/) !== null
) || [];
filesNeedingShims.map((file) => {
const dirnameShim = `
import { fileURLToPath as fileURLToPathShimImport } from "node:url";
import { dirname as dirnamePathShimImport } from "node:path";
const __filename = fileURLToPathShimImport(import.meta.url);
const __dirname = dirnamePathShimImport(__filename);
`;
const shimBuffer = Buffer.from(dirnameShim);
file.contents = Buffer.concat([shimBuffer, file.contents]);
});
});
},
};
export default CjsShimPlugin;
Upvote & Fund
We're using Polar.sh so you can upvote and help fund this issue.
We receive the funding once the issue is completed & confirmed by you.
Thank you in advance for helping prioritize & fund our backlog.
The text was updated successfully, but these errors were encountered:
Avoid bundling dependencies that require shims if possible, or turn off codesplitting.
Similar issue: #914, CJS bundles with code splitting enabled is likely to break the CJS version of checking if a module is main because something generates a wrapper around require and it gets put in a different chunk, so the .main property references the shim chunk, not the chunk the shim is used in.
if (require.main === module) {
// Main CommonJS module
}
All of these edge cases come into play when the shims end up in a different chunk than they're used in.
CJS shim edge cases
When outputting a ESM bundle with shims enabled:
__filename
will never be correct as theimport.meta.url
will be the chunk it's exported from, not the one it's used in.__dirname
will be incorrect if the chunk containing the shims is in a different directory than the chunk importing the shims.Edge cases when using ESM shims
For the ESM shims,
import.meta.url
will never be correct if the shims are in a chunk.One impact of this, is that it breaks checking whether a module is
main
. https://exploringjs.com/nodejs-shell-scripting/ch_nodejs-path.html#detecting-if-module-is-mainHow to fix it
The ESM/CJS shims should be injected into each chunk that requires them, rather than using the
inject
feature ofesbuild
as it's currently done. https://github.com/egoist/tsup/blob/v7.1.0/src/esbuild/index.ts#L220This could be done with an ESBuild plugin, which modifies the output chunks after the build, or as a tsup plugin.
This is a pretty hacky plugin I whipped up to replace the
tsup
shims when building ESM bundles. Having poked around the code base, (and seeing how tsup plugins work), this likely breaks sourcemaps, but they aren't turned on in our project.Upvote & Fund
The text was updated successfully, but these errors were encountered: