UNPKG

10.4 kBJavaScriptView Raw
1"use strict";
2var ts = require('typescript');
3var fs = require('fs');
4var symbols_1 = require('@angular/tsc-wrapped/src/symbols');
5var tsc_wrapped_1 = require('@angular/tsc-wrapped');
6var change_1 = require('./change');
7var node_1 = require('./node');
8var route_utils_1 = require('./route-utils');
9var ReplaySubject_1 = require('rxjs/ReplaySubject');
10require('rxjs/add/observable/empty');
11require('rxjs/add/observable/of');
12require('rxjs/add/operator/do');
13require('rxjs/add/operator/filter');
14require('rxjs/add/operator/last');
15require('rxjs/add/operator/map');
16require('rxjs/add/operator/mergeMap');
17require('rxjs/add/operator/toArray');
18require('rxjs/add/operator/toPromise');
19/**
20* Get TS source file based on path.
21* @param filePath
22* @return source file of ts.SourceFile kind
23*/
24function getSource(filePath) {
25 return ts.createSourceFile(filePath, fs.readFileSync(filePath).toString(), ts.ScriptTarget.Latest, true);
26}
27exports.getSource = getSource;
28/**
29 * Get all the nodes from a source, as an observable.
30 * @param sourceFile The source file object.
31 * @returns {Observable<ts.Node>} An observable of all the nodes in the source.
32 */
33function getSourceNodes(sourceFile) {
34 var subject = new ReplaySubject_1.ReplaySubject();
35 var nodes = [sourceFile];
36 while (nodes.length > 0) {
37 var node = nodes.shift();
38 if (node) {
39 subject.next(node);
40 if (node.getChildCount(sourceFile) >= 0) {
41 nodes.unshift.apply(nodes, node.getChildren());
42 }
43 }
44 }
45 subject.complete();
46 return subject.asObservable();
47}
48exports.getSourceNodes = getSourceNodes;
49/**
50 * Helper for sorting nodes.
51 * @return function to sort nodes in increasing order of position in sourceFile
52 */
53function nodesByPosition(first, second) {
54 return first.pos - second.pos;
55}
56/**
57 * Insert `toInsert` after the last occurence of `ts.SyntaxKind[nodes[i].kind]`
58 * or after the last of occurence of `syntaxKind` if the last occurence is a sub child
59 * of ts.SyntaxKind[nodes[i].kind] and save the changes in file.
60 *
61 * @param nodes insert after the last occurence of nodes
62 * @param toInsert string to insert
63 * @param file file to insert changes into
64 * @param fallbackPos position to insert if toInsert happens to be the first occurence
65 * @param syntaxKind the ts.SyntaxKind of the subchildren to insert after
66 * @return Change instance
67 * @throw Error if toInsert is first occurence but fall back is not set
68 */
69function insertAfterLastOccurrence(nodes, toInsert, file, fallbackPos, syntaxKind) {
70 var lastItem = nodes.sort(nodesByPosition).pop();
71 if (syntaxKind) {
72 lastItem = node_1.findNodes(lastItem, syntaxKind).sort(nodesByPosition).pop();
73 }
74 if (!lastItem && fallbackPos == undefined) {
75 throw new Error("tried to insert " + toInsert + " as first occurence with no fallback position");
76 }
77 var lastItemPosition = lastItem ? lastItem.end : fallbackPos;
78 return new change_1.InsertChange(file, lastItemPosition, toInsert);
79}
80exports.insertAfterLastOccurrence = insertAfterLastOccurrence;
81function getContentOfKeyLiteral(source, node) {
82 if (node.kind == ts.SyntaxKind.Identifier) {
83 return node.text;
84 }
85 else if (node.kind == ts.SyntaxKind.StringLiteral) {
86 return node.text;
87 }
88 else {
89 return null;
90 }
91}
92exports.getContentOfKeyLiteral = getContentOfKeyLiteral;
93function getDecoratorMetadata(source, identifier, module) {
94 var symbols = new symbols_1.Symbols(source);
95 return getSourceNodes(source)
96 .filter(function (node) {
97 return node.kind == ts.SyntaxKind.Decorator
98 && node.expression.kind == ts.SyntaxKind.CallExpression;
99 })
100 .map(function (node) { return node.expression; })
101 .filter(function (expr) {
102 if (expr.expression.kind == ts.SyntaxKind.Identifier) {
103 var id = expr.expression;
104 var metaData = symbols.resolve(id.getFullText(source));
105 if (tsc_wrapped_1.isMetadataImportedSymbolReferenceExpression(metaData)) {
106 return metaData.name == identifier && metaData.module == module;
107 }
108 }
109 else if (expr.expression.kind == ts.SyntaxKind.PropertyAccessExpression) {
110 // This covers foo.NgModule when importing * as foo.
111 var paExpr = expr.expression;
112 // If the left expression is not an identifier, just give up at that point.
113 if (paExpr.expression.kind !== ts.SyntaxKind.Identifier) {
114 return false;
115 }
116 var id = paExpr.name;
117 var moduleId = paExpr.expression;
118 var moduleMetaData = symbols.resolve(moduleId.getFullText(source));
119 if (tsc_wrapped_1.isMetadataModuleReferenceExpression(moduleMetaData)) {
120 return moduleMetaData.module == module && id.getFullText(source) == identifier;
121 }
122 }
123 return false;
124 })
125 .filter(function (expr) { return expr.arguments[0]
126 && expr.arguments[0].kind == ts.SyntaxKind.ObjectLiteralExpression; })
127 .map(function (expr) { return expr.arguments[0]; });
128}
129exports.getDecoratorMetadata = getDecoratorMetadata;
130function _addSymbolToNgModuleMetadata(ngModulePath, metadataField, symbolName, importPath) {
131 var source = getSource(ngModulePath);
132 var metadata = getDecoratorMetadata(source, 'NgModule', '@angular/core');
133 // Find the decorator declaration.
134 return metadata
135 .toPromise()
136 .then(function (node) {
137 if (!node) {
138 return null;
139 }
140 // Get all the children property assignment of object literals.
141 return node.properties
142 .filter(function (prop) { return prop.kind == ts.SyntaxKind.PropertyAssignment; })
143 .filter(function (prop) {
144 var name = prop.name;
145 switch (name.kind) {
146 case ts.SyntaxKind.Identifier:
147 return name.getText(source) == metadataField;
148 case ts.SyntaxKind.StringLiteral:
149 return name.text == metadataField;
150 }
151 return false;
152 });
153 })
154 .then(function (matchingProperties) {
155 if (!matchingProperties) {
156 return null;
157 }
158 if (matchingProperties.length == 0) {
159 return metadata.toPromise();
160 }
161 var assignment = matchingProperties[0];
162 // If it's not an array, nothing we can do really.
163 if (assignment.initializer.kind !== ts.SyntaxKind.ArrayLiteralExpression) {
164 return null;
165 }
166 var arrLiteral = assignment.initializer;
167 if (arrLiteral.elements.length == 0) {
168 // Forward the property.
169 return arrLiteral;
170 }
171 return arrLiteral.elements;
172 })
173 .then(function (node) {
174 if (!node) {
175 /* eslint-disable no-console */
176 console.log('No app module found. Please add your new class to your component.');
177 return new change_1.NoopChange();
178 }
179 if (Array.isArray(node)) {
180 node = node[node.length - 1];
181 }
182 var toInsert;
183 var position = node.getEnd();
184 if (node.kind == ts.SyntaxKind.ObjectLiteralExpression) {
185 // We haven't found the field in the metadata declaration. Insert a new
186 // field.
187 var expr = node;
188 if (expr.properties.length == 0) {
189 position = expr.getEnd() - 1;
190 toInsert = " " + metadataField + ": [" + symbolName + "]\n";
191 }
192 else {
193 node = expr.properties[expr.properties.length - 1];
194 position = node.getEnd();
195 // Get the indentation of the last element, if any.
196 var text = node.getFullText(source);
197 if (text.match('^\r?\r?\n')) {
198 toInsert = "," + text.match(/^\r?\n\s+/)[0] + metadataField + ": [" + symbolName + "]";
199 }
200 else {
201 toInsert = ", " + metadataField + ": [" + symbolName + "]";
202 }
203 }
204 }
205 else if (node.kind == ts.SyntaxKind.ArrayLiteralExpression) {
206 // We found the field but it's empty. Insert it just before the `]`.
207 position--;
208 toInsert = "" + symbolName;
209 }
210 else {
211 // Get the indentation of the last element, if any.
212 var text = node.getFullText(source);
213 if (text.match(/^\r?\n/)) {
214 toInsert = "," + text.match(/^\r?\n(\r?)\s+/)[0] + symbolName;
215 }
216 else {
217 toInsert = ", " + symbolName;
218 }
219 }
220 var insert = new change_1.InsertChange(ngModulePath, position, toInsert);
221 var importInsert = route_utils_1.insertImport(ngModulePath, symbolName.replace(/\..*$/, ''), importPath);
222 return new change_1.MultiChange([insert, importInsert]);
223 });
224}
225/**
226* Custom function to insert a declaration (component, pipe, directive)
227* into NgModule declarations. It also imports the component.
228*/
229function addDeclarationToModule(modulePath, classifiedName, importPath) {
230 return _addSymbolToNgModuleMetadata(modulePath, 'declarations', classifiedName, importPath);
231}
232exports.addDeclarationToModule = addDeclarationToModule;
233/**
234 * Custom function to insert a declaration (component, pipe, directive)
235 * into NgModule declarations. It also imports the component.
236 */
237function addImportToModule(modulePath, classifiedName, importPath) {
238 return _addSymbolToNgModuleMetadata(modulePath, 'imports', classifiedName, importPath);
239}
240exports.addImportToModule = addImportToModule;
241/**
242 * Custom function to insert a provider into NgModule. It also imports it.
243 */
244function addProviderToModule(modulePath, classifiedName, importPath) {
245 return _addSymbolToNgModuleMetadata(modulePath, 'providers', classifiedName, importPath);
246}
247exports.addProviderToModule = addProviderToModule;
248/**
249 * Custom function to insert an export into NgModule. It also imports it.
250 */
251function addExportToModule(modulePath, classifiedName, importPath) {
252 return _addSymbolToNgModuleMetadata(modulePath, 'exports', classifiedName, importPath);
253}
254exports.addExportToModule = addExportToModule;
255//# sourceMappingURL=/Users/hans/Sources/angular-cli/packages/@angular-cli/ast-tools/src/ast-utils.js.map
\No newline at end of file