From c5915451b8115cc7383e6551f7df93496951ad29 Mon Sep 17 00:00:00 2001 From: Shahed Nasser Date: Fri, 10 Jan 2025 19:16:43 +0200 Subject: [PATCH] docs-util: add examples generator for workflows and steps (#10914) * initial changes * docs-util: generate examples for workflows and steps --- .../components/MDXComponents/index.tsx | 4 + .../packages/docs-generator/package.json | 2 - .../src/classes/examples/oas.ts | 73 +--- .../src/classes/helpers/formatter.ts | 28 +- .../package.json | 2 + .../src/render-utils.ts | 4 + .../src/resources/helpers/step-examples.ts | 60 ++++ .../resources/helpers/workflow-examples.ts | 179 ++++++++++ .../src/resources/partials/member.step.hbs | 2 +- .../resources/partials/member.workflow.hbs | 2 +- .../typedoc-plugin-workflows/src/plugin.ts | 5 +- .../src/utils/examples.ts | 35 ++ .../src/utils/helper.ts | 38 +-- www/utils/packages/utils/package.json | 2 + .../packages/utils/src/examples-utils.ts | 319 ++++++++++++++++++ www/utils/packages/utils/src/index.ts | 1 + www/utils/yarn.lock | 196 ++++++++++- 17 files changed, 810 insertions(+), 142 deletions(-) create mode 100644 www/utils/packages/typedoc-plugin-markdown-medusa/src/resources/helpers/step-examples.ts create mode 100644 www/utils/packages/typedoc-plugin-markdown-medusa/src/resources/helpers/workflow-examples.ts create mode 100644 www/utils/packages/typedoc-plugin-workflows/src/utils/examples.ts create mode 100644 www/utils/packages/utils/src/examples-utils.ts diff --git a/www/apps/resources/components/MDXComponents/index.tsx b/www/apps/resources/components/MDXComponents/index.tsx index ab0cb0ceba887..85dedcae7f3f6 100644 --- a/www/apps/resources/components/MDXComponents/index.tsx +++ b/www/apps/resources/components/MDXComponents/index.tsx @@ -5,6 +5,8 @@ import { TypeList, WorkflowDiagram, SourceCodeLink, + CodeTabs, + CodeTab, } from "docs-ui" import { CommerceModuleSections } from "../CommerceModuleSections" @@ -15,6 +17,8 @@ const MDXComponents: MDXComponentsType = { WorkflowDiagram, CommerceModuleSections, SourceCodeLink, + CodeTabs, + CodeTab, } export default MDXComponents diff --git a/www/utils/packages/docs-generator/package.json b/www/utils/packages/docs-generator/package.json index 3ce3a2a271c93..6c8a70a31e44d 100644 --- a/www/utils/packages/docs-generator/package.json +++ b/www/utils/packages/docs-generator/package.json @@ -17,7 +17,6 @@ "type": "module", "exports": "./dist/index.js", "dependencies": { - "@faker-js/faker": "^8.4.0", "@octokit/core": "^5.0.2", "chalk": "^5.4.1", "commander": "^11.1.0", @@ -27,7 +26,6 @@ "openai": "^4.29.1", "openapi-types": "^12.1.3", "pluralize": "^8.0.0", - "prettier": "^3.2.4", "ts-node": "^10.9.1", "typescript": "^5.6.2", "utils": "*", diff --git a/www/utils/packages/docs-generator/src/classes/examples/oas.ts b/www/utils/packages/docs-generator/src/classes/examples/oas.ts index 008f883a8677f..40a943a37f566 100644 --- a/www/utils/packages/docs-generator/src/classes/examples/oas.ts +++ b/www/utils/packages/docs-generator/src/classes/examples/oas.ts @@ -1,8 +1,13 @@ -import { faker } from "@faker-js/faker" import { OpenAPIV3 } from "openapi-types" import { OasArea } from "../kinds/oas.js" import { CodeSample } from "../../types/index.js" -import { capitalize, kebabToCamel, wordsToCamel, wordsToKebab } from "utils" +import { + capitalize, + getFakeStrValue, + kebabToCamel, + wordsToCamel, + wordsToKebab, +} from "utils" import { API_ROUTE_PARAM_REGEX } from "../../constants.js" type CodeSampleData = Omit @@ -274,7 +279,7 @@ class OasExamplesGenerator { ? this.getSchemaRequiredData( typedChildProp as OpenAPIV3.SchemaObject ) - : this.getFakeValue({ + : getFakeStrValue({ name: childName, type: typedChildProp.type, format: typedChildProp.format, @@ -296,7 +301,7 @@ class OasExamplesGenerator { ? this.getSchemaRequiredData( property.items as OpenAPIV3.SchemaObject ) - : this.getFakeValue({ + : getFakeStrValue({ name: propertyName, type: propertyItems.type, format: propertyItems.format, @@ -305,7 +310,7 @@ class OasExamplesGenerator { } } else if (property.type) { // retrieve fake value for all other types - value = this.getFakeValue({ + value = getFakeStrValue({ name: propertyName, type: property.type, format: property.format, @@ -320,64 +325,6 @@ class OasExamplesGenerator { return data } - - /** - * Retrieve the fake value of a property. The value is used in examples. - * - * @param param0 - The property's details - * @returns The fake value - */ - getFakeValue({ - name, - type, - format, - }: { - /** - * The name of the property. It can help when generating the fake value. - * For example, if the name is `id`, the fake value generated will be of the format `id_`. - */ - name: string - /** - * The type of the property. - */ - type: OpenAPIV3.NonArraySchemaObjectType | "array" - /** - * The OAS format of the property. For example, `date-time`. - */ - format?: string - }): unknown { - let value: unknown - - switch (true) { - case type === "string" && format === "date-time": - value = faker.date.future().toISOString() - break - case type === "boolean": - value = faker.datatype.boolean() - break - case type === "integer" || type === "number": - value = faker.number.int() - break - case type === "array": - value = [] - break - case type === "string": - value = faker.helpers - .mustache(`{{${name}}}`, { - id: () => - `id_${faker.string.alphanumeric({ - length: { min: 10, max: 20 }, - })}`, - name: () => faker.person.firstName(), - email: () => faker.internet.email(), - password: () => faker.internet.password({ length: 8 }), - currency: () => faker.finance.currencyCode(), - }) - .replace(`{{${name}}}`, "{value}") - } - - return value !== undefined ? value : "{value}" - } } export default OasExamplesGenerator diff --git a/www/utils/packages/docs-generator/src/classes/helpers/formatter.ts b/www/utils/packages/docs-generator/src/classes/helpers/formatter.ts index 7a18c1362cac7..00d184b6c9651 100644 --- a/www/utils/packages/docs-generator/src/classes/helpers/formatter.ts +++ b/www/utils/packages/docs-generator/src/classes/helpers/formatter.ts @@ -4,8 +4,8 @@ import path from "path" import dirname from "../../utils/dirname.js" import { minimatch } from "minimatch" import { existsSync } from "fs" -import * as prettier from "prettier" import getRelativePaths from "../../utils/get-relative-paths.js" +import { formatWithPrettier } from "utils" /** * A class used to apply formatting to files using ESLint and other formatting options. @@ -174,10 +174,7 @@ class Formatter { content: string, fileName: string ): Promise { - const prettifiedContent = await this.formatStrWithPrettier( - content, - fileName - ) + const prettifiedContent = await formatWithPrettier(content, fileName) const relevantConfig = await this.getESLintOverridesConfigForFile(fileName) const eslint = new ESLint({ @@ -203,27 +200,6 @@ class Formatter { return newContent } - /** - * Format a file's content with prettier. - * - * @param content - The content to format. - * @param fileName - The name of the file the content belongs to. - * @returns The formatted content - */ - async formatStrWithPrettier( - content: string, - fileName: string - ): Promise { - // load config of the file - const prettierConfig = (await prettier.resolveConfig(fileName)) || undefined - - if (prettierConfig && !prettierConfig.parser) { - prettierConfig.parser = "babel-ts" - } - - return await prettier.format(content, prettierConfig) - } - /** * Applies all formatting types to a string. * diff --git a/www/utils/packages/typedoc-plugin-markdown-medusa/package.json b/www/utils/packages/typedoc-plugin-markdown-medusa/package.json index 7002d57ecc1bd..153bb4e2287b8 100644 --- a/www/utils/packages/typedoc-plugin-markdown-medusa/package.json +++ b/www/utils/packages/typedoc-plugin-markdown-medusa/package.json @@ -22,6 +22,7 @@ "typedoc": "0.27.x" }, "devDependencies": { + "@types/js-beautify": "^1.14.3", "@types/node": "^20.12.10", "copyfiles": "^2.4.1", "typedoc": "^0.27.5", @@ -36,6 +37,7 @@ ], "dependencies": { "handlebars": "^4.7.8", + "js-beautify": "^1.15.1", "utils": "*" } } diff --git a/www/utils/packages/typedoc-plugin-markdown-medusa/src/render-utils.ts b/www/utils/packages/typedoc-plugin-markdown-medusa/src/render-utils.ts index beed79368708a..922dd3d0fb58a 100644 --- a/www/utils/packages/typedoc-plugin-markdown-medusa/src/render-utils.ts +++ b/www/utils/packages/typedoc-plugin-markdown-medusa/src/render-utils.ts @@ -78,6 +78,8 @@ import ifMemberShowTitleHelper from "./resources/helpers/if-member-show-title.js import signatureCommentHelper from "./resources/helpers/signature-comment.js" import versionHelper from "./resources/helpers/version.js" import sourceCodeLinkHelper from "./resources/helpers/source-code-link.js" +import workflowExamplesHelper from "./resources/helpers/workflow-examples.js" +import stepExamplesHelper from "./resources/helpers/step-examples.js" import { MarkdownTheme } from "./theme.js" import { getDirname } from "utils" @@ -187,4 +189,6 @@ export function registerHelpers(theme: MarkdownTheme) { signatureCommentHelper() versionHelper() sourceCodeLinkHelper() + workflowExamplesHelper() + stepExamplesHelper() } diff --git a/www/utils/packages/typedoc-plugin-markdown-medusa/src/resources/helpers/step-examples.ts b/www/utils/packages/typedoc-plugin-markdown-medusa/src/resources/helpers/step-examples.ts new file mode 100644 index 0000000000000..e6ad34d10e984 --- /dev/null +++ b/www/utils/packages/typedoc-plugin-markdown-medusa/src/resources/helpers/step-examples.ts @@ -0,0 +1,60 @@ +import Handlebars from "handlebars" +import { DeclarationReflection, SignatureReflection } from "typedoc" +import { getReflectionTypeFakeValueStr, getStepInputType } from "utils" +import pkg from "js-beautify" + +const { js_beautify } = pkg + +export default function () { + Handlebars.registerHelper( + "stepExamples", + function (this: SignatureReflection): string { + const stepReflection = this.parent + + const exampleTags = stepReflection.comment?.blockTags.filter( + (tag) => tag.tag === "@example" + ) + + if (exampleTags?.length) { + return Handlebars.helpers.example(stepReflection) + } + + return generateStepExample(stepReflection) + } + ) +} + +function generateStepExample(stepReflection: DeclarationReflection): string { + if (!stepReflection.signatures?.length) { + return "" + } + const inputType = getStepInputType(stepReflection.signatures[0]) + const inputStr = inputType + ? `${getReflectionTypeFakeValueStr({ + reflectionType: inputType, + name: "", + })}` + : "" + + // generate example + return ` +\`\`\`ts title="src/workflows/my-workflow.ts" +${js_beautify( + `import { createWorkflow } from "@medusajs/framework/workflows-sdk" +import { ${stepReflection.name} } from "@medusajs/medusa/core-flows" + +const myWorkflow = createWorkflow( + "my-workflow", + () => { + const data = ${stepReflection.name}(${inputStr}) + } +)`, + { + indent_size: 2, + brace_style: "preserve-inline", + wrap_line_length: 80, + } +)} +\`\`\` + ` +} diff --git a/www/utils/packages/typedoc-plugin-markdown-medusa/src/resources/helpers/workflow-examples.ts b/www/utils/packages/typedoc-plugin-markdown-medusa/src/resources/helpers/workflow-examples.ts new file mode 100644 index 0000000000000..c3d3f473052d4 --- /dev/null +++ b/www/utils/packages/typedoc-plugin-markdown-medusa/src/resources/helpers/workflow-examples.ts @@ -0,0 +1,179 @@ +import Handlebars from "handlebars" +import pkg from "js-beautify" +import { DeclarationReflection, SignatureReflection } from "typedoc" +import { getReflectionTypeFakeValueStr, getWorkflowInputType } from "utils" + +const { js_beautify } = pkg + +export default function () { + Handlebars.registerHelper( + "workflowExamples", + function (this: SignatureReflection): string { + const workflowReflection = this.parent + const exampleStr: string[] = [] + + const exampleTags = workflowReflection.comment?.blockTags.filter( + (tag) => tag.tag === "@example" + ) + + if (!exampleTags?.length) { + exampleStr.push( + getExecutionCodeTabs({ + exampleCode: generateWorkflowExample(workflowReflection), + workflowName: workflowReflection.name, + }) + ) + } else { + exampleTags.forEach((exampleTag) => { + exampleTag.content.forEach((part) => { + if (part.kind !== "code") { + exampleStr.push(part.text) + return + } + + exampleStr.push( + getExecutionCodeTabs({ + exampleCode: part.text, + workflowName: workflowReflection.name, + }) + ) + }) + }) + } + + return exampleStr.join("\n") + } + ) +} + +function getExecutionCodeTabs({ + exampleCode, + workflowName, +}: { + exampleCode: string + workflowName: string +}): string { + exampleCode = exampleCode.replace("```ts\n", "").replace("\n```", "") + const beautifyOptions: pkg.JSBeautifyOptions = { + indent_size: 2, + brace_style: "preserve-inline", + wrap_line_length: 80, + } + + return ` + + +\`\`\`ts title="src/workflows/my-workflow.ts" +${js_beautify( + `import { createWorkflow } from "@medusajs/framework/workflows-sdk" +import { ${workflowName} } from "@medusajs/medusa/core-flows" + +const myWorkflow = createWorkflow( + "my-workflow", + () => { + ${exampleCode + .replace(`{ result }`, "result") + .replace(`await `, "") + .replace(`(container)\n\t.run(`, ".runAsStep(")} + } +)`, + beautifyOptions +)} +\`\`\` + + + + +\`\`\`ts title="src/api/workflow/route.ts" +${js_beautify( + `import type { + MedusaRequest, + MedusaResponse, +} from "@medusajs/framework/http" +import { ${workflowName} } from "@medusajs/medusa/core-flows" + +export async function POST( + req: MedusaRequest, + res: MedusaResponse +) { + ${exampleCode.replace("container", "req.scope")} + + res.send(result) +} +`, + beautifyOptions +)} +\`\`\` + + + + +\`\`\`ts title="src/subscribers/order-placed.ts" +${js_beautify( + `import { + type SubscriberConfig, + type SubscriberArgs, +} from "@medusajs/framework" +import { ${workflowName} } from "@medusajs/medusa/core-flows" + +export default async function handleOrderPlaced({ + event: { data }, + container, +}: SubscriberArgs<{ id: string }>) { + ${exampleCode} + + console.log(result) +} + +export const config: SubscriberConfig = { + event: "order.placed", +}`, + beautifyOptions +)} +\`\`\` + + + + +\`\`\`ts title="src/jobs/message-daily.ts" +${js_beautify( + `import { MedusaContainer } from "@medusajs/framework/types" +import { ${workflowName} } from "@medusajs/medusa/core-flows" + +export default async function myCustomJob( + container: MedusaContainer +) { + ${exampleCode} + + console.log(result) +} + +export const config = { + name: "run-once-a-day", + schedule: "0 0 * * *", +}`, + beautifyOptions +)} +\`\`\` + + + ` +} + +function generateWorkflowExample( + workflowReflection: DeclarationReflection +): string { + if (!workflowReflection.signatures?.length) { + return "" + } + const inputType = getWorkflowInputType(workflowReflection.signatures[0]) + const inputStr = inputType + ? `{\n\t\tinput: ${getReflectionTypeFakeValueStr({ + reflectionType: inputType, + name: "", + })}\n\t}` + : "" + + // generate example + return `const { result } = await ${workflowReflection.name}(container)\n\t.run(${inputStr})` +} diff --git a/www/utils/packages/typedoc-plugin-markdown-medusa/src/resources/partials/member.step.hbs b/www/utils/packages/typedoc-plugin-markdown-medusa/src/resources/partials/member.step.hbs index 0be55889abcac..418a0e5f0bd2f 100644 --- a/www/utils/packages/typedoc-plugin-markdown-medusa/src/resources/partials/member.step.hbs +++ b/www/utils/packages/typedoc-plugin-markdown-medusa/src/resources/partials/member.step.hbs @@ -10,7 +10,7 @@ {{#if (sectionEnabled "member_signature_example")}} -{{{example this}}} +{{{stepExamples this}}} {{/if}} diff --git a/www/utils/packages/typedoc-plugin-markdown-medusa/src/resources/partials/member.workflow.hbs b/www/utils/packages/typedoc-plugin-markdown-medusa/src/resources/partials/member.workflow.hbs index ea201154c4345..a5b58ff0fd35d 100644 --- a/www/utils/packages/typedoc-plugin-markdown-medusa/src/resources/partials/member.workflow.hbs +++ b/www/utils/packages/typedoc-plugin-markdown-medusa/src/resources/partials/member.workflow.hbs @@ -12,7 +12,7 @@ {{#if (sectionEnabled "member_signature_example")}} -{{{example this}}} +{{{workflowExamples this}}} {{/if}} diff --git a/www/utils/packages/typedoc-plugin-workflows/src/plugin.ts b/www/utils/packages/typedoc-plugin-workflows/src/plugin.ts index d61a53d05d1a7..d421bc306d29b 100644 --- a/www/utils/packages/typedoc-plugin-workflows/src/plugin.ts +++ b/www/utils/packages/typedoc-plugin-workflows/src/plugin.ts @@ -24,6 +24,7 @@ import { getUniqueStrArray, } from "utils" import { StepType } from "./types.js" +import Examples from "./utils/examples.js" type ParsedStep = { stepReflection: DeclarationReflection @@ -38,6 +39,7 @@ type ParsedStep = { class WorkflowsPlugin { protected app: Application protected helper: Helper + protected examplesHelper: Examples protected workflowsTagsMap: Map protected addTagsAfterParsing: { [k: string]: { @@ -49,6 +51,7 @@ class WorkflowsPlugin { constructor(app: Application) { this.app = app this.helper = new Helper() + this.examplesHelper = new Examples() this.workflowsTagsMap = new Map() this.addTagsAfterParsing = {} @@ -526,7 +529,7 @@ class WorkflowsPlugin { new CommentTag(`@example`, [ { kind: "code", - text: this.helper.generateHookExample({ + text: this.examplesHelper.generateHookExample({ hookName: stepId, workflowName, parameter, diff --git a/www/utils/packages/typedoc-plugin-workflows/src/utils/examples.ts b/www/utils/packages/typedoc-plugin-workflows/src/utils/examples.ts new file mode 100644 index 0000000000000..c6ec6091a10b3 --- /dev/null +++ b/www/utils/packages/typedoc-plugin-workflows/src/utils/examples.ts @@ -0,0 +1,35 @@ +import { DeclarationReflection, ParameterReflection } from "typedoc" + +export default class Examples { + generateHookExample({ + hookName, + workflowName, + parameter, + }: { + hookName: string + workflowName: string + parameter: ParameterReflection + }): string { + let str = `import { ${workflowName} } from "@medusajs/medusa/core-flows"\n\n` + + str += `${workflowName}.hooks.${hookName}(\n\t(async ({` + + 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 + } +} diff --git a/www/utils/packages/typedoc-plugin-workflows/src/utils/helper.ts b/www/utils/packages/typedoc-plugin-workflows/src/utils/helper.ts index caacaa7c6c5c6..3958156df8db4 100644 --- a/www/utils/packages/typedoc-plugin-workflows/src/utils/helper.ts +++ b/www/utils/packages/typedoc-plugin-workflows/src/utils/helper.ts @@ -1,8 +1,4 @@ -import { - DeclarationReflection, - ParameterReflection, - ProjectReflection, -} from "typedoc" +import { DeclarationReflection, ProjectReflection } from "typedoc" import ts from "typescript" import { StepModifier, StepType } from "../types.js" import { capitalize, findReflectionInNamespaces } from "utils" @@ -271,38 +267,6 @@ export default class Helper { return `@${stepType}` } - generateHookExample({ - hookName, - workflowName, - parameter, - }: { - hookName: string - workflowName: string - parameter: ParameterReflection - }): string { - let str = `import { ${workflowName} } from "@medusajs/medusa/core-flows"\n\n` - - str += `${workflowName}.hooks.${hookName}(\n\t(async ({` - - 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 - } - getCallExpressionFromBody( body: ts.ConciseBody ): ts.CallExpression | undefined { diff --git a/www/utils/packages/utils/package.json b/www/utils/packages/utils/package.json index 375e14900b2e5..029258f810292 100644 --- a/www/utils/packages/utils/package.json +++ b/www/utils/packages/utils/package.json @@ -23,7 +23,9 @@ "typescript": "^5.6.2" }, "dependencies": { + "@faker-js/faker": "^9.3.0", "octokit": "^3.1.2", + "prettier": "^3.4.2", "rimraf": "^5.0.5" } } diff --git a/www/utils/packages/utils/src/examples-utils.ts b/www/utils/packages/utils/src/examples-utils.ts new file mode 100644 index 0000000000000..71cfc18c2121b --- /dev/null +++ b/www/utils/packages/utils/src/examples-utils.ts @@ -0,0 +1,319 @@ +import { faker } from "@faker-js/faker" +import { + DeclarationReflection, + LiteralType, + ReflectionFlags, + SomeType, +} from "typedoc" +import * as prettier from "prettier" +import { getTypeChildren } from "./get-type-children.js" + +const MAX_LEVEL = 7 + +export function isReflectionTypeOptional( + reflection: DeclarationReflection +): boolean { + return "flags" in reflection + ? (reflection.flags as ReflectionFlags).isOptional + : false +} + +function getReflectionTypeFakeValue({ + reflectionType, + name, + level = 1, +}: { + reflectionType: SomeType + name: string + level?: number +}): unknown { + if (reflectionType.type === "literal") { + return getFakeStrValue({ + name, + type: typeof reflectionType.value, + }) + } + + if (reflectionType.type === "intrinsic") { + return getFakeStrValue({ + name, + type: reflectionType.name, + }) + } + + if (level > MAX_LEVEL) { + return reflectionType.type === "array" ? [] : {} + } + + if (reflectionType.type === "array") { + return new Array( + getReflectionTypeFakeValue({ + reflectionType: reflectionType.elementType, + name, + level: level + 1, + }) + ) + } + + if (reflectionType.type === "reflection") { + if (reflectionType.declaration.type) { + return getReflectionTypeFakeValue({ + reflectionType: reflectionType.declaration.type, + name, + level: level + 1, + }) + } + + if (reflectionType.declaration.children) { + const obj: Record = {} + + reflectionType.declaration.children.forEach((child) => { + if (!child.type || isReflectionTypeOptional(child)) { + return + } + + obj[child.name] = getReflectionTypeFakeValue({ + reflectionType: child.type, + name: child.name, + level: level + 1, + }) + }) + + return obj + } + + return {} + } + + if ( + reflectionType.type === "reference" && + reflectionType.reflection instanceof DeclarationReflection + ) { + if (reflectionType.reflection.name === "BigNumberInput") { + return getFakeStrValue({ + name, + type: "number", + }) + } else if (reflectionType.reflection.name === "Record") { + return getFakeStrValue({ + name, + type: "object", + }) + } + const obj: Record = {} + const children = getTypeChildren({ + reflectionType: reflectionType, + project: reflectionType.reflection.project, + }) + if (!children.length) { + // check whether type is enum + if (reflectionType.reflection.type?.type === "union") { + const isEnum = reflectionType.reflection.type.types.every((type) => { + return type.type === "literal" && typeof type.value === "string" + }) + + if (isEnum) { + return getFakeStrValue({ + name, + type: "enum", + data: { + enum: ( + reflectionType.reflection.type.types as LiteralType[] + ).reduce( + (acc, type) => { + acc[type.value as string] = type.value as string + return acc + }, + {} as Record + ), + }, + }) + } + } + } + + children.forEach((child) => { + if (!child.type || isReflectionTypeOptional(child)) { + return + } + + obj[child.name] = getReflectionTypeFakeValue({ + reflectionType: child.type, + name: child.name, + level: level + 1, + }) + }) + + return obj + } + + if (reflectionType.type === "intersection") { + const obj: Record = {} + + reflectionType.types?.forEach((type) => { + const value = getReflectionTypeFakeValue({ + reflectionType: type, + name, + level: level + 1, + }) + + if (typeof value === "object") { + Object.assign(obj, value) + } else { + obj[name] = value + } + }) + + return obj + } + + if (reflectionType.type === "union" && reflectionType.types.length) { + // check if it's type Record<> + const isRecord = + reflectionType.types[0].type === "reference" && + reflectionType.types[0].name === "Record" + + if (isRecord) { + return getFakeStrValue({ + name, + type: "object", + }) + } + + return getReflectionTypeFakeValue({ + reflectionType: reflectionType.types[0], + name, + level: level + 1, + }) + } + + // TODO: handle more types + return "{value}" +} + +export function getReflectionTypeFakeValueStr({ + reflectionType, + name, +}: { + reflectionType: SomeType + name: string +}): string { + const value = getReflectionTypeFakeValue({ + reflectionType, + name, + }) + + return JSON.stringify(value, null, 2) +} + +export function getFakeStrValue({ + name, + type, + format, + data, +}: { + /** + * The name of the property. It can help when generating the fake value. + * For example, if the name is `id`, the fake value generated will be of the format `id_`. + */ + name: string + /** + * The type of the property, such as `string` or `boolean`. + */ + type: string + /** + * The format of the property, useful for OAS. For example, `date-time`. + */ + format?: string + /** + * Additional data to help generate the fake value. + */ + data?: { + enum: Record + } +}): unknown { + let value: unknown + if (!format && name.endsWith("_at")) { + format = "date-time" + } + + switch (true) { + case type === "string" && format === "date-time": + value = faker.date.future().toISOString() + break + case type === "boolean": + value = faker.datatype.boolean() + break + case type === "integer" || type === "number": + value = faker.number.int({ min: 0, max: 50 }) + break + case type === "array": + value = [] + break + case type === "enum" && data?.enum !== undefined: + value = faker.helpers.enumValue(data.enum) + break + case type === "object": + value = {} + break + case type === "string": + value = faker.helpers.mustache(`{{${name}}}`, { + id: () => + `id_${faker.string.alphanumeric({ + length: { min: 10, max: 20 }, + })}`, + ids: () => + `id_${faker.string.alphanumeric({ + length: { min: 10, max: 20 }, + })}`, + name: () => faker.person.firstName(), + email: () => faker.internet.email(), + password: () => faker.internet.password({ length: 8 }), + currency: () => faker.finance.currencyCode(), + title: () => faker.lorem.word(), + description: () => faker.lorem.sentence(), + url: () => faker.internet.url(), + phone: () => faker.phone.number(), + created_by: () => + `user_${faker.string.alphanumeric({ + length: { min: 10, max: 20 }, + })}`, + updated_by: () => + `user_${faker.string.alphanumeric({ + length: { min: 10, max: 20 }, + })}`, + rejected_by: () => + `user_${faker.string.alphanumeric({ + length: { min: 10, max: 20 }, + })}`, + canceled_by: () => + `user_${faker.string.alphanumeric({ + length: { min: 10, max: 20 }, + })}`, + price: () => faker.commerce.price(), + quantity: () => `${faker.number.int({ min: 1, max: 10 })}`, + }) + try { + value = faker.helpers.fake(value as string) + } catch (e) { + // Ignore error + } + value = (value as string).replace(`{{${name}}}`, "{value}") + } + + return value !== undefined ? value : "{value}" +} + +export async function formatWithPrettier( + content: string, + fileName: string +): Promise { + // load config of the file + const prettierConfig = (await prettier.resolveConfig(fileName)) || undefined + + if (prettierConfig && !prettierConfig.parser) { + prettierConfig.parser = "babel-ts" + } + + return await prettier.format(content, prettierConfig) +} diff --git a/www/utils/packages/utils/src/index.ts b/www/utils/packages/utils/src/index.ts index dd08865670082..9d61aea4dde0f 100644 --- a/www/utils/packages/utils/src/index.ts +++ b/www/utils/packages/utils/src/index.ts @@ -1,4 +1,5 @@ export * from "./dml-utils.js" +export * from "./examples-utils.js" export * from "./general-utils.js" export * from "./get-type-children.js" export * from "./get-project-child.js" diff --git a/www/utils/yarn.lock b/www/utils/yarn.lock index 9c6df4b85939e..6c477c0049529 100644 --- a/www/utils/yarn.lock +++ b/www/utils/yarn.lock @@ -522,10 +522,10 @@ __metadata: languageName: node linkType: hard -"@faker-js/faker@npm:^8.4.0": - version: 8.4.0 - resolution: "@faker-js/faker@npm:8.4.0" - checksum: 2dd9a3f6a38a70baa8d8ae222d9d371eea2b56eba4bd12bb138230e9481687686330fddee7581d3f285cf8ea8fe1534e08e43a110de8743e561a6eccc3fdc670 +"@faker-js/faker@npm:^9.3.0": + version: 9.3.0 + resolution: "@faker-js/faker@npm:9.3.0" + checksum: 6528e2f0bf0abc315780024534074a449e01e7f581f1a50a20ee7103d29d842e1c4d7dd6b27aa173f668308bf55a1d64c2547286c0c4e5a8e08d0b8269aaedc7 languageName: node linkType: hard @@ -1388,6 +1388,13 @@ __metadata: languageName: node linkType: hard +"@one-ini/wasm@npm:0.1.1": + version: 0.1.1 + resolution: "@one-ini/wasm@npm:0.1.1" + checksum: 54700e055037f1a63bfcc86d24822203b25759598c2c3e295d1435130a449108aebc119c9c2e467744767dbe0b6ab47a182c61aa1071ba7368f5e20ab197ba65 + languageName: node + linkType: hard + "@pkgjs/parseargs@npm:^0.11.0": version: 0.11.0 resolution: "@pkgjs/parseargs@npm:0.11.0" @@ -1621,6 +1628,13 @@ __metadata: languageName: node linkType: hard +"@types/js-beautify@npm:^1.14.3": + version: 1.14.3 + resolution: "@types/js-beautify@npm:1.14.3" + checksum: e2e8cbe03c39bc596130151ae050884e4da25fc8dfb15cd1a0d0de92ee0963d43fc37b372de46fc8ba8ec99e6041debc70cdf92a1c160e3b472df3effa5b6c8f + languageName: node + linkType: hard + "@types/json-schema@npm:*, @types/json-schema@npm:^7.0.12, @types/json-schema@npm:^7.0.15": version: 7.0.15 resolution: "@types/json-schema@npm:7.0.15" @@ -1847,6 +1861,13 @@ __metadata: languageName: node linkType: hard +"abbrev@npm:^2.0.0": + version: 2.0.0 + resolution: "abbrev@npm:2.0.0" + checksum: f742a5a107473946f426c691c08daba61a1d15942616f300b5d32fd735be88fef5cba24201757b6c407fd564555fb48c751cfa33519b2605c8a7aadd22baf372 + languageName: node + linkType: hard + "abort-controller@npm:^3.0.0": version: 3.0.0 resolution: "abort-controller@npm:3.0.0" @@ -2386,6 +2407,16 @@ __metadata: languageName: node linkType: hard +"config-chain@npm:^1.1.13": + version: 1.1.13 + resolution: "config-chain@npm:1.1.13" + dependencies: + ini: ^1.3.4 + proto-list: ~1.2.1 + checksum: 39d1df18739d7088736cc75695e98d7087aea43646351b028dfabd5508d79cf6ef4c5bcd90471f52cd87ae470d1c5490c0a8c1a292fbe6ee9ff688061ea0963e + languageName: node + linkType: hard + "convert-source-map@npm:^2.0.0": version: 2.0.0 resolution: "convert-source-map@npm:2.0.0" @@ -2607,7 +2638,6 @@ __metadata: version: 0.0.0-use.local resolution: "docs-generator@workspace:packages/docs-generator" dependencies: - "@faker-js/faker": ^8.4.0 "@octokit/core": ^5.0.2 "@types/eslint": 8.56.6 "@types/node": ^20.12.10 @@ -2620,7 +2650,6 @@ __metadata: openai: ^4.29.1 openapi-types: ^12.1.3 pluralize: ^8.0.0 - prettier: ^3.2.4 ts-node: ^10.9.1 types: "*" typescript: ^5.6.2 @@ -2691,6 +2720,20 @@ __metadata: languageName: node linkType: hard +"editorconfig@npm:^1.0.4": + version: 1.0.4 + resolution: "editorconfig@npm:1.0.4" + dependencies: + "@one-ini/wasm": 0.1.1 + commander: ^10.0.0 + minimatch: 9.0.1 + semver: ^7.5.3 + bin: + editorconfig: bin/editorconfig + checksum: ed6985959d7b34a56e1c09bef118758c81c969489b768d152c93689fce8403b0452462e934f665febaba3478eebc0fd41c0a36100783eaadf6d926c4abc87a3d + languageName: node + linkType: hard + "electron-to-chromium@npm:^1.4.535": version: 1.4.593 resolution: "electron-to-chromium@npm:1.4.593" @@ -3457,6 +3500,22 @@ __metadata: languageName: node linkType: hard +"glob@npm:^10.3.3": + version: 10.4.5 + resolution: "glob@npm:10.4.5" + dependencies: + foreground-child: ^3.1.0 + jackspeak: ^3.1.2 + minimatch: ^9.0.4 + minipass: ^7.1.2 + package-json-from-dist: ^1.0.0 + path-scurry: ^1.11.1 + bin: + glob: dist/esm/bin.mjs + checksum: 19a9759ea77b8e3ca0a43c2f07ecddc2ad46216b786bb8f993c445aee80d345925a21e5280c7b7c6c59e860a0154b84e4b2b60321fea92cd3c56b4a7489f160e + languageName: node + linkType: hard + "glob@npm:^7.0.5, glob@npm:^7.1.3": version: 7.2.3 resolution: "glob@npm:7.2.3" @@ -3685,6 +3744,13 @@ __metadata: languageName: node linkType: hard +"ini@npm:^1.3.4": + version: 1.3.8 + resolution: "ini@npm:1.3.8" + checksum: ec93838d2328b619532e4f1ff05df7909760b6f66d9c9e2ded11e5c1897d6f2f9980c54dd638f88654b00919ce31e827040631eab0a3969e4d1abefa0719516a + languageName: node + linkType: hard + "interpret@npm:^2.2.0": version: 2.2.0 resolution: "interpret@npm:2.2.0" @@ -3855,6 +3921,43 @@ __metadata: languageName: node linkType: hard +"jackspeak@npm:^3.1.2": + version: 3.4.3 + resolution: "jackspeak@npm:3.4.3" + dependencies: + "@isaacs/cliui": ^8.0.2 + "@pkgjs/parseargs": ^0.11.0 + dependenciesMeta: + "@pkgjs/parseargs": + optional: true + checksum: 6acc10d139eaefdbe04d2f679e6191b3abf073f111edf10b1de5302c97ec93fffeb2fdd8681ed17f16268aa9dd4f8c588ed9d1d3bffbbfa6e8bf897cbb3149b9 + languageName: node + linkType: hard + +"js-beautify@npm:^1.15.1": + version: 1.15.1 + resolution: "js-beautify@npm:1.15.1" + dependencies: + config-chain: ^1.1.13 + editorconfig: ^1.0.4 + glob: ^10.3.3 + js-cookie: ^3.0.5 + nopt: ^7.2.0 + bin: + css-beautify: js/bin/css-beautify.js + html-beautify: js/bin/html-beautify.js + js-beautify: js/bin/js-beautify.js + checksum: 4140dd95537143eb429b6c8e47e21310f16c032d97a03163c6c7c0502bc663242a5db08d3ad941b87f24a142ce4f9190c556d2340bcd056545326377dfae5362 + languageName: node + linkType: hard + +"js-cookie@npm:^3.0.5": + version: 3.0.5 + resolution: "js-cookie@npm:3.0.5" + checksum: 04a0e560407b4489daac3a63e231d35f4e86f78bff9d792011391b49c59f721b513411cd75714c418049c8dc9750b20fcddad1ca5a2ca616c3aca4874cce5b3a + languageName: node + linkType: hard + "js-sdsl@npm:^4.1.4": version: 4.4.2 resolution: "js-sdsl@npm:4.4.2" @@ -4179,6 +4282,13 @@ __metadata: languageName: node linkType: hard +"lru-cache@npm:^10.2.0": + version: 10.4.3 + resolution: "lru-cache@npm:10.4.3" + checksum: ebd04fbca961e6c1d6c0af3799adcc966a1babe798f685bb84e6599266599cd95d94630b10262f5424539bc4640107e8a33aa28585374abf561d30d16f4b39fb + languageName: node + linkType: hard + "lru-cache@npm:^5.1.1": version: 5.1.1 resolution: "lru-cache@npm:5.1.1" @@ -4320,6 +4430,15 @@ __metadata: languageName: node linkType: hard +"minimatch@npm:9.0.1": + version: 9.0.1 + resolution: "minimatch@npm:9.0.1" + dependencies: + brace-expansion: ^2.0.1 + checksum: aa043eb8822210b39888a5d0d28df0017b365af5add9bd522f180d2a6962de1cbbf1bdeacdb1b17f410dc3336bc8d76fb1d3e814cdc65d00c2f68e01f0010096 + languageName: node + linkType: hard + "minimatch@npm:^10.0.1": version: 10.0.1 resolution: "minimatch@npm:10.0.1" @@ -4356,7 +4475,7 @@ __metadata: languageName: node linkType: hard -"minimatch@npm:^9.0.5": +"minimatch@npm:^9.0.4, minimatch@npm:^9.0.5": version: 9.0.5 resolution: "minimatch@npm:9.0.5" dependencies: @@ -4379,6 +4498,13 @@ __metadata: languageName: node linkType: hard +"minipass@npm:^7.1.2": + version: 7.1.2 + resolution: "minipass@npm:7.1.2" + checksum: b0fd20bb9fb56e5fa9a8bfac539e8915ae07430a619e4b86ff71f5fc757ef3924b23b2c4230393af1eda647ed3d75739e4e0acb250a6b1eb277cf7f8fe449557 + languageName: node + linkType: hard + "mitt@npm:3.0.0": version: 3.0.0 resolution: "mitt@npm:3.0.0" @@ -4492,6 +4618,17 @@ __metadata: languageName: node linkType: hard +"nopt@npm:^7.2.0": + version: 7.2.1 + resolution: "nopt@npm:7.2.1" + dependencies: + abbrev: ^2.0.0 + bin: + nopt: bin/nopt.js + checksum: a069c7c736767121242037a22a788863accfa932ab285a1eb569eb8cd534b09d17206f68c37f096ae785647435e0c5a5a0a67b42ec743e481a455e5ae6a6df81 + languageName: node + linkType: hard + "npm-run-path@npm:^4.0.1": version: 4.0.1 resolution: "npm-run-path@npm:4.0.1" @@ -4639,6 +4776,13 @@ __metadata: languageName: node linkType: hard +"package-json-from-dist@npm:^1.0.0": + version: 1.0.1 + resolution: "package-json-from-dist@npm:1.0.1" + checksum: 62ba2785eb655fec084a257af34dbe24292ab74516d6aecef97ef72d4897310bc6898f6c85b5cd22770eaa1ce60d55a0230e150fb6a966e3ecd6c511e23d164b + languageName: node + linkType: hard + "packet-reader@npm:1.0.0": version: 1.0.0 resolution: "packet-reader@npm:1.0.0" @@ -4722,6 +4866,16 @@ __metadata: languageName: node linkType: hard +"path-scurry@npm:^1.11.1": + version: 1.11.1 + resolution: "path-scurry@npm:1.11.1" + dependencies: + lru-cache: ^10.2.0 + minipass: ^5.0.0 || ^6.0.2 || ^7.0.0 + checksum: 32a13711a2a505616ae1cc1b5076801e453e7aae6ac40ab55b388bb91b9d0547a52f5aaceff710ea400205f18691120d4431e520afbe4266b836fadede15872d + languageName: node + linkType: hard + "path-type@npm:^4.0.0": version: 4.0.0 resolution: "path-type@npm:4.0.0" @@ -4940,12 +5094,12 @@ __metadata: languageName: node linkType: hard -"prettier@npm:^3.2.4": - version: 3.2.4 - resolution: "prettier@npm:3.2.4" +"prettier@npm:^3.4.2": + version: 3.4.2 + resolution: "prettier@npm:3.4.2" bin: prettier: bin/prettier.cjs - checksum: 88dfeb78ac6096522c9a5b81f1413d875f568420d9bb6a5e5103527912519b993f2bcdcac311fcff5718d5869671d44e4f85827d3626f3a6ce32b9abc65d88e0 + checksum: 99e076a26ed0aba4ebc043880d0f08bbb8c59a4c6641cdee6cdadf2205bdd87aa1d7823f50c3aea41e015e99878d37c58d7b5f0e663bba0ef047f94e36b96446 languageName: node linkType: hard @@ -4963,6 +5117,13 @@ __metadata: languageName: node linkType: hard +"proto-list@npm:~1.2.1": + version: 1.2.4 + resolution: "proto-list@npm:1.2.4" + checksum: b9179f99394ec8a68b8afc817690185f3b03933f7b46ce2e22c1930dc84b60d09f5ad222beab4e59e58c6c039c7f7fcf620397235ef441a356f31f9744010e12 + languageName: node + linkType: hard + "proxy-from-env@npm:1.1.0": version: 1.1.0 resolution: "proxy-from-env@npm:1.1.0" @@ -5295,6 +5456,15 @@ __metadata: languageName: node linkType: hard +"semver@npm:^7.5.3": + version: 7.6.3 + resolution: "semver@npm:7.6.3" + bin: + semver: bin/semver.js + checksum: 88f33e148b210c153873cb08cfe1e281d518aaa9a666d4d148add6560db5cd3c582f3a08ccb91f38d5f379ead256da9931234ed122057f40bb5766e65e58adaf + languageName: node + linkType: hard + "semver@npm:^7.5.4": version: 7.5.4 resolution: "semver@npm:7.5.4" @@ -5817,9 +5987,11 @@ __metadata: version: 0.0.0-use.local resolution: "typedoc-plugin-markdown-medusa@workspace:packages/typedoc-plugin-markdown-medusa" dependencies: + "@types/js-beautify": ^1.14.3 "@types/node": ^20.12.10 copyfiles: ^2.4.1 handlebars: ^4.7.8 + js-beautify: ^1.15.1 typedoc: ^0.27.5 types: "*" typescript: ^5.6.2 @@ -6050,8 +6222,10 @@ __metadata: version: 0.0.0-use.local resolution: "utils@workspace:packages/utils" dependencies: + "@faker-js/faker": ^9.3.0 "@types/node": ^20.12.10 octokit: ^3.1.2 + prettier: ^3.4.2 rimraf: ^5.0.5 typescript: ^5.6.2 peerDependencies: