Stimulusを強化するStimulus-FXにデバッグ機能を追加しました(翻訳)
Stimulusには基本的なデバッグモードが備わっていて、これを用いればStimulusが動いているかどうかや、どのコントローラが初期化・接続されているかを表示できます。
しかしStimulusコントローラでの処理が増えてくると、もっと詳しい情報が必要になりますが、そのときにDOMとコントローラのロジックをいちいち手動で比較せずに済ませたいものです。
そこで、新しいデバッグ方法を模索した結果、私が最近公開したStimulus FXというツールにenableDebug
という新しい「FX」を追加しました。
これを使うと、以下のようにブラウザコンソールのデバッグ情報をより詳しく表示できます。
インストール後のセットアップは簡単です。
// app/javascript/controllers/application.js
+import { enableDebug } from "stimulus-fx"
-const application = Application.start()
+const application = enableDebug(Application.start())
上のセットアップが終わったら、デバッグを有効にしたいStimulusコントローラごとに以下を追加します。
export default class extends Controller {
+ static debug = true
+
// ...
}
このデバッグ機能は、実現可能性をチェックするための実験的機能として追加したものです。本機能のリリースによって、どなたかがこの機能をさらに自分で拡張するきっかけになれば幸いです。私自身もいくつかのアイデアを暖めていますが、皆さんに影響を与えるつもりはありません。
🔗 しくみ
このデバッグ機能のコードは驚くほどシンプルです(#8)。このコードは、application
を引数として受け取るenableDebug
という関数を作成しています(enableDebug(Application.start())
という使い方を見ればおわかりでしょう)。
import { debuggable } from "./debuggable";
import { initialize } from "./enableDebug/initialize";
import { values } from "./enableDebug/values";
import { targets } from "./enableDebug/targets";
export function enableDebug(application) {
const debugFeatures = [
initialize,
targets,
values
];
return debuggable(application, { with: debugFeatures });
}
読みやすいコードですよね?「(デバッグ)機能を持つデバッグ可能なアプリケーションを返す」コードであることは一目瞭然です。
次のdebuggable
関数は、指定の機能をfeatures.forEach(feature => feature(identifier, callbacks));
でループし、識別子とコールバックを渡してその機能を呼び出します。
debuggable
のinitialize
関数は以下のようにシンプルです。
export function initialize(identifier, callbacks) {
const debugCallback = ({ for: lifecycle }) => ({
log(context) {
console.group(`#${lifecycle}`);
console.log("details:", {
application: context.application,
identifier,
controller: context,
element: context.element
});
console.groupEnd();
}
});
["initialize", "connect", "disconnect"].forEach(lifecycle => {
callbacks[lifecycle].push(function() {
debugCallback({ for: lifecycle }).log(this);
});
});
}
このコードの読みやすさについても強調しておきたいと思います。「for: lifecycle
を指定してデバッグコールバック(debugCallback
)を呼び出している」のがおわかりですよね?
この構文は、私の近著『JavaScript for Rails Developers』全体を通じて提唱している方法を体現しています。
values
関数とtargets
関数はもう少しだけ複雑です。まずはsrc/enableDebug/values.jsのコードを見てみましょう。
export function values(identifier, callbacks) {
callbacks.connect.push(function() {
logValues({ on: this.element, for: identifier });
});
}
function logValues({ on: element, for: identifier }) {
const values = allValues(element, identifier);
if (Object.keys(values).length === 0) return;
console.group("Values");
console.table(values);
console.groupEnd();
}
function allValues(element, identifier) {
const prefix = `${identifier}-`;
const dataPrefix = "data-";
const valueSuffix = "-value";
// 要素や識別子から値を取得するロジックをここに書く
}
values
関数はlogValues
からの結果をpush
しています。src/debuggable.jsで定義されているconnect
配列には、console.table
のグループ化の結果が置かれます(targets
関数についても同様です)。
export function debuggable(application, { with: features }) {
// ...
const callbacks = {
initialize: [],
connect: [],
disconnect: []
};
// ...
}
src/debuggable.jsにあるこれらの関数は、続いてStimulusコントローラのconnect
(および他のライフサイクルメソッド)内で呼び出されます。
export function debuggable(application, { with: features }) {
// ...
controller.prototype.connect = function() {
callbacks.connect.forEach(hook => hook.call(this));
// ...
};
}
以上で解説はおしまいです。
本記事の簡単なツアーが、stimulus-fxの機能を自分で拡張してみたい方にお役に立てば幸いです。このデバッグ機能が多くの人に支持されて関心が深まれば、Stimulus本体に移植されるかもしれません。
不明な点がありましたら、お気軽にお問い合わせください。
概要
元サイトの許諾を得て翻訳・公開いたします。
日本語タイトルは内容に即したものにしました。