{"id":34882034,"url":"https://github.com/thwbh/tauri-typegen","last_synced_at":"2026-02-05T17:07:03.292Z","repository":{"id":313273218,"uuid":"1050719257","full_name":"thwbh/tauri-typegen","owner":"thwbh","description":"A rust crate that automatically generates TypeScript models and bindings from your Tauri commands","archived":false,"fork":false,"pushed_at":"2025-12-25T23:43:50.000Z","size":677,"stargazers_count":16,"open_issues_count":0,"forks_count":3,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-12-27T07:06:32.062Z","etag":null,"topics":["bindings","bindings-generator","codegen","command-line-tool","tauri","tauri-plugin","tauri2","typescript-bindings","zod-validation"],"latest_commit_sha":null,"homepage":"","language":"Rust","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/thwbh.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2025-09-04T20:40:34.000Z","updated_at":"2025-12-25T23:38:19.000Z","dependencies_parsed_at":"2025-09-05T01:08:58.640Z","dependency_job_id":"48374c7a-eb81-44e1-af7f-41d569f26079","html_url":"https://github.com/thwbh/tauri-typegen","commit_stats":null,"previous_names":["thwbh/tauri-typegen"],"tags_count":14,"template":false,"template_full_name":null,"purl":"pkg:github/thwbh/tauri-typegen","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/thwbh%2Ftauri-typegen","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/thwbh%2Ftauri-typegen/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/thwbh%2Ftauri-typegen/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/thwbh%2Ftauri-typegen/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/thwbh","download_url":"https://codeload.github.com/thwbh/tauri-typegen/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/thwbh%2Ftauri-typegen/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":29126155,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-02-05T14:05:12.718Z","status":"ssl_error","status_checked_at":"2026-02-05T14:03:53.078Z","response_time":65,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["bindings","bindings-generator","codegen","command-line-tool","tauri","tauri-plugin","tauri2","typescript-bindings","zod-validation"],"created_at":"2025-12-26T02:18:54.368Z","updated_at":"2026-02-05T17:07:03.286Z","avatar_url":"https://github.com/thwbh.png","language":"Rust","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Tauri TypeGen\n\n[![Crates.io](https://img.shields.io/crates/v/tauri-typegen.svg)](https://crates.io/crates/tauri-typegen)\n[![Documentation](https://docs.rs/tauri-typegen/badge.svg)](https://docs.rs/tauri-typegen)\n[![codecov](https://codecov.io/gh/thwbh/tauri-typegen/branch/main/graph/badge.svg)](https://codecov.io/gh/thwbh/tauri-typegen)\n[![Test](https://github.com/thwbh/tauri-typegen/actions/workflows/ci.yml/badge.svg)](https://github.com/thwbh/tauri-typegen/actions/workflows/ci.yml)\n\nA command-line tool that automatically generates TypeScript bindings from your Tauri commands, eliminating manual frontend type creation.\n\n## Features\n\n- 🔍 **Automatic Discovery**: Scans Rust source for `#[tauri::command]` functions\n- 📝 **TypeScript Generation**: Creates TypeScript interfaces for command parameters and return types\n- ✅ **Validation Support**: Optional Zod schema generation with runtime validation\n- 🚀 **Command Bindings**: Strongly-typed frontend functions\n- 📡 **Event Support**: Discovers and types `app.emit()` events\n- 📞 **Channel Support**: Types for streaming `Channel\u003cT\u003e` parameters\n- 🏷️ **Serde Support**: Respects `#[serde(rename)]` and `#[serde(rename_all)]` attributes\n- 🎯 **Type Safety**: Keeps frontend and backend types in sync\n- 🛠️ **Build Integration**: Works as standalone CLI or build dependency\n- ⚡ **Smart Caching**: Only regenerates when source files change\n\n## Table of Contents\n\n- [Installation](#installation)\n- [Quick Setup](#quick-setup)\n- [Recommended Setup](#recommended-setup)\n- [Generated Code](#generated-code)\n- [Using Generated Bindings](#using-generated-bindings)\n- [TypeScript Compatibility](#typescript-compatibility)\n- [API Reference](#api-reference)\n- [Configuration](#configuration)\n- [Caching](#caching)\n- [Usage in CI](#usage-in-ci)\n- [Examples](#examples)\n- [Contributing](#contributing)\n\n## Installation\n\nInstall globally as a CLI tool:\n\n```bash\ncargo install tauri-typegen\n```\n\n**Or** add as a build dependency to your Tauri project:\n\n```bash\ncargo add --build tauri-typegen\n```\n\n## Quick Setup\n\nFor trying it out or one-time generation:\n\n```bash\n# Install CLI\ncargo install tauri-typegen\n\n# Generate types once\ncargo tauri-typegen generate\n\n# Use generated bindings\n```\n\nThis generates TypeScript files in `./src/generated/` from your `./src-tauri/` code.\n\n## Recommended Setup\n\nFor integrated development workflow:\n\n### 1. Install and Initialize\n\n```bash\n# Install CLI\ncargo install tauri-typegen\n\n# Initialize configuration (adds to tauri.conf.json)\ncargo tauri-typegen init\n\n# Or with custom settings\ncargo tauri-typegen init --validation zod --output tauri.conf.json\n```\n\nThis creates a configuration block in your `tauri.conf.json`:\n\n```json\n{\n  \"plugins\": {\n    \"tauri-typegen\": {\n      \"project_path\": \".\",\n      \"output_path\": \"../src/generated\",\n      \"validation_library\": \"none\",\n      \"verbose\": false\n    }\n  }\n}\n```\n\n### 2. Add Build Hook\n\nAdd `tauri-typegen` as a build dependency from within your Tauri project (in the `src-tauri` directory):\n\n```bash\ncd src-tauri\ncargo add --build tauri-typegen\ncd ..\n```\n\nThen add to `src-tauri/build.rs`:\n\n```rust\nfn main() {\n    // Generate TypeScript bindings before build\n    tauri_typegen::BuildSystem::generate_at_build_time()\n        .expect(\"Failed to generate TypeScript bindings\");\n\n    tauri_build::build()\n}\n```\n\nNow types auto-generate on every Rust build:\n\n```bash\nnpm run tauri dev   # Types generated automatically\nnpm run tauri build # Types generated automatically\n```\n\n## Generated Code\n\n### Example Rust Code\n\n```rust\nuse serde::{Deserialize, Serialize};\nuse tauri::ipc::Channel;\n\n#[derive(Serialize, Deserialize)]\npub struct User {\n    pub id: i32,\n    pub name: String,\n    pub email: String,\n}\n\n#[derive(Deserialize)]\npub struct CreateUserRequest {\n    pub name: String,\n    pub email: String,\n}\n\n#[derive(Clone, Serialize)]\npub struct ProgressUpdate {\n    pub percentage: f32,\n    pub message: String,\n}\n\n// Simple command\n#[tauri::command]\npub async fn get_user(id: i32) -\u003e Result\u003cUser, String\u003e {\n    // Implementation\n}\n\n// Command with custom types\n#[tauri::command]\npub async fn create_user(request: CreateUserRequest) -\u003e Result\u003cUser, String\u003e {\n    // Implementation\n}\n\n// Command with Channel for progress streaming\n#[tauri::command]\npub async fn download_file(\n    url: String,\n    on_progress: Channel\u003cProgressUpdate\u003e\n) -\u003e Result\u003cString, String\u003e {\n    // Send progress updates\n    on_progress.send(ProgressUpdate {\n        percentage: 50.0,\n        message: \"Halfway done\".to_string()\n    })?;\n    // Implementation\n}\n\n// Event emission\npub fn notify_user(app: \u0026AppHandle, message: String) {\n    app.emit(\"user-notification\", message).unwrap();\n}\n```\n\n### Generated Files\n\n```\nsrc/generated/\n├── types.ts       # TypeScript interfaces\n├── commands.ts    # Typed command functions\n└── events.ts      # Event listener functions (if events detected)\n```\n\n**Generated `types.ts`:**\n\n```typescript\nimport type { Channel } from '@tauri-apps/api/core';\n\nexport interface User {\n  id: number;\n  name: string;\n  email: string;\n}\n\nexport interface CreateUserRequest {\n  name: string;\n  email: string;\n}\n\nexport interface ProgressUpdate {\n  percentage: number;\n  message: string;\n}\n\nexport interface GetUserParams {\n  id: number;\n}\n\nexport interface CreateUserParams {\n  request: CreateUserRequest;\n}\n\nexport interface DownloadFileParams {\n  url: string;\n  onProgress: Channel\u003cProgressUpdate\u003e;\n}\n```\n\n**Generated `commands.ts`:**\n\n```typescript\nimport { invoke, Channel } from '@tauri-apps/api/core';\nimport * as types from './types';\n\nexport async function getUser(params: types.GetUserParams): Promise\u003ctypes.User\u003e {\n  return invoke('get_user', params);\n}\n\nexport async function createUser(params: types.CreateUserParams): Promise\u003ctypes.User\u003e {\n  return invoke('create_user', params);\n}\n\nexport async function downloadFile(params: types.DownloadFileParams): Promise\u003cstring\u003e {\n  return invoke('download_file', params);\n}\n```\n\n**Generated `events.ts`:**\n\n```typescript\nimport { listen } from '@tauri-apps/api/event';\n\nexport async function onUserNotification(handler: (event: string) =\u003e void) {\n  return listen('user-notification', (event) =\u003e handler(event.payload as string));\n}\n```\n\n### With Zod Validation\n\nWhen using `--validation zod`, generated commands include runtime validation:\n\n```typescript\nexport async function createUser(\n  params: types.CreateUserParams,\n  hooks?: CommandHooks\u003ctypes.User\u003e\n): Promise\u003ctypes.User\u003e {\n  try {\n    const result = types.CreateUserParamsSchema.safeParse(params);\n\n    if (!result.success) {\n      hooks?.onValidationError?.(result.error);\n      throw result.error;\n    }\n\n    const data = await invoke\u003ctypes.User\u003e('create_user', result.data);\n    hooks?.onSuccess?.(data);\n    return data;\n  } catch (error) {\n    if (!(error instanceof ZodError)) {\n      hooks?.onInvokeError?.(error);\n    }\n    throw error;\n  } finally {\n    hooks?.onSettled?.();\n  }\n}\n```\n\n## Using Generated Bindings\n\n### Basic Usage\n\n```typescript\nimport { getUser, createUser, downloadFile } from './generated';\nimport { Channel } from '@tauri-apps/api/core';\n\n// Simple command\nconst user = await getUser({ id: 1 });\n\n// With custom types\nconst newUser = await createUser({\n  request: {\n    name: \"John Doe\",\n    email: \"john@example.com\"\n  }\n});\n\n// With Channel for streaming\nconst onProgress = new Channel\u003cProgressUpdate\u003e();\nonProgress.onmessage = (progress) =\u003e {\n  console.log(`${progress.percentage}%: ${progress.message}`);\n};\n\nconst result = await downloadFile({\n  url: \"https://example.com/file.zip\",\n  onProgress\n});\n```\n\n### With Event Listeners\n\n```typescript\nimport { onUserNotification } from './generated';\n\n// Listen for events\nconst unlisten = await onUserNotification((message) =\u003e {\n  console.log('Notification:', message);\n});\n\n// Stop listening\nunlisten();\n```\n\n### React Example\n\n```tsx\nimport React, { useState } from 'react';\nimport { createUser } from './generated';\nimport type { User } from './generated';\n\nexport function CreateUserForm() {\n  const [name, setName] = useState('');\n  const [email, setEmail] = useState('');\n\n  const handleSubmit = async (e: React.FormEvent) =\u003e {\n    e.preventDefault();\n\n    const user = await createUser({\n      request: { name, email }\n    });\n\n    console.log('Created:', user);\n  };\n\n  return (\n    \u003cform onSubmit={handleSubmit}\u003e\n      \u003cinput value={name} onChange={e =\u003e setName(e.target.value)} /\u003e\n      \u003cinput value={email} onChange={e =\u003e setEmail(e.target.value)} /\u003e\n      \u003cbutton type=\"submit\"\u003eCreate User\u003c/button\u003e\n    \u003c/form\u003e\n  );\n}\n```\n\n### With Zod Validation Hooks\n\n```typescript\nimport { createUser } from './generated';\nimport { toast } from 'sonner';\n\nawait createUser(\n  { request: userData },\n  {\n    onValidationError: (err) =\u003e toast.error(err.errors[0].message),\n    onInvokeError: (err) =\u003e toast.error('Failed to create user'),\n    onSuccess: (user) =\u003e toast.success(`Created ${user.name}!`),\n  }\n);\n```\n\n## TypeScript Compatibility\n\n### Requirements\n\n- **TypeScript 5.0+**\n- **Zod 4.x** (when using Zod validation)\n- **ES2018+** target\n\n### tsconfig.json\n\n```json\n{\n  \"compilerOptions\": {\n    \"target\": \"ES2018\",\n    \"module\": \"ESNext\",\n    \"moduleResolution\": \"bundler\",\n    \"strict\": true,\n    \"esModuleInterop\": true,\n    \"skipLibCheck\": true\n  }\n}\n```\n\n### Type Mappings\n\n| Rust Type | TypeScript |\n|-----------|-----------|\n| `String`, `\u0026str` | `string` |\n| `i8`, `i16`, `i32`, `i64`, `i128`, `isize` | `number` |\n| `u8`, `u16`, `u32`, `u64`, `u128`, `usize` | `number` |\n| `f32`, `f64` | `number` |\n| `bool` | `boolean` |\n| `()` | `void` |\n| `Option\u003cT\u003e` | `T \\| null` |\n| `Vec\u003cT\u003e` | `T[]` |\n| `HashMap\u003cK,V\u003e`, `BTreeMap\u003cK,V\u003e` | `Record\u003cK, V\u003e` |\n| `HashSet\u003cT\u003e`, `BTreeSet\u003cT\u003e` | `T[]` |\n| `(T, U, V)` | `[T, U, V]` |\n| `Channel\u003cT\u003e` | `Channel\u003cT\u003e` |\n| `Result\u003cT, E\u003e` | `T` (errors via Promise rejection) |\n\n### Serde Attribute Support\n\nTauri-typegen respects serde serialization attributes to ensure generated TypeScript types match your JSON API:\n\n#### Field Renaming\n\n```rust\nuse serde::{Deserialize, Serialize};\n\n#[derive(Serialize, Deserialize)]\npub struct User {\n    #[serde(rename = \"userId\")]\n    pub user_id: i32,\n    pub name: String,\n}\n```\n\nGenerates:\n\n```typescript\nexport interface User {\n  userId: number;  // Field renamed as specified\n  name: string;\n}\n```\n\n#### Struct-Level Naming Convention\n\n```rust\n#[derive(Serialize, Deserialize)]\n#[serde(rename_all = \"camelCase\")]\npub struct ApiResponse {\n    pub user_id: i32,\n    pub user_name: String,\n    pub is_active: bool,\n}\n```\n\nGenerates:\n\n```typescript\nexport interface ApiResponse {\n  userId: number;      // snake_case → camelCase\n  userName: string;    // snake_case → camelCase\n  isActive: boolean;   // snake_case → camelCase\n}\n```\n\n**Supported naming conventions:**\n- `camelCase`\n- `PascalCase`\n- `snake_case`\n- `SCREAMING_SNAKE_CASE`\n- `kebab-case`\n- `SCREAMING-KEBAB-CASE`\n\n#### Field Precedence\n\nField-level `rename` takes precedence over struct-level `rename_all`:\n\n```rust\n#[derive(Serialize, Deserialize)]\n#[serde(rename_all = \"camelCase\")]\npub struct User {\n    pub user_id: i32,              // → userId\n    #[serde(rename = \"fullName\")]\n    pub user_name: String,          // → fullName (override)\n}\n```\n\n#### Skip Fields\n\n```rust\n#[derive(Serialize, Deserialize)]\npub struct User {\n    pub id: i32,\n    #[serde(skip)]\n    pub internal_data: String,  // Not included in TypeScript\n}\n```\n\n#### Enum Support\n\nEnums also support serde rename attributes:\n\n```rust\n#[derive(Serialize, Deserialize)]\n#[serde(rename_all = \"SCREAMING_SNAKE_CASE\")]\npub enum MyEnum {\n    HelloWorld,  // → HELLO_WORLD\n    ByeWorld,    // → BYE_WORLD\n}\n```\n\nGenerates:\n\n```typescript\nexport type MyEnum = \"HELLO_WORLD\" | \"BYE_WORLD\";\n\n// With Zod:\nexport const MyEnumSchema = z.enum([\"HELLO_WORLD\", \"BYE_WORLD\"]);\n```\n\nVariant-level rename also works:\n\n```rust\n#[derive(Serialize, Deserialize)]\n#[serde(rename_all = \"camelCase\")]\npub enum Status {\n    InProgress,           // → inProgress\n    #[serde(rename = \"not-started\")]\n    NotStarted,          // → not-started (override)\n}\n```\n\n## API Reference\n\n### CLI Commands\n\n```bash\n# Generate bindings\ncargo tauri-typegen generate [OPTIONS]\n\nOptions:\n  -p, --project-path \u003cPATH\u003e     Tauri source directory [default: ./src-tauri]\n  -o, --output-path \u003cPATH\u003e      Output directory [default: ./src/generated]\n  -v, --validation \u003cLIBRARY\u003e    Validation library: zod or none [default: none]\n      --verbose                 Verbose output\n      --visualize-deps          Generate dependency graph\n  -c, --config \u003cFILE\u003e           Config file path\n  -f, --force                   Force regeneration, ignoring cache\n```\n\n```bash\n# Initialize configuration\ncargo tauri-typegen init [OPTIONS]\n\nOptions:\n  -p, --project-path \u003cPATH\u003e     Tauri source directory [default: ./src-tauri]\n  -g, --generated-path \u003cPATH\u003e   Output directory [default: ./src/generated]\n  -o, --output \u003cFILE\u003e          Config file [default: tauri.conf.json]\n  -v, --validation \u003cLIBRARY\u003e    Validation library [default: none]\n      --force                   Overwrite existing config\n```\n\n### Build Script API\n\nAdd as a build dependency:\n\n```bash\ncd src-tauri\ncargo add --build tauri-typegen\n```\n\nThen in `src-tauri/build.rs`:\n\n```rust\nfn main() {\n    // Generate TypeScript bindings\n    tauri_typegen::BuildSystem::generate_at_build_time()\n        .expect(\"Failed to generate TypeScript bindings\");\n\n    tauri_build::build()\n}\n```\n\n### Programmatic API\n\n```rust\nuse tauri_typegen::{GenerateConfig, generate_from_config};\n\nlet config = GenerateConfig {\n    project_path: \".\".to_string(),\n    output_path: \"../src/generated\".to_string(),\n    validation_library: \"none\".to_string(),\n    verbose: Some(true),\n};\n\nlet files = generate_from_config(\u0026config)?;\n```\n\n## Configuration\n\n### Standalone Config File\n\n```json\n{\n  \"project_path\": \".\",\n  \"output_path\": \"../src/generated\",\n  \"validation_library\": \"none\",\n  \"verbose\": false\n}\n```\n\n### Tauri Config Integration\n\nIn `tauri.conf.json`:\n\n```json\n{\n  \"plugins\": {\n    \"typegen\": {\n      \"projectPath\": \".\",\n      \"outputPath\": \"../src/generated\",\n      \"validationLibrary\": \"zod\",\n      \"verbose\": true,\n      \"force\": false\n    }\n  }\n}\n```\n\n### Validation Options\n\n- **`none`** (default): TypeScript types only, no runtime validation\n- **`zod`**: Generate Zod schemas with runtime validation and hooks\n\n### Custom Type Mappings\n\nMap external Rust types to TypeScript types for libraries like `chrono`, `uuid`, or custom types:\n\n```json\n{\n  \"plugins\": {\n    \"tauri-typegen\": {\n      \"project_path\": \".\",\n      \"output_path\": \"../src/generated\",\n      \"validation_library\": \"zod\",\n      \"type_mappings\": {\n        \"DateTime\u003cUtc\u003e\": \"string\",\n        \"PathBuf\": \"string\",\n        \"Uuid\": \"string\"\n      }\n    }\n  }\n}\n```\n\n**Use cases:**\n- External crate types: `chrono::DateTime\u003cUtc\u003e` → `string`\n- Standard library types: `std::path::PathBuf` → `string`\n- Third-party types: `uuid::Uuid` → `string`\n- Custom wrapper types: `UserId` → `number`\n\n**Example:**\n\nRust code:\n```rust\nuse chrono::{DateTime, Utc};\nuse std::path::PathBuf;\n\n#[derive(Serialize)]\npub struct FileMetadata {\n    pub path: PathBuf,\n    pub created_at: DateTime\u003cUtc\u003e,\n}\n\n#[tauri::command]\npub fn get_file_info() -\u003e FileMetadata {\n    // ...\n}\n```\n\nGenerated TypeScript (with mappings):\n```typescript\nexport interface FileMetadata {\n  path: string;        // PathBuf → string\n  createdAt: string;   // DateTime\u003cUtc\u003e → string\n}\n\nexport async function getFileInfo(): Promise\u003cFileMetadata\u003e {\n  return invoke('get_file_info');\n}\n```\n\n## Caching\n\nTauri-typegen uses smart caching to skip regeneration when nothing has changed, improving build times.\n\n### How It Works\n\nA `.typecache` file is created in your output directory containing hashes of:\n- All discovered Tauri commands\n- All discovered structs and enums\n- Configuration settings that affect output\n\nOn subsequent runs, these hashes are compared. If nothing changed, generation is skipped.\n\n### Force Regeneration\n\nTo bypass the cache and force regeneration:\n\n**CLI flag (highest priority):**\n```bash\ncargo tauri-typegen generate --force\n# or\ncargo tauri-typegen generate -f\n```\n\n**Config file (`tauri.conf.json`):**\n```json\n{\n  \"plugins\": {\n    \"typegen\": {\n      \"force\": true\n    }\n  }\n}\n```\n\n**Programmatic:**\n```rust\nlet mut config = GenerateConfig::default();\nconfig.force = Some(true);\n```\n\nThe CLI `--force` flag always overrides the config file value.\n\n### Cache File Location\n\nThe cache file `.typecache` is stored in your output directory (e.g., `./src/generated/.typecache`). Add it to `.gitignore`:\n\n```gitignore\n# Tauri-typegen cache\n.typecache\n```\n\nOr if your entire output directory is gitignored, the cache file is already excluded.\n\n## Usage in CI\n\nWhen running builds in CI/CD environments, you need to generate TypeScript bindings before the frontend build step.\n\n### Why CI Needs Special Setup\n\nThe `cargo tauri build` command builds the frontend bundle first, before compiling Rust code. This means the build script in `src-tauri/build.rs` hasn't run yet, so bindings aren't generated when the frontend needs them.\n\n### Recommended CI Workflow\n\nInstall and run the CLI tool as a separate step before building:\n\n```yaml\n# GitHub Actions example\n- name: Install tauri-typegen\n  run: cargo install tauri-typegen\n\n- name: Generate TypeScript bindings\n  run: cargo tauri-typegen generate\n\n- name: Build Tauri app\n  run: npm run tauri build\n```\n\n```yaml\n# GitLab CI example\nbuild:\n  script:\n    - cargo install tauri-typegen\n    - cargo tauri-typegen generate\n    - npm run tauri build\n```\n\n### Alternative: Cache the CLI Installation\n\nTo speed up CI runs, cache the installed binary:\n\n```yaml\n# GitHub Actions with caching\n- name: Cache tauri-typegen\n  uses: actions/cache@v4\n  with:\n    path: ~/.cargo/bin/cargo-tauri-typegen\n    key: ${{ runner.os }}-tauri-typegen-${{ hashFiles('**/Cargo.lock') }}\n\n- name: Install tauri-typegen\n  run: cargo install tauri-typegen --locked\n\n- name: Generate bindings\n  run: cargo tauri-typegen generate\n\n- name: Build\n  run: npm run tauri build\n```\n\n## Examples\n\nSee the examples repository: https://github.com/thwbh/tauri-typegen-examples\n\n## Contributing\n \n1. Fork the repository\n2. Create a feature branch\n3. Make your changes\n4. Add tests if applicable\n5. Submit a pull request\n\n## License\n\nThis project is licensed under the MIT license.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fthwbh%2Ftauri-typegen","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fthwbh%2Ftauri-typegen","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fthwbh%2Ftauri-typegen/lists"}