0
0
Fork 0
mirror of https://github.com/denoland/deno.git synced 2025-02-01 20:25:12 -05:00

fix(cli): Don't statically error on dynamic unmapped bare specifiers (#10618)

Fixes #10168
Fixes #10615
Fixes #10616
This commit is contained in:
Nayeem Rahman 2021-05-31 01:20:34 +01:00 committed by GitHub
parent 83ce333633
commit 3a33510bd4
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
12 changed files with 66 additions and 55 deletions

View file

@ -14,11 +14,25 @@ use std::error::Error;
use std::fmt; use std::fmt;
#[derive(Debug)] #[derive(Debug)]
pub struct ImportMapError(String); pub enum ImportMapError {
UnmappedBareSpecifier(String, Option<String>),
Other(String),
}
impl fmt::Display for ImportMapError { impl fmt::Display for ImportMapError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.pad(&self.0) match self {
ImportMapError::UnmappedBareSpecifier(specifier, maybe_referrer) => write!(
f,
"Relative import path \"{}\" not prefixed with / or ./ or ../ and not in import map{}",
specifier,
match maybe_referrer {
Some(referrer) => format!(" from \"{}\"", referrer),
None => format!(""),
}
),
ImportMapError::Other(message) => f.pad(message),
}
} }
} }
@ -51,7 +65,7 @@ impl ImportMap {
let v: Value = match serde_json::from_str(json_string) { let v: Value = match serde_json::from_str(json_string) {
Ok(v) => v, Ok(v) => v,
Err(_) => { Err(_) => {
return Err(ImportMapError( return Err(ImportMapError::Other(
"Unable to parse import map JSON".to_string(), "Unable to parse import map JSON".to_string(),
)); ));
} }
@ -60,7 +74,7 @@ impl ImportMap {
match v { match v {
Value::Object(_) => {} Value::Object(_) => {}
_ => { _ => {
return Err(ImportMapError( return Err(ImportMapError::Other(
"Import map JSON must be an object".to_string(), "Import map JSON must be an object".to_string(),
)); ));
} }
@ -70,7 +84,7 @@ impl ImportMap {
let normalized_imports = match &v.get("imports") { let normalized_imports = match &v.get("imports") {
Some(imports_map) => { Some(imports_map) => {
if !imports_map.is_object() { if !imports_map.is_object() {
return Err(ImportMapError( return Err(ImportMapError::Other(
"Import map's 'imports' must be an object".to_string(), "Import map's 'imports' must be an object".to_string(),
)); ));
} }
@ -84,7 +98,7 @@ impl ImportMap {
let normalized_scopes = match &v.get("scopes") { let normalized_scopes = match &v.get("scopes") {
Some(scope_map) => { Some(scope_map) => {
if !scope_map.is_object() { if !scope_map.is_object() {
return Err(ImportMapError( return Err(ImportMapError::Other(
"Import map's 'scopes' must be an object".to_string(), "Import map's 'scopes' must be an object".to_string(),
)); ));
} }
@ -252,7 +266,7 @@ impl ImportMap {
// Order is preserved because of "preserve_order" feature of "serde_json". // Order is preserved because of "preserve_order" feature of "serde_json".
for (scope_prefix, potential_specifier_map) in scope_map.iter() { for (scope_prefix, potential_specifier_map) in scope_map.iter() {
if !potential_specifier_map.is_object() { if !potential_specifier_map.is_object() {
return Err(ImportMapError(format!( return Err(ImportMapError::Other(format!(
"The value for the {:?} scope prefix must be an object", "The value for the {:?} scope prefix must be an object",
scope_prefix scope_prefix
))); )));
@ -341,7 +355,7 @@ impl ImportMap {
if let Some(address) = maybe_address { if let Some(address) = maybe_address {
return Ok(Some(address.clone())); return Ok(Some(address.clone()));
} else { } else {
return Err(ImportMapError(format!( return Err(ImportMapError::Other(format!(
"Blocked by null entry for \"{:?}\"", "Blocked by null entry for \"{:?}\"",
normalized_specifier normalized_specifier
))); )));
@ -367,7 +381,7 @@ impl ImportMap {
} }
if maybe_address.is_none() { if maybe_address.is_none() {
return Err(ImportMapError(format!( return Err(ImportMapError::Other(format!(
"Blocked by null entry for \"{:?}\"", "Blocked by null entry for \"{:?}\"",
specifier_key specifier_key
))); )));
@ -383,7 +397,7 @@ impl ImportMap {
let url = match resolution_result.join(after_prefix) { let url = match resolution_result.join(after_prefix) {
Ok(url) => url, Ok(url) => url,
Err(_) => { Err(_) => {
return Err(ImportMapError(format!( return Err(ImportMapError::Other(format!(
"Failed to resolve the specifier \"{:?}\" as its after-prefix "Failed to resolve the specifier \"{:?}\" as its after-prefix
portion \"{:?}\" could not be URL-parsed relative to the URL prefix portion \"{:?}\" could not be URL-parsed relative to the URL prefix
\"{:?}\" mapped to by the prefix \"{:?}\"", \"{:?}\" mapped to by the prefix \"{:?}\"",
@ -396,7 +410,7 @@ impl ImportMap {
}; };
if !url.as_str().starts_with(resolution_result.as_str()) { if !url.as_str().starts_with(resolution_result.as_str()) {
return Err(ImportMapError(format!( return Err(ImportMapError::Other(format!(
"The specifier \"{:?}\" backtracks above its prefix \"{:?}\"", "The specifier \"{:?}\" backtracks above its prefix \"{:?}\"",
normalized_specifier, specifier_key normalized_specifier, specifier_key
))); )));
@ -417,7 +431,7 @@ impl ImportMap {
&self, &self,
specifier: &str, specifier: &str,
referrer: &str, referrer: &str,
) -> Result<Option<Url>, ImportMapError> { ) -> Result<Url, ImportMapError> {
let as_url: Option<Url> = let as_url: Option<Url> =
ImportMap::try_url_like_specifier(specifier, referrer); ImportMap::try_url_like_specifier(specifier, referrer);
let normalized_specifier = if let Some(url) = as_url.as_ref() { let normalized_specifier = if let Some(url) = as_url.as_ref() {
@ -434,7 +448,7 @@ impl ImportMap {
)?; )?;
// match found in scopes map // match found in scopes map
if scopes_match.is_some() { if let Some(scopes_match) = scopes_match {
return Ok(scopes_match); return Ok(scopes_match);
} }
@ -445,19 +459,19 @@ impl ImportMap {
)?; )?;
// match found in import map // match found in import map
if imports_match.is_some() { if let Some(imports_match) = imports_match {
return Ok(imports_match); return Ok(imports_match);
} }
// The specifier was able to be turned into a URL, but wasn't remapped into anything. // The specifier was able to be turned into a URL, but wasn't remapped into anything.
if as_url.is_some() { if let Some(as_url) = as_url {
return Ok(as_url); return Ok(as_url);
} }
Err(ImportMapError(format!( Err(ImportMapError::UnmappedBareSpecifier(
"Unmapped bare specifier {:?}", specifier.to_string(),
specifier Some(referrer.to_string()),
))) ))
} }
} }
@ -465,7 +479,6 @@ impl ImportMap {
mod tests { mod tests {
use super::*; use super::*;
use deno_core::resolve_import;
use std::path::Path; use std::path::Path;
use std::path::PathBuf; use std::path::PathBuf;
use walkdir::WalkDir; use walkdir::WalkDir;
@ -652,15 +665,7 @@ mod tests {
let maybe_resolved = import_map let maybe_resolved = import_map
.resolve(&given_specifier, &base_url) .resolve(&given_specifier, &base_url)
.ok() .ok()
.map(|maybe_resolved| { .map(|url| url.to_string());
if let Some(specifier) = maybe_resolved {
specifier.to_string()
} else {
resolve_import(&given_specifier, &base_url)
.unwrap()
.to_string()
}
});
assert_eq!(expected_specifier, &maybe_resolved, "{}", test.name); assert_eq!(expected_specifier, &maybe_resolved, "{}", test.name);
} }
TestKind::Parse { TestKind::Parse {

View file

@ -214,13 +214,7 @@ pub fn resolve_import(
maybe_import_map: &Option<ImportMap>, maybe_import_map: &Option<ImportMap>,
) -> ResolvedDependency { ) -> ResolvedDependency {
let maybe_mapped = if let Some(import_map) = maybe_import_map { let maybe_mapped = if let Some(import_map) = maybe_import_map {
if let Ok(maybe_specifier) = import_map.resolve(specifier, referrer.as_str()).ok()
import_map.resolve(specifier, referrer.as_str())
{
maybe_specifier
} else {
None
}
} else { } else {
None None
}; };

View file

@ -12,6 +12,7 @@ use crate::config_file::IgnoredCompilerOptions;
use crate::config_file::TsConfig; use crate::config_file::TsConfig;
use crate::diagnostics::Diagnostics; use crate::diagnostics::Diagnostics;
use crate::import_map::ImportMap; use crate::import_map::ImportMap;
use crate::import_map::ImportMapError;
use crate::info; use crate::info;
use crate::lockfile::Lockfile; use crate::lockfile::Lockfile;
use crate::media_type::MediaType; use crate::media_type::MediaType;
@ -397,10 +398,13 @@ impl Module {
Ok(specifier) => Some(specifier), Ok(specifier) => Some(specifier),
Err(any_error) => { Err(any_error) => {
match any_error.downcast_ref::<ModuleResolutionError>() { match any_error.downcast_ref::<ModuleResolutionError>() {
Some(ModuleResolutionError::ImportPrefixMissing(_, _)) => None, Some(ModuleResolutionError::ImportPrefixMissing(..)) => None,
_ => { _ => match any_error.downcast_ref::<ImportMapError>() {
return Err(any_error); Some(ImportMapError::UnmappedBareSpecifier(..)) => None,
} _ => {
return Err(any_error);
}
},
} }
} }
}; };
@ -447,10 +451,8 @@ impl Module {
) -> Result<ModuleSpecifier, AnyError> { ) -> Result<ModuleSpecifier, AnyError> {
let maybe_resolve = if let Some(import_map) = self.maybe_import_map.clone() let maybe_resolve = if let Some(import_map) = self.maybe_import_map.clone()
{ {
import_map let import_map = import_map.lock().unwrap();
.lock() Some(import_map.resolve(specifier, self.specifier.as_str())?)
.unwrap()
.resolve(specifier, self.specifier.as_str())?
} else { } else {
None None
}; };

View file

@ -83,10 +83,9 @@ impl ModuleLoader for CliModuleLoader {
if !is_main { if !is_main {
if let Some(import_map) = &self.import_map { if let Some(import_map) = &self.import_map {
let result = import_map.resolve(specifier, referrer)?; return import_map
if let Some(r) = result { .resolve(specifier, referrer)
return Ok(r); .map_err(AnyError::from);
}
} }
} }

View file

@ -0,0 +1 @@
await import("unmapped");

View file

@ -0,0 +1,4 @@
[WILDCARD]error: Uncaught (in promise) TypeError: Relative import path "unmapped" not prefixed with / or ./ or ../ and not in import map from "[WILDCARD]"
await import("unmapped");
^
at [WILDCARD]

View file

@ -1 +1 @@
[WILDCARD]error: relative import path "bad-module.ts" not prefixed with / or ./ or ../ Imported from "[WILDCARD]/error_011_bad_module_specifier.ts" [WILDCARD]error: Relative import path "bad-module.ts" not prefixed with / or ./ or ../ from "[WILDCARD]/error_011_bad_module_specifier.ts"

View file

@ -1,5 +1,5 @@
Check [WILDCARD]error_012_bad_dynamic_import_specifier.ts Check [WILDCARD]error_012_bad_dynamic_import_specifier.ts
error: Uncaught (in promise) TypeError: relative import path "bad-module.ts" not prefixed with / or ./ or ../ Imported from "[WILDCARD]/error_012_bad_dynamic_import_specifier.ts" error: Uncaught (in promise) TypeError: Relative import path "bad-module.ts" not prefixed with / or ./ or ../ from "[WILDCARD]/error_012_bad_dynamic_import_specifier.ts"
const _badModule = await import("bad-module.ts"); const _badModule = await import("bad-module.ts");
^ ^
at async file:///[WILDCARD]/error_012_bad_dynamic_import_specifier.ts:2:22 at async file:///[WILDCARD]/error_012_bad_dynamic_import_specifier.ts:2:22

View file

@ -1,8 +1,8 @@
Caught direct dynamic import error. Caught direct dynamic import error.
TypeError: relative import path "does not exist" not prefixed with / or ./ or ../ Imported from "[WILDCARD]/error_014_catch_dynamic_import_error.js" TypeError: Relative import path "does not exist" not prefixed with / or ./ or ../ from "[WILDCARD]/error_014_catch_dynamic_import_error.js"
at async file:///[WILDCARD]/error_014_catch_dynamic_import_error.js:3:5 at async file:///[WILDCARD]/error_014_catch_dynamic_import_error.js:3:5
Caught indirect direct dynamic import error. Caught indirect direct dynamic import error.
TypeError: relative import path "does not exist either" not prefixed with / or ./ or ../ Imported from "[WILDCARD]/indirect_import_error.js" TypeError: Relative import path "does not exist either" not prefixed with / or ./ or ../ from "[WILDCARD]/indirect_import_error.js"
at async file:///[WILDCARD]/error_014_catch_dynamic_import_error.js:10:5 at async file:///[WILDCARD]/error_014_catch_dynamic_import_error.js:10:5
Caught error thrown by dynamically imported module. Caught error thrown by dynamically imported module.
Error: An error Error: An error

View file

@ -1 +1 @@
[WILDCARD]error: relative import path "baz" not prefixed with / or ./ or ../ Imported from "[WILDCARD]/type_definitions/bar.d.ts" [WILDCARD]error: Relative import path "baz" not prefixed with / or ./ or ../ from "[WILDCARD]/type_definitions/bar.d.ts"

View file

@ -3133,6 +3133,12 @@ console.log("finish");
exit_code: 1, exit_code: 1,
}); });
itest!(_092_import_map_unmapped_bare_specifier {
args: "run --import-map import_maps/import_map.json 092_import_map_unmapped_bare_specifier.ts",
output: "092_import_map_unmapped_bare_specifier.ts.out",
exit_code: 1,
});
itest!(dynamic_import_permissions_remote_remote { itest!(dynamic_import_permissions_remote_remote {
args: "run --quiet --reload --allow-net=localhost:4545 dynamic_import/permissions_remote_remote.ts", args: "run --quiet --reload --allow-net=localhost:4545 dynamic_import/permissions_remote_remote.ts",
output: "dynamic_import/permissions_remote_remote.ts.out", output: "dynamic_import/permissions_remote_remote.ts.out",

View file

@ -39,10 +39,10 @@ impl fmt::Display for ModuleResolutionError {
InvalidPath(ref path) => write!(f, "invalid module path: {:?}", path), InvalidPath(ref path) => write!(f, "invalid module path: {:?}", path),
ImportPrefixMissing(ref specifier, ref maybe_referrer) => write!( ImportPrefixMissing(ref specifier, ref maybe_referrer) => write!(
f, f,
"relative import path \"{}\" not prefixed with / or ./ or ../{}", "Relative import path \"{}\" not prefixed with / or ./ or ../{}",
specifier, specifier,
match maybe_referrer { match maybe_referrer {
Some(referrer) => format!(" Imported from \"{}\"", referrer), Some(referrer) => format!(" from \"{}\"", referrer),
None => format!(""), None => format!(""),
} }
), ),