Skip to content

Commit

Permalink
feat(core): enhance typings, fix variance issues
Browse files Browse the repository at this point in the history
  • Loading branch information
shigma committed Feb 5, 2024
1 parent e34e9cf commit c1ac7b0
Show file tree
Hide file tree
Showing 5 changed files with 50 additions and 34 deletions.
8 changes: 6 additions & 2 deletions packages/cordis/src/index.ts
@@ -1,6 +1,6 @@
import * as core from '@cordisjs/core'
import * as logger from '@cordisjs/logger'
import timer from '@cordisjs/timer'
import { TimerService } from '@cordisjs/timer'

export * from '@cordisjs/core'

Expand All @@ -25,6 +25,10 @@ export namespace Context {
export type Associate<P extends string, C extends Context = Context> = core.Context.Associate<P, C>
}

export interface Context {
[Context.events]: Events<this>
}

export class Context extends core.Context {
baseDir: string

Expand All @@ -36,7 +40,7 @@ export class Context extends core.Context {
this.provide('timer', undefined, true)

this.plugin(logger)
this.plugin(timer)
this.plugin(TimerService)
}
}

Expand Down
2 changes: 1 addition & 1 deletion packages/cordis/src/worker/logger.ts
@@ -1,5 +1,5 @@
import { Logger } from '@cordisjs/logger'
import { Context } from '@cordisjs/core'
import { Context } from '../index.ts'

