mediawiki-extensions-Popups/docs/adr/0010-replace-mustache-js-with-template-literals.md
Stephen Niedzielski 8ba5c0f773 Hygiene: make JSDoc configs consistent
Make the Popups, MobileFrontend, and MinervaNeue JSDocs consistent. For
Popups, specify package.json, readme, and default template options and
moved doc/ to docs/ and autogenerated JavaScript documentation from
doc/autogenerated to docs/js.

http://usejsdoc.org/about-configuring-jsdoc.html
http://usejsdoc.org/about-commandline.html
http://usejsdoc.org/about-configuring-default-template.html

Bug: T188261
Change-Id: I81e64f06265f1ecc4e2ee159deef9b204ea7e957
2018-07-23 14:45:14 -05:00

4.7 KiB

10. Replace Mustache.js with Template Literals

Date: 22/03/2018

Status

Accepted

Context

UI HTML templates are currently populated with variables by the mustache.js template system. This is consistent with other code in MediaWiki but adds an 8.1 KiB dependency* for functionality that is only used in a few places.

Given that ES6 template literals provide similar readability and are part of JavaScript itself, we considered this to be a favorable and sustainable alternative to Mustache templates. Additionally, although the usage of template strings requires transpilation, adding transpiling support enables other ES6 syntaxes to be used such as let / const, arrow functions, and destructuring, all of which are considered language improvements that Extension:Popups can leverage in many areas.

We used the Babel transpiler with babel-preset-env to translate only the necessary JavaScript from ES6 to ES5 for grade A browsers. The overhead for this functionality is nonzero but expected to diminish in time and always be less than the size of the Mustache.js dependency. Please note that while most ES6 syntaxes are supported, the transpiler does not provide polyfills for new APIs (e.g., Array.prototype.includes()) unless configured to do so via babel-polyfill. As polyfills add more overhead and are related but independent of syntax, API changes were not considered in this refactoring.

We also realized that manual HTML escaping of template parameters would be a necessary part of this change. This functionality is built into the double-curly brace syntax of mustache.js but is now performed using mw.html.escape(). These calls are a blemish on the code but appear only in the templates themselves and would be replaced transparently in a UI library such as Preact which leverages JSX. We also anticipate that the template literal syntax would transition neatly to JSX. We don't know that Extension:Popups will ever want to use a UI library and accept these shortcomings may always exist.

*As reported by mw.loader.inspect() on 22/03/2018.

Decision

We compared the sizes before (d35286a) and after (4281670) transpiling and they proved favorable:

Commit index.js (gzip) index.js ext.popups ext.popups.main ext.popups.images mediawiki.template.mustache Total
Before 10.84 KiB 32.88 KiB 96 B 52.5 KiB 3.1 KiB 8.1 KiB 65224B
After 11.46 KiB 35.15 KiB 96 B 52.7 KiB 3.1 KiB 0.0 KiB 57193B

Where "index.js (gzip)" is the minified gzipped size of the resources/dist/index.js Webpack build product as reported by bundlesize, "index.js" is the minified uncompressed size of the same bundle as reported by source-map-explorer and Webpack performance hints, and the remaining columns are the sum of minified uncompressed JavaScript and CSS sizes for each relevant module as reported by mw.loader.inspect() with the last column being a total of these inspect() modules.

The conclusions to draw from this table are that transpilation does increase the size of the Webpack bundle but that the overhead is less than that of the Mustache.js dependency so the overall effect is a size improvement. Additionally, note that the transpiled bundle now encompasses the HTML templates which source-map-explorer reports as contributing a 2.53 KiB minified uncompressed portion of the 35.15 KiB bundle. (Previously, templates were an additional request.) Allowing for rounding errors, this brings the approximate overhead of enabling transpilation to nearly zero, 35.15 KiB - 32.88 KiB - 2.53 KiB ≈ 0, which suggests transpiling as a viable solution for improving code elsewhere that must be written in modern form without compromising on compatibility or performance.

Consequences

ES6 syntax is enabled without changing existing device support and 7.8 KiB less of minified uncompressed JavaScript is delivered.