diff --git a/.changeset/violet-apes-punch.md b/.changeset/violet-apes-punch.md new file mode 100644 index 000000000000..77f4e923c8ce --- /dev/null +++ b/.changeset/violet-apes-punch.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: correctly parse leading comments in function binding diff --git a/packages/svelte/src/compiler/phases/1-parse/read/expression.js b/packages/svelte/src/compiler/phases/1-parse/read/expression.js index 82a667d38c59..a596cdf572cb 100644 --- a/packages/svelte/src/compiler/phases/1-parse/read/expression.js +++ b/packages/svelte/src/compiler/phases/1-parse/read/expression.js @@ -38,6 +38,10 @@ export default function read_expression(parser, opening_token, disallow_loose) { let num_parens = 0; + if (node.leadingComments !== undefined && node.leadingComments.length > 0) { + parser.index = node.leadingComments.at(-1).end; + } + for (let i = parser.index; i < /** @type {number} */ (node.start); i += 1) { if (parser.template[i] === '(') num_parens += 1; } diff --git a/packages/svelte/src/compiler/phases/2-analyze/visitors/BindDirective.js b/packages/svelte/src/compiler/phases/2-analyze/visitors/BindDirective.js index b4de1925df24..7719eee6772e 100644 --- a/packages/svelte/src/compiler/phases/2-analyze/visitors/BindDirective.js +++ b/packages/svelte/src/compiler/phases/2-analyze/visitors/BindDirective.js @@ -132,8 +132,19 @@ export function BindDirective(node, context) { } let i = /** @type {number} */ (node.expression.start); + let leading_comments_start = /**@type {any}*/ (node.expression.leadingComments?.at(0))?.start; + let leading_comments_end = /**@type {any}*/ (node.expression.leadingComments?.at(-1))?.end; while (context.state.analysis.source[--i] !== '{') { - if (context.state.analysis.source[i] === '(') { + if ( + context.state.analysis.source[i] === '(' && + // if the parenthesis is in a leading comment we don't need to throw the error + !( + leading_comments_start && + leading_comments_end && + i <= leading_comments_end && + i >= leading_comments_start + ) + ) { e.bind_invalid_parens(node, node.name); } } diff --git a/packages/svelte/tests/parser-modern/samples/comment-before-function-binding/input.svelte b/packages/svelte/tests/parser-modern/samples/comment-before-function-binding/input.svelte new file mode 100644 index 000000000000..50200a9eac5e --- /dev/null +++ b/packages/svelte/tests/parser-modern/samples/comment-before-function-binding/input.svelte @@ -0,0 +1,10 @@ + + + value, + (v) => value = v.toLowerCase() +} +/> diff --git a/packages/svelte/tests/parser-modern/samples/comment-before-function-binding/output.json b/packages/svelte/tests/parser-modern/samples/comment-before-function-binding/output.json new file mode 100644 index 000000000000..7189e27e964d --- /dev/null +++ b/packages/svelte/tests/parser-modern/samples/comment-before-function-binding/output.json @@ -0,0 +1,326 @@ +{ + "css": null, + "js": [], + "start": 37, + "end": 117, + "type": "Root", + "fragment": { + "type": "Fragment", + "nodes": [ + { + "type": "Text", + "start": 35, + "end": 37, + "raw": "\n\n", + "data": "\n\n" + }, + { + "type": "RegularElement", + "start": 37, + "end": 117, + "name": "input", + "attributes": [ + { + "start": 44, + "end": 114, + "type": "BindDirective", + "name": "value", + "expression": { + "type": "SequenceExpression", + "start": 68, + "end": 112, + "loc": { + "start": { + "line": 7, + "column": 1 + }, + "end": { + "line": 8, + "column": 31 + } + }, + "expressions": [ + { + "type": "ArrowFunctionExpression", + "start": 68, + "end": 79, + "loc": { + "start": { + "line": 7, + "column": 1 + }, + "end": { + "line": 7, + "column": 12 + } + }, + "id": null, + "expression": true, + "generator": false, + "async": false, + "params": [], + "body": { + "type": "Identifier", + "start": 74, + "end": 79, + "loc": { + "start": { + "line": 7, + "column": 7 + }, + "end": { + "line": 7, + "column": 12 + } + }, + "name": "value" + } + }, + { + "type": "ArrowFunctionExpression", + "start": 82, + "end": 112, + "loc": { + "start": { + "line": 8, + "column": 1 + }, + "end": { + "line": 8, + "column": 31 + } + }, + "id": null, + "expression": true, + "generator": false, + "async": false, + "params": [ + { + "type": "Identifier", + "start": 83, + "end": 84, + "loc": { + "start": { + "line": 8, + "column": 2 + }, + "end": { + "line": 8, + "column": 3 + } + }, + "name": "v" + } + ], + "body": { + "type": "AssignmentExpression", + "start": 89, + "end": 112, + "loc": { + "start": { + "line": 8, + "column": 8 + }, + "end": { + "line": 8, + "column": 31 + } + }, + "operator": "=", + "left": { + "type": "Identifier", + "start": 89, + "end": 94, + "loc": { + "start": { + "line": 8, + "column": 8 + }, + "end": { + "line": 8, + "column": 13 + } + }, + "name": "value" + }, + "right": { + "type": "CallExpression", + "start": 97, + "end": 112, + "loc": { + "start": { + "line": 8, + "column": 16 + }, + "end": { + "line": 8, + "column": 31 + } + }, + "callee": { + "type": "MemberExpression", + "start": 97, + "end": 110, + "loc": { + "start": { + "line": 8, + "column": 16 + }, + "end": { + "line": 8, + "column": 29 + } + }, + "object": { + "type": "Identifier", + "start": 97, + "end": 98, + "loc": { + "start": { + "line": 8, + "column": 16 + }, + "end": { + "line": 8, + "column": 17 + } + }, + "name": "v" + }, + "property": { + "type": "Identifier", + "start": 99, + "end": 110, + "loc": { + "start": { + "line": 8, + "column": 18 + }, + "end": { + "line": 8, + "column": 29 + } + }, + "name": "toLowerCase" + }, + "computed": false, + "optional": false + }, + "arguments": [], + "optional": false + } + } + } + ], + "leadingComments": [ + { + "type": "Block", + "value": "* ( ", + "start": 58, + "end": 66 + } + ] + }, + "modifiers": [] + } + ], + "fragment": { + "type": "Fragment", + "nodes": [] + } + } + ] + }, + "options": null, + "instance": { + "type": "Script", + "start": 0, + "end": 35, + "context": "default", + "content": { + "type": "Program", + "start": 8, + "end": 26, + "loc": { + "start": { + "line": 1, + "column": 0 + }, + "end": { + "line": 3, + "column": 0 + } + }, + "body": [ + { + "type": "VariableDeclaration", + "start": 10, + "end": 25, + "loc": { + "start": { + "line": 2, + "column": 1 + }, + "end": { + "line": 2, + "column": 16 + } + }, + "declarations": [ + { + "type": "VariableDeclarator", + "start": 14, + "end": 24, + "loc": { + "start": { + "line": 2, + "column": 5 + }, + "end": { + "line": 2, + "column": 15 + } + }, + "id": { + "type": "Identifier", + "start": 14, + "end": 19, + "loc": { + "start": { + "line": 2, + "column": 5 + }, + "end": { + "line": 2, + "column": 10 + } + }, + "name": "value" + }, + "init": { + "type": "Literal", + "start": 22, + "end": 24, + "loc": { + "start": { + "line": 2, + "column": 13 + }, + "end": { + "line": 2, + "column": 15 + } + }, + "value": "", + "raw": "''" + } + } + ], + "kind": "let" + } + ], + "sourceType": "module" + }, + "attributes": [] + } +} \ No newline at end of file diff --git a/packages/svelte/tests/validator/samples/comment-before-function-binding/errors.json b/packages/svelte/tests/validator/samples/comment-before-function-binding/errors.json new file mode 100644 index 000000000000..fe51488c7066 --- /dev/null +++ b/packages/svelte/tests/validator/samples/comment-before-function-binding/errors.json @@ -0,0 +1 @@ +[] diff --git a/packages/svelte/tests/validator/samples/comment-before-function-binding/input.svelte b/packages/svelte/tests/validator/samples/comment-before-function-binding/input.svelte new file mode 100644 index 000000000000..50200a9eac5e --- /dev/null +++ b/packages/svelte/tests/validator/samples/comment-before-function-binding/input.svelte @@ -0,0 +1,10 @@ + + + value, + (v) => value = v.toLowerCase() +} +/>