refactor(iframe): [PKG-IFRAME-04] preserve types across module consumers

This commit is contained in:
Harvey Zhao committed 2026-09-13 01:43:09 +08:00
1 parent 1342de94ef
commit 047e2df945
24 files changed
+1496 -69

No files matched your search

+19 -8
View File
@@ -1,5 +1,7 @@
import assert from 'node:assert/strict'
import fs from 'node:fs'
import path from 'node:path'
import process from 'node:process'
import { ESLint } from 'eslint'
import { glob } from 'glob'
import compat from 'typescript-compat'
@@ -18,12 +20,14 @@ function parsePluginInfo(pluginPath) {
const file = path.basename(pluginPath)
const baseName = file.replace('.d.ts', '')
const name = baseName
.split('-')
.map((word, index) =>
index === 0 ? word : word[0].toUpperCase() + word.slice(1),
)
.join('')
const name = baseName === 'artplayer-tool-iframe'
? 'ArtplayerToolIframe'
: baseName
.split('-')
.map((word, index) =>
index === 0 ? word : word[0].toUpperCase() + word.slice(1),
)
.join('')
return { name, file }
}
@@ -36,14 +40,19 @@ fs.writeFileSync(artplayerTSoutput, code.trim())
console.log(`✨ Built ${artplayerTSoutput}`);
(async function () {
const pluginsTS = glob.sync('packages/artplayer-*-*/types/*.d.ts')
const available = glob.sync('packages/artplayer-*-*/types/*.d.ts')
const selected = process.argv.slice(2)
const packageOf = file => path.basename(path.dirname(path.dirname(file)))
for (const name of selected)
assert(available.some(file => packageOf(file) === name), `Unknown declaration package: ${name}`)
const pluginsTS = selected.length ? available.filter(file => selected.includes(packageOf(file))) : available
const pluginFiles = []
for (let index = 0; index < pluginsTS.length; index++) {
const type = pluginsTS[index]
const { name, file } = parsePluginInfo(type)
const source = String(fs.readFileSync(type))
const semanticPlugin = ['artplayerPluginHlsControl', 'artplayerPluginAudioTrack', 'artplayerPluginDashControl', 'artplayerPluginAds', 'artplayerPluginAmbilight', 'artplayerProxyCanvas', 'artplayerPluginDocumentPip'].includes(name)
const semanticPlugin = ['artplayerPluginHlsControl', 'artplayerPluginAudioTrack', 'artplayerPluginDashControl', 'artplayerPluginAds', 'artplayerPluginAmbilight', 'artplayerProxyCanvas', 'artplayerPluginDocumentPip', 'ArtplayerToolIframe'].includes(name)
const code = semanticPlugin
? generatePluginEditorDeclaration(source, name)
: `${source.replace(reg, '')}\nexport = ${name};\nexport as namespace ${name};\n`
@@ -71,6 +80,8 @@ console.log(`✨ Built ${artplayerTSoutput}`);
const formatter = await eslint.loadFormatter('stylish')
throw new Error(formatter.format(results))
}
if (selected.length)
return
const commonJsPath = path.join('docs/assets/js/common.js')
const commonJsContent = fs.readFileSync(commonJsPath, 'utf-8')
const newLibUris = allFiles.map(file => `'./assets/ts/${file}'`).join(',\n ')
+6 -3
View File
@@ -16,6 +16,7 @@ export function generatePluginEditorDeclaration(code, name) {
const internal = `${name}Definitions`
let exported = false
let callable = false
let classExport = false
for (const node of source.statements) {
if (ts.isImportDeclaration(node)) {
assert(node.importClause?.isTypeOnly && !node.importClause.namedBindings && node.importClause.name?.text === 'Artplayer' && node.moduleSpecifier.text === 'artplayer', 'Unsupported plugin editor import')
@@ -26,12 +27,13 @@ export function generatePluginEditorDeclaration(code, name) {
exported = true
continue
}
assert(ts.isInterfaceDeclaration(node) || ts.isTypeAliasDeclaration(node) || ts.isFunctionDeclaration(node), 'Unsupported plugin editor declaration')
assert(ts.isInterfaceDeclaration(node) || ts.isTypeAliasDeclaration(node) || ts.isFunctionDeclaration(node) || ts.isClassDeclaration(node), 'Unsupported plugin editor declaration')
assert(node.name && node.name.text !== internal, 'Invalid plugin declaration name')
definitions.push(factory.replaceModifiers(node, [factory.createModifier(ts.SyntaxKind.ExportKeyword)]))
if (ts.isFunctionDeclaration(node)) {
if (ts.isFunctionDeclaration(node) || ts.isClassDeclaration(node)) {
assert.equal(node.name.text, name, 'Unexpected plugin callable')
callable = true
classExport = ts.isClassDeclaration(node)
continue
}
if (node.modifiers?.some(modifier => modifier.kind === ts.SyntaxKind.ExportKeyword)) {
@@ -43,13 +45,14 @@ export function generatePluginEditorDeclaration(code, name) {
))
}
}
assert(exported && callable, 'Plugin editor requires a default function')
assert(exported && callable, 'Plugin editor requires a default function or class')
const namespace = (identifier, statements) => factory.createModuleDeclaration([factory.createModifier(ts.SyntaxKind.DeclareKeyword)], factory.createIdentifier(identifier), factory.createModuleBlock(statements), ts.NodeFlags.Namespace)
const statements = [
namespace(internal, definitions),
factory.createVariableStatement([factory.createModifier(ts.SyntaxKind.DeclareKeyword)], factory.createVariableDeclarationList([
factory.createVariableDeclaration(name, undefined, factory.createTypeQueryNode(factory.createQualifiedName(factory.createIdentifier(internal), factory.createIdentifier(name)))),
], ts.NodeFlags.Const)),
...(classExport ? [factory.createTypeAliasDeclaration(undefined, name, undefined, factory.createTypeReferenceNode(factory.createQualifiedName(factory.createIdentifier(internal), factory.createIdentifier(name))))] : []),
namespace(name, aliases),
factory.createExportAssignment(undefined, true, factory.createIdentifier(name)),
factory.createNamespaceExportDeclaration(factory.createIdentifier(name)),
+4
View File
@@ -80,6 +80,10 @@ export function runTypechecks() {
assert.deepEqual(checkConsumer(compiler, mode, fs.readFileSync(path.join(root, fixture), 'utf8')), [], `Consumer failed: TS ${compiler.version} ${mode} ${fixture}`)
if (mode === 'nodenext-cjs')
assert.deepEqual(checkConsumer(compiler, mode, fs.readFileSync(path.join(root, 'test/types/commonjs.cts'), 'utf8')), [], 'CommonJS export assignment consumer failed')
for (const fixture of ['test/types/iframe.ts', 'refactor/fixtures/consumers/iframe-workspace.ts'])
assert.deepEqual(checkConsumer(compiler, mode, fs.readFileSync(path.join(root, fixture), 'utf8')), [], `Iframe consumer failed: TS ${compiler.version} ${mode}`)
if (mode === 'nodenext-cjs')
assert.deepEqual(checkConsumer(compiler, mode, fs.readFileSync(path.join(root, 'test/types/iframe-commonjs.cts'), 'utf8')), [], 'Iframe CommonJS constructor consumer failed')
console.log(`Consumers passed: TS ${compiler.version} ${mode}`)
}
const runtimeFixtures = ['test/types/runtime-leaf-consumer.ts', 'test/types/runtime-public.ts', 'test/types/runtime-construction.ts']