@nakobase/nakobase-md-html 1.1.0 → 1.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/esm/sanitizer.d.ts.map +1 -1
- package/dist/esm/sanitizer.js +3 -7
- package/dist/esm/utils/md-container.d.ts.map +1 -1
- package/dist/esm/utils/md-container.js +24 -8
- package/dist/esm/utils/md-custom-blocks.d.ts.map +1 -1
- package/dist/esm/utils/md-custom-blocks.js +69 -21
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sanitizer.d.ts","sourceRoot":"","sources":["../../src/sanitizer.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"sanitizer.d.ts","sourceRoot":"","sources":["../../src/sanitizer.ts"],"names":[],"mappings":"AAgCA,eAAO,MAAM,QAAQ,GAAI,MAAM,MAAM,KAAG,MAuBpC,CAAC"}
|
package/dist/esm/sanitizer.js
CHANGED
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
// sanitizer.ts
|
|
2
2
|
import sanitizeHtml, { defaults } from 'sanitize-html';
|
|
3
|
-
const extendedTags = ['iframe', 'code', 'details', 'summary', 'circle', 'img', 'input', 'pre', 'span'];
|
|
3
|
+
const extendedTags = ['iframe', 'code', 'details', 'summary', 'circle', 'img', 'input', 'pre', 'span', 'picture', 'source'];
|
|
4
4
|
const extendedAttributes = {
|
|
5
5
|
a: [...(defaults.allowedAttributes.a || []), 'id', 'class', 'data-line'],
|
|
6
6
|
iframe: ['src', 'width', 'height', 'allow', 'sandbox', 'frameborder'],
|
|
7
7
|
input: ['type', 'checked', 'disabled', 'readonly', 'value', 'class'],
|
|
8
|
+
source: ['srcset', 'type'],
|
|
9
|
+
img: ['src', 'alt', 'width', 'height'],
|
|
8
10
|
ul: ['class'],
|
|
9
11
|
ol: ['class'],
|
|
10
12
|
li: ['class'],
|
|
@@ -31,11 +33,5 @@ export const sanitize = html => sanitizeHtml(html, {
|
|
|
31
33
|
'text-align': [/^(?:left|right|center|justify)$/]
|
|
32
34
|
}
|
|
33
35
|
},
|
|
34
|
-
transformTags: {
|
|
35
|
-
a: sanitizeHtml.simpleTransform('a', {
|
|
36
|
-
target: '_blank',
|
|
37
|
-
rel: 'noopener noreferrer'
|
|
38
|
-
})
|
|
39
|
-
},
|
|
40
36
|
disallowedTagsMode: 'discard'
|
|
41
37
|
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"md-container.d.ts","sourceRoot":"","sources":["../../../src/utils/md-container.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,KAAK,MAAM,uBAAuB,CAAC;AAK/C,eAAO,MAAM,cAAc;uBACG,MAAM;qBAGR,KAAK,EAAE,OAAO,MAAM;CAc/C,CAAC;AAMF,eAAO,MAAM,aAAa;uBACI,MAAM;qBAGR,KAAK,EAAE,OAAO,MAAM;
|
|
1
|
+
{"version":3,"file":"md-container.d.ts","sourceRoot":"","sources":["../../../src/utils/md-container.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,KAAK,MAAM,uBAAuB,CAAC;AAK/C,eAAO,MAAM,cAAc;uBACG,MAAM;qBAGR,KAAK,EAAE,OAAO,MAAM;CAc/C,CAAC;AAMF,eAAO,MAAM,aAAa;uBACI,MAAM;qBAGR,KAAK,EAAE,OAAO,MAAM;CA2C/C,CAAC;AAMF,eAAO,MAAM,kBAAkB;uBACD,MAAM;qBAGR,KAAK,EAAE,OAAO,MAAM;CA4C/C,CAAC;AAOF,eAAO,MAAM,UAAU;uBACO,MAAM;qBAGR,KAAK,EAAE,OAAO,MAAM;CAc/C,CAAC"}
|
|
@@ -19,7 +19,7 @@ export const detailsOptions = {
|
|
|
19
19
|
};
|
|
20
20
|
|
|
21
21
|
// Bubble
|
|
22
|
-
// ::: bubble alt="alt" src="src" width="100" height="100" pos="left"
|
|
22
|
+
// ::: bubble alt="alt" src="src" webp="src.webp" width="100" height="100" pos="left"
|
|
23
23
|
// markdown
|
|
24
24
|
// :::
|
|
25
25
|
export const bubbleOptions = {
|
|
@@ -41,9 +41,17 @@ export const bubbleOptions = {
|
|
|
41
41
|
alt = '',
|
|
42
42
|
width = '100',
|
|
43
43
|
height = '100',
|
|
44
|
-
pos = 'left'
|
|
44
|
+
pos = 'left',
|
|
45
|
+
webp = ''
|
|
45
46
|
} = attrs;
|
|
46
|
-
|
|
47
|
+
const imgHtml = `<img src="${escapeHtml(src)}" alt="${escapeHtml(alt)}" ` + `width="${escapeHtml(width)}" height="${escapeHtml(height)}">`;
|
|
48
|
+
let imageElement;
|
|
49
|
+
if (webp) {
|
|
50
|
+
imageElement = '<picture>' + `<source srcset="${escapeHtml(webp)}" type="image/webp">` + imgHtml + '</picture>';
|
|
51
|
+
} else {
|
|
52
|
+
imageElement = imgHtml;
|
|
53
|
+
}
|
|
54
|
+
return `<div class="bubble ${pos}">` + imageElement + `<div class="bubble-content">`;
|
|
47
55
|
} else {
|
|
48
56
|
return `</div></div>\n`;
|
|
49
57
|
}
|
|
@@ -51,17 +59,17 @@ export const bubbleOptions = {
|
|
|
51
59
|
};
|
|
52
60
|
|
|
53
61
|
// BubbleImage
|
|
54
|
-
// :::
|
|
62
|
+
// ::: bubble-image alt="alt" src="src" webp="src.webp" width="640" height="360"
|
|
55
63
|
// markdown
|
|
56
64
|
// :::
|
|
57
65
|
export const bubbleImageOptions = {
|
|
58
66
|
validate: function (params) {
|
|
59
|
-
return /^
|
|
67
|
+
return /^bubble-image(?:\s+\w+="[^"]*")*\s*$/.test(params.trim());
|
|
60
68
|
},
|
|
61
69
|
render: function (tokens, idx) {
|
|
62
70
|
const isOpeningTag = tokens[idx].nesting === 1;
|
|
63
71
|
if (isOpeningTag) {
|
|
64
|
-
const info = tokens[idx].info.trim().replace(/^
|
|
72
|
+
const info = tokens[idx].info.trim().replace(/^bubble-image\s*/, '');
|
|
65
73
|
const regex = /(\w+)="([^"]*)"/g;
|
|
66
74
|
const attrs = {};
|
|
67
75
|
let m;
|
|
@@ -72,9 +80,17 @@ export const bubbleImageOptions = {
|
|
|
72
80
|
src = '',
|
|
73
81
|
alt = '',
|
|
74
82
|
width = '640',
|
|
75
|
-
height = '360'
|
|
83
|
+
height = '360',
|
|
84
|
+
webp = ''
|
|
76
85
|
} = attrs;
|
|
77
|
-
|
|
86
|
+
const imgHtml = `<img src="${escapeHtml(src)}" alt="${escapeHtml(alt)}" ` + `width="${escapeHtml(width)}" height="${escapeHtml(height)}">`;
|
|
87
|
+
let imageElement;
|
|
88
|
+
if (webp) {
|
|
89
|
+
imageElement = '<picture>' + `<source srcset="${escapeHtml(webp)}" type="image/webp">` + imgHtml + '</picture>';
|
|
90
|
+
} else {
|
|
91
|
+
imageElement = imgHtml;
|
|
92
|
+
}
|
|
93
|
+
return `<div class="bubble-image">` + `<div class="bubble-image-wrapper">` + imageElement + `</div>` + `<div class="bubble-image-content">`;
|
|
78
94
|
} else {
|
|
79
95
|
return `</div></div>\n`;
|
|
80
96
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"md-custom-blocks.d.ts","sourceRoot":"","sources":["../../../src/utils/md-custom-blocks.ts"],"names":[],"mappings":"AAAA,OAAO,UAAU,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"md-custom-blocks.d.ts","sourceRoot":"","sources":["../../../src/utils/md-custom-blocks.ts"],"names":[],"mappings":"AAAA,OAAO,UAAU,MAAM,aAAa,CAAC;AAsDrC,wBAAgB,cAAc,CAAC,EAAE,EAAE,UAAU,QAkC5C"}
|
|
@@ -1,27 +1,75 @@
|
|
|
1
1
|
import { escapeHtml } from 'markdown-it/lib/common/utils';
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
if (!match) return false;
|
|
2
|
+
function escapeRegExp(string) {
|
|
3
|
+
return string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
4
|
+
}
|
|
5
|
+
function registerKVBlock(md, block) {
|
|
6
|
+
const escapedMarker = escapeRegExp(block.marker);
|
|
7
|
+
const blockRe = new RegExp(`^@\\[${escapedMarker}\\]\\(([^)]*)\\)$`);
|
|
8
|
+
const attrRe = /([a-zA-Z0-9-_]+)="([^"]*)"/g;
|
|
9
|
+
md.block.ruler.before('paragraph', block.name, (state, start, _end, silent) => {
|
|
10
|
+
const line = state.src.slice(state.bMarks[start] + state.tShift[start], state.eMarks[start]).trim();
|
|
11
|
+
const m = line.match(blockRe);
|
|
12
|
+
if (!m) return false;
|
|
14
13
|
if (silent) return true;
|
|
15
|
-
const
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
14
|
+
const attrs = {};
|
|
15
|
+
let mat;
|
|
16
|
+
while (mat = attrRe.exec(m[1])) {
|
|
17
|
+
attrs[mat[1]] = mat[2];
|
|
18
|
+
}
|
|
19
|
+
for (const key of block.requiredKeys) {
|
|
20
|
+
if (!attrs[key]) {
|
|
21
|
+
return false;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
const token = state.push(block.name, '', 0);
|
|
25
|
+
token.meta = attrs;
|
|
26
|
+
state.line = start + 1;
|
|
21
27
|
return true;
|
|
22
28
|
});
|
|
23
|
-
md.renderer.rules.
|
|
24
|
-
|
|
25
|
-
return `<a href="${escapeHtml(url)}" target="_blank" rel="noopener noreferrer" class="external-link">${escapeHtml(text)}</a>`;
|
|
29
|
+
md.renderer.rules[block.name] = (tokens, idx) => {
|
|
30
|
+
return block.renderer(tokens[idx].meta);
|
|
26
31
|
};
|
|
32
|
+
}
|
|
33
|
+
export function mdCustomBlocks(md) {
|
|
34
|
+
// @[img](src="", alt="", webp="", width="", height="")
|
|
35
|
+
registerKVBlock(md, {
|
|
36
|
+
name: 'image_with_webp',
|
|
37
|
+
marker: 'img',
|
|
38
|
+
requiredKeys: ['src'],
|
|
39
|
+
renderer: ({
|
|
40
|
+
src,
|
|
41
|
+
alt,
|
|
42
|
+
webp,
|
|
43
|
+
width,
|
|
44
|
+
height,
|
|
45
|
+
caption
|
|
46
|
+
}) => {
|
|
47
|
+
const webpSource = webp ? `<source srcset="${escapeHtml(webp)}" type="image/webp">` : '';
|
|
48
|
+
const altText = alt ? `alt="${escapeHtml(alt)}"` : '';
|
|
49
|
+
const widthAttr = width ? ` width="${escapeHtml(width)}"` : '';
|
|
50
|
+
const heightAttr = height ? ` height="${escapeHtml(height)}"` : '';
|
|
51
|
+
const captionText = caption ? `<em>${escapeHtml(caption)}</em>` : '';
|
|
52
|
+
return `<picture>
|
|
53
|
+
${webpSource}
|
|
54
|
+
<img src="${escapeHtml(src)}" ${altText}${widthAttr}${heightAttr}>
|
|
55
|
+
${captionText}
|
|
56
|
+
</picture>`;
|
|
57
|
+
}
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
// @[link-external](url="", text="")
|
|
61
|
+
registerKVBlock(md, {
|
|
62
|
+
name: 'link_external',
|
|
63
|
+
marker: 'link-external',
|
|
64
|
+
requiredKeys: ['url'],
|
|
65
|
+
renderer: ({
|
|
66
|
+
url,
|
|
67
|
+
text
|
|
68
|
+
}) => {
|
|
69
|
+
const label = text || url;
|
|
70
|
+
return `<a href="${escapeHtml(url)}" target="_blank" rel="noopener noreferrer" class="external-link">
|
|
71
|
+
${escapeHtml(label)}
|
|
72
|
+
</a>`;
|
|
73
|
+
}
|
|
74
|
+
});
|
|
27
75
|
}
|
package/package.json
CHANGED