Closed24

ブログを作って色々やってみる

Tashiro YutakaTashiro Yutaka

開発しているプロジェクトでCI/CDの実行時間(特にVRT、E2E)がボトルネックになっているので、ブログを作ってCI/CDの改善の練習などする。

Tashiro YutakaTashiro Yutaka

Nuxt.js で作っていたが勉強を兼ねて React.js, vite に書き換える。
コンポーネントを元々 .tsx で書いていたので移行は大変ではなかった。(そもそも分量が少ない)
普段の業務で React.js で開発していないので知らないことが多い。

Tashiro YutakaTashiro Yutaka

package.json で "type": "module" を指定しているので ESLint などの設定ファイルは拡張子を .cjs にする。

Tashiro YutakaTashiro Yutaka

vite でのエイリアスの解決に vite-tsconfig-paths というライブラリをインストールする。
vite, vitest, storybook の設定ファイルで利用する。

Tashiro YutakaTashiro Yutaka

vitest で describe , test , expected などを毎回インストールしないために vitest の設定を調整する。

vitest.config.ts
/// <reference types="vitest" />
/// <reference types="vite/client" />

import { defineConfig } from 'vitest/config';

export default defineConfig({
  ...
  test: {
    ...
    globals: true, // describe などを毎回インポートしないようにする
  },
});

tsconfig.json で vitest の型定義を読み込む。

tsconfig.json
{
  "compilerOptions": {
    ...
    "types": ["vitest/globals"]
  },
}
Tashiro YutakaTashiro Yutaka

storybook でエイリアスが解決できないので .storybook/main.cjs に設定を追加する。
webpack と同じように viteFinal で vite の設定を上書きできる。

.storybook/main.cjs
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;
  },
};
Tashiro YutakaTashiro Yutaka

Storybook の start-storybookbuild-storybook バイナリが削除され CLI に devbuild コマンドが追加された。CLI をプロジェクトにインストールする必要が出てきた。

6.x まで

package.json
{
  "scripts": {
    "storybook": "start-storybook <some flags>",
    "build-storybook": "build-storybook <some flags>"
  }
}

7.x から

package.json
{
  "scripts": {
    "storybook": "storybook dev <some flags>",
    "build-storybook": "storybook build <some flags>"
  },
  "devDependencies": {
    "storybook": "next"
  }
}
Tashiro YutakaTashiro Yutaka

以下のコマンドで自動で Storybook のマイグレーションをしてくれる。

$ npx sb@next automigrate
Tashiro YutakaTashiro Yutaka

Storybook の 6.4 で追加された任意のオプション frameworks が 7.x から必須になる。
対象によってはフレームワークとビルドツールを組み合わせたパッケージが必要になる。

https://github.com/storybookjs/storybook/blob/next/MIGRATION.md#framework-field-mandatory

React.js と vite のマイグレーションの例(@storybook/react は引き続き必要なのでアンインストールしない)

$ npm i -D @storybook/react-vite@next

RFC

https://www.notion.so/Frameworks-RFC-89f8aafe3f0941ceb4c24683859ed65c

Tashiro YutakaTashiro Yutaka

Storybook 7.x から ComponentStory, ComponentMeta などの型定義が非推奨になった。
代わりに StoryObj(CSF 3.x), StoryFn(CSF 2.x) を利用する。
Storybook のフォーマットである CFS(Component Story Format)は 3.x から拡張性が高くなり、他のテストツールでも流用することで Story の資産としての価値が上がるので 3.x を利用する。

https://github.com/storybookjs/storybook/blob/next/MIGRATION.md#componentstory-componentstoryobj-componentstoryfn-and-componentmeta-types-are-deprecated

Tashiro YutakaTashiro Yutaka

vite-plugin-eslint と Storybook 7.x を併用するとエラーが発生する。
vite-plugin-eslint で仮想ファイル(/virtual:/)を Lint しようとするために起こるらしい。
vite-plugin-eslint の設定に exclude: /virtual:/ を追加することで仮想ファイルを Lint の対象から外すことで解消できる。

参考にした Issue では excludeRegexp を指定しているが、型定義が string | string[] なので一時的に @ts-ignore して適応。
v1.8.0 から exclude の型定義が string | string[] に変更されているので string に修正。

vite.config.ts
import { defineConfig } from 'vite';
import eslint from 'vite-plugin-eslint';

