mirror of
https://github.com/zhw2590582/ArtPlayer.git
synced 2026-10-10 12:46:15 -08:00
99 lines
5.7 KiB
TypeScript
99 lines
5.7 KiB
TypeScript
import assert from 'node:assert/strict'
|
|
import ts from 'typescript'
|
|
import { moduleIds } from './languages.ts'
|
|
|
|
export interface Contribution { name: string, contrib: string, modulePrefix: string }
|
|
|
|
// Read the fixed metadata without running its browser/CommonJS wrapper.
|
|
export function readContributionMetadata(source: string): Contribution[] {
|
|
const ast = ts.createSourceFile('metadata.js', source, ts.ScriptTarget.Latest, true, ts.ScriptKind.JS)
|
|
const declarations: ts.VariableDeclaration[] = []
|
|
const visit = (node: ts.Node) => {
|
|
if (ts.isVariableDeclaration(node) && ts.isIdentifier(node.name) && node.name.text === 'METADATA')
|
|
declarations.push(node)
|
|
ts.forEachChild(node, visit)
|
|
}
|
|
visit(ast)
|
|
assert.equal(declarations.length, 1, 'Expected one METADATA declaration')
|
|
function property(object: ts.Node | undefined, key: string): ts.Expression {
|
|
assert(object && ts.isObjectLiteralExpression(object), 'Expected metadata object')
|
|
const values = object.properties.filter(item => ts.isPropertyAssignment(item) && (ts.isIdentifier(item.name) || ts.isStringLiteral(item.name)) && item.name.text === key)
|
|
assert.equal(values.length, 1, `Expected one metadata property: ${key}`)
|
|
return (values[0] as ts.PropertyAssignment).initializer
|
|
}
|
|
function literal(object: ts.Node, key: string): string {
|
|
const value = property(object, key)
|
|
assert(ts.isStringLiteral(value), 'Expected literal metadata value')
|
|
return value.text
|
|
}
|
|
const plugins = property(declarations[0]!.initializer, 'PLUGINS')
|
|
assert(ts.isArrayLiteralExpression(plugins), 'Expected plugin array')
|
|
const result = plugins.elements.map((plugin) => {
|
|
const name = literal(plugin, 'name')
|
|
const contrib = literal(plugin, 'contrib')
|
|
const modulePrefix = literal(plugin, 'modulePrefix')
|
|
assert.equal(literal(plugin, 'rootPath'), `./${name}`)
|
|
assert.equal(contrib, `${modulePrefix}/monaco.contribution`)
|
|
for (const type of ['dev', 'min'])
|
|
assert.equal(literal(property(plugin, 'paths'), type), `./release/${type}`)
|
|
return { name, contrib, modulePrefix }
|
|
})
|
|
assert.deepEqual(result.map(item => item.name), ['monaco-typescript', 'monaco-css', 'monaco-json', 'monaco-html', 'monaco-languages'], 'Contribution order changed')
|
|
assert.deepEqual(result.map(item => item.modulePrefix), ['vs/language/typescript', 'vs/language/css', 'vs/language/json', 'vs/language/html', 'vs/basic-languages'], 'Contribution prefix changed')
|
|
return result
|
|
}
|
|
|
|
// The upstream release injects this dependency AFTER per-plugin minification.
|
|
export function injectCoreDependency(source: string, prefix: string): string {
|
|
const pattern = /define\((['"][a-z/\-]+\/fillers\/monaco-editor-core['"]),\[\],/g
|
|
const matches = [...source.matchAll(pattern)]
|
|
assert.equal(matches.length, 1, 'Expected one empty core filler dependency')
|
|
assert.equal(matches[0]![1]!.slice(1, -1), `${prefix}/fillers/monaco-editor-core`, 'Wrong core filler')
|
|
return source.replace(pattern, 'define($1,[\'vs/editor/editor.api\'],')
|
|
}
|
|
|
|
export function assembleContributions(core: string, groups: (Contribution & { source: string })[]): string {
|
|
assert.deepEqual(groups.map(item => item.modulePrefix), ['vs/language/typescript', 'vs/language/css', 'vs/language/json', 'vs/language/html', 'vs/basic-languages'], 'Contribution order changed')
|
|
const entry = '"vs/editor/editor.main"'
|
|
assert.equal(core.split(entry).length, 2, 'Expected unique quoted core entry')
|
|
// Core contains nested UMD calls and addresses AMD names through a string table.
|
|
// Verify the entry's actual define() use, not merely a matching literal string.
|
|
let entries = 0
|
|
const tables = new Map<string, number>()
|
|
const ast = ts.createSourceFile('core.js', core, ts.ScriptTarget.Latest, true, ts.ScriptKind.JS)
|
|
const findTables = (node: ts.Node) => {
|
|
if (ts.isVariableDeclaration(node) && ts.isIdentifier(node.name) && node.initializer && ts.isArrayLiteralExpression(node.initializer)) {
|
|
const index = node.initializer.elements.findIndex(item => ts.isStringLiteral(item) && item.text === 'vs/editor/editor.main')
|
|
if (index >= 0) {
|
|
assert(node.initializer.elements.every(item => ts.isStringLiteral(item)), 'Expected literal core module table')
|
|
tables.set(node.name.text, index)
|
|
}
|
|
}
|
|
ts.forEachChild(node, findTables)
|
|
}
|
|
findTables(ast)
|
|
const visit = (node: ts.Node) => {
|
|
if (ts.isCallExpression(node) && ts.isIdentifier(node.expression) && node.expression.text === 'define') {
|
|
const name = node.arguments[0]
|
|
if (name && ts.isStringLiteral(name) && name.text === 'vs/editor/editor.main')
|
|
entries++
|
|
if (name && ts.isElementAccessExpression(name) && ts.isIdentifier(name.expression) && ts.isNumericLiteral(name.argumentExpression)
|
|
&& tables.get(name.expression.text) === Number(name.argumentExpression.text)) { entries++ }
|
|
}
|
|
ts.forEachChild(node, visit)
|
|
}
|
|
visit(ast)
|
|
assert.equal(entries, 1, 'Missing core AMD entry')
|
|
core = core.replace(entry, '"vs/editor/edcore.main"')
|
|
const extra = groups.map((group) => {
|
|
assert.equal(group.contrib, `${group.modulePrefix}/monaco.contribution`, 'Wrong contribution entry')
|
|
const ids = moduleIds(group.source)
|
|
assert(ids.includes(group.contrib) && ids.every(id => id.startsWith(`${group.modulePrefix}/`)), 'Wrong contribution ownership')
|
|
return injectCoreDependency(group.source, group.modulePrefix)
|
|
})
|
|
extra.push(`define("vs/editor/editor.main", ["vs/editor/edcore.main","${groups.map(group => group.contrib).join('","')}"], function(api) { return api; });`)
|
|
const map = core.lastIndexOf('//# sourceMappingURL=')
|
|
const offset = map === -1 ? core.length : map
|
|
return `${core.slice(0, offset)}\n${extra.join('\n')}\n${core.slice(offset)}`
|
|
}
|