Skip to content

Commit

Permalink
feat(cordis): support ctx.set()
Browse files Browse the repository at this point in the history
  • Loading branch information
shigma committed Mar 4, 2024
1 parent 29dc0df commit cfb2718
Show file tree
Hide file tree
Showing 3 changed files with 42 additions and 38 deletions.
74 changes: 39 additions & 35 deletions packages/core/src/context.ts
Expand Up @@ -121,11 +121,9 @@ export class Context {
if (!internal) {
checkInject(name)
return Reflect.get(target, name, ctx)
}

if (internal.type === 'accessor') {
} else if (internal.type === 'accessor') {
return internal.get.call(ctx)
} else if (internal.type === 'service') {
} else {
if (!internal.builtin) checkInject(name)
return ctx.get(name)
}
Expand All @@ -139,38 +137,11 @@ export class Context {
if (internal.type === 'accessor') {
if (!internal.set) return false
return internal.set.call(ctx, value)
} else {
ctx.emit('internal/warning', new Error(`Assigning to service ${name} is not recommended, please use \`ctx.set()\` method instead`))
ctx.set(name, value)
return true
}

// service
const key = ctx[symbols.isolate][name]
const oldValue = ctx.root[key]
if (oldValue === value) return true

// check override
if (value && oldValue) {
throw new Error(`service ${name} has been registered`)
}
if (value) {
ctx.on('dispose', () => ctx[name] = undefined)
}
if (isUnproxyable(value)) {
ctx.emit('internal/warning', new Error(`service ${name} is an unproxyable object, which may lead to unexpected behavior`))
}

// setup filter for events
const self = Object.create(null)
self[symbols.filter] = (ctx2: Context) => {
// TypeScript is not smart enough to infer the type of `name` here
return ctx[symbols.isolate][name as string] === ctx2[symbols.isolate][name as string]
}

ctx.root.emit(self, 'internal/before-service', name, value)
ctx.root[key] = value
if (value instanceof Object) {
defineProperty(value, symbols.origin, ctx)
}
ctx.root.emit(self, 'internal/service', name, oldValue)
return true
},
}

Expand Down Expand Up @@ -253,6 +224,39 @@ export class Context {
return createTraceable(this, value)
}

set(name: string, value: any) {
this.provide(name)
const key = this[symbols.isolate][name]
const oldValue = this.root[key]
value ??= undefined
if (oldValue === value) return

// check override
if (!isNullable(value) && !isNullable(oldValue)) {
throw new Error(`service ${name} has been registered`)
}
const ctx: Context = this
if (!isNullable(value)) {
ctx.on('dispose', () => ctx.set(name, undefined))
}
if (isUnproxyable(value)) {
ctx.emit('internal/warning', new Error(`service ${name} is an unproxyable object, which may lead to unexpected behavior`))
}

// setup filter for events
const self = Object.create(null)
self[symbols.filter] = (ctx2: Context) => {
return ctx[symbols.isolate][name] === ctx2[symbols.isolate][name]
}

ctx.emit(self, 'internal/before-service', name, value)
ctx.root[key] = value
if (value instanceof Object) {
defineProperty(value, symbols.origin, ctx)
}
ctx.emit(self, 'internal/service', name, oldValue)
}

provide(name: string, value?: any, builtin?: boolean) {
const internal = Context.ensureInternal.call(this.root)
if (name in internal) return
Expand Down
2 changes: 1 addition & 1 deletion packages/core/src/scope.ts
Expand Up @@ -386,7 +386,7 @@ export class MainScope<C extends Context = Context> extends EffectScope<C> {
const instance = new this.plugin(context, config)
const name = instance[Context.expose]
if (name) {
context[name] = instance
context.set(name, instance)
}
if (instance['fork']) {
this.forkables.push(instance['fork'].bind(instance))
Expand Down
4 changes: 2 additions & 2 deletions packages/core/src/service.ts
Expand Up @@ -56,14 +56,14 @@ export abstract class Service<T = unknown, C extends Context = Context> {
self.ctx.runtime.name = name
if (immediate) {
if (_ctx) self[symbols.expose] = name
else self.ctx[name] = self
else self.ctx.set(name, self)
}

self.ctx.on('ready', async () => {
// await until next tick because derived class has not been initialized yet
await Promise.resolve()
await self.start()
if (!immediate) self.ctx[name!] = self
if (!immediate) self.ctx.set(name!, self)
})

self.ctx.on('dispose', () => self.stop())
Expand Down

0 comments on commit cfb2718

Please sign in to comment.