@mark-sorcery/vue 0.1.0 → 0.2.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.
- package/dist/index.cjs +64 -20
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +33 -60
- package/dist/index.d.ts +33 -60
- package/dist/index.js +61 -23
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/dist/index.cjs
CHANGED
|
@@ -3,6 +3,13 @@ let vue = require("vue");
|
|
|
3
3
|
let _mark_sorcery_markdown_parser = require("@mark-sorcery/markdown-parser");
|
|
4
4
|
|
|
5
5
|
//#region src/hast-to-vnodes.ts
|
|
6
|
+
function createNodeKey(node, path) {
|
|
7
|
+
const start = node.position?.start?.offset;
|
|
8
|
+
const end = node.position?.end?.offset;
|
|
9
|
+
if (typeof start === "number" && typeof end === "number") return `${node.tagName}:${start}-${end}`;
|
|
10
|
+
if (typeof start === "number") return `${node.tagName}:${start}`;
|
|
11
|
+
return path;
|
|
12
|
+
}
|
|
6
13
|
/**
|
|
7
14
|
* Convert HAST node properties to Vue-compatible props.
|
|
8
15
|
* - `className` array → `class` string
|
|
@@ -24,24 +31,24 @@ function resolveTag(node, components) {
|
|
|
24
31
|
* Internal recursive converter. `path` is a dot-separated string identifying
|
|
25
32
|
* the node's position in the tree (e.g. `"0"`, `"0.1"`, `"0.1.2"`).
|
|
26
33
|
*/
|
|
27
|
-
function toVNodes(node, components,
|
|
28
|
-
if (Array.isArray(node)) return node.flatMap((n, i) => toVNodes(n, components,
|
|
34
|
+
function toVNodes(node, components, path) {
|
|
35
|
+
if (Array.isArray(node)) return node.flatMap((n, i) => toVNodes(n, components, `${path}.${i}`));
|
|
29
36
|
switch (node.type) {
|
|
30
|
-
case "root": return node.children.flatMap((child, i) => toVNodes(child, components,
|
|
37
|
+
case "root": return node.children.flatMap((child, i) => toVNodes(child, components, String(i)));
|
|
31
38
|
case "element": {
|
|
32
39
|
const { properties = {}, children } = node;
|
|
33
40
|
const tag = resolveTag(node, components);
|
|
41
|
+
const nodeKey = createNodeKey(node, path);
|
|
34
42
|
const props = convertProps(properties);
|
|
35
|
-
const childVNodes = children.flatMap((child, i) => toVNodes(child, components,
|
|
36
|
-
|
|
43
|
+
const childVNodes = children.flatMap((child, i) => toVNodes(child, components, `${path}.${i}`));
|
|
44
|
+
return [typeof tag === "string" ? (0, vue.h)(tag, {
|
|
45
|
+
...props,
|
|
46
|
+
key: nodeKey
|
|
47
|
+
}, childVNodes) : (0, vue.h)(tag, {
|
|
37
48
|
...props,
|
|
49
|
+
key: nodeKey,
|
|
38
50
|
node
|
|
39
|
-
}, { default: () => childVNodes });
|
|
40
|
-
if (transition !== void 0) return [(0, vue.h)(vue.Transition, {
|
|
41
|
-
key: path,
|
|
42
|
-
...transition
|
|
43
|
-
}, { default: () => el })];
|
|
44
|
-
return [el];
|
|
51
|
+
}, { default: () => childVNodes })];
|
|
45
52
|
}
|
|
46
53
|
case "text": return [node.value];
|
|
47
54
|
case "comment": return [(0, vue.createCommentVNode)(node.value)];
|
|
@@ -55,8 +62,8 @@ function toVNodes(node, components, transition, path) {
|
|
|
55
62
|
* @param components - Custom component map or resolver function.
|
|
56
63
|
* @param transition - Optional `<Transition>` config applied to every element node.
|
|
57
64
|
*/
|
|
58
|
-
function hastToVNodes(node, components
|
|
59
|
-
return toVNodes(node, components,
|
|
65
|
+
function hastToVNodes(node, components) {
|
|
66
|
+
return toVNodes(node, components, "");
|
|
60
67
|
}
|
|
61
68
|
|
|
62
69
|
//#endregion
|
|
@@ -72,27 +79,64 @@ const Markdown = (0, vue.defineComponent)({
|
|
|
72
79
|
type: Object,
|
|
73
80
|
default: void 0
|
|
74
81
|
},
|
|
82
|
+
plugins: {
|
|
83
|
+
type: Array,
|
|
84
|
+
default: void 0
|
|
85
|
+
},
|
|
86
|
+
stream: {
|
|
87
|
+
type: Boolean,
|
|
88
|
+
default: false
|
|
89
|
+
},
|
|
75
90
|
components: {
|
|
76
91
|
type: [Object, Function],
|
|
77
92
|
default: () => ({})
|
|
78
|
-
},
|
|
79
|
-
transition: {
|
|
80
|
-
type: [Boolean, Object],
|
|
81
|
-
default: false
|
|
82
93
|
}
|
|
83
94
|
},
|
|
84
95
|
setup(props) {
|
|
85
|
-
const
|
|
96
|
+
const getMarkdown = () => props.markdown ?? "";
|
|
97
|
+
const processor = (0, vue.computed)(() => {
|
|
98
|
+
const options = props.options;
|
|
99
|
+
const propPlugins = props.plugins ?? [];
|
|
100
|
+
return (0, _mark_sorcery_markdown_parser.createProcessor)({
|
|
101
|
+
...options,
|
|
102
|
+
plugins: propPlugins
|
|
103
|
+
});
|
|
104
|
+
});
|
|
105
|
+
const hast = (0, vue.shallowRef)((0, _mark_sorcery_markdown_parser.parse)(processor.value, getMarkdown()));
|
|
106
|
+
let streamMemory;
|
|
107
|
+
let activeProcessor;
|
|
108
|
+
(0, vue.watchEffect)(() => {
|
|
109
|
+
const currentProcessor = processor.value;
|
|
110
|
+
const markdown = getMarkdown();
|
|
111
|
+
if (activeProcessor && activeProcessor !== currentProcessor) streamMemory = props.stream ? (0, _mark_sorcery_markdown_parser.createMemory)() : void 0;
|
|
112
|
+
activeProcessor = currentProcessor;
|
|
113
|
+
if (props.stream) {
|
|
114
|
+
streamMemory ??= (0, _mark_sorcery_markdown_parser.createMemory)();
|
|
115
|
+
hast.value = (0, _mark_sorcery_markdown_parser.parse)(currentProcessor, markdown, streamMemory);
|
|
116
|
+
return;
|
|
117
|
+
}
|
|
118
|
+
if (streamMemory) {
|
|
119
|
+
streamMemory.flush = true;
|
|
120
|
+
streamMemory = void 0;
|
|
121
|
+
return;
|
|
122
|
+
}
|
|
123
|
+
hast.value = (0, _mark_sorcery_markdown_parser.parse)(currentProcessor, markdown);
|
|
124
|
+
});
|
|
86
125
|
return () => {
|
|
87
126
|
const raw = props.components ?? {};
|
|
88
127
|
const components = typeof raw === "function" ? raw : Object.fromEntries(Object.entries(raw).map(([k, v]) => [k, typeof v === "string" || v == null ? v : (0, vue.markRaw)(v)]));
|
|
89
|
-
|
|
90
|
-
return (0, vue.h)(vue.Fragment, hastToVNodes(hast.value, components, transitionConfig));
|
|
128
|
+
return (0, vue.h)(vue.Fragment, hastToVNodes(hast.value, components));
|
|
91
129
|
};
|
|
92
130
|
}
|
|
93
131
|
});
|
|
94
132
|
|
|
95
133
|
//#endregion
|
|
96
134
|
exports.Markdown = Markdown;
|
|
135
|
+
Object.defineProperty(exports, 'createCorePlugin', {
|
|
136
|
+
enumerable: true,
|
|
137
|
+
get: function () {
|
|
138
|
+
return _mark_sorcery_markdown_parser.createCorePlugin;
|
|
139
|
+
}
|
|
140
|
+
});
|
|
97
141
|
exports.hastToVNodes = hastToVNodes;
|
|
98
142
|
//# sourceMappingURL=index.cjs.map
|
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.cjs","names":["
|
|
1
|
+
{"version":3,"file":"index.cjs","names":["Fragment"],"sources":["../src/hast-to-vnodes.ts","../src/Markdown.ts"],"sourcesContent":["import { h, createCommentVNode } from 'vue';\nimport type { Element, Nodes } from 'hast';\nimport type { VNodeArrayChildren } from 'vue';\nimport type { ComponentResolution, Components } from './types.ts';\n\nfunction createNodeKey(node: Element, path: string): string {\n const start = node.position?.start?.offset;\n const end = node.position?.end?.offset;\n\n if (typeof start === 'number' && typeof end === 'number') {\n return `${node.tagName}:${start}-${end}`;\n }\n\n if (typeof start === 'number') {\n return `${node.tagName}:${start}`;\n }\n\n return path;\n}\n\n/**\n * Convert HAST node properties to Vue-compatible props.\n * - `className` array → `class` string\n * - `htmlFor` → `for`\n * - All other properties pass through as-is\n */\nfunction convertProps(properties: Record<string, unknown>): Record<string, unknown> {\n const props: Record<string, unknown> = {};\n\n for (const [key, value] of Object.entries(properties)) {\n if (key === 'className' && Array.isArray(value)) {\n props['class'] = value.join(' ');\n } else if (key === 'htmlFor') {\n props['for'] = value;\n } else {\n props[key] = value;\n }\n }\n\n return props;\n}\n\n/** Resolve the tag/component for an element node from the Components option. */\nfunction resolveTag(node: Element, components: Components): NonNullable<ComponentResolution> {\n const resolved = typeof components === 'function'\n ? components(node)\n : components[node.tagName];\n return resolved ?? node.tagName;\n}\n\n/**\n * Internal recursive converter. `path` is a dot-separated string identifying\n * the node's position in the tree (e.g. `\"0\"`, `\"0.1\"`, `\"0.1.2\"`).\n */\nfunction toVNodes(\n node: Nodes | Nodes[],\n components: Components,\n path: string,\n): VNodeArrayChildren {\n if (Array.isArray(node)) {\n return node.flatMap((n, i) => toVNodes(n, components, `${path}.${i}`));\n }\n\n switch (node.type) {\n case 'root':\n return node.children.flatMap((child, i) =>\n toVNodes(child, components, String(i)),\n );\n\n case 'element': {\n const { properties = {}, children } = node;\n const tag = resolveTag(node, components);\n const nodeKey = createNodeKey(node, path);\n const props = convertProps(properties as Record<string, unknown>);\n const childVNodes: VNodeArrayChildren = children.flatMap((child, i) =>\n toVNodes(child, components, `${path}.${i}`),\n );\n\n // Build the element VNode\n // Custom Vue components also receive the raw HAST `node` prop so they\n // can access the original element (e.g. to extract text content for\n // syntax highlighting or diagram rendering).\n const el = typeof tag === 'string'\n ? h(tag, { ...props, key: nodeKey }, childVNodes)\n : h(tag, { ...props, key: nodeKey, node }, { default: () => childVNodes });\n\n return [el];\n }\n\n case 'text':\n return [node.value];\n\n case 'comment':\n return [createCommentVNode(node.value)];\n\n default:\n return [];\n }\n}\n\n/**\n * Recursively convert a HAST node (or array of nodes) into Vue VNodeArrayChildren.\n *\n * @param node - Root HAST node or array of nodes to convert.\n * @param components - Custom component map or resolver function.\n * @param transition - Optional `<Transition>` config applied to every element node.\n */\nexport function hastToVNodes(\n node: Nodes | Nodes[],\n components: Components,\n): VNodeArrayChildren {\n return toVNodes(node, components, '');\n}\n","import { computed, defineComponent, Fragment, h, markRaw, shallowRef, watchEffect } from 'vue';\nimport { createMemory, createProcessor, parse } from '@mark-sorcery/markdown-parser';\nimport { hastToVNodes } from './hast-to-vnodes.ts';\nimport type {\n Components,\n MarkdownProcessor,\n MarkdownOptions,\n MarkdownProps,\n ParseMemory,\n} from './types.ts';\n\nexport const Markdown = defineComponent({\n name: 'Markdown',\n\n props: {\n markdown: {\n type: String,\n required: true,\n },\n options: {\n type: Object as () => MarkdownOptions,\n default: undefined,\n },\n plugins: {\n type: Array as () => MarkdownProps['plugins'],\n default: undefined,\n },\n stream: {\n type: Boolean,\n default: false,\n },\n components: {\n type: [Object, Function] as unknown as () => Components,\n default: () => ({}),\n },\n } satisfies {\n [K in keyof MarkdownProps]-?: unknown;\n },\n\n setup(props) {\n const getMarkdown = () => props.markdown ?? '';\n\n const processor = computed<MarkdownProcessor>(() => {\n const options = props.options;\n const propPlugins = props.plugins ?? [];\n\n return createProcessor({\n ...options,\n plugins: propPlugins,\n });\n });\n\n const hast = shallowRef(parse(processor.value, getMarkdown()));\n let streamMemory: ParseMemory | undefined;\n let activeProcessor: MarkdownProcessor | undefined;\n\n watchEffect(() => {\n const currentProcessor = processor.value;\n const markdown = getMarkdown();\n\n if (activeProcessor && activeProcessor !== currentProcessor) {\n streamMemory = props.stream ? createMemory() : undefined;\n }\n\n activeProcessor = currentProcessor;\n\n if (props.stream) {\n streamMemory ??= createMemory();\n hast.value = parse(currentProcessor, markdown, streamMemory);\n return;\n }\n\n if (streamMemory) {\n streamMemory.flush = true;\n streamMemory = undefined;\n return;\n }\n\n hast.value = parse(currentProcessor, markdown);\n });\n\n return () => {\n const raw = props.components ?? {};\n // Function resolvers pass through; record values are wrapped in markRaw\n // so Vue doesn't make component objects reactive (perf warning prevention)\n const components: Components = typeof raw === 'function'\n ? raw\n : Object.fromEntries(\n Object.entries(raw).map(([k, v]) =>\n [k, typeof v === 'string' || v == null ? v : markRaw(v)],\n ),\n );\n const vnodes = hastToVNodes(hast.value, components);\n return h(Fragment, vnodes);\n };\n },\n});\n"],"mappings":"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"}
|
package/dist/index.d.cts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import * as vue from "vue";
|
|
2
|
-
import {
|
|
3
|
-
import
|
|
2
|
+
import { Component, VNodeArrayChildren } from "vue";
|
|
3
|
+
import * as _mark_sorcery_markdown_parser0 from "@mark-sorcery/markdown-parser";
|
|
4
|
+
import { CorePluginOptions, MarkdownProcessor, ParseMemory, ParseOptions, ParserPlugin, createCorePlugin } from "@mark-sorcery/markdown-parser";
|
|
4
5
|
|
|
5
6
|
//#region ../../node_modules/.bun/@types+unist@3.0.3/node_modules/@types/unist/index.d.ts
|
|
6
7
|
// ## Interfaces
|
|
@@ -317,36 +318,7 @@ interface Text extends Literal {
|
|
|
317
318
|
interface TextData extends Data {}
|
|
318
319
|
//#endregion
|
|
319
320
|
//#region src/types.d.ts
|
|
320
|
-
|
|
321
|
-
* Configuration forwarded to Vue's `<Transition>` component.
|
|
322
|
-
* All props are optional; when `transition: true` is used on `<Markdown>`,
|
|
323
|
-
* defaults to `{}` which activates Vue's default `v-enter-*` classes.
|
|
324
|
-
*
|
|
325
|
-
* @see https://vuejs.org/api/built-in-components.html#transition
|
|
326
|
-
*/
|
|
327
|
-
interface TransitionConfig {
|
|
328
|
-
name?: string;
|
|
329
|
-
css?: boolean;
|
|
330
|
-
appear?: boolean;
|
|
331
|
-
mode?: 'in-out' | 'out-in' | 'default';
|
|
332
|
-
enterFromClass?: string;
|
|
333
|
-
enterActiveClass?: string;
|
|
334
|
-
enterToClass?: string;
|
|
335
|
-
leaveFromClass?: string;
|
|
336
|
-
leaveActiveClass?: string;
|
|
337
|
-
leaveToClass?: string;
|
|
338
|
-
appearFromClass?: string;
|
|
339
|
-
appearActiveClass?: string;
|
|
340
|
-
appearToClass?: string;
|
|
341
|
-
onBeforeEnter?: BaseTransitionProps['onBeforeEnter'];
|
|
342
|
-
onEnter?: BaseTransitionProps['onEnter'];
|
|
343
|
-
onAfterEnter?: BaseTransitionProps['onAfterEnter'];
|
|
344
|
-
onEnterCancelled?: BaseTransitionProps['onEnterCancelled'];
|
|
345
|
-
onBeforeLeave?: BaseTransitionProps['onBeforeLeave'];
|
|
346
|
-
onLeave?: BaseTransitionProps['onLeave'];
|
|
347
|
-
onAfterLeave?: BaseTransitionProps['onAfterLeave'];
|
|
348
|
-
onLeaveCancelled?: BaseTransitionProps['onLeaveCancelled'];
|
|
349
|
-
}
|
|
321
|
+
type MarkdownOptions = Omit<ParseOptions, 'plugins'>;
|
|
350
322
|
/** A resolved component value: a Vue component, a tag string, or null/undefined to fall back to the default element. */
|
|
351
323
|
type ComponentResolution = Component | string | null | undefined;
|
|
352
324
|
/**
|
|
@@ -382,22 +354,14 @@ interface NodeProps {
|
|
|
382
354
|
interface MarkdownProps {
|
|
383
355
|
/** The markdown string to render. May be a partial/streaming string. */
|
|
384
356
|
markdown: string;
|
|
385
|
-
/** Options forwarded to the underlying markdown
|
|
386
|
-
options?:
|
|
357
|
+
/** Options forwarded to the underlying markdown processor factory, excluding plugins. */
|
|
358
|
+
options?: MarkdownOptions;
|
|
359
|
+
/** Plugins forwarded to the underlying markdown processor factory. */
|
|
360
|
+
plugins?: ParserPlugin[];
|
|
361
|
+
/** Enables parse memory so growing markdown streams preserve confirmed blocks. */
|
|
362
|
+
stream?: boolean;
|
|
387
363
|
/** Custom Vue components or tags to use in place of default HTML elements. */
|
|
388
364
|
components?: Components;
|
|
389
|
-
/**
|
|
390
|
-
* Optional Vue `<Transition>` configuration applied to every rendered element node.
|
|
391
|
-
*
|
|
392
|
-
* - `true` — enables transitions with Vue's default classes (`v-enter-*`).
|
|
393
|
-
* - `TransitionConfig` object — forwarded as props to each `<Transition>` wrapper.
|
|
394
|
-
* - `false` / `undefined` — no transitions (default).
|
|
395
|
-
*
|
|
396
|
-
* @example
|
|
397
|
-
* // Basic fade (add CSS: .fade-enter-active { transition: opacity 0.3s } .fade-enter-from { opacity: 0 })
|
|
398
|
-
* :transition="{ name: 'fade', appear: true }"
|
|
399
|
-
*/
|
|
400
|
-
transition?: boolean | TransitionConfig;
|
|
401
365
|
}
|
|
402
366
|
//#endregion
|
|
403
367
|
//#region src/Markdown.d.ts
|
|
@@ -407,17 +371,21 @@ declare const Markdown: vue.DefineComponent<vue.ExtractPropTypes<{
|
|
|
407
371
|
required: boolean;
|
|
408
372
|
};
|
|
409
373
|
options: {
|
|
410
|
-
type: () =>
|
|
374
|
+
type: () => MarkdownOptions;
|
|
375
|
+
default: undefined;
|
|
376
|
+
};
|
|
377
|
+
plugins: {
|
|
378
|
+
type: () => MarkdownProps["plugins"];
|
|
411
379
|
default: undefined;
|
|
412
380
|
};
|
|
381
|
+
stream: {
|
|
382
|
+
type: BooleanConstructor;
|
|
383
|
+
default: boolean;
|
|
384
|
+
};
|
|
413
385
|
components: {
|
|
414
386
|
type: () => Components;
|
|
415
387
|
default: () => {};
|
|
416
388
|
};
|
|
417
|
-
transition: {
|
|
418
|
-
type: () => boolean | TransitionConfig;
|
|
419
|
-
default: boolean;
|
|
420
|
-
};
|
|
421
389
|
}>, () => vue.VNode<vue.RendererNode, vue.RendererElement, {
|
|
422
390
|
[key: string]: any;
|
|
423
391
|
}>, {}, {}, {}, vue.ComponentOptionsMixin, vue.ComponentOptionsMixin, {}, string, vue.PublicProps, Readonly<vue.ExtractPropTypes<{
|
|
@@ -426,21 +394,26 @@ declare const Markdown: vue.DefineComponent<vue.ExtractPropTypes<{
|
|
|
426
394
|
required: boolean;
|
|
427
395
|
};
|
|
428
396
|
options: {
|
|
429
|
-
type: () =>
|
|
397
|
+
type: () => MarkdownOptions;
|
|
398
|
+
default: undefined;
|
|
399
|
+
};
|
|
400
|
+
plugins: {
|
|
401
|
+
type: () => MarkdownProps["plugins"];
|
|
430
402
|
default: undefined;
|
|
431
403
|
};
|
|
404
|
+
stream: {
|
|
405
|
+
type: BooleanConstructor;
|
|
406
|
+
default: boolean;
|
|
407
|
+
};
|
|
432
408
|
components: {
|
|
433
409
|
type: () => Components;
|
|
434
410
|
default: () => {};
|
|
435
411
|
};
|
|
436
|
-
transition: {
|
|
437
|
-
type: () => boolean | TransitionConfig;
|
|
438
|
-
default: boolean;
|
|
439
|
-
};
|
|
440
412
|
}>> & Readonly<{}>, {
|
|
441
|
-
options:
|
|
413
|
+
options: MarkdownOptions;
|
|
414
|
+
plugins: _mark_sorcery_markdown_parser0.ParserPlugin[] | undefined;
|
|
415
|
+
stream: boolean;
|
|
442
416
|
components: Components;
|
|
443
|
-
transition: boolean | TransitionConfig;
|
|
444
417
|
}, {}, {}, {}, string, vue.ComponentProvideOptions, true, {}, any>;
|
|
445
418
|
//#endregion
|
|
446
419
|
//#region src/hast-to-vnodes.d.ts
|
|
@@ -451,7 +424,7 @@ declare const Markdown: vue.DefineComponent<vue.ExtractPropTypes<{
|
|
|
451
424
|
* @param components - Custom component map or resolver function.
|
|
452
425
|
* @param transition - Optional `<Transition>` config applied to every element node.
|
|
453
426
|
*/
|
|
454
|
-
declare function hastToVNodes(node: Nodes | Nodes[], components: Components
|
|
427
|
+
declare function hastToVNodes(node: Nodes | Nodes[], components: Components): VNodeArrayChildren;
|
|
455
428
|
//#endregion
|
|
456
|
-
export { type Components, Markdown, type MarkdownProps, type NodeProps, type ParseOptions, type
|
|
429
|
+
export { type Components, type CorePluginOptions, Markdown, type MarkdownOptions, type MarkdownProcessor, type MarkdownProps, type NodeProps, type ParseMemory, type ParseOptions, type ParserPlugin, createCorePlugin, hastToVNodes };
|
|
457
430
|
//# sourceMappingURL=index.d.cts.map
|
package/dist/index.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import * as vue from "vue";
|
|
2
|
-
import {
|
|
3
|
-
import
|
|
2
|
+
import { Component, VNodeArrayChildren } from "vue";
|
|
3
|
+
import * as _mark_sorcery_markdown_parser0 from "@mark-sorcery/markdown-parser";
|
|
4
|
+
import { CorePluginOptions, MarkdownProcessor, ParseMemory, ParseOptions, ParserPlugin, createCorePlugin } from "@mark-sorcery/markdown-parser";
|
|
4
5
|
|
|
5
6
|
//#region ../../node_modules/.bun/@types+unist@3.0.3/node_modules/@types/unist/index.d.ts
|
|
6
7
|
// ## Interfaces
|
|
@@ -317,36 +318,7 @@ interface Text extends Literal {
|
|
|
317
318
|
interface TextData extends Data {}
|
|
318
319
|
//#endregion
|
|
319
320
|
//#region src/types.d.ts
|
|
320
|
-
|
|
321
|
-
* Configuration forwarded to Vue's `<Transition>` component.
|
|
322
|
-
* All props are optional; when `transition: true` is used on `<Markdown>`,
|
|
323
|
-
* defaults to `{}` which activates Vue's default `v-enter-*` classes.
|
|
324
|
-
*
|
|
325
|
-
* @see https://vuejs.org/api/built-in-components.html#transition
|
|
326
|
-
*/
|
|
327
|
-
interface TransitionConfig {
|
|
328
|
-
name?: string;
|
|
329
|
-
css?: boolean;
|
|
330
|
-
appear?: boolean;
|
|
331
|
-
mode?: 'in-out' | 'out-in' | 'default';
|
|
332
|
-
enterFromClass?: string;
|
|
333
|
-
enterActiveClass?: string;
|
|
334
|
-
enterToClass?: string;
|
|
335
|
-
leaveFromClass?: string;
|
|
336
|
-
leaveActiveClass?: string;
|
|
337
|
-
leaveToClass?: string;
|
|
338
|
-
appearFromClass?: string;
|
|
339
|
-
appearActiveClass?: string;
|
|
340
|
-
appearToClass?: string;
|
|
341
|
-
onBeforeEnter?: BaseTransitionProps['onBeforeEnter'];
|
|
342
|
-
onEnter?: BaseTransitionProps['onEnter'];
|
|
343
|
-
onAfterEnter?: BaseTransitionProps['onAfterEnter'];
|
|
344
|
-
onEnterCancelled?: BaseTransitionProps['onEnterCancelled'];
|
|
345
|
-
onBeforeLeave?: BaseTransitionProps['onBeforeLeave'];
|
|
346
|
-
onLeave?: BaseTransitionProps['onLeave'];
|
|
347
|
-
onAfterLeave?: BaseTransitionProps['onAfterLeave'];
|
|
348
|
-
onLeaveCancelled?: BaseTransitionProps['onLeaveCancelled'];
|
|
349
|
-
}
|
|
321
|
+
type MarkdownOptions = Omit<ParseOptions, 'plugins'>;
|
|
350
322
|
/** A resolved component value: a Vue component, a tag string, or null/undefined to fall back to the default element. */
|
|
351
323
|
type ComponentResolution = Component | string | null | undefined;
|
|
352
324
|
/**
|
|
@@ -382,22 +354,14 @@ interface NodeProps {
|
|
|
382
354
|
interface MarkdownProps {
|
|
383
355
|
/** The markdown string to render. May be a partial/streaming string. */
|
|
384
356
|
markdown: string;
|
|
385
|
-
/** Options forwarded to the underlying markdown
|
|
386
|
-
options?:
|
|
357
|
+
/** Options forwarded to the underlying markdown processor factory, excluding plugins. */
|
|
358
|
+
options?: MarkdownOptions;
|
|
359
|
+
/** Plugins forwarded to the underlying markdown processor factory. */
|
|
360
|
+
plugins?: ParserPlugin[];
|
|
361
|
+
/** Enables parse memory so growing markdown streams preserve confirmed blocks. */
|
|
362
|
+
stream?: boolean;
|
|
387
363
|
/** Custom Vue components or tags to use in place of default HTML elements. */
|
|
388
364
|
components?: Components;
|
|
389
|
-
/**
|
|
390
|
-
* Optional Vue `<Transition>` configuration applied to every rendered element node.
|
|
391
|
-
*
|
|
392
|
-
* - `true` — enables transitions with Vue's default classes (`v-enter-*`).
|
|
393
|
-
* - `TransitionConfig` object — forwarded as props to each `<Transition>` wrapper.
|
|
394
|
-
* - `false` / `undefined` — no transitions (default).
|
|
395
|
-
*
|
|
396
|
-
* @example
|
|
397
|
-
* // Basic fade (add CSS: .fade-enter-active { transition: opacity 0.3s } .fade-enter-from { opacity: 0 })
|
|
398
|
-
* :transition="{ name: 'fade', appear: true }"
|
|
399
|
-
*/
|
|
400
|
-
transition?: boolean | TransitionConfig;
|
|
401
365
|
}
|
|
402
366
|
//#endregion
|
|
403
367
|
//#region src/Markdown.d.ts
|
|
@@ -407,17 +371,21 @@ declare const Markdown: vue.DefineComponent<vue.ExtractPropTypes<{
|
|
|
407
371
|
required: boolean;
|
|
408
372
|
};
|
|
409
373
|
options: {
|
|
410
|
-
type: () =>
|
|
374
|
+
type: () => MarkdownOptions;
|
|
375
|
+
default: undefined;
|
|
376
|
+
};
|
|
377
|
+
plugins: {
|
|
378
|
+
type: () => MarkdownProps["plugins"];
|
|
411
379
|
default: undefined;
|
|
412
380
|
};
|
|
381
|
+
stream: {
|
|
382
|
+
type: BooleanConstructor;
|
|
383
|
+
default: boolean;
|
|
384
|
+
};
|
|
413
385
|
components: {
|
|
414
386
|
type: () => Components;
|
|
415
387
|
default: () => {};
|
|
416
388
|
};
|
|
417
|
-
transition: {
|
|
418
|
-
type: () => boolean | TransitionConfig;
|
|
419
|
-
default: boolean;
|
|
420
|
-
};
|
|
421
389
|
}>, () => vue.VNode<vue.RendererNode, vue.RendererElement, {
|
|
422
390
|
[key: string]: any;
|
|
423
391
|
}>, {}, {}, {}, vue.ComponentOptionsMixin, vue.ComponentOptionsMixin, {}, string, vue.PublicProps, Readonly<vue.ExtractPropTypes<{
|
|
@@ -426,21 +394,26 @@ declare const Markdown: vue.DefineComponent<vue.ExtractPropTypes<{
|
|
|
426
394
|
required: boolean;
|
|
427
395
|
};
|
|
428
396
|
options: {
|
|
429
|
-
type: () =>
|
|
397
|
+
type: () => MarkdownOptions;
|
|
398
|
+
default: undefined;
|
|
399
|
+
};
|
|
400
|
+
plugins: {
|
|
401
|
+
type: () => MarkdownProps["plugins"];
|
|
430
402
|
default: undefined;
|
|
431
403
|
};
|
|
404
|
+
stream: {
|
|
405
|
+
type: BooleanConstructor;
|
|
406
|
+
default: boolean;
|
|
407
|
+
};
|
|
432
408
|
components: {
|
|
433
409
|
type: () => Components;
|
|
434
410
|
default: () => {};
|
|
435
411
|
};
|
|
436
|
-
transition: {
|
|
437
|
-
type: () => boolean | TransitionConfig;
|
|
438
|
-
default: boolean;
|
|
439
|
-
};
|
|
440
412
|
}>> & Readonly<{}>, {
|
|
441
|
-
options:
|
|
413
|
+
options: MarkdownOptions;
|
|
414
|
+
plugins: _mark_sorcery_markdown_parser0.ParserPlugin[] | undefined;
|
|
415
|
+
stream: boolean;
|
|
442
416
|
components: Components;
|
|
443
|
-
transition: boolean | TransitionConfig;
|
|
444
417
|
}, {}, {}, {}, string, vue.ComponentProvideOptions, true, {}, any>;
|
|
445
418
|
//#endregion
|
|
446
419
|
//#region src/hast-to-vnodes.d.ts
|
|
@@ -451,7 +424,7 @@ declare const Markdown: vue.DefineComponent<vue.ExtractPropTypes<{
|
|
|
451
424
|
* @param components - Custom component map or resolver function.
|
|
452
425
|
* @param transition - Optional `<Transition>` config applied to every element node.
|
|
453
426
|
*/
|
|
454
|
-
declare function hastToVNodes(node: Nodes | Nodes[], components: Components
|
|
427
|
+
declare function hastToVNodes(node: Nodes | Nodes[], components: Components): VNodeArrayChildren;
|
|
455
428
|
//#endregion
|
|
456
|
-
export { type Components, Markdown, type MarkdownProps, type NodeProps, type ParseOptions, type
|
|
429
|
+
export { type Components, type CorePluginOptions, Markdown, type MarkdownOptions, type MarkdownProcessor, type MarkdownProps, type NodeProps, type ParseMemory, type ParseOptions, type ParserPlugin, createCorePlugin, hastToVNodes };
|
|
457
430
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.js
CHANGED
|
@@ -1,7 +1,14 @@
|
|
|
1
|
-
import { Fragment,
|
|
2
|
-
import { parse } from "@mark-sorcery/markdown-parser";
|
|
1
|
+
import { Fragment, computed, createCommentVNode, defineComponent, h, markRaw, shallowRef, watchEffect } from "vue";
|
|
2
|
+
import { createCorePlugin, createMemory, createProcessor, parse } from "@mark-sorcery/markdown-parser";
|
|
3
3
|
|
|
4
4
|
//#region src/hast-to-vnodes.ts
|
|
5
|
+
function createNodeKey(node, path) {
|
|
6
|
+
const start = node.position?.start?.offset;
|
|
7
|
+
const end = node.position?.end?.offset;
|
|
8
|
+
if (typeof start === "number" && typeof end === "number") return `${node.tagName}:${start}-${end}`;
|
|
9
|
+
if (typeof start === "number") return `${node.tagName}:${start}`;
|
|
10
|
+
return path;
|
|
11
|
+
}
|
|
5
12
|
/**
|
|
6
13
|
* Convert HAST node properties to Vue-compatible props.
|
|
7
14
|
* - `className` array → `class` string
|
|
@@ -23,24 +30,24 @@ function resolveTag(node, components) {
|
|
|
23
30
|
* Internal recursive converter. `path` is a dot-separated string identifying
|
|
24
31
|
* the node's position in the tree (e.g. `"0"`, `"0.1"`, `"0.1.2"`).
|
|
25
32
|
*/
|
|
26
|
-
function toVNodes(node, components,
|
|
27
|
-
if (Array.isArray(node)) return node.flatMap((n, i) => toVNodes(n, components,
|
|
33
|
+
function toVNodes(node, components, path) {
|
|
34
|
+
if (Array.isArray(node)) return node.flatMap((n, i) => toVNodes(n, components, `${path}.${i}`));
|
|
28
35
|
switch (node.type) {
|
|
29
|
-
case "root": return node.children.flatMap((child, i) => toVNodes(child, components,
|
|
36
|
+
case "root": return node.children.flatMap((child, i) => toVNodes(child, components, String(i)));
|
|
30
37
|
case "element": {
|
|
31
38
|
const { properties = {}, children } = node;
|
|
32
39
|
const tag = resolveTag(node, components);
|
|
40
|
+
const nodeKey = createNodeKey(node, path);
|
|
33
41
|
const props = convertProps(properties);
|
|
34
|
-
const childVNodes = children.flatMap((child, i) => toVNodes(child, components,
|
|
35
|
-
|
|
42
|
+
const childVNodes = children.flatMap((child, i) => toVNodes(child, components, `${path}.${i}`));
|
|
43
|
+
return [typeof tag === "string" ? h(tag, {
|
|
44
|
+
...props,
|
|
45
|
+
key: nodeKey
|
|
46
|
+
}, childVNodes) : h(tag, {
|
|
36
47
|
...props,
|
|
48
|
+
key: nodeKey,
|
|
37
49
|
node
|
|
38
|
-
}, { default: () => childVNodes });
|
|
39
|
-
if (transition !== void 0) return [h(Transition, {
|
|
40
|
-
key: path,
|
|
41
|
-
...transition
|
|
42
|
-
}, { default: () => el })];
|
|
43
|
-
return [el];
|
|
50
|
+
}, { default: () => childVNodes })];
|
|
44
51
|
}
|
|
45
52
|
case "text": return [node.value];
|
|
46
53
|
case "comment": return [createCommentVNode(node.value)];
|
|
@@ -54,8 +61,8 @@ function toVNodes(node, components, transition, path) {
|
|
|
54
61
|
* @param components - Custom component map or resolver function.
|
|
55
62
|
* @param transition - Optional `<Transition>` config applied to every element node.
|
|
56
63
|
*/
|
|
57
|
-
function hastToVNodes(node, components
|
|
58
|
-
return toVNodes(node, components,
|
|
64
|
+
function hastToVNodes(node, components) {
|
|
65
|
+
return toVNodes(node, components, "");
|
|
59
66
|
}
|
|
60
67
|
|
|
61
68
|
//#endregion
|
|
@@ -71,26 +78,57 @@ const Markdown = defineComponent({
|
|
|
71
78
|
type: Object,
|
|
72
79
|
default: void 0
|
|
73
80
|
},
|
|
81
|
+
plugins: {
|
|
82
|
+
type: Array,
|
|
83
|
+
default: void 0
|
|
84
|
+
},
|
|
85
|
+
stream: {
|
|
86
|
+
type: Boolean,
|
|
87
|
+
default: false
|
|
88
|
+
},
|
|
74
89
|
components: {
|
|
75
90
|
type: [Object, Function],
|
|
76
91
|
default: () => ({})
|
|
77
|
-
},
|
|
78
|
-
transition: {
|
|
79
|
-
type: [Boolean, Object],
|
|
80
|
-
default: false
|
|
81
92
|
}
|
|
82
93
|
},
|
|
83
94
|
setup(props) {
|
|
84
|
-
const
|
|
95
|
+
const getMarkdown = () => props.markdown ?? "";
|
|
96
|
+
const processor = computed(() => {
|
|
97
|
+
const options = props.options;
|
|
98
|
+
const propPlugins = props.plugins ?? [];
|
|
99
|
+
return createProcessor({
|
|
100
|
+
...options,
|
|
101
|
+
plugins: propPlugins
|
|
102
|
+
});
|
|
103
|
+
});
|
|
104
|
+
const hast = shallowRef(parse(processor.value, getMarkdown()));
|
|
105
|
+
let streamMemory;
|
|
106
|
+
let activeProcessor;
|
|
107
|
+
watchEffect(() => {
|
|
108
|
+
const currentProcessor = processor.value;
|
|
109
|
+
const markdown = getMarkdown();
|
|
110
|
+
if (activeProcessor && activeProcessor !== currentProcessor) streamMemory = props.stream ? createMemory() : void 0;
|
|
111
|
+
activeProcessor = currentProcessor;
|
|
112
|
+
if (props.stream) {
|
|
113
|
+
streamMemory ??= createMemory();
|
|
114
|
+
hast.value = parse(currentProcessor, markdown, streamMemory);
|
|
115
|
+
return;
|
|
116
|
+
}
|
|
117
|
+
if (streamMemory) {
|
|
118
|
+
streamMemory.flush = true;
|
|
119
|
+
streamMemory = void 0;
|
|
120
|
+
return;
|
|
121
|
+
}
|
|
122
|
+
hast.value = parse(currentProcessor, markdown);
|
|
123
|
+
});
|
|
85
124
|
return () => {
|
|
86
125
|
const raw = props.components ?? {};
|
|
87
126
|
const components = typeof raw === "function" ? raw : Object.fromEntries(Object.entries(raw).map(([k, v]) => [k, typeof v === "string" || v == null ? v : markRaw(v)]));
|
|
88
|
-
|
|
89
|
-
return h(Fragment, hastToVNodes(hast.value, components, transitionConfig));
|
|
127
|
+
return h(Fragment, hastToVNodes(hast.value, components));
|
|
90
128
|
};
|
|
91
129
|
}
|
|
92
130
|
});
|
|
93
131
|
|
|
94
132
|
//#endregion
|
|
95
|
-
export { Markdown, hastToVNodes };
|
|
133
|
+
export { Markdown, createCorePlugin, hastToVNodes };
|
|
96
134
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":[],"sources":["../src/hast-to-vnodes.ts","../src/Markdown.ts"],"sourcesContent":["import { h, createCommentVNode
|
|
1
|
+
{"version":3,"file":"index.js","names":[],"sources":["../src/hast-to-vnodes.ts","../src/Markdown.ts"],"sourcesContent":["import { h, createCommentVNode } from 'vue';\nimport type { Element, Nodes } from 'hast';\nimport type { VNodeArrayChildren } from 'vue';\nimport type { ComponentResolution, Components } from './types.ts';\n\nfunction createNodeKey(node: Element, path: string): string {\n const start = node.position?.start?.offset;\n const end = node.position?.end?.offset;\n\n if (typeof start === 'number' && typeof end === 'number') {\n return `${node.tagName}:${start}-${end}`;\n }\n\n if (typeof start === 'number') {\n return `${node.tagName}:${start}`;\n }\n\n return path;\n}\n\n/**\n * Convert HAST node properties to Vue-compatible props.\n * - `className` array → `class` string\n * - `htmlFor` → `for`\n * - All other properties pass through as-is\n */\nfunction convertProps(properties: Record<string, unknown>): Record<string, unknown> {\n const props: Record<string, unknown> = {};\n\n for (const [key, value] of Object.entries(properties)) {\n if (key === 'className' && Array.isArray(value)) {\n props['class'] = value.join(' ');\n } else if (key === 'htmlFor') {\n props['for'] = value;\n } else {\n props[key] = value;\n }\n }\n\n return props;\n}\n\n/** Resolve the tag/component for an element node from the Components option. */\nfunction resolveTag(node: Element, components: Components): NonNullable<ComponentResolution> {\n const resolved = typeof components === 'function'\n ? components(node)\n : components[node.tagName];\n return resolved ?? node.tagName;\n}\n\n/**\n * Internal recursive converter. `path` is a dot-separated string identifying\n * the node's position in the tree (e.g. `\"0\"`, `\"0.1\"`, `\"0.1.2\"`).\n */\nfunction toVNodes(\n node: Nodes | Nodes[],\n components: Components,\n path: string,\n): VNodeArrayChildren {\n if (Array.isArray(node)) {\n return node.flatMap((n, i) => toVNodes(n, components, `${path}.${i}`));\n }\n\n switch (node.type) {\n case 'root':\n return node.children.flatMap((child, i) =>\n toVNodes(child, components, String(i)),\n );\n\n case 'element': {\n const { properties = {}, children } = node;\n const tag = resolveTag(node, components);\n const nodeKey = createNodeKey(node, path);\n const props = convertProps(properties as Record<string, unknown>);\n const childVNodes: VNodeArrayChildren = children.flatMap((child, i) =>\n toVNodes(child, components, `${path}.${i}`),\n );\n\n // Build the element VNode\n // Custom Vue components also receive the raw HAST `node` prop so they\n // can access the original element (e.g. to extract text content for\n // syntax highlighting or diagram rendering).\n const el = typeof tag === 'string'\n ? h(tag, { ...props, key: nodeKey }, childVNodes)\n : h(tag, { ...props, key: nodeKey, node }, { default: () => childVNodes });\n\n return [el];\n }\n\n case 'text':\n return [node.value];\n\n case 'comment':\n return [createCommentVNode(node.value)];\n\n default:\n return [];\n }\n}\n\n/**\n * Recursively convert a HAST node (or array of nodes) into Vue VNodeArrayChildren.\n *\n * @param node - Root HAST node or array of nodes to convert.\n * @param components - Custom component map or resolver function.\n * @param transition - Optional `<Transition>` config applied to every element node.\n */\nexport function hastToVNodes(\n node: Nodes | Nodes[],\n components: Components,\n): VNodeArrayChildren {\n return toVNodes(node, components, '');\n}\n","import { computed, defineComponent, Fragment, h, markRaw, shallowRef, watchEffect } from 'vue';\nimport { createMemory, createProcessor, parse } from '@mark-sorcery/markdown-parser';\nimport { hastToVNodes } from './hast-to-vnodes.ts';\nimport type {\n Components,\n MarkdownProcessor,\n MarkdownOptions,\n MarkdownProps,\n ParseMemory,\n} from './types.ts';\n\nexport const Markdown = defineComponent({\n name: 'Markdown',\n\n props: {\n markdown: {\n type: String,\n required: true,\n },\n options: {\n type: Object as () => MarkdownOptions,\n default: undefined,\n },\n plugins: {\n type: Array as () => MarkdownProps['plugins'],\n default: undefined,\n },\n stream: {\n type: Boolean,\n default: false,\n },\n components: {\n type: [Object, Function] as unknown as () => Components,\n default: () => ({}),\n },\n } satisfies {\n [K in keyof MarkdownProps]-?: unknown;\n },\n\n setup(props) {\n const getMarkdown = () => props.markdown ?? '';\n\n const processor = computed<MarkdownProcessor>(() => {\n const options = props.options;\n const propPlugins = props.plugins ?? [];\n\n return createProcessor({\n ...options,\n plugins: propPlugins,\n });\n });\n\n const hast = shallowRef(parse(processor.value, getMarkdown()));\n let streamMemory: ParseMemory | undefined;\n let activeProcessor: MarkdownProcessor | undefined;\n\n watchEffect(() => {\n const currentProcessor = processor.value;\n const markdown = getMarkdown();\n\n if (activeProcessor && activeProcessor !== currentProcessor) {\n streamMemory = props.stream ? createMemory() : undefined;\n }\n\n activeProcessor = currentProcessor;\n\n if (props.stream) {\n streamMemory ??= createMemory();\n hast.value = parse(currentProcessor, markdown, streamMemory);\n return;\n }\n\n if (streamMemory) {\n streamMemory.flush = true;\n streamMemory = undefined;\n return;\n }\n\n hast.value = parse(currentProcessor, markdown);\n });\n\n return () => {\n const raw = props.components ?? {};\n // Function resolvers pass through; record values are wrapped in markRaw\n // so Vue doesn't make component objects reactive (perf warning prevention)\n const components: Components = typeof raw === 'function'\n ? raw\n : Object.fromEntries(\n Object.entries(raw).map(([k, v]) =>\n [k, typeof v === 'string' || v == null ? v : markRaw(v)],\n ),\n );\n const vnodes = hastToVNodes(hast.value, components);\n return h(Fragment, vnodes);\n };\n },\n});\n"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mark-sorcery/vue",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"description": "Vue renderer for Mark Sorcery markdown.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"files": [
|
|
@@ -27,7 +27,7 @@
|
|
|
27
27
|
"test": "vitest run"
|
|
28
28
|
},
|
|
29
29
|
"dependencies": {
|
|
30
|
-
"@mark-sorcery/markdown-parser": "^0.
|
|
30
|
+
"@mark-sorcery/markdown-parser": "^0.2.0"
|
|
31
31
|
},
|
|
32
32
|
"devDependencies": {
|
|
33
33
|
"@types/hast": "^3.0.4",
|