react-shiki 0.11.0 → 0.11.1

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.
@@ -1,4 +1,4 @@
1
- import './style.css';
1
+ import * as React from "react";
2
2
  import { forwardRef, useEffect, useMemo, useRef, useState } from "react";
3
3
  import { Fragment, jsx, jsxs } from "react/jsx-runtime";
4
4
  import { toJsxRuntime } from "hast-util-to-jsx-runtime";
@@ -36,36 +36,103 @@ const throttleHighlighting = (performHighlight, timeoutControl, throttleMs) => {
36
36
  }, delay);
37
37
  };
38
38
  //#endregion
39
+ //#region src/styles/component.css?inline
40
+ 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
+ //#endregion
42
+ //#region src/styles/features.css?inline
43
+ 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
+ //#endregion
45
+ //#region src/lib/styles.ts
46
+ const injected = /* @__PURE__ */ new Set();
47
+ const useStyleInsertionEffect = React.useInsertionEffect ?? React.useLayoutEffect;
48
+ const inject = (css, key) => {
49
+ if (injected.has(key) || typeof document === "undefined") return;
50
+ injected.add(key);
51
+ try {
52
+ const sheet = new CSSStyleSheet();
53
+ sheet.replaceSync(css);
54
+ document.adoptedStyleSheets = [...document.adoptedStyleSheets, sheet];
55
+ } catch {
56
+ const style = document.createElement("style");
57
+ style.dataset.reactShiki = key;
58
+ style.textContent = css;
59
+ document.head.append(style);
60
+ }
61
+ };
62
+ /** Installs component styles before layout without triggering a render. */
63
+ const useComponentStyles = () => {
64
+ useStyleInsertionEffect(() => {
65
+ inject(component_default, "component");
66
+ }, []);
67
+ };
68
+ /** Installs line-number/highlight styles only when those options are used. */
69
+ const useFeatureStyles = (enabled) => {
70
+ useStyleInsertionEffect(() => {
71
+ if (enabled) inject(features_default, "features");
72
+ }, [enabled]);
73
+ };
74
+ //#endregion
39
75
  //#region src/lib/language.ts
40
76
  const FALLBACK_LANGUAGE = "plaintext";
41
- const getEmbeddedLanguages = (code, languageId, highlighter) => {
42
- const bundled = highlighter.getBundledLanguages();
43
- return guessEmbeddedLanguages(code, languageId).flatMap((language) => bundled[language] ?? []);
44
- };
45
77
  const toArray = (value) => value == null ? [] : Array.isArray(value) ? value : [value];
78
+ const isDynamicLanguage = (lang) => typeof lang === "function" || lang instanceof Promise || Array.isArray(lang) || typeof lang === "object" && lang != null && "default" in lang;
79
+ const isRegistration = (lang) => typeof lang === "object" && lang != null && !isDynamicLanguage(lang);
80
+ /** Dedupe identity: strings and registrations by value, dynamic inputs by reference */
46
81
  const languageKey = (lang) => {
47
82
  if (lang == null) return null;
48
83
  if (typeof lang === "string") return `s:${lang}`;
84
+ if (isDynamicLanguage(lang)) return lang;
49
85
  return `o:${lang.name}::${lang.scopeName}`;
50
86
  };
51
87
  const dedupeLanguages = (langs) => {
52
88
  const seen = /* @__PURE__ */ new Set();
53
- const deduped = [];
54
- for (const lang of langs) {
89
+ return langs.filter((lang) => {
55
90
  const key = languageKey(lang);
56
- if (key == null || seen.has(key)) continue;
91
+ if (key == null || seen.has(key)) return false;
57
92
  seen.add(key);
58
- deduped.push(lang);
59
- }
60
- return deduped;
93
+ return true;
94
+ });
95
+ };
96
+ /**
97
+ * A registration matches a query by name, scopeName (full or its last
98
+ * segment, e.g. "source.tsx" -> "tsx"), aliases, or fileTypes.
99
+ */
100
+ const registrationMatches = (candidate, matches) => matches(candidate.name) || matches(candidate.scopeName) || matches(candidate.scopeName?.split(".").pop()) || !!candidate.aliases?.some(matches) || !!candidate.fileTypes?.some(matches);
101
+ /**
102
+ * Resolves the language prop to a shiki language id plus everything the
103
+ * factory should load (primary, preloaded, and custom grammars, deduped).
104
+ */
105
+ const resolveLanguage = (lang, customLanguages, langAliases, preloadLanguages) => {
106
+ const customLangs = toArray(customLanguages);
107
+ const preloadLangs = toArray(preloadLanguages);
108
+ const result = (languageId, primary) => ({
109
+ languageId,
110
+ langsToLoad: dedupeLanguages([
111
+ primary,
112
+ ...preloadLangs,
113
+ ...customLangs
114
+ ])
115
+ });
116
+ if (lang == null || typeof lang === "string" && !lang.trim()) return result(FALLBACK_LANGUAGE);
117
+ if (typeof lang === "object") return result(lang.name, lang);
118
+ const normalized = lang.trim();
119
+ const query = normalized.toLowerCase();
120
+ const matches = (str) => str?.toLowerCase() === query;
121
+ const customMatch = [...customLangs, ...preloadLangs].filter(isRegistration).find((candidate) => registrationMatches(candidate, matches));
122
+ if (customMatch) return result(customMatch.name || normalized, customMatch);
123
+ const alias = Object.entries(langAliases ?? {}).find(([name]) => matches(name))?.[1];
124
+ if (alias) return result(alias, alias);
125
+ return result(normalized, normalized);
61
126
  };
