💬

Tauri2.0+Svelte5のDevContainer開発環境構築

2024/10/10に公開


Tauri 2.0がリリースされたということで、モバイル用ではないのですが作成したかったアプリを作ることにしました。フロントエンドはSvelte5で作成します。インストール方法など公式が丁寧なのであまり迷わないのですが、フロントエンドのフレームワークを使用する場合どちらを先にインストールした方が良いのか等、微妙にハテナ状態になりました。その辺りを含め開発環境を構築した備忘を残しておきます。

疑問点と解決

微妙にわかりにくかった事

create-tauri-appを使う公式ドキュメントのところに以下のような推奨があるが、後からSvelteをインストールした際に競合ファイルを上書きしても問題ないか等で困惑した。

We recommend starting with the vanilla template (HTML, CSS, and JavaScript without a frontend framework) to get started. You can always integrate a frontend framework later.

後からわかった事

create-tauri-appはフロントエンド側の設定等もある程度セットアップされるが、その部分で競合が起きる場合がある。フロントエンド側の設定を全く行わないtauri-cliがあるのでそれを使用すれば競合なく共存できる。

具体的な環境構築手順

前提

  • WSL2のDebian上で稼働しているDockerでDevContainerをホスト
    • Docker Desktopは使用していない
  • 以下を使用する開発環境を構築する
    • Tauri 2.0
    • Svelte 5
    • TailwindCSS
  • カレントディレクトリはプロジェクトディレクトリ想定
  • Windows向けバイナリを作成する

DevContainer設定とビルド

  1. 以下ファイルを作成する
./.devcontainer/devcontainer.json
{
    "name": "my-tauri-app",
    // Or use a Dockerfile or Docker Compose file. More info: https://containers.dev/guide/dockerfile
    "image": "mcr.microsoft.com/devcontainers/base:debian",
    // Features to add to the dev container. More info: https://containers.dev/features.
    "features": {
        "ghcr.io/devcontainers/features/node:1": {},
        "ghcr.io/devcontainers/features/rust:1": {
          "profile": "default"
        }
    },

    // Use 'forwardPorts' to make a list of ports inside the container available locally.
    // "forwardPorts": [],

    // Use 'postCreateCommand' to run commands after the container is created.
    "postCreateCommand": "/bin/sh ./.devcontainer/setup.sh",

    // Configure tool-specific properties.
    "customizations": {
      "vscode": {
        "extensions": [
          "svelte.svelte-vscode",
          "bradlc.vscode-tailwindcss",
          "tauri-apps.tauri-vscode",
        ],
        "settings": {}
      }
    },

    // Uncomment to connect as root instead. More info: https://aka.ms/dev-containers-non-root.
    // "remoteUser": "root"
}
./.devcontainer/setup.sh
#!/bin/sh
sudo apt update
sudo apt install -y libwebkit2gtk-4.1-dev \
  build-essential \
  curl \
  wget \
  file \
  libxdo-dev \
  libssl-dev \
  libayatana-appindicator3-dev \
  librsvg2-dev \
  gcc-mingw-w64-x86-64-win32 \
  nsis
mkdir ./.cargo
cat << EOF > ./.cargo/config.toml
[target.x86_64-pc-windows-gnu]
linker = "x86_64-w64-mingw32-gcc"
EOF
rustup target add x86_64-pc-windows-gnu
cargo install tauri-cli --version "^2.0.0" --locked
  1. code .等でVSCodeでプロジェクトディレクトリを開く
  2. Ctrl+Shift+Pでコマンドパレットを開きDevContainers: Open Folder in Container...を選択
    • なかなか時間がかかるので待つ
  3. コンテナ内で以下コマンドを実行しSvelte5をインストール
npm create svelte@latest
# Need to install the following packages:
# create-svelte@6.3.12
# Ok to proceed? (y) y

# > npx
# > create-svelte
# ┌  Welcome to SvelteKit!
# │
# ◇  Where should we create your project?
# │    (hit Enter to use current directory)
# │
# ◇  Directory not empty. Continue?
# │  Yes
# │
# ◇  Which Svelte app template?
# │  Skeleton project
# │
# ◇  Add type checking with TypeScript?
# │  Yes, using TypeScript syntax
# │
# ◇  Select additional options (use arrow keys/space bar)
# │  Add ESLint for code linting, Add Prettier for code formatting, Add Playwright for browser testing, Try the Svelte 5 preview (unstable!)
# │
# └  Your project is ready!
sed -i '/"dev": "vite dev"/ s/vite dev/vite dev --host/' ./package.json
sed -i '/"preview": "vite preview"/ s/vite preview/vite preview --host/' ./package.json
  1. SvelteをSPA&SSGでトランスパイルするよう設定
