fix(reader): rewrite only real page-break declarations in style attributes

Review follow-up. The rewrite also renamed a custom property such as
`--page-break-before`, leaving its `var()` reference dangling, and it
rewrote text that only reads like a style attribute, such as
` style="page-break-before: always"` shown in a <pre>. It now requires a
property-name boundary and touches only style attributes inside opening
tags.

Verified: inline-page-break.browser.test.ts "a custom property named
like a page break is left alone" and "text that reads like a style
attribute is left alone" fail without this change and pass with it
(8/8); transformers and style suites (219) and pnpm lint pass.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Huang Xin 2026-10-18 00:02:28 +08:00
parent 9371dd67d8
commit df14ae23aa
No known key found for this signature in database
GPG key ID: FEB86D637D085E7D
2 changed files with 19 additions and 4 deletions

View file

@ -63,6 +63,18 @@ describe('styleTransformer page breaks', () => {
expect(column).toBe(1);
});
test('a custom property named like a page break is left alone', async () => {
const html = await transform(
'<p style="--page-break-before: column; break-before: var(--page-break-before)">b</p>',
);
expect(html).toContain('--page-break-before: column; break-before: var(--page-break-before)');
});
test('text that reads like a style attribute is left alone', async () => {
const text = '<pre> style="page-break-before: always"</pre>';
expect(await transform(text)).toBe(text);
});
test('a paragraph without a break stays in the first column', async () => {
const column = await columnOfTarget('', '<p>a</p><p id="target" style="color: red">b</p>');
expect(column).toBe(0);

View file

@ -6,9 +6,9 @@ import type { Transformer } from './types';
// `<style>` blocks and `style` attributes never reach it (#6873).
const toColumnBreaks = (css: string) =>
css
.replace(/page-break-before\s*:/gi, '-webkit-column-break-before:')
.replace(/(?<![\w-])page-break-before\s*:/gi, '-webkit-column-break-before:')
.replace(
/break-before\s*:\s*(avoid-)?page/gi,
/(?<![\w-])break-before\s*:\s*(avoid-)?page/gi,
(_, avoid) => `break-before: ${avoid ?? ''}column`,
);
@ -38,8 +38,11 @@ export const styleTransformer: Transformer = {
result = result.replace(full, `<style${attrs}>${toColumnBreaks(transformed)}</style>`);
}
result = result.replace(/(\sstyle\s*=\s*)("[^"]*"|'[^']*')/gi, (attr, name, value) =>
/break-before/i.test(value) ? name + toColumnBreaks(value) : attr,
// Only `style` attributes inside opening tags, not text that reads like one.
result = result.replace(/<[a-z][^>"']*(?:(?:"[^"]*"|'[^']*')[^>"']*)*>/gi, (tag) =>
tag.replace(/(\sstyle\s*=\s*)("[^"]*"|'[^']*')/gi, (attr, name, value) =>
/break-before/i.test(value) ? name + toColumnBreaks(value) : attr,
),
);
return result;