From 8815ace4188a77bc93caa5eb9696681a47fef10d Mon Sep 17 00:00:00 2001 From: Pete Bacon Darwin Date: Wed, 8 Jan 2020 15:03:30 +0000 Subject: [PATCH] fix(ngcc): insert definitions after statement (#34677) If a class was defined as a class expression in a variable declaration, the definitions were being inserted before the statment's final semi-colon. Now the insertion point will be after the full statement. Fixes #34648 PR Close #34677 --- .../src/rendering/esm_rendering_formatter.ts | 16 ++++++++++++++-- .../rendering/esm_rendering_formatter_spec.ts | 18 +++++++++++++++++- 2 files changed, 31 insertions(+), 3 deletions(-) diff --git a/packages/compiler-cli/ngcc/src/rendering/esm_rendering_formatter.ts b/packages/compiler-cli/ngcc/src/rendering/esm_rendering_formatter.ts index 98a35134e2..d2b42d5522 100644 --- a/packages/compiler-cli/ngcc/src/rendering/esm_rendering_formatter.ts +++ b/packages/compiler-cli/ngcc/src/rendering/esm_rendering_formatter.ts @@ -102,7 +102,8 @@ export class EsmRenderingFormatter implements RenderingFormatter { if (!classSymbol) { throw new Error(`Compiled class does not have a valid symbol: ${compiledClass.name}`); } - const insertionPoint = classSymbol.declaration.valueDeclaration.getEnd(); + const declarationStatement = getDeclarationStatement(classSymbol.declaration.valueDeclaration); + const insertionPoint = declarationStatement.getEnd(); output.appendLeft(insertionPoint, '\n' + definitions); } @@ -273,7 +274,18 @@ export class EsmRenderingFormatter implements RenderingFormatter { } } -function findStatement(node: ts.Node) { +function getDeclarationStatement(node: ts.Node): ts.Statement { + let statement = node; + while (statement) { + if (ts.isVariableStatement(statement) || ts.isClassDeclaration(statement)) { + return statement; + } + statement = statement.parent; + } + throw new Error(`Class is not defined in a declaration statement: ${node.getText()}`); +} + +function findStatement(node: ts.Node): ts.Statement|undefined { while (node) { if (ts.isExpressionStatement(node) || ts.isReturnStatement(node)) { return node; diff --git a/packages/compiler-cli/ngcc/test/rendering/esm_rendering_formatter_spec.ts b/packages/compiler-cli/ngcc/test/rendering/esm_rendering_formatter_spec.ts index 85d43e230f..275c42e5c7 100644 --- a/packages/compiler-cli/ngcc/test/rendering/esm_rendering_formatter_spec.ts +++ b/packages/compiler-cli/ngcc/test/rendering/esm_rendering_formatter_spec.ts @@ -74,10 +74,12 @@ B.decorators = [ { type: OtherB }, { type: Directive, args: [{ selector: '[b]' }] } ]; -export class C {} +var C_1; +let C = C_1 = class C {}; C.decorators = [ { type: Directive, args: [{ selector: '[c]' }] }, ]; +export C; let compileNgModuleFactory = compileNgModuleFactory__PRE_R3__; let badlyFormattedVariable = __PRE_R3__badlyFormattedVariable; @@ -222,6 +224,20 @@ SOME DEFINITION TEXT A.decorators = [ `); }); + + it('should insert the definitions after the variable declaration of class expressions', + () => { + const {renderer, decorationAnalyses, sourceFile} = setup([PROGRAM]); + const output = new MagicString(PROGRAM.contents); + const compiledClass = + decorationAnalyses.get(sourceFile) !.compiledClasses.find(c => c.name === 'C') !; + renderer.addDefinitions(output, compiledClass, 'SOME DEFINITION TEXT'); + expect(output.toString()).toContain(` +let C = C_1 = class C {}; +SOME DEFINITION TEXT +C.decorators = [ +`); + }); }); describe('addAdjacentStatements', () => {