[fix] remove docs scripts (#1651)
This PR removes the docs generation scripts. ### Change Type - [x] `documentation`
This commit is contained in:
parent
2403577da0
commit
d3ce35c916
8 changed files with 0 additions and 1291 deletions
|
@ -1,130 +0,0 @@
|
||||||
// HEY THIS IS COPIED FROM THE WWW FODLER
|
|
||||||
|
|
||||||
export type InputCategory = {
|
|
||||||
id: string
|
|
||||||
title: string
|
|
||||||
description: string
|
|
||||||
groups: Group[]
|
|
||||||
}
|
|
||||||
|
|
||||||
export type InputSection = {
|
|
||||||
id: string
|
|
||||||
title: string
|
|
||||||
description: string
|
|
||||||
categories: InputCategory[]
|
|
||||||
}
|
|
||||||
|
|
||||||
export enum Status {
|
|
||||||
Draft = 'draft',
|
|
||||||
Published = 'published',
|
|
||||||
}
|
|
||||||
/** A tablekeyed by slug of generated markdown content for each item */
|
|
||||||
export type MarkdownContent = Record<string, string>
|
|
||||||
|
|
||||||
/** A table keyed by slug of articles. */
|
|
||||||
export type Articles = Record<string, Article>
|
|
||||||
|
|
||||||
export interface Section {
|
|
||||||
/** The section's id */
|
|
||||||
id: string
|
|
||||||
/** The section's title */
|
|
||||||
title: string
|
|
||||||
/** A desscription of the section. */
|
|
||||||
description: string
|
|
||||||
/** A table keyed by category of each category. */
|
|
||||||
categories: Category[]
|
|
||||||
}
|
|
||||||
|
|
||||||
export type Category = {
|
|
||||||
/** The category's id */
|
|
||||||
id: string
|
|
||||||
/** The category's title */
|
|
||||||
title: string
|
|
||||||
/** A desscription of the category. */
|
|
||||||
description: string
|
|
||||||
/** An ordered array of articleIds that belong to this category. */
|
|
||||||
articleIds: string[]
|
|
||||||
groups: Group[]
|
|
||||||
}
|
|
||||||
|
|
||||||
export type Group = {
|
|
||||||
id: string
|
|
||||||
title: string
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface Author {
|
|
||||||
name: string
|
|
||||||
image: string
|
|
||||||
email: string
|
|
||||||
twitter: string
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface Article {
|
|
||||||
/** The unique id or "slug" for this article. */
|
|
||||||
id: string
|
|
||||||
/** The id of the group to which this article belongs. */
|
|
||||||
groupId: string
|
|
||||||
/** The index of this article inside of the article's group. */
|
|
||||||
groupIndex: number
|
|
||||||
/** The id of the category to which this article belongs. */
|
|
||||||
categoryId: string
|
|
||||||
/** The index of this article inside of the article's category. */
|
|
||||||
categoryIndex: number
|
|
||||||
/** The id of the section to which this article belongs. */
|
|
||||||
sectionId: string
|
|
||||||
/** The index of this article inside of the article's section. */
|
|
||||||
sectionIndex: number
|
|
||||||
/** The article's display title. */
|
|
||||||
title: string
|
|
||||||
/** The article's display description (optional). */
|
|
||||||
description: string | null
|
|
||||||
/** The article's author details (optional). */
|
|
||||||
author: Author | null
|
|
||||||
/** The article's hero image (optional). */
|
|
||||||
hero: string | null
|
|
||||||
/** The article's status (draft, published, hidden, etc) */
|
|
||||||
status: Status
|
|
||||||
/** The date on which the article was published (optional). */
|
|
||||||
date: string | null
|
|
||||||
/** An array of keywords associated with this article. */
|
|
||||||
keywords: string[]
|
|
||||||
/** The URL where the article's source can be found. */
|
|
||||||
sourceUrl: string
|
|
||||||
/** The articleId of the next article in the category. */
|
|
||||||
next: string | null
|
|
||||||
/** The articleId of the previous article in the category. */
|
|
||||||
prev: string | null
|
|
||||||
}
|
|
||||||
|
|
||||||
export type ArticleLinks = {
|
|
||||||
prev: Article | null
|
|
||||||
next: Article | null
|
|
||||||
}
|
|
||||||
|
|
||||||
export type SidebarContentSectionLink = {
|
|
||||||
type: 'section'
|
|
||||||
title: string
|
|
||||||
url: string
|
|
||||||
children: SidebarContentLink[]
|
|
||||||
}
|
|
||||||
export type SidebarContentCategoryLink = {
|
|
||||||
type: 'category'
|
|
||||||
title: string
|
|
||||||
url: string
|
|
||||||
children: SidebarContentLink[]
|
|
||||||
}
|
|
||||||
export type SidebarContentArticleLink = { type: 'article'; title: string; url: string }
|
|
||||||
|
|
||||||
export type SidebarContentLink =
|
|
||||||
| SidebarContentSectionLink
|
|
||||||
| SidebarContentCategoryLink
|
|
||||||
| SidebarContentArticleLink
|
|
||||||
|
|
||||||
export type SidebarContentList = {
|
|
||||||
sectionId: string | null
|
|
||||||
categoryId: string | null
|
|
||||||
articleId: string | null
|
|
||||||
links: SidebarContentLink[]
|
|
||||||
}
|
|
||||||
|
|
||||||
export type GeneratedContent = { sections: Section[]; content: MarkdownContent; articles: Articles }
|
|
|
@ -1,237 +0,0 @@
|
||||||
import { ApiItem, ApiItemKind, ApiModel } from '@microsoft/api-extractor-model'
|
|
||||||
import {
|
|
||||||
DocCodeSpan,
|
|
||||||
DocEscapedText,
|
|
||||||
DocFencedCode,
|
|
||||||
DocLinkTag,
|
|
||||||
DocNode,
|
|
||||||
DocParagraph,
|
|
||||||
DocPlainText,
|
|
||||||
DocSection,
|
|
||||||
DocSoftBreak,
|
|
||||||
} from '@microsoft/tsdoc'
|
|
||||||
import { assert, assertExists, exhaustiveSwitchError } from '@tldraw/utils'
|
|
||||||
import prettier from 'prettier'
|
|
||||||
|
|
||||||
function isOnParentPage(itemKind: ApiItemKind) {
|
|
||||||
switch (itemKind) {
|
|
||||||
case ApiItemKind.CallSignature:
|
|
||||||
case ApiItemKind.Class:
|
|
||||||
case ApiItemKind.EntryPoint:
|
|
||||||
case ApiItemKind.Enum:
|
|
||||||
case ApiItemKind.Function:
|
|
||||||
case ApiItemKind.Interface:
|
|
||||||
case ApiItemKind.Model:
|
|
||||||
case ApiItemKind.Namespace:
|
|
||||||
case ApiItemKind.Package:
|
|
||||||
case ApiItemKind.TypeAlias:
|
|
||||||
case ApiItemKind.Variable:
|
|
||||||
case ApiItemKind.None:
|
|
||||||
return false
|
|
||||||
case ApiItemKind.Constructor:
|
|
||||||
case ApiItemKind.ConstructSignature:
|
|
||||||
case ApiItemKind.EnumMember:
|
|
||||||
case ApiItemKind.Method:
|
|
||||||
case ApiItemKind.MethodSignature:
|
|
||||||
case ApiItemKind.Property:
|
|
||||||
case ApiItemKind.PropertySignature:
|
|
||||||
case ApiItemKind.IndexSignature:
|
|
||||||
return true
|
|
||||||
default:
|
|
||||||
exhaustiveSwitchError(itemKind)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function sanitizeReference(reference: string) {
|
|
||||||
return reference
|
|
||||||
.replace(/[!:()#.[\]]/g, '-')
|
|
||||||
.replace(/-+/g, '-')
|
|
||||||
.replace(/^-/, '')
|
|
||||||
.replace(/\/-/, '/')
|
|
||||||
.replace(/-$/, '')
|
|
||||||
}
|
|
||||||
|
|
||||||
export function getSlug(item: ApiItem): string {
|
|
||||||
return sanitizeReference(item.canonicalReference.toString().replace(/^@tldraw\/[^!]+!/, ''))
|
|
||||||
}
|
|
||||||
|
|
||||||
export function getPath(item: ApiItem): string {
|
|
||||||
if (isOnParentPage(item.kind)) {
|
|
||||||
const parentPath = getPath(assertExists(item.parent))
|
|
||||||
const childSlug = getSlug(item)
|
|
||||||
return `${parentPath}#${childSlug}`
|
|
||||||
}
|
|
||||||
return sanitizeReference(item.canonicalReference.toString().replace(/^@tldraw\/([^!]+)/, '$1/'))
|
|
||||||
}
|
|
||||||
|
|
||||||
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(apiContext: ApiItem, docNode: DocNode) {
|
|
||||||
const writer = new MarkdownWriter(apiContext)
|
|
||||||
await writer.writeDocNode(docNode)
|
|
||||||
return writer.toString()
|
|
||||||
}
|
|
||||||
|
|
||||||
private constructor(private readonly apiContext: ApiItem) {}
|
|
||||||
|
|
||||||
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 if (docNode instanceof DocLinkTag) {
|
|
||||||
if (docNode.urlDestination) {
|
|
||||||
this.write(
|
|
||||||
'[',
|
|
||||||
docNode.linkText ?? docNode.urlDestination,
|
|
||||||
'](',
|
|
||||||
docNode.urlDestination,
|
|
||||||
')'
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
assert(docNode.codeDestination)
|
|
||||||
const apiModel = getTopLevelModel(this.apiContext)
|
|
||||||
const refResult = apiModel.resolveDeclarationReference(
|
|
||||||
docNode.codeDestination,
|
|
||||||
this.apiContext
|
|
||||||
)
|
|
||||||
|
|
||||||
if (refResult.errorMessage) {
|
|
||||||
throw new Error(refResult.errorMessage)
|
|
||||||
}
|
|
||||||
const linkedItem = assertExists(refResult.resolvedApiItem)
|
|
||||||
const path = getPath(linkedItem)
|
|
||||||
|
|
||||||
this.write(
|
|
||||||
'[',
|
|
||||||
docNode.linkText ?? getDefaultReferenceText(linkedItem),
|
|
||||||
'](/gen/',
|
|
||||||
path,
|
|
||||||
')'
|
|
||||||
)
|
|
||||||
}
|
|
||||||
} 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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function getDefaultReferenceText(item: ApiItem): string {
|
|
||||||
function parentPrefix(str: string, sep = '.'): string {
|
|
||||||
if (!item.parent) return str
|
|
||||||
return `${getDefaultReferenceText(item.parent)}${sep}${str}`
|
|
||||||
}
|
|
||||||
switch (item.kind) {
|
|
||||||
case ApiItemKind.CallSignature:
|
|
||||||
return parentPrefix(`${item.displayName}()`)
|
|
||||||
case ApiItemKind.Constructor:
|
|
||||||
case ApiItemKind.ConstructSignature: {
|
|
||||||
const parent = assertExists(item.parent)
|
|
||||||
return `new ${getDefaultReferenceText(parent)}()`
|
|
||||||
}
|
|
||||||
case ApiItemKind.EnumMember:
|
|
||||||
case ApiItemKind.Method:
|
|
||||||
case ApiItemKind.MethodSignature:
|
|
||||||
case ApiItemKind.Property:
|
|
||||||
case ApiItemKind.PropertySignature:
|
|
||||||
return parentPrefix(item.displayName)
|
|
||||||
case ApiItemKind.IndexSignature:
|
|
||||||
return parentPrefix(`[${item.displayName}]`, '')
|
|
||||||
case ApiItemKind.Class:
|
|
||||||
case ApiItemKind.EntryPoint:
|
|
||||||
case ApiItemKind.Enum:
|
|
||||||
case ApiItemKind.Function:
|
|
||||||
case ApiItemKind.Interface:
|
|
||||||
case ApiItemKind.Model:
|
|
||||||
case ApiItemKind.Namespace:
|
|
||||||
case ApiItemKind.Package:
|
|
||||||
case ApiItemKind.TypeAlias:
|
|
||||||
case ApiItemKind.Variable:
|
|
||||||
case ApiItemKind.None:
|
|
||||||
return item.displayName
|
|
||||||
default:
|
|
||||||
exhaustiveSwitchError(item.kind)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function getTopLevelModel(item: ApiItem): ApiModel {
|
|
||||||
const model = assertExists(item.getAssociatedModel())
|
|
||||||
if (model.parent) {
|
|
||||||
return getTopLevelModel(model.parent)
|
|
||||||
}
|
|
||||||
return model
|
|
||||||
}
|
|
|
@ -1,10 +0,0 @@
|
||||||
// import { buildDocs } from './build-docs'
|
|
||||||
import { generateContent } from './generateContent'
|
|
||||||
|
|
||||||
async function main() {
|
|
||||||
const { log: nicelog } = console
|
|
||||||
nicelog('Creating content for www.')
|
|
||||||
await generateContent()
|
|
||||||
}
|
|
||||||
|
|
||||||
main()
|
|
|
@ -1,128 +0,0 @@
|
||||||
import { ApiModel } from '@microsoft/api-extractor-model'
|
|
||||||
import fs from 'fs'
|
|
||||||
import path from 'path'
|
|
||||||
import { Articles, GeneratedContent, InputSection, MarkdownContent } from './docs-types'
|
|
||||||
import { getSlug } from './docs-utils'
|
|
||||||
import { generateSection } from './generateSection'
|
|
||||||
import { getApiMarkdown } from './getApiMarkdown'
|
|
||||||
|
|
||||||
const { log: nicelog } = console
|
|
||||||
|
|
||||||
async function generateApiDocs() {
|
|
||||||
const apiInputSection: InputSection = {
|
|
||||||
id: 'gen' as string,
|
|
||||||
title: 'API',
|
|
||||||
description: "Reference for the tldraw package's APIs (generated).",
|
|
||||||
categories: [],
|
|
||||||
}
|
|
||||||
|
|
||||||
const addedCategories = new Set<string>()
|
|
||||||
|
|
||||||
const OUTPUT_DIR = path.join(process.cwd(), '..', '..', 'bublic', 'docs', 'gen')
|
|
||||||
const INPUT_DIR = path.join(process.cwd(), '..', '..', 'bublic', 'packages')
|
|
||||||
|
|
||||||
if (fs.existsSync(OUTPUT_DIR)) {
|
|
||||||
fs.rmdirSync(OUTPUT_DIR, { recursive: true })
|
|
||||||
}
|
|
||||||
|
|
||||||
fs.mkdirSync(OUTPUT_DIR)
|
|
||||||
|
|
||||||
// to include more packages in docs, add them to devDependencies in package.json
|
|
||||||
const packageJson = JSON.parse(fs.readFileSync(path.join(process.cwd(), 'package.json'), 'utf8'))
|
|
||||||
const tldrawPackagesToIncludeInDocs = Object.keys(packageJson.devDependencies).filter((dep) =>
|
|
||||||
dep.startsWith('@tldraw/')
|
|
||||||
)
|
|
||||||
const model = new ApiModel()
|
|
||||||
const packageModels = []
|
|
||||||
|
|
||||||
for (const packageName of tldrawPackagesToIncludeInDocs) {
|
|
||||||
// Get the file contents
|
|
||||||
const filePath = path.join(INPUT_DIR, packageName.replace('@tldraw/', ''), 'api', 'api.json')
|
|
||||||
|
|
||||||
packageModels.push(model.loadPackage(filePath))
|
|
||||||
}
|
|
||||||
|
|
||||||
for (const packageModel of packageModels) {
|
|
||||||
try {
|
|
||||||
const categoryName = packageModel.name.replace(`@tldraw/`, '')
|
|
||||||
|
|
||||||
if (!addedCategories.has(categoryName)) {
|
|
||||||
apiInputSection.categories!.push({
|
|
||||||
id: categoryName,
|
|
||||||
title: packageModel.name,
|
|
||||||
description: '',
|
|
||||||
groups: [
|
|
||||||
{
|
|
||||||
id: 'Namespace',
|
|
||||||
title: 'Namespaces',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 'Class',
|
|
||||||
title: 'Classes',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 'Function',
|
|
||||||
title: 'Functions',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 'Variable',
|
|
||||||
title: 'Variables',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 'Enum',
|
|
||||||
title: 'Enums',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 'Interface',
|
|
||||||
title: 'Interfaces',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 'TypeAlias',
|
|
||||||
title: 'TypeAliases',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
})
|
|
||||||
addedCategories.add(categoryName)
|
|
||||||
}
|
|
||||||
|
|
||||||
const entrypoint = packageModel.entryPoints[0]
|
|
||||||
|
|
||||||
for (let j = 0; j < entrypoint.members.length; j++) {
|
|
||||||
const item = entrypoint.members[j]
|
|
||||||
const result = await getApiMarkdown(categoryName, item, j)
|
|
||||||
const outputFileName = `${getSlug(item)}.mdx`
|
|
||||||
fs.writeFileSync(path.join(OUTPUT_DIR, outputFileName), result.markdown)
|
|
||||||
}
|
|
||||||
} catch (e: any) {
|
|
||||||
throw Error(`Could not create API docs for ${packageModel.name}: ${e.message}`)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return apiInputSection
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function generateApiContent(): Promise<GeneratedContent> {
|
|
||||||
const content: MarkdownContent = {}
|
|
||||||
const articles: Articles = {}
|
|
||||||
|
|
||||||
try {
|
|
||||||
nicelog('• Generating api docs site content (content.json)')
|
|
||||||
|
|
||||||
const inputApiSection = await generateApiDocs()
|
|
||||||
const outputApiSection = generateSection(inputApiSection, content, articles)
|
|
||||||
const contentComplete = { sections: [outputApiSection], content, articles }
|
|
||||||
|
|
||||||
fs.writeFileSync(
|
|
||||||
path.join(process.cwd(), 'api-content.json'),
|
|
||||||
JSON.stringify(contentComplete, null, 2)
|
|
||||||
)
|
|
||||||
|
|
||||||
nicelog('✔ Generated api content.')
|
|
||||||
|
|
||||||
return contentComplete
|
|
||||||
} catch (error: any) {
|
|
||||||
nicelog(`x Could not generate site content: ${error.message}`)
|
|
||||||
|
|
||||||
throw error
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,229 +0,0 @@
|
||||||
import fs from 'fs'
|
|
||||||
import matter from 'gray-matter'
|
|
||||||
import path from 'path'
|
|
||||||
import authors from '../../../docs/authors.json'
|
|
||||||
import sections from '../../../docs/sections.json'
|
|
||||||
import {
|
|
||||||
Article,
|
|
||||||
Articles,
|
|
||||||
Category,
|
|
||||||
GeneratedContent,
|
|
||||||
Group,
|
|
||||||
MarkdownContent,
|
|
||||||
Section,
|
|
||||||
Status,
|
|
||||||
} from './docs-types'
|
|
||||||
|
|
||||||
const { log: nicelog } = console
|
|
||||||
|
|
||||||
type InputCategory = {
|
|
||||||
id: string
|
|
||||||
title: string
|
|
||||||
description: string
|
|
||||||
groups: Group[]
|
|
||||||
}
|
|
||||||
|
|
||||||
type InputSection = {
|
|
||||||
id: string
|
|
||||||
title: string
|
|
||||||
description: string
|
|
||||||
categories: InputCategory[]
|
|
||||||
}
|
|
||||||
|
|
||||||
function generateSection(
|
|
||||||
section: InputSection,
|
|
||||||
content: MarkdownContent,
|
|
||||||
articles: Articles
|
|
||||||
): Section {
|
|
||||||
// A temporary table of categories
|
|
||||||
const _categories: Record<string, Category> = {}
|
|
||||||
|
|
||||||
// Uncategorized articles
|
|
||||||
const _ucg: Article[] = []
|
|
||||||
|
|
||||||
// A temporary table of articles mapped to categories
|
|
||||||
const _categoryArticles: Record<string, Article[]> = Object.fromEntries(
|
|
||||||
section.categories.map((category) => [category.id, []])
|
|
||||||
)
|
|
||||||
|
|
||||||
// The file directory for this section
|
|
||||||
const dir = path.join(process.cwd(), '..', '..', 'bublic', 'docs', section.id)
|
|
||||||
|
|
||||||
fs.readdirSync(dir, { withFileTypes: false }).forEach((result: string | Buffer) => {
|
|
||||||
try {
|
|
||||||
const filename = result.toString()
|
|
||||||
|
|
||||||
const fileContent = fs.readFileSync(path.join(dir, filename)).toString()
|
|
||||||
|
|
||||||
const extension = path.extname(filename)
|
|
||||||
|
|
||||||
const articleId = filename.replace(extension, '')
|
|
||||||
|
|
||||||
const parsed = matter({ content: fileContent }, {})
|
|
||||||
|
|
||||||
if (process.env.NODE_ENV !== 'development' && parsed.data.status !== 'published') {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// If a category was provided but that category was not found in the section, throw an error
|
|
||||||
const category =
|
|
||||||
parsed.data.category && section.categories.find((c) => c.id === parsed.data.category)
|
|
||||||
if (parsed.data.category && !category) {
|
|
||||||
throw Error(
|
|
||||||
`Could not find a category for section ${section.id} with id ${parsed.data.category}.`
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (parsed.data.author && !authors[parsed.data.author as keyof typeof authors]) {
|
|
||||||
throw Error(`Could not find an author with id ${parsed.data.author}.`)
|
|
||||||
}
|
|
||||||
|
|
||||||
// By default, the category is ucg (uncategorized)
|
|
||||||
const { category: categoryId = 'ucg' } = parsed.data
|
|
||||||
|
|
||||||
const article: Article = {
|
|
||||||
id: articleId,
|
|
||||||
sectionIndex: 0,
|
|
||||||
groupIndex: -1,
|
|
||||||
groupId: parsed.data.group ?? null,
|
|
||||||
categoryIndex: parsed.data.order ?? -1,
|
|
||||||
sectionId: section.id,
|
|
||||||
categoryId: parsed.data.category ?? 'ucg',
|
|
||||||
status: parsed.data.status ?? Status.Draft,
|
|
||||||
title: parsed.data.title ?? 'Article',
|
|
||||||
description: parsed.data.description ?? 'An article for the docs site.',
|
|
||||||
hero: parsed.data.hero ?? null,
|
|
||||||
date: parsed.data.date ? new Date(parsed.data.date).toISOString() : null,
|
|
||||||
keywords: parsed.data.keywords ?? [],
|
|
||||||
next: null,
|
|
||||||
prev: null,
|
|
||||||
author: parsed.data.author
|
|
||||||
? authors[parsed.data.author as keyof typeof authors] ?? null
|
|
||||||
: null,
|
|
||||||
sourceUrl: `https://github.com/tldraw/tldraw/tree/main/apps/docs/content/${section.id}/${articleId}${extension}`,
|
|
||||||
}
|
|
||||||
|
|
||||||
if (article.id === section.id) {
|
|
||||||
article.categoryIndex = -1
|
|
||||||
article.sectionIndex = -1
|
|
||||||
articles[section.id + '_index'] = article
|
|
||||||
content[section.id + '_index'] = parsed.content
|
|
||||||
} else {
|
|
||||||
if (category) {
|
|
||||||
if (article.id === category.id) {
|
|
||||||
article.categoryIndex = -1
|
|
||||||
article.sectionIndex = -1
|
|
||||||
articles[category.id + '_index'] = article
|
|
||||||
content[category.id + '_index'] = parsed.content
|
|
||||||
} else {
|
|
||||||
_categoryArticles[categoryId].push(article)
|
|
||||||
content[articleId] = parsed.content
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
_ucg.push(article)
|
|
||||||
content[articleId] = parsed.content
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
console.error(e)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
const sortArticles = (articleA: Article, articleB: Article) => {
|
|
||||||
const { categoryIndex: categoryIndexA, date: dateA = '01/01/1970' } = articleA
|
|
||||||
const { categoryIndex: categoryIndexB, date: dateB = '01/01/1970' } = articleB
|
|
||||||
|
|
||||||
return categoryIndexA === categoryIndexB
|
|
||||||
? new Date(dateB!).getTime() > new Date(dateA!).getTime()
|
|
||||||
? 1
|
|
||||||
: -1
|
|
||||||
: categoryIndexA < categoryIndexB
|
|
||||||
? -1
|
|
||||||
: 1
|
|
||||||
}
|
|
||||||
|
|
||||||
let sectionIndex = 0
|
|
||||||
|
|
||||||
// Sort ucg articles by date and add them to the articles table
|
|
||||||
_ucg.sort(sortArticles).forEach((article, i) => {
|
|
||||||
article.categoryIndex = i
|
|
||||||
article.sectionIndex = sectionIndex++
|
|
||||||
article.prev = _ucg[i - 1]?.id ?? null
|
|
||||||
article.next = _ucg[i + 1]?.id ?? null
|
|
||||||
articles[article.id] = article
|
|
||||||
})
|
|
||||||
|
|
||||||
// Sort categorized articles by date and add them to the articles table
|
|
||||||
section.categories.forEach((category) => {
|
|
||||||
const categoryArticles = _categoryArticles[category.id]
|
|
||||||
|
|
||||||
categoryArticles.sort(sortArticles).forEach((article, i) => {
|
|
||||||
article.categoryIndex = i
|
|
||||||
article.sectionIndex = sectionIndex++
|
|
||||||
article.prev = categoryArticles[i - 1]?.id ?? null
|
|
||||||
article.next = categoryArticles[i + 1]?.id ?? null
|
|
||||||
articles[article.id] = article
|
|
||||||
})
|
|
||||||
|
|
||||||
_categories[category.id] = {
|
|
||||||
...category,
|
|
||||||
articleIds: categoryArticles.map((article) => article.id),
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
return {
|
|
||||||
...section,
|
|
||||||
categories: [
|
|
||||||
{
|
|
||||||
id: 'ucg',
|
|
||||||
title: 'Uncategorized',
|
|
||||||
description: 'Articles that do not belong to a category.',
|
|
||||||
groups: [],
|
|
||||||
articleIds: _ucg
|
|
||||||
.sort((a, b) => a.sectionIndex - b.sectionIndex)
|
|
||||||
.map((article) => article.id),
|
|
||||||
},
|
|
||||||
...section.categories.map(({ id }) => _categories[id]).filter((c) => c.articleIds.length > 0),
|
|
||||||
],
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function generateContent(): Promise<GeneratedContent> {
|
|
||||||
const content: MarkdownContent = {}
|
|
||||||
const articles: Articles = {}
|
|
||||||
|
|
||||||
nicelog('• Generating site content (content.json)')
|
|
||||||
|
|
||||||
try {
|
|
||||||
const outputSections: Section[] = [...(sections as InputSection[])]
|
|
||||||
.map((section) => generateSection(section, content, articles))
|
|
||||||
.filter((section) => section.categories.some((c) => c.articleIds.length > 0))
|
|
||||||
|
|
||||||
nicelog('✔ Generated site content.')
|
|
||||||
|
|
||||||
// Write to disk
|
|
||||||
|
|
||||||
const generatedApiContent = (await import(
|
|
||||||
path.join(process.cwd(), 'api-content.json')
|
|
||||||
)) as GeneratedContent
|
|
||||||
|
|
||||||
const contentComplete: GeneratedContent = {
|
|
||||||
sections: generatedApiContent
|
|
||||||
? [...outputSections, ...generatedApiContent.sections]
|
|
||||||
: outputSections,
|
|
||||||
content: generatedApiContent ? { ...content, ...generatedApiContent.content } : content,
|
|
||||||
articles: generatedApiContent ? { ...articles, ...generatedApiContent.articles } : articles,
|
|
||||||
}
|
|
||||||
|
|
||||||
fs.writeFileSync(
|
|
||||||
path.join(process.cwd(), 'content.json'),
|
|
||||||
JSON.stringify(contentComplete, null, 2)
|
|
||||||
)
|
|
||||||
|
|
||||||
return contentComplete
|
|
||||||
} catch (error) {
|
|
||||||
nicelog(`x Could not generate site content.`)
|
|
||||||
|
|
||||||
throw error
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,171 +0,0 @@
|
||||||
import fs from 'fs'
|
|
||||||
import matter from 'gray-matter'
|
|
||||||
import path from 'path'
|
|
||||||
import authors from '../../../docs/authors.json'
|
|
||||||
import {
|
|
||||||
Article,
|
|
||||||
Articles,
|
|
||||||
Category,
|
|
||||||
InputSection,
|
|
||||||
MarkdownContent,
|
|
||||||
Section,
|
|
||||||
Status,
|
|
||||||
} from './docs-types'
|
|
||||||
|
|
||||||
export function generateSection(
|
|
||||||
section: InputSection,
|
|
||||||
content: MarkdownContent,
|
|
||||||
articles: Articles
|
|
||||||
): Section {
|
|
||||||
// A temporary table of categories
|
|
||||||
const _categories: Record<string, Category> = {}
|
|
||||||
|
|
||||||
// Uncategorized articles
|
|
||||||
const _ucg: Article[] = []
|
|
||||||
|
|
||||||
// A temporary table of articles mapped to categories
|
|
||||||
const _categoryArticles: Record<string, Article[]> = Object.fromEntries(
|
|
||||||
section.categories.map((category) => [category.id, []])
|
|
||||||
)
|
|
||||||
|
|
||||||
// The file directory for this section
|
|
||||||
const dir = path.join(process.cwd(), '..', '..', 'bublic', 'docs', section.id)
|
|
||||||
|
|
||||||
fs.readdirSync(dir, { withFileTypes: false }).forEach((result: string | Buffer) => {
|
|
||||||
try {
|
|
||||||
const filename = result.toString()
|
|
||||||
|
|
||||||
const fileContent = fs.readFileSync(path.join(dir, filename)).toString()
|
|
||||||
|
|
||||||
const extension = path.extname(filename)
|
|
||||||
|
|
||||||
const articleId = filename.replace(extension, '')
|
|
||||||
|
|
||||||
const parsed = matter({ content: fileContent }, {})
|
|
||||||
|
|
||||||
if (process.env.NODE_ENV !== 'development' && parsed.data.status !== 'published') {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// If a category was provided but that category was not found in the section, throw an error
|
|
||||||
const category =
|
|
||||||
parsed.data.category && section.categories.find((c) => c.id === parsed.data.category)
|
|
||||||
if (parsed.data.category && !category) {
|
|
||||||
throw Error(
|
|
||||||
`Could not find a category for section ${section.id} with id ${parsed.data.category}.`
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (parsed.data.author && !authors[parsed.data.author as keyof typeof authors]) {
|
|
||||||
throw Error(`Could not find an author with id ${parsed.data.author}.`)
|
|
||||||
}
|
|
||||||
|
|
||||||
// By default, the category is ucg (uncategorized)
|
|
||||||
const { category: categoryId = 'ucg' } = parsed.data
|
|
||||||
|
|
||||||
const article: Article = {
|
|
||||||
id: articleId,
|
|
||||||
sectionIndex: 0,
|
|
||||||
groupIndex: -1,
|
|
||||||
groupId: parsed.data.group ?? null,
|
|
||||||
categoryIndex: parsed.data.order ?? -1,
|
|
||||||
sectionId: section.id,
|
|
||||||
categoryId: parsed.data.category ?? 'ucg',
|
|
||||||
status: parsed.data.status ?? Status.Draft,
|
|
||||||
title: parsed.data.title ?? 'Article',
|
|
||||||
description: parsed.data.description ?? 'An article for the docs site.',
|
|
||||||
hero: parsed.data.hero ?? null,
|
|
||||||
date: parsed.data.date ? new Date(parsed.data.date).toISOString() : null,
|
|
||||||
keywords: parsed.data.keywords ?? [],
|
|
||||||
next: null,
|
|
||||||
prev: null,
|
|
||||||
author: parsed.data.author
|
|
||||||
? authors[parsed.data.author as keyof typeof authors] ?? null
|
|
||||||
: null,
|
|
||||||
sourceUrl: `https://github.com/tldraw/tldraw/tree/main/apps/docs/content/${section.id}/${articleId}${extension}`,
|
|
||||||
}
|
|
||||||
|
|
||||||
if (article.id === section.id) {
|
|
||||||
article.categoryIndex = -1
|
|
||||||
article.sectionIndex = -1
|
|
||||||
articles[section.id + '_index'] = article
|
|
||||||
content[section.id + '_index'] = parsed.content
|
|
||||||
} else {
|
|
||||||
if (category) {
|
|
||||||
if (article.id === category.id) {
|
|
||||||
article.categoryIndex = -1
|
|
||||||
article.sectionIndex = -1
|
|
||||||
articles[category.id + '_index'] = article
|
|
||||||
content[category.id + '_index'] = parsed.content
|
|
||||||
} else {
|
|
||||||
_categoryArticles[categoryId].push(article)
|
|
||||||
content[articleId] = parsed.content
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
_ucg.push(article)
|
|
||||||
content[articleId] = parsed.content
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
console.error(e)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
const sortArticles = (articleA: Article, articleB: Article) => {
|
|
||||||
const { categoryIndex: categoryIndexA, date: dateA = '01/01/1970' } = articleA
|
|
||||||
const { categoryIndex: categoryIndexB, date: dateB = '01/01/1970' } = articleB
|
|
||||||
|
|
||||||
return categoryIndexA === categoryIndexB
|
|
||||||
? new Date(dateB!).getTime() > new Date(dateA!).getTime()
|
|
||||||
? 1
|
|
||||||
: -1
|
|
||||||
: categoryIndexA < categoryIndexB
|
|
||||||
? -1
|
|
||||||
: 1
|
|
||||||
}
|
|
||||||
|
|
||||||
let sectionIndex = 0
|
|
||||||
|
|
||||||
// Sort ucg articles by date and add them to the articles table
|
|
||||||
_ucg.sort(sortArticles).forEach((article, i) => {
|
|
||||||
article.categoryIndex = i
|
|
||||||
article.sectionIndex = sectionIndex++
|
|
||||||
article.prev = _ucg[i - 1]?.id ?? null
|
|
||||||
article.next = _ucg[i + 1]?.id ?? null
|
|
||||||
articles[article.id] = article
|
|
||||||
})
|
|
||||||
|
|
||||||
// Sort categorized articles by date and add them to the articles table
|
|
||||||
section.categories.forEach((category) => {
|
|
||||||
const categoryArticles = _categoryArticles[category.id]
|
|
||||||
|
|
||||||
categoryArticles.sort(sortArticles).forEach((article, i) => {
|
|
||||||
article.categoryIndex = i
|
|
||||||
article.sectionIndex = sectionIndex++
|
|
||||||
article.prev = categoryArticles[i - 1]?.id ?? null
|
|
||||||
article.next = categoryArticles[i + 1]?.id ?? null
|
|
||||||
articles[article.id] = article
|
|
||||||
})
|
|
||||||
|
|
||||||
_categories[category.id] = {
|
|
||||||
...category,
|
|
||||||
articleIds: categoryArticles.map((article) => article.id),
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
return {
|
|
||||||
...section,
|
|
||||||
categories: [
|
|
||||||
{
|
|
||||||
id: 'ucg',
|
|
||||||
title: 'Uncategorized',
|
|
||||||
description: 'Articles that do not belong to a category.',
|
|
||||||
groups: [],
|
|
||||||
articleIds: _ucg
|
|
||||||
.sort((a, b) => a.sectionIndex - b.sectionIndex)
|
|
||||||
.map((article) => article.id),
|
|
||||||
},
|
|
||||||
...section.categories.map(({ id }) => _categories[id]).filter((c) => c.articleIds.length > 0),
|
|
||||||
],
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,373 +0,0 @@
|
||||||
import {
|
|
||||||
ApiClass,
|
|
||||||
ApiConstructSignature,
|
|
||||||
ApiConstructor,
|
|
||||||
ApiDeclaredItem,
|
|
||||||
ApiDocumentedItem,
|
|
||||||
ApiEnum,
|
|
||||||
ApiFunction,
|
|
||||||
ApiInterface,
|
|
||||||
ApiItem,
|
|
||||||
ApiItemKind,
|
|
||||||
ApiMethod,
|
|
||||||
ApiMethodSignature,
|
|
||||||
ApiNamespace,
|
|
||||||
ApiProperty,
|
|
||||||
ApiPropertySignature,
|
|
||||||
ApiReadonlyMixin,
|
|
||||||
ApiReleaseTagMixin,
|
|
||||||
ApiStaticMixin,
|
|
||||||
ApiTypeAlias,
|
|
||||||
ApiVariable,
|
|
||||||
Excerpt,
|
|
||||||
ReleaseTag,
|
|
||||||
} from '@microsoft/api-extractor-model'
|
|
||||||
import { assert, assertExists } from '@tldraw/utils'
|
|
||||||
import { MarkdownWriter, formatWithPrettier, getPath, getSlug } from './docs-utils'
|
|
||||||
|
|
||||||
type Result = { markdown: string }
|
|
||||||
|
|
||||||
const date = new Intl.DateTimeFormat('en-US', {
|
|
||||||
year: 'numeric',
|
|
||||||
month: '2-digit',
|
|
||||||
day: '2-digit',
|
|
||||||
}).format(new Date())
|
|
||||||
|
|
||||||
export async function getApiMarkdown(categoryName: string, item: ApiItem, j: number) {
|
|
||||||
const result = { markdown: '' }
|
|
||||||
|
|
||||||
addFrontmatter(result, item, categoryName, j)
|
|
||||||
addTags(result, item)
|
|
||||||
|
|
||||||
const toc: Result = { markdown: '' }
|
|
||||||
const membersResult: Result = { markdown: '' }
|
|
||||||
|
|
||||||
if (item.members) {
|
|
||||||
const constructors = []
|
|
||||||
const properties = []
|
|
||||||
const methods = []
|
|
||||||
for (const member of item.members) {
|
|
||||||
switch (member.kind) {
|
|
||||||
case ApiItemKind.Constructor:
|
|
||||||
case ApiItemKind.ConstructSignature:
|
|
||||||
constructors.push(member)
|
|
||||||
break
|
|
||||||
case ApiItemKind.Variable:
|
|
||||||
case ApiItemKind.Property:
|
|
||||||
case ApiItemKind.PropertySignature:
|
|
||||||
properties.push(member)
|
|
||||||
break
|
|
||||||
case ApiItemKind.Method:
|
|
||||||
case ApiItemKind.Function:
|
|
||||||
case ApiItemKind.MethodSignature:
|
|
||||||
methods.push(member)
|
|
||||||
break
|
|
||||||
case ApiItemKind.EnumMember:
|
|
||||||
case ApiItemKind.Class:
|
|
||||||
case ApiItemKind.TypeAlias:
|
|
||||||
case ApiItemKind.Interface:
|
|
||||||
// TODO: document these
|
|
||||||
break
|
|
||||||
default:
|
|
||||||
throw new Error(`Unknown member kind: ${member.kind} ${member.displayName}`)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
const constructorResult = { markdown: '' }
|
|
||||||
const propertiesResult = { markdown: '' }
|
|
||||||
const methodsResult = { markdown: '' }
|
|
||||||
|
|
||||||
if (constructors.length) {
|
|
||||||
for (const member of constructors) {
|
|
||||||
await addMarkdownForMember(constructorResult, member)
|
|
||||||
addHorizontalRule(constructorResult)
|
|
||||||
}
|
|
||||||
addMarkdown(membersResult, constructorResult.markdown)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (properties.length) {
|
|
||||||
addMarkdown(toc, `- [Properties](#properties)\n`)
|
|
||||||
addMarkdown(propertiesResult, `## Properties\n\n`)
|
|
||||||
for (const member of properties) {
|
|
||||||
addMarkdown(toc, ` - [${member.displayName}](#${getSlug(member)})\n`)
|
|
||||||
await addMarkdownForMember(propertiesResult, member)
|
|
||||||
addHorizontalRule(propertiesResult)
|
|
||||||
}
|
|
||||||
addMarkdown(membersResult, propertiesResult.markdown)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (methods.length) {
|
|
||||||
addMarkdown(toc, `- [Methods](#methods)\n`)
|
|
||||||
addMarkdown(methodsResult, `## Methods\n\n`)
|
|
||||||
for (const member of methods) {
|
|
||||||
addMarkdown(toc, ` - [${member.displayName}](#${getSlug(member)})\n`)
|
|
||||||
await addMarkdownForMember(methodsResult, member)
|
|
||||||
addHorizontalRule(methodsResult)
|
|
||||||
}
|
|
||||||
addMarkdown(membersResult, methodsResult.markdown)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (toc.markdown.length) {
|
|
||||||
result.markdown += `<details>\n\t<summary>Table of Contents</summary>\n`
|
|
||||||
addMarkdown(result, toc.markdown)
|
|
||||||
result.markdown += `</details>\n\n`
|
|
||||||
}
|
|
||||||
|
|
||||||
await addDocComment(result, item)
|
|
||||||
addReferences(result, item)
|
|
||||||
|
|
||||||
if (membersResult.markdown.length) {
|
|
||||||
addHorizontalRule(result)
|
|
||||||
addMarkdown(result, membersResult.markdown)
|
|
||||||
}
|
|
||||||
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
/* --------------------- Helpers -------------------- */
|
|
||||||
|
|
||||||
function addMarkdown(result: Result, markdown: string) {
|
|
||||||
result.markdown += markdown
|
|
||||||
}
|
|
||||||
|
|
||||||
async function addMarkdownForMember(result: Result, member: ApiItem) {
|
|
||||||
addMemberName(result, member)
|
|
||||||
addTags(result, member)
|
|
||||||
await addDocComment(result, member)
|
|
||||||
addReferences(result, member)
|
|
||||||
}
|
|
||||||
|
|
||||||
function addFrontmatter(result: Result, member: ApiItem, categoryName: string, order: number) {
|
|
||||||
result.markdown += `---
|
|
||||||
title: ${member.displayName}
|
|
||||||
status: published
|
|
||||||
category: ${categoryName}
|
|
||||||
group: ${member.kind}
|
|
||||||
author: api
|
|
||||||
date: ${date}
|
|
||||||
order: ${order}
|
|
||||||
---`
|
|
||||||
}
|
|
||||||
|
|
||||||
function addHorizontalRule(result: Result) {
|
|
||||||
result.markdown += `---\n\n`
|
|
||||||
}
|
|
||||||
|
|
||||||
function addMemberName(result: Result, member: ApiItem) {
|
|
||||||
if (member.kind === 'Constructor') {
|
|
||||||
result.markdown += `### \`Constructor\`\n\n`
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!member.displayName) return
|
|
||||||
result.markdown += `### \`${member.displayName}${
|
|
||||||
member.kind === 'Method' ? '()' : ''
|
|
||||||
}\` \\{#${getSlug(member)}}\n\n`
|
|
||||||
}
|
|
||||||
|
|
||||||
async function addDocComment(result: Result, member: ApiItem) {
|
|
||||||
if (!(member instanceof ApiDocumentedItem)) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if (member.tsdocComment) {
|
|
||||||
result.markdown += await MarkdownWriter.docNodeToMarkdown(
|
|
||||||
member,
|
|
||||||
member.tsdocComment.summarySection
|
|
||||||
)
|
|
||||||
|
|
||||||
const exampleBlocks = member.tsdocComment.customBlocks.filter(
|
|
||||||
(block) => block.blockTag.tagNameWithUpperCase === '@EXAMPLE'
|
|
||||||
)
|
|
||||||
|
|
||||||
if (exampleBlocks.length) {
|
|
||||||
result.markdown += `\n\n`
|
|
||||||
result.markdown += `##### Example\n\n`
|
|
||||||
for (const example of exampleBlocks) {
|
|
||||||
result.markdown += await MarkdownWriter.docNodeToMarkdown(member, example.content)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (
|
|
||||||
member instanceof ApiMethod ||
|
|
||||||
member instanceof ApiMethodSignature ||
|
|
||||||
member instanceof ApiConstructor ||
|
|
||||||
member instanceof ApiConstructSignature ||
|
|
||||||
member instanceof ApiFunction
|
|
||||||
) {
|
|
||||||
result.markdown += `##### Parameters\n\n\n`
|
|
||||||
if (!member.parameters.length) {
|
|
||||||
result.markdown += `None\n\n`
|
|
||||||
} else {
|
|
||||||
result.markdown += '<ParametersTable>\n\n'
|
|
||||||
for (const param of member.parameters) {
|
|
||||||
result.markdown += '<ParametersTableRow>\n'
|
|
||||||
result.markdown += '<ParametersTableName>\n\n'
|
|
||||||
result.markdown += `\`${param.name}\`\n\n`
|
|
||||||
|
|
||||||
if (param.isOptional) {
|
|
||||||
result.markdown += ` <Small>(optional)</Small>\n\n`
|
|
||||||
}
|
|
||||||
result.markdown += `</ParametersTableName>\n`
|
|
||||||
result.markdown += `<ParametersTableDescription>\n\n`
|
|
||||||
result.markdown += await typeExcerptToMarkdown(param.parameterTypeExcerpt, {
|
|
||||||
kind: 'ParameterType',
|
|
||||||
printWidth: 60,
|
|
||||||
})
|
|
||||||
result.markdown += `\n\n`
|
|
||||||
if (param.tsdocParamBlock) {
|
|
||||||
result.markdown += await MarkdownWriter.docNodeToMarkdown(
|
|
||||||
member,
|
|
||||||
param.tsdocParamBlock.content
|
|
||||||
)
|
|
||||||
}
|
|
||||||
result.markdown += `\n\n</ParametersTableDescription>\n`
|
|
||||||
result.markdown += `</ParametersTableRow>\n`
|
|
||||||
}
|
|
||||||
result.markdown += '</ParametersTable>\n\n'
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!(member instanceof ApiConstructor)) {
|
|
||||||
result.markdown += `##### Returns\n\n\n`
|
|
||||||
result.markdown += await typeExcerptToMarkdown(member.returnTypeExcerpt, {
|
|
||||||
kind: 'ReturnType',
|
|
||||||
})
|
|
||||||
result.markdown += `\n\n`
|
|
||||||
if (member.tsdocComment && member.tsdocComment.returnsBlock) {
|
|
||||||
result.markdown += await MarkdownWriter.docNodeToMarkdown(
|
|
||||||
member,
|
|
||||||
member.tsdocComment.returnsBlock.content
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if (
|
|
||||||
member instanceof ApiVariable ||
|
|
||||||
member instanceof ApiTypeAlias ||
|
|
||||||
member instanceof ApiProperty ||
|
|
||||||
member instanceof ApiPropertySignature ||
|
|
||||||
member instanceof ApiClass ||
|
|
||||||
member instanceof ApiInterface ||
|
|
||||||
member instanceof ApiEnum ||
|
|
||||||
member instanceof ApiNamespace
|
|
||||||
) {
|
|
||||||
const params = member.tsdocComment?.params
|
|
||||||
if (params && params.count > 0) {
|
|
||||||
result.markdown += `##### Parameters\n\n\n`
|
|
||||||
result.markdown += '<ParametersTable>\n\n'
|
|
||||||
for (const block of params.blocks) {
|
|
||||||
result.markdown += '<ParametersTableRow>\n'
|
|
||||||
result.markdown += '<ParametersTableName>\n\n'
|
|
||||||
result.markdown += `\`${block.parameterName}\`\n\n`
|
|
||||||
result.markdown += `</ParametersTableName>\n`
|
|
||||||
result.markdown += `<ParametersTableDescription>\n\n`
|
|
||||||
result.markdown += await MarkdownWriter.docNodeToMarkdown(member, block.content)
|
|
||||||
result.markdown += `\n\n</ParametersTableDescription>\n`
|
|
||||||
result.markdown += `</ParametersTableRow>\n`
|
|
||||||
}
|
|
||||||
result.markdown += '</ParametersTable>\n\n'
|
|
||||||
}
|
|
||||||
|
|
||||||
// no specific docs for these types
|
|
||||||
result.markdown += `##### Signature\n\n\n`
|
|
||||||
result.markdown += await typeExcerptToMarkdown(member.excerpt, { kind: member.kind })
|
|
||||||
result.markdown += `\n\n`
|
|
||||||
} else {
|
|
||||||
throw new Error('unknown member kind: ' + member.kind)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async function typeExcerptToMarkdown(
|
|
||||||
excerpt: Excerpt,
|
|
||||||
{ kind, printWidth }: { kind: ApiItemKind | 'ReturnType' | 'ParameterType'; printWidth?: number }
|
|
||||||
) {
|
|
||||||
let code = ''
|
|
||||||
for (const token of excerpt.spannedTokens) {
|
|
||||||
code += token.text
|
|
||||||
}
|
|
||||||
|
|
||||||
code = code.replace(/^export /, '')
|
|
||||||
code = code.replace(/^declare /, '')
|
|
||||||
|
|
||||||
switch (kind) {
|
|
||||||
case ApiItemKind.CallSignature:
|
|
||||||
case ApiItemKind.EntryPoint:
|
|
||||||
case ApiItemKind.EnumMember:
|
|
||||||
case ApiItemKind.Function:
|
|
||||||
case ApiItemKind.Model:
|
|
||||||
case ApiItemKind.Namespace:
|
|
||||||
case ApiItemKind.None:
|
|
||||||
case ApiItemKind.Package:
|
|
||||||
case ApiItemKind.TypeAlias:
|
|
||||||
code = await formatWithPrettier(code, { printWidth })
|
|
||||||
break
|
|
||||||
case 'ReturnType':
|
|
||||||
case 'ParameterType':
|
|
||||||
code = await formatWithPrettier(`type X = () =>${code}`, { printWidth })
|
|
||||||
assert(code.startsWith('type X = () =>'))
|
|
||||||
code = code = code.replace(/^type X = \(\) =>[ \n]/, '')
|
|
||||||
break
|
|
||||||
case ApiItemKind.Class:
|
|
||||||
case ApiItemKind.Enum:
|
|
||||||
case ApiItemKind.Interface:
|
|
||||||
code = await formatWithPrettier(`${code} {}`, { printWidth })
|
|
||||||
break
|
|
||||||
case ApiItemKind.Constructor:
|
|
||||||
case ApiItemKind.ConstructSignature:
|
|
||||||
case ApiItemKind.IndexSignature:
|
|
||||||
case ApiItemKind.Method:
|
|
||||||
case ApiItemKind.MethodSignature:
|
|
||||||
case ApiItemKind.Property:
|
|
||||||
case ApiItemKind.PropertySignature:
|
|
||||||
case ApiItemKind.Variable:
|
|
||||||
code = await formatWithPrettier(`class X { ${code} }`, { printWidth })
|
|
||||||
assert(code.startsWith('class X {\n'))
|
|
||||||
assert(code.endsWith('\n}'))
|
|
||||||
code = code.slice('class X {\n'.length, -'\n}'.length)
|
|
||||||
code = code.replace(/^ {2}/gm, '')
|
|
||||||
break
|
|
||||||
default:
|
|
||||||
throw Error()
|
|
||||||
}
|
|
||||||
|
|
||||||
return ['```ts', code, '```'].join('\n')
|
|
||||||
}
|
|
||||||
|
|
||||||
function addTags(result: Result, member: ApiItem) {
|
|
||||||
const tags = []
|
|
||||||
if (ApiReleaseTagMixin.isBaseClassOf(member)) {
|
|
||||||
tags.push(ReleaseTag[member.releaseTag])
|
|
||||||
}
|
|
||||||
if (ApiStaticMixin.isBaseClassOf(member) && member.isStatic) {
|
|
||||||
tags.push('Static')
|
|
||||||
}
|
|
||||||
if (ApiReadonlyMixin.isBaseClassOf(member) && member.isReadonly) {
|
|
||||||
tags.push('Readonly')
|
|
||||||
}
|
|
||||||
tags.push(member.kind)
|
|
||||||
result.markdown += `<Small>${tags.join(' ')}</Small>\n\n`
|
|
||||||
}
|
|
||||||
|
|
||||||
function addReferences(result: Result, member: ApiItem) {
|
|
||||||
if (!(member instanceof ApiDeclaredItem)) return
|
|
||||||
const references = new Set<string>()
|
|
||||||
|
|
||||||
member.excerptTokens.forEach((token) => {
|
|
||||||
if (token.kind !== 'Reference') return
|
|
||||||
const apiItemResult = assertExists(member.getAssociatedModel()).resolveDeclarationReference(
|
|
||||||
assertExists(token.canonicalReference),
|
|
||||||
member
|
|
||||||
)
|
|
||||||
if (apiItemResult.errorMessage) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
const apiItem = assertExists(apiItemResult.resolvedApiItem)
|
|
||||||
const url = `/gen/${getPath(apiItem)}`
|
|
||||||
references.add(`[${token.text}](${url})`)
|
|
||||||
})
|
|
||||||
|
|
||||||
if (references.size) {
|
|
||||||
result.markdown += `##### References\n\n`
|
|
||||||
result.markdown += Array.from(references).join(', ') + '\n\n'
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,13 +0,0 @@
|
||||||
// import { buildDocs } from './build-docs'
|
|
||||||
import { generateApiContent } from './generateApiContent'
|
|
||||||
import { generateContent } from './generateContent'
|
|
||||||
|
|
||||||
async function main() {
|
|
||||||
const { log: nicelog } = console
|
|
||||||
nicelog('Creating content for www.')
|
|
||||||
// await buildDocs()
|
|
||||||
await generateApiContent()
|
|
||||||
await generateContent()
|
|
||||||
}
|
|
||||||
|
|
||||||
main()
|
|
Loading…
Reference in a new issue