react-shiki 0.11.2 → 0.11.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +17 -0
- package/dist/core.d.mts +2 -5
- package/dist/core.mjs +3 -5
- package/dist/core.mjs.map +1 -1
- package/dist/{factory-DySLHBEo.mjs → factory-Ds5IsDw-.mjs} +4 -5
- package/dist/{factory-DySLHBEo.mjs.map → factory-Ds5IsDw-.mjs.map} +1 -1
- package/dist/index.d.mts +2 -5
- package/dist/index.mjs +4 -6
- package/dist/index.mjs.map +1 -1
- package/dist/{component-Cf8Topxy.mjs → plugins-C0Uf67_P.mjs} +125 -90
- package/dist/plugins-C0Uf67_P.mjs.map +1 -0
- package/dist/{component-DRPT3D6t.d.mts → plugins-DdjjMnbY.d.mts} +38 -30
- package/dist/web.d.mts +2 -5
- package/dist/web.mjs +4 -6
- package/dist/web.mjs.map +1 -1
- package/package.json +47 -51
- package/dist/component-Cf8Topxy.mjs.map +0 -1
package/README.md
CHANGED
|
@@ -306,6 +306,7 @@ The core inputs, positional arguments on the hook, props/children on the compone
|
|
|
306
306
|
| Option | Type | Default | Description |
|
|
307
307
|
| ------------------- | ------------------ | --------------- | ----------------------------------------------------------------------------- |
|
|
308
308
|
| `delay` | `number` | - | Minimum time between highlights (in milliseconds), no throttling by default |
|
|
309
|
+
| `cache` | `Map` | - | Keeps highlighted output across unmounts, see [Caching Across Remounts](#caching-across-remounts) |
|
|
309
310
|
| `engine` | `'javascript' \| 'oniguruma' \| RegexEngine` | `'oniguruma'` | RegExp engine for syntax highlighting; named engines are created and cached internally |
|
|
310
311
|
| `highlighter` | `Highlighter \| HighlighterCore` | - | Custom highlighter instance, required for the core bundle |
|
|
311
312
|
| `outputFormat` | `string` | `'react'` | Output format: 'react' for React nodes, 'html' for HTML string, or 'tokens' for Shiki tokens (hook only, experimental) |
|
|
@@ -798,6 +799,22 @@ const highlightedCode = useShikiHighlighter(code, "tsx", "github-dark", {
|
|
|
798
799
|
});
|
|
799
800
|
```
|
|
800
801
|
|
|
802
|
+
### Caching Across Remounts
|
|
803
|
+
|
|
804
|
+
Lists that remount their content, like chat transcripts when switching threads or virtualized lists while scrolling, highlight every code block again by default. Pass a `Map` as `cache` and unchanged blocks paint on their first render instead:
|
|
805
|
+
|
|
806
|
+
```tsx
|
|
807
|
+
const cache = new Map();
|
|
808
|
+
|
|
809
|
+
<ShikiHighlighter language="tsx" theme="github-dark" cache={cache}>
|
|
810
|
+
{code}
|
|
811
|
+
</ShikiHighlighter>
|
|
812
|
+
```
|
|
813
|
+
|
|
814
|
+
- A block's output is stored when it unmounts, so the partial code of a streaming block is never cached.
|
|
815
|
+
- Entries are keyed by code, language, theme and options, so blocks with different settings can share one cache. Transformers are told apart by `name`, so give blocks with different transformers of the same name their own cache.
|
|
816
|
+
- The cache is yours: keep one per app or per conversation and `clear()` it when that content goes away.
|
|
817
|
+
|
|
801
818
|
---
|
|
802
819
|
|
|
803
820
|
Made with ❤️ by [Bassim (AVGVSTVS96)](https://github.com/AVGVSTVS96)
|
package/dist/core.d.mts
CHANGED
|
@@ -1,8 +1,7 @@
|
|
|
1
|
-
import { a as EngineName, c as
|
|
1
|
+
import { a as EngineName, c as HighlighterOptions, d as OutputFormat, f as PreloadLanguage, h as UseShikiHighlighter, i as Element, l as HighlighterOptionsFor, m as Themes, n as rehypeInlineCodeProperty, o as HighlightCache, p as Theme, r as ShikiHighlighterProps, s as HighlightResult, t as isInlineCode, u as Language } from "./plugins-DdjjMnbY.mjs";
|
|
2
2
|
import { LanguageRegistration, TokensResult, createHighlighterCore } from "shiki/core";
|
|
3
3
|
import { createOnigurumaEngine } from "shiki/engine/oniguruma";
|
|
4
4
|
import { createJavaScriptRawEngine, createJavaScriptRegexEngine } from "shiki/engine/javascript";
|
|
5
|
-
//#region src/core.d.ts
|
|
6
5
|
/**
|
|
7
6
|
* Highlight code with shiki (core bundle)
|
|
8
7
|
*
|
|
@@ -42,6 +41,4 @@ export declare const useShikiHighlighter: UseShikiHighlighter;
|
|
|
42
41
|
* Requires a highlighter to be provided.
|
|
43
42
|
*/
|
|
44
43
|
export declare const ShikiHighlighter: import("react").ForwardRefExoticComponent<ShikiHighlighterProps & import("react").RefAttributes<HTMLElement>>;
|
|
45
|
-
|
|
46
|
-
export { type Element, type EngineName, type HighlightResult, type HighlighterOptions, type HighlighterOptionsFor, type Language, type LanguageRegistration, type OutputFormat, type PreloadLanguage, ShikiHighlighter as default, type ShikiHighlighterProps, type Theme, type Themes, type TokensResult, type UseShikiHighlighter, createHighlighterCore, createJavaScriptRawEngine, createJavaScriptRegexEngine, createOnigurumaEngine, isInlineCode, rehypeInlineCodeProperty };
|
|
47
|
-
//# sourceMappingURL=core.d.mts.map
|
|
44
|
+
export { type Element, type EngineName, type HighlightCache, type HighlightResult, type HighlighterOptions, type HighlighterOptionsFor, type Language, type LanguageRegistration, type OutputFormat, type PreloadLanguage, ShikiHighlighter as default, type ShikiHighlighterProps, type Theme, type Themes, type TokensResult, type UseShikiHighlighter, createHighlighterCore, createJavaScriptRawEngine, createJavaScriptRegexEngine, createOnigurumaEngine, isInlineCode, rehypeInlineCodeProperty };
|
package/dist/core.mjs
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import { i as useHighlight, n as
|
|
1
|
+
import { i as useHighlight, n as rehypeInlineCodeProperty, r as createShikiHighlighterComponent, t as isInlineCode } from "./plugins-C0Uf67_P.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";
|
|
5
|
-
|
|
5
|
+
// src/bundles/core.ts
|
|
6
6
|
/**
|
|
7
7
|
* Validates that a highlighter is provided for the core bundle.
|
|
8
8
|
* The core bundle requires users to provide their own highlighter instance.
|
|
@@ -11,8 +11,7 @@ function validateCoreHighlighter(highlighter) {
|
|
|
11
11
|
if (!highlighter) throw new Error("react-shiki/core requires a custom highlighter. Use createHighlighterCore() from react-shiki or switch to react-shiki for plug-and-play usage.");
|
|
12
12
|
return highlighter;
|
|
13
13
|
}
|
|
14
|
-
|
|
15
|
-
//#region src/core.ts
|
|
14
|
+
// src/core.ts
|
|
16
15
|
/**
|
|
17
16
|
* Highlight code with shiki (core bundle)
|
|
18
17
|
*
|
|
@@ -58,7 +57,6 @@ const useShikiHighlighter = (code, lang, themeInput, options = {}) => {
|
|
|
58
57
|
* Requires a highlighter to be provided.
|
|
59
58
|
*/
|
|
60
59
|
const ShikiHighlighter = /*#__PURE__*/ createShikiHighlighterComponent(useShikiHighlighter);
|
|
61
|
-
//#endregion
|
|
62
60
|
export { ShikiHighlighter, ShikiHighlighter as default, createHighlighterCore, createJavaScriptRawEngine, createJavaScriptRegexEngine, createOnigurumaEngine, isInlineCode, rehypeInlineCodeProperty, useShikiHighlighter };
|
|
63
61
|
|
|
64
62
|
//# sourceMappingURL=core.mjs.map
|
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';\
|
|
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\n return highlighter;\n}\n","import { useHighlight } from './lib/hook';\nimport { validateCoreHighlighter } from './bundles/core';\nimport type { UseShikiHighlighter } from './lib/types';\nimport { createShikiHighlighterComponent } from './lib/component';\n// oxlint-disable-next-line unicorn/prefer-export-from -- `export type … from` here makes tsdown fold the core entry's declarations into the shared .d.mts chunk\nimport type { ShikiHighlighterProps } from './lib/component';\n\nexport { isInlineCode, rehypeInlineCodeProperty } from './lib/plugins';\n\nexport type { ShikiHighlighterProps };\n\nexport type {\n UseShikiHighlighter,\n Language,\n PreloadLanguage,\n Theme,\n Themes,\n Element,\n EngineName,\n HighlighterOptions,\n HighlighterOptionsFor,\n HighlightCache,\n HighlightResult,\n OutputFormat,\n} from './lib/types';\n\nexport { createHighlighterCore } from 'shiki/core';\n\nexport { createOnigurumaEngine } from 'shiki/engine/oniguruma';\n\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 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 =\n // oxlint-disable-next-line eslint/no-inline-comments -- the #__PURE__ annotation lets bundlers drop the component when only the hook is used\n /*#__PURE__*/ createShikiHighlighterComponent(useShikiHighlighter);\n\nexport default ShikiHighlighter;\n"],"mappings":";;;;;;;;;AAMA,SAAgB,wBACd,aACiB;CACjB,IAAI,CAAC,aACH,MAAM,IAAI,MACR,gJAEF;CAGF,OAAO;AACT;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;ACqDA,MAAa,uBACX,MACA,MACA,YACA,UAAU,CAAC,MACR;CACH,MAAM,cAAc,wBAAwB,QAAQ,WAAW;CAE/D,OAAO,aACL,MACA,MACA,YACA;EACE,GAAG;EACH;CACF,GACA,YAAY,WACd;AACF;;;;;AAMA,MAAa,mBAEG,8CAAgC,mBAAmB"}
|
|
@@ -1,17 +1,16 @@
|
|
|
1
|
-
import { a as isLoadableLanguage } from "./
|
|
1
|
+
import { a as isLoadableLanguage } from "./plugins-C0Uf67_P.mjs";
|
|
2
2
|
import { createOnigurumaEngine } from "shiki/engine/oniguruma";
|
|
3
|
-
|
|
3
|
+
// src/bundles/factory.ts
|
|
4
4
|
/**
|
|
5
5
|
* Builds a highlighter factory for a Shiki bundle (full or web).
|
|
6
6
|
* Unloadable languages are filtered out so unknown ids fall back to
|
|
7
7
|
* plaintext instead of throwing.
|
|
8
8
|
*/
|
|
9
|
-
const bundledHighlighterFactory = (getSingletonHighlighter, bundledLanguages) => (langsToLoad, themesToLoad, engine) => getSingletonHighlighter({
|
|
9
|
+
const bundledHighlighterFactory = (getSingletonHighlighter, bundledLanguages) => async (langsToLoad, themesToLoad, engine) => await getSingletonHighlighter({
|
|
10
10
|
langs: langsToLoad.filter((lang) => isLoadableLanguage(lang, bundledLanguages)),
|
|
11
11
|
themes: themesToLoad,
|
|
12
12
|
engine: engine ?? createOnigurumaEngine(import("shiki/wasm"))
|
|
13
13
|
});
|
|
14
|
-
//#endregion
|
|
15
14
|
export { bundledHighlighterFactory as t };
|
|
16
15
|
|
|
17
|
-
//# sourceMappingURL=factory-
|
|
16
|
+
//# sourceMappingURL=factory-Ds5IsDw-.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"factory-
|
|
1
|
+
{"version":3,"file":"factory-Ds5IsDw-.mjs","names":[],"sources":["../src/bundles/factory.ts"],"sourcesContent":["import { createOnigurumaEngine } from 'shiki/engine/oniguruma';\nimport type {\n Awaitable,\n DynamicImportLanguageRegistration,\n HighlighterCore,\n RegexEngine,\n} from 'shiki/core';\nimport type {\n HighlighterFactory,\n PreloadLanguage,\n Theme,\n} from '../lib/types';\nimport { isLoadableLanguage } from '../lib/language';\n\ntype GetSingletonHighlighter = (options: {\n langs: NonNullable<PreloadLanguage>[];\n themes: Theme[];\n engine: Awaitable<RegexEngine>;\n}) => Promise<HighlighterCore>;\n\n/**\n * Builds a highlighter factory for a Shiki bundle (full or web).\n * Unloadable languages are filtered out so unknown ids fall back to\n * plaintext instead of throwing.\n */\nexport const bundledHighlighterFactory =\n (\n getSingletonHighlighter: GetSingletonHighlighter,\n bundledLanguages: Record<string, DynamicImportLanguageRegistration>\n ): HighlighterFactory =>\n async (langsToLoad, themesToLoad, engine) =>\n await getSingletonHighlighter({\n langs: langsToLoad.filter((lang) =>\n isLoadableLanguage(lang, bundledLanguages)\n ),\n themes: themesToLoad,\n engine: engine ?? createOnigurumaEngine(import('shiki/wasm')),\n });\n"],"mappings":";;;;;;;;AAyBA,MAAa,6BAET,yBACA,qBAEF,OAAO,aAAa,cAAc,WAChC,MAAM,wBAAwB;CAC5B,OAAO,YAAY,QAAQ,SACzB,mBAAmB,MAAM,gBAAgB,CAC3C;CACA,QAAQ;CACR,QAAQ,UAAU,sBAAsB,OAAO,aAAa;AAC9D,CAAC"}
|
package/dist/index.d.mts
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
|
-
import { a as EngineName, c as
|
|
1
|
+
import { a as EngineName, c as HighlighterOptions, d as OutputFormat, f as PreloadLanguage, h as UseShikiHighlighter, i as Element, l as HighlighterOptionsFor, m as Themes, n as rehypeInlineCodeProperty, o as HighlightCache, p as Theme, r as ShikiHighlighterProps, s as HighlightResult, t as isInlineCode, u as Language } from "./plugins-DdjjMnbY.mjs";
|
|
2
2
|
import { LanguageRegistration, TokensResult } from "shiki";
|
|
3
3
|
import { createJavaScriptRawEngine, createJavaScriptRegexEngine } from "shiki/engine/javascript";
|
|
4
|
-
//#region src/index.d.ts
|
|
5
4
|
/**
|
|
6
5
|
* Highlight code with shiki (full bundle)
|
|
7
6
|
*
|
|
@@ -31,6 +30,4 @@ export declare const useShikiHighlighter: UseShikiHighlighter;
|
|
|
31
30
|
* Includes all languages and themes for maximum compatibility.
|
|
32
31
|
*/
|
|
33
32
|
export declare const ShikiHighlighter: import("react").ForwardRefExoticComponent<ShikiHighlighterProps & import("react").RefAttributes<HTMLElement>>;
|
|
34
|
-
|
|
35
|
-
export { type Element, type EngineName, type HighlightResult, type HighlighterOptions, type HighlighterOptionsFor, type Language, type LanguageRegistration, type OutputFormat, type PreloadLanguage, ShikiHighlighter as default, type ShikiHighlighterProps, type Theme, type Themes, type TokensResult, type UseShikiHighlighter, createJavaScriptRawEngine, createJavaScriptRegexEngine, isInlineCode, rehypeInlineCodeProperty };
|
|
36
|
-
//# sourceMappingURL=index.d.mts.map
|
|
33
|
+
export { type Element, type EngineName, type HighlightCache, type HighlightResult, type HighlighterOptions, type HighlighterOptionsFor, type Language, type LanguageRegistration, type OutputFormat, type PreloadLanguage, ShikiHighlighter as default, type ShikiHighlighterProps, type Theme, type Themes, type TokensResult, type UseShikiHighlighter, createJavaScriptRawEngine, createJavaScriptRegexEngine, isInlineCode, rehypeInlineCodeProperty };
|
package/dist/index.mjs
CHANGED
|
@@ -1,15 +1,14 @@
|
|
|
1
|
-
import { i as useHighlight, n as
|
|
2
|
-
import { t as bundledHighlighterFactory } from "./factory-
|
|
1
|
+
import { i as useHighlight, n as rehypeInlineCodeProperty, r as createShikiHighlighterComponent, t as isInlineCode } from "./plugins-C0Uf67_P.mjs";
|
|
2
|
+
import { t as bundledHighlighterFactory } from "./factory-Ds5IsDw-.mjs";
|
|
3
3
|
import { bundledLanguages, getSingletonHighlighter } from "shiki";
|
|
4
4
|
import { createJavaScriptRawEngine, createJavaScriptRegexEngine } from "shiki/engine/javascript";
|
|
5
|
-
|
|
5
|
+
// src/bundles/full.ts
|
|
6
6
|
/**
|
|
7
7
|
* Highlighter factory for the full Shiki bundle: every language and
|
|
8
8
|
* theme, largest size, maximum compatibility.
|
|
9
9
|
*/
|
|
10
10
|
const createFullHighlighter = bundledHighlighterFactory(getSingletonHighlighter, bundledLanguages);
|
|
11
|
-
|
|
12
|
-
//#region src/index.ts
|
|
11
|
+
// src/index.ts
|
|
13
12
|
/**
|
|
14
13
|
* Highlight code with shiki (full bundle)
|
|
15
14
|
*
|
|
@@ -39,7 +38,6 @@ const useShikiHighlighter = (code, lang, themeInput, options = {}) => useHighlig
|
|
|
39
38
|
* Includes all languages and themes for maximum compatibility.
|
|
40
39
|
*/
|
|
41
40
|
const ShikiHighlighter = /*#__PURE__*/ createShikiHighlighterComponent(useShikiHighlighter);
|
|
42
|
-
//#endregion
|
|
43
41
|
export { ShikiHighlighter, ShikiHighlighter as default, createJavaScriptRawEngine, createJavaScriptRegexEngine, isInlineCode, rehypeInlineCodeProperty, useShikiHighlighter };
|
|
44
42
|
|
|
45
43
|
//# sourceMappingURL=index.mjs.map
|
package/dist/index.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.mjs","names":[],"sources":["../src/bundles/full.ts","../src/index.ts"],"sourcesContent":["import { getSingletonHighlighter, bundledLanguages } from 'shiki';\nimport { bundledHighlighterFactory } from './factory';\n\n/**\n * Highlighter factory for the full Shiki bundle: every language and\n * theme, largest size, maximum compatibility.\n */\nexport const createFullHighlighter = bundledHighlighterFactory(\n getSingletonHighlighter,\n bundledLanguages\n);\n","import { useHighlight } from './lib/hook';\nimport { createFullHighlighter } from './bundles/full';\nimport type { UseShikiHighlighter } from './lib/types';\
|
|
1
|
+
{"version":3,"file":"index.mjs","names":[],"sources":["../src/bundles/full.ts","../src/index.ts"],"sourcesContent":["import { getSingletonHighlighter, bundledLanguages } from 'shiki';\nimport { bundledHighlighterFactory } from './factory';\n\n/**\n * Highlighter factory for the full Shiki bundle: every language and\n * theme, largest size, maximum compatibility.\n */\nexport const createFullHighlighter = bundledHighlighterFactory(\n getSingletonHighlighter,\n bundledLanguages\n);\n","import { useHighlight } from './lib/hook';\nimport { createFullHighlighter } from './bundles/full';\nimport type { UseShikiHighlighter } from './lib/types';\nimport { createShikiHighlighterComponent } from './lib/component';\n// oxlint-disable-next-line unicorn/prefer-export-from -- `export type … from` here makes tsdown fold the core entry's declarations into the shared .d.mts chunk\nimport type { ShikiHighlighterProps } from './lib/component';\n\nexport { isInlineCode, rehypeInlineCodeProperty } from './lib/plugins';\n\nexport type { ShikiHighlighterProps };\n\nexport type {\n UseShikiHighlighter,\n Language,\n PreloadLanguage,\n Theme,\n Themes,\n Element,\n EngineName,\n HighlighterOptions,\n HighlighterOptionsFor,\n HighlightCache,\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 =\n // oxlint-disable-next-line eslint/no-inline-comments -- the #__PURE__ annotation lets bundlers drop the component when only the hook is used\n /*#__PURE__*/ createShikiHighlighterComponent(useShikiHighlighter);\n\nexport default ShikiHighlighter;\n"],"mappings":";;;;;;;;;AAOA,MAAa,wBAAwB,0BACnC,yBACA,gBACF;;;;;;;;;;;;;;;;;;;;;;;;;AC8CA,MAAa,uBACX,MACA,MACA,YACA,UAAU,CAAC,MACR,aAAa,MAAM,MAAM,YAAY,SAAS,qBAAqB;;;;;AAMxE,MAAa,mBAEG,8CAAgC,mBAAmB"}
|
|
@@ -4,9 +4,9 @@ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
|
4
4
|
import { toJsxRuntime } from "hast-util-to-jsx-runtime";
|
|
5
5
|
import { dequal } from "dequal";
|
|
6
6
|
import { guessEmbeddedLanguages, isSpecialLang } from "shiki/core";
|
|
7
|
-
import { visit } from "unist-util-visit";
|
|
8
7
|
import { clsx } from "clsx";
|
|
9
|
-
|
|
8
|
+
import { visit } from "unist-util-visit";
|
|
9
|
+
// src/lib/utils.ts
|
|
10
10
|
/**
|
|
11
11
|
* Returns a referentially stable version of `value` that only updates when content changes.
|
|
12
12
|
* Uses reference equality as a fast path and deep comparison to prevent unnecessary updates.
|
|
@@ -35,14 +35,11 @@ const throttleHighlighting = (performHighlight, timeoutControl, throttleMs) => {
|
|
|
35
35
|
timeoutControl.current.nextAllowedTime = Date.now() + throttleMs;
|
|
36
36
|
}, delay);
|
|
37
37
|
};
|
|
38
|
-
|
|
39
|
-
//#region src/styles/component.css?inline
|
|
38
|
+
// src/styles/component.css?inline
|
|
40
39
|
var component_default = ":where(.rs-root){position:relative}:where(.rs-root.rs-default-styles pre){border-radius:.5rem;padding-block:1.25rem;padding-inline:1.5rem;overflow:auto}:where(.rs-root .rs-language-label){z-index:1;letter-spacing:-.05em;color:#6b7280d9;font-family:monospace;font-size:.75rem;position:absolute;top:.5rem;right:.75rem}\n";
|
|
41
|
-
|
|
42
|
-
//#region src/styles/features.css?inline
|
|
40
|
+
// src/styles/features.css?inline
|
|
43
41
|
var features_default = ":where(.rs-has-line-numbers){counter-reset:line-number calc(var(--line-start,1) - 1)}:where(.rs-has-line-numbers .rs-line-number):before{counter-increment:line-number;content:counter(line-number);box-sizing:content-box;min-width:var(--rs-line-numbers-width,2ch);padding-left:var(--rs-line-numbers-padding-left,0ch);padding-right:var(--rs-line-numbers-padding-right,2ch);color:var(--rs-line-numbers-foreground,#6b728080);font-size:var(--rs-line-numbers-font-size,inherit);font-weight:var(--rs-line-numbers-font-weight,inherit);line-height:var(--rs-line-numbers-line-height,inherit);font-family:var(--rs-line-numbers-font-family,inherit);opacity:var(--rs-line-numbers-opacity,1);user-select:none;pointer-events:none;justify-content:flex-end;align-items:flex-start;display:inline-flex}:where(.rs-has-highlighted-lines){width:fit-content;min-width:100%;display:block}:where(.rs-highlighted-line){--_rs-hl-background:var(--rs-highlighted-line-background,color-mix(in srgb, currentColor 10%, transparent));background-color:var(--_rs-hl-background);width:100%;min-height:1lh;display:inline-block}:where(.rs-default-styles .rs-highlighted-line){border-image:linear-gradient(var(--_rs-hl-background) 0 0) fill 0 / 0 / 0 100vw;background-color:#0000}:where(.rs-has-line-numbers .rs-highlighted-line):before{color:var(--rs-highlighted-line-number-foreground,color-mix(in srgb, currentColor 65%, transparent))}\n";
|
|
44
|
-
|
|
45
|
-
//#region src/lib/styles.ts
|
|
42
|
+
// src/lib/styles.ts
|
|
46
43
|
const injected = /* @__PURE__ */ new Set();
|
|
47
44
|
const useStyleInsertionEffect = React.useInsertionEffect ?? React.useLayoutEffect;
|
|
48
45
|
const inject = (css, key) => {
|
|
@@ -71,10 +68,12 @@ const useFeatureStyles = (enabled) => {
|
|
|
71
68
|
if (enabled) inject(features_default, "features");
|
|
72
69
|
}, [enabled]);
|
|
73
70
|
};
|
|
74
|
-
|
|
75
|
-
//#region src/lib/language.ts
|
|
71
|
+
// src/lib/language.ts
|
|
76
72
|
const FALLBACK_LANGUAGE = "plaintext";
|
|
77
|
-
const toArray = (value) =>
|
|
73
|
+
const toArray = (value) => {
|
|
74
|
+
if (value == null) return [];
|
|
75
|
+
return Array.isArray(value) ? value : [value];
|
|
76
|
+
};
|
|
78
77
|
const isDynamicLanguage = (lang) => typeof lang === "function" || lang instanceof Promise || Array.isArray(lang) || typeof lang === "object" && lang != null && "default" in lang;
|
|
79
78
|
const isRegistration = (lang) => typeof lang === "object" && lang != null && !isDynamicLanguage(lang);
|
|
80
79
|
/** Dedupe identity: strings and registrations by value, dynamic inputs by reference */
|
|
@@ -144,17 +143,15 @@ const getEmbeddedLanguages = (code, languageId, highlighter) => {
|
|
|
144
143
|
const bundled = highlighter.getBundledLanguages();
|
|
145
144
|
return guessEmbeddedLanguages(code, languageId).flatMap((language) => bundled[language] ?? []);
|
|
146
145
|
};
|
|
147
|
-
|
|
148
|
-
//#region src/lib/theme.ts
|
|
146
|
+
// src/lib/theme.ts
|
|
149
147
|
const DEFAULT_THEMES = {
|
|
150
148
|
light: "github-light",
|
|
151
149
|
dark: "github-dark"
|
|
152
150
|
};
|
|
153
151
|
const isTextmateTheme = (value) => {
|
|
154
152
|
if (typeof value !== "object" || value === null) return false;
|
|
155
|
-
const
|
|
156
|
-
const
|
|
157
|
-
const hasIdentity = typeof v.name === "string" || typeof v.type === "string";
|
|
153
|
+
const hasTokens = "tokenColors" in value && Array.isArray(value.tokenColors) || "settings" in value && Array.isArray(value.settings);
|
|
154
|
+
const hasIdentity = "name" in value && typeof value.name === "string" || "type" in value && typeof value.type === "string";
|
|
158
155
|
return hasTokens && hasIdentity;
|
|
159
156
|
};
|
|
160
157
|
function resolveTheme(themeInput) {
|
|
@@ -179,12 +176,17 @@ function resolveTheme(themeInput) {
|
|
|
179
176
|
themesToLoad: validEntries.map(([, value]) => value)
|
|
180
177
|
};
|
|
181
178
|
}
|
|
182
|
-
|
|
183
|
-
//#region src/lib/engine.ts
|
|
179
|
+
// src/lib/engine.ts
|
|
184
180
|
const engines = /* @__PURE__ */ new Map();
|
|
185
181
|
const loaders = {
|
|
186
|
-
javascript:
|
|
187
|
-
|
|
182
|
+
javascript: async () => {
|
|
183
|
+
const { createJavaScriptRegexEngine } = await import("shiki/engine/javascript");
|
|
184
|
+
return createJavaScriptRegexEngine();
|
|
185
|
+
},
|
|
186
|
+
oniguruma: async () => {
|
|
187
|
+
const { createOnigurumaEngine } = await import("shiki/engine/oniguruma");
|
|
188
|
+
return await createOnigurumaEngine(import("shiki/wasm"));
|
|
189
|
+
}
|
|
188
190
|
};
|
|
189
191
|
/**
|
|
190
192
|
* Resolves a named engine to a lazily created, cached instance.
|
|
@@ -195,17 +197,15 @@ const loaders = {
|
|
|
195
197
|
*/
|
|
196
198
|
const resolveEngine = (engine) => {
|
|
197
199
|
if (typeof engine !== "string") return engine;
|
|
198
|
-
|
|
199
|
-
if (!load) throw new Error(`[react-shiki] unknown engine '${engine}', expected 'javascript' or 'oniguruma'`);
|
|
200
|
+
if (!Object.hasOwn(loaders, engine)) throw new Error(`[react-shiki] unknown engine '${engine}', expected 'javascript' or 'oniguruma'`);
|
|
200
201
|
const cached = engines.get(engine);
|
|
201
202
|
if (cached) return cached;
|
|
202
|
-
const instance =
|
|
203
|
+
const instance = loaders[engine]();
|
|
203
204
|
instance.catch(() => engines.delete(engine));
|
|
204
205
|
engines.set(engine, instance);
|
|
205
206
|
return instance;
|
|
206
207
|
};
|
|
207
|
-
|
|
208
|
-
//#region src/lib/transformers.ts
|
|
208
|
+
// src/lib/transformers.ts
|
|
209
209
|
const toLineNumberSet = (lineNumbers) => new Set(lineNumbers.filter(Number.isInteger));
|
|
210
210
|
/**
|
|
211
211
|
* Creates a transformer that enables line numbers display
|
|
@@ -249,8 +249,7 @@ function highlightedLinesTransformer(lineNumbers, startLine = 1) {
|
|
|
249
249
|
}
|
|
250
250
|
};
|
|
251
251
|
}
|
|
252
|
-
|
|
253
|
-
//#region src/lib/options.ts
|
|
252
|
+
// src/lib/options.ts
|
|
254
253
|
const buildThemeOptions = (resolvedTheme, defaultColor, cssVariablePrefix) => {
|
|
255
254
|
if (resolvedTheme.isMulti) return {
|
|
256
255
|
themes: resolvedTheme.themes,
|
|
@@ -261,7 +260,7 @@ const buildThemeOptions = (resolvedTheme, defaultColor, cssVariablePrefix) => {
|
|
|
261
260
|
};
|
|
262
261
|
const buildShikiOptions = (languageId, resolvedTheme, options) => {
|
|
263
262
|
const { delay, customLanguages, preloadLanguages, outputFormat, highlighter, langAlias, engine, defaultColor, cssVariablePrefix, showLineNumbers, startingLineNumber, highlightLineNumbers, transformers: userTransformers, ...shikiPassthrough } = options;
|
|
264
|
-
const transformers = [...userTransformers
|
|
263
|
+
const transformers = [...userTransformers ?? []];
|
|
265
264
|
if (showLineNumbers) transformers.push(lineNumbersTransformer(startingLineNumber));
|
|
266
265
|
if (highlightLineNumbers?.length) transformers.push(highlightedLinesTransformer(highlightLineNumbers, startingLineNumber));
|
|
267
266
|
return {
|
|
@@ -271,8 +270,7 @@ const buildShikiOptions = (languageId, resolvedTheme, options) => {
|
|
|
271
270
|
transformers
|
|
272
271
|
};
|
|
273
272
|
};
|
|
274
|
-
|
|
275
|
-
//#region src/lib/hook.ts
|
|
273
|
+
// src/lib/hook.ts
|
|
276
274
|
async function highlight(code, resolved, opts, factory) {
|
|
277
275
|
const { languageId, langsToLoad, themesToLoad, shikiOptions } = resolved;
|
|
278
276
|
const format = opts.outputFormat ?? "react";
|
|
@@ -297,12 +295,19 @@ async function highlight(code, resolved, opts, factory) {
|
|
|
297
295
|
};
|
|
298
296
|
return (outputs[format] ?? outputs.react)();
|
|
299
297
|
}
|
|
300
|
-
const
|
|
301
|
-
|
|
302
|
-
|
|
298
|
+
const readCache = (cache, key) => {
|
|
299
|
+
if (!key) return null;
|
|
300
|
+
const cached = cache?.get(key);
|
|
301
|
+
return cached === void 0 || cached === "" ? null : cached;
|
|
302
|
+
};
|
|
303
|
+
const useHighlight = (code, lang, themeInput, optionsInput, highlighterFactory) => {
|
|
304
|
+
const { cache, ...rest } = optionsInput ?? {};
|
|
305
|
+
const cacheRef = useRef(cache);
|
|
306
|
+
cacheRef.current = cache;
|
|
307
|
+
useFeatureStyles(Boolean(rest.showLineNumbers || rest.highlightLineNumbers?.length));
|
|
303
308
|
const stableLang = useStableValue(lang);
|
|
304
309
|
const stableTheme = useStableValue(themeInput);
|
|
305
|
-
const stableOpts = useStableValue(
|
|
310
|
+
const stableOpts = useStableValue(rest);
|
|
306
311
|
const resolved = useMemo(() => {
|
|
307
312
|
const { languageId, langsToLoad } = resolveLanguage(stableLang, stableOpts.customLanguages, stableOpts.langAlias, stableOpts.preloadLanguages);
|
|
308
313
|
const theme = resolveTheme(stableTheme);
|
|
@@ -318,6 +323,27 @@ const useHighlight = (code, lang, themeInput, options = {}, highlighterFactory)
|
|
|
318
323
|
stableTheme,
|
|
319
324
|
stableOpts
|
|
320
325
|
]);
|
|
326
|
+
const hasCache = Boolean(cache);
|
|
327
|
+
const cacheScope = useMemo(() => hasCache ? JSON.stringify([
|
|
328
|
+
stableOpts.outputFormat ?? "react",
|
|
329
|
+
resolved.shikiOptions,
|
|
330
|
+
stableOpts.startingLineNumber,
|
|
331
|
+
stableOpts.highlightLineNumbers
|
|
332
|
+
]) : void 0, [
|
|
333
|
+
hasCache,
|
|
334
|
+
resolved,
|
|
335
|
+
stableOpts
|
|
336
|
+
]);
|
|
337
|
+
const cacheKey = cacheScope && `${cacheScope}\n${code}`;
|
|
338
|
+
const [highlightedCode, setHighlightedCode] = useState(() => readCache(cache, cacheKey));
|
|
339
|
+
const shown = useRef({
|
|
340
|
+
key: cacheKey,
|
|
341
|
+
result: highlightedCode
|
|
342
|
+
});
|
|
343
|
+
useEffect(() => () => {
|
|
344
|
+
const { key, result } = shown.current;
|
|
345
|
+
if (key && result !== null && result !== "") cacheRef.current?.set(key, result);
|
|
346
|
+
}, []);
|
|
321
347
|
const requestIdRef = useRef(0);
|
|
322
348
|
const timeoutControl = useRef({
|
|
323
349
|
nextAllowedTime: 0,
|
|
@@ -327,29 +353,85 @@ const useHighlight = (code, lang, themeInput, options = {}, highlighterFactory)
|
|
|
327
353
|
highlighterFactoryRef.current = highlighterFactory;
|
|
328
354
|
useEffect(() => {
|
|
329
355
|
const requestId = ++requestIdRef.current;
|
|
330
|
-
|
|
356
|
+
const timeouts = timeoutControl.current;
|
|
357
|
+
const show = (result) => {
|
|
358
|
+
shown.current = {
|
|
359
|
+
key: cacheKey,
|
|
360
|
+
result
|
|
361
|
+
};
|
|
362
|
+
setHighlightedCode(result);
|
|
363
|
+
};
|
|
331
364
|
const run = async () => {
|
|
332
365
|
try {
|
|
333
366
|
const result = await highlight(code, resolved, stableOpts, highlighterFactoryRef.current);
|
|
334
|
-
if (requestId === requestIdRef.current)
|
|
367
|
+
if (requestId === requestIdRef.current) show(result);
|
|
335
368
|
} catch (error) {
|
|
336
369
|
console.error("[react-shiki] highlight failed", error);
|
|
337
370
|
}
|
|
338
371
|
};
|
|
339
|
-
|
|
372
|
+
const cached = readCache(cacheRef.current, cacheKey);
|
|
373
|
+
if (!resolved.languageId) return () => {
|
|
374
|
+
clearTimeout(timeouts.timeoutId);
|
|
375
|
+
};
|
|
376
|
+
if (cached !== null) {
|
|
377
|
+
if (shown.current.result !== cached) show(cached);
|
|
378
|
+
} else if (stableOpts.delay) throttleHighlighting(run, timeoutControl, stableOpts.delay);
|
|
340
379
|
else run();
|
|
341
380
|
return () => {
|
|
342
|
-
clearTimeout(
|
|
381
|
+
clearTimeout(timeouts.timeoutId);
|
|
343
382
|
};
|
|
344
383
|
}, [
|
|
345
384
|
code,
|
|
385
|
+
cacheKey,
|
|
346
386
|
resolved,
|
|
347
387
|
stableOpts
|
|
348
388
|
]);
|
|
349
389
|
return highlightedCode;
|
|
350
390
|
};
|
|
351
|
-
|
|
352
|
-
|
|
391
|
+
// src/lib/component.tsx
|
|
392
|
+
let warnedTokensOutputFormat = false;
|
|
393
|
+
/**
|
|
394
|
+
* The component's props exclude `outputFormat: 'tokens'`, but plain-JS
|
|
395
|
+
* callers can still pass it. Tokens are not renderable React children,
|
|
396
|
+
* so fall back to 'react' instead of crashing the render.
|
|
397
|
+
*/
|
|
398
|
+
const resolveOutputFormat = (outputFormat) => {
|
|
399
|
+
if (outputFormat !== "tokens") return outputFormat;
|
|
400
|
+
if (!warnedTokensOutputFormat) {
|
|
401
|
+
warnedTokensOutputFormat = true;
|
|
402
|
+
console.warn("[react-shiki] outputFormat 'tokens' is only supported by the useShikiHighlighter hook, falling back to 'react'");
|
|
403
|
+
}
|
|
404
|
+
return "react";
|
|
405
|
+
};
|
|
406
|
+
/**
|
|
407
|
+
* Base ShikiHighlighter component factory.
|
|
408
|
+
* This creates a component that uses the provided hook implementation.
|
|
409
|
+
*/
|
|
410
|
+
const createShikiHighlighterComponent = (useShikiHighlighterImpl) => forwardRef(({ language, theme, addDefaultStyles = true, style, langStyle, className, langClassName, showLanguage = true, children: code, as: Element = "div", outputFormat, ...options }, ref) => {
|
|
411
|
+
useComponentStyles();
|
|
412
|
+
const displayLanguageId = typeof language === "object" ? language.name || null : language?.trim() || null;
|
|
413
|
+
const highlightedCode = useShikiHighlighterImpl(code, language, theme, {
|
|
414
|
+
...options,
|
|
415
|
+
outputFormat: resolveOutputFormat(outputFormat)
|
|
416
|
+
});
|
|
417
|
+
return /* @__PURE__ */ jsxs(Element, {
|
|
418
|
+
ref,
|
|
419
|
+
"data-testid": "shiki-container",
|
|
420
|
+
"data-slot": "container",
|
|
421
|
+
className: clsx("rs-root", "not-prose", addDefaultStyles && "rs-default-styles", className),
|
|
422
|
+
style,
|
|
423
|
+
children: [showLanguage && displayLanguageId ? /* @__PURE__ */ jsx("span", {
|
|
424
|
+
"data-slot": "language-label",
|
|
425
|
+
className: clsx("rs-language-label", langClassName),
|
|
426
|
+
style: langStyle,
|
|
427
|
+
children: displayLanguageId
|
|
428
|
+
}) : null, typeof highlightedCode === "string" ? /* @__PURE__ */ jsx("div", {
|
|
429
|
+
"data-slot": "content",
|
|
430
|
+
dangerouslySetInnerHTML: { __html: highlightedCode }
|
|
431
|
+
}) : highlightedCode]
|
|
432
|
+
});
|
|
433
|
+
});
|
|
434
|
+
// src/lib/plugins.ts
|
|
353
435
|
/**
|
|
354
436
|
* Rehype plugin to add an 'inline' property to <code> elements
|
|
355
437
|
* Sets 'inline' property to true if the <code> is not within a <pre> tag
|
|
@@ -374,55 +456,8 @@ function rehypeInlineCodeProperty() {
|
|
|
374
456
|
* const isInline = node && isInlineCode(node: Element)
|
|
375
457
|
*/
|
|
376
458
|
const isInlineCode = (node) => {
|
|
377
|
-
return !(node.children
|
|
378
|
-
};
|
|
379
|
-
//#endregion
|
|
380
|
-
//#region src/lib/component.tsx
|
|
381
|
-
let warnedTokensOutputFormat = false;
|
|
382
|
-
/**
|
|
383
|
-
* The component's props exclude `outputFormat: 'tokens'`, but plain-JS
|
|
384
|
-
* callers can still pass it. Tokens are not renderable React children,
|
|
385
|
-
* so fall back to 'react' instead of crashing the render.
|
|
386
|
-
*/
|
|
387
|
-
const resolveOutputFormat = (outputFormat) => {
|
|
388
|
-
if (outputFormat !== "tokens") return outputFormat;
|
|
389
|
-
if (!warnedTokensOutputFormat) {
|
|
390
|
-
warnedTokensOutputFormat = true;
|
|
391
|
-
console.warn("[react-shiki] outputFormat 'tokens' is only supported by the useShikiHighlighter hook, falling back to 'react'");
|
|
392
|
-
}
|
|
393
|
-
return "react";
|
|
394
|
-
};
|
|
395
|
-
/**
|
|
396
|
-
* Base ShikiHighlighter component factory.
|
|
397
|
-
* This creates a component that uses the provided hook implementation.
|
|
398
|
-
*/
|
|
399
|
-
const createShikiHighlighterComponent = (useShikiHighlighterImpl) => {
|
|
400
|
-
return forwardRef(({ language, theme, addDefaultStyles = true, style, langStyle, className, langClassName, showLanguage = true, children: code, as: Element = "div", outputFormat, ...options }, ref) => {
|
|
401
|
-
useComponentStyles();
|
|
402
|
-
const displayLanguageId = typeof language === "object" ? language.name || null : language?.trim() || null;
|
|
403
|
-
const highlightedCode = useShikiHighlighterImpl(code, language, theme, {
|
|
404
|
-
...options,
|
|
405
|
-
outputFormat: resolveOutputFormat(outputFormat)
|
|
406
|
-
});
|
|
407
|
-
return /* @__PURE__ */ jsxs(Element, {
|
|
408
|
-
ref,
|
|
409
|
-
"data-testid": "shiki-container",
|
|
410
|
-
"data-slot": "container",
|
|
411
|
-
className: clsx("rs-root", "not-prose", addDefaultStyles && "rs-default-styles", className),
|
|
412
|
-
style,
|
|
413
|
-
children: [showLanguage && displayLanguageId ? /* @__PURE__ */ jsx("span", {
|
|
414
|
-
"data-slot": "language-label",
|
|
415
|
-
className: clsx("rs-language-label", langClassName),
|
|
416
|
-
style: langStyle,
|
|
417
|
-
children: displayLanguageId
|
|
418
|
-
}) : null, typeof highlightedCode === "string" ? /* @__PURE__ */ jsx("div", {
|
|
419
|
-
"data-slot": "content",
|
|
420
|
-
dangerouslySetInnerHTML: { __html: highlightedCode }
|
|
421
|
-
}) : highlightedCode]
|
|
422
|
-
});
|
|
423
|
-
});
|
|
459
|
+
return !(node.children ?? []).filter((child) => child.type === "text").map((child) => child.value).join("").includes("\n");
|
|
424
460
|
};
|
|
425
|
-
|
|
426
|
-
export { isLoadableLanguage as a, useHighlight as i, isInlineCode as n, rehypeInlineCodeProperty as r, createShikiHighlighterComponent as t };
|
|
461
|
+
export { isLoadableLanguage as a, useHighlight as i, rehypeInlineCodeProperty as n, createShikiHighlighterComponent as r, isInlineCode as t };
|
|
427
462
|
|
|
428
|
-
//# sourceMappingURL=
|
|
463
|
+
//# sourceMappingURL=plugins-C0Uf67_P.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"plugins-C0Uf67_P.mjs","names":["componentCss","featuresCss"],"sources":["../src/lib/utils.ts","../src/styles/component.css?inline","../src/styles/features.css?inline","../src/lib/styles.ts","../src/lib/language.ts","../src/lib/theme.ts","../src/lib/engine.ts","../src/lib/transformers.ts","../src/lib/options.ts","../src/lib/hook.ts","../src/lib/component.tsx","../src/lib/plugins.ts"],"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: { current: 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\n timeoutControl.current.timeoutId = setTimeout(() => {\n void performHighlight();\n timeoutControl.current.nextAllowedTime = Date.now() + throttleMs;\n }, delay);\n};\n","/* Unlayered :where() defaults: immune to layered resets, overridden by any user rule. */\n:where(.rs-root) {\n position: relative;\n}\n\n:where(.rs-root.rs-default-styles pre) {\n overflow: auto;\n border-radius: 0.5rem;\n padding-inline: 1.5rem;\n padding-block: 1.25rem;\n}\n\n:where(.rs-root .rs-language-label) {\n position: absolute;\n z-index: 1;\n right: 0.75rem;\n top: 0.5rem;\n font-family: monospace;\n font-size: 0.75rem;\n letter-spacing: -0.05em;\n color: rgba(107, 114, 128, 0.85);\n}\n",":where(.rs-has-line-numbers) {\n counter-reset: line-number calc(var(--line-start, 1) - 1);\n}\n\n:where(.rs-has-line-numbers .rs-line-number)::before {\n counter-increment: line-number;\n content: counter(line-number);\n display: inline-flex;\n justify-content: flex-end;\n align-items: flex-start;\n box-sizing: content-box;\n min-width: var(--rs-line-numbers-width, 2ch);\n padding-left: var(--rs-line-numbers-padding-left, 0ch);\n padding-right: var(--rs-line-numbers-padding-right, 2ch);\n color: var(--rs-line-numbers-foreground, rgba(107, 114, 128, 0.5));\n font-size: var(--rs-line-numbers-font-size, inherit);\n font-weight: var(--rs-line-numbers-font-weight, inherit);\n line-height: var(--rs-line-numbers-line-height, inherit);\n font-family: var(--rs-line-numbers-font-family, inherit);\n opacity: var(--rs-line-numbers-opacity, 1);\n user-select: none;\n pointer-events: none;\n}\n\n:where(.rs-has-highlighted-lines) {\n display: block;\n width: fit-content;\n min-width: 100%;\n}\n\n:where(.rs-highlighted-line) {\n --_rs-hl-background: var(\n --rs-highlighted-line-background,\n color-mix(in srgb, currentColor 10%, transparent)\n );\n display: inline-block;\n width: 100%;\n min-height: 1lh;\n background-color: var(--_rs-hl-background);\n}\n\n/* Full bleed: the 100vw outset paints into the pre's padding, clipped by its overflow. */\n:where(.rs-default-styles .rs-highlighted-line) {\n background-color: transparent;\n border-image:\n linear-gradient(var(--_rs-hl-background) 0 0) fill 0 / 0 / 0 100vw;\n}\n\n:where(.rs-has-line-numbers .rs-highlighted-line)::before {\n color: var(\n --rs-highlighted-line-number-foreground,\n color-mix(in srgb, currentColor 65%, transparent)\n );\n}\n","import componentCss from '../styles/component.css?inline';\nimport featuresCss from '../styles/features.css?inline';\nimport * as React from 'react';\n// Side-effect import so tsdown emits dist/style.css for the ./css export;\n// stripped from the built JS, guarded by check:package\nimport '../styles/index.css';\n\nconst injected = new Set<string>();\n\nconst useStyleInsertionEffect =\n React.useInsertionEffect ?? React.useLayoutEffect;\n\nconst inject = (css: string, key: string) => {\n if (injected.has(key) || typeof document === 'undefined') {\n return;\n }\n\n injected.add(key);\n\n try {\n // Not governed by CSP style-src in current browsers, unlike the <style> fallback\n const sheet = new CSSStyleSheet();\n sheet.replaceSync(css);\n document.adoptedStyleSheets = [...document.adoptedStyleSheets, sheet];\n } catch {\n const style = document.createElement('style');\n style.dataset.reactShiki = key;\n style.textContent = css;\n document.head.append(style);\n }\n};\n\n/** Installs component styles before layout without triggering a render. */\nexport const useComponentStyles = () => {\n useStyleInsertionEffect(() => {\n inject(componentCss, 'component');\n }, []);\n};\n\n/** Installs line-number/highlight styles only when those options are used. */\nexport const useFeatureStyles = (enabled: boolean) => {\n useStyleInsertionEffect(() => {\n if (enabled) {\n inject(featuresCss, 'features');\n }\n }, [enabled]);\n};\n","import type { Language, PreloadLanguage } 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\nconst toArray = <T>(value?: T | T[]): T[] => {\n if (value == null) {\n return [];\n }\n\n return Array.isArray(value) ? value : [value];\n};\n\n/** Deferred `LanguageInput` forms (getter, promise, module, array) that shiki resolves itself */\ntype DynamicLanguage = Exclude<LanguageInput, LanguageRegistration>;\n\nconst isDynamicLanguage = (\n lang: PreloadLanguage\n): lang is DynamicLanguage =>\n typeof lang === 'function' ||\n lang instanceof Promise ||\n Array.isArray(lang) ||\n (typeof lang === 'object' && lang != null && 'default' in lang);\n\nconst isRegistration = (\n lang: PreloadLanguage\n): lang is LanguageRegistration =>\n typeof lang === 'object' && lang != null && !isDynamicLanguage(lang);\n\n/** Dedupe identity: strings and registrations by value, dynamic inputs by reference */\nconst languageKey = (lang: PreloadLanguage): string | object | null => {\n if (lang == null) {\n return null;\n }\n\n if (typeof lang === 'string') {\n return `s:${lang}`;\n }\n\n if (isDynamicLanguage(lang)) {\n return lang;\n }\n\n return `o:${lang.name}::${lang.scopeName}`;\n};\n\nconst dedupeLanguages = (langs: PreloadLanguage[]): PreloadLanguage[] => {\n const seen = new Set<string | object>();\n\n return langs.filter((lang) => {\n const key = languageKey(lang);\n\n if (key == null || seen.has(key)) {\n return false;\n }\n\n seen.add(key);\n\n return true;\n });\n};\n\n/**\n * A registration matches a query by name, scopeName (full or its last\n * segment, e.g. \"source.tsx\" -> \"tsx\"), aliases, or fileTypes.\n */\nconst registrationMatches = (\n candidate: LanguageRegistration,\n matches: (str: string | undefined) => boolean\n): boolean =>\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\ninterface LanguageResult {\n languageId: string;\n langsToLoad: PreloadLanguage[];\n}\n\n/**\n * Resolves the language prop to a shiki language id plus everything the\n * factory should load (primary, preloaded, and custom grammars, deduped).\n */\nexport const resolveLanguage = (\n lang: Language,\n customLanguages?: LanguageRegistration | LanguageRegistration[],\n langAliases?: Record<string, string>,\n preloadLanguages?: PreloadLanguage | PreloadLanguage[]\n): LanguageResult => {\n const customLangs = toArray(customLanguages);\n const preloadLangs = toArray(preloadLanguages);\n\n const result = (\n languageId: string,\n primary?: Language\n ): LanguageResult => ({\n languageId,\n langsToLoad: dedupeLanguages([\n primary,\n ...preloadLangs,\n ...customLangs,\n ]),\n });\n\n if (lang == null || (typeof lang === 'string' && !lang.trim())) {\n return result(FALLBACK_LANGUAGE);\n }\n\n if (typeof lang === 'object') {\n return result(lang.name, lang);\n }\n\n const normalized = lang.trim();\n const query = normalized.toLowerCase();\n\n const matches = (str: string | undefined): boolean =>\n str?.toLowerCase() === query;\n\n // Custom registrations (from both customLanguages and preloadLanguages)\n const customMatch = [...customLangs, ...preloadLangs]\n .filter(isRegistration)\n .find((candidate) => registrationMatches(candidate, matches));\n\n if (customMatch) {\n return result(customMatch.name || normalized, customMatch);\n }\n\n const alias = Object.entries(langAliases ?? {}).find(([name]) =>\n matches(name)\n )?.[1];\n\n if (alias) {\n return result(alias, alias);\n }\n\n // Any other string passes through to the factory\n return result(normalized, normalized);\n};\n\n/**\n * Used in factories to check if language is supported.\n * Objects are validated as grammar registrations (name + scopeName);\n * dynamic imports pass through for shiki to resolve.\n */\nexport const isLoadableLanguage = <T extends string>(\n lang: PreloadLanguage,\n bundledLanguages: Record<T, DynamicImportLanguageRegistration>\n): lang is NonNullable<PreloadLanguage> => {\n if (lang == null) {\n return false;\n }\n\n if (typeof lang === 'string') {\n return lang in bundledLanguages;\n }\n\n if (isDynamicLanguage(lang)) {\n return true;\n }\n\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\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\n return guessEmbeddedLanguages(code, languageId).flatMap(\n // oxlint-disable-next-line typescript/promise-function-async -- returns shiki's LanguageInput, which can itself be a promise\n (language) => bundled[language] ?? []\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 | {\n isMulti: true;\n themes: Record<string, Theme>;\n themesToLoad: Theme[];\n }\n | { isMulti: false; theme: Theme; themesToLoad: Theme[] };\n\nconst isTextmateTheme = (\n value: Theme | Themes | undefined\n): value is ThemeRegistrationAny => {\n if (typeof value !== 'object' || value === null) {\n return false;\n }\n\n const hasTokens =\n ('tokenColors' in value && Array.isArray(value.tokenColors)) ||\n ('settings' in value && Array.isArray(value.settings));\n\n const hasIdentity =\n ('name' in value && typeof value.name === 'string') ||\n ('type' in value && typeof value.type === 'string');\n\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\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);\n\n return {\n isMulti: true,\n themes,\n themesToLoad: validEntries.map(([, value]) => value),\n };\n}\n","import type { Awaitable, RegexEngine } from 'shiki/core';\nimport type { EngineName } from './types';\n\nconst engines = new Map<EngineName, Promise<RegexEngine>>();\n\n// Dynamic imports keep unused engines out of the loaded graph: naming an\n// engine loads only that engine, and 'javascript' never fetches the wasm.\nconst loaders: Record<EngineName, () => Promise<RegexEngine>> = {\n javascript: async () => {\n const { createJavaScriptRegexEngine } =\n await import('shiki/engine/javascript');\n\n return createJavaScriptRegexEngine();\n },\n oniguruma: async () => {\n const { createOnigurumaEngine } =\n await import('shiki/engine/oniguruma');\n\n return await createOnigurumaEngine(import('shiki/wasm'));\n },\n};\n\n/**\n * Resolves a named engine to a lazily created, cached instance.\n * Engine instances and undefined pass through untouched.\n *\n * The cache makes named engines referentially stable across renders and\n * call sites, unlike inline `createJavaScriptRegexEngine()` calls.\n */\nexport const resolveEngine = (\n engine: EngineName | Awaitable<RegexEngine> | undefined\n): Awaitable<RegexEngine> | undefined => {\n if (typeof engine !== 'string') {\n return engine;\n }\n\n if (!Object.hasOwn(loaders, engine)) {\n throw new Error(\n `[react-shiki] unknown engine '${engine}', expected 'javascript' or 'oniguruma'`\n );\n }\n\n const cached = engines.get(engine);\n\n if (cached) {\n return cached;\n }\n\n const instance = loaders[engine]();\n // Evict rejected loads (e.g. transient wasm fetch failure) so the\n // next highlight retries instead of reusing the cached rejection.\n instance.catch(() => engines.delete(engine));\n engines.set(engine, instance);\n\n return instance;\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\n if (startLine !== 1) {\n const existing = node.properties?.style;\n node.properties = {\n ...node.properties,\n style: [existing, `--line-start: ${startLine}`]\n .filter(Boolean)\n .join('; '),\n };\n }\n },\n line(node) {\n this.addClassToHast(node, 'rs-line-number');\n\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 // oxlint-disable-next-line typescript/no-deprecated -- omitted from the options passed through to shiki\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\n if (showLineNumbers) {\n transformers.push(lineNumbersTransformer(startingLineNumber));\n }\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 type { ReactElement } 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 HighlightCache,\n HighlighterFactory,\n HighlighterOptions,\n HighlighterOptionsFor,\n HighlightResult,\n HighlightResultMap,\n Language,\n OutputFormat,\n PreloadLanguage,\n Theme,\n Themes,\n TimeoutState,\n} from './types';\n\nimport { throttleHighlighting, useStableValue } from './utils';\nimport { useFeatureStyles } from './styles';\nimport {\n getEmbeddedLanguages,\n resolveLanguage,\n resolveLoadedLanguage,\n} from './language';\nimport { resolveTheme } from './theme';\nimport { resolveEngine } from './engine';\nimport { buildShikiOptions } from './options';\n\nexport async function highlight<F extends OutputFormat = 'react'>(\n code: string,\n resolved: {\n languageId: string;\n langsToLoad: PreloadLanguage[];\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\n const format: OutputFormat = opts.outputFormat ?? 'react';\n\n const highlighter =\n opts.highlighter ??\n (await (async () => {\n const hl = await factory(\n langsToLoad,\n themesToLoad,\n resolveEngine(opts.engine)\n );\n\n await hl.loadLanguage(\n ...getEmbeddedLanguages(code, languageId, hl)\n );\n\n return hl;\n })());\n\n const language = resolveLoadedLanguage(\n languageId,\n highlighter.getLoadedLanguages()\n );\n\n const options = { ...shikiOptions, lang: language };\n\n const outputs = {\n react: (): ReactElement =>\n // oxlint-disable-next-line typescript/no-unsafe-return -- toJsxRuntime returns the global JSX.Element, which React 19's types no longer declare\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 } satisfies { [K in OutputFormat]: () => HighlightResultMap[K] };\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 // oxlint-disable-next-line typescript/no-unsafe-type-assertion, anti-slop/require-safety-comment-for-type-assertion -- outputs[format] returns HighlightResultMap[format], and TS can't tie format to the generic F\n return (outputs[format] ?? outputs.react)() as HighlightResultMap[F];\n}\n\nconst readCache = <F extends OutputFormat>(\n cache: HighlightCache | undefined,\n key: string | undefined\n): HighlightResult<F> => {\n if (!key) {\n return null;\n }\n\n // oxlint-disable-next-line typescript/no-unsafe-type-assertion, anti-slop/require-safety-comment-for-type-assertion -- the key includes outputFormat, so an entry under it is HighlightResultMap[F]\n const cached = cache?.get(key) as HighlightResultMap[F] | undefined;\n\n return cached === undefined || cached === '' ? null : cached;\n};\n\nexport const useHighlight = <F extends OutputFormat = 'react'>(\n code: string,\n lang: Language,\n themeInput: Theme | Themes,\n optionsInput: HighlighterOptionsFor<F> | undefined,\n highlighterFactory: HighlighterFactory\n): HighlightResult<F> => {\n const { cache, ...rest } = optionsInput ?? {};\n const cacheRef = useRef(cache);\n cacheRef.current = cache;\n\n // This hook owns the line-number/highlight output, so it also owns the\n // corresponding styles. The primitive boolean limits effect reruns.\n useFeatureStyles(\n Boolean(rest.showLineNumbers || rest.highlightLineNumbers?.length)\n );\n\n const stableLang = useStableValue(lang);\n const stableTheme = useStableValue(themeInput);\n const stableOpts = useStableValue(rest);\n\n const resolved = useMemo(() => {\n const { languageId, langsToLoad } = resolveLanguage(\n stableLang,\n // oxlint-disable-next-line typescript/no-deprecated -- the hook still honors its own deprecated prop\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\n return {\n languageId,\n langsToLoad,\n themesToLoad: theme.themesToLoad,\n shikiOptions,\n };\n }, [stableLang, stableTheme, stableOpts]);\n\n const hasCache = Boolean(cache);\n\n const cacheScope = useMemo(\n () =>\n hasCache\n ? JSON.stringify([\n stableOpts.outputFormat ?? 'react',\n resolved.shikiOptions,\n stableOpts.startingLineNumber,\n stableOpts.highlightLineNumbers,\n ])\n : undefined,\n [hasCache, resolved, stableOpts]\n );\n\n const cacheKey = cacheScope && `${cacheScope}\\n${code}`;\n\n const [highlightedCode, setHighlightedCode] = useState(() =>\n readCache<F>(cache, cacheKey)\n );\n\n const shown = useRef({ key: cacheKey, result: highlightedCode });\n\n useEffect(\n () => () => {\n const { key, result } = shown.current;\n\n if (key && result !== null && result !== '') {\n cacheRef.current?.set(key, result);\n }\n },\n []\n );\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 const timeouts = timeoutControl.current;\n\n const show = (result: HighlightResult<F>) => {\n shown.current = { key: cacheKey, result };\n setHighlightedCode(result);\n };\n\n const run = async () => {\n try {\n const result = await highlight<F>(\n code,\n resolved,\n stableOpts,\n highlighterFactoryRef.current\n );\n\n if (requestId === requestIdRef.current) {\n show(result);\n }\n } catch (error) {\n console.error('[react-shiki] highlight failed', error);\n }\n };\n\n const cached = readCache<F>(cacheRef.current, cacheKey);\n\n if (!resolved.languageId) {\n return () => {\n clearTimeout(timeouts.timeoutId);\n };\n }\n\n if (cached !== null) {\n if (shown.current.result !== cached) {\n show(cached);\n }\n } else if (stableOpts.delay) {\n throttleHighlighting(run, timeoutControl, stableOpts.delay);\n } else {\n void run();\n }\n\n return () => {\n clearTimeout(timeouts.timeoutId);\n };\n }, [code, cacheKey, resolved, stableOpts]);\n\n return highlightedCode;\n};\n","import { 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';\nimport { useComponentStyles } from './styles';\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: OutputFormat | undefined\n): HighlighterOptions['outputFormat'] => {\n if (outputFormat !== 'tokens') {\n return outputFormat;\n }\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\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 * 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 forwardRef<HTMLElement, ShikiHighlighterProps>(\n // oxlint-disable-next-line react/display-name -- naming the render function would change the name React DevTools shows for the published component\n (\n {\n language,\n theme,\n addDefaultStyles = true,\n style,\n langStyle,\n className,\n langClassName,\n showLanguage = true,\n children: code,\n as: Element = 'div',\n outputFormat,\n ...options\n },\n ref\n ) => {\n // Kept inside the component so hook-only bundles can tree-shake it\n // together with the component CSS.\n useComponentStyles();\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 {\n ...options,\n outputFormat: resolveOutputFormat(outputFormat),\n }\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 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 // oxlint-disable-next-line react/no-danger -- outputFormat 'html' renders shiki's escaped HTML by design\n dangerouslySetInnerHTML={{ __html: highlightedCode }}\n />\n ) : (\n highlightedCode\n )}\n </Element>\n );\n }\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"],"mappings":";;;;;;;;;;;;;AASA,MAAa,kBAAqB,UAAgB;CAChD,MAAM,MAAM,OAAO,KAAK;CAExB,IAAI,UAAU,IAAI,WAAW,CAAC,OAAO,OAAO,IAAI,OAAO,GACrD,IAAI,UAAU;CAGhB,OAAO,IAAI;AACb;;;;;;;;;;;;AAaA,MAAa,wBACX,kBACA,gBACA,eACG;CACH,aAAa,eAAe,QAAQ,SAAS;CAE7C,MAAM,QAAQ,KAAK,IACjB,GACA,eAAe,QAAQ,kBAAkB,KAAK,IAAI,CACpD;CAEA,eAAe,QAAQ,YAAY,iBAAiB;EAClD,iBAAsB;EACtB,eAAe,QAAQ,kBAAkB,KAAK,IAAI,IAAI;CACxD,GAAG,KAAK;AACV;;AC7CA,IAAA,oBAAA;;ACDA,IAAA,mBAAA;;ACOA,MAAM,2BAAW,IAAI,IAAY;AAEjC,MAAM,0BACJ,MAAM,sBAAsB,MAAM;AAEpC,MAAM,UAAU,KAAa,QAAgB;CAC3C,IAAI,SAAS,IAAI,GAAG,KAAK,OAAO,aAAa,aAC3C;CAGF,SAAS,IAAI,GAAG;CAEhB,IAAI;EAEF,MAAM,QAAQ,IAAI,cAAc;EAChC,MAAM,YAAY,GAAG;EACrB,SAAS,qBAAqB,CAAC,GAAG,SAAS,oBAAoB,KAAK;CACtE,QAAQ;EACN,MAAM,QAAQ,SAAS,cAAc,OAAO;EAC5C,MAAM,QAAQ,aAAa;EAC3B,MAAM,cAAc;EACpB,SAAS,KAAK,OAAO,KAAK;CAC5B;AACF;;AAGA,MAAa,2BAA2B;CACtC,8BAA8B;EAC5B,OAAOA,mBAAc,WAAW;CAClC,GAAG,CAAC,CAAC;AACP;;AAGA,MAAa,oBAAoB,YAAqB;CACpD,8BAA8B;EAC5B,IAAI,SACF,OAAOC,kBAAa,UAAU;CAElC,GAAG,CAAC,OAAO,CAAC;AACd;;ACpCA,MAAa,oBAAoB;AAEjC,MAAM,WAAc,UAAyB;CAC3C,IAAI,SAAS,MACX,OAAO,CAAC;CAGV,OAAO,MAAM,QAAQ,KAAK,IAAI,QAAQ,CAAC,KAAK;AAC9C;AAKA,MAAM,qBACJ,SAEA,OAAO,SAAS,cAChB,gBAAgB,WAChB,MAAM,QAAQ,IAAI,KACjB,OAAO,SAAS,YAAY,QAAQ,QAAQ,aAAa;AAE5D,MAAM,kBACJ,SAEA,OAAO,SAAS,YAAY,QAAQ,QAAQ,CAAC,kBAAkB,IAAI;;AAGrE,MAAM,eAAe,SAAkD;CACrE,IAAI,QAAQ,MACV,OAAO;CAGT,IAAI,OAAO,SAAS,UAClB,OAAO,KAAK;CAGd,IAAI,kBAAkB,IAAI,GACxB,OAAO;CAGT,OAAO,KAAK,KAAK,KAAK,IAAI,KAAK;AACjC;AAEA,MAAM,mBAAmB,UAAgD;CACvE,MAAM,uBAAO,IAAI,IAAqB;CAEtC,OAAO,MAAM,QAAQ,SAAS;EAC5B,MAAM,MAAM,YAAY,IAAI;EAE5B,IAAI,OAAO,QAAQ,KAAK,IAAI,GAAG,GAC7B,OAAO;EAGT,KAAK,IAAI,GAAG;EAEZ,OAAO;CACT,CAAC;AACH;;;;;AAMA,MAAM,uBACJ,WACA,YAEA,QAAQ,UAAU,IAAI,KACtB,QAAQ,UAAU,SAAS,KAC3B,QAAQ,UAAU,WAAW,MAAM,GAAG,CAAC,CAAC,IAAI,CAAC,KAC7C,CAAC,CAAC,UAAU,SAAS,KAAK,OAAO,KACjC,CAAC,CAAC,UAAU,WAAW,KAAK,OAAO;;;;;AAWrC,MAAa,mBACX,MACA,iBACA,aACA,qBACmB;CACnB,MAAM,cAAc,QAAQ,eAAe;CAC3C,MAAM,eAAe,QAAQ,gBAAgB;CAE7C,MAAM,UACJ,YACA,aACoB;EACpB;EACA,aAAa,gBAAgB;GAC3B;GACA,GAAG;GACH,GAAG;EACL,CAAC;CACH;CAEA,IAAI,QAAQ,QAAS,OAAO,SAAS,YAAY,CAAC,KAAK,KAAK,GAC1D,OAAO,OAAO,iBAAiB;CAGjC,IAAI,OAAO,SAAS,UAClB,OAAO,OAAO,KAAK,MAAM,IAAI;CAG/B,MAAM,aAAa,KAAK,KAAK;CAC7B,MAAM,QAAQ,WAAW,YAAY;CAErC,MAAM,WAAW,QACf,KAAK,YAAY,MAAM;CAGzB,MAAM,cAAc,CAAC,GAAG,aAAa,GAAG,YAAY,CAAC,CAClD,OAAO,cAAc,CAAC,CACtB,MAAM,cAAc,oBAAoB,WAAW,OAAO,CAAC;CAE9D,IAAI,aACF,OAAO,OAAO,YAAY,QAAQ,YAAY,WAAW;CAG3D,MAAM,QAAQ,OAAO,QAAQ,eAAe,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,UACrD,QAAQ,IAAI,CACd,CAAC,GAAG;CAEJ,IAAI,OACF,OAAO,OAAO,OAAO,KAAK;CAI5B,OAAO,OAAO,YAAY,UAAU;AACtC;;;;;;AAOA,MAAa,sBACX,MACA,qBACyC;CACzC,IAAI,QAAQ,MACV,OAAO;CAGT,IAAI,OAAO,SAAS,UAClB,OAAO,QAAQ;CAGjB,IAAI,kBAAkB,IAAI,GACxB,OAAO;CAGT,OACE,OAAO,KAAK,SAAS,YAAY,OAAO,KAAK,cAAc;AAE/D;;;;;AAMA,MAAa,yBACX,YACA,oBAEA,cAAc,UAAU,KAAK,gBAAgB,SAAS,UAAU,IAC5D,aACA;AAEN,MAAa,wBACX,MACA,YACA,gBACoB;CACpB,MAAM,UACJ,YAAY,oBAAoB;CAElC,OAAO,uBAAuB,MAAM,UAAU,CAAC,CAAC,SAE7C,aAAa,QAAQ,aAAa,CAAC,CACtC;AACF;;ACnMA,MAAa,iBAAyB;CACpC,OAAO;CACP,MAAM;AACR;AAUA,MAAM,mBACJ,UACkC;CAClC,IAAI,OAAO,UAAU,YAAY,UAAU,MACzC,OAAO;CAGT,MAAM,YACH,iBAAiB,SAAS,MAAM,QAAQ,MAAM,WAAW,KACzD,cAAc,SAAS,MAAM,QAAQ,MAAM,QAAQ;CAEtD,MAAM,cACH,UAAU,SAAS,OAAO,MAAM,SAAS,YACzC,UAAU,SAAS,OAAO,MAAM,SAAS;CAE5C,OAAO,aAAa;AACtB;AAEA,SAAgB,aAAa,YAA2C;CACtE,IACE,OAAO,eAAe,YACtB,eAAe,QACf,gBAAgB,UAAU,GAE1B,OAAO;EACL,SAAS;EACT,OAAO;EACP,cAAc,CAAC,UAAU;CAC3B;CAGF,MAAM,eAAe,OAAO,QAAQ,UAAU,CAAC,CAAC,QAC7C,CAAC,KAAK,WACL,IAAI,KAAK,MAAM,OACb,OAAO,UAAU,YAAY,MAAM,KAAK,MAAM,MAC9C,gBAAgB,KAAK,EAC3B;CAEA,IAAI,aAAa,WAAW,GAAG;EAC7B,QAAQ,KACN,oEACF;EAEA,OAAO;GACL,SAAS;GACT,QAAQ;GACR,cAAc,OAAO,OAAO,cAAc;EAC5C;CACF;CAEA,IAAI,aAAa,WAAW,OAAO,KAAK,UAAU,CAAC,CAAC,QAClD,QAAQ,KACN,+EACF;CAKF,OAAO;EACL,SAAS;EACT,QAJa,OAAO,YAAY,YAI3B;EACL,cAAc,aAAa,KAAK,GAAG,WAAW,KAAK;CACrD;AACF;;AC5EA,MAAM,0BAAU,IAAI,IAAsC;AAI1D,MAAM,UAA0D;CAC9D,YAAY,YAAY;EACtB,MAAM,EAAE,gCACN,MAAM,OAAO;EAEf,OAAO,4BAA4B;CACrC;CACA,WAAW,YAAY;EACrB,MAAM,EAAE,0BACN,MAAM,OAAO;EAEf,OAAO,MAAM,sBAAsB,OAAO,aAAa;CACzD;AACF;;;;;;;;AASA,MAAa,iBACX,WACuC;CACvC,IAAI,OAAO,WAAW,UACpB,OAAO;CAGT,IAAI,CAAC,OAAO,OAAO,SAAS,MAAM,GAChC,MAAM,IAAI,MACR,iCAAiC,OAAO,wCAC1C;CAGF,MAAM,SAAS,QAAQ,IAAI,MAAM;CAEjC,IAAI,QACF,OAAO;CAGT,MAAM,WAAW,QAAQ,OAAO,CAAC;CAGjC,SAAS,YAAY,QAAQ,OAAO,MAAM,CAAC;CAC3C,QAAQ,IAAI,QAAQ,QAAQ;CAE5B,OAAO;AACT;;ACrDA,MAAM,mBAAmB,gBACvB,IAAI,IAAI,YAAY,OAAO,OAAO,SAAS,CAAC;;;;;AAM9C,SAAgB,uBAAuB,YAAY,GAAqB;CACtE,OAAO;EACL,MAAM;EACN,KAAK,MAAM;GACT,KAAK,eAAe,MAAM,qBAAqB;GAE/C,IAAI,cAAc,GAAG;IACnB,MAAM,WAAW,KAAK,YAAY;IAClC,KAAK,aAAa;KAChB,GAAG,KAAK;KACR,OAAO,CAAC,UAAU,iBAAiB,WAAW,CAAC,CAC5C,OAAO,OAAO,CAAC,CACf,KAAK,IAAI;IACd;GACF;EACF;EACA,KAAK,MAAM;GACT,KAAK,eAAe,MAAM,gBAAgB;GAE1C,OAAO;EACT;CACF;AACF;;;;;;AAOA,SAAgB,4BACd,aACA,YAAY,GACM;CAClB,MAAM,mBAAmB,gBAAgB,WAAW;CAEpD,OAAO;EACL,MAAM;EACN,KAAK,MAAM;GACT,KAAK,eAAe,MAAM,0BAA0B;EACtD;EACA,KAAK,MAAM,MAAM;GACf,MAAM,gBAAgB,YAAY,OAAO;GAEzC,IAAI,iBAAiB,IAAI,aAAa,GACpC,KAAK,eAAe,MAAM,qBAAqB;GAGjD,OAAO;EACT;CACF;AACF;;ACzCA,MAAM,qBACJ,eACA,cACA,sBAG0C;CAC1C,IAAI,cAAc,SAChB,OAAO;EACL,QAAQ,cAAc;EACtB;EACA;CACF;CAGF,OAAO,EACL,OAAO,cAAc,MACvB;AACF;AAEA,MAAa,qBACX,YACA,eACA,YACsB;CACtB,MAAM,EACJ,OAEA,iBACA,kBACA,cACA,aACA,WACA,QACA,cACA,mBACA,iBACA,oBACA,sBACA,cAAc,kBACd,GAAG,qBACD;CAEJ,MAAM,eAAe,CAAC,GAAI,oBAAoB,CAAC,CAAE;CAEjD,IAAI,iBACF,aAAa,KAAK,uBAAuB,kBAAkB,CAAC;CAG9D,IAAI,sBAAsB,QACxB,aAAa,KACX,4BACE,sBACA,kBACF,CACF;CAGF,OAAO;EACL,MAAM;EACN,GAAG,kBAAkB,eAAe,cAAc,iBAAiB;EACnE,GAAG;EACH;CACF;AACF;;AClDA,eAAsB,UACpB,MACA,UAMA,MAGA,SACgC;CAChC,MAAM,EAAE,YAAY,aAAa,cAAc,iBAC7C;CAEF,MAAM,SAAuB,KAAK,gBAAgB;CAElD,MAAM,cACJ,KAAK,eACJ,OAAO,YAAY;EAClB,MAAM,KAAK,MAAM,QACf,aACA,cACA,cAAc,KAAK,MAAM,CAC3B;EAEA,MAAM,GAAG,aACP,GAAG,qBAAqB,MAAM,YAAY,EAAE,CAC9C;EAEA,OAAO;CACT,EAAA,CAAG;CAEL,MAAM,WAAW,sBACf,YACA,YAAY,mBAAmB,CACjC;CAEA,MAAM,UAAU;EAAE,GAAG;EAAc,MAAM;CAAS;CAElD,MAAM,UAAU;EACd,aAEE,aAAa,YAAY,WAAW,MAAM,OAAO,GAAG;GAClD;GACA;GACA;EACF,CAAC;EACH,YAAY,YAAY,WAAW,MAAM,OAAO;EAChD,cAAc,YAAY,aAAa,MAAM,OAAO;CACtD;CAKA,QAAQ,QAAQ,WAAW,QAAQ,MAAA,CAAO;AAC5C;AAEA,MAAM,aACJ,OACA,QACuB;CACvB,IAAI,CAAC,KACH,OAAO;CAIT,MAAM,SAAS,OAAO,IAAI,GAAG;CAE7B,OAAO,WAAW,KAAA,KAAa,WAAW,KAAK,OAAO;AACxD;AAEA,MAAa,gBACX,MACA,MACA,YACA,cACA,uBACuB;CACvB,MAAM,EAAE,OAAO,GAAG,SAAS,gBAAgB,CAAC;CAC5C,MAAM,WAAW,OAAO,KAAK;CAC7B,SAAS,UAAU;CAInB,iBACE,QAAQ,KAAK,mBAAmB,KAAK,sBAAsB,MAAM,CACnE;CAEA,MAAM,aAAa,eAAe,IAAI;CACtC,MAAM,cAAc,eAAe,UAAU;CAC7C,MAAM,aAAa,eAAe,IAAI;CAEtC,MAAM,WAAW,cAAc;EAC7B,MAAM,EAAE,YAAY,gBAAgB,gBAClC,YAEA,WAAW,iBACX,WAAW,WACX,WAAW,gBACb;EAEA,MAAM,QAAQ,aAAa,WAAW;EAEtC,MAAM,eAAe,kBAAkB,YAAY,OAAO,UAAU;EAEpE,OAAO;GACL;GACA;GACA,cAAc,MAAM;GACpB;EACF;CACF,GAAG;EAAC;EAAY;EAAa;CAAU,CAAC;CAExC,MAAM,WAAW,QAAQ,KAAK;CAE9B,MAAM,aAAa,cAEf,WACI,KAAK,UAAU;EACb,WAAW,gBAAgB;EAC3B,SAAS;EACT,WAAW;EACX,WAAW;CACb,CAAC,IACD,KAAA,GACN;EAAC;EAAU;EAAU;CAAU,CACjC;CAEA,MAAM,WAAW,cAAc,GAAG,WAAW,IAAI;CAEjD,MAAM,CAAC,iBAAiB,sBAAsB,eAC5C,UAAa,OAAO,QAAQ,CAC9B;CAEA,MAAM,QAAQ,OAAO;EAAE,KAAK;EAAU,QAAQ;CAAgB,CAAC;CAE/D,sBACc;EACV,MAAM,EAAE,KAAK,WAAW,MAAM;EAE9B,IAAI,OAAO,WAAW,QAAQ,WAAW,IACvC,SAAS,SAAS,IAAI,KAAK,MAAM;CAErC,GACA,CAAC,CACH;CAEA,MAAM,eAAe,OAAO,CAAC;CAE7B,MAAM,iBAAiB,OAAqB;EAC1C,iBAAiB;EACjB,WAAW,KAAA;CACb,CAAC;CAID,MAAM,wBAAwB,OAAO,kBAAkB;CACvD,sBAAsB,UAAU;CAEhC,gBAAgB;EACd,MAAM,YAAY,EAAE,aAAa;EACjC,MAAM,WAAW,eAAe;EAEhC,MAAM,QAAQ,WAA+B;GAC3C,MAAM,UAAU;IAAE,KAAK;IAAU;GAAO;GACxC,mBAAmB,MAAM;EAC3B;EAEA,MAAM,MAAM,YAAY;GACtB,IAAI;IACF,MAAM,SAAS,MAAM,UACnB,MACA,UACA,YACA,sBAAsB,OACxB;IAEA,IAAI,cAAc,aAAa,SAC7B,KAAK,MAAM;GAEf,SAAS,OAAO;IACd,QAAQ,MAAM,kCAAkC,KAAK;GACvD;EACF;EAEA,MAAM,SAAS,UAAa,SAAS,SAAS,QAAQ;EAEtD,IAAI,CAAC,SAAS,YACZ,aAAa;GACX,aAAa,SAAS,SAAS;EACjC;EAGF,IAAI,WAAW,MACT;OAAA,MAAM,QAAQ,WAAW,QAC3B,KAAK,MAAM;EAAA,OAER,IAAI,WAAW,OACpB,qBAAqB,KAAK,gBAAgB,WAAW,KAAK;OAE1D,IAAS;EAGX,aAAa;GACX,aAAa,SAAS,SAAS;EACjC;CACF,GAAG;EAAC;EAAM;EAAU;EAAU;CAAU,CAAC;CAEzC,OAAO;AACT;;ACtOA,IAAI,2BAA2B;;;;;;AAO/B,MAAM,uBACJ,iBACuC;CACvC,IAAI,iBAAiB,UACnB,OAAO;CAGT,IAAI,CAAC,0BAA0B;EAC7B,2BAA2B;EAC3B,QAAQ,KACN,gHACF;CACF;CAEA,OAAO;AACT;;;;;AA4EA,MAAa,mCACX,4BAEA,YAGI,EACE,UACA,OACA,mBAAmB,MACnB,OACA,WACA,WACA,eACA,eAAe,MACf,UAAU,MACV,IAAI,UAAU,OACd,cACA,GAAG,WAEL,QACG;CAGH,mBAAmB;CAEnB,MAAM,oBACJ,OAAO,aAAa,WAChB,SAAS,QAAQ,OACjB,UAAU,KAAK,KAAK;CAE1B,MAAM,kBAAkB,wBACtB,MACA,UACA,OACA;EACE,GAAG;EACH,cAAc,oBAAoB,YAAY;CAChD,CACF;CAEA,OACE,qBAAC,SAAD;EACO;EACL,eAAY;EACZ,aAAU;EACV,WAAW,KACT,WACA,aACA,oBAAoB,qBACpB,SACF;EACO;EAVT,UAAA,CAYG,gBAAgB,oBACf,oBAAC,QAAD;GACE,aAAU;GACV,WAAW,KAAK,qBAAqB,aAAa;GAClD,OAAO;GAEN,UAAA;EACG,CAAA,IACJ,MACH,OAAO,oBAAoB,WAC1B,oBAAC,OAAD;GACE,aAAU;GAEV,yBAAyB,EAAE,QAAQ,gBAAgB;EACpD,CAAA,IAED,eAEK;;AAEb,CACF;;;;;;;;;;;;AC5KF,SAAgB,2BAA2B;CACzC,QAAQ,SAA0B;EAChC,MAAM,MAAM,YAAY,MAAe,QAAQ,WAAW;GACxD,IACE,KAAK,YAAY,UACjB,QAAQ,SAAS,aACjB,OAAO,YAAY,OAEnB,KAAK,WAAW,SAAS;EAE7B,CAAC;CACH;AACF;;;;;;;AAQA,MAAa,gBAAgB,SAA2B;CAMtD,OAAO,EALc,KAAK,YAAY,CAAC,EAAA,CACpC,QAAQ,UAAU,MAAM,SAAS,MAAM,CAAC,CACxC,KAAK,UAAU,MAAM,KAAK,CAAC,CAC3B,KAAK,EAEU,CAAC,CAAC,SAAS,IAAI;AACnC"}
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { ReactElement } from "react";
|
|
2
2
|
import { Awaitable, BundledHighlighterOptions, BundledLanguage, BundledTheme, CodeOptionsMultipleThemes, CodeToHastOptions, Highlighter, HighlighterCore, LanguageInput, LanguageRegistration, RegexEngine, SpecialLanguage, StringLiteralUnion, ThemeRegistrationAny, TokensResult } from "shiki";
|
|
3
|
-
//#region ../node_modules/.pnpm/@types+unist@3.0.3/node_modules/@types/unist/index.d.ts
|
|
4
3
|
// ## Interfaces
|
|
5
4
|
/**
|
|
6
5
|
* Info associated with nodes by the ecosystem.
|
|
@@ -81,8 +80,6 @@ interface Node$1 {
|
|
|
81
80
|
*/
|
|
82
81
|
position?: Position | undefined;
|
|
83
82
|
}
|
|
84
|
-
//#endregion
|
|
85
|
-
//#region ../node_modules/.pnpm/@types+hast@3.0.5/node_modules/@types/hast/index.d.ts
|
|
86
83
|
// ## Interfaces
|
|
87
84
|
/**
|
|
88
85
|
* Info associated with hast nodes by the ecosystem.
|
|
@@ -947,8 +944,6 @@ interface Text extends Literal {
|
|
|
947
944
|
* Info associated with hast texts by the ecosystem.
|
|
948
945
|
*/
|
|
949
946
|
interface TextData extends Data {}
|
|
950
|
-
//#endregion
|
|
951
|
-
//#region src/lib/types.d.ts
|
|
952
947
|
/**
|
|
953
948
|
* HTML Element, use to type `node` from react-markdown
|
|
954
949
|
*/
|
|
@@ -1070,6 +1065,21 @@ interface ReactShikiOptions {
|
|
|
1070
1065
|
* });
|
|
1071
1066
|
*/
|
|
1072
1067
|
highlighter?: Highlighter | HighlighterCore;
|
|
1068
|
+
/**
|
|
1069
|
+
* Keeps highlighted output across unmounts, so remounting unchanged code
|
|
1070
|
+
* paints on its first render without highlighting again. Pass a `Map` you
|
|
1071
|
+
* own, shared by every highlighter that should reuse results, and
|
|
1072
|
+
* `clear()` it when the content it holds goes away. Output is stored on
|
|
1073
|
+
* unmount, so partial code from streaming never lands in it.
|
|
1074
|
+
*
|
|
1075
|
+
* @example
|
|
1076
|
+
* const cache = new Map();
|
|
1077
|
+
*
|
|
1078
|
+
* <ShikiHighlighter cache={cache} language="ts" theme="nord">
|
|
1079
|
+
* {code}
|
|
1080
|
+
* </ShikiHighlighter>
|
|
1081
|
+
*/
|
|
1082
|
+
cache?: HighlightCache;
|
|
1073
1083
|
/**
|
|
1074
1084
|
* Whether to show line numbers
|
|
1075
1085
|
* @default false
|
|
@@ -1099,6 +1109,10 @@ interface HighlightResultMap {
|
|
|
1099
1109
|
html: string;
|
|
1100
1110
|
tokens: TokensResult;
|
|
1101
1111
|
}
|
|
1112
|
+
/**
|
|
1113
|
+
* Where highlighted output is kept between mounts; a `Map` satisfies it.
|
|
1114
|
+
*/
|
|
1115
|
+
type HighlightCache = Pick<Map<string, HighlightResultMap[OutputFormat]>, 'get' | 'set'>;
|
|
1102
1116
|
type HighlightResult<F extends OutputFormat = 'react'> = HighlightResultMap[F] | null;
|
|
1103
1117
|
/**
|
|
1104
1118
|
* Per-call options shape: every highlighter option except `outputFormat`,
|
|
@@ -1113,28 +1127,6 @@ type HighlighterOptionsFor<F extends OutputFormat> = Omit<HighlighterOptions, 'o
|
|
|
1113
1127
|
outputFormat?: F;
|
|
1114
1128
|
};
|
|
1115
1129
|
type UseShikiHighlighter = <F extends OutputFormat = 'react'>(code: string, lang: Language, themeInput: Theme | Themes, options?: HighlighterOptionsFor<F>) => HighlightResult<F>;
|
|
1116
|
-
//#endregion
|
|
1117
|
-
//#region src/lib/plugins.d.ts
|
|
1118
|
-
/**
|
|
1119
|
-
* Rehype plugin to add an 'inline' property to <code> elements
|
|
1120
|
-
* Sets 'inline' property to true if the <code> is not within a <pre> tag
|
|
1121
|
-
*
|
|
1122
|
-
* Pass this plugin to the `rehypePlugins` prop of react-markdown
|
|
1123
|
-
* You can then access `inline` as a prop in components passed to react-markdown
|
|
1124
|
-
*
|
|
1125
|
-
* @example
|
|
1126
|
-
* <ReactMarkdown rehypePlugins={[rehypeInlineCodeProperty]} />
|
|
1127
|
-
*/
|
|
1128
|
-
declare function rehypeInlineCodeProperty(): (tree: Root) => undefined;
|
|
1129
|
-
/**
|
|
1130
|
-
* Function to determine if code is inline based on the presence of line breaks
|
|
1131
|
-
*
|
|
1132
|
-
* @example
|
|
1133
|
-
* const isInline = node && isInlineCode(node: Element)
|
|
1134
|
-
*/
|
|
1135
|
-
declare const isInlineCode: (node: Element) => boolean;
|
|
1136
|
-
//#endregion
|
|
1137
|
-
//#region src/lib/component.d.ts
|
|
1138
1130
|
/**
|
|
1139
1131
|
* Props for the ShikiHighlighter component
|
|
1140
1132
|
*/
|
|
@@ -1195,6 +1187,22 @@ interface ShikiHighlighterProps extends HighlighterOptions {
|
|
|
1195
1187
|
*/
|
|
1196
1188
|
as?: React.ElementType;
|
|
1197
1189
|
}
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
|
-
|
|
1190
|
+
/**
|
|
1191
|
+
* Rehype plugin to add an 'inline' property to <code> elements
|
|
1192
|
+
* Sets 'inline' property to true if the <code> is not within a <pre> tag
|
|
1193
|
+
*
|
|
1194
|
+
* Pass this plugin to the `rehypePlugins` prop of react-markdown
|
|
1195
|
+
* You can then access `inline` as a prop in components passed to react-markdown
|
|
1196
|
+
*
|
|
1197
|
+
* @example
|
|
1198
|
+
* <ReactMarkdown rehypePlugins={[rehypeInlineCodeProperty]} />
|
|
1199
|
+
*/
|
|
1200
|
+
declare function rehypeInlineCodeProperty(): (tree: Root) => undefined;
|
|
1201
|
+
/**
|
|
1202
|
+
* Function to determine if code is inline based on the presence of line breaks
|
|
1203
|
+
*
|
|
1204
|
+
* @example
|
|
1205
|
+
* const isInline = node && isInlineCode(node: Element)
|
|
1206
|
+
*/
|
|
1207
|
+
declare const isInlineCode: (node: Element) => boolean;
|
|
1208
|
+
export { EngineName as a, HighlighterOptions as c, OutputFormat as d, PreloadLanguage as f, UseShikiHighlighter as h, Element as i, HighlighterOptionsFor as l, Themes as m, rehypeInlineCodeProperty as n, HighlightCache as o, Theme as p, ShikiHighlighterProps as r, HighlightResult as s, isInlineCode as t, Language as u };
|
package/dist/web.d.mts
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
|
-
import { a as EngineName, c as
|
|
1
|
+
import { a as EngineName, c as HighlighterOptions, d as OutputFormat, f as PreloadLanguage, h as UseShikiHighlighter, i as Element, l as HighlighterOptionsFor, m as Themes, n as rehypeInlineCodeProperty, o as HighlightCache, p as Theme, r as ShikiHighlighterProps, s as HighlightResult, t as isInlineCode, u as Language } from "./plugins-DdjjMnbY.mjs";
|
|
2
2
|
import { createJavaScriptRawEngine, createJavaScriptRegexEngine } from "shiki/engine/javascript";
|
|
3
3
|
import { LanguageRegistration, TokensResult } from "shiki/bundle/web";
|
|
4
|
-
//#region src/web.d.ts
|
|
5
4
|
/**
|
|
6
5
|
* Highlight code with shiki (web bundle)
|
|
7
6
|
*
|
|
@@ -31,6 +30,4 @@ export declare const useShikiHighlighter: UseShikiHighlighter;
|
|
|
31
30
|
* Includes web-focused languages for balanced size and functionality.
|
|
32
31
|
*/
|
|
33
32
|
export declare const ShikiHighlighter: import("react").ForwardRefExoticComponent<ShikiHighlighterProps & import("react").RefAttributes<HTMLElement>>;
|
|
34
|
-
|
|
35
|
-
export { type Element, type EngineName, type HighlightResult, type HighlighterOptions, type HighlighterOptionsFor, type Language, type LanguageRegistration, type OutputFormat, type PreloadLanguage, ShikiHighlighter as default, type ShikiHighlighterProps, type Theme, type Themes, type TokensResult, type UseShikiHighlighter, createJavaScriptRawEngine, createJavaScriptRegexEngine, isInlineCode, rehypeInlineCodeProperty };
|
|
36
|
-
//# sourceMappingURL=web.d.mts.map
|
|
33
|
+
export { type Element, type EngineName, type HighlightCache, type HighlightResult, type HighlighterOptions, type HighlighterOptionsFor, type Language, type LanguageRegistration, type OutputFormat, type PreloadLanguage, ShikiHighlighter as default, type ShikiHighlighterProps, type Theme, type Themes, type TokensResult, type UseShikiHighlighter, createJavaScriptRawEngine, createJavaScriptRegexEngine, isInlineCode, rehypeInlineCodeProperty };
|
package/dist/web.mjs
CHANGED
|
@@ -1,16 +1,15 @@
|
|
|
1
|
-
import { i as useHighlight, n as
|
|
2
|
-
import { t as bundledHighlighterFactory } from "./factory-
|
|
1
|
+
import { i as useHighlight, n as rehypeInlineCodeProperty, r as createShikiHighlighterComponent, t as isInlineCode } from "./plugins-C0Uf67_P.mjs";
|
|
2
|
+
import { t as bundledHighlighterFactory } from "./factory-Ds5IsDw-.mjs";
|
|
3
3
|
import { createJavaScriptRawEngine, createJavaScriptRegexEngine } from "shiki/engine/javascript";
|
|
4
4
|
import { bundledLanguages, getSingletonHighlighter } from "shiki/bundle/web";
|
|
5
|
-
|
|
5
|
+
// src/bundles/web.ts
|
|
6
6
|
/**
|
|
7
7
|
* Highlighter factory for the web Shiki bundle: web-focused languages
|
|
8
8
|
* (HTML, CSS, JS, TS, JSON, Markdown, Vue, JSX, Svelte, etc.) at a
|
|
9
9
|
* fraction of the full bundle's size.
|
|
10
10
|
*/
|
|
11
11
|
const createWebHighlighter = bundledHighlighterFactory(getSingletonHighlighter, bundledLanguages);
|
|
12
|
-
|
|
13
|
-
//#region src/web.ts
|
|
12
|
+
// src/web.ts
|
|
14
13
|
/**
|
|
15
14
|
* Highlight code with shiki (web bundle)
|
|
16
15
|
*
|
|
@@ -40,7 +39,6 @@ const useShikiHighlighter = (code, lang, themeInput, options = {}) => useHighlig
|
|
|
40
39
|
* Includes web-focused languages for balanced size and functionality.
|
|
41
40
|
*/
|
|
42
41
|
const ShikiHighlighter = /*#__PURE__*/ createShikiHighlighterComponent(useShikiHighlighter);
|
|
43
|
-
//#endregion
|
|
44
42
|
export { ShikiHighlighter, ShikiHighlighter as default, createJavaScriptRawEngine, createJavaScriptRegexEngine, isInlineCode, rehypeInlineCodeProperty, useShikiHighlighter };
|
|
45
43
|
|
|
46
44
|
//# sourceMappingURL=web.mjs.map
|
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} from 'shiki/bundle/web';\nimport { bundledHighlighterFactory } from './factory';\n\n/**\n * Highlighter factory for the web Shiki bundle: web-focused languages\n * (HTML, CSS, JS, TS, JSON, Markdown, Vue, JSX, Svelte, etc.) at a\n * fraction of the full bundle's size.\n */\nexport const createWebHighlighter = bundledHighlighterFactory(\n getSingletonHighlighter,\n bundledLanguages\n);\n","import { useHighlight } from './lib/hook';\nimport { createWebHighlighter } from './bundles/web';\nimport type { UseShikiHighlighter } from './lib/types';\
|
|
1
|
+
{"version":3,"file":"web.mjs","names":[],"sources":["../src/bundles/web.ts","../src/web.ts"],"sourcesContent":["import {\n getSingletonHighlighter,\n bundledLanguages,\n} from 'shiki/bundle/web';\nimport { bundledHighlighterFactory } from './factory';\n\n/**\n * Highlighter factory for the web Shiki bundle: web-focused languages\n * (HTML, CSS, JS, TS, JSON, Markdown, Vue, JSX, Svelte, etc.) at a\n * fraction of the full bundle's size.\n */\nexport const createWebHighlighter = bundledHighlighterFactory(\n getSingletonHighlighter,\n bundledLanguages\n);\n","import { useHighlight } from './lib/hook';\nimport { createWebHighlighter } from './bundles/web';\nimport type { UseShikiHighlighter } from './lib/types';\nimport { createShikiHighlighterComponent } from './lib/component';\n// oxlint-disable-next-line unicorn/prefer-export-from -- `export type … from` here makes tsdown fold the core entry's declarations into the shared .d.mts chunk\nimport type { ShikiHighlighterProps } from './lib/component';\n\nexport { isInlineCode, rehypeInlineCodeProperty } from './lib/plugins';\n\nexport type { ShikiHighlighterProps };\n\nexport type {\n UseShikiHighlighter,\n Language,\n PreloadLanguage,\n Theme,\n Themes,\n Element,\n EngineName,\n HighlighterOptions,\n HighlighterOptionsFor,\n HighlightCache,\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 =\n // oxlint-disable-next-line eslint/no-inline-comments -- the #__PURE__ annotation lets bundlers drop the component when only the hook is used\n /*#__PURE__*/ createShikiHighlighterComponent(useShikiHighlighter);\n\nexport default ShikiHighlighter;\n"],"mappings":";;;;;;;;;;AAWA,MAAa,uBAAuB,0BAClC,yBACA,gBACF;;;;;;;;;;;;;;;;;;;;;;;;;AC6CA,MAAa,uBACX,MACA,MACA,YACA,UAAU,CAAC,MACR,aAAa,MAAM,MAAM,YAAY,SAAS,oBAAoB;;;;;AAMvE,MAAa,mBAEG,8CAAgC,mBAAmB"}
|
package/package.json
CHANGED
|
@@ -1,7 +1,17 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "react-shiki",
|
|
3
|
+
"version": "0.11.3",
|
|
3
4
|
"description": "Syntax highlighter component for react using shiki",
|
|
4
|
-
"
|
|
5
|
+
"keywords": [
|
|
6
|
+
"code",
|
|
7
|
+
"highlighter",
|
|
8
|
+
"react",
|
|
9
|
+
"react-syntax-highlighter",
|
|
10
|
+
"shiki",
|
|
11
|
+
"syntax",
|
|
12
|
+
"syntax-highlighter"
|
|
13
|
+
],
|
|
14
|
+
"homepage": "https://react-shiki.vercel.app/",
|
|
5
15
|
"license": "MIT",
|
|
6
16
|
"author": {
|
|
7
17
|
"name": "Bassim Shahidy",
|
|
@@ -10,33 +20,19 @@
|
|
|
10
20
|
},
|
|
11
21
|
"repository": {
|
|
12
22
|
"type": "git",
|
|
13
|
-
"
|
|
14
|
-
"
|
|
23
|
+
"url": "git+https://github.com/AVGVSTVS96/react-shiki.git",
|
|
24
|
+
"directory": "packages/react-shiki"
|
|
15
25
|
},
|
|
16
|
-
"homepage": "https://react-shiki.vercel.app/",
|
|
17
|
-
"keywords": [
|
|
18
|
-
"react",
|
|
19
|
-
"shiki",
|
|
20
|
-
"code",
|
|
21
|
-
"syntax",
|
|
22
|
-
"highlighter",
|
|
23
|
-
"syntax-highlighter",
|
|
24
|
-
"react-syntax-highlighter"
|
|
25
|
-
],
|
|
26
|
-
"type": "module",
|
|
27
|
-
"main": "./dist/index.mjs",
|
|
28
|
-
"types": "./dist/index.d.mts",
|
|
29
26
|
"files": [
|
|
30
27
|
"dist",
|
|
31
28
|
"src/styles/css.d.ts"
|
|
32
29
|
],
|
|
30
|
+
"type": "module",
|
|
33
31
|
"sideEffects": [
|
|
34
32
|
"**/*.css"
|
|
35
33
|
],
|
|
36
|
-
"
|
|
37
|
-
|
|
38
|
-
"provenance": true
|
|
39
|
-
},
|
|
34
|
+
"main": "./dist/index.mjs",
|
|
35
|
+
"types": "./dist/index.d.mts",
|
|
40
36
|
"exports": {
|
|
41
37
|
".": {
|
|
42
38
|
"types": "./dist/index.d.mts",
|
|
@@ -55,19 +51,9 @@
|
|
|
55
51
|
"default": "./dist/style.css"
|
|
56
52
|
}
|
|
57
53
|
},
|
|
58
|
-
"
|
|
59
|
-
"
|
|
60
|
-
"
|
|
61
|
-
"react": ">=16.14.0",
|
|
62
|
-
"react-dom": ">=16.14.0"
|
|
63
|
-
},
|
|
64
|
-
"peerDependenciesMeta": {
|
|
65
|
-
"@types/react": {
|
|
66
|
-
"optional": true
|
|
67
|
-
},
|
|
68
|
-
"@types/react-dom": {
|
|
69
|
-
"optional": true
|
|
70
|
-
}
|
|
54
|
+
"publishConfig": {
|
|
55
|
+
"access": "public",
|
|
56
|
+
"provenance": true
|
|
71
57
|
},
|
|
72
58
|
"dependencies": {
|
|
73
59
|
"clsx": "^2.1.1",
|
|
@@ -78,34 +64,44 @@
|
|
|
78
64
|
},
|
|
79
65
|
"devDependencies": {
|
|
80
66
|
"@arethetypeswrong/core": "^0.18.5",
|
|
67
|
+
"@testing-library/dom": "^10.4.2",
|
|
81
68
|
"@testing-library/jest-dom": "^7.0.1",
|
|
82
69
|
"@testing-library/react": "^16.3.3",
|
|
83
|
-
"@tsdown/css": "^0.23.
|
|
70
|
+
"@tsdown/css": "^0.23.7",
|
|
84
71
|
"@types/hast": "^3.0.5",
|
|
85
|
-
"@types/jest": "^30.0.0",
|
|
86
72
|
"@types/node": "25.9.9",
|
|
87
73
|
"@types/react": "^19.3.0",
|
|
88
|
-
"@vitejs/plugin-react": "^6.1.2",
|
|
89
|
-
"html-react-parser": "^6.1.8",
|
|
90
|
-
"jsdom": "^30.1.2",
|
|
91
|
-
"publint": "^0.3.25",
|
|
92
|
-
"rolldown": "^1.2.12",
|
|
93
|
-
"tsdown": "^0.23.0",
|
|
94
|
-
"vitest": "^5.0.3",
|
|
95
|
-
"react": "^19.3.0",
|
|
96
|
-
"react-dom": "^19.3.0",
|
|
97
74
|
"@types/react-dom": "^19.3.0",
|
|
98
|
-
"@
|
|
75
|
+
"@vitejs/plugin-react": "^6.2.1",
|
|
76
|
+
"html-react-parser": "^6.1.9",
|
|
77
|
+
"jsdom": "^31.0.0",
|
|
78
|
+
"publint": "^0.3.25",
|
|
79
|
+
"react": "^19.3.1",
|
|
80
|
+
"react-dom": "^19.3.1",
|
|
81
|
+
"rolldown": "^1.3.0",
|
|
82
|
+
"tsdown": "^0.23.7",
|
|
83
|
+
"vitest": "^5.1.0"
|
|
84
|
+
},
|
|
85
|
+
"peerDependencies": {
|
|
86
|
+
"@types/react": ">=16.14.0",
|
|
87
|
+
"@types/react-dom": ">=16.14.0",
|
|
88
|
+
"react": ">=16.14.0",
|
|
89
|
+
"react-dom": ">=16.14.0"
|
|
90
|
+
},
|
|
91
|
+
"peerDependenciesMeta": {
|
|
92
|
+
"@types/react": {
|
|
93
|
+
"optional": true
|
|
94
|
+
},
|
|
95
|
+
"@types/react-dom": {
|
|
96
|
+
"optional": true
|
|
97
|
+
}
|
|
99
98
|
},
|
|
100
99
|
"scripts": {
|
|
101
100
|
"dev": "tsdown --watch",
|
|
102
101
|
"build": "tsdown",
|
|
103
|
-
"check:package": "node scripts/check-package.
|
|
104
|
-
"test": "vitest",
|
|
102
|
+
"check:package": "node scripts/check-package.ts",
|
|
103
|
+
"test": "vitest run",
|
|
105
104
|
"bench": "vitest bench --typecheck.enabled=false",
|
|
106
|
-
"
|
|
107
|
-
"lint:fix": "biome lint --write .",
|
|
108
|
-
"format": "biome format --write .",
|
|
109
|
-
"check": "tsc && biome check ."
|
|
105
|
+
"typecheck": "tsc"
|
|
110
106
|
}
|
|
111
107
|
}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"component-Cf8Topxy.mjs","names":["componentCss","featuresCss"],"sources":["../src/lib/utils.ts","../src/styles/component.css?inline","../src/styles/features.css?inline","../src/lib/styles.ts","../src/lib/language.ts","../src/lib/theme.ts","../src/lib/engine.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: { current: 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","/* Unlayered :where() defaults: immune to layered resets, overridden by any user rule. */\n:where(.rs-root) {\n position: relative;\n}\n\n:where(.rs-root.rs-default-styles pre) {\n overflow: auto;\n border-radius: 0.5rem;\n padding-inline: 1.5rem;\n padding-block: 1.25rem;\n}\n\n:where(.rs-root .rs-language-label) {\n position: absolute;\n z-index: 1;\n right: 0.75rem;\n top: 0.5rem;\n font-family: monospace;\n font-size: 0.75rem;\n letter-spacing: -0.05em;\n color: rgba(107, 114, 128, 0.85);\n}\n",":where(.rs-has-line-numbers) {\n counter-reset: line-number calc(var(--line-start, 1) - 1);\n}\n\n:where(.rs-has-line-numbers .rs-line-number)::before {\n counter-increment: line-number;\n content: counter(line-number);\n display: inline-flex;\n justify-content: flex-end;\n align-items: flex-start;\n box-sizing: content-box;\n min-width: var(--rs-line-numbers-width, 2ch);\n padding-left: var(--rs-line-numbers-padding-left, 0ch);\n padding-right: var(--rs-line-numbers-padding-right, 2ch);\n color: var(--rs-line-numbers-foreground, rgba(107, 114, 128, 0.5));\n font-size: var(--rs-line-numbers-font-size, inherit);\n font-weight: var(--rs-line-numbers-font-weight, inherit);\n line-height: var(--rs-line-numbers-line-height, inherit);\n font-family: var(--rs-line-numbers-font-family, inherit);\n opacity: var(--rs-line-numbers-opacity, 1);\n user-select: none;\n pointer-events: none;\n}\n\n:where(.rs-has-highlighted-lines) {\n display: block;\n width: fit-content;\n min-width: 100%;\n}\n\n:where(.rs-highlighted-line) {\n --_rs-hl-background: var(\n --rs-highlighted-line-background,\n color-mix(in srgb, currentColor 10%, transparent)\n );\n display: inline-block;\n width: 100%;\n min-height: 1lh;\n background-color: var(--_rs-hl-background);\n}\n\n/* Full bleed: the 100vw outset paints into the pre's padding, clipped by its overflow. */\n:where(.rs-default-styles .rs-highlighted-line) {\n background-color: transparent;\n border-image:\n linear-gradient(var(--_rs-hl-background) 0 0) fill 0 / 0 / 0 100vw;\n}\n\n:where(.rs-has-line-numbers .rs-highlighted-line)::before {\n color: var(\n --rs-highlighted-line-number-foreground,\n color-mix(in srgb, currentColor 65%, transparent)\n );\n}\n","import componentCss from '../styles/component.css?inline';\nimport featuresCss from '../styles/features.css?inline';\nimport * as React from 'react';\n// Side-effect import so tsdown emits dist/style.css for the ./css export;\n// stripped from the built JS, guarded by check:package\nimport '../styles/index.css';\n\nconst injected = new Set<string>();\nconst useStyleInsertionEffect =\n React.useInsertionEffect ?? React.useLayoutEffect;\n\nconst inject = (css: string, key: string) => {\n if (injected.has(key) || typeof document === 'undefined') return;\n injected.add(key);\n try {\n // Not governed by CSP style-src in current browsers, unlike the <style> fallback\n const sheet = new CSSStyleSheet();\n sheet.replaceSync(css);\n document.adoptedStyleSheets = [...document.adoptedStyleSheets, sheet];\n } catch {\n const style = document.createElement('style');\n style.dataset.reactShiki = key;\n style.textContent = css;\n document.head.append(style);\n }\n};\n\n/** Installs component styles before layout without triggering a render. */\nexport const useComponentStyles = () => {\n useStyleInsertionEffect(() => {\n inject(componentCss, 'component');\n }, []);\n};\n\n/** Installs line-number/highlight styles only when those options are used. */\nexport const useFeatureStyles = (enabled: boolean) => {\n useStyleInsertionEffect(() => {\n if (enabled) inject(featuresCss, 'features');\n }, [enabled]);\n};\n","import type { Language, PreloadLanguage } 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\nconst toArray = <T>(value?: T | T[]): T[] =>\n value == null ? [] : Array.isArray(value) ? value : [value];\n\n/** Deferred `LanguageInput` forms (getter, promise, module, array) that shiki resolves itself */\ntype DynamicLanguage = Exclude<LanguageInput, LanguageRegistration>;\n\nconst isDynamicLanguage = (\n lang: PreloadLanguage\n): lang is DynamicLanguage =>\n typeof lang === 'function' ||\n lang instanceof Promise ||\n Array.isArray(lang) ||\n (typeof lang === 'object' && lang != null && 'default' in lang);\n\nconst isRegistration = (\n lang: PreloadLanguage\n): lang is LanguageRegistration =>\n typeof lang === 'object' && lang != null && !isDynamicLanguage(lang);\n\n/** Dedupe identity: strings and registrations by value, dynamic inputs by reference */\nconst languageKey = (lang: PreloadLanguage): string | object | null => {\n if (lang == null) return null;\n if (typeof lang === 'string') return `s:${lang}`;\n if (isDynamicLanguage(lang)) return lang;\n return `o:${lang.name}::${lang.scopeName}`;\n};\n\nconst dedupeLanguages = (langs: PreloadLanguage[]): PreloadLanguage[] => {\n const seen = new Set<string | object>();\n return langs.filter((lang) => {\n const key = languageKey(lang);\n if (key == null || seen.has(key)) return false;\n seen.add(key);\n return true;\n });\n};\n\n/**\n * A registration matches a query by name, scopeName (full or its last\n * segment, e.g. \"source.tsx\" -> \"tsx\"), aliases, or fileTypes.\n */\nconst registrationMatches = (\n candidate: LanguageRegistration,\n matches: (str: string | undefined) => boolean\n): boolean =>\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\ntype LanguageResult = {\n languageId: string;\n langsToLoad: PreloadLanguage[];\n};\n\n/**\n * Resolves the language prop to a shiki language id plus everything the\n * factory should load (primary, preloaded, and custom grammars, deduped).\n */\nexport const resolveLanguage = (\n lang: Language,\n customLanguages?: LanguageRegistration | LanguageRegistration[],\n langAliases?: Record<string, string>,\n preloadLanguages?: PreloadLanguage | PreloadLanguage[]\n): LanguageResult => {\n const customLangs = toArray(customLanguages);\n const preloadLangs = toArray(preloadLanguages);\n\n const result = (\n languageId: string,\n primary?: Language\n ): LanguageResult => ({\n languageId,\n langsToLoad: dedupeLanguages([\n primary,\n ...preloadLangs,\n ...customLangs,\n ]),\n });\n\n if (lang == null || (typeof lang === 'string' && !lang.trim())) {\n return result(FALLBACK_LANGUAGE);\n }\n\n if (typeof lang === 'object') {\n return result(lang.name, lang);\n }\n\n const normalized = lang.trim();\n const query = normalized.toLowerCase();\n const matches = (str: string | undefined): boolean =>\n str?.toLowerCase() === query;\n\n // Custom registrations (from both customLanguages and preloadLanguages)\n const customMatch = [...customLangs, ...preloadLangs]\n .filter(isRegistration)\n .find((candidate) => registrationMatches(candidate, matches));\n if (customMatch) {\n return result(customMatch.name || normalized, customMatch);\n }\n\n const alias = Object.entries(langAliases ?? {}).find(([name]) =>\n matches(name)\n )?.[1];\n if (alias) {\n return result(alias, alias);\n }\n\n // Any other string passes through to the factory\n return result(normalized, normalized);\n};\n\n/**\n * Used in factories to check if language is supported.\n * Objects are validated as grammar registrations (name + scopeName);\n * dynamic imports pass through for shiki to resolve.\n */\nexport const isLoadableLanguage = <T extends string>(\n lang: PreloadLanguage,\n bundledLanguages: Record<T, DynamicImportLanguageRegistration>\n): lang is NonNullable<PreloadLanguage> => {\n if (lang == null) return false;\n if (typeof lang === 'string') return lang in bundledLanguages;\n if (isDynamicLanguage(lang)) return true;\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\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","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 { Awaitable, RegexEngine } from 'shiki/core';\nimport type { EngineName } from './types';\n\nconst engines = new Map<EngineName, Promise<RegexEngine>>();\n\n// Dynamic imports keep unused engines out of the loaded graph: naming an\n// engine loads only that engine, and 'javascript' never fetches the wasm.\nconst loaders: Record<EngineName, () => Promise<RegexEngine>> = {\n javascript: () =>\n import('shiki/engine/javascript').then((m) =>\n m.createJavaScriptRegexEngine()\n ),\n oniguruma: () =>\n import('shiki/engine/oniguruma').then((m) =>\n m.createOnigurumaEngine(import('shiki/wasm'))\n ),\n};\n\n/**\n * Resolves a named engine to a lazily created, cached instance.\n * Engine instances and undefined pass through untouched.\n *\n * The cache makes named engines referentially stable across renders and\n * call sites, unlike inline `createJavaScriptRegexEngine()` calls.\n */\nexport const resolveEngine = (\n engine: EngineName | Awaitable<RegexEngine> | undefined\n): Awaitable<RegexEngine> | undefined => {\n if (typeof engine !== 'string') return engine;\n\n const load = loaders[engine];\n if (!load) {\n throw new Error(\n `[react-shiki] unknown engine '${engine}', expected 'javascript' or 'oniguruma'`\n );\n }\n\n const cached = engines.get(engine);\n if (cached) return cached;\n\n const instance = load();\n // Evict rejected loads (e.g. transient wasm fetch failure) so the\n // next highlight retries instead of reusing the cached rejection.\n instance.catch(() => engines.delete(engine));\n engines.set(engine, instance);\n return instance;\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 existing = node.properties?.style as string | undefined;\n node.properties = {\n ...node.properties,\n style: [existing, `--line-start: ${startLine}`]\n .filter(Boolean)\n .join('; '),\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 PreloadLanguage,\n Theme,\n Themes,\n TimeoutState,\n} from './types';\n\nimport { throttleHighlighting, useStableValue } from './utils';\nimport { useFeatureStyles } from './styles';\nimport {\n getEmbeddedLanguages,\n resolveLanguage,\n resolveLoadedLanguage,\n} from './language';\nimport { resolveTheme } from './theme';\nimport { resolveEngine } from './engine';\nimport { buildShikiOptions } from './options';\n\nexport async function highlight<F extends OutputFormat = 'react'>(\n code: string,\n resolved: {\n languageId: string;\n langsToLoad: PreloadLanguage[];\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(\n langsToLoad,\n themesToLoad,\n resolveEngine(opts.engine)\n );\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 // This hook owns the line-number/highlight output, so it also owns the\n // corresponding styles. The primitive boolean limits effect reruns.\n useFeatureStyles(\n Boolean(\n options.showLineNumbers || options.highlightLineNumbers?.length\n )\n );\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 { 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';\nimport { useComponentStyles } from './styles';\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 * 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 addDefaultStyles = true,\n style,\n langStyle,\n className,\n langClassName,\n showLanguage = true,\n children: code,\n as: Element = 'div',\n outputFormat,\n ...options\n },\n ref\n ) => {\n // Kept inside the component so hook-only bundles can tree-shake it\n // together with the component CSS.\n useComponentStyles();\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 {\n ...options,\n outputFormat: resolveOutputFormat(outputFormat),\n }\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 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":";;;;;;;;;;;;;AASA,MAAa,kBAAqB,UAAgB;CAChD,MAAM,MAAM,OAAO,KAAK;CAExB,IAAI,UAAU,IAAI,WAAW,CAAC,OAAO,OAAO,IAAI,OAAO,GACrD,IAAI,UAAU;CAGhB,OAAO,IAAI;AACb;;;;;;;;;;;;AAaA,MAAa,wBACX,kBACA,gBACA,eACG;CACH,aAAa,eAAe,QAAQ,SAAS;CAE7C,MAAM,QAAQ,KAAK,IACjB,GACA,eAAe,QAAQ,kBAAkB,KAAK,IAAI,CACpD;CACA,eAAe,QAAQ,YAAY,iBAAiB;EAClD,iBAAiB;EACjB,eAAe,QAAQ,kBAAkB,KAAK,IAAI,IAAI;CACxD,GAAG,KAAK;AACV;;;AC5CA,IAAA,oBAAA;;;ACDA,IAAA,mBAAA;;;ACOA,MAAM,2BAAW,IAAI,IAAY;AACjC,MAAM,0BACJ,MAAM,sBAAsB,MAAM;AAEpC,MAAM,UAAU,KAAa,QAAgB;CAC3C,IAAI,SAAS,IAAI,GAAG,KAAK,OAAO,aAAa,aAAa;CAC1D,SAAS,IAAI,GAAG;CAChB,IAAI;EAEF,MAAM,QAAQ,IAAI,cAAc;EAChC,MAAM,YAAY,GAAG;EACrB,SAAS,qBAAqB,CAAC,GAAG,SAAS,oBAAoB,KAAK;CACtE,QAAQ;EACN,MAAM,QAAQ,SAAS,cAAc,OAAO;EAC5C,MAAM,QAAQ,aAAa;EAC3B,MAAM,cAAc;EACpB,SAAS,KAAK,OAAO,KAAK;CAC5B;AACF;;AAGA,MAAa,2BAA2B;CACtC,8BAA8B;EAC5B,OAAOA,mBAAc,WAAW;CAClC,GAAG,CAAC,CAAC;AACP;;AAGA,MAAa,oBAAoB,YAAqB;CACpD,8BAA8B;EAC5B,IAAI,SAAS,OAAOC,kBAAa,UAAU;CAC7C,GAAG,CAAC,OAAO,CAAC;AACd;;;AC7BA,MAAa,oBAAoB;AAEjC,MAAM,WAAc,UAClB,SAAS,OAAO,CAAC,IAAI,MAAM,QAAQ,KAAK,IAAI,QAAQ,CAAC,KAAK;AAK5D,MAAM,qBACJ,SAEA,OAAO,SAAS,cAChB,gBAAgB,WAChB,MAAM,QAAQ,IAAI,KACjB,OAAO,SAAS,YAAY,QAAQ,QAAQ,aAAa;AAE5D,MAAM,kBACJ,SAEA,OAAO,SAAS,YAAY,QAAQ,QAAQ,CAAC,kBAAkB,IAAI;;AAGrE,MAAM,eAAe,SAAkD;CACrE,IAAI,QAAQ,MAAM,OAAO;CACzB,IAAI,OAAO,SAAS,UAAU,OAAO,KAAK;CAC1C,IAAI,kBAAkB,IAAI,GAAG,OAAO;CACpC,OAAO,KAAK,KAAK,KAAK,IAAI,KAAK;AACjC;AAEA,MAAM,mBAAmB,UAAgD;CACvE,MAAM,uBAAO,IAAI,IAAqB;CACtC,OAAO,MAAM,QAAQ,SAAS;EAC5B,MAAM,MAAM,YAAY,IAAI;EAC5B,IAAI,OAAO,QAAQ,KAAK,IAAI,GAAG,GAAG,OAAO;EACzC,KAAK,IAAI,GAAG;EACZ,OAAO;CACT,CAAC;AACH;;;;;AAMA,MAAM,uBACJ,WACA,YAEA,QAAQ,UAAU,IAAI,KACtB,QAAQ,UAAU,SAAS,KAC3B,QAAQ,UAAU,WAAW,MAAM,GAAG,CAAC,CAAC,IAAI,CAAC,KAC7C,CAAC,CAAC,UAAU,SAAS,KAAK,OAAO,KACjC,CAAC,CAAC,UAAU,WAAW,KAAK,OAAO;;;;;AAWrC,MAAa,mBACX,MACA,iBACA,aACA,qBACmB;CACnB,MAAM,cAAc,QAAQ,eAAe;CAC3C,MAAM,eAAe,QAAQ,gBAAgB;CAE7C,MAAM,UACJ,YACA,aACoB;EACpB;EACA,aAAa,gBAAgB;GAC3B;GACA,GAAG;GACH,GAAG;EACL,CAAC;CACH;CAEA,IAAI,QAAQ,QAAS,OAAO,SAAS,YAAY,CAAC,KAAK,KAAK,GAC1D,OAAO,OAAO,iBAAiB;CAGjC,IAAI,OAAO,SAAS,UAClB,OAAO,OAAO,KAAK,MAAM,IAAI;CAG/B,MAAM,aAAa,KAAK,KAAK;CAC7B,MAAM,QAAQ,WAAW,YAAY;CACrC,MAAM,WAAW,QACf,KAAK,YAAY,MAAM;CAGzB,MAAM,cAAc,CAAC,GAAG,aAAa,GAAG,YAAY,CAAC,CAClD,OAAO,cAAc,CAAC,CACtB,MAAM,cAAc,oBAAoB,WAAW,OAAO,CAAC;CAC9D,IAAI,aACF,OAAO,OAAO,YAAY,QAAQ,YAAY,WAAW;CAG3D,MAAM,QAAQ,OAAO,QAAQ,eAAe,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,UACrD,QAAQ,IAAI,CACd,CAAC,GAAG;CACJ,IAAI,OACF,OAAO,OAAO,OAAO,KAAK;CAI5B,OAAO,OAAO,YAAY,UAAU;AACtC;;;;;;AAOA,MAAa,sBACX,MACA,qBACyC;CACzC,IAAI,QAAQ,MAAM,OAAO;CACzB,IAAI,OAAO,SAAS,UAAU,OAAO,QAAQ;CAC7C,IAAI,kBAAkB,IAAI,GAAG,OAAO;CACpC,OACE,OAAO,KAAK,SAAS,YAAY,OAAO,KAAK,cAAc;AAE/D;;;;;AAMA,MAAa,yBACX,YACA,oBAEA,cAAc,UAAU,KAAK,gBAAgB,SAAS,UAAU,IAC5D,aACA;AAEN,MAAa,wBACX,MACA,YACA,gBACoB;CACpB,MAAM,UACJ,YAAY,oBAAoB;CAClC,OAAO,uBAAuB,MAAM,UAAU,CAAC,CAAC,SAC7C,aAAa,QAAQ,aAAa,CAAC,CACtC;AACF;;;ACjKA,MAAa,iBAAyB;CACpC,OAAO;CACP,MAAM;AACR;AAMA,MAAM,mBACJ,UACkC;CAClC,IAAI,OAAO,UAAU,YAAY,UAAU,MAAM,OAAO;CACxD,MAAM,IAAI;CAGV,MAAM,YACJ,MAAM,QAAQ,EAAE,WAAW,KAAK,MAAM,QAAQ,EAAE,QAAQ;CAC1D,MAAM,cACJ,OAAO,EAAE,SAAS,YAAY,OAAO,EAAE,SAAS;CAClD,OAAO,aAAa;AACtB;AAEA,SAAgB,aAAa,YAA2C;CACtE,IACE,OAAO,eAAe,YACtB,eAAe,QACf,gBAAgB,UAAU,GAE1B,OAAO;EACL,SAAS;EACT,OAAO;EACP,cAAc,CAAC,UAAU;CAC3B;CAGF,MAAM,eAAe,OAAO,QAAQ,UAAU,CAAC,CAAC,QAC7C,CAAC,KAAK,WACL,IAAI,KAAK,MAAM,OACb,OAAO,UAAU,YAAY,MAAM,KAAK,MAAM,MAC9C,gBAAgB,KAAK,EAC3B;CAEA,IAAI,aAAa,WAAW,GAAG;EAC7B,QAAQ,KACN,oEACF;EACA,OAAO;GACL,SAAS;GACT,QAAQ;GACR,cAAc,OAAO,OAAO,cAAc;EAC5C;CACF;CAEA,IAAI,aAAa,WAAW,OAAO,KAAK,UAAU,CAAC,CAAC,QAClD,QAAQ,KACN,+EACF;CAIF,OAAO;EACL,SAAS;EACT,QAHa,OAAO,YAAY,YAG3B;EACL,cAAc,aAAa,KAAK,GAAG,WAAW,KAAc;CAC9D;AACF;;;AClEA,MAAM,0BAAU,IAAI,IAAsC;AAI1D,MAAM,UAA0D;CAC9D,kBACE,OAAO,0BAA0B,CAAC,MAAM,MACtC,EAAE,4BAA4B,CAChC;CACF,iBACE,OAAO,yBAAyB,CAAC,MAAM,MACrC,EAAE,sBAAsB,OAAO,aAAa,CAC9C;AACJ;;;;;;;;AASA,MAAa,iBACX,WACuC;CACvC,IAAI,OAAO,WAAW,UAAU,OAAO;CAEvC,MAAM,OAAO,QAAQ;CACrB,IAAI,CAAC,MACH,MAAM,IAAI,MACR,iCAAiC,OAAO,wCAC1C;CAGF,MAAM,SAAS,QAAQ,IAAI,MAAM;CACjC,IAAI,QAAQ,OAAO;CAEnB,MAAM,WAAW,KAAK;CAGtB,SAAS,YAAY,QAAQ,OAAO,MAAM,CAAC;CAC3C,QAAQ,IAAI,QAAQ,QAAQ;CAC5B,OAAO;AACT;;;AC5CA,MAAM,mBAAmB,gBACvB,IAAI,IAAI,YAAY,OAAO,OAAO,SAAS,CAAC;;;;;AAM9C,SAAgB,uBAAuB,YAAY,GAAqB;CACtE,OAAO;EACL,MAAM;EACN,KAAK,MAAM;GACT,KAAK,eAAe,MAAM,qBAAqB;GAC/C,IAAI,cAAc,GAAG;IACnB,MAAM,WAAW,KAAK,YAAY;IAClC,KAAK,aAAa;KAChB,GAAG,KAAK;KACR,OAAO,CAAC,UAAU,iBAAiB,WAAW,CAAC,CAC5C,OAAO,OAAO,CAAC,CACf,KAAK,IAAI;IACd;GACF;EACF;EACA,KAAK,MAAM;GACT,KAAK,eAAe,MAAM,gBAAgB;GAC1C,OAAO;EACT;CACF;AACF;;;;;;AAOA,SAAgB,4BACd,aACA,YAAY,GACM;CAClB,MAAM,mBAAmB,gBAAgB,WAAW;CAEpD,OAAO;EACL,MAAM;EACN,KAAK,MAAM;GACT,KAAK,eAAe,MAAM,0BAA0B;EACtD;EACA,KAAK,MAAM,MAAM;GACf,MAAM,gBAAgB,YAAY,OAAO;GAEzC,IAAI,iBAAiB,IAAI,aAAa,GACpC,KAAK,eAAe,MAAM,qBAAqB;GAGjD,OAAO;EACT;CACF;AACF;;;ACvCA,MAAM,qBACJ,eACA,cACA,sBAG0C;CAC1C,IAAI,cAAc,SAChB,OAAO;EACL,QAAQ,cAAc;EACtB;EACA;CACF;CAGF,OAAO,EACL,OAAO,cAAc,MACvB;AACF;AAEA,MAAa,qBACX,YACA,eACA,YACsB;CACtB,MAAM,EACJ,OACA,iBACA,kBACA,cACA,aACA,WACA,QACA,cACA,mBACA,iBACA,oBACA,sBACA,cAAc,kBACd,GAAG,qBACD;CAEJ,MAAM,eAAe,CAAC,GAAI,oBAAoB,CAAC,CAAE;CACjD,IAAI,iBACF,aAAa,KAAK,uBAAuB,kBAAkB,CAAC;CAE9D,IAAI,sBAAsB,QACxB,aAAa,KACX,4BACE,sBACA,kBACF,CACF;CAGF,OAAO;EACL,MAAM;EACN,GAAG,kBAAkB,eAAe,cAAc,iBAAiB;EACnE,GAAG;EACH;CACF;AACF;;;ACjDA,eAAsB,UACpB,MACA,UAMA,MAGA,SACgC;CAChC,MAAM,EAAE,YAAY,aAAa,cAAc,iBAC7C;CACF,MAAM,SAAuB,KAAK,gBAAgB;CAElD,MAAM,cACJ,KAAK,eACJ,OAAO,YAAY;EAClB,MAAM,KAAK,MAAM,QACf,aACA,cACA,cAAc,KAAK,MAAM,CAC3B;EACA,MAAM,GAAG,aACP,GAAG,qBAAqB,MAAM,YAAY,EAAE,CAC9C;EACA,OAAO;CACT,EAAA,CAAG;CAEL,MAAM,WAAW,sBACf,YACA,YAAY,mBAAmB,CACjC;CACA,MAAM,UAAU;EAAE,GAAG;EAAc,MAAM;CAAS;CAElD,MAAM,UAAgE;EACpE,aACE,aAAa,YAAY,WAAW,MAAM,OAAO,GAAG;GAClD;GACA;GACA;EACF,CAAC;EACH,YAAY,YAAY,WAAW,MAAM,OAAO;EAChD,cAAc,YAAY,aAAa,MAAM,OAAO;CACtD;CAIA,QAAQ,QAAQ,WAAW,QAAQ,MAAA,CAAO;AAC5C;AAEA,MAAa,gBACX,MACA,MACA,YACA,UAAoC,CAAC,GACrC,uBACuB;CACvB,MAAM,CAAC,iBAAiB,sBACtB,SAA6B,IAAI;CAInC,iBACE,QACE,QAAQ,mBAAmB,QAAQ,sBAAsB,MAC3D,CACF;CAEA,MAAM,aAAa,eAAe,IAAI;CACtC,MAAM,cAAc,eAAe,UAAU;CAC7C,MAAM,aAAa,eAAe,OAAO;CAEzC,MAAM,WAAW,cAAc;EAC7B,MAAM,EAAE,YAAY,gBAAgB,gBAClC,YACA,WAAW,iBACX,WAAW,WACX,WAAW,gBACb;EAEA,MAAM,QAAQ,aAAa,WAAW;EAEtC,MAAM,eAAe,kBAAkB,YAAY,OAAO,UAAU;EACpE,OAAO;GACL;GACA;GACA,cAAc,MAAM;GACpB;EACF;CACF,GAAG;EAAC;EAAY;EAAa;CAAU,CAAC;CAExC,MAAM,eAAe,OAAO,CAAC;CAE7B,MAAM,iBAAiB,OAAqB;EAC1C,iBAAiB;EACjB,WAAW,KAAA;CACb,CAAC;CAID,MAAM,wBAAwB,OAAO,kBAAkB;CACvD,sBAAsB,UAAU;CAEhC,gBAAgB;EACd,MAAM,YAAY,EAAE,aAAa;EAEjC,IAAI,CAAC,SAAS,YAAY;EAE1B,MAAM,MAAM,YAAY;GACtB,IAAI;IACF,MAAM,SAAS,MAAM,UACnB,MACA,UACA,YACA,sBAAsB,OACxB;IACA,IAAI,cAAc,aAAa,SAC7B,mBAAmB,MAAM;GAE7B,SAAS,OAAO;IACd,QAAQ,MAAM,kCAAkC,KAAK;GACvD;EACF;EAEA,IAAI,WAAW,OACb,qBAAqB,KAAK,gBAAgB,WAAW,KAAK;OAE1D,IAAI;EAGN,aAAa;GACX,aAAa,eAAe,QAAQ,SAAS;EAC/C;CACF,GAAG;EAAC;EAAM;EAAU;CAAU,CAAC;CAE/B,OAAO;AACT;;;;;;;;;;;;;AC3JA,SAAgB,2BAA2B;CACzC,QAAQ,SAA0B;EAChC,MAAM,MAAM,YAAY,MAAe,QAAQ,WAAW;GACxD,IACE,KAAK,YAAY,UACjB,QAAQ,SAAS,aACjB,OAAO,YAAY,OAEnB,KAAK,WAAW,SAAS;EAE7B,CAAC;CACH;AACF;;;;;;;AAQA,MAAa,gBAAgB,SAA2B;CAMtD,OAAO,EALc,KAAK,YAAY,CAAC,EAAA,CACpC,QAAQ,UAAU,MAAM,SAAS,MAAM,CAAC,CACxC,KAAK,UAAU,MAAM,KAAK,CAAC,CAC3B,KAAK,EAEU,CAAC,CAAC,SAAS,IAAI;AACnC;;;AC5BA,IAAI,2BAA2B;;;;;;AAO/B,MAAM,uBACJ,iBACuC;CACvC,IAAK,iBAAkC,UAAU,OAAO;CAExD,IAAI,CAAC,0BAA0B;EAC7B,2BAA2B;EAC3B,QAAQ,KACN,gHACF;CACF;CACA,OAAO;AACT;;;;;AA4EA,MAAa,mCACX,4BACG;CACH,OAAO,YAEH,EACE,UACA,OACA,mBAAmB,MACnB,OACA,WACA,WACA,eACA,eAAe,MACf,UAAU,MACV,IAAI,UAAU,OACd,cACA,GAAG,WAEL,QACG;EAGH,mBAAmB;EAEnB,MAAM,oBACJ,OAAO,aAAa,WAChB,SAAS,QAAQ,OACjB,UAAU,KAAK,KAAK;EAE1B,MAAM,kBAAkB,wBACtB,MACA,UACA,OACA;GACE,GAAG;GACH,cAAc,oBAAoB,YAAY;EAChD,CACF;EAEA,OACE,qBAAC,SAAD;GACO;GACL,eAAY;GACZ,aAAU;GACV,WAAW,KACT,WACA,aACA,oBAAoB,qBACpB,SACF;GACO;GAVT,UAAA,CAYG,gBAAgB,oBACf,oBAAC,QAAD;IACE,aAAU;IACV,WAAW,KAAK,qBAAqB,aAAa;IAClD,OAAO;IAEN,UAAA;GACG,CAAA,IACJ,MACH,OAAO,oBAAoB,WAC1B,oBAAC,OAAD;IACE,aAAU;IACV,yBAAyB,EAAE,QAAQ,gBAAgB;GACpD,CAAA,IAED,eAEK;;CAEb,CACF;AACF"}
|