@vmz/plugin-markdown-it 0.0.0 → 0.0.3
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 +1 -1
- package/components/MarkdownIt.vmz +19 -0
- package/package.json +33 -6
- package/runtime.ts +103 -0
- package/vmz.plugin.ts +50 -0
package/README.md
CHANGED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div class="markdown-host" html={format(source)}></div>
|
|
3
|
+
</template>
|
|
4
|
+
|
|
5
|
+
<script client>
|
|
6
|
+
import { renderMarkdown } from '@vmz/plugin-markdown-it/runtime';
|
|
7
|
+
|
|
8
|
+
function format(source) {
|
|
9
|
+
try {
|
|
10
|
+
return renderMarkdown(source ?? '');
|
|
11
|
+
} catch {
|
|
12
|
+
return '';
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export default class MarkdownIt {
|
|
17
|
+
public source: string = '';
|
|
18
|
+
}
|
|
19
|
+
</script>
|
package/package.json
CHANGED
|
@@ -1,10 +1,37 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vmz/plugin-markdown-it",
|
|
3
|
-
"version": "0.0.
|
|
4
|
-
"
|
|
3
|
+
"version": "0.0.3",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"description": "VMZ markdown-it adapter — MarkdownIt + engines.markdown registration",
|
|
5
6
|
"license": "MIT",
|
|
6
|
-
"
|
|
7
|
-
"
|
|
8
|
-
|
|
9
|
-
|
|
7
|
+
"main": "./vmz.plugin.ts",
|
|
8
|
+
"types": "./vmz.plugin.ts",
|
|
9
|
+
"exports": {
|
|
10
|
+
".": {
|
|
11
|
+
"types": "./vmz.plugin.ts",
|
|
12
|
+
"default": "./vmz.plugin.ts"
|
|
13
|
+
},
|
|
14
|
+
"./runtime": {
|
|
15
|
+
"types": "./runtime.ts",
|
|
16
|
+
"default": "./runtime.ts"
|
|
17
|
+
},
|
|
18
|
+
"./package.json": "./package.json"
|
|
19
|
+
},
|
|
20
|
+
"dependencies": {
|
|
21
|
+
"@vmz/plugin": "0.0.3",
|
|
22
|
+
"markdown-it": "^14.1.0"
|
|
23
|
+
},
|
|
24
|
+
"keywords": [
|
|
25
|
+
"vmz",
|
|
26
|
+
"plugin",
|
|
27
|
+
"markdown",
|
|
28
|
+
"markdown-it"
|
|
29
|
+
],
|
|
30
|
+
"publishConfig": {
|
|
31
|
+
"access": "public"
|
|
32
|
+
},
|
|
33
|
+
"repository": {
|
|
34
|
+
"type": "git",
|
|
35
|
+
"url": "git+https://github.com/doki-land/vmz-framework.git"
|
|
36
|
+
}
|
|
10
37
|
}
|
package/runtime.ts
ADDED
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Deterministic CommonMark-ish subset for documents + `<Markdown>` .
|
|
3
|
+
*/
|
|
4
|
+
import MarkdownIt from 'markdown-it';
|
|
5
|
+
|
|
6
|
+
/** @type {import('markdown-it').default | null} */
|
|
7
|
+
let cached = null;
|
|
8
|
+
|
|
9
|
+
function createMd() {
|
|
10
|
+
if (cached) return cached;
|
|
11
|
+
cached = new MarkdownIt({
|
|
12
|
+
html: false,
|
|
13
|
+
linkify: true,
|
|
14
|
+
typographer: false,
|
|
15
|
+
breaks: false,
|
|
16
|
+
});
|
|
17
|
+
return cached;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* @param {string} source
|
|
22
|
+
* @returns {string} HTML fragment (no wrapping document)
|
|
23
|
+
*/
|
|
24
|
+
export function renderMarkdown(source) {
|
|
25
|
+
const md = createMd();
|
|
26
|
+
return md.render(String(source ?? ''));
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* @param {string} source
|
|
31
|
+
* @returns {{
|
|
32
|
+
* html: string,
|
|
33
|
+
* headings: Array<{ level: number, id: string, text: string }>,
|
|
34
|
+
* links: Array<{ href: string, text: string }>,
|
|
35
|
+
* fences: Array<{ lang: string, info: string, content: string, lineStart: number, lineEnd: number }>
|
|
36
|
+
* }}
|
|
37
|
+
*/
|
|
38
|
+
export function analyzeMarkdown(source) {
|
|
39
|
+
const md = createMd();
|
|
40
|
+
const tokens = md.parse(String(source ?? ''), {});
|
|
41
|
+
/** @type {Array<{ level: number, id: string, text: string }>} */
|
|
42
|
+
const headings = [];
|
|
43
|
+
/** @type {Array<{ href: string, text: string }>} */
|
|
44
|
+
const links = [];
|
|
45
|
+
/** @type {Array<{ lang: string, info: string, content: string, lineStart: number, lineEnd: number }>} */
|
|
46
|
+
const fences = [];
|
|
47
|
+
const seenIds = new Set();
|
|
48
|
+
|
|
49
|
+
for (let i = 0; i < tokens.length; i++) {
|
|
50
|
+
const t = tokens[i];
|
|
51
|
+
if (t.type === 'heading_open') {
|
|
52
|
+
const level = Number(String(t.tag || 'h1').slice(1)) || 1;
|
|
53
|
+
const inline = tokens[i + 1];
|
|
54
|
+
const text = inline && inline.type === 'inline' ? inline.content : '';
|
|
55
|
+
let id = slugify(text);
|
|
56
|
+
if (seenIds.has(id)) {
|
|
57
|
+
let n = 2;
|
|
58
|
+
while (seenIds.has(`${id}-${n}`)) n++;
|
|
59
|
+
id = `${id}-${n}`;
|
|
60
|
+
}
|
|
61
|
+
seenIds.add(id);
|
|
62
|
+
headings.push({ level, id, text });
|
|
63
|
+
// Inject id into open token attrs for render.
|
|
64
|
+
t.attrSet('id', id);
|
|
65
|
+
}
|
|
66
|
+
if (t.type === 'fence') {
|
|
67
|
+
const info = String(t.info || '').trim();
|
|
68
|
+
const lang = (info.split(/\s+/)[0] || '').toLowerCase();
|
|
69
|
+
const map = Array.isArray(t.map) ? t.map : [0, 0];
|
|
70
|
+
fences.push({
|
|
71
|
+
lang,
|
|
72
|
+
info,
|
|
73
|
+
content: String(t.content || ''),
|
|
74
|
+
lineStart: map[0] + 1,
|
|
75
|
+
lineEnd: map[1],
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
if (t.type === 'inline' && Array.isArray(t.children)) {
|
|
79
|
+
for (const c of t.children) {
|
|
80
|
+
if (c.type === 'link_open') {
|
|
81
|
+
const href = c.attrGet('href') || '';
|
|
82
|
+
links.push({ href, text: '' });
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
const html = md.renderer.render(tokens, md.options, {});
|
|
89
|
+
return { html, headings, links, fences };
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/** @param {string} text */
|
|
93
|
+
export function slugify(text) {
|
|
94
|
+
return (
|
|
95
|
+
String(text || '')
|
|
96
|
+
.trim()
|
|
97
|
+
.toLowerCase()
|
|
98
|
+
.replace(/[^\p{L}\p{N}\s_-]/gu, '')
|
|
99
|
+
.replace(/\s+/g, '-')
|
|
100
|
+
.replace(/-+/g, '-')
|
|
101
|
+
.replace(/^-|-$/g, '') || 'section'
|
|
102
|
+
);
|
|
103
|
+
}
|
package/vmz.plugin.ts
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { definePlugin, loadPluginSource } from '@vmz/plugin';
|
|
2
|
+
|
|
3
|
+
const source = loadPluginSource(import.meta.url, 'components/MarkdownIt.vmz');
|
|
4
|
+
|
|
5
|
+
export default definePlugin({
|
|
6
|
+
name: '@vmz/plugin-markdown-it',
|
|
7
|
+
version: '0.1.0',
|
|
8
|
+
protocol: '0.1.0',
|
|
9
|
+
stages: ['workspace_resolve', 'analyzer'],
|
|
10
|
+
deterministic: true,
|
|
11
|
+
async contribute(ctx) {
|
|
12
|
+
if (ctx.stage === 'workspace_resolve') {
|
|
13
|
+
return {
|
|
14
|
+
stage: 'workspace_resolve',
|
|
15
|
+
cacheKey: `@vmz/plugin-markdown-it:MarkdownIt.vmz:${source.contentHash.slice(0, 12)}`,
|
|
16
|
+
items: [
|
|
17
|
+
{
|
|
18
|
+
id: 'component-markdown-it',
|
|
19
|
+
kind: 'source',
|
|
20
|
+
path: 'src/components/MarkdownIt.vmz',
|
|
21
|
+
content: source.content,
|
|
22
|
+
contentHash: source.contentHash,
|
|
23
|
+
materialize: true,
|
|
24
|
+
engine: 'markdown-it',
|
|
25
|
+
engineKind: 'markdown',
|
|
26
|
+
},
|
|
27
|
+
],
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
if (ctx.stage === 'analyzer') {
|
|
31
|
+
return {
|
|
32
|
+
stage: 'analyzer',
|
|
33
|
+
cacheKey: '@vmz/plugin-markdown-it:analyzer',
|
|
34
|
+
items: [
|
|
35
|
+
{
|
|
36
|
+
id: 'engine-markdown-it',
|
|
37
|
+
kind: 'analyzer',
|
|
38
|
+
path: 'src/components/MarkdownIt.vmz',
|
|
39
|
+
severity: 'advice',
|
|
40
|
+
message: 'markdown engine markdown-it online',
|
|
41
|
+
code: 'vmz.engine.markdown-it',
|
|
42
|
+
engine: 'markdown-it',
|
|
43
|
+
engineKind: 'markdown',
|
|
44
|
+
},
|
|
45
|
+
],
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
return { stage: ctx.stage, items: [] };
|
|
49
|
+
},
|
|
50
|
+
});
|