Skip to content

Commit 884c2b8

Browse files
committed
refactor(@angular-devkit/build-angular): remove babel core runtime imports from several build optimizer passes
The `elide-angular-metadata` and `pure-toplevel-functions` build optimization passes have been cleaned up and restructured to remove the need for a direct runtime dependency on `@babel/core`.
1 parent 822e7a4 commit 884c2b8

File tree

2 files changed

+47
-42
lines changed

2 files changed

+47
-42
lines changed

packages/angular_devkit/build_angular/src/tools/babel/plugins/elide-angular-metadata.ts

+41-36
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
* found in the LICENSE file at https://angular.io/license
77
*/
88

9-
import { NodePath, PluginObj, types } from '@babel/core';
9+
import type { NodePath, PluginObj } from '@babel/core';
1010

1111
/**
1212
* The name of the Angular class metadata function created by the Angular compiler.
@@ -30,9 +30,19 @@ const SET_CLASS_DEBUG_INFO_NAME = 'ɵsetClassDebugInfo';
3030
* @returns An a string iterable containing one or more keywords.
3131
*/
3232
export function getKeywords(): Iterable<string> {
33-
return [SET_CLASS_METADATA_NAME, SET_CLASS_METADATA_ASYNC_NAME, SET_CLASS_DEBUG_INFO_NAME];
33+
return Object.keys(angularMetadataFunctions);
3434
}
3535

