unrspack_resolver/package_json.rs
1use std::{fmt::Display, path::Path};
2
3use crate::ResolveError;
4
5/// Abstract representation for the contents of a `package.json` file, as well
6/// as the location where it was found.
7///
8/// This representation makes no assumptions regarding how the file was
9/// deserialized.
10#[allow(clippy::missing_errors_doc)] // trait impls should be free to return any typesafe error
11pub trait PackageJson: Sized {
12 /// Returns the path where the `package.json` was found.
13 ///
14 /// Contains the `package.json` filename.
15 ///
16 /// This does not need to be the path where the file is stored on disk.
17 /// See [Self::realpath()].
18 #[must_use]
19 fn path(&self) -> &Path;
20
21 /// Returns the path where the `package.json` file was stored on disk.
22 ///
23 /// Contains the `package.json` filename.
24 ///
25 /// This is the canonicalized version of [Self::path()], where all symbolic
26 /// links are resolved.
27 #[must_use]
28 fn realpath(&self) -> &Path;
29
30 /// Directory to `package.json`.
31 ///
32 /// # Panics
33 ///
34 /// * When the `package.json` path is misconfigured.
35 #[must_use]
36 fn directory(&self) -> &Path;
37
38 /// Name of the package.
39 ///
40 /// The "name" field can be used together with the "exports" field to
41 /// self-reference a package using its name.
42 ///
43 /// <https://nodejs.org/api/packages.html#name>
44 fn name(&self) -> Option<&str>;
45
46 /// Returns the package type, if one is configured in the `package.json`.
47 ///
48 /// <https://nodejs.org/api/packages.html#type>
49 fn r#type(&self) -> Option<PackageType>;
50
51 /// The "main" field defines the entry point of a package when imported by
52 /// name via a node_modules lookup. Its value should be a path.
53 ///
54 /// When a package has an "exports" field, this will take precedence over
55 /// the "main" field when importing the package by name.
56 ///
57 /// Values are dynamically retrieved from [crate::ResolveOptions::main_fields].
58 ///
59 /// <https://nodejs.org/api/packages.html#main>
60 #[must_use]
61 fn main_fields<'a>(&'a self, main_fields: &'a [String]) -> impl Iterator<Item = &'a str> + 'a;
62
63 /// The "exports" field allows defining the entry points of a package when
64 /// imported by name loaded either via a node_modules lookup or a
65 /// self-reference to its own name.
66 ///
67 /// <https://nodejs.org/api/packages.html#exports>
68 #[must_use]
69 fn exports_fields<'a>(
70 &'a self,
71 exports_fields: &'a [Vec<String>],
72 ) -> impl Iterator<Item = impl ImportsExportsEntry<'a>> + 'a;
73
74 /// In addition to the "exports" field, there is a package "imports" field
75 /// to create private mappings that only apply to import specifiers from
76 /// within the package itself.
77 ///
78 /// <https://nodejs.org/api/packages.html#subpath-imports>
79 #[must_use]
80 fn imports_fields<'a>(
81 &'a self,
82 imports_fields: &'a [Vec<String>],
83 ) -> impl Iterator<Item = impl ImportsExportsMap<'a>> + 'a;
84
85 /// Resolves the request string for this `package.json` by looking at the
86 /// "browser" field.
87 ///
88 /// <https://github.com/defunctzombie/package-browser-field-spec>
89 fn resolve_browser_field<'a>(
90 &'a self,
91 path: &Path,
92 request: Option<&str>,
93 alias_fields: &'a [Vec<String>],
94 ) -> Result<Option<&'a str>, ResolveError>;
95}
96
97#[derive(Clone, Copy, Debug, Eq, PartialEq)]
98#[cfg_attr(feature = "fs_cache", derive(serde::Deserialize, serde::Serialize))]
99#[cfg_attr(feature = "fs_cache", serde(rename_all = "lowercase"))]
100pub enum PackageType {
101 CommonJs,
102 Module,
103}
104
105impl Display for PackageType {
106 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
107 match self {
108 Self::CommonJs => f.write_str("commonjs"),
109 Self::Module => f.write_str("module"),
110 }
111 }
112}
113
114/// Trait used for representing entries in the `imports` and `exports` fields
115/// without allocation.
116pub trait ImportsExportsEntry<'a>: Clone + Sized {
117 type Array: ImportsExportsArray<'a, Entry = Self>;
118 type Map: ImportsExportsMap<'a, Entry = Self>;
119
120 fn kind(&self) -> ImportsExportsKind;
121
122 fn as_string(&self) -> Option<&'a str>;
123 fn as_array(&self) -> Option<Self::Array>;
124 fn as_map(&self) -> Option<Self::Map>;
125}
126
127/// Trait used for representing array values in the `imports` and `exports`
128/// fields without allocation.
129pub trait ImportsExportsArray<'a>: Clone + Sized {
130 type Entry: ImportsExportsEntry<'a, Array = Self>;
131
132 fn is_empty(&self) -> bool {
133 self.len() == 0
134 }
135
136 fn len(&self) -> usize;
137 fn iter(&self) -> impl Iterator<Item = Self::Entry>;
138}
139
140/// Trait used for representing map (object) values in the `imports` and
141/// `exports` fields without allocation.
142pub trait ImportsExportsMap<'a>: Clone + Sized {
143 type Entry: ImportsExportsEntry<'a, Map = Self>;
144
145 fn get(&self, key: &str) -> Option<Self::Entry>;
146 fn keys(&self) -> impl Iterator<Item = &'a str>;
147 fn iter(&self) -> impl Iterator<Item = (&'a str, Self::Entry)>;
148}
149
150#[derive(Clone, Copy, Debug, Eq, PartialEq)]
151pub enum ImportsExportsKind {
152 String,
153 Array,
154 Map,
155 Invalid,
156}