diff --git a/lib/util/TaskManager.test.ts b/lib/util/TaskManager.test.ts deleted file mode 100644 index f6176b8..0000000 --- a/lib/util/TaskManager.test.ts +++ /dev/null @@ -1,57 +0,0 @@ -import { describe, it } from 'node:test'; -import { TaskManager } from './TaskManager.js'; -import assert from 'node:assert/strict'; - -describe('TaskManager', () => { - it('should execute tasks', async () => { - let result = ''; - - const taskManager = new TaskManager(async (c) => { - result += `${c.file}\n`; - }); - - await taskManager.execute(['a', 'b', 'c']); - - assert.equal(result, 'a\nb\nc\n'); - }); - - it('should execute tasks that were added after the execution has started', async () => { - let result = ''; - - const taskManager = new TaskManager(async (c) => { - result += `${c.file}\n`; - - if (c.file === 'a') { - c.add('c'); - } - }); - - await taskManager.execute(['a', 'b']); - - assert.equal(result, 'a\nb\nc\n'); - }); - - it('should prioritize new tasks over existing tasks', async () => { - let result = ''; - const count = { a: 0, b: 0, c: 0 }; - - const taskManager = new TaskManager(async (c) => { - count[c.file as 'a' | 'b' | 'c']++; - await Promise.resolve(); - - if (c.signal.aborted) { - return; - } - - result += `${c.file}:${count[c.file as 'a' | 'b' | 'c']}\n`; - - if (c.file === 'a') { - c.add('b'); - } - }); - - await taskManager.execute(['a', 'b', 'c']); - - assert.equal(result, 'a:1\nc:1\nb:2\n'); - }); -}); diff --git a/lib/util/TaskManager.ts b/lib/util/TaskManager.ts deleted file mode 100644 index 26b3aff..0000000 --- a/lib/util/TaskManager.ts +++ /dev/null @@ -1,69 +0,0 @@ -type TaskHandler = ({ - file, - signal, - add, -}: { - file: string; - signal: AbortSignal; - add: (...files: string[]) => void; -}) => Promise; - -type Task = { - file: string; - controller: AbortController; - promise: Promise; - isFulfilled: boolean; -}; - -export class TaskManager { - #handler: TaskHandler; - #queue: string[] = []; - #ongoing: Task[] = []; - - constructor(handler: TaskHandler) { - this.#handler = handler; - } - - #startQueued() { - while (this.#queue.length > 0) { - const file = this.#queue.shift(); - - if (!file) { - break; - } - - const controller = new AbortController(); - const signal = controller.signal; - - const task = { - file, - controller, - promise: this.#handler({ - file, - signal, - add: (...files) => { - this.#ongoing - .filter((t) => files.includes(t.file)) - .forEach((t) => t.controller.abort()); - this.#queue.push(...files); - }, - }).then(() => { - task.isFulfilled = true; - }), - isFulfilled: false, - }; - - this.#ongoing.push(task); - } - } - - async execute(files: string[]) { - this.#queue.push(...files); - - while (this.#queue.length > 0 || this.#ongoing.length > 0) { - this.#startQueued(); - await Promise.race(this.#ongoing.map((t) => t.promise)); - this.#ongoing = this.#ongoing.filter((t) => !t.isFulfilled); - } - } -} diff --git a/lib/util/edit.ts b/lib/util/edit.ts index d953dda..e3287a5 100644 --- a/lib/util/edit.ts +++ b/lib/util/edit.ts @@ -9,7 +9,6 @@ import { import { Vertexes } from './DependencyGraph.js'; import { createDependencyGraph } from './createDependencyGraph.js'; import { MemoryFileService } from './MemoryFileService.js'; -import { TaskManager } from './TaskManager.js'; import { findFileUsage } from './findFileUsage.js'; import { parseFile } from './parseFile.js'; import { Output } from './Output.js'; @@ -627,74 +626,97 @@ export const edit = async ({ initialFiles.sort((a, b) => a.depth - b.depth); - const taskManager = new TaskManager(async (c) => { - // if the file is not in the file service, it means it has been deleted in a previous iteration - if (!fileService.exists(c.file)) { - return; - } - - const vertex = dependencyGraph.vertexes.get(c.file); + const queue = [initialFiles]; - await Promise.resolve(); + while (queue.length > 0) { + const first = queue.shift(); - if (c.signal.aborted) { - return; + if (!first) { + break; } - const result = processFile({ - targetFile: c.file, - vertexes: dependencyGraph.eject(), + const current = { + vertexes: dependencyGraph.vertexes, files: fileService.eject(), fileNames: fileService.getFileNames(), - deleteUnusedFile, - enableCodeFix, - options, - projectRoot, - }); - - if (c.signal.aborted) { - return; - } + }; - switch (result.operation) { - case 'delete': { - if (entrypoints.includes(c.file)) { - break; + const next = first + .map((v) => { + // if the file is not in the file service, it means it has been deleted in a previous iteration + if (!fileService.exists(v.file)) { + return; } - output.deleteFile(c.file); - fileService.delete(c.file); - if (vertex) { - dependencyGraph.deleteVertex(c.file); + const result = processFile({ + targetFile: v.file, + vertexes: current.vertexes, + files: current.files, + fileNames: current.fileNames, + deleteUnusedFile, + enableCodeFix, + options, + projectRoot, + }); + + return { result, ...v }; + }) + .filter((r) => !!r) + .map(({ result, file }) => { + const vertex = dependencyGraph.vertexes.get(file); - if (recursive) { - c.add( - ...Array.from(vertex.to).filter((f) => !entrypoints.includes(f)), - ); + switch (result.operation) { + case 'delete': { + if (entrypoints.includes(file)) { + return []; + } + output.deleteFile(file); + fileService.delete(file); + + if (vertex) { + dependencyGraph.deleteVertex(file); + + if (recursive) { + return Array.from(vertex.to).filter( + (f) => !entrypoints.includes(f), + ); + } + } + return []; } - } - break; - } - case 'edit': { - for (const item of result.removedExports) { - output.removeExport({ - file: item.fileName, - content: fileService.get(item.fileName), - code: item.code, - position: item.position, - }); - } - fileService.set(c.file, result.content); + case 'edit': { + for (const item of result.removedExports) { + output.removeExport({ + file: item.fileName, + content: fileService.get(item.fileName), + code: item.code, + position: item.position, + }); + } + fileService.set(file, result.content); - if (vertex && result.removedExports.length > 0 && recursive) { - c.add( - ...Array.from(vertex.to).filter((f) => !entrypoints.includes(f)), - ); + if (vertex && result.removedExports.length > 0 && recursive) { + return Array.from(vertex.to).filter( + (f) => !entrypoints.includes(f), + ); + } + return []; + } } - break; - } + }); + + const files = Array.from(new Set(next.flat())); + + if (files.length > 0) { + queue.push( + files.map((file) => ({ + file, + depth: dependencyGraph.vertexes.get(file)?.data.depth || Infinity, + })), + ); } - }); + } - await taskManager.execute(initialFiles.map((v) => v.file)); + // this is kept for compatibility with the old implementation + return Promise.resolve(); };