Skip to content

Commit 1f06b11

Browse files
authored
feat: add cause to RespecError; show stacktrace with --verbose (#4782)
1 parent 149bb4a commit 1f06b11

11 files changed

+60
-22
lines changed

src/core/before-save.js

+1-2
Original file line numberDiff line numberDiff line change
@@ -38,8 +38,7 @@ function performTransformations(transforms, doc) {
3838
const nameOrPosition = `\`${fn.name}\`` || `at position ${pos}`;
3939
const msg = docLink`Function ${nameOrPosition}\` threw an error during processing of ${"[beforeSave]"}.`;
4040
const hint = "See developer console.";
41-
showError(msg, name, { hint });
42-
console.error(err);
41+
showError(msg, name, { hint, cause: err });
4342
} finally {
4443
pos++;
4544
}

src/core/caniuse.js

+1-2
Original file line numberDiff line numberDiff line change
@@ -103,8 +103,7 @@ export async function run(conf) {
103103
function handleError(err, options, featureURL) {
104104
const msg = `Failed to retrieve feature "${options.feature}".`;
105105
const hint = docLink`Please check the feature key on [caniuse.com](https://caniuse.com) and update ${"[caniuse]"}.`;
106-
showError(msg, name, { hint });
107-
console.error(err);
106+
showError(msg, name, { hint, cause: err });
108107
return html`<a href="${featureURL}">caniuse.com</a>`;
109108
}
110109

src/core/contrib.js

+1-2
Original file line numberDiff line numberDiff line change
@@ -60,8 +60,7 @@ async function showContributors(editors, apiURL) {
6060
);
6161
} catch (error) {
6262
const msg = "Error loading contributors from GitHub.";
63-
showError(msg, name);
64-
console.error(error);
63+
showError(msg, name, { cause: error });
6564
return null;
6665
}
6766
}

src/core/custom-elements/rs-changelog.js

+4-3
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,9 @@ export const element = class ChangelogElement extends HTMLElement {
3434
${{
3535
any: fetchCommits(from, to, filter)
3636
.then(commits => toHTML(commits))
37-
.catch(error => showError(error.message, name, { elements: [this] }))
37+
.catch(error =>
38+
showError(error.message, name, { elements: [this], cause: error })
39+
)
3840
.finally(() => {
3941
this.dispatchEvent(new CustomEvent("done"));
4042
}),
@@ -70,8 +72,7 @@ async function fetchCommits(from, to, filter) {
7072
commits = commits.filter(filter);
7173
} catch (error) {
7274
const msg = `Error loading commits from GitHub. ${error.message}`;
73-
console.error(error);
74-
throw new Error(msg);
75+
throw new Error(msg, { cause: error });
7576
}
7677
return commits;
7778
}

src/core/data-include.js

+1-2
Original file line numberDiff line numberDiff line change
@@ -102,8 +102,7 @@ async function runIncludes(root, currentDepth) {
102102
}
103103
} catch (err) {
104104
const msg = `\`data-include\` failed: \`${url}\` (${err.message}).`;
105-
console.error(msg, el, err);
106-
showError(msg, name, { elements: [el] });
105+
showError(msg, name, { elements: [el], cause: err });
107106
}
108107
});
109108
await Promise.all(promisesToInclude);

src/core/post-process.js

+1-2
Original file line numberDiff line numberDiff line change
@@ -32,8 +32,7 @@ export async function run(config) {
3232
} catch (err) {
3333
const msg = `Function ${f.name} threw an error during \`postProcess\`.`;
3434
const hint = "See developer console.";
35-
showError(msg, name, { hint });
36-
console.error(err);
35+
showError(msg, name, { hint, cause: err });
3736
}
3837
});
3938
await Promise.all(promises);

src/core/pre-process.js

+1-2
Original file line numberDiff line numberDiff line change
@@ -31,8 +31,7 @@ export async function run(config) {
3131
} catch (err) {
3232
const msg = `Function ${f.name} threw an error during \`preProcess\`.`;
3333
const hint = "See developer console.";
34-
showError(msg, name, { hint });
35-
console.error(err);
34+
showError(msg, name, { hint, cause: err });
3635
}
3736
});
3837
await Promise.all(promises);

src/core/utils.js

