refactor(ngcc): simplify and rename `getClassDeclarationFromInnerDeclaration()` (#38959)

The new function does not try to restrict the kind of AST node that it
finds, leaving that to the caller. This will make it more resuable in the
UMD reflection host.

PR Close #38959
This commit is contained in:
Pete Bacon Darwin 2020-09-27 11:42:43 +01:00 committed by atscott
parent 5038e5741b
commit 1d6e67478e
3 changed files with 54 additions and 51 deletions

View File

@ -302,6 +302,7 @@ export class Esm2015ReflectionHost extends TypeScriptReflectionHost implements N
if (superDeclaration.known !== null || superDeclaration.identity !== null) { if (superDeclaration.known !== null || superDeclaration.identity !== null) {
return superDeclaration; return superDeclaration;
} }
let declarationNode: ts.Node = superDeclaration.node; let declarationNode: ts.Node = superDeclaration.node;
if (isNamedVariableDeclaration(superDeclaration.node) && !isTopLevel(superDeclaration.node)) { if (isNamedVariableDeclaration(superDeclaration.node) && !isTopLevel(superDeclaration.node)) {
const variableValue = this.getVariableValue(superDeclaration.node); const variableValue = this.getVariableValue(superDeclaration.node);
@ -310,9 +311,9 @@ export class Esm2015ReflectionHost extends TypeScriptReflectionHost implements N
} }
} }
const outerClassNode = getClassDeclarationFromInnerDeclaration(declarationNode); const outerNode = getOuterNodeFromInnerDeclaration(declarationNode);
const declaration = outerClassNode !== null ? const declaration = outerNode !== null && isNamedVariableDeclaration(outerNode) ?
this.getDeclarationOfIdentifier(outerClassNode.name) : this.getDeclarationOfIdentifier(outerNode.name) :
superDeclaration; superDeclaration;
if (declaration === null || declaration.node === null || declaration.known !== null) { if (declaration === null || declaration.node === null || declaration.known !== null) {
return declaration; return declaration;
@ -2569,51 +2570,53 @@ function isTopLevel(node: ts.Node): boolean {
} }
/** /**
* Get the actual (outer) declaration of a class. * Get a node that represents the actual (outer) declaration of a class from its implementation.
* *
* Sometimes, the implementation of a class is an expression that is hidden inside an IIFE and * Sometimes, the implementation of a class is an expression that is hidden inside an IIFE and
* returned to be assigned to a variable outside the IIFE, which is what the rest of the program * assigned to a variable outside the IIFE, which is what the rest of the program interacts with.
* interacts with. * For example,
* *
* Given the inner declaration, we want to get to the declaration of the outer variable that * ```
* represents the class. * OuterNode = Alias = (function() { function InnerNode() {} return InnerNode; })();
* ```
* *
* @param node a node that could be the inner declaration inside an IIFE. * @param node a node that could be the implementation inside an IIFE.
* @returns the outer variable declaration or `null` if it is not a "class". * @returns a node that represents the outer declaration, or `null` if it is does not match the IIFE
* format shown above.
*/ */
export function getClassDeclarationFromInnerDeclaration(node: ts.Node): export function getOuterNodeFromInnerDeclaration(node: ts.Node): ts.Node|null {
ClassDeclaration<ts.VariableDeclaration>|null { if (!ts.isFunctionDeclaration(node) && !ts.isClassDeclaration(node) &&
if (ts.isFunctionDeclaration(node) || ts.isClassDeclaration(node) || !ts.isVariableStatement(node)) {
ts.isVariableStatement(node)) { return null;
// It might be the function expression inside the IIFE. We need to go 5 levels up...
// - IIFE body.
let outerNode = node.parent;
if (!outerNode || !ts.isBlock(outerNode)) return null;
// - IIFE function expression.
outerNode = outerNode.parent;
if (!outerNode || (!ts.isFunctionExpression(outerNode) && !ts.isArrowFunction(outerNode))) {
return null;
}
outerNode = outerNode.parent;
// - Parenthesis inside IIFE.
if (outerNode && ts.isParenthesizedExpression(outerNode)) outerNode = outerNode.parent;
// - IIFE call expression.
if (!outerNode || !ts.isCallExpression(outerNode)) return null;
outerNode = outerNode.parent;
// - Parenthesis around IIFE.
if (outerNode && ts.isParenthesizedExpression(outerNode)) outerNode = outerNode.parent;
// - Outer variable declaration.
if (!outerNode || !ts.isVariableDeclaration(outerNode)) return null;
// Finally, ensure that the variable declaration has a `name` identifier.
return hasNameIdentifier(outerNode) ? outerNode : null;
} }
return null; // It might be the function expression inside the IIFE. We need to go 5 levels up...
// - IIFE body.
let outerNode = node.parent;
if (!outerNode || !ts.isBlock(outerNode)) return null;
// - IIFE function expression.
outerNode = outerNode.parent;
if (!outerNode || (!ts.isFunctionExpression(outerNode) && !ts.isArrowFunction(outerNode))) {
return null;
}
outerNode = outerNode.parent;
// - Parenthesis inside IIFE.
if (outerNode && ts.isParenthesizedExpression(outerNode)) outerNode = outerNode.parent;
// - IIFE call expression.
if (!outerNode || !ts.isCallExpression(outerNode)) return null;
outerNode = outerNode.parent;
// - Parenthesis around IIFE.
if (outerNode && ts.isParenthesizedExpression(outerNode)) outerNode = outerNode.parent;
// - Skip any aliases between the IIFE and the far left hand side of any assignments.
while (isAssignment(outerNode.parent)) {
outerNode = outerNode.parent;
}
return outerNode;
} }

View File

@ -8,10 +8,10 @@
import * as ts from 'typescript'; import * as ts from 'typescript';
import {ClassDeclaration, ClassMember, ClassMemberKind, Declaration, Decorator, FunctionDefinition, KnownDeclaration, Parameter, reflectObjectLiteral} from '../../../src/ngtsc/reflection'; import {ClassDeclaration, ClassMember, ClassMemberKind, Declaration, Decorator, FunctionDefinition, isNamedFunctionDeclaration, KnownDeclaration, Parameter, reflectObjectLiteral} from '../../../src/ngtsc/reflection';
import {getTsHelperFnFromDeclaration, getTsHelperFnFromIdentifier, hasNameIdentifier} from '../utils'; import {getTsHelperFnFromDeclaration, getTsHelperFnFromIdentifier, hasNameIdentifier} from '../utils';
import {Esm2015ReflectionHost, getClassDeclarationFromInnerDeclaration, getPropertyValueFromSymbol, isAssignmentStatement, ParamInfo} from './esm2015_host'; import {Esm2015ReflectionHost, getOuterNodeFromInnerDeclaration, getPropertyValueFromSymbol, isAssignmentStatement, ParamInfo} from './esm2015_host';
import {NgccClassSymbol} from './ngcc_host'; import {NgccClassSymbol} from './ngcc_host';
@ -186,16 +186,16 @@ export class Esm5ReflectionHost extends Esm2015ReflectionHost {
return classSymbol; return classSymbol;
} }
if (!ts.isFunctionDeclaration(declaration) || !hasNameIdentifier(declaration)) { if (!isNamedFunctionDeclaration(declaration)) {
return undefined; return undefined;
} }
const outerDeclaration = getClassDeclarationFromInnerDeclaration(declaration); const outerNode = getOuterNodeFromInnerDeclaration(declaration);
if (outerDeclaration === null || !hasNameIdentifier(outerDeclaration)) { if (outerNode === null || !hasNameIdentifier(outerNode)) {
return undefined; return undefined;
} }
return this.createClassSymbol(outerDeclaration, declaration); return this.createClassSymbol(outerNode.name, declaration);
} }
/** /**

View File

@ -71,9 +71,9 @@ export function findAll<T>(node: ts.Node, test: (node: ts.Node) => node is ts.No
* @param declaration The declaration to test. * @param declaration The declaration to test.
* @returns true if the declaration has an identifier for a name. * @returns true if the declaration has an identifier for a name.
*/ */
export function hasNameIdentifier(declaration: ts.Declaration): declaration is ts.Declaration& export function hasNameIdentifier(declaration: ts.Node): declaration is ts.Declaration&
{name: ts.Identifier} { {name: ts.Identifier} {
const namedDeclaration: ts.Declaration&{name?: ts.Node} = declaration; const namedDeclaration: ts.Node&{name?: ts.Node} = declaration;
return namedDeclaration.name !== undefined && ts.isIdentifier(namedDeclaration.name); return namedDeclaration.name !== undefined && ts.isIdentifier(namedDeclaration.name);
} }