diff --git a/src/bundler/LinkerContext.rs b/src/bundler/LinkerContext.rs index 60649fdd6f50..6f078aef85d5 100644 --- a/src/bundler/LinkerContext.rs +++ b/src/bundler/LinkerContext.rs @@ -2416,17 +2416,15 @@ 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), ), 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..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() @@ -2625,8 +2638,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/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/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..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(); @@ -6084,8 +6083,11 @@ 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(); + 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_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..65b947ab8dde 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"("); @@ -3314,26 +3314,42 @@ pub(crate) mod __gated_printer { self.options.module_type != bundle_opts::Format::InternalBakeDev ); - let wrap = level.gte(Level::Equals); + // A `