docs: resdesign and restructure modules references (#5372)

* docs: change format of module reference

* description fix

* update structure + comments

* added new options to README

* small text fix

* change section ordering

* change how required/optional are shown

* remove optional text

* docs: redesigned accordion
This commit is contained in:
Shahed Nasser
2023-10-16 19:51:55 +03:00
committed by GitHub
parent 3dada88c81
commit 2d74ec256f
218 changed files with 5434 additions and 7684 deletions

View File

@@ -25,9 +25,8 @@ module.exports = ({
},
expandMembers: true,
showCommentsAsHeader: true,
parameterStyle: "list",
parameterStyle: "component",
useTsLinkResolution: false,
showReturnSignature: false,
sections: {
reflection_typeParameters: false,
member_declaration_typeParameters: false,
@@ -39,13 +38,30 @@ module.exports = ({
member_signature_sources: false,
member_signature_title: false,
title_reflectionPath: false,
member_sources_definedIn: false,
member_signature_returns: false,
},
reflectionGroups: {
Constructors: false,
Properties: false,
},
parameterComponent: "ParameterTypes",
mdxImports: [
`import ParameterTypes from "@site/src/components/ParameterTypes"`,
],
}
: {}),
: {
showCommentsAsHeader: true,
sections: {
member_sources_definedIn: false,
reflection_hierarchy: false,
},
parameterStyle: "component",
parameterComponent: "ParameterTypes",
mdxImports: [
`import ParameterTypes from "@site/src/components/ParameterTypes"`,
],
}),
...additionalFormatting,
}
}
@@ -73,6 +89,8 @@ module.exports = ({
hideMembersSymbol: true,
formatting,
allReflectionsHaveOwnDocument: true,
objectLiteralTypeDeclarationStyle: "component",
mdxOutput: true,
...extraOptions,
}
}

View File

