Skip to content

Commit 32548bc

Browse files
authored
refactor: unwrap ConfigPlugin namespace to flat exports + self-reexport (#22876)
1 parent 86c54c5 commit 32548bc

1 file changed

Lines changed: 67 additions & 67 deletions

File tree

packages/opencode/src/config/plugin.ts

Lines changed: 67 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -4,81 +4,81 @@ import { pathToFileURL } from "url"
44
import { isPathPluginSpec, parsePluginSpecifier, resolvePathPluginTarget } from "@/plugin/shared"
55
import path from "path"
66

7-
export namespace ConfigPlugin {
8-
const Options = z.record(z.string(), z.unknown())
9-
export type Options = z.infer<typeof Options>
10-
11-
// Spec is the user-config value: either just a plugin identifier, or the identifier plus inline options.
12-
// It answers "what should we load?" but says nothing about where that value came from.
13-
export const Spec = z.union([z.string(), z.tuple([z.string(), Options])])
14-
export type Spec = z.infer<typeof Spec>
15-
16-
export type Scope = "global" | "local"
17-
18-
// Origin keeps the original config provenance attached to a spec.
19-
// After multiple config files are merged, callers still need to know which file declared the plugin
20-
// and whether it should behave like a global or project-local plugin.
21-
export type Origin = {
22-
spec: Spec
23-
source: string
24-
scope: Scope
25-
}
7+
const Options = z.record(z.string(), z.unknown())
8+
export type Options = z.infer<typeof Options>
269

27-
export async function load(dir: string) {
28-
const plugins: ConfigPlugin.Spec[] = []
29-
30-
for (const item of await Glob.scan("{plugin,plugins}/*.{ts,js}", {
31-
cwd: dir,
32-
absolute: true,
33-
dot: true,
34-
symlink: true,
35-
})) {
36-
plugins.push(pathToFileURL(item).href)
37-
}
38-
return plugins
39-
}
10+
// Spec is the user-config value: either just a plugin identifier, or the identifier plus inline options.
11+
// It answers "what should we load?" but says nothing about where that value came from.
12+
export const Spec = z.union([z.string(), z.tuple([z.string(), Options])])
13+
export type Spec = z.infer<typeof Spec>
4014

41-
export function pluginSpecifier(plugin: Spec): string {
42-
return Array.isArray(plugin) ? plugin[0] : plugin
43-
}
15+
export type Scope = "global" | "local"
16+
17+
// Origin keeps the original config provenance attached to a spec.
18+
// After multiple config files are merged, callers still need to know which file declared the plugin
19+
// and whether it should behave like a global or project-local plugin.
20+
export type Origin = {
21+
spec: Spec
22+
source: string
23+
scope: Scope
24+
}
25+
26+
export async function load(dir: string) {
27+
const plugins: Spec[] = []
4428

45-
export function pluginOptions(plugin: Spec): Options | undefined {
46-
return Array.isArray(plugin) ? plugin[1] : undefined
29+
for (const item of await Glob.scan("{plugin,plugins}/*.{ts,js}", {
30+
cwd: dir,
31+
absolute: true,
32+
dot: true,
33+
symlink: true,
34+
})) {
35+
plugins.push(pathToFileURL(item).href)
4736
}
37+
return plugins
38+
}
39+
40+
export function pluginSpecifier(plugin: Spec): string {
41+
return Array.isArray(plugin) ? plugin[0] : plugin
42+
}
43+
44+
export function pluginOptions(plugin: Spec): Options | undefined {
45+
return Array.isArray(plugin) ? plugin[1] : undefined
46+
}
4847

49-
// Path-like specs are resolved relative to the config file that declared them so merges later on do not
50-
// accidentally reinterpret `./plugin.ts` relative to some other directory.
51-
export async function resolvePluginSpec(plugin: Spec, configFilepath: string): Promise<Spec> {
52-
const spec = pluginSpecifier(plugin)
53-
if (!isPathPluginSpec(spec)) return plugin
48+
// Path-like specs are resolved relative to the config file that declared them so merges later on do not
49+
// accidentally reinterpret `./plugin.ts` relative to some other directory.
50+
export async function resolvePluginSpec(plugin: Spec, configFilepath: string): Promise<Spec> {
51+
const spec = pluginSpecifier(plugin)
52+
if (!isPathPluginSpec(spec)) return plugin
5453

55-
const base = path.dirname(configFilepath)
56-
const file = (() => {
57-
if (spec.startsWith("file://")) return spec
58-
if (path.isAbsolute(spec) || /^[A-Za-z]:[\\/]/.test(spec)) return pathToFileURL(spec).href
59-
return pathToFileURL(path.resolve(base, spec)).href
60-
})()
54+
const base = path.dirname(configFilepath)
55+
const file = (() => {
56+
if (spec.startsWith("file://")) return spec
57+
if (path.isAbsolute(spec) || /^[A-Za-z]:[\\/]/.test(spec)) return pathToFileURL(spec).href
58+
return pathToFileURL(path.resolve(base, spec)).href
59+
})()
6160

62-
const resolved = await resolvePathPluginTarget(file).catch(() => file)
61+
const resolved = await resolvePathPluginTarget(file).catch(() => file)
6362

64-
if (Array.isArray(plugin)) return [resolved, plugin[1]]
65-
return resolved
66-
}
63+
if (Array.isArray(plugin)) return [resolved, plugin[1]]
64+
return resolved
65+
}
66+
67+
// Dedupe on the load identity (package name for npm specs, exact file URL for local specs), but keep the
68+
// full Origin so downstream code still knows which config file won and where follow-up writes should go.
69+
export function deduplicatePluginOrigins(plugins: Origin[]): Origin[] {
70+
const seen = new Set<string>()
71+
const list: Origin[] = []
6772

68-
// Dedupe on the load identity (package name for npm specs, exact file URL for local specs), but keep the
69-
// full Origin so downstream code still knows which config file won and where follow-up writes should go.
70-
export function deduplicatePluginOrigins(plugins: Origin[]): Origin[] {
71-
const seen = new Set<string>()
72-
const list: Origin[] = []
73-
74-
for (const plugin of plugins.toReversed()) {
75-
const spec = pluginSpecifier(plugin.spec)
76-
const name = spec.startsWith("file://") ? spec : parsePluginSpecifier(spec).pkg
77-
if (seen.has(name)) continue
78-
seen.add(name)
79-
list.push(plugin)
80-
}
81-
82-
return list.toReversed()
73+
for (const plugin of plugins.toReversed()) {
74+
const spec = pluginSpecifier(plugin.spec)
75+
const name = spec.startsWith("file://") ? spec : parsePluginSpecifier(spec).pkg
76+
if (seen.has(name)) continue
77+
seen.add(name)
78+
list.push(plugin)
8379
}
80+
81+
return list.toReversed()
8482
}
83+
84+
export * as ConfigPlugin from "./plugin"

0 commit comments

Comments
 (0)