UNPKG

8.93 kBJavaScriptView Raw
1"use strict";
2Object.defineProperty(exports, "__esModule", { value: true });
3exports.findClass = exports.addMethod = exports.addParameterToConstructor = exports.replaceNodeValue = exports.getImport = exports.addGlobal = exports.insertImport = exports.removeChange = exports.replaceChange = exports.insertChange = void 0;
4const typescript_1 = require("./typescript");
5const typescript_2 = require("nx/src/utils/typescript");
6let tsModule;
7function nodesByPosition(first, second) {
8 return first.getStart() - second.getStart();
9}
10function updateTsSourceFile(host, sourceFile, filePath) {
11 const newFileContents = host.read(filePath).toString('utf-8');
12 return sourceFile.update(newFileContents, {
13 newLength: newFileContents.length,
14 span: {
15 length: sourceFile.text.length,
16 start: 0,
17 },
18 });
19}
20function insertChange(host, sourceFile, filePath, insertPosition, contentToInsert) {
21 const content = host.read(filePath).toString();
22 const prefix = content.substring(0, insertPosition);
23 const suffix = content.substring(insertPosition);
24 host.write(filePath, `${prefix}${contentToInsert}${suffix}`);
25 return updateTsSourceFile(host, sourceFile, filePath);
26}
27exports.insertChange = insertChange;
28function replaceChange(host, sourceFile, filePath, insertPosition, contentToInsert, oldContent) {
29 const content = host.read(filePath, 'utf-8');
30 const prefix = content.substring(0, insertPosition);
31 const suffix = content.substring(insertPosition + oldContent.length);
32 const text = content.substring(insertPosition, insertPosition + oldContent.length);
33 if (text !== oldContent) {
34 throw new Error(`Invalid replace: "${text}" != "${oldContent}".`);
35 }
36 host.write(filePath, `${prefix}${contentToInsert}${suffix}`);
37 return updateTsSourceFile(host, sourceFile, filePath);
38}
39exports.replaceChange = replaceChange;
40function removeChange(host, sourceFile, filePath, removePosition, contentToRemove) {
41 const content = host.read(filePath).toString();
42 const prefix = content.substring(0, removePosition);
43 const suffix = content.substring(removePosition + contentToRemove.length);
44 host.write(filePath, `${prefix}${suffix}`);
45 return updateTsSourceFile(host, sourceFile, filePath);
46}
47exports.removeChange = removeChange;
48function insertImport(host, source, fileToEdit, symbolName, fileName, isDefault = false) {
49 if (!tsModule) {
50 tsModule = (0, typescript_1.ensureTypescript)();
51 }
52 const rootNode = source;
53 const allImports = (0, typescript_2.findNodes)(rootNode, tsModule.SyntaxKind.ImportDeclaration);
54 // get nodes that map to import statements from the file fileName
55 const relevantImports = allImports.filter((node) => {
56 // StringLiteral of the ImportDeclaration is the import file (fileName in this case).
57 const importFiles = node
58 .getChildren()
59 .filter((child) => child.kind === tsModule.SyntaxKind.StringLiteral)
60 .map((n) => n.text);
61 return importFiles.filter((file) => file === fileName).length === 1;
62 });
63 if (relevantImports.length > 0) {
64 let importsAsterisk = false;
65 // imports from import file
66 const imports = [];
67 relevantImports.forEach((n) => {
68 Array.prototype.push.apply(imports, (0, typescript_2.findNodes)(n, tsModule.SyntaxKind.Identifier));
69 if ((0, typescript_2.findNodes)(n, tsModule.SyntaxKind.AsteriskToken).length > 0) {
70 importsAsterisk = true;
71 }
72 });
73 // if imports * from fileName, don't add symbolName
74 if (importsAsterisk) {
75 return source;
76 }
77 const importTextNodes = imports.filter((n) => n.text === symbolName);
78 // insert import if it's not there
79 if (importTextNodes.length === 0) {
80 const fallbackPos = (0, typescript_2.findNodes)(relevantImports[0], tsModule.SyntaxKind.CloseBraceToken)[0].getStart() ||
81 (0, typescript_2.findNodes)(relevantImports[0], tsModule.SyntaxKind.FromKeyword)[0].getStart();
82 return insertAfterLastOccurrence(host, source, imports, `, ${symbolName}`, fileToEdit, fallbackPos);
83 }
84 return source;
85 }
86 // no such import declaration exists
87 const useStrict = (0, typescript_2.findNodes)(rootNode, tsModule.SyntaxKind.StringLiteral).filter((n) => n.text === 'use strict');
88 let fallbackPos = 0;
89 if (useStrict.length > 0) {
90 fallbackPos = useStrict[0].end;
91 }
92 const open = isDefault ? '' : '{ ';
93 const close = isDefault ? '' : ' }';
94 // if there are no imports or 'use strict' statement, insert import at beginning of file
95 const insertAtBeginning = allImports.length === 0 && useStrict.length === 0;
96 const separator = insertAtBeginning ? '' : ';\n';
97 const toInsert = `${separator}import ${open}${symbolName}${close}` +
98 ` from '${fileName}'${insertAtBeginning ? ';\n' : ''}`;
99 return insertAfterLastOccurrence(host, source, allImports, toInsert, fileToEdit, fallbackPos, tsModule.SyntaxKind.StringLiteral);
100}
101exports.insertImport = insertImport;
102function insertAfterLastOccurrence(host, sourceFile, nodes, toInsert, pathToFile, fallbackPos, syntaxKind) {
103 // sort() has a side effect, so make a copy so that we won't overwrite the parent's object.
104 let lastItem = [...nodes].sort(nodesByPosition).pop();
105 if (!lastItem) {
106 throw new Error();
107 }
108 if (syntaxKind) {
109 lastItem = (0, typescript_2.findNodes)(lastItem, syntaxKind).sort(nodesByPosition).pop();
110 }
111 if (!lastItem && fallbackPos == undefined) {
112 throw new Error(`tried to insert ${toInsert} as first occurrence with no fallback position`);
113 }
114 const lastItemPosition = lastItem ? lastItem.getEnd() : fallbackPos;
115 return insertChange(host, sourceFile, pathToFile, lastItemPosition, toInsert);
116}
117function addGlobal(host, source, modulePath, statement) {
118 if (!tsModule) {
119 tsModule = (0, typescript_1.ensureTypescript)();
120 }
121 const allImports = (0, typescript_2.findNodes)(source, tsModule.SyntaxKind.ImportDeclaration);
122 if (allImports.length > 0) {
123 const lastImport = allImports[allImports.length - 1];
124 return insertChange(host, source, modulePath, lastImport.end + 1, `\n${statement}\n`);
125 }
126 else {
127 return insertChange(host, source, modulePath, 0, `${statement}\n`);
128 }
129}
130exports.addGlobal = addGlobal;
131function getImport(source, predicate) {
132 if (!tsModule) {
133 tsModule = (0, typescript_1.ensureTypescript)();
134 }
135 const allImports = (0, typescript_2.findNodes)(source, tsModule.SyntaxKind.ImportDeclaration);
136 const matching = allImports.filter((i) => predicate(i.moduleSpecifier.getText()));
137 return matching.map((i) => {
138 const moduleSpec = i.moduleSpecifier
139 .getText()
140 .substring(1, i.moduleSpecifier.getText().length - 1);
141 const t = i.importClause.namedBindings.getText();
142 const bindings = t
143 .replace('{', '')
144 .replace('}', '')
145 .split(',')
146 .map((q) => q.trim());
147 return { moduleSpec, bindings };
148 });
149}
150exports.getImport = getImport;
151function replaceNodeValue(host, sourceFile, modulePath, node, content) {
152 return replaceChange(host, sourceFile, modulePath, node.getStart(node.getSourceFile()), content, node.getText());
153}
154exports.replaceNodeValue = replaceNodeValue;
155function addParameterToConstructor(tree, source, modulePath, opts) {
156 if (!tsModule) {
157 tsModule = (0, typescript_1.ensureTypescript)();
158 }
159 const clazz = findClass(source, opts.className);
160 const constructor = clazz.members.filter((m) => m.kind === tsModule.SyntaxKind.Constructor)[0];
161 if (constructor) {
162 throw new Error('Should be tested'); // TODO: check this
163 }
164 return addMethod(tree, source, modulePath, {
165 className: opts.className,
166 methodHeader: `constructor(${opts.param})`,
167 });
168}
169exports.addParameterToConstructor = addParameterToConstructor;
170function addMethod(tree, source, modulePath, opts) {
171 const clazz = findClass(source, opts.className);
172 const body = opts.body
173 ? `
174${opts.methodHeader} {
175${opts.body}
176}
177`
178 : `
179${opts.methodHeader} {}
180`;
181 return insertChange(tree, source, modulePath, clazz.end - 1, body);
182}
183exports.addMethod = addMethod;
184function findClass(source, className, silent = false) {
185 if (!tsModule) {
186 tsModule = (0, typescript_1.ensureTypescript)();
187 }
188 const nodes = (0, typescript_1.getSourceNodes)(source);
189 const clazz = nodes.filter((n) => n.kind === tsModule.SyntaxKind.ClassDeclaration &&
190 n.name.text === className)[0];
191 if (!clazz && !silent) {
192 throw new Error(`Cannot find class '${className}'.`);
193 }
194 return clazz;
195}
196exports.findClass = findClass;
197//# sourceMappingURL=ast-utils.js.map
\No newline at end of file