From cb05314db863b0aa72005c6c7aa496575939db29 Mon Sep 17 00:00:00 2001 From: Knut Wannheden Date: Fri, 31 Oct 2025 14:50:21 +0100 Subject: [PATCH 1/3] JavaScript: Fix parser `prefix` assignment bug The `prefix` wasn't always getting assigned to the outermost element when parsing variables with modifiers. --- .../rewrite/src/javascript/parser.ts | 11 ++++- .../test/javascript/remove-import.test.ts | 43 +++++++++++++++++++ 2 files changed, 52 insertions(+), 2 deletions(-) diff --git a/rewrite-javascript/rewrite/src/javascript/parser.ts b/rewrite-javascript/rewrite/src/javascript/parser.ts index 1cd27f10a2..1b40855884 100644 --- a/rewrite-javascript/rewrite/src/javascript/parser.ts +++ b/rewrite-javascript/rewrite/src/javascript/parser.ts @@ -2640,8 +2640,15 @@ export class JavaScriptParserVisitor { return produce(this.visitVariableDeclarationList(node.declarationList), draft => { if (node.modifiers) { draft.modifiers = this.mapModifiers(node).concat(draft.modifiers); + draft.prefix = this.prefix(node); + } else { + // When there are no modifiers on the VariableStatement, move the prefix from the first + // declaration list modifier (const/let/var) to the parent J.VariableDeclarations + if (draft.modifiers.length > 0) { + draft.prefix = draft.modifiers[0].prefix; + draft.modifiers[0].prefix = emptySpace; + } } - draft.prefix = this.prefix(node); }); } @@ -3064,7 +3071,7 @@ export class JavaScriptParserVisitor { modifiers.push({ kind: J.Kind.Modifier, id: randomId(), - prefix: this.prefix(kind), + prefix: modifiers.length === 0 ? this.prefix(kind) : this.prefix(kind), markers: emptyMarkers, annotations: [], keyword: kind.kind === ts.SyntaxKind.VarKeyword ? 'var' : diff --git a/rewrite-javascript/rewrite/test/javascript/remove-import.test.ts b/rewrite-javascript/rewrite/test/javascript/remove-import.test.ts index 141893e689..f0271d6ef4 100644 --- a/rewrite-javascript/rewrite/test/javascript/remove-import.test.ts +++ b/rewrite-javascript/rewrite/test/javascript/remove-import.test.ts @@ -959,5 +959,48 @@ describe('RemoveImport visitor', () => { ) ); }); + + test('should remove leading empty lines with variable declaration', async () => { + const spec = new RecipeSpec(); + spec.recipe = fromVisitor(new RemoveImport("fs", "readFile")); + + //language=typescript + await spec.rewriteRun( + typescript( + ` + import {readFile} from "fs"; + + const foo = 1; + console.log(foo); + `, + ` + const foo = 1; + console.log(foo);` + ) + ); + }); + + test('should remove leading empty lines with function declaration', async () => { + const spec = new RecipeSpec(); + spec.recipe = fromVisitor(new RemoveImport("fs", "readFile")); + + //language=typescript + await spec.rewriteRun( + typescript( + ` + import {readFile} from "fs"; + + function foo() { + return 42; + } + `, + ` + function foo() { + return 42; + } + ` + ) + ); + }); }); }); From feca0d9e8a7f403f3f2a0cdd577501a07bbd2a02 Mon Sep 17 00:00:00 2001 From: Knut Wannheden Date: Fri, 31 Oct 2025 15:49:50 +0100 Subject: [PATCH 2/3] Fix test expectations --- .../rewrite/test/javascript/format/format.test.ts | 4 ---- 1 file changed, 4 deletions(-) diff --git a/rewrite-javascript/rewrite/test/javascript/format/format.test.ts b/rewrite-javascript/rewrite/test/javascript/format/format.test.ts index 3c115a2510..a2c19f5855 100644 --- a/rewrite-javascript/rewrite/test/javascript/format/format.test.ts +++ b/rewrite-javascript/rewrite/test/javascript/format/format.test.ts @@ -100,7 +100,6 @@ describe('AutoformatVisitor', () => { if (1 > 0) { console.log("four", "three", "six"); } - let i = 1; while (i < 4) { i++; @@ -112,7 +111,6 @@ describe('AutoformatVisitor', () => { } finally { console.log("finally"); } - const isTypeScriptFun = i > 3 ? "yes" : "hell yeah!"; for (let j = 1; j <= 5; j++) { console.log(\`Number: \` + j); @@ -138,7 +136,6 @@ describe('AutoformatVisitor', () => { )}); test('a statement following an if', () => { - // TODO not sure if there should be a newline after the if return spec.rewriteRun( // @formatter:off //language=typescript @@ -150,7 +147,6 @@ describe('AutoformatVisitor', () => { ` if (1 > 0) { } - let i = 1; `) // @formatter:on From ace02b8f4afec426ef492b53742f5d7a696c1306 Mon Sep 17 00:00:00 2001 From: Knut Wannheden Date: Fri, 31 Oct 2025 16:00:27 +0100 Subject: [PATCH 3/3] Simplify --- .../rewrite/src/javascript/parser.ts | 14 +++----------- 1 file changed, 3 insertions(+), 11 deletions(-) diff --git a/rewrite-javascript/rewrite/src/javascript/parser.ts b/rewrite-javascript/rewrite/src/javascript/parser.ts index 1b40855884..5972eb9378 100644 --- a/rewrite-javascript/rewrite/src/javascript/parser.ts +++ b/rewrite-javascript/rewrite/src/javascript/parser.ts @@ -2637,18 +2637,10 @@ export class JavaScriptParserVisitor { } visitVariableStatement(node: ts.VariableStatement): JS.ScopedVariableDeclarations | J.VariableDeclarations { + const prefix = this.prefix(node); return produce(this.visitVariableDeclarationList(node.declarationList), draft => { - if (node.modifiers) { - draft.modifiers = this.mapModifiers(node).concat(draft.modifiers); - draft.prefix = this.prefix(node); - } else { - // When there are no modifiers on the VariableStatement, move the prefix from the first - // declaration list modifier (const/let/var) to the parent J.VariableDeclarations - if (draft.modifiers.length > 0) { - draft.prefix = draft.modifiers[0].prefix; - draft.modifiers[0].prefix = emptySpace; - } - } + draft.prefix = prefix; + draft.modifiers = this.mapModifiers(node).concat(draft.modifiers); }); }