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:
parent
83ce333633
commit
3a33510bd4
12 changed files with 66 additions and 55 deletions
|
@ -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 {
|
||||||
|
|
|
@ -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
|
||||||
};
|
};
|
||||||
|
|
|
@ -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
|
||||||
};
|
};
|
||||||
|
|
|
@ -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);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
1
cli/tests/092_import_map_unmapped_bare_specifier.ts
Normal file
1
cli/tests/092_import_map_unmapped_bare_specifier.ts
Normal file
|
@ -0,0 +1 @@
|
||||||
|
await import("unmapped");
|
4
cli/tests/092_import_map_unmapped_bare_specifier.ts.out
Normal file
4
cli/tests/092_import_map_unmapped_bare_specifier.ts.out
Normal 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]
|
|
@ -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"
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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"
|
||||||
|
|
|
@ -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",
|
||||||
|
|
|
@ -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!(""),
|
||||||
}
|
}
|
||||||
),
|
),
|
||||||
|
|
Loading…
Add table
Reference in a new issue