From 6ae0b8989791966673433a816cd669fdf32e2780 Mon Sep 17 00:00:00 2001 From: Cosmin Popovici Date: Thu, 24 Sep 2026 19:26:55 +0300 Subject: [PATCH 1/3] perf(shorthandCss): merge each distinct style value only once Co-Authored-By: Claude Opus 5.5 (1M context) --- src/tests/transformers/shorthandCss.test.ts | 8 ++++++++ src/transformers/shorthandCss.ts | 19 ++++++++++++++----- 2 files changed, 22 insertions(+), 5 deletions(-) diff --git a/src/tests/transformers/shorthandCss.test.ts b/src/tests/transformers/shorthandCss.test.ts index 9c8df424..cc65ae1d 100644 --- a/src/tests/transformers/shorthandCss.test.ts +++ b/src/tests/transformers/shorthandCss.test.ts @@ -18,6 +18,14 @@ describe('shorthandCss', () => { expect(result).not.toContain('margin-left:') expect(result).not.toContain('margin-right:') }) + + it('merges repeated identical style values on every element', () => { + const style = 'padding-top: 4px; padding-right: 8px; padding-bottom: 4px; padding-left: 8px' + const html = `

A

B` + const result = shorthandCss(html) + expect(result.match(/padding: 4px 8px/g)).toHaveLength(3) + expect(result).not.toContain('padding-top:') + }) }) describe('margin shorthand', () => { diff --git a/src/transformers/shorthandCss.ts b/src/transformers/shorthandCss.ts index e8e39612..8f44dae2 100644 --- a/src/transformers/shorthandCss.ts +++ b/src/transformers/shorthandCss.ts @@ -99,25 +99,34 @@ export function shorthandCssDom(dom: ChildNode[], options: ShorthandCssOptions = const allowedTags = options.tags ?? [] const hasTagFilter = allowedTags.length > 0 + /** + * Utility-generated inline styles repeat heavily across elements, so + * each distinct value only goes through postcss once per call. + */ + const cache = new Map() + /** * Merge longhand within a single inline-style value. Returns the merged * string when shorter, otherwise the original. Wraps the value in a * dummy selector since postcss-merge-longhand operates on rules. */ const mergeStyleValue = (styleValue: string): string => { + let result = cache.get(styleValue) + if (result !== undefined) return result + + result = styleValue try { const { css } = postcss() .use(mergeLonghand) .use(mergeBorder) .process(`div { ${styleValue} }`, { parser: safeParser }) const match = css.match(/div\s*\{\s*([^}]+)\s*\}/) - if (match && match[1]) { - const merged = match[1].trim() - if (merged !== styleValue) return merged - } + if (match && match[1]) result = match[1].trim() } catch {} - return styleValue + + cache.set(styleValue, result) + return result } walk(dom, (node) => { From ecbe1cf434bad26379742c738bcf2f432b781595 Mon Sep 17 00:00:00 2001 From: Cosmin Popovici Date: Fri, 2 Oct 2026 14:58:41 +0300 Subject: [PATCH 2/3] test(shorthandCss): assert postcss runs once per distinct style value Co-Authored-By: Claude Opus 5.5 (1M context) --- src/tests/transformers/shorthandCss.test.ts | 28 ++++++++++++++++++++- 1 file changed, 27 insertions(+), 1 deletion(-) diff --git a/src/tests/transformers/shorthandCss.test.ts b/src/tests/transformers/shorthandCss.test.ts index cc65ae1d..1c6f3fa3 100644 --- a/src/tests/transformers/shorthandCss.test.ts +++ b/src/tests/transformers/shorthandCss.test.ts @@ -1,6 +1,22 @@ -import { describe, it, expect } from 'vitest' +import { describe, it, expect, vi } from 'vitest' import { shorthandCss, type ShorthandCssOptions } from '../../transformers/shorthandCss.ts' +/** + * Count postcss runs: the transformer builds a fresh postcss + * processor per style value it merges, which calls this + * plugin creator once. + */ +const mergeLonghandCalls = vi.hoisted(() => ({ count: 0 })) + +vi.mock('postcss-merge-longhand', async (importOriginal) => { + const { default: plugin } = await importOriginal() + const counted = Object.assign((...args: Parameters) => { + mergeLonghandCalls.count++ + return plugin(...args) + }, { postcss: true as const }) + return { default: counted } +}) + function run(html: string, options?: ShorthandCssOptions | boolean): string { if (options === false) return html const opts = (options === true || options == null) ? {} : options @@ -26,6 +42,16 @@ describe('shorthandCss', () => { expect(result.match(/padding: 4px 8px/g)).toHaveLength(3) expect(result).not.toContain('padding-top:') }) + + it('runs postcss once per distinct style value', () => { + const a = 'padding-top: 4px; padding-right: 8px; padding-bottom: 4px; padding-left: 8px' + const b = 'margin-top: 2px; margin-right: 2px; margin-bottom: 2px; margin-left: 2px' + const html = `

A

B
C
` + + mergeLonghandCalls.count = 0 + shorthandCss(html) + expect(mergeLonghandCalls.count).toBe(2) + }) }) describe('margin shorthand', () => { From c6a61c417aede42a9385ddaa043900165a641c81 Mon Sep 17 00:00:00 2001 From: Cosmin Popovici Date: Fri, 2 Oct 2026 15:07:57 +0300 Subject: [PATCH 3/3] test(shorthandCss): assert cache is not shared across calls Co-Authored-By: Claude Opus 5.5 (1M context) --- src/tests/transformers/shorthandCss.test.ts | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/tests/transformers/shorthandCss.test.ts b/src/tests/transformers/shorthandCss.test.ts index 1c6f3fa3..884fa02d 100644 --- a/src/tests/transformers/shorthandCss.test.ts +++ b/src/tests/transformers/shorthandCss.test.ts @@ -52,6 +52,15 @@ describe('shorthandCss', () => { shorthandCss(html) expect(mergeLonghandCalls.count).toBe(2) }) + + it('does not reuse cached style values across calls', () => { + const html = '

A

' + + mergeLonghandCalls.count = 0 + shorthandCss(html) + shorthandCss(html) + expect(mergeLonghandCalls.count).toBe(2) + }) }) describe('margin shorthand', () => {