From 57fd081dc29263db8b70bb50eb91b4b7bec9e082 Mon Sep 17 00:00:00 2001
From: kevin9327 <5299031+kevin9327@users.noreply.github.com>
Date: Tue, 29 Sep 2026 06:43:57 +0900
Subject: [PATCH] Keep the space a whitespace-only span puts between two words
A text fragment remembers whether space touched its edges, so mergeText
can glue the pieces a parser split out of one word and space the rest.
A text node that is only whitespace was skipped without a trace, so the
fragments on either side of it looked adjacent and were glued.
Wikipedia's HTML writes every as
, and the text around it shares a parent, so the Python article
read "Firstappeared", "20February 1991", "Python3.0", and "December1989",
43 blocks in all.
A whitespace-only node now marks the text block before it as ending in
space, which is what it was on the page.
Co-Authored-By: Claude Opus 5.5
---
src/distill.js | 6 ++++++
tests/distill.test.js | 14 ++++++++++++++
2 files changed, 20 insertions(+)
diff --git a/src/distill.js b/src/distill.js
index d9274c1..71b93c3 100644
--- a/src/distill.js
+++ b/src/distill.js
@@ -347,6 +347,12 @@ export function distill(html, url = '', { view = null, type = '' } = {}) {
// space that was never in the page.
if (text) {
blocks.push({ type: 'text', text, host: node.parentNode, pre: /^\s/.test(raw), post: /\s$/.test(raw) });
+ } else if (raw && blocks.at(-1)?.type === 'text') {
+ // A node that is only a space still separates the words either side
+ // of it. Wikipedia writes every as a span holding nothing
+ // else, and the fragments around it share a parent, so without this
+ // an infobox read "Firstappeared" and "20February 1991".
+ blocks.at(-1).post = true;
}
return;
}
diff --git a/tests/distill.test.js b/tests/distill.test.js
index d365fda..d73f7fb 100644
--- a/tests/distill.test.js
+++ b/tests/distill.test.js
@@ -347,6 +347,20 @@ test('an entity does not put spaces inside a word', () => {
assert.ok(text.includes('interface & its restraint'), 'a real space was swallowed');
});
+test('a space in an element of its own still separates two words', () => {
+ // Wikipedia's HTML writes every as .
+ // That span has no text once whitespace is collapsed, and the words either
+ // side of it were glued: "Firstappeared", "20February 1991".
+ const p = distill(`
+ | First appeared |
+ 20 February 1991 |
+
ten items
`, 'https://example.test/');
+ const text = p.blocks.map((b) => b.text).join('\n');
+ assert.ok(text.includes('First appeared'), `words glued:\n${text}`);
+ assert.ok(text.includes('20 February 1991'), `words glued:\n${text}`);
+ assert.ok(text.includes('ten items'), `words glued:\n${text}`);
+});
+
test('an icon button is named by its aria-label, a nameless one is dropped', () => {
const blocks = social().blocks;
assert.ok(blocks.some((b) => b.type === 'button' && b.text === 'Follow'), 'a labelled button went missing');