From 26cd9f543376349e3d9b680977609cc862d471b0 Mon Sep 17 00:00:00 2001 From: Pete Bacon Darwin Date: Fri, 17 Aug 2018 07:50:45 +0100 Subject: [PATCH] feat(ivy): implement `Renderer.getSwitchableDeclarations` (#25534) This supports the "ngcc ivy switch" specified in #25238. PR Close #25534 --- .../ngcc/src/rendering/esm2015_renderer.ts | 11 ++++++ .../src/ngcc/src/rendering/renderer.ts | 8 ++++- .../test/rendering/esm2015_renderer_spec.ts | 36 +++++++++++++++++++ .../src/ngcc/test/rendering/renderer_spec.ts | 9 +++-- 4 files changed, 60 insertions(+), 4 deletions(-) diff --git a/packages/compiler-cli/src/ngcc/src/rendering/esm2015_renderer.ts b/packages/compiler-cli/src/ngcc/src/rendering/esm2015_renderer.ts index d3a473a4f5c2c..9714a3714bd2a 100644 --- a/packages/compiler-cli/src/ngcc/src/rendering/esm2015_renderer.ts +++ b/packages/compiler-cli/src/ngcc/src/rendering/esm2015_renderer.ts @@ -7,6 +7,7 @@ */ import * as ts from 'typescript'; import MagicString from 'magic-string'; +import {POST_NGCC_MARKER, PRE_NGCC_MARKER} from '../host/ngcc_host'; import {AnalyzedClass} from '../analyzer'; import {Renderer} from './renderer'; @@ -70,4 +71,14 @@ export class Esm2015Renderer extends Renderer { } }); } + + rewriteSwitchableDeclarations(outputText: MagicString, sourceFile: ts.SourceFile): void { + const declarations = this.host.getSwitchableDeclarations(sourceFile); + declarations.forEach(declaration => { + const start = declaration.initializer.getStart(); + const end = declaration.initializer.getEnd(); + const replacement = declaration.initializer.text.replace(PRE_NGCC_MARKER, POST_NGCC_MARKER); + outputText.overwrite(start, end, replacement); + }); + } } diff --git a/packages/compiler-cli/src/ngcc/src/rendering/renderer.ts b/packages/compiler-cli/src/ngcc/src/rendering/renderer.ts index a5f421352c7f3..2556c5e066426 100644 --- a/packages/compiler-cli/src/ngcc/src/rendering/renderer.ts +++ b/packages/compiler-cli/src/ngcc/src/rendering/renderer.ts @@ -89,9 +89,13 @@ export abstract class Renderer { file.sourceFile); this.addImports(outputText, importManager.getAllImports(file.sourceFile.fileName, null)); - // QUESTION: do we need to remove contructor param metadata and property decorators? + + // TODO: remove contructor param metadata and property decorators (we need info from the + // handlers to do this) this.removeDecorators(outputText, decoratorsToRemove); + this.rewriteSwitchableDeclarations(outputText, file.sourceFile); + return this.renderSourceAndMap(file, input, outputText, targetPath); } @@ -102,6 +106,8 @@ export abstract class Renderer { output: MagicString, analyzedClass: AnalyzedClass, definitions: string): void; protected abstract removeDecorators( output: MagicString, decoratorsToRemove: Map): void; + protected abstract rewriteSwitchableDeclarations( + outputText: MagicString, sourceFile: ts.SourceFile): void; /** * Add the decorator nodes that are to be removed to a map diff --git a/packages/compiler-cli/src/ngcc/test/rendering/esm2015_renderer_spec.ts b/packages/compiler-cli/src/ngcc/test/rendering/esm2015_renderer_spec.ts index bf9f65629132b..a8ff47e7f0641 100644 --- a/packages/compiler-cli/src/ngcc/test/rendering/esm2015_renderer_spec.ts +++ b/packages/compiler-cli/src/ngcc/test/rendering/esm2015_renderer_spec.ts @@ -46,6 +46,19 @@ export class C {} C.decorators = [ { type: Directive, args: [{ selector: '[c]' }] }, ]; +let compileNgModuleFactory = compileNgModuleFactory__PRE_NGCC__; +let badlyFormattedVariable = __PRE_NGCC__badlyFormattedVariable; + +function compileNgModuleFactory__PRE_NGCC__(injector, options, moduleType) { + const compilerFactory = injector.get(CompilerFactory); + const compiler = compilerFactory.createCompiler([options]); + return compiler.compileModuleAsync(moduleType); +} + +function compileNgModuleFactory__POST_NGCC__(injector, options, moduleType) { + ngDevMode && assertNgModuleType(moduleType); + return Promise.resolve(new R3NgModuleFactory(moduleType)); +} // Some other content` }; @@ -82,6 +95,29 @@ export class A {}`); }); }); + describe('rewriteSwitchableDeclarations', () => { + it('should switch marked declaration initializers', () => { + const {renderer, program} = setup(PROGRAM); + const file = program.getSourceFile('some/file.js'); + if (file === undefined) { + throw new Error(`Could not find source file`); + } + const output = new MagicString(PROGRAM.contents); + renderer.rewriteSwitchableDeclarations(output, file); + expect(output.toString()) + .not.toContain(`let compileNgModuleFactory = compileNgModuleFactory__PRE_NGCC__;`); + expect(output.toString()) + .toContain(`let badlyFormattedVariable = __PRE_NGCC__badlyFormattedVariable;`); + expect(output.toString()) + .toContain(`let compileNgModuleFactory = compileNgModuleFactory__POST_NGCC__;`); + expect(output.toString()) + .toContain( + `function compileNgModuleFactory__PRE_NGCC__(injector, options, moduleType) {`); + expect(output.toString()) + .toContain( + `function compileNgModuleFactory__POST_NGCC__(injector, options, moduleType) {`); + }); + }); describe('addDefinitions', () => { it('should insert the definitions directly after the class declaration', () => { diff --git a/packages/compiler-cli/src/ngcc/test/rendering/renderer_spec.ts b/packages/compiler-cli/src/ngcc/test/rendering/renderer_spec.ts index a298e7a25cc7a..c58559ea64c3e 100644 --- a/packages/compiler-cli/src/ngcc/test/rendering/renderer_spec.ts +++ b/packages/compiler-cli/src/ngcc/test/rendering/renderer_spec.ts @@ -29,6 +29,9 @@ class TestRenderer extends Renderer { removeDecorators(output: MagicString, decoratorsToRemove: Map) { output.prepend('\n// REMOVE DECORATORS\n'); } + rewriteSwitchableDeclarations(output: MagicString, sourceFile: ts.SourceFile): void { + output.prepend('\n// REWRITTEN DECLARATIONS\n'); + } } function createTestRenderer() { @@ -68,7 +71,7 @@ describe('Renderer', () => { ] }); const RENDERED_CONTENTS = - `\n// REMOVE DECORATORS\n\n// ADD IMPORTS\n\n// ADD CONSTANTS\n\n// ADD DEFINITIONS\n` + + `\n// REWRITTEN DECLARATIONS\n\n// REMOVE DECORATORS\n\n// ADD IMPORTS\n\n// ADD CONSTANTS\n\n// ADD DEFINITIONS\n` + INPUT_PROGRAM.contents; const OUTPUT_PROGRAM_MAP = fromObject({ 'version': 3, @@ -78,14 +81,14 @@ describe('Renderer', () => { 'import { Directive } from \'@angular/core\';\nexport class A {\n foo(x) {\n return x;\n }\n}\nA.decorators = [\n { type: Directive, args: [{ selector: \'[a]\' }] }\n];\n' ], 'names': [], - 'mappings': ';;;;;;;;AAAA;;;;;;;;;' + 'mappings': ';;;;;;;;;;AAAA;;;;;;;;;' }); const MERGED_OUTPUT_PROGRAM_MAP = fromObject({ 'version': 3, 'sources': ['/file.ts'], 'names': [], - 'mappings': ';;;;;;;;AAAA', + 'mappings': ';;;;;;;;;;AAAA', 'file': '/output_file.js', 'sourcesContent': [ 'import { Directive } from \'@angular/core\';\nexport class A {\n foo(x: string): string {\n return x;\n }\n static decorators = [\n { type: Directive, args: [{ selector: \'[a]\' }] }\n ];\n}'