summaryrefslogtreecommitdiff
path: root/core/module_specifier.rs
diff options
context:
space:
mode:
Diffstat (limited to 'core/module_specifier.rs')
-rw-r--r--core/module_specifier.rs282
1 files changed, 114 insertions, 168 deletions
diff --git a/core/module_specifier.rs b/core/module_specifier.rs
index a9ce57099..dc6b4d6bf 100644
--- a/core/module_specifier.rs
+++ b/core/module_specifier.rs
@@ -1,9 +1,6 @@
// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license.
use crate::normalize_path;
-use serde::de;
-use serde::Deserialize;
-use serde::Deserializer;
use std::env::current_dir;
use std::error::Error;
use std::fmt;
@@ -11,6 +8,8 @@ use std::path::PathBuf;
use url::ParseError;
use url::Url;
+pub const DUMMY_SPECIFIER: &str = "<unknown>";
+
/// Error indicating the reason resolving a module specifier failed.
#[derive(Clone, Debug, Eq, PartialEq)]
pub enum ModuleResolutionError {
@@ -51,174 +50,126 @@ impl fmt::Display for ModuleResolutionError {
}
}
-#[derive(
- Debug, Clone, Eq, Hash, PartialEq, serde::Serialize, Ord, PartialOrd,
-)]
/// Resolved module specifier
-pub struct ModuleSpecifier(Url);
-
-impl ModuleSpecifier {
- fn is_dummy_specifier(specifier: &str) -> bool {
- specifier == "<unknown>"
- }
-
- pub fn as_url(&self) -> &Url {
- &self.0
- }
-
- pub fn as_str(&self) -> &str {
- self.0.as_str()
- }
-
- /// Resolves module using this algorithm:
- /// https://html.spec.whatwg.org/multipage/webappapis.html#resolve-a-module-specifier
- pub fn resolve_import(
- specifier: &str,
- base: &str,
- ) -> Result<ModuleSpecifier, ModuleResolutionError> {
- let url = match Url::parse(specifier) {
- // 1. Apply the URL parser to specifier.
- // If the result is not failure, return he result.
- Ok(url) => 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(ParseError::RelativeUrlWithoutBase)
- if !(specifier.starts_with('/')
- || specifier.starts_with("./")
- || specifier.starts_with("../")) =>
- {
- let maybe_referrer = if base.is_empty() {
- None
- } else {
- Some(base.to_string())
- };
- return Err(ImportPrefixMissing(specifier.to_string(), maybe_referrer));
- }
-
- // 3. Return the result of applying the URL parser to specifier with base
- // URL as the base URL.
- Err(ParseError::RelativeUrlWithoutBase) => {
- let base = if ModuleSpecifier::is_dummy_specifier(base) {
- // Handle <unknown> case, happening under e.g. repl.
- // Use CWD for such case.
-
- // Forcefully join base to current dir.
- // Otherwise, later joining in Url would be interpreted in
- // the parent directory (appending trailing slash does not work)
- let path = current_dir().unwrap().join(base);
- Url::from_file_path(path).unwrap()
- } else {
- Url::parse(base).map_err(InvalidBaseUrl)?
- };
- base.join(&specifier).map_err(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) => return Err(InvalidUrl(err)),
- };
-
- Ok(ModuleSpecifier(url))
- }
-
- /// Converts a string representing an absolute URL into a ModuleSpecifier.
- pub fn resolve_url(
- url_str: &str,
- ) -> Result<ModuleSpecifier, ModuleResolutionError> {
- Url::parse(url_str)
- .map(ModuleSpecifier)
- .map_err(ModuleResolutionError::InvalidUrl)
- }
+pub type ModuleSpecifier = Url;
+
+/// Resolves module using this algorithm:
+/// https://html.spec.whatwg.org/multipage/webappapis.html#resolve-a-module-specifier
+pub fn resolve_import(
+ specifier: &str,
+ base: &str,
+) -> Result<ModuleSpecifier, ModuleResolutionError> {
+ let url = match Url::parse(specifier) {
+ // 1. Apply the URL parser to specifier.
+ // If the result is not failure, return he result.
+ Ok(url) => 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(ParseError::RelativeUrlWithoutBase)
+ if !(specifier.starts_with('/')
+ || specifier.starts_with("./")
+ || specifier.starts_with("../")) =>
+ {
+ let maybe_referrer = if base.is_empty() {
+ None
+ } else {
+ Some(base.to_string())
+ };
+ return Err(ImportPrefixMissing(specifier.to_string(), maybe_referrer));
+ }
- /// Takes a string representing either an absolute URL or a file path,
- /// as it may be passed to deno as a command line argument.
- /// The string is interpreted as a URL if it starts with a valid URI scheme,
- /// e.g. 'http:' or 'file:' or 'git+ssh:'. If not, it's interpreted as a
- /// file path; if it is a relative path it's resolved relative to the current
- /// working directory.
- pub fn resolve_url_or_path(
- specifier: &str,
- ) -> Result<ModuleSpecifier, ModuleResolutionError> {
- if Self::specifier_has_uri_scheme(specifier) {
- Self::resolve_url(specifier)
- } else {
- Self::resolve_path(specifier)
+ // 3. Return the result of applying the URL parser to specifier with base
+ // URL as the base URL.
+ Err(ParseError::RelativeUrlWithoutBase) => {
+ let base = if base == DUMMY_SPECIFIER {
+ // Handle <unknown> case, happening under e.g. repl.
+ // Use CWD for such case.
+
+ // Forcefully join base to current dir.
+ // Otherwise, later joining in Url would be interpreted in
+ // the parent directory (appending trailing slash does not work)
+ let path = current_dir().unwrap().join(base);
+ Url::from_file_path(path).unwrap()
+ } else {
+ Url::parse(base).map_err(InvalidBaseUrl)?
+ };
+ base.join(&specifier).map_err(InvalidUrl)?
}
- }
- /// Converts a string representing a relative or absolute path into a
- /// ModuleSpecifier. A relative path is considered relative to the current
- /// working directory.
- pub fn resolve_path(
- path_str: &str,
- ) -> Result<ModuleSpecifier, ModuleResolutionError> {
- let path = current_dir().unwrap().join(path_str);
- let path = normalize_path(&path);
- Url::from_file_path(path.clone())
- .map(ModuleSpecifier)
- .map_err(|()| ModuleResolutionError::InvalidPath(path))
- }
+ // 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) => return Err(InvalidUrl(err)),
+ };
- /// Returns true if the input string starts with a sequence of characters
- /// that could be a valid URI scheme, like 'https:', 'git+ssh:' or 'data:'.
- ///
- /// According to RFC 3986 (https://tools.ietf.org/html/rfc3986#section-3.1),
- /// a valid scheme has the following format:
- /// scheme = ALPHA *( ALPHA / DIGIT / "+" / "-" / "." )
- ///
- /// We additionally require the scheme to be at least 2 characters long,
- /// because otherwise a windows path like c:/foo would be treated as a URL,
- /// while no schemes with a one-letter name actually exist.
- fn specifier_has_uri_scheme(specifier: &str) -> bool {
- let mut chars = specifier.chars();
- let mut len = 0usize;
- // THe first character must be a letter.
- match chars.next() {
- Some(c) if c.is_ascii_alphabetic() => len += 1,
- _ => return false,
- }
- // Second and following characters must be either a letter, number,
- // plus sign, minus sign, or dot.
- loop {
- match chars.next() {
- Some(c) if c.is_ascii_alphanumeric() || "+-.".contains(c) => len += 1,
- Some(':') if len >= 2 => return true,
- _ => return false,
- }
- }
- }
+ Ok(url)
}
-impl fmt::Display for ModuleSpecifier {
- fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
- self.0.fmt(f)
- }
+/// Converts a string representing an absolute URL into a ModuleSpecifier.
+pub fn resolve_url(
+ url_str: &str,
+) -> Result<ModuleSpecifier, ModuleResolutionError> {
+ Url::parse(url_str).map_err(ModuleResolutionError::InvalidUrl)
}
-impl From<Url> for ModuleSpecifier {
- fn from(url: Url) -> Self {
- ModuleSpecifier(url)
+/// Takes a string representing either an absolute URL or a file path,
+/// as it may be passed to deno as a command line argument.
+/// The string is interpreted as a URL if it starts with a valid URI scheme,
+/// e.g. 'http:' or 'file:' or 'git+ssh:'. If not, it's interpreted as a
+/// file path; if it is a relative path it's resolved relative to the current
+/// working directory.
+pub fn resolve_url_or_path(
+ specifier: &str,
+) -> Result<ModuleSpecifier, ModuleResolutionError> {
+ if specifier_has_uri_scheme(specifier) {
+ resolve_url(specifier)
+ } else {
+ resolve_path(specifier)
}
}
-impl PartialEq<String> for ModuleSpecifier {
- fn eq(&self, other: &String) -> bool {
- &self.to_string() == other
- }
+/// Converts a string representing a relative or absolute path into a
+/// ModuleSpecifier. A relative path is considered relative to the current
+/// working directory.
+pub fn resolve_path(
+ path_str: &str,
+) -> Result<ModuleSpecifier, ModuleResolutionError> {
+ let path = current_dir().unwrap().join(path_str);
+ let path = normalize_path(&path);
+ Url::from_file_path(path.clone())
+ .map_err(|()| ModuleResolutionError::InvalidPath(path))
}
-impl<'de> Deserialize<'de> for ModuleSpecifier {
- fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
- where
- D: Deserializer<'de>,
- {
- let url_str: String = Deserialize::deserialize(deserializer)?;
- ModuleSpecifier::resolve_url(&url_str).map_err(de::Error::custom)
+/// Returns true if the input string starts with a sequence of characters
+/// that could be a valid URI scheme, like 'https:', 'git+ssh:' or 'data:'.
+///
+/// According to RFC 3986 (https://tools.ietf.org/html/rfc3986#section-3.1),
+/// a valid scheme has the following format:
+/// scheme = ALPHA *( ALPHA / DIGIT / "+" / "-" / "." )
+///
+/// We additionally require the scheme to be at least 2 characters long,
+/// because otherwise a windows path like c:/foo would be treated as a URL,
+/// while no schemes with a one-letter name actually exist.
+fn specifier_has_uri_scheme(specifier: &str) -> bool {
+ let mut chars = specifier.chars();
+ let mut len = 0usize;
+ // THe first character must be a letter.
+ match chars.next() {
+ Some(c) if c.is_ascii_alphabetic() => len += 1,
+ _ => return false,
+ }
+ // Second and following characters must be either a letter, number,
+ // plus sign, minus sign, or dot.
+ loop {
+ match chars.next() {
+ Some(c) if c.is_ascii_alphanumeric() || "+-.".contains(c) => len += 1,
+ Some(':') if len >= 2 => return true,
+ _ => return false,
+ }
}
}
@@ -306,9 +257,7 @@ mod tests {
];
for (specifier, base, expected_url) in tests {
- let url = ModuleSpecifier::resolve_import(specifier, base)
- .unwrap()
- .to_string();
+ let url = resolve_import(specifier, base).unwrap().to_string();
assert_eq!(url, expected_url);
}
}
@@ -385,7 +334,7 @@ mod tests {
];
for (specifier, base, expected_err) in tests {
- let err = ModuleSpecifier::resolve_import(specifier, base).unwrap_err();
+ let err = resolve_import(specifier, base).unwrap_err();
assert_eq!(err, expected_err);
}
}
@@ -487,9 +436,7 @@ mod tests {
}
for (specifier, expected_url) in tests {
- let url = ModuleSpecifier::resolve_url_or_path(specifier)
- .unwrap()
- .to_string();
+ let url = resolve_url_or_path(specifier).unwrap().to_string();
assert_eq!(url, expected_url);
}
}
@@ -509,7 +456,7 @@ mod tests {
}
for (specifier, expected_err) in tests {
- let err = ModuleSpecifier::resolve_url_or_path(specifier).unwrap_err();
+ let err = resolve_url_or_path(specifier).unwrap_err();
assert_eq!(err, expected_err);
}
}
@@ -539,7 +486,7 @@ mod tests {
];
for (specifier, expected) in tests {
- let result = ModuleSpecifier::specifier_has_uri_scheme(specifier);
+ let result = specifier_has_uri_scheme(specifier);
assert_eq!(result, expected);
}
}
@@ -565,8 +512,7 @@ mod tests {
fn test_deserialize_module_specifier() {
let actual: ModuleSpecifier =
from_value(json!("http://deno.land/x/mod.ts")).unwrap();
- let expected =
- ModuleSpecifier::resolve_url("http://deno.land/x/mod.ts").unwrap();
+ let expected = resolve_url("http://deno.land/x/mod.ts").unwrap();
assert_eq!(actual, expected);
}
}