Compare commits

...
Author SHA1 Message Date
Kit Langton 75cbba355e refactor: unwrap PluginLoader namespace + self-reexport 2026-04-16 19:53:44 -04:00
+27 -27
View File
@@ -11,30 +11,29 @@ import {
import { ConfigPlugin } from "@/config/plugin"
import { InstallationVersion } from "@/installation/version"
export namespace PluginLoader {
export type Plan = {
export type Plan = {
spec: string
options: ConfigPlugin.Options | undefined
deprecated: boolean
}
export type Resolved = Plan & {
}
export type Resolved = Plan & {
source: PluginSource
target: string
entry: string
pkg?: PluginPackage
}
export type Missing = Plan & {
}
export type Missing = Plan & {
source: PluginSource
target: string
pkg?: PluginPackage
message: string
}
export type Loaded = Resolved & {
}
export type Loaded = Resolved & {
mod: Record<string, unknown>
}
}
type Candidate = { origin: ConfigPlugin.Origin; plan: Plan }
type Report = {
type Candidate = { origin: ConfigPlugin.Origin; plan: Plan }
type Report = {
start?: (candidate: Candidate, retry: boolean) => void
missing?: (candidate: Candidate, retry: boolean, message: string, resolved: Missing) => void
error?: (
@@ -44,21 +43,21 @@ export namespace PluginLoader {
error: unknown,
resolved?: Resolved,
) => void
}
}
function plan(item: ConfigPlugin.Spec): Plan {
function plan(item: ConfigPlugin.Spec): Plan {
const spec = ConfigPlugin.pluginSpecifier(item)
return { spec, options: ConfigPlugin.pluginOptions(item), deprecated: isDeprecatedPlugin(spec) }
}
}
export async function resolve(
export async function resolve(
plan: Plan,
kind: PluginKind,
): Promise<
): Promise<
| { ok: true; value: Resolved }
| { ok: false; stage: "missing"; value: Missing }
| { ok: false; stage: "install" | "entry" | "compatibility"; error: unknown }
> {
> {
let target = ""
try {
target = await resolvePluginTarget(plan.spec)
@@ -94,9 +93,9 @@ export namespace PluginLoader {
}
}
return { ok: true, value: { ...plan, source: base.source, target: base.target, entry: base.entry, pkg: base.pkg } }
}
}
export async function load(row: Resolved): Promise<{ ok: true; value: Loaded } | { ok: false; error: unknown }> {
export async function load(row: Resolved): Promise<{ ok: true; value: Loaded } | { ok: false; error: unknown }> {
let mod
try {
mod = await import(row.entry)
@@ -105,16 +104,16 @@ export namespace PluginLoader {
}
if (!mod) return { ok: false, error: new Error(`Plugin ${row.spec} module is empty`) }
return { ok: true, value: { ...row, mod } }
}
}
async function attempt<R>(
async function attempt<R>(
candidate: Candidate,
kind: PluginKind,
retry: boolean,
finish: ((load: Loaded, origin: ConfigPlugin.Origin, retry: boolean) => Promise<R | undefined>) | undefined,
missing: ((value: Missing, origin: ConfigPlugin.Origin, retry: boolean) => Promise<R | undefined>) | undefined,
report: Report | undefined,
): Promise<R | undefined> {
): Promise<R | undefined> {
const plan = candidate.plan
if (plan.deprecated) return
report?.start?.(candidate, retry)
@@ -138,18 +137,18 @@ export namespace PluginLoader {
}
if (!finish) return loaded.value as R
return finish(loaded.value, candidate.origin, retry)
}
}
type Input<R> = {
type Input<R> = {
items: ConfigPlugin.Origin[]
kind: PluginKind
wait?: () => Promise<void>
finish?: (load: Loaded, origin: ConfigPlugin.Origin, retry: boolean) => Promise<R | undefined>
missing?: (value: Missing, origin: ConfigPlugin.Origin, retry: boolean) => Promise<R | undefined>
report?: Report
}
}
export async function loadExternal<R = Loaded>(input: Input<R>): Promise<R[]> {
export async function loadExternal<R = Loaded>(input: Input<R>): Promise<R[]> {
const candidates = input.items.map((origin) => ({ origin, plan: plan(origin.spec) }))
const list: Array<Promise<R | undefined>> = []
for (const candidate of candidates) {
@@ -170,5 +169,6 @@ export namespace PluginLoader {
const ready: R[] = []
for (const item of out) if (item !== undefined) ready.push(item)
return ready
}
}
export * as PluginLoader from "./loader"