|
| 1 | +import { |
| 2 | + createSafeHtmlWithoutSanitize as clientCreateSafeHtmlWithoutSanitize, |
| 3 | + isSafeHtml as clientIsSafeHtml, |
| 4 | + sanitizeHtml as clientSanitizeHtml, |
| 5 | +} from './sanitizeHtml'; |
| 6 | + |
| 7 | +import type {SafeHtml} from '../../../types/internal'; |
| 8 | + |
| 9 | +const createSafeHtmlWithoutSanitize = clientCreateSafeHtmlWithoutSanitize; |
| 10 | +const isSafeHtml: typeof clientIsSafeHtml = clientIsSafeHtml; |
| 11 | +const sanitizeHtml = clientSanitizeHtml; |
| 12 | + |
1 | 13 | /** |
2 | 14 | * Creates JSX runtime (functions `createElement` and `Fragment`). |
3 | 15 | * This client function should not use scope variables (except global functions). |
4 | 16 | * @internal |
5 | 17 | */ |
6 | 18 | export function createJsxRuntime(): JSX.Runtime { |
7 | | - const createElement: JSX.CreateElement = (type, properties, ...children) => ''; |
8 | | - const Fragment: JSX.Fragment = ({children}) => ''; |
| 19 | + const maxDepth = 8; |
| 20 | + |
| 21 | + const createElement: JSX.CreateElement = (type, properties, ...children) => { |
| 22 | + const flatChildren = children.flat(maxDepth); |
| 23 | + |
| 24 | + if (typeof type === 'function') { |
| 25 | + const propertiesWithChildren = |
| 26 | + flatChildren.length === 0 ? properties : {...properties, children: flatChildren}; |
| 27 | + |
| 28 | + return type(propertiesWithChildren ?? undefined); |
| 29 | + } |
| 30 | + |
| 31 | + const childrenParts: readonly SafeHtml[] = flatChildren.map((child) => |
| 32 | + isSafeHtml(child) ? child : sanitizeHtml`${child}`, |
| 33 | + ); |
| 34 | + const childrenHtml = createSafeHtmlWithoutSanitize`${childrenParts.join('')}`; |
| 35 | + |
| 36 | + if (properties == null) { |
| 37 | + return sanitizeHtml`<${type}>${childrenHtml}</${type}>`; |
| 38 | + } |
| 39 | + |
| 40 | + const attributesParts: readonly SafeHtml[] = Object.entries(properties).map( |
| 41 | + ([key, value]) => sanitizeHtml`${key}="${value}"`, |
| 42 | + ); |
| 43 | + const attributesHtml = createSafeHtmlWithoutSanitize`${attributesParts.join('')}`; |
| 44 | + |
| 45 | + return sanitizeHtml`<${type} ${attributesHtml}>${childrenHtml}</${type}>`; |
| 46 | + }; |
| 47 | + |
| 48 | + const Fragment: JSX.Fragment = (properties) => { |
| 49 | + if (properties?.children == null) { |
| 50 | + return createSafeHtmlWithoutSanitize``; |
| 51 | + } |
| 52 | + |
| 53 | + if (!Array.isArray(properties.children)) { |
| 54 | + return sanitizeHtml`${properties.children}`; |
| 55 | + } |
| 56 | + |
| 57 | + const flatChildren: unknown[] = properties.children.flat(maxDepth); |
| 58 | + const childrenParts: readonly SafeHtml[] = flatChildren.map((child) => |
| 59 | + isSafeHtml(child) ? child : sanitizeHtml`${child}`, |
| 60 | + ); |
| 61 | + |
| 62 | + return createSafeHtmlWithoutSanitize`${childrenParts.join('')}`; |
| 63 | + }; |
9 | 64 |
|
10 | | - return {createElement, Fragment}; |
| 65 | + return {Fragment, createElement}; |
11 | 66 | } |
0 commit comments