1use std::{collections::BTreeMap, fmt::Display, ops::Deref};
2
3use anyhow::{Result, bail};
4use rustc_hash::FxHashMap;
5use serde::{Deserialize, Serialize};
6use serde_json::Value;
7use turbo_rcstr::RcStr;
8use turbo_tasks::FxIndexMap;
9
10use super::{
11 alias_map::{AliasMap, AliasMapIter, AliasPattern, AliasTemplate},
12 options::ConditionValue,
13 pattern::Pattern,
14};
15
16#[derive(Copy, Clone)]
18enum ExportImport {
19 Export,
20 Import,
21}
22
23impl Display for ExportImport {
24 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
25 match self {
26 Self::Export => f.write_str("export"),
27 Self::Import => f.write_str("import"),
28 }
29 }
30}
31
32#[derive(Clone, PartialEq, Eq, Hash, Debug, Serialize, Deserialize)]
36pub enum SubpathValue {
37 Alternatives(Vec<SubpathValue>),
42
43 Conditional(Vec<(RcStr, SubpathValue)>),
50
51 Result(RcStr),
54
55 Excluded,
58}
59
60#[derive(Clone, PartialEq, Eq, Hash, Debug, Serialize, Deserialize)]
63pub enum ReplacedSubpathValue {
64 Alternatives(Vec<ReplacedSubpathValue>),
65 Conditional(Vec<(RcStr, ReplacedSubpathValue)>),
66 Result(Pattern),
67 Excluded,
68}
69
70impl AliasTemplate for SubpathValue {
71 type Output<'a>
72 = Result<ReplacedSubpathValue>
73 where
74 Self: 'a;
75
76 fn convert(&self) -> Result<ReplacedSubpathValue> {
77 Ok(match self {
78 SubpathValue::Alternatives(list) => ReplacedSubpathValue::Alternatives(
79 list.iter()
80 .map(|value: &SubpathValue| value.convert())
81 .collect::<Result<Vec<_>>>()?,
82 ),
83 SubpathValue::Conditional(list) => ReplacedSubpathValue::Conditional(
84 list.iter()
85 .map(|(condition, value)| Ok((condition.clone(), value.convert()?)))
86 .collect::<Result<Vec<_>>>()?,
87 ),
88 SubpathValue::Result(value) => ReplacedSubpathValue::Result(value.clone().into()),
89 SubpathValue::Excluded => ReplacedSubpathValue::Excluded,
90 })
91 }
92
93 fn replace(&self, capture: &Pattern) -> Result<ReplacedSubpathValue> {
94 Ok(match self {
95 SubpathValue::Alternatives(list) => ReplacedSubpathValue::Alternatives(
96 list.iter()
97 .map(|value: &SubpathValue| value.replace(capture))
98 .collect::<Result<Vec<_>>>()?,
99 ),
100 SubpathValue::Conditional(list) => ReplacedSubpathValue::Conditional(
101 list.iter()
102 .map(|(condition, value)| Ok((condition.clone(), value.replace(capture)?)))
103 .collect::<Result<Vec<_>>>()?,
104 ),
105 SubpathValue::Result(value) => {
106 ReplacedSubpathValue::Result(capture.spread_into_star(value))
107 }
108 SubpathValue::Excluded => ReplacedSubpathValue::Excluded,
109 })
110 }
111}
112
113impl SubpathValue {
114 fn results_mut(&mut self) -> ResultsIterMut<'_> {
116 ResultsIterMut { stack: vec![self] }
117 }
118
119 pub fn add_results<'a>(
124 &'a self,
125 conditions: &BTreeMap<RcStr, ConditionValue>,
126 unspecified_condition: &ConditionValue,
127 condition_overrides: &mut FxHashMap<&'a str, ConditionValue>,
128 target: &mut Vec<(&'a str, Vec<(&'a str, bool)>)>,
129 ) -> bool {
130 match self {
131 SubpathValue::Alternatives(list) => {
132 for value in list {
133 if value.add_results(
134 conditions,
135 unspecified_condition,
136 condition_overrides,
137 target,
138 ) {
139 return true;
140 }
141 }
142 false
143 }
144 SubpathValue::Conditional(list) => {
145 for (condition, value) in list {
146 let condition_value = if condition == "default" {
147 &ConditionValue::Set
148 } else {
149 condition_overrides
150 .get(condition.as_str())
151 .or_else(|| conditions.get(condition))
152 .unwrap_or(unspecified_condition)
153 };
154 match condition_value {
155 ConditionValue::Set => {
156 if value.add_results(
157 conditions,
158 unspecified_condition,
159 condition_overrides,
160 target,
161 ) {
162 return true;
163 }
164 }
165 ConditionValue::Unset => {}
166 ConditionValue::Unknown => {
167 condition_overrides.insert(condition, ConditionValue::Set);
168 if value.add_results(
169 conditions,
170 unspecified_condition,
171 condition_overrides,
172 target,
173 ) {
174 condition_overrides.insert(condition, ConditionValue::Unset);
175 } else {
176 condition_overrides.remove(condition.as_str());
177 }
178 }
179 }
180 }
181 false
182 }
183 SubpathValue::Result(r) => {
184 target.push((
185 r,
186 condition_overrides
187 .iter()
188 .filter_map(|(k, v)| match v {
189 ConditionValue::Set => Some((*k, true)),
190 ConditionValue::Unset => Some((*k, false)),
191 ConditionValue::Unknown => None,
192 })
193 .collect(),
194 ));
195 true
196 }
197 SubpathValue::Excluded => true,
198 }
199 }
200
201 fn try_new(value: &Value, ty: ExportImport) -> Result<Self> {
202 match value {
203 Value::Null => Ok(SubpathValue::Excluded),
204 Value::String(s) => Ok(SubpathValue::Result(s.as_str().into())),
205 Value::Number(_) => bail!("numeric values are invalid in {ty}s field entries"),
206 Value::Bool(_) => bail!("boolean values are invalid in {ty}s field entries"),
207 Value::Object(object) => Ok(SubpathValue::Conditional(
208 object
209 .iter()
210 .map(|(key, value)| {
211 if key.starts_with('.') {
212 bail!(
213 "invalid key \"{}\" in an {ty} field conditions object. Did you \
214 mean to place this request at a higher level?",
215 key
216 );
217 }
218
219 Ok((key.as_str().into(), SubpathValue::try_new(value, ty)?))
220 })
221 .collect::<Result<Vec<_>>>()?,
222 )),
223 Value::Array(array) => Ok(SubpathValue::Alternatives(
224 array
225 .iter()
226 .map(|value| SubpathValue::try_new(value, ty))
227 .collect::<Result<Vec<_>>>()?,
228 )),
229 }
230 }
231}
232
233impl ReplacedSubpathValue {
234 pub fn add_results<'a>(
239 &'a self,
240 conditions: &BTreeMap<RcStr, ConditionValue>,
241 unspecified_condition: &ConditionValue,
242 condition_overrides: &mut FxHashMap<&'a str, ConditionValue>,
243 target: &mut Vec<(&'a Pattern, Vec<(&'a str, bool)>)>,
244 ) -> bool {
245 match self {
246 ReplacedSubpathValue::Alternatives(list) => {
247 for value in list {
248 if value.add_results(
249 conditions,
250 unspecified_condition,
251 condition_overrides,
252 target,
253 ) {
254 return true;
255 }
256 }
257 false
258 }
259 ReplacedSubpathValue::Conditional(list) => {
260 for (condition, value) in list {
261 let condition_value = if condition == "default" {
262 &ConditionValue::Set
263 } else {
264 condition_overrides
265 .get(condition.as_str())
266 .or_else(|| conditions.get(condition))
267 .unwrap_or(unspecified_condition)
268 };
269 match condition_value {
270 ConditionValue::Set => {
271 if value.add_results(
272 conditions,
273 unspecified_condition,
274 condition_overrides,
275 target,
276 ) {
277 return true;
278 }
279 }
280 ConditionValue::Unset => {}
281 ConditionValue::Unknown => {
282 condition_overrides.insert(condition, ConditionValue::Set);
283 if value.add_results(
284 conditions,
285 unspecified_condition,
286 condition_overrides,
287 target,
288 ) {
289 condition_overrides.insert(condition, ConditionValue::Unset);
290 } else {
291 condition_overrides.remove(condition.as_str());
292 }
293 }
294 }
295 }
296 false
297 }
298 ReplacedSubpathValue::Result(r) => {
299 target.push((
300 r,
301 condition_overrides
302 .iter()
303 .filter_map(|(k, v)| match v {
304 ConditionValue::Set => Some((*k, true)),
305 ConditionValue::Unset => Some((*k, false)),
306 ConditionValue::Unknown => None,
307 })
308 .collect(),
309 ));
310 true
311 }
312 ReplacedSubpathValue::Excluded => true,
313 }
314 }
315}
316
317struct ResultsIterMut<'a> {
318 stack: Vec<&'a mut SubpathValue>,
319}
320
321impl<'a> Iterator for ResultsIterMut<'a> {
322 type Item = &'a mut RcStr;
323
324 fn next(&mut self) -> Option<Self::Item> {
325 while let Some(value) = self.stack.pop() {
326 match value {
327 SubpathValue::Alternatives(list) => {
328 for value in list {
329 self.stack.push(value);
330 }
331 }
332 SubpathValue::Conditional(list) => {
333 for (_, value) in list {
334 self.stack.push(value);
335 }
336 }
337 SubpathValue::Result(r) => return Some(r),
338 SubpathValue::Excluded => {}
339 }
340 }
341 None
342 }
343}
344
345#[derive(PartialEq, Eq, Serialize, Deserialize)]
347pub struct ExportsField(AliasMap<SubpathValue>);
348
349impl TryFrom<&Value> for ExportsField {
350 type Error = anyhow::Error;
351
352 fn try_from(value: &Value) -> Result<Self> {
353 let map = match value {
356 Value::Object(object) => {
357 let mut map = AliasMap::new();
358 let mut conditions = vec![];
361
362 for (key, value) in object.iter() {
363 if key != "." && !key.starts_with("./") {
366 conditions.push((key, value));
367 continue;
368 }
369
370 let mut value = SubpathValue::try_new(value, ExportImport::Export)?;
371
372 let pattern = if is_folder_shorthand(key) {
373 expand_folder_shorthand(key, &mut value)?
374 } else {
375 AliasPattern::parse(key.as_str())
376 };
377
378 map.insert(pattern, value);
379 }
380
381 if !conditions.is_empty() {
382 map.insert(
383 AliasPattern::Exact(".".into()),
384 SubpathValue::Conditional(
385 conditions
386 .into_iter()
387 .map(|(key, value)| {
388 Ok((
389 key.as_str().into(),
390 SubpathValue::try_new(value, ExportImport::Export)?,
391 ))
392 })
393 .collect::<Result<Vec<_>>>()?,
394 ),
395 );
396 }
397
398 map
399 }
400 Value::String(string) => {
401 let mut map = AliasMap::new();
402 map.insert(
403 AliasPattern::exact("."),
404 SubpathValue::Result(string.as_str().into()),
405 );
406 map
407 }
408 Value::Array(array) => {
409 let mut map = AliasMap::new();
410 map.insert(
411 AliasPattern::exact("."),
412 SubpathValue::Alternatives(
416 array
417 .iter()
418 .map(|value| SubpathValue::try_new(value, ExportImport::Export))
419 .collect::<Result<Vec<_>>>()?,
420 ),
421 );
422 map
423 }
424 _ => {
425 bail!("\"exports\" field must be an object or a string");
426 }
427 };
428 Ok(Self(map))
429 }
430}
431
432impl Deref for ExportsField {
433 type Target = AliasMap<SubpathValue>;
434 fn deref(&self) -> &Self::Target {
435 &self.0
436 }
437}
438
439#[derive(PartialEq, Eq, Serialize, Deserialize)]
441pub struct ImportsField(AliasMap<SubpathValue>);
442
443impl TryFrom<&Value> for ImportsField {
444 type Error = anyhow::Error;
445
446 fn try_from(value: &Value) -> Result<Self> {
447 let map = match value {
450 Value::Object(object) => {
451 let mut map = AliasMap::new();
452
453 for (key, value) in object.iter() {
454 if !key.starts_with('#') {
455 bail!("imports key \"{key}\" must begin with a '#'")
456 }
457 let value = SubpathValue::try_new(value, ExportImport::Import)?;
458 map.insert(AliasPattern::parse(key.as_str()), value);
459 }
460
461 map
462 }
463 _ => bail!("\"imports\" field must be an object"),
464 };
465 Ok(Self(map))
466 }
467}
468
469impl Deref for ImportsField {
470 type Target = AliasMap<SubpathValue>;
471 fn deref(&self) -> &Self::Target {
472 &self.0
473 }
474}
475
476fn is_folder_shorthand(key: &str) -> bool {
478 key.ends_with('/') && key.find('*').is_none()
479}
480
481fn expand_folder_shorthand(key: &str, value: &mut SubpathValue) -> Result<AliasPattern> {
489 let pattern = AliasPattern::wildcard(key, "");
491
492 for result in value.results_mut() {
494 if result.ends_with('/') {
495 if result.find('*').is_none() {
496 let mut buf = result.to_string();
497 buf.push('*');
498 *result = buf.into();
499 } else {
500 bail!(
501 "invalid exports field value \"{}\" for key \"{}\": \"*\" is not allowed in \
502 folder exports",
503 result,
504 key
505 );
506 }
507 } else {
508 bail!(
509 "invalid exports field value \"{}\" for key \"{}\": folder exports must end with \
510 \"/\"",
511 result,
512 key
513 );
514 }
515 }
516
517 Ok(pattern)
518}
519
520#[turbo_tasks::value(shared)]
522#[derive(Default)]
523pub struct ResolveAliasMap(#[turbo_tasks(trace_ignore)] AliasMap<SubpathValue>);
524
525impl TryFrom<&FxIndexMap<RcStr, Value>> for ResolveAliasMap {
526 type Error = anyhow::Error;
527
528 fn try_from(object: &FxIndexMap<RcStr, Value>) -> Result<Self> {
529 let mut map = AliasMap::new();
530
531 for (key, value) in object.iter() {
532 let mut value = SubpathValue::try_new(value, ExportImport::Export)?;
533
534 let pattern = if is_folder_shorthand(key) {
535 expand_folder_shorthand(key, &mut value)?
536 } else {
537 AliasPattern::parse(key.as_str())
538 };
539
540 map.insert(pattern, value);
541 }
542 Ok(Self(map))
543 }
544}
545
546impl<'a> IntoIterator for &'a ResolveAliasMap {
547 type Item = (AliasPattern, &'a SubpathValue);
548 type IntoIter = AliasMapIter<'a, SubpathValue>;
549
550 fn into_iter(self) -> Self::IntoIter {
551 (&self.0).into_iter()
552 }
553}