package markdown import ( "bytes" "encoding/json" "strings" ) type jsonMark struct { Type string `json:"type"` Href string `json:"href,omitempty"` } type jsonText struct { Type string `json:"type"` Text string `json:"text"` Marks []jsonMark `json:"marks"` } type jsonHeading struct { Type string `json:"type"` Level int `json:"level"` Align string `json:"align,omitempty"` Children []jsonText `json:"children"` } type jsonParagraph struct { Type string `json:"type"` Align string `json:"align,omitempty"` Children []jsonText `json:"children"` } type jsonList struct { Type string `json:"type"` Ordered bool `json:"ordered"` Items [][]any `json:"items"` } type jsonBlockquote struct { Type string `json:"type"` Children []any `json:"children"` } type jsonCode struct { Type string `json:"type"` Language string `json:"language"` Text string `json:"text"` } type jsonRule struct { Type string `json:"type"` } type jsonImage struct { Type string `json:"type"` Src string `json:"src"` Alt string `json:"alt"` } func renderJSON(blocks []block) (string, error) { var buffer bytes.Buffer encoder := json.NewEncoder(&buffer) encoder.SetEscapeHTML(false) if err := encoder.Encode(toJSONBlocks(blocks)); err != nil { return "", err } return strings.TrimSuffix(buffer.String(), "\n"), nil } func toJSONBlocks(blocks []block) []any { result := make([]any, 0, len(blocks)) for _, b := range blocks { result = append(result, toJSONBlock(b)) } return result } func toJSONBlock(b block) any { switch b.kind { case kindHeading: return jsonHeading{Type: "heading", Level: b.level, Align: b.align, Children: toJSONTexts(b.inlines)} case kindParagraph: return jsonParagraph{Type: "paragraph", Align: b.align, Children: toJSONTexts(b.inlines)} case kindList: items := make([][]any, 0, len(b.items)) for _, item := range b.items { items = append(items, toJSONBlocks(item)) } return jsonList{Type: "list", Ordered: b.ordered, Items: items} case kindBlockquote: return jsonBlockquote{Type: "blockquote", Children: toJSONBlocks(b.children)} case kindCode: return jsonCode{Type: "code", Language: b.language, Text: b.text} case kindImage: return jsonImage{Type: "image", Src: b.src, Alt: b.alt} } return jsonRule{Type: "hr"} } func toJSONTexts(inlines []inline) []jsonText { plain := withoutCodeMarks(inlines) texts := make([]jsonText, 0, len(plain)) for _, item := range plain { marks := make([]jsonMark, 0, len(item.marks)) for _, m := range item.marks { marks = append(marks, jsonMark{Type: m.kind, Href: m.href}) } texts = append(texts, jsonText{Type: "text", Text: item.text, Marks: marks}) } return texts }