npm install --save-dev @sveltejs/adapter-static
sed -i '/^import adapter from/ s:@sveltejs/adapter-auto:@sveltejs/adapter-static:' ./svelte.config.js
cat << EOF | sed -i '/adapter: adapter()/ r /dev/stdin' ./svelte.config.js
    adapter: adapter({
      pages: 'build',
      assets: 'build',
      fallback: undefined,
      precompress: false,
      strict: true
    }),
EOF
sed -i '/adapter: adapter()/ d' ./svelte.config.js
cat << EOF > ./src/routes/+layout.ts
export const prerender = true;
export const ssr = false;
EOF
  1. TailwindCSSをインストール
npm install --save-dev tailwindcss postcss autoprefixer
npx tailwindcss init -p
sed -i '/content: \[\]/ s:\[\]:\["./src/**/*.{html,js,svelte,ts}"\]:' ./tailwind.config.js
cat << EOF > ./src/app.css
@tailwind base;
@tailwind components;
@tailwind utilities;
EOF
sed -i '/"plugins": \["prettier-plugin-svelte"\]/ s/"prettier-plugin-svelte"/"prettier-plugin-svelte", "prettier-plugin-tailwindcss"/' ./.prettierrc
  1. Tauri用ディレクトリを追加 (./src-tauri)
cargo tauri init
# ✔ What is your app name? · my-tauri-app
# ✔ What should the window title be? · my-app-title
# ✔ Where are your web assets (HTML/CSS/JS) located, relative to the "<current dir>/src-tauri/tauri.conf.json" file that will be created? · ../build
# ✔ What is the url of your dev server? · http://localhost:5173
# ✔ What is your frontend dev command? · npm run dev
# ✔ What is your frontend build command? · npm run build
sed -i '/"identifier": "com.tauri.dev"/ s/com.tauri.dev/dev.example.tauri/' ./src-tauri/tauri.conf.json
sed -i '/"csp": null/ s/null/"default-src '\''self'\''"/' ./src-tauri/tauri.conf.json
sed -i '/"version": "0.1.0"/ d' ./src-tauri/tauri.conf.json
sed -i '/"targets": "all"/ a \    "category": "Utility",' ./src-tauri/tauri.conf.json

sed -i '/^name = "app"/ s/app/my-tauri-app/' ./src-tauri/Cargo.toml
sed -i '/^description = "A Tauri App"/ s/A Tauri App/My tauri application/' ./src-tauri/Cargo.toml
sed -i '/^authors = \["you"\]/ s/you/myname/' ./src-tauri/Cargo.toml
sed -i '/^license = ""/ s/""/"MIT OR Apache-2.0"/' ./src-tauri/Cargo.toml
sed -i '/^repository = ""/ s#""#"https://github.com/name/repository"#' ./src-tauri/Cargo.toml
  1. dev動作確認
cargo tauri dev


img_tauri_dev
9. build動作確認

cargo tauri build --target x86_64-pc-windows-gnu
# `--release` option is included in `tauri build`
# output bin to \\wsl.localhost\dist_name\project_dir\my-tauri-app\src-tauri\target\x86_64-pc-windows-gnu\release\my-tauri-app.exe
# output installer to \\wsl.localhost\dist_name\project_dir\my-tauri-app\src-tauri\target/x86_64-pc-windows-gnu/release/bundle/nsis/my-tauri-app_0.1.0_x64-setup.exe


img_tauri_build.png

特殊仕様入り自分用完全自動スクリプト

変更点を明確にしておきバージョンアップ等に備える。

setup files
  • 実行前にGitHubでレポジトリを作成する
