ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

WebToApp 模块市场投稿指南:从 module.json 到审核上线的完整开发实战

WebToApp 模块市场投稿指南:从 module.json 到审核上线的完整开发实战 WebToApp 模块市场投稿指南从 module.json 到审核上线的完整开发实战【免费下载链接】web-to-appThe most full featured web-to-app toolkit on Android, a complete APK workshop that runs entirely on your phone项目地址: https://gitcode.com/GitHub_Trending/web/web-to-app本篇指南以 WebToApp 仓库中modules/目录即 WebToApp 官方模块市场为对象系统讲解市场的工作原理、模块清单manifest与索引registry的字段规则、main.js在 WebView 中的注入合约、本地校验工具以及维护者审核清单。读完本文你将掌握在 WebToApp 模块市场上提交、审查并维护一个 JS/CSS 模块的完整流程并能结合仓库源码理解安装与注入的底层实现。模块市场是什么一个由 Git 仓库直接驱动的应用内市场modules/目录就是WebToApp 的模块市场。App 内置的市场页面中展示的所有模块都是直接从这个文件夹通过raw.githubusercontent.com拉取的并以cdn.jsdelivr.net/gh/作为 CDN 兜底。整个市场没有其他后端——一个 PR 一旦合并到main分支下一刻就会上线不存在发布窗口或人工上线操作。本目录下的 modules/README.md 是模块市场投稿规则的唯一主文档仓库根目录README.md与.github/CONTRIBUTING.md只保留高层摘要真正的字段规则与提交流程均以它为准。目录结构modules/ ├── registry.json ← App 首次下载的索引文件 ├── submissions.json ← CI 生成PR / 贡献者元数据 ├── README.md ← 投稿规则主文档即本文所依据的文档 └── module-path/ ← 每个模块一个目录 ├── module.json ← 模块清单必需 ├── main.js ← 模块源码必需 ├── style.css ← 可选 CSS加载时自动注入 └── icon.png ← 可选图标最大 256 KB也支持 .svg/.webp/.jpg市场的读取与安装链路当用户打开应用内市场时App 会同时拉取registry.json和submissions.json只渲染两边都出现的条目——这是只展示已合并 PR这一承诺的实现机制注册表负责描述有哪些模块提交文件负责证明哪些模块真的合并进了main。点击Install时App 才会下载对应模块的module.json和main.js若hasCss为true则连同style.css并把结果交给本地扩展管理器完成安装。registry.json会被缓存一小时市场页面上的刷新按钮可以绕过缓存强制更新。图标的两套体系registry.json和module.json中都存在icon字段它接受一个 Material Icons 名称字符串如auto_awesome、dark_mode主要用于本地内置模块。若想使用真实的品牌图片需要在注册表层级设置iconUrl相对路径icon.png必须指向模块目录内与main.js同级的icon.png/icon.svg/icon.webp/icon.jpg/icon.jpeg之一且文件小于 256 KB否则会直接导致 CI 校验失败绝对https://URL指向仓库外的图片托管地址两个字段都不设置时App 会用模块名的首字母自动生成一个圆形头像。提交一个模块的完整流程Forkshiaho777/web-to-app仓库。在modules/下新建一个唯一且为 kebab-case的目录例如modules/dark-reader-lite/。这个文件夹名就是registry.json条目中的path。目录内至少包含两个文件module.json— 模块清单字段规则见下文 module.json schemamain.js— 在 WebView 中执行的模块代码。在 modules/registry.json 中追加一条对应记录并保证id、name、version、runAt、permissions在两个文件之间保持一致registry 是列表展示面manifest 才是实际被安装的内容。提交 Pull Request。维护者按 审核 Checklist 审查后合并CI 会自动校验整个modules/目录建议先运行 本地校验 自查。合并后所有客户端在下次刷新默认 1 小时缓存即可看到新模块。整个提交流程没有独立的开发者账号、API Key 或投稿门户——Fork 改代码提 PR 即是全部。module.json schemamodule.json是模块的完整清单App 安装时以它为真实依据。完整的示例{ id: globally-unique-id, name: Display Name, description: Paragraph shown on the install page., icon: material-icon-name, category: OTHER, tags: [tag1, tag2], version: { code: 1, name: 1.0.0, changelog: Initial release }, author: { name: Your Name, url: https://github.com/your-handle, email: optionalexample.com }, runAt: DOCUMENT_END, urlMatches: [ { pattern: *, isRegex: false, exclude: false } ], permissions: [DOM_ACCESS], configItems: [ { key: greeting, name: Greeting text, description: Shown in the floating banner., type: TEXT, defaultValue: Hello, WebToApp!, required: false } ] }注意module.json中的version是一个对象包含code单调递增的整数、namesemver 字符串与changelog而在registry.json中对应的字段只是 semver 字符串——同一个版本号两种形态数值必须保持一致。仓库中 modules/hello-world/module.json 是一个真实的最小示例其configItems声明了greetingTEXT与durationMsNUMBER两个可配置项App 端会据此自动生成设置表单。允许的 category 取值CONTENT_FILTER、CONTENT_ENHANCE、STYLE_MODIFIER、THEME、FUNCTION_ENHANCE、AUTOMATION、NAVIGATION、DATA_EXTRACT、DATA_SAVE、INTERACTION、ACCESSIBILITY、MEDIA、VIDEO、IMAGE、AUDIO、SECURITY、ANTI_TRACKING、SOCIAL、SHOPPING、READING、TRANSLATE、DEVELOPER、OTHER。未知值不会破坏安装而是回落为OTHER——代价仅仅是该模块不会出现在市场首页的分类筛选芯片中。从源码看ExtensionModule.kt 中ModuleCategory枚举为每个分类都映射了 Material 图标与本地化显示名如READING(book)、VIDEO(videocam)App 分类面板即由此驱动。允许的 runAt 取值DOCUMENT_START、DOCUMENT_END、DOCUMENT_IDLE、CONTEXT_MENU、BEFORE_UNLOAD。省略时默认为DOCUMENT_END。源码中ModuleRunTime将每个取值映射到对应的 DOM 事件如DOCUMENT_END→DOMContentLoadedBEFORE_UNLOAD→beforeunload这决定了模块脚本在页面生命周期的哪个时刻被注入执行。允许的 permissions 取值DOM_ACCESS、DOM_OBSERVE、CSS_INJECT、STORAGE、COOKIE、INDEXED_DB、CACHE、NETWORK、WEBSOCKET、FETCH_INTERCEPT、CLIPBOARD、NOTIFICATION、ALERT、KEYBOARD、MOUSE、TOUCH、LOCATION、CAMERA、MICROPHONE、DEVICE_INFO、MEDIA、FULLSCREEN、PICTURE_IN_PICTURE、SCREEN_CAPTURE、DOWNLOAD、FILE_ACCESS、EVAL、IFRAME、WINDOW_OPEN、HISTORY、NAVIGATION。权限列表在安装页面上只起展示与提示作用运行时并不会按它做沙箱隔离——审查者用它来识别危险能力。其中危险项COOKIE、INDEXED_DB、NETWORK、WEBSOCKET、FETCH_INTERCEPT、CLIPBOARD、LOCATION、CAMERA、MICROPHONE、SCREEN_CAPTURE、FILE_ACCESS、EVAL、IFRAME在评审时会受到额外关注。这与 ExtensionModule.kt 中ModulePermission枚举的dangerous标记一一对应。允许的 configItems[].type 取值TEXT、TEXTAREA、NUMBER、BOOLEAN、SELECT、MULTI_SELECT、RADIO、CHECKBOX、COLOR、URL、EMAIL、PASSWORD、REGEX、CSS_SELECTOR、JAVASCRIPT、JSON、RANGE、DATE、TIME、DATETIME、FILE、IMAGE。每个类型在 App 设置页都有对应的输入控件与本地化说明见ConfigItemType枚举及Strings.configType*文案。SELECT类型配合options数组使用——modules/reading-mode/module.json 中的themelight/sepia/dark与fontFamilyserif/sans/mono就是典型用法。urlMatches 匹配模式支持两种模式isRegex: false推荐Chrome 扩展风格的 glob 匹配*匹配任意数量的字符*://...会展开为(https?|ftp|file)://all_urls和*都表示匹配所有 URL。isRegex: trueJava 风格正则每次 URL 匹配有 200ms 超时超时按不匹配处理。exclude: true让该规则从匹配集合中做减法若某模块只写了 exclude 规则则匹配除此之外的所有 URL。App 端的实现位于 ExtensionModule.kt 的matchesUrl/matchRuleglob 会被编译为正则*→.*、*://→ 协议组、其余字符转义编译结果放入有界 LRU 缓存容量 64避免每次页面加载重复编译正则分支则由单线程执行器提交并在 200ms 内取结果超时返回false。从源码结构看这套匹配逻辑同时被 App 端用于判断面板按钮的 Active/Inactive 状态。registry.json 索引条目 schemaregistry.json是 App 先拉取用来渲染列表的索引每个条目镜像模块清单并额外携带path文件夹名与hasCss标志{ id: globally-unique-id, path: folder-name-under-modules, name: Display Name, description: One-line summary, icon: material-icon-name, category: OTHER, tags: [tag1, tag2], version: 1.0.0, minAppVersion: 33, author: { name: Your Name, url: https://github.com/your-handle }, runAt: DOCUMENT_END, permissions: [DOM_ACCESS, CSS_INJECT], urlMatches: [ { pattern: *, isRegex: false, exclude: false } ], hasCss: false, iconUrl: icon.png }minAppVersion允许你发布一个依赖特定新 API 的模块——App 端versionCode低于该值的用户将看不到这条目。当前仓库的versionCode为66v2.6.4只有当你确实依赖更新版本时才设置它。目前注册表中的条目均使用minAppVersion: 33。hasCss与模块目录中是否存在style.css严格对应true时 App 会在安装时一并拉取 CSS。iconUrl可选。相对路径icon.png/icon.svg/icon.webp/icon.jpg/icon.jpeg指向模块目录内文件必须小于 256 KB或绝对https://URL。缺省时显示首字母圆形头像。当前 modules/registry.json 中收录了 8 个模块覆盖OTHER、STYLE_MODIFIER、READING、INTERACTION、NAVIGATION、ACCESSIBILITY、VIDEO等分类runAt在DOCUMENT_START与DOCUMENT_END之间按需选择——例如web-tint视觉滤镜与tv-dpad-cursorTV 遥控光标需要在页面早期介入而使用DOCUMENT_STARTreading-mode、auto-scroll等则等待 DOM 就绪。submissions.json决定谁出现在市场modules/submissions.json 由Module Market Publish工作流.github/workflows/modules-publish.yml在每次向main推送时自动生成每个条目对应一个真正落地到main的模块包含 PR 编号、合并时间、PR 作者的 GitHub 身份以及该模块目录下所有其他提交者的 GitHub 身份contributors列表。App 端市场将原始提交者与这些贡献者以叠加头像展示并聚合出一个贡献者榜单。App 端只展示出现在submissions.json里的模块——这就是只展示已合并 PR的全部机制客户端没有内置白名单也没有可绕过的过滤模块若不在该文件中用户就完全看不到它。目前文件中的条目既包含经由 PR 合并的第三方模块如wta-auto-open-video-playerPR #134也包含维护者直推的官方模块标记direct: true。发布工作流的执行逻辑向main推送且改动触及modules/时遍历所有模块目录。对每个目录用git log --diff-filterA找出引入该目录的提交。调用GET /repos/{owner}/{repo}/commits/{sha}/pulls判断该提交是否属于某个已合并的 PR若是则记录 PR 编号、URL、merged_at与 PR 作者的 GitHub login 头像。否则按直接推送处理记录提交作者解析出的 GitHub login。任何真实作者都会被记录模块只能通过评审或维护者推送进入main只有 bot 身份被排除。对每个模块遍历其完整提交历史用 commit API 解析出每位作者的 GitHub login把除原始提交者及 bot之外的人全部记入contributors。将重新生成的文件提交回main。作为贡献者你无需做任何事——PR 合并几秒后工作流自动跑完下一次有人打开市场就能看到你的模块。main.js 注入合约与运行时环境模块代码在注入前会被包进一个IIFE中执行并提供以下全局变量全局值__MODULE_INFO__{ id, name, icon, version, uiConfig, runMode }__MODULE_CONFIG__用户已保存配置的{ key: value }对象__MODULE_UI_CONFIG__面板 UI 配置与__MODULE_INFO__.uiConfig一致__MODULE_RUN_MODE__INTERACTIVE或AUTOgetConfig(key, defaultValue)读取__MODULE_CONFIG__的便捷访问器代码外层还有一层try/catch未捕获的异常会以模块名作为前缀写入console.error不会中断页面上的其他注入。因此你无需自己再包一层try/catch但必须意识到错误是静默吞掉的——失败的模块不会打扰用户但也可能悄悄失效。若你携带了style.css并在registry.json中把hasCss设为true它会被注入为idext-module-模块id的style标签且先于你的代码执行。一个最小的 hello-world 模块(function () { var greeting getConfig(greeting, Hello!); var banner document.createElement(div); banner.textContent greeting; banner.style.cssText position:fixed;top:24px;left:50%; transform:translateX(-50%);padding:10px 16px;background:#111; color:#fff;border-radius:12px;z-index:2147483647;; document.body.appendChild(banner); setTimeout(function () { banner.remove(); }, 3000); })();仓库中 modules/hello-world/main.js 是完整版它通过getConfig(greeting, ...)与getConfig(durationMs, ...)读取两个可配置项实现了带淡入淡出动画的浮动横幅。更多参考见 modules/reading-mode/main.js。运行时包装的源码级还原App 端这一注入合约的生成逻辑位于 ExtensionModule.kt 的generateExecutableCode()它把configValues、UI 配置、runMode、URL 匹配规则序列化后注入包装代码随后依次声明getConfig函数、注入 CSS、把用户代码放进try/catch最后执行面板自动注册逻辑。也就是说README 中描述的IIFE 全局变量 try/catch并非约定俗成而是由 App 代码强制生成的执行环境。可选注册一个面板按钮如果模块带有交互式 UI可通过__WTA_MODULE_UI__.register注册到浮动面板让用户能从面板呼出它__WTA_MODULE_UI__.register({ id: __MODULE_INFO__.id, name: __MODULE_INFO__.name, icon: __MODULE_INFO__.icon, uiConfig: __MODULE_UI_CONFIG__, runMode: __MODULE_RUN_MODE__, onClick: function () { // open your UI } });如果模块声明了configItems却没有调用register运行时会自动注册一个默认入口至少保证用户能打开模块设置页。reading-mode的 main.js 展示了标准用法将onClick: toggle交给运行时由运行时负责按钮渲染。版本管理与用户配置保留发布更新时必须同时提升module.json中的version.code/version.name与registry.json中的version。客户端会用 semver 与本地已安装版本比较并提供一键升级。升级保留用户配置新清单configItems中仍然存在的 key其值会被保留被移除的 key 会被自动清理。因此重命名 config key 等于重置它——请在version.changelog中记录这类破坏性变更。仓库内reading-mode的版本演进1.0.0 → 1.1.0即为佐证changelog明确记录了改进文章提取广告/侧栏惩罚、阅读时长估算、自定义字号等变更。本地校验仓库自带一个 Python 校验器它模拟 App 安装时的检查并叠加 CI 用来卡 PR 的若干正确性规则python3 .github/scripts/ci/validate_modules.py脚本位于 .github/scripts/ci/validate_modules.py仅使用标准库无需pip install。同一个脚本也会在 GitHub Actions.github/workflows/modules-check.yml中运行——任何改动modules/的 PR 都必须通过 CI 才能被维护者合并。校验器会捕获registry.json或任一module.json的 JSON 解析错误必填字段缺失id、name、versioncategory/runAt/permissions/configItems[].type的非法枚举值registry.json与module.json之间的字段不一致id、name、version、runAt、漏报的权限文件夹名不是 kebab-casemodules 目录与 registry 条目不对齐孤立目录 / 幽灵条目重复的id或path缺少必需文件module.json、main.js多余文件未被iconUrl引用的图标、额外子目录等给出 warninghasCss标志与磁盘上style.css是否存在不匹配iconUrl引用了不存在或超过 256 KB 的图片main.js顶层return在 IIFE 包装内会成为语法错误getConfig(...)调用但configItems未声明对应字段。审核 Checklist以下是维护者在合并前逐项核查的内容投稿者亦可对照自查目录名和id唯一且为 kebab-casemodule.json与registry.json的id/name/version/runAt/permissions/urlMatches一致main.js可读除非 PR 中附源码链接否则不接受混淆/压缩代码没有无条件调用第三方网络端点未在permissions中声明就读取document.cookie或鉴权 tokenurlMatches范围合理侵入性强的模块不应无脑写*代码能在 IIFE 包装下正常运行没有顶层returnrunAt与代码预期一致DOCUMENT_START模块不得假设document.body已存在version.name变更时version.code已随之 1hasCss当且仅当目录中存在style.css时才为true若设置iconUrl文件存在、小于 256 KB、扩展名为png/svg/webp/jpg/jpeg之一没有未被iconUrl引用的图标文件或其他运行时无法触达的多余文件从示例模块到生产模块hello-worldmain.js / module.json最小可运行模板展示getConfig读取 TEXT 与 NUMBER 两类配置项适合作为新模块的起点。reading-modemain.js / module.json在 200 行内演示了完整模块面——多类型configItemsSELECT/NUMBER/BOOLEAN、基于文本密度与标签权重的文章提取启发式、带安全回滚的 DOM 重构、__WTA_MODULE_UI__.register面板注册以及用sessionStorage在 SPA 导航间保持阅读状态。注释明确建议生产环境替换为 Mozilla 的mozilla/readability级提取库。内置模块的对照App 端 BuiltInModules.kt 以代码形式定义了媒体下载器、视频增强、网页分析、页内查找、高级深色模式等 8 个内置模块builtIn true它们与市场模块共享同一套ExtensionModule数据模型与注入管线——理解内置模块的实现有助于写出契合 App 交互范式的市场模块。综上modules/目录本身即是一个以 Git 为后端的微型应用商店registry.json负责发现submissions.json负责可信度module.json负责安装语义main.js/style.css负责执行而 CI 校验器与审核清单共同守住了目录的格式与安全边界。投稿者只需遵循本文的字段规则与流程即可让自己的模块在合并后的下一次刷新进入全球用户的手机。【免费下载链接】web-to-appThe most full featured web-to-app toolkit on Android, a complete APK workshop that runs entirely on your phone项目地址: https://gitcode.com/GitHub_Trending/web/web-to-app创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表