Skip to content

Commit

Permalink
feat: add cause to RespecError; show stacktrace with --verbose (#…
Browse files Browse the repository at this point in the history
  • Loading branch information
sidvishnoi authored Nov 26, 2024
1 parent 149bb4a commit 1f06b11
Show file tree
Hide file tree
Showing 11 changed files with 60 additions and 22 deletions.
3 changes: 1 addition & 2 deletions src/core/before-save.js
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,7 @@ function performTransformations(transforms, doc) {
const nameOrPosition = `\`${fn.name}\`` || `at position ${pos}`;
const msg = docLink`Function ${nameOrPosition}\` threw an error during processing of ${"[beforeSave]"}.`;
const hint = "See developer console.";
showError(msg, name, { hint });
console.error(err);
showError(msg, name, { hint, cause: err });
} finally {
pos++;
}
Expand Down
3 changes: 1 addition & 2 deletions src/core/caniuse.js
Original file line number Diff line number Diff line change
Expand Up @@ -103,8 +103,7 @@ export async function run(conf) {
function handleError(err, options, featureURL) {
const msg = `Failed to retrieve feature "${options.feature}".`;
const hint = docLink`Please check the feature key on [caniuse.com](https://caniuse.com) and update ${"[caniuse]"}.`;
showError(msg, name, { hint });
console.error(err);
showError(msg, name, { hint, cause: err });
return html`<a href="${featureURL}">caniuse.com</a>`;
}

Expand Down
3 changes: 1 addition & 2 deletions src/core/contrib.js
Original file line number Diff line number Diff line change
Expand Up @@ -60,8 +60,7 @@ async function showContributors(editors, apiURL) {
);
} catch (error) {
const msg = "Error loading contributors from GitHub.";
showError(msg, name);
console.error(error);
showError(msg, name, { cause: error });
return null;
}
}
Expand Down
7 changes: 4 additions & 3 deletions src/core/custom-elements/rs-changelog.js
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,9 @@ export const element = class ChangelogElement extends HTMLElement {
${{
any: fetchCommits(from, to, filter)
.then(commits => toHTML(commits))
.catch(error => showError(error.message, name, { elements: [this] }))
.catch(error =>
showError(error.message, name, { elements: [this], cause: error })
)
.finally(() => {
this.dispatchEvent(new CustomEvent("done"));
}),
Expand Down Expand Up @@ -70,8 +72,7 @@ async function fetchCommits(from, to, filter) {
commits = commits.filter(filter);
} catch (error) {
const msg = `Error loading commits from GitHub. ${error.message}`;
console.error(error);
throw new Error(msg);
throw new Error(msg, { cause: error });
}
return commits;
}
Expand Down
3 changes: 1 addition & 2 deletions src/core/data-include.js
Original file line number Diff line number Diff line change
Expand Up @@ -102,8 +102,7 @@ async function runIncludes(root, currentDepth) {
}
} catch (err) {
const msg = `\`data-include\` failed: \`${url}\` (${err.message}).`;
console.error(msg, el, err);
showError(msg, name, { elements: [el] });
showError(msg, name, { elements: [el], cause: err });
}
});
await Promise.all(promisesToInclude);
Expand Down
3 changes: 1 addition & 2 deletions src/core/post-process.js
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,7 @@ export async function run(config) {
} catch (err) {
const msg = `Function ${f.name} threw an error during \`postProcess\`.`;
const hint = "See developer console.";
showError(msg, name, { hint });
console.error(err);
showError(msg, name, { hint, cause: err });
}
});
await Promise.all(promises);
Expand Down
3 changes: 1 addition & 2 deletions src/core/pre-process.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,7 @@ export async function run(config) {
} catch (err) {
const msg = `Function ${f.name} threw an error during \`preProcess\`.`;
const hint = "See developer console.";
showError(msg, name, { hint });
console.error(err);
showError(msg, name, { hint, cause: err });
}
});
await Promise.all(promises);
Expand Down
25 changes: 21 additions & 4 deletions src/core/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -294,8 +294,7 @@ export function runTransforms(content, flist, ...funcArgs) {
} catch (e) {
const msg = `call to \`${meth}()\` failed with: ${e}.`;
const hint = "See developer console for stack trace.";
showWarning(msg, "utils/runTransforms", { hint });
console.error(e);
showWarning(msg, "utils/runTransforms", { hint, cause: e });
}
}
}
Expand Down Expand Up @@ -850,7 +849,7 @@ export class RespecError extends Error {
* @param {Parameters<typeof showError>[2] & { isWarning: boolean }} options
*/
constructor(message, plugin, options) {
super(message);
super(message, { ...(options.cause && { cause: options.cause }) });
const name = options.isWarning ? "ReSpecWarning" : "ReSpecError";
Object.assign(this, { message, plugin, name, ...options });
if (options.elements) {
Expand All @@ -864,7 +863,23 @@ export class RespecError extends Error {
const { message, name, stack } = this;
// @ts-expect-error https://github.com/microsoft/TypeScript/issues/26792
const { plugin, hint, elements, title, details } = this;
return { message, name, plugin, hint, elements, title, details, stack };
return {
message,
name,
plugin,
hint,
elements,
title,
details,
stack,
...(this.cause instanceof Error && {
cause: {
name: this.cause.name,
message: this.cause.message,
stack: this.cause.stack,
},
}),
};
}
}

Expand All @@ -876,6 +891,7 @@ export class RespecError extends Error {
* @param {HTMLElement[]} [options.elements] Offending elements.
* @param {string} [options.title] Title attribute for offending elements. Can be a shorter form of the message.
* @param {string} [options.details] Any further details/context.
* @param {Error} [options.cause] The error that caused this one.
*/
export function showError(message, pluginName, options = {}) {
const opts = { ...options, isWarning: false };
Expand All @@ -890,6 +906,7 @@ export function showError(message, pluginName, options = {}) {
* @param {HTMLElement[]} [options.elements] Offending elements.
* @param {string} [options.title] Title attribute for offending elements. Can be a shorter form of the message.
* @param {string} [options.details] Any further details/context.
* @param {Error} [options.cause] The error that caused this one.
*/
export function showWarning(message, pluginName, options = {}) {
const opts = { ...options, isWarning: true };
Expand Down
4 changes: 2 additions & 2 deletions src/jsconfig.json
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
{
"compilerOptions": {
"target": "es2019",
"target": "es2022",
"moduleResolution": "node",
"noImplicitThis": true,
"module": "es2020"
"module": "es2022"
}
}
19 changes: 18 additions & 1 deletion tools/respec2html.js
Original file line number Diff line number Diff line change
Expand Up @@ -90,15 +90,32 @@ class Logger {

/** @param {import("./respecDocWriter").ReSpecError} rsError */
_printDetails(rsError) {
const shouldPrintStacktrace = this._shouldPrintStacktrace(rsError);
const print = (title, value) => {
if (!value) return;
const padWidth = "Plugin".length + 1; // "Plugin" is the longest title
const longestTitle = shouldPrintStacktrace ? "Stacktrace" : "Plugin";
const padWidth = longestTitle.length + 1;
const paddedTitle = `${title}:`.padStart(padWidth);
console.error(" ", colors.bold(paddedTitle), this._formatMarkdown(value));
};
print("Count", rsError.elements && String(rsError.elements.length));
print("Plugin", rsError.plugin);
print("Hint", rsError.hint);
if (shouldPrintStacktrace) {
let stacktrace = `${rsError.stack}`;
if (rsError.cause) {
stacktrace += `\n ${colors.bold("Caused by:")} ${rsError.cause.stack.split("\n").join("\n ")}`;
}
print("Stacktrace", stacktrace);
}
}

_shouldPrintStacktrace(rsError) {
return (
this.verbose &&
!!rsError.stack &&
(!!rsError.cause?.stack || rsError.plugin === "unknown")
);
}
}

Expand Down
9 changes: 9 additions & 0 deletions tools/respecDocWriter.js
Original file line number Diff line number Diff line change
Expand Up @@ -310,10 +310,19 @@ function handleConsoleMessages(page, onError, onWarning) {
// Old ReSpec versions might report errors as strings.
return JSON.stringify({ message: String(obj) });
} else if (obj instanceof Error && !obj.plugin) {
let cause;
if (obj.cause instanceof Error) {
cause = {
name: obj.cause.name,
message: obj.cause.message,
stack: obj.cause.stack,
};
}
return JSON.stringify({
message: obj.message,
plugin: "unknown",
name: obj.name,
cause,
stack: obj.stack?.replace(
obj.message,
`${obj.message.slice(0, 30)}…`
Expand Down

0 comments on commit 1f06b11

Please sign in to comment.