Skip to content

Commit 1333ef0

Browse files
committed
feat: support new provide
1 parent d6aabd4 commit 1333ef0

File tree

7 files changed

+50
-36
lines changed

7 files changed

+50
-36
lines changed

packages/core/src/context.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ export namespace Context {
1717
}
1818

1919
export interface Item<C extends Context> {
20+
name: string
2021
value?: any
2122
source: C
2223
}
@@ -64,7 +65,6 @@ export class Context {
6465
static readonly events: unique symbol = symbols.events as any
6566
static readonly static: unique symbol = symbols.static as any
6667
static readonly filter: unique symbol = symbols.filter as any
67-
static readonly expose: unique symbol = symbols.expose as any
6868
static readonly isolate: unique symbol = symbols.isolate as any
6969
static readonly internal: unique symbol = symbols.internal as any
7070
static readonly intercept: unique symbol = symbols.intercept as any

packages/core/src/events.ts

Lines changed: 21 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ class Lifecycle {
5555
property: 'ctx',
5656
})
5757

58-
defineProperty(this.on('internal/listener', function (this: Context, name, listener, options: EventOptions) {
58+
ctx.scope.leak(this.on('internal/listener', function (this: Context, name, listener, options: EventOptions) {
5959
const method = options.prepend ? 'unshift' : 'push'
6060
if (name === 'ready') {
6161
if (!this.lifecycle.isActive) return
@@ -69,18 +69,18 @@ class Lifecycle {
6969
this.scope.runtime.forkables[method](listener as any)
7070
return this.scope.collect('event <fork>', () => remove(this.scope.runtime.forkables, listener))
7171
}
72-
}), Context.static, ctx.scope)
72+
}))
7373

7474
for (const level of ['info', 'error', 'warning']) {
75-
defineProperty(this.on(`internal/${level}`, (format, ...param) => {
75+
ctx.scope.leak(this.on(`internal/${level}`, (format, ...param) => {
7676
if (this._hooks[`internal/${level}`].length > 1) return
7777
// eslint-disable-next-line no-console
7878
console.info(format, ...param)
79-
}), Context.static, ctx.scope)
79+
}))
8080
}
8181

8282
// non-reusable plugin forks are not responsive to isolated service changes
83-
defineProperty(this.on('internal/before-service', function (this: Context, name) {
83+
ctx.scope.leak(this.on('internal/before-service', function (this: Context, name) {
8484
for (const runtime of this.registry.values()) {
8585
if (!runtime.inject[name]?.required) continue
8686
const scopes = runtime.isReusable ? runtime.children : [runtime]
@@ -90,9 +90,9 @@ class Lifecycle {
9090
scope.reset()
9191
}
9292
}
93-
}, { global: true }), Context.static, ctx.scope)
93+
}, { global: true }))
9494

95-
defineProperty(this.on('internal/service', function (this: Context, name) {
95+
ctx.scope.leak(this.on('internal/service', function (this: Context, name) {
9696
for (const runtime of this.registry.values()) {
9797
if (!runtime.inject[name]?.required) continue
9898
const scopes = runtime.isReusable ? runtime.children : [runtime]
@@ -101,7 +101,18 @@ class Lifecycle {
101101
scope.start()
102102
}
103103
}
104-
}, { global: true }), Context.static, ctx.scope)
104+
}, { global: true }))
105+
106+
ctx.scope.leak(this.on('internal/status', function (scope: EffectScope) {
107+
if (scope.status !== ScopeStatus.ACTIVE) return
108+
for (const key of Reflect.ownKeys(ctx[symbols.store])) {
109+
const item = ctx[symbols.store][key as symbol]
110+
if (item.source.scope !== scope) continue
111+
if (item.value) {
112+
item.source.emit(item.source, 'internal/service', item.name, item.value)
113+
}
114+
}
115+
}, { global: true }))
105116

106117
// inject in ancestor contexts
107118
const checkInject = (scope: EffectScope, name: string) => {
@@ -112,9 +123,9 @@ class Lifecycle {
112123
return checkInject(scope.parent.scope, name)
113124
}
114125

115-
defineProperty(this.on('internal/inject', function (this: Context, name) {
126+
ctx.scope.leak(this.on('internal/inject', function (this: Context, name) {
116127
return checkInject(this.scope, name)
117-
}, { global: true }), Context.static, ctx.scope)
128+
}, { global: true }))
118129
}
119130

120131
async flush() {

packages/core/src/reflect.ts

Lines changed: 15 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { defineProperty, Dict, isNullable } from 'cosmokit'
22
import { Context } from './context'
33
import { getTraceable, isObject, isUnproxyable, symbols, withProps } from './utils'
4+
import { ScopeStatus } from './scope'
45

56
declare module './context' {
67
interface Context {
@@ -105,12 +106,14 @@ class ReflectService {
105106
this._mixin('lifecycle', ['on', 'once', 'parallel', 'emit', 'serial', 'bail', 'start', 'stop'])
106107
}
107108

108-
get(name: string) {
109+
get(name: string, strict = false) {
109110
const internal = this.ctx[symbols.internal][name]
110111
if (internal?.type !== 'service') return
111112
const key = this.ctx[symbols.isolate][name]
112-
const value = this.ctx[symbols.store][key]?.value
113-
return getTraceable(this.ctx, value)
113+
const item = this.ctx[symbols.store][key]
114+
if (!item) return
115+
if (strict && item.source.scope.status !== ScopeStatus.ACTIVE) return
116+
return getTraceable(this.ctx, item.value)
114117
}
115118

116119
set(name: string, value: any) {
@@ -141,9 +144,13 @@ class ReflectService {
141144
return ctx[symbols.isolate][name] === ctx2[symbols.isolate][name]
142145
}
143146

144-
ctx.emit(self, 'internal/before-service', name, value)
145-
ctx[symbols.store][key] = { value, source: ctx }
146-
ctx.emit(self, 'internal/service', name, oldValue)
147+
if (ctx.scope.status === ScopeStatus.ACTIVE) {
148+
ctx.emit(self, 'internal/before-service', name, value)
149+
}
150+
ctx[symbols.store][key] = { name, value, source: self }
151+
if (ctx.scope.status === ScopeStatus.ACTIVE) {
152+
ctx.emit(self, 'internal/service', name, oldValue)
153+
}
147154
return dispose
148155
}
149156

@@ -154,7 +161,7 @@ class ReflectService {
154161
internal[name] = { type: 'service', builtin }
155162
this.ctx.root[symbols.isolate][name] = key
156163
if (!isObject(value)) return
157-
this.ctx[symbols.store][key] = { value, source: null! }
164+
this.ctx[symbols.store][key] = { name, value, source: null! }
158165
defineProperty(value, symbols.tracker, {
159166
associate: name,
160167
property: 'ctx',
@@ -165,7 +172,7 @@ class ReflectService {
165172
const internal = this.ctx.root[symbols.internal]
166173
if (name in internal) return () => {}
167174
internal[name] = { type: 'accessor', ...options }
168-
return () => delete this.ctx.root[symbols.isolate][name]
175+
return () => delete internal[name]
169176
}
170177

171178
accessor(name: string, options: Omit<Context.Internal.Accessor, 'type'>) {

packages/core/src/registry.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@ export namespace Plugin {
6060
reusable?: boolean
6161
Config?: (config: any) => T
6262
inject?: Inject
63+
provide?: string | string[]
6364
intercept?: Dict<boolean>
6465
}
6566

packages/core/src/scope.ts

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -147,7 +147,8 @@ export abstract class EffectScope<C extends Context = Context> {
147147
}
148148

149149
ensure(callback: () => Promise<void>) {
150-
const task = callback()
150+
const task = Promise.resolve()
151+
.then(callback)
151152
.catch((reason) => {
152153
this.context.emit(this.ctx, 'internal/error', reason)
153154
this.cancel(reason)
@@ -168,10 +169,14 @@ export abstract class EffectScope<C extends Context = Context> {
168169

169170
get ready() {
170171
return Object.entries(this.runtime.inject).every(([name, inject]) => {
171-
return !inject.required || !isNullable(this.ctx.get(name))
172+
return !inject.required || !isNullable(this.ctx.reflect.get(name, true))
172173
})
173174
}
174175

176+
leak<T>(disposable: T) {
177+
return defineProperty(disposable, Context.static, this)
178+
}
179+
175180
reset() {
176181
this.isActive = false
177182
this.disposables = this.disposables.splice(0).filter((dispose) => {
@@ -259,7 +264,7 @@ export class ForkScope<C extends Context = Context> extends EffectScope<C> {
259264
constructor(parent: Context, public runtime: MainScope<C>, config: C['config'], error?: any) {
260265
super(parent as C, config)
261266

262-
this.dispose = defineProperty(parent.scope.collect(`fork <${parent.runtime.name}>`, () => {
267+
this.dispose = runtime.leak(parent.scope.collect(`fork <${parent.runtime.name}>`, () => {
263268
this.uid = null
264269
this.reset()
265270
this.context.emit('internal/fork', this)
@@ -268,7 +273,7 @@ export class ForkScope<C extends Context = Context> extends EffectScope<C> {
268273
parent.registry.delete(runtime.plugin)
269274
}
270275
return result
271-
}), Context.static, runtime)
276+
}))
272277

273278
runtime.children.push(this)
274279
runtime.disposables.push(this.dispose)
@@ -363,10 +368,6 @@ export class MainScope<C extends Context = Context> extends EffectScope<C> {
363368
} else if (isConstructor(this.plugin)) {
364369
// eslint-disable-next-line new-cap
365370
const instance = new this.plugin(context, config)
366-
const name = instance[Context.expose]
367-
if (name) {
368-
context.set(name, instance)
369-
}
370371
if (instance['fork']) {
371372
this.forkables.push(instance['fork'].bind(instance))
372373
}

packages/core/src/service.ts

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,8 @@ export abstract class Service<C extends Context = Context> {
66
static readonly invoke: unique symbol = symbols.invoke as any
77
static readonly extend: unique symbol = symbols.extend as any
88
static readonly tracker: unique symbol = symbols.tracker as any
9-
static readonly provide: unique symbol = symbols.provide as any
109
static readonly immediate: unique symbol = symbols.immediate as any
10+
static readonly provide = 'provide' as any
1111

1212
protected start(): Awaitable<void> {}
1313
protected stop(): Awaitable<void> {}
@@ -28,21 +28,17 @@ export abstract class Service<C extends Context = Context> {
2828
}
2929
self.ctx = ctx
3030
self.name = name
31-
self.config = config
31+
// self.config = config
3232
defineProperty(self, symbols.tracker, tracker)
3333

3434
self.ctx.provide(name)
3535
self.ctx.runtime.name = name
36-
if (immediate) {
37-
if (_ctx) self[symbols.expose] = name
38-
else self.ctx.set(name, self)
39-
}
36+
self.ctx.set(name, self)
4037

4138
self.ctx.on('ready', async () => {
4239
// await until next tick because derived class has not been initialized yet
4340
await Promise.resolve()
4441
await self.start()
45-
if (!immediate) self.ctx.set(name!, self)
4642
})
4743

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

packages/core/src/utils.ts

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,6 @@ export const symbols = {
1717
events: Symbol.for('cordis.events') as typeof Context.events,
1818
static: Symbol.for('cordis.static') as typeof Context.static,
1919
filter: Symbol.for('cordis.filter') as typeof Context.filter,
20-
expose: Symbol.for('cordis.expose') as typeof Context.expose,
2120
isolate: Symbol.for('cordis.isolate') as typeof Context.isolate,
2221
internal: Symbol.for('cordis.internal') as typeof Context.internal,
2322
intercept: Symbol.for('cordis.intercept') as typeof Context.intercept,
@@ -26,7 +25,6 @@ export const symbols = {
2625
invoke: Symbol.for('cordis.invoke') as typeof Service.invoke,
2726
extend: Symbol.for('cordis.extend') as typeof Service.extend,
2827
tracker: Symbol.for('cordis.tracker') as typeof Service.tracker,
29-
provide: Symbol.for('cordis.provide') as typeof Service.provide,
3028
immediate: Symbol.for('cordis.immediate') as typeof Service.immediate,
3129
}
3230

0 commit comments

Comments
 (0)