turbopack_core/
compile_time_info.rs

1use anyhow::Result;
2use serde::{Deserialize, Serialize};
3use turbo_rcstr::RcStr;
4use turbo_tasks::{FxIndexMap, NonLocalValue, ResolvedVc, Vc, trace::TraceRawVcs};
5use turbo_tasks_fs::FileSystemPath;
6
7use crate::environment::Environment;
8
9#[macro_export]
10macro_rules! definable_name_map_pattern_internal {
11    ($name:ident) => {
12        [stringify!($name).into()]
13    };
14    ($name:ident typeof) => {
15        [stringify!($name).into(), $crate::compile_time_info::DefineableNameSegment::TypeOf]
16    };
17    // Entry point for non-recursive calls
18    ($name:ident . $($more:ident).+ typeof) => {
19        $crate::definable_name_map_pattern_internal!($($more).+ typeof, [stringify!($name).into()])
20    };
21    ($name:ident . $($more:ident).+) => {
22        $crate::definable_name_map_pattern_internal!($($more).+, [stringify!($name).into()])
23    };
24    // Pop first ident and push to end of array: (id, ..., [...]) => (..., [..., id])
25    ($name:ident, [$($array:expr),+]) => {
26        [$($array),+, stringify!($name).into()]
27    };
28    ($name:ident . $($more:ident).+, [$($array:expr),+]) => {
29        $crate::definable_name_map_pattern_internal!($($more).+, [$($array),+, stringify!($name).into()])
30    };
31    ($name:ident typeof, [$($array:expr),+]) => {
32        [$($array),+, stringify!($name).into(), $crate::compile_time_info::DefineableNameSegment::TypeOf]
33    };
34    ($name:ident . $($more:ident).+ typeof, [$($array:expr),+]) => {
35        $crate::definable_name_map_pattern_internal!($($more).+ typeof, [$($array),+, stringify!($name).into()])
36    };
37}
38
39// TODO stringify split map collect could be optimized with a marco
40#[macro_export]
41macro_rules! definable_name_map_internal {
42    // Allow spreading a map: free_var_references!(..xy.into_iter(), FOO = "bar")
43    ($map:ident, .. $value:expr) => {
44        for (key, value) in $value {
45            $map.insert(
46                key.into(),
47                value.into()
48            );
49        }
50    };
51    ($map:ident, .. $value:expr, $($more:tt)+) => {
52        $crate::definable_name_map_internal!($map, .. $value);
53        $crate::definable_name_map_internal!($map, $($more)+);
54    };
55    // Base case: a single entry
56    ($map:ident, typeof $($name:ident).+ = $value:expr $(,)?) => {
57        $map.insert(
58            $crate::definable_name_map_pattern_internal!($($name).+ typeof).into(),
59            $value.into()
60        );
61    };
62    ($map:ident, $($name:ident).+ = $value:expr $(,)?) => {
63        $map.insert(
64            $crate::definable_name_map_pattern_internal!($($name).+).into(),
65            $value.into()
66        );
67    };
68    // Recursion: split off first entry
69    ($map:ident, typeof $($name:ident).+ = $value:expr, $($more:tt)+) => {
70        $crate::definable_name_map_internal!($map, typeof $($name).+ = $value);
71        $crate::definable_name_map_internal!($map, $($more)+);
72    };
73    ($map:ident, $($name:ident).+ = $value:expr, $($more:tt)+) => {
74        $crate::definable_name_map_internal!($map, $($name).+ = $value);
75        $crate::definable_name_map_internal!($map, $($more)+);
76    };
77
78}
79
80#[macro_export]
81macro_rules! compile_time_defines {
82    ($($more:tt)+) => {
83        {
84            let mut map = $crate::__private::FxIndexMap::default();
85            $crate::definable_name_map_internal!(map, $($more)+);
86            $crate::compile_time_info::CompileTimeDefines(map)
87        }
88    };
89}
90
91#[macro_export]
92macro_rules! free_var_references {
93    ($($more:tt)+) => {
94        {
95            let mut map = $crate::__private::FxIndexMap::default();
96            $crate::definable_name_map_internal!(map, $($more)+);
97            $crate::compile_time_info::FreeVarReferences(map)
98        }
99    };
100}
101
102// TODO: replace with just a `serde_json::Value`
103// https://linear.app/vercel/issue/WEB-1641/compiletimedefinevalue-should-just-use-serde-jsonvalue
104#[turbo_tasks::value(serialization = "auto_for_input")]
105#[derive(Debug, Clone, Hash)]
106pub enum CompileTimeDefineValue {
107    Bool(bool),
108    String(RcStr),
109    JSON(RcStr),
110}
111
112impl From<bool> for CompileTimeDefineValue {
113    fn from(value: bool) -> Self {
114        Self::Bool(value)
115    }
116}
117
118impl From<RcStr> for CompileTimeDefineValue {
119    fn from(value: RcStr) -> Self {
120        Self::String(value)
121    }
122}
123
124impl From<String> for CompileTimeDefineValue {
125    fn from(value: String) -> Self {
126        Self::String(value.into())
127    }
128}
129
130impl From<&str> for CompileTimeDefineValue {
131    fn from(value: &str) -> Self {
132        Self::String(value.into())
133    }
134}
135
136impl From<serde_json::Value> for CompileTimeDefineValue {
137    fn from(value: serde_json::Value) -> Self {
138        Self::JSON(value.to_string().into())
139    }
140}
141
142#[turbo_tasks::value(serialization = "auto_for_input")]
143#[derive(Debug, Clone, Hash)]
144pub enum DefineableNameSegment {
145    Name(RcStr),
146    TypeOf,
147}
148
149impl From<RcStr> for DefineableNameSegment {
150    fn from(value: RcStr) -> Self {
151        DefineableNameSegment::Name(value)
152    }
153}
154
155impl From<&str> for DefineableNameSegment {
156    fn from(value: &str) -> Self {
157        DefineableNameSegment::Name(value.into())
158    }
159}
160
161impl From<String> for DefineableNameSegment {
162    fn from(value: String) -> Self {
163        DefineableNameSegment::Name(value.into())
164    }
165}
166
167#[turbo_tasks::value(transparent)]
168#[derive(Debug, Clone)]
169pub struct CompileTimeDefines(pub FxIndexMap<Vec<DefineableNameSegment>, CompileTimeDefineValue>);
170
171#[turbo_tasks::value(transparent)]
172#[derive(Debug, Clone)]
173pub struct CompileTimeDefinesIndividual(
174    pub FxIndexMap<Vec<DefineableNameSegment>, ResolvedVc<CompileTimeDefineValue>>,
175);
176
177impl IntoIterator for CompileTimeDefines {
178    type Item = (Vec<DefineableNameSegment>, CompileTimeDefineValue);
179    type IntoIter = indexmap::map::IntoIter<Vec<DefineableNameSegment>, CompileTimeDefineValue>;
180
181    fn into_iter(self) -> Self::IntoIter {
182        self.0.into_iter()
183    }
184}
185
186#[turbo_tasks::value_impl]
187impl CompileTimeDefines {
188    #[turbo_tasks::function]
189    pub fn empty() -> Vc<Self> {
190        Vc::cell(FxIndexMap::default())
191    }
192
193    #[turbo_tasks::function]
194    pub fn individual(&self) -> Vc<CompileTimeDefinesIndividual> {
195        Vc::cell(
196            self.0
197                .iter()
198                .map(|(key, value)| (key.clone(), value.clone().resolved_cell()))
199                .collect(),
200        )
201    }
202}
203
204#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, TraceRawVcs, NonLocalValue)]
205pub enum InputRelativeConstant {
206    // The project relative directory name of the source file
207    DirName,
208    // The project relative file name of the source file.
209    FileName,
210}
211
212#[turbo_tasks::value]
213#[derive(Debug, Clone)]
214pub enum FreeVarReference {
215    EcmaScriptModule {
216        request: RcStr,
217        lookup_path: Option<ResolvedVc<FileSystemPath>>,
218        export: Option<RcStr>,
219    },
220    Ident(RcStr),
221    Member(RcStr, RcStr),
222    Value(CompileTimeDefineValue),
223    InputRelative(InputRelativeConstant),
224    Error(RcStr),
225}
226
227impl From<bool> for FreeVarReference {
228    fn from(value: bool) -> Self {
229        Self::Value(value.into())
230    }
231}
232
233impl From<String> for FreeVarReference {
234    fn from(value: String) -> Self {
235        Self::Value(value.into())
236    }
237}
238
239impl From<&str> for FreeVarReference {
240    fn from(value: &str) -> Self {
241        Self::Value(value.into())
242    }
243}
244
245impl From<CompileTimeDefineValue> for FreeVarReference {
246    fn from(value: CompileTimeDefineValue) -> Self {
247        Self::Value(value)
248    }
249}
250
251#[turbo_tasks::value(transparent)]
252#[derive(Debug, Clone)]
253pub struct FreeVarReferences(pub FxIndexMap<Vec<DefineableNameSegment>, FreeVarReference>);
254
255/// A map from the last element (the member prop) to a map of the rest of the name to the value.
256#[turbo_tasks::value(transparent)]
257#[derive(Debug, Clone)]
258pub struct FreeVarReferencesIndividual(
259    pub  FxIndexMap<
260        DefineableNameSegment,
261        FxIndexMap<Vec<DefineableNameSegment>, ResolvedVc<FreeVarReference>>,
262    >,
263);
264
265#[turbo_tasks::value_impl]
266impl FreeVarReferences {
267    #[turbo_tasks::function]
268    pub fn empty() -> Vc<Self> {
269        Vc::cell(FxIndexMap::default())
270    }
271
272    #[turbo_tasks::function]
273    pub fn individual(&self) -> Vc<FreeVarReferencesIndividual> {
274        let mut result: FxIndexMap<
275            DefineableNameSegment,
276            FxIndexMap<Vec<DefineableNameSegment>, ResolvedVc<FreeVarReference>>,
277        > = FxIndexMap::default();
278
279        for (key, value) in &self.0 {
280            let (last_key, key) = key.split_last().unwrap();
281            result
282                .entry(last_key.clone())
283                .or_default()
284                .insert(key.to_vec(), value.clone().resolved_cell());
285        }
286
287        Vc::cell(result)
288    }
289}
290
291#[turbo_tasks::value(shared)]
292#[derive(Debug, Clone)]
293pub struct CompileTimeInfo {
294    pub environment: ResolvedVc<Environment>,
295    pub defines: ResolvedVc<CompileTimeDefines>,
296    pub free_var_references: ResolvedVc<FreeVarReferences>,
297}
298
299impl CompileTimeInfo {
300    pub fn builder(environment: ResolvedVc<Environment>) -> CompileTimeInfoBuilder {
301        CompileTimeInfoBuilder {
302            environment,
303            defines: None,
304            free_var_references: None,
305        }
306    }
307}
308
309#[turbo_tasks::value_impl]
310impl CompileTimeInfo {
311    #[turbo_tasks::function]
312    pub async fn new(environment: ResolvedVc<Environment>) -> Result<Vc<Self>> {
313        Ok(CompileTimeInfo {
314            environment,
315            defines: CompileTimeDefines::empty().to_resolved().await?,
316            free_var_references: FreeVarReferences::empty().to_resolved().await?,
317        }
318        .cell())
319    }
320
321    #[turbo_tasks::function]
322    pub fn environment(&self) -> Vc<Environment> {
323        *self.environment
324    }
325}
326
327pub struct CompileTimeInfoBuilder {
328    environment: ResolvedVc<Environment>,
329    defines: Option<ResolvedVc<CompileTimeDefines>>,
330    free_var_references: Option<ResolvedVc<FreeVarReferences>>,
331}
332
333impl CompileTimeInfoBuilder {
334    pub fn defines(mut self, defines: ResolvedVc<CompileTimeDefines>) -> Self {
335        self.defines = Some(defines);
336        self
337    }
338
339    pub fn free_var_references(
340        mut self,
341        free_var_references: ResolvedVc<FreeVarReferences>,
342    ) -> Self {
343        self.free_var_references = Some(free_var_references);
344        self
345    }
346
347    pub async fn build(self) -> Result<CompileTimeInfo> {
348        Ok(CompileTimeInfo {
349            environment: self.environment,
350            defines: match self.defines {
351                Some(defines) => defines,
352                None => CompileTimeDefines::empty().to_resolved().await?,
353            },
354            free_var_references: match self.free_var_references {
355                Some(free_var_references) => free_var_references,
356                None => FreeVarReferences::empty().to_resolved().await?,
357            },
358        })
359    }
360
361    pub async fn cell(self) -> Result<Vc<CompileTimeInfo>> {
362        Ok(self.build().await?.cell())
363    }
364}
365
366#[cfg(test)]
367mod test {
368    use turbo_tasks::FxIndexMap;
369
370    use crate::compile_time_info::{DefineableNameSegment, FreeVarReference, FreeVarReferences};
371
372    #[test]
373    fn macro_parser() {
374        assert_eq!(
375            free_var_references!(
376                FOO = "bar",
377                FOO = false,
378                Buffer = FreeVarReference::EcmaScriptModule {
379                    request: "node:buffer".into(),
380                    lookup_path: None,
381                    export: Some("Buffer".into()),
382                },
383            ),
384            FreeVarReferences(FxIndexMap::from_iter(vec![
385                (vec!["FOO".into()], FreeVarReference::Value("bar".into())),
386                (vec!["FOO".into()], FreeVarReference::Value(false.into())),
387                (
388                    vec!["Buffer".into()],
389                    FreeVarReference::EcmaScriptModule {
390                        request: "node:buffer".into(),
391                        lookup_path: None,
392                        export: Some("Buffer".into()),
393                    }
394                ),
395            ]))
396        );
397    }
398
399    #[test]
400    fn macro_parser_typeof() {
401        assert_eq!(
402            free_var_references!(
403                typeof x = "a",
404                typeof x.y = "b",
405                typeof x.y.z = "c"
406            ),
407            FreeVarReferences(FxIndexMap::from_iter(vec![
408                (
409                    vec!["x".into(), DefineableNameSegment::TypeOf],
410                    FreeVarReference::Value("a".into())
411                ),
412                (
413                    vec!["x".into(), "y".into(), DefineableNameSegment::TypeOf],
414                    FreeVarReference::Value("b".into())
415                ),
416                (
417                    vec![
418                        "x".into(),
419                        "y".into(),
420                        "z".into(),
421                        DefineableNameSegment::TypeOf
422                    ],
423                    FreeVarReference::Value("c".into())
424                )
425            ]))
426        );
427    }
428}