docs: generate events reference (#12341)

* docs: generate events reference

* change link in navbar

* fix redirect
This commit is contained in:
Shahed Nasser authored and GitHub committed 2025-05-01 19:39:53 +03:00
1 parent 332e46d1db
commit 70a99a2434
94 files changed
+31164 -18109

No files matched your search

File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
@@ -3,6 +3,7 @@ import DefaultKindGenerator, { GetDocBlockOptions } from "./default.js"
import { glob } from "glob"
import getMonorepoRoot from "../../utils/get-monorepo-root.js"
import { readFile } from "fs/promises"
import { MedusaEvent } from "types"
class EventsKindGenerator extends DefaultKindGenerator<ts.VariableDeclaration> {
protected allowedKinds: ts.SyntaxKind[] = [ts.SyntaxKind.VariableDeclaration]
@@ -33,17 +34,7 @@ class EventsKindGenerator extends DefaultKindGenerator<ts.VariableDeclaration> {
const properties = (node.initializer as ts.ObjectLiteralExpression)
.properties
const events: {
name: string
parentName: string
propertyName: string
payload: string
description?: string
workflows: string[]
version?: string
deprecated?: boolean
deprecated_message?: string
}[] = properties
const events: MedusaEvent[] = properties
.filter((property) => ts.isPropertyAssignment(property))
.map((property) => {
const propertyAssignment = property as ts.PropertyAssignment
@@ -57,6 +57,18 @@
{
"tagName": "@tags",
"syntaxKind": "block"
},
{
"tagName": "@eventName",
"syntaxKind": "block"
},
{
"tagName": "@workflows",
"syntaxKind": "block"
},
{
"tagName": "@eventPayload",
"syntaxKind": "block"
}
]
}
@@ -47,6 +47,19 @@ const customOptions: Record<string, Partial<TypeDocOptions>> = {
tsConfigName: "types.json",
name: "event",
}),
events: getOptions({
entryPointPath: "packages/core/utils/src/core-flows/events.ts",
tsConfigName: "utils.json",
name: "events",
enableEventsResolver: true,
}),
"module-events": getOptions({
entryPointPath: "packages/core/utils/src/core-flows/events.ts",
tsConfigName: "utils.json",
name: "module-events",
enableEventsResolver: true,
generateCustomNamespaces: true,
}),
file: getOptions({
entryPointPath: "packages/core/utils/src/file/abstract-file-provider.ts",
tsConfigName: "utils.json",
@@ -0,0 +1,29 @@
import { FormattingOptionsType } from "types"
const eventsOptions: FormattingOptionsType = {
"^modules/events/": {
reflectionDescription: `This documentation page includes the list of all events emitted by [Medusa's workflows](https://docs.medusajs.com/resources/medusa-workflows-reference).`,
frontmatterData: {
slug: "/references/events",
sidebar_label: "Events Reference",
},
isEventsReference: true,
reflectionTitle: {
fullReplacement: "Events Reference",
},
},
"^module_events": {
expandMembers: true,
isEventsReference: true,
reflectionDescription: `This reference shows all the events emitted by the Medusa application related to the {{alias}} Module. If you use the module outside the Medusa application, these events aren't emitted.`,
reflectionTitle: {
suffix: "Module Events Reference",
},
frontmatterData: {
slug: "/references/{{alias-slug}}/events",
sidebar_label: "Events Reference",
},
},
}
export default eventsOptions
@@ -18,6 +18,7 @@ import cacheOptions from "./cache.js"
import eventOptions from "./event.js"
import fileServiceOptions from "./file-service.js"
import notificationServiceOptions from "./notification-service.js"
import eventsOptions from "./events.js"
const mergerCustomOptions: FormattingOptionsType = {
...authProviderOptions,
@@ -25,6 +26,7 @@ const mergerCustomOptions: FormattingOptionsType = {
...coreFlowsOptions,
...dmlOptions,
...eventOptions,
...eventsOptions,
...fileServiceOptions,
...fileOptions,
...fulfillmentProviderOptions,
@@ -30,6 +30,9 @@ const allowedProjectDocuments: AllowedProjectDocumentsOption = {
[ReflectionKind.Method]: true,
[ReflectionKind.Property]: true,
},
"module-events": {
[ReflectionKind.Namespace]: true,
},
}
modules.forEach((module) => {
@@ -0,0 +1,106 @@
import { readFileSync } from "fs"
import path from "path"
import {
Application,
Comment,
CommentTag,
Context,
Converter,
DeclarationReflection,
ParameterType,
ReflectionKind,
} from "typedoc"
import { MedusaEvent } from "types"
import { getDirname } from "utils"
export class EventsResolver {
private app: Application
private events: MedusaEvent[] = []
constructor(app: Application) {
this.app = app
this.app.options.addDeclaration({
name: "enableEventsResolver",
help: "Whether to resolve events.",
type: ParameterType.Boolean,
defaultValue: false,
})
this.app.converter.on(
Converter.EVENT_RESOLVE_BEGIN,
this.resolveEvents.bind(this)
)
}
resolveEvents(context: Context) {
if (!this.app.options.getValue("enableEventsResolver")) {
return
}
if (!this.events.length) {
const __dirname = getDirname(import.meta.url)
const jsonFilePath = path.resolve(
__dirname,
path.join("..", "..", "..", "generated", "events-output.json")
)
// read events file
const eventsJSON = readFileSync(jsonFilePath, "utf-8")
this.events = JSON.parse(eventsJSON)
}
for (const reflection of context.project.getReflectionsByKind(
ReflectionKind.Variable
)) {
if (
!(reflection instanceof DeclarationReflection) ||
reflection.type?.type !== "reflection" ||
!reflection.type.declaration.children
) {
continue
}
const relatedEvents = this.events.filter(
(event) => event.parentName === reflection.name
)
if (!relatedEvents.length) {
continue
}
// loop over variable properties
for (const property of reflection.type.declaration.children) {
const propertyEvent = relatedEvents.find(
(event) => event.propertyName === property.name
)
if (!propertyEvent) {
continue
}
if (!property.comment) {
property.comment = new Comment()
}
property.comment.blockTags.push(
new CommentTag("@eventName", [
{
kind: "code",
text: propertyEvent.name,
},
])
)
property.comment.blockTags.push(
new CommentTag("@workflows", [
{
kind: "text",
text: propertyEvent.workflows.join(", "),
},
])
)
}
}
}
}
@@ -13,6 +13,7 @@ import { load as dmlTypesNormalizer } from "./dml-types-normalizer.js"
import { MermaidDiagramDMLGenerator } from "./mermaid-diagram-dml-generator.js"
import { load as dmlJsonParser } from "./dml-json-parser.js"
import { GenerateCustomNamespacePlugin } from "./generate-custom-namespaces.js"
import { EventsResolver } from "./events-resolver.js"
export function load(app: Application) {
resolveReferencesPluginLoad(app)
@@ -30,4 +31,5 @@ export function load(app: Application) {
new DmlRelationsResolver(app)
new MermaidDiagramDMLGenerator(app)
new GenerateCustomNamespacePlugin(app)
new EventsResolver(app)
}
@@ -38,6 +38,7 @@
"dependencies": {
"handlebars": "^4.7.8",
"js-beautify": "^1.15.1",
"slugify": "^1.6.6",
"utils": "*"
}
}
@@ -80,6 +80,8 @@ import versionHelper from "./resources/helpers/version.js"
import sourceCodeLinkHelper from "./resources/helpers/source-code-link.js"
import workflowExamplesHelper from "./resources/helpers/workflow-examples.js"
import stepExamplesHelper from "./resources/helpers/step-examples.js"
import ifEventsReferenceHelper from "./resources/helpers/if-events-reference.js"
import eventsListingHelper from "./resources/helpers/events-listing.js"
import { MarkdownTheme } from "./theme.js"
import { getDirname } from "utils"
@@ -191,4 +193,6 @@ export function registerHelpers(theme: MarkdownTheme) {
sourceCodeLinkHelper()
workflowExamplesHelper()
stepExamplesHelper()
ifEventsReferenceHelper(theme)
eventsListingHelper()
}
@@ -0,0 +1,142 @@
import Handlebars from "handlebars"
import pkg from "slugify"
import { DeclarationReflection } from "typedoc"
import { pascalToWords } from "utils"
const slugify = pkg.default
export default function () {
Handlebars.registerHelper(
"eventsListing",
function (this: DeclarationReflection) {
const content: string[] = []
const subtitleLevel = (this.children?.length ?? 0) > 1 ? 3 : 2
const showHeader = (this.children?.length ?? 0) > 1
this.children?.forEach((child, index) => {
content.push(
formatEventsType(child as DeclarationReflection, {
subtitleLevel,
showHeader,
})
)
if (index < this.children!.length - 1) {
content.push("")
content.push("---")
content.push("")
}
})
return content.join("\n")
}
)
}
function formatEventsType(
eventVariable: DeclarationReflection,
{
subtitleLevel = 3,
showHeader = true,
}: {
subtitleLevel?: number
showHeader?: boolean
}
) {
if (eventVariable.type?.type !== "reflection") {
return ""
}
const content: string[] = []
const subHeaderPrefix = "#".repeat(subtitleLevel)
const header = pascalToWords(
eventVariable.name.replaceAll("WorkflowEvents", "")
)
if (showHeader) {
content.push(`## ${header} Events`)
}
content.push("")
const eventProperties = eventVariable.type.declaration.children || []
content.push(`${subHeaderPrefix} Summary`)
content.push("")
// table start
content.push(`<Table>`)
// table header start
content.push(` <Table.Header>`)
content.push(` <Table.Row>`)
content.push(` <Table.HeaderCell>\nEvent\n</Table.HeaderCell>`)
content.push(` <Table.HeaderCell>\nDescription\n</Table.HeaderCell>`)
// table header end
content.push(` </Table.Row>`)
content.push(` </Table.Header>`)
// table body start
content.push(` <Table.Body>`)
eventProperties.forEach((event) => {
const eventName =
event.comment?.blockTags
.find((tag) => tag.tag === "@eventName")
?.content.map((content) => content.text)
.join("") || ""
const eventDescription = event.comment?.summary
.map((content) => content.text)
.join("")
content.push(` <Table.Row>`)
content.push(
` <Table.Cell>\n[${eventName}](#${slugify(
eventName.replace(".", ""),
{
lower: true,
}
)})\n</Table.Cell>`
)
content.push(` <Table.Cell>\n${eventDescription}\n</Table.Cell>`)
content.push(` </Table.Row>`)
})
// table body end
content.push(` </Table.Body>`)
// table end
content.push(`</Table>`)
content.push("")
eventProperties.forEach((event, index) => {
const eventName = event.comment?.blockTags
.find((tag) => tag.tag === "@eventName")
?.content.map((content) => content.text)
.join("")
const eventDescription = event.comment?.summary
.map((content) => content.text)
.join("")
const eventPayload = event.comment?.blockTags
.find((tag) => tag.tag === "@eventPayload")
?.content.map((content) => content.text)
.join("")
const workflows = event.comment?.blockTags
.find((tag) => tag.tag === "@workflows")
?.content.map((content) => content.text)
.join("")
.split(", ")
content.push(`${subHeaderPrefix} \`${eventName}\``)
content.push("")
content.push(eventDescription || "")
content.push("")
content.push(`${subHeaderPrefix}# Payload`)
content.push("")
content.push(eventPayload || "")
content.push("")
content.push(`${subHeaderPrefix}# Workflows Emitting this Event`)
content.push("")
workflows?.forEach((workflow) => {
content.push(`- [${workflow}](/references/medusa-workflows/${workflow})`)
})
content.push("")
if (index < eventProperties.length - 1) {
content.push("---")
content.push("")
}
})
return content.join("\n")
}
@@ -0,0 +1,15 @@
import Handlebars from "handlebars"
import { Reflection } from "typedoc"
import { MarkdownTheme } from "../../theme.js"
export default function (theme: MarkdownTheme) {
Handlebars.registerHelper(
"ifEventsReference",
function (this: Reflection, options: Handlebars.HelperOptions) {
const { isEventsReference = false } =
theme.getFormattingOptionsForLocation()
return isEventsReference ? options.fn(this) : options.inverse(this)
}
)
}
@@ -6,6 +6,16 @@
{{{ startSections }}}
{{#ifEventsReference}}
{{#with model}}
{{{ eventsListing }}}
{{/with}}
{{else}}
{{#with model}}
{{#if (sectionEnabled "reflection_comment")}}
@@ -148,4 +158,6 @@
{{/with}}
{{/ifEventsReference}}
{{{ endSections }}}
@@ -1,4 +1,7 @@
import { Reflection, ReflectionKind } from "typedoc"
import pkg from "slugify"
const slugify = pkg.default
export function replaceTemplateVariables(
reflection: Reflection,
@@ -11,6 +14,12 @@ export function replaceTemplateVariables(
return text
.replaceAll("{{alias}}", reflection.name)
.replaceAll("{{alias-lower}}", reflection.name.toLowerCase())
.replaceAll(
"{{alias-slug}}",
slugify(reflection.name, {
lower: true,
})
)
.replaceAll("{{parent.alias}}", reflection.parent?.name || "")
.replaceAll(
"{{parent.alias-lower}}",
+18
View File
@@ -100,6 +100,7 @@ export type FormattingOptionType = {
shouldIncrementAfterStartSections?: boolean
hideTocHeaders?: boolean
workflowDiagramComponent?: string
isEventsReference?: boolean
}
export type AllowedProjectDocumentsOption = {
@@ -289,6 +290,11 @@ export declare module "typedoc" {
* Optionally specify a name prefix for all custom namespaces.
*/
customNamespaceNamePrefix: string
/**
* Whether to resolve events.
* @defaultValue false
*/
enableEventsResolver: boolean
}
}
@@ -328,3 +334,15 @@ export declare type NamespaceGenerateDetails = {
*/
children?: NamespaceGenerateDetails[]
}
export declare type MedusaEvent = {
name: string
parentName: string
propertyName: string
payload: string
description?: string
workflows: string[]
version?: string
deprecated?: boolean
deprecated_message?: string
}
+8
View File
@@ -5556,6 +5556,13 @@ __metadata:
languageName: node
linkType: hard
"slugify@npm:^1.6.6":
version: 1.6.6
resolution: "slugify@npm:1.6.6"
checksum: e7e63f08f389a371d6228bc19d64ec84360bf0a538333446cc49dbbf3971751a6d180d2f31551188dd007a65ca771e69f574e0283290a7825a818e90b75ef44d
languageName: node
linkType: hard
"source-map@npm:^0.6.1":
version: 0.6.1
resolution: "source-map@npm:0.6.1"
@@ -6035,6 +6042,7 @@ __metadata:
copyfiles: ^2.4.1
handlebars: ^4.7.8
js-beautify: ^1.15.1
slugify: ^1.6.6
typedoc: ^0.27.5
types: "*"
typescript: ^5.6.2