Skip to content

Commit

Permalink
fix: update attr balancing logic
Browse files Browse the repository at this point in the history
  • Loading branch information
natemoo-re committed Sep 27, 2024
1 parent ec35c91 commit dfba2bc
Show file tree
Hide file tree
Showing 2 changed files with 60 additions and 10 deletions.
15 changes: 10 additions & 5 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -233,14 +233,19 @@ export function parse(input: string | ReturnType<typeof html>): any {
} else {
// Ensure attr expressions are balanced
if (!isBalanced(token[3])) {
token[3] += '>'
let infiniteloop = 0;
const max = token[3].length + str.slice(DOM_PARSER_RE.lastIndex).length;
while (!isBalanced(token[3])) {
if (infiniteloop === 999) throw new Error('Infinite loop detected!');
const chunk = str.substring(DOM_PARSER_RE.lastIndex, DOM_PARSER_RE.lastIndex + str.substring(DOM_PARSER_RE.lastIndex).indexOf('>'));
if (token[3].length > max) {
// TODO: better error stacktrace
throw new Error(`Syntax error!\n${token[3]}`);
}
const gtIndex = str.substring(DOM_PARSER_RE.lastIndex).indexOf('>');
if (gtIndex != -1) {
token[3] += '>'
}
const chunk = str.substring(DOM_PARSER_RE.lastIndex, DOM_PARSER_RE.lastIndex + gtIndex);
token[3] += chunk;
DOM_PARSER_RE.lastIndex += chunk.length + 1;
infiniteloop++;
}
}
tag = {
Expand Down
55 changes: 50 additions & 5 deletions test/astro.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,19 +17,64 @@ let value: string;
expect(output).toEqual(input);
});

it("does not hang on unmatched braces", async () => {
it("handles invalid expressions", async () => {
const input = `---
let value: string;
let value = { awesome: { }};
---
<h1>Hello {world}</h1>
<h1>Hello {world!}</h1>
<Component a="1" b="2" c={\`three\`} items={astro.map((item: string) => {
const value: string = item.split('{').reverse().join('{');
const value: string = item.split('').reverse().join('');
return value;
})}>Hello world</Component>`;
const doc = parse(input);
const output = await render(doc);
expect(output).toEqual(input);
});
});

it("gracefully handles elements inside attributes", async () => {
const input = `---
let value: string;
---
<h1>Hello {world}</h1>
<Component a="1" b="2" c={\`three\`} items={astro.map((item: string) => <li>{item}</li>)}>Hello world</Component>`;
const doc = parse(input);
expect(doc.children.at(-1).attributes.items).toEqual('{astro.map((item: string) => <li>{item}</li>)}');
const output = await render(doc);
expect(output).toEqual(input);
});

it("fails with error on unmatched braces", async () => {
const input = `
<Component items={astro.map(({item: string) => <li>{item}</li>)}>Hello world</Component>`;
let error: unknown;
try {
parse(input);
} catch (e) {
error = e;
}
expect(error).toBeInstanceOf(Error);
});

it("handles single-line comments", async () => {
const input = '<Component items={astro.map((item: string) => <li>\n//{{{((([[[[{item}\n</li>)}>Hello world</Component>';
const doc = parse(input);
const output = await render(doc);
expect(output).toEqual(input);
});

it("handles multi-line comments", async () => {
const input = '<Component items={astro.map((item: string) => <li>\n/* \n * {{{ }\n */\n{item}</li>)}>Hello world</Component>';
const doc = parse(input);
const output = await render(doc);
expect(output).toEqual(input);
});

it("handles regex", async () => {
const input = '<Component items={astro.map((item: string) => <li>\n{/{{{{/gmi.test(item)}\n</li>)}>Hello world</Component>';
const doc = parse(input);
const output = await render(doc);
expect(output).toEqual(input);
});
});

0 comments on commit dfba2bc

Please sign in to comment.