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