summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorKit Langton <[email protected]>2026-04-16 16:59:17 -0400
committerGitHub <[email protected]>2026-04-16 16:59:17 -0400
commit32548bcb4af7db393d91f315ec2222954326e766 (patch)
tree363b1c7d84d0d1d03e5e8c3318a0830eb67c7c7d
parent86c54c5acc7b3bf4d527cbd9fdfbd5dfc925b4d6 (diff)
downloadopencode-32548bcb4af7db393d91f315ec2222954326e766.tar.gz
opencode-32548bcb4af7db393d91f315ec2222954326e766.zip
refactor: unwrap ConfigPlugin namespace to flat exports + self-reexport (#22876)
-rw-r--r--packages/opencode/src/config/plugin.ts134
1 files changed, 67 insertions, 67 deletions
diff --git a/packages/opencode/src/config/plugin.ts b/packages/opencode/src/config/plugin.ts
index 3a10c0a71..7d335bcc5 100644
--- a/packages/opencode/src/config/plugin.ts
+++ b/packages/opencode/src/config/plugin.ts
@@ -4,81 +4,81 @@ import { pathToFileURL } from "url"
import { isPathPluginSpec, parsePluginSpecifier, resolvePathPluginTarget } from "@/plugin/shared"
import path from "path"
-export namespace ConfigPlugin {
- const Options = z.record(z.string(), z.unknown())
- export type Options = z.infer<typeof Options>
-
- // Spec is the user-config value: either just a plugin identifier, or the identifier plus inline options.
- // It answers "what should we load?" but says nothing about where that value came from.
- export const Spec = z.union([z.string(), z.tuple([z.string(), Options])])
- export type Spec = z.infer<typeof Spec>
-
- export type Scope = "global" | "local"
-
- // Origin keeps the original config provenance attached to a spec.
- // After multiple config files are merged, callers still need to know which file declared the plugin
- // and whether it should behave like a global or project-local plugin.
- export type Origin = {
- spec: Spec
- source: string
- scope: Scope
- }
+const Options = z.record(z.string(), z.unknown())
+export type Options = z.infer<typeof Options>
- export async function load(dir: string) {
- const plugins: ConfigPlugin.Spec[] = []
-
- for (const item of await Glob.scan("{plugin,plugins}/*.{ts,js}", {
- cwd: dir,
- absolute: true,
- dot: true,
- symlink: true,
- })) {
- plugins.push(pathToFileURL(item).href)
- }
- return plugins
- }
+// Spec is the user-config value: either just a plugin identifier, or the identifier plus inline options.
+// It answers "what should we load?" but says nothing about where that value came from.
+export const Spec = z.union([z.string(), z.tuple([z.string(), Options])])
+export type Spec = z.infer<typeof Spec>
- export function pluginSpecifier(plugin: Spec): string {
- return Array.isArray(plugin) ? plugin[0] : plugin
- }
+export type Scope = "global" | "local"
+
+// Origin keeps the original config provenance attached to a spec.
+// After multiple config files are merged, callers still need to know which file declared the plugin
+// and whether it should behave like a global or project-local plugin.
+export type Origin = {
+ spec: Spec
+ source: string
+ scope: Scope
+}
+
+export async function load(dir: string) {
+ const plugins: Spec[] = []
- export function pluginOptions(plugin: Spec): Options | undefined {
- return Array.isArray(plugin) ? plugin[1] : undefined
+ for (const item of await Glob.scan("{plugin,plugins}/*.{ts,js}", {
+ cwd: dir,
+ absolute: true,
+ dot: true,
+ symlink: true,
+ })) {
+ plugins.push(pathToFileURL(item).href)
}
+ return plugins
+}
+
+export function pluginSpecifier(plugin: Spec): string {
+ return Array.isArray(plugin) ? plugin[0] : plugin
+}
+
+export function pluginOptions(plugin: Spec): Options | undefined {
+ return Array.isArray(plugin) ? plugin[1] : undefined
+}
- // Path-like specs are resolved relative to the config file that declared them so merges later on do not
- // accidentally reinterpret `./plugin.ts` relative to some other directory.
- export async function resolvePluginSpec(plugin: Spec, configFilepath: string): Promise<Spec> {
- const spec = pluginSpecifier(plugin)
- if (!isPathPluginSpec(spec)) return plugin
+// Path-like specs are resolved relative to the config file that declared them so merges later on do not
+// accidentally reinterpret `./plugin.ts` relative to some other directory.
+export async function resolvePluginSpec(plugin: Spec, configFilepath: string): Promise<Spec> {
+ const spec = pluginSpecifier(plugin)
+ if (!isPathPluginSpec(spec)) return plugin
- const base = path.dirname(configFilepath)
- const file = (() => {
- if (spec.startsWith("file://")) return spec
- if (path.isAbsolute(spec) || /^[A-Za-z]:[\\/]/.test(spec)) return pathToFileURL(spec).href
- return pathToFileURL(path.resolve(base, spec)).href
- })()
+ const base = path.dirname(configFilepath)
+ const file = (() => {
+ if (spec.startsWith("file://")) return spec
+ if (path.isAbsolute(spec) || /^[A-Za-z]:[\\/]/.test(spec)) return pathToFileURL(spec).href
+ return pathToFileURL(path.resolve(base, spec)).href
+ })()
- const resolved = await resolvePathPluginTarget(file).catch(() => file)
+ const resolved = await resolvePathPluginTarget(file).catch(() => file)
- if (Array.isArray(plugin)) return [resolved, plugin[1]]
- return resolved
- }
+ if (Array.isArray(plugin)) return [resolved, plugin[1]]
+ return resolved
+}
+
+// Dedupe on the load identity (package name for npm specs, exact file URL for local specs), but keep the
+// full Origin so downstream code still knows which config file won and where follow-up writes should go.
+export function deduplicatePluginOrigins(plugins: Origin[]): Origin[] {
+ const seen = new Set<string>()
+ const list: Origin[] = []
- // Dedupe on the load identity (package name for npm specs, exact file URL for local specs), but keep the
- // full Origin so downstream code still knows which config file won and where follow-up writes should go.
- export function deduplicatePluginOrigins(plugins: Origin[]): Origin[] {
- const seen = new Set<string>()
- const list: Origin[] = []
-
- for (const plugin of plugins.toReversed()) {
- const spec = pluginSpecifier(plugin.spec)
- const name = spec.startsWith("file://") ? spec : parsePluginSpecifier(spec).pkg
- if (seen.has(name)) continue
- seen.add(name)
- list.push(plugin)
- }
-
- return list.toReversed()
+ for (const plugin of plugins.toReversed()) {
+ const spec = pluginSpecifier(plugin.spec)
+ const name = spec.startsWith("file://") ? spec : parsePluginSpecifier(spec).pkg
+ if (seen.has(name)) continue
+ seen.add(name)
+ list.push(plugin)
}
+
+ return list.toReversed()
}
+
+export * as ConfigPlugin from "./plugin"