Posted on 2024/02/10, 11:25 AM By admin22
前回に続きマークダウンの話題ですが、今回文法の拡張を実際にやって見ました。
参考)
https://zenn.dev/januswel/articles/745787422d425b01e0c1
https://github.com/januswel/unified-sample/blob/main/src/plugins/transformers/zenn-message.ts
上記コードで一応変換はできますが、複数の変換を一度にしたかったので、コードの理解も合わせてチャレンジして見ました。
zenn.ts
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 |
import unified from "unified"; import { Node, Parent } from "unist"; import parser from "remark-parse"; import toHast from "remark-rehype"; import compiler from "rehype-stringify"; import visit from "unist-util-visit"; import { VFileCompatible } from "vfile"; import { H } from "mdast-util-to-hast"; import { Paragraph } from "mdast"; import { isParent, isText, isParagraph } from "./util"; import all from "./all"; const MSG_START1 = ":::msg1\n"; const MSG_START2 = ":::msg2\n"; const MSG_END = "\n:::"; function isMessage(node: unknown): node is Paragraph { if (!isParagraph(node)) { return false; } const { children } = node; const firstChild = children[0]; if (!(isText(firstChild) && firstChild.value.startsWith(MSG_START1))) { return false; } const lastChild = children[children.length - 1]; if (!(isText(lastChild) && lastChild.value.endsWith(MSG_END))) { return false; } return true; } function isMessage2(node: unknown): node is Paragraph { if (!isParagraph(node)) { return false; } const { children } = node; const firstChild = children[0]; if (!(isText(firstChild) && firstChild.value.startsWith(MSG_START2))) { return false; } const lastChild = children[children.length - 1]; if (!(isText(lastChild) && lastChild.value.endsWith(MSG_END))) { return false; } return true; } function processFirstChild(children: Array<Node>, identifier: string) { const firstChild = children[0]; const firstValue = firstChild.value as string; if (firstValue === identifier) { children.shift(); } else { children[0] = { ...firstChild, value: firstValue.slice(identifier.length), }; } } function processLastChild(children: Array<Node>, identifier: string) { const lastIndex = children.length - 1; const lastChild = children[lastIndex]; const lastValue = lastChild.value as string; if (lastValue === identifier) { children.pop(); } else { children[lastIndex] = { ...lastChild, value: lastValue.slice(0, lastValue.length - identifier.length), }; } } function visitor(node: Paragraph, index: number, parent: Parent | undefined) { if (!isParent(parent)) { return; } const children = [...node.children]; processFirstChild(children, MSG_START1); processLastChild(children, MSG_END); parent.children[index] = { type: "message", attr: "v1", children, }; } function visitor2(node: Paragraph, index: number, parent: Parent | undefined) { if (!isParent(parent)) { return; } let children = [...node.children]; processFirstChild(children, MSG_START2); processLastChild(children, MSG_END); const ch = children[0] const aa = {...ch, attr:'attr'} parent.children[index] = { type: "message", attr: "v2", children, }; } export const attacher: unified.Plugin = () => { return (tree: Node, _file: VFileCompatible) => { visit(tree, isMessage, visitor); }; }; export const attacher2: unified.Plugin = () => { return (tree: Node, _file: VFileCompatible) => { visit(tree, isMessage2, visitor2); }; }; export function handler(h: H, node: Node) { if(node.attr == 'v1'){ return { type: "element", tagName: "div", properties: { className: ["text-xl"], }, children: all(h, node), }; } else{ return { type: "element", tagName: "div", properties: { className: ["m-10","p-4"], }, children: all(h, node), }; } } const processor = unified() .use(parser) .use(attacher) .use(attacher2) .use(toHast, { handlers: { message: handler, }, }) //.use(print) .use(compiler); const str = ` :::msg1 Hello! ::: abcdefg :::msg2 one two three. ::: ` const res = processor.processSync(str).toString() console.log(res) |
実行結果
1 2 3 4 |
$ npx ts-node zenn.ts <div class="text-xl">Hello!</div> <p>abcdefg</p> <div class="m-10 p-4">one two three.</div> |
:::msg1, :::msg2をそれぞれ、最近流行りの、tailwindcssに変換して見ました。
ちょっと強引なやり方になってしまいました。
もっと仕組みを理解すればスマートにできると思いますが、とりあえずこれはこれでいろいろ試せました。
Categories: 未分類 タグ: TypeScript