Storybookを入れたときに発生した謎のModule parse failed: Unexpected tokenエラー

npx storybook@latest initを実行したところ、次のようなエラーが出ました。

storybookでModule parse failed: Unexpected tokenエラーが出ている様子

全文は長いので一部だけ抜粋

ERROR in ./node_modules/.pnpm/@storybook+components@7.5.1_@types+react-dom@18.2.14_@types+react@18.2.33_react-dom@18.2.0_react@18.2.0/node_modules/@storybook/components/dist/chunk-MUPK3MH6.mjs 3:2128
Module parse failed: Unexpected token (3:2128)
You may need an appropriate loader to handle this file type, currently no loaders are configured to process this file. See https://webpack.js.org/concepts#loaders
| import { __commonJS } from './chunk-JRLSWQMA.mjs';
|
> var require_markdown=__commonJS({"../../node_modules/refractor/lang/markdown.js"

ぱっと見で何が起きてるか全くわかりませんね。
node_modulesを消したりはしてみたんですが効果はなし。
似たようなプロジェクトを作り直してやってみたんですが、全く再現しませんでした。

ダメ元で `pnpm-lock.yaml` と `node_modules` を削除してlockファイルを作り直したら直りました。

一応何が原因だったかを探りたいので、作り直して再生成された `pnpm-lock.yaml` と作り直す前のものを比較してみます。
大きく分けて3つの違いがありました。

/@storybook/react@7.5.1(react-dom@18.2.0)(react@18.2.0)(typescript@5.2.2)の依存であるacornのバージョンが異なる
-  /acorn-import-assertions@1.9.0(acorn@8.11.1):
+   /acorn-import-assertions@1.9.0(acorn@8.11.2):
    resolution: {integrity: sha512-cmMwop9x+8KFhxvKrKfPYmN6/pKTYYHBqLa0DfvVZcKMJWNyWLnaqND7dx/qn66R7ewM1UX5XMaDVP5wlVTaVA==}
    peerDependencies:
      acorn: ^8
    dependencies:
-      acorn: 8.11.1
+      acorn: 8.11.2
    dev: true

  /acorn-jsx@5.3.2(acorn@7.4.1):
    resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==}
    peerDependencies:
      acorn: ^6.0.0 || ^7.0.0 || ^8.0.0
    dependencies:
      acorn: 7.4.1
    dev: true

-   /acorn-jsx@5.3.2(acorn@8.11.1):
+  /acorn-jsx@5.3.2(acorn@8.11.2):
    resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==}
    peerDependencies:
      acorn: ^6.0.0 || ^7.0.0 || ^8.0.0
    dependencies:
-      acorn: 8.11.1
+      acorn: 8.11.2
    dev: true

  /acorn-walk@7.2.0:
    resolution: {integrity: sha512-OPdCF6GsMIP+Az+aWfAAOEt2/+iVDKE7oy6lJ098aoe59oAmK76qV6Gw60SbZ8jHuG2wH058GF4pLFbYamYrVA==}
    engines: {node: '>=0.4.0'}
    dev: true

  /acorn@7.4.1:
    resolution: {integrity: sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==}
    engines: {node: '>=0.4.0'}
    hasBin: true
    dev: true

-   /acorn@8.11.1:
+  /acorn@8.11.2:
-    resolution: {integrity: sha512-IJTNCJMRHfRfb8un89z1QtS0x890C2QUrUxFMK8zy+RizcId6mfnqOf68Bu9YkDgpLYuvCm6aYbwDatXVZPjMQ==}
+    resolution: {integrity: sha512-nc0Axzp/0FILLEVsm4fNwLCwMttvhEI263QtVPQcbpfZZ3ts0hLsZGOpE6czNlid7CJ9MlyH8reXkpsf3YUY4w==}
    engines: {node: '>=0.4.0'}
    hasBin: true
    dev: true
