Skip to content
2 changes: 1 addition & 1 deletion src/ast/e.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2425,7 +2425,7 @@ pub type UnwrappedRequireIndexOptional =
pub struct RequireString {
pub import_record_index: u32,

/// Set when `unwrap_commonjs_to_esm` turned this `require()` into an import.
/// Set when unwrapping `const x = require()` into an import; consumed by `visit_decls`.
pub unwrapped_id: UnwrappedRequireIndexOptional,
}
impl Default for RequireString {
Expand Down
6 changes: 0 additions & 6 deletions src/bun_core/util.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3287,12 +3287,6 @@ impl<I: GenericIndexInt, M> GenericIndex<I, M> {
GenericIndexOptional(self.0, core::marker::PhantomData)
}
}
impl<I: GenericIndexInt, M> GenericIndexOptional<I, M> {
#[inline]
pub fn is_some(self) -> bool {
!self.is_none()
}
}

/// `GenericIndex::Optional` — `MAX` is `none`.
#[repr(transparent)]
Expand Down
22 changes: 3 additions & 19 deletions src/bundler/LinkerContext.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2489,25 +2489,16 @@ impl<'a> LinkerContext<'a> {
pub(crate) fn require_or_import_meta_for_source(
&mut self,
source_index: crate::IndexInt,
was_unwrapped_require: bool,
) -> js_printer::RequireOrImportMeta {
let flags = self.graph.meta.items_flags()[source_index as usize];
js_printer::RequireOrImportMeta {
exports_ref: if flags.wrap == WrapKind::Esm
|| (was_unwrapped_require
&& self.graph.ast.items_flags()[source_index as usize]
.contains(AstFlags::FORCE_CJS_TO_ESM))
{
exports_ref: if flags.wrap == WrapKind::Esm {
self.graph.ast.items_exports_ref()[source_index as usize]
} else {
Ref::NONE
},
is_wrapper_async: flags.is_async_or_has_async_dependency,
wrapper_ref: self.graph.ast.items_wrapper_ref()[source_index as usize],

was_unwrapped_require: was_unwrapped_require
&& self.graph.ast.items_flags()[source_index as usize]
.contains(AstFlags::FORCE_CJS_TO_ESM),
}
}

Expand Down Expand Up @@ -2778,12 +2769,8 @@ impl<'a> LinkerContext<'a> {
/// can call back into `LinkerContext::require_or_import_meta_for_source`.
impl<'a> js_printer::RequireOrImportMetaSource for LinkerContext<'a> {
#[inline]
fn require_or_import_meta_for_source(
&mut self,
id: u32,
was_unwrapped_require: bool,
) -> js_printer::RequireOrImportMeta {
LinkerContext::require_or_import_meta_for_source(self, id, was_unwrapped_require)
fn require_or_import_meta_for_source(&mut self, id: u32) -> js_printer::RequireOrImportMeta {
LinkerContext::require_or_import_meta_for_source(self, id)
}
}

Expand Down Expand Up @@ -4470,9 +4457,6 @@ impl<'a> LinkerContext<'a> {
// A named import that holds a namespace (`export * as`) prints as
// an import identifier.
bun_ast::ExprData::EImportIdentifier(identifier) => identifier.ref_,
bun_ast::ExprData::ERequireString(require) => {
return require.unwrapped_id.get().is_some();
}
_ => return false,
};
// A require() lifted into an import binds an ordinary local, so user
Expand Down
11 changes: 7 additions & 4 deletions src/js_parser/p.rs
Original file line number Diff line number Diff line change
Expand Up @@ -353,6 +353,7 @@ pub struct P<'a, const TYPESCRIPT: bool, const SCAN_ONLY: bool> {
/// beside the real count rather than decremented from it so the minifier's
/// single-use substitution still sees every use.
pub(crate) namespace_tracked_uses: HashMap<Ref, u32>,
/// The file is CommonJS in a package of `unwrap_commonjs_packages`.
pub(crate) unwrap_all_requires: bool,

pub(crate) commonjs_named_exports: bun_ast::ast_result::CommonJSNamedExports,
Expand Down Expand Up @@ -1304,9 +1305,7 @@ impl<'a, const TYPESCRIPT: bool, const SCAN_ONLY: bool> P<'a, TYPESCRIPT, SCAN_O
self.conditional_namespace_records(e.yes, out)?;
self.conditional_namespace_records(e.no, out)
}
js_ast::ExprData::ERequireString(req)
if self.options.bundle && req.unwrapped_id.get().is_none() =>
{
js_ast::ExprData::ERequireString(req) if self.options.bundle => {
out.push(req.import_record_index);
Some(())
}
Expand Down Expand Up @@ -1434,7 +1433,7 @@ impl<'a, const TYPESCRIPT: bool, const SCAN_ONLY: bool> P<'a, TYPESCRIPT, SCAN_O
/// `try_track_dynamic_import_destructure` / `maybe_rewrite_property_access`
/// can record aliases against it.
pub(crate) fn require_namespace_ref(&mut self, req: E::RequireString) -> Option<Ref> {
if !self.options.bundle || req.unwrapped_id.get().is_some() {
if !self.options.bundle {
return None;
}
let ns = self.new_symbol(js_ast::symbol::Kind::Other, b"require_ns");
Expand Down Expand Up @@ -3327,6 +3326,10 @@ impl<'a, const TYPESCRIPT: bool, const SCAN_ONLY: bool> P<'a, TYPESCRIPT, SCAN_O
|| self.esm_import_keyword.len > 0
|| self.esm_export_keyword.len > 0
|| self.top_level_await_keyword.len > 0;
// The unwrap list converts CommonJS files. An ES module, by syntax or by type, is not one.
self.unwrap_all_requires = self.unwrap_all_requires
&& !self.has_es_module_syntax
&& self.options.module_type != options::ModuleType::Esm;

if let Some(factory) = self.lexer.jsx_pragma.jsx() {
// `Span.text` is a `StoreStr` into lexer-owned source; valid for 'a.
Expand Down
4 changes: 3 additions & 1 deletion src/js_parser/parse/parse_entry.rs
Original file line number Diff line number Diff line change
Expand Up @@ -568,6 +568,9 @@ impl<'a> Parser<'a> {
p.should_fold_typescript_constant_expressions = true;
}

// A lazy export (JSON, text, a file path) is not CommonJS that the unwrap list converts.
p.unwrap_all_requires = false;

// If we added to `p.symbols` it's going to fuck up all the indices
// in the `symbols` array.
debug_assert!(p.symbols.len() == 0);
Expand Down Expand Up @@ -1605,7 +1608,6 @@ impl<'a> Parser<'a> {
if p.options.features.unwrap_commonjs_to_esm
&& p.unwrap_all_requires
&& !p.options.is_entry_point
&& !p.has_es_module_syntax
&& p.commonjs_named_exports.count() == 0
&& !p.has_top_level_return
&& !p.has_with_scope
Expand Down
3 changes: 1 addition & 2 deletions src/js_parser/parser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1036,8 +1036,7 @@ pub struct ExprIn {
/// tests.
pub(crate) assign_target: js_ast::AssignTarget,

/// Currently this is only used when unwrapping a call to `require()`
/// with `__toESM()`.
/// Identifier-binding initializer; only used to unwrap `const x = require()` into an import.
pub(crate) is_immediately_assigned_to_decl: bool,

pub(crate) property_access_for_method_call_maybe_should_replace_with_undefined: bool,
Expand Down
7 changes: 5 additions & 2 deletions src/js_parser/visit/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -355,7 +355,11 @@ impl<'a, const TYPESCRIPT: bool, const SCAN_ONLY: bool> P<'a, TYPESCRIPT, SCAN_O
self.visit_expr_in_out(
&mut val,
ExprIn {
is_immediately_assigned_to_decl: true,
// Only the identifier unwrap below consumes the marker this requests.
is_immediately_assigned_to_decl: matches!(
decl.binding.data,
BData::BIdentifier(_)
),
..Default::default()
},
);
Expand Down Expand Up @@ -555,7 +559,6 @@ impl<'a, const TYPESCRIPT: bool, const SCAN_ONLY: bool> P<'a, TYPESCRIPT, SCAN_O
let local = id.r#ref;
if self.import_items_for_namespace.contains_key(&local)
|| !self.options.bundle
|| req.unwrapped_id.get().is_some()
{
break 'split_require;
}
Expand Down
54 changes: 15 additions & 39 deletions src/js_printer/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1383,20 +1383,15 @@ pub struct Options<'a> {
}

impl<'a> Options<'a> {
pub(crate) fn require_or_import_meta_for_source(
&self,
id: u32,
was_unwrapped_require: bool,
) -> RequireOrImportMeta {
pub(crate) fn require_or_import_meta_for_source(&self, id: u32) -> RequireOrImportMeta {
if self
.require_or_import_meta_for_source_callback
.ctx
.is_none()
{
return RequireOrImportMeta::default();
}
self.require_or_import_meta_for_source_callback
.call(id, was_unwrapped_require)
self.require_or_import_meta_for_source_callback.call(id)
}
}

Expand Down Expand Up @@ -1472,20 +1467,19 @@ pub struct RequireOrImportMeta {
pub wrapper_ref: Ref,
pub exports_ref: Ref,
pub is_wrapper_async: bool,
pub was_unwrapped_require: bool,
}

// Clone/Copy: bitwise OK — `ctx` is a non-owning opaque backref the caller
// keeps alive for the print pass; `callback` is POD.
#[derive(Clone, Copy)]
pub struct RequireOrImportMetaCallback {
pub(crate) ctx: Option<NonNull<()>>,
pub(crate) callback: fn(*mut (), u32, bool) -> RequireOrImportMeta,
pub(crate) callback: fn(*mut (), u32) -> RequireOrImportMeta,
}

impl Default for RequireOrImportMetaCallback {
fn default() -> Self {
fn noop(_: *mut (), _: u32, _: bool) -> RequireOrImportMeta {
fn noop(_: *mut (), _: u32) -> RequireOrImportMeta {
RequireOrImportMeta::default()
}
Self {
Expand All @@ -1498,28 +1492,20 @@ impl Default for RequireOrImportMetaCallback {
/// PORTING.md §Dispatch — manual vtable. The erased thunk is monomorphized
/// over `T: RequireOrImportMetaSource`, so `callback` stays a captureless `fn`.
pub trait RequireOrImportMetaSource {
fn require_or_import_meta_for_source(
&mut self,
id: u32,
was_unwrapped_require: bool,
) -> RequireOrImportMeta;
fn require_or_import_meta_for_source(&mut self, id: u32) -> RequireOrImportMeta;
}

impl RequireOrImportMetaCallback {
pub(crate) fn call(&self, id: u32, was_unwrapped_require: bool) -> RequireOrImportMeta {
(self.callback)(self.ctx.unwrap().as_ptr(), id, was_unwrapped_require)
pub(crate) fn call(&self, id: u32) -> RequireOrImportMeta {
(self.callback)(self.ctx.unwrap().as_ptr(), id)
}

pub fn init<T: RequireOrImportMetaSource>(ctx: &mut T) -> Self {
fn thunk<T: RequireOrImportMetaSource>(
p: *mut (),
id: u32,
was_unwrapped_require: bool,
) -> RequireOrImportMeta {
fn thunk<T: RequireOrImportMetaSource>(p: *mut (), id: u32) -> RequireOrImportMeta {
// SAFETY: `p` was constructed from `&mut T` in `init` below; caller guarantees
// `ctx` outlives this `RequireOrImportMetaCallback`, so the cast-back
// deref is valid and exclusive.
unsafe { (*p.cast::<T>()).require_or_import_meta_for_source(id, was_unwrapped_require) }
unsafe { (*p.cast::<T>()).require_or_import_meta_for_source(id) }
}
Self {
// Type-erased to `*mut ()` and cast back to `*mut T` inside the thunk before dereference.
Expand Down Expand Up @@ -2013,7 +1999,6 @@ pub(crate) mod __gated_printer {
match statement {
None => self.print_require_or_import_expr(
import.import_record_index,
false,
&[],
Expr::EMPTY,
Level::Lowest,
Expand All @@ -2034,7 +2019,6 @@ pub(crate) mod __gated_printer {
self.print_equals();
self.print_require_or_import_expr(
import.import_record_index,
false,
&[],
Expr::EMPTY,
Level::Lowest,
Expand Down Expand Up @@ -2084,7 +2068,6 @@ pub(crate) mod __gated_printer {
match statement {
None => self.print_require_or_import_expr(
import.import_record_index,
false,
&[],
Expr::EMPTY,
Level::Lowest,
Expand Down Expand Up @@ -2707,7 +2690,6 @@ pub(crate) mod __gated_printer {
pub(crate) fn print_require_or_import_expr(
&mut self,
import_record_index: u32,
was_unwrapped_require: bool,
leading_interior_comments: &[G::Comment],
import_options: Expr,
level_: Level,
Expand Down Expand Up @@ -2756,10 +2738,9 @@ pub(crate) mod __gated_printer {
}

if record.source_index.is_valid() {
let mut meta = self.options.require_or_import_meta_for_source(
record.source_index.get(),
was_unwrapped_require,
);
let mut meta = self
.options
.require_or_import_meta_for_source(record.source_index.get());

// Don't need the namespace object if the result is unused anyway
if flags.contains(ExprFlag::ExprResultIsUnused) {
Expand Down Expand Up @@ -2803,7 +2784,6 @@ pub(crate) mod __gated_printer {
// Internal "require()" or "import()"
let has_side_effects = meta.wrapper_ref.is_valid()
|| meta.exports_ref.is_valid()
|| meta.was_unwrapped_require
|| self.options.input_files_for_dev_server.is_some();
if record.kind == ImportKind::Dynamic {
self.print_space_before_identifier();
Expand Down Expand Up @@ -2836,7 +2816,7 @@ pub(crate) mod __gated_printer {
let path = &input_files[record.source_index.get() as usize].path;
self.print_string_literal_utf8(path.pretty, false);
self.print(b")");
} else if !meta.was_unwrapped_require {
} else {
// Call the wrapper
if meta.wrapper_ref.is_valid() {
self.print_space_before_identifier();
Expand Down Expand Up @@ -2872,10 +2852,6 @@ pub(crate) mod __gated_printer {
self.print(b")");
}
}
} else {
if !meta.exports_ref.is_empty() {
self.print_symbol(meta.exports_ref);
}
}

if wrap_comma_operator {
Expand Down Expand Up @@ -3582,9 +3558,10 @@ pub(crate) mod __gated_printer {
}
}
ExprData::ERequireString(e) => {
// An unwrapped require() is consumed by the parser's visit_decls, never printed.
debug_assert!(e.unwrapped_id.is_none());
self.print_require_or_import_expr(
e.import_record_index,
e.unwrapped_id.is_some(),
&[],
Expr::EMPTY,
level,
Expand Down Expand Up @@ -3652,7 +3629,6 @@ pub(crate) mod __gated_printer {
} else {
self.print_require_or_import_expr(
e.import_record_index,
false,
&[], // e.leading_interior_comments,
e.options,
level,
Expand Down
Loading
Loading