Plugin packages
aleclarson opened this issue · 2 comments
- Plugins found in
node_modules
are loaded automatically- Their name must match a pattern like Babel uses (https://babeljs.io/docs/en/options#name-normalization) but imagine those patterns with
tusken
instead ofbabel
- Their name must match a pattern like Babel uses (https://babeljs.io/docs/en/options#name-normalization) but imagine those patterns with
- Plugins can alter the generated client, which means they can:
- Inject runtime code
- Modify the
db
,pg
, andt
objects - Modify any TypeScript interface with declaration merging
- Plugins can add their own config options to
tusken.config.js
- Plugins can alter the database schema with explicit permission
tusken apply
brings up a multi-select prompt of unapplied plugins?
- Plugins can add their own database schema for metadata purposes
Database extensions
Any module in a plugin package's dist/database
folder is read during tusken generate
command. These modules are bundled and seamlessly plugged into the generated client. Private instance properties and global variables are renamed to avoid collisions between plugins.
import { Database, DatabaseConfig } from 'tusken'
const DEFAULT_FOO = 1
export default class extends Database {
// Instance properties
private _foo: number
// Read and manipulate the config and/or initialize properties
constructor(config: DatabaseConfig & { foo?: number }) {
super(config)
this._foo = config.foo ?? DEFAULT_FOO
}
// Define new methods
foo() {...}
}
Similar runtime extensions will be supported as well. Like dist/select
modules can extend the Select
class. We might even allow plugin packages to provide their own extension namespaces, so dist/<plugin>
could be supported too.
→ Compiled extension
When the runtime extension above is compiled by tusken generate
, it's injected into the generated client as roughly the following code:
import * as tusken from 'tusken'
export interface DatabaseConfig extends tusken.DatabaseConfig {
foo?: number
}
const DEFAULT_FOO = 1
class Database extends tusken.Database {
private _foo: number
constructor(config: DatabaseConfig) {
super(config)
this._foo = config.foo ?? DEFAULT_FOO
}
foo() {...}
}
export default new Database({
/* generated options go here */
})
The generated Database
subclass is shared by all extension modules. As said before, any conflicting private instance properties and global variables are renamed.
Partial plugins
In your Tusken config, you can specify which extensions of a plugin you wish to use:
export default defineConfig({
include: [
// The default if a plugin isn't specified, but required if the package name
// doesn't include "tusken-plugin-" or similar
'tusken-plugin-admin/*',
// Use only the "map" extension from the tusken-plugin-array package.
'tusken-plugin-array/map',
})
The include
array will have its types generated so it can warn you about typos and give you auto-completion.
Probably also want support for shorthand objects:
{ 'tusken-plugin-array': ['map', 'forEach'] },
And we should probably just omit the tusken-plugin
part?
'array/*',
'array/map',
{ array: ['map', 'filter'] },