36+
/**
37+
* An object map of function names and related value checks for discovery of Angular generated
38+
* metadata calls.
39+
*/
40+
const angularMetadataFunctions: Record<string, (args: NodePath[]) => boolean> = {
41+
[SET_CLASS_METADATA_NAME]: isSetClassMetadataCall,
42+
[SET_CLASS_METADATA_ASYNC_NAME]: isSetClassMetadataAsyncCall,
43+
[SET_CLASS_DEBUG_INFO_NAME]: isSetClassDebugInfoCall,
44+
};
45+
3646
/**
3747
* A babel plugin factory function for eliding the Angular class metadata function (`ɵsetClassMetadata`).
3848
*
@@ -41,24 +51,25 @@ export function getKeywords(): Iterable<string> {
4151
export default function (): PluginObj {
4252
return {
4353
visitor: {
44-
CallExpression(path: NodePath<types.CallExpression>) {
45-
const callee = path.node.callee;
46-
const callArguments = path.node.arguments;
54+
CallExpression(path) {
55+
const callee = path.get('callee');
4756

4857
// The function being called must be the metadata function name
4958
let calleeName;
50-
if (types.isMemberExpression(callee) && types.isIdentifier(callee.property)) {
51-
calleeName = callee.property.name;
52-
} else if (types.isIdentifier(callee)) {
53-
calleeName = callee.name;
59+
if (callee.isMemberExpression()) {
60+
const calleeProperty = callee.get('property');
61+
if (calleeProperty.isIdentifier()) {
62+
calleeName = calleeProperty.node.name;
63+
}
64+
} else if (callee.isIdentifier()) {
65+
calleeName = callee.node.name;
66+
}
67+
68+
if (!calleeName) {
69+
return;
5470
}
5571

56-
if (
57-
calleeName !== undefined &&
58-
(isRemoveClassMetadataCall(calleeName, callArguments) ||
59-
isRemoveClassmetadataAsyncCall(calleeName, callArguments) ||
60-
isSetClassDebugInfoCall(calleeName, callArguments))
61-
) {
72+
if (angularMetadataFunctions[calleeName]?.(path.get('arguments'))) {
6273
// The metadata function is always emitted inside a function expression
6374
const parent = path.getFunctionParent();
6475

@@ -74,47 +85,41 @@ export default function (): PluginObj {
7485
}
7586

7687
/** Determines if a function call is a call to `setClassMetadata`. */
77-
function isRemoveClassMetadataCall(name: string, args: types.CallExpression['arguments']): boolean {
88+
function isSetClassMetadataCall(callArguments: NodePath[]): boolean {
7889
// `setClassMetadata` calls have to meet the following criteria:
7990
// * First must be an identifier
8091
// * Second must be an array literal
8192
return (
82-
name === SET_CLASS_METADATA_NAME &&
83-
args.length === 4 &&
84-
types.isIdentifier(args[0]) &&
85-
types.isArrayExpression(args[1])
93+
callArguments.length === 4 &&
94+
callArguments[0].isIdentifier() &&
95+
callArguments[1].isArrayExpression()
8696
);
8797
}
8898

8999
/** Determines if a function call is a call to `setClassMetadataAsync`. */
90-
function isRemoveClassmetadataAsyncCall(
91-
name: string,
92-
args: types.CallExpression['arguments'],
93-
): boolean {
100+
function isSetClassMetadataAsyncCall(callArguments: NodePath[]): boolean {
94101
// `setClassMetadataAsync` calls have to meet the following criteria:
95102
// * First argument must be an identifier.
96103
// * Second argument must be an inline function.
97104
// * Third argument must be an inline function.
98105
return (
99-
name === SET_CLASS_METADATA_ASYNC_NAME &&
100-
args.length === 3 &&
101-
types.isIdentifier(args[0]) &&
102-
isInlineFunction(args[1]) &&
103-
isInlineFunction(args[2])
106+
callArguments.length === 3 &&
107+
callArguments[0].isIdentifier() &&
108+
isInlineFunction(callArguments[1]) &&
109+
isInlineFunction(callArguments[2])
104110
);
105111
}
106112

107113
/** Determines if a function call is a call to `setClassDebugInfo`. */
108-
function isSetClassDebugInfoCall(name: string, args: types.CallExpression['arguments']): boolean {
114+
function isSetClassDebugInfoCall(callArguments: NodePath[]): boolean {
109115
return (
110-
name === SET_CLASS_DEBUG_INFO_NAME &&
111-
args.length === 2 &&
112-
types.isIdentifier(args[0]) &&
113-
types.isObjectExpression(args[1])
116+
callArguments.length === 2 &&
117+
callArguments[0].isIdentifier() &&
118+
callArguments[1].isObjectExpression()
114119
);
115120
}
116121

117122
/** Determines if a node is an inline function expression. */
118-
function isInlineFunction(node: types.Node): boolean {
119-
return types.isFunctionExpression(node) || types.isArrowFunctionExpression(node);
123+
function isInlineFunction(path: NodePath): boolean {
124+
return path.isFunctionExpression() || path.isArrowFunctionExpression();
120125
}

packages/angular_devkit/build_angular/src/tools/babel/plugins/pure-toplevel-functions.ts

+6-6
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
* found in the LICENSE file at https://angular.io/license
77
*/
88

9-
import { NodePath, PluginObj, types } from '@babel/core';
9+
import type { PluginObj } from '@babel/core';
1010
import annotateAsPure from '@babel/helper-annotate-as-pure';
1111
import * as tslib from 'tslib';
1212

@@ -40,28 +40,28 @@ function isTslibHelperName(name: string): boolean {
4040
export default function (): PluginObj {
4141
return {
4242
visitor: {
43-
CallExpression(path: NodePath<types.CallExpression>) {
43+
CallExpression(path) {
4444
// If the expression has a function parent, it is not top-level
4545
if (path.getFunctionParent()) {
4646
return;
4747
}
4848

49-
const callee = path.node.callee;
49+
const callee = path.get('callee');
5050
if (
51-
(types.isFunctionExpression(callee) || types.isArrowFunctionExpression(callee)) &&
51+
(callee.isFunctionExpression() || callee.isArrowFunctionExpression()) &&
5252
path.node.arguments.length !== 0
5353
) {
5454
return;
5555
}
5656
// Do not annotate TypeScript helpers emitted by the TypeScript compiler.
5757
// TypeScript helpers are intended to cause side effects.
58-
if (types.isIdentifier(callee) && isTslibHelperName(callee.name)) {
58+
if (callee.isIdentifier() && isTslibHelperName(callee.node.name)) {
5959
return;
6060
}
6161

6262
annotateAsPure(path);
6363
},
64-
NewExpression(path: NodePath<types.NewExpression>) {
64+
NewExpression(path) {
6565
// If the expression has a function parent, it is not top-level
6666
if (!path.getFunctionParent()) {
6767
annotateAsPure(path);

0 commit comments

Comments
 (0)