Files
react-to-markdown/src/renderer.ts
T

117 lines
3.3 KiB
TypeScript

import { root, text } from 'mdast-builder';
import { Children as ReactChildren, Fragment } from 'react';
import * as stringify from 'remark-stringify';
import * as unified from 'unified';
// tslint:disable-next-line: no-implicit-dependencies
import { Parent as MDASTParent } from 'unist';
import {
isClassComponent,
isFunctionalComponent,
ReactElementSubsetWithPrimitive,
} from './helpers';
import handleTag from './tagHandler';
function render(
node: ReactElementSubsetWithPrimitive,
parentASTNode?: MDASTParent
): string | null {
let parentASTNodeMod: MDASTParent;
if (parentASTNode === undefined) {
parentASTNodeMod = root([]);
} else {
parentASTNodeMod = parentASTNode;
}
let nextParentASTNode = parentASTNodeMod;
if (node === null || node === undefined) {
// Handle (skip) null and undefined nodes
} else if (typeof node === 'string' || typeof node === 'number') {
// Handle (render) string and number nodes as string in markdown
const currASTNode = text(node.toString());
parentASTNodeMod.children.push(currASTNode);
} else if (Array.isArray(node)) {
// Handle (render) Fragment nodes or arrays
node.forEach(fragmentEle => render(fragmentEle, parentASTNodeMod));
} else if (node.type === Fragment) {
const childrenArray = ReactChildren.toArray(
node.props.children
) as ReactElementSubsetWithPrimitive[];
// Render processed array of children
render(childrenArray, parentASTNodeMod);
} else {
const { props, type: elementType } = node;
let processChildren = true;
// Handle nested function or class components
{
if (isFunctionalComponent(elementType)) {
const renderOutputRaw = elementType.call(null, props);
const renderOutput = ReactChildren.toArray(
renderOutputRaw
) as ReactElementSubsetWithPrimitive[];
// Render processed array of children
render(renderOutput, parentASTNodeMod);
}
if (isClassComponent(elementType)) {
const elementInstance = new elementType(props);
const renderOutputRaw = elementInstance.render();
const renderOutput = ReactChildren.toArray(
renderOutputRaw
) as ReactElementSubsetWithPrimitive[];
// Render processed array of children
render(renderOutput, parentASTNodeMod);
}
}
{
// Handle intrinsic types of element like div, h1
const newParentASTNode = handleTag(node, parentASTNodeMod);
if (newParentASTNode === undefined) {
processChildren = false;
} else {
processChildren = true;
nextParentASTNode = newParentASTNode;
}
}
// Handle children
if (processChildren) {
const { children } = props;
// Render processed array of children
const renderOutput = ReactChildren.toArray(
children
) as ReactElementSubsetWithPrimitive[];
render(renderOutput, nextParentASTNode);
}
}
// Return stuff
{
// For root call, return string
if (parentASTNode === undefined && parentASTNodeMod.children.length > 0) {
const processor = unified().use(stringify, {
bullet: '-',
fence: '`',
fences: true,
incrementListMarker: true,
});
const output = processor.stringify(parentASTNodeMod);
return output;
}
return null;
}
}
export { render };