mirror of
https://github.com/anomalyco/opencode.git
synced 2026-05-17 01:52:55 +00:00
Refactor into plugin loader and do not enforce (#20112)
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_meta: undefined,
|
||||
plugin_records: undefined,
|
||||
})
|
||||
const wait = spyOn(TuiConfig, "waitForDependencies").mockResolvedValue()
|
||||
const cwd = spyOn(process, "cwd").mockImplementation(() => tmp.path)
|
||||
|
||||
@@ -48,7 +48,7 @@ test("installs plugin without loading it", async () => {
|
||||
process.env.OPENCODE_PLUGIN_META_FILE = path.join(tmp.path, "plugin-meta.json")
|
||||
let cfg: Awaited<ReturnType<typeof TuiConfig.get>> = {
|
||||
plugin: [],
|
||||
plugin_meta: undefined,
|
||||
plugin_records: undefined,
|
||||
}
|
||||
const get = spyOn(TuiConfig, "get").mockImplementation(async () => cfg)
|
||||
const wait = spyOn(TuiConfig, "waitForDependencies").mockResolvedValue()
|
||||
@@ -68,12 +68,13 @@ test("installs plugin without loading it", async () => {
|
||||
await TuiPluginRuntime.init(api)
|
||||
cfg = {
|
||||
plugin: [[tmp.extra.spec, { marker: tmp.extra.marker }]],
|
||||
plugin_meta: {
|
||||
[tmp.extra.spec]: {
|
||||
plugin_records: [
|
||||
{
|
||||
item: [tmp.extra.spec, { marker: tmp.extra.marker }],
|
||||
scope: "local",
|
||||
source: path.join(tmp.path, "tui.json"),
|
||||
},
|
||||
},
|
||||
],
|
||||
}
|
||||
|
||||
const out = await TuiPluginRuntime.installPlugin(tmp.extra.spec)
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { expect, spyOn, test } from "bun:test"
|
||||
import fs from "fs/promises"
|
||||
import path from "path"
|
||||
import { pathToFileURL } from "url"
|
||||
import { tmpdir } from "../../fixture/fixture"
|
||||
import { createTuiPluginApi } from "../../fixture/tui-plugin"
|
||||
import { TuiConfig } from "../../../src/config/tui"
|
||||
@@ -45,9 +46,13 @@ 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_meta: {
|
||||
[tmp.extra.spec]: { scope: "local", source: path.join(tmp.path, "tui.json") },
|
||||
},
|
||||
plugin_records: [
|
||||
{
|
||||
item: [tmp.extra.spec, { marker: tmp.extra.marker }],
|
||||
scope: "local",
|
||||
source: path.join(tmp.path, "tui.json"),
|
||||
},
|
||||
],
|
||||
})
|
||||
const wait = spyOn(TuiConfig, "waitForDependencies").mockResolvedValue()
|
||||
const cwd = spyOn(process, "cwd").mockImplementation(() => tmp.path)
|
||||
@@ -70,6 +75,65 @@ test("loads npm tui plugin from package ./tui export", async () => {
|
||||
}
|
||||
})
|
||||
|
||||
test("does not use npm package exports dot for tui entry", async () => {
|
||||
await using tmp = await tmpdir({
|
||||
init: async (dir) => {
|
||||
const mod = path.join(dir, "mods", "acme-plugin")
|
||||
const marker = path.join(dir, "dot-called.txt")
|
||||
await fs.mkdir(mod, { recursive: true })
|
||||
|
||||
await Bun.write(
|
||||
path.join(mod, "package.json"),
|
||||
JSON.stringify({
|
||||
name: "acme-plugin",
|
||||
type: "module",
|
||||
exports: { ".": "./index.js" },
|
||||
}),
|
||||
)
|
||||
await Bun.write(
|
||||
path.join(mod, "index.js"),
|
||||
`export default {
|
||||
id: "demo.dot",
|
||||
tui: async () => {
|
||||
await Bun.write(${JSON.stringify(marker)}, "called")
|
||||
},
|
||||
}
|
||||
`,
|
||||
)
|
||||
|
||||
return { mod, marker, spec: "acme-plugin@1.0.0" }
|
||||
},
|
||||
})
|
||||
|
||||
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: [
|
||||
{
|
||||
item: tmp.extra.spec,
|
||||
scope: "local",
|
||||
source: path.join(tmp.path, "tui.json"),
|
||||
},
|
||||
],
|
||||
})
|
||||
const wait = spyOn(TuiConfig, "waitForDependencies").mockResolvedValue()
|
||||
const cwd = spyOn(process, "cwd").mockImplementation(() => tmp.path)
|
||||
const install = spyOn(BunProc, "install").mockResolvedValue(tmp.extra.mod)
|
||||
|
||||
try {
|
||||
await TuiPluginRuntime.init(createTuiPluginApi())
|
||||
await expect(fs.readFile(tmp.extra.marker, "utf8")).rejects.toThrow()
|
||||
expect(TuiPluginRuntime.list().some((item) => item.spec === tmp.extra.spec)).toBe(false)
|
||||
} finally {
|
||||
await TuiPluginRuntime.dispose()
|
||||
install.mockRestore()
|
||||
cwd.mockRestore()
|
||||
get.mockRestore()
|
||||
wait.mockRestore()
|
||||
delete process.env.OPENCODE_PLUGIN_META_FILE
|
||||
}
|
||||
})
|
||||
|
||||
test("rejects npm tui export that resolves outside plugin directory", async () => {
|
||||
await using tmp = await tmpdir({
|
||||
init: async (dir) => {
|
||||
@@ -107,9 +171,13 @@ 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_meta: {
|
||||
[tmp.extra.spec]: { scope: "local", source: path.join(tmp.path, "tui.json") },
|
||||
},
|
||||
plugin_records: [
|
||||
{
|
||||
item: tmp.extra.spec,
|
||||
scope: "local",
|
||||
source: path.join(tmp.path, "tui.json"),
|
||||
},
|
||||
],
|
||||
})
|
||||
const wait = spyOn(TuiConfig, "waitForDependencies").mockResolvedValue()
|
||||
const cwd = spyOn(process, "cwd").mockImplementation(() => tmp.path)
|
||||
@@ -166,9 +234,13 @@ 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_meta: {
|
||||
[tmp.extra.spec]: { scope: "local", source: path.join(tmp.path, "tui.json") },
|
||||
},
|
||||
plugin_records: [
|
||||
{
|
||||
item: tmp.extra.spec,
|
||||
scope: "local",
|
||||
source: path.join(tmp.path, "tui.json"),
|
||||
},
|
||||
],
|
||||
})
|
||||
const wait = spyOn(TuiConfig, "waitForDependencies").mockResolvedValue()
|
||||
const cwd = spyOn(process, "cwd").mockImplementation(() => tmp.path)
|
||||
@@ -187,3 +259,228 @@ test("rejects npm tui plugin that exports server and tui together", async () =>
|
||||
delete process.env.OPENCODE_PLUGIN_META_FILE
|
||||
}
|
||||
})
|
||||
|
||||
test("does not use npm package main for tui entry", async () => {
|
||||
await using tmp = await tmpdir({
|
||||
init: async (dir) => {
|
||||
const mod = path.join(dir, "mods", "acme-plugin")
|
||||
const marker = path.join(dir, "main-called.txt")
|
||||
await fs.mkdir(mod, { recursive: true })
|
||||
|
||||
await Bun.write(
|
||||
path.join(mod, "package.json"),
|
||||
JSON.stringify({
|
||||
name: "acme-plugin",
|
||||
type: "module",
|
||||
main: "./index.js",
|
||||
}),
|
||||
)
|
||||
await Bun.write(
|
||||
path.join(mod, "index.js"),
|
||||
`export default {
|
||||
id: "demo.main",
|
||||
tui: async () => {
|
||||
await Bun.write(${JSON.stringify(marker)}, "called")
|
||||
},
|
||||
}
|
||||
`,
|
||||
)
|
||||
|
||||
return { mod, marker, spec: "acme-plugin@1.0.0" }
|
||||
},
|
||||
})
|
||||
|
||||
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: [
|
||||
{
|
||||
item: tmp.extra.spec,
|
||||
scope: "local",
|
||||
source: path.join(tmp.path, "tui.json"),
|
||||
},
|
||||
],
|
||||
})
|
||||
const wait = spyOn(TuiConfig, "waitForDependencies").mockResolvedValue()
|
||||
const cwd = spyOn(process, "cwd").mockImplementation(() => tmp.path)
|
||||
const install = spyOn(BunProc, "install").mockResolvedValue(tmp.extra.mod)
|
||||
|
||||
try {
|
||||
await TuiPluginRuntime.init(createTuiPluginApi())
|
||||
await expect(fs.readFile(tmp.extra.marker, "utf8")).rejects.toThrow()
|
||||
expect(TuiPluginRuntime.list().some((item) => item.spec === tmp.extra.spec)).toBe(false)
|
||||
} finally {
|
||||
await TuiPluginRuntime.dispose()
|
||||
install.mockRestore()
|
||||
cwd.mockRestore()
|
||||
get.mockRestore()
|
||||
wait.mockRestore()
|
||||
delete process.env.OPENCODE_PLUGIN_META_FILE
|
||||
}
|
||||
})
|
||||
|
||||
test("does not use directory package main for tui entry", async () => {
|
||||
await using tmp = await tmpdir({
|
||||
init: async (dir) => {
|
||||
const mod = path.join(dir, "mods", "dir-plugin")
|
||||
const spec = pathToFileURL(mod).href
|
||||
const marker = path.join(dir, "dir-main-called.txt")
|
||||
await fs.mkdir(mod, { recursive: true })
|
||||
|
||||
await Bun.write(
|
||||
path.join(mod, "package.json"),
|
||||
JSON.stringify({
|
||||
name: "dir-plugin",
|
||||
type: "module",
|
||||
main: "./main.js",
|
||||
}),
|
||||
)
|
||||
await Bun.write(
|
||||
path.join(mod, "main.js"),
|
||||
`export default {
|
||||
id: "demo.dir.main",
|
||||
tui: async () => {
|
||||
await Bun.write(${JSON.stringify(marker)}, "called")
|
||||
},
|
||||
}
|
||||
`,
|
||||
)
|
||||
|
||||
return { marker, spec }
|
||||
},
|
||||
})
|
||||
|
||||
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: [
|
||||
{
|
||||
item: tmp.extra.spec,
|
||||
scope: "local",
|
||||
source: path.join(tmp.path, "tui.json"),
|
||||
},
|
||||
],
|
||||
})
|
||||
const wait = spyOn(TuiConfig, "waitForDependencies").mockResolvedValue()
|
||||
const cwd = spyOn(process, "cwd").mockImplementation(() => tmp.path)
|
||||
|
||||
try {
|
||||
await TuiPluginRuntime.init(createTuiPluginApi())
|
||||
await expect(fs.readFile(tmp.extra.marker, "utf8")).rejects.toThrow()
|
||||
expect(TuiPluginRuntime.list().some((item) => item.spec === tmp.extra.spec)).toBe(false)
|
||||
} finally {
|
||||
await TuiPluginRuntime.dispose()
|
||||
cwd.mockRestore()
|
||||
get.mockRestore()
|
||||
wait.mockRestore()
|
||||
delete process.env.OPENCODE_PLUGIN_META_FILE
|
||||
}
|
||||
})
|
||||
|
||||
test("uses directory index fallback for tui when package.json is missing", async () => {
|
||||
await using tmp = await tmpdir({
|
||||
init: async (dir) => {
|
||||
const mod = path.join(dir, "mods", "dir-index")
|
||||
const spec = pathToFileURL(mod).href
|
||||
const marker = path.join(dir, "dir-index-called.txt")
|
||||
await fs.mkdir(mod, { recursive: true })
|
||||
await Bun.write(
|
||||
path.join(mod, "index.ts"),
|
||||
`export default {
|
||||
id: "demo.dir.index",
|
||||
tui: async () => {
|
||||
await Bun.write(${JSON.stringify(marker)}, "called")
|
||||
},
|
||||
}
|
||||
`,
|
||||
)
|
||||
return { marker, spec }
|
||||
},
|
||||
})
|
||||
|
||||
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: [
|
||||
{
|
||||
item: tmp.extra.spec,
|
||||
scope: "local",
|
||||
source: path.join(tmp.path, "tui.json"),
|
||||
},
|
||||
],
|
||||
})
|
||||
const wait = spyOn(TuiConfig, "waitForDependencies").mockResolvedValue()
|
||||
const cwd = spyOn(process, "cwd").mockImplementation(() => tmp.path)
|
||||
|
||||
try {
|
||||
await TuiPluginRuntime.init(createTuiPluginApi())
|
||||
await expect(fs.readFile(tmp.extra.marker, "utf8")).resolves.toBe("called")
|
||||
expect(TuiPluginRuntime.list().find((item) => item.id === "demo.dir.index")?.active).toBe(true)
|
||||
} finally {
|
||||
await TuiPluginRuntime.dispose()
|
||||
cwd.mockRestore()
|
||||
get.mockRestore()
|
||||
wait.mockRestore()
|
||||
delete process.env.OPENCODE_PLUGIN_META_FILE
|
||||
}
|
||||
})
|
||||
|
||||
test("uses npm package name when tui plugin id is omitted", async () => {
|
||||
await using tmp = await tmpdir({
|
||||
init: async (dir) => {
|
||||
const mod = path.join(dir, "mods", "acme-plugin")
|
||||
const marker = path.join(dir, "name-id-called.txt")
|
||||
await fs.mkdir(mod, { recursive: true })
|
||||
|
||||
await Bun.write(
|
||||
path.join(mod, "package.json"),
|
||||
JSON.stringify({
|
||||
name: "acme-plugin",
|
||||
type: "module",
|
||||
exports: { ".": "./index.js", "./tui": "./tui.js" },
|
||||
}),
|
||||
)
|
||||
await Bun.write(path.join(mod, "index.js"), "export default {}\n")
|
||||
await Bun.write(
|
||||
path.join(mod, "tui.js"),
|
||||
`export default {
|
||||
tui: async (_api, options) => {
|
||||
if (!options?.marker) return
|
||||
await Bun.write(options.marker, "called")
|
||||
},
|
||||
}
|
||||
`,
|
||||
)
|
||||
|
||||
return { mod, marker, spec: "acme-plugin@1.0.0" }
|
||||
},
|
||||
})
|
||||
|
||||
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: [
|
||||
{
|
||||
item: [tmp.extra.spec, { marker: tmp.extra.marker }],
|
||||
scope: "local",
|
||||
source: path.join(tmp.path, "tui.json"),
|
||||
},
|
||||
],
|
||||
})
|
||||
const wait = spyOn(TuiConfig, "waitForDependencies").mockResolvedValue()
|
||||
const cwd = spyOn(process, "cwd").mockImplementation(() => tmp.path)
|
||||
const install = spyOn(BunProc, "install").mockResolvedValue(tmp.extra.mod)
|
||||
|
||||
try {
|
||||
await TuiPluginRuntime.init(createTuiPluginApi())
|
||||
await expect(fs.readFile(tmp.extra.marker, "utf8")).resolves.toBe("called")
|
||||
expect(TuiPluginRuntime.list().find((item) => item.spec === tmp.extra.spec)?.id).toBe("acme-plugin")
|
||||
} finally {
|
||||
await TuiPluginRuntime.dispose()
|
||||
install.mockRestore()
|
||||
cwd.mockRestore()
|
||||
get.mockRestore()
|
||||
wait.mockRestore()
|
||||
delete process.env.OPENCODE_PLUGIN_META_FILE
|
||||
}
|
||||
})
|
||||
|
||||
@@ -39,12 +39,13 @@ test("skips external tui plugins in pure mode", async () => {
|
||||
|
||||
const get = spyOn(TuiConfig, "get").mockResolvedValue({
|
||||
plugin: [[tmp.extra.spec, { marker: tmp.extra.marker }]],
|
||||
plugin_meta: {
|
||||
[tmp.extra.spec]: {
|
||||
plugin_records: [
|
||||
{
|
||||
item: [tmp.extra.spec, { marker: tmp.extra.marker }],
|
||||
scope: "local",
|
||||
source: path.join(tmp.path, "tui.json"),
|
||||
},
|
||||
},
|
||||
],
|
||||
})
|
||||
const wait = spyOn(TuiConfig, "waitForDependencies").mockResolvedValue()
|
||||
const cwd = spyOn(process, "cwd").mockImplementation(() => tmp.path)
|
||||
|
||||
@@ -468,10 +468,18 @@ test("continues loading when a plugin is missing config metadata", async () => {
|
||||
[tmp.extra.goodSpec, { marker: tmp.extra.goodMarker }],
|
||||
tmp.extra.bareSpec,
|
||||
],
|
||||
plugin_meta: {
|
||||
[tmp.extra.goodSpec]: { scope: "local", source: path.join(tmp.path, "tui.json") },
|
||||
[tmp.extra.bareSpec]: { scope: "local", source: path.join(tmp.path, "tui.json") },
|
||||
},
|
||||
plugin_records: [
|
||||
{
|
||||
item: [tmp.extra.goodSpec, { marker: tmp.extra.goodMarker }],
|
||||
scope: "local",
|
||||
source: path.join(tmp.path, "tui.json"),
|
||||
},
|
||||
{
|
||||
item: tmp.extra.bareSpec,
|
||||
scope: "local",
|
||||
source: path.join(tmp.path, "tui.json"),
|
||||
},
|
||||
],
|
||||
})
|
||||
const wait = spyOn(TuiConfig, "waitForDependencies").mockResolvedValue()
|
||||
const cwd = spyOn(process, "cwd").mockImplementation(() => tmp.path)
|
||||
@@ -493,6 +501,84 @@ test("continues loading when a plugin is missing config metadata", async () => {
|
||||
}
|
||||
})
|
||||
|
||||
test("initializes external tui plugins in config order", async () => {
|
||||
const globalJson = path.join(Global.Path.config, "tui.json")
|
||||
const globalJsonc = path.join(Global.Path.config, "tui.jsonc")
|
||||
const backupJson = await Bun.file(globalJson)
|
||||
.text()
|
||||
.catch(() => undefined)
|
||||
const backupJsonc = await Bun.file(globalJsonc)
|
||||
.text()
|
||||
.catch(() => undefined)
|
||||
|
||||
await fs.rm(globalJson, { force: true }).catch(() => {})
|
||||
await fs.rm(globalJsonc, { force: true }).catch(() => {})
|
||||
|
||||
await using tmp = await tmpdir({
|
||||
init: async (dir) => {
|
||||
const a = path.join(dir, "order-a.ts")
|
||||
const b = path.join(dir, "order-b.ts")
|
||||
const aSpec = pathToFileURL(a).href
|
||||
const bSpec = pathToFileURL(b).href
|
||||
const marker = path.join(dir, "tui-order.txt")
|
||||
|
||||
await Bun.write(
|
||||
a,
|
||||
`import fs from "fs/promises"
|
||||
|
||||
export default {
|
||||
id: "demo.tui.order.a",
|
||||
tui: async () => {
|
||||
await fs.appendFile(${JSON.stringify(marker)}, "a-start\\n")
|
||||
await Bun.sleep(25)
|
||||
await fs.appendFile(${JSON.stringify(marker)}, "a-end\\n")
|
||||
},
|
||||
}
|
||||
`,
|
||||
)
|
||||
await Bun.write(
|
||||
b,
|
||||
`import fs from "fs/promises"
|
||||
|
||||
export default {
|
||||
id: "demo.tui.order.b",
|
||||
tui: async () => {
|
||||
await fs.appendFile(${JSON.stringify(marker)}, "b\\n")
|
||||
},
|
||||
}
|
||||
`,
|
||||
)
|
||||
await Bun.write(path.join(dir, "tui.json"), JSON.stringify({ plugin: [aSpec, bSpec] }, null, 2))
|
||||
|
||||
return { marker }
|
||||
},
|
||||
})
|
||||
|
||||
process.env.OPENCODE_PLUGIN_META_FILE = path.join(tmp.path, "plugin-meta.json")
|
||||
const cwd = spyOn(process, "cwd").mockImplementation(() => tmp.path)
|
||||
|
||||
try {
|
||||
await TuiPluginRuntime.init(createTuiPluginApi())
|
||||
const lines = (await fs.readFile(tmp.extra.marker, "utf8")).trim().split("\n")
|
||||
expect(lines).toEqual(["a-start", "a-end", "b"])
|
||||
} finally {
|
||||
await TuiPluginRuntime.dispose()
|
||||
cwd.mockRestore()
|
||||
delete process.env.OPENCODE_PLUGIN_META_FILE
|
||||
|
||||
if (backupJson === undefined) {
|
||||
await fs.rm(globalJson, { force: true }).catch(() => {})
|
||||
} else {
|
||||
await Bun.write(globalJson, backupJson)
|
||||
}
|
||||
if (backupJsonc === undefined) {
|
||||
await fs.rm(globalJsonc, { force: true }).catch(() => {})
|
||||
} else {
|
||||
await Bun.write(globalJsonc, backupJsonc)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
describe("tui.plugin.loader", () => {
|
||||
let data: Data
|
||||
|
||||
|
||||
@@ -44,12 +44,13 @@ test("toggles plugin runtime state by exported id", async () => {
|
||||
plugin_enabled: {
|
||||
"demo.toggle": false,
|
||||
},
|
||||
plugin_meta: {
|
||||
[tmp.extra.spec]: {
|
||||
plugin_records: [
|
||||
{
|
||||
item: [tmp.extra.spec, { marker: tmp.extra.marker }],
|
||||
scope: "local",
|
||||
source: path.join(tmp.path, "tui.json"),
|
||||
},
|
||||
},
|
||||
],
|
||||
})
|
||||
const wait = spyOn(TuiConfig, "waitForDependencies").mockResolvedValue()
|
||||
const cwd = spyOn(process, "cwd").mockImplementation(() => tmp.path)
|
||||
@@ -121,12 +122,13 @@ test("kv plugin_enabled overrides tui config on startup", async () => {
|
||||
plugin_enabled: {
|
||||
"demo.startup": false,
|
||||
},
|
||||
plugin_meta: {
|
||||
[tmp.extra.spec]: {
|
||||
plugin_records: [
|
||||
{
|
||||
item: [tmp.extra.spec, { marker: tmp.extra.marker }],
|
||||
scope: "local",
|
||||
source: path.join(tmp.path, "tui.json"),
|
||||
},
|
||||
},
|
||||
],
|
||||
})
|
||||
const wait = spyOn(TuiConfig, "waitForDependencies").mockResolvedValue()
|
||||
const cwd = spyOn(process, "cwd").mockImplementation(() => tmp.path)
|
||||
|
||||
Reference in New Issue
Block a user