react-shiki 0.10.0 → 0.11.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/LICENSE +1 -1
- package/README.md +55 -41
- package/dist/{component-CjpuWrgO.d.mts → component-B-gutZRK.d.mts} +38 -7
- package/dist/{component-C5lsWZSq.mjs → component-DdW14SOw.mjs} +63 -20
- package/dist/component-DdW14SOw.mjs.map +1 -0
- package/dist/core.d.mts +5 -6
- package/dist/core.mjs +2 -2
- package/dist/core.mjs.map +1 -1
- package/dist/index.d.mts +5 -6
- package/dist/index.mjs +3 -5
- package/dist/index.mjs.map +1 -1
- package/dist/style.css +64 -51
- package/dist/web.d.mts +5 -6
- package/dist/web.mjs +3 -5
- package/dist/web.mjs.map +1 -1
- package/package.json +11 -11
- package/src/styles/features.css +52 -49
- package/dist/component-C5lsWZSq.mjs.map +0 -1
package/LICENSE
CHANGED
package/README.md
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
# 🎨 react-shiki
|
|
2
2
|
|
|
3
|
-
|
|
4
3
|
A performant client-side syntax highlighting component and hook for React, built with [Shiki](https://shiki.matsu.io/).
|
|
5
4
|
|
|
6
5
|
[See the demo page with highlighted code blocks showcasing several Shiki themes!](https://react-shiki.vercel.app/)
|
|
@@ -37,14 +36,14 @@ A performant client-side syntax highlighting component and hook for React, built
|
|
|
37
36
|
- [Handling Inline Code](#handling-inline-code)
|
|
38
37
|
- [Performance](#performance)
|
|
39
38
|
- [Throttling Real-time Highlighting](#throttling-real-time-highlighting)
|
|
40
|
-
- [Output
|
|
39
|
+
- [Output Formats](#output-formats)
|
|
41
40
|
<!--toc:end-->
|
|
42
41
|
|
|
43
42
|
## Features
|
|
44
43
|
|
|
45
44
|
- 🖼️ Provides both a `ShikiHighlighter` component and a `useShikiHighlighter` hook for more flexibility
|
|
46
|
-
- 🔐 Flexible output: Choose between React elements (no `dangerouslySetInnerHTML`) or HTML strings
|
|
47
|
-
- 📦 Multiple bundle options: Full bundle (~1.2MB gz), web bundle (~
|
|
45
|
+
- 🔐 Flexible output: Choose between React elements (no `dangerouslySetInnerHTML`) or HTML strings
|
|
46
|
+
- 📦 Multiple bundle options: Full bundle (~1.2MB gz), web bundle (~707KB gz), or minimal core bundle for fine-grained bundle control
|
|
48
47
|
- 🖌️ Full support for custom TextMate themes and languages
|
|
49
48
|
- 🧬 Automatic highlighting of embedded languages (e.g. TypeScript fenced inside Markdown) via Shiki's `guessEmbeddedLanguages`
|
|
50
49
|
- 🔧 Supports passing custom Shiki transformers to the highlighter, in addition to all other options supported by `codeToHast`
|
|
@@ -203,17 +202,15 @@ createJavaScriptRegexEngine({ forgiving: true });
|
|
|
203
202
|
|
|
204
203
|
See [Shiki - RegExp Engines](https://shiki.style/guide/regex-engines) for more info.
|
|
205
204
|
|
|
206
|
-
|
|
207
205
|
## Configuration
|
|
208
206
|
|
|
209
207
|
### Common Configuration Options
|
|
210
208
|
|
|
211
|
-
|
|
212
209
|
| Option | Type | Default | Description |
|
|
213
210
|
| ------------------- | ------------------ | --------------- | ----------------------------------------------------------------------------- |
|
|
214
211
|
| `code` | `string` | - | Code to highlight |
|
|
215
|
-
| `language` | `string \| object` | - | Language to highlight, built-in or custom
|
|
216
|
-
| `theme` | `string \| object` | `'github-dark'` | Single or multi-theme configuration, built-in or custom
|
|
212
|
+
| `language` | `string \| object` | - | Language to highlight, built-in or custom TextMate grammar object |
|
|
213
|
+
| `theme` | `string \| object` | `'github-dark'` | Single or multi-theme configuration, built-in or custom TextMate theme object |
|
|
217
214
|
| `delay` | `number` | `0` | Delay between highlights (in milliseconds) |
|
|
218
215
|
| `customLanguages` | `array` | `[]` | **Deprecated**: use `preloadLanguages` instead. |
|
|
219
216
|
| `preloadLanguages` | `array` | `[]` | Preload bundled language IDs and custom language grammars |
|
|
@@ -221,13 +218,14 @@ See [Shiki - RegExp Engines](https://shiki.style/guide/regex-engines) for more i
|
|
|
221
218
|
| `engine` | `RegexEngine` | Oniguruma | RegExp engine for syntax highlighting (Oniguruma, JavaScript RegExp, or JavaScript Raw) |
|
|
222
219
|
| `showLineNumbers` | `boolean` | `false` | Display line numbers alongside code |
|
|
223
220
|
| `startingLineNumber` | `number` | `1` | Starting line number when line numbers are enabled |
|
|
221
|
+
| `highlightLineNumbers` | `number[]` | - | Displayed line numbers to highlight |
|
|
224
222
|
| `transformers` | `array` | `[]` | Custom Shiki transformers for modifying the highlighting output |
|
|
225
223
|
| `cssVariablePrefix` | `string` | `'--shiki'` | Prefix for CSS variables storing theme colors |
|
|
226
224
|
| `defaultColor` | `string \| false` | `'light'` | Default theme mode when using multiple themes, can also disable default theme |
|
|
227
|
-
| `outputFormat` | `string` | `'react'` | Output format: 'react' for React nodes, 'html' for HTML string
|
|
225
|
+
| `outputFormat` | `string` | `'react'` | Output format: 'react' for React nodes, 'html' for HTML string, or 'tokens' for Shiki tokens (hook only, experimental) |
|
|
228
226
|
| `tabindex` | `number` | `0` | Tab index for the code block |
|
|
229
227
|
| `decorations` | `array` | `[]` | Custom decorations to wrap the highlighted tokens with |
|
|
230
|
-
| `structure` | `string` | `classic` | The structure of the generated HAST and HTML - `classic` or `inline` |
|
|
228
|
+
| `structure` | `string` | `'classic'` | The structure of the generated HAST and HTML - `classic` or `inline` |
|
|
231
229
|
| [`codeToHastOptions`](https://github.com/shikijs/shiki/blob/main/packages/types/src/options.ts#L121) | - | - | All other options supported by Shiki's `codeToHast` |
|
|
232
230
|
|
|
233
231
|
### Component-specific Props
|
|
@@ -246,7 +244,7 @@ The `ShikiHighlighter` component offers minimal built-in styling and customizati
|
|
|
246
244
|
|
|
247
245
|
### Embedded Language Highlighting
|
|
248
246
|
|
|
249
|
-
react-shiki uses Shiki's [`guessEmbeddedLanguages`](https://shiki.
|
|
247
|
+
react-shiki uses Shiki's [`guessEmbeddedLanguages`](https://github.com/shikijs/shiki/blob/main/packages/core/src/utils/strings.ts) to automatically detect, load, and highlight languages nested inside other languages. For example, a TypeScript fenced code block inside a Markdown document will be highlighted as TypeScript without any extra configuration. Embedded languages are dynamically loaded on demand, as long as they exist in your bundle.
|
|
250
248
|
|
|
251
249
|
### Multi-theme Support
|
|
252
250
|
|
|
@@ -337,7 +335,7 @@ For broader browser support or more control, add CSS snippets to your site to en
|
|
|
337
335
|
|
|
338
336
|
### Custom Themes
|
|
339
337
|
|
|
340
|
-
Custom themes can be passed as a TextMate theme
|
|
338
|
+
Custom themes can be passed as a TextMate theme JavaScript object ([example](https://github.com/antfu/textmate-grammars-themes/blob/main/packages/tm-themes/themes/dark-plus.json)).
|
|
341
339
|
|
|
342
340
|
```tsx
|
|
343
341
|
import tokyoNight from "../styles/tokyo-night.json";
|
|
@@ -353,7 +351,7 @@ const highlightedCode = useShikiHighlighter(code, "tsx", tokyoNight);
|
|
|
353
351
|
|
|
354
352
|
### Custom Languages
|
|
355
353
|
|
|
356
|
-
Custom languages should be passed as a TextMate grammar
|
|
354
|
+
Custom languages should be passed as a TextMate grammar JavaScript object ([example](https://github.com/shikijs/textmate-grammars-themes/blob/main/packages/tm-grammars/grammars/typescript.json)).
|
|
357
355
|
```tsx
|
|
358
356
|
import mcfunction from "../langs/mcfunction.tmLanguage.json";
|
|
359
357
|
|
|
@@ -390,7 +388,7 @@ const highlightedCode = useShikiHighlighter(code, "typescript", "github-dark", {
|
|
|
390
388
|
```
|
|
391
389
|
|
|
392
390
|
> [!NOTE]
|
|
393
|
-
> Bundled
|
|
391
|
+
> Bundled languages are loaded on demand and do not need to be preloaded.
|
|
394
392
|
|
|
395
393
|
### Language Aliases
|
|
396
394
|
|
|
@@ -430,25 +428,16 @@ const highlightedCode = useShikiHighlighter(code, "tsx", "github-dark", {
|
|
|
430
428
|
|
|
431
429
|
### Line Numbers
|
|
432
430
|
|
|
433
|
-
|
|
434
|
-
and can be customized with CSS variables:
|
|
431
|
+
Line numbers are CSS-based and can be customized with CSS variables:
|
|
435
432
|
|
|
436
433
|
```tsx
|
|
437
434
|
// Component
|
|
438
435
|
<ShikiHighlighter
|
|
439
436
|
language="javascript"
|
|
440
437
|
theme="github-dark"
|
|
441
|
-
showLineNumbers
|
|
438
|
+
showLineNumbers
|
|
442
439
|
startingLineNumber={0} // default is 1
|
|
443
|
-
|
|
444
|
-
{code}
|
|
445
|
-
</ShikiHighlighter>
|
|
446
|
-
|
|
447
|
-
<ShikiHighlighter
|
|
448
|
-
language="python"
|
|
449
|
-
theme="github-dark"
|
|
450
|
-
showLineNumbers
|
|
451
|
-
startingLineNumber={0}
|
|
440
|
+
highlightLineNumbers={[2, 4]}
|
|
452
441
|
>
|
|
453
442
|
{code}
|
|
454
443
|
</ShikiHighlighter>
|
|
@@ -456,21 +445,21 @@ and can be customized with CSS variables:
|
|
|
456
445
|
// Hook (import 'react-shiki/css' for line numbers to work)
|
|
457
446
|
const highlightedCode = useShikiHighlighter(code, "javascript", "github-dark", {
|
|
458
447
|
showLineNumbers: true,
|
|
459
|
-
startingLineNumber: 0,
|
|
448
|
+
startingLineNumber: 0,
|
|
449
|
+
highlightLineNumbers: [2, 4],
|
|
460
450
|
});
|
|
461
451
|
```
|
|
462
452
|
|
|
463
453
|
> [!NOTE]
|
|
464
|
-
> When using the hook with line numbers, import the CSS file or provide your own CSS
|
|
465
|
-
> for `.rs-line-number` (line `span`) and `.rs-has-line-numbers` (container `code` element).
|
|
454
|
+
> When using the hook with line numbers or highlighted lines, import the CSS file or provide your own CSS
|
|
455
|
+
> for `.rs-line-number` (line `span`), `.rs-highlighted-line` (line `span`), and `.rs-has-line-numbers` / `.rs-has-highlighted-lines` (container `code` element).
|
|
466
456
|
> ```tsx
|
|
467
457
|
> import 'react-shiki/css';
|
|
468
458
|
> ```
|
|
469
459
|
|
|
470
|
-
|
|
471
|
-
> Legacy `.line-numbers` / `.has-line-numbers` selectors and unprefixed line-number CSS variables are still supported for this release cycle, but are deprecated and will be removed in the next major.
|
|
460
|
+
`highlightLineNumbers` uses displayed line numbers, so it works with `startingLineNumber`.
|
|
472
461
|
|
|
473
|
-
Component-internal default classes are namespaced under `rs-*` and
|
|
462
|
+
Component-internal default classes are namespaced under `rs-*` and written with zero-specificity `:where()` selectors, so they can't be clobbered by CSS resets (e.g. Tailwind preflight) regardless of stylesheet load order, while any rule in your own CSS — even a bare element selector — overrides them.
|
|
474
463
|
|
|
475
464
|
Available CSS variables for customization:
|
|
476
465
|
```css
|
|
@@ -481,8 +470,12 @@ Available CSS variables for customization:
|
|
|
481
470
|
--rs-line-numbers-font-size: inherit;
|
|
482
471
|
--rs-line-numbers-font-weight: inherit;
|
|
483
472
|
--rs-line-numbers-opacity: 1;
|
|
473
|
+
--rs-highlighted-line-background: color-mix(in srgb, currentColor 10%, transparent);
|
|
474
|
+
--rs-highlighted-line-number-foreground: color-mix(in srgb, currentColor 65%, transparent);
|
|
484
475
|
```
|
|
485
476
|
|
|
477
|
+
With default styles, highlighted-line backgrounds automatically extend into the `pre`'s horizontal padding, reaching the container edges. When styling the `pre` yourself, highlights cover the code content area.
|
|
478
|
+
|
|
486
479
|
You can customize them in your own CSS or by using the style prop on the component:
|
|
487
480
|
```tsx
|
|
488
481
|
<ShikiHighlighter
|
|
@@ -573,9 +566,9 @@ const CodeHighlight = ({ className, children, node, ...props }) => {
|
|
|
573
566
|
**Method 2: Using the `rehypeInlineCodeProperty` plugin:**
|
|
574
567
|
|
|
575
568
|
`react-shiki` also exports `rehypeInlineCodeProperty`, a rehype plugin that
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
569
|
+
adds an `inline` prop to `react-markdown` code components. It works by checking
|
|
570
|
+
if `<code>` is nested within a `<pre>` tag; if not, it's considered inline code
|
|
571
|
+
and the `inline` prop is set to `true`.
|
|
579
572
|
|
|
580
573
|
It's passed as a `rehypePlugin` to `react-markdown`:
|
|
581
574
|
|
|
@@ -623,7 +616,7 @@ const CodeHighlight = ({
|
|
|
623
616
|
|
|
624
617
|
### Throttling Real-time Highlighting
|
|
625
618
|
|
|
626
|
-
|
|
619
|
+
To reduce highlighting work for frequently changing code:
|
|
627
620
|
|
|
628
621
|
```tsx
|
|
629
622
|
// With the component
|
|
@@ -637,11 +630,11 @@ const highlightedCode = useShikiHighlighter(code, "tsx", "github-dark", {
|
|
|
637
630
|
});
|
|
638
631
|
```
|
|
639
632
|
|
|
640
|
-
### Output
|
|
633
|
+
### Output Formats
|
|
641
634
|
|
|
642
|
-
`react-shiki`
|
|
635
|
+
`react-shiki` can return highlighted code in three formats:
|
|
643
636
|
|
|
644
|
-
**React Nodes (Default)** -
|
|
637
|
+
**React Nodes (Default)** - Rendered as React elements, no `dangerouslySetInnerHTML` required
|
|
645
638
|
```tsx
|
|
646
639
|
// Hook
|
|
647
640
|
const highlightedCode = useShikiHighlighter(code, "tsx", "github-dark");
|
|
@@ -652,7 +645,7 @@ const highlightedCode = useShikiHighlighter(code, "tsx", "github-dark");
|
|
|
652
645
|
</ShikiHighlighter>
|
|
653
646
|
```
|
|
654
647
|
|
|
655
|
-
**HTML String** -
|
|
648
|
+
**HTML String** - Rendered via `dangerouslySetInnerHTML`
|
|
656
649
|
```tsx
|
|
657
650
|
// Hook (returns HTML string, use dangerouslySetInnerHTML to render)
|
|
658
651
|
const highlightedCode = useShikiHighlighter(code, "tsx", "github-dark", {
|
|
@@ -665,7 +658,28 @@ const highlightedCode = useShikiHighlighter(code, "tsx", "github-dark", {
|
|
|
665
658
|
</ShikiHighlighter>
|
|
666
659
|
```
|
|
667
660
|
|
|
668
|
-
|
|
661
|
+
**Shiki Tokens (Experimental)** - Hook-only output for custom renderers
|
|
662
|
+
```tsx
|
|
663
|
+
const highlighted = useShikiHighlighter(code, "tsx", "github-dark", {
|
|
664
|
+
outputFormat: "tokens",
|
|
665
|
+
});
|
|
666
|
+
|
|
667
|
+
const rendered = highlighted?.tokens.map((line, i) => (
|
|
668
|
+
<div key={i}>
|
|
669
|
+
{line.map((token, j) => (
|
|
670
|
+
<span key={j} style={{ color: token.color }}>
|
|
671
|
+
{token.content}
|
|
672
|
+
</span>
|
|
673
|
+
))}
|
|
674
|
+
</div>
|
|
675
|
+
));
|
|
676
|
+
```
|
|
677
|
+
|
|
678
|
+
The React and HTML formats spend their rendering work in different phases. HTML output skips per-token React element creation and reconciliation, reducing render-phase overhead for large, one-shot highlights, but each update replaces the code block's entire DOM subtree via `innerHTML`. React output pays for element creation and diffing on every update, but commits only incremental DOM mutations, minimizing DOM churn for frequently re-highlighted code such as streaming LLM output.
|
|
679
|
+
|
|
680
|
+
HTML output hands the highlighted markup to the DOM via `dangerouslySetInnerHTML`, so only use it with trusted code sources. The default React output is the safe choice for untrusted content.
|
|
681
|
+
|
|
682
|
+
Token output is for when you need to own rendering yourself; it is intentionally available on the hook, not the `ShikiHighlighter` component, since react-shiki's markup-based features no longer apply once you control the markup. Note that Shiki's `codeToTokens` does not run transformers or decorations, so markup-producing options, including transformers, decorations, line numbers, and `structure`, have no effect on token output.
|
|
669
683
|
|
|
670
684
|
---
|
|
671
685
|
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { ReactElement } from "react";
|
|
2
|
-
import { BundledHighlighterOptions, BundledLanguage, BundledTheme, CodeOptionsMultipleThemes, CodeToHastOptions, Highlighter, HighlighterCore, LanguageRegistration, SpecialLanguage, StringLiteralUnion, ThemeRegistrationAny } from "shiki";
|
|
2
|
+
import { BundledHighlighterOptions, BundledLanguage, BundledTheme, CodeOptionsMultipleThemes, CodeToHastOptions, Highlighter, HighlighterCore, LanguageRegistration, SpecialLanguage, StringLiteralUnion, ThemeRegistrationAny, TokensResult } from "shiki";
|
|
3
3
|
|
|
4
4
|
//#region ../node_modules/.pnpm/@types+unist@3.0.3/node_modules/@types/unist/index.d.ts
|
|
5
5
|
// ## Interfaces
|
|
@@ -362,7 +362,12 @@ interface ReactShikiOptions {
|
|
|
362
362
|
/**
|
|
363
363
|
* Output format for the highlighted code.
|
|
364
364
|
* - 'react': Returns React nodes (default, safer)
|
|
365
|
-
* - 'html': Returns HTML string (
|
|
365
|
+
* - 'html': Returns HTML string (rendered via dangerouslySetInnerHTML)
|
|
366
|
+
*
|
|
367
|
+
* The hook additionally accepts 'tokens' (experimental) to return raw
|
|
368
|
+
* Shiki tokens for custom rendering. It is deliberately excluded here
|
|
369
|
+
* and only enters through the hook's generic signature, so option
|
|
370
|
+
* objects typed with this interface keep the classic return type.
|
|
366
371
|
* @default 'react'
|
|
367
372
|
*/
|
|
368
373
|
outputFormat?: 'react' | 'html';
|
|
@@ -404,6 +409,10 @@ interface ReactShikiOptions {
|
|
|
404
409
|
* @default 1
|
|
405
410
|
*/
|
|
406
411
|
startingLineNumber?: number;
|
|
412
|
+
/**
|
|
413
|
+
* Displayed line numbers to highlight
|
|
414
|
+
*/
|
|
415
|
+
highlightLineNumbers?: number[];
|
|
407
416
|
}
|
|
408
417
|
/**
|
|
409
418
|
* Configuration options for the syntax highlighter
|
|
@@ -414,10 +423,28 @@ interface HighlighterOptions extends ReactShikiOptions, Pick<CodeOptionsMultiple
|
|
|
414
423
|
* State for the throttling logic
|
|
415
424
|
*/
|
|
416
425
|
/**
|
|
417
|
-
*
|
|
426
|
+
* Supported output formats and their corresponding result shapes.
|
|
418
427
|
*/
|
|
419
|
-
type
|
|
420
|
-
|
|
428
|
+
type OutputFormat = 'react' | 'html' | 'tokens';
|
|
429
|
+
interface HighlightResultMap {
|
|
430
|
+
react: ReactElement;
|
|
431
|
+
html: string;
|
|
432
|
+
tokens: TokensResult;
|
|
433
|
+
}
|
|
434
|
+
type HighlightResult<F extends OutputFormat = 'react'> = HighlightResultMap[F] | null;
|
|
435
|
+
/**
|
|
436
|
+
* Per-call options shape: every highlighter option except `outputFormat`,
|
|
437
|
+
* plus a narrowed `outputFormat?: F` so the return type can be inferred
|
|
438
|
+
* from the format the caller actually passes.
|
|
439
|
+
*
|
|
440
|
+
* This is the only place 'tokens' is accepted. Keeping it out of
|
|
441
|
+
* `HighlighterOptions` means values typed with that interface infer
|
|
442
|
+
* `F = 'react' | 'html'` and keep their pre-tokens return type.
|
|
443
|
+
*/
|
|
444
|
+
type HighlighterOptionsFor<F extends OutputFormat> = Omit<HighlighterOptions, 'outputFormat'> & {
|
|
445
|
+
outputFormat?: F;
|
|
446
|
+
};
|
|
447
|
+
type UseShikiHighlighter = <F extends OutputFormat = 'react'>(code: string, lang: Language, themeInput: Theme | Themes, options?: HighlighterOptionsFor<F>) => HighlightResult<F>;
|
|
421
448
|
//#endregion
|
|
422
449
|
//#region src/lib/plugins.d.ts
|
|
423
450
|
/**
|
|
@@ -504,6 +531,10 @@ interface ShikiHighlighterProps extends HighlighterOptions {
|
|
|
504
531
|
* @default 1
|
|
505
532
|
*/
|
|
506
533
|
startingLineNumber?: number;
|
|
534
|
+
/**
|
|
535
|
+
* Displayed line numbers to highlight
|
|
536
|
+
*/
|
|
537
|
+
highlightLineNumbers?: number[];
|
|
507
538
|
/**
|
|
508
539
|
* The HTML element that wraps the generated code block.
|
|
509
540
|
* @default 'div'
|
|
@@ -511,5 +542,5 @@ interface ShikiHighlighterProps extends HighlighterOptions {
|
|
|
511
542
|
as?: React.ElementType;
|
|
512
543
|
}
|
|
513
544
|
//#endregion
|
|
514
|
-
export {
|
|
515
|
-
//# sourceMappingURL=component-
|
|
545
|
+
export { HighlightResult as a, Language as c, Themes as d, UseShikiHighlighter as f, Element as i, OutputFormat as l, isInlineCode as n, HighlighterOptions as o, rehypeInlineCodeProperty as r, HighlighterOptionsFor as s, ShikiHighlighterProps as t, Theme as u };
|
|
546
|
+
//# sourceMappingURL=component-B-gutZRK.d.mts.map
|
|
@@ -95,18 +95,19 @@ const resolveLanguage = (lang, customLanguages, langAliases, preloadLanguages) =
|
|
|
95
95
|
languageId = lang.name;
|
|
96
96
|
primaryLang = lang;
|
|
97
97
|
} else {
|
|
98
|
-
const
|
|
98
|
+
const normalizedLang = lang.trim();
|
|
99
|
+
const lowerLang = normalizedLang.toLowerCase();
|
|
99
100
|
const matches = (str) => str?.toLowerCase() === lowerLang;
|
|
100
101
|
const customMatch = customMatchPool.find((candidate) => typeof candidate === "object" && candidate != null && !!(matches(candidate.name) || matches(candidate.scopeName) || matches(candidate.scopeName?.split(".").pop()) || candidate.aliases?.some(matches) || candidate.fileTypes?.some(matches)));
|
|
101
102
|
if (customMatch) {
|
|
102
|
-
languageId = customMatch.name ||
|
|
103
|
+
languageId = customMatch.name || normalizedLang;
|
|
103
104
|
primaryLang = customMatch;
|
|
104
|
-
} else if (langAliases?.[
|
|
105
|
-
languageId = langAliases[
|
|
106
|
-
primaryLang = langAliases[
|
|
105
|
+
} else if (langAliases?.[normalizedLang]) {
|
|
106
|
+
languageId = langAliases[normalizedLang];
|
|
107
|
+
primaryLang = langAliases[normalizedLang];
|
|
107
108
|
} else {
|
|
108
|
-
languageId =
|
|
109
|
-
primaryLang =
|
|
109
|
+
languageId = normalizedLang;
|
|
110
|
+
primaryLang = normalizedLang;
|
|
110
111
|
}
|
|
111
112
|
}
|
|
112
113
|
return {
|
|
@@ -155,6 +156,7 @@ function resolveTheme(themeInput) {
|
|
|
155
156
|
}
|
|
156
157
|
//#endregion
|
|
157
158
|
//#region src/lib/transformers.ts
|
|
159
|
+
const toLineNumberSet = (lineNumbers) => new Set(lineNumbers.filter(Number.isInteger));
|
|
158
160
|
/**
|
|
159
161
|
* Creates a transformer that enables line numbers display
|
|
160
162
|
* @param startLine - The starting line number (defaults to 1)
|
|
@@ -163,7 +165,6 @@ function lineNumbersTransformer(startLine = 1) {
|
|
|
163
165
|
return {
|
|
164
166
|
name: "react-shiki:line-numbers",
|
|
165
167
|
code(node) {
|
|
166
|
-
this.addClassToHast(node, "has-line-numbers");
|
|
167
168
|
this.addClassToHast(node, "rs-has-line-numbers");
|
|
168
169
|
if (startLine !== 1) {
|
|
169
170
|
const existingStyle = node.properties?.style || "";
|
|
@@ -175,12 +176,30 @@ function lineNumbersTransformer(startLine = 1) {
|
|
|
175
176
|
}
|
|
176
177
|
},
|
|
177
178
|
line(node) {
|
|
178
|
-
this.addClassToHast(node, "line-numbers");
|
|
179
179
|
this.addClassToHast(node, "rs-line-number");
|
|
180
180
|
return node;
|
|
181
181
|
}
|
|
182
182
|
};
|
|
183
183
|
}
|
|
184
|
+
/**
|
|
185
|
+
* Creates a transformer that marks selected displayed line numbers.
|
|
186
|
+
* @param lineNumbers - Displayed line numbers to highlight
|
|
187
|
+
* @param startLine - The starting line number (defaults to 1)
|
|
188
|
+
*/
|
|
189
|
+
function highlightedLinesTransformer(lineNumbers, startLine = 1) {
|
|
190
|
+
const highlightedLines = toLineNumberSet(lineNumbers);
|
|
191
|
+
return {
|
|
192
|
+
name: "react-shiki:highlight-lines",
|
|
193
|
+
code(node) {
|
|
194
|
+
this.addClassToHast(node, "rs-has-highlighted-lines");
|
|
195
|
+
},
|
|
196
|
+
line(node, line) {
|
|
197
|
+
const displayedLine = startLine + line - 1;
|
|
198
|
+
if (highlightedLines.has(displayedLine)) this.addClassToHast(node, "rs-highlighted-line");
|
|
199
|
+
return node;
|
|
200
|
+
}
|
|
201
|
+
};
|
|
202
|
+
}
|
|
184
203
|
//#endregion
|
|
185
204
|
//#region src/lib/options.ts
|
|
186
205
|
const buildThemeOptions = (resolvedTheme, defaultColor, cssVariablePrefix) => {
|
|
@@ -192,9 +211,10 @@ const buildThemeOptions = (resolvedTheme, defaultColor, cssVariablePrefix) => {
|
|
|
192
211
|
return { theme: resolvedTheme.theme };
|
|
193
212
|
};
|
|
194
213
|
const buildShikiOptions = (languageId, resolvedTheme, options) => {
|
|
195
|
-
const { delay, customLanguages, preloadLanguages, outputFormat, highlighter, langAlias, engine, defaultColor, cssVariablePrefix, showLineNumbers, startingLineNumber, transformers: userTransformers, ...shikiPassthrough } = options;
|
|
214
|
+
const { delay, customLanguages, preloadLanguages, outputFormat, highlighter, langAlias, engine, defaultColor, cssVariablePrefix, showLineNumbers, startingLineNumber, highlightLineNumbers, transformers: userTransformers, ...shikiPassthrough } = options;
|
|
196
215
|
const transformers = [...userTransformers || []];
|
|
197
216
|
if (showLineNumbers) transformers.push(lineNumbersTransformer(startingLineNumber));
|
|
217
|
+
if (highlightLineNumbers?.length) transformers.push(highlightedLinesTransformer(highlightLineNumbers, startingLineNumber));
|
|
198
218
|
return {
|
|
199
219
|
lang: languageId,
|
|
200
220
|
...buildThemeOptions(resolvedTheme, defaultColor, cssVariablePrefix),
|
|
@@ -206,6 +226,7 @@ const buildShikiOptions = (languageId, resolvedTheme, options) => {
|
|
|
206
226
|
//#region src/lib/hook.ts
|
|
207
227
|
async function highlight(code, resolved, opts, factory) {
|
|
208
228
|
const { languageId, langsToLoad, themesToLoad, shikiOptions } = resolved;
|
|
229
|
+
const format = opts.outputFormat ?? "react";
|
|
209
230
|
const highlighter = opts.highlighter ?? await (async () => {
|
|
210
231
|
const hl = await factory(langsToLoad, themesToLoad, opts.engine);
|
|
211
232
|
await hl.loadLanguage(...getEmbeddedLanguages(code, languageId, hl));
|
|
@@ -216,11 +237,16 @@ async function highlight(code, resolved, opts, factory) {
|
|
|
216
237
|
...shikiOptions,
|
|
217
238
|
lang: language
|
|
218
239
|
};
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
240
|
+
const outputs = {
|
|
241
|
+
react: () => toJsxRuntime(highlighter.codeToHast(code, options), {
|
|
242
|
+
jsx,
|
|
243
|
+
jsxs,
|
|
244
|
+
Fragment
|
|
245
|
+
}),
|
|
246
|
+
html: () => highlighter.codeToHtml(code, options),
|
|
247
|
+
tokens: () => highlighter.codeToTokens(code, options)
|
|
248
|
+
};
|
|
249
|
+
return (outputs[format] ?? outputs.react)();
|
|
224
250
|
}
|
|
225
251
|
const useHighlight = (code, lang, themeInput, options = {}, highlighterFactory) => {
|
|
226
252
|
const [highlightedCode, setHighlightedCode] = useState(null);
|
|
@@ -247,12 +273,14 @@ const useHighlight = (code, lang, themeInput, options = {}, highlighterFactory)
|
|
|
247
273
|
nextAllowedTime: 0,
|
|
248
274
|
timeoutId: void 0
|
|
249
275
|
});
|
|
276
|
+
const highlighterFactoryRef = useRef(highlighterFactory);
|
|
277
|
+
highlighterFactoryRef.current = highlighterFactory;
|
|
250
278
|
useEffect(() => {
|
|
251
279
|
const requestId = ++requestIdRef.current;
|
|
252
280
|
if (!resolved.languageId) return;
|
|
253
281
|
const run = async () => {
|
|
254
282
|
try {
|
|
255
|
-
const result = await highlight(code, resolved, stableOpts,
|
|
283
|
+
const result = await highlight(code, resolved, stableOpts, highlighterFactoryRef.current);
|
|
256
284
|
if (requestId === requestIdRef.current) setHighlightedCode(result);
|
|
257
285
|
} catch (error) {
|
|
258
286
|
console.error("[react-shiki] highlight failed", error);
|
|
@@ -266,8 +294,7 @@ const useHighlight = (code, lang, themeInput, options = {}, highlighterFactory)
|
|
|
266
294
|
}, [
|
|
267
295
|
code,
|
|
268
296
|
resolved,
|
|
269
|
-
stableOpts
|
|
270
|
-
highlighterFactory
|
|
297
|
+
stableOpts
|
|
271
298
|
]);
|
|
272
299
|
return highlightedCode;
|
|
273
300
|
};
|
|
@@ -301,12 +328,26 @@ const isInlineCode = (node) => {
|
|
|
301
328
|
};
|
|
302
329
|
//#endregion
|
|
303
330
|
//#region src/lib/component.tsx
|
|
331
|
+
let warnedTokensOutputFormat = false;
|
|
332
|
+
/**
|
|
333
|
+
* The component's props exclude `outputFormat: 'tokens'`, but plain-JS
|
|
334
|
+
* callers can still pass it. Tokens are not renderable React children,
|
|
335
|
+
* so fall back to 'react' instead of crashing the render.
|
|
336
|
+
*/
|
|
337
|
+
const resolveOutputFormat = (outputFormat) => {
|
|
338
|
+
if (outputFormat !== "tokens") return outputFormat;
|
|
339
|
+
if (!warnedTokensOutputFormat) {
|
|
340
|
+
warnedTokensOutputFormat = true;
|
|
341
|
+
console.warn("[react-shiki] outputFormat 'tokens' is only supported by the useShikiHighlighter hook, falling back to 'react'");
|
|
342
|
+
}
|
|
343
|
+
return "react";
|
|
344
|
+
};
|
|
304
345
|
/**
|
|
305
346
|
* Base ShikiHighlighter component factory.
|
|
306
347
|
* This creates a component that uses the provided hook implementation.
|
|
307
348
|
*/
|
|
308
349
|
const createShikiHighlighterComponent = (useShikiHighlighterImpl) => {
|
|
309
|
-
return forwardRef(({ language, theme, delay, transformers, defaultColor, cssVariablePrefix, addDefaultStyles = true, style, langStyle, className, langClassName, showLanguage = true, showLineNumbers = false, startingLineNumber = 1, children: code, as: Element = "div", customLanguages, preloadLanguages, ...shikiOptions }, ref) => {
|
|
350
|
+
return forwardRef(({ language, theme, delay, transformers, defaultColor, cssVariablePrefix, addDefaultStyles = true, style, langStyle, className, langClassName, showLanguage = true, showLineNumbers = false, startingLineNumber = 1, highlightLineNumbers, children: code, as: Element = "div", customLanguages, preloadLanguages, outputFormat, ...shikiOptions }, ref) => {
|
|
310
351
|
const options = {
|
|
311
352
|
delay,
|
|
312
353
|
transformers,
|
|
@@ -316,6 +357,8 @@ const createShikiHighlighterComponent = (useShikiHighlighterImpl) => {
|
|
|
316
357
|
defaultColor,
|
|
317
358
|
cssVariablePrefix,
|
|
318
359
|
startingLineNumber,
|
|
360
|
+
highlightLineNumbers,
|
|
361
|
+
outputFormat: resolveOutputFormat(outputFormat),
|
|
319
362
|
...shikiOptions
|
|
320
363
|
};
|
|
321
364
|
const displayLanguageId = typeof language === "object" ? language.name || null : language?.trim() || null;
|
|
@@ -342,4 +385,4 @@ const createShikiHighlighterComponent = (useShikiHighlighterImpl) => {
|
|
|
342
385
|
//#endregion
|
|
343
386
|
export { isLoadableLanguage as a, useHighlight as i, isInlineCode as n, rehypeInlineCodeProperty as r, createShikiHighlighterComponent as t };
|
|
344
387
|
|
|
345
|
-
//# sourceMappingURL=component-
|
|
388
|
+
//# sourceMappingURL=component-DdW14SOw.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"component-DdW14SOw.mjs","names":[],"sources":["../src/lib/utils.ts","../src/lib/language.ts","../src/lib/theme.ts","../src/lib/transformers.ts","../src/lib/options.ts","../src/lib/hook.ts","../src/lib/plugins.ts","../src/lib/component.tsx"],"sourcesContent":["import { useRef } from 'react';\nimport { dequal } from 'dequal';\n\nimport type { TimeoutState } from './types';\n\n/**\n * Returns a referentially stable version of `value` that only updates when content changes.\n * Uses reference equality as a fast path and deep comparison to prevent unnecessary updates.\n */\nexport const useStableValue = <T>(value: T): T => {\n const ref = useRef(value);\n\n if (value !== ref.current && !dequal(value, ref.current)) {\n ref.current = value;\n }\n\n return ref.current;\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 clearTimeout(timeoutControl.current.timeoutId);\n\n const delay = Math.max(\n 0,\n timeoutControl.current.nextAllowedTime - Date.now()\n );\n timeoutControl.current.timeoutId = setTimeout(() => {\n performHighlight();\n timeoutControl.current.nextAllowedTime = Date.now() + throttleMs;\n }, delay);\n};\n","import type { Language } from './types';\nimport { guessEmbeddedLanguages, isSpecialLang } from 'shiki/core';\nimport type {\n DynamicImportLanguageRegistration,\n Highlighter,\n HighlighterCore,\n LanguageInput,\n LanguageRegistration,\n} from 'shiki';\n\nexport const FALLBACK_LANGUAGE = 'plaintext';\n\nexport const getEmbeddedLanguages = (\n code: string,\n languageId: string,\n highlighter: Highlighter | HighlighterCore\n): LanguageInput[] => {\n const bundled: Record<string, LanguageInput> =\n highlighter.getBundledLanguages();\n return guessEmbeddedLanguages(code, languageId).flatMap(\n (language) => bundled[language] ?? []\n );\n};\n\n/**\n * Resolved languages and metadata\n */\ntype LanguageResult = {\n languageId: string;\n langsToLoad: Language[];\n};\n\nconst toArray = <T>(value?: T | T[]): T[] =>\n value == null ? [] : Array.isArray(value) ? value : [value];\n\nconst languageKey = (lang: Language): string | null => {\n if (lang == null) return null;\n if (typeof lang === 'string') return `s:${lang}`;\n return `o:${lang.name}::${lang.scopeName}`;\n};\n\nconst dedupeLanguages = (langs: Language[]): Language[] => {\n const seen = new Set<string>();\n const deduped: Language[] = [];\n\n for (const lang of langs) {\n const key = languageKey(lang);\n if (key == null || seen.has(key)) continue;\n seen.add(key);\n deduped.push(lang);\n }\n\n return deduped;\n};\n\n/**\n * Used in factories to check if language is supported.\n * Objects are validated as grammar registrations (name + scopeName).\n */\nexport const isLoadableLanguage = <T extends string>(\n lang: Language,\n bundledLanguages: Record<T, DynamicImportLanguageRegistration>\n): lang is NonNullable<Language> => {\n if (lang == null) return false;\n if (typeof lang === 'string') return lang in bundledLanguages;\n return (\n typeof lang.name === 'string' && typeof lang.scopeName === 'string'\n );\n};\n\n/**\n * Used in hook to resolve loaded language for highlighting.\n * Falls back to \"plaintext\" if not supported.\n */\nexport const resolveLoadedLanguage = (\n languageId: string,\n loadedLanguages: string[]\n): string =>\n isSpecialLang(languageId) || loadedLanguages.includes(languageId)\n ? languageId\n : FALLBACK_LANGUAGE;\n\n/**\n * Resolves the language input to standardized IDs and objects for Shiki\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 * - langsToLoad: The language objects/string ids to load\n */\nexport const resolveLanguage = (\n lang: Language,\n customLanguages?: LanguageRegistration | LanguageRegistration[],\n langAliases?: Record<string, string>,\n preloadLanguages?: Language | Language[]\n): LanguageResult => {\n const customLangs = toArray(customLanguages);\n const preloadLangs = toArray(preloadLanguages);\n const customMatchPool = [...customLangs, ...preloadLangs];\n let languageId = FALLBACK_LANGUAGE;\n let primaryLang: Language;\n\n // Language is null or empty string\n if (lang == null || (typeof lang === 'string' && !lang.trim())) {\n return {\n languageId,\n langsToLoad: dedupeLanguages([...preloadLangs, ...customLangs]),\n };\n }\n\n // Language is custom object\n if (typeof lang === 'object') {\n languageId = lang.name;\n primaryLang = lang;\n } else {\n // Language is string\n const normalizedLang = lang.trim();\n const lowerLang = normalizedLang.toLowerCase();\n const matches = (str: string | undefined): boolean =>\n str?.toLowerCase() === lowerLang;\n\n // Check custom registrations (from both customLanguages and preloadLanguages)\n const customMatch = customMatchPool.find(\n (candidate): candidate is LanguageRegistration =>\n typeof candidate === 'object' &&\n candidate != null &&\n !!(\n matches(candidate.name) ||\n matches(candidate.scopeName) ||\n matches(candidate.scopeName?.split('.').pop()) ||\n candidate.aliases?.some(matches) ||\n candidate.fileTypes?.some(matches)\n )\n );\n\n if (customMatch) {\n languageId = customMatch.name || normalizedLang;\n primaryLang = customMatch;\n } else if (langAliases?.[normalizedLang]) {\n // Language is aliased\n languageId = langAliases[normalizedLang];\n primaryLang = langAliases[normalizedLang];\n } else {\n // For any other string, pass it through to the factory\n languageId = normalizedLang;\n primaryLang = normalizedLang;\n }\n }\n\n return {\n languageId,\n langsToLoad: dedupeLanguages([\n primaryLang,\n ...preloadLangs,\n ...customLangs,\n ]),\n };\n};\n","import type { Theme, Themes } from './types';\nimport type { ThemeRegistrationAny } from 'shiki/core';\n\nexport const DEFAULT_THEMES: Themes = {\n light: 'github-light',\n dark: 'github-dark',\n};\n\nexport type ResolvedTheme =\n | { isMulti: true; themes: Themes; themesToLoad: Theme[] }\n | { isMulti: false; theme: Theme; themesToLoad: Theme[] };\n\nconst isTextmateTheme = (\n value: unknown\n): value is ThemeRegistrationAny => {\n if (typeof value !== 'object' || value === null) return false;\n const v = value as Partial<ThemeRegistrationAny> & {\n settings?: unknown;\n };\n const hasTokens =\n Array.isArray(v.tokenColors) || Array.isArray(v.settings);\n const hasIdentity =\n typeof v.name === 'string' || typeof v.type === 'string';\n return hasTokens && hasIdentity;\n};\n\nexport function resolveTheme(themeInput: Theme | Themes): ResolvedTheme {\n if (\n typeof themeInput !== 'object' ||\n themeInput === null ||\n isTextmateTheme(themeInput)\n ) {\n return {\n isMulti: false,\n theme: themeInput,\n themesToLoad: [themeInput],\n };\n }\n\n const validEntries = Object.entries(themeInput).filter(\n ([key, value]) =>\n key.trim() !== '' &&\n ((typeof value === 'string' && value.trim() !== '') ||\n isTextmateTheme(value))\n );\n\n if (validEntries.length === 0) {\n console.warn(\n '[react-shiki] invalid multi-theme config, falling back to defaults'\n );\n return {\n isMulti: true,\n themes: DEFAULT_THEMES,\n themesToLoad: Object.values(DEFAULT_THEMES),\n };\n }\n\n if (validEntries.length !== Object.keys(themeInput).length) {\n console.warn(\n '[react-shiki] multi-theme config contained invalid entries; they were dropped'\n );\n }\n\n const themes = Object.fromEntries(validEntries) as Themes;\n return {\n isMulti: true,\n themes,\n themesToLoad: validEntries.map(([, value]) => value as Theme),\n };\n}\n","import type { ShikiTransformer } from 'shiki/core';\n\nconst toLineNumberSet = (lineNumbers: readonly number[]) =>\n new Set(lineNumbers.filter(Number.isInteger));\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, 'rs-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, 'rs-line-number');\n return node;\n },\n };\n}\n\n/**\n * Creates a transformer that marks selected displayed line numbers.\n * @param lineNumbers - Displayed line numbers to highlight\n * @param startLine - The starting line number (defaults to 1)\n */\nexport function highlightedLinesTransformer(\n lineNumbers: readonly number[],\n startLine = 1\n): ShikiTransformer {\n const highlightedLines = toLineNumberSet(lineNumbers);\n\n return {\n name: 'react-shiki:highlight-lines',\n code(node) {\n this.addClassToHast(node, 'rs-has-highlighted-lines');\n },\n line(node, line) {\n const displayedLine = startLine + line - 1;\n\n if (highlightedLines.has(displayedLine)) {\n this.addClassToHast(node, 'rs-highlighted-line');\n }\n\n return node;\n },\n };\n}\n","import type {\n BundledTheme,\n CodeOptionsMultipleThemes,\n CodeOptionsSingleTheme,\n CodeToHastOptions,\n} from 'shiki';\n\nimport type {\n HighlighterOptions,\n HighlighterOptionsFor,\n OutputFormat,\n} from './types';\nimport type { ResolvedTheme } from './theme';\nimport {\n highlightedLinesTransformer,\n lineNumbersTransformer,\n} from './transformers';\n\nconst buildThemeOptions = (\n resolvedTheme: ResolvedTheme,\n defaultColor: HighlighterOptions['defaultColor'],\n cssVariablePrefix: HighlighterOptions['cssVariablePrefix']\n):\n | CodeOptionsMultipleThemes<BundledTheme>\n | CodeOptionsSingleTheme<BundledTheme> => {\n if (resolvedTheme.isMulti) {\n return {\n themes: resolvedTheme.themes,\n defaultColor,\n cssVariablePrefix,\n };\n }\n\n return {\n theme: resolvedTheme.theme,\n };\n};\n\nexport const buildShikiOptions = (\n languageId: string,\n resolvedTheme: ResolvedTheme,\n options: HighlighterOptionsFor<OutputFormat>\n): CodeToHastOptions => {\n const {\n delay,\n customLanguages,\n preloadLanguages,\n outputFormat,\n highlighter,\n langAlias,\n engine,\n defaultColor,\n cssVariablePrefix,\n showLineNumbers,\n startingLineNumber,\n highlightLineNumbers,\n transformers: userTransformers,\n ...shikiPassthrough\n } = options;\n\n const transformers = [...(userTransformers || [])];\n if (showLineNumbers) {\n transformers.push(lineNumbersTransformer(startingLineNumber));\n }\n if (highlightLineNumbers?.length) {\n transformers.push(\n highlightedLinesTransformer(\n highlightLineNumbers,\n startingLineNumber\n )\n );\n }\n\n return {\n lang: languageId,\n ...buildThemeOptions(resolvedTheme, defaultColor, cssVariablePrefix),\n ...shikiPassthrough,\n transformers,\n };\n};\n","import { useEffect, useMemo, useRef, useState } from 'react';\nimport { jsx, jsxs, Fragment } from 'react/jsx-runtime';\nimport { toJsxRuntime } from 'hast-util-to-jsx-runtime';\nimport type { CodeToHastOptions } from 'shiki';\n\nimport type {\n HighlighterFactory,\n HighlighterOptions,\n HighlighterOptionsFor,\n HighlightResult,\n HighlightResultMap,\n Language,\n OutputFormat,\n Theme,\n Themes,\n TimeoutState,\n} from './types';\n\nimport { throttleHighlighting, useStableValue } from './utils';\nimport {\n getEmbeddedLanguages,\n resolveLanguage,\n resolveLoadedLanguage,\n} from './language';\nimport { resolveTheme } from './theme';\nimport { buildShikiOptions } from './options';\n\nexport async function highlight<F extends OutputFormat = 'react'>(\n code: string,\n resolved: {\n languageId: string;\n langsToLoad: Language[];\n themesToLoad: Theme[];\n shikiOptions: CodeToHastOptions;\n },\n opts: Pick<HighlighterOptions, 'highlighter' | 'engine'> & {\n outputFormat?: F;\n },\n factory: HighlighterFactory\n): Promise<HighlightResultMap[F]> {\n const { languageId, langsToLoad, themesToLoad, shikiOptions } =\n resolved;\n const format: OutputFormat = opts.outputFormat ?? 'react';\n\n const highlighter =\n opts.highlighter ??\n (await (async () => {\n const hl = await factory(langsToLoad, themesToLoad, opts.engine);\n await hl.loadLanguage(\n ...getEmbeddedLanguages(code, languageId, hl)\n );\n return hl;\n })());\n\n const language = resolveLoadedLanguage(\n languageId,\n highlighter.getLoadedLanguages()\n );\n const options = { ...shikiOptions, lang: language };\n\n const outputs: { [K in OutputFormat]: () => HighlightResultMap[K] } = {\n react: () =>\n toJsxRuntime(highlighter.codeToHast(code, options), {\n jsx,\n jsxs,\n Fragment,\n }),\n html: () => highlighter.codeToHtml(code, options),\n tokens: () => highlighter.codeToTokens(code, options),\n };\n\n // Untyped call sites can pass formats outside OutputFormat; degrade to\n // react rendering like the pre-tokens ternary did instead of throwing.\n return (outputs[format] ?? outputs.react)() as HighlightResultMap[F];\n}\n\nexport const useHighlight = <F extends OutputFormat = 'react'>(\n code: string,\n lang: Language,\n themeInput: Theme | Themes,\n options: HighlighterOptionsFor<F> = {},\n highlighterFactory: HighlighterFactory\n): HighlightResult<F> => {\n const [highlightedCode, setHighlightedCode] =\n useState<HighlightResult<F>>(null);\n\n const stableLang = useStableValue(lang);\n const stableTheme = useStableValue(themeInput);\n const stableOpts = useStableValue(options);\n\n const resolved = useMemo(() => {\n const { languageId, langsToLoad } = resolveLanguage(\n stableLang,\n stableOpts.customLanguages,\n stableOpts.langAlias,\n stableOpts.preloadLanguages\n );\n\n const theme = resolveTheme(stableTheme);\n\n const shikiOptions = buildShikiOptions(languageId, theme, stableOpts);\n return {\n languageId,\n langsToLoad,\n themesToLoad: theme.themesToLoad,\n shikiOptions,\n };\n }, [stableLang, stableTheme, stableOpts]);\n\n const requestIdRef = useRef(0);\n\n const timeoutControl = useRef<TimeoutState>({\n nextAllowedTime: 0,\n timeoutId: undefined,\n });\n\n // Factory is invoked imperatively; its identity is not semantic, so it must\n // not invalidate the effect on every render. See issue #161.\n const highlighterFactoryRef = useRef(highlighterFactory);\n highlighterFactoryRef.current = highlighterFactory;\n\n useEffect(() => {\n const requestId = ++requestIdRef.current;\n\n if (!resolved.languageId) return;\n\n const run = async () => {\n try {\n const result = await highlight<F>(\n code,\n resolved,\n stableOpts,\n highlighterFactoryRef.current\n );\n if (requestId === requestIdRef.current) {\n setHighlightedCode(result);\n }\n } catch (error) {\n console.error('[react-shiki] highlight failed', error);\n }\n };\n\n if (stableOpts.delay) {\n throttleHighlighting(run, timeoutControl, stableOpts.delay);\n } else {\n run();\n }\n\n return () => {\n clearTimeout(timeoutControl.current.timeoutId);\n };\n }, [code, resolved, stableOpts]);\n\n return highlightedCode;\n};\n","import { visit } from 'unist-util-visit';\nimport type { Root } from 'hast';\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: Root): undefined => {\n visit(tree, 'element', (node: Element, _index, parent) => {\n if (\n node.tagName === 'code' &&\n parent?.type === 'element' &&\n parent.tagName !== 'pre'\n ) {\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","import '../styles/component.css';\nimport '../styles/features.css';\nimport { clsx } from 'clsx';\n\nimport type {\n HighlighterOptions,\n Language,\n OutputFormat,\n Theme,\n Themes,\n UseShikiHighlighter,\n} from './types';\nimport { forwardRef } from 'react';\n\nlet warnedTokensOutputFormat = false;\n\n/**\n * The component's props exclude `outputFormat: 'tokens'`, but plain-JS\n * callers can still pass it. Tokens are not renderable React children,\n * so fall back to 'react' instead of crashing the render.\n */\nconst resolveOutputFormat = (\n outputFormat: HighlighterOptions['outputFormat']\n): HighlighterOptions['outputFormat'] => {\n if ((outputFormat as OutputFormat) !== 'tokens') return outputFormat;\n\n if (!warnedTokensOutputFormat) {\n warnedTokensOutputFormat = true;\n console.warn(\n \"[react-shiki] outputFormat 'tokens' is only supported by the useShikiHighlighter hook, falling back to 'react'\"\n );\n }\n return 'react';\n};\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 * Displayed line numbers to highlight\n */\n highlightLineNumbers?: number[];\n\n /**\n * The HTML element that wraps the generated code block.\n * @default 'div'\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 highlightLineNumbers,\n children: code,\n as: Element = 'div',\n customLanguages,\n preloadLanguages,\n outputFormat,\n ...shikiOptions\n },\n ref\n ) => {\n const options: HighlighterOptions = {\n delay,\n transformers,\n customLanguages,\n preloadLanguages,\n showLineNumbers,\n defaultColor,\n cssVariablePrefix,\n startingLineNumber,\n highlightLineNumbers,\n outputFormat: resolveOutputFormat(outputFormat),\n ...shikiOptions,\n };\n\n const displayLanguageId =\n typeof language === 'object'\n ? language.name || null\n : language?.trim() || null;\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 data-slot=\"container\"\n className={clsx(\n 'rs-root',\n 'not-prose',\n addDefaultStyles && 'rs-default-styles',\n className\n )}\n style={style}\n >\n {showLanguage && displayLanguageId ? (\n <span\n id=\"language-label\"\n data-slot=\"language-label\"\n className={clsx('rs-language-label', langClassName)}\n style={langStyle}\n >\n {displayLanguageId}\n </span>\n ) : null}\n {typeof highlightedCode === 'string' ? (\n <div\n data-slot=\"content\"\n dangerouslySetInnerHTML={{ __html: highlightedCode }}\n />\n ) : (\n highlightedCode\n )}\n </Element>\n );\n }\n );\n};\n"],"mappings":"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"}
|
package/dist/core.d.mts
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
|
-
import { a as
|
|
2
|
-
import
|
|
3
|
-
import { LanguageRegistration, createHighlighterCore } from "shiki/core";
|
|
1
|
+
import { a as HighlightResult, c as Language, d as Themes, f as UseShikiHighlighter, i as Element, l as OutputFormat, n as isInlineCode, o as HighlighterOptions, r as rehypeInlineCodeProperty, s as HighlighterOptionsFor, t as ShikiHighlighterProps, u as Theme } from "./component-B-gutZRK.mjs";
|
|
2
|
+
import { LanguageRegistration, TokensResult, createHighlighterCore } from "shiki/core";
|
|
4
3
|
import { createOnigurumaEngine } from "shiki/engine/oniguruma";
|
|
5
4
|
import { createJavaScriptRawEngine, createJavaScriptRegexEngine } from "shiki/engine/javascript";
|
|
6
5
|
|
|
@@ -12,7 +11,7 @@ import { createJavaScriptRawEngine, createJavaScriptRegexEngine } from "shiki/en
|
|
|
12
11
|
* @param lang - Language (bundled or custom)
|
|
13
12
|
* @param theme - Theme (bundled, multi-theme, or custom)
|
|
14
13
|
* @param options - react-shiki options + shiki options
|
|
15
|
-
* @returns Highlighted code as React elements
|
|
14
|
+
* @returns Highlighted code as React elements (default), HTML string, or Shiki tokens, based on `outputFormat`
|
|
16
15
|
*
|
|
17
16
|
* @example
|
|
18
17
|
* ```tsx
|
|
@@ -43,7 +42,7 @@ declare const useShikiHighlighter: UseShikiHighlighter;
|
|
|
43
42
|
* ShikiHighlighter component using a custom highlighter.
|
|
44
43
|
* Requires a highlighter to be provided.
|
|
45
44
|
*/
|
|
46
|
-
declare const ShikiHighlighter:
|
|
45
|
+
declare const ShikiHighlighter: import("react").ForwardRefExoticComponent<ShikiHighlighterProps & import("react").RefAttributes<HTMLElement>>;
|
|
47
46
|
//#endregion
|
|
48
|
-
export { type Element, type HighlighterOptions, type Language, type LanguageRegistration, ShikiHighlighter, ShikiHighlighter as default, type ShikiHighlighterProps, type Theme, type Themes, type UseShikiHighlighter, createHighlighterCore, createJavaScriptRawEngine, createJavaScriptRegexEngine, createOnigurumaEngine, isInlineCode, rehypeInlineCodeProperty, useShikiHighlighter };
|
|
47
|
+
export { type Element, type HighlightResult, type HighlighterOptions, type HighlighterOptionsFor, type Language, type LanguageRegistration, type OutputFormat, ShikiHighlighter, ShikiHighlighter as default, type ShikiHighlighterProps, type Theme, type Themes, type TokensResult, type UseShikiHighlighter, createHighlighterCore, createJavaScriptRawEngine, createJavaScriptRegexEngine, createOnigurumaEngine, isInlineCode, rehypeInlineCodeProperty, useShikiHighlighter };
|
|
49
48
|
//# sourceMappingURL=core.d.mts.map
|
package/dist/core.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { i as useHighlight, n as isInlineCode, r as rehypeInlineCodeProperty, t as createShikiHighlighterComponent } from "./component-
|
|
1
|
+
import { i as useHighlight, n as isInlineCode, r as rehypeInlineCodeProperty, t as createShikiHighlighterComponent } from "./component-DdW14SOw.mjs";
|
|
2
2
|
import { createHighlighterCore } from "shiki/core";
|
|
3
3
|
import { createOnigurumaEngine } from "shiki/engine/oniguruma";
|
|
4
4
|
import { createJavaScriptRawEngine, createJavaScriptRegexEngine } from "shiki/engine/javascript";
|
|
@@ -20,7 +20,7 @@ function validateCoreHighlighter(highlighter) {
|
|
|
20
20
|
* @param lang - Language (bundled or custom)
|
|
21
21
|
* @param theme - Theme (bundled, multi-theme, or custom)
|
|
22
22
|
* @param options - react-shiki options + shiki options
|
|
23
|
-
* @returns Highlighted code as React elements
|
|
23
|
+
* @returns Highlighted code as React elements (default), HTML string, or Shiki tokens, based on `outputFormat`
|
|
24
24
|
*
|
|
25
25
|
* @example
|
|
26
26
|
* ```tsx
|
package/dist/core.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"core.mjs","names":[],"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 { useHighlight } 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\nexport type { LanguageRegistration } from 'shiki/core';\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
|
|
1
|
+
{"version":3,"file":"core.mjs","names":[],"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 { useHighlight } 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 HighlighterOptionsFor,\n HighlightResult,\n OutputFormat,\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\nexport type { LanguageRegistration, TokensResult } from 'shiki/core';\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 (default), HTML string, or Shiki tokens, based on `outputFormat`\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 useHighlight(\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":";;;;;;;;;AAMA,SAAgB,wBACd,aACiB;CACjB,IAAI,CAAC,aACH,MAAM,IAAI,MACR,gJAEF;CAEF,OAAO;AACT;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;ACmDA,MAAa,uBACX,MACA,MACA,YACA,UAAU,CAAC,MACR;CAEH,MAAM,cAAc,wBAAwB,QAAQ,WAAW;CAE/D,OAAO,aACL,MACA,MACA,YACA;EACE,GAAG;EACH;CACF,GACA,YAAY,WACd;AACF;;;;;AAMA,MAAa,mBAAmB,gCAC9B,mBACF"}
|
package/dist/index.d.mts
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
|
-
import { a as
|
|
2
|
-
import
|
|
3
|
-
import { LanguageRegistration } from "shiki";
|
|
1
|
+
import { a as HighlightResult, c as Language, d as Themes, f as UseShikiHighlighter, i as Element, l as OutputFormat, n as isInlineCode, o as HighlighterOptions, r as rehypeInlineCodeProperty, s as HighlighterOptionsFor, t as ShikiHighlighterProps, u as Theme } from "./component-B-gutZRK.mjs";
|
|
2
|
+
import { LanguageRegistration, TokensResult } from "shiki";
|
|
4
3
|
import { createJavaScriptRawEngine, createJavaScriptRegexEngine } from "shiki/engine/javascript";
|
|
5
4
|
|
|
6
5
|
//#region src/index.d.ts
|
|
@@ -11,7 +10,7 @@ import { createJavaScriptRawEngine, createJavaScriptRegexEngine } from "shiki/en
|
|
|
11
10
|
* @param lang - Language (bundled or custom)
|
|
12
11
|
* @param theme - Theme (bundled, multi-theme, or custom)
|
|
13
12
|
* @param options - react-shiki options + shiki options
|
|
14
|
-
* @returns Highlighted code as React elements
|
|
13
|
+
* @returns Highlighted code as React elements (default), HTML string, or Shiki tokens, based on `outputFormat`
|
|
15
14
|
*
|
|
16
15
|
* @example
|
|
17
16
|
* ```tsx
|
|
@@ -32,7 +31,7 @@ declare const useShikiHighlighter: UseShikiHighlighter;
|
|
|
32
31
|
* ShikiHighlighter component using the full bundle.
|
|
33
32
|
* Includes all languages and themes for maximum compatibility.
|
|
34
33
|
*/
|
|
35
|
-
declare const ShikiHighlighter:
|
|
34
|
+
declare const ShikiHighlighter: import("react").ForwardRefExoticComponent<ShikiHighlighterProps & import("react").RefAttributes<HTMLElement>>;
|
|
36
35
|
//#endregion
|
|
37
|
-
export { type Element, type HighlighterOptions, type Language, type LanguageRegistration, ShikiHighlighter, ShikiHighlighter as default, type ShikiHighlighterProps, type Theme, type Themes, type UseShikiHighlighter, createJavaScriptRawEngine, createJavaScriptRegexEngine, isInlineCode, rehypeInlineCodeProperty, useShikiHighlighter };
|
|
36
|
+
export { type Element, type HighlightResult, type HighlighterOptions, type HighlighterOptionsFor, type Language, type LanguageRegistration, type OutputFormat, ShikiHighlighter, ShikiHighlighter as default, type ShikiHighlighterProps, type Theme, type Themes, type TokensResult, type UseShikiHighlighter, createJavaScriptRawEngine, createJavaScriptRegexEngine, isInlineCode, rehypeInlineCodeProperty, useShikiHighlighter };
|
|
38
37
|
//# sourceMappingURL=index.d.mts.map
|
package/dist/index.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { a as isLoadableLanguage, i as useHighlight, n as isInlineCode, r as rehypeInlineCodeProperty, t as createShikiHighlighterComponent } from "./component-
|
|
1
|
+
import { a as isLoadableLanguage, i as useHighlight, n as isInlineCode, r as rehypeInlineCodeProperty, t as createShikiHighlighterComponent } from "./component-DdW14SOw.mjs";
|
|
2
2
|
import { bundledLanguages, getSingletonHighlighter } from "shiki";
|
|
3
3
|
import { createOnigurumaEngine } from "shiki/engine/oniguruma";
|
|
4
4
|
import { createJavaScriptRawEngine, createJavaScriptRegexEngine } from "shiki/engine/javascript";
|
|
@@ -23,7 +23,7 @@ async function createFullHighlighter(langsToLoad, themesToLoad, engine) {
|
|
|
23
23
|
* @param lang - Language (bundled or custom)
|
|
24
24
|
* @param theme - Theme (bundled, multi-theme, or custom)
|
|
25
25
|
* @param options - react-shiki options + shiki options
|
|
26
|
-
* @returns Highlighted code as React elements
|
|
26
|
+
* @returns Highlighted code as React elements (default), HTML string, or Shiki tokens, based on `outputFormat`
|
|
27
27
|
*
|
|
28
28
|
* @example
|
|
29
29
|
* ```tsx
|
|
@@ -39,9 +39,7 @@ async function createFullHighlighter(langsToLoad, themesToLoad, engine) {
|
|
|
39
39
|
*
|
|
40
40
|
* Full bundle (~6.4MB minified, 1.2MB gzipped). For smaller bundles: `react-shiki/web` or `react-shiki/core`
|
|
41
41
|
*/
|
|
42
|
-
const useShikiHighlighter = (code, lang, themeInput, options = {}) =>
|
|
43
|
-
return useHighlight(code, lang, themeInput, options, createFullHighlighter);
|
|
44
|
-
};
|
|
42
|
+
const useShikiHighlighter = (code, lang, themeInput, options = {}) => useHighlight(code, lang, themeInput, options, createFullHighlighter);
|
|
45
43
|
/**
|
|
46
44
|
* ShikiHighlighter component using the full bundle.
|
|
47
45
|
* Includes all languages and themes for maximum compatibility.
|
package/dist/index.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.mjs","names":[],"sources":["../src/bundles/full.ts","../src/index.ts"],"sourcesContent":["import {\n getSingletonHighlighter,\n bundledLanguages,\n type Highlighter,\n type Awaitable,\n type RegexEngine,\n} from 'shiki';\nimport { createOnigurumaEngine } from 'shiki/engine/oniguruma';\nimport type { Language, Theme } from '../lib/types';\nimport { isLoadableLanguage } from '../lib/language';\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: Language[],\n themesToLoad: Theme[],\n engine?: Awaitable<RegexEngine>\n): Promise<Highlighter> {\n const langs = langsToLoad.filter((lang) =>\n isLoadableLanguage(lang, bundledLanguages)\n );\n\n return await getSingletonHighlighter({\n langs,\n themes: themesToLoad,\n engine: engine ?? createOnigurumaEngine(import('shiki/wasm')),\n });\n}\n","import { useHighlight } 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\nexport type { LanguageRegistration } from 'shiki';\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
|
|
1
|
+
{"version":3,"file":"index.mjs","names":[],"sources":["../src/bundles/full.ts","../src/index.ts"],"sourcesContent":["import {\n getSingletonHighlighter,\n bundledLanguages,\n type Highlighter,\n type Awaitable,\n type RegexEngine,\n} from 'shiki';\nimport { createOnigurumaEngine } from 'shiki/engine/oniguruma';\nimport type { Language, Theme } from '../lib/types';\nimport { isLoadableLanguage } from '../lib/language';\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: Language[],\n themesToLoad: Theme[],\n engine?: Awaitable<RegexEngine>\n): Promise<Highlighter> {\n const langs = langsToLoad.filter((lang) =>\n isLoadableLanguage(lang, bundledLanguages)\n );\n\n return await getSingletonHighlighter({\n langs,\n themes: themesToLoad,\n engine: engine ?? createOnigurumaEngine(import('shiki/wasm')),\n });\n}\n","import { useHighlight } 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 HighlighterOptionsFor,\n HighlightResult,\n OutputFormat,\n} from './lib/types';\n\nexport {\n createJavaScriptRegexEngine,\n createJavaScriptRawEngine,\n} from 'shiki/engine/javascript';\n\nexport type { LanguageRegistration, TokensResult } from 'shiki';\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 (default), HTML string, or Shiki tokens, based on `outputFormat`\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) => useHighlight(code, lang, themeInput, options, createFullHighlighter);\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":";;;;;;;;;AAeA,eAAsB,sBACpB,aACA,cACA,QACsB;CAKtB,OAAO,MAAM,wBAAwB;EACnC,OALY,YAAY,QAAQ,SAChC,mBAAmB,MAAM,gBAAgB,CAIrC;EACJ,QAAQ;EACR,QAAQ,UAAU,sBAAsB,OAAO,aAAa;CAC9D,CAAC;AACH;;;;;;;;;;;;;;;;;;;;;;;;;;AC0BA,MAAa,uBACX,MACA,MACA,YACA,UAAU,CAAC,MACR,aAAa,MAAM,MAAM,YAAY,SAAS,qBAAqB;;;;;AAMxE,MAAa,mBAAmB,gCAC9B,mBACF"}
|
package/dist/style.css
CHANGED
|
@@ -1,55 +1,68 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
1
|
+
:where(.rs-root) {
|
|
2
|
+
position: relative;
|
|
3
|
+
}
|
|
4
|
+
|
|
5
|
+
:where(.rs-root.rs-default-styles) pre {
|
|
6
|
+
border-radius: .5rem;
|
|
7
|
+
padding-block: 1.25rem;
|
|
8
|
+
padding-inline: 1.5rem;
|
|
9
|
+
overflow: auto;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
:where(.rs-root) .rs-language-label {
|
|
13
|
+
z-index: 1;
|
|
14
|
+
letter-spacing: -.05em;
|
|
15
|
+
color: #6b7280d9;
|
|
16
|
+
font-family: monospace;
|
|
17
|
+
font-size: .75rem;
|
|
18
|
+
position: absolute;
|
|
19
|
+
top: .5rem;
|
|
20
|
+
right: .75rem;
|
|
21
|
+
}
|
|
22
|
+
:where(.rs-has-line-numbers) {
|
|
23
|
+
counter-reset: line-number calc(var(--line-start, 1) - 1);
|
|
24
|
+
}
|
|
5
25
|
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
26
|
+
:where(.rs-has-line-numbers .rs-line-number):before {
|
|
27
|
+
counter-increment: line-number;
|
|
28
|
+
content: counter(line-number);
|
|
29
|
+
box-sizing: content-box;
|
|
30
|
+
min-width: var(--rs-line-numbers-width, 2ch);
|
|
31
|
+
padding-left: var(--rs-line-numbers-padding-left, 0ch);
|
|
32
|
+
padding-right: var(--rs-line-numbers-padding-right, 2ch);
|
|
33
|
+
color: var(--rs-line-numbers-foreground, #6b728080);
|
|
34
|
+
font-size: var(--rs-line-numbers-font-size, inherit);
|
|
35
|
+
font-weight: var(--rs-line-numbers-font-weight, inherit);
|
|
36
|
+
line-height: var(--rs-line-numbers-line-height, inherit);
|
|
37
|
+
font-family: var(--rs-line-numbers-font-family, inherit);
|
|
38
|
+
opacity: var(--rs-line-numbers-opacity, 1);
|
|
39
|
+
user-select: none;
|
|
40
|
+
pointer-events: none;
|
|
41
|
+
justify-content: flex-end;
|
|
42
|
+
align-items: flex-start;
|
|
43
|
+
display: inline-flex;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
:where(.rs-has-highlighted-lines) {
|
|
47
|
+
width: fit-content;
|
|
48
|
+
min-width: 100%;
|
|
49
|
+
display: block;
|
|
50
|
+
}
|
|
11
51
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
content: counter(line-number);
|
|
26
|
-
box-sizing: content-box;
|
|
27
|
-
min-width: var(--rs-line-numbers-width, 2ch);
|
|
28
|
-
padding-left: var(--rs-line-numbers-padding-left, 2ch);
|
|
29
|
-
padding-right: var(--rs-line-numbers-padding-right, 2ch);
|
|
30
|
-
color: var(--rs-line-numbers-foreground, #6b728099);
|
|
31
|
-
font-size: var(--rs-line-numbers-font-size, inherit);
|
|
32
|
-
font-weight: var(--rs-line-numbers-font-weight, inherit);
|
|
33
|
-
line-height: var(--rs-line-numbers-line-height, inherit);
|
|
34
|
-
font-family: var(--rs-line-numbers-font-family, inherit);
|
|
35
|
-
opacity: var(--rs-line-numbers-opacity, 1);
|
|
36
|
-
user-select: none;
|
|
37
|
-
pointer-events: none;
|
|
38
|
-
justify-content: flex-end;
|
|
39
|
-
align-items: flex-start;
|
|
40
|
-
display: inline-flex;
|
|
41
|
-
}
|
|
52
|
+
:where(.rs-highlighted-line) {
|
|
53
|
+
--_rs-hl-background: var(--rs-highlighted-line-background, color-mix(in srgb, currentColor 10%, transparent));
|
|
54
|
+
background-color: var(--_rs-hl-background);
|
|
55
|
+
width: 100%;
|
|
56
|
+
min-height: 1lh;
|
|
57
|
+
display: inline-block;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
:where(.rs-default-styles) .rs-highlighted-line {
|
|
61
|
+
border-image: linear-gradient(var(--_rs-hl-background) 0 0) fill 0 / 0 / 0
|
|
62
|
+
100vw;
|
|
63
|
+
background-color: #0000;
|
|
64
|
+
}
|
|
42
65
|
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
--rs-line-numbers-foreground: var(--line-numbers-foreground, #6b728080);
|
|
46
|
-
--rs-line-numbers-width: var(--line-numbers-width, 2ch);
|
|
47
|
-
--rs-line-numbers-padding-left: var(--line-numbers-padding-left, 0ch);
|
|
48
|
-
--rs-line-numbers-padding-right: var(--line-numbers-padding-right, 2ch);
|
|
49
|
-
--rs-line-numbers-font-size: var(--line-numbers-font-size, inherit);
|
|
50
|
-
--rs-line-numbers-font-weight: var(--line-numbers-font-weight, inherit);
|
|
51
|
-
--rs-line-numbers-line-height: var(--line-numbers-line-height, inherit);
|
|
52
|
-
--rs-line-numbers-font-family: var(--line-numbers-font-family, inherit);
|
|
53
|
-
--rs-line-numbers-opacity: var(--line-numbers-opacity, 1);
|
|
54
|
-
}
|
|
66
|
+
:where(.rs-has-line-numbers .rs-highlighted-line):before {
|
|
67
|
+
color: var(--rs-highlighted-line-number-foreground, color-mix(in srgb, currentColor 65%, transparent));
|
|
55
68
|
}
|
package/dist/web.d.mts
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
|
-
import { a as
|
|
2
|
-
import * as _$react from "react";
|
|
1
|
+
import { a as HighlightResult, c as Language, d as Themes, f as UseShikiHighlighter, i as Element, l as OutputFormat, n as isInlineCode, o as HighlighterOptions, r as rehypeInlineCodeProperty, s as HighlighterOptionsFor, t as ShikiHighlighterProps, u as Theme } from "./component-B-gutZRK.mjs";
|
|
3
2
|
import { createJavaScriptRawEngine, createJavaScriptRegexEngine } from "shiki/engine/javascript";
|
|
4
|
-
import { LanguageRegistration } from "shiki/bundle/web";
|
|
3
|
+
import { LanguageRegistration, TokensResult } from "shiki/bundle/web";
|
|
5
4
|
|
|
6
5
|
//#region src/web.d.ts
|
|
7
6
|
/**
|
|
@@ -11,7 +10,7 @@ import { LanguageRegistration } from "shiki/bundle/web";
|
|
|
11
10
|
* @param lang - Language (bundled or custom)
|
|
12
11
|
* @param theme - Theme (bundled, multi-theme, or custom)
|
|
13
12
|
* @param options - react-shiki options + shiki options
|
|
14
|
-
* @returns Highlighted code as React elements
|
|
13
|
+
* @returns Highlighted code as React elements (default), HTML string, or Shiki tokens, based on `outputFormat`
|
|
15
14
|
*
|
|
16
15
|
* @example
|
|
17
16
|
* ```tsx
|
|
@@ -32,7 +31,7 @@ declare const useShikiHighlighter: UseShikiHighlighter;
|
|
|
32
31
|
* ShikiHighlighter component using the web bundle.
|
|
33
32
|
* Includes web-focused languages for balanced size and functionality.
|
|
34
33
|
*/
|
|
35
|
-
declare const ShikiHighlighter:
|
|
34
|
+
declare const ShikiHighlighter: import("react").ForwardRefExoticComponent<ShikiHighlighterProps & import("react").RefAttributes<HTMLElement>>;
|
|
36
35
|
//#endregion
|
|
37
|
-
export { type Element, type HighlighterOptions, type Language, type LanguageRegistration, ShikiHighlighter, ShikiHighlighter as default, type ShikiHighlighterProps, type Theme, type Themes, type UseShikiHighlighter, createJavaScriptRawEngine, createJavaScriptRegexEngine, isInlineCode, rehypeInlineCodeProperty, useShikiHighlighter };
|
|
36
|
+
export { type Element, type HighlightResult, type HighlighterOptions, type HighlighterOptionsFor, type Language, type LanguageRegistration, type OutputFormat, ShikiHighlighter, ShikiHighlighter as default, type ShikiHighlighterProps, type Theme, type Themes, type TokensResult, type UseShikiHighlighter, createJavaScriptRawEngine, createJavaScriptRegexEngine, isInlineCode, rehypeInlineCodeProperty, useShikiHighlighter };
|
|
38
37
|
//# sourceMappingURL=web.d.mts.map
|
package/dist/web.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { a as isLoadableLanguage, i as useHighlight, n as isInlineCode, r as rehypeInlineCodeProperty, t as createShikiHighlighterComponent } from "./component-
|
|
1
|
+
import { a as isLoadableLanguage, i as useHighlight, n as isInlineCode, r as rehypeInlineCodeProperty, t as createShikiHighlighterComponent } from "./component-DdW14SOw.mjs";
|
|
2
2
|
import { createOnigurumaEngine } from "shiki/engine/oniguruma";
|
|
3
3
|
import { createJavaScriptRawEngine, createJavaScriptRegexEngine } from "shiki/engine/javascript";
|
|
4
4
|
import { bundledLanguages, getSingletonHighlighter } from "shiki/bundle/web";
|
|
@@ -24,7 +24,7 @@ async function createWebHighlighter(langsToLoad, themesToLoad, engine) {
|
|
|
24
24
|
* @param lang - Language (bundled or custom)
|
|
25
25
|
* @param theme - Theme (bundled, multi-theme, or custom)
|
|
26
26
|
* @param options - react-shiki options + shiki options
|
|
27
|
-
* @returns Highlighted code as React elements
|
|
27
|
+
* @returns Highlighted code as React elements (default), HTML string, or Shiki tokens, based on `outputFormat`
|
|
28
28
|
*
|
|
29
29
|
* @example
|
|
30
30
|
* ```tsx
|
|
@@ -40,9 +40,7 @@ async function createWebHighlighter(langsToLoad, themesToLoad, engine) {
|
|
|
40
40
|
*
|
|
41
41
|
* Web bundle (~3.8MB minified, 695KB gzipped). For other bundles: `react-shiki` or `react-shiki/core`
|
|
42
42
|
*/
|
|
43
|
-
const useShikiHighlighter = (code, lang, themeInput, options = {}) =>
|
|
44
|
-
return useHighlight(code, lang, themeInput, options, createWebHighlighter);
|
|
45
|
-
};
|
|
43
|
+
const useShikiHighlighter = (code, lang, themeInput, options = {}) => useHighlight(code, lang, themeInput, options, createWebHighlighter);
|
|
46
44
|
/**
|
|
47
45
|
* ShikiHighlighter component using the web bundle.
|
|
48
46
|
* Includes web-focused languages for balanced size and functionality.
|
package/dist/web.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"web.mjs","names":[],"sources":["../src/bundles/web.ts","../src/web.ts"],"sourcesContent":["import {\n getSingletonHighlighter,\n bundledLanguages,\n type Highlighter,\n type Awaitable,\n type RegexEngine,\n} from 'shiki/bundle/web';\nimport { createOnigurumaEngine } from 'shiki/engine/oniguruma';\nimport type { Language, Theme } from '../lib/types';\nimport { isLoadableLanguage } from '../lib/language';\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: Language[],\n themesToLoad: Theme[],\n engine?: Awaitable<RegexEngine>\n): Promise<Highlighter> {\n const langs = langsToLoad.filter((lang) =>\n isLoadableLanguage(lang, bundledLanguages)\n );\n\n return await getSingletonHighlighter({\n langs,\n themes: themesToLoad,\n engine: engine ?? createOnigurumaEngine(import('shiki/wasm')),\n });\n}\n","import { useHighlight } 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\nexport type {
|
|
1
|
+
{"version":3,"file":"web.mjs","names":[],"sources":["../src/bundles/web.ts","../src/web.ts"],"sourcesContent":["import {\n getSingletonHighlighter,\n bundledLanguages,\n type Highlighter,\n type Awaitable,\n type RegexEngine,\n} from 'shiki/bundle/web';\nimport { createOnigurumaEngine } from 'shiki/engine/oniguruma';\nimport type { Language, Theme } from '../lib/types';\nimport { isLoadableLanguage } from '../lib/language';\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: Language[],\n themesToLoad: Theme[],\n engine?: Awaitable<RegexEngine>\n): Promise<Highlighter> {\n const langs = langsToLoad.filter((lang) =>\n isLoadableLanguage(lang, bundledLanguages)\n );\n\n return await getSingletonHighlighter({\n langs,\n themes: themesToLoad,\n engine: engine ?? createOnigurumaEngine(import('shiki/wasm')),\n });\n}\n","import { useHighlight } 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 HighlighterOptionsFor,\n HighlightResult,\n OutputFormat,\n} from './lib/types';\n\nexport {\n createJavaScriptRegexEngine,\n createJavaScriptRawEngine,\n} from 'shiki/engine/javascript';\n\nexport type {\n LanguageRegistration,\n TokensResult,\n} from 'shiki/bundle/web';\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 (default), HTML string, or Shiki tokens, based on `outputFormat`\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) => useHighlight(code, lang, themeInput, options, createWebHighlighter);\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":";;;;;;;;;;AAgBA,eAAsB,qBACpB,aACA,cACA,QACsB;CAKtB,OAAO,MAAM,wBAAwB;EACnC,OALY,YAAY,QAAQ,SAChC,mBAAmB,MAAM,gBAAgB,CAIrC;EACJ,QAAQ;EACR,QAAQ,UAAU,sBAAsB,OAAO,aAAa;CAC9D,CAAC;AACH;;;;;;;;;;;;;;;;;;;;;;;;;;AC4BA,MAAa,uBACX,MACA,MACA,YACA,UAAU,CAAC,MACR,aAAa,MAAM,MAAM,YAAY,SAAS,oBAAoB;;;;;AAMvE,MAAa,mBAAmB,gCAC9B,mBACF"}
|
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.11.0",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": {
|
|
7
7
|
"name": "Bassim Shahidy",
|
|
@@ -75,24 +75,24 @@
|
|
|
75
75
|
"clsx": "^2.1.1",
|
|
76
76
|
"dequal": "^2.0.3",
|
|
77
77
|
"hast-util-to-jsx-runtime": "^2.3.6",
|
|
78
|
-
"shiki": "^4.0
|
|
78
|
+
"shiki": "^4.3.0",
|
|
79
79
|
"unist-util-visit": "^5.1.0"
|
|
80
80
|
},
|
|
81
81
|
"devDependencies": {
|
|
82
|
-
"@arethetypeswrong/core": "^0.18.
|
|
82
|
+
"@arethetypeswrong/core": "^0.18.4",
|
|
83
83
|
"@testing-library/jest-dom": "^6.9.1",
|
|
84
84
|
"@testing-library/react": "^16.3.2",
|
|
85
|
-
"@tsdown/css": "0.
|
|
85
|
+
"@tsdown/css": "0.22.3",
|
|
86
86
|
"@types/hast": "^3.0.4",
|
|
87
87
|
"@types/jest": "^30.0.0",
|
|
88
|
-
"@types/node": "25.
|
|
89
|
-
"@types/react": "^19.2.
|
|
90
|
-
"@vitejs/plugin-react": "^6.0.
|
|
91
|
-
"html-react-parser": "^6.1.
|
|
88
|
+
"@types/node": "25.9.4",
|
|
89
|
+
"@types/react": "^19.2.17",
|
|
90
|
+
"@vitejs/plugin-react": "^6.0.3",
|
|
91
|
+
"html-react-parser": "^6.1.3",
|
|
92
92
|
"jsdom": "^29.1.1",
|
|
93
|
-
"publint": "^0.3.
|
|
94
|
-
"tsdown": "^0.
|
|
95
|
-
"vitest": "^4.1.
|
|
93
|
+
"publint": "^0.3.21",
|
|
94
|
+
"tsdown": "^0.22.3",
|
|
95
|
+
"vitest": "^4.1.9"
|
|
96
96
|
},
|
|
97
97
|
"scripts": {
|
|
98
98
|
"dev": "tsdown --watch",
|
package/src/styles/features.css
CHANGED
|
@@ -1,51 +1,54 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
1
|
+
:where(.rs-has-line-numbers) {
|
|
2
|
+
counter-reset: line-number calc(var(--line-start, 1) - 1);
|
|
3
|
+
}
|
|
4
|
+
|
|
5
|
+
:where(.rs-has-line-numbers .rs-line-number)::before {
|
|
6
|
+
counter-increment: line-number;
|
|
7
|
+
content: counter(line-number);
|
|
8
|
+
display: inline-flex;
|
|
9
|
+
justify-content: flex-end;
|
|
10
|
+
align-items: flex-start;
|
|
11
|
+
box-sizing: content-box;
|
|
12
|
+
min-width: var(--rs-line-numbers-width, 2ch);
|
|
13
|
+
padding-left: var(--rs-line-numbers-padding-left, 0ch);
|
|
14
|
+
padding-right: var(--rs-line-numbers-padding-right, 2ch);
|
|
15
|
+
color: var(--rs-line-numbers-foreground, rgba(107, 114, 128, 0.5));
|
|
16
|
+
font-size: var(--rs-line-numbers-font-size, inherit);
|
|
17
|
+
font-weight: var(--rs-line-numbers-font-weight, inherit);
|
|
18
|
+
line-height: var(--rs-line-numbers-line-height, inherit);
|
|
19
|
+
font-family: var(--rs-line-numbers-font-family, inherit);
|
|
20
|
+
opacity: var(--rs-line-numbers-opacity, 1);
|
|
21
|
+
user-select: none;
|
|
22
|
+
pointer-events: none;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
:where(.rs-has-highlighted-lines) {
|
|
26
|
+
display: block;
|
|
27
|
+
width: fit-content;
|
|
28
|
+
min-width: 100%;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
:where(.rs-highlighted-line) {
|
|
32
|
+
--_rs-hl-background: var(
|
|
33
|
+
--rs-highlighted-line-background,
|
|
34
|
+
color-mix(in srgb, currentColor 10%, transparent)
|
|
35
|
+
);
|
|
36
|
+
display: inline-block;
|
|
37
|
+
width: 100%;
|
|
38
|
+
min-height: 1lh;
|
|
39
|
+
background-color: var(--_rs-hl-background);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/* Full bleed: the 100vw outset paints into the pre's padding, clipped by its overflow. */
|
|
43
|
+
:where(.rs-default-styles) .rs-highlighted-line {
|
|
44
|
+
background-color: transparent;
|
|
45
|
+
border-image: linear-gradient(var(--_rs-hl-background) 0 0) fill 0 / 0 / 0
|
|
46
|
+
100vw;
|
|
47
|
+
}
|
|
22
48
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
rgba(107, 114, 128, 0.5)
|
|
29
|
-
);
|
|
30
|
-
--rs-line-numbers-width: var(--line-numbers-width, 2ch);
|
|
31
|
-
--rs-line-numbers-padding-left: var(--line-numbers-padding-left, 0ch);
|
|
32
|
-
--rs-line-numbers-padding-right: var(
|
|
33
|
-
--line-numbers-padding-right,
|
|
34
|
-
2ch
|
|
35
|
-
);
|
|
36
|
-
--rs-line-numbers-font-size: var(--line-numbers-font-size, inherit);
|
|
37
|
-
--rs-line-numbers-font-weight: var(
|
|
38
|
-
--line-numbers-font-weight,
|
|
39
|
-
inherit
|
|
40
|
-
);
|
|
41
|
-
--rs-line-numbers-line-height: var(
|
|
42
|
-
--line-numbers-line-height,
|
|
43
|
-
inherit
|
|
44
|
-
);
|
|
45
|
-
--rs-line-numbers-font-family: var(
|
|
46
|
-
--line-numbers-font-family,
|
|
47
|
-
inherit
|
|
48
|
-
);
|
|
49
|
-
--rs-line-numbers-opacity: var(--line-numbers-opacity, 1);
|
|
50
|
-
}
|
|
49
|
+
:where(.rs-has-line-numbers .rs-highlighted-line)::before {
|
|
50
|
+
color: var(
|
|
51
|
+
--rs-highlighted-line-number-foreground,
|
|
52
|
+
color-mix(in srgb, currentColor 65%, transparent)
|
|
53
|
+
);
|
|
51
54
|
}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"component-C5lsWZSq.mjs","names":[],"sources":["../src/lib/utils.ts","../src/lib/language.ts","../src/lib/theme.ts","../src/lib/transformers.ts","../src/lib/options.ts","../src/lib/hook.ts","../src/lib/plugins.ts","../src/lib/component.tsx"],"sourcesContent":["import { useRef } from 'react';\nimport { dequal } from 'dequal';\n\nimport type { TimeoutState } from './types';\n\n/**\n * Returns a referentially stable version of `value` that only updates when content changes.\n * Uses reference equality as a fast path and deep comparison to prevent unnecessary updates.\n */\nexport const useStableValue = <T>(value: T): T => {\n const ref = useRef(value);\n\n if (value !== ref.current && !dequal(value, ref.current)) {\n ref.current = value;\n }\n\n return ref.current;\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 clearTimeout(timeoutControl.current.timeoutId);\n\n const delay = Math.max(\n 0,\n timeoutControl.current.nextAllowedTime - Date.now()\n );\n timeoutControl.current.timeoutId = setTimeout(() => {\n performHighlight();\n timeoutControl.current.nextAllowedTime = Date.now() + throttleMs;\n }, delay);\n};\n","import type { Language } from './types';\nimport { guessEmbeddedLanguages, isSpecialLang } from 'shiki/core';\nimport type {\n DynamicImportLanguageRegistration,\n Highlighter,\n HighlighterCore,\n LanguageInput,\n LanguageRegistration,\n} from 'shiki';\n\nexport const FALLBACK_LANGUAGE = 'plaintext';\n\nexport const getEmbeddedLanguages = (\n code: string,\n languageId: string,\n highlighter: Highlighter | HighlighterCore\n): LanguageInput[] => {\n const bundled: Record<string, LanguageInput> =\n highlighter.getBundledLanguages();\n return guessEmbeddedLanguages(code, languageId).flatMap(\n (language) => bundled[language] ?? []\n );\n};\n\n/**\n * Resolved languages and metadata\n */\ntype LanguageResult = {\n languageId: string;\n langsToLoad: Language[];\n};\n\nconst toArray = <T>(value?: T | T[]): T[] =>\n value == null ? [] : Array.isArray(value) ? value : [value];\n\nconst languageKey = (lang: Language): string | null => {\n if (lang == null) return null;\n if (typeof lang === 'string') return `s:${lang}`;\n return `o:${lang.name}::${lang.scopeName}`;\n};\n\nconst dedupeLanguages = (langs: Language[]): Language[] => {\n const seen = new Set<string>();\n const deduped: Language[] = [];\n\n for (const lang of langs) {\n const key = languageKey(lang);\n if (key == null || seen.has(key)) continue;\n seen.add(key);\n deduped.push(lang);\n }\n\n return deduped;\n};\n\n/**\n * Used in factories to check if language is supported.\n * Objects are validated as grammar registrations (name + scopeName).\n */\nexport const isLoadableLanguage = <T extends string>(\n lang: Language,\n bundledLanguages: Record<T, DynamicImportLanguageRegistration>\n): lang is NonNullable<Language> => {\n if (lang == null) return false;\n if (typeof lang === 'string') return lang in bundledLanguages;\n return (\n typeof lang.name === 'string' && typeof lang.scopeName === 'string'\n );\n};\n\n/**\n * Used in hook to resolve loaded language for highlighting.\n * Falls back to \"plaintext\" if not supported.\n */\nexport const resolveLoadedLanguage = (\n languageId: string,\n loadedLanguages: string[]\n): string =>\n isSpecialLang(languageId) || loadedLanguages.includes(languageId)\n ? languageId\n : FALLBACK_LANGUAGE;\n\n/**\n * Resolves the language input to standardized IDs and objects for Shiki\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 * - langsToLoad: The language objects/string ids to load\n */\nexport const resolveLanguage = (\n lang: Language,\n customLanguages?: LanguageRegistration | LanguageRegistration[],\n langAliases?: Record<string, string>,\n preloadLanguages?: Language | Language[]\n): LanguageResult => {\n const customLangs = toArray(customLanguages);\n const preloadLangs = toArray(preloadLanguages);\n const customMatchPool = [...customLangs, ...preloadLangs];\n let languageId = FALLBACK_LANGUAGE;\n let primaryLang: Language;\n\n // Language is null or empty string\n if (lang == null || (typeof lang === 'string' && !lang.trim())) {\n return {\n languageId,\n langsToLoad: dedupeLanguages([...preloadLangs, ...customLangs]),\n };\n }\n\n // Language is custom object\n if (typeof lang === 'object') {\n languageId = lang.name;\n primaryLang = lang;\n } else {\n // Language is string\n const lowerLang = lang.toLowerCase();\n const matches = (str: string | undefined): boolean =>\n str?.toLowerCase() === lowerLang;\n\n // Check custom registrations (from both customLanguages and preloadLanguages)\n const customMatch = customMatchPool.find(\n (candidate): candidate is LanguageRegistration =>\n typeof candidate === 'object' &&\n candidate != null &&\n !!(\n matches(candidate.name) ||\n matches(candidate.scopeName) ||\n matches(candidate.scopeName?.split('.').pop()) ||\n candidate.aliases?.some(matches) ||\n candidate.fileTypes?.some(matches)\n )\n );\n\n if (customMatch) {\n languageId = customMatch.name || lang;\n primaryLang = customMatch;\n } else if (langAliases?.[lang]) {\n // Language is aliased\n languageId = langAliases[lang];\n primaryLang = langAliases[lang];\n } else {\n // For any other string, pass it through to the factory\n languageId = lang;\n primaryLang = lang;\n }\n }\n\n return {\n languageId,\n langsToLoad: dedupeLanguages([\n primaryLang,\n ...preloadLangs,\n ...customLangs,\n ]),\n };\n};\n","import type { Theme, Themes } from './types';\nimport type { ThemeRegistrationAny } from 'shiki/core';\n\nexport const DEFAULT_THEMES: Themes = {\n light: 'github-light',\n dark: 'github-dark',\n};\n\nexport type ResolvedTheme =\n | { isMulti: true; themes: Themes; themesToLoad: Theme[] }\n | { isMulti: false; theme: Theme; themesToLoad: Theme[] };\n\nconst isTextmateTheme = (\n value: unknown\n): value is ThemeRegistrationAny => {\n if (typeof value !== 'object' || value === null) return false;\n const v = value as Partial<ThemeRegistrationAny> & {\n settings?: unknown;\n };\n const hasTokens =\n Array.isArray(v.tokenColors) || Array.isArray(v.settings);\n const hasIdentity =\n typeof v.name === 'string' || typeof v.type === 'string';\n return hasTokens && hasIdentity;\n};\n\nexport function resolveTheme(themeInput: Theme | Themes): ResolvedTheme {\n if (\n typeof themeInput !== 'object' ||\n themeInput === null ||\n isTextmateTheme(themeInput)\n ) {\n return {\n isMulti: false,\n theme: themeInput,\n themesToLoad: [themeInput],\n };\n }\n\n const validEntries = Object.entries(themeInput).filter(\n ([key, value]) =>\n key.trim() !== '' &&\n ((typeof value === 'string' && value.trim() !== '') ||\n isTextmateTheme(value))\n );\n\n if (validEntries.length === 0) {\n console.warn(\n '[react-shiki] invalid multi-theme config, falling back to defaults'\n );\n return {\n isMulti: true,\n themes: DEFAULT_THEMES,\n themesToLoad: Object.values(DEFAULT_THEMES),\n };\n }\n\n if (validEntries.length !== Object.keys(themeInput).length) {\n console.warn(\n '[react-shiki] multi-theme config contained invalid entries; they were dropped'\n );\n }\n\n const themes = Object.fromEntries(validEntries) as Themes;\n return {\n isMulti: true,\n themes,\n themesToLoad: validEntries.map(([, value]) => value as Theme),\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 this.addClassToHast(node, 'rs-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 this.addClassToHast(node, 'rs-line-number');\n return node;\n },\n };\n}\n","import type {\n BundledTheme,\n CodeOptionsMultipleThemes,\n CodeOptionsSingleTheme,\n CodeToHastOptions,\n} from 'shiki';\n\nimport type { HighlighterOptions } from './types';\nimport type { ResolvedTheme } from './theme';\nimport { lineNumbersTransformer } from './transformers';\n\nconst buildThemeOptions = (\n resolvedTheme: ResolvedTheme,\n defaultColor: HighlighterOptions['defaultColor'],\n cssVariablePrefix: HighlighterOptions['cssVariablePrefix']\n):\n | CodeOptionsMultipleThemes<BundledTheme>\n | CodeOptionsSingleTheme<BundledTheme> => {\n if (resolvedTheme.isMulti) {\n return {\n themes: resolvedTheme.themes,\n defaultColor,\n cssVariablePrefix,\n };\n }\n\n return {\n theme: resolvedTheme.theme,\n };\n};\n\nexport const buildShikiOptions = (\n languageId: string,\n resolvedTheme: ResolvedTheme,\n options: HighlighterOptions\n): CodeToHastOptions => {\n const {\n delay,\n customLanguages,\n preloadLanguages,\n outputFormat,\n highlighter,\n langAlias,\n engine,\n defaultColor,\n cssVariablePrefix,\n showLineNumbers,\n startingLineNumber,\n transformers: userTransformers,\n ...shikiPassthrough\n } = options;\n\n const transformers = [...(userTransformers || [])];\n if (showLineNumbers) {\n transformers.push(lineNumbersTransformer(startingLineNumber));\n }\n\n return {\n lang: languageId,\n ...buildThemeOptions(resolvedTheme, defaultColor, cssVariablePrefix),\n ...shikiPassthrough,\n transformers,\n };\n};\n","import { useEffect, useMemo, useRef, useState } from 'react';\nimport { jsx, jsxs, Fragment } from 'react/jsx-runtime';\nimport { toJsxRuntime } from 'hast-util-to-jsx-runtime';\nimport type { CodeToHastOptions } from 'shiki';\n\nimport type {\n HighlightedCode,\n HighlighterFactory,\n HighlighterOptions,\n Language,\n Theme,\n Themes,\n TimeoutState,\n} from './types';\n\nimport { throttleHighlighting, useStableValue } from './utils';\nimport {\n getEmbeddedLanguages,\n resolveLanguage,\n resolveLoadedLanguage,\n} from './language';\nimport { resolveTheme } from './theme';\nimport { buildShikiOptions } from './options';\n\nexport async function highlight(\n code: string,\n resolved: {\n languageId: string;\n langsToLoad: Language[];\n themesToLoad: Theme[];\n shikiOptions: CodeToHastOptions;\n },\n opts: Pick<\n HighlighterOptions,\n 'highlighter' | 'outputFormat' | 'engine'\n >,\n factory: HighlighterFactory\n) {\n const { languageId, langsToLoad, themesToLoad, shikiOptions } =\n resolved;\n\n const highlighter =\n opts.highlighter ??\n (await (async () => {\n const hl = await factory(langsToLoad, themesToLoad, opts.engine);\n await hl.loadLanguage(\n ...getEmbeddedLanguages(code, languageId, hl)\n );\n return hl;\n })());\n\n const language = resolveLoadedLanguage(\n languageId,\n highlighter.getLoadedLanguages()\n );\n const options = { ...shikiOptions, lang: language };\n\n return opts.outputFormat === 'html'\n ? highlighter.codeToHtml(code, options)\n : toJsxRuntime(highlighter.codeToHast(code, options), {\n jsx,\n jsxs,\n Fragment,\n });\n}\n\nexport const useHighlight = (\n code: string,\n lang: Language,\n themeInput: Theme | Themes,\n options: HighlighterOptions = {},\n highlighterFactory: HighlighterFactory\n) => {\n const [highlightedCode, setHighlightedCode] =\n useState<HighlightedCode>(null);\n\n const stableLang = useStableValue(lang);\n const stableTheme = useStableValue(themeInput);\n const stableOpts = useStableValue(options);\n\n const resolved = useMemo(() => {\n const { languageId, langsToLoad } = resolveLanguage(\n stableLang,\n stableOpts.customLanguages,\n stableOpts.langAlias,\n stableOpts.preloadLanguages\n );\n\n const theme = resolveTheme(stableTheme);\n\n const shikiOptions = buildShikiOptions(languageId, theme, stableOpts);\n return {\n languageId,\n langsToLoad,\n themesToLoad: theme.themesToLoad,\n shikiOptions,\n };\n }, [stableLang, stableTheme, stableOpts]);\n\n const requestIdRef = useRef(0);\n\n const timeoutControl = useRef<TimeoutState>({\n nextAllowedTime: 0,\n timeoutId: undefined,\n });\n\n useEffect(() => {\n const requestId = ++requestIdRef.current;\n\n if (!resolved.languageId) return;\n\n const run = async () => {\n try {\n const result = await highlight(\n code,\n resolved,\n stableOpts,\n highlighterFactory\n );\n if (requestId === requestIdRef.current) {\n setHighlightedCode(result);\n }\n } catch (error) {\n console.error('[react-shiki] highlight failed', error);\n }\n };\n\n if (stableOpts.delay) {\n throttleHighlighting(run, timeoutControl, stableOpts.delay);\n } else {\n run();\n }\n\n return () => {\n clearTimeout(timeoutControl.current.timeoutId);\n };\n }, [code, resolved, stableOpts, highlighterFactory]);\n\n return highlightedCode;\n};\n","import { visit } from 'unist-util-visit';\nimport type { Root } from 'hast';\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: Root): undefined => {\n visit(tree, 'element', (node: Element, _index, parent) => {\n if (\n node.tagName === 'code' &&\n parent?.type === 'element' &&\n parent.tagName !== 'pre'\n ) {\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","import '../styles/component.css';\nimport '../styles/features.css';\nimport { clsx } from 'clsx';\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 'div'\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 = 'div',\n customLanguages,\n preloadLanguages,\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 preloadLanguages,\n showLineNumbers,\n defaultColor,\n cssVariablePrefix,\n startingLineNumber,\n ...shikiOptions,\n };\n\n const displayLanguageId =\n typeof language === 'object'\n ? language.name || null\n : language?.trim() || null;\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 data-slot=\"container\"\n className={clsx(\n 'rs-root',\n 'not-prose',\n addDefaultStyles && 'rs-default-styles',\n className\n )}\n style={style}\n >\n {showLanguage && displayLanguageId ? (\n <span\n id=\"language-label\"\n data-slot=\"language-label\"\n className={clsx('rs-language-label', langClassName)}\n style={langStyle}\n >\n {displayLanguageId}\n </span>\n ) : null}\n {typeof highlightedCode === 'string' ? (\n <div\n data-slot=\"content\"\n dangerouslySetInnerHTML={{ __html: highlightedCode }}\n />\n ) : (\n highlightedCode\n )}\n </Element>\n );\n }\n );\n};\n"],"mappings":"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"}
|