1 | "use strict";
|
2 | Object.defineProperty(exports, "__esModule", { value: true });
|
3 | exports.findClass = exports.addMethod = exports.addParameterToConstructor = exports.replaceNodeValue = exports.getImport = exports.addGlobal = exports.insertImport = exports.removeChange = exports.replaceChange = exports.insertChange = void 0;
|
4 | const typescript_1 = require("./typescript");
|
5 | const typescript_2 = require("nx/src/utils/typescript");
|
6 | let tsModule;
|
7 | function nodesByPosition(first, second) {
|
8 | return first.getStart() - second.getStart();
|
9 | }
|
10 | function 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 | }
|
20 | function 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 | }
|
27 | exports.insertChange = insertChange;
|
28 | function 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 | }
|
39 | exports.replaceChange = replaceChange;
|
40 | function 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 | }
|
47 | exports.removeChange = removeChange;
|
48 | function 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 |
|
55 | const relevantImports = allImports.filter((node) => {
|
56 |
|
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 |
|
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 |
|
74 | if (importsAsterisk) {
|
75 | return source;
|
76 | }
|
77 | const importTextNodes = imports.filter((n) => n.text === symbolName);
|
78 |
|
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 |
|
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 |
|
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 | }
|
101 | exports.insertImport = insertImport;
|
102 | function insertAfterLastOccurrence(host, sourceFile, nodes, toInsert, pathToFile, fallbackPos, syntaxKind) {
|
103 |
|
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 | }
|
117 | function 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 | }
|
130 | exports.addGlobal = addGlobal;
|
131 | function 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 | }
|
150 | exports.getImport = getImport;
|
151 | function replaceNodeValue(host, sourceFile, modulePath, node, content) {
|
152 | return replaceChange(host, sourceFile, modulePath, node.getStart(node.getSourceFile()), content, node.getText());
|
153 | }
|
154 | exports.replaceNodeValue = replaceNodeValue;
|
155 | function 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');
|
163 | }
|
164 | return addMethod(tree, source, modulePath, {
|
165 | className: opts.className,
|
166 | methodHeader: `constructor(${opts.param})`,
|
167 | });
|
168 | }
|
169 | exports.addParameterToConstructor = addParameterToConstructor;
|
170 | function 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 | }
|
183 | exports.addMethod = addMethod;
|
184 | function 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 | }
|
196 | exports.findClass = findClass;
|
197 |
|
\ | No newline at end of file |