From 1d6e67478e18b4d594b2d95ff21a2b3bd572c71c Mon Sep 17 00:00:00 2001 From: Pete Bacon Darwin Date: Sun, 27 Sep 2020 11:42:43 +0100 Subject: [PATCH] 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 --- .../ngcc/src/host/esm2015_host.ts | 89 ++++++++++--------- .../compiler-cli/ngcc/src/host/esm5_host.ts | 12 +-- packages/compiler-cli/ngcc/src/utils.ts | 4 +- 3 files changed, 54 insertions(+), 51 deletions(-) diff --git a/packages/compiler-cli/ngcc/src/host/esm2015_host.ts b/packages/compiler-cli/ngcc/src/host/esm2015_host.ts index 704114cae2..8054083462 100644 --- a/packages/compiler-cli/ngcc/src/host/esm2015_host.ts +++ b/packages/compiler-cli/ngcc/src/host/esm2015_host.ts @@ -302,6 +302,7 @@ export class Esm2015ReflectionHost extends TypeScriptReflectionHost implements N if (superDeclaration.known !== null || superDeclaration.identity !== null) { return superDeclaration; } + let declarationNode: ts.Node = superDeclaration.node; if (isNamedVariableDeclaration(superDeclaration.node) && !isTopLevel(superDeclaration.node)) { const variableValue = this.getVariableValue(superDeclaration.node); @@ -310,9 +311,9 @@ export class Esm2015ReflectionHost extends TypeScriptReflectionHost implements N } } - const outerClassNode = getClassDeclarationFromInnerDeclaration(declarationNode); - const declaration = outerClassNode !== null ? - this.getDeclarationOfIdentifier(outerClassNode.name) : + const outerNode = getOuterNodeFromInnerDeclaration(declarationNode); + const declaration = outerNode !== null && isNamedVariableDeclaration(outerNode) ? + this.getDeclarationOfIdentifier(outerNode.name) : superDeclaration; if (declaration === null || declaration.node === null || declaration.known !== null) { 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 - * returned to be assigned to a variable outside the IIFE, which is what the rest of the program - * interacts with. + * assigned to a variable outside the IIFE, which is what the rest of the program 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. - * @returns the outer variable declaration or `null` if it is not a "class". + * @param node a node that could be the implementation inside an IIFE. + * @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): - ClassDeclaration|null { - if (ts.isFunctionDeclaration(node) || ts.isClassDeclaration(node) || - ts.isVariableStatement(node)) { - // 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; +export function getOuterNodeFromInnerDeclaration(node: ts.Node): ts.Node|null { + if (!ts.isFunctionDeclaration(node) && !ts.isClassDeclaration(node) && + !ts.isVariableStatement(node)) { + return 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; } diff --git a/packages/compiler-cli/ngcc/src/host/esm5_host.ts b/packages/compiler-cli/ngcc/src/host/esm5_host.ts index 5464fbcff4..89decab694 100644 --- a/packages/compiler-cli/ngcc/src/host/esm5_host.ts +++ b/packages/compiler-cli/ngcc/src/host/esm5_host.ts @@ -8,10 +8,10 @@ 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 {Esm2015ReflectionHost, getClassDeclarationFromInnerDeclaration, getPropertyValueFromSymbol, isAssignmentStatement, ParamInfo} from './esm2015_host'; +import {Esm2015ReflectionHost, getOuterNodeFromInnerDeclaration, getPropertyValueFromSymbol, isAssignmentStatement, ParamInfo} from './esm2015_host'; import {NgccClassSymbol} from './ngcc_host'; @@ -186,16 +186,16 @@ export class Esm5ReflectionHost extends Esm2015ReflectionHost { return classSymbol; } - if (!ts.isFunctionDeclaration(declaration) || !hasNameIdentifier(declaration)) { + if (!isNamedFunctionDeclaration(declaration)) { return undefined; } - const outerDeclaration = getClassDeclarationFromInnerDeclaration(declaration); - if (outerDeclaration === null || !hasNameIdentifier(outerDeclaration)) { + const outerNode = getOuterNodeFromInnerDeclaration(declaration); + if (outerNode === null || !hasNameIdentifier(outerNode)) { return undefined; } - return this.createClassSymbol(outerDeclaration, declaration); + return this.createClassSymbol(outerNode.name, declaration); } /** diff --git a/packages/compiler-cli/ngcc/src/utils.ts b/packages/compiler-cli/ngcc/src/utils.ts index caaea704b2..7a67e5049b 100644 --- a/packages/compiler-cli/ngcc/src/utils.ts +++ b/packages/compiler-cli/ngcc/src/utils.ts @@ -71,9 +71,9 @@ export function findAll(node: ts.Node, test: (node: ts.Node) => node is ts.No * @param declaration The declaration to test. * @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} { - const namedDeclaration: ts.Declaration&{name?: ts.Node} = declaration; + const namedDeclaration: ts.Node&{name?: ts.Node} = declaration; return namedDeclaration.name !== undefined && ts.isIdentifier(namedDeclaration.name); }