Skip to content
Merged
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
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions turbopack/crates/turbopack-ecmascript/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ tokio = { workspace = true }
tracing = { workspace = true }
turbo-bincode = { workspace = true }
turbo-esregex = { workspace = true }
turbo-frozenmap = { workspace = true }
turbo-rcstr = { workspace = true }
turbo-tasks = { workspace = true }
turbo-tasks-fs = { workspace = true }
Expand Down
31 changes: 17 additions & 14 deletions turbopack/crates/turbopack-ecmascript/src/references/esm/export.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ use swc_core::{
},
quote, quote_expr,
};
use turbo_frozenmap::FrozenMap;
use turbo_rcstr::{RcStr, rcstr};
use turbo_tasks::{
FxIndexMap, NonLocalValue, ResolvedVc, TryFlatJoinIterExt, Vc, trace::TraceRawVcs, turbofmt,
Expand Down Expand Up @@ -529,7 +530,7 @@ async fn emit_star_exports_issue(source_ident: Vc<AssetIdent>, message: RcStr) -
#[derive(Hash, Debug)]
pub struct EsmExports {
/// Explicit exports
pub exports: BTreeMap<RcStr, EsmExport>,
pub exports: FrozenMap<RcStr, EsmExport>,
/// Unexpanded `export * from ...` statements (expanded in `expand_star_exports`)
pub star_exports: Vec<ResolvedVc<Box<dyn ModuleReference>>>,
}
Expand All @@ -541,7 +542,7 @@ pub struct EsmExports {
#[turbo_tasks::value(shared)]
#[derive(Hash, Debug)]
pub struct ExpandedExports {
pub exports: BTreeMap<RcStr, EsmExport>,
pub exports: FrozenMap<RcStr, EsmExport>,
/// Modules we couldn't analyze all exports of.
pub dynamic_exports: Vec<ResolvedVc<Box<dyn EcmascriptChunkPlaceable>>>,
}
Expand All @@ -559,16 +560,16 @@ impl EsmExports {
module_reference: Vc<Box<dyn ModuleReference>>,
) -> Result<Vc<EcmascriptExports>> {
let module_reference = module_reference.to_resolved().await?;
let mut exports = BTreeMap::new();
let mut exports = Vec::new();
let default = rcstr!("default");
exports.insert(
exports.push((
default.clone(),
EsmExport::ImportedBinding(module_reference, default, false),
);
));

Ok(EcmascriptExports::EsmExports(
EsmExports {
exports,
exports: FrozenMap::from(exports),
star_exports: vec![module_reference],
}
.resolved_cell(),
Expand All @@ -581,7 +582,11 @@ impl EsmExports {
&self,
export_usage_info: Vc<ModuleExportUsageInfo>,
) -> Result<Vc<ExpandedExports>> {
let mut exports: BTreeMap<RcStr, EsmExport> = self.exports.clone();
let mut exports: BTreeMap<_, _> = self
.exports
.iter()
.map(|(k, v)| (k.clone(), v.clone()))
.collect();
let mut dynamic_exports = vec![];
let export_usage_info = export_usage_info.await?;

Expand All @@ -608,12 +613,10 @@ impl EsmExports {
continue;
}

if !exports.contains_key(export) {
exports.insert(
export.clone(),
EsmExport::ImportedBinding(esm_ref, export.clone(), false),
);
}
// the spec indicates first-one-wins: https://tc39.es/ecma262/#_ref_9060
exports
.entry(export.clone())
.or_insert_with(|| EsmExport::ImportedBinding(esm_ref, export.clone(), false));
}

if !export_info.dynamic_exporting_modules.is_empty() {
Expand All @@ -622,7 +625,7 @@ impl EsmExports {
}

Ok(ExpandedExports {
exports,
exports: FrozenMap::from(exports),
dynamic_exports,
}
.cell())
Expand Down
26 changes: 13 additions & 13 deletions turbopack/crates/turbopack-ecmascript/src/references/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@ pub mod util;
pub mod worker;

use std::{
collections::BTreeMap,
future::Future,
mem::take,
ops::Deref,
Expand Down Expand Up @@ -62,6 +61,7 @@ use swc_core::{
};
use tokio::sync::OnceCell;
use tracing::Instrument;
use turbo_frozenmap::FrozenMap;
use turbo_rcstr::{RcStr, rcstr};
use turbo_tasks::{
FxIndexMap, FxIndexSet, NonLocalValue, PrettyPrintError, ReadRef, ResolvedVc, TaskInput,
Expand Down Expand Up @@ -907,21 +907,21 @@ async fn analyze_ecmascript_module_internal(
analysis.add_esm_reexport_reference(i);
}
Reexport::Namespace { exported: n } => {
esm_exports.insert(
esm_exports.push((
n.as_str().into(),
EsmExport::ImportedNamespace(ResolvedVc::upcast(reference)),
);
));
analysis.add_esm_reexport_reference(i);
}
Reexport::Named { imported, exported } => {
esm_exports.insert(
esm_exports.push((
exported.as_str().into(),
EsmExport::ImportedBinding(
ResolvedVc::upcast(reference),
imported.to_string().into(),
false,
),
);
));
analysis.add_esm_reexport_reference(i);
}
}
Expand All @@ -939,7 +939,7 @@ async fn analyze_ecmascript_module_internal(
}

let esm_exports = EsmExports {
exports: esm_exports,
exports: FrozenMap::from(esm_exports),
star_exports: esm_star_exports,
}
.cell();
Expand Down Expand Up @@ -4003,7 +4003,7 @@ struct ModuleReferencesVisitor<'a> {
old_analyzer: StaticAnalyser,
import_references: &'a [ResolvedVc<EsmAssetReference>],
analysis: &'a mut AnalyzeEcmascriptModuleResultBuilder,
esm_exports: BTreeMap<RcStr, EsmExport>,
esm_exports: Vec<(RcStr, EsmExport)>,
webpack_runtime: Option<(RcStr, Span)>,
webpack_entry: bool,
webpack_chunks: Vec<Lit>,
Expand All @@ -4026,7 +4026,7 @@ impl<'a> ModuleReferencesVisitor<'a> {
old_analyzer: StaticAnalyser::default(),
import_references,
analysis,
esm_exports: BTreeMap::new(),
esm_exports: Vec::new(),
webpack_runtime: None,
webpack_entry: false,
webpack_chunks: Vec::new(),
Expand Down Expand Up @@ -4186,7 +4186,7 @@ impl VisitAstPath for ModuleReferencesVisitor<'_> {
)
}
};
self.esm_exports.insert(key, export);
self.esm_exports.push((key, export));
}
}
}
Expand All @@ -4212,7 +4212,7 @@ impl VisitAstPath for ModuleReferencesVisitor<'_> {
let liveness = self.get_export_ident_liveness((id.clone(), ctx));
let name: RcStr = id.as_str().into();
self.esm_exports
.insert(name.clone(), EsmExport::LocalBinding(name, liveness));
.push((name.clone(), EsmExport::LocalBinding(name, liveness)));
};
match decl {
Decl::Class(ClassDecl { ident, .. }) | Decl::Fn(FnDecl { ident, .. }) => {
Expand Down Expand Up @@ -4252,14 +4252,14 @@ impl VisitAstPath for ModuleReferencesVisitor<'_> {
export: &'ast ExportDefaultExpr,
ast_path: &mut AstNodePath<AstParentNodeRef<'r>>,
) {
self.esm_exports.insert(
self.esm_exports.push((
rcstr!("default"),
EsmExport::LocalBinding(
magic_identifier::mangle("default export").into(),
// The expression passed to `export default` cannot be mutated
Liveness::Constant,
),
);
));
if self.analyze_mode.is_code_gen() {
self.analysis.add_code_gen(EsmModuleItem::new(
as_parent_path(ast_path).into(),
Expand Down Expand Up @@ -4287,7 +4287,7 @@ impl VisitAstPath for ModuleReferencesVisitor<'_> {
Liveness::Constant,
),
};
self.esm_exports.insert(rcstr!("default"), export);
self.esm_exports.push((rcstr!("default"), export));
}
DefaultDecl::TsInterfaceDecl(..) => {
// ignore
Expand Down
38 changes: 16 additions & 22 deletions turbopack/crates/turbopack-ecmascript/src/rename/module.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
use std::collections::BTreeMap;

use anyhow::{Result, bail};
use turbo_frozenmap::FrozenMap;
use turbo_tasks::{ResolvedVc, Vc};
use turbo_tasks_fs::{File, FileContent};
use turbopack_core::{
Expand Down Expand Up @@ -208,33 +207,28 @@ impl EcmascriptChunkPlaceable for EcmascriptModuleRenameModule {
#[turbo_tasks::function]
async fn get_exports(&self) -> Result<Vc<EcmascriptExports>> {
let reference = self.module_reference().await?;
let mut exports = BTreeMap::new();

match &self.part {
let export = match &self.part {
ModulePart::RenamedExport {
original_export,
export,
} => {
exports.insert(
export.clone(),
EsmExport::ImportedBinding(
ResolvedVc::upcast(reference),
original_export.clone(),
false,
),
);
}
ModulePart::RenamedNamespace { export } => {
exports.insert(
export.clone(),
EsmExport::ImportedNamespace(ResolvedVc::upcast(reference)),
);
}
} => (
export.clone(),
EsmExport::ImportedBinding(
ResolvedVc::upcast(reference),
original_export.clone(),
false,
),
),
ModulePart::RenamedNamespace { export } => (
export.clone(),
EsmExport::ImportedNamespace(ResolvedVc::upcast(reference)),
),
_ => bail!("Unexpected ModulePart for EcmascriptModuleRenameModule"),
}
};

let exports = EsmExports {
exports,
exports: FrozenMap::from_unique_sorted_box(Box::new([export])),
star_exports: Vec::new(),
}
.resolved_cell();
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
use std::collections::BTreeMap;

use anyhow::{Result, bail};
use turbo_frozenmap::FrozenMap;
use turbo_tasks::{ResolvedVc, Vc};
use turbopack_core::{
chunk::{
Expand Down Expand Up @@ -189,18 +188,16 @@ impl EcmascriptAnalyzable for EcmascriptModuleFacadeModule {
impl EcmascriptChunkPlaceable for EcmascriptModuleFacadeModule {
#[turbo_tasks::function]
async fn get_exports(&self) -> Result<Vc<EcmascriptExports>> {
let mut exports = BTreeMap::new();
let mut star_exports = Vec::new();

let EcmascriptExports::EsmExports(esm_exports) = &*self.module.get_exports().await? else {
bail!("EcmascriptModuleFacadeModule must only be used on modules with EsmExports");
};
let esm_exports = esm_exports.await?;
let mut exports = Vec::with_capacity(esm_exports.exports.len());
for (name, export) in &esm_exports.exports {
let name = name.clone();
match export {
EsmExport::LocalBinding(_, liveness) => {
exports.insert(
exports.push((
name.clone(),
EsmExport::ImportedBinding(
ResolvedVc::upcast(
Expand All @@ -215,27 +212,26 @@ impl EcmascriptChunkPlaceable for EcmascriptModuleFacadeModule {
name,
*liveness == Liveness::Mutable,
),
);
));
}
EsmExport::ImportedNamespace(reference) => {
exports.insert(name, EsmExport::ImportedNamespace(*reference));
exports.push((name, EsmExport::ImportedNamespace(*reference)));
}
EsmExport::ImportedBinding(reference, imported_name, mutable) => {
exports.insert(
exports.push((
name,
EsmExport::ImportedBinding(*reference, imported_name.clone(), *mutable),
);
));
}
EsmExport::Error => {
exports.insert(name, EsmExport::Error);
exports.push((name, EsmExport::Error));
}
}
}
star_exports.extend(esm_exports.star_exports.iter().copied());

let exports = EsmExports {
exports,
star_exports,
exports: FrozenMap::from_unique_sorted_box(exports.into_boxed_slice()),
star_exports: esm_exports.star_exports.clone(),
}
.resolved_cell();
Ok(EcmascriptExports::EsmExports(exports).cell())
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
use std::collections::BTreeMap;

use anyhow::{Result, bail};
use turbo_frozenmap::FrozenMap;
use turbo_tasks::{ResolvedVc, Vc};
use turbopack_core::{
chunk::{
Expand Down Expand Up @@ -142,27 +141,27 @@ impl EcmascriptChunkPlaceable for EcmascriptModuleLocalsModule {
bail!("EcmascriptModuleLocalsModule must only be used on modules with EsmExports");
};
let esm_exports = exports.await?;
let mut exports = BTreeMap::new();
let mut exports = Vec::new();

for (name, export) in &esm_exports.exports {
match export {
EsmExport::ImportedBinding(..) | EsmExport::ImportedNamespace(..) => {
// not included in locals module
}
EsmExport::LocalBinding(local_name, liveness) => {
exports.insert(
exports.push((
name.clone(),
EsmExport::LocalBinding(local_name.clone(), *liveness),
);
));
}
EsmExport::Error => {
exports.insert(name.clone(), EsmExport::Error);
exports.push((name.clone(), EsmExport::Error));
}
}
}

let exports = EsmExports {
exports,
exports: FrozenMap::from_unique_sorted_box(exports.into_boxed_slice()),
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So this "from sorted box" hinges on esm_exports.exports being a sorted vec already.
So if somebody accidentally changes esm_exports.exports to be a different key-value collection, then this would silently break?

Copy link
Copy Markdown
Member Author

@bgw bgw Apr 14, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

star_exports: vec![],
}
.resolved_cell();
Expand Down
Loading