| | 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; |
| | }); |
| | }; |
| | }; |
| |
|