declare module '@cordisjs/loader' {
interface Loader {
Expand Down
3 changes: 1 addition & 2 deletions packages/core/src/events.ts
@@ -1,7 +1,6 @@
import { Awaitable, defineProperty, Promisify, remove } from 'cosmokit'
import { Context } from './context.ts'
import { EffectScope, ForkScope, MainScope, ScopeStatus } from './scope.ts'
import { Plugin } from './registry.ts'

export function isBailed(value: any) {
return value !== null && value !== false && value !== undefined
Expand Down Expand Up @@ -175,7 +174,7 @@ export class Lifecycle {
}

export interface Events<in C extends Context = Context> {
'fork': Plugin.Function<C, C['config']>
'fork'(ctx: C, config: C['config']): void
'ready'(): Awaitable<void>
'dispose'(): Awaitable<void>
'internal/fork'(fork: ForkScope<C>): void
Expand Down
57 changes: 32 additions & 25 deletions packages/core/src/registry.ts
Expand Up @@ -18,48 +18,53 @@ export type Plugin<C extends Context = Context, T = any> =
| Plugin.Object<C, T>

export namespace Plugin {
export interface Base {
export interface Base<T = any> {
name?: string
reactive?: boolean
reusable?: boolean
Config?: (config?: any) => any
Config?: (config: any) => T
inject?: string[] | Inject
/** @deprecated use `inject` instead */
using?: string[] | Inject
}

export interface Function<C extends Context = Context, T = any> extends Base {
(ctx: C, options: T): void
export interface Transform<S, T> {
schema?: true
Config: (config: S) => T
}

export interface Constructor<C extends Context = Context, T = any> extends Base {
new (ctx: C, options: T): void
export interface Function<C extends Context = Context, T = any> extends Base<T> {
(ctx: C, config: T): void
}

export interface Object<C extends Context = Context, T = any> extends Base {
apply: (ctx: C, options: T) => void
export interface Constructor<C extends Context = Context, T = any> extends Base<T> {
new (ctx: C, config: T): void
}

export interface Object<C extends Context = Context, T = any> extends Base<T> {
apply: (ctx: C, config: T) => void
}
}

export type Spread<T> = undefined extends T ? [config?: T] : [config: T]

declare module './context.ts' {
export interface Context {
/* eslint-disable max-len */
/** @deprecated use `ctx.inject()` instead */
using(deps: string[] | Inject, callback: Plugin.Function<Context.Parameterized<this, void>, void>): ForkScope<Context.Parameterized<this, void>>
inject(deps: string[] | Inject, callback: Plugin.Function<Context.Parameterized<this, void>, void>): ForkScope<Context.Parameterized<this, void>>
plugin<T, S = T>(plugin: Plugin.Function<Context.Parameterized<this, T>, T> & { schema?: true; Config: (config?: S) => T }, config?: S): ForkScope<Context.Parameterized<this, T>>
plugin<T, S = T>(plugin: Plugin.Constructor<Context.Parameterized<this, T>, T> & { schema?: true; Config: (config?: S) => T }, config?: S): ForkScope<Context.Parameterized<this, T>>
plugin<T, S = T>(plugin: Plugin.Object<Context.Parameterized<this, T>, T> & { schema?: true; Config: (config?: S) => T }, config?: S): ForkScope<Context.Parameterized<this, T>>
plugin<T>(plugin: Plugin.Function<Context.Parameterized<this, T>, T>, config?: T): ForkScope<Context.Parameterized<this, T>>
plugin<T>(plugin: Plugin.Constructor<Context.Parameterized<this, T>, T>, config?: T): ForkScope<Context.Parameterized<this, T>>
plugin<T>(plugin: Plugin.Object<Context.Parameterized<this, T>, T>, config?: T): ForkScope<Context.Parameterized<this, T>>
/* eslint-enable max-len */
using(deps: string[] | Inject, callback: Plugin.Function<this, void>): ForkScope<this>
inject(deps: string[] | Inject, callback: Plugin.Function<this, void>): ForkScope<this>
plugin<T = undefined, S = T>(plugin: Plugin.Constructor<this, T> & Plugin.Transform<S, T>, ...args: Spread<S>): ForkScope<this>
plugin<T = undefined, S = T>(plugin: Plugin.Function<this, T> & Plugin.Transform<S, T>, ...args: Spread<S>): ForkScope<this>
plugin<T = undefined, S = T>(plugin: Plugin.Object<this, T> & Plugin.Transform<S, T>, ...args: Spread<S>): ForkScope<this>
plugin<T = undefined>(plugin: Plugin.Constructor<this, T>, ...args: Spread<T>): ForkScope<this>
plugin<T = undefined>(plugin: Plugin.Function<this, T>, ...args: Spread<T>): ForkScope<this>
plugin<T = undefined>(plugin: Plugin.Object<this, T>, ...args: Spread<T>): ForkScope<this>
}
}

export class Registry<C extends Context = Context> {
private _counter = 0
private _internal = new Map<Plugin<C>, MainScope<C>>()
private _internal = new Map<Plugin, MainScope<C>>()

constructor(private root: Context, config: any) {
defineProperty(this, Context.current, root)
Expand All @@ -83,19 +88,21 @@ export class Registry<C extends Context = Context> {
throw new Error('invalid plugin, expect function or object with an "apply" method, received ' + typeof plugin)
}

get(plugin: Plugin<C>) {
get(plugin: Plugin) {
return this._internal.get(this.resolve(plugin))
}

has(plugin: Plugin<C>) {
has(plugin: Plugin) {
return this._internal.has(this.resolve(plugin))
}

set(plugin: Plugin<C>, state: MainScope<C>) {
return this._internal.set(this.resolve(plugin), state)
set(plugin: Plugin, state: MainScope<C>) {
const oldValue = this._internal.get(this.resolve(plugin))
this._internal.set(this.resolve(plugin), state)
return oldValue
}

delete(plugin: Plugin<C>) {
delete(plugin: Plugin) {
plugin = this.resolve(plugin)
const runtime = this.get(plugin)
if (!runtime) return
Expand All @@ -116,7 +123,7 @@ export class Registry<C extends Context = Context> {
return this._internal.entries()
}

forEach(callback: (value: MainScope<C>, key: Plugin<C>, map: Map<Plugin<C>, MainScope<C>>) => void) {
forEach(callback: (value: MainScope<C>, key: Plugin, map: Map<Plugin, MainScope<C>>) => void) {
return this._internal.forEach(callback)
}

Expand Down
14 changes: 10 additions & 4 deletions packages/core/src/scope.ts
Expand Up @@ -7,7 +7,8 @@ declare module './context.ts' {
export interface Context {
scope: EffectScope<this>
runtime: MainScope<this>
effect<T extends DisposableLike>(callback: () => T): T
effect<T extends DisposableLike>(callback: Callable<T, [ctx: this]>): T
effect<T extends DisposableLike, R>(callback: Callable<T, [ctx: this, config: R]>, config: R): T
/** @deprecated use `ctx.effect()` instead */
collect(label: string, callback: () => void): () => void
accept(callback?: (config: this['config']) => void | boolean, options?: AcceptOptions): () => boolean
Expand All @@ -20,6 +21,8 @@ export type Disposable = () => void

export type DisposableLike = Disposable | { dispose: Disposable }

export type Callable<T, R extends unknown[]> = ((...args: R) => T) | (new (...args: R) => T)

export interface AcceptOptions {
passive?: boolean
immediate?: boolean
Expand Down Expand Up @@ -88,9 +91,12 @@ export abstract class EffectScope<C extends Context = Context> {
throw new CordisError('INACTIVE_EFFECT')
}

effect(callback: () => DisposableLike) {
effect(callback: Callable<DisposableLike, [ctx: C, config: any]>, config?: any) {
this.assertActive()
const result = callback()
const result = isConstructor(callback)
// eslint-disable-next-line new-cap
? new callback(this.ctx, config)
: callback(this.ctx, config)
const original = typeof result === 'function' ? result : result.dispose.bind(result)
const wrapped = () => {
// make sure the original callback is not called twice
Expand Down Expand Up @@ -317,7 +323,7 @@ export class MainScope<C extends Context = Context> extends EffectScope<C> {
isReusable?: boolean = false
isReactive?: boolean = false

constructor(registry: Registry<C>, public plugin: Plugin<C>, config: any, error?: any) {
constructor(registry: Registry<C>, public plugin: Plugin, config: any, error?: any) {
super(registry[Context.current] as C, config)
registry.set(plugin, this)
if (!plugin) {
Expand Down

0 comments on commit c1ac7b0

Please sign in to comment.