Skip to content

Commit 4855d93

Browse files
committed
first stab at a docs generator
1 parent 2b2fd6a commit 4855d93

File tree

7 files changed

+247
-70
lines changed

7 files changed

+247
-70
lines changed

README.md

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -46,12 +46,10 @@ Each param is associaated with the last function or method.
4646
Each comment is associated with the last keyword.
4747

4848
# CLI
49-
Hyper-docs cli will output an AST. It will also accept an AST as input
50-
and compile to either html (`--html`) or markdown (`--md`) pending on the
51-
specified flag.
49+
Hyper-docs cli will output an AST. No args produces an HTML document.
5250

5351
```bash
54-
hyper-docs /path/to/projects | ./bin/docs --html
52+
hyper-docs /path/to/projects | ./bin/docs > path/to/api.html
5553
```
5654

5755
# PARSER

compile.js

Lines changed: 176 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -1,71 +1,212 @@
1+
//
2+
// Produces a single page (searchable) document with a TOC.
3+
//
4+
// Yeah, its a lot of messy string templating. If you have
5+
// a better idea, feel free to make a pull request :)
6+
//
7+
const fs = require('fs')
18
const marked = require('marked')
2-
const hl = require('highlight.js')
39

10+
marked.setOptions({
11+
renderer: new marked.Renderer()
12+
})
13+
14+
const escapeString = s => {
15+
s = s.replace('&', '&')
16+
.replace('>', '>')
17+
.replace('<', '&lt;')
18+
.replace('\'', '&#x27;')
19+
.replace('"', '&quot;')
20+
.replace('`', '&#x60;')
21+
return s
22+
}
23+
24+
//
25+
// Return Types
26+
//
27+
const templateReturnType = type => `
28+
|Return Type|Description|
29+
|:---|:---|
30+
|${type.return.TYPE}|${type.return.comment}|
31+
`
32+
33+
//
34+
// Properties
35+
//
36+
const templateProperties = members => {
37+
const props = members.filter(m => m.type === 'property')
38+
39+
if (!props.length) {
40+
return '' // there are no operators
41+
}
42+
43+
let header = [
44+
`### PROPERTIES\n`,
45+
`|Type|Name|Description|`,
46+
`|:---|:---|:----------|`
47+
]
48+
49+
const rows = props.map(prop =>
50+
`|${prop.TYPE}|${prop.name}|${prop.comment || ''}|`
51+
)
52+
53+
return [...header, ...rows].join('\n')
54+
}
55+
56+
//
57+
// Operators
58+
//
459
const templateOperators = members => {
560
const operators = members.filter(m => m.type === 'operator')
661

7-
let table = `|Operator|Description|\n`
8-
table += `|:-------|:----------|\n`
62+
if (!operators.length) {
63+
return '' // there are no operators
64+
}
965

10-
table += operators.map(operator => {
11-
return `|${operator.name}|${operator.comment}|`
12-
}).join('\n')
66+
let header = [
67+
`### OPERATORS\n`,
68+
`|Operator|Description|`,
69+
`|:-------|:----------|`
70+
]
71+
72+
const rows = operators.map(operator =>
73+
`|${operator.name}|${operator.comment || ''}|`
74+
)
75+
76+
return [...header, ...rows].join('\n')
77+
}
78+
79+
//
80+
// Params
81+
//
82+
const templateParams = type => {
83+
if (!type.params || !Object.keys(type.params).length) {
84+
return ''
85+
}
86+
87+
return [
88+
``,
89+
`|Parameter|Description|`,
90+
`|:---|:---|`,
91+
...Object.keys(type.params).map(key => {
92+
const param = type.params[key]
93+
return `|${key}|${param.comment}|`
94+
})
95+
].join('\n')
96+
}
97+
98+
//
99+
// Methods
100+
//
101+
const templateMethods = members => {
102+
const methods = members.filter(m => m.type === 'method')
103+
104+
if (!methods.length) {
105+
return '' // there are no operators
106+
}
107+
108+
const header = [`### METHODS\n`]
109+
110+
const rows = methods.map(method => {
111+
return [
112+
`#### ${method.name}`,
113+
`${method.comment}`,
114+
`\`\`\``,
115+
method.raw,
116+
`\`\`\``,
117+
templateReturnType(method),
118+
templateParams(method),
119+
``
120+
].join('\n')
121+
})
13122

14-
return table
123+
return [...header, ...rows].join('\n')
15124
}
16125

