|
1 | 1 | // Copyright 2018-2025 the Deno authors. MIT license. |
2 | 2 |
|
3 | | -use std::collections::HashMap; |
4 | | -use std::path::PathBuf; |
5 | | -use std::sync::Arc; |
6 | | - |
7 | | -use deno_cache_dir::file_fetcher::CacheSetting; |
8 | | -use deno_cache_dir::file_fetcher::FetchLocalOptions; |
9 | | -use deno_cache_dir::file_fetcher::FetchNoFollowErrorKind; |
10 | | -use deno_cache_dir::file_fetcher::FileOrRedirect; |
11 | | -use deno_core::futures::FutureExt; |
12 | | -use deno_core::ModuleSpecifier; |
13 | | -use deno_graph::source::CacheInfo; |
14 | | -use deno_graph::source::LoadFuture; |
15 | | -use deno_graph::source::LoadResponse; |
16 | | -use deno_graph::source::Loader; |
17 | | -use deno_resolver::npm::DenoInNpmPackageChecker; |
18 | | -use deno_runtime::deno_permissions::PermissionsContainer; |
19 | | -use node_resolver::InNpmPackageChecker; |
20 | | - |
21 | | -use crate::args::jsr_url; |
22 | | -use crate::file_fetcher::CliFetchNoFollowErrorKind; |
23 | | -use crate::file_fetcher::CliFileFetcher; |
24 | | -use crate::file_fetcher::FetchNoFollowOptions; |
25 | | -use crate::file_fetcher::FetchPermissionsOptionRef; |
26 | | -use crate::sys::CliSys; |
27 | | - |
28 | 3 | mod cache_db; |
29 | 4 | mod caches; |
30 | 5 | mod check; |
@@ -55,232 +30,8 @@ pub use node::NodeAnalysisCache; |
55 | 30 | pub use parsed_source::LazyGraphSourceParser; |
56 | 31 | pub use parsed_source::ParsedSourceCache; |
57 | 32 |
|
| 33 | +use crate::sys::CliSys; |
| 34 | + |
58 | 35 | pub type GlobalHttpCache = deno_cache_dir::GlobalHttpCache<CliSys>; |
59 | 36 | pub type LocalLspHttpCache = deno_cache_dir::LocalLspHttpCache<CliSys>; |
60 | 37 | pub use deno_cache_dir::HttpCache; |
61 | | -use deno_error::JsErrorBox; |
62 | | - |
63 | | -pub struct FetchCacherOptions { |
64 | | - pub file_header_overrides: HashMap<ModuleSpecifier, HashMap<String, String>>, |
65 | | - pub permissions: PermissionsContainer, |
66 | | - /// If we're publishing for `deno publish`. |
67 | | - pub is_deno_publish: bool, |
68 | | -} |
69 | | - |
70 | | -/// A "wrapper" for the FileFetcher and DiskCache for the Deno CLI that provides |
71 | | -/// a concise interface to the DENO_DIR when building module graphs. |
72 | | -pub struct FetchCacher { |
73 | | - pub file_header_overrides: HashMap<ModuleSpecifier, HashMap<String, String>>, |
74 | | - file_fetcher: Arc<CliFileFetcher>, |
75 | | - global_http_cache: Arc<GlobalHttpCache>, |
76 | | - in_npm_pkg_checker: DenoInNpmPackageChecker, |
77 | | - permissions: PermissionsContainer, |
78 | | - sys: CliSys, |
79 | | - is_deno_publish: bool, |
80 | | - cache_info_enabled: bool, |
81 | | -} |
82 | | - |
83 | | -impl FetchCacher { |
84 | | - pub fn new( |
85 | | - file_fetcher: Arc<CliFileFetcher>, |
86 | | - global_http_cache: Arc<GlobalHttpCache>, |
87 | | - in_npm_pkg_checker: DenoInNpmPackageChecker, |
88 | | - sys: CliSys, |
89 | | - options: FetchCacherOptions, |
90 | | - ) -> Self { |
91 | | - Self { |
92 | | - file_fetcher, |
93 | | - global_http_cache, |
94 | | - in_npm_pkg_checker, |
95 | | - sys, |
96 | | - file_header_overrides: options.file_header_overrides, |
97 | | - permissions: options.permissions, |
98 | | - is_deno_publish: options.is_deno_publish, |
99 | | - cache_info_enabled: false, |
100 | | - } |
101 | | - } |
102 | | - |
103 | | - /// The cache information takes a bit of time to fetch and it's |
104 | | - /// not always necessary. It should only be enabled for deno info. |
105 | | - pub fn enable_loading_cache_info(&mut self) { |
106 | | - self.cache_info_enabled = true; |
107 | | - } |
108 | | - |
109 | | - /// Only use this for `deno info`. |
110 | | - fn get_local_path(&self, specifier: &ModuleSpecifier) -> Option<PathBuf> { |
111 | | - // TODO(@kitsonk) fix when deno_graph does not query cache for synthetic |
112 | | - // modules |
113 | | - if specifier.scheme() == "flags" { |
114 | | - None |
115 | | - } else if specifier.scheme() == "file" { |
116 | | - specifier.to_file_path().ok() |
117 | | - } else { |
118 | | - self.global_http_cache.local_path_for_url(specifier).ok() |
119 | | - } |
120 | | - } |
121 | | -} |
122 | | - |
123 | | -impl Loader for FetchCacher { |
124 | | - fn get_cache_info(&self, specifier: &ModuleSpecifier) -> Option<CacheInfo> { |
125 | | - if !self.cache_info_enabled { |
126 | | - return None; |
127 | | - } |
128 | | - |
129 | | - #[allow(deprecated)] |
130 | | - let local = self.get_local_path(specifier)?; |
131 | | - if local.is_file() { |
132 | | - Some(CacheInfo { local: Some(local) }) |
133 | | - } else { |
134 | | - None |
135 | | - } |
136 | | - } |
137 | | - |
138 | | - fn load( |
139 | | - &self, |
140 | | - specifier: &ModuleSpecifier, |
141 | | - options: deno_graph::source::LoadOptions, |
142 | | - ) -> LoadFuture { |
143 | | - use deno_graph::source::CacheSetting as LoaderCacheSetting; |
144 | | - |
145 | | - if specifier.scheme() == "file" |
146 | | - && specifier.path().contains("/node_modules/") |
147 | | - { |
148 | | - // The specifier might be in a completely different symlinked tree than |
149 | | - // what the node_modules url is in (ex. `/my-project-1/node_modules` |
150 | | - // symlinked to `/my-project-2/node_modules`), so first we checked if the path |
151 | | - // is in a node_modules dir to avoid needlessly canonicalizing, then now compare |
152 | | - // against the canonicalized specifier. |
153 | | - let specifier = node_resolver::resolve_specifier_into_node_modules( |
154 | | - &self.sys, specifier, |
155 | | - ); |
156 | | - if self.in_npm_pkg_checker.in_npm_package(&specifier) { |
157 | | - return Box::pin(std::future::ready(Ok(Some( |
158 | | - LoadResponse::External { specifier }, |
159 | | - )))); |
160 | | - } |
161 | | - } |
162 | | - |
163 | | - if self.is_deno_publish |
164 | | - && matches!(specifier.scheme(), "http" | "https") |
165 | | - && !specifier.as_str().starts_with(jsr_url().as_str()) |
166 | | - { |
167 | | - // mark non-JSR remote modules as external so we don't need --allow-import |
168 | | - // permissions as these will error out later when publishing |
169 | | - return Box::pin(std::future::ready(Ok(Some(LoadResponse::External { |
170 | | - specifier: specifier.clone(), |
171 | | - })))); |
172 | | - } |
173 | | - |
174 | | - let file_fetcher = self.file_fetcher.clone(); |
175 | | - let file_header_overrides = self.file_header_overrides.clone(); |
176 | | - let permissions = self.permissions.clone(); |
177 | | - let specifier = specifier.clone(); |
178 | | - let is_statically_analyzable = !options.was_dynamic_root; |
179 | | - |
180 | | - async move { |
181 | | - let maybe_cache_setting = match options.cache_setting { |
182 | | - LoaderCacheSetting::Use => None, |
183 | | - LoaderCacheSetting::Reload => { |
184 | | - if matches!(file_fetcher.cache_setting(), CacheSetting::Only) { |
185 | | - return Err(deno_graph::source::LoadError::Other(Arc::new(JsErrorBox::generic( |
186 | | - "Could not resolve version constraint using only cached data. Try running again without --cached-only" |
187 | | - )))); |
188 | | - } |
189 | | - Some(CacheSetting::ReloadAll) |
190 | | - } |
191 | | - LoaderCacheSetting::Only => Some(CacheSetting::Only), |
192 | | - }; |
193 | | - file_fetcher |
194 | | - .fetch_no_follow( |
195 | | - &specifier, |
196 | | - FetchPermissionsOptionRef::Restricted(&permissions, |
197 | | - if is_statically_analyzable { |
198 | | - deno_runtime::deno_permissions::CheckSpecifierKind::Static |
199 | | - } else { |
200 | | - deno_runtime::deno_permissions::CheckSpecifierKind::Dynamic |
201 | | - }), |
202 | | - FetchNoFollowOptions { |
203 | | - local: FetchLocalOptions { |
204 | | - // only include the mtime in dynamic branches because we only |
205 | | - // need to know about it then in order to tell whether to reload |
206 | | - // or not |
207 | | - include_mtime: options.in_dynamic_branch, |
208 | | - }, |
209 | | - maybe_auth: None, |
210 | | - maybe_accept: None, |
211 | | - maybe_cache_setting: maybe_cache_setting.as_ref(), |
212 | | - maybe_checksum: options.maybe_checksum.as_ref(), |
213 | | - }) |
214 | | - .await |
215 | | - .map(|file_or_redirect| { |
216 | | - match file_or_redirect { |
217 | | - FileOrRedirect::File(file) => { |
218 | | - let maybe_headers = |
219 | | - match (file.maybe_headers, file_header_overrides.get(&specifier)) { |
220 | | - (Some(headers), Some(overrides)) => { |
221 | | - Some(headers.into_iter().chain(overrides.clone()).collect()) |
222 | | - } |
223 | | - (Some(headers), None) => Some(headers), |
224 | | - (None, Some(overrides)) => Some(overrides.clone()), |
225 | | - (None, None) => None, |
226 | | - }; |
227 | | - Ok(Some(LoadResponse::Module { |
228 | | - specifier: file.url, |
229 | | - maybe_headers, |
230 | | - mtime: file.mtime, |
231 | | - content: file.source, |
232 | | - })) |
233 | | - }, |
234 | | - FileOrRedirect::Redirect(redirect_specifier) => { |
235 | | - Ok(Some(LoadResponse::Redirect { |
236 | | - specifier: redirect_specifier, |
237 | | - })) |
238 | | - }, |
239 | | - } |
240 | | - }) |
241 | | - .unwrap_or_else(|err| { |
242 | | - let err = err.into_kind(); |
243 | | - match err { |
244 | | - CliFetchNoFollowErrorKind::FetchNoFollow(err) => { |
245 | | - let err = err.into_kind(); |
246 | | - match err { |
247 | | - FetchNoFollowErrorKind::NotFound(_) => Ok(None), |
248 | | - FetchNoFollowErrorKind::UrlToFilePath { .. } | |
249 | | - FetchNoFollowErrorKind::ReadingBlobUrl { .. } | |
250 | | - FetchNoFollowErrorKind::ReadingFile { .. } | |
251 | | - FetchNoFollowErrorKind::FetchingRemote { .. } | |
252 | | - FetchNoFollowErrorKind::ClientError { .. } | |
253 | | - FetchNoFollowErrorKind::NoRemote { .. } | |
254 | | - FetchNoFollowErrorKind::DataUrlDecode { .. } | |
255 | | - FetchNoFollowErrorKind::RedirectResolution { .. } | |
256 | | - FetchNoFollowErrorKind::CacheRead { .. } | |
257 | | - FetchNoFollowErrorKind::CacheSave { .. } | |
258 | | - FetchNoFollowErrorKind::UnsupportedScheme { .. } | |
259 | | - FetchNoFollowErrorKind::RedirectHeaderParse { .. } | |
260 | | - FetchNoFollowErrorKind::InvalidHeader { .. } => Err(deno_graph::source::LoadError::Other(Arc::new(JsErrorBox::from_err(err)))), |
261 | | - FetchNoFollowErrorKind::NotCached { .. } => { |
262 | | - if options.cache_setting == LoaderCacheSetting::Only { |
263 | | - Ok(None) |
264 | | - } else { |
265 | | - Err(deno_graph::source::LoadError::Other(Arc::new(JsErrorBox::from_err(err)))) |
266 | | - } |
267 | | - }, |
268 | | - FetchNoFollowErrorKind::ChecksumIntegrity(err) => { |
269 | | - // convert to the equivalent deno_graph error so that it |
270 | | - // enhances it if this is passed to deno_graph |
271 | | - Err( |
272 | | - deno_graph::source::LoadError::ChecksumIntegrity(deno_graph::source::ChecksumIntegrityError { |
273 | | - actual: err.actual, |
274 | | - expected: err.expected, |
275 | | - }), |
276 | | - ) |
277 | | - } |
278 | | - } |
279 | | - }, |
280 | | - CliFetchNoFollowErrorKind::PermissionCheck(permission_check_error) => Err(deno_graph::source::LoadError::Other(Arc::new(JsErrorBox::from_err(permission_check_error)))), |
281 | | - } |
282 | | - }) |
283 | | - } |
284 | | - .boxed_local() |
285 | | - } |
286 | | -} |
0 commit comments