angular-cn/packages/compiler-cli/ngcc/test/analysis/private_declarations_analyz...

258 lines
9.4 KiB
TypeScript

/**
* @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';
import {AbsoluteFsPath, absoluteFrom} from '../../../src/ngtsc/file_system';
import {TestFile, runInEachFileSystem} from '../../../src/ngtsc/file_system/testing';
import {Reference} from '../../../src/ngtsc/imports';
import {getDeclaration} from '../../../src/ngtsc/testing';
import {loadTestFiles} from '../../../test/helpers/src/mock_file_loading';
import {NgccReferencesRegistry} from '../../src/analysis/ngcc_references_registry';
import {PrivateDeclarationsAnalyzer} from '../../src/analysis/private_declarations_analyzer';
import {Esm2015ReflectionHost} from '../../src/host/esm2015_host';
import {MockLogger} from '../helpers/mock_logger';
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[] = [
{
name: _('/node_modules/test-package/src/entry_point.js'),
isRoot: true,
contents: `
export {PublicComponent} from './a';
export {ModuleA} from './mod';
export {ModuleB} from './b';
`
},
{
name: _('/node_modules/test-package/src/a.js'),
isRoot: false,
contents: `
import {Component} from '@angular/core';
export class PublicComponent {}
PublicComponent.decorators = [
{type: Component, args: [{selectors: 'a', template: ''}]}
];
`
},
{
name: _('/node_modules/test-package/src/b.js'),
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]}]}
];
`
},
{
name: _('/node_modules/test-package/src/c.js'),
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: ''}]}
];
`
},
{
name: _('/node_modules/test-package/src/mod.js'),
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 = [
{
name: _('/node_modules/test-package/typings/entry_point.d.ts'),
isRoot: true,
contents: `
export {PublicComponent} from './a';
export {ModuleA} from './mod';
export {ModuleB} from './b';
`
},
{
name: _('/node_modules/test-package/typings/a.d.ts'),
isRoot: false,
contents: `
export declare class PublicComponent {}
`
},
{
name: _('/node_modules/test-package/typings/b.d.ts'),
isRoot: false,
contents: `
export declare class ModuleB {}
`
},
{
name: _('/node_modules/test-package/typings/c.d.ts'),
isRoot: false,
contents: `
export declare class InternalComponent1 {}
`
},
{
name: _('/node_modules/test-package/typings/mod.d.ts'),
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(
program, referencesRegistry, _('/node_modules/test-package/src/a.js'),
'PublicComponent');
addToReferencesRegistry(
program, referencesRegistry, _('/node_modules/test-package/src/b.js'),
'PrivateComponent1');
addToReferencesRegistry(
program, referencesRegistry, _('/node_modules/test-package/src/c.js'),
'InternalComponent1');
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([
{
identifier: 'PrivateComponent1',
from: _('/node_modules/test-package/src/b.js'),
dtsFrom: null,
alias: null
},
{
identifier: 'InternalComponent1',
from: _('/node_modules/test-package/src/c.js'),
dtsFrom: _('/node_modules/test-package/typings/c.d.ts'),
alias: null
},
]);
});
it('should find all non-public declarations that were aliased', () => {
const _ = absoluteFrom;
const ALIASED_EXPORTS_PROGRAM = [
{
name: _('/node_modules/test-package/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: _('/node_modules/test-package/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: _('/node_modules/test-package/typings/entry_point.d.ts'),
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);
addToReferencesRegistry(
program, referencesRegistry, _('/node_modules/test-package/src/a.js'), 'ComponentOne');
addToReferencesRegistry(
program, referencesRegistry, _('/node_modules/test-package/src/a.js'), 'ComponentTwo');
const analyses = analyzer.analyzeProgram(program);
expect(analyses).toEqual([{
identifier: 'ComponentOne',
from: _('/node_modules/test-package/src/a.js'),
dtsFrom: null,
alias: 'aliasedComponentOne',
}]);
});
});
});
function setup(jsProgram: TestFile[], dtsProgram: TestFile[]) {
loadTestFiles(jsProgram);
loadTestFiles(dtsProgram);
const {src: {program}, dts} = makeTestEntryPointBundle(
'test-package', 'esm2015', false, getRootFiles(jsProgram), getRootFiles(dtsProgram));
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));
}
});