diff options
author | Bartek IwaĆczuk <biwanczuk@gmail.com> | 2024-09-05 07:25:56 +0100 |
---|---|---|
committer | GitHub <noreply@github.com> | 2024-09-05 08:25:56 +0200 |
commit | acd01eb2b41e37a480943f79cfa28fc220c4baca (patch) | |
tree | a9808419bc88099fc811162d000007f6b2c30f9d /cli/graph_util.rs | |
parent | 105c2e336a4312ed1714ab893c57eae2a19068c7 (diff) |
feat: Add a hint on error about 'Relative import path ... not prefixed with ...' (#25430)
Running a file like:
```
import "@std/dotenv/load";
```
Without a mapping in `imports` field of `deno.json` or `dependencies` of
`package.json`
will now error out with a hint:
```
error: Relative import path "@std/dotenv/load" not prefixed with / or ./ or ../
hint: Try running `deno add @std/dotenv/load`
at [WILDCARD]bare_specifier_without_import/main.ts:1:8
```
Closes https://github.com/denoland/deno/issues/24699
---------
Co-authored-by: David Sherret <dsherret@users.noreply.github.com>
Diffstat (limited to 'cli/graph_util.rs')
-rw-r--r-- | cli/graph_util.rs | 60 |
1 files changed, 56 insertions, 4 deletions
diff --git a/cli/graph_util.rs b/cli/graph_util.rs index a9fcfd83d..f3ac64a43 100644 --- a/cli/graph_util.rs +++ b/cli/graph_util.rs @@ -724,12 +724,25 @@ impl ModuleGraphBuilder { pub fn enhanced_resolution_error_message(error: &ResolutionError) -> String { let mut message = format_deno_graph_error(error); - if let Some(specifier) = get_resolution_error_bare_node_specifier(error) { + let maybe_hint = if let Some(specifier) = + get_resolution_error_bare_node_specifier(error) + { if !*DENO_DISABLE_PEDANTIC_NODE_WARNINGS { - message.push_str(&format!( - "\nIf you want to use a built-in Node module, add a \"node:\" prefix (ex. \"node:{specifier}\")." - )); + Some(format!("If you want to use a built-in Node module, add a \"node:\" prefix (ex. \"node:{specifier}\").")) + } else { + None } + } else { + get_import_prefix_missing_error(error).map(|specifier| { + format!( + "If you want to use a JSR or npm package, try running `deno add {}`", + specifier + ) + }) + }; + + if let Some(hint) = maybe_hint { + message.push_str(&format!("\n {} {}", colors::cyan("hint:"), hint)); } message @@ -864,6 +877,45 @@ fn get_resolution_error_bare_specifier( } } +fn get_import_prefix_missing_error(error: &ResolutionError) -> Option<&str> { + let mut maybe_specifier = None; + if let ResolutionError::InvalidSpecifier { + error: SpecifierError::ImportPrefixMissing { specifier, .. }, + .. + } = error + { + maybe_specifier = Some(specifier); + } else if let ResolutionError::ResolverError { error, .. } = error { + match error.as_ref() { + ResolveError::Specifier(specifier_error) => { + if let SpecifierError::ImportPrefixMissing { specifier, .. } = + specifier_error + { + maybe_specifier = Some(specifier); + } + } + ResolveError::Other(other_error) => { + if let Some(SpecifierError::ImportPrefixMissing { specifier, .. }) = + other_error.downcast_ref::<SpecifierError>() + { + maybe_specifier = Some(specifier); + } + } + } + } + + // NOTE(bartlomieju): For now, return None if a specifier contains a dot or a space. This is because + // suggesting to `deno add bad-module.ts` makes no sense and is worse than not providing + // a suggestion at all. This should be improved further in the future + if let Some(specifier) = maybe_specifier { + if specifier.contains('.') || specifier.contains(' ') { + return None; + } + } + + maybe_specifier.map(|s| s.as_str()) +} + /// Gets if any of the specified root's "file:" dependents are in the /// provided changed set. pub fn has_graph_root_local_dependent_changed( |