From f5e78949d7307629856a1d5204dc31fc320c9e1d Mon Sep 17 00:00:00 2001 From: DJj123dj <80536295+DJj123dj@users.noreply.github.com> Date: Fri, 31 Jan 2025 16:03:02 +0100 Subject: [PATCH] Added typedoc for OT Docs API Reference --- .docs/createDocs.js | 246 ++++++++++++++++++++++++++++++++++++++ .docs/typedoc-config.json | 13 ++ .gitignore | 18 ++- package.json | 3 +- src/core/startup/init.ts | 4 +- 5 files changed, 275 insertions(+), 9 deletions(-) create mode 100644 .docs/createDocs.js create mode 100644 .docs/typedoc-config.json diff --git a/.docs/createDocs.js b/.docs/createDocs.js new file mode 100644 index 0000000..639150e --- /dev/null +++ b/.docs/createDocs.js @@ -0,0 +1,246 @@ +//@ts-check +const fs = require("fs") + +/*////// OT DOCS GENERATOR //////// +What does this file do?: +Well, this file will parse the result returned from typedoc (the documentation generator). +The resulting file will then be used in the OT Docs markdown generator to generate the Open Ticket API Reference. + +In short: +This file generates the documentation for Open Ticket :) + +Contributing: +If you want to contribute to the Open Ticket Docs, please run the following command: + +> npm run docs + +This will give you a "result.json" file which can then be used in the OT Docs. +Further instructions can be found there. +/////////////////////////////////*/ + +if (!fs.existsSync(".docs/typedoc-result.json")){ + console.log("Unable to generate documentation! Run typedoc first!") + process.exit(1) +} +const result = JSON.parse(fs.readFileSync(".docs/typedoc-result.json").toString()) +const availableElements = [] +const skipElementNames = [ + "#opendiscord-types" +] + +const handleFunction = (memberType) => { + if (!memberType || !memberType.signatures || !Array.isArray(memberType.signatures)) return {type:"unknown"} + + //try to get signature + let signature = memberType.signatures.find((s) => !s.typeParameters) + if (!signature) signature = memberType.signatures[0] + if (!signature) return {type:"unknown"} + + //try to get comment + let comment = signature.comment + if (!comment) comment = memberType.signatures.find((s) => s.comment)?.comment + if (!comment) comment = null + + const signatureInherited = signature.flags.isInherited ? true : false + const signatureComment = comment ? comment.summary?.map((c) => c.text).join("") : null + const signatureParameters = signature.parameters?.map((p) => { + return {name:p.name,details:handleType(p.type)} + }) ?? [] + const signatureReturns = handleType(signature.type) + + return {type:"function",inherited:signatureInherited,comment:signatureComment,parameters:signatureParameters,returns:signatureReturns} +} + +const handleType = (memberType) => { + if (!memberType || !memberType.type) return {type:"unknown"} + else if (memberType.type == "intrinsic") return {type:"primitive",name:memberType.name} + else if (memberType.type == "array") return {type:"array",child:handleType(memberType.elementType)} + else if (memberType.type == "union") return {type:"union",children:memberType.types.map((t) => handleType(t))} + else if (memberType.type == "intersection") return {type:"intersection",children:memberType.types.map((t) => handleType(t))} + else if (memberType.type == "reference"){ + const referenceTypeArguments = (memberType.typeArguments) ? memberType.typeArguments.map((ta) => handleType(ta)) : null + let referenceType = availableElements.find((el) => el.name == memberType.name)?.type ?? null + + if (referenceType){ + //member is known type (from Open Ticket) + return {type:"reference",name:memberType.name,target:referenceType,typeArguments:referenceTypeArguments} + }else if (memberType.package){ + //member is type from node.js or another package + if (memberType.package == "typescript") return {type:"internal",name:memberType.name,typeArguments:referenceTypeArguments} + if (memberType.package == "open-ticket" && memberType.refersToTypeParameter) return {type:"typeParam",name:memberType.name,typeArguments:referenceTypeArguments} + else return {type:"external",package:memberType.package,name:memberType.name,typeArguments:referenceTypeArguments} + }else return {type:"unknown"} + } + else if (memberType.type == "literal") return {type:"literal",name:(typeof memberType.value == "string") ? JSON.stringify(memberType.value) : String(memberType.value)} + else if (memberType.type == "reflection" && memberType.declaration && memberType.declaration.signatures && memberType.declaration.signatures[0]){ + return handleFunction(memberType.declaration) + }else if (memberType.type == "typeOperator"){ + if (memberType.operator == "keyof") return {type:"keyof",child:handleType(memberType.target)} + else if (memberType.operator == "readonly") return {type:"readonly",child:handleType(memberType.target)} + else if (memberType.operator == "unique") return {type:"unique",child:handleType(memberType.target)} + } + else if (memberType.type == "conditional") return {type:"conditional",checker:handleType(memberType.checkType),extends:handleType(memberType.extendsType),trueValue:handleType(memberType.trueType),falseValue:handleType(memberType.falseType)} + else if (memberType.type == "indexedAccess") return {type:"index",index:handleType(memberType.indexType),object:handleType(memberType.objectType)} + else if (memberType.type == "mapped") return {type:"mapped",parameterName:memberType.parameter,parameter:handleType(memberType.parameterType),template:handleType(memberType.templateType)} + else if (memberType.type == "optional") return {type:"optional",child:handleType(memberType.elementType)} + else if (memberType.type == "predicate") return {type:"predicate",name:memberType.name,target:handleType(memberType.targetType)} + else if (memberType.type == "query") return {type:"query",target:handleType(memberType.queryType)} + else if (memberType.type == "rest") return {type:"rest",child:handleType(memberType.elementType)} + else if (memberType.type == "tuple") return {type:"tuple",children:memberType.elements.map((t) => handleType(t))} + else if (memberType.type == "templateLiteral") return {type:"template",head:memberType.head,tails:memberType.tail.map((t) => { + return {element:handleType(t[0]),text:t[1]} + })} + else return {type:"unknown"} +} + +for (const file of result.children){ + const classIds = file.groups?.find((g) => g.title == "Classes")?.children ?? [] + const interfaceIds = file.groups?.find((g) => g.title == "Interfaces")?.children ?? [] + const typeIds = file.groups?.find((g) => g.title == "Type Aliases")?.children ?? [] + const enumIds = file.groups?.find((g) => g.title == "Enumerations")?.children ?? [] + const varIds = file.groups?.find((g) => g.title == "Variables")?.children ?? [] + + for (const declaration of file.children){ + const declarationName = declaration.name + const declarationType = classIds.includes(declaration.id) ? "class" : interfaceIds.includes(declaration.id) ? "interface" : typeIds.includes(declaration.id) ? "type" : enumIds.includes(declaration.id) ? "enum" : varIds.includes(declaration.id) ? "variable" : "other" + availableElements.push({name:declarationName,type:declarationType}) + } +} + +const exported = [] +for (const file of result.children){ + const classIds = file.groups?.find((g) => g.title == "Classes")?.children ?? [] + const interfaceIds = file.groups?.find((g) => g.title == "Interfaces")?.children ?? [] + const typeIds = file.groups?.find((g) => g.title == "Type Aliases")?.children ?? [] + const enumIds = file.groups?.find((g) => g.title == "Enumerations")?.children ?? [] + const varIds = file.groups?.find((g) => g.title == "Variables")?.children ?? [] + + for (const declaration of (file.children ?? [])){ + const declarationName = declaration.name + if (skipElementNames.includes(declarationName)) continue + + const declarationType = classIds.includes(declaration.id) ? "class" : interfaceIds.includes(declaration.id) ? "interface" : typeIds.includes(declaration.id) ? "type" : enumIds.includes(declaration.id) ? "enum" : varIds.includes(declaration.id) ? "variable" : "other" + const declarationTypeParams = (declaration.typeParameters) ? declaration.typeParameters.map((tp) => { + return {name:tp.name,type:handleType(tp.type)} + }) : null + const declarationComment = declaration.comment?.summary?.map((c) => c.text).join("") ?? null + const declarationConstant = declaration.flags.isConst ? true : false + + const rawDeclarationSource = declaration.sources ? (declaration.sources[0] ?? null) : null + const declarationSource = rawDeclarationSource ? rawDeclarationSource.fileName+":"+rawDeclarationSource.line+":"+rawDeclarationSource.character : null + + const declarationChildren = [] + if (declarationType == "class"){ + //CLASS + const constructorIds = declaration.groups?.find((g) => g.title == "Constructors")?.children ?? [] + const propertyIds = declaration.groups?.find((g) => g.title == "Properties")?.children ?? [] + const methodIds = declaration.groups?.find((g) => g.title == "Methods")?.children ?? [] + + for (const member of (declaration.children ?? [])){ + const memberName = member.name + const memberType = constructorIds.includes(member.id) ? "constructor" : propertyIds.includes(member.id) ? "property" : methodIds.includes(member.id) ? "method" : "other" + let memberComment = member.comment?.summary?.map((c) => c.text).join("") ?? null + + const rawMemberSource = member.sources ? (member.sources[0] ?? null) : null + let memberSource = rawMemberSource ? rawMemberSource.fileName+":"+rawMemberSource.line+":"+rawMemberSource.character : null + + let memberDetails = null + if (memberType == "property"){ + //CLASS => PROPERTY + memberDetails = handleType(member.type) + + }else if (memberType == "method"){ + //CLASS => METHOD + memberDetails = handleFunction(member) + memberComment = memberDetails.comment + } + + declarationChildren.push({ + type:memberType, + name:memberName, + comment:memberComment, + source:memberSource, + details:memberDetails + }) + } + }else if (declarationType == "interface"){ + //INTERFACE + const propertyIds = declaration.groups?.find((g) => g.title == "Properties")?.children ?? [] + const methodIds = declaration.groups?.find((g) => g.title == "Methods")?.children ?? [] + + for (const member of (declaration.children ?? [])){ + const memberName = member.name + const memberType = propertyIds.includes(member.id) ? "property" : methodIds.includes(member.id) ? "method" : "other" + let memberComment = member.comment?.summary?.map((c) => c.text).join("") ?? null + + const rawMemberSource = member.sources ? (member.sources[0] ?? null) : null + let memberSource = rawMemberSource ? rawMemberSource.fileName+":"+rawMemberSource.line+":"+rawMemberSource.character : null + + let memberDetails = null + if (memberType == "property"){ + //INTERFACE => PROPERTY + memberDetails = handleType(member.type) + }else if (memberType == "method"){ + //INTERFACE => METHOD + memberDetails = handleFunction(member) + memberComment = memberDetails.comment + } + + declarationChildren.push({ + type:memberType, + name:memberName, + comment:memberComment, + source:memberSource, + details:memberDetails + }) + } + }else if (declarationType == "type"){ + //TYPE + declarationChildren.push(handleType(declaration.type)) + }else if (declarationType == "enum"){ + //ENUM + const enumerableIds = declaration.groups?.find((g) => g.title == "Enumeration Members")?.children ?? [] + + for (const member of (declaration.children ?? [])){ + const memberName = member.name + const memberType = enumerableIds.includes(member.id) ? "enumerable" : "other" + const memberComment = member.comment?.summary?.map((c) => c.text).join("") ?? null + + const rawMemberSource = member.sources ? (member.sources[0] ?? null) : null + let memberSource = rawMemberSource ? rawMemberSource.fileName+":"+rawMemberSource.line+":"+rawMemberSource.character : null + + let memberDetails = null + if (memberType == "enumerable"){ + //INTERFACE => PROPERTY + memberDetails = handleType(member.type) + } + + declarationChildren.push({ + type:memberType, + name:memberName, + comment:memberComment, + source:memberSource, + details:memberDetails + }) + } + }else if (declarationType == "variable"){ + //VARIABLE + declarationChildren.push(handleType(declaration.type)) + + }else if (declarationType == "other"){ + } + + exported.push({ + type:declarationType, + name:declarationName, + comment:declarationComment, + constant:declarationConstant, + source:declarationSource, + children:declarationChildren, + typeParams:declarationTypeParams + }) + } +} + +fs.writeFileSync(".docs/result.json",JSON.stringify(exported,null,"\t")) +fs.rmSync(".docs/typedoc-result.json") \ No newline at end of file diff --git a/.docs/typedoc-config.json b/.docs/typedoc-config.json new file mode 100644 index 0000000..61e64f7 --- /dev/null +++ b/.docs/typedoc-config.json @@ -0,0 +1,13 @@ +{ + "$schema": "https://typedoc.org/schema.json", + "entryPoints": [ + "../src/core/api/modules/", + "../src/core/api/defaults/", + "../src/core/api/openticket/", + "../src/core/api/main.ts", + "../src/core/startup/init.ts", + ], + "entryPointStrategy": "expand", + "json": "./typedoc-result.json", + "basePath": "../" +} \ No newline at end of file diff --git a/.gitignore b/.gitignore index b9a2291..3a10e77 100644 --- a/.gitignore +++ b/.gitignore @@ -1,16 +1,22 @@ node_modules/ package-lock.json +.vscode/ + devconfig/ devdatabase/ +plugins/* +!plugins/example-plugin/ +database/openticket.sqlite +dist/ + otdebug.txt + .DS_Store */.DS_Store */*/.DS_Store */*/*/.DS_Store **/.DS_Store -TEMP/ -.vscode/ -plugins/* -!plugins/example-plugin/ -dist/ -database/openticket.sqlite \ No newline at end of file + +.docs/* +!.docs/createDocs.js +!.docs/typedoc-config.json \ No newline at end of file diff --git a/package.json b/package.json index 8fba6ed..dbede6b 100644 --- a/package.json +++ b/package.json @@ -16,7 +16,8 @@ "start": "node index.js", "startnc": "node index.js --no-compile", "test": "node index.js --dev-config --dev-database", - "testnc": "node index.js --no-compile --dev-config --dev-database" + "testnc": "node index.js --no-compile --dev-config --dev-database", + "docs": "npx typedoc --options .docs/typedoc-config.json && node .docs/createDocs.js" }, "type": "commonjs", "license": "GPL-3.0-only", diff --git a/src/core/startup/init.ts b/src/core/startup/init.ts index 494cf08..bb987e9 100644 --- a/src/core/startup/init.ts +++ b/src/core/startup/init.ts @@ -61,7 +61,7 @@ const timer = (ms:number): Promise => { }) } -interface ODUtilities { +export interface ODUtilities { /**## project `utility variable` * This is the name of the project you are currently in. * @@ -120,7 +120,7 @@ interface ODUtilities { * * It shouldn't be used by plugins because this is an internal API feature! */ -class ODVersionMigration { +export class ODVersionMigration { /**The version to migrate data to */ version: api.ODVersion /**The migration function */