mirror of
https://github.com/anomalyco/opencode.git
synced 2026-05-29 15:50:21 +00:00
Refactor plugin/config loading, add theme-only plugin package support (#20556)
This commit is contained in:
@@ -33,7 +33,7 @@ test("adds tui plugin at runtime from spec", async () => {
|
||||
process.env.OPENCODE_PLUGIN_META_FILE = path.join(tmp.path, "plugin-meta.json")
|
||||
const get = spyOn(TuiConfig, "get").mockResolvedValue({
|
||||
plugin: [],
|
||||
plugin_records: undefined,
|
||||
plugin_origins: undefined,
|
||||
})
|
||||
const wait = spyOn(TuiConfig, "waitForDependencies").mockResolvedValue()
|
||||
const cwd = spyOn(process, "cwd").mockImplementation(() => tmp.path)
|
||||
@@ -59,3 +59,49 @@ test("adds tui plugin at runtime from spec", async () => {
|
||||
delete process.env.OPENCODE_PLUGIN_META_FILE
|
||||
}
|
||||
})
|
||||
|
||||
test("retries runtime add for file plugins after dependency wait", async () => {
|
||||
await using tmp = await tmpdir({
|
||||
init: async (dir) => {
|
||||
const mod = path.join(dir, "retry-plugin")
|
||||
const spec = pathToFileURL(mod).href
|
||||
const marker = path.join(dir, "retry-add.txt")
|
||||
await fs.mkdir(mod, { recursive: true })
|
||||
return { mod, spec, marker }
|
||||
},
|
||||
})
|
||||
|
||||
process.env.OPENCODE_PLUGIN_META_FILE = path.join(tmp.path, "plugin-meta.json")
|
||||
const get = spyOn(TuiConfig, "get").mockResolvedValue({
|
||||
plugin: [],
|
||||
plugin_origins: undefined,
|
||||
})
|
||||
const wait = spyOn(TuiConfig, "waitForDependencies").mockImplementation(async () => {
|
||||
await Bun.write(
|
||||
path.join(tmp.extra.mod, "index.ts"),
|
||||
`export default {
|
||||
id: "demo.add.retry",
|
||||
tui: async () => {
|
||||
await Bun.write(${JSON.stringify(tmp.extra.marker)}, "called")
|
||||
},
|
||||
}
|
||||
`,
|
||||
)
|
||||
})
|
||||
const cwd = spyOn(process, "cwd").mockImplementation(() => tmp.path)
|
||||
|
||||
try {
|
||||
await TuiPluginRuntime.init(createTuiPluginApi())
|
||||
|
||||
await expect(TuiPluginRuntime.addPlugin(tmp.extra.spec)).resolves.toBe(true)
|
||||
await expect(fs.readFile(tmp.extra.marker, "utf8")).resolves.toBe("called")
|
||||
expect(wait).toHaveBeenCalledTimes(1)
|
||||
expect(TuiPluginRuntime.list().find((item) => item.id === "demo.add.retry")?.active).toBe(true)
|
||||
} finally {
|
||||
await TuiPluginRuntime.dispose()
|
||||
cwd.mockRestore()
|
||||
get.mockRestore()
|
||||
wait.mockRestore()
|
||||
delete process.env.OPENCODE_PLUGIN_META_FILE
|
||||
}
|
||||
})
|
||||
|
||||
@@ -52,7 +52,7 @@ test("installs plugin without loading it", async () => {
|
||||
process.env.OPENCODE_PLUGIN_META_FILE = path.join(tmp.path, "plugin-meta.json")
|
||||
const cfg: Awaited<ReturnType<typeof TuiConfig.get>> = {
|
||||
plugin: [],
|
||||
plugin_records: undefined,
|
||||
plugin_origins: undefined,
|
||||
}
|
||||
const get = spyOn(TuiConfig, "get").mockImplementation(async () => cfg)
|
||||
const wait = spyOn(TuiConfig, "waitForDependencies").mockResolvedValue()
|
||||
|
||||
@@ -46,9 +46,9 @@ test("loads npm tui plugin from package ./tui export", async () => {
|
||||
process.env.OPENCODE_PLUGIN_META_FILE = path.join(tmp.path, "plugin-meta.json")
|
||||
const get = spyOn(TuiConfig, "get").mockResolvedValue({
|
||||
plugin: [[tmp.extra.spec, { marker: tmp.extra.marker }]],
|
||||
plugin_records: [
|
||||
plugin_origins: [
|
||||
{
|
||||
item: [tmp.extra.spec, { marker: tmp.extra.marker }],
|
||||
spec: [tmp.extra.spec, { marker: tmp.extra.marker }],
|
||||
scope: "local",
|
||||
source: path.join(tmp.path, "tui.json"),
|
||||
},
|
||||
@@ -108,9 +108,9 @@ test("does not use npm package exports dot for tui entry", async () => {
|
||||
process.env.OPENCODE_PLUGIN_META_FILE = path.join(tmp.path, "plugin-meta.json")
|
||||
const get = spyOn(TuiConfig, "get").mockResolvedValue({
|
||||
plugin: [tmp.extra.spec],
|
||||
plugin_records: [
|
||||
plugin_origins: [
|
||||
{
|
||||
item: tmp.extra.spec,
|
||||
spec: tmp.extra.spec,
|
||||
scope: "local",
|
||||
source: path.join(tmp.path, "tui.json"),
|
||||
},
|
||||
@@ -171,9 +171,9 @@ test("rejects npm tui export that resolves outside plugin directory", async () =
|
||||
process.env.OPENCODE_PLUGIN_META_FILE = path.join(tmp.path, "plugin-meta.json")
|
||||
const get = spyOn(TuiConfig, "get").mockResolvedValue({
|
||||
plugin: [tmp.extra.spec],
|
||||
plugin_records: [
|
||||
plugin_origins: [
|
||||
{
|
||||
item: tmp.extra.spec,
|
||||
spec: tmp.extra.spec,
|
||||
scope: "local",
|
||||
source: path.join(tmp.path, "tui.json"),
|
||||
},
|
||||
@@ -234,9 +234,9 @@ test("rejects npm tui plugin that exports server and tui together", async () =>
|
||||
process.env.OPENCODE_PLUGIN_META_FILE = path.join(tmp.path, "plugin-meta.json")
|
||||
const get = spyOn(TuiConfig, "get").mockResolvedValue({
|
||||
plugin: [tmp.extra.spec],
|
||||
plugin_records: [
|
||||
plugin_origins: [
|
||||
{
|
||||
item: tmp.extra.spec,
|
||||
spec: tmp.extra.spec,
|
||||
scope: "local",
|
||||
source: path.join(tmp.path, "tui.json"),
|
||||
},
|
||||
@@ -293,9 +293,9 @@ test("does not use npm package main for tui entry", async () => {
|
||||
process.env.OPENCODE_PLUGIN_META_FILE = path.join(tmp.path, "plugin-meta.json")
|
||||
const get = spyOn(TuiConfig, "get").mockResolvedValue({
|
||||
plugin: [tmp.extra.spec],
|
||||
plugin_records: [
|
||||
plugin_origins: [
|
||||
{
|
||||
item: tmp.extra.spec,
|
||||
spec: tmp.extra.spec,
|
||||
scope: "local",
|
||||
source: path.join(tmp.path, "tui.json"),
|
||||
},
|
||||
@@ -359,9 +359,9 @@ test("does not use directory package main for tui entry", async () => {
|
||||
process.env.OPENCODE_PLUGIN_META_FILE = path.join(tmp.path, "plugin-meta.json")
|
||||
const get = spyOn(TuiConfig, "get").mockResolvedValue({
|
||||
plugin: [tmp.extra.spec],
|
||||
plugin_records: [
|
||||
plugin_origins: [
|
||||
{
|
||||
item: tmp.extra.spec,
|
||||
spec: tmp.extra.spec,
|
||||
scope: "local",
|
||||
source: path.join(tmp.path, "tui.json"),
|
||||
},
|
||||
@@ -407,9 +407,9 @@ test("uses directory index fallback for tui when package.json is missing", async
|
||||
process.env.OPENCODE_PLUGIN_META_FILE = path.join(tmp.path, "plugin-meta.json")
|
||||
const get = spyOn(TuiConfig, "get").mockResolvedValue({
|
||||
plugin: [tmp.extra.spec],
|
||||
plugin_records: [
|
||||
plugin_origins: [
|
||||
{
|
||||
item: tmp.extra.spec,
|
||||
spec: tmp.extra.spec,
|
||||
scope: "local",
|
||||
source: path.join(tmp.path, "tui.json"),
|
||||
},
|
||||
@@ -465,9 +465,9 @@ test("uses npm package name when tui plugin id is omitted", async () => {
|
||||
process.env.OPENCODE_PLUGIN_META_FILE = path.join(tmp.path, "plugin-meta.json")
|
||||
const get = spyOn(TuiConfig, "get").mockResolvedValue({
|
||||
plugin: [[tmp.extra.spec, { marker: tmp.extra.marker }]],
|
||||
plugin_records: [
|
||||
plugin_origins: [
|
||||
{
|
||||
item: [tmp.extra.spec, { marker: tmp.extra.marker }],
|
||||
spec: [tmp.extra.spec, { marker: tmp.extra.marker }],
|
||||
scope: "local",
|
||||
source: path.join(tmp.path, "tui.json"),
|
||||
},
|
||||
|
||||
@@ -39,9 +39,9 @@ test("skips external tui plugins in pure mode", async () => {
|
||||
|
||||
const get = spyOn(TuiConfig, "get").mockResolvedValue({
|
||||
plugin: [[tmp.extra.spec, { marker: tmp.extra.marker }]],
|
||||
plugin_records: [
|
||||
plugin_origins: [
|
||||
{
|
||||
item: [tmp.extra.spec, { marker: tmp.extra.marker }],
|
||||
spec: [tmp.extra.spec, { marker: tmp.extra.marker }],
|
||||
scope: "local",
|
||||
source: path.join(tmp.path, "tui.json"),
|
||||
},
|
||||
|
||||
@@ -468,14 +468,14 @@ test("continues loading when a plugin is missing config metadata", async () => {
|
||||
[tmp.extra.goodSpec, { marker: tmp.extra.goodMarker }],
|
||||
tmp.extra.bareSpec,
|
||||
],
|
||||
plugin_records: [
|
||||
plugin_origins: [
|
||||
{
|
||||
item: [tmp.extra.goodSpec, { marker: tmp.extra.goodMarker }],
|
||||
spec: [tmp.extra.goodSpec, { marker: tmp.extra.goodMarker }],
|
||||
scope: "local",
|
||||
source: path.join(tmp.path, "tui.json"),
|
||||
},
|
||||
{
|
||||
item: tmp.extra.bareSpec,
|
||||
spec: tmp.extra.bareSpec,
|
||||
scope: "local",
|
||||
source: path.join(tmp.path, "tui.json"),
|
||||
},
|
||||
|
||||
@@ -44,9 +44,9 @@ test("toggles plugin runtime state by exported id", async () => {
|
||||
plugin_enabled: {
|
||||
"demo.toggle": false,
|
||||
},
|
||||
plugin_records: [
|
||||
plugin_origins: [
|
||||
{
|
||||
item: [tmp.extra.spec, { marker: tmp.extra.marker }],
|
||||
spec: [tmp.extra.spec, { marker: tmp.extra.marker }],
|
||||
scope: "local",
|
||||
source: path.join(tmp.path, "tui.json"),
|
||||
},
|
||||
@@ -122,9 +122,9 @@ test("kv plugin_enabled overrides tui config on startup", async () => {
|
||||
plugin_enabled: {
|
||||
"demo.startup": false,
|
||||
},
|
||||
plugin_records: [
|
||||
plugin_origins: [
|
||||
{
|
||||
item: [tmp.extra.spec, { marker: tmp.extra.marker }],
|
||||
spec: [tmp.extra.spec, { marker: tmp.extra.marker }],
|
||||
scope: "local",
|
||||
source: path.join(tmp.path, "tui.json"),
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user