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 | */
|
---|
9 | var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
---|
10 | if (k2 === undefined) k2 = k;
|
---|
11 | Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
|
---|
12 | }) : (function(o, m, k, k2) {
|
---|
13 | if (k2 === undefined) k2 = k;
|
---|
14 | o[k2] = m[k];
|
---|
15 | }));
|
---|
16 | var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
---|
17 | Object.defineProperty(o, "default", { enumerable: true, value: v });
|
---|
18 | }) : function(o, v) {
|
---|
19 | o["default"] = v;
|
---|
20 | });
|
---|
21 | var __importStar = (this && this.__importStar) || function (mod) {
|
---|
22 | if (mod && mod.__esModule) return mod;
|
---|
23 | var result = {};
|
---|
24 | if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
---|
25 | __setModuleDefault(result, mod);
|
---|
26 | return result;
|
---|
27 | };
|
---|
28 | Object.defineProperty(exports, "__esModule", { value: true });
|
---|
29 | exports.getWrapEnumsTransformer = void 0;
|
---|
30 | const ts = __importStar(require("typescript"));
|
---|
31 | const ast_utils_1 = require("../helpers/ast-utils");
|
---|
32 | function isBlockLike(node) {
|
---|
33 | return (node.kind === ts.SyntaxKind.Block ||
|
---|
34 | node.kind === ts.SyntaxKind.ModuleBlock ||
|
---|
35 | node.kind === ts.SyntaxKind.CaseClause ||
|
---|
36 | node.kind === ts.SyntaxKind.DefaultClause ||
|
---|
37 | node.kind === ts.SyntaxKind.SourceFile);
|
---|
38 | }
|
---|
39 | function getWrapEnumsTransformer() {
|
---|
40 | return (context) => {
|
---|
41 | const transformer = (sf) => {
|
---|
42 | const result = visitBlockStatements(sf.statements, context);
|
---|
43 | return context.factory.updateSourceFile(sf, ts.setTextRange(result, sf.statements));
|
---|
44 | };
|
---|
45 | return transformer;
|
---|
46 | };
|
---|
47 | }
|
---|
48 | exports.getWrapEnumsTransformer = getWrapEnumsTransformer;
|
---|
49 | function visitBlockStatements(statements, context) {
|
---|
50 | // copy of statements to modify; lazy initialized
|
---|
51 | let updatedStatements;
|
---|
52 | const nodeFactory = context.factory;
|
---|
53 | const visitor = (node) => {
|
---|
54 | if (isBlockLike(node)) {
|
---|
55 | let result = visitBlockStatements(node.statements, context);
|
---|
56 | if (result === node.statements) {
|
---|
57 | return node;
|
---|
58 | }
|
---|
59 | result = ts.setTextRange(result, node.statements);
|
---|
60 | switch (node.kind) {
|
---|
61 | case ts.SyntaxKind.Block:
|
---|
62 | return nodeFactory.updateBlock(node, result);
|
---|
63 | case ts.SyntaxKind.ModuleBlock:
|
---|
64 | return nodeFactory.updateModuleBlock(node, result);
|
---|
65 | case ts.SyntaxKind.CaseClause:
|
---|
66 | return nodeFactory.updateCaseClause(node, node.expression, result);
|
---|
67 | case ts.SyntaxKind.DefaultClause:
|
---|
68 | return nodeFactory.updateDefaultClause(node, result);
|
---|
69 | default:
|
---|
70 | return node;
|
---|
71 | }
|
---|
72 | }
|
---|
73 | else {
|
---|
74 | return node;
|
---|
75 | }
|
---|
76 | };
|
---|
77 | // 'oIndex' is the original statement index; 'uIndex' is the updated statement index
|
---|
78 | for (let oIndex = 0, uIndex = 0; oIndex < statements.length - 1; oIndex++, uIndex++) {
|
---|
79 | const currentStatement = statements[oIndex];
|
---|
80 | let newStatement;
|
---|
81 | let oldStatementsLength = 0;
|
---|
82 | // these can't contain an enum declaration
|
---|
83 | if (currentStatement.kind === ts.SyntaxKind.ImportDeclaration) {
|
---|
84 | continue;
|
---|
85 | }
|
---|
86 | // enum declarations must:
|
---|
87 | // * not be last statement
|
---|
88 | // * be a variable statement
|
---|
89 | // * have only one declaration
|
---|
90 | // * have an identifer as a declaration name
|
---|
91 | // ClassExpression declarations must:
|
---|
92 | // * not be last statement
|
---|
93 | // * be a variable statement
|
---|
94 | // * have only one declaration
|
---|
95 | // * have an ClassExpression or BinaryExpression and a right
|
---|
96 | // of kind ClassExpression as a initializer
|
---|
97 | if (ts.isVariableStatement(currentStatement) &&
|
---|
98 | currentStatement.declarationList.declarations.length === 1) {
|
---|
99 | const variableDeclaration = currentStatement.declarationList.declarations[0];
|
---|
100 | const initializer = variableDeclaration.initializer;
|
---|
101 | if (ts.isIdentifier(variableDeclaration.name)) {
|
---|
102 | const name = variableDeclaration.name.text;
|
---|
103 | if (!initializer) {
|
---|
104 | const iife = findEnumIife(name, statements[oIndex + 1]);
|
---|
105 | if (iife) {
|
---|
106 | // update IIFE and replace variable statement and old IIFE
|
---|
107 | oldStatementsLength = 2;
|
---|
108 | newStatement = updateEnumIife(nodeFactory, currentStatement, iife[0], iife[1]);
|
---|
109 | // skip IIFE statement
|
---|
110 | oIndex++;
|
---|
111 | }
|
---|
112 | }
|
---|
113 | else if (ts.isClassExpression(initializer) ||
|
---|
114 | (ts.isBinaryExpression(initializer) && ts.isClassExpression(initializer.right))) {
|
---|
115 | const classStatements = findStatements(name, statements, oIndex);
|
---|
116 | if (!classStatements) {
|
---|
117 | continue;
|
---|
118 | }
|
---|
119 | oldStatementsLength = classStatements.length;
|
---|
120 | newStatement = createWrappedClass(nodeFactory, variableDeclaration, classStatements);
|
---|
121 | oIndex += classStatements.length - 1;
|
---|
122 | }
|
---|
123 | }
|
---|
124 | }
|
---|
125 | else if (ts.isClassDeclaration(currentStatement)) {
|
---|
126 | const name = currentStatement.name.text;
|
---|
127 | const classStatements = findStatements(name, statements, oIndex);
|
---|
128 | if (!classStatements) {
|
---|
129 | continue;
|
---|
130 | }
|
---|
131 | oldStatementsLength = classStatements.length;
|
---|
132 | newStatement = createWrappedClass(nodeFactory, currentStatement, classStatements);
|
---|
133 | oIndex += oldStatementsLength - 1;
|
---|
134 | }
|
---|
135 | if (newStatement && newStatement.length > 0) {
|
---|
136 | if (!updatedStatements) {
|
---|
137 | updatedStatements = [...statements];
|
---|
138 | }
|
---|
139 | updatedStatements.splice(uIndex, oldStatementsLength, ...newStatement);
|
---|
140 | // When having more than a single new statement
|
---|
141 | // we need to update the update Index
|
---|
142 | uIndex += newStatement ? newStatement.length - 1 : 0;
|
---|
143 | }
|
---|
144 | const result = ts.visitNode(currentStatement, visitor);
|
---|
145 | if (result !== currentStatement) {
|
---|
146 | if (!updatedStatements) {
|
---|
147 | updatedStatements = statements.slice();
|
---|
148 | }
|
---|
149 | updatedStatements[uIndex] = result;
|
---|
150 | }
|
---|
151 | }
|
---|
152 | // if changes, return updated statements
|
---|
153 | // otherwise, return original array instance
|
---|
154 | return updatedStatements ? nodeFactory.createNodeArray(updatedStatements) : statements;
|
---|
155 | }
|
---|
156 | // TS 2.3 enums have statements that are inside a IIFE.
|
---|
157 | function findEnumIife(name, statement) {
|
---|
158 | if (!ts.isExpressionStatement(statement)) {
|
---|
159 | return null;
|
---|
160 | }
|
---|
161 | const expression = statement.expression;
|
---|
162 | if (!expression || !ts.isCallExpression(expression) || expression.arguments.length !== 1) {
|
---|
163 | return null;
|
---|
164 | }
|
---|
165 | const callExpression = expression;
|
---|
166 | let exportExpression;
|
---|
167 | if (!ts.isParenthesizedExpression(callExpression.expression)) {
|
---|
168 | return null;
|
---|
169 | }
|
---|
170 | const functionExpression = callExpression.expression.expression;
|
---|
171 | if (!ts.isFunctionExpression(functionExpression)) {
|
---|
172 | return null;
|
---|
173 | }
|
---|
174 | // The name of the parameter can be different than the name of the enum if it was renamed
|
---|
175 | // due to scope hoisting.
|
---|
176 | const parameter = functionExpression.parameters[0];
|
---|
177 | if (!ts.isIdentifier(parameter.name)) {
|
---|
178 | return null;
|
---|
179 | }
|
---|
180 | const parameterName = parameter.name.text;
|
---|
181 | let argument = callExpression.arguments[0];
|
---|
182 | if (!ts.isBinaryExpression(argument) ||
|
---|
183 | !ts.isIdentifier(argument.left) ||
|
---|
184 | argument.left.text !== name) {
|
---|
185 | return null;
|
---|
186 | }
|
---|
187 | let potentialExport = false;
|
---|
188 | if (argument.operatorToken.kind === ts.SyntaxKind.FirstAssignment) {
|
---|
189 | if (ts.isBinaryExpression(argument.right) &&
|
---|
190 | argument.right.operatorToken.kind !== ts.SyntaxKind.BarBarToken) {
|
---|
191 | return null;
|
---|
192 | }
|
---|
193 | potentialExport = true;
|
---|
194 | argument = argument.right;
|
---|
195 | }
|
---|
196 | if (!ts.isBinaryExpression(argument)) {
|
---|
197 | return null;
|
---|
198 | }
|
---|
199 | if (argument.operatorToken.kind !== ts.SyntaxKind.BarBarToken) {
|
---|
200 | return null;
|
---|
201 | }
|
---|
202 | if (potentialExport && !ts.isIdentifier(argument.left)) {
|
---|
203 | exportExpression = argument.left;
|
---|
204 | }
|
---|
205 | // Go through all the statements and check that all match the name
|
---|
206 | for (const statement of functionExpression.body.statements) {
|
---|
207 | if (!ts.isExpressionStatement(statement) ||
|
---|
208 | !ts.isBinaryExpression(statement.expression) ||
|
---|
209 | !ts.isElementAccessExpression(statement.expression.left)) {
|
---|
210 | return null;
|
---|
211 | }
|
---|
212 | const leftExpression = statement.expression.left.expression;
|
---|
213 | if (!ts.isIdentifier(leftExpression) || leftExpression.text !== parameterName) {
|
---|
214 | return null;
|
---|
215 | }
|
---|
216 | }
|
---|
217 | return [callExpression, exportExpression];
|
---|
218 | }
|
---|
219 | function updateHostNode(nodeFactory, hostNode, expression) {
|
---|
220 | // Update existing host node with the pure comment before the variable declaration initializer.
|
---|
221 | const variableDeclaration = hostNode.declarationList.declarations[0];
|
---|
222 | const outerVarStmt = nodeFactory.updateVariableStatement(hostNode, hostNode.modifiers, nodeFactory.updateVariableDeclarationList(hostNode.declarationList, [
|
---|
223 | nodeFactory.updateVariableDeclaration(variableDeclaration, variableDeclaration.name, variableDeclaration.exclamationToken, variableDeclaration.type, expression),
|
---|
224 | ]));
|
---|
225 | return outerVarStmt;
|
---|
226 | }
|
---|
227 | /**
|
---|
228 | * Find enums, class expression or declaration statements.
|
---|
229 | *
|
---|
230 | * The classExpressions block to wrap in an iife must
|
---|
231 | * - end with an ExpressionStatement
|
---|
232 | * - it's expression must be a BinaryExpression
|
---|
233 | * - have the same name
|
---|
234 | *
|
---|
235 | * ```
|
---|
236 | let Foo = class Foo {};
|
---|
237 | Foo = __decorate([]);
|
---|
238 | ```
|
---|
239 | */
|
---|
240 | function findStatements(name, statements, statementIndex, offset = 0) {
|
---|
241 | let count = 1;
|
---|
242 | for (let index = statementIndex + 1; index < statements.length; ++index) {
|
---|
243 | const statement = statements[index];
|
---|
244 | if (!ts.isExpressionStatement(statement)) {
|
---|
245 | break;
|
---|
246 | }
|
---|
247 | const expression = statement.expression;
|
---|
248 | if (ts.isCallExpression(expression)) {
|
---|
249 | // Ex:
|
---|
250 | // setClassMetadata(FooClass, [{}], void 0);
|
---|
251 | // __decorate([propDecorator()], FooClass.prototype, "propertyName", void 0);
|
---|
252 | // __decorate([propDecorator()], FooClass, "propertyName", void 0);
|
---|
253 | // __decorate$1([propDecorator()], FooClass, "propertyName", void 0);
|
---|
254 | const args = expression.arguments;
|
---|
255 | if (args.length > 2) {
|
---|
256 | const isReferenced = args.some((arg) => {
|
---|
257 | const potentialIdentifier = ts.isPropertyAccessExpression(arg) ? arg.expression : arg;
|
---|
258 | return ts.isIdentifier(potentialIdentifier) && potentialIdentifier.text === name;
|
---|
259 | });
|
---|
260 | if (isReferenced) {
|
---|
261 | count++;
|
---|
262 | continue;
|
---|
263 | }
|
---|
264 | }
|
---|
265 | }
|
---|
266 | else if (ts.isBinaryExpression(expression)) {
|
---|
267 | const node = ts.isBinaryExpression(expression.left) ? expression.left.left : expression.left;
|
---|
268 | const leftExpression = ts.isPropertyAccessExpression(node) || ts.isElementAccessExpression(node)
|
---|
269 | ? // Static Properties // Ex: Foo.bar = 'value';
|
---|
270 | // ENUM Property // Ex: ChangeDetectionStrategy[ChangeDetectionStrategy.Default] = "Default";
|
---|
271 | node.expression
|
---|
272 | : // Ex: FooClass = __decorate([Component()], FooClass);
|
---|
273 | node;
|
---|
274 | if (ts.isIdentifier(leftExpression) && leftExpression.text === name) {
|
---|
275 | count++;
|
---|
276 | continue;
|
---|
277 | }
|
---|
278 | }
|
---|
279 | break;
|
---|
280 | }
|
---|
281 | if (count > 1) {
|
---|
282 | return statements.slice(statementIndex + offset, statementIndex + count);
|
---|
283 | }
|
---|
284 | return undefined;
|
---|
285 | }
|
---|
286 | function updateEnumIife(nodeFactory, hostNode, iife, exportAssignment) {
|
---|
287 | if (!ts.isParenthesizedExpression(iife.expression) ||
|
---|
288 | !ts.isFunctionExpression(iife.expression.expression)) {
|
---|
289 | throw new Error('Invalid IIFE Structure');
|
---|
290 | }
|
---|
291 | // Ignore export assignment if variable is directly exported
|
---|
292 | if (hostNode.modifiers &&
|
---|
293 | hostNode.modifiers.findIndex((m) => m.kind == ts.SyntaxKind.ExportKeyword) != -1) {
|
---|
294 | exportAssignment = undefined;
|
---|
295 | }
|
---|
296 | const expression = iife.expression.expression;
|
---|
297 | const updatedFunction = nodeFactory.updateFunctionExpression(expression, expression.modifiers, expression.asteriskToken, expression.name, expression.typeParameters, expression.parameters, expression.type, nodeFactory.updateBlock(expression.body, [
|
---|
298 | ...expression.body.statements,
|
---|
299 | nodeFactory.createReturnStatement(expression.parameters[0].name),
|
---|
300 | ]));
|
---|
301 | let arg = nodeFactory.createObjectLiteralExpression();
|
---|
302 | if (exportAssignment) {
|
---|
303 | arg = nodeFactory.createBinaryExpression(exportAssignment, ts.SyntaxKind.BarBarToken, arg);
|
---|
304 | }
|
---|
305 | const updatedIife = nodeFactory.updateCallExpression(iife, nodeFactory.updateParenthesizedExpression(iife.expression, updatedFunction), iife.typeArguments, [arg]);
|
---|
306 | let value = ast_utils_1.addPureComment(updatedIife);
|
---|
307 | if (exportAssignment) {
|
---|
308 | value = nodeFactory.createBinaryExpression(exportAssignment, ts.SyntaxKind.FirstAssignment, updatedIife);
|
---|
309 | }
|
---|
310 | return [updateHostNode(nodeFactory, hostNode, value)];
|
---|
311 | }
|
---|
312 | function createWrappedClass(nodeFactory, hostNode, statements) {
|
---|
313 | const name = hostNode.name.text;
|
---|
314 | const updatedStatements = [...statements];
|
---|
315 | if (ts.isClassDeclaration(hostNode)) {
|
---|
316 | updatedStatements[0] = nodeFactory.createClassDeclaration(hostNode.decorators, undefined, hostNode.name, hostNode.typeParameters, hostNode.heritageClauses, hostNode.members);
|
---|
317 | }
|
---|
318 | const pureIife = ast_utils_1.addPureComment(nodeFactory.createImmediatelyInvokedArrowFunction([
|
---|
319 | ...updatedStatements,
|
---|
320 | nodeFactory.createReturnStatement(nodeFactory.createIdentifier(name)),
|
---|
321 | ]));
|
---|
322 | const modifiers = hostNode.modifiers;
|
---|
323 | const isDefault = !!modifiers && modifiers.some((x) => x.kind === ts.SyntaxKind.DefaultKeyword);
|
---|
324 | const newStatement = [];
|
---|
325 | newStatement.push(nodeFactory.createVariableStatement(isDefault ? undefined : modifiers, nodeFactory.createVariableDeclarationList([nodeFactory.createVariableDeclaration(name, undefined, undefined, pureIife)], ts.NodeFlags.Let)));
|
---|
326 | if (isDefault) {
|
---|
327 | newStatement.push(nodeFactory.createExportAssignment(undefined, undefined, false, nodeFactory.createIdentifier(name)));
|
---|
328 | }
|
---|
329 | return newStatement;
|
---|
330 | }
|
---|