refactor(compiler-cli): implement DeclarationNode node type (#38959)

Previously the `ConcreteDeclaration` and `InlineDeclaration` had
different properties for the underlying node type. And the `InlineDeclaration`
did not store a value that represented its declaration.

It turns out that a natural declaration node for an inline type is the
expression. For example in UMD/CommonJS this would be the `exports.<name>`
property access node.

So this expression is now used for the `node` of `InlineDeclaration` types
and the `expression` property is dropped.

To support this the codebase has been refactored to use a new `DeclarationNode`
type which is a union of `ts.Declaration|ts.Expression` instead of `ts.Declaration`
throughout.

PR Close #38959
This commit is contained in:
Pete Bacon Darwin
2020-09-29 20:42:20 +01:00
committed by atscott
parent 2c0282f4c2
commit 0accd1e68d
54 changed files with 276 additions and 255 deletions

View File

@ -17,7 +17,7 @@ import {DependencyTracker} from '../../incremental/api';
import {IndexingContext} from '../../indexer';
import {ClassPropertyMapping, DirectiveMeta, DirectiveTypeCheckMeta, extractDirectiveTypeCheckMeta, InjectableClassRegistry, MetadataReader, MetadataRegistry, TemplateMapping} from '../../metadata';
import {EnumValue, PartialEvaluator} from '../../partial_evaluator';
import {ClassDeclaration, Decorator, ReflectionHost, reflectObjectLiteral} from '../../reflection';
import {ClassDeclaration, DeclarationNode, Decorator, ReflectionHost, reflectObjectLiteral} from '../../reflection';
import {ComponentScopeReader, LocalModuleScopeRegistry} from '../../scope';
import {AnalysisOutput, CompileResult, DecoratorHandler, DetectResult, HandlerFlags, HandlerPrecedence, ResolveResult} from '../../transform';
import {TemplateSourceMapping, TypeCheckContext} from '../../typecheck/api';
@ -103,7 +103,7 @@ export class ComponentDecoratorHandler implements
* any potential <link> tags which might need to be loaded. This cache ensures that work is not
* thrown away, and the parsed template is reused during the analyze phase.
*/
private preanalyzeTemplateCache = new Map<ts.Declaration, ParsedTemplateWithSource>();
private preanalyzeTemplateCache = new Map<DeclarationNode, ParsedTemplateWithSource>();
readonly precedence = HandlerPrecedence.PRIMARY;
readonly name = ComponentDecoratorHandler.name;

View File

@ -10,7 +10,7 @@ import {Expression, ExternalExpr, FunctionExpr, Identifiers, InvokeFunctionExpr,
import * as ts from 'typescript';
import {DefaultImportRecorder} from '../../imports';
import {CtorParameter, Decorator, ReflectionHost, TypeValueReferenceKind} from '../../reflection';
import {CtorParameter, DeclarationNode, Decorator, ReflectionHost, TypeValueReferenceKind} from '../../reflection';
import {valueReferenceToExpression, wrapFunctionExpressionsInParens} from './util';
@ -23,8 +23,9 @@ import {valueReferenceToExpression, wrapFunctionExpressionsInParens} from './uti
* as a `Statement` for inclusion along with the class.
*/
export function generateSetClassMetadataCall(
clazz: ts.Declaration, reflection: ReflectionHost, defaultImportRecorder: DefaultImportRecorder,
isCore: boolean, annotateForClosureCompiler?: boolean): Statement|null {
clazz: DeclarationNode, reflection: ReflectionHost,
defaultImportRecorder: DefaultImportRecorder, isCore: boolean,
annotateForClosureCompiler?: boolean): Statement|null {
if (!reflection.isClass(clazz)) {
return null;
}

View File

@ -13,7 +13,7 @@ import {ErrorCode, FatalDiagnosticError, makeDiagnostic, makeRelatedInformation}
import {DefaultImportRecorder, Reference, ReferenceEmitter} from '../../imports';
import {InjectableClassRegistry, MetadataReader, MetadataRegistry} from '../../metadata';
import {PartialEvaluator, ResolvedValue} from '../../partial_evaluator';
import {ClassDeclaration, Decorator, ReflectionHost, reflectObjectLiteral, typeNodeToValueExpr} from '../../reflection';
import {ClassDeclaration, DeclarationNode, Decorator, isNamedClassDeclaration, ReflectionHost, reflectObjectLiteral, typeNodeToValueExpr} from '../../reflection';
import {NgModuleRouteAnalyzer} from '../../routing';
import {LocalModuleScopeRegistry, ScopeData} from '../../scope';
import {FactoryTracker} from '../../shims/api';
@ -434,14 +434,14 @@ export class NgModuleDecoratorHandler implements
}
private _toR3Reference(
valueRef: Reference<ts.Declaration>, valueContext: ts.SourceFile,
valueRef: Reference<ClassDeclaration>, valueContext: ts.SourceFile,
typeContext: ts.SourceFile): R3Reference {
if (valueRef.hasOwningModuleGuess) {
return toR3Reference(valueRef, valueRef, valueContext, valueContext, this.refEmitter);
} else {
let typeRef = valueRef;
let typeNode = this.reflector.getDtsDeclaration(typeRef.node);
if (typeNode !== null && ts.isClassDeclaration(typeNode)) {
if (typeNode !== null && isNamedClassDeclaration(typeNode)) {
typeRef = new Reference(typeNode);
}
return toR3Reference(valueRef, typeRef, valueContext, typeContext, this.refEmitter);
@ -539,7 +539,7 @@ export class NgModuleDecoratorHandler implements
return null;
}
// Verify that a `ts.Declaration` reference is a `ClassDeclaration` reference.
// Verify that a "Declaration" reference is a `ClassDeclaration` reference.
private isClassDeclarationReference(ref: Reference): ref is Reference<ClassDeclaration> {
return this.reflector.isClass(ref.node);
}

View File

@ -6,8 +6,8 @@
* found in the LICENSE file at https://angular.io/license
*/
import * as ts from 'typescript';
import {Reference} from '../../imports';
import {DeclarationNode} from '../../reflection';
/**
* Implement this interface if you want DecoratorHandlers to register
@ -18,7 +18,7 @@ export interface ReferencesRegistry {
* Register one or more references in the registry.
* @param references A collection of references to register.
*/
add(source: ts.Declaration, ...references: Reference<ts.Declaration>[]): void;
add(source: DeclarationNode, ...references: Reference<DeclarationNode>[]): void;
}
/**
@ -27,5 +27,5 @@ export interface ReferencesRegistry {
* The ngcc tool implements a working version for its purposes.
*/
export class NoopReferencesRegistry implements ReferencesRegistry {
add(source: ts.Declaration, ...references: Reference<ts.Declaration>[]): void {}
add(source: DeclarationNode, ...references: Reference<DeclarationNode>[]): void {}
}

View File

@ -21,7 +21,7 @@ import {CompoundMetadataReader, CompoundMetadataRegistry, DtsMetadataReader, Inj
import {ModuleWithProvidersScanner} from '../../modulewithproviders';
import {PartialEvaluator} from '../../partial_evaluator';
import {NOOP_PERF_RECORDER, PerfRecorder} from '../../perf';
import {ClassDeclaration, TypeScriptReflectionHost} from '../../reflection';
import {DeclarationNode, TypeScriptReflectionHost} from '../../reflection';
import {AdapterResourceLoader} from '../../resource';
import {entryPointKeyFor, NgModuleRouteAnalyzer} from '../../routing';
import {ComponentScopeReader, LocalModuleScopeRegistry, MetadataDtsModuleScopeResolver} from '../../scope';
@ -238,7 +238,7 @@ export class NgCompiler {
/**
* Retrieves the `ts.Declaration`s for any component(s) which use the given template file.
*/
getComponentsWithTemplateFile(templateFilePath: string): ReadonlySet<ts.Declaration> {
getComponentsWithTemplateFile(templateFilePath: string): ReadonlySet<DeclarationNode> {
const {templateMapping} = this.ensureAnalyzed();
return templateMapping.getComponentsWithTemplate(resolve(templateFilePath));
}
@ -378,7 +378,7 @@ export class NgCompiler {
*
* See the `indexing` package for more details.
*/
getIndexedComponents(): Map<ts.Declaration, IndexedComponent> {
getIndexedComponents(): Map<DeclarationNode, IndexedComponent> {
const compilation = this.ensureAnalyzed();
const context = new IndexingContext();
compilation.traitCompiler.index(context);
@ -880,7 +880,7 @@ https://v9.angular.io/guide/template-typecheck#template-type-checking`,
class ReferenceGraphAdapter implements ReferencesRegistry {
constructor(private graph: ReferenceGraph) {}
add(source: ts.Declaration, ...references: Reference<ts.Declaration>[]): void {
add(source: DeclarationNode, ...references: Reference<DeclarationNode>[]): void {
for (const {node} of references) {
let sourceFile = node.getSourceFile();
if (sourceFile === undefined) {

View File

@ -11,6 +11,7 @@ ts_library(
deps = [
"//packages/compiler-cli/src/ngtsc/diagnostics",
"//packages/compiler-cli/src/ngtsc/file_system",
"//packages/compiler-cli/src/ngtsc/reflection",
"//packages/compiler-cli/src/ngtsc/shims:api",
"//packages/compiler-cli/src/ngtsc/util",
"@npm//@types/node",

View File

@ -9,6 +9,7 @@
import * as ts from 'typescript';
import {ErrorCode, ngErrorCode} from '../../diagnostics';
import {DeclarationNode} from '../../reflection';
import {ReferenceGraph} from './reference_graph';
@ -39,7 +40,7 @@ export function checkForPrivateExports(
const diagnostics: ts.Diagnostic[] = [];
// Firstly, compute the exports of the entry point. These are all the Exported classes.
const topLevelExports = new Set<ts.Declaration>();
const topLevelExports = new Set<DeclarationNode>();
// Do this via `ts.TypeChecker.getExportsOfModule`.
const moduleSymbol = checker.getSymbolAtLocation(entryPoint);
@ -63,7 +64,7 @@ export function checkForPrivateExports(
// Next, go through each exported class and expand it to the set of classes it makes Visible,
// using the `ReferenceGraph`. For each Visible class, verify that it's also Exported, and queue
// an error if it isn't. `checkedSet` ensures only one error is queued per class.
const checkedSet = new Set<ts.Declaration>();
const checkedSet = new Set<DeclarationNode>();
// Loop through each Exported class.
// TODO(alxhub): use proper iteration when the legacy build is removed. (#27762)
@ -110,7 +111,7 @@ export function checkForPrivateExports(
return diagnostics;
}
function getPosOfDeclaration(decl: ts.Declaration): {start: number, length: number} {
function getPosOfDeclaration(decl: DeclarationNode): {start: number, length: number} {
const node: ts.Node = getIdentifierOfDeclaration(decl) || decl;
return {
start: node.getStart(),
@ -118,7 +119,7 @@ function getPosOfDeclaration(decl: ts.Declaration): {start: number, length: numb
};
}
function getIdentifierOfDeclaration(decl: ts.Declaration): ts.Identifier|null {
function getIdentifierOfDeclaration(decl: DeclarationNode): ts.Identifier|null {
if ((ts.isClassDeclaration(decl) || ts.isVariableDeclaration(decl) ||
ts.isFunctionDeclaration(decl)) &&
decl.name !== undefined && ts.isIdentifier(decl.name)) {
@ -128,12 +129,12 @@ function getIdentifierOfDeclaration(decl: ts.Declaration): ts.Identifier|null {
}
}
function getNameOfDeclaration(decl: ts.Declaration): string {
function getNameOfDeclaration(decl: DeclarationNode): string {
const id = getIdentifierOfDeclaration(decl);
return id !== null ? id.text : '(unnamed)';
}
function getDescriptorOfDeclaration(decl: ts.Declaration): string {
function getDescriptorOfDeclaration(decl: DeclarationNode): string {
switch (decl.kind) {
case ts.SyntaxKind.ClassDeclaration:
return 'class';

View File

@ -6,9 +6,9 @@
* found in the LICENSE file at https://angular.io/license
*/
import * as ts from 'typescript';
import {DeclarationNode} from '../../reflection';
export class ReferenceGraph<T = ts.Declaration> {
export class ReferenceGraph<T = DeclarationNode> {
private references = new Map<T, Set<T>>();
add(from: T, to: T): void {

View File

@ -11,7 +11,7 @@ import * as ts from 'typescript';
import {UnifiedModulesHost} from '../../core/api';
import {absoluteFromSourceFile, dirname, LogicalFileSystem, LogicalProjectPath, relative, toRelativeImport} from '../../file_system';
import {stripExtension} from '../../file_system/src/util';
import {ReflectionHost} from '../../reflection';
import {DeclarationNode, isConcreteDeclaration, ReflectionHost} from '../../reflection';
import {getSourceFile, isDeclaration, isTypeDeclaration, nodeNameForError} from '../../util/src/typescript';
import {findExportedNameOfNode} from './find_export';
@ -136,7 +136,7 @@ export class AbsoluteModuleStrategy implements ReferenceEmitStrategy {
* A cache of the exports of specific modules, because resolving a module to its exports is a
* costly operation.
*/
private moduleExportsCache = new Map<string, Map<ts.Declaration, string>|null>();
private moduleExportsCache = new Map<string, Map<DeclarationNode, string>|null>();
constructor(
protected program: ts.Program, protected checker: ts.TypeChecker,
@ -170,7 +170,7 @@ export class AbsoluteModuleStrategy implements ReferenceEmitStrategy {
return new ExternalExpr(new ExternalReference(specifier, symbolName));
}
private resolveImportName(moduleName: string, target: ts.Declaration, fromFile: string): string
private resolveImportName(moduleName: string, target: DeclarationNode, fromFile: string): string
|null {
const exports = this.getExportsOfModule(moduleName, fromFile);
if (exports !== null && exports.has(target)) {
@ -181,7 +181,7 @@ export class AbsoluteModuleStrategy implements ReferenceEmitStrategy {
}
private getExportsOfModule(moduleName: string, fromFile: string):
Map<ts.Declaration, string>|null {
Map<DeclarationNode, string>|null {
if (!this.moduleExportsCache.has(moduleName)) {
this.moduleExportsCache.set(moduleName, this.enumerateExportsOfModule(moduleName, fromFile));
}
@ -189,7 +189,7 @@ export class AbsoluteModuleStrategy implements ReferenceEmitStrategy {
}
protected enumerateExportsOfModule(specifier: string, fromFile: string):
Map<ts.Declaration, string>|null {
Map<DeclarationNode, string>|null {
// First, resolve the module specifier to its entry point, and get the ts.Symbol for it.
const entryPointFile = this.moduleResolver.resolveModule(specifier, fromFile);
if (entryPointFile === null) {
@ -200,13 +200,9 @@ export class AbsoluteModuleStrategy implements ReferenceEmitStrategy {
if (exports === null) {
return null;
}
const exportMap = new Map<ts.Declaration, string>();
const exportMap = new Map<DeclarationNode, string>();
exports.forEach((declaration, name) => {
// It's okay to skip inline declarations, since by definition they're not target-able with a
// ts.Declaration anyway.
if (declaration.node !== null) {
exportMap.set(declaration.node, name);
}
exportMap.set(declaration.node, name);
});
return exportMap;
}

View File

@ -7,8 +7,7 @@
*/
import {ParseSourceFile} from '@angular/compiler';
import * as ts from 'typescript';
import {ClassDeclaration} from '../../reflection';
import {ClassDeclaration, DeclarationNode} from '../../reflection';
/**
* Describes the kind of identifier found in a template.
@ -129,7 +128,7 @@ export interface IndexedComponent {
file: ParseSourceFile;
template: {
identifiers: Set<TopLevelIdentifier>,
usedComponents: Set<ts.Declaration>,
usedComponents: Set<DeclarationNode>,
isInline: boolean,
file: ParseSourceFile;
};

View File

@ -7,7 +7,7 @@
*/
import {ParseSourceFile} from '@angular/compiler';
import * as ts from 'typescript';
import {DeclarationNode} from '../../reflection';
import {IndexedComponent} from './api';
import {IndexingContext} from './context';
import {getTemplateIdentifiers} from './template';
@ -18,13 +18,13 @@ import {getTemplateIdentifiers} from './template';
*
* The context must be populated before `generateAnalysis` is called.
*/
export function generateAnalysis(context: IndexingContext): Map<ts.Declaration, IndexedComponent> {
const analysis = new Map<ts.Declaration, IndexedComponent>();
export function generateAnalysis(context: IndexingContext): Map<DeclarationNode, IndexedComponent> {
const analysis = new Map<DeclarationNode, IndexedComponent>();
context.components.forEach(({declaration, selector, boundTemplate, templateMeta}) => {
const name = declaration.name.getText();
const usedComponents = new Set<ts.Declaration>();
const usedComponents = new Set<DeclarationNode>();
const usedDirs = boundTemplate.getUsedDirectives();
usedDirs.forEach(dir => {
if (dir.isComponent) {

View File

@ -29,7 +29,7 @@ export function getComponentDeclaration(componentStr: string, className: string)
return getDeclaration(
program.program, getTestFilePath(), className,
(value: ts.Declaration): value is ClassDeclaration => ts.isClassDeclaration(value));
(value: ts.Node): value is ClassDeclaration => ts.isClassDeclaration(value));
}
/**

View File

@ -11,7 +11,7 @@ import * as ts from 'typescript';
import {Reference} from '../../imports';
import {OwningModule} from '../../imports/src/references';
import {DependencyTracker} from '../../incremental/api';
import {ConcreteDeclaration, Declaration, EnumMember, FunctionDefinition, InlineDeclaration, ReflectionHost, SpecialDeclarationKind} from '../../reflection';
import {Declaration, DeclarationNode, EnumMember, FunctionDefinition, isConcreteDeclaration, ReflectionHost, SpecialDeclarationKind} from '../../reflection';
import {isDeclaration} from '../../util/src/typescript';
import {ArrayConcatBuiltinFn, ArraySliceBuiltinFn} from './builtin';
@ -231,12 +231,7 @@ export class StaticInterpreter {
return this.getResolvedEnum(decl.node, decl.identity.enumMembers, context);
}
const declContext = {...context, ...joinModuleContext(context, node, decl)};
// The identifier's declaration is either concrete (a ts.Declaration exists for it) or inline
// (a direct reference to a ts.Expression).
// TODO(alxhub): remove cast once TS is upgraded in g3.
const result = decl.node !== null ?
this.visitDeclaration(decl.node, declContext) :
this.visitExpression((decl as InlineDeclaration).expression, declContext);
const result = this.visitDeclaration(decl.node, declContext);
if (result instanceof Reference) {
// Only record identifiers to non-synthetic references. Synthetic references may not have the
// same value at runtime as they do at compile time, so it's not legal to refer to them by the
@ -250,7 +245,7 @@ export class StaticInterpreter {
return result;
}
private visitDeclaration(node: ts.Declaration, context: Context): ResolvedValue {
private visitDeclaration(node: DeclarationNode, context: Context): ResolvedValue {
if (this.dependencyTracker !== null) {
this.dependencyTracker.addDependency(context.originatingFile, node.getSourceFile());
}
@ -342,10 +337,7 @@ export class StaticInterpreter {
};
// Visit both concrete and inline declarations.
// TODO(alxhub): remove cast once TS is upgraded in g3.
return decl.node !== null ?
this.visitDeclaration(decl.node, declContext) :
this.visitExpression((decl as InlineDeclaration).expression, declContext);
return this.visitDeclaration(decl.node, declContext);
});
}
@ -667,7 +659,7 @@ export class StaticInterpreter {
return map;
}
private getReference<T extends ts.Declaration>(node: T, context: Context): Reference<T> {
private getReference<T extends DeclarationNode>(node: T, context: Context): Reference<T> {
return new Reference(node, owningModule(context));
}
}
@ -733,11 +725,3 @@ function owningModule(context: Context, override: OwningModule|null = null): Own
return null;
}
}
/**
* Helper type guard to workaround a narrowing limitation in g3, where testing for
* `decl.node !== null` would not narrow `decl` to be of type `ConcreteDeclaration`.
*/
function isConcreteDeclaration(decl: Declaration): decl is ConcreteDeclaration {
return decl.node !== null;
}

View File

@ -11,7 +11,7 @@ import {absoluteFrom, getSourceFileOrError} from '../../file_system';
import {runInEachFileSystem} from '../../file_system/testing';
import {Reference} from '../../imports';
import {DependencyTracker} from '../../incremental/api';
import {Declaration, KnownDeclaration, SpecialDeclarationKind, TypeScriptReflectionHost} from '../../reflection';
import {Declaration, DeclarationKind, isConcreteDeclaration, KnownDeclaration, SpecialDeclarationKind, TypeScriptReflectionHost} from '../../reflection';
import {getDeclaration, makeProgram} from '../../testing';
import {DynamicValue} from '../src/dynamic';
import {PartialEvaluator} from '../src/interface';
@ -920,7 +920,7 @@ runInEachFileSystem(() => {
class DownleveledEnumReflectionHost extends TypeScriptReflectionHost {
getDeclarationOfIdentifier(id: ts.Identifier): Declaration|null {
const declaration = super.getDeclarationOfIdentifier(id);
if (declaration !== null && declaration.node !== null) {
if (declaration !== null && isConcreteDeclaration(declaration)) {
const enumMembers = [
{name: ts.createStringLiteral('ValueA'), initializer: ts.createStringLiteral('a')},
{name: ts.createStringLiteral('ValueB'), initializer: ts.createStringLiteral('b')},
@ -961,6 +961,7 @@ runInEachFileSystem(() => {
node: id,
viaModule: null,
identity: null,
kind: DeclarationKind.Concrete,
};
}
@ -968,8 +969,8 @@ runInEachFileSystem(() => {
}
}
function getTsHelperFn(node: ts.Declaration): KnownDeclaration|null {
const id = (node as ts.Declaration & {name?: ts.Identifier}).name || null;
function getTsHelperFn(node: ts.Node): KnownDeclaration|null {
const id = (node as ts.Node & {name?: ts.Identifier}).name || null;
const name = id && id.text;
switch (name) {

View File

@ -10,6 +10,7 @@ ts_library(
deps = [
"//packages:types",
"//packages/compiler-cli/src/ngtsc/file_system",
"//packages/compiler-cli/src/ngtsc/reflection",
"@npm//@types/node",
"@npm//typescript",
],

View File

@ -6,13 +6,12 @@
* found in the LICENSE file at https://angular.io/license
*/
import * as ts from 'typescript';
import {DeclarationNode} from '../../reflection';
export interface PerfRecorder {
readonly enabled: boolean;
mark(name: string, node?: ts.SourceFile|ts.Declaration, category?: string, detail?: string): void;
start(name: string, node?: ts.SourceFile|ts.Declaration, category?: string, detail?: string):
number;
mark(name: string, node?: DeclarationNode, category?: string, detail?: string): void;
start(name: string, node?: DeclarationNode, category?: string, detail?: string): number;
stop(span: number): void;
}

View File

@ -5,18 +5,15 @@
* 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 {DeclarationNode} from '../../reflection';
import {PerfRecorder} from './api';
export const NOOP_PERF_RECORDER: PerfRecorder = {
enabled: false,
mark: (name: string, node: ts.SourceFile|ts.Declaration, category?: string, detail?: string):
void => {},
start: (name: string, node: ts.SourceFile|ts.Declaration, category?: string, detail?: string):
number => {
return 0;
},
mark: (name: string, node: DeclarationNode, category?: string, detail?: string): void => {},
start: (name: string, node: DeclarationNode, category?: string, detail?: string): number => {
return 0;
},
stop: (span: number|false): void => {},
};

View File

@ -9,6 +9,7 @@
import * as fs from 'fs';
import * as ts from 'typescript';
import {resolve} from '../../file_system';
import {DeclarationNode} from '../../reflection';
import {PerfRecorder} from './api';
import {HrTime, mark, timeSinceInMicros} from './clock';
@ -24,14 +25,12 @@ export class PerfTracker implements PerfRecorder {
return new PerfTracker(mark());
}
mark(name: string, node?: ts.SourceFile|ts.Declaration, category?: string, detail?: string):
void {
mark(name: string, node?: DeclarationNode, category?: string, detail?: string): void {
const msg = this.makeLogMessage(PerfLogEventType.MARK, name, node, category, detail, undefined);
this.log.push(msg);
}
start(name: string, node?: ts.SourceFile|ts.Declaration, category?: string, detail?: string):
number {
start(name: string, node?: DeclarationNode, category?: string, detail?: string): number {
const span = this.nextSpanId++;
const msg = this.makeLogMessage(PerfLogEventType.SPAN_OPEN, name, node, category, detail, span);
this.log.push(msg);
@ -47,7 +46,7 @@ export class PerfTracker implements PerfRecorder {
}
private makeLogMessage(
type: PerfLogEventType, name: string, node: ts.SourceFile|ts.Declaration|undefined,
type: PerfLogEventType, name: string, node: DeclarationNode|undefined,
category: string|undefined, detail: string|undefined, span: number|undefined): PerfLogEvent {
const msg: PerfLogEvent = {
type,

View File

@ -17,6 +17,7 @@ import {NgCompilerOptions} from './core/api';
import {TrackedIncrementalBuildStrategy} from './incremental';
import {IndexedComponent} from './indexer';
import {NOOP_PERF_RECORDER, PerfRecorder, PerfTracker} from './perf';
import {DeclarationNode} from './reflection';
import {retagAllTsFiles, untagAllTsFiles} from './shims';
import {ReusedProgramStrategy} from './typecheck';
@ -276,7 +277,7 @@ export class NgtscProgram implements api.Program {
return ((opts && opts.mergeEmitResultsCallback) || mergeEmitResults)(emitResults);
}
getIndexedComponents(): Map<ts.Declaration, IndexedComponent> {
getIndexedComponents(): Map<DeclarationNode, IndexedComponent> {
return this.compiler.getIndexedComponents();
}

View File

@ -113,7 +113,7 @@ export function isDecoratorIdentifier(exp: ts.Expression): exp is DecoratorIdent
* For `ReflectionHost` purposes, a class declaration should always have a `name` identifier,
* because we need to be able to reference it in other parts of the program.
*/
export type ClassDeclaration<T extends ts.Declaration = ts.Declaration> = T&{name: ts.Identifier};
export type ClassDeclaration<T extends DeclarationNode = DeclarationNode> = T&{name: ts.Identifier};
/**
* An enumeration of possible kinds of class members.
@ -275,7 +275,7 @@ export interface ImportedTypeValueReference {
*/
nestedPath: string[]|null;
valueDeclaration: ts.Declaration;
valueDeclaration: DeclarationNode;
}
/**
@ -530,10 +530,38 @@ export interface EnumMember {
initializer: ts.Expression;
}
/**
* A type that is used to identify a declaration.
*
* Declarations are normally `ts.Declaration` types such as variable declarations, class
* declarations, function declarations etc.
* But in some cases there is no `ts.Declaration` that can be used for a declaration, such
* as when they are declared inline as part of an exported expression. Then we must use a
* `ts.Expression` as the declaration.
* An example of this is `exports.someVar = 42` where the declaration expression would be
* `exports.someVar`.
*/
export type DeclarationNode = ts.Declaration|ts.Expression;
/**
* The type of a Declaration - whether its node is concrete (ts.Declaration) or inline
* (ts.Expression). See `ConcreteDeclaration`, `InlineDeclaration` and `DeclarationNode` for more
* information about this.
*/
export const enum DeclarationKind {
Concrete,
Inline,
}
/**
* Base type for all `Declaration`s.
*/
export interface BaseDeclaration<T extends ts.Declaration = ts.Declaration> {
export interface BaseDeclaration<T extends DeclarationNode> {
/**
* The type of the underlying `node`.
*/
kind: DeclarationKind;
/**
* The absolute module path from which the symbol was imported into the application, if the symbol
* was imported via an absolute module (even through a chain of re-exports). If the symbol is part
@ -544,7 +572,7 @@ export interface BaseDeclaration<T extends ts.Declaration = ts.Declaration> {
/**
* TypeScript reference to the declaration itself, if one exists.
*/
node: T|null;
node: T;
/**
* If set, describes the type of the known declaration this declaration resolves to.
@ -553,13 +581,16 @@ export interface BaseDeclaration<T extends ts.Declaration = ts.Declaration> {
}
/**
* A declaration that has an associated TypeScript `ts.Declaration`.
*
* The alternative is an `InlineDeclaration`.
* Returns true if the `decl` is a `ConcreteDeclaration` (ie. that its `node` property is a
* `ts.Declaration`).
*/
export function isConcreteDeclaration(decl: Declaration): decl is ConcreteDeclaration {
return decl.kind === DeclarationKind.Concrete;
}
export interface ConcreteDeclaration<T extends ts.Declaration = ts.Declaration> extends
BaseDeclaration<T> {
node: T;
kind: DeclarationKind.Concrete;
/**
* Optionally represents a special identity of the declaration, or `null` if the declaration
@ -585,29 +616,20 @@ export interface DownleveledEnum {
}
/**
* A declaration that does not have an associated TypeScript `ts.Declaration`, only a
* `ts.Expression`.
* A declaration that does not have an associated TypeScript `ts.Declaration`.
*
* This can occur in some downlevelings when an `export const VAR = ...;` (a `ts.Declaration`) is
* transpiled to an assignment statement (e.g. `exports.VAR = ...;`). There is no `ts.Declaration`
* associated with `VAR` in that case, only an expression.
*/
export interface InlineDeclaration extends BaseDeclaration {
node: null;
/**
* The `ts.Expression` which constitutes the value of the declaration.
*/
expression: ts.Expression;
export interface InlineDeclaration extends
BaseDeclaration<Exclude<DeclarationNode, ts.Declaration>> {
kind: DeclarationKind.Inline;
}
/**
* The declaration of a symbol, along with information about how it was imported into the
* application.
*
* This can either be a `ConcreteDeclaration` if the underlying TypeScript node for the symbol is an
* actual `ts.Declaration`, or an `InlineDeclaration` if the declaration was transpiled in certain
* downlevelings to a `ts.Expression` instead.
*/
export type Declaration<T extends ts.Declaration = ts.Declaration> =
ConcreteDeclaration<T>|InlineDeclaration;
@ -637,7 +659,7 @@ export interface ReflectionHost {
* @returns an array of `Decorator` metadata if decorators are present on the declaration, or
* `null` if either no decorators were present or if the declaration is not of a decoratable type.
*/
getDecoratorsOfDeclaration(declaration: ts.Declaration): Decorator[]|null;
getDecoratorsOfDeclaration(declaration: DeclarationNode): Decorator[]|null;
/**
* Examine a declaration which should be of a class, and return metadata about the members of the
@ -802,7 +824,7 @@ export interface ReflectionHost {
* Note that the `ts.Declaration` returned from this function may not be from the same
* `ts.Program` as the input declaration.
*/
getDtsDeclaration(declaration: ts.Declaration): ts.Declaration|null;
getDtsDeclaration(declaration: DeclarationNode): ts.Declaration|null;
/**
* Get a `ts.Identifier` for a given `ClassDeclaration` which can be used to refer to the class

View File

@ -8,7 +8,7 @@
import * as ts from 'typescript';
import {ClassDeclaration, ClassMember, ClassMemberKind, CtorParameter, Declaration, Decorator, FunctionDefinition, Import, isDecoratorIdentifier, ReflectionHost} from './host';
import {ClassDeclaration, ClassMember, ClassMemberKind, CtorParameter, Declaration, DeclarationKind, DeclarationNode, Decorator, FunctionDefinition, Import, isDecoratorIdentifier, ReflectionHost} from './host';
import {typeToValue} from './type_to_value';
import {isNamedClassDeclaration} from './util';
@ -19,7 +19,7 @@ import {isNamedClassDeclaration} from './util';
export class TypeScriptReflectionHost implements ReflectionHost {
constructor(protected checker: ts.TypeChecker) {}
getDecoratorsOfDeclaration(declaration: ts.Declaration): Decorator[]|null {
getDecoratorsOfDeclaration(declaration: DeclarationNode): Decorator[]|null {
if (declaration.decorators === undefined || declaration.decorators.length === 0) {
return null;
}
@ -187,7 +187,7 @@ export class TypeScriptReflectionHost implements ReflectionHost {
return declaration.initializer || null;
}
getDtsDeclaration(_: ts.Declaration): ts.Declaration|null {
getDtsDeclaration(_: ClassDeclaration): ts.Declaration|null {
return null;
}
@ -207,7 +207,7 @@ export class TypeScriptReflectionHost implements ReflectionHost {
return null;
}
const decl: ts.Declaration = symbol.declarations[0];
const decl = symbol.declarations[0];
const importDecl = getContainingImportDeclaration(decl);
// Ignore declarations that are defined locally (not imported).
@ -318,6 +318,7 @@ export class TypeScriptReflectionHost implements ReflectionHost {
known: null,
viaModule,
identity: null,
kind: DeclarationKind.Concrete,
};
} else if (symbol.declarations !== undefined && symbol.declarations.length > 0) {
return {
@ -325,6 +326,7 @@ export class TypeScriptReflectionHost implements ReflectionHost {
known: null,
viaModule,
identity: null,
kind: DeclarationKind.Concrete,
};
} else {
return null;

View File

@ -9,7 +9,7 @@ import * as ts from 'typescript';
import {absoluteFrom, getSourceFileOrError} from '../../file_system';
import {runInEachFileSystem} from '../../file_system/testing';
import {getDeclaration, makeProgram} from '../../testing';
import {ClassMember, ClassMemberKind, CtorParameter, TypeValueReferenceKind} from '../src/host';
import {ClassMember, ClassMemberKind, CtorParameter, DeclarationKind, TypeValueReferenceKind} from '../src/host';
import {TypeScriptReflectionHost} from '../src/typescript';
import {isNamedClassDeclaration} from '../src/util';
@ -360,6 +360,7 @@ runInEachFileSystem(() => {
const Target = foo.type.typeName;
const decl = host.getDeclarationOfIdentifier(Target);
expect(decl).toEqual({
kind: DeclarationKind.Concrete,
node: targetDecl,
known: null,
viaModule: 'absolute',
@ -395,6 +396,7 @@ runInEachFileSystem(() => {
known: null,
viaModule: 'absolute',
identity: null,
kind: DeclarationKind.Concrete
});
});
});

View File

@ -12,7 +12,7 @@ import * as ts from 'typescript';
import {ErrorCode, makeDiagnostic, makeRelatedInformation} from '../../diagnostics';
import {AliasingHost, Reexport, Reference, ReferenceEmitter} from '../../imports';
import {DirectiveMeta, MetadataReader, MetadataRegistry, NgModuleMeta, PipeMeta} from '../../metadata';
import {ClassDeclaration} from '../../reflection';
import {ClassDeclaration, DeclarationNode} from '../../reflection';
import {identifierOfNode, nodeNameForError} from '../../util/src/typescript';
import {ExportScope, ScopeData} from './api';
@ -293,14 +293,14 @@ export class LocalModuleScopeRegistry implements MetadataRegistry, ComponentScop
// - the directives and pipes which are exported to any NgModules which import this one.
// Directives and pipes in the compilation scope.
const compilationDirectives = new Map<ts.Declaration, DirectiveMeta>();
const compilationPipes = new Map<ts.Declaration, PipeMeta>();
const compilationDirectives = new Map<DeclarationNode, DirectiveMeta>();
const compilationPipes = new Map<DeclarationNode, PipeMeta>();
const declared = new Set<ts.Declaration>();
const declared = new Set<DeclarationNode>();
// Directives and pipes exported to any importing NgModules.
const exportDirectives = new Map<ts.Declaration, DirectiveMeta>();
const exportPipes = new Map<ts.Declaration, PipeMeta>();
const exportDirectives = new Map<DeclarationNode, DirectiveMeta>();
const exportPipes = new Map<DeclarationNode, PipeMeta>();
// The algorithm is as follows:
// 1) Add all of the directives/pipes from each NgModule imported into the current one to the
@ -477,7 +477,7 @@ export class LocalModuleScopeRegistry implements MetadataRegistry, ComponentScop
*/
private getExportedScope(
ref: Reference<ClassDeclaration>, diagnostics: ts.Diagnostic[],
ownerForErrors: ts.Declaration, type: 'import'|'export'): ExportScope|null|undefined {
ownerForErrors: DeclarationNode, type: 'import'|'export'): ExportScope|null|undefined {
if (ref.node.getSourceFile().isDeclarationFile) {
// The NgModule is declared in a .d.ts file. Resolve it with the `DependencyScopeReader`.
if (!ts.isClassDeclaration(ref.node)) {
@ -499,7 +499,7 @@ export class LocalModuleScopeRegistry implements MetadataRegistry, ComponentScop
}
private getReexports(
ngModule: NgModuleMeta, ref: Reference<ClassDeclaration>, declared: Set<ts.Declaration>,
ngModule: NgModuleMeta, ref: Reference<ClassDeclaration>, declared: Set<DeclarationNode>,
exported: {directives: DirectiveMeta[], pipes: PipeMeta[]},
diagnostics: ts.Diagnostic[]): Reexport[]|null {
let reexports: Reexport[]|null = null;
@ -569,7 +569,7 @@ export class LocalModuleScopeRegistry implements MetadataRegistry, ComponentScop
* Produce a `ts.Diagnostic` for an invalid import or export from an NgModule.
*/
function invalidRef(
clazz: ts.Declaration, decl: Reference<ts.Declaration>,
clazz: DeclarationNode, decl: Reference<DeclarationNode>,
type: 'import'|'export'): ts.Diagnostic {
const code =
type === 'import' ? ErrorCode.NGMODULE_INVALID_IMPORT : ErrorCode.NGMODULE_INVALID_EXPORT;
@ -607,7 +607,7 @@ function invalidRef(
* Produce a `ts.Diagnostic` for an import or export which itself has errors.
*/
function invalidTransitiveNgModuleRef(
clazz: ts.Declaration, decl: Reference<ts.Declaration>,
clazz: DeclarationNode, decl: Reference<DeclarationNode>,
type: 'import'|'export'): ts.Diagnostic {
const code =
type === 'import' ? ErrorCode.NGMODULE_INVALID_IMPORT : ErrorCode.NGMODULE_INVALID_EXPORT;
@ -620,7 +620,7 @@ function invalidTransitiveNgModuleRef(
* Produce a `ts.Diagnostic` for an exported directive or pipe which was not declared or imported
* by the NgModule in question.
*/
function invalidReexport(clazz: ts.Declaration, decl: Reference<ts.Declaration>): ts.Diagnostic {
function invalidReexport(clazz: DeclarationNode, decl: Reference<DeclarationNode>): ts.Diagnostic {
return makeDiagnostic(
ErrorCode.NGMODULE_INVALID_REEXPORT, identifierOfNode(decl.node) || decl.node,
`Present in the NgModule.exports of ${

View File

@ -11,6 +11,7 @@ ts_library(
deps = [
"//packages:types",
"//packages/compiler-cli/src/ngtsc/file_system",
"//packages/compiler-cli/src/ngtsc/reflection",
"@npm//typescript",
],
)

View File

@ -11,6 +11,7 @@
import * as ts from 'typescript';
import {AbsoluteFsPath, dirname, getFileSystem, getSourceFileOrError, NgtscCompilerHost} from '../../file_system';
import {DeclarationNode} from '../../reflection';
export function makeProgram(
files: {name: AbsoluteFsPath, contents: string, isRoot?: boolean}[],
@ -57,7 +58,7 @@ export function makeProgram(
* An error will be thrown if there is not at least one AST node with the given `name` and passes
* the `predicate` test.
*/
export function getDeclaration<T extends ts.Declaration>(
export function getDeclaration<T extends DeclarationNode>(
program: ts.Program, fileName: AbsoluteFsPath, name: string,
assert: (value: any) => value is T): T {
const sf = getSourceFileOrError(program, fileName);
@ -78,8 +79,8 @@ export function getDeclaration<T extends ts.Declaration>(
/**
* Walk the AST tree from the `rootNode` looking for a declaration that has the given `name`.
*/
export function walkForDeclarations(name: string, rootNode: ts.Node): ts.Declaration[] {
const chosenDecls: ts.Declaration[] = [];
export function walkForDeclarations(name: string, rootNode: ts.Node): DeclarationNode[] {
const chosenDecls: DeclarationNode[] = [];
rootNode.forEachChild(node => {
if (ts.isVariableStatement(node)) {
node.declarationList.declarations.forEach(decl => {

View File

@ -13,7 +13,7 @@ import {ErrorCode, FatalDiagnosticError} from '../../diagnostics';
import {IncrementalBuild} from '../../incremental/api';
import {IndexingContext} from '../../indexer';
import {PerfRecorder} from '../../perf';
import {ClassDeclaration, Decorator, ReflectionHost} from '../../reflection';
import {ClassDeclaration, DeclarationNode, Decorator, ReflectionHost} from '../../reflection';
import {ProgramTypeCheckAdapter, TypeCheckContext} from '../../typecheck/api';
import {getSourceFile, isExported} from '../../util/src/typescript';
@ -463,7 +463,7 @@ export class TraitCompiler implements ProgramTypeCheckAdapter {
}
}
compile(clazz: ts.Declaration, constantPool: ConstantPool): CompileResult[]|null {
compile(clazz: DeclarationNode, constantPool: ConstantPool): CompileResult[]|null {
const original = ts.getOriginalNode(clazz) as typeof clazz;
if (!this.reflector.isClass(clazz) || !this.reflector.isClass(original) ||
!this.classes.has(original)) {

View File

@ -9,6 +9,7 @@ ts_library(
deps = [
"//packages:types",
"//packages/compiler",
"//packages/compiler-cli/src/ngtsc/reflection",
"//packages/compiler-cli/src/ngtsc/typecheck/api",
"@npm//typescript",
],

View File

@ -7,6 +7,7 @@
*/
import * as ts from 'typescript';
import {DeclarationNode} from '../../../reflection';
import {TemplateId} from '../../api';
@ -22,7 +23,7 @@ interface HasNextTemplateId {
[NEXT_TEMPLATE_ID]: number;
}
export function getTemplateId(clazz: ts.Declaration): TemplateId {
export function getTemplateId(clazz: DeclarationNode): TemplateId {
const node = clazz as ts.Declaration & Partial<HasTemplateId>;
if (node[TEMPLATE_ID] === undefined) {
node[TEMPLATE_ID] = allocateTemplateId(node.getSourceFile());

View File

@ -8,7 +8,7 @@
import * as ts from 'typescript';
import {OwningModule, Reference} from '../../imports';
import {ReflectionHost} from '../../reflection';
import {DeclarationNode, ReflectionHost} from '../../reflection';
import {canEmitType, ResolvedTypeReference, TypeEmitter} from './type_emitter';
@ -89,7 +89,7 @@ export class TypeParameterEmitter {
return new Reference(declaration.node, owningModule);
}
private isLocalTypeParameter(decl: ts.Declaration): boolean {
private isLocalTypeParameter(decl: DeclarationNode): boolean {
// Checking for local type parameters only occurs during resolution of type parameters, so it is
// guaranteed that type parameters are present.
return this.typeParameters!.some(param => param === decl);

View File

@ -11,6 +11,7 @@ ts_library(
"//packages:types",
"//packages/compiler-cli/src/ngtsc/file_system",
"//packages/compiler-cli/src/ngtsc/incremental:api",
"//packages/compiler-cli/src/ngtsc/reflection",
"@npm//@types/node",
"@npm//typescript",
],

View File

@ -11,6 +11,7 @@ const D_TS = /\.d\.ts$/i;
import * as ts from 'typescript';
import {AbsoluteFsPath, absoluteFrom} from '../../file_system';
import {DeclarationNode} from '../../reflection';
export function isDtsPath(filePath: string): boolean {
return D_TS.test(filePath);
@ -82,7 +83,7 @@ export function isTypeDeclaration(node: ts.Node): node is ts.EnumDeclaration|
ts.isInterfaceDeclaration(node);
}
export function isExported(node: ts.Declaration): boolean {
export function isExported(node: DeclarationNode): boolean {
let topLevel: ts.Node = node;
if (ts.isVariableDeclaration(node) && ts.isVariableDeclarationList(node.parent)) {
topLevel = node.parent.parent;