turbopack_ecmascript/analyzer/well_known/
require_context.rs1use std::{
2 hash::{Hash, Hasher},
3 sync::LazyLock,
4};
5
6use anyhow::{Result, bail};
7use turbo_esregex::EsRegex;
8use turbo_rcstr::RcStr;
9use turbo_tasks::{FxIndexMap, Vc};
10
11use crate::{
12 analyzer::{ConstantValue, JsValue},
13 references::require_context::RequireContextMap,
14};
15
16#[derive(Debug, Clone)]
17pub struct RequireContextOptions {
18 pub dir: RcStr,
19 pub include_subdirs: bool,
20 pub filter: EsRegex,
22}
23
24pub fn parse_require_context(args: &[JsValue<'_>]) -> Result<RequireContextOptions> {
27 if !(1..=3).contains(&args.len()) {
28 bail!("require.context() only supports 1-3 arguments (mode is not supported)");
30 }
31
32 let Some(dir) = args[0].as_str().map(|s| s.into()) else {
33 bail!("require.context(dir, ...) requires dir to be a constant string");
34 };
35
36 let include_subdirs = if let Some(include_subdirs) = args.get(1) {
37 if let Some(include_subdirs) = include_subdirs.as_bool() {
38 include_subdirs
39 } else {
40 bail!(
41 "require.context(..., includeSubdirs, ...) requires includeSubdirs to be a \
42 constant boolean",
43 );
44 }
45 } else {
46 true
47 };
48
49 let filter = if let Some(filter) = args.get(2) {
50 if let JsValue::Constant(ConstantValue::Regex(box (pattern, flags))) = filter {
51 EsRegex::new(pattern, flags)?
52 } else {
53 bail!("require.context(..., ..., filter) requires filter to be a regex");
54 }
55 } else {
56 static DEFAULT_REGEX: LazyLock<EsRegex> =
59 LazyLock::new(|| EsRegex::new(r"^\./.*$", "").unwrap());
60
61 DEFAULT_REGEX.clone()
62 };
63
64 Ok(RequireContextOptions {
65 dir,
66 include_subdirs,
67 filter,
68 })
69}
70
71#[derive(Debug, Clone, Eq, PartialEq)]
72pub struct RequireContextValue(pub(crate) FxIndexMap<RcStr, RcStr>);
73
74impl RequireContextValue {
75 pub async fn from_context_map(map: Vc<RequireContextMap>) -> Result<Self> {
76 let mut context_map = FxIndexMap::default();
77
78 for (key, entry) in map.await?.iter() {
79 context_map.insert(key.clone(), entry.origin_relative.clone());
80 }
81
82 Ok(RequireContextValue(context_map))
83 }
84}
85
86impl Hash for RequireContextValue {
87 fn hash<H: Hasher>(&self, state: &mut H) {
88 self.0.len().hash(state);
89 for (i, (k, v)) in self.0.iter().enumerate() {
90 i.hash(state);
91 k.hash(state);
92 v.hash(state);
93 }
94 }
95}