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:
1 parent
c21407afbe
commit
fe200e0bb3
7 files changed
+110
-266
No files matched your search
@@ -16,6 +16,22 @@ const customOptions: Record<string, Partial<TypeDocOptions>> = {
|
|||||||
name: "core-flows",
|
name: "core-flows",
|
||||||
plugin: ["typedoc-plugin-workflows"],
|
plugin: ["typedoc-plugin-workflows"],
|
||||||
enableWorkflowsPlugins: true,
|
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({
|
"auth-provider": getOptions({
|
||||||
entryPointPath: "packages/core/utils/src/auth/abstract-auth-provider.ts",
|
entryPointPath: "packages/core/utils/src/auth/abstract-auth-provider.ts",
|
||||||
@@ -31,7 +47,6 @@ const customOptions: Record<string, Partial<TypeDocOptions>> = {
|
|||||||
],
|
],
|
||||||
tsConfigName: "utils.json",
|
tsConfigName: "utils.json",
|
||||||
name: "dml",
|
name: "dml",
|
||||||
generateNamespaces: true,
|
|
||||||
}),
|
}),
|
||||||
file: getOptions({
|
file: getOptions({
|
||||||
entryPointPath: "packages/core/utils/src/file/abstract-file-provider.ts",
|
entryPointPath: "packages/core/utils/src/file/abstract-file-provider.ts",
|
||||||
|
|||||||
@@ -36,6 +36,7 @@
|
|||||||
"dependencies": {
|
"dependencies": {
|
||||||
"eslint": "^8.53.0",
|
"eslint": "^8.53.0",
|
||||||
"glob": "^10.3.10",
|
"glob": "^10.3.10",
|
||||||
|
"minimatch": "^10.0.1",
|
||||||
"utils": "*",
|
"utils": "*",
|
||||||
"yaml": "^2.3.3"
|
"yaml": "^2.3.3"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,277 +1,81 @@
|
|||||||
|
import { minimatch } from "minimatch"
|
||||||
import {
|
import {
|
||||||
Application,
|
Application,
|
||||||
Comment,
|
Comment,
|
||||||
CommentDisplayPart,
|
|
||||||
CommentTag,
|
|
||||||
Context,
|
|
||||||
Converter,
|
Converter,
|
||||||
DeclarationReflection,
|
DeclarationReflection,
|
||||||
ParameterType,
|
ParameterType,
|
||||||
Reflection,
|
|
||||||
ReflectionCategory,
|
|
||||||
ReflectionKind,
|
ReflectionKind,
|
||||||
} from "typedoc"
|
} from "typedoc"
|
||||||
|
import { NamespaceGenerateDetails } from "types"
|
||||||
|
|
||||||
type PluginOptions = {
|
export function load(app: Application) {
|
||||||
generateNamespaces: boolean
|
app.options.addDeclaration({
|
||||||
parentNamespace: string
|
name: "enableNamespaceGenerator",
|
||||||
namePrefix: string
|
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 {
|
const generatedNamespaces: Map<string, DeclarationReflection> = new Map()
|
||||||
private options?: PluginOptions
|
|
||||||
private app: Application
|
|
||||||
private parentNamespace?: DeclarationReflection
|
|
||||||
private currentNamespaceHeirarchy: DeclarationReflection[]
|
|
||||||
private currentContext?: Context
|
|
||||||
private scannedComments = false
|
|
||||||
|
|
||||||
constructor(app: Application) {
|
app.converter.on(Converter.EVENT_BEGIN, (context) => {
|
||||||
this.app = app
|
if (!app.options.getValue("enableNamespaceGenerator")) {
|
||||||
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) {
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
this.options = {
|
const namespaces = app.options.getValue(
|
||||||
generateNamespaces: this.app.options.getValue("generateNamespaces"),
|
"generateNamespaces"
|
||||||
parentNamespace: this.app.options.getValue("parentNamespace"),
|
) as unknown as NamespaceGenerateDetails[]
|
||||||
namePrefix: this.app.options.getValue("namePrefix"),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
loadNamespace(namespaceName: string): DeclarationReflection {
|
namespaces.forEach((namespace) => {
|
||||||
const formattedName = this.formatName(namespaceName)
|
const genNamespace = context.createDeclarationReflection(
|
||||||
return this.currentContext?.project
|
ReflectionKind.Namespace,
|
||||||
.getReflectionsByKind(ReflectionKind.Namespace)
|
void 0,
|
||||||
.find(
|
void 0,
|
||||||
(m) =>
|
namespace.name
|
||||||
m.name === formattedName &&
|
)
|
||||||
(!this.currentNamespaceHeirarchy.length ||
|
|
||||||
m.parent?.id ===
|
|
||||||
this.currentNamespaceHeirarchy[
|
|
||||||
this.currentNamespaceHeirarchy.length - 1
|
|
||||||
].id)
|
|
||||||
) as DeclarationReflection
|
|
||||||
}
|
|
||||||
|
|
||||||
createNamespace(namespaceName: string): DeclarationReflection | undefined {
|
if (namespace.description) {
|
||||||
if (!this.currentContext) {
|
genNamespace.comment = new Comment([
|
||||||
return
|
{
|
||||||
}
|
kind: "text",
|
||||||
const formattedName = this.formatName(namespaceName)
|
text: namespace.description,
|
||||||
const namespace = this.currentContext?.createDeclarationReflection(
|
},
|
||||||
ReflectionKind.Namespace,
|
])
|
||||||
void 0,
|
|
||||||
void 0,
|
|
||||||
formattedName
|
|
||||||
)
|
|
||||||
|
|
||||||
namespace.children = []
|
|
||||||
|
|
||||||
return namespace
|
|
||||||
}
|
|
||||||
|
|
||||||
formatName(namespaceName: string): string {
|
|
||||||
return `${this.options?.namePrefix}${namespaceName}`
|
|
||||||
}
|
|
||||||
|
|
||||||
generateNamespaceFromTag({
|
|
||||||
tag,
|
|
||||||
summary,
|
|
||||||
}: {
|
|
||||||
tag: CommentTag
|
|
||||||
reflection?: DeclarationReflection
|
|
||||||
summary?: CommentDisplayPart[]
|
|
||||||
}) {
|
|
||||||
const categoryHeirarchy = tag.content[0].text.split(".")
|
|
||||||
categoryHeirarchy.forEach((cat, index) => {
|
|
||||||
// check whether a namespace exists with the category name.
|
|
||||||
let namespace = this.loadNamespace(cat)
|
|
||||||
|
|
||||||
if (!namespace) {
|
|
||||||
// add a namespace for this category
|
|
||||||
namespace = this.createNamespace(cat) || namespace
|
|
||||||
|
|
||||||
namespace.comment = new Comment()
|
|
||||||
if (this.currentNamespaceHeirarchy.length) {
|
|
||||||
namespace.comment.modifierTags.add("@namespaceMember")
|
|
||||||
}
|
|
||||||
if (summary && index === categoryHeirarchy.length - 1) {
|
|
||||||
namespace.comment.summary = summary
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
this.currentContext =
|
|
||||||
this.currentContext?.withScope(namespace) || this.currentContext
|
|
||||||
|
|
||||||
this.currentNamespaceHeirarchy.push(namespace)
|
generatedNamespaces.set(namespace.pathPattern, genNamespace)
|
||||||
})
|
})
|
||||||
}
|
})
|
||||||
|
|
||||||
/**
|
app.converter.on(
|
||||||
* create categories in the last namespace if the
|
Converter.EVENT_CREATE_DECLARATION,
|
||||||
* reflection has a category
|
(context, reflection) => {
|
||||||
*/
|
if (!app.options.getValue("enableNamespaceGenerator")) {
|
||||||
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) {
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
const comments = context.getFileComment(sourceFile)
|
const symbol = context.project.getSymbolFromReflection(reflection)
|
||||||
comments?.blockTags
|
const filePath = symbol?.valueDeclaration?.getSourceFile().fileName
|
||||||
.filter((tag) => tag.tag === "@customNamespace")
|
|
||||||
.forEach((tag) => {
|
|
||||||
this.generateNamespaceFromTag({ tag, summary: comments.summary })
|
|
||||||
if (this.currentNamespaceHeirarchy.length) {
|
|
||||||
// add comments of the file to the last created namespace
|
|
||||||
this.currentNamespaceHeirarchy[
|
|
||||||
this.currentNamespaceHeirarchy.length - 1
|
|
||||||
].comment = comments
|
|
||||||
|
|
||||||
this.currentNamespaceHeirarchy[
|
if (!filePath) {
|
||||||
this.currentNamespaceHeirarchy.length - 1
|
return
|
||||||
].comment!.blockTags = this.currentNamespaceHeirarchy[
|
}
|
||||||
this.currentNamespaceHeirarchy.length - 1
|
|
||||||
].comment!.blockTags.filter((tag) => tag.tag !== "@customNamespace")
|
|
||||||
}
|
|
||||||
// reset values
|
|
||||||
this.currentNamespaceHeirarchy = []
|
|
||||||
this.currentContext = context
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
this.scannedComments = true
|
generatedNamespaces.forEach((namespace, pathPattern) => {
|
||||||
}
|
if (!minimatch(filePath, pathPattern)) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
getReflectionComments(
|
namespace.addChild(reflection)
|
||||||
reflection: DeclarationReflection
|
})
|
||||||
): Comment | undefined {
|
|
||||||
if (reflection.comment) {
|
|
||||||
return reflection.comment
|
|
||||||
}
|
}
|
||||||
|
)
|
||||||
// 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 { load as signatureModifierPlugin } from "./signature-modifier"
|
||||||
import { MermaidDiagramGenerator } from "./mermaid-diagram-generator"
|
import { MermaidDiagramGenerator } from "./mermaid-diagram-generator"
|
||||||
import { load as parentIgnorePlugin } from "./parent-ignore"
|
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 { DmlRelationsResolver } from "./dml-relations-resolver"
|
||||||
import { load as dmlTypesNormalizer } from "./dml-types-normalizer"
|
import { load as dmlTypesNormalizer } from "./dml-types-normalizer"
|
||||||
import { MermaidDiagramDMLGenerator } from "./mermaid-diagram-dml-generator"
|
import { MermaidDiagramDMLGenerator } from "./mermaid-diagram-dml-generator"
|
||||||
@@ -23,8 +23,8 @@ export function load(app: Application) {
|
|||||||
parentIgnorePlugin(app)
|
parentIgnorePlugin(app)
|
||||||
dmlTypesNormalizer(app)
|
dmlTypesNormalizer(app)
|
||||||
dmlJsonParser(app)
|
dmlJsonParser(app)
|
||||||
|
generateNamespacePlugin(app)
|
||||||
|
|
||||||
new GenerateNamespacePlugin(app)
|
|
||||||
new MermaidDiagramGenerator(app)
|
new MermaidDiagramGenerator(app)
|
||||||
new DmlRelationsResolver(app)
|
new DmlRelationsResolver(app)
|
||||||
new MermaidDiagramDMLGenerator(app)
|
new MermaidDiagramDMLGenerator(app)
|
||||||
|
|||||||
+27
-13
@@ -197,19 +197,6 @@ export declare module "typedoc" {
|
|||||||
* @defaultValue true
|
* @defaultValue true
|
||||||
*/
|
*/
|
||||||
outputModules: boolean
|
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.
|
* Whether to enable the React Query manipulator.
|
||||||
* @defaultValue false
|
* @defaultValue false
|
||||||
@@ -262,6 +249,15 @@ export declare module "typedoc" {
|
|||||||
* @defaultValue false
|
* @defaultValue false
|
||||||
*/
|
*/
|
||||||
enableWorkflowsPlugins: boolean
|
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
|
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 {
|
export function isWorkflow(reflection: SignatureReflection): boolean {
|
||||||
return (
|
return (
|
||||||
reflection.parent.children?.some((child) => child.name === "runAsStep") ||
|
reflection.parent?.children?.some((child) => child.name === "runAsStep") ||
|
||||||
false
|
false
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -3906,6 +3906,15 @@ __metadata:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
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":
|
"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
|
version: 3.1.2
|
||||||
resolution: "minimatch@npm:3.1.2"
|
resolution: "minimatch@npm:3.1.2"
|
||||||
@@ -5374,6 +5383,7 @@ __metadata:
|
|||||||
"@types/node": ^16.11.10
|
"@types/node": ^16.11.10
|
||||||
eslint: ^8.53.0
|
eslint: ^8.53.0
|
||||||
glob: ^10.3.10
|
glob: ^10.3.10
|
||||||
|
minimatch: ^10.0.1
|
||||||
types: "*"
|
types: "*"
|
||||||
typescript: 5.5
|
typescript: 5.5
|
||||||
utils: "*"
|
utils: "*"
|
||||||
|
|||||||
Reference in new issue
Block a user