@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 CHANGED
@@ -140,6 +140,9 @@
140
140
  "dxUtils/recentSearches",
141
141
  "dxUtils/regexps",
142
142
  "dxUtils/withTypedRefs",
143
- "dxUtils/wordpress"
143
+ "dxUtils/wordpress",
144
+ "dxUtils/shiki",
145
+ "dxUtils/shikiStatic",
146
+ "dxUtils/shikiCore"
144
147
  ]
145
148
  }
package/package.json CHANGED
@@ -1,14 +1,10 @@
1
1
  {
2
2
  "name": "@salesforcedevs/dx-components",
3
- "version": "1.30.1-node22-1",
3
+ "version": "1.30.1",
4
4
  "description": "DX Lightning web components",
5
5
  "license": "MIT",
6
6
  "engines": {
7
- "node": ">=22.0.0"
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": "7874e944643e2f47606634d9d62ca585cefde113"
47
+ "gitHead": "7ea089ea32ead00fcc2c873a279464b17aee3428"
52
48
  }
@@ -34,7 +34,8 @@ export const createHighlighter = jest.fn().mockImplementation(() => {
34
34
  "apex",
35
35
  "text",
36
36
  "handlebars"
37
- ])
37
+ ]),
38
+ loadLanguage: jest.fn().mockResolvedValue(undefined)
38
39
  });
39
40
  });
40
41
 
@@ -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 { BundledLanguage, BundledTheme, HighlighterCore } from "shiki";
2
- import { getCustomLanguageGrammars } from "dxUtils/shikiGrammars";
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
- interface ShikiSingleton {
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
- if (shikiInstance.highlighter) {
107
- return shikiInstance.highlighter;
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
- return highlighter.codeToHtml(code, {
188
- lang: mappedLanguage,
189
- theme: THEME_MAP[theme],
190
- transformers: [transformerColorizedBrackets()],
191
- colorReplacements:
192
- THEME_MAP_COLOR_REPLACEMENTS[theme].colorReplacements
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
- // Fallback to plain text on error
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
- const div = document.createElement("div");
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,3 @@
1
+ <template>
2
+ <!-- This is a utility module for Shiki syntax highlighting (static imports) -->
3
+ </template>
@@ -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
+ }