62
127
  /**
63
128
  * Used in factories to check if language is supported.
64
- * Objects are validated as grammar registrations (name + scopeName).
129
+ * Objects are validated as grammar registrations (name + scopeName);
130
+ * dynamic imports pass through for shiki to resolve.
65
131
  */
66
132
  const isLoadableLanguage = (lang, bundledLanguages) => {
67
133
  if (lang == null) return false;
68
134
  if (typeof lang === "string") return lang in bundledLanguages;
135
+ if (isDynamicLanguage(lang)) return true;
69
136
  return typeof lang.name === "string" && typeof lang.scopeName === "string";
70
137
  };
71
138
  /**
@@ -73,51 +140,9 @@ const isLoadableLanguage = (lang, bundledLanguages) => {
73
140
  * Falls back to "plaintext" if not supported.
74
141
  */
75
142
  const resolveLoadedLanguage = (languageId, loadedLanguages) => isSpecialLang(languageId) || loadedLanguages.includes(languageId) ? languageId : FALLBACK_LANGUAGE;
76
- /**
77
- * Resolves the language input to standardized IDs and objects for Shiki
78
- * @param lang The language input from props
79
- * @param customLanguages An array of custom textmate grammar objects or a single grammar object
80
- * @returns A LanguageResult object containing:
81
- * - languageId: The resolved language ID
82
- * - langsToLoad: The language objects/string ids to load
83
- */
84
- const resolveLanguage = (lang, customLanguages, langAliases, preloadLanguages) => {
85
- const customLangs = toArray(customLanguages);
86
- const preloadLangs = toArray(preloadLanguages);
87
- const customMatchPool = [...customLangs, ...preloadLangs];
88
- let languageId = FALLBACK_LANGUAGE;
89
- let primaryLang;
90
- if (lang == null || typeof lang === "string" && !lang.trim()) return {
91
- languageId,
92
- langsToLoad: dedupeLanguages([...preloadLangs, ...customLangs])
93
- };
94
- if (typeof lang === "object") {
95
- languageId = lang.name;
96
- primaryLang = lang;
97
- } else {
98
- const normalizedLang = lang.trim();
99
- const lowerLang = normalizedLang.toLowerCase();
100
- const matches = (str) => str?.toLowerCase() === lowerLang;
101
- const customMatch = customMatchPool.find((candidate) => typeof candidate === "object" && candidate != null && !!(matches(candidate.name) || matches(candidate.scopeName) || matches(candidate.scopeName?.split(".").pop()) || candidate.aliases?.some(matches) || candidate.fileTypes?.some(matches)));
102
- if (customMatch) {
103
- languageId = customMatch.name || normalizedLang;
104
- primaryLang = customMatch;
105
- } else if (langAliases?.[normalizedLang]) {
106
- languageId = langAliases[normalizedLang];
107
- primaryLang = langAliases[normalizedLang];
108
- } else {
109
- languageId = normalizedLang;
110
- primaryLang = normalizedLang;
111
- }
112
- }
113
- return {
114
- languageId,
115
- langsToLoad: dedupeLanguages([
116
- primaryLang,
117
- ...preloadLangs,
118
- ...customLangs
119
- ])
120
- };
143
+ const getEmbeddedLanguages = (code, languageId, highlighter) => {
144
+ const bundled = highlighter.getBundledLanguages();
145
+ return guessEmbeddedLanguages(code, languageId).flatMap((language) => bundled[language] ?? []);
121
146
  };
122
147
  //#endregion
123
148
  //#region src/lib/theme.ts
@@ -155,6 +180,31 @@ function resolveTheme(themeInput) {
155
180
  };
156
181
  }
157
182
  //#endregion
183
+ //#region src/lib/engine.ts
184
+ const engines = /* @__PURE__ */ new Map();
185
+ const loaders = {
186
+ javascript: () => import("shiki/engine/javascript").then((m) => m.createJavaScriptRegexEngine()),
187
+ oniguruma: () => import("shiki/engine/oniguruma").then((m) => m.createOnigurumaEngine(import("shiki/wasm")))
188
+ };
189
+ /**
190
+ * Resolves a named engine to a lazily created, cached instance.
191
+ * Engine instances and undefined pass through untouched.
192
+ *
193
+ * The cache makes named engines referentially stable across renders and
194
+ * call sites, unlike inline `createJavaScriptRegexEngine()` calls.
195
+ */
196
+ const resolveEngine = (engine) => {
197
+ if (typeof engine !== "string") return engine;
198
+ const load = loaders[engine];
199
+ if (!load) throw new Error(`[react-shiki] unknown engine '${engine}', expected 'javascript' or 'oniguruma'`);
200
+ const cached = engines.get(engine);
201
+ if (cached) return cached;
202
+ const instance = load();
203
+ instance.catch(() => engines.delete(engine));
204
+ engines.set(engine, instance);
205
+ return instance;
206
+ };
207
+ //#endregion
158
208
  //#region src/lib/transformers.ts