+21-4
Original file line numberDiff line numberDiff line change
@@ -294,8 +294,7 @@ export function runTransforms(content, flist, ...funcArgs) {
294294
} catch (e) {
295295
const msg = `call to \`${meth}()\` failed with: ${e}.`;
296296
const hint = "See developer console for stack trace.";
297-
showWarning(msg, "utils/runTransforms", { hint });
298-
console.error(e);
297+
showWarning(msg, "utils/runTransforms", { hint, cause: e });
299298
}
300299
}
301300
}
@@ -850,7 +849,7 @@ export class RespecError extends Error {
850849
* @param {Parameters<typeof showError>[2] & { isWarning: boolean }} options
851850
*/
852851
constructor(message, plugin, options) {
853-
super(message);
852+
super(message, { ...(options.cause && { cause: options.cause }) });
854853
const name = options.isWarning ? "ReSpecWarning" : "ReSpecError";
855854
Object.assign(this, { message, plugin, name, ...options });
856855
if (options.elements) {
@@ -864,7 +863,23 @@ export class RespecError extends Error {
864863
const { message, name, stack } = this;
865864
// @ts-expect-error https://github.com/microsoft/TypeScript/issues/26792
866865
const { plugin, hint, elements, title, details } = this;
867-
return { message, name, plugin, hint, elements, title, details, stack };
866+
return {
867+
message,
868+
name,
869+
plugin,
870+
hint,
871+
elements,
872+
title,
873+
details,
874+
stack,
875+
...(this.cause instanceof Error && {
876+
cause: {
877+
name: this.cause.name,
878+
message: this.cause.message,
879+
stack: this.cause.stack,
880+
},
881+
}),
882+
};
868883
}
869884
}
870885

@@ -876,6 +891,7 @@ export class RespecError extends Error {
876891
* @param {HTMLElement[]} [options.elements] Offending elements.
877892
* @param {string} [options.title] Title attribute for offending elements. Can be a shorter form of the message.
878893
* @param {string} [options.details] Any further details/context.
894+
* @param {Error} [options.cause] The error that caused this one.
879895
*/
880896
export function showError(message, pluginName, options = {}) {
881897
const opts = { ...options, isWarning: false };
@@ -890,6 +906,7 @@ export function showError(message, pluginName, options = {}) {
890906
* @param {HTMLElement[]} [options.elements] Offending elements.
891907
* @param {string} [options.title] Title attribute for offending elements. Can be a shorter form of the message.
892908
* @param {string} [options.details] Any further details/context.
909+
* @param {Error} [options.cause] The error that caused this one.
893910
*/
894911
export function showWarning(message, pluginName, options = {}) {
895912
const opts = { ...options, isWarning: true };

src/jsconfig.json

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
{
22
"compilerOptions": {
3-
"target": "es2019",
3+
"target": "es2022",
44
"moduleResolution": "node",
55
"noImplicitThis": true,
6-
"module": "es2020"
6+
"module": "es2022"
77
}
88
}

tools/respec2html.js

+18-1
Original file line numberDiff line numberDiff line change
@@ -90,15 +90,32 @@ class Logger {
9090

9191
/** @param {import("./respecDocWriter").ReSpecError} rsError */
9292
_printDetails(rsError) {
93+
const shouldPrintStacktrace = this._shouldPrintStacktrace(rsError);
9394
const print = (title, value) => {
9495
if (!value) return;
95-
const padWidth = "Plugin".length + 1; // "Plugin" is the longest title
96+
const longestTitle = shouldPrintStacktrace ? "Stacktrace" : "Plugin";
97+
const padWidth = longestTitle.length + 1;
9698
const paddedTitle = `${title}:`.padStart(padWidth);
9799
console.error(" ", colors.bold(paddedTitle), this._formatMarkdown(value));
98100
};
99101
print("Count", rsError.elements && String(rsError.elements.length));
100102
print("Plugin", rsError.plugin);
101103
print("Hint", rsError.hint);
104+
if (shouldPrintStacktrace) {
105+
let stacktrace = `${rsError.stack}`;
106+
if (rsError.cause) {
107+
stacktrace += `\n ${colors.bold("Caused by:")} ${rsError.cause.stack.split("\n").join("\n ")}`;
108+
}
109+
print("Stacktrace", stacktrace);
110+
}
111+
}
112+
113+
_shouldPrintStacktrace(rsError) {
114+
return (
115+
this.verbose &&
116+
!!rsError.stack &&
117+
(!!rsError.cause?.stack || rsError.plugin === "unknown")
118+
);
102119
}
103120
}
104121

tools/respecDocWriter.js

+9
Original file line numberDiff line numberDiff line change
@@ -310,10 +310,19 @@ function handleConsoleMessages(page, onError, onWarning) {
310310
// Old ReSpec versions might report errors as strings.
311311
return JSON.stringify({ message: String(obj) });
312312
} else if (obj instanceof Error && !obj.plugin) {
313+
let cause;
314+
if (obj.cause instanceof Error) {
315+
cause = {
316+
name: obj.cause.name,
317+
message: obj.cause.message,
318+
stack: obj.cause.stack,
319+
};
320+
}
313321
return JSON.stringify({
314322
message: obj.message,
315323
plugin: "unknown",
316324
name: obj.name,
325+
cause,
317326
stack: obj.stack?.replace(
318327
obj.message,
319328
`${obj.message.slice(0, 30)}…`

0 commit comments

Comments
 (0)