mirror of
https://github.com/zhw2590582/ArtPlayer.git
synced 2026-10-22 05:11:20 -08:00
refactor(core): CORE-08 type plugin registration and guard closed instances
This commit is contained in:
1 parent
f9707bad71
commit
874dac9f22
36 files changed
+2865
-142
No files matched your search
@@ -0,0 +1,22 @@
|
||||
import type { BuiltinRegistry, PluginHost } from './types'
|
||||
import { isClosing } from '../lifecycle/instance'
|
||||
import { isMobile } from '../utils/compatibility'
|
||||
import autoOrientation from './autoOrientation'
|
||||
import autoPlayback from './autoPlayback'
|
||||
import fastForward from './fastForward'
|
||||
import lock from './lock'
|
||||
import miniProgressBar from './miniProgressBar'
|
||||
|
||||
export default function installBuiltins<Host extends PluginHost<Host>>(registry: BuiltinRegistry<Host>, option: PluginHost<Host>['option']): void {
|
||||
// Read each condition at its original turn; earlier plugins may change options.
|
||||
if (!isClosing(registry.art) && option.miniProgressBar && !option.isLive)
|
||||
registry.add(miniProgressBar)
|
||||
if (!isClosing(registry.art) && option.lock && isMobile)
|
||||
registry.add(lock)
|
||||
if (!isClosing(registry.art) && option.autoPlayback && !option.isLive)
|
||||
registry.add(autoPlayback)
|
||||
if (!isClosing(registry.art) && option.autoOrientation && isMobile)
|
||||
registry.add(autoOrientation)
|
||||
if (!isClosing(registry.art) && option.fastForward && isMobile && !option.isLive)
|
||||
registry.add(fastForward)
|
||||
}
|
||||
@@ -1,59 +0,0 @@
|
||||
import { def, errorHandle, has, isMobile } from '../utils'
|
||||
import autoOrientation from './autoOrientation'
|
||||
import autoPlayback from './autoPlayback'
|
||||
import fastForward from './fastForward'
|
||||
import lock from './lock'
|
||||
import miniProgressBar from './miniProgressBar'
|
||||
|
||||
export default class Plugins {
|
||||
constructor(art) {
|
||||
this.art = art
|
||||
this.id = 0
|
||||
|
||||
const { option } = art
|
||||
|
||||
if (option.miniProgressBar && !option.isLive) {
|
||||
this.add(miniProgressBar)
|
||||
}
|
||||
|
||||
if (option.lock && isMobile) {
|
||||
this.add(lock)
|
||||
}
|
||||
|
||||
if (option.autoPlayback && !option.isLive) {
|
||||
this.add(autoPlayback)
|
||||
}
|
||||
|
||||
if (option.autoOrientation && isMobile) {
|
||||
this.add(autoOrientation)
|
||||
}
|
||||
|
||||
if (option.fastForward && isMobile && !option.isLive) {
|
||||
this.add(fastForward)
|
||||
}
|
||||
|
||||
for (let index = 0; index < option.plugins.length; index++) {
|
||||
this.add(option.plugins[index])
|
||||
}
|
||||
}
|
||||
|
||||
add(plugin) {
|
||||
this.id += 1
|
||||
const result = plugin.call(this.art, this.art)
|
||||
if (result instanceof Promise) {
|
||||
return result.then(res => this.next(plugin, res))
|
||||
}
|
||||
else {
|
||||
return this.next(plugin, result)
|
||||
}
|
||||
}
|
||||
|
||||
next(plugin, result) {
|
||||
const pluginName = (result && result.name) || plugin.name || `plugin${this.id}`
|
||||
errorHandle(!has(this, pluginName), `Cannot add a plugin that already has the same name: ${pluginName}`)
|
||||
def(this, pluginName, {
|
||||
value: result,
|
||||
})
|
||||
return this
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
import type { PluginFactory, PluginHost, PluginRegistration } from './types'
|
||||
import { isClosing } from '../lifecycle/instance'
|
||||
import { errorHandle } from '../utils/error'
|
||||
import installBuiltins from './builtins'
|
||||
import registerPlugin from './registration'
|
||||
|
||||
export default class Plugins<Host extends PluginHost<Host>> {
|
||||
declare art: Host
|
||||
declare id: number
|
||||
|
||||
constructor(art: Host) {
|
||||
this.art = art
|
||||
this.id = 0
|
||||
const { option } = art
|
||||
installBuiltins(this, option)
|
||||
for (let index = 0; index < option.plugins.length; index++) {
|
||||
if (isClosing(art))
|
||||
return
|
||||
const registration = this.add(option.plugins[index]!)
|
||||
if (registration instanceof Promise) {
|
||||
// Constructor registrations have no caller-owned Promise to observe.
|
||||
registration.catch(error => console.warn('Failed to initialize ArtPlayer plugin:', error))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
add<Result>(plugin: PluginFactory<Host, Result>): PluginRegistration<Result, this>
|
||||
add(plugin: PluginFactory<Host>): this | Promise<this> {
|
||||
errorHandle(!isClosing(this.art), 'Cannot add a plugin after ArtPlayer is destroyed')
|
||||
this.id += 1
|
||||
const result = plugin.call(this.art, this.art)
|
||||
if (result instanceof Promise)
|
||||
return result.then(res => this.next(plugin, res))
|
||||
return this.next(plugin, result)
|
||||
}
|
||||
|
||||
next(plugin: PluginFactory<Host>, result: unknown): this {
|
||||
return registerPlugin(this, plugin, result)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
import { isClosing } from '../lifecycle/instance'
|
||||
import { errorHandle } from '../utils/error'
|
||||
import { def, has } from '../utils/property'
|
||||
|
||||
export default function registerPlugin<Registry extends { art: object, id: number }>(registry: Registry, plugin: { name: string }, result: unknown): Registry {
|
||||
if (isClosing(registry.art))
|
||||
return registry
|
||||
// Keep the historical duck-typed name lookup and native property-key coercion.
|
||||
const name = (result && (result as { name?: unknown }).name) || plugin.name || `plugin${registry.id}`
|
||||
if (isClosing(registry.art))
|
||||
return registry
|
||||
let key = name as PropertyKey
|
||||
const duplicate = has(registry, key)
|
||||
if (isClosing(registry.art))
|
||||
return registry
|
||||
const message = `Cannot add a plugin that already has the same name: ${name}`
|
||||
if (isClosing(registry.art))
|
||||
return registry
|
||||
errorHandle(!duplicate, message)
|
||||
// Object keys can execute user code during native coercion. Keep that third
|
||||
// coercion in its historical position, then check closure before defining.
|
||||
if ((typeof name === 'object' && name !== null) || typeof name === 'function')
|
||||
key = Reflect.ownKeys({ [key]: undefined })[0]!
|
||||
if (!isClosing(registry.art))
|
||||
def(registry, key, { value: result })
|
||||
return registry
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
import type { PluginFactory } from '../../types/plugin'
|
||||
import type { ResolvedOption } from '../option/types'
|
||||
|
||||
export type { PluginFactory } from '../../types/plugin'
|
||||
|
||||
export type PluginRegistration<Result, Registry>
|
||||
= unknown extends Result ? Registry | Promise<Registry>
|
||||
: Result extends Promise<unknown> ? Promise<Registry> : Registry
|
||||
|
||||
export interface PluginHost<Host> {
|
||||
option: Pick<ResolvedOption, 'miniProgressBar' | 'isLive' | 'lock' | 'autoPlayback' | 'autoOrientation' | 'fastForward'> & {
|
||||
plugins: PluginFactory<Host>[]
|
||||
}
|
||||
}
|
||||
|
||||
export interface BuiltinRegistry<Host extends PluginHost<Host>> {
|
||||
art: Host
|
||||
add: (plugin: PluginFactory<Host>) => unknown
|
||||
}
|
||||
Reference in new issue
Block a user