From e93f2c7ec475b7639501a2aa4b3b0ba78acde127 Mon Sep 17 00:00:00 2001 From: Anas Chakroun Date: Thu, 30 Oct 2025 05:14:24 +0100 Subject: [PATCH 1/2] fix: handle decorators after type declarations --- src/index.ts | 6 +- .../expected.json | 409 ++++++++++++++++++ .../decorators_type_before_decorator/input.ts | 9 + 3 files changed, 423 insertions(+), 1 deletion(-) create mode 100644 test/decorators_type_before_decorator/expected.json create mode 100644 test/decorators_type_before_decorator/input.ts diff --git a/src/index.ts b/src/index.ts index 53c4f17..37b7201 100644 --- a/src/index.ts +++ b/src/index.ts @@ -256,10 +256,14 @@ export function tsPlugin(options?: { if (code === 60) { return this.finishOp(tt.relational, 1); } + // Handle @ for decorators - can appear after type declarations + if (code === 64) { + ++this.pos; + return this.finishToken(tokTypes.at); + } return super.getTokenFromCode(code); } - readToken(code: number): any { if (!this.inType) { let context = this.curContext(); diff --git a/test/decorators_type_before_decorator/expected.json b/test/decorators_type_before_decorator/expected.json new file mode 100644 index 0000000..a68f77a --- /dev/null +++ b/test/decorators_type_before_decorator/expected.json @@ -0,0 +1,409 @@ +{ + "type": "Program", + "start": 0, + "end": 122, + "loc": { + "start": { + "line": 1, + "column": 0 + }, + "end": { + "line": 10, + "column": 0 + } + }, + "body": [ + { + "type": "TSInterfaceDeclaration", + "start": 0, + "end": 48, + "loc": { + "start": { + "line": 1, + "column": 0 + }, + "end": { + "line": 4, + "column": 1 + } + }, + "id": { + "type": "Identifier", + "start": 10, + "end": 14, + "loc": { + "start": { + "line": 1, + "column": 10 + }, + "end": { + "line": 1, + "column": 14 + } + }, + "name": "User" + }, + "body": { + "type": "TSInterfaceBody", + "start": 15, + "end": 48, + "loc": { + "start": { + "line": 1, + "column": 15 + }, + "end": { + "line": 4, + "column": 1 + } + }, + "body": [ + { + "type": "TSPropertySignature", + "start": 19, + "end": 30, + "loc": { + "start": { + "line": 2, + "column": 2 + }, + "end": { + "line": 2, + "column": 13 + } + }, + "computed": false, + "key": { + "type": "Identifier", + "start": 19, + "end": 21, + "loc": { + "start": { + "line": 2, + "column": 2 + }, + "end": { + "line": 2, + "column": 4 + } + }, + "name": "id" + }, + "typeAnnotation": { + "type": "TSTypeAnnotation", + "start": 21, + "end": 29, + "loc": { + "start": { + "line": 2, + "column": 4 + }, + "end": { + "line": 2, + "column": 12 + } + }, + "typeAnnotation": { + "type": "TSNumberKeyword", + "start": 23, + "end": 29, + "loc": { + "start": { + "line": 2, + "column": 6 + }, + "end": { + "line": 2, + "column": 12 + } + } + } + } + }, + { + "type": "TSPropertySignature", + "start": 33, + "end": 46, + "loc": { + "start": { + "line": 3, + "column": 2 + }, + "end": { + "line": 3, + "column": 15 + } + }, + "computed": false, + "key": { + "type": "Identifier", + "start": 33, + "end": 37, + "loc": { + "start": { + "line": 3, + "column": 2 + }, + "end": { + "line": 3, + "column": 6 + } + }, + "name": "name" + }, + "typeAnnotation": { + "type": "TSTypeAnnotation", + "start": 37, + "end": 45, + "loc": { + "start": { + "line": 3, + "column": 6 + }, + "end": { + "line": 3, + "column": 14 + } + }, + "typeAnnotation": { + "type": "TSStringKeyword", + "start": 39, + "end": 45, + "loc": { + "start": { + "line": 3, + "column": 8 + }, + "end": { + "line": 3, + "column": 14 + } + } + } + } + } + ] + } + }, + { + "type": "ExportNamedDeclaration", + "start": 64, + "end": 121, + "loc": { + "start": { + "line": 7, + "column": 0 + }, + "end": { + "line": 9, + "column": 1 + } + }, + "exportKind": "value", + "declaration": { + "type": "ClassDeclaration", + "start": 50, + "end": 121, + "loc": { + "start": { + "line": 6, + "column": 0 + }, + "end": { + "line": 9, + "column": 1 + } + }, + "decorators": [ + { + "type": "Decorator", + "start": 50, + "end": 63, + "loc": { + "start": { + "line": 6, + "column": 0 + }, + "end": { + "line": 6, + "column": 13 + } + }, + "expression": { + "type": "CallExpression", + "start": 51, + "end": 63, + "loc": { + "start": { + "line": 6, + "column": 1 + }, + "end": { + "line": 6, + "column": 13 + } + }, + "callee": { + "type": "Identifier", + "start": 51, + "end": 61, + "loc": { + "start": { + "line": 6, + "column": 1 + }, + "end": { + "line": 6, + "column": 11 + } + }, + "name": "Injectable" + }, + "arguments": [] + } + } + ], + "id": { + "type": "Identifier", + "start": 77, + "end": 88, + "loc": { + "start": { + "line": 7, + "column": 13 + }, + "end": { + "line": 7, + "column": 24 + } + }, + "name": "UserService" + }, + "superClass": null, + "body": { + "type": "ClassBody", + "start": 89, + "end": 121, + "loc": { + "start": { + "line": 7, + "column": 25 + }, + "end": { + "line": 9, + "column": 1 + } + }, + "body": [ + { + "type": "MethodDefinition", + "start": 93, + "end": 119, + "loc": { + "start": { + "line": 8, + "column": 2 + }, + "end": { + "line": 8, + "column": 28 + } + }, + "static": false, + "computed": false, + "key": { + "type": "Identifier", + "start": 93, + "end": 100, + "loc": { + "start": { + "line": 8, + "column": 2 + }, + "end": { + "line": 8, + "column": 9 + } + }, + "name": "getUser" + }, + "kind": "method", + "value": { + "type": "FunctionExpression", + "start": 100, + "end": 119, + "loc": { + "start": { + "line": 8, + "column": 9 + }, + "end": { + "line": 8, + "column": 28 + } + }, + "id": null, + "expression": false, + "generator": false, + "async": false, + "params": [], + "body": { + "type": "BlockStatement", + "start": 103, + "end": 119, + "loc": { + "start": { + "line": 8, + "column": 12 + }, + "end": { + "line": 8, + "column": 28 + } + }, + "body": [ + { + "type": "ReturnStatement", + "start": 105, + "end": 117, + "loc": { + "start": { + "line": 8, + "column": 14 + }, + "end": { + "line": 8, + "column": 26 + } + }, + "argument": { + "type": "Literal", + "start": 112, + "end": 116, + "loc": { + "start": { + "line": 8, + "column": 21 + }, + "end": { + "line": 8, + "column": 25 + } + }, + "value": null, + "raw": "null" + } + } + ] + } + } + } + ] + } + }, + "specifiers": [], + "source": null + } + ], + "sourceType": "module" +} diff --git a/test/decorators_type_before_decorator/input.ts b/test/decorators_type_before_decorator/input.ts new file mode 100644 index 0000000..d6e3739 --- /dev/null +++ b/test/decorators_type_before_decorator/input.ts @@ -0,0 +1,9 @@ +interface User { + id: number; + name: string; +} + +@Injectable() +export class UserService { + getUser() { return null; } +} From e9ce73d05ed637f53b3bf0d8f67670997a921b48 Mon Sep 17 00:00:00 2001 From: Anas Chakroun Date: Mon, 17 Nov 2025 07:08:26 +0100 Subject: [PATCH 2/2] chore: add changeset for #21 --- .changeset/cyan-ants-fail.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/cyan-ants-fail.md diff --git a/.changeset/cyan-ants-fail.md b/.changeset/cyan-ants-fail.md new file mode 100644 index 0000000..35728bc --- /dev/null +++ b/.changeset/cyan-ants-fail.md @@ -0,0 +1,5 @@ +--- +'@sveltejs/acorn-typescript': patch +--- + +fix: handle decorators after type declarations