159
209
  const toLineNumberSet = (lineNumbers) => new Set(lineNumbers.filter(Number.isInteger));
160
210
  /**
@@ -167,11 +217,10 @@ function lineNumbersTransformer(startLine = 1) {
167
217
  code(node) {
168
218
  this.addClassToHast(node, "rs-has-line-numbers");
169
219
  if (startLine !== 1) {
170
- const existingStyle = node.properties?.style || "";
171
- const newStyle = existingStyle ? `${existingStyle}; --line-start: ${startLine}` : `--line-start: ${startLine}`;
220
+ const existing = node.properties?.style;
172
221
  node.properties = {
173
222
  ...node.properties,
174
- style: newStyle
223
+ style: [existing, `--line-start: ${startLine}`].filter(Boolean).join("; ")
175
224
  };
176
225
  }
177
226
  },
@@ -228,7 +277,7 @@ async function highlight(code, resolved, opts, factory) {
228
277
  const { languageId, langsToLoad, themesToLoad, shikiOptions } = resolved;
229
278
  const format = opts.outputFormat ?? "react";
230
279
  const highlighter = opts.highlighter ?? await (async () => {
231
- const hl = await factory(langsToLoad, themesToLoad, opts.engine);
280
+ const hl = await factory(langsToLoad, themesToLoad, resolveEngine(opts.engine));
232
281
  await hl.loadLanguage(...getEmbeddedLanguages(code, languageId, hl));
233
282
  return hl;
234
283
  })();
@@ -250,6 +299,7 @@ async function highlight(code, resolved, opts, factory) {
250
299
  }
251
300
  const useHighlight = (code, lang, themeInput, options = {}, highlighterFactory) => {
252
301
  const [highlightedCode, setHighlightedCode] = useState(null);
302
+ useFeatureStyles(Boolean(options.showLineNumbers || options.highlightLineNumbers?.length));
253
303
  const stableLang = useStableValue(lang);
254
304
  const stableTheme = useStableValue(themeInput);
255
305
  const stableOpts = useStableValue(options);
@@ -347,22 +397,13 @@ const resolveOutputFormat = (outputFormat) => {
347
397
  * This creates a component that uses the provided hook implementation.
348
398
  */