./.devcontainer/devcontainer.json
{
    "name": "${localWorkspaceFolderBasename}",
    // Or use a Dockerfile or Docker Compose file. More info: https://containers.dev/guide/dockerfile
    "image": "mcr.microsoft.com/devcontainers/base:debian",
    // Features to add to the dev container. More info: https://containers.dev/features.
    "features": {
        "ghcr.io/devcontainers/features/node:1": {},
        "ghcr.io/devcontainers/features/rust:1": {
          "profile": "default"
        }
    },

    // Use 'forwardPorts' to make a list of ports inside the container available locally.
    // "forwardPorts": [],

    // Use 'postCreateCommand' to run commands after the container is created.
    "postCreateCommand": "/bin/bash ./.devcontainer/setup.sh ${localWorkspaceFolderBasename}",

    // Configure tool-specific properties.
    "customizations": {
      "vscode": {
        "extensions": [
          "svelte.svelte-vscode",
          "bradlc.vscode-tailwindcss",
          "tauri-apps.tauri-vscode",
        ],
        "settings": {}
      }
    },

    // Uncomment to connect as root instead. More info: https://aka.ms/dev-containers-non-root.
    // "remoteUser": "root"
}
./.devcontainer/svelte.mjs
import { create } from "create-svelte";

async function createSvelteProject() {
  await create(".", {
    name: process.argv[2],
    template: "skeleton",
    types: "typescript",
    prettier: true,
    eslint: true,
    playwright: true,
    vitest: false,
    svelte5: true
  });
}

createSvelteProject();
./.devcontainer/setup.sh
#!/bin/bash
# install prerequisites (for windows -> gcc-mingw-w64-x86-64-win32, nsis)
sudo apt update
sudo apt install -y libwebkit2gtk-4.1-dev \
  build-essential \
  curl \
  wget \
  file \
  libxdo-dev \
  libssl-dev \
  libayatana-appindicator3-dev \
  librsvg2-dev \
  gcc-mingw-w64-x86-64-win32 \
  nsis
mkdir ./.cargo
cat << EOF > ./.cargo/config.toml
[target.x86_64-pc-windows-gnu]
linker = "x86_64-w64-mingw32-gcc"
EOF
rustup target add x86_64-pc-windows-gnu
cargo install tauri-cli --version "^2.0.0" --locked

# install svelte & set up ssg
npm install create-svelte
node ./.devcontainer/svelte.mjs $1
sleep 3
sed -i '/"dev": "vite dev"/ s/vite dev/vite dev --host/' ./package.json
sed -i '/"preview": "vite preview"/ s/vite preview/vite preview --host/' ./package.json
sed -i '/"strict": true/ a \		"typeRoots": ["./node_modules/@types", "./src/lib/type"],' ./tsconfig.json
sed -i '/ignores:/ i\
		files: ["src/lib/util.ts"],\
		rules: {\
			"@typescript-eslint/no-explicit-any": "off",\
		}\
	},\
	{' ./eslint.config.js
sed -i '/^declare global/ i /// <reference types="../lib/type" />' ./src/app.d.ts
sed -i '/favicon.png/ s#<link rel="icon" href="%sveltekit.assets%/favicon.png" />#<link rel="icon" href="data:,">#' ./src/app.html
cat << EOF > ./src/routes/+layout.svelte
<script module lang="ts">
  import "../app.css";
  import { emitTo } from "@tauri-apps/api/event";
</script>
<script lang="ts">
  function oncontextmenu(ev: Event) {
    ev.preventDefault();
    emitTo("main", "right-click");
  }
</script>
<svelte:document {oncontextmenu} />

<header>
  <h1>HEADER</h1>
</header>

<main>
  <slot />
</main>

<footer>
  <p>footer</p>
</footer>
EOF
rm ./src/lib/index.ts
rm ./static/favicon.png

npm install --save-dev @sveltejs/adapter-static
sed -i '/^import adapter from/ s:@sveltejs/adapter-auto:@sveltejs/adapter-static:' ./svelte.config.js
cat << EOF | sed -i '/adapter: adapter()/ r /dev/stdin' ./svelte.config.js
    adapter: adapter({
      pages: 'build',
      assets: 'build',
      fallback: undefined,
      precompress: false,
      strict: true
    }),
EOF
sed -i '/adapter: adapter()/ d' ./svelte.config.js
cat << EOF > ./src/routes/+layout.ts
export const prerender = true;
export const ssr = false;
EOF

