Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 3 additions & 5 deletions src/bundler/LinkerContext.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Comment thread
robobun marked this conversation as resolved.
Resolved

hmr_ref: if self.options.output_format == Format::InternalBakeDev {
ast.wrapper_ref
Expand Down
41 changes: 27 additions & 14 deletions src/bundler/ParseTask.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
}

Expand Down Expand Up @@ -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
Expand All @@ -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,
Comment thread
robobun marked this conversation as resolved.
Resolved
(
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<Box<[u8]>>; 4] = [
bun_core::Once::new(),
Expand Down Expand Up @@ -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)
}

// ───────────────────────────────────────────────────────────────────────────
Expand Down Expand Up @@ -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()
Expand Down Expand Up @@ -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() {
Expand Down
5 changes: 4 additions & 1 deletion src/bundler/bundle_v2.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
2 changes: 1 addition & 1 deletion src/bundler/linker_context/generateCodeForLazyExport.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 => {
Expand Down
16 changes: 6 additions & 10 deletions src/bundler/linker_context/generateCompileResultForJSChunk.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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};

Expand Down Expand Up @@ -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
Expand Down
10 changes: 0 additions & 10 deletions src/bundler/linker_context/postProcessJSChunk.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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,
Expand Down
7 changes: 1 addition & 6 deletions src/bundler/linker_context/scanImportsAndExports.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
Expand Down Expand Up @@ -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
// ──────────────────────────────────────────────────────────────────────────
Expand Down
8 changes: 8 additions & 0 deletions src/bundler/options.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion src/bundler/transpiler.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
20 changes: 17 additions & 3 deletions src/js_parser/p.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down Expand Up @@ -6084,15 +6083,30 @@ 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,
data: js_ast::ExprData::EImportMetaMain(E::ImportMetaMain { inverted }),
}
}

/// 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
}
Expand Down
9 changes: 5 additions & 4 deletions src/js_parser/parse/parse_entry.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<bool>,
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.
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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,
Expand Down
38 changes: 27 additions & 11 deletions src/js_printer/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Comment thread
robobun marked this conversation as resolved.
Resolved
if wrap {
self.print(b"(");
Expand All @@ -3314,26 +3314,42 @@ pub(crate) mod __gated_printer {
self.options.module_type != bundle_opts::Format::InternalBakeDev
);

let wrap = level.gte(Level::Equals);
// A `<script>` has no `module`. `??` takes no bare `&&` or `||` operand.
let guard_host_module =
self.options.module_type == bundle_opts::Format::Iife;
let wrap = guard_host_module || level.gte(Level::Equals);
if wrap {
self.print(b"(");
}
self.print_space_before_identifier();
self.add_source_mapping(expr.loc);

if guard_host_module {
self.print(b"typeof module");
if data.inverted {
self.print_whitespacer(ws!(b" == \"undefined\" || "));
} else {
self.print_whitespacer(ws!(b" != \"undefined\" && "));
}
}

if let Some(require) = self.options.require_ref {
self.print_symbol(require);
} else {
self.print(b"require");
}

if data.inverted {
self.print_whitespacer(ws!(b".main != "));
} else {
self.print_whitespacer(ws!(b".main == "));
}
// Strict: an undefined `.main` must not equal a host `module` of null.
self.print_whitespacer(match (guard_host_module, data.inverted) {
(true, false) => ws!(b".main === "),
(true, true) => ws!(b".main !== "),
(false, false) => ws!(b".main == "),
(false, true) => ws!(b".main != "),
});

if self.options.target == bun_ast::Target::Node {
if guard_host_module {
self.print(b"module");
} else if self.options.target == bun_ast::Target::Node {
// "__require.module"
if let Some(require) = self.options.require_ref {
self.print_symbol(require);
Expand Down
9 changes: 9 additions & 0 deletions src/options_types/bundle_enums.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
Loading
Loading