esbuild
esbuild copied to clipboard
`import.meta` behavior inconsistency between targets
import.meta
has different instances when target
is below es2020
. But it has same instance if target
is same or above es2020
.
If it is intented to be unmodified (https://github.com/evanw/esbuild/issues/208#issuecomment-652691781) (import.meta
's scope is the output file), I think it needs to point the same instance.
current behaviors
Without bundle
main.mjs
import { sub } from './sub.mjs'
import.meta.url = 'main'
const main = () => {
console.log(import.meta.url)
}
main()
sub()
sub.mjs
import.meta.url = 'sub'
export const sub = () => {
console.log(import.meta.url)
}
When I run node main.mjs
, it outputs:
main
sub
With bundle (es2020
)
After I bundle this with esbuild --bundle --format=esm --target=es2020 main.mjs
, it becomes:
// sub.mjs
import.meta.url = "sub";
var sub = () => {
console.log(import.meta.url);
};
// main.mjs
import.meta.url = "main";
var main = () => {
console.log(import.meta.url);
};
main();
sub();
When I run this, it outputs:
main
main
With bundle (es2015
)
After I bundle this with esbuild --bundle --format=esm --target=es2015 main.mjs
, it becomes:
// sub.mjs
var import_meta = {};
import_meta.url = "sub";
var sub = () => {
console.log(import_meta.url);
};
// main.mjs
var import_meta2 = {};
import_meta2.url = "main";
var main = () => {
console.log(import_meta2.url);
};
main();
sub();
When I run this, it outputs:
main
sub
https://github.com/evanw/esbuild/commit/ac97be74c3b5453740f84fc40098ccae95cde19f You can see the reason in change log
Thanks. I know the reason. I think the output of es2015
bundle would be better to be the code below. This has more consistency with es2020
bundle output.
// sub.mjs
var import_meta = {};
import_meta.url = "sub";
var sub = () => {
console.log(import_meta.url);
};
// main.mjs
import_meta.url = "main";
var main = () => {
console.log(import_meta.url);
};
main();
sub();
Oh, sorry, I see
There is an existing workaround in tsup/cjs_shims.js by injecting import.meta.url
manually:
esbuild main.mjs --bundle --define:import.meta.url=importMetaUrl --inject:cjs_shims.js --format=esm
// cjs_shims.js
var getImportMetaUrl = () => typeof document === "undefined" ? new URL("file:" + __filename).href : document.currentScript && document.currentScript.src || new URL("main.js", document.baseURI).href;
var importMetaUrl = /* @__PURE__ */ getImportMetaUrl();
// sub.mjs
importMetaUrl = "sub";
var sub = () => {
console.log(importMetaUrl);
};
// main.mjs
importMetaUrl = "main";
var main = () => {
console.log(importMetaUrl);
};
main();
sub();
But in fact import.meta
is from ES2020. I'm not sure what's the correct behavior before that target. esbuild is replacing it with a simple object to prevent syntax error (likewise import()
becomes new Promise((r) => r(require()))
). But we still have to handle it at the most of the time, and some of them cannot be handled easily.