summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--cli/compiler.rs8
-rw-r--r--cli/flags.rs4
-rw-r--r--cli/import_map.rs642
-rw-r--r--cli/main.rs90
-rw-r--r--cli/module_specifier.rs81
-rw-r--r--cli/ops.rs15
-rw-r--r--cli/state.rs116
-rw-r--r--cli/worker.rs108
8 files changed, 520 insertions, 544 deletions
diff --git a/cli/compiler.rs b/cli/compiler.rs
index 4233262a3..a33ccfcb0 100644
--- a/cli/compiler.rs
+++ b/cli/compiler.rs
@@ -247,8 +247,8 @@ mod tests {
fn test_compile_sync() {
tokio_util::init(|| {
let specifier = "./tests/002_hello.ts";
- use crate::worker;
- let module_name = worker::root_specifier_to_url(specifier)
+ use crate::module_specifier::ModuleSpecifier;
+ let module_name = ModuleSpecifier::resolve_root(specifier)
.unwrap()
.to_string();
@@ -294,8 +294,8 @@ mod tests {
#[test]
fn test_bundle_async() {
let specifier = "./tests/002_hello.ts";
- use crate::worker;
- let module_name = worker::root_specifier_to_url(specifier)
+ use crate::module_specifier::ModuleSpecifier;
+ let module_name = ModuleSpecifier::resolve_root(specifier)
.unwrap()
.to_string();
diff --git a/cli/flags.rs b/cli/flags.rs
index 41d3f9308..6d4a95f9e 100644
--- a/cli/flags.rs
+++ b/cli/flags.rs
@@ -502,8 +502,8 @@ pub enum DenoSubcommand {
}
fn get_default_bundle_filename(source_file: &str) -> String {
- use crate::worker::root_specifier_to_url;
- let url = root_specifier_to_url(source_file).unwrap();
+ use crate::module_specifier::ModuleSpecifier;
+ let url = ModuleSpecifier::resolve_root(source_file).unwrap().to_url();
let path_segments = url.path_segments().unwrap();
let last = path_segments.last().unwrap();
String::from(last.trim_end_matches(".ts").trim_end_matches(".js"))
diff --git a/cli/import_map.rs b/cli/import_map.rs
index 5fe423595..6f4095a90 100644
--- a/cli/import_map.rs
+++ b/cli/import_map.rs
@@ -1,3 +1,4 @@
+use crate::module_specifier::ModuleSpecifier;
use indexmap::IndexMap;
use serde_json::Map;
use serde_json::Value;
@@ -22,7 +23,7 @@ impl ImportMapError {
// can't resolve URL with other schemes (eg. data:, about:, blob:)
const SUPPORTED_FETCH_SCHEMES: [&str; 3] = ["http", "https", "file"];
-type SpecifierMap = IndexMap<String, Vec<String>>;
+type SpecifierMap = IndexMap<String, Vec<ModuleSpecifier>>;
type ScopesMap = IndexMap<String, SpecifierMap>;
#[derive(Debug)]
@@ -157,8 +158,8 @@ impl ImportMap {
specifier_key: &str,
base_url: &str,
potential_addresses: Vec<String>,
- ) -> Vec<String> {
- let mut normalized_addresses: Vec<String> = vec![];
+ ) -> Vec<ModuleSpecifier> {
+ let mut normalized_addresses: Vec<ModuleSpecifier> = vec![];
for potential_address in potential_addresses {
let url =
@@ -177,7 +178,9 @@ impl ImportMap {
continue;
}
- normalized_addresses.push(url_string);
+ let normalized_address = ModuleSpecifier::resolve(&url_string, ".")
+ .expect("Address should be valid module specifier");
+ normalized_addresses.push(normalized_address);
}
normalized_addresses
@@ -311,7 +314,7 @@ impl ImportMap {
scopes: &ScopesMap,
normalized_specifier: &str,
referrer: &str,
- ) -> Result<Option<String>, ImportMapError> {
+ ) -> Result<Option<ModuleSpecifier>, ImportMapError> {
// exact-match
if let Some(scope_imports) = scopes.get(referrer) {
if let Ok(scope_match) =
@@ -347,7 +350,7 @@ impl ImportMap {
pub fn resolve_imports_match(
imports: &SpecifierMap,
normalized_specifier: &str,
- ) -> Result<Option<String>, ImportMapError> {
+ ) -> Result<Option<ModuleSpecifier>, ImportMapError> {
// exact-match
if let Some(address_vec) = imports.get(normalized_specifier) {
if address_vec.is_empty() {
@@ -361,7 +364,7 @@ impl ImportMap {
"Specifier {:?} was mapped to {:?}.",
normalized_specifier, address
);
- return Ok(Some(address.to_string()));
+ return Ok(Some(address.clone()));
} else {
return Err(ImportMapError::new(
"Multi-address mappings are not yet supported",
@@ -383,12 +386,10 @@ impl ImportMap {
let address = address_vec.first().unwrap();
let after_prefix = &normalized_specifier[specifier_key.len()..];
- if let Ok(base_url) = Url::parse(address) {
- if let Ok(url) = base_url.join(after_prefix) {
- let resolved_url = url.to_string();
- debug!("Specifier {:?} was mapped to {:?} (via prefix specifier key {:?}).", normalized_specifier, resolved_url, address);
- return Ok(Some(resolved_url));
- }
+ let base_url = address.to_url();
+ if let Ok(url) = base_url.join(after_prefix) {
+ debug!("Specifier {:?} was mapped to {:?} (via prefix specifier key {:?}).", normalized_specifier, url, address);
+ return Ok(Some(ModuleSpecifier::from(url)));
}
unreachable!();
@@ -420,7 +421,7 @@ impl ImportMap {
&self,
specifier: &str,
referrer: &str,
- ) -> Result<Option<String>, ImportMapError> {
+ ) -> Result<Option<ModuleSpecifier>, ImportMapError> {
let resolved_url: Option<Url> =
ImportMap::try_url_like_specifier(specifier, referrer);
let normalized_specifier = match &resolved_url {
@@ -449,7 +450,7 @@ impl ImportMap {
// no match in import map but we got resolvable URL
if let Some(resolved_url) = resolved_url {
- return Ok(Some(resolved_url.to_string()));
+ return Ok(Some(ModuleSpecifier::from(resolved_url)));
}
Err(ImportMapError::new(&format!(
@@ -1250,43 +1251,52 @@ mod tests {
}
}
+ fn assert_resolve(
+ result: Result<Option<ModuleSpecifier>, ImportMapError>,
+ expected_url: &str,
+ ) {
+ let maybe_url = result
+ .unwrap_or_else(|err| panic!("ImportMap::resolve failed: {:?}", err));
+ let resolved_url =
+ maybe_url.unwrap_or_else(|| panic!("Unexpected None resolved URL"));
+ assert_eq!(resolved_url, expected_url.to_string());
+ }
+
#[test]
fn resolve_unmapped_relative_specifiers() {
let referrer_url = "https://example.com/js/script.ts";
let import_map = get_empty_import_map();
// Should resolve ./ specifiers as URLs.
- assert_eq!(
- import_map.resolve("./foo", referrer_url).unwrap(),
- Some("https://example.com/js/foo".to_string())
+ assert_resolve(
+ import_map.resolve("./foo", referrer_url),
+ "https://example.com/js/foo",
);
- assert_eq!(
- import_map.resolve("./foo/bar", referrer_url).unwrap(),
- Some("https://example.com/js/foo/bar".to_string())
+ assert_resolve(
+ import_map.resolve("./foo/bar", referrer_url),
+ "https://example.com/js/foo/bar",
);
- assert_eq!(
- import_map.resolve("./foo/../bar", referrer_url).unwrap(),
- Some("https://example.com/js/bar".to_string())
+ assert_resolve(
+ import_map.resolve("./foo/../bar", referrer_url),
+ "https://example.com/js/bar",
);
- assert_eq!(
- import_map.resolve("./foo/../../bar", referrer_url).unwrap(),
- Some("https://example.com/bar".to_string())
+ assert_resolve(
+ import_map.resolve("./foo/../../bar", referrer_url),
+ "https://example.com/bar",
);
// Should resolve ../ specifiers as URLs.
- assert_eq!(
- import_map.resolve("../foo", referrer_url).unwrap(),
- Some("https://example.com/foo".to_string())
+ assert_resolve(
+ import_map.resolve("../foo", referrer_url),
+ "https://example.com/foo",
);
- assert_eq!(
- import_map.resolve("../foo/bar", referrer_url).unwrap(),
- Some("https://example.com/foo/bar".to_string())
+ assert_resolve(
+ import_map.resolve("../foo/bar", referrer_url),
+ "https://example.com/foo/bar",
);
- assert_eq!(
- import_map
- .resolve("../../../foo/bar", referrer_url)
- .unwrap(),
- Some("https://example.com/foo/bar".to_string())
+ assert_resolve(
+ import_map.resolve("../../../foo/bar", referrer_url),
+ "https://example.com/foo/bar",
);
}
@@ -1296,47 +1306,39 @@ mod tests {
let import_map = get_empty_import_map();
// Should resolve / specifiers as URLs.
- assert_eq!(
- import_map.resolve("/foo", referrer_url).unwrap(),
- Some("https://example.com/foo".to_string())
+ assert_resolve(
+ import_map.resolve("/foo", referrer_url),
+ "https://example.com/foo",
);
- assert_eq!(
- import_map.resolve("/foo/bar", referrer_url).unwrap(),
- Some("https://example.com/foo/bar".to_string())
+ assert_resolve(
+ import_map.resolve("/foo/bar", referrer_url),
+ "https://example.com/foo/bar",
);
- assert_eq!(
- import_map.resolve("../../foo/bar", referrer_url).unwrap(),
- Some("https://example.com/foo/bar".to_string())
+ assert_resolve(
+ import_map.resolve("../../foo/bar", referrer_url),
+ "https://example.com/foo/bar",
);
- assert_eq!(
- import_map.resolve("/../foo/../bar", referrer_url).unwrap(),
- Some("https://example.com/bar".to_string())
+ assert_resolve(
+ import_map.resolve("/../foo/../bar", referrer_url),
+ "https://example.com/bar",
);
// Should parse absolute fetch-scheme URLs.
- assert_eq!(
- import_map
- .resolve("https://example.net", referrer_url)
- .unwrap(),
- Some("https://example.net/".to_string())
+ assert_resolve(
+ import_map.resolve("https://example.net", referrer_url),
+ "https://example.net/",
);
- assert_eq!(
- import_map
- .resolve("https://ex%41mple.com/", referrer_url)
- .unwrap(),
- Some("https://example.com/".to_string())
+ assert_resolve(
+ import_map.resolve("https://ex%41mple.com/", referrer_url),
+ "https://example.com/",
);
- assert_eq!(
- import_map
- .resolve("https:example.org", referrer_url)
- .unwrap(),
- Some("https://example.org/".to_string())
+ assert_resolve(
+ import_map.resolve("https:example.org", referrer_url),
+ "https://example.org/",
);
- assert_eq!(
- import_map
- .resolve("https://///example.com///", referrer_url)
- .unwrap(),
- Some("https://example.com///".to_string())
+ assert_resolve(
+ import_map.resolve("https://///example.com///", referrer_url),
+ "https://example.com///",
);
}
@@ -1414,39 +1416,37 @@ mod tests {
let import_map = ImportMap::from_json(base_url, json_map).unwrap();
// Should work for package main modules.
- assert_eq!(
- import_map.resolve("moment", referrer_url).unwrap(),
- Some("https://example.com/deps/moment/src/moment.js".to_string())
+ assert_resolve(
+ import_map.resolve("moment", referrer_url),
+ "https://example.com/deps/moment/src/moment.js",
);
- assert_eq!(
- import_map.resolve("lodash-dot", referrer_url).unwrap(),
- Some("https://example.com/app/deps/lodash-es/lodash.js".to_string())
+ assert_resolve(
+ import_map.resolve("lodash-dot", referrer_url),
+ "https://example.com/app/deps/lodash-es/lodash.js",
);
- assert_eq!(
- import_map.resolve("lodash-dotdot", referrer_url).unwrap(),
- Some("https://example.com/deps/lodash-es/lodash.js".to_string())
+ assert_resolve(
+ import_map.resolve("lodash-dotdot", referrer_url),
+ "https://example.com/deps/lodash-es/lodash.js",
);
// Should work for package submodules.
- assert_eq!(
- import_map.resolve("moment/foo", referrer_url).unwrap(),
- Some("https://example.com/deps/moment/src/foo".to_string())
+ assert_resolve(
+ import_map.resolve("moment/foo", referrer_url),
+ "https://example.com/deps/moment/src/foo",
);
- assert_eq!(
- import_map.resolve("lodash-dot/foo", referrer_url).unwrap(),
- Some("https://example.com/app/deps/lodash-es/foo".to_string())
+ assert_resolve(
+ import_map.resolve("lodash-dot/foo", referrer_url),
+ "https://example.com/app/deps/lodash-es/foo",
);
- assert_eq!(
- import_map
- .resolve("lodash-dotdot/foo", referrer_url)
- .unwrap(),
- Some("https://example.com/deps/lodash-es/foo".to_string())
+ assert_resolve(
+ import_map.resolve("lodash-dotdot/foo", referrer_url),
+ "https://example.com/deps/lodash-es/foo",
);
// Should work for package names that end in a slash.
- assert_eq!(
- import_map.resolve("moment/", referrer_url).unwrap(),
- Some("https://example.com/deps/moment/src/".to_string())
+ assert_resolve(
+ import_map.resolve("moment/", referrer_url),
+ "https://example.com/deps/moment/src/",
);
// Should fail for package modules that are not declared.
@@ -1476,33 +1476,31 @@ mod tests {
let import_map = ImportMap::from_json(base_url, json_map).unwrap();
// Should work for explicitly-mapped specifiers that happen to have a slash.
- assert_eq!(
- import_map
- .resolve("package/withslash", referrer_url)
- .unwrap(),
- Some("https://example.com/deps/package-with-slash/index.mjs".to_string())
+ assert_resolve(
+ import_map.resolve("package/withslash", referrer_url),
+ "https://example.com/deps/package-with-slash/index.mjs",
);
// Should work when the specifier has punctuation.
- assert_eq!(
- import_map.resolve(".", referrer_url).unwrap(),
- Some("https://example.com/lib/dot.mjs".to_string())
+ assert_resolve(
+ import_map.resolve(".", referrer_url),
+ "https://example.com/lib/dot.mjs",
);
- assert_eq!(
- import_map.resolve("..", referrer_url).unwrap(),
- Some("https://example.com/lib/dotdot.mjs".to_string())
+ assert_resolve(
+ import_map.resolve("..", referrer_url),
+ "https://example.com/lib/dotdot.mjs",
);
- assert_eq!(
- import_map.resolve("..\\\\", referrer_url).unwrap(),
- Some("https://example.com/lib/dotdotbackslash.mjs".to_string())
+ assert_resolve(
+ import_map.resolve("..\\\\", referrer_url),
+ "https://example.com/lib/dotdotbackslash.mjs",
);
- assert_eq!(
- import_map.resolve("%2E", referrer_url).unwrap(),
- Some("https://example.com/lib/percent2e.mjs".to_string())
+ assert_resolve(
+ import_map.resolve("%2E", referrer_url),
+ "https://example.com/lib/percent2e.mjs",
);
- assert_eq!(
- import_map.resolve("%2F", referrer_url).unwrap(),
- Some("https://example.com/lib/percent2f.mjs".to_string())
+ assert_resolve(
+ import_map.resolve("%2F", referrer_url),
+ "https://example.com/lib/percent2f.mjs",
);
// Should fail for attempting to get a submodule of something not declared with a trailing slash.
@@ -1537,45 +1535,35 @@ mod tests {
let import_map = ImportMap::from_json(base_url, json_map).unwrap();
// Should remap to other URLs.
- assert_eq!(
- import_map
- .resolve("https://example.com/lib/foo.mjs", referrer_url)
- .unwrap(),
- Some("https://example.com/app/more/bar.mjs".to_string())
+ assert_resolve(
+ import_map.resolve("https://example.com/lib/foo.mjs", referrer_url),
+ "https://example.com/app/more/bar.mjs",
);
- assert_eq!(
- import_map
- .resolve("https://///example.com/lib/foo.mjs", referrer_url)
- .unwrap(),
- Some("https://example.com/app/more/bar.mjs".to_string())
+ assert_resolve(
+ import_map.resolve("https://///example.com/lib/foo.mjs", referrer_url),
+ "https://example.com/app/more/bar.mjs",
);
- assert_eq!(
- import_map.resolve("/lib/foo.mjs", referrer_url).unwrap(),
- Some("https://example.com/app/more/bar.mjs".to_string())
+ assert_resolve(
+ import_map.resolve("/lib/foo.mjs", referrer_url),
+ "https://example.com/app/more/bar.mjs",
);
- assert_eq!(
+ assert_resolve(
import_map
- .resolve("https://example.com/app/dotrelative/foo.mjs", referrer_url)
- .unwrap(),
- Some("https://example.com/lib/dot.mjs".to_string())
+ .resolve("https://example.com/app/dotrelative/foo.mjs", referrer_url),
+ "https://example.com/lib/dot.mjs",
);
- assert_eq!(
- import_map
- .resolve("../app/dotrelative/foo.mjs", referrer_url)
- .unwrap(),
- Some("https://example.com/lib/dot.mjs".to_string())
+ assert_resolve(
+ import_map.resolve("../app/dotrelative/foo.mjs", referrer_url),
+ "https://example.com/lib/dot.mjs",
);
- assert_eq!(
+ assert_resolve(
import_map
- .resolve("https://example.com/dotdotrelative/foo.mjs", referrer_url)
- .unwrap(),
- Some("https://example.com/lib/dotdot.mjs".to_string())
+ .resolve("https://example.com/dotdotrelative/foo.mjs", referrer_url),
+ "https://example.com/lib/dotdot.mjs",
);
- assert_eq!(
- import_map
- .resolve("../dotdotrelative/foo.mjs", referrer_url)
- .unwrap(),
- Some("https://example.com/lib/dotdot.mjs".to_string())
+ assert_resolve(
+ import_map.resolve("../dotdotrelative/foo.mjs", referrer_url),
+ "https://example.com/lib/dotdot.mjs",
);
// Should fail for URLs that remap to empty arrays.
@@ -1603,55 +1591,47 @@ mod tests {
);
// Should remap URLs that are just composed from / and ..
- assert_eq!(
- import_map
- .resolve("https://example.com/", referrer_url)
- .unwrap(),
- Some("https://example.com/lib/slash-only/".to_string())
+ assert_resolve(
+ import_map.resolve("https://example.com/", referrer_url),
+ "https://example.com/lib/slash-only/",
);
- assert_eq!(
- import_map.resolve("/", referrer_url).unwrap(),
- Some("https://example.com/lib/slash-only/".to_string())
+ assert_resolve(
+ import_map.resolve("/", referrer_url),
+ "https://example.com/lib/slash-only/",
);
- assert_eq!(
- import_map.resolve("../", referrer_url).unwrap(),
- Some("https://example.com/lib/slash-only/".to_string())
+ assert_resolve(
+ import_map.resolve("../", referrer_url),
+ "https://example.com/lib/slash-only/",
);
- assert_eq!(
- import_map
- .resolve("https://example.com/app/", referrer_url)
- .unwrap(),
- Some("https://example.com/lib/dotslash-only/".to_string())
+ assert_resolve(
+ import_map.resolve("https://example.com/app/", referrer_url),
+ "https://example.com/lib/dotslash-only/",
);
- assert_eq!(
- import_map.resolve("/app/", referrer_url).unwrap(),
- Some("https://example.com/lib/dotslash-only/".to_string())
+ assert_resolve(
+ import_map.resolve("/app/", referrer_url),
+ "https://example.com/lib/dotslash-only/",
);
- assert_eq!(
- import_map.resolve("../app/", referrer_url).unwrap(),
- Some("https://example.com/lib/dotslash-only/".to_string())
+ assert_resolve(
+ import_map.resolve("../app/", referrer_url),
+ "https://example.com/lib/dotslash-only/",
);
// Should remap URLs that are prefix-matched by keys with trailing slashes.
- assert_eq!(
- import_map.resolve("/test/foo.mjs", referrer_url).unwrap(),
- Some("https://example.com/lib/url-trailing-slash/foo.mjs".to_string())
+ assert_resolve(
+ import_map.resolve("/test/foo.mjs", referrer_url),
+ "https://example.com/lib/url-trailing-slash/foo.mjs",
);
- assert_eq!(
- import_map
- .resolve("https://example.com/app/test/foo.mjs", referrer_url)
- .unwrap(),
- Some(
- "https://example.com/lib/url-trailing-slash-dot/foo.mjs".to_string()
- )
+ assert_resolve(
+ import_map.resolve("https://example.com/app/test/foo.mjs", referrer_url),
+ "https://example.com/lib/url-trailing-slash-dot/foo.mjs",
);
// Should use the last entry's address when URL-like specifiers parse to the same absolute URL.
//
// NOTE: this works properly because of "preserve_order" feature flag to "serde_json" crate
- assert_eq!(
- import_map.resolve("/test", referrer_url).unwrap(),
- Some("https://example.com/lib/test2.mjs".to_string())
+ assert_resolve(
+ import_map.resolve("/test", referrer_url),
+ "https://example.com/lib/test2.mjs",
);
}
@@ -1672,25 +1652,25 @@ mod tests {
}"#;
let import_map = ImportMap::from_json(base_url, json_map).unwrap();
- assert_eq!(
- import_map.resolve("a", referrer_url).unwrap(),
- Some("https://example.com/1".to_string())
+ assert_resolve(
+ import_map.resolve("a", referrer_url),
+ "https://example.com/1",
);
- assert_eq!(
- import_map.resolve("a/", referrer_url).unwrap(),
- Some("https://example.com/2/".to_string())
+ assert_resolve(
+ import_map.resolve("a/", referrer_url),
+ "https://example.com/2/",
);
- assert_eq!(
- import_map.resolve("a/b", referrer_url).unwrap(),
- Some("https://example.com/3".to_string())
+ assert_resolve(
+ import_map.resolve("a/b", referrer_url),
+ "https://example.com/3",
);
- assert_eq!(
- import_map.resolve("a/b/", referrer_url).unwrap(),
- Some("https://example.com/4/".to_string())
+ assert_resolve(
+ import_map.resolve("a/b/", referrer_url),
+ "https://example.com/4/",
);
- assert_eq!(
- import_map.resolve("a/b/c", referrer_url).unwrap(),
- Some("https://example.com/4/c".to_string())
+ assert_resolve(
+ import_map.resolve("a/b/c", referrer_url),
+ "https://example.com/4/c",
);
}
@@ -1709,17 +1689,17 @@ mod tests {
assert!(import_map.resolve("a", referrer_url).is_err());
assert!(import_map.resolve("a/", referrer_url).is_err());
assert!(import_map.resolve("a/x", referrer_url).is_err());
- assert_eq!(
- import_map.resolve("a/b", referrer_url).unwrap(),
- Some("https://example.com/3".to_string())
+ assert_resolve(
+ import_map.resolve("a/b", referrer_url),
+ "https://example.com/3",
);
- assert_eq!(
- import_map.resolve("a/b/", referrer_url).unwrap(),
- Some("https://example.com/4/".to_string())
+ assert_resolve(
+ import_map.resolve("a/b/", referrer_url),
+ "https://example.com/4/",
);
- assert_eq!(
- import_map.resolve("a/b/c", referrer_url).unwrap(),
- Some("https://example.com/4/c".to_string())
+ assert_resolve(
+ import_map.resolve("a/b/c", referrer_url),
+ "https://example.com/4/c",
);
assert!(import_map.resolve("a/x/c", referrer_url).is_err());
}
@@ -1766,25 +1746,21 @@ mod tests {
let js_in_dir = "https://example.com/js/app.mjs";
let with_js_prefix = "https://example.com/jsiscool";
- assert_eq!(
- import_map.resolve("moment", js_non_dir).unwrap(),
- Some("https://example.com/only-triggered-by-exact/moment".to_string())
+ assert_resolve(
+ import_map.resolve("moment", js_non_dir),
+ "https://example.com/only-triggered-by-exact/moment",
);
- assert_eq!(
- import_map.resolve("moment/foo", js_non_dir).unwrap(),
- Some(
- "https://example.com/only-triggered-by-exact/moment/foo".to_string()
- )
+ assert_resolve(
+ import_map.resolve("moment/foo", js_non_dir),
+ "https://example.com/only-triggered-by-exact/moment/foo",
);
- assert_eq!(
- import_map.resolve("moment", js_in_dir).unwrap(),
- Some("https://example.com/triggered-by-any-subpath/moment".to_string())
+ assert_resolve(
+ import_map.resolve("moment", js_in_dir),
+ "https://example.com/triggered-by-any-subpath/moment",
);
- assert_eq!(
- import_map.resolve("moment/foo", js_in_dir).unwrap(),
- Some(
- "https://example.com/triggered-by-any-subpath/moment/foo".to_string()
- )
+ assert_resolve(
+ import_map.resolve("moment/foo", js_in_dir),
+ "https://example.com/triggered-by-any-subpath/moment/foo",
);
assert!(import_map.resolve("moment", with_js_prefix).is_err());
assert!(import_map.resolve("moment/foo", with_js_prefix).is_err());
@@ -1809,15 +1785,13 @@ mod tests {
let js_in_dir = "https://example.com/js/app.mjs";
let with_js_prefix = "https://example.com/jsiscool";
- assert_eq!(
- import_map.resolve("moment", js_non_dir).unwrap(),
- Some("https://example.com/only-triggered-by-exact/moment".to_string())
+ assert_resolve(
+ import_map.resolve("moment", js_non_dir),
+ "https://example.com/only-triggered-by-exact/moment",
);
- assert_eq!(
- import_map.resolve("moment/foo", js_non_dir).unwrap(),
- Some(
- "https://example.com/only-triggered-by-exact/moment/foo".to_string()
- )
+ assert_resolve(
+ import_map.resolve("moment/foo", js_non_dir),
+ "https://example.com/only-triggered-by-exact/moment/foo",
);
assert!(import_map.resolve("moment", js_in_dir).is_err());
assert!(import_map.resolve("moment/foo", js_in_dir).is_err());
@@ -1846,15 +1820,13 @@ mod tests {
assert!(import_map.resolve("moment", js_non_dir).is_err());
assert!(import_map.resolve("moment/foo", js_non_dir).is_err());
- assert_eq!(
- import_map.resolve("moment", js_in_dir).unwrap(),
- Some("https://example.com/triggered-by-any-subpath/moment".to_string())
+ assert_resolve(
+ import_map.resolve("moment", js_in_dir),
+ "https://example.com/triggered-by-any-subpath/moment",
);
- assert_eq!(
- import_map.resolve("moment/foo", js_in_dir).unwrap(),
- Some(
- "https://example.com/triggered-by-any-subpath/moment/foo".to_string()
- )
+ assert_resolve(
+ import_map.resolve("moment/foo", js_in_dir),
+ "https://example.com/triggered-by-any-subpath/moment/foo",
);
assert!(import_map.resolve("moment", with_js_prefix).is_err());
assert!(import_map.resolve("moment/foo", with_js_prefix).is_err());
@@ -1893,75 +1865,61 @@ mod tests {
let top_level = "https://example.com/app.mjs";
// Should resolve scoped.
- assert_eq!(
- import_map.resolve("lodash-dot", js_in_dir).unwrap(),
- Some(
- "https://example.com/app/node_modules_2/lodash-es/lodash.js"
- .to_string()
- )
+ assert_resolve(
+ import_map.resolve("lodash-dot", js_in_dir),
+ "https://example.com/app/node_modules_2/lodash-es/lodash.js",
);
- assert_eq!(
- import_map.resolve("lodash-dotdot", js_in_dir).unwrap(),
- Some(
- "https://example.com/node_modules_2/lodash-es/lodash.js".to_string()
- )
+ assert_resolve(
+ import_map.resolve("lodash-dotdot", js_in_dir),
+ "https://example.com/node_modules_2/lodash-es/lodash.js",
);
- assert_eq!(
- import_map.resolve("lodash-dot/foo", js_in_dir).unwrap(),
- Some("https://example.com/app/node_modules_2/lodash-es/foo".to_string())
+ assert_resolve(
+ import_map.resolve("lodash-dot/foo", js_in_dir),
+ "https://example.com/app/node_modules_2/lodash-es/foo",
);
- assert_eq!(
- import_map.resolve("lodash-dotdot/foo", js_in_dir).unwrap(),
- Some("https://example.com/node_modules_2/lodash-es/foo".to_string())
+ assert_resolve(
+ import_map.resolve("lodash-dotdot/foo", js_in_dir),
+ "https://example.com/node_modules_2/lodash-es/foo",
);
// Should apply best scope match.
- assert_eq!(
- import_map.resolve("moment", top_level).unwrap(),
- Some(
- "https://example.com/node_modules_3/moment/src/moment.js".to_string()
- )
+ assert_resolve(
+ import_map.resolve("moment", top_level),
+ "https://example.com/node_modules_3/moment/src/moment.js",
);
- assert_eq!(
- import_map.resolve("moment", js_in_dir).unwrap(),
- Some(
- "https://example.com/node_modules_3/moment/src/moment.js".to_string()
- )
+ assert_resolve(
+ import_map.resolve("moment", js_in_dir),
+ "https://example.com/node_modules_3/moment/src/moment.js",
);
- assert_eq!(
- import_map.resolve("vue", js_in_dir).unwrap(),
- Some(
- "https://example.com/node_modules_3/vue/dist/vue.runtime.esm.js"
- .to_string()
- )
+ assert_resolve(
+ import_map.resolve("vue", js_in_dir),
+ "https://example.com/node_modules_3/vue/dist/vue.runtime.esm.js",
);
// Should fallback to "imports".
- assert_eq!(
- import_map.resolve("moment/foo", top_level).unwrap(),
- Some("https://example.com/node_modules/moment/src/foo".to_string())
+ assert_resolve(
+ import_map.resolve("moment/foo", top_level),
+ "https://example.com/node_modules/moment/src/foo",
);
- assert_eq!(
- import_map.resolve("moment/foo", js_in_dir).unwrap(),
- Some("https://example.com/node_modules/moment/src/foo".to_string())
+ assert_resolve(
+ import_map.resolve("moment/foo", js_in_dir),
+ "https://example.com/node_modules/moment/src/foo",
);
- assert_eq!(
- import_map.resolve("lodash-dot", top_level).unwrap(),
- Some(
- "https://example.com/app/node_modules/lodash-es/lodash.js".to_string()
- )
+ assert_resolve(
+ import_map.resolve("lodash-dot", top_level),
+ "https://example.com/app/node_modules/lodash-es/lodash.js",
);
- assert_eq!(
- import_map.resolve("lodash-dotdot", top_level).unwrap(),
- Some("https://example.com/node_modules/lodash-es/lodash.js".to_string())
+ assert_resolve(
+ import_map.resolve("lodash-dotdot", top_level),
+ "https://example.com/node_modules/lodash-es/lodash.js",
);
- assert_eq!(
- import_map.resolve("lodash-dot/foo", top_level).unwrap(),
- Some("https://example.com/app/node_modules/lodash-es/foo".to_string())
+ assert_resolve(
+ import_map.resolve("lodash-dot/foo", top_level),
+ "https://example.com/app/node_modules/lodash-es/foo",
);
- assert_eq!(
- import_map.resolve("lodash-dotdot/foo", top_level).unwrap(),
- Some("https://example.com/node_modules/lodash-es/foo".to_string())
+ assert_resolve(
+ import_map.resolve("lodash-dotdot/foo", top_level),
+ "https://example.com/node_modules/lodash-es/foo",
);
// Should still fail for package-like specifiers that are not declared.
@@ -1996,45 +1954,45 @@ mod tests {
let scope_3_url = "https://example.com/scope2/scope3/foo.mjs";
// Should fall back to "imports" when none match.
- assert_eq!(
- import_map.resolve("a", scope_1_url).unwrap(),
- Some("https://example.com/a-1.mjs".to_string())
+ assert_resolve(
+ import_map.resolve("a", scope_1_url),
+ "https://example.com/a-1.mjs",
);
- assert_eq!(
- import_map.resolve("b", scope_1_url).unwrap(),
- Some("https://example.com/b-1.mjs".to_string())
+ assert_resolve(
+ import_map.resolve("b", scope_1_url),
+ "https://example.com/b-1.mjs",
);
- assert_eq!(
- import_map.resolve("c", scope_1_url).unwrap(),
- Some("https://example.com/c-1.mjs".to_string())
+ assert_resolve(
+ import_map.resolve("c", scope_1_url),
+ "https://example.com/c-1.mjs",
);
// Should use a direct scope override.
- assert_eq!(
- import_map.resolve("a", scope_2_url).unwrap(),
- Some("https://example.com/a-2.mjs".to_string())
+ assert_resolve(
+ import_map.resolve("a", scope_2_url),
+ "https://example.com/a-2.mjs",
);
- assert_eq!(
- import_map.resolve("b", scope_2_url).unwrap(),
- Some("https://example.com/b-1.mjs".to_string())
+ assert_resolve(
+ import_map.resolve("b", scope_2_url),
+ "https://example.com/b-1.mjs",
);
- assert_eq!(
- import_map.resolve("c", scope_2_url).unwrap(),
- Some("https://example.com/c-1.mjs".to_string())
+ assert_resolve(
+ import_map.resolve("c", scope_2_url),
+ "https://example.com/c-1.mjs",
);
// Should use an indirect scope override.
- assert_eq!(
- import_map.resolve("a", scope_3_url).unwrap(),
- Some("https://example.com/a-2.mjs".to_string())
+ assert_resolve(
+ import_map.resolve("a", scope_3_url),
+ "https://example.com/a-2.mjs",
);
- assert_eq!(
- import_map.resolve("b", scope_3_url).unwrap(),
- Some("https://example.com/b-3.mjs".to_string())
+ assert_resolve(
+ import_map.resolve("b", scope_3_url),
+ "https://example.com/b-3.mjs",
);
- assert_eq!(
- import_map.resolve("c", scope_3_url).unwrap(),
- Some("https://example.com/c-1.mjs".to_string())
+ assert_resolve(
+ import_map.resolve("c", scope_3_url),
+ "https://example.com/c-1.mjs",
);
}
@@ -2066,37 +2024,37 @@ mod tests {
let in_dir_above_map = "https://example.com/foo.mjs";
// Should resolve an empty string scope using the import map URL.
- assert_eq!(
- import_map.resolve("a", base_url).unwrap(),
- Some("https://example.com/a-empty-string.mjs".to_string())
+ assert_resolve(
+ import_map.resolve("a", base_url),
+ "https://example.com/a-empty-string.mjs",
);
- assert_eq!(
- import_map.resolve("a", in_same_dir_as_map).unwrap(),
- Some("https://example.com/a-1.mjs".to_string())
+ assert_resolve(
+ import_map.resolve("a", in_same_dir_as_map),
+ "https://example.com/a-1.mjs",
);
// Should resolve a ./ scope using the import map URL's directory.
- assert_eq!(
- import_map.resolve("b", base_url).unwrap(),
- Some("https://example.com/b-dot-slash.mjs".to_string())
+ assert_resolve(
+ import_map.resolve("b", base_url),
+ "https://example.com/b-dot-slash.mjs",
);
- assert_eq!(
- import_map.resolve("b", in_same_dir_as_map).unwrap(),
- Some("https://example.com/b-dot-slash.mjs".to_string())
+ assert_resolve(
+ import_map.resolve("b", in_same_dir_as_map),
+ "https://example.com/b-dot-slash.mjs",
);
// Should resolve a ../ scope using the import map URL's directory.
- assert_eq!(
- import_map.resolve("c", base_url).unwrap(),
- Some("https://example.com/c-dot-dot-slash.mjs".to_string())
+ assert_resolve(
+ import_map.resolve("c", base_url),
+ "https://example.com/c-dot-dot-slash.mjs",
);
- assert_eq!(
- import_map.resolve("c", in_same_dir_as_map).unwrap(),
- Some("https://example.com/c-dot-dot-slash.mjs".to_string())
+ assert_resolve(
+ import_map.resolve("c", in_same_dir_as_map),
+ "https://example.com/c-dot-dot-slash.mjs",
);
- assert_eq!(
- import_map.resolve("c", in_dir_above_map).unwrap(),
- Some("https://example.com/c-dot-dot-slash.mjs".to_string())
+ assert_resolve(
+ import_map.resolve("c", in_dir_above_map),
+ "https://example.com/c-dot-dot-slash.mjs",
);
}
@@ -2121,13 +2079,13 @@ mod tests {
}"#;
let import_map = ImportMap::from_json(base_url, json_map).unwrap();
- assert_eq!(
- import_map.resolve("std:blank", base_url).unwrap(),
- Some("https://example.com/app/blank.mjs".to_string())
+ assert_resolve(
+ import_map.resolve("std:blank", base_url),
+ "https://example.com/app/blank.mjs",
);
- assert_eq!(
- import_map.resolve("std:none", base_url).unwrap(),
- Some("https://example.com/app/none.mjs".to_string())
+ assert_resolve(
+ import_map.resolve("std:none", base_url),
+ "https://example.com/app/none.mjs",
);
}
}
diff --git a/cli/main.rs b/cli/main.rs
index 5976d42ba..11575ef63 100644
--- a/cli/main.rs
+++ b/cli/main.rs
@@ -27,6 +27,7 @@ mod http_body;
mod http_util;
mod import_map;
pub mod js_errors;
+mod module_specifier;
pub mod msg;
pub mod msg_util;
pub mod ops;
@@ -45,9 +46,9 @@ pub mod worker;
use crate::compiler::bundle_async;
use crate::errors::RustOrJsError;
+use crate::module_specifier::ModuleSpecifier;
use crate::progress::Progress;
use crate::state::ThreadSafeState;
-use crate::worker::root_specifier_to_url;
use crate::worker::Worker;
use deno::v8_set_flags;
use flags::DenoFlags;
@@ -98,51 +99,53 @@ where
pub fn print_file_info(
worker: Worker,
- url: &str,
+ module_specifier: &ModuleSpecifier,
) -> impl Future<Item = Worker, Error = ()> {
- state::fetch_module_meta_data_and_maybe_compile_async(&worker.state, url, ".")
- .and_then(move |out| {
- println!("{} {}", ansi::bold("local:".to_string()), &(out.filename));
-
+ state::fetch_module_meta_data_and_maybe_compile_async(
+ &worker.state,
+ module_specifier,
+ ).and_then(move |out| {
+ println!("{} {}", ansi::bold("local:".to_string()), &(out.filename));
+
+ println!(
+ "{} {}",
+ ansi::bold("type:".to_string()),
+ msg::enum_name_media_type(out.media_type)
+ );
+
+ if out.maybe_output_code_filename.is_some() {
println!(
"{} {}",
- ansi::bold("type:".to_string()),
- msg::enum_name_media_type(out.media_type)
+ ansi::bold("compiled:".to_string()),
+ out.maybe_output_code_filename.as_ref().unwrap(),
);
+ }
- if out.maybe_output_code_filename.is_some() {
- println!(
- "{} {}",
- ansi::bold("compiled:".to_string()),
- out.maybe_output_code_filename.as_ref().unwrap(),
- );
- }
-
- if out.maybe_source_map_filename.is_some() {
- println!(
- "{} {}",
- ansi::bold("map:".to_string()),
- out.maybe_source_map_filename.as_ref().unwrap()
- );
- }
+ if out.maybe_source_map_filename.is_some() {
+ println!(
+ "{} {}",
+ ansi::bold("map:".to_string()),
+ out.maybe_source_map_filename.as_ref().unwrap()
+ );
+ }
- if let Some(deps) =
- worker.state.modules.lock().unwrap().deps(&out.module_name)
- {
- println!("{}{}", ansi::bold("deps:\n".to_string()), deps.name);
- if let Some(ref depsdeps) = deps.deps {
- for d in depsdeps {
- println!("{}", d);
- }
+ if let Some(deps) =
+ worker.state.modules.lock().unwrap().deps(&out.module_name)
+ {
+ println!("{}{}", ansi::bold("deps:\n".to_string()), deps.name);
+ if let Some(ref depsdeps) = deps.deps {
+ for d in depsdeps {
+ println!("{}", d);
}
- } else {
- println!(
- "{} cannot retrieve full dependency graph",
- ansi::bold("deps:".to_string()),
- );
}
- Ok(worker)
- }).map_err(|err| println!("{}", err))
+ } else {
+ println!(
+ "{} cannot retrieve full dependency graph",
+ ansi::bold("deps:".to_string()),
+ );
+ }
+ Ok(worker)
+ }).map_err(|err| println!("{}", err))
}
fn create_worker_and_state(
@@ -193,10 +196,8 @@ fn fetch_or_info_command(
js_check(worker.execute("denoMain()"));
debug!("main_module {}", main_module);
- let main_url = root_specifier_to_url(&main_module).unwrap();
-
worker
- .execute_mod_async(&main_url, true)
+ .execute_mod_async(&main_module, true)
.map_err(print_err_and_exit)
.and_then(move |()| {
if print_info {
@@ -269,11 +270,10 @@ fn bundle_command(flags: DenoFlags, argv: Vec<String>) {
let (mut _worker, state) = create_worker_and_state(flags, argv);
let main_module = state.main_module().unwrap();
- let main_url = root_specifier_to_url(&main_module).unwrap();
assert!(state.argv.len() >= 3);
let out_file = state.argv[2].clone();
debug!(">>>>> bundle_async START");
- let bundle_future = bundle_async(state, main_url.to_string(), out_file)
+ let bundle_future = bundle_async(state, main_module.to_string(), out_file)
.map_err(|e| {
debug!("diagnostics returned, exiting!");
eprintln!("\n{}", e.to_string());
@@ -313,10 +313,8 @@ fn run_script(flags: DenoFlags, argv: Vec<String>) {
js_check(worker.execute("denoMain()"));
debug!("main_module {}", main_module);
- let main_url = root_specifier_to_url(&main_module).unwrap();
-
worker
- .execute_mod_async(&main_url, false)
+ .execute_mod_async(&main_module, false)
.and_then(move |()| {
worker.then(|result| {
js_check(result);
diff --git a/cli/module_specifier.rs b/cli/module_specifier.rs
new file mode 100644
index 000000000..475211289
--- /dev/null
+++ b/cli/module_specifier.rs
@@ -0,0 +1,81 @@
+use std::fmt;
+use url::Url;
+
+#[derive(Debug, Clone, PartialEq)]
+/// Resolved module specifier
+pub struct ModuleSpecifier(Url);
+
+impl ModuleSpecifier {
+ pub fn to_url(&self) -> Url {
+ self.0.clone()
+ }
+ /// Resolves module using this algorithm:
+ /// https://html.spec.whatwg.org/multipage/webappapis.html#resolve-a-module-specifier
+ 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));
+ }
+
+ // 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);
+ }
+
+ // 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))
+ }
+
+ /// Takes a string representing a path or URL to a module, but of the type
+ /// passed through the command-line interface for the main module. This is
+ /// slightly different than specifiers used in import statements: "foo.js" for
+ /// example is allowed here, whereas in import statements a leading "./" is
+ /// required ("./foo.js"). This function is aware of the current working
+ /// 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))
+ }
+ }
+}
+
+impl From<Url> for ModuleSpecifier {
+ fn from(url: Url) -> Self {
+ ModuleSpecifier(url)
+ }
+}
+
+impl fmt::Display for ModuleSpecifier {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ self.0.fmt(f)
+ }
+}
+
+impl PartialEq<String> for ModuleSpecifier {
+ fn eq(&self, other: &String) -> bool {
+ &self.to_string() == other
+ }
+}
diff --git a/cli/ops.rs b/cli/ops.rs
index 61f29ac9f..e9fd48d0d 100644
--- a/cli/ops.rs
+++ b/cli/ops.rs
@@ -10,6 +10,7 @@ use crate::fs as deno_fs;
use crate::http_util;
use crate::js_errors::apply_source_map;
use crate::js_errors::JSErrorColor;
+use crate::module_specifier::ModuleSpecifier;
use crate::msg;
use crate::msg_util;
use crate::rand;
@@ -24,12 +25,10 @@ use crate::state::ThreadSafeState;
use crate::tokio_util;
use crate::tokio_write;
use crate::version;
-use crate::worker::root_specifier_to_url;
use crate::worker::Worker;
use deno::js_check;
use deno::Buf;
use deno::JSError;
-//use deno::Loader;
use deno::Op;
use deno::PinnedBuf;
use flatbuffers::FlatBufferBuilder;
@@ -341,7 +340,9 @@ fn op_start(
let deno_version = version::DENO;
let deno_version_off = builder.create_string(deno_version);
- let main_module = state.main_module().map(|m| builder.create_string(&m));
+ let main_module = state
+ .main_module()
+ .map(|m| builder.create_string(&m.to_string()));
let xeval_delim = state
.flags
@@ -507,7 +508,7 @@ fn op_fetch_module_meta_data(
Some(import_map) => {
match import_map.resolve(specifier, referrer) {
Ok(result) => match result {
- Some(url) => url.clone(),
+ Some(module_specifier) => module_specifier.to_string(),
None => specifier.to_string(),
},
Err(err) => panic!("error resolving using import map: {:?}", err), // TODO: this should be coerced to DenoError
@@ -2082,11 +2083,11 @@ fn op_create_worker(
js_check(worker.execute("denoMain()"));
js_check(worker.execute("workerMain()"));
- let op = root_specifier_to_url(specifier)
- .and_then(|specifier_url| {
+ let op = ModuleSpecifier::resolve_root(specifier)
+ .and_then(|module_specifier| {
Ok(
worker
- .execute_mod_async(&specifier_url, false)
+ .execute_mod_async(&module_specifier, false)
.and_then(move |()| {
let mut workers_tl = parent_state.workers.lock().unwrap();
workers_tl.insert(rid, worker.shared());
diff --git a/cli/state.rs b/cli/state.rs
index bb284038c..df1bf1cc5 100644
--- a/cli/state.rs
+++ b/cli/state.rs
@@ -7,13 +7,13 @@ use crate::errors::DenoResult;
use crate::flags;
use crate::global_timer::GlobalTimer;
use crate::import_map::ImportMap;
+use crate::module_specifier::ModuleSpecifier;
use crate::msg;
use crate::ops;
use crate::permissions::DenoPermissions;
use crate::progress::Progress;
use crate::resources;
use crate::resources::ResourceId;
-use crate::worker::resolve_module_spec;
use crate::worker::Worker;
use deno::Buf;
use deno::Loader;
@@ -60,7 +60,7 @@ pub struct ThreadSafeState(Arc<State>);
#[cfg_attr(feature = "cargo-clippy", allow(stutter))]
pub struct State {
pub modules: Arc<Mutex<deno::Modules>>,
- pub main_module: Option<String>,
+ pub main_module: Option<ModuleSpecifier>,
pub dir: deno_dir::DenoDir,
pub argv: Vec<String>,
pub permissions: DenoPermissions,
@@ -113,44 +113,40 @@ impl ThreadSafeState {
pub fn fetch_module_meta_data_and_maybe_compile_async(
state: &ThreadSafeState,
- specifier: &str,
- referrer: &str,
+ module_specifier: &ModuleSpecifier,
) -> impl Future<Item = ModuleMetaData, Error = DenoError> {
let state_ = state.clone();
- let specifier = specifier.to_string();
- let referrer = referrer.to_string();
- let is_root = referrer == ".";
-
- let f =
- futures::future::result(state.resolve(&specifier, &referrer, is_root));
- f.and_then(move |module_id| {
- let use_cache = !state_.flags.reload || state_.has_compiled(&module_id);
- let no_fetch = state_.flags.no_fetch;
-
- state_
- .dir
- .fetch_module_meta_data_async(&specifier, &referrer, use_cache, no_fetch)
- .and_then(move |out| {
- if out.media_type == msg::MediaType::TypeScript
- && !out.has_output_code_and_source_map()
- {
- debug!(">>>>> compile_sync START");
- Either::A(
- compile_async(state_.clone(), &out)
- .map_err(|e| {
- debug!("compiler error exiting!");
- eprintln!("\n{}", e.to_string());
- std::process::exit(1);
- }).and_then(move |out| {
- debug!(">>>>> compile_sync END");
- Ok(out)
- }),
- )
- } else {
- Either::B(futures::future::ok(out))
- }
- })
- })
+ let use_cache =
+ !state_.flags.reload || state_.has_compiled(&module_specifier.to_string());
+ let no_fetch = state_.flags.no_fetch;
+
+ state_
+ .dir
+ .fetch_module_meta_data_async(
+ &module_specifier.to_string(),
+ ".",
+ use_cache,
+ no_fetch,
+ ).and_then(move |out| {
+ if out.media_type == msg::MediaType::TypeScript
+ && !out.has_output_code_and_source_map()
+ {
+ debug!(">>>>> compile_sync START");
+ Either::A(
+ compile_async(state_.clone(), &out)
+ .map_err(|e| {
+ debug!("compiler error exiting!");
+ eprintln!("\n{}", e.to_string());
+ std::process::exit(1);
+ }).and_then(move |out| {
+ debug!(">>>>> compile_sync END");
+ Ok(out)
+ }),
+ )
+ } else {
+ Either::B(futures::future::ok(out))
+ }
+ })
}
impl Loader for ThreadSafeState {
@@ -164,28 +160,25 @@ impl Loader for ThreadSafeState {
) -> Result<String, Self::Error> {
if !is_root {
if let Some(import_map) = &self.import_map {
- match import_map.resolve(specifier, referrer) {
- Ok(result) => {
- if result.is_some() {
- return Ok(result.unwrap());
- }
- }
- Err(err) => {
- // TODO(bartlomieju): this should be coerced to DenoError
- panic!("error resolving using import map: {:?}", err);
- }
+ let result = import_map.resolve(specifier, referrer)?;
+ if result.is_some() {
+ return Ok(result.unwrap().to_string());
}
}
}
- resolve_module_spec(specifier, referrer).map_err(DenoError::from)
+ let module_specifier =
+ ModuleSpecifier::resolve(specifier, referrer).map_err(DenoError::from)?;
+ Ok(module_specifier.to_string())
}
/// Given an absolute url, load its source code.
fn load(&self, url: &str) -> Box<deno::SourceCodeInfoFuture<Self::Error>> {
self.metrics.resolve_count.fetch_add(1, Ordering::SeqCst);
+ let module_specifier = ModuleSpecifier::resolve_root(url)
+ .expect("should already been properly resolved");
Box::new(
- fetch_module_meta_data_and_maybe_compile_async(self, url, ".")
+ fetch_module_meta_data_and_maybe_compile_async(self, &module_specifier)
.map_err(|err| {
eprintln!("{}", err);
err
@@ -256,18 +249,15 @@ impl ThreadSafeState {
let dir =
deno_dir::DenoDir::new(custom_root, &config, progress.clone()).unwrap();
- let main_module: Option<String> = if argv_rest.len() <= 1 {
+ let main_module: Option<ModuleSpecifier> = if argv_rest.len() <= 1 {
None
} else {
- let specifier = argv_rest[1].clone();
- let referrer = ".";
- // TODO: does this really have to be resolved by DenoDir?
- // Maybe we can call `resolve_module_spec`
- match dir.resolve_module_url(&specifier, referrer) {
- Ok(url) => Some(url.to_string()),
+ let root_specifier = argv_rest[1].clone();
+ match ModuleSpecifier::resolve_root(&root_specifier) {
+ Ok(specifier) => Some(specifier),
Err(e) => {
- debug!("Potentially swallowed error {}", e);
- None
+ // TODO: handle unresolvable specifier
+ panic!("Unable to resolve root specifier: {:?}", e);
}
}
};
@@ -275,11 +265,11 @@ impl ThreadSafeState {
let mut import_map = None;
if let Some(file_name) = &flags.import_map_path {
let base_url = match &main_module {
- Some(url) => url,
+ Some(module_specifier) => module_specifier.clone(),
None => unreachable!(),
};
- match ImportMap::load(base_url, file_name) {
+ match ImportMap::load(&base_url.to_string(), file_name) {
Ok(map) => import_map = Some(map),
Err(err) => {
println!("{:?}", err);
@@ -319,9 +309,9 @@ impl ThreadSafeState {
}
/// Read main module from argv
- pub fn main_module(&self) -> Option<String> {
+ pub fn main_module(&self) -> Option<ModuleSpecifier> {
match &self.main_module {
- Some(url) => Some(url.to_string()),
+ Some(module_specifier) => Some(module_specifier.clone()),
None => None,
}
}
diff --git a/cli/worker.rs b/cli/worker.rs
index 94d33e391..f11aa93e9 100644
--- a/cli/worker.rs
+++ b/cli/worker.rs
@@ -2,6 +2,7 @@
use crate::errors::DenoError;
use crate::errors::RustOrJsError;
use crate::js_errors;
+use crate::module_specifier::ModuleSpecifier;
use crate::state::ThreadSafeState;
use crate::tokio_util;
use deno;
@@ -11,7 +12,6 @@ use futures::Async;
use futures::Future;
use std::sync::Arc;
use std::sync::Mutex;
-use url::Url;
/// Wraps deno::Isolate to provide source maps, ops for the CLI, and
/// high-level module loading
@@ -57,7 +57,7 @@ impl Worker {
/// Executes the provided JavaScript module.
pub fn execute_mod_async(
&mut self,
- js_url: &Url,
+ module_specifier: &ModuleSpecifier,
is_prefetch: bool,
) -> impl Future<Item = (), Error = RustOrJsError> {
let worker = self.clone();
@@ -65,8 +65,12 @@ impl Worker {
let loader = self.state.clone();
let isolate = self.isolate.clone();
let modules = self.state.modules.clone();
- let recursive_load =
- deno::RecursiveLoad::new(js_url.as_str(), loader, isolate, modules);
+ let recursive_load = deno::RecursiveLoad::new(
+ &module_specifier.to_string(),
+ loader,
+ isolate,
+ modules,
+ );
recursive_load
.and_then(move |id| -> Result<(), deno::JSErrorOr<DenoError>> {
worker.state.progress.done();
@@ -96,10 +100,10 @@ impl Worker {
/// Executes the provided JavaScript module.
pub fn execute_mod(
&mut self,
- js_url: &Url,
+ module_specifier: &ModuleSpecifier,
is_prefetch: bool,
) -> Result<(), RustOrJsError> {
- tokio_util::block_on(self.execute_mod_async(js_url, is_prefetch))
+ tokio_util::block_on(self.execute_mod_async(module_specifier, is_prefetch))
}
/// Applies source map to the error.
@@ -108,58 +112,6 @@ impl Worker {
}
}
-// https://html.spec.whatwg.org/multipage/webappapis.html#resolve-a-module-specifier
-// TODO(ry) Add tests.
-// TODO(ry) Move this to core?
-pub fn resolve_module_spec(
- specifier: &str,
- base: &str,
-) -> Result<String, 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(specifier_url) = Url::parse(specifier) {
- return Ok(specifier_url.to_string());
- }
-
- // 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(ry) This is (probably) not the correct error to return here.
- return Err(url::ParseError::RelativeUrlWithCannotBeABaseBase);
- }
-
- // 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(u.to_string())
-}
-
-/// Takes a string representing a path or URL to a module, but of the type
-/// passed through the command-line interface for the main module. This is
-/// slightly different than specifiers used in import statements: "foo.js" for
-/// example is allowed here, whereas in import statements a leading "./" is
-/// required ("./foo.js"). This function is aware of the current working
-/// directory and returns an absolute URL.
-pub fn root_specifier_to_url(
- root_specifier: &str,
-) -> Result<Url, url::ParseError> {
- let maybe_url = Url::parse(root_specifier);
- if let Ok(url) = maybe_url {
- Ok(url)
- } else {
- let cwd = std::env::current_dir().unwrap();
- let base = Url::from_directory_path(cwd).unwrap();
- base.join(root_specifier)
- }
-}
-
impl Future for Worker {
type Item = ();
type Error = JSError;
@@ -186,12 +138,9 @@ mod tests {
#[test]
fn execute_mod_esm_imports_a() {
- let filename = std::env::current_dir()
- .unwrap()
- .join("tests/esm_imports_a.js");
- let js_url = Url::from_file_path(filename).unwrap();
-
- let argv = vec![String::from("./deno"), js_url.to_string()];
+ let module_specifier =
+ ModuleSpecifier::resolve_root("tests/esm_imports_a.js").unwrap();
+ let argv = vec![String::from("./deno"), module_specifier.to_string()];
let state = ThreadSafeState::new(
flags::DenoFlags::default(),
argv,
@@ -202,7 +151,7 @@ mod tests {
tokio_util::run(lazy(move || {
let mut worker =
Worker::new("TEST".to_string(), StartupData::None, state);
- let result = worker.execute_mod(&js_url, false);
+ let result = worker.execute_mod(&module_specifier, false);
if let Err(err) = result {
eprintln!("execute_mod err {:?}", err);
}
@@ -217,10 +166,9 @@ mod tests {
#[test]
fn execute_mod_circular() {
- let filename = std::env::current_dir().unwrap().join("tests/circular1.js");
- let js_url = Url::from_file_path(filename).unwrap();
-
- let argv = vec![String::from("./deno"), js_url.to_string()];
+ let module_specifier =
+ ModuleSpecifier::resolve_root("tests/circular1.js").unwrap();
+ let argv = vec![String::from("./deno"), module_specifier.to_string()];
let state = ThreadSafeState::new(
flags::DenoFlags::default(),
argv,
@@ -231,7 +179,7 @@ mod tests {
tokio_util::run(lazy(move || {
let mut worker =
Worker::new("TEST".to_string(), StartupData::None, state);
- let result = worker.execute_mod(&js_url, false);
+ let result = worker.execute_mod(&module_specifier, false);
if let Err(err) = result {
eprintln!("execute_mod err {:?}", err);
}
@@ -246,11 +194,9 @@ mod tests {
#[test]
fn execute_006_url_imports() {
- let filename = std::env::current_dir()
- .unwrap()
- .join("tests/006_url_imports.ts");
- let js_url = Url::from_file_path(filename).unwrap();
- let argv = vec![String::from("deno"), js_url.to_string()];
+ let module_specifier =
+ ModuleSpecifier::resolve_root("tests/006_url_imports.ts").unwrap();
+ let argv = vec![String::from("deno"), module_specifier.to_string()];
let mut flags = flags::DenoFlags::default();
flags.reload = true;
let state =
@@ -263,7 +209,7 @@ mod tests {
state,
);
js_check(worker.execute("denoMain()"));
- let result = worker.execute_mod(&js_url, false);
+ let result = worker.execute_mod(&module_specifier, false);
if let Err(err) = result {
eprintln!("execute_mod err {:?}", err);
}
@@ -378,8 +324,9 @@ mod tests {
tokio_util::init(|| {
// "foo" is not a vailid module specifier so this should return an error.
let mut worker = create_test_worker();
- let js_url = root_specifier_to_url("does-not-exist").unwrap();
- let result = worker.execute_mod_async(&js_url, false).wait();
+ let module_specifier =
+ ModuleSpecifier::resolve_root("does-not-exist").unwrap();
+ let result = worker.execute_mod_async(&module_specifier, false).wait();
assert!(result.is_err());
})
}
@@ -390,8 +337,9 @@ mod tests {
// This assumes cwd is project root (an assumption made throughout the
// tests).
let mut worker = create_test_worker();
- let js_url = root_specifier_to_url("./tests/002_hello.ts").unwrap();
- let result = worker.execute_mod_async(&js_url, false).wait();
+ let module_specifier =
+ ModuleSpecifier::resolve_root("./tests/002_hello.ts").unwrap();
+ let result = worker.execute_mod_async(&module_specifier, false).wait();
assert!(result.is_ok());
})
}