Skip to content

Commit 7de0e1f

Browse files
authored
Merge pull request #22 from PureStackStudio/normalization-perf
Improve normalization performance and fix double entity decoding.
2 parents ce52306 + d6d9ff6 commit 7de0e1f

27 files changed

Lines changed: 176 additions & 35 deletions

File tree

‎package.json‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"private": true,
33
"name": "purestack-repo",
4-
"version": "1.1.7",
4+
"version": "1.1.8",
55
"packageManager": "yarn@4.18.1",
66
"type": "module",
77
"sideEffects": false,

‎packages/purestack/package.json‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "purestack",
3-
"version": "1.1.7",
3+
"version": "1.1.8",
44
"packageManager": "yarn@4.9.2",
55
"module": "./dist/purestack.mjs",
66
"types": "./dist/purestack.d.mts",

‎packages/purestack/src/index.ts‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
1-
export const version: string = '1.1.7'
1+
export const version: string = '1.1.8'
22

33
export * from '@purestack/ts-ssg'

‎packages/ts-common/package.json‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@purestack/ts-common",
3-
"version": "1.1.7",
3+
"version": "1.1.8",
44
"packageManager": "yarn@4.9.2",
55
"module": "./dist/ts-common.mjs",
66
"types": "./dist/ts-common.d.mts",

‎packages/ts-components/package.json‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@purestack/ts-components",
3-
"version": "1.1.7",
3+
"version": "1.1.8",
44
"packageManager": "yarn@4.9.2",
55
"module": "./dist/ts-components.mjs",
66
"types": "./dist/ts-components.d.mts",
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
import { describe, expect, it } from 'vitest'
2+
import { sanitizeEmailStyleValue } from './emailHtmlPolicy'
3+
4+
describe('sanitizeEmailStyleValue', () => {
5+
it.each([
6+
[' red ', 'red'],
7+
['red!important', 'red'],
8+
[' red \t!IMPORTANT \n', 'red'],
9+
['red !important extra', 'red !important extra'],
10+
['red ! important', 'red ! important'],
11+
['!important', undefined],
12+
])('normalizes %j to %j', (value, expected) => {
13+
expect(sanitizeEmailStyleValue('color', value)).toBe(expected)
14+
})
15+
16+
it('rejects oversized values containing long internal whitespace', () => {
17+
const value = `red${' '.repeat(200_000)}x`
18+
expect(sanitizeEmailStyleValue('color', value)).toBeUndefined()
19+
})
20+
21+
it('applies the length limit after removing the important suffix', () => {
22+
const value = 'a'.repeat(512)
23+
expect(sanitizeEmailStyleValue('font-family', `${value} !important`)).toBe(
24+
value,
25+
)
26+
expect(
27+
sanitizeEmailStyleValue('font-family', `${value}a !important`),
28+
).toBeUndefined()
29+
})
30+
})

‎packages/ts-components/src/standard/composer/emailHtmlPolicy.ts‎

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -432,7 +432,10 @@ export function normalizeEmailContentId(value: string) {
432432
}
433433

434434
function stripImportant(value: string) {
435-
return value.replace(/\s*!important\s*$/i, '').trim()
435+
const trimmed = value.trim()
436+
return trimmed.slice(-10).toLowerCase() === '!important'
437+
? trimmed.slice(0, -10).trimEnd()
438+
: trimmed
436439
}
437440

438441
function isSafeCssValue(value: string) {

‎packages/ts-css/package.json‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@purestack/ts-css",
3-
"version": "1.1.7",
3+
"version": "1.1.8",
44
"packageManager": "yarn@4.9.2",
55
"module": "./dist/ts-css.mjs",
66
"types": "./dist/ts-css.d.mts",

‎packages/ts-css/src/hyphenize.test.ts‎

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,14 @@ describe('hyphenizeCss', () => {
3232
expect(hyphenizeCss('ID')).toBe('id') // all uppercase, no hyphen
3333
})
3434

35+
it('handles long uppercase runs with and without an acronym boundary', () => {
36+
const acronym = 'A'.repeat(200_000)
37+
expect(hyphenizeCss(acronym)).toBe(acronym.toLowerCase())
38+
expect(hyphenizeCss(`${acronym}Value`)).toBe(
39+
`${acronym.toLowerCase()}-value`,
40+
)
41+
})
42+
3543
it('preserves existing kebab-case / hyphens', () => {
3644
expect(hyphenizeCss('font-size')).toBe('font-size')
3745
expect(hyphenizeCss('-moz-appearance')).toBe('-moz-appearance')

‎packages/ts-css/src/hyphenize.ts‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ export function hyphenizeCss(prop: string): string {
3636
// 1. Split acronym boundaries like "XMLHttp" → "XML-Http"
3737
// 2. Then split camelCase boundaries like "fooBar" → "foo-Bar"
3838
const withSeparators = withOpera
39-
.replace(/([A-Z]+)([A-Z][a-z])/g, '$1-$2')
39+
.replace(/([A-Z])([A-Z][a-z])/g, '$1-$2')
4040
.replace(/([a-z0-9])([A-Z])/g, '$1-$2')
4141

4242
return withSeparators.toLowerCase()

0 commit comments

Comments
 (0)