turbopack_ecmascript/references/
exports_info.rs

1use 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, trace::TraceRawVcs};
10use turbopack_core::chunk::ChunkingContext;
11
12use crate::{
13    chunk::{EcmascriptChunkPlaceable, EcmascriptExports},
14    code_gen::{CodeGen, CodeGeneration},
15    create_visitor, magic_identifier,
16    references::AstPath,
17};
18
19/// Responsible for initializing the `ExportsInfoBinding` object binding, so that it may be
20/// referenced in the the file.
21///
22/// There can be many references, and they appear at any nesting in the file. But we must only
23/// initialize the binding a single time.
24///
25/// This singleton behavior must be enforced by the caller!
26#[derive(
27    PartialEq, Eq, TraceRawVcs, ValueDebugFormat, NonLocalValue, Hash, Debug, Encode, Decode,
28)]
29pub struct ExportsInfoBinding {}
30
31impl ExportsInfoBinding {
32    #[allow(clippy::new_without_default)]
33    pub fn new() -> Self {
34        ExportsInfoBinding {}
35    }
36
37    pub async fn code_generation(
38        &self,
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
48        let props = if let EcmascriptExports::EsmExports(exports) = &*exports.await? {
49            exports
50                .await?
51                .exports
52                .keys()
53                .map(|e| {
54                    let used: Expr = export_usage_info.is_export_used(e).into();
55                    PropOrSpread::Prop(Box::new(swc_core::ecma::ast::Prop::KeyValue(
56                        KeyValueProp {
57                            key: PropName::Str(e.as_str().into()),
58                            value: quote!("{ used: $v }" as Box<Expr>, v: Expr = used),
59                        },
60                    )))
61                })
62                .collect()
63        } else {
64            vec![]
65        };
66
67        let data = Expr::Object(ObjectLit {
68            props,
69            span: DUMMY_SP,
70        });
71
72        Ok(CodeGeneration::hoisted_stmt(
73            rcstr!("__webpack_exports_info__"),
74            quote!(
75                "const $name = $data;" as Stmt,
76                name = exports_ident(),
77                data: Expr = data
78            ),
79        ))
80    }
81}
82
83impl From<ExportsInfoBinding> for CodeGen {
84    fn from(val: ExportsInfoBinding) -> Self {
85        CodeGen::ExportsInfoBinding(val)
86    }
87}
88
89/// Handles rewriting `__webpack_exports_info__` references into the injected binding created by
90/// ExportsInfoBinding.
91///
92/// There can be many references, and they appear at any nesting in the file. But all references
93/// refer to the same mutable object.
94#[derive(
95    PartialEq, Eq, TraceRawVcs, ValueDebugFormat, NonLocalValue, Hash, Debug, Encode, Decode,
96)]
97pub struct ExportsInfoRef {
98    ast_path: AstPath,
99}
100
101impl ExportsInfoRef {
102    pub fn new(ast_path: AstPath) -> Self {
103        ExportsInfoRef { ast_path }
104    }
105
106    pub async fn code_generation(
107        &self,
108        _chunking_context: Vc<Box<dyn ChunkingContext>>,
109    ) -> Result<CodeGeneration> {
110        let visitor = create_visitor!(self.ast_path, visit_mut_expr, |expr: &mut Expr| {
111            *expr = Expr::Ident(exports_ident());
112        });
113
114        Ok(CodeGeneration::visitors(vec![visitor]))
115    }
116}
117
118impl From<ExportsInfoRef> for CodeGen {
119    fn from(val: ExportsInfoRef) -> Self {
120        CodeGen::ExportsInfoRef(val)
121    }
122}
123
124fn exports_ident() -> Ident {
125    Ident::new(
126        magic_identifier::mangle("__webpack_exports_info__").into(),
127        DUMMY_SP,
128        Default::default(),
129    )
130}