mirror of
https://github.com/zhw2590582/ArtPlayer.git
synced 2026-10-11 22:06:17 -08:00
refactor(dash): [PKG-DASH-04] migrate strict TypeScript and compatible declarations
This commit is contained in:
1 parent
7de04573b6
commit
6840544e0e
38 files changed
+1744
-102
No files matched your search
@@ -0,0 +1,67 @@
|
||||
import fs from 'node:fs'
|
||||
import process from 'node:process'
|
||||
import { hash } from '../../refactor/scripts/releases.mjs'
|
||||
import { compilePackage } from '../helpers/load.js'
|
||||
import { expect, test } from './fixtures.js'
|
||||
|
||||
test('Monaco consumes DASH global types and executes the emitted plugin factory', async ({ page }, testInfo) => {
|
||||
const code = process.env.ARTPLAYER_DASH_ARTIFACT
|
||||
? fs.readFileSync(process.env.ARTPLAYER_DASH_ARTIFACT, 'utf8')
|
||||
: await compilePackage('artplayer-plugin-dash-control', 'umd')
|
||||
const declarations = Object.fromEntries(['artplayer', 'artplayer-plugin-dash-control'].map(name => [name, hash(fs.readFileSync(new URL(`../../docs/assets/ts/${name}.d.ts`, import.meta.url)))]))
|
||||
const inputs = { candidate: process.env.ARTPLAYER_DASH_ARTIFACT || 'workspace source build', sha256: hash(code), declarations }
|
||||
await testInfo.attach('dash-editor-inputs', { contentType: 'application/json', body: JSON.stringify(inputs) })
|
||||
await page.goto('/test/player.html?core=candidate&chapter=published')
|
||||
await page.addScriptTag({ content: code })
|
||||
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 names = ['artplayer', 'artplayer-plugin-dash-control']
|
||||
const libraries = await Promise.all(names.map(async (name) => {
|
||||
const uri = `file:///${name}.d.ts`
|
||||
const source = await (await fetch(`/assets/ts/${name}.d.ts`)).text()
|
||||
return { uri, handle: types.typescriptDefaults.addExtraLib(source, uri) }
|
||||
}))
|
||||
const model = api.editor.createModel(`
|
||||
const dashOption: artplayerPluginDashControl.Option = {
|
||||
quality: { getName: level => String(level.height) },
|
||||
audio: { getName: track => track.lang || String(track.id) },
|
||||
}
|
||||
const oldQuality: NonNullable<Parameters<typeof artplayerPluginDashControl>[0]['quality']> = { getName: (value: object) => String(value) }
|
||||
const typedFactory: (art: Artplayer) => artplayerPluginDashControl.Result = artplayerPluginDashControl(dashOption)
|
||||
const defaultFactory = artplayerPluginDashControl()
|
||||
`, 'typescript', api.Uri.parse('file:///dash-consumer.ts'))
|
||||
const invalid = api.editor.createModel('artplayerPluginDashControl({ quality: { getName: level => level.height } })', 'typescript', api.Uri.parse('file:///dash-invalid.ts'))
|
||||
try {
|
||||
const worker = await (await types.getTypeScriptWorker())(model.uri, invalid.uri)
|
||||
const syntax = await worker.getSyntacticDiagnostics(model.uri.toString())
|
||||
const semantic = await worker.getSemanticDiagnostics(model.uri.toString())
|
||||
const declarations = (await Promise.all(libraries.map(lib => worker.getSemanticDiagnostics(lib.uri)))).flat()
|
||||
const bad = await worker.getSemanticDiagnostics(invalid.uri.toString())
|
||||
if (syntax.length || semantic.length || declarations.length)
|
||||
return { syntax, semantic, declarations, invalid: bad.map(item => item.code), executed: false }
|
||||
const output = await worker.getEmitOutput(model.uri.toString())
|
||||
const script = output.outputFiles.find(file => file.name.endsWith('.js'))
|
||||
if (!script)
|
||||
throw new Error('Monaco did not emit the DASH fixture')
|
||||
// eslint-disable-next-line no-new-func -- Exercise the local editor Run action with its real emitted code.
|
||||
const executed = new Function(`${script.text}\nreturn typeof typedFactory === 'function' && typeof defaultFactory === 'function'`)()
|
||||
return { syntax, semantic, declarations, invalid: bad.map(item => item.code), executed }
|
||||
}
|
||||
finally {
|
||||
model.dispose()
|
||||
invalid.dispose()
|
||||
for (const library of libraries)
|
||||
library.handle.dispose()
|
||||
}
|
||||
})
|
||||
expect(result.syntax).toEqual([])
|
||||
expect(result.semantic).toEqual([])
|
||||
expect(result.declarations).toEqual([])
|
||||
expect(result.invalid).toEqual([2769])
|
||||
expect(result.executed).toBe(true)
|
||||
})
|
||||
@@ -0,0 +1,43 @@
|
||||
import type { AudioTrack, Config, Option, QualityLevel, Result } from 'artplayer-plugin-dash-control'
|
||||
import Artplayer from 'artplayer'
|
||||
import dashControl from 'artplayer-plugin-dash-control'
|
||||
import legacy from 'artplayer-plugin-dash-control/legacy'
|
||||
|
||||
const option: Option = {
|
||||
quality: { control: true, getName: level => `${level.height}p` },
|
||||
audio: { setting: true, getName: track => track.lang || String(track.id) },
|
||||
}
|
||||
const art = new Artplayer({ container: '#player', url: 'video.mpd', plugins: [dashControl(), dashControl(option), legacy()] })
|
||||
const result: Result = dashControl(option)(art)
|
||||
type HistoricalOption = Parameters<typeof dashControl>[0]
|
||||
const oldQuality: HistoricalOption['quality'] = { getName: (item: object) => String(item) }
|
||||
dashControl({ quality: oldQuality })
|
||||
dashControl(undefined)
|
||||
const done: void = result.update()
|
||||
const objectConfig: Config = { getName: (item: object) => String(item) }
|
||||
dashControl({ quality: objectConfig, audio: objectConfig })
|
||||
const level: QualityLevel = { height: 720, id: 0 }
|
||||
const track: AudioTrack = { id: 'en', lang: 'en' }
|
||||
|
||||
interface CustomLevel { height: number, bandwidth: number }
|
||||
interface CustomTrack { id: number, lang: string, roles: string[] }
|
||||
dashControl({ quality: { getName: (value: CustomLevel) => String(value.bandwidth) }, audio: { getName: (value: CustomTrack) => value.roles.join(',') } })
|
||||
dashControl<CustomLevel, CustomTrack>({ audio: { getName: track => track.lang } })
|
||||
|
||||
// @ts-expect-error Update remains synchronous.
|
||||
const promise: Promise<void> = result.update()
|
||||
// @ts-expect-error Update has no options parameter.
|
||||
result.update({})
|
||||
// @ts-expect-error Plugin name is the original literal.
|
||||
const invalidName: Result['name'] = 'dash'
|
||||
// @ts-expect-error DASH formatters receive only one argument.
|
||||
dashControl({ quality: { getName: (value: QualityLevel, index: number) => String(index) } })
|
||||
// @ts-expect-error Custom formatters must return a string.
|
||||
dashControl({ audio: { getName: () => 123 } })
|
||||
// @ts-expect-error Visibility options stay boolean.
|
||||
legacy({ quality: { control: 'yes' } })
|
||||
// @ts-expect-error Null was never a valid options object.
|
||||
dashControl(null)
|
||||
// @ts-expect-error Default SDK fields cannot silently become any.
|
||||
dashControl({ quality: { getName: level => level.unknownProperty } })
|
||||
void [done, level, track, promise, invalidName]
|
||||
@@ -0,0 +1,9 @@
|
||||
import type PublicFactory from 'artplayer-plugin-dash-control'
|
||||
import factory from '../../packages/artplayer-plugin-dash-control/src/index'
|
||||
|
||||
const compatible: typeof PublicFactory = factory
|
||||
const implementation: typeof factory = compatible
|
||||
implementation({ quality: { getName: level => `${level.height}p` }, audio: { getName: track => track.lang || String(track.id) } })
|
||||
compatible()
|
||||
// @ts-expect-error Source and public declaration reject invalid formatter results.
|
||||
factory({ quality: { getName: () => 1 } })
|
||||
Reference in new issue
Block a user