mirror of
https://github.com/garrytan/gstack.git
synced 2026-09-15 17:35:29 +02:00
fix(make-pdf): stop URLs swallowing smartypants placeholders
A bare autolinked URL (<a href="X">X</a>) has zero whitespace between the URL text and its own closing tag. TAG_RE carves that </a> into a NUL-delimited SMARTPANTS_PRESERVED placeholder BEFORE the URL pass runs, and URL_RE's \S+ swallowed the adjacent placeholder into the URL match. The restore pass is single-shot, so the inner placeholder never restored: raw "SMARTPANTS_PRESERVED_N" text leaked into the rendered link, the </a> vanished, and link-blue styling bled into the rest of the document (#2084). Excluding the NUL sentinel (\u0000) from the URL character class stops the match from crossing into an already-carved zone. Contributed by @marshaung (PR #2280; PR #2339 by @BrendaB24 covered the same smartypants defect). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Fable 5
parent
de08279969
commit
7b71a62e40
@@ -56,6 +56,34 @@ describe("smartypants", () => {
|
||||
expect(out).toContain("\u201cdetails\u201d");
|
||||
});
|
||||
|
||||
// A URL flush against a following tag (no whitespace between) used to let
|
||||
// URL_RE's \S+ swallow the placeholder standing in for that tag. The
|
||||
// placeholder never restored, so the tag vanished and its styling bled
|
||||
// into the rest of the document. The test above misses this because its
|
||||
// URL is followed by a space.
|
||||
test("does not leak placeholders for a bare autolinked URL", () => {
|
||||
const input = `<p>see <a href="https://ex.com">https://ex.com</a> ok</p>`;
|
||||
const out = smartypants(input);
|
||||
expect(out).not.toContain("SMARTPANTS_PRESERVED");
|
||||
expect(out).toContain("</a>");
|
||||
expect(out).toBe(input);
|
||||
});
|
||||
|
||||
test("does not leak placeholders for a bold URL", () => {
|
||||
const input = `<p>see <strong><a href="https://ex.com">https://ex.com</a></strong> ok</p>`;
|
||||
const out = smartypants(input);
|
||||
expect(out).not.toContain("SMARTPANTS_PRESERVED");
|
||||
expect(out).toContain("</a></strong>");
|
||||
expect(out).toBe(input);
|
||||
});
|
||||
|
||||
test("does not leak placeholders when a URL is followed by punctuation", () => {
|
||||
const input = `<p>see <a href="https://ex.com">https://ex.com</a>, ok</p>`;
|
||||
const out = smartypants(input);
|
||||
expect(out).not.toContain("SMARTPANTS_PRESERVED");
|
||||
expect(out).toContain("</a>");
|
||||
});
|
||||
|
||||
test("does NOT touch HTML attribute values", () => {
|
||||
const out = smartypants(`<a href="it's-a-test.html">link</a>`);
|
||||
expect(out).toContain(`href="it's-a-test.html"`);
|
||||
|
||||
Reference in New Issue
Block a user