From 684e81be92218bd8a026add1e31e5ba81563513b Mon Sep 17 00:00:00 2001 From: robobun <117481402+robobun@users.noreply.github.com> Date: Mon, 2 Nov 2026 11:33:57 +0000 Subject: [PATCH 1/2] bundler: decide import.meta.main keep/lower from the entry point's own target The printer kept import.meta.main for esm output only and read the bundle's target. The parser recorded the __require use of the lowered form for target node only and read the file's target. The two disagreed for an iife for bun and for a `#!/usr/bin/env bun` entry point built for another target: the output printed __require with no definition. Format::keeps_import_meta_main(target) is now the one rule. The printer and ParseTask both call it with the target the file was parsed for. An iife for bun keeps import.meta.main: Bun loads a `// @bun` file as an ES module. --- src/bundler/LinkerContext.rs | 3 +- src/bundler/ParseTask.rs | 4 +- src/bundler/transpiler.rs | 2 +- src/js_parser/p.rs | 2 +- src/js_parser/parse/parse_entry.rs | 9 +- src/js_printer/lib.rs | 8 +- src/options_types/bundle_enums.rs | 9 ++ test/bundler/bundler_edgecase.test.ts | 126 ++++++++++++++++++++++++++ 8 files changed, 150 insertions(+), 13 deletions(-) diff --git a/src/bundler/LinkerContext.rs b/src/bundler/LinkerContext.rs index 60649fdd6f50..180c9b3ff8bf 100644 --- a/src/bundler/LinkerContext.rs +++ b/src/bundler/LinkerContext.rs @@ -2426,7 +2426,8 @@ impl<'a> LinkerContext<'a> { line_offset_tables: Some( &self.graph.files.items_line_offset_table()[source_index.get() as usize], ), - target: self.options.target, + // A `#!/usr/bin/env bun` entry point can be parsed for a target other than the bundle's. + target: ast.target, hmr_ref: if self.options.output_format == Format::InternalBakeDev { ast.wrapper_ref diff --git a/src/bundler/ParseTask.rs b/src/bundler/ParseTask.rs index 4b4cf26f9ded..f9e5d1f5d174 100644 --- a/src/bundler/ParseTask.rs +++ b/src/bundler/ParseTask.rs @@ -2625,8 +2625,8 @@ pub mod parse_worker { // in which we inline `true`. if topts.inline_entrypoint_import_meta_main || !task.is_entry_point { opts.import_meta_main_value = Some(task.is_entry_point && !topts.has_dev_server()); - } else if target == options::Target::Node { - opts.lower_import_meta_main_for_node_js = true; + } else { + opts.lower_import_meta_main = !output_format.keeps_import_meta_main(target); } opts.tree_shaking = if task.source_index.is_runtime() { diff --git a/src/bundler/transpiler.rs b/src/bundler/transpiler.rs index 90e748d3c5c4..691434566b4e 100644 --- a/src/bundler/transpiler.rs +++ b/src/bundler/transpiler.rs @@ -1504,7 +1504,7 @@ impl<'a> Transpiler<'a> { transform_only: self.options.transform_only, import_meta_glob: None, import_meta_main_value: None, - lower_import_meta_main_for_node_js: false, + lower_import_meta_main: false, framework: None, repl_mode: self.options.repl_mode, lower_toml_datetimes: false, diff --git a/src/js_parser/p.rs b/src/js_parser/p.rs index 7af9d48c2bda..dda03901c59e 100644 --- a/src/js_parser/p.rs +++ b/src/js_parser/p.rs @@ -6084,7 +6084,7 @@ impl<'a, const TYPESCRIPT: bool, const SCAN_ONLY: bool> P<'a, TYPESCRIPT, SCAN_O // // The printer can handle this for us, but we need to reference // a handle to the `__require` function. - if self.options.lower_import_meta_main_for_node_js { + if self.options.lower_import_meta_main { self.record_usage_of_runtime_require(); } Expr { diff --git a/src/js_parser/parse/parse_entry.rs b/src/js_parser/parse/parse_entry.rs index 026bfc8ba168..e30e80de5c1a 100644 --- a/src/js_parser/parse/parse_entry.rs +++ b/src/js_parser/parse/parse_entry.rs @@ -181,7 +181,8 @@ pub struct Options<'a> { /// Used for inlining the state of import.meta.main during visiting pub import_meta_main_value: Option, - pub lower_import_meta_main_for_node_js: bool, + /// The printer lowers `import.meta.main`: see `Format::keeps_import_meta_main`. + pub lower_import_meta_main: bool, /// When using react fast refresh or server components, the framework is /// able to customize what import sources are used. @@ -231,7 +232,7 @@ impl<'a> Default for Options<'a> { transform_only: false, import_meta_glob: None, import_meta_main_value: None, - lower_import_meta_main_for_node_js: false, + lower_import_meta_main: false, framework: None, repl_mode: false, lower_toml_datetimes: false, @@ -326,7 +327,7 @@ impl<'a> Options<'a> { transform_only: self.transform_only, import_meta_glob: self.import_meta_glob, import_meta_main_value: self.import_meta_main_value, - lower_import_meta_main_for_node_js: self.lower_import_meta_main_for_node_js, + lower_import_meta_main: self.lower_import_meta_main, framework: self.framework, repl_mode: self.repl_mode, lower_toml_datetimes: self.lower_toml_datetimes, @@ -410,7 +411,7 @@ impl<'a> Options<'a> { transform_only: false, import_meta_glob: None, import_meta_main_value: None, - lower_import_meta_main_for_node_js: false, + lower_import_meta_main: false, framework: None, repl_mode: false, lower_toml_datetimes: loader == options::Loader::Toml, diff --git a/src/js_printer/lib.rs b/src/js_printer/lib.rs index 264fdd74b3c7..bc7e783d0104 100644 --- a/src/js_printer/lib.rs +++ b/src/js_printer/lib.rs @@ -3286,11 +3286,11 @@ pub(crate) mod __gated_printer { } } ExprData::EImportMetaMain(data) => { - if self.options.module_type == bundle_opts::Format::Esm - && self.options.target != bun_ast::Target::Node + if self + .options + .module_type + .keeps_import_meta_main(self.options.target) { - // Node.js doesn't support import.meta.main - // Most of the time, leave it in there let wrap = data.inverted && level.gte(Level::Prefix); if wrap { self.print(b"("); diff --git a/src/options_types/bundle_enums.rs b/src/options_types/bundle_enums.rs index 796703191ba3..3f18e61fadb2 100644 --- a/src/options_types/bundle_enums.rs +++ b/src/options_types/bundle_enums.rs @@ -47,6 +47,15 @@ impl Format { self == Format::Esm } + /// Whether `import.meta.main` of an entry point parsed for `target` is printed as written. + pub fn keeps_import_meta_main(self, target: Target) -> bool { + match self { + Format::Esm => !target.is_node(), + Format::Iife => target.is_bun(), // Bun loads its `// @bun` output as an ES module. + Format::Cjs | Format::InternalBakeDev => false, + } + } + pub const MAP: __ComptimeStringMap_FORMAT_MAP = __ComptimeStringMap_FORMAT_MAP(()); // `to_js`/`from_js` live as extension-trait methods in the `*_jsc` crate. diff --git a/test/bundler/bundler_edgecase.test.ts b/test/bundler/bundler_edgecase.test.ts index b65ae9be6569..1ee3c7a04a0c 100644 --- a/test/bundler/bundler_edgecase.test.ts +++ b/test/bundler/bundler_edgecase.test.ts @@ -2444,6 +2444,132 @@ describe("bundler", () => { api.expectFile("/out.js").toMatch(/[^\.:]module/); // `.module` and `node:module` are not ok. }, }); + // The entry point has four forms of the check. `other` is not an entry + // point: its two fold to `false`. A bundle runs as the main module, then + // required from runner.cjs, which flips each value of the entry point. + const importMetaMainFiles = { + "/entry.ts": /* js */ ` + import {other} from './other'; + console.log( + capture(import.meta.main), + capture(require.main === module), + capture(!import.meta.main), + capture(require.main !== module), + ...other, + ); + `, + "/other.ts": /* js */ ` + globalThis['ca' + 'pture'] = x => x; + + export const other = [capture(require.main === module), capture(import.meta.main)]; + `, + }; + const importMetaMainRunner = { + "/runner.cjs": /* js */ `require("./out.js");`, + }; + const importMetaMainAsMain = "true true false false false false"; + const importMetaMainWhenRequired = "false false true true false false"; + const importMetaMainKept = [ + "false", + "false", + "import.meta.main", + "import.meta.main", + "!import.meta.main", + "!import.meta.main", + ]; + // Bun loads a file that starts with `// @bun` as an ES module, unless the + // pragma also says `@bun-cjs` (cjs output). An iife for bun has + // import.meta.main and has no `require` or `module`. + itBundled("edgecase/ImportMetaMainIIFETargetBun", { + files: importMetaMainFiles, + runtimeFiles: importMetaMainRunner, + target: "bun", + format: "iife", + capture: importMetaMainKept, + onAfterBundle(api) { + api.expectFile("/out.js").toStartWith("// @bun\n"); + api.expectFile("/out.js").not.toContain("require"); + api.expectFile("/out.js").not.toContain("module"); + }, + run: [{ stdout: importMetaMainAsMain }, { file: "/runner.cjs", stdout: importMetaMainWhenRequired }], + }); + itBundled("edgecase/ImportMetaMainCJSTargetBun", { + files: importMetaMainFiles, + runtimeFiles: importMetaMainRunner, + target: "bun", + format: "cjs", + capture: [ + "false", + "false", + "require.main == module", + "require.main == module", + "!(require.main == module)", + "require.main != module", + ], + onAfterBundle(api) { + api.expectFile("/out.js").toStartWith("// @bun @bun-cjs\n"); + }, + run: [{ stdout: importMetaMainAsMain }, { file: "/runner.cjs", stdout: importMetaMainWhenRequired }], + }); + // A build with no target builds an entry point that starts with + // `#!/usr/bin/env bun` for bun: its output has the `// @bun` pragma. itBundled + // always gives a target, so these two build directly. + for (const backend of ["cli", "api"] as const) { + test.concurrent(`edgecase/ImportMetaMainBunHashbangNoTargetIIFE-${backend}`, async () => { + using dir = tempDir("import-meta-main-bun-hashbang", { + "entry.ts": "#!/usr/bin/env bun\n" + importMetaMainFiles["/entry.ts"], + "other.ts": importMetaMainFiles["/other.ts"], + "runner.cjs": importMetaMainRunner["/runner.cjs"], + "build.ts": /* js */ ` + const { success, logs } = await Bun.build({ + entrypoints: ["./entry.ts"], + format: "iife", + outdir: ".", + naming: "out.js", + }); + if (!success) throw new AggregateError(logs); + `, + }); + const spawn = async (...args: string[]) => { + await using proc = Bun.spawn({ + cmd: [bunExe(), ...args], + cwd: String(dir), + env: bunEnv, + stdio: ["ignore", "pipe", "pipe"], + }); + const [stdout, stderr, exitCode] = await Promise.all([proc.stdout.text(), proc.stderr.text(), proc.exited]); + return { stdout, stderr, exitCode }; + }; + const build = await spawn( + ...(backend === "cli" ? ["build", "entry.ts", "--format=iife", "--outfile=out.js"] : ["build.ts"]), + ); + expect({ stderr: build.stderr, exitCode: build.exitCode }).toEqual({ stderr: "", exitCode: 0 }); + const out = await Bun.file(join(String(dir), "out.js")).text(); + expect(out).toStartWith("#!/usr/bin/env bun\n// @bun\n"); + expect(out).not.toContain("__require"); + expect(await spawn("out.js")).toEqual({ stdout: importMetaMainAsMain + "\n", stderr: "", exitCode: 0 }); + expect(await spawn("runner.cjs")).toEqual({ + stdout: importMetaMainWhenRequired + "\n", + stderr: "", + exitCode: 0, + }); + }); + } + // `bun build --no-bundle --target=bun` prints for the format of the command line. + itBundled("edgecase/ImportMetaMainNoBundleIIFETargetBun", { + files: { + "/entry.ts": /* js */ ` + console.log(import.meta.main, require.main === module, !import.meta.main); + `, + }, + bundling: false, + target: "bun", + format: "iife", + onAfterBundle(api) { + api.expectFile("/out.js").toBe("console.log(import.meta.main, import.meta.main, !import.meta.main);\n"); + }, + run: { stdout: "true true false" }, + }); itBundled("edgecase/IdentifierInEnum#13081", { files: { "/entry.ts": ` From b617bbbd79f5c20786fdd37eb3918754989e9431 Mon Sep 17 00:00:00 2001 From: robobun <117481402+robobun@users.noreply.github.com> Date: Mon, 2 Nov 2026 11:42:24 +0000 Subject: [PATCH 2/2] bundler: define __require in iife output The linker printed the runtime's __require for every format but cjs. The parser imported the part that defines it for esm only. In iife output the name was printed and never defined. should_call_runtime_require(format) is now the one rule. The parser flag that imports __require and the linker sites that print and record it all call it. An iife for node uses the definition that reads the require of the host: an iife has no import statement and no import.meta.url. The iife lowering of import.meta.main compares against the module of the host, behind a typeof guard. A script with no module reads false. --- src/bundler/LinkerContext.rs | 5 +- src/bundler/ParseTask.rs | 37 +- src/bundler/bundle_v2.rs | 5 +- .../generateCodeForLazyExport.rs | 2 +- .../generateCompileResultForJSChunk.rs | 16 +- .../linker_context/postProcessJSChunk.rs | 10 - .../linker_context/scanImportsAndExports.rs | 7 +- src/bundler/options.rs | 8 + src/js_parser/p.rs | 18 +- src/js_printer/lib.rs | 30 +- test/bundler/bundler_cjs.test.ts | 390 ++++++++++++++++++ test/bundler/bundler_edgecase.test.ts | 152 +++++++ 12 files changed, 627 insertions(+), 53 deletions(-) diff --git a/src/bundler/LinkerContext.rs b/src/bundler/LinkerContext.rs index 180c9b3ff8bf..6f078aef85d5 100644 --- a/src/bundler/LinkerContext.rs +++ b/src/bundler/LinkerContext.rs @@ -2416,10 +2416,7 @@ impl<'a> LinkerContext<'a> { } else { Ref::NONE }, - require_ref: match self.options.output_format { - Format::Cjs => None, // use unbounded global - _ => runtime_require_ref, - }, + require_ref: runtime_require_ref, require_or_import_meta_for_source_callback: Some( js_printer::RequireOrImportMetaCallback::init(self), ), diff --git a/src/bundler/ParseTask.rs b/src/bundler/ParseTask.rs index f9e5d1f5d174..e33bf3ad667d 100644 --- a/src/bundler/ParseTask.rs +++ b/src/bundler/ParseTask.rs @@ -316,8 +316,11 @@ impl<'a> ParseTask<'a> { /// Re-export of `parse_worker::get_runtime_source` as an associated fn so /// callers can spell it `ParseTask::get_runtime_source`. #[inline] - pub(crate) fn get_runtime_source(target: options::Target) -> RuntimeSource { - parse_worker::get_runtime_source(target) + pub(crate) fn get_runtime_source( + target: options::Target, + output_format: options::Format, + ) -> RuntimeSource { + parse_worker::get_runtime_source(target, output_format) } } @@ -570,7 +573,10 @@ export var __callDispose = (stack, error, hasError) => { pub mod parse_worker { use super::*; - fn get_runtime_source_comptime(target: options::Target) -> RuntimeSource { + fn get_runtime_source_comptime( + target: options::Target, + output_format: options::Format, + ) -> RuntimeSource { // The runtime module is the shared `runtime.js` body plus a per-target // `__require`/`__using` tail. Concatenating at compile time would embed // four copies of the 13 KB body, so each variant is assembled once on @@ -582,11 +588,16 @@ pub mod parse_worker { Node, Other, } - let variant = match target { - options::Target::Bun => Variant::Bun, - options::Target::BunMacro => Variant::BunMacro, - options::Target::Node => Variant::Node, - _ => Variant::Other, + let variant = match (target, output_format) { + (options::Target::Bun, _) => Variant::Bun, + (options::Target::BunMacro, _) => Variant::BunMacro, + // An iife has no `import` statement: read the host's `require`. + (options::Target::Node, options::Format::Iife) => Variant::Other, + ( + options::Target::Node, + options::Format::Esm | options::Format::Cjs | options::Format::InternalBakeDev, + ) => Variant::Node, + (options::Target::Browser | options::Target::ServerComponentsSsr, _) => Variant::Other, }; static SOURCES: [bun_core::Once>; 4] = [ bun_core::Once::new(), @@ -661,8 +672,11 @@ pub mod parse_worker { RuntimeSource { parse_task, source } } - pub(crate) fn get_runtime_source(target: options::Target) -> RuntimeSource { - get_runtime_source_comptime(target) + pub(crate) fn get_runtime_source( + target: options::Target, + output_format: options::Format, + ) -> RuntimeSource { + get_runtime_source_comptime(target, output_format) } // ─────────────────────────────────────────────────────────────────────────── @@ -2505,8 +2519,7 @@ pub mod parse_worker { opts.features.ascii_only = target.is_bun(); opts.features.hot_module_reloading = output_format == options::Format::InternalBakeDev && !task.source_index.is_runtime(); - opts.features.auto_polyfill_require = - output_format == options::Format::Esm && !opts.features.hot_module_reloading; + opts.features.auto_polyfill_require = options::should_call_runtime_require(output_format); opts.features.react_fast_refresh = topts.react_fast_refresh && loader.is_jsx() && !source.path.is_node_module(); opts.features.react_compiler = if topts.react_compiler.is_enabled() diff --git a/src/bundler/bundle_v2.rs b/src/bundler/bundle_v2.rs index 706e739ccb98..28a436f39304 100644 --- a/src/bundler/bundle_v2.rs +++ b/src/bundler/bundle_v2.rs @@ -3724,7 +3724,10 @@ pub mod bv2_impl { /// Common prelude shared by all enqueue_entry_points_* variants: add the runtime task. fn enqueue_entry_points_common(&mut self) -> Result<(), Error> { // Add the runtime - let rt = ParseTask::get_runtime_source(self.transpiler.options.target); + let rt = ParseTask::get_runtime_source( + self.transpiler.options.target, + self.transpiler.options.output_format, + ); self.graph.input_files.append(crate::Graph::InputFile { source: rt.source, loader: Loader::Js, diff --git a/src/bundler/linker_context/generateCodeForLazyExport.rs b/src/bundler/linker_context/generateCodeForLazyExport.rs index f486564afea0..3b0188dfdf36 100644 --- a/src/bundler/linker_context/generateCodeForLazyExport.rs +++ b/src/bundler/linker_context/generateCodeForLazyExport.rs @@ -172,7 +172,7 @@ pub(crate) fn generate_code_for_lazy_export( // output, so the part that holds the call must import it. let calls_runtime_require = matches!(expr.data, ExprData::ECall(ref c) if matches!(c.target.data, ExprData::ERequireCallTarget)) - && this.options.output_format != crate::options::OutputFormat::Cjs; + && crate::options::should_call_runtime_require(this.options.output_format); match exports_kind { bun_ast::ExportsKind::Cjs => { diff --git a/src/bundler/linker_context/generateCompileResultForJSChunk.rs b/src/bundler/linker_context/generateCompileResultForJSChunk.rs index f39841f65f5e..8e47d29091a9 100644 --- a/src/bundler/linker_context/generateCompileResultForJSChunk.rs +++ b/src/bundler/linker_context/generateCompileResultForJSChunk.rs @@ -7,7 +7,7 @@ use bun_threading::thread_pool as ThreadPoolLib; use crate::analyze_transpiled_module::ModuleInfo; use crate::linker_context_mod::LinkerContext; -use crate::options::OutputFormat; +use crate::options::should_call_runtime_require; use crate::thread_pool::Worker; use crate::{Chunk, CompileResult, Index, PartRange}; @@ -100,15 +100,11 @@ fn generate_compile_result_for_js_chunk_impl( .graph .symbols .follow(runtime_members.get(b"__toESM".as_slice()).unwrap().ref_); - let runtime_require_ref = if c.options.output_format == OutputFormat::Cjs { - None - } else { - Some( - c.graph - .symbols - .follow(runtime_members.get(b"__require".as_slice()).unwrap().ref_), - ) - }; + let runtime_require_ref = should_call_runtime_require(c.options.output_format).then(|| { + c.graph + .symbols + .follow(runtime_members.get(b"__require".as_slice()).unwrap().ref_) + }); // `worker.arena` (= `BackRef` to `worker.heap`) is a disjoint field from // `worker.temporary_arena` / `worker.stmt_list` borrowed `&mut` above, so diff --git a/src/bundler/linker_context/postProcessJSChunk.rs b/src/bundler/linker_context/postProcessJSChunk.rs index 1455395e4bc6..58a3ad7a5f9d 100644 --- a/src/bundler/linker_context/postProcessJSChunk.rs +++ b/src/bundler/linker_context/postProcessJSChunk.rs @@ -144,15 +144,6 @@ pub(crate) fn post_process_js_chunk( .graph .symbols .follow(runtime_members.get(&b"__toESM"[..]).unwrap().ref_); - let runtime_require_ref = if c.options.output_format == options::OutputFormat::Cjs { - None - } else { - Some( - c.graph - .symbols - .follow(runtime_members.get(&b"__require"[..]).unwrap().ref_), - ) - }; // The chunk's module record, assembled in output order: the printer records // the cross-chunk prefix imports below, then the per-part-range results @@ -200,7 +191,6 @@ pub(crate) fn post_process_js_chunk( indent: Default::default(), has_run_symbol_renamer: true, - require_ref: runtime_require_ref, minify_whitespace: c.options.minify_whitespace, minify_identifiers: c.options.minify_identifiers, minify_syntax: c.options.minify_syntax, diff --git a/src/bundler/linker_context/scanImportsAndExports.rs b/src/bundler/linker_context/scanImportsAndExports.rs index 73dee5b22d28..b466b9d89e57 100644 --- a/src/bundler/linker_context/scanImportsAndExports.rs +++ b/src/bundler/linker_context/scanImportsAndExports.rs @@ -1144,7 +1144,7 @@ pub(crate) fn scan_imports_and_exports( // nor does a split `require()` (`import.meta.require`). if kind != ImportKind::Dynamic && !is_external_dyn - && should_call_runtime_require(output_format) + && options::should_call_runtime_require(output_format) { runtime_require_uses += 1; } @@ -1440,11 +1440,6 @@ pub(crate) fn scan_imports_and_exports( Ok(()) } -#[inline] -fn should_call_runtime_require(format: options::Format) -> bool { - format != Format::Cjs -} - // ────────────────────────────────────────────────────────────────────────── // DependencyWrapper // ────────────────────────────────────────────────────────────────────────── diff --git a/src/bundler/options.rs b/src/bundler/options.rs index 457bc6993f04..fc3581db2eb6 100644 --- a/src/bundler/options.rs +++ b/src/bundler/options.rs @@ -321,6 +321,14 @@ impl TargetExt for Target { pub use bun_options_types::Format; pub use bun_options_types::WindowsOptions; +/// Whether an unbundled `require` is the runtime's `__require`, for the parser and the linker. +pub(crate) fn should_call_runtime_require(format: Format) -> bool { + match format { + Format::Esm | Format::Iife => true, + Format::Cjs | Format::InternalBakeDev => false, + } +} + // Re-export of `bun_ast::Loader`. // There is exactly ONE `Loader`; re-export so the bundler's // `BundleOptions.loaders` and the resolver's `Path::loader()` operate on the diff --git a/src/js_parser/p.rs b/src/js_parser/p.rs index dda03901c59e..3a3346424832 100644 --- a/src/js_parser/p.rs +++ b/src/js_parser/p.rs @@ -6031,9 +6031,8 @@ impl<'a, const TYPESCRIPT: bool, const SCAN_ONLY: bool> P<'a, TYPESCRIPT, SCAN_O } /// This is only allowed to be called if allow_runtime is true - /// If --target=bun, this does nothing. + /// It does nothing unless the output calls the runtime's `__require`. pub(crate) fn record_usage_of_runtime_require(&mut self) { - // target bun does not have __require if self.options.features.auto_polyfill_require { debug_assert!(self.options.features.allow_runtime); self.ensure_require_symbol(); @@ -6086,6 +6085,9 @@ impl<'a, const TYPESCRIPT: bool, const SCAN_ONLY: bool> P<'a, TYPESCRIPT, SCAN_O // a handle to the `__require` function. if self.options.lower_import_meta_main { self.record_usage_of_runtime_require(); + if self.options.output_format == options::Format::Iife { + self.reserve_host_module_name(); + } } Expr { loc, @@ -6093,6 +6095,18 @@ impl<'a, const TYPESCRIPT: bool, const SCAN_ONLY: bool> P<'a, TYPESCRIPT, SCAN_O } } + /// The iife lowering of `import.meta.main` prints `module`: keep bundle bindings off the name. + fn reserve_host_module_name(&mut self) { + let reserved = self.module_scope().generated.slice().iter().any(|ref_| { + let symbol = &self.symbols[ref_.inner_index() as usize]; + symbol.kind == js_ast::symbol::Kind::Unbound + && symbol.original_name.slice() == b"module" + }); + if !reserved { + self.declare_generated_symbol(js_ast::symbol::Kind::Unbound, b"module"); + } + } + pub(crate) fn keep_expr_symbol_name(&mut self, _value: Expr, _name: &[u8]) -> Expr { _value } diff --git a/src/js_printer/lib.rs b/src/js_printer/lib.rs index bc7e783d0104..65b947ab8dde 100644 --- a/src/js_printer/lib.rs +++ b/src/js_printer/lib.rs @@ -3314,26 +3314,42 @@ pub(crate) mod __gated_printer { self.options.module_type != bundle_opts::Format::InternalBakeDev ); - let wrap = level.gte(Level::Equals); + // A `