From 92d75f8b814b52ae978113cea9eeb9fe6494f32a Mon Sep 17 00:00:00 2001 From: David Sherret Date: Fri, 21 Jun 2024 15:32:50 -0400 Subject: [PATCH 1/6] feat: resolve_import --- rs-lib/src/lib.rs | 75 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 75 insertions(+) diff --git a/rs-lib/src/lib.rs b/rs-lib/src/lib.rs index 646fc57..448b59d 100644 --- a/rs-lib/src/lib.rs +++ b/rs-lib/src/lib.rs @@ -1118,6 +1118,8 @@ fn resolve_imports_match( return Ok(Some(url)); } + log::debug!("Backtrace: {}", std::backtrace::Backtrace::force_capture()); + #[cfg(feature = "logging")] log::debug!( "Specifier {:?} was not mapped in import map.", @@ -1127,6 +1129,79 @@ fn resolve_imports_match( Ok(None) } +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum ResolveImportError { + InvalidUrl(url::ParseError), + ImportPrefixMissing { + specifier: String, + referrer: Option, + }, +} + + +impl Error for ResolveImportError { + fn source(&self) -> Option<&(dyn Error + 'static)> { + match self { + Self::InvalidUrl(ref err) => Some(err), + _ => None, + } + } +} + +impl fmt::Display for ResolveImportError { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match self { + Self::InvalidUrl(ref err) => write!(f, "invalid URL: {err}"), + Self::ImportPrefixMissing { specifier, .. } => write!( + f, + "Relative import path \"{specifier}\" not prefixed with / or ./ or ../", + ), + } + } +} + +/// Given a specifier string and a referring module specifier, try to resolve +/// the target module specifier, erroring if it cannot be resolved. +pub fn resolve_import( + specifier: &str, + referrer: &Url, +) -> Result { + match Url::parse(specifier) { + // 1. Apply the URL parser to specifier. + // If the result is not failure, return he result. + Ok(url) => Ok(url), + + // 2. If specifier does not start with the character U+002F SOLIDUS (/), + // the two-character sequence U+002E FULL STOP, U+002F SOLIDUS (./), + // or the three-character sequence U+002E FULL STOP, U+002E FULL STOP, + // U+002F SOLIDUS (../), return failure. + Err(url::ParseError::RelativeUrlWithoutBase) + if !(specifier.starts_with('/') + || specifier.starts_with("./") + || specifier.starts_with("../")) => + { + Err(ResolveImportError::ImportPrefixMissing { + specifier: specifier.to_string(), + referrer: Some(referrer.clone()), + }) + } + + // 3. Return the result of applying the URL parser to specifier with base + // URL as the base URL. + Err(url::ParseError::RelativeUrlWithoutBase) => { + referrer + .join(specifier) + .map_err(ResolveImportError::InvalidUrl) + } + + // If parsing the specifier as a URL failed for a different reason than + // it being relative, always return the original error. We don't want to + // return `ImportPrefixMissing` or `InvalidBaseUrl` if the real + // problem lies somewhere else. + Err(err) => Err(ResolveImportError::InvalidUrl(err)), + } +} + #[cfg(test)] mod test { use super::*; From 9faff281c7705f3083a63d21210b3fee31e4f112 Mon Sep 17 00:00:00 2001 From: David Sherret Date: Fri, 21 Jun 2024 15:34:30 -0400 Subject: [PATCH 2/6] update docs --- rs-lib/src/lib.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/rs-lib/src/lib.rs b/rs-lib/src/lib.rs index 448b59d..80c2f03 100644 --- a/rs-lib/src/lib.rs +++ b/rs-lib/src/lib.rs @@ -1162,6 +1162,9 @@ impl fmt::Display for ResolveImportError { /// Given a specifier string and a referring module specifier, try to resolve /// the target module specifier, erroring if it cannot be resolved. +/// +/// This function is useful for resolving specifiers in situations without an +/// import map. pub fn resolve_import( specifier: &str, referrer: &Url, From 7e79a3d791658dd915a535d68bc68423e1a6fc86 Mon Sep 17 00:00:00 2001 From: David Sherret Date: Fri, 21 Jun 2024 15:37:27 -0400 Subject: [PATCH 3/6] format --- rs-lib/src/lib.rs | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/rs-lib/src/lib.rs b/rs-lib/src/lib.rs index 80c2f03..7e51d2d 100644 --- a/rs-lib/src/lib.rs +++ b/rs-lib/src/lib.rs @@ -1138,7 +1138,6 @@ pub enum ResolveImportError { }, } - impl Error for ResolveImportError { fn source(&self) -> Option<&(dyn Error + 'static)> { match self { @@ -1191,11 +1190,9 @@ pub fn resolve_import( // 3. Return the result of applying the URL parser to specifier with base // URL as the base URL. - Err(url::ParseError::RelativeUrlWithoutBase) => { - referrer - .join(specifier) - .map_err(ResolveImportError::InvalidUrl) - } + Err(url::ParseError::RelativeUrlWithoutBase) => referrer + .join(specifier) + .map_err(ResolveImportError::InvalidUrl), // If parsing the specifier as a URL failed for a different reason than // it being relative, always return the original error. We don't want to From cb46bf99da42ea2e8a58fe8994efbfc8dfaafc91 Mon Sep 17 00:00:00 2001 From: David Sherret Date: Fri, 21 Jun 2024 20:06:26 -0400 Subject: [PATCH 4/6] make it a module instead --- rs-lib/src/lib.rs | 73 +------------------------------------ rs-lib/src/specifier.rs | 79 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 80 insertions(+), 72 deletions(-) create mode 100644 rs-lib/src/specifier.rs diff --git a/rs-lib/src/lib.rs b/rs-lib/src/lib.rs index 7e51d2d..1c9561d 100644 --- a/rs-lib/src/lib.rs +++ b/rs-lib/src/lib.rs @@ -12,6 +12,7 @@ use url::Url; #[cfg(feature = "ext")] pub mod ext; +pub mod specifier; #[derive(Debug, Clone, PartialEq, Eq)] pub enum ImportMapDiagnostic { @@ -1129,78 +1130,6 @@ fn resolve_imports_match( Ok(None) } -#[derive(Debug, Clone, PartialEq, Eq)] -pub enum ResolveImportError { - InvalidUrl(url::ParseError), - ImportPrefixMissing { - specifier: String, - referrer: Option, - }, -} - -impl Error for ResolveImportError { - fn source(&self) -> Option<&(dyn Error + 'static)> { - match self { - Self::InvalidUrl(ref err) => Some(err), - _ => None, - } - } -} - -impl fmt::Display for ResolveImportError { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match self { - Self::InvalidUrl(ref err) => write!(f, "invalid URL: {err}"), - Self::ImportPrefixMissing { specifier, .. } => write!( - f, - "Relative import path \"{specifier}\" not prefixed with / or ./ or ../", - ), - } - } -} - -/// Given a specifier string and a referring module specifier, try to resolve -/// the target module specifier, erroring if it cannot be resolved. -/// -/// This function is useful for resolving specifiers in situations without an -/// import map. -pub fn resolve_import( - specifier: &str, - referrer: &Url, -) -> Result { - match Url::parse(specifier) { - // 1. Apply the URL parser to specifier. - // If the result is not failure, return he result. - Ok(url) => Ok(url), - - // 2. If specifier does not start with the character U+002F SOLIDUS (/), - // the two-character sequence U+002E FULL STOP, U+002F SOLIDUS (./), - // or the three-character sequence U+002E FULL STOP, U+002E FULL STOP, - // U+002F SOLIDUS (../), return failure. - Err(url::ParseError::RelativeUrlWithoutBase) - if !(specifier.starts_with('/') - || specifier.starts_with("./") - || specifier.starts_with("../")) => - { - Err(ResolveImportError::ImportPrefixMissing { - specifier: specifier.to_string(), - referrer: Some(referrer.clone()), - }) - } - - // 3. Return the result of applying the URL parser to specifier with base - // URL as the base URL. - Err(url::ParseError::RelativeUrlWithoutBase) => referrer - .join(specifier) - .map_err(ResolveImportError::InvalidUrl), - - // If parsing the specifier as a URL failed for a different reason than - // it being relative, always return the original error. We don't want to - // return `ImportPrefixMissing` or `InvalidBaseUrl` if the real - // problem lies somewhere else. - Err(err) => Err(ResolveImportError::InvalidUrl(err)), - } -} #[cfg(test)] mod test { diff --git a/rs-lib/src/specifier.rs b/rs-lib/src/specifier.rs new file mode 100644 index 0000000..8c75e19 --- /dev/null +++ b/rs-lib/src/specifier.rs @@ -0,0 +1,79 @@ +// Copyright 2018-2024 the Deno authors. MIT license. + +use std::error::Error; +use std::fmt; + +use url::Url; + +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum SpecifierError { + InvalidUrl(url::ParseError), + ImportPrefixMissing { + specifier: String, + referrer: Option, + }, +} + +impl Error for SpecifierError { + fn source(&self) -> Option<&(dyn Error + 'static)> { + match self { + Self::InvalidUrl(ref err) => Some(err), + _ => None, + } + } +} + +impl fmt::Display for SpecifierError { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match self { + Self::InvalidUrl(ref err) => write!(f, "invalid URL: {err}"), + Self::ImportPrefixMissing { specifier, .. } => write!( + f, + "Relative import path \"{specifier}\" not prefixed with / or ./ or ../", + ), + } + } +} + +/// Given a specifier string and a referring module specifier, try to resolve +/// the target module specifier, erroring if it cannot be resolved. +/// +/// This function is useful for resolving specifiers in situations without an +/// import map. +pub fn resolve_import( + specifier: &str, + referrer: &Url, +) -> Result { + match Url::parse(specifier) { + // 1. Apply the URL parser to specifier. + // If the result is not failure, return he result. + Ok(url) => Ok(url), + + // 2. If specifier does not start with the character U+002F SOLIDUS (/), + // the two-character sequence U+002E FULL STOP, U+002F SOLIDUS (./), + // or the three-character sequence U+002E FULL STOP, U+002E FULL STOP, + // U+002F SOLIDUS (../), return failure. + Err(url::ParseError::RelativeUrlWithoutBase) + if !(specifier.starts_with('/') + || specifier.starts_with("./") + || specifier.starts_with("../")) => + { + Err(SpecifierError::ImportPrefixMissing { + specifier: specifier.to_string(), + referrer: Some(referrer.clone()), + }) + } + + // 3. Return the result of applying the URL parser to specifier with base + // URL as the base URL. + Err(url::ParseError::RelativeUrlWithoutBase) => { + referrer.join(specifier).map_err(SpecifierError::InvalidUrl) + } + + // If parsing the specifier as a URL failed for a different reason than + // it being relative, always return the original error. We don't want to + // return `ImportPrefixMissing` or `InvalidBaseUrl` if the real + // problem lies somewhere else. + Err(err) => Err(SpecifierError::InvalidUrl(err)), + } +} \ No newline at end of file From f5c16ce6c2829e27347192bcb2a12c0e41be807f Mon Sep 17 00:00:00 2001 From: David Sherret Date: Fri, 21 Jun 2024 20:18:17 -0400 Subject: [PATCH 5/6] format as per usual --- rs-lib/src/lib.rs | 1 - rs-lib/src/specifier.rs | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/rs-lib/src/lib.rs b/rs-lib/src/lib.rs index 1c9561d..1d01513 100644 --- a/rs-lib/src/lib.rs +++ b/rs-lib/src/lib.rs @@ -1130,7 +1130,6 @@ fn resolve_imports_match( Ok(None) } - #[cfg(test)] mod test { use super::*; diff --git a/rs-lib/src/specifier.rs b/rs-lib/src/specifier.rs index 8c75e19..866f211 100644 --- a/rs-lib/src/specifier.rs +++ b/rs-lib/src/specifier.rs @@ -76,4 +76,4 @@ pub fn resolve_import( // problem lies somewhere else. Err(err) => Err(SpecifierError::InvalidUrl(err)), } -} \ No newline at end of file +} From c7b6a21900e9892260a98699b985cf6cc9bd08a5 Mon Sep 17 00:00:00 2001 From: David Sherret Date: Sun, 23 Jun 2024 22:30:09 -0400 Subject: [PATCH 6/6] Apply suggestions from code review --- rs-lib/src/lib.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/rs-lib/src/lib.rs b/rs-lib/src/lib.rs index 1d01513..9f65691 100644 --- a/rs-lib/src/lib.rs +++ b/rs-lib/src/lib.rs @@ -1119,8 +1119,6 @@ fn resolve_imports_match( return Ok(Some(url)); } - log::debug!("Backtrace: {}", std::backtrace::Backtrace::force_capture()); - #[cfg(feature = "logging")] log::debug!( "Specifier {:?} was not mapped in import map.",