From 95330e04deb3fbf76560a5c983d35f2043c79320 Mon Sep 17 00:00:00 2001 From: Reaper Gelera Date: Wed, 6 Dec 2023 01:44:29 +0530 Subject: [PATCH] feat: add in build batcher --- src/index.ts | 13 ++++++++---- src/{ => lib}/hooks.ts | 0 src/lib/promise.ts | 45 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 54 insertions(+), 4 deletions(-) rename src/{ => lib}/hooks.ts (100%) create mode 100644 src/lib/promise.ts diff --git a/src/index.ts b/src/index.ts index 21fc48b..4f1dc61 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,7 +1,8 @@ import esbuild from 'esbuild' import { defu } from 'defu' import glob from 'tiny-glob' -import { createHook } from './hooks.js' +import { createHook } from './lib/hooks.js' +import { batcher } from './lib/promise.js' export type GlobOptions = { cwd?: string @@ -60,10 +61,14 @@ class ContextManager { } } - async build() { + async build({ limit = Infinity } = {}) { await this.#createContext() - await Promise.all(this.#contexts.map(x => x.rebuild())) - await this.#eventBus.emit('complete', null) + const errors = await batcher(x => x.rebuild(), { limit })(this.#contexts) + if (errors.length > 0) { + await this.#eventBus.emit('error', errors) + } else { + await this.#eventBus.emit('complete', null) + } } async watch() { diff --git a/src/hooks.ts b/src/lib/hooks.ts similarity index 100% rename from src/hooks.ts rename to src/lib/hooks.ts diff --git a/src/lib/promise.ts b/src/lib/promise.ts new file mode 100644 index 0000000..f9d0ee4 --- /dev/null +++ b/src/lib/promise.ts @@ -0,0 +1,45 @@ +type BatchOptions = { + limit: number +} + +/** + * TODO: can be faster + */ +export const batcher = + ( + mapper: (item: Item) => Promise, + { limit = Infinity }: BatchOptions + ) => + async (collection: T) => { + const errors: Error[] = [] + const pwindow: Item[] = [] + + const process = async () => { + let batch: any = [] + while (pwindow.length > 0) { + const item = pwindow.shift() + if (item) { + batch.push(mapper(item)) + } + } + const result = await Promise.allSettled(batch) + result.forEach(d => { + if (d.status === 'rejected') { + errors.push(new Error(d.reason)) + } + }) + } + + for (let item of collection) { + pwindow.push(item) + if (pwindow.length >= limit) { + await process() + } + } + + if (pwindow.length) { + await process() + } + + return errors + }