--- title: KumaScript slug: MDN/Tools/KumaScript tags: - Guide - Kuma - KumaScript - MDN Meta - NeedsContent - Site-wide - ガイド - ツール translation_of: MDN/Tools/KumaScript ---
MDN を動かしている Yari プラットフォームでは、コンテンツの特定の側面を自動化するために、 KumaScript と呼ばれる古くからのテンプレート/マクロシステムがまだ利用可能です。このシステムの使用をいつか中止したいと考えていますが、それまでは MDN はこのシステムに依存しています。この記事では、 KumaScript の使用に関する基本的な情報を提供します。
KumaScript は次のことは行いません。
KumaScript は MDN で埋め込み JavaScript テンプレートに利用されています。これらのテンプレートは MDN の筆者ならば誰でも文書内で、マクロを使用して呼び出すことができます。
KumaScript のスクリプトはテンプレートであり、それぞれのテンプレートは Github のKumaScript リポジトリーの macros ディレクトリーに格納されているファイルです。テンプレートは以下のようなものです。
<% for (let i = 0; i < $0; i++) { %> Hello #<%= i %> <% } %>
テンプレートの呼び出しにはマクロを使用します。上記のテンプレートを呼び出すと、次のようになります (macros ディレクトリに hello.ejs
というファイル名で保存されている場合)。
\{{hello(3)}}
マクロの出力は以下のようなものです。
Hello #0 Hello #1 Hello #2
マクロの呼び出しは、次のような一般的な形を取ります。
\{{templateName("arg0", "arg1", ..., "argN")}}
マクロの構文は、以下の規則に基づいて構成されます。
\{{
と \}}
の文字です。半実験的な機能 (動作保証なし) として、以下のように引数が一つだけの場合は、引数に JSON オブジェクトを指定できます。
\{{templateName({ "Alpha": "one", "Beta": ["a", "b", "c"], "Foo": "https:\/\/mozilla.org\/" })}}
このマクロからのデータは、テンプレートコード内で $0
引数のオブジェクトとして利用できます (例えば、 $0.Alpha
, $0.Beta
, $0.Foo
)。これにより、引数の単純なリストで実現することが難しい又は不可能な複雑なデータ構造を、マクロ引数で表すことができます。
なお、この引数の形はとても繊細です。 — 正確に JSON の構文に従っていなければならず、間違いを犯しやすいエスケープ文字の要件が求められます (例えば、すべてのスラッシュをエスケープするなど)。疑わしい場合は、 JSON をバリデーターに掛けてみてください。
"\{{
" という文字の並びはマクロの開始を示すため、実際にページ内で "\{{
" および "}}
" を使用したい場合は問題になります。おそらく DocumentParsingError
メッセージが発生するでしょう。
この場合、 \\{
のように最初の中括弧をバックスラッシュでエスケープすることができます。
それぞれの KumaScript テンプレートは、 KumaScript の macros ディレクトリに格納されているファイルです。これらのファイルは GitHub 上の何らかのオープンソースプロジェクトのファイルとして作成したり編集したりします。
KumaScript テンプレートは、いくつかの簡単な規則で、組み込み JavaScript テンプレートエンジンによって処理されます。
$0
, $1
, $2
, などのような変数として利用できます。引数のリスト全体は、テンプレート内で変数 arguments
として利用できます。<%= expr %>
— 出力される前に HTML エスケープされた JavaScript の式の値 (<
および >
は <
および >
となる)<%- expr %>
— エスケープされない JavaScript の式の値 (マークアップを動的に構築したり、マークアップを含むことができる他のテンプレートの結果に使用してください)<%
と %>
の間は JavaScript として解釈される。すなわち、ループ、条件文、関数などを含めることが可能。<% %>
ブロック内は出力ストリームに含まれない。ブロック内の変数などをブロック外で使用する場合は、以下のように <%= %>
を用いる。
<% for (var i = 0; i < $0; i++) { %> Hello #<%= i %> <% } %>
JavaScriptのコードは <% ... %>
に含まれており、 %> ... <%
の間に現れるものは出力されることに注意してください。 JS の for
ループは、1つ目の <% %>
ブロックで始まり、出力モードに移行して、2つ目の <% %>
の JS ブロックで終わらせることができます。
KumaScript には前章までに紹介したもの以外に、高度な機能もあります。
Wiki が KumaScript サービスを呼び出す際には、KumaScript がテンプレートに変数として利用できるようにする、現在の文書に関するいくつかのコンテキストを渡します。
env.path
env.url
env.id
env.files
env.review_tags
env.locale
env.title
env.slug
env.tags
env.modified
env.cache_control
Cache-Control
ヘッダーで、現在の MDN 文書がリクエストされた際に送信されたものであり、キャッシュを無効にするかどうかの判断をするのに便利です個々の file オブジェクトは以下の様なフィールドを持ちます。
title
description
filename
size
author
mime
url
変数 env.tags
および env.review_tags
がタグの配列を返します。これらを使って作業するには、もちろんたくさんの方法がありますが、ここではいくつかを提案します。
ページに特定のタグが存在するかどうかを調べるには、このようにします。
if (env.tags.indexOf("tag") !== −1) { // The page has the tag "tag" }
次のようにして、ページのすべてのタグを反復処理するようにすることもできます。
env.tag.forEach(function(tag) { // do whatever you need to do, such as: if (tag.indexOf("a") === 0) { // this tag starts with "a" - woohoo! } });
KumaScript は、 KumaScript マクロ用にいくつかの組み込みメソッドと API を提供します。マクロは、 module.exports
を使用して新しい API メソッドをエクスポートすることもできます。
この文書は手動で管理されているので、コードとともに古くなってしまう可能性があります。この点を考慮して、KumaScript のソースにある組み込み API の最新状態を常にチェックしましょう。しかし、ここではテンプレートに公開されている便利なメソッドの一部を紹介します。
md5(string)
template("name", ["arg0", "arg1", ..., "argN"])
指定されたテンプレートを、指定された引数のリストを使って実行し、その結果を返します。
例: <%- template("warning", ["foo", "bar", "baz"]) %>
.
DOMxRef
マクロを使用した例: <%- template("DOMxRef", ["Event.bubbles", "bubbles"]) %>
.
これは JavaScript の関数です。ですから、引数が $2 のような引数の変数である場合は、引用符で囲まないでください。 <%- template("warning", [$1, $2, "baz"]) %>
のようにします。コードのブロック内から他のテンプレートを呼び出す必要がある場合は、 <%
... %>
を使用しないでください。例えば myvar = "<li>" + template("LXRSearch", ["ident", "i", $1]) + "</li>";
のようにします。
require(name)
module.exports
に代入されたものをすべて返します。<% const my_module = require('MyModule'); %>
のように使用されます。cacheFn(key, timeout, function_to_cache)
env.cache_control
の値を尊重し、ログイン中のユーザーが Shift を押しながら更新を押すことで送信される no-cache
でキャッシュを無効化します。request
request/request
にアクセスします。 KumaScript テンプレート内でこのモジュールを使用するにはあまり使い勝手が良くないので、簡単にするためにモジュール API の中でラップすると良いでしょう。log.debug(string)
環境スクリプトによって自動的に読み込まれ、すべてのテンプレートで利用可能になる一連の組み込み API があます。 MDN が何年も前に依存していた古い DekiScript システムからいくつかの機能を提供しています。
これらは、テンプレート間で共通の変数やメソッドを共有するために使用されます。
kuma.*
- KumaMDN.*
- MDN:Commonpage.*
- DekiScript:Pagestring.*
- DekiScript:Stringuri.*
- DekiScript:Uriweb.*
- DekiScript:Webwiki.*
- DekiScript:Wiki他に利用可能な API には以下のようなものがあります。
kuma.inspect(object)
log.debug()
と共に良く使用します。 node.js の util.inspect()
も参照してください。kuma.htmlEscape(string)
&, <, >, "
の文字を &, <, >, "
にそれぞれエスケープします。kuma.url
url
モジュールも参照してください。kuma.fetchFeed(url)
InsertFeedLinkList
も参照してください。組み込みの require()
メソッドを使って、テンプレートをモジュールとして読み込み、テンプレート間で共通の変数やメソッドを共有することができます。モジュールはテンプレートの中で次のように定義できます。
<% module.exports = { add: function (a, b) { return a + b; } } %>
このテンプレートが macros ディレクトリーに MathLib.ejs
として保存されていたとすると、次のように別のテンプレートで使用することができます。
<% var math_lib = require("MathLib"); %> The result of 2 + 2 = <%= math_lib.add(2, 2) %>
このテンプレートの出力は以下の様になるでしょう。
The result of 2 + 2 = 4
デバッグ時に役立つヒントです。 log.debug()
メソッドを使うと、テンプレートを実行しているページの上部にあるスクリプトメッセージ領域にテキストを出力することができます。ただし、これらのメッセージはすべてのユーザーが見ることができるため、デバッグが終わったら確実に削除する必要があります。これを使うには、次のようにしてください。
<%- log.debug("テキストはこちら"); %>
もちろん、役立つのであれば、スクリプトコードを使ってより複雑な出力を作ることもできます。
KumaScript テンプレートは、パフォーマンスを向上させるために深くキャッシュされます。ほとんどの場合、これは頻繁に変更されないコンテンツを提供するのに適しています。しかし、ログインしたユーザーがスクリプトの問題に気づいたときに、ページを強制的に再生成するための 2 つのオプションがあります。
Cache-Control: max-age=0
ヘッダーのリクエストを発行することで、現在のページのコンテンツのキャッシュを無効にします。Cache-Control: no-cache
ヘッダー付きのリクエストを発行することで、 KumaScript は、現在のページ、および現在のページで使用されているテンプレートやコンテンツのキャッシュを無効にします。このセクションでは、MDN で使用されるテンプレートの一般的なパターンの例をリストアップします。これには、古い DekiScript テンプレートと新しい KumaScript の同等品のサンプルがあります。
繰り返しになりますが、ページで使用されているテンプレートを編集後に強制的に再読み込みさせるには、Shift を押しながら再読み込みします。再読み込みを使用するだけで、ページのコンテンツが再生成されますが、キャッシュされたテンプレートやインクルードされたコンテンツが使用されます。ページ自体のコンテンツ以外のキャッシュを無効にするには、 Shift を押しながらの再読み込みが必要です。
時々、ページ読込時に、次のようなスクリプティングメッセージが表示されることがあります。
Kumascript service failed unexpectedly: <class 'httplib.BadStatusLine'>
これはおそらく、 KumaScript サービスの一時的な障害です。ページを更新すると、このエラーは消えるかもしれません。それがうまくいかない場合は、Shift を押しながら再読み込みを試してみてください。何度か試してみてもエラーが消えない場合は、 Mozilla Developer Network に IT バグを提出して調査を依頼してください。
いくつかのページで、以下の様なスクリプトエラーメッセージを見かける場合があるでしょう。
Syntax error at line 436, column 461: Expected valid JSON object as the parameter of the preceding macro but...
その様なページを編集状態にした場合、ページ下部に以下の様なマクロが見つかるかもしれません。
\{{ wiki.languages({ "zh-tw": "zh_tw/Core_JavaScript_1.5_教學/JavaScript_概要", ... }) }}
この問題を解決するには、マクロを削除してください。または、情報を温存するために次のように両側の中括弧を HTML コメント <!-- -->
で置き換えてください。
<!-- wiki.languages({ "zh-tw": "zh_tw/Core_JavaScript_1.5_教學/JavaScript_概要", ... }) -->
Kuma は様々な方法でローカライズに対応しているため、これらのマクロは実際にはもう必要ありません。しかし、ローカライズされたページ間の関係を再検討する必要がある場合に備えて、これらのマクロはそのまま残されています。残念ながら、マイグレーションではいくつかのマクロを正しく変換できなかったようです。
KumaScript では、現在の文書のロケールは環境変数から取得することができます。
const lang = env.locale;
変数 env.locale
は信頼すべきものであり、すべての文書で定義されています。
以下のように mdn.getFileContent()
関数を用いることで、添付ファイルの内容を読み取ることができます。
<% let contents = mdn.getFileContent(fileUrl); // ... do stuff with the contents ... %>
または
<%- mdn.getFileContent(fileObject); %>
つまり、読み込むファイルのURLを指定する場合と、ファイルオブジェクトとして指定する場合があります。あるページのファイルオブジェクトは、配列 env.files
を通じてアクセスできます。ですから、例えば、記事に添付された最初のファイルの内容を埋め込むには、次のようにします。
<%- mdn.getFileContent(env.files[0]); %>
注: テキスト以外のファイルのコンテンツをこの方法で埋め込もうとすると、生のコンテンツがテキストとして注入されてしまうので、おそらく避けた方がいいでしょう。これは、テキストの添付ファイルの内容にアクセスするためのものです。
ファイルが見つからない場合は、空の文字列が返されます。現在のところ、空のファイルと存在しないファイルの違いを見分ける方法はありません。しかし、もし空のファイルを wiki に置いているのであれば、それは間違っています。
テンプレートは wiki ページのように翻訳されるわけではなく、 1 つのテンプレートをいくつものロケールで使用することになります。
そのため、現在の文書のロケールに合わせてコンテンツを出力する主な方法は、 env.locale
の値に基づいています。これには様々な方法がありますが、従来の DekiScript テンプレートの変換にはいくつかのパターンがあります。
これに相当する KumaScript では、以下のような単純な if/else ブロックで実現できます。
<% if ("fr" == env.locale) { %> <%- template("CSSRef") %> « <a href="/fr/docs/Référence_CSS/Extensions_Mozilla">Référence CSS: Extensions Mozilla</a> <% } else if ("ja" == env.locale) { %> <%- template("CSSRef") %> « <a href="/ja/docs/CSS_Reference/Mozilla_Extensions">CSS リファレンス: Mozilla 拡張仕様</a> <% } else if ("pl" == env.locale) { %> <%- template("CSSRef") %> « <a href="/pl/docs/Dokumentacja_CSS/Rozszerzenia_Mozilli">Dokumentacja CSS: Rozszerzenia Mozilli</a> <% } else if ("de" == env.locale) { %> <%- template("CSSRef") %> « <a href="/de/docs/CSS_Referenz/Mozilla_CSS_Erweiterungen">CSS Referenz: Mozilla Erweiterungen</a> <% } else { %> <%- template("CSSRef") %> « <a href="/en-US/docs/CSS_Reference/Mozilla_Extensions">CSS Reference: Mozilla Extensions</a> <% } %>
どのテキストエディターがお気に入りであるかによりますが、ブラウザーベースのエディターからコピー&貼り付けして、一連の検索/置換正規表現を使ってこのパターンを攻略すれば、大体のことは解決できるでしょう。
私が愛用しているエディターは MacVim ですが、次の一連の正規表現を使えば、後は手動でちょっとしたクリーンアップをするだけで、大部分の作業が完了します。
%s#<span#^M<span#g %s#<span lang="\(.*\)" .*>#<% } else if ("\1" == env.locale) { %>#g %s#<span class="script">template.CSSxRef(#<%- template("CSSxRef", [# %s#)</span> </span>#]) %>
また、テンプレートごとにパターンが微妙に変わることもあります。そのため、移行スクリプトで自動的に処理することができなかったのだと思います。
mdn.localString()
を使用すると、次のようにロケールに応じて異なる文字列が定義されます。
<% var s_title = mdn.localString({ "en-US": "Firefox for Developers", "de": "Firefox für Entwickler", "es": "Firefox para desarrolladores" }); %> <span class="title"><%= s_title %></span>
オブジェクトに適切なロケールが無い場合、 "en-US" の値が初期値として使用されます。