mirror of
https://github.com/zhw2590582/ArtPlayer.git
synced 2026-10-08 19:06:15 -08:00
build: ENG-06 support TS entries and preserve AMD globals
This commit is contained in:
1 parent
771482426f
commit
b53461da1c
23 files changed
+1046
-65
No files matched your search
+9
-18
@@ -2,8 +2,8 @@ import fs from 'node:fs'
|
||||
import path from 'node:path'
|
||||
import process from 'node:process'
|
||||
import cpy from 'cpy'
|
||||
import prompts from 'prompts'
|
||||
import { build as viteBuild } from 'vite'
|
||||
import { getEntryFile, selectProjects } from './projects.js'
|
||||
import { getGlobalName, getProjects, getViteBuildConfig } from './utils.js'
|
||||
|
||||
const projects = getProjects()
|
||||
@@ -28,9 +28,11 @@ async function build(name, targetName, clean = false) {
|
||||
const projectDir = projects[name]
|
||||
const { version } = JSON.parse(fs.readFileSync(path.join(projectDir, 'package.json'), 'utf-8'))
|
||||
const distDir = path.join(projectDir, 'dist')
|
||||
const entryFile = path.join(projectDir, 'src/index.js')
|
||||
const entryFile = getEntryFile(projectDir)
|
||||
|
||||
if (clean && fs.existsSync(distDir)) {
|
||||
if (fs.realpathSync(distDir) !== path.join(fs.realpathSync(projectDir), 'dist'))
|
||||
throw new Error(`Refusing to clean redirected dist directory: ${distDir}`)
|
||||
fs.rmSync(distDir, { recursive: true, force: true })
|
||||
}
|
||||
|
||||
@@ -79,22 +81,11 @@ async function buildProject(name) {
|
||||
}
|
||||
|
||||
async function runBuild() {
|
||||
if (process.argv.includes('all')) {
|
||||
for (const name of Object.keys(projects)) {
|
||||
await buildProject(name)
|
||||
}
|
||||
console.log('✅ Finished building all packages!')
|
||||
}
|
||||
else {
|
||||
const { value } = await prompts({
|
||||
type: 'select',
|
||||
name: 'value',
|
||||
message: 'Which project do you want to build?',
|
||||
choices: Object.keys(projects).map(name => ({ title: name, value: name })),
|
||||
})
|
||||
if (value)
|
||||
await buildProject(value)
|
||||
}
|
||||
const { names } = await selectProjects(projects, 'build')
|
||||
for (const name of names)
|
||||
await buildProject(name)
|
||||
if (names.length)
|
||||
console.log(`✅ Finished building ${names.length} package(s)!`)
|
||||
}
|
||||
|
||||
runBuild().catch((error) => {
|
||||
|
||||
+17
-16
@@ -1,17 +1,18 @@
|
||||
import fs from 'node:fs'
|
||||
import path from 'node:path'
|
||||
import prompts from 'prompts'
|
||||
import process from 'node:process'
|
||||
import servor from 'servor'
|
||||
import openBrowser from 'servor/utils/openBrowser.js'
|
||||
import { build as viteBuild } from 'vite'
|
||||
import { getEntryFile, selectProjects } from './projects.js'
|
||||
import { createRebuildQueue } from './rebuild.js'
|
||||
import { getGlobalName, getProjects, getViteBuildConfig } from './utils.js'
|
||||
|
||||
const projects = getProjects()
|
||||
|
||||
async function develop(name) {
|
||||
async function develop(name, open) {
|
||||
const projectPath = projects[name]
|
||||
const uncompiledPath = path.resolve(`docs/uncompiled/${name}`)
|
||||
const entryFile = path.join(projectPath, 'src/index.js')
|
||||
let browserOpened = false
|
||||
|
||||
const { url } = await servor({
|
||||
@@ -27,7 +28,7 @@ async function develop(name) {
|
||||
const startTime = Date.now()
|
||||
try {
|
||||
const config = getViteBuildConfig({
|
||||
entry: entryFile,
|
||||
entry: getEntryFile(projectPath),
|
||||
outDir: uncompiledPath,
|
||||
name: getGlobalName(name),
|
||||
format: 'iife',
|
||||
@@ -40,7 +41,7 @@ async function develop(name) {
|
||||
await viteBuild({ root: projectPath, ...config })
|
||||
console.log(`[${name}] ✅ Built in ${Date.now() - startTime}ms`)
|
||||
|
||||
if (!browserOpened) {
|
||||
if (open && !browserOpened) {
|
||||
browserOpened = true
|
||||
openBrowser(url)
|
||||
}
|
||||
@@ -50,7 +51,9 @@ async function develop(name) {
|
||||
}
|
||||
}
|
||||
|
||||
await buildBundle()
|
||||
const rebuild = createRebuildQueue(buildBundle)
|
||||
await rebuild()
|
||||
console.log(`[${name}] Demo: ${url}/?libs=./uncompiled/${name}/index.js`)
|
||||
|
||||
const srcPath = path.join(projectPath, 'src')
|
||||
console.log(`[${name}] 👀 Watching ${srcPath}...`)
|
||||
@@ -58,18 +61,16 @@ async function develop(name) {
|
||||
fs.watch(srcPath, { recursive: true }, async (_, filename) => {
|
||||
if (filename) {
|
||||
console.log(`[${name}] 📝 Changed: ${filename}`)
|
||||
await buildBundle()
|
||||
await rebuild()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
(async () => {
|
||||
const { value } = await prompts({
|
||||
type: 'select',
|
||||
name: 'value',
|
||||
message: 'Which project do you want to develop?',
|
||||
choices: Object.keys(projects).map(name => ({ title: name, value: name })),
|
||||
})
|
||||
if (value)
|
||||
await develop(value)
|
||||
})()
|
||||
const { names, open } = await selectProjects(projects, 'dev')
|
||||
if (names.length)
|
||||
await develop(names[0], open)
|
||||
})().catch((error) => {
|
||||
console.error('❌ Development server failed:', error)
|
||||
process.exitCode = 1
|
||||
})
|
||||
@@ -0,0 +1,65 @@
|
||||
import fs from 'node:fs'
|
||||
import path from 'node:path'
|
||||
import process from 'node:process'
|
||||
import prompts from 'prompts'
|
||||
|
||||
export function getProjects(root = process.cwd()) {
|
||||
const packages = path.join(root, 'packages')
|
||||
return Object.fromEntries(fs.readdirSync(packages).sort()
|
||||
.filter(name => name !== 'artplayer-vitepress' && fs.existsSync(path.join(packages, name, 'package.json')))
|
||||
.map(name => [name, path.join(packages, name)]))
|
||||
}
|
||||
|
||||
export function getEntryFile(projectDir) {
|
||||
const entries = ['index.js', 'index.ts'].map(name => path.join(projectDir, 'src', name)).filter(file => fs.existsSync(file) && fs.statSync(file).isFile())
|
||||
if (entries.length !== 1)
|
||||
throw new Error(`${path.basename(projectDir)} must have exactly one src/index.js or src/index.ts; found ${entries.length}`)
|
||||
return entries[0]
|
||||
}
|
||||
|
||||
export function parseProjects(args, projects, command) {
|
||||
const names = []
|
||||
let open = true
|
||||
for (const arg of args) {
|
||||
if (arg === '--no-open' && command === 'dev')
|
||||
open = false
|
||||
else if (arg === '--help')
|
||||
return { help: true, names: [], open }
|
||||
else if (arg !== '--')
|
||||
names.push(arg)
|
||||
}
|
||||
if (names.includes('all')) {
|
||||
if (command !== 'build' || names.length !== 1)
|
||||
throw new Error('Use build all on its own; dev accepts one package')
|
||||
return { names: Object.keys(projects), open }
|
||||
}
|
||||
for (const name of names) {
|
||||
if (!Object.hasOwn(projects, name))
|
||||
throw new Error(`Unknown package: ${name}. Use yarn ${command} --help`)
|
||||
}
|
||||
if (command === 'dev' && names.length > 1)
|
||||
throw new Error('dev accepts one package')
|
||||
return { names: [...new Set(names)], open }
|
||||
}
|
||||
|
||||
export async function selectProjects(projects, command) {
|
||||
const selection = parseProjects(process.argv.slice(2), projects, command)
|
||||
if (selection.help) {
|
||||
console.log(command === 'build' ? 'Usage: yarn build [all | package ...]' : 'Usage: yarn dev [package] [--no-open]')
|
||||
return selection
|
||||
}
|
||||
if (!selection.names.length) {
|
||||
if (!process.stdin.isTTY)
|
||||
throw new Error(`Select a package explicitly in non-interactive mode: yarn ${command} artplayer`)
|
||||
const { value } = await prompts({
|
||||
type: 'select',
|
||||
name: 'value',
|
||||
message: `Which project do you want to ${command === 'build' ? 'build' : 'develop'}?`,
|
||||
choices: Object.keys(projects).map(name => ({ title: name, value: name })),
|
||||
})
|
||||
selection.names = value ? [value] : []
|
||||
}
|
||||
for (const name of selection.names)
|
||||
getEntryFile(projects[name])
|
||||
return selection
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
export function createRebuildQueue(build) {
|
||||
let pending = false
|
||||
let active
|
||||
return () => {
|
||||
pending = true
|
||||
if (!active) {
|
||||
active = Promise.resolve().then(async () => {
|
||||
while (pending) {
|
||||
pending = false
|
||||
await build()
|
||||
}
|
||||
}).finally(() => { active = undefined })
|
||||
}
|
||||
return active
|
||||
}
|
||||
}
|
||||
+13
-25
@@ -1,20 +1,4 @@
|
||||
import fs from 'node:fs'
|
||||
import path from 'node:path'
|
||||
import process from 'node:process'
|
||||
import { glob } from 'glob'
|
||||
|
||||
export function getProjects() {
|
||||
return glob
|
||||
.sync('packages/*')
|
||||
.sort()
|
||||
.filter(item => !item.endsWith('artplayer-vitepress'))
|
||||
.filter(item => fs.existsSync(path.join(item, 'package.json')))
|
||||
.reduce((result, item) => {
|
||||
const name = item.split(/\/|\\/g).pop()
|
||||
result[name] = path.resolve(process.cwd(), item)
|
||||
return result
|
||||
}, {})
|
||||
}
|
||||
export { getProjects } from './projects.js'
|
||||
|
||||
// Convert kebab-case to PascalCase: artplayer-plugin-ads -> ArtplayerPluginAds
|
||||
export function toPascalCase(name) {
|
||||
@@ -71,7 +55,7 @@ export function getViteBuildConfig(options) {
|
||||
plugins: banner
|
||||
? [{
|
||||
name: 'add-banner-and-global',
|
||||
generateBundle(_, bundle) {
|
||||
generateBundle(outputOptions, bundle) {
|
||||
for (const chunk of Object.values(bundle)) {
|
||||
if (chunk.type === 'chunk') {
|
||||
// Remove any inline worker banners (inside template literals)
|
||||
@@ -83,14 +67,18 @@ export function getViteBuildConfig(options) {
|
||||
}
|
||||
// For UMD format, modify the wrapper to always expose global
|
||||
// even when AMD loader is present (fix for RequireJS environments)
|
||||
if (format === 'umd') {
|
||||
// Match both original and minified UMD patterns:
|
||||
// Original: typeof define === 'function' && define.amd ? define(factory) :
|
||||
// Minified: "function"===typeof define&&define.amd?define(e):
|
||||
// Change to: ... ? (global.Name = factory(), define(function() { return global.Name; })) :
|
||||
if (outputOptions.format === 'umd') {
|
||||
const wrapper = code.replace(/^\/\*![\s\S]*?\*\/\s*/, '').match(/^[!(\s]*function\s*\(\s*([\w$]+)\s*,\s*([\w$]+)\s*\)/)
|
||||
if (!wrapper)
|
||||
throw new Error(`Unrecognized UMD wrapper for ${name}`)
|
||||
const global = wrapper[1]
|
||||
const amd = /["']function["']\s*===?\s*typeof define\s*&&\s*define\.amd\s*\?\s*define\(([\w$]+)\)\s*:/
|
||||
if (!amd.test(code))
|
||||
throw new Error(`Unrecognized AMD branch for ${name}`)
|
||||
// Keep AMD and the global export identical regardless of minified parameter names.
|
||||
code = code.replace(
|
||||
/["']function["']\s*===?\s*typeof define\s*&&\s*define\.amd\s*\?\s*define\((\w+)\)\s*:/,
|
||||
`"function"==typeof define&&define.amd?(t.${name}=$1(),define(function(){return t.${name}})):`,
|
||||
amd,
|
||||
`"function"==typeof define&&define.amd?(${global}.${name}=$1(),define(function(){return ${global}.${name}})):`,
|
||||
)
|
||||
}
|
||||
chunk.code = code
|
||||
|
||||
Reference in new issue
Block a user