From d3ce35c9165a885bcd58f7ccc175a9593a182ba5 Mon Sep 17 00:00:00 2001 From: Steve Ruiz Date: Tue, 27 Jun 2023 09:34:26 +0100 Subject: [PATCH] [fix] remove docs scripts (#1651) This PR removes the docs generation scripts. ### Change Type - [x] `documentation` --- scripts/lib/docs/docs-types.ts | 130 --------- scripts/lib/docs/docs-utils.ts | 237 ---------------- scripts/lib/docs/generate-on-reload.ts | 10 - scripts/lib/docs/generateApiContent.ts | 128 --------- scripts/lib/docs/generateContent.ts | 229 --------------- scripts/lib/docs/generateSection.ts | 171 ------------ scripts/lib/docs/getApiMarkdown.ts | 373 ------------------------- scripts/lib/docs/index.ts | 13 - 8 files changed, 1291 deletions(-) delete mode 100644 scripts/lib/docs/docs-types.ts delete mode 100644 scripts/lib/docs/docs-utils.ts delete mode 100644 scripts/lib/docs/generate-on-reload.ts delete mode 100644 scripts/lib/docs/generateApiContent.ts delete mode 100644 scripts/lib/docs/generateContent.ts delete mode 100644 scripts/lib/docs/generateSection.ts delete mode 100644 scripts/lib/docs/getApiMarkdown.ts delete mode 100644 scripts/lib/docs/index.ts diff --git a/scripts/lib/docs/docs-types.ts b/scripts/lib/docs/docs-types.ts deleted file mode 100644 index 435734edf..000000000 --- a/scripts/lib/docs/docs-types.ts +++ /dev/null @@ -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 - -/** A table keyed by slug of articles. */ -export type Articles = Record - -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 } diff --git a/scripts/lib/docs/docs-utils.ts b/scripts/lib/docs/docs-utils.ts deleted file mode 100644 index 4833e12ac..000000000 --- a/scripts/lib/docs/docs-utils.ts +++ /dev/null @@ -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 -} diff --git a/scripts/lib/docs/generate-on-reload.ts b/scripts/lib/docs/generate-on-reload.ts deleted file mode 100644 index 4fd85f3e7..000000000 --- a/scripts/lib/docs/generate-on-reload.ts +++ /dev/null @@ -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() diff --git a/scripts/lib/docs/generateApiContent.ts b/scripts/lib/docs/generateApiContent.ts deleted file mode 100644 index 173490d5e..000000000 --- a/scripts/lib/docs/generateApiContent.ts +++ /dev/null @@ -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() - - 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 { - 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 - } -} diff --git a/scripts/lib/docs/generateContent.ts b/scripts/lib/docs/generateContent.ts deleted file mode 100644 index 1ea93e001..000000000 --- a/scripts/lib/docs/generateContent.ts +++ /dev/null @@ -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 = {} - - // Uncategorized articles - const _ucg: Article[] = [] - - // A temporary table of articles mapped to categories - const _categoryArticles: Record = 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 { - 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 - } -} diff --git a/scripts/lib/docs/generateSection.ts b/scripts/lib/docs/generateSection.ts deleted file mode 100644 index 48cf14069..000000000 --- a/scripts/lib/docs/generateSection.ts +++ /dev/null @@ -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 = {} - - // Uncategorized articles - const _ucg: Article[] = [] - - // A temporary table of articles mapped to categories - const _categoryArticles: Record = 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), - ], - } -} diff --git a/scripts/lib/docs/getApiMarkdown.ts b/scripts/lib/docs/getApiMarkdown.ts deleted file mode 100644 index 735e60b0f..000000000 --- a/scripts/lib/docs/getApiMarkdown.ts +++ /dev/null @@ -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 += `
\n\tTable of Contents\n` - addMarkdown(result, toc.markdown) - result.markdown += `
\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 += '\n\n' - for (const param of member.parameters) { - result.markdown += '\n' - result.markdown += '\n\n' - result.markdown += `\`${param.name}\`\n\n` - - if (param.isOptional) { - result.markdown += ` (optional)\n\n` - } - result.markdown += `\n` - result.markdown += `\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\n` - result.markdown += `\n` - } - result.markdown += '\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 += '\n\n' - for (const block of params.blocks) { - result.markdown += '\n' - result.markdown += '\n\n' - result.markdown += `\`${block.parameterName}\`\n\n` - result.markdown += `\n` - result.markdown += `\n\n` - result.markdown += await MarkdownWriter.docNodeToMarkdown(member, block.content) - result.markdown += `\n\n\n` - result.markdown += `\n` - } - result.markdown += '\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 += `${tags.join(' ')}\n\n` -} - -function addReferences(result: Result, member: ApiItem) { - if (!(member instanceof ApiDeclaredItem)) return - const references = new Set() - - 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' - } -} diff --git a/scripts/lib/docs/index.ts b/scripts/lib/docs/index.ts deleted file mode 100644 index 64b9456f1..000000000 --- a/scripts/lib/docs/index.ts +++ /dev/null @@ -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()