テーマとコンポーネントでの JavaScript の読み込み遅延

@david さんのおかげで、テーマにおける「eager」読み込みの JavaScript に関する非常にクリーンなパターンができました。

つまり、*.js.es6 ファイルを javascripts ディレクトリに置くだけで、プラグインと同様に動作するようになります。これは素晴らしいことです。

例えば、以下のようにイニシャライザーを作成すると、プラグインと 100% 同等の機能を実現できます。

  • /javascripts/discourse/initializers/my-init.js.es6 という名前のファイルを作成します
import { withPluginApi } from "discourse/lib/plugin-api";

function initialize(api) {
  // api を通じた初期化処理をここに記述
}

export default {
  name: "discourse-otp",

  initialize() {
    withPluginApi("0.8.28", initialize);
  }
};

これは非常に重要な機能です。これにより、大規模で複雑なテーマを多数の部品に分割できるようになり、リンティングや構文ハイライトなどの便利な機能を利用できます。

ただし、場合によっては JavaScript ペイロードをオプションで配信したいこともあります。

例えば、特定の Markdown を含む投稿のみを装飾する場合を考えてみましょう。使用するかどうか分からない時点で、100KBもの大規模なライブラリをダウンロードするのは無意味です。

私はこの問題を、以下の変更を踏まえてコンポーネント内で回避策を講じました。

具体的には以下のようにしました。

import loadScript from "discourse/lib/load-script";

function generateOtp($elem) {
  loadScript(settings.theme_uploads.jsotp).then(() => {
     // ここに処理を記述
  });
}

これにより、以下の対応が必要になりました。

  1. theme authorized extensions.js を追加する
  2. content security policy script src で、特定のアセットに対する CSP のバイパスを追加する
  3. about.json でアセット名を指定する

テーマコンポーネント作成者にとって、これはあまりにも手間がかかります。このレベルの高度な技術が必要では、配布競争において勝機がありません。

これを使いやすいものにするために、以下の 2 つの選択肢が考えられます。

  1. システムに、アクティブなテーマ内の .js アセットを自動的に CSP 対象とし、デフォルトでテーマが .js をアップロードできるようにする
  2. javascript_cache のような仕組みへ移行し、defer しないテーマ用 JavaScript で利用できるようにする

私は 1 の方に傾いています。.js をテーマの許可拡張子に追加するのは些細な変更であり、自動 CSP も不可能ではないはずです。

@pmusaraj / @Johani / @Osama さん、ご意見をお聞かせください。

The ability to reference theme uploads in JS is a great addition! :100:

This makes a lot of sense to me because anything you can do in a .js file can already be done in files in the javascripts folder of the theme. So, I don’t see any harm in allowing themes to have .js uploads by default.

テーマの js アップロードを許可するように CSP を教えることだけが残っています。js ファイルは、しばらくの間、テーマのアップロードとしてデフォルトで許可されています。

テーマの js アップロードが CSP によってブロックされない場合、Image Annotator - Allows you to annotate images in the previewer のようなコンポーネントは、ホームページで依存関係を読み込む必要がなくなります(gzip で約 170kb)。たとえば、そのコンポーネントは、コンポーザーが開かれた場合にのみ、それらの依存関係を読み込む必要があります。さらに、匿名ビューアには決して読み込む必要はありません。

また、この変更により、テーマはメインスレッドからオフで重い処理を実行できる Web Worker ファイルを持つことができるようになります。

上記の引用で「許可」したのは、それらをブロブとして持つことができるためですが、文字列で JavaScript をいじるよりも、個別のファイルで持つ方がはるかに便利です。

良いニュースがあります。実装は少し複雑ですが、ついにテーマコンポーネントでWebワーカーを使用したい場合のために、ローカルJSアセットのサポートを開始します。

CSPを回避する他のクリーンな方法はありません。ワーカーファイルを同じドメインから提供することで、この巨大な問題を解決します。

注…これは現在出荷されています :confetti_ball: