mirror of
https://github.com/the1812/Bilibili-Evolved.git
synced 2025-11-04 21:22:45 +08:00
Merge pull request #3195 from timongh/change-code-loading
改进代码字符串的加载方式2
This commit is contained in:
commit
e651e33863
@ -1,10 +1,10 @@
|
||||
import {
|
||||
addComponentListener,
|
||||
getComponentSettings,
|
||||
getGeneralSettings,
|
||||
isComponentEnabled,
|
||||
getComponentSettings,
|
||||
} from '@/core/settings'
|
||||
import { fullyLoaded, contentLoaded } from '@/core/life-cycle'
|
||||
import { contentLoaded, fullyLoaded } from '@/core/life-cycle'
|
||||
import { LoadingMode } from '@/core/loading-mode'
|
||||
import { Widget } from '@/components/widget'
|
||||
import { ComponentMetadata } from './types'
|
||||
@ -26,7 +26,10 @@ const loadI18n = async (component: ComponentMetadata) => {
|
||||
}
|
||||
const { addI18nData } = await import('@/components/i18n/helpers')
|
||||
Object.entries(component.i18n).forEach(([language, data]) => {
|
||||
const { map = [], regex = [] } = data
|
||||
const {
|
||||
map = [],
|
||||
regex = [],
|
||||
} = data
|
||||
addI18nData(language, map, regex)
|
||||
})
|
||||
}
|
||||
@ -35,14 +38,20 @@ const getComponentWidgetName = (component: ComponentMetadata) => `${component.na
|
||||
/** 提取组件中的Widget */
|
||||
const loadWidget = async (component: ComponentMetadata) => {
|
||||
if (component.widget) {
|
||||
const widget: Widget = { ...component.widget, name: getComponentWidgetName(component) }
|
||||
const widget: Widget = {
|
||||
...component.widget,
|
||||
name: getComponentWidgetName(component),
|
||||
}
|
||||
const { addData } = await import('@/plugins/data')
|
||||
const { WidgetsPlugin } = await import('./settings-panel')
|
||||
addData(WidgetsPlugin, (widgets: Widget[]) => {
|
||||
if (widgets.find(w => w.name === widget.name)) {
|
||||
return
|
||||
}
|
||||
const { urlInclude, urlExclude } = widget
|
||||
const {
|
||||
urlInclude,
|
||||
urlExclude,
|
||||
} = widget
|
||||
if (component.urlInclude) {
|
||||
if (urlInclude) {
|
||||
urlInclude.push(...component.urlInclude)
|
||||
@ -66,7 +75,7 @@ const loadedComponents: Record<string, Record<string, any>> = {}
|
||||
/** 获取已加载组件的导出内容
|
||||
* @param name 组件名称
|
||||
*/
|
||||
export const importComponent = <ComponentExportType> (
|
||||
export const importComponent = <ComponentExportType>(
|
||||
name: string,
|
||||
): Record<string, ComponentExportType> => {
|
||||
if (!(name in loadedComponents)) {
|
||||
@ -137,12 +146,17 @@ export const loadComponent = async (component: ComponentMetadata) => {
|
||||
/** 加载所有用户组件的定义 (不运行) */
|
||||
export const loadAllUserComponents = async () => {
|
||||
const { settings } = await import('@/core/settings')
|
||||
const { batchParseCode } = await import('@/core/external-input')
|
||||
const {
|
||||
loadFeaturesFromCodes,
|
||||
FeatureKind,
|
||||
} = await import('@/core/external-input/load-features-from-codes')
|
||||
const loadUserComponent = (component: ComponentMetadata) => {
|
||||
components.push(component)
|
||||
componentsMap[component.name] = component
|
||||
}
|
||||
const userComponents = await batchParseCode<ComponentMetadata>(
|
||||
const userComponents = await loadFeaturesFromCodes(
|
||||
FeatureKind.Component,
|
||||
Object.keys(settings.userComponents),
|
||||
Object.values(settings.userComponents).map(it => it.code),
|
||||
)
|
||||
userComponents.forEach(loadUserComponent)
|
||||
@ -153,12 +167,12 @@ export const loadAllComponents = async () => {
|
||||
const { loadAllPlugins } = await import('@/plugins/plugin')
|
||||
const loadComponents = () => loadAllPlugins(components)
|
||||
.then(() => Promise.allSettled(components.map(loadI18n)))
|
||||
.then(() => Promise.allSettled(components.map(loadComponent)))
|
||||
.then(async () => {
|
||||
.then(() => Promise.allSettled(components.map(loadComponent))).then(async () => {
|
||||
if (generalSettings.devMode) {
|
||||
const { componentLoadTime, componentResolveTime } = await import(
|
||||
'@/core/performance/component-trace'
|
||||
)
|
||||
const {
|
||||
componentLoadTime,
|
||||
componentResolveTime,
|
||||
} = await import('@/core/performance/component-trace')
|
||||
const { logStats } = await import('@/core/performance/stats')
|
||||
logStats('components block', componentLoadTime)
|
||||
logStats('components resolve', componentResolveTime)
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import { pickFile } from './file-picker'
|
||||
import { pickFile } from '@/core/file-picker'
|
||||
|
||||
/** 外部输入包装类型, 详见`parseExternalInput`的文档 */
|
||||
export type ExternalInput<T> = undefined | string | T
|
||||
@ -54,22 +54,3 @@ export const parseExternalInput = async <T>(input: ExternalInput<T>): Promise<T>
|
||||
return input
|
||||
}
|
||||
}
|
||||
export const batchParseCode = async<T>(inputs: string[]): Promise<T[]> => {
|
||||
try {
|
||||
const exports = {}
|
||||
const result = inputs.map(input => eval(input) as T)
|
||||
if (Object.values(exports).length > 0) {
|
||||
const { coreApis } = await import('@/core/core-apis')
|
||||
return Object.values(exports).map(value => {
|
||||
if (typeof value === 'function') {
|
||||
return value(coreApis) as T
|
||||
}
|
||||
return value as T
|
||||
})
|
||||
}
|
||||
return result
|
||||
} catch (error) {
|
||||
console.error(error)
|
||||
return null
|
||||
}
|
||||
}
|
||||
31
src/core/external-input/load-feature-code-all-settled.ts
Normal file
31
src/core/external-input/load-feature-code-all-settled.ts
Normal file
@ -0,0 +1,31 @@
|
||||
import {
|
||||
loadFeatureCode, LoadFeatureCodeResult,
|
||||
} from '@/core/external-input/load-feature-code'
|
||||
|
||||
type LdRes<X> = LoadFeatureCodeResult<X>
|
||||
type SettledRes<T> = PromiseSettledResult<T>
|
||||
type FilledRes<T> = PromiseFulfilledResult<T>
|
||||
|
||||
const unwrapSettledResult = <T>(r: SettledRes<T>): T => (r as FilledRes<T>).value
|
||||
|
||||
const mapSettledArray = <T>(arr: SettledRes<T>[]): T[] => (
|
||||
arr.map(unwrapSettledResult)
|
||||
)
|
||||
|
||||
const mapSettleResult = <T>(p: Promise<SettledRes<T>[]>): Promise<T[]> => (
|
||||
p.then(mapSettledArray)
|
||||
)
|
||||
|
||||
/**
|
||||
* 批量加载组件或插件的代码字符串,获取其导出 feature
|
||||
*
|
||||
* @param codes 代码字符串数组
|
||||
* @returns 不会失败的 `Promise`。其结果为一个数组,其中每个元素都是代表代码执行结果的对象
|
||||
*/
|
||||
export const loadFeatureCodeAllSettled = <X>(
|
||||
codes: string[],
|
||||
): Promise<LoadFeatureCodeResult<X>[]> => lodash(codes)
|
||||
.map<Promise<LdRes<X>>>(loadFeatureCode)
|
||||
.thru<Promise<SettledRes<LdRes<X>>[]>>(arr => Promise.allSettled(arr))
|
||||
.thru(mapSettleResult)
|
||||
.value()
|
||||
164
src/core/external-input/load-feature-code-all.ts
Normal file
164
src/core/external-input/load-feature-code-all.ts
Normal file
@ -0,0 +1,164 @@
|
||||
import {
|
||||
loadFeatureCode, LoadFeatureCodeResult, LoadFeatureCodeResultError,
|
||||
LoadFeatureCodeResultOk,
|
||||
} from '@/core/external-input/load-feature-code'
|
||||
import { FeatureBase } from '@/components/types'
|
||||
|
||||
interface ResultInstance {
|
||||
readonly isOk: <X extends FeatureBase>(
|
||||
this: LoadFeatureCodeAllResult<X>
|
||||
) => this is LoadFeatureCodeAllResultOk<X>
|
||||
|
||||
readonly isError: (
|
||||
this: LoadFeatureCodeAllResult<FeatureBase>
|
||||
) => this is LoadFeatureCodeAllResultError
|
||||
|
||||
readonly isNoExport: (
|
||||
this: LoadFeatureCodeAllResult<FeatureBase>
|
||||
) => this is LoadFeatureCodeAllResultNoExport
|
||||
|
||||
readonly isCodeThrew: (
|
||||
this: LoadFeatureCodeAllResult<FeatureBase>
|
||||
) => this is LoadFeatureCodeAllResultCodeThrew
|
||||
}
|
||||
|
||||
/**
|
||||
* 成功从代码中获取 features
|
||||
*
|
||||
* @namespace
|
||||
* @property features 从代码中获取的导出值
|
||||
*/
|
||||
interface LoadFeatureCodeAllResultOk<X extends FeatureBase> extends ResultInstance {
|
||||
readonly tag: 'Ok',
|
||||
readonly features: X[],
|
||||
}
|
||||
|
||||
/** 代码没有导出任何值 */
|
||||
interface LoadFeatureCodeAllResultNoExport extends ResultInstance {
|
||||
readonly tag: 'NoExport',
|
||||
}
|
||||
|
||||
/**
|
||||
* 执行代码过程中产生了抛出值。
|
||||
*
|
||||
* @namespace
|
||||
* @property thrown 抛出的值
|
||||
*/
|
||||
interface LoadFeatureCodeAllResultCodeThrew extends ResultInstance {
|
||||
readonly tag: 'CodeThrew',
|
||||
readonly thrown: unknown,
|
||||
}
|
||||
|
||||
type LoadFeatureCodeAllResultError =
|
||||
LoadFeatureCodeAllResultNoExport
|
||||
| LoadFeatureCodeAllResultCodeThrew
|
||||
type LoadFeatureCodeAllResult<X extends FeatureBase> =
|
||||
LoadFeatureCodeAllResultOk<X>
|
||||
| LoadFeatureCodeAllResultError
|
||||
|
||||
const resultProto: ResultInstance = {
|
||||
isOk() { return this.tag === 'Ok' },
|
||||
isError() { return this.tag !== 'Ok' },
|
||||
isNoExport() { return this.tag === 'NoExport' },
|
||||
isCodeThrew() { return this.tag === 'CodeThrew' },
|
||||
}
|
||||
|
||||
const okResult = <X extends FeatureBase>(
|
||||
features: X[],
|
||||
): LoadFeatureCodeAllResultOk<X> => lodash.create(
|
||||
resultProto,
|
||||
{
|
||||
tag: 'Ok' as const,
|
||||
features,
|
||||
},
|
||||
)
|
||||
|
||||
const noExportResult = lodash.create(resultProto, {
|
||||
tag: 'NoExport' as const,
|
||||
})
|
||||
|
||||
const codeThrewResult = (thrown: unknown): LoadFeatureCodeAllResultCodeThrew => lodash.create(
|
||||
resultProto,
|
||||
{
|
||||
tag: 'CodeThrew' as const,
|
||||
thrown,
|
||||
},
|
||||
)
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
type Task<Ok, Err = never> = Promise<Ok>
|
||||
|
||||
type LdRes<X> = LoadFeatureCodeResult<X>
|
||||
type LdOk<X> = LoadFeatureCodeResultOk<X>
|
||||
type LdErr = LoadFeatureCodeResultError
|
||||
|
||||
type LdAllRes<X> = LoadFeatureCodeAllResult<X>
|
||||
type LdAllOk<X> = LoadFeatureCodeAllResultOk<X>
|
||||
type LdAllErr = LoadFeatureCodeAllResultError
|
||||
|
||||
type LoadCodesTask<X> = Task<LdOk<X>[], [number, LdErr]>
|
||||
|
||||
// covert `Task<LdRes<X>>` to `Task<LdOk<X>, LdErr>`
|
||||
const rejectErrorResult = <X>(t: Task<LdRes<X>>): Task<LdOk<X>, LdErr> => (
|
||||
t.then(r => (r.isOk() ? r : Promise.reject(r)))
|
||||
)
|
||||
|
||||
// load feature code, and return `Task<LdOk<X>, LdErr>`
|
||||
const loadCode = <X>(code: string): Task<LdOk<X>, LdErr> => (
|
||||
rejectErrorResult(loadFeatureCode(code))
|
||||
)
|
||||
|
||||
// covert `Task`'s `Err` type from `T` to `[number, T]`
|
||||
const addIndexToRejected = <N extends number, O, E>(
|
||||
t: Task<O, E>,
|
||||
i: N,
|
||||
// eslint-disable-next-line prefer-promise-reject-errors
|
||||
): Task<O, [N, E]> => t.catch(e => Promise.reject([i, e]))
|
||||
|
||||
// create `LdAllOk` from an array of `LdOk`
|
||||
const createOkResult = <X>(arr: LdOk<X>[]): LdAllOk<X> => (
|
||||
okResult(arr.map(r => r.feature))
|
||||
)
|
||||
|
||||
// create `LdAllErr` from `[number, LdErr]`
|
||||
const createErrResult = (t: [number, LdErr]): LdAllErr => (
|
||||
t[1].isCodeThrew()
|
||||
? codeThrewResult(t[1].thrown)
|
||||
: noExportResult
|
||||
)
|
||||
|
||||
// load all feature codes, and return `LoadCodesTask`
|
||||
const loadCodes = <X>(codes: string[]): LoadCodesTask<X> => lodash(codes)
|
||||
.map<Task<LdOk<X>, LdErr>>(loadCode)
|
||||
.map(addIndexToRejected)
|
||||
.thru<LoadCodesTask<X>>(arr => Promise.all(arr))
|
||||
.value()
|
||||
|
||||
// create a `LdAllRes` wrapped by `Task`
|
||||
const createTaskResult = <X>(t: LoadCodesTask<X>): Task<LdAllRes<X>> => t
|
||||
.then(createOkResult)
|
||||
.catch(createErrResult)
|
||||
|
||||
/**
|
||||
* 批量加载组件或插件的代码字符串,获取其导出 feature
|
||||
*
|
||||
* 只要有一个代码出现了错误,则返回错误。
|
||||
*
|
||||
* @param codes 代码字符串数组
|
||||
* @returns 一个不会失败的 `Promise`,其结果值为 `LoadFeatureCodeAllResult`
|
||||
*/
|
||||
const loadFeatureCodeAll = <X>(codes: string[]): Promise<LoadFeatureCodeAllResult<X>> => (
|
||||
lodash(codes)
|
||||
.thru<LoadCodesTask<X>>(loadCodes)
|
||||
.thru(createTaskResult)
|
||||
.value()
|
||||
)
|
||||
|
||||
export {
|
||||
loadFeatureCodeAll,
|
||||
LoadFeatureCodeAllResult,
|
||||
LoadFeatureCodeAllResultOk,
|
||||
LoadFeatureCodeAllResultError,
|
||||
LoadFeatureCodeAllResultNoExport,
|
||||
LoadFeatureCodeAllResultCodeThrew,
|
||||
}
|
||||
112
src/core/external-input/load-feature-code.ts
Normal file
112
src/core/external-input/load-feature-code.ts
Normal file
@ -0,0 +1,112 @@
|
||||
import { FeatureBase } from '@/components/types'
|
||||
|
||||
interface ResultInstance {
|
||||
readonly isOk: <X extends FeatureBase>(
|
||||
this: LoadFeatureCodeResult<X>
|
||||
) => this is LoadFeatureCodeResultOk<X>
|
||||
|
||||
readonly isError: (
|
||||
this: LoadFeatureCodeResult<FeatureBase>
|
||||
) => this is LoadFeatureCodeResultError
|
||||
|
||||
readonly isNoExport: (
|
||||
this: LoadFeatureCodeResult<FeatureBase>
|
||||
) => this is LoadFeatureCodeResultNoExport
|
||||
|
||||
readonly isCodeThrew: (
|
||||
this: LoadFeatureCodeResult<FeatureBase>
|
||||
) => this is LoadFeatureCodeResultCodeThrew
|
||||
}
|
||||
|
||||
/**
|
||||
* 成功从代码中获取 feature
|
||||
*
|
||||
* @namespace
|
||||
* @property feature 从代码中获取的导出值
|
||||
*/
|
||||
interface LoadFeatureCodeResultOk<X extends FeatureBase> extends ResultInstance {
|
||||
readonly tag: 'Ok',
|
||||
readonly feature: X,
|
||||
}
|
||||
|
||||
/** 代码没有导出任何值 */
|
||||
interface LoadFeatureCodeResultNoExport extends ResultInstance {
|
||||
readonly tag: 'NoExport',
|
||||
}
|
||||
|
||||
/**
|
||||
* 执行代码过程中产生了抛出值。
|
||||
*
|
||||
* @namespace
|
||||
* @property thrown 抛出的值
|
||||
*/
|
||||
interface LoadFeatureCodeResultCodeThrew extends ResultInstance {
|
||||
readonly tag: 'CodeThrew',
|
||||
readonly thrown: unknown,
|
||||
}
|
||||
|
||||
type LoadFeatureCodeResultError =
|
||||
LoadFeatureCodeResultNoExport
|
||||
| LoadFeatureCodeResultCodeThrew
|
||||
type LoadFeatureCodeResult<X extends FeatureBase> =
|
||||
LoadFeatureCodeResultOk<X>
|
||||
| LoadFeatureCodeResultError
|
||||
|
||||
const resultProto: ResultInstance = {
|
||||
isOk() { return this.tag === 'Ok' },
|
||||
isError() { return this.tag !== 'Ok' },
|
||||
isNoExport() { return this.tag === 'NoExport' },
|
||||
isCodeThrew() { return this.tag === 'CodeThrew' },
|
||||
}
|
||||
|
||||
const okResult = <X extends FeatureBase>(feature: X): LoadFeatureCodeResultOk<X> => lodash.create(
|
||||
resultProto,
|
||||
{
|
||||
tag: 'Ok' as const,
|
||||
feature,
|
||||
},
|
||||
)
|
||||
|
||||
const noExportResult = lodash.create(resultProto, {
|
||||
tag: 'NoExport' as const,
|
||||
})
|
||||
|
||||
const codeThrewResult = (thrown: unknown): LoadFeatureCodeResultCodeThrew => lodash.create(
|
||||
resultProto,
|
||||
{
|
||||
tag: 'CodeThrew' as const,
|
||||
thrown,
|
||||
},
|
||||
)
|
||||
|
||||
/**
|
||||
* 加载组件或插件的代码字符串,获取其导出 feature
|
||||
*
|
||||
* @param code 代码字符串
|
||||
* @returns 一个不会失败的 `Promise`,其结果值为 {@link LoadFeatureCodeResult}
|
||||
*/
|
||||
const loadFeatureCode = async <X extends FeatureBase>(
|
||||
code: string,
|
||||
): Promise<LoadFeatureCodeResult<X>> => {
|
||||
// 收集代码导出值
|
||||
const exports = {}
|
||||
try {
|
||||
eval(code)
|
||||
} catch (thrown) {
|
||||
return codeThrewResult(thrown)
|
||||
}
|
||||
const values = Object.values(exports)
|
||||
if (values.length === 0) {
|
||||
return noExportResult
|
||||
}
|
||||
return okResult(values[0] as X)
|
||||
}
|
||||
|
||||
export {
|
||||
loadFeatureCode,
|
||||
LoadFeatureCodeResult,
|
||||
LoadFeatureCodeResultOk,
|
||||
LoadFeatureCodeResultError,
|
||||
LoadFeatureCodeResultNoExport,
|
||||
LoadFeatureCodeResultCodeThrew,
|
||||
}
|
||||
124
src/core/external-input/load-features-from-codes.ts
Normal file
124
src/core/external-input/load-features-from-codes.ts
Normal file
@ -0,0 +1,124 @@
|
||||
import {
|
||||
LoadFeatureCodeResultError, LoadFeatureCodeResultOk,
|
||||
} from '@/core/external-input/load-feature-code'
|
||||
import { Toast } from '@/core/toast'
|
||||
import { useScopedConsole } from '@/core/utils/log'
|
||||
import { ComponentMetadata } from '@/components/types'
|
||||
import { PluginMetadata } from '@/plugins/plugin'
|
||||
import {
|
||||
loadFeatureCodeAllSettled,
|
||||
} from '@/core/external-input/load-feature-code-all-settled'
|
||||
|
||||
const curConsole = useScopedConsole(
|
||||
'@/core/external-input/load-features-from-codes.ts',
|
||||
)
|
||||
|
||||
export enum FeatureKind {
|
||||
Component = 'Component',
|
||||
Plugin = 'Plugin',
|
||||
}
|
||||
|
||||
const logError = (kind: FeatureKind): (
|
||||
featureName: string, err: LoadFeatureCodeResultError) => void => {
|
||||
const prefix = kind === FeatureKind.Component ? 'component' : 'plugin'
|
||||
return (featureName, err) => {
|
||||
if (err.isNoExport()) {
|
||||
curConsole.error(
|
||||
`${prefix} '${featureName}' exports no value, failed to load`,
|
||||
)
|
||||
} else {
|
||||
curConsole.error(
|
||||
`${prefix} '${featureName}' throws something when importing, failed to load`,
|
||||
{ thrown: err.thrown },
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const reportErrToUser = (
|
||||
featureKind: FeatureKind,
|
||||
errNames: string[],
|
||||
): void => {
|
||||
type ErrInfo = number | string[]
|
||||
|
||||
const emptyErrInfo: () => string[] = () => []
|
||||
|
||||
const accErrInfo = (acc: ErrInfo, featureName: string): ErrInfo => {
|
||||
if (Array.isArray(acc)) {
|
||||
if (acc.length < 3) {
|
||||
acc.push(featureName)
|
||||
return acc
|
||||
}
|
||||
return 4
|
||||
}
|
||||
return acc + 1
|
||||
}
|
||||
|
||||
const reportErrInfo = (kind: FeatureKind, info: ErrInfo): void => {
|
||||
const kindName = kind === FeatureKind.Component ? '组件' : '插件'
|
||||
if (Array.isArray(info)) {
|
||||
Toast.error(
|
||||
`${kindName} "${info.join('", "')}" 加载失败。请向我们反馈,以解决此问题。`,
|
||||
`${kindName}加载失败`,
|
||||
)
|
||||
} else {
|
||||
Toast.error(
|
||||
`有 ${info} 个${kindName}加载失败,请向我们反馈,以解决此问题。`,
|
||||
`${kindName}加载失败`,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
const errInfo = errNames.reduce(accErrInfo, emptyErrInfo())
|
||||
reportErrInfo(featureKind, errInfo)
|
||||
}
|
||||
|
||||
export type FeatureMetadata = ComponentMetadata | PluginMetadata
|
||||
|
||||
/**
|
||||
* 批量加载组件或插件代码
|
||||
*
|
||||
* 如果遇到错误会向 console 和用户输出错误信息
|
||||
*
|
||||
* `names` 和 `codes` 应该是一一对应的
|
||||
*
|
||||
* @param kind 组件或插件类型
|
||||
* @param names 组件或插件名称
|
||||
* @param codes 组件或插件代码
|
||||
* @return 返回加载成功的组件或插件
|
||||
*/
|
||||
export async function loadFeaturesFromCodes(
|
||||
kind: FeatureKind.Component,
|
||||
names: string[],
|
||||
codes: string[],
|
||||
): Promise<ComponentMetadata[]>
|
||||
export async function loadFeaturesFromCodes(
|
||||
kind: FeatureKind.Plugin,
|
||||
names: string[],
|
||||
codes: string[],
|
||||
): Promise<PluginMetadata[]>
|
||||
export async function loadFeaturesFromCodes(
|
||||
kind: FeatureKind,
|
||||
names: string[],
|
||||
codes: string[],
|
||||
): Promise<FeatureMetadata[]> {
|
||||
const results = await loadFeatureCodeAllSettled<FeatureMetadata>(codes)
|
||||
const [namedOk, namedErr] = lodash(results)
|
||||
.map((r, i) => [names[i], r] as const)
|
||||
.partition(([, r]) => r.isOk())
|
||||
.value()
|
||||
|
||||
// 输出日志
|
||||
lodash.forEach(namedErr, lodash.spread(logError(kind)))
|
||||
|
||||
// 向用户输出错误报告
|
||||
if (namedErr.length > 0) {
|
||||
const errNames = namedErr.map(([name]) => name)
|
||||
reportErrToUser(kind, errNames)
|
||||
}
|
||||
|
||||
return lodash.map(
|
||||
namedOk,
|
||||
([, r]) => (r as LoadFeatureCodeResultOk<FeatureMetadata>).feature,
|
||||
)
|
||||
}
|
||||
@ -1,7 +1,7 @@
|
||||
import { ComponentMetadata, FeatureBase } from '@/components/component'
|
||||
import { deleteValue } from '@/core/utils'
|
||||
import { CoreApis } from '../core/core-apis'
|
||||
import { addData, registerData, registerAndGetData } from './data'
|
||||
import { CoreApis } from '@/core/core-apis'
|
||||
import { addData, registerAndGetData, registerData } from './data'
|
||||
import { addHook, getHook } from './hook'
|
||||
|
||||
/** 插件初始化时的传入参数, 可以解构并调用 */
|
||||
@ -23,6 +23,7 @@ export interface PluginMinimalData extends FeatureBase {
|
||||
/** 显示名称, 默认同插件名称 */
|
||||
displayName?: string
|
||||
}
|
||||
|
||||
type PartialRequired<Target, Props extends keyof Target> = Target & {
|
||||
[P in Props]-?: Target[P]
|
||||
}
|
||||
@ -48,7 +49,7 @@ export const plugins: PluginMetadata[] = getBuiltInPlugins()
|
||||
|
||||
/**
|
||||
* 安装插件
|
||||
* @param input 插件数据
|
||||
* @param code 插件代码
|
||||
*/
|
||||
export const installPlugin = async (code: string) => {
|
||||
const { parseExternalInput } = await import('../core/external-input')
|
||||
@ -95,13 +96,11 @@ export const installPlugin = async (code: string) => {
|
||||
*/
|
||||
export const uninstallPlugin = async (nameOrDisplayName: string) => {
|
||||
const { settings } = await import('@/core/settings')
|
||||
const existingPlugin = Object.entries(settings.userPlugins)
|
||||
.find(([name, { displayName }]) => {
|
||||
if (name === nameOrDisplayName || displayName === nameOrDisplayName) {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
})
|
||||
const existingPlugin = Object.entries(settings.userPlugins).find(
|
||||
([name, { displayName }]) => (
|
||||
name === nameOrDisplayName || displayName === nameOrDisplayName
|
||||
),
|
||||
)
|
||||
if (!existingPlugin) {
|
||||
throw new Error(`没有找到与名称'${nameOrDisplayName}'相关联的插件`)
|
||||
}
|
||||
@ -155,20 +154,33 @@ export const loadPlugin = async (plugin: PluginMetadata) => {
|
||||
* @param components 组件列表
|
||||
*/
|
||||
export const loadAllPlugins = async (components: ComponentMetadata[]) => {
|
||||
const { settings, getGeneralSettings } = await import('@/core/settings')
|
||||
const { batchParseCode } = await import('@/core/external-input')
|
||||
const otherPlugins = components
|
||||
const {
|
||||
settings,
|
||||
getGeneralSettings,
|
||||
} = await import('@/core/settings')
|
||||
const {
|
||||
loadFeaturesFromCodes,
|
||||
FeatureKind,
|
||||
} = await import('@/core/external-input/load-features-from-codes')
|
||||
const otherPlugins = lodash(components)
|
||||
.map(extractPluginFromComponent)
|
||||
.filter(p => p !== null)
|
||||
.concat(await batchParseCode<PluginMetadata>(
|
||||
.map(p => p as PluginMetadata)
|
||||
.concat(await loadFeaturesFromCodes(
|
||||
FeatureKind.Plugin,
|
||||
Object.keys(settings.userPlugins),
|
||||
Object.values(settings.userPlugins).map(p => p.code),
|
||||
))
|
||||
.value()
|
||||
plugins.push(...otherPlugins)
|
||||
return Promise.allSettled(
|
||||
plugins.map(loadPlugin),
|
||||
).then(async () => {
|
||||
if (getGeneralSettings().devMode) {
|
||||
const { pluginLoadTime, pluginResolveTime } = await import('@/core/performance/plugin-trace')
|
||||
const {
|
||||
pluginLoadTime,
|
||||
pluginResolveTime,
|
||||
} = await import('@/core/performance/plugin-trace')
|
||||
const { logStats } = await import('@/core/performance/stats')
|
||||
logStats('plugins block', pluginLoadTime)
|
||||
logStats('plugins resolve', pluginResolveTime)
|
||||
|
||||
Loading…
Reference in New Issue
Block a user