core: return useful error when import path has no prefix like ./

This commit is contained in:
Bert Belder 2019-06-30 19:45:59 +02:00
parent 9d18f97327
commit 32cde32e54
6 changed files with 119 additions and 53 deletions

View file

@ -7,6 +7,7 @@ use crate::resolve_addr::ResolveAddrError;
use crate::source_maps::apply_source_map;
use crate::source_maps::SourceMapGetter;
use deno::JSError;
use deno::ModuleResolutionError;
use hyper;
#[cfg(unix)]
use nix::{errno::Errno, Error as UnixError};
@ -30,6 +31,7 @@ enum Repr {
UrlErr(url::ParseError),
HyperErr(hyper::Error),
ImportMapErr(import_map::ImportMapError),
ModuleResolutionErr(ModuleResolutionError),
Diagnostic(diagnostics::Diagnostic),
JSError(JSError),
}
@ -42,6 +44,24 @@ pub fn new(kind: ErrorKind, msg: String) -> DenoError {
}
impl DenoError {
fn url_error_kind(err: url::ParseError) -> ErrorKind {
use url::ParseError::*;
match err {
EmptyHost => ErrorKind::EmptyHost,
IdnaError => ErrorKind::IdnaError,
InvalidPort => ErrorKind::InvalidPort,
InvalidIpv4Address => ErrorKind::InvalidIpv4Address,
InvalidIpv6Address => ErrorKind::InvalidIpv6Address,
InvalidDomainCharacter => ErrorKind::InvalidDomainCharacter,
RelativeUrlWithoutBase => ErrorKind::RelativeUrlWithoutBase,
RelativeUrlWithCannotBeABaseBase => {
ErrorKind::RelativeUrlWithCannotBeABaseBase
}
SetHostOnCannotBeABaseUrl => ErrorKind::SetHostOnCannotBeABaseUrl,
Overflow => ErrorKind::Overflow,
}
}
pub fn kind(&self) -> ErrorKind {
match self.repr {
Repr::Simple(kind, ref _msg) => kind,
@ -70,23 +90,7 @@ impl DenoError {
_ => unreachable!(),
}
}
Repr::UrlErr(ref err) => {
use url::ParseError::*;
match err {
EmptyHost => ErrorKind::EmptyHost,
IdnaError => ErrorKind::IdnaError,
InvalidPort => ErrorKind::InvalidPort,
InvalidIpv4Address => ErrorKind::InvalidIpv4Address,
InvalidIpv6Address => ErrorKind::InvalidIpv6Address,
InvalidDomainCharacter => ErrorKind::InvalidDomainCharacter,
RelativeUrlWithoutBase => ErrorKind::RelativeUrlWithoutBase,
RelativeUrlWithCannotBeABaseBase => {
ErrorKind::RelativeUrlWithCannotBeABaseBase
}
SetHostOnCannotBeABaseUrl => ErrorKind::SetHostOnCannotBeABaseUrl,
Overflow => ErrorKind::Overflow,
}
}
Repr::UrlErr(err) => Self::url_error_kind(err),
Repr::HyperErr(ref err) => {
// For some reason hyper::errors::Kind is private.
if err.is_parse() {
@ -102,6 +106,13 @@ impl DenoError {
}
}
Repr::ImportMapErr(ref _err) => ErrorKind::ImportMapError,
Repr::ModuleResolutionErr(err) => {
use ModuleResolutionError::*;
match err {
InvalidUrl(err) | InvalidBaseUrl(err) => Self::url_error_kind(err),
ImportPathPrefixMissing => ErrorKind::ImportPathPrefixMissing,
}
}
Repr::Diagnostic(ref _err) => ErrorKind::Diagnostic,
Repr::JSError(ref _err) => ErrorKind::JSError,
}
@ -126,6 +137,7 @@ impl fmt::Display for DenoError {
Repr::UrlErr(ref err) => err.fmt(f),
Repr::HyperErr(ref err) => err.fmt(f),
Repr::ImportMapErr(ref err) => f.pad(&err.msg),
Repr::ModuleResolutionErr(ref err) => err.fmt(f),
Repr::Diagnostic(ref err) => err.fmt(f),
Repr::JSError(ref err) => JSErrorColor(err).fmt(f),
}
@ -140,6 +152,7 @@ impl std::error::Error for DenoError {
Repr::UrlErr(ref err) => err.description(),
Repr::HyperErr(ref err) => err.description(),
Repr::ImportMapErr(ref err) => &err.msg,
Repr::ModuleResolutionErr(ref err) => err.description(),
Repr::Diagnostic(ref err) => &err.items[0].message,
Repr::JSError(ref err) => &err.description(),
}
@ -152,6 +165,7 @@ impl std::error::Error for DenoError {
Repr::UrlErr(ref err) => Some(err),
Repr::HyperErr(ref err) => Some(err),
Repr::ImportMapErr(ref _err) => None,
Repr::ModuleResolutionErr(ref err) => err.source(),
Repr::Diagnostic(ref _err) => None,
Repr::JSError(ref err) => Some(err),
}
@ -241,6 +255,14 @@ impl From<import_map::ImportMapError> for DenoError {
}
}
impl From<ModuleResolutionError> for DenoError {
fn from(err: ModuleResolutionError) -> Self {
Self {
repr: Repr::ModuleResolutionErr(err),
}
}
}
impl From<diagnostics::Diagnostic> for DenoError {
fn from(diagnostic: diagnostics::Diagnostic) -> Self {
Self {

View file

@ -141,6 +141,7 @@ enum ErrorKind: byte {
NoAsyncSupport,
NoSyncSupport,
ImportMapError,
ImportPathPrefixMissing,
// other kinds
Diagnostic,

View file

@ -17,7 +17,7 @@ pub use crate::isolate::*;
pub use crate::js_errors::*;
pub use crate::libdeno::deno_mod;
pub use crate::libdeno::PinnedBuf;
pub use crate::module_specifier::ModuleSpecifier;
pub use crate::module_specifier::*;
pub use crate::modules::*;
pub fn v8_version() -> &'static str {

View file

@ -1,6 +1,40 @@
use std::error::Error;
use std::fmt;
use url::ParseError;
use url::Url;
/// Error indicating the reason resolving a module specifier failed.
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum ModuleResolutionError {
InvalidUrl(ParseError),
InvalidBaseUrl(ParseError),
ImportPathPrefixMissing,
}
use ModuleResolutionError::*;
impl Error for ModuleResolutionError {
fn source(&self) -> Option<&(dyn Error + 'static)> {
match self {
InvalidUrl(ref err) | InvalidBaseUrl(ref err) => Some(err),
_ => None,
}
}
}
impl fmt::Display for ModuleResolutionError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
InvalidUrl(ref err) => write!(f, "invalid URL: {}", err),
InvalidBaseUrl(ref err) => {
write!(f, "invalid base URL for relative import: {}", err)
}
ImportPathPrefixMissing => {
write!(f, "relative import path not prefixed with / or ./ or ../")
}
}
}
}
#[derive(Debug, Clone, PartialEq)]
/// Resolved module specifier
pub struct ModuleSpecifier(Url);
@ -15,32 +49,39 @@ impl ModuleSpecifier {
pub fn resolve(
specifier: &str,
base: &str,
) -> Result<ModuleSpecifier, url::ParseError> {
// 1. Apply the URL parser to specifier. If the result is not failure, return
// the result.
// let specifier = parse_local_or_remote(specifier)?.to_string();
if let Ok(url) = Url::parse(specifier) {
return Ok(ModuleSpecifier(url));
}
) -> 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.
if !specifier.starts_with('/')
&& !specifier.starts_with("./")
&& !specifier.starts_with("../")
{
// TODO This is (probably) not the correct error to return here.
// TODO: This error is very not-user-friendly
return Err(url::ParseError::RelativeUrlWithCannotBeABaseBase);
}
// 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("../")) =>
{
Err(ImportPathPrefixMissing)?
}
// 3. Return the result of applying the URL parser to specifier with base URL
// as the base URL.
let base_url = Url::parse(base)?;
let u = base_url.join(&specifier)?;
Ok(ModuleSpecifier(u))
// 3. Return the result of applying the URL parser to specifier with base
// URL as the base URL.
Err(ParseError::RelativeUrlWithoutBase) => {
let base = 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 `ImportPathPrefixMissing` or `InvalidBaseUrl` if the real
// problem lies somewhere else.
Err(err) => Err(InvalidUrl(err))?,
};
Ok(ModuleSpecifier(url))
}
/// Takes a string representing a path or URL to a module, but of the type
@ -51,15 +92,17 @@ impl ModuleSpecifier {
/// directory and returns an absolute URL.
pub fn resolve_root(
root_specifier: &str,
) -> Result<ModuleSpecifier, url::ParseError> {
if let Ok(url) = Url::parse(root_specifier) {
Ok(ModuleSpecifier(url))
} else {
let cwd = std::env::current_dir().unwrap();
let base = Url::from_directory_path(cwd).unwrap();
let url = base.join(root_specifier)?;
Ok(ModuleSpecifier(url))
}
) -> Result<ModuleSpecifier, ModuleResolutionError> {
let url = match Url::parse(root_specifier) {
Ok(url) => url,
Err(..) => {
let cwd = std::env::current_dir().unwrap();
let base = Url::from_directory_path(cwd).unwrap();
base.join(&root_specifier).map_err(InvalidUrl)?
}
};
Ok(ModuleSpecifier(url))
}
}

View file

@ -1,4 +1,4 @@
[WILDCARD]error: Uncaught RelativeUrlWithCannotBeABaseBase: relative URL with a cannot-be-a-base base
[WILDCARD]error: Uncaught ImportPathPrefixMissing: relative import path not prefixed with / or ./ or ../
[WILDCARD] js/errors.ts:[WILDCARD]
at DenoError (js/errors.ts:[WILDCARD])
at maybeError (js/errors.ts:[WILDCARD])

View file

@ -1,4 +1,4 @@
[WILDCARD]error: Uncaught RelativeUrlWithCannotBeABaseBase: relative URL with a cannot-be-a-base base
[WILDCARD]error: Uncaught ImportPathPrefixMissing: relative import path not prefixed with / or ./ or ../
[WILDCARD] js/errors.ts:[WILDCARD]
at DenoError (js/errors.ts:[WILDCARD])
at maybeError (js/errors.ts:[WILDCARD])