| 
									
										
										
										
											2018-11-21 09:05:27 +00:00
										 |  |  | /** | 
					
						
							|  |  |  |  * @license | 
					
						
							|  |  |  |  * Copyright Google Inc. All Rights Reserved. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * Use of this source code is governed by an MIT-style license that can be | 
					
						
							|  |  |  |  * found in the LICENSE file at https://angular.io/license
 | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | import * as ts from 'typescript'; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-03-20 13:47:58 +00:00
										 |  |  | import {Reference} from '../../../src/ngtsc/imports'; | 
					
						
							| 
									
										
										
										
											2018-11-21 09:05:27 +00:00
										 |  |  | import {NgccReferencesRegistry} from '../../src/analysis/ngcc_references_registry'; | 
					
						
							|  |  |  | import {PrivateDeclarationsAnalyzer} from '../../src/analysis/private_declarations_analyzer'; | 
					
						
							|  |  |  | import {Esm2015ReflectionHost} from '../../src/host/esm2015_host'; | 
					
						
							| 
									
										
										
										
											2019-03-29 10:13:14 +00:00
										 |  |  | import {MockLogger} from '../helpers/mock_logger'; | 
					
						
							| 
									
										
										
										
											2018-11-21 09:05:27 +00:00
										 |  |  | import {getDeclaration, makeTestBundleProgram, makeTestProgram} from '../helpers/utils'; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-02-14 18:59:46 +01:00
										 |  |  | describe('PrivateDeclarationsAnalyzer', () => { | 
					
						
							|  |  |  |   describe('analyzeProgram()', () => { | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     const TEST_PROGRAM = [ | 
					
						
							|  |  |  |       { | 
					
						
							|  |  |  |         name: '/src/entry_point.js', | 
					
						
							|  |  |  |         isRoot: true, | 
					
						
							|  |  |  |         contents: `
 | 
					
						
							| 
									
										
										
										
											2018-11-21 09:05:27 +00:00
										 |  |  |     export {PublicComponent} from './a'; | 
					
						
							|  |  |  |     export {ModuleA} from './mod'; | 
					
						
							|  |  |  |     export {ModuleB} from './b'; | 
					
						
							|  |  |  |   `
 | 
					
						
							| 
									
										
										
										
											2019-02-14 18:59:46 +01:00
										 |  |  |       }, | 
					
						
							|  |  |  |       { | 
					
						
							|  |  |  |         name: '/src/a.js', | 
					
						
							|  |  |  |         isRoot: false, | 
					
						
							|  |  |  |         contents: `
 | 
					
						
							| 
									
										
										
										
											2018-11-21 09:05:27 +00:00
										 |  |  |     import {Component} from '@angular/core'; | 
					
						
							|  |  |  |     export class PublicComponent {} | 
					
						
							|  |  |  |     PublicComponent.decorators = [ | 
					
						
							|  |  |  |       {type: Component, args: [{selectors: 'a', template: ''}]} | 
					
						
							|  |  |  |     ]; | 
					
						
							|  |  |  |   `
 | 
					
						
							| 
									
										
										
										
											2019-02-14 18:59:46 +01:00
										 |  |  |       }, | 
					
						
							|  |  |  |       { | 
					
						
							|  |  |  |         name: '/src/b.js', | 
					
						
							|  |  |  |         isRoot: false, | 
					
						
							|  |  |  |         contents: `
 | 
					
						
							| 
									
										
										
										
											2018-11-21 09:05:27 +00:00
										 |  |  |     import {Component, NgModule} from '@angular/core'; | 
					
						
							| 
									
										
										
										
											2019-02-14 18:59:46 +01:00
										 |  |  |     class PrivateComponent1 {} | 
					
						
							|  |  |  |     PrivateComponent1.decorators = [ | 
					
						
							| 
									
										
										
										
											2018-11-21 09:05:27 +00:00
										 |  |  |       {type: Component, args: [{selectors: 'b', template: ''}]} | 
					
						
							|  |  |  |     ]; | 
					
						
							| 
									
										
										
										
											2019-02-14 18:59:46 +01:00
										 |  |  |     class PrivateComponent2 {} | 
					
						
							|  |  |  |     PrivateComponent2.decorators = [ | 
					
						
							|  |  |  |       {type: Component, args: [{selectors: 'c', template: ''}]} | 
					
						
							|  |  |  |     ]; | 
					
						
							| 
									
										
										
										
											2018-11-21 09:05:27 +00:00
										 |  |  |     export class ModuleB {} | 
					
						
							|  |  |  |     ModuleB.decorators = [ | 
					
						
							| 
									
										
										
										
											2019-02-14 18:59:46 +01:00
										 |  |  |       {type: NgModule, args: [{declarations: [PrivateComponent1]}]} | 
					
						
							| 
									
										
										
										
											2018-11-21 09:05:27 +00:00
										 |  |  |     ]; | 
					
						
							|  |  |  |   `
 | 
					
						
							| 
									
										
										
										
											2019-02-14 18:59:46 +01:00
										 |  |  |       }, | 
					
						
							|  |  |  |       { | 
					
						
							|  |  |  |         name: '/src/c.js', | 
					
						
							|  |  |  |         isRoot: false, | 
					
						
							|  |  |  |         contents: `
 | 
					
						
							| 
									
										
										
										
											2018-11-21 09:05:27 +00:00
										 |  |  |     import {Component} from '@angular/core'; | 
					
						
							| 
									
										
										
										
											2019-02-14 18:59:46 +01:00
										 |  |  |     export class InternalComponent1 {} | 
					
						
							|  |  |  |     InternalComponent1.decorators = [ | 
					
						
							|  |  |  |       {type: Component, args: [{selectors: 'd', template: ''}]} | 
					
						
							|  |  |  |     ]; | 
					
						
							|  |  |  |     export class InternalComponent2 {} | 
					
						
							|  |  |  |     InternalComponent2.decorators = [ | 
					
						
							|  |  |  |       {type: Component, args: [{selectors: 'e', template: ''}]} | 
					
						
							| 
									
										
										
										
											2018-11-21 09:05:27 +00:00
										 |  |  |     ]; | 
					
						
							|  |  |  |   `
 | 
					
						
							| 
									
										
										
										
											2019-02-14 18:59:46 +01:00
										 |  |  |       }, | 
					
						
							|  |  |  |       { | 
					
						
							|  |  |  |         name: '/src/mod.js', | 
					
						
							|  |  |  |         isRoot: false, | 
					
						
							|  |  |  |         contents: `
 | 
					
						
							| 
									
										
										
										
											2018-11-21 09:05:27 +00:00
										 |  |  |     import {Component, NgModule} from '@angular/core'; | 
					
						
							|  |  |  |     import {PublicComponent} from './a'; | 
					
						
							|  |  |  |     import {ModuleB} from './b'; | 
					
						
							| 
									
										
										
										
											2019-02-14 18:59:46 +01:00
										 |  |  |     import {InternalComponent1} from './c'; | 
					
						
							| 
									
										
										
										
											2018-11-21 09:05:27 +00:00
										 |  |  |     export class ModuleA {} | 
					
						
							|  |  |  |     ModuleA.decorators = [ | 
					
						
							|  |  |  |       {type: NgModule, args: [{ | 
					
						
							| 
									
										
										
										
											2019-02-14 18:59:46 +01:00
										 |  |  |         declarations: [PublicComponent, InternalComponent1], | 
					
						
							| 
									
										
										
										
											2018-11-21 09:05:27 +00:00
										 |  |  |         imports: [ModuleB] | 
					
						
							|  |  |  |       }]} | 
					
						
							|  |  |  |     ]; | 
					
						
							|  |  |  |   `
 | 
					
						
							| 
									
										
										
										
											2019-02-14 18:59:46 +01:00
										 |  |  |       } | 
					
						
							|  |  |  |     ]; | 
					
						
							|  |  |  |     const TEST_DTS_PROGRAM = [ | 
					
						
							|  |  |  |       { | 
					
						
							|  |  |  |         name: '/typings/entry_point.d.ts', | 
					
						
							|  |  |  |         isRoot: true, | 
					
						
							|  |  |  |         contents: `
 | 
					
						
							| 
									
										
										
										
											2018-11-21 09:05:27 +00:00
										 |  |  |     export {PublicComponent} from './a'; | 
					
						
							|  |  |  |     export {ModuleA} from './mod'; | 
					
						
							|  |  |  |     export {ModuleB} from './b'; | 
					
						
							|  |  |  |   `
 | 
					
						
							| 
									
										
										
										
											2019-02-14 18:59:46 +01:00
										 |  |  |       }, | 
					
						
							|  |  |  |       { | 
					
						
							|  |  |  |         name: '/typings/a.d.ts', | 
					
						
							|  |  |  |         isRoot: false, | 
					
						
							|  |  |  |         contents: `
 | 
					
						
							| 
									
										
										
										
											2018-11-21 09:05:27 +00:00
										 |  |  |     export declare class PublicComponent {} | 
					
						
							|  |  |  |   `
 | 
					
						
							| 
									
										
										
										
											2019-02-14 18:59:46 +01:00
										 |  |  |       }, | 
					
						
							|  |  |  |       { | 
					
						
							|  |  |  |         name: '/typings/b.d.ts', | 
					
						
							|  |  |  |         isRoot: false, | 
					
						
							|  |  |  |         contents: `
 | 
					
						
							| 
									
										
										
										
											2018-11-21 09:05:27 +00:00
										 |  |  |     export declare class ModuleB {} | 
					
						
							|  |  |  |   `
 | 
					
						
							| 
									
										
										
										
											2019-02-14 18:59:46 +01:00
										 |  |  |       }, | 
					
						
							|  |  |  |       { | 
					
						
							|  |  |  |         name: '/typings/c.d.ts', | 
					
						
							|  |  |  |         isRoot: false, | 
					
						
							|  |  |  |         contents: `
 | 
					
						
							|  |  |  |     export declare class InternalComponent1 {} | 
					
						
							| 
									
										
										
										
											2018-11-21 09:05:27 +00:00
										 |  |  |   `
 | 
					
						
							| 
									
										
										
										
											2019-02-14 18:59:46 +01:00
										 |  |  |       }, | 
					
						
							|  |  |  |       { | 
					
						
							|  |  |  |         name: '/typings/mod.d.ts', | 
					
						
							|  |  |  |         isRoot: false, | 
					
						
							|  |  |  |         contents: `
 | 
					
						
							| 
									
										
										
										
											2018-11-21 09:05:27 +00:00
										 |  |  |     import {PublicComponent} from './a'; | 
					
						
							|  |  |  |     import {ModuleB} from './b'; | 
					
						
							| 
									
										
										
										
											2019-02-14 18:59:46 +01:00
										 |  |  |     import {InternalComponent1} from './c'; | 
					
						
							| 
									
										
										
										
											2018-11-21 09:05:27 +00:00
										 |  |  |     export declare class ModuleA {} | 
					
						
							|  |  |  |   `
 | 
					
						
							| 
									
										
										
										
											2019-02-14 18:59:46 +01:00
										 |  |  |       }, | 
					
						
							|  |  |  |     ]; | 
					
						
							| 
									
										
										
										
											2018-11-21 09:05:27 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |     it('should find all NgModule declarations that were not publicly exported from the entry-point', | 
					
						
							|  |  |  |        () => { | 
					
						
							| 
									
										
										
										
											2019-02-14 18:59:46 +01:00
										 |  |  |          const {program, referencesRegistry, analyzer} = setup(TEST_PROGRAM, TEST_DTS_PROGRAM); | 
					
						
							| 
									
										
										
										
											2018-11-21 09:05:27 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-02-14 18:59:46 +01:00
										 |  |  |          addToReferencesRegistry(program, referencesRegistry, '/src/a.js', 'PublicComponent'); | 
					
						
							|  |  |  |          addToReferencesRegistry(program, referencesRegistry, '/src/b.js', 'PrivateComponent1'); | 
					
						
							|  |  |  |          addToReferencesRegistry(program, referencesRegistry, '/src/c.js', 'InternalComponent1'); | 
					
						
							| 
									
										
										
										
											2018-11-21 09:05:27 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |          const analyses = analyzer.analyzeProgram(program); | 
					
						
							| 
									
										
										
										
											2019-02-14 18:59:46 +01:00
										 |  |  |          // Note that `PrivateComponent2` and `InternalComponent2` are not found because they are
 | 
					
						
							|  |  |  |          // not added to the ReferencesRegistry (i.e. they were not declared in an NgModule).
 | 
					
						
							| 
									
										
										
										
											2018-11-21 09:05:27 +00:00
										 |  |  |          expect(analyses.length).toEqual(2); | 
					
						
							|  |  |  |          expect(analyses).toEqual([ | 
					
						
							| 
									
										
										
										
											2019-02-14 18:59:46 +01:00
										 |  |  |            {identifier: 'PrivateComponent1', from: '/src/b.js', dtsFrom: null, alias: null}, | 
					
						
							|  |  |  |            { | 
					
						
							|  |  |  |              identifier: 'InternalComponent1', | 
					
						
							|  |  |  |              from: '/src/c.js', | 
					
						
							|  |  |  |              dtsFrom: '/typings/c.d.ts', | 
					
						
							|  |  |  |              alias: null | 
					
						
							|  |  |  |            }, | 
					
						
							| 
									
										
										
										
											2018-11-21 09:05:27 +00:00
										 |  |  |          ]); | 
					
						
							|  |  |  |        }); | 
					
						
							| 
									
										
										
										
											2019-02-14 18:59:46 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |     const ALIASED_EXPORTS_PROGRAM = [ | 
					
						
							|  |  |  |       { | 
					
						
							|  |  |  |         name: '/src/entry_point.js', | 
					
						
							|  |  |  |         isRoot: true, | 
					
						
							|  |  |  |         contents: `
 | 
					
						
							|  |  |  |         // This component is only exported as an alias.
 | 
					
						
							|  |  |  |         export {ComponentOne as aliasedComponentOne} from './a'; | 
					
						
							|  |  |  |         // This component is exported both as itself and an alias.
 | 
					
						
							|  |  |  |         export {ComponentTwo as aliasedComponentTwo, ComponentTwo} from './a'; | 
					
						
							|  |  |  |       `
 | 
					
						
							|  |  |  |       }, | 
					
						
							|  |  |  |       { | 
					
						
							|  |  |  |         name: '/src/a.js', | 
					
						
							|  |  |  |         isRoot: false, | 
					
						
							|  |  |  |         contents: `
 | 
					
						
							|  |  |  |       import {Component} from '@angular/core'; | 
					
						
							|  |  |  |       export class ComponentOne {} | 
					
						
							|  |  |  |       ComponentOne.decorators = [ | 
					
						
							|  |  |  |         {type: Component, args: [{selectors: 'a', template: ''}]} | 
					
						
							|  |  |  |       ]; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       export class ComponentTwo {} | 
					
						
							|  |  |  |       Component.decorators = [ | 
					
						
							|  |  |  |         {type: Component, args: [{selectors: 'a', template: ''}]} | 
					
						
							|  |  |  |       ]; | 
					
						
							|  |  |  |     `
 | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |     ]; | 
					
						
							|  |  |  |     const ALIASED_EXPORTS_DTS_PROGRAM = [ | 
					
						
							|  |  |  |       { | 
					
						
							|  |  |  |         name: '/typings/entry_point.d.ts', | 
					
						
							|  |  |  |         isRoot: true, | 
					
						
							|  |  |  |         contents: `
 | 
					
						
							|  |  |  |         export declare class aliasedComponentOne {} | 
					
						
							|  |  |  |         export declare class ComponentTwo {} | 
					
						
							|  |  |  |         export {ComponentTwo as aliasedComponentTwo} | 
					
						
							|  |  |  |       `
 | 
					
						
							|  |  |  |       }, | 
					
						
							|  |  |  |     ]; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     it('should find all non-public declarations that were aliased', () => { | 
					
						
							|  |  |  |       const {program, referencesRegistry, analyzer} = | 
					
						
							|  |  |  |           setup(ALIASED_EXPORTS_PROGRAM, ALIASED_EXPORTS_DTS_PROGRAM); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       addToReferencesRegistry(program, referencesRegistry, '/src/a.js', 'ComponentOne'); | 
					
						
							|  |  |  |       addToReferencesRegistry(program, referencesRegistry, '/src/a.js', 'ComponentTwo'); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       const analyses = analyzer.analyzeProgram(program); | 
					
						
							|  |  |  |       expect(analyses).toEqual([{ | 
					
						
							|  |  |  |         identifier: 'ComponentOne', | 
					
						
							|  |  |  |         from: '/src/a.js', | 
					
						
							|  |  |  |         dtsFrom: null, | 
					
						
							|  |  |  |         alias: 'aliasedComponentOne', | 
					
						
							|  |  |  |       }]); | 
					
						
							|  |  |  |     }); | 
					
						
							| 
									
										
										
										
											2018-11-21 09:05:27 +00:00
										 |  |  |   }); | 
					
						
							|  |  |  | }); | 
					
						
							| 
									
										
										
										
											2019-02-14 18:59:46 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  | type Files = { | 
					
						
							|  |  |  |   name: string, | 
					
						
							|  |  |  |   contents: string, isRoot?: boolean | undefined | 
					
						
							|  |  |  | }[]; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | function setup(jsProgram: Files, dtsProgram: Files) { | 
					
						
							|  |  |  |   const program = makeTestProgram(...jsProgram); | 
					
						
							|  |  |  |   const dts = makeTestBundleProgram(dtsProgram); | 
					
						
							| 
									
										
										
										
											2019-03-29 10:13:14 +00:00
										 |  |  |   const host = new Esm2015ReflectionHost(new MockLogger(), false, program.getTypeChecker(), dts); | 
					
						
							| 
									
										
										
										
											2019-02-14 18:59:46 +01:00
										 |  |  |   const referencesRegistry = new NgccReferencesRegistry(host); | 
					
						
							|  |  |  |   const analyzer = new PrivateDeclarationsAnalyzer(host, referencesRegistry); | 
					
						
							|  |  |  |   return {program, referencesRegistry, analyzer}; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /** | 
					
						
							|  |  |  |  * Add up the named component to the references registry. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * This would normally be done by the decoration handlers in the `DecorationAnalyzer`. | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | function addToReferencesRegistry( | 
					
						
							|  |  |  |     program: ts.Program, registry: NgccReferencesRegistry, fileName: string, | 
					
						
							|  |  |  |     componentName: string) { | 
					
						
							|  |  |  |   const declaration = getDeclaration(program, fileName, componentName, ts.isClassDeclaration); | 
					
						
							|  |  |  |   registry.add(null !, new Reference(declaration)); | 
					
						
							|  |  |  | } |