react-shiki 0.10.1 → 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 CHANGED
@@ -1,6 +1,6 @@
1
1
  MIT License
2
2
 
3
- Copyright (c) 2024 Bassim Shahidy
3
+ Copyright (c) 2026 Bassim Shahidy
4
4
 
5
5
  Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  of this software and associated documentation files (the "Software"), to deal
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 Format Optimization](#output-format-optimization)
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 for better performance
47
- - 📦 Multiple bundle options: Full bundle (~1.2MB gz), web bundle (~695KB gz), or minimal core bundle for fine-grained bundle control
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 textmate grammer object |
216
- | `theme` | `string \| object` | `'github-dark'` | Single or multi-theme configuration, built-in or custom textmate theme object |
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.style) 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.
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 in JavaScript object. For example, [it should look like this](https://github.com/antfu/textmate-grammars-themes/blob/main/packages/tm-themes/themes/dark-plus.json).
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 in JavaScript object. For example, [it should look like this](https://github.com/shikijs/textmate-grammars-themes/blob/main/packages/tm-grammars/grammars/typescript.json)
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 language are dynamically loaded as needed, and do not need to be preloaded as long as they exist in your bundle.
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
- Display line numbers alongside your code, these are CSS-based
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). <!-- TODO: The CSS vars/classes/selectors are poorly documented -->
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
- > [!WARNING]
471
- > Legacy `.line-numbers` / `.has-line-numbers` selectors and unprefixed line-number CSS variables are removed as of 0.10.1.
460
+ `highlightLineNumbers` uses displayed line numbers, so it works with `startingLineNumber`.
472
461
 
473
- Component-internal default classes are namespaced under `rs-*` and shipped inside `@layer base` so app-level utilities can override them more predictably.
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
- provides the same API as `react-markdown` prior to `9.0.0`. It reintroduces the
577
- `inline` prop which works by checking if `<code>` is nested within a `<pre>` tag,
578
- if not, it's considered inline code and the `inline` prop is set to `true`.
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
- For improved performance when highlighting frequently changing code:
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 Format Optimization
633
+ ### Output Formats
641
634
 
642
- `react-shiki` provides two output formats to balance safety and performance:
635
+ `react-shiki` can return highlighted code in three formats:
643
636
 
644
- **React Nodes (Default)** - Safer, no `dangerouslySetInnerHTML` required
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** - 15-45% faster performance
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
- Choose HTML output when performance is critical and you trust the code source. Use the default React output when handling untrusted content or when security is the primary concern.
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 (~15-45% faster, requires dangerouslySetInnerHTML)
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
- * Public API signature for the useShikiHighlighter hook.
426
+ * Supported output formats and their corresponding result shapes.
418
427
  */
419
- type HighlightedCode = ReactElement | string | null;
420
- type UseShikiHighlighter = (code: string, lang: Language, themeInput: Theme | Themes, options?: HighlighterOptions) => HighlightedCode;
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 { HighlighterOptions as a, Themes as c, Element as i, UseShikiHighlighter as l, isInlineCode as n, Language as o, rehypeInlineCodeProperty as r, Theme as s, ShikiHighlighterProps as t };
515
- //# sourceMappingURL=component-CjpuWrgO.d.mts.map
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 lowerLang = lang.toLowerCase();
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 || lang;
103
+ languageId = customMatch.name || normalizedLang;
103
104
  primaryLang = customMatch;
104
- } else if (langAliases?.[lang]) {
105
- languageId = langAliases[lang];
106
- primaryLang = langAliases[lang];
105
+ } else if (langAliases?.[normalizedLang]) {
106
+ languageId = langAliases[normalizedLang];
107
+ primaryLang = langAliases[normalizedLang];
107
108
  } else {
108
- languageId = lang;
109
- primaryLang = lang;
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)
@@ -179,6 +181,25 @@ function lineNumbersTransformer(startLine = 1) {
179
181
  }
180
182
  };
181
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
+ }
182
203
  //#endregion
183
204
  //#region src/lib/options.ts
