feat(plugins): load activation scope from plugin manifest
Plugins can now declare activationScope ("app" | "project") in their
manifest; loader/runtime honor it to defer activation of project-scoped
plugins until a project is focused instead of activating everything at
app bootstrap. Bumps sdk/IdeaSDK to the commit that adds the field.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@ -349,6 +349,130 @@ describe("loadPlugins", () => {
|
||||
]);
|
||||
});
|
||||
|
||||
it("leaves project-scoped plugins pending without a focused project", async () => {
|
||||
const bundle = dataUrl(`
|
||||
export function activate(ctx) {
|
||||
globalThis.__pendingProjectPluginActivated = ctx.pluginId;
|
||||
}
|
||||
`);
|
||||
const noFocusedProjectGateways = {
|
||||
...gateways,
|
||||
focusedProject: {
|
||||
async getFocusedProject() {
|
||||
return null;
|
||||
},
|
||||
},
|
||||
} as PluginGatewaySet;
|
||||
|
||||
const { registry, failures, pending } = await loadPlugins(
|
||||
[
|
||||
entry({
|
||||
id: "dev.acme.project-only",
|
||||
displayName: "Project Only",
|
||||
activationScope: "project",
|
||||
bundleUrl: bundle,
|
||||
}),
|
||||
],
|
||||
noFocusedProjectGateways,
|
||||
);
|
||||
|
||||
expect(registry.list()).toEqual([]);
|
||||
expect(failures).toEqual([]);
|
||||
expect(pending).toEqual([
|
||||
{
|
||||
pluginId: "dev.acme.project-only",
|
||||
displayName: "Project Only",
|
||||
reason: "En attente d'un projet actif.",
|
||||
},
|
||||
]);
|
||||
expect((globalThis as Record<string, unknown>).__pendingProjectPluginActivated).toBeUndefined();
|
||||
});
|
||||
|
||||
it("activates project-scoped plugins when a project is already focused", async () => {
|
||||
const bundle = dataUrl(`
|
||||
export function activate(ctx) {
|
||||
globalThis.__focusedProjectPluginActivated = ctx.pluginId;
|
||||
}
|
||||
`);
|
||||
const focusedProjectGateways = {
|
||||
...gateways,
|
||||
focusedProject: {
|
||||
async getFocusedProject() {
|
||||
return { id: "p1", name: "Alpha", root: "/tmp/alpha" };
|
||||
},
|
||||
},
|
||||
} as PluginGatewaySet;
|
||||
|
||||
const { registry, failures, pending } = await loadPlugins(
|
||||
[
|
||||
entry({
|
||||
id: "dev.acme.project-focused",
|
||||
displayName: "Project Focused",
|
||||
activationScope: "project",
|
||||
bundleUrl: bundle,
|
||||
}),
|
||||
],
|
||||
focusedProjectGateways,
|
||||
);
|
||||
|
||||
expect(failures).toEqual([]);
|
||||
expect(pending).toEqual([]);
|
||||
expect(registry.list().map((p) => p.pluginId)).toEqual(["dev.acme.project-focused"]);
|
||||
expect((globalThis as Record<string, unknown>).__focusedProjectPluginActivated).toBe(
|
||||
"dev.acme.project-focused",
|
||||
);
|
||||
});
|
||||
|
||||
it("treats omitted activationScope as app and isolates a project-required activation failure", async () => {
|
||||
const projectDependentBundle = dataUrl(`
|
||||
export async function activate(ctx) {
|
||||
await ctx.services.workspace.getProjectRoot();
|
||||
}
|
||||
`);
|
||||
const healthyBundle = dataUrl(`
|
||||
export function activate(ctx) {
|
||||
globalThis.__healthyAppPluginActivated = ctx.pluginId;
|
||||
}
|
||||
`);
|
||||
const noFocusedProjectGateways = {
|
||||
...gateways,
|
||||
focusedProject: {
|
||||
async getFocusedProject() {
|
||||
return null;
|
||||
},
|
||||
},
|
||||
} as PluginGatewaySet;
|
||||
|
||||
const { registry, failures, pending } = await loadPlugins(
|
||||
[
|
||||
entry({
|
||||
id: "dev.acme.default-app-scope",
|
||||
displayName: "Default App Scope",
|
||||
capabilities: ["tooling"],
|
||||
bundleUrl: projectDependentBundle,
|
||||
}),
|
||||
entry({
|
||||
id: "dev.acme.healthy-app",
|
||||
displayName: "Healthy App",
|
||||
bundleUrl: healthyBundle,
|
||||
}),
|
||||
],
|
||||
noFocusedProjectGateways,
|
||||
);
|
||||
|
||||
expect(registry.list().map((p) => p.pluginId)).toEqual(["dev.acme.healthy-app"]);
|
||||
expect(failures).toEqual([
|
||||
{
|
||||
pluginId: "dev.acme.default-app-scope",
|
||||
reason: "no current project is focused",
|
||||
},
|
||||
]);
|
||||
expect(pending).toEqual([]);
|
||||
expect((globalThis as Record<string, unknown>).__healthyAppPluginActivated).toBe(
|
||||
"dev.acme.healthy-app",
|
||||
);
|
||||
});
|
||||
|
||||
it("loads the hello-plugin command and layout contribution shape", async () => {
|
||||
const bundle = dataUrl(`
|
||||
export function activate(ctx) {
|
||||
|
||||
Reference in New Issue
Block a user