|
1 |
| -import { Group } from "../dependencyManager/types"; |
2 |
| -import { removeIndexesFromSourceCode } from "../helper/file"; |
| 1 | +import path from "path"; |
| 2 | +import { Worker } from "worker_threads"; |
3 | 3 | import DependencyTreeManager from "../dependencyManager/dependencyManager";
|
| 4 | +import { Group } from "../dependencyManager/types"; |
4 | 5 | import { File } from "./types";
|
5 |
| -import Parser from "tree-sitter"; |
6 |
| -import assert from "assert"; |
7 |
| -import { getLanguagePlugin } from "../languagesPlugins"; |
8 |
| -import { DepExport } from "../languagesPlugins/types"; |
9 | 6 |
|
10 | 7 | class SplitRunner {
|
11 | 8 | private dependencyTreeManager: DependencyTreeManager;
|
12 |
| - private entrypointPath: string; |
13 | 9 | private group: Group;
|
14 |
| - private files: File[]; |
15 | 10 |
|
16 | 11 | constructor(dependencyTreeManager: DependencyTreeManager, group: Group) {
|
17 | 12 | this.dependencyTreeManager = dependencyTreeManager;
|
18 |
| - this.entrypointPath = dependencyTreeManager.dependencyTree.path; |
19 | 13 | this.group = group;
|
20 |
| - this.files = dependencyTreeManager.getFiles(); |
21 |
| - } |
22 |
| - |
23 |
| - #removeAnnotationFromOtherGroups() { |
24 |
| - this.files = this.files.map((file) => { |
25 |
| - const languagePlugin = getLanguagePlugin(this.entrypointPath, file.path); |
26 |
| - |
27 |
| - const updatedSourceCode = languagePlugin.removeAnnotationFromOtherGroups( |
28 |
| - file.sourceCode, |
29 |
| - this.group, |
30 |
| - ); |
31 |
| - return { ...file, sourceCode: updatedSourceCode }; |
32 |
| - }); |
33 |
| - } |
34 |
| - |
35 |
| - #getExportMap() { |
36 |
| - const exportMap = new Map<string, DepExport[]>(); |
37 |
| - |
38 |
| - this.files.forEach((file) => { |
39 |
| - const languagePlugin = getLanguagePlugin(this.entrypointPath, file.path); |
40 |
| - |
41 |
| - const tree = languagePlugin.parser.parse(file.sourceCode); |
42 |
| - |
43 |
| - const exports = languagePlugin.getExports(tree.rootNode); |
44 |
| - |
45 |
| - exportMap.set(file.path, exports); |
46 |
| - }); |
47 |
| - |
48 |
| - return exportMap; |
49 |
| - } |
50 |
| - |
51 |
| - #removeInvalidImportsAndUsages(exportMap: Map<string, DepExport[]>) { |
52 |
| - this.files = this.files.map((file) => { |
53 |
| - const languagePlugin = getLanguagePlugin(this.entrypointPath, file.path); |
54 |
| - |
55 |
| - const updatedSourceCode = languagePlugin.cleanupInvalidImports( |
56 |
| - file.path, |
57 |
| - file.sourceCode, |
58 |
| - exportMap, |
59 |
| - ); |
60 |
| - |
61 |
| - return { ...file, sourceCode: updatedSourceCode }; |
62 |
| - }); |
63 | 14 | }
|
64 | 15 |
|
65 |
| - #removeUnusedImports() { |
66 |
| - this.files = this.files.map((file) => { |
67 |
| - const languagePlugin = getLanguagePlugin(this.entrypointPath, file.path); |
| 16 | + async run(): Promise<File[]> { |
| 17 | + console.time("\nSplitting"); |
68 | 18 |
|
69 |
| - const updatedSourceCode = languagePlugin.cleanupUnusedImports( |
70 |
| - file.path, |
71 |
| - file.sourceCode, |
72 |
| - ); |
73 |
| - |
74 |
| - return { ...file, sourceCode: updatedSourceCode }; |
| 19 | + const worker = new Worker(path.resolve(__dirname, "worker.js"), { |
| 20 | + workerData: { |
| 21 | + entrypointPath: this.dependencyTreeManager.dependencyTree.path, |
| 22 | + group: this.group, |
| 23 | + files: this.dependencyTreeManager.getFiles(), |
| 24 | + }, |
75 | 25 | });
|
76 |
| - } |
77 |
| - |
78 |
| - #removeUnusedFiles() { |
79 |
| - let fileRemoved = true; |
80 |
| - while (fileRemoved) { |
81 |
| - fileRemoved = false; |
82 |
| - |
83 |
| - // We always want to keep the entrypoint file. |
84 |
| - // It will never be imported anywhere, so we add it now. |
85 |
| - const filesToKeep = new Set<string>(); |
86 |
| - filesToKeep.add(this.dependencyTreeManager.dependencyTree.path); |
87 |
| - |
88 |
| - this.files.forEach((file) => { |
89 |
| - const languagePlugin = getLanguagePlugin( |
90 |
| - this.entrypointPath, |
91 |
| - file.path, |
92 |
| - ); |
93 |
| - |
94 |
| - const tree = languagePlugin.parser.parse(file.sourceCode); |
95 |
| - |
96 |
| - const imports = languagePlugin.getImports(file.path, tree.rootNode); |
97 |
| - |
98 |
| - imports.forEach((depImport) => { |
99 |
| - if (depImport.isExternal || !depImport.source) { |
100 |
| - // Ignore external dependencies |
101 |
| - return; |
102 |
| - } |
103 |
| - |
104 |
| - filesToKeep.add(depImport.source); |
105 |
| - }); |
106 |
| - }); |
107 |
| - |
108 |
| - const previousFilesLength = this.files.length; |
109 | 26 |
|
110 |
| - this.files = this.files.filter((file) => { |
111 |
| - return filesToKeep.has(file.path); |
| 27 | + return new Promise<File[]>((resolve, reject) => { |
| 28 | + worker.on("message", (updatedFiles: File[]) => { |
| 29 | + console.timeEnd("Splitting"); |
| 30 | + resolve(updatedFiles); |
112 | 31 | });
|
113 | 32 |
|
114 |
| - if (this.files.length !== previousFilesLength) { |
115 |
| - fileRemoved = true; |
116 |
| - } |
117 |
| - } |
118 |
| - } |
119 |
| - |
120 |
| - #removeUnusedExports(exportMap: Map<string, DepExport[]>) { |
121 |
| - let exportDeleted = true; |
122 |
| - while (exportDeleted) { |
123 |
| - exportDeleted = false; |
124 |
| - |
125 |
| - // const usedExportMap = new Map<string, Export>(); |
126 |
| - |
127 |
| - this.files = this.files.map((file) => { |
128 |
| - const languagePlugin = getLanguagePlugin( |
129 |
| - this.entrypointPath, |
130 |
| - file.path, |
131 |
| - ); |
132 |
| - |
133 |
| - const tree = languagePlugin.parser.parse(file.sourceCode); |
134 |
| - |
135 |
| - const imports = languagePlugin.getImports(file.path, tree.rootNode); |
136 |
| - |
137 |
| - imports.forEach((depImport) => { |
138 |
| - if (depImport.isExternal || !depImport.source) { |
139 |
| - // Ignore external dependencies |
140 |
| - return; |
141 |
| - } |
142 |
| - |
143 |
| - // for each import, reconstruct the export map |
144 |
| - const depExport = exportMap.get(depImport.source); |
145 |
| - if (!depExport) { |
146 |
| - throw new Error("Export not found"); |
147 |
| - } |
148 |
| - |
149 |
| - // check named imports |
150 |
| - }); |
151 |
| - |
152 |
| - return file; |
153 |
| - }); |
154 |
| - } |
155 |
| - // TODO |
156 |
| - // Step 1, create variable to track which export is used |
157 |
| - // Step 2, iterate over all file imports. If the import is used, mark the export as used |
158 |
| - // Step 3, iterate over each file, and remove the unused exports |
159 |
| - |
160 |
| - // Repeat above step until no more unused exports are found |
161 |
| - assert(exportMap); |
162 |
| - } |
163 |
| - |
164 |
| - #removeErrors() { |
165 |
| - this.files = this.files.map((file) => { |
166 |
| - const languagePlugin = getLanguagePlugin(this.entrypointPath, file.path); |
167 |
| - |
168 |
| - const tree = languagePlugin.parser.parse(file.sourceCode); |
169 |
| - |
170 |
| - const indexesToRemove: { startIndex: number; endIndex: number }[] = []; |
171 |
| - |
172 |
| - const query = new Parser.Query( |
173 |
| - languagePlugin.parser.getLanguage(), |
174 |
| - "(ERROR) @error", |
175 |
| - ); |
176 |
| - const errorCaptures = query.captures(tree.rootNode); |
177 |
| - errorCaptures.forEach((capture) => { |
178 |
| - indexesToRemove.push({ |
179 |
| - startIndex: capture.node.startIndex, |
180 |
| - endIndex: capture.node.endIndex, |
181 |
| - }); |
| 33 | + worker.on("error", reject); |
| 34 | + worker.on("exit", (code) => { |
| 35 | + if (code !== 0) { |
| 36 | + reject(new Error(`Worker stopped with exit code ${code}`)); |
| 37 | + } |
182 | 38 | });
|
183 |
| - |
184 |
| - const updatedSourceCode = removeIndexesFromSourceCode( |
185 |
| - file.sourceCode, |
186 |
| - indexesToRemove, |
187 |
| - ); |
188 |
| - |
189 |
| - return { ...file, sourceCode: updatedSourceCode }; |
190 | 39 | });
|
191 | 40 | }
|
192 |
| - |
193 |
| - run() { |
194 |
| - console.info("\n"); |
195 |
| - console.time("Splitting"); |
196 |
| - |
197 |
| - console.time("remove annotation from other groups"); |
198 |
| - this.#removeAnnotationFromOtherGroups(); |
199 |
| - console.timeEnd("remove annotation from other groups"); |
200 |
| - |
201 |
| - console.time("Get export map"); |
202 |
| - const exportMap = this.#getExportMap(); |
203 |
| - console.timeEnd("Get export map"); |
204 |
| - |
205 |
| - console.time("Remove invalid imports and usages"); |
206 |
| - this.#removeInvalidImportsAndUsages(exportMap); |
207 |
| - console.timeEnd("Remove invalid imports and usages"); |
208 |
| - |
209 |
| - console.time("Remove unused imports"); |
210 |
| - this.#removeUnusedImports(); |
211 |
| - console.timeEnd("Remove unused imports"); |
212 |
| - |
213 |
| - console.time("Remove unused files"); |
214 |
| - this.#removeUnusedFiles(); |
215 |
| - console.timeEnd("Remove unused files"); |
216 |
| - |
217 |
| - console.time("Remove unused exports"); |
218 |
| - this.#removeUnusedExports(exportMap); |
219 |
| - console.timeEnd("Remove unused exports"); |
220 |
| - |
221 |
| - console.time("Remove errors"); |
222 |
| - this.#removeErrors(); |
223 |
| - console.timeEnd("Remove errors"); |
224 |
| - |
225 |
| - console.timeEnd("Splitting"); |
226 |
| - |
227 |
| - return this.files; |
228 |
| - } |
229 | 41 | }
|
230 | 42 |
|
231 | 43 | export default SplitRunner;
|
0 commit comments