docs-util: add support for workflows in markdown theme (#8485)
* add template for workflows * initial changes * added support for parallel steps * added support for when * added merge options * fix merge options * fix to tooltip * clean up * remove redirects * fixes * theme fixes + added merge options * generate hook examples + fixes * changed namespaces * add custom autogenerator * change type of additional data
This commit is contained in:
@@ -8,6 +8,7 @@ import { baseOptions } from "./base-options.js"
|
||||
import path from "path"
|
||||
import { rootPathPrefix } from "./general.js"
|
||||
import { modules } from "./references.js"
|
||||
import { getCoreFlowNamespaces } from "../utils/get-namespaces.js"
|
||||
|
||||
const customOptions: Record<string, Partial<TypeDocOptions>> = {
|
||||
"core-flows": getOptions({
|
||||
@@ -18,20 +19,7 @@ const customOptions: Record<string, Partial<TypeDocOptions>> = {
|
||||
enableWorkflowsPlugins: true,
|
||||
enableNamespaceGenerator: true,
|
||||
// @ts-expect-error there's a typing issue in typedoc
|
||||
generateNamespaces: [
|
||||
{
|
||||
name: "Workflows",
|
||||
description:
|
||||
"Workflows listed here are created by Medusa and can be imported from `@medusajs/core-flows`.",
|
||||
pathPattern: "**/packages/core/core-flows/**/workflows/**",
|
||||
},
|
||||
{
|
||||
name: "Steps",
|
||||
description:
|
||||
"Steps listed here are created by Medusa and can be imported from `@medusajs/core-flows`.",
|
||||
pathPattern: "**/packages/core/core-flows/**/steps/**",
|
||||
},
|
||||
],
|
||||
generateNamespaces: getCoreFlowNamespaces(),
|
||||
}),
|
||||
"auth-provider": getOptions({
|
||||
entryPointPath: "packages/core/utils/src/auth/abstract-auth-provider.ts",
|
||||
|
||||
+77
@@ -0,0 +1,77 @@
|
||||
import { FormattingOptionsType } from "types"
|
||||
import baseSectionsOptions from "../base-section-options.js"
|
||||
|
||||
const coreFlowsOptions: FormattingOptionsType = {
|
||||
"^core_flows": {
|
||||
expandMembers: true,
|
||||
sections: {
|
||||
...baseSectionsOptions,
|
||||
member_getterSetter: false,
|
||||
},
|
||||
workflowDiagramComponent: "WorkflowDiagram",
|
||||
mdxImports: [`import { TypeList, WorkflowDiagram } from "docs-ui"`],
|
||||
},
|
||||
"^modules/core_flows/page\\.mdx": {
|
||||
reflectionDescription:
|
||||
"This section of the documentation provides a reference to Medusa's workflows and steps that you can use in your customizations.",
|
||||
reflectionGroups: {
|
||||
Namespaces: true,
|
||||
Enumerations: false,
|
||||
Classes: false,
|
||||
Interfaces: false,
|
||||
"Type Aliases": false,
|
||||
Variables: false,
|
||||
"Enumeration Members": false,
|
||||
Functions: false,
|
||||
},
|
||||
hideTocHeaders: true,
|
||||
frontmatterData: {
|
||||
slug: "/references/medusa-workflows",
|
||||
},
|
||||
reflectionTitle: {
|
||||
fullReplacement: "Medusa Workflows API Reference",
|
||||
},
|
||||
},
|
||||
"^core_flows/.*/.*(Workflows|Steps)/page\\.mdx": {
|
||||
expandMembers: false,
|
||||
reflectionGroups: {
|
||||
Variables: false,
|
||||
Properties: false,
|
||||
"Type Literals": false,
|
||||
},
|
||||
sections: {
|
||||
...baseSectionsOptions,
|
||||
member_getterSetter: false,
|
||||
members_categories: false,
|
||||
},
|
||||
hideTocHeaders: true,
|
||||
},
|
||||
"^core_flows/.*Workflows/functions/.*/page\\.mdx": {
|
||||
reflectionDescription:
|
||||
"This documentation provides a reference to the `{{alias}}`. It belongs to the `@medusajs/core-flows` package.",
|
||||
frontmatterData: {
|
||||
slug: "/references/medusa-workflows/{{alias}}",
|
||||
sidebar_label: "{{alias}}",
|
||||
},
|
||||
reflectionTitle: {
|
||||
kind: false,
|
||||
typeParameters: false,
|
||||
suffix: "- Medusa Workflows API Reference",
|
||||
},
|
||||
},
|
||||
"^core_flows/.*Steps/functions/.*/page\\.mdx": {
|
||||
reflectionDescription:
|
||||
"This documentation provides a reference to the `{{alias}}`. It belongs to the `@medusajs/core-flows` package.",
|
||||
frontmatterData: {
|
||||
slug: "/references/medusa-workflows/steps/{{alias}}",
|
||||
sidebar_label: "{{alias}}",
|
||||
},
|
||||
reflectionTitle: {
|
||||
kind: false,
|
||||
typeParameters: false,
|
||||
suffix: "- Medusa Workflows API Reference",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
export default coreFlowsOptions
|
||||
+2
@@ -11,9 +11,11 @@ import searchOptions from "./search.js"
|
||||
import taxProviderOptions from "./tax-provider.js"
|
||||
import workflowsOptions from "./workflows.js"
|
||||
import dmlOptions from "./dml.js"
|
||||
import coreFlowsOptions from "./core-flows.js"
|
||||
|
||||
const mergerCustomOptions: FormattingOptionsType = {
|
||||
...authProviderOptions,
|
||||
...coreFlowsOptions,
|
||||
...dmlOptions,
|
||||
...fileOptions,
|
||||
...fulfillmentProviderOptions,
|
||||
|
||||
@@ -12,6 +12,10 @@ import { FormattingOptionType } from "types"
|
||||
import { kebabToCamel, kebabToPascal, kebabToSnake, kebabToTitle } from "utils"
|
||||
import baseSectionsOptions from "./base-section-options.js"
|
||||
import mergerCustomOptions from "./merger-custom-options/index.js"
|
||||
import {
|
||||
getCoreFlowNamespaces,
|
||||
getNamespaceNames,
|
||||
} from "../utils/get-namespaces.js"
|
||||
|
||||
const mergerOptions: Partial<TypeDocOptions> = {
|
||||
...baseOptions,
|
||||
@@ -36,7 +40,9 @@ const mergerOptions: Partial<TypeDocOptions> = {
|
||||
"helper-steps",
|
||||
"workflows",
|
||||
],
|
||||
allReflectionsHaveOwnDocumentInNamespace: ["Utilities"],
|
||||
allReflectionsHaveOwnDocumentInNamespace: [
|
||||
...getNamespaceNames(getCoreFlowNamespaces()),
|
||||
],
|
||||
formatting: {
|
||||
"*": {
|
||||
showCommentsAsHeader: true,
|
||||
|
||||
@@ -0,0 +1,74 @@
|
||||
import { readdirSync } from "fs"
|
||||
import { rootPathPrefix } from "../constants/general.js"
|
||||
import { NamespaceGenerateDetails } from "types"
|
||||
import { capitalize, kebabToTitle } from "utils"
|
||||
import path from "path"
|
||||
|
||||
export function getCoreFlowNamespaces(): NamespaceGenerateDetails[] {
|
||||
const namespaces: NamespaceGenerateDetails[] = []
|
||||
const rootFlowsPath = path.join(
|
||||
rootPathPrefix,
|
||||
"packages",
|
||||
"core",
|
||||
"core-flows",
|
||||
"src"
|
||||
)
|
||||
|
||||
// retrieve directories
|
||||
const directories = readdirSync(rootFlowsPath, {
|
||||
withFileTypes: true,
|
||||
})
|
||||
|
||||
directories.forEach((directory) => {
|
||||
if (!directory.isDirectory()) {
|
||||
return
|
||||
}
|
||||
|
||||
const namespaceName = kebabToTitle(directory.name)
|
||||
const pathPattern = `**/packages/core/core-flows/src/${directory.name}/**`
|
||||
|
||||
const namespace: NamespaceGenerateDetails = {
|
||||
name: namespaceName,
|
||||
pathPattern,
|
||||
children: [],
|
||||
}
|
||||
|
||||
const subDirs = readdirSync(path.join(rootFlowsPath, directory.name), {
|
||||
withFileTypes: true,
|
||||
})
|
||||
|
||||
subDirs.forEach((dir) => {
|
||||
if (
|
||||
!dir.isDirectory() ||
|
||||
(dir.name !== "workflows" && dir.name !== "steps")
|
||||
) {
|
||||
return
|
||||
}
|
||||
|
||||
namespace.children!.push({
|
||||
name: `${capitalize(dir.name)}_${namespaceName}`,
|
||||
pathPattern: `**/packages/core/core-flows/src/${directory.name}/${dir.name}`,
|
||||
})
|
||||
})
|
||||
|
||||
namespaces.push(namespace)
|
||||
})
|
||||
|
||||
return namespaces
|
||||
}
|
||||
|
||||
export function getNamespaceNames(
|
||||
namespaces: NamespaceGenerateDetails[]
|
||||
): string[] {
|
||||
const names: string[] = []
|
||||
|
||||
namespaces.forEach((namespace) => {
|
||||
names.push(namespace.name)
|
||||
|
||||
if (namespace.children) {
|
||||
names.push(...getNamespaceNames(namespace.children))
|
||||
}
|
||||
})
|
||||
|
||||
return names
|
||||
}
|
||||
@@ -2,6 +2,7 @@ import { minimatch } from "minimatch"
|
||||
import {
|
||||
Application,
|
||||
Comment,
|
||||
Context,
|
||||
Converter,
|
||||
DeclarationReflection,
|
||||
ParameterType,
|
||||
@@ -34,25 +35,26 @@ export function load(app: Application) {
|
||||
"generateNamespaces"
|
||||
) as unknown as NamespaceGenerateDetails[]
|
||||
|
||||
namespaces.forEach((namespace) => {
|
||||
const genNamespace = context.createDeclarationReflection(
|
||||
ReflectionKind.Namespace,
|
||||
void 0,
|
||||
void 0,
|
||||
namespace.name
|
||||
)
|
||||
const generateNamespaces = (ns: NamespaceGenerateDetails[]) => {
|
||||
const createdNamespaces: DeclarationReflection[] = []
|
||||
ns.forEach((namespace) => {
|
||||
const genNamespace = createNamespace(context, namespace)
|
||||
|
||||
if (namespace.description) {
|
||||
genNamespace.comment = new Comment([
|
||||
{
|
||||
kind: "text",
|
||||
text: namespace.description,
|
||||
},
|
||||
])
|
||||
}
|
||||
generatedNamespaces.set(namespace.pathPattern, genNamespace)
|
||||
|
||||
generatedNamespaces.set(namespace.pathPattern, genNamespace)
|
||||
})
|
||||
if (namespace.children) {
|
||||
generateNamespaces(namespace.children).forEach((child) =>
|
||||
genNamespace.addChild(child)
|
||||
)
|
||||
}
|
||||
|
||||
createdNamespaces.push(genNamespace)
|
||||
})
|
||||
|
||||
return createdNamespaces
|
||||
}
|
||||
|
||||
generateNamespaces(namespaces)
|
||||
})
|
||||
|
||||
app.converter.on(
|
||||
@@ -69,13 +71,61 @@ export function load(app: Application) {
|
||||
return
|
||||
}
|
||||
|
||||
generatedNamespaces.forEach((namespace, pathPattern) => {
|
||||
if (!minimatch(filePath, pathPattern)) {
|
||||
return
|
||||
}
|
||||
const namespaces = app.options.getValue(
|
||||
"generateNamespaces"
|
||||
) as unknown as NamespaceGenerateDetails[]
|
||||
|
||||
namespace.addChild(reflection)
|
||||
})
|
||||
const findNamespace = (
|
||||
ns: NamespaceGenerateDetails[]
|
||||
): DeclarationReflection | undefined => {
|
||||
let found: DeclarationReflection | undefined
|
||||
ns.some((namespace) => {
|
||||
if (namespace.children) {
|
||||
// give priorities to children
|
||||
found = findNamespace(namespace.children)
|
||||
if (found) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
if (!minimatch(filePath, namespace.pathPattern)) {
|
||||
return false
|
||||
}
|
||||
|
||||
found = generatedNamespaces.get(namespace.pathPattern)
|
||||
|
||||
return found !== undefined
|
||||
})
|
||||
|
||||
return found
|
||||
}
|
||||
|
||||
const namespace = findNamespace(namespaces)
|
||||
|
||||
namespace?.addChild(reflection)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
function createNamespace(
|
||||
context: Context,
|
||||
namespace: NamespaceGenerateDetails
|
||||
): DeclarationReflection {
|
||||
const genNamespace = context.createDeclarationReflection(
|
||||
ReflectionKind.Namespace,
|
||||
void 0,
|
||||
void 0,
|
||||
namespace.name
|
||||
)
|
||||
|
||||
if (namespace.description) {
|
||||
genNamespace.comment = new Comment([
|
||||
{
|
||||
kind: "text",
|
||||
text: namespace.description,
|
||||
},
|
||||
])
|
||||
}
|
||||
|
||||
return genNamespace
|
||||
}
|
||||
|
||||
@@ -69,6 +69,11 @@ import dmlPropertiesHelper from "./resources/helpers/dml-properties"
|
||||
import ifWorkflowStepHelper from "./resources/helpers/if-workflow-step"
|
||||
import stepInputHelper from "./resources/helpers/step-input"
|
||||
import stepOutputHelper from "./resources/helpers/step-output"
|
||||
import ifWorkflowHelper from "./resources/helpers/if-workflow"
|
||||
import workflowInputHelper from "./resources/helpers/workflow-input"
|
||||
import workflowOutputHelper from "./resources/helpers/workflow-output"
|
||||
import workflowDiagramHelper from "./resources/helpers/workflow-diagram"
|
||||
import workflowHooksHelper from "./resources/helpers/workflow-hooks"
|
||||
import { MarkdownTheme } from "./theme"
|
||||
|
||||
const TEMPLATE_PATH = path.join(__dirname, "resources", "templates")
|
||||
@@ -166,4 +171,9 @@ export function registerHelpers(theme: MarkdownTheme) {
|
||||
ifWorkflowStepHelper()
|
||||
stepInputHelper(theme)
|
||||
stepOutputHelper(theme)
|
||||
ifWorkflowHelper()
|
||||
workflowInputHelper(theme)
|
||||
workflowOutputHelper(theme)
|
||||
workflowDiagramHelper(theme)
|
||||
workflowHooksHelper(theme)
|
||||
}
|
||||
|
||||
@@ -1,14 +1,15 @@
|
||||
import * as Handlebars from "handlebars"
|
||||
import { Reflection, SignatureReflection } from "typedoc"
|
||||
import { isWorkflowStep } from "utils"
|
||||
import { isWorkflow, isWorkflowStep } from "utils"
|
||||
|
||||
export default function () {
|
||||
Handlebars.registerHelper("example", function (reflection: Reflection) {
|
||||
const isStep =
|
||||
const isWorkflowOrStep =
|
||||
reflection.variant === "signature" &&
|
||||
isWorkflowStep(reflection as SignatureReflection)
|
||||
(isWorkflowStep(reflection as SignatureReflection) ||
|
||||
isWorkflow(reflection as SignatureReflection))
|
||||
const targetReflection =
|
||||
isStep && reflection.parent ? reflection.parent : reflection
|
||||
isWorkflowOrStep && reflection.parent ? reflection.parent : reflection
|
||||
const exampleTag = targetReflection.comment?.blockTags.find(
|
||||
(tag) => tag.tag === "@example"
|
||||
)
|
||||
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
import * as Handlebars from "handlebars"
|
||||
import { SignatureReflection } from "typedoc"
|
||||
import { isWorkflow } from "utils"
|
||||
|
||||
export default function () {
|
||||
Handlebars.registerHelper(
|
||||
"ifWorkflow",
|
||||
function (this: SignatureReflection, options: Handlebars.HelperOptions) {
|
||||
return isWorkflow(this) ? options.fn(this) : options.inverse(this)
|
||||
}
|
||||
)
|
||||
}
|
||||
@@ -24,6 +24,7 @@ export default function (theme: MarkdownTheme) {
|
||||
reflectionType: inputType,
|
||||
project: this.project || options.data.theme.project,
|
||||
maxLevel,
|
||||
wrapObject: true,
|
||||
})
|
||||
|
||||
if (!input.length) {
|
||||
|
||||
@@ -24,6 +24,7 @@ export default function (theme: MarkdownTheme) {
|
||||
reflectionType: outputType,
|
||||
project: this.project || options.data.theme.project,
|
||||
maxLevel,
|
||||
wrapObject: true,
|
||||
})
|
||||
|
||||
if (!output.length) {
|
||||
|
||||
@@ -3,6 +3,7 @@ import {
|
||||
DeclarationReflection,
|
||||
ProjectReflection,
|
||||
ReflectionGroup,
|
||||
ReflectionKind,
|
||||
} from "typedoc"
|
||||
import { MarkdownTheme } from "../../theme"
|
||||
import { escapeChars } from "utils"
|
||||
@@ -13,13 +14,18 @@ export default function (theme: MarkdownTheme) {
|
||||
function (this: ProjectReflection | DeclarationReflection) {
|
||||
const md: string[] = []
|
||||
|
||||
const { hideInPageTOC } = theme
|
||||
const { hideInPageTOC, allReflectionsHaveOwnDocumentInNamespace } = theme
|
||||
const { hideTocHeaders, reflectionGroupRename = {} } =
|
||||
theme.getFormattingOptionsForLocation()
|
||||
|
||||
const isVisible = this.groups?.some((group) =>
|
||||
group.allChildrenHaveOwnDocument()
|
||||
)
|
||||
const isNamespaceVisible =
|
||||
this.kind === ReflectionKind.Namespace &&
|
||||
allReflectionsHaveOwnDocumentInNamespace.includes(this.name)
|
||||
const isVisible =
|
||||
isNamespaceVisible ||
|
||||
this.groups?.some((group) => {
|
||||
return group.allChildrenHaveOwnDocument()
|
||||
})
|
||||
|
||||
function pushGroup(group: ReflectionGroup, md: string[]) {
|
||||
const children = group.children.map(
|
||||
@@ -47,7 +53,7 @@ export default function (theme: MarkdownTheme) {
|
||||
md.push("\n")
|
||||
})
|
||||
} else {
|
||||
if (!hideInPageTOC || group.allChildrenHaveOwnDocument()) {
|
||||
if (!hideInPageTOC || isVisible) {
|
||||
if (!hideTocHeaders) {
|
||||
md.push(`${headingLevel} ${groupTitle}\n\n`)
|
||||
}
|
||||
|
||||
+113
@@ -0,0 +1,113 @@
|
||||
import { MarkdownTheme } from "../../theme"
|
||||
import * as Handlebars from "handlebars"
|
||||
import { DocumentReflection, SignatureReflection } from "typedoc"
|
||||
import { formatWorkflowDiagramComponent } from "../../utils/format-workflow-diagram-component"
|
||||
import { getProjectChild } from "utils"
|
||||
import { getWorkflowReflectionFromNamespace } from "../../utils/workflow-utils"
|
||||
|
||||
export default function (theme: MarkdownTheme) {
|
||||
Handlebars.registerHelper(
|
||||
"workflowDiagram",
|
||||
function (this: SignatureReflection): string {
|
||||
const { workflowDiagramComponent } =
|
||||
theme.getFormattingOptionsForLocation()
|
||||
if (!this.parent?.documents?.length) {
|
||||
return ""
|
||||
}
|
||||
|
||||
const steps: Record<string, unknown>[] = []
|
||||
|
||||
this.parent.documents.forEach((document, index) => {
|
||||
if (document.name === "when") {
|
||||
const condition = getDocumentTagValue(document, "@whenCondition")
|
||||
const depth = getDocumentTagValue(document, "@workflowDepth")
|
||||
|
||||
const whenStep = {
|
||||
type: "when",
|
||||
condition,
|
||||
depth,
|
||||
steps: [] as Record<string, unknown>[],
|
||||
}
|
||||
|
||||
document.children?.forEach((childDocument) => {
|
||||
whenStep.steps.push(
|
||||
getStep({
|
||||
document: childDocument,
|
||||
theme,
|
||||
index,
|
||||
})
|
||||
)
|
||||
})
|
||||
|
||||
steps.push(whenStep)
|
||||
} else {
|
||||
steps.push(
|
||||
getStep({
|
||||
document,
|
||||
theme,
|
||||
index,
|
||||
})
|
||||
)
|
||||
}
|
||||
})
|
||||
|
||||
return (
|
||||
`${Handlebars.helpers.titleLevel()} Diagram\n\n` +
|
||||
formatWorkflowDiagramComponent({
|
||||
component: workflowDiagramComponent,
|
||||
componentItem: {
|
||||
name: this.name,
|
||||
steps,
|
||||
},
|
||||
})
|
||||
)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
function getStep({
|
||||
document,
|
||||
theme,
|
||||
index,
|
||||
}: {
|
||||
document: DocumentReflection
|
||||
theme: MarkdownTheme
|
||||
index: number
|
||||
}) {
|
||||
const type = document.comment?.modifierTags.has("@workflowStep")
|
||||
? "workflow"
|
||||
: document.comment?.modifierTags.has("@hook")
|
||||
? "hook"
|
||||
: "step"
|
||||
|
||||
const namespaceRefl = theme.project
|
||||
? getWorkflowReflectionFromNamespace(theme.project, document.name)
|
||||
: undefined
|
||||
|
||||
const associatedReflection =
|
||||
namespaceRefl ||
|
||||
(theme.project ? getProjectChild(theme.project, document.name) : undefined)
|
||||
const depth = getDocumentTagValue(document, `@workflowDepth`) || `${index}`
|
||||
|
||||
return {
|
||||
type,
|
||||
name: document.name,
|
||||
description: associatedReflection?.comment
|
||||
? Handlebars.helpers.comments(associatedReflection.comment, true)
|
||||
: "",
|
||||
link:
|
||||
type === "hook" || !associatedReflection?.url
|
||||
? `#${document.name}`
|
||||
: Handlebars.helpers.relativeURL(associatedReflection.url),
|
||||
depth: parseInt(depth),
|
||||
}
|
||||
}
|
||||
|
||||
function getDocumentTagValue(
|
||||
document: DocumentReflection,
|
||||
tag: `@${string}`
|
||||
): string | undefined {
|
||||
return document.comment
|
||||
?.getTag(tag)
|
||||
?.content.find((tagContent) => tagContent.kind === "text")?.text
|
||||
}
|
||||
+72
@@ -0,0 +1,72 @@
|
||||
import { MarkdownTheme } from "../../theme"
|
||||
import * as Handlebars from "handlebars"
|
||||
import { SignatureReflection } from "typedoc"
|
||||
import { cleanUpHookInput, getProjectChild } from "utils"
|
||||
|
||||
export default function (theme: MarkdownTheme) {
|
||||
Handlebars.registerHelper(
|
||||
"workflowHooks",
|
||||
function (this: SignatureReflection): string {
|
||||
if (!this.parent?.documents || !theme.project) {
|
||||
return ""
|
||||
}
|
||||
|
||||
const hooks = this.parent.documents.filter(
|
||||
(document) => document.comment?.modifierTags.has("@hook")
|
||||
)
|
||||
|
||||
if (!hooks.length) {
|
||||
return ""
|
||||
}
|
||||
|
||||
let str = `${Handlebars.helpers.titleLevel()} Hooks`
|
||||
|
||||
Handlebars.helpers.incrementCurrentTitleLevel()
|
||||
|
||||
const hooksTitleLevel = Handlebars.helpers.titleLevel()
|
||||
|
||||
hooks.forEach((hook) => {
|
||||
// show the hook's input
|
||||
const hookReflection = getProjectChild(theme.project!, hook.name)
|
||||
|
||||
if (
|
||||
!hookReflection ||
|
||||
!hookReflection.signatures?.length ||
|
||||
!hookReflection.signatures[0].parameters?.length
|
||||
) {
|
||||
return
|
||||
}
|
||||
|
||||
str += `\n\n${hooksTitleLevel} ${hook.name}\n\n`
|
||||
|
||||
const hookExample = hookReflection.comment?.getTag(`@example`)
|
||||
|
||||
if (hookExample) {
|
||||
Handlebars.helpers.incrementCurrentTitleLevel()
|
||||
const innerTitleLevel = Handlebars.helpers.titleLevel()
|
||||
|
||||
str += `${innerTitleLevel} Example\n\n\`\`\`ts\n${Handlebars.helpers.comment(
|
||||
hookExample.content
|
||||
)}\n\`\`\`\n\n${innerTitleLevel} Input\n\n`
|
||||
|
||||
Handlebars.helpers.decrementCurrentTitleLevel()
|
||||
}
|
||||
|
||||
str += `Handlers consuming this hook accept the following input.\n\n`
|
||||
|
||||
str += Handlebars.helpers.parameterComponent.call(
|
||||
cleanUpHookInput(hookReflection.signatures[0].parameters),
|
||||
{
|
||||
hash: {
|
||||
sectionTitle: hook.name,
|
||||
},
|
||||
}
|
||||
)
|
||||
})
|
||||
|
||||
Handlebars.helpers.decrementCurrentTitleLevel()
|
||||
|
||||
return str
|
||||
}
|
||||
)
|
||||
}
|
||||
+44
@@ -0,0 +1,44 @@
|
||||
import { MarkdownTheme } from "../../theme"
|
||||
import * as Handlebars from "handlebars"
|
||||
import { SignatureReflection } from "typedoc"
|
||||
import { getWorkflowInputType } from "utils"
|
||||
import { formatParameterComponent } from "../../utils/format-parameter-component"
|
||||
import { getReflectionTypeParameters } from "../../utils/reflection-type-parameters"
|
||||
|
||||
export default function (theme: MarkdownTheme) {
|
||||
Handlebars.registerHelper(
|
||||
"workflowInput",
|
||||
function (
|
||||
this: SignatureReflection,
|
||||
options: Handlebars.HelperOptions
|
||||
): string {
|
||||
const { parameterComponent, maxLevel, parameterComponentExtraProps } =
|
||||
theme.getFormattingOptionsForLocation()
|
||||
|
||||
const inputType = getWorkflowInputType(this)
|
||||
if (!inputType) {
|
||||
return ""
|
||||
}
|
||||
|
||||
const input = getReflectionTypeParameters({
|
||||
reflectionType: inputType,
|
||||
project: this.project || options.data.theme.project,
|
||||
maxLevel,
|
||||
wrapObject: true,
|
||||
})
|
||||
|
||||
if (!input.length) {
|
||||
return ""
|
||||
}
|
||||
|
||||
const formattedComponent = formatParameterComponent({
|
||||
parameterComponent,
|
||||
componentItems: input,
|
||||
extraProps: parameterComponentExtraProps,
|
||||
sectionTitle: options.hash.sectionTitle,
|
||||
})
|
||||
|
||||
return `${Handlebars.helpers.titleLevel()} Input\n\n${formattedComponent}`
|
||||
}
|
||||
)
|
||||
}
|
||||
+44
@@ -0,0 +1,44 @@
|
||||
import { MarkdownTheme } from "../../theme"
|
||||
import * as Handlebars from "handlebars"
|
||||
import { SignatureReflection } from "typedoc"
|
||||
import { getWorkflowOutputType } from "utils"
|
||||
import { formatParameterComponent } from "../../utils/format-parameter-component"
|
||||
import { getReflectionTypeParameters } from "../../utils/reflection-type-parameters"
|
||||
|
||||
export default function (theme: MarkdownTheme) {
|
||||
Handlebars.registerHelper(
|
||||
"workflowOutput",
|
||||
function (
|
||||
this: SignatureReflection,
|
||||
options: Handlebars.HelperOptions
|
||||
): string {
|
||||
const { parameterComponent, maxLevel, parameterComponentExtraProps } =
|
||||
theme.getFormattingOptionsForLocation()
|
||||
|
||||
const outputType = getWorkflowOutputType(this)
|
||||
if (!outputType) {
|
||||
return ""
|
||||
}
|
||||
|
||||
const output = getReflectionTypeParameters({
|
||||
reflectionType: outputType,
|
||||
project: this.project || options.data.theme.project,
|
||||
maxLevel,
|
||||
wrapObject: true,
|
||||
})
|
||||
|
||||
if (!output.length) {
|
||||
return ""
|
||||
}
|
||||
|
||||
const formattedComponent = formatParameterComponent({
|
||||
parameterComponent,
|
||||
componentItems: output,
|
||||
extraProps: parameterComponentExtraProps,
|
||||
sectionTitle: options.hash.sectionTitle,
|
||||
})
|
||||
|
||||
return `${Handlebars.helpers.titleLevel()} Output\n\n${formattedComponent}`
|
||||
}
|
||||
)
|
||||
}
|
||||
+8
@@ -4,6 +4,12 @@
|
||||
|
||||
{{else}}
|
||||
|
||||
{{#ifWorkflow}}
|
||||
|
||||
{{> member.workflow}}
|
||||
|
||||
{{else}}
|
||||
|
||||
{{#ifWorkflowStep}}
|
||||
|
||||
{{> member.step}}
|
||||
@@ -14,4 +20,6 @@
|
||||
|
||||
{{/ifWorkflowStep}}
|
||||
|
||||
{{/ifWorkflow}}
|
||||
|
||||
{{/ifReactQueryType}}
|
||||
+21
@@ -0,0 +1,21 @@
|
||||
{{{signatureTitle accessor parent}}}
|
||||
|
||||
{{#if (sectionEnabled "member_signature_comment")}}
|
||||
|
||||
{{> comment}}
|
||||
|
||||
{{/if}}
|
||||
|
||||
{{#if (sectionEnabled "member_signature_example")}}
|
||||
|
||||
{{{example this}}}
|
||||
|
||||
{{/if}}
|
||||
|
||||
{{{workflowDiagram}}}
|
||||
|
||||
{{{workflowInput sectionTitle=name}}}
|
||||
|
||||
{{{workflowOutput sectionTitle=name}}}
|
||||
|
||||
{{{workflowHooks}}}
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
export function formatWorkflowDiagramComponent({
|
||||
component,
|
||||
componentItem,
|
||||
}: {
|
||||
component: string | undefined
|
||||
componentItem: Record<string, unknown>
|
||||
}): string {
|
||||
return `<${component} workflow={${JSON.stringify(componentItem)}} />`
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
import {
|
||||
DeclarationReflection,
|
||||
ProjectReflection,
|
||||
ReflectionKind,
|
||||
} from "typedoc"
|
||||
|
||||
export function getWorkflowReflectionFromNamespace(
|
||||
project: ProjectReflection,
|
||||
reflName: string
|
||||
): DeclarationReflection | undefined {
|
||||
let found: DeclarationReflection | undefined
|
||||
project
|
||||
.getChildrenByKind(ReflectionKind.Module)
|
||||
.find((moduleRef) => moduleRef.name === "core-flows")
|
||||
?.getChildrenByKind(ReflectionKind.Namespace)
|
||||
.some((namespace) => {
|
||||
found = namespace.getChildByName(reflName) as DeclarationReflection
|
||||
|
||||
return found !== undefined
|
||||
})
|
||||
|
||||
return found
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
import {
|
||||
Application,
|
||||
Comment,
|
||||
CommentTag,
|
||||
Context,
|
||||
Converter,
|
||||
DeclarationReflection,
|
||||
@@ -14,7 +15,13 @@ import {
|
||||
import ts, { SyntaxKind, VariableStatement } from "typescript"
|
||||
import { WorkflowManager, WorkflowDefinition } from "@medusajs/orchestration"
|
||||
import Helper from "./utils/helper"
|
||||
import { isWorkflow } from "utils"
|
||||
import { isWorkflow, isWorkflowStep } from "utils"
|
||||
import { StepType } from "./types"
|
||||
|
||||
type ParsedStep = {
|
||||
stepReflection: DeclarationReflection
|
||||
stepType: StepType
|
||||
}
|
||||
|
||||
/**
|
||||
* A plugin that extracts a workflow's steps, hooks, their types, and attaches them as
|
||||
@@ -92,12 +99,20 @@ class WorkflowsPlugin {
|
||||
continue
|
||||
}
|
||||
|
||||
this.parseSteps({
|
||||
this.parseWorkflow({
|
||||
workflowId,
|
||||
constructorFn: initializer.arguments[1],
|
||||
context,
|
||||
parentReflection: reflection.parent,
|
||||
})
|
||||
|
||||
if (!reflection.comment && reflection.parent.comment) {
|
||||
reflection.comment = reflection.parent.comment
|
||||
}
|
||||
} else if (isWorkflowStep(reflection)) {
|
||||
if (!reflection.comment && reflection.parent.comment) {
|
||||
reflection.comment = reflection.parent.comment
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -107,7 +122,7 @@ class WorkflowsPlugin {
|
||||
*
|
||||
* @param param0 - The workflow's details.
|
||||
*/
|
||||
parseSteps({
|
||||
parseWorkflow({
|
||||
workflowId,
|
||||
constructorFn,
|
||||
context,
|
||||
@@ -130,138 +145,241 @@ class WorkflowsPlugin {
|
||||
parentReflection.documents = []
|
||||
}
|
||||
|
||||
let stepDepth = 1
|
||||
|
||||
constructorFn.body.statements.forEach((statement) => {
|
||||
let initializer: ts.CallExpression | undefined
|
||||
switch (statement.kind) {
|
||||
case SyntaxKind.VariableStatement:
|
||||
const variableInitializer = (statement as VariableStatement)
|
||||
.declarationList.declarations[0].initializer
|
||||
|
||||
if (
|
||||
!variableInitializer ||
|
||||
!ts.isCallExpression(variableInitializer)
|
||||
) {
|
||||
return
|
||||
}
|
||||
|
||||
initializer = variableInitializer
|
||||
break
|
||||
case SyntaxKind.ExpressionStatement:
|
||||
const statementInitializer = (statement as ts.ExpressionStatement)
|
||||
.expression
|
||||
if (!ts.isCallExpression(statementInitializer)) {
|
||||
return
|
||||
}
|
||||
|
||||
initializer = statementInitializer
|
||||
}
|
||||
const initializer = this.getInitializerOfNode(statement)
|
||||
|
||||
if (!initializer) {
|
||||
return
|
||||
}
|
||||
|
||||
const { stepId, stepReflection } =
|
||||
this.parseStep({
|
||||
const initializerName = this.helper.normalizeName(
|
||||
initializer.expression.getText()
|
||||
)
|
||||
|
||||
if (initializerName === "when") {
|
||||
this.parseWhenStep({
|
||||
initializer,
|
||||
parentReflection,
|
||||
context,
|
||||
workflow,
|
||||
stepDepth,
|
||||
})
|
||||
} else {
|
||||
const steps = this.parseSteps({
|
||||
initializer,
|
||||
context,
|
||||
workflow,
|
||||
}) || {}
|
||||
workflowVarName: parentReflection.name,
|
||||
})
|
||||
|
||||
if (!stepId || !stepReflection) {
|
||||
return
|
||||
if (!steps.length) {
|
||||
return
|
||||
}
|
||||
|
||||
steps.forEach((step) => {
|
||||
this.createStepDocumentReflection({
|
||||
...step,
|
||||
depth: stepDepth,
|
||||
parentReflection,
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
const stepModifier = this.helper.getModifier(initializer)
|
||||
|
||||
const documentReflection = new DocumentReflection(
|
||||
stepReflection.name,
|
||||
stepReflection,
|
||||
[],
|
||||
{}
|
||||
)
|
||||
|
||||
documentReflection.comment = new Comment()
|
||||
documentReflection.comment.modifierTags.add(stepModifier)
|
||||
|
||||
parentReflection.documents?.push(documentReflection)
|
||||
stepDepth++
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse a step to retrieve its ID and reflection.
|
||||
* Parses steps in an initializer, retrieving each of their ID and reflection.
|
||||
*
|
||||
* @param param0 - The step's details.
|
||||
* @returns The step's ID and reflection, if found.
|
||||
*/
|
||||
parseStep({
|
||||
parseSteps({
|
||||
initializer,
|
||||
context,
|
||||
workflow,
|
||||
workflowVarName,
|
||||
}: {
|
||||
initializer: ts.CallExpression
|
||||
context: Context
|
||||
workflow?: WorkflowDefinition
|
||||
}):
|
||||
| {
|
||||
stepId: string
|
||||
stepReflection: DeclarationReflection
|
||||
}
|
||||
| undefined {
|
||||
workflowVarName: string
|
||||
}): ParsedStep[] {
|
||||
const steps: ParsedStep[] = []
|
||||
const initializerName = this.helper.normalizeName(
|
||||
initializer.expression.getText()
|
||||
)
|
||||
|
||||
let stepId: string | undefined
|
||||
let stepReflection: DeclarationReflection | undefined
|
||||
|
||||
if (
|
||||
this.helper.getStepType(initializer) === "hook" &&
|
||||
"symbol" in initializer.arguments[1]
|
||||
) {
|
||||
// get the hook's name from the first argument
|
||||
stepId = this.helper.normalizeName(initializer.arguments[0].getText())
|
||||
stepReflection = this.assembleHookReflection({
|
||||
stepId,
|
||||
context,
|
||||
inputSymbol: initializer.arguments[1].symbol as ts.Symbol,
|
||||
})
|
||||
} else {
|
||||
const initializerReflection =
|
||||
context.project.getChildByName(initializerName)
|
||||
|
||||
if (
|
||||
!initializerReflection ||
|
||||
!(initializerReflection instanceof DeclarationReflection)
|
||||
) {
|
||||
return
|
||||
if (initializerName === "parallelize") {
|
||||
if (!initializer.arguments.length) {
|
||||
return steps
|
||||
}
|
||||
|
||||
const { initializer } =
|
||||
this.helper.getReflectionSymbolAndInitializer({
|
||||
project: context.project,
|
||||
reflection: initializerReflection,
|
||||
}) || {}
|
||||
initializer.arguments.forEach((argument) => {
|
||||
if (!ts.isCallExpression(argument)) {
|
||||
return
|
||||
}
|
||||
|
||||
steps.push(
|
||||
...this.parseSteps({
|
||||
initializer: argument,
|
||||
context,
|
||||
workflow,
|
||||
workflowVarName,
|
||||
})
|
||||
)
|
||||
})
|
||||
} else {
|
||||
let stepId: string | undefined
|
||||
let stepReflection: DeclarationReflection | undefined
|
||||
let stepType = this.helper.getStepType(initializer)
|
||||
|
||||
if (stepType === "hook" && "symbol" in initializer.arguments[1]) {
|
||||
// get the hook's name from the first argument
|
||||
stepId = this.helper.normalizeName(initializer.arguments[0].getText())
|
||||
stepReflection = this.assembleHookReflection({
|
||||
stepId,
|
||||
context,
|
||||
inputSymbol: initializer.arguments[1].symbol as ts.Symbol,
|
||||
workflowName: workflowVarName,
|
||||
})
|
||||
} else {
|
||||
const initializerReflection =
|
||||
context.project.getChildByName(initializerName)
|
||||
|
||||
if (
|
||||
!initializerReflection ||
|
||||
!(initializerReflection instanceof DeclarationReflection)
|
||||
) {
|
||||
return steps
|
||||
}
|
||||
|
||||
const { initializer: originalInitializer } =
|
||||
this.helper.getReflectionSymbolAndInitializer({
|
||||
project: context.project,
|
||||
reflection: initializerReflection,
|
||||
}) || {}
|
||||
|
||||
if (!originalInitializer) {
|
||||
return steps
|
||||
}
|
||||
|
||||
stepId = this.helper.getStepOrWorkflowId(
|
||||
originalInitializer,
|
||||
context.project,
|
||||
true
|
||||
)
|
||||
stepType = this.helper.getStepType(originalInitializer)
|
||||
stepReflection = initializerReflection
|
||||
}
|
||||
|
||||
// check if is a step in the workflow
|
||||
if (
|
||||
stepId &&
|
||||
stepType &&
|
||||
stepReflection &&
|
||||
workflow?.handlers_.get(stepId)
|
||||
) {
|
||||
steps.push({
|
||||
stepReflection,
|
||||
stepType,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
return steps
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses the step in a `when` condition, and creates a `when` document with the steps as child documents.
|
||||
*
|
||||
* @param param0 - The when stp's details.
|
||||
*/
|
||||
parseWhenStep({
|
||||
initializer,
|
||||
parentReflection,
|
||||
context,
|
||||
workflow,
|
||||
stepDepth,
|
||||
}: {
|
||||
initializer: ts.CallExpression
|
||||
parentReflection: DeclarationReflection
|
||||
context: Context
|
||||
workflow?: WorkflowDefinition
|
||||
stepDepth: number
|
||||
}) {
|
||||
const whenInitializer = (initializer.expression as ts.CallExpression)
|
||||
.expression as ts.CallExpression
|
||||
const thenInitializer = initializer
|
||||
|
||||
if (
|
||||
whenInitializer.arguments.length < 2 ||
|
||||
(!ts.isFunctionExpression(whenInitializer.arguments[1]) &&
|
||||
!ts.isArrowFunction(whenInitializer.arguments[1])) ||
|
||||
thenInitializer.arguments.length < 1 ||
|
||||
(!ts.isFunctionExpression(thenInitializer.arguments[0]) &&
|
||||
!ts.isArrowFunction(thenInitializer.arguments[0]))
|
||||
) {
|
||||
return
|
||||
}
|
||||
|
||||
const whenCondition = whenInitializer.arguments[1].body.getText()
|
||||
|
||||
const thenStatements = (thenInitializer.arguments[0].body as ts.Block)
|
||||
.statements
|
||||
|
||||
const documentReflection = new DocumentReflection(
|
||||
"when",
|
||||
parentReflection,
|
||||
[],
|
||||
{}
|
||||
)
|
||||
|
||||
documentReflection.comment = new Comment()
|
||||
documentReflection.comment.modifierTags.add(this.helper.getModifier(`when`))
|
||||
documentReflection.comment.blockTags.push(
|
||||
new CommentTag(`@workflowDepth`, [
|
||||
{
|
||||
kind: "text",
|
||||
text: `${stepDepth}`,
|
||||
},
|
||||
])
|
||||
)
|
||||
documentReflection.comment.blockTags.push(
|
||||
new CommentTag(`@whenCondition`, [
|
||||
{
|
||||
kind: "text",
|
||||
text: whenCondition,
|
||||
},
|
||||
])
|
||||
)
|
||||
|
||||
thenStatements.forEach((statement) => {
|
||||
const initializer = this.getInitializerOfNode(statement)
|
||||
|
||||
if (!initializer) {
|
||||
return
|
||||
}
|
||||
|
||||
stepId = this.helper.getStepOrWorkflowId(
|
||||
this.parseSteps({
|
||||
initializer,
|
||||
context.project,
|
||||
true
|
||||
)
|
||||
stepReflection = initializerReflection
|
||||
}
|
||||
context,
|
||||
workflow,
|
||||
workflowVarName: parentReflection.name,
|
||||
}).forEach((step) => {
|
||||
this.createStepDocumentReflection({
|
||||
...step,
|
||||
depth: stepDepth,
|
||||
parentReflection: documentReflection,
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
// check if is a step in the workflow
|
||||
if (!stepId || !stepReflection || !workflow?.handlers_.get(stepId)) {
|
||||
return
|
||||
}
|
||||
|
||||
return {
|
||||
stepId,
|
||||
stepReflection,
|
||||
if (documentReflection.children?.length) {
|
||||
parentReflection.documents?.push(documentReflection)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -275,10 +393,12 @@ class WorkflowsPlugin {
|
||||
stepId,
|
||||
context,
|
||||
inputSymbol,
|
||||
workflowName,
|
||||
}: {
|
||||
stepId: string
|
||||
context: Context
|
||||
inputSymbol: ts.Symbol
|
||||
workflowName: string
|
||||
}): DeclarationReflection {
|
||||
const declarationReflection = context.createDeclarationReflection(
|
||||
ReflectionKind.Function,
|
||||
@@ -286,6 +406,14 @@ class WorkflowsPlugin {
|
||||
undefined,
|
||||
stepId
|
||||
)
|
||||
|
||||
declarationReflection.comment = new Comment()
|
||||
declarationReflection.comment.summary = [
|
||||
{
|
||||
kind: "text",
|
||||
text: "This step is a hook that you can inject custom functionality into.",
|
||||
},
|
||||
]
|
||||
const signatureReflection = new SignatureReflection(
|
||||
stepId,
|
||||
ReflectionKind.SomeSignature,
|
||||
@@ -300,6 +428,10 @@ class WorkflowsPlugin {
|
||||
|
||||
parameter.type = ReferenceType.createSymbolReference(inputSymbol, context)
|
||||
|
||||
if (parameter.type.name === "__object") {
|
||||
parameter.type.name = "object"
|
||||
}
|
||||
|
||||
signatureReflection.parameters = []
|
||||
|
||||
signatureReflection.parameters.push(parameter)
|
||||
@@ -308,8 +440,162 @@ class WorkflowsPlugin {
|
||||
|
||||
declarationReflection.signatures.push(signatureReflection)
|
||||
|
||||
declarationReflection.comment.blockTags.push(
|
||||
new CommentTag(`@example`, [
|
||||
{
|
||||
kind: "code",
|
||||
text: this.helper.generateHookExample({
|
||||
hookName: stepId,
|
||||
workflowName,
|
||||
parameter,
|
||||
}),
|
||||
},
|
||||
])
|
||||
)
|
||||
|
||||
return declarationReflection
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a document reflection for a step.
|
||||
*
|
||||
* @param param0 - The step's details.
|
||||
*/
|
||||
createStepDocumentReflection({
|
||||
stepType,
|
||||
stepReflection,
|
||||
depth,
|
||||
parentReflection,
|
||||
}: ParsedStep & {
|
||||
depth: number
|
||||
parentReflection: DeclarationReflection | DocumentReflection
|
||||
}) {
|
||||
const stepModifier = this.helper.getModifier(stepType)
|
||||
|
||||
const documentReflection = new DocumentReflection(
|
||||
stepReflection.name,
|
||||
stepReflection,
|
||||
[],
|
||||
{}
|
||||
)
|
||||
|
||||
documentReflection.comment = new Comment()
|
||||
documentReflection.comment.modifierTags.add(stepModifier)
|
||||
documentReflection.comment.blockTags.push(
|
||||
new CommentTag(`@workflowDepth`, [
|
||||
{
|
||||
kind: "text",
|
||||
text: `${depth}`,
|
||||
},
|
||||
])
|
||||
)
|
||||
|
||||
if (parentReflection.isDocument()) {
|
||||
parentReflection.addChild(documentReflection)
|
||||
} else {
|
||||
parentReflection.documents?.push(documentReflection)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the initializer in a node, if available.
|
||||
*
|
||||
* @param node - The node to search for an initializer in.
|
||||
* @returns The initializer, if found.
|
||||
*/
|
||||
getInitializerOfNode(node: ts.Node): ts.CallExpression | undefined {
|
||||
let initializer: ts.CallExpression | undefined
|
||||
switch (node.kind) {
|
||||
case SyntaxKind.CallExpression:
|
||||
initializer = node as ts.CallExpression
|
||||
break
|
||||
case SyntaxKind.VariableStatement:
|
||||
const variableInitializer = (node as VariableStatement).declarationList
|
||||
.declarations[0].initializer
|
||||
|
||||
if (!variableInitializer) {
|
||||
return
|
||||
}
|
||||
|
||||
initializer = this.findCallExpression(variableInitializer)
|
||||
break
|
||||
case SyntaxKind.ExpressionStatement:
|
||||
const statementInitializer = (node as ts.ExpressionStatement).expression
|
||||
|
||||
initializer = this.findCallExpression(statementInitializer)
|
||||
break
|
||||
case SyntaxKind.ReturnStatement:
|
||||
let returnInitializer = (node as ts.ReturnStatement).expression
|
||||
|
||||
if (
|
||||
returnInitializer &&
|
||||
ts.isNewExpression(returnInitializer) &&
|
||||
returnInitializer.expression.getText().includes("WorkflowResponse") &&
|
||||
returnInitializer.arguments?.length
|
||||
) {
|
||||
returnInitializer = this.getInitializerOfNode(
|
||||
returnInitializer.arguments[0]
|
||||
)
|
||||
}
|
||||
|
||||
if (!returnInitializer) {
|
||||
return
|
||||
}
|
||||
|
||||
initializer = this.findCallExpression(returnInitializer)
|
||||
|
||||
break
|
||||
}
|
||||
|
||||
return initializer ? this.cleanUpInitializer(initializer) : undefined
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds a `CallExpression` in an expression and returns it, if available.
|
||||
*
|
||||
* @param expression - The expression to search in.
|
||||
* @param skipCallCheck - Whether to skip the `CallExpression` check the first time. Useful for the {@link cleanUpInitializer} method.
|
||||
* @returns The `CallExpression` if found.
|
||||
*/
|
||||
findCallExpression(
|
||||
expression: ts.Expression,
|
||||
skipCallCheck = false
|
||||
): ts.CallExpression | undefined {
|
||||
let initializer = expression
|
||||
while (
|
||||
(skipCallCheck || !ts.isCallExpression(initializer)) &&
|
||||
"expression" in initializer
|
||||
) {
|
||||
initializer = initializer.expression as ts.Expression
|
||||
skipCallCheck = false
|
||||
}
|
||||
|
||||
return initializer && ts.isCallExpression(initializer)
|
||||
? initializer
|
||||
: undefined
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds an inner call expression of a call expression, if the provided one is not allowed.
|
||||
* This is useful for steps that chain a `.config` method, for example.
|
||||
*
|
||||
* @param initializer - The call expression to search in.
|
||||
* @returns The call expression to be used.
|
||||
*/
|
||||
cleanUpInitializer(initializer: ts.CallExpression): ts.CallExpression {
|
||||
if (!("name" in initializer.expression)) {
|
||||
return initializer
|
||||
}
|
||||
|
||||
const initializerName = (initializer.expression.name as ts.Identifier)
|
||||
.escapedText
|
||||
|
||||
if (initializerName === "config") {
|
||||
return this.findCallExpression(initializer, true) || initializer
|
||||
}
|
||||
|
||||
return initializer
|
||||
}
|
||||
}
|
||||
|
||||
export default WorkflowsPlugin
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
export type StepType = "step" | "workflowStep" | "hook"
|
||||
export type StepType = "step" | "workflowStep" | "hook" | "when"
|
||||
|
||||
export type StepModifier = "@step" | "@workflowStep" | "@hook"
|
||||
export type StepModifier = "@step" | "@workflowStep" | "@hook" | "@when"
|
||||
|
||||
@@ -1,5 +1,9 @@
|
||||
import { DeclarationReflection, ProjectReflection } from "typedoc"
|
||||
import ts from "typescript"
|
||||
import {
|
||||
DeclarationReflection,
|
||||
ParameterReflection,
|
||||
ProjectReflection,
|
||||
} from "typedoc"
|
||||
import ts, { isStringLiteral } from "typescript"
|
||||
import { StepModifier, StepType } from "../types"
|
||||
|
||||
/**
|
||||
@@ -13,7 +17,27 @@ export default class Helper {
|
||||
* @returns The normalized name.
|
||||
*/
|
||||
normalizeName(name: string) {
|
||||
return name.replace(".runAsStep", "").replace(/^"/, "").replace(/"$/, "")
|
||||
const nameWithoutQuotes = name.replace(/^"/, "").replace(/"$/, "")
|
||||
|
||||
const dotPos = nameWithoutQuotes.indexOf(".")
|
||||
const parenPos = nameWithoutQuotes.indexOf("(")
|
||||
|
||||
// If both indices of dot and parenthesis are -1, set endIndex to -1
|
||||
// if one of them is -1, use the other's value
|
||||
// if both aren't -1, use the minimum
|
||||
const endIndex =
|
||||
dotPos === -1 && parenPos === -1
|
||||
? -1
|
||||
: dotPos === -1
|
||||
? parenPos
|
||||
: parenPos === -1
|
||||
? dotPos
|
||||
: Math.min(dotPos, parenPos)
|
||||
|
||||
return nameWithoutQuotes.substring(
|
||||
0,
|
||||
endIndex === -1 ? nameWithoutQuotes.length : endIndex
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -64,13 +88,39 @@ export default class Helper {
|
||||
project: ProjectReflection,
|
||||
checkWorkflowStep = false
|
||||
): string | undefined {
|
||||
const idVar = initializer.arguments[0]
|
||||
const idArg = initializer.arguments[0]
|
||||
const isWorkflowStep =
|
||||
checkWorkflowStep && this.getStepType(initializer) === "workflowStep"
|
||||
const idVarName = this.normalizeName(idVar.getText())
|
||||
const idArgValue = this.normalizeName(idArg.getText())
|
||||
|
||||
let stepId: string | undefined
|
||||
|
||||
if (ts.isObjectLiteralExpression(idArg)) {
|
||||
const nameProperty = idArg.properties.find(
|
||||
(property) => property.name?.getText() === "name"
|
||||
)
|
||||
|
||||
if (nameProperty && ts.isPropertyAssignment(nameProperty)) {
|
||||
const nameValue = this.normalizeName(nameProperty.initializer.getText())
|
||||
stepId = ts.isStringLiteral(nameProperty.initializer)
|
||||
? nameValue
|
||||
: this.getValueFromReflection(nameValue, project)
|
||||
}
|
||||
} else if (!isStringLiteral(idArg)) {
|
||||
stepId = this.getValueFromReflection(idArgValue, project)
|
||||
} else {
|
||||
stepId = idArgValue
|
||||
}
|
||||
|
||||
return isWorkflowStep ? `${stepId}-as-step` : stepId
|
||||
}
|
||||
|
||||
private getValueFromReflection(
|
||||
refName: string,
|
||||
project: ProjectReflection
|
||||
): string | undefined {
|
||||
// load it from the project
|
||||
const idVarReflection = project.getChildByName(idVarName)
|
||||
const idVarReflection = project.getChildByName(refName)
|
||||
|
||||
if (
|
||||
!idVarReflection ||
|
||||
@@ -80,9 +130,7 @@ export default class Helper {
|
||||
return
|
||||
}
|
||||
|
||||
const stepId = idVarReflection.type.value as string
|
||||
|
||||
return isWorkflowStep ? `${stepId}-as-step` : stepId
|
||||
return idVarReflection.type.value as string
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -97,6 +145,8 @@ export default class Helper {
|
||||
return "workflowStep"
|
||||
case "createHook":
|
||||
return "hook"
|
||||
case "when":
|
||||
return "when"
|
||||
default:
|
||||
return "step"
|
||||
}
|
||||
@@ -108,9 +158,39 @@ export default class Helper {
|
||||
* @param initializer - The step's initializer.
|
||||
* @returns The step's modifier.
|
||||
*/
|
||||
getModifier(initializer: ts.CallExpression): StepModifier {
|
||||
const stepType = this.getStepType(initializer)
|
||||
|
||||
getModifier(stepType: StepType): StepModifier {
|
||||
return `@${stepType}`
|
||||
}
|
||||
|
||||
generateHookExample({
|
||||
hookName,
|
||||
workflowName,
|
||||
parameter,
|
||||
}: {
|
||||
hookName: string
|
||||
workflowName: string
|
||||
parameter: ParameterReflection
|
||||
}): string {
|
||||
let str = `import { ${workflowName} } from "@medusajs/core-flows"\n\n`
|
||||
|
||||
str += `${workflowName}.hooks.${hookName}(\n\tasync (({`
|
||||
|
||||
if (
|
||||
parameter.type?.type === "reference" &&
|
||||
parameter.type.reflection instanceof DeclarationReflection &&
|
||||
parameter.type.reflection.children
|
||||
) {
|
||||
parameter.type.reflection.children.forEach((childParam, index) => {
|
||||
if (index > 0) {
|
||||
str += `,`
|
||||
}
|
||||
|
||||
str += ` ${childParam.name}`
|
||||
})
|
||||
}
|
||||
|
||||
str += ` }, { container }) => {\n\t\t//TODO\n\t})\n)`
|
||||
|
||||
return str
|
||||
}
|
||||
}
|
||||
|
||||
+5
@@ -83,6 +83,7 @@ export type FormattingOptionType = {
|
||||
endSections?: string[]
|
||||
shouldIncrementAfterStartSections?: boolean
|
||||
hideTocHeaders?: boolean
|
||||
workflowDiagramComponent?: string
|
||||
}
|
||||
|
||||
export declare module "typedoc" {
|
||||
@@ -286,4 +287,8 @@ export declare type NamespaceGenerateDetails = {
|
||||
* namespace
|
||||
*/
|
||||
pathPattern: string
|
||||
/**
|
||||
* The namespace's children
|
||||
*/
|
||||
children?: NamespaceGenerateDetails[]
|
||||
}
|
||||
|
||||
@@ -0,0 +1,63 @@
|
||||
import {
|
||||
DeclarationReflection,
|
||||
IntrinsicType,
|
||||
ParameterReflection,
|
||||
Reflection,
|
||||
} from "typedoc"
|
||||
|
||||
export function cleanUpHookInput(
|
||||
parameters: ParameterReflection[]
|
||||
): ParameterReflection[] {
|
||||
return parameters.map((parameter) => {
|
||||
if (parameter.type?.type !== "reference" || !parameter.type.reflection) {
|
||||
return parameter
|
||||
}
|
||||
|
||||
cleanUpReflectionType(parameter.type.reflection)
|
||||
|
||||
if (
|
||||
parameter.type.reflection &&
|
||||
parameter.type.reflection instanceof DeclarationReflection &&
|
||||
parameter.type.reflection.children
|
||||
) {
|
||||
parameter.type.reflection.children.forEach(cleanUpReflectionType)
|
||||
}
|
||||
|
||||
return parameter
|
||||
})
|
||||
}
|
||||
|
||||
function cleanUpReflectionType(reflection: Reflection): Reflection {
|
||||
if (
|
||||
!(reflection instanceof DeclarationReflection) &&
|
||||
!(reflection instanceof ParameterReflection)
|
||||
) {
|
||||
return reflection
|
||||
}
|
||||
if (
|
||||
reflection.type?.type === "reference" &&
|
||||
reflection.type.name === "WorkflowData" &&
|
||||
reflection.type.typeArguments?.length
|
||||
) {
|
||||
reflection.type = reflection.type.typeArguments[0]
|
||||
}
|
||||
|
||||
if (reflection.defaultValue) {
|
||||
delete reflection.defaultValue
|
||||
}
|
||||
|
||||
if (reflection.name === "additional_data") {
|
||||
reflection.type = new IntrinsicType("Record<string, unknown> | undefined")
|
||||
} else if (
|
||||
reflection.type?.type === "intersection" &&
|
||||
reflection.type.types.length >= 2
|
||||
) {
|
||||
reflection.type = reflection.type.types[1]
|
||||
}
|
||||
|
||||
if (reflection instanceof DeclarationReflection && reflection.children) {
|
||||
reflection.children.forEach(cleanUpReflectionType)
|
||||
}
|
||||
|
||||
return reflection
|
||||
}
|
||||
@@ -2,6 +2,7 @@ export * from "./dml-utils"
|
||||
export * from "./get-type-children"
|
||||
export * from "./get-project-child"
|
||||
export * from "./get-type-str"
|
||||
export * from "./hooks-util"
|
||||
export * from "./step-utils"
|
||||
export * from "./str-formatting"
|
||||
export * from "./str-utils"
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
import { ArrayType, SignatureReflection, SomeType, UnionType } from "typedoc"
|
||||
|
||||
const disallowedIntrinsicTypeNames = ["unknown", "void", "any", "never"]
|
||||
|
||||
export function isWorkflowStep(reflection: SignatureReflection): boolean {
|
||||
return (
|
||||
reflection.parent?.children?.some((child) => child.name === "__step__") ||
|
||||
@@ -10,11 +12,7 @@ export function isWorkflowStep(reflection: SignatureReflection): boolean {
|
||||
export function getStepInputType(
|
||||
reflection: SignatureReflection
|
||||
): SomeType | undefined {
|
||||
if (!isWorkflowStep(reflection)) {
|
||||
return
|
||||
}
|
||||
|
||||
if (!reflection.parameters?.length) {
|
||||
if (!isWorkflowStep(reflection) || !reflection.parameters?.length) {
|
||||
return
|
||||
}
|
||||
|
||||
@@ -28,6 +26,13 @@ export function getStepOutputType(
|
||||
return
|
||||
}
|
||||
|
||||
if (
|
||||
reflection.type?.type === "intrinsic" &&
|
||||
disallowedIntrinsicTypeNames.includes(reflection.type.name)
|
||||
) {
|
||||
return
|
||||
}
|
||||
|
||||
if (reflection.type?.type !== "intersection") {
|
||||
return reflection.type
|
||||
}
|
||||
@@ -52,6 +57,12 @@ function cleanUpType(itemType: SomeType | undefined): SomeType | undefined {
|
||||
return cleanUpUnionType(itemType)
|
||||
case "array":
|
||||
return cleanUpArrayType(itemType)
|
||||
case "intrinsic":
|
||||
if (disallowedIntrinsicTypeNames.includes(itemType.name)) {
|
||||
return undefined
|
||||
}
|
||||
|
||||
return itemType
|
||||
default:
|
||||
return itemType
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { SignatureReflection } from "typedoc"
|
||||
import { ReferenceType, SignatureReflection, SomeType } from "typedoc"
|
||||
|
||||
export function isWorkflow(reflection: SignatureReflection): boolean {
|
||||
return (
|
||||
@@ -6,3 +6,57 @@ export function isWorkflow(reflection: SignatureReflection): boolean {
|
||||
false
|
||||
)
|
||||
}
|
||||
|
||||
export function getWorkflowInputType(
|
||||
reflection: SignatureReflection
|
||||
): SomeType | undefined {
|
||||
const exportedWorkflowType = getExportedWorkflowType(reflection)
|
||||
|
||||
const inputType = exportedWorkflowType?.typeArguments![0]
|
||||
|
||||
return isAllowedType(inputType) ? inputType : undefined
|
||||
}
|
||||
|
||||
export function getWorkflowOutputType(
|
||||
reflection: SignatureReflection
|
||||
): SomeType | undefined {
|
||||
const exportedWorkflowType = getExportedWorkflowType(reflection)
|
||||
|
||||
const outputType = exportedWorkflowType?.typeArguments![1]
|
||||
|
||||
return isAllowedType(outputType) ? outputType : undefined
|
||||
}
|
||||
|
||||
function getExportedWorkflowType(
|
||||
reflection: SignatureReflection
|
||||
): ReferenceType | undefined {
|
||||
if (
|
||||
!isWorkflow(reflection) ||
|
||||
reflection.type?.type !== "intersection" ||
|
||||
reflection.type.types.length < 2
|
||||
) {
|
||||
return
|
||||
}
|
||||
|
||||
const exportedWorkflowType = reflection.type.types[1]
|
||||
|
||||
if (
|
||||
exportedWorkflowType.type !== "reference" ||
|
||||
exportedWorkflowType.name !== "ExportedWorkflow" ||
|
||||
(exportedWorkflowType.typeArguments?.length || 0) < 1
|
||||
) {
|
||||
return
|
||||
}
|
||||
|
||||
return exportedWorkflowType
|
||||
}
|
||||
|
||||
const disallowedIntrinsicTypeNames = ["unknown", "void", "any", "never"]
|
||||
|
||||
function isAllowedType(type: SomeType | undefined): boolean {
|
||||
return (
|
||||
type !== undefined &&
|
||||
(type.type !== "intrinsic" ||
|
||||
!disallowedIntrinsicTypeNames.includes(type.name))
|
||||
)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user