A Node.js module that will minify HTML attribute class names, IDs, and data-* attributes in a coordinated fashion across your HTML, CSS, and JS files. This is useful if you want to minify your code even more than standard minifiers do, or if you want to obfuscate your code and make it harder to read, reverse engineer, or repurpose.
This module is a pre-minifier that focuses only on HTML class names, IDs, and data-* attributes. It will not minify the rest of your code. As such, this tool should be run on your code before you run it through your standard minifier(s). You could do it in the opposite order if you like, but the outputs from this module will not be minified even if the inputs were.
This module was built and is maintained by the Roosevelt web framework team, but it can be used independently of Roosevelt as well.
Documentation
-
This module renames:
classattribute values.idattribute values.data-*attribute names.- The values of any other attributes you list in the
renameAttributeValuesparam, the wayclassandidvalues are renamed. Those attributes' names are left alone.
The new names will be renamed to the shortest possible value, e.g. a, b, c, etc. Names are handed out in descending order of how often each one appears, so the names you use most get the shortest replacements.
-
This module then updates:
- In HTML files:
- Attributes that reference any IDs that have been renamed. Attributes that reference IDs are:
for,form,headers,itemref,list,usemap,aria-activedescendant,aria-controls,aria-describedby,aria-details,aria-errormessage,aria-flowto,aria-labelledby, andaria-owns. - In-page fragment links, e.g.
<a href="#someId">. Links that point anywhere else are left alone. <map name>, so thatusemapkeeps pointing at the right image map.- Inline CSS code in
<style>tags that references any renamed attributes. - Inline JS code in
<script>tags that references any renamed attributes. - Markup in
<script type="text/template">tags and similar. - Inline JS code in event handler attributes like
onclick,onmouseover, etc that references any renamed attributes.
- Attributes that reference any IDs that have been renamed. Attributes that reference IDs are:
- In CSS files and inline CSS code:
- Class and ID selectors, including inside functional pseudo-classes like
:not(),:is(), and:has(). - Attribute selectors:
[data-*]attribute names, and the values in[class="…"],[class~="…"],[id="…"], and anyrenameAttributeValuesattributes you configured. - Selectors reached through
@extend.
- Class and ID selectors, including inside functional pseudo-classes like
- In JS files and inline JS code:
- Selector strings passed to
querySelector,querySelectorAll,closest, andmatches. getElementByIdandgetElementsByClassNamearguments.classList.add,.remove,.toggle,.contains, and.replacearguments.classNameandclassList.valueassignments.idassignments.data-*attribute names passed togetAttribute,setAttribute,removeAttribute,hasAttribute, andtoggleAttribute, plus theclassandidvalues passed tosetAttribute.element.dataset.someNameandelement.dataset['some-name'].document.forms.someFormId.- Inline HTML in the JS assigned to
innerHTMLorouterHTML, or passed toinsertAdjacentHTMLordocument.write. - Inline CSS in the JS passed to
CSSStyleSheet.replaceSyncorinsertRule. - The implicit global variables browsers create for elements with an
id. - Any of the above assembled out of template literals, string concatenation, or a variable holding a string literal.
- Selector strings passed to
- In HTML files:
The renames this module can make are limited to the references it can actually see. In particular:
- Names assembled at runtime cannot be followed. If your code does
element.id = prefix + suffixor your template writesclass="{someVariable}", this module has no way to know what the resulting name will be, so it leaves it alone. Values containing template syntax are skipped rather than guessed at. - JS strings are only rewritten where the context proves they are selectors. A string that merely happens to match a class name like a module path, a cookie name, a sentence, a media query is left alone.
- Stylesheets outside your
cssDirare not renamed. If you@importa third party stylesheet fromnode_modules, the class names it defines cannot be renamed in it, while your HTML using those classes would be. Point theexemptStylesheetsparam at those files and every name they define is left alone on your side of the fence too. - Preprocessor selectors built with
&cannot be renamed. In Less and Sass,.block { &-element { … } }produces a.block-elementclass that never appears literally in your source. This module warns when it sees this pattern and leaves the selector alone. - Less and Sass mixins are left alone, since
.mixin()is a function call rather than a selector. - Substring attribute selectors are left alone.
[data-x^="foo"]has to keep matching every value that starts withfoo, and this module cannot know what those are. The attribute name is still renamed; the value is not. - The indented Sass syntax (
.sass) is not supported, only the braced dialects.
When this module cannot safely rename something, it says so rather than guessing. Pass an onWarning callback to hear about it.
If you find an edge case this module doesn't handle yet, file an issue, or better yet submit a pull request with a failing test for the scenario you would like to work. Or even better submit a PR with the code fix too!