Quick start
From a built web app to an installer in dist-native/, with one script per command.
An installer in six steps
-
Install.
@moku-labs/coreand@moku-labs/commonare peers. A realnodebinary must be onPATH:@tauri-apps/clidoes not run under Bun.install bun add @moku-labs/native @moku-labs/core @moku-labs/common -
Ignore the output. The generated Tauri project and the installers are never committed.
.gitignore .moku/ dist-native/ -
Compose the native app. It is its own
createApp, next to your web app. It never runs in the page. -
Add one script per command. The
cliplugin has no argument parser. Each script calls one method. -
Ask the doctor. It tells you what the host is missing for each target, with the command that fixes it.
-
Build. The installers land in
dist-native/<target>/.
The code
import { createApp } from "@moku-labs/native";
export const native = createApp({
config: {
app: { name: "MyApp", identifier: "com.example.myapp" },
web: {
build: "bun run build",
devCommand: "bun run dev",
devUrl: "http://localhost:5173",
dist: "dist"
},
system: [{ name: "store" }]
}
});- Both are required. No icon yet: a placeholder is generated.
- Names drive dependencies, Rust init and permissions. An unknown name throws at
createApp.
import { native } from "../src/native";
await native.start();
const ok = await native.cli.doctor();
await native.stop();
if (!ok) process.exitCode = 1;doctorreturns the verdict. Setting the exit code is yours.
import { native } from "../src/native";
await native.start();
await native.cli.build();
await native.stop();- No target: the host's own desktop target.
bun scripts/native-doctor.ts bun scripts/native-build.ts
On a Mac this leaves a .dmg and a .app in dist-native/macos/. The first build compiles Rust, so
it takes minutes.
The dev loop
import { native } from "../src/native";
await native.start();
await native.cli.dev();
await native.stop();- Runs until you stop it. Ctrl-C ends the whole process group.
dev runs web.devCommand, waits for web.devUrl to answer and opens the app window on it.
Add a phone
Mobile is opt-in. Name the target and ask the doctor again:
import { native } from "../src/native";
await native.start();
await native.cli.doctor({ target: "ios" });
await native.cli.build({ target: "ios", simulator: true });
await native.stop();- Unsigned, for the simulator. No Apple account needed.
Related
- How it works: the generated project and the pipeline.
- iOS simulator: the tools this needs.
- Signing and store builds: when you ship.