Files
wehub-resource-sync 2114b14ee0
Sync main into demo / sync (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 12:35:26 +08:00

10 KiB

Build and Tooling

This page documents the platform-side build pipeline, local dev server plugins, generated artifacts, and validation commands.

MobileGym is a single Vite project, not a monorepo. Runtime code lives under os/, apps/, and system/; benchmark tooling lives under bench_env/.

Package scripts

Command Current behavior
npm run dev Starts Vite on port 3000, host 0.0.0.0.
npm run build Runs vite build.
npm run preview Runs vite preview.
npm run test Runs vitest run.
npm run test:watch Runs vitest.
npm run lint Runs ESLint on os/ and apps/, then scripts/lint_store_getters.mjs.
npm run build:web-css Builds web/tailwind.css with the Tailwind CLI.
npm run watch:web-css Watches the standalone web CSS entry.

TypeScript

tsconfig.json uses:

Setting Value
target ES2022
module ESNext
module resolution bundler
JSX react-jsx
strict true
path alias @/* maps to the repository root

The main TS project includes index.tsx, os/**/*.ts(x), apps/**/*.ts(x), and system/**/*.ts(x). It excludes generated/output-heavy directories such as dist, runs, bench_env, ui_dumps, and extracted asset folders.

For large runtime changes, run:

npx tsc --noEmit

Small doc/style/data changes do not require a full type check.

ESLint

eslint.config.js applies to runtime code in:

  • os/**/*.{ts,tsx}
  • apps/**/*.{ts,tsx}
  • system/**/*.{ts,tsx}

Rules currently enforced:

Rule Why
No bare Date.now() Runtime code must use TimeService.now() or TimeService.realNow() depending on simulated vs wall-clock time.
No new Date(...) Runtime code must use TimeService.getDate(), fromTimestamp(), fromLocalParts(), or parsing helpers.
React Hooks rules Standard hook correctness.
React Hooks exhaustive deps Warning-level dependency checks.

os/TimeService.ts is exempt from the Date restrictions because it implements the abstraction.

scripts/lint_store_getters.mjs catches Zustand patterns that do not re-render correctly, especially store actions used as query getters and subscriptions to stable function references.

Vite config

The project uses vite.config.ts with React and several project-specific plugins.

Server settings

Setting Value
port 3000
host 0.0.0.0
allowedHosts true
ignored watch path **/runs/**

Resolve settings

Setting Value
alias @ → repository root
dedupe react, react-dom, lucide-react
optimizeDeps exclude @sqlite.org/sqlite-wasm

@sqlite.org/sqlite-wasm is excluded so its own new URL("sqlite3.wasm", import.meta.url) path resolves beside the package assets instead of Vite's optimized dependency cache.

Vite plugins

Plugin Dev Build/preview Purpose
tailwindCliPlugin Builds index.css, then starts a Tailwind CLI watcher. Builds index.css during buildStart. Uses Tailwind CSS v4 CLI instead of the JS plugin.
react() yes yes React transform and Fast Refresh.
accessLogPlugin yes preview Logs method, URL, status, and timing.
serveAppAssetsPlugin yes build Serves or emits app-owned assets under /@app-assets/<AppName>/....
serveCdnPlugin yes preview Maps /cdn/ to repo-local mobilegym-data/.
listPublicFilesPlugin yes no Provides /api/list-public-files for local viewers.
fileSystemPlugin yes build Provides /api/sdcard in dev and emits sdcard/manifest.json in builds.
runsExplorerPlugin yes no Provides /api/runs for local run exploration.
apiGatewayPlugin yes preview Provides /api/gw/<service>/... same-origin gateway endpoints.

App assets

App-owned binary assets should live under:

  • apps/<AppName>/assets/
  • system/<AppName>/assets/

They are available through stable URLs:

/@app-assets/<AppName>/<path>

WMR bundles owned by an app may live under:

apps/<AppName>/wmr/
system/<AppName>/wmr/

and are served as:

/@app-assets/<AppName>/wmr/<path>

In dev, the plugin serves files from disk. In production builds, it emits them into dist/@app-assets/.

Prefer Vite imports for ordinary component assets when possible. Use the stable URL path when a subsystem needs a URL string, such as WMR XML/image resources.

CDN data

serveCdnPlugin maps:

/cdn/<path>

to:

mobilegym-data/<path>

The helper in os/utils/cdn.ts should be used when runtime code needs URLs for this local/production-compatible data root.

Virtual SD card

fileSystemPlugin scans public/sdcard.

Endpoint/artifact Behavior
GET /api/sdcard Dev-only JSON listing of files and directories. Cached until watched files change.
dist/sdcard/manifest.json Build artifact emitted from the same scan logic.

The scanner intentionally keeps system-style dot directories such as .data and .cache, but skips known noise files like .DS_Store and .manifest.json.

API gateway

apiGatewayPlugin is the same-origin gateway used by NetworkService.

Service Endpoint Notes
fetch POST /api/gw/fetch JSON payload with url, method, headers, and string body.
proxy /api/gw/proxy?url=https://... Streams requests/responses for assets and non-string bodies.

VITE_GW_ALLOW_HOSTS can be set to a comma-separated host allowlist. If it is empty, dev mode allows all hosts.

The gateway keeps a server-side cookie jar per x-gw-session and contains defensive handling for upstream compression, aborts, retries, and repeated 404 image/video fetches.

Navigation artifacts

Run after modifying navigation declarations:

node scripts/build_nav_artifacts.mjs <AppName>

Useful variants:

node scripts/build_nav_artifacts.mjs <AppName> --data data/index.ts
node scripts/build_nav_artifacts.mjs <AppName> --skip-tasks
node scripts/check_navigation_declaration_consistency.mjs <AppName> --actions

Generated files are written under public/:

Artifact Meaning
<app>_nav_graph.json Full schema graph.
<app>_nav_graph_simplified.json Route-level graph with uiStates collapsed.
<app>_data_graph.json Concrete data-mode graph when --data is provided.
<app>_action_tasks.json / .jsonl Generated action trajectories from the schema graph.
<app>_action_tasks_data.json Generated action trajectories from the data graph when --data is provided.

See ../navigation/declaration.md and ../navigation/data-sources.md for declaration semantics.

WMR tooling

WMR is the Widget Markup Runtime used by launcher widgets and app-owned canvas widgets.

Current implementation lives under:

Area Files
Widget discovery os/wmr/WmrWidgetService.ts
Bundle loading/cache os/wmr/WmrBundleCache.ts
XML parser os/wmr/engine/parser.ts
Expression/variable runtime os/wmr/engine/expression.ts, os/wmr/engine/variables.ts
Canvas renderer os/wmr/engine/renderer.ts, os/wmr/WmrRenderer.tsx
Content providers os/wmr/engine/contentProviders.ts
Perf metrics os/wmr/WmrPerf.ts via window.__WMR_PERF__

Theme widgets are listed from cdn('themes')/manifest.json. App-owned WMR bundles can use inline sources and /@app-assets/<AppName>/wmr/... URLs.

i18n tooling

OS-level i18n lives in:

File Role
os/locale.ts Locale state, backed by OsStateStore.settings.global.language.
os/i18n/index.ts useOsT(), osT(), and app-name localization helpers.
os/i18n/en.ts English dictionary and patchAppNames().
os/PackageManagerService.ts Auto-discovers manifests and patches displayNameEn into the English dictionary.

Manifest displayNameEn values are injected automatically; new apps should not edit os/i18n/en.ts just to add their app name.

Validation checklist

Change type Suggested check
Navigation declaration or new page route node scripts/build_nav_artifacts.mjs <AppName>
Runtime TypeScript behavior across several files npx tsc --noEmit
OS/app runtime code npm run lint
System app runtime code npx eslint system/
Store action/query selector changes node scripts/lint_store_getters.mjs [AppName...]
Bench tasks Follow bench_env/docs/task/ guides before editing or running tasks.

New app checklist

Use this as the compact version of the app onboarding checklist:

Area Check
Manifest manifest.ts exports manifest, has a unique id, displayName, displayNameEn, icon, theme, and any intentFilters.
Entry component <Name>App.tsx matches *App.tsx, has a default export, wraps routes in MemoryRouter, and calls useAppNavigationHandler inside the router.
Navigation Every route, discrete UI state, transition, action, and scroll container is declared in navigation.declaration.ts.
Business navigation Pages use the app's go() / back() helper, never direct useNavigate() imports.
DOM tagging Interactive controls use gesture hooks so they emit data-trigger* or data-action*; back/close controls bind system.back.
Data Structural constants live in constants.ts; replaceable runtime defaults live in data/defaults.json; data/index.ts exports <APPNAME>_CONFIG.
Large data Big public datasets stay out of defaults.json; add loader.ts and decide hook vs store-action vs service consumption.
Store state.ts uses createAppStore* with the same appId as manifest.id; _temp is reserved for transient UI state.
Resources App icons are Ic* aliases in res/icons.tsx; data-driven icon names also use Ic*.
UI chrome Page roots reserve status bar space and declare status/navigation bar foreground when needed.
Keyboard Bottom input bars use flex layout plus data-keep-keyboard; tab bars that should disappear use data-hide-on-keyboard.
Validation Run node scripts/build_nav_artifacts.mjs <AppName> after navigation changes; run type/lint checks when the runtime change is broad.