forked from documentationjs/documentation
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathsort.js
138 lines (129 loc) · 4.09 KB
/
sort.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
import parseMarkdown from './remark-parse.js';
import chalk from 'chalk';
import path from 'path';
import fs from 'fs';
/**
* Sort two documentation objects, given an optional order object. Returns
* a numeric sorting value that is compatible with stream-sort.
*
* @param {Array<Object>} comments all comments
* @param {Object} options options from documentation.yml
* @returns {number} sorting value
* @private
*/
export default function (comments, options) {
if (!options || !options.toc) {
return sortComments(comments, options && options.sortOrder);
}
let i = 0;
const indexes = Object.create(null);
const toBeSorted = Object.create(null);
const paths = Object.create(null);
const fixed = [];
const walk = function (tocPath, val) {
if (typeof val === 'object' && val.name) {
val.kind = 'note';
indexes[val.name] = i++;
if (typeof val.file === 'string') {
let filename = val.file;
if (!path.isAbsolute(val.file)) {
filename = path.join(process.cwd(), val.file);
}
try {
val.description = fs.readFileSync(filename).toString();
delete val.file;
} catch (err) {
process.stderr.write(chalk.red(`Failed to read file ${filename}`));
}
} else if (!val.description) {
val.description = '';
}
if (typeof val.description === 'string') {
val.description = parseMarkdown(val.description);
}
const childPath = tocPath.concat({ scope: 'static', name: val.name });
val.path = childPath;
if (val.children) {
val.children.forEach(walk.bind(null, childPath));
}
fixed.push(val);
} else {
indexes[val] = i++;
toBeSorted[val] = false;
paths[val] = tocPath.concat({ scope: 'static', name: val, toc: true });
}
};
// Table of contents 'theme' entries: defined as objects
// in the YAML list
options.toc.forEach(walk.bind(null, []));
const unfixed = [];
comments.forEach(function (comment) {
let commentPath;
if (!comment.memberof && (commentPath = paths[comment.name])) {
comment.path = commentPath;
delete paths[comment.name];
}
// If comment is of kind 'note', this means that we must be _re_ sorting
// the list, and the TOC note entries were already added to the list. Bail
// out here so that we don't add duplicates.
if (comment.kind === 'note') {
return;
}
// If comment is top-level and `name` matches a TOC entry, add it to the
// to-be-sorted list.
if (!comment.memberof && indexes[comment.name] !== undefined) {
fixed.push(comment);
toBeSorted[comment.name] = true;
} else {
unfixed.push(comment);
}
});
fixed.sort((a, b) => {
if (indexes[a.name] !== undefined && indexes[b.name] !== undefined) {
return indexes[a.name] - indexes[b.name];
}
return 0;
});
sortComments(unfixed, options.sortOrder);
Object.keys(toBeSorted)
.filter(key => toBeSorted[key] === false)
.forEach(key => {
process.stderr.write(
chalk.red(
'Table of contents defined sorting of ' +
key +
' but no documentation with that namepath was found\n'
)
);
});
return fixed.concat(unfixed);
}
function compareCommentsByField(field, a, b) {
const akey = a[field];
const bkey = b[field];
if (akey && bkey) {
return akey.localeCompare(bkey, undefined, { caseFirst: 'upper' });
}
if (akey) return 1;
if (bkey) return -1;
return 0;
}
function compareCommentsBySourceLocation(a, b) {
return a.context.sortKey.localeCompare(b.context.sortKey);
}
const sortFns = {
alpha: compareCommentsByField.bind(null, 'name'),
source: compareCommentsBySourceLocation,
kind: compareCommentsByField.bind(null, 'kind'),
access: compareCommentsByField.bind(null, 'access'),
memberof: compareCommentsByField.bind(null, 'memberof')
};
function sortComments(comments, sortOrder) {
return comments.sort((a, b) => {
for (const sortMethod of sortOrder || ['source']) {
const r = sortFns[sortMethod](a, b);
if (r !== 0) return r;
}
return 0;
});
}