docs-util: support models implemented with DML in typedoc custom plugins (#7847)

- Support generating reference for models implemented with DML
- Support resolving and generating mermaid diagram for relations

The Currency Module was used an example so its reference is generated to showcase the work of this PR.
This commit is contained in:
Shahed Nasser
2024-07-01 07:34:51 +00:00
committed by GitHub
parent b62f23ea00
commit 1f360a3245
59 changed files with 1455 additions and 1005 deletions
@@ -1,12 +1,12 @@
{
"id": 1856,
"id": 185,
"name": "currency-models",
"variant": "project",
"kind": 1,
"flags": {},
"children": [
{
"id": 1857,
"id": 186,
"name": "Currency",
"variant": "declaration",
"kind": 32,
@@ -24,14 +24,14 @@
{
"type": "reflection",
"declaration": {
"id": 1858,
"id": 187,
"name": "__type",
"variant": "declaration",
"kind": 65536,
"flags": {},
"children": [
{
"id": 1859,
"id": 188,
"name": "code",
"variant": "declaration",
"kind": 1024,
@@ -48,7 +48,7 @@
"defaultValue": "..."
},
{
"id": 1860,
"id": 189,
"name": "symbol",
"variant": "declaration",
"kind": 1024,
@@ -65,7 +65,7 @@
"defaultValue": "..."
},
{
"id": 1861,
"id": 190,
"name": "symbol_native",
"variant": "declaration",
"kind": 1024,
@@ -82,7 +82,7 @@
"defaultValue": "..."
},
{
"id": 1862,
"id": 191,
"name": "name",
"variant": "declaration",
"kind": 1024,
@@ -99,7 +99,7 @@
"defaultValue": "..."
},
{
"id": 1863,
"id": 192,
"name": "decimal_digits",
"variant": "declaration",
"kind": 1024,
@@ -116,7 +116,7 @@
"defaultValue": "..."
},
{
"id": 1864,
"id": 193,
"name": "rounding",
"variant": "declaration",
"kind": 1024,
@@ -137,12 +137,12 @@
{
"title": "Properties",
"children": [
1859,
1860,
1861,
1862,
1863,
1864
188,
189,
190,
191,
192,
193
]
}
]
@@ -151,14 +151,14 @@
{
"type": "reflection",
"declaration": {
"id": 1865,
"id": 194,
"name": "__type",
"variant": "declaration",
"kind": 65536,
"flags": {},
"children": [
{
"id": 1866,
"id": 195,
"name": "code",
"variant": "declaration",
"kind": 1024,
@@ -175,7 +175,7 @@
"defaultValue": "..."
},
{
"id": 1867,
"id": 196,
"name": "symbol",
"variant": "declaration",
"kind": 1024,
@@ -192,7 +192,7 @@
"defaultValue": "..."
},
{
"id": 1868,
"id": 197,
"name": "symbol_native",
"variant": "declaration",
"kind": 1024,
@@ -209,7 +209,7 @@
"defaultValue": "..."
},
{
"id": 1869,
"id": 198,
"name": "name",
"variant": "declaration",
"kind": 1024,
@@ -226,7 +226,7 @@
"defaultValue": "..."
},
{
"id": 1870,
"id": 199,
"name": "decimal_digits",
"variant": "declaration",
"kind": 1024,
@@ -243,7 +243,7 @@
"defaultValue": "..."
},
{
"id": 1871,
"id": 200,
"name": "rounding",
"variant": "declaration",
"kind": 1024,
@@ -264,12 +264,12 @@
{
"title": "Properties",
"children": [
1866,
1867,
1868,
1869,
1870,
1871
195,
196,
197,
198,
199,
200
]
}
]
@@ -278,14 +278,14 @@
{
"type": "reflection",
"declaration": {
"id": 1872,
"id": 201,
"name": "__type",
"variant": "declaration",
"kind": 65536,
"flags": {},
"children": [
{
"id": 1873,
"id": 202,
"name": "raw_rounding",
"variant": "declaration",
"kind": 1024,
@@ -305,7 +305,7 @@
{
"title": "Properties",
"children": [
1873
202
]
}
]
@@ -332,81 +332,81 @@
{
"title": "Variables",
"children": [
1857
186
]
}
],
"packageName": "@medusajs/currency",
"symbolIdMap": {
"1856": {
"185": {
"sourceFileName": "../../../../packages/modules/currency/src/models/index.ts",
"qualifiedName": ""
},
"1857": {
"186": {
"sourceFileName": "../../../../packages/modules/currency/src/models/currency.ts",
"qualifiedName": "default"
},
"1858": {
"187": {
"sourceFileName": "../../../../packages/modules/currency/src/models/currency.ts",
"qualifiedName": "__object"
},
"1859": {
"188": {
"sourceFileName": "../../../../packages/modules/currency/src/models/currency.ts",
"qualifiedName": "__object.code"
},
"1860": {
"189": {
"sourceFileName": "../../../../packages/modules/currency/src/models/currency.ts",
"qualifiedName": "__object.symbol"
},
"1861": {
"190": {
"sourceFileName": "../../../../packages/modules/currency/src/models/currency.ts",
"qualifiedName": "__object.symbol_native"
},
"1862": {
"191": {
"sourceFileName": "../../../../packages/modules/currency/src/models/currency.ts",
"qualifiedName": "__object.name"
},
"1863": {
"192": {
"sourceFileName": "../../../../packages/modules/currency/src/models/currency.ts",
"qualifiedName": "__object.decimal_digits"
},
"1864": {
"193": {
"sourceFileName": "../../../../packages/modules/currency/src/models/currency.ts",
"qualifiedName": "__object.rounding"
},
"1865": {
"194": {
"sourceFileName": "../../../../packages/core/utils/src/dml/entity-builder.ts",
"qualifiedName": "__type"
},
"1866": {
"195": {
"sourceFileName": "../../../../packages/modules/currency/src/models/currency.ts",
"qualifiedName": "code"
},
"1867": {
"196": {
"sourceFileName": "../../../../packages/modules/currency/src/models/currency.ts",
"qualifiedName": "symbol"
},
"1868": {
"197": {
"sourceFileName": "../../../../packages/modules/currency/src/models/currency.ts",
"qualifiedName": "symbol_native"
},
"1869": {
"198": {
"sourceFileName": "../../../../packages/modules/currency/src/models/currency.ts",
"qualifiedName": "name"
},
"1870": {
"199": {
"sourceFileName": "../../../../packages/modules/currency/src/models/currency.ts",
"qualifiedName": "decimal_digits"
},
"1871": {
"200": {
"sourceFileName": "../../../../packages/modules/currency/src/models/currency.ts",
"qualifiedName": "rounding"
},
"1872": {
"201": {
"sourceFileName": "../../../../packages/core/utils/src/dml/entity-builder.ts",
"qualifiedName": "__type"
},
"1873": {
"202": {
"sourceFileName": "",
"qualifiedName": "raw_rounding"
}
@@ -416,7 +416,7 @@
"1": "../../../../packages/modules/currency/src/models/index.ts"
},
"reflections": {
"1": 1856
"1": 185
}
}
}
File diff suppressed because it is too large Load Diff
@@ -4,7 +4,6 @@ const fileOptions: FormattingOptionsType = {
"^file/.*AbstractFileProviderService": {
reflectionGroups: {
Properties: false,
Constructors: false,
},
reflectionDescription: `In this document, youll learn how to create a file provider module and the methods you must implement in its main service.`,
frontmatterData: {
@@ -4,7 +4,6 @@ const notificationOptions: FormattingOptionsType = {
"^notification/.*AbstractNotificationProviderService": {
reflectionGroups: {
Properties: false,
Constructors: false,
},
reflectionDescription: `In this document, youll learn how to create a notification provider module and the methods you must implement in it.`,
frontmatterData: {
@@ -6,6 +6,7 @@ import { modules } from "./references.js"
import {
customModuleServiceNames,
customModuleTitles,
dmlModules,
} from "./references-details.js"
import { FormattingOptionType } from "types"
import { kebabToCamel, kebabToPascal, kebabToSnake, kebabToTitle } from "utils"
@@ -28,7 +29,12 @@ const mergerOptions: Partial<TypeDocOptions> = {
objectLiteralTypeDeclarationStyle: "component",
mdxOutput: true,
maxLevel: 3,
allReflectionsHaveOwnDocument: [...modules, "dml", "workflows"],
allReflectionsHaveOwnDocument: [
...modules,
...dmlModules.map((module) => `${module}-models`),
"dml",
"workflows",
],
allReflectionsHaveOwnDocumentInNamespace: ["Utilities"],
formatting: {
"*": {
@@ -55,6 +61,8 @@ const mergerOptions: Partial<TypeDocOptions> = {
)
? customModuleServiceNames[moduleName]
: `I${kebabToPascal(moduleName)}ModuleService`
const isDmlModule = dmlModules.includes(moduleName)
return Object.assign(obj, {
// module config
[`^${snakeCaseModuleName}`]: {
@@ -107,11 +115,15 @@ const mergerOptions: Partial<TypeDocOptions> = {
typeParameters: false,
suffix: `- ${titleModuleName} Module Data Models Reference`,
},
reflectionGroups: {
Constructors: false,
Functions: false,
Methods: false,
},
reflectionGroups: isDmlModule
? {
Variables: true,
}
: {
Constructors: false,
Functions: false,
Methods: false,
},
},
[`^modules/${snakeCaseModuleName}_models`]: {
reflectionDescription: `This documentation provides a reference to the data models in the ${titleModuleName} Module`,
@@ -122,6 +134,11 @@ const mergerOptions: Partial<TypeDocOptions> = {
reflectionTitle: {
fullReplacement: `${titleModuleName} Module Data Models Reference`,
},
reflectionGroupRename: isDmlModule
? {
Variables: "Data Models",
}
: {},
},
} as FormattingOptionType)
}, {} as FormattingOptionType),
@@ -23,3 +23,6 @@ export const customModulesOptions: Record<string, Partial<TypeDocOptions>> = {
),
},
}
// a list of modules that now support DML
export const dmlModules = ["currency"]
@@ -18,6 +18,10 @@ export default function getModelOptions({
tsConfigName: `${moduleName}.json`,
generateModelsDiagram: true,
diagramAddToFile: entryPath,
resolveDmlRelations: true,
generateDMLsDiagram: true,
diagramDMLAddToFile: entryPath,
normalizeDmlTypes: true,
...typedocOptions,
})
}
@@ -0,0 +1 @@
export const RELATION_NAMES = ["HasOne", "HasMany", "BelongsTo", "ManyToMany"]
@@ -0,0 +1,111 @@
import {
Application,
Context,
Converter,
DeclarationReflection,
ParameterType,
ReferenceType,
} from "typedoc"
import { getDmlProperties, isDmlEntity } from "utils"
import { RELATION_NAMES } from "./constants"
export class DmlRelationsResolver {
private app: Application
private dmlReflectionsAndProperties: {
reflection: DeclarationReflection
properties: DeclarationReflection[]
}[]
constructor(app: Application) {
this.app = app
this.dmlReflectionsAndProperties = []
this.app.options.addDeclaration({
name: "resolveDmlRelations",
help: "Whether to enable resolving DML relations.",
type: ParameterType.Boolean,
defaultValue: false,
})
this.app.converter.on(
Converter.EVENT_CREATE_DECLARATION,
this.addReflection.bind(this)
)
this.app.converter.on(
Converter.EVENT_RESOLVE_BEGIN,
this.resolveRelations.bind(this)
)
}
addReflection(_context: Context, reflection: DeclarationReflection) {
if (!this.app.options.getValue("resolveDmlRelations")) {
return
}
if (isDmlEntity(reflection)) {
this.dmlReflectionsAndProperties?.push({
reflection,
properties: getDmlProperties(reflection.type as ReferenceType),
})
}
}
resolveRelations(context: Context) {
if (!this.app.options.getValue("resolveDmlRelations")) {
return
}
this.dmlReflectionsAndProperties.forEach(({ properties }) => {
properties.forEach((property) => {
if (
property.type?.type !== "reference" ||
!RELATION_NAMES.includes(property.type.name)
) {
return
}
// try to find the reflection that this relation points to
const relatedReflectionType = property.type.typeArguments?.[0]
if (
relatedReflectionType?.type !== "reflection" ||
!relatedReflectionType.declaration.signatures?.length ||
relatedReflectionType.declaration.signatures[0].type?.type !==
"reference"
) {
return
}
const relatedReflection = this.findReflectionMatchingProperties(
getDmlProperties(relatedReflectionType.declaration.signatures[0].type)
)
if (!relatedReflection) {
return
}
// replace type argument with reference to related reflection
property.type.typeArguments = [
ReferenceType.createResolvedReference(
relatedReflection.name,
relatedReflection,
context.project
),
]
})
})
}
findReflectionMatchingProperties(
properties: DeclarationReflection[]
): DeclarationReflection | undefined {
return this.dmlReflectionsAndProperties.find(({ properties: refProps }) => {
return properties.every((property) => {
return refProps.find(
(refProp) =>
refProp.name === property.name &&
(refProp.type as ReferenceType).name ===
(property.type as ReferenceType).name
)
})
})?.reflection
}
}
@@ -0,0 +1,69 @@
import {
Application,
Context,
Converter,
DeclarationReflection,
ParameterType,
ReferenceType,
ReflectionFlag,
ReflectionKind,
} from "typedoc"
import { getDmlProperties, isDmlEntity } from "utils"
export function load(app: Application) {
app.options.addDeclaration({
name: "normalizeDmlTypes",
help: "Whether to normalize DML types.",
type: ParameterType.Boolean,
defaultValue: false,
})
app.converter.on(Converter.EVENT_RESOLVE_BEGIN, (context: Context) => {
if (!app.options.getValue("normalizeDmlTypes")) {
return
}
for (const reflection of context.project.getReflectionsByKind(
ReflectionKind.Variable
)) {
if (
!(reflection instanceof DeclarationReflection) ||
!isDmlEntity(reflection)
) {
break
}
const properties = getDmlProperties(reflection.type as ReferenceType)
properties.forEach((property) => {
if (property.type?.type !== "reference") {
return
}
normalizeNullable(property)
})
}
})
}
function normalizeNullable(property: DeclarationReflection) {
const propertyReference = property.type as ReferenceType
if (
propertyReference.name !== "NullableModifier" ||
!propertyReference.typeArguments ||
propertyReference.typeArguments?.length < 2
) {
return
}
const actualType = propertyReference.typeArguments[1]
if (actualType.type !== "reference") {
return
}
// change the property's type to reference the actual type
property.type = actualType
// set a flag on the property to consider it optional
property.setFlag(ReflectionFlag.Optional)
}
@@ -8,6 +8,9 @@ import { load as signatureModifierPlugin } from "./signature-modifier"
import { MermaidDiagramGenerator } from "./mermaid-diagram-generator"
import { load as parentIgnorePlugin } from "./parent-ignore"
import { GenerateNamespacePlugin } from "./generate-namespace"
import { DmlRelationsResolver } from "./dml-relations-resolver"
import { load as dmlTypesNormalizer } from "./dml-types-normalizer"
import { MermaidDiagramDMLGenerator } from "./mermaid-diagram-dml-generator"
export function load(app: Application) {
resolveReferencesPluginLoad(app)
@@ -17,7 +20,10 @@ export function load(app: Application) {
eslintExamplePlugin(app)
signatureModifierPlugin(app)
parentIgnorePlugin(app)
dmlTypesNormalizer(app)
new GenerateNamespacePlugin(app)
new MermaidDiagramGenerator(app)
new DmlRelationsResolver(app)
new MermaidDiagramDMLGenerator(app)
}
@@ -0,0 +1,251 @@
import path from "path"
import {
Application,
Comment,
Context,
Converter,
DeclarationReflection,
ParameterType,
ReferenceType,
Reflection,
ReflectionKind,
TypeDocOptionMap,
} from "typedoc"
import { RELATION_NAMES } from "./constants"
import { getDmlProperties, isDmlEntity } from "utils"
type Relations = Map<
string,
{
target: string
left: MermaidRelationType
right?: MermaidRelationType
name: string
}[]
>
type PluginOptions = Pick<
TypeDocOptionMap,
"generateModelsDiagram" | "diagramAddToFile"
>
const ALLOWED_RELATION_NAMES = RELATION_NAMES.filter(
(name) => name !== "BelongsTo"
)
type MermaidRelationType =
| "one-to-one"
| "one-to-many"
| "many-to-one"
| "many-to-many"
export class MermaidDiagramDMLGenerator {
private app: Application
private options?: PluginOptions
private mainFileReflection?: Reflection
constructor(app: Application) {
this.app = app
this.app.options.addDeclaration({
name: "generateDMLsDiagram",
help: "Whether to generate a Mermaid.js class diagram for data models in the reference.",
type: ParameterType.Boolean,
defaultValue: false,
})
this.app.options.addDeclaration({
name: "diagramDMLAddToFile",
help: "The file to add the mermaid diagram to. The diagram is added as a package comment.",
type: ParameterType.String,
})
app.converter.on(
Converter.EVENT_CREATE_DECLARATION,
this.setMainFile.bind(this)
)
app.converter.on(
Converter.EVENT_RESOLVE_BEGIN,
this.findRelations.bind(this)
)
}
getPluginOptions(): PluginOptions {
if (this.options) {
return this.options
}
this.options = {
generateModelsDiagram: this.app.options.getValue("generateDMLsDiagram"),
diagramAddToFile: this.app.options.getValue("diagramDMLAddToFile"),
}
return this.options
}
setMainFile(context: Context) {
const options = this.getPluginOptions()
if (
this.mainFileReflection ||
!options.generateModelsDiagram ||
!options.diagramAddToFile
) {
return
}
const mainFilePath = options.diagramAddToFile.startsWith("packages")
? path.resolve("..", "..", "..", "..", options.diagramAddToFile)
: options.diagramAddToFile
const mainFileSource = context.program.getSourceFile(mainFilePath)
if (!mainFileSource) {
return
}
const mainFileSymbol = context.checker.getSymbolAtLocation(mainFileSource)
if (!mainFileSymbol) {
return
}
this.mainFileReflection =
context.project.getReflectionFromSymbol(mainFileSymbol)
}
findRelations(context: Context) {
const options = this.getPluginOptions()
if (
!this.mainFileReflection ||
!options.generateModelsDiagram ||
!options.diagramAddToFile
) {
return
}
const relations: Relations = new Map()
for (const reflection of context.project.getReflectionsByKind(
ReflectionKind.Variable
)) {
if (
!(reflection instanceof DeclarationReflection) ||
!isDmlEntity(reflection)
) {
return
}
const reflectionProperties = getDmlProperties(
reflection.type as ReferenceType
)
// find relations of that reflection
reflectionProperties.forEach((property) => {
if (
property.type?.type !== "reference" ||
!ALLOWED_RELATION_NAMES.includes(property.type.name) ||
property.type.typeArguments?.length !== 1 ||
property.type.typeArguments[0].type !== "reference"
) {
return
}
const targetReflection = property.type.typeArguments[0].reflection
if (!targetReflection) {
return
}
// if entry already exists in relation, don't add anything
const exists =
relations
.get(reflection.name)
?.some((relation) => relation.target === targetReflection.name) ||
relations
.get(targetReflection.name)
?.some((relation) => relation.target === reflection.name)
if (exists) {
return
}
const relationType = this.getMermaidRelation(property.type.name)
if (!relationType) {
return
}
if (!relations.has(reflection.name)) {
relations.set(reflection.name, [])
}
relations.get(reflection.name)?.push({
target: targetReflection.name,
left: relationType,
right: this.getReverseRelationType(relationType),
name: property.name,
})
})
}
if (!relations.size) {
return
}
this.mainFileReflection.comment = new Comment([
{
text: "## Relations Overview\n\n",
kind: "text",
},
{
text: this.buildMermaidDiagram(relations),
kind: "code",
},
])
}
getMermaidRelation(relation: string): MermaidRelationType | undefined {
switch (relation) {
case "HasOne":
return "one-to-one"
case "HasMany":
return "one-to-many"
case "ManyToMany":
return "many-to-many"
}
}
getReverseRelationType(
relationType: MermaidRelationType
): MermaidRelationType {
return relationType.split("-").reverse().join("-") as MermaidRelationType
}
buildMermaidDiagram(relations: Relations): string {
const linePrefix = `\t`
const lineSuffix = `\n`
let diagram = `erDiagram${lineSuffix}`
relations.forEach((itemRelations, itemName) => {
itemRelations.forEach((itemRelation) => {
diagram += `${linePrefix}${itemName} ${this.getRelationTypeSymbol(
itemRelation.left,
"left"
)}--${this.getRelationTypeSymbol(itemRelation.right!, "right")} ${
itemRelation.target
} : ${itemRelation.name}${lineSuffix}`
})
})
return "```mermaid\n" + diagram + "\n```"
}
getRelationTypeSymbol(
relationType: MermaidRelationType,
direction: "left" | "right"
): string {
switch (relationType) {
case "one-to-one":
return "||"
case "one-to-many":
return direction === "left" ? "||" : "|{"
case "many-to-many":
return direction === "left" ? "}|" : "|{"
case "many-to-one":
return direction === "left" ? "}|" : "||"
}
}
}
@@ -64,6 +64,8 @@ import ifShowSeparatorForTitleLevelHelper from "./resources/helpers/if-show-sepa
import shouldExpandPropertiesHelper from "./resources/helpers/should-expand-properties"
import shouldExpandDeclarationChildrenHelper from "./resources/helpers/should-expand-declaration-children"
import startSectionsHelper from "./resources/helpers/start-sections"
import ifDmlEntityHelper from "./resources/helpers/if-dml-entity"
import dmlPropertiesHelper from "./resources/helpers/dml-properties"
import { MarkdownTheme } from "./theme"
const TEMPLATE_PATH = path.join(__dirname, "resources", "templates")
@@ -156,4 +158,6 @@ export function registerHelpers(theme: MarkdownTheme) {
shouldExpandPropertiesHelper(theme)
shouldExpandDeclarationChildrenHelper(theme)
startSectionsHelper(theme)
ifDmlEntityHelper()
dmlPropertiesHelper()
}
@@ -0,0 +1,23 @@
import * as Handlebars from "handlebars"
import { DeclarationReflection, ReferenceType } from "typedoc"
import { getDmlProperties, isDmlEntity } from "utils"
export default function () {
Handlebars.registerHelper(
"dmlProperties",
function (this: DeclarationReflection) {
if (!isDmlEntity(this)) {
return ""
}
const properties = getDmlProperties(this.type as ReferenceType)
// TODO resolve the property types to names/native types
return Handlebars.helpers.typeDeclarationMembers.call(properties, {
hash: {
sectionTitle: this.name,
},
})
}
)
}
@@ -0,0 +1,12 @@
import * as Handlebars from "handlebars"
import { DeclarationReflection } from "typedoc"
import { isDmlEntity } from "utils"
export default function () {
Handlebars.registerHelper(
"ifDmlEntity",
function (this: DeclarationReflection, options: Handlebars.HelperOptions) {
return isDmlEntity(this) ? options.fn(this) : options.inverse(this)
}
)
}
@@ -14,7 +14,8 @@ export default function (theme: MarkdownTheme) {
const md: string[] = []
const { hideInPageTOC } = theme
const { hideTocHeaders } = theme.getFormattingOptionsForLocation()
const { hideTocHeaders, reflectionGroupRename = {} } =
theme.getFormattingOptionsForLocation()
const isVisible = this.groups?.some((group) =>
group.allChildrenHaveOwnDocument()
@@ -36,7 +37,9 @@ export default function (theme: MarkdownTheme) {
}
const headingLevel = hideInPageTOC ? `##` : `###`
this.groups?.forEach((group) => {
const groupTitle = group.title
const groupTitle = Object.hasOwn(reflectionGroupRename, group.title)
? reflectionGroupRename[group.title]
: group.title
if (group.categories) {
group.categories.forEach((category) => {
md.push(`${headingLevel} ${category.title} ${groupTitle}\n\n`)
@@ -27,10 +27,6 @@ export default function (theme: MarkdownTheme) {
[]
) as DeclarationReflection[]
// if (typeof options.hash.sectionTitle !== "string") {
// console.log("here2")
// }
let result = ""
switch (theme.objectLiteralTypeDeclarationStyle) {
case "list": {
@@ -0,0 +1,7 @@
{{#if (sectionEnabled "member_declaration_comment")}}
{{> comment}}
{{/if}}
{{{dmlProperties}}}
@@ -46,8 +46,16 @@
{{#if (sectionEnabled "member_declaration")}}
{{#ifDmlEntity}}
{{> member.dml}}
{{else}}
{{> member.declaration}}
{{/ifDmlEntity}}
{{/if}}
{{/ifIsReference}}
@@ -2,6 +2,7 @@ import {
Comment,
DeclarationReflection,
ProjectReflection,
ReferenceType,
ReflectionKind,
ReflectionType,
} from "typedoc"
@@ -15,6 +16,7 @@ import {
stripLineBreaks,
} from "utils"
import { MarkdownTheme } from "../theme"
import { getDmlProperties, isDmlEntity } from "utils"
const ALLOWED_KINDS: ReflectionKind[] = [
ReflectionKind.EnumMember,
@@ -153,7 +155,18 @@ export function reflectionComponentFormatter({
const hasChildren = "children" in reflection && reflection.children?.length
if (
if (reflection.variant === "declaration" && isDmlEntity(reflection)) {
componentItem.children = getDmlProperties(
reflection.type as ReferenceType
).map((childItem) =>
reflectionComponentFormatter({
reflection: childItem,
level: level + 1,
maxLevel,
project,
})
)
} else if (
(reflection.type || hasChildren) &&
level + 1 <= (maxLevel || MarkdownTheme.MAX_LEVEL)
) {
@@ -2,7 +2,8 @@
"extends": "../../tsconfig",
"compilerOptions": {
"outDir": "./dist",
"rootDir": "./src"
"rootDir": "./src",
"lib": ["es2022"]
},
"include": ["src"]
}
+23
View File
@@ -54,6 +54,9 @@ export type FormattingOptionType = {
reflectionGroups?: {
[k: string]: boolean
}
reflectionGroupRename?: {
[k: string]: string
}
reflectionCategories?: {
[k: string]: boolean
}
@@ -234,5 +237,25 @@ export declare module "typedoc" {
* The file to add the mermaid diagram to. The diagram is added as a package comment.
*/
diagramAddToFile: string
/**
* Whether to generate a Mermaid.js class diagram for data models in the reference.
* (Used for DML)
*/
generateDMLsDiagram: boolean
/**
* The file to add the mermaid diagram to. The diagram is added as a package comment.
* (Used for DML)
*/
diagramDMLAddToFile: string
/**
* Whether to enable resolving DML relations.
* @defaultValue false
*/
resolveDmlRelations: boolean
/**
* Whether to normalize DML types.
* @defaultValue false
*/
normalizeDmlTypes: boolean
}
}
+24
View File
@@ -0,0 +1,24 @@
import { DeclarationReflection, ReferenceType, ReflectionType } from "typedoc"
export function isDmlEntity(reflection: DeclarationReflection) {
if (reflection.type?.type !== "reference") {
return false
}
return reflection.type.name === "DmlEntity"
}
export function getDmlProperties(
reflectionType: ReferenceType
): DeclarationReflection[] {
if (
!reflectionType.typeArguments?.length ||
reflectionType.typeArguments[0].type !== "intersection"
) {
return []
}
const schemaType = reflectionType.typeArguments[0].types[0] as ReflectionType
return schemaType.declaration.children || []
}
+1
View File
@@ -1,3 +1,4 @@
export * from "./dml-utils"
export * from "./get-type-children"
export * from "./get-project-child"
export * from "./get-type-str"