turbopack_ecmascript/references/
exports_info.rs1use anyhow::Result;
2use bincode::{Decode, Encode};
3use swc_core::{
4 common::DUMMY_SP,
5 ecma::ast::{Expr, Ident, KeyValueProp, ObjectLit, PropName, PropOrSpread},
6 quote,
7};
8use turbo_rcstr::rcstr;
9use turbo_tasks::{NonLocalValue, ResolvedVc, Vc, debug::ValueDebugFormat};
10use turbopack_core::chunk::ChunkingContext;
11
12use crate::{
13 ast_path_trie::{AstPathId, AstPathTrie},
14 chunk::{EcmascriptChunkPlaceable, EcmascriptExports},
15 code_gen::{CodeGen, CodeGeneration},
16 create_visitor, magic_identifier,
17 references::esm::mangle::mangled_export_names,
18};
19
20#[derive(PartialEq, Eq, ValueDebugFormat, NonLocalValue, Hash, Debug, Encode, Decode)]
28pub struct ExportsInfoBinding {}
29
30impl ExportsInfoBinding {
31 #[allow(clippy::new_without_default)]
32 pub fn new() -> Self {
33 ExportsInfoBinding {}
34 }
35
36 pub async fn code_generation(
37 &self,
38 _trie: &AstPathTrie,
39 chunking_context: Vc<Box<dyn ChunkingContext>>,
40 module: ResolvedVc<Box<dyn EcmascriptChunkPlaceable>>,
41 exports: ResolvedVc<EcmascriptExports>,
42 ) -> Result<CodeGeneration> {
43 let export_usage_info = chunking_context
44 .module_export_usage(*ResolvedVc::upcast(module))
45 .await?;
46 let export_usage_info = export_usage_info.export_usage.await?;
47 let exports = exports.await?;
53 let mangled_names = mangled_export_names(*module, chunking_context).await?;
54
55 let props = if let EcmascriptExports::EsmExports(exports) = &*exports {
56 exports
57 .await?
58 .exports
59 .keys()
60 .map(|e| {
61 let is_used = export_usage_info.is_export_used(e);
62 let used: Expr = is_used.into();
63 let can_mangle_names = mangled_names.as_ref().filter(|_| is_used);
72 let can_mangle_expr: Expr = can_mangle_names.is_some().into();
73 let mangled_name: Expr =
74 match can_mangle_names {
75 Some(names) => Expr::Lit(names.get(e).unwrap_or(e).as_str().into()),
76 None => Expr::Lit(swc_core::ecma::ast::Lit::Null(
77 swc_core::ecma::ast::Null { span: DUMMY_SP },
78 )),
79 };
80 PropOrSpread::Prop(Box::new(swc_core::ecma::ast::Prop::KeyValue(
81 KeyValueProp {
82 key: PropName::Str(e.as_str().into()),
83 value: quote!(
84 "{ used: $v, canMangle: $c, mangledName: $m }" as Box<Expr>,
85 v: Expr = used,
86 c: Expr = can_mangle_expr,
87 m: Expr = mangled_name
88 ),
89 },
90 )))
91 })
92 .collect()
93 } else {
94 vec![]
95 };
96
97 let data = Expr::Object(ObjectLit {
98 props,
99 span: DUMMY_SP,
100 });
101
102 Ok(CodeGeneration::hoisted_stmt(
103 rcstr!("__webpack_exports_info__"),
104 quote!(
105 "var $name = $data;" as Stmt,
106 name = exports_ident(),
107 data: Expr = data
108 ),
109 ))
110 }
111}
112
113impl From<ExportsInfoBinding> for CodeGen {
114 fn from(val: ExportsInfoBinding) -> Self {
115 CodeGen::ExportsInfoBinding(val)
116 }
117}
118
119#[derive(PartialEq, Eq, ValueDebugFormat, NonLocalValue, Hash, Debug, Encode, Decode)]
125pub struct ExportsInfoRef {
126 ast_path: AstPathId,
127}
128
129impl ExportsInfoRef {
130 pub fn new(ast_path: AstPathId) -> Self {
131 ExportsInfoRef { ast_path }
132 }
133
134 pub async fn code_generation(
135 &self,
136 trie: &AstPathTrie,
137 _chunking_context: Vc<Box<dyn ChunkingContext>>,
138 ) -> Result<CodeGeneration> {
139 let visitor = create_visitor!(trie, self.ast_path, visit_mut_expr, |expr: &mut Expr| {
140 *expr = Expr::Ident(exports_ident());
141 });
142
143 Ok(CodeGeneration::visitors(vec![visitor]))
144 }
145}
146
147impl From<ExportsInfoRef> for CodeGen {
148 fn from(val: ExportsInfoRef) -> Self {
149 CodeGen::ExportsInfoRef(val)
150 }
151}
152
153fn exports_ident() -> Ident {
154 Ident::new(
155 magic_identifier::mangle("__webpack_exports_info__").into(),
156 DUMMY_SP,
157 Default::default(),
158 )
159}