From 7ed984b30cb6d54e0c23fb1da354fd4658114780 Mon Sep 17 00:00:00 2001 From: crisbeto Date: Thu, 21 Nov 2019 21:07:54 +0100 Subject: [PATCH] fix(ivy): add flag to skip non-exported classes (#33921) (#34340) In ViewEngine we were only generating code for exported classes, however with Ivy we do it no matter whether the class has been exported or not. These changes add an extra flag that allows consumers to opt into the ViewEngine behavior. The flag works by treating non-exported classes as if they're set to `jit: true`. Fixes #33724. PR Close #33921 PR Close #34340 --- packages/compiler-cli/src/ngtsc/program.ts | 3 +- .../src/ngtsc/transform/src/compilation.ts | 9 +++- packages/compiler-cli/src/transformers/api.ts | 6 +++ .../compiler-cli/test/ngtsc/ngtsc_spec.ts | 54 +++++++++++++++++++ 4 files changed, 69 insertions(+), 3 deletions(-) diff --git a/packages/compiler-cli/src/ngtsc/program.ts b/packages/compiler-cli/src/ngtsc/program.ts index f05bd8b1d3..7378985406 100644 --- a/packages/compiler-cli/src/ngtsc/program.ts +++ b/packages/compiler-cli/src/ngtsc/program.ts @@ -644,7 +644,8 @@ export class NgtscProgram implements api.Program { return new IvyCompilation( handlers, this.reflector, this.importRewriter, this.incrementalDriver, this.perfRecorder, - this.sourceToFactorySymbols, scopeRegistry); + this.sourceToFactorySymbols, scopeRegistry, + this.options.compileNonExportedClasses !== false); } private get reflector(): TypeScriptReflectionHost { diff --git a/packages/compiler-cli/src/ngtsc/transform/src/compilation.ts b/packages/compiler-cli/src/ngtsc/transform/src/compilation.ts index 72d2a4712a..c7762386cb 100644 --- a/packages/compiler-cli/src/ngtsc/transform/src/compilation.ts +++ b/packages/compiler-cli/src/ngtsc/transform/src/compilation.ts @@ -17,7 +17,7 @@ import {PerfRecorder} from '../../perf'; import {ClassDeclaration, ReflectionHost, isNamedClassDeclaration, reflectNameOfDeclaration} from '../../reflection'; import {LocalModuleScopeRegistry} from '../../scope'; import {TypeCheckContext} from '../../typecheck'; -import {getSourceFile} from '../../util/src/typescript'; +import {getSourceFile, isExported} from '../../util/src/typescript'; import {AnalysisOutput, CompileResult, DecoratorHandler, DetectResult, HandlerPrecedence} from './api'; import {DtsFileTransformer} from './declaration'; @@ -80,7 +80,8 @@ export class IvyCompilation { private handlers: DecoratorHandler[], private reflector: ReflectionHost, private importRewriter: ImportRewriter, private incrementalDriver: IncrementalDriver, private perf: PerfRecorder, private sourceToFactorySymbols: Map>|null, - private scopeRegistry: LocalModuleScopeRegistry) {} + private scopeRegistry: LocalModuleScopeRegistry, private compileNonExportedClasses: boolean) { + } get exportStatements(): Map> { return this.reexportMap; } @@ -90,6 +91,10 @@ export class IvyCompilation { analyzeAsync(sf: ts.SourceFile): Promise|undefined { return this.analyze(sf, true); } private detectHandlersForClass(node: ClassDeclaration): IvyClass|null { + if (!this.compileNonExportedClasses && !isExported(node)) { + return null; + } + // The first step is to reflect the decorators. const classDecorators = this.reflector.getDecoratorsOfDeclaration(node); let ivyClass: IvyClass|null = null; diff --git a/packages/compiler-cli/src/transformers/api.ts b/packages/compiler-cli/src/transformers/api.ts index d39daa5d79..48c178363e 100644 --- a/packages/compiler-cli/src/transformers/api.ts +++ b/packages/compiler-cli/src/transformers/api.ts @@ -391,6 +391,12 @@ export interface CompilerOptions extends ts.CompilerOptions { * support these future imports. */ generateDeepReexports?: boolean; + + /** + * Whether the compiler should avoid generating code for classes that haven't been exported. + * This is only active when building with `enableIvy: true`. Defaults to `true`. + */ + compileNonExportedClasses?: boolean; } export interface CompilerHost extends ts.CompilerHost { diff --git a/packages/compiler-cli/test/ngtsc/ngtsc_spec.ts b/packages/compiler-cli/test/ngtsc/ngtsc_spec.ts index 19014a7cd7..877b8dc0c7 100644 --- a/packages/compiler-cli/test/ngtsc/ngtsc_spec.ts +++ b/packages/compiler-cli/test/ngtsc/ngtsc_spec.ts @@ -5168,6 +5168,60 @@ export const Foo = Foo__PRE_R3__; expect(jsContents).toContain('styles: ["h1[_ngcontent-%COMP%] {font-size: larger}"]'); }); }); + + describe('non-exported classes', () => { + beforeEach(() => env.tsconfig({compileNonExportedClasses: false})); + + it('should not emit directive definitions for non-exported classes if configured', () => { + env.write('test.ts', ` + import {Directive} from '@angular/core'; + + @Directive({ + selector: '[test]' + }) + class TestDirective {} + `); + env.driveMain(); + const jsContents = env.getContents('test.js'); + + expect(jsContents).not.toContain('defineDirective('); + expect(jsContents).toContain('Directive({'); + }); + + it('should not emit component definitions for non-exported classes if configured', () => { + env.write('test.ts', ` + import {Component} from '@angular/core'; + + @Component({ + selector: 'test', + template: 'hello' + }) + class TestComponent {} + `); + env.driveMain(); + const jsContents = env.getContents('test.js'); + + expect(jsContents).not.toContain('defineComponent('); + expect(jsContents).toContain('Component({'); + }); + + it('should not emit module definitions for non-exported classes if configured', () => { + env.write('test.ts', ` + import {NgModule} from '@angular/core'; + + @NgModule({ + declarations: [] + }) + class TestModule {} + `); + env.driveMain(); + const jsContents = env.getContents('test.js'); + + expect(jsContents).not.toContain('defineNgModule('); + expect(jsContents).toContain('NgModule({'); + }); + }); + }); function expectTokenAtPosition(