import assert from 'node:assert/strict' import { execFileSync } from 'node:child_process' import fs from 'node:fs' import path from 'node:path' import process from 'node:process' import { chromium, firefox, webkit } from '@playwright/test' import vue from '@vitejs/plugin-vue' import { build, preview } from 'vite' import { packedFiles } from '../../scripts/package-check.mjs' import { consumerDirectory, removeConsumer, run, workspace, writeJson } from '../../scripts/package-consumer.mjs' import { hash, readMember } from './releases.mjs' assert.equal(process.version.slice(1), fs.readFileSync(path.join(workspace, '.node-version'), 'utf8').trim()) assert.equal(process.env.npm_config_user_agent?.split(' ')[0], 'yarn/1.22.22') assert(process.argv.slice(2).every(arg => arg === '--before')) const yarn = process.env.npm_execpath assert(yarn && fs.existsSync(yarn)) const before = process.argv.includes('--before') const output = fs.mkdtempSync(path.join(workspace, 'refactor/.cache/vue-consumer-')) const consumer = consumerDirectory() const report = { task: 'EX-02', before, node: process.version, source: execFileSync('git', ['rev-parse', 'HEAD'], { cwd: workspace, encoding: 'utf8' }).trim(), packages: [], browsers: [], passed: false } console.log(`Vue consumer evidence: ${output}`) try { const dependencies = {} for (const name of ['artplayer', 'artplayer-plugin-danmuku', 'artplayer-plugin-document-pip']) { const archive = path.join(output, `${name}.tgz`) fs.writeFileSync(path.join(output, `${name}-pack.log`), run([yarn, 'pack', '--filename', archive], path.join(workspace, 'packages', name))) dependencies[name] = `file:${archive.replaceAll('\\', '/')}` report.packages.push({ name, archive, sha256: hash(fs.readFileSync(archive)) }) } const manifest = JSON.parse(fs.readFileSync(path.join(workspace, 'package.json'), 'utf8')) dependencies.vue = manifest.devDependencies.vue const devDependencies = { 'vue-tsc': manifest.devDependencies['vue-tsc'], 'typescript': manifest.devDependencies.typescript } writeJson(path.join(consumer, 'package.json'), { name: 'vue-artplayer-isolated-consumer', private: true, type: 'module', dependencies, devDependencies }) fs.writeFileSync(path.join(output, 'install.log'), run([yarn, 'install', '--offline', '--ignore-scripts', '--non-interactive'], consumer)) const lock = fs.readFileSync(path.join(consumer, 'yarn.lock')) fs.writeFileSync(path.join(output, 'consumer-yarn.lock'), lock) fs.writeFileSync(path.join(output, 'frozen.log'), run([yarn, 'install', '--offline', '--frozen-lockfile', '--force', '--ignore-scripts', '--non-interactive'], consumer)) assert.deepEqual(fs.readFileSync(path.join(consumer, 'yarn.lock')), lock) for (const pkg of report.packages) { for (const member of packedFiles(pkg.archive)) assert.deepEqual(fs.readFileSync(path.join(consumer, 'node_modules', pkg.name, member.replace(/^package\//, ''))), readMember(pkg.archive, member)) } const example = path.join(workspace, 'example/vue.js') fs.cpSync(path.join(example, 'src'), path.join(consumer, 'src'), { recursive: true }) for (const file of ['index.html', 'tsconfig.json']) fs.copyFileSync(path.join(example, file), path.join(consumer, file)) if (before) { const old = execFileSync('git', ['show', '4df96893649ee0d3ffe1504af0609c3c6e023409:example/vue.js/src/Player.vue'], { cwd: workspace }) fs.writeFileSync(path.join(consumer, 'src/Player.vue'), old) report.beforeWrapper = hash(old) } for (const file of ['Lifecycle.vue', 'entry.ts', 'LegacyConsumer.vue', 'legacy-entry.js', 'consumer.ts']) fs.copyFileSync(path.join(workspace, 'test/vue', file), path.join(consumer, file)) for (const [file, entry] of [['harness.html', 'entry.ts'], ['legacy.html', 'legacy-entry.js']]) fs.writeFileSync(path.join(consumer, file), `
`) fs.mkdirSync(path.join(consumer, 'public')) const media = fs.readFileSync(path.join(workspace, 'test/browser/media/pattern.mp4')) fs.writeFileSync(path.join(consumer, 'public/pattern.mp4'), media) fs.copyFileSync(path.join(example, 'public/favicon.ico'), path.join(consumer, 'public/favicon.ico')) report.media = { file: 'test/browser/media/pattern.mp4', sha256: hash(media) } report.inputs = Object.fromEntries(['src/Player.vue', 'src/player-options.ts', 'src/App.vue', 'src/main.ts', 'index.html', 'Lifecycle.vue', 'entry.ts', 'LegacyConsumer.vue', 'legacy-entry.js', 'consumer.ts'].map(file => [file, hash(fs.readFileSync(path.join(consumer, file)))])) const config = JSON.parse(fs.readFileSync(path.join(consumer, 'tsconfig.json'), 'utf8')) config.include = ['src', 'Lifecycle.vue', 'entry.ts', 'consumer.ts'] writeJson(path.join(consumer, 'tsconfig.json'), config) const typeLog = run([path.join(consumer, 'node_modules/vue-tsc/bin/vue-tsc.js'), '-p', 'tsconfig.json', '--noEmit', '--listFiles', '--pretty', 'false'], consumer) fs.writeFileSync(path.join(output, 'types.log'), typeLog) const files = typeLog.trim().split(/\r?\n/) for (const file of files) { const actual = fs.realpathSync(file) assert(actual.startsWith(fs.realpathSync(consumer) + path.sep), `Types escaped consumer: ${actual}`) } report.types = { vueTsc: manifest.devDependencies['vue-tsc'], typescript: manifest.devDependencies.typescript, vue: dependencies.vue, files: files.length, isolated: true, negativeCases: 3 } for (const mode of ['development', 'production']) { const outDir = path.join(output, mode) await build({ root: consumer, configFile: false, plugins: [vue({ isProduction: mode === 'production' })], mode, define: { 'process.env.NODE_ENV': JSON.stringify(mode) }, build: { outDir, emptyOutDir: false, rollupOptions: { input: { app: path.join(consumer, 'index.html'), harness: path.join(consumer, 'harness.html'), legacy: path.join(consumer, 'legacy.html') } } } }) const server = await preview({ root: consumer, configFile: false, build: { outDir }, preview: { host: '127.0.0.1', port: 0, open: false } }) const base = `http://127.0.0.1:${server.httpServer.address().port}` try { for (const [engine, type] of Object.entries({ chromium, firefox, webkit })) { const browser = await type.launch() const row = { mode, engine, version: browser.version(), checks: [], passed: false } report.browsers.push(row) const page = await browser.newPage() await page.addInitScript(() => { const active = new Set() let created = 0 const NativeWorker = window.Worker window.Worker = class extends NativeWorker { constructor(url, options) { super(url, options) active.add(this) created++ } terminate() { active.delete(this) return super.terminate() } } window.workerProbe = () => ({ active: active.size, created }) }) const unexpected = [] page.on('pageerror', error => unexpected.push(error.message)) await page.route('https://artplayer.org/assets/sample/danmuku.xml', route => route.fulfill({ contentType: 'text/xml', body: '