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
5 changes: 5 additions & 0 deletions src/ast/e.rs
Original file line number Diff line number Diff line change
Expand Up @@ -201,6 +201,11 @@ pub struct Super;
#[derive(Clone, Copy, Default)]
pub struct ImportMeta;

impl ImportMeta {
/// Sixth parameter of the CommonJS wrapper. JSCommonJSModule.cpp passes `import.meta` for it.
pub const CJS_WRAPPER_ARG: &'static [u8] = b"$Bun_import_meta";
}

#[derive(Clone, Copy, Default)]
pub struct ImportMetaMain {
/// If true, print `!import.meta.main` (or `require.main != module`).
Expand Down
8 changes: 8 additions & 0 deletions src/bundler/LinkerContext.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2313,6 +2313,12 @@ impl<'a> LinkerContext<'a> {
Ok(true)
}

/// The one place that decides whether a chunk gets the `@bun-cjs` function wrapper.
pub(crate) fn chunk_has_bun_cjs_wrapper(&self, chunk: &Chunk) -> bool {
self.options.output_format == Format::Cjs
&& self.graph.ast.items_target()[chunk.entry_point.source_index() as usize].is_bun()
}

pub(crate) fn print_code_for_file_in_chunk_js(
&mut self,
r: renamer::Renamer,
Expand All @@ -2324,6 +2330,7 @@ impl<'a> LinkerContext<'a> {
to_esm_ref: Ref,
to_commonjs_ref: Ref,
runtime_require_ref: Option<Ref>,
inside_bun_cjs_wrapper: bool,
source_index: Index,
source: &Source,
module_info: Option<&mut crate::analyze_transpiled_module::ModuleInfo>,
Expand Down Expand Up @@ -2403,6 +2410,7 @@ impl<'a> LinkerContext<'a> {
Format::Cjs => None, // use unbounded global
_ => runtime_require_ref,
},
inside_bun_cjs_wrapper,
require_or_import_meta_for_source_callback:
js_printer::RequireOrImportMetaCallback::init(self),
line_offset_tables: Some(line_offset_table),
Expand Down
4 changes: 4 additions & 0 deletions src/bundler/ParseTask.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2639,6 +2639,10 @@ pub mod parse_worker {
opts.lower_import_meta_main_for_node_js = true;
}

// For bun, cjs output gets import.meta from the `@bun-cjs` wrapper instead.
opts.inline_import_meta_paths = topts.framework.is_some()
|| (output_format == options::Format::Cjs && !target.is_bun());

opts.tree_shaking = if task.source_index.is_runtime() {
true
} else {
Expand Down
7 changes: 3 additions & 4 deletions src/bundler/bundle_v2.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7766,10 +7766,9 @@ pub mod bv2_impl {
fn default() -> Self {
CompileResult::Javascript {
source_index: 0,
result: bun_js_printer::PrintResult::Result(bun_js_printer::PrintResultSuccess {
code: Box::new([]),
source_map: None,
}),
result: bun_js_printer::PrintResult::Result(
bun_js_printer::PrintResultSuccess::default(),
),
module_info: None,
}
}
Expand Down
8 changes: 4 additions & 4 deletions src/bundler/linker_context/generateCodeForFileInChunkJS.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ pub fn generate_code_for_file_in_chunk_js<'r, 'src>(
to_common_js_ref: Ref,
to_esm_ref: Ref,
runtime_require_ref: Option<Ref>,
inside_bun_cjs_wrapper: bool,
stmts: &mut StmtList,
arena: &Bump,
temp_arena: &Bump,
Expand Down Expand Up @@ -226,6 +227,7 @@ pub fn generate_code_for_file_in_chunk_js<'r, 'src>(
Ref::NONE,
Ref::NONE,
None,
false,
part_range.source_index,
source,
module_info,
Expand Down Expand Up @@ -967,10 +969,7 @@ pub fn generate_code_for_file_in_chunk_js<'r, 'src>(
let out_stmts: &mut [Stmt] = out_stmts.slice_mut();

if out_stmts.is_empty() {
return PrintResult::Result(PrintResultSuccess {
code: Box::new([]),
source_map: None,
});
return PrintResult::Result(PrintResultSuccess::default());
}

// `get_source` returns `&'static Source` (parse_graph SoA is append-only and
Expand All @@ -987,6 +986,7 @@ pub fn generate_code_for_file_in_chunk_js<'r, 'src>(
to_esm_ref,
to_common_js_ref,
runtime_require_ref,
inside_bun_cjs_wrapper,
part_range.source_index,
source,
module_info,
Expand Down
2 changes: 2 additions & 0 deletions src/bundler/linker_context/generateCompileResultForJSChunk.rs
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,7 @@ fn generate_compile_result_for_js_chunk_impl(
.follow(runtime_members.get(b"__require".as_slice()).unwrap().ref_),
)
};
let inside_bun_cjs_wrapper = c.chunk_has_bun_cjs_wrapper(chunk);

// `worker.arena` (= `BackRef` to `worker.heap`) is a disjoint field from
// `worker.temporary_arena` / `worker.stmt_list` borrowed `&mut` above, so
Expand Down Expand Up @@ -149,6 +150,7 @@ fn generate_compile_result_for_js_chunk_impl(
to_common_js_ref,
to_esm_ref,
runtime_require_ref,
inside_bun_cjs_wrapper,
stmt_list,
worker_alloc,
&**arena,
Expand Down
55 changes: 34 additions & 21 deletions src/bundler/linker_context/postProcessJSChunk.rs
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,18 @@ fn module_preload_registration(
Ok(code)
}

fn chunk_uses_import_meta_arg(chunk: &Chunk) -> bool {
chunk.compile_results_for_chunk.iter().any(|result| {
matches!(
result,
CompileResult::Javascript {
result: PrintResult::Result(printed),
..
} if printed.uses_import_meta_arg
)
})
}

/// This runs after we've already populated the compile results
pub(crate) fn post_process_js_chunk(
ctx: GenerateChunkCtx,
Expand Down Expand Up @@ -361,10 +373,7 @@ pub(crate) fn post_process_js_chunk(

break 'brk CompileResult::Javascript {
source_index: Index::INVALID.value(),
result: PrintResult::Result(js_printer::PrintResultSuccess {
code: Box::default(),
source_map: None,
}),
result: PrintResult::Result(js_printer::PrintResultSuccess::default()),
module_info: None,
};
};
Expand Down Expand Up @@ -441,20 +450,27 @@ pub(crate) fn post_process_js_chunk(

// Add @bun comments and CJS wrapper start for each chunk when targeting Bun.
let is_bun = c.graph.ast.items_target()[chunk.entry_point.source_index() as usize].is_bun();
if is_bun {
if c.options.generate_bytecode_cache && output_format == options::OutputFormat::Cjs {
const INPUT: &[u8] =
b"// @bun @bytecode @bun-cjs\n(function(exports, require, module, __filename, __dirname) {";
j.push_static(INPUT);
line_offset.advance(INPUT);
} else if c.options.generate_bytecode_cache {
let has_bun_cjs_wrapper = c.chunk_has_bun_cjs_wrapper(chunk);
if has_bun_cjs_wrapper {
let mut push = |bytes: &'static [u8]| {
j.push_static(bytes);
line_offset.advance(bytes);
};
push(if c.options.generate_bytecode_cache {
b"// @bun @bytecode @bun-cjs\n"
} else {
b"// @bun @bun-cjs\n"
});
push(b"(function(exports, require, module, __filename, __dirname");
if chunk_uses_import_meta_arg(chunk) {
push(b", ");
push(E::ImportMeta::CJS_WRAPPER_ARG);
}
push(b") {");
} else if is_bun {
if c.options.generate_bytecode_cache {
j.push_static(b"// @bun @bytecode\n");
line_offset.advance(b"// @bun @bytecode\n");
} else if output_format == options::OutputFormat::Cjs {
const INPUT: &[u8] =
b"// @bun @bun-cjs\n(function(exports, require, module, __filename, __dirname) {";
j.push_static(INPUT);
line_offset.advance(INPUT);
} else {
j.push_static(b"// @bun\n");
line_offset.advance(b"// @bun\n");
Expand Down Expand Up @@ -749,7 +765,7 @@ pub(crate) fn post_process_js_chunk(
}
}
options::OutputFormat::Cjs => {
if is_bun {
if has_bun_cjs_wrapper {
j.push_static(b"})\n");
line_offset.advance(b"})\n");
}
Expand Down Expand Up @@ -1260,10 +1276,7 @@ pub(crate) fn generate_entry_point_tail_js<'a>(
if stmts.is_empty() {
return CompileResult::Javascript {
source_index,
result: PrintResult::Result(js_printer::PrintResultSuccess {
code: Box::default(),
source_map: None,
}),
result: PrintResult::Result(js_printer::PrintResultSuccess::default()),
module_info: None,
};
}
Expand Down
5 changes: 5 additions & 0 deletions src/bundler/linker_context/renameSymbolsInChunk.rs
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ pub(crate) unsafe fn rename_symbols_in_chunk(
// borrow does not assert immutability over the heap cells written below.
// SAFETY: see fn safety doc — `c` is live for the call.
let c: &LinkerContext<'_> = unsafe { &*c };
let has_bun_cjs_wrapper = c.chunk_has_bun_cjs_wrapper(chunk);

// ── raw SoA column pointers (root provenance) ────────────────────────
// `split_raw()` derives `*mut [T]` directly from the buffer base with no
Expand Down Expand Up @@ -144,6 +145,10 @@ pub(crate) unsafe fn rename_symbols_in_chunk(
};

let mut reserved_names = renamer::compute_initial_reserved_names(c.options.output_format)?;
if has_bun_cjs_wrapper {
// The printer refers to this wrapper parameter by name.
reserved_names.put(bun_ast::E::ImportMeta::CJS_WRAPPER_ARG, 1)?;
}
for &source_index in files_in_order {
renamer::compute_reserved_names_for_scope(
&all_module_scopes[source_index as usize],
Expand Down
1 change: 1 addition & 0 deletions src/bundler/transpiler.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1598,6 +1598,7 @@ impl<'a> Transpiler<'a> {
transform_only: self.options.transform_only,
import_meta_main_value: None,
lower_import_meta_main_for_node_js: false,
inline_import_meta_paths: false,
framework: None,
repl_mode: self.options.repl_mode,
lower_toml_datetimes: false,
Expand Down
8 changes: 2 additions & 6 deletions src/js_parser/fold.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use bun_collections::VecExt;
use bun_core::feature_flags as FeatureFlags;

use crate::p::P;
use crate::parser::{self as js_parser, IdentifierOpts, RelocateVars, RelocateVarsMode};
use crate::parser::{IdentifierOpts, RelocateVars, RelocateVarsMode};
use bun_ast::ast_result::CommonJSNamedExport;
use bun_ast::{self as js_ast, Binding, E, Expr, Flags, G, LocRef, S};

Expand Down Expand Up @@ -599,11 +599,7 @@ impl<'a, const TYPESCRIPT: bool, const SCAN_ONLY: bool> P<'a, TYPESCRIPT, SCAN_O
});
}

// Inline import.meta properties for Bake
if p.options.framework.is_some()
|| (p.options.bundle
&& p.options.output_format == js_parser::options::Format::Cjs)
{
if p.options.inline_import_meta_paths {
if name == b"dir" || name == b"dirname" {
// Inline import.meta.dir
return Some(
Expand Down
2 changes: 1 addition & 1 deletion src/js_parser/p.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9144,7 +9144,7 @@ impl<'a, const TYPESCRIPT: bool, const SCAN_ONLY: bool> P<'a, TYPESCRIPT, SCAN_O
};
if self.has_import_meta {
self.import_meta_ref =
self.new_symbol(js_ast::symbol::Kind::Other, b"$Bun_import_meta");
self.new_symbol(js_ast::symbol::Kind::Other, E::ImportMeta::CJS_WRAPPER_ARG);
args[5] = Arg {
binding: self.b(
B::Identifier {
Expand Down
6 changes: 6 additions & 0 deletions src/js_parser/parse/parse_entry.rs
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,9 @@ pub struct Options<'a> {
pub import_meta_main_value: Option<bool>,
pub lower_import_meta_main_for_node_js: bool,

/// Inline the source file's paths for `import.meta.dir`, `.dirname`, `.file`, `.path`, `.url`.
pub inline_import_meta_paths: bool,

/// When using react fast refresh or server components, the framework is
/// able to customize what import sources are used.
pub framework: Option<&'a options::Framework>, // TYPE_ONLY: was bun_runtime::bake::Framework
Expand Down Expand Up @@ -142,6 +145,7 @@ impl<'a> Default for Options<'a> {
transform_only: false,
import_meta_main_value: None,
lower_import_meta_main_for_node_js: false,
inline_import_meta_paths: false,
framework: None,
repl_mode: false,
lower_toml_datetimes: false,
Expand Down Expand Up @@ -228,6 +232,7 @@ impl<'a> Options<'a> {
transform_only: self.transform_only,
import_meta_main_value: self.import_meta_main_value,
lower_import_meta_main_for_node_js: self.lower_import_meta_main_for_node_js,
inline_import_meta_paths: self.inline_import_meta_paths,
framework: self.framework,
repl_mode: self.repl_mode,
lower_toml_datetimes: self.lower_toml_datetimes,
Expand Down Expand Up @@ -301,6 +306,7 @@ impl<'a> Options<'a> {
transform_only: false,
import_meta_main_value: None,
lower_import_meta_main_for_node_js: false,
inline_import_meta_paths: false,
framework: None,
repl_mode: false,
lower_toml_datetimes: loader == options::Loader::Toml,
Expand Down
32 changes: 20 additions & 12 deletions src/js_printer/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1326,6 +1326,8 @@ pub struct Options<'a> {
pub module_preload_ref: Ref,
pub require_ref: Option<Ref>,
pub import_meta_ref: Ref,
/// Print `import.meta` as `E::ImportMeta::CJS_WRAPPER_ARG`, a parameter of the `@bun-cjs` wrapper.
pub inside_bun_cjs_wrapper: bool,
pub hmr_ref: Ref,
pub indent: Indentation,
// allocator dropped — global mimalloc (this is an AST crate but Options.allocator is the global default)
Expand Down Expand Up @@ -1408,6 +1410,7 @@ impl<'a> Default for Options<'a> {
module_preload_ref: Ref::NONE,
require_ref: None,
import_meta_ref: Ref::NONE,
inside_bun_cjs_wrapper: false,
hmr_ref: Ref::NONE,
indent: Indentation::default(),
source_map_handler: None,
Expand Down Expand Up @@ -1553,9 +1556,12 @@ pub enum PrintResult {
Err(crate::Error),
}

#[derive(Default)]
pub struct PrintResultSuccess {
pub code: Box<[u8]>,
pub source_map: Option<SourceMap::Chunk>,
/// `code` refers to `E::ImportMeta::CJS_WRAPPER_ARG` (see `Options::inside_bun_cjs_wrapper`).
pub uses_import_meta_arg: bool,
}

// do not make this a packed struct
Expand Down Expand Up @@ -1677,6 +1683,7 @@ pub(crate) mod __gated_printer {
pub(crate) stack_overflowed: bool,

pub(crate) was_lazy_export: bool,
pub(crate) uses_import_meta_arg: bool,
// Always carried; gated at call sites with MAY_HAVE_MODULE_INFO.
pub(crate) module_info: Option<&'a mut analyze_transpiled_module::ModuleInfo>,

Expand Down Expand Up @@ -3290,23 +3297,22 @@ pub(crate) mod __gated_printer {
debug_assert!(self.options.hmr_ref.is_valid());
self.print_symbol(self.options.hmr_ref);
self.print(b".importMeta");
} else if !self.options.import_meta_ref.is_valid() {
// Most of the time, leave it in there
if let Some(mi) = self.module_info() {
mi.flags.contains_import_meta = true;
}
self.print(b"import.meta");
} else {
// Note: The bundler will not hit this code path. The bundler will replace
// the ImportMeta AST node with a regular Identifier AST node.
//
// This is currently only used in Bun's runtime for CommonJS modules
// referencing import.meta
} else if self.options.import_meta_ref.is_valid() {
// The runtime's CommonJS wrapper (`WrapMode::BunCommonjs`).
//
// TODO: This assertion trips when using `import.meta` with `--format=cjs`
debug_assert!(self.options.module_type == bundle_opts::Format::Cjs);

self.print_symbol(self.options.import_meta_ref);
} else if self.options.inside_bun_cjs_wrapper {
self.print(E::ImportMeta::CJS_WRAPPER_ARG);
self.uses_import_meta_arg = true;
} else {
// Most of the time, leave it in there
if let Some(mi) = self.module_info() {
mi.flags.contains_import_meta = true;
}
self.print(b"import.meta");
Comment thread
coderabbitai[bot] marked this conversation as resolved.
}
}
ExprData::EImportMetaMain(data) => {
Expand Down Expand Up @@ -7026,6 +7032,7 @@ pub(crate) mod __gated_printer {
stack_check: bun_core::StackCheck::init(),
stack_overflowed: false,
was_lazy_export: false,
uses_import_meta_arg: false,
module_info: None,
}
}
Expand Down Expand Up @@ -8230,6 +8237,7 @@ pub(crate) fn print_with_writer_and_platform<
PrintResult::Result(PrintResultSuccess {
code: buffer.take_slice().into(),
source_map,
uses_import_meta_arg: printer.uses_import_meta_arg,
})
}

Expand Down
Loading
Loading