Skip to content

Commit 637d37e

Browse files
committed
feat(loader): support isolate transfer
1 parent 7ac5457 commit 637d37e

File tree

3 files changed

+150
-120
lines changed

3 files changed

+150
-120
lines changed

packages/loader/src/entry.ts

Lines changed: 33 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -9,18 +9,18 @@ export namespace Entry {
99
config?: any
1010
disabled?: boolean | null
1111
intercept?: Dict | null
12-
isolate?: Dict<true | string>
12+
isolate?: Dict<true | string> | null
1313
when?: any
1414
}
1515
}
1616

17-
function swap<T extends {}>(target: T, source?: T | null): T {
18-
const result = { ...target }
19-
for (const key in result) {
20-
delete target[key]
17+
function swap<T extends {}>(target: T, source?: T | null) {
18+
for (const key of Reflect.ownKeys(target)) {
19+
Reflect.deleteProperty(target, key)
20+
}
21+
for (const key of Reflect.ownKeys(source || {})) {
22+
Reflect.defineProperty(target, key, Reflect.getOwnPropertyDescriptor(source!, key)!)
2123
}
22-
Object.assign(target, source)
23-
return result
2424
}
2525

2626
function takeEntries(object: {}, keys: string[]) {
@@ -62,30 +62,18 @@ export class Entry {
6262
}
6363
}
6464

65-
patch(ctx?: Context, legacy?: Entry.Options) {
66-
ctx ??= this.parent.extend({
67-
[Context.intercept]: Object.create(this.parent[Context.intercept]),
68-
[Context.isolate]: Object.create(this.parent[Context.isolate]),
69-
})
70-
ctx.emit('loader/patch', this, legacy)
71-
swap(ctx[Context.intercept], this.options.intercept)
72-
65+
patch(ctx: Context, ref: Context = ctx) {
7366
// part 1: prepare isolate map
74-
const newMap: Dict<symbol> = Object.create(Object.getPrototypeOf(ctx[Context.isolate]))
67+
const newMap: Dict<symbol> = Object.create(Object.getPrototypeOf(ref[Context.isolate]))
7568
for (const [key, label] of Object.entries(this.options.isolate ?? {})) {
7669
const realm = this.resolveRealm(label)
7770
newMap[key] = (this.loader.realms[realm] ??= Object.create(null))[key] ??= Symbol(`${key}${realm}`)
7871
}
79-
for (const [key, label] of Object.entries(legacy?.isolate ?? {})) {
80-
if (this.options.isolate?.[key] === label) continue
81-
const name = this.resolveRealm(label)
82-
this.loader._clearRealm(key, name)
83-
}
8472

8573
// part 2: generate service diff
8674
const diff: [string, symbol, symbol, symbol, symbol][] = []
8775
const oldMap = ctx[Context.isolate]
88-
for (const key in { ...oldMap, ...newMap }) {
76+
for (const key in { ...oldMap, ...newMap, ...this.loader.delims }) {
8977
if (newMap[key] === oldMap[key]) continue
9078
const delim = this.loader.delims[key] ??= Symbol(key)
9179
ctx[delim] = Symbol(`${key}#${this.options.id}`)
@@ -114,8 +102,14 @@ export class Entry {
114102
}
115103

116104
// part 3.2: update service impl, prevent double update
117-
this.fork?.update(this.options.config)
118-
swap(ctx[Context.isolate], newMap)
105+
swap(ctx[Context.intercept], this.options.intercept)
106+
if (ctx === ref) {
107+
this.fork?.update(this.options.config)
108+
swap(ctx[Context.isolate], newMap)
109+
} else {
110+
Object.setPrototypeOf(ctx, Object.getPrototypeOf(ref))
111+
swap(ctx, ref)
112+
}
119113
for (const [, symbol1, symbol2, flag1, flag2] of diff) {
120114
if (flag1 === flag2 && ctx[symbol1] && !ctx[symbol2]) {
121115
ctx.root[symbol2] = ctx.root[symbol1]
@@ -139,7 +133,13 @@ export class Entry {
139133
delete ctx[this.loader.delims[key]]
140134
}
141135
}
142-
return ctx
136+
}
137+
138+
createContext() {
139+
return this.parent.extend({
140+
[Context.intercept]: Object.create(this.parent[Context.intercept]),
141+
[Context.isolate]: Object.create(this.parent[Context.isolate]),
142+
})
143143
}
144144

145145
async update(parent: Context, options: Entry.Options) {
@@ -150,12 +150,18 @@ export class Entry {
150150
this.stop()
151151
} else if (this.fork) {
152152
this.isUpdate = true
153-
this.patch(this.fork.parent, legacy)
153+
for (const [key, label] of Object.entries(legacy.isolate ?? {})) {
154+
if (this.options.isolate?.[key] === label) continue
155+
const name = this.resolveRealm(label)
156+
this.loader._clearRealm(key, name)
157+
}
158+
this.patch(this.fork.parent)
154159
} else {
155160
this.parent.emit('loader/entry', 'apply', this)
156161
const plugin = await this.loader.resolve(this.options.name)
157162
if (!plugin) return
158-
const ctx = this.patch()
163+
const ctx = this.createContext()
164+
this.patch(ctx)
159165
this.fork = ctx.plugin(plugin, this.options.config)
160166
this.fork.entry = this
161167
}

packages/loader/src/shared.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -284,7 +284,7 @@ export abstract class Loader<T extends Loader.Options = Loader.Options> extends
284284
this.writeConfig()
285285
}
286286

287-
teleport(id: string, target: string, index = Infinity) {
287+
transfer(id: string, target: string, index = Infinity) {
288288
const entry = this.entries[id]
289289
if (!entry) throw new Error(`entry ${id} not found`)
290290
const sourceEntry = entry.parent.scope.entry!
@@ -296,7 +296,8 @@ export abstract class Loader<T extends Loader.Options = Loader.Options> extends
296296
if (sourceEntry === targetEntry) return
297297
entry.parent = targetEntry.fork.ctx
298298
if (!entry.fork) return
299-
entry.patch()
299+
const ctx = entry.createContext()
300+
entry.patch(entry.fork.parent, ctx)
300301
}
301302

302303
paths(scope: EffectScope): string[] {

0 commit comments

Comments
 (0)