-
Notifications
You must be signed in to change notification settings - Fork 2
/
typedoc-markdown.mjs
139 lines (124 loc) · 2.9 KB
/
typedoc-markdown.mjs
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
139
// @ts-check
import fs from 'node:fs/promises'
import path from 'node:path'
import { Application, TSConfigReader, PageEvent } from 'typedoc'
const __dirname = path.dirname(new URL(import.meta.url).pathname)
const DEFAULT_OPTIONS = {
// disableOutputCheck: true,
cleanOutputDir: true,
excludeInternal: true,
readme: 'none',
out: path.resolve(__dirname, './api'),
entryDocument: 'index.md',
hideBreadcrumbs: false,
hideInPageTOC: true,
preserveAnchorCasing: true,
}
/**
*
* @param {Partial<import('typedoc').TypeDocOptions>} config
*/
export async function createTypeDocApp(config = {}) {
const options = {
...DEFAULT_OPTIONS,
...config,
}
const app = await Application.bootstrapWithPlugins(options)
// If you want TypeDoc to load tsconfig.json / typedoc.json files
app.options.addReader(new TSConfigReader())
app.renderer.on(
PageEvent.END,
/**
*
* @param {import('typedoc').PageEvent} page
*/
(page) => {
if (!page.contents) {
return
}
page.contents = prependYAML(page.contents, {
// TODO: figure out a way to point to the source files?
editLink: false,
})
}
)
async function serve() {
app.convertAndWatch(handleProject)
}
async function build() {
if (
(await exists(options.out)) &&
(await fs.stat(options.out)).isDirectory()
) {
await fs.rm(options.out, { recursive: true })
}
const project = await app.convert()
return handleProject(project)
}
/**
*
* @param {import('typedoc').ProjectReflection | undefined} project
*/
async function handleProject(project) {
if (project) {
// Rendered docs
try {
await app.generateDocs(project, options.out)
app.logger.info(`generated at ${options.out}.`)
} catch (error) {
app.logger.error(error)
}
} else {
app.logger.error('No project')
}
}
return {
build,
serve,
}
}
async function exists(path) {
try {
await fs.access(path)
return true
} catch {
return false
}
}
/**
* @typedef {Record<string, string | number | boolean>} FrontMatterVars
*/
/**
* Prepends YAML block to a string
* @param {string} contents - string to prepend to
* @param {FrontMatterVars} vars - object of required front matter variables
*/
function prependYAML(contents, vars) {
return contents
.replace(/^/, toYAML(vars) + '\n\n')
.replace(/[\r\n]{3,}/g, '\n\n')
}
/**
* Converts YAML object to a YAML string
* @param {FrontMatterVars} vars
*/
function toYAML(vars) {
const yaml = `---
${Object.entries(vars)
.map(
([key, value]) =>
`${key}: ${
typeof value === 'string' ? `"${escapeDoubleQuotes(value)}"` : value
}`
)
.join('\n')}
---`
return yaml
}
/**
* Escapes double quotes in a string
* @param {string} str - string to escape
*/
function escapeDoubleQuotes(str) {
return str.replace(/"/g, '\\"')
}