120 lines
2.8 KiB
TypeScript
120 lines
2.8 KiB
TypeScript
import { ApiItem } from '@microsoft/api-extractor-model'
|
|
import {
|
|
DocCodeSpan,
|
|
DocEscapedText,
|
|
DocFencedCode,
|
|
DocNode,
|
|
DocParagraph,
|
|
DocPlainText,
|
|
DocSection,
|
|
DocSoftBreak,
|
|
} from '@microsoft/tsdoc'
|
|
import prettier from 'prettier'
|
|
|
|
export function getPath(item: { canonicalReference: ApiItem['canonicalReference'] }): string {
|
|
return item.canonicalReference
|
|
.toString()
|
|
.replace(/^@tldraw\/([^!]+)/, '$1/')
|
|
.replace(/[!:()#.]/g, '-')
|
|
.replace(/-+/g, '-')
|
|
.replace(/^-/, '')
|
|
.replace(/\/-/, '/')
|
|
.replace(/-$/, '')
|
|
}
|
|
|
|
export function getSlug(item: { canonicalReference: ApiItem['canonicalReference'] }): string {
|
|
return getPath(item).replace(/^[^/]+\//, '')
|
|
}
|
|
|
|
const prettierConfigPromise = prettier.resolveConfig(__dirname)
|
|
const languages: { [tag: string]: string | undefined } = {
|
|
ts: 'typescript',
|
|
tsx: 'typescript',
|
|
}
|
|
|
|
export async function formatWithPrettier(
|
|
code: string,
|
|
{
|
|
languageTag,
|
|
// roughly the width of our code blocks on a desktop
|
|
printWidth = 80,
|
|
}: { languageTag?: string; printWidth?: number } = {}
|
|
) {
|
|
const language = languages[languageTag || 'ts']
|
|
if (!language) {
|
|
throw new Error(`Unknown language: ${languageTag}`)
|
|
}
|
|
const prettierConfig = await prettierConfigPromise
|
|
const formattedCode = prettier.format(code, {
|
|
...prettierConfig,
|
|
parser: language,
|
|
printWidth,
|
|
tabWidth: 2,
|
|
useTabs: false,
|
|
})
|
|
|
|
return formattedCode.trimEnd()
|
|
}
|
|
|
|
export class MarkdownWriter {
|
|
static async docNodeToMarkdown(docNode: DocNode) {
|
|
const writer = new MarkdownWriter()
|
|
await writer.writeDocNode(docNode)
|
|
return writer.toString()
|
|
}
|
|
|
|
private result = ''
|
|
|
|
write(...parts: string[]): this {
|
|
this.result += parts.join('')
|
|
return this
|
|
}
|
|
|
|
endsWith(str: string) {
|
|
return this.result.endsWith(str)
|
|
}
|
|
|
|
writeIfNeeded(str: string): this {
|
|
if (!this.endsWith(str)) {
|
|
this.write(str)
|
|
}
|
|
return this
|
|
}
|
|
|
|
async writeDocNode(docNode: DocNode) {
|
|
if (docNode instanceof DocPlainText) {
|
|
this.write(docNode.text)
|
|
} else if (docNode instanceof DocSection || docNode instanceof DocParagraph) {
|
|
await this.writeDocNodes(docNode.nodes)
|
|
this.writeIfNeeded('\n\n')
|
|
} else if (docNode instanceof DocSoftBreak) {
|
|
this.writeIfNeeded('\n')
|
|
} else if (docNode instanceof DocCodeSpan) {
|
|
this.write('`', docNode.code, '`')
|
|
} else if (docNode instanceof DocFencedCode) {
|
|
this.writeIfNeeded('\n').write(
|
|
'```',
|
|
docNode.language,
|
|
'\n',
|
|
await formatWithPrettier(docNode.code, { languageTag: docNode.language }),
|
|
'\n',
|
|
'```\n'
|
|
)
|
|
} else if (docNode instanceof DocEscapedText) {
|
|
this.write(docNode.encodedText)
|
|
} else {
|
|
throw new Error(`Unknown docNode kind: ${docNode.kind}`)
|
|
}
|
|
}
|
|
|
|
async writeDocNodes(docNodes: readonly DocNode[]) {
|
|
for (const docNode of docNodes) {
|
|
await this.writeDocNode(docNode)
|
|
}
|
|
return this
|
|
}
|
|
|
|
toString() {
|
|
return this.result
|
|
}
|
|
}
|