parcel_resolver/
package_json.rs

1use std::{
2  borrow::Cow,
3  cmp::Ordering,
4  ops::Range,
5  path::{Component, Path, PathBuf},
6};
7
8use bitflags::bitflags;
9use glob_match::{glob_match, glob_match_with_captures};
10use indexmap::IndexMap;
11use serde::Deserialize;
12
13use crate::{
14  cache::{Cache, CachedPath},
15  error::JsonError,
16  specifier::{decode_path, Specifier, SpecifierType},
17  ResolverError,
18};
19
20bitflags! {
21  /// A package.json top-level entry field.
22  #[derive(serde::Serialize)]
23  pub struct Fields: u8 {
24    /// The "main" field.
25    const MAIN = 1 << 0;
26    /// The "module" field.
27    const MODULE = 1 << 1;
28    /// The "source" field.
29    const SOURCE = 1 << 2;
30    /// The "browser" field.
31    const BROWSER = 1 << 3;
32    /// The "alias" field.
33    const ALIAS = 1 << 4;
34    /// The "tsconfig" field.
35    const TSCONFIG = 1 << 5;
36    /// The "types" field.
37    const TYPES = 1 << 6;
38  }
39}
40
41#[derive(serde::Deserialize, Debug, Default)]
42#[serde(rename_all = "camelCase")]
43struct SerializedPackageJson {
44  #[serde(default)]
45  pub name: String,
46  #[serde(rename = "type", default)]
47  pub module_type: ModuleType,
48  main: Option<PathBuf>,
49  module: Option<PathBuf>,
50  tsconfig: Option<PathBuf>,
51  types: Option<PathBuf>,
52  #[serde(default)]
53  pub source: SourceField,
54  #[serde(default)]
55  browser: BrowserField,
56  #[serde(default)]
57  alias: IndexMap<Specifier<'static>, AliasValue<'static>>,
58  #[serde(default)]
59  exports: ExportsField,
60  #[serde(default)]
61  imports: IndexMap<ExportsKey<'static>, ExportsField>,
62  #[serde(default)]
63  side_effects: SideEffects,
64}
65
66#[derive(Debug)]
67pub struct PackageJson {
68  pub path: CachedPath,
69  pub name: String,
70  pub module_type: ModuleType,
71  main: Option<CachedPath>,
72  module: Option<CachedPath>,
73  tsconfig: Option<CachedPath>,
74  types: Option<CachedPath>,
75  pub source: SourceField,
76  browser: BrowserField,
77  alias: IndexMap<Specifier<'static>, AliasValue<'static>>,
78  exports: ExportsField,
79  imports: IndexMap<ExportsKey<'static>, ExportsField>,
80  side_effects: SideEffects,
81}
82
83/// Whether the module is ESM, CommonJS, or JSON according to its extension or the package.json "type" field.
84#[derive(serde::Serialize, serde::Deserialize, Debug, Clone, Copy, Default, PartialEq)]
85#[serde(rename_all = "lowercase")]
86pub enum ModuleType {
87  Module,
88  Json,
89  #[default]
90  #[serde(other)]
91  CommonJs,
92}
93
94#[derive(serde::Deserialize, Debug, Default)]
95#[serde(untagged)]
96pub enum BrowserField {
97  #[default]
98  None,
99  String(String),
100  Map(IndexMap<Specifier<'static>, AliasValue<'static>>),
101}
102
103#[derive(serde::Deserialize, Debug, Default)]
104#[serde(untagged)]
105pub enum SourceField {
106  #[default]
107  None,
108  String(String),
109  Map(IndexMap<Specifier<'static>, AliasValue<'static>>),
110  Array(Vec<String>),
111  Bool(bool),
112}
113
114#[derive(serde::Deserialize, Debug, Default, PartialEq)]
115#[serde(untagged)]
116pub enum ExportsField {
117  #[default]
118  None,
119  String(String),
120  #[serde(skip)]
121  Path(CachedPath),
122  Array(Vec<ExportsField>),
123  Map(IndexMap<ExportsKey<'static>, ExportsField>),
124}
125
126impl ExportsField {
127  fn convert_paths(&mut self, base: &CachedPath, cache: &Cache) {
128    match self {
129      ExportsField::String(target) => {
130        if target.starts_with("./") && !target.contains('*') {
131          // If target split on "/" or "\" contains any "", ".", "..", or "node_modules" segments after
132          // the first "." segment, case insensitive and including percent encoded variants,
133          // throw an Invalid Package Target error.
134          let target_path = decode_path(target.as_ref(), SpecifierType::Esm).0;
135          if target_path
136            .components()
137            .enumerate()
138            .any(|(index, c)| match c {
139              Component::ParentDir => true,
140              Component::CurDir => index > 0,
141              Component::Normal(c) => c.eq_ignore_ascii_case("node_modules"),
142              _ => false,
143            })
144          {
145            return;
146          }
147
148          *self = ExportsField::Path(base.resolve(&target_path, cache));
149        }
150      }
151      ExportsField::Array(arr) => {
152        for item in arr {
153          item.convert_paths(base, cache);
154        }
155      }
156      ExportsField::Map(map) => {
157        for val in map.values_mut() {
158          val.convert_paths(base, cache);
159        }
160      }
161      _ => {}
162    }
163  }
164}
165
166bitflags! {
167  /// A common package.json "exports" field.
168  pub struct ExportsCondition: u16 {
169    /// The "import" condition. True when the package was referenced using the ESM `import` syntax.
170    const IMPORT = 1 << 0;
171    /// The "require" condition. True when the package was referenced using the CommonJS `require` function.
172    const REQUIRE = 1 << 1;
173    /// The "module" condition. True when the package was referenced from either the ESM `import` syntax or the CommonJS `require` function/
174    const MODULE = 1 << 2;
175    /// The "node" condition. True when the module will run in a Node environment.
176    const NODE = 1 << 3;
177    /// The "browser" condition. True when the module will run in a browser environment.
178    const BROWSER = 1 << 4;
179    /// The "worker" condition. True when the module will run in a web worker or service worker environment.
180    const WORKER = 1 << 5;
181    /// The "worklet" condition. True when the module will run in a worklet environment.
182    const WORKLET = 1 << 6;
183    /// The "electron" condition. True when the module will run in an Electron environment.
184    const ELECTRON = 1 << 7;
185    /// The "development" condition. True when the module will run in a development environment.
186    const DEVELOPMENT = 1 << 8;
187    /// The "production" condition. True when the module will run in a production environment.
188    const PRODUCTION = 1 << 9;
189    /// The "types" condition. True when loading TypeScript types.
190    const TYPES = 1 << 10;
191    /// The "default" condition when no other conditions matched.
192    const DEFAULT = 1 << 11;
193    /// The "style" condition. True when the package was referenced from a stylesheet (e.g. CSS, Sass, Stylus, etc.).
194    const STYLE = 1 << 12;
195    /// The "sass" condition. True when the package was referenced from a Sass stylesheet.
196    const SASS = 1 << 13;
197    /// The "less" condition. True when the package was referenced from a Less stylesheet.
198    const LESS = 1 << 14;
199    /// The "stylus" condition. True when the package was referenced from a Stylus stylesheet.
200    const STYLUS = 1 << 15;
201  }
202}
203
204impl Default for ExportsCondition {
205  fn default() -> Self {
206    ExportsCondition::empty()
207  }
208}
209
210impl TryFrom<&str> for ExportsCondition {
211  type Error = ();
212  fn try_from(value: &str) -> Result<Self, Self::Error> {
213    Ok(match value {
214      "import" => ExportsCondition::IMPORT,
215      "require" => ExportsCondition::REQUIRE,
216      "module" => ExportsCondition::MODULE,
217      "node" => ExportsCondition::NODE,
218      "browser" => ExportsCondition::BROWSER,
219      "worker" => ExportsCondition::WORKER,
220      "worklet" => ExportsCondition::WORKLET,
221      "electron" => ExportsCondition::ELECTRON,
222      "development" => ExportsCondition::DEVELOPMENT,
223      "production" => ExportsCondition::PRODUCTION,
224      "types" => ExportsCondition::TYPES,
225      "default" => ExportsCondition::DEFAULT,
226      "style" => ExportsCondition::STYLE,
227      "sass" => ExportsCondition::SASS,
228      "less" => ExportsCondition::LESS,
229      "stylus" => ExportsCondition::STYLUS,
230      _ => return Err(()),
231    })
232  }
233}
234
235#[derive(Debug, PartialEq, Eq, Hash)]
236pub enum ExportsKey<'a> {
237  Main,
238  Pattern(Cow<'a, str>),
239  Condition(ExportsCondition),
240  CustomCondition(String),
241}
242
243impl<'a> From<&str> for ExportsKey<'a> {
244  fn from(key: &str) -> Self {
245    if key == "." {
246      ExportsKey::Main
247    } else if let Some(key) = key.strip_prefix("./") {
248      ExportsKey::Pattern(Cow::Owned(key.to_owned()))
249    } else if let Some(key) = key.strip_prefix('#') {
250      ExportsKey::Pattern(Cow::Owned(key.to_owned()))
251    } else if let Ok(c) = ExportsCondition::try_from(key) {
252      ExportsKey::Condition(c)
253    } else {
254      ExportsKey::CustomCondition(key.to_owned())
255    }
256  }
257}
258
259impl<'de> Deserialize<'de> for ExportsKey<'static> {
260  fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
261  where
262    D: serde::Deserializer<'de>,
263  {
264    let s: &'de str = Deserialize::deserialize(deserializer)?;
265    Ok(ExportsKey::from(s))
266  }
267}
268
269#[derive(serde::Deserialize, Clone, PartialEq, Debug)]
270#[serde(untagged)]
271pub enum AliasValue<'a> {
272  #[serde(bound(deserialize = "'a: 'static"))]
273  Specifier(Specifier<'a>),
274  Bool(bool),
275  Global {
276    global: String,
277  },
278}
279
280#[derive(serde::Deserialize, Clone, Default, PartialEq, Debug)]
281#[serde(untagged)]
282pub enum SideEffects {
283  #[default]
284  None,
285  Boolean(bool),
286  String(String),
287  Array(Vec<String>),
288}
289
290/// An error that occurred in a package.json.
291#[derive(Debug, Clone, PartialEq, serde::Serialize)]
292pub enum PackageJsonError {
293  /// An invalid package.json "exports" or "imports" target.
294  InvalidPackageTarget,
295  /// The requested subpath of a package.json was not exported.
296  PackagePathNotExported,
297  /// An invalid specifier was requested.
298  InvalidSpecifier,
299  /// A package import was not defined.
300  ImportNotDefined,
301}
302
303#[derive(Debug, PartialEq)]
304pub enum ExportsResolution<'a> {
305  None,
306  Path(CachedPath),
307  Package(Cow<'a, str>),
308}
309
310impl PackageJson {
311  pub fn read(path: &CachedPath, cache: &Cache) -> Result<PackageJson, ResolverError> {
312    let contents = cache.fs.read_to_string(path.as_path())?;
313    let mut pkg = PackageJson::parse(path.clone(), contents, cache)
314      .map_err(|e| JsonError::new(path.as_path().into(), e))?;
315
316    // If the package has a `source` field, make sure
317    // - the package is behind symlinks
318    // - and the realpath to the packages does not includes `node_modules`.
319    // Since such package is likely a pre-compiled module
320    // installed with package managers, rather than including a source code.
321    if !matches!(pkg.source, SourceField::None) {
322      let realpath = pkg.path.canonicalize(&cache)?;
323      if realpath == pkg.path || realpath.in_node_modules() {
324        pkg.source = SourceField::None;
325      }
326    }
327
328    Ok(pkg)
329  }
330
331  pub fn parse(path: CachedPath, data: String, cache: &Cache) -> serde_json::Result<PackageJson> {
332    let parsed: SerializedPackageJson = serde_json::from_str(&data)?;
333    Ok(PackageJson::from_serialized(path, parsed, cache))
334  }
335
336  fn from_serialized(
337    path: CachedPath,
338    mut parsed: SerializedPackageJson,
339    cache: &Cache,
340  ) -> PackageJson {
341    parsed.exports.convert_paths(&path, cache);
342    PackageJson {
343      name: parsed.name,
344      module_type: parsed.module_type,
345      main: parsed.main.map(|main| path.resolve(&main, cache)),
346      module: parsed.module.map(|module| path.resolve(&module, cache)),
347      tsconfig: parsed
348        .tsconfig
349        .map(|tsconfig| path.resolve(&tsconfig, cache)),
350      types: parsed.types.map(|types| path.resolve(&types, cache)),
351      source: parsed.source,
352      browser: parsed.browser,
353      alias: parsed.alias,
354      exports: parsed.exports,
355      imports: parsed.imports,
356      side_effects: parsed.side_effects,
357      path,
358    }
359  }
360
361  pub fn entries<'a>(&'a self, fields: Fields, cache: &'a Cache) -> EntryIter {
362    EntryIter {
363      package: self,
364      fields,
365      cache,
366    }
367  }
368
369  pub fn source(&self, cache: &Cache) -> Option<CachedPath> {
370    match &self.source {
371      SourceField::None | SourceField::Array(_) | SourceField::Bool(_) => None,
372      SourceField::String(source) => Some(self.path.resolve(Path::new(source), cache)),
373      SourceField::Map(map) => match map.get(&Specifier::Package(
374        Cow::Borrowed(self.name.as_str()),
375        Cow::Borrowed(""),
376      )) {
377        Some(AliasValue::Specifier(Specifier::Relative(s))) => Some(self.path.resolve(s, cache)),
378        _ => None,
379      },
380    }
381  }
382
383  pub fn has_exports(&self) -> bool {
384    self.exports != ExportsField::None
385  }
386
387  pub fn resolve_package_exports(
388    &self,
389    subpath: &str,
390    conditions: ExportsCondition,
391    custom_conditions: &[String],
392    paths: &Cache,
393  ) -> Result<CachedPath, PackageJsonError> {
394    // If exports is an Object with both a key starting with "." and a key not starting with ".", throw an Invalid Package Configuration error.
395    if let ExportsField::Map(map) = &self.exports {
396      let mut has_conditions = false;
397      let mut has_patterns = false;
398      for key in map.keys() {
399        has_conditions = has_conditions
400          || matches!(
401            key,
402            ExportsKey::Condition(..) | ExportsKey::CustomCondition(..)
403          );
404        has_patterns = has_patterns || matches!(key, ExportsKey::Pattern(..) | ExportsKey::Main);
405        if has_conditions && has_patterns {
406          return Err(PackageJsonError::InvalidPackageTarget);
407        }
408      }
409    }
410
411    if subpath.is_empty() {
412      let mut main_export = &ExportsField::None;
413      match &self.exports {
414        ExportsField::None
415        | ExportsField::String(_)
416        | ExportsField::Path(_)
417        | ExportsField::Array(_) => {
418          main_export = &self.exports;
419        }
420        ExportsField::Map(map) => {
421          if let Some(v) = map.get(&ExportsKey::Main) {
422            main_export = v;
423          } else if !map.keys().any(|k| matches!(k, ExportsKey::Pattern(_))) {
424            main_export = &self.exports;
425          }
426        }
427      }
428
429      if main_export != &ExportsField::None {
430        match self.resolve_package_target(
431          main_export,
432          "",
433          false,
434          conditions,
435          custom_conditions,
436          paths,
437        )? {
438          ExportsResolution::Path(path) => return Ok(path),
439          ExportsResolution::None | ExportsResolution::Package(..) => {}
440        }
441      }
442    } else if let ExportsField::Map(exports) = &self.exports {
443      // All exports must start with "." at this point.
444      match self.resolve_package_imports_exports(
445        subpath,
446        exports,
447        false,
448        conditions,
449        custom_conditions,
450        paths,
451      )? {
452        ExportsResolution::Path(path) => return Ok(path),
453        ExportsResolution::None | ExportsResolution::Package(..) => {}
454      }
455    }
456
457    Err(PackageJsonError::PackagePathNotExported)
458  }
459
460  pub fn resolve_package_imports<'a>(
461    &'a self,
462    specifier: &'a str,
463    conditions: ExportsCondition,
464    custom_conditions: &[String],
465    paths: &Cache,
466  ) -> Result<ExportsResolution<'a>, PackageJsonError> {
467    if specifier == "#" || specifier.starts_with("#/") {
468      return Err(PackageJsonError::InvalidSpecifier);
469    }
470
471    match self.resolve_package_imports_exports(
472      specifier,
473      &self.imports,
474      true,
475      conditions,
476      custom_conditions,
477      paths,
478    )? {
479      ExportsResolution::None => {}
480      res => return Ok(res),
481    }
482
483    Err(PackageJsonError::ImportNotDefined)
484  }
485
486  fn resolve_package_target<'a>(
487    &'a self,
488    target: &'a ExportsField,
489    pattern_match: &str,
490    is_imports: bool,
491    conditions: ExportsCondition,
492    custom_conditions: &[String],
493    paths: &Cache,
494  ) -> Result<ExportsResolution<'_>, PackageJsonError> {
495    match target {
496      ExportsField::String(target) => {
497        if !target.starts_with("./") {
498          if !is_imports || target.starts_with("../") || target.starts_with('/') {
499            return Err(PackageJsonError::InvalidPackageTarget);
500          }
501
502          if !pattern_match.is_empty() {
503            let target = target.replace('*', pattern_match);
504            return Ok(ExportsResolution::Package(Cow::Owned(target)));
505          }
506
507          return Ok(ExportsResolution::Package(Cow::Borrowed(target)));
508        }
509
510        let target = if pattern_match.is_empty() {
511          Cow::Borrowed(target.as_str())
512        } else {
513          Cow::Owned(target.replace('*', pattern_match))
514        };
515
516        // If target split on "/" or "\" contains any "", ".", "..", or "node_modules" segments after
517        // the first "." segment, case insensitive and including percent encoded variants,
518        // throw an Invalid Package Target error.
519        let target_path = decode_path(target.as_ref(), SpecifierType::Esm).0;
520        if target_path
521          .components()
522          .enumerate()
523          .any(|(index, c)| match c {
524            Component::ParentDir => true,
525            Component::CurDir => index > 0,
526            Component::Normal(c) => c.eq_ignore_ascii_case("node_modules"),
527            _ => false,
528          })
529        {
530          return Err(PackageJsonError::InvalidPackageTarget);
531        }
532
533        let resolved_target = self.path.resolve(&target_path, paths);
534        return Ok(ExportsResolution::Path(resolved_target));
535      }
536      ExportsField::Path(target) => return Ok(ExportsResolution::Path(target.clone())),
537      ExportsField::Map(target) => {
538        // We must iterate in object insertion order.
539        for (key, value) in target {
540          let matches = match key {
541            ExportsKey::Condition(key) => {
542              *key == ExportsCondition::DEFAULT || conditions.contains(*key)
543            }
544            ExportsKey::CustomCondition(key) => custom_conditions.iter().any(|k| k == key),
545            _ => false,
546          };
547          if matches {
548            match self.resolve_package_target(
549              value,
550              pattern_match,
551              is_imports,
552              conditions,
553              custom_conditions,
554              paths,
555            )? {
556              ExportsResolution::None => continue,
557              res => return Ok(res),
558            }
559          }
560        }
561      }
562      ExportsField::Array(target) => {
563        if target.is_empty() {
564          return Err(PackageJsonError::PackagePathNotExported);
565        }
566
567        for item in target {
568          match self.resolve_package_target(
569            item,
570            pattern_match,
571            is_imports,
572            conditions,
573            custom_conditions,
574            paths,
575          ) {
576            Err(_) | Ok(ExportsResolution::None) => continue,
577            Ok(res) => return Ok(res),
578          }
579        }
580      }
581      ExportsField::None => return Ok(ExportsResolution::None),
582    }
583
584    Ok(ExportsResolution::None)
585  }
586
587  fn resolve_package_imports_exports<'a>(
588    &'a self,
589    match_key: &'a str,
590    match_obj: &'a IndexMap<ExportsKey, ExportsField>,
591    is_imports: bool,
592    conditions: ExportsCondition,
593    custom_conditions: &[String],
594    paths: &Cache,
595  ) -> Result<ExportsResolution<'_>, PackageJsonError> {
596    let pattern = ExportsKey::Pattern(Cow::Borrowed(match_key));
597    if let Some(target) = match_obj.get(&pattern) {
598      if !match_key.contains('*') {
599        return self.resolve_package_target(
600          target,
601          "",
602          is_imports,
603          conditions,
604          custom_conditions,
605          paths,
606        );
607      }
608    }
609
610    let mut best_key = "";
611    let mut best_match = "";
612    for key in match_obj.keys() {
613      if let ExportsKey::Pattern(key) = key {
614        if let Some((pattern_base, pattern_trailer)) = key.split_once('*') {
615          if match_key.starts_with(pattern_base)
616            && !pattern_trailer.contains('*')
617            && (pattern_trailer.is_empty()
618              || (match_key.len() >= key.len() && match_key.ends_with(pattern_trailer)))
619            && pattern_key_compare(best_key, key) == Ordering::Greater
620          {
621            best_key = key;
622            best_match = &match_key[pattern_base.len()..match_key.len() - pattern_trailer.len()];
623          }
624        }
625      }
626    }
627
628    if !best_key.is_empty() {
629      return self.resolve_package_target(
630        &match_obj[&ExportsKey::Pattern(Cow::Borrowed(best_key))],
631        best_match,
632        is_imports,
633        conditions,
634        custom_conditions,
635        paths,
636      );
637    }
638
639    Ok(ExportsResolution::None)
640  }
641
642  pub fn resolve_aliases<'a>(
643    &'a self,
644    specifier: &Specifier<'a>,
645    fields: Fields,
646  ) -> Option<Cow<'a, AliasValue<'a>>> {
647    if fields.contains(Fields::SOURCE) {
648      if let SourceField::Map(source) = &self.source {
649        match self.resolve_alias(source, specifier) {
650          None => {}
651          res => return res,
652        }
653      }
654    }
655
656    if fields.contains(Fields::ALIAS) {
657      match self.resolve_alias(&self.alias, specifier) {
658        None => {}
659        res => return res,
660      }
661    }
662
663    if fields.contains(Fields::BROWSER) {
664      if let BrowserField::Map(browser) = &self.browser {
665        match self.resolve_alias(browser, specifier) {
666          None => {}
667          res => return res,
668        }
669      }
670    }
671
672    None
673  }
674
675  fn resolve_alias<'a>(
676    &'a self,
677    map: &'a IndexMap<Specifier<'a>, AliasValue<'a>>,
678    specifier: &Specifier<'a>,
679  ) -> Option<Cow<'a, AliasValue>> {
680    if let Some(alias) = self.lookup_alias(map, specifier) {
681      return Some(alias);
682    }
683
684    if let Specifier::Package(package, subpath) = specifier {
685      if let Some(alias) =
686        self.lookup_alias(map, &Specifier::Package(package.clone(), Cow::Borrowed("")))
687      {
688        match alias.as_ref() {
689          AliasValue::Specifier(base) => {
690            // Join the subpath back onto the resolved alias.
691            match base {
692              Specifier::Package(base_pkg, base_subpath) => {
693                let subpath = if !base_subpath.is_empty() && !subpath.is_empty() {
694                  let mut full_subpath =
695                    String::with_capacity(base_subpath.len() + subpath.len() + 1);
696                  full_subpath.push_str(base_subpath);
697                  full_subpath.push('/');
698                  full_subpath.push_str(subpath);
699                  Cow::Owned(full_subpath)
700                } else if !subpath.is_empty() {
701                  subpath.clone()
702                } else {
703                  return Some(alias);
704                };
705                return Some(Cow::Owned(AliasValue::Specifier(Specifier::Package(
706                  base_pkg.clone(),
707                  subpath,
708                ))));
709              }
710              Specifier::Relative(path) => {
711                if subpath.is_empty() {
712                  return Some(alias);
713                } else {
714                  return Some(Cow::Owned(AliasValue::Specifier(Specifier::Relative(
715                    Cow::Owned(path.join(subpath.as_ref())),
716                  ))));
717                }
718              }
719              Specifier::Absolute(path) => {
720                if subpath.is_empty() {
721                  return Some(alias);
722                } else {
723                  return Some(Cow::Owned(AliasValue::Specifier(Specifier::Absolute(
724                    Cow::Owned(path.join(subpath.as_ref())),
725                  ))));
726                }
727              }
728              Specifier::Tilde(path) => {
729                if subpath.is_empty() {
730                  return Some(alias);
731                } else {
732                  return Some(Cow::Owned(AliasValue::Specifier(Specifier::Tilde(
733                    Cow::Owned(path.join(subpath.as_ref())),
734                  ))));
735                }
736              }
737              _ => return Some(alias),
738            }
739          }
740          _ => return Some(alias),
741        };
742      }
743    }
744
745    None
746  }
747
748  fn lookup_alias<'a>(
749    &'a self,
750    map: &'a IndexMap<Specifier<'a>, AliasValue<'a>>,
751    specifier: &Specifier<'a>,
752  ) -> Option<Cow<'a, AliasValue>> {
753    if let Some(value) = map.get(specifier) {
754      return Some(Cow::Borrowed(value));
755    }
756
757    // Match glob aliases.
758    for (key, value) in map {
759      let (glob, path) = match (key, specifier) {
760        (Specifier::Relative(glob), Specifier::Relative(path))
761        | (Specifier::Absolute(glob), Specifier::Absolute(path))
762        | (Specifier::Tilde(glob), Specifier::Tilde(path)) => (
763          glob.as_os_str().to_string_lossy(),
764          path.as_os_str().to_string_lossy(),
765        ),
766        (Specifier::Package(module_a, glob), Specifier::Package(module_b, path))
767          if module_a == module_b =>
768        {
769          (Cow::Borrowed(glob.as_ref()), Cow::Borrowed(path.as_ref()))
770        }
771        (pkg_a @ Specifier::Package(..), pkg_b @ Specifier::Package(..)) => {
772          // Glob could be in the package name, e.g. "@internal/*"
773          (pkg_a.to_string(), pkg_b.to_string())
774        }
775        _ => continue,
776      };
777
778      if let Some(captures) = glob_match_with_captures(&glob, &path) {
779        let res = match value {
780          AliasValue::Specifier(specifier) => AliasValue::Specifier(match specifier {
781            Specifier::Relative(r) => {
782              Specifier::Relative(replace_path_captures(r, &path, &captures)?)
783            }
784            Specifier::Absolute(r) => {
785              Specifier::Absolute(replace_path_captures(r, &path, &captures)?)
786            }
787            Specifier::Tilde(r) => Specifier::Tilde(replace_path_captures(r, &path, &captures)?),
788            Specifier::Package(module, subpath) => {
789              Specifier::Package(module.clone(), replace_captures(subpath, &path, &captures))
790            }
791            _ => return Some(Cow::Borrowed(value)),
792          }),
793          _ => return Some(Cow::Borrowed(value)),
794        };
795
796        return Some(Cow::Owned(res));
797      }
798    }
799
800    None
801  }
802
803  pub fn has_side_effects(&self, path: &Path) -> bool {
804    let path = path
805      .strip_prefix(self.path.as_path().parent().unwrap())
806      .ok()
807      .and_then(|path| path.as_os_str().to_str());
808
809    let path = match path {
810      Some(p) => p,
811      None => return true,
812    };
813
814    fn side_effects_glob_matches(glob: &str, path: &str) -> bool {
815      // Trim leading "./"
816      let glob = glob.strip_prefix("./").unwrap_or(glob);
817
818      // If the glob does not contain any '/' characters, prefix with "**/" to match webpack.
819      let glob = if !glob.contains('/') {
820        Cow::Owned(format!("**/{}", glob))
821      } else {
822        Cow::Borrowed(glob)
823      };
824
825      glob_match(glob.as_ref(), path)
826    }
827
828    match &self.side_effects {
829      SideEffects::None => true,
830      SideEffects::Boolean(b) => *b,
831      SideEffects::String(glob) => side_effects_glob_matches(glob, path),
832      SideEffects::Array(globs) => globs
833        .iter()
834        .any(|glob| side_effects_glob_matches(glob, path)),
835    }
836  }
837}
838
839fn replace_path_captures<'a>(
840  s: &'a Path,
841  path: &str,
842  captures: &Vec<Range<usize>>,
843) -> Option<Cow<'a, Path>> {
844  Some(
845    match replace_captures(s.as_os_str().to_str()?, path, captures) {
846      Cow::Borrowed(b) => Cow::Borrowed(Path::new(b)),
847      Cow::Owned(b) => Cow::Owned(PathBuf::from(b)),
848    },
849  )
850}
851
852/// Inserts captures matched in a glob against `path` using a pattern string.
853/// Replacements are inserted using JS-like $N syntax, e.g. $1 for the first capture.
854fn replace_captures<'a>(s: &'a str, path: &str, captures: &Vec<Range<usize>>) -> Cow<'a, str> {
855  let mut res = Cow::Borrowed(s);
856  let bytes = s.as_bytes();
857  for (idx, _) in s.match_indices('$').rev() {
858    let mut end = idx;
859    while end + 1 < bytes.len() && bytes[end + 1].is_ascii_digit() {
860      end += 1;
861    }
862
863    if end != idx {
864      if let Ok(capture_index) = s[idx + 1..end + 1].parse::<usize>() {
865        if capture_index > 0 && capture_index - 1 < captures.len() {
866          res
867            .to_mut()
868            .replace_range(idx..end + 1, &path[captures[capture_index - 1].clone()]);
869        }
870      }
871    }
872  }
873
874  res
875}
876
877fn pattern_key_compare(a: &str, b: &str) -> Ordering {
878  let a_pos = a.chars().position(|c| c == '*');
879  let b_pos = b.chars().position(|c| c == '*');
880  let base_length_a = a_pos.map_or(a.len(), |p| p + 1);
881  let base_length_b = b_pos.map_or(b.len(), |p| p + 1);
882  let cmp = base_length_b.cmp(&base_length_a);
883  if cmp != Ordering::Equal {
884    return cmp;
885  }
886
887  if a_pos.is_none() {
888    return Ordering::Greater;
889  }
890
891  if b_pos.is_none() {
892    return Ordering::Less;
893  }
894
895  b.len().cmp(&a.len())
896}
897
898pub struct EntryIter<'a> {
899  package: &'a PackageJson,
900  fields: Fields,
901  cache: &'a Cache,
902}
903
904impl<'a> Iterator for EntryIter<'a> {
905  type Item = (CachedPath, &'static str);
906
907  fn next(&mut self) -> Option<Self::Item> {
908    if self.fields.contains(Fields::SOURCE) {
909      self.fields.remove(Fields::SOURCE);
910      if let Some(source) = self.package.source(&self.cache) {
911        return Some((source, "source"));
912      }
913    }
914
915    if self.fields.contains(Fields::TYPES) {
916      self.fields.remove(Fields::TYPES);
917      if let Some(types) = &self.package.types {
918        return Some((types.clone(), "types"));
919      }
920    }
921
922    if self.fields.contains(Fields::BROWSER) {
923      self.fields.remove(Fields::BROWSER);
924      match &self.package.browser {
925        BrowserField::None => {}
926        BrowserField::String(browser) => {
927          return Some((
928            self.package.path.resolve(Path::new(browser), self.cache),
929            "browser",
930          ))
931        }
932        BrowserField::Map(map) => {
933          if let Some(AliasValue::Specifier(Specifier::Relative(s))) = map.get(&Specifier::Package(
934            Cow::Borrowed(&self.package.name),
935            Cow::Borrowed(""),
936          )) {
937            return Some((self.package.path.resolve(s, self.cache), "browser"));
938          }
939        }
940      }
941    }
942
943    if self.fields.contains(Fields::MODULE) {
944      self.fields.remove(Fields::MODULE);
945      if let Some(module) = &self.package.module {
946        return Some((module.clone(), "module"));
947      }
948    }
949
950    if self.fields.contains(Fields::MAIN) {
951      self.fields.remove(Fields::MAIN);
952      if let Some(main) = &self.package.main {
953        return Some((main.clone(), "main"));
954      }
955    }
956
957    if self.fields.contains(Fields::TSCONFIG) {
958      self.fields.remove(Fields::TSCONFIG);
959      if let Some(tsconfig) = &self.package.tsconfig {
960        return Some((tsconfig.clone(), "tsconfig"));
961      }
962    }
963
964    None
965  }
966}
967
968#[cfg(test)]
969mod tests {
970  use super::*;
971  use indexmap::indexmap;
972
973  // Based on https://github.com/lukeed/resolve.exports/blob/master/test/resolve.js,
974  // https://github.com/privatenumber/resolve-pkg-maps/tree/develop/tests, and
975  // https://github.com/webpack/enhanced-resolve/blob/main/test/exportsField.js
976
977  #[test]
978  fn exports_string() {
979    let cache = Cache::default();
980    let pkg = PackageJson::from_serialized(
981      cache.get_normalized("/foo/package.json"),
982      SerializedPackageJson {
983        name: "foobar".into(),
984        exports: ExportsField::String("./exports.js".into()),
985        ..Default::default()
986      },
987      &cache,
988    );
989
990    assert_eq!(
991      pkg
992        .resolve_package_exports("", ExportsCondition::empty(), &[], &cache)
993        .unwrap(),
994      cache.get_normalized("/foo/exports.js")
995    );
996    // assert_eq!(pkg.resolve_package_exports("./exports.js", &[]).unwrap(), cache.get_normalized("/foo/exports.js"), &cache);
997    // assert_eq!(pkg.resolve_package_exports("foobar", &[]).unwrap(), cache.get_normalized("/foo/exports.js"), &cache);
998  }
999
1000  #[test]
1001  fn exports_dot() {
1002    let cache = Cache::default();
1003    let pkg = PackageJson::from_serialized(
1004      cache.get_normalized("/foo/package.json"),
1005      SerializedPackageJson {
1006        name: "foobar".into(),
1007        exports: ExportsField::Map(indexmap! {
1008          ".".into() => ExportsField::String("./exports.js".into())
1009        }),
1010        ..Default::default()
1011      },
1012      &cache,
1013    );
1014
1015    assert_eq!(
1016      pkg
1017        .resolve_package_exports("", ExportsCondition::empty(), &[], &cache)
1018        .unwrap(),
1019      cache.get_normalized("/foo/exports.js")
1020    );
1021    assert!(matches!(
1022      pkg.resolve_package_exports(".", ExportsCondition::empty(), &[], &cache),
1023      Err(PackageJsonError::PackagePathNotExported)
1024    ));
1025    // assert_eq!(pkg.resolve_package_exports("foobar", &[]).unwrap(), cache.get_normalized("/foo/exports.js"), &cache);
1026  }
1027
1028  #[test]
1029  fn exports_dot_conditions() {
1030    let cache = Cache::default();
1031    let pkg = PackageJson::from_serialized(
1032      cache.get_normalized("/foo/package.json"),
1033      SerializedPackageJson {
1034        name: "foobar".into(),
1035        exports: ExportsField::Map(indexmap! {
1036          ".".into() => ExportsField::Map(indexmap! {
1037            "import".into() => ExportsField::String("./import.js".into()),
1038            "require".into() => ExportsField::String("./require.js".into())
1039          })
1040        }),
1041        ..Default::default()
1042      },
1043      &cache,
1044    );
1045
1046    assert_eq!(
1047      pkg
1048        .resolve_package_exports(
1049          "",
1050          ExportsCondition::IMPORT | ExportsCondition::REQUIRE,
1051          &[],
1052          &cache
1053        )
1054        .unwrap(),
1055      cache.get_normalized("/foo/import.js")
1056    );
1057    assert_eq!(
1058      pkg
1059        .resolve_package_exports("", ExportsCondition::REQUIRE, &[], &cache)
1060        .unwrap(),
1061      cache.get_normalized("/foo/require.js")
1062    );
1063    assert!(matches!(
1064      pkg.resolve_package_exports("", ExportsCondition::empty(), &[], &cache),
1065      Err(PackageJsonError::PackagePathNotExported)
1066    ));
1067    assert!(matches!(
1068      pkg.resolve_package_exports("", ExportsCondition::NODE, &[], &cache),
1069      Err(PackageJsonError::PackagePathNotExported)
1070    ));
1071  }
1072
1073  #[test]
1074  fn exports_map_string() {
1075    let cache = Cache::default();
1076    let pkg = PackageJson::from_serialized(
1077      cache.get_normalized("/foo/package.json"),
1078      SerializedPackageJson {
1079        name: "foobar".into(),
1080        exports: ExportsField::Map(indexmap! {
1081          "./foo".into() => ExportsField::String("./exports.js".into()),
1082          "./.invisible".into() => ExportsField::String("./.invisible.js".into()),
1083          "./".into() => ExportsField::String("./".into()),
1084          "./*".into() => ExportsField::String("./*.js".into())
1085        }),
1086        ..Default::default()
1087      },
1088      &cache,
1089    );
1090
1091    assert_eq!(
1092      pkg
1093        .resolve_package_exports("foo", ExportsCondition::empty(), &[], &cache)
1094        .unwrap(),
1095      cache.get_normalized("/foo/exports.js")
1096    );
1097    assert_eq!(
1098      pkg
1099        .resolve_package_exports(".invisible", ExportsCondition::empty(), &[], &cache)
1100        .unwrap(),
1101      cache.get_normalized("/foo/.invisible.js")
1102    );
1103    assert_eq!(
1104      pkg
1105        .resolve_package_exports("file", ExportsCondition::empty(), &[], &cache)
1106        .unwrap(),
1107      cache.get_normalized("/foo/file.js")
1108    );
1109  }
1110
1111  #[test]
1112  fn exports_map_conditions() {
1113    let cache = Cache::default();
1114    let pkg = PackageJson::from_serialized(
1115      cache.get_normalized("/foo/package.json"),
1116      SerializedPackageJson {
1117        name: "foobar".into(),
1118        exports: ExportsField::Map(indexmap! {
1119          "./foo".into() => ExportsField::Map(indexmap! {
1120            "import".into() => ExportsField::String("./import.js".into()),
1121            "require".into() => ExportsField::String("./require.js".into())
1122          })
1123        }),
1124        ..Default::default()
1125      },
1126      &cache,
1127    );
1128
1129    assert_eq!(
1130      pkg
1131        .resolve_package_exports(
1132          "foo",
1133          ExportsCondition::IMPORT | ExportsCondition::REQUIRE,
1134          &[],
1135          &cache
1136        )
1137        .unwrap(),
1138      cache.get_normalized("/foo/import.js")
1139    );
1140    assert_eq!(
1141      pkg
1142        .resolve_package_exports("foo", ExportsCondition::REQUIRE, &[], &cache)
1143        .unwrap(),
1144      cache.get_normalized("/foo/require.js")
1145    );
1146    assert!(matches!(
1147      pkg.resolve_package_exports("foo", ExportsCondition::empty(), &[], &cache),
1148      Err(PackageJsonError::PackagePathNotExported)
1149    ));
1150    assert!(matches!(
1151      pkg.resolve_package_exports("foo", ExportsCondition::NODE, &[], &cache),
1152      Err(PackageJsonError::PackagePathNotExported)
1153    ));
1154  }
1155
1156  #[test]
1157  fn nested_conditions() {
1158    let cache = Cache::default();
1159    let pkg = PackageJson::from_serialized(
1160      cache.get_normalized("/foo/package.json"),
1161      SerializedPackageJson {
1162        name: "foobar".into(),
1163        exports: ExportsField::Map(indexmap! {
1164          "node".into() => ExportsField::Map(indexmap! {
1165            "import".into() => ExportsField::String("./import.js".into()),
1166            "require".into() => ExportsField::String("./require.js".into())
1167          }),
1168          "default".into() => ExportsField::String("./default.js".into())
1169        }),
1170        ..Default::default()
1171      },
1172      &cache,
1173    );
1174
1175    assert_eq!(
1176      pkg
1177        .resolve_package_exports(
1178          "",
1179          ExportsCondition::NODE | ExportsCondition::IMPORT,
1180          &[],
1181          &cache
1182        )
1183        .unwrap(),
1184      cache.get_normalized("/foo/import.js")
1185    );
1186    assert_eq!(
1187      pkg
1188        .resolve_package_exports(
1189          "",
1190          ExportsCondition::NODE | ExportsCondition::REQUIRE,
1191          &[],
1192          &cache
1193        )
1194        .unwrap(),
1195      cache.get_normalized("/foo/require.js")
1196    );
1197    assert_eq!(
1198      pkg
1199        .resolve_package_exports("", ExportsCondition::IMPORT, &[], &cache)
1200        .unwrap(),
1201      cache.get_normalized("/foo/default.js")
1202    );
1203    assert_eq!(
1204      pkg
1205        .resolve_package_exports("", ExportsCondition::empty(), &[], &cache)
1206        .unwrap(),
1207      cache.get_normalized("/foo/default.js")
1208    );
1209    assert_eq!(
1210      pkg
1211        .resolve_package_exports("", ExportsCondition::NODE, &[], &cache)
1212        .unwrap(),
1213      cache.get_normalized("/foo/default.js")
1214    );
1215  }
1216
1217  #[test]
1218  fn custom_conditions() {
1219    let cache = Cache::default();
1220    let pkg = PackageJson::from_serialized(
1221      cache.get_normalized("/foo/package.json"),
1222      SerializedPackageJson {
1223        name: "foobar".into(),
1224        exports: ExportsField::Map(indexmap! {
1225          "custom".into() => ExportsField::String("./custom.js".into()),
1226          "default".into() => ExportsField::String("./default.js".into())
1227        }),
1228        ..Default::default()
1229      },
1230      &cache,
1231    );
1232    assert_eq!(
1233      pkg
1234        .resolve_package_exports("", ExportsCondition::NODE, &["custom".into()], &cache)
1235        .unwrap(),
1236      cache.get_normalized("/foo/custom.js")
1237    );
1238    assert_eq!(
1239      pkg
1240        .resolve_package_exports("", ExportsCondition::NODE, &[], &cache)
1241        .unwrap(),
1242      cache.get_normalized("/foo/default.js")
1243    );
1244  }
1245
1246  #[test]
1247  fn subpath_nested_conditions() {
1248    let cache = Cache::default();
1249    let pkg = PackageJson::from_serialized(
1250      cache.get_normalized("/foo/package.json"),
1251      SerializedPackageJson {
1252        name: "foobar".into(),
1253        exports: ExportsField::Map(indexmap! {
1254          "./lite".into() => ExportsField::Map(indexmap! {
1255            "node".into() => ExportsField::Map(indexmap! {
1256              "import".into() => ExportsField::String("./node_import.js".into()),
1257              "require".into() => ExportsField::String("./node_require.js".into())
1258            }),
1259            "browser".into() => ExportsField::Map(indexmap! {
1260              "import".into() => ExportsField::String("./browser_import.js".into()),
1261              "require".into() => ExportsField::String("./browser_require.js".into())
1262            }),
1263          })
1264        }),
1265        ..Default::default()
1266      },
1267      &cache,
1268    );
1269
1270    assert_eq!(
1271      pkg
1272        .resolve_package_exports(
1273          "lite",
1274          ExportsCondition::NODE | ExportsCondition::IMPORT,
1275          &[],
1276          &cache
1277        )
1278        .unwrap(),
1279      cache.get_normalized("/foo/node_import.js")
1280    );
1281    assert_eq!(
1282      pkg
1283        .resolve_package_exports(
1284          "lite",
1285          ExportsCondition::NODE | ExportsCondition::REQUIRE,
1286          &[],
1287          &cache
1288        )
1289        .unwrap(),
1290      cache.get_normalized("/foo/node_require.js")
1291    );
1292    assert_eq!(
1293      pkg
1294        .resolve_package_exports(
1295          "lite",
1296          ExportsCondition::BROWSER | ExportsCondition::IMPORT,
1297          &[],
1298          &cache
1299        )
1300        .unwrap(),
1301      cache.get_normalized("/foo/browser_import.js")
1302    );
1303    assert_eq!(
1304      pkg
1305        .resolve_package_exports(
1306          "lite",
1307          ExportsCondition::BROWSER | ExportsCondition::REQUIRE,
1308          &[],
1309          &cache
1310        )
1311        .unwrap(),
1312      cache.get_normalized("/foo/browser_require.js")
1313    );
1314    assert!(matches!(
1315      pkg.resolve_package_exports("lite", ExportsCondition::empty(), &[], &cache),
1316      Err(PackageJsonError::PackagePathNotExported)
1317    ));
1318  }
1319
1320  #[test]
1321  fn subpath_star() {
1322    let cache = Cache::default();
1323    let pkg = PackageJson::from_serialized(
1324      cache.get_normalized("/foo/package.json"),
1325      SerializedPackageJson {
1326        name: "foobar".into(),
1327        exports: ExportsField::Map(indexmap! {
1328          "./*".into() => ExportsField::String("./cheese/*.mjs".into()),
1329          "./pizza/*".into() => ExportsField::String("./pizza/*.mjs".into()),
1330          "./burritos/*".into() => ExportsField::String("./burritos/*/*.mjs".into()),
1331          "./literal".into() => ExportsField::String("./literal/*.js".into()),
1332        }),
1333        ..Default::default()
1334      },
1335      &cache,
1336    );
1337
1338    assert_eq!(
1339      pkg
1340        .resolve_package_exports("hello", ExportsCondition::empty(), &[], &cache)
1341        .unwrap(),
1342      cache.get_normalized("/foo/cheese/hello.mjs")
1343    );
1344    assert_eq!(
1345      pkg
1346        .resolve_package_exports("hello/world", ExportsCondition::empty(), &[], &cache)
1347        .unwrap(),
1348      cache.get_normalized("/foo/cheese/hello/world.mjs")
1349    );
1350    assert_eq!(
1351      pkg
1352        .resolve_package_exports("hello.js", ExportsCondition::empty(), &[], &cache)
1353        .unwrap(),
1354      cache.get_normalized("/foo/cheese/hello.js.mjs")
1355    );
1356    assert_eq!(
1357      pkg
1358        .resolve_package_exports("pizza/test", ExportsCondition::empty(), &[], &cache)
1359        .unwrap(),
1360      cache.get_normalized("/foo/pizza/test.mjs")
1361    );
1362    assert_eq!(
1363      pkg
1364        .resolve_package_exports("burritos/test", ExportsCondition::empty(), &[], &cache)
1365        .unwrap(),
1366      cache.get_normalized("/foo/burritos/test/test.mjs")
1367    );
1368    assert_eq!(
1369      pkg
1370        .resolve_package_exports("literal", ExportsCondition::empty(), &[], &cache)
1371        .unwrap(),
1372      cache.get_normalized("/foo/literal/*.js")
1373    );
1374
1375    let pkg = PackageJson::from_serialized(
1376      cache.get_normalized("/foo/package.json"),
1377      SerializedPackageJson {
1378        name: "foobar".into(),
1379        exports: ExportsField::Map(indexmap! {
1380          "./*".into() => ExportsField::String("./*.js".into()),
1381          "./*.js".into() => ExportsField::None,
1382          "./internal/*".into() => ExportsField::None,
1383        }),
1384        ..Default::default()
1385      },
1386      &cache,
1387    );
1388    assert_eq!(
1389      pkg
1390        .resolve_package_exports("file", ExportsCondition::empty(), &[], &cache)
1391        .unwrap(),
1392      cache.get_normalized("/foo/file.js")
1393    );
1394    assert!(matches!(
1395      pkg.resolve_package_exports("file.js", ExportsCondition::empty(), &[], &cache),
1396      Err(PackageJsonError::PackagePathNotExported)
1397    ));
1398    assert!(matches!(
1399      pkg.resolve_package_exports("internal/file", ExportsCondition::empty(), &[], &cache),
1400      Err(PackageJsonError::PackagePathNotExported)
1401    ));
1402  }
1403
1404  #[test]
1405  fn exports_null() {
1406    let cache = Cache::default();
1407    let pkg = PackageJson::from_serialized(
1408      cache.get_normalized("/foo/package.json"),
1409      SerializedPackageJson {
1410        name: "foobar".into(),
1411        exports: ExportsField::Map(indexmap! {
1412          "./features/*.js".into() => ExportsField::String("./src/features/*.js".into()),
1413          "./features/private-internal/*".into() => ExportsField::None,
1414        }),
1415        ..Default::default()
1416      },
1417      &cache,
1418    );
1419
1420    assert_eq!(
1421      pkg
1422        .resolve_package_exports("features/foo.js", ExportsCondition::empty(), &[], &cache)
1423        .unwrap(),
1424      cache.get_normalized("/foo/src/features/foo.js")
1425    );
1426    assert_eq!(
1427      pkg
1428        .resolve_package_exports(
1429          "features/foo/bar.js",
1430          ExportsCondition::empty(),
1431          &[],
1432          &cache
1433        )
1434        .unwrap(),
1435      cache.get_normalized("/foo/src/features/foo/bar.js")
1436    );
1437    assert!(matches!(
1438      pkg.resolve_package_exports(
1439        "features/private-internal/foo.js",
1440        ExportsCondition::empty(),
1441        &[],
1442        &cache
1443      ),
1444      Err(PackageJsonError::PackagePathNotExported)
1445    ),);
1446  }
1447
1448  #[test]
1449  fn exports_array() {
1450    let cache = Cache::default();
1451    let pkg = PackageJson::from_serialized(
1452      cache.get_normalized("/foo/package.json"),
1453      SerializedPackageJson {
1454        name: "foobar".into(),
1455        exports: ExportsField::Map(indexmap! {
1456          "./utils/*".into() => ExportsField::Map(indexmap! {
1457            "browser".into() => ExportsField::Map(indexmap! {
1458              "worklet".into() => ExportsField::Array(vec![ExportsField::String("./*".into()), ExportsField::String("./node/*".into())]),
1459              "default".into() => ExportsField::Map(indexmap! {
1460                "node".into() => ExportsField::String("./node/*".into())
1461              })
1462            })
1463          }),
1464          "./test/*".into() => ExportsField::Array(vec![ExportsField::String("lodash/*".into()), ExportsField::String("./bar/*".into())]),
1465          "./file".into() => ExportsField::Array(vec![ExportsField::String("http://a.com".into()), ExportsField::String("./file.js".into())])
1466        }),
1467        ..Default::default()
1468      },
1469      &cache,
1470    );
1471
1472    assert_eq!(
1473      pkg
1474        .resolve_package_exports(
1475          "utils/index.js",
1476          ExportsCondition::BROWSER | ExportsCondition::WORKLET,
1477          &[],
1478          &cache
1479        )
1480        .unwrap(),
1481      cache.get_normalized("/foo/index.js")
1482    );
1483    assert_eq!(
1484      pkg
1485        .resolve_package_exports(
1486          "utils/index.js",
1487          ExportsCondition::BROWSER | ExportsCondition::NODE,
1488          &[],
1489          &cache
1490        )
1491        .unwrap(),
1492      cache.get_normalized("/foo/node/index.js")
1493    );
1494    assert_eq!(
1495      pkg
1496        .resolve_package_exports("test/index.js", ExportsCondition::empty(), &[], &cache)
1497        .unwrap(),
1498      cache.get_normalized("/foo/bar/index.js")
1499    );
1500    assert_eq!(
1501      pkg
1502        .resolve_package_exports("file", ExportsCondition::empty(), &[], &cache)
1503        .unwrap(),
1504      cache.get_normalized("/foo/file.js")
1505    );
1506    assert!(matches!(
1507      pkg.resolve_package_exports("utils/index.js", ExportsCondition::BROWSER, &[], &cache),
1508      Err(PackageJsonError::PackagePathNotExported)
1509    ));
1510    assert!(matches!(
1511      pkg.resolve_package_exports("dir/file.js", ExportsCondition::BROWSER, &[], &cache),
1512      Err(PackageJsonError::PackagePathNotExported)
1513    ));
1514
1515    let pkg = PackageJson::from_serialized(
1516      cache.get_normalized("/foo/package.json"),
1517      SerializedPackageJson {
1518        name: "foobar".into(),
1519        exports: ExportsField::Array(vec![
1520          ExportsField::Map(indexmap! {
1521            "node".into() => ExportsField::String("./a.js".into())
1522          }),
1523          ExportsField::String("./b.js".into()),
1524        ]),
1525        ..Default::default()
1526      },
1527      &cache,
1528    );
1529
1530    assert_eq!(
1531      pkg
1532        .resolve_package_exports("", ExportsCondition::empty(), &[], &cache)
1533        .unwrap(),
1534      cache.get_normalized("/foo/b.js")
1535    );
1536    assert_eq!(
1537      pkg
1538        .resolve_package_exports("", ExportsCondition::NODE, &[], &cache)
1539        .unwrap(),
1540      cache.get_normalized("/foo/a.js")
1541    );
1542  }
1543
1544  #[test]
1545  fn exports_invalid() {
1546    let cache = Cache::default();
1547    let pkg = PackageJson::from_serialized(
1548      cache.get_normalized("/foo/package.json"),
1549      SerializedPackageJson {
1550        name: "foobar".into(),
1551        exports: ExportsField::Map(indexmap! {
1552          "./invalid".into() => ExportsField::String("../invalid".into()),
1553          "./absolute".into() => ExportsField::String("/absolute".into()),
1554          "./package".into() => ExportsField::String("package".into()),
1555          "./utils/index".into() => ExportsField::String("./src/../index.js".into()),
1556          "./dist/*".into() => ExportsField::String("./src/../../*".into()),
1557          "./modules/*".into() => ExportsField::String("./node_modules/*".into()),
1558          "./modules2/*".into() => ExportsField::String("./NODE_MODULES/*".into()),
1559          "./*/*".into() => ExportsField::String("./file.js".into())
1560        }),
1561        ..Default::default()
1562      },
1563      &cache,
1564    );
1565
1566    assert!(matches!(
1567      pkg.resolve_package_exports("invalid", ExportsCondition::empty(), &[], &cache),
1568      Err(PackageJsonError::InvalidPackageTarget)
1569    ));
1570    assert!(matches!(
1571      pkg.resolve_package_exports("absolute", ExportsCondition::empty(), &[], &cache),
1572      Err(PackageJsonError::InvalidPackageTarget)
1573    ));
1574    assert!(matches!(
1575      pkg.resolve_package_exports("package", ExportsCondition::empty(), &[], &cache),
1576      Err(PackageJsonError::InvalidPackageTarget)
1577    ));
1578    assert!(matches!(
1579      pkg.resolve_package_exports("utils/index", ExportsCondition::empty(), &[], &cache),
1580      Err(PackageJsonError::InvalidPackageTarget)
1581    ));
1582    assert!(matches!(
1583      pkg.resolve_package_exports("dist/foo", ExportsCondition::empty(), &[], &cache),
1584      Err(PackageJsonError::InvalidPackageTarget)
1585    ));
1586    assert!(matches!(
1587      pkg.resolve_package_exports("modules/foo", ExportsCondition::empty(), &[], &cache),
1588      Err(PackageJsonError::InvalidPackageTarget)
1589    ));
1590    assert!(matches!(
1591      pkg.resolve_package_exports("a/b", ExportsCondition::empty(), &[], &cache),
1592      Err(PackageJsonError::PackagePathNotExported)
1593    ));
1594    assert!(matches!(
1595      pkg.resolve_package_exports("a/*", ExportsCondition::empty(), &[], &cache),
1596      Err(PackageJsonError::PackagePathNotExported)
1597    ));
1598
1599    let pkg = PackageJson::from_serialized(
1600      cache.get_normalized("/foo/package.json"),
1601      SerializedPackageJson {
1602        name: "foobar".into(),
1603        exports: ExportsField::Map(indexmap! {
1604          ".".into() => ExportsField::String("./foo.js".into()),
1605          "node".into() => ExportsField::String("./bar.js".into()),
1606        }),
1607        ..Default::default()
1608      },
1609      &cache,
1610    );
1611
1612    assert!(matches!(
1613      pkg.resolve_package_exports("", ExportsCondition::NODE, &[], &cache),
1614      Err(PackageJsonError::InvalidPackageTarget)
1615    ));
1616    assert!(matches!(
1617      pkg.resolve_package_exports("", ExportsCondition::NODE, &[], &cache),
1618      Err(PackageJsonError::InvalidPackageTarget)
1619    ));
1620  }
1621
1622  #[test]
1623  fn imports() {
1624    let cache = Cache::default();
1625    let pkg = PackageJson::from_serialized(
1626      cache.get_normalized("/foo/package.json"),
1627      SerializedPackageJson {
1628        name: "foobar".into(),
1629        imports: indexmap! {
1630          "#foo".into() => ExportsField::String("./foo.mjs".into()),
1631          "#internal/*".into() => ExportsField::String("./src/internal/*.mjs".into()),
1632          "#bar".into() => ExportsField::String("bar".into()),
1633        },
1634        ..Default::default()
1635      },
1636      &cache,
1637    );
1638
1639    assert_eq!(
1640      pkg
1641        .resolve_package_imports("foo", ExportsCondition::empty(), &[], &cache)
1642        .unwrap(),
1643      ExportsResolution::Path(cache.get_normalized("/foo/foo.mjs"))
1644    );
1645    assert_eq!(
1646      pkg
1647        .resolve_package_imports("internal/foo", ExportsCondition::empty(), &[], &cache)
1648        .unwrap(),
1649      ExportsResolution::Path(cache.get_normalized("/foo/src/internal/foo.mjs"))
1650    );
1651    assert_eq!(
1652      pkg
1653        .resolve_package_imports("bar", ExportsCondition::empty(), &[], &cache)
1654        .unwrap(),
1655      ExportsResolution::Package("bar".into())
1656    );
1657  }
1658
1659  #[test]
1660  fn import_conditions() {
1661    let cache = Cache::default();
1662    let pkg = PackageJson::from_serialized(
1663      cache.get_normalized("/foo/package.json"),
1664      SerializedPackageJson {
1665        name: "foobar".into(),
1666        imports: indexmap! {
1667          "#entry/*".into() => ExportsField::Map(indexmap! {
1668            "node".into() => ExportsField::String("./node/*.js".into()),
1669            "browser".into() => ExportsField::String("./browser/*.js".into())
1670          })
1671        },
1672        ..Default::default()
1673      },
1674      &cache,
1675    );
1676    assert_eq!(
1677      pkg
1678        .resolve_package_imports("entry/foo", ExportsCondition::NODE, &[], &cache)
1679        .unwrap(),
1680      ExportsResolution::Path(cache.get_normalized("/foo/node/foo.js"))
1681    );
1682    assert_eq!(
1683      pkg
1684        .resolve_package_imports("entry/foo", ExportsCondition::BROWSER, &[], &cache)
1685        .unwrap(),
1686      ExportsResolution::Path(cache.get_normalized("/foo/browser/foo.js"))
1687    );
1688    assert_eq!(
1689      pkg
1690        .resolve_package_imports(
1691          "entry/foo",
1692          ExportsCondition::NODE | ExportsCondition::BROWSER,
1693          &[],
1694          &cache
1695        )
1696        .unwrap(),
1697      ExportsResolution::Path(cache.get_normalized("/foo/node/foo.js"))
1698    );
1699  }
1700
1701  #[test]
1702  fn aliases() {
1703    let cache = Cache::default();
1704    let pkg = PackageJson::from_serialized(
1705      cache.get_normalized("/foo/package.json"),
1706      SerializedPackageJson {
1707        name: "foobar".into(),
1708        alias: indexmap! {
1709          "./foo.js".into() => AliasValue::Specifier("./foo-alias.js".into()),
1710          "bar".into()  => AliasValue::Specifier("./bar-alias.js".into()),
1711          "lodash".into()  => AliasValue::Specifier("my-lodash".into()),
1712          "lodash/clone".into()  => AliasValue::Specifier("./clone.js".into()),
1713          "test".into() => AliasValue::Specifier("./test".into()),
1714          "foo/*".into() => AliasValue::Specifier("bar/$1".into()),
1715          "./foo/src/**".into() => AliasValue::Specifier("./foo/lib/$1".into()),
1716          "/foo/src/**".into() => AliasValue::Specifier("/foo/lib/$1".into()),
1717          "~/foo/src/**".into() => AliasValue::Specifier("~/foo/lib/$1".into()),
1718          "url".into() => AliasValue::Bool(false),
1719          "@internal/**".into() => AliasValue::Specifier("./internal/$1".into()),
1720          "@foo/*/bar/*".into() => AliasValue::Specifier("./test/$1/$2".into()),
1721        },
1722        ..Default::default()
1723      },
1724      &cache,
1725    );
1726
1727    assert_eq!(
1728      pkg.resolve_aliases(&"./foo.js".into(), Fields::ALIAS),
1729      Some(Cow::Owned(AliasValue::Specifier("./foo-alias.js".into())))
1730    );
1731    assert_eq!(
1732      pkg.resolve_aliases(&"bar".into(), Fields::ALIAS),
1733      Some(Cow::Owned(AliasValue::Specifier("./bar-alias.js".into())))
1734    );
1735    assert_eq!(
1736      pkg.resolve_aliases(&"lodash".into(), Fields::ALIAS),
1737      Some(Cow::Owned(AliasValue::Specifier("my-lodash".into())))
1738    );
1739    assert_eq!(
1740      pkg.resolve_aliases(&"lodash/foo".into(), Fields::ALIAS),
1741      Some(Cow::Owned(AliasValue::Specifier("my-lodash/foo".into())))
1742    );
1743    assert_eq!(
1744      pkg.resolve_aliases(&"lodash/clone".into(), Fields::ALIAS),
1745      Some(Cow::Owned(AliasValue::Specifier("./clone.js".into())))
1746    );
1747    assert_eq!(
1748      pkg.resolve_aliases(&"test".into(), Fields::ALIAS),
1749      Some(Cow::Owned(AliasValue::Specifier("./test".into())))
1750    );
1751    assert_eq!(
1752      pkg.resolve_aliases(&"test/foo".into(), Fields::ALIAS),
1753      Some(Cow::Owned(AliasValue::Specifier("./test/foo".into())))
1754    );
1755    assert_eq!(
1756      pkg.resolve_aliases(&"foo/hi".into(), Fields::ALIAS),
1757      Some(Cow::Owned(AliasValue::Specifier("bar/hi".into())))
1758    );
1759    assert_eq!(
1760      pkg.resolve_aliases(&"./foo/src/a/b".into(), Fields::ALIAS),
1761      Some(Cow::Owned(AliasValue::Specifier("./foo/lib/a/b".into())))
1762    );
1763    assert_eq!(
1764      pkg.resolve_aliases(&"/foo/src/a/b".into(), Fields::ALIAS),
1765      Some(Cow::Owned(AliasValue::Specifier("/foo/lib/a/b".into())))
1766    );
1767    assert_eq!(
1768      pkg.resolve_aliases(&"~/foo/src/a/b".into(), Fields::ALIAS),
1769      Some(Cow::Owned(AliasValue::Specifier("~/foo/lib/a/b".into())))
1770    );
1771    assert_eq!(
1772      pkg.resolve_aliases(&"url".into(), Fields::ALIAS),
1773      Some(Cow::Owned(AliasValue::Bool(false)))
1774    );
1775    assert_eq!(
1776      pkg.resolve_aliases(&"@internal/foo".into(), Fields::ALIAS),
1777      Some(Cow::Owned(AliasValue::Specifier("./internal/foo".into())))
1778    );
1779    assert_eq!(
1780      pkg.resolve_aliases(&"@internal/foo/bar".into(), Fields::ALIAS),
1781      Some(Cow::Owned(AliasValue::Specifier(
1782        "./internal/foo/bar".into()
1783      )))
1784    );
1785    assert_eq!(
1786      pkg.resolve_aliases(&"@foo/a/bar/b".into(), Fields::ALIAS),
1787      Some(Cow::Owned(AliasValue::Specifier("./test/a/b".into())))
1788    );
1789  }
1790
1791  #[allow(clippy::single_range_in_vec_init)]
1792  #[test]
1793  fn test_replace_captures() {
1794    assert_eq!(
1795      replace_captures("test/$1/$2", "foo/bar/baz", &vec![4..7, 8..11]),
1796      Cow::Borrowed("test/bar/baz")
1797    );
1798    assert_eq!(
1799      replace_captures("test/$1/$2", "foo/bar/baz", &vec![4..7]),
1800      Cow::Borrowed("test/bar/$2")
1801    );
1802    assert_eq!(
1803      replace_captures("test/$1/$2/$3", "foo/bar/baz", &vec![4..7, 8..11]),
1804      Cow::Borrowed("test/bar/baz/$3")
1805    );
1806    assert_eq!(
1807      replace_captures("test/$1/$2/$", "foo/bar/baz", &vec![4..7, 8..11]),
1808      Cow::Borrowed("test/bar/baz/$")
1809    );
1810    assert_eq!(
1811      replace_captures("te$st/$1/$2", "foo/bar/baz", &vec![4..7, 8..11]),
1812      Cow::Borrowed("te$st/bar/baz")
1813    );
1814  }
1815
1816  #[test]
1817  fn side_effects_none() {
1818    let cache = Cache::default();
1819    let pkg = PackageJson::from_serialized(
1820      cache.get_normalized("/foo/package.json"),
1821      SerializedPackageJson {
1822        name: "foobar".into(),
1823        ..Default::default()
1824      },
1825      &cache,
1826    );
1827
1828    assert!(pkg.has_side_effects(Path::new("/foo/index.js")));
1829    assert!(pkg.has_side_effects(Path::new("/foo/bar/index.js")));
1830    assert!(pkg.has_side_effects(Path::new("/index.js")));
1831  }
1832
1833  #[test]
1834  fn side_effects_bool() {
1835    let cache = Cache::default();
1836    let pkg = PackageJson::from_serialized(
1837      cache.get_normalized("/foo/package.json"),
1838      SerializedPackageJson {
1839        name: "foobar".into(),
1840        side_effects: SideEffects::Boolean(false),
1841        ..Default::default()
1842      },
1843      &cache,
1844    );
1845
1846    assert!(!pkg.has_side_effects(Path::new("/foo/index.js")));
1847    assert!(!pkg.has_side_effects(Path::new("/foo/bar/index.js")));
1848    assert!(pkg.has_side_effects(Path::new("/index.js")));
1849
1850    let pkg = PackageJson {
1851      side_effects: SideEffects::Boolean(true),
1852      ..pkg
1853    };
1854
1855    assert!(pkg.has_side_effects(Path::new("/foo/index.js")));
1856    assert!(pkg.has_side_effects(Path::new("/foo/bar/index.js")));
1857    assert!(pkg.has_side_effects(Path::new("/index.js")));
1858  }
1859
1860  #[test]
1861  fn side_effects_glob() {
1862    let cache = Cache::default();
1863    let pkg = PackageJson::from_serialized(
1864      cache.get_normalized("/foo/package.json"),
1865      SerializedPackageJson {
1866        name: "foobar".into(),
1867        side_effects: SideEffects::String("*.css".into()),
1868        ..Default::default()
1869      },
1870      &cache,
1871    );
1872
1873    assert!(pkg.has_side_effects(Path::new("/foo/a.css")));
1874    assert!(pkg.has_side_effects(Path::new("/foo/bar/baz.css")));
1875    assert!(pkg.has_side_effects(Path::new("/foo/bar/x/baz.css")));
1876    assert!(!pkg.has_side_effects(Path::new("/foo/a.js")));
1877    assert!(!pkg.has_side_effects(Path::new("/foo/bar/baz.js")));
1878    assert!(pkg.has_side_effects(Path::new("/index.js")));
1879
1880    let pkg = PackageJson {
1881      side_effects: SideEffects::String("bar/*.css".into()),
1882      ..pkg
1883    };
1884
1885    assert!(!pkg.has_side_effects(Path::new("/foo/a.css")));
1886    assert!(pkg.has_side_effects(Path::new("/foo/bar/baz.css")));
1887    assert!(!pkg.has_side_effects(Path::new("/foo/bar/x/baz.css")));
1888    assert!(!pkg.has_side_effects(Path::new("/foo/a.js")));
1889    assert!(!pkg.has_side_effects(Path::new("/foo/bar/baz.js")));
1890    assert!(pkg.has_side_effects(Path::new("/index.js")));
1891
1892    let pkg = PackageJson {
1893      side_effects: SideEffects::String("./bar/*.css".into()),
1894      ..pkg
1895    };
1896
1897    assert!(!pkg.has_side_effects(Path::new("/foo/a.css")));
1898    assert!(pkg.has_side_effects(Path::new("/foo/bar/baz.css")));
1899    assert!(!pkg.has_side_effects(Path::new("/foo/bar/x/baz.css")));
1900    assert!(!pkg.has_side_effects(Path::new("/foo/a.js")));
1901    assert!(!pkg.has_side_effects(Path::new("/foo/bar/baz.js")));
1902    assert!(pkg.has_side_effects(Path::new("/index.js")));
1903  }
1904
1905  #[test]
1906  fn side_effects_array() {
1907    let cache = Cache::default();
1908    let pkg = PackageJson::from_serialized(
1909      cache.get_normalized("/foo/package.json"),
1910      SerializedPackageJson {
1911        name: "foobar".into(),
1912        side_effects: SideEffects::Array(vec!["*.css".into(), "*.html".into()]),
1913        ..Default::default()
1914      },
1915      &cache,
1916    );
1917
1918    assert!(pkg.has_side_effects(Path::new("/foo/a.css")));
1919    assert!(pkg.has_side_effects(Path::new("/foo/bar/baz.css")));
1920    assert!(pkg.has_side_effects(Path::new("/foo/bar/x/baz.css")));
1921    assert!(pkg.has_side_effects(Path::new("/foo/a.html")));
1922    assert!(pkg.has_side_effects(Path::new("/foo/bar/baz.html")));
1923    assert!(pkg.has_side_effects(Path::new("/foo/bar/x/baz.html")));
1924    assert!(!pkg.has_side_effects(Path::new("/foo/a.js")));
1925    assert!(!pkg.has_side_effects(Path::new("/foo/bar/baz.js")));
1926    assert!(pkg.has_side_effects(Path::new("/index.js")));
1927  }
1928
1929  #[test]
1930  fn parsing() {
1931    let pkg: SerializedPackageJson = serde_json::from_str(r#"{"type":"script"}"#).unwrap();
1932    assert_eq!(pkg.module_type, ModuleType::CommonJs);
1933    let pkg: SerializedPackageJson = serde_json::from_str(r#"{"name":"foo"}"#).unwrap();
1934    assert_eq!(pkg.module_type, ModuleType::CommonJs);
1935  }
1936}