summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--cli/main.rs23
-rw-r--r--runtime/fmt_errors.rs63
-rw-r--r--tests/specs/run/import_common_js/__test__.jsonc12
-rw-r--r--tests/specs/run/import_common_js/exports_error.js1
-rw-r--r--tests/specs/run/import_common_js/exports_error.out7
-rw-r--r--tests/specs/run/import_common_js/module_error.js3
-rw-r--r--tests/specs/run/import_common_js/module_error.out7
7 files changed, 111 insertions, 5 deletions
diff --git a/cli/main.rs b/cli/main.rs
index 3b366306a..da274d8ed 100644
--- a/cli/main.rs
+++ b/cli/main.rs
@@ -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>()
{
diff --git a/runtime/fmt_errors.rs b/runtime/fmt_errors.rs
index 4687dbd47..b2cec2a5a 100644
--- a/runtime/fmt_errors.rs
+++ b/runtime/fmt_errors.rs
@@ -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)]
diff --git a/tests/specs/run/import_common_js/__test__.jsonc b/tests/specs/run/import_common_js/__test__.jsonc
index a09929cdd..950c7f68c 100644
--- a/tests/specs/run/import_common_js/__test__.jsonc
+++ b/tests/specs/run/import_common_js/__test__.jsonc
@@ -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
+ }
]
}
diff --git a/tests/specs/run/import_common_js/exports_error.js b/tests/specs/run/import_common_js/exports_error.js
new file mode 100644
index 000000000..a6a850fe2
--- /dev/null
+++ b/tests/specs/run/import_common_js/exports_error.js
@@ -0,0 +1 @@
+Object.defineProperty(exports, "__esModule", { value: true });
diff --git a/tests/specs/run/import_common_js/exports_error.out b/tests/specs/run/import_common_js/exports_error.out
new file mode 100644
index 000000000..41eda2457
--- /dev/null
+++ b/tests/specs/run/import_common_js/exports_error.out
@@ -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`.
diff --git a/tests/specs/run/import_common_js/module_error.js b/tests/specs/run/import_common_js/module_error.js
new file mode 100644
index 000000000..59a3cc0e7
--- /dev/null
+++ b/tests/specs/run/import_common_js/module_error.js
@@ -0,0 +1,3 @@
+module.exports = {
+ foobar: "foobar",
+};
diff --git a/tests/specs/run/import_common_js/module_error.out b/tests/specs/run/import_common_js/module_error.out
new file mode 100644
index 000000000..53a908d29
--- /dev/null
+++ b/tests/specs/run/import_common_js/module_error.out
@@ -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`.