UNPKG

28.6 kBJavaScriptView Raw
1"use strict";
2var __values = (this && this.__values) || function(o) {
3 var s = typeof Symbol === "function" && Symbol.iterator, m = s && o[s], i = 0;
4 if (m) return m.call(o);
5 if (o && typeof o.length === "number") return {
6 next: function () {
7 if (o && i >= o.length) o = void 0;
8 return { value: o && o[i++], done: !o };
9 }
10 };
11 throw new TypeError(s ? "Object is not iterable." : "Symbol.iterator is not defined.");
12};
13var __read = (this && this.__read) || function (o, n) {
14 var m = typeof Symbol === "function" && o[Symbol.iterator];
15 if (!m) return o;
16 var i = m.call(o), r, ar = [], e;
17 try {
18 while ((n === void 0 || n-- > 0) && !(r = i.next()).done) ar.push(r.value);
19 }
20 catch (error) { e = { error: error }; }
21 finally {
22 try {
23 if (r && !r.done && (m = i["return"])) m.call(i);
24 }
25 finally { if (e) throw e.error; }
26 }
27 return ar;
28};
29var __spreadArray = (this && this.__spreadArray) || function (to, from) {
30 for (var i = 0, il = from.length, j = to.length; i < il; i++, j++)
31 to[j] = from[i];
32 return to;
33};
34exports.__esModule = true;
35exports.containsProperty = exports.replaceImport = exports.insertImport = exports.addBootstrapToModule = exports.addExportToModule = exports.addProviderToComponent = exports.addProviderToModule = exports.addImportToModule = exports.addDeclarationToModule = exports.getDecoratorMetadata = exports.getContentOfKeyLiteral = exports.insertAfterLastOccurrence = exports.getSourceNodes = exports.findNodes = void 0;
36/* istanbul ignore file */
37/**
38 * @license
39 * Copyright Google Inc. All Rights Reserved.
40 *
41 * Use of this source code is governed by an MIT-style license that can be
42 * found in the LICENSE file at https://angular.io/license
43 */
44var ts = require("typescript");
45var change_1 = require("./change");
46/**
47 * Find all nodes from the AST in the subtree of node of SyntaxKind kind.
48 * @param node
49 * @param kind
50 * @param max The maximum number of items to return.
51 * @return all nodes of kind, or [] if none is found
52 */
53function findNodes(node, kind, max) {
54 var e_1, _a;
55 if (max === void 0) { max = Infinity; }
56 if (!node || max == 0) {
57 return [];
58 }
59 var arr = [];
60 if (node.kind === kind) {
61 arr.push(node);
62 max--;
63 }
64 if (max > 0) {
65 try {
66 for (var _b = __values(node.getChildren()), _c = _b.next(); !_c.done; _c = _b.next()) {
67 var child = _c.value;
68 findNodes(child, kind, max).forEach(function (node) {
69 if (max > 0) {
70 arr.push(node);
71 }
72 max--;
73 });
74 if (max <= 0) {
75 break;
76 }
77 }
78 }
79 catch (e_1_1) { e_1 = { error: e_1_1 }; }
80 finally {
81 try {
82 if (_c && !_c.done && (_a = _b["return"])) _a.call(_b);
83 }
84 finally { if (e_1) throw e_1.error; }
85 }
86 }
87 return arr;
88}
89exports.findNodes = findNodes;
90/**
91 * Get all the nodes from a source.
92 * @param sourceFile The source file object.
93 * @returns {Observable<ts.Node>} An observable of all the nodes in the source.
94 */
95function getSourceNodes(sourceFile) {
96 var nodes = [sourceFile];
97 var result = [];
98 while (nodes.length > 0) {
99 var node = nodes.shift();
100 if (node) {
101 result.push(node);
102 if (node.getChildCount(sourceFile) >= 0) {
103 nodes.unshift.apply(nodes, __spreadArray([], __read(node.getChildren())));
104 }
105 }
106 }
107 return result;
108}
109exports.getSourceNodes = getSourceNodes;
110/**
111 * Helper for sorting nodes.
112 * @return function to sort nodes in increasing order of position in sourceFile
113 */
114function nodesByPosition(first, second) {
115 return first.pos - second.pos;
116}
117/**
118 * Insert `toInsert` after the last occurence of `ts.SyntaxKind[nodes[i].kind]`
119 * or after the last of occurence of `syntaxKind` if the last occurence is a sub child
120 * of ts.SyntaxKind[nodes[i].kind] and save the changes in file.
121 *
122 * @param nodes insert after the last occurence of nodes
123 * @param toInsert string to insert
124 * @param file file to insert changes into
125 * @param fallbackPos position to insert if toInsert happens to be the first occurence
126 * @param syntaxKind the ts.SyntaxKind of the subchildren to insert after
127 * @return Change instance
128 * @throw Error if toInsert is first occurence but fall back is not set
129 */
130function insertAfterLastOccurrence(nodes, toInsert, file, fallbackPos, syntaxKind) {
131 var lastItem = nodes.sort(nodesByPosition).pop();
132 if (!lastItem) {
133 throw new Error();
134 }
135 if (syntaxKind) {
136 lastItem = findNodes(lastItem, syntaxKind).sort(nodesByPosition).pop();
137 }
138 if (!lastItem && fallbackPos == undefined) {
139 throw new Error("tried to insert " + toInsert + " as first occurence with no fallback position");
140 }
141 var lastItemPosition = lastItem ? lastItem.end : fallbackPos;
142 return new change_1.InsertChange(file, lastItemPosition, toInsert);
143}
144exports.insertAfterLastOccurrence = insertAfterLastOccurrence;
145function getContentOfKeyLiteral(_source, node) {
146 if (node.kind == ts.SyntaxKind.Identifier) {
147 return node.text;
148 }
149 else if (node.kind == ts.SyntaxKind.StringLiteral) {
150 return node.text;
151 }
152 else {
153 return null;
154 }
155}
156exports.getContentOfKeyLiteral = getContentOfKeyLiteral;
157function _angularImportsFromNode(node, _sourceFile) {
158 var _a;
159 var ms = node.moduleSpecifier;
160 var modulePath;
161 switch (ms.kind) {
162 case ts.SyntaxKind.StringLiteral:
163 modulePath = ms.text;
164 break;
165 default:
166 return {};
167 }
168 if (!modulePath.startsWith('@angular/')) {
169 return {};
170 }
171 if (node.importClause) {
172 if (node.importClause.name) {
173 // This is of the form `import Name from 'path'`. Ignore.
174 return {};
175 }
176 else if (node.importClause.namedBindings) {
177 var nb = node.importClause.namedBindings;
178 if (nb.kind == ts.SyntaxKind.NamespaceImport) {
179 // This is of the form `import * as name from 'path'`. Return `name.`.
180 return _a = {},
181 _a[nb.name.text + '.'] = modulePath,
182 _a;
183 }
184 else {
185 // This is of the form `import {a,b,c} from 'path'`
186 var namedImports = nb;
187 return namedImports.elements
188 .map(function (is) {
189 return is.propertyName ? is.propertyName.text : is.name.text;
190 })
191 .reduce(function (acc, curr) {
192 acc[curr] = modulePath;
193 return acc;
194 }, {});
195 }
196 }
197 return {};
198 }
199 else {
200 // This is of the form `import 'path';`. Nothing to do.
201 return {};
202 }
203}
204function getDecoratorMetadata(source, identifier, module) {
205 var angularImports = findNodes(source, ts.SyntaxKind.ImportDeclaration)
206 .map(function (node) {
207 return _angularImportsFromNode(node, source);
208 })
209 .reduce(function (acc, current) {
210 var e_2, _a;
211 try {
212 for (var _b = __values(Object.keys(current)), _c = _b.next(); !_c.done; _c = _b.next()) {
213 var key = _c.value;
214 acc[key] = current[key];
215 }
216 }
217 catch (e_2_1) { e_2 = { error: e_2_1 }; }
218 finally {
219 try {
220 if (_c && !_c.done && (_a = _b["return"])) _a.call(_b);
221 }
222 finally { if (e_2) throw e_2.error; }
223 }
224 return acc;
225 }, {});
226 return getSourceNodes(source)
227 .filter(function (node) {
228 return (node.kind == ts.SyntaxKind.Decorator &&
229 node.expression.kind == ts.SyntaxKind.CallExpression);
230 })
231 .map(function (node) { return node.expression; })
232 .filter(function (expr) {
233 if (expr.expression.kind == ts.SyntaxKind.Identifier) {
234 var id = expr.expression;
235 return (id.getFullText(source) == identifier &&
236 angularImports[id.getFullText(source)] === module);
237 }
238 else if (expr.expression.kind == ts.SyntaxKind.PropertyAccessExpression) {
239 // This covers foo.NgModule when importing * as foo.
240 var paExpr = expr.expression;
241 // If the left expression is not an identifier, just give up at that point.
242 if (paExpr.expression.kind !== ts.SyntaxKind.Identifier) {
243 return false;
244 }
245 var id = paExpr.name.text;
246 var moduleId = paExpr.expression.getText(source);
247 return id === identifier && angularImports[moduleId + '.'] === module;
248 }
249 return false;
250 })
251 .filter(function (expr) {
252 return expr.arguments[0] &&
253 expr.arguments[0].kind == ts.SyntaxKind.ObjectLiteralExpression;
254 })
255 .map(function (expr) { return expr.arguments[0]; });
256}
257exports.getDecoratorMetadata = getDecoratorMetadata;
258function _addSymbolToNgModuleMetadata(source, ngModulePath, metadataField, symbolName, importPath) {
259 var nodes = getDecoratorMetadata(source, 'NgModule', '@angular/core');
260 var node = nodes[0]; // eslint-disable-line @typescript-eslint/no-explicit-any
261 // Find the decorator declaration.
262 if (!node) {
263 return [];
264 }
265 // Get all the children property assignment of object literals.
266 var matchingProperties = node.properties
267 .filter(function (prop) { return prop.kind == ts.SyntaxKind.PropertyAssignment; })
268 // Filter out every fields that's not "metadataField". Also handles string literals
269 // (but not expressions).
270 .filter(function (prop) {
271 var name = prop.name;
272 switch (name.kind) {
273 case ts.SyntaxKind.Identifier:
274 return name.getText(source) == metadataField;
275 case ts.SyntaxKind.StringLiteral:
276 return name.text == metadataField;
277 }
278 return false;
279 });
280 // Get the last node of the array literal.
281 if (!matchingProperties) {
282 return [];
283 }
284 if (matchingProperties.length == 0) {
285 // We haven't found the field in the metadata declaration. Insert a new field.
286 var expr = node;
287 var position_1;
288 var toInsert_1;
289 if (expr.properties.length == 0) {
290 position_1 = expr.getEnd() - 1;
291 toInsert_1 = " " + metadataField + ": [" + symbolName + "]\n";
292 }
293 else {
294 node = expr.properties[expr.properties.length - 1];
295 position_1 = node.getEnd();
296 // Get the indentation of the last element, if any.
297 var text = node.getFullText(source);
298 var matches = text.match(/^\r?\n\s*/);
299 if (matches.length > 0) {
300 toInsert_1 = "," + matches[0] + metadataField + ": [" + symbolName + "]";
301 }
302 else {
303 toInsert_1 = ", " + metadataField + ": [" + symbolName + "]";
304 }
305 }
306 var newMetadataProperty = new change_1.InsertChange(ngModulePath, position_1, toInsert_1);
307 var newMetadataImport = insertImport(source, ngModulePath, symbolName.replace(/\..*$/, ''), importPath);
308 return [newMetadataProperty, newMetadataImport];
309 }
310 var assignment = matchingProperties[0];
311 // If it's not an array, nothing we can do really.
312 if (assignment.initializer.kind !== ts.SyntaxKind.ArrayLiteralExpression) {
313 return [];
314 }
315 var arrLiteral = assignment.initializer;
316 if (arrLiteral.elements.length == 0) {
317 // Forward the property.
318 node = arrLiteral;
319 }
320 else {
321 node = arrLiteral.elements;
322 }
323 if (!node) {
324 console.log('No app module found. Please add your new class to your component.');
325 return [];
326 }
327 if (Array.isArray(node)) {
328 var nodeArray = node;
329 var symbolsArray = nodeArray.map(function (node) { return node.getText(); });
330 if (symbolsArray.includes(symbolName)) {
331 return [];
332 }
333 node = node[node.length - 1];
334 var effectsModule = nodeArray.find(function (node) {
335 return (node.getText().includes('EffectsModule.forRoot') &&
336 symbolName.includes('EffectsModule.forRoot')) ||
337 (node.getText().includes('EffectsModule.forFeature') &&
338 symbolName.includes('EffectsModule.forFeature'));
339 });
340 if (effectsModule && symbolName.includes('EffectsModule')) {
341 var effectsArgs = effectsModule.arguments.shift();
342 if (effectsArgs &&
343 effectsArgs.kind === ts.SyntaxKind.ArrayLiteralExpression) {
344 var effectsElements = effectsArgs
345 .elements;
346 var _a = __read(symbolName.match(/\[(.*)\]/), 2), effectsSymbol = _a[1];
347 var epos = void 0;
348 if (effectsElements.length === 0) {
349 epos = effectsArgs.getStart() + 1;
350 return [new change_1.InsertChange(ngModulePath, epos, effectsSymbol)];
351 }
352 else {
353 var lastEffect = effectsElements[effectsElements.length - 1];
354 epos = lastEffect.getEnd();
355 // Get the indentation of the last element, if any.
356 var text = lastEffect.getFullText(source);
357 var effectInsert = void 0;
358 if (text.match('^\r?\r?\n')) {
359 effectInsert = "," + text.match(/^\r?\n\s+/)[0] + effectsSymbol;
360 }
361 else {
362 effectInsert = ", " + effectsSymbol;
363 }
364 return [new change_1.InsertChange(ngModulePath, epos, effectInsert)];
365 }
366 }
367 else {
368 return [];
369 }
370 }
371 }
372 var toInsert;
373 var position = node.getEnd();
374 if (node.kind == ts.SyntaxKind.ObjectLiteralExpression) {
375 // We haven't found the field in the metadata declaration. Insert a new
376 // field.
377 var expr = node;
378 if (expr.properties.length == 0) {
379 position = expr.getEnd() - 1;
380 toInsert = " " + metadataField + ": [" + symbolName + "]\n";
381 }
382 else {
383 node = expr.properties[expr.properties.length - 1];
384 position = node.getEnd();
385 // Get the indentation of the last element, if any.
386 var text = node.getFullText(source);
387 if (text.match('^\r?\r?\n')) {
388 toInsert = "," + text.match(/^\r?\n\s+/)[0] + metadataField + ": [" + symbolName + "]";
389 }
390 else {
391 toInsert = ", " + metadataField + ": [" + symbolName + "]";
392 }
393 }
394 }
395 else if (node.kind == ts.SyntaxKind.ArrayLiteralExpression) {
396 // We found the field but it's empty. Insert it just before the `]`.
397 position--;
398 toInsert = "" + symbolName;
399 }
400 else {
401 // Get the indentation of the last element, if any.
402 var text = node.getFullText(source);
403 if (text.match(/^\r?\n/)) {
404 toInsert = "," + text.match(/^\r?\n(\r?)\s+/)[0] + symbolName;
405 }
406 else {
407 toInsert = ", " + symbolName;
408 }
409 }
410 var insert = new change_1.InsertChange(ngModulePath, position, toInsert);
411 var importInsert = insertImport(source, ngModulePath, symbolName.replace(/\..*$/, ''), importPath);
412 return [insert, importInsert];
413}
414function _addSymbolToComponentMetadata(source, componentPath, metadataField, symbolName, importPath) {
415 var nodes = getDecoratorMetadata(source, 'Component', '@angular/core');
416 var node = nodes[0]; // eslint-disable-line @typescript-eslint/no-explicit-any
417 // Find the decorator declaration.
418 if (!node) {
419 return [];
420 }
421 // Get all the children property assignment of object literals.
422 var matchingProperties = node.properties
423 .filter(function (prop) { return prop.kind == ts.SyntaxKind.PropertyAssignment; })
424 // Filter out every fields that's not "metadataField". Also handles string literals
425 // (but not expressions).
426 .filter(function (prop) {
427 var name = prop.name;
428 switch (name.kind) {
429 case ts.SyntaxKind.Identifier:
430 return name.getText(source) == metadataField;
431 case ts.SyntaxKind.StringLiteral:
432 return name.text == metadataField;
433 }
434 return false;
435 });
436 // Get the last node of the array literal.
437 if (!matchingProperties) {
438 return [];
439 }
440 if (matchingProperties.length == 0) {
441 // We haven't found the field in the metadata declaration. Insert a new field.
442 var expr = node;
443 var position_2;
444 var toInsert_2;
445 if (expr.properties.length == 0) {
446 position_2 = expr.getEnd() - 1;
447 toInsert_2 = " " + metadataField + ": [" + symbolName + "]\n";
448 }
449 else {
450 node = expr.properties[expr.properties.length - 1];
451 position_2 = node.getEnd();
452 // Get the indentation of the last element, if any.
453 var text = node.getFullText(source);
454 var matches = text.match(/^\r?\n\s*/);
455 if (matches.length > 0) {
456 toInsert_2 = "," + matches[0] + metadataField + ": [" + symbolName + "]";
457 }
458 else {
459 toInsert_2 = ", " + metadataField + ": [" + symbolName + "]";
460 }
461 }
462 var newMetadataProperty = new change_1.InsertChange(componentPath, position_2, toInsert_2);
463 var newMetadataImport = insertImport(source, componentPath, symbolName.replace(/\..*$/, ''), importPath);
464 return [newMetadataProperty, newMetadataImport];
465 }
466 var assignment = matchingProperties[0];
467 // If it's not an array, nothing we can do really.
468 if (assignment.initializer.kind !== ts.SyntaxKind.ArrayLiteralExpression) {
469 return [];
470 }
471 var arrLiteral = assignment.initializer;
472 if (arrLiteral.elements.length == 0) {
473 // Forward the property.
474 node = arrLiteral;
475 }
476 else {
477 node = arrLiteral.elements;
478 }
479 if (!node) {
480 console.log('No component found. Please add your new class to your component.');
481 return [];
482 }
483 if (Array.isArray(node)) {
484 var nodeArray = node;
485 var symbolsArray = nodeArray.map(function (node) { return node.getText(); });
486 if (symbolsArray.includes(symbolName)) {
487 return [];
488 }
489 node = node[node.length - 1];
490 }
491 var toInsert;
492 var position = node.getEnd();
493 if (node.kind == ts.SyntaxKind.ObjectLiteralExpression) {
494 // We haven't found the field in the metadata declaration. Insert a new
495 // field.
496 var expr = node;
497 if (expr.properties.length == 0) {
498 position = expr.getEnd() - 1;
499 toInsert = " " + metadataField + ": [" + symbolName + "]\n";
500 }
501 else {
502 node = expr.properties[expr.properties.length - 1];
503 position = node.getEnd();
504 // Get the indentation of the last element, if any.
505 var text = node.getFullText(source);
506 if (text.match('^\r?\r?\n')) {
507 toInsert = "," + text.match(/^\r?\n\s+/)[0] + metadataField + ": [" + symbolName + "]";
508 }
509 else {
510 toInsert = ", " + metadataField + ": [" + symbolName + "]";
511 }
512 }
513 }
514 else if (node.kind == ts.SyntaxKind.ArrayLiteralExpression) {
515 // We found the field but it's empty. Insert it just before the `]`.
516 position--;
517 toInsert = "" + symbolName;
518 }
519 else {
520 // Get the indentation of the last element, if any.
521 var text = node.getFullText(source);
522 if (text.match(/^\r?\n/)) {
523 toInsert = "," + text.match(/^\r?\n(\r?)\s+/)[0] + symbolName;
524 }
525 else {
526 toInsert = ", " + symbolName;
527 }
528 }
529 var insert = new change_1.InsertChange(componentPath, position, toInsert);
530 var importInsert = insertImport(source, componentPath, symbolName.replace(/\..*$/, ''), importPath);
531 return [insert, importInsert];
532}
533/**
534 * Custom function to insert a declaration (component, pipe, directive)
535 * into NgModule declarations. It also imports the component.
536 */
537function addDeclarationToModule(source, modulePath, classifiedName, importPath) {
538 return _addSymbolToNgModuleMetadata(source, modulePath, 'declarations', classifiedName, importPath);
539}
540exports.addDeclarationToModule = addDeclarationToModule;
541/**
542 * Custom function to insert a declaration (component, pipe, directive)
543 * into NgModule declarations. It also imports the component.
544 */
545function addImportToModule(source, modulePath, classifiedName, importPath) {
546 return _addSymbolToNgModuleMetadata(source, modulePath, 'imports', classifiedName, importPath);
547}
548exports.addImportToModule = addImportToModule;
549/**
550 * Custom function to insert a provider into NgModule. It also imports it.
551 */
552function addProviderToModule(source, modulePath, classifiedName, importPath) {
553 return _addSymbolToNgModuleMetadata(source, modulePath, 'providers', classifiedName, importPath);
554}
555exports.addProviderToModule = addProviderToModule;
556/**
557 * Custom function to insert a provider into Component. It also imports it.
558 */
559function addProviderToComponent(source, componentPath, classifiedName, importPath) {
560 return _addSymbolToComponentMetadata(source, componentPath, 'providers', classifiedName, importPath);
561}
562exports.addProviderToComponent = addProviderToComponent;
563/**
564 * Custom function to insert an export into NgModule. It also imports it.
565 */
566function addExportToModule(source, modulePath, classifiedName, importPath) {
567 return _addSymbolToNgModuleMetadata(source, modulePath, 'exports', classifiedName, importPath);
568}
569exports.addExportToModule = addExportToModule;
570/**
571 * Custom function to insert an export into NgModule. It also imports it.
572 */
573function addBootstrapToModule(source, modulePath, classifiedName, importPath) {
574 return _addSymbolToNgModuleMetadata(source, modulePath, 'bootstrap', classifiedName, importPath);
575}
576exports.addBootstrapToModule = addBootstrapToModule;
577/**
578 * Add Import `import { symbolName } from fileName` if the import doesn't exit
579 * already. Assumes fileToEdit can be resolved and accessed.
580 * @param fileToEdit (file we want to add import to)
581 * @param symbolName (item to import)
582 * @param fileName (path to the file)
583 * @param isDefault (if true, import follows style for importing default exports)
584 * @return Change
585 */
586function insertImport(source, fileToEdit, symbolName, fileName, isDefault) {
587 if (isDefault === void 0) { isDefault = false; }
588 var rootNode = source;
589 var allImports = findNodes(rootNode, ts.SyntaxKind.ImportDeclaration);
590 // get nodes that map to import statements from the file fileName
591 var relevantImports = allImports.filter(function (node) {
592 // StringLiteral of the ImportDeclaration is the import file (fileName in this case).
593 var importFiles = node
594 .getChildren()
595 .filter(function (child) { return child.kind === ts.SyntaxKind.StringLiteral; })
596 .map(function (n) { return n.text; });
597 return importFiles.filter(function (file) { return file === fileName; }).length === 1;
598 });
599 if (relevantImports.length > 0) {
600 var importsAsterisk_1 = false;
601 // imports from import file
602 var imports_1 = [];
603 relevantImports.forEach(function (n) {
604 Array.prototype.push.apply(imports_1, findNodes(n, ts.SyntaxKind.Identifier));
605 if (findNodes(n, ts.SyntaxKind.AsteriskToken).length > 0) {
606 importsAsterisk_1 = true;
607 }
608 });
609 // if imports * from fileName, don't add symbolName
610 if (importsAsterisk_1) {
611 return new change_1.NoopChange();
612 }
613 var importTextNodes = imports_1.filter(function (n) { return n.text === symbolName; });
614 // insert import if it's not there
615 if (importTextNodes.length === 0) {
616 var fallbackPos_1 = findNodes(relevantImports[0], ts.SyntaxKind.CloseBraceToken)[0].getStart() ||
617 findNodes(relevantImports[0], ts.SyntaxKind.FromKeyword)[0].getStart();
618 return insertAfterLastOccurrence(imports_1, ", " + symbolName, fileToEdit, fallbackPos_1);
619 }
620 return new change_1.NoopChange();
621 }
622 // no such import declaration exists
623 var useStrict = findNodes(rootNode, ts.SyntaxKind.StringLiteral).filter(function (n) { return n.getText() === 'use strict'; });
624 var fallbackPos = 0;
625 if (useStrict.length > 0) {
626 fallbackPos = useStrict[0].end;
627 }
628 var open = isDefault ? '' : '{ ';
629 var close = isDefault ? '' : ' }';
630 // if there are no imports or 'use strict' statement, insert import at beginning of file
631 var insertAtBeginning = allImports.length === 0 && useStrict.length === 0;
632 var separator = insertAtBeginning ? '' : ';\n';
633 var toInsert = separator + "import " + open + symbolName + close +
634 (" from '" + fileName + "'" + (insertAtBeginning ? ';\n' : ''));
635 return insertAfterLastOccurrence(allImports, toInsert, fileToEdit, fallbackPos, ts.SyntaxKind.StringLiteral);
636}
637exports.insertImport = insertImport;
638function replaceImport(sourceFile, path, importFrom, importAsIs, importToBe) {
639 var imports = sourceFile.statements
640 .filter(ts.isImportDeclaration)
641 .filter(function (_a) {
642 var moduleSpecifier = _a.moduleSpecifier;
643 return moduleSpecifier.getText(sourceFile) === "'" + importFrom + "'" ||
644 moduleSpecifier.getText(sourceFile) === "\"" + importFrom + "\"";
645 });
646 if (imports.length === 0) {
647 return [];
648 }
649 var importText = function (specifier) {
650 if (specifier.name.text) {
651 return specifier.name.text;
652 }
653 // if import is renamed
654 if (specifier.propertyName && specifier.propertyName.text) {
655 return specifier.propertyName.text;
656 }
657 return '';
658 };
659 var changes = imports.map(function (p) {
660 var _a;
661 var namedImports = (_a = p === null || p === void 0 ? void 0 : p.importClause) === null || _a === void 0 ? void 0 : _a.namedBindings;
662 if (!namedImports) {
663 return [];
664 }
665 var importSpecifiers = namedImports.elements;
666 var isAlreadyImported = importSpecifiers
667 .map(importText)
668 .includes(importToBe);
669 var importChanges = importSpecifiers.map(function (specifier, index) {
670 var text = importText(specifier);
671 // import is not the one we're looking for, can be skipped
672 if (text !== importAsIs) {
673 return undefined;
674 }
675 // identifier has not been imported, simply replace the old text with the new text
676 if (!isAlreadyImported) {
677 return change_1.createReplaceChange(sourceFile, specifier, importAsIs, importToBe);
678 }
679 var nextIdentifier = importSpecifiers[index + 1];
680 // identifer is not the last, also clean up the comma
681 if (nextIdentifier) {
682 return change_1.createRemoveChange(sourceFile, specifier, specifier.getStart(sourceFile), nextIdentifier.getStart(sourceFile));
683 }
684 // there are no imports following, just remove it
685 return change_1.createRemoveChange(sourceFile, specifier, specifier.getStart(sourceFile), specifier.getEnd());
686 });
687 return importChanges.filter(Boolean);
688 });
689 return changes.reduce(function (imports, curr) { return imports.concat(curr); }, []);
690}
691exports.replaceImport = replaceImport;
692function containsProperty(objectLiteral, propertyName) {
693 return (objectLiteral &&
694 objectLiteral.properties.some(function (prop) {
695 return ts.isPropertyAssignment(prop) &&
696 ts.isIdentifier(prop.name) &&
697 prop.name.text === propertyName;
698 }));
699}
700exports.containsProperty = containsProperty;
701//# sourceMappingURL=ast-utils.js.map
\No newline at end of file