2019-04-28 15:48:35 -04: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 MagicString from 'magic-string';
|
|
|
|
import * as ts from 'typescript';
|
2020-04-06 03:30:08 -04:00
|
|
|
|
2019-06-06 15:22:32 -04:00
|
|
|
import {absoluteFrom, getFileSystem} from '../../../src/ngtsc/file_system';
|
2020-04-06 03:30:08 -04:00
|
|
|
import {runInEachFileSystem, TestFile} from '../../../src/ngtsc/file_system/testing';
|
2019-10-14 16:04:42 -04:00
|
|
|
import {Reexport} from '../../../src/ngtsc/imports';
|
2019-04-28 15:48:35 -04:00
|
|
|
import {Import, ImportManager} from '../../../src/ngtsc/translator';
|
2020-04-06 03:30:08 -04:00
|
|
|
import {loadTestFiles} from '../../../test/helpers';
|
2019-07-18 16:05:32 -04:00
|
|
|
import {DecorationAnalyzer} from '../../src/analysis/decoration_analyzer';
|
2019-04-28 15:48:35 -04:00
|
|
|
import {ModuleWithProvidersAnalyzer, ModuleWithProvidersInfo} from '../../src/analysis/module_with_providers_analyzer';
|
2020-04-06 03:30:08 -04:00
|
|
|
import {NgccReferencesRegistry} from '../../src/analysis/ngcc_references_registry';
|
|
|
|
import {ExportInfo, PrivateDeclarationsAnalyzer} from '../../src/analysis/private_declarations_analyzer';
|
|
|
|
import {CompiledClass} from '../../src/analysis/types';
|
2019-04-28 15:48:35 -04:00
|
|
|
import {Esm2015ReflectionHost} from '../../src/host/esm2015_host';
|
|
|
|
import {DtsRenderer} from '../../src/rendering/dts_renderer';
|
2020-04-06 03:30:08 -04:00
|
|
|
import {RedundantDecoratorMap, RenderingFormatter} from '../../src/rendering/rendering_formatter';
|
2019-04-28 15:48:35 -04:00
|
|
|
import {MockLogger} from '../helpers/mock_logger';
|
2020-04-06 03:30:08 -04:00
|
|
|
import {getRootFiles, makeTestEntryPointBundle} from '../helpers/utils';
|
2019-04-28 15:48:35 -04:00
|
|
|
|
|
|
|
class TestRenderingFormatter implements RenderingFormatter {
|
|
|
|
addImports(output: MagicString, imports: Import[], sf: ts.SourceFile) {
|
|
|
|
output.prepend('\n// ADD IMPORTS\n');
|
|
|
|
}
|
|
|
|
addExports(output: MagicString, baseEntryPointPath: string, exports: ExportInfo[]) {
|
|
|
|
output.prepend('\n// ADD EXPORTS\n');
|
|
|
|
}
|
2019-10-14 16:04:42 -04:00
|
|
|
addDirectExports(output: MagicString, exports: Reexport[]) {
|
|
|
|
output.prepend('\n// ADD DIRECT EXPORTS\n');
|
|
|
|
}
|
2019-04-28 15:48:35 -04:00
|
|
|
addConstants(output: MagicString, constants: string, file: ts.SourceFile): void {
|
|
|
|
output.prepend('\n// ADD CONSTANTS\n');
|
|
|
|
}
|
|
|
|
addDefinitions(output: MagicString, compiledClass: CompiledClass, definitions: string) {
|
|
|
|
output.prepend('\n// ADD DEFINITIONS\n');
|
|
|
|
}
|
2019-11-06 12:03:56 -05:00
|
|
|
addAdjacentStatements(output: MagicString, compiledClass: CompiledClass, statements: string) {
|
|
|
|
output.prepend('\n// ADD ADJACENT STATEMENTS\n');
|
|
|
|
}
|
2019-04-28 15:48:35 -04:00
|
|
|
removeDecorators(output: MagicString, decoratorsToRemove: RedundantDecoratorMap) {
|
|
|
|
output.prepend('\n// REMOVE DECORATORS\n');
|
|
|
|
}
|
|
|
|
rewriteSwitchableDeclarations(output: MagicString, sourceFile: ts.SourceFile): void {
|
|
|
|
output.prepend('\n// REWRITTEN DECLARATIONS\n');
|
|
|
|
}
|
|
|
|
addModuleWithProvidersParams(
|
|
|
|
output: MagicString, moduleWithProviders: ModuleWithProvidersInfo[],
|
|
|
|
importManager: ImportManager): void {
|
|
|
|
output.prepend('\n// ADD MODUlE WITH PROVIDERS PARAMS\n');
|
|
|
|
}
|
2020-04-06 03:30:08 -04:00
|
|
|
printStatement(): string {
|
|
|
|
return 'IGNORED';
|
|
|
|
}
|
2019-04-28 15:48:35 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
function createTestRenderer(
|
2019-06-06 15:22:32 -04:00
|
|
|
packageName: string, files: TestFile[], dtsFiles?: TestFile[], mappingFiles?: TestFile[]) {
|
2019-04-28 15:48:35 -04:00
|
|
|
const logger = new MockLogger();
|
2019-06-06 15:22:32 -04:00
|
|
|
loadTestFiles(files);
|
|
|
|
if (dtsFiles) {
|
|
|
|
loadTestFiles(dtsFiles);
|
|
|
|
}
|
|
|
|
if (mappingFiles) {
|
|
|
|
loadTestFiles(mappingFiles);
|
|
|
|
}
|
|
|
|
const fs = getFileSystem();
|
2019-04-28 15:48:35 -04:00
|
|
|
const isCore = packageName === '@angular/core';
|
2019-06-06 15:22:32 -04:00
|
|
|
const bundle = makeTestEntryPointBundle(
|
2019-08-07 19:19:52 -04:00
|
|
|
'test-package', 'esm2015', isCore, getRootFiles(files), dtsFiles && getRootFiles(dtsFiles));
|
2019-12-18 09:03:04 -05:00
|
|
|
const host = new Esm2015ReflectionHost(logger, isCore, bundle.src, bundle.dts);
|
2019-04-28 15:48:35 -04:00
|
|
|
const referencesRegistry = new NgccReferencesRegistry(host);
|
2019-05-25 15:53:52 -04:00
|
|
|
const decorationAnalyses =
|
|
|
|
new DecorationAnalyzer(fs, bundle, host, referencesRegistry).analyzeProgram();
|
2019-04-28 15:48:35 -04:00
|
|
|
const moduleWithProvidersAnalyses =
|
2019-11-16 15:12:58 -05:00
|
|
|
new ModuleWithProvidersAnalyzer(host, referencesRegistry, true)
|
|
|
|
.analyzeProgram(bundle.src.program);
|
2019-04-28 15:48:35 -04:00
|
|
|
const privateDeclarationsAnalyses =
|
|
|
|
new PrivateDeclarationsAnalyzer(host, referencesRegistry).analyzeProgram(bundle.src.program);
|
|
|
|
const testFormatter = new TestRenderingFormatter();
|
|
|
|
spyOn(testFormatter, 'addExports').and.callThrough();
|
|
|
|
spyOn(testFormatter, 'addImports').and.callThrough();
|
|
|
|
spyOn(testFormatter, 'addDefinitions').and.callThrough();
|
2019-11-06 12:03:56 -05:00
|
|
|
spyOn(testFormatter, 'addAdjacentStatements').and.callThrough();
|
2019-04-28 15:48:35 -04:00
|
|
|
spyOn(testFormatter, 'addConstants').and.callThrough();
|
|
|
|
spyOn(testFormatter, 'removeDecorators').and.callThrough();
|
|
|
|
spyOn(testFormatter, 'rewriteSwitchableDeclarations').and.callThrough();
|
|
|
|
spyOn(testFormatter, 'addModuleWithProvidersParams').and.callThrough();
|
2019-11-04 12:29:01 -05:00
|
|
|
spyOn(testFormatter, 'printStatement').and.callThrough();
|
2019-04-28 15:48:35 -04:00
|
|
|
|
2019-05-25 15:38:33 -04:00
|
|
|
const renderer = new DtsRenderer(testFormatter, fs, logger, host, bundle);
|
2019-04-28 15:48:35 -04:00
|
|
|
|
2020-04-06 03:30:08 -04:00
|
|
|
return {
|
|
|
|
renderer,
|
|
|
|
testFormatter,
|
|
|
|
decorationAnalyses,
|
|
|
|
moduleWithProvidersAnalyses,
|
|
|
|
privateDeclarationsAnalyses,
|
|
|
|
bundle
|
|
|
|
};
|
2019-04-28 15:48:35 -04:00
|
|
|
}
|
|
|
|
|
2019-06-06 15:22:32 -04:00
|
|
|
runInEachFileSystem(() => {
|
|
|
|
describe('DtsRenderer', () => {
|
|
|
|
let _: typeof absoluteFrom;
|
|
|
|
let INPUT_PROGRAM: TestFile;
|
|
|
|
let INPUT_DTS_PROGRAM: TestFile;
|
|
|
|
|
|
|
|
beforeEach(() => {
|
|
|
|
_ = absoluteFrom;
|
|
|
|
INPUT_PROGRAM = {
|
2019-05-25 16:34:40 -04:00
|
|
|
name: _('/node_modules/test-package/src/file.js'),
|
2019-06-06 15:22:32 -04:00
|
|
|
contents:
|
|
|
|
`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`
|
|
|
|
};
|
|
|
|
INPUT_DTS_PROGRAM = {
|
2019-12-18 09:03:05 -05:00
|
|
|
name: _('/node_modules/test-package/typings/file.d.ts'),
|
2019-06-06 15:22:32 -04:00
|
|
|
contents: `export declare class A {\nfoo(x: number): number;\n}\n`
|
|
|
|
};
|
|
|
|
});
|
|
|
|
|
|
|
|
it('should render extract types into typings files', () => {
|
2020-04-06 03:30:08 -04:00
|
|
|
const {
|
|
|
|
renderer,
|
|
|
|
decorationAnalyses,
|
|
|
|
privateDeclarationsAnalyses,
|
|
|
|
moduleWithProvidersAnalyses
|
|
|
|
} = createTestRenderer('test-package', [INPUT_PROGRAM], [INPUT_DTS_PROGRAM]);
|
2019-06-06 15:22:32 -04:00
|
|
|
const result = renderer.renderProgram(
|
|
|
|
decorationAnalyses, privateDeclarationsAnalyses, moduleWithProvidersAnalyses);
|
|
|
|
|
2019-12-18 09:03:05 -05:00
|
|
|
const typingsFile =
|
2020-04-06 03:30:08 -04:00
|
|
|
result.find(f => f.path === _('/node_modules/test-package/typings/file.d.ts'))!;
|
2019-06-06 15:22:32 -04:00
|
|
|
expect(typingsFile.contents)
|
|
|
|
.toContain(
|
feat(compiler): add dependency info and ng-content selectors to metadata (#35695)
This commit augments the `FactoryDef` declaration of Angular decorated
classes to contain information about the parameter decorators used in
the constructor. If no constructor is present, or none of the parameters
have any Angular decorators, then this will be represented using the
`null` type. Otherwise, a tuple type is used where the entry at index `i`
corresponds with parameter `i`. Each tuple entry can be one of two types:
1. If the associated parameter does not have any Angular decorators,
the tuple entry will be the `null` type.
2. Otherwise, a type literal is used that may declare at least one of
the following properties:
- "attribute": if `@Attribute` is present. The injected attribute's
name is used as string literal type, or the `unknown` type if the
attribute name is not a string literal.
- "self": if `@Self` is present, always of type `true`.
- "skipSelf": if `@SkipSelf` is present, always of type `true`.
- "host": if `@Host` is present, always of type `true`.
- "optional": if `@Optional` is present, always of type `true`.
A property is only present if the corresponding decorator is used.
Note that the `@Inject` decorator is currently not included, as it's
non-trivial to properly convert the token's value expression to a
type that is valid in a declaration file.
Additionally, the `ComponentDefWithMeta` declaration that is created for
Angular components has been extended to include all selectors on
`ng-content` elements within the component's template.
This additional metadata is useful for tooling such as the Angular
Language Service, as it provides the ability to offer suggestions for
directives/components defined in libraries. At the moment, such
tooling extracts the necessary information from the _metadata.json_
manifest file as generated by ngc, however this metadata representation
is being replaced by the information emitted into the declaration files.
Resolves FW-1870
PR Close #35695
2020-02-26 16:05:44 -05:00
|
|
|
'foo(x: number): number;\n static ɵfac: ɵngcc0.ɵɵFactoryDef<A, never>;\n static ɵdir: ɵngcc0.ɵɵDirectiveDefWithMeta');
|
2019-06-06 15:22:32 -04:00
|
|
|
});
|
|
|
|
|
|
|
|
it('should render imports into typings files', () => {
|
2020-04-06 03:30:08 -04:00
|
|
|
const {
|
|
|
|
renderer,
|
|
|
|
decorationAnalyses,
|
|
|
|
privateDeclarationsAnalyses,
|
|
|
|
moduleWithProvidersAnalyses
|
|
|
|
} = createTestRenderer('test-package', [INPUT_PROGRAM], [INPUT_DTS_PROGRAM]);
|
2019-06-06 15:22:32 -04:00
|
|
|
const result = renderer.renderProgram(
|
|
|
|
decorationAnalyses, privateDeclarationsAnalyses, moduleWithProvidersAnalyses);
|
|
|
|
|
2019-12-18 09:03:05 -05:00
|
|
|
const typingsFile =
|
2020-04-06 03:30:08 -04:00
|
|
|
result.find(f => f.path === _('/node_modules/test-package/typings/file.d.ts'))!;
|
2019-06-06 15:22:32 -04:00
|
|
|
expect(typingsFile.contents).toContain(`\n// ADD IMPORTS\n`);
|
|
|
|
});
|
|
|
|
|
|
|
|
it('should render exports into typings files', () => {
|
2020-04-06 03:30:08 -04:00
|
|
|
const {
|
|
|
|
renderer,
|
|
|
|
decorationAnalyses,
|
|
|
|
privateDeclarationsAnalyses,
|
|
|
|
moduleWithProvidersAnalyses
|
|
|
|
} = createTestRenderer('test-package', [INPUT_PROGRAM], [INPUT_DTS_PROGRAM]);
|
2019-06-06 15:22:32 -04:00
|
|
|
|
|
|
|
// Add a mock export to trigger export rendering
|
2019-05-25 16:34:40 -04:00
|
|
|
privateDeclarationsAnalyses.push({
|
|
|
|
identifier: 'ComponentB',
|
|
|
|
from: _('/node_modules/test-package/src/file.js'),
|
|
|
|
dtsFrom: _('/typings/b.d.ts')
|
|
|
|
});
|
2019-06-06 15:22:32 -04:00
|
|
|
|
|
|
|
const result = renderer.renderProgram(
|
|
|
|
decorationAnalyses, privateDeclarationsAnalyses, moduleWithProvidersAnalyses);
|
|
|
|
|
2019-12-18 09:03:05 -05:00
|
|
|
const typingsFile =
|
2020-04-06 03:30:08 -04:00
|
|
|
result.find(f => f.path === _('/node_modules/test-package/typings/file.d.ts'))!;
|
2019-06-06 15:22:32 -04:00
|
|
|
expect(typingsFile.contents).toContain(`\n// ADD EXPORTS\n`);
|
|
|
|
});
|
|
|
|
|
|
|
|
it('should render ModuleWithProviders type params', () => {
|
2020-04-06 03:30:08 -04:00
|
|
|
const {
|
|
|
|
renderer,
|
|
|
|
decorationAnalyses,
|
|
|
|
privateDeclarationsAnalyses,
|
|
|
|
moduleWithProvidersAnalyses
|
|
|
|
} = createTestRenderer('test-package', [INPUT_PROGRAM], [INPUT_DTS_PROGRAM]);
|
2019-06-06 15:22:32 -04:00
|
|
|
|
|
|
|
const result = renderer.renderProgram(
|
|
|
|
decorationAnalyses, privateDeclarationsAnalyses, moduleWithProvidersAnalyses);
|
|
|
|
|
2019-12-18 09:03:05 -05:00
|
|
|
const typingsFile =
|
2020-04-06 03:30:08 -04:00
|
|
|
result.find(f => f.path === _('/node_modules/test-package/typings/file.d.ts'))!;
|
2019-06-06 15:22:32 -04:00
|
|
|
expect(typingsFile.contents).toContain(`\n// ADD MODUlE WITH PROVIDERS PARAMS\n`);
|
|
|
|
});
|
2019-04-28 15:48:35 -04:00
|
|
|
});
|
|
|
|
});
|