fix: allow multiple optional parameters with defaults (#12070)

* fix: allow multiple optional parameters with defaults

* Apply suggestions from code review

* partial fix

* feat: parse as a whole function

* couple of fixes

* work around acorn-typescript quirks

* add the harder test

* Update .changeset/ten-geese-share.md

---------

Co-authored-by: Rich Harris <hello@rich-harris.dev>
Co-authored-by: Rich Harris <rich.harris@vercel.com>
pull/12132/head
Paolo Ricciuti 3 months ago committed by GitHub
parent 5581216e4d
commit 965c12f633
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

@ -0,0 +1,5 @@
---
"svelte": patch
---
fix: allow multiple optional parameters with defaults in snippets

@ -1,6 +1,7 @@
import * as acorn from 'acorn';
import { walk } from 'zimmerframe';
import { tsPlugin } from 'acorn-typescript';
import { locator } from '../../state.js';
const ParserWithTS = acorn.Parser.extend(tsPlugin({ allowSatisfies: true }));
@ -127,6 +128,16 @@ function amend(source, node) {
// @ts-expect-error
delete node.loc.end.index;
if (typeof node.loc?.end === 'number') {
const loc = locator(node.loc.end);
if (loc) {
node.loc.end = {
line: loc.line,
column: loc.column
};
}
}
if (/** @type {any} */ (node).typeAnnotation && node.end === undefined) {
// i think there might be a bug in acorn-typescript that prevents
// `end` from being assigned when there's a type annotation

@ -3,6 +3,7 @@ import read_expression from '../read/expression.js';
import * as e from '../../../errors.js';
import { create_fragment } from '../utils/create.js';
import { walk } from 'zimmerframe';
import { parse_expression_at } from '../acorn.js';
const regex_whitespace_with_closing_curly_brace = /^\s*}/;
@ -268,39 +269,28 @@ function open(parser) {
e.expected_identifier(parser.index);
}
parser.eat('(', true);
parser.allow_whitespace();
/** @type {import('estree').Pattern[]} */
const parameters = [];
let slice_end = parser.index;
while (!parser.match(')')) {
let pattern = read_pattern(parser, true);
parser.allow_whitespace();
if (parser.eat('=')) {
parser.allow_whitespace();
const right = read_expression(parser);
pattern = {
type: 'AssignmentPattern',
left: pattern,
right: right,
start: pattern.start,
end: right.end
};
}
parser.eat('(', true);
parameters.push(pattern);
let parentheses = 1;
let params = '';
if (!parser.eat(',')) break;
parser.allow_whitespace();
while (!parser.match(')') || parentheses !== 1) {
if (parser.match('(')) parentheses++;
if (parser.match(')')) parentheses--;
params += parser.read(/^./);
}
parser.eat(')', true);
let function_expression = /** @type {import('estree').ArrowFunctionExpression} */ (
parse_expression_at(
parser.template.slice(0, slice_end).replace(/\S/g, ' ') + `(${params}) => {}`,
parser.ts,
0
)
);
parser.allow_whitespace();
parser.eat('}', true);
parser.index += 2;
/** @type {ReturnType<typeof parser.append<import('#compiler').SnippetBlock>>} */
const block = parser.append({
@ -313,10 +303,9 @@ function open(parser) {
end: name_end,
name
},
parameters,
parameters: function_expression.params,
body: create_fragment()
});
parser.stack.push(block);
parser.fragments.push(block.body);

@ -32,20 +32,28 @@
"loc": {
"start": {
"line": 3,
"column": 14,
"character": 43
"column": 14
},
"end": {
"line": 3,
"column": 25,
"character": 54
"column": 25
}
},
"end": 54,
"end": 46,
"typeAnnotation": {
"type": "TSTypeAnnotation",
"start": 46,
"end": 54,
"loc": {
"start": {
"line": 3,
"column": 17
},
"end": {
"line": 3,
"column": 25
}
},
"typeAnnotation": {
"type": "TSStringKeyword",
"start": 48,

@ -54,7 +54,10 @@
"line": 6,
"column": 12
},
"end": 87
"end": {
"line": 6,
"column": 25
}
},
"name": "e",
"typeAnnotation": {

@ -0,0 +1,5 @@
import { test } from '../../test';
export default test({
html: '013/023'
});

@ -0,0 +1,9 @@
{#snippet one(a, b = 1, c = (2, 3))}
{a}{b}{c}
{/snippet}
{#snippet two(a, b = (1, 2), c = 3)}
{a}{b}{c}
{/snippet}
{@render one(0)}/{@render two(0)}
Loading…
Cancel
Save