feat(lib/plugins): refactor discord plugin API as API extensions
The aim is to reduce Discord-specific code in core libraries.
This commit is contained in:
parent
6d1ac67bff
commit
3cc039ae73
9 changed files with 144 additions and 122 deletions
|
|
@ -21,6 +21,9 @@
|
|||
"./native": {
|
||||
"default": "./src/native.ts"
|
||||
},
|
||||
"./init": {
|
||||
"default": "./src/init.ts"
|
||||
},
|
||||
"./preinit": {
|
||||
"default": "./src/preinit.ts"
|
||||
},
|
||||
|
|
|
|||
26
lib/discord/src/init.ts
Normal file
26
lib/discord/src/init.ts
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
import { _initExts, _uapi } from '@revenge-mod/plugins/_'
|
||||
import { defineLazyProperty } from '@revenge-mod/utils/objects'
|
||||
|
||||
import { Logger } from './common'
|
||||
|
||||
import type { UnscopedInitPluginApi } from '@revenge-mod/plugins/types'
|
||||
import type { PluginApiDiscord } from './types/revenge'
|
||||
|
||||
const uapi = _uapi as UnscopedInitPluginApi
|
||||
const discord = (uapi.discord = {} as PluginApiDiscord)
|
||||
|
||||
// TODO: Rolldown issue: Bug requires us to do { ...require('...') } instead of just require('...')
|
||||
defineLazyProperty(discord, 'actions', () => ({ ...require('@revenge-mod/discord/actions') }))
|
||||
defineLazyProperty(discord, 'common', () => ({ ...require('@revenge-mod/discord/common') }))
|
||||
defineLazyProperty(discord, 'design', () => ({ ...require('@revenge-mod/discord/design') }))
|
||||
defineLazyProperty(discord, 'native', () => ({ ...require('@revenge-mod/discord/native') }))
|
||||
defineLazyProperty(discord, 'modules', () => ({
|
||||
settings: {
|
||||
...require('@revenge-mod/discord/modules/settings'),
|
||||
renderer: require('@revenge-mod/discord/modules/settings/renderer'),
|
||||
},
|
||||
}))
|
||||
|
||||
_initExts.push((api, { manifest: { id } }) =>
|
||||
defineLazyProperty(api, 'logger', () => new Logger(`Revenge > Plugins (${id})`)),
|
||||
)
|
||||
32
lib/discord/src/types/revenge.ts
Normal file
32
lib/discord/src/types/revenge.ts
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
import type { DiscordModules } from '.'
|
||||
|
||||
export interface PluginApiDiscord {
|
||||
actions: PluginApiDiscord.Actions
|
||||
common: PluginApiDiscord.Common
|
||||
design: PluginApiDiscord.Design
|
||||
modules: PluginApiDiscord.Modules
|
||||
native: PluginApiDiscord.Native
|
||||
}
|
||||
|
||||
export namespace PluginApiDiscord {
|
||||
export type Actions = typeof import('@revenge-mod/discord/actions')
|
||||
export type Common = typeof import('@revenge-mod/discord/common')
|
||||
export type Design = typeof import('@revenge-mod/discord/design')
|
||||
export type Native = typeof import('@revenge-mod/discord/native')
|
||||
|
||||
export interface Modules {
|
||||
settings: typeof import('@revenge-mod/discord/modules/settings') & {
|
||||
renderer: typeof import('@revenge-mod/discord/modules/settings/renderer')
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
declare module '@revenge-mod/plugins/types' {
|
||||
export interface UnscopedInitPluginApi {
|
||||
discord: PluginApiDiscord
|
||||
}
|
||||
|
||||
export interface InitPluginApi {
|
||||
logger: InstanceType<DiscordModules.Logger>
|
||||
}
|
||||
}
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
import { type StorageOptions, getStorage } from '@revenge-mod/storage'
|
||||
import { allSettled, sleepReject } from '@revenge-mod/utils/promises'
|
||||
|
||||
import { _uapi } from './apis'
|
||||
import { _uapi as uapi } from './apis'
|
||||
|
||||
import { PluginFlags as Flag, PluginsStorageDirectory, PluginStatus as Status } from './constants'
|
||||
|
||||
|
|
@ -16,6 +16,12 @@ import type {
|
|||
PreInitPluginApi,
|
||||
} from './types'
|
||||
|
||||
export const _uapi = uapi
|
||||
|
||||
export const _preInitExts: Array<(api: PreInitPluginApi, plugin: InternalPlugin) => void> = []
|
||||
export const _initExts: Array<(api: InitPluginApi, plugin: InternalPlugin) => void> = []
|
||||
export const _startExts: Array<(api: PluginApi, plugin: InternalPlugin) => void> = []
|
||||
|
||||
export const _plugins = new Map<PluginManifest['id'], InternalPlugin>()
|
||||
export const _metas = new Map<
|
||||
PluginManifest['id'],
|
||||
|
|
@ -68,62 +74,56 @@ function handlePluginError(e: unknown, plugin: InternalPlugin) {
|
|||
if (!(iflags & InternalPluginFlags.Essential)) return plugin.disable()
|
||||
}
|
||||
|
||||
function preparePluginPreInit(id: PluginManifest['id']) {
|
||||
const plugin = _plugins.get(id)!
|
||||
const meta = _metas.get(id)!
|
||||
function preparePluginPreInit(plugin: InternalPlugin) {
|
||||
const meta = _metas.get(plugin.manifest.id)!
|
||||
|
||||
// Clear errors from previous runs
|
||||
plugin.errors = []
|
||||
plugin.status &= ~Flag.Errored
|
||||
|
||||
meta[0] = {
|
||||
const api = (meta[0] = {
|
||||
cleanup: (...items) => {
|
||||
plugin._c.push(...items)
|
||||
},
|
||||
plugin,
|
||||
unscoped: _uapi,
|
||||
}
|
||||
})
|
||||
|
||||
for (const ext of _preInitExts) ext(api, plugin)
|
||||
|
||||
meta[3] = PluginApiLevel.PreInit
|
||||
}
|
||||
|
||||
function preparePluginInit(id: PluginManifest['id'], storageOptions?: StorageOptions<AnyObject>) {
|
||||
const meta = _metas.get(id)!
|
||||
function preparePluginInit(plugin: InternalPlugin, storageOptions?: StorageOptions<AnyObject>) {
|
||||
const meta = _metas.get(plugin.manifest.id)!
|
||||
const api = meta[0] as InitPluginApi
|
||||
|
||||
Object.defineProperties(api, {
|
||||
logger: {
|
||||
configurable: true,
|
||||
get() {
|
||||
// @ts-expect-error
|
||||
// biome-ignore lint/performance/noDelete: <explanation>
|
||||
delete api.logger
|
||||
return (api.logger = new api.unscoped.discord.common.Logger(`Revenge > Plugins (${id})`))
|
||||
},
|
||||
},
|
||||
storage: {
|
||||
configurable: true,
|
||||
get() {
|
||||
// @ts-expect-error
|
||||
// biome-ignore lint/performance/noDelete: <explanation>
|
||||
delete api.storage
|
||||
return (api.storage = getStorage(`${PluginsStorageDirectory}/${id}.json`, {
|
||||
...storageOptions,
|
||||
directory: 'documents',
|
||||
}))
|
||||
},
|
||||
Object.defineProperty(api, 'storage', {
|
||||
configurable: true,
|
||||
get() {
|
||||
// @ts-expect-error
|
||||
// biome-ignore lint/performance/noDelete: <explanation>
|
||||
delete api.storage
|
||||
return (api.storage = getStorage(`${PluginsStorageDirectory}/${plugin.manifest.id}.json`, {
|
||||
...storageOptions,
|
||||
directory: 'documents',
|
||||
}))
|
||||
},
|
||||
})
|
||||
|
||||
for (const ext of _initExts) ext(api, plugin)
|
||||
|
||||
meta[3] = PluginApiLevel.Init
|
||||
}
|
||||
|
||||
function preparePluginStart(id: PluginManifest['id']) {
|
||||
const meta = _metas.get(id)!
|
||||
function preparePluginStart(plugin: InternalPlugin) {
|
||||
const meta = _metas.get(plugin.manifest.id)!
|
||||
|
||||
// const api = meta[0] as PluginApi
|
||||
const api = meta[0] as PluginApi
|
||||
// api.settings = ...
|
||||
|
||||
for (const ext of _startExts) ext(api, plugin)
|
||||
|
||||
meta[3] = PluginApiLevel.Start
|
||||
}
|
||||
|
||||
|
|
@ -146,17 +146,20 @@ export function enablePlugin(plugin: InternalPlugin, late: boolean) {
|
|||
}
|
||||
|
||||
export async function preInitPlugin(plugin: InternalPlugin) {
|
||||
const { manifest, lifecycles } = plugin
|
||||
const {
|
||||
manifest: { id },
|
||||
lifecycles,
|
||||
} = plugin
|
||||
if (!lifecycles.preInit) return
|
||||
|
||||
const meta = _metas.get(manifest.id)!
|
||||
const meta = _metas.get(id)!
|
||||
const [, promises] = meta
|
||||
|
||||
preparePluginPreInit(manifest.id)
|
||||
preparePluginPreInit(plugin)
|
||||
|
||||
if (!(plugin.flags & Flag.Enabled)) throw new Error(`Plugin "${manifest.id}" is not enabled`)
|
||||
if (!(plugin.flags & Flag.Enabled)) throw new Error(`Plugin "${id}" is not enabled`)
|
||||
if (plugin.status & (Status.PreIniting | Status.PreInited))
|
||||
throw new Error(`Plugin preInit lifecycle for "${manifest.id}" is already running`)
|
||||
throw new Error(`Plugin preInit lifecycle for "${id}" is already running`)
|
||||
|
||||
plugin.status |= Status.PreIniting
|
||||
|
||||
|
|
@ -174,18 +177,21 @@ export async function preInitPlugin(plugin: InternalPlugin) {
|
|||
}
|
||||
|
||||
export async function initPlugin(plugin: InternalPlugin) {
|
||||
const { manifest, lifecycles } = plugin
|
||||
const {
|
||||
manifest: { id },
|
||||
lifecycles,
|
||||
} = plugin
|
||||
if (!lifecycles.init) return
|
||||
|
||||
const meta = _metas.get(manifest.id)!
|
||||
const meta = _metas.get(id)!
|
||||
const [, promises, , apiLevel] = meta
|
||||
|
||||
if (!(plugin.flags & Flag.Enabled)) throw new Error(`Plugin "${manifest.id}" is not enabled`)
|
||||
if (!(plugin.flags & Flag.Enabled)) throw new Error(`Plugin "${id}" is not enabled`)
|
||||
if (plugin.status & (Status.Initing | Status.Inited))
|
||||
throw new Error(`Plugin init lifecycle for "${manifest.id}" is already running`)
|
||||
throw new Error(`Plugin init lifecycle for "${id}" is already running`)
|
||||
|
||||
if (apiLevel < PluginApiLevel.PreInit) preparePluginPreInit(manifest.id)
|
||||
if (apiLevel < PluginApiLevel.Init) preparePluginInit(manifest.id)
|
||||
if (apiLevel < PluginApiLevel.PreInit) preparePluginPreInit(plugin)
|
||||
if (apiLevel < PluginApiLevel.Init) preparePluginInit(plugin)
|
||||
|
||||
try {
|
||||
const prom = lifecycles.init(meta[0] as InitPluginApi)
|
||||
|
|
@ -201,21 +207,24 @@ export async function initPlugin(plugin: InternalPlugin) {
|
|||
}
|
||||
|
||||
export async function startPlugin(plugin: InternalPlugin) {
|
||||
const { manifest, lifecycles } = plugin
|
||||
const {
|
||||
manifest: { id },
|
||||
lifecycles,
|
||||
} = plugin
|
||||
if (!lifecycles.start) return
|
||||
|
||||
const meta = _metas.get(manifest.id)!
|
||||
const meta = _metas.get(id)!
|
||||
const [, promises, , apiLevel] = meta
|
||||
|
||||
if (!(plugin.flags & Flag.Enabled)) throw new Error(`Plugin "${manifest.id}" is not enabled`)
|
||||
if (!(plugin.flags & Flag.Enabled)) throw new Error(`Plugin "${id}" is not enabled`)
|
||||
if (plugin.status & (Status.Starting | Status.Started))
|
||||
throw new Error(`Plugin start lifecycle for "${manifest.id}" is already running`)
|
||||
throw new Error(`Plugin start lifecycle for "${id}" is already running`)
|
||||
|
||||
plugin.status |= Status.Starting
|
||||
|
||||
if (apiLevel < PluginApiLevel.PreInit) preparePluginPreInit(manifest.id)
|
||||
if (apiLevel < PluginApiLevel.Init) preparePluginInit(manifest.id)
|
||||
if (apiLevel < PluginApiLevel.Start) preparePluginStart(manifest.id)
|
||||
if (apiLevel < PluginApiLevel.PreInit) preparePluginPreInit(plugin)
|
||||
if (apiLevel < PluginApiLevel.Init) preparePluginInit(plugin)
|
||||
if (apiLevel < PluginApiLevel.Start) preparePluginStart(plugin)
|
||||
|
||||
try {
|
||||
const prom = lifecycles.start(meta[0] as PluginApi)
|
||||
|
|
@ -233,15 +242,17 @@ export async function startPlugin(plugin: InternalPlugin) {
|
|||
const MaxWaitTime = 5000
|
||||
|
||||
export async function stopPlugin(plugin: InternalPlugin) {
|
||||
const { manifest, lifecycles } = plugin
|
||||
const meta = _metas.get(manifest.id)!
|
||||
const {
|
||||
manifest: { id },
|
||||
lifecycles,
|
||||
} = plugin
|
||||
const meta = _metas.get(id)!
|
||||
const [, promises, iflags, apiLevel] = meta
|
||||
|
||||
if (iflags & InternalPluginFlags.Essential)
|
||||
throw new Error(`Plugin "${manifest.id}" is essential and cannot be stopped`)
|
||||
if (iflags & InternalPluginFlags.Essential) throw new Error(`Plugin "${id}" is essential and cannot be stopped`)
|
||||
|
||||
if (!(plugin.flags & Flag.Enabled)) throw new Error(`Plugin "${manifest.id}" is not enabled`)
|
||||
if (plugin.status & Status.Stopping) throw new Error(`Plugin "${manifest.id}" is stopping`)
|
||||
if (!(plugin.flags & Flag.Enabled)) throw new Error(`Plugin "${id}" is not enabled`)
|
||||
if (plugin.status & Status.Stopping) throw new Error(`Plugin "${id}" is stopping`)
|
||||
|
||||
// If the plugin is running its lifecycles, we need to wait for it to finish, then we'll stop it
|
||||
// We want to wait at max 5 seconds for the lifecycles to finish
|
||||
|
|
@ -256,11 +267,11 @@ export async function stopPlugin(plugin: InternalPlugin) {
|
|||
})
|
||||
.catch(e => handlePluginError(e, plugin))
|
||||
else if (!(plugin.status & (Status.PreInited | Status.Inited | Status.Started)))
|
||||
throw new Error(`Plugin "${manifest.id}" is not running`)
|
||||
throw new Error(`Plugin "${id}" is not running`)
|
||||
|
||||
if (apiLevel < PluginApiLevel.PreInit) preparePluginPreInit(manifest.id)
|
||||
if (apiLevel < PluginApiLevel.Init) preparePluginInit(manifest.id, plugin._s)
|
||||
if (apiLevel < PluginApiLevel.Start) preparePluginStart(manifest.id)
|
||||
if (apiLevel < PluginApiLevel.PreInit) preparePluginPreInit(plugin)
|
||||
if (apiLevel < PluginApiLevel.Init) preparePluginInit(plugin, plugin._s)
|
||||
if (apiLevel < PluginApiLevel.Start) preparePluginStart(plugin)
|
||||
|
||||
plugin.status |= Status.Stopping
|
||||
|
||||
|
|
|
|||
|
|
@ -1,41 +0,0 @@
|
|||
export interface PluginApiDiscord {
|
||||
actions: PluginApiDiscordActions
|
||||
common: PluginApiDiscordCommon
|
||||
design: PluginApiDiscordDesign
|
||||
modules: PluginApiDiscordModules
|
||||
native: PluginApiDiscordNative
|
||||
}
|
||||
|
||||
export type PluginApiDiscordActions = typeof import('@revenge-mod/discord/actions')
|
||||
export type PluginApiDiscordCommon = typeof import('@revenge-mod/discord/common')
|
||||
export type PluginApiDiscordDesign = typeof import('@revenge-mod/discord/design')
|
||||
export type PluginApiDiscordNative = typeof import('@revenge-mod/discord/native')
|
||||
|
||||
export interface PluginApiDiscordModules {
|
||||
settings: typeof import('@revenge-mod/discord/modules/settings') & {
|
||||
renderer: typeof import('@revenge-mod/discord/modules/settings/renderer')
|
||||
}
|
||||
}
|
||||
|
||||
export const discord: PluginApiDiscord = {
|
||||
get actions() {
|
||||
return (this.actions = require('@revenge-mod/discord/actions'))
|
||||
},
|
||||
get common() {
|
||||
return (this.common = require('@revenge-mod/discord/common'))
|
||||
},
|
||||
get design() {
|
||||
return (this.design = require('@revenge-mod/discord/design'))
|
||||
},
|
||||
native: require('@revenge-mod/discord/native'),
|
||||
modules: {
|
||||
get settings() {
|
||||
const api = {
|
||||
...require('@revenge-mod/discord/modules/settings'),
|
||||
renderer: require('@revenge-mod/discord/modules/settings/renderer'),
|
||||
}
|
||||
|
||||
return (this.settings = api)
|
||||
},
|
||||
},
|
||||
}
|
||||
|
|
@ -1,3 +1,5 @@
|
|||
import { defineLazyProperty } from '@revenge-mod/utils/objects'
|
||||
|
||||
export interface PluginApiExternals {
|
||||
Browserify: typeof import('@revenge-mod/externals/browserify')
|
||||
ReactNativeClipboard: typeof import('@revenge-mod/externals/react-native-clipboard')
|
||||
|
|
@ -5,17 +7,12 @@ export interface PluginApiExternals {
|
|||
Shopify: typeof import('@revenge-mod/externals/shopify')
|
||||
}
|
||||
|
||||
export const externals: PluginApiExternals = {
|
||||
get Browserify() {
|
||||
return (this.Browserify = require('@revenge-mod/externals/browserify'))
|
||||
},
|
||||
get ReactNativeClipboard() {
|
||||
return (this.ReactNativeClipboard = require('@revenge-mod/externals/react-native-clipboard'))
|
||||
},
|
||||
get ReactNavigation() {
|
||||
return (this.ReactNavigation = require('@revenge-mod/externals/react-navigation'))
|
||||
},
|
||||
get Shopify() {
|
||||
return (this.Shopify = require('@revenge-mod/externals/shopify'))
|
||||
},
|
||||
}
|
||||
export const externals = {} as PluginApiExternals
|
||||
|
||||
// TODO: Rolldown issue: Bug requires us to do { ...require('...') } instead of just require('...')
|
||||
defineLazyProperty(externals, 'Browserify', () => ({ ...require('@revenge-mod/externals/browserify') }))
|
||||
defineLazyProperty(externals, 'ReactNativeClipboard', () => ({
|
||||
...require('@revenge-mod/externals/react-native-clipboard'),
|
||||
}))
|
||||
defineLazyProperty(externals, 'ReactNavigation', () => ({ ...require('@revenge-mod/externals/react-navigation') }))
|
||||
defineLazyProperty(externals, 'Shopify', () => ({ ...require('@revenge-mod/externals/shopify') }))
|
||||
|
|
|
|||
|
|
@ -3,7 +3,6 @@ import { onRunApplication } from '@revenge-mod/react/native'
|
|||
import { _uapi } from './apis'
|
||||
|
||||
import * as AssetsLibrary from '@revenge-mod/assets'
|
||||
import { discord } from './apis/discord'
|
||||
import { externals } from './apis/externals'
|
||||
import { react } from './apis/react'
|
||||
|
||||
|
|
@ -15,7 +14,6 @@ import type { UnscopedInitPluginApi } from './types'
|
|||
// Setup init plugin APIs
|
||||
const uapi = _uapi as UnscopedInitPluginApi
|
||||
uapi.assets = AssetsLibrary
|
||||
uapi.discord = discord
|
||||
uapi.externals = externals
|
||||
uapi.react = react
|
||||
|
||||
|
|
|
|||
|
|
@ -1,11 +1,8 @@
|
|||
import type { PluginFlags, PluginStatus } from './constants'
|
||||
|
||||
import type { DiscordModules } from '@revenge-mod/discord/types'
|
||||
|
||||
import type { Storage, StorageOptions } from '@revenge-mod/storage'
|
||||
import type { AnyObject } from '@revenge-mod/utils/types'
|
||||
|
||||
import type { PluginApiDiscord } from './apis/discord'
|
||||
import type { PluginApiExternals } from './apis/externals'
|
||||
import type { PluginApiModules } from './apis/modules'
|
||||
import type { PluginApiPlugins } from './apis/plugins'
|
||||
|
|
@ -28,7 +25,6 @@ export interface UnscopedPreInitPluginApi {
|
|||
*/
|
||||
export interface UnscopedInitPluginApi extends UnscopedPreInitPluginApi {
|
||||
assets: typeof import('@revenge-mod/assets')
|
||||
discord: PluginApiDiscord
|
||||
externals: PluginApiExternals
|
||||
react: PluginApiReact
|
||||
}
|
||||
|
|
@ -60,7 +56,6 @@ export interface PreInitPluginApi {
|
|||
*/
|
||||
export interface InitPluginApi<S extends AnyObject = AnyObject> extends PreInitPluginApi {
|
||||
unscoped: UnscopedInitPluginApi
|
||||
logger: InstanceType<DiscordModules.Logger>
|
||||
/**
|
||||
* The plugin storage.
|
||||
*
|
||||
|
|
|
|||
|
|
@ -28,6 +28,7 @@ onModuleFirstRequired(IndexModuleId, function onIndexRequired() {
|
|||
onModuleInitialized(IndexModuleId, function onIndexInitialized() {
|
||||
runCatching(() => {
|
||||
// Initialize init libraries
|
||||
require('@revenge-mod/discord/init')
|
||||
|
||||
// Run all init plugins
|
||||
require('~/plugins/init')
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue