diff options
| author | ST-DDT <[email protected]> | 2024-04-01 10:21:18 +0200 |
|---|---|---|
| committer | GitHub <[email protected]> | 2024-04-01 10:21:18 +0200 |
| commit | 6191a5d883048b694404dbf42527caba395828ea (patch) | |
| tree | d0f18f17789cb0bbdb5d6087f1a95772438dfe27 /scripts/apidocs/processing/class.ts | |
| parent | 7dae52bfcd93c41ec9d2c4dd4d96a07f31c3dfc1 (diff) | |
| download | faker-6191a5d883048b694404dbf42527caba395828ea.tar.xz faker-6191a5d883048b694404dbf42527caba395828ea.zip | |
docs: rewrite api-docs generation using ts-morph (#2628)
Diffstat (limited to 'scripts/apidocs/processing/class.ts')
| -rw-r--r-- | scripts/apidocs/processing/class.ts | 223 |
1 files changed, 223 insertions, 0 deletions
diff --git a/scripts/apidocs/processing/class.ts b/scripts/apidocs/processing/class.ts new file mode 100644 index 00000000..5af5e874 --- /dev/null +++ b/scripts/apidocs/processing/class.ts @@ -0,0 +1,223 @@ +import type { ClassDeclaration, InterfaceDeclaration, Project } from 'ts-morph'; +import { required, valuesForKeys } from '../utils/value-checks'; +import { newProcessingError } from './error'; +import type { JSDocableLikeNode } from './jsdocs'; +import { + getDeprecated, + getDescription, + getExamples, + getJsDocs, +} from './jsdocs'; +import type { RawApiDocsMethod } from './method'; +import { + processClassConstructors, + processClassMethods, + processInterfaceMethods, + processProjectFunctions, +} from './method'; + +/** + * Represents a raw page in the API docs. + */ +export interface RawApiDocsPage { + /** + * The title of the page as shown to users. + */ + title: string; + /** + * The title of the page in camel case as used in paths. + */ + camelTitle: string; + /** + * The category of the page, if it has one. + */ + category: string | undefined; + /** + * The deprecation notice of the page, if it has one. + */ + deprecated: string | undefined; + /** + * The description of the page. + */ + description: string; + /** + * The usage examples of the elements on the page. + */ + examples: string[]; + /** + * The api methods on the page. + */ + methods: RawApiDocsMethod[]; +} + +// Classes + +function getAllClasses( + project: Project, + filter: (name: string) => boolean = () => true +): Record<string, ClassDeclaration> { + return Object.fromEntries( + project + .getSourceFiles() + .flatMap((file) => file.getClasses()) + .map((clazz) => [clazz.getNameOrThrow(), clazz] as const) + .filter(([name]) => filter(name)) + ); +} + +export function processProjectClasses(project: Project): RawApiDocsPage[] { + return processClasses( + valuesForKeys(getAllClasses(project), ['Faker', 'SimpleFaker']) + ); +} + +function processClasses(classes: ClassDeclaration[]): RawApiDocsPage[] { + return classes.map((clazz) => { + try { + return processClass(clazz); + } catch (error) { + throw newProcessingError({ + type: 'class', + name: clazz.getNameOrThrow(), + source: clazz, + cause: error, + }); + } + }); +} + +export function processClass(clazz: ClassDeclaration): RawApiDocsPage { + const result = processModule(clazz); + result.methods.unshift(...processClassConstructors(clazz)); + return result; +} + +// Modules + +export function processModuleClasses(project: Project): RawApiDocsPage[] { + return processModules( + Object.values( + getAllClasses( + project, + (module: string): boolean => + module.endsWith('Module') && !module.startsWith('Simple') + ) + ).sort((a, b) => a.getNameOrThrow().localeCompare(b.getNameOrThrow())) + ); +} + +function processModules(modules: ClassDeclaration[]): RawApiDocsPage[] { + return modules.map((module) => { + try { + return processModule(module, 'Modules'); + } catch (error: unknown) { + throw newProcessingError({ + type: 'module', + name: getModuleName(module), + source: module, + cause: error, + }); + } + }); +} + +function processModule( + module: ClassDeclaration, + category: string | undefined = undefined +): RawApiDocsPage { + const title = getModuleName(module); + + return { + ...preparePage(module, title, category), + methods: processClassMethods(module), + }; +} + +function getModuleName(module: ClassDeclaration): string { + return required(module.getName(), 'module name').replace(/Module$/, ''); +} + +// Interfaces + +function getAllInterfaces( + project: Project +): Record<string, InterfaceDeclaration> { + return Object.fromEntries( + project + .getSourceFiles() + .flatMap((file) => file.getInterfaces()) + .map((iface) => [iface.getName(), iface] as const) + ); +} + +export function processProjectInterfaces(project: Project): RawApiDocsPage[] { + return processInterfaces( + valuesForKeys(getAllInterfaces(project), ['Randomizer']) + ); +} + +function processInterfaces( + interfaces: InterfaceDeclaration[] +): RawApiDocsPage[] { + return interfaces.map((iface) => { + try { + return processInterface(iface); + } catch (error) { + throw newProcessingError({ + type: 'interface', + name: iface.getName(), + source: iface, + cause: error, + }); + } + }); +} + +function processInterface(iface: InterfaceDeclaration): RawApiDocsPage { + return { + ...preparePage(iface, iface.getName()), + methods: processInterfaceMethods(iface), + }; +} + +// Utilities + +export function processProjectUtilities(project: Project): RawApiDocsPage { + console.log(`- Utilities`); + + return { + title: 'Utilities', + camelTitle: 'utils', + category: undefined, + deprecated: undefined, + description: 'A list of all the utilities available in Faker.js.', + examples: [], + methods: processProjectFunctions(project, 'mergeLocales'), + }; +} + +// Helpers + +function preparePage( + module: JSDocableLikeNode, + title: string, + category: string | undefined = undefined +): RawApiDocsPage { + console.log(`- ${title}`); + + const jsdocs = getJsDocs(module); + + return { + title, + camelTitle: toCamelCase(title), + category, + deprecated: getDeprecated(jsdocs), + description: getDescription(jsdocs), + examples: getExamples(jsdocs), + methods: [], + }; +} + +function toCamelCase(value: string): string { + return value.substring(0, 1).toLowerCase() + value.substring(1); +} |
