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;
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}
436
437impl CompileTimeInfo {
438 pub fn builder(environment: ResolvedVc<Environment>) -> CompileTimeInfoBuilder {
439 CompileTimeInfoBuilder {
440 environment,
441 defines: None,
442 free_var_references: None,
443 hot_module_replacement_enabled: false,
444 }
445 }
446}
447
448#[turbo_tasks::value_impl]
449impl CompileTimeInfo {
450 #[turbo_tasks::function]
451 pub async fn new(environment: ResolvedVc<Environment>) -> Result<Vc<Self>> {
452 Ok(CompileTimeInfo {
453 environment,
454 defines: CompileTimeDefines::empty().to_resolved().await?,
455 free_var_references: FreeVarReferences::empty().to_resolved().await?,
456 hot_module_replacement_enabled: false,
457 }
458 .cell())
459 }
460
461 #[turbo_tasks::function]
462 pub fn environment(&self) -> Vc<Environment> {
463 *self.environment
464 }
465}
466
467pub struct CompileTimeInfoBuilder {
468 environment: ResolvedVc<Environment>,
469 defines: Option<ResolvedVc<CompileTimeDefines>>,
470 free_var_references: Option<ResolvedVc<FreeVarReferences>>,
471 hot_module_replacement_enabled: bool,
472}
473
474impl CompileTimeInfoBuilder {
475 pub fn defines(mut self, defines: ResolvedVc<CompileTimeDefines>) -> Self {
476 self.defines = Some(defines);
477 self
478 }
479
480 pub fn free_var_references(
481 mut self,
482 free_var_references: ResolvedVc<FreeVarReferences>,
483 ) -> Self {
484 self.free_var_references = Some(free_var_references);
485 self
486 }
487
488 pub fn hot_module_replacement_enabled(mut self, enabled: bool) -> Self {
489 self.hot_module_replacement_enabled = enabled;
490 self
491 }
492
493 pub async fn build(self) -> Result<CompileTimeInfo> {
494 Ok(CompileTimeInfo {
495 environment: self.environment,
496 defines: match self.defines {
497 Some(defines) => defines,
498 None => CompileTimeDefines::empty().to_resolved().await?,
499 },
500 free_var_references: match self.free_var_references {
501 Some(free_var_references) => free_var_references,
502 None => FreeVarReferences::empty().to_resolved().await?,
503 },
504 hot_module_replacement_enabled: self.hot_module_replacement_enabled,
505 })
506 }
507
508 pub async fn cell(self) -> Result<Vc<CompileTimeInfo>> {
509 Ok(self.build().await?.cell())
510 }
511}
512
513#[cfg(test)]
514mod test {
515 use std::{
516 collections::hash_map::DefaultHasher,
517 hash::{Hash, Hasher},
518 };
519
520 use smallvec::smallvec;
521 use turbo_rcstr::rcstr;
522 use turbo_tasks::FxIndexMap;
523
524 use crate::compile_time_info::{
525 DefinableNameSegment, DefinableNameSegmentRef, DefinableNameSegmentRefs, FreeVarReference,
526 FreeVarReferences,
527 };
528
529 fn hash_value<T: Hash>(value: &T) -> u64 {
530 let mut hasher = DefaultHasher::new();
531 value.hash(&mut hasher);
532 hasher.finish()
533 }
534
535 #[test]
536 fn hash_segment_name_matches() {
537 let segment = DefinableNameSegment::Name(rcstr!("process"));
538 let segment_ref = DefinableNameSegmentRef::Name("process");
539 assert_eq!(
540 hash_value(&segment),
541 hash_value(&segment_ref),
542 "DefinableNameSegment::Name and DefinableNameSegmentRef::Name must have matching Hash"
543 );
544 }
545
546 #[test]
547 fn hash_segment_call_matches() {
548 let segment = DefinableNameSegment::Call(rcstr!("foo"));
549 let segment_ref = DefinableNameSegmentRef::Call("foo");
550 assert_eq!(
551 hash_value(&segment),
552 hash_value(&segment_ref),
553 "DefinableNameSegment::Call and DefinableNameSegmentRef::Call must have matching Hash"
554 );
555 }
556
557 #[test]
558 fn hash_segment_typeof_matches() {
559 let segment = DefinableNameSegment::TypeOf;
560 let segment_ref = DefinableNameSegmentRef::TypeOf;
561 assert_eq!(
562 hash_value(&segment),
563 hash_value(&segment_ref),
564 "DefinableNameSegment::TypeOf and DefinableNameSegmentRef::TypeOf must have matching \
565 Hash"
566 );
567 }
568
569 #[test]
570 fn hash_segments_vec_matches() {
571 let segments: Vec<DefinableNameSegment> = vec![
572 DefinableNameSegment::Name(rcstr!("process")),
573 DefinableNameSegment::Name(rcstr!("env")),
574 DefinableNameSegment::Name(rcstr!("NODE_ENV")),
575 ];
576 let segments_ref = DefinableNameSegmentRefs(smallvec![
577 DefinableNameSegmentRef::Name("process"),
578 DefinableNameSegmentRef::Name("env"),
579 DefinableNameSegmentRef::Name("NODE_ENV"),
580 ]);
581 assert_eq!(
582 hash_value(&segments),
583 hash_value(&segments_ref),
584 "Vec<DefinableNameSegment> and DefinableNameSegmentRefs must have matching Hash"
585 );
586 }
587
588 #[test]
589 fn hash_segments_with_typeof_matches() {
590 let segments: Vec<DefinableNameSegment> = vec![
591 DefinableNameSegment::Name(rcstr!("process")),
592 DefinableNameSegment::TypeOf,
593 ];
594 let segments_ref = DefinableNameSegmentRefs(smallvec![
595 DefinableNameSegmentRef::Name("process"),
596 DefinableNameSegmentRef::TypeOf,
597 ]);
598 assert_eq!(
599 hash_value(&segments),
600 hash_value(&segments_ref),
601 "Vec<DefinableNameSegment> with TypeOf and DefinableNameSegmentRefs must have \
602 matching Hash"
603 );
604 }
605
606 #[test]
607 fn hash_segments_with_call_matches() {
608 let segments: Vec<DefinableNameSegment> = vec![
609 DefinableNameSegment::Name(rcstr!("foo")),
610 DefinableNameSegment::Call(rcstr!("bar")),
611 ];
612 let segments_ref = DefinableNameSegmentRefs(smallvec![
613 DefinableNameSegmentRef::Name("foo"),
614 DefinableNameSegmentRef::Call("bar"),
615 ]);
616 assert_eq!(
617 hash_value(&segments),
618 hash_value(&segments_ref),
619 "Vec<DefinableNameSegment> with Call and DefinableNameSegmentRefs must have matching \
620 Hash"
621 );
622 }
623
624 #[test]
625 fn macro_parser() {
626 assert_eq!(
627 free_var_references!(
628 FOO = "bar",
629 FOO = false,
630 Buffer = FreeVarReference::EcmaScriptModule {
631 request: rcstr!("node:buffer"),
632 lookup_path: None,
633 export: Some(rcstr!("Buffer")),
634 },
635 ),
636 FreeVarReferences(FxIndexMap::from_iter(vec![
637 (
638 vec![rcstr!("FOO").into()],
639 FreeVarReference::Value(rcstr!("bar").into())
640 ),
641 (
642 vec![rcstr!("FOO").into()],
643 FreeVarReference::Value(false.into())
644 ),
645 (
646 vec![rcstr!("Buffer").into()],
647 FreeVarReference::EcmaScriptModule {
648 request: rcstr!("node:buffer"),
649 lookup_path: None,
650 export: Some(rcstr!("Buffer")),
651 }
652 ),
653 ]))
654 );
655 }
656
657 #[test]
658 fn macro_parser_typeof() {
659 assert_eq!(
660 free_var_references!(
661 typeof x = "a",
662 typeof x.y = "b",
663 typeof x.y.z = "c"
664 ),
665 FreeVarReferences(FxIndexMap::from_iter(vec![
666 (
667 vec![rcstr!("x").into(), DefinableNameSegment::TypeOf],
668 FreeVarReference::Value(rcstr!("a").into())
669 ),
670 (
671 vec![
672 rcstr!("x").into(),
673 rcstr!("y").into(),
674 DefinableNameSegment::TypeOf
675 ],
676 FreeVarReference::Value(rcstr!("b").into())
677 ),
678 (
679 vec![
680 rcstr!("x").into(),
681 rcstr!("y").into(),
682 rcstr!("z").into(),
683 DefinableNameSegment::TypeOf
684 ],
685 FreeVarReference::Value(rcstr!("b").into())
686 ),
687 (
688 vec![
689 rcstr!("x").into(),
690 rcstr!("y").into(),
691 rcstr!("z").into(),
692 DefinableNameSegment::TypeOf
693 ],
694 FreeVarReference::Value(rcstr!("c").into())
695 )
696 ]))
697 );
698 }
699
700 #[test]
701 fn indexmap_lookup_with_equivalent() {
702 let mut map: FxIndexMap<Vec<DefinableNameSegment>, &str> = FxIndexMap::default();
705 map.insert(
706 vec![
707 DefinableNameSegment::Name(rcstr!("process")),
708 DefinableNameSegment::Name(rcstr!("env")),
709 DefinableNameSegment::Name(rcstr!("NODE_ENV")),
710 ],
711 "production",
712 );
713 map.insert(
714 vec![
715 DefinableNameSegment::Name(rcstr!("process")),
716 DefinableNameSegment::Name(rcstr!("turbopack")),
717 ],
718 "true",
719 );
720
721 let key = DefinableNameSegmentRefs(smallvec![
723 DefinableNameSegmentRef::Name("process"),
724 DefinableNameSegmentRef::Name("env"),
725 DefinableNameSegmentRef::Name("NODE_ENV"),
726 ]);
727 assert_eq!(
728 map.get(&key),
729 Some(&"production"),
730 "IndexMap lookup with Equivalent trait should work"
731 );
732
733 let key2 = DefinableNameSegmentRefs(smallvec![
734 DefinableNameSegmentRef::Name("process"),
735 DefinableNameSegmentRef::Name("turbopack"),
736 ]);
737 assert_eq!(
738 map.get(&key2),
739 Some(&"true"),
740 "IndexMap lookup with Equivalent trait should work for shorter keys"
741 );
742
743 let key3 = DefinableNameSegmentRefs(smallvec![
744 DefinableNameSegmentRef::Name("process"),
745 DefinableNameSegmentRef::Name("nonexistent"),
746 ]);
747 assert_eq!(
748 map.get(&key3),
749 None,
750 "IndexMap lookup should return None for nonexistent keys"
751 );
752 }
753
754 #[test]
755 fn fxhashset_rcstr_lookup_with_str() {
756 use rustc_hash::FxHashSet;
759
760 let mut set: FxHashSet<turbo_rcstr::RcStr> = FxHashSet::default();
761 set.insert(rcstr!("process"));
762 set.insert(rcstr!("env"));
763 set.insert(rcstr!("NODE_ENV"));
764
765 assert!(
768 set.contains("process"),
769 "FxHashSet<RcStr> lookup with &str should work for 'process'"
770 );
771 assert!(
772 set.contains("env"),
773 "FxHashSet<RcStr> lookup with &str should work for 'env'"
774 );
775 assert!(
776 set.contains("NODE_ENV"),
777 "FxHashSet<RcStr> lookup with &str should work for 'NODE_ENV'"
778 );
779 assert!(
780 !set.contains("nonexistent"),
781 "FxHashSet<RcStr> lookup with &str should return false for nonexistent keys"
782 );
783 }
784}