2023-01-02 16:00:42 -05:00
|
|
|
// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
|
2020-09-21 08:26:41 -04:00
|
|
|
|
2020-08-12 16:34:17 -04:00
|
|
|
use crate::normalize_path;
|
2019-06-30 13:45:59 -04:00
|
|
|
use std::error::Error;
|
2019-06-12 15:00:08 -04:00
|
|
|
use std::fmt;
|
2023-03-13 13:50:23 -04:00
|
|
|
use std::path::Path;
|
2019-07-30 10:34:57 -04:00
|
|
|
use std::path::PathBuf;
|
2019-06-30 13:45:59 -04:00
|
|
|
use url::ParseError;
|
2019-06-12 15:00:08 -04:00
|
|
|
use url::Url;
|
|
|
|
|
2019-06-30 13:45:59 -04:00
|
|
|
/// Error indicating the reason resolving a module specifier failed.
|
2019-07-30 10:34:57 -04:00
|
|
|
#[derive(Clone, Debug, Eq, PartialEq)]
|
2019-06-30 13:45:59 -04:00
|
|
|
pub enum ModuleResolutionError {
|
|
|
|
InvalidUrl(ParseError),
|
|
|
|
InvalidBaseUrl(ParseError),
|
2019-07-30 10:34:57 -04:00
|
|
|
InvalidPath(PathBuf),
|
2019-11-26 12:14:04 -05:00
|
|
|
ImportPrefixMissing(String, Option<String>),
|
2019-06-30 13:45:59 -04:00
|
|
|
}
|
|
|
|
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 {
|
2023-01-27 10:43:16 -05:00
|
|
|
InvalidUrl(ref err) => write!(f, "invalid URL: {err}"),
|
2019-06-30 13:45:59 -04:00
|
|
|
InvalidBaseUrl(ref err) => {
|
2023-01-27 10:43:16 -05:00
|
|
|
write!(f, "invalid base URL for relative import: {err}")
|
2019-06-30 13:45:59 -04:00
|
|
|
}
|
2023-01-27 10:43:16 -05:00
|
|
|
InvalidPath(ref path) => write!(f, "invalid module path: {path:?}"),
|
2020-05-17 11:33:44 -04:00
|
|
|
ImportPrefixMissing(ref specifier, ref maybe_referrer) => write!(
|
|
|
|
f,
|
2021-05-30 20:20:34 -04:00
|
|
|
"Relative import path \"{}\" not prefixed with / or ./ or ../{}",
|
2020-05-17 11:33:44 -04:00
|
|
|
specifier,
|
|
|
|
match maybe_referrer {
|
2023-01-27 10:43:16 -05:00
|
|
|
Some(referrer) => format!(" from \"{referrer}\""),
|
2022-01-15 01:10:12 -05:00
|
|
|
None => String::new(),
|
2020-05-17 11:33:44 -04:00
|
|
|
}
|
|
|
|
),
|
2019-06-30 13:45:59 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-06-12 15:00:08 -04:00
|
|
|
/// Resolved module specifier
|
2021-02-17 13:47:18 -05:00
|
|
|
pub type ModuleSpecifier = Url;
|
|
|
|
|
|
|
|
/// Resolves module using this algorithm:
|
2021-09-05 10:22:45 -04:00
|
|
|
/// <https://html.spec.whatwg.org/multipage/webappapis.html#resolve-a-module-specifier>
|
2021-02-17 13:47:18 -05:00
|
|
|
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));
|
|
|
|
}
|
2019-06-30 13:45:59 -04:00
|
|
|
|
2021-02-17 13:47:18 -05:00
|
|
|
// 3. Return the result of applying the URL parser to specifier with base
|
|
|
|
// URL as the base URL.
|
|
|
|
Err(ParseError::RelativeUrlWithoutBase) => {
|
2023-03-14 16:47:09 -04:00
|
|
|
let base = Url::parse(base).map_err(InvalidBaseUrl)?;
|
2021-07-30 09:03:41 -04:00
|
|
|
base.join(specifier).map_err(InvalidUrl)?
|
core: clearly define when module lookup is path-based vs URL-based
The rules are now as follows:
* In `import` statements, as mandated by the WHATWG specification,
the import specifier is always treated as a URL.
If it is a relative URL, it must start with either / or ./ or ../
* A script name passed to deno as a command line argument may be either
an absolute URL or a local path.
- If the name starts with a valid URI scheme followed by a colon, e.g.
'http:', 'https:', 'file:', 'foo+bar:', it always interpreted as a
URL (even if Deno doesn't support the indicated protocol).
- Otherwise, the script name is interpreted as a local path. The local
path may be relative, and operating system semantics determine how
it is resolved. Prefixing a relative path with ./ is not required.
2019-07-08 03:55:24 -04:00
|
|
|
}
|
|
|
|
|
2021-02-17 13:47:18 -05:00
|
|
|
// 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)),
|
|
|
|
};
|
core: clearly define when module lookup is path-based vs URL-based
The rules are now as follows:
* In `import` statements, as mandated by the WHATWG specification,
the import specifier is always treated as a URL.
If it is a relative URL, it must start with either / or ./ or ../
* A script name passed to deno as a command line argument may be either
an absolute URL or a local path.
- If the name starts with a valid URI scheme followed by a colon, e.g.
'http:', 'https:', 'file:', 'foo+bar:', it always interpreted as a
URL (even if Deno doesn't support the indicated protocol).
- Otherwise, the script name is interpreted as a local path. The local
path may be relative, and operating system semantics determine how
it is resolved. Prefixing a relative path with ./ is not required.
2019-07-08 03:55:24 -04:00
|
|
|
|
2021-02-17 13:47:18 -05:00
|
|
|
Ok(url)
|
2019-06-12 15:00:08 -04:00
|
|
|
}
|
|
|
|
|
2021-02-17 13:47:18 -05:00
|
|
|
/// 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)
|
2019-06-12 15:00:08 -04:00
|
|
|
}
|
|
|
|
|
2023-03-13 21:12:09 -04:00
|
|
|
/// 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 passed
|
|
|
|
/// `current_dir`.
|
|
|
|
pub fn resolve_url_or_path(
|
|
|
|
specifier: &str,
|
|
|
|
current_dir: &Path,
|
|
|
|
) -> Result<ModuleSpecifier, ModuleResolutionError> {
|
|
|
|
if specifier_has_uri_scheme(specifier) {
|
|
|
|
resolve_url(specifier)
|
|
|
|
} else {
|
|
|
|
resolve_path(specifier, current_dir)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-02-17 13:47:18 -05:00
|
|
|
/// Converts a string representing a relative or absolute path into a
|
2023-03-13 21:12:09 -04:00
|
|
|
/// ModuleSpecifier. A relative path is considered relative to the passed
|
|
|
|
/// `current_dir`.
|
2021-02-17 13:47:18 -05:00
|
|
|
pub fn resolve_path(
|
|
|
|
path_str: &str,
|
2023-03-13 13:50:23 -04:00
|
|
|
current_dir: &Path,
|
2021-02-17 13:47:18 -05:00
|
|
|
) -> Result<ModuleSpecifier, ModuleResolutionError> {
|
2023-03-13 13:50:23 -04:00
|
|
|
let path = current_dir.join(path_str);
|
2022-12-17 17:20:15 -05:00
|
|
|
let path = normalize_path(path);
|
2023-01-14 23:06:46 -05:00
|
|
|
Url::from_file_path(&path)
|
2021-02-17 13:47:18 -05:00
|
|
|
.map_err(|()| ModuleResolutionError::InvalidPath(path))
|
2019-06-12 15:00:08 -04:00
|
|
|
}
|
core: clearly define when module lookup is path-based vs URL-based
The rules are now as follows:
* In `import` statements, as mandated by the WHATWG specification,
the import specifier is always treated as a URL.
If it is a relative URL, it must start with either / or ./ or ../
* A script name passed to deno as a command line argument may be either
an absolute URL or a local path.
- If the name starts with a valid URI scheme followed by a colon, e.g.
'http:', 'https:', 'file:', 'foo+bar:', it always interpreted as a
URL (even if Deno doesn't support the indicated protocol).
- Otherwise, the script name is interpreted as a local path. The local
path may be relative, and operating system semantics determine how
it is resolved. Prefixing a relative path with ./ is not required.
2019-07-08 03:55:24 -04:00
|
|
|
|
2021-02-17 13:47:18 -05:00
|
|
|
/// 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,
|
|
|
|
}
|
2021-01-31 22:30:41 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
core: clearly define when module lookup is path-based vs URL-based
The rules are now as follows:
* In `import` statements, as mandated by the WHATWG specification,
the import specifier is always treated as a URL.
If it is a relative URL, it must start with either / or ./ or ../
* A script name passed to deno as a command line argument may be either
an absolute URL or a local path.
- If the name starts with a valid URI scheme followed by a colon, e.g.
'http:', 'https:', 'file:', 'foo+bar:', it always interpreted as a
URL (even if Deno doesn't support the indicated protocol).
- Otherwise, the script name is interpreted as a local path. The local
path may be relative, and operating system semantics determine how
it is resolved. Prefixing a relative path with ./ is not required.
2019-07-08 03:55:24 -04:00
|
|
|
#[cfg(test)]
|
|
|
|
mod tests {
|
|
|
|
use super::*;
|
2021-01-31 22:30:41 -05:00
|
|
|
use crate::serde_json::from_value;
|
|
|
|
use crate::serde_json::json;
|
2023-03-14 16:47:09 -04:00
|
|
|
use std::env::current_dir;
|
2020-08-12 16:34:17 -04:00
|
|
|
use std::path::Path;
|
core: clearly define when module lookup is path-based vs URL-based
The rules are now as follows:
* In `import` statements, as mandated by the WHATWG specification,
the import specifier is always treated as a URL.
If it is a relative URL, it must start with either / or ./ or ../
* A script name passed to deno as a command line argument may be either
an absolute URL or a local path.
- If the name starts with a valid URI scheme followed by a colon, e.g.
'http:', 'https:', 'file:', 'foo+bar:', it always interpreted as a
URL (even if Deno doesn't support the indicated protocol).
- Otherwise, the script name is interpreted as a local path. The local
path may be relative, and operating system semantics determine how
it is resolved. Prefixing a relative path with ./ is not required.
2019-07-08 03:55:24 -04:00
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_resolve_import() {
|
|
|
|
let tests = vec![
|
2019-06-25 16:14:36 -04:00
|
|
|
(
|
|
|
|
"./005_more_imports.ts",
|
|
|
|
"http://deno.land/core/tests/006_url_imports.ts",
|
|
|
|
"http://deno.land/core/tests/005_more_imports.ts",
|
|
|
|
),
|
|
|
|
(
|
|
|
|
"../005_more_imports.ts",
|
|
|
|
"http://deno.land/core/tests/006_url_imports.ts",
|
|
|
|
"http://deno.land/core/005_more_imports.ts",
|
|
|
|
),
|
|
|
|
(
|
|
|
|
"http://deno.land/core/tests/005_more_imports.ts",
|
|
|
|
"http://deno.land/core/tests/006_url_imports.ts",
|
|
|
|
"http://deno.land/core/tests/005_more_imports.ts",
|
|
|
|
),
|
|
|
|
(
|
|
|
|
"data:text/javascript,export default 'grapes';",
|
|
|
|
"http://deno.land/core/tests/006_url_imports.ts",
|
|
|
|
"data:text/javascript,export default 'grapes';",
|
|
|
|
),
|
|
|
|
(
|
|
|
|
"blob:https://whatwg.org/d0360e2f-caee-469f-9a2f-87d5b0456f6f",
|
|
|
|
"http://deno.land/core/tests/006_url_imports.ts",
|
|
|
|
"blob:https://whatwg.org/d0360e2f-caee-469f-9a2f-87d5b0456f6f",
|
|
|
|
),
|
|
|
|
(
|
|
|
|
"javascript:export default 'artichokes';",
|
|
|
|
"http://deno.land/core/tests/006_url_imports.ts",
|
|
|
|
"javascript:export default 'artichokes';",
|
|
|
|
),
|
|
|
|
(
|
|
|
|
"data:text/plain,export default 'kale';",
|
|
|
|
"http://deno.land/core/tests/006_url_imports.ts",
|
|
|
|
"data:text/plain,export default 'kale';",
|
|
|
|
),
|
core: clearly define when module lookup is path-based vs URL-based
The rules are now as follows:
* In `import` statements, as mandated by the WHATWG specification,
the import specifier is always treated as a URL.
If it is a relative URL, it must start with either / or ./ or ../
* A script name passed to deno as a command line argument may be either
an absolute URL or a local path.
- If the name starts with a valid URI scheme followed by a colon, e.g.
'http:', 'https:', 'file:', 'foo+bar:', it always interpreted as a
URL (even if Deno doesn't support the indicated protocol).
- Otherwise, the script name is interpreted as a local path. The local
path may be relative, and operating system semantics determine how
it is resolved. Prefixing a relative path with ./ is not required.
2019-07-08 03:55:24 -04:00
|
|
|
(
|
|
|
|
"/dev/core/tests/005_more_imports.ts",
|
|
|
|
"file:///home/yeti",
|
|
|
|
"file:///dev/core/tests/005_more_imports.ts",
|
|
|
|
),
|
|
|
|
(
|
|
|
|
"//zombo.com/1999.ts",
|
|
|
|
"https://cherry.dev/its/a/thing",
|
|
|
|
"https://zombo.com/1999.ts",
|
|
|
|
),
|
|
|
|
(
|
|
|
|
"http://deno.land/this/url/is/valid",
|
|
|
|
"base is clearly not a valid url",
|
|
|
|
"http://deno.land/this/url/is/valid",
|
|
|
|
),
|
|
|
|
(
|
|
|
|
"//server/some/dir/file",
|
|
|
|
"file:///home/yeti/deno",
|
|
|
|
"file://server/some/dir/file",
|
|
|
|
),
|
|
|
|
// This test is disabled because the url crate does not follow the spec,
|
|
|
|
// dropping the server part from the final result.
|
|
|
|
// (
|
|
|
|
// "/another/path/at/the/same/server",
|
|
|
|
// "file://server/some/dir/file",
|
|
|
|
// "file://server/another/path/at/the/same/server",
|
|
|
|
// ),
|
|
|
|
];
|
|
|
|
|
|
|
|
for (specifier, base, expected_url) in tests {
|
2021-02-17 13:47:18 -05:00
|
|
|
let url = resolve_import(specifier, base).unwrap().to_string();
|
core: clearly define when module lookup is path-based vs URL-based
The rules are now as follows:
* In `import` statements, as mandated by the WHATWG specification,
the import specifier is always treated as a URL.
If it is a relative URL, it must start with either / or ./ or ../
* A script name passed to deno as a command line argument may be either
an absolute URL or a local path.
- If the name starts with a valid URI scheme followed by a colon, e.g.
'http:', 'https:', 'file:', 'foo+bar:', it always interpreted as a
URL (even if Deno doesn't support the indicated protocol).
- Otherwise, the script name is interpreted as a local path. The local
path may be relative, and operating system semantics determine how
it is resolved. Prefixing a relative path with ./ is not required.
2019-07-08 03:55:24 -04:00
|
|
|
assert_eq!(url, expected_url);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_resolve_import_error() {
|
|
|
|
use url::ParseError::*;
|
|
|
|
use ModuleResolutionError::*;
|
|
|
|
|
|
|
|
let tests = vec![
|
2020-04-30 03:10:28 -04:00
|
|
|
(
|
|
|
|
"awesome.ts",
|
|
|
|
"<unknown>",
|
|
|
|
ImportPrefixMissing(
|
|
|
|
"awesome.ts".to_string(),
|
|
|
|
Some("<unknown>".to_string()),
|
|
|
|
),
|
|
|
|
),
|
2019-06-25 16:14:36 -04:00
|
|
|
(
|
|
|
|
"005_more_imports.ts",
|
|
|
|
"http://deno.land/core/tests/006_url_imports.ts",
|
2019-11-26 12:14:04 -05:00
|
|
|
ImportPrefixMissing(
|
|
|
|
"005_more_imports.ts".to_string(),
|
|
|
|
Some("http://deno.land/core/tests/006_url_imports.ts".to_string()),
|
|
|
|
),
|
2019-06-25 16:14:36 -04:00
|
|
|
),
|
|
|
|
(
|
|
|
|
".tomato",
|
|
|
|
"http://deno.land/core/tests/006_url_imports.ts",
|
2019-11-26 12:14:04 -05:00
|
|
|
ImportPrefixMissing(
|
|
|
|
".tomato".to_string(),
|
|
|
|
Some("http://deno.land/core/tests/006_url_imports.ts".to_string()),
|
|
|
|
),
|
2019-06-25 16:14:36 -04:00
|
|
|
),
|
|
|
|
(
|
|
|
|
"..zucchini.mjs",
|
|
|
|
"http://deno.land/core/tests/006_url_imports.ts",
|
2019-11-26 12:14:04 -05:00
|
|
|
ImportPrefixMissing(
|
|
|
|
"..zucchini.mjs".to_string(),
|
|
|
|
Some("http://deno.land/core/tests/006_url_imports.ts".to_string()),
|
|
|
|
),
|
2019-06-25 16:14:36 -04:00
|
|
|
),
|
|
|
|
(
|
|
|
|
r".\yam.es",
|
|
|
|
"http://deno.land/core/tests/006_url_imports.ts",
|
2019-11-26 12:14:04 -05:00
|
|
|
ImportPrefixMissing(
|
|
|
|
r".\yam.es".to_string(),
|
|
|
|
Some("http://deno.land/core/tests/006_url_imports.ts".to_string()),
|
|
|
|
),
|
2019-06-25 16:14:36 -04:00
|
|
|
),
|
|
|
|
(
|
|
|
|
r"..\yam.es",
|
|
|
|
"http://deno.land/core/tests/006_url_imports.ts",
|
2019-11-26 12:14:04 -05:00
|
|
|
ImportPrefixMissing(
|
|
|
|
r"..\yam.es".to_string(),
|
|
|
|
Some("http://deno.land/core/tests/006_url_imports.ts".to_string()),
|
|
|
|
),
|
2019-06-25 16:14:36 -04:00
|
|
|
),
|
core: clearly define when module lookup is path-based vs URL-based
The rules are now as follows:
* In `import` statements, as mandated by the WHATWG specification,
the import specifier is always treated as a URL.
If it is a relative URL, it must start with either / or ./ or ../
* A script name passed to deno as a command line argument may be either
an absolute URL or a local path.
- If the name starts with a valid URI scheme followed by a colon, e.g.
'http:', 'https:', 'file:', 'foo+bar:', it always interpreted as a
URL (even if Deno doesn't support the indicated protocol).
- Otherwise, the script name is interpreted as a local path. The local
path may be relative, and operating system semantics determine how
it is resolved. Prefixing a relative path with ./ is not required.
2019-07-08 03:55:24 -04:00
|
|
|
(
|
|
|
|
"https://eggplant:b/c",
|
|
|
|
"http://deno.land/core/tests/006_url_imports.ts",
|
|
|
|
InvalidUrl(InvalidPort),
|
|
|
|
),
|
|
|
|
(
|
|
|
|
"https://eggplant@/c",
|
|
|
|
"http://deno.land/core/tests/006_url_imports.ts",
|
|
|
|
InvalidUrl(EmptyHost),
|
|
|
|
),
|
|
|
|
(
|
|
|
|
"./foo.ts",
|
|
|
|
"/relative/base/url",
|
|
|
|
InvalidBaseUrl(RelativeUrlWithoutBase),
|
|
|
|
),
|
|
|
|
];
|
|
|
|
|
|
|
|
for (specifier, base, expected_err) in tests {
|
2021-02-17 13:47:18 -05:00
|
|
|
let err = resolve_import(specifier, base).unwrap_err();
|
core: clearly define when module lookup is path-based vs URL-based
The rules are now as follows:
* In `import` statements, as mandated by the WHATWG specification,
the import specifier is always treated as a URL.
If it is a relative URL, it must start with either / or ./ or ../
* A script name passed to deno as a command line argument may be either
an absolute URL or a local path.
- If the name starts with a valid URI scheme followed by a colon, e.g.
'http:', 'https:', 'file:', 'foo+bar:', it always interpreted as a
URL (even if Deno doesn't support the indicated protocol).
- Otherwise, the script name is interpreted as a local path. The local
path may be relative, and operating system semantics determine how
it is resolved. Prefixing a relative path with ./ is not required.
2019-07-08 03:55:24 -04:00
|
|
|
assert_eq!(err, expected_err);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
2023-03-14 13:18:01 -04:00
|
|
|
fn test_resolve_url_or_path() {
|
core: clearly define when module lookup is path-based vs URL-based
The rules are now as follows:
* In `import` statements, as mandated by the WHATWG specification,
the import specifier is always treated as a URL.
If it is a relative URL, it must start with either / or ./ or ../
* A script name passed to deno as a command line argument may be either
an absolute URL or a local path.
- If the name starts with a valid URI scheme followed by a colon, e.g.
'http:', 'https:', 'file:', 'foo+bar:', it always interpreted as a
URL (even if Deno doesn't support the indicated protocol).
- Otherwise, the script name is interpreted as a local path. The local
path may be relative, and operating system semantics determine how
it is resolved. Prefixing a relative path with ./ is not required.
2019-07-08 03:55:24 -04:00
|
|
|
// Absolute URL.
|
2019-06-25 16:14:36 -04:00
|
|
|
let mut tests: Vec<(&str, String)> = vec![
|
|
|
|
(
|
|
|
|
"http://deno.land/core/tests/006_url_imports.ts",
|
|
|
|
"http://deno.land/core/tests/006_url_imports.ts".to_string(),
|
|
|
|
),
|
|
|
|
(
|
|
|
|
"https://deno.land/core/tests/006_url_imports.ts",
|
|
|
|
"https://deno.land/core/tests/006_url_imports.ts".to_string(),
|
|
|
|
),
|
|
|
|
];
|
core: clearly define when module lookup is path-based vs URL-based
The rules are now as follows:
* In `import` statements, as mandated by the WHATWG specification,
the import specifier is always treated as a URL.
If it is a relative URL, it must start with either / or ./ or ../
* A script name passed to deno as a command line argument may be either
an absolute URL or a local path.
- If the name starts with a valid URI scheme followed by a colon, e.g.
'http:', 'https:', 'file:', 'foo+bar:', it always interpreted as a
URL (even if Deno doesn't support the indicated protocol).
- Otherwise, the script name is interpreted as a local path. The local
path may be relative, and operating system semantics determine how
it is resolved. Prefixing a relative path with ./ is not required.
2019-07-08 03:55:24 -04:00
|
|
|
|
|
|
|
// The local path tests assume that the cwd is the deno repo root.
|
|
|
|
let cwd = current_dir().unwrap();
|
|
|
|
let cwd_str = cwd.to_str().unwrap();
|
|
|
|
|
|
|
|
if cfg!(target_os = "windows") {
|
|
|
|
// Absolute local path.
|
|
|
|
let expected_url = "file:///C:/deno/tests/006_url_imports.ts";
|
|
|
|
tests.extend(vec![
|
|
|
|
(
|
|
|
|
r"C:/deno/tests/006_url_imports.ts",
|
|
|
|
expected_url.to_string(),
|
|
|
|
),
|
|
|
|
(
|
|
|
|
r"C:\deno\tests\006_url_imports.ts",
|
|
|
|
expected_url.to_string(),
|
|
|
|
),
|
|
|
|
(
|
|
|
|
r"\\?\C:\deno\tests\006_url_imports.ts",
|
|
|
|
expected_url.to_string(),
|
|
|
|
),
|
|
|
|
// Not supported: `Url::from_file_path()` fails.
|
|
|
|
// (r"\\.\C:\deno\tests\006_url_imports.ts", expected_url.to_string()),
|
|
|
|
// Not supported: `Url::from_file_path()` performs the wrong conversion.
|
|
|
|
// (r"//./C:/deno/tests/006_url_imports.ts", expected_url.to_string()),
|
|
|
|
]);
|
|
|
|
|
|
|
|
// Rooted local path without drive letter.
|
|
|
|
let expected_url = format!(
|
|
|
|
"file:///{}:/deno/tests/006_url_imports.ts",
|
|
|
|
cwd_str.get(..1).unwrap(),
|
|
|
|
);
|
|
|
|
tests.extend(vec![
|
|
|
|
(r"/deno/tests/006_url_imports.ts", expected_url.to_string()),
|
2020-07-16 04:53:07 -04:00
|
|
|
(r"\deno\tests\006_url_imports.ts", expected_url.to_string()),
|
|
|
|
(
|
|
|
|
r"\deno\..\deno\tests\006_url_imports.ts",
|
|
|
|
expected_url.to_string(),
|
|
|
|
),
|
|
|
|
(r"\deno\.\tests\006_url_imports.ts", expected_url),
|
core: clearly define when module lookup is path-based vs URL-based
The rules are now as follows:
* In `import` statements, as mandated by the WHATWG specification,
the import specifier is always treated as a URL.
If it is a relative URL, it must start with either / or ./ or ../
* A script name passed to deno as a command line argument may be either
an absolute URL or a local path.
- If the name starts with a valid URI scheme followed by a colon, e.g.
'http:', 'https:', 'file:', 'foo+bar:', it always interpreted as a
URL (even if Deno doesn't support the indicated protocol).
- Otherwise, the script name is interpreted as a local path. The local
path may be relative, and operating system semantics determine how
it is resolved. Prefixing a relative path with ./ is not required.
2019-07-08 03:55:24 -04:00
|
|
|
]);
|
|
|
|
|
|
|
|
// Relative local path.
|
|
|
|
let expected_url = format!(
|
|
|
|
"file:///{}/tests/006_url_imports.ts",
|
2022-02-24 20:03:12 -05:00
|
|
|
cwd_str.replace('\\', "/")
|
core: clearly define when module lookup is path-based vs URL-based
The rules are now as follows:
* In `import` statements, as mandated by the WHATWG specification,
the import specifier is always treated as a URL.
If it is a relative URL, it must start with either / or ./ or ../
* A script name passed to deno as a command line argument may be either
an absolute URL or a local path.
- If the name starts with a valid URI scheme followed by a colon, e.g.
'http:', 'https:', 'file:', 'foo+bar:', it always interpreted as a
URL (even if Deno doesn't support the indicated protocol).
- Otherwise, the script name is interpreted as a local path. The local
path may be relative, and operating system semantics determine how
it is resolved. Prefixing a relative path with ./ is not required.
2019-07-08 03:55:24 -04:00
|
|
|
);
|
|
|
|
tests.extend(vec![
|
|
|
|
(r"tests/006_url_imports.ts", expected_url.to_string()),
|
|
|
|
(r"tests\006_url_imports.ts", expected_url.to_string()),
|
2019-12-23 09:59:44 -05:00
|
|
|
(r"./tests/006_url_imports.ts", (*expected_url).to_string()),
|
|
|
|
(r".\tests\006_url_imports.ts", (*expected_url).to_string()),
|
core: clearly define when module lookup is path-based vs URL-based
The rules are now as follows:
* In `import` statements, as mandated by the WHATWG specification,
the import specifier is always treated as a URL.
If it is a relative URL, it must start with either / or ./ or ../
* A script name passed to deno as a command line argument may be either
an absolute URL or a local path.
- If the name starts with a valid URI scheme followed by a colon, e.g.
'http:', 'https:', 'file:', 'foo+bar:', it always interpreted as a
URL (even if Deno doesn't support the indicated protocol).
- Otherwise, the script name is interpreted as a local path. The local
path may be relative, and operating system semantics determine how
it is resolved. Prefixing a relative path with ./ is not required.
2019-07-08 03:55:24 -04:00
|
|
|
]);
|
|
|
|
|
|
|
|
// UNC network path.
|
|
|
|
let expected_url = "file://server/share/deno/cool";
|
|
|
|
tests.extend(vec![
|
|
|
|
(r"\\server\share\deno\cool", expected_url.to_string()),
|
|
|
|
(r"\\server/share/deno/cool", expected_url.to_string()),
|
|
|
|
// Not supported: `Url::from_file_path()` performs the wrong conversion.
|
|
|
|
// (r"//server/share/deno/cool", expected_url.to_string()),
|
|
|
|
]);
|
|
|
|
} else {
|
|
|
|
// Absolute local path.
|
|
|
|
let expected_url = "file:///deno/tests/006_url_imports.ts";
|
|
|
|
tests.extend(vec![
|
|
|
|
("/deno/tests/006_url_imports.ts", expected_url.to_string()),
|
|
|
|
("//deno/tests/006_url_imports.ts", expected_url.to_string()),
|
|
|
|
]);
|
|
|
|
|
|
|
|
// Relative local path.
|
2023-01-27 10:43:16 -05:00
|
|
|
let expected_url = format!("file://{cwd_str}/tests/006_url_imports.ts");
|
core: clearly define when module lookup is path-based vs URL-based
The rules are now as follows:
* In `import` statements, as mandated by the WHATWG specification,
the import specifier is always treated as a URL.
If it is a relative URL, it must start with either / or ./ or ../
* A script name passed to deno as a command line argument may be either
an absolute URL or a local path.
- If the name starts with a valid URI scheme followed by a colon, e.g.
'http:', 'https:', 'file:', 'foo+bar:', it always interpreted as a
URL (even if Deno doesn't support the indicated protocol).
- Otherwise, the script name is interpreted as a local path. The local
path may be relative, and operating system semantics determine how
it is resolved. Prefixing a relative path with ./ is not required.
2019-07-08 03:55:24 -04:00
|
|
|
tests.extend(vec![
|
|
|
|
("tests/006_url_imports.ts", expected_url.to_string()),
|
2020-07-16 04:53:07 -04:00
|
|
|
("./tests/006_url_imports.ts", expected_url.to_string()),
|
|
|
|
(
|
|
|
|
"tests/../tests/006_url_imports.ts",
|
|
|
|
expected_url.to_string(),
|
|
|
|
),
|
|
|
|
("tests/./006_url_imports.ts", expected_url),
|
core: clearly define when module lookup is path-based vs URL-based
The rules are now as follows:
* In `import` statements, as mandated by the WHATWG specification,
the import specifier is always treated as a URL.
If it is a relative URL, it must start with either / or ./ or ../
* A script name passed to deno as a command line argument may be either
an absolute URL or a local path.
- If the name starts with a valid URI scheme followed by a colon, e.g.
'http:', 'https:', 'file:', 'foo+bar:', it always interpreted as a
URL (even if Deno doesn't support the indicated protocol).
- Otherwise, the script name is interpreted as a local path. The local
path may be relative, and operating system semantics determine how
it is resolved. Prefixing a relative path with ./ is not required.
2019-07-08 03:55:24 -04:00
|
|
|
]);
|
|
|
|
}
|
|
|
|
|
|
|
|
for (specifier, expected_url) in tests {
|
2023-03-14 13:18:01 -04:00
|
|
|
let url = resolve_url_or_path(specifier, &cwd).unwrap().to_string();
|
core: clearly define when module lookup is path-based vs URL-based
The rules are now as follows:
* In `import` statements, as mandated by the WHATWG specification,
the import specifier is always treated as a URL.
If it is a relative URL, it must start with either / or ./ or ../
* A script name passed to deno as a command line argument may be either
an absolute URL or a local path.
- If the name starts with a valid URI scheme followed by a colon, e.g.
'http:', 'https:', 'file:', 'foo+bar:', it always interpreted as a
URL (even if Deno doesn't support the indicated protocol).
- Otherwise, the script name is interpreted as a local path. The local
path may be relative, and operating system semantics determine how
it is resolved. Prefixing a relative path with ./ is not required.
2019-07-08 03:55:24 -04:00
|
|
|
assert_eq!(url, expected_url);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
2023-03-13 21:12:09 -04:00
|
|
|
fn test_resolve_url_or_path_deprecated_error() {
|
core: clearly define when module lookup is path-based vs URL-based
The rules are now as follows:
* In `import` statements, as mandated by the WHATWG specification,
the import specifier is always treated as a URL.
If it is a relative URL, it must start with either / or ./ or ../
* A script name passed to deno as a command line argument may be either
an absolute URL or a local path.
- If the name starts with a valid URI scheme followed by a colon, e.g.
'http:', 'https:', 'file:', 'foo+bar:', it always interpreted as a
URL (even if Deno doesn't support the indicated protocol).
- Otherwise, the script name is interpreted as a local path. The local
path may be relative, and operating system semantics determine how
it is resolved. Prefixing a relative path with ./ is not required.
2019-07-08 03:55:24 -04:00
|
|
|
use url::ParseError::*;
|
|
|
|
use ModuleResolutionError::*;
|
|
|
|
|
|
|
|
let mut tests = vec![
|
|
|
|
("https://eggplant:b/c", InvalidUrl(InvalidPort)),
|
|
|
|
("https://:8080/a/b/c", InvalidUrl(EmptyHost)),
|
|
|
|
];
|
|
|
|
if cfg!(target_os = "windows") {
|
2019-07-30 10:34:57 -04:00
|
|
|
let p = r"\\.\c:/stuff/deno/script.ts";
|
|
|
|
tests.push((p, InvalidPath(PathBuf::from(p))));
|
core: clearly define when module lookup is path-based vs URL-based
The rules are now as follows:
* In `import` statements, as mandated by the WHATWG specification,
the import specifier is always treated as a URL.
If it is a relative URL, it must start with either / or ./ or ../
* A script name passed to deno as a command line argument may be either
an absolute URL or a local path.
- If the name starts with a valid URI scheme followed by a colon, e.g.
'http:', 'https:', 'file:', 'foo+bar:', it always interpreted as a
URL (even if Deno doesn't support the indicated protocol).
- Otherwise, the script name is interpreted as a local path. The local
path may be relative, and operating system semantics determine how
it is resolved. Prefixing a relative path with ./ is not required.
2019-07-08 03:55:24 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
for (specifier, expected_err) in tests {
|
2023-03-14 13:18:01 -04:00
|
|
|
let err =
|
|
|
|
resolve_url_or_path(specifier, &PathBuf::from("/")).unwrap_err();
|
core: clearly define when module lookup is path-based vs URL-based
The rules are now as follows:
* In `import` statements, as mandated by the WHATWG specification,
the import specifier is always treated as a URL.
If it is a relative URL, it must start with either / or ./ or ../
* A script name passed to deno as a command line argument may be either
an absolute URL or a local path.
- If the name starts with a valid URI scheme followed by a colon, e.g.
'http:', 'https:', 'file:', 'foo+bar:', it always interpreted as a
URL (even if Deno doesn't support the indicated protocol).
- Otherwise, the script name is interpreted as a local path. The local
path may be relative, and operating system semantics determine how
it is resolved. Prefixing a relative path with ./ is not required.
2019-07-08 03:55:24 -04:00
|
|
|
assert_eq!(err, expected_err);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_specifier_has_uri_scheme() {
|
|
|
|
let tests = vec![
|
|
|
|
("http://foo.bar/etc", true),
|
|
|
|
("HTTP://foo.bar/etc", true),
|
|
|
|
("http:ftp:", true),
|
|
|
|
("http:", true),
|
|
|
|
("hTtP:", true),
|
|
|
|
("ftp:", true),
|
|
|
|
("mailto:spam@please.me", true),
|
|
|
|
("git+ssh://git@github.com/denoland/deno", true),
|
|
|
|
("blob:https://whatwg.org/mumbojumbo", true),
|
|
|
|
("abc.123+DEF-ghi:", true),
|
|
|
|
("abc.123+def-ghi:@", true),
|
|
|
|
("", false),
|
|
|
|
(":not", false),
|
|
|
|
("http", false),
|
|
|
|
("c:dir", false),
|
|
|
|
("X:", false),
|
|
|
|
("./http://not", false),
|
|
|
|
("1abc://kinda/but/no", false),
|
|
|
|
("schluẞ://no/more", false),
|
|
|
|
];
|
|
|
|
|
|
|
|
for (specifier, expected) in tests {
|
2021-02-17 13:47:18 -05:00
|
|
|
let result = specifier_has_uri_scheme(specifier);
|
core: clearly define when module lookup is path-based vs URL-based
The rules are now as follows:
* In `import` statements, as mandated by the WHATWG specification,
the import specifier is always treated as a URL.
If it is a relative URL, it must start with either / or ./ or ../
* A script name passed to deno as a command line argument may be either
an absolute URL or a local path.
- If the name starts with a valid URI scheme followed by a colon, e.g.
'http:', 'https:', 'file:', 'foo+bar:', it always interpreted as a
URL (even if Deno doesn't support the indicated protocol).
- Otherwise, the script name is interpreted as a local path. The local
path may be relative, and operating system semantics determine how
it is resolved. Prefixing a relative path with ./ is not required.
2019-07-08 03:55:24 -04:00
|
|
|
assert_eq!(result, expected);
|
|
|
|
}
|
|
|
|
}
|
2020-07-16 04:53:07 -04:00
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_normalize_path() {
|
|
|
|
assert_eq!(normalize_path(Path::new("a/../b")), PathBuf::from("b"));
|
|
|
|
assert_eq!(normalize_path(Path::new("a/./b/")), PathBuf::from("a/b/"));
|
|
|
|
assert_eq!(
|
|
|
|
normalize_path(Path::new("a/./b/../c")),
|
|
|
|
PathBuf::from("a/c")
|
|
|
|
);
|
|
|
|
|
|
|
|
if cfg!(windows) {
|
|
|
|
assert_eq!(
|
|
|
|
normalize_path(Path::new("C:\\a\\.\\b\\..\\c")),
|
|
|
|
PathBuf::from("C:\\a\\c")
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
2021-01-31 22:30:41 -05:00
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_deserialize_module_specifier() {
|
|
|
|
let actual: ModuleSpecifier =
|
|
|
|
from_value(json!("http://deno.land/x/mod.ts")).unwrap();
|
2021-02-17 13:47:18 -05:00
|
|
|
let expected = resolve_url("http://deno.land/x/mod.ts").unwrap();
|
2021-01-31 22:30:41 -05:00
|
|
|
assert_eq!(actual, expected);
|
|
|
|
}
|
core: clearly define when module lookup is path-based vs URL-based
The rules are now as follows:
* In `import` statements, as mandated by the WHATWG specification,
the import specifier is always treated as a URL.
If it is a relative URL, it must start with either / or ./ or ../
* A script name passed to deno as a command line argument may be either
an absolute URL or a local path.
- If the name starts with a valid URI scheme followed by a colon, e.g.
'http:', 'https:', 'file:', 'foo+bar:', it always interpreted as a
URL (even if Deno doesn't support the indicated protocol).
- Otherwise, the script name is interpreted as a local path. The local
path may be relative, and operating system semantics determine how
it is resolved. Prefixing a relative path with ./ is not required.
2019-07-08 03:55:24 -04:00
|
|
|
}
|