aboutsummaryrefslogtreecommitdiff
path: root/scripts/apidocs/processing/class.ts
diff options
context:
space:
mode:
authorST-DDT <[email protected]>2024-04-01 10:21:18 +0200
committerGitHub <[email protected]>2024-04-01 10:21:18 +0200
commit6191a5d883048b694404dbf42527caba395828ea (patch)
treed0f18f17789cb0bbdb5d6087f1a95772438dfe27 /scripts/apidocs/processing/class.ts
parent7dae52bfcd93c41ec9d2c4dd4d96a07f31c3dfc1 (diff)
downloadfaker-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.ts223
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);
+}