top of page

Velo

実装

備忘録

コラム

その他

カテゴリー

is not a function を1つずつ直すと、貼り直し1往復でエラーが1個しか進まない

  • 1 時間前
  • 読了時間: 4分

webMethod が届くようになった直後、画面に出たのはこれだった。

想定外のエラーで中断しました。
[詳細] getSummary: (0 , _dateUtils.parseDate) is not a function

前の症状とは意味が違う。今度は関数が呼ばれていて、その中で落ちている。サイト上の日付ユーティリティ(この記事では public/dateUtils.js と呼ぶ)が parseDate を export していない、と読める。

ここで dateUtils.js を貼り直して公開すれば、たしかにこのエラーは消える。そして次の名前で同じエラーが出る。手で貼り付けて反映する運用では、この1往復に数分かかる。エラーが1個進むたびに数分を払う形になっていて、しかも残りが何個あるのかが分からない。

同じ症状になる原因が2つある

この一行からは、少なくとも2つの状態が区別できない。


何が起きているか

なぜ同じ症状になるか

A

サイト上の public/dateUtils.js が古い、または別の内容になっている

parseDate が無い

B

サイト上の public/constants.js が壊れている

dateUtils.js は先頭で constants.js を import している。依存の評価に失敗すると dateUtils.js の名前空間が空になり、export が全部 undefined になる

A なら dateUtils.js を貼ればよい。B で dateUtils.js を貼っても何も変わらない。先に constants.js を貼る必要がある。画面のエラー1行では、どちらなのか決められない。

大量の import を typeof でまとめて見る

そこで、症状が出るまで待つのをやめた。バックエンドの2ファイルが import している名前を全部、typeof で調べて、届いていないものをまとめて返す。

function missingDependencies() {
  const groups = [
    ['public/dateUtils.js', { formatJpDate, formatDeadline, getTodayYmd, addDaysYmd, parseDate }],
    ['public/constants.js', { COLLECTIONS, toRefId, formatAddress, MAIL_KIND, … }],
    ['backend/dataAccess.js', { dbFind, dbInsert, dbUpdate, elevated, createReadBudget, … }],
  ];

  const missing = [];
  for (const [module, bag] of groups) {
    for (const name of Object.keys(bag)) {
      if (typeof bag[name] === 'undefined') {
        missing.push({ module, name });
      }
    }
  }
  return missing;
}

オブジェクトに束ねてキーを回すだけなので、名前を列挙する手間以外には何もない。dataAccess.js 自身の import も同じ仕組みで報告させて、こちらの結果に混ぜる。あちらの依存が死んでいると、こちらからは「注文が0件」に見えるだけで理由が分からないからである。

画面に出るのはこうなった。

サイト上のファイルが古いため集計できません。下のファイルを貼り直してください。
[詳細] 届いていない import があります。
・public/dateUtils.js: formatJpDate, formatDeadline, getTodayYmd, addDaysYmd, parseDate
public/dateUtils.js を貼り直してください(constants は届いています)。
貼り直す順: public/constants.js → public/dateUtils.js → backend/dataAccess.js → backend/summaryOps.js

この出方で A と B が割れる。

出方

意味

dateUtils.js の名前だけが欠ける

A。そのファイル自身が古い

dateUtils.js と constants.js の両方が欠ける

B。連鎖しているので constants.js を先に貼る

dataAccess.js 経由の欠落も出る

あちらの依存も古い。依存の下から順に貼る

あるモジュールの名前が全部欠けているなら、そのファイル自身が古いのではなく、そのファイルが import している先が壊れていて評価されていない可能性が高い。全滅かどうかが、貼り直す先を決める材料になる。

検査を呼ぶ場所を間違えない

この検査は、公開関数の入口で1回だけ呼ぶ。typeof を見るだけなので費用はほとんど無い。

やってはいけないのは、モジュールの評価時に呼ぶことである。評価時に import した値へ触れば、それ自体が同じ落とし穴になる。依存が古いときに、依存が古いことを報告する仕組みごと読み込みに失敗する、という形にしかならない。診断は、診断される状態でも動く場所に置く。

フロントエンド側でも同じことをする

バックエンドが自分の依存を調べられるのは、バックエンドが読み込まれている場合に限る。読み込みそのものが失敗していれば、フロントエンドから見えるのは空のクライアントプロキシだけになる。そこで、ページ側でも到着を確かめた。

const BACKEND = { getSummary, exportCsv, markCompleted,
                  sendReminders, sendNotices };
const missingBackendNames = BACKEND_NAMES.filter((n) => typeof BACKEND[n] !== 'function');

届かなかった名前と届いた名前の両方を出す。5つは同じモジュールなので、届き方で貼り直す先が割れる。

届き方

意味

貼り直す先

5つとも欠ける

.web.js のビルドが失敗している

実装本体とその依存

一部だけ欠ける

サイト上の .web.js が古い版

その .web.js

呼び出しは全部この検査を通す入口に集約して、未到着なら投げずに { ok: false, notice, detail } を返す。集計、CSV、完了処理、通知の4系統が、同じ形で機能を落として応答する。案内はモジュールの読み込み時と画面の初期表示の両方で出した。日付を選ぶ前に気付けるようにするためである。

ボタンは無効化しなかった。押しても無反応にすると、原因が利用者にも開発者にも見えなくなる。押せば理由が出る状態のほうが、まだ扱いやすい。

関連記事

Stores/Orders が存在しなかった

WDE0025 は権限や綴りの問題に見える。実際には注文コレクションの ID が世代で変わっていた。作成日のフィールド名まで変わる。

 
 
 

コメント


bottom of page