@lwrjs/markdown-view-provider 0.7.2 → 0.7.5
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.
|
@@ -26,42 +26,128 @@ __markAsModule(exports);
|
|
|
26
26
|
__export(exports, {
|
|
27
27
|
getHtmlHighlighter: () => getHtmlHighlighter
|
|
28
28
|
});
|
|
29
|
-
var import_unified = __toModule(require("unified"));
|
|
30
|
-
var import_unist_util_modify_children = __toModule(require("unist-util-modify-children"));
|
|
31
29
|
var import_hast_util_to_string = __toModule(require("hast-util-to-string"));
|
|
30
|
+
var import_hast_util_is_element = __toModule(require("hast-util-is-element"));
|
|
31
|
+
var import_unist_util_visit = __toModule(require("unist-util-visit"));
|
|
32
32
|
var Shiki = __toModule(require("shiki"));
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
33
|
+
async function getHtmlHighlighter({theme}) {
|
|
34
|
+
const highlighter = await Shiki.getHighlighter({
|
|
35
|
+
theme,
|
|
36
|
+
langs: ["javascript"]
|
|
37
|
+
});
|
|
38
|
+
const languageRegistry = {
|
|
39
|
+
loaded: new Set(highlighter.getLoadedLanguages()),
|
|
40
|
+
unknown: new Set()
|
|
41
|
+
};
|
|
42
|
+
async function loadMissingLanguages(langs) {
|
|
43
|
+
const promises = [];
|
|
44
|
+
for (const lang of langs) {
|
|
45
|
+
if (!languageRegistry.loaded.has(lang) && !languageRegistry.unknown.has(lang)) {
|
|
46
|
+
promises.push(highlighter.loadLanguage(lang).then(() => languageRegistry.loaded.add(lang), () => languageRegistry.unknown.add(lang)));
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
await Promise.all(promises);
|
|
50
|
+
}
|
|
46
51
|
return function htmlHighlighter() {
|
|
47
|
-
return function(tree) {
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
if (error.message != "No language registration for term")
|
|
57
|
-
throw error;
|
|
58
|
-
else
|
|
59
|
-
return;
|
|
60
|
-
}
|
|
61
|
-
const highlightedElm = hastParser.parse(highlightedCodeHTML);
|
|
62
|
-
parent.children[index] = highlightedElm;
|
|
52
|
+
return async function(tree) {
|
|
53
|
+
const highlightCandidates = [];
|
|
54
|
+
(0, import_unist_util_visit.default)(tree, "element", (node, index, parent) => {
|
|
55
|
+
if (!(0, import_hast_util_is_element.default)(node) || node.tagName !== "code" || !(0, import_hast_util_is_element.default)(parent) || parent.tagName !== "pre") {
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
|
+
const lang = getLanguage(node);
|
|
59
|
+
if (lang) {
|
|
60
|
+
highlightCandidates.push({parent, node, lang});
|
|
63
61
|
}
|
|
64
|
-
})
|
|
62
|
+
});
|
|
63
|
+
const langs = new Set(highlightCandidates.map(({lang}) => lang));
|
|
64
|
+
await loadMissingLanguages(langs);
|
|
65
|
+
for (const {parent, node, lang} of highlightCandidates) {
|
|
66
|
+
if (languageRegistry.loaded.has(lang)) {
|
|
67
|
+
const tokens = highlighter.codeToThemedTokens((0, import_hast_util_to_string.default)(node), lang, theme);
|
|
68
|
+
addClassToHastElement(parent, "shiki");
|
|
69
|
+
const backgroundColor = highlighter.getBackgroundColor(theme);
|
|
70
|
+
addStyleToHastElement(parent, `background-color: ${backgroundColor}`);
|
|
71
|
+
node.children = tokensToHast(tokens);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
65
74
|
};
|
|
66
75
|
};
|
|
67
76
|
}
|
|
77
|
+
function getLanguage(node) {
|
|
78
|
+
const className = node.properties?.className;
|
|
79
|
+
if (className) {
|
|
80
|
+
for (const part of className) {
|
|
81
|
+
const match = part.match(/language-(\w+)/);
|
|
82
|
+
if (match) {
|
|
83
|
+
return match[1];
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
return void 0;
|
|
88
|
+
}
|
|
89
|
+
function addClassToHastElement(elm, klass) {
|
|
90
|
+
const properties = elm.properties || {};
|
|
91
|
+
const className = properties.className || [];
|
|
92
|
+
className.push(klass);
|
|
93
|
+
properties.className = className;
|
|
94
|
+
elm.properties = properties;
|
|
95
|
+
}
|
|
96
|
+
function addStyleToHastElement(elm, style) {
|
|
97
|
+
const properties = elm.properties || {};
|
|
98
|
+
const styles = properties.style || [];
|
|
99
|
+
styles.push(style);
|
|
100
|
+
properties.style = styles;
|
|
101
|
+
elm.properties = properties;
|
|
102
|
+
}
|
|
103
|
+
function tokensToHast(lines) {
|
|
104
|
+
const tree = [];
|
|
105
|
+
for (const line of lines) {
|
|
106
|
+
if (line.length === 0) {
|
|
107
|
+
tree.push({
|
|
108
|
+
type: "text",
|
|
109
|
+
value: "\n"
|
|
110
|
+
});
|
|
111
|
+
} else {
|
|
112
|
+
for (const token of line) {
|
|
113
|
+
tree.push({
|
|
114
|
+
type: "element",
|
|
115
|
+
tagName: "span",
|
|
116
|
+
properties: {
|
|
117
|
+
style: tokenToStyle(token)
|
|
118
|
+
},
|
|
119
|
+
children: [
|
|
120
|
+
{
|
|
121
|
+
type: "text",
|
|
122
|
+
value: token.content
|
|
123
|
+
}
|
|
124
|
+
]
|
|
125
|
+
});
|
|
126
|
+
}
|
|
127
|
+
tree.push({
|
|
128
|
+
type: "text",
|
|
129
|
+
value: "\n"
|
|
130
|
+
});
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
tree.pop();
|
|
134
|
+
return tree;
|
|
135
|
+
}
|
|
136
|
+
function tokenToStyle(token) {
|
|
137
|
+
const styles = [];
|
|
138
|
+
if (token.color) {
|
|
139
|
+
styles.push(`color: ${token.color}`);
|
|
140
|
+
}
|
|
141
|
+
if (token.fontStyle) {
|
|
142
|
+
if (token.fontStyle & Shiki.FontStyle.Bold) {
|
|
143
|
+
styles.push("font-weight: bold");
|
|
144
|
+
}
|
|
145
|
+
if (token.fontStyle & Shiki.FontStyle.Italic) {
|
|
146
|
+
styles.push("font-style: italic");
|
|
147
|
+
}
|
|
148
|
+
if (token.fontStyle & Shiki.FontStyle.Underline) {
|
|
149
|
+
styles.push("text-decoration: underline");
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
return styles;
|
|
153
|
+
}
|
|
@@ -7,6 +7,6 @@ interface HtmlHighlighterOptions {
|
|
|
7
7
|
* @param options Allow the configuration of the HTML Highlighter (e.g. the `theme` used).
|
|
8
8
|
* @returns An unified Plugin with the HTML Transformer.
|
|
9
9
|
*/
|
|
10
|
-
export declare function getHtmlHighlighter(
|
|
10
|
+
export declare function getHtmlHighlighter({ theme }: HtmlHighlighterOptions): Promise<unified.Plugin>;
|
|
11
11
|
export {};
|
|
12
12
|
//# sourceMappingURL=highlighter.d.ts.map
|
|
@@ -1,54 +1,145 @@
|
|
|
1
|
-
import unified from 'unified';
|
|
2
|
-
import unistUtilModifyChildren from 'unist-util-modify-children';
|
|
3
1
|
import hastUtilToString from 'hast-util-to-string';
|
|
2
|
+
import isElement from 'hast-util-is-element';
|
|
3
|
+
import visit from 'unist-util-visit';
|
|
4
4
|
import * as Shiki from 'shiki';
|
|
5
|
-
import rehypeParse from 'rehype-parse';
|
|
6
|
-
const hastParser = unified().use(rehypeParse, { fragment: true });
|
|
7
|
-
function isPreNode(node) {
|
|
8
|
-
return node.tagName === 'pre';
|
|
9
|
-
}
|
|
10
|
-
function isParentNode(node) {
|
|
11
|
-
return Array.isArray(node.children);
|
|
12
|
-
}
|
|
13
|
-
function isCodeNode(node) {
|
|
14
|
-
return node.tagName === 'code';
|
|
15
|
-
}
|
|
16
5
|
/**
|
|
17
6
|
* Generates an configured unified.Plugin for highlighting `code` snippets.
|
|
18
7
|
* @param options Allow the configuration of the HTML Highlighter (e.g. the `theme` used).
|
|
19
8
|
* @returns An unified Plugin with the HTML Transformer.
|
|
20
9
|
*/
|
|
21
|
-
export async function getHtmlHighlighter(
|
|
22
|
-
|
|
10
|
+
export async function getHtmlHighlighter({ theme }) {
|
|
11
|
+
// By default the Shiki highlighter loads all the languages upfront, which can take up to 500 ms.
|
|
12
|
+
//
|
|
13
|
+
// It's currently impossible to create a `Highlighter` without preloading only language
|
|
14
|
+
// (https://github.com/shikijs/shiki/issues/326). In the mean time, we forcing the highlighter
|
|
15
|
+
// to only load JavaScript.
|
|
16
|
+
const highlighter = await Shiki.getHighlighter({
|
|
17
|
+
theme,
|
|
18
|
+
langs: ['javascript'],
|
|
19
|
+
});
|
|
20
|
+
const languageRegistry = {
|
|
21
|
+
loaded: new Set(highlighter.getLoadedLanguages()),
|
|
22
|
+
unknown: new Set(),
|
|
23
|
+
};
|
|
24
|
+
async function loadMissingLanguages(langs) {
|
|
25
|
+
const promises = [];
|
|
26
|
+
for (const lang of langs) {
|
|
27
|
+
if (!languageRegistry.loaded.has(lang) && !languageRegistry.unknown.has(lang)) {
|
|
28
|
+
promises.push(highlighter.loadLanguage(lang).then(() => languageRegistry.loaded.add(lang), () => languageRegistry.unknown.add(lang)));
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
await Promise.all(promises);
|
|
32
|
+
}
|
|
23
33
|
return function htmlHighlighter() {
|
|
24
|
-
return function (tree) {
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
const
|
|
48
|
-
parent
|
|
34
|
+
return async function (tree) {
|
|
35
|
+
const highlightCandidates = [];
|
|
36
|
+
visit(tree, 'element', (node, index, parent) => {
|
|
37
|
+
// Ignore non-code elements: pre > code.
|
|
38
|
+
if (!isElement(node) ||
|
|
39
|
+
node.tagName !== 'code' ||
|
|
40
|
+
!isElement(parent) ||
|
|
41
|
+
parent.tagName !== 'pre') {
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
44
|
+
// Ignore node if no language is specified.
|
|
45
|
+
const lang = getLanguage(node);
|
|
46
|
+
if (lang) {
|
|
47
|
+
highlightCandidates.push({ parent, node, lang });
|
|
48
|
+
}
|
|
49
|
+
});
|
|
50
|
+
const langs = new Set(highlightCandidates.map(({ lang }) => lang));
|
|
51
|
+
await loadMissingLanguages(langs);
|
|
52
|
+
for (const { parent, node, lang } of highlightCandidates) {
|
|
53
|
+
if (languageRegistry.loaded.has(lang)) {
|
|
54
|
+
const tokens = highlighter.codeToThemedTokens(hastUtilToString(node), lang, theme);
|
|
55
|
+
// Add background color and class name to the <pre> element.
|
|
56
|
+
addClassToHastElement(parent, 'shiki');
|
|
57
|
+
const backgroundColor = highlighter.getBackgroundColor(theme);
|
|
58
|
+
addStyleToHastElement(parent, `background-color: ${backgroundColor}`);
|
|
59
|
+
// Replace the <code> element with highlighted nodes.
|
|
60
|
+
node.children = tokensToHast(tokens);
|
|
49
61
|
}
|
|
50
|
-
}
|
|
62
|
+
}
|
|
51
63
|
};
|
|
52
64
|
};
|
|
53
65
|
}
|
|
66
|
+
function getLanguage(node) {
|
|
67
|
+
const className = node.properties?.className;
|
|
68
|
+
if (className) {
|
|
69
|
+
for (const part of className) {
|
|
70
|
+
const match = part.match(/language-(\w+)/);
|
|
71
|
+
if (match) {
|
|
72
|
+
return match[1];
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
return undefined;
|
|
77
|
+
}
|
|
78
|
+
function addClassToHastElement(elm, klass) {
|
|
79
|
+
const properties = elm.properties || {};
|
|
80
|
+
const className = properties.className || [];
|
|
81
|
+
className.push(klass);
|
|
82
|
+
properties.className = className;
|
|
83
|
+
elm.properties = properties;
|
|
84
|
+
}
|
|
85
|
+
function addStyleToHastElement(elm, style) {
|
|
86
|
+
const properties = elm.properties || {};
|
|
87
|
+
const styles = properties.style || [];
|
|
88
|
+
styles.push(style);
|
|
89
|
+
properties.style = styles;
|
|
90
|
+
elm.properties = properties;
|
|
91
|
+
}
|
|
92
|
+
function tokensToHast(lines) {
|
|
93
|
+
const tree = [];
|
|
94
|
+
for (const line of lines) {
|
|
95
|
+
if (line.length === 0) {
|
|
96
|
+
tree.push({
|
|
97
|
+
type: 'text',
|
|
98
|
+
value: '\n',
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
else {
|
|
102
|
+
for (const token of line) {
|
|
103
|
+
tree.push({
|
|
104
|
+
type: 'element',
|
|
105
|
+
tagName: 'span',
|
|
106
|
+
properties: {
|
|
107
|
+
style: tokenToStyle(token),
|
|
108
|
+
},
|
|
109
|
+
children: [
|
|
110
|
+
{
|
|
111
|
+
type: 'text',
|
|
112
|
+
value: token.content,
|
|
113
|
+
},
|
|
114
|
+
],
|
|
115
|
+
});
|
|
116
|
+
}
|
|
117
|
+
tree.push({
|
|
118
|
+
type: 'text',
|
|
119
|
+
value: '\n',
|
|
120
|
+
});
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
// Remove trailing newline at the end of the code block.
|
|
124
|
+
tree.pop();
|
|
125
|
+
return tree;
|
|
126
|
+
}
|
|
127
|
+
function tokenToStyle(token) {
|
|
128
|
+
const styles = [];
|
|
129
|
+
if (token.color) {
|
|
130
|
+
styles.push(`color: ${token.color}`);
|
|
131
|
+
}
|
|
132
|
+
if (token.fontStyle) {
|
|
133
|
+
if (token.fontStyle & Shiki.FontStyle.Bold) {
|
|
134
|
+
styles.push('font-weight: bold');
|
|
135
|
+
}
|
|
136
|
+
if (token.fontStyle & Shiki.FontStyle.Italic) {
|
|
137
|
+
styles.push('font-style: italic');
|
|
138
|
+
}
|
|
139
|
+
if (token.fontStyle & Shiki.FontStyle.Underline) {
|
|
140
|
+
styles.push('text-decoration: underline');
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
return styles;
|
|
144
|
+
}
|
|
54
145
|
//# sourceMappingURL=highlighter.js.map
|
package/package.json
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
"publishConfig": {
|
|
5
5
|
"access": "public"
|
|
6
6
|
},
|
|
7
|
-
"version": "0.7.
|
|
7
|
+
"version": "0.7.5",
|
|
8
8
|
"homepage": "https://developer.salesforce.com/docs/platform/lwr/overview",
|
|
9
9
|
"repository": {
|
|
10
10
|
"type": "git",
|
|
@@ -30,29 +30,27 @@
|
|
|
30
30
|
"build/**/*.d.ts"
|
|
31
31
|
],
|
|
32
32
|
"dependencies": {
|
|
33
|
-
"@lwrjs/base-template-engine": "0.7.
|
|
34
|
-
"@lwrjs/shared-utils": "0.7.
|
|
33
|
+
"@lwrjs/base-template-engine": "0.7.5",
|
|
34
|
+
"@lwrjs/shared-utils": "0.7.5",
|
|
35
35
|
"gray-matter": "^4.0.2",
|
|
36
36
|
"hast-util-has-property": "^1.0.4",
|
|
37
37
|
"hast-util-heading-rank": "^1.0.1",
|
|
38
|
+
"hast-util-is-element": "^1.1.0",
|
|
38
39
|
"hast-util-to-string": "^1.0.4",
|
|
39
|
-
"prismjs": "^1.21.0",
|
|
40
|
-
"rehype-parse": "^7.0.1",
|
|
41
40
|
"rehype-raw": "^5.0.0",
|
|
42
41
|
"rehype-stringify": "^8.0.0",
|
|
43
42
|
"remark-gfm": "^1.0.0",
|
|
44
43
|
"remark-parse": "^9.0.0",
|
|
45
44
|
"remark-rehype": "^8.0.0",
|
|
46
|
-
"shiki": "^0.9.
|
|
45
|
+
"shiki": "^0.9.15",
|
|
47
46
|
"unified": "^9.2.0",
|
|
48
|
-
"unist-util-modify-children": "^2.0.0",
|
|
49
47
|
"vfile": "^4.2.1"
|
|
50
48
|
},
|
|
51
49
|
"devDependencies": {
|
|
52
|
-
"@lwrjs/types": "0.7.
|
|
50
|
+
"@lwrjs/types": "0.7.5"
|
|
53
51
|
},
|
|
54
52
|
"engines": {
|
|
55
53
|
"node": ">=14.15.4 <19"
|
|
56
54
|
},
|
|
57
|
-
"gitHead": "
|
|
55
|
+
"gitHead": "a30a1ca0d171c56914d65dd8747bfdfbd3529efa"
|
|
58
56
|
}
|