From b303b0dc059e617e56d24e75b0d62a26702f68b6 Mon Sep 17 00:00:00 2001 From: Aleksey Semikozov Date: Thu, 23 Apr 2026 16:13:17 -0300 Subject: [PATCH 1/2] Vite plugin - fix field initializer placement in constructor body --- .../build/vite-plugin-devextreme.test.ts | 16 ++++++++++++++++ .../devextreme/build/vite-plugin-devextreme.ts | 16 ++++++++++------ 2 files changed, 26 insertions(+), 6 deletions(-) diff --git a/packages/devextreme/build/vite-plugin-devextreme.test.ts b/packages/devextreme/build/vite-plugin-devextreme.test.ts index 620c257266e2..d32a0421e9ff 100644 --- a/packages/devextreme/build/vite-plugin-devextreme.test.ts +++ b/packages/devextreme/build/vite-plugin-devextreme.test.ts @@ -128,6 +128,22 @@ describe('moveFieldInitializersToConstructor', () => { const out = transform(input, moveFieldInitializersToConstructor); expect(out).toMatch(/constructor\(\)\s*\{\s*this\.foo\s*=\s*42;\s*const\s+x\s*=\s*1;\s*this\.x\s*=\s*x/); }); + + test('inserts right after super() when a non-matching statement precedes param-property assignment', () => { + const input = ` + class Derived extends Base { + cached = 'init'; + constructor(x) { + super(); + this.doSetup(); + this.x = x; + } + } + `; + const out = transform(input, moveFieldInitializersToConstructor); + expect(out).toMatch(/super\(\);\s*this\.cached\s*=\s*'init';\s*this\.doSetup\(\);\s*this\.x\s*=\s*x/); + expect(out).not.toMatch(/this\.x\s*=\s*x;\s*this\.cached\s*=\s*'init'/); + }); }); describe('removeUninitializedClassFields', () => { diff --git a/packages/devextreme/build/vite-plugin-devextreme.ts b/packages/devextreme/build/vite-plugin-devextreme.ts index 81a29f54b212..62dffb863e23 100644 --- a/packages/devextreme/build/vite-plugin-devextreme.ts +++ b/packages/devextreme/build/vite-plugin-devextreme.ts @@ -89,12 +89,15 @@ export function moveFieldInitializersToConstructor(): unknown { const ctorBody = ctor.body!.body as Stmt[]; - let insertAt = 0; - for (let i = 0; i < ctorBody.length; i += 1) { - const stmt = ctorBody[i]; - const isSuperCall = stmt.type === 'ExpressionStatement' + const superCallIdx = ctorBody.findIndex( + (stmt) => stmt.type === 'ExpressionStatement' && stmt.expression?.type === 'CallExpression' - && stmt.expression.callee?.type === 'Super'; + && stmt.expression.callee?.type === 'Super', + ); + + let insertAt = superCallIdx !== -1 ? superCallIdx + 1 : 0; + while (insertAt < ctorBody.length) { + const stmt = ctorBody[insertAt]; const isParamPropertyAssignment = stmt.type === 'ExpressionStatement' && stmt.expression?.type === 'AssignmentExpression' && stmt.expression.operator === '=' @@ -104,7 +107,8 @@ export function moveFieldInitializersToConstructor(): unknown { && stmt.expression.left.property?.name === stmt.expression.right.name && paramNames.has(stmt.expression.right.name!); - if (isSuperCall || isParamPropertyAssignment) insertAt = i + 1; + if (!isParamPropertyAssignment) break; + insertAt += 1; } ctorBody.splice(insertAt, 0, ...(assignments as Stmt[])); From 63a3cab69e2f9d9877251c671ecfb2b4c1de9133 Mon Sep 17 00:00:00 2001 From: Aleksey Semikozov Date: Thu, 23 Apr 2026 16:23:51 -0300 Subject: [PATCH 2/2] Vite plugin - assert full expected output in placement test --- .../build/vite-plugin-devextreme.test.ts | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/packages/devextreme/build/vite-plugin-devextreme.test.ts b/packages/devextreme/build/vite-plugin-devextreme.test.ts index d32a0421e9ff..d827da0c7ec2 100644 --- a/packages/devextreme/build/vite-plugin-devextreme.test.ts +++ b/packages/devextreme/build/vite-plugin-devextreme.test.ts @@ -19,6 +19,10 @@ function transform(code: string, plugin: unknown): string { return result!.code!; } +function normalize(code: string): string { + return code.replace(/\s+/g, ' ').trim(); +} + describe('moveFieldInitializersToConstructor', () => { test('inserts field initializer after super() in derived class', () => { const input = ` @@ -140,9 +144,18 @@ describe('moveFieldInitializersToConstructor', () => { } } `; + const expected = ` + class Derived extends Base { + constructor(x) { + super(); + this.cached = 'init'; + this.doSetup(); + this.x = x; + } + } + `; const out = transform(input, moveFieldInitializersToConstructor); - expect(out).toMatch(/super\(\);\s*this\.cached\s*=\s*'init';\s*this\.doSetup\(\);\s*this\.x\s*=\s*x/); - expect(out).not.toMatch(/this\.x\s*=\s*x;\s*this\.cached\s*=\s*'init'/); + expect(normalize(out)).toBe(normalize(expected)); }); });