# install tailwind
npm install --save-dev tailwindcss postcss autoprefixer
npx tailwindcss init -p
sleep 3
sed -i '/content: \[\]/ s:\[\]:\["./src/**/*.{html,js,svelte,ts}", "./lib/style.ts", "./lib/assy/*.svelte"\]:' ./tailwind.config.js
cat << EOF | sed -i '/extend: {}/ r /dev/stdin' ./tailwind.config.js
    extend: {
      colors: {
        canvas: "var(--color-canvas)",
        active: "var(--color-active)",
        inactive: "var(--color-inactive)",
        charline: "var(--color-charline)",
        invalid: "var(--color-invalid)",
      }
    }
EOF
sed -i '/extend: {}/ d' ./tailwind.config.js
cat << EOF > ./src/app.css
@tailwind base;
@tailwind components;
@tailwind utilities;

  :root {
    --color-canvas: #000;
    --color-active: #000;
    --color-inactive: #000;
    --color-charline: #000;
    --color-invalid: #000;
  }
  html {
    font-family: system-ui, ui-sans-serif, -apple-system, "Hiragino Sans", "Yu Gothic UI", "Segoe UI", "Meiryo", sans-serif;
  }
  body {
    background-color: var(--color-canvas);
  }
EOF
sed -i '/"plugins": \["prettier-plugin-svelte"\]/ s/"prettier-plugin-svelte"/"prettier-plugin-svelte", "prettier-plugin-tailwindcss"/' ./.prettierrc

# Install tauri
cargo tauri init -A $1 -W $1 -D ../build -P http://localhost:5173 --before-dev-command "npm run dev" --before-build-command "npm run build"
sleep 5
sed -i "/\"identifier\": \"com.tauri.dev\"/ s/com.tauri.dev/dev.scirexs.${1//_/-}/" ./src-tauri/tauri.conf.json
sed -i '/"csp": null/ s/null/"default-src '\''self'\''"/' ./src-tauri/tauri.conf.json
sed -i '/"version": "0.1.0"/ d' ./src-tauri/tauri.conf.json
sed -i "/\"targets\": \"all\"/ a \    \"copyright\": \"(c) $(date +'%Y') scirexs\"," ./src-tauri/tauri.conf.json
sed -i '/"targets": "all"/ a \    "category": "Utility",' ./src-tauri/tauri.conf.json

sed -i "/^name = \"app\"/ s/app/$1/" ./src-tauri/Cargo.toml
sed -i '/^description = "A Tauri App"/ s/A Tauri App/My tauri application/' ./src-tauri/Cargo.toml
sed -i '/^authors = \["you"\]/ s/you/scirexs/' ./src-tauri/Cargo.toml
sed -i '/^license = ""/ s/""/"MIT"/' ./src-tauri/Cargo.toml
sed -i "/^repository = \"\"/ s#\"\"#\"https://github.com/scirexs/$1\"#" ./src-tauri/Cargo.toml
cat << EOF >> ./src-tauri/Cargo.toml

[profile.dev]
incremental = true

[profile.release]
codegen-units = 1
lto = true
opt-level = "s"
panic = "abort"
strip = true
EOF

# Setup git
cat << EOF > ./.gitignore
# https://github.com/github/gitignore/blob/main/Node.gitignore
logs
*.log
npm-debug.log*
report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
pids
*.pid
*.seed
*.pid.lock
build/Release
node_modules/
*.tsbuildinfo
.npm
.eslintcache
.stylelintcache
.rpt2_cache/
.rts2_cache_cjs/
.rts2_cache_es/
.rts2_cache_umd/
.node_repl_history
*.tgz
.env
.env.*
!.env.example
!.env.test
.vscode-test
# Svelte
.output
.vercel
/.svelte-kit
/build
.DS_Store
Thumbs.db
vite.config.js.timestamp-*
vite.config.ts.timestamp-*
# Rust
debug/
target/
Cargo.lock
**/*.rs.bk
EOF
git config --global user.name "scirexs"
git config --global user.email "scirexs@example.com"
git config --global init.defaultBranch main
git init
git add --all
git commit -m "Initial commit."
git remote add origin https://github.com/scirexs/$1.git
git fetch origin main
git rebase origin/main
git push -u origin main
git config pull.ff only

# copy svelte components to ./src/lib

参考文献

Discussion