Skip to content

Commit

Permalink
refa: remove accept / decline
Browse files Browse the repository at this point in the history
  • Loading branch information
shigma committed Nov 9, 2024
1 parent 61ac112 commit 28c09ff
Show file tree
Hide file tree
Showing 5 changed files with 22 additions and 130 deletions.
4 changes: 2 additions & 2 deletions packages/cordis/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,8 @@ export interface Context {
export class Context extends core.Context {
baseDir: string

constructor(config?: any) {
super(config)
constructor() {
super()
this.baseDir = globalThis.process?.cwd?.() || ''

this.provide('logger', undefined, true)
Expand Down
23 changes: 5 additions & 18 deletions packages/core/src/context.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { defineProperty, Dict } from 'cosmokit'
import { Dict } from 'cosmokit'
import EventsService from './events'
import ReflectService from './reflect'
import Registry from './registry'
import { getTraceable, resolveConfig, symbols } from './utils'
import { getTraceable, symbols } from './utils'
import { EffectScope } from './scope'

export { EventsService, ReflectService, Registry }
Expand Down Expand Up @@ -85,30 +85,17 @@ export class Context {
return object
}

constructor(config?: any) {
config = resolveConfig(this.constructor, config)
constructor() {
this[symbols.store] = Object.create(null)
this[symbols.isolate] = Object.create(null)
this[symbols.internal] = Object.create(null)
this[symbols.intercept] = Object.create(null)
const self: Context = new Proxy(this, ReflectService.handler)
self.root = self
self.scope = new EffectScope(self, config, () => {})
self.scope = new EffectScope(self, {}, () => {})
self.reflect = new ReflectService(self)
self.registry = new Registry(self, config)
self.registry = new Registry(self)
self.events = new EventsService(self)

const attach = (internal: Context[typeof symbols.internal]) => {
if (!internal) return
attach(Object.getPrototypeOf(internal))
for (const key of Object.getOwnPropertyNames(internal)) {
const constructor = internal[key]['prototype']?.constructor
if (!constructor) continue
self[internal[key]['key']] = new constructor(self, config)
defineProperty(self[internal[key]['key']], 'ctx', self)
}
}
attach(this[symbols.internal])
return self
}

Expand Down
23 changes: 5 additions & 18 deletions packages/core/src/registry.ts
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,7 @@ class Registry<C extends Context = Context> {
private _internal = new Map<Function, Plugin.Runtime<C>>()
protected context: Context

constructor(public ctx: C, config: any) {
constructor(public ctx: C) {
defineProperty(this, symbols.tracker, {
associate: 'registry',
property: 'ctx',
Expand Down Expand Up @@ -190,29 +190,19 @@ class Registry<C extends Context = Context> {
return this.plugin({ inject, apply: callback, name: callback.name })
}

plugin(plugin: Plugin<C>, config?: any, error?: any) {
plugin(plugin: Plugin<C>, config?: any) {
// check if it's a valid plugin
const key = this.resolve(plugin, true)
this.ctx.scope.assertActive()

// resolve plugin config
if (!error) {
try {
config = resolveConfig(plugin, config)
} catch (reason) {
this.context.emit(this.ctx, 'internal/error', reason)
error = reason
config = null
}
}

let runtime = this._internal.get(key)
if (!runtime) {
runtime = Plugin.resolve<C>(plugin)
this._internal.set(key!, runtime)
}

const scope = new EffectScope(this.ctx, config, async (ctx, config) => {
config = resolveConfig(plugin, config)
if (typeof plugin !== 'function') {
await plugin.apply(ctx, config)
} else if (isConstructor(plugin)) {
Expand All @@ -226,11 +216,8 @@ class Registry<C extends Context = Context> {
await plugin(ctx, config)
}
}, runtime)
if (!config) {
scope.cancel(error)
} else {
scope.start()
}

scope.start()
return scope
}
}
Expand Down
85 changes: 7 additions & 78 deletions packages/core/src/scope.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,12 @@
import { deepEqual, isNullable, remove } from 'cosmokit'
import { isNullable } from 'cosmokit'
import { Context } from './context'
import { Plugin } from './registry'
import { DisposableList, resolveConfig } from './utils'
import { DisposableList } from './utils'

declare module './context' {
export interface Context {
scope: EffectScope<this>
effect(callback: Effect): () => boolean
accept(callback?: (config: this['config']) => void | boolean, options?: AcceptOptions): () => boolean
accept(keys: (keyof this['config'])[], callback?: (config: this['config']) => void | boolean, options?: AcceptOptions): () => boolean
decline(keys: (keyof this['config'])[]): () => boolean
}
}

Expand Down Expand Up @@ -55,7 +52,6 @@ export class EffectScope<C extends Context = Context> {
public uid: number | null
public ctx: C
public disposables = new DisposableList<Disposable>()
public error: any
public status = ScopeStatus.PENDING
public isActive = false
public dispose: () => void
Expand Down Expand Up @@ -143,7 +139,6 @@ export class EffectScope<C extends Context = Context> {

async restart() {
await this.reset()
this.error = null
this.hasError = false
this.status = ScopeStatus.PENDING
await this.start()
Expand All @@ -165,8 +160,7 @@ export class EffectScope<C extends Context = Context> {
}
}

cancel(reason?: any) {
this.error = reason
cancel() {
this.updateStatus(() => this.hasError = true)
this.reset()
}
Expand Down Expand Up @@ -198,77 +192,12 @@ export class EffectScope<C extends Context = Context> {
this.updateStatus()
}

accept(callback?: (config: C['config']) => void | boolean, options?: AcceptOptions): () => boolean
accept(keys: string[], callback?: (config: C['config']) => void | boolean, options?: AcceptOptions): () => boolean
accept(...args: any[]) {
const keys = Array.isArray(args[0]) ? args.shift() : null
const acceptor: Acceptor = { keys, callback: args[0], ...args[1] }
return this.effect(() => {
this.acceptors.push(acceptor)
if (acceptor.immediate) acceptor.callback?.(this.config)
return () => remove(this.acceptors, acceptor)
})
}

decline(keys: string[]) {
return this.accept(keys, () => true)
}

checkUpdate(resolved: any, forced?: boolean) {
if (forced || !this.config) return [true, true]
if (forced === false) return [false, false]

const modified: Record<string, boolean> = Object.create(null)
const checkPropertyUpdate = (key: string) => {
const result = modified[key] ??= !deepEqual(this.config[key], resolved[key])
hasUpdate ||= result
return result
}

const ignored = new Set<string>()
let hasUpdate = false, shouldRestart = false
let fallback: boolean | null = this.runtime?.isReactive || null
for (const { keys, callback, passive } of this.acceptors) {
if (!keys) {
fallback ||= !passive
} else if (passive) {
keys?.forEach(key => ignored.add(key))
} else {
let hasUpdate = false
for (const key of keys) {
hasUpdate ||= checkPropertyUpdate(key)
}
if (!hasUpdate) continue
}
const result = callback?.(resolved)
if (result) shouldRestart = true
}

for (const key in { ...this.config, ...resolved }) {
if (fallback === false) continue
if (!(key in modified) && !ignored.has(key)) {
const hasUpdate = checkPropertyUpdate(key)
if (fallback === null) shouldRestart ||= hasUpdate
}
}
return [hasUpdate, shouldRestart]
}

update(config: any, forced?: boolean) {
const oldConfig = this.config
let resolved: any
try {
resolved = resolveConfig(this.runtime?.plugin, config)
} catch (error) {
this.context.emit('internal/error', error)
return this.cancel(error)
}
const [hasUpdate, shouldRestart] = this.checkUpdate(resolved, forced)
this.context.emit('internal/before-update', this, config)
this.config = resolved
if (hasUpdate) {
this.context.emit('internal/update', this, oldConfig)
}
if (shouldRestart) this.restart()
this.config = config
// FIXME: use single emit
this.context.emit('internal/update', this, oldConfig)
this.restart()
}
}
17 changes: 3 additions & 14 deletions packages/loader/src/config/entry.ts
Original file line number Diff line number Diff line change
Expand Up @@ -82,12 +82,7 @@ export class Entry<C extends Context = Context> {

_resolveConfig(plugin: any): [any, any?] {
if (plugin[EntryGroup.key]) return [this.options.config]
try {
return [interpolate(this.ctx, this.options.config)]
} catch (error) {
this.context.emit(this.ctx, 'internal/error', error)
return [null, error]
}
return interpolate(this.ctx, this.options.config)
}

patch(options: Partial<EntryOptions> = {}) {
Expand All @@ -101,12 +96,7 @@ export class Entry<C extends Context = Context> {
if (this.scope && 'config' in options) {
// step 2: update fork (when options.config is updated)
this.suspend = true
const [config, error] = this._resolveConfig(this.scope.runtime?.plugin)
if (error) {
this.scope.cancel(error)
} else {
this.scope.update(config)
}
this.scope.update(this._resolveConfig(this.scope.runtime?.plugin))
} else if (this.subgroup && 'disabled' in options) {
// step 3: check children (when options.disabled is updated)
const tree = this.subtree ?? this.parent.tree
Expand Down Expand Up @@ -166,8 +156,7 @@ export class Entry<C extends Context = Context> {
const plugin = this.loader.unwrapExports(exports)
this.patch()
this.ctx[Entry.key] = this
const [config, error] = this._resolveConfig(plugin)
this.scope = this.ctx.registry.plugin(plugin, config, error)
this.scope = this.ctx.registry.plugin(plugin, this._resolveConfig(plugin))
this.context.emit('loader/entry-fork', this, 'apply')
}

Expand Down

0 comments on commit 28c09ff

Please sign in to comment.