Skip to main content

turbopack_ecmascript/references/
constant_value.rs

1use anyhow::Result;
2use bincode::{Decode, Encode};
3use swc_core::{
4    common::{DUMMY_SP, FileName, SourceMap, sync::Lrc},
5    ecma::{
6        ast::{
7            ArrayLit, EsVersion, Expr, KeyValueProp, Lit, ObjectLit, Prop, PropName, Regex, Str,
8        },
9        parser::{Syntax, parse_file_as_expr},
10        visit::fields::PropField,
11    },
12    quote,
13};
14use turbo_rcstr::RcStr;
15use turbo_tasks::{NonLocalValue, Vc, debug::ValueDebugFormat};
16use turbopack_core::{chunk::ChunkingContext, compile_time_info::CompileTimeDefineValue};
17
18use crate::{
19    ast_path_trie::{AstPathId, AstPathTrie},
20    code_gen::{CodeGen, CodeGeneration},
21    create_visitor,
22};
23
24#[derive(Clone, Debug, PartialEq, Eq, Hash, ValueDebugFormat, NonLocalValue, Encode, Decode)]
25pub struct ConstantValueCodeGen {
26    value: CompileTimeDefineValue,
27    path: AstPathId,
28}
29
30impl ConstantValueCodeGen {
31    pub fn new(value: CompileTimeDefineValue, path: AstPathId) -> Self {
32        ConstantValueCodeGen { value, path }
33    }
34    pub async fn code_generation(
35        &self,
36        trie: &AstPathTrie,
37        _chunking_context: Vc<Box<dyn ChunkingContext>>,
38    ) -> Result<CodeGeneration> {
39        let value = self.value.clone();
40        let mut visitors = Vec::new();
41
42        if matches!(
43            trie.get(self.path),
44            Some(swc_core::ecma::visit::AstParentKind::Prop(
45                PropField::Shorthand
46            ))
47        ) {
48            let ast_path = trie.parent_or_root(self.path);
49            visitors.push(create_visitor!(
50                exact,
51                trie,
52                ast_path,
53                visit_mut_prop,
54                |prop: &mut Prop| {
55                    if let Prop::Shorthand(ident) = prop {
56                        *prop = Prop::KeyValue(KeyValueProp {
57                            key: PropName::Ident(ident.clone().into()),
58                            value: value_to_expr(&value).into(),
59                        });
60                    }
61                }
62            ));
63        } else {
64            visitors.push(create_visitor!(
65                trie,
66                self.path,
67                visit_mut_expr,
68                |expr: &mut Expr| {
69                    *expr = value_to_expr(&value);
70                }
71            ));
72        }
73
74        Ok(CodeGeneration::visitors(visitors))
75    }
76}
77
78impl From<ConstantValueCodeGen> for CodeGen {
79    fn from(val: ConstantValueCodeGen) -> Self {
80        CodeGen::ConstantValueCodeGen(val)
81    }
82}
83
84fn value_to_expr(value: &CompileTimeDefineValue) -> Expr {
85    match value {
86        CompileTimeDefineValue::Undefined => {
87            quote!("(\"TURBOPACK compile-time value\", void 0)" as Expr)
88        }
89        CompileTimeDefineValue::Null => {
90            quote!("(\"TURBOPACK compile-time value\", null)" as Expr)
91        }
92        CompileTimeDefineValue::Bool(true) => {
93            quote!("(\"TURBOPACK compile-time value\", true)" as Expr)
94        }
95        CompileTimeDefineValue::Bool(false) => {
96            quote!("(\"TURBOPACK compile-time value\", false)" as Expr)
97        }
98        CompileTimeDefineValue::Number(n) => {
99            quote!("(\"TURBOPACK compile-time value\", $e)" as Expr, e: Expr = n
100                .as_f64()
101                .expect("unreachable: serde-json has arbitrary_precision disabled")
102                .into())
103        }
104        CompileTimeDefineValue::String(s) => {
105            quote!("(\"TURBOPACK compile-time value\", $e)" as Expr, e: Expr = s.as_str().into())
106        }
107        CompileTimeDefineValue::BigInt(n) => {
108            quote!("(\"TURBOPACK compile-time value\", $e)" as Expr, e: Expr = Expr::Lit(Lit::BigInt(n.as_ref().clone().into())))
109        }
110        CompileTimeDefineValue::Regex(pattern, flags) => {
111            quote!("(\"TURBOPACK compile-time value\", $e)" as Expr, e: Expr = Expr::Lit(Lit::Regex(Regex {
112               span: DUMMY_SP,
113               exp: pattern.as_str().into(),
114               flags: flags.as_str().into(),
115            })))
116        }
117        CompileTimeDefineValue::Array(a) => {
118            quote!("(\"TURBOPACK compile-time value\", $e)" as Expr, e: Expr = Expr::Array(ArrayLit {
119                span: DUMMY_SP,
120                elems: a.iter().map(|i| Some(value_to_expr(i).into())).collect(),
121            }))
122        }
123        CompileTimeDefineValue::Object(m) => {
124            quote!("(\"TURBOPACK compile-time value\", $e)" as Expr, e: Expr = Expr::Object(ObjectLit {
125                span: DUMMY_SP,
126                props: m
127                    .iter()
128                    .map(|(k, v)| {
129                        swc_core::ecma::ast::PropOrSpread::Prop(
130                            Prop::KeyValue(KeyValueProp {
131                                key: PropName::Str(Str::from(k.as_str())),
132                                value: value_to_expr(v).into(),
133                            })
134                            .into(),
135                        )
136                    })
137                    .collect(),
138            }))
139        }
140        CompileTimeDefineValue::Evaluate(s) => parse_single_expr_lit(s),
141    }
142}
143
144pub(crate) fn parse_single_expr_lit(expr_lit: &RcStr) -> Expr {
145    let cm = Lrc::new(SourceMap::default());
146    let fm = cm.new_source_file(FileName::Anon.into(), expr_lit.clone());
147    parse_file_as_expr(
148        &fm,
149        Syntax::Es(Default::default()),
150        EsVersion::latest(),
151        None,
152        &mut vec![],
153    )
154    .map_or(
155        quote!("(\"Failed parsed TURBOPACK compile-time value\", $s)" as Expr, s: Expr = expr_lit.as_str().into()),
156        |expr| quote!("(\"TURBOPACK compile-time value\", $e)" as Expr, e: Expr = *expr),
157    )
158}