1use std::fmt::Display;
2
3use anyhow::Result;
4use bincode::{Decode, Encode};
5use indexmap::Equivalent;
6use num_bigint::BigInt;
7use rustc_hash::FxHashSet;
8use smallvec::{SmallVec, smallvec};
9use turbo_rcstr::{RcStr, rcstr};
10use turbo_tasks::{FxIndexMap, NonLocalValue, ResolvedVc, Vc, trace::TraceRawVcs};
11use turbo_tasks_fs::FileSystemPath;
12
13use crate::{environment::Environment, issue::IssueSeverity};
14
15#[macro_export]
16macro_rules! definable_name_map_pattern_internal {
17 ($name:ident) => {
18 [stringify!($name).into()]
19 };
20 ($name:ident typeof) => {
21 [stringify!($name).into(), $crate::compile_time_info::DefinableNameSegment::TypeOf]
22 };
23 ($name:ident . $($more:ident).+ typeof) => {
25 $crate::definable_name_map_pattern_internal!($($more).+ typeof, [stringify!($name).into()])
26 };
27 ($name:ident . $($more:ident).+) => {
28 $crate::definable_name_map_pattern_internal!($($more).+, [stringify!($name).into()])
29 };
30 ($name:ident, [$($array:expr),+]) => {
32 [$($array),+, stringify!($name).into()]
33 };
34 ($name:ident . $($more:ident).+, [$($array:expr),+]) => {
35 $crate::definable_name_map_pattern_internal!($($more).+, [$($array),+, stringify!($name).into()])
36 };
37 ($name:ident typeof, [$($array:expr),+]) => {
38 [$($array),+, stringify!($name).into(), $crate::compile_time_info::DefinableNameSegment::TypeOf]
39 };
40 ($name:ident . $($more:ident).+ typeof, [$($array:expr),+]) => {
41 $crate::definable_name_map_pattern_internal!($($more).+ typeof, [$($array),+, stringify!($name).into()])
42 };
43}
44
45#[macro_export]
47macro_rules! definable_name_map_internal {
48 ($map:ident, .. $value:expr) => {
50 for (key, value) in $value {
51 $map.insert(
52 key.into(),
53 value.into()
54 );
55 }
56 };
57 ($map:ident, .. $value:expr, $($more:tt)+) => {
58 $crate::definable_name_map_internal!($map, .. $value);
59 $crate::definable_name_map_internal!($map, $($more)+);
60 };
61 ($map:ident, typeof $($name:ident).+ = $value:expr $(,)?) => {
63 $map.insert(
64 $crate::definable_name_map_pattern_internal!($($name).+ typeof).into(),
65 $value.into()
66 );
67 };
68 ($map:ident, $($name:ident).+ = $value:expr $(,)?) => {
69 $map.insert(
70 $crate::definable_name_map_pattern_internal!($($name).+).into(),
71 $value.into()
72 );
73 };
74 ($map:ident, typeof $($name:ident).+ = $value:expr, $($more:tt)+) => {
76 $crate::definable_name_map_internal!($map, typeof $($name).+ = $value);
77 $crate::definable_name_map_internal!($map, $($more)+);
78 };
79 ($map:ident, $($name:ident).+ = $value:expr, $($more:tt)+) => {
80 $crate::definable_name_map_internal!($map, $($name).+ = $value);
81 $crate::definable_name_map_internal!($map, $($more)+);
82 };
83
84}
85
86#[macro_export]
87macro_rules! compile_time_defines {
88 ($($more:tt)+) => {
89 {
90 let mut map = $crate::__private::FxIndexMap::default();
91 $crate::definable_name_map_internal!(map, $($more)+);
92 $crate::compile_time_info::CompileTimeDefines(map)
93 }
94 };
95}
96
97#[macro_export]
98macro_rules! free_var_references {
99 ($($more:tt)+) => {
100 {
101 let mut map = $crate::__private::FxIndexMap::default();
102 $crate::definable_name_map_internal!(map, $($more)+);
103 $crate::compile_time_info::FreeVarReferences(map)
104 }
105 };
106}
107
108#[turbo_tasks::value(shared)]
111#[derive(Debug, Clone, Hash)]
112pub enum CompileTimeDefineValue {
113 Null,
114 Bool(bool),
115 Number(
116 #[bincode(with = "turbo_bincode::serde_self_describing")]
117 #[turbo_tasks(trace_ignore)]
118 serde_json::Number,
119 ),
120 String(RcStr),
121 BigInt(
122 #[bincode(with_serde)]
123 #[turbo_tasks(trace_ignore)]
124 Box<BigInt>,
125 ),
126 Array(Vec<CompileTimeDefineValue>),
127 Object(Vec<(RcStr, CompileTimeDefineValue)>),
128 Undefined,
129 Evaluate(RcStr),
130 Regex(RcStr, RcStr),
131}
132
133impl From<bool> for CompileTimeDefineValue {
134 fn from(value: bool) -> Self {
135 Self::Bool(value)
136 }
137}
138
139impl From<RcStr> for CompileTimeDefineValue {
140 fn from(value: RcStr) -> Self {
141 Self::String(value)
142 }
143}
144
145impl From<String> for CompileTimeDefineValue {
146 fn from(value: String) -> Self {
147 Self::String(value.into())
148 }
149}
150
151impl From<&str> for CompileTimeDefineValue {
152 fn from(value: &str) -> Self {
153 Self::String(value.into())
154 }
155}
156
157impl From<serde_json::Value> for CompileTimeDefineValue {
158 fn from(value: serde_json::Value) -> Self {
159 match value {
160 serde_json::Value::Null => Self::Null,
161 serde_json::Value::Bool(b) => Self::Bool(b),
162 serde_json::Value::Number(n) => Self::Number(n),
163 serde_json::Value::String(s) => Self::String(s.into()),
164 serde_json::Value::Array(a) => Self::Array(a.into_iter().map(|i| i.into()).collect()),
165 serde_json::Value::Object(m) => {
166 Self::Object(m.into_iter().map(|(k, v)| (k.into(), v.into())).collect())
167 }
168 }
169 }
170}
171
172impl CompileTimeDefineValue {
173 pub fn display_ecmascript(&self) -> impl Display + '_ {
174 CompileTimeDefineValueDisplay { value: self }
175 }
176}
177
178struct CompileTimeDefineValueDisplay<'a> {
179 value: &'a CompileTimeDefineValue,
180}
181
182impl Display for CompileTimeDefineValueDisplay<'_> {
183 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
184 match self.value {
185 CompileTimeDefineValue::Null => write!(f, "null"),
186 CompileTimeDefineValue::Bool(false) => write!(f, "false"),
187 CompileTimeDefineValue::Bool(true) => write!(f, "true"),
188 CompileTimeDefineValue::Number(v) => write!(f, "{}", serde_json::to_string(v).unwrap()),
189 CompileTimeDefineValue::String(v) => write!(f, "{}", serde_json::to_string(v).unwrap()),
190 CompileTimeDefineValue::BigInt(b) => write!(f, "{}n", b),
191 CompileTimeDefineValue::Array(a) => {
192 write!(f, "[")?;
193 for (i, v) in a.iter().enumerate() {
194 if i > 0 {
195 write!(f, ", ")?;
196 }
197 write!(f, "{}", v.display_ecmascript())?;
198 }
199 write!(f, "]")?;
200 Ok(())
201 }
202 CompileTimeDefineValue::Object(o) => {
203 write!(f, "{{")?;
204 for (i, (k, v)) in o.iter().enumerate() {
205 if i > 0 {
206 write!(f, ", ")?;
207 }
208 write!(
209 f,
210 "{}: {}",
211 serde_json::to_string(k).unwrap(),
212 v.display_ecmascript()
213 )?;
214 }
215 write!(f, "}}")?;
216 Ok(())
217 }
218 CompileTimeDefineValue::Undefined => write!(f, "undefined"),
219 CompileTimeDefineValue::Evaluate(e) => write!(f, "{}", e),
220 CompileTimeDefineValue::Regex(regex, flags) => write!(f, "/{}/{}", regex, flags),
221 }
222 }
223}
224
225#[turbo_tasks::value]
226#[derive(Debug, Clone, PartialOrd, Ord)]
227pub enum DefinableNameSegment {
228 Name(RcStr),
229 Call(RcStr),
230 TypeOf,
231}
232
233impl std::hash::Hash for DefinableNameSegment {
239 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
240 match self {
241 Self::Name(s) => {
242 0u8.hash(state);
243 s.as_str().hash(state);
244 }
245 Self::Call(s) => {
246 1u8.hash(state);
247 s.as_str().hash(state);
248 }
249 Self::TypeOf => {
250 2u8.hash(state);
251 }
252 }
253 }
254}
255
256impl From<RcStr> for DefinableNameSegment {
257 fn from(value: RcStr) -> Self {
258 DefinableNameSegment::Name(value)
259 }
260}
261
262impl From<&str> for DefinableNameSegment {
263 fn from(value: &str) -> Self {
264 DefinableNameSegment::Name(value.into())
265 }
266}
267
268impl From<String> for DefinableNameSegment {
269 fn from(value: String) -> Self {
270 DefinableNameSegment::Name(value.into())
271 }
272}
273
274#[derive(Copy, Clone, PartialEq, Eq)]
275pub enum DefinableNameSegmentRef<'a> {
276 Name(&'a str),
277 Call(&'a str),
278 TypeOf,
279}
280
281impl std::hash::Hash for DefinableNameSegmentRef<'_> {
285 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
286 match self {
287 Self::Name(s) => {
288 0u8.hash(state);
289 s.hash(state);
290 }
291 Self::Call(s) => {
292 1u8.hash(state);
293 s.hash(state);
294 }
295 Self::TypeOf => {
296 2u8.hash(state);
297 }
298 }
299 }
300}
301
302impl Equivalent<DefinableNameSegment> for DefinableNameSegmentRef<'_> {
303 fn equivalent(&self, key: &DefinableNameSegment) -> bool {
304 match (self, key) {
305 (DefinableNameSegmentRef::Name(a), DefinableNameSegment::Name(b)) => **a == *b.as_str(),
306 (DefinableNameSegmentRef::Call(a), DefinableNameSegment::Call(b)) => **a == *b.as_str(),
307 (DefinableNameSegmentRef::TypeOf, DefinableNameSegment::TypeOf) => true,
308 _ => false,
309 }
310 }
311}
312
313#[derive(Clone, PartialEq, Eq)]
314pub struct DefinableNameSegmentRefs<'a>(pub SmallVec<[DefinableNameSegmentRef<'a>; 4]>);
315
316impl std::hash::Hash for DefinableNameSegmentRefs<'_> {
318 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
319 self.0.len().hash(state);
320 for segment in &self.0 {
321 segment.hash(state);
322 }
323 }
324}
325
326impl Equivalent<Vec<DefinableNameSegment>> for DefinableNameSegmentRefs<'_> {
327 fn equivalent(&self, key: &Vec<DefinableNameSegment>) -> bool {
328 if self.0.len() != key.len() {
329 return false;
330 }
331 for (a, b) in self.0.iter().zip(key.iter()) {
332 if !a.equivalent(b) {
333 return false;
334 }
335 }
336 true
337 }
338}
339
340#[turbo_tasks::value(transparent, cell = "keyed")]
341#[derive(Debug, Clone)]
342pub struct CompileTimeDefines(
343 #[bincode(with = "turbo_bincode::indexmap")]
344 pub FxIndexMap<Vec<DefinableNameSegment>, CompileTimeDefineValue>,
345);
346
347impl IntoIterator for CompileTimeDefines {
348 type Item = (Vec<DefinableNameSegment>, CompileTimeDefineValue);
349 type IntoIter = indexmap::map::IntoIter<Vec<DefinableNameSegment>, CompileTimeDefineValue>;
350
351 fn into_iter(self) -> Self::IntoIter {
352 self.0.into_iter()
353 }
354}
355
356#[turbo_tasks::value_impl]
357impl CompileTimeDefines {
358 #[turbo_tasks::function]
359 pub fn empty() -> Vc<Self> {
360 Vc::cell(FxIndexMap::default())
361 }
362
363 #[turbo_tasks::function]
364 pub async fn read_process_env(&self, key: RcStr) -> Result<Vc<Option<RcStr>>> {
365 let key = DefinableNameSegmentRefs(smallvec![
366 DefinableNameSegmentRef::Name("process"),
367 DefinableNameSegmentRef::Name("env"),
368 DefinableNameSegmentRef::Name(&key),
369 ]);
370 Ok(Vc::cell(match self.0.get(&key) {
371 Some(CompileTimeDefineValue::String(s)) => Some(s.clone()),
372 _ => None,
373 }))
374 }
375}
376
377#[derive(Debug, Clone, Copy, PartialEq, Eq, TraceRawVcs, NonLocalValue, Encode, Decode)]
378pub enum InputRelativeConstant {
379 DirName,
381 FileName,
383}
384
385#[derive(Debug, Clone, TraceRawVcs, NonLocalValue, Encode, Decode, PartialEq, Eq)]
386pub enum FreeVarReference {
387 EcmaScriptModule {
388 request: RcStr,
389 lookup_path: Option<FileSystemPath>,
390 export: Option<RcStr>,
391 },
392 Ident(RcStr),
393 Member(RcStr, RcStr),
394 Value(CompileTimeDefineValue),
395 InputRelative(InputRelativeConstant),
396 ReportUsage {
399 message: RcStr,
400 severity: IssueSeverity,
401 inner: Option<Box<FreeVarReference>>,
402 },
403}
404
405impl From<bool> for FreeVarReference {
406 fn from(value: bool) -> Self {
407 Self::Value(value.into())
408 }
409}
410
411impl From<String> for FreeVarReference {
412 fn from(value: String) -> Self {
413 Self::Value(value.into())
414 }
415}
416impl From<RcStr> for FreeVarReference {
417 fn from(value: RcStr) -> Self {
418 Self::Value(value.into())
419 }
420}
421
422impl From<&str> for FreeVarReference {
423 fn from(value: &str) -> Self {
424 Self::Value(value.into())
425 }
426}
427
428impl From<CompileTimeDefineValue> for FreeVarReference {
429 fn from(value: CompileTimeDefineValue) -> Self {
430 Self::Value(value)
431 }
432}
433
434#[turbo_tasks::value(transparent, cell = "keyed")]
435#[derive(Debug, Clone)]
436pub struct FreeVarReferences(
437 #[bincode(with = "turbo_bincode::indexmap")]
438 pub FxIndexMap<Vec<DefinableNameSegment>, FreeVarReference>,
439);
440
441#[turbo_tasks::value(transparent, cell = "keyed")]
442pub struct FreeVarReferencesMembers(FxHashSet<RcStr>);
443
444#[turbo_tasks::value_impl]
445impl FreeVarReferences {
446 #[turbo_tasks::function]
447 pub fn empty() -> Vc<Self> {
448 Vc::cell(FxIndexMap::default())
449 }
450
451 #[turbo_tasks::function]
452 pub fn members(&self) -> Vc<FreeVarReferencesMembers> {
453 let mut members = FxHashSet::default();
454 for (key, _) in self.0.iter() {
455 if let Some(name) = key
456 .iter()
457 .rfind(|segment| {
458 matches!(
459 segment,
460 DefinableNameSegment::Name(_) | DefinableNameSegment::Call(_)
461 )
462 })
463 .and_then(|segment| match segment {
464 DefinableNameSegment::Name(n) | DefinableNameSegment::Call(n) => Some(n),
465 _ => None,
466 })
467 {
468 members.insert(name.clone());
469 }
470 }
471 Vc::cell(members)
472 }
473}
474
475impl IntoIterator for FreeVarReferences {
476 type Item = (Vec<DefinableNameSegment>, FreeVarReference);
477 type IntoIter = indexmap::map::IntoIter<Vec<DefinableNameSegment>, FreeVarReference>;
478
479 fn into_iter(self) -> Self::IntoIter {
480 self.0.into_iter()
481 }
482}
483
484#[turbo_tasks::value(shared)]
485#[derive(Debug, Clone)]
486pub struct CompileTimeInfo {
487 pub environment: ResolvedVc<Environment>,
488 pub defines: ResolvedVc<CompileTimeDefines>,
489 pub free_var_references: ResolvedVc<FreeVarReferences>,
490 pub hot_module_replacement_enabled: bool,
491 pub import_meta_env_base_url: RcStr,
492}
493
494impl CompileTimeInfo {
495 pub fn builder(environment: ResolvedVc<Environment>) -> CompileTimeInfoBuilder {
496 CompileTimeInfoBuilder {
497 environment,
498 defines: None,
499 free_var_references: None,
500 hot_module_replacement_enabled: false,
501 import_meta_env_base_url: rcstr!("/"),
502 }
503 }
504}
505
506#[turbo_tasks::value_impl]
507impl CompileTimeInfo {
508 #[turbo_tasks::function]
509 pub async fn new(environment: ResolvedVc<Environment>) -> Result<Vc<Self>> {
510 Ok(CompileTimeInfo {
511 environment,
512 defines: CompileTimeDefines::empty().to_resolved().await?,
513 free_var_references: FreeVarReferences::empty().to_resolved().await?,
514 hot_module_replacement_enabled: false,
515 import_meta_env_base_url: rcstr!("/"),
516 }
517 .cell())
518 }
519
520 #[turbo_tasks::function]
521 pub fn environment(&self) -> Vc<Environment> {
522 *self.environment
523 }
524}
525
526pub struct CompileTimeInfoBuilder {
527 environment: ResolvedVc<Environment>,
528 defines: Option<ResolvedVc<CompileTimeDefines>>,
529 free_var_references: Option<ResolvedVc<FreeVarReferences>>,
530 hot_module_replacement_enabled: bool,
531 import_meta_env_base_url: RcStr,
532}
533
534impl CompileTimeInfoBuilder {
535 pub fn defines(mut self, defines: ResolvedVc<CompileTimeDefines>) -> Self {
536 self.defines = Some(defines);
537 self
538 }
539
540 pub fn free_var_references(
541 mut self,
542 free_var_references: ResolvedVc<FreeVarReferences>,
543 ) -> Self {
544 self.free_var_references = Some(free_var_references);
545 self
546 }
547
548 pub fn hot_module_replacement_enabled(mut self, enabled: bool) -> Self {
549 self.hot_module_replacement_enabled = enabled;
550 self
551 }
552
553 pub fn import_meta_env_base_url(mut self, base_url: RcStr) -> Self {
554 self.import_meta_env_base_url = base_url;
555 self
556 }
557
558 pub async fn build(self) -> Result<CompileTimeInfo> {
559 Ok(CompileTimeInfo {
560 environment: self.environment,
561 defines: match self.defines {
562 Some(defines) => defines,
563 None => CompileTimeDefines::empty().to_resolved().await?,
564 },
565 free_var_references: match self.free_var_references {
566 Some(free_var_references) => free_var_references,
567 None => FreeVarReferences::empty().to_resolved().await?,
568 },
569 hot_module_replacement_enabled: self.hot_module_replacement_enabled,
570 import_meta_env_base_url: self.import_meta_env_base_url,
571 })
572 }
573
574 pub async fn cell(self) -> Result<Vc<CompileTimeInfo>> {
575 Ok(self.build().await?.cell())
576 }
577}
578
579#[cfg(test)]
580mod test {
581 use std::{
582 collections::hash_map::DefaultHasher,
583 hash::{Hash, Hasher},
584 };
585
586 use smallvec::smallvec;
587 use turbo_rcstr::rcstr;
588 use turbo_tasks::FxIndexMap;
589
590 use crate::compile_time_info::{
591 CompileTimeDefineValue, DefinableNameSegment, DefinableNameSegmentRef,
592 DefinableNameSegmentRefs, FreeVarReference, FreeVarReferences,
593 };
594
595 fn hash_value<T: Hash>(value: &T) -> u64 {
596 let mut hasher = DefaultHasher::new();
597 value.hash(&mut hasher);
598 hasher.finish()
599 }
600
601 #[test]
602 fn hash_segment_name_matches() {
603 let segment = DefinableNameSegment::Name(rcstr!("process"));
604 let segment_ref = DefinableNameSegmentRef::Name("process");
605 assert_eq!(
606 hash_value(&segment),
607 hash_value(&segment_ref),
608 "DefinableNameSegment::Name and DefinableNameSegmentRef::Name must have matching Hash"
609 );
610 }
611
612 #[test]
613 fn hash_segment_call_matches() {
614 let segment = DefinableNameSegment::Call(rcstr!("foo"));
615 let segment_ref = DefinableNameSegmentRef::Call("foo");
616 assert_eq!(
617 hash_value(&segment),
618 hash_value(&segment_ref),
619 "DefinableNameSegment::Call and DefinableNameSegmentRef::Call must have matching Hash"
620 );
621 }
622
623 #[test]
624 fn hash_segment_typeof_matches() {
625 let segment = DefinableNameSegment::TypeOf;
626 let segment_ref = DefinableNameSegmentRef::TypeOf;
627 assert_eq!(
628 hash_value(&segment),
629 hash_value(&segment_ref),
630 "DefinableNameSegment::TypeOf and DefinableNameSegmentRef::TypeOf must have matching \
631 Hash"
632 );
633 }
634
635 #[test]
636 fn hash_segments_vec_matches() {
637 let segments: Vec<DefinableNameSegment> = vec![
638 DefinableNameSegment::Name(rcstr!("process")),
639 DefinableNameSegment::Name(rcstr!("env")),
640 DefinableNameSegment::Name(rcstr!("NODE_ENV")),
641 ];
642 let segments_ref = DefinableNameSegmentRefs(smallvec![
643 DefinableNameSegmentRef::Name("process"),
644 DefinableNameSegmentRef::Name("env"),
645 DefinableNameSegmentRef::Name("NODE_ENV"),
646 ]);
647 assert_eq!(
648 hash_value(&segments),
649 hash_value(&segments_ref),
650 "Vec<DefinableNameSegment> and DefinableNameSegmentRefs must have matching Hash"
651 );
652 }
653
654 #[test]
655 fn hash_segments_with_typeof_matches() {
656 let segments: Vec<DefinableNameSegment> = vec![
657 DefinableNameSegment::Name(rcstr!("process")),
658 DefinableNameSegment::TypeOf,
659 ];
660 let segments_ref = DefinableNameSegmentRefs(smallvec![
661 DefinableNameSegmentRef::Name("process"),
662 DefinableNameSegmentRef::TypeOf,
663 ]);
664 assert_eq!(
665 hash_value(&segments),
666 hash_value(&segments_ref),
667 "Vec<DefinableNameSegment> with TypeOf and DefinableNameSegmentRefs must have \
668 matching Hash"
669 );
670 }
671
672 #[test]
673 fn hash_segments_with_call_matches() {
674 let segments: Vec<DefinableNameSegment> = vec![
675 DefinableNameSegment::Name(rcstr!("foo")),
676 DefinableNameSegment::Call(rcstr!("bar")),
677 ];
678 let segments_ref = DefinableNameSegmentRefs(smallvec![
679 DefinableNameSegmentRef::Name("foo"),
680 DefinableNameSegmentRef::Call("bar"),
681 ]);
682 assert_eq!(
683 hash_value(&segments),
684 hash_value(&segments_ref),
685 "Vec<DefinableNameSegment> with Call and DefinableNameSegmentRefs must have matching \
686 Hash"
687 );
688 }
689
690 #[test]
691 fn macro_parser() {
692 assert_eq!(
693 free_var_references!(
694 FOO = "bar",
695 FOO = false,
696 Buffer = FreeVarReference::EcmaScriptModule {
697 request: rcstr!("node:buffer"),
698 lookup_path: None,
699 export: Some(rcstr!("Buffer")),
700 },
701 ),
702 FreeVarReferences(FxIndexMap::from_iter(vec![
703 (
704 vec![rcstr!("FOO").into()],
705 FreeVarReference::Value(rcstr!("bar").into())
706 ),
707 (
708 vec![rcstr!("FOO").into()],
709 FreeVarReference::Value(false.into())
710 ),
711 (
712 vec![rcstr!("Buffer").into()],
713 FreeVarReference::EcmaScriptModule {
714 request: rcstr!("node:buffer"),
715 lookup_path: None,
716 export: Some(rcstr!("Buffer")),
717 }
718 ),
719 ]))
720 );
721 }
722
723 #[test]
724 fn macro_parser_typeof() {
725 assert_eq!(
726 free_var_references!(
727 typeof x = "a",
728 typeof x.y = "b",
729 typeof x.y.z = "c"
730 ),
731 FreeVarReferences(FxIndexMap::from_iter(vec![
732 (
733 vec![rcstr!("x").into(), DefinableNameSegment::TypeOf],
734 FreeVarReference::Value(rcstr!("a").into())
735 ),
736 (
737 vec![
738 rcstr!("x").into(),
739 rcstr!("y").into(),
740 DefinableNameSegment::TypeOf
741 ],
742 FreeVarReference::Value(rcstr!("b").into())
743 ),
744 (
745 vec![
746 rcstr!("x").into(),
747 rcstr!("y").into(),
748 rcstr!("z").into(),
749 DefinableNameSegment::TypeOf
750 ],
751 FreeVarReference::Value(rcstr!("b").into())
752 ),
753 (
754 vec![
755 rcstr!("x").into(),
756 rcstr!("y").into(),
757 rcstr!("z").into(),
758 DefinableNameSegment::TypeOf
759 ],
760 FreeVarReference::Value(rcstr!("c").into())
761 )
762 ]))
763 );
764 }
765
766 #[test]
767 fn indexmap_lookup_with_equivalent() {
768 let mut map: FxIndexMap<Vec<DefinableNameSegment>, &str> = FxIndexMap::default();
771 map.insert(
772 vec![
773 DefinableNameSegment::Name(rcstr!("process")),
774 DefinableNameSegment::Name(rcstr!("env")),
775 DefinableNameSegment::Name(rcstr!("NODE_ENV")),
776 ],
777 "production",
778 );
779 map.insert(
780 vec![
781 DefinableNameSegment::Name(rcstr!("process")),
782 DefinableNameSegment::Name(rcstr!("turbopack")),
783 ],
784 "true",
785 );
786
787 let key = DefinableNameSegmentRefs(smallvec![
789 DefinableNameSegmentRef::Name("process"),
790 DefinableNameSegmentRef::Name("env"),
791 DefinableNameSegmentRef::Name("NODE_ENV"),
792 ]);
793 assert_eq!(
794 map.get(&key),
795 Some(&"production"),
796 "IndexMap lookup with Equivalent trait should work"
797 );
798
799 let key2 = DefinableNameSegmentRefs(smallvec![
800 DefinableNameSegmentRef::Name("process"),
801 DefinableNameSegmentRef::Name("turbopack"),
802 ]);
803 assert_eq!(
804 map.get(&key2),
805 Some(&"true"),
806 "IndexMap lookup with Equivalent trait should work for shorter keys"
807 );
808
809 let key3 = DefinableNameSegmentRefs(smallvec![
810 DefinableNameSegmentRef::Name("process"),
811 DefinableNameSegmentRef::Name("nonexistent"),
812 ]);
813 assert_eq!(
814 map.get(&key3),
815 None,
816 "IndexMap lookup should return None for nonexistent keys"
817 );
818 }
819
820 #[test]
821 fn fxhashset_rcstr_lookup_with_str() {
822 use rustc_hash::FxHashSet;
825
826 let mut set: FxHashSet<turbo_rcstr::RcStr> = FxHashSet::default();
827 set.insert(rcstr!("process"));
828 set.insert(rcstr!("env"));
829 set.insert(rcstr!("NODE_ENV"));
830
831 assert!(
834 set.contains("process"),
835 "FxHashSet<RcStr> lookup with &str should work for 'process'"
836 );
837 assert!(
838 set.contains("env"),
839 "FxHashSet<RcStr> lookup with &str should work for 'env'"
840 );
841 assert!(
842 set.contains("NODE_ENV"),
843 "FxHashSet<RcStr> lookup with &str should work for 'NODE_ENV'"
844 );
845 assert!(
846 !set.contains("nonexistent"),
847 "FxHashSet<RcStr> lookup with &str should return false for nonexistent keys"
848 );
849 }
850
851 #[test]
852 fn compile_time_define_value_display_ecmascript() {
853 fn t(v: CompileTimeDefineValue) -> String {
854 v.display_ecmascript().to_string()
855 }
856
857 use serde_json::Number;
858 assert_eq!(t(CompileTimeDefineValue::Null), "null");
859 assert_eq!(t(CompileTimeDefineValue::Bool(false)), "false");
860 assert_eq!(t(CompileTimeDefineValue::Bool(true)), "true");
861 assert_eq!(
862 t(CompileTimeDefineValue::Number(
863 Number::from_f64(42.1).unwrap()
864 )),
865 "42.1"
866 );
867 assert_eq!(
868 t(CompileTimeDefineValue::String(rcstr!(r#"a"b"#))),
869 r#""a\"b""#
870 );
871 assert_eq!(
872 t(CompileTimeDefineValue::String(rcstr!(r#"a'b"#))),
873 r#""a'b""#
874 );
875 assert_eq!(
876 t(CompileTimeDefineValue::BigInt(Box::new(42.into()))),
877 "42n"
878 );
879 assert_eq!(t(CompileTimeDefineValue::Undefined), "undefined");
880 assert_eq!(
881 t(CompileTimeDefineValue::Evaluate(rcstr!("someExpression"))),
882 "someExpression"
883 );
884 assert_eq!(
885 t(CompileTimeDefineValue::Regex(
886 rcstr!("pattern"),
887 rcstr!("flags")
888 )),
889 "/pattern/flags"
890 );
891
892 assert_eq!(
893 t(CompileTimeDefineValue::Array(vec![
894 CompileTimeDefineValue::Bool(true),
895 CompileTimeDefineValue::Number(Number::from(42)),
896 CompileTimeDefineValue::String(rcstr!(r#"a"b"#)),
897 ])),
898 r#"[true, 42, "a\"b"]"#
899 );
900 assert_eq!(
901 t(CompileTimeDefineValue::Object(vec![
902 (rcstr!("foo"), CompileTimeDefineValue::Bool(true)),
903 (
904 rcstr!("bar"),
905 CompileTimeDefineValue::Number(Number::from(42))
906 ),
907 (
908 rcstr!(r#"a"b"#),
909 CompileTimeDefineValue::String(rcstr!(r#"c"d"#),)
910 ),
911 ])),
912 r#"{"foo": true, "bar": 42, "a\"b": "c\"d"}"#
913 );
914 }
915}