私は 6 年間維持してきたオープンソースプロジェクト ——RSSHub を持っていますが、崩壊の危機に直面しています。
背景#
表面的には、約 30k のスター、900 人以上の貢献者、毎月 3 億以上のリクエスト、数え切れないユーザー、毎月数十ドルのスポンサーシップ、絶え間ない issue と pr、コードはほぼ毎日更新されており、非常に健康で活気に満ちています。しかし、見えないところでは、数年間にわたる高額なメンテナンスコスト、毎月 1000 ドル以上のサーバー費用、毎日繰り返される煩雑で徐々に蓄積されるメンテナンス作業が、崩壊の縁で繰り返し揺れ動いています。
プロジェクトは 6 年前に開発され、多くの当時「次世代」を謳った流行の Node.js 技術スタックや依存ライブラリは、今や時代の涙となり、今見ると非常に古く感じます。現在流行している新技術(JSX、TypeScript、Serverless など)を適用することができません。また、そのアーキテクチャも非常に不合理で、各ルートの情報が複数の場所に散らばっており、1 つのルートを開発または変更するには多くの修正が必要です。1 つの場所でルートを登録し、別の場所でルートスクリプトを書き、さらに別の場所で Radar ルールを書き、また別の場所で文書を作成する…… これにより多くの作業量が増え、ミスが起こりやすくなります。以前はルートが少なかったため問題ではありませんでしたが、今では耐え難いものとなっています。
このようなひどいインフラの下で現状を維持することが精一杯で、新機能を開発することは無理な話で、将来的な更新の難易度を増すだけです。そのため、時々頭に浮かぶ新しいアイデアを実現することも非常に難しいです。
これらの問題を解決する唯一の方法は、現代的なフレームワークと新しい設計のアーキテクチャを使用してコアを再構築することですが、ルートが増えるにつれて改造コストも高くなり、基本的な変更には数ヶ月の作業が必要になる可能性があります。そのため、問題が深刻化しているにもかかわらず、使えないわけではないという原則に従って、先延ばしにしてきました。
しかし、これはやらざるを得ないことなので、私は時間を見つけて数ヶ月をかけて再設計と再構築を行いました。
技術スタックの更新#
koa -> Hono#
最初のステップであり、最も基本的で難易度が高いのは、以前使用していた Web フレームワークkoaを置き換えることです。6 年前に流行した次世代 Web フレームワークとして、作者はすでに開発を放棄しており、調査の結果、JSX、TypeScript、Serverless のサポートが最も良いHonoに切り替えることに決めました。
彼らの API の違いは非常に大きく、すべてのミドルウェアを再構築し、すべてのルートで使用されている koa API を置き換える必要があります。
主な変更点:
https://github.com/DIYgod/RSSHub/pull/14295
Hono の作者もこの改造を非常に気に入っています。
JavaScript -> TypeScript#
TypeScript に切り替えることで、多くの型の問題や低レベルのエラーを回避でき、最も重要なのは、数百人の貢献者が一貫性を保ち、エラーが発生しにくく、後続の貢献のルートコードの質があまり悪くならないことを保証できます。
主な変更点:
CommonJS -> ESM#
ESM は数年前に一部の Node.js コア開発者によって強く推奨された規範で、いくつかの利点がありますが、最大の問題は、以前の CommonJS との互換性がないことによるエコシステムの分断と機能の簡素化による批判です。
この数年の発展を経て、現在ではほとんどのシーンでなんとか使えると言えます。tsxも CommonJS と ESM の混在シーンをサポートしています。
最大限の努力を尽くしましたが、いくつかの CommonJS コードは一時的に移行が難しく、現在は tsx を使用して実行するしかなく、一部の Serverless(例えば Vercel)とは互換性がありませんが、今後徐々に解決する機会もあります。
主な変更点:
- https://github.com/DIYgod/RSSHub/pull/14619
- https://github.com/DIYgod/RSSHub/pull/14691
- https://github.com/DIYgod/RSSHub/pull/14632
art-template -> JSX#
art-templateは koa をサポートするテンプレートエンジンで、6 年前にはもっと流行していたテンプレートエンジンがありましたが、名前は覚えていません。art-template を選んだのは、その流行していたものが当時理解できなかったからで、こちらは非常にシンプルだからです。
Hono は JSX のサポートを内蔵しており、JSX については多くを説明する必要はありません。正統派の JavaScript の構文拡張で、React を使用するのと同等です。
主な変更点:
- https://github.com/DIYgod/RSSHub/commit/3bfdf9427cb8cf063cf7d231ec621278495f5a44
- https://github.com/DIYgod/RSSHub/commit/94cf0742afa8bf18510ad9ded9b76dcd2ad52c90
Jest -> Vitest#
Jest はかつて流行していたテストフレームワークですが、ESM 時代の到来以降、次第に使い物にならなくなりました。ESM のサポートは常に実装的な「experimental support」であり、現在は Vitest がより流行しています。
主な変更点:
https://github.com/DIYgod/RSSHub/commit/38e42156a0622a2cd09f328d2d60623813b8df28
Got -> ?#
現在使用しているGotもすでに積極的にメンテナンスされていない状態で、良い代替品も見つかりません。今後はネイティブの Fetch または自作の Fetch に切り替えるかもしれませんが、まだ手を付けていません。
新しいルート標準#
私自身の能力はまだ不十分で、コミュニティの開発者たちとの議論の中で多くを学び、改善しました。このプロセスは非常に興味深いものでした:https://github.com/DIYgod/RSSHub/issues/14685
主な変更点:
https://github.com/DIYgod/RSSHub/pull/14718
歴史#
新しい標準は、ルート情報が過度に分散している問題を解決するために作成され、今回が第三版にあたります。
第一版は RSSHub の開発段階から来ており、その時点ではルートの数がこれほど多くなるとは予見していなかったため、ほとんど計画がなく、すべてのルートが同じファイルに登録され、その後ルートスクリプトや文書を追加していきました。後にこのファイルはどんどん大きくなり、衝突が起こりやすくなり、すべてのルートスクリプトが起動時に読み込まれるため、プログラムの性能が悪化していきました。
第二版はNeverBehaveがメンテナンスしていた時期に、名前空間を導入し、router.js、radar.js を分割しました。同じ名前空間のルートは 1 つのフォルダーと 1 つまたは複数の Markdown 文書に集中させ、さらに遅延読み込みを実現し、メンテナンス性と性能を大幅に向上させましたが、それでも複数のファイルに分散しており、異なるファイルの情報が不一致になりやすく、エラーが発生する可能性がありました。
現在#
今回、ルートファイルを 2 種類に分けました。namespace.ts と任意の名前のルートファイルです。
namespace.ts は、namespace という名前のオブジェクトをエクスポートすることで、名前空間の情報を定義します。
import type { Namespace } from '@/types';
export const namespace: Namespace = {
// ...
};
namespace に含まれるフィールドは TypeScript によって制限されます。
interface Namespace {
name: string;
url?: string;
categories?: string[];
description?: string;
}
これらの情報はコンパイル後に文書と RSSHub Radar によって利用されます。
ルートファイルは、route という名前のオブジェクトをエクスポートすることで、ルートの情報を定義します。
import { Route } from '@/types';
export const route: Route = {
// ...
};
route に含まれるフィールドは TypeScript によって制限されます。
interface Route {
path: string | string[];
name: string;
url?: string;
maintainers: string[];
handler: (ctx: Context) => Promise<Data> | Data;
example: string;
parameters?: Record<string, string>;
description?: string;
categories?: string[];
features: {
requireConfig?: string[] | false;
requirePuppeteer?: boolean;
antiCrawler?: boolean;
supportRadar?: boolean;
supportBT?: boolean;
supportPodcast?: boolean;
supportScihub?: boolean;
};
radar?: {
source: string[];
target?: string;
};
}
以前の route.js、maintainer.js、radar.js と文書の情報はこの 1 つのファイルに集中され、多くの定義を減らし、エラーの可能性も減少しました。
実装#
実装の論理は、開発環境がルートファイル全体を遍歴し、すべての namespace.ts とルートファイルを見つけて情報を読み取り、ルートをロードします。生成環境では、事前にコンパイルされたパスリストを使用して遍歴と不必要なロードプロセスを回避します。コードは:https://github.com/DIYgod/RSSHub/blob/master/lib/registry.ts
文書もルートフォルダーを遍歴し、必要な情報を見つけて一連の Markdown ファイルを合成します。もはや手動で維持する必要はなく、コードは:https://github.com/DIYgod/RSSHub/blob/master/scripts/workflow/build-routes.ts
もちろん、以前のルート標準で開発されたルートは新しい標準に移行する必要があり、直接放棄することはできません。すでにスクリプトを使用して情報を一括で取得し整理した後、置き換えを行いましたが、特に文書が非常に混乱しており、多くのエラーがあるため、取得した情報にも多くのエラーが含まれており、今後徐々に手動で修正する必要があります。
未来#
この一連の改善を通じて、RSSHub はついに歴史的な負担を捨て、新機能の開発に専念できるようになりました。ここに私が蓄積したアイデアをいくつか挙げてみます:
- RSSHub はデータの集合体であり、用途は RSS だけではないので、JSON 出力機能を強化し、汎用の RESTful API として使用できるようにすることができます。例えば、次のページのインターフェースを提供したり、Twitter のフォロワー数のような非フィードデータを出力したりすることができます。
- ユーザーシステムとユーザーによるカスタム設定、自分のプライベートな購読アドレスを生成する#14706
- ルートエラー通知と健康度検査#14712
- RSS3 ノードとの連携と暗号通貨の収益共有 https://twitter.com/rss3_/status/1731822029199094012
- AI 翻訳と要約
- より詳細なインスタンスデータ分析と逆推導による自動推薦の Radar ルール
- ローカルブラウザやクライアントにバインドされた RSSHub インスタンス、実際に反クローラーの問題を解決する可能性がある
- ...
最後に、オープンソースは非常に高価な事業であり、RSSHub が今まで生き延びてこれたのは、これらの開発者の助けのおかげです。
そして、これらのスポンサーの善意のある人々のおかげでもあります。
もし RSSHub があなたを助けているなら、ぜひ積極的に参加し、情報の自由な未来に向けて自分の小さな力を貢献していただければと思います。