Files
RawMark/web/src/common/plugins/mount.ts
T

56 lines
1.6 KiB
TypeScript

import type { AsyncCleanup, Awaitable, MountContext } from '@@/plugin-api'
import { t as translate } from '@@/i18n/runtime'
// 每次挂载使用独立容器;超时或停用后的异步渲染只能修改已脱离页面的容器。
export function mountContent(
parent: HTMLElement,
mount: (context: MountContext) => Awaitable<void | AsyncCleanup>,
fail: (error: unknown) => void,
): () => void {
const controller = new AbortController()
const container = document.createElement('div')
parent.replaceChildren(container)
let cleanup: void | AsyncCleanup
let stopped = false
let pending = true
const runCleanup = (fn: AsyncCleanup) => {
try {
void Promise.resolve(fn()).catch(fail)
} catch (error) {
fail(error)
}
}
const stop = () => {
if (stopped) return
stopped = true
controller.abort()
container.remove()
if (cleanup) runCleanup(cleanup)
clearTimeout(timer)
}
const timer = setTimeout(() => {
if (!pending || stopped) return
stop()
fail(new Error(translate('common.plugin_view_rendering_exceeded_seconds_0d20d2')))
}, 10_000)
void Promise.resolve().then(() => {
if (!stopped) return mount({ container, signal: controller.signal })
}).then((result) => {
pending = false
clearTimeout(timer)
if (result !== undefined && typeof result !== 'function') throw new Error(translate('common.plugin_mount_must_return_a_c66e82'))
if (stopped) {
if (result) runCleanup(result)
} else {
cleanup = result
}
}).catch((error) => {
pending = false
clearTimeout(timer)
if (!stopped) {
stop(); fail(error)
}
})
return stop
}