UNPKG

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