- 前端
- 音视频
- 插件系统
【免费下载链接】clappr
An extensible, plugin-oriented, HTML5-first media player for the web
本文是 Clappr —— 一个可扩展、插件化、以 HTML5 优先的 Web 媒体播放器(开源仓库)—— 的开发者贡献指南(AGENTS.md,同时通过符号链接CLAUDE.md提供给 Claude 类 Agent 使用)的系统化解读。文章以该指南为主体,结合仓库内真实源码与配置展开,帮助你在本地搭建开发环境、理清 monorepo 包结构、掌握构建/测试/lint 命令,并遵守提交与代码评审的工程规范,最终能安全、高效地向该仓库提交高质量的代码变更。
项目定位与总体约束
Clappr 是一个开源的、插件导向、HTML5-first 的 Web 媒体播放器。其核心架构强调"模块化组合播放体验",整体仓库采用Lerna + Yarn workspaces管理的 monorepo 布局,每个 package 都拥有独立的package.json。仓库对性能(流媒体、DOM、bundle 体积)有明确要求,任何改动都必须考虑其对这三者的影响。
AGENTS.md是面向 Agent(如 Claude)与人类开发者共用的"仓库内开发手册";CLAUDE.md是指向该文件的符号链接,二者内容完全一致。它明确了风格与类型检查的唯一权威来源是 ESLint、Prettier、包配置与 CI——指南本身不重复这些工具已强制的内容,而是聚焦于"工具未覆盖"的项目级决策与流程。
Monorepo 目录结构与包职责
顶层划分:apps/与packages/
从仓库根目录(package.json中workspaces: ["apps/*", "packages/*"])看,所有子项目分为两类:
| 目录 | 说明 |
|---|---|
apps/clappr.io/ | 官方文档站点(Docusaurus),权威文档位于docs/下 |
packages/player/ | 主播放器 bundle(@clappr/player),公开入口,嵌入 Web 应用即引入此包 |
packages/clappr-core/ | 核心架构(@clappr/core):Player、Core、Container、Playback 等抽象 |
packages/clappr-plugins/ | 官方插件集合(@clappr/plugins),提供开箱即用的扩展 |
packages/clappr-zepto/ | 轻量 DOM 工具层,服务于 Clappr 内部 UI 渲染(Zepto 的现代化分支) |
packages/hlsjs-playback/ | 基于 hls.js 的 HLS 播放模块(@clappr/hlsjs-playback) |
packages/dash-shaka-playback/ | 基于 Shaka Player 的 MPEG-DASH 播放模块 |
packages/html5-tvs-playback/ | 面向 HbbTV 智能电视的 HTML5 播放模块(@clappr/clappr-html5-tvs-playback),支持 VoD/Live 与 OIPF DRM |
test/ | 共享的 dist 产物 smoke 辅助(如 ES5 子类化契约,见 issue #2540) |
packages/clappr-telemetry/亦在仓库中提供遥测辅助能力。包与包之间通过源码级别名互相引用(见下文vite.config.base.mjs的clapprSiblingSourceAlias),因此不要期望单个包能脱离 monorepo 独立安装运行——monorepo 是唯一入口。
代码地图(Code map)
指南给出了核心代码的关键位置,便于按主题定位实现:
- Player:
packages/clappr-core/src/components/player/ - Core:
packages/clappr-core/src/components/core/ - Container:
packages/clappr-core/src/components/container/ - Playback 基类:
packages/clappr-core/src/base/playback/ - MediaControl:
packages/clappr-plugins/src/plugins/media_control/
插件类型体系包括CorePlugin、UICorePlugin、ContainerPlugin、UIContainerPlugin、Playback、MediaControl六类,详细说明见 架构文档。
依赖管理与共享配置
依赖放置规则
核心规则:被 2 个及以上包共同使用的依赖,必须放在根devDependencies;仅单个包使用的依赖留在该包自身。根package.json的devDependencies中可见vite、vitest、eslint、lerna、prettier、browserslist、rollup-plugin-visualizer、terser等共享工具,正是这一规则的体现。新增共享工具时放在根目录并移除各包的副本,优先于使用 Yarnresolutions。
共享配置文件一览
| 配置 | 路径 | 作用 |
|---|---|---|
| Babel | babel.base.json | 构建预设,modules: false;Vite 工厂将其应用到发布的dist/产物。仓库中内容为{"presets": [["@babel/preset-env", { "modules": false }]]} |
| Vite | vite.config.base.mjs | 共享的 library-mode 工厂;每个包再以各自的vite.config.mjs调用它 |
| Vitest | vitest.config.base.mjs | 共享测试配置(jsdom环境、globals、v8 覆盖率);每包有自己的vitest.config.mjs |
| Browserslist | .browserslistrc | monorepo 的 ES5 底线:> 0.5%/last 2 versions/not ie <= 11 |
| ESLint | eslint.config.js | flat config;eslint与@eslint/js声明在根 |
| Knip | knip.json | 所有 workspace 共用一份根配置,只能从仓库根运行(yarn knip) |
依赖规则的源码佐证:共享 Vite 工厂
vite.config.base.mjs导出defineClapprLib(pkgSpec)工厂,各包配置(如 packages/clappr-core/vite.config.mjs)只声明name、entry、fileName(umd/es/min 三档)、alias、cssLoadPaths、exports等描述性字段。工厂内部完成以下关键工作:
clapprSiblingSourceAlias():把@clappr/core、@clappr/zepto、@clappr/plugins、@clappr/hlsjs-playback解析到兄弟包的src/main.js源码,实现包间源码级互引;babelEs5Output():在generateBundle阶段(Rolldown 会重印对象简写,故 ES5 转换必须在最后一个可见最终代码的钩子执行)用babel.base.json对产物做 ES5 转换;cssTargetFromBrowserslistrc():读取.browserslistrc并把 browserslist 查询映射为 esbuild 的 CSS target;- 当环境变量
ANALYZE_BUNDLE=true时注入rollup-plugin-visualizer,输出dist/bundle-stats.html(含 gzip 体积); umdAmdDefine():修正 Vite UMD 产物define([])与 AMD 的差异,保证 smoke 测试可运行;clapprAssetStrings():将相对引入的.html/.svg转为?raw、.scss转为?inline字符串资源。
对应的根级测试 test/define-clappr-lib.test.js 验证:当entry指向不存在的文件时,defineClapprLib返回的配置函数会抛出missing entry错误且不写入dist/。
ES5 底线为何如此重要(#2540)
.browserslistrc中的注释揭示了这一约束的深层原因:发布的dist/产物会被 ES5 编写的第三方插件继承(subclass),因此至少需要保留一个不支持class的目标(bb 7/10与ie_mob 10/11是促使 preset-env 降级class的唯一目标),切勿添加not dead。test/dist-contract.js提供三个契约守卫:
expectEs5Syntax:用 acorn 解析产物,扫描class、arrow、const/let、模板字符串、简写属性、默认参数、rest 参数等 ES5+ 语法;expectEs5Subclassable:用Base.call(this)+Object.create(Base.prototype)的经典 ES5 方式子类化产物并实例化,确认不抛错;expectSourcemapFromSrc:断言 sourcemap 的sources指向src/下的真实源文件。
这些 smoke 守卫在yarn build:dist之后由test/dist.smoke.test.js系列与 CI 执行,防止 ES5 底线漂移。
工具链与常用命令
包管理器与发布
| 命令 | 作用 |
|---|---|
yarn install | 安装依赖 |
yarn add <package> -W | 添加根依赖 |
yarn workspace <package-name> add <dependency> | 添加包级依赖 |
lerna run <command> | 对所有包执行命令 |
lerna run <command> --scope=<package-name> | 对单个包执行命令 |
yarn release | 版本发布(lerna version);npm publish 由 Release workflow 通过 OIDC 完成 |
根lerna.json配置印证了发布策略:npmClient: "yarn"、version: "independent"(各包独立版本)、command.version.allowBranch: "main"、conventionalCommits: true、提交信息模板chore: publish。
运行项目(本地开发)
- 播放器开发:
yarn dev→ 启动@clappr/player,访问 http://localhost:8080 - 核心包:
lerna run start --scope=@clappr/core - 插件包:
lerna run start --scope=@clappr/plugins - 文档站点:
yarn workspace clappr-docs start
根package.json中"dev": "lerna run start --scope=@clappr/player"与指南一致。
构建、lint、测试
| 命令 | 作用 |
|---|---|
yarn build | 构建播放器及其依赖(开发者/贡献者路径):lerna run build --scope=@clappr/player --include-dependencies |
yarn build:dist | 运行每个包的release脚本(最小化产物,CI 亦会验证):lerna run release,匹配 7 个可发布包的prepublishOnly |
yarn lint/yarn lint:fix | ESLint(lerna run lint && eslint test/) |
yarn knip | 检测未使用的文件、导出与依赖(基于根knip.json,必须从仓库根运行) |
yarn format/yarn format:check | Prettier(**/*.{js,ts,json,md}) |
yarn test | 对定义了test脚本的包执行lerna run test --no-bail,随后根级vitest run --dir test --passWithNoTests --globals |
yarn test:smoke | dist 产物 smoke 测试(hlsjs-playback、dash-shaka-playback、clappr-zepto);在yarn build:dist之后运行(CI 同样如此)。本地流程:yarn build:dist && yarn test:smoke |
按包 / 按文件运行测试的示例:
# 单包 lerna run test --scope=@clappr/plugins lerna run test --scope=@clappr/hlsjs-playback # 单文件(从仓库根) lerna run test --scope=@clappr/core -- path/to/test.test.js # 从包根目录直接使用 vitest vitest run src/path/to/test.test.js vitest run --testNamePattern "pattern" vitest run --watch vitest run --coverage环境要求
本地开发要求Node.js ≥ 24(根package.json的engines.node与.nvmrc中的24均锁定该大版本)。使用 nvm 时,先在项目根执行nvm install再nvm use,之后才能运行任何 yarn 命令——Yarn 1 在 engine 检查失败时会中止所有命令。
文档导航与按需阅读
指南强调:默认不读文档,仅在任务涉及对应主题时打开。这对 Agent 尤为关键——避免为每个任务都加载全部文档,按需取用即可:
| 主题 | 路径 |
|---|---|
| 发布/发版 | .github/RELEASING.md(发布包清单、sourcemap 策略、Release workflow、发布说明) |
| 架构 | apps/clappr.io/docs/architecture.md |
| 快速上手 | apps/clappr.io/docs/getting_started.md |
| Player API | apps/clappr.io/docs/api.md |
| 插件开发 | apps/clappr.io/docs/guides/how_to_build_plugins.md |
| 事件 | apps/clappr.io/docs/guides/events.md |
| 支持的格式 | apps/clappr.io/docs/supported_formats.md |
| FAQ | apps/clappr.io/docs/faq.md |
| HLS / DASH / 智能电视播放 | packages/hlsjs-playback/README.md、packages/dash-shaka-playback/README.md、packages/html5-tvs-playback/README.md |
判断边界:NEVER / ASK / ALWAYS
NEVER(绝对禁止)
- 提交密钥、令牌或
.env文件 - 使用
eval()或Function构造函数 - 将令牌存入
localStorage(优先 httpOnly cookie 或内存) - 对不可信用户输入使用
innerHTML - 在控制台、错误信息或 URL 中记录或暴露敏感数据
ASK(动手前先询问)
- 新增依赖之前(考虑 bundle 体积、维护成本、替代方案)
- 对共享包(
@clappr/core、@clappr/plugins、@clappr/player)进行大型或高风险改动之前
ALWAYS(始终遵守)
- Conventional Commits 规范:
<type>(<scope>): <description>,描述用英文 - 优先
async/await而非.then();并行任务使用Promise.all() - 清理资源:定时器、监听器、observer、连接、媒体元素、Blob URL
- 在代码库适配处优先组合(composition)而非继承
- 测试行为而非实现;测试需独立,使用
afterEach/afterAll做清理 - 提交(staging/commit)时读取并执行
.agents/skills/commit/SKILL.md(分支检查、conventional 格式、英文信息)
Skills:提交与代码评审的标准流程
仓库在.agents/skills/下固化了两项 Agent 技能(每个技能目录含SKILL.md):
.agents/skills/code-review/SKILL.md—— 完整的代码评审:定义了严重级别(severities)与输出模板。进行 PR 评审、代码评审或任何针对 diff 的结构化合并前反馈时,先读取并执行它.agents/skills/commit/SKILL.md—— 暂存与提交:使用 conventional commits
关键使用方式:当任务匹配某个 skill 时,直接读取其SKILL.md并按步骤执行,不要通过任何工具间接调用;这应在回答问题或运行 git 之前完成(包括"快速"的 PR 评审)。若多个 skill 同时适用,按合理顺序执行(例如先 code-review 再 commit)。
编码约定速查
命名:布尔值用is*/has*/can*前缀;方法用动词;类用名词;私有成员用_前缀;常量用UPPER_SNAKE_CASE。
架构:播放器组件优先使用类(classes);单一职责;方法控制在约 30 行以内;提前返回;偏好组合。
导入:使用 ES6 imports;包内优先使用相对路径。
DOM 与性能:批量读写 DOM;缓存引用;事件委托;resize 用 debounce/throttle;触摸/滚动监听用 passive;使用requestAnimationFrame;动画优先transform/opacity。
安全:按上下文(HTML、JS、URL)净化用户内容;合并不可信对象时校验键;状态变更请求需 CSRF 防护;校验postMessage的origin;使用具体的targetOrigin(绝不使用"*");重定向前校验 URL。
TypeScript(使用场景):避免any,使用unknown;接口描述形状;使用类型守卫;类型定义贴近使用处。
注释:解释"为什么",而非"是什么"。
给贡献者的实操清单
- 环境准备:
nvm install && nvm use(Node ≥ 24),再yarn install; - 本地开发:
yarn dev启动播放器,lerna run start --scope=...启动指定包,yarn workspace clappr-docs start启动文档站; - 提交前跑完校验:
yarn lint、yarn format、yarn test;涉及 dist 时先yarn build:dist再yarn test:smoke;顺带yarn knip检查冗余; - 依赖归属:跨 2+ 包共用的依赖放根
devDependencies,单包使用的留在包内; - 遵循判断边界:不提交密钥、不用
eval、不向localStorage存令牌、净化不可信innerHTML; - 提交与评审:先读
.agents/skills/commit/SKILL.md与.agents/skills/code-review/SKILL.md,按 conventional commits 格式提交,评审时按 skill 定义的模板输出。
延伸阅读
- 快速上手:安装
@clappr/player与基础集成示例 - 架构概览:Player、Core、Container 与插件如何交互
- 插件开发指南:创建并注册自定义插件
- Player API 参考:Player 方法与属性完整参考
- 事件指南:播放器事件体系
- 支持格式:各格式支持情况
- README.md:项目总览、获取与本地开发说明
- 前端
- 音视频
- 插件系统
【免费下载链接】clappr
An extensible, plugin-oriented, HTML5-first media player for the web
相关推荐
Clappr 仓库工程指南:Lerna + Yarn 工作区架构、ES5 构建底线与开发规范
Clappr 仓库工程指南:Lerna + Yarn 工作区架构、ES5 构建底线与开发规范 本文以 Clappr 开源仓库根目录的 CLAUDE.md 工程指
前端音视频插件系统Budibase 源码仓库开发全指南:Lerna Monorepo 架构、测试规范与本地开发环境搭建
Budibase 源码仓库开发全指南:Lerna Monorepo 架构、测试规范与本地开发环境搭建 本篇技术指南以 Budibase 仓库根目录的 CLAUD
低代码人工智能AI Agent工作流自动化后端前端Cua 开源仓库开发指南:跨语言 Monorepo 的组件地图、工具链与协作规范
Cua 开源仓库开发指南:跨语言 Monorepo 的组件地图、工具链与协作规范 Cua(Computer Use Agent)是一个覆盖 Rust、Pytho
人工智能AI Agent大模型GUI 自动化MCP 服务模型评测微调强化学习工具调用
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考