Closed9

Remix+CloudflareでWebサイトを作る 2(MUIエラー修正・Web Analitics導入・フォームバリデーション・ドメイン購入・Superflare)

saneatsusaneatsu

【2024-02-03】エラー: Element type is invalid: expected a string

エラー内容

こんな感じのコードを書いたがアイコンを表示している箇所でエラーが出る

import { FavoriteBorder } from "@mui/icons-material";
import EmailIcon from "@mui/icons-material/Email";
import IconButton from "@mui/joy/IconButton";

export default function LoginPage() {
  return (
    <IconButton variant="soft" color="primary" size="sm">
        {/* ここでエラー */}
        <EmailIcon />
        {/* これだとエラーがでない */}        
        <FavoriteBorder />
      </IconButton>
    </div>
  );
}

内容は以下。

Error: Element type is invalid: expected a string (for built-in components) or a class/function (for composite components) but got: object.

Check the render method of `LoginPage`.
    at createFiberFromTypeAndProps (http://0.0.0.0:8788/build/_shared/chunk-GIAAE3CH.js:20439:25)
    at createFiberFromElement (http://0.0.0.0:8788/build/_shared/chunk-GIAAE3CH.js:20460:23)
    at reconcileSingleElement (http://0.0.0.0:8788/build/_shared/chunk-GIAAE3CH.js:11801:31)
    at reconcileChildFibers2 (http://0.0.0.0:8788/build/_shared/chunk-GIAAE3CH.js:11838:43)
    at reconcileChildren (http://0.0.0.0:8788/build/_shared/chunk-GIAAE3CH.js:14292:37)
    at updateHostComponent (http://0.0.0.0:8788/build/_shared/chunk-GIAAE3CH.js:14802:11)
    at beginWork (http://0.0.0.0:8788/build/_shared/chunk-GIAAE3CH.js:15923:22)
    at beginWork$1 (http://0.0.0.0:8788/build/_shared/chunk-GIAAE3CH.js:19749:22)
    at performUnitOfWork (http://0.0.0.0:8788/build/_shared/chunk-GIAAE3CH.js:19194:20)
    at workLoopSync (http://0.0.0.0:8788/build/_shared/chunk-GIAAE3CH.js:19133:13)

原因

この記事を見る限り、Matreial UIのアイコンはドキュメントに書いてあるimport方法で読み込んでもこのエラーが出てしまう場合があるらしい。
この記事にとはちょっと違うが、以下のようにするとうまく表示された。
公式ドキュメントのように LightModeIcon にするのではなく LightMode として Icon を省く必要がある。

// 🚨 表示されない
import LightModeIcon from "@mui/icons-material/LightMode";

// ✅ 表示される
import { LightMode } from "@mui/icons-material";
saneatsusaneatsu

【2024-02-03】Hydration関連のエラー色々

エラー内容

以下のエラーが発生

installHook.js:1 Error: Hydration failed because the initial UI does not match what was rendered on the server.
react-dom.development.js:86 Warning: An error occurred during hydration. The server HTML was replaced with client content in <#document>.
Uncaught Error: Hydration failed because the initial UI does not match what was rendered on the server.
ncaught Error: There was an error while hydrating. Because the error happened outside of a Suspense boundary, the entire root will switch to client rendering.

解決策

<StyledEngineProvider injectFirst> を削除するとエラーが出なくなった。

app/root.tsx
export default function App() {
  return (
    <html lang="en">
      <head>
        <meta charSet="utf-8" />
        <meta name="viewport" content="width=device-width, initial-scale=1" />
        <Meta />
        <Links />
      </head>
      <body>
        {/* <StyledEngineProvider injectFirst> */}
        <Outlet />
        {/* </StyledEngineProvider> */}
        <ScrollRestoration />
        <Scripts />
        <LiveReload />
      </body>
    </html>
  );
}

これでラップすることでJoy UI本来のスタイルよりもカスタムスタイルを優先的に適用させられるようになるっぽい。
1つ上のコメントで書いてあるとおり、アイコンのimport順ですらエラーになるから公式あまり信用しないでおこうかな。

MUI、なんだか雲行き怪しいな。。

Style library interoperability - Material UI

https://zenn.dev/metallic_kfc/articles/96791d888dddc1

saneatsusaneatsu

【2024-02-04】エラー: Not symbolizing stack traces because $LLVM_SYMBOLIZER is not set

背景

以下のSandboxはMUIが公式に出しているものだが、これを参考にしながらログインページを作成していた中で発生した
admiring-curie-k77ty6 - CodeSandbox

エラー内容

$ npm run dev
...

workerd/util/symbolizer.c++:98: warning: Not symbolizing stack traces because $LLVM_SYMBOLIZER is not set. To symbolize stack traces, set $LLVM_S

workerd/jsg/jsg.c++:136: error: took recursive isolate lock; kj::getStackTrace() = 10060a9ef 1008c9f07 1008e095b 1008e08df 101f19107 100c69493 100c6a1af 100bceaff 100bced27 101eeaadf 1009cde07 101eeae07 1009ce8eb 101eeae07 1008ab1c7 101eec78b 101eeae07 1008abdbb 101eebab3 101eeaadf 1008ac4

Remix v2へアップデートした時のメモ | Web Scratch
このページを見る限り、$ kill $(lsof -t -i:8788) でキルしたら解決されるそうだが解決されない。

Sandbox の中にある<CssVarsProvider defaultMode="dark" disableTransitionOnChange> らへんが怪しいかと思ったがコードを消しても何回かリロードするとこのエラーがちょくちょくでてきてしまう。
少なくともブラウザコンソールでは何もエラーがでていないし、UI側の問題ではなさそう。

🐛 BUG: "warning: Not symbolizing stack traces because $LLVM_SYMBOLIZER is not set" · Issue #3631 · cloudflare/workers-sdk
このissueを見る限りCloudflareのWranglerを最新にするのが良しとされている。

解決

package.jsonを見ると、"wrangler": "3.8.0" になっているが2024-02-04時点での最新は3.26.0

ということでアップグレードしてみると治った!

saneatsusaneatsu

【2024-02-04】Web Analyticsの導入

使ってみる

CloudflareのページをポチポチいじっていたらAnalyticsのページがあったのでスクリプトを埋め込んでデプロイしてみる。

https://zenn.dev/kameoncloud/articles/fcd91fa424f576

ローカルからデプロイしてみると今までと違いアカウントを選択するフローが存在し、「プレビュー」ではなく「プロダクション」環境にデプロイされた。

$ npm run deploy
> deploy
> npm run build && wrangler pages deploy ./public
> build
> remix build

 info  building... (NODE_ENV=production)
 info  built (7.3s)
✔ Select an account › Hogehoge Account
No project selected. Would you like to create one or use an existing project?
  Create a new project
❯ Use an existing project
Select a project:
❯ YOUR_PROJECT_NAME

以下は過去7日間のデータ(開発してからまだ3日なので今までの全てと同義)だが、アカウント分析を見てみたら海外から謎のアクセスあって怖い...。

ついでに通知設定

サイドバーをいじっていたら通知設定があったのでなんとなくDDos攻撃アラートだけ設定してみる。
「テスト」ボタンを押すと設定したメアドに通知が飛んでくる。
通知系の設定でテスト機能があるのGood UX。


saneatsusaneatsu

【2024-02-04】エラー: Could not resolve "crypto"

エラー内容

なんかちょくちょく出てくるな。

$ npm run dev

> dev
> remix dev --manual -c "npm run start"


 💿  remix dev

 info  building...
✘ [ERROR] Could not resolve "crypto"

    node_modules/cookie-signature/index.js:5:21:
      5 │ var crypto = require('crypto');
        ╵                      ~~~~~~~~

  The package "crypto" wasn't found on the file system but is built into node. Are you trying to bundle for node? You can use "platform: 'node'" to do that, which will remove this error.


✘ [ERROR] Could not resolve "stream"

    node_modules/stream-slice/index.js:3:24:
      3 │ var Transform = require('stream').Transform;
        ╵                         ~~~~~~~~

  The package "stream" wasn't found on the file system but is built into node. Are you trying to bundle for node? You can use "platform: 'node'" to do that, which will remove this error.

原因・解決方法

原因を特定するとどうやら以下のloaderを追加すると出てきていた。
同じようなコードは他のページにもあるのに謎だなぁと思って、エラーを吐かないコードと見比べていたらjsonのimport元の違いがあった。

Remix v1をいじっていたときのコードからコピペしてきた弊害。
json@remix-run/node@remix-run/cloudflare どちらからもimportできるが、v1だと@remix-run/nodeを使っていたのでミスってしまった。

import { json } from "@remix-run/node"; // エラーが出る
import { json } from "@remix-run/cloudflare"; // エラーが出ない

export async function loader({ context }: LoaderFunctionArgs) {
  const env = context.env as Env;

  const users = await client(env.DB).select().from(usersSchema).all();

  return json({
    users: users ?? [],
  });
}
saneatsusaneatsu

【2024-02-04】エラー: ReferenceError: Buffer is not defined

背景

Basic認証を実装しようとして以下のようなコードを書いていた

app/routes/login.ts
const isAuthorized = (request: Request) => {
  const header = request.headers.get("Authorization");
  if (!header) return false;

  const base64 = header.replace("Basic ", "");
  const [username, password] = Buffer.from(base64, "base64")
    .toString()
    .split(":");
  
  return username === "admin" && password === "password";
};

export const loader = async ({ request }: LoaderArgs) => {
  // ログイン済みの場合 /admin に遷移させる
  const userId = await getUserId(request);
  if (userId) return redirect("/admin");

  // Basic認証
  if (!isAuthorized(request)) {
    return json({ authorized: false }, { status: 401 });
  }
  return json({ authorized: true });
};

エラー内容

ReferenceError: Buffer is not defined
    at isAuthorized (file:///Users/.../.wrangler/tmp/dev-RRisB7/7ly80mup8w2.js:44174:69)
    at loader2 (file:///Users/.../.wrangler/tmp/dev-RRisB7/7ly80mup8w2.js:44179:44)

原因・解決方法

フォーカスする限りBuffer は存在しそう。
実際にこのコードはRemix v1をいじっていた時に動いていた。

以下を追加したら修正された。
エラー文で調べてみるとwindow.Buffer でアクセスしたり、そもそも npm i buffer している人もいるがその必要は無し。

app/routes/login.ts
import { Buffer } from "buffer";

Basic認証のキャッシュ削除方法

エラーとは関係ないけど、ホスト名の前に<適当な文字列>@ を入れると削除される。
例: http://hoge@0.0.0.0:8788/login

ただし、http://hoge@ がついていると正しくUsernameとPasswordを入れても常にBasic認証が出てきてしまうので、削除後には再度 hoge@ をなくした正しいアドレスにアクセスする必要があることに注意。

Chromeの場合は http://hoge@ が検索フォームに表示されず 0.0.0.0:8788/login だけが表示されているので最初これに気づかなかった。

saneatsusaneatsu

【2024-02-04】フォームバリデーションはどれを使うか

候補

更新頻度、人気を考慮すると以下の2つ。

remix-validated-form

https://www.npmjs.com/package/remix-validated-form

Remix v1の時に使ってたので基本的な使い方はある程度わかってるけど、当時は凝ったことをしようとした時に痒いところに手が届きにくかった印象がある(もちろん自分の力量不足の可能性は大いにあるが)。

あとDemo をクリックしたら「デモサイト」に飛べるのかと思ったら動画ファイルダウンロードさせようとしてくるのだるい。

remix-hook-form

https://www.npmjs.com/package/remix-hook-form

使ったことがないが、仕事では react-hook-form を使っているので、こちらに関してもある程度勝手はわかっているし、使いやすかった印象がある。
https://zenn.dev/manalink_dev/articles/winter-react-form-mokumoku-meetup-report

最近上の記事も見たけどReact Hook Formはやっぱドキュメント充実してて良いよなぁと思う。
remix-validated-form は最後の更新が4ヶ月前だが、こっちは2週間前。
もちろん単純に後発だから、というのはあるかもしれないが remix-validated-form が4ヶ月更新されていないのがちょっとネガティブに見えてしまう。

結論

まだまだ小さいプロダクトでだめだったら修正しやすいはず。
使ったことのない remix-hook-form の方を使ってみる。

追記:書いてみたら remix-hook-form + Joy UI + Zod をいい感じに使えて良さそう

更に追記:結局フロントとバックエンドどちらも共通のスキーマを使ってバリデーションをできるConformを使うようになりました。とても便利。

その他

なんかのタイミングで「It will be sunset」使いたい。

saneatsusaneatsu

【2024-02-04】ドメインを取得する

空いているドメイン調べてみる

サポートされているドメインの中から良さそうなのを見繕うと以下。

ドメイン名 ドル/年
APP_NAME.party 4.16
APP_NAME.app 12.18
APP_NAME.red 15.18
APP_NAME.ink 19.18
APP_NAME.beer 20.18
APP_NAME.systems 21.18
APP_NAME.news 21.18
APP_NAME.ninja 21.18
APP_NAME.land 25.18
APP_NAME.media 28.18

com, io, infoらへんは既にとられていた。
appやnewsは普通に良いし、partyとかbeerもかわいい。

正直いつリリースできるかわからないので3月中にいい感じのところまで実装終わってたらapp news party beerのうち空いてる1つを取ろうかな。

買う

買っちゃったら開発頑張らなくちゃいけないし。
「ジム代払ったらジム行くはず」というのと同じ理論。
行かなさすぎて解約したけど。

時間かかりそうだしここらへんの設定無知なのでまた今度。

色々簡単に済んで助かる。

saneatsusaneatsu

Hello, Superflare!

Superflareというもの

RemixのactionでCloudflareに色々リクエスト送るのどうやるのか調べていたら以下の記事を発見。

https://zenn.dev/azukiazusa/articles/superflare-for-cloudflare-workers

以下のような記述もありなんだか良さそう。

Superflare より提供されている parseMultipartFormData 関数を使うことで、ファイルをメモリに展開せず、R2 にファイルを直接ストリーミングできます。R2 ストレージの操作は storage オブジェクトを使用します。storage().putRandom() メソッドはランダムな名前を生成してファイルをアップロードします。extension オプションを指定することで、ファイルの拡張子を指定できます。

プロジェクトの中身見てみる

$ npx superflare@latest new でRemix + Superflareのプロジェクトが作成できるので中身を見てみる。
drizzleの時に困っていたseed作成用ファイルなどもある。最初っからこいつを使っておけば...。
なんといってもCloudflareとの連携をスムーズにしてくれるのが良い。
上のZennの記事にたんまりExample Codeあったから割とすぐに動くものは作れそう。

npm install

また最初から作るのは面倒なので公式に則って npm コマンドで superflareを追加しようとしたら以下のエラーが発生。

$ npm install @superflare/remix
npm ERR! code ERESOLVE
npm ERR! ERESOLVE unable to resolve dependency tree
npm ERR!
npm ERR! While resolving: YOUR_PROJECT_NAME@undefined
npm ERR! Found: @remix-run/cloudflare@2.6.0
npm ERR! node_modules/@remix-run/cloudflare
npm ERR!   @remix-run/cloudflare@"^2.5.1" from the root project
npm ERR!
npm ERR! Could not resolve dependency:
npm ERR! peer @remix-run/cloudflare@"^1.14.0" from @superflare/remix@0.0.18
npm ERR! node_modules/@superflare/remix
npm ERR!   @superflare/remix@"*" from the root project
npm ERR!
npm ERR! Fix the upstream dependency conflict, or retry
npm ERR! this command with --force or --legacy-peer-deps
npm ERR! to accept an incorrect (and potentially broken) dependency resolution.
npm ERR!
npm ERR!
npm ERR! For a full report see:
npm ERR! /Users/saneatsuwakana/.npm/_logs/2024-02-04T07_57_14_114Z-eresolve-report.txt

npm ERR! A complete log of this run can be found in: /Users/saneatsuwakana/.npm/_logs/2024-02-04T07_57_14_114Z-debug-0.log

ERESOLVE unable to resolve dependency treeの解決方法 #Docker - Qiita
この記事を参考にオプションを付けてインストールし直した。

npm install @superflare/remix --save --legacy-peer-deps

が、この後にもパッケージimport周りやら諸々の設定周りでかなりしんどい。
Repository見る感じAlpha版で止まってしまってあまり開発も活発じゃなさそう。

https://zenn.dev/chimame/articles/60e2824a42fec0#superflare

この記事でも同様のことを書いている

現状はRemixでしか動作しません。
Remixプロジェクトの後からSuperflareを入れるのは結構苦労します。(RequestHandlerの修正やCloudflare > Workersの型やビルドなどなど結構手が入ります)
使用してみた限りAlpha版です。コンセプトとして出している状況に近いです。

結論

採用見送り。

使いたかったので残念。

このスクラップは2ヶ月前にクローズされました