`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.