/next@13.5.6(@babel/core@7.23.2)(react-dom@18.2.0)(react@18.2.0)の依存であるcaniuse-liteのバージョンが異なる
-  /caniuse-lite@1.0.30001554:
-    resolution: {integrity: sha512-A2E3U//MBwbJVzebddm1YfNp7Nud5Ip+IPn4BozBmn4KqVX7AvluoIDFWjsv5OkGnKUXQVmMSoMKLa3ScCblcQ==}
+  /caniuse-lite@1.0.30001555:
+    resolution: {integrity: sha512-NzbUFKUnJ3DTcq6YyZB6+qqhfD112uR3uoEnkmfzm2wVzUNsFkU7AwBjKQ654Sp5cau0JxhFyRSn/tQZ+XfygA==}
next-authのバージョンが異なる
-  /next-auth@4.24.3(next@13.5.6)(react-dom@18.2.0)(react@18.2.0):
-    resolution: {integrity: sha512-n1EvmY7MwQMSOkCh6jhI6uBneB6VVtkYELVMEwVaCLD1mBD3IAAucwk+90kgxramW09nSp5drvynwfNCi1JjaQ==}
+  /next-auth@4.24.4(next@13.5.6)(react-dom@18.2.0)(react@18.2.0):
+    resolution: {integrity: sha512-5DGffi+OpkbU62vPQIJ1z+hFnmow+ec5Qrn9m6eoglIO51m0DlrmLxBduZEwKAYDEg9k2joi1yelgmq1vqK3aQ==}
    peerDependencies:
-      next: ^12.2.5 || ^13
+      next: ^12.2.5 || ^13 || ^14
      nodemailer: ^6.6.5
      react: ^17.0.2 || ^18
      react-dom: ^17.0.2 || ^18

最も怪しいのはacronですね。ということでacronの直近のコミット履歴からissueをたどるとこんなものを発見しました。
github.com
github.com
まさに今回発生した現象という感じですね。
というわけでインストールタイミングが悪くて、たまたま依存モジュールの不具合を踏んでしまったというのが事の顛末でした。

工数を理由に管理画面のテストを雑にするのはよくない

という話を同僚としていた。いい話だからブログに書いてと言われたので、無茶苦茶久しぶりに記事を書いた。

通常のユーザーが利用できる面はどういった操作が行われるかわからない以上、テストはしっかり書くべきという意識が強いと思う。実際それは間違っていないし、考えうる限りのケースをテストするべきだと思う。

一方で管理画面は利用できるユーザーが限られているし、悪意のある操作が行われる可能性も限りなく低い。故にリソースが足りていないと「管理画面はまあそこそこのテストでいいでしょ」とか「管理画面は壊れてたら直せばいいから、単純なとこはテストいらないでしょ」みたいな考えに陥りがちだけど、実際は強い権限による操作が行われるのだから相応のテストを書いて然るべきだよね、ということを話していた。

管理画面は通常ユーザーが見れる面と違って権限チェックなども必要だし、これによってテスト書くのめんどくさくなりがちだよねという話題もある。でもテスト手法が確立されていればそれを模倣するだけでいいし、その手法が存在していないってことは実装当初の時点でちゃんとテスト書いてないのでは?という話もできる。まあ後付でどんどんチェック項目増えて肥大化して〜みたいなパターンも考えられるけど、それで追従できないならそれはそれで設計ミスってそうな気はする。

ユーザーからしたら自分の操作起因ではないステータスの変更が行われるわけで、テスト不十分による不具合で誤BANなんてされた日には不快度MAX!!みたいな感じになるので、「この操作が正常に動かなかったときに、対象ユーザーとしてどう思うか」という「操作される側」の意識は常に持っておきたい。

とはいえこれは本当に工数のことを考えていない話で、火の車だった場合には「そんなことしてられっかよ!」という気持ちになるのもまあ分かる。サービスは無料で動かせないからね。でもそれでサービスの信用度下げて「でもあそこ誤BAN多いからな」みたいな風評がついてしまうと新規ユーザー獲得率にも影響するし、自分自身一度信用する気が失せたサービスは可能な限り使いたくないと思っているので、少なくとも自分は自分自身が不具合踏んで(踏まれて)不快になりそうなポイントはちゃんとテストするようにしような、というマインドで開発している。

というかまあこういう脅迫をしてちゃんと工数を確保するのもテックリードの仕事なんだろうな(幸いうちのプロダクトオーナーはその辺の理解度高いので脅迫しなくても大抵やらせてもらえる環境でとてもありがたいのだけど)

ルーターを変えた

実はIPoE接続にしてからずっと違和感があって、ページを訪れた際に画像が表示されなかったり、よくわからない通信切断が行われていた。
タスクマネージャーとかでモニターを眺めてると明らかに通信そのものが行われていないので一発でわかる。
あとTweetDeckを使っているとTL更新で画像だけ取ってこれないみたいなのがしょっちゅう起こるのでわかりやすい。

今まで堪えていたけど、最近マスターデュエルでコイントス時の接続が不安定になって困ること(本当にフィールドに移るタイミングだけで、それ以外は問題ない)がちょいちょいあって、流石に許容できないので色々環境変えて問題の切り分けを行っていった。
結局何が原因だったかというと、タイトルにある通りルーターが原因だった。

続きを読む

sshuttleでNOPASSWDが効かなくなっていた

突然sshuttle --sudoersで設定してたNOPASSWDが効かなくなったのでなんでだろうねーと思っていたのですが、sshuttleが1.1.0にアップデートされて意図せずコマンドの順序が変わっていたということが原因のようです。

既にmergeされているのでそのうち直りそうですね。

github.com
github.com

mini-css-extract-pluginのアプデでハマった

mini-css-extract-pluginを2.4.5にあげる際に、ビルドが通らなくなってしまった事があったのでメモを残しておきます。

結論から書くと、自分の場合はpublicPathが意図せず二重に設定されてしまっていることが問題でした。

続きを読む

gensim/word2vecにwindow幅を固定する最高オプションが追加されていた

gensim/word2vecではwindow幅を無茶苦茶に大きくしてもランダムにsamplingされるので、実際には単語同士が遠い場合にコンテキストとして扱われない可能性がありました。
そこでこのwindow幅を固定するためのshrink_windowsオプションを追加するp-rがmergeされていました。これをFalseにすることで固定するという感じです。

github.com

このオプションのモチベーションとしては語順が重要でないコーパス(例えば自然言語分かち書きしたものではなく、特定の単語列をsentenceとして読み込ませた場合など)に対してはsentence内の全単語をコンテキストとしたほうが普通に考えたら正しそうだよね、みたいなことがありうるのでそういうパターンで有効です。

TypeScriptである型の部分型であることを保証する型定義

言い換えると、あるオブジェクトAに存在するプロパティが、あるオブジェクトBに生えているプロパティのいずれかに該当することを保証し、かつどのプロパティが生えてるかは自由、という感じにしたい。

追記:
id:nanto_vi さんに教えていただいたのですが、組み込みで Partial という型があり、基本的にはこちらを使えば良さそうです。
TypeScript: Documentation - Utility Types

以下中身がどうなっているかなど

keyofとMapped Type使えばなんかできそうだなーと思いつつも

type PartialObjectOf<T> = {
  [P in keyof T] : T[P]
}

こんな風に書くと結局全てのプロパティを持っていないといけないし、どうすればスマートに書けるかなーと思っていた。
実際にはこう書けば良い

type PartialObjectOf<T> = {
  [P in keyof T]? : T[P]
}

要はプロパティがoptionalであることを示せばよいわけですね。
ちなみに一部のプロパティが確実にあることを保証したい場合は以下のように交差型を使用すればよいです。

type PartialObjectOf<T> = {
  [P in keyof T]? : T[P]
} & { prop: string }