349
399
  const createShikiHighlighterComponent = (useShikiHighlighterImpl) => {
350
- return forwardRef(({ language, theme, delay, transformers, defaultColor, cssVariablePrefix, addDefaultStyles = true, style, langStyle, className, langClassName, showLanguage = true, showLineNumbers = false, startingLineNumber = 1, highlightLineNumbers, children: code, as: Element = "div", customLanguages, preloadLanguages, outputFormat, ...shikiOptions }, ref) => {
351
- const options = {
352
- delay,
353
- transformers,
354
- customLanguages,
355
- preloadLanguages,
356
- showLineNumbers,
357
- defaultColor,
358
- cssVariablePrefix,
359
- startingLineNumber,
360
- highlightLineNumbers,
361
- outputFormat: resolveOutputFormat(outputFormat),
362
- ...shikiOptions
363
- };
400
+ return forwardRef(({ language, theme, addDefaultStyles = true, style, langStyle, className, langClassName, showLanguage = true, children: code, as: Element = "div", outputFormat, ...options }, ref) => {
401
+ useComponentStyles();
364
402
  const displayLanguageId = typeof language === "object" ? language.name || null : language?.trim() || null;
365
- const highlightedCode = useShikiHighlighterImpl(code, language, theme, options);
403
+ const highlightedCode = useShikiHighlighterImpl(code, language, theme, {
404
+ ...options,
405
+ outputFormat: resolveOutputFormat(outputFormat)
406
+ });
366
407
  return /* @__PURE__ */ jsxs(Element, {
367
408
  ref,
368
409
  "data-testid": "shiki-container",
@@ -370,7 +411,6 @@ const createShikiHighlighterComponent = (useShikiHighlighterImpl) => {
370
411
  className: clsx("rs-root", "not-prose", addDefaultStyles && "rs-default-styles", className),
371
412
  style,
372
413
  children: [showLanguage && displayLanguageId ? /* @__PURE__ */ jsx("span", {
373
- id: "language-label",
374
414
  "data-slot": "language-label",
375
415
  className: clsx("rs-language-label", langClassName),
376
416
  style: langStyle,
@@ -385,4 +425,4 @@ const createShikiHighlighterComponent = (useShikiHighlighterImpl) => {
385
425
  //#endregion
386
426
  export { isLoadableLanguage as a, useHighlight as i, isInlineCode as n, rehypeInlineCodeProperty as r, createShikiHighlighterComponent as t };
387
427
 
388
- //# sourceMappingURL=component-DdW14SOw.mjs.map
428
+ //# sourceMappingURL=component-Cf8Topxy.mjs.map
@@ -0,0 +1 @@
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: React.RefObject<TimeoutState>,\n throttleMs: number\n) => {\n clearTimeout(timeoutControl.current.timeoutId);\n\n const delay = Math.max(\n 0,\n timeoutControl.current.nextAllowedTime - Date.now()\n );\n timeoutControl.current.timeoutId = setTimeout(() => {\n performHighlight();\n timeoutControl.current.nextAllowedTime = Date.now() + throttleMs;\n }, delay);\n};\n","/* 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":"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"}
@@ -1,6 +1,5 @@
1
1
  import { ReactElement } from "react";
2
- import { BundledHighlighterOptions, BundledLanguage, BundledTheme, CodeOptionsMultipleThemes, CodeToHastOptions, Highlighter, HighlighterCore, LanguageRegistration, SpecialLanguage, StringLiteralUnion, ThemeRegistrationAny, TokensResult } from "shiki";
3
-
2
+ import { Awaitable, BundledHighlighterOptions, BundledLanguage, BundledTheme, CodeOptionsMultipleThemes, CodeToHastOptions, Highlighter, HighlighterCore, LanguageInput, LanguageRegistration, RegexEngine, SpecialLanguage, StringLiteralUnion, ThemeRegistrationAny, TokensResult } from "shiki";
4
3
  //#region ../node_modules/.pnpm/@types+unist@3.0.3/node_modules/@types/unist/index.d.ts
5
4
  // ## Interfaces
6
5
  /**
@@ -317,6 +316,12 @@ type Element = Element$1;
317
316
  * @see https://shiki.style/languages
318
317
  */
319
318
  type Language = LanguageRegistration | StringLiteralUnion<BundledLanguage> | SpecialLanguage | undefined;
319
+ /**
320
+ * A `preloadLanguages` entry: a bundled language id, a custom grammar
321
+ * object, or a dynamic grammar import (shiki's `LanguageInput`), e.g.
322
+ * `() => import('../langs/mcfunction.json')`
323
+ */
324
+ type PreloadLanguage = Language | LanguageInput;
320
325
  type MultiThemeKey = 'dark' | 'light' | (string & {});
321
326
  /**
322
327
  * A Shiki BundledTheme or a custom textmate theme object
@@ -339,7 +344,11 @@ type Theme = ThemeRegistrationAny | StringLiteralUnion<BundledTheme>;
339
344
  *
340
345
  * @see https://shiki.style/guide/dual-themes
341
346
  */
342
- type Themes = { [Key in MultiThemeKey]: Theme };
347
+ type Themes = { [Key in MultiThemeKey]: Theme; };
348
+ /**
349
+ * Named RegExp engines that react-shiki creates and caches internally.
350
+ */
351
+ type EngineName = 'javascript' | 'oniguruma';
343
352
  /**
344
353
  * Configuration options specific to react-shiki
345
354
  */
@@ -349,6 +358,18 @@ interface ReactShikiOptions {
349
358
  * @default undefined (no throttling)
350
359
  */
351
360
  delay?: number;
361
+ /**
362
+ * RegExp engine for syntax highlighting.
363
+ * - 'oniguruma': compiled WebAssembly engine, maximum grammar support
364
+ * - 'javascript': pure-JS engine, no WASM fetch, faster startup
365
+ *
366
+ * Named engines are created lazily and cached by react-shiki, so they
367
+ * are safe to pass inline. A custom engine instance is also accepted;
368
+ * create it once at module scope, as a new instance on every render
369
+ * re-triggers highlighting.
370
+ * @default 'oniguruma'
371
+ */
372
+ engine?: EngineName | Awaitable<RegexEngine>;
352
373
  /**
353
374
  * Custom textmate grammars to be preloaded for highlighting.
354
375
  * @deprecated Use preloadLanguages instead.
@@ -357,8 +378,16 @@ interface ReactShikiOptions {
357
378
  /**
358
379
  * Preload custom grammars or bundled languages.
359
380
  * Supports custom textmate grammars, replaces deprecated `customLanguages`
381
+ *
382
+ * Full/web bundles also accept dynamic grammar imports, keeping custom
383
+ * grammars out of the main bundle. Define them at module scope, preferring
384
+ * getters: a fresh getter each render re-triggers highlighting, while
385
+ * swapping a bare promise at runtime is not detected.
386
+ *
387
+ * @example
388
+ * const preload = [() => import('../langs/mcfunction.json')];
360
389
  */
361
- preloadLanguages?: Language | Language[];
390
+ preloadLanguages?: PreloadLanguage | PreloadLanguage[];
362
391
  /**
363
392
  * Output format for the highlighted code.
364
393
  * - 'react': Returns React nodes (default, safer)
@@ -418,10 +447,7 @@ interface ReactShikiOptions {
418
447
  * Configuration options for the syntax highlighter
419
448
  * Extends CodeToHastOptions to allow passing any Shiki options directly
420
449
  */
421
- interface HighlighterOptions extends ReactShikiOptions, Pick<CodeOptionsMultipleThemes<BundledTheme>, 'defaultColor' | 'cssVariablePrefix'>, Omit<CodeToHastOptions, 'lang' | 'theme' | 'themes'>, Pick<BundledHighlighterOptions<string, string>, 'langAlias' | 'engine'> {}
422
- /**
423
- * State for the throttling logic
424
- */
450
+ interface HighlighterOptions extends ReactShikiOptions, Pick<CodeOptionsMultipleThemes<BundledTheme>, 'defaultColor' | 'cssVariablePrefix'>, Omit<CodeToHastOptions, 'lang' | 'theme' | 'themes'>, Pick<BundledHighlighterOptions<string, string>, 'langAlias'> {}
425
451
  /**
426
452
  * Supported output formats and their corresponding result shapes.
427
453
  */
@@ -521,20 +547,6 @@ interface ShikiHighlighterProps extends HighlighterOptions {
521
547
  * @default true
522
548
  */
523
549
  showLanguage?: boolean;
524
- /**
525
- * Whether to show line numbers
526
- * @default false
527
- */
528
- showLineNumbers?: boolean;
529
- /**
530
- * Starting line number (when showLineNumbers is true)
531
- * @default 1
532
- */
533
- startingLineNumber?: number;
534
- /**
535
- * Displayed line numbers to highlight
536
- */
537
- highlightLineNumbers?: number[];
538
550
  /**
539
551
  * The HTML element that wraps the generated code block.
540
552
  * @default 'div'
@@ -542,5 +554,5 @@ interface ShikiHighlighterProps extends HighlighterOptions {
542
554
  as?: React.ElementType;
543
555
  }
544
556
  //#endregion
545
- export { HighlightResult as a, Language as c, Themes as d, UseShikiHighlighter as f, Element as i, OutputFormat as l, isInlineCode as n, HighlighterOptions as o, rehypeInlineCodeProperty as r, HighlighterOptionsFor as s, ShikiHighlighterProps as t, Theme as u };
546
- //# sourceMappingURL=component-B-gutZRK.d.mts.map
557
+ export { EngineName as a, HighlighterOptionsFor as c, PreloadLanguage as d, Theme as f, Element as i, Language as l, UseShikiHighlighter as m, isInlineCode as n, HighlightResult as o, Themes as p, rehypeInlineCodeProperty as r, HighlighterOptions as s, ShikiHighlighterProps as t, OutputFormat as u };
558
+ //# sourceMappingURL=component-DlK6KonE.d.mts.map
package/dist/core.d.mts CHANGED
@@ -1,8 +1,7 @@
1
- import { a as HighlightResult, c as Language, d as Themes, f as UseShikiHighlighter, i as Element, l as OutputFormat, n as isInlineCode, o as HighlighterOptions, r as rehypeInlineCodeProperty, s as HighlighterOptionsFor, t as ShikiHighlighterProps, u as Theme } from "./component-B-gutZRK.mjs";
1
+ import { a as EngineName, c as HighlighterOptionsFor, d as PreloadLanguage, f as Theme, i as Element, l as Language, m as UseShikiHighlighter, n as isInlineCode, o as HighlightResult, p as Themes, r as rehypeInlineCodeProperty, s as HighlighterOptions, t as ShikiHighlighterProps, u as OutputFormat } from "./component-DlK6KonE.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
-
6
5
  //#region src/core.d.ts
7
6
  /**
8
7
  * Highlight code with shiki (core bundle)
@@ -44,5 +43,5 @@ declare const useShikiHighlighter: UseShikiHighlighter;
44
43
  */
45
44
  declare const ShikiHighlighter: import("react").ForwardRefExoticComponent<ShikiHighlighterProps & import("react").RefAttributes<HTMLElement>>;
46
45
  //#endregion
47
- export { type Element, type HighlightResult, type HighlighterOptions, type HighlighterOptionsFor, type Language, type LanguageRegistration, type OutputFormat, ShikiHighlighter, ShikiHighlighter as default, type ShikiHighlighterProps, type Theme, type Themes, type TokensResult, type UseShikiHighlighter, createHighlighterCore, createJavaScriptRawEngine, createJavaScriptRegexEngine, createOnigurumaEngine, isInlineCode, rehypeInlineCodeProperty, useShikiHighlighter };
46
+ export { type Element, type EngineName, type HighlightResult, type HighlighterOptions, type HighlighterOptionsFor, type Language, type LanguageRegistration, type OutputFormat, type PreloadLanguage, ShikiHighlighter, ShikiHighlighter as default, type ShikiHighlighterProps, type Theme, type Themes, type TokensResult, type UseShikiHighlighter, createHighlighterCore, createJavaScriptRawEngine, createJavaScriptRegexEngine, createOnigurumaEngine, isInlineCode, rehypeInlineCodeProperty, useShikiHighlighter };
48
47
  //# sourceMappingURL=core.d.mts.map
package/dist/core.mjs CHANGED
@@ -1,4 +1,4 @@
1
- import { i as useHighlight, n as isInlineCode, r as rehypeInlineCodeProperty, t as createShikiHighlighterComponent } from "./component-DdW14SOw.mjs";
1
+ import { i as useHighlight, n as isInlineCode, r as rehypeInlineCodeProperty, t as createShikiHighlighterComponent } from "./component-Cf8Topxy.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";
@@ -57,7 +57,7 @@ const useShikiHighlighter = (code, lang, themeInput, options = {}) => {
57
57
  * ShikiHighlighter component using a custom highlighter.
58
58
  * Requires a highlighter to be provided.
59
59
  */
60
- const ShikiHighlighter = createShikiHighlighterComponent(useShikiHighlighter);
60
+ const ShikiHighlighter = /*#__PURE__*/ createShikiHighlighterComponent(useShikiHighlighter);
61
61
  //#endregion
62
62
  export { ShikiHighlighter, ShikiHighlighter as default, createHighlighterCore, createJavaScriptRawEngine, createJavaScriptRegexEngine, createOnigurumaEngine, isInlineCode, rehypeInlineCodeProperty, useShikiHighlighter };
63
63
 
package/dist/core.mjs.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"core.mjs","names":[],"sources":["../src/bundles/core.ts","../src/core.ts"],"sourcesContent":["import type { HighlighterCore } from 'shiki';\n\n/**\n * Validates that a highlighter is provided for the core bundle.\n * The core bundle requires users to provide their own highlighter instance.\n */\nexport function validateCoreHighlighter(\n highlighter: HighlighterCore | undefined\n): HighlighterCore {\n if (!highlighter) {\n throw new Error(\n 'react-shiki/core requires a custom highlighter. ' +\n 'Use createHighlighterCore() from react-shiki or switch to react-shiki for plug-and-play usage.'\n );\n }\n return highlighter;\n}\n","import { useHighlight } from './lib/hook';\nimport { validateCoreHighlighter } from './bundles/core';\nimport type { UseShikiHighlighter } from './lib/types';\n\nexport { isInlineCode, rehypeInlineCodeProperty } from './lib/plugins';\n\nimport {\n createShikiHighlighterComponent,\n type ShikiHighlighterProps,\n} from './lib/component';\n\nexport type { ShikiHighlighterProps };\n\nexport type {\n UseShikiHighlighter,\n Language,\n Theme,\n Themes,\n Element,\n HighlighterOptions,\n HighlighterOptionsFor,\n HighlightResult,\n OutputFormat,\n} from './lib/types';\n\nexport { createHighlighterCore } from 'shiki/core';\nexport { createOnigurumaEngine } from 'shiki/engine/oniguruma';\nexport {\n createJavaScriptRegexEngine,\n createJavaScriptRawEngine,\n} from 'shiki/engine/javascript';\n\nexport type { LanguageRegistration, TokensResult } from 'shiki/core';\n\n/**\n * Highlight code with shiki (core bundle)\n *\n * @param code - Code to highlight\n * @param lang - Language (bundled or custom)\n * @param theme - Theme (bundled, multi-theme, or custom)\n * @param options - react-shiki options + shiki options\n * @returns Highlighted code as React elements (default), HTML string, or Shiki tokens, based on `outputFormat`\n *\n * @example\n * ```tsx\n * import { createHighlighterCore, createOnigurumaEngine } from 'react-shiki/core';\n *\n *\n * const highlighter = await createHighlighterCore({\n * themes: [import('@shikijs/themes/github-light'), import('@shikijs/themes/github-dark')],\n * langs: [import('@shikijs/langs/typescript')],\n * engine: createOnigurumaEngine(import('shiki/wasm'))\n * });\n *\n * const highlighted = useShikiHighlighter(\n * 'const x = 1;',\n * 'typescript',\n * {\n * light: 'github-light',\n * dark: 'github-dark'\n * },\n * { highlighter }\n * );\n * ```\n *\n * Core bundle (minimal). For plug-and-play: `react-shiki` or `react-shiki/web`\n */\nexport const useShikiHighlighter: UseShikiHighlighter = (\n code,\n lang,\n themeInput,\n options = {}\n) => {\n // Validate that highlighter is provided\n const highlighter = validateCoreHighlighter(options.highlighter);\n\n return useHighlight(\n code,\n lang,\n themeInput,\n {\n ...options,\n highlighter,\n },\n async () => highlighter\n );\n};\n\n/**\n * ShikiHighlighter component using a custom highlighter.\n * Requires a highlighter to be provided.\n */\nexport const ShikiHighlighter = createShikiHighlighterComponent(\n useShikiHighlighter\n);\nexport default ShikiHighlighter;\n"],"mappings":";;;;;;;;;AAMA,SAAgB,wBACd,aACiB;CACjB,IAAI,CAAC,aACH,MAAM,IAAI,MACR,gJAEF;CAEF,OAAO;AACT;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;ACmDA,MAAa,uBACX,MACA,MACA,YACA,UAAU,CAAC,MACR;CAEH,MAAM,cAAc,wBAAwB,QAAQ,WAAW;CAE/D,OAAO,aACL,MACA,MACA,YACA;EACE,GAAG;EACH;CACF,GACA,YAAY,WACd;AACF;;;;;AAMA,MAAa,mBAAmB,gCAC9B,mBACF"}
1
+ {"version":3,"file":"core.mjs","names":[],"sources":["../src/bundles/core.ts","../src/core.ts"],"sourcesContent":["import type { HighlighterCore } from 'shiki';\n\n/**\n * Validates that a highlighter is provided for the core bundle.\n * The core bundle requires users to provide their own highlighter instance.\n */\nexport function validateCoreHighlighter(\n highlighter: HighlighterCore | undefined\n): HighlighterCore {\n if (!highlighter) {\n throw new Error(\n 'react-shiki/core requires a custom highlighter. ' +\n 'Use createHighlighterCore() from react-shiki or switch to react-shiki for plug-and-play usage.'\n );\n }\n return highlighter;\n}\n","import { useHighlight } from './lib/hook';\nimport { validateCoreHighlighter } from './bundles/core';\nimport type { UseShikiHighlighter } from './lib/types';\n\nexport { isInlineCode, rehypeInlineCodeProperty } from './lib/plugins';\n\nimport {\n createShikiHighlighterComponent,\n type ShikiHighlighterProps,\n} from './lib/component';\n\nexport type { ShikiHighlighterProps };\n\nexport type {\n UseShikiHighlighter,\n Language,\n PreloadLanguage,\n Theme,\n Themes,\n Element,\n EngineName,\n HighlighterOptions,\n HighlighterOptionsFor,\n HighlightResult,\n OutputFormat,\n} from './lib/types';\n\nexport { createHighlighterCore } from 'shiki/core';\nexport { createOnigurumaEngine } from 'shiki/engine/oniguruma';\nexport {\n createJavaScriptRegexEngine,\n createJavaScriptRawEngine,\n} from 'shiki/engine/javascript';\n\nexport type { LanguageRegistration, TokensResult } from 'shiki/core';\n\n/**\n * Highlight code with shiki (core bundle)\n *\n * @param code - Code to highlight\n * @param lang - Language (bundled or custom)\n * @param theme - Theme (bundled, multi-theme, or custom)\n * @param options - react-shiki options + shiki options\n * @returns Highlighted code as React elements (default), HTML string, or Shiki tokens, based on `outputFormat`\n *\n * @example\n * ```tsx\n * import { createHighlighterCore, createOnigurumaEngine } from 'react-shiki/core';\n *\n *\n * const highlighter = await createHighlighterCore({\n * themes: [import('@shikijs/themes/github-light'), import('@shikijs/themes/github-dark')],\n * langs: [import('@shikijs/langs/typescript')],\n * engine: createOnigurumaEngine(import('shiki/wasm'))\n * });\n *\n * const highlighted = useShikiHighlighter(\n * 'const x = 1;',\n * 'typescript',\n * {\n * light: 'github-light',\n * dark: 'github-dark'\n * },\n * { highlighter }\n * );\n * ```\n *\n * Core bundle (minimal). For plug-and-play: `react-shiki` or `react-shiki/web`\n */\nexport const useShikiHighlighter: UseShikiHighlighter = (\n code,\n lang,\n themeInput,\n options = {}\n) => {\n 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 /*#__PURE__*/ createShikiHighlighterComponent(useShikiHighlighter);\nexport default ShikiHighlighter;\n"],"mappings":";;;;;;;;;AAMA,SAAgB,wBACd,aACiB;CACjB,IAAI,CAAC,aACH,MAAM,IAAI,MACR,gJAEF;CAEF,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,mBACG,8CAAgC,mBAAmB"}
@@ -0,0 +1,17 @@
1
+ import { a as isLoadableLanguage } from "./component-Cf8Topxy.mjs";
2
+ import { createOnigurumaEngine } from "shiki/engine/oniguruma";
3
+ //#region src/bundles/factory.ts
4
+ /**
5
+ * Builds a highlighter factory for a Shiki bundle (full or web).
6
+ * Unloadable languages are filtered out so unknown ids fall back to
7
+ * plaintext instead of throwing.
8
+ */
9
+ const bundledHighlighterFactory = (getSingletonHighlighter, bundledLanguages) => (langsToLoad, themesToLoad, engine) => getSingletonHighlighter({
10
+ langs: langsToLoad.filter((lang) => isLoadableLanguage(lang, bundledLanguages)),
11
+ themes: themesToLoad,
12
+ engine: engine ?? createOnigurumaEngine(import("shiki/wasm"))
13
+ });
14
+ //#endregion
15
+ export { bundledHighlighterFactory as t };
16
+
17
+ //# sourceMappingURL=factory-DySLHBEo.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"factory-DySLHBEo.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 (langsToLoad, themesToLoad, engine) =>\n 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,sBAED,aAAa,cAAc,WAC1B,wBAAwB;CACtB,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 HighlightResult, c as Language, d as Themes, f as UseShikiHighlighter, i as Element, l as OutputFormat, n as isInlineCode, o as HighlighterOptions, r as rehypeInlineCodeProperty, s as HighlighterOptionsFor, t as ShikiHighlighterProps, u as Theme } from "./component-B-gutZRK.mjs";
1
+ import { a as EngineName, c as HighlighterOptionsFor, d as PreloadLanguage, f as Theme, i as Element, l as Language, m as UseShikiHighlighter, n as isInlineCode, o as HighlightResult, p as Themes, r as rehypeInlineCodeProperty, s as HighlighterOptions, t as ShikiHighlighterProps, u as OutputFormat } from "./component-DlK6KonE.mjs";
2
2
  import { LanguageRegistration, TokensResult } from "shiki";
3
3
  import { createJavaScriptRawEngine, createJavaScriptRegexEngine } from "shiki/engine/javascript";
4
-
5
4
  //#region src/index.d.ts
6
5
  /**
7
6
  * Highlight code with shiki (full bundle)
@@ -33,5 +32,5 @@ declare const useShikiHighlighter: UseShikiHighlighter;
33
32
  */
34
33
  declare const ShikiHighlighter: import("react").ForwardRefExoticComponent<ShikiHighlighterProps & import("react").RefAttributes<HTMLElement>>;
35
34
  //#endregion
36
- export { type Element, type HighlightResult, type HighlighterOptions, type HighlighterOptionsFor, type Language, type LanguageRegistration, type OutputFormat, ShikiHighlighter, ShikiHighlighter as default, type ShikiHighlighterProps, type Theme, type Themes, type TokensResult, type UseShikiHighlighter, createJavaScriptRawEngine, createJavaScriptRegexEngine, isInlineCode, rehypeInlineCodeProperty, useShikiHighlighter };
35
+ export { type Element, type EngineName, type HighlightResult, type HighlighterOptions, type HighlighterOptionsFor, type Language, type LanguageRegistration, type OutputFormat, type PreloadLanguage, ShikiHighlighter, ShikiHighlighter as default, type ShikiHighlighterProps, type Theme, type Themes, type TokensResult, type UseShikiHighlighter, createJavaScriptRawEngine, createJavaScriptRegexEngine, isInlineCode, rehypeInlineCodeProperty, useShikiHighlighter };
37
36
  //# sourceMappingURL=index.d.mts.map
package/dist/index.mjs CHANGED
@@ -1,19 +1,13 @@
1
- import { a as isLoadableLanguage, i as useHighlight, n as isInlineCode, r as rehypeInlineCodeProperty, t as createShikiHighlighterComponent } from "./component-DdW14SOw.mjs";
1
+ import { i as useHighlight, n as isInlineCode, r as rehypeInlineCodeProperty, t as createShikiHighlighterComponent } from "./component-Cf8Topxy.mjs";
2
+ import { t as bundledHighlighterFactory } from "./factory-DySLHBEo.mjs";
2
3
  import { bundledLanguages, getSingletonHighlighter } from "shiki";
3
- import { createOnigurumaEngine } from "shiki/engine/oniguruma";
4
4
  import { createJavaScriptRawEngine, createJavaScriptRegexEngine } from "shiki/engine/javascript";
5
5
  //#region src/bundles/full.ts
6
6
  /**
7
- * Creates a highlighter using the full Shiki bundle with all languages and themes.
8
- * This is the largest bundle but provides maximum compatibility.
7
+ * Highlighter factory for the full Shiki bundle: every language and
8
+ * theme, largest size, maximum compatibility.
9
9
  */
10
- async function createFullHighlighter(langsToLoad, themesToLoad, engine) {
11
- return await getSingletonHighlighter({
12
- langs: langsToLoad.filter((lang) => isLoadableLanguage(lang, bundledLanguages)),
13
- themes: themesToLoad,
14
- engine: engine ?? createOnigurumaEngine(import("shiki/wasm"))
15
- });
16
- }
10
+ const createFullHighlighter = bundledHighlighterFactory(getSingletonHighlighter, bundledLanguages);
17
11
  //#endregion
18
12
  //#region src/index.ts
19
13
  /**
@@ -44,7 +38,7 @@ const useShikiHighlighter = (code, lang, themeInput, options = {}) => useHighlig
44
38
  * ShikiHighlighter component using the full bundle.
45
39
  * Includes all languages and themes for maximum compatibility.
46
40
  */
47
- const ShikiHighlighter = createShikiHighlighterComponent(useShikiHighlighter);
41
+ const ShikiHighlighter = /*#__PURE__*/ createShikiHighlighterComponent(useShikiHighlighter);
48
42
  //#endregion
49
43
  export { ShikiHighlighter, ShikiHighlighter as default, createJavaScriptRawEngine, createJavaScriptRegexEngine, isInlineCode, rehypeInlineCodeProperty, useShikiHighlighter };
50
44