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 +96 -124
- package/dist/{chunk-ZUXU23SW.js → chunk-SCXX26KJ.js} +60 -51
- package/dist/chunk-SCXX26KJ.js.map +1 -0
- package/dist/{component-B4tYZaD8.d.ts → component-mMdHg8JD.d.ts} +9 -3
- package/dist/core.d.ts +23 -11
- package/dist/core.js +16 -6
- package/dist/core.js.map +1 -1
- package/dist/index.d.ts +22 -8
- package/dist/index.js +18 -7
- package/dist/index.js.map +1 -1
- package/dist/web.d.ts +22 -8
- package/dist/web.js +15 -6
- package/dist/web.js.map +1 -1
- package/package.json +1 -2
- package/dist/chunk-ZUXU23SW.js.map +0 -1
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
|
-
|
|
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
|
-
- 🔐
|
|
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,
|
|
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**:
|
|
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
|
|
136
|
-
- **Oniguruma** -
|
|
137
|
-
- **JavaScript** -
|
|
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
|
-
|
|
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
|
|
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
|
-
###
|
|
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
|
-
|
|
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
|
-
|
|
578
|
-
|
|
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
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
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
|
-
|
|
626
|
-
|
|
639
|
+
**HTML String** - 15-45% faster performance
|
|
627
640
|
```tsx
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
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
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
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
|
-
|
|
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
|
-
|
|
667
|
-
|
|
668
|
-
|
|
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 {
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
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
|
|
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 =
|
|
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
|
|
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
|
-
|
|
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-
|
|
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-
|
|
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-
|
|
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
|
-
*
|
|
12
|
-
*
|
|
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
|
-
* ```
|
|
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/
|
|
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
|
|
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
|
|
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-
|
|
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 {
|
|
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(
|
|
25
|
-
|
|
26
|
-
|
|
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/
|
|
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-
|
|
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-
|
|
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
|
-
*
|
|
10
|
-
*
|
|
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
|
-
*
|
|
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
|
|
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-
|
|
6
|
+
} from "./chunk-SCXX26KJ.js";
|
|
7
7
|
|
|
8
8
|
// src/bundles/full.ts
|
|
9
|
-
import {
|
|
10
|
-
|
|
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
|
-
|
|
34
|
-
|
|
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 {
|
|
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-
|
|
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-
|
|
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
|
-
*
|
|
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
|
-
*
|
|
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
|
-
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
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-
|
|
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
|
-
|
|
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
|
-
|
|
36
|
-
|
|
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[]
|
|
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.
|
|
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"]}
|