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 ($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::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#[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]
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 DirName,
209 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#[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}