mirror of
https://github.com/zhw2590582/ArtPlayer.git
synced 2026-10-15 00:56:16 -08:00
refactor(core): [CORE-21] generate compatible public and editor declarations
This commit is contained in:
1 parent
ee895a558f
commit
faaa3e8c2d
123 files changed
+31305
-1712
No files matched your search
@@ -0,0 +1,57 @@
|
||||
import { expect, test } from './fixtures.js'
|
||||
|
||||
test('docs Monaco worker consumes generated core types and runs a typed player example', async ({ page }) => {
|
||||
await page.goto('/test/player.html?core=candidate&chapter=published')
|
||||
await page.addScriptTag({ url: '/assets/js/vs/loader.js' })
|
||||
const result = await page.evaluate(async () => {
|
||||
window.require.config({ paths: { vs: '/assets/js/vs' } })
|
||||
await new Promise((resolve, reject) => window.require(['vs/editor/editor.main'], resolve, reject))
|
||||
const api = window.monaco
|
||||
const types = api.languages.typescript
|
||||
types.typescriptDefaults.setCompilerOptions({ target: types.ScriptTarget.ES2020, strict: true, skipLibCheck: false, noEmit: false, types: [], allowNonTsExtensions: true })
|
||||
const source = await (await fetch('/assets/ts/artplayer.d.ts')).text()
|
||||
const libraryUri = 'file:///artplayer.d.ts'
|
||||
const library = types.typescriptDefaults.addExtraLib(source, libraryUri)
|
||||
const model = api.editor.createModel(`
|
||||
const editorOption: Artplayer.OptionInput = { container: '.player', url: '/test/pattern.mp4', muted: true }
|
||||
const editorArt: Artplayer = new Artplayer(editorOption)
|
||||
const editorEvents: Artplayer.Emitter<{ value: [number] }> = new Artplayer.Emitter()
|
||||
editorEvents.emit('value', 1)
|
||||
`, 'typescript', api.Uri.parse('file:///consumer.ts'))
|
||||
const invalid = api.editor.createModel('new Artplayer({ container: 123, url: "" })', 'typescript', api.Uri.parse('file:///invalid.ts'))
|
||||
try {
|
||||
const getWorker = await types.getTypeScriptWorker()
|
||||
const worker = await getWorker(model.uri, invalid.uri)
|
||||
const syntax = await worker.getSyntacticDiagnostics(model.uri.toString())
|
||||
const semantic = await worker.getSemanticDiagnostics(model.uri.toString())
|
||||
const declaration = await worker.getSemanticDiagnostics(libraryUri)
|
||||
const bad = await worker.getSemanticDiagnostics(invalid.uri.toString())
|
||||
const output = await worker.getEmitOutput(model.uri.toString())
|
||||
if (syntax.length || semantic.length || declaration.length)
|
||||
return { syntax, semantic, declaration, invalid: bad.map(item => item.code), ready: false }
|
||||
const script = output.outputFiles.find(file => file.name.endsWith('.js'))
|
||||
if (!script)
|
||||
throw new Error('Monaco did not emit the typed example')
|
||||
// eslint-disable-next-line no-new-func -- Execute the locally compiled editor fixture, as the demo's Run action does.
|
||||
const art = new Function(`${script.text}\nreturn editorArt`)()
|
||||
try {
|
||||
if (!art.isReady)
|
||||
await new Promise(resolve => art.on('ready', resolve))
|
||||
return { syntax, semantic, declaration, invalid: bad.map(item => item.code), ready: art.isReady && art.video.videoWidth > 0 }
|
||||
}
|
||||
finally {
|
||||
art.destroy()
|
||||
}
|
||||
}
|
||||
finally {
|
||||
model.dispose()
|
||||
invalid.dispose()
|
||||
library.dispose()
|
||||
}
|
||||
})
|
||||
expect(result.syntax).toEqual([])
|
||||
expect(result.semantic).toEqual([])
|
||||
expect(result.declaration).toEqual([])
|
||||
expect(result.invalid.length).toBeGreaterThan(0)
|
||||
expect(result.ready).toBe(true)
|
||||
})
|
||||
@@ -0,0 +1,169 @@
|
||||
import { expect, test } from './fixtures.js'
|
||||
|
||||
for (const core of ['published', 'candidate']) {
|
||||
test(`${core}: constructor callback hosts expose only initialized components`, async ({ page }) => {
|
||||
await page.goto(`/test/player.html?core=${core}&chapter=published`)
|
||||
const result = await page.evaluate(async () => {
|
||||
const observations = []
|
||||
const receivers = []
|
||||
const names = ['template', 'events', 'storage', 'icons', 'i18n', 'notice', 'player', 'layers', 'controls', 'contextmenu', 'subtitle', 'info', 'loading', 'hotkey', 'mask', 'setting', 'plugins']
|
||||
const record = (phase, receiver, instance) => {
|
||||
receivers.push(receiver)
|
||||
observations.push({ phase, receiver: receiver === instance, fields: names.filter(name => Object.hasOwn(instance, name)) })
|
||||
}
|
||||
let mounted
|
||||
const settingMounted = new Promise(resolve => mounted = resolve)
|
||||
let ready
|
||||
const loaded = new Promise(resolve => ready = resolve)
|
||||
const art = new window.Artplayer({
|
||||
container: '.player',
|
||||
url: '/test/pattern.mp4',
|
||||
type: 'fixture',
|
||||
muted: true,
|
||||
setting: true,
|
||||
proxy(instance) {
|
||||
record('proxy', this, instance)
|
||||
return document.createElement('video')
|
||||
},
|
||||
layers: [{ html: 'Layer', mounted() { record('layer', this, this) } }],
|
||||
controls: [{ html: 'Control', position: 'left', mounted() { record('control', this, this) } }],
|
||||
contextmenu: [{ html: 'Menu', mounted() { record('contextmenu', this, this) } }],
|
||||
settings: [{ html: 'Setting', mounted() {
|
||||
record('setting', this, this)
|
||||
mounted()
|
||||
} }],
|
||||
plugins: [function (instance) {
|
||||
record('plugin', this, instance)
|
||||
return { name: 'phases' }
|
||||
}],
|
||||
customType: { fixture(video, url, instance) {
|
||||
record('customType', this, instance)
|
||||
video.src = url
|
||||
} },
|
||||
}, function (instance) {
|
||||
record('ready', this, instance)
|
||||
ready()
|
||||
})
|
||||
try {
|
||||
await Promise.all([settingMounted, loaded])
|
||||
return { names, observations, complete: window.Artplayer.instances.includes(art), receivers: receivers.every(receiver => receiver === art) }
|
||||
}
|
||||
finally {
|
||||
art.destroy()
|
||||
}
|
||||
})
|
||||
expect(result.complete).toBe(true)
|
||||
expect(result.receivers).toBe(true)
|
||||
const expected = { proxy: 0, layer: 7, control: 8, contextmenu: 9, plugin: 16, setting: 17, customType: 17, ready: 17 }
|
||||
expect(result.observations.map(item => item.phase).sort()).toEqual(Object.keys(expected).sort())
|
||||
for (const item of result.observations) {
|
||||
expect(item.receiver).toBe(true)
|
||||
expect(item.fields, item.phase).toEqual(result.names.slice(0, expected[item.phase]))
|
||||
}
|
||||
})
|
||||
|
||||
test(`${core}: container validation rejects non-div elements`, async ({ page }) => {
|
||||
await page.goto(`/test/player.html?core=${core}&chapter=published`)
|
||||
const result = await page.evaluate(() => {
|
||||
const container = document.createElement('section')
|
||||
document.body.append(container)
|
||||
try {
|
||||
const player = new window.Artplayer({ container, url: '' })
|
||||
player.destroy()
|
||||
return 'accepted'
|
||||
}
|
||||
catch (error) {
|
||||
return error.message
|
||||
}
|
||||
finally {
|
||||
container.remove()
|
||||
}
|
||||
})
|
||||
expect(result).toBe('Unsupported container element type, only support \'div\' but got \'section\'')
|
||||
})
|
||||
}
|
||||
|
||||
test('precise declarations: actual return values and callback receivers agree with the installed view', async ({ page }) => {
|
||||
await page.goto('/test/player.html?core=candidate&chapter=published')
|
||||
const result = await page.evaluate(async () => {
|
||||
const Player = window.Artplayer
|
||||
const container = document.querySelector('.player')
|
||||
const callbacks = []
|
||||
let ready
|
||||
const loaded = new Promise(resolve => ready = resolve)
|
||||
const factory = function (instance) {
|
||||
callbacks.push({ same: this === instance, registryAbsent: this.plugins === undefined })
|
||||
return { name: 'precise', update(value) {
|
||||
return value
|
||||
} }
|
||||
}
|
||||
const art = new Player({
|
||||
container,
|
||||
url: '/test/pattern.mp4',
|
||||
muted: true,
|
||||
plugins: [factory, window.artplayerPluginChapter({ chapters: [{ start: 0, end: Infinity, title: 'Existing plugin' }] })],
|
||||
}, function (instance) {
|
||||
ready({ same: this === instance, inInstances: Player.instances.includes(instance) })
|
||||
})
|
||||
try {
|
||||
const readyResult = await loaded
|
||||
const sync = art.plugins.add(function (instance) {
|
||||
callbacks.push({ same: this === instance, registry: this.plugins.art === instance })
|
||||
return { name: 'sync' }
|
||||
})
|
||||
const pending = art.plugins.add(async function (instance) {
|
||||
callbacks.push({ same: this === instance, registry: this.plugins.art === instance })
|
||||
return { name: 'async' }
|
||||
})
|
||||
const asyncResult = await pending
|
||||
const item = { name: 'typed-setting', html: 'Setting' }
|
||||
const added = art.setting.add(item)
|
||||
const updated = art.setting.update({ name: item.name, html: 'Updated' })
|
||||
const control = art.controls.add({ name: 'typed-control', html: 'Control', position: 'left' })
|
||||
const style = art.subtitle.style({ color: 'white' })
|
||||
art.notice.show = 'Visible'
|
||||
const notice = art.notice.show
|
||||
art.notice.show = false
|
||||
const hidden = art.notice.show
|
||||
const pause = art.pause()
|
||||
const removed = art.setting.remove(item.name)
|
||||
return {
|
||||
ready: readyResult,
|
||||
callbacks,
|
||||
constructor: art.constructor === Player,
|
||||
optionFactory: art.option.plugins[0] === factory,
|
||||
registry: sync === art.plugins && asyncResult === art.plugins,
|
||||
promiseBranch: pending instanceof Promise && !(sync instanceof Promise),
|
||||
commandReads: ['seek', 'forward', 'backward', 'switch', 'quality'].every(name => art[name] === undefined),
|
||||
setting: added === item && updated === item && removed === undefined && art.setting.find(item.name) === null,
|
||||
control: control === undefined,
|
||||
subtitle: style === art.template.$subtitle && Array.isArray(art.subtitle.cues) && Array.isArray(art.subtitle.activeCues),
|
||||
notice: notice === true && hidden === false,
|
||||
template: art.template.$container === container && !('html' in art.template) && typeof Player.html === 'string',
|
||||
pause: pause === undefined,
|
||||
absentStatics: !('env' in Player) && !('build' in Player),
|
||||
chapter: typeof art.plugins.artplayerPluginChapter?.update === 'function',
|
||||
}
|
||||
}
|
||||
finally {
|
||||
art.destroy()
|
||||
}
|
||||
})
|
||||
expect(result).toEqual({
|
||||
ready: { same: true, inInstances: true },
|
||||
callbacks: [{ same: true, registryAbsent: true }, { same: true, registry: true }, { same: true, registry: true }],
|
||||
constructor: true,
|
||||
optionFactory: true,
|
||||
registry: true,
|
||||
promiseBranch: true,
|
||||
commandReads: true,
|
||||
setting: true,
|
||||
control: true,
|
||||
subtitle: true,
|
||||
notice: true,
|
||||
template: true,
|
||||
pause: true,
|
||||
absentStatics: true,
|
||||
chapter: true,
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,45 @@
|
||||
import assert from 'node:assert/strict'
|
||||
import fs from 'node:fs'
|
||||
import os from 'node:os'
|
||||
import path from 'node:path'
|
||||
// eslint-disable-next-line test/no-import-node-test -- Declaration generation must reject drift and invalid public sources.
|
||||
import { test } from 'node:test'
|
||||
import { generateCoreDeclarations } from '../scripts/build-types.mjs'
|
||||
|
||||
test('declaration checks reject stale output without overwriting it and recover through explicit generation', async () => {
|
||||
const root = fs.mkdtempSync(path.join(os.tmpdir(), 'artplayer-declarations-'))
|
||||
const folder = path.join(root, 'packages/artplayer')
|
||||
fs.mkdirSync(path.join(folder, 'public'), { recursive: true })
|
||||
fs.mkdirSync(path.join(folder, 'types'), { recursive: true })
|
||||
const source = path.join(folder, 'public/contract.ts')
|
||||
const output = path.join(folder, 'types/contract.d.ts')
|
||||
try {
|
||||
fs.writeFileSync(source, 'export interface Contract { name: string }\n')
|
||||
await assert.rejects(generateCoreDeclarations({ root }), /stale/)
|
||||
assert.equal(fs.existsSync(output), false)
|
||||
await generateCoreDeclarations({ root, write: true })
|
||||
const generated = fs.readFileSync(output, 'utf8')
|
||||
assert.match(generated, /Generated from public\/contract.ts/)
|
||||
assert.deepEqual((await generateCoreDeclarations({ root })).changed, [])
|
||||
fs.writeFileSync(source, 'export interface Contract { name: string, enabled: boolean }\n')
|
||||
await assert.rejects(generateCoreDeclarations({ root }), /stale/)
|
||||
assert.equal(fs.readFileSync(output, 'utf8'), generated)
|
||||
await generateCoreDeclarations({ root, write: true })
|
||||
assert.match(fs.readFileSync(output, 'utf8'), /enabled: boolean/)
|
||||
assert.equal(fs.existsSync(path.join(folder, 'types/contract.js')), false)
|
||||
fs.writeFileSync(source, 'export interface Contract { name: MissingPublicType }\n')
|
||||
const valid = fs.readFileSync(output, 'utf8')
|
||||
await assert.rejects(generateCoreDeclarations({ root, write: true }), /MissingPublicType/)
|
||||
assert.equal(fs.readFileSync(output, 'utf8'), valid)
|
||||
fs.mkdirSync(path.join(folder, 'src'))
|
||||
fs.writeFileSync(path.join(folder, 'src/private.ts'), 'export interface Private { internal: boolean }\n')
|
||||
fs.writeFileSync(source, 'export type { Private } from "../src/private.js"\n')
|
||||
await assert.rejects(generateCoreDeclarations({ root, write: true }), /rootDir|non-public/)
|
||||
assert.equal(fs.readFileSync(output, 'utf8'), valid)
|
||||
}
|
||||
finally {
|
||||
assert.equal(path.dirname(path.resolve(root)), path.resolve(os.tmpdir()))
|
||||
assert(path.basename(root).startsWith('artplayer-declarations-'))
|
||||
fs.rmSync(root, { recursive: true, force: true })
|
||||
}
|
||||
})
|
||||
@@ -0,0 +1,21 @@
|
||||
import assert from 'node:assert/strict'
|
||||
import fs from 'node:fs'
|
||||
// eslint-disable-next-line test/no-import-node-test -- Exercise the real standalone editor compiler and generator.
|
||||
import { test } from 'node:test'
|
||||
import { ESLint } from 'eslint'
|
||||
import compat from 'typescript-compat'
|
||||
import { asGlobalDeclaration, checkCoreEditorDeclaration, generateCoreEditorDeclaration } from '../scripts/editor-types.mjs'
|
||||
|
||||
test('editor declarations are reproducible, standalone and preserve constructor/named types in both compilers', async () => {
|
||||
const generated = generateCoreEditorDeclaration()
|
||||
const file = 'docs/assets/ts/artplayer.d.ts'
|
||||
const eslint = new ESLint({ fix: true, fixTypes: ['layout'] })
|
||||
const [formatted] = await eslint.lintText(generated, { filePath: file })
|
||||
assert.equal(formatted.errorCount, 0)
|
||||
assert.equal(fs.readFileSync(file, 'utf8').replaceAll('\r\n', '\n'), formatted.output || generated, 'Editor declarations are stale; run yarn build:ts')
|
||||
assert.deepEqual(checkCoreEditorDeclaration(formatted.output || generated, compat), [])
|
||||
const corrupted = generated.replace('export type OptionInput = ArtplayerDefinitions.OptionInput;', 'export type OptionInput = MissingEditorType;')
|
||||
assert.notEqual(corrupted, generated, 'Missing corruption target')
|
||||
assert(checkCoreEditorDeclaration(corrupted).some(diagnostic => diagnostic.code === 2304), 'Invalid output must not be hidden by skipLibCheck')
|
||||
assert.throws(() => asGlobalDeclaration('export { default } from "./missing"', 'Artplayer'), /unresolved exports/)
|
||||
})
|
||||
@@ -14,6 +14,8 @@ test('Package checks reject missing files, missing wildcard exports and internal
|
||||
for (const missing of files)
|
||||
assert.throws(() => checkFiles(manifest, files.filter(file => file !== missing)), /Missing/)
|
||||
assert.throws(() => checkFiles(manifest, [...files, 'package/tsconfig.json']), /tsconfig/)
|
||||
for (const source of ['src/internal.ts', 'public/artplayer.ts', 'node_modules/private/index.js'])
|
||||
assert.throws(() => checkFiles(manifest, [...files, `package/${source}`]), /Source\/dependencies/)
|
||||
assert.throws(() => checkFiles(manifest, files, ['package/dist/old.js']), /Historical/)
|
||||
})
|
||||
|
||||
|
||||
@@ -38,6 +38,16 @@ Remove a known case when its owning task fixes it; keep frozen release evidence
|
||||
`yarn test:package:release` additionally rejects any remaining known type errors.
|
||||
Passing this command alone is not authorization or sufficient evidence to publish.
|
||||
|
||||
The optional core `artplayer/runtime` entry has eight additional strict consumer
|
||||
groups: TS 5.1.6 and 5.9.3 each check Node10 CommonJS, NodeNext CJS/ESM and Bundler.
|
||||
These fixtures cover accurate returns and getter/setter types, construction-stage
|
||||
callback hosts, both old and new module augmentations, old chapter factories and
|
||||
the shared modern/legacy constructor. The old root remains isolated from modern
|
||||
accessor syntax and retains its five original consumer groups. Runtime checks
|
||||
verify the added entries reuse existing JS module and constructor identities;
|
||||
`runtime/types` remains type-only. All generated core declarations are packaged,
|
||||
while their authored `public/` source graph and internal implementation are excluded.
|
||||
|
||||
Reports, archives, installed artifact copies and build/install logs are under
|
||||
`refactor/.cache/packages/run-*`; `latest.json` points to the last successful
|
||||
compatibility run (a strict release rejection still leaves its report). For browser
|
||||
|
||||
@@ -27,6 +27,19 @@ const vm = require('node:vm');
|
||||
const legacy = await import('artplayer/legacy')
|
||||
const pluginLegacy = await import('artplayer-plugin-chapter/legacy')
|
||||
check('DIST.esm-legacy', legacy.default.version === core.version && typeof pluginLegacy.default({}) === 'function')
|
||||
if (!expected.baseline) {
|
||||
const precise = require('artplayer/runtime')
|
||||
const preciseEsm = await import('artplayer/runtime')
|
||||
const preciseLegacy = require('artplayer/runtime/legacy')
|
||||
const preciseLegacyEsm = await import('artplayer/runtime/legacy')
|
||||
check('DIST.runtime-cjs-identity', precise === core && require.resolve('artplayer/runtime') === require.resolve('artplayer'))
|
||||
check('DIST.runtime-esm-identity', preciseEsm === esm && preciseEsm.default === esm.default)
|
||||
check('DIST.runtime-legacy-identity', preciseLegacy === require('artplayer/legacy') && preciseLegacyEsm === legacy)
|
||||
check('DIST.runtime-shared-statics', precise.instances === core.instances && preciseEsm.default.instances === esm.default.instances && preciseLegacy.instances === legacy.default.instances)
|
||||
assert.throws(() => require('artplayer/runtime/types'), error => error.code === 'ERR_PACKAGE_PATH_NOT_EXPORTED')
|
||||
await assert.rejects(import('artplayer/runtime/types'), error => error.code === 'ERR_PACKAGE_PATH_NOT_EXPORTED')
|
||||
check('DIST.runtime-types-only', true)
|
||||
}
|
||||
for (const [name, filename, globalName] of [
|
||||
['core', 'artplayer/dist/artplayer.js', 'Artplayer'],
|
||||
['core-legacy', 'artplayer/dist/artplayer.legacy.js', 'Artplayer'],
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
/* eslint-disable ts/no-require-imports, ts/no-this-alias -- Exercise actual CJS syntax and callback receiver types. */
|
||||
import Artplayer = require('artplayer/runtime')
|
||||
import LegacyRuntime = require('artplayer/runtime/legacy')
|
||||
|
||||
const option: Artplayer.OptionInput = { container: '#player' }
|
||||
const art: Artplayer = new Artplayer(option)
|
||||
const legacy: Artplayer = new LegacyRuntime(option)
|
||||
const factory: Artplayer.PluginFactory<{ name: string }> = function (instance) {
|
||||
const receiver: Artplayer.PluginHost = this
|
||||
const value: undefined = instance.seek
|
||||
void [receiver, value]
|
||||
return { name: 'cjs' }
|
||||
}
|
||||
const registry: Artplayer.Plugins = art.plugins.add(factory)
|
||||
// @ts-expect-error CJS must not reintroduce legacy false getter values.
|
||||
const falseSeek: number = legacy.seek
|
||||
void [registry, falseSeek]
|
||||
@@ -0,0 +1,28 @@
|
||||
import type { Component, Controls } from '../../packages/artplayer/public/runtime/component'
|
||||
import type { Setting, SettingItem } from '../../packages/artplayer/public/runtime/setting'
|
||||
import type Artplayer from '../../packages/artplayer/src'
|
||||
import type { ControlHost } from '../../packages/artplayer/src/control/types'
|
||||
import type { SettingHost } from '../../packages/artplayer/src/setting/types'
|
||||
|
||||
declare const source: Artplayer
|
||||
const controls: Controls<ControlHost> = source.controls
|
||||
const component: Component<ControlHost> = source.controls
|
||||
const setting: Setting<SettingHost> = source.setting
|
||||
const added: undefined = controls.add({ name: 'sample', html: 1, position: 'left' })
|
||||
const updated: undefined = controls.update({ name: 'sample', html: 2 })
|
||||
const nullable: SettingItem<SettingHost> | null = setting.find('absent')
|
||||
const item = { name: 'setting', html: 'Item', extra: 1 }
|
||||
const same: typeof item = setting.add(item)
|
||||
const changed: SettingItem<SettingHost> = setting.update({ name: 'setting', html: 'Updated' })
|
||||
const removed: void = setting.remove('setting')
|
||||
setting.add({ name: 'callback', html: 'Callback', onClick(entry) {
|
||||
const offset: number = this.subtitleOffset
|
||||
return [offset, entry.name]
|
||||
} })
|
||||
// @ts-expect-error The updated item is not the whole setting registry.
|
||||
const registry: Setting<SettingHost> = setting.update({ name: 'setting' })
|
||||
// @ts-expect-error A missing setting result is null, not undefined.
|
||||
const absent: undefined = setting.find('absent')
|
||||
// @ts-expect-error Control add does not return a mounted element.
|
||||
const element: HTMLElement = controls.add({ html: 'Button', position: 'left' })
|
||||
export { absent, added, changed, component, element, nullable, registry, removed, same, updated }
|
||||
@@ -0,0 +1,72 @@
|
||||
/* eslint-disable ts/no-this-alias -- Assignments verify callback initialization views. */
|
||||
import type { ContextmenuHost, ControlHost, CustomTypeHost, LayerHost, OptionInput, PluginHost, ProxyHost } from 'artplayer/runtime'
|
||||
import Artplayer from 'artplayer/runtime'
|
||||
|
||||
const option: OptionInput = {
|
||||
container: document.createElement('div'),
|
||||
proxy(art) {
|
||||
const host: ProxyHost = art
|
||||
const id: number = host.id
|
||||
art.on('ready', () => {})
|
||||
// @ts-expect-error Template has not returned during proxy construction.
|
||||
void art.template
|
||||
// @ts-expect-error Accessing the video getter before Template returns would throw.
|
||||
void art.video
|
||||
// @ts-expect-error Player descriptors have not been installed yet.
|
||||
art.seek = 1
|
||||
void id
|
||||
return document.createElement('video')
|
||||
},
|
||||
customType: { custom(video, url, art) {
|
||||
const host: CustomTypeHost = art
|
||||
host.seek = '1'
|
||||
const seek: undefined = host.seek
|
||||
// URL assignment defers customType until construction has finished.
|
||||
const player: object = art.player
|
||||
art.controls.show = true
|
||||
void [video, url, seek, player]
|
||||
} },
|
||||
layers: [art => ({ html: String(art.id), mounted() {
|
||||
const host: LayerHost = this
|
||||
host.seek = 1
|
||||
// @ts-expect-error Layer has not returned when its mounted callback runs.
|
||||
host.layers.show = true
|
||||
} })],
|
||||
controls: [{ position: 'left', mounted() {
|
||||
const host: ControlHost = this
|
||||
host.layers.show = true
|
||||
// @ts-expect-error Control has not returned when its mounted callback runs.
|
||||
host.controls.show = true
|
||||
} }],
|
||||
contextmenu: [{ mounted() {
|
||||
const host: ContextmenuHost = this
|
||||
host.controls.show = true
|
||||
// @ts-expect-error Contextmenu has not returned when its mounted callback runs.
|
||||
host.contextmenu.show = true
|
||||
} }],
|
||||
setting: true,
|
||||
settings: [{ html: 'Setting', mounted() {
|
||||
// Setting mounted uses a deferred callback after construction completes.
|
||||
const host: Artplayer = this
|
||||
host.setting.show = true
|
||||
host.plugins.add(() => ({ name: 'from-setting' }))
|
||||
} }],
|
||||
plugins: [function (art) {
|
||||
const host: PluginHost = art
|
||||
host.setting.show = true
|
||||
host.seek = 1
|
||||
// @ts-expect-error Registry is assigned only after constructor factories return.
|
||||
host.plugins.add(() => ({ name: 'too-early' }))
|
||||
return { name: 'construction' }
|
||||
}],
|
||||
}
|
||||
const art = new Artplayer(option, function (art) {
|
||||
art.seek = 2
|
||||
this.controls.show = true
|
||||
this.plugins.add(() => ({ name: 'ready' }))
|
||||
})
|
||||
// @ts-expect-error Only div containers satisfy the runtime element validation.
|
||||
const invalidContainer: OptionInput = { container: document.createElement('section') }
|
||||
// @ts-expect-error A configured proxy must return a video or media-like canvas.
|
||||
const invalidProxy: OptionInput = { container: '#player', proxy: () => undefined }
|
||||
void [art, invalidContainer, invalidProxy]
|
||||
@@ -0,0 +1,30 @@
|
||||
import type { Emitter } from '../../packages/artplayer/public/emitter'
|
||||
import type { Events } from '../../packages/artplayer/public/runtime/events'
|
||||
import type { Notice, Subtitle, SubtitleCue } from '../../packages/artplayer/public/runtime/subtitle'
|
||||
import type Artplayer from '../../packages/artplayer/src'
|
||||
|
||||
declare const source: Artplayer
|
||||
const events: Emitter<Events> = source
|
||||
const subtitle: Subtitle = source.subtitle
|
||||
const notice: Notice = source.notice
|
||||
const visible: boolean = notice.show
|
||||
notice.show = 'message'
|
||||
const switched: Promise<string | null | undefined> = subtitle.switch('/subtitle.vtt')
|
||||
const styled: HTMLDivElement = subtitle.style('font-size', 20)
|
||||
events.on('subtitleBeforeUpdate', (cues) => {
|
||||
const actual: SubtitleCue[] = cues
|
||||
return actual.map(cue => cue.text)
|
||||
})
|
||||
events.on('blur', event => event.type)
|
||||
source.on('subtitleBeforeUpdate', cues => cues.map(cue => cue.text))
|
||||
events.emit('subtitleAfterUpdate', [])
|
||||
events.emit('seek', 1.5, '1.5')
|
||||
// @ts-expect-error The corrected payload cannot retain historical scalar cue inference.
|
||||
events.on('subtitleBeforeUpdate', (cue: VTTCue) => cue.text)
|
||||
// @ts-expect-error Native focus loss carries its original Event.
|
||||
events.emit('blur')
|
||||
// @ts-expect-error The actual source map must also reject a scalar update payload.
|
||||
source.emit('subtitleAfterUpdate', 1)
|
||||
// @ts-expect-error A visibility getter does not return the last message.
|
||||
const message: string = notice.show
|
||||
export { events, message, styled, switched, visible }
|
||||
@@ -0,0 +1,31 @@
|
||||
import type SourceArtplayer from '../../packages/artplayer/src'
|
||||
import type { I18nHost } from '../../packages/artplayer/src/i18n'
|
||||
import type RuntimeArtplayer from '../../packages/artplayer/types/runtime'
|
||||
import type { ArtplayerConstructor } from '../../packages/artplayer/types/runtime'
|
||||
import type { Config, EventRegistry, Hotkey, I18n, Storage } from '../../packages/artplayer/types/runtime/services'
|
||||
|
||||
declare const source: SourceArtplayer
|
||||
declare const constructor: typeof SourceArtplayer
|
||||
const events: EventRegistry = source.events
|
||||
const storage: Storage = source.storage
|
||||
const hotkey: Hotkey<SourceArtplayer> = source.hotkey
|
||||
const language: I18n<I18nHost> = source.i18n
|
||||
const config: Config = constructor.config
|
||||
const validator: ArtplayerConstructor['validator'] = constructor.validator
|
||||
const kindOf: ArtplayerConstructor['kindOf'] = constructor.kindOf
|
||||
const scheme: ArtplayerConstructor['scheme'] = constructor.scheme
|
||||
|
||||
// The facade covers every implementation member without whole-instance assertions.
|
||||
const missingInstance: Exclude<keyof SourceArtplayer, keyof RuntimeArtplayer> extends never ? true : false = true
|
||||
const extraInstance: Exclude<keyof RuntimeArtplayer, keyof SourceArtplayer | 'flv' | 'm3u8' | 'hls' | 'ts' | 'mpd' | 'torrent'> extends never ? true : false = true
|
||||
const missingStatic: Exclude<keyof typeof SourceArtplayer, keyof ArtplayerConstructor> extends never ? true : false = true
|
||||
const extraStatic: Exclude<keyof ArtplayerConstructor, keyof typeof SourceArtplayer> extends never ? true : false = true
|
||||
|
||||
type ValueMembers = 'id' | 'isLock' | 'isReady' | 'isFocus' | 'isInput' | 'isRotate' | 'isDestroy' | 'video' | 'player' | 'reset' | 'destroy'
|
||||
const values: Pick<RuntimeArtplayer, ValueMembers> = source
|
||||
type SharedOptions = Exclude<keyof RuntimeArtplayer['option'], 'proxy' | 'plugins' | 'customType' | 'layers' | 'controls' | 'contextmenu' | 'settings'>
|
||||
const options: Pick<RuntimeArtplayer['option'], SharedOptions> = source.option
|
||||
const storageValue: unknown = storage.get()
|
||||
// @ts-expect-error Persisted JSON can be a primitive, not just a settings object.
|
||||
const falseSettings: Record<string, unknown> = storage.get()
|
||||
void [events, storage, hotkey, language, config, validator, kindOf, scheme, missingInstance, extraInstance, missingStatic, extraStatic, values, options, storageValue, falseSettings]
|
||||
@@ -0,0 +1,54 @@
|
||||
import type { Emitter } from 'artplayer'
|
||||
import type { Controls } from '../../packages/artplayer/types/runtime/component.js'
|
||||
import type { Events } from '../../packages/artplayer/types/runtime/events.js'
|
||||
import type { NativeMedia } from '../../packages/artplayer/types/runtime/media.js'
|
||||
import type { Player } from '../../packages/artplayer/types/runtime/player.js'
|
||||
import type { Plugins } from '../../packages/artplayer/types/runtime/plugin.js'
|
||||
import type { Setting, SettingItem } from '../../packages/artplayer/types/runtime/setting.js'
|
||||
import type { Notice, Subtitle } from '../../packages/artplayer/types/runtime/subtitle.js'
|
||||
import type { Icons, Template } from '../../packages/artplayer/types/runtime/template.js'
|
||||
import type { Utils } from '../../packages/artplayer/types/runtime/utils.js'
|
||||
|
||||
declare const player: Player<NativeMedia>
|
||||
declare const registry: Plugins<Player<NativeMedia>>
|
||||
declare const events: Emitter<Events>
|
||||
declare const notice: Notice
|
||||
declare const subtitle: Subtitle
|
||||
declare const template: Template<Player<NativeMedia>, NativeMedia>
|
||||
declare const icons: Icons
|
||||
declare const utils: Utils
|
||||
declare const controls: Controls<Player<NativeMedia>>
|
||||
declare const setting: Setting<Player<NativeMedia>>
|
||||
const read: undefined = player.seek
|
||||
player.seek = '2'
|
||||
const play: Promise<void> = player.play()
|
||||
const pip: Element | null | boolean = player.pip
|
||||
const immediate: typeof registry = registry.add(art => ({ name: 'plain', value: art.currentTime }))
|
||||
const pending: Promise<typeof registry> = registry.add(async art => ({ name: 'async', value: art.currentTime }))
|
||||
events.on('subtitleBeforeUpdate', cues => cues.map(cue => cue.text))
|
||||
events.on('video:error', event => event.type)
|
||||
const visible: boolean = notice.show
|
||||
notice.show = new Error('test')
|
||||
const changed: Promise<string | null | undefined> = subtitle.switch('/subtitle.vtt')
|
||||
const native: NativeMedia = template.$video
|
||||
const wrapper: HTMLElement = icons.play
|
||||
const custom: HTMLElement | undefined = icons.custom
|
||||
const target = { id: 1 }
|
||||
const same: typeof target = utils.def(target, 'name', { value: 'name' })
|
||||
const delayed: void = utils.debounce(() => 1, 0, {})()
|
||||
const mounted: undefined = controls.add({ name: 'button', html: 'Button', position: 'left' })
|
||||
const settingResult: SettingItem<Player<NativeMedia>> | null = setting.find('entry')
|
||||
const input = { name: 'entry', html: 'Entry', custom: 123 }
|
||||
const settingIdentity: typeof input = setting.add(input)
|
||||
const updated: SettingItem<Player<NativeMedia>> = setting.update(input)
|
||||
// @ts-expect-error Reading a setter-only command is not a numeric result.
|
||||
const number: number = player.seek
|
||||
// @ts-expect-error Native error forwarding preserves Event, not an Error object.
|
||||
events.emit('video:error', new Error('wrong payload'))
|
||||
// @ts-expect-error Update callbacks receive arrays.
|
||||
events.on('subtitleBeforeUpdate', (cue: VTTCue) => cue.text)
|
||||
// @ts-expect-error Deferred callbacks have no immediate numeric result.
|
||||
const immediateNumber: number = utils.throttle(() => 1, 0)()
|
||||
// @ts-expect-error A setting update returns the item, not the registry.
|
||||
const settingRegistry: typeof setting = setting.update(input)
|
||||
export { changed, custom, delayed, immediate, immediateNumber, mounted, native, number, pending, pip, play, read, same, settingIdentity, settingRegistry, settingResult, updated, visible, wrapper }
|
||||
@@ -0,0 +1,36 @@
|
||||
import type { CanvasMedia, MediaSurface, NativeMedia } from '../../packages/artplayer/public/runtime/media'
|
||||
import type { Player } from '../../packages/artplayer/public/runtime/player'
|
||||
import type { MediaSurface as SourceMedia } from '../../packages/artplayer/src/media/types'
|
||||
import type { PlayerProperties } from '../../packages/artplayer/src/player/properties'
|
||||
|
||||
declare const actual: PlayerProperties
|
||||
declare const media: SourceMedia
|
||||
declare const native: Player<NativeMedia>
|
||||
declare const canvas: Player<CanvasMedia>
|
||||
const publicPlayer: Player = actual
|
||||
const publicMedia: MediaSurface = media
|
||||
const internalMedia: SourceMedia = publicMedia
|
||||
const commands: undefined[] = [publicPlayer.seek, publicPlayer.forward, publicPlayer.backward, publicPlayer.switch, publicPlayer.quality]
|
||||
const pip: Element | null | boolean = publicPlayer.pip
|
||||
const url: string | null = publicPlayer.url
|
||||
const nativePlay: Promise<void> = native.play()
|
||||
const nativePause: void = native.pause()
|
||||
const nativeToggle: void | Promise<void> = native.toggle()
|
||||
const proxyPlay: Promise<unknown> = canvas.play()
|
||||
publicPlayer.seek = '1.5'
|
||||
publicPlayer.forward = 2
|
||||
publicPlayer.quality = [{ html: document.createElement('span'), url: '/video.mp4' }]
|
||||
publicPlayer.pip = true
|
||||
// @ts-expect-error Commands do not have the historical fictitious numeric getters.
|
||||
const position: number = publicPlayer.seek
|
||||
// @ts-expect-error Accurate write types are not widened just to satisfy TS 4.3's accessor restriction.
|
||||
publicPlayer.quality = undefined
|
||||
// @ts-expect-error A native PiP element is a result, not a supported setter input.
|
||||
publicPlayer.pip = document.createElement('video')
|
||||
// @ts-expect-error The proxy play result cannot be assumed to be a native void result.
|
||||
const proxyNative: Promise<void> = canvas.play()
|
||||
type Assert<Condition extends true> = Condition
|
||||
type Complete = Assert<Exclude<keyof PlayerProperties, keyof Player> extends never ? true : false>
|
||||
type NoInventedProperties = Assert<Exclude<keyof Player, keyof PlayerProperties> extends never ? true : false>
|
||||
export type { Complete, NoInventedProperties }
|
||||
export { commands, internalMedia, nativePause, nativePlay, nativeToggle, pip, position, proxyNative, proxyPlay, url }
|
||||
@@ -0,0 +1,165 @@
|
||||
/* eslint-disable ts/no-this-alias -- Assignments prove contextual callback receiver types. */
|
||||
import type { PluginFactory as LegacyPluginFactory } from 'artplayer'
|
||||
import type { CustomTypeHost, Events, OptionInput, PluginFactory, PluginHost, Plugins } from 'artplayer/runtime'
|
||||
import chapter from 'artplayer-plugin-chapter'
|
||||
import Artplayer from 'artplayer/runtime'
|
||||
import LegacyRuntime from 'artplayer/runtime/legacy'
|
||||
|
||||
declare module 'artplayer/types' {
|
||||
interface Plugins {
|
||||
legacyRuntimeExtension?: { set: (count: number) => void }
|
||||
}
|
||||
interface Events {
|
||||
'legacy-runtime:update': [count: number]
|
||||
}
|
||||
}
|
||||
|
||||
declare module 'artplayer/runtime/types' {
|
||||
interface Plugins {
|
||||
runtimeExtension?: { update: (count: number) => void }
|
||||
}
|
||||
interface Events {
|
||||
'runtime-extension:update': [count: number]
|
||||
}
|
||||
}
|
||||
|
||||
const factory: PluginFactory<{ name: string, update: (count: number) => void }> = function (art) {
|
||||
const seek: undefined = this.seek
|
||||
void seek
|
||||
// @ts-expect-error A constructor factory cannot assume the registry has been assigned.
|
||||
void art.plugins.art
|
||||
return { name: 'runtimeExtension', update(count) {
|
||||
art.emit('runtime-extension:update', count)
|
||||
} }
|
||||
}
|
||||
|
||||
const option: OptionInput = {
|
||||
container: document.createElement('div'),
|
||||
plugins: [factory, function (art) {
|
||||
const receiver: PluginHost = this
|
||||
const value: undefined = art.seek
|
||||
void [receiver, value]
|
||||
return { name: 'inline' }
|
||||
}],
|
||||
controls: [{ name: 'test', position: 'left', mounted(element) {
|
||||
const value: undefined = this.seek
|
||||
element.textContent = String(value)
|
||||
} }],
|
||||
settings: [{ html: 'Custom', onClick(item) {
|
||||
const visible: boolean = this.notice.show
|
||||
return `${item.html}: ${visible}`
|
||||
} }],
|
||||
customType: { hls(video, url, art) {
|
||||
const receiver: CustomTypeHost = this
|
||||
const value: undefined = art.forward
|
||||
void [receiver, value, video, url]
|
||||
} },
|
||||
}
|
||||
const art = new Artplayer(option, function (instance) {
|
||||
const receiver: Artplayer = this
|
||||
instance.plugins.runtimeExtension?.update(1)
|
||||
void receiver
|
||||
})
|
||||
const sync: Plugins = art.plugins.add(factory)
|
||||
const async: Promise<Plugins> = art.plugins.add(async () => ({ name: 'async' }))
|
||||
const inline = art.plugins.add(function (instance) {
|
||||
const receiver: Artplayer = this
|
||||
const value: undefined = instance.backward
|
||||
void [receiver, value]
|
||||
return { name: 'inlineAdd' }
|
||||
})
|
||||
const same: Plugins = inline
|
||||
const seek: undefined = art.seek
|
||||
art.seek = 2
|
||||
art.seek = '2'
|
||||
art.notice.show = 'Message'
|
||||
const shown: boolean = art.notice.show
|
||||
const pip: Element | null | boolean = art.pip
|
||||
art.pip = true
|
||||
const icon: HTMLElement = art.icons.play
|
||||
const customIcon: HTMLElement | undefined = art.icons.customIcon
|
||||
const queried: HTMLDivElement | null = art.template.query<HTMLDivElement>('.optional')
|
||||
const settingItem = { name: 'precise-item', html: 'Item', marker: 1 }
|
||||
const added: typeof settingItem = art.setting.add(settingItem)
|
||||
const removed: void = art.setting.remove(settingItem.name)
|
||||
const subtitleStyle: HTMLDivElement = art.subtitle.style({ color: 'red' })
|
||||
const target = { count: 1 }
|
||||
const defined: typeof target = Artplayer.utils.def(target, Symbol('count'), {
|
||||
get() { return this.count },
|
||||
})
|
||||
const debounced = Artplayer.utils.debounce((value: number) => value + 1, 10)
|
||||
const throttled = Artplayer.utils.throttle((value: number) => value + 1, 10)
|
||||
const delayed: void = debounced(1)
|
||||
const limited: void = throttled(1)
|
||||
const missing = art.setting.find('missing')
|
||||
if (missing)
|
||||
missing.html = 'Found'
|
||||
art.on('runtime-extension:update', count => count.toFixed())
|
||||
art.on('subtitleAfterUpdate', cues => cues.map(cue => cue.text))
|
||||
art.plugins.runtimeExtension?.update(2)
|
||||
const payload: Events['runtime-extension:update'] = [1]
|
||||
Artplayer.config.events.push('custom-event')
|
||||
Artplayer.DEBUG = false
|
||||
// @ts-expect-error defineProperty requires an object target in an installed consumer too.
|
||||
Artplayer.utils.def(123, 'value', { value: 1 })
|
||||
|
||||
declare const legacyFactory: LegacyPluginFactory
|
||||
const legacyResult: Plugins | Promise<Plugins> = art.plugins.add(legacyFactory)
|
||||
art.plugins.add(chapter())
|
||||
art.plugins.legacyRuntimeExtension?.set(2)
|
||||
art.on('legacy-runtime:update', count => count.toFixed())
|
||||
const legacyRuntime: Artplayer = new LegacyRuntime(option)
|
||||
const legacyOptions = new Artplayer({ container: '#legacy', plugins: [legacyFactory] }, function (instance) {
|
||||
const value: undefined = this.seek
|
||||
const other: undefined = instance.seek
|
||||
void [value, other]
|
||||
})
|
||||
|
||||
class Derived extends Artplayer {
|
||||
marker = true
|
||||
}
|
||||
const derived = new Derived(option)
|
||||
const chained: Derived = derived.on('ready', () => {})
|
||||
|
||||
// @ts-expect-error Command properties have no getter value.
|
||||
const falseSeek: number = art.seek
|
||||
// @ts-expect-error New control entries require one of the three supported positions.
|
||||
art.controls.add({ html: 'Missing position' })
|
||||
// @ts-expect-error Unknown control positions fail runtime validation.
|
||||
art.controls.add({ html: 'Wrong position', position: 'middle' })
|
||||
// @ts-expect-error Synchronous registration is not a Promise.
|
||||
const falsePromise: Promise<Plugins> = sync
|
||||
// @ts-expect-error Getter visibility is boolean.
|
||||
const falseNotice: string = art.notice.show
|
||||
// @ts-expect-error Native PiP returns an element or null, while WebKit returns boolean.
|
||||
const falsePip: boolean = pip
|
||||
// @ts-expect-error The PiP setter takes a boolean, not the native getter's element.
|
||||
art.pip = document.createElement('video')
|
||||
// @ts-expect-error Icons expose wrapper elements, not their SVG input strings.
|
||||
const falseIcon: string = art.icons.play
|
||||
// @ts-expect-error SSR and custom query results may be absent.
|
||||
const falseQuery: HTMLDivElement = art.template.query<HTMLDivElement>('.optional')
|
||||
// @ts-expect-error A delayed wrapper does not return the callback's number.
|
||||
const falseDelayed: number = debounced(1)
|
||||
// @ts-expect-error A throttled wrapper does not return the callback's number.
|
||||
const falseLimited: number = throttled(1)
|
||||
// @ts-expect-error defineProperty returns its target, not a descriptor.
|
||||
const falseDescriptor: PropertyDescriptor = defined
|
||||
// @ts-expect-error Precise built-in events reject scalar subtitle payloads.
|
||||
art.emit('subtitleAfterUpdate', 'bad')
|
||||
// @ts-expect-error Augmented payloads stay checked.
|
||||
art.emit('runtime-extension:update', 'bad')
|
||||
// @ts-expect-error Augmented plugin methods stay checked.
|
||||
art.plugins.runtimeExtension?.update('bad')
|
||||
// @ts-expect-error Legacy plugin augmentation keeps its method types on the precise view.
|
||||
art.plugins.legacyRuntimeExtension?.set('bad')
|
||||
// @ts-expect-error Legacy event augmentation keeps its payload type on the precise view.
|
||||
art.emit('legacy-runtime:update', 'bad')
|
||||
// @ts-expect-error Static env was never exposed by the runtime.
|
||||
void Artplayer.env
|
||||
// @ts-expect-error Template html belongs to the constructor.
|
||||
void art.template.html
|
||||
// @ts-expect-error Missing setting entries are null, not undefined.
|
||||
const falseMissing: undefined = art.setting.find('missing')
|
||||
void [sync, async, same, seek, shown, payload, legacyResult, legacyRuntime, legacyOptions, chained, falseSeek, falsePromise, falseNotice, falseMissing]
|
||||
void [pip, icon, customIcon, queried, added, removed, subtitleStyle, defined, delayed, limited, falsePip, falseIcon, falseQuery, falseDelayed, falseLimited, falseDescriptor]
|
||||
@@ -0,0 +1,34 @@
|
||||
import type { PluginFactory, Plugins } from '../../packages/artplayer/public/runtime/plugin'
|
||||
import type { IconName, Icons, Template } from '../../packages/artplayer/public/runtime/template'
|
||||
import type Artplayer from '../../packages/artplayer/src'
|
||||
import type iconDefaults from '../../packages/artplayer/src/icons/defaults'
|
||||
|
||||
declare const source: Artplayer
|
||||
const plugins: Plugins<Artplayer> = source.plugins
|
||||
const sync: Plugins<Artplayer> = plugins.add(function (art) {
|
||||
// eslint-disable-next-line ts/no-this-alias -- Check the public factory's inferred receiver against the actual source instance.
|
||||
const same: Artplayer = this
|
||||
return { name: 'sync', same, art }
|
||||
})
|
||||
const asyncRegistry: Promise<Plugins<Artplayer>> = plugins.add(async () => ({ name: 'async' }))
|
||||
declare const unknownFactory: PluginFactory<Artplayer>
|
||||
const uncertain: Plugins<Artplayer> | Promise<Plugins<Artplayer>> = plugins.add(unknownFactory)
|
||||
const template: Template<Artplayer> = source.template
|
||||
const icons: Icons = source.icons
|
||||
const icon: HTMLElement = icons.play
|
||||
const customIcon: HTMLElement | undefined = icons.custom
|
||||
const subtitle: HTMLDivElement | null = template.$subtitle
|
||||
const query: HTMLVideoElement | null = template.query<HTMLVideoElement>('video')
|
||||
// @ts-expect-error Sync registration is not a Promise.
|
||||
const syncPromise: Promise<Plugins<Artplayer>> = plugins.add(() => ({ name: 'plain' }))
|
||||
// @ts-expect-error Template.html belongs to the constructor, not its instance.
|
||||
const instanceHtml = template.html
|
||||
// @ts-expect-error Icon wrappers are not promised to be div elements.
|
||||
const div: HTMLDivElement = icons.play
|
||||
// @ts-expect-error Missing custom icon keys require a presence guard.
|
||||
const present: HTMLElement = icons.custom
|
||||
type Assert<Condition extends true> = Condition
|
||||
type AllIcons = Assert<Exclude<keyof typeof iconDefaults, IconName> extends never ? true : false>
|
||||
type NoInventedIcons = Assert<Exclude<IconName, keyof typeof iconDefaults> extends never ? true : false>
|
||||
export type { AllIcons, NoInventedIcons }
|
||||
export { asyncRegistry, customIcon, div, icon, instanceHtml, present, query, subtitle, sync, syncPromise, uncertain }
|
||||
@@ -0,0 +1,31 @@
|
||||
import type { Utils } from '../../packages/artplayer/public/runtime/utils'
|
||||
import * as source from '../../packages/artplayer/src/utils'
|
||||
|
||||
const utils: Utils = source
|
||||
const target = { enabled: true }
|
||||
const same: typeof target = utils.def(target, 'value', { value: 1 })
|
||||
const delayed: void = utils.debounce(() => 123, 0, {})()
|
||||
const throttled: void = utils.throttle(() => 456, 0)()
|
||||
const receiver = { count: 0 }
|
||||
const callback = utils.debounce(function (this: typeof receiver, increment: number) {
|
||||
this.count += increment
|
||||
}, 0)
|
||||
callback.call(receiver, 2)
|
||||
const append: Element | ChildNode | null = utils.append(document.createElement('div'), '')
|
||||
const style: number | string = utils.getStyle(document.body, 'width', Math.random() > 0.5)
|
||||
const queried: HTMLVideoElement | null = utils.query<HTMLVideoElement>('video', document.createDocumentFragment())
|
||||
const removed: Text = utils.remove(document.createTextNode('text'))
|
||||
utils.sleep()
|
||||
// @ts-expect-error The callback result is not returned by the debounce wrapper.
|
||||
const immediate: number = utils.debounce(() => 1, 0)()
|
||||
// @ts-expect-error Native defineProperty requires a descriptor object.
|
||||
utils.def(target, 'value', 123)
|
||||
// @ts-expect-error Native defineProperty also requires an object target.
|
||||
utils.def(123, 'value', { value: 1 })
|
||||
// @ts-expect-error Optional event targets do not make composed paths optional arrays.
|
||||
const nullable: null = utils.getComposedPath({ target: null })
|
||||
type Assert<Condition extends true> = Condition
|
||||
type Complete = Assert<Exclude<keyof typeof source, keyof Utils> extends never ? true : false>
|
||||
type NoInventedExports = Assert<Exclude<keyof Utils, keyof typeof source> extends never ? true : false>
|
||||
export type { Complete, NoInventedExports }
|
||||
export { append, delayed, immediate, nullable, queried, removed, same, style, throttled }
|
||||
Reference in new issue
Block a user