@@ -3,12 +3,16 @@ import updateSection from 'update-section';
33import * as md from '@textlint/markdown-to-ast' ;
44import { TxtNode } from '@textlint/ast-node-types' ;
55import { getHtmlHeaders } from './get-html-headers' ;
6+ import { replaceVariables } from './utils' ;
67import {
78 OPENING_COMMENT ,
89 CLOSING_COMMENT ,
910 CHECK_OPENING_COMMENT ,
1011 CHECK_CLOSING_COMMENT ,
1112 DEFAULT_TITLE ,
13+ DEFAULT_HTML_TEMPLATE ,
14+ DEFAULT_ITEM_TEMPLATE ,
15+ DEFAULT_SEPARATOR ,
1216} from '..' ;
1317import { TransformOptions , Header , HeaderWithRepetition , HeaderWithAnchor , SectionInfo , TransformResult } from '../types' ;
1418
@@ -22,10 +26,11 @@ const getTargetComments = (checkComments: Array<string>, defaultComments: string
2226
2327export const matchesStart = ( checkOpeningComments ?: Array < string > ) => ( line : string ) : boolean => getTargetComments ( checkOpeningComments ?? [ ] , CHECK_OPENING_COMMENT ) . some ( comment => new RegExp ( comment ) . test ( line ) ) ;
2428export const matchesEnd = ( checkClosingComments ?: Array < string > ) => ( line : string ) : boolean => getTargetComments ( checkClosingComments ?? [ ] , CHECK_CLOSING_COMMENT ) . some ( comment => new RegExp ( comment ) . test ( line ) ) ;
25- const addAnchor = ( mode : string | undefined , moduleName : string | undefined , header : HeaderWithRepetition ) : HeaderWithAnchor => {
29+ const addAnchor = ( mode : string , moduleName : string | undefined , header : HeaderWithRepetition ) : HeaderWithAnchor => {
2630 return {
2731 ...header ,
2832 anchor : anchor ( header . name , mode , header . repetition , moduleName ) ,
33+ hash : getUrlHash ( header . name , mode , header . repetition , moduleName ) ,
2934 } ;
3035} ;
3136
@@ -115,6 +120,24 @@ const determineTitle = (title: string | undefined, isNotitle: boolean | undefine
115120 return wrapTitle ( getTitle ( title , lines , info ) , isFolding ) ;
116121} ;
117122
123+ const getHeaderContents = ( headers : Array < HeaderWithAnchor > , indentation : string , lowestRank : number , entryPrefix : string ) : string => headers . map ( header => getHeaderItem ( header , indentation , lowestRank , entryPrefix ) ) . join ( '\n' ) ;
124+
125+ const getHeaderItem = ( header : HeaderWithAnchor , indentation : string , lowestRank : number , entryPrefix : string ) : string => {
126+ return `${ indentation . repeat ( header . rank - lowestRank ) } ${ entryPrefix } ${ header . anchor } ` ;
127+ } ;
128+
129+ const getHtmlHeaderContents = ( headers : Array < HeaderWithAnchor > , htmlTemplate : string | undefined , itemTemplate : string | undefined , separator : string | undefined ) : string => replaceVariables ( htmlTemplate ?? DEFAULT_HTML_TEMPLATE , [ {
130+ key : 'ITEMS' ,
131+ replace : `\n${ headers . map ( header => getHeaderItemHtml ( header , itemTemplate ) ) . join ( `\n${ separator ?? DEFAULT_SEPARATOR } \n` ) } \n` ,
132+ } ] ) ;
133+
134+ const getHeaderItemHtml = ( header : HeaderWithAnchor , itemTemplate : string | undefined ) : string => {
135+ return replaceVariables ( itemTemplate ?? DEFAULT_ITEM_TEMPLATE , [
136+ { key : 'LINK' , replace : `#${ header . hash } ` } ,
137+ { key : 'TEXT' , replace : header . name } ,
138+ ] ) ;
139+ } ;
140+
118141export const transform = (
119142 content : string ,
120143 {
@@ -131,14 +154,18 @@ export const transform = (
131154 closingComment,
132155 checkOpeningComments,
133156 checkClosingComments,
157+ isHtml,
158+ htmlTemplate,
159+ itemTemplate,
160+ separator,
134161 } : TransformOptions = { } ,
135162) : TransformResult => {
136- mode = mode || 'github.com' ;
137- entryPrefix = entryPrefix || '-' ;
163+ const _mode = mode || 'github.com' ;
164+ const _entryPrefix = entryPrefix || '-' ;
138165
139166 // only limit *HTML* headings by default
140167 // eslint-disable-next-line no-magic-numbers
141- const maxHeaderLevelHtml = maxHeaderLevel || 4 ;
168+ const maxHeaderLevelHtml = isHtml ? 1 : ( maxHeaderLevel || 4 ) ;
142169 const lines = content . split ( '\n' ) ;
143170 const info : SectionInfo = updateSection . parse ( lines , matchesStart ( checkOpeningComments ) , matchesEnd ( checkClosingComments ) ) ;
144171
@@ -158,19 +185,19 @@ export const transform = (
158185 const headers = getMarkdownHeaders ( linesToToc , maxHeaderLevel ) . concat ( getHtmlHeaders ( linesToToc , maxHeaderLevelHtml ) ) ;
159186 headers . sort ( ( header1 , header2 ) => header1 . line - header2 . line ) ;
160187
161- const allHeaders = countHeaders ( headers , mode , moduleName ) ;
188+ const allHeaders = countHeaders ( headers , _mode , moduleName ) ;
162189 const lowestRank = Math . min ( ...allHeaders . map ( header => header . rank ) ) ;
163- const linkedHeaders = allHeaders . map ( header => addAnchor ( mode , moduleName , header ) ) ;
190+ const linkedHeaders = allHeaders . map ( header => addAnchor ( _mode , moduleName , header ) ) ;
164191
165192 const inferredTitle = linkedHeaders . length ? determineTitle ( title , isNotitle , isFolding , lines , info ) : '' ;
166193 const titleSeparator = inferredTitle ? '\n\n' : '\n' ;
167194
168195 // 4 spaces required for proper indention on Bitbucket and GitLab
169- const indentation = ( mode === 'bitbucket.org' || mode === 'gitlab.com' ) ? ' ' : ' ' ;
196+ const indentation = ( _mode === 'bitbucket.org' || _mode === 'gitlab.com' ) ? ' ' : ' ' ;
170197 const toc =
171198 inferredTitle +
172199 titleSeparator +
173- linkedHeaders . map ( header => indentation . repeat ( header . rank - lowestRank ) + entryPrefix + ' ' + header . anchor ) . join ( '\n' ) +
200+ ( isHtml ? getHtmlHeaderContents ( linkedHeaders , htmlTemplate , itemTemplate , separator ) : getHeaderContents ( linkedHeaders , indentation , lowestRank , _entryPrefix ) ) +
174201 '\n' ;
175202 const wrappedToc = ( openingComment ?? OPENING_COMMENT ) + '\n' + wrapToc ( toc , inferredTitle , isFolding ) + '\n' + ( closingComment ?? CLOSING_COMMENT ) ;
176203 if ( currentToc === wrappedToc ) {
0 commit comments