@forming/react 0.0.0-stage → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,3 @@
1
+ import React from 'react';
2
+ export declare function inline(text: string, keyPrefix?: string): React.ReactNode[];
3
+ export declare function markdown(text: string): React.ReactNode[];
@@ -0,0 +1,61 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ // Restricted Markdown for display content. The subset is deliberately small
3
+ // and is turned straight into React elements: no HTML is ever parsed or
4
+ // injected, and links are limited to http(s) and mailto.
5
+ //
6
+ // Blocks: paragraphs (blank-line separated; a single newline is a line
7
+ // break), headings (#, ##, ###), bullet lists (- or *), numbered lists (1.).
8
+ // Inline: **bold**, *italic*, `code`, [text](https://…).
9
+ const SAFE_LINK = /^(https?:\/\/|mailto:)/i;
10
+ export function inline(text, keyPrefix = '') {
11
+ const out = [];
12
+ const pattern = /(\*\*([^*]+)\*\*)|(\*([^*]+)\*)|(`([^`]+)`)|(\[([^\]]+)\]\(([^)\s]+)\))/g;
13
+ let last = 0;
14
+ let match;
15
+ let index = 0;
16
+ while ((match = pattern.exec(text)) !== null) {
17
+ if (match.index > last)
18
+ out.push(text.slice(last, match.index));
19
+ const key = `${keyPrefix}${index++}`;
20
+ if (match[2] !== undefined)
21
+ out.push(_jsx("strong", { children: match[2] }, key));
22
+ else if (match[4] !== undefined)
23
+ out.push(_jsx("em", { children: match[4] }, key));
24
+ else if (match[6] !== undefined)
25
+ out.push(_jsx("code", { children: match[6] }, key));
26
+ else if (match[8] !== undefined) {
27
+ const href = match[9];
28
+ out.push(SAFE_LINK.test(href) ? (_jsx("a", { href: href, rel: "noopener noreferrer", target: "_blank", children: match[8] }, key)) : (match[0]));
29
+ }
30
+ last = match.index + match[0].length;
31
+ }
32
+ if (last < text.length)
33
+ out.push(text.slice(last));
34
+ return out;
35
+ }
36
+ function lines(text, keyPrefix) {
37
+ return text.split('\n').flatMap((line, i, all) => {
38
+ const parts = inline(line, `${keyPrefix}l${i}-`);
39
+ return i < all.length - 1 ? [...parts, _jsx("br", {}, `${keyPrefix}br${i}`)] : parts;
40
+ });
41
+ }
42
+ export function markdown(text) {
43
+ const blocks = text.replace(/\r\n/g, '\n').split(/\n[ \t]*\n+/);
44
+ return blocks.map((block, b) => {
45
+ const trimmed = block.replace(/^\n+|\n+$/g, '');
46
+ if (!trimmed)
47
+ return null;
48
+ const heading = /^(#{1,3})\s+(.+)$/.exec(trimmed);
49
+ if (heading && !trimmed.includes('\n')) {
50
+ const level = heading[1].length;
51
+ const content = inline(heading[2], `b${b}-`);
52
+ return level === 1 ? (_jsx("h3", { children: content }, b)) : level === 2 ? (_jsx("h4", { children: content }, b)) : (_jsx("h5", { children: content }, b));
53
+ }
54
+ const items = trimmed.split('\n');
55
+ if (items.every(line => /^\s*[-*]\s+/.test(line)))
56
+ return (_jsx("ul", { children: items.map((line, i) => (_jsx("li", { children: inline(line.replace(/^\s*[-*]\s+/, ''), `b${b}i${i}-`) }, i))) }, b));
57
+ if (items.every(line => /^\s*\d+[.)]\s+/.test(line)))
58
+ return (_jsx("ol", { children: items.map((line, i) => (_jsx("li", { children: inline(line.replace(/^\s*\d+[.)]\s+/, ''), `b${b}i${i}-`) }, i))) }, b));
59
+ return _jsx("p", { children: lines(trimmed, `b${b}-`) }, b);
60
+ });
61
+ }