diff --git a/.changeset/tidy-mice-tell.md b/.changeset/tidy-mice-tell.md new file mode 100644 index 0000000..dba9bd1 --- /dev/null +++ b/.changeset/tidy-mice-tell.md @@ -0,0 +1,7 @@ +--- +"@kopflos-cms/express": patch +"@kopflos-cms/vite": patch +"kopflos": patch +--- + +Plugins are now implemented as classes diff --git a/packages/cli/lib/command/build.ts b/packages/cli/lib/command/build.ts index 5c4a2f6..04a9196 100644 --- a/packages/cli/lib/command/build.ts +++ b/packages/cli/lib/command/build.ts @@ -13,7 +13,7 @@ export default async function (args: BuildArgs) { const plugins = await loadPlugins(config.plugins) log.info('Running build actions...') - const buildActions = plugins.map(plugin => plugin.build?.()) + const buildActions = plugins.map(Plugin => Plugin.build?.()) if (buildActions.length === 0) { return log.warn('No plugins with build actions found') } else { diff --git a/packages/core/lib/Kopflos.ts b/packages/core/lib/Kopflos.ts index b5e10f1..a90d6c7 100644 --- a/packages/core/lib/Kopflos.ts +++ b/packages/core/lib/Kopflos.ts @@ -61,8 +61,7 @@ export interface PluginConfig { } export interface KopflosPlugin { - readonly name: string - build?: () => Promise | void + readonly name?: string onStart?(): Promise | void onStop?(): Promise | void apiTriples?(): Promise | DatasetCore | Stream @@ -82,6 +81,7 @@ export interface Kopflos { export interface KopflosPluginConstructor { new(instance: Kopflos): KopflosPlugin + build?: () => Promise | void } interface Clients { diff --git a/packages/core/plugin/shorthandTerms.ts b/packages/core/plugin/shorthandTerms.ts index 1b5dbd0..7b87d10 100644 --- a/packages/core/plugin/shorthandTerms.ts +++ b/packages/core/plugin/shorthandTerms.ts @@ -1,11 +1,14 @@ import type { Stream } from '@rdfjs/types' -import type { KopflosPlugin } from '../lib/Kopflos.js' +import type { Kopflos, KopflosPlugin, KopflosPluginConstructor } from '../lib/Kopflos.js' -export default function (): KopflosPlugin { - return { - apiTriples(kopflos): Stream { - const { env } = kopflos +export default function (): KopflosPluginConstructor { + return class implements KopflosPlugin { + constructor(private readonly kopflos: Kopflos) { + } + + apiTriples(): Stream { + const { env } = this.kopflos return env.fromFile(new URL('../graphs/shorthands.ttl', import.meta.url)) - }, + } } } diff --git a/packages/core/plugins.ts b/packages/core/plugins.ts index d9e3d65..a7e18bb 100644 --- a/packages/core/plugins.ts +++ b/packages/core/plugins.ts @@ -1,7 +1,7 @@ import log from './lib/log.js' -import type { KopflosConfig, KopflosPlugin } from './lib/Kopflos.js' +import type { KopflosConfig, KopflosPluginConstructor } from './lib/Kopflos.js' -export async function loadPlugins(plugins: KopflosConfig['plugins']): Promise { +export async function loadPlugins(plugins: KopflosConfig['plugins']): Promise { const pluginsCombined = Object.entries({ '@kopflos-cms/core/plugin/shorthandTerms.js': {}, ...plugins, diff --git a/packages/core/test/lib/Kopflos.test.ts b/packages/core/test/lib/Kopflos.test.ts index 4a85d2e..c5f0dbd 100644 --- a/packages/core/test/lib/Kopflos.test.ts +++ b/packages/core/test/lib/Kopflos.test.ts @@ -620,8 +620,9 @@ describe('lib/Kopflos', () => { it('calls onStart on plugins once', async function () { // given - const plugin = { - onStart: sinon.spy(), + const onStart = sinon.spy() + const plugin = class { + onStart = onStart } const instance = new Kopflos({ ...config, @@ -636,15 +637,16 @@ describe('lib/Kopflos', () => { await instance.start() // then - expect(plugin.onStart).to.have.been.calledOnce + expect(onStart).to.have.been.calledOnce }) }) describe('stop', () => { it('calls onStop on plugins', async function () { // given - const plugin = { - onStop: sinon.spy(), + const onStop = sinon.spy() + const plugin = class { + onStop = onStop } const instance = new Kopflos({ ...config, @@ -659,12 +661,12 @@ describe('lib/Kopflos', () => { await instance.stop() // then - expect(plugin.onStop).to.have.been.called + expect(onStop).to.have.been.called }) it('ignores plugins without onStop', async function () { // given - const plugin = {} + const plugin = class {} const instance = new Kopflos({ ...config, sparql: { diff --git a/packages/express/plugin/middleware.ts b/packages/express/plugin/middleware.ts index 3fa1718..09756c1 100644 --- a/packages/express/plugin/middleware.ts +++ b/packages/express/plugin/middleware.ts @@ -1,4 +1,4 @@ -import type { KopflosPlugin } from '@kopflos-cms/core' +import type { KopflosPlugin, KopflosPluginConstructor } from '@kopflos-cms/core' import type { Router } from 'express' import { createLogger } from '@kopflos-cms/logger' @@ -17,9 +17,14 @@ declare module '@kopflos-cms/core' { } } -export default function ({ before = [], after = [] }: Options): KopflosPlugin { - function use(middlewares: Array) { - return async function (host: Router) { +export default function ({ before = [], after = [] }: Options): KopflosPluginConstructor { + return class implements KopflosPlugin { + readonly name = '@kopflos-cms/express/middleware' + + declare beforeMiddleware: (host: Router) => Promise + declare afterMiddleware: (host: Router) => Promise + + private async use(middlewares: Array, host: Router) { const promises = middlewares.map(async middleware => { let module: string let options: unknown | undefined @@ -41,10 +46,10 @@ export default function ({ before = [], after = [] }: Options): KopflosPlugin { current = promises.splice(0, 1) } } - } - return { - beforeMiddleware: use(before), - afterMiddleware: use(after), + constructor() { + this.beforeMiddleware = this.use.bind(null, before) + this.afterMiddleware = this.use.bind(null, after) + } } } diff --git a/packages/vite/index.ts b/packages/vite/index.ts index 68b2803..a606407 100644 --- a/packages/vite/index.ts +++ b/packages/vite/index.ts @@ -1,5 +1,5 @@ import { resolve } from 'node:path' -import type { Kopflos, KopflosPlugin } from '@kopflos-cms/core' +import type { Kopflos, KopflosEnvironment, KopflosPlugin, KopflosPluginConstructor } from '@kopflos-cms/core' import express from 'express' import { build } from 'vite' import { createViteServer } from './lib/server.js' @@ -21,20 +21,29 @@ declare module '@kopflos-cms/core' { } } -export default function ({ outDir = 'dist', ...options }: Options): KopflosPlugin { +export default function ({ outDir = 'dist', ...options }: Options): KopflosPluginConstructor { const rootDir = resolve(process.cwd(), options.root || '') const buildDir = resolve(process.cwd(), outDir) - return { - onStart({ env }: Kopflos): Promise | void { + return class implements KopflosPlugin { + readonly name = '@kopflos-cms/vite' + + private env: KopflosEnvironment + + constructor(instance: Kopflos) { + this.env = instance.env + } + + onStart(): Promise | void { const viteVars = { - basePath: env.kopflos.config.mode === 'development' ? rootDir : buildDir, + basePath: this.env.kopflos.config.mode === 'development' ? rootDir : buildDir, } log.info('Variables', viteVars) - env.kopflos.variables.VITE = Object.freeze(viteVars) - }, - async beforeMiddleware(host: express.Router, { env }) { - if (env.kopflos.config.mode === 'development') { + this.env.kopflos.variables.VITE = Object.freeze(viteVars) + } + + async beforeMiddleware(host: express.Router) { + if (this.env.kopflos.config.mode === 'development') { log.info('Development UI mode. Creating Vite server...') const viteServer = await createViteServer(options) host.use(viteServer.middlewares) @@ -43,10 +52,11 @@ export default function ({ outDir = 'dist', ...options }: Options): KopflosPlugi log.debug('Build directory:', buildDir) host.use(express.static(buildDir)) } - }, - async build() { + } + + static async build() { log.info('Building UI...') await build(await prepareConfig({ outDir, ...options })) - }, + } } }