react-shiki
Version:
Syntax highlighter component for react using shiki
1 lines • 26.3 kB
Source Map (JSON)
{"version":3,"file":"component-CYq7SnJ0.mjs","names":[],"sources":["../src/lib/utils.ts","../src/lib/language.ts","../src/lib/theme.ts","../src/lib/transformers.ts","../src/lib/options.ts","../src/lib/hook.ts","../src/lib/plugins.ts","../src/lib/component.tsx"],"sourcesContent":["import { useRef } from 'react';\nimport { dequal } from 'dequal';\n\nimport type { TimeoutState } from './types';\n\n/**\n * Returns a referentially stable version of `value` that only updates when content changes.\n * Uses reference equality as a fast path and deep comparison to prevent unnecessary updates.\n */\nexport const useStableValue = <T>(value: T): T => {\n const ref = useRef(value);\n\n if (value !== ref.current && !dequal(value, ref.current)) {\n ref.current = value;\n }\n\n return ref.current;\n};\n\n/**\n * Optionally throttles rapid sequential highlighting operations\n *\n * @example\n * const timeoutControl = useRef<TimeoutState>({\n * nextAllowedTime: 0,\n * timeoutId: undefined\n * });\n *\n * throttleHighlighting(highlightCode, timeoutControl, 1000);\n */\nexport const throttleHighlighting = (\n performHighlight: () => Promise<void>,\n timeoutControl: React.RefObject<TimeoutState>,\n throttleMs: number\n) => {\n clearTimeout(timeoutControl.current.timeoutId);\n\n const delay = Math.max(\n 0,\n timeoutControl.current.nextAllowedTime - Date.now()\n );\n timeoutControl.current.timeoutId = setTimeout(() => {\n performHighlight();\n timeoutControl.current.nextAllowedTime = Date.now() + throttleMs;\n }, delay);\n};\n","import type { Language } from './types';\nimport { guessEmbeddedLanguages, isSpecialLang } from 'shiki/core';\nimport type {\n DynamicImportLanguageRegistration,\n Highlighter,\n HighlighterCore,\n LanguageInput,\n LanguageRegistration,\n} from 'shiki';\n\nexport const FALLBACK_LANGUAGE = 'plaintext';\n\nexport const getEmbeddedLanguages = (\n code: string,\n languageId: string,\n highlighter: Highlighter | HighlighterCore\n): LanguageInput[] => {\n const bundled: Record<string, LanguageInput> =\n highlighter.getBundledLanguages();\n return guessEmbeddedLanguages(code, languageId).flatMap(\n (language) => bundled[language] ?? []\n );\n};\n\n/**\n * Resolved languages and metadata\n */\ntype LanguageResult = {\n languageId: string;\n langsToLoad: Language[];\n};\n\nconst toArray = <T>(value?: T | T[]): T[] =>\n value == null ? [] : Array.isArray(value) ? value : [value];\n\nconst languageKey = (lang: Language): string | null => {\n if (lang == null) return null;\n if (typeof lang === 'string') return `s:${lang}`;\n return `o:${lang.name}::${lang.scopeName}`;\n};\n\nconst dedupeLanguages = (langs: Language[]): Language[] => {\n const seen = new Set<string>();\n const deduped: Language[] = [];\n\n for (const lang of langs) {\n const key = languageKey(lang);\n if (key == null || seen.has(key)) continue;\n seen.add(key);\n deduped.push(lang);\n }\n\n return deduped;\n};\n\n/**\n * Used in factories to check if language is supported.\n * Objects are validated as grammar registrations (name + scopeName).\n */\nexport const isLoadableLanguage = <T extends string>(\n lang: Language,\n bundledLanguages: Record<T, DynamicImportLanguageRegistration>\n): lang is NonNullable<Language> => {\n if (lang == null) return false;\n if (typeof lang === 'string') return lang in bundledLanguages;\n return (\n typeof lang.name === 'string' && typeof lang.scopeName === 'string'\n );\n};\n\n/**\n * Used in hook to resolve loaded language for highlighting.\n * Falls back to \"plaintext\" if not supported.\n */\nexport const resolveLoadedLanguage = (\n languageId: string,\n loadedLanguages: string[]\n): string =>\n isSpecialLang(languageId) || loadedLanguages.includes(languageId)\n ? languageId\n : FALLBACK_LANGUAGE;\n\n/**\n * Resolves the language input to standardized IDs and objects for Shiki\n * @param lang The language input from props\n * @param customLanguages An array of custom textmate grammar objects or a single grammar object\n * @returns A LanguageResult object containing:\n * - languageId: The resolved language ID\n * - langsToLoad: The language objects/string ids to load\n */\nexport const resolveLanguage = (\n lang: Language,\n customLanguages?: LanguageRegistration | LanguageRegistration[],\n langAliases?: Record<string, string>,\n preloadLanguages?: Language | Language[]\n): LanguageResult => {\n const customLangs = toArray(customLanguages);\n const preloadLangs = toArray(preloadLanguages);\n const customMatchPool = [...customLangs, ...preloadLangs];\n let languageId = FALLBACK_LANGUAGE;\n let primaryLang: Language;\n\n // Language is null or empty string\n if (lang == null || (typeof lang === 'string' && !lang.trim())) {\n return {\n languageId,\n langsToLoad: dedupeLanguages([...preloadLangs, ...customLangs]),\n };\n }\n\n // Language is custom object\n if (typeof lang === 'object') {\n languageId = lang.name;\n primaryLang = lang;\n } else {\n // Language is string\n const lowerLang = lang.toLowerCase();\n const matches = (str: string | undefined): boolean =>\n str?.toLowerCase() === lowerLang;\n\n // Check custom registrations (from both customLanguages and preloadLanguages)\n const customMatch = customMatchPool.find(\n (candidate): candidate is LanguageRegistration =>\n typeof candidate === 'object' &&\n candidate != null &&\n !!(\n matches(candidate.name) ||\n matches(candidate.scopeName) ||\n matches(candidate.scopeName?.split('.').pop()) ||\n candidate.aliases?.some(matches) ||\n candidate.fileTypes?.some(matches)\n )\n );\n\n if (customMatch) {\n languageId = customMatch.name || lang;\n primaryLang = customMatch;\n } else if (langAliases?.[lang]) {\n // Language is aliased\n languageId = langAliases[lang];\n primaryLang = langAliases[lang];\n } else {\n // For any other string, pass it through to the factory\n languageId = lang;\n primaryLang = lang;\n }\n }\n\n return {\n languageId,\n langsToLoad: dedupeLanguages([\n primaryLang,\n ...preloadLangs,\n ...customLangs,\n ]),\n };\n};\n","import type { Theme, Themes } from './types';\nimport type { ThemeRegistrationAny } from 'shiki/core';\n\nexport const DEFAULT_THEMES: Themes = {\n light: 'github-light',\n dark: 'github-dark',\n};\n\nexport type ResolvedTheme =\n | { isMulti: true; themes: Themes; themesToLoad: Theme[] }\n | { isMulti: false; theme: Theme; themesToLoad: Theme[] };\n\nconst isTextmateTheme = (\n value: unknown\n): value is ThemeRegistrationAny => {\n if (typeof value !== 'object' || value === null) return false;\n const v = value as Partial<ThemeRegistrationAny> & {\n settings?: unknown;\n };\n const hasTokens =\n Array.isArray(v.tokenColors) || Array.isArray(v.settings);\n const hasIdentity =\n typeof v.name === 'string' || typeof v.type === 'string';\n return hasTokens && hasIdentity;\n};\n\nexport function resolveTheme(themeInput: Theme | Themes): ResolvedTheme {\n if (\n typeof themeInput !== 'object' ||\n themeInput === null ||\n isTextmateTheme(themeInput)\n ) {\n return {\n isMulti: false,\n theme: themeInput,\n themesToLoad: [themeInput],\n };\n }\n\n const validEntries = Object.entries(themeInput).filter(\n ([key, value]) =>\n key.trim() !== '' &&\n ((typeof value === 'string' && value.trim() !== '') ||\n isTextmateTheme(value))\n );\n\n if (validEntries.length === 0) {\n console.warn(\n '[react-shiki] invalid multi-theme config, falling back to defaults'\n );\n return {\n isMulti: true,\n themes: DEFAULT_THEMES,\n themesToLoad: Object.values(DEFAULT_THEMES),\n };\n }\n\n if (validEntries.length !== Object.keys(themeInput).length) {\n console.warn(\n '[react-shiki] multi-theme config contained invalid entries; they were dropped'\n );\n }\n\n const themes = Object.fromEntries(validEntries) as Themes;\n return {\n isMulti: true,\n themes,\n themesToLoad: validEntries.map(([, value]) => value as Theme),\n };\n}\n","import type { ShikiTransformer } from 'shiki/core';\n\n/**\n * Creates a transformer that enables line numbers display\n * @param startLine - The starting line number (defaults to 1)\n */\nexport function lineNumbersTransformer(startLine = 1): ShikiTransformer {\n return {\n name: 'react-shiki:line-numbers',\n code(node) {\n this.addClassToHast(node, 'rs-has-line-numbers');\n if (startLine !== 1) {\n const existingStyle = (node.properties?.style as string) || '';\n const newStyle = existingStyle\n ? `${existingStyle}; --line-start: ${startLine}`\n : `--line-start: ${startLine}`;\n node.properties = {\n ...node.properties,\n style: newStyle,\n };\n }\n },\n line(node) {\n this.addClassToHast(node, 'rs-line-number');\n return node;\n },\n };\n}\n","import type {\n BundledTheme,\n CodeOptionsMultipleThemes,\n CodeOptionsSingleTheme,\n CodeToHastOptions,\n} from 'shiki';\n\nimport type { HighlighterOptions } from './types';\nimport type { ResolvedTheme } from './theme';\nimport { lineNumbersTransformer } from './transformers';\n\nconst buildThemeOptions = (\n resolvedTheme: ResolvedTheme,\n defaultColor: HighlighterOptions['defaultColor'],\n cssVariablePrefix: HighlighterOptions['cssVariablePrefix']\n):\n | CodeOptionsMultipleThemes<BundledTheme>\n | CodeOptionsSingleTheme<BundledTheme> => {\n if (resolvedTheme.isMulti) {\n return {\n themes: resolvedTheme.themes,\n defaultColor,\n cssVariablePrefix,\n };\n }\n\n return {\n theme: resolvedTheme.theme,\n };\n};\n\nexport const buildShikiOptions = (\n languageId: string,\n resolvedTheme: ResolvedTheme,\n options: HighlighterOptions\n): CodeToHastOptions => {\n const {\n delay,\n customLanguages,\n preloadLanguages,\n outputFormat,\n highlighter,\n langAlias,\n engine,\n defaultColor,\n cssVariablePrefix,\n showLineNumbers,\n startingLineNumber,\n transformers: userTransformers,\n ...shikiPassthrough\n } = options;\n\n const transformers = [...(userTransformers || [])];\n if (showLineNumbers) {\n transformers.push(lineNumbersTransformer(startingLineNumber));\n }\n\n return {\n lang: languageId,\n ...buildThemeOptions(resolvedTheme, defaultColor, cssVariablePrefix),\n ...shikiPassthrough,\n transformers,\n };\n};\n","import { useEffect, useMemo, useRef, useState } from 'react';\nimport { jsx, jsxs, Fragment } from 'react/jsx-runtime';\nimport { toJsxRuntime } from 'hast-util-to-jsx-runtime';\nimport type { CodeToHastOptions } from 'shiki';\n\nimport type {\n HighlightedCode,\n HighlighterFactory,\n HighlighterOptions,\n Language,\n Theme,\n Themes,\n TimeoutState,\n} from './types';\n\nimport { throttleHighlighting, useStableValue } from './utils';\nimport {\n getEmbeddedLanguages,\n resolveLanguage,\n resolveLoadedLanguage,\n} from './language';\nimport { resolveTheme } from './theme';\nimport { buildShikiOptions } from './options';\n\nexport async function highlight(\n code: string,\n resolved: {\n languageId: string;\n langsToLoad: Language[];\n themesToLoad: Theme[];\n shikiOptions: CodeToHastOptions;\n },\n opts: Pick<\n HighlighterOptions,\n 'highlighter' | 'outputFormat' | 'engine'\n >,\n factory: HighlighterFactory\n) {\n const { languageId, langsToLoad, themesToLoad, shikiOptions } =\n resolved;\n\n const highlighter =\n opts.highlighter ??\n (await (async () => {\n const hl = await factory(langsToLoad, themesToLoad, opts.engine);\n await hl.loadLanguage(\n ...getEmbeddedLanguages(code, languageId, hl)\n );\n return hl;\n })());\n\n const language = resolveLoadedLanguage(\n languageId,\n highlighter.getLoadedLanguages()\n );\n const options = { ...shikiOptions, lang: language };\n\n return opts.outputFormat === 'html'\n ? highlighter.codeToHtml(code, options)\n : toJsxRuntime(highlighter.codeToHast(code, options), {\n jsx,\n jsxs,\n Fragment,\n });\n}\n\nexport const useHighlight = (\n code: string,\n lang: Language,\n themeInput: Theme | Themes,\n options: HighlighterOptions = {},\n highlighterFactory: HighlighterFactory\n) => {\n const [highlightedCode, setHighlightedCode] =\n useState<HighlightedCode>(null);\n\n const stableLang = useStableValue(lang);\n const stableTheme = useStableValue(themeInput);\n const stableOpts = useStableValue(options);\n\n const resolved = useMemo(() => {\n const { languageId, langsToLoad } = resolveLanguage(\n stableLang,\n stableOpts.customLanguages,\n stableOpts.langAlias,\n stableOpts.preloadLanguages\n );\n\n const theme = resolveTheme(stableTheme);\n\n const shikiOptions = buildShikiOptions(languageId, theme, stableOpts);\n return {\n languageId,\n langsToLoad,\n themesToLoad: theme.themesToLoad,\n shikiOptions,\n };\n }, [stableLang, stableTheme, stableOpts]);\n\n const requestIdRef = useRef(0);\n\n const timeoutControl = useRef<TimeoutState>({\n nextAllowedTime: 0,\n timeoutId: undefined,\n });\n\n // Factory is invoked imperatively; its identity is not semantic, so it must\n // not invalidate the effect on every render. See issue #161.\n const highlighterFactoryRef = useRef(highlighterFactory);\n highlighterFactoryRef.current = highlighterFactory;\n\n useEffect(() => {\n const requestId = ++requestIdRef.current;\n\n if (!resolved.languageId) return;\n\n const run = async () => {\n try {\n const result = await highlight(\n code,\n resolved,\n stableOpts,\n highlighterFactoryRef.current\n );\n if (requestId === requestIdRef.current) {\n setHighlightedCode(result);\n }\n } catch (error) {\n console.error('[react-shiki] highlight failed', error);\n }\n };\n\n if (stableOpts.delay) {\n throttleHighlighting(run, timeoutControl, stableOpts.delay);\n } else {\n run();\n }\n\n return () => {\n clearTimeout(timeoutControl.current.timeoutId);\n };\n }, [code, resolved, stableOpts]);\n\n return highlightedCode;\n};\n","import { visit } from 'unist-util-visit';\nimport type { Root } from 'hast';\nimport type { Element } from './types';\n\n/**\n * Rehype plugin to add an 'inline' property to <code> elements\n * Sets 'inline' property to true if the <code> is not within a <pre> tag\n *\n * Pass this plugin to the `rehypePlugins` prop of react-markdown\n * You can then access `inline` as a prop in components passed to react-markdown\n *\n * @example\n * <ReactMarkdown rehypePlugins={[rehypeInlineCodeProperty]} />\n */\nexport function rehypeInlineCodeProperty() {\n return (tree: Root): undefined => {\n visit(tree, 'element', (node: Element, _index, parent) => {\n if (\n node.tagName === 'code' &&\n parent?.type === 'element' &&\n parent.tagName !== 'pre'\n ) {\n node.properties.inline = true;\n }\n });\n };\n}\n\n/**\n * Function to determine if code is inline based on the presence of line breaks\n *\n * @example\n * const isInline = node && isInlineCode(node: Element)\n */\nexport const isInlineCode = (node: Element): boolean => {\n const textContent = (node.children || [])\n .filter((child) => child.type === 'text')\n .map((child) => child.value)\n .join('');\n\n return !textContent.includes('\\n');\n};\n","import '../styles/component.css';\nimport '../styles/features.css';\nimport { clsx } from 'clsx';\n\nimport type {\n HighlighterOptions,\n Language,\n Theme,\n Themes,\n UseShikiHighlighter,\n} from './types';\nimport { forwardRef } from 'react';\n\n/**\n * Props for the ShikiHighlighter component\n */\nexport interface ShikiHighlighterProps extends HighlighterOptions {\n /**\n * The programming language for syntax highlighting\n * Supports custom textmate grammar objects in addition to Shiki's bundled languages\n * @see https://shiki.style/languages\n */\n language: Language;\n\n /**\n * The code to be highlighted\n */\n children: string;\n\n /**\n * The color theme or themes for syntax highlighting\n * Supports single, dual, or multiple themes\n * Supports custom textmate theme objects in addition to Shiki's bundled themes\n *\n * @example\n * theme='github-dark' // single theme\n * theme={{ light: 'github-light', dark: 'github-dark' }} // multi-theme\n *\n * @see https://shiki.style/themes\n */\n theme: Theme | Themes;\n\n /**\n * Controls the application of default styles to the generated code blocks\n *\n * Default styles include padding, overflow handling, border radius, language label styling, and font settings\n * @default true\n */\n addDefaultStyles?: boolean;\n\n /**\n * Add custom inline styles to the generated code block\n */\n style?: React.CSSProperties;\n\n /**\n * Add custom inline styles to the language label\n */\n langStyle?: React.CSSProperties;\n\n /**\n * Add custom CSS class names to the generated code block\n */\n className?: string;\n\n /**\n * Add custom CSS class names to the language label\n */\n langClassName?: string;\n\n /**\n * Whether to show the language label\n * @default true\n */\n showLanguage?: boolean;\n\n /**\n * Whether to show line numbers\n * @default false\n */\n showLineNumbers?: boolean;\n\n /**\n * Starting line number (when showLineNumbers is true)\n * @default 1\n */\n startingLineNumber?: number;\n\n /**\n * The HTML element that wraps the generated code block.\n * @default 'div'\n */\n as?: React.ElementType;\n}\n\n/**\n * Base ShikiHighlighter component factory.\n * This creates a component that uses the provided hook implementation.\n */\nexport const createShikiHighlighterComponent = (\n useShikiHighlighterImpl: UseShikiHighlighter\n) => {\n return forwardRef<HTMLElement, ShikiHighlighterProps>(\n (\n {\n language,\n theme,\n delay,\n transformers,\n defaultColor,\n cssVariablePrefix,\n addDefaultStyles = true,\n style,\n langStyle,\n className,\n langClassName,\n showLanguage = true,\n showLineNumbers = false,\n startingLineNumber = 1,\n children: code,\n as: Element = 'div',\n customLanguages,\n preloadLanguages,\n ...shikiOptions\n },\n ref\n ) => {\n // Destructure some options for use in hook\n const options: HighlighterOptions = {\n delay,\n transformers,\n customLanguages,\n preloadLanguages,\n showLineNumbers,\n defaultColor,\n cssVariablePrefix,\n startingLineNumber,\n ...shikiOptions,\n };\n\n const displayLanguageId =\n typeof language === 'object'\n ? language.name || null\n : language?.trim() || null;\n\n const highlightedCode = useShikiHighlighterImpl(\n code,\n language,\n theme,\n options\n );\n\n return (\n <Element\n ref={ref}\n data-testid=\"shiki-container\"\n data-slot=\"container\"\n className={clsx(\n 'rs-root',\n 'not-prose',\n addDefaultStyles && 'rs-default-styles',\n className\n )}\n style={style}\n >\n {showLanguage && displayLanguageId ? (\n <span\n id=\"language-label\"\n data-slot=\"language-label\"\n className={clsx('rs-language-label', langClassName)}\n style={langStyle}\n >\n {displayLanguageId}\n </span>\n ) : null}\n {typeof highlightedCode === 'string' ? (\n <div\n data-slot=\"content\"\n dangerouslySetInnerHTML={{ __html: highlightedCode }}\n />\n ) : (\n highlightedCode\n )}\n </Element>\n );\n }\n );\n};\n"],"mappings":"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"}