From 87b322bbe24378af3d4e2faae2d6ec4e44ad3494 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Wed, 15 Nov 2023 22:44:53 -0500 Subject: [PATCH] various --- .../src/compiler/phases/1-parse/acorn.js | 2 + .../src/compiler/phases/1-parse/state/tag.js | 13 + .../compiler/phases/1-parse/strip-types.js | 39 +- .../samples/snippets/input.svelte | 6 +- .../samples/snippets/output.json | 91 +++- .../typescript-in-event-handler/input.svelte | 10 + .../typescript-in-event-handler/output.json | 482 ++++++++++++++++++ .../samples/snippet-argument/main.svelte | 2 +- 8 files changed, 590 insertions(+), 55 deletions(-) create mode 100644 packages/svelte/tests/parser-modern/samples/typescript-in-event-handler/input.svelte create mode 100644 packages/svelte/tests/parser-modern/samples/typescript-in-event-handler/output.json diff --git a/packages/svelte/src/compiler/phases/1-parse/acorn.js b/packages/svelte/src/compiler/phases/1-parse/acorn.js index 6d202d85a4..b947eff2d3 100644 --- a/packages/svelte/src/compiler/phases/1-parse/acorn.js +++ b/packages/svelte/src/compiler/phases/1-parse/acorn.js @@ -13,6 +13,7 @@ export function parse(source) { const { onComment, add_comments } = get_comment_handlers(source); const ast = /** @type {import('estree').Program} */ ( strip_types( + source, // @ts-expect-error ParserWithTS.parse(source, { onComment, @@ -37,6 +38,7 @@ export function parse_expression_at(source, index) { const { onComment, add_comments } = get_comment_handlers(source); const ast = /** @type {import('estree').Expression} */ ( strip_types( + source, // @ts-expect-error ParserWithTS.parseExpressionAt(source, index, { onComment, diff --git a/packages/svelte/src/compiler/phases/1-parse/state/tag.js b/packages/svelte/src/compiler/phases/1-parse/state/tag.js index 6f9f9663a9..a7580f8696 100644 --- a/packages/svelte/src/compiler/phases/1-parse/state/tag.js +++ b/packages/svelte/src/compiler/phases/1-parse/state/tag.js @@ -2,6 +2,7 @@ import read_context from '../read/context.js'; import read_expression from '../read/expression.js'; import { error } from '../../../errors.js'; import { create_fragment } from '../utils/create.js'; +import { parse_expression_at } from '../acorn.js'; const regex_whitespace_with_closing_curly_brace = /^\s*}/; @@ -261,6 +262,18 @@ function open(parser) { const context = parser.match(')') ? null : read_context(parser); + parser.allow_whitespace(); + if (context && parser.eat(':')) { + // we need to trick Acorn into parsing the type annotation + const insert = '_ as '; + let a = parser.index - insert.length; + const template = ' '.repeat(a) + insert + parser.template.slice(parser.index); + const expression = parse_expression_at(template, a); + + context.typeAnnotation = /** @type {any} */ (expression).typeAnnotation; + parser.index = /** @type {number} */ (expression.end); + } + parser.allow_whitespace(); parser.eat(')', true); diff --git a/packages/svelte/src/compiler/phases/1-parse/strip-types.js b/packages/svelte/src/compiler/phases/1-parse/strip-types.js index 2c4e426b31..67f2c027a5 100644 --- a/packages/svelte/src/compiler/phases/1-parse/strip-types.js +++ b/packages/svelte/src/compiler/phases/1-parse/strip-types.js @@ -2,45 +2,24 @@ import { walk } from 'zimmerframe'; import * as b from '../../utils/builders.js'; /** + * @param {string} source * @param {import('estree').Node} node */ -export function strip_types(node) { +export function strip_types(source, node) { return walk(node, null, { _(node, context) { - // @ts-expect-error - if (node.exportKind === 'type' || node.importKind === 'type') { - console.log('>>>', node); - return b.empty; - } - // @ts-expect-error delete node.loc.start.index; // @ts-expect-error delete node.loc.end.index; - // // @ts-expect-error - // delete node.returnType; - // // @ts-expect-error - // delete node.importKind; - // // @ts-expect-error - // delete node.exportKind; - // // @ts-expect-error - // delete node.typeAnnotation; - // // @ts-expect-error - // delete node.typeParameters; - // if (node.type.startsWith('TS')) { - // const ts_node = /** @type {any} */ (node); - - // switch (ts_node.type) { - // case 'TSAsExpression': - // case 'TSNonNullExpression': - // // hack to make sure parser skips over the type assertion - // ts_node.expression.end = ts_node.end; - // return context.visit(ts_node.expression); - // default: - // return b.empty; - // } - // } + 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 + let end = /** @type {any} */ (node).typeAnnotation.start; + while (/\s/.test(source[end - 1])) end -= 1; + node.end = end; + } context.next(); } diff --git a/packages/svelte/tests/parser-modern/samples/snippets/input.svelte b/packages/svelte/tests/parser-modern/samples/snippets/input.svelte index 7c2a94b7eb..e509a3a5f8 100644 --- a/packages/svelte/tests/parser-modern/samples/snippets/input.svelte +++ b/packages/svelte/tests/parser-modern/samples/snippets/input.svelte @@ -1,5 +1,5 @@ -{#snippet foo()} -

hello

+{#snippet foo(msg: string)} +

{msg}

{/snippet} -{@render foo()} +{@render foo(msg)} diff --git a/packages/svelte/tests/parser-modern/samples/snippets/output.json b/packages/svelte/tests/parser-modern/samples/snippets/output.json index 7093d8a860..d9896d9264 100644 --- a/packages/svelte/tests/parser-modern/samples/snippets/output.json +++ b/packages/svelte/tests/parser-modern/samples/snippets/output.json @@ -2,7 +2,7 @@ "css": null, "js": [], "start": 0, - "end": 58, + "end": 72, "type": "Root", "fragment": { "type": "Fragment", @@ -10,39 +10,73 @@ { "type": "SnippetBlock", "start": 0, - "end": 41, + "end": 52, "expression": { "type": "Identifier", "start": 10, "end": 13, "name": "foo" }, - "context": null, + "context": { + "type": "Identifier", + "name": "msg", + "start": 14, + "end": 17, + "typeAnnotation": { + "type": "TSStringKeyword", + "start": 19, + "end": 25, + "loc": { + "start": { + "line": 1, + "column": 19 + }, + "end": { + "line": 1, + "column": 25 + } + } + } + }, "body": { "type": "Fragment", "nodes": [ { "type": "Text", - "start": 16, - "end": 18, + "start": 27, + "end": 29, "raw": "\n\t", "data": "\n\t" }, { "type": "RegularElement", - "start": 18, - "end": 30, + "start": 29, + "end": 41, "name": "p", "attributes": [], "fragment": { "type": "Fragment", "nodes": [ { - "type": "Text", - "start": 21, - "end": 26, - "raw": "hello", - "data": "hello" + "type": "ExpressionTag", + "start": 32, + "end": 37, + "expression": { + "type": "Identifier", + "start": 33, + "end": 36, + "loc": { + "start": { + "line": 2, + "column": 5 + }, + "end": { + "line": 2, + "column": 8 + } + }, + "name": "msg" + } } ], "transparent": true @@ -50,8 +84,8 @@ }, { "type": "Text", - "start": 30, - "end": 31, + "start": 41, + "end": 42, "raw": "\n", "data": "\n" } @@ -61,19 +95,19 @@ }, { "type": "Text", - "start": 41, - "end": 43, + "start": 52, + "end": 54, "raw": "\n\n", "data": "\n\n" }, { "type": "RenderTag", - "start": 43, - "end": 58, + "start": 54, + "end": 72, "expression": { "type": "Identifier", - "start": 52, - "end": 55, + "start": 63, + "end": 66, "loc": { "start": { "line": 5, @@ -86,7 +120,22 @@ }, "name": "foo" }, - "argument": null + "argument": { + "type": "Identifier", + "start": 67, + "end": 70, + "loc": { + "start": { + "line": 5, + "column": 13 + }, + "end": { + "line": 5, + "column": 16 + } + }, + "name": "msg" + } } ], "transparent": false diff --git a/packages/svelte/tests/parser-modern/samples/typescript-in-event-handler/input.svelte b/packages/svelte/tests/parser-modern/samples/typescript-in-event-handler/input.svelte new file mode 100644 index 0000000000..c72a3f9db1 --- /dev/null +++ b/packages/svelte/tests/parser-modern/samples/typescript-in-event-handler/input.svelte @@ -0,0 +1,10 @@ + + + diff --git a/packages/svelte/tests/parser-modern/samples/typescript-in-event-handler/output.json b/packages/svelte/tests/parser-modern/samples/typescript-in-event-handler/output.json new file mode 100644 index 0000000000..db944847f1 --- /dev/null +++ b/packages/svelte/tests/parser-modern/samples/typescript-in-event-handler/output.json @@ -0,0 +1,482 @@ +{ + "css": null, + "js": [], + "start": 44, + "end": 163, + "type": "Root", + "fragment": { + "type": "Fragment", + "nodes": [ + { + "type": "Text", + "start": 42, + "end": 44, + "raw": "\n\n", + "data": "\n\n" + }, + { + "type": "RegularElement", + "start": 44, + "end": 163, + "name": "button", + "attributes": [ + { + "start": 53, + "end": 137, + "type": "OnDirective", + "name": "click", + "modifiers": [], + "expression": { + "type": "ArrowFunctionExpression", + "start": 63, + "end": 136, + "loc": { + "start": { + "line": 6, + "column": 11 + }, + "end": { + "line": 9, + "column": 2 + } + }, + "id": null, + "expression": false, + "generator": false, + "async": false, + "params": [ + { + "type": "Identifier", + "start": 64, + "end": 65, + "loc": { + "start": { + "line": 6, + "column": 12 + }, + "end": 77 + }, + "name": "e", + "typeAnnotation": { + "type": "TSTypeAnnotation", + "start": 65, + "end": 77, + "loc": { + "start": { + "line": 6, + "column": 13 + }, + "end": { + "line": 6, + "column": 25 + } + }, + "typeAnnotation": { + "type": "TSTypeReference", + "start": 67, + "end": 77, + "loc": { + "start": { + "line": 6, + "column": 15 + }, + "end": { + "line": 6, + "column": 25 + } + }, + "typeName": { + "type": "Identifier", + "start": 67, + "end": 77, + "loc": { + "start": { + "line": 6, + "column": 15 + }, + "end": { + "line": 6, + "column": 25 + } + }, + "name": "MouseEvent" + } + } + } + } + ], + "body": { + "type": "BlockStatement", + "start": 82, + "end": 136, + "loc": { + "start": { + "line": 6, + "column": 30 + }, + "end": { + "line": 9, + "column": 2 + } + }, + "body": [ + { + "type": "VariableDeclaration", + "start": 86, + "end": 117, + "loc": { + "start": { + "line": 7, + "column": 2 + }, + "end": { + "line": 7, + "column": 33 + } + }, + "declarations": [ + { + "type": "VariableDeclarator", + "start": 92, + "end": 116, + "loc": { + "start": { + "line": 7, + "column": 8 + }, + "end": { + "line": 7, + "column": 32 + } + }, + "id": { + "type": "Identifier", + "start": 92, + "end": 20, + "loc": { + "start": { + "line": 7, + "column": 8 + }, + "end": { + "line": 7, + "column": 20 + } + }, + "name": "next", + "typeAnnotation": { + "type": "TSTypeAnnotation", + "start": 96, + "end": 104, + "loc": { + "start": { + "line": 7, + "column": 12 + }, + "end": { + "line": 7, + "column": 20 + } + }, + "typeAnnotation": { + "type": "TSNumberKeyword", + "start": 98, + "end": 104, + "loc": { + "start": { + "line": 7, + "column": 14 + }, + "end": { + "line": 7, + "column": 20 + } + } + } + } + }, + "init": { + "type": "BinaryExpression", + "start": 107, + "end": 116, + "loc": { + "start": { + "line": 7, + "column": 23 + }, + "end": { + "line": 7, + "column": 32 + } + }, + "left": { + "type": "Identifier", + "start": 107, + "end": 112, + "loc": { + "start": { + "line": 7, + "column": 23 + }, + "end": { + "line": 7, + "column": 28 + } + }, + "name": "count" + }, + "operator": "+", + "right": { + "type": "Literal", + "start": 115, + "end": 116, + "loc": { + "start": { + "line": 7, + "column": 31 + }, + "end": { + "line": 7, + "column": 32 + } + }, + "value": 1, + "raw": "1" + } + } + } + ], + "kind": "const" + }, + { + "type": "ExpressionStatement", + "start": 120, + "end": 133, + "loc": { + "start": { + "line": 8, + "column": 2 + }, + "end": { + "line": 8, + "column": 15 + } + }, + "expression": { + "type": "AssignmentExpression", + "start": 120, + "end": 132, + "loc": { + "start": { + "line": 8, + "column": 2 + }, + "end": { + "line": 8, + "column": 14 + } + }, + "operator": "=", + "left": { + "type": "Identifier", + "start": 120, + "end": 125, + "loc": { + "start": { + "line": 8, + "column": 2 + }, + "end": { + "line": 8, + "column": 7 + } + }, + "name": "count" + }, + "right": { + "type": "Identifier", + "start": 128, + "end": 132, + "loc": { + "start": { + "line": 8, + "column": 10 + }, + "end": { + "line": 8, + "column": 14 + } + }, + "name": "next" + } + } + } + ] + } + } + } + ], + "fragment": { + "type": "Fragment", + "nodes": [ + { + "type": "Text", + "start": 139, + "end": 147, + "raw": "clicks: ", + "data": "clicks: " + }, + { + "type": "ExpressionTag", + "start": 147, + "end": 154, + "expression": { + "type": "Identifier", + "start": 148, + "end": 153, + "loc": { + "start": { + "line": 10, + "column": 10 + }, + "end": { + "line": 10, + "column": 15 + } + }, + "name": "count" + } + } + ], + "transparent": true + } + } + ], + "transparent": false + }, + "options": null, + "instance": { + "type": "Script", + "start": 0, + "end": 42, + "context": "default", + "content": { + "type": "Program", + "start": 8, + "end": 33, + "loc": { + "start": { + "line": 1, + "column": 0 + }, + "end": { + "line": 3, + "column": 0 + } + }, + "body": [ + { + "type": "VariableDeclaration", + "start": 10, + "end": 32, + "loc": { + "start": { + "line": 2, + "column": 1 + }, + "end": { + "line": 2, + "column": 23 + } + }, + "declarations": [ + { + "type": "VariableDeclarator", + "start": 14, + "end": 31, + "loc": { + "start": { + "line": 2, + "column": 5 + }, + "end": { + "line": 2, + "column": 22 + } + }, + "id": { + "type": "Identifier", + "start": 14, + "end": 19, + "loc": { + "start": { + "line": 2, + "column": 5 + }, + "end": { + "line": 2, + "column": 10 + } + }, + "name": "count" + }, + "init": { + "type": "CallExpression", + "start": 22, + "end": 31, + "loc": { + "start": { + "line": 2, + "column": 13 + }, + "end": { + "line": 2, + "column": 22 + } + }, + "callee": { + "type": "Identifier", + "start": 22, + "end": 28, + "loc": { + "start": { + "line": 2, + "column": 13 + }, + "end": { + "line": 2, + "column": 19 + } + }, + "name": "$state" + }, + "arguments": [ + { + "type": "Literal", + "start": 29, + "end": 30, + "loc": { + "start": { + "line": 2, + "column": 20 + }, + "end": { + "line": 2, + "column": 21 + } + }, + "value": 0, + "raw": "0" + } + ], + "optional": false + } + } + ], + "kind": "let" + } + ], + "sourceType": "module" + } + } +} \ No newline at end of file diff --git a/packages/svelte/tests/runtime-runes/samples/snippet-argument/main.svelte b/packages/svelte/tests/runtime-runes/samples/snippet-argument/main.svelte index 9337d7729b..a9af9578b5 100644 --- a/packages/svelte/tests/runtime-runes/samples/snippet-argument/main.svelte +++ b/packages/svelte/tests/runtime-runes/samples/snippet-argument/main.svelte @@ -2,7 +2,7 @@ let count = $state(0); -{#snippet foo(n)} +{#snippet foo(n: number)}

clicks: {n}

{/snippet}