markdown-to-jsx
Version:
A very fast and versatile markdown toolchain. AST, React, React Native, SolidJS, Vue, Markdown, and HTML output available with full customization.
402 lines (401 loc) • 13.8 kB
TypeScript
import { Component, JSX as JSX2, Accessor, Context } from "solid-js";
import * as React from "react";
/**
* Analogous to `node.type`. Please note that the values here may change at any time,
* so do not hard code against the value directly.
*/
declare const RuleTypeConst: {
readonly blockQuote: 0;
readonly breakLine: 1;
readonly breakThematic: 2;
readonly codeBlock: 3;
readonly codeInline: 4;
readonly footnote: 5;
readonly footnoteReference: 6;
readonly frontmatter: 7;
readonly gfmTask: 8;
readonly heading: 9;
readonly htmlBlock: 10;
readonly htmlComment: 11;
readonly htmlSelfClosing: 12;
readonly image: 13;
readonly link: 14;
readonly orderedList: 15;
readonly paragraph: 16;
readonly ref: 17;
readonly refCollection: 18;
readonly table: 19;
readonly text: 20;
readonly textFormatted: 21;
readonly unorderedList: 22;
};
type RuleTypeValue = (typeof RuleTypeConst)[keyof typeof RuleTypeConst];
/**
* markdown-to-jsx types and interfaces
*/
declare namespace MarkdownToJSX {
/**
* RequireAtLeastOne<{ ... }> <- only requires at least one key
*/
type RequireAtLeastOne<
T,
Keys extends keyof T = keyof T
> = Pick<T, Exclude<keyof T, Keys>> & { [K in Keys]-? : Required<Pick<T, K>> & Partial<Pick<T, Exclude<Keys, K>>> }[Keys];
export type CreateElement = typeof React.createElement;
export type HTMLTags = keyof React.JSX.IntrinsicElements & (string & {});
export type State = {
/** true if the current content is inside anchor link grammar */
inAnchor?: boolean;
/** true if inside a blockquote */
inBlockQuote?: boolean;
/** true if parsing in an HTML context */
inHTML?: boolean;
/** true if in a list */
inList?: boolean;
/** true if parsing in an inline context (subset of rules around formatting and links) */
inline?: boolean;
/** use this for the `key` prop */
key?: string | number;
/** reference definitions (footnotes are stored with '^' prefix) */
refs?: {
[key: string]: {
target: string;
title: string | undefined;
};
};
/** current recursion depth during rendering */
renderDepth?: number;
};
export interface BlockQuoteNode {
alert?: string;
children: MarkdownToJSX.ASTNode[];
type: typeof RuleType2.blockQuote;
}
export interface BreakLineNode {
type: typeof RuleType2.breakLine;
}
export interface BreakThematicNode {
type: typeof RuleType2.breakThematic;
}
export interface CodeBlockNode {
type: typeof RuleType2.codeBlock;
attrs?: React.JSX.IntrinsicAttributes;
lang?: string;
text: string;
}
export interface CodeInlineNode {
type: typeof RuleType2.codeInline;
text: string;
}
export interface FootnoteNode {
type: typeof RuleType2.footnote;
}
export interface FootnoteReferenceNode {
type: typeof RuleType2.footnoteReference;
target: string;
text: string;
}
export interface FrontmatterNode {
type: typeof RuleType2.frontmatter;
text: string;
}
export interface GFMTaskNode {
type: typeof RuleType2.gfmTask;
completed: boolean;
}
export interface HeadingNode {
type: typeof RuleType2.heading;
children: MarkdownToJSX.ASTNode[];
id: string;
level: 1 | 2 | 3 | 4 | 5 | 6;
}
export interface HTMLCommentNode {
type: typeof RuleType2.htmlComment;
text: string;
}
export interface ImageNode {
type: typeof RuleType2.image;
alt?: string;
target: string;
title?: string;
}
export interface LinkNode {
type: typeof RuleType2.link;
children: MarkdownToJSX.ASTNode[];
target: string | null;
title?: string;
}
export interface OrderedListNode {
type: typeof RuleType2.orderedList;
items: MarkdownToJSX.ASTNode[][];
start?: number;
}
export interface UnorderedListNode {
type: typeof RuleType2.unorderedList;
items: MarkdownToJSX.ASTNode[][];
}
export interface ParagraphNode {
type: typeof RuleType2.paragraph;
children: MarkdownToJSX.ASTNode[];
}
export interface ReferenceNode {
type: typeof RuleType2.ref;
}
export interface ReferenceCollectionNode {
type: typeof RuleType2.refCollection;
refs: {
[key: string]: {
target: string;
title: string | undefined;
};
};
}
export interface TableNode {
type: typeof RuleType2.table;
/**
* alignment for each table column
*/
align: ("left" | "right" | "center")[];
cells: MarkdownToJSX.ASTNode[][][];
header: MarkdownToJSX.ASTNode[][];
}
export interface TextNode {
type: typeof RuleType2.text;
text: string;
}
export interface FormattedTextNode {
type: typeof RuleType2.textFormatted;
/**
* the corresponding html tag
*/
tag: string;
children: MarkdownToJSX.ASTNode[];
}
export interface HTMLNode {
type: typeof RuleType2.htmlBlock;
attrs?: Record<string, any>;
children?: ASTNode[] | undefined;
verbatim?: boolean;
rawAttrs?: string;
rawText?: string | undefined;
/** @deprecated Use `rawText` instead. This property will be removed in a future major version. */
text?: string | undefined;
tag: string;
}
export interface HTMLSelfClosingNode {
type: typeof RuleType2.htmlSelfClosing;
attrs?: Record<string, any>;
isClosingTag?: boolean;
tag: string;
rawText?: string;
}
export type ASTNode = BlockQuoteNode | BreakLineNode | BreakThematicNode | CodeBlockNode | CodeInlineNode | FootnoteNode | FootnoteReferenceNode | FrontmatterNode | GFMTaskNode | HeadingNode | HTMLCommentNode | ImageNode | LinkNode | OrderedListNode | UnorderedListNode | ParagraphNode | ReferenceNode | ReferenceCollectionNode | TableNode | TextNode | FormattedTextNode | HTMLNode | HTMLSelfClosingNode;
export type ASTRender = (ast: MarkdownToJSX.ASTNode | MarkdownToJSX.ASTNode[], state: MarkdownToJSX.State) => React.ReactNode;
export type Override = RequireAtLeastOne<{
component: React.ElementType;
props: Object;
}> | React.ElementType;
export type Overrides = { [tag in HTMLTags]? : Override } & {
[customComponent: string]: Override;
};
export type Options = Partial<{
/**
* Ultimate control over the output of all rendered JSX.
*/
createElement: (tag: Parameters<CreateElement>[0], props: React.JSX.IntrinsicAttributes, ...children: React.ReactNode[]) => React.ReactNode;
/**
* The library automatically generates an anchor tag for bare URLs included in the markdown
* document, but this behavior can be disabled if desired.
*/
disableAutoLink: boolean;
/**
* Disable the compiler's best-effort transcription of provided raw HTML
* into JSX-equivalent. This is the functionality that prevents the need to
* use `dangerouslySetInnerHTML` in React.
*/
disableParsingRawHTML: boolean;
/**
* Enable GFM tagfilter extension to filter potentially dangerous HTML tags.
* When enabled, the following tags are escaped: title, textarea, style, xmp,
* iframe, noembed, noframes, script, plaintext.
* https://github.github.com/gfm/#disallowed-raw-html-extension-
* @default true
*/
tagfilter?: boolean;
/**
* Forces the compiler to have space between hash sign and the header text which
* is explicitly stated in the most of the markdown specs.
* https://github.github.com/gfm/#atx-heading
* `The opening sequence of # characters must be followed by a space or by the end of line.`
*/
enforceAtxHeadings: boolean;
/**
* **⚠️ SECURITY WARNING: STRONGLY DISCOURAGED FOR USER INPUTS**
*
* When enabled, attempts to eval expressions in JSX props that cannot be serialized
* as JSON (functions, variables, complex expressions). This uses `eval()` which can
* execute arbitrary code.
*
* **ONLY use this option when:**
* - The markdown source is completely trusted (e.g., your own documentation)
* - You control all JSX components and their props
* - The content is NOT user-generated or user-editable
*
* **DO NOT use this option when:**
* - Processing user-submitted markdown
* - Rendering untrusted content
* - Building public-facing applications with user content
*
* Example unsafe input: `<Component onClick={() => fetch('/admin/delete-all')} />`
*
* When disabled (default), unserializable expressions remain as strings that can be
* safely inspected or handled on a case-by-case basis via custom renderRule logic.
*
* @default false
*/
evalUnserializableExpressions?: boolean;
/**
* Forces the compiler to always output content with a block-level wrapper
* (`<p>` or any block-level syntax your markdown already contains.)
*/
forceBlock: boolean;
/**
* Forces the compiler to always output content with an inline wrapper (`<span>`)
*/
forceInline: boolean;
/**
* Forces the compiler to wrap results, even if there is only a single
* child or no children.
*/
forceWrapper: boolean;
/**
* Selectively control the output of particular HTML tags as they would be
* emitted by the compiler.
*/
overrides: Overrides;
/**
* Allows for full control over rendering of particular rules.
* For example, to implement a LaTeX renderer such as `react-katex`:
*
* ```
* renderRule(next, node, renderChildren, state) {
* if (node.type === RuleType.codeBlock && node.lang === 'latex') {
* return (
* <TeX as="div" key={state.key}>
* {String.raw`${node.text}`}
* </TeX>
* )
* }
*
* return next();
* }
* ```
*
* Thar be dragons obviously, but you can do a lot with this
* (have fun!) To see how things work internally, check the `render`
* method in source for a particular rule.
*/
renderRule: (next: () => React.ReactNode, node: ASTNode, renderChildren: ASTRender, state: State) => React.ReactNode;
/**
* Override the built-in sanitizer function for URLs, etc if desired. The built-in version is available as a library
export called `sanitizer`.
*/
sanitizer: (value: string, tag: string, attribute: string) => string | null;
/**
* Override normalization of non-URI-safe characters for use in generating
* HTML IDs for anchor linking purposes.
*/
slugify: (input: string, defaultFn: (input: string) => string) => string;
/**
* Declare the type of the wrapper to be used when there are multiple
* children to render. Set to `null` to get an array of children back
* without any wrapper, or use `React.Fragment` to get a React element
* that won't show up in the DOM.
*/
wrapper: React.ElementType | null;
/**
* Props to apply to the wrapper element.
*/
wrapperProps?: React.JSX.IntrinsicAttributes;
/**
* Preserve frontmatter in the output by rendering it as a <pre> element.
* By default, frontmatter is parsed but not rendered.
* @default false
*/
preserveFrontmatter?: boolean;
}>;
}
declare const RuleType2: typeof RuleTypeConst;
type RuleType2 = RuleTypeValue;
type RequireAtLeastOne<
T,
Keys extends keyof T = keyof T
> = MarkdownToJSX.RequireAtLeastOne<T, Keys>;
declare global {
var parseMetrics: {
blockParsers: {
[key: string]: {
attempts: number;
hits: number;
hitTimings: number[];
};
};
inlineParsers: {
[key: string]: {
attempts: number;
hits: number;
hitTimings: number[];
};
};
totalOperations: number;
blockParseIterations: number;
inlineParseIterations: number;
} | null;
var parseMetricsStartTimes: Map<string, number> | null;
}
/**
* Given a markdown string, return an abstract syntax tree (AST) of the markdown.
*
* The first node in the AST is a reference collection node. This node contains all the
* reference definitions found in the markdown. These reference definitions are used to
* resolve reference links and images in the markdown.
*
* @param source - The markdown string to parse.
* @param options - The options for the parser.
* @returns The AST of the markdown.
*/
declare function parser(source: string, options?: MarkdownToJSX.Options): MarkdownToJSX.ASTNode[];
declare function sanitizer(input: string): string | null;
declare function slugify(str: string): string;
type HTag = string | Component<Record<string, unknown>>;
type HProps = Record<string, unknown>;
type HChildren = JSX2.Element | JSX2.Element[] | string | null;
type SolidOverride = RequireAtLeastOne<{
component: string | Component<Record<string, unknown>>;
props: Record<string, unknown>;
}> | string | Component<Record<string, unknown>>;
type SolidOverrides = { [tag in MarkdownToJSX.HTMLTags]? : SolidOverride } & {
[customComponent: string]: SolidOverride;
};
type SolidOptions = Omit<MarkdownToJSX.Options, "createElement" | "wrapperProps" | "renderRule" | "overrides"> & {
createElement?: (tag: HTag, props: HProps, ...children: HChildren[]) => JSX2.Element;
wrapperProps?: JSX2.HTMLAttributes<HTMLElement>;
renderRule?: (next: () => JSX2.Element | string | null, node: MarkdownToJSX.ASTNode, renderChildren: (children: MarkdownToJSX.ASTNode[]) => JSX2.Element | JSX2.Element[], state: Omit<MarkdownToJSX.State, "key">) => JSX2.Element | string | null;
overrides?: SolidOverrides;
};
declare function astToJSX(ast: MarkdownToJSX.ASTNode[], options?: SolidOptions): JSX2.Element | JSX2.Element[] | null;
declare function compiler(markdown?: string, options?: SolidOptions): JSX2.Element | JSX2.Element[] | null;
declare const MarkdownContext: Context<SolidOptions | undefined>;
declare const MarkdownProvider: Component<{
options?: SolidOptions;
children: JSX2.Element;
}>;
/**
* A SolidJS component for easy markdown rendering. Feed the markdown content as a direct child
* and the rest is taken care of automatically. Supports reactive content via signals/accessors.
*/
declare const Markdown: Component<Omit<JSX2.HTMLAttributes<HTMLElement>, "children"> & {
children?: string | Accessor<string> | null;
options?: SolidOptions;
}>;
export { slugify, sanitizer, parser, Markdown as default, compiler, astToJSX, SolidOverrides, SolidOverride, SolidOptions, RuleType2 as RuleType, MarkdownToJSX, MarkdownProvider, MarkdownContext, Markdown };