Minify HTML and JavaScript

Shrink a document with the round-trip-safe Minify layout, and minify the inline <script> content it carries with minify_js().

Minify the output

Set the Html config’s layout field to a Minify to shrink the markup. Every transform is round-trip safe: the minified bytes reparse to the same tree, so minifying never changes meaning. The four flags fold insignificant whitespace, omit the start/end tags the WHATWG rules make optional, drop redundant attribute quotes, and strip comments; all default on. Because layout holds one mode, a Minify and an Indent cannot be combined.

import turbohtml
from turbohtml import Html, Minify

doc = turbohtml.parse(
    "<html><head><title>Hi</title></head><body><p class='lead'>one</p>  <p>two</p><!--note--></body></html>"
)
print(doc.serialize(Html(layout=Minify())))
print(doc.serialize(Html(layout=Minify(omit_optional_tags=False, collapse_whitespace=False))))
<title>Hi</title><p class=lead>one</p> <p>two
<html><head><title>Hi</title></head><body><p class=lead>one</p>  <p>two</p></body></html>

Whitespace-significant elements (pre, textarea, listing) and raw-text elements (script, style) keep their content verbatim, and a tag is never dropped when omitting it would let the reparse reconstruct a formatting element across the boundary.

When the input is a string rather than a built tree – the minify-html and htmlmin use case – turbohtml.clean.minify() parses and minifies in one call, taking the same Minify options:

from turbohtml.clean import minify

print(minify("<html><head><title>Hi</title></head><body><p class='lead'>one</p>  <p>two</p><!--note--></body></html>"))
<title>Hi</title><p class=lead>one</p> <p>two

Minify JavaScript

minify_js() minifies a JavaScript string on its own. It always folds whitespace, comments and number literals — /*! ... */ bang comments and any @license / @preserve comment are the exception, kept byte-exact as a leading banner so a license header survives, exactly as the CSS minifier keeps them. A JSMinify toggles the two heavier passes — mangle renames local bindings to short names and fold runs constant folding and dead-code elimination. Top-level names are global, so they are never renamed; only bindings local to a function are. A construct the parser does not handle raises ValueError rather than passing through silently.

from turbohtml.clean import minify_js, JSMinify

source = "function f(x) { var half = x / 2; return half * half; }"
print(minify_js(source))
print(minify_js(source, JSMinify(mangle=False)))
function f(b){var a=b/2;return a*a}
function f(x){var half=x/2;return half*half}

Inline <script> minification rides on HTML minification: pass a JSMinify as Minify’s minify_js (the default None leaves scripts untouched). Only scripts the type attribute marks as JavaScript are rewritten — a type="application/json" or importmap payload is left byte-for-byte — and a script the parser cannot handle is emitted verbatim, so one bad <script> never breaks the document.

import turbohtml
from turbohtml import Html, Minify
from turbohtml.clean import JSMinify

doc = turbohtml.parse("<p>hi<script>function plus(a, b) { return a + b; }</script>")
print(doc.serialize(Html(layout=Minify(minify_js=JSMinify()))))
<p>hi<script>function plus(b,a){return b+a}</script>