Repository navigation
Releases: lukeed/html
Release list
v1.0.2
Fixes
Most of these were reported in #3 and #4.
Parsing
- A stray quote in a tag, like
<a href="/x"">, no longer turns the rest of the page into text. Tags follow the attribute rules of htmlparser2: a quote starts a value only after=. (877ff5a) - An unquoted value can end with
/, so<svg><path d=M0/>is not self-closing, like in htmlparser2 and browsers. (877ff5a) - The tokenizer follows browsers at the edges: (e1f66fb)
- An unfinished tag at the end of the source is dropped, not kept as text.
- Only
<!doctype …>is a directive. Other<!…>markup is a comment. <![CDATA[…]]>is text inside<svg>and<math>.<!-->and<!--->are empty comments, and--!>ends a comment.
- Inside
<svg>, the text of<style>is decoded like normal text, because it is not raw text there. (f816928) - Selectors skip the contents of
<template>, like css-select. (6c0340a)
@lukeed/html/mutable
- An edited self-closing tag, like
<path/>in SVG, keeps its/>. If it gets children, it gets an end tag. (95c4a98) - A rebuilt tag copies its unedited attributes as written, so
viewBoxstaysviewBoxand©stays©. An edited attribute keeps its name as written. A new attribute is written as given tosetAttr(). (b56b76a, 744e073) - An element without an end tag in the source, like
<li>without</li>, gets one when an edit moves what closed it. Before, nodes inserted after it ended up inside it. (bed2162) - Text that moves into or out of
<script>,<style>, or<title>is escaped for its new parent. (bd54bf7) - The
htmlsetter parses in the context of the element, sosvg.html = "<path/><path/>"gives two paths. (f816928)
Features
clone()onElement,Text,Comment, andDirectivein@lukeed/html/mutable. (07966a0)- A
namesetter onElementin@lukeed/html/mutable, to rename an element. (1dd6b37) Document.text, in both modules. (7a95b39)
Performance
Edits are faster. Results on 12 real pages (4.79 MB), with Bun 1.4.4 and Node 26.12.1 on an Apple M1 Max:
| Workload | Bun | Node |
|---|---|---|
mutable parse + edit + stringify |
27.8ms → 20.7ms | 35.5ms → 28.3ms |
When an edit only adds attributes, the tag is copied from the source and the new attributes are added at the end, so the other values are not escaped again. (744e073) On pages with <template>, part of the gain comes from selectors that now skip template contents.
Other workloads are within a few percent of 1.0.1, and memory is the same. (89bd48a, c6ff936, 8faf3f9)
Notes
- Some output changes:
<!x>and similar markup are nowCommentnodes, notDirectivenodes.- The value of a
<?…>comment now starts with?, like in browsers. - In a rebuilt tag, an empty attribute value is written as
name="". - The comment and directive setters throw for a value that would not parse back to the same node.
- The bundle is larger: 6.3 kB gzip for
@lukeed/html(was 5.9 kB) and 8.8 kB for@lukeed/html/mutable(was 7.1 kB).
Full Changelog: v1.0.1...v1.0.2
v1.0.1
Performance
This release makes parsing and queries faster, and makes the parsed tree smaller. The API does not change.
Results on 12 real pages (4.79 MB), with Bun 1.4.4 and Node 26.12.1 on an Apple M1 Max:
| Workload | Bun | Node |
|---|---|---|
parse() |
11.8ms → 7.9ms | 12.5ms → 11.7ms |
parse() + query |
18.2ms → 11.8ms | 22.3ms → 16.3ms |
mutable.parse() |
14.0ms → 10.3ms | 26.1ms → 13.1ms |
mutable parse + stringify |
13.7ms → 10.0ms | 15.2ms → 13.5ms |
mutable parse + edit + stringify |
33.4ms → 27.8ms | 44.1ms → 35.5ms |
The parsed trees of the corpus now hold 4.9 MB of memory on Node, down from 5.5 MB. With @lukeed/html/mutable, they hold 20.9 MB, down from 22.1 MB.
Changes
getAttr(),hasAttr(), and selectors read attributes from a flat list. The frozenattributesrecord is built only when you read it. (41e8b5e)- The parser finds the end of a tag with a character loop, not a regex. (f5fc7f7)
- The tree is stored in typed arrays. (8363b0d)
- Common tag names are shared strings, so the parser makes no new string for each tag. (b17dbbf, bbe5916)
- Internal caches are sized to the tree, so the engine keeps them in fast storage. (2e20ce9)
- Each tag needs one lookup for its parse rules, not up to five. (9cac437)
Notes
- The bundle is 0.7 kB larger: 5.9 kB gzip for
@lukeed/htmland 7.1 kB for@lukeed/html/mutable. - If you read
attributeson every element, the tree holds a little more memory than before, because it keeps the list and the record.getAttr()does not have this cost.
Full Changelog: v1.0.0...v1.0.1