相変わらず Bootstrap を使い続けてしまっている
前回のあらすじ:Tailwind は最低限のデザインで構わない自分にとっては Not for me だった - ブラウザ拡張開発日記
Tailwind を無料で使う場合、UI Component ライブラリは無料の 3rd party 製のものを探して選ぶ必要があるわけだけど、品質がまちまちだし、流行り廃れがあるのでせっかく選んだものが後に負債化する、とか考えるのが嫌なので、
— Cside (@Cside_) 2023年11月9日
公式が無料で高品質な UI Component を一通り提供してくれる Bootstrap を未だに使い続けてるわけなのですが、令和に Bootstrap...? スキルセット的にどうなの...? みたいなモヤりをずっと抱えつつ使っている...。
— Cside (@Cside_) 2023年11月9日
拡張機能の有料機能を、買い切り or サブスク どちらで提供するか、悩んだメモ
実装はあらかた終わったが、最後の最後に、買い切りかサブスクかで迷いが生じてしまった。
相談できる同僚もいないので、ここで自分の考えを整理したい。
対象のプロダクト
一見簡単そうに見えるが、パーサーが非常に複雑で、コード行数 5,000 行超えの大作となっている。これだけ手間隙かけて無料はちょっとなぁ、と思い始め、一部の機能を有料で提供する決断をした。
リスク要因
2 つある。
1. 拡張が、Notion の「非公式」API に依存していること
2. 僕の体調が不安定であること
- 現在は 1 週間に平均して 4〜5 日くらいは(短い時間ながらも)コードを書けている
- しかし、体調が悪化したり、強い副作用の薬を服用するようになった場合、1〜2 ヶ月、最悪それ以上の期間、コードが書けなくなる可能性がありうる。(過去にもそういう事があった)
最も悪いケースを考えれば、 「僕が体調不良でコードを書けない期間」と「 Notion の API の(後方互換性を壊す)仕様変更」が同時に起こった場合、その期間はサービスが停止する ことになる。
サービスが当面の間 停止する場合の対応
上記の最悪ケースが起こり、当面の間サービスが停止する緊急事態になった場合。(しかも、いつコードを再び書けるようになるかも見通せない場合)
買い切りの場合
- 有料プランの導線をいったん閉じ、場合によっては、つい最近お金を払ってくれたユーザーに対して返金対応をする
サブスクの場合
- 有料プランの導線をいったん閉じ、 サービスが再開できる日まで、サブスク加入ユーザーに対して「毎月」払い戻しの対応をする
- 「サービスを再開できるまでの間、サブスク加入ユーザーにはいったん解約してもらう」ということは ExtensionPay / Stripe の仕様上できなさそう(せいぜい、お願いのメールを一件一件手動で送るくらい)なので、毎月払い戻しをする以外無いだろう
以上を踏まえ、買い切りか、サブスクか
- サブスクはやめておいたほうが良さそう
- コードか書けないくらい病気が悪化しているときに、払い戻しというタスクが毎月発生するのは、
(1) 既存のサブスク加入ユーザーへの罪悪感
(2) 早く回復しなければ、という焦りが生じる
という 2 点から、ただでさえ病気で余裕の無くなっている精神状態にさらに追い打ちをかけることになる 。それはかなり危険
- コードか書けないくらい病気が悪化しているときに、払い戻しというタスクが毎月発生するのは、
- よって、消去法的に、買い切りに決定する。
終わりに
- 上記の 2 つのリスク要因が無ければ、迷わずサブスクにしていただろう
- 生活上、一度しかお金が入ってこないよりも、毎月お金が入ってきた方が嬉しいに決まっている
- それに、メンテナンスに今後も継続的に時間を取られるだろうし。
- ユーザーとしても、無名の僕にいきなりまとまった額を支払うのには抵抗があるはずなので、試しやすい少額で使い始められるサブスクリプションが嬉しいユーザーはいるだろう
- しかし、リスク要因を無視することは出来ないので、今回は買い切りという選択を選ぶこととする。
ブラウザ拡張機能を作る際に利用している外部サービス 3 つ
Sentry - エラー収集
言わずとしれたエラー収集サービス。アラートを Slack に流してる。
💡エラーに個人情報が混じってる場合は適切にマスクした上で送信すること。
無料プランだと Slack 連携が使えないので
Sentry の Webhook
→ Vercel に立てているリクエスト中継アプリ
→ Slack の Incoming Webhook
みたいな感じで Sentry の Webhook を Slack までリレーしている。
Vercel のリクエスト変換アプリのコードはこんな感じ。(クリックで展開)
const { IncomingWebhook } = require("@slack/webhook"); export async function POST(request) { const params = await request.json(); const webhook = new IncomingWebhook( "YOUR SLACK INCOMING WEBHOOK URL", { icon_url: "YOUR ICON URL", channel: "YOUR SLACK CHANNEL", username: "Sentry", } ); await webhook.send( `${params.event.title}\n${params.url}\n( by ${params.project} )` ); return new Response("OK"); }
リクエスト変換アプリを立てる場所はVercel でなくても、Google Cloud でも AWS でも何でも良いと思う。
canny.io - バグ報告/機能要望
バグレポートや機能要望をユーザーが投稿できる SaaS 。
以前までは Github Issues を使っていたが、言わずもがな Github アカウントが必要でユーザーにとって敷居が高かったので、Google などのアカウントでログインできるこちらのサービスに変えた。
ExtensionPay - 有料機能の実装
決済機能をまるっと請け負ってくれるサービス。(正確にはまだ使っておらず、近々使う予定。)
これを使えば、サーバーを自前で持たなくても、クライアント JS のコードだけで有料機能の実装ができる。
ExtensionPay の内部では Stripe で決済を実行しているので、Stripe のアカウントが必要となる。(審査を通すために Web サイトなどをこしらえるのが割とだるい)
ただ、問題なのは競合サービスが存在しないことで、もし ExtensionPay がサ終した場合、決済のサーバーサイドを自前で用意しなければならないことになる。。サ終に怯えながら使ってる。
どうしたもんかなー。(昔は Google 公式が、拡張機能の決済システムを提供してくれていたらしいのだが、だいぶ昔に廃止されてしまった。復活してくれないかなー。無いか。。)
はてなブログを Markdown で書くとき、リスト(箇条書き)の先頭の記号を入力補完するブラウザ拡張機能
URL が動的に変わった場合に何かする( Chrome 拡張で)
追記
chrome.tabs.onUpdated の方がいいかも。
ユーザーに対して "ブラウザの履歴" 権限を要求しないのも、ユーザーの心象が良い。
追記前の文章ここから
URL が動的に変わった場合、とは要するに pushState された後に何かすることを指す。
window.popstate イベント というのがあるが、これは現在の Chrome などのブラウザでは使えない。
ではどうするか ... というと、chrome.webNavigation API の onHistoryStateUpdated イベントを listen すれば良い。
https://example.com/*
でページの URL が変更された場合 *1 に何かするコードの例:
chrome.webNavigation.onHistoryStateUpdated.addListener( detail => { /* 何かする */ }, { url: { urlPrefix: 'https://example.com/'} }, );
このコードは background service worker でしか動かないので、content script でなにかしたい場合は chrome.tabs.sendMessage() で content script にメッセージを投げる。
/* background.ts */ chrome.webNavigation.onHistoryStateUpdated.addListener( detail => chrome.tabs.sendMessage(detail.tabId), { url: { urlPrefix: 'https://example.com/'} }, );
/* content.ts */ chrome.runtime.onMessage.addListener(() => { /* 何かする */ });
ネック
この方法の唯一のネックは、ユーザーに webNavigation
permission をインストール時に要求することになること。
「おいおい何で history にアクセスできる権限を要求するんだよ!!」とクレームが来たことが1度だけあるので、気にする人は気にするかもしれない。
*1:動的に変わった場合も、初回読み込み時も含む
複数の PJ で使う TypeScript のコードを共通ライブラリ化する
ライブラリを作るにあたり:tsup を使った
tsup とは:rollup みたいなバンドルツール。TypeScript で npm パッケージを作るのに特化している。
これを使わないと何が面倒臭いか:
- CommonJS / ES Module 両対応させる場合、それぞれ別々の tsconfig.json を用意しなければならない
- tsup だと、非常にシンプルな config ファイル 1 個だけで CommonJS / ES Module 両対応できる
- スクリプト ( node_module/.bin/ に設置されるやつ) を作る場合、ES Module だと、
import './foo.js'
みたいに import 文に.js
を書かないと実行時エラーになってしまう- tsup を使っていると
.js
を書かなくても実行時エラーにならない
- tsup を使っていると
- スクリプトを作る場合、ビルドした後に
chmod +x
しなければならない- tsup を使っていれば(ry
ライブラリをどこに上げるか→ Github Packages にした
GitHub Packagesのクイックスタート - GitHub Docs
Github Packages とは:パッケージをホストおよび管理する Github の機能。npm 互換がある。
ここに Github Actions を使ってパッケージをアップロードすれば、あとは npm パッケージをインストールするのと同じように、 npm install <package name>
でそのパッケージを使えるようになる。
他に検討したがボツにしたもの:
- 普通の素の Github リポジトリをホスト先として使う
- 要は
npm install https://github.com/Cside/some-package
みたいなことをすること - 👍 npm publish とかすることなく、Push するだけで更新が反映されるのは楽
- ❌ ビルドした .js ファイルも Git 管理しないといけない( Pull Request の差分とかが地獄になる)
- 要は
- npm の公開パッケージ
- ❌ 非常に個人的なコードなので、公開パッケージとしてアップロードするのは躊躇われる
- npm のプライベートパッケージ
- ❌ 月額料金がかかる
- Git submodule
Github Packages にもデメリットは 1 つあり、それは Github の Access Token を含んだ .npmrc を、リポジトリを触る全員が設置しないといけないこと。
これはメンテナにとってはだるそうなので、ここだけ気になるんだよなー。。(どうしようもならなそうだけど)
Plasmo がいまさら気になる(まだちゃんと触れていない)
割と前からあるっぽいが、社会と断絶した生活を送ってるため、昨日知った。。
公式サイトでは下記のように謳っている。
印象
- 👍 魅力的に思えること
- 😥 懸念
結論
懸念点は触ってみないと何とも言えないので、どっかで腰を据えて触りたい。