docs-util: added workflows typedoc plugin (#8463)
* initial changes * finish workflow plugin * add comments * remove todo
This commit is contained in:
@@ -16,6 +16,8 @@
|
||||
"type": "module",
|
||||
"exports": "./dist/index.js",
|
||||
"dependencies": {
|
||||
"@medusajs/core-flows": "link:../../../../packages/core/core-flows",
|
||||
"@medusajs/workflows-sdk": "link:../../../../packages/core/workflows-sdk",
|
||||
"chalk": "^5.3.0",
|
||||
"commander": "^11.1.0",
|
||||
"dotenv": "^16.3.1",
|
||||
@@ -25,6 +27,7 @@
|
||||
"typedoc-plugin-custom": "*",
|
||||
"typedoc-plugin-markdown-medusa": "*",
|
||||
"typedoc-plugin-rename-defaults": "^0.7.0",
|
||||
"typedoc-plugin-workflows": "*",
|
||||
"types": "*",
|
||||
"typescript": "5.5",
|
||||
"utils": "*",
|
||||
|
||||
@@ -19,6 +19,10 @@ export default async function generate(
|
||||
) {
|
||||
const references = names.includes("all") ? allReferences : names
|
||||
|
||||
if (references.includes("core-flows")) {
|
||||
await import("@medusajs/core-flows")
|
||||
}
|
||||
|
||||
for (const referenceName of references) {
|
||||
const referenceType = getReferenceType(referenceName)
|
||||
try {
|
||||
|
||||
@@ -10,6 +10,13 @@ import { rootPathPrefix } from "./general.js"
|
||||
import { modules } from "./references.js"
|
||||
|
||||
const customOptions: Record<string, Partial<TypeDocOptions>> = {
|
||||
"core-flows": getOptions({
|
||||
entryPointPath: "packages/core/core-flows/src/index.ts",
|
||||
tsConfigName: "core-flows.json",
|
||||
name: "core-flows",
|
||||
plugin: ["typedoc-plugin-workflows"],
|
||||
enableWorkflowsPlugins: true,
|
||||
}),
|
||||
"auth-provider": getOptions({
|
||||
entryPointPath: "packages/core/utils/src/auth/abstract-auth-provider.ts",
|
||||
tsConfigName: "utils.json",
|
||||
|
||||
@@ -21,6 +21,7 @@ export const modules = [
|
||||
|
||||
const allReferences = [
|
||||
...modules,
|
||||
"core-flows",
|
||||
"auth-provider",
|
||||
"dml",
|
||||
"file",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import * as Handlebars from "handlebars"
|
||||
import { Reflection, SignatureReflection } from "typedoc"
|
||||
import { isWorkflowStep } from "../../utils/step-utils"
|
||||
import { isWorkflowStep } from "utils"
|
||||
|
||||
export default function () {
|
||||
Handlebars.registerHelper("example", function (reflection: Reflection) {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import * as Handlebars from "handlebars"
|
||||
import { SignatureReflection } from "typedoc"
|
||||
import { isWorkflowStep } from "../../utils/step-utils"
|
||||
import { isWorkflowStep } from "utils"
|
||||
|
||||
export default function () {
|
||||
Handlebars.registerHelper(
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { MarkdownTheme } from "../../theme"
|
||||
import * as Handlebars from "handlebars"
|
||||
import { SignatureReflection } from "typedoc"
|
||||
import { getStepInputType } from "../../utils/step-utils"
|
||||
import { getStepInputType } from "utils"
|
||||
import { formatParameterComponent } from "../../utils/format-parameter-component"
|
||||
import { getReflectionTypeParameters } from "../../utils/reflection-type-parameters"
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { MarkdownTheme } from "../../theme"
|
||||
import * as Handlebars from "handlebars"
|
||||
import { SignatureReflection } from "typedoc"
|
||||
import { getStepOutputType } from "../../utils/step-utils"
|
||||
import { getStepOutputType } from "utils"
|
||||
import { formatParameterComponent } from "../../utils/format-parameter-component"
|
||||
import { getReflectionTypeParameters } from "../../utils/reflection-type-parameters"
|
||||
|
||||
|
||||
2
www/utils/packages/typedoc-plugin-workflows/.gitignore
vendored
Normal file
2
www/utils/packages/typedoc-plugin-workflows/.gitignore
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
dist
|
||||
.yarn
|
||||
44
www/utils/packages/typedoc-plugin-workflows/package.json
Normal file
44
www/utils/packages/typedoc-plugin-workflows/package.json
Normal file
@@ -0,0 +1,44 @@
|
||||
{
|
||||
"name": "typedoc-plugin-workflows",
|
||||
"private": true,
|
||||
"version": "0.0.0",
|
||||
"license": "MIT",
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
},
|
||||
"description": "A plugin that generates information relevant for workflows",
|
||||
"main": "./dist/index.js",
|
||||
"exports": "./dist/index.js",
|
||||
"files": [
|
||||
"dist"
|
||||
],
|
||||
"author": "Shahed Nasser",
|
||||
"scripts": {
|
||||
"build": "tsc",
|
||||
"watch": "tsc --watch",
|
||||
"lint": "eslint --ext .ts src"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"typedoc": "0.26.x"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/eslint": "^8.56.6",
|
||||
"@types/node": "^16.11.10",
|
||||
"types": "*",
|
||||
"typescript": "5.5"
|
||||
},
|
||||
"keywords": [
|
||||
"typedocplugin",
|
||||
"packages",
|
||||
"monorepo",
|
||||
"typedoc"
|
||||
],
|
||||
"dependencies": {
|
||||
"@medusajs/core-flows": "link:../../../../packages/core/core-flows",
|
||||
"@medusajs/workflows-sdk": "link:../../../../packages/core/workflows-sdk",
|
||||
"eslint": "^8.53.0",
|
||||
"glob": "^10.3.10",
|
||||
"utils": "*",
|
||||
"yaml": "^2.3.3"
|
||||
}
|
||||
}
|
||||
6
www/utils/packages/typedoc-plugin-workflows/src/index.ts
Normal file
6
www/utils/packages/typedoc-plugin-workflows/src/index.ts
Normal file
@@ -0,0 +1,6 @@
|
||||
import { Application } from "typedoc"
|
||||
import WorkflowsPlugin from "./plugin"
|
||||
|
||||
export function load(app: Application) {
|
||||
new WorkflowsPlugin(app)
|
||||
}
|
||||
315
www/utils/packages/typedoc-plugin-workflows/src/plugin.ts
Normal file
315
www/utils/packages/typedoc-plugin-workflows/src/plugin.ts
Normal file
@@ -0,0 +1,315 @@
|
||||
import {
|
||||
Application,
|
||||
Comment,
|
||||
Context,
|
||||
Converter,
|
||||
DeclarationReflection,
|
||||
DocumentReflection,
|
||||
ParameterReflection,
|
||||
ParameterType,
|
||||
ReferenceType,
|
||||
ReflectionKind,
|
||||
SignatureReflection,
|
||||
} from "typedoc"
|
||||
import ts, { SyntaxKind, VariableStatement } from "typescript"
|
||||
import { WorkflowManager, WorkflowDefinition } from "@medusajs/orchestration"
|
||||
import Helper from "./utils/helper"
|
||||
import { isWorkflow } from "utils"
|
||||
|
||||
/**
|
||||
* A plugin that extracts a workflow's steps, hooks, their types, and attaches them as
|
||||
* documents to the workflow's reflection.
|
||||
*/
|
||||
class WorkflowsPlugin {
|
||||
protected app: Application
|
||||
protected helper: Helper
|
||||
|
||||
constructor(app: Application) {
|
||||
this.app = app
|
||||
this.helper = new Helper()
|
||||
|
||||
this.registerOptions()
|
||||
this.registerEventHandlers()
|
||||
}
|
||||
|
||||
/**
|
||||
* Register the plugin's options.
|
||||
*/
|
||||
registerOptions() {
|
||||
this.app.options.addDeclaration({
|
||||
name: "enableWorkflowsPlugins",
|
||||
help: "Whether to enable the workflows plugin.",
|
||||
type: ParameterType.Boolean, // The default
|
||||
defaultValue: false,
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Register event handlers.
|
||||
*/
|
||||
registerEventHandlers() {
|
||||
this.app.converter.on(
|
||||
Converter.EVENT_RESOLVE_BEGIN,
|
||||
this.handleResolve.bind(this)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* When the converter begins resolving a project, this method is triggered. It finds
|
||||
* all signatures that are workflows and attaches the necessary information to them.
|
||||
*
|
||||
* @param context - The project's context.
|
||||
*/
|
||||
handleResolve(context: Context) {
|
||||
for (const reflection of context.project.getReflectionsByKind(
|
||||
ReflectionKind.All
|
||||
)) {
|
||||
if (!(reflection instanceof SignatureReflection)) {
|
||||
continue
|
||||
}
|
||||
|
||||
if (isWorkflow(reflection)) {
|
||||
const { initializer } =
|
||||
this.helper.getReflectionSymbolAndInitializer({
|
||||
project: context.project,
|
||||
reflection: reflection.parent,
|
||||
}) || {}
|
||||
|
||||
if (
|
||||
!initializer ||
|
||||
(!ts.isArrowFunction(initializer.arguments[1]) &&
|
||||
!ts.isFunctionExpression(initializer.arguments[1]))
|
||||
) {
|
||||
continue
|
||||
}
|
||||
|
||||
const workflowId = this.helper.getStepOrWorkflowId(
|
||||
initializer,
|
||||
context.project
|
||||
)
|
||||
|
||||
if (!workflowId) {
|
||||
continue
|
||||
}
|
||||
|
||||
this.parseSteps({
|
||||
workflowId,
|
||||
constructorFn: initializer.arguments[1],
|
||||
context,
|
||||
parentReflection: reflection.parent,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse the steps of a workflow and attach them as documents to the parent reflection.
|
||||
*
|
||||
* @param param0 - The workflow's details.
|
||||
*/
|
||||
parseSteps({
|
||||
workflowId,
|
||||
constructorFn,
|
||||
context,
|
||||
parentReflection,
|
||||
}: {
|
||||
workflowId: string
|
||||
constructorFn: ts.ArrowFunction | ts.FunctionExpression
|
||||
context: Context
|
||||
parentReflection: DeclarationReflection
|
||||
}) {
|
||||
// use the workflow manager to check whether something in the constructor
|
||||
// body is a step/hook
|
||||
const workflow = WorkflowManager.getWorkflow(workflowId)
|
||||
|
||||
if (!ts.isBlock(constructorFn.body)) {
|
||||
return
|
||||
}
|
||||
|
||||
if (!parentReflection.documents) {
|
||||
parentReflection.documents = []
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
if (!initializer) {
|
||||
return
|
||||
}
|
||||
|
||||
const { stepId, stepReflection } =
|
||||
this.parseStep({
|
||||
initializer,
|
||||
context,
|
||||
workflow,
|
||||
}) || {}
|
||||
|
||||
if (!stepId || !stepReflection) {
|
||||
return
|
||||
}
|
||||
|
||||
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)
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse a step to retrieve its ID and reflection.
|
||||
*
|
||||
* @param param0 - The step's details.
|
||||
* @returns The step's ID and reflection, if found.
|
||||
*/
|
||||
parseStep({
|
||||
initializer,
|
||||
context,
|
||||
workflow,
|
||||
}: {
|
||||
initializer: ts.CallExpression
|
||||
context: Context
|
||||
workflow?: WorkflowDefinition
|
||||
}):
|
||||
| {
|
||||
stepId: string
|
||||
stepReflection: DeclarationReflection
|
||||
}
|
||||
| undefined {
|
||||
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
|
||||
}
|
||||
|
||||
const { initializer } =
|
||||
this.helper.getReflectionSymbolAndInitializer({
|
||||
project: context.project,
|
||||
reflection: initializerReflection,
|
||||
}) || {}
|
||||
|
||||
if (!initializer) {
|
||||
return
|
||||
}
|
||||
|
||||
stepId = this.helper.getStepOrWorkflowId(
|
||||
initializer,
|
||||
context.project,
|
||||
true
|
||||
)
|
||||
stepReflection = initializerReflection
|
||||
}
|
||||
|
||||
// check if is a step in the workflow
|
||||
if (!stepId || !stepReflection || !workflow?.handlers_.get(stepId)) {
|
||||
return
|
||||
}
|
||||
|
||||
return {
|
||||
stepId,
|
||||
stepReflection,
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This method creates a declaration reflection for a hook, since a hook doesn't have its own reflection.
|
||||
*
|
||||
* @param param0 - The hook's details.
|
||||
* @returns The hook's reflection
|
||||
*/
|
||||
assembleHookReflection({
|
||||
stepId,
|
||||
context,
|
||||
inputSymbol,
|
||||
}: {
|
||||
stepId: string
|
||||
context: Context
|
||||
inputSymbol: ts.Symbol
|
||||
}): DeclarationReflection {
|
||||
const declarationReflection = context.createDeclarationReflection(
|
||||
ReflectionKind.Function,
|
||||
undefined,
|
||||
undefined,
|
||||
stepId
|
||||
)
|
||||
const signatureReflection = new SignatureReflection(
|
||||
stepId,
|
||||
ReflectionKind.SomeSignature,
|
||||
declarationReflection
|
||||
)
|
||||
|
||||
const parameter = new ParameterReflection(
|
||||
"input",
|
||||
ReflectionKind.Parameter,
|
||||
signatureReflection
|
||||
)
|
||||
|
||||
parameter.type = ReferenceType.createSymbolReference(inputSymbol, context)
|
||||
|
||||
signatureReflection.parameters = []
|
||||
|
||||
signatureReflection.parameters.push(parameter)
|
||||
|
||||
declarationReflection.signatures = []
|
||||
|
||||
declarationReflection.signatures.push(signatureReflection)
|
||||
|
||||
return declarationReflection
|
||||
}
|
||||
}
|
||||
|
||||
export default WorkflowsPlugin
|
||||
3
www/utils/packages/typedoc-plugin-workflows/src/types.ts
Normal file
3
www/utils/packages/typedoc-plugin-workflows/src/types.ts
Normal file
@@ -0,0 +1,3 @@
|
||||
export type StepType = "step" | "workflowStep" | "hook"
|
||||
|
||||
export type StepModifier = "@step" | "@workflowStep" | "@hook"
|
||||
116
www/utils/packages/typedoc-plugin-workflows/src/utils/helper.ts
Normal file
116
www/utils/packages/typedoc-plugin-workflows/src/utils/helper.ts
Normal file
@@ -0,0 +1,116 @@
|
||||
import { DeclarationReflection, ProjectReflection } from "typedoc"
|
||||
import ts from "typescript"
|
||||
import { StepModifier, StepType } from "../types"
|
||||
|
||||
/**
|
||||
* A class of helper methods.
|
||||
*/
|
||||
export default class Helper {
|
||||
/**
|
||||
* Remove from a name, preferrably a function expression's name, extraneous details.
|
||||
*
|
||||
* @param name - The name to normalize.
|
||||
* @returns The normalized name.
|
||||
*/
|
||||
normalizeName(name: string) {
|
||||
return name.replace(".runAsStep", "").replace(/^"/, "").replace(/"$/, "")
|
||||
}
|
||||
|
||||
/**
|
||||
* Get symbol and initializer of a reflection.
|
||||
*
|
||||
* @param param0 - The reflection's details.
|
||||
* @returns The symbol and initializer, if found.
|
||||
*/
|
||||
getReflectionSymbolAndInitializer({
|
||||
project,
|
||||
reflection,
|
||||
}: {
|
||||
project: ProjectReflection
|
||||
reflection: DeclarationReflection
|
||||
}):
|
||||
| {
|
||||
symbol: ts.Symbol
|
||||
initializer: ts.CallExpression
|
||||
}
|
||||
| undefined {
|
||||
const symbol = project.getSymbolFromReflection(reflection)
|
||||
|
||||
if (
|
||||
!symbol ||
|
||||
!symbol.valueDeclaration ||
|
||||
!("initializer" in symbol.valueDeclaration)
|
||||
) {
|
||||
return
|
||||
}
|
||||
|
||||
return {
|
||||
symbol,
|
||||
initializer: symbol.valueDeclaration.initializer as ts.CallExpression,
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the ID of a step or a workflow.
|
||||
*
|
||||
* @param initializer - The associated initializer. For example, `createWorkflow`.
|
||||
* @param project - The typedoc project.
|
||||
* @param checkWorkflowStep - Whether to check if a workflow is a step. If enabled, the `-as-step` suffix is added
|
||||
* to the ID. This is useful when testing against the workflow's steps retrieved by the workflow manager.
|
||||
* @returns The ID of the step or workflow.
|
||||
*/
|
||||
getStepOrWorkflowId(
|
||||
initializer: ts.CallExpression,
|
||||
project: ProjectReflection,
|
||||
checkWorkflowStep = false
|
||||
): string | undefined {
|
||||
const idVar = initializer.arguments[0]
|
||||
const isWorkflowStep =
|
||||
checkWorkflowStep && this.getStepType(initializer) === "workflowStep"
|
||||
const idVarName = this.normalizeName(idVar.getText())
|
||||
|
||||
// load it from the project
|
||||
const idVarReflection = project.getChildByName(idVarName)
|
||||
|
||||
if (
|
||||
!idVarReflection ||
|
||||
!(idVarReflection instanceof DeclarationReflection) ||
|
||||
idVarReflection.type?.type !== "literal"
|
||||
) {
|
||||
return
|
||||
}
|
||||
|
||||
const stepId = idVarReflection.type.value as string
|
||||
|
||||
return isWorkflowStep ? `${stepId}-as-step` : stepId
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the type of the step.
|
||||
*
|
||||
* @param initializer - The initializer of the step.
|
||||
* @returns The step's type.
|
||||
*/
|
||||
getStepType(initializer: ts.CallExpression): StepType {
|
||||
switch (initializer.expression.getText()) {
|
||||
case "createWorkflow":
|
||||
return "workflowStep"
|
||||
case "createHook":
|
||||
return "hook"
|
||||
default:
|
||||
return "step"
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the modifier to use based on the step's type.
|
||||
*
|
||||
* @param initializer - The step's initializer.
|
||||
* @returns The step's modifier.
|
||||
*/
|
||||
getModifier(initializer: ts.CallExpression): StepModifier {
|
||||
const stepType = this.getStepType(initializer)
|
||||
|
||||
return `@${stepType}`
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"extends": "../../tsconfig",
|
||||
"compilerOptions": {
|
||||
"outDir": "./dist",
|
||||
"rootDir": "./src"
|
||||
},
|
||||
"include": ["src"]
|
||||
}
|
||||
5
www/utils/packages/types/lib/index.d.ts
vendored
5
www/utils/packages/types/lib/index.d.ts
vendored
@@ -257,6 +257,11 @@ export declare module "typedoc" {
|
||||
* @defaultValue false
|
||||
*/
|
||||
normalizeDmlTypes: boolean
|
||||
/**
|
||||
* Whether to enable the workflows plugin.
|
||||
* @defaultValue false
|
||||
*/
|
||||
enableWorkflowsPlugins: boolean
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -11,7 +11,8 @@
|
||||
"scripts": {
|
||||
"build": "yarn clean && tsc",
|
||||
"lint": "eslint --ext .ts src",
|
||||
"clean": "rimraf dist"
|
||||
"clean": "rimraf dist",
|
||||
"watch": "tsc --watch"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"typedoc": "0.26.x"
|
||||
|
||||
@@ -2,5 +2,7 @@ export * from "./dml-utils"
|
||||
export * from "./get-type-children"
|
||||
export * from "./get-project-child"
|
||||
export * from "./get-type-str"
|
||||
export * from "./step-utils"
|
||||
export * from "./str-formatting"
|
||||
export * from "./str-utils"
|
||||
export * from "./workflow-utils"
|
||||
|
||||
8
www/utils/packages/utils/src/workflow-utils.ts
Normal file
8
www/utils/packages/utils/src/workflow-utils.ts
Normal file
@@ -0,0 +1,8 @@
|
||||
import { SignatureReflection } from "typedoc"
|
||||
|
||||
export function isWorkflow(reflection: SignatureReflection): boolean {
|
||||
return (
|
||||
reflection.parent.children?.some((child) => child.name === "runAsStep") ||
|
||||
false
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user