Refactor plugin/config loading, add theme-only plugin package support (#20556)

This commit is contained in:
Sebastian
2026-04-02 01:50:22 +02:00
committed by GitHub
parent 854484babf
commit f6fd43e574
24 changed files with 1246 additions and 539 deletions

View File

@@ -50,6 +50,10 @@ function resolveExportPath(raw: string, dir: string) {
return path.resolve(dir, raw)
}
function isAbsolutePath(raw: string) {
return path.isAbsolute(raw) || /^[A-Za-z]:[\\/]/.test(raw)
}
function extractExportValue(value: unknown): string | undefined {
if (typeof value === "string") return value
if (!isRecord(value)) return undefined
@@ -68,14 +72,18 @@ function packageMain(pkg: PluginPackage) {
return next
}
function resolvePackagePath(spec: string, raw: string, kind: PluginKind, pkg: PluginPackage) {
function resolvePackageFile(spec: string, raw: string, kind: string, pkg: PluginPackage) {
const resolved = resolveExportPath(raw, pkg.dir)
const root = Filesystem.resolve(pkg.dir)
const next = Filesystem.resolve(resolved)
if (!Filesystem.contains(root, next)) {
throw new Error(`Plugin ${spec} resolved ${kind} entry outside plugin directory`)
}
return pathToFileURL(next).href
return next
}
function resolvePackagePath(spec: string, raw: string, kind: PluginKind, pkg: PluginPackage) {
return pathToFileURL(resolvePackageFile(spec, raw, kind, pkg)).href
}
function resolvePackageEntrypoint(spec: string, kind: PluginKind, pkg: PluginPackage) {
@@ -106,7 +114,7 @@ async function resolveDirectoryIndex(dir: string) {
async function resolveTargetDirectory(target: string) {
const file = targetPath(target)
if (!file) return
const stat = Filesystem.stat(file)
const stat = await Filesystem.statAsync(file)
if (!stat?.isDirectory()) return
return file
}
@@ -147,13 +155,13 @@ async function resolvePluginEntrypoint(spec: string, target: string, kind: Plugi
}
export function isPathPluginSpec(spec: string) {
return spec.startsWith("file://") || spec.startsWith(".") || path.isAbsolute(spec) || /^[A-Za-z]:[\\/]/.test(spec)
return spec.startsWith("file://") || spec.startsWith(".") || isAbsolutePath(spec)
}
export async function resolvePathPluginTarget(spec: string) {
const raw = spec.startsWith("file://") ? fileURLToPath(spec) : spec
const file = path.isAbsolute(raw) || /^[A-Za-z]:[\\/]/.test(raw) ? raw : path.resolve(raw)
const stat = Filesystem.stat(file)
const stat = await Filesystem.statAsync(file)
if (!stat?.isDirectory()) {
if (spec.startsWith("file://")) return spec
return pathToFileURL(file).href
@@ -190,7 +198,7 @@ export async function resolvePluginTarget(spec: string, parsed = parsePluginSpec
export async function readPluginPackage(target: string): Promise<PluginPackage> {
const file = target.startsWith("file://") ? fileURLToPath(target) : target
const stat = Filesystem.stat(file)
const stat = await Filesystem.statAsync(file)
const dir = stat?.isDirectory() ? file : path.dirname(file)
const pkg = path.join(dir, "package.json")
const json = await Filesystem.readJson<Record<string, unknown>>(pkg)
@@ -211,6 +219,32 @@ export async function createPluginEntry(spec: string, target: string, kind: Plug
}
}
export function readPackageThemes(spec: string, pkg: PluginPackage) {
const field = pkg.json["oc-themes"]
if (field === undefined) return []
if (!Array.isArray(field)) {
throw new TypeError(`Plugin ${spec} has invalid oc-themes field`)
}
const list = field.map((item) => {
if (typeof item !== "string") {
throw new TypeError(`Plugin ${spec} has invalid oc-themes entry`)
}
const raw = item.trim()
if (!raw) {
throw new TypeError(`Plugin ${spec} has empty oc-themes entry`)
}
if (raw.startsWith("file://") || isAbsolutePath(raw)) {
throw new TypeError(`Plugin ${spec} oc-themes entry must be relative: ${item}`)
}
return resolvePackageFile(spec, raw, "oc-themes", pkg)
})
return Array.from(new Set(list))
}
export function readPluginId(id: unknown, spec: string) {
if (id === undefined) return
if (typeof id !== "string") throw new TypeError(`Plugin ${spec} has invalid id type ${typeof id}`)