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