docs-util: added a namespaces plugin (#8500)
Added a namespaces plugin that allows generating namespaces and automatically adds reflections in a path pattern to the namespace. This is particularly useful for the workflows reference, as workflows under the `**/packages/core/core-flows/**/workflows/**` path pattern are shown under a Workflows title, and steps under the path pattern `**/packages/core/core-flows/**/steps/**` are shown under a Steps title.
This commit is contained in:
@@ -16,6 +16,22 @@ const customOptions: Record<string, Partial<TypeDocOptions>> = {
|
||||
name: "core-flows",
|
||||
plugin: ["typedoc-plugin-workflows"],
|
||||
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/**",
|
||||
},
|
||||
],
|
||||
}),
|
||||
"auth-provider": getOptions({
|
||||
entryPointPath: "packages/core/utils/src/auth/abstract-auth-provider.ts",
|
||||
@@ -31,7 +47,6 @@ const customOptions: Record<string, Partial<TypeDocOptions>> = {
|
||||
],
|
||||
tsConfigName: "utils.json",
|
||||
name: "dml",
|
||||
generateNamespaces: true,
|
||||
}),
|
||||
file: getOptions({
|
||||
entryPointPath: "packages/core/utils/src/file/abstract-file-provider.ts",
|
||||
|
||||
@@ -36,6 +36,7 @@
|
||||
"dependencies": {
|
||||
"eslint": "^8.53.0",
|
||||
"glob": "^10.3.10",
|
||||
"minimatch": "^10.0.1",
|
||||
"utils": "*",
|
||||
"yaml": "^2.3.3"
|
||||
}
|
||||
|
||||
@@ -1,277 +1,81 @@
|
||||
import { minimatch } from "minimatch"
|
||||
import {
|
||||
Application,
|
||||
Comment,
|
||||
CommentDisplayPart,
|
||||
CommentTag,
|
||||
Context,
|
||||
Converter,
|
||||
DeclarationReflection,
|
||||
ParameterType,
|
||||
Reflection,
|
||||
ReflectionCategory,
|
||||
ReflectionKind,
|
||||
} from "typedoc"
|
||||
import { NamespaceGenerateDetails } from "types"
|
||||
|
||||
type PluginOptions = {
|
||||
generateNamespaces: boolean
|
||||
parentNamespace: string
|
||||
namePrefix: string
|
||||
}
|
||||
export function load(app: Application) {
|
||||
app.options.addDeclaration({
|
||||
name: "enableNamespaceGenerator",
|
||||
type: ParameterType.Boolean,
|
||||
defaultValue: false,
|
||||
help: "Whether to enable the namespace generator plugin.",
|
||||
})
|
||||
app.options.addDeclaration({
|
||||
name: "generateNamespaces",
|
||||
type: ParameterType.Mixed,
|
||||
defaultValue: [],
|
||||
help: "The namespaces to generate.",
|
||||
})
|
||||
|
||||
export class GenerateNamespacePlugin {
|
||||
private options?: PluginOptions
|
||||
private app: Application
|
||||
private parentNamespace?: DeclarationReflection
|
||||
private currentNamespaceHeirarchy: DeclarationReflection[]
|
||||
private currentContext?: Context
|
||||
private scannedComments = false
|
||||
const generatedNamespaces: Map<string, DeclarationReflection> = new Map()
|
||||
|
||||
constructor(app: Application) {
|
||||
this.app = app
|
||||
this.currentNamespaceHeirarchy = []
|
||||
this.declareOptions()
|
||||
|
||||
this.app.converter.on(
|
||||
Converter.EVENT_RESOLVE,
|
||||
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) {
|
||||
app.converter.on(Converter.EVENT_BEGIN, (context) => {
|
||||
if (!app.options.getValue("enableNamespaceGenerator")) {
|
||||
return
|
||||
}
|
||||
|
||||
this.options = {
|
||||
generateNamespaces: this.app.options.getValue("generateNamespaces"),
|
||||
parentNamespace: this.app.options.getValue("parentNamespace"),
|
||||
namePrefix: this.app.options.getValue("namePrefix"),
|
||||
}
|
||||
}
|
||||
const namespaces = app.options.getValue(
|
||||
"generateNamespaces"
|
||||
) as unknown as NamespaceGenerateDetails[]
|
||||
|
||||
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
|
||||
}
|
||||
namespaces.forEach((namespace) => {
|
||||
const genNamespace = context.createDeclarationReflection(
|
||||
ReflectionKind.Namespace,
|
||||
void 0,
|
||||
void 0,
|
||||
namespace.name
|
||||
)
|
||||
|
||||
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
|
||||
}
|
||||
if (namespace.description) {
|
||||
genNamespace.comment = new Comment([
|
||||
{
|
||||
kind: "text",
|
||||
text: namespace.description,
|
||||
},
|
||||
])
|
||||
}
|
||||
this.currentContext =
|
||||
this.currentContext?.withScope(namespace) || this.currentContext
|
||||
|
||||
this.currentNamespaceHeirarchy.push(namespace)
|
||||
generatedNamespaces.set(namespace.pathPattern, genNamespace)
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
/**
|
||||
* create categories in the last namespace if the
|
||||
* reflection has a category
|
||||
*/
|
||||
attachCategories(
|
||||
reflection: DeclarationReflection,
|
||||
comments: Comment | undefined
|
||||
) {
|
||||
if (!this.currentNamespaceHeirarchy.length) {
|
||||
return
|
||||
}
|
||||
|
||||
const parentNamespace =
|
||||
this.currentNamespaceHeirarchy[this.currentNamespaceHeirarchy.length - 1]
|
||||
comments?.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: Reflection) {
|
||||
if (!(reflection instanceof DeclarationReflection)) {
|
||||
return
|
||||
}
|
||||
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
|
||||
const comments = this.getReflectionComments(reflection)
|
||||
comments?.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)
|
||||
})
|
||||
|
||||
comments?.removeTags("@customNamespace")
|
||||
this.attachCategories(reflection, comments)
|
||||
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) {
|
||||
app.converter.on(
|
||||
Converter.EVENT_CREATE_DECLARATION,
|
||||
(context, reflection) => {
|
||||
if (!app.options.getValue("enableNamespaceGenerator")) {
|
||||
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
|
||||
const symbol = context.project.getSymbolFromReflection(reflection)
|
||||
const filePath = symbol?.valueDeclaration?.getSourceFile().fileName
|
||||
|
||||
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
|
||||
})
|
||||
})
|
||||
if (!filePath) {
|
||||
return
|
||||
}
|
||||
|
||||
this.scannedComments = true
|
||||
}
|
||||
generatedNamespaces.forEach((namespace, pathPattern) => {
|
||||
if (!minimatch(filePath, pathPattern)) {
|
||||
return
|
||||
}
|
||||
|
||||
getReflectionComments(
|
||||
reflection: DeclarationReflection
|
||||
): Comment | undefined {
|
||||
if (reflection.comment) {
|
||||
return reflection.comment
|
||||
namespace.addChild(reflection)
|
||||
})
|
||||
}
|
||||
|
||||
// try to retrieve comment from signature
|
||||
if (!reflection.signatures?.length) {
|
||||
return
|
||||
}
|
||||
return reflection.signatures.find((signature) => signature.comment)?.comment
|
||||
}
|
||||
|
||||
// for debugging
|
||||
printCurrentHeirarchy() {
|
||||
return this.currentNamespaceHeirarchy.map((heirarchy) => heirarchy.name)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@ import { load as eslintExamplePlugin } from "./eslint-example"
|
||||
import { load as signatureModifierPlugin } from "./signature-modifier"
|
||||
import { MermaidDiagramGenerator } from "./mermaid-diagram-generator"
|
||||
import { load as parentIgnorePlugin } from "./parent-ignore"
|
||||
import { GenerateNamespacePlugin } from "./generate-namespace"
|
||||
import { load as generateNamespacePlugin } from "./generate-namespace"
|
||||
import { DmlRelationsResolver } from "./dml-relations-resolver"
|
||||
import { load as dmlTypesNormalizer } from "./dml-types-normalizer"
|
||||
import { MermaidDiagramDMLGenerator } from "./mermaid-diagram-dml-generator"
|
||||
@@ -23,8 +23,8 @@ export function load(app: Application) {
|
||||
parentIgnorePlugin(app)
|
||||
dmlTypesNormalizer(app)
|
||||
dmlJsonParser(app)
|
||||
generateNamespacePlugin(app)
|
||||
|
||||
new GenerateNamespacePlugin(app)
|
||||
new MermaidDiagramGenerator(app)
|
||||
new DmlRelationsResolver(app)
|
||||
new MermaidDiagramDMLGenerator(app)
|
||||
|
||||
40
www/utils/packages/types/lib/index.d.ts
vendored
40
www/utils/packages/types/lib/index.d.ts
vendored
@@ -197,19 +197,6 @@ 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
|
||||
@@ -262,6 +249,15 @@ export declare module "typedoc" {
|
||||
* @defaultValue false
|
||||
*/
|
||||
enableWorkflowsPlugins: boolean
|
||||
/**
|
||||
* Whether to enable the namespace generator plugin.
|
||||
* @defaultValue false
|
||||
*/
|
||||
enableNamespaceGenerator: boolean
|
||||
/**
|
||||
* The namespaces to generate.
|
||||
*/
|
||||
generateNamespaces: NamespaceGenerateDetails[]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -273,3 +269,21 @@ export declare type DmlFile = {
|
||||
properties: DmlObject
|
||||
}
|
||||
}
|
||||
|
||||
export declare type NamespaceGenerateDetails = {
|
||||
/**
|
||||
* The namespace's names.
|
||||
*/
|
||||
name: string
|
||||
/**
|
||||
* The namespace's description. Will be attached
|
||||
* as a summary comment.
|
||||
*/
|
||||
description?: string
|
||||
/**
|
||||
* A path pattern to pass to minimatch that
|
||||
* checks if a file / its reflections belong to the
|
||||
* namespace
|
||||
*/
|
||||
pathPattern: string
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@ import { SignatureReflection } from "typedoc"
|
||||
|
||||
export function isWorkflow(reflection: SignatureReflection): boolean {
|
||||
return (
|
||||
reflection.parent.children?.some((child) => child.name === "runAsStep") ||
|
||||
reflection.parent?.children?.some((child) => child.name === "runAsStep") ||
|
||||
false
|
||||
)
|
||||
}
|
||||
|
||||
@@ -3906,6 +3906,15 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"minimatch@npm:^10.0.1":
|
||||
version: 10.0.1
|
||||
resolution: "minimatch@npm:10.0.1"
|
||||
dependencies:
|
||||
brace-expansion: ^2.0.1
|
||||
checksum: e6c29a81fe83e1877ad51348306be2e8aeca18c88fdee7a99df44322314279e15799e41d7cb274e4e8bb0b451a3bc622d6182e157dfa1717d6cda75e9cd8cd5d
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"minimatch@npm:^3.0.2, minimatch@npm:^3.0.3, minimatch@npm:^3.0.5, minimatch@npm:^3.1.1, minimatch@npm:^3.1.2":
|
||||
version: 3.1.2
|
||||
resolution: "minimatch@npm:3.1.2"
|
||||
@@ -5374,6 +5383,7 @@ __metadata:
|
||||
"@types/node": ^16.11.10
|
||||
eslint: ^8.53.0
|
||||
glob: ^10.3.10
|
||||
minimatch: ^10.0.1
|
||||
types: "*"
|
||||
typescript: 5.5
|
||||
utils: "*"
|
||||
|
||||
Reference in New Issue
Block a user