fix: plugin API types + JS options validation
This commit is contained in:
parent
a629b2ad44
commit
0fdc1f1cff
4 changed files with 87 additions and 25 deletions
|
|
@ -21,7 +21,12 @@ import {
|
||||||
} from '.'
|
} from '.'
|
||||||
import { pPending } from './dependency-graph'
|
import { pPending } from './dependency-graph'
|
||||||
import { registerRepositoryEvents } from './repositories'
|
import { registerRepositoryEvents } from './repositories'
|
||||||
import type { PluginManifest, PluginOptionsFactory } from '../types'
|
import type {
|
||||||
|
PluginLifecycles,
|
||||||
|
PluginManifest,
|
||||||
|
PluginOptions,
|
||||||
|
PluginOptionsFactory,
|
||||||
|
} from '../types'
|
||||||
import type {
|
import type {
|
||||||
AnyPlugin,
|
AnyPlugin,
|
||||||
PluginError,
|
PluginError,
|
||||||
|
|
@ -221,9 +226,44 @@ export function confirmInstall(
|
||||||
return callNativeMethod('revenge.plugins.confirmInstall', [token, accepted])
|
return callNativeMethod('revenge.plugins.confirmInstall', [token, accepted])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function assertIsFunction(
|
||||||
|
name: string,
|
||||||
|
value: unknown,
|
||||||
|
): asserts value is (...args: any[]) => any {
|
||||||
|
if (typeof value !== 'function')
|
||||||
|
throw new Error(`${name} must be a function, got ${typeof value}`)
|
||||||
|
}
|
||||||
|
|
||||||
function createOptionsFactory(script?: string): PluginOptionsFactory {
|
function createOptionsFactory(script?: string): PluginOptionsFactory {
|
||||||
if (!script) return () => ({})
|
if (!script) return () => ({})
|
||||||
return () => new Function('revenge', `return ${script}`)(pUnscopedApi)
|
|
||||||
|
return () => {
|
||||||
|
const opts = new Function('revenge', 'plugin', `return ${script}`)(
|
||||||
|
pUnscopedApi,
|
||||||
|
// See types.consumers.ts
|
||||||
|
(opts: PluginOptions) => opts,
|
||||||
|
)()
|
||||||
|
if (typeof opts !== 'object' || opts === null)
|
||||||
|
throw new Error('Plugin options must be an object')
|
||||||
|
|
||||||
|
if (
|
||||||
|
opts.SettingsComponent !== undefined &&
|
||||||
|
typeof opts.SettingsComponent !== 'function'
|
||||||
|
)
|
||||||
|
throw new Error(
|
||||||
|
'SettingsComponent must be a function React component',
|
||||||
|
)
|
||||||
|
|
||||||
|
for (const key of ['preInit', 'init', 'start'] as Array<
|
||||||
|
keyof PluginLifecycles
|
||||||
|
>) {
|
||||||
|
if (opts[key] !== undefined) {
|
||||||
|
assertIsFunction(key, opts[key])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return opts
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
declare module '@revenge-mod/modules/native' {
|
declare module '@revenge-mod/modules/native' {
|
||||||
|
|
|
||||||
|
|
@ -9,21 +9,22 @@ import defer * as DiscordUtilsMetroSubscriptions from '@revenge-mod/discord/util
|
||||||
import { defineLazyProperties } from '@revenge-mod/utils/object'
|
import { defineLazyProperties } from '@revenge-mod/utils/object'
|
||||||
import { guardIndexInitialized } from '.'
|
import { guardIndexInitialized } from '.'
|
||||||
|
|
||||||
export interface PluginApiDiscord {
|
export interface PreInitPluginApiDiscord {
|
||||||
actions: PluginApiDiscord.Actions
|
actions: PreInitPluginApiDiscord.Actions
|
||||||
/**
|
/** This API is not available in the `preInit` phase. */
|
||||||
* This submodule is only available after the index module (ID 0) is initialized.
|
common: unknown
|
||||||
* Attempting to access it before then will throw an error.
|
design: PreInitPluginApiDiscord.Design
|
||||||
*/
|
flux: PreInitPluginApiDiscord.Flux
|
||||||
common: PluginApiDiscord.Common
|
modules: PreInitPluginApiDiscord.Modules
|
||||||
design: PluginApiDiscord.Design
|
native: PreInitPluginApiDiscord.Native
|
||||||
flux: PluginApiDiscord.Flux
|
utils: PreInitPluginApiDiscord.Utils
|
||||||
modules: PluginApiDiscord.Modules
|
|
||||||
native: PluginApiDiscord.Native
|
|
||||||
utils: PluginApiDiscord.Utils
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export namespace PluginApiDiscord {
|
export interface InitPluginApiDiscord extends PreInitPluginApiDiscord {
|
||||||
|
common: PreInitPluginApiDiscord.Common
|
||||||
|
}
|
||||||
|
|
||||||
|
export namespace PreInitPluginApiDiscord {
|
||||||
export type Actions = typeof import('@revenge-mod/discord/actions')
|
export type Actions = typeof import('@revenge-mod/discord/actions')
|
||||||
export type Common = typeof import('@revenge-mod/discord/common')
|
export type Common = typeof import('@revenge-mod/discord/common')
|
||||||
export type Design = typeof import('@revenge-mod/discord/design')
|
export type Design = typeof import('@revenge-mod/discord/design')
|
||||||
|
|
@ -46,7 +47,7 @@ export namespace PluginApiDiscord {
|
||||||
|
|
||||||
export const discord = defineLazyProperties(
|
export const discord = defineLazyProperties(
|
||||||
{
|
{
|
||||||
modules: defineLazyProperties({} as PluginApiDiscord.Modules, {
|
modules: defineLazyProperties({} as PreInitPluginApiDiscord.Modules, {
|
||||||
mainTabsV2: () => {
|
mainTabsV2: () => {
|
||||||
return DiscordModulesMainTabsV2
|
return DiscordModulesMainTabsV2
|
||||||
},
|
},
|
||||||
|
|
@ -55,7 +56,7 @@ export const discord = defineLazyProperties(
|
||||||
...require('@revenge-mod/discord/modules/settings/renderer'),
|
...require('@revenge-mod/discord/modules/settings/renderer'),
|
||||||
}),
|
}),
|
||||||
}),
|
}),
|
||||||
utils: defineLazyProperties({} as PluginApiDiscord.Utils, {
|
utils: defineLazyProperties({} as PreInitPluginApiDiscord.Utils, {
|
||||||
finders: () => {
|
finders: () => {
|
||||||
return DiscordUtilsFinders
|
return DiscordUtilsFinders
|
||||||
},
|
},
|
||||||
|
|
@ -63,7 +64,7 @@ export const discord = defineLazyProperties(
|
||||||
subscriptions: DiscordUtilsMetroSubscriptions,
|
subscriptions: DiscordUtilsMetroSubscriptions,
|
||||||
}),
|
}),
|
||||||
}),
|
}),
|
||||||
} as PluginApiDiscord,
|
} as PreInitPluginApiDiscord,
|
||||||
{
|
{
|
||||||
actions: () => {
|
actions: () => {
|
||||||
return DiscordActions
|
return DiscordActions
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,8 @@
|
||||||
import type { FunctionComponent } from 'react'
|
import type { FunctionComponent } from 'react'
|
||||||
import type { PluginApiDiscord } from './apis/discord'
|
import type {
|
||||||
|
InitPluginApiDiscord,
|
||||||
|
PreInitPluginApiDiscord,
|
||||||
|
} from './apis/discord'
|
||||||
import type { PluginApiExternals } from './apis/externals'
|
import type { PluginApiExternals } from './apis/externals'
|
||||||
import type { PluginApiModules } from './apis/modules'
|
import type { PluginApiModules } from './apis/modules'
|
||||||
import type { PluginApiPlugins } from './apis/plugins'
|
import type { PluginApiPlugins } from './apis/plugins'
|
||||||
|
|
@ -23,8 +26,9 @@ export interface UnscopedPreInitPluginApi<
|
||||||
react: PluginApiReact
|
react: PluginApiReact
|
||||||
assets: typeof import('@revenge-mod/assets')
|
assets: typeof import('@revenge-mod/assets')
|
||||||
externals: PluginApiExternals
|
externals: PluginApiExternals
|
||||||
components: typeof import('@revenge-mod/components')
|
/** This API is not available in the `preInit` phase. */
|
||||||
discord: PluginApiDiscord
|
components: unknown
|
||||||
|
discord: PreInitPluginApiDiscord
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -33,7 +37,10 @@ export interface UnscopedPreInitPluginApi<
|
||||||
*/
|
*/
|
||||||
export interface UnscopedInitPluginApi<
|
export interface UnscopedInitPluginApi<
|
||||||
O extends PluginApiExtensionsOptions = PluginApiExtensionsOptions,
|
O extends PluginApiExtensionsOptions = PluginApiExtensionsOptions,
|
||||||
> extends UnscopedPreInitPluginApi<O> {}
|
> extends UnscopedPreInitPluginApi<O> {
|
||||||
|
components: typeof import('@revenge-mod/components')
|
||||||
|
discord: InitPluginApiDiscord
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The unscoped plugin API. This API is available as a global for plugins.
|
* The unscoped plugin API. This API is available as a global for plugins.
|
||||||
|
|
|
||||||
|
|
@ -1,14 +1,28 @@
|
||||||
import type {
|
import type {
|
||||||
PluginApiExtensionsOptions,
|
PluginApiExtensionsOptions,
|
||||||
PluginManifest,
|
|
||||||
PluginOptions,
|
PluginOptions,
|
||||||
|
UnscopedInitPluginApi,
|
||||||
|
UnscopedPluginApi,
|
||||||
|
UnscopedPreInitPluginApi,
|
||||||
} from '@revenge-mod/plugins/types'
|
} from '@revenge-mod/plugins/types'
|
||||||
|
|
||||||
declare global {
|
declare global {
|
||||||
|
/**
|
||||||
|
* Defines an entrypoint for a Revenge JS plugin.
|
||||||
|
*/
|
||||||
export function plugin<O extends PluginApiExtensionsOptions>(
|
export function plugin<O extends PluginApiExtensionsOptions>(
|
||||||
manifest: PluginManifest,
|
|
||||||
options: PluginOptions<O>,
|
options: PluginOptions<O>,
|
||||||
): void
|
): PluginOptions<O>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The unscoped plugin API. **Use directly only when necessary.**
|
||||||
|
*
|
||||||
|
* You should be using the `unscoped` property of the `PluginApi` object instead.
|
||||||
|
*/
|
||||||
|
export const revenge:
|
||||||
|
| UnscopedPreInitPluginApi
|
||||||
|
| UnscopedInitPluginApi
|
||||||
|
| UnscopedPluginApi
|
||||||
}
|
}
|
||||||
|
|
||||||
export * from './globals'
|
export * from './globals'
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue