1
0
Fork 0
mirror of https://github.com/denoland/deno.git synced 2024-11-24 15:19:26 -05:00

fix: add suggestion how to fix importing CJS module (#21764)

```
$ cat exports_error.js
Object.defineProperty(exports, "__esModule", { value: true });

$ deno exports_error.js
error: Uncaught (in promise) ReferenceError: exports is not defined
Object.defineProperty(exports, "__esModule", { value: true });
                      ^
    at file:///exports_error.js:1:23

    info: Deno doesn't support CommonJS modules without `.cjs` extension.
    hint: Rewrite this module to ESM or change the file extension to `.cjs`.
```
This commit is contained in:
Bartek Iwańczuk 2024-09-05 13:49:07 +01:00 committed by GitHub
parent dda6328745
commit 794d347ec6
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
7 changed files with 111 additions and 5 deletions

View file

@ -46,7 +46,8 @@ use deno_core::error::JsError;
use deno_core::futures::FutureExt;
use deno_core::unsync::JoinHandle;
use deno_npm::resolution::SnapshotFromLockfileError;
use deno_runtime::fmt_errors::format_js_error;
use deno_runtime::fmt_errors::format_js_error_with_suggestions;
use deno_runtime::fmt_errors::FixSuggestion;
use deno_runtime::tokio_util::create_and_run_current_thread_with_maybe_metrics;
use deno_terminal::colors;
use factory::CliFactory;
@ -336,12 +337,30 @@ fn exit_with_message(message: &str, code: i32) -> ! {
std::process::exit(code);
}
fn get_suggestions_for_commonjs_error(e: &JsError) -> Vec<FixSuggestion> {
if e.name.as_deref() == Some("ReferenceError") {
if let Some(msg) = &e.message {
if msg.contains("module is not defined")
|| msg.contains("exports is not defined")
{
return vec![
FixSuggestion::info("Deno does not support CommonJS modules without `.cjs` extension."),
FixSuggestion::hint("Rewrite this module to ESM or change the file extension to `.cjs`."),
];
}
}
}
vec![]
}
fn exit_for_error(error: AnyError) -> ! {
let mut error_string = format!("{error:?}");
let mut error_code = 1;
if let Some(e) = error.downcast_ref::<JsError>() {
error_string = format_js_error(e);
let suggestions = get_suggestions_for_commonjs_error(e);
error_string = format_js_error_with_suggestions(e, suggestions);
} else if let Some(SnapshotFromLockfileError::IntegrityCheckFailed(e)) =
error.downcast_ref::<SnapshotFromLockfileError>()
{

View file

@ -20,6 +20,34 @@ struct IndexedErrorReference<'a> {
index: usize,
}
#[derive(Debug)]
enum FixSuggestionKind {
Info,
Hint,
}
#[derive(Debug)]
pub struct FixSuggestion<'a> {
kind: FixSuggestionKind,
message: &'a str,
}
impl<'a> FixSuggestion<'a> {
pub fn info(message: &'a str) -> Self {
Self {
kind: FixSuggestionKind::Info,
message,
}
}
pub fn hint(message: &'a str) -> Self {
Self {
kind: FixSuggestionKind::Hint,
message,
}
}
}
struct AnsiColors;
impl deno_core::error::ErrorFormat for AnsiColors {
@ -129,6 +157,7 @@ fn format_aggregated_error(
index: nested_circular_reference_index,
}),
false,
vec![],
);
for line in error_string.trim_start_matches("Uncaught ").lines() {
@ -143,6 +172,7 @@ fn format_js_error_inner(
js_error: &JsError,
circular: Option<IndexedErrorReference>,
include_source_code: bool,
suggestions: Vec<FixSuggestion>,
) -> String {
let mut s = String::new();
@ -190,7 +220,7 @@ fn format_js_error_inner(
let error_string = if is_caused_by_circular {
cyan(format!("[Circular *{}]", circular.unwrap().index)).to_string()
} else {
format_js_error_inner(cause, circular, false)
format_js_error_inner(cause, circular, false, vec![])
};
write!(
@ -200,6 +230,21 @@ fn format_js_error_inner(
)
.unwrap();
}
if !suggestions.is_empty() {
write!(s, "\n\n").unwrap();
for (index, suggestion) in suggestions.iter().enumerate() {
write!(s, " ").unwrap();
match suggestion.kind {
FixSuggestionKind::Hint => write!(s, "{} ", cyan("hint:")).unwrap(),
FixSuggestionKind::Info => write!(s, "{} ", yellow("info:")).unwrap(),
};
write!(s, "{}", suggestion.message).unwrap();
if index != (suggestions.len() - 1) {
writeln!(s).unwrap();
}
}
}
s
}
@ -211,7 +256,21 @@ pub fn format_js_error(js_error: &JsError) -> String {
index: 1,
});
format_js_error_inner(js_error, circular, true)
format_js_error_inner(js_error, circular, true, vec![])
}
/// Format a [`JsError`] for terminal output, printing additional suggestions.
pub fn format_js_error_with_suggestions(
js_error: &JsError,
suggestions: Vec<FixSuggestion>,
) -> String {
let circular =
find_recursive_cause(js_error).map(|reference| IndexedErrorReference {
reference,
index: 1,
});
format_js_error_inner(js_error, circular, true, suggestions)
}
#[cfg(test)]

View file

@ -1,6 +1,16 @@
{
"steps": [
{ "args": "run -R index.cjs", "output": "index.out" },
{ "args": "run -R main.ts", "output": "main.out" }
{ "args": "run -R main.ts", "output": "main.out" },
{
"args": "run module_error.js",
"output": "module_error.out",
"exitCode": 1
},
{
"args": "run exports_error.js",
"output": "exports_error.out",
"exitCode": 1
}
]
}

View file

@ -0,0 +1 @@
Object.defineProperty(exports, "__esModule", { value: true });

View file

@ -0,0 +1,7 @@
error: Uncaught (in promise) ReferenceError: exports is not defined
Object.defineProperty(exports, "__esModule", { value: true });
^
at [WILDCARD]exports_error.js:1:23
info: Deno does not support CommonJS modules without `.cjs` extension.
hint: Rewrite this module to ESM or change the file extension to `.cjs`.

View file

@ -0,0 +1,3 @@
module.exports = {
foobar: "foobar",
};

View file

@ -0,0 +1,7 @@
error: Uncaught (in promise) ReferenceError: module is not defined
module.exports = {
^
at [WILDCARD]module_error.js:1:1
info: Deno does not support CommonJS modules without `.cjs` extension.
hint: Rewrite this module to ESM or change the file extension to `.cjs`.