source: trip-planner-front/node_modules/@angular-devkit/build-angular/src/babel/plugins/adjust-static-class-members.js@ 6a3a178

Last change on this file since 6a3a178 was 6a3a178, checked in by Ema <ema_spirova@…>, 3 years ago

initial commit

  • Property mode set to 100644
File size: 12.4 KB
Line 
1"use strict";
2/**
3 * @license
4 * Copyright Google LLC All Rights Reserved.
5 *
6 * Use of this source code is governed by an MIT-style license that can be
7 * found in the LICENSE file at https://angular.io/license
8 */
9var __importDefault = (this && this.__importDefault) || function (mod) {
10 return (mod && mod.__esModule) ? mod : { "default": mod };
11};
12Object.defineProperty(exports, "__esModule", { value: true });
13exports.getKeywords = void 0;
14const core_1 = require("@babel/core");
15const helper_annotate_as_pure_1 = __importDefault(require("@babel/helper-annotate-as-pure"));
16/**
17 * The name of the Typescript decorator helper function created by the TypeScript compiler.
18 */
19const TSLIB_DECORATE_HELPER_NAME = '__decorate';
20/**
21 * The set of Angular static fields that should always be wrapped.
22 * These fields may appear to have side effects but are safe to remove if the associated class
23 * is otherwise unused within the output.
24 */
25const angularStaticsToWrap = new Set([
26 'ɵcmp',
27 'ɵdir',
28 'ɵfac',
29 'ɵinj',
30 'ɵmod',
31 'ɵpipe',
32 'ɵprov',
33 'INJECTOR_KEY',
34]);
35/**
36 * An object map of static fields and related value checks for discovery of Angular generated
37 * JIT related static fields.
38 */
39const angularStaticsToElide = {
40 'ctorParameters'(path) {
41 return path.isFunctionExpression() || path.isArrowFunctionExpression();
42 },
43 'decorators'(path) {
44 return path.isArrayExpression();
45 },
46 'propDecorators'(path) {
47 return path.isObjectExpression();
48 },
49};
50/**
51 * Provides one or more keywords that if found within the content of a source file indicate
52 * that this plugin should be used with a source file.
53 *
54 * @returns An a string iterable containing one or more keywords.
55 */
56function getKeywords() {
57 return ['class'];
58}
59exports.getKeywords = getKeywords;
60/**
61 * Determines whether a property and its initializer value can be safely wrapped in a pure
62 * annotated IIFE. Values that may cause side effects are not considered safe to wrap.
63 * Wrapping such values may cause runtime errors and/or incorrect runtime behavior.
64 *
65 * @param propertyName The name of the property to analyze.
66 * @param assignmentValue The initializer value that will be assigned to the property.
67 * @returns If the property can be safely wrapped, then true; otherwise, false.
68 */
69function canWrapProperty(propertyName, assignmentValue) {
70 if (angularStaticsToWrap.has(propertyName)) {
71 return true;
72 }
73 const { leadingComments } = assignmentValue.node;
74 if (leadingComments === null || leadingComments === void 0 ? void 0 : leadingComments.some(
75 // `@pureOrBreakMyCode` is used by closure and is present in Angular code
76 ({ value }) => value.includes('@__PURE__') ||
77 value.includes('#__PURE__') ||
78 value.includes('@pureOrBreakMyCode'))) {
79 return true;
80 }
81 return assignmentValue.isPure();
82}
83/**
84 * Analyze the sibling nodes of a class to determine if any downlevel elements should be
85 * wrapped in a pure annotated IIFE. Also determines if any elements have potential side
86 * effects.
87 *
88 * @param origin The starting NodePath location for analyzing siblings.
89 * @param classIdentifier The identifier node that represents the name of the class.
90 * @param allowWrappingDecorators Whether to allow decorators to be wrapped.
91 * @returns An object containing the results of the analysis.
92 */
93function analyzeClassSiblings(origin, classIdentifier, allowWrappingDecorators) {
94 var _a;
95 const wrapStatementPaths = [];
96 let hasPotentialSideEffects = false;
97 for (let i = 1;; ++i) {
98 const nextStatement = origin.getSibling(+origin.key + i);
99 if (!nextStatement.isExpressionStatement()) {
100 break;
101 }
102 // Valid sibling statements for class declarations are only assignment expressions
103 // and TypeScript decorator helper call expressions
104 const nextExpression = nextStatement.get('expression');
105 if (nextExpression.isCallExpression()) {
106 if (!core_1.types.isIdentifier(nextExpression.node.callee) ||
107 nextExpression.node.callee.name !== TSLIB_DECORATE_HELPER_NAME) {
108 break;
109 }
110 if (allowWrappingDecorators) {
111 wrapStatementPaths.push(nextStatement);
112 }
113 else {
114 // Statement cannot be safely wrapped which makes wrapping the class unneeded.
115 // The statement will prevent even a wrapped class from being optimized away.
116 hasPotentialSideEffects = true;
117 }
118 continue;
119 }
120 else if (!nextExpression.isAssignmentExpression()) {
121 break;
122 }
123 // Valid assignment expressions should be member access expressions using the class
124 // name as the object and an identifier as the property for static fields or only
125 // the class name for decorators.
126 const left = nextExpression.get('left');
127 if (left.isIdentifier()) {
128 if (!left.scope.bindingIdentifierEquals(left.node.name, classIdentifier) ||
129 !core_1.types.isCallExpression(nextExpression.node.right) ||
130 !core_1.types.isIdentifier(nextExpression.node.right.callee) ||
131 nextExpression.node.right.callee.name !== TSLIB_DECORATE_HELPER_NAME) {
132 break;
133 }
134 if (allowWrappingDecorators) {
135 wrapStatementPaths.push(nextStatement);
136 }
137 else {
138 // Statement cannot be safely wrapped which makes wrapping the class unneeded.
139 // The statement will prevent even a wrapped class from being optimized away.
140 hasPotentialSideEffects = true;
141 }
142 continue;
143 }
144 else if (!left.isMemberExpression() ||
145 !core_1.types.isIdentifier(left.node.object) ||
146 !left.scope.bindingIdentifierEquals(left.node.object.name, classIdentifier) ||
147 !core_1.types.isIdentifier(left.node.property)) {
148 break;
149 }
150 const propertyName = left.node.property.name;
151 const assignmentValue = nextExpression.get('right');
152 if ((_a = angularStaticsToElide[propertyName]) === null || _a === void 0 ? void 0 : _a.call(angularStaticsToElide, assignmentValue)) {
153 nextStatement.remove();
154 --i;
155 }
156 else if (canWrapProperty(propertyName, assignmentValue)) {
157 wrapStatementPaths.push(nextStatement);
158 }
159 else {
160 // Statement cannot be safely wrapped which makes wrapping the class unneeded.
161 // The statement will prevent even a wrapped class from being optimized away.
162 hasPotentialSideEffects = true;
163 }
164 }
165 return { hasPotentialSideEffects, wrapStatementPaths };
166}
167/**
168 * The set of classed already visited and analyzed during the plugin's execution.
169 * This is used to prevent adjusted classes from being repeatedly analyzed which can lead
170 * to an infinite loop.
171 */
172const visitedClasses = new WeakSet();
173/**
174 * A babel plugin factory function for adjusting classes; primarily with Angular metadata.
175 * The adjustments include wrapping classes with known safe or no side effects with pure
176 * annotations to support dead code removal of unused classes. Angular compiler generated
177 * metadata static fields not required in AOT mode are also elided to better support bundler-
178 * level treeshaking.
179 *
180 * @returns A babel plugin object instance.
181 */
182function default_1() {
183 return {
184 visitor: {
185 ClassDeclaration(path, state) {
186 const { node: classNode, parentPath } = path;
187 const { wrapDecorators } = state.opts;
188 if (visitedClasses.has(classNode)) {
189 return;
190 }
191 // Analyze sibling statements for elements of the class that were downleveled
192 const hasExport = parentPath.isExportNamedDeclaration() || parentPath.isExportDefaultDeclaration();
193 const origin = hasExport ? parentPath : path;
194 const { wrapStatementPaths, hasPotentialSideEffects } = analyzeClassSiblings(origin, classNode.id, wrapDecorators);
195 visitedClasses.add(classNode);
196 if (hasPotentialSideEffects || wrapStatementPaths.length === 0) {
197 return;
198 }
199 const wrapStatementNodes = [];
200 for (const statementPath of wrapStatementPaths) {
201 wrapStatementNodes.push(statementPath.node);
202 statementPath.remove();
203 }
204 // Wrap class and safe static assignments in a pure annotated IIFE
205 const container = core_1.types.arrowFunctionExpression([], core_1.types.blockStatement([
206 classNode,
207 ...wrapStatementNodes,
208 core_1.types.returnStatement(core_1.types.cloneNode(classNode.id)),
209 ]));
210 const replacementInitializer = core_1.types.callExpression(core_1.types.parenthesizedExpression(container), []);
211 helper_annotate_as_pure_1.default(replacementInitializer);
212 // Replace class with IIFE wrapped class
213 const declaration = core_1.types.variableDeclaration('let', [
214 core_1.types.variableDeclarator(core_1.types.cloneNode(classNode.id), replacementInitializer),
215 ]);
216 if (parentPath.isExportDefaultDeclaration()) {
217 // When converted to a variable declaration, the default export must be moved
218 // to a subsequent statement to prevent a JavaScript syntax error.
219 parentPath.replaceWithMultiple([
220 declaration,
221 core_1.types.exportNamedDeclaration(undefined, [
222 core_1.types.exportSpecifier(core_1.types.cloneNode(classNode.id), core_1.types.identifier('default')),
223 ]),
224 ]);
225 }
226 else {
227 path.replaceWith(declaration);
228 }
229 },
230 ClassExpression(path, state) {
231 const { node: classNode, parentPath } = path;
232 const { wrapDecorators } = state.opts;
233 // Class expressions are used by TypeScript to represent downlevel class/constructor decorators.
234 // If not wrapping decorators, they do not need to be processed.
235 if (!wrapDecorators || visitedClasses.has(classNode)) {
236 return;
237 }
238 if (!classNode.id ||
239 !parentPath.isVariableDeclarator() ||
240 !core_1.types.isIdentifier(parentPath.node.id) ||
241 parentPath.node.id.name !== classNode.id.name) {
242 return;
243 }
244 const origin = parentPath.parentPath;
245 if (!origin.isVariableDeclaration() || origin.node.declarations.length !== 1) {
246 return;
247 }
248 const { wrapStatementPaths, hasPotentialSideEffects } = analyzeClassSiblings(origin, parentPath.node.id, wrapDecorators);
249 visitedClasses.add(classNode);
250 if (hasPotentialSideEffects || wrapStatementPaths.length === 0) {
251 return;
252 }
253 const wrapStatementNodes = [];
254 for (const statementPath of wrapStatementPaths) {
255 wrapStatementNodes.push(statementPath.node);
256 statementPath.remove();
257 }
258 // Wrap class and safe static assignments in a pure annotated IIFE
259 const container = core_1.types.arrowFunctionExpression([], core_1.types.blockStatement([
260 core_1.types.variableDeclaration('let', [
261 core_1.types.variableDeclarator(core_1.types.cloneNode(classNode.id), classNode),
262 ]),
263 ...wrapStatementNodes,
264 core_1.types.returnStatement(core_1.types.cloneNode(classNode.id)),
265 ]));
266 const replacementInitializer = core_1.types.callExpression(core_1.types.parenthesizedExpression(container), []);
267 helper_annotate_as_pure_1.default(replacementInitializer);
268 // Add the wrapped class directly to the variable declaration
269 parentPath.get('init').replaceWith(replacementInitializer);
270 },
271 },
272 };
273}
274exports.default = default_1;
Note: See TracBrowser for help on using the repository browser.