modulesplit

command
v0.2.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Aug 28, 2026 License: BSD-3-Clause Imports: 11 Imported by: 0

Documentation

Overview

Command modulesplit turns a classic script into a module-and-fallback pair.

modulesplit -module-suffix .mjs page.html
<script type="module" src="/a.mjs"></script>
<script src="/a.js" nomodule defer></script>

A browser with module support runs the first and ignores the second; one without does the opposite. The pair has to be exact about which is which, because a browser that ran both would run the page's code twice.

The original element is turned into the module and a copy is inserted after it as the fallback, rather than the other way round, so that the module comes first in source order - which is what a reader expects and what keeps the nomodule copy from being the one an old browser starts fetching first.

Copying an element means re-emitting its attributes into markup this program builds, and that is the part worth reading. Everything the library reports is raw attribute source, so:

the attribute value as reported   /a.js?x=1&amp;y=2
EscapeAttribute of it             /a.js?x=1&amp;amp;y=2   wrong: a different url
decoded, then EscapeAttribute     /a.js?x=1&amp;y=2       right

The middle line is the obvious call and it silently changes the URL. So the copy decodes each value and escapes the result, which is what the documentation on EscapeAttribute prescribes for a value read from the document.

Decoded by decodeAttribute below, not by html.UnescapeString, and that is the second half of the same trap. In an attribute value a named reference without its semicolon is not a reference when the next character is "=" or ASCII alphanumeric, and the standard library decodes it anyway - so "/a.js?x=1&copy=2" comes back with a copyright sign in it and the pair fetches a URL the document never named. Element.Attribute states the rule.

The same rule applies going the other way, and this program had it wrong first. SetAttribute takes attribute source, so a decoded value has to be escaped before it is written back: a src of "?a=1&amp;lt=2" decodes to "?a=1&lt=2", and writing that raw reads back as "?a=1<=2", because "&lt" is a character reference with or without its semicolon.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL