diff --git a/packages/builders/.gitignore b/packages/builders/.gitignore index 69227d965403..50a709e10bc1 100644 --- a/packages/builders/.gitignore +++ b/packages/builders/.gitignore @@ -19,7 +19,7 @@ dist/ typings/ docs/**/* -!docs/index.yml +!docs/index.json !docs/README.md !docs/examples/ !docs/examples/*.md diff --git a/packages/builders/docs/index.json b/packages/builders/docs/index.json new file mode 100644 index 000000000000..557341ae9dff --- /dev/null +++ b/packages/builders/docs/index.json @@ -0,0 +1 @@ +[{ "name": "General", "files": [{ "name": "Welcome", "id": "welcome", "path": "../../README.md" }] }] diff --git a/packages/builders/docs/index.yml b/packages/builders/docs/index.yml deleted file mode 100644 index 2c993519e6e4..000000000000 --- a/packages/builders/docs/index.yml +++ /dev/null @@ -1,5 +0,0 @@ -- name: General - files: - - name: Welcome - id: welcome - path: ../../README.md diff --git a/packages/builders/package.json b/packages/builders/package.json index 0a0c01f62994..76a68b14125c 100644 --- a/packages/builders/package.json +++ b/packages/builders/package.json @@ -6,7 +6,7 @@ "build": "tsup", "lint": "prettier --check . && eslint src __tests__ --ext mjs,js,ts", "format": "prettier --write . && eslint src __tests__ --ext mjs,js,ts --fix", - "docs": "typedoc --json docs/typedoc-out.json src/index.ts && ts-docgen -i docs/typedoc-out.json -c docs/index.yml -o docs/docs.json", + "docs": "docgen -i src/index.ts -c docs/index.json -o docs/docs.json --typescript", "prepublishOnly": "yarn build && yarn lint && yarn test", "changelog": "git cliff --prepend ./CHANGELOG.md -u -c ./cliff.toml -r ../../ --include-path 'packages/builders/*'", "release": "cliff-jumper" @@ -60,6 +60,7 @@ "tslib": "^2.4.0" }, "devDependencies": { + "@discordjs/docgen": "workspace:^", "@discordjs/scripts": "workspace:^", "@favware/cliff-jumper": "^1.8.3", "@types/node": "^16.11.38", @@ -71,7 +72,6 @@ "eslint-plugin-import": "^2.26.0", "prettier": "^2.6.2", "tsup": "^6.1.0", - "typedoc": "^0.22.17", "typescript": "^4.7.3" }, "engines": { diff --git a/packages/collection/.gitignore b/packages/collection/.gitignore index a1f7b02f5d76..86b93e929ae6 100644 --- a/packages/collection/.gitignore +++ b/packages/collection/.gitignore @@ -18,7 +18,7 @@ pids dist/ typings/ docs/**/* -!docs/index.yml +!docs/index.json !docs/README.md # Miscellaneous diff --git a/packages/collection/docs/index.json b/packages/collection/docs/index.json new file mode 100644 index 000000000000..557341ae9dff --- /dev/null +++ b/packages/collection/docs/index.json @@ -0,0 +1 @@ +[{ "name": "General", "files": [{ "name": "Welcome", "id": "welcome", "path": "../../README.md" }] }] diff --git a/packages/collection/docs/index.yml b/packages/collection/docs/index.yml deleted file mode 100644 index 2c993519e6e4..000000000000 --- a/packages/collection/docs/index.yml +++ /dev/null @@ -1,5 +0,0 @@ -- name: General - files: - - name: Welcome - id: welcome - path: ../../README.md diff --git a/packages/collection/package.json b/packages/collection/package.json index f7cc2a1efdce..3db98176243a 100644 --- a/packages/collection/package.json +++ b/packages/collection/package.json @@ -6,7 +6,7 @@ "build": "tsup", "lint": "prettier --check . && eslint src __tests__ --ext mjs,js,ts", "format": "prettier --write . && eslint src __tests__ --ext mjs,js,ts --fix", - "docs": "typedoc --json docs/typedoc-out.json src/index.ts && ts-docgen -i docs/typedoc-out.json -c docs/index.yml -o docs/docs.json", + "docs": "docgen -i src/index.ts -c docs/index.json -o docs/docs.json --typescript", "prepublishOnly": "yarn build && yarn lint && yarn test", "changelog": "git cliff --prepend ./CHANGELOG.md -u -c ./cliff.toml -r ../../ --include-path 'packages/collection/*'", "release": "cliff-jumper" @@ -48,6 +48,7 @@ }, "homepage": "https://discord.js.org", "devDependencies": { + "@discordjs/docgen": "workspace:^", "@discordjs/scripts": "workspace:^", "@favware/cliff-jumper": "^1.8.3", "@types/node": "^16.11.38", @@ -59,7 +60,6 @@ "eslint-plugin-import": "^2.26.0", "prettier": "^2.6.2", "tsup": "^6.1.0", - "typedoc": "^0.22.17", "typescript": "^4.7.3" }, "engines": { diff --git a/packages/docgen/package.json b/packages/docgen/package.json index a1da8b632f45..b6ab716ad6f8 100644 --- a/packages/docgen/package.json +++ b/packages/docgen/package.json @@ -49,10 +49,11 @@ }, "homepage": "https://discord.js.org", "dependencies": { - "@discordjs/collection": "workspace:^", + "@discordjs/collection": "^0.7.0", "commander": "^9.3.0", "jsdoc-to-markdown": "^7.1.1", - "tslib": "^2.4.0" + "tslib": "^2.4.0", + "typedoc": "^0.22.17" }, "devDependencies": { "@favware/cliff-jumper": "^1.8.3", diff --git a/packages/docgen/src/documentation.ts b/packages/docgen/src/documentation.ts index 352b2c563900..732cd66e8847 100644 --- a/packages/docgen/src/documentation.ts +++ b/packages/docgen/src/documentation.ts @@ -1,5 +1,6 @@ -import { join } from 'node:path'; +import { dirname, join } from 'node:path'; import { Collection } from '@discordjs/collection'; +import type { DeclarationReflection } from 'typedoc'; import type { ChildTypes, Class, Config, CustomDocs, RootTypes } from './interfaces/index.js'; import { DocumentedClass } from './types/class.js'; import { DocumentedConstructor } from './types/constructor.js'; @@ -23,101 +24,199 @@ export class Documentation { public readonly externals = new Collection(); public constructor( - data: RootTypes[], + data: RootTypes[] | DeclarationReflection[], private readonly config: Config, private readonly custom?: Record, ) { - let items = data; - for (const item of items) { - switch (item.kind) { - case 'class': { - this.classes.set(item.name, new DocumentedClass(item, config)); - items = items.filter((i) => i.longname !== item.longname); - break; - } - case 'function': { - if (item.scope === 'global' || !item.memberof) { + if (config.typescript) { + const items = data as DeclarationReflection[]; + + for (const item of items) { + switch (item.kindString) { + case 'Class': { + this.classes.set(item.name, new DocumentedClass(item, config)); + if (item.children) { + this.parse(item.children, item.name); + } + break; + } + + case 'Function': { this.functions.set(item.name, new DocumentedMethod(item, config)); - items = items.filter((i) => i.longname !== item.longname); + break; } - break; - } - case 'interface': { - this.interfaces.set(item.name, new DocumentedInterface(item as unknown as Class, config)); - items = items.filter((i) => i.longname !== item.longname); - break; - } - case 'typedef': { - this.typedefs.set(item.name, new DocumentedTypeDef(item, config)); - items = items.filter((i) => i.longname !== item.longname); - break; + + case 'Interface': + case 'Type alias': + case 'Enumeration': + this.typedefs.set(item.name, new DocumentedTypeDef(item, config)); + if (item.children) { + this.parse(item.children, item.name); + } + break; + + default: + break; } - case 'external': { - this.externals.set(item.name, new DocumentedExternal(item, config)); - items = items.filter((i) => i.longname !== item.longname); - break; + } + } else { + let items = data as RootTypes[]; + + for (const item of items) { + switch (item.kind) { + case 'class': { + this.classes.set(item.name, new DocumentedClass(item, config)); + items = items.filter((i) => i.longname !== item.longname); + break; + } + case 'function': { + if (item.scope === 'global' || !item.memberof) { + this.functions.set(item.name, new DocumentedMethod(item, config)); + items = items.filter((i) => i.longname !== item.longname); + } + break; + } + case 'interface': { + this.interfaces.set(item.name, new DocumentedInterface(item as unknown as Class, config)); + items = items.filter((i) => i.longname !== item.longname); + break; + } + case 'typedef': { + this.typedefs.set(item.name, new DocumentedTypeDef(item, config)); + items = items.filter((i) => i.longname !== item.longname); + break; + } + case 'external': { + this.externals.set(item.name, new DocumentedExternal(item, config)); + items = items.filter((i) => i.longname !== item.longname); + break; + } + default: + break; } - default: - break; } - } - this.parse(items as ChildTypes[]); + this.parse(items as ChildTypes[]); + } } - public parse(items: ChildTypes[]) { - for (const member of items) { - let item: DocumentedMethod | DocumentedConstructor | DocumentedMember | DocumentedEvent | null = null; + public parse(items: ChildTypes[] | DeclarationReflection[], memberOf = '') { + if (this.config.typescript) { + const it = items as DeclarationReflection[]; - switch (member.kind) { - case 'constructor': { - item = new DocumentedConstructor(member, this.config); - break; - } - case 'function': { - item = new DocumentedMethod(member, this.config); - break; - } - case 'member': { - item = new DocumentedMember(member, this.config); - break; - } - case 'event': { - item = new DocumentedEvent(member, this.config); - break; + for (const member of it) { + let item: DocumentedMethod | DocumentedConstructor | DocumentedMember | DocumentedEvent | null = null; + + switch (member.kindString) { + case 'Constructor': { + item = new DocumentedConstructor(member, this.config); + break; + } + case 'Method': { + item = new DocumentedMethod(member, this.config); + break; + } + case 'Property': { + item = new DocumentedMember(member, this.config); + break; + } + case 'event': { + item = new DocumentedEvent(member, this.config); + break; + } + default: { + // eslint-disable-next-line @typescript-eslint/restrict-template-expressions + console.warn(`- Unknown documentation kind "${member.kindString}" - \n${JSON.stringify(member)}\n`); + } } - default: { - // @ts-expect-error - // eslint-disable-next-line @typescript-eslint/restrict-template-expressions - console.warn(`- Unknown documentation kind "${member.kind}" - \n${JSON.stringify(member)}\n`); + + const parent = this.classes.get(memberOf) ?? this.interfaces.get(memberOf); + if (parent) { + if (item) { + parent.add(item); + } else { + console.warn( + `- Documentation item could not be constructed for "${member.name}" - \n${JSON.stringify(member)}\n`, + ); + } + continue; } + + const info = []; + const name = (member.name || item?.data.name) ?? 'UNKNOWN'; + const meta = + member.kindString === 'constructor' + ? null + : { + file: member.sources?.[0]?.fileName, + line: member.sources?.[0]?.line, + path: dirname(member.sources?.[0]?.fileName ?? ''), + }; + + if (memberOf) info.push(`member of "${memberOf}"`); + if (meta) info.push(`${join(meta.path, meta.file ?? '')}${meta.line ? `:${meta.line}` : ''}`); + + console.warn(`- "${name}"${info.length ? ` (${info.join(', ')})` : ''} has no accessible parent.`); + if (!name && !info.length) console.warn('Raw object:', member); } + } else { + const it = items as ChildTypes[]; - const parent = this.classes.get(member.memberof ?? '') ?? this.interfaces.get(member.memberof ?? ''); - if (parent) { - if (item) { - parent.add(item); - } else { - console.warn( - `- Documentation item could not be constructed for "${member.name}" - \n${JSON.stringify(member)}\n`, - ); + for (const member of it) { + let item: DocumentedMethod | DocumentedConstructor | DocumentedMember | DocumentedEvent | null = null; + + switch (member.kind) { + case 'constructor': { + item = new DocumentedConstructor(member, this.config); + break; + } + case 'function': { + item = new DocumentedMethod(member, this.config); + break; + } + case 'member': { + item = new DocumentedMember(member, this.config); + break; + } + case 'event': { + item = new DocumentedEvent(member, this.config); + break; + } + default: { + // @ts-expect-error + // eslint-disable-next-line @typescript-eslint/restrict-template-expressions + console.warn(`- Unknown documentation kind "${member.kind}" - \n${JSON.stringify(member)}\n`); + } } - continue; - } - const info = []; - const name = (member.name || item?.data.name) ?? 'UNKNOWN'; - const memberof = member.memberof ?? item?.data.memberof; - const meta = - member.kind === 'constructor' - ? null - : { file: member.meta.filename, line: member.meta.lineno, path: member.meta.path }; + const parent = this.classes.get(member.memberof ?? '') ?? this.interfaces.get(member.memberof ?? ''); + if (parent) { + if (item) { + parent.add(item); + } else { + console.warn( + `- Documentation item could not be constructed for "${member.name}" - \n${JSON.stringify(member)}\n`, + ); + } + continue; + } - if (memberof) info.push(`member of "${memberof}"`); - if (meta) info.push(`${join(meta.path, meta.file)}${meta.line ? `:${meta.line}` : ''}`); + const info = []; + const name = (member.name || item?.data.name) ?? 'UNKNOWN'; + // @ts-expect-error + // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-unnecessary-condition + const memberof = member.memberof ?? item?.data?.memberof; + const meta = + member.kind === 'constructor' + ? null + : { file: member.meta.filename, line: member.meta.lineno, path: member.meta.path }; - console.warn(`- "${name}"${info.length ? ` (${info.join(', ')})` : ''} has no accessible parent.`); - if (!name && !info.length) console.warn('Raw object:', member); + if (memberof) info.push(`member of "${memberof as string}"`); + if (meta) info.push(`${join(meta.path, meta.file)}${meta.line ? `:${meta.line}` : ''}`); + + console.warn(`- "${name}"${info.length ? ` (${info.join(', ')})` : ''} has no accessible parent.`); + if (!name && !info.length) console.warn('Raw object:', member); + } } } diff --git a/packages/docgen/src/index.ts b/packages/docgen/src/index.ts index fbcb42fee3ca..38e56f6eb01d 100644 --- a/packages/docgen/src/index.ts +++ b/packages/docgen/src/index.ts @@ -3,6 +3,7 @@ import { readFileSync, writeFileSync } from 'node:fs'; import { join, basename, extname, dirname, relative } from 'node:path'; import { createCommand } from 'commander'; import jsdoc2md from 'jsdoc-to-markdown'; +import { Application, DeclarationReflection, TSConfigReader } from 'typedoc'; import { Documentation } from './documentation.js'; import type { ChildTypes, CustomDocs, RootTypes } from './interfaces/index.js'; import packageFile from '../package.json'; @@ -12,6 +13,7 @@ interface CLIOptions { custom: string; root: string; output: string; + typescript: boolean; } interface CustomFiles { @@ -30,14 +32,29 @@ const command = createCommand() .option('-i, --input ', 'Source directories to parse JSDocs in') .option('-c, --custom ', 'Custom docs definition file to use') .option('-r, --root [string]', 'Root directory of the project', '.') - .option('-o, --output ', 'Path to output file'); + .option('-o, --output ', 'Path to output file') + .option('--typescript', '', false); const program = command.parse(process.argv); const options = program.opts(); -console.log('Parsing JSDocs in source files...'); -const data = jsdoc2md.getTemplateDataSync({ files: options.input }) as (RootTypes & ChildTypes)[]; -console.log(`${data.length} JSDoc items parsed.`); +let data: (RootTypes & ChildTypes)[] | DeclarationReflection[] = []; +if (options.typescript) { + console.log('Parsing Typescript in source files...'); + const app = new Application(); + app.options.addReader(new TSConfigReader()); + app.bootstrap({ entryPoints: options.input }); + const project = app.convert(); + if (project) { + // @ts-expect-error + data = app.serializer.toObject(project).children!; + console.log(`${data.length} items parsed.`); + } +} else { + console.log('Parsing JSDocs in source files...'); + data = jsdoc2md.getTemplateDataSync({ files: options.input }) as (RootTypes & ChildTypes)[]; + console.log(`${data.length} JSDoc items parsed.`); +} const custom: Record = {}; if (options.custom) { diff --git a/packages/docgen/src/interfaces/config.interface.ts b/packages/docgen/src/interfaces/config.interface.ts index fc7b97ba95ec..3a72f94d1610 100644 --- a/packages/docgen/src/interfaces/config.interface.ts +++ b/packages/docgen/src/interfaces/config.interface.ts @@ -1,3 +1,7 @@ export interface Config { + input: string[]; + custom: string; root: string; + output: string; + typescript: boolean; } diff --git a/packages/docgen/src/types/class.ts b/packages/docgen/src/types/class.ts index b740c91c0ff1..a7a50797b56e 100644 --- a/packages/docgen/src/types/class.ts +++ b/packages/docgen/src/types/class.ts @@ -1,4 +1,6 @@ +import { parse } from 'node:path'; import { Collection } from '@discordjs/collection'; +import type { DeclarationReflection } from 'typedoc'; import { DocumentedConstructor } from './constructor.js'; import { DocumentedEvent } from './event.js'; import { DocumentedItemMeta } from './item-meta.js'; @@ -7,8 +9,9 @@ import { DocumentedMember } from './member.js'; import { DocumentedMethod } from './method.js'; import { DocumentedVarType } from './var-type.js'; import type { Class, Config } from '../interfaces/index.js'; +import { parseType } from '../util/parseType.js'; -export class DocumentedClass extends DocumentedItem { +export class DocumentedClass extends DocumentedItem { public readonly props = new Collection(); public readonly methods = new Collection(); @@ -21,15 +24,29 @@ export class DocumentedClass extends DocumentedItem { public implements: DocumentedVarType | null = null; - public constructor(data: Class, config: Config) { + public constructor(data: Class | DeclarationReflection, config: Config) { super(data, config); - if (data.augments) { - this.extends = new DocumentedVarType({ names: data.augments }, this.config); - } + if (config.typescript) { + const d = data as DeclarationReflection; + const extended = d.extendedTypes?.[0]; + if (extended) { + this.extends = new DocumentedVarType({ names: [parseType(extended)] }, this.config); + } + + const implemented = d.implementedTypes?.[0]; + if (implemented) { + this.implements = new DocumentedVarType({ names: [parseType(implemented)] }, this.config); + } + } else { + const d = data as Class; + if (d.augments) { + this.extends = new DocumentedVarType({ names: d.augments }, this.config); + } - if (data.implements) { - this.implements = new DocumentedVarType({ names: data.implements }, this.config); + if (d.implements) { + this.implements = new DocumentedVarType({ names: d.implements }, this.config); + } } } @@ -38,7 +55,9 @@ export class DocumentedClass extends DocumentedItem { if (this.construct) throw new Error(`Doc ${this.data.name} already has constructor`); this.construct = item; } else if (item instanceof DocumentedMethod) { - const prefix = item.data.scope === 'static' ? 's-' : ''; + // @ts-expect-error + // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access + const prefix = item.data.scope === 'static' || item.data.flags?.isStatic ? 's-' : ''; if (this.methods.has(prefix + item.data.name)) { throw new Error(`Doc ${this.data.name} already has method ${item.data.name}`); } @@ -57,20 +76,57 @@ export class DocumentedClass extends DocumentedItem { } public override serializer() { + if (this.config.typescript) { + const data = this.data as DeclarationReflection; + let meta; + + const sources = data.sources?.[0]; + if (sources) { + meta = new DocumentedItemMeta(sources, this.config).serialize(); + } + + return { + // @ts-expect-error + // eslint-disable-next-line @typescript-eslint/no-unsafe-argument + name: data.name === 'default' ? parse(meta?.file ?? 'default').name : data.name, + // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition + description: data.comment?.shortText?.trim(), + // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition + see: data.comment?.tags?.filter((t) => t.tagName === 'see').map((t) => t.text.trim()), + extends: this.extends?.serialize(), + implements: this.implements?.serialize(), + access: + // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition + data.flags.isPrivate || data.comment?.tags?.some((t) => t.tagName === 'private' || t.tagName === 'internal') + ? 'private' + : undefined, + // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition + abstract: data.comment?.tags?.some((t) => t.tagName === 'abstract'), + // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition + deprecated: data.comment?.tags?.some((t) => t.tagName === 'deprecated'), + construct: this.construct?.serialize(), + props: this.props.size ? this.props.map((p) => p.serialize()) : undefined, + methods: this.methods.size ? this.methods.map((m) => m.serialize()) : undefined, + events: this.events.size ? this.events.map((e) => e.serialize()) : undefined, + meta, + }; + } + + const data = this.data as Class; return { - name: this.data.name, - description: this.data.description, - see: this.data.see, + name: data.name, + description: data.description, + see: data.see, extends: this.extends?.serialize(), implements: this.implements?.serialize(), - access: this.data.access, - abstract: this.data.virtual, - deprecated: this.data.deprecated, + access: data.access, + abstract: data.virtual, + deprecated: data.deprecated, construct: this.construct?.serialize(), props: this.props.size ? this.props.map((p) => p.serialize()) : undefined, methods: this.methods.size ? this.methods.map((m) => m.serialize()) : undefined, events: this.events.size ? this.events.map((e) => e.serialize()) : undefined, - meta: new DocumentedItemMeta(this.data.meta, this.config).serialize(), + meta: new DocumentedItemMeta(data.meta, this.config).serialize(), }; } } diff --git a/packages/docgen/src/types/constructor.ts b/packages/docgen/src/types/constructor.ts index eb9c029cade5..1329b4ed65d8 100644 --- a/packages/docgen/src/types/constructor.ts +++ b/packages/docgen/src/types/constructor.ts @@ -1,17 +1,40 @@ +import type { DeclarationReflection, SignatureReflection } from 'typedoc'; import { DocumentedItem } from './item.js'; import { DocumentedParam } from './param.js'; import type { Constructor } from '../interfaces/index.js'; -export class DocumentedConstructor extends DocumentedItem { +export class DocumentedConstructor extends DocumentedItem { public override serializer() { + if (this.config.typescript) { + const data = this.data as DeclarationReflection; + const signature = (data.signatures ?? [])[0] ?? data; + + return { + name: data.name, + // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition + description: signature.comment?.shortText?.trim(), + // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition + see: signature.comment?.tags?.filter((t) => t.tagName === 'see').map((t) => t.text.trim()), + access: + data.flags.isPrivate || + // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition + signature.comment?.tags?.some((t) => t.tagName === 'private' || t.tagName === 'internal') + ? 'private' + : undefined, + // @ts-expect-error + params: signature.parameters + ? (signature as SignatureReflection).parameters?.map((p) => new DocumentedParam(p, this.config)) + : undefined, + }; + } + + const data = this.data as Constructor; return { - name: this.data.name, - description: this.data.description, - see: this.data.see, - access: this.data.access, - params: this.data.params?.length - ? this.data.params.map((p) => new DocumentedParam(p, this.config).serialize()) - : undefined, + name: data.name, + description: data.description, + see: data.see, + access: data.access, + params: data.params?.length ? data.params.map((p) => new DocumentedParam(p, this.config).serialize()) : undefined, }; } } diff --git a/packages/docgen/src/types/event.ts b/packages/docgen/src/types/event.ts index 25ca0fddff37..678ea0e9d542 100644 --- a/packages/docgen/src/types/event.ts +++ b/packages/docgen/src/types/event.ts @@ -1,19 +1,45 @@ +import type { DeclarationReflection, SignatureReflection } from 'typedoc'; import { DocumentedItemMeta } from './item-meta.js'; import { DocumentedItem } from './item.js'; import { DocumentedParam } from './param.js'; import type { Event } from '../interfaces/index.js'; -export class DocumentedEvent extends DocumentedItem { +export class DocumentedEvent extends DocumentedItem { public override serializer() { + if (this.config.typescript) { + const data = this.data as DeclarationReflection; + const signature = (data.signatures ?? [])[0] ?? data; + let meta; + + const sources = data.sources?.[0]; + if (sources) { + meta = new DocumentedItemMeta(sources, this.config).serialize(); + } + + return { + name: data.name, + // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition + description: signature.comment?.shortText?.trim(), + // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition + see: signature.comment?.tags?.filter((t) => t.tagName === 'see').map((t) => t.text.trim()), + // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition + deprecated: signature.comment?.tags?.some((t) => t.tagName === 'deprecated'), + // @ts-expect-error + params: signature.parameters + ? (signature as SignatureReflection).parameters?.map((p) => new DocumentedParam(p, this.config)) + : undefined, + meta, + }; + } + + const data = this.data as Event; return { - name: this.data.name, - description: this.data.description, - see: this.data.see, - deprecated: this.data.deprecated, - params: this.data.params?.length - ? this.data.params.map((p) => new DocumentedParam(p, this.config).serialize()) - : undefined, - meta: new DocumentedItemMeta(this.data.meta, this.config).serialize(), + name: data.name, + description: data.description, + see: data.see, + deprecated: data.deprecated, + params: data.params?.length ? data.params.map((p) => new DocumentedParam(p, this.config).serialize()) : undefined, + meta: new DocumentedItemMeta(data.meta, this.config).serialize(), }; } } diff --git a/packages/docgen/src/types/item-meta.ts b/packages/docgen/src/types/item-meta.ts index 48f7a12155ef..28a31abf561a 100644 --- a/packages/docgen/src/types/item-meta.ts +++ b/packages/docgen/src/types/item-meta.ts @@ -1,13 +1,25 @@ -import { relative } from 'node:path'; +import { basename, dirname, relative } from 'node:path'; +import type { SourceReference } from 'typedoc'; import { DocumentedItem } from './item.js'; import type { Meta } from '../interfaces/index.js'; -export class DocumentedItemMeta extends DocumentedItem { +export class DocumentedItemMeta extends DocumentedItem { public override serializer() { + if (this.config.typescript) { + const data = this.data as SourceReference; + + return { + line: data.line, + file: basename(data.fileName), + path: dirname(data.fileName), + }; + } + + const data = this.data as Meta; return { - line: this.data.lineno, - file: this.data.filename, - path: relative(this.config.root, this.data.path).replace(/\\/g, '/'), + line: data.lineno, + file: data.filename, + path: relative(this.config.root, data.path).replace(/\\/g, '/'), }; } } diff --git a/packages/docgen/src/types/item.ts b/packages/docgen/src/types/item.ts index 5814440f953f..25fdd92e0521 100644 --- a/packages/docgen/src/types/item.ts +++ b/packages/docgen/src/types/item.ts @@ -1,9 +1,10 @@ +import type { DeclarationReflection } from 'typedoc'; import type { Config, Item } from '../interfaces/index.js'; -export class DocumentedItem { +export class DocumentedItem { public constructor(public readonly data: T, public readonly config: Config) {} - public serialize() { + public serialize(): unknown { try { return this.serializer(); } catch (err) { diff --git a/packages/docgen/src/types/member.ts b/packages/docgen/src/types/member.ts index 0c82c14523df..6717e88b5a32 100644 --- a/packages/docgen/src/types/member.ts +++ b/packages/docgen/src/types/member.ts @@ -1,27 +1,101 @@ +import type { DeclarationReflection } from 'typedoc'; import { DocumentedItemMeta } from './item-meta.js'; import { DocumentedItem } from './item.js'; import { DocumentedParam } from './param.js'; import { DocumentedVarType } from './var-type.js'; import type { Member } from '../interfaces/index.js'; +import { parseType } from '../util/parseType.js'; -export class DocumentedMember extends DocumentedItem { +export class DocumentedMember extends DocumentedItem { public override serializer() { + if (this.config.typescript) { + const data = this.data as DeclarationReflection; + let meta; + + const sources = data.sources?.[0]; + if (sources) { + meta = new DocumentedItemMeta(sources, this.config).serialize(); + } + + const base = { + name: data.name, + // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition + description: data.comment?.shortText?.trim(), + // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition + see: data.comment?.tags?.filter((t) => t.tagName === 'see').map((t) => t.text.trim()), + scope: data.flags.isStatic ? 'static' : undefined, + access: + // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition + data.flags.isPrivate || data.comment?.tags?.some((t) => t.tagName === 'private' || t.tagName === 'internal') + ? 'private' + : undefined, + readonly: data.flags.isReadonly, + // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition + abstract: data.comment?.tags?.some((t) => t.tagName === 'abstract'), + // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition + deprecated: data.comment?.tags?.some((t) => t.tagName === 'deprecated'), + default: + // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition + data.comment?.tags?.find((t) => t.tagName === 'default')?.text.trim() ?? + (data.defaultValue === '...' ? undefined : data.defaultValue), + type: data.type ? new DocumentedVarType({ names: [parseType(data.type)] }, this.config).serialize() : undefined, + meta, + }; + + if (data.kindString === 'Accessor') { + const getter = data.getSignature; + const hasSetter = data.setSignature; + + if (!getter) { + throw new Error("Can't parse accessor without getter."); + } + + if (!hasSetter) base.readonly = true; + + return { + ...base, + // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition + description: getter.comment?.shortText?.trim(), + // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition + see: getter.comment?.tags?.filter((t) => t.tagName === 'see').map((t) => t.text.trim()), + access: + getter.flags.isPrivate || + getter.comment?.tags.some((t) => t.tagName === 'private' || t.tagName === 'internal') + ? 'private' + : undefined, + readonly: base.readonly || !hasSetter, + abstract: getter.comment?.tags.some((t) => t.tagName === 'abstract'), + deprecated: getter.comment?.tags.some((t) => t.tagName === 'deprecated'), + // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment + default: + base.default ?? + getter.comment?.tags.find((t) => t.tagName === 'default')?.text.trim() ?? + // @ts-expect-error + getter.defaultValue, + type: getter.type ? parseType(getter.type) : undefined, + }; + } + + return base; + } + + const data = this.data as Member; return { - name: this.data.name, - description: this.data.description, - see: this.data.see, - scope: this.data.scope, - access: this.data.access, - readonly: this.data.readonly, - nullable: this.data.nullable, - abstract: this.data.virtual, - deprecated: this.data.deprecated, - default: this.data.default, - type: new DocumentedVarType(this.data.type, this.config).serialize(), - props: this.data.properties?.length - ? this.data.properties.map((p) => new DocumentedParam(p, this.config).serialize()) + name: data.name, + description: data.description, + see: data.see, + scope: data.scope, + access: data.access, + readonly: data.readonly, + nullable: data.nullable, + abstract: data.virtual, + deprecated: data.deprecated, + default: data.default, + type: new DocumentedVarType(data.type, this.config).serialize(), + props: data.properties?.length + ? data.properties.map((p) => new DocumentedParam(p, this.config).serialize()) : undefined, - meta: new DocumentedItemMeta(this.data.meta, this.config).serialize(), + meta: new DocumentedItemMeta(data.meta, this.config).serialize(), }; } } diff --git a/packages/docgen/src/types/method.ts b/packages/docgen/src/types/method.ts index c08e7845e2f8..53fa41fa633c 100644 --- a/packages/docgen/src/types/method.ts +++ b/packages/docgen/src/types/method.ts @@ -1,39 +1,86 @@ +import type { DeclarationReflection, SignatureReflection } from 'typedoc'; import { DocumentedItemMeta } from './item-meta.js'; import { DocumentedItem } from './item.js'; import { DocumentedParam } from './param.js'; import { DocumentedVarType } from './var-type.js'; import type { Method } from '../interfaces/index.js'; +import { parseType } from '../util/parseType.js'; -export class DocumentedMethod extends DocumentedItem { +export class DocumentedMethod extends DocumentedItem { public override serializer() { + if (this.config.typescript) { + const data = this.data as DeclarationReflection; + const signature = (data.signatures ?? [])[0] ?? data; + let meta; + + const sources = data.sources?.[0]; + if (sources) { + meta = new DocumentedItemMeta(sources, this.config).serialize(); + } + + return { + name: data.name, + // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition + description: signature.comment?.shortText?.trim(), + // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition + see: signature.comment?.tags?.filter((t) => t.tagName === 'see').map((t) => t.text.trim()), + scope: data.flags.isStatic ? 'static' : undefined, + access: + data.flags.isPrivate || + // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition + signature.comment?.tags?.some((t) => t.tagName === 'private' || t.tagName === 'internal') + ? 'private' + : undefined, + // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition + examples: signature.comment?.tags?.filter((t) => t.tagName === 'example').map((t) => t.text.trim()), + // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition + abstract: signature.comment?.tags?.some((t) => t.tagName === 'abstract'), + // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition + deprecated: signature.comment?.tags?.some((t) => t.tagName === 'deprecated'), + // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition + emits: signature.comment?.tags?.filter((t) => t.tagName === 'emits').map((t) => t.text.trim()), + // @ts-expect-error + params: signature.parameters + ? (signature as SignatureReflection).parameters?.map((p) => new DocumentedParam(p, this.config)) + : undefined, + returns: signature.type + ? new DocumentedVarType( + { names: [parseType(signature.type)], description: signature.comment?.returns?.trim() }, + this.config, + ).serialize() + : undefined, + returnsDescription: signature.comment?.returns?.trim(), + meta, + }; + } + + const data = this.data as Method; return { - name: this.data.name, - description: this.data.description, - see: this.data.see, - scope: this.data.scope, - access: this.data.access, - inherits: this.data.inherits, - inherited: this.data.inherited, - implements: this.data.implements, - examples: this.data.examples, - abstract: this.data.virtual && !this.data.inherited, - deprecated: this.data.deprecated, - emits: this.data.fires, - throws: this.data.exceptions, - params: this.data.params?.length - ? this.data.params.map((p) => new DocumentedParam(p, this.config).serialize()) - : undefined, - async: this.data.async, - generator: this.data.generator, - returns: this.data.returns?.length - ? this.data.returns.map((p) => + name: data.name, + description: data.description, + see: data.see, + scope: data.scope, + access: data.access, + inherits: data.inherits, + inherited: data.inherited, + implements: data.implements, + examples: data.examples, + abstract: data.virtual && !data.inherited, + deprecated: data.deprecated, + emits: data.fires, + throws: data.exceptions, + params: data.params?.length ? data.params.map((p) => new DocumentedParam(p, this.config).serialize()) : undefined, + async: data.async, + generator: data.generator, + returns: data.returns?.length + ? data.returns.map((p) => new DocumentedVarType( { names: p.type.names, description: p.description, nullable: p.nullable }, this.config, ).serialize(), ) : undefined, - meta: new DocumentedItemMeta(this.data.meta, this.config).serialize(), + meta: new DocumentedItemMeta(data.meta, this.config).serialize(), }; } } diff --git a/packages/docgen/src/types/param.ts b/packages/docgen/src/types/param.ts index 86f7e6908729..1a66936c6967 100644 --- a/packages/docgen/src/types/param.ts +++ b/packages/docgen/src/types/param.ts @@ -1,17 +1,37 @@ +import type { ParameterReflection } from 'typedoc'; import { DocumentedItem } from './item.js'; import { DocumentedVarType } from './var-type.js'; import type { Param } from '../interfaces/index.js'; +import { parseType } from '../util/parseType.js'; -export class DocumentedParam extends DocumentedItem { +export class DocumentedParam extends DocumentedItem { public override serializer() { + if (this.config.typescript) { + const data = this.data as ParameterReflection; + + return { + name: data.name, + // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition + description: data.comment?.shortText?.trim() ?? data.comment?.text.trim(), + optional: data.flags.isOptional || typeof data.defaultValue != 'undefined', + default: + // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition + data.comment?.tags?.find((t) => t.tagName === 'default')?.text.trim() ?? + (data.defaultValue === '...' ? undefined : data.defaultValue), + type: data.type ? new DocumentedVarType({ names: [parseType(data.type)] }, this.config).serialize() : undefined, + variable: data.flags.isRest, + }; + } + + const data = this.data as Param; return { - name: this.data.name, - description: this.data.description, - optional: this.data.optional, - default: this.data.defaultvalue, - variable: this.data.variable, - nullable: this.data.nullable, - type: new DocumentedVarType(this.data.type, this.config).serialize(), + name: data.name, + description: data.description, + optional: data.optional, + default: data.defaultvalue, + variable: data.variable, + nullable: data.nullable, + type: new DocumentedVarType(data.type, this.config).serialize(), }; } } diff --git a/packages/docgen/src/types/typedef.ts b/packages/docgen/src/types/typedef.ts index d7abca9c1aa0..2164b472b380 100644 --- a/packages/docgen/src/types/typedef.ts +++ b/packages/docgen/src/types/typedef.ts @@ -1,28 +1,156 @@ +import type { DeclarationReflection } from 'typedoc'; import { DocumentedItemMeta } from './item-meta.js'; import { DocumentedItem } from './item.js'; import { DocumentedParam } from './param.js'; import { DocumentedVarType } from './var-type.js'; import type { Typedef } from '../interfaces/index.js'; +import { parseType } from '../util/parseType.js'; +import { isReflectionType } from '../util/types.js'; -export class DocumentedTypeDef extends DocumentedItem { +export class DocumentedTypeDef extends DocumentedItem { public override serializer() { + if (this.config.typescript) { + const data = this.data as DeclarationReflection; + let meta; + + const sources = data.sources?.[0]; + if (sources) { + meta = new DocumentedItemMeta(sources, this.config).serialize(); + } + + const baseReturn = { + name: data.name, + // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition + description: data.comment?.shortText?.trim(), + // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition + see: data.comment?.tags?.filter((t) => t.tagName === 'see').map((t) => t.text.trim()), + access: + // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition + data.flags.isPrivate || data.comment?.tags?.some((t) => t.tagName === 'private' || t.tagName === 'internal') + ? 'private' + : undefined, + // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition + deprecated: data.comment?.tags?.some((t) => t.tagName === 'deprecated'), + type: data.type ? new DocumentedVarType({ names: [parseType(data.type)] }, this.config).serialize() : undefined, + meta, + }; + + let typeDef: DeclarationReflection | undefined; + if (isReflectionType(data.type)) { + typeDef = data.type.declaration; + } else if (data.kindString === 'Interface') { + typeDef = data; + } else if (data.kindString === 'Enumeration') { + return { + ...baseReturn, + props: data.children?.length + ? data.children.map((child) => ({ + name: child.name, + description: child.comment?.shortText.trim(), + type: typeof child.defaultValue == 'undefined' ? undefined : [[[child.defaultValue]]], + })) + : undefined, + }; + } + + if (typeDef) { + const { children, signatures } = typeDef; + + if (children && children.length > 0) { + const props = children.map((child) => ({ + name: child.name, + // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition + description: child.comment?.shortText?.trim() ?? child.signatures?.[0]?.comment?.shortText?.trim(), + optional: child.flags.isOptional || typeof child.defaultValue != 'undefined', + default: + // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition + child.comment?.tags?.find((t) => t.tagName === 'default')?.text.trim() ?? + (child.defaultValue === '...' ? undefined : child.defaultValue), + type: child.type + ? new DocumentedVarType({ names: [parseType(child.type)] }, this.config).serialize() + : child.kindString === 'Method' + ? new DocumentedVarType( + { + names: [ + parseType({ + type: 'reflection', + declaration: child, + }), + ], + }, + this.config, + ).serialize() + : undefined, + })); + + return { + ...baseReturn, + props, + }; + } + + if (signatures && signatures.length > 0) { + const sig = signatures[0]; + + const params = sig?.parameters?.map((param) => ({ + name: param.name, + // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition + description: param.comment?.shortText?.trim(), + optional: param.flags.isOptional || typeof param.defaultValue != 'undefined', + default: + // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition + param.comment?.tags?.find((t) => t.tagName === 'default')?.text.trim() ?? + (param.defaultValue === '...' ? undefined : param.defaultValue), + type: param.type + ? new DocumentedVarType({ names: [parseType(param.type)] }, this.config).serialize() + : undefined, + })); + + return { + ...baseReturn, + // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition + description: sig?.comment?.shortText?.trim(), + // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition + see: sig?.comment?.tags?.filter((t) => t.tagName === 'see').map((t) => t.text.trim()), + access: + sig?.flags.isPrivate || + // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition + sig?.comment?.tags?.some((t) => t.tagName === 'private' || t.tagName === 'internal') + ? 'private' + : undefined, + // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition + deprecated: sig?.comment?.tags?.some((t) => t.tagName === 'deprecated'), + params, + returns: sig?.type + ? new DocumentedVarType( + { names: [parseType(sig.type)], description: sig.comment?.returns?.trim() }, + this.config, + ).serialize() + : undefined, + returnsDescription: sig?.comment?.returns?.trim(), + }; + } + } + + return baseReturn; + } + + const data = this.data as Typedef; return { - name: this.data.name, - description: this.data.description, - see: this.data.see, - access: this.data.access, - deprecated: this.data.deprecated, - type: new DocumentedVarType(this.data.type, this.config).serialize(), - props: this.data.properties?.length - ? this.data.properties.map((p) => new DocumentedParam(p, this.config).serialize()) - : undefined, - params: this.data.params?.length - ? this.data.params.map((p) => new DocumentedParam(p, this.config).serialize()) + name: data.name, + description: data.description, + see: data.see, + access: data.access, + deprecated: data.deprecated, + type: new DocumentedVarType(data.type, this.config).serialize(), + props: data.properties?.length + ? data.properties.map((p) => new DocumentedParam(p, this.config).serialize()) : undefined, - returns: this.data.returns?.length - ? this.data.returns.map((p) => new DocumentedVarType(p, this.config).serialize()) + params: data.params?.length ? data.params.map((p) => new DocumentedParam(p, this.config).serialize()) : undefined, + returns: data.returns?.length + ? data.returns.map((p) => new DocumentedVarType(p, this.config).serialize()) : undefined, - meta: new DocumentedItemMeta(this.data.meta, this.config).serialize(), + meta: new DocumentedItemMeta(data.meta, this.config).serialize(), }; } } diff --git a/packages/docgen/src/types/var-type.ts b/packages/docgen/src/types/var-type.ts index 40933216d6d2..963cccbc123e 100644 --- a/packages/docgen/src/types/var-type.ts +++ b/packages/docgen/src/types/var-type.ts @@ -1,47 +1,36 @@ import { DocumentedItem } from './item.js'; import type { VarType } from '../interfaces/index.js'; +import { parseType } from '../util/parseType.js'; +import { splitVarName } from '../util/splitVarName.js'; export class DocumentedVarType extends DocumentedItem { public override serializer() { - const names = this.data.names?.map((name) => this.splitVarName(name)); + if (this.config.typescript) { + const data = this.data; + const names = data.names?.map((name) => splitVarName(parseType(name))); - if (!this.data.description && !this.data.nullable) { + if (!data.description && !data.nullable) { + return names; + } + + return { + types: names, + description: data.description, + nullable: data.nullable, + }; + } + + const data = this.data; + const names = data.names?.map((name) => splitVarName(name)); + + if (!data.description && !data.nullable) { return names; } return { types: names, - description: this.data.description, - nullable: this.data.nullable, + description: data.description, + nullable: data.nullable, }; } - - private splitVarName(str: string) { - const res: string[][] = []; - let currGroup: string[] = []; - let currStr = ''; - - const isASymbol = (char: string) => '-!$%^&*()_+|~=`{}[]:;<>?, '.includes(char); - - for (const char of str) { - const currentlyInASymbolSection = isASymbol(currStr[0]!); - const charIsASymbol = isASymbol(char); - - if (currStr.length && currentlyInASymbolSection !== charIsASymbol) { - currGroup.push(currStr); - currStr = char; - - if (!charIsASymbol) { - res.push(currGroup); - currGroup = []; - } - } else { - currStr += char; - } - } - currGroup.push(currStr); - res.push(currGroup); - - return res; - } } diff --git a/packages/docgen/src/util/parseType.ts b/packages/docgen/src/util/parseType.ts new file mode 100644 index 000000000000..e75a5974ea76 --- /dev/null +++ b/packages/docgen/src/util/parseType.ts @@ -0,0 +1,112 @@ +import type { JSONOutput } from 'typedoc'; +import { + isArrayType, + isConditionalType, + isIndexedAccessType, + isIntersectionType, + isPredicateType, + isReferenceType, + isReflectionType, + isLiteralType, + isTupleType, + isTypeOperatorType, + isUnionType, + isQueryType, + isInferredType, + isIntrinsicType, + isUnknownType, +} from './types'; + +export function parseType(t: JSONOutput.SomeType | JSONOutput.Type | string): string { + if (typeof t === 'string') { + return t; + } + + if (isArrayType(t)) { + return `Array<${parseType(t.elementType)}>`; + } + + if (isConditionalType(t)) { + const { checkType, extendsType, trueType, falseType } = t; + return `${parseType(checkType)} extends ${parseType(extendsType)} ? ${parseType(trueType)} : ${parseType( + falseType, + )}`; + } + + if (isIndexedAccessType(t)) { + return `${parseType(t.objectType)}[${parseType(t.indexType)}]`; + } + + if (isIntersectionType(t)) { + return t.types.map(parseType).join(' & '); + } + + if (isPredicateType(t)) { + return (t.asserts ? 'asserts ' : '') + t.name + (t.targetType ? ` is ${parseType(t.targetType)}` : ''); + } + + if (isReferenceType(t)) { + return t.name + (t.typeArguments ? `<${t.typeArguments.map(parseType).join(', ')}>` : ''); + } + + if (isReflectionType(t)) { + const obj: Record = {}; + + const { children, signatures } = t.declaration!; + + // This is run when we're parsing interface-like declaration + if (children && children.length > 0) { + for (const child of children) { + const { type } = child; + if (type) { + obj[child.name] = parseType(type); + } + } + return `{\n${Object.entries(obj) + .map(([key, value]) => `${key}: ${value as string}`) + .join(',\n')}\n}`; + } + + // This is run if we're parsing a function type + if (signatures && signatures.length > 0) { + const s = signatures[0]; + const params = s?.parameters?.map((p) => `${p.name}: ${p.type ? parseType(p.type) : 'unknown'}`); + return `(${params?.join(', ') ?? '...args: unknown[]'}) => ${s?.type ? parseType(s.type) : 'unknown'}`; + } + + return '{}'; + } + + if (isLiteralType(t)) { + if (typeof t.value == 'string') { + return `'${t.value}'`; + } + // eslint-disable-next-line @typescript-eslint/restrict-template-expressions + return `${t.value}`; + } + + if (isTupleType(t)) { + return `[${(t.elements ?? []).map(parseType).join(', ')}]`; + } + + if (isTypeOperatorType(t)) { + return `${t.operator} ${parseType(t.target)}`; + } + + if (isUnionType(t)) { + return t.types + .map(parseType) + .filter((s) => Boolean(s) && s.trim().length > 0) + .join(' | '); + } + + if (isQueryType(t)) { + return `(typeof ${parseType(t.queryType)})`; + } + + if (isInferredType(t) || isIntrinsicType(t) || isUnknownType(t)) { + return t.name; + } + + return 'unknown'; +} diff --git a/packages/docgen/src/util/splitVarName.ts b/packages/docgen/src/util/splitVarName.ts new file mode 100644 index 000000000000..37a47e452f11 --- /dev/null +++ b/packages/docgen/src/util/splitVarName.ts @@ -0,0 +1,28 @@ +export function splitVarName(str: string) { + const res: string[][] = []; + let currGroup: string[] = []; + let currStr = ''; + + const isASymbol = (char: string) => '-!$%^&*()_+|~=`{}[]:;<>?, '.includes(char); + + for (const char of str) { + const currentlyInASymbolSection = isASymbol(currStr[0]!); + const charIsASymbol = isASymbol(char); + + if (currStr.length && currentlyInASymbolSection !== charIsASymbol) { + currGroup.push(currStr); + currStr = char; + + if (!charIsASymbol) { + res.push(currGroup); + currGroup = []; + } + } else { + currStr += char; + } + } + currGroup.push(currStr); + res.push(currGroup); + + return res; +} diff --git a/packages/docgen/src/util/types.ts b/packages/docgen/src/util/types.ts new file mode 100644 index 000000000000..e9a3fc83611d --- /dev/null +++ b/packages/docgen/src/util/types.ts @@ -0,0 +1,67 @@ +/* eslint-disable @typescript-eslint/no-unsafe-member-access */ +import type { JSONOutput } from 'typedoc'; + +interface QueryType { + type: 'query'; + queryType: JSONOutput.SomeType; +} + +export function isArrayType(value: any): value is JSONOutput.ArrayType { + return typeof value == 'object' && value.type === 'array'; +} + +export function isConditionalType(value: any): value is JSONOutput.ConditionalType { + return typeof value == 'object' && value.type === 'conditional'; +} + +export function isIndexedAccessType(value: any): value is JSONOutput.IndexedAccessType { + return typeof value == 'object' && value.type === 'indexedAccess'; +} + +export function isInferredType(value: any): value is JSONOutput.InferredType { + return typeof value == 'object' && value.type === 'inferred'; +} + +export function isIntersectionType(value: any): value is JSONOutput.IntersectionType { + return typeof value == 'object' && value.type === 'intersection'; +} + +export function isIntrinsicType(value: any): value is JSONOutput.IntrinsicType { + return typeof value == 'object' && value.type === 'intrinsic'; +} + +export function isPredicateType(value: any): value is JSONOutput.PredicateType { + return typeof value == 'object' && value.type === 'predicate'; +} + +export function isReferenceType(value: any): value is JSONOutput.ReferenceType { + return typeof value == 'object' && value.type === 'reference'; +} + +export function isReflectionType(value: any): value is JSONOutput.ReflectionType { + return typeof value == 'object' && value.type === 'reflection'; +} + +export function isLiteralType(value: any): value is JSONOutput.LiteralType { + return typeof value == 'object' && value.type === 'literal'; +} + +export function isTupleType(value: any): value is JSONOutput.TupleType { + return typeof value == 'object' && value.type === 'tuple'; +} + +export function isTypeOperatorType(value: any): value is JSONOutput.TypeOperatorType { + return typeof value == 'object' && value.type === 'typeOperator'; +} + +export function isUnionType(value: any): value is JSONOutput.UnionType { + return typeof value == 'object' && value.type === 'union'; +} + +export function isUnknownType(value: any): value is JSONOutput.UnknownType { + return typeof value == 'object' && value.type === 'unknown'; +} + +export function isQueryType(value: any): value is QueryType { + return typeof value == 'object' && value.type === 'query'; +} diff --git a/packages/proxy/.gitignore b/packages/proxy/.gitignore index a1f7b02f5d76..86b93e929ae6 100644 --- a/packages/proxy/.gitignore +++ b/packages/proxy/.gitignore @@ -18,7 +18,7 @@ pids dist/ typings/ docs/**/* -!docs/index.yml +!docs/index.json !docs/README.md # Miscellaneous diff --git a/packages/proxy/docs/index.json b/packages/proxy/docs/index.json new file mode 100644 index 000000000000..557341ae9dff --- /dev/null +++ b/packages/proxy/docs/index.json @@ -0,0 +1 @@ +[{ "name": "General", "files": [{ "name": "Welcome", "id": "welcome", "path": "../../README.md" }] }] diff --git a/packages/proxy/docs/index.yml b/packages/proxy/docs/index.yml deleted file mode 100644 index 2c993519e6e4..000000000000 --- a/packages/proxy/docs/index.yml +++ /dev/null @@ -1,5 +0,0 @@ -- name: General - files: - - name: Welcome - id: welcome - path: ../../README.md diff --git a/packages/proxy/package.json b/packages/proxy/package.json index 103cee12005c..6fdd93b3f1a5 100644 --- a/packages/proxy/package.json +++ b/packages/proxy/package.json @@ -6,7 +6,7 @@ "build": "tsup && tsc --emitDeclarationOnly --incremental", "lint": "prettier --check . && eslint src __tests__ --ext mjs,js,ts", "format": "prettier --write . && eslint src __tests__ --ext mjs,js,ts --fix", - "docs": "typedoc --json docs/typedoc-out.json src/index.ts && ts-docgen -i docs/typedoc-out.json -c docs/index.yml -o docs/docs.json", + "docs": "docgen -i src/index.ts -c docs/index.json -o docs/docs.json --typescript", "prepublishOnly": "yarn build && yarn lint && yarn test", "changelog": "git cliff --prepend ./CHANGELOG.md -u -c ./cliff.toml -r ../../ --include-path 'packages/proxy/*'", "release": "cliff-jumper" @@ -57,6 +57,7 @@ "undici": "^5.4.0" }, "devDependencies": { + "@discordjs/docgen": "workspace:^", "@discordjs/scripts": "workspace:^", "@favware/cliff-jumper": "^1.8.3", "@types/node": "^16.11.38", @@ -70,7 +71,6 @@ "prettier": "^2.6.2", "supertest": "^6.2.3", "tsup": "^6.1.0", - "typedoc": "^0.22.17", "typescript": "^4.7.3" }, "engines": { diff --git a/packages/rest/.gitignore b/packages/rest/.gitignore index a1f7b02f5d76..86b93e929ae6 100644 --- a/packages/rest/.gitignore +++ b/packages/rest/.gitignore @@ -18,7 +18,7 @@ pids dist/ typings/ docs/**/* -!docs/index.yml +!docs/index.json !docs/README.md # Miscellaneous diff --git a/packages/rest/docs/index.json b/packages/rest/docs/index.json new file mode 100644 index 000000000000..557341ae9dff --- /dev/null +++ b/packages/rest/docs/index.json @@ -0,0 +1 @@ +[{ "name": "General", "files": [{ "name": "Welcome", "id": "welcome", "path": "../../README.md" }] }] diff --git a/packages/rest/docs/index.yml b/packages/rest/docs/index.yml deleted file mode 100644 index 2c993519e6e4..000000000000 --- a/packages/rest/docs/index.yml +++ /dev/null @@ -1,5 +0,0 @@ -- name: General - files: - - name: Welcome - id: welcome - path: ../../README.md diff --git a/packages/rest/package.json b/packages/rest/package.json index 741f2a482969..3b4af9c3d9ab 100644 --- a/packages/rest/package.json +++ b/packages/rest/package.json @@ -6,7 +6,7 @@ "build": "tsup && tsc --emitDeclarationOnly --incremental", "lint": "prettier --check . && eslint src __tests__ --ext mjs,js,ts", "format": "prettier --write . && eslint src __tests__ --ext mjs,js,ts --fix", - "docs": "typedoc --json docs/typedoc-out.json src/index.ts && ts-docgen -i docs/typedoc-out.json -c docs/index.yml -o docs/docs.json", + "docs": "docgen -i src/index.ts -c docs/index.json -o docs/docs.json --typescript", "prepublishOnly": "yarn build && yarn lint && yarn test", "changelog": "git cliff --prepend ./CHANGELOG.md -u -c ./cliff.toml -r ../../ --include-path 'packages/rest/*'", "release": "cliff-jumper" @@ -58,6 +58,7 @@ "undici": "^5.4.0" }, "devDependencies": { + "@discordjs/docgen": "workspace:^", "@discordjs/scripts": "workspace:^", "@favware/cliff-jumper": "^1.8.3", "@typescript-eslint/eslint-plugin": "^5.27.1", @@ -68,7 +69,6 @@ "eslint-plugin-import": "^2.26.0", "prettier": "^2.6.2", "tsup": "^6.1.0", - "typedoc": "^0.22.17", "typescript": "^4.7.3" }, "engines": { diff --git a/packages/scripts/package.json b/packages/scripts/package.json index 1f9efecd4ebb..8d9a5154b639 100644 --- a/packages/scripts/package.json +++ b/packages/scripts/package.json @@ -8,9 +8,6 @@ "lint": "prettier --check . && eslint src --ext mjs,js,ts", "format": "prettier --write . && eslint src --ext mjs,js,ts --fix" }, - "bin": { - "ts-docgen": "./dist/docs.js" - }, "main": "./dist/index.js", "module": "./dist/index.mjs", "types": "./dist/index.d.ts", @@ -45,7 +42,6 @@ }, "homepage": "https://discord.js.org", "dependencies": { - "@discordjs/ts-docgen": "^0.4.1", "commander": "^9.3.0", "tslib": "^2.4.0" }, diff --git a/packages/scripts/src/docs.ts b/packages/scripts/src/docs.ts deleted file mode 100644 index 6f5e07b1967b..000000000000 --- a/packages/scripts/src/docs.ts +++ /dev/null @@ -1,25 +0,0 @@ -#!/usr/bin/env node -import { runGenerator } from '@discordjs/ts-docgen'; -import { createCommand } from 'commander'; -import packageFile from '../package.json'; - -interface CLIOptions { - input: string; - custom: string; - output: string; -} - -const command = createCommand() - .version(packageFile.version) - .option('-i, --input ', 'Path to an existing TypeDoc JSON output file') - .option('-c, --custom ', 'Custom docs definition file to use') - .option('-o, --output ', 'Path to output file'); - -const program = command.parse(process.argv); -const options = program.opts(); - -runGenerator({ - existingOutput: options.input, - custom: options.custom, - output: options.output, -}); diff --git a/packages/scripts/src/index.ts b/packages/scripts/src/index.ts new file mode 100644 index 000000000000..cb0ff5c3b541 --- /dev/null +++ b/packages/scripts/src/index.ts @@ -0,0 +1 @@ +export {}; diff --git a/packages/scripts/tsup.config.ts b/packages/scripts/tsup.config.ts index c61d9fad96dd..98646f835822 100644 --- a/packages/scripts/tsup.config.ts +++ b/packages/scripts/tsup.config.ts @@ -1,6 +1,5 @@ import { createTsupConfig } from '../../tsup.config'; export default createTsupConfig({ - entry: ['src/docs.ts'], minify: true, }); diff --git a/packages/voice/.gitignore b/packages/voice/.gitignore index 94c4e922342e..0d090a04fe0f 100644 --- a/packages/voice/.gitignore +++ b/packages/voice/.gitignore @@ -14,8 +14,9 @@ pids # Dist dist/ typings/ + docs/**/* -!docs/index.yml +!docs/index.json !docs/README.md # Miscellaneous diff --git a/packages/voice/docs/index.json b/packages/voice/docs/index.json new file mode 100644 index 000000000000..557341ae9dff --- /dev/null +++ b/packages/voice/docs/index.json @@ -0,0 +1 @@ +[{ "name": "General", "files": [{ "name": "Welcome", "id": "welcome", "path": "../../README.md" }] }] diff --git a/packages/voice/docs/index.yml b/packages/voice/docs/index.yml deleted file mode 100644 index 2c993519e6e4..000000000000 --- a/packages/voice/docs/index.yml +++ /dev/null @@ -1,5 +0,0 @@ -- name: General - files: - - name: Welcome - id: welcome - path: ../../README.md diff --git a/packages/voice/package.json b/packages/voice/package.json index de32fafefc6d..8ca3d905c3f3 100644 --- a/packages/voice/package.json +++ b/packages/voice/package.json @@ -7,7 +7,7 @@ "test": "jest --pass-with-no-tests --collect-coverage", "lint": "prettier --check . && eslint src __tests__ --ext mjs,js,ts", "format": "prettier --write . && eslint src __tests__ --ext mjs,js,ts --fix", - "docs": "typedoc --json docs/typedoc-out.json src/index.ts && ts-docgen -i docs/typedoc-out.json -c docs/index.yml -o docs/docs.json", + "docs": "docgen -i src/index.ts -c docs/index.json -o docs/docs.json --typescript", "prepublishOnly": "yarn build && yarn lint && yarn test", "changelog": "git cliff --prepend ./CHANGELOG.md -u -c ./cliff.toml -r ../../ --include-path 'packages/voice/*'", "release": "cliff-jumper" @@ -62,6 +62,7 @@ "@babel/core": "^7.18.2", "@babel/preset-env": "^7.18.2", "@babel/preset-typescript": "^7.17.12", + "@discordjs/docgen": "workspace:^", "@discordjs/scripts": "workspace:^", "@favware/cliff-jumper": "^1.8.3", "@types/jest": "^28.1.1", @@ -78,7 +79,6 @@ "prettier": "^2.6.2", "tsup": "^6.1.0", "tweetnacl": "^1.0.3", - "typedoc": "^0.22.17", "typescript": "^4.7.3" }, "engines": { diff --git a/yarn.lock b/yarn.lock index a6b4580852b7..483062db8b1b 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1608,6 +1608,7 @@ __metadata: version: 0.0.0-use.local resolution: "@discordjs/builders@workspace:packages/builders" dependencies: + "@discordjs/docgen": "workspace:^" "@discordjs/scripts": "workspace:^" "@favware/cliff-jumper": ^1.8.3 "@sapphire/shapeshift": ^3.1.0 @@ -1625,15 +1626,22 @@ __metadata: ts-mixer: ^6.0.1 tslib: ^2.4.0 tsup: ^6.1.0 - typedoc: ^0.22.17 typescript: ^4.7.3 languageName: unknown linkType: soft +"@discordjs/collection@npm:^0.7.0": + version: 0.7.0 + resolution: "@discordjs/collection@npm:0.7.0" + checksum: 141aa35a5433bacba3617b533557b4948388c7b59cdaecee51ccd721c1b9242e50d95bdef53ee2491535a017095f5072ace3c3e9e594193f67a1c5a8a4b7db93 + languageName: node + linkType: hard + "@discordjs/collection@workspace:^, @discordjs/collection@workspace:packages/collection": version: 0.0.0-use.local resolution: "@discordjs/collection@workspace:packages/collection" dependencies: + "@discordjs/docgen": "workspace:^" "@discordjs/scripts": "workspace:^" "@favware/cliff-jumper": ^1.8.3 "@types/node": ^16.11.38 @@ -1645,7 +1653,6 @@ __metadata: eslint-plugin-import: ^2.26.0 prettier: ^2.6.2 tsup: ^6.1.0 - typedoc: ^0.22.17 typescript: ^4.7.3 languageName: unknown linkType: soft @@ -1671,7 +1678,7 @@ __metadata: version: 0.0.0-use.local resolution: "@discordjs/docgen@workspace:packages/docgen" dependencies: - "@discordjs/collection": "workspace:^" + "@discordjs/collection": ^0.7.0 "@favware/cliff-jumper": ^1.8.3 "@types/jsdoc-to-markdown": ^7.0.3 "@types/node": ^16.11.38 @@ -1686,6 +1693,7 @@ __metadata: prettier: ^2.6.2 tslib: ^2.4.0 tsup: ^6.1.0 + typedoc: ^0.22.17 typescript: ^4.7.3 bin: docgen: ./dist/index.js @@ -1696,6 +1704,7 @@ __metadata: version: 0.0.0-use.local resolution: "@discordjs/proxy@workspace:packages/proxy" dependencies: + "@discordjs/docgen": "workspace:^" "@discordjs/rest": "workspace:^" "@discordjs/scripts": "workspace:^" "@favware/cliff-jumper": ^1.8.3 @@ -1711,7 +1720,6 @@ __metadata: supertest: ^6.2.3 tslib: ^2.4.0 tsup: ^6.1.0 - typedoc: ^0.22.17 typescript: ^4.7.3 undici: ^5.4.0 languageName: unknown @@ -1722,6 +1730,7 @@ __metadata: resolution: "@discordjs/rest@workspace:packages/rest" dependencies: "@discordjs/collection": "workspace:^" + "@discordjs/docgen": "workspace:^" "@discordjs/scripts": "workspace:^" "@favware/cliff-jumper": ^1.8.3 "@sapphire/async-queue": ^1.3.1 @@ -1736,7 +1745,6 @@ __metadata: prettier: ^2.6.2 tslib: ^2.4.0 tsup: ^6.1.0 - typedoc: ^0.22.17 typescript: ^4.7.3 undici: ^5.4.0 languageName: unknown @@ -1746,7 +1754,6 @@ __metadata: version: 0.0.0-use.local resolution: "@discordjs/scripts@workspace:packages/scripts" dependencies: - "@discordjs/ts-docgen": ^0.4.1 "@types/node": ^16.11.38 "@typescript-eslint/eslint-plugin": ^5.27.1 "@typescript-eslint/parser": ^5.27.1 @@ -1759,21 +1766,9 @@ __metadata: tslib: ^2.4.0 tsup: ^6.1.0 typescript: ^4.7.3 - bin: - ts-docgen: ./dist/docs.js languageName: unknown linkType: soft -"@discordjs/ts-docgen@npm:^0.4.1": - version: 0.4.1 - resolution: "@discordjs/ts-docgen@npm:0.4.1" - dependencies: - js-yaml: ^4.1.0 - typedoc: ^0.22.15 - checksum: fbb8c7041812ae0130043451501b73d732a05afaec01136f3c877dae08d7fe9bb913c9763b60010b28bbc50c0043354ae01f5bc2626e1a24a767cb337a29a9a9 - languageName: node - linkType: hard - "@discordjs/voice@workspace:packages/voice": version: 0.0.0-use.local resolution: "@discordjs/voice@workspace:packages/voice" @@ -1781,6 +1776,7 @@ __metadata: "@babel/core": ^7.18.2 "@babel/preset-env": ^7.18.2 "@babel/preset-typescript": ^7.17.12 + "@discordjs/docgen": "workspace:^" "@discordjs/scripts": "workspace:^" "@favware/cliff-jumper": ^1.8.3 "@types/jest": ^28.1.1 @@ -1802,7 +1798,6 @@ __metadata: tslib: ^2.4.0 tsup: ^6.1.0 tweetnacl: ^1.0.3 - typedoc: ^0.22.17 typescript: ^4.7.3 ws: ^8.7.0 languageName: unknown @@ -6027,7 +6022,7 @@ dts-critic@latest: languageName: node linkType: hard -"glob@npm:^7.1.1, glob@npm:^7.1.3, glob@npm:^7.1.4, glob@npm:^7.2.0": +"glob@npm:^7.1.1, glob@npm:^7.1.3, glob@npm:^7.1.4": version: 7.2.0 resolution: "glob@npm:7.2.0" dependencies: @@ -10609,23 +10604,6 @@ dts-critic@latest: languageName: node linkType: hard -"typedoc@npm:^0.22.15": - version: 0.22.15 - resolution: "typedoc@npm:0.22.15" - dependencies: - glob: ^7.2.0 - lunr: ^2.3.9 - marked: ^4.0.12 - minimatch: ^5.0.1 - shiki: ^0.10.1 - peerDependencies: - typescript: 4.0.x || 4.1.x || 4.2.x || 4.3.x || 4.4.x || 4.5.x || 4.6.x - bin: - typedoc: bin/typedoc - checksum: 3f5f1cb9288bf811f42df59750c7062a026a23257b38dfe227515a30007a28e3d8139187949fcd19300fd6b2ef76bcdc4cf54549100bff3e000e61bb19958fb2 - languageName: node - linkType: hard - "typedoc@npm:^0.22.17": version: 0.22.17 resolution: "typedoc@npm:0.22.17"