@@ -18,7 +18,6 @@ module.exports = modulesConfig({
text: "Beta",
},
slug: "/references/pricing",
// hide_table_of_contents: true,
},
},
},
@@ -26,7 +25,7 @@ module.exports = modulesConfig({
pattern: "IPricingModuleService/methods",
additionalFormatting: {
reflectionDescription:
"This documentation provides a reference to the {{alias}} {{kind}}. This belongs to the Pricing Module.",
"This documentation provides a reference to the `{{alias}}` {{kind}}. This belongs to the Pricing Module.",
frontmatterData: {
displayed_sidebar: "pricingReference",
badge: {

View File

@@ -6,6 +6,7 @@ A plugin that forks and customizes the [typedoc-plugin-markdown](https://github.
Aside from the options detailed in [typedoc-plugin-markdown](https://github.com/tgreyuk/typedoc-plugin-markdown/tree/master/packages/typedoc-plugin-markdown#options), the following options are accepted:
- `mdxImports`: A boolean value indicating whether the outputted files should be MDX files with the `.mdx` extension.
- `formatting`: An object whose keys are string patterns used to target specific files. You can also use the string `*` to target all files. The values are objects having the following properties:
- `sections`: (optional) an object whose keys are of type [`SectionKey`](./src/types.ts#L19) and values are boolean. This property is used to enable/disable certain sections in the outputted generated docs.
- `reflectionGroups`: (optional) an object whose keys are titles of reflection groups (for example, `Constructors`), and values are boolean. This property is used to enable/disable reflection group from being documented.
@@ -17,10 +18,11 @@ Aside from the options detailed in [typedoc-plugin-markdown](https://github.com/
- `expandMembers`: (optional) a boolean indicating whether members in a page should be expanded. When enabled, the member titles (for example, `Methods`) are removed and the heading level of nested titles whithin the member is elevated by `1`.
- `showCommentAsHeader`: (optional) a boolean indicating whether comments, for example, a method's name, are represented as headers.
- `showCommentsAsDetails`: (optional) a boolean indicating whether comments, for example, a method's name, are represented as details component.
- `parameterStyle`: (optional) a string indicating how parameters are displayed. Its value can be `table` (default) or `list`.
- `parameterStyle`: (optional) a string indicating how parameters are displayed. Its value can be `table` (default), `list`, or `component`.
- `showReturnSignature`: (optional) a boolean indicating whether to show the signature for returned values.
- `frontmatterData`: (optional) an object that will be injected as frontmatter to the pages matching specified pattern.
-
- `parameterComponent`: (optional) a string indicating the name of a React component to pass the parameters as an object to. This is only used if the `parameterStyle` option is set to `component`. This also must be used with the `mdxOutput` option enabled, and an import string for the component passed to the `mdxImports` option. The React component will receive a `parameters` prop, which is an array of type [Parameter](./src/types.ts#L95).
- `mdxImports`: (optional) an array of strings, each holding an import statement that will be added to the beginning of each page. For example, `["import ParameterTypes from "@site/src/components/ParameterTypes""]`. Must be used along with the `mdxOutput` option enabled.
## Build Plugin

View File

@@ -87,7 +87,7 @@ export function load(app: Application) {
type: ParameterType.String,
defaultValue: "table",
validate: (x) => {
const availableValues = ["table", "list"]
const availableValues = ["table", "list", "component"]
if (!availableValues.includes(x)) {
throw new Error(
`Wrong value for objectLiteralTypeDeclarationStyle, the expected value is one of ${availableValues}`
@@ -102,5 +102,12 @@ export function load(app: Application) {
type: ParameterType.Object,
defaultValue: {},
})
app.options.addDeclaration({
help: "[Markdown Plugin] Whether outputted files should have an mdx extension.",
name: "mdxOutput",
type: ParameterType.Boolean,
defaultValue: false,
})
}
export { MarkdownTheme }

View File

@@ -37,8 +37,16 @@ import parameterHelper from "./resources/helpers/parameter"
import debugHelper from "./resources/helpers/debug"
import frontmatterHelper from "./resources/helpers/frontmatter"
import reflectionDescriptionHelper from "./resources/helpers/reflection-description"
import mdxImportsHelper from "./resources/helpers/mdx-imports"
import parameterComponentHelper from "./resources/helpers/parameter-component"
import typeParameterComponentHelper from "./resources/helpers/type-parameter-component"
import showPropertiesAsComponentHelper from "./resources/helpers/show-properties-as-component"
import commentTagHelper from "./resources/helpers/comment-tag"
import exampleHelper from "./resources/helpers/example"
import { MarkdownTheme } from "./theme"
// test
const TEMPLATE_PATH = path.join(__dirname, "resources", "templates")
export const indexTemplate = Handlebars.compile(
@@ -68,7 +76,7 @@ export function registerPartials() {
export function registerHelpers(theme: MarkdownTheme) {
breadcrumbsHelper(theme)
commentHelper(theme)
commentsHelper(theme)
commentsHelper()
declarationTitleHelper(theme)
escapeHelper()
hierarchyHelper()
@@ -86,7 +94,7 @@ export function registerHelpers(theme: MarkdownTheme) {
reflectionPathHelper()
reflectionTitleHelper(theme)
relativeUrlHelper(theme)
returns()
returns(theme)
signatureTitleHelper(theme)
tocHelper(theme)
typeHelper()
@@ -102,4 +110,10 @@ export function registerHelpers(theme: MarkdownTheme) {
debugHelper()
frontmatterHelper(theme)
reflectionDescriptionHelper(theme)
mdxImportsHelper(theme)
parameterComponentHelper(theme)
typeParameterComponentHelper(theme)
showPropertiesAsComponentHelper(theme)
commentTagHelper(theme)
exampleHelper()
}

View File

@@ -0,0 +1,27 @@
import * as Handlebars from "handlebars"
import { CommentTag } from "typedoc"
import { camelToTitleCase } from "../../utils"
import { MarkdownTheme } from "../../theme"
export default function (theme: MarkdownTheme) {
Handlebars.registerHelper(
"commentTag",
function (tag: CommentTag, commentLevel = 4, parent = null) {
const { showCommentsAsHeader, showCommentsAsDetails } =
theme.getFormattingOptionsForLocation()
const tagTitle = camelToTitleCase(tag.tag.substring(1)),
tagContent = Handlebars.helpers.comment(tag.content)
if (showCommentsAsHeader) {
return `${Handlebars.helpers.titleLevel.call(
parent,
commentLevel
)} ${tagTitle}\n\n${tagContent}`
} else if (showCommentsAsDetails) {
return `<details>\n<summary>\n${tagTitle}\n</summary>\n\n${tagContent}\n\n</details>`
}
return `**${tagTitle}**\n\n${tagContent}`
}
)
}

View File

@@ -1,9 +1,7 @@
import * as Handlebars from "handlebars"
import { Comment } from "typedoc"
import { camelToTitleCase } from "../../utils"
import { MarkdownTheme } from "../../theme"
export default function (theme: MarkdownTheme) {
export default function () {
Handlebars.registerHelper(
"comments",
function (
@@ -13,33 +11,23 @@ export default function (theme: MarkdownTheme) {
commentLevel = 4,
parent = null
) {
const { showCommentsAsHeader, showCommentsAsDetails } =
theme.getFormattingOptionsForLocation()
const md: string[] = []
if (showSummary && comment.summary) {
md.push(Handlebars.helpers.comment(comment.summary))
}
const filteredTags = comment.blockTags.filter(
(tag) => tag.tag !== "@returns"
)
const filteredTags = comment.blockTags
.filter((tag) => tag.tag !== "@returns")
.filter((tag) => tag.tag !== "@example")
if (showTags && comment.blockTags?.length) {
const tags = filteredTags.map((tag) => {
const tagTitle = camelToTitleCase(tag.tag.substring(1)),
tagContent = Handlebars.helpers.comment(tag.content)
if (showCommentsAsHeader) {
return `${Handlebars.helpers.titleLevel.call(
parent || comment,
commentLevel
)} ${tagTitle}\n\n${tagContent}`
} else if (showCommentsAsDetails) {
return `<details>\n<summary>\n${tagTitle}\n</summary>\n\n${tagContent}\n\n</details>`
} else {
return `**${tagTitle}**\n\n${tagContent}`
}
return Handlebars.helpers.commentTag(
tag,
commentLevel,
parent || comment
)
})
md.push(tags.join("\n\n"))
}

View File

@@ -0,0 +1,19 @@
import * as Handlebars from "handlebars"
import { Reflection } from "typedoc"
export default function () {
Handlebars.registerHelper(
"example",
function (reflection: Reflection, commentLevel = 4) {
const exampleTag = reflection.comment?.blockTags.find(
(tag) => tag.tag === "@example"
)
if (!exampleTag) {
return ""
}
return Handlebars.helpers.commentTag(exampleTag, commentLevel, reflection)
}
)
}

View File

@@ -0,0 +1,14 @@
import * as Handlebars from "handlebars"
import { MarkdownTheme } from "../../theme"
export default function (theme: MarkdownTheme) {
Handlebars.registerHelper("mdxImports", function () {
if (!theme.mdxOutput) {
return ""
}
const { mdxImports } = theme.getFormattingOptionsForLocation()
return mdxImports?.join("\n") || ""
})
}

View File

@@ -0,0 +1,22 @@
import * as Handlebars from "handlebars"
import { ReflectionParameterType } from "../../types"
import { parseParams } from "../../utils/params-utils"
import { MarkdownTheme } from "../../theme"
import { reflectionComponentFormatter } from "../../utils/reflection-formatter"
export default function (theme: MarkdownTheme) {
Handlebars.registerHelper(
"parameterComponent",
function (this: ReflectionParameterType[]) {
const { parameterComponent } = theme.getFormattingOptionsForLocation()
const parameters = this.reduce(
(acc: ReflectionParameterType[], current) => parseParams(current, acc),
[]
).map((parameter) => reflectionComponentFormatter(parameter, 1))
return `<${parameterComponent} parameters={${JSON.stringify(
parameters
)}} />`
}
)
}

View File

@@ -1,7 +1,7 @@
import * as Handlebars from "handlebars"
import reflectionFomatter from "../../utils/reflection-formatter"
import { ReflectionParameterType } from "../../types"
import { parseParams } from "../../utils/params-utils"
import reflectionFormatter from "../../utils/reflection-formatter"
export default function () {
Handlebars.registerHelper(
@@ -21,7 +21,7 @@ export default function () {
function list(parameters: ReflectionParameterType[]) {
const items = parameters.map((parameter) => {
return reflectionFomatter(parameter)
return reflectionFormatter(parameter, "list")
})
return items.join("\n")

View File

@@ -1,119 +1,31 @@
import * as Handlebars from "handlebars"
import { ParameterReflection, ReflectionType } from "typedoc"
import { stripLineBreaks } from "../../utils"
import { getReflectionType } from "./type"
import { parseParams } from "../../utils/params-utils"
import { ReflectionParameterType } from "../../types"
import {
getTableHeaders,
reflectionTableFormatter,
} from "../../utils/reflection-formatter"
export default function () {
Handlebars.registerHelper(
"parameterTable",
function (this: ReflectionParameterType[]) {
return table(
this.reduce(
(acc: ReflectionParameterType[], current: ReflectionParameterType) =>
parseParams(current, acc),
[]
) as ParameterReflection[]
const parameters = this.reduce(
(acc: ReflectionParameterType[], current: ReflectionParameterType) =>
parseParams(current, acc),
[]
)
const headers = getTableHeaders(parameters)
const rows = parameters.map((parameter) =>
reflectionTableFormatter(parameter)
)
return `\n| ${headers.join(" | ")} |\n| ${headers
.map(() => ":------")
.join(" | ")} |\n${rows.join("")}`
}
)
}
function table(parameters: ParameterReflection[]) {
const showDefaults = hasDefaultValues(parameters)
const comments = parameters.map(
(param: ParameterReflection) => !!param.comment?.hasVisibleComponent()
)
const hasComments = !comments.every((value) => !value)
const headers = ["Name", "Type"]
if (showDefaults) {
headers.push("Default value")
}
if (hasComments) {
headers.push("Description")
}
const rows = parameters.map((parameter) => {
const row: string[] = []
const nbsp = " " // ? <== Unicode no-break space character
const rest = parameter.flags.isRest ? "..." : ""
const optional = parameter.flags.isOptional ? "?" : ""
const isDestructuredParam = parameter.name == "__namedParameters"
const isDestructuredParamProp =
parameter.name.startsWith("__namedParameters.")
if (isDestructuredParam) {
row.push(`\`${rest}«destructured»\``)
} else if (isDestructuredParamProp) {
row.push(`${nbsp}\`${rest}${parameter.name.slice(18)}${optional}\``)
} else {
row.push(`\`${rest}${parameter.name}${optional}\``)
}
row.push(
parameter.type
? Handlebars.helpers.type.call(parameter.type, "object")
: getReflectionType(parameter, "object")
)
if (showDefaults) {
row.push(getDefaultValue(parameter))
}
if (hasComments) {
const comments = getComments(parameter)
if (comments) {
row.push(
stripLineBreaks(Handlebars.helpers.comments(comments)).replace(
/\|/g,
"\\|"
)
)
} else {
row.push("-")
}
}
return `| ${row.join(" | ")} |\n`
})
const output = `\n| ${headers.join(" | ")} |\n| ${headers
.map(() => ":------")
.join(" | ")} |\n${rows.join("")}`
return output
}
function getDefaultValue(parameter: ParameterReflection) {
return parameter.defaultValue && parameter.defaultValue !== "..."
? `\`${parameter.defaultValue}\``
: "`undefined`"
}
function hasDefaultValues(parameters: ParameterReflection[]) {
const defaultValues = (parameters as ParameterReflection[]).map(
(param) =>
param.defaultValue !== "{}" &&
param.defaultValue !== "..." &&
!!param.defaultValue
)
return !defaultValues.every((value) => !value)
}
function getComments(parameter: ParameterReflection) {
if (parameter.type instanceof ReflectionType) {
if (
parameter.type?.declaration?.signatures &&
parameter.type?.declaration?.signatures[0]?.comment
) {
return parameter.type?.declaration?.signatures[0]?.comment
}
}
return parameter.comment
}

View File

@@ -10,6 +10,8 @@ export default function (theme: MarkdownTheme) {
if (parameterStyle === "list") {
return Handlebars.helpers.parameterList.call(this)
} else if (parameterStyle === "component") {
return Handlebars.helpers.parameterComponent.call(this)
} else {
return Handlebars.helpers.parameterTable.call(this)
}

View File

@@ -0,0 +1,12 @@
import * as Handlebars from "handlebars"
import { MarkdownTheme } from "../../theme"
import { DeclarationReference } from "typedoc"
export default function (theme: MarkdownTheme) {
Handlebars.registerHelper(
"propertiesComponent",
function (this: DeclarationReference) {
console.log(this)
}
)
}

View File

@@ -1,34 +1,106 @@
import * as Handlebars from "handlebars"
import { Comment, DeclarationReflection } from "typedoc"
import reflectionFomatter from "../../utils/reflection-formatter"
import { Comment, DeclarationReflection, SignatureReflection } from "typedoc"
import { MarkdownTheme } from "../../theme"
import reflectionFormatter from "../../utils/reflection-formatter"
import { returnReflectionComponentFormatter } from "../../utils/return-reflection-formatter"
import { Parameter } from "../../types"
export default function () {
Handlebars.registerHelper("returns", function (comment: Comment) {
const md: string[] = []
if (comment.blockTags?.length) {
const tags = comment.blockTags
.filter((tag) => tag.tag === "@returns")
.map((tag) => {
let result = Handlebars.helpers.comment(tag.content)
tag.content.forEach((commentPart) => {
if (
"target" in commentPart &&
commentPart.target instanceof DeclarationReflection
) {
const content = commentPart.target.children?.map((childItem) =>
reflectionFomatter(childItem)
)
result += `\n\n<details>\n<summary>\n${
commentPart.target.name
}\n</summary>\n\n${content?.join("\n")}\n\n</details>`
}
})
return result
})
md.push(tags.join("\n\n"))
export default function (theme: MarkdownTheme) {
Handlebars.registerHelper(
"returns",
function (reflection: SignatureReflection) {
if (reflection.variant === "signature" && "type" in reflection) {
return getReturnFromType(theme, reflection)
} else if (reflection.comment) {
return getReturnFromComment(theme, reflection.comment)
} else {
return ""
}
}
return md.join("")
})
)
}
function getReturnFromType(
theme: MarkdownTheme,
reflection: SignatureReflection
) {
const { parameterStyle, parameterComponent } =
theme.getFormattingOptionsForLocation()
if (!reflection.type) {
return ""
}
const componentItems = returnReflectionComponentFormatter(
reflection.type,
reflection.project || theme.project,
reflection.comment,
1
)
if (parameterStyle === "component") {
return `<${parameterComponent} parameters={${JSON.stringify(
componentItems
)}} />`
} else {
return formatReturnAsList(componentItems)
}
}
function formatReturnAsList(componentItems: Parameter[], level = 1): string {
const prefix = `${Array(level - 1)
.fill("\t")
.join("")}-`
return componentItems
.map(
(item) =>
`${prefix}\`${item.name}\`: ${
item.optional || item.defaultValue
? `(${item.optional ? "optional" : ""}${
item.optional && item.defaultValue ? "," : ""
}${item.defaultValue ? `default: ${item.defaultValue}` : ""}) `
: ""
}${item.description}${
item.children?.length
? `\n${formatReturnAsList(item.children, level + 1)}`
: ""
}`
)
.join("\n")
}
function getReturnFromComment(theme: MarkdownTheme, comment: Comment) {
const md: string[] = []
const { parameterStyle, parameterComponent } =
theme.getFormattingOptionsForLocation()
if (comment.blockTags?.length) {
const tags = comment.blockTags
.filter((tag) => tag.tag === "@returns")
.map((tag) => {
let result = Handlebars.helpers.comment(tag.content)
tag.content.forEach((commentPart) => {
if (
"target" in commentPart &&
commentPart.target instanceof DeclarationReflection
) {
const content = commentPart.target.children?.map((childItem) =>
reflectionFormatter(childItem, parameterStyle, 1)
)
result +=
parameterStyle === "component"
? `\n\n<${parameterComponent} parameters={${JSON.stringify(
content
)}} title={"${commentPart.target.name}"} />\n\n`
: `\n\n<details>\n<summary>\n${
commentPart.target.name
}\n</summary>\n\n${content?.join("\n")}\n\n</details>`
}
})
return result
})
md.push(tags.join("\n\n"))
}
return md.join("")
}

View File

@@ -0,0 +1,15 @@
import * as Handlebars from "handlebars"
import { MarkdownTheme } from "../../theme"
export default function (theme: MarkdownTheme) {
Handlebars.registerHelper(
"showPropertiesAsComponent",
function (title: string) {
const { parameterStyle } = theme.getFormattingOptionsForLocation()
// console.log(parameterStyle, title)
return parameterStyle === "component" && title === "Properties"
}
)
}

View File

@@ -2,14 +2,15 @@ import * as Handlebars from "handlebars"
import { DeclarationReflection, ReflectionType } from "typedoc"
import { MarkdownTheme } from "../../theme"
import { escapeChars, stripLineBreaks } from "../../utils"
import reflectionFomatter from "../../utils/reflection-formatter"
import { parseParams } from "../../utils/params-utils"
import { ReflectionParameterType } from "../../types"
import reflectionFormatter from "../../utils/reflection-formatter"
export default function (theme: MarkdownTheme) {
Handlebars.registerHelper(
"typeDeclarationMembers",
function (this: DeclarationReflection[]) {
const { parameterComponent } = theme.getFormattingOptionsForLocation()
const comments = this.map(
(param) => !!param.comment?.hasVisibleComponent()
)
@@ -27,6 +28,10 @@ export default function (theme: MarkdownTheme) {
result = getListMarkdownContent(properties)
break
}
case "component": {
result = getComponentMarkdownContent(properties, parameterComponent)
break
}
case "table": {
result = getTableMarkdownContent(properties, hasComments)
break
@@ -38,11 +43,24 @@ export default function (theme: MarkdownTheme) {
}
function getListMarkdownContent(properties: DeclarationReflection[]) {
const items = properties.map((property) => reflectionFomatter(property))
const items = properties.map((property) =>
reflectionFormatter(property, "list")
)
return items.join("\n")
}
function getComponentMarkdownContent(
properties: DeclarationReflection[],
parameterComponent?: string
) {
const parameters = properties.map((property) =>
reflectionFormatter(property, "component")
)
return `<${parameterComponent} parameters={${JSON.stringify(parameters)}} />`
}
function getTableMarkdownContent(
properties: DeclarationReflection[],
hasComments: boolean

View File

@@ -0,0 +1,20 @@
import * as Handlebars from "handlebars"
import { TypeParameterReflection } from "typedoc"
import { reflectionComponentFormatter } from "../../utils/reflection-formatter"
import { MarkdownTheme } from "../../theme"
export default function (theme: MarkdownTheme) {
Handlebars.registerHelper(
"typeParameterComponent",
function (this: TypeParameterReflection[]) {
const { parameterComponent } = theme.getFormattingOptionsForLocation()
const parameters = this.map((parameter) =>
reflectionComponentFormatter(parameter, 1)
)
return `<${parameterComponent} parameters={${JSON.stringify(
parameters
)}} />`
}
)
}

View File

@@ -1,6 +1,6 @@
import * as Handlebars from "handlebars"
import { TypeParameterReflection } from "typedoc"
import reflectionFomatter from "../../utils/reflection-formatter"
import reflectionFormatter from "../../utils/reflection-formatter"
export default function () {
Handlebars.registerHelper(
@@ -12,7 +12,9 @@ export default function () {
}
function list(parameters: TypeParameterReflection[]) {
const items = parameters.map((parameter) => reflectionFomatter(parameter))
const items = parameters.map((parameter) =>
reflectionFormatter(parameter, "list")
)
return items.join("\n")
}

View File

@@ -1,82 +1,23 @@
import * as Handlebars from "handlebars"
import { TypeParameterReflection } from "typedoc"
import { stripLineBreaks } from "../../utils"
import {
getTableHeaders,
reflectionTableFormatter,
} from "../../utils/reflection-formatter"
import { hasTypes } from "../../utils/type-utils"
export default function () {
Handlebars.registerHelper(
"typeParameterTable",
function (this: TypeParameterReflection[]) {
return table(this)
const showTypeCol = hasTypes(this)
const headers = getTableHeaders(this, showTypeCol)
const rows = this.map((parameter) => reflectionTableFormatter(parameter))
return `\n| ${headers.join(" | ")} |\n| ${headers
.map(() => ":------")
.join(" | ")} |\n${rows.join("")}`
}
)
}
function table(parameters: TypeParameterReflection[]) {
const showTypeCol = hasTypes(parameters)
const comments = parameters.map(
(param: TypeParameterReflection) => !!param.comment?.hasVisibleComponent()
)
const hasComments = !comments.every((value) => !value)
const headers = ["Name"]
if (showTypeCol) {
headers.push("Type")
}
if (hasComments) {
headers.push("Description")
}
const rows = parameters.map((parameter: TypeParameterReflection) => {
const row: string[] = []
row.push(`\`${parameter.name}\``)
if (showTypeCol) {
const typeCol: string[] = []
if (!parameter.type && !parameter.default) {
typeCol.push(`\`${parameter.name}\``)
}
if (parameter.type) {
typeCol.push(
`extends ${Handlebars.helpers.type.call(parameter.type, "object")}`
)
}
if (parameter.default) {
if (parameter.type) {
typeCol.push(" = ")
}
typeCol.push(Handlebars.helpers.type.call(parameter.default))
}
row.push(typeCol.join(""))
}
if (hasComments) {
if (parameter.comment?.summary) {
row.push(
stripLineBreaks(
Handlebars.helpers.comment(parameter.comment?.summary)
).replace(/\|/g, "\\|")
)
} else {
row.push("-")
}
}
return `| ${row.join(" | ")} |\n`
})
const output = `\n| ${headers.join(" | ")} |\n| ${headers
.map(() => ":------")
.join(" | ")} |\n${rows.join("")}`
return output
}
function hasTypes(parameters: TypeParameterReflection[]) {
const types = (parameters as TypeParameterReflection[]).map(
(param) => !!param.type || !!param.default
)
return !types.every((value) => !value)
}

View File

@@ -10,6 +10,8 @@ export default function (theme: MarkdownTheme) {
if (parameterStyle === "list") {
return Handlebars.helpers.typeParameterList.call(this)
} else if (parameterStyle === "component") {
return Handlebars.helpers.typeParameterComponent.call(this)
} else {
return Handlebars.helpers.typeParameterTable.call(this)
}

View File

@@ -2,26 +2,19 @@ import * as Handlebars from "handlebars"
import {
ArrayType,
ConditionalType,
DeclarationReflection,
IndexedAccessType,
InferredType,
IntersectionType,
IntrinsicType,
LiteralType,
PredicateType,
QueryType,
ReferenceType,
ReflectionType,
SignatureReflection,
TupleType,
TypeOperatorType,
UnionType,
UnknownType,
} from "typedoc"
import { escapeChars } from "../../utils"
import { ReflectionParameterType } from "../../types"
type Collapse = "object" | "function" | "all" | "none"
import getType, { Collapse } from "../../utils/type-utils"
export default function () {
Handlebars.registerHelper(
@@ -46,267 +39,7 @@ export default function () {
collapse: Collapse = "none",
emphasis = true
) {
if (this instanceof ReferenceType) {
return getReferenceType(this, emphasis)
}
if (this instanceof ArrayType && this.elementType) {
return getArrayType(this, emphasis)
}
if (this instanceof UnionType && this.types) {
return getUnionType(this, emphasis)
}
if (this instanceof IntersectionType && this.types) {
return getIntersectionType(this)
}
if (this instanceof TupleType && this.elements) {
return getTupleType(this)
}
if (this instanceof IntrinsicType && this.name) {
return getIntrinsicType(this, emphasis)
}
if (this instanceof ReflectionType) {
return getReflectionType(this, collapse)
}
if (this instanceof DeclarationReflection) {
return getReflectionType(this, collapse)
}
if (this instanceof TypeOperatorType) {
return getTypeOperatorType(this)
}
if (this instanceof QueryType) {
return getQueryType(this)
}
if (this instanceof ConditionalType) {
return getConditionalType(this)
}
if (this instanceof IndexedAccessType) {
return getIndexAccessType(this)
}
if (this instanceof UnknownType) {
return getUnknownType(this)
}
if (this instanceof InferredType) {
return getInferredType(this)
}
if (this instanceof LiteralType) {
return getLiteralType(this)
}
return this ? escapeChars(this.toString()) : ""
return getType(this, collapse, emphasis)
}
)
}
function getLiteralType(model: LiteralType) {
if (typeof model.value === "bigint") {
return `\`${model.value}n\``
}
return `\`\`${JSON.stringify(model.value)}\`\``
}
export function getReflectionType(
model: ReflectionParameterType,
collapse: Collapse
) {
const root = model instanceof ReflectionType ? model.declaration : model
if ("signatures" in root && root.signatures) {
return collapse === "function" || collapse === "all"
? `\`fn\``
: getFunctionType(root.signatures)
}
return collapse === "object" || collapse === "all"
? `\`Object\``
: getDeclarationType(root as DeclarationReflection)
}
export function getDeclarationType(model: DeclarationReflection) {
if (model.indexSignature || model.children) {
let indexSignature = ""
const declarationIndexSignature = model.indexSignature
if (declarationIndexSignature) {
const key = declarationIndexSignature.parameters
? declarationIndexSignature.parameters.map(
(param) => `\`[${param.name}: ${param.type}]\``
)
: ""
const obj = Handlebars.helpers.type.call(declarationIndexSignature.type)
indexSignature = `${key}: ${obj}; `
}
const types =
model.children &&
model.children.map((obj) => {
return `\`${obj.name}${
obj.flags.isOptional ? "?" : ""
}\`: ${Handlebars.helpers.type.call(
obj.signatures || obj.children ? obj : obj.type
)} ${
obj.defaultValue && obj.defaultValue !== "..."
? `= ${escapeChars(obj.defaultValue)}`
: ""
}`
})
return `{ ${indexSignature ? indexSignature : ""}${
types ? types.join("; ") : ""
} }${
model.defaultValue && model.defaultValue !== "..."
? `= ${escapeChars(model.defaultValue)}`
: ""
}`
}
return "{}"
}
export function getFunctionType(modelSignatures: SignatureReflection[]) {
const functions = modelSignatures.map((fn) => {
const typeParams = fn.typeParameters
? `<${fn.typeParameters
.map((typeParameter) => typeParameter.name)
.join(", ")}\\>`
: []
const params = fn.parameters
? fn.parameters.map((param) => {
return `${param.flags.isRest ? "..." : ""}\`${param.name}${
param.flags.isOptional ? "?" : ""
}\`: ${Handlebars.helpers.type.call(param.type ? param.type : param)}`
})
: []
const returns = Handlebars.helpers.type.call(fn.type)
return typeParams + `(${params.join(", ")}) => ${returns}`
})
return functions.join("")
}
export function getReferenceType(model: ReferenceType, emphasis: boolean) {
if (model.reflection || (model.name && model.typeArguments)) {
const reflection: string[] = []
if (model.reflection?.url) {
reflection.push(
emphasis
? `[${`\`${model.reflection.name}\``}](${Handlebars.helpers.relativeURL(
model.reflection.url
)})`
: model.reflection.name
)
} else {
reflection.push(
emphasis
? model.externalUrl
? `[${`\`${model.name}\``}]( ${model.externalUrl} )`
: `\`${model.name}\``
: model.name
)
}
if (model.typeArguments && model.typeArguments.length > 0) {
reflection.push(
`<${model.typeArguments
.map((typeArgument) =>
Handlebars.helpers.type.call(typeArgument, "none", emphasis)
)
.join(", ")}\\>`
)
}
return reflection.join("")
}
return emphasis
? model.externalUrl
? `[${`\`${model.name}\``}]( ${model.externalUrl} )`
: `\`${model.name}\``
: escapeChars(model.name)
}
export function getArrayType(model: ArrayType, emphasis: boolean) {
const arrayType = Handlebars.helpers.type.call(
model.elementType,
"none",
emphasis
)
return model.elementType.type === "union"
? `(${arrayType})[]`
: `${arrayType}[]`
}
export function getUnionType(model: UnionType, emphasis: boolean) {
return model.types
.map((unionType) =>
Handlebars.helpers.type.call(unionType, "none", emphasis)
)
.join(` \\| `)
}
export function getIntersectionType(model: IntersectionType) {
return model.types
.map((intersectionType) => Handlebars.helpers.type.call(intersectionType))
.join(" & ")
}
export function getTupleType(model: TupleType) {
return `[${model.elements
.map((element) => Handlebars.helpers.type.call(element))
.join(", ")}]`
}
export function getIntrinsicType(model: IntrinsicType, emphasis: boolean) {
return emphasis ? `\`${model.name}\`` : escapeChars(model.name)
}
export function getTypeOperatorType(model: TypeOperatorType) {
return `${model.operator} ${Handlebars.helpers.type.call(model.target)}`
}
export function getQueryType(model: QueryType) {
return `typeof ${Handlebars.helpers.type.call(model.queryType)}`
}
export function getInferredType(model: InferredType) {
return `infer ${escapeChars(model.name)}`
}
export function getUnknownType(model: UnknownType) {
return escapeChars(model.name)
}
export function getConditionalType(model: ConditionalType) {
const md: string[] = []
if (model.checkType) {
md.push(Handlebars.helpers.type.call(model.checkType))
}
md.push("extends")
if (model.extendsType) {
md.push(Handlebars.helpers.type.call(model.extendsType))
}
md.push("?")
if (model.trueType) {
md.push(Handlebars.helpers.type.call(model.trueType))
}
md.push(":")
if (model.falseType) {
md.push(Handlebars.helpers.type.call(model.falseType))
}
return md.join(" ")
}
export function getIndexAccessType(model: IndexedAccessType) {
const md: string[] = []
if (model.objectType) {
md.push(Handlebars.helpers.type.call(model.objectType))
}
if (model.indexType) {
md.push(`[${Handlebars.helpers.type.call(model.indexType)}]`)
}
return md.join("")
}

View File

@@ -1,3 +1,11 @@
{{#with model}}
{{{frontmatter}}}
{{/with}}
{{{mdxImports}}}
{{#ifShowBreadcrumbs}}
{{{breadcrumbs}}}

View File

@@ -2,6 +2,12 @@
{{> comment}}
{{#if (sectionEnabled "member_declaration_example")}}
{{{example this 4}}}
{{/if}}
{{#if (sectionEnabled "member_declaration_typeParameters")}}
{{#if typeParameters}}

View File

@@ -28,6 +28,12 @@
{{/if}}
{{#if (sectionEnabled "member_signature_example")}}
{{{example this 4}}}
{{/if}}
{{#if (sectionEnabled "member_signature_parameters")}}
{{#if parameters}}
@@ -66,7 +72,7 @@
{{/if}}
{{#if (getFormattingOption "showReturnSignature")}}
{{#if (sectionEnabled "member_signature_returns")}}
{{#with type}}
@@ -76,12 +82,8 @@
{{/if}}
{{#with comment}}
{{{returns this}}}
{{/with}}
{{#with type}}
{{#if (sectionEnabled "member_signature_declarationSignatures")}}

View File

@@ -14,12 +14,24 @@ ___
{{/unless}}
{{#if (showPropertiesAsComponent title)}}
{{#with children}}
{{{parameterComponent}}}
{{/with}}
{{else}}
{{#each children}}
{{> member}}
{{/each}}
{{/if}}
{{/each}}
{{else}}
@@ -30,6 +42,16 @@ ___
{{/unless}}
{{#if (showPropertiesAsComponent title)}}
{{#with children}}
{{{parameterComponent}}}
{{/with}}
{{else}}
{{#each children}}
{{> member}}
@@ -38,4 +60,6 @@ ___
{{/if}}
{{/if}}
{{/if}}

View File

@@ -1,9 +1,3 @@
{{#with model}}
{{{frontmatter}}}
{{/with}}
{{> header}}
{{#with model.readme}}

View File

@@ -1,9 +1,3 @@
{{#with model}}
{{{frontmatter}}}
{{/with}}
{{> header}}
{{> title}}
@@ -22,6 +16,12 @@
{{/with}}
{{#if (sectionEnabled "reflection_example")}}
{{{example model 2}}}
{{/if}}
{{#if (sectionEnabled "reflection_typeParameters")}}
{{#if model.typeParameters}}

View File

@@ -1,9 +1,3 @@
{{#with model}}
{{{frontmatter}}}
{{/with}}
{{> header}}
{{> title}}

View File

@@ -47,6 +47,7 @@ export class MarkdownTheme extends Theme {
preserveAnchorCasing!: boolean
objectLiteralTypeDeclarationStyle: ObjectLiteralDeclarationStyle
formattingOptions: FormattingOptionsType
mdxOutput: boolean
project?: ProjectReflection
reflection?: DeclarationReflection
@@ -83,6 +84,7 @@ export class MarkdownTheme extends Theme {
this.formattingOptions = this.getOption(
"formatting"
) as FormattingOptionsType
this.mdxOutput = this.getOption("mdxOutput") as boolean
this.listenTo(this.owner, {
[RendererEvent.BEGIN]: this.onBeginRenderer,
@@ -162,7 +164,12 @@ export class MarkdownTheme extends Theme {
}
toUrl(mapping: Mapping, reflection: DeclarationReflection) {
return mapping.directory + "/" + this.getUrl(reflection) + ".md"
return (
mapping.directory +
"/" +
this.getUrl(reflection) +
(this.mdxOutput ? ".mdx" : ".md")
)
}
getUrl(reflection: Reflection, relative?: Reflection): string {
@@ -364,7 +371,7 @@ export class MarkdownTheme extends Theme {
}
get globalsFile() {
return "modules.md"
return `modules.${this.mdxOutput ? "mdx" : "md"}`
}
getFormattingOptionsForLocation(): FormattingOptionType {

View File

@@ -8,14 +8,14 @@ import {
TypeParameterReflection,
} from "typedoc"
export type ParameterStyle = "table" | "list"
export type ParameterStyle = "table" | "list" | "component"
export type ReflectionTitleOptions = {
typeParameters?: boolean
kind?: boolean
}
export type ObjectLiteralDeclarationStyle = "table" | "list"
export type ObjectLiteralDeclarationStyle = "table" | "list" | "component"
export type SectionKey =
| "comment"
@@ -23,6 +23,7 @@ export type SectionKey =
| "member_declaration_indexSignature"
| "member_declaration_signatures"
| "member_declaration_typeDeclaration"
| "member_declaration_example"
| "member_getteSetter_getSignature"
| "member_getteSetter_setSignature"
| "member_signatures"
@@ -33,10 +34,10 @@ export type SectionKey =
| "member_signature_comment"
| "member_signature_typeParameters"
| "member_signature_parameters"
| "showReturnSignature"
| "member_signature_example"
| "member_signature_returns"
| "member_signature_declarationSignatures"
| "member_signature_declarationChildren"
| "member_signature_comment"
| "member_signature_sources"
| "member_sources_implementationOf"
| "member_sources_inheritedFrom"
@@ -70,8 +71,9 @@ export type FormattingOptionType = {
showCommentsAsHeader?: boolean
showCommentsAsDetails?: boolean
parameterStyle?: ParameterStyle
showReturnSignature?: boolean
frontmatterData?: Record<string, unknown>
parameterComponent?: string
mdxImports?: string[]
}
export type FormattingOptionsType = {
@@ -90,6 +92,15 @@ export type Mapping = {
template: (pageEvent: PageEvent<ContainerReflection>) => string
}
export type Parameter = {
name: string
type: string
optional?: boolean
defaultValue?: string
description?: string
children?: Parameter[]
}
export class NavigationItem {
title: string
url: string

View File

@@ -18,6 +18,7 @@ export function formatContents(contents: string) {
export function escapeChars(str: string) {
return str
.replace(/>/g, "\\>")
.replace(/>/g, "\\>")
.replace(/_/g, "\\_")
.replace(/`/g, "\\`")

View File

@@ -1,14 +1,38 @@
import { Comment, DeclarationReflection, ReflectionType } from "typedoc"
import {
Comment,
DeclarationReflection,
ProjectReflection,
ReflectionType,
SomeType,
} from "typedoc"
import * as Handlebars from "handlebars"
import { stripLineBreaks } from "../utils"
import { ReflectionParameterType } from "../types"
import { Parameter, ParameterStyle, ReflectionParameterType } from "../types"
import getType, { getReflectionType } from "./type-utils"
const MAX_LEVEL = 3
export default function reflectionFomatter(
export default function reflectionFormatter(
reflection: ReflectionParameterType,
type: ParameterStyle = "table",
level = 1
): string | Parameter {
switch (type) {
case "list":
return reflectionListFormatter(reflection, level)
case "component":
return reflectionComponentFormatter(reflection, level)
case "table":
return reflectionTableFormatter(reflection)
default:
return ""
}
}
export function reflectionListFormatter(
reflection: ReflectionParameterType,
level = 1
) {
): string {
const prefix = `${Array(level - 1)
.fill("\t")
.join("")}-`
@@ -25,16 +49,17 @@ export default function reflectionFomatter(
if (comments) {
item += stripLineBreaks(Handlebars.helpers.comments(comments))
}
const hasChildren = "children" in reflection && reflection.children?.length
if ((reflection.type || hasChildren) && level + 1 <= MAX_LEVEL) {
const children = hasChildren
? reflection.children
: getTypeChildren(reflection.type!, reflection.project)
const itemChildren: string[] = []
comments.summary.forEach((commentSummary) => {
if ("target" in commentSummary) {
const targetReflection = commentSummary.target as DeclarationReflection
if (targetReflection.children && level + 1 <= MAX_LEVEL) {
targetReflection.children.forEach((childItem) => {
itemChildren.push(reflectionFomatter(childItem, level + 1))
})
}
}
children?.forEach((childItem) => {
itemChildren.push(reflectionListFormatter(childItem, level + 1))
})
if (itemChildren.length) {
// TODO maybe we should check the type of the reflection and replace
@@ -50,7 +75,120 @@ export default function reflectionFomatter(
return item
}
function getDefaultValue(parameter: ReflectionParameterType): string | null {
export function reflectionComponentFormatter(
reflection: ReflectionParameterType,
level = 1
): Parameter {
const defaultValue = getDefaultValue(reflection) || ""
const optional = reflection.flags.isOptional
const comments = getComments(reflection)
const componentItem: Parameter = {
name: reflection.name,
type: reflection.type
? getType(reflection.type, "object")
: getReflectionType(reflection, "object"),
description: comments
? stripLineBreaks(Handlebars.helpers.comments(comments))
: "",
optional,
defaultValue,
children: [],
}
const hasChildren = "children" in reflection && reflection.children?.length
if ((reflection.type || hasChildren) && level + 1 <= MAX_LEVEL) {
const children = hasChildren
? reflection.children
: getTypeChildren(reflection.type!, reflection.project)
children?.forEach((childItem) => {
componentItem.children?.push(
reflectionComponentFormatter(childItem, level + 1)
)
})
}
return componentItem
}
export function reflectionTableFormatter(
parameter: ReflectionParameterType
): string {
const showDefaults = hasDefaultValues([parameter])
const hasComments = !!parameter.comment?.hasVisibleComponent()
const row: string[] = []
const nbsp = " " // ? <== Unicode no-break space character
const rest = parameter.flags.isRest ? "..." : ""
const optional = parameter.flags.isOptional ? "?" : ""
const isDestructuredParam = parameter.name == "__namedParameters"
const isDestructuredParamProp =
parameter.name.startsWith("__namedParameters.")
if (isDestructuredParam) {
row.push(`\`${rest}«destructured»\``)
} else if (isDestructuredParamProp) {
row.push(`${nbsp}\`${rest}${parameter.name.slice(18)}${optional}\``)
} else {
row.push(`\`${rest}${parameter.name}${optional}\``)
}
row.push(
parameter.type
? Handlebars.helpers.type.call(parameter.type, "object")
: getReflectionType(parameter, "object")
)
if (showDefaults) {
row.push(getDefaultValue(parameter) || "")
}
if (hasComments) {
const comments = getComments(parameter)
if (comments) {
row.push(
stripLineBreaks(Handlebars.helpers.comments(comments)).replace(
/\|/g,
"\\|"
)
)
} else {
row.push("-")
}
}
return `| ${row.join(" | ")} |\n`
}
export function getTableHeaders(
parameters: ReflectionParameterType[],
showTypeHeader?: boolean
): string[] {
const showDefaults = hasDefaultValues(parameters)
const hasComments = parameters.some(
(parameter) => !!parameter.comment?.hasVisibleComponent()
)
const headers = ["Name"]
if (showTypeHeader) {
headers.push("Type")
}
if (showDefaults) {
headers.push("Default value")
}
if (hasComments) {
headers.push("Description")
}
return headers
}
export function getDefaultValue(
parameter: ReflectionParameterType
): string | null {
if (!("defaultValue" in parameter)) {
return null
}
@@ -59,7 +197,21 @@ function getDefaultValue(parameter: ReflectionParameterType): string | null {
: null
}
function getComments(parameter: ReflectionParameterType): Comment | undefined {
export function hasDefaultValues(parameters: ReflectionParameterType[]) {
const defaultValues = (parameters as ReflectionParameterType[]).map(
(param) =>
"defaultValue" in param &&
param.defaultValue !== "{}" &&
param.defaultValue !== "..." &&
!!param.defaultValue
)
return !defaultValues.every((value) => !value)
}
export function getComments(
parameter: ReflectionParameterType
): Comment | undefined {
if (parameter.type instanceof ReflectionType) {
if (
parameter.type?.declaration?.signatures &&
@@ -70,3 +222,28 @@ function getComments(parameter: ReflectionParameterType): Comment | undefined {
}
return parameter.comment
}
export function getTypeChildren(
reflectionType: SomeType,
project: ProjectReflection
) {
let children: DeclarationReflection[] = []
switch (reflectionType.type) {
case "reference":
// eslint-disable-next-line no-case-declarations
const referencedReflection = project.getChildByName(reflectionType.name)
if (
referencedReflection instanceof DeclarationReflection &&
referencedReflection.children
) {
children = referencedReflection.children
}
break
case "array":
children = getTypeChildren(reflectionType.elementType, project)
}
return children
}

View File

@@ -0,0 +1,180 @@
import {
Comment,
DeclarationReflection,
ProjectReflection,
ReflectionFlags,
SomeType,
} from "typedoc"
import * as Handlebars from "handlebars"
import getType from "./type-utils"
import { Parameter } from "../types"
import {
getDefaultValue,
reflectionComponentFormatter,
} from "./reflection-formatter"
const MAX_LEVEL = 3
export function returnReflectionComponentFormatter(
reflectionType: SomeType,
project: ProjectReflection,
comment?: Comment,
level = 1
): Parameter[] {
const typeName = getType(reflectionType, "object", false)
const type = getType(reflectionType, "object")
const componentItem: Parameter[] = []
if (reflectionType.type === "reference") {
// put type name as a title and its referenced items as children.
if (reflectionType.typeArguments) {
const parentKey = componentItem.push({
name: "name" in reflectionType ? reflectionType.name : typeName,
type,
optional:
"flags" in reflectionType
? (reflectionType.flags as ReflectionFlags).isOptional
: false,
defaultValue:
"declaration" in reflectionType
? getDefaultValue(
reflectionType.declaration as DeclarationReflection
) || ""
: "",
description: comment ? getReturnComment(comment) : "",
children: [],
})
if (!isOnlyVoid(reflectionType.typeArguments) && level + 1 <= MAX_LEVEL) {
reflectionType.typeArguments.forEach((typeArg) => {
const typeArgComponent = returnReflectionComponentFormatter(
typeArg,
project,
undefined,
level + 1
)
if (typeArgComponent.length) {
componentItem[parentKey - 1].children?.push(...typeArgComponent)
}
})
}
} else if (reflectionType.reflection) {
componentItem.push(
reflectionComponentFormatter(
reflectionType.reflection as DeclarationReflection,
level
)
)
} else {
// try to retrieve reflection by its name
const reflection = project.getChildByName(reflectionType.name)
if (reflection) {
componentItem.push(
reflectionComponentFormatter(
reflection as DeclarationReflection,
level
)
)
}
}
} else if (reflectionType.type === "array") {
const parentKey = componentItem.push({
name:
"name" in reflectionType ? (reflectionType.name as string) : typeName,
type,
optional:
"flags" in reflectionType
? (reflectionType.flags as ReflectionFlags).isOptional
: false,
defaultValue:
"declaration" in reflectionType
? getDefaultValue(
reflectionType.declaration as DeclarationReflection
) || ""
: "",
description: comment ? getReturnComment(comment) : "",
children: [],
})
if (level + 1 <= MAX_LEVEL) {
const elementTypeItem = returnReflectionComponentFormatter(
reflectionType.elementType,
project,
undefined,
level + 1
)
if (elementTypeItem.length) {
componentItem[parentKey - 1].children?.push(...elementTypeItem)
}
}
} else if (reflectionType.type === "tuple") {
let pushTo: Parameter[] = []
if (level === 1) {
const parentKey = componentItem.push({
name:
"name" in reflectionType ? (reflectionType.name as string) : typeName,
type,
optional:
"flags" in reflectionType
? (reflectionType.flags as ReflectionFlags).isOptional
: false,
defaultValue:
"declaration" in reflectionType
? getDefaultValue(
reflectionType.declaration as DeclarationReflection
) || ""
: "",
description: comment ? getReturnComment(comment) : "",
children: [],
})
pushTo = componentItem[parentKey - 1].children!
} else {
pushTo = componentItem
}
if (level + 1 <= MAX_LEVEL) {
reflectionType.elements.forEach((element) => {
const elementTypeItem = returnReflectionComponentFormatter(
element,
project,
undefined,
level + 1
)
if (elementTypeItem.length) {
pushTo.push(...elementTypeItem)
}
})
}
} else {
// put type as the only component.
componentItem.push({
name: "name" in reflectionType ? reflectionType.name : typeName,
type,
optional:
"flags" in reflectionType
? (reflectionType.flags as ReflectionFlags).isOptional
: true,
defaultValue:
"declaration" in reflectionType
? getDefaultValue(reflectionType.declaration) || ""
: "",
description: comment ? getReturnComment(comment) : "",
children: [],
})
}
return componentItem
}
export function getReturnComment(comment: Comment): string {
if (!comment.blockTags?.length) {
return ""
}
return comment.blockTags
.filter((tag) => tag.tag === "@returns")
.map((tag) => Handlebars.helpers.comment(tag.content))
.join("\n\n")
}
export function isOnlyVoid(reflectionTypes: SomeType[]) {
return reflectionTypes.every(
(type) => type.type === "intrinsic" && type.name === "void"
)
}

View File

@@ -0,0 +1,300 @@
import {
ArrayType,
ConditionalType,
DeclarationReflection,
IndexedAccessType,
InferredType,
IntersectionType,
IntrinsicType,
LiteralType,
QueryType,
ReferenceType,
ReflectionType,
SignatureReflection,
SomeType,
TupleType,
TypeOperatorType,
TypeParameterReflection,
UnionType,
UnknownType,
} from "typedoc"
import * as Handlebars from "handlebars"
import { ReflectionParameterType } from "../types"
import { escapeChars } from "../utils"
export type Collapse = "object" | "function" | "all" | "none"
export default function getType(
reflectionType: SomeType,
collapse: Collapse = "none",
emphasis = true
): string {
if (reflectionType instanceof ReferenceType) {
return getReferenceType(reflectionType, emphasis)
}
if (reflectionType instanceof ArrayType && reflectionType.elementType) {
return getArrayType(reflectionType, emphasis)
}
if (reflectionType instanceof UnionType && reflectionType.types) {
return getUnionType(reflectionType, emphasis)
}
if (reflectionType instanceof IntersectionType && reflectionType.types) {
return getIntersectionType(reflectionType)
}
if (reflectionType instanceof TupleType && reflectionType.elements) {
return getTupleType(reflectionType)
}
if (reflectionType instanceof IntrinsicType && reflectionType.name) {
return getIntrinsicType(reflectionType, emphasis)
}
if (reflectionType instanceof ReflectionType) {
return getReflectionType(reflectionType.declaration, collapse)
}
if (reflectionType instanceof DeclarationReflection) {
return getReflectionType(reflectionType, collapse)
}
if (reflectionType instanceof TypeOperatorType) {
return getTypeOperatorType(reflectionType)
}
if (reflectionType instanceof QueryType) {
return getQueryType(reflectionType)
}
if (reflectionType instanceof ConditionalType) {
return getConditionalType(reflectionType)
}
if (reflectionType instanceof IndexedAccessType) {
return getIndexAccessType(reflectionType)
}
if (reflectionType instanceof UnknownType) {
return getUnknownType(reflectionType)
}
if (reflectionType instanceof InferredType) {
return getInferredType(reflectionType)
}
if (reflectionType instanceof LiteralType) {
return getLiteralType(reflectionType)
}
return reflectionType ? escapeChars(reflectionType.toString()) : ""
}
export function getReflectionType(
model: ReflectionParameterType,
collapse: Collapse
): string {
if ("signatures" in model && model.signatures) {
return collapse === "function" || collapse === "all"
? `\`fn\``
: getFunctionType(model.signatures)
}
return collapse === "object" || collapse === "all"
? `\`object\``
: getDeclarationType(model as DeclarationReflection)
}
export function getDeclarationType(model: DeclarationReflection): string {
if (model.indexSignature || model.children) {
let indexSignature = ""
const declarationIndexSignature = model.indexSignature
if (declarationIndexSignature) {
const key = declarationIndexSignature.parameters
? declarationIndexSignature.parameters.map(
(param) => `\`[${param.name}: ${param.type}]\``
)
: ""
const obj = declarationIndexSignature.type
? getType(declarationIndexSignature.type)
: ""
indexSignature = `${key}: ${obj}; `
}
const types =
model.children &&
model.children.map((obj) => {
return `\`${obj.name}${obj.flags.isOptional ? "?" : ""}\`: ${
obj.type ? getType(obj.type) : escapeChars(obj.toString())
} ${
obj.defaultValue && obj.defaultValue !== "..."
? `= ${escapeChars(obj.defaultValue)}`
: ""
}`
})
return `{ ${indexSignature ? indexSignature : ""}${
types ? types.join("; ") : ""
} }${
model.defaultValue && model.defaultValue !== "..."
? `= ${escapeChars(model.defaultValue)}`
: ""
}`
}
return "{}"
}
export function getFunctionType(
modelSignatures: SignatureReflection[]
): string {
const functions = modelSignatures.map((fn) => {
const typeParams = fn.typeParameters
? `<${fn.typeParameters
.map((typeParameter) => typeParameter.name)
.join(", ")}\\>`
: []
const params = fn.parameters
? fn.parameters.map((param) => {
return `${param.flags.isRest ? "..." : ""}\`${param.name}${
param.flags.isOptional ? "?" : ""
}\`: ${
param.type ? getType(param.type) : escapeChars(param.toString())
}`
})
: []
const returns = fn.type ? getType(fn.type) : escapeChars(fn.toString())
return typeParams + `(${params.join(", ")}) => ${returns}`
})
return functions.join("")
}
export function getLiteralType(model: LiteralType): string {
if (typeof model.value === "bigint") {
return `\`${model.value}n\``
}
return `\`\`${JSON.stringify(model.value)}\`\``
}
export function getReferenceType(
model: ReferenceType,
emphasis: boolean
): string {
const shouldShowLink = emphasis && model.name !== "Record"
if (model.reflection || (model.name && model.typeArguments)) {
const reflection: string[] = []
if (model.reflection?.url) {
reflection.push(
shouldShowLink
? `[${`\`${model.reflection.name}\``}](${Handlebars.helpers.relativeURL(
model.reflection.url
)})`
: model.reflection.name
)
} else {
reflection.push(
shouldShowLink
? model.externalUrl
? `[${`\`${model.name}\``}]( ${model.externalUrl} )`
: `\`${model.name}\``
: model.name
)
}
if (model.typeArguments && model.typeArguments.length > 0) {
reflection.push(
`<${model.typeArguments
.map((typeArgument) => getType(typeArgument, "none", emphasis))
.join(", ")}\\>`
)
}
return reflection.join("")
}
return shouldShowLink
? model.externalUrl
? `[${`\`${model.name}\``}]( ${model.externalUrl} )`
: `\`${model.name}\``
: escapeChars(model.name)
}
export function getArrayType(model: ArrayType, emphasis: boolean): string {
const arrayType = getType(model.elementType, "none", emphasis)
return model.elementType.type === "union"
? `(${arrayType})[]`
: `${arrayType}[]`
}
export function getUnionType(model: UnionType, emphasis: boolean): string {
return model.types
.map((unionType) => getType(unionType, "none", emphasis))
.join(` \\| `)
}
export function getIntersectionType(model: IntersectionType): string {
return model.types
.map((intersectionType) => getType(intersectionType))
.join(" & ")
}
export function getTupleType(model: TupleType): string {
return `[${model.elements.map((element) => getType(element)).join(", ")}]`
}
export function getIntrinsicType(
model: IntrinsicType,
emphasis: boolean
): string {
return emphasis ? `\`${model.name}\`` : escapeChars(model.name)
}
export function getTypeOperatorType(model: TypeOperatorType): string {
return `${model.operator} ${getType(model.target)}`
}
export function getQueryType(model: QueryType): string {
return `typeof ${getType(model.queryType)}`
}
export function getInferredType(model: InferredType): string {
return `infer ${escapeChars(model.name)}`
}
export function getUnknownType(model: UnknownType): string {
return escapeChars(model.name)
}
export function getConditionalType(model: ConditionalType): string {
const md: string[] = []
if (model.checkType) {
md.push(getType(model.checkType))
}
md.push("extends")
if (model.extendsType) {
md.push(getType(model.extendsType))
}
md.push("?")
if (model.trueType) {
md.push(getType(model.trueType))
}
md.push(":")
if (model.falseType) {
md.push(getType(model.falseType))
}
return md.join(" ")
}
export function getIndexAccessType(model: IndexedAccessType): string {
const md: string[] = []
if (model.objectType) {
md.push(getType(model.objectType))
}
if (model.indexType) {
md.push(`[${getType(model.indexType)}]`)
}
return md.join("")
}
export function hasTypes(parameters: TypeParameterReflection[]) {
const types = (parameters as TypeParameterReflection[]).map(
(param) => !!param.type || !!param.default
)
return !types.every((value) => !value)
}