mirror of
https://github.com/zhw2590582/ArtPlayer.git
synced 2026-10-12 06:26:16 -08:00
test(site): [SITE-05] enforce built links and validate online editors
This commit is contained in:
1 parent
6b580d1667
commit
fb78328c43
17 files changed
+2122
-15
No files matched your search
+7
-5
@@ -45,10 +45,10 @@
|
||||
"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,library,pages,site-vendor,browser-validation,release,consumers}/**/*.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/dev-server.test.js test/pages-artifact.test.js test/site-notices.test.js test/monaco-provenance.test.js test/monaco-unicode.test.js test/site-console.test.js test/vconsole-lifecycle.test.js test/browser-validation.test.js test/rollback-files.test.js test/pages-recovery.test.js test/installed-types.test.js test/ecosystem-types.test.js",
|
||||
"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-links.test.js test/site-editor.test.js test/plugin-scaffold.test.js test/dev-server.test.js test/pages-artifact.test.js test/site-notices.test.js test/monaco-provenance.test.js test/monaco-unicode.test.js test/site-console.test.js test/vconsole-lifecycle.test.js test/browser-validation.test.js test/rollback-files.test.js test/pages-recovery.test.js test/installed-types.test.js test/ecosystem-types.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 check:versions && yarn check:release-ledger && yarn typecheck:release && yarn check:rollback-inventory && yarn lint && yarn typecheck:library && yarn typecheck:scaffold && yarn typecheck:docs-tools && yarn check:docs-smoke && yarn check:editor-types && yarn check:llm && yarn check:vconsole && yarn check:console && yarn check:site-notices && 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:vconsole && yarn build:console && yarn build:site-notices && yarn build:test && yarn build:docs && yarn test:imports",
|
||||
"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 check:versions && yarn check:release-ledger && yarn typecheck:release && yarn check:rollback-inventory && yarn lint && yarn typecheck:library && yarn typecheck:scaffold && yarn typecheck:docs-tools && yarn check:docs-smoke && yarn check:editor-types && yarn check:llm && yarn check:vconsole && yarn check:console && yarn check:site-notices && yarn typecheck:site-assets && yarn check:site-assets && yarn check:site-links && 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:vconsole && yarn build:console && yarn build:site-notices && yarn build:test && yarn build:docs && yarn check:site-links && 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",
|
||||
"test:unit": "node --test test/danmuku-setting.test.js test/danmuku-heatmap.test.js test/danmuku-renderer.test.js test/danmuku-scheduler.test.js test/danmuku-worker-client.test.js test/danmuku-input.test.js test/danmuku-parser.test.js test/danmuku-failures.test.js test/danmuku-mask-failures.test.js test/danmuku-mask-lifecycle.test.js test/asr-encoding.test.js test/asr-lifecycle.test.js test/asr-audio.test.js test/asr.test.js test/jassub.test.js test/jassub-registration.test.js test/jassub-runtime.test.js test/jassub-vendor-lifecycle.test.js test/jassub-frame-polyfill.test.js test/jassub-offscreen.test.js test/multiple-subtitles-ass.test.js test/multiple-subtitles-caption.test.js test/multiple-subtitles-merge.test.js test/multiple-subtitles-entities.test.js test/multiple-subtitles-lifecycle.test.js test/multiple-subtitles-failures.test.js test/multiple-subtitles.test.js test/multiple-subtitles-vendor.test.js test/vtt-thumbnail-parser.test.js test/vtt-thumbnail-lifecycle.test.js test/vtt-thumbnail.test.js test/auto-thumbnail-exports.test.js test/auto-thumbnail-canvas.test.js test/auto-thumbnail-frames.test.js test/auto-thumbnail-encoding.test.js test/auto-thumbnail-metadata.test.js test/auto-thumbnail-lifecycle.test.js test/auto-thumbnail-registration.test.js test/auto-thumbnail.test.js test/thumbnail-emitter.test.js test/thumbnail-runtime.test.js test/thumbnail-vendor.test.js test/thumbnail-lifecycle.test.js test/thumbnail-input.test.js test/thumbnail.test.js test/iframe-navigation.test.js test/iframe-boundaries.test.js test/iframe-lifecycle.test.js test/iframe.test.js test/mediabunny.test.js test/mediabunny-shim.test.js test/mediabunny-coordination.test.js test/mediabunny-video.test.js test/mediabunny-audio.test.js test/mediabunny-hls.test.js test/mediabunny-entry.test.js test/mediabunny-capability.test.js test/mediabunny-load.test.js test/mediabunny-input.test.js test/dpip.test.js test/dpip-lifecycle.test.js test/canvas.test.js test/canvas-lifecycle.test.js test/ambilight.test.js test/ambilight-lifecycle.test.js test/ambilight-proxy.test.js test/vast-exports.test.js test/vast.test.js test/vast-lifecycle.test.js test/vast-compatibility.test.js test/ads.test.js test/ads-lifecycle.test.js test/playback.test.js test/dash-control.test.js test/dash-contract.test.js test/dash-lifecycle.test.js test/dash-events.test.js test/hls-control.test.js test/audio-track.test.js test/public-behavior.test.js test/helpers.test.js test/chapter.test.js test/utils.test.js test/resource-scope.test.js test/instance-lifecycle.test.js test/options.test.js test/media-hosts.test.js test/plugins.test.js test/source.test.js test/playback-properties.test.js test/media-events.test.js test/template-resources.test.js test/core-vendor.test.js test/component-resources.test.js test/setting-model.test.js test/setting-layout.test.js test/setting-resources.test.js test/subtitle.test.js test/display-native.test.js test/display-video-fullscreen.test.js test/display-pip.test.js test/display-mini.test.js test/display-sizing.test.js test/display-orientation.test.js test/hotkey.test.js test/listener-registry.test.js test/global-events.test.js test/pointer-events.test.js test/gesture.test.js test/event-scheduling.test.js test/notice.test.js test/fast-forward.test.js test/auto-playback.test.js test/builtin-layers.test.js test/prompt-components.test.js test/screenshot.test.js test/thumbnails.test.js test/progress.test.js test/environment.test.js test/storage.test.js test/facade-properties.test.js test/dom-boundaries.test.js test/initialization.test.js test/entry.test.js test/accessibility-button.test.js test/accessibility-focus.test.js test/accessibility-slider.test.js test/asr-fallback-routing.test.js test/asr-local-example.test.js test/asr-explicit-capture.test.js test/chromecast.test.js test/chromecast-failures.test.js test/chromecast-runtime.test.js test/asr-media-error.test.js test/thumbnail-cleanup-errors.test.js test/dash-cleanup-errors.test.js test/hls-example.test.js test/dash-example.test.js test/thumbnail-policy.test.js",
|
||||
@@ -128,7 +128,7 @@
|
||||
"check:site-assets": "node scripts/build-site-assets.mjs --check",
|
||||
"typecheck:site-assets": "node node_modules/typescript/bin/tsc -p packages/artplayer-vitepress/browser/tsconfig.json --noEmit",
|
||||
"check:llm": "node scripts/build-llm.js --check",
|
||||
"test:site-build": "node --test test/site-build.test.js test/build-docs.test.js test/site-markdown.test.js",
|
||||
"test:site-build": "node --test test/site-build.test.js test/build-docs.test.js test/site-markdown.test.js test/site-links.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",
|
||||
@@ -181,7 +181,8 @@
|
||||
"test:release-bundle": "node --test refactor/scripts/release-bundle.test.mjs refactor/scripts/release-verify.test.mjs",
|
||||
"test:ecosystem-types": "node scripts/consumers/ecosystem.ts",
|
||||
"check:versions": "node refactor/scripts/version-plan.mjs --prepared",
|
||||
"release:verify-bundle": "yarn check:toolchain --strict && node scripts/verify-release.mjs"
|
||||
"release:verify-bundle": "yarn check:toolchain --strict && node scripts/verify-release.mjs",
|
||||
"check:site-links": "node scripts/check-site-links.mjs"
|
||||
},
|
||||
"browserslist": "last 1 Chrome version",
|
||||
"devDependencies": {
|
||||
@@ -205,6 +206,7 @@
|
||||
"eslint": "9.39.2",
|
||||
"eslint-plugin-format": "2.0.1",
|
||||
"glob": "13.0.6",
|
||||
"htmlparser2": "10.1.0",
|
||||
"lerna": "8.2.4",
|
||||
"less": "4.5.1",
|
||||
"linkedom": "0.18.13",
|
||||
|
||||
@@ -75,8 +75,12 @@ Use `yarn test:site-build` for the pinned-component compilation and drift guard,
|
||||
before `yarn test:browser:source document-search.spec.js document-site.spec.js`.
|
||||
Browser checks cover both language URLs, pointer navigation, keyboard selection,
|
||||
Escape, focus, no results, and retained document identity (no form reload).
|
||||
They isolate external scripts and do not validate AdSense, physical devices,
|
||||
all search-index anchors or the complete online editor; those remain SITE-05 work.
|
||||
They isolate external scripts and do not validate AdSense or physical devices.
|
||||
`yarn check:site-links` separately checks every built page's local href/src and
|
||||
every shipped search-index target/anchor. The site-editor, site-loading and
|
||||
editor declaration suites exercise the actual Monaco UI and worker. Remote
|
||||
GitHub edit links for new guides require those source paths to reach `master`;
|
||||
an unpublished local guide is not proof that its external edit link is available.
|
||||
|
||||
Tools have their own navigation group. Iframe explains the required child inject
|
||||
step and serialized commit body, without treating the protocol as a sandbox or
|
||||
|
||||
@@ -21292,6 +21292,10 @@
|
||||
"file": "scripts/site-build/i18n.ts",
|
||||
"sha256Lf": "394b8f19d795980f49cf1557b79b4708f3419689d0fa53be348b83b655d9a984"
|
||||
},
|
||||
{
|
||||
"file": "scripts/site-build/links.ts",
|
||||
"sha256Lf": "004eac68a543ef3f936b3e32bda2d7226b319e82df2ce9180b0c00eb69c576bf"
|
||||
},
|
||||
{
|
||||
"file": "packages/artplayer-vitepress/build/markdown.ts",
|
||||
"sha256Lf": "6d639e25bab97c973940661e2aac6f1034ab53ab1c016310a571ab52392819da"
|
||||
@@ -21356,7 +21360,7 @@
|
||||
"build:i18n": "node ./scripts/build-i18n.js",
|
||||
"build:test": "node ./scripts/build-test.js",
|
||||
"build:all": "yarn ci:build && yarn lint",
|
||||
"ci:build": "yarn build:types && yarn build all && yarn build:i18n && yarn build:ts && yarn build:llm && yarn build:vconsole && yarn build:console && yarn build:site-notices && yarn build:test && yarn build:docs && yarn test:imports",
|
||||
"ci:build": "yarn build:types && yarn build all && yarn build:i18n && yarn build:ts && yarn build:llm && yarn build:vconsole && yarn build:console && yarn build:site-notices && yarn build:test && yarn build:docs && yarn check:site-links && yarn test:imports",
|
||||
"build:types": "node scripts/build-types.mjs --write"
|
||||
},
|
||||
"siteManifest": {
|
||||
|
||||
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,56 @@
|
||||
# SITE-05 全站本地链接与编辑器验证
|
||||
|
||||
起点:`6b580d166747d2a1d460666d4259238beb851f4c`。本批加入持续可运行的链接门槛,
|
||||
并验证原在线编辑器和声明;SITE-05 保持 doing,未进入正式复盘。
|
||||
|
||||
## 实现和依赖
|
||||
|
||||
新增 `scripts/site-build/links.ts` 与薄 CLI `scripts/check-site-links.mjs`。
|
||||
`yarn check:site-links` 检查 docs/document 全部生成 HTML 的 href/src、本地文件、
|
||||
目录 index、中文/转义锚点和历史 name 锚点,并导入实际构建的 search-data 模块
|
||||
检查所有结果 URL;不是重新生成一份索引再声称发布索引正确。索引缺失/重复/结构
|
||||
错误或空白会直接失败。HTML 脚本文本与 inert template 内容不作为可点击链接,
|
||||
不执行页面代码;HTML base 暂不支持时明确拒绝,不静默选错相对地址。外部 URL
|
||||
只列出,CI 不将整个第三方网络可用性作为确定性构建的条件。
|
||||
|
||||
初始 Linkedom TS 版本与 DOM lib 冲突,未加 skipLibCheck 或类型忽略。改用已有
|
||||
传递依赖 htmlparser2 10.1.0 的有类型解析器,并显式声明为根 devDependency。
|
||||
Yarn 只增加精确选择器,原版本/integrity 不变;没有新增传递包或播放器运行依赖。
|
||||
根冻结安装锁不变,严格检查为41个工具/1473个选择器。实现地图和命令写入
|
||||
scripts/site-build/README.md、站点 README、工具链、CI 与浏览器维护文档。
|
||||
|
||||
`ci:check` 增加提交产物检查,`ci:build` 在文档构建之后检查,失败退出非零。
|
||||
三组回归覆盖实体编码、相对/目录路径、缺失资源/锚点、无效URL及异常索引。
|
||||
已接入 test:site-build/test:node;未触发远端 Actions。
|
||||
|
||||
## 本地验证
|
||||
|
||||
Node24.21.0 / Yarn1.22.22:
|
||||
|
||||
- 67个生成页面、5766次本地引用、942条真实构建搜索结果,缺失文件/锚点均为0。
|
||||
报告列出82个外部URL;不是82个已验证外部锚点。
|
||||
- test:site-build 11/11,1334.8651ms;test:ci 80/80,5761.8837ms;均0失败/跳过。
|
||||
- typecheck:docs-tools、修改文件只读ESLint、严格工具链、site inventory和CI配置通过。
|
||||
- check:editor-types校验24份输出及当前/兼容编译器;check:site-assets校验5份产物/66路由。
|
||||
- source浏览器命令包含site-editor、site-loading、editor-declarations、editor-types、
|
||||
document-search及document-site。Playwright按正则匹配,实际还选中Ads/Audio/DASH/
|
||||
HLS/Thumbnail的编辑器类型文件;报告记录全部72项,三引擎各24项,0失败/跳过/重试。
|
||||
覆盖真实Monaco编译/运行、Ctrl-S、重复Run、语法错误保留旧播放器、CSS/脚本顺序、
|
||||
拒绝storage的降级、持久设置、声明加载失败清理、迟到示例不覆盖新Run、语言路由、
|
||||
嵌入代码URL/依赖转发及搜索交互。部分SDK用例有受控替代,不代表真实SDK或设备验收。
|
||||
|
||||
浏览器和静态结果见 [site05-links-editor.json](../baselines/site05-links-editor.json)。
|
||||
媒体库源码未变,不将本批浏览器检查替代安装包、真机或EX-03全部示例矩阵。
|
||||
|
||||
## 外部链接缺口
|
||||
|
||||
首次Node直连81个去掉fragment后的外部地址:2个200、79个连接/超时错误。
|
||||
检测系统代理后,用curl HEAD跟随重定向复测81个,每个一次、4并发、每请求15秒上限:
|
||||
42个200、39个404,无传输失败。HTTP成功只证明本次可达,不验证远端fragment或SDK功能。
|
||||
39个404全部是新增指南的GitHub master编辑源文件链接,本地源码存在,默认分支尚未
|
||||
包含它们。保留准确目标,不跳转到其他页面冒充编辑当前文件,也不擅自推送/合并。
|
||||
SITE-05继续doing,远端源码可用后重验;SITE-06最终交付仍依赖SITE-05和字体来源SITE-07。
|
||||
|
||||
共享输入变化后的21库候选刷新仍由REL-02处理;旧候选证据不代表当前共享指纹。
|
||||
回退本提交移除新检查、devDependency/脚本和记录;播放器API、生成文档URL不变。
|
||||
没有推送、部署、发布或启动用户保留的正式复盘。
|
||||
@@ -1,5 +1,9 @@
|
||||
# PR 检查和 Pages 操作说明
|
||||
|
||||
SITE-05 增加 `yarn check:site-links`:`ci:check` 校验已提交文档,`ci:build` 在重建
|
||||
文档后检查生成 HTML 的 href/src、本地文件/锚点及实际搜索索引。任一缺失使入口失败。
|
||||
外部 URL 只列入报告,在线编辑器和真实播放由浏览器矩阵另验;本地配置不代表远端运行。
|
||||
|
||||
同一20包安装映射的Windows WebKit26.6完整运行现已结束:bb2c12df3执行77文件/845项,
|
||||
694通过、13失败、138能力跳过、0重试,1214.860秒。失败包括8项Audio、1项Chapter、
|
||||
1项候选弹幕CPU间隔漏发和3项旧JASSUB;候选JASSUB三核心通过。见
|
||||
|
||||
@@ -23,7 +23,7 @@ chapter 试点可使用 `http://localhost:8082/?libs=./uncompiled/artplayer-plug
|
||||
2. 每次记录核心、插件、语言包和外部 SDK 的实际 URL、版本及产物内容标识。旧/新组合必须有意指定,不能把陈旧构建或误混版本作为候选验收证据。
|
||||
3. 独立用例使用隔离测试上下文或测试专属 origin,并明确 prod/ts/code/log 状态。不要清除用户日常 Chrome 的全部存储。单独设置连续 Run 用例,检查多实例全部销毁、监听器/DOM/资源清理及重复加载依赖的影响。
|
||||
4. common.js 在移动设备判断成立时转到 mobile.html;报告记录最终 URL,移动页通过不能计作桌面 Monaco 编辑器通过。
|
||||
5. 选中 TS 模式不等于严格类型检查通过;当前编辑器直接 eval 内容,不能假设可以运行任意 TS 语法。类型验收仍由固定版本 tsc 和真实消费者完成。
|
||||
5. TS 模式已由 Monaco worker 先检查语法并生成 JavaScript,再交给执行器;编译失败保留现有播放器。语义诊断不会自动阻止 Run,因此能够运行不等于严格类型检查通过。类型验收继续使用固定版本 tsc、Monaco 语义诊断和真实消费者。
|
||||
6. 优先使用许可明确的本地样本。外部 CDN、广告、流媒体和模型请求逐项登记;网络失败与播放器回归区分。受控测试可隔离无关外部请求并记录,实际 SDK 集成保留独立验收,不能一概屏蔽后声称完整通过。
|
||||
|
||||
## 从演示补成可靠回归
|
||||
|
||||
+1
-1
@@ -688,7 +688,7 @@
|
||||
- SITE-HLS-01: [记录](changes/2026-09-15-SITE-HLS-01-guides.md) [记录](baselines/hls-docs-validation.json)
|
||||
- SITE-DASH-01: [记录](changes/2026-09-15-SITE-DASH-01-guides.md) [记录](baselines/dash-docs-validation.json)
|
||||
- SITE-04: [记录](site-content-review.md) [记录](changes/2026-09-16-SITE-04-chapter-ambilight-guides.md) [记录](baselines/site04-chapter-ambilight-guides.json) [记录](changes/2026-09-16-SITE-04-dpip-asr-guides.md) [记录](baselines/site04-dpip-asr-guides.json) [记录](changes/2026-09-16-SITE-04-auto-multi-guides.md) [记录](baselines/site04-auto-multi-guides.json) [记录](changes/2026-09-16-SITE-04-ads-vast-guides.md) [记录](baselines/site04-ads-vast-guides.json) [记录](changes/2026-09-16-SITE-04-jassub-mask-cast-guides.md) [记录](baselines/site04-jassub-mask-cast-guides.json) [记录](changes/2026-09-16-SITE-04-tools-guides.md) [记录](baselines/site04-tools-guides.json) [记录](changes/2026-09-16-SITE-04-proxies-guides.md) [记录](baselines/site04-proxies-guides.json) [记录](changes/2026-09-16-SITE-04-existing-guides.md) [记录](baselines/site04-existing-guides.json) [记录](core-content-review.json) [记录](baselines/site04-core-services.json) [记录](changes/2026-09-16-SITE-04-core-services.md) [记录](baselines/site04-template-icons.json) [记录](changes/2026-09-16-SITE-04-template-icons.md) [记录](baselines/site04-components.json) [记录](changes/2026-09-16-SITE-04-components.md) [记录](baselines/site04-setting.json) [记录](changes/2026-09-16-SITE-04-setting.md) [记录](baselines/site04-plugin-registry.json) [记录](changes/2026-09-16-SITE-04-plugin-registry.md) [记录](baselines/site04-subtitle.json) [记录](changes/2026-09-16-SITE-04-subtitle.md) [记录](baselines/site04-utils.json) [记录](changes/2026-09-16-SITE-04-utils.md) [记录](baselines/site04-globals.json) [记录](changes/2026-09-16-SITE-04-globals.md) [记录](baselines/site04-events.json) [记录](changes/2026-09-16-SITE-04-events.md) [记录](baselines/site04-options.json) [记录](changes/2026-09-16-SITE-04-options.md) [记录](baselines/site04-css.json) [记录](changes/2026-09-16-SITE-04-css.md) [记录](baselines/site04-lifecycle.json) [记录](changes/2026-09-16-SITE-04-lifecycle.md) [记录](baselines/site04-playback.json) [记录](changes/2026-09-16-SITE-04-playback.md) [记录](baselines/site04-display.json) [记录](changes/2026-09-16-SITE-04-complete.md)
|
||||
- SITE-05: [记录](changes/2026-09-16-SITE-05-search.md) [记录](baselines/site05-search.json)
|
||||
- SITE-05: [记录](changes/2026-09-16-SITE-05-search.md) [记录](baselines/site05-search.json) [记录](changes/2026-09-16-SITE-05-links-editor.md) [记录](baselines/site05-links-editor.json)
|
||||
- SITE-07: [记录](site-inventory.md) [记录](baselines/site-provenance.json) [记录](changes/2026-09-14-SITE-07-vendor-notices.md) [记录](baselines/site-notices-checkpoint.json) [记录](baselines/site-codicons-provenance.json) [记录](baselines/site-codicons-validation.json) [记录](changes/2026-09-15-SITE-07-codicons.md) [记录](baselines/vconsole-notices-provenance.json) [记录](baselines/vconsole-notices-validation.json) [记录](changes/2026-09-15-SITE-07-vconsole-notices.md) [记录](console-modernization.md) [记录](baselines/site-console-inventory.json) [记录](baselines/site-console-validation.json) [记录](changes/2026-09-15-SITE-07-console-baseline.md) [记录](baselines/console-feed-provenance.json) [记录](changes/2026-09-15-SITE-07-console-feed-source.md) [记录](baselines/console-commonjs-provenance.json) [记录](changes/2026-09-15-SITE-07-console-commonjs.md) [记录](baselines/console-esm-provenance.json) [记录](changes/2026-09-15-SITE-07-console-esm.md) [记录](baselines/console-embedded-notices.json) [记录](baselines/console-notices-validation.json) [记录](changes/2026-09-15-SITE-07-console-notices.md) [记录](baselines/console-embedded-sources.json) [记录](baselines/console-embedded-validation.json) [记录](changes/2026-09-15-SITE-07-console-embedded-sources.md) [记录](baselines/console-derived-attribution.json) [记录](baselines/console-derived-validation.json) [记录](changes/2026-09-15-SITE-07-console-derived-attribution.md) [记录](baselines/console-stackoverflow-provenance.json) [记录](baselines/console-stackoverflow-validation.json) [记录](changes/2026-09-15-SITE-07-console-stackoverflow.md) [记录](baselines/console-shallowequal-validation.json) [记录](changes/2026-09-15-SITE-07-console-shallowequal.md) [记录](console-notice-review.md) [记录](baselines/console-notice-review-validation.json) [记录](changes/2026-09-15-SITE-07-console-notice-review.md) [记录](baselines/monaco-typescript-provenance.json) [记录](baselines/monaco-typescript-validation.json) [记录](changes/2026-09-15-SITE-07-monaco-typescript.md) [记录](baselines/monaco-languages-provenance.json) [记录](baselines/monaco-languages-validation.json) [记录](changes/2026-09-15-SITE-07-monaco-languages.md) [记录](baselines/monaco-language-notices.json) [记录](baselines/monaco-language-notices-validation.json) [记录](changes/2026-09-15-SITE-07-monaco-language-notices.md) [记录](baselines/monaco-modes-provenance.json) [记录](baselines/monaco-modes-validation.json) [记录](changes/2026-09-15-SITE-07-monaco-modes.md) [记录](baselines/monaco-basic-provenance.json) [记录](baselines/monaco-basic-validation.json) [记录](changes/2026-09-15-SITE-07-monaco-basic.md) [记录](baselines/monaco-core-origins-provenance.json) [记录](baselines/monaco-core-origins-validation.json) [记录](changes/2026-09-15-SITE-07-monaco-core-origins.md) [记录](baselines/monaco-core-build-provenance.json) [记录](baselines/monaco-core-build-validation.json) [记录](changes/2026-09-15-SITE-07-monaco-core-build.md) [记录](baselines/monaco-contributions-provenance.json) [记录](baselines/monaco-contributions-validation.json) [记录](changes/2026-09-15-SITE-07-monaco-contributions.md) [记录](baselines/monaco-css-build-provenance.json) [记录](baselines/monaco-css-build-validation.json) [记录](changes/2026-09-15-SITE-07-monaco-css.md) [记录](baselines/monaco-node-path-provenance.json) [记录](baselines/monaco-node-path-validation.json) [记录](changes/2026-09-15-SITE-07-monaco-node-path.md) [记录](changes/2026-09-15-SITE-07-monaco-dom.md) [记录](baselines/monaco-dom-origins-validation.json) [记录](changes/2026-09-15-SITE-07-monaco-unicode.md) [记录](baselines/monaco-unicode-provenance.json) [记录](baselines/monaco-unicode-validation.json) [记录](changes/2026-09-15-SITE-07-font-notices.md) [记录](baselines/site-font-notices-provenance.json) [记录](baselines/site-font-notices-validation.json)
|
||||
- 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)
|
||||
|
||||
@@ -1,5 +1,14 @@
|
||||
# 进度与证据
|
||||
|
||||
## SITE-05 本地链接、编辑器与远端缺口
|
||||
|
||||
新增check:site-links并接入CI:67生成页面/5766本地引用/942搜索结果全部有效。
|
||||
真实编辑器与文档三浏览器72项、构建11项、CI回归80项均通过,声明/工具链/类型检查
|
||||
通过,见[记录](changes/2026-09-16-SITE-05-links-editor.md)。外部链接经系统代理HEAD
|
||||
复测81个:42个200、39个404;404全部是新增指南尚未进入GitHub master的编辑源文件。
|
||||
不擅自推送/合并,SITE-05保持doing,225 done /22 doing /38 todo。后续远端源码可用
|
||||
时复验链接;SITE-06还保留字体许可门槛。共享候选输入需REL-02刷新,正式复盘仍等待用户。
|
||||
|
||||
## SITE-05 搜索交互修复
|
||||
|
||||
修复 Enter 默认表单提交造成的整页重载、有内容时 Escape 不关闭、查询变化后选择
|
||||
|
||||
+3
-1
@@ -4624,7 +4624,9 @@
|
||||
"acceptance": "文档构建、链接、嵌入路径与声明注入通过;真实完整 demo 保留 EX-03 独立门槛;核对 ENG-PM-01 登记的搜索 peer 范围和真实搜索行为",
|
||||
"evidence": [
|
||||
"changes/2026-09-16-SITE-05-search.md",
|
||||
"baselines/site05-search.json"
|
||||
"baselines/site05-search.json",
|
||||
"changes/2026-09-16-SITE-05-links-editor.md",
|
||||
"baselines/site05-links-editor.json"
|
||||
]
|
||||
},
|
||||
{
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
|
||||
1. 切换到 .node-version 指定的 Node,安装 Yarn 1.22.22;例如 `npm install --global yarn@1.22.22`,然后确认 `node --version` 和 `yarn --version`。npm 仅可用于安装 Yarn 工具及消费者兼容检查,不用于维护本仓库依赖锁。
|
||||
2. 干净 checkout 执行 `yarn install --frozen-lockfile --non-interactive`,禁止 CI 自动更新锁或忽略安装脚本/engines。参见 [Yarn Classic install](https://classic.yarnpkg.com/en/docs/cli/install/)。
|
||||
3. 执行 `yarn check:toolchain --strict`,核对实际 Node/Yarn、20 个固定开发工具和 22 个 workspace 的声明及传递依赖锁条目。普通检查允许满足最低工具要求的 Node,同时打印标准版本。
|
||||
3. 执行 `yarn check:toolchain --strict`,核对实际 Node/Yarn、根固定开发工具和 22 个 workspace 的声明及传递依赖锁条目。工具数量以本次输出为准;普通检查允许满足最低工具要求的 Node,同时打印标准版本。
|
||||
4. 执行 `yarn test:playback`、`yarn test:dash-control`、`yarn build all` 和 `yarn workspace artplayer-vitepress build`。ENG-02 已拆分只读 lint 与 lint:fix;PR/主线入口和独立 Pages 流程见 ci-setup.md。
|
||||
|
||||
私有根包最低 Node 为 ^20.19.0 || >=22.12.0,与原本使用的 Vite 7 一致。标准工具链验证
|
||||
@@ -19,6 +19,11 @@ CORE-25 默认选项缺陷已修复并通过相同三 Node 与严格打包检查
|
||||
## 锁文件和依赖维护
|
||||
|
||||
- 根构建工具固定精确版本并放在 devDependencies;发布包的 dependencies/peer 范围保持原样。
|
||||
- SITE-05 将已有传递依赖 `htmlparser2@10.1.0` 显式声明为根 devDependency,供
|
||||
`yarn check:site-links` 解析生成 HTML 使用;不进入播放器包。原版本和完整性不变,
|
||||
根 yarn.lock 只合并新增的精确选择器。链接检查不执行 HTML 的脚本,不请求外部 URL。
|
||||
文档站 FlexSearch peer 已对齐 0.7.43,见[搜索验证](changes/2026-09-16-SITE-05-search.md)。
|
||||
Yarn workspace add 后须再从根目录安装并核对 resolutions,防止工作区子命令遗漏根覆盖。
|
||||
- PKG-JASSUB-09 新增根 devDependency `pngjs@7.0.0`,在 Node 中解码 Playwright 截图,
|
||||
用实际合成画面与转移画布 readback 对照;不进入发布包。仅新增其精确 Yarn 锁条目,
|
||||
并验证固定 Node/Yarn 的 frozen 安装及严格工具链检查。上游 API 见
|
||||
@@ -39,7 +44,7 @@ Monaco 浏览器资产匹配的精确 API 类型。只使用 type import,不
|
||||
或加入播放器产物;root yarn.lock 仅增加该版本的一个条目。新增
|
||||
`yarn test:site-editor` 验证编辑器状态/读取失败;浏览器检查仍使用原 assets/js/vs。
|
||||
|
||||
ENG-01 的 [npm 验证报告](baselines/toolchain-validation.json) 是切换前历史证据,不代表 Yarn 已验证。[Yarn 验证报告](baselines/yarn-validation.json) 记录最终干净冻结安装、20 项 Node 测试、21 库包 63 产物及文档站构建全部通过;63 产物 SHA-256 与 ENG-01 完全相同。保留的旧锁条目版本变化为零,9 个 workspace runtime 依赖解析与 npm 基线一致。搜索 peer 警告由 SITE-05 接续验证。
|
||||
ENG-01 的 [npm 验证报告](baselines/toolchain-validation.json) 是切换前历史证据,不代表 Yarn 已验证。[Yarn 验证报告](baselines/yarn-validation.json) 记录最终干净冻结安装、20 项 Node 测试、21 库包 63 产物及文档站构建全部通过;63 产物 SHA-256 与 ENG-01 完全相同。保留的旧锁条目版本变化为零,9 个 workspace runtime 依赖解析与 npm 基线一致。搜索 peer 后续已由 SITE-05 对齐版本并完成三引擎交互验证;未启用的托管提供方警告保留说明。
|
||||
|
||||
构建通过不代表运行时、类型、真实浏览器或 npm 发布验收已完成。Chrome 不可用时按 release-reviews.md 使用内置浏览器并注明实际环境。
|
||||
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
import process from 'node:process'
|
||||
import { checkSiteLinks } from './site-build/links.ts'
|
||||
|
||||
try {
|
||||
const report = await checkSiteLinks(`${process.cwd()}/docs`)
|
||||
console.log(JSON.stringify(report, null, 2))
|
||||
if (report.issues.length)
|
||||
process.exitCode = 1
|
||||
}
|
||||
catch (error) {
|
||||
console.error(error instanceof Error ? error.message : error)
|
||||
process.exitCode = 1
|
||||
}
|
||||
@@ -88,4 +88,23 @@ native radio selection and visible code blocks after clicking the built code-gro
|
||||
labels. SITE-03 still
|
||||
owns desktop editor UI migration, SITE-04 semantic/bilingual documentation and
|
||||
SITE-05 full site/search/link acceptance. No npm/Pages publication or remote CI
|
||||
run follows from a successful local build. No dependencies were added.
|
||||
run follows from a successful local build.
|
||||
|
||||
## Generated documentation links
|
||||
|
||||
`yarn check:site-links` reads every generated HTML page under `docs/document/`
|
||||
and the actual built `virtual_search-data.*.js` module. `links.ts` uses the pinned
|
||||
root development dependency `htmlparser2@10.1.0` to collect href/src references,
|
||||
IDs and legacy named anchors without executing page scripts. It resolves local
|
||||
files, directory indexes, encoded fragments and references back to the site root.
|
||||
Script contents and inert template contents are not treated as rendered links.
|
||||
An HTML base element requires explicit checker support rather than silently
|
||||
using the wrong base. Missing/ambiguous/malformed search data and missing HTML
|
||||
output fail the command. A missing target or anchor also exits nonzero.
|
||||
|
||||
The checker runs in `ci:check` against committed output and after the documentation
|
||||
build in `ci:build`. Its JSON lists external URLs without requesting them. External
|
||||
availability, CSS/JavaScript-discovered URLs, interactive Run Code forwarding,
|
||||
playback and online editor behavior need separate checks. `test/site-links.test.js`
|
||||
exercises valid HTML/entities, missing assets and anchors, invalid URLs and broken
|
||||
search modules; it is included in `test:site-build` and `test:node`.
|
||||
@@ -0,0 +1,156 @@
|
||||
import assert from 'node:assert/strict'
|
||||
import { createHash } from 'node:crypto'
|
||||
import fs from 'node:fs/promises'
|
||||
import path from 'node:path'
|
||||
import { pathToFileURL } from 'node:url'
|
||||
import { Parser } from 'htmlparser2'
|
||||
|
||||
export interface LinkIssue {
|
||||
source: string
|
||||
href: string
|
||||
reason: string
|
||||
}
|
||||
|
||||
async function files(directory: string): Promise<string[]> {
|
||||
const entries = await fs.readdir(directory, { withFileTypes: true })
|
||||
const result: string[] = []
|
||||
for (const entry of entries.sort((a, b) => a.name.localeCompare(b.name))) {
|
||||
assert(!entry.isSymbolicLink(), `Unexpected symlink: ${entry.name}`)
|
||||
const file = path.join(directory, entry.name)
|
||||
if (entry.isDirectory())
|
||||
result.push(...await files(file))
|
||||
else result.push(file)
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
function record(value: unknown): value is Record<string, unknown> {
|
||||
return Boolean(value && typeof value === 'object' && !Array.isArray(value))
|
||||
}
|
||||
|
||||
function readHtml(source: string) {
|
||||
const anchors = new Set<string>()
|
||||
const references: string[] = []
|
||||
let templateDepth = 0
|
||||
new Parser({
|
||||
onopentag(name, attributes) {
|
||||
const inert = templateDepth > 0
|
||||
if (name === 'template')
|
||||
templateDepth++
|
||||
if (inert)
|
||||
return
|
||||
assert(name !== 'base' || !attributes.href, 'An HTML base URL requires explicit link-checker support')
|
||||
if (attributes.id)
|
||||
anchors.add(attributes.id)
|
||||
if (name === 'a' && attributes.name)
|
||||
anchors.add(attributes.name)
|
||||
for (const attribute of ['href', 'src']) {
|
||||
if (attributes[attribute])
|
||||
references.push(attributes[attribute])
|
||||
}
|
||||
},
|
||||
onclosetag(name) {
|
||||
if (name === 'template')
|
||||
templateDepth--
|
||||
},
|
||||
}).end(source)
|
||||
return { anchors, references }
|
||||
}
|
||||
|
||||
// Check the shipped index, not a separately regenerated approximation of its links.
|
||||
export async function readSearchLinks(documentRoot: string) {
|
||||
const chunks = path.join(documentRoot, 'assets/chunks')
|
||||
const matches = (await fs.readdir(chunks)).filter(name => /^virtual_search-data\.[\w-]+\.js$/.test(name))
|
||||
assert.equal(matches.length, 1, 'Expected exactly one built search data module')
|
||||
const name = matches[0]
|
||||
assert(name)
|
||||
const file = path.join(chunks, name)
|
||||
const sha256 = createHash('sha256').update(await fs.readFile(file)).digest('hex')
|
||||
const module: unknown = await import(`${pathToFileURL(file).href}?sha256=${sha256}`)
|
||||
assert(record(module) && record(module.default) && record(module.default.PREVIEW_LOOKUP), 'Invalid built search lookup')
|
||||
const links = Object.entries(module.default.PREVIEW_LOOKUP).map(([id, value]) => {
|
||||
assert(record(value) && typeof value.l === 'string' && value.l.length, `Missing search URL: ${id}`)
|
||||
return { id, href: value.l }
|
||||
})
|
||||
assert(links.length > 0, 'Empty search lookup')
|
||||
return { file, sha256, links }
|
||||
}
|
||||
|
||||
export async function checkSiteLinks(siteRoot: string) {
|
||||
const root = await fs.realpath(siteRoot)
|
||||
const documentRoot = path.join(root, 'document')
|
||||
const htmlFiles = (await files(documentRoot)).filter(file => file.endsWith('.html'))
|
||||
assert(htmlFiles.length > 0, 'No generated documentation pages')
|
||||
const search = await readSearchLinks(documentRoot)
|
||||
const documents = new Map<string, ReturnType<typeof readHtml>>()
|
||||
const external = new Set<string>()
|
||||
const issues: LinkIssue[] = []
|
||||
let localReferences = 0
|
||||
|
||||
async function document(file: string) {
|
||||
let value = documents.get(file)
|
||||
if (!value) {
|
||||
value = readHtml(await fs.readFile(file, 'utf8'))
|
||||
documents.set(file, value)
|
||||
}
|
||||
return value
|
||||
}
|
||||
|
||||
async function check(href: string, source: string, base = source) {
|
||||
try {
|
||||
const url = new URL(href, new URL(base, 'https://artplayer.org'))
|
||||
if (!['http:', 'https:'].includes(url.protocol))
|
||||
return
|
||||
if (url.hostname !== 'artplayer.org') {
|
||||
external.add(url.href)
|
||||
return
|
||||
}
|
||||
localReferences++
|
||||
let file = path.resolve(root, `.${decodeURIComponent(url.pathname)}`)
|
||||
assert(file === root || file.startsWith(`${root}${path.sep}`), 'Target outside site root')
|
||||
let stat
|
||||
try {
|
||||
stat = await fs.stat(file)
|
||||
}
|
||||
catch {
|
||||
throw new Error('Missing local file')
|
||||
}
|
||||
if (stat.isDirectory()) {
|
||||
file = path.join(file, 'index.html')
|
||||
assert((await fs.stat(file)).isFile(), 'Missing directory index')
|
||||
}
|
||||
assert((await fs.realpath(file)).startsWith(`${root}${path.sep}`), 'Target resolves outside site root')
|
||||
if (url.hash && file.endsWith('.html')) {
|
||||
const id = decodeURIComponent(url.hash.slice(1))
|
||||
const target = await document(file)
|
||||
if (id && id.toLowerCase() !== 'top') {
|
||||
assert(target.anchors.has(id), `Missing anchor: ${id}`)
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (error) {
|
||||
issues.push({ source, href, reason: error instanceof Error ? error.message : String(error) })
|
||||
}
|
||||
}
|
||||
|
||||
for (const file of htmlFiles) {
|
||||
const source = `/${path.relative(root, file).replaceAll('\\', '/')}`
|
||||
const html = await document(file)
|
||||
for (const href of html.references)
|
||||
await check(href, source)
|
||||
}
|
||||
const pageIssues = issues.length
|
||||
for (const link of search.links)
|
||||
await check(link.href, `search:${link.id}`, '/document/')
|
||||
|
||||
return {
|
||||
pages: htmlFiles.length,
|
||||
localReferences,
|
||||
searchEntries: search.links.length,
|
||||
searchModule: { path: path.relative(root, search.file).replaceAll('\\', '/'), sha256: search.sha256 },
|
||||
externalUrls: [...external].sort(),
|
||||
pageIssues,
|
||||
searchIssues: issues.length - pageIssues,
|
||||
issues,
|
||||
}
|
||||
}
|
||||
@@ -29,6 +29,7 @@
|
||||
"build-site-notices.mjs",
|
||||
"build-i18n.js",
|
||||
"build-docs.js",
|
||||
"check-site-links.mjs",
|
||||
"../packages/artplayer-vitepress/build/**/*.ts",
|
||||
"build-vconsole.mjs",
|
||||
"browser-validation/**/*.ts",
|
||||
|
||||
@@ -0,0 +1,54 @@
|
||||
import assert from 'node:assert/strict'
|
||||
import fs from 'node:fs/promises'
|
||||
import os from 'node:os'
|
||||
import path from 'node:path'
|
||||
// eslint-disable-next-line test/no-import-node-test -- Exercise the generated site's real filesystem and HTML links.
|
||||
import test from 'node:test'
|
||||
import { checkSiteLinks } from '../scripts/site-build/links.ts'
|
||||
|
||||
async function fixture(t, lookup = { a: { l: 'target.html#中文' } }) {
|
||||
const root = await fs.mkdtemp(path.join(os.tmpdir(), 'artplayer-site-links-'))
|
||||
t.after(async () => {
|
||||
assert.equal(path.dirname(path.resolve(root)), path.resolve(os.tmpdir()))
|
||||
assert(path.basename(root).startsWith('artplayer-site-links-'))
|
||||
await fs.rm(root, { recursive: true, force: true })
|
||||
})
|
||||
await fs.mkdir(path.join(root, 'document/assets/chunks'), { recursive: true })
|
||||
await fs.writeFile(path.join(root, 'package.json'), '{"type":"module"}')
|
||||
await fs.writeFile(path.join(root, 'document/assets/chunks/virtual_search-data.test.js'), `export default ${JSON.stringify({ PREVIEW_LOOKUP: lookup })}`)
|
||||
await fs.writeFile(path.join(root, 'document/index.html'), '<a href="target.html#%E4%B8%AD%E6%96%87">target</a><a href="https://example.com/unchecked">external</a><a href="mailto:test@example.com">email</a>')
|
||||
await fs.writeFile(path.join(root, 'document/target.html'), '<h1 id="中文">Heading</h1><a name="legacy"></a><a href="#legacy">legacy</a><a href="/document/">home</a><img src="/image.svg?v=1"><script src="/script.js"></script><h2 id="a&b">Entity</h2><a href="#a%26b">entity</a><script>const html = \'<a href="/not-a-link">\';</script><template><a href="/inert">inert</a></template>')
|
||||
await fs.writeFile(path.join(root, 'image.svg'), '<svg/>')
|
||||
await fs.writeFile(path.join(root, 'script.js'), '')
|
||||
return root
|
||||
}
|
||||
|
||||
test('built pages and the shipped search index resolve encoded anchors, assets and directory URLs', async (t) => {
|
||||
const report = await checkSiteLinks(await fixture(t))
|
||||
assert.equal(report.pages, 2)
|
||||
assert.equal(report.searchEntries, 1)
|
||||
assert.equal(report.localReferences, 7)
|
||||
assert.deepEqual(report.externalUrls, ['https://example.com/unchecked'])
|
||||
assert.deepEqual(report.issues, [])
|
||||
})
|
||||
|
||||
test('broken assets, ordinary anchors and actual search-index anchors fail independently', async (t) => {
|
||||
const root = await fixture(t, { a: { l: 'target.html#absent' }, b: { l: 'missing.html' } })
|
||||
await fs.appendFile(path.join(root, 'document/index.html'), '<a href="target.html#missing">bad</a><img src="/missing.svg"><a href="/%ZZ">invalid</a>')
|
||||
const report = await checkSiteLinks(root)
|
||||
assert.equal(report.pageIssues, 3)
|
||||
assert.equal(report.searchIssues, 2)
|
||||
assert(report.issues.some(issue => issue.source === 'search:a' && issue.reason.includes('Missing anchor')))
|
||||
assert(report.issues.some(issue => issue.source === 'search:b' && issue.reason === 'Missing local file'))
|
||||
})
|
||||
|
||||
test('missing, ambiguous or malformed search modules cannot silently pass', async (t) => {
|
||||
const root = await fixture(t, { a: {} })
|
||||
await assert.rejects(checkSiteLinks(root), /Missing search URL/)
|
||||
const chunks = path.join(root, 'document/assets/chunks')
|
||||
await fs.copyFile(path.join(chunks, 'virtual_search-data.test.js'), path.join(chunks, 'virtual_search-data.extra.js'))
|
||||
await assert.rejects(checkSiteLinks(root), /exactly one/)
|
||||
await fs.unlink(path.join(chunks, 'virtual_search-data.extra.js'))
|
||||
await fs.unlink(path.join(chunks, 'virtual_search-data.test.js'))
|
||||
await assert.rejects(checkSiteLinks(root), /exactly one/)
|
||||
})
|
||||
@@ -4754,7 +4754,7 @@ html-void-elements@^3.0.0:
|
||||
resolved "https://registry.npmjs.org/html-void-elements/-/html-void-elements-3.0.0.tgz"
|
||||
integrity sha512-bEqo66MRXsUGxWHV5IP0PUiAWwoEjba4VCzg0LjFJBpchPaTfyfCKTG6bc5F8ucKec3q5y6qOdGyYTSBEvhCrg==
|
||||
|
||||
htmlparser2@^10.1.0:
|
||||
htmlparser2@10.1.0, htmlparser2@^10.1.0:
|
||||
version "10.1.0"
|
||||
resolved "https://registry.yarnpkg.com/htmlparser2/-/htmlparser2-10.1.0.tgz#fe3f2e12c73b6e462d4e10395db9c1119e4d6ae4"
|
||||
integrity sha512-VTZkM9GWRAtEpveh7MSF6SjjrpNVNNVJfFup7xTY3UpFtm67foy9HDVXneLtFVt4pMz5kZtgNcvCniNFb1hlEQ==
|
||||
|
||||
Reference in new issue
Block a user