mirror of
https://github.com/odoo/owl.git
synced 2025-10-06 19:59:41 +07:00
[IMP] compiler: improve error message when failing to compile template
Previously, when a template failed to compile because of a syntax error (typically because we don't do any syntax checking when compiling expression, allowing invalid expressions to be transpiled successfully), the error reporting was very minimal: you would only get the error message itself (eg: "Unexpected token") with the stack information of the error pointing to the call to `new Function` in owl, which is not very useful. This commit catches the compilation error and completes it with information about the template name when available, and also adds the generated code to the error message, allowing the user to just copy/paste it in their web console or code editor to get a more precise location for the error.
This commit is contained in:
committed by
Géry Debongnie
parent
7bc9d34a64
commit
9d99f8936b
@@ -37,4 +37,22 @@ describe("basic validation", () => {
|
||||
const template = `<div t-best-beer="rochefort 10">test</div>`;
|
||||
expect(() => renderToString(template)).toThrow("Unknown QWeb directive: 't-best-beer'");
|
||||
});
|
||||
|
||||
test("compilation error", () => {
|
||||
const template = `<div t-att-class="a b">test</div>`;
|
||||
expect(() => renderToString(template))
|
||||
.toThrow(`Failed to compile anonymous template: Unexpected identifier
|
||||
|
||||
generated code:
|
||||
function(app, bdom, helpers) {
|
||||
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
|
||||
|
||||
let block1 = createBlock(\`<div block-attribute-0="class">test</div>\`);
|
||||
|
||||
return function template(ctx, node, key = "") {
|
||||
let attr1 = ctx['a']ctx['b'];
|
||||
return block1([attr1]);
|
||||
}
|
||||
}`);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -12,6 +12,18 @@ exports[`basics display a nice error if a component is not a component 1`] = `
|
||||
}"
|
||||
`;
|
||||
|
||||
exports[`basics display a nice error if a non-root component template fails to compile 1`] = `
|
||||
"function anonymous(app, bdom, helpers
|
||||
) {
|
||||
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
|
||||
const comp1 = app.createComponent(\`Child\`, true, false, false, []);
|
||||
|
||||
return function template(ctx, node, key = \\"\\") {
|
||||
return comp1({}, key + \`__1\`, node, this, null);
|
||||
}
|
||||
}"
|
||||
`;
|
||||
|
||||
exports[`basics display a nice error if it cannot find component (in dev mode) 1`] = `
|
||||
"function anonymous(app, bdom, helpers
|
||||
) {
|
||||
|
||||
@@ -143,6 +143,66 @@ describe("basics", () => {
|
||||
);
|
||||
});
|
||||
|
||||
test("display a nice error if the root component template fails to compile", async () => {
|
||||
// This is a special case: mount throws synchronously and we don't have any
|
||||
// node which can handle the error, hence the different structure of this test
|
||||
class Comp extends Component {
|
||||
static template = xml`<div t-att-class="a b">test</div>`;
|
||||
}
|
||||
const app = new App(Comp);
|
||||
let error: Error;
|
||||
try {
|
||||
await app.mount(fixture);
|
||||
} catch (e) {
|
||||
error = e as Error;
|
||||
}
|
||||
const expectedErrorMessage = `Failed to compile anonymous template: Unexpected identifier
|
||||
|
||||
generated code:
|
||||
function(app, bdom, helpers) {
|
||||
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
|
||||
|
||||
let block1 = createBlock(\`<div block-attribute-0="class">test</div>\`);
|
||||
|
||||
return function template(ctx, node, key = "") {
|
||||
let attr1 = ctx['a']ctx['b'];
|
||||
return block1([attr1]);
|
||||
}
|
||||
}`;
|
||||
expect(error!).toBeDefined();
|
||||
expect(error!.message).toBe(expectedErrorMessage);
|
||||
});
|
||||
|
||||
test("display a nice error if a non-root component template fails to compile", async () => {
|
||||
class Child extends Component {
|
||||
static template = xml`<div t-att-class="a b">test</div>`;
|
||||
}
|
||||
class Parent extends Component {
|
||||
static components = { Child };
|
||||
static template = xml`<Child/>`;
|
||||
}
|
||||
const expectedErrorMessage = `Failed to compile anonymous template: Unexpected identifier
|
||||
|
||||
generated code:
|
||||
function(app, bdom, helpers) {
|
||||
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
|
||||
|
||||
let block1 = createBlock(\`<div block-attribute-0="class">test</div>\`);
|
||||
|
||||
return function template(ctx, node, key = "") {
|
||||
let attr1 = ctx['a']ctx['b'];
|
||||
return block1([attr1]);
|
||||
}
|
||||
}`;
|
||||
const app = new App(Parent as typeof Component);
|
||||
let error: Error;
|
||||
const mountProm = app.mount(fixture).catch((e: Error) => (error = e));
|
||||
await expect(nextAppError(app)).resolves.toThrow(expectedErrorMessage);
|
||||
await mountProm;
|
||||
expect(error!).toBeDefined();
|
||||
expect(error!.message).toBe(expectedErrorMessage);
|
||||
});
|
||||
|
||||
test("simple catchError", async () => {
|
||||
class Boom extends Component {
|
||||
static template = xml`<div t-esc="a.b.c"/>`;
|
||||
|
||||
@@ -988,7 +988,7 @@ describe("Portal: Props validation", () => {
|
||||
error = e as Error;
|
||||
}
|
||||
expect(error!).toBeDefined();
|
||||
expect(error!.message).toBe(`Unexpected token ','`);
|
||||
expect(error!.message).toContain(`Unexpected token ','`);
|
||||
});
|
||||
|
||||
test("target must be a valid selector", async () => {
|
||||
|
||||
Reference in New Issue
Block a user