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 ($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 ($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#[macro_export]
41macro_rules! definable_name_map_internal {
42 ($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 ($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 ($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#[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 DirName,
208 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#[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}