docs: generate medusa-react reference (#6004)

* add new plugin for better organization

* added handling in theme for mutations and query types

* added tsdoc to hooks

* added tsdocs to utility functions

* added tsdoc to providers

* generated reference

* general fixes for generated reference

* generated api reference specs + general fixes

* add missing import react

* split utilities into different directories

* added overview page

* added link to customer authentication section

* fix lint errors

* added changeset

* fix readme

* fixed build error

* added expand fields + other sections to overview

* updated what's new section

* general refactoring

* remove unnecessary query field

* fix links

* added ignoreApi option
This commit is contained in:
Shahed Nasser authored and GitHub committed 2024-01-05 17:03:38 +02:00
1 parent 6fc6a9de6a
commit 7d650771d1
2811 files changed
+231856 -455063

No files matched your search

@@ -128,10 +128,10 @@ export default class TypedocManager {
const signature = mappedSignature.signatures[0]
// get the props of the component from the
// first parameter in the signature.
const props = getTypeChildren(
signature.parameters![0].type!,
this.project
)
const props = getTypeChildren({
reflectionType: signature.parameters![0].type!,
project: this.project,
})
// this stores props that should be removed from the
// spec
@@ -482,7 +482,10 @@ export default class TypedocManager {
childName
) as DeclarationReflection
} else if (reflection.type) {
getTypeChildren(reflection.type, this.project).some((child) => {
getTypeChildren({
reflectionType: reflection.type,
project: this.project,
}).some((child) => {
if (child.name === childName) {
childReflection = child
return true
@@ -61,6 +61,7 @@ export function generateReference(referenceName: string) {
formatColoredLog(colorLog, referenceName, chunk.trim())
})
typedocProcess.on("exit", (code) => {
formatColoredLog(colorLog, referenceName, "Finished Generating reference.")
generatedCount++
if (generatedCount >= totalCount && !ranMerger && code !== 1) {
runMerger()
@@ -81,7 +82,6 @@ export function generateReference(referenceName: string) {
)
})
})
formatColoredLog(colorLog, referenceName, "Finished Generating reference.")
}
function formatColoredLog(
+111 -21
View File
@@ -64,6 +64,7 @@ module.exports = {
"stock-location",
"workflows",
],
allReflectionsHaveOwnDocumentInNamespace: ["Utilities"],
formatting: {
"*": {
showCommentsAsHeader: true,
@@ -131,7 +132,7 @@ module.exports = {
displayed_sidebar: "inventoryReference",
},
},
"^IInventoryService/methods": {
"^inventory/IInventoryService/methods": {
reflectionDescription:
"This documentation provides a reference to the `{{alias}}` {{kind}}. This belongs to the Inventory Module.",
frontmatterData: {
@@ -189,29 +190,118 @@ module.exports = {
"^js_client/.*LineItemsResource": {
maxLevel: 3,
},
"^(js_client/.*modules/.*internal|internal.*/modules)": {
reflectionGroups: {
Constructors: false,
"Type Aliases": false,
Enumerations: false,
"Enumeration Members": false,
Classes: false,
Functions: false,
Interfaces: false,
References: false,
// MEDUSA REACT CONFIG
"^medusa_react": {
frontmatterData: {
displayed_sidebar: "medusaReactSidebar",
},
parameterComponentExtraProps: {
expandUrl:
"https://docs.medusajs.com/medusa-react/overview#expanding-fields",
},
},
"^internal.*/.*js_client.*": {
reflectionGroups: {
Constructors: false,
},
"^modules/medusa_react\\.mdx": {
frontmatterData: {
displayed_sidebar: "jsClientSidebar",
displayed_sidebar: "medusaReactSidebar",
},
reflectionGroups: {
Variables: false,
Functions: false,
},
reflectionCategories: {
Mutations: false,
Queries: false,
Other: false,
},
},
"^medusa_react/(medusa_react\\.Hooks\\.mdx|.*medusa_react\\.Hooks\\.Admin\\.mdx|.*medusa_react\\.Hooks\\.Store\\.mdx|medusa_react\\.Providers\\.mdx)":
{
reflectionGroups: {
Functions: false,
},
},
"^medusa_react/Providers/.*": {
expandMembers: true,
frontmatterData: {
displayed_sidebar: "medusaReactSidebar",
slug: "/references/medusa-react/providers/{{alias-lower}}",
sidebar_label: "{{alias}}",
},
reflectionTitle: {
suffix: " Provider Overview",
},
},
"^medusa_react/medusa_react\\.Utilities": {
expandMembers: true,
reflectionTitle: {
prefix: "Medusa React ",
},
},
"^medusa_react/Utilities/.*": {
expandMembers: true,
frontmatterData: {
displayed_sidebar: "medusaReactSidebar",
slug: "/references/medusa-react/utilities/{{alias}}",
},
},
"^medusa_react/medusa_react\\.Hooks\\.mdx": {
frontmatterData: {
displayed_sidebar: "medusaReactSidebar",
slug: "/references/medusa-react/hooks",
},
},
"^medusa_react/Hooks/Admin/.*Admin\\.mdx": {
frontmatterData: {
displayed_sidebar: "medusaReactSidebar",
slug: "/references/medusa-react/hooks/admin",
},
},
"^medusa_react/Hooks/Admin/.*": {
frontmatterData: {
displayed_sidebar: "medusaReactSidebar",
slug: "/references/medusa-react/hooks/admin/{{alias-lower}}",
},
},
"^medusa_react/Hooks/Store/.*Store\\.mdx": {
frontmatterData: {
displayed_sidebar: "medusaReactSidebar",
slug: "/references/medusa-react/hooks/store",
},
},
"^medusa_react/Hooks/Store/.*": {
frontmatterData: {
displayed_sidebar: "medusaReactSidebar",
slug: "/references/medusa-react/hooks/store/{{alias-lower}}",
},
},
"^medusa_react/medusa_react\\.Providers\\.mdx": {
frontmatterData: {
displayed_sidebar: "medusaReactSidebar",
slug: "/references/medusa-react/providers",
},
},
"^medusa_react/medusa_react\\.Utilities\\.mdx": {
frontmatterData: {
displayed_sidebar: "medusaReactSidebar",
slug: "/references/medusa-react/utilities",
},
},
"^medusa_react/Hooks/.*Admin\\.Inventory_Items": {
maxLevel: 4,
},
"^medusa_react/Hooks/.*Admin\\.Products": {
maxLevel: 4,
},
"^medusa_react/Hooks/.*Admin\\.Stock_Locations": {
maxLevel: 5,
},
"^medusa_react/Hooks/.*Admin\\.Users": {
maxLevel: 5,
},
// MEDUSA CONFIG
"^medusa": {
"^medusa/": {
frontmatterData: {
displayed_sidebar: "homepage",
},
@@ -249,13 +339,13 @@ module.exports = {
},
// PRICING CONFIG
"^(pricing|IPricingModuleService)": {
"^pricing": {
...modulesOptions,
frontmatterData: {
displayed_sidebar: "pricingReference",
},
},
"^IPricingModuleService/methods": {
"^pricing/IPricingModuleService/methods": {
reflectionDescription:
"This documentation provides a reference to the `{{alias}}` {{kind}}. This belongs to the Pricing Module.",
frontmatterData: {
@@ -298,7 +388,7 @@ module.exports = {
displayed_sidebar: "productReference",
},
},
"^IProductModuleService/methods": {
"^product/IProductModuleService/methods": {
reflectionDescription:
"This documentation provides a reference to the {{alias}} {{kind}}. This belongs to the Product Module.",
frontmatterData: {
@@ -349,7 +439,7 @@ module.exports = {
displayed_sidebar: "stockLocationReference",
},
},
"^IStockLocationService/methods": {
"^stock-location/IStockLocationService/methods": {
reflectionDescription:
"This documentation provides a reference to the `{{alias}}` {{kind}}. This belongs to the Stock Location Module.",
frontmatterData: {
@@ -0,0 +1,9 @@
{
"$schema": "http://json.schemastore.org/tsconfig",
"extends": [
"../../../../packages/medusa-react/tsconfig.json"
],
"compilerOptions": {
"noImplicitReturns": false,
}
}
@@ -33,6 +33,18 @@
{
"tagName": "@excludeExternal",
"syntaxKind": "modifier"
},
{
"tagName": "@customNamespace",
"syntaxKind": "block"
},
{
"tagName": "@namespaceMember",
"syntaxKind": "modifier"
},
{
"tagName": "@typeParamDefinition",
"syntaxKind": "block"
}
]
}
@@ -0,0 +1,10 @@
/* eslint-disable @typescript-eslint/no-var-requires */
const getConfig = require("./utils/get-config")
module.exports = getConfig({
entryPointPath: "packages/medusa-react/src/index.ts",
tsConfigName: "medusa-react.json",
name: "medusa-react",
generateNamespaces: true,
ignoreApi: true,
})
@@ -35,6 +35,15 @@ The following options are useful for linting:
- `eslintPathName`: The path to the ESLint configuration file.
- `pluginsResolvePath`: The path to resolve plugins used in the ESLint configuration files.
### Generate Namespace Plugin
If the `generateNamespaces` option is enabled, Namespaces are created from reflections having the `@customNamespace` tag. It also attaches categories (using the `@category` tag) of the same reflection to its generated parent namespace.
It also accepts the following options:
- `parentNamespace`: The name of a parent namespace to make the generated namespaces as its children.
- `namePrefix`: A prefix to add to the name of the generated namespaces.
---
## Build the Plugin
@@ -0,0 +1,258 @@
import {
Application,
Comment,
CommentDisplayPart,
CommentTag,
Context,
Converter,
DeclarationReflection,
ParameterType,
ReflectionCategory,
ReflectionKind,
} from "typedoc"
type PluginOptions = {
generateNamespaces: boolean
parentNamespace: string
namePrefix: string
}
export class GenerateNamespacePlugin {
private options?: PluginOptions
private app: Application
private parentNamespace?: DeclarationReflection
private currentNamespaceHeirarchy: DeclarationReflection[]
private currentContext?: Context
private scannedComments = false
constructor(app: Application) {
this.app = app
this.currentNamespaceHeirarchy = []
this.declareOptions()
this.app.converter.on(
Converter.EVENT_CREATE_DECLARATION,
this.handleCreateDeclarationEvent.bind(this)
)
this.app.converter.on(
Converter.EVENT_CREATE_DECLARATION,
this.scanComments.bind(this)
)
}
declareOptions() {
this.app.options.addDeclaration({
name: "generateNamespaces",
type: ParameterType.Boolean,
defaultValue: false,
help: "Whether to enable conversion of categories to namespaces.",
})
this.app.options.addDeclaration({
name: "parentNamespace",
type: ParameterType.String,
defaultValue: "",
help: "Optionally specify a parent namespace to place all generated namespaces in.",
})
this.app.options.addDeclaration({
name: "namePrefix",
type: ParameterType.String,
defaultValue: "",
help: "Optionally specify a name prefix for all namespaces.",
})
}
readOptions() {
if (this.options) {
return
}
this.options = {
generateNamespaces: this.app.options.getValue("generateNamespaces"),
parentNamespace: this.app.options.getValue("parentNamespace"),
namePrefix: this.app.options.getValue("namePrefix"),
}
}
loadNamespace(namespaceName: string): DeclarationReflection {
const formattedName = this.formatName(namespaceName)
return this.currentContext?.project
.getReflectionsByKind(ReflectionKind.Namespace)
.find(
(m) =>
m.name === formattedName &&
(!this.currentNamespaceHeirarchy.length ||
m.parent?.id ===
this.currentNamespaceHeirarchy[
this.currentNamespaceHeirarchy.length - 1
].id)
) as DeclarationReflection
}
createNamespace(namespaceName: string): DeclarationReflection | undefined {
if (!this.currentContext) {
return
}
const formattedName = this.formatName(namespaceName)
const namespace = this.currentContext?.createDeclarationReflection(
ReflectionKind.Namespace,
void 0,
void 0,
formattedName
)
namespace.children = []
return namespace
}
formatName(namespaceName: string): string {
return `${this.options?.namePrefix}${namespaceName}`
}
generateNamespaceFromTag({
tag,
summary,
}: {
tag: CommentTag
reflection?: DeclarationReflection
summary?: CommentDisplayPart[]
}) {
const categoryHeirarchy = tag.content[0].text.split(".")
categoryHeirarchy.forEach((cat, index) => {
// check whether a namespace exists with the category name.
let namespace = this.loadNamespace(cat)
if (!namespace) {
// add a namespace for this category
namespace = this.createNamespace(cat) || namespace
namespace.comment = new Comment()
if (this.currentNamespaceHeirarchy.length) {
namespace.comment.modifierTags.add("@namespaceMember")
}
if (summary && index === categoryHeirarchy.length - 1) {
namespace.comment.summary = summary
}
}
this.currentContext =
this.currentContext?.withScope(namespace) || this.currentContext
this.currentNamespaceHeirarchy.push(namespace)
})
}
/**
* create categories in the last namespace if the
* reflection has a category
*/
attachCategories(reflection: DeclarationReflection) {
if (!this.currentNamespaceHeirarchy.length) {
return
}
const parentNamespace =
this.currentNamespaceHeirarchy[this.currentNamespaceHeirarchy.length - 1]
reflection.comment?.blockTags
.filter((tag) => tag.tag === "@category")
.forEach((tag) => {
const categoryName = tag.content[0].text
if (!parentNamespace.categories) {
parentNamespace.categories = []
}
let category = parentNamespace.categories.find(
(category) => category.title === categoryName
)
if (!category) {
category = new ReflectionCategory(categoryName)
parentNamespace.categories.push(category)
}
category.children.push(reflection)
})
}
handleCreateDeclarationEvent(
context: Context,
reflection: DeclarationReflection
) {
this.readOptions()
if (this.options?.parentNamespace && !this.parentNamespace) {
this.parentNamespace =
this.loadNamespace(this.options.parentNamespace) ||
this.createNamespace(this.options.parentNamespace)
}
this.currentNamespaceHeirarchy = []
if (this.parentNamespace) {
this.currentNamespaceHeirarchy.push(this.parentNamespace)
}
this.currentContext = context
reflection.comment?.blockTags
.filter((tag) => tag.tag === "@customNamespace")
.forEach((tag) => {
this.generateNamespaceFromTag({
tag,
})
if (
reflection.parent instanceof DeclarationReflection ||
reflection.parent?.isProject()
) {
reflection.parent.children = reflection.parent.children?.filter(
(child) => child.id !== reflection.id
)
}
this.currentContext?.addChild(reflection)
})
reflection.comment?.removeTags("@customNamespace")
this.attachCategories(reflection)
this.currentContext = undefined
this.currentNamespaceHeirarchy = []
}
/**
* Scan all source files for `@customNamespace` tag to generate namespaces
* This is mainly helpful to pull summaries of the namespaces.
*/
scanComments(context: Context) {
if (this.scannedComments) {
return
}
this.currentContext = context
const fileNames = context.program.getRootFileNames()
fileNames.forEach((fileName) => {
const sourceFile = context.program.getSourceFile(fileName)
if (!sourceFile) {
return
}
const comments = context.getFileComment(sourceFile)
comments?.blockTags
.filter((tag) => tag.tag === "@customNamespace")
.forEach((tag) => {
this.generateNamespaceFromTag({ tag, summary: comments.summary })
if (this.currentNamespaceHeirarchy.length) {
// add comments of the file to the last created namespace
this.currentNamespaceHeirarchy[
this.currentNamespaceHeirarchy.length - 1
].comment = comments
this.currentNamespaceHeirarchy[
this.currentNamespaceHeirarchy.length - 1
].comment!.blockTags = this.currentNamespaceHeirarchy[
this.currentNamespaceHeirarchy.length - 1
].comment!.blockTags.filter((tag) => tag.tag !== "@customNamespace")
}
// reset values
this.currentNamespaceHeirarchy = []
this.currentContext = context
})
})
this.scannedComments = true
}
// for debugging
printCurrentHeirarchy() {
return this.currentNamespaceHeirarchy.map((heirarchy) => heirarchy.name)
}
}
@@ -5,6 +5,7 @@ import { load as parseOasSchemaPlugin } from "./parse-oas-schema-plugin"
import { load as apiIgnorePlugin } from "./api-ignore"
import { load as eslintExamplePlugin } from "./eslint-example"
import { load as signatureModifierPlugin } from "./signature-modifier"
import { GenerateNamespacePlugin } from "./generate-namespace"
export function load(app: Application) {
resolveReferencesPluginLoad(app)
@@ -13,4 +14,6 @@ export function load(app: Application) {
apiIgnorePlugin(app)
eslintExamplePlugin(app)
signatureModifierPlugin(app)
new GenerateNamespacePlugin(app)
}
@@ -178,7 +178,10 @@ function addComments(schema: Schema, reflection: Reflection) {
}
const children =
"type" in reflection
? getTypeChildren(reflection.type as SomeType, reflection.project)
? getTypeChildren({
reflectionType: reflection.type as SomeType,
project: reflection.project,
})
: "children" in reflection
? (reflection.children as DeclarationReflection[])
: []
@@ -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:
- `allReflectionsHaveOwnDocumentInNamespace`: An array of namespace names whose child members should have their own documents.
- `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.
@@ -40,6 +40,13 @@ export function load(app: Application) {
defaultValue: [],
})
app.options.addDeclaration({
help: "[Markdown Plugin] Specify namespace names where all reflections are outputted into seperate files.",
name: "allReflectionsHaveOwnDocumentInNamespace",
type: ParameterType.Array,
defaultValue: [],
})
app.options.addDeclaration({
help: "[Markdown Plugin] Separator used to format filenames.",
name: "filenameSeparator",
@@ -49,10 +49,17 @@ import decrementCurrentTitleLevelHelper from "./resources/helpers/decrement-curr
import incrementCurrentTitleLevelHelper from "./resources/helpers/increment-current-title-level"
import hasMoreThanOneSignatureHelper from "./resources/helpers/has-more-than-one-signature"
import ifCanShowConstructorsTitleHelper from "./resources/helpers/if-can-show-constructors-title"
import ifReactQueryTypeHelper from "./resources/helpers/if-react-query-type"
import ifHasHookParamsHelper from "./resources/helpers/if-has-hook-params"
import reactQueryHookParamsHelper from "./resources/helpers/react-query-hook-params"
import ifHasMutationParamsHelper from "./resources/helpers/if-has-mutation-params"
import reactQueryMutationParamsHelper from "./resources/helpers/react-query-mutation-params"
import ifHasMutationReturnHelper from "./resources/helpers/if-has-mutation-return"
import reactQueryMutationReturnHelper from "./resources/helpers/react-query-mutation-return"
import ifHasQueryReturnHelper from "./resources/helpers/if-has-query-return"
import reactQueryQueryReturnHelper from "./resources/helpers/react-query-query-return"
import { MarkdownTheme } from "./theme"
// test
const TEMPLATE_PATH = path.join(__dirname, "resources", "templates")
export const indexTemplate = Handlebars.compile(
@@ -128,4 +135,13 @@ export function registerHelpers(theme: MarkdownTheme) {
incrementCurrentTitleLevelHelper(theme)
hasMoreThanOneSignatureHelper(theme)
ifCanShowConstructorsTitleHelper()
ifReactQueryTypeHelper()
ifHasHookParamsHelper()
reactQueryHookParamsHelper()
ifHasMutationParamsHelper(theme)
reactQueryMutationParamsHelper(theme)
ifHasMutationReturnHelper(theme)
reactQueryMutationReturnHelper(theme)
ifHasQueryReturnHelper(theme)
reactQueryQueryReturnHelper(theme)
}
@@ -1,7 +1,13 @@
import * as Handlebars from "handlebars"
import { Comment } from "typedoc"
const EXCLUDED_TAGS = ["@returns", "@example", "@featureFlag"]
const EXCLUDED_TAGS = [
"@returns",
"@example",
"@featureFlag",
"@category",
"@typeParamDefinition",
]
export default function () {
Handlebars.registerHelper(
@@ -0,0 +1,14 @@
import * as Handlebars from "handlebars"
import { SignatureReflection } from "typedoc"
import { getHookParams } from "../../utils/react-query-utils"
export default function () {
Handlebars.registerHelper(
"ifHasHookParams",
function (this: SignatureReflection, options: Handlebars.HelperOptions) {
return getHookParams(this).length
? options.fn(this)
: options.inverse(this)
}
)
}
@@ -0,0 +1,23 @@
import * as Handlebars from "handlebars"
import { SignatureReflection } from "typedoc"
import { getMutationParams } from "../../utils/react-query-utils"
import { MarkdownTheme } from "../../theme"
export default function (theme: MarkdownTheme) {
Handlebars.registerHelper(
"ifHasMutationParams",
function (this: SignatureReflection, options: Handlebars.HelperOptions) {
return getMutationParams({
signatureReflection: this,
project: theme.project || this.project,
reflectionTypeGetterOptions: {
// we only need to check if there are mutation parameters,
// so no need to get all the children
maxLevel: 1,
},
}).length
? options.fn(this)
: options.inverse(this)
}
)
}
@@ -0,0 +1,23 @@
import * as Handlebars from "handlebars"
import { SignatureReflection } from "typedoc"
import { getMutationReturn } from "../../utils/react-query-utils"
import { MarkdownTheme } from "../../theme"
export default function (theme: MarkdownTheme) {
Handlebars.registerHelper(
"ifHasMutationReturn",
function (this: SignatureReflection, options: Handlebars.HelperOptions) {
return getMutationReturn({
signatureReflection: this,
project: theme.project || this.project,
reflectionTypeGetterOptions: {
// we only need to check if there are mutation returns,
// so no need to get all the children
maxLevel: 1,
},
}).length
? options.fn(this)
: options.inverse(this)
}
)
}
@@ -0,0 +1,23 @@
import * as Handlebars from "handlebars"
import { SignatureReflection } from "typedoc"
import { getQueryReturn } from "../../utils/react-query-utils"
import { MarkdownTheme } from "../../theme"
export default function (theme: MarkdownTheme) {
Handlebars.registerHelper(
"ifHasQueryReturn",
function (this: SignatureReflection, options: Handlebars.HelperOptions) {
return getQueryReturn({
signatureReflection: this,
project: theme.project || this.project,
reflectionTypeGetterOptions: {
// we only need to check if there are query returns,
// so no need to get all the children
maxLevel: 1,
},
}).length
? options.fn(this)
: options.inverse(this)
}
)
}
@@ -0,0 +1,17 @@
import * as Handlebars from "handlebars"
import { SignatureReflection } from "typedoc"
import {
isReactQueryMutation,
isReactQueryQuery,
} from "../../utils/react-query-utils"
export default function () {
Handlebars.registerHelper(
"ifReactQueryType",
function (this: SignatureReflection, options: Handlebars.HelperOptions) {
return isReactQueryMutation(this) || isReactQueryQuery(this)
? options.fn(this)
: options.inverse(this)
}
)
}
@@ -3,6 +3,7 @@ import { ReflectionParameterType } from "../../types"
import { parseParams } from "../../utils/params-utils"
import { MarkdownTheme } from "../../theme"
import { reflectionComponentFormatter } from "../../utils/reflection-formatter"
import { formatParameterComponent } from "../../utils/format-parameter-component"
export default function (theme: MarkdownTheme) {
Handlebars.registerHelper(
@@ -21,19 +22,11 @@ export default function (theme: MarkdownTheme) {
})
)
let extraProps: string[] = []
if (parameterComponentExtraProps) {
extraProps = Object.entries(parameterComponentExtraProps).map(
([key, value]) => `${key}=${JSON.stringify(value)}`
)
}
return `<${parameterComponent} parameters={${JSON.stringify(
parameters,
null,
2
)}} ${extraProps.join(" ")}/>`
return formatParameterComponent({
parameterComponent,
componentItems: parameters,
extraProps: parameterComponentExtraProps,
})
}
)
}
@@ -0,0 +1,19 @@
import * as Handlebars from "handlebars"
import { SignatureReflection } from "typedoc"
import { getHookParams } from "../../utils/react-query-utils"
export default function () {
Handlebars.registerHelper(
"reactQueryHookParams",
function (this: SignatureReflection) {
let parametersStr = ""
const hookParameters = getHookParams(this)
if (hookParameters?.length) {
parametersStr = Handlebars.helpers.parameter.call(hookParameters)
}
return parametersStr
}
)
}
@@ -0,0 +1,37 @@
import * as Handlebars from "handlebars"
import { SignatureReflection } from "typedoc"
import { getMutationParams } from "../../utils/react-query-utils"
import { MarkdownTheme } from "../../theme"
import { formatParameterComponent } from "../../utils/format-parameter-component"
export default function (theme: MarkdownTheme) {
Handlebars.registerHelper(
"reactQueryMutationParams",
function (this: SignatureReflection) {
const {
parameterStyle,
parameterComponent,
maxLevel,
parameterComponentExtraProps,
} = theme.getFormattingOptionsForLocation()
const mutationParameters = getMutationParams({
signatureReflection: this,
project: theme.project || this.project,
reflectionTypeGetterOptions: {
maxLevel,
},
})
if (parameterStyle !== "component") {
// TODO maybe handle other cases? But for now it's not important
return ""
}
return formatParameterComponent({
parameterComponent,
componentItems: mutationParameters,
extraProps: parameterComponentExtraProps,
})
}
)
}
@@ -0,0 +1,37 @@
import * as Handlebars from "handlebars"
import { SignatureReflection } from "typedoc"
import { getMutationReturn } from "../../utils/react-query-utils"
import { MarkdownTheme } from "../../theme"
import { formatParameterComponent } from "../../utils/format-parameter-component"
export default function (theme: MarkdownTheme) {
Handlebars.registerHelper(
"reactQueryMutationReturn",
function (this: SignatureReflection) {
const {
parameterStyle,
parameterComponent,
maxLevel,
parameterComponentExtraProps,
} = theme.getFormattingOptionsForLocation()
const mutationParameters = getMutationReturn({
signatureReflection: this,
project: theme.project || this.project,
reflectionTypeGetterOptions: {
maxLevel,
},
})
if (parameterStyle !== "component") {
// TODO maybe handle other cases? But for now it's not important
return ""
}
return formatParameterComponent({
parameterComponent,
componentItems: mutationParameters,
extraProps: parameterComponentExtraProps,
})
}
)
}
@@ -0,0 +1,37 @@
import * as Handlebars from "handlebars"
import { SignatureReflection } from "typedoc"
import { getQueryReturn } from "../../utils/react-query-utils"
import { MarkdownTheme } from "../../theme"
import { formatParameterComponent } from "../../utils/format-parameter-component"
export default function (theme: MarkdownTheme) {
Handlebars.registerHelper(
"reactQueryQueryReturn",
function (this: SignatureReflection) {
const {
parameterStyle,
parameterComponent,
maxLevel,
parameterComponentExtraProps,
} = theme.getFormattingOptionsForLocation()
const mutationParameters = getQueryReturn({
signatureReflection: this,
project: theme.project || this.project,
reflectionTypeGetterOptions: {
maxLevel,
},
})
if (parameterStyle !== "component") {
// TODO maybe handle other cases? But for now it's not important
return ""
}
return formatParameterComponent({
parameterComponent,
componentItems: mutationParameters,
extraProps: parameterComponentExtraProps,
})
}
)
}
@@ -3,6 +3,7 @@ import { PageEvent, ParameterReflection, ReflectionKind } from "typedoc"
import { MarkdownTheme } from "../../theme"
import { getDisplayName } from "../../utils"
import { escapeChars } from "utils"
import { replaceTemplateVariables } from "../../utils/reflection-template-strings"
export default function (theme: MarkdownTheme) {
Handlebars.registerHelper(
@@ -12,10 +13,16 @@ export default function (theme: MarkdownTheme) {
const { reflectionTitle } = theme.getFormattingOptionsForLocation()
if (reflectionTitle?.fullReplacement?.length) {
return reflectionTitle.fullReplacement
return theme.reflection
? replaceTemplateVariables(
theme.reflection,
reflectionTitle.fullReplacement
)
: reflectionTitle.fullReplacement
}
const title: string[] = [""]
const title: string[] = [reflectionTitle?.prefix || ""]
if (
reflectionTitle?.kind &&
this.model?.kind &&
@@ -2,8 +2,9 @@ import * as Handlebars from "handlebars"
import { Comment, DeclarationReflection, SignatureReflection } from "typedoc"
import { MarkdownTheme } from "../../theme"
import reflectionFormatter from "../../utils/reflection-formatter"
import { returnReflectionComponentFormatter } from "../../utils/return-reflection-formatter"
import { getReflectionTypeParameters } from "../../utils/reflection-type-parameters"
import { Parameter } from "../../types"
import { formatParameterComponent } from "../../utils/format-parameter-component"
export default function (theme: MarkdownTheme) {
Handlebars.registerHelper(
@@ -24,14 +25,18 @@ function getReturnFromType(
theme: MarkdownTheme,
reflection: SignatureReflection
) {
const { parameterStyle, parameterComponent, maxLevel } =
theme.getFormattingOptionsForLocation()
const {
parameterStyle,
parameterComponent,
maxLevel,
parameterComponentExtraProps,
} = theme.getFormattingOptionsForLocation()
if (!reflection.type) {
return ""
}
const componentItems = returnReflectionComponentFormatter({
const componentItems = getReflectionTypeParameters({
reflectionType: reflection.type,
project: reflection.project || theme.project,
comment: reflection.comment,
@@ -40,11 +45,11 @@ function getReturnFromType(
})
if (parameterStyle === "component") {
return `<${parameterComponent} parameters={${JSON.stringify(
return formatParameterComponent({
parameterComponent,
componentItems,
null,
2
)}} />`
extraProps: parameterComponentExtraProps,
})
} else {
return formatReturnAsList(componentItems)
}
@@ -74,8 +79,12 @@ function formatReturnAsList(componentItems: Parameter[], level = 1): string {
function getReturnFromComment(theme: MarkdownTheme, comment: Comment) {
const md: string[] = []
const { parameterStyle, parameterComponent, maxLevel } =
theme.getFormattingOptionsForLocation()
const {
parameterStyle,
parameterComponent,
maxLevel,
parameterComponentExtraProps,
} = theme.getFormattingOptionsForLocation()
if (comment.blockTags?.length) {
const tags = comment.blockTags
@@ -97,11 +106,14 @@ function getReturnFromComment(theme: MarkdownTheme, comment: Comment) {
)
result +=
parameterStyle === "component"
? `\n\n<${parameterComponent} parameters={${JSON.stringify(
content,
null,
2
)}} title={"${commentPart.target.name}"} />\n\n`
? `\n\n${formatParameterComponent({
parameterComponent,
componentItems: content as Parameter[],
extraProps: {
...parameterComponentExtraProps,
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>`
@@ -2,15 +2,16 @@ import * as Handlebars from "handlebars"
import { DeclarationReflection, ReflectionType } from "typedoc"
import { MarkdownTheme } from "../../theme"
import { parseParams } from "../../utils/params-utils"
import { ReflectionParameterType } from "../../types"
import { Parameter, ReflectionParameterType } from "../../types"
import reflectionFormatter from "../../utils/reflection-formatter"
import { escapeChars, stripLineBreaks } from "utils"
import { formatParameterComponent } from "../../utils/format-parameter-component"
export default function (theme: MarkdownTheme) {
Handlebars.registerHelper(
"typeDeclarationMembers",
function (this: DeclarationReflection[]) {
const { parameterComponent, maxLevel } =
const { parameterComponent, maxLevel, parameterComponentExtraProps } =
theme.getFormattingOptionsForLocation()
const comments = this.map(
(param) => !!param.comment?.hasVisibleComponent()
@@ -30,11 +31,12 @@ export default function (theme: MarkdownTheme) {
break
}
case "component": {
result = getComponentMarkdownContent(
result = getComponentMarkdownContent({
properties,
parameterComponent,
maxLevel
)
maxLevel,
parameterComponentExtraProps,
})
break
}
case "table": {
@@ -58,11 +60,17 @@ function getListMarkdownContent(properties: DeclarationReflection[]) {
return items.join("\n")
}
function getComponentMarkdownContent(
properties: DeclarationReflection[],
parameterComponent?: string,
function getComponentMarkdownContent({
properties,
parameterComponent,
maxLevel,
parameterComponentExtraProps,
}: {
properties: DeclarationReflection[]
parameterComponent?: string
maxLevel?: number | undefined
) {
parameterComponentExtraProps?: Record<string, unknown>
}) {
const parameters = properties.map((property) =>
reflectionFormatter({
reflection: property,
@@ -72,11 +80,11 @@ function getComponentMarkdownContent(
})
)
return `<${parameterComponent} parameters={${JSON.stringify(
parameters,
null,
2
)}} />`
return formatParameterComponent({
parameterComponent,
componentItems: parameters as Parameter[],
extraProps: parameterComponentExtraProps,
})
}
function getTableMarkdownContent(
@@ -2,12 +2,13 @@ import * as Handlebars from "handlebars"
import { TypeParameterReflection } from "typedoc"
import { reflectionComponentFormatter } from "../../utils/reflection-formatter"
import { MarkdownTheme } from "../../theme"
import { formatParameterComponent } from "../../utils/format-parameter-component"
export default function (theme: MarkdownTheme) {
Handlebars.registerHelper(
"typeParameterComponent",
function (this: TypeParameterReflection[]) {
const { parameterComponent, maxLevel } =
const { parameterComponent, maxLevel, parameterComponentExtraProps } =
theme.getFormattingOptionsForLocation()
const parameters = this.map((parameter) =>
reflectionComponentFormatter({
@@ -17,11 +18,11 @@ export default function (theme: MarkdownTheme) {
})
)
return `<${parameterComponent} parameters={${JSON.stringify(
parameters,
null,
2
)}} />`
return formatParameterComponent({
parameterComponent,
componentItems: parameters,
extraProps: parameterComponentExtraProps,
})
}
)
}
@@ -70,7 +70,7 @@
{{incrementCurrentTitleLevel}}
{{> member.signature showSources=false parent=../type.declaration }}
{{> member.signature-wrapper showSources=false parent=../type.declaration }}
{{decrementCurrentTitleLevel}}
@@ -4,7 +4,7 @@
{{#with getSignature}}
{{> member.signature accessor="get" showSources=true }}
{{> member.signature-wrapper accessor="get" showSources=true }}
{{/with}}
@@ -18,7 +18,7 @@
{{#with setSignature}}
{{> member.signature accessor="set" showSources=true }}
{{> member.signature-wrapper accessor="set" showSources=true }}
{{/with}}
@@ -16,7 +16,7 @@
{{#each signatures}}
{{> member.signature showSources=true parent=.. }}
{{> member.signature-wrapper showSources=true parent=.. }}
{{/each}}
@@ -58,9 +58,11 @@
{{#unless @last}}
{{#unless hasOwnDocument}}
{{#unless removeSeparator}}
___
{{/unless}}
{{/unless}}
{{/unless}}
{{#unless hasOwnDocument}}
@@ -0,0 +1,125 @@
{{{signatureTitle accessor parent}}}
{{#if (getFormattingOption "expandMembers")}}
{{#if (hasMoreThanOneSignature parent)}}
{{incrementCurrentTitleLevel}}
{{/if}}
{{/if}}
{{#if (sectionEnabled "member_signature_comment")}}
{{#with comment}}
{{#if hasVisibleComponent}}
{{{comments this true false}}}
{{/if}}
{{/with}}
{{/if}}
{{#if (sectionEnabled "member_signature_example")}}
{{{example this}}}
{{/if}}
{{#if (sectionEnabled "member_signature_typeParameters")}}
{{#if typeParameters}}
{{{titleLevel}}} Type Parameters
{{#with typeParameters}}
{{{typeParameter}}}
{{/with}}
{{/if}}
{{/if}}
{{#if (sectionEnabled "member_signature_parameters")}}
{{#ifHasHookParams}}
{{{titleLevel}}} Hook Parameters
{{{reactQueryHookParams}}}
{{/ifHasHookParams}}
{{#ifHasMutationParams}}
{{{titleLevel}}} Mutation Function Parameters
{{{reactQueryMutationParams}}}
{{/ifHasMutationParams}}
{{/if}}
{{#ifShowReturns}}
{{#ifHasMutationReturn}}
{{{titleLevel}}} Mutation Function Returned Data
{{{reactQueryMutationReturn}}}
{{/ifHasMutationReturn}}
{{#ifHasQueryReturn}}
{{{titleLevel}}} Query Returned Data
{{{reactQueryQueryReturn}}}
{{/ifHasQueryReturn}}
{{/ifShowReturns}}
{{#if (sectionEnabled "member_signature_comment")}}
{{#with comment}}
{{#if hasVisibleComponent}}
{{{comments this false true ..}}}
{{/if}}
{{/with}}
{{/if}}
{{#if (sectionEnabled "member_signature_sources")}}
{{#if showSources}}
{{incrementCurrentTitleLevel}}
{{> member.sources}}
{{decrementCurrentTitleLevel}}
{{/if}}
{{/if}}
{{#if (getFormattingOption "expandMembers")}}
{{#if (hasMoreThanOneSignature parent)}}
{{decrementCurrentTitleLevel}}
{{/if}}
{{/if}}
@@ -0,0 +1,9 @@
{{#ifReactQueryType}}
{{> member.react-query.signature}}
{{else}}
{{> member.signature}}
{{/ifReactQueryType}}
@@ -90,7 +90,7 @@
{{#each declaration.signatures}}
{{> member.signature showSources=false parent=../declaration }}
{{> member.signature-wrapper showSources=false parent=../declaration }}
{{/each}}
@@ -4,6 +4,10 @@
{{#each categories}}
{{#unless @first}}
___
{{/unless}}
{{#unless allChildrenHaveOwnDocument}}
{{#unless (getFormattingOption "expandMembers")}}
@@ -18,7 +22,7 @@
{{#unless hasOwnDocument}}
{{> member}}
{{> member removeSeparator=true}}
{{/unless}}
@@ -100,7 +100,7 @@
{{incrementCurrentTitleLevel}}
{{> member.signature showSources=true }}
{{> member.signature-wrapper showSources=true }}
{{decrementCurrentTitleLevel}}
@@ -30,6 +30,7 @@ import { Mapping } from "./types"
export class MarkdownTheme extends Theme {
allReflectionsHaveOwnDocument!: string[]
allReflectionsHaveOwnDocumentInNamespace: string[]
entryDocument: string
entryPoints!: string[]
filenameSeparator!: string
@@ -66,6 +67,9 @@ export class MarkdownTheme extends Theme {
// prettier-ignore
this.allReflectionsHaveOwnDocument = this.getOption("allReflectionsHaveOwnDocument") as string[]
this.allReflectionsHaveOwnDocumentInNamespace = this.getOption(
"allReflectionsHaveOwnDocumentInNamespace"
) as string[]
this.entryDocument = this.getOption("entryDocument") as string
this.entryPoints = this.getOption("entryPoints") as string[]
this.filenameSeparator = this.getOption("filenameSeparator") as string
@@ -143,14 +147,36 @@ export class MarkdownTheme extends Theme {
return urls
}
getAliasPath(reflection: Reflection): string {
return path.join(
reflection.parent && !reflection.parent.isProject()
? this.getAliasPath(reflection.parent)
: "",
reflection.getAlias()
)
}
buildUrls(
reflection: DeclarationReflection,
urls: UrlMapping[]
): UrlMapping[] {
const mapping = this.getMappings(
reflection,
reflection.parent?.isProject() ? "" : reflection.parent?.getAlias()
).find((mapping) => reflection.kindOf(mapping.kind))
reflection.parent?.isProject() || !reflection.parent
? ""
: this.getAliasPath(reflection.parent)
).find(
(mapping) =>
reflection.kindOf(mapping.kind) &&
mapping.modifiers.has.every(
(modifier) => reflection.comment?.hasModifier(modifier) || false
) &&
mapping.modifiers.not.every((modifier) => {
return reflection.comment
? !reflection.comment.hasModifier(modifier)
: true
})
)
if (mapping) {
if (!reflection.url || !MarkdownTheme.URL_PREFIX.test(reflection.url)) {
const url = this.toUrl(mapping, reflection)
@@ -176,12 +202,23 @@ export class MarkdownTheme extends Theme {
return (
mapping.directory +
"/" +
this.getUrl(reflection) +
this.getUrl({
reflection,
directory: mapping.directory,
}) +
(this.mdxOutput ? ".mdx" : ".md")
)
}
getUrl(reflection: Reflection, relative?: Reflection): string {
getUrl({
reflection,
directory,
relative,
}: {
reflection: Reflection
directory: string
relative?: Reflection
}): string {
let url = reflection.getAlias()
if (
@@ -189,8 +226,15 @@ export class MarkdownTheme extends Theme {
reflection.parent !== relative &&
!(reflection.parent instanceof ProjectReflection)
) {
url =
this.getUrl(reflection.parent, relative) + this.filenameSeparator + url
const urlPrefix = this.getUrl({
reflection: reflection.parent,
directory,
relative,
})
const fileNameSeparator = this.getFileNameSeparator(
`${directory}/${urlPrefix}`
)
url = urlPrefix + fileNameSeparator + url
}
return url.replace(/^_/, "")
@@ -280,11 +324,14 @@ export class MarkdownTheme extends Theme {
}
}
getModuleParents(reflection: DeclarationReflection): DeclarationReflection[] {
getParentsOfKind(
reflection: DeclarationReflection,
kind: ReflectionKind
): DeclarationReflection[] {
const parents: DeclarationReflection[] = []
let currentParent = reflection?.parent as DeclarationReflection | undefined
do {
if (currentParent?.kind === ReflectionKind.Module) {
if (currentParent?.kind === kind) {
parents.push(currentParent)
}
currentParent = currentParent?.parent as DeclarationReflection | undefined
@@ -294,11 +341,28 @@ export class MarkdownTheme extends Theme {
}
getAllReflectionsHaveOwnDocument(reflection: DeclarationReflection): boolean {
const moduleParents = this.getModuleParents(reflection)
return moduleParents.some((parent) =>
this.allReflectionsHaveOwnDocument.includes(parent.name)
const moduleParents = this.getParentsOfKind(
reflection,
ReflectionKind.Module
)
const namespaceParents = this.getParentsOfKind(
reflection,
ReflectionKind.Namespace
)
return (
moduleParents.some((parent) =>
this.allReflectionsHaveOwnDocument.includes(parent.name)
) ||
namespaceParents.some((parent) =>
this.allReflectionsHaveOwnDocumentInNamespace.includes(parent.name)
)
)
}
getFileNameSeparator(pathPrefix: string): string {
const formattingOptions = this.getFormattingOptions(pathPrefix)
return formattingOptions.fileNameSeparator || this.filenameSeparator
}
getMappings(
@@ -308,36 +372,60 @@ export class MarkdownTheme extends Theme {
return [
{
kind: [ReflectionKind.Module],
modifiers: {
has: [],
not: [],
},
isLeaf: false,
directory: path.join(directoryPrefix || "", "modules"),
template: this.getReflectionTemplate(),
},
{
kind: [ReflectionKind.Namespace],
modifiers: {
has: [],
not: [],
},
isLeaf: false,
directory: path.join(directoryPrefix || "", "modules"),
directory: path.join(directoryPrefix || ""),
template: this.getReflectionTemplate(),
},
{
kind: [ReflectionKind.Enum],
modifiers: {
has: [],
not: [],
},
isLeaf: false,
directory: path.join(directoryPrefix || "", "enums"),
template: this.getReflectionTemplate(),
},
{
kind: [ReflectionKind.Class],
modifiers: {
has: [],
not: [],
},
isLeaf: false,
directory: path.join(directoryPrefix || "", "classes"),
template: this.getReflectionTemplate(),
},
{
kind: [ReflectionKind.Interface],
modifiers: {
has: [],
not: [],
},
isLeaf: false,
directory: path.join(directoryPrefix || "", "interfaces"),
template: this.getReflectionTemplate(),
},
{
kind: [ReflectionKind.TypeAlias],
modifiers: {
has: [],
not: [],
},
isLeaf: true,
directory: path.join(directoryPrefix || "", "types"),
template: this.getReflectionMemberTemplate(),
@@ -346,18 +434,30 @@ export class MarkdownTheme extends Theme {
? [
{
kind: [ReflectionKind.Variable],
modifiers: {
has: [],
not: [],
},
isLeaf: true,
directory: path.join(directoryPrefix || "", "variables"),
template: this.getReflectionMemberTemplate(),
},
{
kind: [ReflectionKind.Function],
modifiers: {
has: [],
not: [],
},
isLeaf: true,
directory: path.join(directoryPrefix || "", "functions"),
template: this.getReflectionMemberTemplate(),
},
{
kind: [ReflectionKind.Method],
modifiers: {
has: [],
not: [],
},
isLeaf: true,
directory: path.join(directoryPrefix || "", "methods"),
template: this.getReflectionMemberTemplate(),
@@ -393,6 +493,25 @@ export class MarkdownTheme extends Theme {
page.model instanceof ProjectReflection
) {
this.removeGroups(page.model)
this.removeCategories(page.model)
this.sortCategories(page.model)
}
if (
this.reflection instanceof DeclarationReflection &&
this.reflection.parent instanceof ProjectReflection
) {
const namespacesGroup = this.reflection.groups?.find(
(group) => group.title === "Namespaces"
)
if (namespacesGroup) {
// remove the namespaces that have the `@namespaceMember` modifier
namespacesGroup.children = namespacesGroup.children.filter((child) => {
return child.comment
? !child.comment.hasModifier("@namespaceMember")
: true
})
}
}
if (
@@ -428,6 +547,34 @@ export class MarkdownTheme extends Theme {
})
}
protected removeCategories(
model?: DeclarationReflection | ProjectReflection
) {
if (!model?.categories) {
return
}
const options = this.getFormattingOptionsForLocation()
model.categories = model.categories.filter((category) => {
return (
!options.reflectionCategories ||
!(category.title in options.reflectionCategories) ||
options.reflectionCategories[category.title]
)
})
}
protected sortCategories(model?: DeclarationReflection | ProjectReflection) {
if (!model?.categories) {
return
}
model.categories.sort((categoryA, categoryB) => {
return categoryA.title.localeCompare(categoryB.title)
})
}
get globalsFile() {
return `modules.${this.mdxOutput ? "mdx" : "md"}`
}
@@ -441,10 +588,14 @@ export class MarkdownTheme extends Theme {
return {}
}
return this.getFormattingOptions(this.location)
}
getFormattingOptions(location: string): FormattingOptionType {
const applicableOptions: FormattingOptionType[] = []
Object.keys(this.formattingOptions).forEach((key) => {
if (key === "*" || new RegExp(key).test(this.location)) {
if (key === "*" || new RegExp(key).test(location)) {
applicableOptions.push(this.formattingOptions[key])
}
})
@@ -16,6 +16,10 @@ export type ReflectionParameterType =
export type Mapping = {
kind: ReflectionKind[]
modifiers: {
has: `@${string}`[]
not: `@${string}`[]
}
isLeaf: boolean
directory: string
template: (pageEvent: PageEvent<ContainerReflection>) => string
@@ -0,0 +1,58 @@
import { Parameter } from "../types"
type FormatParameterComponentProps = {
parameterComponent: string | undefined
componentItems: Parameter[]
extraProps?: Record<string, unknown>
}
/**
* Function passed to an a Parameter array's sort method. It sorts
* parameter items by whether they're optional or not. So, required items
* are placed first in the array.
*/
function componentItemsSorter(a: Parameter, b: Parameter): number {
if (a.optional) {
return 1
}
if (b.optional) {
return -1
}
return 0
}
/**
* Function that goes through items and their children to apply the
* {@link componentItemsSorter} function on them and sort the items.
*/
function sortComponentItems(items: Parameter[]): Parameter[] {
items.sort(componentItemsSorter)
items = items.map((item) => {
if (item.children) {
item.children = sortComponentItems(item.children)
}
return item
})
return items
}
export function formatParameterComponent({
parameterComponent,
componentItems,
extraProps,
}: FormatParameterComponentProps): string {
let extraPropsArr: string[] = []
if (extraProps) {
extraPropsArr = Object.entries(extraProps).map(
([key, value]) => `${key}=${JSON.stringify(value)}`
)
}
// reorder component items to show required items first
componentItems = sortComponentItems(componentItems)
return `<${parameterComponent} parameters={${JSON.stringify(
componentItems
)}} ${extraPropsArr.join(" ")}/>`
}
@@ -0,0 +1,263 @@
import {
Comment,
ParameterReflection,
ProjectReflection,
ReferenceType,
SignatureReflection,
SomeType,
} from "typedoc"
import { Parameter } from "../types"
import {
GetReflectionTypeParametersParams,
getReflectionTypeParameters,
} from "./reflection-type-parameters"
const MUTATION_PARAM_TYPE_NAME = "UseMutationOptions"
const MUTATION_RETURN_TYPE_NAME = "UseMutationResult"
const QUERY_PARAM_TYPE_NAME = "UseQueryOptionsWrapper"
const BLACKLISTED_PARAM_NAMES = ["constructor", "void"]
export function isReactQueryMutation(
signatureReflection: SignatureReflection
): boolean {
return (
signatureReflection.type?.type === "reference" &&
signatureReflection.type.name === MUTATION_RETURN_TYPE_NAME
)
}
export function getReactQueryQueryParameterType(
signatureReflection: SignatureReflection
): ReferenceType | undefined {
let parameterType: ReferenceType | undefined
signatureReflection.parameters?.some((parameter) => {
if (
parameter.type?.type === "reference" &&
parameter.type.name === QUERY_PARAM_TYPE_NAME
) {
parameterType = parameter.type
return true
}
return false
})
return parameterType
}
export function isReactQueryQuery(
signatureReflection: SignatureReflection
): boolean {
return getReactQueryQueryParameterType(signatureReflection) !== undefined
}
/**
* Some parameter types are declared as classes which shows their constructor as
* an accepted parameter. This removes the constructor from the list of parameters.
*/
export function cleanUpParameterTypes(parameters: Parameter[]): Parameter[] {
return parameters.filter((parameter, index) => {
const keep = !BLACKLISTED_PARAM_NAMES.includes(parameter.name)
if (keep && parameter.children) {
parameters[index].children = cleanUpParameterTypes(parameter.children)
}
return keep
})
}
export function getHookParams(
signatureReflection: SignatureReflection
): ParameterReflection[] {
let hookParameters: ParameterReflection[] = []
if (isReactQueryMutation(signatureReflection)) {
/**
* check parameters before the options parameter having the {@link MUTATION_PARAM_TYPE_NAME} type
*/
hookParameters =
signatureReflection.parameters?.filter((parameter) => {
return (
parameter.type?.type !== "reference" ||
parameter.type.name !== MUTATION_PARAM_TYPE_NAME
)
}) || []
} else if (isReactQueryQuery(signatureReflection)) {
/**
* check parameters before the options parameter having the {@link QUERY_PARAM_TYPE_NAME} type
*/
hookParameters =
signatureReflection.parameters?.filter((parameter) => {
return (
parameter.type?.type !== "reference" ||
parameter.type.name !== QUERY_PARAM_TYPE_NAME
)
}) || []
}
return hookParameters
}
/**
* Load comments of a type param from the signature's comments if it has the
* `@typeParamDefinition` tag.
*/
function loadTypeComments(
signatureReflection: SignatureReflection,
typeName: string
): Comment | undefined {
const tagRegex = /^(?<typeName>\w+) - (?<typeDef>.*)$/
const typeComment = new Comment()
signatureReflection.comment?.blockTags
.filter((tag) => tag.tag === "@typeParamDefinition")
.some((tag) => {
return tag.content.some((tagContent) => {
const match = tagContent.text.match(tagRegex)
if (match?.groups?.typeName === typeName && match?.groups?.typeDef) {
typeComment.summary.push({
kind: "text",
text: match.groups.typeDef,
})
return true
}
return false
})
})
return typeComment.summary.length > 0 ? typeComment : undefined
}
type GetMutationParamsParams = {
signatureReflection: SignatureReflection
project: ProjectReflection
reflectionTypeGetterOptions?: Partial<GetReflectionTypeParametersParams>
}
type GetParamsOptions = GetMutationParamsParams & {
reflectionType: SomeType
}
function getParams({
signatureReflection,
reflectionType,
reflectionTypeGetterOptions,
...rest
}: GetParamsOptions): Parameter[] {
const comment =
"name" in reflectionType
? loadTypeComments(signatureReflection, reflectionType.name)
: reflectionTypeGetterOptions?.comment
return cleanUpParameterTypes(
getReflectionTypeParameters({
reflectionType,
comment,
isReturn: false,
...reflectionTypeGetterOptions,
...rest,
})
)
}
export function getMutationParams({
signatureReflection,
reflectionTypeGetterOptions,
project,
}: GetMutationParamsParams): Parameter[] {
let mutationParameters: Parameter[] = []
if (isReactQueryMutation(signatureReflection)) {
const mutationOptionParam = signatureReflection.parameters?.find(
(parameter) => {
return (
parameter.type?.type === "reference" &&
parameter.type.name === MUTATION_PARAM_TYPE_NAME
)
}
)
if (
mutationOptionParam?.type?.type === "reference" &&
(mutationOptionParam.type.typeArguments?.length || 0) >= 3
) {
mutationParameters = getParams({
signatureReflection,
project,
reflectionType: mutationOptionParam.type.typeArguments![2],
reflectionTypeGetterOptions: {
...reflectionTypeGetterOptions,
wrapObject:
mutationOptionParam.type.typeArguments![2].type === "reference",
},
})
}
}
return mutationParameters
}
export function hasResponseTypeArgument(type: ReferenceType): boolean {
return (
(type.typeArguments?.length || 0) >= 1 &&
type.typeArguments![0].type === "reference" &&
type.typeArguments![0].name === "Response" &&
(type.typeArguments![0].typeArguments?.length || 0) >= 1
)
}
type GetMutationReturnParams = GetMutationParamsParams
export function getMutationReturn({
signatureReflection,
reflectionTypeGetterOptions,
project,
}: GetMutationReturnParams): Parameter[] {
let returnParams: Parameter[] = []
if (!isReactQueryMutation(signatureReflection)) {
return returnParams
}
const returnType = signatureReflection.type! as ReferenceType
if (hasResponseTypeArgument(returnType)) {
returnParams = getParams({
signatureReflection,
project,
reflectionType: (returnType.typeArguments![0] as ReferenceType)
.typeArguments![0],
reflectionTypeGetterOptions: {
...reflectionTypeGetterOptions,
wrapObject:
(returnType.typeArguments![0] as ReferenceType).typeArguments![0]
.type === "reference",
},
})
}
return returnParams
}
type GetQueryReturnParams = GetMutationParamsParams
export function getQueryReturn({
signatureReflection,
reflectionTypeGetterOptions,
project,
}: GetQueryReturnParams): Parameter[] {
let returnParams: Parameter[] = []
const parameterType = getReactQueryQueryParameterType(signatureReflection)
if (parameterType && hasResponseTypeArgument(parameterType)) {
returnParams = getParams({
signatureReflection,
project,
reflectionType: (parameterType.typeArguments![0] as ReferenceType)
.typeArguments![0],
reflectionTypeGetterOptions,
})
}
return returnParams
}
@@ -85,7 +85,10 @@ export function reflectionListFormatter({
) {
const children = hasChildren
? reflection.children
: getTypeChildren(reflection.type!, reflection.project)
: getTypeChildren({
reflectionType: reflection.type!,
project: reflection.project,
})
const itemChildren: string[] = []
let itemChildrenKind: ReflectionKind | null = null
children?.forEach((childItem: DeclarationReflection) => {
@@ -156,7 +159,11 @@ export function reflectionComponentFormatter({
) {
const children = hasChildren
? reflection.children
: getTypeChildren(reflection.type!, project || reflection.project)
: getTypeChildren({
reflectionType: reflection.type!,
project: project || reflection.project,
maxLevel,
})
children
?.filter((childItem: DeclarationReflection) =>
@@ -10,6 +10,16 @@ export function replaceTemplateVariables(
return text
.replaceAll("{{alias}}", reflection.getAlias())
.replaceAll("{{alias-lower}}", reflection.getAlias().toLowerCase())
.replaceAll("{{parent.alias}}", reflection.parent?.getAlias() || "")
.replaceAll(
"{{parent.alias-lower}}",
reflection.parent?.getAlias().toLowerCase() || ""
)
.replaceAll(
"{{parent.parent.alias}}",
reflection.parent?.parent?.getAlias() || ""
)
.replaceAll("{{kind}}", getKindAsText(reflection.kind))
}
@@ -15,21 +15,25 @@ import {
import { MarkdownTheme } from "../theme"
import { getProjectChild, getType, getTypeChildren } from "utils"
type ReturnReflectionComponentFormatterParams = {
export type GetReflectionTypeParametersParams = {
reflectionType: SomeType
project: ProjectReflection
comment?: Comment
level: number
level?: number
maxLevel?: number | undefined
wrapObject?: boolean
isReturn?: boolean
}
export function returnReflectionComponentFormatter({
export function getReflectionTypeParameters({
reflectionType,
project,
comment,
level = 1,
maxLevel,
}: ReturnReflectionComponentFormatterParams): Parameter[] {
wrapObject = false,
isReturn = true,
}: GetReflectionTypeParametersParams): Parameter[] {
const typeName = getType({
reflectionType,
collapse: "object",
@@ -39,11 +43,6 @@ export function returnReflectionComponentFormatter({
project,
getRelativeUrlMethod: Handlebars.helpers.relativeURL,
})
// if (
// typeName === "Record&#60;string, unknown&#62; \\| PaymentProcessorError"
// ) {
// console.log(reflectionType)
// }
const type = getType({
reflectionType: reflectionType,
collapse: "object",
@@ -51,28 +50,39 @@ export function returnReflectionComponentFormatter({
escape: true,
getRelativeUrlMethod: Handlebars.helpers.relativeURL,
})
const formatParameter = () => {
return {
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
? isReturn
? getReturnComment(comment)
: Handlebars.helpers.comment(comment.summary)
: !comment && !isReturn
? loadComment(typeName, project)
: "",
expandable: comment?.hasModifier(`@expandable`) || false,
featureFlag: Handlebars.helpers.featureFlag(comment),
children: [],
}
}
const componentItem: Parameter[] = []
const canRetrieveChildren = level + 1 <= (maxLevel || MarkdownTheme.MAX_LEVEL)
if (reflectionType.type === "reference") {
if (reflectionType.typeArguments || reflectionType.refersToTypeParameter) {
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) : "",
expandable: comment?.hasModifier(`@expandable`) || false,
featureFlag: Handlebars.helpers.featureFlag(comment),
children: [],
})
const parentKey = componentItem.push(formatParameter())
const typeArgs = reflectionType.typeArguments
? reflectionType.typeArguments
: "typeParameters" in reflectionType
@@ -89,7 +99,7 @@ export function returnReflectionComponentFormatter({
if (!reflectionTypeArg) {
return
}
const typeArgComponent = returnReflectionComponentFormatter({
const typeArgComponent = getReflectionTypeParameters({
reflectionType: reflectionTypeArg,
project,
level: level + 1,
@@ -102,75 +112,53 @@ export function returnReflectionComponentFormatter({
} else {
const reflection = (reflectionType.reflection ||
getProjectChild(project, reflectionType.name)) as DeclarationReflection
const parentKey = wrapObject
? componentItem.push(formatParameter())
: undefined
if (reflection) {
const reflectionChildren = canRetrieveChildren
? reflection.children ||
getTypeChildren(reflectionType, project, level)
getTypeChildren({
reflectionType,
project,
level,
maxLevel,
})
: undefined
if (reflectionChildren?.length) {
reflectionChildren.forEach((childItem) => {
componentItem.push(
reflectionComponentFormatter({
reflection: childItem as DeclarationReflection,
level,
maxLevel,
project,
})
)
})
} else {
componentItem.push(
reflectionComponentFormatter({
reflection,
const childParameter = reflectionComponentFormatter({
reflection: childItem as DeclarationReflection,
level,
maxLevel,
project,
})
)
parentKey
? componentItem[parentKey - 1].children?.push(childParameter)
: componentItem.push(childParameter)
})
} else {
const childParameter = reflectionComponentFormatter({
reflection,
level,
maxLevel,
project,
})
parentKey
? componentItem[parentKey - 1].children?.push(childParameter)
: componentItem.push(childParameter)
}
} else {
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) : "",
expandable: comment?.hasModifier(`@expandable`) || false,
featureFlag: Handlebars.helpers.featureFlag(comment),
children: [],
})
parentKey
? componentItem[parentKey - 1].children?.push(formatParameter())
: componentItem.push(formatParameter())
}
}
} 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) : "",
expandable: comment?.hasModifier(`@expandable`) || false,
featureFlag: Handlebars.helpers.featureFlag(comment),
children: [],
})
const parentKey = componentItem.push(formatParameter())
if (canRetrieveChildren) {
const elementTypeItem = returnReflectionComponentFormatter({
const elementTypeItem = getReflectionTypeParameters({
reflectionType: reflectionType.elementType,
project,
level: level + 1,
@@ -181,32 +169,19 @@ export function returnReflectionComponentFormatter({
} 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) : "",
expandable: comment?.hasModifier(`@expandable`) || false,
featureFlag: Handlebars.helpers.featureFlag(comment),
children: [],
})
const parentKey = componentItem.push(formatParameter())
pushTo = componentItem[parentKey - 1].children!
} else {
pushTo = componentItem
const parentKey = wrapObject
? componentItem.push(formatParameter())
: undefined
pushTo = parentKey
? componentItem[parentKey - 1].children!
: componentItem
}
if (canRetrieveChildren) {
reflectionType.elements.forEach((element) => {
const elementTypeItem = returnReflectionComponentFormatter({
const elementTypeItem = getReflectionTypeParameters({
reflectionType: element,
project,
level: level + 1,
@@ -215,24 +190,47 @@ export function returnReflectionComponentFormatter({
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) : "",
expandable: comment?.hasModifier(`@expandable`) || false,
featureFlag: Handlebars.helpers.featureFlag(comment),
children: [],
} else if (reflectionType.type === "intersection") {
const parentKey = wrapObject
? componentItem.push(formatParameter())
: undefined
reflectionType.types.forEach((childType) => {
const childParameter = getReflectionTypeParameters({
reflectionType: childType,
project,
level: level + 1,
maxLevel,
})
parentKey
? componentItem[parentKey - 1].children?.push(...childParameter)
: componentItem.push(...childParameter)
})
} else if (reflectionType.type === "reflection") {
const parentKey =
type === "`object`" && typeName === "object"
? undefined
: componentItem.push(formatParameter())
reflectionType.declaration.children?.forEach((childItem) => {
const childParameter = reflectionComponentFormatter({
reflection: childItem as DeclarationReflection,
level,
maxLevel,
project,
})
parentKey
? componentItem[parentKey - 1].children?.push(childParameter)
: componentItem.push(childParameter)
})
} else {
const parentKey = wrapObject
? componentItem.push(formatParameter())
: undefined
// put type as the only component.
parentKey
? componentItem[parentKey - 1].children?.push(formatParameter())
: componentItem.push(formatParameter())
}
return componentItem
@@ -249,6 +247,23 @@ export function getReturnComment(comment: Comment): string {
.join("\n\n")
}
export function loadComment(
reflectionTypeName: string,
project: ProjectReflection
): string {
// try to load reflection from project
const reflection = getProjectChild(project, reflectionTypeName)
if (reflection) {
return (
reflection.comment?.summary
.map((summaryPart) => summaryPart.text)
.join("\n\n") || ""
)
}
return ""
}
export function isOnlyVoid(reflectionTypes: SomeType[]) {
return reflectionTypes.every(
(type) => type.type === "intrinsic" && type.name === "void"
+27
View File
@@ -50,9 +50,13 @@ export type FormattingOptionType = {
reflectionGroups?: {
[k: string]: boolean
}
reflectionCategories?: {
[k: string]: boolean
}
reflectionTitle?: {
kind: boolean
typeParameters: boolean
prefix?: string
suffix?: string
fullReplacement?: string
}
@@ -66,6 +70,7 @@ export type FormattingOptionType = {
parameterComponentExtraProps?: Record<string, unknown>
mdxImports?: string[]
maxLevel?: number
fileNameSeparator?: string
}
export declare module "typedoc" {
@@ -178,5 +183,27 @@ export declare module "typedoc" {
* @defaultValue true
*/
outputModules: boolean
/**
* Whether to enable category to namespace conversion.
* @defaultValue false
*/
generateNamespaces: boolean
/**
* Optionally specify a parent namespace to place all generated namespaces in.
*/
parentNamespace: string
/**
* Optionally specify a name prefix for all generated namespaces.
*/
namePrefix: string
/**
* Whether to enable the React Query manipulator.
* @defaultValue false
*/
enableReactQueryManipulator: boolean
/**
* Namespace names whose child members should have their own documents.
*/
allReflectionsHaveOwnDocumentInNamespace: string[]
}
}
@@ -1,23 +1,37 @@
/* eslint-disable no-case-declarations */
import { DeclarationReflection, ProjectReflection, SomeType } from "typedoc"
import { getProjectChild } from "./get-project-child"
const MAX_LEVEL = 3
type GetTypeChildrenOptions = {
reflectionType: SomeType
project: ProjectReflection | undefined
level?: number
maxLevel?: number
}
export function getTypeChildren(
reflectionType: SomeType,
project: ProjectReflection | undefined,
level = 1
): DeclarationReflection[] {
export function getTypeChildren({
reflectionType,
project,
level = 1,
maxLevel = 3,
}: GetTypeChildrenOptions): DeclarationReflection[] {
let children: DeclarationReflection[] = []
if (level > MAX_LEVEL) {
if (level > maxLevel) {
return children
}
switch (reflectionType.type) {
case "intersection":
reflectionType.types.forEach((intersectionType) => {
children.push(...getTypeChildren(intersectionType, project, level + 1))
children.push(
...getTypeChildren({
reflectionType: intersectionType,
project,
level: level + 1,
maxLevel,
})
)
})
break
case "reference":
@@ -36,13 +50,16 @@ export function getTypeChildren(
if (referencedReflection.children) {
children = referencedReflection.children
} else if (referencedReflection.type) {
children = getTypeChildren(
referencedReflection.type,
children = getTypeChildren({
reflectionType: referencedReflection.type,
project,
level + 1
)
level: level + 1,
maxLevel,
})
}
} else if (reflectionType.typeArguments?.length) {
// Only useful if the reflection type is `Pick<...>`.
const toKeepChildren: string[] = []
reflectionType.typeArguments.forEach((typeArgument, index) => {
if (reflectionType.name === "Omit" && index > 0) {
switch (typeArgument.type) {
@@ -54,23 +71,69 @@ export function getTypeChildren(
if (childItem.type === "literal") {
removeChild(childItem.value?.toString(), children)
} else {
getTypeChildren(childItem, project, level + 1).forEach(
(child) => {
removeChild(child.name, children)
}
)
getTypeChildren({
reflectionType: childItem,
project,
level: level + 1,
maxLevel,
}).forEach((child) => {
removeChild(child.name, children)
})
}
})
}
} else if (reflectionType.name === "Pick") {
if (index === 0 && !children.length) {
children = getTypeChildren({
reflectionType: typeArgument,
project,
level: level + 1,
maxLevel,
})
} else {
switch (typeArgument.type) {
case "literal":
if (typeArgument.value) {
toKeepChildren.push(typeArgument.value?.toString())
}
break
case "union":
typeArgument.types.forEach((childItem) => {
if (childItem.type === "literal") {
if (childItem.value) {
toKeepChildren.push(childItem.value?.toString())
}
} else {
getTypeChildren({
reflectionType: childItem,
project,
level: level + 1,
maxLevel,
}).forEach((child) => {
if (child.name) {
toKeepChildren.push(child.name)
}
})
}
})
}
}
} else {
const typeArgumentChildren = getTypeChildren(
typeArgument,
const typeArgumentChildren = getTypeChildren({
reflectionType: typeArgument,
project,
level + 1
)
level: level + 1,
maxLevel,
})
children.push(...typeArgumentChildren)
}
})
if (toKeepChildren.length) {
children = children.filter((child) =>
toKeepChildren.includes(child.name)
)
}
}
break
case "reflection":
@@ -79,7 +142,12 @@ export function getTypeChildren(
]
break
case "array":
children = getTypeChildren(reflectionType.elementType, project, level + 1)
children = getTypeChildren({
reflectionType: reflectionType.elementType,
project,
level: level + 1,
maxLevel,
})
}
return filterChildren(children)
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff