import cheerio from "cheerio" ; import { decode } from "html-entities" ; import { listNarrativeFiles , LitvisDocument , LitvisNarrative } from "litvis" ; import _ from "lodash" ; import { EntityDefinitionWithOrigin } from "narrative-schema-common" ; import { Position } from "unist" ; import { VFile } from "vfile" ; const findOrFilter = ( $ , func , selector ) : Cheerio => { const cheerioSelectorParts = [ ] ; if ( selector ) { cheerioSelectorParts . push ( `[ns-role="label"]` ) ; cheerioSelectorParts . push ( `[ns-label-name="${ selector . label }"]` ) ; if ( selector . kind ) { cheerioSelectorParts . push ( `[ns-label-kind="${ selector . kind }"]` ) ; } } let $result = func ( cheerioSelectorParts . join ( "" ) ) ; if ( selector . trimmedContent ) { $result = $result . filter ( ( i , element ) => $ ( element ) . text ( ) . trim ( ) === selector . trimmedContent ) ; } return $result ; } ; const find = ( $ , $where , selector ) : Cheerio => { return findOrFilter ( $ , $where . find . bind ( $where ) , selector ) ; } ; const filter = ( $ , $what , selector ) : Cheerio => { return findOrFilter ( $ , $what . filter . bind ( $what ) , selector ) ; } ; const ruleIsNotFollowed = ( position ? ) : Error => { const result = new Error ( "rule is not followed" ) ; if ( position ) { ( result as any ) . position = position ; } return result ; } ; const elementPosition = ( $el ) : Position | undefined => { const startColumn = parseInt ( $el . attr ( "ns-position-start-column" ) || "0" , 10 ) ; const startLine = parseInt ( $el . attr ( "ns-position-start-line" ) || "0" , 10 ) ; const endColumn = parseInt ( $el . attr ( "ns-position-end-column" ) || "0" , 10 ) ; const endLine = parseInt ( $el . attr ( "ns-position-end-line" ) || "0" , 10 ) ; if ( Number . isFinite ( startColumn ) && Number . isFinite ( startLine ) && Number . isFinite ( endColumn ) && Number . isFinite ( endLine ) ) { return { start : { column : startColumn , line : startLine , } , end : { column : endColumn , line : endLine , } , } ; } return undefined ; } ; export const postEnhanceWithLitvis = ( processedNarrative , html , updateLintingReport ) => { const postEnhancedHtml = html . replace ( /<p>(<litvis-narrative-schema-label>.*<\/litvis-narrative-schema-label>)<\/p>/g , "$1" ) . replace ( /<litvis-narrative-schema-label>(.*)<\/litvis-narrative-schema-label>/g , ( outer , inner ) => decode ( inner ) ) ; const lastFile = _ . last ( processedNarrative . documents ) as LitvisDocument ; if ( processedNarrative . composedNarrativeSchema ) { const $ = cheerio . load ( postEnhancedHtml , { xmlMode : true } ) ; const $root = $ . root ( ) ; processedNarrative . composedNarrativeSchema . rules . forEach ( ( ruleDefinition ) => { const ruleData = ruleDefinition . data ; try { const $selection = find ( $ , $root , ruleData . selector ) ; if ( Number . isFinite ( ruleData . minimumOccurrences ) && $selection . length < ruleData . minimumOccurrences ) { throw ruleIsNotFollowed ( ) ; } if ( Number . isFinite ( ruleData . maximumOccurrences ) && $selection . length > ruleData . maximumOccurrences ) { throw ruleIsNotFollowed ( ) ; } $selection . each ( ( i , el ) => { const $el = $ ( el ) ; if ( ruleData . children && Number . isFinite ( ruleData . children . minimumTrimmedTextLength ) ) { const trimmedText = $el . text ( ) . trim ( ) ; if ( trimmedText . length < ruleData . children . minimumTrimmedTextLength ) { throw ruleIsNotFollowed ( elementPosition ( $el ) ) ; } } if ( ruleData . followedBy && ruleData . followedBy . selector && filter ( $ , $el . next ( ) , ruleData . followedBy . selector ) . length === 0 ) { throw ruleIsNotFollowed ( elementPosition ( $el ) ) ; } if ( ruleData . notFollowedBy && ruleData . notFollowedBy . selector && filter ( $ , $el . next ( ) , ruleData . notFollowedBy . selector ) . length !== 0 ) { throw ruleIsNotFollowed ( elementPosition ( $el ) ) ; } if ( ruleData . before && ruleData . before . selector && filter ( $ , $el . nextAll ( ) , ruleData . before . selector ) . length === 0 ) { throw ruleIsNotFollowed ( elementPosition ( $el ) ) ; } if ( ruleData . after && ruleData . after . selector && filter ( $ , $el . prevAll ( ) , ruleData . after . selector ) . length !== 0 ) { throw ruleIsNotFollowed ( elementPosition ( $el ) ) ; } } ) ; } catch ( e ) { if ( e . message === "rule is not followed" ) { lastFile . message ( ruleData . description , e . position , "narrative-schema:rule-validation" ) ; } else { console . error ( e ) ; } } } ) ; } if ( updateLintingReport ) { updateLintingReport ( listNarrativeFiles ( processedNarrative ) ) ; } return postEnhancedHtml ; } ;