Skip to content

Commit

Permalink
feat(loader): support entry api
Browse files Browse the repository at this point in the history
  • Loading branch information
shigma committed Apr 30, 2024
1 parent dc605cb commit e67ea4a
Show file tree
Hide file tree
Showing 2 changed files with 59 additions and 67 deletions.
62 changes: 19 additions & 43 deletions packages/loader/src/entry.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,14 @@ export namespace Entry {
id: string
name: string
config?: any
disabled?: boolean
intercept?: Dict
disabled?: boolean | null
intercept?: Dict | null
isolate?: Dict<boolean | string>
when?: any
}
}

function swapAssign<T extends {}>(target: T, source?: T): T {
function swapAssign<T extends {}>(target: T, source?: T | null): T {
const result = { ...target }
for (const key in result) {
delete target[key]
Expand All @@ -40,27 +40,26 @@ function sortKeys<T extends {}>(object: T, prepend = ['id', 'name'], append = ['
return Object.assign(object, Object.fromEntries([...part1, ...rest, ...part2]))
}

const kEntry = Symbol('cordis.entry')

export class Entry {
static for(ctx: Context) {
return ctx[kEntry] as Entry | undefined
}

public fork: ForkScope | null = null
public fork?: ForkScope
public isUpdate = false
public parent!: Context
public options!: Entry.Options

constructor(public loader: Loader, public parent: Context, public options: Entry.Options) {
sortKeys(this.options)
}
constructor(public loader: Loader) {}

unlink() {
const config = this.parent.config as Entry.Options[]
const index = config.indexOf(this.options)
if (index >= 0) config.splice(index, 1)
}

amend(ctx: Context) {
amend(ctx?: Context) {
ctx ??= this.parent.extend({
[Context.intercept]: Object.create(this.parent[Context.intercept]),
[Context.isolate]: Object.create(this.parent[Context.isolate]),
})
ctx.emit('loader/patch', this)
swapAssign(ctx[Context.intercept], this.options.intercept)
const neoMap: Dict<symbol> = Object.create(Object.getPrototypeOf(ctx[Context.isolate]))
for (const [key, label] of Object.entries(this.options.isolate ?? {})) {
Expand All @@ -87,55 +86,32 @@ export class Entry {
}
ctx.emit(self, 'internal/service', key)
}
return ctx
}

// TODO: handle parent change
async update(parent: Context, options: Entry.Options) {
this.parent = parent
this.options = sortKeys(options)
if (!this.loader.isTruthyLike(options.when) || options.disabled) {
this.stop()
} else {
await this.resume()
}
}

async resume() {
if (this.fork) {
} else if (this.fork) {
this.isUpdate = true
this.amend(this.fork.parent)
this.fork.update(this.options.config)
} else {
this.parent.emit('loader/entry', 'apply', this)
const plugin = await this.loader.resolve(this.options.name)
if (!plugin) return
const ctx = this.parent.extend({
[kEntry]: this,
[Context.intercept]: Object.create(this.parent[Context.intercept]),
[Context.isolate]: Object.create(this.parent[Context.isolate]),
})
this.amend(ctx)
const ctx = this.amend()
this.fork = ctx.plugin(plugin, this.options.config)
this.fork.entry = this
}
}

stop() {
if (!this.fork) return
this.fork.dispose()
this.fork = null
}

// modification

meta(options: any) {
for (const key of Object.keys(options)) {
delete this.options[key]
if (options[key] === null) {
delete options[key]
}
}
sortKeys(this.options)
// await this.loader.writeConfig(true)
this.fork?.dispose()
this.fork = undefined
}
}

Expand Down
64 changes: 40 additions & 24 deletions packages/loader/src/shared.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ declare module '@cordisjs/core' {
'config'(): void
'exit'(signal: NodeJS.Signals): Promise<void>
'loader/entry'(type: string, entry: Entry): void
'loader/patch'(entry: Entry): void
}

interface Context {
Expand Down Expand Up @@ -78,11 +79,7 @@ export abstract class Loader<T extends Loader.Options = Loader.Options> extends

constructor(public app: Context, public options: T) {
super(app, 'loader', true)
this.root = new Entry(this, app, {
id: '',
name: 'cordis/group',
config: [],
})
this.root = new Entry(this)
this.entries[''] = this.root
this.realms.root = app.root[Context.isolate]

Expand Down Expand Up @@ -115,6 +112,8 @@ export abstract class Loader<T extends Loader.Options = Loader.Options> extends
fork.entry.options.disabled = true
this.writeConfig()
})

this.app.on('loader/patch', (entry) => {})
}

async init(filename?: string) {
Expand Down Expand Up @@ -235,21 +234,33 @@ export abstract class Loader<T extends Loader.Options = Loader.Options> extends
return options.id!
}

async update(parent: Context, options: Entry.Options) {
this.ensureId(options)
const entry = this.entries[options.id] ??= new Entry(this, parent, options)
return entry.update(parent, options)
async ensure(parent: Context, options: Omit<Entry.Options, 'id'>) {
const id = this.ensureId(options)
const entry = this.entries[id] ??= new Entry(this)
await entry.update(parent, options as Entry.Options)
return id
}

async update(id: string, options: Entry.Options) {
const entry = this.entries[id]
if (!entry) throw new Error(`entry ${id} not found`)
for (const [key, value] of Object.entries(options)) {
if (isNullable(value)) {
delete entry.options[key]
} else {
entry.options[key] = value
}
}
this.writeConfig()
return entry.update(entry.parent, entry.options)
}

async create(options: Omit<Entry.Options, 'id'>, target = '', index = Infinity) {
const parentEntry = this.entries[target]
if (!parentEntry) throw new Error('cannot locate parent entry')
const id = this.ensureId(options)
parentEntry.options.config.splice(index, 0, options)
const targetEntry = this.entries[target]
if (!targetEntry?.fork) throw new Error(`entry ${target} not found`)
targetEntry.options.config.splice(index, 0, options)
this.writeConfig()
if (!parentEntry.fork) return id
await this.update(parentEntry.fork.ctx, options as Entry.Options)
return id
return this.ensure(targetEntry.fork.ctx, options)
}

remove(id: string, passive = false) {
Expand All @@ -265,16 +276,17 @@ export abstract class Loader<T extends Loader.Options = Loader.Options> extends

teleport(id: string, target: string, index = Infinity) {
const entry = this.entries[id]
if (!entry) throw new Error(`entry ${id} not found`)
const sourceEntry = entry.parent.scope.entry!
const targetEntry = this.entries[target]
if (!targetEntry) throw new Error('cannot locate target entry')
if (!targetEntry?.fork) throw new Error(`entry ${target} not found`)
entry.unlink()
targetEntry.options.config.splice(index, 0, entry.options)
if (sourceEntry !== targetEntry) {
entry.parent = targetEntry.fork!.ctx
entry.amend(entry.parent) // refresh parent only?
}
this.writeConfig()
if (sourceEntry === targetEntry) return
entry.parent = targetEntry.fork.ctx
if (!entry.fork) return
entry.amend()
}

paths(scope: EffectScope): string[] {
Expand All @@ -293,7 +305,11 @@ export abstract class Loader<T extends Loader.Options = Loader.Options> extends
async start() {
await this.readConfig()
this.root.options.config = this.config
this.root.resume()
this.root.update(this.app, {
id: '',
name: 'cordis/group',
config: [],
})
this.app.emit('config')

while (this.tasks.size) {
Expand All @@ -320,7 +336,7 @@ export function createGroup(config?: Entry.Options[], options: GroupOptions = {}

function group(ctx: Context, config: Entry.Options[]) {
for (const options of config) {
ctx.loader.update(ctx, options)
ctx.loader.ensure(ctx, options)
}

ctx.accept((neo: Entry.Options[]) => {
Expand All @@ -334,7 +350,7 @@ export function createGroup(config?: Entry.Options[], options: GroupOptions = {}
if (!neoMap[id]) {
ctx.loader.remove(id, true)
} else {
ctx.loader.update(ctx, neoMap[id])
ctx.loader.ensure(ctx, neoMap[id])
}
}
}, { passive: true })
Expand Down

0 comments on commit e67ea4a

Please sign in to comment.