@jxsuite/markup 0.4.12 → 0.4.13
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.
- package/package.json +2 -2
- package/src/html-to-jx.ts +18 -17
- package/src/md-html.ts +23 -10
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jxsuite/markup",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.13",
|
|
4
4
|
"description": "Browser-safe markup conversion: HTML → Jx nodes, markdown → sanitized HTML",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -26,7 +26,7 @@
|
|
|
26
26
|
"upgrade": "bun update --latest && bun install"
|
|
27
27
|
},
|
|
28
28
|
"dependencies": {
|
|
29
|
-
"@jxsuite/schema": "^
|
|
29
|
+
"@jxsuite/schema": "^4.0.0",
|
|
30
30
|
"hast-util-from-html": "^2.0.3",
|
|
31
31
|
"hast-util-whitespace": "^3.0.0",
|
|
32
32
|
"property-information": "^7.2.0",
|
package/src/html-to-jx.ts
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { fromHtml } from "hast-util-from-html";
|
|
2
2
|
import { whitespace } from "hast-util-whitespace";
|
|
3
|
-
import { find, html
|
|
3
|
+
import { find, html } from "property-information";
|
|
4
|
+
import { isString } from "@jxsuite/schema/guards";
|
|
4
5
|
|
|
5
|
-
import type {
|
|
6
|
+
import type { Properties, RootContent } from "hast";
|
|
6
7
|
import type { JxElement } from "@jxsuite/schema/types";
|
|
7
8
|
|
|
8
9
|
/**
|
|
@@ -13,14 +14,14 @@ import type { JxElement } from "@jxsuite/schema/types";
|
|
|
13
14
|
*/
|
|
14
15
|
export function htmlToJx(htmlString: string) {
|
|
15
16
|
const hast = fromHtml(htmlString, { fragment: true });
|
|
16
|
-
return convertHastChildren(hast.children
|
|
17
|
+
return convertHastChildren(hast.children);
|
|
17
18
|
}
|
|
18
19
|
|
|
19
20
|
/**
|
|
20
|
-
* @param {
|
|
21
|
+
* @param {RootContent[]} children
|
|
21
22
|
* @returns {(JxElement | string)[]}
|
|
22
23
|
*/
|
|
23
|
-
function convertHastChildren(children:
|
|
24
|
+
function convertHastChildren(children: RootContent[]) {
|
|
24
25
|
const result: (JxElement | string)[] = [];
|
|
25
26
|
for (const child of children) {
|
|
26
27
|
const converted = convertHastNode(child);
|
|
@@ -32,21 +33,21 @@ function convertHastChildren(children: HastNode[]) {
|
|
|
32
33
|
}
|
|
33
34
|
|
|
34
35
|
/**
|
|
35
|
-
* @param {
|
|
36
|
+
* @param {RootContent} node
|
|
36
37
|
* @returns {JxElement | string | null}
|
|
37
38
|
*/
|
|
38
|
-
function convertHastNode(node:
|
|
39
|
+
function convertHastNode(node: RootContent) {
|
|
39
40
|
if (node.type === "text") {
|
|
40
|
-
if (whitespace(node
|
|
41
|
+
if (whitespace(node)) {
|
|
41
42
|
return null;
|
|
42
43
|
}
|
|
43
|
-
return node.value
|
|
44
|
+
return node.value;
|
|
44
45
|
}
|
|
45
46
|
|
|
46
47
|
if (node.type === "element") {
|
|
47
48
|
const el: JxElement = { tagName: node.tagName };
|
|
48
49
|
|
|
49
|
-
if (
|
|
50
|
+
if (Object.keys(node.properties).length > 0) {
|
|
50
51
|
const { style, attrs } = hastPropsToJx(node.properties);
|
|
51
52
|
if (Object.keys(attrs).length > 0) {
|
|
52
53
|
el.attributes = attrs;
|
|
@@ -56,12 +57,12 @@ function convertHastNode(node: HastNode) {
|
|
|
56
57
|
}
|
|
57
58
|
}
|
|
58
59
|
|
|
59
|
-
const kids =
|
|
60
|
+
const kids = convertHastChildren(node.children);
|
|
60
61
|
|
|
61
|
-
if (kids.length === 1 &&
|
|
62
|
+
if (kids.length === 1 && isString(kids[0])) {
|
|
62
63
|
[el.textContent] = kids;
|
|
63
64
|
} else if (kids.length > 0) {
|
|
64
|
-
el.children = kids
|
|
65
|
+
el.children = kids;
|
|
65
66
|
}
|
|
66
67
|
|
|
67
68
|
return el;
|
|
@@ -71,10 +72,10 @@ function convertHastNode(node: HastNode) {
|
|
|
71
72
|
}
|
|
72
73
|
|
|
73
74
|
/**
|
|
74
|
-
* @param {
|
|
75
|
+
* @param {Properties} properties
|
|
75
76
|
* @returns {{ style: Record<string, string>; attrs: Record<string, string> }}
|
|
76
77
|
*/
|
|
77
|
-
function hastPropsToJx(properties:
|
|
78
|
+
function hastPropsToJx(properties: Properties) {
|
|
78
79
|
const attrs: Record<string, string> = {};
|
|
79
80
|
const style: Record<string, string> = {};
|
|
80
81
|
|
|
@@ -83,10 +84,10 @@ function hastPropsToJx(properties: Record<string, unknown>) {
|
|
|
83
84
|
continue;
|
|
84
85
|
}
|
|
85
86
|
|
|
86
|
-
const info = find(
|
|
87
|
+
const info = find(html, key);
|
|
87
88
|
const name = info.attribute;
|
|
88
89
|
|
|
89
|
-
if (name === "style" &&
|
|
90
|
+
if (name === "style" && isString(value)) {
|
|
90
91
|
parseInlineStyle(value, style);
|
|
91
92
|
continue;
|
|
92
93
|
}
|
package/src/md-html.ts
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Md-html — browser-safe markdown → sanitized HTML string
|
|
3
3
|
*
|
|
4
|
-
* Small display pipeline for rendering untrusted markdown (e.g. LLM chat output) as
|
|
5
|
-
*
|
|
6
|
-
*
|
|
4
|
+
* Small display pipeline for rendering untrusted markdown (e.g. LLM chat output) as HTML:
|
|
5
|
+
* remark-parse + remark-gfm → remark-rehype (raw HTML disabled) → rehype-sanitize (default schema:
|
|
6
|
+
* strips scripts, event handlers, javascript:/data: URLs) → stringify.
|
|
7
7
|
*
|
|
8
|
-
* Like everything in @jxsuite/markup this module has no node-only imports, so it is safe
|
|
9
|
-
*
|
|
8
|
+
* Like everything in @jxsuite/markup this module has no node-only imports, so it is safe for the
|
|
9
|
+
* studio bundle. It intentionally produces an HTML _string_, not Jx nodes — use
|
|
10
10
|
* `@jxsuite/parser/markdown` for markdown → Jx document transpilation.
|
|
11
11
|
*
|
|
12
12
|
* @module @jxsuite/markup/md-html
|
|
@@ -20,22 +20,35 @@ import remarkParse from "remark-parse";
|
|
|
20
20
|
import remarkRehype from "remark-rehype";
|
|
21
21
|
import { unified } from "unified";
|
|
22
22
|
|
|
23
|
+
/**
|
|
24
|
+
* A unified attacher: called with `this` bound to the processor and the plugin's optional settings.
|
|
25
|
+
* Its result (a parser, compiler or transformer) is consumed by unified, never by us.
|
|
26
|
+
*/
|
|
27
|
+
type UnifiedPlugin = (...settings: never[]) => void;
|
|
28
|
+
|
|
23
29
|
/**
|
|
24
30
|
* Minimal structural surface of the unified processor used here — same rationale as
|
|
25
31
|
* `UnifiedProcessor` in @jxsuite/parser's types (oxlint sees the ESM-only unified types as `any`),
|
|
26
32
|
* plus `processSync` which that shared type doesn't carry.
|
|
27
33
|
*/
|
|
28
34
|
interface MdHtmlProcessor {
|
|
29
|
-
use: (plugin:
|
|
35
|
+
use: (plugin: UnifiedPlugin) => MdHtmlProcessor;
|
|
30
36
|
processSync: (source: string) => { toString: () => string };
|
|
31
37
|
}
|
|
32
38
|
|
|
33
|
-
|
|
34
|
-
|
|
39
|
+
/* SAFETY: `unified`, `remark-parse`, `remark-rehype` and `rehype-stringify` are shorthand
|
|
40
|
+
`declare module` entries in the root types.d.ts, so their imports are `any` here. unified@11
|
|
41
|
+
exports `unified` as a callable Processor whose call returns a fresh Processor; that Processor's
|
|
42
|
+
`use(plugin)` returns itself and `processSync(string)` returns a VFile whose toString() yields
|
|
43
|
+
the compiled HTML, which is exactly the MdHtmlProcessor surface. Each of the three plugin
|
|
44
|
+
packages default-exports a unified attacher function taking optional settings, which is the
|
|
45
|
+
UnifiedPlugin shape. */
|
|
46
|
+
const processor = (unified as () => MdHtmlProcessor)()
|
|
47
|
+
.use(remarkParse as UnifiedPlugin)
|
|
35
48
|
.use(remarkGfm)
|
|
36
|
-
.use(remarkRehype)
|
|
49
|
+
.use(remarkRehype as UnifiedPlugin)
|
|
37
50
|
.use(rehypeSanitize)
|
|
38
|
-
.use(rehypeStringify);
|
|
51
|
+
.use(rehypeStringify as UnifiedPlugin);
|
|
39
52
|
|
|
40
53
|
/**
|
|
41
54
|
* Convert markdown source to a sanitized HTML string (GFM tables/strikethrough/task lists
|