@msom/xbuild
Version:
@msom/xbuild
105 lines (94 loc) • 2.95 kB
text/typescript
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);
}
}