diff --git a/src/node/plugin.ts b/src/node/plugin.ts index 061523a0..8d5e552b 100644 --- a/src/node/plugin.ts +++ b/src/node/plugin.ts @@ -10,11 +10,11 @@ const fsp = fs.promises interface Plugin extends pluginapi.Plugin { /** - * These fields are populated from the plugin's package.json. + * These fields are populated from the plugin's package.json + * and now guaranteed to exist. */ name: string version: string - description: string /** * path to the node module on the disk. @@ -34,7 +34,7 @@ interface Application extends pluginapi.Application { * Please see that file for details. */ export class PluginAPI { - private readonly plugins = new Array() + private readonly plugins = new Map() private readonly logger: Logger public constructor( @@ -54,7 +54,7 @@ export class PluginAPI { */ public async applications(): Promise { const apps = new Array() - for (const p of this.plugins) { + for (const [_, p] of this.plugins) { const pluginApps = await p.applications() // Add plugin key to each app. @@ -65,8 +65,11 @@ export class PluginAPI { plugin: { name: p.name, version: p.version, - description: p.description, modulePath: p.modulePath, + + displayName: p.displayName, + description: p.description, + path: p.path, }, } }), @@ -79,7 +82,7 @@ export class PluginAPI { * mount mounts all plugin routers onto r. */ public mount(r: express.Router): void { - for (const p of this.plugins) { + for (const [_, p] of this.plugins) { r.use(`/${p.name}`, p.router()) } } @@ -129,7 +132,7 @@ export class PluginAPI { encoding: "utf8", }) const packageJSON: PackageJSON = JSON.parse(str) - for (const p of this.plugins) { + for (const [_, p] of this.plugins) { if (p.name === packageJSON.name) { this.logger.warn( `ignoring duplicate plugin ${q(p.name)} at ${q(dir)}, using previously loaded ${q(p.modulePath)}`, @@ -138,7 +141,7 @@ export class PluginAPI { } } const p = this._loadPlugin(dir, packageJSON) - this.plugins.push(p) + this.plugins.set(p.name, p) } catch (err) { if (err.code !== "ENOENT") { this.logger.warn(`failed to load plugin: ${err.message}`) @@ -147,6 +150,8 @@ export class PluginAPI { } private _loadPlugin(dir: string, packageJSON: PackageJSON): Plugin { + dir = path.resolve(dir) + const logger = this.logger.named(packageJSON.name) logger.debug("loading plugin", field("plugin_dir", dir), field("package_json", packageJSON)) @@ -165,11 +170,20 @@ export class PluginAPI { const p = { name: packageJSON.name, version: packageJSON.version, - description: packageJSON.description, modulePath: dir, ...require(dir), } as Plugin + if (!p.displayName) { + throw new Error("plugin missing displayName") + } + if (!p.description) { + throw new Error("plugin missing description") + } + if (!p.path) { + throw new Error("plugin missing path") + } + p.init({ logger: logger, }) @@ -183,7 +197,6 @@ export class PluginAPI { interface PackageJSON { name: string version: string - description: string engines: { "code-server": string } diff --git a/test/plugin.test.ts b/test/plugin.test.ts index 5836dead..1e63fa8d 100644 --- a/test/plugin.test.ts +++ b/test/plugin.test.ts @@ -17,14 +17,20 @@ describe("plugin", () => { assert.deepEqual( [ { - name: "goland", + name: "test app", version: "4.0.0", + + description: "my description", iconPath: "/icon.svg", + plugin: { name: "test-plugin", version: "1.0.0", - description: "Fake plugin for testing code-server's plugin API", modulePath: path.join(__dirname, "test-plugin"), + + description: "Plugin used in code-server tests.", + displayName: "Test Plugin", + path: "/test-plugin", }, }, ], diff --git a/test/test-plugin/package.json b/test/test-plugin/package.json index ccdeabb5..c1f2e698 100644 --- a/test/test-plugin/package.json +++ b/test/test-plugin/package.json @@ -2,7 +2,6 @@ "private": true, "name": "test-plugin", "version": "1.0.0", - "description": "Fake plugin for testing code-server's plugin API", "engines": { "code-server": "^3.6.0" }, diff --git a/test/test-plugin/src/index.ts b/test/test-plugin/src/index.ts index bc37d7c0..6435592b 100644 --- a/test/test-plugin/src/index.ts +++ b/test/test-plugin/src/index.ts @@ -1,7 +1,11 @@ import * as express from "express" -import * as path from "path" +import * as fspath from "path" import * as pluginapi from "../../../typings/pluginapi" +export const displayName = "Test Plugin" +export const path = "/test-plugin" +export const description = "Plugin used in code-server tests." + export function init(config: pluginapi.PluginConfig) { config.logger.debug("test-plugin loaded!") } @@ -9,7 +13,7 @@ export function init(config: pluginapi.PluginConfig) { export function router(): express.Router { const r = express.Router() r.get("/goland/icon.svg", (req, res) => { - res.sendFile(path.resolve(__dirname, "../public/icon.svg")) + res.sendFile(fspath.resolve(__dirname, "../public/icon.svg")) }) return r } @@ -17,9 +21,11 @@ export function router(): express.Router { export function applications(): pluginapi.Application[] { return [ { - name: "goland", + name: "test app", version: "4.0.0", iconPath: "/icon.svg", + + description: "my description", }, ] } diff --git a/tsconfig.json b/tsconfig.json index ac3a1df5..0db0b190 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -16,7 +16,8 @@ "tsBuildInfoFile": "./.cache/tsbuildinfo", "incremental": true, "rootDir": "./src", - "typeRoots": ["./node_modules/@types", "./typings"] + "typeRoots": ["./node_modules/@types", "./typings"], + "downlevelIteration": true }, "include": ["./src/**/*.ts"] } diff --git a/typings/pluginapi.d.ts b/typings/pluginapi.d.ts index bc8d2ef5..7b61c4ce 100644 --- a/typings/pluginapi.d.ts +++ b/typings/pluginapi.d.ts @@ -70,9 +70,14 @@ export interface Plugin { version?: string /** - * These two are used in the overlay. + * Name used in the overlay. */ displayName: string + + /** + * Used in overlay. + * Should be a full sentence describing the plugin. + */ description: string /**