来週、テーマやコンポーネントで QUnit テストを実行可能にするこの PR をマージする予定です。これにより、Discourse によるテーマの JavaScript の処理/トランスパイル方法も変更されます。コアのコードを大幅に書き換えずに(それ自体が他の後方互換性のない変更をもたらす可能性もあります)、これらの変更を後方互換性のある形で行うことは非常に困難なため、サイトのアップグレード時にテーマやコンポーネントの JavaScript が機能しなくなる可能性があります。
この投稿では、テーマやコンポーネントに影響を与える可能性のある変更内容と、それに対処するために必要な手順について説明します。
1. <script type="text/discourse-plugin"> タグ内の JavaScript が 厳格モード 有効化で実行される
この変更は、<script type="text/discourse-plugin"> タグ内にない JS コードには影響しません。コードが通常の <script> タグ内にあるか、単体の .js ファイルにある場合は、この変更の影響を完全に受けないため安全です。
テーマやコンポーネントがこの変更の影響を受けるかどうかを判断する最も簡単な方法は、JS コードを 即時実行関数式 (IIFE) で囲み、コードの先頭に "use strict"; を追加することです。例えば、現在のテーマコードが以下のようになっているとします:
<script type="text/discourse-plugin" version="0.8.11">
a = 5;
console.log(a);
</script>
これを IIFE で囲むと、以下のようになります("use strict"; は厳格モードを有効にするために重要であり、厳格モード下でのコードの動作を確認するために必要です):
<script type="text/discourse-plugin" version="0.8.11">
(function() {
"use strict";
a = 5;
console.log(a);
})();
</script>
この操作後にコンポーネントが機能しなくなった場合、サイトをアップグレードした際に機能しなくなります。コードを修正するには、まず MDN ドキュメント の JavaScript 厳格モードについて読み、テーマやコンポーネントが厳格モードで禁止されている処理を行っていないか確認することを強くお勧めします。もし行っている場合は、禁止された処理を行わないようにコードをリファクタリングする必要があります。
最も一般的に発生するエラーは、var(または let/const)キーワードなしで変数を宣言した際の ReferenceError です。上記の例では、a = 5; という行が厳格モード下で ReferenceError 例外をスローします。これは var を追加するのを忘れたためです。修正後のコードは以下のようになります:
<script type="text/discourse-plugin" version="0.8.11">
(function() {
"use strict";
var a = 5;
console.log(a);
})();
</script>
テストや修正が完了したら、IIFE と "use strict"; の行を削除しても構いません。
2. テーマの JavaScript モジュールパスにテーマ ID がプレフィックスとして付与される
以前、テーマの JavaScript を複数のファイルに分割 できる新機能を実装しました。今回の変更について説明するために、この機能の仕組みについて少し補足します。
単体の JavaScript ファイルを含むテーマやコンポーネントが Discourse インスタンスにインストールされると、Discourse はすべての JavaScript ファイルをループ処理し、それぞれに対して JavaScript モジュールを作成します。各モジュールには一意の識別子(パス)が必要であるため、Discourse はファイルパス(若干の変更を加えたもの)をモジュールのパスとして使用します。
例えば、テーマやコンポーネントに javascripts/discourse/helpers/my-helper.js というファイルがある場合、Discourse はそのファイルに対してモジュールを作成し、パスとして discourse/helpers/my-helper を割り当てます。このモジュールには、元のファイル内の JavaScript がトランスパイルされたバージョンが含まれます。
モジュールの利点は、あるモジュールから別のモジュールへクラスや関数、オブジェクトなどをインポートできることです。例えば、my-helper から xyz という関数を他のモジュールにインポートするには、以下のような import 文を使用します:
// javascripts/discourse/controllers/my-theme-controller.js
import { xyz } from "discourse/helpers/my-helper";
来週マージ予定の PR は、テーマのモジュールパスにプレフィックスを追加します。したがって、上記の例では my-helper のパスが discourse/helpers/my-helper から discourse/theme-<theme_id>/helpers/my-helper に変更されます。これにより、モジュールパスが変更されるため、上記の import 文は機能しなくなります。これを修正するには、import 文内のパスを絶対パスから相対パスに変更するだけです:
// javascripts/discourse/controllers/my-theme-controller.js
import { xyz } from "../helpers/my-helper";
これで import 文が再び機能するようになります。この変更の影響を受けたコンポーネントの実際の例と修正方法は、以下の PR 1 と 2 を参照してください。
繰り返しになりますが、この変更の影響を受けるのは、テーマやコンポーネントが自身のモジュールからインポートを行っている場合のみです。コアモジュールからのインポートはこの変更の影響を受けません。
参考になれば幸いです。ご質問がございましたら、お気軽にお知らせください。