fix(config): properly pass ts-based options to plugins
This commit is contained in:
@@ -19,6 +19,7 @@ export interface RegisteredComponent {
|
|||||||
class ComponentRegistry {
|
class ComponentRegistry {
|
||||||
private components = new Map<string, RegisteredComponent>()
|
private components = new Map<string, RegisteredComponent>()
|
||||||
private instanceCache = new Map<string, QuartzComponent>()
|
private instanceCache = new Map<string, QuartzComponent>()
|
||||||
|
private optionOverrides = new Map<string, Record<string, unknown>>()
|
||||||
|
|
||||||
register(
|
register(
|
||||||
name: string,
|
name: string,
|
||||||
@@ -41,6 +42,17 @@ class ComponentRegistry {
|
|||||||
return new Map(this.components)
|
return new Map(this.components)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Store option overrides for a plugin, keyed by plugin directory name. */
|
||||||
|
setOptionOverrides(pluginName: string, opts?: Record<string, unknown>): void {
|
||||||
|
if (!opts || Object.keys(opts).length === 0) return
|
||||||
|
this.optionOverrides.set(pluginName, { ...this.optionOverrides.get(pluginName), ...opts })
|
||||||
|
this.instanceCache.clear()
|
||||||
|
}
|
||||||
|
|
||||||
|
getOptionOverrides(pluginName: string): Record<string, unknown> | undefined {
|
||||||
|
return this.optionOverrides.get(pluginName)
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Instantiate a component constructor with options, returning a cached instance
|
* Instantiate a component constructor with options, returning a cached instance
|
||||||
* if the same constructor was already called with equivalent options.
|
* if the same constructor was already called with equivalent options.
|
||||||
|
|||||||
@@ -349,7 +349,8 @@ export async function loadQuartzConfig(
|
|||||||
// If the module exports an init() function, call it with merged options
|
// If the module exports an init() function, call it with merged options
|
||||||
// so component-only plugins can receive user configuration from YAML.
|
// so component-only plugins can receive user configuration from YAML.
|
||||||
if (typeof module.init === "function") {
|
if (typeof module.init === "function") {
|
||||||
const options = { ...manifest?.defaultOptions, ...entry.options }
|
const initOverrides = componentRegistry.getOptionOverrides(gitSpec.name)
|
||||||
|
const options = { ...manifest?.defaultOptions, ...entry.options, ...initOverrides }
|
||||||
await module.init(Object.keys(options).length > 0 ? options : undefined)
|
await module.init(Object.keys(options).length > 0 ? options : undefined)
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
@@ -440,7 +441,8 @@ export async function loadQuartzConfig(
|
|||||||
)
|
)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
const options = { ...manifest?.defaultOptions, ...entry.options }
|
const pluginOverrides = componentRegistry.getOptionOverrides(gitSpec.name)
|
||||||
|
const options = { ...manifest?.defaultOptions, ...entry.options, ...pluginOverrides }
|
||||||
instances.push(factory(Object.keys(options).length > 0 ? options : undefined))
|
instances.push(factory(Object.keys(options).length > 0 ? options : undefined))
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error(
|
console.error(
|
||||||
@@ -636,7 +638,8 @@ export async function loadQuartzLayout(layoutOverrides?: {
|
|||||||
if (footerReg) {
|
if (footerReg) {
|
||||||
if (typeof footerReg.component === "function" && !("displayName" in footerReg.component)) {
|
if (typeof footerReg.component === "function" && !("displayName" in footerReg.component)) {
|
||||||
// It's a constructor — use registry cache for consistent instances
|
// It's a constructor — use registry cache for consistent instances
|
||||||
const opts = { ...footerEntry.options }
|
const footerOverrides = componentRegistry.getOptionOverrides("footer")
|
||||||
|
const opts = { ...footerEntry.options, ...footerOverrides }
|
||||||
footer = componentRegistry.instantiate(
|
footer = componentRegistry.instantiate(
|
||||||
footerReg.component as QuartzComponentConstructor,
|
footerReg.component as QuartzComponentConstructor,
|
||||||
Object.keys(opts).length > 0 ? opts : undefined,
|
Object.keys(opts).length > 0 ? opts : undefined,
|
||||||
@@ -726,7 +729,8 @@ function buildLayoutForEntries(
|
|||||||
if (typeof reg.component === "function" && !("displayName" in reg.component)) {
|
if (typeof reg.component === "function" && !("displayName" in reg.component)) {
|
||||||
// It's a constructor — use registry cache to avoid duplicate instances
|
// It's a constructor — use registry cache to avoid duplicate instances
|
||||||
// (and duplicate afterDOMLoaded scripts) across page-type layouts
|
// (and duplicate afterDOMLoaded scripts) across page-type layouts
|
||||||
const opts = { ...entry.options }
|
const tsOverrides = componentRegistry.getOptionOverrides(name)
|
||||||
|
const opts = { ...entry.options, ...tsOverrides }
|
||||||
const optsArg = Object.keys(opts).length > 0 ? opts : undefined
|
const optsArg = Object.keys(opts).length > 0 ? opts : undefined
|
||||||
component = componentRegistry.instantiate(
|
component = componentRegistry.instantiate(
|
||||||
reg.component as QuartzComponentConstructor,
|
reg.component as QuartzComponentConstructor,
|
||||||
|
|||||||
@@ -829,14 +829,16 @@ export async function regeneratePluginIndex(options: { verbose?: boolean } = {})
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
const plugins = fs.readdirSync(PLUGINS_CACHE_DIR).filter((name) => {
|
const pluginDirs = fs.readdirSync(PLUGINS_CACHE_DIR).filter((name) => {
|
||||||
const pluginPath = path.join(PLUGINS_CACHE_DIR, name)
|
const pluginPath = path.join(PLUGINS_CACHE_DIR, name)
|
||||||
return fs.statSync(pluginPath).isDirectory()
|
return fs.statSync(pluginPath).isDirectory()
|
||||||
})
|
})
|
||||||
|
|
||||||
const exports: string[] = []
|
// Phase 1: Collect all exports per plugin, detect conflicts
|
||||||
|
const pluginExports = new Map<string, { named: string[]; types: string[] }>()
|
||||||
|
const nameCount = new Map<string, number>()
|
||||||
|
|
||||||
for (const pluginName of plugins) {
|
for (const pluginName of pluginDirs) {
|
||||||
const pluginDir = path.join(PLUGINS_CACHE_DIR, pluginName)
|
const pluginDir = path.join(PLUGINS_CACHE_DIR, pluginName)
|
||||||
const distIndex = path.join(pluginDir, "dist", "index.d.ts")
|
const distIndex = path.join(pluginDir, "dist", "index.d.ts")
|
||||||
|
|
||||||
@@ -849,27 +851,85 @@ export async function regeneratePluginIndex(options: { verbose?: boolean } = {})
|
|||||||
|
|
||||||
const dtsContent = fs.readFileSync(distIndex, "utf-8")
|
const dtsContent = fs.readFileSync(distIndex, "utf-8")
|
||||||
const exportedNames = parseExportsFromDts(dtsContent)
|
const exportedNames = parseExportsFromDts(dtsContent)
|
||||||
|
const named = exportedNames.filter((e) => !e.startsWith("type "))
|
||||||
|
const types = exportedNames.filter((e) => e.startsWith("type ")).map((e) => e.slice(5))
|
||||||
|
|
||||||
if (exportedNames.length > 0) {
|
if (named.length > 0 || types.length > 0) {
|
||||||
const namedExports = exportedNames.filter((e) => !e.startsWith("type "))
|
pluginExports.set(pluginName, { named, types })
|
||||||
const typeExports = exportedNames.filter((e) => e.startsWith("type ")).map((e) => e.slice(5))
|
for (const n of named) {
|
||||||
|
nameCount.set(n, (nameCount.get(n) ?? 0) + 1)
|
||||||
if (namedExports.length > 0) {
|
|
||||||
exports.push(`export { ${namedExports.join(", ")} } from "./${pluginName}"`)
|
|
||||||
}
|
|
||||||
if (typeExports.length > 0) {
|
|
||||||
exports.push(`export type { ${typeExports.join(", ")} } from "./${pluginName}"`)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const indexContent = exports.join("\n") + "\n"
|
// Phase 2: Generate index with registry import, plugin map, and conditional top-level exports
|
||||||
|
const lines: string[] = []
|
||||||
|
|
||||||
|
lines.push(`import { componentRegistry } from "../../quartz/components/registry"`)
|
||||||
|
lines.push("")
|
||||||
|
|
||||||
|
// Type re-exports
|
||||||
|
for (const [pluginName, { types }] of pluginExports) {
|
||||||
|
if (types.length > 0) {
|
||||||
|
lines.push(`export type { ${types.join(", ")} } from "./${pluginName}"`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
lines.push("")
|
||||||
|
|
||||||
|
// Generate the plugins map with override wrappers
|
||||||
|
lines.push(
|
||||||
|
`export const plugins: Record<string, Record<string, (...args: unknown[]) => void>> = {`,
|
||||||
|
)
|
||||||
|
for (const [pluginName, { named }] of pluginExports) {
|
||||||
|
if (named.length === 0) continue
|
||||||
|
const escapedName = pluginName.replace(/"/g, '\\"')
|
||||||
|
lines.push(` "${escapedName}": {`)
|
||||||
|
for (const n of named) {
|
||||||
|
lines.push(
|
||||||
|
` ${n}: (...args: unknown[]) => { componentRegistry.setOptionOverrides("${escapedName}", args[0] as Record<string, unknown>); },`,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
lines.push(` },`)
|
||||||
|
}
|
||||||
|
lines.push(`}`)
|
||||||
|
lines.push("")
|
||||||
|
|
||||||
|
// Top-level exports: only for non-conflicting names
|
||||||
|
for (const [pluginName, { named }] of pluginExports) {
|
||||||
|
if (named.length === 0) continue
|
||||||
|
|
||||||
|
const unique = named.filter((n) => (nameCount.get(n) ?? 0) === 1)
|
||||||
|
const conflicting = named.filter((n) => (nameCount.get(n) ?? 0) > 1)
|
||||||
|
|
||||||
|
if (unique.length > 0) {
|
||||||
|
const escapedName = pluginName.replace(/"/g, '\\"')
|
||||||
|
for (const n of unique) {
|
||||||
|
lines.push(`export const ${n} = plugins["${escapedName}"].${n}`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (conflicting.length > 0 && options.verbose) {
|
||||||
|
for (const n of conflicting) {
|
||||||
|
console.warn(
|
||||||
|
styleText("yellow", `⚠`),
|
||||||
|
`Export "${n}" conflicts across plugins — use plugins["${pluginName}"].${n} in quartz.ts`,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
lines.push("")
|
||||||
|
|
||||||
|
const indexContent = lines.join("\n")
|
||||||
const indexPath = path.join(PLUGINS_CACHE_DIR, "index.ts")
|
const indexPath = path.join(PLUGINS_CACHE_DIR, "index.ts")
|
||||||
|
|
||||||
fs.writeFileSync(indexPath, indexContent)
|
fs.writeFileSync(indexPath, indexContent)
|
||||||
|
|
||||||
if (options.verbose) {
|
if (options.verbose) {
|
||||||
console.log(styleText("green", `✓`), `Regenerated plugin index with ${plugins.length} plugins`)
|
console.log(
|
||||||
|
styleText("green", `✓`),
|
||||||
|
`Regenerated plugin index with ${pluginDirs.length} plugins`,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user