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:
@@ -4,7 +4,6 @@ const fileOptions: FormattingOptionsType = {
|
||||
"^file/.*AbstractFileProviderService": {
|
||||
reflectionGroups: {
|
||||
Properties: false,
|
||||
Constructors: false,
|
||||
},
|
||||
reflectionDescription: `In this document, you’ll 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, you’ll 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
www/utils/packages/types/lib/index.d.ts
vendored
23
www/utils/packages/types/lib/index.d.ts
vendored
@@ -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
www/utils/packages/utils/src/dml-utils.ts
Normal file
24
www/utils/packages/utils/src/dml-utils.ts
Normal 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,3 +1,4 @@
|
||||
export * from "./dml-utils"
|
||||
export * from "./get-type-children"
|
||||
export * from "./get-project-child"
|
||||
export * from "./get-type-str"
|
||||
|
||||
Reference in New Issue
Block a user