comark-solid 0.0.3 → 0.1.1
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/README.md +9 -7
- package/dist/index.d.ts +3 -3
- package/dist/index.js +60 -50
- package/package.json +7 -9
package/README.md
CHANGED
|
@@ -2,11 +2,6 @@
|
|
|
2
2
|
|
|
3
3
|
Solid renderer for [Comark](https://comark.dev).
|
|
4
4
|
|
|
5
|
-
> [!WARNING]
|
|
6
|
-
> **Pre-release: Solid 2.0 RC**
|
|
7
|
-
>
|
|
8
|
-
> This package targets the Solid 2.0 RC versions of `solid-js` and `@solidjs/web`. Solid 2.0 is not stable yet, so bugs and compatibility issues may occur.
|
|
9
|
-
|
|
10
5
|
## Installation
|
|
11
6
|
|
|
12
7
|
```bash
|
|
@@ -17,13 +12,20 @@ npm install comark-solid comark
|
|
|
17
12
|
|
|
18
13
|
Parses Markdown strings and renders them directly.
|
|
19
14
|
|
|
15
|
+
String input is parsed asynchronously. Wrap the component in Solid's `Suspense` to show a fallback while parsing.
|
|
16
|
+
|
|
20
17
|
```tsx
|
|
21
18
|
import { Markdown } from "comark-solid";
|
|
19
|
+
import { Suspense } from "solid-js";
|
|
22
20
|
|
|
23
21
|
const markdown = "# Hello World";
|
|
24
22
|
|
|
25
23
|
export default function App() {
|
|
26
|
-
return
|
|
24
|
+
return (
|
|
25
|
+
<Suspense fallback={<div>Loading...</div>}>
|
|
26
|
+
<Markdown value={markdown} />
|
|
27
|
+
</Suspense>
|
|
28
|
+
);
|
|
27
29
|
}
|
|
28
30
|
```
|
|
29
31
|
|
|
@@ -107,7 +109,7 @@ Use the `:` prefix to pass typed values:
|
|
|
107
109
|
### Slots
|
|
108
110
|
|
|
109
111
|
The default slot is passed as `children`.
|
|
110
|
-
Named slots (e.g. `#header`) are passed as
|
|
112
|
+
Named slots (e.g. `#header`) are passed as props prefixed with `slot` (`slotHeader`).
|
|
111
113
|
|
|
112
114
|
```markdown
|
|
113
115
|
::card
|
package/dist/index.d.ts
CHANGED
|
@@ -104,7 +104,7 @@ interface MarkdownProps extends Omit<MarkdownDocumentProps, "value"> {
|
|
|
104
104
|
* @example
|
|
105
105
|
* ```tsx
|
|
106
106
|
* import { Markdown } from "comark-solid";
|
|
107
|
-
* import {
|
|
107
|
+
* import { Suspense } from "solid-js";
|
|
108
108
|
* import { CustomHeading } from "./CustomHeading";
|
|
109
109
|
*
|
|
110
110
|
* const components = {
|
|
@@ -113,12 +113,12 @@ interface MarkdownProps extends Omit<MarkdownDocumentProps, "value"> {
|
|
|
113
113
|
*
|
|
114
114
|
* export default function App() {
|
|
115
115
|
* return (
|
|
116
|
-
* <
|
|
116
|
+
* <Suspense fallback={<div>Loading...</div>}>
|
|
117
117
|
* <Markdown
|
|
118
118
|
* value={source}
|
|
119
119
|
* components={components}
|
|
120
120
|
* />
|
|
121
|
-
* </
|
|
121
|
+
* </Suspense>
|
|
122
122
|
* );
|
|
123
123
|
* }
|
|
124
124
|
* ```
|
package/dist/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { className, createComponent,
|
|
1
|
+
import { Dynamic, className, createComponent, effect, insert, mergeProps, template } from "solid-js/web";
|
|
2
2
|
import { parseMarkdown } from "comark";
|
|
3
|
-
import { isMarkdownDocument, resolveAttributes } from "comark/utils";
|
|
4
|
-
import {
|
|
3
|
+
import { isHtmlVoidElement, isMarkdownDocument, pascalCase, resolveAttributes, toNativeAttributes } from "comark/utils";
|
|
4
|
+
import { Show, Suspense, createMemo, createResource, mergeProps as mergeProps$1 } from "solid-js";
|
|
5
5
|
//#region src/utils/ast.ts
|
|
6
6
|
function getTag(node) {
|
|
7
7
|
if (Array.isArray(node) && node.length >= 1 && typeof node[0] === "string") return node[0];
|
|
@@ -17,7 +17,7 @@ function getChildren(node) {
|
|
|
17
17
|
}
|
|
18
18
|
//#endregion
|
|
19
19
|
//#region src/components/MarkdownDocument.tsx
|
|
20
|
-
var _tmpl$ =
|
|
20
|
+
var _tmpl$ = /*#__PURE__*/ template(`<div>`);
|
|
21
21
|
const DEFAULT_LOCAL_CONTEXT = {
|
|
22
22
|
parent: void 0,
|
|
23
23
|
renderData: {
|
|
@@ -36,26 +36,33 @@ function toElementNode(node) {
|
|
|
36
36
|
node
|
|
37
37
|
};
|
|
38
38
|
}
|
|
39
|
-
const VOID_ELEMENTS = /* @__PURE__ */ new Set([
|
|
40
|
-
"hr",
|
|
41
|
-
"br",
|
|
42
|
-
"img"
|
|
43
|
-
]);
|
|
44
39
|
function resolveProps(node, renderData) {
|
|
45
40
|
return resolveAttributes(getAttributes(node), renderData, { parseJson: true });
|
|
46
41
|
}
|
|
42
|
+
function scopeRenderData(node, props, renderData) {
|
|
43
|
+
return Object.keys(getAttributes(node)).some((key) => key !== "$") ? {
|
|
44
|
+
...renderData,
|
|
45
|
+
props
|
|
46
|
+
} : renderData;
|
|
47
|
+
}
|
|
47
48
|
function getSlotName(node) {
|
|
48
49
|
if (getTag(node) !== "template") return null;
|
|
49
50
|
const props = getAttributes(node);
|
|
50
|
-
|
|
51
|
-
|
|
51
|
+
const name = props.name || Object.keys(props).find((key) => key.startsWith("#"))?.slice(1);
|
|
52
|
+
if (typeof name !== "string" || name.trim() === "") return null;
|
|
53
|
+
return name.trim();
|
|
52
54
|
}
|
|
53
55
|
function getSlotPropName(slotName) {
|
|
54
56
|
if (slotName === "default") return "children";
|
|
55
57
|
return `slot${slotName.charAt(0).toUpperCase() + slotName.slice(1)}`;
|
|
56
58
|
}
|
|
57
59
|
function resolveComponent(element, components) {
|
|
58
|
-
|
|
60
|
+
function lookup(tag) {
|
|
61
|
+
const name = pascalCase(tag);
|
|
62
|
+
return components[`Prose${name}`] || components[name] || components[tag];
|
|
63
|
+
}
|
|
64
|
+
const as = getAttributes(element.node).as;
|
|
65
|
+
const component = typeof as === "string" && lookup(as) || lookup(element.tag);
|
|
59
66
|
if (!component) return null;
|
|
60
67
|
return {
|
|
61
68
|
...element,
|
|
@@ -77,28 +84,25 @@ function createNodeRenderer(components) {
|
|
|
77
84
|
function renderCustomComponent(element, context) {
|
|
78
85
|
const { component, node } = element;
|
|
79
86
|
const props = resolveProps(node, context.renderData);
|
|
80
|
-
const slots = partitionIntoSlots(node,
|
|
81
|
-
|
|
82
|
-
props
|
|
83
|
-
} : context.renderData);
|
|
84
|
-
const CustomComponent = dynamic(() => component);
|
|
85
|
-
return createComponent(Loading, {
|
|
87
|
+
const slots = partitionIntoSlots(node, scopeRenderData(node, props, context.renderData));
|
|
88
|
+
return createComponent(Suspense, {
|
|
86
89
|
fallback: null,
|
|
87
90
|
get children() {
|
|
88
|
-
return createComponent(
|
|
91
|
+
return createComponent(Dynamic, mergeProps({ component }, props, slots));
|
|
89
92
|
}
|
|
90
93
|
});
|
|
91
94
|
}
|
|
92
95
|
function renderNativeElement(element, context) {
|
|
93
96
|
const { tag, node } = element;
|
|
94
|
-
const
|
|
95
|
-
const
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
parent: node
|
|
97
|
+
const resolved = resolveProps(node, context.renderData);
|
|
98
|
+
const props = toNativeAttributes(tag, node, resolved);
|
|
99
|
+
const renderData = scopeRenderData(node, resolved, context.renderData);
|
|
100
|
+
if (isHtmlVoidElement(tag)) return createComponent(Dynamic, mergeProps({ component: tag }, props));
|
|
101
|
+
const children = getChildren(node).filter((child) => !getSlotName(child)).map((child) => renderNode(child, {
|
|
102
|
+
parent: node,
|
|
103
|
+
renderData
|
|
100
104
|
})).filter(Boolean);
|
|
101
|
-
return createComponent(
|
|
105
|
+
return createComponent(Dynamic, mergeProps({ component: tag }, props, { children }));
|
|
102
106
|
}
|
|
103
107
|
function partitionIntoSlots(node, childRenderData) {
|
|
104
108
|
const defaultSlot = [];
|
|
@@ -109,7 +113,7 @@ function createNodeRenderer(components) {
|
|
|
109
113
|
if (slotName) {
|
|
110
114
|
const propName = getSlotPropName(slotName);
|
|
111
115
|
namedSlots[propName] = getChildren(child).map((slotChild) => renderNode(slotChild, {
|
|
112
|
-
parent:
|
|
116
|
+
parent: node,
|
|
113
117
|
renderData: childRenderData
|
|
114
118
|
})).filter(Boolean);
|
|
115
119
|
continue;
|
|
@@ -152,7 +156,7 @@ function createNodeRenderer(components) {
|
|
|
152
156
|
* ```
|
|
153
157
|
*/
|
|
154
158
|
const MarkdownDocument = (_props) => {
|
|
155
|
-
const props =
|
|
159
|
+
const props = mergeProps$1({
|
|
156
160
|
components: {},
|
|
157
161
|
data: {},
|
|
158
162
|
class: void 0
|
|
@@ -172,12 +176,12 @@ const MarkdownDocument = (_props) => {
|
|
|
172
176
|
})).filter(Boolean);
|
|
173
177
|
});
|
|
174
178
|
const wrapperClass = createMemo(() => ["comark-content", props.class].map((cls) => cls?.trim()).filter(Boolean).join(" "));
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
className(_el$,
|
|
179
|
-
|
|
180
|
-
|
|
179
|
+
return (() => {
|
|
180
|
+
var _el$ = _tmpl$();
|
|
181
|
+
insert(_el$, renderedNodes);
|
|
182
|
+
effect(() => className(_el$, wrapperClass()));
|
|
183
|
+
return _el$;
|
|
184
|
+
})();
|
|
181
185
|
};
|
|
182
186
|
//#endregion
|
|
183
187
|
//#region src/components/Markdown.tsx
|
|
@@ -192,7 +196,7 @@ const MarkdownDocument = (_props) => {
|
|
|
192
196
|
* @example
|
|
193
197
|
* ```tsx
|
|
194
198
|
* import { Markdown } from "comark-solid";
|
|
195
|
-
* import {
|
|
199
|
+
* import { Suspense } from "solid-js";
|
|
196
200
|
* import { CustomHeading } from "./CustomHeading";
|
|
197
201
|
*
|
|
198
202
|
* const components = {
|
|
@@ -201,38 +205,44 @@ const MarkdownDocument = (_props) => {
|
|
|
201
205
|
*
|
|
202
206
|
* export default function App() {
|
|
203
207
|
* return (
|
|
204
|
-
* <
|
|
208
|
+
* <Suspense fallback={<div>Loading...</div>}>
|
|
205
209
|
* <Markdown
|
|
206
210
|
* value={source}
|
|
207
211
|
* components={components}
|
|
208
212
|
* />
|
|
209
|
-
* </
|
|
213
|
+
* </Suspense>
|
|
210
214
|
* );
|
|
211
215
|
* }
|
|
212
216
|
* ```
|
|
213
217
|
*/
|
|
214
218
|
const Markdown = (_props) => {
|
|
215
|
-
const props =
|
|
219
|
+
const props = mergeProps$1({
|
|
216
220
|
components: {},
|
|
217
221
|
data: {}
|
|
218
222
|
}, _props);
|
|
223
|
+
const [parsed] = createResource(() => typeof props.value === "string" ? [props.value, props.options] : null, ([value, options]) => parseMarkdown(value, options));
|
|
219
224
|
const document = createMemo(() => {
|
|
220
225
|
const value = props.value;
|
|
221
|
-
return isMarkdownDocument(value) ? value :
|
|
226
|
+
return isMarkdownDocument(value) ? value : parsed();
|
|
222
227
|
});
|
|
223
|
-
return createComponent(
|
|
224
|
-
get
|
|
228
|
+
return createComponent(Show, {
|
|
229
|
+
get when() {
|
|
225
230
|
return document();
|
|
226
231
|
},
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
232
|
+
children: (value) => createComponent(MarkdownDocument, {
|
|
233
|
+
get value() {
|
|
234
|
+
return value();
|
|
235
|
+
},
|
|
236
|
+
get components() {
|
|
237
|
+
return props.components;
|
|
238
|
+
},
|
|
239
|
+
get data() {
|
|
240
|
+
return props.data;
|
|
241
|
+
},
|
|
242
|
+
get ["class"]() {
|
|
243
|
+
return props.class;
|
|
244
|
+
}
|
|
245
|
+
})
|
|
236
246
|
});
|
|
237
247
|
};
|
|
238
248
|
//#endregion
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "comark-solid",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.1.1",
|
|
4
4
|
"description": "Solid renderer for Comark.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"comark",
|
|
@@ -34,23 +34,21 @@
|
|
|
34
34
|
"access": "public"
|
|
35
35
|
},
|
|
36
36
|
"devDependencies": {
|
|
37
|
-
"@solidjs/testing-library": "^
|
|
38
|
-
"@solidjs/vite-plugin": "^3.0.0-next.39",
|
|
39
|
-
"@solidjs/web": "2.0.0-rc.6",
|
|
37
|
+
"@solidjs/testing-library": "^0.8.10",
|
|
40
38
|
"@testing-library/jest-dom": "^7.0.1",
|
|
41
39
|
"bumpp": "^11.1.0",
|
|
42
|
-
"comark": "^0.
|
|
40
|
+
"comark": "^0.8.1",
|
|
43
41
|
"eslint-plugin-solid": "~0.18.0",
|
|
44
42
|
"jsdom": "^30.0.1",
|
|
45
|
-
"solid-js": "^
|
|
43
|
+
"solid-js": "^1.9.15",
|
|
46
44
|
"typescript": "^7.0.2",
|
|
47
45
|
"vite": "npm:@voidzero-dev/vite-plus-core@0.3.0",
|
|
46
|
+
"vite-plugin-solid": "^2.11.14",
|
|
48
47
|
"vite-plus": "0.3.0"
|
|
49
48
|
},
|
|
50
49
|
"peerDependencies": {
|
|
51
|
-
"
|
|
52
|
-
"
|
|
53
|
-
"solid-js": "^2.0.0-rc.6"
|
|
50
|
+
"comark": "^0.8.1",
|
|
51
|
+
"solid-js": "^1.9.0"
|
|
54
52
|
},
|
|
55
53
|
"devEngines": {
|
|
56
54
|
"packageManager": {
|