/* eslint-disable */ // @ts-nocheck /** * This file is provided under the MIT License * Copyright (c) 2015 Espen Hovlandsdal * https://github.com/andi23rosca/solid-markdown/blob/master/license */ import { Component, JSX } from "solid-js"; import { Dynamic } from "solid-js/web"; import { stringify as commas } from "comma-separated-tokens"; import type { Comment, DocType, Element, ElementContent, Root, Text, } from "hast"; import type { Schema } from "property-information"; import { find, hastToReact, svg } from "property-information"; import { stringify as spaces } from "space-separated-tokens"; import style from "style-to-object"; import type { NormalComponents, SolidMarkdownProps } from "./complex-types"; export type Position = { start: { line: number | null; column: number | null; offset: number | null }; end: { line: number | null; column: number | null; offset: number | null }; }; type Raw = { type: "raw"; value: string; }; type Context = { options: Options; schema: Schema; listDepth: number; }; type TransformLink = ( href: string, children: ElementContent[], title?: string, ) => string; type TransformImage = (src: string, alt: string, title?: string) => string; type TransformLinkTargetType = | "_self" | "_blank" | "_parent" | "_top" | (string & {}); type TransformLinkTarget = ( href: string, children: ElementContent[], title?: string, ) => TransformLinkTargetType | undefined; type SolidMarkdownNames = keyof JSX.IntrinsicElements; type CodeComponent = Component< JSX.IntrinsicElements["code"] & SolidMarkdownProps & { inline?: boolean } >; type HeadingComponent = Component< JSX.IntrinsicElements["h1"] & SolidMarkdownProps & { level: number } >; type LiComponent = Component< JSX.IntrinsicElements["li"] & SolidMarkdownProps & { checked: boolean | null; index: number; ordered: boolean; } >; type OrderedListComponent = Component< JSX.IntrinsicElements["ol"] & SolidMarkdownProps & { depth: number; ordered: true } >; type TableCellComponent = Component< JSX.IntrinsicElements["table"] & SolidMarkdownProps & { style?: Record; isHeader: boolean } >; type TableRowComponent = Component< JSX.IntrinsicElements["tr"] & SolidMarkdownProps & { isHeader: boolean } >; type UnorderedListComponent = Component< JSX.IntrinsicElements["ul"] & SolidMarkdownProps & { depth: number; ordered: false } >; type SpecialComponents = { code: CodeComponent | SolidMarkdownNames; h1: HeadingComponent | SolidMarkdownNames; h2: HeadingComponent | SolidMarkdownNames; h3: HeadingComponent | SolidMarkdownNames; h4: HeadingComponent | SolidMarkdownNames; h5: HeadingComponent | SolidMarkdownNames; h6: HeadingComponent | SolidMarkdownNames; li: LiComponent | SolidMarkdownNames; ol: OrderedListComponent | SolidMarkdownNames; td: TableCellComponent | SolidMarkdownNames; th: TableCellComponent | SolidMarkdownNames; tr: TableRowComponent | SolidMarkdownNames; ul: UnorderedListComponent | SolidMarkdownNames; }; type Components = Partial> & Partial; export type Options = { sourcePos: boolean; rawSourcePos: boolean; skipHtml: boolean; includeElementIndex: boolean; transformLinkUri: null | false | TransformLink; transformImageUri?: TransformImage; linkTarget: TransformLinkTargetType | TransformLinkTarget; components: Components; }; const own = {}.hasOwnProperty; // The table-related elements that must not contain whitespace text according // to React. const tableElements = new Set(["table", "thead", "tbody", "tfoot", "tr"]); export function childrenToSolid( context: Context, node: Element | Root, ): JSX.Element[] { const children: JSX.Element[] = []; let childIndex = -1; // let child: Comment | DocType | Element | Raw | Text; while (++childIndex < node.children.length) { const child = node.children[childIndex] as | Comment | DocType | Element | Raw | Text; if (child.type === "element") { children.push(toSolid(context, child, childIndex, node)); } else if (child.type === "text") { // React does not permit whitespace text elements as children of table: // cf. https://github.com/remarkjs/react-markdown/issues/576 if ( node.type !== "element" || !tableElements.has(node.tagName) || child.value !== "\n" ) { children.push(child.value); } } else if (child.type === "raw" && !context.options.skipHtml) { // Default behavior is to show (encoded) HTML. children.push(child.value); } } return children; } function toSolid( context: Context, node: Element, index: number, parent: Element | Root, ): JSX.Element { const options = context.options; const parentSchema = context.schema; const name = node.tagName as SolidMarkdownNames; const properties: Record = {}; let schema = parentSchema; let property: string; if (parentSchema.space === "html" && name === "svg") { schema = svg; context.schema = schema; } if (node.properties) { for (property in node.properties) { if (own.call(node.properties, property)) { addProperty(properties, property, node.properties[property], context); } } } if (name === "ol" || name === "ul") { context.listDepth++; } const children = childrenToSolid(context, node); if (name === "ol" || name === "ul") { context.listDepth--; } // Restore parent schema. context.schema = parentSchema; // Nodes created by plugins do not have positional info, in which case we use // an object that matches the position interface. const position = node.position || { start: { line: null, column: null, offset: null }, end: { line: null, column: null, offset: null }, }; const component = options.components && own.call(options.components, name) ? options.components[name] : name; const basic = typeof component === "string"; //|| component === React.Fragment; // TODO Reimplement is Valid // if (!ReactIs.isValidElementType(component)) { // throw new TypeError( // `Component for name \`${name}\` not defined or is not renderable` // ); // } properties.key = [ name, position.start.line, position.start.column, index, ].join("-"); if (name === "a" && options.linkTarget) { properties.target = typeof options.linkTarget === "function" ? options.linkTarget( String(properties.href || ""), node.children, typeof properties.title === "string" ? properties.title : undefined, ) : options.linkTarget; } if (name === "a" && options.transformLinkUri) { properties.href = options.transformLinkUri( String(properties.href || ""), node.children, typeof properties.title === "string" ? properties.title : undefined, ); } if ( !basic && name === "code" && parent.type === "element" && parent.tagName !== "pre" ) { properties.inline = true; } if ( !basic && (name === "h1" || name === "h2" || name === "h3" || name === "h4" || name === "h5" || name === "h6") ) { properties.level = Number.parseInt(name.charAt(1), 10); } if (name === "img" && options.transformImageUri) { properties.src = options.transformImageUri( String(properties.src || ""), String(properties.alt || ""), typeof properties.title === "string" ? properties.title : undefined, ); } if (!basic && name === "li" && parent.type === "element") { const input = getInputElement(node); properties.checked = input && input.properties ? Boolean(input.properties.checked) : null; properties.index = getElementsBeforeCount(parent, node); properties.ordered = parent.tagName === "ol"; } if (!basic && (name === "ol" || name === "ul")) { properties.ordered = name === "ol"; properties.depth = context.listDepth; } if (name === "td" || name === "th") { if (properties.align) { if (!properties.style) properties.style = {}; // @ts-expect-error assume `style` is an object properties.style.textAlign = properties.align; delete properties.align; } if (!basic) { properties.isHeader = name === "th"; } } if (!basic && name === "tr" && parent.type === "element") { properties.isHeader = Boolean(parent.tagName === "thead"); } // If `sourcePos` is given, pass source information (line/column info from markdown source). if (options.sourcePos) { properties["data-sourcepos"] = flattenPosition(position); } if (!basic && options.rawSourcePos) { properties.sourcePosition = node.position; } // If `includeElementIndex` is given, pass node index info to components. if (!basic && options.includeElementIndex) { properties.index = getElementsBeforeCount(parent, node); properties.siblingCount = getElementsBeforeCount(parent); } if (!basic) { properties.node = node; } return ( {children} ); } function getInputElement(node: Element | Root): Element | null { let index = -1; while (++index < node.children.length) { const child = node.children[index]; if (child.type === "element" && child.tagName === "input") { return child; } } return null; } function getElementsBeforeCount( parent: Element | Root, node?: Element, ): number { let index = -1; let count = 0; while (++index < parent.children.length) { if (parent.children[index] === node) break; if (parent.children[index].type === "element") count++; } return count; } function addProperty( props: Record, prop: string, value: unknown, ctx: Context, ) { const info = find(ctx.schema, prop); let result = value; // Ignore nullish and `NaN` values. if (result === null || result === undefined || result !== result) { return; } // Accept `array`. // Most props are space-separated. if (Array.isArray(result)) { result = info.commaSeparated ? commas(result) : spaces(result); } if (info.property === "style" && typeof result === "string") { result = parseStyle(result); } if (info.space && info.property) { props[ own.call(hastToReact, info.property) ? (hastToReact as any)[info.property] : info.property ] = result; } else if (info.attribute) { props[info.attribute] = result; } } function parseStyle(value: string): Record { const result: Record = {}; try { style(value, iterator); } catch { // Silent. } return result; function iterator(name: string, v: string) { const k = name.slice(0, 4) === "-ms-" ? `ms-${name.slice(4)}` : name; result[k.replace(/-([a-z])/g, styleReplacer)] = v; } } function styleReplacer(_: unknown, $1: string) { return $1.toUpperCase(); } function flattenPosition( pos: | Position | { start: { line: null; column: null; offset: null }; end: { line: null; column: null; offset: null }; }, ): string { return [ pos.start.line, ":", pos.start.column, "-", pos.end.line, ":", pos.end.column, ] .map((d) => String(d)) .join(""); }