--- title: KumaScript slug: MDN/Tools/KumaScript tags: - Guide - Kuma - KumaScript - MDN - MDN Meta translation_of: MDN/Tools/KumaScript ---
Sur la plate-forme Kuma qui propulse MDN, le système de modèles pour automatiser certains aspects du contenu sur le wiki est appelé KumaScript. KumaScript repose sur du JavaScript côté serveur implanté à l'aide de Node.js. Cet article vous fournit des informations basiques sur l'utilisation de KumaScript.
Pour un aperçu détaillé et des questions et réponses sur KumaScript, visitez la discussion Fireside KumaScript de l'équipe de développement de MDN. KumaScript a remplacé DekiScript, qui était le langage modèle pour MindTouch, la précédente plate-forme utilisée par MDN.
KumaScript fonctionne en permettant aux utilisateurs MDN de confiance d'écrire des modèles JavaScript embarqués. Ces modèles peuvent être invoqués dans le contenu du document par tout auteur MDN via l'usage de macros.
Un script en KumaScript est un modèle, et chaque modèle est un fichier du dépôt Github. Un modèle ressemble à ceci :
<% for (var i = 0; i < $0; i++) { %> Hello #<%= i %> <% } %>
Invoquer un modèle se fait avec un macro, pouvant être utilisé à tout endroit dans toute page wiki. Un macro ressemble à ceci :
\{{ hello("3") }}
La sortie du macro ressemble à :
Hello #0 Hello #1 Hello #2
Les modèles KumaScript sont invoqués dans le contenu d'un document avec des macros comme ceci :
\{{ templateName("arg0", "arg1", ..., "argN") }}
La syntaxe des macros consiste en quatre règles :
\\{{
et se terminent avec }}
.En tant que fonctionnalité semi-expérimentale (il n'est pas garantie qu'elle fonctionne), vous pouvez fournir un objet JSON pour le premier paramètre uniquement, par exemple :
\\{{ templateName({ "Alpha":"one", "Beta":["a","b","c"], "Foo":"http:\/\/mozilla.org\/" }) }}
Les données de ce macro sont disponibles dans le code du modèle en tant qu'objet à l'argument $0
(par exemple $0.Alpha
, $0.Beta
, $0.Foo
). Celà vous permet aussi d'exprimer des structures de données complexes dans les paramètres des macros qu'il serait difficile voire impossible d'exprimer comme une simple liste de paramètres.
Notee que ce style de paramètre est très strict — il doit correspondre exactement à la syntaxe JSON, qui a quelques contraintes sur l'échappement des caractères qu'il est aisé d'oublier (par exemple que toutes les barres obliques culbutées doivent être échapées). Si vous avez un doute, essayer de faire passer votre JSON dans un validateur.
Bien que la séquence de caractères "\\{{
" soit utilisée pour démarrer un macro, elle peut causer des problèmes si vous voulez seulement écrire "\\{{
" et "}}
" sur une page. Celà produira alors probablement des messages DocumentParsingError
.
Dans ce cas, vous pouvez échapper la barre oblique culbutée avec une seconde barre, tel que : \\\{{
Each KumaScript template is kept in a separate wiki page. Creating and editing these pages requires an elevated privilege, which MDN admins can grant to trusted editors.
KumaScript templates are processed by an embedded JavaScript template engine with a few simple rules:
$0
, $1
, $2
, and so on. The entire list of parameters is also available in a template as the variable arguments
.<%= expr %>
— the value of a JavaScript expression is escaped for HTML before being included in output (e.g., characters like <
and >
are turned into <
and >
).<%- expr %>
— the value of a JavaScript expression is included in output without any escaping. (Use this if you want to dynamically build markup or use the results of another template that may include markup.)<% %>
block is interpreted as JavaScript. This can include loops, conditionals, etc.<% %>
block can ever contribute to the output stream. But, you can transition from JS mode to output mode using <% %>
—for example:
<% for (var i = 0; i < $0; i++) { %> Hello #<%= i %> <% } %>
Note how the JavaScript code is contained in <% ... %>
, and output happens in the space between %> ... <%
. The for
loop in JS can begin with one <% %>
block, transition to output mode, and finish up in a second <% %>
JS block.
It's important to note that the standard JavaScript {{jsxref("Date")}} constructor is overridden by the KumaScript Date
interface. You can create a JavaScript Date
by calling the KumaScript date.now()
or date.parse()
function.
Beyond the basics, the KumaScript system offers some advanced features.
When the wiki makes a call to the KumaScript service, it passes along some context on the current document that KumaScript makes available to templates as variables:
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
header sent in the request for the current wiki document, useful in deciding whether to invalidate cachesEach file object has the following fields:
title
description
filename
size
author
mime
url
The env.tags
and env.review_tags
variables return arrays of tags. You can work with these in many ways, of course, but here are a couple of suggestions.
You can look to see if a specific tag exists on a page like this:
if (env.tags.indexOf("tag") != −1) { // The page has the tag "tag" }
You can also iterate over all the tags on a page, like this:
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 offers some built-in utility APIs, as well as the ability to define new APIs in modules editable as wiki documents.
This manually-maintained documentation is likely to fall out of date with the code. With that in mind, you can always check out the latest state of built-in APIs in the KumaScript source. But here is a selection of useful methods exposed to templates:
md5(string)
template("name", ["arg0", "arg1", ..., "argN"])
<%- template("warning", ["foo", "bar", "baz"]) %>
.domxref
macro: <%- template("domxref", ["Event.bubbles", "bubbles"]) %>
.<%- template("warning", [$1, $2, "baz"]) %>
. If you need to call another template from within a block of code, do not use <%
... %>
. Example: myvar = "<li>" + template("LXRSearch", ["ident", "i", $1]) + "</li>";
require(name)
module.exports
in the template is returned.<% var my_module = require('MyModule'); %>
.cacheFn(key, timeout, function_to_cache)
env.cache_control
to invalidate cache on no-cache
, which can be sent by a logged-in user hitting shift-refresh.request
mikeal/request
, a library for making HTTP requests. Using this module in KumaScript templates is not yet very friendly, so you may want to wrap usage in module APIs that simplify things.log.debug(string)
There's only one API built in at the moment, in the kuma
namespace. You can see the most up to date list of methods under kuma
from the KumaScript source code, but here are a few:
kuma.inspect(object)
log.debug()
. See also: node.js util.inspect()
.kuma.htmlEscape(string)
&, <, >, "
to &, <, >, "
, respectively.kuma.url
url
module.kuma.fetchFeed(url)
Template:InsertFeedLinkList
Using the built-in require()
method, you can load a template as a module to share common variables and methods between templates. A module can be defined in a template like this:
<% module.exports = { add: function (a, b) { return a + b; } } %>
Assuming this template is saved as /en-US/docs/Template:MathLib
, you can use it in another template like so:
<% var math_lib = require("MathLib"); %> The result of 2 + 2 = <%= math_lib.add(2, 2) %>
And, the output of this template will be:
The result of 2 + 2 = 4
There are a set of modules editable as wiki templates that are automatically loaded and made available to every template. This set is defined in the configuration file for the KumaScript service - any changes to this requires an IT bug to edit configuration and a restart of the service.
For the most part, these attempt to provide stand-ins for legacy DekiScript features to ease template migration. But, going forward, these can be used to share common variables and methods between templates:
mdn.*
- Template:MDN:CommonDate.*
- Template:DekiScript:DatePage.*
- Template:DekiScript:PageString.*
- Template:DekiScript:StringUri.*
- Template:DekiScript:UriWeb.*
- Template:DekiScript:WebWiki.*
- Template:DekiScript:WikiNote: You might notice that the DekiScript modules use a built-in method named buildAPI()
, like so:
<% module.exports = buildAPI({ StartsWith: function (str, sub_str) { return (''+str).indexOf(sub_str) === 0; } }); %>
The reason for this is because DekiScript is case-insensitive when it comes to references to API methods, whereas JavaScript is strict about uppercase and lowercase in references. So, buildAPI()
is a hack to try to cover common case variations in DekiScript calls found in legacy templates.
With that in mind, please do not use buildAPI()
in new modules.
A useful tip when debugging. You can use the log.debug()
method to output text to the scripting messages area at the top of the page that's running your template. Note that you need to be really sure to remove these when you're done debugging, as they're visible to all users! To use it, just do something like this:
<%- log.debug("Some text goes here"); %>
You can, of course, create more complex output using script code if it's helpful.
KumaScript templates are heavily cached to improve performance. For the most part, this works great to serve up content that doesn't change very often. But, as a logged-in user, you have two options to force a page to be regenerated, in case you notice issues with scripting:
Cache-Control: max-age=0
header.Cache-Control: no-cache
header.When using templates, it's common to open the template's code in a browser window to review the comments at the top, which are used to document the template, its parameters, and how to use it properly. To quickly access templates, you can create a Firefox search keyword, which gives you a shortcut you can type in the URL box to get to a template more easily.
To create a search keyword, open the bookmarks window by choosing "Show all bookmarks" in the Bookmarks menu, or by pressing Control-Shift-B (Command-Shift-B on Mac). Then from the utility ("Gear") menu in the Library window that appears, choose "New Bookmark...".
This causes the bookmark editing dialog to appear. Fill that out as follows:
The resulting dialog looks something like this:
Then click the "Add" button to save your new search keyword. From then on, typing your keyword, then a space, then the name of a macro will open that macro in your current tab. So if you used "t" as the keyword, typing t ListSubpages will show you the page at {{TemplateLink("ListSubpages")}}.
This section will list examples of common patterns for templates used on MDN, including samples of legacy DekiScript templates and their new KumaScript equivalents.
It bears repeating: To force templates used on a page to be reloaded after editing, hit Shift-Reload. Just using Reload by itself will cause the page contents to be regenerated, but using cached templates and included content. A Shift-Reload is necessary to invalidate caches beyond just the content of the page itself.
Sometimes, you'll see a scripting message like this when you load a page:
Kumascript service failed unexpectedly: <class 'httplib.BadStatusLine'>
This is probably a temporary failure of the KumaScript service. If you Refresh the page, the error may disappear. If that doesn't work, try a Shift-Refresh. If, after a few tries, the error persists - file an IT bug for Mozilla Developer Network to ask for an investigation.
On some pages, you'll see a scripting error like this:
Syntax error at line 436, column 461: Expected valid JSON object as the parameter of the preceding macro but...
If you edit the page, you'll probably see a macro like this at the bottom of the page:
\{{ wiki.languages({ "zh-tw": "zh_tw/Core_JavaScript_1.5_教學/JavaScript_概要", ... }) }}
To fix the problem, just delete the macro. Or, replace the curly braces on either side with HTML comments <!-- -->
to preserve the information, like so:
<!-- wiki.languages({ "zh-tw": "zh_tw/Core_JavaScript_1.5_教學/JavaScript_概要", ... }) -->
Because Kuma supports localization differently, these macros aren't actually needed any more. But, they've been left intact in case we need to revisit the relationships between localized pages. Unfortunately, it seems like migration has failed to convert some of them properly.
In KumaScript, the locale of the current document is exposed as an environment variable:
var lang = env.locale;
The env.locale
variable should be reliable and defined for every document.
You can read the contents of an attached file by using the mdn.getFileContent()
function, like this:
<% var contents = mdn.getFileContent(fileUrl); ... do stuff with the contents ... %>
or
<%-mdn.getFileContent(fileObject)%>
In other words, you may specify either the URL of the file to read or as a file object. The file objects for a page can be accessed through the array env.files
. So, for example, to embed the contents of the first file attached to the article, you can do this:
<%-mdn.getFileContent(env.files[0])%>
If the file isn't found, an empty string is returned. There is currently no way to tell the difference between an empty file and a nonexistent one. But if you're putting empty files on the wiki, you're doing it wrong.
Templates cannot be translated like other wiki pages. KumaScript only looks for templates in the en-US locale (i.e., /en-US/docs/Template:{name}
), and does not look for templates that have been translated to another locale (i.e., /fr/docs/Template:{name}
).
So the main way to output content tailored to the current document locale is to pivot on the value of env.locale
. There are many ways to do this, but a few patterns are common in the conversion of legacy DekiScript templates:
The KumaScript equivalent of this can be achieved with simple if/else blocks, like so:
<% if ("fr" == env.locale) { %> <%- template("CSSRef") %> « <a title="Référence_CSS/Extensions_Mozilla" href="/fr/docs/Référence_CSS/Extensions_Mozilla">Référence CSS:Extensions Mozilla</a> <% } else if ("ja" == env.locale) { %> <%- template("CSSRef") %> « <a title="CSS_Reference/Mozilla_Extensions" href="/ja/docs/CSS_Reference/Mozilla_Extensions">CSS リファレンス:Mozilla 拡張仕様</a> <% } else if ("pl" == env.locale) { %> <%- template("CSSRef") %> « <a title="Dokumentacja_CSS/Rozszerzenia_Mozilli" href="/pl/docs/Dokumentacja_CSS/Rozszerzenia_Mozilli">Dokumentacja CSS:Rozszerzenia Mozilli</a> <% } else if ("de" == env.locale) { %> <%- template("CSSRef") %> « <a title="CSS_Referenz/Mozilla_CSS_Erweiterungen" href="/de/docs/CSS_Referenz/Mozilla_CSS_Erweiterungen">CSS Referenz: Mozilla Erweiterungen</a> <% } else { %> <%- template("CSSRef") %> « <a title="CSS_Reference/Mozilla_Extensions" href="/en-US/docs/CSS_Reference/Mozilla_Extensions">CSS Reference:Mozilla Extensions</a> <% } %>
Depending on what text editor is your favorite, you may be able to copy & paste from the browser-based editor and attack this pattern with a series of search/replace regexes to get you most of the way there.
My favorite editor is MacVim, and a series of regexes like this does the bulk of the work with just a little manual clean up following:
%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>#]) %>
Your mileage may vary, and patterns change slightly from template to template. That's why the migration script was unable to just handle this automatically, after all.
Rather than switch between full chunks of markup, you can define a set of strings, switch them based on locale, and then use them to fill in placeholders in a single chunk of markup:
<% var s_title = 'Firefox for Developers'; switch (env.locale) { case 'de': s_title = "Firefox für Entwickler"; break; case 'fr': s_title = "Firefox pour les développeurs"; break; case 'es': s_title = "Firefox para desarrolladores"; break; }; %> <span class="title"><%= s_title %></span>
mdn.localString()
A recent addition to the Template:MDN:Common
module is mdn.localString()
, used like this:
<% 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>
This is more concise than the switch statement, and may be a better choice where a single string is concerned. However, if many strings need to be translated (e.g., as in CSSRef), a switch statement might help keep all the strings grouped by locale and more easily translated that way.
When the object does not have the appropriate locale, the value of "en-US" is used as the initial value.