diff --git a/packages/intl/.size-limit.json b/packages/intl/.size-limit.json index 95e98559..37d5d6de 100644 --- a/packages/intl/.size-limit.json +++ b/packages/intl/.size-limit.json @@ -3,7 +3,7 @@ "name": "All publics", "path": "dist/index.js", "import": "*", - "limit": "1.8 kB" + "limit": "1.85 kB" }, { "name": "Minimal set", diff --git a/packages/intl/src/format/rich.spec.ts b/packages/intl/src/format/rich.spec.ts index ca67268e..01f76881 100644 --- a/packages/intl/src/format/rich.spec.ts +++ b/packages/intl/src/format/rich.spec.ts @@ -24,6 +24,10 @@ const tags = { strong: (chunks: (string | Node)[]) => ({ type: 'strong', children: chunks + }), + br: () => ({ + type: 'br', + children: [] }) } @@ -93,6 +97,48 @@ describe('intl', () => { 'guidelines.' ]) }) + + it('should map self-closing tags', () => { + expect(mapTags('First line
second line', tags)).toEqual([ + 'First line', + { + type: 'br', + children: [] + }, + 'second line' + ]) + expect(mapTags('First line
second line', tags)).toEqual([ + 'First line', + { + type: 'br', + children: [] + }, + 'second line' + ]) + }) + + it('should map self-closing tags inside other tags', () => { + expect(mapTags('First
second
', tags)).toEqual([ + { + type: 'strong', + children: [ + 'First', + { + type: 'br', + children: [] + }, + 'second' + ] + } + ]) + }) + + it('should ignore unknown self-closing tags', () => { + expect(mapTags('First
second', tags)).toEqual([ + 'First', + 'second' + ]) + }) }) }) }) diff --git a/packages/intl/src/format/rich.ts b/packages/intl/src/format/rich.ts index 33736f51..c36dcb78 100644 --- a/packages/intl/src/format/rich.ts +++ b/packages/intl/src/format/rich.ts @@ -63,7 +63,7 @@ export function rich( } } -const tagRegex = /<\/?([A-Za-z][\w.-]*)>/g +const tagRegex = /<(\/?)([A-Za-z][\w.-]*)\s*(\/?)>/g interface RichFrame { tag: string @@ -72,6 +72,7 @@ interface RichFrame { /** * Maps rich-text tags in a string to chunks using provided tag handlers. + * Self-closing tags are mapped with empty chunks. * Unknown or malformed tags are ignored as markup and only their text content is kept. * @param input - Rich-text input string. * @param tags - Tag handlers used to map known tags. @@ -91,7 +92,7 @@ export function mapTags( let match: RegExpExecArray | null while (match = tagRegex.exec(input)) { - const [rawTag, tag] = match + const [, closing, tag, selfClosing] = match const current = stack[stack.length - 1] if (match.index > lastIndex) { @@ -99,13 +100,15 @@ export function mapTags( } if (tag in tags) { - if (rawTag[1] === '/') { + if (closing) { if (current.tag === tag) { stack.pop() stack[stack.length - 1].chunks.push( tags[tag](current.chunks) ) } + } else if (selfClosing) { + current.chunks.push(tags[tag]([])) } else { stack.push({ tag,