turbopack_core/
compile_time_info.rs

1use anyhow::Result;
2use serde::{Deserialize, Serialize};
3use turbo_rcstr::RcStr;
4use turbo_tasks::{FxIndexMap, NonLocalValue, ResolvedVc, TaskInput, 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::DefinableNameSegment::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::DefinableNameSegment::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]
105#[derive(Debug, Clone, Hash, TaskInput)]
106pub enum CompileTimeDefineValue {
107    Bool(bool),
108    String(RcStr),
109    JSON(RcStr),
110    Undefined,
111}
112
113impl From<bool> for CompileTimeDefineValue {
114    fn from(value: bool) -> Self {
115        Self::Bool(value)
116    }
117}
118
119impl From<RcStr> for CompileTimeDefineValue {
120    fn from(value: RcStr) -> Self {
121        Self::String(value)
122    }
123}
124
125impl From<String> for CompileTimeDefineValue {
126    fn from(value: String) -> Self {
127        Self::String(value.into())
128    }
129}
130
131impl From<&str> for CompileTimeDefineValue {
132    fn from(value: &str) -> Self {
133        Self::String(value.into())
134    }
135}
136
137impl From<serde_json::Value> for CompileTimeDefineValue {
138    fn from(value: serde_json::Value) -> Self {
139        Self::JSON(value.to_string().into())
140    }
141}
142
143#[turbo_tasks::value]
144#[derive(Debug, Clone, Hash)]
145pub enum DefinableNameSegment {
146    Name(RcStr),
147    TypeOf,
148}
149
150impl From<RcStr> for DefinableNameSegment {
151    fn from(value: RcStr) -> Self {
152        DefinableNameSegment::Name(value)
153    }
154}
155
156impl From<&str> for DefinableNameSegment {
157    fn from(value: &str) -> Self {
158        DefinableNameSegment::Name(value.into())
159    }
160}
161
162impl From<String> for DefinableNameSegment {
163    fn from(value: String) -> Self {
164        DefinableNameSegment::Name(value.into())
165    }
166}
167
168#[turbo_tasks::value(transparent)]
169#[derive(Debug, Clone)]
170pub struct CompileTimeDefines(pub FxIndexMap<Vec<DefinableNameSegment>, CompileTimeDefineValue>);
171
172#[turbo_tasks::value(transparent)]
173#[derive(Debug, Clone)]
174pub struct CompileTimeDefinesIndividual(
175    pub FxIndexMap<Vec<DefinableNameSegment>, ResolvedVc<CompileTimeDefineValue>>,
176);
177
178impl IntoIterator for CompileTimeDefines {
179    type Item = (Vec<DefinableNameSegment>, CompileTimeDefineValue);
180    type IntoIter = indexmap::map::IntoIter<Vec<DefinableNameSegment>, CompileTimeDefineValue>;
181
182    fn into_iter(self) -> Self::IntoIter {
183        self.0.into_iter()
184    }
185}
186
187#[turbo_tasks::value_impl]
188impl CompileTimeDefines {
189    #[turbo_tasks::function]
190    pub fn empty() -> Vc<Self> {
191        Vc::cell(FxIndexMap::default())
192    }
193
194    #[turbo_tasks::function]
195    pub fn individual(&self) -> Vc<CompileTimeDefinesIndividual> {
196        Vc::cell(
197            self.0
198                .iter()
199                .map(|(key, value)| (key.clone(), value.clone().resolved_cell()))
200                .collect(),
201        )
202    }
203}
204
205#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, TraceRawVcs, NonLocalValue)]
206pub enum InputRelativeConstant {
207    // The project relative directory name of the source file
208    DirName,
209    // The project relative file name of the source file.
210    FileName,
211}
212
213#[turbo_tasks::value]
214#[derive(Debug, Clone)]
215pub enum FreeVarReference {
216    EcmaScriptModule {
217        request: RcStr,
218        lookup_path: Option<FileSystemPath>,
219        export: Option<RcStr>,
220    },
221    Ident(RcStr),
222    Member(RcStr, RcStr),
223    Value(CompileTimeDefineValue),
224    InputRelative(InputRelativeConstant),
225    Error(RcStr),
226}
227
228impl From<bool> for FreeVarReference {
229    fn from(value: bool) -> Self {
230        Self::Value(value.into())
231    }
232}
233
234impl From<String> for FreeVarReference {
235    fn from(value: String) -> Self {
236        Self::Value(value.into())
237    }
238}
239
240impl From<&str> for FreeVarReference {
241    fn from(value: &str) -> Self {
242        Self::Value(value.into())
243    }
244}
245
246impl From<CompileTimeDefineValue> for FreeVarReference {
247    fn from(value: CompileTimeDefineValue) -> Self {
248        Self::Value(value)
249    }
250}
251
252#[turbo_tasks::value(transparent)]
253#[derive(Debug, Clone)]
254pub struct FreeVarReferences(pub FxIndexMap<Vec<DefinableNameSegment>, FreeVarReference>);
255
256/// A map from the last element (the member prop) to a map of the rest of the name to the value.
257#[turbo_tasks::value(transparent)]
258#[derive(Debug, Clone)]
259pub struct FreeVarReferencesIndividual(
260    pub  FxIndexMap<
261        DefinableNameSegment,
262        FxIndexMap<Vec<DefinableNameSegment>, ResolvedVc<FreeVarReference>>,
263    >,
264);
265
266#[turbo_tasks::value_impl]
267impl FreeVarReferences {
268    #[turbo_tasks::function]
269    pub fn empty() -> Vc<Self> {
270        Vc::cell(FxIndexMap::default())
271    }
272
273    #[turbo_tasks::function]
274    pub fn individual(&self) -> Vc<FreeVarReferencesIndividual> {
275        let mut result: FxIndexMap<
276            DefinableNameSegment,
277            FxIndexMap<Vec<DefinableNameSegment>, ResolvedVc<FreeVarReference>>,
278        > = FxIndexMap::default();
279
280        for (key, value) in &self.0 {
281            let (last_key, key) = key.split_last().unwrap();
282            result
283                .entry(last_key.clone())
284                .or_default()
285                .insert(key.to_vec(), value.clone().resolved_cell());
286        }
287
288        Vc::cell(result)
289    }
290}
291
292#[turbo_tasks::value(shared)]
293#[derive(Debug, Clone)]
294pub struct CompileTimeInfo {
295    pub environment: ResolvedVc<Environment>,
296    pub defines: ResolvedVc<CompileTimeDefines>,
297    pub free_var_references: ResolvedVc<FreeVarReferences>,
298}
299
300impl CompileTimeInfo {
301    pub fn builder(environment: ResolvedVc<Environment>) -> CompileTimeInfoBuilder {
302        CompileTimeInfoBuilder {
303            environment,
304            defines: None,
305            free_var_references: None,
306        }
307    }
308}
309
310#[turbo_tasks::value_impl]
311impl CompileTimeInfo {
312    #[turbo_tasks::function]
313    pub async fn new(environment: ResolvedVc<Environment>) -> Result<Vc<Self>> {
314        Ok(CompileTimeInfo {
315            environment,
316            defines: CompileTimeDefines::empty().to_resolved().await?,
317            free_var_references: FreeVarReferences::empty().to_resolved().await?,
318        }
319        .cell())
320    }
321
322    #[turbo_tasks::function]
323    pub fn environment(&self) -> Vc<Environment> {
324        *self.environment
325    }
326}
327
328pub struct CompileTimeInfoBuilder {
329    environment: ResolvedVc<Environment>,
330    defines: Option<ResolvedVc<CompileTimeDefines>>,
331    free_var_references: Option<ResolvedVc<FreeVarReferences>>,
332}
333
334impl CompileTimeInfoBuilder {
335    pub fn defines(mut self, defines: ResolvedVc<CompileTimeDefines>) -> Self {
336        self.defines = Some(defines);
337        self
338    }
339
340    pub fn free_var_references(
341        mut self,
342        free_var_references: ResolvedVc<FreeVarReferences>,
343    ) -> Self {
344        self.free_var_references = Some(free_var_references);
345        self
346    }
347
348    pub async fn build(self) -> Result<CompileTimeInfo> {
349        Ok(CompileTimeInfo {
350            environment: self.environment,
351            defines: match self.defines {
352                Some(defines) => defines,
353                None => CompileTimeDefines::empty().to_resolved().await?,
354            },
355            free_var_references: match self.free_var_references {
356                Some(free_var_references) => free_var_references,
357                None => FreeVarReferences::empty().to_resolved().await?,
358            },
359        })
360    }
361
362    pub async fn cell(self) -> Result<Vc<CompileTimeInfo>> {
363        Ok(self.build().await?.cell())
364    }
365}
366
367#[cfg(test)]
368mod test {
369    use turbo_tasks::FxIndexMap;
370
371    use crate::compile_time_info::{DefinableNameSegment, FreeVarReference, FreeVarReferences};
372
373    #[test]
374    fn macro_parser() {
375        assert_eq!(
376            free_var_references!(
377                FOO = "bar",
378                FOO = false,
379                Buffer = FreeVarReference::EcmaScriptModule {
380                    request: "node:buffer".into(),
381                    lookup_path: None,
382                    export: Some("Buffer".into()),
383                },
384            ),
385            FreeVarReferences(FxIndexMap::from_iter(vec![
386                (vec!["FOO".into()], FreeVarReference::Value("bar".into())),
387                (vec!["FOO".into()], FreeVarReference::Value(false.into())),
388                (
389                    vec!["Buffer".into()],
390                    FreeVarReference::EcmaScriptModule {
391                        request: "node:buffer".into(),
392                        lookup_path: None,
393                        export: Some("Buffer".into()),
394                    }
395                ),
396            ]))
397        );
398    }
399
400    #[test]
401    fn macro_parser_typeof() {
402        assert_eq!(
403            free_var_references!(
404                typeof x = "a",
405                typeof x.y = "b",
406                typeof x.y.z = "c"
407            ),
408            FreeVarReferences(FxIndexMap::from_iter(vec![
409                (
410                    vec!["x".into(), DefinableNameSegment::TypeOf],
411                    FreeVarReference::Value("a".into())
412                ),
413                (
414                    vec!["x".into(), "y".into(), DefinableNameSegment::TypeOf],
415                    FreeVarReference::Value("b".into())
416                ),
417                (
418                    vec![
419                        "x".into(),
420                        "y".into(),
421                        "z".into(),
422                        DefinableNameSegment::TypeOf
423                    ],
424                    FreeVarReference::Value("c".into())
425                )
426            ]))
427        );
428    }
429}