diff --git a/src/ast/s.rs b/src/ast/s.rs index 247898d67059..4a4223adb007 100644 --- a/src/ast/s.rs +++ b/src/ast/s.rs @@ -241,11 +241,7 @@ pub struct Local { pub kind: Kind, // = Kind::KVar pub decls: G::DeclList, // = .{} pub is_export: bool, // = false - /// The TypeScript compiler doesn't generate code for "import foo = bar" - /// statements where the import is never used. - pub was_ts_import_equals: bool, // = false - - pub was_commonjs_export: bool, // = false + pub origin: LocalOrigin, } impl Default for Local { @@ -254,8 +250,7 @@ impl Default for Local { kind: Kind::default(), decls: bun_alloc::AstAlloc::vec(), is_export: false, - was_ts_import_equals: false, - was_commonjs_export: false, + origin: LocalOrigin::Normal, } } } @@ -271,7 +266,28 @@ impl Local { } self.kind == other.kind && self.is_export == other.is_export - && self.was_commonjs_export == other.was_commonjs_export + && self.origin.is_commonjs_export() == other.origin.is_commonjs_export() + } +} + +#[derive(Clone, Copy, PartialEq, Eq, Default)] +pub enum LocalOrigin { + #[default] + Normal, + /// From TS `import x = ...`; dropped if unused (matches tsc). + TsImportEquals, + /// From rewritten `exports.x = ...`. + CommonJsExport, +} + +impl LocalOrigin { + #[inline] + pub fn is_ts_import_equals(self) -> bool { + matches!(self, Self::TsImportEquals) + } + #[inline] + pub fn is_commonjs_export(self) -> bool { + matches!(self, Self::CommonJsExport) } } diff --git a/src/bundler/OutputFile.rs b/src/bundler/OutputFile.rs index fe1ba06bd9da..995b3096d36a 100644 --- a/src/bundler/OutputFile.rs +++ b/src/bundler/OutputFile.rs @@ -104,11 +104,26 @@ impl Clone for OutputFile { #[derive(Default, Clone, Copy)] pub struct BakeExtra { - pub(crate) is_route: bool, - pub fully_static: bool, + pub route: BakeRouteKind, pub bake_is_runtime: bool, } +#[derive(Default, Clone, Copy, PartialEq, Eq)] +pub enum BakeRouteKind { + #[default] + NotRoute, + Route, + /// Route with no transitive `"use client"` boundary. + FullyStaticRoute, +} + +impl BakeRouteKind { + #[inline] + pub fn is_fully_static(self) -> bool { + matches!(self, Self::FullyStaticRoute) + } +} + pub type Index = bun_core::GenericIndex; pub type IndexOptional = bun_core::GenericIndexOptional; diff --git a/src/bundler/bundle_v2.rs b/src/bundler/bundle_v2.rs index 648bb40bcf38..3e7015e5e1e5 100644 --- a/src/bundler/bundle_v2.rs +++ b/src/bundler/bundle_v2.rs @@ -6252,7 +6252,8 @@ pub mod bv2_impl { }; if resolve_result.flags.is_external() { - if resolve_result.flags.is_external_and_rewrite_import_path() + if resolve_result.flags.external_kind() + == bun_resolver::ExternalKind::ExternalRewritePath && !strings::eql_long( resolve_result.path_pair.primary.text, import_record.path.text, diff --git a/src/bundler/linker_context/convertStmtsForChunk.rs b/src/bundler/linker_context/convertStmtsForChunk.rs index 746fdc6af3be..5035de8fbae6 100644 --- a/src/bundler/linker_context/convertStmtsForChunk.rs +++ b/src/bundler/linker_context/convertStmtsForChunk.rs @@ -451,7 +451,7 @@ pub(crate) fn convert_stmts_for_chunk( stmt = Stmt::alloc(copied, stmt.loc); stmt.data.s_local_mut().unwrap().is_export = false; } else if FeatureFlags::UNWRAP_COMMONJS_TO_ESM - && s.was_commonjs_export + && s.origin.is_commonjs_export() && wrap == WrapKind::Cjs { debug_assert!(s.decls.len() == 1); diff --git a/src/bundler/linker_context/generateChunksInParallel.rs b/src/bundler/linker_context/generateChunksInParallel.rs index 6d44301fcbe1..965ade26477a 100644 --- a/src/bundler/linker_context/generateChunksInParallel.rs +++ b/src/bundler/linker_context/generateChunksInParallel.rs @@ -1240,9 +1240,13 @@ pub(crate) fn generate_chunks_in_parallel( if output_kind == options::OutputKind::EntryPoint && side == options::Side::Server { - extra.is_route = true; - extra.fully_static = !static_route_visitor - .has_transitive_use_client(chunk.entry_point.source_index()); + extra.route = if static_route_visitor + .has_transitive_use_client(chunk.entry_point.source_index()) + { + BakeRouteKind::Route + } else { + BakeRouteKind::FullyStaticRoute + }; } break 'brk extra; @@ -1290,4 +1294,4 @@ pub(crate) fn generate_chunks_in_parallel( use crate::EntryPoint; use crate::options::SourceMapOption; -use crate::output_file::BakeExtra; +use crate::output_file::{BakeExtra, BakeRouteKind}; diff --git a/src/bundler/linker_context/generateCodeForFileInChunkJS.rs b/src/bundler/linker_context/generateCodeForFileInChunkJS.rs index 6f5dfa72c6b3..dd0ff1d8f75f 100644 --- a/src/bundler/linker_context/generateCodeForFileInChunkJS.rs +++ b/src/bundler/linker_context/generateCodeForFileInChunkJS.rs @@ -970,8 +970,7 @@ fn merge_adjacent_local_stmts(stmts: &mut Vec, _arena: &Bump) { S::Local { decls: Vec::move_from_list(clone), is_export: before.is_export, - was_commonjs_export: before.was_commonjs_export, - was_ts_import_equals: before.was_ts_import_equals, + origin: before.origin, kind: before.kind, }, prev_loc, diff --git a/src/js_parser/p.rs b/src/js_parser/p.rs index ae8c8539a8db..91e474677f7f 100644 --- a/src/js_parser/p.rs +++ b/src/js_parser/p.rs @@ -8551,7 +8551,9 @@ impl<'a, const TYPESCRIPT: bool, const SCAN_ONLY: bool> P<'a, TYPESCRIPT, SCAN_O } } js_ast::StmtData::SLocal(local) => { - if local.was_commonjs_export || self.commonjs_named_exports.count() == 0 { + if local.origin.is_commonjs_export() + || self.commonjs_named_exports.count() == 0 + { for decl in local.decls.slice() { if let Some(value) = &decl.value { if !matches!(value.data, js_ast::ExprData::EMissing(_)) diff --git a/src/js_parser/parse/parse_entry.rs b/src/js_parser/parse/parse_entry.rs index a2c38296078b..470e7da22b75 100644 --- a/src/js_parser/parse/parse_entry.rs +++ b/src/js_parser/parse/parse_entry.rs @@ -894,8 +894,7 @@ impl<'a> Parser<'a> { let _local = S::Local { kind: local.kind, is_export: local.is_export, - was_ts_import_equals: local.was_ts_import_equals, - was_commonjs_export: local.was_commonjs_export, + origin: local.origin, decls: G::DeclList::init_one(G::Decl { binding: decl.binding, value: decl.value, diff --git a/src/js_parser/parse/parse_typescript.rs b/src/js_parser/parse/parse_typescript.rs index 0be9bef96c78..462c636ecc2a 100644 --- a/src/js_parser/parse/parse_typescript.rs +++ b/src/js_parser/parse/parse_typescript.rs @@ -376,7 +376,7 @@ impl<'a, const TYPESCRIPT: bool, const SCAN_ONLY: bool> P<'a, TYPESCRIPT, SCAN_O for stmt in stmts.iter() { match &stmt.data { StmtData::SLocal(local) => { - if local.was_ts_import_equals && !local.is_export { + if local.origin.is_ts_import_equals() && !local.is_export { import_equal_count += 1; } } @@ -564,8 +564,7 @@ impl<'a, const TYPESCRIPT: bool, const SCAN_ONLY: bool> P<'a, TYPESCRIPT, SCAN_O kind, decls, is_export: opts.is_export, - was_ts_import_equals: true, - ..Default::default() + origin: S::LocalOrigin::TsImportEquals, }, loc, )) diff --git a/src/js_parser/scan/scan_imports.rs b/src/js_parser/scan/scan_imports.rs index 6ce633a54a6d..b34a49c67754 100644 --- a/src/js_parser/scan/scan_imports.rs +++ b/src/js_parser/scan/scan_imports.rs @@ -645,7 +645,7 @@ impl<'a> ImportScanner<'a> { // Remove unused import-equals statements, since those likely // correspond to types instead of values - if st.was_ts_import_equals && !st.is_export && st.decls.len_u32() > 0 { + if st.origin.is_ts_import_equals() && !st.is_export && st.decls.len_u32() > 0 { let decl = &st.decls.slice()[0]; // Skip to the underlying reference diff --git a/src/js_parser/visit/mod.rs b/src/js_parser/visit/mod.rs index f9a3fe97bc7f..efe561d975b6 100644 --- a/src/js_parser/visit/mod.rs +++ b/src/js_parser/visit/mod.rs @@ -1523,7 +1523,7 @@ impl<'a, const TYPESCRIPT: bool, const SCAN_ONLY: bool> P<'a, TYPESCRIPT, SCAN_O if local.kind.is_using() { continue; } - if !local.is_export && !local.was_commonjs_export { + if !local.is_export && !local.origin.is_commonjs_export() { let mut any_decl_in_const_values = local.kind == LocalKind::KConst; let decls: &mut [Decl] = local.decls.slice_mut(); let mut end: usize = 0; diff --git a/src/js_parser/visit/visit_stmt.rs b/src/js_parser/visit/visit_stmt.rs index c28c444521cf..8b5ee23d68e5 100644 --- a/src/js_parser/visit/visit_stmt.rs +++ b/src/js_parser/visit/visit_stmt.rs @@ -1472,9 +1472,8 @@ impl<'a, const TYPESCRIPT: bool, const SCAN_ONLY: bool> P<'a, TYPESCRIPT, SCAN_O S::Local { kind: S::Kind::KVar, is_export: false, - was_commonjs_export: true, + origin: S::LocalOrigin::CommonJsExport, decls, - ..Default::default() }, stmt.loc, ); diff --git a/src/resolver/lib.rs b/src/resolver/lib.rs index a86684dee288..bd0f2d956935 100644 --- a/src/resolver/lib.rs +++ b/src/resolver/lib.rs @@ -52,8 +52,9 @@ pub use tsconfig_json::TSConfigJSON; pub use ::bun_install_types::resolver_hooks as install_types; pub use resolver::{AnyResolveWatcher, BrowserMapPathKind, Bufs, Dirname, Resolver}; pub use result::{ - DebugLogs, DirEntryResolveQueueItem, FlushMode, LoadResult, MatchResult, MatchStatus, PathPair, - PendingResolution, PendingResolutionTag, Result, ResultFlags, ResultUnion, + DebugLogs, DirEntryResolveQueueItem, ExternalKind, FlushMode, LoadResult, MatchResult, + MatchStatus, PathPair, PendingResolution, PendingResolutionTag, Result, ResultFlags, + ResultUnion, }; pub use standalone_module_graph::StandaloneModuleGraph; diff --git a/src/resolver/resolver.rs b/src/resolver/resolver.rs index b3c577215aee..3bd483fe1153 100644 --- a/src/resolver/resolver.rs +++ b/src/resolver/resolver.rs @@ -269,8 +269,9 @@ pub use ::bun_options_types::global_cache::GlobalCache; // inside `impl Resolver` resolve unchanged. use crate::options; use crate::result::{ - DebugLogs, DirEntryResolveQueueItem, FlushMode, LoadResult, MatchResult, MatchStatus, PathPair, - PendingResolution, PendingResolutionTag, Result, ResultFlags, ResultUnion, + DebugLogs, DirEntryResolveQueueItem, ExternalKind, FlushMode, LoadResult, MatchResult, + MatchStatus, PathPair, PendingResolution, PendingResolutionTag, Result, ResultFlags, + ResultUnion, }; use crate::standalone_module_graph::StandaloneModuleGraph; use bun_alloc as allocators; @@ -2163,8 +2164,11 @@ impl<'a> Resolver<'a> { .is_success() { let mut flags = ResultFlags::default(); - flags.set_is_external(match_result.is_external); - flags.set_is_external_and_rewrite_import_path(match_result.is_external); + flags.set_external_kind(if match_result.is_external { + ExternalKind::ExternalRewritePath + } else { + ExternalKind::NotExternal + }); return ResultUnion::Success(Result { path_pair: match_result.path_pair, dirname_fd: match_result.dirname_fd, @@ -2342,12 +2346,13 @@ impl<'a> Resolver<'a> { result.flags.is_from_node_modules() || res.is_node_module, ); result.module_type = res.module_type; - result.flags.set_is_external(res.is_external); // Potentially rewrite the import path if it's external that // was remapped to a different path - result - .flags - .set_is_external_and_rewrite_import_path(result.flags.is_external()); + result.flags.set_external_kind(if res.is_external { + ExternalKind::ExternalRewritePath + } else { + ExternalKind::NotExternal + }); if result.path_pair.primary.is_disabled && result.path_pair.secondary.is_none() { return ResultUnion::Success(result); @@ -2389,13 +2394,14 @@ impl<'a> Resolver<'a> { result.file_fd = remapped.file_fd; result.package_json = remapped.package_json; result.module_type = remapped.module_type; - result.flags.set_is_external(remapped.is_external); // Potentially rewrite the import path if it's external that // was remapped to a different path - result.flags.set_is_external_and_rewrite_import_path( - result.flags.is_external(), - ); + result.flags.set_external_kind(if remapped.is_external { + ExternalKind::ExternalRewritePath + } else { + ExternalKind::NotExternal + }); result.flags.set_is_from_node_modules( result.flags.is_from_node_modules() diff --git a/src/resolver/result.rs b/src/resolver/result.rs index e8b99ae85da2..c1212bab954f 100644 --- a/src/resolver/result.rs +++ b/src/resolver/result.rs @@ -127,8 +127,9 @@ impl Default for Result { bitflags::bitflags! { #[derive(Default, Clone, Copy)] pub struct ResultFlags: u8 { + // Bits 0..=1 encode [`ExternalKind`]; write via `set_external_kind`. const IS_EXTERNAL = 1 << 0; - const IS_EXTERNAL_AND_REWRITE_IMPORT_PATH = 1 << 1; + const REWRITE_IMPORT_PATH = 1 << 1; const IS_STANDALONE_MODULE = 1 << 2; // This is true when the package was loaded from within the node_modules directory. const IS_FROM_NODE_MODULES = 1 << 3; @@ -138,23 +139,43 @@ bitflags::bitflags! { } } -// Convenience accessors with field-style names. +#[derive(Clone, Copy, PartialEq, Eq, Default)] +pub enum ExternalKind { + #[default] + NotExternal, + External, + /// External, and the import specifier should be rewritten to the resolved path. + ExternalRewritePath, +} + impl ResultFlags { #[inline] pub fn is_external(self) -> bool { self.contains(Self::IS_EXTERNAL) } #[inline] - pub(crate) fn set_is_external(&mut self, v: bool) { - self.set(Self::IS_EXTERNAL, v) - } - #[inline] - pub fn is_external_and_rewrite_import_path(self) -> bool { - self.contains(Self::IS_EXTERNAL_AND_REWRITE_IMPORT_PATH) + pub fn external_kind(self) -> ExternalKind { + debug_assert!( + !self.contains(Self::REWRITE_IMPORT_PATH) || self.contains(Self::IS_EXTERNAL) + ); + if !self.contains(Self::IS_EXTERNAL) { + ExternalKind::NotExternal + } else if self.contains(Self::REWRITE_IMPORT_PATH) { + ExternalKind::ExternalRewritePath + } else { + ExternalKind::External + } } #[inline] - pub(crate) fn set_is_external_and_rewrite_import_path(&mut self, v: bool) { - self.set(Self::IS_EXTERNAL_AND_REWRITE_IMPORT_PATH, v) + pub(crate) fn set_external_kind(&mut self, kind: ExternalKind) { + self.set( + Self::IS_EXTERNAL, + !matches!(kind, ExternalKind::NotExternal), + ); + self.set( + Self::REWRITE_IMPORT_PATH, + matches!(kind, ExternalKind::ExternalRewritePath), + ); } #[inline] pub(crate) fn is_standalone_module(self) -> bool { diff --git a/src/runtime/bake/production.rs b/src/runtime/bake/production.rs index 137be91eb6e6..2d4525c8e9c8 100644 --- a/src/runtime/bake/production.rs +++ b/src/runtime/bake/production.rs @@ -1146,7 +1146,8 @@ fn build_with_vm(ctx: Context, cwd: &[u8], pt: &mut PerThread) -> crate::Result< route.r#type.get(), pt.output_file(main_file_route_index) .bake_extra - .fully_static, + .route + .is_fully_static(), ) .bits(), ),