import mdast from "mdast" ; import unified from "unified" ; import unist from "unist" ; import { parents } from "unist-util-parents" ; import * as unistUtilSelect from "unist-util-select" ; import { QAPromptNode , qaPromptNodeType } from "./index" ; export default function qaPromptPlugin ( this ) { return extractQAPromptNodes ; } const questionPrefix = "Q. " ; const answerPrefix = "A. " ; const answerSplitRegexp = new RegExp ( `\n${ answerPrefix }` , "m" ) ; type NodeWithParent < N extends unist . Node = unist . Node > = unist . Node & { parent ? : NodeWithParent < unist . Parent > ; node : N ; } ; function extractQAPromptNodes ( node ) { const nodeWithParents = parents ( node ) ; const answerNodes = unistUtilSelect . selectAll ( `paragraph>text[value^='${ answerPrefix }']` , nodeWithParents ) as NodeWithParent [ ] ; for ( const answerNode of answerNodes ) { const parent = answerNode . parent ! . parent ! . node ; const answerParagraphIndex = parent . children . indexOf ( answerNode . parent ! . node ) ; if ( answerParagraphIndex === - 1 || answerParagraphIndex === 0 ) { throw new Error ( `Unexpected QA prompt answer node: ${ JSON . stringify ( answerNode , null , "\t" ) }` ) ; } const questionParagraphNode = parent . children [ answerParagraphIndex - 1 ] as mdast . Paragraph ; if ( questionParagraphNode . type === "paragraph" ) { const questionTextNode = questionParagraphNode . children [ 0 ] as mdast . Text ; if ( questionParagraphNode . children . length === 1 && questionTextNode . type === "text" ) { if ( questionTextNode . value . startsWith ( questionPrefix ) ) { const answerParagraphNode = parent . children [ answerParagraphIndex ] as mdast . Paragraph ; questionTextNode . value = questionTextNode . value . slice ( questionPrefix . length ) ; const answerTextNode = answerParagraphNode . children [ 0 ] as mdast . Text ; answerTextNode . value = answerTextNode . value . slice ( answerPrefix . length ) ; const qaPromptNode = { type : qaPromptNodeType , question : questionParagraphNode , answer : answerParagraphNode , } ; parent . children . splice ( answerParagraphIndex - 1 , 2 , qaPromptNode ) ; } } } } const questionNodes = unistUtilSelect . selectAll ( `paragraph>text[value^='${ questionPrefix }']` , nodeWithParents ) as NodeWithParent [ ] ; for ( const questionNode of questionNodes ) { const paragraphNode = questionNode . parent ! . node as mdast . Paragraph ; const splitNodeIndex = paragraphNode . children . findIndex ( ( node ) => node . type === "text" && answerSplitRegexp . test ( ( node as mdast . Text ) . value ) ) ; if ( splitNodeIndex === - 1 ) { continue ; } const splitNode = paragraphNode . children [ splitNodeIndex ] as mdast . Text ; const match = splitNode . value . match ( answerSplitRegexp ) ! ; const preSplitString = splitNode . value . slice ( 0 , match . index ! ) ; const postSplitString = splitNode . value . slice ( match . index ! ) ; const questionPhrasingNodes = paragraphNode . children . slice ( 0 , splitNodeIndex ) ; const answerPhrasingNodes = paragraphNode . children . slice ( splitNodeIndex ) ; if ( preSplitString !== "" && answerPhrasingNodes [ 0 ] . type === "text" ) { questionPhrasingNodes . push ( { type : "text" , value : preSplitString , } ) ; answerPhrasingNodes [ 0 ] . value = postSplitString ; } ( questionPhrasingNodes [ 0 ] as mdast . Text ) . value = ( questionPhrasingNodes [ 0 ] as mdast . Text ) . value . slice ( questionPrefix . length ) ; ( answerPhrasingNodes [ 0 ] as mdast . Text ) . value = ( answerPhrasingNodes [ 0 ] as mdast . Text ) . value . slice ( answerPrefix . length + 1 ) ; const qaPromptNode = { type : qaPromptNodeType , question : { type : "paragraph" , children : questionPhrasingNodes } , answer : { type : "paragraph" , children : answerPhrasingNodes } , } ; const paragraphContainer = questionNode . parent ! . parent ! . node as unist . Parent ; paragraphContainer . children . splice ( paragraphContainer . children . indexOf ( paragraphNode ) , 1 , qaPromptNode ) ; } return node ; }