blog/content/posts/2026-01-31/index.md

621 lines
16 KiB
Markdown
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

---
author: usbharu
draft: false
categories:
- 技術
date: 2026-02-01T13:07:30+09:00
tags:
- pnpm
- npm
- Tauri
- Next.js
- Biome
- Storybook
- Changesets
- TypeScript
- Renovate
- Vitest
keywords:
- pnpm
- npm
- Tauri
- Next.js
- Biome
- Storybook
- Changesets
- TypeScript
- Renovate
- Vitest
title: プロジェクトを作るときに考えたいこと
relpermalink: posts/2026-01-31/
url: posts/2026-01-31/
decription: プロジェクトを作るときに考えたいこと
---
何か新しいものを作ろうとしたときに、毎回調べている気がするのでまとめてみた。
最近はWeb(Next.js)Desktop(Tauri+Vite+React)で作ることが多いので、なんだかんだ偏っているけどnpm使うなら使えるはず
## プロジェクトの構造を作る
基本的にpnpmを使う
```bash
pnpm init --init-type module
```
モノレポでやる場合は`pnpm-workspace.yaml`を追加する
[pnpm-workspace.yamlの構文](https://pnpm.io/ja/next/pnpm-workspace_yaml)
大体こんな感じのディレクトリ構造でやることが多い desktopとwebのフォルダは`pnpm create ほにゃらら`が作るので自分では作らない
```bash
tree .
.
├── apps
│ ├── desktop
│ └── web
└── packages
├── ui
└── utils
7 directories, 0 files
```
### Next.js
こんな感じで作成する
ESLintではなくBiomeを使う
```bash
create next-app@latest web
✔ Would you like to use the recommended Next.js defaults? No, customize settings
✔ Would you like to use TypeScript? … No / Yes
✔ Which linter would you like to use? Biome
✔ Would you like to use React Compiler? … No / Yes
✔ Would you like to use Tailwind CSS? … No / Yes
✔ Would you like your code inside a `src/` directory? … No / Yes
✔ Would you like to use App Router? (recommended) … No / Yes
✔ Would you like to customize the import alias (`@/*` by default)? … No / Yes
✔ What import alias would you like configured? … @/*
```
`pnpm-workspace.yaml`と`biome.json`が作成されるが、プロジェクトルートで作成するので必要に応じて削除する。
### Tauri
Rustがインストールされていることを確認する
```bash
pnpm create tauri-app
✔ Project name · desktop
✔ Identifier · dev.usbharu.test
✔ Choose which language to use for your frontend · TypeScript / JavaScript - (pnpm, yarn, npm, deno, bun)
✔ Choose your package manager · pnpm
✔ Choose your UI template · React - (https://react.dev/)
✔ Choose your UI flavor · TypeScript
```
### TypeScript
```bash
pnpm add -D typescript
npx tsc --init
```
npm scriptにtypecheckを追加しておく
正直いらないが… AIが利用できるコマンドを制限するときに楽
```json
"typecheck": "tsc --noEmit"
```
### Vite(ライブラリモード)
npmに公開するかはともかくライブラリを作るときはViteのライブラリモードを利用するといいらしい やったことない
https://ja.vite.dev/guide/build#library-mode
単にモレポの1パッケージとして作るだけなら不要
---
pnpmでモレポ内のパッケージを追加する場合
```bash
pnpm add --workspace @test/ui
```
npmではやったことないけど、バージョンカタログの仕組みであるpnpm catalogで管理するというのもいいかも
https://pnpm.io/ja/catalogs
## テストとか諸々
### Biome
Biome君VSCodeの拡張機能がちょっと残念だけど基本的に優秀なので使ってます
https://biomejs.dev/ja/guides/getting-started/#%E8%A8%AD%E5%AE%9A
```bash
pnpm add -w -D @biomejs/biome
pnpm exec biome init
```
- JSONスキーマをURLからnode_modules内のファイルに変える
- 自動的にインストールされているバージョンのスキーマを使うことができる
- useIgnoreFile
- *.d.tsファイルの除外
- tailwindcss対応
```json
{
"$schema": "./node_modules/@biomejs/biome/configuration_schema.json",
"vcs": {
"enabled": true,
"clientKind": "git",
"useIgnoreFile": true
},
"files": {
"ignoreUnknown": true,
"includes": ["**", "!**/*.d.ts"]
},
"formatter": {
"enabled": true,
"indentStyle": "space",
"indentWidth": 2
},
"linter": {
"enabled": true,
"rules": {
"recommended": true,
"a11y": {
"noStaticElementInteractions": "off"
},
"nursery": {
"useSortedClasses": {
"fix": "safe",
"level": "error",
"options": {
"functions": ["twMerge", "twJoin", "tv", "cn"]
}
}
}
}
},
"javascript": {
"formatter": {
"quoteStyle": "double"
}
},
"css": {
"parser": {
"cssModules": true,
"allowWrongLineComments": true,
"tailwindDirectives": true
},
"formatter": {
"enabled": false
},
"linter": {
"enabled": false
}
},
"assist": {
"enabled": true,
"actions": {
"source": {
"organizeImports": "on"
}
}
}
}
```
npm scriptにlintとlint:fixを追加しておく
--unsafeオプションはご自由に 僕はつけます
```json
"scripts": {
"lint": "biome check",
"lint:fix" : "biome check --write --unsafe"
},
```
### Vitest
テストにはVitestを使う 正直そんなに使ったことない
```bash
pnpm add -D vitest
```
npm scriptにtestを追加する
```json
"scripts": {
"test": "vitest"
},
```
### Storybook
AIと色々やるときにあるとすごい便利
ReactとかのUIライブラリが依存関係に入っていないと自動作成に失敗するみたいなので注意
```bash
pnpm create storybook@latest
```
あとは画面の指示に従うだけ
Tailwindcssの設定もしておく
https://pystyle.info/react-vite-tailwind-storybook/
### Storycap
ビジュアルリグレッションテストを簡単に行えるよう、コンポーネントのスクショを自動で取れるようにしておく
AIにUIコンポーネントを触らせるときにちょっとだけ安心できる
Storycapとあるが正確には改良版のStorycap-testrunというもの
```bash
pnpm add -D @storycap-testrun/browser
```
vitest.setup.tsに以下を追記
```typescript:vitest.setup.ts
import { page } from "vitest/browser";
import { screenshot } from "@storycap-testrun/browser";
afterEach(async (context) => {
await screenshot(page, context);
});
```
vitest.config.tsのプラグインに以下を追記
```typescript:vitest.config.ts
import storycap from "@storycap-testrun/browser/vitest-plugin";
storycap({
output: {
file: (context) => `${context.id}.png`,
},
}),
```
`.gitignore`に`__screenshots__`と書いておくと良い
## Git/GitHubとCI/CD
基本的にGit Flowをちょっと省略したブランチ戦略を取る
`release`ブランチを省略(リリース作業は自動化、それ以外はdevelopでするので)
`hotfix`ブランチも省略
### Gitの設定
やっていなければリポジトリの初期化 masterとmainに注意(n敗)
```bash
git init .
```
`.gitignore`の設定
https://github.com/github/gitignore
こういうところから持ってくる
### コミット前に自動修正
lefthookとbiomeを組み合わせる
```bash
pnpm add -D -w lefthook
```
lefthook.yamlを作成する
```yaml:lefthook.yaml
pre-commit:
commands:
check:
glob: "*.{js,ts,cjs,mjs,d.cts,d.mts,jsx,tsx,json,jsonc}"
run: npx @biomejs/biome check --write --no-errors-on-unmatched --files-ignore-unknown=true --colors=off {staged_files}
stage_fixed: true
pre-push:
commands:
check:
glob: "*.{js,ts,cjs,mjs,d.cts,d.mts,jsx,tsx,json,jsonc}"
run: npx @biomejs/biome check --no-errors-on-unmatched --files-ignore-unknown=true --colors=off {push_files}
```
適用
```bash
pnpm lefthook install
```
### GitHub
とりあえずリポジトリを作る。masterとmainに注意 上でやった`.gitignore`はここでやってもいい
Renovateとかを有効化しておく
設定していく
- オートマージを許可
- デフォルトブランチをdevelopに
- プルリクエストブランチの更新を常に提案する
- ヘッドブランチを自動的に削除する
- Dependabot Alertsを有効にする
- ブランチプロテクションルールを作成する CIの構築をしてからの方が楽
- Masterブランチは直Push禁止
- CI全Passが必須
- バイパス禁止
- Developブランチは直Push許可
- CI全Passが必要だがバイパスを許可
- レビューが必要なら最少Approve数を設定
- チーム開発ならWebhookを設定
- Issue追加・PR追加・リリースあたりを通知してスレッド運用が良さげ 正直SlackのGitHub Appが一番いい
- Discord https://mekurun.com/tips/discord-github/
### Renovate
依存ライブラリのバージョン管理は自動化する
- minorとpatchの自動マージを有効化
- platformAutomergeを有効化
- rebaseWhenをconflictedかneverに
- lockFileMaintenanceをtrueに 正直いらんかも
```json:renovate.json
{
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
"extends": ["config:recommended"],
"packageRules": [
{ "matchUpdateTypes": ["minor", "patch"], "automerge": true }
],
"platformAutomerge": true,
"rebaseWhen": "conflicted",
"lockFileMaintenance": {
"enabled": true,
"recreateWhen": "always",
"rebaseStalePrs": true,
"branchTopic": "lock-file-maintenance",
"commitMessageAction": "Lock file maintenance",
"commitMessageTopic": null,
"commitMessageExtra": null,
"schedule": ["before 4am on monday"],
"groupName": null,
"prBodyDefinitions": {
"Change": "All locks refreshed"
}
}
}
```
### Vitest
package.json内に存在している不要なtestスクリプトは削除しておく
ルートのpackage.jsonに
```json
"test:all": "pnpm -r --reporter-hide-prefix test"
```
を追記しておく
-rオプションで子プロジェクトでも再起的に実行
--reporter-hide-prefixオプションで後述するテストレポートの邪魔を防げる
簡単にテストレポートを手に入れる手段としてGitHubのアテーションを使う
https://vitest.dev/guide/reporters.html#github-actions-reporter
### VRT(ビジュアルリグレッションテスト)
AIにUIを触らせるならあったらすごく便利です
`reg-viz/reg-actions@v3`を使います。
playwrightとのインストールが必要な割に実行する必要があるタイミングが多く、コストが重め
PRのコメントに差分を投稿してくれるけど、PRのマージ先のコミットで必ずVRTが実行されている必要があるため、マージコミットにもVRTのアクションを実行する必要がある(この仕様にめちゃくちゃハマった)
残っている最新のスクショ結果ではなく、最新のコミットのスクショを探すという点に注意
簡単に書くのが凄く難しいので今動いているActionをそのまま書いておきます
`@travel-scheduler/ui`の部分をUIコンポーネントのパッケージ名に変えると動くと思います。
```yaml
name: Visual Regression Test
on:
push:
branches: [develop, main]
pull_request:
branches: [develop, main]
permissions:
contents: write
actions: write
pull-requests: write
jobs:
test:
timeout-minutes: 10
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
shard: [1/3, 2/3, 3/3]
steps:
- uses: actions/checkout@v6
- uses: pnpm/action-setup@v4
- uses: actions/setup-node@v6
with:
node-version: 22
cache: "pnpm"
- name: Install dependencies
run: pnpm install
- name: Cache Playwright binaries
uses: actions/cache@v5
id: playwright-cache
with:
path: ~/.cache/ms-playwright
key: ${{ runner.os }}-playwright-${{ hashFiles('**/pnpm-lock.yaml') }}
- name: Install Playwright Browsers
if: steps.playwright-cache.outputs.cache-hit != 'true'
run: pnpm -F @travel-scheduler/ui exec playwright install chromium
- name: Install Playwright system dependencies
run: pnpm -F @travel-scheduler/ui exec playwright install-deps chromium
- name: Run storycap tests (shard ${{ matrix.shard }})
run: pnpm -F @travel-scheduler/ui exec vitest run --project=storybook --shard=${{ matrix.shard }}
- name: Upload screenshots
uses: actions/upload-artifact@v6
if: always()
with:
name: screenshots-${{ strategy.job-index }}
path: packages/ui/__screenshots__
retention-days: 1
compare:
needs: test
if: always()
timeout-minutes: 5
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- name: Download all screenshot artifacts
uses: actions/download-artifact@v7
with:
pattern: screenshots-*
path: downloaded-screenshots
- name: Merge screenshots
run: |
mkdir -p packages/ui/__screenshots__
for dir in downloaded-screenshots/screenshots-*; do
if [ -d "$dir" ]; then
cp -r "$dir"/* packages/ui/__screenshots__/ 2>/dev/null || true
fi
done
echo "Merged screenshots:"
ls -la packages/ui/__screenshots__/
- name: Upload merged screenshots
uses: actions/upload-artifact@v6
with:
name: storycap-report
path: packages/ui/__screenshots__
retention-days: 30
- name: Run visual regression comparison
uses: reg-viz/reg-actions@v3
with:
github-token: "${{ secrets.GITHUB_TOKEN }}"
image-directory-path: "./packages/ui/__screenshots__"
```
### Changesetsによるバージョン管理
自分は調べながら初めて使ったけど、とても便利そう
インストール
```bash
pnpm add -wD @changesets/cli
```
初期化
```bash
npx changesets init
```
変更を追記(GitHub Appで自動化可能)
```bash
npx changesets
```
追記した変更をまとめてリリース(GitHub Actionで自動化可能)
```bash
npx changesets version
```
変更の追記の確認をしてくれるBot
https://github.com/apps/changeset-bot
masterにpushがあったら自動でリリース(changesets version)を行うAction
```yaml
name: Release
on:
push:
branches:
- master
concurrency: ${{ github.workflow }}-${{ github.ref }}
jobs:
release:
name: Release
permissions:
pull-requests: write
contents: write
runs-on: ubuntu-latest
steps:
- name: Checkout Repo
uses: actions/checkout@v3
- uses: pnpm/action-setup@v4
with:
run_install: false
- name: Setup Node.js 20
uses: actions/setup-node@v3
with:
node-version: 22
- name: Install Dependencies
run: pnpm install
- name: Create Release Pull Request
uses: changesets/action@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
```
### npm versionによるバージョン管理
npmではやったことないけど、Gradleでは近い方法でバージョン管理をしていた 全パッケージが同じバージョン番号を持つプロジェクトやモレポじゃないプロジェクトではGitに全部を合わせることができるので楽
https://qiita.com/minamo173/items/8b8b27bc6ecd17ad925e
masterブランチにtagがpushされたときにfrom-gitで発動させる
### GitHub Copilotによるレビューを行う
自動でレビューさせるかはともかく設定する
https://github.com/settings/copilot/features
で有効化できる
`.github/copilot-instructions.md`に日本語で頼む的な指示を書いておきましょう。
---
AI関連のことも書こうかと思ったけどだいぶ長い記事になったので一旦ここまで