ブログを作って色々やってみる
開発しているプロジェクトでCI/CDの実行時間(特にVRT、E2E)がボトルネックになっているので、ブログを作ってCI/CDの改善の練習などする。
既存のプロフィールサイトにブログ機能を追加する。
Nuxt.js で作っていたが勉強を兼ねて React.js, vite に書き換える。
コンポーネントを元々 .tsx
で書いていたので移行は大変ではなかった。(そもそも分量が少ない)
普段の業務で React.js で開発していないので知らないことが多い。
React 17 からコンポーネントのファイルで React
をインポートする必要がなくなったらしい。
eslint-plugin-react
を使用しているとエラーが発生するため下記のルールをオフにする。
"rules": {
"react/jsx-uses-react": "off",
"react/react-in-jsx-scope": "off"
}
package.json で "type": "module"
を指定しているので ESLint などの設定ファイルは拡張子を .cjs
にする。
vite でのエイリアスの解決に vite-tsconfig-paths というライブラリをインストールする。
vite, vitest, storybook の設定ファイルで利用する。
vitest で describe
, test
, expected
などを毎回インストールしないために vitest の設定を調整する。
/// <reference types="vitest" />
/// <reference types="vite/client" />
import { defineConfig } from 'vitest/config';
export default defineConfig({
...
test: {
...
globals: true, // describe などを毎回インポートしないようにする
},
});
tsconfig.json
で vitest の型定義を読み込む。
{
"compilerOptions": {
...
"types": ["vitest/globals"]
},
}
storybook でエイリアスが解決できないので .storybook/main.cjs
に設定を追加する。
webpack と同じように viteFinal
で vite の設定を上書きできる。
const path = require('path');
const tsconfigPaths = require('vite-tsconfig-paths').default;
module.exports = {
...
async viteFinal(config) {
config.plugins.push(
tsconfigPaths({
projects: [path.resolve(path.dirname(__dirname), 'tsconfig.json')],
})
);
return config;
},
};
React 18 から ReactDOM.render
はサポートされないため createRoot
に書き換える。
個人開発なので Storybook を alpha の 7 に上げてみる。
リリースノート
マイグレーションガイド
Storybook の start-storybook
と build-storybook
バイナリが削除され CLI に dev
と build
コマンドが追加された。CLI をプロジェクトにインストールする必要が出てきた。
6.x まで
{
"scripts": {
"storybook": "start-storybook <some flags>",
"build-storybook": "build-storybook <some flags>"
}
}
7.x から
{
"scripts": {
"storybook": "storybook dev <some flags>",
"build-storybook": "storybook build <some flags>"
},
"devDependencies": {
"storybook": "next"
}
}
以下のコマンドで自動で Storybook のマイグレーションをしてくれる。
$ npx sb@next automigrate
Storybook の 6.4 で追加された任意のオプション frameworks
が 7.x から必須になる。
対象によってはフレームワークとビルドツールを組み合わせたパッケージが必要になる。
@storybook/react
は引き続き必要なのでアンインストールしない)
React.js と vite のマイグレーションの例($ npm i -D @storybook/react-vite@next
RFC
Storybook 7.x から ComponentStory
, ComponentMeta
などの型定義が非推奨になった。
代わりに StoryObj
(CSF 3.x), StoryFn
(CSF 2.x) を利用する。
Storybook のフォーマットである CFS(Component Story Format)は 3.x から拡張性が高くなり、他のテストツールでも流用することで Story の資産としての価値が上がるので 3.x を利用する。
vite-plugin-eslint と Storybook 7.x を併用するとエラーが発生する。
vite-plugin-eslint で仮想ファイル(/virtual:/
)を Lint しようとするために起こるらしい。
vite-plugin-eslint の設定に exclude: /virtual:/
を追加することで仮想ファイルを Lint の対象から外すことで解消できる。
参考にした Issue では exclude
に Regexp
を指定しているが、型定義が string | string[]
なので一時的に @ts-ignore
して適応。
v1.8.0 から exclude
の型定義が string | string[]
に変更されているので string
に修正。
import { defineConfig } from 'vite';
import eslint from 'vite-plugin-eslint';
export default defineConfig({
plugins: [
...
eslint({ exclude: '**/virtual:/**' }),
]
};
monorepo にするとルートディレクトリの node_modules 配下を Lint しようとするので '**/node_modules/**'
を記述する。
関連 Issue
修正の commit
Storybook のバージョン、E2E テストツールの違い(Puppeteer, Playwright)、直列・並列実行による VRT のパフォーマンスの違いをわかりやすくするために monorepo にする。(先にやればよかった)
Story を元に VRT を実装するため @storybook/addon-storyshots
と @storybook/addons-storyshots-puppeteer
をインストールして動作確認したが、 @storybook/addon-storyshots
のテストランナーが jest なので動かなかった。
Story を元に vitest で VRT を実装する方法を探していたら下記の記事が見つかったので、これを実践してみる。(ありがとうございます🙏)
package.json を "mode": "import"
で開発していたが contentful-typescript-codegen の設定ファイルが .cjs に対応していなかったので vue-tsc を参考に設定ファイルのパスを変更して .cjs で動作するように調整。
(ついでにファイルパスも任意のものに調整。)
#!/usr/bin/env node
const fs = require('fs');
const readFileSync = fs.readFileSync;
const libPath = require.resolve(
'contentful-typescript-codegen/dist/contentful-typescript-codegen'
);
fs.readFileSync = (...args) => {
if (args[0] === libPath) {
let lib = readFileSync(...args);
lib = lib.replace(
`./getContentfulEnvironment.js`,
`./config/contentful/typescript-codegen/config.cjs`
);
return lib;
}
return readFileSync(...args);
};
require(libPath);
- monorepo でパッケージを分ける。
- infrastructure パッケージを設けて外部とのやり取りを集約する。
- 抽象に依存させることで、メインとなる web パッケージから外部サービスを意識しないようにする。
- ローカルでは CMS の環境変数も infrastructure パッケージで管理している。
- 現状、CMS の環境をアプリケーションやローカル・本番環境などで分けることを想定していないため。
- CMS に Contentful を利用する。
-
関連パッケージ で API クライアントが作成できる以下の理由から採用せず fetch を採用する。(型定義は利用する。)
- axios に依存しているようなので、不用意に依存パッケージを増やさないため。
- なぜか MSW でリクエストをキャッチできなかったため。
-
関連パッケージ で API クライアントが作成できる以下の理由から採用せず fetch を採用する。(型定義は利用する。)
- ESLint
-
'@typescript-eslint/consistent-type-imports': 'error'
で実態と型の import を分ける。 -
'no-restricted-imports': ['error', { patterns: ['..*'] }]
で親ディレクトリへの参照をさせない。-
"@/*": ["./src/*"]
でエイリアスを指定しているので、モジュールの参照方法を統一するため。- なぜエイリアスの値が配列なのか気になった。
- テストファイル、Storybook のファイルにはルールを適用しない。
-
-
- src 配下に
vite-end.d.ts
がないことで React のライブラリ化につまづいた。- tsconfig.json で
.d.ts
ファイルを除外していることも問題だった。
- tsconfig.json で
/// <reference types="vite/client" />
GitHub Actions の composite action で secrets を利用する方法
GitHub Actions の composite action では secrets を参照出来ない仕様のようなので inputs で受け取るようにする。
最終的には env を定義して inputs を利用せずに secrets を利用した。
https://github.com/ytk6565/ytk6565.net/tree/e36e125425dbf799821094ca79f68528f4ba303e/.github
参考
-
Context availability
- GitHub Actions の Workflow Key に対して参照できる Context が掲載されているドキュメント
-
secrets context
- セキュリティの観点から composite actions では secrets が利用できないとの記載がある。
- 最初はドキュメントにそれらしき記述が見当たらなかったので Stackoverflow の質問・回答を参考にした。
NG
jobs:
build:
steps:
- uses: ./.github/actions/example
runs:
using: 'composite'
steps:
- run: echo "${{ env.ACCESS_TOKEN }}"
env:
# secrets を参照出来ないためエラーが発生する。
ACCESS_TOKEN: ${{ secrets.ACCESS_TOKEN }}
OK
secrets が渡っているときは、echo の出力が "***" になる。
jobs:
build:
steps:
- uses: ./.github/actions/example
# composite actions に secrets を渡す。
with:
ACCESS_TOKEN: ${{ secrets.ACCESS_TOKEN }}
# workflow から secrets を受け取る。
inputs:
ACCESS_TOKEN:
description: Access token
required: true
runs:
using: 'composite'
steps:
- run: echo "${{ env.ACCESS_TOKEN }}"
env:
# inputs を参照する。
ACCESS_TOKEN: ${{ inputs.ACCESS_TOKEN }}
Vercel の自動デプロイ時に .github/ 配下の .yml ファイルの secrets が参照できずエラーが発生したので、GitHub Actions から Vercel にデプロイするように変更。
vite と rollup-plugin-visualizer で依存する rollup のパージョンが異なっていたため pnpm の overrides でバージョンを指定してインストール。
yarn の resolutions と同様の機能
{
"pnpm": {
"overrides": {
"rollup": "2.79.1"
}
}
}
ライブラリ用のパッケージのビルド時に src 配下の構造のままビルドしたいが vite (rollup) だと難しいそうだったので tsup (esbuild) を利用する。
tsup はバンドル( esbuild )と型( rollup )は異なる環境でビルドしていて、ビルドの設定が複雑になったので、バンドルは esbuild で型は tsc で行うことにした。