UNPKG

@msom/xbuild

Version:

@msom/xbuild

105 lines (94 loc) 2.95 kB
import { SourceMap } from "rolldown"; export interface XBuildPlugin { name: string; order?: number; transform?: ( code: string, id: string | null, source: SourceMap | null ) => string | { code: string; map?: SourceMap | null }; } class PluginError extends Error { constructor(pluginName: string, message: string, errorOption?: ErrorOptions) { super(`[xbuild-plugin:${pluginName}]: ${message}`, errorOption); } } interface PluginHooks extends Required<Omit<XBuildPlugin, "name" | "order">> {} type PluginTransformType = keyof PluginHooks; export class PluginManager implements PluginHooks { private declare plugins: XBuildPlugin[]; private declare pluginMap: Map<XBuildPlugin["name"], XBuildPlugin>; constructor(plugins?: XBuildPlugin[] | PluginManager | null) { if (plugins instanceof PluginManager) { return plugins; } else { this.plugins = plugins || []; } this.plugins = this.plugins.filter(Boolean); this.pluginMap = new Map(); this.checkPluginName(); } addPlugins(plugins: Iterable<XBuildPlugin> = []): number { const n = this.plugins.push(...plugins); this.checkPluginName(); return n; } addPlugin(...plugins: XBuildPlugin[]): number { return this.addPlugins(plugins); } private get sortPlugins() { const notOrder = this.plugins.filter( ({ order }) => typeof order === "undefined" ); const sorted = this.plugins .filter(({ order }) => typeof order !== "undefined") .sort((a, b) => a.order! - b.order!); return [...sorted, ...notOrder]; } private checkPluginName() { this.pluginMap.clear(); try { for (const plugin of this.plugins) { if (this.pluginMap.has(plugin.name)) { throw new PluginError(plugin.name, "the name of plugin is exist."); } this.pluginMap.set(plugin.name, plugin); } } catch (e) { this.pluginMap.clear(); this.plugins.length = 0; throw e; } } transform(code: string, id: string | null, source: SourceMap | null) { let pluginName = ""; let _source = source; try { for (const plugin of this.sortPlugins) { pluginName = plugin.name; if (plugin.transform) { let recode = plugin.transform(code, id, _source); recode = typeof recode === "string" ? { code: recode, map: null } : recode; code = recode.code; _source = recode.map || _source; } } return { code, map: _source || source, }; } catch (e) { throw new PluginError( pluginName, e instanceof Error ? e.message : String(e) ); } } apply<T extends PluginTransformType>( hook: T, ...args: Parameters<PluginHooks[T]> ) { return this[hook].apply(this, args); } }