diff --git a/refactor/README.md b/refactor/README.md index cc8568b61..5f998865b 100644 --- a/refactor/README.md +++ b/refactor/README.md @@ -42,6 +42,7 @@ | [第三方来源清单](third-party.json) | 依赖解析、vendor 文件指纹、SDK/CDN 与许可来源缺口 | | [消费者与真实环境矩阵](environment-matrix.md) | 22 包的版本依据、能力/设备/样本、现有证据、验证任务及发布影响 | | [类型检查与迁移入口](typechecking.md) | 根/分包配置、当前/旧编译器、严格正反例和历史声明错误 | +| [JS/TS 构建与开发](build-development.md) | 非交互选包、开发重建、资源夹具和 AMD 全局修复 | ## 维护规则 diff --git a/refactor/baselines/build-validation.json b/refactor/baselines/build-validation.json new file mode 100644 index 000000000..6820ef092 --- /dev/null +++ b/refactor/baselines/build-validation.json @@ -0,0 +1,615 @@ +{ + "schemaVersion": 1, + "task": "ENG-06", + "sourceCommit": "77148242", + "capturedAt": "2026-09-10T13:46:01.009Z", + "runtime": { + "node": "24.21.0", + "yarn": "1.22.22", + "vite": "7.3.6" + }, + "scope": "Build and entrypoint validation, not package feature/device or npm publication approval", + "sourceHashAlgorithm": "sha256-lf", + "sources": { + "scripts/build.js": "8df17bbe2d38b40f97695a29e4559ead5fe55dd2b031c2f4b09506ed09b6fbbc", + "scripts/dev.js": "a611ebdea8bd3ffe811ecb3fa47796a038cf708bb72d30aee2bb12bc61c33c08", + "scripts/projects.js": "76c0465e651b82d414027b491b962bb4751d3a2e86ea3b4b57908a6c29a3b038", + "scripts/rebuild.js": "56b90edf29597550476c9d2c9ce3d79720d3bf421c4ee9e6a6890b77cca9615a", + "scripts/utils.js": "4bf07ce5a1029df6360e402d128233e2ce9873316270473f6c5f23d88533a1ec", + "refactor/fixtures/build/browser.html": "f23cf184f9280b13e6fc49ed81182030f56072f0abae0027592ba460434046ca", + "refactor/fixtures/build/icon.svg": "794225a0e8d9464f748ece35b6fd3b49cf6175088881c4ce1ddd4c0c49ccc759", + "refactor/fixtures/build/index.ts": "926ceac2f574f059ed43ecf734129111766023f1ec017d634dc750c2d5b37e84", + "refactor/fixtures/build/shared.js": "d7b9fb6cdead2fb729501d24b6dd6c5e8a801b95e665069929e064f34e418056", + "refactor/fixtures/build/style.less": "d706fd1267fac56cb207d41d1efe2b19c5b200c1ad346d9f11a9b4e1fad2d5b0", + "refactor/fixtures/build/worker.ts": "2c98d00f6cc5efa557839b98fea27dc9004295910c52ae22f60437a2be7dd126" + }, + "builds": { + "before": "Original build all in isolated checkout at 77148242", + "after": "Changed build all, then final explicit 21-package build with current root scripts", + "libraries": 21, + "formats": 63, + "identical": 39, + "amdReceiverOnly": 24, + "reverseAmdPatchRestoresAllOriginalHashes": true, + "artifacts": [ + { + "file": "packages/artplayer/dist/artplayer.js", + "before": "9ad044a601dafc24f449b690403fb1c343b40e99ffb31043272189fc3c1e7bfa", + "after": "9ad044a601dafc24f449b690403fb1c343b40e99ffb31043272189fc3c1e7bfa" + }, + { + "file": "packages/artplayer/dist/artplayer.legacy.js", + "before": "71eebcca3192000c58e20f2e3cb76e86c05e33b5a1fab0add20436a5933a3e87", + "after": "71eebcca3192000c58e20f2e3cb76e86c05e33b5a1fab0add20436a5933a3e87" + }, + { + "file": "packages/artplayer/dist/artplayer.mjs", + "before": "ac72f41624383ba853474161affb72b5887644449bf7cb18f6d18edeb2f6f997", + "after": "ac72f41624383ba853474161affb72b5887644449bf7cb18f6d18edeb2f6f997" + }, + { + "file": "packages/artplayer-plugin-ads/dist/artplayer-plugin-ads.js", + "before": "ceb2d8a23a6735c161b31a4a427d8a757679aaf8a5a106ef40a6f047061f1df8", + "after": "f01b375191b07e7f691927ed6f7258ee04b06aa1f83e5c91cea471326fa80086" + }, + { + "file": "packages/artplayer-plugin-ads/dist/artplayer-plugin-ads.legacy.js", + "before": "1e10c3a6b51de18e54cd02dd342ab4010fc466de6257659cf1d2e087642aa9a3", + "after": "6259121de4a2e78ccd703fbc54f323a915f8bc0f6b2929df256e437519bcd439" + }, + { + "file": "packages/artplayer-plugin-ads/dist/artplayer-plugin-ads.mjs", + "before": "0919678423424047b28d9d803835d9ebc98f4477e04e58d497e8ed8c48b95008", + "after": "0919678423424047b28d9d803835d9ebc98f4477e04e58d497e8ed8c48b95008" + }, + { + "file": "packages/artplayer-plugin-ambilight/dist/artplayer-plugin-ambilight.js", + "before": "8f32290c65a13b0a3026dee0045fef4125a9c9ac788ecb1675aef05f79172693", + "after": "5442f8c414997dd38dd6b33e98af6679a1825b9ebabd2b81892b4607e3cef53e" + }, + { + "file": "packages/artplayer-plugin-ambilight/dist/artplayer-plugin-ambilight.legacy.js", + "before": "8f32290c65a13b0a3026dee0045fef4125a9c9ac788ecb1675aef05f79172693", + "after": "5442f8c414997dd38dd6b33e98af6679a1825b9ebabd2b81892b4607e3cef53e" + }, + { + "file": "packages/artplayer-plugin-ambilight/dist/artplayer-plugin-ambilight.mjs", + "before": "77486db6d24f50420c952de5b37d226e404089730c7c1c6be226c836f4e85f9c", + "after": "77486db6d24f50420c952de5b37d226e404089730c7c1c6be226c836f4e85f9c" + }, + { + "file": "packages/artplayer-plugin-asr/dist/artplayer-plugin-asr.js", + "before": "7f975b131a9ea207f88a157aafec778bc4906230500db9b30f5bbfbf327b251d", + "after": "7f975b131a9ea207f88a157aafec778bc4906230500db9b30f5bbfbf327b251d" + }, + { + "file": "packages/artplayer-plugin-asr/dist/artplayer-plugin-asr.legacy.js", + "before": "8b9f07ce97a8ce2e4f29e41e79257be530fd7a76558369d34c3d7497ea606c33", + "after": "8b9f07ce97a8ce2e4f29e41e79257be530fd7a76558369d34c3d7497ea606c33" + }, + { + "file": "packages/artplayer-plugin-asr/dist/artplayer-plugin-asr.mjs", + "before": "f1c4274541e86544b65936ff470413cfc7001999ebc193969f95c123be48cd61", + "after": "f1c4274541e86544b65936ff470413cfc7001999ebc193969f95c123be48cd61" + }, + { + "file": "packages/artplayer-plugin-audio-track/dist/artplayer-plugin-audio-track.js", + "before": "473f39f1d750f0722cca7e857c12c3c978ebe3c3b2352e325f1248318e508683", + "after": "844662fa5fc7d70dd6fa3a8ba54614626b730c519411166d711ff89f2a301cb9" + }, + { + "file": "packages/artplayer-plugin-audio-track/dist/artplayer-plugin-audio-track.legacy.js", + "before": "d8dc74bce76112d7430c98e3165605e32fde5d3c603be4b496eac58f574576cb", + "after": "fff9eac4fe4b11116401247270d253e3946c19904aa2a5d3968e5e96140b715d" + }, + { + "file": "packages/artplayer-plugin-audio-track/dist/artplayer-plugin-audio-track.mjs", + "before": "4ac599bc5a1a6ecb0cb1942d4269528bfb2cd9d047f988de61fe671a77ad14cd", + "after": "4ac599bc5a1a6ecb0cb1942d4269528bfb2cd9d047f988de61fe671a77ad14cd" + }, + { + "file": "packages/artplayer-plugin-auto-thumbnail/dist/artplayer-plugin-auto-thumbnail.js", + "before": "ce1f1472bf02b836dd85b92eadf2f00786c02cde11a20ac2801136a0f675d12a", + "after": "65f946f98bc2581685d011e467c0c74def72cae32599a832fb380710c471d85b" + }, + { + "file": "packages/artplayer-plugin-auto-thumbnail/dist/artplayer-plugin-auto-thumbnail.legacy.js", + "before": "3561e51e0a5f294b56641bf0884a24a4a9e326582d2e99416a04f9526c7d0cc0", + "after": "e21e897c78ac73164ccdbcc9963fe6a57caf6de6d40350595e93ebd2ac50efa2" + }, + { + "file": "packages/artplayer-plugin-auto-thumbnail/dist/artplayer-plugin-auto-thumbnail.mjs", + "before": "99da4748422bad54663c669d78963589a64c7cb02102e3544439fec7570a7f8b", + "after": "99da4748422bad54663c669d78963589a64c7cb02102e3544439fec7570a7f8b" + }, + { + "file": "packages/artplayer-plugin-chapter/dist/artplayer-plugin-chapter.js", + "before": "c2aaee11bf1db765433b01d9f8dd3978e41fa4d39655db00ebd03fc0be3e35e0", + "after": "c2aaee11bf1db765433b01d9f8dd3978e41fa4d39655db00ebd03fc0be3e35e0" + }, + { + "file": "packages/artplayer-plugin-chapter/dist/artplayer-plugin-chapter.legacy.js", + "before": "c2aaee11bf1db765433b01d9f8dd3978e41fa4d39655db00ebd03fc0be3e35e0", + "after": "c2aaee11bf1db765433b01d9f8dd3978e41fa4d39655db00ebd03fc0be3e35e0" + }, + { + "file": "packages/artplayer-plugin-chapter/dist/artplayer-plugin-chapter.mjs", + "before": "61342a069b84e0731d44a2464d0dfe637d1514d0037c740371fc866c516d2209", + "after": "61342a069b84e0731d44a2464d0dfe637d1514d0037c740371fc866c516d2209" + }, + { + "file": "packages/artplayer-plugin-chromecast/dist/artplayer-plugin-chromecast.js", + "before": "4959661fc7f22eee3d32363bb0392a486c44499ec4a9c608ccfea55de29aaac6", + "after": "76e28ad12ee3c87d25197062182b5a889b3ad4f62c0121e66e9efaa93a879bd4" + }, + { + "file": "packages/artplayer-plugin-chromecast/dist/artplayer-plugin-chromecast.legacy.js", + "before": "e2ba7aebec099884bb806f3caf77f9ff739ff9f236aefdfd0a4e69a8295c9d01", + "after": "e489537ae2f30ba5e037da81cc479211a03e8a4fa035848c38c29668ab813ffe" + }, + { + "file": "packages/artplayer-plugin-chromecast/dist/artplayer-plugin-chromecast.mjs", + "before": "b6d4e445a78f00fae53a35d239e62cc4a1448086d76058d69d8f9634a5754768", + "after": "b6d4e445a78f00fae53a35d239e62cc4a1448086d76058d69d8f9634a5754768" + }, + { + "file": "packages/artplayer-plugin-danmuku/dist/artplayer-plugin-danmuku.js", + "before": "7c8749288711ef97e80b6f9f00981170aa73f62a8ae25d6d663749a4aef62a8f", + "after": "7c8749288711ef97e80b6f9f00981170aa73f62a8ae25d6d663749a4aef62a8f" + }, + { + "file": "packages/artplayer-plugin-danmuku/dist/artplayer-plugin-danmuku.legacy.js", + "before": "62db96685c48b8573f0480cead6ac934b1d0382b9fdd0046d17d15d2549c56dc", + "after": "62db96685c48b8573f0480cead6ac934b1d0382b9fdd0046d17d15d2549c56dc" + }, + { + "file": "packages/artplayer-plugin-danmuku/dist/artplayer-plugin-danmuku.mjs", + "before": "083d7de2f9a0314955dc83cdeedbc46c36728f7c9a0943ada8c3229edf9264ce", + "after": "083d7de2f9a0314955dc83cdeedbc46c36728f7c9a0943ada8c3229edf9264ce" + }, + { + "file": "packages/artplayer-plugin-danmuku-mask/dist/artplayer-plugin-danmuku-mask.js", + "before": "07cebbfbf77f8ec4b2df6a77d7d7300345b86f2993e220ff14e1aae530e4fb31", + "after": "605c19124d062eafbd64ecde5a18b76f39f2a5ea01525a26ac37b919a4005472" + }, + { + "file": "packages/artplayer-plugin-danmuku-mask/dist/artplayer-plugin-danmuku-mask.legacy.js", + "before": "a678a3c0f6d3920bee3b66af9943b3612935028bff61c96677b8eaf8ef7cc8f0", + "after": "7c87e073fce448756e2c52b5580118f163d43ccd03bb46e76e2f3b44f07d5d68" + }, + { + "file": "packages/artplayer-plugin-danmuku-mask/dist/artplayer-plugin-danmuku-mask.mjs", + "before": "48f7a449dc9555ab4060a578902b5431701f81b2f093a7cb0526042a8c417400", + "after": "48f7a449dc9555ab4060a578902b5431701f81b2f093a7cb0526042a8c417400" + }, + { + "file": "packages/artplayer-plugin-dash-control/dist/artplayer-plugin-dash-control.js", + "before": "248aa1162ffb2480965f8e12d766ba6b7d2abfc2d80e65e8a6e8db6bdfa750c5", + "after": "248aa1162ffb2480965f8e12d766ba6b7d2abfc2d80e65e8a6e8db6bdfa750c5" + }, + { + "file": "packages/artplayer-plugin-dash-control/dist/artplayer-plugin-dash-control.legacy.js", + "before": "248aa1162ffb2480965f8e12d766ba6b7d2abfc2d80e65e8a6e8db6bdfa750c5", + "after": "248aa1162ffb2480965f8e12d766ba6b7d2abfc2d80e65e8a6e8db6bdfa750c5" + }, + { + "file": "packages/artplayer-plugin-dash-control/dist/artplayer-plugin-dash-control.mjs", + "before": "2cc039698627a6baaa5da1cc50cc5db5230ae5d31152b1217dda62e38c67aa3e", + "after": "2cc039698627a6baaa5da1cc50cc5db5230ae5d31152b1217dda62e38c67aa3e" + }, + { + "file": "packages/artplayer-plugin-document-pip/dist/artplayer-plugin-document-pip.js", + "before": "261d4ae84579c569ade1b1e7e91d51e3bd00c6efbea8c51a48b8283e0f4f3496", + "after": "d7e58901c4e1c01e66b8b6c7a1553587b860a0f363fe13e9d9ea2d0cbcfcfa40" + }, + { + "file": "packages/artplayer-plugin-document-pip/dist/artplayer-plugin-document-pip.legacy.js", + "before": "8e36b658196dece391add21c249b735f3abfe4082fc968d3dbbbb1ae58a77b31", + "after": "eaf42b32ae4e3c4817fc7bb2c0fafbb851879d6b3298e4c24699c20a5dcdada9" + }, + { + "file": "packages/artplayer-plugin-document-pip/dist/artplayer-plugin-document-pip.mjs", + "before": "22b432b5fc958f6f838b83fdfcc7e5ab7144137111b1f8cf081eb2e21afd8382", + "after": "22b432b5fc958f6f838b83fdfcc7e5ab7144137111b1f8cf081eb2e21afd8382" + }, + { + "file": "packages/artplayer-plugin-hls-control/dist/artplayer-plugin-hls-control.js", + "before": "7a6ce18a0b16f61f6149e0ea77efbb6e0503f997d39afe84271b59e366759e6d", + "after": "7a6ce18a0b16f61f6149e0ea77efbb6e0503f997d39afe84271b59e366759e6d" + }, + { + "file": "packages/artplayer-plugin-hls-control/dist/artplayer-plugin-hls-control.legacy.js", + "before": "893a3de27f9ed02b4b61f03732aec03bfcaf50d02c9beb15cc8a5767ef9121a0", + "after": "893a3de27f9ed02b4b61f03732aec03bfcaf50d02c9beb15cc8a5767ef9121a0" + }, + { + "file": "packages/artplayer-plugin-hls-control/dist/artplayer-plugin-hls-control.mjs", + "before": "85c4de4940e031d835b4de3ccf68af449d10aec4f630c89ecaf62f5503348931", + "after": "85c4de4940e031d835b4de3ccf68af449d10aec4f630c89ecaf62f5503348931" + }, + { + "file": "packages/artplayer-plugin-jassub/dist/artplayer-plugin-jassub.js", + "before": "271d5284e5246fbd04575526510b3e381e5e60d414c030acecce92fcf2e9b10a", + "after": "7e50bf3478ca36d22986f7149e9b237ad970dcd49e0c524ca2d538be223e87d5" + }, + { + "file": "packages/artplayer-plugin-jassub/dist/artplayer-plugin-jassub.legacy.js", + "before": "0726c96bf0df2aab2662ac8973bb7676a0ca7f8e4cb806aa44cb3ec9de296e64", + "after": "3825e3412db4359105a5fae132ace8ee39a592ea26f95f55920ac4c6201941d2" + }, + { + "file": "packages/artplayer-plugin-jassub/dist/artplayer-plugin-jassub.mjs", + "before": "31e4be88838ddd786f1f2180d2662792ac3b2bcce00b7ccac977b59b9d659716", + "after": "31e4be88838ddd786f1f2180d2662792ac3b2bcce00b7ccac977b59b9d659716" + }, + { + "file": "packages/artplayer-plugin-multiple-subtitles/dist/artplayer-plugin-multiple-subtitles.js", + "before": "c1cb0b511da88347165cc855185de9450a44835bb34bb802281ccdb854ef603c", + "after": "742769de0c560cb7f01b40fc1a25b38420409211c85487a98f4053dbf824d89b" + }, + { + "file": "packages/artplayer-plugin-multiple-subtitles/dist/artplayer-plugin-multiple-subtitles.legacy.js", + "before": "0b17644827e5b199afe8293093c44bfda571821014ff5b087c7c918c630dcc18", + "after": "af3de6fa30b074940b12fcfce8ada04af0c0baa9cc0a8d083493c4e47a28ead3" + }, + { + "file": "packages/artplayer-plugin-multiple-subtitles/dist/artplayer-plugin-multiple-subtitles.mjs", + "before": "e867c64303299a53d2bf6af62cc1e59044f1c7355aeb55e45b15ab2703448cb0", + "after": "e867c64303299a53d2bf6af62cc1e59044f1c7355aeb55e45b15ab2703448cb0" + }, + { + "file": "packages/artplayer-plugin-vast/dist/artplayer-plugin-vast.js", + "before": "72842ae0b4a44e44a080f88783c0c9bcd0b572fa0e946d817b2c36d960a08bfe", + "after": "72842ae0b4a44e44a080f88783c0c9bcd0b572fa0e946d817b2c36d960a08bfe" + }, + { + "file": "packages/artplayer-plugin-vast/dist/artplayer-plugin-vast.legacy.js", + "before": "4b88cec8d12844d639cc7703f88703136a4fe586781d9939bf2e3a0c93b42b60", + "after": "4b88cec8d12844d639cc7703f88703136a4fe586781d9939bf2e3a0c93b42b60" + }, + { + "file": "packages/artplayer-plugin-vast/dist/artplayer-plugin-vast.mjs", + "before": "91fbe929a9d8d780f1656b53697fb0fe400b9375db79370866ec2f4c5313e727", + "after": "91fbe929a9d8d780f1656b53697fb0fe400b9375db79370866ec2f4c5313e727" + }, + { + "file": "packages/artplayer-plugin-vtt-thumbnail/dist/artplayer-plugin-vtt-thumbnail.js", + "before": "2d8e22cbd191bdac5a6c711b641590c2f8a90ba1aae185dd0e02a00baf000f8d", + "after": "2d8e22cbd191bdac5a6c711b641590c2f8a90ba1aae185dd0e02a00baf000f8d" + }, + { + "file": "packages/artplayer-plugin-vtt-thumbnail/dist/artplayer-plugin-vtt-thumbnail.legacy.js", + "before": "ecbd0abe12c537687fb058de552c328c71ced71d911e454d0e37887192e9a291", + "after": "ecbd0abe12c537687fb058de552c328c71ced71d911e454d0e37887192e9a291" + }, + { + "file": "packages/artplayer-plugin-vtt-thumbnail/dist/artplayer-plugin-vtt-thumbnail.mjs", + "before": "413af5e4f28d47c659521332042a8347da357b17af39cf08c89ce3347897c618", + "after": "413af5e4f28d47c659521332042a8347da357b17af39cf08c89ce3347897c618" + }, + { + "file": "packages/artplayer-proxy-canvas/dist/artplayer-proxy-canvas.js", + "before": "ef489570e13f402f1a60eb256ce3900a0021a3d05699c2ba15adfaaeff184d4c", + "after": "b93386ae0d8b6a69c2b30a0e479a20a58e037b1068b28cf4a444b37b66495adb" + }, + { + "file": "packages/artplayer-proxy-canvas/dist/artplayer-proxy-canvas.legacy.js", + "before": "7d25f21170c06d067aeaa1c8e79b15f2c0130a27cff1b410532d6c4ef4d2d45f", + "after": "69341bcbd0916273cae0366d5590ac9d8607b22718ed97a84e844585021e1cd9" + }, + { + "file": "packages/artplayer-proxy-canvas/dist/artplayer-proxy-canvas.mjs", + "before": "4bb4a881787770093652a3262fc214a2529a1c514ef49affaf152b8abf0f4dc6", + "after": "4bb4a881787770093652a3262fc214a2529a1c514ef49affaf152b8abf0f4dc6" + }, + { + "file": "packages/artplayer-proxy-mediabunny/dist/artplayer-proxy-mediabunny.js", + "before": "376a0fffbac289e71717795532d6fce9f3531f87cc025dfd7694952d44479037", + "after": "988ed40e19294345d25347ad37681f329c1580ae2f2c1157f6d5a9589fdfbd7c" + }, + { + "file": "packages/artplayer-proxy-mediabunny/dist/artplayer-proxy-mediabunny.legacy.js", + "before": "8a91401e634fa4fe75ea68d6dd17e0793948584a67bcfab8edfe0c574920b0e7", + "after": "5f30c1bdcf5bca8f314b119b3e349254d5aa164d7a7cba9b53b5f6e09cd56fcd" + }, + { + "file": "packages/artplayer-proxy-mediabunny/dist/artplayer-proxy-mediabunny.mjs", + "before": "5b151b82b4054706d3c7c5c610ffd8ce9f39ef56d8393867b524891331fef8c0", + "after": "5b151b82b4054706d3c7c5c610ffd8ce9f39ef56d8393867b524891331fef8c0" + }, + { + "file": "packages/artplayer-tool-iframe/dist/artplayer-tool-iframe.js", + "before": "2ec257c8055a9448d12af3fecf1fae8ba58c06f47e85072d02c386cd258b0c86", + "after": "9419306aadf112689f98db679750afb84fd32cf4e0fbee328bd5bd3126872639" + }, + { + "file": "packages/artplayer-tool-iframe/dist/artplayer-tool-iframe.legacy.js", + "before": "c0755d79f316cda71f045da86749648f95faf47c09180d525fb04bb753576d5f", + "after": "778530d4ccf9f7d9abb5fcf0ae6cbd68044220eab27291b74eca6d5a168c625c" + }, + { + "file": "packages/artplayer-tool-iframe/dist/artplayer-tool-iframe.mjs", + "before": "cbcdb0516dd5f17ca2572a6b3e363ea63b0cfd9733f3e47689c02088f23f29b9", + "after": "cbcdb0516dd5f17ca2572a6b3e363ea63b0cfd9733f3e47689c02088f23f29b9" + }, + { + "file": "packages/artplayer-tool-thumbnail/dist/artplayer-tool-thumbnail.js", + "before": "f77419cc62eeaa45c0413f4b04d169f78178fd539443cbf7b1e5db4ad4c95c98", + "after": "f77419cc62eeaa45c0413f4b04d169f78178fd539443cbf7b1e5db4ad4c95c98" + }, + { + "file": "packages/artplayer-tool-thumbnail/dist/artplayer-tool-thumbnail.legacy.js", + "before": "f77419cc62eeaa45c0413f4b04d169f78178fd539443cbf7b1e5db4ad4c95c98", + "after": "f77419cc62eeaa45c0413f4b04d169f78178fd539443cbf7b1e5db4ad4c95c98" + }, + { + "file": "packages/artplayer-tool-thumbnail/dist/artplayer-tool-thumbnail.mjs", + "before": "4d46df8cccdc92d18efc6f12abedf85e8881737023f4ead28697c8b1e85eead1", + "after": "4d46df8cccdc92d18efc6f12abedf85e8881737023f4ead28697c8b1e85eead1" + } + ], + "i18n": { + "files": 22, + "identicalToCheckout": true + } + }, + "browser": { + "surface": "Codex in-app browser", + "userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/152.0.0.0 Safari/537.36", + "evidenceMethod": "Observed via real UI actions and DOM snapshots; results transcribed into this report", + "amd": { + "repeatedRuns": 3, + "results": [ + { + "name": "artplayer", + "extension": ".js", + "passed": true + }, + { + "name": "artplayer", + "extension": ".legacy.js", + "passed": true + }, + { + "name": "artplayer-plugin-ads", + "extension": ".js", + "passed": true + }, + { + "name": "artplayer-plugin-ads", + "extension": ".legacy.js", + "passed": true + }, + { + "name": "artplayer-plugin-ambilight", + "extension": ".js", + "passed": true + }, + { + "name": "artplayer-plugin-ambilight", + "extension": ".legacy.js", + "passed": true + }, + { + "name": "artplayer-plugin-asr", + "extension": ".js", + "passed": true + }, + { + "name": "artplayer-plugin-asr", + "extension": ".legacy.js", + "passed": true + }, + { + "name": "artplayer-plugin-audio-track", + "extension": ".js", + "passed": true + }, + { + "name": "artplayer-plugin-audio-track", + "extension": ".legacy.js", + "passed": true + }, + { + "name": "artplayer-plugin-auto-thumbnail", + "extension": ".js", + "passed": true + }, + { + "name": "artplayer-plugin-auto-thumbnail", + "extension": ".legacy.js", + "passed": true + }, + { + "name": "artplayer-plugin-chapter", + "extension": ".js", + "passed": true + }, + { + "name": "artplayer-plugin-chapter", + "extension": ".legacy.js", + "passed": true + }, + { + "name": "artplayer-plugin-chromecast", + "extension": ".js", + "passed": true + }, + { + "name": "artplayer-plugin-chromecast", + "extension": ".legacy.js", + "passed": true + }, + { + "name": "artplayer-plugin-danmuku", + "extension": ".js", + "passed": true + }, + { + "name": "artplayer-plugin-danmuku", + "extension": ".legacy.js", + "passed": true + }, + { + "name": "artplayer-plugin-danmuku-mask", + "extension": ".js", + "passed": true + }, + { + "name": "artplayer-plugin-danmuku-mask", + "extension": ".legacy.js", + "passed": true + }, + { + "name": "artplayer-plugin-dash-control", + "extension": ".js", + "passed": true + }, + { + "name": "artplayer-plugin-dash-control", + "extension": ".legacy.js", + "passed": true + }, + { + "name": "artplayer-plugin-document-pip", + "extension": ".js", + "passed": true + }, + { + "name": "artplayer-plugin-document-pip", + "extension": ".legacy.js", + "passed": true + }, + { + "name": "artplayer-plugin-hls-control", + "extension": ".js", + "passed": true + }, + { + "name": "artplayer-plugin-hls-control", + "extension": ".legacy.js", + "passed": true + }, + { + "name": "artplayer-plugin-jassub", + "extension": ".js", + "passed": true + }, + { + "name": "artplayer-plugin-jassub", + "extension": ".legacy.js", + "passed": true + }, + { + "name": "artplayer-plugin-multiple-subtitles", + "extension": ".js", + "passed": true + }, + { + "name": "artplayer-plugin-multiple-subtitles", + "extension": ".legacy.js", + "passed": true + }, + { + "name": "artplayer-plugin-vast", + "extension": ".js", + "passed": true + }, + { + "name": "artplayer-plugin-vast", + "extension": ".legacy.js", + "passed": true + }, + { + "name": "artplayer-plugin-vtt-thumbnail", + "extension": ".js", + "passed": true + }, + { + "name": "artplayer-plugin-vtt-thumbnail", + "extension": ".legacy.js", + "passed": true + }, + { + "name": "artplayer-proxy-canvas", + "extension": ".js", + "passed": true + }, + { + "name": "artplayer-proxy-canvas", + "extension": ".legacy.js", + "passed": true + }, + { + "name": "artplayer-proxy-mediabunny", + "extension": ".js", + "passed": true + }, + { + "name": "artplayer-proxy-mediabunny", + "extension": ".legacy.js", + "passed": true + }, + { + "name": "artplayer-tool-iframe", + "extension": ".js", + "passed": true + }, + { + "name": "artplayer-tool-iframe", + "extension": ".legacy.js", + "passed": true + }, + { + "name": "artplayer-tool-thumbnail", + "extension": ".js", + "passed": true + }, + { + "name": "artplayer-tool-thumbnail", + "extension": ".legacy.js", + "passed": true + } + ] + }, + "worker": { + "passed": true, + "reply": 42, + "css": true, + "svg": true, + "initialValue": 42, + "afterJsDependencyChange": 43, + "afterSyntaxErrorRecovery": 42 + }, + "existingDemo": { + "url": "http://localhost:8082/?libs=./uncompiled/artplayer/index.js&example=index", + "script": "./uncompiled/artplayer/index.js", + "media": "http://localhost:8082/assets/sample/video.mp4", + "width": 640, + "duration": 90.046009, + "observedPlayingTime": 13.678864, + "pausedAfterClick": true, + "monacoLoaded": true, + "existingAdvertisingScriptsPresent": true + }, + "finalErrorsAndWarnings": [] + }, + "cli": { + "interactiveBuild": "Real TTY prompts selection + Enter built artplayer all 3 formats", + "namedDev": "artplayer and TS build probe on 8082 with --no-open", + "invalidInput": "Unknown package, no TTY arguments and ambiguous JS/TS entry rejected before dist cleanup" + }, + "tests": { + "ciCheck": "passed", + "node": 21, + "baseline": 22, + "failed": 0, + "skipped": 0 + }, + "limitations": [ + "No original dist files rewritten in main checkout; candidate artifacts produced in isolated checkout", + "Node VM exploratory import errors in DOM-dependent bundles were resolved by using the real browser, not by declaring VM support", + "Development watch covers selected package src; external files still require restart", + "Production package TypeScript migration remains zero" + ] +} diff --git a/refactor/build-development.md b/refactor/build-development.md new file mode 100644 index 000000000..7fa347926 --- /dev/null +++ b/refactor/build-development.md @@ -0,0 +1,51 @@ +# 已实现的 JS/TS 构建与开发入口 + +ENG-06 保留原来的 Vite 7.3.6、Terser、三种库产物及路径,增加选包参数和 TS 入口;未切换 bundler 或运行时依赖。固定工具链仍为 Node 24.21.0 / Yarn 1.22.22。 + +```sh +yarn build # TTY 中原交互选择 +yarn build artplayer # 一个包,无交互 +yarn build artplayer artplayer-plugin-chapter +yarn build all # 全部 21 库,文档站仍用自身 build +yarn dev artplayer --no-open # 8082,方便内置浏览器/自动化 +yarn dev # 原交互选择并打开浏览器 +yarn build --help +yarn dev --help +``` + +JS 包使用 src/index.js,TS 包使用 src/index.ts;必须恰有一个入口,同时存在时明确报错,避免残留 JS 遮盖新 TS。没有 TTY 且没有包参数时退出失败,不在 CI 中等待选择。未知名称、路径字符串、错误 all 组合在清理产物或启动服务前拒绝。dev 只接受一个包;build 可按提供顺序构建多个包,重复名称去重。 + +## 实现地图 + +| 文件 | 职责与维护点 | +| --- | --- | +| 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 全局参数,不假定压缩变量名 | +| 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 | + +Vite 支持 TS 转译,但不替代类型检查,源码检查继续运行 yarn typecheck。[Vite 7 的 TS 说明](https://v7.vite.dev/guide/features#typescript) 对此有明确区分。此任务没有将生产 JS 改名为 TS,也没有生成新公开声明;ENG-07 与包迁移任务继续负责声明和 tarball 验收。 + +dev 仍监听所选包的 src 目录,包外共享文件/tsconfig/依赖变化需要重启;不宣称已做完整依赖图热更新。错误会打印并保留监听,修复后重建;--no-open 仅禁止自动启动外部浏览器,访问地址不变。实际验证中源码修改和错误恢复后手动刷新页面确认了新结果。 + +## 本次发现的 AMD 构建缺陷 + +BUILD-AMD-01:旧插件在压缩 UMD 中把参数 t 写死为全局对象。但构建夹具的包装为 function(e,t),此时 t 是工厂;旧补丁生成 t.artplayerPluginBuildProbe=t(),AMD 返回虽可用,全局导出却不存在。补丁现在读取外层包装的第一个参数。只有实际 outputOptions.format=umd 时处理,避免将内联 worker 的 ES 输出当作 UMD。 + +不识别的 UMD/AMD 包装现在明确构建失败,避免升级打包工具后悄悄丢失全局接口。新夹具回归要求 AMD 工厂返回与对应全局为同一对象。两轮 21 库/63 产物比较:39 完全相同,24 个 UMD/legacy 只修正上述接收者,逆向替换该位置即可还原旧 SHA-256。全部 42 个 UMD/legacy 在内置浏览器中验证 AMD 工厂一次执行、返回与全局一致。 + +这修复的是构建路径。旧 npm 内容不变,根目录原有 dist 未手工改写;本次在隔离 checkout 中通过正常脚本生成候选,发布阶段必须用新工具重新生成并验证实际要发布的产物。AMD 入口通过不等于广告、模型、Cast、codec 等完整功能已验证。 + +## 重跑浏览器夹具 + +在用于验证的隔离 checkout 正常构建 21 库;复制 refactor/fixtures/build 下内容到临时包的 src,包名 artplayer-plugin-build-probe、版本 1.0.0。该包仅用于测试,不添加到真实 workspace 范围或发布列表。将 browser.html 复制到 docs/build-probes.html,将实际 21 库包名 JSON 数组写到 docs/build-packages.json。 + +运行 yarn dev artplayer-plugin-build-probe --no-open,打开 http://localhost:8082/build-probes.html。两个按钮分别验证真实 worker 往返及 42 个 AMD/global 入口,DOM 中的 JSON 是实际报告;每个 iframe 在加载后移除,worker 在结束后 terminate。Node 测试只能检查 worker 工厂存在,不能用它代替这次真实往返。 + +首页现有示例另用 `http://localhost:8082/?libs=./uncompiled/artplayer/index.js&example=index` 验证 JS 开发构建、Monaco 加载、媒体 metadata、播放和暂停;该首页包含既有广告脚本,本任务没有改变它们。完整编辑器功能、移动与 SDK 用例仍由 SITE/EX/包集成任务负责。 + +本次证据见 [构建与浏览器记录](baselines/build-validation.json) 和 [变更记录](changes/2026-09-10-ENG-06-build-development.md)。测试数量与入口通过不代表生产源码迁移完成。 diff --git a/refactor/changes/2026-09-10-ENG-06-build-development.md b/refactor/changes/2026-09-10-ENG-06-build-development.md new file mode 100644 index 000000000..672f7d172 --- /dev/null +++ b/refactor/changes/2026-09-10-ENG-06-build-development.md @@ -0,0 +1,15 @@ +# ENG-06:JS/TS 构建与非交互开发 + +日期 2026-09-10;起点 77148242;分支 codex/compatible-modernization。 + +build/dev 已共用入口解析与选包,保留交互、输出路径、三格式和目标语法,新增包参数与 dev --no-open。选包/入口错误先失败,dev 重建串行化并可从源码错误恢复。工具职责与完整运行步骤见 [维护说明](../build-development.md)。 + +新增真实构建夹具和三个 Node 测试,覆盖 TS+JS+Less+SVG+inline worker、三格式消费者、错误输入不清理已有产物、合并通知和失败重试。夹具复现旧 AMD 补丁将工厂参数误当全局的问题 BUILD-AMD-01;现按包装实际参数修复,未知格式拒绝构建。现有 API/global/CJS/ESM 路径未变,修正范围仅原先丢失的 AMD 全局赋值。 + +隔离 checkout 使用相同依赖和正常脚本完成改动前后各 21 库/63 产物构建。39 字节相同;24 只改 AMD 接收者,逆变换后的全部哈希回到旧结果。内置浏览器 Chrome/152 UA 验证 42 个实际 UMD/legacy AMD 入口,全部通过;TS 夹具的 Less/SVG、真实 worker 41->42 往返通过;修改 JS 依赖后值由 42->43,故意破坏 TS 后修复可重新构建并恢复 42。 + +现有首页的真实新 JS dev 构建、Monaco 加载、本地 MP4 metadata(640 宽、约 90.046 秒)、播放推进(13.678864 秒)及暂停已验。最终浏览器 error/warn 为空。开始时旧标签失效和 inventory 查询失败,直接新建内置标签后恢复;没有依赖 Chrome 连接。初始 Node VM 全库导入缺少部分 DOM/global API 的 8 个错误不作为包失败或通过,已改在实际浏览器检查全部 42 项。 + +完整 ci:check 通过 lint、类型及 21 Node +22 基线测试,共 43 项;原交互选择在真实 TTY 按 Enter 构建核心成功。最终按多个包参数重建的 63 产物与已验证候选全同;另生成的 22 个 i18n 文件与此前 checkout 字节相同。检查结果和产物指纹保存于 [验证数据](../baselines/build-validation.json)。未更改生产包版本、公开声明或根分发产物;没有把构建验证扩写为完整 SDK/真机或 npm 发布验收。 + +独立提交 ENG-06,可单独撤销工具改动;后续用新工具生成候选,禁止拿本轮验证替代实际发布 tarball。下一项 ENG-03 统一已有公共行为/单元夹具,再接 ENG-05 浏览器自动化和 chapter 的特有行为/源码迁移。 diff --git a/refactor/fixtures/build/browser.html b/refactor/fixtures/build/browser.html new file mode 100644 index 000000000..37eeadc86 --- /dev/null +++ b/refactor/fixtures/build/browser.html @@ -0,0 +1,66 @@ + + + + +Build compatibility probes + + + + +
Ready
+ + + + diff --git a/refactor/fixtures/build/icon.svg b/refactor/fixtures/build/icon.svg new file mode 100644 index 000000000..a544ea2a3 --- /dev/null +++ b/refactor/fixtures/build/icon.svg @@ -0,0 +1 @@ + diff --git a/refactor/fixtures/build/index.ts b/refactor/fixtures/build/index.ts new file mode 100644 index 000000000..661901549 --- /dev/null +++ b/refactor/fixtures/build/index.ts @@ -0,0 +1,18 @@ +import css from './style.less?inline' +import svg from './icon.svg?raw' +import InlineWorker from './worker.ts?worker&inline' +import { add } from './shared.js' + +interface Options { + label?: string +} + +export default function buildProbe(options: Options = {}) { + return { + label: options.label ?? 'default', + value: add(20, 22), + css, + svg, + createWorker: () => new InlineWorker(), + } +} diff --git a/refactor/fixtures/build/shared.js b/refactor/fixtures/build/shared.js new file mode 100644 index 000000000..fe695eb71 --- /dev/null +++ b/refactor/fixtures/build/shared.js @@ -0,0 +1 @@ +export const add = (left, right) => left + right diff --git a/refactor/fixtures/build/style.less b/refactor/fixtures/build/style.less new file mode 100644 index 000000000..320d60c5a --- /dev/null +++ b/refactor/fixtures/build/style.less @@ -0,0 +1,2 @@ +@accent: #123abc; +.build-probe { color: @accent; } diff --git a/refactor/fixtures/build/worker.ts b/refactor/fixtures/build/worker.ts new file mode 100644 index 000000000..501e3cb10 --- /dev/null +++ b/refactor/fixtures/build/worker.ts @@ -0,0 +1,3 @@ +self.onmessage = (event: MessageEvent) => { + self.postMessage(event.data + 1) +} diff --git a/refactor/plan.md b/refactor/plan.md index ad38a68fb..dfbad0b64 100644 --- a/refactor/plan.md +++ b/refactor/plan.md @@ -4,7 +4,7 @@ 基线:`40fcda6a37d0049d42e49c1e64e70d4fd9ba5f7f`。总任务 214 项,范围 22 个包及工作区/示例。 -状态:todo 188 / doing 0 / blocked 0 / done 26 / deferred 0。风险 L/M/H 表示兼容风险,不表示工期。 +状态:todo 187 / doing 0 / blocked 0 / done 27 / deferred 0。风险 L/M/H 表示兼容风险,不表示工期。 前置依赖是启动条件;验收是完成条件。任务可以继续拆分,但不能复用或悄悄删除旧 ID。 @@ -79,7 +79,7 @@ | ENG-03 | workspace
建立公共行为与单元测试入口 | ENG-02, BASE-03 | 保留现有 node:test,测试目录/夹具/统一入口 | 已有 19 项回归保留,旧版与候选可用同一夹具运行 | M | todo | | ENG-04 | workspace
建立类型测试基础 | ENG-02, BASE-05 | 根与分包 tsconfig、显式 TS 依赖、正反例测试 | 核心/试点与迁移模块严格检查,未迁移第三方/包历史问题独立台账;明确最低/当前 TS 和各环境类型;复用 BASE-05 的四种消费模式,不以 skipLibCheck 掩盖 BASE-TYPE-01/03 | M | done | | ENG-05 | workspace
建立真实浏览器测试服务 | ENG-03, BASE-04, BASE-08 | Playwright projects、本地 Range/失败媒体服务;复用 docs 页面/样本的状态隔离、错误采集与候选资源映射 | Chromium/Firefox/WebKit 的基础播放 smoke 和报告可执行;以媒体状态断言判定通过,区分轻量用例与真实编辑器交互 | M | todo | -| ENG-06 | workspace
支持按包非交互与 JS/TS 构建 | ENG-02, BASE-05 | build/dev 入口解析、指定包参数、原交互保留 | 三种产物、Less/SVG/worker 和本地 8082 demo 正常;保持 BASE-05 的 AMD 同时写入全局行为及 i18n/legacy 入口 | H | todo | +| ENG-06 | workspace
支持按包非交互与 JS/TS 构建 | ENG-02, BASE-05 | build/dev 入口解析、指定包参数、原交互保留 | 三种产物、Less/SVG/worker 和本地 8082 demo 正常;保持 BASE-05 的 AMD 同时写入全局行为及 i18n/legacy 入口 | H | done | | ENG-07 | workspace
建立 tarball 消费与产物检查 | ENG-04, ENG-06 | 隔离 npm 消费 fixtures、API/声明/入口差分 | 不借 workspace 源码通过,能识别缺文件与默认导出变化 | H | todo | | ENG-08 | workspace
增加覆盖率、资源与性能报告 | ENG-03, ENG-05, BASE-06 | 覆盖率基线、资源清理断言、性能报告与阈值 | 关键生命周期分支有门槛,报告不靠无意义断言堆数量;将 BASE-06 的同环境多组配对、计时/压缩审查阈值与资源异常分开接入候选,不把历史现象冻结成正常要求 | M | todo | | ENG-09 | workspace
建立全包依赖影响和文档检查 | ENG-07, DOC-04 | 共享核心/构建影响映射、文档及每任务完成提交的 Git 审计接入;契约-支持版本-测试 ID-命令-候选/报告-任务的覆盖索引 | 核心变化触发必需生态检查;原有 DOC-01 至 04 基线例外明确,后续 done 任务不能缺失独立 commit;计划/已执行/缺证据分开,识别缺少验证归属的公开契约 | M | todo | @@ -429,3 +429,4 @@ - ENG-PM-01: [记录](changes/2026-09-10-ENG-PM-01-yarn-toolchain.md) [记录](baselines/yarn-validation.json) - ENG-02: [记录](changes/2026-09-10-ENG-02-readonly-ci.md) [记录](baselines/ci-validation.json) - ENG-04: [记录](changes/2026-09-10-ENG-04-typechecking.md) [记录](typechecking.md) +- ENG-06: [记录](changes/2026-09-10-ENG-06-build-development.md) [记录](build-development.md) [记录](baselines/build-validation.json) diff --git a/refactor/progress.md b/refactor/progress.md index 26e1995ef..67dbc29fc 100644 --- a/refactor/progress.md +++ b/refactor/progress.md @@ -1,5 +1,11 @@ # 进度与证据 +## 当前实施:ENG-06 JS/TS 构建与开发完成 + +正常 build/dev 支持指定包与唯一 JS/TS 入口,dev 可禁止自动打开浏览器并串行重建。构建夹具复现并修复 AMD 全局参数错误;63 产物中 39 字节相同、24 仅该修复;内置浏览器 42 个 AMD 入口、TS worker/资源及首页真实播放通过。见 [ENG-06](changes/2026-09-10-ENG-06-build-development.md)。 + +当前 214 项,27 完成、187 待办。43 项 Node/基线测试通过,生产源码 TS 迁移仍为零;工具链代码已有实际修改。ENG-04 提交 77148242,本任务独立提交。下一项 ENG-03 公共行为/单元入口,再接 ENG-05 浏览器自动化与 chapter 特有测试和源码迁移。 + ## 当前实施:ENG-04 类型检查基础完成 根/核心/chapter 配置和 yarn typecheck 已接入 CI。TS 5.9.3 三种消费模式与 TS 4.3.5 旧解析通过,NodeNext ESM 七项历史诊断单独记录;40 项 Node/基线测试通过。见 [ENG-04](changes/2026-09-10-ENG-04-typechecking.md)。当前 214 项,26 完成、188 待办;工具代码已有实施,生产源码 TS 迁移仍为零。BASE-08 提交 09902b8b,本任务独立提交。下一项 ENG-06 构建 TS 与非交互入口,再完成测试服务和 chapter 源码迁移。 diff --git a/refactor/risk-guide.md b/refactor/risk-guide.md index ff1da5dee..e66890c55 100644 --- a/refactor/risk-guide.md +++ b/refactor/risk-guide.md @@ -4,7 +4,7 @@ BASE-07 将先前基线发现和第三方来源缺口纳入同一份责任台账 ## 如何读取状态 -39 项目前均为 open。reproduced 表示已保存复现结果;source-observed 表示在源码、声明或产物中看到事实;unverified 表示来源、环境或行为仍需取证。证据等级与处理状态互相独立,不能把 39 项全部称为缺陷,也不能因建立了测试基线而自动接受旧缺陷。 +BASE-07 初始登记 39 项,目前仍为 open;ENG-06 新增并修复 BUILD-AMD-01,共 40 项。reproduced 表示已保存复现结果;source-observed 表示在源码、声明或产物中看到事实;unverified 表示来源、环境或行为仍需取证。证据等级与处理状态互相独立,不能把全部事项称为缺陷,也不能因建立了测试基线而自动接受旧缺陷。 其中 15 项来自生命周期、DOM/示例、消费者类型/分发和性能基线;其余包含发布来源、站点分发、素材来源、8 组内嵌资源和 12 个集成边界。每项都列出负责的任务 ID、证据路径、兼容处理路线与关闭条件。具体复现仍以原基线报告为准。 diff --git a/refactor/risk-table.md b/refactor/risk-table.md index b66e7ae73..cd86465f7 100644 --- a/refactor/risk-table.md +++ b/refactor/risk-table.md @@ -43,3 +43,4 @@ | SDK-10 | open / 待取证 | mediabunny 实际集成验证范围 | PKG-MB-01, PKG-MB-09 | | SDK-11 | open / 待取证 | asr-caller-service 实际集成验证范围 | PKG-ASR-01, PKG-ASR-05, EX-03 | | SDK-12 | open / 待取证 | option-validator 实际集成验证范围 | CORE-01, CORE-07 | +| BUILD-AMD-01 | resolved / 已复现 | AMD 全局导出补丁误将固定参数 t 当作全局对象 | ENG-06 | diff --git a/refactor/risks.json b/refactor/risks.json index ce483d540..9964f2868 100644 --- a/refactor/risks.json +++ b/refactor/risks.json @@ -2,7 +2,7 @@ "schemaVersion": 1, "task": "BASE-07", "sourceCommit": "e01f39ff", - "updatedAt": "2026-09-10T12:58:34.267Z", + "updatedAt": "2026-09-10T13:46:01.011Z", "note": "Open means closure evidence is outstanding; source-observed and unverified are not reproduced runtime bugs. A source issue already mitigated in the workspace still requires candidate regression evidence. No accepted-with-scope item is implied by baseline task completion.", "items": [ { @@ -608,6 +608,28 @@ ], "compatibleResolution": "Bundled validation dependency; preserve validation/default/merge behavior and failure cleanup when typing constructor boundaries.", "closureCriteria": "固定 SDK/外部资源版本和环境;成功/失败/切换/销毁有真实证据,设备和网络限制逐项记录。" + }, + { + "id": "BUILD-AMD-01", + "title": "AMD 全局导出补丁误将固定参数 t 当作全局对象", + "confirmation": "reproduced", + "status": "resolved", + "owners": [ + "ENG-06" + ], + "evidence": [ + "scripts/utils.js", + "refactor/scripts/build.test.mjs", + "refactor/baselines/build-validation.json" + ], + "compatibleResolution": "从实际 UMD 包装提取全局参数,保留 AMD 工厂返回和旧 global 名称;不识别的包装失败而非静默发布。", + "closureCriteria": "正常构建的 TS 资源夹具和全部库 UMD/legacy AMD 返回与全局为同一对象;其余输出仅允许已解释变化。", + "resolutionEvidence": [ + "refactor/changes/2026-09-10-ENG-06-build-development.md", + "refactor/baselines/build-validation.json", + "refactor/scripts/build.test.mjs" + ], + "resolutionRationale": "构建逻辑修复已完成;63 产物中 24 仅修复接收者,42 浏览器 AMD 检查通过。未改变旧 npm 内容;正式发布仍须用新工具生成实际候选。" } ] } diff --git a/refactor/scripts/build.test.mjs b/refactor/scripts/build.test.mjs new file mode 100644 index 000000000..9349b55f4 --- /dev/null +++ b/refactor/scripts/build.test.mjs @@ -0,0 +1,108 @@ +import assert from 'node:assert/strict' +import { execFileSync, spawnSync } from 'node:child_process' +import fs from 'node:fs' +import path from 'node:path' +import test from 'node:test' +import { pathToFileURL } from 'node:url' +import vm from 'node:vm' +import { getEntryFile, parseProjects } from '../../scripts/projects.js' +import { createRebuildQueue } from '../../scripts/rebuild.js' +import { refactorDir } from './releases.mjs' + +const root = path.resolve(refactorDir, '..') +test('Package CLI rejects ambiguous requests and never accepts inherited project names', () => { + const projects = { artplayer: '.', 'artplayer-plugin-chapter': '.' } + assert.deepEqual(parseProjects(['all'], projects, 'build').names, Object.keys(projects)) + assert.deepEqual(parseProjects(['artplayer', 'artplayer'], projects, 'build').names, ['artplayer']) + assert.equal(parseProjects(['artplayer', '--no-open'], projects, 'dev').open, false) + for (const args of [['all', 'artplayer'], ['constructor'], ['../artplayer'], ['--unknown']]) + assert.throws(() => parseProjects(args, projects, 'build')) + assert.throws(() => parseProjects(Object.keys(projects), projects, 'dev')) + assert.throws(() => parseProjects(['all'], projects, 'dev')) +}) + +test('Rebuild queue serializes changes and retries after failure', async () => { + let release + let start + let count = 0 + const started = new Promise(resolve => { start = resolve }) + const first = new Promise(resolve => { release = resolve }) + const rebuild = createRebuildQueue(async () => { + count++ + if (count === 1) { + start() + await first + } + }) + const pending = rebuild() + await started + assert.equal(rebuild(), pending) + assert.equal(rebuild(), pending) + assert.equal(count, 1) + release() + await pending + assert.equal(count, 2) + let fail = true + const retry = createRebuildQueue(async () => { + if (fail) throw new Error('invalid source') + }) + await assert.rejects(retry(), /invalid source/) + fail = false + await retry() +}) + +test('Normal build CLI compiles TypeScript, JS, Less, SVG and inline worker into compatible formats', async () => { + const parent = path.join(refactorDir, '.cache') + fs.mkdirSync(parent, { recursive: true }) + const directory = fs.mkdtempSync(path.join(parent, 'build-check-')) + const name = 'artplayer-plugin-build-probe' + const project = path.join(directory, 'packages', name) + const dist = path.join(project, 'dist') + const run = args => spawnSync(process.execPath, [path.join(root, 'scripts/build.js'), ...args], { cwd: directory, encoding: 'utf8', timeout: 60000 }) + try { + fs.mkdirSync(project, { recursive: true }) + fs.cpSync(path.join(refactorDir, 'fixtures/build'), path.join(project, 'src'), { recursive: true }) + fs.writeFileSync(path.join(project, 'package.json'), JSON.stringify({ name, version: '1.0.0' })) + fs.mkdirSync(dist) + fs.writeFileSync(path.join(dist, 'preserve.txt'), 'keep until a valid build starts') + assert.equal(run(['nonexistent']).status, 1) + assert.equal(run([]).status, 1) + fs.writeFileSync(path.join(project, 'src/index.js'), 'export default 1') + assert.throws(() => getEntryFile(project), /exactly one/) + assert.equal(run([name]).status, 1) + assert(fs.existsSync(path.join(dist, 'preserve.txt'))) + fs.unlinkSync(path.join(project, 'src/index.js')) + 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) { + assert.equal(factory().label, 'default') + const result = factory({ label: 'verified' }) + assert.equal(result.label, 'verified') + assert.equal(result.value, 42) + assert.match(result.css, /#123abc/) + assert.match(result.svg, / { exported = factory() }, { amd: true }) } + vm.runInNewContext(code, amd) + assert.equal(exported, amd.artplayerPluginBuildProbe) + check(exported) + } + check((await import(pathToFileURL(path.join(dist, `${name}.mjs`)))).default) + } + finally { + const relative = path.relative(parent, directory) + assert(relative.startsWith('build-check-') && !relative.includes(path.sep)) + fs.rmSync(directory, { recursive: true, force: true }) + } +}) diff --git a/refactor/tasks.json b/refactor/tasks.json index 83717d95d..a58f20c08 100644 --- a/refactor/tasks.json +++ b/refactor/tasks.json @@ -547,11 +547,15 @@ "ENG-02", "BASE-05" ], - "status": "todo", + "status": "done", "risk": "H", "deliverable": "build/dev 入口解析、指定包参数、原交互保留", "acceptance": "三种产物、Less/SVG/worker 和本地 8082 demo 正常;保持 BASE-05 的 AMD 同时写入全局行为及 i18n/legacy 入口", - "evidence": [] + "evidence": [ + "changes/2026-09-10-ENG-06-build-development.md", + "build-development.md", + "baselines/build-validation.json" + ] }, { "id": "ENG-07", diff --git a/scripts/build.js b/scripts/build.js index 850e9173f..6d466385a 100644 --- a/scripts/build.js +++ b/scripts/build.js @@ -2,8 +2,8 @@ import fs from 'node:fs' import path from 'node:path' import process from 'node:process' import cpy from 'cpy' -import prompts from 'prompts' import { build as viteBuild } from 'vite' +import { getEntryFile, selectProjects } from './projects.js' import { getGlobalName, getProjects, getViteBuildConfig } from './utils.js' const projects = getProjects() @@ -28,9 +28,11 @@ async function build(name, targetName, clean = false) { const projectDir = projects[name] const { version } = JSON.parse(fs.readFileSync(path.join(projectDir, 'package.json'), 'utf-8')) const distDir = path.join(projectDir, 'dist') - const entryFile = path.join(projectDir, 'src/index.js') + const entryFile = getEntryFile(projectDir) if (clean && fs.existsSync(distDir)) { + if (fs.realpathSync(distDir) !== path.join(fs.realpathSync(projectDir), 'dist')) + throw new Error(`Refusing to clean redirected dist directory: ${distDir}`) fs.rmSync(distDir, { recursive: true, force: true }) } @@ -79,22 +81,11 @@ async function buildProject(name) { } async function runBuild() { - if (process.argv.includes('all')) { - for (const name of Object.keys(projects)) { - await buildProject(name) - } - console.log('✅ Finished building all packages!') - } - else { - const { value } = await prompts({ - type: 'select', - name: 'value', - message: 'Which project do you want to build?', - choices: Object.keys(projects).map(name => ({ title: name, value: name })), - }) - if (value) - await buildProject(value) - } + const { names } = await selectProjects(projects, 'build') + for (const name of names) + await buildProject(name) + if (names.length) + console.log(`✅ Finished building ${names.length} package(s)!`) } runBuild().catch((error) => { diff --git a/scripts/dev.js b/scripts/dev.js index d2f051d0c..2494f4b93 100644 --- a/scripts/dev.js +++ b/scripts/dev.js @@ -1,17 +1,18 @@ import fs from 'node:fs' import path from 'node:path' -import prompts from 'prompts' +import process from 'node:process' import servor from 'servor' import openBrowser from 'servor/utils/openBrowser.js' import { build as viteBuild } from 'vite' +import { getEntryFile, selectProjects } from './projects.js' +import { createRebuildQueue } from './rebuild.js' import { getGlobalName, getProjects, getViteBuildConfig } from './utils.js' const projects = getProjects() -async function develop(name) { +async function develop(name, open) { const projectPath = projects[name] const uncompiledPath = path.resolve(`docs/uncompiled/${name}`) - const entryFile = path.join(projectPath, 'src/index.js') let browserOpened = false const { url } = await servor({ @@ -27,7 +28,7 @@ async function develop(name) { const startTime = Date.now() try { const config = getViteBuildConfig({ - entry: entryFile, + entry: getEntryFile(projectPath), outDir: uncompiledPath, name: getGlobalName(name), format: 'iife', @@ -40,7 +41,7 @@ async function develop(name) { await viteBuild({ root: projectPath, ...config }) console.log(`[${name}] ✅ Built in ${Date.now() - startTime}ms`) - if (!browserOpened) { + if (open && !browserOpened) { browserOpened = true openBrowser(url) } @@ -50,7 +51,9 @@ async function develop(name) { } } - await buildBundle() + const rebuild = createRebuildQueue(buildBundle) + await rebuild() + console.log(`[${name}] Demo: ${url}/?libs=./uncompiled/${name}/index.js`) const srcPath = path.join(projectPath, 'src') console.log(`[${name}] 👀 Watching ${srcPath}...`) @@ -58,18 +61,16 @@ async function develop(name) { fs.watch(srcPath, { recursive: true }, async (_, filename) => { if (filename) { console.log(`[${name}] 📝 Changed: ${filename}`) - await buildBundle() + await rebuild() } }) } (async () => { - const { value } = await prompts({ - type: 'select', - name: 'value', - message: 'Which project do you want to develop?', - choices: Object.keys(projects).map(name => ({ title: name, value: name })), - }) - if (value) - await develop(value) -})() + const { names, open } = await selectProjects(projects, 'dev') + if (names.length) + await develop(names[0], open) +})().catch((error) => { + console.error('❌ Development server failed:', error) + process.exitCode = 1 +}) diff --git a/scripts/projects.js b/scripts/projects.js new file mode 100644 index 000000000..9cfd2aa3c --- /dev/null +++ b/scripts/projects.js @@ -0,0 +1,65 @@ +import fs from 'node:fs' +import path from 'node:path' +import process from 'node:process' +import prompts from 'prompts' + +export function getProjects(root = process.cwd()) { + const packages = path.join(root, 'packages') + return Object.fromEntries(fs.readdirSync(packages).sort() + .filter(name => name !== 'artplayer-vitepress' && fs.existsSync(path.join(packages, name, 'package.json'))) + .map(name => [name, path.join(packages, name)])) +} + +export function getEntryFile(projectDir) { + const entries = ['index.js', 'index.ts'].map(name => path.join(projectDir, 'src', name)).filter(file => fs.existsSync(file) && fs.statSync(file).isFile()) + if (entries.length !== 1) + throw new Error(`${path.basename(projectDir)} must have exactly one src/index.js or src/index.ts; found ${entries.length}`) + return entries[0] +} + +export function parseProjects(args, projects, command) { + const names = [] + let open = true + for (const arg of args) { + if (arg === '--no-open' && command === 'dev') + open = false + else if (arg === '--help') + return { help: true, names: [], open } + else if (arg !== '--') + names.push(arg) + } + if (names.includes('all')) { + if (command !== 'build' || names.length !== 1) + throw new Error('Use build all on its own; dev accepts one package') + return { names: Object.keys(projects), open } + } + for (const name of names) { + if (!Object.hasOwn(projects, name)) + throw new Error(`Unknown package: ${name}. Use yarn ${command} --help`) + } + if (command === 'dev' && names.length > 1) + throw new Error('dev accepts one package') + return { names: [...new Set(names)], open } +} + +export async function selectProjects(projects, command) { + const selection = parseProjects(process.argv.slice(2), projects, command) + if (selection.help) { + console.log(command === 'build' ? 'Usage: yarn build [all | package ...]' : 'Usage: yarn dev [package] [--no-open]') + return selection + } + if (!selection.names.length) { + if (!process.stdin.isTTY) + throw new Error(`Select a package explicitly in non-interactive mode: yarn ${command} artplayer`) + const { value } = await prompts({ + type: 'select', + name: 'value', + message: `Which project do you want to ${command === 'build' ? 'build' : 'develop'}?`, + choices: Object.keys(projects).map(name => ({ title: name, value: name })), + }) + selection.names = value ? [value] : [] + } + for (const name of selection.names) + getEntryFile(projects[name]) + return selection +} diff --git a/scripts/rebuild.js b/scripts/rebuild.js new file mode 100644 index 000000000..6420ec630 --- /dev/null +++ b/scripts/rebuild.js @@ -0,0 +1,16 @@ +export function createRebuildQueue(build) { + let pending = false + let active + return () => { + pending = true + if (!active) { + active = Promise.resolve().then(async () => { + while (pending) { + pending = false + await build() + } + }).finally(() => { active = undefined }) + } + return active + } +} diff --git a/scripts/utils.js b/scripts/utils.js index d8ff2a6b1..5a4b8582f 100644 --- a/scripts/utils.js +++ b/scripts/utils.js @@ -1,20 +1,4 @@ -import fs from 'node:fs' -import path from 'node:path' -import process from 'node:process' -import { glob } from 'glob' - -export function getProjects() { - return glob - .sync('packages/*') - .sort() - .filter(item => !item.endsWith('artplayer-vitepress')) - .filter(item => fs.existsSync(path.join(item, 'package.json'))) - .reduce((result, item) => { - const name = item.split(/\/|\\/g).pop() - result[name] = path.resolve(process.cwd(), item) - return result - }, {}) -} +export { getProjects } from './projects.js' // Convert kebab-case to PascalCase: artplayer-plugin-ads -> ArtplayerPluginAds export function toPascalCase(name) { @@ -71,7 +55,7 @@ export function getViteBuildConfig(options) { plugins: banner ? [{ name: 'add-banner-and-global', - generateBundle(_, bundle) { + generateBundle(outputOptions, bundle) { for (const chunk of Object.values(bundle)) { if (chunk.type === 'chunk') { // Remove any inline worker banners (inside template literals) @@ -83,14 +67,18 @@ export function getViteBuildConfig(options) { } // For UMD format, modify the wrapper to always expose global // even when AMD loader is present (fix for RequireJS environments) - if (format === 'umd') { - // Match both original and minified UMD patterns: - // Original: typeof define === 'function' && define.amd ? define(factory) : - // Minified: "function"===typeof define&&define.amd?define(e): - // Change to: ... ? (global.Name = factory(), define(function() { return global.Name; })) : + if (outputOptions.format === 'umd') { + const wrapper = code.replace(/^\/\*![\s\S]*?\*\/\s*/, '').match(/^[!(\s]*function\s*\(\s*([\w$]+)\s*,\s*([\w$]+)\s*\)/) + if (!wrapper) + throw new Error(`Unrecognized UMD wrapper for ${name}`) + const global = wrapper[1] + const amd = /["']function["']\s*===?\s*typeof define\s*&&\s*define\.amd\s*\?\s*define\(([\w$]+)\)\s*:/ + if (!amd.test(code)) + throw new Error(`Unrecognized AMD branch for ${name}`) + // Keep AMD and the global export identical regardless of minified parameter names. code = code.replace( - /["']function["']\s*===?\s*typeof define\s*&&\s*define\.amd\s*\?\s*define\((\w+)\)\s*:/, - `"function"==typeof define&&define.amd?(t.${name}=$1(),define(function(){return t.${name}})):`, + amd, + `"function"==typeof define&&define.amd?(${global}.${name}=$1(),define(function(){return ${global}.${name}})):`, ) } chunk.code = code diff --git a/types/assets.d.ts b/types/assets.d.ts index 56851d03e..50691bad3 100644 --- a/types/assets.d.ts +++ b/types/assets.d.ts @@ -7,3 +7,8 @@ declare module '*.svg' { const svg: string export default svg } + +declare module '*.svg?raw' { + const svg: string + export default svg +}