dtaniguchi.com

blog - Node.js

yarn から npm に戻した話 ② – package.json 編

d.taniguchi

2023年10月7日 22:26

Post / コメント

  • Node.js
  • npm

前回の続きである。

前回は yarn1 系の yarn install コマンドに、 yarn.lock ファイルが存在する場合 Storybook7 の依存性解決が失敗するというバグがあったため、プロジェクトで使用するコマンドをすべて npm に戻したという話をした。

今回は、npm コマンドに戻したことで、 yarn コマンドでは何事もなかった package.json の scripts プロパティ呼び出しが一部失敗するという問題が発生したので、その話をする。

呼び出しが上手くいかなくなったのは、npm-run-all を使用する下記 package.json の 11 行目の build:stg コマンドと、 12 行目の build:prod コマンドである。これは私が STG 環境と PROD 環境向け Build のために 8 行目の build コマンドを参考に追加したものである。8行目の build コマンドは Vue3 の Scaffolding tool にて Project 生成した当初からあったものだ。

package.json
{
  "name": "vue3",
  "version": "0.0.0",
  "private": true,
  "type": "module",
  "scripts": {
    "dev": "vite",
    "build": "run-p type-check \"build-only {@}\" --",
    "build-only": "vite build",
    "type-check": "vue-tsc --build --force",
    "build:stg": "run-p \"build {@}\" -- --mode stg --sourcemap true",
    "build:prod": "run-p \"build {@}\" -- --mode prod",
    // 省略
  },
  "dependencies": {
    // 省略
  },
  "devDependencies": {
    "npm-run-all": "^4.1.5",
    // 省略
  }
}

yarn だと、正常に動作する。

ところが npm を使用すると失敗する。

vite v5.1.3 building for production...
 0 modules transformed.