184
205
  const buildThemeOptions = (resolvedTheme, defaultColor, cssVariablePrefix) => {
@@ -190,9 +211,10 @@ const buildThemeOptions = (resolvedTheme, defaultColor, cssVariablePrefix) => {
190
211
  return { theme: resolvedTheme.theme };
191
212
  };
192
213
  const buildShikiOptions = (languageId, resolvedTheme, options) => {
193
- 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;
194
215
  const transformers = [...userTransformers || []];
195
216
  if (showLineNumbers) transformers.push(lineNumbersTransformer(startingLineNumber));
217
+ if (highlightLineNumbers?.length) transformers.push(highlightedLinesTransformer(highlightLineNumbers, startingLineNumber));
196
218
  return {
197
219
  lang: languageId,
198
220
  ...buildThemeOptions(resolvedTheme, defaultColor, cssVariablePrefix),
@@ -204,6 +226,7 @@ const buildShikiOptions = (languageId, resolvedTheme, options) => {
204
226
  //#region src/lib/hook.ts
205
227
  async function highlight(code, resolved, opts, factory) {
206
228
  const { languageId, langsToLoad, themesToLoad, shikiOptions } = resolved;
229
+ const format = opts.outputFormat ?? "react";
207
230
  const highlighter = opts.highlighter ?? await (async () => {
208
231
  const hl = await factory(langsToLoad, themesToLoad, opts.engine);
209
232
  await hl.loadLanguage(...getEmbeddedLanguages(code, languageId, hl));
@@ -214,11 +237,16 @@ async function highlight(code, resolved, opts, factory) {
214
237
  ...shikiOptions,
215
238
  lang: language
216
239
  };
217
- return opts.outputFormat === "html" ? highlighter.codeToHtml(code, options) : toJsxRuntime(highlighter.codeToHast(code, options), {
218
- jsx,
219
- jsxs,
220
- Fragment
221
- });
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)();
222
250
  }
223
251
  const useHighlight = (code, lang, themeInput, options = {}, highlighterFactory) => {
224
252
  const [highlightedCode, setHighlightedCode] = useState(null);
@@ -300,12 +328,26 @@ const isInlineCode = (node) => {
300
328
  };
301
329
  //#endregion
302
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
+ };
303
345
  /**
304
346
  * Base ShikiHighlighter component factory.
305
347
  * This creates a component that uses the provided hook implementation.
306
348
  */
307
349
  const createShikiHighlighterComponent = (useShikiHighlighterImpl) => {
308
- 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) => {
309
351
  const options = {
310
352
  delay,
311
353
  transformers,
@@ -315,6 +357,8 @@ const createShikiHighlighterComponent = (useShikiHighlighterImpl) => {
315
357
  defaultColor,
316
358
  cssVariablePrefix,
317
359
  startingLineNumber,
360
+ highlightLineNumbers,
361
+ outputFormat: resolveOutputFormat(outputFormat),
318
362
  ...shikiOptions
319
363
  };
320
364
  const displayLanguageId = typeof language === "object" ? language.name || null : language?.trim() || null;
@@ -341,4 +385,4 @@ const createShikiHighlighterComponent = (useShikiHighlighterImpl) => {
341
385
  //#endregion
342
386
  export { isLoadableLanguage as a, useHighlight as i, isInlineCode as n, rehypeInlineCodeProperty as r, createShikiHighlighterComponent as t };
343
387
 
344
- //# sourceMappingURL=component-CYq7SnJ0.mjs.map
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 HighlighterOptions, c as Themes, i as Element, l as UseShikiHighlighter, n as isInlineCode, o as Language, r as rehypeInlineCodeProperty, s as Theme, t as ShikiHighlighterProps } from "./component-CjpuWrgO.mjs";
2
- import * as _$react from "react";
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 or HTML string
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: _$react.ForwardRefExoticComponent<ShikiHighlighterProps & _$react.RefAttributes<HTMLElement>>;
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-CYq7SnJ0.mjs";
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 or HTML string
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 or HTML string\n *\n * @example\n * ```tsx\n * import { createHighlighterCore, createOnigurumaEngine } from 'react-shiki/core';\n *\n *\n * const highlighter = await createHighlighterCore({\n * themes: [import('@shikijs/themes/github-light'), import('@shikijs/themes/github-dark')],\n * langs: [import('@shikijs/langs/typescript')],\n * engine: createOnigurumaEngine(import('shiki/wasm'))\n * });\n *\n * const highlighted = useShikiHighlighter(\n * 'const x = 1;',\n * 'typescript',\n * {\n * light: 'github-light',\n * dark: 'github-dark'\n * },\n * { highlighter }\n * );\n * ```\n *\n * Core bundle (minimal). For plug-and-play: `react-shiki` or `react-shiki/web`\n */\nexport const useShikiHighlighter: UseShikiHighlighter = (\n code,\n lang,\n themeInput,\n options = {}\n) => {\n // Validate that highlighter is provided\n const highlighter = validateCoreHighlighter(options.highlighter);\n\n return 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;AACjB,KAAI,CAAC,YACH,OAAM,IAAI,MACR,iJAED;AAEH,QAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;ACiDT,MAAa,uBACX,MACA,MACA,YACA,UAAU,EAAE,KACT;CAEH,MAAM,cAAc,wBAAwB,QAAQ,YAAY;AAEhE,QAAO,aACL,MACA,MACA,YACA;EACE,GAAG;EACH;EACD,EACD,YAAY,YACb;;;;;;AAOH,MAAa,mBAAmB,gCAC9B,oBACD"}
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 HighlighterOptions, c as Themes, i as Element, l as UseShikiHighlighter, n as isInlineCode, o as Language, r as rehypeInlineCodeProperty, s as Theme, t as ShikiHighlighterProps } from "./component-CjpuWrgO.mjs";
2
- import * as _$react from "react";
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 or HTML string
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: _$react.ForwardRefExoticComponent<ShikiHighlighterProps & _$react.RefAttributes<HTMLElement>>;
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-CYq7SnJ0.mjs";
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 or HTML string
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.
@@ -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 or HTML string\n *\n * @example\n * ```tsx\n * const highlighted = useShikiHighlighter(\n * 'const x = 1;',\n * 'typescript',\n * {\n * light: 'github-light',\n * dark: 'github-dark'\n * }\n * );\n * ```\n *\n * Full bundle (~6.4MB minified, 1.2MB gzipped). For smaller bundles: `react-shiki/web` or `react-shiki/core`\n */\nexport const useShikiHighlighter: UseShikiHighlighter = (\n code,\n lang,\n themeInput,\n options = {}\n) => {\n return useHighlight(\n code,\n lang,\n themeInput,\n options,\n createFullHighlighter\n );\n};\n\n/**\n * ShikiHighlighter component using the full bundle.\n * Includes all languages and themes for maximum compatibility.\n */\nexport const ShikiHighlighter = createShikiHighlighterComponent(\n useShikiHighlighter\n);\nexport default ShikiHighlighter;\n"],"mappings":";;;;;;;;;AAeA,eAAsB,sBACpB,aACA,cACA,QACsB;AAKtB,QAAO,MAAM,wBAAwB;EACnC,OALY,YAAY,QAAQ,SAChC,mBAAmB,MAAM,iBAAiB,CAIrC;EACL,QAAQ;EACR,QAAQ,UAAU,sBAAsB,OAAO,cAAc;EAC9D,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;ACwBJ,MAAa,uBACX,MACA,MACA,YACA,UAAU,EAAE,KACT;AACH,QAAO,aACL,MACA,MACA,YACA,SACA,sBACD;;;;;;AAOH,MAAa,mBAAmB,gCAC9B,oBACD"}
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,46 +1,68 @@
1
- @layer base {
2
- .rs-root {
3
- position: relative;
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
+ }
4
25
 
5
- &.rs-default-styles pre {
6
- border-radius: .5rem;
7
- padding: 1.25rem 1.5rem;
8
- overflow: auto;
9
- }
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
+ }
10
51
 
11
- & .rs-language-label {
12
- letter-spacing: -.05em;
13
- color: #6b7280d9;
14
- font-family: monospace;
15
- font-size: .75rem;
16
- position: absolute;
17
- top: .5rem;
18
- right: .75rem;
19
- }
20
- }
21
- }
22
- @layer base {
23
- .rs-has-line-numbers {
24
- counter-reset: line-number calc(var(--line-start, 1) - 1);
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
+ }
25
65
 
26
- & .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
- }
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));
46
68
  }
package/dist/web.d.mts CHANGED
@@ -1,7 +1,6 @@
1
- import { a as HighlighterOptions, c as Themes, i as Element, l as UseShikiHighlighter, n as isInlineCode, o as Language, r as rehypeInlineCodeProperty, s as Theme, t as ShikiHighlighterProps } from "./component-CjpuWrgO.mjs";
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 or HTML string
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: _$react.ForwardRefExoticComponent<ShikiHighlighterProps & _$react.RefAttributes<HTMLElement>>;
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-CYq7SnJ0.mjs";
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 or HTML string
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 { LanguageRegistration } 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 or HTML string\n *\n * @example\n * ```tsx\n * const highlighted = useShikiHighlighter(\n * 'const x = 1;',\n * 'typescript',\n * {\n * light: 'github-light',\n * dark: 'github-dark'\n * }\n * );\n * ```\n *\n * Web bundle (~3.8MB minified, 695KB gzipped). For other bundles: `react-shiki` or `react-shiki/core`\n */\nexport const useShikiHighlighter: UseShikiHighlighter = (\n code,\n lang,\n themeInput,\n options = {}\n) => {\n return useHighlight(\n code,\n lang,\n themeInput,\n options,\n createWebHighlighter\n );\n};\n\n/**\n * ShikiHighlighter component using the web bundle.\n * Includes web-focused languages for balanced size and functionality.\n */\nexport const ShikiHighlighter = createShikiHighlighterComponent(\n useShikiHighlighter\n);\nexport default ShikiHighlighter;\n"],"mappings":";;;;;;;;;;AAgBA,eAAsB,qBACpB,aACA,cACA,QACsB;AAKtB,QAAO,MAAM,wBAAwB;EACnC,OALY,YAAY,QAAQ,SAChC,mBAAmB,MAAM,iBAAiB,CAIrC;EACL,QAAQ;EACR,QAAQ,UAAU,sBAAsB,OAAO,cAAc;EAC9D,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;ACuBJ,MAAa,uBACX,MACA,MACA,YACA,UAAU,EAAE,KACT;AACH,QAAO,aACL,MACA,MACA,YACA,SACA,qBACD;;;;;;AAOH,MAAa,mBAAmB,gCAC9B,oBACD"}
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.10.1",
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.2",
78
+ "shiki": "^4.3.0",
79
79
  "unist-util-visit": "^5.1.0"
80
80
  },
81
81
  "devDependencies": {
82
- "@arethetypeswrong/core": "^0.18.2",
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.21.10",
85
+ "@tsdown/css": "0.22.3",
86
86
  "@types/hast": "^3.0.4",
87
87
  "@types/jest": "^30.0.0",
88
- "@types/node": "25.6.0",
89
- "@types/react": "^19.2.14",
90
- "@vitejs/plugin-react": "^6.0.1",
91
- "html-react-parser": "^6.1.0",
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.19",
94
- "tsdown": "^0.21.10",
95
- "vitest": "^4.1.5"
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",
@@ -1,25 +1,54 @@
1
- @layer base {
2
- .rs-has-line-numbers {
3
- counter-reset: line-number calc(var(--line-start, 1) - 1);
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
+ }
4
48
 
5
- & .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
- }
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
+ );
25
54
  }
@@ -1 +0,0 @@
1
- {"version":3,"file":"component-CYq7SnJ0.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, '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","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 // 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(\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 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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"}