diff --git a/apps/cli/src/helpers/addons/tauri-setup.ts b/apps/cli/src/helpers/addons/tauri-setup.ts index c4ec46eea..54508fc02 100644 --- a/apps/cli/src/helpers/addons/tauri-setup.ts +++ b/apps/cli/src/helpers/addons/tauri-setup.ts @@ -1,64 +1,14 @@ -import { spinner } from "@clack/prompts"; -import { getLocalWebDevPort } from "@better-fullstack/types"; -import { consola } from "consola"; -import { $ } from "execa"; -import fs from "fs-extra"; -import path from "node:path"; -import pc from "picocolors"; - import type { ProjectConfig } from "../../types"; -import { getPackageRunnerPrefix } from "../../utils/package-runner"; - -export async function setupTauri(config: ProjectConfig) { - const { packageManager, frontend, projectDir } = config; - const s = spinner(); - const clientPackageDir = path.join(projectDir, "apps/web"); - - if (!(await fs.pathExists(clientPackageDir))) { - return; - } - - try { - s.start("Setting up Tauri desktop app support..."); - - const hasNuxt = frontend.includes("nuxt"); - const hasSvelte = frontend.includes("svelte"); - const hasNext = frontend.includes("next"); - - const devUrl = `http://localhost:${getLocalWebDevPort(frontend)}`; - - const frontendDist = hasNuxt - ? "../.output/public" - : hasSvelte - ? "../build" - : hasNext - ? "../.next" - : frontend.includes("react-router") - ? "../build/client" - : frontend.includes("react-vite") - ? "../dist" - : "../dist"; - - const tauriArgs = [ - "@tauri-apps/cli@latest", - "init", - `--app-name=${path.basename(projectDir)}`, - `--window-title=${path.basename(projectDir)}`, - `--frontend-dist=${frontendDist}`, - `--dev-url=${devUrl}`, - `--before-dev-command=${packageManager} run dev`, - `--before-build-command=${packageManager} run build`, - ]; - const prefix = getPackageRunnerPrefix(packageManager); - - await $({ cwd: clientPackageDir, env: { CI: "true" } })`${[...prefix, ...tauriArgs]}`; - - s.stop("Tauri desktop app support configured successfully!"); - } catch (error) { - s.stop(pc.red("Failed to set up Tauri")); - if (error instanceof Error) { - consola.error(pc.red(error.message)); - } - } +/** + * Tauri setup is now handled entirely by template generation. + * The src-tauri/ directory (tauri.conf.json, Cargo.toml, main.rs, lib.rs, etc.) + * is emitted by the addons template handler from templates/addons/tauri/. + * Dependencies (@tauri-apps/cli, @tauri-apps/api) and scripts (tauri, desktop:dev, + * desktop:build) are added by addons-deps.ts. + * + * This function is kept as a no-op for backward compatibility with addons-setup.ts. + */ +export async function setupTauri(_config: ProjectConfig) { + // Templates handle everything — no runtime CLI execution needed. } diff --git a/apps/cli/test/__snapshots__/template-snapshots.test.ts.snap b/apps/cli/test/__snapshots__/template-snapshots.test.ts.snap index 73c2428df..b322a1774 100644 --- a/apps/cli/test/__snapshots__/template-snapshots.test.ts.snap +++ b/apps/cli/test/__snapshots__/template-snapshots.test.ts.snap @@ -687,6 +687,68 @@ exports[`Template Snapshots File Structure Snapshots file structure: algolia-sea ] `; +exports[`Template Snapshots File Structure Snapshots file structure: tauri-next-desktop 1`] = ` +[ + "CLAUDE.md", + "README.md", + "apps/server/.env", + "apps/server/package.json", + "apps/server/src/index.ts", + "apps/server/tsconfig.json", + "apps/server/tsdown.config.ts", + "apps/web/.env", + "apps/web/next-env.d.ts", + "apps/web/next.config.ts", + "apps/web/package.json", + "apps/web/postcss.config.mjs", + "apps/web/src-tauri/Cargo.toml", + "apps/web/src-tauri/build.rs", + "apps/web/src-tauri/capabilities/default.json", + "apps/web/src-tauri/src/lib.rs", + "apps/web/src-tauri/src/main.rs", + "apps/web/src-tauri/tauri.conf.json", + "apps/web/src/app/layout.tsx", + "apps/web/src/app/page.tsx", + "apps/web/src/components/header.tsx", + "apps/web/src/components/loader.tsx", + "apps/web/src/components/mode-toggle.tsx", + "apps/web/src/components/providers.tsx", + "apps/web/src/components/theme-provider.tsx", + "apps/web/src/components/ui/button.tsx", + "apps/web/src/components/ui/card.tsx", + "apps/web/src/components/ui/checkbox.tsx", + "apps/web/src/components/ui/dropdown-menu.tsx", + "apps/web/src/components/ui/input.tsx", + "apps/web/src/components/ui/label.tsx", + "apps/web/src/components/ui/skeleton.tsx", + "apps/web/src/components/ui/sonner.tsx", + "apps/web/src/index.css", + "apps/web/src/lib/utils.ts", + "apps/web/src/utils/trpc.ts", + "apps/web/tsconfig.json", + "package.json", + "packages/api/package.json", + "packages/api/src/context.ts", + "packages/api/src/index.ts", + "packages/api/src/routers/index.ts", + "packages/api/tsconfig.json", + "packages/config/package.json", + "packages/config/tsconfig.base.json", + "packages/db/drizzle.config.ts", + "packages/db/package.json", + "packages/db/src/index.ts", + "packages/db/src/schema/example.ts", + "packages/db/src/schema/index.ts", + "packages/db/tsconfig.json", + "packages/env/package.json", + "packages/env/src/native.ts", + "packages/env/src/server.ts", + "packages/env/src/web.ts", + "packages/env/tsconfig.json", + "tsconfig.json", +] +`; + exports[`Template Snapshots File Structure Snapshots file structure: frontend-only-no-backend 1`] = ` [ "CLAUDE.md", @@ -9940,6 +10002,762 @@ export const db = drizzle({ client, schema }); } `; +exports[`Template Snapshots Key File Content Snapshots key files: tauri-next-desktop 1`] = ` +{ + "fileCount": 63, + "files": [ + { + "content": "[exists]", + "path": "apps/server/.env", + }, + { + "content": +"{ + "name": "server", + "main": "src/index.ts", + "type": "module", + "scripts": { + "build": "tsdown", + "check-types": "tsc -b", + "compile": "bun build --compile --minify --sourcemap --bytecode ./src/index.ts --outfile server", + "dev": "bun run --hot src/index.ts", + "start": "bun run dist/index.js" + }, + "dependencies": { + "dotenv": "catalog:", + "zod": "catalog:", + "@snapshot-tauri-next-desktop/env": "workspace:*", + "@snapshot-tauri-next-desktop/api": "workspace:*", + "@snapshot-tauri-next-desktop/db": "workspace:*", + "hono": "catalog:", + "@trpc/server": "catalog:", + "@hono/trpc-server": "^0.4.2" + }, + "devDependencies": { + "typescript": "^6.0.2", + "tsdown": "^0.21.7", + "@snapshot-tauri-next-desktop/config": "workspace:*", + "@types/bun": "catalog:" + } +} +" +, + "path": "apps/server/package.json", + }, + { + "content": +"import { env } from "@snapshot-tauri-next-desktop/env/server"; +import { trpcServer } from "@hono/trpc-server"; +import { createContext } from "@snapshot-tauri-next-desktop/api/context"; +import { appRouter } from "@snapshot-tauri-next-desktop/api/routers/index"; +import { Hono } from "hono"; +import { cors } from "hono/cors"; +import { logger } from "hono/logger"; + +const app = new Hono(); + +app.use(logger()); +app.use( + "/*", + cors({ + origin: env.CORS_ORIGIN, + allowMethods: ["GET", "POST", "OPTIONS"], + }) +); + + + +app.use( + "/trpc/*", + trpcServer({ + router: appRouter, + createContext: (_opts, context) => { + return createContext({ context }); + }, + }) +); + + + +app.get("/", (c) => { + return c.text("OK"); +}); + +export default app; +" +, + "path": "apps/server/src/index.ts", + }, + { + "content": +"{ + "extends": "@snapshot-tauri-next-desktop/config/tsconfig.base.json", + "compilerOptions": { + "composite": true, + "outDir": "dist", + "paths": { + "@/*": ["./src/*"] + }, + "jsx": "react-jsx", + "jsxImportSource": "hono/jsx" + } +} +" +, + "path": "apps/server/tsconfig.json", + }, + { + "content": "[exists]", + "path": "apps/server/tsdown.config.ts", + }, + { + "content": "[exists]", + "path": "apps/web/.env", + }, + { + "content": "[exists]", + "path": "apps/web/next-env.d.ts", + }, + { + "content": +"import "@snapshot-tauri-next-desktop/env/web"; +import type { NextConfig } from "next"; + +const nextConfig: NextConfig = { + typedRoutes: true, + reactCompiler: true, +}; + +export default nextConfig; + +" +, + "path": "apps/web/next.config.ts", + }, + { + "content": +"{ + "name": "web", + "version": "0.1.0", + "private": true, + "scripts": { + "dev": "next dev --port 3001", + "build": "next build", + "start": "next start", + "tauri": "tauri", + "desktop:dev": "tauri dev", + "desktop:build": "tauri build" + }, + "dependencies": { + "@base-ui/react": "^1.3.0", + "class-variance-authority": "^0.7.1", + "clsx": "^2.1.1", + "next-themes": "^0.4.6", + "sonner": "^2.0.7", + "tailwind-merge": "^3.5.0", + "tw-animate-css": "^1.4.0", + "next": "^16.2.2", + "react": "^19.2.4", + "react-dom": "^19.2.3", + "babel-plugin-react-compiler": "^1.0.0", + "dotenv": "catalog:", + "zod": "catalog:", + "@snapshot-tauri-next-desktop/env": "workspace:*", + "@snapshot-tauri-next-desktop/api": "workspace:*", + "@libsql/client": "catalog:", + "libsql": "catalog:", + "@trpc/tanstack-react-query": "^11.16.0", + "@trpc/client": "catalog:", + "@trpc/server": "catalog:", + "@tanstack/react-query": "^5.96.2", + "@tauri-apps/api": "^2.5.0", + "lucide-react": "^1.7.0" + }, + "devDependencies": { + "@tailwindcss/postcss": "^4.2.2", + "tailwindcss": "^4.2.2", + "@types/node": "^25.5.2", + "@types/react": "^19.2.14", + "@types/react-dom": "^19.2.3", + "typescript": "^5", + "@snapshot-tauri-next-desktop/config": "workspace:*", + "@tanstack/react-query-devtools": "^5.96.2", + "@tauri-apps/cli": "^2.10.1" + } +} +" +, + "path": "apps/web/package.json", + }, + { + "content": "[exists]", + "path": "apps/web/postcss.config.mjs", + }, + { + "content": "[exists]", + "path": "apps/web/src-tauri/build.rs", + }, + { + "content": "[exists]", + "path": "apps/web/src-tauri/capabilities/default.json", + }, + { + "content": +"[package] +name = "snapshot_tauri_next_desktop_desktop" +version = "0.1.0" +description = "A Tauri desktop application" +authors = ["you"] +edition = "2021" + +[lib] +name = "snapshot_tauri_next_desktop_desktop_lib" +crate-type = ["lib", "cdylib", "staticlib"] + +[build-dependencies] +tauri-build = { version = "2", features = [] } + +[dependencies] +tauri = { version = "2", features = [] } +tauri-plugin-opener = "2" +serde = { version = "1", features = ["derive"] } +serde_json = "1" +" +, + "path": "apps/web/src-tauri/Cargo.toml", + }, + { + "content": +"#[tauri::command] +fn greet(name: &str) -> String { + format!("Hello, {}! You've been greeted from Rust!", name) +} + +#[cfg_attr(mobile, tauri::mobile_entry_point)] +pub fn run() { + tauri::Builder::default() + .plugin(tauri_plugin_opener::init()) + .invoke_handler(tauri::generate_handler![greet]) + .run(tauri::generate_context!()) + .expect("error while running tauri application"); +} +" +, + "path": "apps/web/src-tauri/src/lib.rs", + }, + { + "content": +"// Prevents additional console window on Windows in release, DO NOT REMOVE!! +#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")] + +fn main() { + snapshot_tauri_next_desktop_desktop_lib::run() +} +" +, + "path": "apps/web/src-tauri/src/main.rs", + }, + { + "content": "[exists]", + "path": "apps/web/src-tauri/tauri.conf.json", + }, + { + "content": "[exists]", + "path": "apps/web/src/app/layout.tsx", + }, + { + "content": "[exists]", + "path": "apps/web/src/app/page.tsx", + }, + { + "content": "[exists]", + "path": "apps/web/src/components/header.tsx", + }, + { + "content": "[exists]", + "path": "apps/web/src/components/loader.tsx", + }, + { + "content": "[exists]", + "path": "apps/web/src/components/mode-toggle.tsx", + }, + { + "content": "[exists]", + "path": "apps/web/src/components/providers.tsx", + }, + { + "content": "[exists]", + "path": "apps/web/src/components/theme-provider.tsx", + }, + { + "content": "[exists]", + "path": "apps/web/src/components/ui/button.tsx", + }, + { + "content": "[exists]", + "path": "apps/web/src/components/ui/card.tsx", + }, + { + "content": "[exists]", + "path": "apps/web/src/components/ui/checkbox.tsx", + }, + { + "content": "[exists]", + "path": "apps/web/src/components/ui/dropdown-menu.tsx", + }, + { + "content": "[exists]", + "path": "apps/web/src/components/ui/input.tsx", + }, + { + "content": "[exists]", + "path": "apps/web/src/components/ui/label.tsx", + }, + { + "content": "[exists]", + "path": "apps/web/src/components/ui/skeleton.tsx", + }, + { + "content": "[exists]", + "path": "apps/web/src/components/ui/sonner.tsx", + }, + { + "content": "[exists]", + "path": "apps/web/src/index.css", + }, + { + "content": "[exists]", + "path": "apps/web/src/lib/utils.ts", + }, + { + "content": +"import { QueryCache, QueryClient } from '@tanstack/react-query'; +import { createTRPCClient, httpBatchLink } from '@trpc/client'; +import { createTRPCOptionsProxy } from '@trpc/tanstack-react-query'; +import type { AppRouter } from "@snapshot-tauri-next-desktop/api/routers/index"; +import { toast } from 'sonner'; +import { env } from "@snapshot-tauri-next-desktop/env/web"; + +export const queryClient = new QueryClient({ + queryCache: new QueryCache({ + onError: (error, query) => { + toast.error(error.message, { + action: { + label: "retry", + onClick: query.invalidate, + }, + }); + }, + }), +}); + +const trpcClient = createTRPCClient({ + links: [ + httpBatchLink({ + url: \`\${env.NEXT_PUBLIC_SERVER_URL}/trpc\`, + }), + ], +}) + +export const trpc = createTRPCOptionsProxy({ + client: trpcClient, + queryClient, +}); + +" +, + "path": "apps/web/src/utils/trpc.ts", + }, + { + "content": +"{ + "compilerOptions": { + "target": "ES2017", + "lib": ["dom", "dom.iterable", "esnext"], + "allowJs": true, + "skipLibCheck": true, + "strict": true, + "noEmit": true, + "esModuleInterop": true, + "module": "esnext", + "moduleResolution": "bundler", + "resolveJsonModule": true, + "isolatedModules": true, + "verbatimModuleSyntax": true, + "jsx": "preserve", + "incremental": true, + "plugins": [ + { + "name": "next" + } + ], + "paths": { + "@/*": ["./src/*"] + } + }, + "include": [ + "./next-env.d.ts", + "./**/*.ts", + "./**/*.tsx", + "./.next/types/**/*.ts" + ], + "exclude": [ + "./node_modules" + ] +} +" +, + "path": "apps/web/tsconfig.json", + }, + { + "content": "[exists]", + "path": "CLAUDE.md", + }, + { + "content": +"{ + "name": "snapshot-tauri-next-desktop", + "private": true, + "type": "module", + "workspaces": { + "packages": [ + "apps/*", + "packages/*" + ], + "catalog": { + "dotenv": "^17.4.1", + "zod": "^4.3.6", + "@types/bun": "^1.3.11", + "hono": "^4.12.11", + "@trpc/server": "^11.16.0", + "@libsql/client": "^0.17.2", + "libsql": "^0.5.29", + "@trpc/client": "^11.16.0" + } + }, + "scripts": { + "dev": "bun run --filter '*' dev", + "build": "bun run --filter '*' build", + "check-types": "bun run --if-present --filter '*' check-types", + "dev:native": "bun run --filter native dev", + "dev:web": "bun run --filter web dev", + "dev:server": "bun run --filter server dev", + "db:push": "bun run --filter @snapshot-tauri-next-desktop/db db:push", + "db:studio": "bun run --filter @snapshot-tauri-next-desktop/db db:studio", + "db:generate": "bun run --filter @snapshot-tauri-next-desktop/db db:generate", + "db:migrate": "bun run --filter @snapshot-tauri-next-desktop/db db:migrate", + "db:local": "bun run --filter @snapshot-tauri-next-desktop/db db:local" + }, + "packageManager": "bun@1.3.5", + "dependencies": { + "dotenv": "catalog:", + "zod": "catalog:", + "@snapshot-tauri-next-desktop/env": "workspace:*" + }, + "devDependencies": { + "typescript": "^6.0.2", + "@types/bun": "catalog:", + "@snapshot-tauri-next-desktop/config": "workspace:*" + } +} +" +, + "path": "package.json", + }, + { + "content": +"{ + "name": "@snapshot-tauri-next-desktop/api", + "exports": { + ".": { + "default": "./src/index.ts" + }, + "./*": { + "default": "./src/*.ts" + } + }, + "type": "module", + "scripts": {}, + "devDependencies": { + "typescript": "^6.0.2", + "@snapshot-tauri-next-desktop/config": "workspace:*" + }, + "dependencies": { + "dotenv": "catalog:", + "zod": "catalog:", + "@snapshot-tauri-next-desktop/env": "workspace:*", + "@snapshot-tauri-next-desktop/db": "workspace:*", + "@trpc/server": "catalog:", + "@trpc/client": "catalog:", + "hono": "catalog:" + } +} +" +, + "path": "packages/api/package.json", + }, + { + "content": "[exists]", + "path": "packages/api/src/context.ts", + }, + { + "content": +"import { initTRPC } from "@trpc/server"; +import type { Context } from "./context.js"; + +export const t = initTRPC.context().create(); + +export const router = t.router; + +export const publicProcedure = t.procedure; + +" +, + "path": "packages/api/src/index.ts", + }, + { + "content": +"import { + publicProcedure, + router, +} from "../index.js"; + +export const appRouter = router({ + healthCheck: publicProcedure.query(() => { + return "OK"; + }), +}); +export type AppRouter = typeof appRouter; +" +, + "path": "packages/api/src/routers/index.ts", + }, + { + "content": +"{ + "extends": "@snapshot-tauri-next-desktop/config/tsconfig.base.json", + "compilerOptions": { + "declaration": true, + "declarationMap": true, + "sourceMap": true, + "outDir": "dist", + "composite": true + } +} +" +, + "path": "packages/api/tsconfig.json", + }, + { + "content": +"{ + "name": "@snapshot-tauri-next-desktop/config", + "version": "0.0.0", + "private": true +} +" +, + "path": "packages/config/package.json", + }, + { + "content": +"{ + "$schema": "https://json.schemastore.org/tsconfig", + "compilerOptions": { + "target": "ESNext", + "module": "ESNext", + "moduleResolution": "bundler", + "lib": ["ESNext"], + "verbatimModuleSyntax": true, + "strict": true, + "skipLibCheck": true, + "resolveJsonModule": true, + "allowSyntheticDefaultImports": true, + "esModuleInterop": true, + "forceConsistentCasingInFileNames": true, + "isolatedModules": true, + "noUncheckedIndexedAccess": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "noFallthroughCasesInSwitch": true, + "types": [ + "bun" + + ] + } +} +" +, + "path": "packages/config/tsconfig.base.json", + }, + { + "content": +"import { defineConfig } from "drizzle-kit"; +import dotenv from "dotenv"; + +dotenv.config({ + path: "../../apps/server/.env", +}); + +export default defineConfig({ + schema: "./src/schema", + out: "./src/migrations", + dialect: "turso", + dbCredentials: { + url: process.env.DATABASE_URL || "", + }, +}); +" +, + "path": "packages/db/drizzle.config.ts", + }, + { + "content": +"{ + "name": "@snapshot-tauri-next-desktop/db", + "type": "module", + "exports": { + ".": { + "default": "./src/index.ts" + }, + "./*": { + "default": "./src/*.ts" + } + }, + "scripts": { + "db:local": "turso dev --db-file local.db", + "db:push": "drizzle-kit push", + "db:generate": "drizzle-kit generate", + "db:studio": "drizzle-kit studio", + "db:migrate": "drizzle-kit migrate" + }, + "devDependencies": { + "typescript": "^6.0.2", + "@snapshot-tauri-next-desktop/config": "workspace:*", + "drizzle-kit": "^0.31.10" + }, + "dependencies": { + "dotenv": "catalog:", + "zod": "catalog:", + "@snapshot-tauri-next-desktop/env": "workspace:*", + "drizzle-orm": "^0.45.2", + "@libsql/client": "catalog:", + "libsql": "catalog:" + } +} +" +, + "path": "packages/db/package.json", + }, + { + "content": +"import { env } from "@snapshot-tauri-next-desktop/env/server"; +import * as schema from "./schema/index.js"; +import { drizzle } from "drizzle-orm/libsql"; +import { createClient } from "@libsql/client"; + +const client = createClient({ + url: env.DATABASE_URL, +}); + +export const db = drizzle({ client, schema }); + +" +, + "path": "packages/db/src/index.ts", + }, + { + "content": "[exists]", + "path": "packages/db/src/schema/example.ts", + }, + { + "content": +"export * from "./example.js"; +" +, + "path": "packages/db/src/schema/index.ts", + }, + { + "content": +"{ + "extends": "@snapshot-tauri-next-desktop/config/tsconfig.base.json", + "compilerOptions": { + "declaration": true, + "declarationMap": true, + "sourceMap": true, + "outDir": "dist", + "composite": true + } +} +" +, + "path": "packages/db/tsconfig.json", + }, + { + "content": +"{ + "name": "@snapshot-tauri-next-desktop/env", + "version": "0.0.0", + "private": true, + "type": "module", + "exports": { + "./server": "./src/server.ts", + "./web": "./src/web.ts" + }, + "dependencies": { + "dotenv": "catalog:", + "zod": "catalog:", + "@t3-oss/env-nextjs": "^0.13.11", + "@t3-oss/env-core": "^0.13.11" + }, + "devDependencies": { + "typescript": "^6.0.2", + "@types/bun": "catalog:", + "@snapshot-tauri-next-desktop/config": "workspace:*" + } +} +" +, + "path": "packages/env/package.json", + }, + { + "content": "[exists]", + "path": "packages/env/src/native.ts", + }, + { + "content": "[exists]", + "path": "packages/env/src/server.ts", + }, + { + "content": "[exists]", + "path": "packages/env/src/web.ts", + }, + { + "content": +"{ + "extends": "@snapshot-tauri-next-desktop/config/tsconfig.base.json", +} +" +, + "path": "packages/env/tsconfig.json", + }, + { + "content": "[exists]", + "path": "README.md", + }, + { + "content": +"{ + "extends": "@snapshot-tauri-next-desktop/config/tsconfig.base.json", +} +" +, + "path": "tsconfig.json", + }, + ], +} +`; + exports[`Template Snapshots Key File Content Snapshots key files: frontend-only-no-backend 1`] = ` { "fileCount": 35, diff --git a/apps/cli/test/template-snapshots.test.ts b/apps/cli/test/template-snapshots.test.ts index 2b754058e..bb236101e 100644 --- a/apps/cli/test/template-snapshots.test.ts +++ b/apps/cli/test/template-snapshots.test.ts @@ -171,6 +171,20 @@ const SNAPSHOT_CONFIGS: Array<{ }, }, + // === ADDON VARIATIONS === + { + name: "tauri-next-desktop", + config: { + frontend: ["next"], + backend: "hono", + api: "trpc", + database: "sqlite", + orm: "drizzle", + auth: "none", + addons: ["tauri"], + }, + }, + // === SPECIAL CASES === { name: "frontend-only-no-backend", diff --git a/packages/template-generator/src/processors/addons-deps.ts b/packages/template-generator/src/processors/addons-deps.ts index abaf5eb50..2b31596cf 100644 --- a/packages/template-generator/src/processors/addons-deps.ts +++ b/packages/template-generator/src/processors/addons-deps.ts @@ -125,7 +125,12 @@ export function processAddonsDeps(vfs: VirtualFileSystem, config: ProjectConfig) if (config.addons.includes("tauri")) { if (vfs.exists(webPkgPath)) { - addPackageDependency({ vfs, packagePath: webPkgPath, devDependencies: ["@tauri-apps/cli"] }); + addPackageDependency({ + vfs, + packagePath: webPkgPath, + dependencies: ["@tauri-apps/api"], + devDependencies: ["@tauri-apps/cli"], + }); const webPkg = vfs.readJson(webPkgPath); if (webPkg) { webPkg.scripts = { diff --git a/packages/template-generator/src/template-handlers/addons.ts b/packages/template-generator/src/template-handlers/addons.ts index b37751d0c..75048081f 100644 --- a/packages/template-generator/src/template-handlers/addons.ts +++ b/packages/template-generator/src/template-handlers/addons.ts @@ -30,6 +30,14 @@ export async function processAddonTemplates( continue; } + // Tauri templates - add src-tauri to web app + if (addon === "tauri") { + if (vfs.exists("apps/web/package.json")) { + processTemplatesFromPrefix(vfs, templates, "addons/tauri/apps/web", "apps/web", config); + } + continue; + } + // MSW templates - only add to existing packages if (addon === "msw") { if (vfs.exists("apps/web/package.json")) { diff --git a/packages/template-generator/src/utils/add-deps.ts b/packages/template-generator/src/utils/add-deps.ts index 72efb1f8a..5c7e4530a 100644 --- a/packages/template-generator/src/utils/add-deps.ts +++ b/packages/template-generator/src/utils/add-deps.ts @@ -92,6 +92,7 @@ export const dependencyVersionMap = { "@vite-pwa/assets-generator": "^1.0.2", "@tauri-apps/cli": "^2.10.1", + "@tauri-apps/api": "^2.5.0", "@biomejs/biome": "^2.4.10", diff --git a/packages/template-generator/templates/addons/tauri/apps/web/src-tauri/Cargo.toml.hbs b/packages/template-generator/templates/addons/tauri/apps/web/src-tauri/Cargo.toml.hbs new file mode 100644 index 000000000..ec61e8b14 --- /dev/null +++ b/packages/template-generator/templates/addons/tauri/apps/web/src-tauri/Cargo.toml.hbs @@ -0,0 +1,19 @@ +[package] +name = "{{replace projectName "-" "_"}}_desktop" +version = "0.1.0" +description = "A Tauri desktop application" +authors = ["you"] +edition = "2021" + +[lib] +name = "{{replace projectName "-" "_"}}_desktop_lib" +crate-type = ["lib", "cdylib", "staticlib"] + +[build-dependencies] +tauri-build = { version = "2", features = [] } + +[dependencies] +tauri = { version = "2", features = [] } +tauri-plugin-opener = "2" +serde = { version = "1", features = ["derive"] } +serde_json = "1" diff --git a/packages/template-generator/templates/addons/tauri/apps/web/src-tauri/build.rs b/packages/template-generator/templates/addons/tauri/apps/web/src-tauri/build.rs new file mode 100644 index 000000000..d860e1e6a --- /dev/null +++ b/packages/template-generator/templates/addons/tauri/apps/web/src-tauri/build.rs @@ -0,0 +1,3 @@ +fn main() { + tauri_build::build() +} diff --git a/packages/template-generator/templates/addons/tauri/apps/web/src-tauri/capabilities/default.json b/packages/template-generator/templates/addons/tauri/apps/web/src-tauri/capabilities/default.json new file mode 100644 index 000000000..f5c766d53 --- /dev/null +++ b/packages/template-generator/templates/addons/tauri/apps/web/src-tauri/capabilities/default.json @@ -0,0 +1,12 @@ +{ + "$schema": "../gen/schemas/desktop-schema.json", + "identifier": "default", + "description": "Capability for the main window", + "windows": [ + "main" + ], + "permissions": [ + "core:default", + "opener:default" + ] +} diff --git a/packages/template-generator/templates/addons/tauri/apps/web/src-tauri/src/lib.rs b/packages/template-generator/templates/addons/tauri/apps/web/src-tauri/src/lib.rs new file mode 100644 index 000000000..46a3ee3ae --- /dev/null +++ b/packages/template-generator/templates/addons/tauri/apps/web/src-tauri/src/lib.rs @@ -0,0 +1,13 @@ +#[tauri::command] +fn greet(name: &str) -> String { + format!("Hello, {}! You've been greeted from Rust!", name) +} + +#[cfg_attr(mobile, tauri::mobile_entry_point)] +pub fn run() { + tauri::Builder::default() + .plugin(tauri_plugin_opener::init()) + .invoke_handler(tauri::generate_handler![greet]) + .run(tauri::generate_context!()) + .expect("error while running tauri application"); +} diff --git a/packages/template-generator/templates/addons/tauri/apps/web/src-tauri/src/main.rs.hbs b/packages/template-generator/templates/addons/tauri/apps/web/src-tauri/src/main.rs.hbs new file mode 100644 index 000000000..747df590b --- /dev/null +++ b/packages/template-generator/templates/addons/tauri/apps/web/src-tauri/src/main.rs.hbs @@ -0,0 +1,6 @@ +// Prevents additional console window on Windows in release, DO NOT REMOVE!! +#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")] + +fn main() { + {{replace projectName "-" "_"}}_desktop_lib::run() +} diff --git a/packages/template-generator/templates/addons/tauri/apps/web/src-tauri/tauri.conf.json.hbs b/packages/template-generator/templates/addons/tauri/apps/web/src-tauri/tauri.conf.json.hbs new file mode 100644 index 000000000..fe84f5db4 --- /dev/null +++ b/packages/template-generator/templates/addons/tauri/apps/web/src-tauri/tauri.conf.json.hbs @@ -0,0 +1,89 @@ +{ + "$schema": "https://raw.githubusercontent.com/tauri-apps/tauri/dev/crates/tauri-cli/schema.json", + "productName": "{{projectName}}", + "version": "0.1.0", + "identifier": "com.{{replace projectName "-" "."}}.app", + "build": { +{{#if (includes frontend "next")}} + "beforeDevCommand": "cd .. && {{packageManager}} run dev", + "devUrl": "http://localhost:3001", + "beforeBuildCommand": "cd .. && {{packageManager}} run build", + "frontendDist": "../.next" +{{else if (includes frontend "nuxt")}} + "beforeDevCommand": "cd .. && {{packageManager}} run dev", + "devUrl": "http://localhost:3001", + "beforeBuildCommand": "cd .. && {{packageManager}} run build", + "frontendDist": "../.output/public" +{{else if (includes frontend "svelte")}} + "beforeDevCommand": "cd .. && {{packageManager}} run dev", + "devUrl": "http://localhost:5173", + "beforeBuildCommand": "cd .. && {{packageManager}} run build", + "frontendDist": "../build" +{{else if (includes frontend "react-router")}} + "beforeDevCommand": "cd .. && {{packageManager}} run dev", + "devUrl": "http://localhost:5173", + "beforeBuildCommand": "cd .. && {{packageManager}} run build", + "frontendDist": "../build/client" +{{else if (includes frontend "react-vite")}} + "beforeDevCommand": "cd .. && {{packageManager}} run dev", + "devUrl": "http://localhost:5173", + "beforeBuildCommand": "cd .. && {{packageManager}} run build", + "frontendDist": "../dist" +{{else if (includes frontend "solid")}} + "beforeDevCommand": "cd .. && {{packageManager}} run dev", + "devUrl": "http://localhost:3001", + "beforeBuildCommand": "cd .. && {{packageManager}} run build", + "frontendDist": "../dist" +{{else if (includes frontend "solid-start")}} + "beforeDevCommand": "cd .. && {{packageManager}} run dev", + "devUrl": "http://localhost:3001", + "beforeBuildCommand": "cd .. && {{packageManager}} run build", + "frontendDist": "../.output/public" +{{else if (includes frontend "angular")}} + "beforeDevCommand": "cd .. && {{packageManager}} run dev", + "devUrl": "http://localhost:3001", + "beforeBuildCommand": "cd .. && {{packageManager}} run build", + "frontendDist": "../dist/{{projectName}}/browser" +{{else if (includes frontend "astro")}} + "beforeDevCommand": "cd .. && {{packageManager}} run dev", + "devUrl": "http://localhost:3001", + "beforeBuildCommand": "cd .. && {{packageManager}} run build", + "frontendDist": "../dist" +{{else if (includes frontend "tanstack-start")}} + "beforeDevCommand": "cd .. && {{packageManager}} run dev", + "devUrl": "http://localhost:3001", + "beforeBuildCommand": "cd .. && {{packageManager}} run build", + "frontendDist": "../.output/public" +{{else}} + "beforeDevCommand": "cd .. && {{packageManager}} run dev", + "devUrl": "http://localhost:5173", + "beforeBuildCommand": "cd .. && {{packageManager}} run build", + "frontendDist": "../dist" +{{/if}} + }, + "app": { + "windows": [ + { + "title": "{{projectName}}", + "width": 1024, + "height": 768, + "resizable": true, + "fullscreen": false + } + ], + "security": { + "csp": null + } + }, + "bundle": { + "active": true, + "targets": "all", + "icon": [ + "icons/32x32.png", + "icons/128x128.png", + "icons/128x128@2x.png", + "icons/icon.icns", + "icons/icon.ico" + ] + } +}