Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: more readable and usable error message when a render error occurs #154

Merged
merged 2 commits into from
Jan 29, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 5 additions & 2 deletions internal/plugin-vue3/src/generate-code.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,11 @@ export function generateCode(options: ExampleOptions): ExampleResult {
const { descriptor, errors } = parse(code, { filename });
const scopeId = `data-v-${fingerprint}`;

if (errors.length) {
throw new Error(`Errors occured when trying to parse ${filename}.`);
if (errors.length > 0) {
const first = errors[0].message;
throw new Error(
`Errors occured when trying to parse "${filename}": ${first}`,
);
}

const hasScoped = descriptor.styles.some((e) => e.scoped);
Expand Down
8 changes: 7 additions & 1 deletion src/render/render.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import { TemplateLoader } from "./template-loader";
import * as filter from "./filter";
import { findTemplate } from "./find-template";
import { createMarkdownRenderer } from "./create-markdown-renderer";
import { TemplateRenderError } from "./template-render-error";

interface ActiveNavigationLeaf extends NavigationLeaf {
active: boolean;
Expand Down Expand Up @@ -328,7 +329,12 @@ export async function render(
try {
content = await renderTemplate(template, templateData);
} catch (err: unknown) {
const prefix = `Failed to render "${fileInfo.fullPath}"`;
const filename = fileInfo.fullPath;
if (err instanceof Error && err.name === "Template render error") {
/* recreate nunjucks template errors to be a bit more useful and readable */
throw new TemplateRenderError(err.message, filename);
}
const prefix = `Failed to render "${filename}"`;
const message = err instanceof Error ? err.message : String(err);
throw new Error(`${prefix}: ${message}`, { cause: err });
}
Expand Down
18 changes: 18 additions & 0 deletions src/render/template-render-error.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { getActualMessage } from "./template-render-error";

it("should extract actual error from nunjucks error", () => {
expect.assertions(1);
const error = [
"(/path/to/template.html) [Line 8, Column 3]",
" Error: lorem ipsum dolor sit amet",
].join("\n");
const result = getActualMessage(error);
expect(result).toBe("lorem ipsum dolor sit amet");
});

it("should return original message if it cannot be matched", () => {
expect.assertions(1);
const error = ["lorem ipsum dolor sit amet"].join("\n");
const result = getActualMessage(error);
expect(result).toBe("lorem ipsum dolor sit amet");
});
39 changes: 39 additions & 0 deletions src/render/template-render-error.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
const messageRegex = /^[(][^)]+[)] \[Line \d+, Column \d+\]\n\s+Error: (.*)$/;

/**
* Extracts the actual error message from a nunjucks render error.
*
* The error instance should have properties such as `.cause` but these are lost
* somewhere so this serves as a workaround.
*
* @internal
*/
export function getActualMessage(message: string): string {
const match = message.match(messageRegex);
if (match) {
return match[1].trim();
} else {
return message;
}
}

/**
* @internal
*/
export class TemplateRenderError extends Error {
private readonly filename: string;

public constructor(message: string, filename: string) {
super(getActualMessage(message));
this.name = "RenderError";
this.filename = filename;
}

public prettyError(): string {
return [
`An error occured when rendering a document.`,
` Document: "${this.filename}".`,
` Message: ${this.message}`,
].join("\n");
}
}