Documentation
¶
Overview ¶
Command flags gates blocks of markup on feature flags, removing what is off.
<div data-flag="new-checkout">…</div> <div data-flag="new-checkout && !legacy-cart">…</div>
The expression is the interesting half of the decision and the accounting is the interesting half of the rewrite.
An expression is a flag name, "!" for not, "&&" and "||" with the usual precedence, and parentheses. It is evaluated at the start tag, which is possible because a flag's value is a property of the request rather than of the page - the same reason the A/B program can pick its variant before the document begins.
Everything unknown fails closed. A flag the configuration does not have is off, and an expression that does not parse is off, and both are counted rather than waved through: a page naming a flag nobody has heard of is either stale markup or an unreleased feature, and showing it is only ever the wrong guess. -strict turns a malformed expression into an error, for a build rather than a request.
The accounting is where this program needs something the library only gives to one kind of handler. It reports how much visible text was dropped, and a text handler cannot tell that the text it is being handed is inside an element another handler has removed: lolhtml.TextChunk.IsRemoved reports the chunk's own removal and nothing else. lolhtml.Element.IsRemoved does answer for an ancestor, so the element handler keeps a depth counter and the text handler asks it. That the counter comes back down is not obvious either: an element inside a removed one still gets its end-tag callback, which is what makes it work at all, and where the removed element has no end tag of its own the callback arrives on the enclosing element's - late, and inside the removal, so the text it is late for was going to be dropped anyway.
The same guard is what stops a nested gate counting twice. A block gated on an off flag inside a block gated on another off flag is dropped once, by the outer removal; the inner handler still runs, and asking IsRemoved is how it knows the difference between "I dropped this" and "this was already gone".
One hazard this shares with examples/gip/abtest, and which cannot be avoided by either: removing an element removes everything up to the token that closed it, so a gated block that the document left unclosed takes its neighbours with it. The removal is decided at the start tag and the end tag arrives afterwards, so all that can be done is to notice - the end-tag name test - and count it.