angular-cn/packages/compiler-cli/linker/babel/src/babel_declaration_scope.ts

68 lines
2.5 KiB
TypeScript

/**
* @license
* Copyright Google LLC 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 {NodePath, Scope} from '@babel/traverse';
import * as t from '@babel/types';
import {DeclarationScope} from '../../../linker';
export type ConstantScopePath = NodePath<t.Function|t.Program>;
/**
* This class represents the lexical scope of a partial declaration in Babel source code.
*
* Its only responsibility is to compute a reference object for the scope of shared constant
* statements that will be generated during partial linking.
*/
export class BabelDeclarationScope implements DeclarationScope<ConstantScopePath, t.Expression> {
/**
* Construct a new `BabelDeclarationScope`.
*
* @param declarationScope the Babel scope containing the declaration call expression.
*/
constructor(private declarationScope: Scope) {}
/**
* Compute the Babel `NodePath` that can be used to reference the lexical scope where any
* shared constant statements would be inserted.
*
* There will only be a shared constant scope if the expression is in an ECMAScript module, or a
* UMD module. Otherwise `null` is returned to indicate that constant statements must be emitted
* locally to the generated linked definition, to avoid polluting the global scope.
*
* @param expression the expression that points to the Angular core framework import.
*/
getConstantScopeRef(expression: t.Expression): ConstantScopePath|null {
// If the expression is of the form `a.b.c` then we want to get the far LHS (e.g. `a`).
let bindingExpression = expression;
while (t.isMemberExpression(bindingExpression)) {
bindingExpression = bindingExpression.object;
}
if (!t.isIdentifier(bindingExpression)) {
return null;
}
// The binding of the expression is where this identifier was declared.
// This could be a variable declaration, an import namespace or a function parameter.
const binding = this.declarationScope.getBinding(bindingExpression.name);
if (binding === undefined) {
return null;
}
// We only support shared constant statements if the binding was in a UMD module (i.e. declared
// within a `t.Function`) or an ECMASCript module (i.e. declared at the top level of a
// `t.Program` that is marked as a module).
const path = binding.scope.path;
if (!path.isFunctionParent() && !(path.isProgram() && path.node.sourceType === 'module')) {
return null;
}
return path;
}
}