diff --git a/cli/fmt_errors.rs b/cli/fmt_errors.rs index 53a8535f0a..2b95f0fc7e 100644 --- a/cli/fmt_errors.rs +++ b/cli/fmt_errors.rs @@ -5,13 +5,40 @@ use crate::colors::italic_bold; use crate::colors::red; use crate::colors::yellow; use deno_core::error::{AnyError, JsError, JsStackFrame}; +use deno_core::url::Url; use std::error::Error; use std::fmt; use std::ops::Deref; const SOURCE_ABBREV_THRESHOLD: usize = 150; +const DATA_URL_ABBREV_THRESHOLD: usize = 150; -// Keep in sync with `runtime/js/40_error_stack.js`. +pub fn format_file_name(file_name: &str) -> String { + if file_name.len() > DATA_URL_ABBREV_THRESHOLD { + if let Ok(url) = Url::parse(file_name) { + if url.scheme() == "data" { + let data_path = url.path(); + if let Some(data_pieces) = data_path.split_once(',') { + let data_length = data_pieces.1.len(); + if let Some(data_start) = data_pieces.1.get(0..20) { + if let Some(data_end) = data_pieces.1.get(data_length - 20..) { + return format!( + "{}:{},{}......{}", + url.scheme(), + data_pieces.0, + data_start, + data_end + ); + } + } + } + } + } + } + file_name.to_string() +} + +// Keep in sync with `/core/error.js`. pub fn format_location(frame: &JsStackFrame) -> String { let _internal = frame .file_name @@ -22,7 +49,7 @@ pub fn format_location(frame: &JsStackFrame) -> String { } let mut result = String::new(); if let Some(file_name) = &frame.file_name { - result += &cyan(&file_name).to_string(); + result += &cyan(&format_file_name(file_name)).to_string(); } else { if frame.is_eval { result += diff --git a/cli/ops/errors.rs b/cli/ops/errors.rs index 453a9ff6b9..a889b1b0d2 100644 --- a/cli/ops/errors.rs +++ b/cli/ops/errors.rs @@ -1,6 +1,7 @@ // Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. use crate::diagnostics::Diagnostics; +use crate::fmt_errors::format_file_name; use crate::program_state::ProgramState; use crate::source_maps::get_orig_position; use crate::source_maps::CachedMaps; @@ -16,6 +17,7 @@ use std::sync::Arc; pub fn init(rt: &mut deno_core::JsRuntime) { super::reg_sync(rt, "op_apply_source_map", op_apply_source_map); super::reg_sync(rt, "op_format_diagnostic", op_format_diagnostic); + super::reg_sync(rt, "op_format_file_name", op_format_file_name); } #[derive(Deserialize)] @@ -60,3 +62,11 @@ fn op_format_diagnostic( let diagnostic: Diagnostics = serde_json::from_value(args)?; Ok(json!(diagnostic.to_string())) } + +fn op_format_file_name( + _state: &mut OpState, + file_name: String, + _: (), +) -> Result { + Ok(format_file_name(&file_name)) +} diff --git a/cli/tests/integration/run_tests.rs b/cli/tests/integration/run_tests.rs index 14e0e12416..04ba10b7b4 100644 --- a/cli/tests/integration/run_tests.rs +++ b/cli/tests/integration/run_tests.rs @@ -1885,3 +1885,9 @@ itest!(dom_exception_formatting { output: "dom_exception_formatting.ts.out", exit_code: 1, }); + +itest!(long_data_url_formatting { + args: "run long_data_url_formatting.ts", + output: "long_data_url_formatting.ts.out", + exit_code: 1, +}); diff --git a/cli/tests/testdata/import_data_url_error_stack.ts.out b/cli/tests/testdata/import_data_url_error_stack.ts.out index d456ad0ea4..ccd72e4add 100644 --- a/cli/tests/testdata/import_data_url_error_stack.ts.out +++ b/cli/tests/testdata/import_data_url_error_stack.ts.out @@ -1,6 +1,6 @@ [WILDCARD]error: Uncaught Error: Hello 2 throw new Error(`Hello ${A.C}`); ^ - at a (data:application/typescript;base64,ZW51bSBBIHsKICBBLAogIEIsCiAgQywKIH0KIAogZXhwb3J0IGZ1bmN0aW9uIGEoKSB7CiAgIHRocm93IG5ldyBFcnJvcihgSGVsbG8gJHtBLkN9YCk7CiB9CiA=:8:10) + at a (data:application/typescript;base64,ZW51bSBBIHsKICBBLAog......JHtBLkN9YCk7CiB9CiA=:8:10) at file:///[WILDCARD]/import_data_url_error_stack.ts:3:1 [WILDCARD] diff --git a/cli/tests/testdata/long_data_url_formatting.ts b/cli/tests/testdata/long_data_url_formatting.ts new file mode 100644 index 0000000000..2ed2d5a036 --- /dev/null +++ b/cli/tests/testdata/long_data_url_formatting.ts @@ -0,0 +1,3 @@ +await import( + 'data:application/typescript,console.trace("foo"); const error = new Error("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"); console.log(error.stack); throw error;' +); diff --git a/cli/tests/testdata/long_data_url_formatting.ts.out b/cli/tests/testdata/long_data_url_formatting.ts.out new file mode 100644 index 0000000000..ea78c25910 --- /dev/null +++ b/cli/tests/testdata/long_data_url_formatting.ts.out @@ -0,0 +1,8 @@ +[WILDCARD]Trace: foo + at data:application/typescript,console.trace("foo")......stack); throw error;:1:9 +Error: aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa + at data:application/typescript,console.trace("foo")......stack); throw error;:1:37 +error: Uncaught (in promise) Error: aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +console.trace("foo"); const error = new Error("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"); console.log(error.stack); throw error; + ^ + at data:application/typescript,console.trace("foo")......stack); throw error;:1:37 diff --git a/core/02_error.js b/core/02_error.js index c6f6426307..4ec9f7267c 100644 --- a/core/02_error.js +++ b/core/02_error.js @@ -70,7 +70,7 @@ } // Keep in sync with `cli/fmt_errors.rs`. - function formatLocation(callSite) { + function formatLocation(callSite, formatFileNameFn) { if (callSite.isNative()) { return "native"; } @@ -80,7 +80,7 @@ const fileName = callSite.getFileName(); if (fileName) { - result += fileName; + result += formatFileNameFn(fileName); } else { if (callSite.isEval()) { const evalOrigin = callSite.getEvalOrigin(); @@ -106,7 +106,7 @@ } // Keep in sync with `cli/fmt_errors.rs`. - function formatCallSite(callSite) { + function formatCallSite(callSite, formatFileNameFn) { let result = ""; const functionName = callSite.getFunctionName(); @@ -159,11 +159,11 @@ } else if (functionName) { result += functionName; } else { - result += formatLocation(callSite); + result += formatLocation(callSite, formatFileNameFn); return result; } - result += ` (${formatLocation(callSite)})`; + result += ` (${formatLocation(callSite, formatFileNameFn)})`; return result; } @@ -205,7 +205,7 @@ * columnNumber: number * }} sourceMappingFn */ - function createPrepareStackTrace(sourceMappingFn) { + function createPrepareStackTrace(sourceMappingFn, formatFileNameFn) { return function prepareStackTrace( error, callSites, @@ -235,7 +235,10 @@ const formattedCallSites = []; for (const callSite of mappedCallSites) { ArrayPrototypePush(error.__callSiteEvals, evaluateCallSite(callSite)); - ArrayPrototypePush(formattedCallSites, formatCallSite(callSite)); + ArrayPrototypePush( + formattedCallSites, + formatCallSite(callSite, formatFileNameFn), + ); } const message = error.message !== undefined ? error.message : ""; const name = error.name !== undefined ? error.name : "Error"; diff --git a/runtime/js/40_error_stack.js b/runtime/js/40_error_stack.js index e739750440..c8f2aff5a5 100644 --- a/runtime/js/40_error_stack.js +++ b/runtime/js/40_error_stack.js @@ -8,6 +8,10 @@ return core.opSync("op_format_diagnostic", diagnostics); } + function opFormatFileName(location) { + return core.opSync("op_format_file_name", location); + } + function opApplySourceMap(location) { const res = core.opSync("op_apply_source_map", location); return { @@ -18,7 +22,8 @@ } window.__bootstrap.errorStack = { - opApplySourceMap, opFormatDiagnostics, + opFormatFileName, + opApplySourceMap, }; })(this); diff --git a/runtime/js/99_main.js b/runtime/js/99_main.js index 5c1c047664..b16fabe8ad 100644 --- a/runtime/js/99_main.js +++ b/runtime/js/99_main.js @@ -218,17 +218,13 @@ delete Object.prototype.__proto__; ); build.setBuildInfo(runtimeOptions.target); util.setLogDebug(runtimeOptions.debugFlag, source); - // TODO(bartlomieju): a very crude way to disable - // source mapping of errors. This condition is true - // only for compiled standalone binaries. - let prepareStackTrace; - if (runtimeOptions.applySourceMaps) { - prepareStackTrace = core.createPrepareStackTrace( - errorStack.opApplySourceMap, - ); - } else { - prepareStackTrace = core.createPrepareStackTrace(); - } + const prepareStackTrace = core.createPrepareStackTrace( + // TODO(bartlomieju): a very crude way to disable + // source mapping of errors. This condition is true + // only for compiled standalone binaries. + runtimeOptions.applySourceMaps ? errorStack.opApplySourceMap : undefined, + errorStack.opFormatFileName, + ); // deno-lint-ignore prefer-primordials Error.prepareStackTrace = prepareStackTrace; }