2018-11-21 04:05:27 -05: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-06-06 15:22:32 -04:00
|
|
|
import {AbsoluteFsPath, absoluteFrom} from '../../../src/ngtsc/file_system';
|
|
|
|
import {TestFile, runInEachFileSystem} from '../../../src/ngtsc/file_system/testing';
|
2019-03-20 09:47:58 -04:00
|
|
|
import {Reference} from '../../../src/ngtsc/imports';
|
2019-06-06 15:22:32 -04:00
|
|
|
import {getDeclaration} from '../../../src/ngtsc/testing';
|
|
|
|
import {loadTestFiles} from '../../../test/helpers/src/mock_file_loading';
|
2018-11-21 04:05:27 -05: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 06:13:14 -04:00
|
|
|
import {MockLogger} from '../helpers/mock_logger';
|
2019-06-06 15:22:32 -04:00
|
|
|
import {getRootFiles, makeTestEntryPointBundle} from '../helpers/utils';
|
|
|
|
|
|
|
|
runInEachFileSystem(() => {
|
|
|
|
describe('PrivateDeclarationsAnalyzer', () => {
|
|
|
|
describe('analyzeProgram()', () => {
|
|
|
|
it('should find all NgModule declarations that were not publicly exported from the entry-point',
|
|
|
|
() => {
|
|
|
|
const _ = absoluteFrom;
|
|
|
|
|
|
|
|
const TEST_PROGRAM: TestFile[] = [
|
|
|
|
{
|
2019-05-25 16:34:40 -04:00
|
|
|
name: _('/node_modules/test-package/src/entry_point.js'),
|
2019-06-06 15:22:32 -04:00
|
|
|
isRoot: true,
|
|
|
|
contents: `
|
|
|
|
export {PublicComponent} from './a';
|
|
|
|
export {ModuleA} from './mod';
|
|
|
|
export {ModuleB} from './b';
|
2019-02-14 12:59:46 -05:00
|
|
|
`
|
2019-06-06 15:22:32 -04:00
|
|
|
},
|
|
|
|
{
|
2019-05-25 16:34:40 -04:00
|
|
|
name: _('/node_modules/test-package/src/a.js'),
|
2019-06-06 15:22:32 -04:00
|
|
|
isRoot: false,
|
|
|
|
contents: `
|
|
|
|
import {Component} from '@angular/core';
|
|
|
|
export class PublicComponent {}
|
|
|
|
PublicComponent.decorators = [
|
|
|
|
{type: Component, args: [{selectors: 'a', template: ''}]}
|
|
|
|
];
|
2019-02-14 12:59:46 -05:00
|
|
|
`
|
2019-06-06 15:22:32 -04:00
|
|
|
},
|
|
|
|
{
|
2019-05-25 16:34:40 -04:00
|
|
|
name: _('/node_modules/test-package/src/b.js'),
|
2019-06-06 15:22:32 -04:00
|
|
|
isRoot: false,
|
|
|
|
contents: `
|
|
|
|
import {Component, NgModule} from '@angular/core';
|
|
|
|
class PrivateComponent1 {}
|
|
|
|
PrivateComponent1.decorators = [
|
|
|
|
{type: Component, args: [{selectors: 'b', template: ''}]}
|
|
|
|
];
|
|
|
|
class PrivateComponent2 {}
|
|
|
|
PrivateComponent2.decorators = [
|
|
|
|
{type: Component, args: [{selectors: 'c', template: ''}]}
|
|
|
|
];
|
|
|
|
export class ModuleB {}
|
|
|
|
ModuleB.decorators = [
|
|
|
|
{type: NgModule, args: [{declarations: [PrivateComponent1]}]}
|
|
|
|
];
|
|
|
|
`
|
|
|
|
},
|
|
|
|
{
|
2019-05-25 16:34:40 -04:00
|
|
|
name: _('/node_modules/test-package/src/c.js'),
|
2019-06-06 15:22:32 -04:00
|
|
|
isRoot: false,
|
|
|
|
contents: `
|
|
|
|
import {Component} from '@angular/core';
|
|
|
|
export class InternalComponent1 {}
|
|
|
|
InternalComponent1.decorators = [
|
|
|
|
{type: Component, args: [{selectors: 'd', template: ''}]}
|
|
|
|
];
|
|
|
|
export class InternalComponent2 {}
|
|
|
|
InternalComponent2.decorators = [
|
|
|
|
{type: Component, args: [{selectors: 'e', template: ''}]}
|
|
|
|
];
|
|
|
|
`
|
|
|
|
},
|
|
|
|
{
|
2019-05-25 16:34:40 -04:00
|
|
|
name: _('/node_modules/test-package/src/mod.js'),
|
2019-06-06 15:22:32 -04:00
|
|
|
isRoot: false,
|
|
|
|
contents: `
|
|
|
|
import {Component, NgModule} from '@angular/core';
|
|
|
|
import {PublicComponent} from './a';
|
|
|
|
import {ModuleB} from './b';
|
|
|
|
import {InternalComponent1} from './c';
|
|
|
|
export class ModuleA {}
|
|
|
|
ModuleA.decorators = [
|
|
|
|
{type: NgModule, args: [{
|
|
|
|
declarations: [PublicComponent, InternalComponent1],
|
|
|
|
imports: [ModuleB]
|
|
|
|
}]}
|
|
|
|
];
|
|
|
|
`
|
|
|
|
}
|
|
|
|
];
|
|
|
|
const TEST_DTS_PROGRAM = [
|
|
|
|
{
|
2019-05-25 16:34:40 -04:00
|
|
|
name: _('/node_modules/test-package/typings/entry_point.d.ts'),
|
2019-06-06 15:22:32 -04:00
|
|
|
isRoot: true,
|
|
|
|
contents: `
|
|
|
|
export {PublicComponent} from './a';
|
|
|
|
export {ModuleA} from './mod';
|
|
|
|
export {ModuleB} from './b';
|
|
|
|
`
|
|
|
|
},
|
|
|
|
{
|
2019-05-25 16:34:40 -04:00
|
|
|
name: _('/node_modules/test-package/typings/a.d.ts'),
|
2019-06-06 15:22:32 -04:00
|
|
|
isRoot: false,
|
|
|
|
contents: `
|
|
|
|
export declare class PublicComponent {}
|
|
|
|
`
|
|
|
|
},
|
|
|
|
{
|
2019-05-25 16:34:40 -04:00
|
|
|
name: _('/node_modules/test-package/typings/b.d.ts'),
|
2019-06-06 15:22:32 -04:00
|
|
|
isRoot: false,
|
|
|
|
contents: `
|
|
|
|
export declare class ModuleB {}
|
|
|
|
`
|
|
|
|
},
|
|
|
|
{
|
2019-05-25 16:34:40 -04:00
|
|
|
name: _('/node_modules/test-package/typings/c.d.ts'),
|
2019-06-06 15:22:32 -04:00
|
|
|
isRoot: false,
|
|
|
|
contents: `
|
|
|
|
export declare class InternalComponent1 {}
|
|
|
|
`
|
|
|
|
},
|
|
|
|
{
|
2019-05-25 16:34:40 -04:00
|
|
|
name: _('/node_modules/test-package/typings/mod.d.ts'),
|
2019-06-06 15:22:32 -04:00
|
|
|
isRoot: false,
|
|
|
|
contents: `
|
|
|
|
import {PublicComponent} from './a';
|
|
|
|
import {ModuleB} from './b';
|
|
|
|
import {InternalComponent1} from './c';
|
|
|
|
export declare class ModuleA {}
|
|
|
|
`
|
|
|
|
},
|
|
|
|
];
|
|
|
|
const {program, referencesRegistry, analyzer} = setup(TEST_PROGRAM, TEST_DTS_PROGRAM);
|
|
|
|
|
|
|
|
addToReferencesRegistry(
|
2019-05-25 16:34:40 -04:00
|
|
|
program, referencesRegistry, _('/node_modules/test-package/src/a.js'),
|
|
|
|
'PublicComponent');
|
2019-06-06 15:22:32 -04:00
|
|
|
addToReferencesRegistry(
|
2019-05-25 16:34:40 -04:00
|
|
|
program, referencesRegistry, _('/node_modules/test-package/src/b.js'),
|
|
|
|
'PrivateComponent1');
|
|
|
|
addToReferencesRegistry(
|
|
|
|
program, referencesRegistry, _('/node_modules/test-package/src/c.js'),
|
|
|
|
'InternalComponent1');
|
2019-06-06 15:22:32 -04:00
|
|
|
|
|
|
|
const analyses = analyzer.analyzeProgram(program);
|
|
|
|
// 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).
|
|
|
|
expect(analyses.length).toEqual(2);
|
|
|
|
expect(analyses).toEqual([
|
2019-05-25 16:34:40 -04:00
|
|
|
{
|
|
|
|
identifier: 'PrivateComponent1',
|
|
|
|
from: _('/node_modules/test-package/src/b.js'),
|
|
|
|
dtsFrom: null,
|
|
|
|
alias: null
|
|
|
|
},
|
2019-06-06 15:22:32 -04:00
|
|
|
{
|
|
|
|
identifier: 'InternalComponent1',
|
2019-05-25 16:34:40 -04:00
|
|
|
from: _('/node_modules/test-package/src/c.js'),
|
|
|
|
dtsFrom: _('/node_modules/test-package/typings/c.d.ts'),
|
2019-06-06 15:22:32 -04:00
|
|
|
alias: null
|
|
|
|
},
|
|
|
|
]);
|
|
|
|
});
|
|
|
|
|
|
|
|
it('should find all non-public declarations that were aliased', () => {
|
|
|
|
const _ = absoluteFrom;
|
|
|
|
const ALIASED_EXPORTS_PROGRAM = [
|
|
|
|
{
|
2019-05-25 16:34:40 -04:00
|
|
|
name: _('/node_modules/test-package/src/entry_point.js'),
|
2019-06-06 15:22:32 -04:00
|
|
|
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';
|
|
|
|
`
|
|
|
|
},
|
|
|
|
{
|
2019-05-25 16:34:40 -04:00
|
|
|
name: _('/node_modules/test-package/src/a.js'),
|
2019-06-06 15:22:32 -04:00
|
|
|
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 = [
|
|
|
|
{
|
2019-05-25 16:34:40 -04:00
|
|
|
name: _('/node_modules/test-package/typings/entry_point.d.ts'),
|
2019-06-06 15:22:32 -04:00
|
|
|
isRoot: true,
|
|
|
|
contents: `
|
|
|
|
export declare class aliasedComponentOne {}
|
|
|
|
export declare class ComponentTwo {}
|
|
|
|
export {ComponentTwo as aliasedComponentTwo}
|
|
|
|
`
|
|
|
|
},
|
|
|
|
];
|
|
|
|
const {program, referencesRegistry, analyzer} =
|
|
|
|
setup(ALIASED_EXPORTS_PROGRAM, ALIASED_EXPORTS_DTS_PROGRAM);
|
|
|
|
|
2019-05-25 16:34:40 -04:00
|
|
|
addToReferencesRegistry(
|
|
|
|
program, referencesRegistry, _('/node_modules/test-package/src/a.js'), 'ComponentOne');
|
|
|
|
addToReferencesRegistry(
|
|
|
|
program, referencesRegistry, _('/node_modules/test-package/src/a.js'), 'ComponentTwo');
|
2019-06-06 15:22:32 -04:00
|
|
|
|
|
|
|
const analyses = analyzer.analyzeProgram(program);
|
|
|
|
expect(analyses).toEqual([{
|
|
|
|
identifier: 'ComponentOne',
|
2019-05-25 16:34:40 -04:00
|
|
|
from: _('/node_modules/test-package/src/a.js'),
|
2019-06-06 15:22:32 -04:00
|
|
|
dtsFrom: null,
|
|
|
|
alias: 'aliasedComponentOne',
|
|
|
|
}]);
|
|
|
|
});
|
2019-02-14 12:59:46 -05:00
|
|
|
});
|
2018-11-21 04:05:27 -05:00
|
|
|
});
|
2019-02-14 12:59:46 -05:00
|
|
|
|
2019-06-06 15:22:32 -04:00
|
|
|
function setup(jsProgram: TestFile[], dtsProgram: TestFile[]) {
|
|
|
|
loadTestFiles(jsProgram);
|
|
|
|
loadTestFiles(dtsProgram);
|
|
|
|
const {src: {program}, dts} = makeTestEntryPointBundle(
|
2019-08-07 19:19:52 -04:00
|
|
|
'test-package', 'esm2015', false, getRootFiles(jsProgram), getRootFiles(dtsProgram));
|
2019-06-06 15:22:32 -04:00
|
|
|
const host = new Esm2015ReflectionHost(new MockLogger(), false, program.getTypeChecker(), dts);
|
|
|
|
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: AbsoluteFsPath,
|
|
|
|
componentName: string) {
|
|
|
|
const declaration = getDeclaration(program, fileName, componentName, ts.isClassDeclaration);
|
|
|
|
registry.add(null !, new Reference(declaration));
|
|
|
|
}
|
|
|
|
});
|