react-shiki
Version:
Syntax highlighter component for react using shiki
515 lines (514 loc) • 14 kB
text/typescript
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