17-
const templateClass = (key, type) => `
18-
### \`${key}\`
126+
const templateClassOrStruct = (key, type) => `
127+
## ${escapeString(key)}
19128
${type.comment}
20129
21130
${templateOperators(type.members)}
131+
132+
${templateMethods(type.members)}
133+
134+
${templateProperties(type.members)}
22135
`
23136

24137
const templateFunction = (key, type) => `
25-
### \`${key}\`
138+
## ${escapeString(key)}
26139
${type.comment}
140+
141+
\`\`\`
142+
${type.raw}
143+
\`\`\`
144+
145+
${templateReturnType(type)}
146+
147+
${templateParams(type)}
27148
`
28149

29-
const template = ast => `
30-
# [${ast.namespace}](${ast.repo})
150+
const createMD = ast => {
151+
if (!ast.namespace) return ''
152+
153+
return `
154+
# ${ast.namespace.join('::')}
31155
32-
## TYPES
33156
${
34157
Object.keys(ast.types).map(key => {
35158
const type = ast.types[key]
36159
37160
return type.members
38-
? templateClass(key, type)
161+
? templateClassOrStruct(key, type)
39162
: templateFunction(key, type)
40163
}).join('\n')
41164
}
42165
`
166+
}
43167

44-
const createHTML = s => {
45-
marked.setOptions({
46-
renderer: new marked.Renderer(),
47-
highlight: code => hl.highlightAuto(code).value
48-
})
168+
module.exports = buffers => {
169+
const toc = {}
170+
const groups = {}
49171

50-
return marked(s)
51-
}
172+
buffers.forEach(ast => {
173+
if (!ast.namespace) return
52174

53-
const createMD = ast => {
54-
if (!ast.namespace) {
55-
return ''
56-
} else {
57-
ast.namespace = ast.namespace.join('::')
58-
}
175+
const ns = ast.namespace.join('::')
59176

60-
return template(ast)
61-
}
177+
const output = marked(createMD(ast))
62178

63-
module.exports = (buffers, type) => {
64-
buffers.forEach(ast => {
65-
const output = type === 'html'
66-
? createHTML(createMD(ast))
67-
: createMD(ast)
179+
if (ast.types) {
180+
toc[ns] = toc[ns] || []
181+
toc[ns].push(...Object.keys(ast.types))
182+
}
183+
184+
groups[ns] = groups[ns] || []
185+
groups[ns].push(output)
186+
})
68187

69-
process.stdout.write(output)
188+
const flattened = []
189+
190+
Object.keys(toc).forEach(key => {
191+
const k = escapeString(key)
192+
flattened.push(`<a href="${k}">${k}</a><ul>`)
193+
194+
flattened.push(...toc[key].map(type =>
195+
`<li>${escapeString(type)}</li>`))
196+
197+
flattened.push(`</ul>`)
70198
})
199+
200+
const output = `
201+
<div class="toc">
202+
${flattened.join('\n')}
203+
</div>
204+
205+
<div class="docs">
206+
${Object.values(groups).map(buf => buf.join('')).join('\n')}
207+
</div>
208+
`
209+
210+
const template = fs.readFileSync(`${__dirname}/template.txt`, 'utf8')
211+
process.stdout.write(template.replace('DOCS', output))
71212
}

index.js

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ const parseUrl = s => {
6565
// Read all files from a path and parse their headers for docs
6666
//
6767
function main (argv) {
68-
if (argv[0] === '--md' || argv[0] === '--html') {
68+
if (!argv.length) {
6969
let buffers = fs
7070
.readFileSync(0, 'utf8')
7171
.split(os.EOL)
@@ -78,8 +78,7 @@ function main (argv) {
7878
process.exit(1)
7979
}
8080

81-
const type = argv[0].slice(2)
82-
compile(buffers, type)
81+
compile(buffers)
8382
return
8483
}
8584

package-lock.json

Lines changed: 0 additions & 5 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,6 @@
1818
},
1919
"homepage": "https://github.com/datcxx/hyper-docs#readme",
2020
"dependencies": {
21-
"highlight.js": "^9.15.6",
2221
"marked": "^0.6.2"
2322
}
2423
}

0 commit comments

Comments
 (0)