export default defineConfig({
  plugins: [
    ...
    eslint({ exclude: '**/virtual:/**' }),
  ]
};

monorepo にするとルートディレクトリの node_modules 配下を Lint しようとするので '**/node_modules/**' を記述する。

関連 Issue

https://github.com/storybookjs/builder-vite/issues/367

https://github.com/gxmari007/vite-plugin-eslint/pull/29

修正の commit

https://github.com/gxmari007/vite-plugin-eslint/commit/f9aa1124f8fb271779f73eec1d959bcb3d5d5106

Tashiro YutakaTashiro Yutaka

Storybook のバージョン、E2E テストツールの違い(Puppeteer, Playwright)、直列・並列実行による VRT のパフォーマンスの違いをわかりやすくするために monorepo にする。(先にやればよかった)

Tashiro YutakaTashiro Yutaka

Story を元に VRT を実装するため @storybook/addon-storyshots@storybook/addons-storyshots-puppeteer をインストールして動作確認したが、 @storybook/addon-storyshots のテストランナーが jest なので動かなかった。
Story を元に vitest で VRT を実装する方法を探していたら下記の記事が見つかったので、これを実践してみる。(ありがとうございます🙏)

https://zenn.dev/sterashima78/articles/47c9a109988b3c

Tashiro YutakaTashiro Yutaka

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);

Tashiro YutakaTashiro Yutaka
  • monorepo でパッケージを分ける。
  • infrastructure パッケージを設けて外部とのやり取りを集約する。
    • 抽象に依存させることで、メインとなる web パッケージから外部サービスを意識しないようにする。
    • ローカルでは CMS の環境変数も infrastructure パッケージで管理している。
      • 現状、CMS の環境をアプリケーションやローカル・本番環境などで分けることを想定していないため。
  • CMS に Contentful を利用する。
    • 関連パッケージ で API クライアントが作成できる以下の理由から採用せず fetch を採用する。(型定義は利用する。)
      • axios に依存しているようなので、不用意に依存パッケージを増やさないため。
      • なぜか MSW でリクエストをキャッチできなかったため。
  • ESLint
    • '@typescript-eslint/consistent-type-imports': 'error' で実態と型の import を分ける。
    • 'no-restricted-imports': ['error', { patterns: ['..*'] }] で親ディレクトリへの参照をさせない。
      • "@/*": ["./src/*"] でエイリアスを指定しているので、モジュールの参照方法を統一するため。
        • なぜエイリアスの値が配列なのか気になった。
      • テストファイル、Storybook のファイルにはルールを適用しない。
Tashiro YutakaTashiro Yutaka
  • src 配下に vite-end.d.ts がないことで React のライブラリ化につまづいた。
    • tsconfig.json で .d.ts ファイルを除外していることも問題だった。
/// <reference types="vite/client" />
Tashiro YutakaTashiro Yutaka

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

.github/workflows/example.yml
jobs:
  build:
    steps:
      - uses: ./.github/actions/example
.github/actions/example.yml
runs:
  using: 'composite'
  steps:
    - run: echo "${{ env.ACCESS_TOKEN }}"
      env:
        # secrets を参照出来ないためエラーが発生する。
        ACCESS_TOKEN: ${{ secrets.ACCESS_TOKEN }}

OK

secrets が渡っているときは、echo の出力が "***" になる。

.github/workflows/example.yml
jobs:
  build:
    steps:
      - uses: ./.github/actions/example
        # composite actions に secrets を渡す。
        with:
          ACCESS_TOKEN: ${{ secrets.ACCESS_TOKEN }}
.github/actions/example.yml
# 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 }}
Tashiro YutakaTashiro Yutaka

vite と rollup-plugin-visualizer で依存する rollup のパージョンが異なっていたため pnpm の overrides でバージョンを指定してインストール。

yarn の resolutions と同様の機能

package.json
{
  "pnpm": {
    "overrides": {
      "rollup": "2.79.1"
    }
  }
}
Tashiro YutakaTashiro Yutaka

ライブラリ用のパッケージのビルド時に src 配下の構造のままビルドしたいが vite (rollup) だと難しいそうだったので tsup (esbuild) を利用する

tsup はバンドル( esbuild )と型( rollup )は異なる環境でビルドしていて、ビルドの設定が複雑になったので、バンドルは esbuild で型は tsc で行うことにした。

このスクラップは2024/01/21にクローズされました