Skip to content

Commit

Permalink
feat(core): remove the lifecycle onBootstrap, onDestroy
Browse files Browse the repository at this point in the history
  • Loading branch information
ones-liupengfei committed Feb 17, 2023
1 parent bc6d946 commit e95d0d3
Show file tree
Hide file tree
Showing 13 changed files with 220 additions and 554 deletions.
78 changes: 12 additions & 66 deletions packages/core/src/lib/app.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,8 @@
import type { LifecyleCallbackType, DependencyType, RelateType } from '../types' // eslint-disable-line
import { deduplicate } from './utils'

export class App {
public dependenciesReady: boolean = false
public bootstrapping: Promise<void> = null
public dependencies: Array<{ name: string; ctx?: Record<string, any>; data?: any }> = []
public relatedApps: Array<{ name: string; ctx?: Record<string, any> }> = []
public doBootstrap?: LifecyleCallbackType
public doActivate?: LifecyleCallbackType
public doDestroy?: LifecyleCallbackType
public activated: Promise<void> = null
public dependencies: Array<string> = []
public relatedApps: Array<string> = []
public doActivate?: () => void | Promise<void>
public isRallieCoreApp: boolean

constructor(public name: string) {
Expand All @@ -21,75 +15,27 @@ export class App {
* @param relatedApps
* @returns
*/
public relateTo(relatedApps: RelateType[]) {
const getName = (relateApp: RelateType) =>
typeof relateApp === 'string' ? relateApp : relateApp.name
const deduplicatedRelatedApps = deduplicate(relatedApps)
const currentRelatedAppNames: string[] = this.relatedApps.map((item) => item.name)
deduplicatedRelatedApps.forEach((relatedApp) => {
if (!currentRelatedAppNames.includes(getName(relatedApp))) {
this.relatedApps.push({
name: getName(relatedApp),
ctx: typeof relatedApp !== 'string' ? relatedApp.ctx : undefined,
})
}
})
public relateTo(relatedApps: string[]) {
this.relatedApps = Array.from(new Set([...this.relatedApps, ...relatedApps]))
return this
}

/**
* indicate the apps to be activate before your app is bootstrapped
* indicate the apps to be activate before your app is activated
* @param dependencies
*/
public relyOn(dependencies: DependencyType[]) {
const getName = (dependencyApp: DependencyType) =>
typeof dependencyApp === 'string' ? dependencyApp : dependencyApp.name
const deduplicatedDependencies: DependencyType[] = deduplicate(dependencies)
const currentDependenciesNames = this.dependencies.map((item) => item.name)
const currentRelatedAppsNames = this.relatedApps.map((item) => item.name)
deduplicatedDependencies.forEach((dependency) => {
const name = getName(dependency)
if (!currentDependenciesNames.includes(name)) {
this.dependencies.push({
name,
ctx: typeof dependency !== 'string' ? dependency.ctx : undefined,
data: typeof dependency !== 'string' ? dependency.data : undefined,
})
}
if (!currentRelatedAppsNames.includes(name)) {
this.relatedApps.push({
name,
ctx: typeof dependency !== 'string' ? dependency.ctx : undefined,
})
}
})
return this
}

/**
* indicate the callback your app will run when it's activated the first time
* @param {function} callback
*/
public onBootstrap(callback: LifecyleCallbackType) {
this.doBootstrap = callback
public relyOn(dependencies: string[]) {
this.relateTo(dependencies)
this.dependencies = Array.from(new Set([...this.dependencies, ...dependencies]))
return this
}

/**
* indicate the callback your app will run when it's activated after the first time
* indicate the callback your app will run when it's activated
* @param callback
*/
public onActivate(callback: LifecyleCallbackType) {
public onActivate(callback: () => void | Promise<void>) {
this.doActivate = callback
return this
}

/**
* indicate the callback when your app is destroyed
* @param callback
*/
public onDestroy(callback: LifecyleCallbackType) {
this.doDestroy = callback
return this
}
}
188 changes: 56 additions & 132 deletions packages/core/src/lib/bus.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,54 +30,15 @@ export class Bus {
return this.apps[appName] && typeof this.apps[appName] !== 'boolean'
}

/**
* config the bus
* @param conf the new configuration object
*/
public config(conf: Partial<ConfType>) {
this.conf = {
...this.conf,
...conf,
assets: {
...this.conf.assets,
...(conf?.assets || {}),
},
}
return this
}

/**
* register the middleware
* @param middleware
*/
public use(middleware: MiddlewareFnType) {
if (typeof middleware !== 'function') {
throw new Error(Errors.wrongMiddlewareType())
}
this.middlewares.push(middleware)
this.composedMiddlewareFn = compose(this.middlewares)
return this
}

/**
* create the context to pass to the middleware
* @param ctx
* @returns
*/
private createContext(name: string, ctx: Record<string, any> = {}) {
private createContext(name: string) {
const context: ContextType = {
name,
loadScript: loader.loadScript,
loadLink: loader.loadLink,
...ctx,
}
return context
}

/**
* the core middleware
* @param ctx the context
*/
private async loadResourcesFromAssetsConfig(ctx: ContextType) {
const { name, loadScript = loader.loadScript, loadLink = loader.loadLink } = ctx
const { assets } = this.conf
Expand All @@ -98,26 +59,49 @@ export class Bus {
}
}

/**
* create a socket
* @return the socket instance
*/
private async innerActivateApp(name: string, visitPath: string[]) {
await this.loadApp(name)
if (this.isRallieCoreApp(name)) {
const app = this.apps[name] as App
await this.loadRelatedApps(app)
if (visitPath.includes(name)) {
const startIndex = visitPath.indexOf(name)
const circularPath = [...visitPath.slice(startIndex), name]
throw new Error(Errors.circularDependencies(name, circularPath))
}
visitPath.push(name)
if (!app.activated) {
const activating = async () => {
await this.activateDependencies(app, visitPath)
app.doActivate && (await Promise.resolve(app.doActivate()))
}
app.activated = activating()
}
await app.activated
visitPath.pop()
}
}

private async activateDependencies(app: App, visitPath: string[]) {
if (app.dependencies.length !== 0) {
for (const appName of app.dependencies) {
await this.innerActivateApp(appName, visitPath)
}
}
}

private async loadRelatedApps(app: App) {
await Promise.all(app.relatedApps.map((appName) => this.loadApp(appName)))
}

public createSocket() {
return new Socket(this.eventEmitter, this.stores)
}

/**
* return true if an app is created
*/
public existApp(name: string) {
return !!this.apps[name]
}

/**
* create an app
* @param name the name of the app
* @return the app instance
*/
public createApp(name: string) {
if (this.existApp(name)) {
throw new Error(Errors.createExistingApp(name))
Expand All @@ -127,15 +111,11 @@ export class Bus {
return app
}

/**
* load the resources of an app
* @param ctx
*/
public async loadApp(name: string, ctx: Record<string, any> = {}) {
public async loadApp(name: string) {
if (!this.apps[name]) {
if (!this.loadingApps[name]) {
this.loadingApps[name] = new Promise((resolve, reject) => {
const context = this.createContext(name, ctx)
const context = this.createContext(name)
// apply the middlewares
this.composedMiddlewareFn(context, this.loadResourcesFromAssetsConfig.bind(this))
.then(() => {
Expand All @@ -157,81 +137,35 @@ export class Bus {
}
}

private async activateDependencies(app: App, visitPath: string[]) {
if (!app.dependenciesReady && app.dependencies.length !== 0) {
for (const dependence of app.dependencies) {
const { name, data, ctx } = dependence
await this.activateApp(name, data, ctx, visitPath)
}
app.dependenciesReady = true
}
public async activateApp(name: string) {
await this.innerActivateApp(name, [])
}

private async loadRelatedApps(app: App) {
await Promise.all(app.relatedApps.map(({ name, ctx }) => this.loadApp(name, ctx)))
}

/**
* activate an app
* @param name
* @param data
*/
public async activateApp<T = any>(
name: string,
data?: T,
ctx: Record<string, any> = {},
visitPath: string[] = [],
) {
await this.loadApp(name, ctx)
if (this.isRallieCoreApp(name)) {
const app = this.apps[name] as App
await this.loadRelatedApps(app)
if (visitPath.includes(name)) {
const startIndex = visitPath.indexOf(name)
const circularPath = [...visitPath.slice(startIndex), name]
throw new Error(Errors.circularDependencies(name, circularPath))
}
visitPath.push(name)
if (!app.bootstrapping) {
const bootstrapping = async () => {
await this.activateDependencies(app, visitPath)
if (app.doBootstrap) {
await Promise.resolve(app.doBootstrap(data))
} else if (app.doActivate) {
await Promise.resolve(app.doActivate(data))
}
}
app.bootstrapping = bootstrapping()
await app.bootstrapping
} else {
await app.bootstrapping
app.doActivate && (await Promise.resolve(app.doActivate(data)))
}
visitPath.pop()
public config(conf: Partial<ConfType>) {
this.conf = {
...this.conf,
...conf,
assets: {
...this.conf.assets,
...(conf?.assets || {}),
},
}
return this
}

/**
* destroy an app
* @param name
* @param data
*/
public async destroyApp<T = any>(name: string, data?: T) {
if (this.isRallieCoreApp(name)) {
const app = this.apps[name] as App
app.doDestroy && (await Promise.resolve(app.doDestroy(data)))
app.bootstrapping = null
app.dependenciesReady = false
public use(middleware: MiddlewareFnType) {
if (typeof middleware !== 'function') {
throw new Error(Errors.wrongMiddlewareType())
}
this.middlewares.push(middleware)
this.composedMiddlewareFn = compose(this.middlewares)
return this
}
}

const busProxy = {}
export const DEFAULT_BUS_NAME = 'DEFAULT_BUS'
/**
* create a bus and record it on window.RALLIE_BUS_STORE
* @param name the name of the bus
*/

export const createBus = (name: string = DEFAULT_BUS_NAME) => {
if (window.RALLIE_BUS_STORE === undefined) {
Reflect.defineProperty(window, 'RALLIE_BUS_STORE', {
Expand All @@ -252,20 +186,10 @@ export const createBus = (name: string = DEFAULT_BUS_NAME) => {
}
}

/**
* get the bus from window.RALLIE_BUS_STORE
* @param name the name of the bus
* @returns
*/
export const getBus = (name: string = DEFAULT_BUS_NAME) => {
return window.RALLIE_BUS_STORE && window.RALLIE_BUS_STORE[name]
}

/**
* get the bus from window.RALLIE_BUS_STORE, if the bus is not created, then create it
* @param name the name of the bus
* @returns
*/
export const touchBus = (name: string = DEFAULT_BUS_NAME): [Bus, boolean] => {
let bus: Bus = null
let isHost: boolean = false
Expand Down
Loading

0 comments on commit e95d0d3

Please sign in to comment.