alloc / tusken

100% type-safe query builder compatible with any Postgres client 🐘 Generated table/function types, tree-shakable, implicit type casts, and more
Other
223 stars 3 forks source link

Plugin packages #11

Open aleclarson opened 2 years ago

aleclarson commented 2 years ago
aleclarson commented 2 years ago

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.

aleclarson commented 2 years ago

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'] },