UNPKG

react-shiki

Version:

Syntax highlighter component for react using shiki

515 lines (514 loc) 14 kB
import { ReactElement } from "react"; import { BundledHighlighterOptions, BundledLanguage, BundledTheme, CodeOptionsMultipleThemes, CodeToHastOptions, Highlighter, HighlighterCore, LanguageRegistration, SpecialLanguage, StringLiteralUnion, ThemeRegistrationAny } from "shiki"; //#region ../node_modules/.pnpm/@types+unist@3.0.3/node_modules/@types/unist/index.d.ts // ## Interfaces /** * Info associated with nodes by the ecosystem. * * This space is guaranteed to never be specified by unist or specifications * implementing unist. * But you can use it in utilities and plugins to store data. * * This type can be augmented to register custom data. * For example: * * ```ts * declare module 'unist' { * interface Data { * // `someNode.data.myId` is typed as `number | undefined` * myId?: number | undefined * } * } * ``` */ interface Data$1 {} /** * One place in a source file. */ interface Point { /** * Line in a source file (1-indexed integer). */ line: number; /** * Column in a source file (1-indexed integer). */ column: number; /** * Character in a source file (0-indexed integer). */ offset?: number | undefined; } /** * Position of a node in a source document. * * A position is a range between two points. */ interface Position { /** * Place of the first character of the parsed source region. */ start: Point; /** * Place of the first character after the parsed source region. */ end: Point; } /** * Abstract unist node. * * The syntactic unit in unist syntax trees are called nodes. * * This interface is supposed to be extended. * If you can use {@link Literal} or {@link Parent}, you should. * But for example in markdown, a `thematicBreak` (`***`), is neither literal * nor parent, but still a node. */ interface Node$1 { /** * Node type. */ type: string; /** * Info from the ecosystem. */ data?: Data$1 | undefined; /** * Position of a node in a source document. * * Nodes that are generated (not in the original source document) must not * have a position. */ position?: Position | undefined; } //#endregion //#region ../node_modules/.pnpm/@types+hast@3.0.4/node_modules/@types/hast/index.d.ts // ## Interfaces /** * Info associated with hast nodes by the ecosystem. * * This space is guaranteed to never be specified by unist or hast. * But you can use it in utilities and plugins to store data. * * This type can be augmented to register custom data. * For example: * * ```ts * declare module 'hast' { * interface Data { * // `someNode.data.myId` is typed as `number | undefined` * myId?: number | undefined * } * } * ``` */ interface Data extends Data$1 {} /** * Info associated with an element. */ interface Properties { [PropertyName: string]: boolean | number | string | null | undefined | Array<string | number>; } // ## Content maps /** * Union of registered hast nodes that can occur in {@link Element}. * * To register mote custom hast nodes, add them to {@link ElementContentMap}. * They will be automatically added here. */ type ElementContent = ElementContentMap[keyof ElementContentMap]; /** * Registry of all hast nodes that can occur as children of {@link Element}. * * For a union of all {@link Element} children, see {@link ElementContent}. */ interface ElementContentMap { comment: Comment; element: Element$1; text: Text; } /** * Union of registered hast nodes that can occur in {@link Root}. * * To register custom hast nodes, add them to {@link RootContentMap}. * They will be automatically added here. */ type RootContent = RootContentMap[keyof RootContentMap]; /** * Registry of all hast nodes that can occur as children of {@link Root}. * * > 👉 **Note**: {@link Root} does not need to be an entire document. * > it can also be a fragment. * * For a union of all {@link Root} children, see {@link RootContent}. */ interface RootContentMap { comment: Comment; doctype: Doctype; element: Element$1; text: Text; } // ## Abstract nodes /** * Abstract hast node. * * This interface is supposed to be extended. * If you can use {@link Literal} or {@link Parent}, you should. * But for example in HTML, a `Doctype` is neither literal nor parent, but * still a node. * * To register custom hast nodes, add them to {@link RootContentMap} and other * places where relevant (such as {@link ElementContentMap}). * * For a union of all registered hast nodes, see {@link Nodes}. */ interface Node extends Node$1 { /** * Info from the ecosystem. */ data?: Data | undefined; } /** * Abstract hast node that contains the smallest possible value. * * This interface is supposed to be extended if you make custom hast nodes. * * For a union of all registered hast literals, see {@link Literals}. */ interface Literal extends Node { /** * Plain-text value. */ value: string; } /** * Abstract hast node that contains other hast nodes (*children*). * * This interface is supposed to be extended if you make custom hast nodes. * * For a union of all registered hast parents, see {@link Parents}. */ interface Parent extends Node { /** * List of children. */ children: RootContent[]; } // ## Concrete nodes /** * HTML comment. */ interface Comment extends Literal { /** * Node type of HTML comments in hast. */ type: "comment"; /** * Data associated with the comment. */ data?: CommentData | undefined; } /** * Info associated with hast comments by the ecosystem. */ interface CommentData extends Data {} /** * HTML document type. */ interface Doctype extends Node$1 { /** * Node type of HTML document types in hast. */ type: "doctype"; /** * Data associated with the doctype. */ data?: DoctypeData | undefined; } /** * Info associated with hast doctypes by the ecosystem. */ interface DoctypeData extends Data {} /** * HTML element. */ interface Element$1 extends Parent { /** * Node type of elements. */ type: "element"; /** * Tag name (such as `'body'`) of the element. */ tagName: string; /** * Info associated with the element. */ properties: Properties; /** * Children of element. */ children: ElementContent[]; /** * When the `tagName` field is `'template'`, a `content` field can be * present. */ content?: Root | undefined; /** * Data associated with the element. */ data?: ElementData | undefined; } /** * Info associated with hast elements by the ecosystem. */ interface ElementData extends Data {} /** * Document fragment or a whole document. * * Should be used as the root of a tree and must not be used as a child. * * Can also be used as the value for the content field on a `'template'` element. */ interface Root extends Parent { /** * Node type of hast root. */ type: "root"; /** * Children of root. */ children: RootContent[]; /** * Data associated with the hast root. */ data?: RootData | undefined; } /** * Info associated with hast root nodes by the ecosystem. */ interface RootData extends Data {} /** * HTML character data (plain text). */ interface Text extends Literal { /** * Node type of HTML character data (plain text) in hast. */ type: "text"; /** * Data associated with the text. */ data?: TextData | undefined; } /** * Info associated with hast texts by the ecosystem. */ interface TextData extends Data {} //#endregion //#region src/lib/types.d.ts /** * HTML Element, use to type `node` from react-markdown */ type Element = Element$1; /** * A Shiki BundledLanguage or a custom textmate grammar object * @see https://shiki.style/languages */ type Language = LanguageRegistration | StringLiteralUnion<BundledLanguage> | SpecialLanguage | undefined; type MultiThemeKey = 'dark' | 'light' | (string & {}); /** * A Shiki BundledTheme or a custom textmate theme object * @see https://shiki.style/themes */ type Theme = ThemeRegistrationAny | StringLiteralUnion<BundledTheme>; /** * A map of color names to themes. * This allows you to specify multiple themes for the generated code. * Supports custom textmate theme objects in addition to Shiki's bundled themes * * @example * ```ts * useShikiHighlighter(code, language, { * light: 'github-light', * dark: 'github-dark', * dim: 'github-dark-dimmed' * }) * ``` * * @see https://shiki.style/guide/dual-themes */ type Themes = { [Key in MultiThemeKey]: Theme }; /** * Configuration options specific to react-shiki */ interface ReactShikiOptions { /** * Minimum time (in milliseconds) between highlight operations. * @default undefined (no throttling) */ delay?: number; /** * Custom textmate grammars to be preloaded for highlighting. * @deprecated Use preloadLanguages instead. */ customLanguages?: LanguageRegistration | LanguageRegistration[]; /** * Preload custom grammars or bundled languages. * Supports custom textmate grammars, replaces deprecated `customLanguages` */ preloadLanguages?: Language | Language[]; /** * Output format for the highlighted code. * - 'react': Returns React nodes (default, safer) * - 'html': Returns HTML string (~15-45% faster, requires dangerouslySetInnerHTML) * @default 'react' */ outputFormat?: 'react' | 'html'; /** * Custom Shiki highlighter instance to use instead of the default one. * Keeps bundle small by only importing specified languages/themes. * Can be either a Highlighter or HighlighterCore instance. * * @example * import { * createHighlighterCore, * createOnigurumaEngine, * useShikiHighlighter * } from "react-shiki"; * * const customHighlighter = await createHighlighterCore({ * themes: [ * import('@shikijs/themes/nord') * ], * langs: [ * import('@shikijs/langs/javascript'), * import('@shikijs/langs/typescript') * ], * engine: createOnigurumaEngine(import('shiki/wasm')) * }); * * const highlightedCode = useShikiHighlighter(code, language, theme, { * highlighter: customHighlighter, * }); */ highlighter?: Highlighter | HighlighterCore; /** * Whether to show line numbers * @default false */ showLineNumbers?: boolean; /** * Starting line number (when showLineNumbers is true) * @default 1 */ startingLineNumber?: number; } /** * Configuration options for the syntax highlighter * Extends CodeToHastOptions to allow passing any Shiki options directly */ interface HighlighterOptions extends ReactShikiOptions, Pick<CodeOptionsMultipleThemes<BundledTheme>, 'defaultColor' | 'cssVariablePrefix'>, Omit<CodeToHastOptions, 'lang' | 'theme' | 'themes'>, Pick<BundledHighlighterOptions<string, string>, 'langAlias' | 'engine'> {} /** * State for the throttling logic */ /** * Public API signature for the useShikiHighlighter hook. */ type HighlightedCode = ReactElement | string | null; type UseShikiHighlighter = (code: string, lang: Language, themeInput: Theme | Themes, options?: HighlighterOptions) => HighlightedCode; //#endregion //#region src/lib/plugins.d.ts /** * Rehype plugin to add an 'inline' property to <code> elements * Sets 'inline' property to true if the <code> is not within a <pre> tag * * Pass this plugin to the `rehypePlugins` prop of react-markdown * You can then access `inline` as a prop in components passed to react-markdown * * @example * <ReactMarkdown rehypePlugins={[rehypeInlineCodeProperty]} /> */ declare function rehypeInlineCodeProperty(): (tree: Root) => undefined; /** * Function to determine if code is inline based on the presence of line breaks * * @example * const isInline = node && isInlineCode(node: Element) */ declare const isInlineCode: (node: Element) => boolean; //#endregion //#region src/lib/component.d.ts /** * Props for the ShikiHighlighter component */ interface ShikiHighlighterProps extends HighlighterOptions { /** * The programming language for syntax highlighting * Supports custom textmate grammar objects in addition to Shiki's bundled languages * @see https://shiki.style/languages */ language: Language; /** * The code to be highlighted */ children: string; /** * The color theme or themes for syntax highlighting * Supports single, dual, or multiple themes * Supports custom textmate theme objects in addition to Shiki's bundled themes * * @example * theme='github-dark' // single theme * theme={{ light: 'github-light', dark: 'github-dark' }} // multi-theme * * @see https://shiki.style/themes */ theme: Theme | Themes; /** * Controls the application of default styles to the generated code blocks * * Default styles include padding, overflow handling, border radius, language label styling, and font settings * @default true */ addDefaultStyles?: boolean; /** * Add custom inline styles to the generated code block */ style?: React.CSSProperties; /** * Add custom inline styles to the language label */ langStyle?: React.CSSProperties; /** * Add custom CSS class names to the generated code block */ className?: string; /** * Add custom CSS class names to the language label */ langClassName?: string; /** * Whether to show the language label * @default true */ showLanguage?: boolean; /** * Whether to show line numbers * @default false */ showLineNumbers?: boolean; /** * Starting line number (when showLineNumbers is true) * @default 1 */ startingLineNumber?: number; /** * The HTML element that wraps the generated code block. * @default 'div' */ as?: React.ElementType; } //#endregion export { HighlighterOptions as a, Themes as c, Element as i, UseShikiHighlighter as l, isInlineCode as n, Language as o, rehypeInlineCodeProperty as r, Theme as s, ShikiHighlighterProps as t }; //# sourceMappingURL=component-CjpuWrgO.d.mts.map