22const sortArray = require ( 'sort-array' )
33const transform = require ( './transform' )
44const a = require ( 'array-tools' )
5+ const t = require ( 'typical' )
56
67/**
78 *
@@ -12,16 +13,18 @@ const a = require('array-tools')
1213
1314exports . parse = parse
1415exports . getStats = getStats
16+ exports . groupBy = groupBy
1517
1618/**
1719 * @param {object[] } - jsdoc output
1820 * @param [options] {object}
1921 * @param [options.private] {boolean} - Include identifier documentation marked as `@private` in the output
2022 * @param [options.html] {boolean} - Enable experimental parsing of .html files.
2123 * @param [options.sort-by=[ 'scope', 'category', 'kind', 'order' ]] {string[]} - Sort by one of more fields, e.g. `--sort-by kind category`. Pass the special value `none` to remove the default sort order.
24+ * @param [options.group-by] {string[]}
2225 */
2326function parse ( jsdocExplainOutput , options ) {
24- options = options || { }
27+ options = new ParseOptions ( options )
2528 let data = transform ( jsdocExplainOutput )
2629
2730 data = data . filter ( function ( doclet ) {
@@ -41,6 +44,10 @@ function parse (jsdocExplainOutput, options) {
4144 data = sort ( data , [ 'scope' , 'category' , 'kind' , 'order' ] )
4245 }
4346
47+ if ( options [ 'group-by' ] ) {
48+ data = groupBy ( data , options [ 'group-by' ] )
49+ }
50+
4451 return data
4552}
4653
@@ -66,15 +73,102 @@ function getStats (jsdocExplainOutput) {
6673}
6774
6875function sort ( array , sortBy ) {
69- var order = {
70- kind : [ 'class' , 'constructor' , 'mixin' , 'member' , 'namespace' ,
76+ var customSortOrders = {
77+ kind : [ 'class' , 'constructor' , 'mixin' , 'member' , 'namespace' , 'enum' ,
7178 'constant' , 'function' , 'event' , 'typedef' , 'external' ] ,
7279 scope : [ 'global' , 'instance' , 'static' , 'inner' ]
7380 }
7481
7582 if ( ! sortBy ) {
7683 return array
7784 } else {
78- return sortArray ( array , sortBy , order )
85+ return sortArray ( array , sortBy , customSortOrders )
86+ }
87+ }
88+
89+ function _addGroup ( doclets , groupByFields ) {
90+ return doclets . map ( doclet => {
91+ doclet . _group = groupByFields
92+ . map ( field => t . isDefined ( doclet [ field ] ) ? doclet [ field ] : null )
93+ return doclet
94+ } )
95+ }
96+
97+ /**
98+ * takes the children of this, groups them, inserts group headings..
99+ */
100+ function groupBy ( doclets , groupByFields ) {
101+ var commonSequence = require ( 'common-sequence' )
102+
103+ /* don't modify the input array */
104+ groupByFields = groupByFields . slice ( 0 )
105+
106+ /* remove groupByFields that don't have any values */
107+ // console.error(groupByFields)
108+ groupByFields
109+ . forEach ( group => {
110+ const docletGroupValues = doclets
111+ . filter ( doclet => doclet . kind !== 'constructor' )
112+ . map ( d => d [ group ] )
113+ let groupValues = a . unique ( docletGroupValues )
114+ if ( groupValues . length <= 1 ) groupByFields = a . without ( groupByFields , group )
115+ } )
116+ // console.error(groupByFields)
117+ doclets = _addGroup ( doclets , groupByFields )
118+
119+ var inserts = [ ]
120+ var prevGroup = [ ]
121+ doclets . forEach ( ( doclet , index ) => {
122+ if ( ! deepEqual ( doclet . _group , prevGroup ) ) {
123+ var common = commonSequence ( doclet . _group , prevGroup )
124+ doclet . _group . forEach ( ( group , i ) => {
125+ if ( group !== common [ i ] && group !== null ) {
126+ inserts . push ( {
127+ index : index ,
128+ group : group
129+ } )
130+ }
131+ } )
132+ }
133+ prevGroup = doclet . _group
134+ delete doclet . _group
135+ } )
136+
137+ /* insert group records */
138+ inserts . reverse ( ) . forEach ( ( insert , i ) => {
139+ // console.error(i, insert)
140+ doclets . splice ( insert . index , 0 , { id : insert . group , kind : 'group' , parentId : null } )
141+ } )
142+
143+ /* update parentIds for group members */
144+ let currentGroup = null
145+ doclets . forEach ( ( d , index ) => {
146+ d . parentId = currentGroup
147+ if ( index === 0 ) {
148+ currentGroup = d . id
149+ } else {
150+ if ( d . kind === 'group' ) currentGroup = d . id
151+ }
152+ } )
153+
154+ return doclets
155+ }
156+
157+ function deepEqual ( a , b ) {
158+ return JSON . stringify ( a ) === JSON . stringify ( b )
159+ }
160+
161+ /**
162+ * Common jsdoc2md options
163+ */
164+ class ParseOptions {
165+ constructor ( options ) {
166+ options = options || { }
167+
168+ this [ 'group-by' ] = null
169+
170+ this [ 'sort-by' ] = [ 'scope' , 'category' , 'kind' , 'order' ]
171+
172+ Object . assign ( this , options )
79173 }
80174}
0 commit comments