x Build failed in 6ms
error during build:
RollupError: Could not resolve entry module "stg/index.html".
    at getRollupEror (file:///Users/work/Desktop/vue3/node_modules/rollup/dist/es/shared/parseAst.js:375:41)
    at error (file:///Users/work/Desktop/vue3/node_modules/rollup/dist/es/shared/parseAst.js:372:42)
    at ModuleLoader.loadEntryModule (file:///Users/work/Desktop/vue3/node_modules/rollup/dist/es/shared/node-entry.js:18928:20)
    at async Promise.all (index 0)
ERROR: "build-only stg" exited with 1.
ERROR: "build --mode stg --sourcemap true" exited with 1.

コマンドに引数を引き渡すための Syntax だと思われる `{@} – -` だが、私は今回のプロジェクトで初めて目にすることになった。これは何物だろう。npm の公式を調べたのだが情報が出てこない。

エラーを眺める限り引数がズレている。それは分かるが、この問題の辛いところはエラー内容が発生している問題を表現していない点にある。キーワードをいろいろと変えて検索を試みたのだが、同じ問題に直面しているページすら発見できなかった。そんなことがあり得るのか?

諦めるのは簡単だ。 STG 環境や PROD 環境向け Build コマンドを 以下のようにしてしまえばいいだけだ。

    "build:stg": "vite build --mode stg --sourcemap true",
    "build:prod": "vite build --mode prod",

しかし、これは敗北である。これでは、型チェックコマンドを別途実行しないといけない。何より折角の build コマンドが再利用できない。再利用できる物は徹底的に再利用するのがプログラマの美徳だ。プロは差分しか書かない。平然と重複コードを許すエンジニアに価値はない。こんなコードは耐えられない。この回避策はあまりに情けない。

ヤケクソ気味になり、ひたすら問題に体当たりしていると、解決方法が判明した。以下のように修正したところ、 npm コマンドで引数が正しく引き渡された。

    "build": "run-p type-check \"build-only {@}\" -- --",
    "build:stg": "run-p \"build {@}\" -- -- --mode stg --sourcemap true",
    "build:prod": "run-p \"build {@}\" -- -- --mode prod",

おわかりだろうか、` – -` をさらにもう一つ追加するのだ。

驚くべきことに、 Vue3 の Scaffolding tool で最初から設定されている build コマンドから間違っていたのである。Vue3 の開発チームはまだ yarn を使っているのかもしれない。

yarn から npm に戻した話 ① – Storybook が起動しない編

d.taniguchi

2023年10月6日 6:19

Post / コメント

  • Node.js
  • npm
  • Storybook

引き続き、Vue3 による新規開発プロジェクトで仕事をしている。

プロジェクト基盤の整備が終わり、コンポーネント開発に着手した。 Composition API でボタンや入力コンポーネントを作成したり、Router に Page コンポーネントと Path の紐付けを追加したり、といった作業を進める。

Vue Router4 に Nested Routes があるのは嬉しかった。今回 Modal 表示を Path で制御したい箇所がある。Next.js の App Router では Layout 作成がしやすかった。この機能は積極的に使いたい。

いくつかコンポーネントができたので、 Storybook7 を導入して作成したコンポーネントを登録することにした。まずは Local 上で Project に Storybook を Install 、正常に起動した。続いて docker-compose.yml にコンテナを追加して起動しようとしたとき問題が発生した。起動しないのである。またかい。Vite が Docker で動作しないに引き続きである。

Dockerfile は以下のような内容だ。

Dockerfile
FROM node:18.18 AS development

ENV LANG C.UTF-8
ENV TZ Asia/Tokyo

RUN apt-get update

WORKDIR /app
COPY ["package.json", "yarn.lock", "./"]

RUN yarn

エラー表示は以下の通り。

storybook  | 🔴 Error: It looks like you are having a known issue with package hoisting.
storybook  | Please check the following issue for details and solutions: https://github.com/storybookjs/storybook/issues/22431#issuecomment-1630086092
storybook  | 
storybook  | 
storybook  | /app/node_modules/cli-table3/src/utils.js:1
storybook  | const stringWidth = require('string-width');
storybook  |                     ^
storybook  | 
storybook  | Error [ERR_REQUIRE_ESM]: require() of ES Module /app/node_modules/string-width/index.js from /app/node_modules/cli-table3/src/utils.js not supported.
storybook  | Instead change the require of index.js in /app/node_modules/cli-table3/src/utils.js to a dynamic import() which is available in all CommonJS modules.
storybook  |     at Object.<anonymous> (/app/node_modules/cli-table3/src/utils.js:1:21)
storybook  |     at Object.<anonymous> (/app/node_modules/cli-table3/src/table.js:2:15)
storybook  |     at Object.<anonymous> (/app/node_modules/cli-table3/index.js:1:18)
storybook  |     at Object.<anonymous> (/app/node_modules/@storybook/core-server/dist/index.js:66:2799)
storybook  |     at Object.<anonymous> (/app/node_modules/@storybook/cli/dist/generate.js:11:4494)
storybook  |     at Object.<anonymous> (/app/node_modules/@storybook/cli/bin/index.js:26:1)
storybook  |     at Object.<anonymous> (/app/node_modules/storybook/index.js:3:1) {
storybook  |   code: 'ERR_REQUIRE_ESM'
storybook  | }

御丁寧なことにエラーに直接 Issue の URL が明記されている。世の中にそんなエラー表示があったのか。

https://github.com/storybookjs/storybook/issues/22431#issuecomment-1630086092

Issue を要約すると次のようになった。

  • yarn ではなく、 pnpm を使用したら動いた
  • yarn ver. 1 系のバグ
  • yarn ver.3 系にアップグレードすると直る
  • yarn.lock がある状態で yarn コマンドを実行すると依存性解決に失敗する
  • package.json だけの状態から yarn コマンドを実行すると依存性解決は成功する

試しに正常動作している Local の node_modules を削除して yarn コマンドを再実行すると、 問題が出現した。原因は Docker ではなく yarn だった。yarn install コマンドでは yarn.lock ファイルがある場合と、ない場合で異なる処理系を用いているのだろうか。悪い設計の典型だ。

依存性解決に yarn.lock ファイルを利用できないということは、コンテナイメージが作られる度に、 Install されるライブラリの Version が微妙に異なるコンテナができあがることを意味する。仮にE2E テストが失敗しても、テストに使ったコンテナが消えた途端、テストが失敗したライブラリの Version 情報が失われることになる。こんな方式は採用できない。

呆れながら npm i コマンドを実行し、node_modules を削除後 npm ci コマンドを実行したら、 Storybook は正常に立ち上がり、 Docker 上でも問題なく動いた。

Vue3 や Storybook の公式を見てみると npm をデフォルトとして表示している。 Storybook 公式での並び順は、今回の Issue を反映しているかのようだ。もう yarn を使用する理由はないようだ。

私のプロジェクトでも、すべて npm で作業を進めることにした。Dockerfile やドキュメント類のコマンドはすべて yarn から npm に戻した。