16 KiB
Contributing to WebToApp
Thanks for taking the time to help. WebToApp moves fastest when contributions stay small and well-scoped — pick one of the lanes below and ignore the rest.
English · 简体中文
This guide targets WebToApp 2.1.8 (versionCode 47).
Lanes
| You want to… | Go to | Effort |
|---|---|---|
| Publish a JS/CSS module to the in-app Module Market | modules/README.md |
hours |
| File a bug, request a feature, or ask a question | GitHub Issues | minutes |
| Fix a bug or build a feature in the Android client | This guide ↓ | days |
If you're not sure which lane fits, open an issue or a discussion first. There is no need to write code before there's agreement on the shape of the change.
Module Market submissions
The fastest way to ship something useful to every WebToApp user is to publish
a module. The canonical Module Market guide lives in
modules/README.md. Use that file for the full
schema, field rules, reviewer checklist, and CI validation details. The short
version here is only meant to help you pick the right contribution lane:
- Fork the repo.
- Add
modules/<your-module>/module.jsonandmain.js(plusstyle.cssif you need CSS). - Add an entry to
modules/registry.json. - Open a PR.
The market has no backend. Clients read registry.json and
submissions.json, and only merged modules show up in the catalog. Module
changes are validated in CI by .github/scripts/ci/validate_modules.py:
python3 .github/scripts/ci/validate_modules.py
Code contributions
Before you write code
- Search issues for prior discussion of the same idea.
- For non-trivial changes, open an issue first describing the problem and the approach you have in mind. This is much cheaper than rewriting after review.
- Avoid adding new dependencies. The list in
app/build.gradle.ktsis intentionally restrained — the project signs and packages APKs in-process, and the host pinstargetSdk = 28on purpose because generated apps rely on fork+exec native runtimes. New dependencies need a strong justification.
Local setup
You'll need:
- Android Studio Hedgehog or newer
- JDK 17
- The Gradle wrapper pins Gradle 9.4.1 — no system Gradle install required
git clone https://github.com/shiaho777/web-to-app.git
cd web-to-app
./gradlew assembleDebug
The repo has three Gradle modules: app (the full builder and host),
shell (the runtime-only host embedded in generated APKs), and
clone-host (the host code used by app cloning). Shared runtime code is
authored in app and synchronized into shell, so changes there usually need
both :app and :shell to compile.
Run the checks before submitting:
./gradlew :app:compileDebugKotlin
./gradlew :shell:compileDebugKotlin
./gradlew :app:testDebugUnitTest
Native code (
node_launcher,go_exec_loader, the APK optimizer) builds via CMake per-ABI and needs the Android NDK + CMake installed through the SDK Manager. CI installscmake;3.22.1andndk;28.2.13676358.
Where things live
| Area | Path |
|---|---|
App types & central config (AppType, WebApp) |
app/src/main/java/com/webtoapp/data/model/ |
| On-device APK builder / signer | app/src/main/java/com/webtoapp/core/apkbuilder/ |
| Server runtimes (Node / PHP / Python / Go / WordPress) | app/src/main/java/com/webtoapp/core/{nodejs,php,python,golang,wordpress}/ |
| WebView engine, native bridge, fingerprint disguise | app/src/main/java/com/webtoapp/core/{webview,engine,appearance}/ |
| Extension modules, Module Market, AI Coding | app/src/main/java/com/webtoapp/core/{extension,market,aicoding}/ |
| Compose UI screens & design system | app/src/main/java/com/webtoapp/ui/ |
| DI graph (source of truth) | app/src/main/java/com/webtoapp/di/AppModule.kt |
Coding conventions
WebToApp leans on the Kotlin and Jetpack Compose patterns already in the codebase. A few rules worth calling out:
- This codebase ships without comments. Source files are kept
comment-free by convention — let clear names and small functions carry the
intent. Don't add
//,/* */, or KDoc blocks; if a block needs a comment to be understood, prefer rewriting it. (Strings inside code, e.g. URLs, are obviously fine.) - Build new UI on the Wta design system. Everything renders through
com.webtoapp.ui.design. The olderPremium*/Enhanced*/Settings*components are retained as permanent alias layers over the Wta internals — don't add new ones, and you don't need to rip them out. A build-time audit (.github/scripts/audit_ui_design_system.py, wired intobuild.gradle.kts) tracks legacy UI debt against.github/scripts/ui_design_allowlist.txt. - Reuse the design tokens in
ui/design/WtaTokens.ktfor spacing, radius, alpha, and elevation. Don't hard-code numbers. - Strings must cover all 10 supported languages. UI copy lives in
core/i18n/Strings.kt(facadeobject Strings+ in-fileStringsA…StringsE, split only for the JVM constant pool). Supported: Chinese, English, Arabic, Portuguese, Spanish, French, German, Russian, Japanese, Korean. Every new or changed user-facingwhen (Strings.lang)block must have real translations for all 10 branches — do not leave pt/es/fr/de/ru/ja/ko as English placeholders. Brand names and pure format tokens may match English. If you truly cannot translate one language, use English there and flag it in the PR.StringsKtTranslationParityTestcurrently enforces the core three and forbids hardcodedelse -> "..."; full 10-way coverage is still required by project convention. - No new top-level singletons unless you discuss it first. The DI graph in
di/AppModule.ktis the source of truth. - The Native Bridge is capability-gated. Any method exposed to web content
via
@JavascriptInterfacemust be guarded by the per-capability allow-list (NativeBridgeCapabilities). Never expose a native capability to arbitrary pages without a gate. PRs that bypass this will be rejected. - Preview and packaged runtime are two paths. Bridges and WebView hooks must
be registered (and injected, when applicable) both for in-app preview
(
WebViewManager/WebViewActivity) and for generated APKs (ShellActivity). Shared runtime sources are authored only underapp/and synced intoshell/via./gradlew :shell:syncShellRuntimeSources --rerun-tasks— do not hand-edit generatedshell/trees. - Avoid catching
Exceptionto silence errors. If recovery is impossible, log viaAppLoggerand re-throw or return a failedResult.
Security
Anything touching the WebView, file IO, APK signing, or the native bridge has downstream impact on every generated app, so it gets extra scrutiny. Specifically, PRs will be declined if they:
- store or expose credentials/secrets to web content,
- widen the native surface exposed to arbitrary pages without a capability gate,
- weaken APK signing, isolation, or the fingerprint-disguise defaults,
- bundle unrelated changes inside a large diff.
Commit messages
We don't enforce Conventional Commits, but a clear subject line and a wrapped body that explains why the change exists make reviews much faster. Example:
ModuleMarket: cache registry.json for an hour
Repeatedly hitting raw.githubusercontent.com on every screen open is wasteful
and triggers GitHub's anonymous rate limit on slow connections. Cache the
parsed registry under cache/module_market/ and treat anything fresher than
an hour as authoritative; the refresh button always bypasses the cache.
Pull requests
- Branch from
main. Keep PRs focused — one logical change per PR. - Describe the user-visible effect in the PR body, not just the code change.
- If your PR touches the build system, native code, or APK packaging, attach
the output of
./gradlew :app:assembleDebug(or note the failure if it fails on your machine). - CI runs on every PR. A green CI is required before merge.
Reviewer expectations
Maintainer reviews look for:
- Correctness. Does the change do what the PR description says?
- Safety. See Security above.
- Scope discipline. Drive-by refactors expand the review surface. Land them in a separate PR.
- Style fit. See Coding conventions above.
Code of conduct
Feedback is required to be precise. "This code is garbage" is acceptable only when accompanied by which code, why, and what would be less so. The maintainer (shiaho) has on file a standing waiver covering criticism of himself, his judgment, and his life choices. Praise unaccompanied by a working patch is logged and otherwise disregarded.
贡献 WebToApp(中文)
非常感谢你愿意花时间。WebToApp 的迭代速度取决于"小而聚焦"的贡献——下面三条路 里挑一条走,其他的先忽略。
本指南对应 WebToApp 2.1.8(versionCode 47)。
你想做什么?
| 你想…… | 路径 | 投入 |
|---|---|---|
| 给应用内的 模块市场 提交一个 JS/CSS 模块 | modules/README.md |
几小时 |
| 报 Bug、提 Feature、问问题 | GitHub Issues | 几分钟 |
| 修 Bug 或在 Android 客户端里做新功能 | 见下方代码贡献小节 | 几天 |
不确定走哪条,先开 issue 或 discussion——动手前对齐方向,比写完再返工便宜 得多。
模块市场贡献
让你的工作触达每一个用户最快的方式就是提一个模块。Schema、审核 Checklist 和
CI 校验细节的主文档是 modules/README.md。这里仅保
留一个极简入口,方便你先判断自己是不是走这条贡献路线:
- Fork 本仓库
- 新建
modules/<你的模块>/module.json和main.js(需要 CSS 时再加style.css) - 在
modules/registry.json里加一行索引 - 提 PR
市场没有后端。客户端读取 registry.json 和 submissions.json,只有已合
并的模块才会在市场出现。模块改动会经过 CI 的
.github/scripts/ci/validate_modules.py 校验,提 PR 前建议先本地跑:
python3 .github/scripts/ci/validate_modules.py
代码贡献
动手前
- 在 issues 里先搜一下 类似讨论
- 较大的改动请先开 issue 说明要解决的问题和方案
- 谨慎引入新依赖。
app/build.gradle.kts的依赖列表刻意保持精简——本项目 全程在设备内签名打包 APK,并特意把targetSdk锁在 28,因为生成应用依赖fork、exec原生运行时。新依赖需要充分理由。
本地环境
- Android Studio Hedgehog 或更新版本
- JDK 17
- Gradle wrapper 已锁定 Gradle 9.4.1,无需系统安装 Gradle
git clone https://github.com/shiaho777/web-to-app.git
cd web-to-app
./gradlew assembleDebug
仓库有三个 Gradle 模块:app(完整构建器和宿主)、shell(嵌入生
成 APK 的运行时宿主)和 clone-host(应用克隆使用的宿主代码)。共享运
行时代码以 app 为唯一事实来源,再同步到 shell,所以改动这部分通常至少
要保证 :app 和 :shell 都能编译。
提交前请跑通:
./gradlew :app:compileDebugKotlin
./gradlew :shell:compileDebugKotlin
./gradlew :app:testDebugUnitTest
原生代码(
node_launcher、go_exec_loader、APK 优化器)按 ABI 经 CMake 编译,需要通过 SDK Manager 安装 Android NDK + CMake。CI 安装的是cmake;3.22.1与ndk;28.2.13676358。
代码大致位置
| 区域 | 路径 |
|---|---|
应用类型与核心配置(AppType、WebApp) |
app/src/main/java/com/webtoapp/data/model/ |
| 设备端 APK 打包 / 签名 | app/src/main/java/com/webtoapp/core/apkbuilder/ |
| 服务端运行时(Node / PHP / Python / Go / WordPress) | app/src/main/java/com/webtoapp/core/{nodejs,php,python,golang,wordpress}/ |
| WebView 引擎、原生桥、指纹伪装 | app/src/main/java/com/webtoapp/core/{webview,engine,appearance}/ |
| 扩展模块、模块市场、AI 编程 | app/src/main/java/com/webtoapp/core/{extension,market,aicoding}/ |
| Compose UI 与设计系统 | app/src/main/java/com/webtoapp/ui/ |
| DI 依赖图(单一事实来源) | app/src/main/java/com/webtoapp/di/AppModule.kt |
代码风格
- 本仓库不写注释。 源码按约定保持无注释——用清晰的命名和小函数表达意图。
不要加
//、/* */或 KDoc;如果某段代码非得靠注释才能看懂,优先重写它。 (代码里字符串内的内容,比如 URL,当然不受此限。) - 新 UI 一律构建在 Wta 设计系统之上——所有界面通过
com.webtoapp.ui.design渲染。旧的Premium*/Enhanced*/Settings*组件作为 Wta 内部实现的 永久别名层保留——不要再新增,也无需强行替换。构建期有一个审计脚本 (.github/scripts/audit_ui_design_system.py,接进build.gradle.kts)按.github/scripts/ui_design_allowlist.txt跟踪历史 UI 债务。 - 复用
ui/design/WtaTokens.kt里的设计 token(间距、圆角、透明度、高度), 别硬编码数字 - 字符串必须覆盖全部 10 种已支持语言:文案在
core/i18n/Strings.kt(facadeobject Strings+ 同文件StringsA…StringsE,拆分只为常量池)。 已支持:中 / 英 / 阿 / 葡 / 西 / 法 / 德 / 俄 / 日 / 韩。 新增或修改面向用户的when (Strings.lang)必须为 10 个分支写真实翻译, 禁止把 pt/es/fr/de/ru/ja/ko 继续当英文占位。品牌名、纯格式符可与英文相同。 某语实在不会翻可暂填英文并在 PR 标明。StringsKtTranslationParityTest目前强制三核心 + 禁止硬编码else -> "..."; 项目约定仍要求 10 语全覆盖 - 引入新的全局单例前请先讨论;
di/AppModule.kt是单一事实来源 - 原生桥是按能力门禁的:任何通过
@JavascriptInterface暴露给网页的方法, 都必须经过逐能力白名单(NativeBridgeCapabilities)。绝不要在没有门禁的 情况下把原生能力暴露给任意页面。绕过门禁的 PR 会被拒 - 预览和打包运行时是两条路径:桥接类 / WebView hook 要在预览
(
WebViewManager/WebViewActivity)和生成 APK(ShellActivity)两侧都 注册(需要 hook 网页 API 的还要注入脚本)。共享运行时代码只改app/, 改完跑./gradlew :shell:syncShellRuntimeSources --rerun-tasks;不要手改shell/下的同步产物 - 不要 catch 然后吞掉异常;用
AppLogger记录后重新抛出或返回失败的Result
安全
动到 WebView、文件 IO、APK 签名或原生桥的改动会影响每一个生成出来的 应用,因此审核更严。出现以下情况的 PR 会被拒:
- 把凭据 / 密钥存储或暴露给网页内容
- 在没有能力门禁的情况下扩大暴露给任意页面的原生面
- 削弱 APK 签名、隔离或指纹伪装的默认强度
- 在一个大 diff 里夹带不相关的改动
Commit 消息
不强制 Conventional Commits,但请把"为什么"写进正文。示例见英文段。
Pull Request
- 从
main分出分支,每个 PR 只解决一件事 - PR 描述写"用户看得到的效果",不只是代码 diff
- 改动涉及构建系统、原生代码或 APK 打包时,附上
./gradlew :app:assembleDebug的结果 - CI 必须绿色才会合并
Review 标准
- 正确性——是否真的做了 PR 描述里写的事
- 安全——见上面《安全》小节
- 聚焦——顺手做的重构请单独发 PR
- 风格——参考上面那条
行为准则
反馈须精准。"这代码是坨屎"仅在同时说明"哪段代码、为何如此、怎样才不至于" 时方予受理。维护者(shiaho)已就针对其本人、其判断力及其人生选择的批评 出具长期豁免一份并存档。未附可用补丁的赞美,予以记录,余不受理。