Skip to content
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
5 changes: 5 additions & 0 deletions .changeset/tiny-kings-serve.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'svelte': patch
---

chore: more loose parser improvements
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,10 @@ import { find_matching_bracket } from '../utils/bracket.js';

/**
* @param {Parser} parser
* @param {string} [opening_token]
* @returns {Expression}
*/
export default function read_expression(parser) {
export default function read_expression(parser, opening_token) {
try {
const node = parse_expression_at(parser.template, parser.ts, parser.index);

Expand Down Expand Up @@ -42,7 +43,7 @@ export default function read_expression(parser) {
} catch (err) {
if (parser.loose) {
// Find the next } and treat it as the end of the expression
const end = find_matching_bracket(parser.template, parser.index, '{');
const end = find_matching_bracket(parser.template, parser.index, opening_token ?? '{');
if (end) {
const start = parser.index;
parser.index = end;
Expand Down
9 changes: 6 additions & 3 deletions packages/svelte/src/compiler/phases/1-parse/state/element.js
Original file line number Diff line number Diff line change
Expand Up @@ -123,8 +123,11 @@ export default function element(parser) {
}

if (!regex_valid_element_name.test(name) && !regex_valid_component_name.test(name)) {
const bounds = { start: start + 1, end: start + 1 + name.length };
e.tag_invalid_name(bounds);
// <div. -> in the middle of typing -> allow in loose mode
if (!parser.loose || !name.endsWith('.')) {
const bounds = { start: start + 1, end: start + 1 + name.length };
e.tag_invalid_name(bounds);
}
}

if (root_only_meta_tags.has(name)) {
Expand All @@ -141,7 +144,7 @@ export default function element(parser) {

const type = meta_tags.has(name)
? meta_tags.get(name)
: regex_valid_component_name.test(name)
: regex_valid_component_name.test(name) || (parser.loose && name.endsWith('.'))
? 'Component'
: name === 'title' && parent_is_head(parser.stack)
? 'TitleElement'
Expand Down
55 changes: 52 additions & 3 deletions packages/svelte/src/compiler/phases/1-parse/state/tag.js
Original file line number Diff line number Diff line change
Expand Up @@ -174,13 +174,30 @@ function open(parser) {
if (parser.eat('(')) {
parser.allow_whitespace();

key = read_expression(parser);
key = read_expression(parser, '(');
parser.allow_whitespace();
parser.eat(')', true);
parser.allow_whitespace();
}

parser.eat('}', true);
const matches = parser.eat('}', true, false);

if (!matches) {
// Parser may have read the `as` as part of the expression (e.g. in `{#each foo. as x}`)
if (parser.template.slice(parser.index - 4, parser.index) === ' as ') {
const prev_index = parser.index;
context = read_pattern(parser);
parser.eat('}', true);
expression = {
type: 'Identifier',
name: '',
start: expression.start,
end: prev_index - 4
};
} else {
parser.eat('}', true); // rerun to produce the parser error
}
}

/** @type {AST.EachBlock} */
const block = parser.append({
Expand Down Expand Up @@ -246,7 +263,39 @@ function open(parser) {
parser.fragments.push(block.pending);
}

parser.eat('}', true);
const matches = parser.eat('}', true, false);

// Parser may have read the `then/catch` as part of the expression (e.g. in `{#await foo. then x}`)
if (!matches) {
if (parser.template.slice(parser.index - 6, parser.index) === ' then ') {
const prev_index = parser.index;
block.value = read_pattern(parser);
parser.eat('}', true);
block.expression = {
type: 'Identifier',
name: '',
start: expression.start,
end: prev_index - 6
};
block.then = block.pending;
block.pending = null;
} else if (parser.template.slice(parser.index - 7, parser.index) === ' catch ') {
const prev_index = parser.index;
block.error = read_pattern(parser);
parser.eat('}', true);
block.expression = {
type: 'Identifier',
name: '',
start: expression.start,
end: prev_index - 7
};
block.catch = block.pending;
block.pending = null;
} else {
parser.eat('}', true); // rerun to produce the parser error
}
}

parser.stack.push(block);

return;
Expand Down
5 changes: 5 additions & 0 deletions packages/svelte/src/compiler/phases/1-parse/utils/bracket.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ const SQUARE_BRACKET_OPEN = '['.charCodeAt(0);
const SQUARE_BRACKET_CLOSE = ']'.charCodeAt(0);
const CURLY_BRACKET_OPEN = '{'.charCodeAt(0);
const CURLY_BRACKET_CLOSE = '}'.charCodeAt(0);
const PARENTHESES_OPEN = '('.charCodeAt(0);
const PARENTHESES_CLOSE = ')'.charCodeAt(0);

/** @param {number} code */
export function is_bracket_open(code) {
Expand Down Expand Up @@ -34,6 +36,9 @@ export function get_bracket_close(open) {
if (open === CURLY_BRACKET_OPEN) {
return CURLY_BRACKET_CLOSE;
}
if (open === PARENTHESES_OPEN) {
return PARENTHESES_CLOSE;
}
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,15 @@

asd{a.}asd
{foo[bar.]}

{#if x.}{/if}

{#each array as item (item.)}{/each}

{#each obj. as item}{/each}

{#await x.}{/await}

{#await x. then y}{/await}

{#await x. catch y}{/await}
Loading
Loading