fix(ngcc): use the correct identifiers when updating typings files (#34254)

Previously the identifiers used in the typings files were the same as
those used in the source files.

When the typings files and the source files do not match exactly, e.g.
when one of them is flattened, while the other is a deep tree, it is
possible for identifiers to be renamed.

This commit ensures that the correct identifier is used in typings files
when the typings file does not export the same name as the source file.

Fixes https://github.com/angular/ngcc-validation/pull/608

PR Close #34254
This commit is contained in:
Pete Bacon Darwin
2019-12-18 14:03:05 +00:00
committed by Kara Erickson
parent f22a6eb00e
commit 31be29a9f3
14 changed files with 94 additions and 39 deletions

View File

@ -235,6 +235,34 @@ runInEachFileSystem(() => {
`TestClass.ɵprov = ɵngcc0.ɵɵdefineInjectable({`);
});
it('should use the correct type name in typings files when an export has a different name in source files',
() => {
// We need to make sure that changes to the typings files use the correct name
// static ɵprov: ɵngcc0.ɵɵInjectableDef<ɵangular_packages_common_common_a>;
mainNgcc({
basePath: '/node_modules',
targetEntryPointPath: '@angular/common',
propertiesToConsider: ['esm2015']
});
// In `@angular/common` the `NgClassR3Impl` class gets exported as something like
// `ɵangular_packages_common_common_a`.
const jsContents = fs.readFile(_(`/node_modules/@angular/common/fesm2015/common.js`));
const exportedNameMatch = jsContents.match(/export.* NgClassR3Impl as ([^ ,}]+)/);
if (exportedNameMatch === null) {
return fail(
'Expected `/node_modules/@angular/common/fesm2015/common.js` to export `NgClassR3Impl` via an alias');
}
const exportedName = exportedNameMatch[1];
// We need to make sure that the flat typings file exports this directly
const dtsContents = fs.readFile(_('/node_modules/@angular/common/common.d.ts'));
expect(dtsContents)
.toContain(`export declare class ${exportedName} implements ɵNgClassImpl`);
// And that ngcc's modifications to that class use the correct (exported) name
expect(dtsContents).toContain(`static ɵprov: ɵngcc0.ɵɵInjectableDef<${exportedName}>`);
});
it('should add generic type for ModuleWithProviders and generate exports for private modules',
() => {
compileIntoApf('test-package', {

View File

@ -20,7 +20,7 @@ import {AnalysisOutput, CompileResult, DecoratorHandler, DetectResult, HandlerFl
import {compileNgFactoryDefField} from './factory';
import {generateSetClassMetadataCall} from './metadata';
import {findAngularDecorator, getConstructorDependencies, isAngularDecorator, makeDuplicateDeclarationError, readBaseClass, unwrapConstructorDependencies, unwrapExpression, unwrapForwardRef, validateConstructorDependencies, wrapFunctionExpressionsInParens} from './util';
import {findAngularDecorator, getConstructorDependencies, isAngularDecorator, makeDuplicateDeclarationError, readBaseClass, unwrapConstructorDependencies, unwrapExpression, unwrapForwardRef, validateConstructorDependencies, wrapFunctionExpressionsInParens, wrapTypeReference} from './util';
const EMPTY_OBJECT: {[key: string]: string} = {};
const FIELD_DECORATORS = [
@ -292,6 +292,9 @@ export function extractDirectiveMetadata(
// Detect if the component inherits from another class
const usesInheritance = reflector.hasBaseClass(clazz);
const type = wrapTypeReference(reflector, clazz);
const internalType = new WrappedNodeExpr(reflector.getInternalNameOfClass(clazz));
const metadata: R3DirectiveMetadata = {
name: clazz.name.text,
deps: ctorDeps, host,
@ -300,9 +303,7 @@ export function extractDirectiveMetadata(
},
inputs: {...inputsFromMeta, ...inputsFromFields},
outputs: {...outputsFromMeta, ...outputsFromFields}, queries, viewQueries, selector,
fullInheritance: !!(flags & HandlerFlags.FULL_INHERITANCE),
type: new WrappedNodeExpr(clazz.name),
internalType: new WrappedNodeExpr(reflector.getInternalNameOfClass(clazz)),
fullInheritance: !!(flags & HandlerFlags.FULL_INHERITANCE), type, internalType,
typeArgumentCount: reflector.getGenericArityOfClass(clazz) || 0,
typeSourceSpan: EMPTY_SOURCE_SPAN, usesInheritance, exportAs, providers
};

View File

@ -16,7 +16,7 @@ import {AnalysisOutput, CompileResult, DecoratorHandler, DetectResult, HandlerPr
import {compileNgFactoryDefField} from './factory';
import {generateSetClassMetadataCall} from './metadata';
import {findAngularDecorator, getConstructorDependencies, getValidConstructorDependencies, isAngularCore, unwrapConstructorDependencies, unwrapForwardRef, validateConstructorDependencies} from './util';
import {findAngularDecorator, getConstructorDependencies, getValidConstructorDependencies, isAngularCore, unwrapConstructorDependencies, unwrapForwardRef, validateConstructorDependencies, wrapTypeReference} from './util';
export interface InjectableHandlerData {
meta: R3InjectableMetadata;
@ -130,7 +130,7 @@ function extractInjectableMetadata(
clazz: ClassDeclaration, decorator: Decorator,
reflector: ReflectionHost): R3InjectableMetadata {
const name = clazz.name.text;
const type = new WrappedNodeExpr(clazz.name);
const type = wrapTypeReference(reflector, clazz);
const internalType = new WrappedNodeExpr(reflector.getInternalNameOfClass(clazz));
const typeArgumentCount = reflector.getGenericArityOfClass(clazz) || 0;
if (decorator.args === null) {

View File

@ -22,7 +22,7 @@ import {getSourceFile} from '../../util/src/typescript';
import {generateSetClassMetadataCall} from './metadata';
import {ReferencesRegistry} from './references_registry';
import {combineResolvers, findAngularDecorator, forwardRefResolver, getReferenceOriginForDiagnostics, getValidConstructorDependencies, isExpressionForwardReference, toR3Reference, unwrapExpression, wrapFunctionExpressionsInParens} from './util';
import {combineResolvers, findAngularDecorator, forwardRefResolver, getReferenceOriginForDiagnostics, getValidConstructorDependencies, isExpressionForwardReference, toR3Reference, unwrapExpression, wrapFunctionExpressionsInParens, wrapTypeReference} from './util';
export interface NgModuleAnalysis {
mod: R3NgModuleMetadata;
@ -220,10 +220,14 @@ export class NgModuleDecoratorHandler implements
declarations.some(isForwardReference) || imports.some(isForwardReference) ||
exports.some(isForwardReference);
const type = wrapTypeReference(this.reflector, node);
const internalType = new WrappedNodeExpr(this.reflector.getInternalNameOfClass(node));
const adjacentType = new WrappedNodeExpr(this.reflector.getAdjacentNameOfClass(node));
const ngModuleDef: R3NgModuleMetadata = {
type: new WrappedNodeExpr(node.name),
internalType: new WrappedNodeExpr(this.reflector.getInternalNameOfClass(node)),
adjacentType: new WrappedNodeExpr(this.reflector.getAdjacentNameOfClass(node)),
type,
internalType,
adjacentType,
bootstrap,
declarations,
exports,
@ -256,8 +260,8 @@ export class NgModuleDecoratorHandler implements
const ngInjectorDef: R3InjectorMetadata = {
name,
type: new WrappedNodeExpr(node.name),
internalType: new WrappedNodeExpr(this.reflector.getInternalNameOfClass(node)),
type,
internalType,
deps: getValidConstructorDependencies(
node, this.reflector, this.defaultImportRecorder, this.isCore),
providers,

View File

@ -19,7 +19,7 @@ import {AnalysisOutput, CompileResult, DecoratorHandler, DetectResult, HandlerPr
import {compileNgFactoryDefField} from './factory';
import {generateSetClassMetadataCall} from './metadata';
import {findAngularDecorator, getValidConstructorDependencies, makeDuplicateDeclarationError, unwrapExpression} from './util';
import {findAngularDecorator, getValidConstructorDependencies, makeDuplicateDeclarationError, unwrapExpression, wrapTypeReference} from './util';
export interface PipeHandlerData {
meta: R3PipeMetadata;
@ -53,8 +53,9 @@ export class PipeDecoratorHandler implements DecoratorHandler<Decorator, PipeHan
analyze(clazz: ClassDeclaration, decorator: Readonly<Decorator>):
AnalysisOutput<PipeHandlerData> {
const name = clazz.name.text;
const type = new WrappedNodeExpr(clazz.name);
const type = wrapTypeReference(this.reflector, clazz);
const internalType = new WrappedNodeExpr(this.reflector.getInternalNameOfClass(clazz));
if (decorator.args === null) {
throw new FatalDiagnosticError(
ErrorCode.DECORATOR_NOT_CALLED, Decorator.nodeForError(decorator),

View File

@ -435,3 +435,18 @@ export function makeDuplicateDeclarationError(
ErrorCode.NGMODULE_DECLARATION_NOT_UNIQUE, node.name,
`The ${kind} '${node.name.text}' is declared by more than one NgModule.`, context);
}
/**
* Create an R3Reference for a class.
*
* The `value` is the exported declaration of the class from its source file.
* The `type` is an expression that would be used by ngcc in the typings (.d.ts) files.
*/
export function wrapTypeReference(reflector: ReflectionHost, clazz: ClassDeclaration): R3Reference {
const dtsClass = reflector.getDtsDeclaration(clazz);
const value = new WrappedNodeExpr(clazz.name);
const type = dtsClass !== null && isNamedClassDeclaration(dtsClass) ?
new WrappedNodeExpr(dtsClass.name) :
value;
return {value, type};
}