$shibayu36->blog;

クラスター株式会社のソフトウェアエンジニアです。エンジニアリングや読書などについて書いています。

yarn workspaceを使っている時でもvscodeでeslintプラグインが動くようにする

例えばyarn workspaceを使って、

  • packages/hoge/
  • packages/fuga/

のようにワークスペースを二つに分けて開発したいときがある。この時、ワークスペースごとでeslintの設定やインストールするpackageが分かれてしまっているのでESLintプラグインがうまく動かない。

この場合でもうまく動かすにはeslint.workingDirectoriesを設定すると良い。そのプロジェクトの.vscode/settings.jsonに以下のように設定しておくと良いだろう。

{
  // TypeScriptでもeslintが動くように
  "eslint.validate": [
    "javascript",
    "javascriptreact",
    "typescript",
    "typescriptreact"
  ],
  // yarnを使う
  "eslint.packageManager": "yarn",
  // workspaceを指定する。CWDも変えたい。
  "eslint.workingDirectories": [
    { "directory": "./packages/hoge", "changeProcessCWD": true },
    { "directory": "./packages/fuga", "changeProcessCWD": true },
  ]
}

Next.jsのpages/ディレクトリ以下にはページ用のファイル以外は置いてはならない

Next.jsではpages/ディレクトリ以下にページ用の実装ファイルを置く規約となっているが、それに追加して、このディレクトリ以下にはページ用以外のファイルは置いてはならないということが分かった。ページ用以外のファイルとは、例えばテストのファイルや共通ロジックのための実装などである。

ファイルを置くことで起こる問題と起こる理由

例えば日記サービスを考えたとして、pages/diaries.tsxのような日記一覧のページを実装するファイルがあったとする。この時、テストファイルはJestの流儀にしたがって、pages/tests/diaries.test.tsxもしくはpages/diaries.test.tsxに置きたくなる。

しかし、この時、次の問題が起こってしまう

  • Next.jsはテストファイルもページ用の実装とみなし、next build時に成果物に含めてしまう
    • .next/server/static/jfklanfkealknl/pages/diaries.test.js のようなファイルができる
  • ルーティングも機能してしまい、例えば/diaries.testのようなURLにアクセスできてしまう。ページの実装がないのでISEが返る

このようになる理由はNext.jsがpages/以下の全てのファイルをentrypointとしてwebpackでビルドしてしまうためである(このへんこのへんを参照)。webpackのentrypointとして指定されているので、next.config.jsのwebpackの設定でIgnorePluginなどを入れたとしても解決しない。

解決策

https://github.com/zeit/next.js/issues/3728 で色々と議論されている。少なくともpagesGlobPatternのような設定や、excludePatternのようなものは提供しない方向となっていそう。

解決策としては例えば以下のようなパターンがありえる。

いろいろ検討したけど、テストのために本番用のコード側を変えすぎるのは避けたい(本番用のロジックでsymlink作ったりしたくない)と考え、__pages_tests__ディレクトリを別で切ってテストを配置することにした。__pages_tests__というディレクトリ名にしたのは、pages/以下のテストしか置かないということを明示したかったためである。

最終形

pages/diaries.tsx
__pages_tests__/diaries.test.tsx

MySQLのALTER TABLEでTEXT NOT NULLなカラムをエラー無しで追加する

課題

MySQL :: MySQL 5.6 リファレンスマニュアル :: 11.6 データ型デフォルト値によると、

  • MySQLのTEXTカラムにはデフォルト値を設定できない
  • 厳密な SQL モードを有効にした場合、NOT NULLなカラムがINSERTに含まれていないとエラーが発生する

そのため、以下のようなスキーマが存在していて

CREATE TABLE `article` (
  `id` BIGINT UNSIGNED NOT NULL,
  `title` VARCHAR(100) NOT NULL,
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

アプリケーションから次のようにINSERT文を発行しているとして

INSERT INTO `article`
  SET id = :id,
      title = :title
  • 先にALTER TABLE article ADD COLUMN body TEXT NOT NULLすると、INSERT文がエラーになってしまう
  • 先にアプリケーションをデプロイして、INSERTのSETにbodyを追加しても、存在しないカラムへのセットになってしまい、INSERT文がエラーになってしまう

という風に、ALTER TABLEが先でもアプリケーションデプロイが先でもエラーが発生してしまう。

解決策

先にDEFAULT句付きのVARCHARなカラムを追加し、デプロイしてINSERT句を直した後、TEXTカラムに変更すると良い。

  • ALTER TABLE article ADD body VARCHAR(100) NOT NULL DEFAULT '';
  • アプリケーションをデプロイして、SETにbodyを追加する
  • ALTER TABLE article MODIFY body TEXT NOT NULL;

こうするとエラーを起こさずにALTER TABLEが成功した。

補足: 全部VARCHARでいいんじゃないか問題

varcharとtextの違い(mysql innodb) - lxyuma BLOGのように、全部VARCHARで良いんじゃないかという話題がある。しかし、https://dev.mysql.com/doc/refman/5.6/ja/column-count-limit.html によると、

  • すべてのテーブル (ストレージエンジンには無関係) の最大行サイズは 65,535 バイトです
  • すべてのカラムの合計長は最大行サイズを超えられないので、このサイズはカラム数 (およびサイズの可能性もあり) を制約します
  • BLOB カラムと TEXT カラムは、その内容が行の残りとは別に格納されるので、行サイズに対してそれぞれ 9 から 12 (1から4+8) バイトになります。

ということで、VARCHARで長い文字列長を使うと一気に行サイズを使い果たして死にそう。