@salesforcedevs/dx-components 1.30.1-node22-1 → 1.30.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/lwc.config.json +4 -1
- package/package.json +3 -7
- package/src/modules/dxUtils/shiki/__mocks__/shiki.ts +2 -1
- package/src/modules/dxUtils/shiki/__mocks__/shikijs.ts +6 -0
- package/src/modules/dxUtils/shiki/shiki.ts +26 -176
- package/src/modules/dxUtils/shikiCore/shikiCore.ts +188 -0
- package/src/modules/dxUtils/shikiStatic/shikiStatic.html +3 -0
- package/src/modules/dxUtils/shikiStatic/shikiStatic.ts +46 -0
package/lwc.config.json
CHANGED
package/package.json
CHANGED
|
@@ -1,14 +1,10 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@salesforcedevs/dx-components",
|
|
3
|
-
"version": "1.30.1
|
|
3
|
+
"version": "1.30.1",
|
|
4
4
|
"description": "DX Lightning web components",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"engines": {
|
|
7
|
-
"node": "
|
|
8
|
-
},
|
|
9
|
-
"volta": {
|
|
10
|
-
"node": "22.22.0",
|
|
11
|
-
"yarn": "1.22.22"
|
|
7
|
+
"node": "20.x"
|
|
12
8
|
},
|
|
13
9
|
"publishConfig": {
|
|
14
10
|
"access": "public"
|
|
@@ -48,5 +44,5 @@
|
|
|
48
44
|
"luxon": "3.4.4",
|
|
49
45
|
"msw": "^2.12.4"
|
|
50
46
|
},
|
|
51
|
-
"gitHead": "
|
|
47
|
+
"gitHead": "7ea089ea32ead00fcc2c873a279464b17aee3428"
|
|
52
48
|
}
|
|
@@ -21,3 +21,9 @@ export default mockLanguage;
|
|
|
21
21
|
// Also export as named export for themes
|
|
22
22
|
export const theme = mockTheme;
|
|
23
23
|
export const lang = mockLanguage;
|
|
24
|
+
|
|
25
|
+
// Mock for @shikijs/colorized-brackets
|
|
26
|
+
export const transformerColorizedBrackets = () => ({
|
|
27
|
+
name: "colorized-brackets",
|
|
28
|
+
preprocess: (code: string) => code
|
|
29
|
+
});
|
|
@@ -1,5 +1,17 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
import {
|
|
1
|
+
import type { HighlighterCore } from "shiki";
|
|
2
|
+
import {
|
|
3
|
+
createShikiState,
|
|
4
|
+
initializeHighlighter,
|
|
5
|
+
highlightCodeWithShiki,
|
|
6
|
+
escapeHtml as coreEscapeHtml,
|
|
7
|
+
ShikiState
|
|
8
|
+
} from "dxUtils/shikiCore";
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Dynamic loading version of Shiki.
|
|
12
|
+
* Uses dynamic import() for code splitting - shiki is lazy loaded on first use.
|
|
13
|
+
* Note: May not work with SSG. Use dxUtils/shikiStatic for SSG compatibility.
|
|
14
|
+
*/
|
|
3
15
|
|
|
4
16
|
let shikiModulePromise: Promise<typeof import("shiki")> | null = null;
|
|
5
17
|
let bracketsModulePromise: Promise<
|
|
@@ -14,156 +26,13 @@ async function getBrackets() {
|
|
|
14
26
|
return (bracketsModulePromise ??= import("@shikijs/colorized-brackets"));
|
|
15
27
|
}
|
|
16
28
|
|
|
17
|
-
|
|
18
|
-
highlighter: HighlighterCore | null;
|
|
19
|
-
initialized: boolean;
|
|
20
|
-
initPromise: Promise<HighlighterCore> | null;
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
const shikiInstance: ShikiSingleton = {
|
|
24
|
-
highlighter: null,
|
|
25
|
-
initialized: false,
|
|
26
|
-
initPromise: null
|
|
27
|
-
};
|
|
28
|
-
|
|
29
|
-
// Theme mapping for light/dark modes
|
|
30
|
-
const THEME_MAP: Record<string, BundledTheme> = {
|
|
31
|
-
light: "light-plus",
|
|
32
|
-
dark: "material-theme-darker"
|
|
33
|
-
};
|
|
34
|
-
|
|
35
|
-
const THEME_MAP_COLOR_REPLACEMENTS: Record<
|
|
36
|
-
string,
|
|
37
|
-
{ colorReplacements: Record<string, string> }
|
|
38
|
-
> = {
|
|
39
|
-
light: {
|
|
40
|
-
colorReplacements: {}
|
|
41
|
-
},
|
|
42
|
-
dark: {
|
|
43
|
-
colorReplacements: {
|
|
44
|
-
"#545454": "#8A8A8A" // Replace comment color for WCAG 2.1 compliance
|
|
45
|
-
}
|
|
46
|
-
}
|
|
47
|
-
};
|
|
48
|
-
|
|
49
|
-
// Language mapping for custom languages to supported ones
|
|
50
|
-
const LANGUAGE_MAP: Record<string, BundledLanguage> = {
|
|
51
|
-
ssjs: "javascript", // Server-Side JavaScript
|
|
52
|
-
gtl: "handlebars", // Guide Template Language similar to Handlebars
|
|
53
|
-
lwc: "jsx",
|
|
54
|
-
sql_docs_template: "sql", // Custom SQL variant
|
|
55
|
-
visualforce: "html", // Visualforce uses HTML-like syntax
|
|
56
|
-
js: "javascript",
|
|
57
|
-
ts: "typescript",
|
|
58
|
-
yml: "yaml",
|
|
59
|
-
md: "markdown",
|
|
60
|
-
sh: "bash"
|
|
61
|
-
};
|
|
62
|
-
|
|
63
|
-
// Core languages loaded eagerly (keep minimal; load others on demand)
|
|
64
|
-
const CORE_LANGUAGES: BundledLanguage[] = [
|
|
65
|
-
"apex",
|
|
66
|
-
"javascript",
|
|
67
|
-
"html",
|
|
68
|
-
"css",
|
|
69
|
-
"json",
|
|
70
|
-
"sql",
|
|
71
|
-
"bash",
|
|
72
|
-
"xml",
|
|
73
|
-
"graphql",
|
|
74
|
-
"jsx",
|
|
75
|
-
getCustomLanguageGrammars().ampscript
|
|
76
|
-
];
|
|
29
|
+
const shikiState: ShikiState = createShikiState();
|
|
77
30
|
|
|
78
|
-
// Non-critical languages that can be loaded asynchronously when needed
|
|
79
|
-
const OPTIONAL_LANGUAGES: Record<string, any> = {
|
|
80
|
-
// @ts-ignore
|
|
81
|
-
typescript: "typescript",
|
|
82
|
-
// @ts-ignore
|
|
83
|
-
markdown: "markdown",
|
|
84
|
-
// @ts-ignore
|
|
85
|
-
python: "python",
|
|
86
|
-
// @ts-ignore
|
|
87
|
-
java: "java",
|
|
88
|
-
// @ts-ignore
|
|
89
|
-
yaml: "yaml",
|
|
90
|
-
// @ts-ignore
|
|
91
|
-
php: "php",
|
|
92
|
-
// @ts-ignore
|
|
93
|
-
swift: "swift",
|
|
94
|
-
// @ts-ignore
|
|
95
|
-
kotlin: "kotlin",
|
|
96
|
-
// @ts-ignore
|
|
97
|
-
handlebars: "handlebars",
|
|
98
|
-
// @ts-ignore
|
|
99
|
-
dataweave: getCustomLanguageGrammars().dataweave,
|
|
100
|
-
// @ts-ignore
|
|
101
|
-
agentscript: getCustomLanguageGrammars().agentscript
|
|
102
|
-
};
|
|
103
|
-
|
|
104
|
-
// Initialize Shiki highlighter (lazy-load module and keep initial set minimal)
|
|
105
31
|
async function initializeShiki(): Promise<HighlighterCore> {
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
if (shikiInstance.initPromise) {
|
|
111
|
-
return shikiInstance.initPromise;
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
// Assign promise IMMEDIATELY before any async work to prevent race conditions
|
|
115
|
-
shikiInstance.initPromise = (async () => {
|
|
116
|
-
const shiki = await getShiki();
|
|
117
|
-
const highlighter = await shiki.createHighlighter({
|
|
118
|
-
themes: ["light-plus", "material-theme-darker"],
|
|
119
|
-
langs: CORE_LANGUAGES
|
|
120
|
-
});
|
|
121
|
-
shikiInstance.highlighter = highlighter;
|
|
122
|
-
shikiInstance.initialized = true;
|
|
123
|
-
return highlighter;
|
|
124
|
-
})();
|
|
125
|
-
|
|
126
|
-
try {
|
|
127
|
-
return await shikiInstance.initPromise;
|
|
128
|
-
} catch (error) {
|
|
129
|
-
console.error("Failed to initialize Shiki:", error);
|
|
130
|
-
shikiInstance.initPromise = null;
|
|
131
|
-
throw error;
|
|
132
|
-
}
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
// Async function to load additional languages when needed
|
|
136
|
-
async function loadLanguageIfNeeded(
|
|
137
|
-
highlighter: HighlighterCore,
|
|
138
|
-
language: string
|
|
139
|
-
): Promise<void> {
|
|
140
|
-
const loadedLanguages = highlighter.getLoadedLanguages();
|
|
141
|
-
|
|
142
|
-
if (!loadedLanguages.includes(language as BundledLanguage)) {
|
|
143
|
-
// Check if it's an optional language that needs to be loaded
|
|
144
|
-
const languageLoader = OPTIONAL_LANGUAGES[language];
|
|
145
|
-
|
|
146
|
-
if (languageLoader) {
|
|
147
|
-
try {
|
|
148
|
-
await highlighter.loadLanguage(languageLoader);
|
|
149
|
-
} catch (error) {
|
|
150
|
-
console.warn(
|
|
151
|
-
`Failed to load optional language ${language}:`,
|
|
152
|
-
error
|
|
153
|
-
);
|
|
154
|
-
}
|
|
155
|
-
}
|
|
156
|
-
}
|
|
32
|
+
const shikiModule = await getShiki();
|
|
33
|
+
return initializeHighlighter(shikiState, shikiModule);
|
|
157
34
|
}
|
|
158
35
|
|
|
159
|
-
// Get the mapped language or fallback to the original
|
|
160
|
-
function getMappedLanguage(language: string): BundledLanguage {
|
|
161
|
-
const mapped =
|
|
162
|
-
LANGUAGE_MAP[language.toLowerCase()] || language.toLowerCase();
|
|
163
|
-
return mapped as BundledLanguage;
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
// Highlight code with Shiki
|
|
167
36
|
export async function highlightCode(
|
|
168
37
|
code: string,
|
|
169
38
|
language: string,
|
|
@@ -171,39 +40,20 @@ export async function highlightCode(
|
|
|
171
40
|
): Promise<string> {
|
|
172
41
|
try {
|
|
173
42
|
const highlighter = await initializeShiki();
|
|
174
|
-
let mappedLanguage = getMappedLanguage(language);
|
|
175
|
-
|
|
176
|
-
// Try to load the language asynchronously if it's not already loaded
|
|
177
|
-
await loadLanguageIfNeeded(highlighter, mappedLanguage);
|
|
178
|
-
|
|
179
|
-
// Check if the language is supported after potential async loading
|
|
180
|
-
const loadedLanguages = highlighter.getLoadedLanguages();
|
|
181
|
-
if (!loadedLanguages.includes(mappedLanguage)) {
|
|
182
|
-
mappedLanguage = "text" as BundledLanguage;
|
|
183
|
-
}
|
|
184
|
-
|
|
185
43
|
const { transformerColorizedBrackets } = await getBrackets();
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
theme
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
});
|
|
44
|
+
return highlightCodeWithShiki(
|
|
45
|
+
code,
|
|
46
|
+
language,
|
|
47
|
+
theme,
|
|
48
|
+
highlighter,
|
|
49
|
+
transformerColorizedBrackets
|
|
50
|
+
);
|
|
194
51
|
} catch (error) {
|
|
195
52
|
console.error("Failed to highlight code with Shiki:", error);
|
|
196
|
-
|
|
197
|
-
return `<pre class="shiki ${theme}"><code>${escapeHtml(
|
|
198
|
-
code
|
|
199
|
-
)}</code></pre>`;
|
|
53
|
+
return `<pre class="shiki ${theme}"><code>${escapeHtml(code)}</code></pre>`;
|
|
200
54
|
}
|
|
201
55
|
}
|
|
202
56
|
|
|
203
|
-
// Utility function to escape HTML
|
|
204
57
|
export function escapeHtml(text: string): string {
|
|
205
|
-
|
|
206
|
-
div.textContent = text;
|
|
207
|
-
// eslint-disable-next-line @lwc/lwc/no-inner-html
|
|
208
|
-
return div.innerHTML;
|
|
58
|
+
return coreEscapeHtml(text);
|
|
209
59
|
}
|
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
import type { BundledLanguage, BundledTheme, HighlighterCore } from "shiki";
|
|
2
|
+
import { getCustomLanguageGrammars } from "dxUtils/shikiGrammars";
|
|
3
|
+
|
|
4
|
+
export interface ShikiModule {
|
|
5
|
+
createHighlighter: (options: {
|
|
6
|
+
themes: string[];
|
|
7
|
+
langs: BundledLanguage[];
|
|
8
|
+
}) => Promise<HighlighterCore>;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export interface BracketsModule {
|
|
12
|
+
transformerColorizedBrackets: () => any;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export interface ShikiState {
|
|
16
|
+
highlighter: HighlighterCore | null;
|
|
17
|
+
initialized: boolean;
|
|
18
|
+
initPromise: Promise<HighlighterCore> | null;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export const THEME_MAP: Record<string, BundledTheme> = {
|
|
22
|
+
light: "light-plus",
|
|
23
|
+
dark: "material-theme-darker"
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
export const THEME_MAP_COLOR_REPLACEMENTS: Record<
|
|
27
|
+
string,
|
|
28
|
+
{ colorReplacements: Record<string, string> }
|
|
29
|
+
> = {
|
|
30
|
+
light: {
|
|
31
|
+
colorReplacements: {}
|
|
32
|
+
},
|
|
33
|
+
dark: {
|
|
34
|
+
colorReplacements: {
|
|
35
|
+
"#545454": "#8A8A8A"
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
export const LANGUAGE_MAP: Record<string, BundledLanguage> = {
|
|
41
|
+
ssjs: "javascript",
|
|
42
|
+
gtl: "handlebars",
|
|
43
|
+
lwc: "jsx",
|
|
44
|
+
sql_docs_template: "sql",
|
|
45
|
+
visualforce: "html",
|
|
46
|
+
js: "javascript",
|
|
47
|
+
ts: "typescript",
|
|
48
|
+
yml: "yaml",
|
|
49
|
+
md: "markdown",
|
|
50
|
+
sh: "bash"
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
export const CORE_LANGUAGES: BundledLanguage[] = [
|
|
54
|
+
"apex",
|
|
55
|
+
"javascript",
|
|
56
|
+
"html",
|
|
57
|
+
"css",
|
|
58
|
+
"json",
|
|
59
|
+
"sql",
|
|
60
|
+
"bash",
|
|
61
|
+
"xml",
|
|
62
|
+
"graphql",
|
|
63
|
+
"jsx",
|
|
64
|
+
getCustomLanguageGrammars().ampscript
|
|
65
|
+
];
|
|
66
|
+
|
|
67
|
+
export const OPTIONAL_LANGUAGES: Record<string, any> = {
|
|
68
|
+
// @ts-ignore
|
|
69
|
+
typescript: "typescript",
|
|
70
|
+
// @ts-ignore
|
|
71
|
+
markdown: "markdown",
|
|
72
|
+
// @ts-ignore
|
|
73
|
+
python: "python",
|
|
74
|
+
// @ts-ignore
|
|
75
|
+
java: "java",
|
|
76
|
+
// @ts-ignore
|
|
77
|
+
yaml: "yaml",
|
|
78
|
+
// @ts-ignore
|
|
79
|
+
php: "php",
|
|
80
|
+
// @ts-ignore
|
|
81
|
+
swift: "swift",
|
|
82
|
+
// @ts-ignore
|
|
83
|
+
kotlin: "kotlin",
|
|
84
|
+
// @ts-ignore
|
|
85
|
+
handlebars: "handlebars",
|
|
86
|
+
// @ts-ignore
|
|
87
|
+
dataweave: getCustomLanguageGrammars().dataweave,
|
|
88
|
+
// @ts-ignore
|
|
89
|
+
agentscript: getCustomLanguageGrammars().agentscript
|
|
90
|
+
};
|
|
91
|
+
|
|
92
|
+
export async function loadLanguageIfNeeded(
|
|
93
|
+
highlighter: HighlighterCore,
|
|
94
|
+
language: string
|
|
95
|
+
): Promise<void> {
|
|
96
|
+
const loadedLanguages = highlighter.getLoadedLanguages();
|
|
97
|
+
|
|
98
|
+
if (!loadedLanguages.includes(language as BundledLanguage)) {
|
|
99
|
+
const languageLoader = OPTIONAL_LANGUAGES[language];
|
|
100
|
+
|
|
101
|
+
if (languageLoader) {
|
|
102
|
+
try {
|
|
103
|
+
await highlighter.loadLanguage(languageLoader);
|
|
104
|
+
} catch (error) {
|
|
105
|
+
console.warn(
|
|
106
|
+
`Failed to load optional language ${language}:`,
|
|
107
|
+
error
|
|
108
|
+
);
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
export function getMappedLanguage(language: string): BundledLanguage {
|
|
115
|
+
const mapped =
|
|
116
|
+
LANGUAGE_MAP[language.toLowerCase()] || language.toLowerCase();
|
|
117
|
+
return mapped as BundledLanguage;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
export function escapeHtml(text: string): string {
|
|
121
|
+
const div = document.createElement("div");
|
|
122
|
+
div.textContent = text;
|
|
123
|
+
// eslint-disable-next-line @lwc/lwc/no-inner-html
|
|
124
|
+
return div.innerHTML;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
export function createShikiState(): ShikiState {
|
|
128
|
+
return {
|
|
129
|
+
highlighter: null,
|
|
130
|
+
initialized: false,
|
|
131
|
+
initPromise: null
|
|
132
|
+
};
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
export async function initializeHighlighter(
|
|
136
|
+
shikiState: ShikiState,
|
|
137
|
+
shikiModule: ShikiModule
|
|
138
|
+
): Promise<HighlighterCore> {
|
|
139
|
+
if (shikiState.highlighter) {
|
|
140
|
+
return shikiState.highlighter;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
if (shikiState.initPromise) {
|
|
144
|
+
return shikiState.initPromise;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
shikiState.initPromise = (async () => {
|
|
148
|
+
const highlighter = await shikiModule.createHighlighter({
|
|
149
|
+
themes: ["light-plus", "material-theme-darker"],
|
|
150
|
+
langs: CORE_LANGUAGES
|
|
151
|
+
});
|
|
152
|
+
shikiState.highlighter = highlighter;
|
|
153
|
+
shikiState.initialized = true;
|
|
154
|
+
return highlighter;
|
|
155
|
+
})();
|
|
156
|
+
|
|
157
|
+
try {
|
|
158
|
+
return await shikiState.initPromise;
|
|
159
|
+
} catch (error) {
|
|
160
|
+
console.error("Failed to initialize Shiki:", error);
|
|
161
|
+
shikiState.initPromise = null;
|
|
162
|
+
throw error;
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
export async function highlightCodeWithShiki(
|
|
167
|
+
code: string,
|
|
168
|
+
language: string,
|
|
169
|
+
theme: "light" | "dark",
|
|
170
|
+
highlighter: HighlighterCore,
|
|
171
|
+
transformerColorizedBrackets: () => any
|
|
172
|
+
): Promise<string> {
|
|
173
|
+
let mappedLanguage = getMappedLanguage(language);
|
|
174
|
+
|
|
175
|
+
await loadLanguageIfNeeded(highlighter, mappedLanguage);
|
|
176
|
+
|
|
177
|
+
const loadedLanguages = highlighter.getLoadedLanguages();
|
|
178
|
+
if (!loadedLanguages.includes(mappedLanguage)) {
|
|
179
|
+
mappedLanguage = "text" as BundledLanguage;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
return highlighter.codeToHtml(code, {
|
|
183
|
+
lang: mappedLanguage,
|
|
184
|
+
theme: THEME_MAP[theme],
|
|
185
|
+
transformers: [transformerColorizedBrackets()],
|
|
186
|
+
colorReplacements: THEME_MAP_COLOR_REPLACEMENTS[theme].colorReplacements
|
|
187
|
+
});
|
|
188
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import type { HighlighterCore } from "shiki";
|
|
2
|
+
import * as shikiModule from "shiki";
|
|
3
|
+
import { transformerColorizedBrackets } from "@shikijs/colorized-brackets";
|
|
4
|
+
import {
|
|
5
|
+
createShikiState,
|
|
6
|
+
initializeHighlighter,
|
|
7
|
+
highlightCodeWithShiki,
|
|
8
|
+
escapeHtml as coreEscapeHtml,
|
|
9
|
+
ShikiState
|
|
10
|
+
} from "dxUtils/shikiCore";
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Static loading version of Shiki.
|
|
14
|
+
* Uses static imports - shiki is bundled with this module.
|
|
15
|
+
* Use this for SSG compatibility and it is very specific to SSG generated components using LWR.
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
const shikiState: ShikiState = createShikiState();
|
|
19
|
+
|
|
20
|
+
async function initializeShiki(): Promise<HighlighterCore> {
|
|
21
|
+
return initializeHighlighter(shikiState, shikiModule);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export async function highlightCode(
|
|
25
|
+
code: string,
|
|
26
|
+
language: string,
|
|
27
|
+
theme: "light" | "dark" = "light"
|
|
28
|
+
): Promise<string> {
|
|
29
|
+
try {
|
|
30
|
+
const highlighter = await initializeShiki();
|
|
31
|
+
return highlightCodeWithShiki(
|
|
32
|
+
code,
|
|
33
|
+
language,
|
|
34
|
+
theme,
|
|
35
|
+
highlighter,
|
|
36
|
+
transformerColorizedBrackets
|
|
37
|
+
);
|
|
38
|
+
} catch (error) {
|
|
39
|
+
console.error("Failed to highlight code with Shiki:", error);
|
|
40
|
+
return `<pre class="shiki ${theme}"><code>${escapeHtml(code)}</code></pre>`;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export function escapeHtml(text: string): string {
|
|
45
|
+
return coreEscapeHtml(text);
|
|
46
|
+
}
|