Parsing splits one text run into several Text nodes at each character reference (`<p>a & b</p>` gives 3 child nodes)
メンテナーはふだん 5 日以内に返信
評価
- 難易度
- 2/5
- 見積もり時間
- 1〜3時間
- 初心者へのやさしさ
- 68/100
- issue の種類
- バグ
- 明瞭さ
- おおむね明確
- 活発さ
- 活発
- 技術スタック
- html, javascript
調査の方向性
esm/shared/parse-from-string.js の ontext ハンドラーから始めます。issue によると、このハンドラーはパーサーのコールバックごとに Text ノードを追加します。その動作を htmlparser2 の DomHandler と比較し、文字参照を含むマークアップの回帰テストを追加します。例を解析したときにデータが "a & b" の Text ノードが 1 つ生成され、既存のカバレッジが 100% のままであれば完了です。
索引モデルが issue の本文から書いたものです。
説明
Parsing <p>a & b</p> gives the <p> three Text nodes ("a ", "&", " b"). The HTML parsing algorithm appends each character to the previous Text node, and jsdom gives one Text node, "a & b". Code that walks childNodes and treats each Text node as one run sees extra runs wherever the source has an entity. normalize() merges them afterwards, but every consumer of parsed output has to know to call it on every subtree, and a spec parser never produces the split in the first place ("insert a character": https://html.spec.whatwg.org/multipage/parsing.html#insert-a-character).
Asks:
- Should the parser merge text into the previous Text node when the previous sibling under the same parent is a Text node, as the HTML parsing algorithm does?
- If yes, I can open a PR that changes
esm/only, with a test, keeping coverage at 100%.
Environment: linkedom 0.18.14 (npm, latest) and main at e2ee208, htmlparser2 10.1.0, Node 24.14.1, macOS.
Test case:
import { DOMParser } from 'linkedom'
const doc = new DOMParser().parseFromString('<!doctype html><html><body><p>a & b</p></body></html>', 'text/html')
const p = doc.querySelector('p')
console.log('textContent:', JSON.stringify(p.textContent))
console.log('childNodes.length:', p.childNodes.length)
console.log('childNodes data:', JSON.stringify([...p.childNodes].map((n) => n.data)))
Output:
textContent: "a & b"
childNodes.length: 3
childNodes data: ["a ","&"," b"]
Expected: childNodes.length: 1, data ["a & b"] (what jsdom 29.1.1 returns for the same markup).
| Input | linkedom Text nodes | jsdom Text nodes |
|---|---|---|
<p>a and b</p> |
1 | 1 |
<p>a & b</p> |
3 | 1 |
<p>x < y > z ©</p> |
6 | 1 |
parseHTML gives the same result as DOMParser (3 nodes for <p>a & b</p>).
Raw output (npm 0.18.14, main e2ee208, variants, htmlparser2)
$ node repro.mjs # linkedom 0.18.14 from npm
textContent: "a & b"
childNodes.length: 3
childNodes data: ["a ","&"," b"]
$ node repro-main.mjs # main e2ee208 esm/
textContent: "a & b"
childNodes.length: 3
childNodes data: ["a ","&"," b"]
$ node variants.mjs
no entity: 1 ["a and b"]
&: 3 ["a ","&"," b"]
< > ©: 6 ["x ","<"," y ",">"," z ","©"]
parseHTML: 3
$ node htmlparser2-ontext.mjs # htmlparser2 10.1.0, decodeEntities: true
htmlparser2 ontext calls: ["a ","&"," b"]
$ node jsdom-control.mjs
jsdom childNodes.length: 1 ["a & b"]
Where it comes from: htmlparser2 with decodeEntities: true calls ontext once per piece around each character reference. The ontext handler in parseFromString appends a new Text node for every call:
htmlparser2's own DomHandler merges each ontext piece into the previous Text node (domhandler 5.0.3, lib/index.js, ontext), so with the same callbacks it yields one node for <p>a & b</p>.
No existing issue found for this; the closest are #62 and #71, which are about serializing entities, not the node split.
- 主要言語
- HTML
- スター
- 2.1k
- フォーク
- 117
- 平均マージ
- 4日 13時間
- マージ済み PR(30日)
- 2
環境構築
このプロジェクトには開発コンテナ、Dockerfile、コントリビューションガイドがありません。まず README を読み、一般的な手順ははじめてのコントリビューションガイドを参照してください。
はじめの一歩
- issue を最後まで読み、次にプロジェクトのコントリビューションガイドを読みます。
- 着手することを issue にコメントします — 二人が同じ作業をするのを防げます。
- リポジトリをフォークし、ブランチを切って変更します。
- issue 番号を参照したプルリクエストを送ります。
WebReflection/linkedom のほかの issue
-
難易度 2/5 1〜3時間 初心者へのやさしさ 82/100
WebReflection/linkedom#365 ·
メンテナーはふだん 5 日以内に返信
-
SVG elements in an HTML document report an upper-cased tagName ("PATH" instead of "path")対応中かも @jonasporto が 9 日前に担当しました。 オープン
難易度 2/5 1〜3時間 初心者へのやさしさ 85/100
WebReflection/linkedom#344 · コメント 1 件 ·
メンテナーはふだん 5 日以内に返信
-
`TextElement.toString()` puts the element text back at the first `><` of the start tag, so an attribute value containing `><` swallows the text of `<style>`, `<script>`, `<title>` and `<textarea>`対応中かも @vsolano9 が 22 日前に担当しました。 オープン
難易度 2/5 1〜3時間 初心者へのやさしさ 78/100
WebReflection/linkedom#341 · コメント 1 件 ·
メンテナーはふだん 5 日以内に返信
-
難易度 2/5 1〜3時間 初心者へのやさしさ 82/100
WebReflection/linkedom#340 · コメント 1 件 ·
メンテナーはふだん 5 日以内に返信
-
Element#matches() compiles the selector on every call対応中かも @jonasporto が 22 日前に担当しました。 オープン
難易度 2/5 1〜3時間 初心者へのやさしさ 35/100
WebReflection/linkedom#348 · コメント 1 件 ·
メンテナーはふだん 5 日以内に返信
WebReflection/linkedom の issue をすべて見る
似ている issue
-
`Time.at(t, in: nil)` returns UTC instead of the local time (or the time zone of `t`), unlike CRubyオープン
難易度 2/5 1〜3時間 初心者へのやさしさ 78/100
メンテナーはふだん 1 日以内に返信
-
難易度 2/5 1〜3時間 初心者へのやさしさ 78/100
メンテナーはふだん 1 日以内に返信
-
bug implicit-state
難易度 2/5 1〜3時間 初心者へのやさしさ 78/100
-
難易度 2/5 1〜3時間 初心者へのやさしさ 68/100
PecanProject/sipnet#435 · コメント 1 件 ·
メンテナーはふだん 2 日以内に返信
-
難易度 2/5 1〜3時間 初心者へのやさしさ 68/100