| | import type { Plugin } from 'unified';
|
| | import { visit } from 'unist-util-visit';
|
| | import type { Break, Content, Paragraph, PhrasingContent, Root, Text } from 'mdast';
|
| | import { LINE_BREAK, NBSP, PHRASE_PARENTS, TAB_AS_SPACES } from '$lib/constants/literal-html';
|
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| |
|
| |
|
| | function preserveIndent(line: string): string {
|
| | let index = 0;
|
| | let output = '';
|
| |
|
| | while (index < line.length) {
|
| | const char = line[index];
|
| |
|
| | if (char === ' ') {
|
| | output += NBSP;
|
| | index += 1;
|
| | continue;
|
| | }
|
| |
|
| | if (char === '\t') {
|
| | output += TAB_AS_SPACES;
|
| | index += 1;
|
| | continue;
|
| | }
|
| |
|
| | break;
|
| | }
|
| |
|
| | return output + line.slice(index);
|
| | }
|
| |
|
| | function createLiteralChildren(value: string): PhrasingContent[] {
|
| | const lines = value.split(LINE_BREAK);
|
| | const nodes: PhrasingContent[] = [];
|
| |
|
| | for (const [lineIndex, rawLine] of lines.entries()) {
|
| | if (lineIndex > 0) {
|
| | nodes.push({ type: 'break' } as Break as unknown as PhrasingContent);
|
| | }
|
| |
|
| | nodes.push({
|
| | type: 'text',
|
| | value: preserveIndent(rawLine)
|
| | } as Text as unknown as PhrasingContent);
|
| | }
|
| |
|
| | if (!nodes.length) {
|
| | nodes.push({ type: 'text', value: '' } as Text as unknown as PhrasingContent);
|
| | }
|
| |
|
| | return nodes;
|
| | }
|
| |
|
| | export const remarkLiteralHtml: Plugin<[], Root> = () => {
|
| | return (tree) => {
|
| | visit(tree, 'html', (node, index, parent) => {
|
| | if (!parent || typeof index !== 'number') {
|
| | return;
|
| | }
|
| |
|
| | const replacement = createLiteralChildren(node.value);
|
| |
|
| | if (!PHRASE_PARENTS.has(parent.type as string)) {
|
| | const paragraph: Paragraph = {
|
| | type: 'paragraph',
|
| | children: replacement as Paragraph['children'],
|
| | data: { literalHtml: true }
|
| | };
|
| |
|
| | const siblings = parent.children as unknown as Content[];
|
| | siblings.splice(index, 1, paragraph as unknown as Content);
|
| |
|
| | if (index > 0) {
|
| | const previous = siblings[index - 1] as Paragraph | undefined;
|
| |
|
| | if (
|
| | previous?.type === 'paragraph' &&
|
| | (previous.data as { literalHtml?: boolean } | undefined)?.literalHtml
|
| | ) {
|
| | const prevChildren = previous.children as unknown as PhrasingContent[];
|
| |
|
| | if (prevChildren.length) {
|
| | const lastChild = prevChildren[prevChildren.length - 1];
|
| |
|
| | if (lastChild.type !== 'break') {
|
| | prevChildren.push({
|
| | type: 'break'
|
| | } as Break as unknown as PhrasingContent);
|
| | }
|
| | }
|
| |
|
| | prevChildren.push(...(paragraph.children as unknown as PhrasingContent[]));
|
| |
|
| | siblings.splice(index, 1);
|
| |
|
| | return index;
|
| | }
|
| | }
|
| |
|
| | return index + 1;
|
| | }
|
| |
|
| | (parent.children as unknown as PhrasingContent[]).splice(
|
| | index,
|
| | 1,
|
| | ...(replacement as unknown as PhrasingContent[])
|
| | );
|
| |
|
| | return index + replacement.length;
|
| | });
|
| | };
|
| | };
|
| |
|