react-shiki 0.7.4 → 0.9.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/README.md CHANGED
@@ -1,27 +1,31 @@
1
1
  # 🎨 [react-shiki](https://npmjs.com/react-shiki)
2
2
 
3
- > [!NOTE]
4
- > This library is still in development. More features will be implemented, and the API may change.
5
- > Contributions are welcome!
6
3
 
7
4
  A performant client-side syntax highlighting component and hook for React, built with [Shiki](https://shiki.matsu.io/).
8
5
 
9
6
  [See the demo page with highlighted code blocks showcasing several Shiki themes!](https://react-shiki.vercel.app/)
10
7
 
11
8
  <!--toc:start-->
12
-
13
9
  - 🎨 [react-shiki](https://npmjs.com/react-shiki)
14
10
  - [Features](#features)
15
11
  - [Installation](#installation)
16
12
  - [Usage](#usage)
17
13
  - [Bundle Options](#bundle-options)
14
+ - [`react-shiki` (Full Bundle)](#react-shiki-full-bundle)
15
+ - [`react-shiki/web` (Web Bundle)](#react-shikiweb-web-bundle)
16
+ - [`react-shiki/core` (Minimal Bundle)](#react-shikicore-minimal-bundle)
17
+ - [RegExp Engines](#regexp-engines)
18
18
  - [Configuration](#configuration)
19
19
  - [Common Configuration Options](#common-configuration-options)
20
20
  - [Component-specific Props](#component-specific-props)
21
21
  - [Multi-theme Support](#multi-theme-support)
22
+ - [Making Themes Reactive](#making-themes-reactive)
23
+ - [Option 1: Using `light-dark()` Function (Recommended)](#option-1-using-light-dark-function-recommended)
24
+ - [Option 2: CSS Theme Switching](#option-2-css-theme-switching)
22
25
  - [Custom Themes](#custom-themes)
23
26
  - [Custom Languages](#custom-languages)
24
27
  - [Preloading Custom Languages](#preloading-custom-languages)
28
+ - [Language Aliases](#language-aliases)
25
29
  - [Custom Transformers](#custom-transformers)
26
30
  - [Line Numbers](#line-numbers)
27
31
  - [Integration](#integration)
@@ -29,13 +33,14 @@ A performant client-side syntax highlighting component and hook for React, built
29
33
  - [Handling Inline Code](#handling-inline-code)
30
34
  - [Performance](#performance)
31
35
  - [Throttling Real-time Highlighting](#throttling-real-time-highlighting)
36
+ - [Output Format Optimization](#output-format-optimization)
32
37
  - [Streaming and LLM Chat UI](#streaming-and-llm-chat-ui)
33
- <!--toc:end-->
38
+ <!--toc:end-->
34
39
 
35
40
  ## Features
36
41
 
37
42
  - 🖼️ Provides both a `ShikiHighlighter` component and a `useShikiHighlighter` hook for more flexibility
38
- - 🔐 Shiki output is processed from HAST directly into React elements, no `dangerouslySetInnerHTML` required
43
+ - 🔐 Flexible output: Choose between React elements (no `dangerouslySetInnerHTML`) or HTML strings for better performance
39
44
  - 📦 Multiple bundle options: Full bundle (~1.2MB gz), web bundle (~695KB gz), or minimal core bundle for fine-grained bundle control
40
45
  - 🖌️ Full support for custom TextMate themes and languages
41
46
  - 🔧 Supports passing custom Shiki transformers to the highlighter, in addition to all other options supported by `codeToHast`
@@ -90,17 +95,19 @@ function CodeBlock({ code, language }) {
90
95
  ```tsx
91
96
  import ShikiHighlighter from 'react-shiki';
92
97
  ```
93
- - **Size**: ~6.4MB minified, 1.2MB gzipped
98
+ - **Size**: ~6.4MB minified, ~1.2MB gzipped (includes ~12KB react-shiki)
94
99
  - **Languages**: All Shiki languages and themes
100
+ - **Exported engines**: `createJavaScriptRegexEngine`, `createJavaScriptRawEngine`
95
101
  - **Use case**: Unknown language requirements, maximum language support
96
102
  - **Setup**: Zero configuration required
97
103
 
98
- ### `react-shiki/web` (Web Bundle)
104
+ ### `react-shiki/web` (Web Bundle)
99
105
  ```tsx
100
106
  import ShikiHighlighter from 'react-shiki/web';
101
107
  ```
102
- - **Size**: ~3.8MB minified, 695KB gzipped
108
+ - **Size**: ~3.8MB minified, ~707KB gzipped (includes ~12KB react-shiki)
103
109
  - **Languages**: Web-focused languages (HTML, CSS, JS, TS, JSON, Markdown, Vue, JSX, Svelte)
110
+ - **Exported engines**: `createJavaScriptRegexEngine`, `createJavaScriptRawEngine`
104
111
  - **Use case**: Web applications with balanced size/functionality
105
112
  - **Setup**: Drop-in replacement for main entry point
106
113
 
@@ -124,7 +131,7 @@ const highlighter = await createHighlighterCore({
124
131
  {code}
125
132
  </ShikiHighlighter>
126
133
  ```
127
- - **Size**: Minimal (only what you import)
134
+ - **Size**: ~12KB + your imported themes/languages
128
135
  - **Languages**: User-defined via custom highlighter
129
136
  - **Use case**: Production apps requiring maximum bundle control
130
137
  - **Setup**: Requires custom highlighter configuration
@@ -132,11 +139,59 @@ const highlighter = await createHighlighterCore({
132
139
 
133
140
  ### RegExp Engines
134
141
 
135
- Shiki offers two built-in engines:
136
- - **Oniguruma** - default, uses the compiled Oniguruma WebAssembly, and offer maximum language support
137
- - **JavaScript** - smaller bundle, faster startup, recommended when running highlighting on the client
142
+ Shiki offers three built-in engines for syntax highlighting:
143
+ - **Oniguruma** - Default engine using compiled WebAssembly, offers maximum language support
144
+ - **JavaScript RegExp** - Smaller bundle, faster startup, compiles patterns on-the-fly, recommended for client-side highlighting
145
+ - **JavaScript Raw** - For [pre-compiled languages](https://shiki.style/guide/regex-engines#pre-compiled-languages), skips transpilation step for best performance
146
+
147
+ #### Using Engines with Full and Web Bundles
148
+
149
+ The full and web bundles use Oniguruma by default, but you can override this with the `engine` option:
150
+
151
+ ```tsx
152
+ import {
153
+ useShikiHighlighter,
154
+ createJavaScriptRegexEngine,
155
+ createJavaScriptRawEngine
156
+ } from 'react-shiki';
157
+
158
+ // Hook with JavaScript RegExp engine
159
+ const highlightedCode = useShikiHighlighter(code, 'typescript', 'github-dark', {
160
+ engine: createJavaScriptRegexEngine()
161
+ });
162
+
163
+ // Component with JavaScript Raw engine (for pre-compiled languages)
164
+ // See https://shiki.style/guide/regex-engines#pre-compiled-languages
165
+ <ShikiHighlighter
166
+ language="typescript"
167
+ theme="github-dark"
168
+ engine={createJavaScriptRawEngine()}
169
+ >
170
+ {code}
171
+ </ShikiHighlighter>
172
+ ```
173
+
174
+ #### Using Engines with Core Bundle
175
+
176
+ When using the core bundle, you must specify an engine:
177
+
178
+ ```tsx
179
+ import {
180
+ createHighlighterCore,
181
+ createOnigurumaEngine,
182
+ createJavaScriptRegexEngine
183
+ } from 'react-shiki/core';
184
+
185
+ const highlighter = await createHighlighterCore({
186
+ themes: [import('@shikijs/themes/nord')],
187
+ langs: [import('@shikijs/langs/typescript')],
188
+ engine: createJavaScriptRegexEngine() // or createOnigurumaEngine(import('shiki/wasm'))
189
+ });
190
+ ```
191
+
192
+ #### Engine Options
138
193
 
139
- Unlike the Oniguruma engine, the JavaScript engine is [strict by default](https://shiki.style/guide/regex-engines#use-with-unsupported-languages). It will throw an error if it encounters an invalid Oniguruma pattern or a pattern that it cannot convert. If you want best-effort results for unsupported grammars, you can enable the forgiving option to suppress any conversion errors:
194
+ The JavaScript RegExp engine is [strict by default](https://shiki.style/guide/regex-engines#use-with-unsupported-languages). For best-effort results with unsupported grammars, enable the `forgiving` option:
140
195
 
141
196
  ```tsx
142
197
  createJavaScriptRegexEngine({ forgiving: true });
@@ -158,11 +213,13 @@ See [Shiki - RegExp Engines](https://shiki.style/guide/regex-engines) for more i
158
213
  | `delay` | `number` | `0` | Delay between highlights (in milliseconds) |
159
214
  | `customLanguages` | `array` | `[]` | Array of custom languages to preload |
160
215
  | `langAlias` | `object` | `{}` | Map of language aliases |
216
+ | `engine` | `RegexEngine` | Oniguruma | RegExp engine for syntax highlighting (Oniguruma, JavaScript RegExp, or JavaScript Raw) |
161
217
  | `showLineNumbers` | `boolean` | `false` | Display line numbers alongside code |
162
218
  | `startingLineNumber` | `number` | `1` | Starting line number when line numbers are enabled |
163
219
  | `transformers` | `array` | `[]` | Custom Shiki transformers for modifying the highlighting output |
164
220
  | `cssVariablePrefix` | `string` | `'--shiki'` | Prefix for CSS variables storing theme colors |
165
221
  | `defaultColor` | `string \| false` | `'light'` | Default theme mode when using multiple themes, can also disable default theme |
222
+ | `outputFormat` | `string` | `'react'` | Output format: 'react' for React nodes, 'html' for HTML string |
166
223
  | `tabindex` | `number` | `0` | Tab index for the code block |
167
224
  | `decorations` | `array` | `[]` | Custom decorations to wrap the highlighted tokens with |
168
225
  | `structure` | `string` | `classic` | The structure of the generated HAST and HTML - `classic` or `inline` |
@@ -253,12 +310,12 @@ Ensure your site sets the `color-scheme` CSS property:
253
310
  color-scheme: light dark;
254
311
  }
255
312
 
256
- /* Or dynamically with a class */
257
- * {
313
+ /* Or dynamically for class based dark mode */
314
+ :root {
258
315
  color-scheme: light;
259
316
  }
260
317
 
261
- .dark {
318
+ :root.dark {
262
319
  color-scheme: dark;
263
320
  }
264
321
  ```
@@ -564,121 +621,36 @@ const highlightedCode = useShikiHighlighter(code, "tsx", "github-dark", {
564
621
  });
565
622
  ```
566
623
 
567
- ### Streaming and LLM Chat UI
568
-
569
- `react-shiki` can be used to highlight streamed code from LLM responses in real-time.
570
-
571
- I use it for an LLM chatbot UI, it renders markdown and highlights
572
- code in memoized chat messages.
624
+ ### Output Format Optimization
573
625
 
574
- Using `useShikiHighlighter` hook:
626
+ `react-shiki` provides two output formats to balance safety and performance:
575
627
 
628
+ **React Nodes (Default)** - Safer, no `dangerouslySetInnerHTML` required
576
629
  ```tsx
577
- import type { ReactNode } from "react";
578
- import { isInlineCode, useShikiHighlighter, type Element } from "react-shiki";
579
- import tokyoNight from "@styles/tokyo-night.mjs";
580
-
581
- interface CodeHighlightProps {
582
- className?: string | undefined;
583
- children?: ReactNode | undefined;
584
- node?: Element | undefined;
585
- }
586
-
587
- export const CodeHighlight = ({
588
- className,
589
- children,
590
- node,
591
- ...props
592
- }: CodeHighlightProps) => {
593
- const code = String(children).trim();
594
- const language = className?.match(/language-(\w+)/)?.[1];
595
-
596
- const isInline = node ? isInlineCode(node) : false;
597
-
598
- const highlightedCode = useShikiHighlighter(code, language, tokyoNight, {
599
- delay: 150,
600
- });
630
+ // Hook
631
+ const highlightedCode = useShikiHighlighter(code, "tsx", "github-dark");
601
632
 
602
- return !isInline ? (
603
- <div
604
- className="shiki not-prose relative [&_pre]:overflow-auto
605
- [&_pre]:rounded-lg [&_pre]:px-6 [&_pre]:py-5"
606
- >
607
- {language ? (
608
- <span
609
- className="absolute right-3 top-2 text-xs tracking-tighter
610
- text-muted-foreground/85"
611
- >
612
- {language}
613
- </span>
614
- ) : null}
615
- {highlightedCode}
616
- </div>
617
- ) : (
618
- <code className={className} {...props}>
619
- {children}
620
- </code>
621
- );
622
- };
633
+ // Component
634
+ <ShikiHighlighter language="tsx" theme="github-dark">
635
+ {code}
636
+ </ShikiHighlighter>
623
637
  ```
624
638
 
625
- Or using the `ShikiHighlighter` component:
626
-
639
+ **HTML String** - 15-45% faster performance
627
640
  ```tsx
628
- import type { ReactNode } from "react";
629
- import ShikiHighlighter, { isInlineCode, type Element } from "react-shiki";
630
-
631
- interface CodeHighlightProps {
632
- className?: string | undefined;
633
- children?: ReactNode | undefined;
634
- node?: Element | undefined;
635
- }
636
-
637
- export const CodeHighlight = ({
638
- className,
639
- children,
640
- node,
641
- ...props
642
- }: CodeHighlightProps): JSX.Element => {
643
- const match = className?.match(/language-(\w+)/);
644
- const language = match ? match[1] : undefined;
645
- const code = String(children).trim();
646
-
647
- const isInline: boolean | undefined = node ? isInlineCode(node) : undefined;
641
+ // Hook (returns HTML string, use dangerouslySetInnerHTML to render)
642
+ const highlightedCode = useShikiHighlighter(code, "tsx", "github-dark", {
643
+ outputFormat: 'html'
644
+ });
648
645
 
649
- return !isInline ? (
650
- <ShikiHighlighter
651
- language={language}
652
- theme="github-dark"
653
- delay={150}
654
- {...props}
655
- >
656
- {code}
657
- </ShikiHighlighter>
658
- ) : (
659
- <code className={className}>{code}</code>
660
- );
661
- };
646
+ // Component (automatically uses dangerouslySetInnerHTML when outputFormat is 'html')
647
+ <ShikiHighlighter language="tsx" theme="github-dark" outputFormat="html">
648
+ {code}
649
+ </ShikiHighlighter>
662
650
  ```
663
651
 
664
- Passed to `react-markdown` as a `code` component in memoized chat messages:
652
+ Choose HTML output when performance is critical and you trust the code source. Use the default React output when handling untrusted content or when security is the primary concern.
665
653
 
666
- ```tsx
667
- const RenderedMessage = React.memo(({ message }: { message: Message }) => (
668
- <div className={cn(messageStyles[message.role])}>
669
- <ReactMarkdown components={{ code: CodeHighlight }}>
670
- {message.content}
671
- </ReactMarkdown>
672
- </div>
673
- ));
674
-
675
- export const ChatMessages = ({ messages }: { messages: Message[] }) => {
676
- return (
677
- <div className="space-y-4">
678
- {messages.map((message) => (
679
- <RenderedMessage key={message.id} message={message} />
680
- ))}
681
- </div>
682
- );
683
- };
684
- ```
654
+ ---
655
+
656
+ Made with ❤️ by [Bassim (AVGVSTVS96)](https://github.com/AVGVSTVS96)
@@ -1,17 +1,33 @@
1
+ // src/lib/hook.ts
2
+ import {
3
+ useEffect,
4
+ useMemo,
5
+ useRef as useRef2,
6
+ useState
7
+ } from "react";
8
+ import { jsx, jsxs, Fragment } from "react/jsx-runtime";
9
+ import { toJsxRuntime } from "hast-util-to-jsx-runtime";
10
+
1
11
  // src/lib/utils.ts
2
- import { visit } from "unist-util-visit";
3
- function rehypeInlineCodeProperty() {
4
- return (tree) => {
5
- visit(tree, "element", (node, _index, parent) => {
6
- if (node.tagName === "code" && parent.tagName !== "pre") {
7
- node.properties.inline = true;
8
- }
9
- });
10
- };
11
- }
12
- var isInlineCode = (node) => {
13
- const textContent = (node.children || []).filter((child) => child.type === "text").map((child) => child.value).join("");
14
- return !textContent.includes("\n");
12
+ import { useRef } from "react";
13
+ import { dequal } from "dequal";
14
+ var useStableOptions = (value) => {
15
+ const ref = useRef(value);
16
+ const revision = useRef(0);
17
+ if (typeof value !== "object" || value === null) {
18
+ if (value !== ref.current) {
19
+ ref.current = value;
20
+ revision.current += 1;
21
+ }
22
+ return [value, revision.current];
23
+ }
24
+ if (value !== ref.current) {
25
+ if (!dequal(value, ref.current)) {
26
+ ref.current = value;
27
+ revision.current += 1;
28
+ }
29
+ }
30
+ return [ref.current, revision.current];
15
31
  };
16
32
  var throttleHighlighting = (performHighlight, timeoutControl, throttleMs) => {
17
33
  const now = Date.now();
@@ -23,17 +39,6 @@ var throttleHighlighting = (performHighlight, timeoutControl, throttleMs) => {
23
39
  }, delay);
24
40
  };
25
41
 
26
- // src/lib/hook.ts
27
- import {
28
- useEffect,
29
- useMemo,
30
- useRef,
31
- useState
32
- } from "react";
33
- import { dequal } from "dequal/lite";
34
- import { jsx, jsxs, Fragment } from "react/jsx-runtime";
35
- import { toJsxRuntime } from "hast-util-to-jsx-runtime";
36
-
37
42
  // src/lib/resolvers.ts
38
43
  var resolveLanguage = (lang, customLanguages, langAliases) => {
39
44
  const normalizedCustomLangs = customLanguages ? Array.isArray(customLanguages) ? customLanguages : [customLanguages] : [];
@@ -128,25 +133,7 @@ var DEFAULT_THEMES = {
128
133
  light: "github-light",
129
134
  dark: "github-dark"
130
135
  };
131
- var useStableOptions = (value) => {
132
- const ref = useRef(value);
133
- const revision = useRef(0);
134
- if (typeof value !== "object" || value === null) {
135
- if (value !== ref.current) {
136
- ref.current = value;
137
- revision.current += 1;
138
- }
139
- return [value, revision.current];
140
- }
141
- if (value !== ref.current) {
142
- if (!dequal(value, ref.current)) {
143
- ref.current = value;
144
- revision.current += 1;
145
- }
146
- }
147
- return [ref.current, revision.current];
148
- };
149
- var useShikiHighlighter = (code, lang, themeInput, createHighlighter, options = {}) => {
136
+ var useShikiHighlighter = (code, lang, themeInput, options = {}, highlighterFactory) => {
150
137
  const [highlightedCode, setHighlightedCode] = useState(null);
151
138
  const [stableLang, langRev] = useStableOptions(lang);
152
139
  const [stableTheme, themeRev] = useStableOptions(themeInput);
@@ -160,7 +147,7 @@ var useShikiHighlighter = (code, lang, themeInput, createHighlighter, options =
160
147
  [stableLang, stableOpts.customLanguages, stableOpts.langAlias]
161
148
  );
162
149
  const { isMultiTheme, themeId, multiTheme, singleTheme, themesToLoad } = useMemo(() => resolveTheme(stableTheme), [stableTheme]);
163
- const timeoutControl = useRef({
150
+ const timeoutControl = useRef2({
164
151
  nextAllowedTime: 0,
165
152
  timeoutId: void 0
166
153
  });
@@ -195,16 +182,21 @@ var useShikiHighlighter = (code, lang, themeInput, createHighlighter, options =
195
182
  let isMounted = true;
196
183
  const highlightCode = async () => {
197
184
  if (!languageId) return;
198
- const highlighter = stableOpts.highlighter ? stableOpts.highlighter : await createHighlighter(
185
+ const highlighter = stableOpts.highlighter ? stableOpts.highlighter : await highlighterFactory(
199
186
  langsToLoad,
200
- themesToLoad
187
+ themesToLoad,
188
+ stableOpts.engine
201
189
  );
202
190
  const loadedLanguages = highlighter.getLoadedLanguages();
203
191
  const langToUse = loadedLanguages.includes(languageId) ? languageId : "plaintext";
204
192
  const finalOptions = { ...shikiOptions, lang: langToUse };
205
- const hast = highlighter.codeToHast(code, finalOptions);
206
193
  if (isMounted) {
207
- setHighlightedCode(toJsxRuntime(hast, { jsx, jsxs, Fragment }));
194
+ const output = stableOpts.outputFormat === "html" ? highlighter.codeToHtml(code, finalOptions) : toJsxRuntime(highlighter.codeToHast(code, finalOptions), {
195
+ jsx,
196
+ jsxs,
197
+ Fragment
198
+ });
199
+ setHighlightedCode(output);
208
200
  }
209
201
  };
210
202
  const { delay } = stableOpts;
@@ -228,6 +220,22 @@ var useShikiHighlighter = (code, lang, themeInput, createHighlighter, options =
228
220
  return highlightedCode;
229
221
  };
230
222
 
223
+ // src/lib/plugins.ts
224
+ import { visit } from "unist-util-visit";
225
+ function rehypeInlineCodeProperty() {
226
+ return (tree) => {
227
+ visit(tree, "element", (node, _index, parent) => {
228
+ if (node.tagName === "code" && parent.tagName !== "pre") {
229
+ node.properties.inline = true;
230
+ }
231
+ });
232
+ };
233
+ }
234
+ var isInlineCode = (node) => {
235
+ const textContent = (node.children || []).filter((child) => child.type === "text").map((child) => child.value).join("");
236
+ return !textContent.includes("\n");
237
+ };
238
+
231
239
  // #style-inject:#style-inject
232
240
  function styleInject(css, { insertAt } = {}) {
233
241
  if (!css || typeof document === "undefined") return;
@@ -299,6 +307,7 @@ var createShikiHighlighterComponent = (useShikiHighlighterImpl) => {
299
307
  theme,
300
308
  options
301
309
  );
310
+ const isHtmlOutput = typeof highlightedCode === "string";
302
311
  return /* @__PURE__ */ jsxs2(
303
312
  Element,
304
313
  {
@@ -322,7 +331,7 @@ var createShikiHighlighterComponent = (useShikiHighlighterImpl) => {
322
331
  children: displayLanguageId
323
332
  }
324
333
  ) : null,
325
- highlightedCode
334
+ isHtmlOutput ? /* @__PURE__ */ jsx2("div", { dangerouslySetInnerHTML: { __html: highlightedCode } }) : highlightedCode
326
335
  ]
327
336
  }
328
337
  );
@@ -331,9 +340,9 @@ var createShikiHighlighterComponent = (useShikiHighlighterImpl) => {
331
340
  };
332
341
 
333
342
  export {
343
+ useShikiHighlighter,
334
344
  rehypeInlineCodeProperty,
335
345
  isInlineCode,
336
- useShikiHighlighter,
337
346
  createShikiHighlighterComponent
338
347
  };
339
- //# sourceMappingURL=chunk-ZUXU23SW.js.map
348
+ //# sourceMappingURL=chunk-SCXX26KJ.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/lib/hook.ts","../src/lib/utils.ts","../src/lib/resolvers.ts","../src/lib/transformers.ts","../src/lib/plugins.ts","#style-inject:#style-inject","../src/lib/styles.css","../src/lib/component.tsx"],"sourcesContent":["import {\n useEffect,\n useMemo,\n useRef,\n useState,\n type ReactNode,\n} from 'react';\n\nimport { jsx, jsxs, Fragment } from 'react/jsx-runtime';\nimport { toJsxRuntime } from 'hast-util-to-jsx-runtime';\n\nimport type {\n CodeToHastOptions,\n CodeOptionsSingleTheme,\n CodeOptionsMultipleThemes,\n Highlighter,\n HighlighterCore,\n Awaitable,\n RegexEngine,\n BundledTheme,\n} from 'shiki';\n\nimport type { ShikiLanguageRegistration } from './extended-types';\n\nimport type {\n Language,\n Theme,\n HighlighterOptions,\n TimeoutState,\n Themes,\n} from './types';\n\nimport { throttleHighlighting, useStableOptions } from './utils';\nimport { resolveLanguage, resolveTheme } from './resolvers';\nimport { lineNumbersTransformer } from './transformers';\n\nconst DEFAULT_THEMES: Themes = {\n light: 'github-light',\n dark: 'github-dark',\n};\n\n/**\n * Base hook for syntax highlighting using Shiki.\n * This is the core implementation used by all entry points.\n *\n * @param code - The code to highlight\n * @param lang - Language for highlighting\n * @param themeInput - Theme or themes to use\n * @param options - Highlighting options\n * @param highlighterFactory - Factory function to create highlighter (internal use)\n */\nexport const useShikiHighlighter = (\n code: string,\n lang: Language,\n themeInput: Theme | Themes,\n options: HighlighterOptions = {},\n highlighterFactory: (\n langsToLoad: ShikiLanguageRegistration,\n themesToLoad: Theme[],\n engine?: Awaitable<RegexEngine>\n ) => Promise<Highlighter | HighlighterCore>\n) => {\n const [highlightedCode, setHighlightedCode] = useState<\n ReactNode | string | null\n >(null);\n\n // Stabilize options, language and theme inputs to prevent unnecessary\n // re-renders or recalculations when object references change\n const [stableLang, langRev] = useStableOptions(lang);\n const [stableTheme, themeRev] = useStableOptions(themeInput);\n const [stableOpts, optsRev] = useStableOptions(options);\n\n const { languageId, langsToLoad } = useMemo(\n () =>\n resolveLanguage(\n stableLang,\n stableOpts.customLanguages,\n stableOpts.langAlias\n ),\n [stableLang, stableOpts.customLanguages, stableOpts.langAlias]\n );\n\n const { isMultiTheme, themeId, multiTheme, singleTheme, themesToLoad } =\n useMemo(() => resolveTheme(stableTheme), [stableTheme]);\n\n const timeoutControl = useRef<TimeoutState>({\n nextAllowedTime: 0,\n timeoutId: undefined,\n });\n\n const shikiOptions = useMemo<CodeToHastOptions>(() => {\n const languageOption = { lang: languageId };\n const {\n defaultColor,\n cssVariablePrefix,\n showLineNumbers,\n startingLineNumber,\n ...restOptions\n } = stableOpts;\n\n const themeOptions = isMultiTheme\n ? ({\n themes: multiTheme || DEFAULT_THEMES,\n defaultColor,\n cssVariablePrefix,\n } as CodeOptionsMultipleThemes<BundledTheme>)\n : ({\n theme: singleTheme || DEFAULT_THEMES.dark,\n } as CodeOptionsSingleTheme<BundledTheme>);\n\n const transformers = restOptions.transformers || [];\n if (showLineNumbers) {\n transformers.push(lineNumbersTransformer(startingLineNumber));\n }\n\n return {\n ...languageOption,\n ...themeOptions,\n ...restOptions,\n transformers,\n };\n }, [languageId, themeId, langRev, themeRev, optsRev]);\n\n useEffect(() => {\n let isMounted = true;\n\n const highlightCode = async () => {\n if (!languageId) return;\n\n const highlighter = stableOpts.highlighter\n ? stableOpts.highlighter\n : await highlighterFactory(\n langsToLoad as ShikiLanguageRegistration,\n themesToLoad,\n stableOpts.engine\n );\n\n const loadedLanguages = highlighter.getLoadedLanguages();\n const langToUse = loadedLanguages.includes(languageId)\n ? languageId\n : 'plaintext';\n const finalOptions = { ...shikiOptions, lang: langToUse };\n\n if (isMounted) {\n const output =\n stableOpts.outputFormat === 'html'\n ? highlighter.codeToHtml(code, finalOptions)\n : toJsxRuntime(highlighter.codeToHast(code, finalOptions), {\n jsx,\n jsxs,\n Fragment,\n });\n setHighlightedCode(output);\n }\n };\n\n const { delay } = stableOpts;\n\n if (delay) {\n throttleHighlighting(highlightCode, timeoutControl, delay);\n } else {\n highlightCode().catch(console.error);\n }\n\n return () => {\n isMounted = false;\n clearTimeout(timeoutControl.current.timeoutId);\n };\n }, [\n code,\n shikiOptions,\n stableOpts.delay,\n stableOpts.highlighter,\n langsToLoad,\n themesToLoad,\n ]);\n\n return highlightedCode;\n};\n","import { useRef } from 'react';\nimport { dequal } from 'dequal';\n\nimport type { TimeoutState } from './types';\n\n/**\n * Returns a deep-stable reference and a version counter that only changes when content changes.\n * Includes optimizations for primitive values and reference equality.\n */\nexport const useStableOptions = <T>(value: T) => {\n const ref = useRef(value);\n const revision = useRef(0);\n\n // Fast-path for primitive values\n if (typeof value !== 'object' || value === null) {\n if (value !== ref.current) {\n ref.current = value;\n revision.current += 1;\n }\n return [value, revision.current] as const;\n }\n\n // Reference equality check before expensive deep comparison\n if (value !== ref.current) {\n if (!dequal(value, ref.current)) {\n ref.current = value;\n revision.current += 1;\n }\n }\n\n return [ref.current, revision.current] as const;\n};\n\n/**\n * Optionally throttles rapid sequential highlighting operations\n *\n * @example\n * const timeoutControl = useRef<TimeoutState>({\n * nextAllowedTime: 0,\n * timeoutId: undefined\n * });\n *\n * throttleHighlighting(highlightCode, timeoutControl, 1000);\n */\nexport const throttleHighlighting = (\n performHighlight: () => Promise<void>,\n timeoutControl: React.RefObject<TimeoutState>,\n throttleMs: number\n) => {\n const now = Date.now();\n clearTimeout(timeoutControl.current.timeoutId);\n\n const delay = Math.max(0, timeoutControl.current.nextAllowedTime - now);\n timeoutControl.current.timeoutId = setTimeout(() => {\n performHighlight().catch(console.error);\n timeoutControl.current.nextAllowedTime = now + throttleMs;\n }, delay);\n};\n","import type { Language, Theme, Themes } from './types';\nimport type { ThemeRegistrationAny } from 'shiki/core';\nimport type { LanguageRegistration } from './extended-types';\n\n/**\n * Resolved languages and metadata\n */\ntype LanguageResult = {\n languageId: string;\n displayLanguageId: string | null;\n langsToLoad: Language;\n};\n\n/**\n * Resolves the language input to standardized IDs and objects for Shiki and UI display\n * @param lang The language input from props\n * @param customLanguages An array of custom textmate grammar objects or a single grammar object\n * @returns A LanguageResult object containing:\n * - languageId: The resolved language ID\n * - displayLanguageId: The display language ID\n * - langToLoad: The language object or string id to load\n */\nexport const resolveLanguage = (\n lang: Language,\n customLanguages?: LanguageRegistration | LanguageRegistration[],\n langAliases?: Record<string, string>\n): LanguageResult => {\n const normalizedCustomLangs = customLanguages\n ? Array.isArray(customLanguages)\n ? customLanguages\n : [customLanguages]\n : [];\n\n // Language is null or empty string\n if (lang == null || (typeof lang === 'string' && !lang.trim())) {\n return {\n languageId: 'plaintext',\n displayLanguageId: 'plaintext',\n langsToLoad: undefined,\n };\n }\n\n // Language is custom\n if (typeof lang === 'object') {\n return {\n languageId: lang.name,\n displayLanguageId: lang.name || null,\n langsToLoad: lang,\n };\n }\n\n // Language is string\n const lowerLang = lang.toLowerCase();\n const matches = (str: string | undefined): boolean =>\n str?.toLowerCase() === lowerLang;\n\n // Check if the string identifies a provided custom language\n const customMatch = normalizedCustomLangs.find(\n (cl) =>\n matches(cl.name) ||\n matches(cl.scopeName) ||\n matches(cl.scopeName?.split('.').pop()) ||\n cl.aliases?.some(matches) ||\n cl.fileTypes?.some(matches)\n );\n\n if (customMatch) {\n return {\n languageId: customMatch.name || lang,\n displayLanguageId: lang,\n langsToLoad: customMatch,\n };\n }\n\n // Check if language is aliased\n if (langAliases?.[lang]) {\n return {\n languageId: langAliases[lang],\n displayLanguageId: lang,\n langsToLoad: langAliases[lang],\n };\n }\n\n // For any other string, pass it through,\n // fallback is handled in highlighter factories\n return {\n languageId: lang,\n displayLanguageId: lang,\n langsToLoad: lang,\n };\n};\n\n/**\n * Resolved themes and metadata\n */\ninterface ThemeResult {\n isMultiTheme: boolean;\n themeId: Theme;\n multiTheme?: Themes | ThemeRegistrationAny | null;\n singleTheme?: Theme | undefined;\n themesToLoad: Theme[];\n}\n\n/**\n * Determines theme configuration and returns the resolved theme with metadata\n * @param themeInput - The theme input, either as a string name or theme object\n * @returns Object containing:\n * - isMultiTheme: If theme input is a multi-theme configuration\n * - themeId: Theme reference identifier\n * - multiTheme: The multi-theme config if it exists\n * - singleTheme: The single theme if it exists\n * - themesToLoad: The themes to load when creating the highlighter\n */\nexport function resolveTheme(themeInput: Theme | Themes): ThemeResult {\n const isTextmateTheme =\n typeof themeInput === 'object' &&\n 'tokenColors' in themeInput &&\n Array.isArray(themeInput.tokenColors);\n\n // Assume non textmate objects are multi theme configs\n const isMultiThemeConfig =\n typeof themeInput === 'object' &&\n themeInput !== null &&\n !isTextmateTheme;\n\n const validMultiThemeObj =\n typeof themeInput === 'object' &&\n themeInput !== null &&\n !isTextmateTheme &&\n Object.entries(themeInput).some(\n ([key, value]) =>\n key &&\n value &&\n key.trim() !== '' &&\n value !== '' &&\n (typeof value === 'string' || isTextmateTheme)\n );\n\n if (isMultiThemeConfig) {\n const themeId = validMultiThemeObj\n ? `multi-${Object.values(themeInput)\n .map(\n (theme) =>\n (typeof theme === 'string' ? theme : theme?.name) ||\n 'custom'\n )\n .sort()\n .join('-')}`\n : 'multi-default';\n\n // If config is invalid, return null to handle fallback in `buildShikiOptions()`\n return {\n isMultiTheme: true,\n themeId,\n multiTheme: validMultiThemeObj ? themeInput : null,\n themesToLoad: validMultiThemeObj ? Object.values(themeInput) : [],\n };\n }\n\n return {\n isMultiTheme: false,\n themeId:\n typeof themeInput === 'string'\n ? themeInput\n : themeInput?.name || 'custom',\n singleTheme: themeInput,\n themesToLoad: [themeInput],\n };\n}\n","import type { ShikiTransformer } from 'shiki/core';\n\n/**\n * Creates a transformer that enables line numbers display\n * @param startLine - The starting line number (defaults to 1)\n */\nexport function lineNumbersTransformer(startLine = 1): ShikiTransformer {\n return {\n name: 'react-shiki:line-numbers',\n code(node) {\n this.addClassToHast(node, 'has-line-numbers');\n if (startLine !== 1) {\n const existingStyle = (node.properties?.style as string) || '';\n const newStyle = existingStyle\n ? `${existingStyle}; --line-start: ${startLine}`\n : `--line-start: ${startLine}`;\n node.properties = {\n ...node.properties,\n style: newStyle,\n };\n }\n },\n line(node) {\n this.addClassToHast(node, 'line-numbers');\n return node;\n },\n };\n}\n","import { visit } from 'unist-util-visit';\nimport type { Element } from './types';\n\n/**\n * Rehype plugin to add an 'inline' property to <code> elements\n * Sets 'inline' property to true if the <code> is not within a <pre> tag\n *\n * Pass this plugin to the `rehypePlugins` prop of react-markdown\n * You can then access `inline` as a prop in components passed to react-markdown\n *\n * @example\n * <ReactMarkdown rehypePlugins={[rehypeInlineCodeProperty]} />\n */\nexport function rehypeInlineCodeProperty() {\n return (tree: any): undefined => {\n visit(tree, 'element', (node: Element, _index, parent: Element) => {\n if (node.tagName === 'code' && parent.tagName !== 'pre') {\n node.properties.inline = true;\n }\n });\n };\n}\n\n/**\n * Function to determine if code is inline based on the presence of line breaks\n *\n * @example\n * const isInline = node && isInlineCode(node: Element)\n */\nexport const isInlineCode = (node: Element): boolean => {\n const textContent = (node.children || [])\n .filter((child) => child.type === 'text')\n .map((child) => child.value)\n .join('');\n\n return !textContent.includes('\\n');\n};\n","\n export default function styleInject(css, { insertAt } = {}) {\n if (!css || typeof document === 'undefined') return\n \n const head = document.head || document.getElementsByTagName('head')[0]\n const style = document.createElement('style')\n style.type = 'text/css'\n \n if (insertAt === 'top') {\n if (head.firstChild) {\n head.insertBefore(style, head.firstChild)\n } else {\n head.appendChild(style)\n }\n } else {\n head.appendChild(style)\n }\n \n if (style.styleSheet) {\n style.styleSheet.cssText = css\n } else {\n style.appendChild(document.createTextNode(css))\n }\n }\n ","import styleInject from '#style-inject';styleInject(\".relative {\\n position: relative;\\n}\\n.defaultStyles pre {\\n overflow: auto;\\n border-radius: 0.5rem;\\n padding-left: 1.5rem;\\n padding-right: 1.5rem;\\n padding-top: 1.25rem;\\n padding-bottom: 1.25rem;\\n}\\n.languageLabel {\\n position: absolute;\\n right: 0.75rem;\\n top: 0.5rem;\\n font-family: monospace;\\n font-size: 0.75rem;\\n letter-spacing: -0.05em;\\n color: rgba(107, 114, 128, 0.85);\\n}\\n.line-numbers::before {\\n counter-increment: line-number;\\n content: counter(line-number);\\n display: inline-flex;\\n justify-content: flex-end;\\n align-items: flex-start;\\n box-sizing: content-box;\\n min-width: var(--line-numbers-width, 2ch);\\n padding-left: var(--line-numbers-padding-left, 2ch);\\n padding-right: var(--line-numbers-padding-right, 2ch);\\n color: var(--line-numbers-foreground, rgba(107, 114, 128, 0.6));\\n font-size: var(--line-numbers-font-size, inherit);\\n font-weight: var(--line-numbers-font-weight, inherit);\\n line-height: var(--line-numbers-line-height, inherit);\\n font-family: var(--line-numbers-font-family, inherit);\\n opacity: var(--line-numbers-opacity, 1);\\n user-select: none;\\n pointer-events: none;\\n}\\n.has-line-numbers {\\n counter-reset: line-number calc(var(--line-start, 1) - 1);\\n --line-numbers-foreground: rgba(107, 114, 128, 0.5);\\n --line-numbers-width: 2ch;\\n --line-numbers-padding-left: 0ch;\\n --line-numbers-padding-right: 2ch;\\n --line-numbers-font-size: inherit;\\n --line-numbers-font-weight: inherit;\\n --line-numbers-line-height: inherit;\\n --line-numbers-font-family: inherit;\\n --line-numbers-opacity: 1;\\n}\\n\")","import './styles.css';\nimport { clsx } from 'clsx';\nimport { resolveLanguage } from './resolvers';\n\nimport type {\n HighlighterOptions,\n Language,\n Theme,\n Themes,\n UseShikiHighlighter,\n} from './types';\nimport { forwardRef } from 'react';\n\n/**\n * Props for the ShikiHighlighter component\n */\nexport interface ShikiHighlighterProps extends HighlighterOptions {\n /**\n * The programming language for syntax highlighting\n * Supports custom textmate grammar objects in addition to Shiki's bundled languages\n * @see https://shiki.style/languages\n */\n language: Language;\n\n /**\n * The code to be highlighted\n */\n children: string;\n\n /**\n * The color theme or themes for syntax highlighting\n * Supports single, dual, or multiple themes\n * Supports custom textmate theme objects in addition to Shiki's bundled themes\n *\n * @example\n * theme='github-dark' // single theme\n * theme={{ light: 'github-light', dark: 'github-dark' }} // multi-theme\n *\n * @see https://shiki.style/themes\n */\n theme: Theme | Themes;\n\n /**\n * Controls the application of default styles to the generated code blocks\n *\n * Default styles include padding, overflow handling, border radius, language label styling, and font settings\n * @default true\n */\n addDefaultStyles?: boolean;\n\n /**\n * Add custom inline styles to the generated code block\n */\n style?: React.CSSProperties;\n\n /**\n * Add custom inline styles to the language label\n */\n langStyle?: React.CSSProperties;\n\n /**\n * Add custom CSS class names to the generated code block\n */\n className?: string;\n\n /**\n * Add custom CSS class names to the language label\n */\n langClassName?: string;\n\n /**\n * Whether to show the language label\n * @default true\n */\n showLanguage?: boolean;\n\n /**\n * Whether to show line numbers\n * @default false\n */\n showLineNumbers?: boolean;\n\n /**\n * Starting line number (when showLineNumbers is true)\n * @default 1\n */\n startingLineNumber?: number;\n\n /**\n * The HTML element that wraps the generated code block.\n * @default 'pre'\n */\n as?: React.ElementType;\n}\n\n/**\n * Base ShikiHighlighter component factory.\n * This creates a component that uses the provided hook implementation.\n */\nexport const createShikiHighlighterComponent = (\n useShikiHighlighterImpl: UseShikiHighlighter\n) => {\n return forwardRef<HTMLElement, ShikiHighlighterProps>(\n (\n {\n language,\n theme,\n delay,\n transformers,\n defaultColor,\n cssVariablePrefix,\n addDefaultStyles = true,\n style,\n langStyle,\n className,\n langClassName,\n showLanguage = true,\n showLineNumbers = false,\n startingLineNumber = 1,\n children: code,\n as: Element = 'pre',\n customLanguages,\n ...shikiOptions\n },\n ref\n ) => {\n // Destructure some options for use in hook\n const options: HighlighterOptions = {\n delay,\n transformers,\n customLanguages,\n showLineNumbers,\n defaultColor,\n cssVariablePrefix,\n startingLineNumber,\n ...shikiOptions,\n };\n\n // Use resolveLanguage to get displayLanguageId directly\n const { displayLanguageId } = resolveLanguage(\n language,\n customLanguages\n );\n\n const highlightedCode = useShikiHighlighterImpl(\n code,\n language,\n theme,\n options\n );\n\n const isHtmlOutput = typeof highlightedCode === 'string';\n\n return (\n <Element\n ref={ref}\n data-testid=\"shiki-container\"\n className={clsx(\n 'relative',\n 'not-prose',\n addDefaultStyles && 'defaultStyles',\n className\n )}\n style={style}\n id=\"shiki-container\"\n >\n {showLanguage && displayLanguageId ? (\n <span\n className={clsx('languageLabel', langClassName)}\n style={langStyle}\n id=\"language-label\"\n >\n {displayLanguageId}\n </span>\n ) : null}\n {isHtmlOutput ? (\n <div dangerouslySetInnerHTML={{ __html: highlightedCode }} />\n ) : (\n highlightedCode\n )}\n </Element>\n );\n }\n );\n};\n"],"mappings":"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","names":["useRef","useRef","jsx","jsxs"]}
@@ -85,6 +85,13 @@ interface ReactShikiOptions {
85
85
  * Custom textmate grammars to be preloaded for highlighting.
86
86
  */
87
87
  customLanguages?: LanguageRegistration | LanguageRegistration[];
88
+ /**
89
+ * Output format for the highlighted code.
90
+ * - 'react': Returns React nodes (default, safer)
91
+ * - 'html': Returns HTML string (~15-45% faster, requires dangerouslySetInnerHTML)
92
+ * @default 'react'
93
+ */
94
+ outputFormat?: 'react' | 'html';
88
95
  /**
89
96
  * Custom Shiki highlighter instance to use instead of the default one.
90
97
  * Keeps bundle small by only importing specified languages/themes.
@@ -128,13 +135,12 @@ interface ReactShikiOptions {
128
135
  * Configuration options for the syntax highlighter
129
136
  * Extends CodeToHastOptions to allow passing any Shiki options directly
130
137
  */
131
- interface HighlighterOptions extends ReactShikiOptions, Pick<CodeOptionsMultipleThemes<BundledTheme>, 'defaultColor' | 'cssVariablePrefix'>, Omit<CodeToHastOptions, 'lang' | 'theme' | 'themes'>, Pick<BundledHighlighterOptions<string, string>, 'langAlias'> {
138
+ interface HighlighterOptions extends ReactShikiOptions, Pick<CodeOptionsMultipleThemes<BundledTheme>, 'defaultColor' | 'cssVariablePrefix'>, Omit<CodeToHastOptions, 'lang' | 'theme' | 'themes'>, Pick<BundledHighlighterOptions<string, string>, 'langAlias' | 'engine'> {
132
139
  }
133
140
  /**
134
141
  * Public API signature for the useShikiHighlighter hook.
135
- * This ensures all entry points have consistent signatures.
136
142
  */
137
- type UseShikiHighlighter = (code: string, lang: Language, themeInput: Theme | Themes, options?: HighlighterOptions) => ReactNode;
143
+ type UseShikiHighlighter = (code: string, lang: Language, themeInput: Theme | Themes, options?: HighlighterOptions) => ReactNode | string | null;
138
144
 
139
145
  /**
140
146
  * Rehype plugin to add an 'inline' property to <code> elements
package/dist/core.d.ts CHANGED
@@ -1,32 +1,44 @@
1
1
  import * as react from 'react';
2
- import { S as ShikiHighlighterProps, U as UseShikiHighlighter } from './component-B4tYZaD8.js';
3
- export { E as Element, H as HighlighterOptions, L as Language, T as Theme, a as Themes, i as isInlineCode, r as rehypeInlineCodeProperty } from './component-B4tYZaD8.js';
2
+ import { S as ShikiHighlighterProps, U as UseShikiHighlighter } from './component-mMdHg8JD.js';
3
+ export { E as Element, H as HighlighterOptions, L as Language, T as Theme, a as Themes, i as isInlineCode, r as rehypeInlineCodeProperty } from './component-mMdHg8JD.js';
4
4
  export { createHighlighterCore } from 'shiki/core';
5
5
  export { createOnigurumaEngine } from 'shiki/engine/oniguruma';
6
- export { createJavaScriptRegexEngine } from 'shiki/engine/javascript';
6
+ export { createJavaScriptRawEngine, createJavaScriptRegexEngine } from 'shiki/engine/javascript';
7
7
  import 'shiki';
8
8
  import 'hast';
9
9
 
10
10
  /**
11
- * A React hook that provides syntax highlighting using Shiki with a custom highlighter.
12
- * Requires a highlighter to be provided in options for minimal bundle size.
11
+ * Highlight code with shiki (core bundle)
12
+ *
13
+ * @param code - Code to highlight
14
+ * @param lang - Language (bundled or custom)
15
+ * @param theme - Theme (bundled, multi-theme, or custom)
16
+ * @param options - react-shiki options + shiki options
17
+ * @returns Highlighted code as React elements or HTML string
13
18
  *
14
19
  * @example
15
- * ```ts
20
+ * ```tsx
16
21
  * import { createHighlighterCore, createOnigurumaEngine } from 'react-shiki/core';
17
22
  *
23
+ *
18
24
  * const highlighter = await createHighlighterCore({
19
- * themes: [import('@shikijs/themes/nord')],
25
+ * themes: [import('@shikijs/themes/github-light'), import('@shikijs/themes/github-dark')],
20
26
  * langs: [import('@shikijs/langs/typescript')],
21
27
  * engine: createOnigurumaEngine(import('shiki/wasm'))
22
28
  * });
23
29
  *
24
- * const code = useShikiHighlighter(code, 'typescript', 'nord', { highlighter });
30
+ * const highlighted = useShikiHighlighter(
31
+ * 'const x = 1;',
32
+ * 'typescript',
33
+ * {
34
+ * light: 'github-light',
35
+ * dark: 'github-dark'
36
+ * },
37
+ * { highlighter }
38
+ * );
25
39
  * ```
26
40
  *
27
- * For plug-and-play usage, consider:
28
- * - `react-shiki` for full shiki bundle (~6.4MB minified, 1.2MB gzipped)
29
- * - `react-shiki/web` for smaller shiki web bundle (~3.8MB minified, 695KB gzipped)
41
+ * Core bundle (minimal). For plug-and-play: `react-shiki` or `react-shiki/web`
30
42
  */
31
43
  declare const useShikiHighlighter: UseShikiHighlighter;
32
44
  /**
package/dist/core.js CHANGED
@@ -3,7 +3,7 @@ import {
3
3
  isInlineCode,
4
4
  rehypeInlineCodeProperty,
5
5
  useShikiHighlighter
6
- } from "./chunk-ZUXU23SW.js";
6
+ } from "./chunk-SCXX26KJ.js";
7
7
 
8
8
  // src/bundles/core.ts
9
9
  function validateCoreHighlighter(highlighter) {
@@ -18,13 +18,22 @@ function validateCoreHighlighter(highlighter) {
18
18
  // src/core.ts
19
19
  import { createHighlighterCore } from "shiki/core";
20
20
  import { createOnigurumaEngine } from "shiki/engine/oniguruma";
21
- import { createJavaScriptRegexEngine } from "shiki/engine/javascript";
21
+ import {
22
+ createJavaScriptRegexEngine,
23
+ createJavaScriptRawEngine
24
+ } from "shiki/engine/javascript";
22
25
  var useShikiHighlighter2 = (code, lang, themeInput, options = {}) => {
23
26
  const highlighter = validateCoreHighlighter(options.highlighter);
24
- return useShikiHighlighter(code, lang, themeInput, async () => highlighter, {
25
- ...options,
26
- highlighter
27
- });
27
+ return useShikiHighlighter(
28
+ code,
29
+ lang,
30
+ themeInput,
31
+ {
32
+ ...options,
33
+ highlighter
34
+ },
35
+ async () => highlighter
36
+ );
28
37
  };
29
38
  var ShikiHighlighter = createShikiHighlighterComponent(
30
39
  useShikiHighlighter2
@@ -33,6 +42,7 @@ var core_default = ShikiHighlighter;
33
42
  export {
34
43
  ShikiHighlighter,
35
44
  createHighlighterCore,
45
+ createJavaScriptRawEngine,
36
46
  createJavaScriptRegexEngine,
37
47
  createOnigurumaEngine,
38
48
  core_default as default,
package/dist/core.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/bundles/core.ts","../src/core.ts"],"sourcesContent":["import type { HighlighterCore } from 'shiki';\n\n/**\n * Validates that a highlighter is provided for the core bundle.\n * The core bundle requires users to provide their own highlighter instance.\n */\nexport function validateCoreHighlighter(\n highlighter: HighlighterCore | undefined\n): HighlighterCore {\n if (!highlighter) {\n throw new Error(\n 'react-shiki/core requires a custom highlighter. ' +\n 'Use createHighlighterCore() from react-shiki or switch to react-shiki for plug-and-play usage.'\n );\n }\n return highlighter;\n}\n","import { useShikiHighlighter as useBaseHook } from './lib/hook';\nimport { validateCoreHighlighter } from './bundles/core';\nimport type { UseShikiHighlighter } from './lib/types';\n\nexport { isInlineCode, rehypeInlineCodeProperty } from './lib/utils';\n\nimport {\n createShikiHighlighterComponent,\n type ShikiHighlighterProps,\n} from './lib/component';\n\nexport type { ShikiHighlighterProps };\n\nexport type {\n UseShikiHighlighter,\n Language,\n Theme,\n Themes,\n Element,\n HighlighterOptions,\n} from './lib/types';\n\nexport { createHighlighterCore } from 'shiki/core';\nexport { createOnigurumaEngine } from 'shiki/engine/oniguruma';\nexport { createJavaScriptRegexEngine } from 'shiki/engine/javascript';\n\n/**\n * A React hook that provides syntax highlighting using Shiki with a custom highlighter.\n * Requires a highlighter to be provided in options for minimal bundle size.\n *\n * @example\n * ```ts\n * import { createHighlighterCore, createOnigurumaEngine } from 'react-shiki/core';\n *\n * const highlighter = await createHighlighterCore({\n * themes: [import('@shikijs/themes/nord')],\n * langs: [import('@shikijs/langs/typescript')],\n * engine: createOnigurumaEngine(import('shiki/wasm'))\n * });\n *\n * const code = useShikiHighlighter(code, 'typescript', 'nord', { highlighter });\n * ```\n *\n * For plug-and-play usage, consider:\n * - `react-shiki` for full shiki bundle (~6.4MB minified, 1.2MB gzipped)\n * - `react-shiki/web` for smaller shiki web bundle (~3.8MB minified, 695KB gzipped)\n */\nexport const useShikiHighlighter: UseShikiHighlighter = (\n code,\n lang,\n themeInput,\n options = {}\n) => {\n // Validate that highlighter is provided\n const highlighter = validateCoreHighlighter(options.highlighter);\n\n return useBaseHook(code, lang, themeInput, async () => highlighter, {\n ...options,\n highlighter,\n });\n};\n\n/**\n * ShikiHighlighter component using a custom highlighter.\n * Requires a highlighter to be provided.\n */\nexport const ShikiHighlighter = createShikiHighlighterComponent(\n useShikiHighlighter\n);\nexport default ShikiHighlighter;\n"],"mappings":";;;;;;;;AAMO,SAAS,wBACd,aACiB;AACjB,MAAI,CAAC,aAAa;AAChB,UAAM,IAAI;AAAA,MACR;AAAA,IAEF;AAAA,EACF;AACA,SAAO;AACT;;;ACMA,SAAS,6BAA6B;AACtC,SAAS,6BAA6B;AACtC,SAAS,mCAAmC;AAuBrC,IAAMA,uBAA2C,CACtD,MACA,MACA,YACA,UAAU,CAAC,MACR;AAEH,QAAM,cAAc,wBAAwB,QAAQ,WAAW;AAE/D,SAAO,oBAAY,MAAM,MAAM,YAAY,YAAY,aAAa;AAAA,IAClE,GAAG;AAAA,IACH;AAAA,EACF,CAAC;AACH;AAMO,IAAM,mBAAmB;AAAA,EAC9BA;AACF;AACA,IAAO,eAAQ;","names":["useShikiHighlighter"]}
1
+ {"version":3,"sources":["../src/bundles/core.ts","../src/core.ts"],"sourcesContent":["import type { HighlighterCore } from 'shiki';\n\n/**\n * Validates that a highlighter is provided for the core bundle.\n * The core bundle requires users to provide their own highlighter instance.\n */\nexport function validateCoreHighlighter(\n highlighter: HighlighterCore | undefined\n): HighlighterCore {\n if (!highlighter) {\n throw new Error(\n 'react-shiki/core requires a custom highlighter. ' +\n 'Use createHighlighterCore() from react-shiki or switch to react-shiki for plug-and-play usage.'\n );\n }\n return highlighter;\n}\n","import { useShikiHighlighter as useBaseHook } from './lib/hook';\nimport { validateCoreHighlighter } from './bundles/core';\nimport type { UseShikiHighlighter } from './lib/types';\n\nexport { isInlineCode, rehypeInlineCodeProperty } from './lib/plugins';\n\nimport {\n createShikiHighlighterComponent,\n type ShikiHighlighterProps,\n} from './lib/component';\n\nexport type { ShikiHighlighterProps };\n\nexport type {\n UseShikiHighlighter,\n Language,\n Theme,\n Themes,\n Element,\n HighlighterOptions,\n} from './lib/types';\n\nexport { createHighlighterCore } from 'shiki/core';\nexport { createOnigurumaEngine } from 'shiki/engine/oniguruma';\nexport {\n createJavaScriptRegexEngine,\n createJavaScriptRawEngine,\n} from 'shiki/engine/javascript';\n\n/**\n * Highlight code with shiki (core bundle)\n *\n * @param code - Code to highlight\n * @param lang - Language (bundled or custom)\n * @param theme - Theme (bundled, multi-theme, or custom)\n * @param options - react-shiki options + shiki options\n * @returns Highlighted code as React elements or HTML string\n *\n * @example\n * ```tsx\n * import { createHighlighterCore, createOnigurumaEngine } from 'react-shiki/core';\n *\n *\n * const highlighter = await createHighlighterCore({\n * themes: [import('@shikijs/themes/github-light'), import('@shikijs/themes/github-dark')],\n * langs: [import('@shikijs/langs/typescript')],\n * engine: createOnigurumaEngine(import('shiki/wasm'))\n * });\n *\n * const highlighted = useShikiHighlighter(\n * 'const x = 1;',\n * 'typescript',\n * {\n * light: 'github-light',\n * dark: 'github-dark'\n * },\n * { highlighter }\n * );\n * ```\n *\n * Core bundle (minimal). For plug-and-play: `react-shiki` or `react-shiki/web`\n */\nexport const useShikiHighlighter: UseShikiHighlighter = (\n code,\n lang,\n themeInput,\n options = {}\n) => {\n // Validate that highlighter is provided\n const highlighter = validateCoreHighlighter(options.highlighter);\n\n return useBaseHook(\n code,\n lang,\n themeInput,\n {\n ...options,\n highlighter,\n },\n async () => highlighter\n );\n};\n\n/**\n * ShikiHighlighter component using a custom highlighter.\n * Requires a highlighter to be provided.\n */\nexport const ShikiHighlighter = createShikiHighlighterComponent(\n useShikiHighlighter\n);\nexport default ShikiHighlighter;\n"],"mappings":";;;;;;;;AAMO,SAAS,wBACd,aACiB;AACjB,MAAI,CAAC,aAAa;AAChB,UAAM,IAAI;AAAA,MACR;AAAA,IAEF;AAAA,EACF;AACA,SAAO;AACT;;;ACMA,SAAS,6BAA6B;AACtC,SAAS,6BAA6B;AACtC;AAAA,EACE;AAAA,EACA;AAAA,OACK;AAmCA,IAAMA,uBAA2C,CACtD,MACA,MACA,YACA,UAAU,CAAC,MACR;AAEH,QAAM,cAAc,wBAAwB,QAAQ,WAAW;AAE/D,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,MACE,GAAG;AAAA,MACH;AAAA,IACF;AAAA,IACA,YAAY;AAAA,EACd;AACF;AAMO,IAAM,mBAAmB;AAAA,EAC9BA;AACF;AACA,IAAO,eAAQ;","names":["useShikiHighlighter"]}
package/dist/index.d.ts CHANGED
@@ -1,19 +1,33 @@
1
1
  import * as react from 'react';
2
- import { S as ShikiHighlighterProps, U as UseShikiHighlighter } from './component-B4tYZaD8.js';
3
- export { E as Element, H as HighlighterOptions, L as Language, T as Theme, a as Themes, i as isInlineCode, r as rehypeInlineCodeProperty } from './component-B4tYZaD8.js';
2
+ import { S as ShikiHighlighterProps, U as UseShikiHighlighter } from './component-mMdHg8JD.js';
3
+ export { E as Element, H as HighlighterOptions, L as Language, T as Theme, a as Themes, i as isInlineCode, r as rehypeInlineCodeProperty } from './component-mMdHg8JD.js';
4
+ export { createJavaScriptRawEngine, createJavaScriptRegexEngine } from 'shiki/engine/javascript';
4
5
  import 'shiki';
5
6
  import 'shiki/core';
6
7
  import 'hast';
7
8
 
8
9
  /**
9
- * A React hook that provides syntax highlighting using Shiki with the full bundle.
10
- * Includes all languages and themes for maximum compatibility.
10
+ * Highlight code with shiki (full bundle)
11
+ *
12
+ * @param code - Code to highlight
13
+ * @param lang - Language (bundled or custom)
14
+ * @param theme - Theme (bundled, multi-theme, or custom)
15
+ * @param options - react-shiki options + shiki options
16
+ * @returns Highlighted code as React elements or HTML string
11
17
  *
12
- * Bundle size: ~6.4MB minified (1.2MB gzipped)
18
+ * @example
19
+ * ```tsx
20
+ * const highlighted = useShikiHighlighter(
21
+ * 'const x = 1;',
22
+ * 'typescript',
23
+ * {
24
+ * light: 'github-light',
25
+ * dark: 'github-dark'
26
+ * }
27
+ * );
28
+ * ```
13
29
  *
14
- * For smaller bundles, consider:
15
- * - `react-shiki/web` for smaller shiki web bundle (~3.8MB minified, 695KB gzipped)
16
- * - `react-shiki/core` for custom fine-grained bundle
30
+ * Full bundle (~6.4MB minified, 1.2MB gzipped). For smaller bundles: `react-shiki/web` or `react-shiki/core`
17
31
  */
18
32
  declare const useShikiHighlighter: UseShikiHighlighter;
19
33
  /**
package/dist/index.js CHANGED
@@ -3,21 +3,26 @@ import {
3
3
  isInlineCode,
4
4
  rehypeInlineCodeProperty,
5
5
  useShikiHighlighter
6
- } from "./chunk-ZUXU23SW.js";
6
+ } from "./chunk-SCXX26KJ.js";
7
7
 
8
8
  // src/bundles/full.ts
9
- import { getSingletonHighlighter } from "shiki";
10
- async function createFullHighlighter(langsToLoad, themesToLoad) {
9
+ import {
10
+ getSingletonHighlighter
11
+ } from "shiki";
12
+ import { createOnigurumaEngine } from "shiki/engine/oniguruma";
13
+ async function createFullHighlighter(langsToLoad, themesToLoad, engine) {
11
14
  try {
12
15
  return await getSingletonHighlighter({
13
16
  langs: [langsToLoad],
14
- themes: themesToLoad
17
+ themes: themesToLoad,
18
+ engine: engine ?? createOnigurumaEngine(import("shiki/wasm"))
15
19
  });
16
20
  } catch (error) {
17
21
  if (error instanceof Error && error.message.includes("Language")) {
18
22
  return await getSingletonHighlighter({
19
23
  langs: ["plaintext"],
20
- themes: themesToLoad
24
+ themes: themesToLoad,
25
+ engine: engine ?? createOnigurumaEngine(import("shiki/wasm"))
21
26
  });
22
27
  }
23
28
  throw error;
@@ -25,13 +30,17 @@ async function createFullHighlighter(langsToLoad, themesToLoad) {
25
30
  }
26
31
 
27
32
  // src/index.ts
33
+ import {
34
+ createJavaScriptRegexEngine,
35
+ createJavaScriptRawEngine
36
+ } from "shiki/engine/javascript";
28
37
  var useShikiHighlighter2 = (code, lang, themeInput, options = {}) => {
29
38
  return useShikiHighlighter(
30
39
  code,
31
40
  lang,
32
41
  themeInput,
33
- createFullHighlighter,
34
- options
42
+ options,
43
+ createFullHighlighter
35
44
  );
36
45
  };
37
46
  var ShikiHighlighter = createShikiHighlighterComponent(
@@ -40,6 +49,8 @@ var ShikiHighlighter = createShikiHighlighterComponent(
40
49
  var src_default = ShikiHighlighter;
41
50
  export {
42
51
  ShikiHighlighter,
52
+ createJavaScriptRawEngine,
53
+ createJavaScriptRegexEngine,
43
54
  src_default as default,
44
55
  isInlineCode,
45
56
  rehypeInlineCodeProperty,
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/bundles/full.ts","../src/index.ts"],"sourcesContent":["import { getSingletonHighlighter, type Highlighter } from 'shiki';\nimport type { ShikiLanguageRegistration } from '../lib/extended-types';\nimport type { Theme } from '../lib/types';\n\n/**\n * Creates a highlighter using the full Shiki bundle with all languages and themes.\n * This is the largest bundle but provides maximum compatibility.\n */\nexport async function createFullHighlighter(\n langsToLoad: ShikiLanguageRegistration,\n themesToLoad: Theme[]\n): Promise<Highlighter> {\n try {\n return await getSingletonHighlighter({\n langs: [langsToLoad],\n themes: themesToLoad,\n });\n } catch (error) {\n if (error instanceof Error && error.message.includes('Language')) {\n return await getSingletonHighlighter({\n langs: ['plaintext'],\n themes: themesToLoad,\n });\n }\n throw error;\n }\n}\n","import { useShikiHighlighter as useBaseHook } from './lib/hook';\nimport { createFullHighlighter } from './bundles/full';\nimport type { UseShikiHighlighter } from './lib/types';\n\nexport { isInlineCode, rehypeInlineCodeProperty } from './lib/utils';\n\nimport {\n createShikiHighlighterComponent,\n type ShikiHighlighterProps,\n} from './lib/component';\n\nexport type { ShikiHighlighterProps };\n\nexport type {\n UseShikiHighlighter,\n Language,\n Theme,\n Themes,\n Element,\n HighlighterOptions,\n} from './lib/types';\n\n/**\n * A React hook that provides syntax highlighting using Shiki with the full bundle.\n * Includes all languages and themes for maximum compatibility.\n *\n * Bundle size: ~6.4MB minified (1.2MB gzipped)\n *\n * For smaller bundles, consider:\n * - `react-shiki/web` for smaller shiki web bundle (~3.8MB minified, 695KB gzipped)\n * - `react-shiki/core` for custom fine-grained bundle\n */\nexport const useShikiHighlighter: UseShikiHighlighter = (\n code,\n lang,\n themeInput,\n options = {}\n) => {\n return useBaseHook(\n code,\n lang,\n themeInput,\n createFullHighlighter,\n options\n );\n};\n\n/**\n * ShikiHighlighter component using the full bundle.\n * Includes all languages and themes for maximum compatibility.\n */\nexport const ShikiHighlighter = createShikiHighlighterComponent(\n useShikiHighlighter\n);\nexport default ShikiHighlighter;\n"],"mappings":";;;;;;;;AAAA,SAAS,+BAAiD;AAQ1D,eAAsB,sBACpB,aACA,cACsB;AACtB,MAAI;AACF,WAAO,MAAM,wBAAwB;AAAA,MACnC,OAAO,CAAC,WAAW;AAAA,MACnB,QAAQ;AAAA,IACV,CAAC;AAAA,EACH,SAAS,OAAO;AACd,QAAI,iBAAiB,SAAS,MAAM,QAAQ,SAAS,UAAU,GAAG;AAChE,aAAO,MAAM,wBAAwB;AAAA,QACnC,OAAO,CAAC,WAAW;AAAA,QACnB,QAAQ;AAAA,MACV,CAAC;AAAA,IACH;AACA,UAAM;AAAA,EACR;AACF;;;ACMO,IAAMA,uBAA2C,CACtD,MACA,MACA,YACA,UAAU,CAAC,MACR;AACH,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AACF;AAMO,IAAM,mBAAmB;AAAA,EAC9BA;AACF;AACA,IAAO,cAAQ;","names":["useShikiHighlighter"]}
1
+ {"version":3,"sources":["../src/bundles/full.ts","../src/index.ts"],"sourcesContent":["import {\n getSingletonHighlighter,\n type Highlighter,\n type Awaitable,\n type RegexEngine,\n} from 'shiki';\nimport { createOnigurumaEngine } from 'shiki/engine/oniguruma';\nimport type { ShikiLanguageRegistration } from '../lib/extended-types';\nimport type { Theme } from '../lib/types';\n\n/**\n * Creates a highlighter using the full Shiki bundle with all languages and themes.\n * This is the largest bundle but provides maximum compatibility.\n */\nexport async function createFullHighlighter(\n langsToLoad: ShikiLanguageRegistration,\n themesToLoad: Theme[],\n engine?: Awaitable<RegexEngine>\n): Promise<Highlighter> {\n try {\n return await getSingletonHighlighter({\n langs: [langsToLoad],\n themes: themesToLoad,\n engine: engine ?? createOnigurumaEngine(import('shiki/wasm')),\n });\n } catch (error) {\n if (error instanceof Error && error.message.includes('Language')) {\n return await getSingletonHighlighter({\n langs: ['plaintext'],\n themes: themesToLoad,\n engine: engine ?? createOnigurumaEngine(import('shiki/wasm')),\n });\n }\n throw error;\n }\n}\n","import { useShikiHighlighter as useBaseHook } from './lib/hook';\nimport { createFullHighlighter } from './bundles/full';\nimport type { UseShikiHighlighter } from './lib/types';\n\nexport { isInlineCode, rehypeInlineCodeProperty } from './lib/plugins';\n\nimport {\n createShikiHighlighterComponent,\n type ShikiHighlighterProps,\n} from './lib/component';\n\nexport type { ShikiHighlighterProps };\n\nexport type {\n UseShikiHighlighter,\n Language,\n Theme,\n Themes,\n Element,\n HighlighterOptions,\n} from './lib/types';\n\nexport {\n createJavaScriptRegexEngine,\n createJavaScriptRawEngine,\n} from 'shiki/engine/javascript';\n\n/**\n * Highlight code with shiki (full bundle)\n *\n * @param code - Code to highlight\n * @param lang - Language (bundled or custom)\n * @param theme - Theme (bundled, multi-theme, or custom)\n * @param options - react-shiki options + shiki options\n * @returns Highlighted code as React elements or HTML string\n *\n * @example\n * ```tsx\n * const highlighted = useShikiHighlighter(\n * 'const x = 1;',\n * 'typescript',\n * {\n * light: 'github-light',\n * dark: 'github-dark'\n * }\n * );\n * ```\n *\n * Full bundle (~6.4MB minified, 1.2MB gzipped). For smaller bundles: `react-shiki/web` or `react-shiki/core`\n */\nexport const useShikiHighlighter: UseShikiHighlighter = (\n code,\n lang,\n themeInput,\n options = {}\n) => {\n return useBaseHook(\n code,\n lang,\n themeInput,\n options,\n createFullHighlighter\n );\n};\n\n/**\n * ShikiHighlighter component using the full bundle.\n * Includes all languages and themes for maximum compatibility.\n */\nexport const ShikiHighlighter = createShikiHighlighterComponent(\n useShikiHighlighter\n);\nexport default ShikiHighlighter;\n"],"mappings":";;;;;;;;AAAA;AAAA,EACE;AAAA,OAIK;AACP,SAAS,6BAA6B;AAQtC,eAAsB,sBACpB,aACA,cACA,QACsB;AACtB,MAAI;AACF,WAAO,MAAM,wBAAwB;AAAA,MACnC,OAAO,CAAC,WAAW;AAAA,MACnB,QAAQ;AAAA,MACR,QAAQ,UAAU,sBAAsB,OAAO,YAAY,CAAC;AAAA,IAC9D,CAAC;AAAA,EACH,SAAS,OAAO;AACd,QAAI,iBAAiB,SAAS,MAAM,QAAQ,SAAS,UAAU,GAAG;AAChE,aAAO,MAAM,wBAAwB;AAAA,QACnC,OAAO,CAAC,WAAW;AAAA,QACnB,QAAQ;AAAA,QACR,QAAQ,UAAU,sBAAsB,OAAO,YAAY,CAAC;AAAA,MAC9D,CAAC;AAAA,IACH;AACA,UAAM;AAAA,EACR;AACF;;;ACbA;AAAA,EACE;AAAA,EACA;AAAA,OACK;AAyBA,IAAMA,uBAA2C,CACtD,MACA,MACA,YACA,UAAU,CAAC,MACR;AACH,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AACF;AAMO,IAAM,mBAAmB;AAAA,EAC9BA;AACF;AACA,IAAO,cAAQ;","names":["useShikiHighlighter"]}
package/dist/web.d.ts CHANGED
@@ -1,19 +1,33 @@
1
1
  import * as react from 'react';
2
- import { S as ShikiHighlighterProps, U as UseShikiHighlighter } from './component-B4tYZaD8.js';
3
- export { E as Element, H as HighlighterOptions, L as Language, T as Theme, a as Themes, i as isInlineCode, r as rehypeInlineCodeProperty } from './component-B4tYZaD8.js';
2
+ import { S as ShikiHighlighterProps, U as UseShikiHighlighter } from './component-mMdHg8JD.js';
3
+ export { E as Element, H as HighlighterOptions, L as Language, T as Theme, a as Themes, i as isInlineCode, r as rehypeInlineCodeProperty } from './component-mMdHg8JD.js';
4
+ export { createJavaScriptRawEngine, createJavaScriptRegexEngine } from 'shiki/engine/javascript';
4
5
  import 'shiki';
5
6
  import 'shiki/core';
6
7
  import 'hast';
7
8
 
8
9
  /**
9
- * A React hook that provides syntax highlighting using Shiki with the web bundle.
10
- * Includes web-focused languages (HTML, CSS, JS, TS, JSON, Markdown, Astro, JSX, Svelte, Vue etc.)
10
+ * Highlight code with shiki (web bundle)
11
11
  *
12
- * Bundle size: ~3.8MB minified (695KB gzipped)
12
+ * @param code - Code to highlight
13
+ * @param lang - Language (bundled or custom)
14
+ * @param theme - Theme (bundled, multi-theme, or custom)
15
+ * @param options - react-shiki options + shiki options
16
+ * @returns Highlighted code as React elements or HTML string
13
17
  *
14
- * For other options, consider:
15
- * - `react-shiki` for full shiki bundle (~6.4MB minified, 1.2MB gzipped)
16
- * - `react-shiki/core` for custom fine-grained bundle
18
+ * @example
19
+ * ```tsx
20
+ * const highlighted = useShikiHighlighter(
21
+ * 'const x = 1;',
22
+ * 'typescript',
23
+ * {
24
+ * light: 'github-light',
25
+ * dark: 'github-dark'
26
+ * }
27
+ * );
28
+ * ```
29
+ *
30
+ * Web bundle (~3.8MB minified, 695KB gzipped). For other bundles: `react-shiki` or `react-shiki/core`
17
31
  */
18
32
  declare const useShikiHighlighter: UseShikiHighlighter;
19
33
  /**
package/dist/web.js CHANGED
@@ -3,23 +3,26 @@ import {
3
3
  isInlineCode,
4
4
  rehypeInlineCodeProperty,
5
5
  useShikiHighlighter
6
- } from "./chunk-ZUXU23SW.js";
6
+ } from "./chunk-SCXX26KJ.js";
7
7
 
8
8
  // src/bundles/web.ts
9
9
  import {
10
10
  getSingletonHighlighter
11
11
  } from "shiki/bundle/web";
12
- async function createWebHighlighter(langsToLoad, themesToLoad) {
12
+ import { createOnigurumaEngine } from "shiki/engine/oniguruma";
13
+ async function createWebHighlighter(langsToLoad, themesToLoad, engine) {
13
14
  try {
14
15
  return await getSingletonHighlighter({
15
16
  langs: [langsToLoad],
16
- themes: themesToLoad
17
+ themes: themesToLoad,
18
+ engine: engine ?? createOnigurumaEngine(import("shiki/wasm"))
17
19
  });
18
20
  } catch (error) {
19
21
  if (error instanceof Error && error.message.includes("Language")) {
20
22
  return await getSingletonHighlighter({
21
23
  langs: ["plaintext"],
22
- themes: themesToLoad
24
+ themes: themesToLoad,
25
+ engine: engine ?? createOnigurumaEngine(import("shiki/wasm"))
23
26
  });
24
27
  }
25
28
  throw error;
@@ -27,13 +30,17 @@ async function createWebHighlighter(langsToLoad, themesToLoad) {
27
30
  }
28
31
 
29
32
  // src/web.ts
33
+ import {
34
+ createJavaScriptRegexEngine,
35
+ createJavaScriptRawEngine
36
+ } from "shiki/engine/javascript";
30
37
  var useShikiHighlighter2 = (code, lang, themeInput, options = {}) => {
31
38
  return useShikiHighlighter(
32
39
  code,
33
40
  lang,
34
41
  themeInput,
35
- createWebHighlighter,
36
- options
42
+ options,
43
+ createWebHighlighter
37
44
  );
38
45
  };
39
46
  var ShikiHighlighter = createShikiHighlighterComponent(
@@ -42,6 +49,8 @@ var ShikiHighlighter = createShikiHighlighterComponent(
42
49
  var web_default = ShikiHighlighter;
43
50
  export {
44
51
  ShikiHighlighter,
52
+ createJavaScriptRawEngine,
53
+ createJavaScriptRegexEngine,
45
54
  web_default as default,
46
55
  isInlineCode,
47
56
  rehypeInlineCodeProperty,
package/dist/web.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/bundles/web.ts","../src/web.ts"],"sourcesContent":["import {\n getSingletonHighlighter,\n type Highlighter,\n} from 'shiki/bundle/web';\nimport type { ShikiLanguageRegistration } from '../lib/extended-types';\nimport type { Theme } from '../lib/types';\n\n/**\n * Creates a highlighter using the web Shiki bundle with web-focused languages.\n * Smaller than the full bundle while covering most web development needs.\n * Includes: HTML, CSS, JS, TS, JSON, Markdown, Vue, JSX, Svelte, etc.\n */\nexport async function createWebHighlighter(\n langsToLoad: ShikiLanguageRegistration,\n themesToLoad: Theme[]\n): Promise<Highlighter> {\n try {\n return await getSingletonHighlighter({\n langs: [langsToLoad],\n themes: themesToLoad,\n });\n } catch (error) {\n if (error instanceof Error && error.message.includes('Language')) {\n return await getSingletonHighlighter({\n langs: ['plaintext'],\n themes: themesToLoad,\n });\n }\n throw error;\n }\n}\n","import { useShikiHighlighter as useBaseHook } from './lib/hook';\nimport { createWebHighlighter } from './bundles/web';\nimport type { UseShikiHighlighter } from './lib/types';\n\nexport { isInlineCode, rehypeInlineCodeProperty } from './lib/utils';\n\nimport {\n createShikiHighlighterComponent,\n type ShikiHighlighterProps,\n} from './lib/component';\n\nexport type { ShikiHighlighterProps };\n\nexport type {\n UseShikiHighlighter,\n Language,\n Theme,\n Themes,\n Element,\n HighlighterOptions,\n} from './lib/types';\n\n/**\n * A React hook that provides syntax highlighting using Shiki with the web bundle.\n * Includes web-focused languages (HTML, CSS, JS, TS, JSON, Markdown, Astro, JSX, Svelte, Vue etc.)\n *\n * Bundle size: ~3.8MB minified (695KB gzipped)\n *\n * For other options, consider:\n * - `react-shiki` for full shiki bundle (~6.4MB minified, 1.2MB gzipped)\n * - `react-shiki/core` for custom fine-grained bundle\n */\nexport const useShikiHighlighter: UseShikiHighlighter = (\n code,\n lang,\n themeInput,\n options = {}\n) => {\n return useBaseHook(\n code,\n lang,\n themeInput,\n createWebHighlighter,\n options\n );\n};\n\n/**\n * ShikiHighlighter component using the web bundle.\n * Includes web-focused languages for balanced size and functionality.\n */\nexport const ShikiHighlighter = createShikiHighlighterComponent(\n useShikiHighlighter\n);\nexport default ShikiHighlighter;\n"],"mappings":";;;;;;;;AAAA;AAAA,EACE;AAAA,OAEK;AASP,eAAsB,qBACpB,aACA,cACsB;AACtB,MAAI;AACF,WAAO,MAAM,wBAAwB;AAAA,MACnC,OAAO,CAAC,WAAW;AAAA,MACnB,QAAQ;AAAA,IACV,CAAC;AAAA,EACH,SAAS,OAAO;AACd,QAAI,iBAAiB,SAAS,MAAM,QAAQ,SAAS,UAAU,GAAG;AAChE,aAAO,MAAM,wBAAwB;AAAA,QACnC,OAAO,CAAC,WAAW;AAAA,QACnB,QAAQ;AAAA,MACV,CAAC;AAAA,IACH;AACA,UAAM;AAAA,EACR;AACF;;;ACEO,IAAMA,uBAA2C,CACtD,MACA,MACA,YACA,UAAU,CAAC,MACR;AACH,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AACF;AAMO,IAAM,mBAAmB;AAAA,EAC9BA;AACF;AACA,IAAO,cAAQ;","names":["useShikiHighlighter"]}
1
+ {"version":3,"sources":["../src/bundles/web.ts","../src/web.ts"],"sourcesContent":["import {\n getSingletonHighlighter,\n type Highlighter,\n type Awaitable,\n type RegexEngine,\n} from 'shiki/bundle/web';\nimport { createOnigurumaEngine } from 'shiki/engine/oniguruma';\nimport type { ShikiLanguageRegistration } from '../lib/extended-types';\nimport type { Theme } from '../lib/types';\n\n/**\n * Creates a highlighter using the web Shiki bundle with web-focused languages.\n * Smaller than the full bundle while covering most web development needs.\n * Includes: HTML, CSS, JS, TS, JSON, Markdown, Vue, JSX, Svelte, etc.\n */\nexport async function createWebHighlighter(\n langsToLoad: ShikiLanguageRegistration,\n themesToLoad: Theme[],\n engine?: Awaitable<RegexEngine>\n): Promise<Highlighter> {\n try {\n return await getSingletonHighlighter({\n langs: [langsToLoad],\n themes: themesToLoad,\n engine: engine ?? createOnigurumaEngine(import('shiki/wasm')),\n });\n } catch (error) {\n if (error instanceof Error && error.message.includes('Language')) {\n return await getSingletonHighlighter({\n langs: ['plaintext'],\n themes: themesToLoad,\n engine: engine ?? createOnigurumaEngine(import('shiki/wasm')),\n });\n }\n throw error;\n }\n}\n","import { useShikiHighlighter as useBaseHook } from './lib/hook';\nimport { createWebHighlighter } from './bundles/web';\nimport type { UseShikiHighlighter } from './lib/types';\n\nexport { isInlineCode, rehypeInlineCodeProperty } from './lib/plugins';\n\nimport {\n createShikiHighlighterComponent,\n type ShikiHighlighterProps,\n} from './lib/component';\n\nexport type { ShikiHighlighterProps };\n\nexport type {\n UseShikiHighlighter,\n Language,\n Theme,\n Themes,\n Element,\n HighlighterOptions,\n} from './lib/types';\n\nexport {\n createJavaScriptRegexEngine,\n createJavaScriptRawEngine,\n} from 'shiki/engine/javascript';\n\n/**\n * Highlight code with shiki (web bundle)\n *\n * @param code - Code to highlight\n * @param lang - Language (bundled or custom)\n * @param theme - Theme (bundled, multi-theme, or custom)\n * @param options - react-shiki options + shiki options\n * @returns Highlighted code as React elements or HTML string\n *\n * @example\n * ```tsx\n * const highlighted = useShikiHighlighter(\n * 'const x = 1;',\n * 'typescript',\n * {\n * light: 'github-light',\n * dark: 'github-dark'\n * }\n * );\n * ```\n *\n * Web bundle (~3.8MB minified, 695KB gzipped). For other bundles: `react-shiki` or `react-shiki/core`\n */\nexport const useShikiHighlighter: UseShikiHighlighter = (\n code,\n lang,\n themeInput,\n options = {}\n) => {\n return useBaseHook(\n code,\n lang,\n themeInput,\n options,\n createWebHighlighter\n );\n};\n\n/**\n * ShikiHighlighter component using the web bundle.\n * Includes web-focused languages for balanced size and functionality.\n */\nexport const ShikiHighlighter = createShikiHighlighterComponent(\n useShikiHighlighter\n);\nexport default ShikiHighlighter;\n"],"mappings":";;;;;;;;AAAA;AAAA,EACE;AAAA,OAIK;AACP,SAAS,6BAA6B;AAStC,eAAsB,qBACpB,aACA,cACA,QACsB;AACtB,MAAI;AACF,WAAO,MAAM,wBAAwB;AAAA,MACnC,OAAO,CAAC,WAAW;AAAA,MACnB,QAAQ;AAAA,MACR,QAAQ,UAAU,sBAAsB,OAAO,YAAY,CAAC;AAAA,IAC9D,CAAC;AAAA,EACH,SAAS,OAAO;AACd,QAAI,iBAAiB,SAAS,MAAM,QAAQ,SAAS,UAAU,GAAG;AAChE,aAAO,MAAM,wBAAwB;AAAA,QACnC,OAAO,CAAC,WAAW;AAAA,QACnB,QAAQ;AAAA,QACR,QAAQ,UAAU,sBAAsB,OAAO,YAAY,CAAC;AAAA,MAC9D,CAAC;AAAA,IACH;AACA,UAAM;AAAA,EACR;AACF;;;ACdA;AAAA,EACE;AAAA,EACA;AAAA,OACK;AAyBA,IAAMA,uBAA2C,CACtD,MACA,MACA,YACA,UAAU,CAAC,MACR;AACH,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AACF;AAMO,IAAM,mBAAmB;AAAA,EAC9BA;AACF;AACA,IAAO,cAAQ;","names":["useShikiHighlighter"]}
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "react-shiki",
3
3
  "description": "Syntax highlighter component for react using shiki",
4
- "version": "0.7.4",
4
+ "version": "0.9.0",
5
5
  "license": "MIT",
6
6
  "author": {
7
7
  "name": "Bassim Shahidy",
@@ -77,7 +77,6 @@
77
77
  "@types/node": "22.17.2",
78
78
  "@types/react": "^19.1.10",
79
79
  "@vitejs/plugin-react": "^4.7.0",
80
- "benny": "^3.7.1",
81
80
  "html-react-parser": "^5.2.6",
82
81
  "jsdom": "^26.1.0",
83
82
  "tsup": "^8.5.0",
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/lib/utils.ts","../src/lib/hook.ts","../src/lib/resolvers.ts","../src/lib/transformers.ts","#style-inject:#style-inject","../src/lib/styles.css","../src/lib/component.tsx"],"sourcesContent":["import { visit } from 'unist-util-visit';\n\nimport type { TimeoutState, Element } from './types';\n\n/**\n * Rehype plugin to add an 'inline' property to <code> elements\n * Sets 'inline' property to true if the <code> is not within a <pre> tag\n *\n * Pass this plugin to the `rehypePlugins` prop of react-markdown\n * You can then access `inline` as a prop in components passed to react-markdown\n *\n * @example\n * <ReactMarkdown rehypePlugins={[rehypeInlineCodeProperty]} />\n */\nexport function rehypeInlineCodeProperty() {\n return (tree: any): undefined => {\n visit(tree, 'element', (node: Element, _index, parent: Element) => {\n if (node.tagName === 'code' && parent.tagName !== 'pre') {\n node.properties.inline = true;\n }\n });\n };\n}\n\n/**\n * Function to determine if code is inline based on the presence of line breaks\n *\n * @example\n * const isInline = node && isInlineCode(node: Element)\n */\nexport const isInlineCode = (node: Element): boolean => {\n const textContent = (node.children || [])\n .filter((child) => child.type === 'text')\n .map((child) => child.value)\n .join('');\n\n return !textContent.includes('\\n');\n};\n\n/**\n * Optionally throttles rapid sequential highlighting operations\n * Exported for testing in __tests__/throttleHighlighting.test.ts\n *\n * @example\n * const timeoutControl = useRef<TimeoutState>({\n * nextAllowedTime: 0,\n * timeoutId: undefined\n * });\n *\n * throttleHighlighting(highlightCode, timeoutControl, 1000);\n */\nexport const throttleHighlighting = (\n performHighlight: () => Promise<void>,\n timeoutControl: React.RefObject<TimeoutState>,\n throttleMs: number\n) => {\n const now = Date.now();\n clearTimeout(timeoutControl.current.timeoutId);\n\n const delay = Math.max(0, timeoutControl.current.nextAllowedTime - now);\n timeoutControl.current.timeoutId = setTimeout(() => {\n performHighlight().catch(console.error);\n timeoutControl.current.nextAllowedTime = now + throttleMs;\n }, delay);\n};\n","import {\n useEffect,\n useMemo,\n useRef,\n useState,\n type ReactNode,\n} from 'react';\n\nimport { dequal } from 'dequal/lite';\n\nimport { jsx, jsxs, Fragment } from 'react/jsx-runtime';\nimport { toJsxRuntime } from 'hast-util-to-jsx-runtime';\n\nimport type {\n CodeToHastOptions,\n CodeOptionsSingleTheme,\n CodeOptionsMultipleThemes,\n Highlighter,\n HighlighterCore,\n} from 'shiki';\n\nimport type { ShikiLanguageRegistration } from './extended-types';\n\nimport type {\n Language,\n Theme,\n HighlighterOptions,\n TimeoutState,\n Themes,\n} from './types';\n\nimport { throttleHighlighting } from './utils';\nimport { resolveLanguage, resolveTheme } from './resolvers';\nimport { lineNumbersTransformer } from './transformers';\n\nconst DEFAULT_THEMES: Themes = {\n light: 'github-light',\n dark: 'github-dark',\n};\n\n/**\n * Returns a deep-stable reference and a version counter that only changes when content changes.\n * Includes optimizations for primitive values and reference equality.\n */\nconst useStableOptions = <T>(value: T) => {\n const ref = useRef(value);\n const revision = useRef(0);\n\n // Fast-path for primitive values\n if (typeof value !== 'object' || value === null) {\n if (value !== ref.current) {\n ref.current = value;\n revision.current += 1;\n }\n return [value, revision.current] as const;\n }\n\n // Reference equality check before expensive deep comparison\n if (value !== ref.current) {\n if (!dequal(value, ref.current)) {\n ref.current = value;\n revision.current += 1;\n }\n }\n\n return [ref.current, revision.current] as const;\n};\n\n/**\n * Base hook for syntax highlighting using Shiki.\n * This is the core implementation used by all entry points.\n *\n * @param code - The code to highlight\n * @param lang - Language for highlighting\n * @param themeInput - Theme or themes to use\n * @param options - Highlighting options\n * @param createHighlighter - Factory function to create highlighter (internal use)\n */\nexport const useShikiHighlighter = (\n code: string,\n lang: Language,\n themeInput: Theme | Themes,\n createHighlighter: (\n langsToLoad: ShikiLanguageRegistration,\n themesToLoad: Theme[]\n ) => Promise<Highlighter | HighlighterCore>,\n options: HighlighterOptions = {}\n) => {\n const [highlightedCode, setHighlightedCode] =\n useState<ReactNode | null>(null);\n\n // Stabilize options, language and theme inputs to prevent unnecessary\n // re-renders or recalculations when object references change\n const [stableLang, langRev] = useStableOptions(lang);\n const [stableTheme, themeRev] = useStableOptions(themeInput);\n const [stableOpts, optsRev] = useStableOptions(options);\n\n const { languageId, langsToLoad } = useMemo(\n () =>\n resolveLanguage(\n stableLang,\n stableOpts.customLanguages,\n stableOpts.langAlias\n ),\n [stableLang, stableOpts.customLanguages, stableOpts.langAlias]\n );\n\n const { isMultiTheme, themeId, multiTheme, singleTheme, themesToLoad } =\n useMemo(() => resolveTheme(stableTheme), [stableTheme]);\n\n const timeoutControl = useRef<TimeoutState>({\n nextAllowedTime: 0,\n timeoutId: undefined,\n });\n\n const shikiOptions = useMemo<CodeToHastOptions>(() => {\n const languageOption = { lang: languageId };\n const {\n defaultColor,\n cssVariablePrefix,\n showLineNumbers,\n startingLineNumber,\n ...restOptions\n } = stableOpts;\n\n const themeOptions = isMultiTheme\n ? ({\n themes: multiTheme || DEFAULT_THEMES,\n defaultColor,\n cssVariablePrefix,\n } as CodeOptionsMultipleThemes)\n : ({\n theme: singleTheme || DEFAULT_THEMES.dark,\n } as CodeOptionsSingleTheme);\n\n const transformers = restOptions.transformers || [];\n if (showLineNumbers) {\n transformers.push(lineNumbersTransformer(startingLineNumber));\n }\n\n return {\n ...languageOption,\n ...themeOptions,\n ...restOptions,\n transformers,\n };\n }, [languageId, themeId, langRev, themeRev, optsRev]);\n\n useEffect(() => {\n let isMounted = true;\n\n const highlightCode = async () => {\n if (!languageId) return;\n\n const highlighter = stableOpts.highlighter\n ? stableOpts.highlighter\n : await createHighlighter(\n langsToLoad as ShikiLanguageRegistration,\n themesToLoad\n );\n\n const loadedLanguages = highlighter.getLoadedLanguages();\n const langToUse = loadedLanguages.includes(languageId)\n ? languageId\n : 'plaintext';\n const finalOptions = { ...shikiOptions, lang: langToUse };\n\n const hast = highlighter.codeToHast(code, finalOptions);\n\n if (isMounted) {\n setHighlightedCode(toJsxRuntime(hast, { jsx, jsxs, Fragment }));\n }\n };\n\n const { delay } = stableOpts;\n\n if (delay) {\n throttleHighlighting(highlightCode, timeoutControl, delay);\n } else {\n highlightCode().catch(console.error);\n }\n\n return () => {\n isMounted = false;\n clearTimeout(timeoutControl.current.timeoutId);\n };\n }, [\n code,\n shikiOptions,\n stableOpts.delay,\n stableOpts.highlighter,\n langsToLoad,\n themesToLoad,\n ]);\n\n return highlightedCode;\n};\n","import type { Language, Theme, Themes } from './types';\nimport type { ThemeRegistrationAny } from 'shiki/core';\nimport type { LanguageRegistration } from './extended-types';\n\n/**\n * Resolved languages and metadata\n */\ntype LanguageResult = {\n languageId: string;\n displayLanguageId: string | null;\n langsToLoad: Language;\n};\n\n/**\n * Resolves the language input to standardized IDs and objects for Shiki and UI display\n * @param lang The language input from props\n * @param customLanguages An array of custom textmate grammar objects or a single grammar object\n * @returns A LanguageResult object containing:\n * - languageId: The resolved language ID\n * - displayLanguageId: The display language ID\n * - langToLoad: The language object or string id to load\n */\nexport const resolveLanguage = (\n lang: Language,\n customLanguages?: LanguageRegistration | LanguageRegistration[],\n langAliases?: Record<string, string>\n): LanguageResult => {\n const normalizedCustomLangs = customLanguages\n ? Array.isArray(customLanguages)\n ? customLanguages\n : [customLanguages]\n : [];\n\n // Language is null or empty string\n if (lang == null || (typeof lang === 'string' && !lang.trim())) {\n return {\n languageId: 'plaintext',\n displayLanguageId: 'plaintext',\n langsToLoad: undefined,\n };\n }\n\n // Language is custom\n if (typeof lang === 'object') {\n return {\n languageId: lang.name,\n displayLanguageId: lang.name || null,\n langsToLoad: lang,\n };\n }\n\n // Language is string\n const lowerLang = lang.toLowerCase();\n const matches = (str: string | undefined): boolean =>\n str?.toLowerCase() === lowerLang;\n\n // Check if the string identifies a provided custom language\n const customMatch = normalizedCustomLangs.find(\n (cl) =>\n matches(cl.name) ||\n matches(cl.scopeName) ||\n matches(cl.scopeName?.split('.').pop()) ||\n cl.aliases?.some(matches) ||\n cl.fileTypes?.some(matches)\n );\n\n if (customMatch) {\n return {\n languageId: customMatch.name || lang,\n displayLanguageId: lang,\n langsToLoad: customMatch,\n };\n }\n\n // Check if language is aliased\n if (langAliases?.[lang]) {\n return {\n languageId: langAliases[lang],\n displayLanguageId: lang,\n langsToLoad: langAliases[lang],\n };\n }\n\n // For any other string, pass it through,\n // fallback is handled in highlighter factories\n return {\n languageId: lang,\n displayLanguageId: lang,\n langsToLoad: lang,\n };\n};\n\n/**\n * Resolved themes and metadata\n */\ninterface ThemeResult {\n isMultiTheme: boolean;\n themeId: Theme;\n multiTheme?: Themes | ThemeRegistrationAny | null;\n singleTheme?: Theme | undefined;\n themesToLoad: Theme[];\n}\n\n/**\n * Determines theme configuration and returns the resolved theme with metadata\n * @param themeInput - The theme input, either as a string name or theme object\n * @returns Object containing:\n * - isMultiTheme: If theme input is a multi-theme configuration\n * - themeId: Theme reference identifier\n * - multiTheme: The multi-theme config if it exists\n * - singleTheme: The single theme if it exists\n * - themesToLoad: The themes to load when creating the highlighter\n */\nexport function resolveTheme(themeInput: Theme | Themes): ThemeResult {\n const isTextmateTheme =\n typeof themeInput === 'object' &&\n 'tokenColors' in themeInput &&\n Array.isArray(themeInput.tokenColors);\n\n // Assume non textmate objects are multi theme configs\n const isMultiThemeConfig =\n typeof themeInput === 'object' &&\n themeInput !== null &&\n !isTextmateTheme;\n\n const validMultiThemeObj =\n typeof themeInput === 'object' &&\n themeInput !== null &&\n !isTextmateTheme &&\n Object.entries(themeInput).some(\n ([key, value]) =>\n key &&\n value &&\n key.trim() !== '' &&\n value !== '' &&\n (typeof value === 'string' || isTextmateTheme)\n );\n\n if (isMultiThemeConfig) {\n const themeId = validMultiThemeObj\n ? `multi-${Object.values(themeInput)\n .map(\n (theme) =>\n (typeof theme === 'string' ? theme : theme?.name) ||\n 'custom'\n )\n .sort()\n .join('-')}`\n : 'multi-default';\n\n // If config is invalid, return null to handle fallback in `buildShikiOptions()`\n return {\n isMultiTheme: true,\n themeId,\n multiTheme: validMultiThemeObj ? themeInput : null,\n themesToLoad: validMultiThemeObj ? Object.values(themeInput) : [],\n };\n }\n\n return {\n isMultiTheme: false,\n themeId:\n typeof themeInput === 'string'\n ? themeInput\n : themeInput?.name || 'custom',\n singleTheme: themeInput,\n themesToLoad: [themeInput],\n };\n}\n","import type { ShikiTransformer } from 'shiki/core';\n\n/**\n * Creates a transformer that enables line numbers display\n * @param startLine - The starting line number (defaults to 1)\n */\nexport function lineNumbersTransformer(startLine = 1): ShikiTransformer {\n return {\n name: 'react-shiki:line-numbers',\n code(node) {\n this.addClassToHast(node, 'has-line-numbers');\n if (startLine !== 1) {\n const existingStyle = (node.properties?.style as string) || '';\n const newStyle = existingStyle\n ? `${existingStyle}; --line-start: ${startLine}`\n : `--line-start: ${startLine}`;\n node.properties = {\n ...node.properties,\n style: newStyle,\n };\n }\n },\n line(node) {\n this.addClassToHast(node, 'line-numbers');\n return node;\n },\n };\n}\n","\n export default function styleInject(css, { insertAt } = {}) {\n if (!css || typeof document === 'undefined') return\n \n const head = document.head || document.getElementsByTagName('head')[0]\n const style = document.createElement('style')\n style.type = 'text/css'\n \n if (insertAt === 'top') {\n if (head.firstChild) {\n head.insertBefore(style, head.firstChild)\n } else {\n head.appendChild(style)\n }\n } else {\n head.appendChild(style)\n }\n \n if (style.styleSheet) {\n style.styleSheet.cssText = css\n } else {\n style.appendChild(document.createTextNode(css))\n }\n }\n ","import styleInject from '#style-inject';styleInject(\".relative {\\n position: relative;\\n}\\n.defaultStyles pre {\\n overflow: auto;\\n border-radius: 0.5rem;\\n padding-left: 1.5rem;\\n padding-right: 1.5rem;\\n padding-top: 1.25rem;\\n padding-bottom: 1.25rem;\\n}\\n.languageLabel {\\n position: absolute;\\n right: 0.75rem;\\n top: 0.5rem;\\n font-family: monospace;\\n font-size: 0.75rem;\\n letter-spacing: -0.05em;\\n color: rgba(107, 114, 128, 0.85);\\n}\\n.line-numbers::before {\\n counter-increment: line-number;\\n content: counter(line-number);\\n display: inline-flex;\\n justify-content: flex-end;\\n align-items: flex-start;\\n box-sizing: content-box;\\n min-width: var(--line-numbers-width, 2ch);\\n padding-left: var(--line-numbers-padding-left, 2ch);\\n padding-right: var(--line-numbers-padding-right, 2ch);\\n color: var(--line-numbers-foreground, rgba(107, 114, 128, 0.6));\\n font-size: var(--line-numbers-font-size, inherit);\\n font-weight: var(--line-numbers-font-weight, inherit);\\n line-height: var(--line-numbers-line-height, inherit);\\n font-family: var(--line-numbers-font-family, inherit);\\n opacity: var(--line-numbers-opacity, 1);\\n user-select: none;\\n pointer-events: none;\\n}\\n.has-line-numbers {\\n counter-reset: line-number calc(var(--line-start, 1) - 1);\\n --line-numbers-foreground: rgba(107, 114, 128, 0.5);\\n --line-numbers-width: 2ch;\\n --line-numbers-padding-left: 0ch;\\n --line-numbers-padding-right: 2ch;\\n --line-numbers-font-size: inherit;\\n --line-numbers-font-weight: inherit;\\n --line-numbers-line-height: inherit;\\n --line-numbers-font-family: inherit;\\n --line-numbers-opacity: 1;\\n}\\n\")","import './styles.css';\nimport { clsx } from 'clsx';\nimport { resolveLanguage } from './resolvers';\n\nimport type {\n HighlighterOptions,\n Language,\n Theme,\n Themes,\n} from './types';\nimport { forwardRef } from 'react';\n\n/**\n * Props for the ShikiHighlighter component\n */\nexport interface ShikiHighlighterProps extends HighlighterOptions {\n /**\n * The programming language for syntax highlighting\n * Supports custom textmate grammar objects in addition to Shiki's bundled languages\n * @see https://shiki.style/languages\n */\n language: Language;\n\n /**\n * The code to be highlighted\n */\n children: string;\n\n /**\n * The color theme or themes for syntax highlighting\n * Supports single, dual, or multiple themes\n * Supports custom textmate theme objects in addition to Shiki's bundled themes\n *\n * @example\n * theme='github-dark' // single theme\n * theme={{ light: 'github-light', dark: 'github-dark' }} // multi-theme\n *\n * @see https://shiki.style/themes\n */\n theme: Theme | Themes;\n\n /**\n * Controls the application of default styles to the generated code blocks\n *\n * Default styles include padding, overflow handling, border radius, language label styling, and font settings\n * @default true\n */\n addDefaultStyles?: boolean;\n\n /**\n * Add custom inline styles to the generated code block\n */\n style?: React.CSSProperties;\n\n /**\n * Add custom inline styles to the language label\n */\n langStyle?: React.CSSProperties;\n\n /**\n * Add custom CSS class names to the generated code block\n */\n className?: string;\n\n /**\n * Add custom CSS class names to the language label\n */\n langClassName?: string;\n\n /**\n * Whether to show the language label\n * @default true\n */\n showLanguage?: boolean;\n\n /**\n * Whether to show line numbers\n * @default false\n */\n showLineNumbers?: boolean;\n\n /**\n * Starting line number (when showLineNumbers is true)\n * @default 1\n */\n startingLineNumber?: number;\n\n /**\n * The HTML element that wraps the generated code block.\n * @default 'pre'\n */\n as?: React.ElementType;\n}\n\n/**\n * Base ShikiHighlighter component factory.\n * This creates a component that uses the provided hook implementation.\n */\nexport const createShikiHighlighterComponent = (\n useShikiHighlighterImpl: (\n code: string,\n lang: Language,\n themeInput: Theme | Themes,\n options?: HighlighterOptions\n ) => React.ReactNode\n) => {\n return forwardRef<HTMLElement, ShikiHighlighterProps>(\n (\n {\n language,\n theme,\n delay,\n transformers,\n defaultColor,\n cssVariablePrefix,\n addDefaultStyles = true,\n style,\n langStyle,\n className,\n langClassName,\n showLanguage = true,\n showLineNumbers = false,\n startingLineNumber = 1,\n children: code,\n as: Element = 'pre',\n customLanguages,\n ...shikiOptions\n },\n ref\n ) => {\n // Destructure some options for use in hook\n const options: HighlighterOptions = {\n delay,\n transformers,\n customLanguages,\n showLineNumbers,\n defaultColor,\n cssVariablePrefix,\n startingLineNumber,\n ...shikiOptions,\n };\n\n // Use resolveLanguage to get displayLanguageId directly\n const { displayLanguageId } = resolveLanguage(\n language,\n customLanguages\n );\n\n const highlightedCode = useShikiHighlighterImpl(\n code,\n language,\n theme,\n options\n );\n\n return (\n <Element\n ref={ref}\n data-testid=\"shiki-container\"\n className={clsx(\n 'relative',\n 'not-prose',\n addDefaultStyles && 'defaultStyles',\n className\n )}\n style={style}\n id=\"shiki-container\"\n >\n {showLanguage && displayLanguageId ? (\n <span\n className={clsx('languageLabel', langClassName)}\n style={langStyle}\n id=\"language-label\"\n >\n {displayLanguageId}\n </span>\n ) : null}\n {highlightedCode}\n </Element>\n );\n }\n );\n};\n"],"mappings":"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","names":["jsx","jsxs"]}