mirror of
https://github.com/zhw2590582/ArtPlayer.git
synced 2026-10-08 10:56:15 -08:00
refactor(tooling): [MOD-02] split library build and development into typed modules
This commit is contained in:
1 parent
28c7abb2bb
commit
401d4cdd65
36 files changed
+1644
-427
No files matched your search
+6
-4
@@ -40,14 +40,14 @@
|
||||
"test:dash-control": "node --test test/dash-control.test.js test/dash-contract.test.js test/dash-lifecycle.test.js test/dash-events.test.js",
|
||||
"dev": "npx cross-env NODE_ENV=development node ./scripts/dev.js",
|
||||
"build": "npx cross-env NODE_ENV=production node ./scripts/build.js",
|
||||
"lint": "eslint \"packages/*/{src,public,types,package.json}\" \"scripts/*.{js,mjs}\" \"packages/artplayer-vitepress/{browser,build}/**/*.ts\" \"scripts/{docs-smoke,editor-declarations,documentation,site-build}/**/*.ts\" \"scripts/plugin/*.{js,ts}\" \"test/*\" \"docs/assets/ts/*\" \"types/*.d.ts\" \"playwright*.config.js\" --no-fix",
|
||||
"lint": "eslint \"packages/*/{src,public,types,package.json}\" \"scripts/*.{js,mjs}\" \"packages/artplayer-vitepress/{browser,build}/**/*.ts\" \"scripts/{docs-smoke,editor-declarations,documentation,site-build,library}/**/*.ts\" \"scripts/plugin/*.{js,ts}\" \"test/*\" \"docs/assets/ts/*\" \"types/*.d.ts\" \"playwright*.config.js\" --no-fix",
|
||||
"build:all": "yarn ci:build && yarn lint",
|
||||
"check:toolchain": "node scripts/check-toolchain.mjs",
|
||||
"lint:fix": "eslint \"packages/*/{src,public,types,package.json}\" \"scripts/*.{js,mjs}\" \"packages/artplayer-vitepress/{browser,build}/**/*.ts\" \"scripts/{docs-smoke,editor-declarations,documentation,site-build}/**/*.ts\" \"scripts/plugin/*.{js,ts}\" \"test/*\" \"docs/assets/ts/*\" \"types/*.d.ts\" \"playwright*.config.js\" --fix",
|
||||
"lint:fix": "eslint \"packages/*/{src,public,types,package.json}\" \"scripts/*.{js,mjs}\" \"packages/artplayer-vitepress/{browser,build}/**/*.ts\" \"scripts/{docs-smoke,editor-declarations,documentation,site-build,library}/**/*.ts\" \"scripts/plugin/*.{js,ts}\" \"test/*\" \"docs/assets/ts/*\" \"types/*.d.ts\" \"playwright*.config.js\" --fix",
|
||||
"check:plan": "node refactor/scripts/plan.mjs --check",
|
||||
"test:node": "yarn test:unit && node --test test/toolchain.test.js test/build-docs.test.js test/package-check.test.js test/declarations.test.js test/editor-types.test.js test/coverage.test.js test/performance-report.test.js test/media-gate.test.js test/ci-summary.test.js test/package-runtime.test.js test/library-build.test.js test/site-loading.test.js test/documentation-pipeline.test.js test/site-build.test.js test/site-markdown.test.js test/site-editor.test.js test/plugin-scaffold.test.js",
|
||||
"test:baseline": "node --test refactor/scripts/*.test.mjs",
|
||||
"ci:check": "yarn check:toolchain --strict && yarn check:commits --report && yarn check:impact --report && yarn check:ci && yarn test:contracts && yarn check:contracts --report && yarn check:plan && yarn lint && yarn typecheck:scaffold && yarn typecheck:docs-tools && yarn check:docs-smoke && yarn check:editor-types && yarn check:llm && yarn typecheck:site-assets && yarn check:site-assets && yarn check:types && yarn typecheck && yarn typecheck:react && yarn lint:react && yarn typecheck:vue && yarn lint:vue && yarn test",
|
||||
"ci:check": "yarn check:toolchain --strict && yarn check:commits --report && yarn check:impact --report && yarn check:ci && yarn test:contracts && yarn check:contracts --report && yarn check:plan && yarn lint && yarn typecheck:library && yarn typecheck:scaffold && yarn typecheck:docs-tools && yarn check:docs-smoke && yarn check:editor-types && yarn check:llm && yarn typecheck:site-assets && yarn check:site-assets && yarn check:types && yarn typecheck && yarn typecheck:react && yarn lint:react && yarn typecheck:vue && yarn lint:vue && yarn test",
|
||||
"ci:build": "yarn build:types && yarn build all && yarn build:i18n && yarn build:ts && yarn build:llm && yarn build:test && yarn build:docs && yarn test:imports",
|
||||
"test:imports": "node --test test/esm.test.js test/i18n.test.js test/ssr.test.js test/asr-distribution.test.js",
|
||||
"typecheck": "node scripts/typecheck.mjs",
|
||||
@@ -129,7 +129,9 @@
|
||||
"test:site-build": "node --test test/site-build.test.js test/build-docs.test.js test/site-markdown.test.js",
|
||||
"test:site-editor": "node --test test/site-editor.test.js",
|
||||
"typecheck:scaffold": "node node_modules/typescript/bin/tsc -p scripts/tsconfig.scaffold.json --noEmit",
|
||||
"test:scaffold": "node --test test/plugin-scaffold.test.js"
|
||||
"test:scaffold": "node --test test/plugin-scaffold.test.js",
|
||||
"typecheck:library": "node node_modules/typescript/bin/tsc -p scripts/tsconfig.library.json --noEmit",
|
||||
"test:library": "node --test refactor/scripts/build.test.mjs test/library-build.test.js test/performance-report.test.js test/plugin-scaffold.test.js"
|
||||
},
|
||||
"browserslist": "last 1 Chrome version",
|
||||
"devDependencies": {
|
||||
|
||||
@@ -0,0 +1,736 @@
|
||||
{
|
||||
"task": "MOD-02",
|
||||
"date": "2026-09-14",
|
||||
"environment": {
|
||||
"os": "Windows",
|
||||
"node": "24.21.0",
|
||||
"yarn": "1.22.22",
|
||||
"typescript": "5.9.3",
|
||||
"browsers": {
|
||||
"chromium": "153.0.8010.12",
|
||||
"firefox": "155.0",
|
||||
"webkit": "26.6"
|
||||
}
|
||||
},
|
||||
"results": {
|
||||
"library": {
|
||||
"passed": 18,
|
||||
"failed": 0
|
||||
},
|
||||
"ci": {
|
||||
"passed": 50,
|
||||
"failed": 0
|
||||
},
|
||||
"browser": {
|
||||
"passed": 3,
|
||||
"failed": 0,
|
||||
"skipped": 0,
|
||||
"report": "refactor/.cache/library-browser-final/report.json"
|
||||
},
|
||||
"strictTypes": "passed",
|
||||
"docsTypes": "passed",
|
||||
"toolchain": "passed",
|
||||
"lint": {
|
||||
"errors": 0,
|
||||
"existingWarnings": 1
|
||||
},
|
||||
"baseline": {
|
||||
"passed": 522,
|
||||
"failed": 0,
|
||||
"skipped": 0
|
||||
}
|
||||
},
|
||||
"wholeBuildComparison": {
|
||||
"task": "MOD-02",
|
||||
"baseline": "28c7abb2bb8dfb7c84acfc7c5ec05c5882623069",
|
||||
"node": "v24.21.0",
|
||||
"names": [
|
||||
"artplayer",
|
||||
"artplayer-plugin-ads",
|
||||
"artplayer-plugin-ambilight",
|
||||
"artplayer-plugin-asr",
|
||||
"artplayer-plugin-audio-track",
|
||||
"artplayer-plugin-auto-thumbnail",
|
||||
"artplayer-plugin-chapter",
|
||||
"artplayer-plugin-chromecast",
|
||||
"artplayer-plugin-danmuku",
|
||||
"artplayer-plugin-danmuku-mask",
|
||||
"artplayer-plugin-dash-control",
|
||||
"artplayer-plugin-document-pip",
|
||||
"artplayer-plugin-hls-control",
|
||||
"artplayer-plugin-jassub",
|
||||
"artplayer-plugin-multiple-subtitles",
|
||||
"artplayer-plugin-vast",
|
||||
"artplayer-plugin-vtt-thumbnail",
|
||||
"artplayer-proxy-canvas",
|
||||
"artplayer-proxy-mediabunny",
|
||||
"artplayer-tool-iframe",
|
||||
"artplayer-tool-thumbnail"
|
||||
],
|
||||
"before": [
|
||||
{
|
||||
"path": "packages/artplayer/dist/artplayer.js",
|
||||
"sha256": "6dc8ca6d6eb34aa8a056cbc31328efd634530b3c5c60f0014cb7ce719fabc335"
|
||||
},
|
||||
{
|
||||
"path": "packages/artplayer/dist/artplayer.legacy.js",
|
||||
"sha256": "80f74a8a39df4baf64cc00c65d21cd57c011bfcdead0da38eea077fbc67c5c04"
|
||||
},
|
||||
{
|
||||
"path": "packages/artplayer/dist/artplayer.mjs",
|
||||
"sha256": "432e2b0ad92009a09743e020bf7c928c8b8f18a52496622b16259ec9ab20059f"
|
||||
},
|
||||
{
|
||||
"path": "packages/artplayer-plugin-ads/dist/artplayer-plugin-ads.js",
|
||||
"sha256": "1429541242f49c4d558eda242a63b6e57a876c2dba06ce9f631d71d315028023"
|
||||
},
|
||||
{
|
||||
"path": "packages/artplayer-plugin-ads/dist/artplayer-plugin-ads.legacy.js",
|
||||
"sha256": "4f64ac015e0850b71f51cf01d9fc6f238c95414730c71e91d1240c40e28499d8"
|
||||
},
|
||||
{
|
||||
"path": "packages/artplayer-plugin-ads/dist/artplayer-plugin-ads.mjs",
|
||||
"sha256": "8ad7df8145b2514467cac0f733151619c36fa965e620df575c0779604372503f"
|
||||
},
|
||||
{
|
||||
"path": "packages/artplayer-plugin-ambilight/dist/artplayer-plugin-ambilight.js",
|
||||
"sha256": "21ee5227f26cfff6e82ee6932f6eafc9741fa3be0ec2f5252a47b53ceea14599"
|
||||
},
|
||||
{
|
||||
"path": "packages/artplayer-plugin-ambilight/dist/artplayer-plugin-ambilight.legacy.js",
|
||||
"sha256": "c3ed1a54e44a0202811d70378cb19edf87b1ebd010d05fe96ebe64d0b4301d5d"
|
||||
},
|
||||
{
|
||||
"path": "packages/artplayer-plugin-ambilight/dist/artplayer-plugin-ambilight.mjs",
|
||||
"sha256": "ef126959819c4973da27dbb1e60ccccc54386c23f8c7750ce1ba4d38f306aaaa"
|
||||
},
|
||||
{
|
||||
"path": "packages/artplayer-plugin-asr/dist/artplayer-plugin-asr.js",
|
||||
"sha256": "06b386f6a4744801f4153944ed6e2bd37822a85e177aa2a0d800c30002ef87bf"
|
||||
},
|
||||
{
|
||||
"path": "packages/artplayer-plugin-asr/dist/artplayer-plugin-asr.legacy.js",
|
||||
"sha256": "639ae9bdede0e26c8af0251b9c1ed12e7fba9e0b6b79cb0556290741040f9697"
|
||||
},
|
||||
{
|
||||
"path": "packages/artplayer-plugin-asr/dist/artplayer-plugin-asr.mjs",
|
||||
"sha256": "1b7bac676d2c4976046b08fd7c1ec25724ff24952f5d93fc546e219466d975d8"
|
||||
},
|
||||
{
|
||||
"path": "packages/artplayer-plugin-audio-track/dist/artplayer-plugin-audio-track.js",
|
||||
"sha256": "23b10856628c5568f1aa7ad88e926c7da8d96edfadf6df872777062a78a4b4be"
|
||||
},
|
||||
{
|
||||
"path": "packages/artplayer-plugin-audio-track/dist/artplayer-plugin-audio-track.legacy.js",
|
||||
"sha256": "41b94e84e3d3729b9373cb13de2f64d5ea30b4c36627f38583bf4c3c9bc2160c"
|
||||
},
|
||||
{
|
||||
"path": "packages/artplayer-plugin-audio-track/dist/artplayer-plugin-audio-track.mjs",
|
||||
"sha256": "9caa5a6e11a2e777760dd2835426842a87d04fb081ea2e7d48c7229035fd89a9"
|
||||
},
|
||||
{
|
||||
"path": "packages/artplayer-plugin-auto-thumbnail/dist/artplayer-plugin-auto-thumbnail.js",
|
||||
"sha256": "430ac17e9025d3cc6bf37bd83316db5067bf438b30c2727b43c3e5fc4910e1d0"
|
||||
},
|
||||
{
|
||||
"path": "packages/artplayer-plugin-auto-thumbnail/dist/artplayer-plugin-auto-thumbnail.legacy.js",
|
||||
"sha256": "efb30986ffc6a28859ca37126af82824601f25f0a42f7ea2c75a41f3da2c50ef"
|
||||
},
|
||||
{
|
||||
"path": "packages/artplayer-plugin-auto-thumbnail/dist/artplayer-plugin-auto-thumbnail.mjs",
|
||||
"sha256": "6080dd90bde239515646899bdd8e71d17de24119523e4883025fbf39b1a6a302"
|
||||
},
|
||||
{
|
||||
"path": "packages/artplayer-plugin-chapter/dist/artplayer-plugin-chapter.js",
|
||||
"sha256": "179c3ea90ddc85da27b46f000110e32d464dfa5c8c394e98316dc1b75d6f2a72"
|
||||
},
|
||||
{
|
||||
"path": "packages/artplayer-plugin-chapter/dist/artplayer-plugin-chapter.legacy.js",
|
||||
"sha256": "dade919ed044cbf46ac16e505f538f86a86edccf177da2e14d79d111559d4bef"
|
||||
},
|
||||
{
|
||||
"path": "packages/artplayer-plugin-chapter/dist/artplayer-plugin-chapter.mjs",
|
||||
"sha256": "754fbcfc60add74d6b6ad3bc8db253df3eb14ace13ac8a5439be0592bd5512d0"
|
||||
},
|
||||
{
|
||||
"path": "packages/artplayer-plugin-chromecast/dist/artplayer-plugin-chromecast.js",
|
||||
"sha256": "80b12b3081086488c9d80f03979c336d4a6fdb44dca298e218dfeaaadb295d00"
|
||||
},
|
||||
{
|
||||
"path": "packages/artplayer-plugin-chromecast/dist/artplayer-plugin-chromecast.legacy.js",
|
||||
"sha256": "b21a30852403914a3bf3defc89aff11f2cc23b044ed68ccf667164b477f04172"
|
||||
},
|
||||
{
|
||||
"path": "packages/artplayer-plugin-chromecast/dist/artplayer-plugin-chromecast.mjs",
|
||||
"sha256": "68d81fcab469f74fe9d9740842d989e10144f7ff28c7ade91ed22adee90f44d7"
|
||||
},
|
||||
{
|
||||
"path": "packages/artplayer-plugin-danmuku/dist/artplayer-plugin-danmuku.js",
|
||||
"sha256": "133b7412e1a468b34f4f7ab3779958259f13899e037833029931f02b0833d088"
|
||||
},
|
||||
{
|
||||
"path": "packages/artplayer-plugin-danmuku/dist/artplayer-plugin-danmuku.legacy.js",
|
||||
"sha256": "9f0cb9969419a7d0c02d54790c423353b83f775acd95fbdbaeeb29a7fc42c816"
|
||||
},
|
||||
{
|
||||
"path": "packages/artplayer-plugin-danmuku/dist/artplayer-plugin-danmuku.mjs",
|
||||
"sha256": "356406cda2e3e47376a87c939b76fb7c55f86d7c3829127da625b01241c67a2f"
|
||||
},
|
||||
{
|
||||
"path": "packages/artplayer-plugin-danmuku-mask/dist/artplayer-plugin-danmuku-mask.js",
|
||||
"sha256": "406a8c28207d386cffe1e61dc79afd8a6a9d5cbd1dc8e1661b4febbf48182f55"
|
||||
},
|
||||
{
|
||||
"path": "packages/artplayer-plugin-danmuku-mask/dist/artplayer-plugin-danmuku-mask.legacy.js",
|
||||
"sha256": "62d61dcedd3e90fa480589c2f1e3168c97635232c98ffe8c25c7918a2f3c66db"
|
||||
},
|
||||
{
|
||||
"path": "packages/artplayer-plugin-danmuku-mask/dist/artplayer-plugin-danmuku-mask.mjs",
|
||||
"sha256": "51936576383129170125b33d2983858e0841ea507368970f6d154bc9d78583b1"
|
||||
},
|
||||
{
|
||||
"path": "packages/artplayer-plugin-dash-control/dist/artplayer-plugin-dash-control.js",
|
||||
"sha256": "a594009be5c17406844f817c5a79930c6ed60add4a5ef0578783f163a1bae012"
|
||||
},
|
||||
{
|
||||
"path": "packages/artplayer-plugin-dash-control/dist/artplayer-plugin-dash-control.legacy.js",
|
||||
"sha256": "b00a837a99f7a7cccd415af335cbf0c68f1e4267fa7cfd8d91e945e3dc634151"
|
||||
},
|
||||
{
|
||||
"path": "packages/artplayer-plugin-dash-control/dist/artplayer-plugin-dash-control.mjs",
|
||||
"sha256": "0c5a7e4d84e9641778e0a22ff9c78db9bd4ac2f08f51358519443046a528fb93"
|
||||
},
|
||||
{
|
||||
"path": "packages/artplayer-plugin-document-pip/dist/artplayer-plugin-document-pip.js",
|
||||
"sha256": "56440aad3ed06be55ec3a57d781f7de239e9aecf40a067e88f2cffcd01a86d6a"
|
||||
},
|
||||
{
|
||||
"path": "packages/artplayer-plugin-document-pip/dist/artplayer-plugin-document-pip.legacy.js",
|
||||
"sha256": "67dd14de3cc16a04ae6c1938dcba61b1355f4a15e5fa06f62a41b18d14d4d356"
|
||||
},
|
||||
{
|
||||
"path": "packages/artplayer-plugin-document-pip/dist/artplayer-plugin-document-pip.mjs",
|
||||
"sha256": "541b4ad6e1cbadd9156ee1f317559c97b9fddae37668ef32046f1fa47c392ec2"
|
||||
},
|
||||
{
|
||||
"path": "packages/artplayer-plugin-hls-control/dist/artplayer-plugin-hls-control.js",
|
||||
"sha256": "8e163d4ab6734cbc45e7f539de7db644c7faf36d0318469a86d300bf6365cca6"
|
||||
},
|
||||
{
|
||||
"path": "packages/artplayer-plugin-hls-control/dist/artplayer-plugin-hls-control.legacy.js",
|
||||
"sha256": "fd7be1c5bf8f994b61fc040ff96eb628fc442568d99f4b5fee4f611bac1d28c0"
|
||||
},
|
||||
{
|
||||
"path": "packages/artplayer-plugin-hls-control/dist/artplayer-plugin-hls-control.mjs",
|
||||
"sha256": "64286498c1e90c3927236318383db193698a9dcbf39f91e93fc2b4ea2239e786"
|
||||
},
|
||||
{
|
||||
"path": "packages/artplayer-plugin-jassub/dist/artplayer-plugin-jassub.js",
|
||||
"sha256": "fb8cd8b7b6fb57a7866f929cfe20f7f582c86af96101f23c572dd79b4912dc9f"
|
||||
},
|
||||
{
|
||||
"path": "packages/artplayer-plugin-jassub/dist/artplayer-plugin-jassub.legacy.js",
|
||||
"sha256": "e19fea01316f78245e4508999bc441c483cfb47abfa6b5afbba529af3d05bfd8"
|
||||
},
|
||||
{
|
||||
"path": "packages/artplayer-plugin-jassub/dist/artplayer-plugin-jassub.mjs",
|
||||
"sha256": "d5f9df0621de4b5a9a5a8a232e290f99a1d8a2eda1bc0f8a34a2cb530c95653e"
|
||||
},
|
||||
{
|
||||
"path": "packages/artplayer-plugin-multiple-subtitles/dist/artplayer-plugin-multiple-subtitles.js",
|
||||
"sha256": "14f4de91201f6b2cd55ea22a042d456c7149e4a38c22bc2ee0107f8a5e417446"
|
||||
},
|
||||
{
|
||||
"path": "packages/artplayer-plugin-multiple-subtitles/dist/artplayer-plugin-multiple-subtitles.legacy.js",
|
||||
"sha256": "2059a43e4634b6c1a924a1cb451c2348356d6c20d26628b975b1d3545394d323"
|
||||
},
|
||||
{
|
||||
"path": "packages/artplayer-plugin-multiple-subtitles/dist/artplayer-plugin-multiple-subtitles.mjs",
|
||||
"sha256": "07e4f628b4038d7c250f6242c410ddb188148b4ed58bb592975532666412e3e2"
|
||||
},
|
||||
{
|
||||
"path": "packages/artplayer-plugin-vast/dist/artplayer-plugin-vast.js",
|
||||
"sha256": "874803ec726f04649bb636c1376de9e038754310ab0016a22da44f189b7a5ce0"
|
||||
},
|
||||
{
|
||||
"path": "packages/artplayer-plugin-vast/dist/artplayer-plugin-vast.legacy.js",
|
||||
"sha256": "8a4166838aae5d150dc1a18cc594fb038df425df42eff1944e0d095df060dbb9"
|
||||
},
|
||||
{
|
||||
"path": "packages/artplayer-plugin-vast/dist/artplayer-plugin-vast.mjs",
|
||||
"sha256": "ffbab3a7cd6c1852c42a726acd0e0a5dbbb636dc9e8c2e8a084df962ff6ef860"
|
||||
},
|
||||
{
|
||||
"path": "packages/artplayer-plugin-vtt-thumbnail/dist/artplayer-plugin-vtt-thumbnail.js",
|
||||
"sha256": "df70ccb5261fd988ba8c2c2d689ca58653bb5426242ffd1076569a1d225997a7"
|
||||
},
|
||||
{
|
||||
"path": "packages/artplayer-plugin-vtt-thumbnail/dist/artplayer-plugin-vtt-thumbnail.legacy.js",
|
||||
"sha256": "732ade2c647281cf26446d6c7433e916f8189e2fdf2687127a56556c415544b1"
|
||||
},
|
||||
{
|
||||
"path": "packages/artplayer-plugin-vtt-thumbnail/dist/artplayer-plugin-vtt-thumbnail.mjs",
|
||||
"sha256": "d35e28015df65e27da0fc01d06db07bad58e08d498b2d11fedfea35e6c4339c1"
|
||||
},
|
||||
{
|
||||
"path": "packages/artplayer-proxy-canvas/dist/artplayer-proxy-canvas.js",
|
||||
"sha256": "a901e5c5fc00a64a6c2ae9d4b2b846950412710ec9672d30640d40987d4f9e73"
|
||||
},
|
||||
{
|
||||
"path": "packages/artplayer-proxy-canvas/dist/artplayer-proxy-canvas.legacy.js",
|
||||
"sha256": "052263fdd7ab657a0183d874a706c2cf82527233c5e60b58158caa0677718b97"
|
||||
},
|
||||
{
|
||||
"path": "packages/artplayer-proxy-canvas/dist/artplayer-proxy-canvas.mjs",
|
||||
"sha256": "66ca7271f52545a8796f563f2d5ca04581bed8d60d218463923325acf5b73ccf"
|
||||
},
|
||||
{
|
||||
"path": "packages/artplayer-proxy-mediabunny/dist/artplayer-proxy-mediabunny.js",
|
||||
"sha256": "23ce59f86e7159eedd4c371219c5ed58db83341e1f3fa3c67dcb8f1259e243cb"
|
||||
},
|
||||
{
|
||||
"path": "packages/artplayer-proxy-mediabunny/dist/artplayer-proxy-mediabunny.legacy.js",
|
||||
"sha256": "2c523a4535ae71213d279b54abf3b1d164c81e3d8abe3d9c9906e9a9ac840c26"
|
||||
},
|
||||
{
|
||||
"path": "packages/artplayer-proxy-mediabunny/dist/artplayer-proxy-mediabunny.mjs",
|
||||
"sha256": "3126446aa6461b78e56855ac851a2abf5a12359c7354e5564af6efaa0903f3fa"
|
||||
},
|
||||
{
|
||||
"path": "packages/artplayer-tool-iframe/dist/artplayer-tool-iframe.js",
|
||||
"sha256": "7408fceccb000e1b304abe30ac9bc364da7860c3896ad5aaa6af427804c237e6"
|
||||
},
|
||||
{
|
||||
"path": "packages/artplayer-tool-iframe/dist/artplayer-tool-iframe.legacy.js",
|
||||
"sha256": "4e8bd39cdc37e98bff9cd19b5e0c9453ebe5231264242fc1c573aa96edb5ea8a"
|
||||
},
|
||||
{
|
||||
"path": "packages/artplayer-tool-iframe/dist/artplayer-tool-iframe.mjs",
|
||||
"sha256": "ac4c5efa670c0429fc95bdab7902df60f5b5540f0dad9c7e65c2bd660796dc83"
|
||||
},
|
||||
{
|
||||
"path": "packages/artplayer-tool-thumbnail/dist/artplayer-tool-thumbnail.js",
|
||||
"sha256": "ef72d277e817c2f7312c10815ee91a74dbb9334285476d14fae7f4449b3d2758"
|
||||
},
|
||||
{
|
||||
"path": "packages/artplayer-tool-thumbnail/dist/artplayer-tool-thumbnail.legacy.js",
|
||||
"sha256": "f1d62e3cee7e060e7ecff4a21e7589e2befb2e405976efcdee73719857d0be03"
|
||||
},
|
||||
{
|
||||
"path": "packages/artplayer-tool-thumbnail/dist/artplayer-tool-thumbnail.mjs",
|
||||
"sha256": "abdbc7c28bcccc1a8dee543f1bd784f850c147856fc148c3bf4b8ea863f3d6ea"
|
||||
}
|
||||
],
|
||||
"candidate": [
|
||||
{
|
||||
"path": "packages/artplayer/dist/artplayer.js",
|
||||
"sha256": "6dc8ca6d6eb34aa8a056cbc31328efd634530b3c5c60f0014cb7ce719fabc335"
|
||||
},
|
||||
{
|
||||
"path": "packages/artplayer/dist/artplayer.legacy.js",
|
||||
"sha256": "80f74a8a39df4baf64cc00c65d21cd57c011bfcdead0da38eea077fbc67c5c04"
|
||||
},
|
||||
{
|
||||
"path": "packages/artplayer/dist/artplayer.mjs",
|
||||
"sha256": "432e2b0ad92009a09743e020bf7c928c8b8f18a52496622b16259ec9ab20059f"
|
||||
},
|
||||
{
|
||||
"path": "packages/artplayer-plugin-ads/dist/artplayer-plugin-ads.js",
|
||||
"sha256": "1429541242f49c4d558eda242a63b6e57a876c2dba06ce9f631d71d315028023"
|
||||
},
|
||||
{
|
||||
"path": "packages/artplayer-plugin-ads/dist/artplayer-plugin-ads.legacy.js",
|
||||
"sha256": "4f64ac015e0850b71f51cf01d9fc6f238c95414730c71e91d1240c40e28499d8"
|
||||
},
|
||||
{
|
||||
"path": "packages/artplayer-plugin-ads/dist/artplayer-plugin-ads.mjs",
|
||||
"sha256": "8ad7df8145b2514467cac0f733151619c36fa965e620df575c0779604372503f"
|
||||
},
|
||||
{
|
||||
"path": "packages/artplayer-plugin-ambilight/dist/artplayer-plugin-ambilight.js",
|
||||
"sha256": "21ee5227f26cfff6e82ee6932f6eafc9741fa3be0ec2f5252a47b53ceea14599"
|
||||
},
|
||||
{
|
||||
"path": "packages/artplayer-plugin-ambilight/dist/artplayer-plugin-ambilight.legacy.js",
|
||||
"sha256": "c3ed1a54e44a0202811d70378cb19edf87b1ebd010d05fe96ebe64d0b4301d5d"
|
||||
},
|
||||
{
|
||||
"path": "packages/artplayer-plugin-ambilight/dist/artplayer-plugin-ambilight.mjs",
|
||||
"sha256": "ef126959819c4973da27dbb1e60ccccc54386c23f8c7750ce1ba4d38f306aaaa"
|
||||
},
|
||||
{
|
||||
"path": "packages/artplayer-plugin-asr/dist/artplayer-plugin-asr.js",
|
||||
"sha256": "06b386f6a4744801f4153944ed6e2bd37822a85e177aa2a0d800c30002ef87bf"
|
||||
},
|
||||
{
|
||||
"path": "packages/artplayer-plugin-asr/dist/artplayer-plugin-asr.legacy.js",
|
||||
"sha256": "639ae9bdede0e26c8af0251b9c1ed12e7fba9e0b6b79cb0556290741040f9697"
|
||||
},
|
||||
{
|
||||
"path": "packages/artplayer-plugin-asr/dist/artplayer-plugin-asr.mjs",
|
||||
"sha256": "1b7bac676d2c4976046b08fd7c1ec25724ff24952f5d93fc546e219466d975d8"
|
||||
},
|
||||
{
|
||||
"path": "packages/artplayer-plugin-audio-track/dist/artplayer-plugin-audio-track.js",
|
||||
"sha256": "23b10856628c5568f1aa7ad88e926c7da8d96edfadf6df872777062a78a4b4be"
|
||||
},
|
||||
{
|
||||
"path": "packages/artplayer-plugin-audio-track/dist/artplayer-plugin-audio-track.legacy.js",
|
||||
"sha256": "41b94e84e3d3729b9373cb13de2f64d5ea30b4c36627f38583bf4c3c9bc2160c"
|
||||
},
|
||||
{
|
||||
"path": "packages/artplayer-plugin-audio-track/dist/artplayer-plugin-audio-track.mjs",
|
||||
"sha256": "9caa5a6e11a2e777760dd2835426842a87d04fb081ea2e7d48c7229035fd89a9"
|
||||
},
|
||||
{
|
||||
"path": "packages/artplayer-plugin-auto-thumbnail/dist/artplayer-plugin-auto-thumbnail.js",
|
||||
"sha256": "430ac17e9025d3cc6bf37bd83316db5067bf438b30c2727b43c3e5fc4910e1d0"
|
||||
},
|
||||
{
|
||||
"path": "packages/artplayer-plugin-auto-thumbnail/dist/artplayer-plugin-auto-thumbnail.legacy.js",
|
||||
"sha256": "efb30986ffc6a28859ca37126af82824601f25f0a42f7ea2c75a41f3da2c50ef"
|
||||
},
|
||||
{
|
||||
"path": "packages/artplayer-plugin-auto-thumbnail/dist/artplayer-plugin-auto-thumbnail.mjs",
|
||||
"sha256": "6080dd90bde239515646899bdd8e71d17de24119523e4883025fbf39b1a6a302"
|
||||
},
|
||||
{
|
||||
"path": "packages/artplayer-plugin-chapter/dist/artplayer-plugin-chapter.js",
|
||||
"sha256": "179c3ea90ddc85da27b46f000110e32d464dfa5c8c394e98316dc1b75d6f2a72"
|
||||
},
|
||||
{
|
||||
"path": "packages/artplayer-plugin-chapter/dist/artplayer-plugin-chapter.legacy.js",
|
||||
"sha256": "dade919ed044cbf46ac16e505f538f86a86edccf177da2e14d79d111559d4bef"
|
||||
},
|
||||
{
|
||||
"path": "packages/artplayer-plugin-chapter/dist/artplayer-plugin-chapter.mjs",
|
||||
"sha256": "754fbcfc60add74d6b6ad3bc8db253df3eb14ace13ac8a5439be0592bd5512d0"
|
||||
},
|
||||
{
|
||||
"path": "packages/artplayer-plugin-chromecast/dist/artplayer-plugin-chromecast.js",
|
||||
"sha256": "80b12b3081086488c9d80f03979c336d4a6fdb44dca298e218dfeaaadb295d00"
|
||||
},
|
||||
{
|
||||
"path": "packages/artplayer-plugin-chromecast/dist/artplayer-plugin-chromecast.legacy.js",
|
||||
"sha256": "b21a30852403914a3bf3defc89aff11f2cc23b044ed68ccf667164b477f04172"
|
||||
},
|
||||
{
|
||||
"path": "packages/artplayer-plugin-chromecast/dist/artplayer-plugin-chromecast.mjs",
|
||||
"sha256": "68d81fcab469f74fe9d9740842d989e10144f7ff28c7ade91ed22adee90f44d7"
|
||||
},
|
||||
{
|
||||
"path": "packages/artplayer-plugin-danmuku/dist/artplayer-plugin-danmuku.js",
|
||||
"sha256": "133b7412e1a468b34f4f7ab3779958259f13899e037833029931f02b0833d088"
|
||||
},
|
||||
{
|
||||
"path": "packages/artplayer-plugin-danmuku/dist/artplayer-plugin-danmuku.legacy.js",
|
||||
"sha256": "9f0cb9969419a7d0c02d54790c423353b83f775acd95fbdbaeeb29a7fc42c816"
|
||||
},
|
||||
{
|
||||
"path": "packages/artplayer-plugin-danmuku/dist/artplayer-plugin-danmuku.mjs",
|
||||
"sha256": "356406cda2e3e47376a87c939b76fb7c55f86d7c3829127da625b01241c67a2f"
|
||||
},
|
||||
{
|
||||
"path": "packages/artplayer-plugin-danmuku-mask/dist/artplayer-plugin-danmuku-mask.js",
|
||||
"sha256": "406a8c28207d386cffe1e61dc79afd8a6a9d5cbd1dc8e1661b4febbf48182f55"
|
||||
},
|
||||
{
|
||||
"path": "packages/artplayer-plugin-danmuku-mask/dist/artplayer-plugin-danmuku-mask.legacy.js",
|
||||
"sha256": "62d61dcedd3e90fa480589c2f1e3168c97635232c98ffe8c25c7918a2f3c66db"
|
||||
},
|
||||
{
|
||||
"path": "packages/artplayer-plugin-danmuku-mask/dist/artplayer-plugin-danmuku-mask.mjs",
|
||||
"sha256": "51936576383129170125b33d2983858e0841ea507368970f6d154bc9d78583b1"
|
||||
},
|
||||
{
|
||||
"path": "packages/artplayer-plugin-dash-control/dist/artplayer-plugin-dash-control.js",
|
||||
"sha256": "a594009be5c17406844f817c5a79930c6ed60add4a5ef0578783f163a1bae012"
|
||||
},
|
||||
{
|
||||
"path": "packages/artplayer-plugin-dash-control/dist/artplayer-plugin-dash-control.legacy.js",
|
||||
"sha256": "b00a837a99f7a7cccd415af335cbf0c68f1e4267fa7cfd8d91e945e3dc634151"
|
||||
},
|
||||
{
|
||||
"path": "packages/artplayer-plugin-dash-control/dist/artplayer-plugin-dash-control.mjs",
|
||||
"sha256": "0c5a7e4d84e9641778e0a22ff9c78db9bd4ac2f08f51358519443046a528fb93"
|
||||
},
|
||||
{
|
||||
"path": "packages/artplayer-plugin-document-pip/dist/artplayer-plugin-document-pip.js",
|
||||
"sha256": "56440aad3ed06be55ec3a57d781f7de239e9aecf40a067e88f2cffcd01a86d6a"
|
||||
},
|
||||
{
|
||||
"path": "packages/artplayer-plugin-document-pip/dist/artplayer-plugin-document-pip.legacy.js",
|
||||
"sha256": "67dd14de3cc16a04ae6c1938dcba61b1355f4a15e5fa06f62a41b18d14d4d356"
|
||||
},
|
||||
{
|
||||
"path": "packages/artplayer-plugin-document-pip/dist/artplayer-plugin-document-pip.mjs",
|
||||
"sha256": "541b4ad6e1cbadd9156ee1f317559c97b9fddae37668ef32046f1fa47c392ec2"
|
||||
},
|
||||
{
|
||||
"path": "packages/artplayer-plugin-hls-control/dist/artplayer-plugin-hls-control.js",
|
||||
"sha256": "8e163d4ab6734cbc45e7f539de7db644c7faf36d0318469a86d300bf6365cca6"
|
||||
},
|
||||
{
|
||||
"path": "packages/artplayer-plugin-hls-control/dist/artplayer-plugin-hls-control.legacy.js",
|
||||
"sha256": "fd7be1c5bf8f994b61fc040ff96eb628fc442568d99f4b5fee4f611bac1d28c0"
|
||||
},
|
||||
{
|
||||
"path": "packages/artplayer-plugin-hls-control/dist/artplayer-plugin-hls-control.mjs",
|
||||
"sha256": "64286498c1e90c3927236318383db193698a9dcbf39f91e93fc2b4ea2239e786"
|
||||
},
|
||||
{
|
||||
"path": "packages/artplayer-plugin-jassub/dist/artplayer-plugin-jassub.js",
|
||||
"sha256": "fb8cd8b7b6fb57a7866f929cfe20f7f582c86af96101f23c572dd79b4912dc9f"
|
||||
},
|
||||
{
|
||||
"path": "packages/artplayer-plugin-jassub/dist/artplayer-plugin-jassub.legacy.js",
|
||||
"sha256": "e19fea01316f78245e4508999bc441c483cfb47abfa6b5afbba529af3d05bfd8"
|
||||
},
|
||||
{
|
||||
"path": "packages/artplayer-plugin-jassub/dist/artplayer-plugin-jassub.mjs",
|
||||
"sha256": "d5f9df0621de4b5a9a5a8a232e290f99a1d8a2eda1bc0f8a34a2cb530c95653e"
|
||||
},
|
||||
{
|
||||
"path": "packages/artplayer-plugin-multiple-subtitles/dist/artplayer-plugin-multiple-subtitles.js",
|
||||
"sha256": "14f4de91201f6b2cd55ea22a042d456c7149e4a38c22bc2ee0107f8a5e417446"
|
||||
},
|
||||
{
|
||||
"path": "packages/artplayer-plugin-multiple-subtitles/dist/artplayer-plugin-multiple-subtitles.legacy.js",
|
||||
"sha256": "2059a43e4634b6c1a924a1cb451c2348356d6c20d26628b975b1d3545394d323"
|
||||
},
|
||||
{
|
||||
"path": "packages/artplayer-plugin-multiple-subtitles/dist/artplayer-plugin-multiple-subtitles.mjs",
|
||||
"sha256": "07e4f628b4038d7c250f6242c410ddb188148b4ed58bb592975532666412e3e2"
|
||||
},
|
||||
{
|
||||
"path": "packages/artplayer-plugin-vast/dist/artplayer-plugin-vast.js",
|
||||
"sha256": "874803ec726f04649bb636c1376de9e038754310ab0016a22da44f189b7a5ce0"
|
||||
},
|
||||
{
|
||||
"path": "packages/artplayer-plugin-vast/dist/artplayer-plugin-vast.legacy.js",
|
||||
"sha256": "8a4166838aae5d150dc1a18cc594fb038df425df42eff1944e0d095df060dbb9"
|
||||
},
|
||||
{
|
||||
"path": "packages/artplayer-plugin-vast/dist/artplayer-plugin-vast.mjs",
|
||||
"sha256": "ffbab3a7cd6c1852c42a726acd0e0a5dbbb636dc9e8c2e8a084df962ff6ef860"
|
||||
},
|
||||
{
|
||||
"path": "packages/artplayer-plugin-vtt-thumbnail/dist/artplayer-plugin-vtt-thumbnail.js",
|
||||
"sha256": "df70ccb5261fd988ba8c2c2d689ca58653bb5426242ffd1076569a1d225997a7"
|
||||
},
|
||||
{
|
||||
"path": "packages/artplayer-plugin-vtt-thumbnail/dist/artplayer-plugin-vtt-thumbnail.legacy.js",
|
||||
"sha256": "732ade2c647281cf26446d6c7433e916f8189e2fdf2687127a56556c415544b1"
|
||||
},
|
||||
{
|
||||
"path": "packages/artplayer-plugin-vtt-thumbnail/dist/artplayer-plugin-vtt-thumbnail.mjs",
|
||||
"sha256": "d35e28015df65e27da0fc01d06db07bad58e08d498b2d11fedfea35e6c4339c1"
|
||||
},
|
||||
{
|
||||
"path": "packages/artplayer-proxy-canvas/dist/artplayer-proxy-canvas.js",
|
||||
"sha256": "a901e5c5fc00a64a6c2ae9d4b2b846950412710ec9672d30640d40987d4f9e73"
|
||||
},
|
||||
{
|
||||
"path": "packages/artplayer-proxy-canvas/dist/artplayer-proxy-canvas.legacy.js",
|
||||
"sha256": "052263fdd7ab657a0183d874a706c2cf82527233c5e60b58158caa0677718b97"
|
||||
},
|
||||
{
|
||||
"path": "packages/artplayer-proxy-canvas/dist/artplayer-proxy-canvas.mjs",
|
||||
"sha256": "66ca7271f52545a8796f563f2d5ca04581bed8d60d218463923325acf5b73ccf"
|
||||
},
|
||||
{
|
||||
"path": "packages/artplayer-proxy-mediabunny/dist/artplayer-proxy-mediabunny.js",
|
||||
"sha256": "23ce59f86e7159eedd4c371219c5ed58db83341e1f3fa3c67dcb8f1259e243cb"
|
||||
},
|
||||
{
|
||||
"path": "packages/artplayer-proxy-mediabunny/dist/artplayer-proxy-mediabunny.legacy.js",
|
||||
"sha256": "2c523a4535ae71213d279b54abf3b1d164c81e3d8abe3d9c9906e9a9ac840c26"
|
||||
},
|
||||
{
|
||||
"path": "packages/artplayer-proxy-mediabunny/dist/artplayer-proxy-mediabunny.mjs",
|
||||
"sha256": "3126446aa6461b78e56855ac851a2abf5a12359c7354e5564af6efaa0903f3fa"
|
||||
},
|
||||
{
|
||||
"path": "packages/artplayer-tool-iframe/dist/artplayer-tool-iframe.js",
|
||||
"sha256": "7408fceccb000e1b304abe30ac9bc364da7860c3896ad5aaa6af427804c237e6"
|
||||
},
|
||||
{
|
||||
"path": "packages/artplayer-tool-iframe/dist/artplayer-tool-iframe.legacy.js",
|
||||
"sha256": "4e8bd39cdc37e98bff9cd19b5e0c9453ebe5231264242fc1c573aa96edb5ea8a"
|
||||
},
|
||||
{
|
||||
"path": "packages/artplayer-tool-iframe/dist/artplayer-tool-iframe.mjs",
|
||||
"sha256": "ac4c5efa670c0429fc95bdab7902df60f5b5540f0dad9c7e65c2bd660796dc83"
|
||||
},
|
||||
{
|
||||
"path": "packages/artplayer-tool-thumbnail/dist/artplayer-tool-thumbnail.js",
|
||||
"sha256": "ef72d277e817c2f7312c10815ee91a74dbb9334285476d14fae7f4449b3d2758"
|
||||
},
|
||||
{
|
||||
"path": "packages/artplayer-tool-thumbnail/dist/artplayer-tool-thumbnail.legacy.js",
|
||||
"sha256": "f1d62e3cee7e060e7ecff4a21e7589e2befb2e405976efcdee73719857d0be03"
|
||||
},
|
||||
{
|
||||
"path": "packages/artplayer-tool-thumbnail/dist/artplayer-tool-thumbnail.mjs",
|
||||
"sha256": "abdbc7c28bcccc1a8dee543f1bd784f850c147856fc148c3bf4b8ea863f3d6ea"
|
||||
}
|
||||
],
|
||||
"differences": []
|
||||
},
|
||||
"servorPortDefect": {
|
||||
"task": "MOD-DEV-01",
|
||||
"node": "v24.21.0",
|
||||
"servor": "4.0.2",
|
||||
"occupiedPort": 12671,
|
||||
"code": 0,
|
||||
"signal": null,
|
||||
"output": "[ERR] The port you have specified is already in use!\n",
|
||||
"interpretation": "Historical upstream defect reproduction, not candidate success: an occupied requested port exits the entire process with status zero."
|
||||
},
|
||||
"sources": [
|
||||
{
|
||||
"path": "scripts/build.js",
|
||||
"sha256": "ec716c545d6bd0a06fdf7b616a487e0cc7605212de5c413bc247efa6737d5870"
|
||||
},
|
||||
{
|
||||
"path": "scripts/dev.js",
|
||||
"sha256": "86b13818172c2ab9ed6792f32e14af1b194783292264ff4476bcbe0f54697176"
|
||||
},
|
||||
{
|
||||
"path": "scripts/utils.js",
|
||||
"sha256": "b42450026f5e8a621b59cd84f91d5c1d6915f4ec026ad2ee0980b06f16f6ae79"
|
||||
},
|
||||
{
|
||||
"path": "scripts/projects.js",
|
||||
"sha256": "3956faff0032ab5b91daa45f390a664ca246559621f95fd2f53cef8bb8b58bdf"
|
||||
},
|
||||
{
|
||||
"path": "scripts/rebuild.js",
|
||||
"sha256": "de8d73c482ac22c7c197910a755eb38cf9f3e4719c2daa9d994dd9122668fddf"
|
||||
},
|
||||
{
|
||||
"path": "scripts/build-analysis.mjs",
|
||||
"sha256": "b5e62d1fcd9fd9216f8849a15b5fc70bf0280d978b9992c0ff5302927884c15f"
|
||||
},
|
||||
{
|
||||
"path": "scripts/tsconfig.library.json",
|
||||
"sha256": "3f47bba61969a74be0285c67d19293ffb736ac9831a9ff78abac4789dcaf5e50"
|
||||
},
|
||||
{
|
||||
"path": "scripts/performance-artifacts.mjs",
|
||||
"sha256": "5749bc5d75d9d12fe73c281cfa12741cec29e3fd7032324eaa63c6670774e9c4"
|
||||
},
|
||||
{
|
||||
"path": "test/performance-report.test.js",
|
||||
"sha256": "3092854bea819b4436e00a9cc26615119ccaffef5f745d6c1459e1ed9a33422d"
|
||||
},
|
||||
{
|
||||
"path": "test/browser/library-development.spec.js",
|
||||
"sha256": "d2f606432618319815d588edda2bf01513db4154b5ddef0f4813ed5cdd80ff27"
|
||||
},
|
||||
{
|
||||
"path": "refactor/scripts/build.test.mjs",
|
||||
"sha256": "41942c147d63880169c9c7b032b395b511ca0573dc4ac09ba54e01338013b521"
|
||||
},
|
||||
{
|
||||
"path": "refactor/scripts/library-build-comparison.mjs",
|
||||
"sha256": "956e697d4d67a0417c71398f345d44144f8c3a9997f262c2b014c7a3ca5c0725"
|
||||
},
|
||||
{
|
||||
"path": "refactor/scripts/servor-port-probe.mjs",
|
||||
"sha256": "c3ae47466a575755b63c74fafd25a8fd8df160b36c2fe119cb7f5dfadfd26e2b"
|
||||
},
|
||||
{
|
||||
"path": "scripts/library/analysis.ts",
|
||||
"sha256": "68ce6b33d0257ccd34a2abc5a75a7a6e06420cd93eaa00501cc04e38d48ad78b"
|
||||
},
|
||||
{
|
||||
"path": "scripts/library/banner.ts",
|
||||
"sha256": "c07b9b033cf1e3ad68e9ce80c0f9e7955d24abe8ad291e01ad22b5303c9f9ec5"
|
||||
},
|
||||
{
|
||||
"path": "scripts/library/config.ts",
|
||||
"sha256": "57ce344e05e5373a9c243a37a223e2403c67b25541b4c77f5289a83a008b6a23"
|
||||
},
|
||||
{
|
||||
"path": "scripts/library/development.ts",
|
||||
"sha256": "7b6af5ef9bc985def5d604ae0dbc75faa57be907edfb8a06a5420f18462ba5b4"
|
||||
},
|
||||
{
|
||||
"path": "scripts/library/names.ts",
|
||||
"sha256": "3749777e71f2f471e0edb4a1104d2c650d09643f57149a42f1c135dc79b7b1b5"
|
||||
},
|
||||
{
|
||||
"path": "scripts/library/production.ts",
|
||||
"sha256": "f309ec71319d7d99f703bba3d11f3db53c1c240d6af9d0d5e6a5e36eb3b1bfeb"
|
||||
},
|
||||
{
|
||||
"path": "scripts/library/projects.ts",
|
||||
"sha256": "fc658271482a4f293c21d606ac87b2668dbc88e3028b345a622587ebc0d151ca"
|
||||
},
|
||||
{
|
||||
"path": "scripts/library/rebuild.ts",
|
||||
"sha256": "01cfc0f5f58263baa891e7c783349f1ff212a423a03a6805aecea568a482aec0"
|
||||
},
|
||||
{
|
||||
"path": "scripts/library/vendor.d.ts",
|
||||
"sha256": "c64b7a80155c6ffc95683a208de01ba5d8d4c7b8e673b2dc5a51479a467e9f6a"
|
||||
}
|
||||
],
|
||||
"logs": [
|
||||
{
|
||||
"path": "refactor/.cache/library-types.log",
|
||||
"sha256": "258b10b22b662a74ceb797d7522c1fada9d3eb274e0b5d399297124213a6bb60"
|
||||
},
|
||||
{
|
||||
"path": "refactor/.cache/library-docs-types.log",
|
||||
"sha256": "a81eda6ec03482b64e1f2e445981950400f44c198f4a2807b4d8d0ce8a40e0c5"
|
||||
},
|
||||
{
|
||||
"path": "refactor/.cache/library-final.log",
|
||||
"sha256": "57ea9a6ad9a9dbe51021ed4e931a01eda245bf6075d2f96aa51361c7959d706f"
|
||||
},
|
||||
{
|
||||
"path": "refactor/.cache/library-lint.log",
|
||||
"sha256": "582d5ca5dd5743cc6c4ee0d79209e83ae092192fc08a4459108c9378da5977ca"
|
||||
},
|
||||
{
|
||||
"path": "refactor/.cache/library-ci.log",
|
||||
"sha256": "4f3da506e18e73d591b3639a45d58ff8d6d2dd85b10cdd6b9e6ea83e264e450d"
|
||||
},
|
||||
{
|
||||
"path": "refactor/.cache/library-toolchain.log",
|
||||
"sha256": "26a73433d38e21611f5fac5a5ee69f4fccbe6504ff8911e14804263ff971bcb2"
|
||||
},
|
||||
{
|
||||
"path": "refactor/.cache/library-browser-initial.log",
|
||||
"sha256": "5d89458936d534add82759a5d5c7c619573ca4926808b5ca9daf2862d4bfee16"
|
||||
},
|
||||
{
|
||||
"path": "refactor/.cache/library-browser-reload-initial.log",
|
||||
"sha256": "327d7313fff811981422a6b1006ec059571d3e2aabea1396076070657a4bd922"
|
||||
},
|
||||
{
|
||||
"path": "refactor/.cache/library-browser-final.log",
|
||||
"sha256": "f1ca8c33db2481eacfb8769dc2fbc304979b09d502042820aef6240752d4c795"
|
||||
},
|
||||
{
|
||||
"path": "refactor/.cache/library-comparison.log",
|
||||
"sha256": "3e3b74b295a44218b6266e0593af346a3686e3108db4b44822a25658faf44135"
|
||||
},
|
||||
{
|
||||
"path": "refactor/.cache/servor-port-probe.log",
|
||||
"sha256": "69d774eb3a5ce6a0b17e398d4fecd228054f2a42465703e9ddc7c1aefe261682"
|
||||
},
|
||||
{
|
||||
"path": "refactor/.cache/library-baseline.log",
|
||||
"sha256": "0379a619c3b1323b32ae74a95da757318a6dd9538fef586de987fb5d94644d78"
|
||||
}
|
||||
],
|
||||
"limitations": [
|
||||
"Windows browser evidence; remote OS matrices remain pending",
|
||||
"No library media playback or npm publication claims",
|
||||
"Servor occupied-port exit0 remains open in MOD-DEV-01",
|
||||
"No new dependencies, Lerna changes or real dist/docs rebuild"
|
||||
]
|
||||
}
|
||||
@@ -4,8 +4,10 @@ MOD-PLUGIN-01 将 `yarn create:plugin` 的实现拆为严格 TS 渲染/文件写
|
||||
保留旧 JS 命令入口。生成 TS 工厂、CJS/ESM 声明、分发消费测试与维护文档;
|
||||
拒绝覆盖既有包/示例,失败时回退自己的写入并保留外部修改。
|
||||
运行 `yarn typecheck:scaffold` 与 `yarn test:scaffold`,具体限制和模块地图见
|
||||
[生成器维护指南](../scripts/plugin/README.md)。MOD-02 继续负责 dev/build/utils,
|
||||
本子任务不代表全部工程脚本已经迁移。
|
||||
[生成器维护指南](../scripts/plugin/README.md)。MOD-02 将剩余库构建/开发实现移到
|
||||
scripts/library 的严格 TS 模块,保留旧命令/模块入口;其模块地图与生命周期限制见
|
||||
[库工具维护指南](../scripts/library/README.md)。Servor 端口失败状态和关闭责任由
|
||||
MOD-DEV-01 继续处理,不因 TS 迁移而豁免。
|
||||
|
||||
ENG-06 保留原来的 Vite 7.3.6、Terser、三种库产物及路径,增加选包参数和 TS 入口;未切换 bundler 或运行时依赖。固定工具链仍为 Node 24.21.0 / Yarn 1.22.22。
|
||||
|
||||
@@ -32,11 +34,12 @@ JS 包使用 src/index.js,TS 包使用 src/index.ts;必须恰有一个入口
|
||||
|
||||
| 文件 | 职责与维护点 |
|
||||
| --- | --- |
|
||||
| scripts/projects.js | 库包清单、CLI 选包、旧 prompts 入口、JS/TS 入口唯一性;utils.js 继续转出 getProjects,保留原脚本调用 |
|
||||
| scripts/build.js | 三格式顺序构建、受限 dist 清理、banner、复制 docs/compiled;main=.js/es2020、legacy=.legacy.js/es2015、ESM=.mjs/es2020 |
|
||||
| scripts/dev.js | 8082 服务、docs/uncompiled/包名/index.js、首次成功打开浏览器、源目录监听及错误后的继续开发 |
|
||||
| scripts/rebuild.js | 合并构建期间的通知并串行重建,避免多个构建同时清空同一输出目录;失败后下一次请求仍可运行 |
|
||||
| scripts/utils.js | 共享 Vite 资源/worker/output 配置与历史全局名称;AMD 补丁依据实际 UMD 全局参数,不假定压缩变量名 |
|
||||
| scripts/projects.js | 兼容导出 library/projects.ts:库清单、CLI 选择和 JS/TS 唯一入口;保留原 prompts 交互 |
|
||||
| scripts/build.js | 命令门面调用 library/production.ts;顺序构建、dist 清理、docs/compiled 复制和分析;banner 独立为 library/banner.ts |
|
||||
| scripts/dev.js | 命令门面调用 library/development.ts;默认 8082、docs/uncompiled/包名/index.js、首次成功打开浏览器、源码监听和失败恢复 |
|
||||
| scripts/rebuild.js | 兼容导出 library/rebuild.ts;合并通知、串行构建、失败后的下一次请求 |
|
||||
| scripts/utils.js | 兼容导出 library/config.ts、names.ts 和 projects.ts;Vite/worker 配置与命名分离,AMD 补丁由 banner.ts 维护 |
|
||||
| scripts/build-analysis.mjs | 兼容导出 library/analysis.ts;分析文件位于 cache,不进入 dist |
|
||||
| types/assets.d.ts | Less inline、SVG URL 与 SVG raw 的字符串类型;特定 worker 类型边界随拥有该 worker 的包配置 |
|
||||
| refactor/fixtures/build/ | 同时使用 TS、相邻 JS、Less、SVG raw、TS inline worker 的独立构建夹具;不属于发布包 |
|
||||
| refactor/scripts/build.test.mjs | 真实 CLI 产物、错误不误清理、CJS/global/AMD/ESM、队列错误恢复;浏览器 worker/全库 AMD 使用 browser.html |
|
||||
|
||||
@@ -0,0 +1,73 @@
|
||||
# MOD-02: Typed library build and development tooling
|
||||
|
||||
## Scope, structure and compatibility
|
||||
|
||||
Preserve the historical build/dev commands, selectors, global names, resource
|
||||
handling, banner/notices, distribution filenames and analysis output. JS/MJS
|
||||
entrypoints remain as checked shims. The implementation moves to strict TS modules
|
||||
for production/development, config, banner handling, names, project selection,
|
||||
rebuild queue and analysis; responsibilities and dependencies are documented in
|
||||
[scripts/library/README.md](../../scripts/library/README.md). The scaffold part was
|
||||
already completed independently as MOD-PLUGIN-01.
|
||||
|
||||
Installed Vite declarations constrain plugin hooks and config mutation. Narrow
|
||||
ambient declarations cover only the consumed prompts2.4.2/Servor4.0.2 APIs, inspected
|
||||
in their installed source. No dependencies, lockfile, Lerna or installation hooks
|
||||
changed. Tool execution uses canonical Node24.21.0; built package consumption retains
|
||||
its separate Node/browser compatibility requirements.
|
||||
|
||||
Manifest parsing now starts from unknown and rejects a missing/non-string version
|
||||
before cleaning that package's dist. Performance evidence fingerprints every TS/JS
|
||||
and JSON module under scripts/library as well as the previous shims/lock; changes
|
||||
and additions/removals must invalidate old installed-artifact measurements. Markdown
|
||||
is not a build input. The tests cover the previously unnoticed migration hazard
|
||||
where unchanged shims could hide a changed TS config.
|
||||
|
||||
Development keeps port8082 and --no-open. Tests use the explicit internal run function
|
||||
with a fixture port, leaving the existing8082 process untouched. Output directories
|
||||
are created before Servor enumerates Linux watch directories; actual Linux execution
|
||||
remains part of remote CI, not inferred from the Windows run. Build errors keep the
|
||||
source watcher alive; queued changes remain serial.
|
||||
|
||||
## Validation
|
||||
|
||||
- `library-build-comparison.mjs`: 21 libraries / 63 artifacts built from the same
|
||||
current sources and fixture paths with frozen scripts at
|
||||
`28c7abb2bb8dfb7c84acfc7c5ec05c5882623069`, then current scripts. Every SHA-256
|
||||
is identical. Full report and logs retained in .cache/library-comparison-gjDNPX.
|
||||
Existing real dist/docs artifacts were not rebuilt or manually edited.
|
||||
- `yarn test:library`: 18 passed, including CLI invalid inputs, malformed version
|
||||
protection, TS/JS/Less/SVG/inline-worker builds, CJS/global/AMD/ESM, analysis output
|
||||
identity, private-public exclusion, performance evidence and generated plugins.
|
||||
- `yarn typecheck:library` and `yarn typecheck:docs-tools`: strict checks passed;
|
||||
the old JS/MJS shims are checked too.
|
||||
- `library-development.spec.js`: final 3 passed on Windows Chromium, Firefox and
|
||||
WebKit. Real server, worker postMessage result, TS edits, automatic page reload
|
||||
and syntax-error recovery were checked. No mocked dev server or playback claim.
|
||||
- First browser run failed because --input-type=module leaked into Vite's Less
|
||||
worker and broke CommonJS require; using a normal .mjs fixture launcher fixes the
|
||||
test environment. A second run had Chromium navigation cancellation when manual
|
||||
reload raced the real automatic reload; tests now assert the automatic update
|
||||
itself. Those reports are retained separately; neither is called a production fix.
|
||||
- `yarn lint`: passed with one existing docs/assets/ts/artplayer.d.ts warning.
|
||||
`yarn test:ci`: 50 passed. Remote workflow runs are not claimed.
|
||||
- `yarn test:baseline`: 522 passed, zero skipped. Strict toolchain and final
|
||||
plan/risk checks also pass; dependencies and the root lock remain unchanged.
|
||||
|
||||
Source/log hashes, artifact comparison, browser results and final gate results are
|
||||
recorded in [library-tooling-validation.json](../baselines/library-tooling-validation.json).
|
||||
|
||||
## Remaining server defect and next step
|
||||
|
||||
`servor-port-probe.mjs` binds its own random port, calls installed Servor4.0.2 in an
|
||||
isolated child, and reproduces its error message followed by exit status0. This is
|
||||
old upstream behavior, not a successful candidate check. DEV-SERVOR-PORT-01 remains
|
||||
open; MOD-DEV-01 is added before the final tooling decision to fix failure status
|
||||
and define HTTP/watch/reload/timeout shutdown ownership. Servor exposes no close
|
||||
handle and currently owns these resources for process lifetime. No unrelated server
|
||||
was terminated and no vendor patch or dependency replacement was hidden in this task.
|
||||
|
||||
MOD-02 completes the TS/structure and matching-output contract, not every dev-server
|
||||
lifecycle issue, remote OS check or the entire refactor. Revert this dedicated task
|
||||
commit to restore its old scripts and validation mapping together; do not revert
|
||||
only the shims without their TS targets. Local commit only, no push or publication.
|
||||
@@ -12,6 +12,7 @@
|
||||
| `yarn typecheck:react` / `yarn typecheck:vue` | 原 React TSX / Vue SFC 示例严格检查,ci:check 同时执行对应 lint |
|
||||
| `yarn typecheck:docs-tools` / `yarn check:docs-smoke` | 严格检查 TS 示例/声明生成器及 JS/MJS 门面;只读核对确定性生成的 readiness smoke,ci:check 执行 |
|
||||
| `yarn typecheck:scaffold` / `yarn test:scaffold` | 严格检查插件生成器与旧 JS 入口;生成包真实构建/类型/分发消费和写入失败回归;分别接入 ci:check 与 test:node |
|
||||
| `yarn typecheck:library` / `yarn test:library` | 严格检查库构建/开发 TS 与旧门面;复用真实资源构建、分发、性能证据和生成器回归;类型检查接入 ci:check |
|
||||
| `yarn check:editor-types` | 只读核对全部编辑器声明、SDK notices 和实际 libUris;主 TS 5.9.3/历史 4.3.5 整组语义检查,ci:check 执行 |
|
||||
| `yarn build:site-assets` / `yarn check:site-assets` | 从站点 browser/ TS 生成三个经典脚本;check 只读,root build:docs 先生成;完整 VitePress 构建另行验收 |
|
||||
| `yarn typecheck:site-assets` | 严格检查站点 browser/ 模块;生成脚本由 typecheck:docs-tools 的 checkJs 覆盖;均进入 ci:check |
|
||||
|
||||
+6
-4
@@ -2,9 +2,9 @@
|
||||
|
||||
> 由 tasks.json 生成。请修改数据后运行 `node refactor/scripts/plan.mjs --write`,不要手改本表。
|
||||
|
||||
基线:`40fcda6a37d0049d42e49c1e64e70d4fd9ba5f7f`。总任务 244 项,范围 22 个包及工作区/示例。
|
||||
基线:`40fcda6a37d0049d42e49c1e64e70d4fd9ba5f7f`。总任务 245 项,范围 22 个包及工作区/示例。
|
||||
|
||||
状态:todo 57 / doing 16 / blocked 0 / done 171 / deferred 0。风险 L/M/H 表示兼容风险,不表示工期。
|
||||
状态:todo 57 / doing 16 / blocked 0 / done 172 / deferred 0。风险 L/M/H 表示兼容风险,不表示工期。
|
||||
|
||||
前置依赖是启动条件;验收是完成条件。任务可以继续拆分,但不能复用或悄悄删除旧 ID。
|
||||
|
||||
@@ -401,10 +401,11 @@
|
||||
| --- | --- | --- | --- | --- | --- | --- |
|
||||
| MOD-01 | workspace<br>Bun 固定版本干净安装试点 | ENG-09 | 独立目录的 Bun 安装与 Yarn 冻结基线对比,默认仍为用户选定的 Yarn | Node 测试仍通过;安装与资源一致才决定采用;不改 bundler;不自动替换默认 packageManager 或维护锁文件 | H | todo |
|
||||
| MOD-PLUGIN-01 | workspace<br>迁移插件生成器与类型化模板 | ENG-06, SITE-03 | 保留 create:plugin CLI;纯渲染/文件发布边界、TS 工厂/声明/测试/示例及维护指南 | 防止覆盖现有包或示例;失败回退;生成包可构建并通过严格类型、旧模块消费及测试;不自动修改既有包或运行安装发布 | M | done |
|
||||
| MOD-02 | workspace<br>整理剩余开发/构建脚本与插件模板 | ENG-06, SITE-03, MOD-PLUGIN-01 | dev/build/utils/create-plugin 的 TS 与可测 CLI,模板同时提供旧 API | 旧脚本入口保留、新插件类型/测试/示例齐全,Lerna 改动单独取证 | M | todo |
|
||||
| MOD-02 | workspace<br>整理剩余开发/构建脚本与插件模板 | ENG-06, SITE-03, MOD-PLUGIN-01 | dev/build/utils/create-plugin 的 TS 与可测 CLI,模板同时提供旧 API | 旧脚本入口保留、新插件类型/测试/示例齐全,Lerna 改动单独取证 | M | done |
|
||||
| MOD-DEV-01 | workspace<br>修复开发服务器失败状态并明确资源生命周期 | MOD-02 | 修复 Servor 占用端口却退出 0 的已复现问题;明确 HTTP/监听/热刷新连接与定时器的启动、失败和关闭责任 | 保持默认 8082、docs 页面、自动刷新和旧 dev 命令;端口冲突退出非零且不终止其他服务,连续启动/停止及编译错误恢复有实际验证;跨平台监听差异单独留证 | M | todo |
|
||||
| MOD-03 | workspace<br>测量并优化核心热路径 | CORE-22, ENG-08 | DOM 读写、进度更新、持久化、初始化的测量与改进 | 相同设备媒体多次比较,契约不变,收益及无效尝试记录;复用 BASE-06 的原始样本与测量限制,至少三组同环境旧新配对,不以单次变快宣称收益 | M | todo |
|
||||
| MOD-04 | workspace<br>测量并优化重型插件/proxy | PKG-DANMUKU-09, PKG-MASK-06, PKG-MB-10, ENG-08 | 帧/队列/推理/音画同步与资源长期运行比较 | 不改默认算法/阈值,性能改善有证据;无收益则保留旧实现 | M | todo |
|
||||
| MOD-05 | workspace<br>完成工具链与性能采用决策 | MOD-01, MOD-02, MOD-03, MOD-04 | 最终 runtime/packageManager/构建配置及性能台账 | 干净安装和全包检查通过;Bun 未采用有理由,不为状态强行切换 | M | todo |
|
||||
| MOD-05 | workspace<br>完成工具链与性能采用决策 | MOD-01, MOD-02, MOD-03, MOD-04, MOD-DEV-01 | 最终 runtime/packageManager/构建配置及性能台账 | 干净安装和全包检查通过;Bun 未采用有理由,不为状态强行切换 | M | todo |
|
||||
|
||||
## 8 发布验收
|
||||
|
||||
@@ -622,6 +623,7 @@
|
||||
- EX-01: [记录](changes/2026-09-14-EX-01-react-consumer.md) [记录](baselines/react-consumer-validation.json) [记录](scripts/react-consumer.mjs)
|
||||
- EX-02: [记录](changes/2026-09-14-EX-02-vue-consumer.md) [记录](baselines/vue-consumer-validation.json) [记录](scripts/vue-consumer.mjs)
|
||||
- MOD-PLUGIN-01: [记录](changes/2026-09-14-MOD-PLUGIN-01-scaffold.md) [记录](baselines/scaffold-validation.json)
|
||||
- MOD-02: [记录](changes/2026-09-14-MOD-02-library-tooling.md) [记录](baselines/library-tooling-validation.json)
|
||||
- PKG-FACTORY-01: [记录](baselines/factory-assignment-gaps.json) [记录](baselines/factory-compatibility-proposals.json) [记录](factory-compatibility-decision.md) [记录](changes/2026-09-12-PKG-FACTORY-01-decision.md) [记录](type-compatibility-policy.md) [记录](baselines/factory-compatibility-validation.json) [记录](changes/2026-09-13-PKG-FACTORY-01-compatible-types.md)
|
||||
- CORE-25: [记录](changes/2026-09-13-CORE-25-defaults-ssr.md) [记录](baselines/defaults-ssr-validation.json)
|
||||
- ENG-12: [记录](changes/2026-09-13-ENG-12-library-public.md) [记录](baselines/library-public-validation.json)
|
||||
|
||||
@@ -1,5 +1,23 @@
|
||||
# 进度与证据
|
||||
|
||||
## MOD-02 库构建与开发脚本 TS 迁移完成
|
||||
|
||||
旧 build/dev/utils/projects/rebuild/build-analysis 入口保留,实现拆为严格 TS
|
||||
模块,Vite 配置、banner/AMD、命名、包选择、队列和分析责任分离。63 个产物
|
||||
在同一来源/路径下用旧脚本与新脚本构建,SHA-256 全部相同;真实 dist/docs 未改。
|
||||
性能证据校验同步覆盖新的 TS 构建输入。默认8082保留,测试使用自有端口,未
|
||||
终止原有服务。输出目录提前创建,避免漏入 Servor 初始 Linux 目录枚举;远端
|
||||
Linux/macOS 实测仍待 CI。Windows 三浏览器验证真实 Worker、自动刷新、修改和
|
||||
编译失败恢复最终3项通过,测试启动参数/手动刷新竞态的失败报告另存。
|
||||
18项专项、522项baseline、50项CI回归通过;严格库/文档工具类型和工具链检查
|
||||
通过,lint 保留1条既有warning。详情见[记录](changes/2026-09-14-MOD-02-library-tooling.md)
|
||||
和[证据](baselines/library-tooling-validation.json)。没有新增依赖、Lerna变更或发布。
|
||||
另用隔离子进程复现 Servor4.0.2 端口占用却退出0,新增开放风险及 MOD-DEV-01
|
||||
作为下一任务处理启动失败和资源关闭责任,已接入最终工具链任务依赖。
|
||||
245项:172 done、16 doing、57 todo。MOD-02 完成不代表该服务器缺陷或完整
|
||||
重构完成。本地独立提交;VAST 默认行为选择仍待用户答复。
|
||||
|
||||
|
||||
## MOD-PLUGIN-01 插件生成器与 TS 模板完成
|
||||
|
||||
生成器拆为严格 TS 的模板渲染、文件写入和 CLI,保留 create.js 旧命令入口及
|
||||
|
||||
@@ -262,3 +262,4 @@
|
||||
| SITE-EDITOR-AMD-01 | resolved / 已复现 | Late Monaco language AMD initialization races with example dependency loading | SITE-03 |
|
||||
| VTT-CORE-NAME-01 | open / 已复现 | Published VTT 1.0.1 control name conflicts with the core placeholder introduced in 5.1.7 | PKG-VTT-THUMB-05, REL-08 |
|
||||
| SCAFFOLD-OUTPUT-01 | resolved / 已复现 | Plugin generation overwrites existing examples and links to a different example name | MOD-PLUGIN-01 |
|
||||
| DEV-SERVOR-PORT-01 | open / 已复现 | Servor exits the entire dev process with status zero when the requested port is occupied | MOD-DEV-01 |
|
||||
@@ -5914,6 +5914,23 @@
|
||||
"test/plugin-scaffold.test.js"
|
||||
],
|
||||
"resolutionRationale": "Frozen old generator reproduces overwrite and mismatched URL. Candidate refuses old or concurrent output, preserves external edits on rollback, generates matching dotted URLs and passes actual three-format package consumers and strict types."
|
||||
},
|
||||
{
|
||||
"id": "DEV-SERVOR-PORT-01",
|
||||
"title": "Servor exits the entire dev process with status zero when the requested port is occupied",
|
||||
"confirmation": "reproduced",
|
||||
"status": "open",
|
||||
"owners": [
|
||||
"MOD-DEV-01"
|
||||
],
|
||||
"evidence": [
|
||||
"refactor/changes/2026-09-14-MOD-02-library-tooling.md",
|
||||
"refactor/scripts/servor-port-probe.mjs",
|
||||
"refactor/baselines/library-tooling-validation.json"
|
||||
],
|
||||
"compatibleResolution": "Preserve the default dev URL and CLI while reporting startup failures with nonzero status, without stopping another service. Define ownership of HTTP, watch and reload resources.",
|
||||
"closureCriteria": "Real occupied-port and repeated startup/shutdown checks pass for the candidate; build, reload and error recovery remain compatible. Separate source-observed resource ownership limitations from reproduced leaks.",
|
||||
"workspaceState": "Pinned Servor4.0.2 probe prints the occupied-port error and exits0. TS migration retains that upstream behavior; MOD-DEV-01 must resolve it before the final tooling decision. Servor has no exposed close handle; this is a source observation, not a measured leak."
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -75,6 +75,10 @@ test('Normal build CLI compiles TypeScript, JS, Less, SVG and inline worker into
|
||||
assert.equal(run([name]).status, 1)
|
||||
assert(fs.existsSync(path.join(dist, 'preserve.txt')))
|
||||
fs.unlinkSync(path.join(project, 'src/index.js'))
|
||||
fs.writeFileSync(path.join(project, 'package.json'), JSON.stringify({ name, version: 1 }))
|
||||
assert.equal(run([name]).status, 1, 'Malformed version must fail before dist is cleaned')
|
||||
assert(fs.existsSync(path.join(dist, 'preserve.txt')))
|
||||
fs.writeFileSync(path.join(project, 'package.json'), JSON.stringify({ name, version: '1.0.0' }))
|
||||
execFileSync(process.execPath, [path.join(root, 'scripts/build.js'), name], { cwd: directory, env: { ...process.env, NODE_ENV: 'production' }, encoding: 'utf8', timeout: 60000 })
|
||||
assert.deepEqual(fs.readdirSync(dist).sort(), [`${name}.js`, `${name}.legacy.js`, `${name}.mjs`])
|
||||
function check(factory) {
|
||||
|
||||
@@ -0,0 +1,50 @@
|
||||
import assert from 'node:assert/strict'
|
||||
import { execFileSync } from 'node:child_process'
|
||||
import { createHash } from 'node:crypto'
|
||||
import fs from 'node:fs'
|
||||
import path from 'node:path'
|
||||
import process from 'node:process'
|
||||
import { fileURLToPath } from 'node:url'
|
||||
|
||||
const root = fileURLToPath(new URL('../../', import.meta.url))
|
||||
const baseline = '28c7abb2bb8dfb7c84acfc7c5ec05c5882623069'
|
||||
const directory = fs.mkdtempSync(path.join(root, 'refactor/.cache/library-comparison-'))
|
||||
const names = fs.readdirSync(path.join(root, 'packages')).filter(name => name !== 'artplayer-vitepress' && fs.existsSync(path.join(root, 'packages', name, 'package.json'))).sort()
|
||||
const sha = file => createHash('sha256').update(fs.readFileSync(file)).digest('hex')
|
||||
fs.mkdirSync(path.join(directory, 'scripts'))
|
||||
fs.writeFileSync(path.join(directory, 'package.json'), '{"type":"module"}\n')
|
||||
for (const file of ['build.js', 'utils.js', 'projects.js', 'build-analysis.mjs'])
|
||||
fs.writeFileSync(path.join(directory, 'scripts', file), execFileSync('git', ['show', `${baseline}:scripts/${file}`], { cwd: root }))
|
||||
for (const name of names) {
|
||||
const target = path.join(directory, 'packages', name)
|
||||
fs.mkdirSync(target, { recursive: true })
|
||||
for (const file of ['src', 'public', 'package.json', 'THIRD_PARTY_NOTICES']) {
|
||||
const source = path.join(root, 'packages', name, file)
|
||||
if (fs.existsSync(source))
|
||||
fs.cpSync(source, path.join(target, file), { recursive: true })
|
||||
}
|
||||
}
|
||||
const artifacts = () => names.flatMap(name => ['.js', '.legacy.js', '.mjs'].map(extension => {
|
||||
const relative = `packages/${name}/dist/${name}${extension}`
|
||||
return { path: relative, sha256: sha(path.join(directory, relative)) }
|
||||
}))
|
||||
function build(script, label) {
|
||||
const log = fs.openSync(path.join(directory, `${label}.log`), 'w')
|
||||
try {
|
||||
execFileSync(process.execPath, [script, 'all'], { cwd: directory, stdio: ['ignore', log, log], timeout: 300000 })
|
||||
}
|
||||
finally {
|
||||
fs.closeSync(log)
|
||||
}
|
||||
}
|
||||
console.log(`Library comparison fixture: ${directory}`)
|
||||
build(path.join(directory, 'scripts/build.js'), 'before')
|
||||
const before = artifacts()
|
||||
fs.writeFileSync(path.join(directory, 'before.json'), `${JSON.stringify(before, null, 2)}\n`)
|
||||
build(path.join(root, 'scripts/build.js'), 'candidate')
|
||||
const candidate = artifacts()
|
||||
const differences = candidate.filter((item, index) => item.sha256 !== before[index].sha256)
|
||||
const report = { task: 'MOD-02', baseline, node: process.version, names, before, candidate, differences }
|
||||
fs.writeFileSync(path.join(directory, 'report.json'), `${JSON.stringify(report, null, 2)}\n`)
|
||||
assert.deepEqual(differences, [], 'Production bytes changed across the tooling migration')
|
||||
console.log(`Compared ${names.length} libraries / ${candidate.length} artifacts: identical SHA-256. Report: ${directory}/report.json`)
|
||||
@@ -0,0 +1,35 @@
|
||||
import assert from 'node:assert/strict'
|
||||
import { spawn } from 'node:child_process'
|
||||
import { once } from 'node:events'
|
||||
import fs from 'node:fs'
|
||||
import net from 'node:net'
|
||||
import path from 'node:path'
|
||||
import process from 'node:process'
|
||||
import { fileURLToPath } from 'node:url'
|
||||
|
||||
const root = fileURLToPath(new URL('../../', import.meta.url))
|
||||
const server = net.createServer()
|
||||
server.listen(0)
|
||||
await once(server, 'listening')
|
||||
const { port } = server.address()
|
||||
try {
|
||||
const child = spawn(process.execPath, ['--input-type=module', '--eval', "import servor from 'servor'; await servor({port:Number(process.env.ARTPLAYER_PROBE_PORT),reload:false}); process.exit(7)"], {
|
||||
cwd: root,
|
||||
env: { ...process.env, ARTPLAYER_PROBE_PORT: String(port) },
|
||||
windowsHide: true,
|
||||
timeout: 10000,
|
||||
})
|
||||
let output = ''
|
||||
child.stdout.on('data', (data) => { output += data })
|
||||
child.stderr.on('data', (data) => { output += data })
|
||||
const [code, signal] = await once(child, 'exit')
|
||||
const version = JSON.parse(fs.readFileSync(path.join(root, 'node_modules/servor/package.json'), 'utf8')).version
|
||||
const result = { task: 'MOD-DEV-01', node: process.version, servor: version, occupiedPort: port, code, signal, output, interpretation: 'Historical upstream defect reproduction, not candidate success: an occupied requested port exits the entire process with status zero.' }
|
||||
fs.writeFileSync(path.join(root, 'refactor/.cache/servor-port-probe.json'), `${JSON.stringify(result, null, 2)}\n`)
|
||||
assert.match(output, /port.*already in use/)
|
||||
assert.equal(code, 0, 'Pinned Servor no longer reproduces its historical successful failure exit')
|
||||
console.log(JSON.stringify(result, null, 2))
|
||||
}
|
||||
finally {
|
||||
await new Promise(resolve => server.close(resolve))
|
||||
}
|
||||
+22
-2
@@ -4459,10 +4459,29 @@
|
||||
"SITE-03",
|
||||
"MOD-PLUGIN-01"
|
||||
],
|
||||
"status": "todo",
|
||||
"status": "done",
|
||||
"risk": "M",
|
||||
"deliverable": "dev/build/utils/create-plugin 的 TS 与可测 CLI,模板同时提供旧 API",
|
||||
"acceptance": "旧脚本入口保留、新插件类型/测试/示例齐全,Lerna 改动单独取证",
|
||||
"evidence": [
|
||||
"changes/2026-09-14-MOD-02-library-tooling.md",
|
||||
"baselines/library-tooling-validation.json"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "MOD-DEV-01",
|
||||
"phase": "7 工具链与性能",
|
||||
"title": "修复开发服务器失败状态并明确资源生命周期",
|
||||
"scope": [
|
||||
"workspace"
|
||||
],
|
||||
"dependsOn": [
|
||||
"MOD-02"
|
||||
],
|
||||
"status": "todo",
|
||||
"risk": "M",
|
||||
"deliverable": "修复 Servor 占用端口却退出 0 的已复现问题;明确 HTTP/监听/热刷新连接与定时器的启动、失败和关闭责任",
|
||||
"acceptance": "保持默认 8082、docs 页面、自动刷新和旧 dev 命令;端口冲突退出非零且不终止其他服务,连续启动/停止及编译错误恢复有实际验证;跨平台监听差异单独留证",
|
||||
"evidence": []
|
||||
},
|
||||
{
|
||||
@@ -4512,7 +4531,8 @@
|
||||
"MOD-01",
|
||||
"MOD-02",
|
||||
"MOD-03",
|
||||
"MOD-04"
|
||||
"MOD-04",
|
||||
"MOD-DEV-01"
|
||||
],
|
||||
"status": "todo",
|
||||
"risk": "M",
|
||||
|
||||
@@ -1,5 +1,9 @@
|
||||
# 已实现的类型检查与迁移入口
|
||||
|
||||
`yarn typecheck:library` 检查 scripts/library 的生产构建、开发、配置、banner、
|
||||
分析、包选择和重建队列,同时检查旧 JS/MJS 门面。Vite 配置使用已安装的官方
|
||||
类型;vendor.d.ts 只描述实际消费的 prompts2.4.2/Servor4.0.2 调用,不声明全 SDK。
|
||||
|
||||
`yarn typecheck:scaffold` 检查 scripts/plugin 的严格 TS 模块及旧 JS 命令入口。
|
||||
模板本身保留占位符,`yarn test:scaffold` 对实际渲染包执行源码检查和
|
||||
TS 5.9.3 NodeNext / TS 4.3.5 classic 消费者正反例;生成声明地图见
|
||||
|
||||
@@ -1,44 +1 @@
|
||||
import { Buffer } from 'node:buffer'
|
||||
import { createHash } from 'node:crypto'
|
||||
import fs from 'node:fs'
|
||||
import path from 'node:path'
|
||||
import process from 'node:process'
|
||||
import { gzipSync } from 'node:zlib'
|
||||
|
||||
export function createBuildAnalysis() {
|
||||
const root = process.cwd()
|
||||
const parent = path.join(root, 'refactor/.cache/build-analysis')
|
||||
fs.mkdirSync(parent, { recursive: true })
|
||||
const directory = fs.mkdtempSync(path.join(parent, 'run-'))
|
||||
fs.writeFileSync(path.join(parent, 'latest.json'), `${JSON.stringify({ output: path.relative(root, directory).replaceAll('\\', '/') })}\n`)
|
||||
return { root, directory }
|
||||
}
|
||||
|
||||
export function buildAnalysisPlugin({ root, directory }, name, format) {
|
||||
return {
|
||||
name: 'artplayer-build-analysis',
|
||||
generateBundle(_options, bundle) {
|
||||
for (const chunk of Object.values(bundle).filter(item => item.type === 'chunk')) {
|
||||
const modules = Object.entries(chunk.modules).map(([id, info]) => {
|
||||
const normalized = id.replaceAll('\\', '/')
|
||||
const dependency = normalized.lastIndexOf('/node_modules/')
|
||||
const file = id.startsWith('\0')
|
||||
? `virtual:${id.slice(1)}`
|
||||
: dependency === -1 ? path.relative(root, id).replaceAll('\\', '/') : normalized.slice(dependency + 1)
|
||||
return { file, renderedBytes: Buffer.byteLength(info.code || ''), renderedCodeUnits: info.renderedLength, originalCodeUnits: info.originalLength, renderedExports: info.renderedExports, removedExports: info.removedExports }
|
||||
}).sort((a, b) => b.renderedBytes - a.renderedBytes || a.file.localeCompare(b.file))
|
||||
const report = {
|
||||
schemaVersion: 1,
|
||||
name,
|
||||
format,
|
||||
node: process.version,
|
||||
file: chunk.fileName,
|
||||
artifact: { bytes: Buffer.byteLength(chunk.code), gzip9: gzipSync(chunk.code, { level: 9 }).length, sha256: createHash('sha256').update(chunk.code).digest('hex') },
|
||||
note: 'Rendered UTF-8 bytes precede final chunk minification; they are not additive compressed sizes or download measurements. Rollup lengths are separately recorded as UTF-16 code units. Original code units describe Rollup input, not raw TypeScript.',
|
||||
modules,
|
||||
}
|
||||
fs.writeFileSync(path.join(directory, `${name}-${format}.json`), `${JSON.stringify(report, null, 2)}\n`)
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
export { buildAnalysisPlugin, createBuildAnalysis } from './library/analysis.ts'
|
||||
+1
-97
@@ -1,101 +1,5 @@
|
||||
import fs from 'node:fs'
|
||||
import path from 'node:path'
|
||||
import process from 'node:process'
|
||||
import cpy from 'cpy'
|
||||
import { build as viteBuild } from 'vite'
|
||||
import { buildAnalysisPlugin, createBuildAnalysis } from './build-analysis.mjs'
|
||||
import { getEntryFile, selectProjects } from './projects.js'
|
||||
import { getGlobalName, getProjects, getViteBuildConfig } from './utils.js'
|
||||
|
||||
const projects = getProjects()
|
||||
const compiledPath = path.resolve('docs/compiled')
|
||||
|
||||
function getBanner(name, version) {
|
||||
const noticeFile = path.join(projects[name], 'THIRD_PARTY_NOTICES')
|
||||
const notices = fs.existsSync(noticeFile) ? fs.readFileSync(noticeFile, 'utf8').trim() : ''
|
||||
const noticeBlock = notices ? `\n *\n${notices.split(/\r?\n/).map(line => line.trimEnd() ? ` * ${line.trimEnd()}` : ' *').join('\n')}` : ''
|
||||
return `/*!
|
||||
* ${name}.js v${version}
|
||||
* Github: https://github.com/zhw2590582/ArtPlayer
|
||||
* (c) 2017-${new Date().getFullYear()} Harvey Zhao
|
||||
* Released under the MIT License.${noticeBlock}
|
||||
*/`
|
||||
}
|
||||
|
||||
const BUILD_FORMATS = {
|
||||
main: { format: 'umd', ext: '.js', target: 'es2020', minify: 'terser' },
|
||||
legacy: { format: 'umd', ext: '.legacy.js', target: 'es2015', minify: 'terser' },
|
||||
esm: { format: 'es', ext: '.mjs', target: 'es2020', minify: false },
|
||||
}
|
||||
|
||||
async function build(name, targetName, clean = false, analysis) {
|
||||
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 = 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 })
|
||||
}
|
||||
|
||||
const startTime = Date.now()
|
||||
const { format, ext, target, minify } = BUILD_FORMATS[targetName]
|
||||
const fileName = `${name}${ext}`
|
||||
|
||||
const config = getViteBuildConfig({
|
||||
entry: entryFile,
|
||||
outDir: distDir,
|
||||
name: getGlobalName(name),
|
||||
format,
|
||||
fileName,
|
||||
minify,
|
||||
target,
|
||||
banner: getBanner(name, version),
|
||||
})
|
||||
|
||||
// Terser options for all builds (preserve banner comments starting with /*!)
|
||||
const isLegacy = targetName === 'legacy'
|
||||
config.build.terserOptions = {
|
||||
ecma: isLegacy ? 5 : 2020,
|
||||
compress: { ecma: isLegacy ? 5 : 2020, comparisons: false, inline: 2 },
|
||||
mangle: { safari10: true },
|
||||
format: {
|
||||
ecma: isLegacy ? 5 : 2020,
|
||||
comments: /^!/,
|
||||
ascii_only: true,
|
||||
max_line_len: false, // No line length limit
|
||||
},
|
||||
}
|
||||
if (analysis)
|
||||
config.build.rollupOptions.plugins.push(buildAnalysisPlugin(analysis, name, targetName))
|
||||
|
||||
await viteBuild({ root: projectDir, ...config })
|
||||
|
||||
const outFile = path.join(distDir, fileName)
|
||||
const size = (fs.statSync(outFile).size / 1024).toFixed(2)
|
||||
await cpy(outFile, compiledPath, { flat: true })
|
||||
|
||||
console.log(`✨ Built@${targetName} ${name}@${version} Time@${Date.now() - startTime}ms Size@${size}kb`)
|
||||
}
|
||||
|
||||
async function buildProject(name, analysis) {
|
||||
await build(name, 'main', true, analysis)
|
||||
await build(name, 'legacy', false, analysis)
|
||||
await build(name, 'esm', false, analysis)
|
||||
}
|
||||
|
||||
async function runBuild() {
|
||||
const { names, analyze } = await selectProjects(projects, 'build')
|
||||
const analysis = analyze ? createBuildAnalysis() : undefined
|
||||
for (const name of names)
|
||||
await buildProject(name, analysis)
|
||||
if (analysis)
|
||||
console.log(`Build analysis: ${analysis.directory}`)
|
||||
if (names.length)
|
||||
console.log(`✅ Finished building ${names.length} package(s)!`)
|
||||
}
|
||||
import { runBuild } from './library/production.ts'
|
||||
|
||||
runBuild().catch((error) => {
|
||||
console.error('❌ Build failed:', error)
|
||||
|
||||
+2
-71
@@ -1,76 +1,7 @@
|
||||
import fs from 'node:fs'
|
||||
import path from 'node:path'
|
||||
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'
|
||||
import { runDevelop } from './library/development.ts'
|
||||
|
||||
const projects = getProjects()
|
||||
|
||||
async function develop(name, open) {
|
||||
const projectPath = projects[name]
|
||||
const uncompiledPath = path.resolve(`docs/uncompiled/${name}`)
|
||||
let browserOpened = false
|
||||
|
||||
const { url } = await servor({
|
||||
root: 'docs',
|
||||
fallback: 'index.html',
|
||||
reload: true,
|
||||
port: 8082,
|
||||
})
|
||||
|
||||
fs.mkdirSync(uncompiledPath, { recursive: true })
|
||||
|
||||
async function buildBundle() {
|
||||
const startTime = Date.now()
|
||||
try {
|
||||
const config = getViteBuildConfig({
|
||||
entry: getEntryFile(projectPath),
|
||||
outDir: uncompiledPath,
|
||||
name: getGlobalName(name),
|
||||
format: 'iife',
|
||||
fileName: 'index.js',
|
||||
minify: false,
|
||||
emptyOutDir: true,
|
||||
})
|
||||
config.define['process.env.NODE_ENV'] = JSON.stringify('development')
|
||||
|
||||
await viteBuild({ root: projectPath, ...config })
|
||||
console.log(`[${name}] ✅ Built in ${Date.now() - startTime}ms`)
|
||||
|
||||
if (open && !browserOpened) {
|
||||
browserOpened = true
|
||||
openBrowser(url)
|
||||
}
|
||||
}
|
||||
catch (error) {
|
||||
console.error(`[${name}] ❌ Build error:`, error)
|
||||
}
|
||||
}
|
||||
|
||||
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}...`)
|
||||
|
||||
fs.watch(srcPath, { recursive: true }, async (_, filename) => {
|
||||
if (filename) {
|
||||
console.log(`[${name}] 📝 Changed: ${filename}`)
|
||||
await rebuild()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
(async () => {
|
||||
const { names, open } = await selectProjects(projects, 'dev')
|
||||
if (names.length)
|
||||
await develop(names[0], open)
|
||||
})().catch((error) => {
|
||||
runDevelop().catch((error) => {
|
||||
console.error('❌ Development server failed:', error)
|
||||
process.exitCode = 1
|
||||
})
|
||||
@@ -0,0 +1,98 @@
|
||||
# Library build and development tooling
|
||||
|
||||
Use Node from `.node-version` and Yarn Classic 1.22.22. The historical JS/MJS
|
||||
entrypoints remain checked compatibility shims; implementation lives in strict TS.
|
||||
No runtime/build dependency or bundler was replaced by this migration.
|
||||
|
||||
| Module | Responsibility |
|
||||
| --- | --- |
|
||||
| `projects.ts` | Deterministic workspace discovery, CLI selection, non-TTY failure, one JS/TS entry |
|
||||
| `names.ts` | Historical camelCase/PascalCase library global names |
|
||||
| `config.ts` | Vite configuration and resource/worker/output targets; typed with installed Vite declarations |
|
||||
| `banner.ts` | Version/license/notices, UMD AMD/global identity, inline-worker banner removal |
|
||||
| `production.ts` | Sequential package/format builds, checked dist cleanup, docs copy and optional analysis |
|
||||
| `analysis.ts` | Per-build module and artifact size/hash reports outside dist |
|
||||
| `development.ts` | Servor demo startup, initial IIFE build, source watcher, first-success browser opening |
|
||||
| `rebuild.ts` | One active build Promise, coalesced pending changes and later retry after rejection |
|
||||
| `vendor.d.ts` | Only the consumed prompts 2.4.2 / Servor 4.0.2 APIs; verified against installed sources |
|
||||
|
||||
`build.js` and `dev.js` own command execution/error reporting. `utils.js`,
|
||||
`projects.js`, `rebuild.js` and `build-analysis.mjs` keep their previous named
|
||||
exports. Importing implementation modules does not start a server/build or read
|
||||
the current workspace; the explicit run functions do that work. TypeScript
|
||||
annotation removal uses the canonical Node version; Node 20 consumer validation
|
||||
concerns the built libraries, not executing these repository TS tools.
|
||||
|
||||
## Stable commands and build behavior
|
||||
|
||||
```sh
|
||||
yarn build artplayer artplayer-plugin-chapter
|
||||
yarn build all --analyze
|
||||
yarn dev artplayer --no-open
|
||||
yarn build --help
|
||||
yarn dev --help
|
||||
yarn typecheck:library
|
||||
yarn test:library
|
||||
```
|
||||
|
||||
No-argument TTY selection uses the same prompts UI. Missing names in non-TTY mode,
|
||||
unknown/inherited project names and ambiguous JS/TS entries fail before cleaning
|
||||
dist or starting the server. Build format order remains modern UMD `.js` es2020,
|
||||
legacy UMD `.legacy.js` es2015, then ESM `.mjs` es2020. Main/legacy use the same
|
||||
Terser options; ESM remains unminified. Notice text, worker handling, AMD globals,
|
||||
default exports, copied docs paths and `--analyze` output remain intact. A missing
|
||||
or non-string manifest version now fails before that package's dist cleanup.
|
||||
Multi-package builds are sequential, not an all-packages transaction.
|
||||
|
||||
Vite types constrain configuration but do not replace source typechecking. Avoid
|
||||
casts that hide incompatible plugin/output types. `config.ts` deliberately sets
|
||||
`publicDir: false` so core declaration sources cannot leak into distribution files.
|
||||
Keep unknown UMD wrappers as explicit failures when upgrading Vite/Terser.
|
||||
|
||||
Performance artifact verification fingerprints the JS entry shims and all code/JSON
|
||||
under `scripts/library/`, including additions/removals. Do not reduce that coverage
|
||||
to `config.ts` alone: banners, name mapping, project discovery or analysis may also
|
||||
affect artifacts. Markdown maintenance changes are not build inputs.
|
||||
|
||||
## Development lifetime and remaining server work
|
||||
|
||||
CLI defaults remain port 8082 and docs/index.html, with output at
|
||||
docs/uncompiled/<package>/index.js. The output directory is created before Servor
|
||||
enumerates directories for its Linux file watchers. The internal `runDevelop`
|
||||
function accepts a port only for isolated callers/tests; the CLI does not add a
|
||||
new flag. Do not stop an unrelated server occupying 8082 to run a test.
|
||||
|
||||
Build errors are reported while keeping the source watcher alive. Changes during
|
||||
a build schedule one subsequent build rather than concurrent writes. Browser
|
||||
opening happens once after the first successful build; `--no-open` suppresses it.
|
||||
Only the package src tree is watched; external dependencies/configuration changes
|
||||
still require restart. Tests use real automatic reload rather than racing it with
|
||||
a second manual navigation.
|
||||
|
||||
Servor still owns its HTTP server, docs watchers, reload sockets/timers and SIGINT
|
||||
handler until process exit; its API exposes no close handle. MOD-DEV-01 tracks
|
||||
replacing/adapting that lifetime and fixing its reproduced occupied-port exit code
|
||||
zero. `node refactor/scripts/servor-port-probe.mjs` reproduces that upstream defect
|
||||
in an isolated child without touching 8082. It is not a candidate-success test.
|
||||
This migration does not claim those server limitations are resolved.
|
||||
|
||||
## Evidence and regression ownership
|
||||
|
||||
- `refactor/scripts/build.test.mjs`: CLI validation, serialization/error recovery,
|
||||
real TS/JS/Less/SVG/inline-worker builds, CJS/global/AMD/ESM and analysis identity.
|
||||
- `test/library-build.test.js`: public files never copied into library dist.
|
||||
- `test/performance-report.test.js`: changed/new/removed TS build inputs cannot
|
||||
reuse stale installed performance artifacts.
|
||||
- `test/plugin-scaffold.test.js`: newly generated plugins use these real tools.
|
||||
- `test/browser/library-development.spec.js`: real dev server in Chromium, Firefox
|
||||
and WebKit, worker messaging, automatic reload, source edits and error recovery.
|
||||
Uses fixture ports and disposes its own child; it does not test media playback.
|
||||
- `node refactor/scripts/library-build-comparison.mjs`: copy current package sources
|
||||
into a retained fixture, build all libraries with frozen pre-migration scripts
|
||||
and current scripts at the same paths, compare all artifact hashes. This is an
|
||||
explicit whole-build validation, not part of every quick unit test.
|
||||
|
||||
Remote Linux/macOS/Windows CI remains a separate release gate. The local browser
|
||||
evidence for MOD-02 is Windows; source inspection of Linux watcher registration
|
||||
does not substitute for a remote Linux run. Lerna and root installation hooks were
|
||||
not changed. The scaffold's separate guide remains in `scripts/plugin/README.md`.
|
||||
@@ -0,0 +1,45 @@
|
||||
import type { Plugin } from 'vite'
|
||||
import { Buffer } from 'node:buffer'
|
||||
import { createHash } from 'node:crypto'
|
||||
import fs from 'node:fs'
|
||||
import path from 'node:path'
|
||||
import process from 'node:process'
|
||||
import { gzipSync } from 'node:zlib'
|
||||
|
||||
export function createBuildAnalysis() {
|
||||
const root = process.cwd()
|
||||
const parent = path.join(root, 'refactor/.cache/build-analysis')
|
||||
fs.mkdirSync(parent, { recursive: true })
|
||||
const directory = fs.mkdtempSync(path.join(parent, 'run-'))
|
||||
fs.writeFileSync(path.join(parent, 'latest.json'), `${JSON.stringify({ output: path.relative(root, directory).replaceAll('\\', '/') })}\n`)
|
||||
return { root, directory }
|
||||
}
|
||||
|
||||
export function buildAnalysisPlugin({ root, directory }: ReturnType<typeof createBuildAnalysis>, name: string, format: string): Plugin {
|
||||
return {
|
||||
name: 'artplayer-build-analysis',
|
||||
generateBundle(_options, bundle) {
|
||||
for (const chunk of Object.values(bundle).filter(item => item.type === 'chunk')) {
|
||||
const modules = Object.entries(chunk.modules).map(([id, info]) => {
|
||||
const normalized = id.replaceAll('\\', '/')
|
||||
const dependency = normalized.lastIndexOf('/node_modules/')
|
||||
const file = id.startsWith('\0')
|
||||
? `virtual:${id.slice(1)}`
|
||||
: dependency === -1 ? path.relative(root, id).replaceAll('\\', '/') : normalized.slice(dependency + 1)
|
||||
return { file, renderedBytes: Buffer.byteLength(info.code || ''), renderedCodeUnits: info.renderedLength, originalCodeUnits: info.originalLength, renderedExports: info.renderedExports, removedExports: info.removedExports }
|
||||
}).sort((a, b) => b.renderedBytes - a.renderedBytes || a.file.localeCompare(b.file))
|
||||
const report = {
|
||||
schemaVersion: 1,
|
||||
name,
|
||||
format,
|
||||
node: process.version,
|
||||
file: chunk.fileName,
|
||||
artifact: { bytes: Buffer.byteLength(chunk.code), gzip9: gzipSync(chunk.code, { level: 9 }).length, sha256: createHash('sha256').update(chunk.code).digest('hex') },
|
||||
note: 'Rendered UTF-8 bytes precede final chunk minification; they are not additive compressed sizes or download measurements. Rollup lengths are separately recorded as UTF-16 code units. Original code units describe Rollup input, not raw TypeScript.',
|
||||
modules,
|
||||
}
|
||||
fs.writeFileSync(path.join(directory, `${name}-${format}.json`), `${JSON.stringify(report, null, 2)}\n`)
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
import type { Plugin } from 'vite'
|
||||
import fs from 'node:fs'
|
||||
import path from 'node:path'
|
||||
|
||||
export function getBanner(project: string, name: string, version: string): string {
|
||||
const noticeFile = path.join(project, 'THIRD_PARTY_NOTICES')
|
||||
const notices = fs.existsSync(noticeFile) ? fs.readFileSync(noticeFile, 'utf8').trim() : ''
|
||||
const noticeBlock = notices ? `\n *\n${notices.split(/\r?\n/).map(line => line.trimEnd() ? ` * ${line.trimEnd()}` : ' *').join('\n')}` : ''
|
||||
return `/*!
|
||||
* ${name}.js v${version}
|
||||
* Github: https://github.com/zhw2590582/ArtPlayer
|
||||
* (c) 2017-${new Date().getFullYear()} Harvey Zhao
|
||||
* Released under the MIT License.${noticeBlock}
|
||||
*/`
|
||||
}
|
||||
|
||||
export function bannerPlugin(name: string, banner: string): Plugin {
|
||||
return {
|
||||
name: 'add-banner-and-global',
|
||||
generateBundle(outputOptions, bundle) {
|
||||
for (const chunk of Object.values(bundle)) {
|
||||
if (chunk.type !== 'chunk')
|
||||
continue
|
||||
let code = chunk.code.replace(/`\/\*![\s\S]*?\*\/\n/g, '`')
|
||||
if (!code.startsWith('/*!'))
|
||||
code = `${banner}\n${code}`
|
||||
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}`)
|
||||
code = code.replace(amd, `"function"==typeof define&&define.amd?(${global}.${name}=$1(),define(function(){return ${global}.${name}})):`)
|
||||
}
|
||||
chunk.code = code
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
export function workerBannerPlugin(): Plugin {
|
||||
return {
|
||||
name: 'remove-worker-banner',
|
||||
generateBundle(_, bundle) {
|
||||
for (const chunk of Object.values(bundle)) {
|
||||
if (chunk.type === 'chunk' && chunk.code.startsWith('/*!'))
|
||||
chunk.code = chunk.code.replace(/^\/\*![\s\S]*?\*\/\s*/, '')
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
import type { BuildOptions, InlineConfig, LibraryFormats, Plugin } from 'vite'
|
||||
import { bannerPlugin, workerBannerPlugin } from './banner.ts'
|
||||
|
||||
export interface LibraryConfigOptions {
|
||||
entry: string
|
||||
outDir: string
|
||||
name: string
|
||||
format: LibraryFormats
|
||||
fileName: string
|
||||
minify?: boolean | 'esbuild' | 'terser'
|
||||
target?: string | string[]
|
||||
banner?: string
|
||||
emptyOutDir?: boolean
|
||||
}
|
||||
|
||||
type LibraryConfig = InlineConfig & {
|
||||
build: BuildOptions & { rollupOptions: NonNullable<BuildOptions['rollupOptions']> & { plugins: Plugin[] } }
|
||||
define: Record<string, string>
|
||||
}
|
||||
|
||||
export function getViteBuildConfig(options: LibraryConfigOptions): LibraryConfig {
|
||||
const { entry, outDir, name, format, fileName, minify = 'esbuild', target = 'es2020', banner, emptyOutDir = false } = options
|
||||
return {
|
||||
configFile: false,
|
||||
publicDir: false,
|
||||
logLevel: 'warn',
|
||||
build: {
|
||||
outDir,
|
||||
emptyOutDir,
|
||||
minify,
|
||||
target,
|
||||
lib: { entry, name, formats: [format], fileName: () => fileName },
|
||||
rollupOptions: {
|
||||
output: { exports: 'default' },
|
||||
plugins: banner ? [bannerPlugin(name, banner)] : [],
|
||||
},
|
||||
},
|
||||
worker: { format: 'es', plugins: () => [workerBannerPlugin()] },
|
||||
define: { 'process.env.NODE_ENV': JSON.stringify(minify ? 'production' : 'development') },
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
import fs from 'node:fs'
|
||||
import path from 'node:path'
|
||||
import servor from 'servor'
|
||||
import openBrowser from 'servor/utils/openBrowser.js'
|
||||
import { build as viteBuild } from 'vite'
|
||||
import { getViteBuildConfig } from './config.ts'
|
||||
import { getGlobalName } from './names.ts'
|
||||
import { getEntryFile, getProjects, selectProjects } from './projects.ts'
|
||||
import { createRebuildQueue } from './rebuild.ts'
|
||||
|
||||
async function develop(projectPath: string, name: string, open: boolean, port: number) {
|
||||
const uncompiledPath = path.resolve(`docs/uncompiled/${name}`)
|
||||
let browserOpened = false
|
||||
|
||||
// Servor enumerates existing directories when installing its Linux watchers.
|
||||
fs.mkdirSync(uncompiledPath, { recursive: true })
|
||||
|
||||
const { url } = await servor({
|
||||
root: 'docs',
|
||||
fallback: 'index.html',
|
||||
reload: true,
|
||||
port,
|
||||
})
|
||||
|
||||
async function buildBundle() {
|
||||
const startTime = Date.now()
|
||||
try {
|
||||
const config = getViteBuildConfig({
|
||||
entry: getEntryFile(projectPath),
|
||||
outDir: uncompiledPath,
|
||||
name: getGlobalName(name),
|
||||
format: 'iife',
|
||||
fileName: 'index.js',
|
||||
minify: false,
|
||||
emptyOutDir: true,
|
||||
})
|
||||
config.define['process.env.NODE_ENV'] = JSON.stringify('development')
|
||||
|
||||
await viteBuild({ root: projectPath, ...config })
|
||||
console.log(`[${name}] ✅ Built in ${Date.now() - startTime}ms`)
|
||||
|
||||
if (open && !browserOpened) {
|
||||
browserOpened = true
|
||||
openBrowser(url)
|
||||
}
|
||||
}
|
||||
catch (error) {
|
||||
console.error(`[${name}] ❌ Build error:`, error)
|
||||
}
|
||||
}
|
||||
|
||||
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}...`)
|
||||
|
||||
fs.watch(srcPath, { recursive: true }, async (_, filename) => {
|
||||
if (filename) {
|
||||
console.log(`[${name}] 📝 Changed: ${filename}`)
|
||||
await rebuild()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
export async function runDevelop({ port = 8082 }: { port?: number } = {}): Promise<void> {
|
||||
const projects = getProjects()
|
||||
const { names, open } = await selectProjects(projects, 'dev')
|
||||
const name = names[0]
|
||||
if (name)
|
||||
await develop(projects[name]!, name, open, port)
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
export function toPascalCase(name: string): string {
|
||||
return name.replace(/(^|-)([a-z])/g, (_, _prefix: string, letter: string) => letter.toUpperCase())
|
||||
}
|
||||
|
||||
export function toCamelCase(name: string): string {
|
||||
return name.replace(/-([a-z])/g, (_, letter: string) => letter.toUpperCase())
|
||||
}
|
||||
|
||||
export function getGlobalName(name: string): string {
|
||||
return name === 'artplayer' || name.startsWith('artplayer-tool-') ? toPascalCase(name) : toCamelCase(name)
|
||||
}
|
||||
@@ -0,0 +1,88 @@
|
||||
import fs from 'node:fs'
|
||||
import path from 'node:path'
|
||||
import cpy from 'cpy'
|
||||
import { build as viteBuild } from 'vite'
|
||||
import { buildAnalysisPlugin, createBuildAnalysis } from './analysis.ts'
|
||||
import { getBanner } from './banner.ts'
|
||||
import { getViteBuildConfig } from './config.ts'
|
||||
import { getGlobalName } from './names.ts'
|
||||
import { getEntryFile, getProjects, selectProjects } from './projects.ts'
|
||||
|
||||
const BUILD_FORMATS = {
|
||||
main: { format: 'umd', ext: '.js', target: 'es2020', minify: 'terser' },
|
||||
legacy: { format: 'umd', ext: '.legacy.js', target: 'es2015', minify: 'terser' },
|
||||
esm: { format: 'es', ext: '.mjs', target: 'es2020', minify: false },
|
||||
} as const
|
||||
|
||||
async function build(projectDir: string, compiledPath: string, name: string, targetName: keyof typeof BUILD_FORMATS, clean = false, analysis?: ReturnType<typeof createBuildAnalysis>) {
|
||||
const manifest: unknown = JSON.parse(fs.readFileSync(path.join(projectDir, 'package.json'), 'utf-8'))
|
||||
if (!manifest || typeof manifest !== 'object' || !('version' in manifest) || typeof manifest.version !== 'string')
|
||||
throw new Error(`Missing package version: ${name}`)
|
||||
const { version } = manifest
|
||||
const distDir = path.join(projectDir, 'dist')
|
||||
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 })
|
||||
}
|
||||
|
||||
const startTime = Date.now()
|
||||
const { format, ext, target, minify } = BUILD_FORMATS[targetName]
|
||||
const fileName = `${name}${ext}`
|
||||
|
||||
const config = getViteBuildConfig({
|
||||
entry: entryFile,
|
||||
outDir: distDir,
|
||||
name: getGlobalName(name),
|
||||
format,
|
||||
fileName,
|
||||
minify,
|
||||
target,
|
||||
banner: getBanner(projectDir, name, version),
|
||||
})
|
||||
|
||||
// Terser options for all builds (preserve banner comments starting with /*!)
|
||||
const isLegacy = targetName === 'legacy'
|
||||
config.build.terserOptions = {
|
||||
ecma: isLegacy ? 5 : 2020,
|
||||
compress: { ecma: isLegacy ? 5 : 2020, comparisons: false, inline: 2 },
|
||||
mangle: { safari10: true },
|
||||
format: {
|
||||
ecma: isLegacy ? 5 : 2020,
|
||||
comments: /^!/,
|
||||
ascii_only: true,
|
||||
max_line_len: false, // No line length limit
|
||||
},
|
||||
}
|
||||
if (analysis)
|
||||
config.build.rollupOptions.plugins.push(buildAnalysisPlugin(analysis, name, targetName))
|
||||
|
||||
await viteBuild({ root: projectDir, ...config })
|
||||
|
||||
const outFile = path.join(distDir, fileName)
|
||||
const size = (fs.statSync(outFile).size / 1024).toFixed(2)
|
||||
await cpy(outFile, compiledPath, { flat: true })
|
||||
|
||||
console.log(`✨ Built@${targetName} ${name}@${version} Time@${Date.now() - startTime}ms Size@${size}kb`)
|
||||
}
|
||||
|
||||
async function buildProject(projectDir: string, compiledPath: string, name: string, analysis?: ReturnType<typeof createBuildAnalysis>) {
|
||||
await build(projectDir, compiledPath, name, 'main', true, analysis)
|
||||
await build(projectDir, compiledPath, name, 'legacy', false, analysis)
|
||||
await build(projectDir, compiledPath, name, 'esm', false, analysis)
|
||||
}
|
||||
|
||||
export async function runBuild(): Promise<void> {
|
||||
const projects = getProjects()
|
||||
const compiledPath = path.resolve('docs/compiled')
|
||||
const { names, analyze } = await selectProjects(projects, 'build')
|
||||
const analysis = analyze ? createBuildAnalysis() : undefined
|
||||
for (const name of names)
|
||||
await buildProject(projects[name]!, compiledPath, name, analysis)
|
||||
if (analysis)
|
||||
console.log(`Build analysis: ${analysis.directory}`)
|
||||
if (names.length)
|
||||
console.log(`✅ Finished building ${names.length} package(s)!`)
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
import fs from 'node:fs'
|
||||
import path from 'node:path'
|
||||
import process from 'node:process'
|
||||
import prompts from 'prompts'
|
||||
|
||||
export type Projects = Record<string, string>
|
||||
export type Command = 'build' | 'dev'
|
||||
export interface Selection { names: string[], open: boolean, analyze?: boolean, help?: boolean }
|
||||
|
||||
export function getProjects(root = process.cwd()): Projects {
|
||||
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: string): string {
|
||||
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: string[], projects: Projects, command: Command): Selection {
|
||||
const names: string[] = []
|
||||
let open = true
|
||||
let analyze = false
|
||||
for (const arg of args) {
|
||||
if (arg === '--no-open' && command === 'dev')
|
||||
open = false
|
||||
else if (arg === '--analyze' && command === 'build')
|
||||
analyze = true
|
||||
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, analyze }
|
||||
}
|
||||
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, analyze }
|
||||
}
|
||||
|
||||
export async function selectProjects(projects: Projects, command: Command) {
|
||||
const selection = parseProjects(process.argv.slice(2), projects, command)
|
||||
if (selection.help) {
|
||||
console.log(command === 'build' ? 'Usage: yarn build [all | package ...] [--analyze]' : '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: () => void | Promise<void>): () => Promise<void> {
|
||||
let pending = false
|
||||
let active: Promise<void> | undefined
|
||||
return () => {
|
||||
pending = true
|
||||
if (!active) {
|
||||
active = Promise.resolve().then(async () => {
|
||||
while (pending) {
|
||||
pending = false
|
||||
await build()
|
||||
}
|
||||
}).finally(() => { active = undefined })
|
||||
}
|
||||
return active
|
||||
}
|
||||
}
|
||||
Vendored
+22
@@ -0,0 +1,22 @@
|
||||
// Only the installed APIs used by library tooling; not general SDK declarations.
|
||||
declare module 'prompts' {
|
||||
export default function prompts(question: {
|
||||
type: 'select'
|
||||
name: 'value'
|
||||
message: string
|
||||
choices: { title: string, value: string }[]
|
||||
}): Promise<{ value?: string }>
|
||||
}
|
||||
|
||||
declare module 'servor' {
|
||||
export default function servor(options: {
|
||||
root: string
|
||||
fallback: string
|
||||
reload: boolean
|
||||
port: number
|
||||
}): Promise<{ url: string }>
|
||||
}
|
||||
|
||||
declare module 'servor/utils/openBrowser.js' {
|
||||
export default function openBrowser(url: string): true | undefined
|
||||
}
|
||||
@@ -29,6 +29,10 @@ export function verifyPerformanceArtifacts(root, mapFile) {
|
||||
toolchain[file] = digest(path.join(root, file))
|
||||
assert.equal(digest(path.join(directory, 'build', file)), toolchain[file], `Stale build input: ${file}`)
|
||||
}
|
||||
const libraryInputs = location => Object.fromEntries(walk(path.join(location, 'scripts/library')).filter(file => /\.(?:[cm]?[jt]s|json)$/.test(file)).sort().map(file => [path.relative(location, file).replaceAll('\\', '/'), digest(file)]))
|
||||
const currentLibrary = libraryInputs(root)
|
||||
assert.deepEqual(libraryInputs(path.join(directory, 'build')), currentLibrary, 'Stale library build inputs; rerun yarn test:package')
|
||||
Object.assign(toolchain, currentLibrary)
|
||||
const inputs = []
|
||||
for (const name of ['artplayer', 'artplayer-plugin-chapter']) {
|
||||
const pkg = report.packages.find(pkg => pkg.name === name)
|
||||
|
||||
+1
-68
@@ -1,68 +1 @@
|
||||
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
|
||||
let analyze = false
|
||||
for (const arg of args) {
|
||||
if (arg === '--no-open' && command === 'dev')
|
||||
open = false
|
||||
else if (arg === '--analyze' && command === 'build')
|
||||
analyze = true
|
||||
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, analyze }
|
||||
}
|
||||
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, analyze }
|
||||
}
|
||||
|
||||
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 ...] [--analyze]' : '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
|
||||
}
|
||||
export { getEntryFile, getProjects, parseProjects, selectProjects } from './library/projects.ts'
|
||||
+1
-16
@@ -1,16 +1 @@
|
||||
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
|
||||
}
|
||||
}
|
||||
export { createRebuildQueue } from './library/rebuild.ts'
|
||||
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"extends": "../tsconfig.base.json",
|
||||
"compilerOptions": {
|
||||
"lib": ["ES2022", "DOM", "DOM.Iterable"],
|
||||
"types": ["node"],
|
||||
"allowImportingTsExtensions": true,
|
||||
"checkJs": true
|
||||
},
|
||||
"include": ["library/**/*.ts", "build.js", "dev.js", "utils.js", "projects.js", "rebuild.js", "build-analysis.mjs"]
|
||||
}
|
||||
+3
-114
@@ -1,114 +1,3 @@
|
||||
export { getProjects } from './projects.js'
|
||||
|
||||
// Convert kebab-case to PascalCase: artplayer-plugin-ads -> ArtplayerPluginAds
|
||||
export function toPascalCase(name) {
|
||||
return name.replace(/(^|-)([a-z])/g, (_, _p, c) => c.toUpperCase())
|
||||
}
|
||||
|
||||
// Convert kebab-case to camelCase: artplayer-plugin-ads -> artplayerPluginAds
|
||||
export function toCamelCase(name) {
|
||||
return name.replace(/-([a-z])/g, (_, c) => c.toUpperCase())
|
||||
}
|
||||
|
||||
// Get the global variable name for a package
|
||||
// - artplayer, artplayer-tool-* -> PascalCase (class)
|
||||
// - artplayer-plugin-*, artplayer-proxy-* -> camelCase (function)
|
||||
export function getGlobalName(name) {
|
||||
if (name === 'artplayer' || name.startsWith('artplayer-tool-')) {
|
||||
return toPascalCase(name)
|
||||
}
|
||||
return toCamelCase(name)
|
||||
}
|
||||
|
||||
// Get Vite build config
|
||||
export function getViteBuildConfig(options) {
|
||||
const {
|
||||
entry,
|
||||
outDir,
|
||||
name,
|
||||
format,
|
||||
fileName,
|
||||
minify = 'esbuild',
|
||||
target = 'es2020',
|
||||
banner,
|
||||
emptyOutDir = false,
|
||||
} = options
|
||||
|
||||
const config = {
|
||||
configFile: false,
|
||||
publicDir: false,
|
||||
logLevel: 'warn',
|
||||
build: {
|
||||
outDir,
|
||||
emptyOutDir,
|
||||
minify,
|
||||
target,
|
||||
lib: {
|
||||
entry,
|
||||
name,
|
||||
formats: [format],
|
||||
fileName: () => fileName,
|
||||
},
|
||||
rollupOptions: {
|
||||
output: {
|
||||
exports: 'default',
|
||||
},
|
||||
plugins: banner
|
||||
? [{
|
||||
name: 'add-banner-and-global',
|
||||
generateBundle(outputOptions, bundle) {
|
||||
for (const chunk of Object.values(bundle)) {
|
||||
if (chunk.type === 'chunk') {
|
||||
// Remove any inline worker banners (inside template literals)
|
||||
// Match: `/*! ... */\n and replace with just `
|
||||
let code = chunk.code.replace(/`\/\*![\s\S]*?\*\/\n/g, '`')
|
||||
// Add main banner if not present
|
||||
if (!code.startsWith('/*!')) {
|
||||
code = `${banner}\n${code}`
|
||||
}
|
||||
// For UMD format, modify the wrapper to always expose global
|
||||
// even when AMD loader is present (fix for RequireJS environments)
|
||||
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(
|
||||
amd,
|
||||
`"function"==typeof define&&define.amd?(${global}.${name}=$1(),define(function(){return ${global}.${name}})):`,
|
||||
)
|
||||
}
|
||||
chunk.code = code
|
||||
}
|
||||
}
|
||||
},
|
||||
}]
|
||||
: [],
|
||||
},
|
||||
},
|
||||
// Disable banner for inline workers
|
||||
worker: {
|
||||
format: 'es',
|
||||
plugins: () => [{
|
||||
name: 'remove-worker-banner',
|
||||
generateBundle(_, bundle) {
|
||||
for (const chunk of Object.values(bundle)) {
|
||||
if (chunk.type === 'chunk' && chunk.code.startsWith('/*!')) {
|
||||
// Remove banner from worker code
|
||||
chunk.code = chunk.code.replace(/^\/\*![\s\S]*?\*\/\s*/, '')
|
||||
}
|
||||
}
|
||||
},
|
||||
}],
|
||||
},
|
||||
define: {
|
||||
'process.env.NODE_ENV': JSON.stringify(minify ? 'production' : 'development'),
|
||||
},
|
||||
}
|
||||
|
||||
return config
|
||||
}
|
||||
export { getViteBuildConfig } from './library/config.ts'
|
||||
export { getGlobalName, toCamelCase, toPascalCase } from './library/names.ts'
|
||||
export { getProjects } from './library/projects.ts'
|
||||
@@ -1,5 +1,10 @@
|
||||
# Tests and fixture ownership
|
||||
|
||||
`yarn test:library` runs actual library builds, build-input freshness checks and the
|
||||
scaffold's generated consumers. The new build modules live in `scripts/library/`;
|
||||
`yarn typecheck:library` checks them and the retained JS/MJS entrypoints. Browser
|
||||
development/watch/error-recovery coverage is `browser/library-development.spec.js`.
|
||||
|
||||
`yarn test:scaffold` covers old plugin-generator defects, exclusive writes and
|
||||
rollback, the historical CLI path, and actual generated package builds and type
|
||||
consumers. It runs in ignored fixtures without adding a workspace or real demo;
|
||||
|
||||
@@ -1,5 +1,11 @@
|
||||
# Browser regression entry
|
||||
|
||||
`library-development.spec.js` starts the actual typed development runner on an
|
||||
isolated fixture port. It checks generated TS/Less/SVG/inline-worker assets,
|
||||
automatic reload on edits and recovery after a compiler error in all three
|
||||
engines. It owns and stops only its child server, never the user's port 8082
|
||||
session. This is development-tool validation, not media playback acceptance.
|
||||
|
||||
`site-loading.spec.js` covers actual desktop/mobile loading, dependency order,
|
||||
failure/retry/AMD restoration, example priority, repeated Run and stale responses,
|
||||
plus Run Code and language routing. External analytics/ads are inert test routes;
|
||||
|
||||
@@ -0,0 +1,101 @@
|
||||
import { spawn } from 'node:child_process'
|
||||
import fs from 'node:fs'
|
||||
import net from 'node:net'
|
||||
import path from 'node:path'
|
||||
import process from 'node:process'
|
||||
import { fileURLToPath, pathToFileURL } from 'node:url'
|
||||
import { expect, test } from '@playwright/test'
|
||||
|
||||
const root = fileURLToPath(new URL('../../', import.meta.url))
|
||||
const cache = path.join(root, 'refactor/.cache')
|
||||
|
||||
function removeFixture(directory) {
|
||||
if (fs.realpathSync(directory) !== directory || path.dirname(directory) !== fs.realpathSync(cache))
|
||||
throw new Error(`Refusing redirected fixture cleanup: ${directory}`)
|
||||
fs.rmSync(directory, { recursive: true, force: true })
|
||||
}
|
||||
|
||||
async function freePort() {
|
||||
const server = net.createServer()
|
||||
await new Promise((resolve, reject) => {
|
||||
server.once('error', reject)
|
||||
server.listen(0, resolve)
|
||||
})
|
||||
const port = server.address().port
|
||||
await new Promise(resolve => server.close(resolve))
|
||||
return port
|
||||
}
|
||||
|
||||
test('typed development builds real assets and workers, watches edits and recovers from compilation errors', async ({ page }, testInfo) => {
|
||||
test.setTimeout(60000)
|
||||
const directory = fs.mkdtempSync(path.join(cache, 'library-development-'))
|
||||
const name = 'artplayer-plugin-build-probe'
|
||||
const source = path.join(directory, 'packages', name, 'src')
|
||||
fs.mkdirSync(source, { recursive: true })
|
||||
fs.cpSync(path.join(root, 'refactor/fixtures/build'), source, { recursive: true })
|
||||
fs.writeFileSync(path.join(source, '../package.json'), JSON.stringify({ name, version: '1.0.0' }))
|
||||
fs.mkdirSync(path.join(directory, 'docs'))
|
||||
fs.writeFileSync(path.join(directory, 'docs/index.html'), `<!doctype html><html><body><main></main>
|
||||
<script src="/uncompiled/${name}/index.js"></script><script>
|
||||
window.probe = artplayerPluginBuildProbe();
|
||||
document.querySelector('main').textContent = String(probe.value);
|
||||
</script></body></html>`)
|
||||
const port = await freePort()
|
||||
const url = `http://localhost:${port}`
|
||||
const module = pathToFileURL(path.join(root, 'scripts/library/development.ts')).href
|
||||
const code = `import { runDevelop } from ${JSON.stringify(module)};
|
||||
process.argv = [process.execPath, 'fixture.js', '${name}', '--no-open'];
|
||||
await runDevelop({port:${port}});`
|
||||
const launcher = path.join(directory, 'launch.mjs')
|
||||
fs.writeFileSync(launcher, code)
|
||||
const child = spawn(process.execPath, [launcher], { cwd: directory, windowsHide: true, stdio: ['ignore', 'pipe', 'pipe'] })
|
||||
let output = ''
|
||||
child.stdout.on('data', (data) => {
|
||||
output += data
|
||||
})
|
||||
child.stderr.on('data', (data) => {
|
||||
output += data
|
||||
})
|
||||
const exited = new Promise(resolve => child.once('exit', resolve))
|
||||
try {
|
||||
await expect.poll(() => output, { timeout: 20000 }).toContain('Watching')
|
||||
expect(child.exitCode).toBeNull()
|
||||
await page.goto(url)
|
||||
await expect(page.locator('main')).toHaveText('42')
|
||||
expect(await page.evaluate(() => window.probe.css)).toContain('#123abc')
|
||||
expect(await page.evaluate(() => window.probe.svg)).toContain('<svg')
|
||||
const workerResult = await page.evaluate(() => new Promise((resolve, reject) => {
|
||||
const worker = window.probe.createWorker()
|
||||
worker.onmessage = ({ data }) => {
|
||||
worker.terminate()
|
||||
resolve(data)
|
||||
}
|
||||
worker.onerror = (error) => {
|
||||
worker.terminate()
|
||||
reject(new Error(error.message))
|
||||
}
|
||||
worker.postMessage(21)
|
||||
}))
|
||||
expect(workerResult).toBe(22)
|
||||
const entry = path.join(source, 'index.ts')
|
||||
const original = fs.readFileSync(entry, 'utf8')
|
||||
const before = output.length
|
||||
fs.writeFileSync(entry, original.replace('add(20, 22)', 'add(20, 23)'))
|
||||
await expect.poll(() => output.slice(before), { timeout: 15000 }).toContain('Built in')
|
||||
await expect(page.locator('main')).toHaveText('43')
|
||||
const valid = output.length
|
||||
fs.writeFileSync(entry, 'export default function broken( {')
|
||||
await expect.poll(() => output.slice(valid), { timeout: 15000 }).toContain('Build error')
|
||||
expect(child.exitCode).toBeNull()
|
||||
const failed = output.length
|
||||
fs.writeFileSync(entry, original.replace('add(20, 22)', 'add(20, 24)'))
|
||||
await expect.poll(() => output.slice(failed), { timeout: 15000 }).toContain('Built in')
|
||||
await expect(page.locator('main')).toHaveText('44')
|
||||
}
|
||||
finally {
|
||||
await testInfo.attach('development-server.log', { body: output, contentType: 'text/plain' })
|
||||
child.kill()
|
||||
await exited
|
||||
removeFixture(directory)
|
||||
}
|
||||
})
|
||||
@@ -87,6 +87,7 @@ test('performance refuses stale sources, build tools and altered installed bundl
|
||||
'scripts/build.js': 'build fixture\n',
|
||||
'scripts/utils.js': 'utils fixture\n',
|
||||
'scripts/projects.js': 'projects fixture\n',
|
||||
'scripts/library/config.ts': 'typed build fixture\n',
|
||||
}
|
||||
for (const [file, content] of Object.entries(toolchain)) {
|
||||
write(path.join(root, file), content)
|
||||
@@ -115,6 +116,12 @@ test('performance refuses stale sources, build tools and altered installed bundl
|
||||
write(path.join(root, 'scripts/build.js'), 'changed compiler\n')
|
||||
assert.throws(() => verifyPerformanceArtifacts(root, map), /Stale build input/)
|
||||
write(path.join(root, 'scripts/build.js'), toolchain['scripts/build.js'])
|
||||
write(path.join(root, 'scripts/library/config.ts'), 'changed TS config\n')
|
||||
assert.throws(() => verifyPerformanceArtifacts(root, map), /Stale library build inputs/)
|
||||
write(path.join(root, 'scripts/library/config.ts'), toolchain['scripts/library/config.ts'])
|
||||
write(path.join(snapshot, 'scripts/library/removed.ts'), 'stale module\n')
|
||||
assert.throws(() => verifyPerformanceArtifacts(root, map), /Stale library build inputs/)
|
||||
fs.unlinkSync(path.join(snapshot, 'scripts/library/removed.ts'))
|
||||
write(path.join(output, 'artifacts/artplayer/dist/artplayer.js'), 'changed bundle\n')
|
||||
assert.throws(() => verifyPerformanceArtifacts(root, map), /Installed performance artifact changed/)
|
||||
})
|
||||
Reference in new issue
Block a user