zudoku 0.18.2 → 0.18.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli/common/logger.js +9 -0
- package/dist/cli/common/logger.js.map +1 -1
- package/dist/config/validators/validate.d.ts +23 -3
- package/dist/config/validators/validate.js +7 -1
- package/dist/config/validators/validate.js.map +1 -1
- package/dist/lib/components/Header.js +2 -2
- package/dist/lib/components/Header.js.map +1 -1
- package/dist/lib/oas/parser/upgrade/index.d.ts +2 -2
- package/dist/lib/oas/parser/upgrade/index.js +3 -20
- package/dist/lib/oas/parser/upgrade/index.js.map +1 -1
- package/dist/lib/plugins/markdown/MdxPage.d.ts +9 -1
- package/dist/lib/plugins/markdown/MdxPage.js +14 -1
- package/dist/lib/plugins/markdown/MdxPage.js.map +1 -1
- package/dist/lib/plugins/markdown/index.js +1 -1
- package/dist/lib/plugins/markdown/index.js.map +1 -1
- package/dist/lib/plugins/openapi/client/GraphQLClient.js +12 -0
- package/dist/lib/plugins/openapi/client/GraphQLClient.js.map +1 -1
- package/dist/lib/plugins/openapi/client/useCreateQuery.d.ts +1 -1
- package/dist/lib/plugins/openapi/client/useCreateQuery.js +4 -2
- package/dist/lib/plugins/openapi/client/useCreateQuery.js.map +1 -1
- package/dist/lib/plugins/openapi/interfaces.d.ts +1 -1
- package/dist/lib/plugins/openapi/post-processors/removeExtensions.d.ts +6 -0
- package/dist/lib/plugins/openapi/post-processors/removeExtensions.js +14 -0
- package/dist/lib/plugins/openapi/post-processors/removeExtensions.js.map +1 -0
- package/dist/lib/plugins/openapi/post-processors/removeExtensions.test.d.ts +1 -0
- package/dist/lib/plugins/openapi/post-processors/removeExtensions.test.js +125 -0
- package/dist/lib/plugins/openapi/post-processors/removeExtensions.test.js.map +1 -0
- package/dist/lib/plugins/openapi/post-processors/removePaths.d.ts +11 -0
- package/dist/lib/plugins/openapi/post-processors/removePaths.js +33 -0
- package/dist/lib/plugins/openapi/post-processors/removePaths.js.map +1 -0
- package/dist/lib/plugins/openapi/post-processors/removePaths.test.d.ts +1 -0
- package/dist/lib/plugins/openapi/post-processors/removePaths.test.js +104 -0
- package/dist/lib/plugins/openapi/post-processors/removePaths.test.js.map +1 -0
- package/dist/lib/plugins/openapi/post-processors/traverse.d.ts +1 -0
- package/dist/lib/plugins/openapi/post-processors/traverse.js +2 -0
- package/dist/lib/plugins/openapi/post-processors/traverse.js.map +1 -0
- package/dist/lib/util/traverse.d.ts +2 -0
- package/dist/lib/util/traverse.js +18 -0
- package/dist/lib/util/traverse.js.map +1 -0
- package/dist/vite/config.js +12 -0
- package/dist/vite/config.js.map +1 -1
- package/dist/vite/config.test.js +3 -4
- package/dist/vite/config.test.js.map +1 -1
- package/dist/vite/output.js +20 -0
- package/dist/vite/output.js.map +1 -1
- package/dist/vite/plugin-api.js +23 -19
- package/dist/vite/plugin-api.js.map +1 -1
- package/dist/vite/plugin-component.js +14 -19
- package/dist/vite/plugin-component.js.map +1 -1
- package/dist/vite/plugin-docs.test.js +15 -23
- package/dist/vite/plugin-docs.test.js.map +1 -1
- package/dist/zuplo/with-zuplo.d.ts +3 -0
- package/dist/zuplo/with-zuplo.js +28 -0
- package/dist/zuplo/with-zuplo.js.map +1 -0
- package/lib/MdxPage-B2FpJ9KC.js +183 -0
- package/lib/MdxPage-B2FpJ9KC.js.map +1 -0
- package/lib/{OperationList-BwBl1xrD.js → OperationList-BkNQEsNs.js} +459 -457
- package/lib/OperationList-BkNQEsNs.js.map +1 -0
- package/lib/assets/{worker-CPsGZsve.js → worker-BHClFO3A.js} +434 -438
- package/lib/assets/worker-BHClFO3A.js.map +1 -0
- package/lib/{createServer-DK-g7kbB.js → createServer-CpJlUPtn.js} +4457 -5247
- package/lib/createServer-CpJlUPtn.js.map +1 -0
- package/lib/{index-DNxQ_rCt.js → index-C7SaIME0.js} +49 -45
- package/lib/index-C7SaIME0.js.map +1 -0
- package/lib/object_hash-CvlLgU-M.js +785 -0
- package/lib/object_hash-CvlLgU-M.js.map +1 -0
- package/lib/post-processors/removeExtensions.js +11 -0
- package/lib/post-processors/removeExtensions.js.map +1 -0
- package/lib/post-processors/removePaths.js +28 -0
- package/lib/post-processors/removePaths.js.map +1 -0
- package/lib/post-processors/traverse.js +12 -0
- package/lib/post-processors/traverse.js.map +1 -0
- package/lib/zudoku.components.js +1 -2
- package/lib/zudoku.components.js.map +1 -1
- package/lib/zudoku.openapi-worker.js +1 -1
- package/lib/zudoku.plugin-markdown.js +15 -14
- package/lib/zudoku.plugin-markdown.js.map +1 -1
- package/lib/zudoku.plugin-openapi.js +1 -1
- package/package.json +15 -5
- package/src/lib/components/Header.tsx +1 -2
- package/src/lib/oas/parser/upgrade/index.ts +4 -27
- package/src/lib/plugins/markdown/MdxPage.tsx +25 -1
- package/src/lib/plugins/markdown/index.tsx +1 -0
- package/src/lib/plugins/openapi/client/GraphQLClient.tsx +17 -0
- package/src/lib/plugins/openapi/client/useCreateQuery.ts +5 -2
- package/src/lib/plugins/openapi/interfaces.ts +1 -1
- package/src/lib/plugins/openapi/post-processors/removeExtensions.test.ts +144 -0
- package/src/lib/plugins/openapi/post-processors/removeExtensions.ts +24 -0
- package/src/lib/plugins/openapi/post-processors/removePaths.test.ts +126 -0
- package/src/lib/plugins/openapi/post-processors/removePaths.ts +55 -0
- package/src/lib/plugins/openapi/post-processors/traverse.ts +1 -0
- package/src/lib/util/traverse.ts +25 -0
- package/lib/MdxPage-Bwn-VSsH.js +0 -174
- package/lib/MdxPage-Bwn-VSsH.js.map +0 -1
- package/lib/OperationList-BwBl1xrD.js.map +0 -1
- package/lib/assets/worker-CPsGZsve.js.map +0 -1
- package/lib/createServer-DK-g7kbB.js.map +0 -1
- package/lib/index-DNxQ_rCt.js.map +0 -1
|
@@ -22,18 +22,18 @@ class c {
|
|
|
22
22
|
*/
|
|
23
23
|
resolveFilePath(t) {
|
|
24
24
|
const o = this.getDocsConfigs();
|
|
25
|
-
let
|
|
25
|
+
let e;
|
|
26
26
|
return o.forEach(({ files: i }) => {
|
|
27
|
-
if (
|
|
27
|
+
if (e)
|
|
28
28
|
return;
|
|
29
|
-
const
|
|
29
|
+
const s = c.getRootDir(i);
|
|
30
30
|
for (const a of D) {
|
|
31
|
-
if (
|
|
31
|
+
if (e)
|
|
32
32
|
return;
|
|
33
|
-
const r = u.join(
|
|
34
|
-
u.existsSync(r) && (
|
|
33
|
+
const r = u.join(s, `${t}${a}`);
|
|
34
|
+
u.existsSync(r) && (e = r);
|
|
35
35
|
}
|
|
36
|
-
}),
|
|
36
|
+
}), e;
|
|
37
37
|
}
|
|
38
38
|
/**
|
|
39
39
|
* Gets the root directory from a files glob
|
|
@@ -53,32 +53,33 @@ class c {
|
|
|
53
53
|
filesGlob: t,
|
|
54
54
|
fsPath: o
|
|
55
55
|
}) {
|
|
56
|
-
const
|
|
57
|
-
return
|
|
56
|
+
const e = this.getRootDir(t), i = new RegExp(`^${e}(.*).mdx?`), s = o.match(i);
|
|
57
|
+
return s == null ? void 0 : s.at(1);
|
|
58
58
|
}
|
|
59
59
|
}
|
|
60
60
|
const C = (n) => ({
|
|
61
61
|
getRoutes: () => {
|
|
62
62
|
const t = /* @__PURE__ */ new Map();
|
|
63
63
|
return n.forEach(
|
|
64
|
-
({ fileImports: o, files:
|
|
64
|
+
({ fileImports: o, files: e, defaultOptions: i }) => Object.entries(o).flatMap(([s, a]) => {
|
|
65
65
|
const r = c.resolveRoutePath({
|
|
66
|
-
filesGlob:
|
|
67
|
-
fsPath:
|
|
66
|
+
filesGlob: e,
|
|
67
|
+
fsPath: s
|
|
68
68
|
});
|
|
69
69
|
if (!r) return [];
|
|
70
70
|
if (t.has(r))
|
|
71
71
|
return console.warn(
|
|
72
|
-
`Duplicate route path found for ${r}. Skipping file at '${
|
|
72
|
+
`Duplicate route path found for ${r}. Skipping file at '${s}'.`
|
|
73
73
|
), [];
|
|
74
74
|
const h = {
|
|
75
75
|
path: r,
|
|
76
76
|
lazy: async () => {
|
|
77
|
-
const { MdxPage: l } = await import("./MdxPage-
|
|
77
|
+
const { MdxPage: l } = await import("./MdxPage-B2FpJ9KC.js"), { default: p, ...g } = await a();
|
|
78
78
|
return {
|
|
79
79
|
element: /* @__PURE__ */ P.jsx(
|
|
80
80
|
l,
|
|
81
81
|
{
|
|
82
|
+
file: s,
|
|
82
83
|
mdxComponent: p,
|
|
83
84
|
...g,
|
|
84
85
|
defaultOptions: i
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"zudoku.plugin-markdown.js","sources":["../src/lib/plugins/markdown/resolver.ts","../src/lib/plugins/markdown/index.tsx"],"sourcesContent":["import fs from \"fs\";\nimport path from \"path\";\nimport {\n ZudokuConfig,\n ZudokuDocsConfig,\n} from \"../../../config/validators/validate.js\";\n\nconst DEFAULT_DOCS_FILES = \"/pages/**/*.{md,mdx}\";\n\n// TODO: This should be dynamic based on the glob selector\nconst SUPPORTED_EXTENSIONS = [\".md\", \".mdx\"];\n\n/**\n * Utilities for resolving markdown file paths and routes\n */\nexport class DocResolver {\n constructor(private config: ZudokuConfig) {}\n\n fileMap = new Map<string, string>();\n\n /**\n * Gets the default docs config from the zudoku config\n */\n getDocsConfigs() {\n const docsConfigs: ZudokuDocsConfig[] = this.config.docs\n ? Array.isArray(this.config.docs)\n ? this.config.docs\n : [this.config.docs]\n : [{ files: DEFAULT_DOCS_FILES }];\n\n return docsConfigs;\n }\n\n /**\n * Resolves the first matching file system path for a given docId\n * @param docId - The docId to resolve\n * @returns\n */\n resolveFilePath(docId: string) {\n const docsConfigs = this.getDocsConfigs();\n let fsPath: string | undefined;\n\n docsConfigs.forEach(({ files: fileGlob }) => {\n if (fsPath) {\n return;\n }\n const rootDir = DocResolver.getRootDir(fileGlob);\n for (const ext of SUPPORTED_EXTENSIONS) {\n if (fsPath) {\n return;\n }\n const checkPath = path.join(rootDir, `${docId}${ext}`);\n if (fs.existsSync(checkPath)) {\n fsPath = checkPath;\n }\n }\n });\n\n return fsPath;\n }\n\n /**\n * Gets the root directory from a files glob\n */\n private static getRootDir(filesGlob: string) {\n let rootDir = filesGlob.split(\"**\")[0];\n if (!rootDir) {\n throw new Error(\"Invalid files glob. Must have '**' in the path.\");\n }\n rootDir = rootDir.replace(\"/**\", \"/\");\n return rootDir;\n }\n\n /**\n * Resolves the route path for a given file system path\n * @param options - The options to resolve the route path\n * @returns The string route path\n */\n static resolveRoutePath({\n filesGlob,\n fsPath,\n }: {\n filesGlob: string;\n fsPath: string;\n }) {\n const rootDir = this.getRootDir(filesGlob);\n const re = new RegExp(`^${rootDir}(.*).mdx?`);\n const match = fsPath.match(re);\n const routePath = match?.at(1);\n return routePath;\n }\n}\n","import type { Toc } from \"@stefanprobst/rehype-extract-toc\";\nimport type { MDXProps } from \"mdx/types.js\";\nimport { RouteObject } from \"react-router-dom\";\nimport { ZudokuDocsConfig } from \"../../../config/validators/validate.js\";\nimport type { ZudokuPlugin } from \"../../core/plugins.js\";\nimport { DocResolver } from \"./resolver.js\";\n\nexport interface MarkdownPluginOptions extends ZudokuDocsConfig {\n fileImports: Record<string, () => Promise<MDXImport>>;\n}\nexport type MarkdownPluginDefaultOptions = Pick<\n Frontmatter,\n \"toc\" | \"disablePager\"\n>;\n\nexport type Frontmatter = {\n title?: string;\n description?: string;\n category?: string;\n toc?: boolean;\n disablePager?: boolean;\n};\n\nexport type MDXImport = {\n tableOfContents: Toc;\n frontmatter: Frontmatter;\n default: (props: MDXProps) => JSX.Element;\n};\n\nexport const markdownPlugin = (\n options: MarkdownPluginOptions[],\n): ZudokuPlugin => ({\n getRoutes: () => {\n const routeMap = new Map<string, RouteObject>();\n options.forEach(({ fileImports, files, defaultOptions }) =>\n Object.entries(fileImports).flatMap(([file, importPromise]) => {\n const routePath = DocResolver.resolveRoutePath({\n filesGlob: files,\n fsPath: file,\n });\n\n if (!routePath) return [];\n\n if (routeMap.has(routePath)) {\n // eslint-disable-next-line no-console\n console.warn(\n `Duplicate route path found for ${routePath}. Skipping file at '${file}'.`,\n );\n return [];\n }\n\n const route: RouteObject = {\n path: routePath,\n lazy: async () => {\n const { MdxPage } = await import(\"./MdxPage.js\");\n const { default: Component, ...props } = await importPromise();\n return {\n element: (\n <MdxPage\n mdxComponent={Component}\n {...props}\n defaultOptions={defaultOptions}\n />\n ),\n };\n },\n };\n routeMap.set(routePath, route);\n }),\n );\n return [...routeMap.values()];\n },\n});\n"],"names":["DEFAULT_DOCS_FILES","SUPPORTED_EXTENSIONS","DocResolver","config","__publicField","docId","docsConfigs","fsPath","fileGlob","rootDir","ext","checkPath","path","fs","filesGlob","re","match","markdownPlugin","options","routeMap","fileImports","files","defaultOptions","file","importPromise","routePath","route","MdxPage","Component","props","jsx"],"mappings":";;;;;AAOA,MAAMA,IAAqB,wBAGrBC,IAAuB,CAAC,OAAO,MAAM;AAKpC,MAAMC,EAAY;AAAA,EACvB,YAAoBC,GAAsB;AAE1C,IAAAC,EAAA,qCAAc;AAFM,SAAA,SAAAD;AAAA,EAAuB;AAAA;AAAA;AAAA;AAAA,EAO3C,iBAAiB;AAOR,WANiC,KAAK,OAAO,OAChD,MAAM,QAAQ,KAAK,OAAO,IAAI,IAC5B,KAAK,OAAO,OACZ,CAAC,KAAK,OAAO,IAAI,IACnB,CAAC,EAAE,OAAOH,EAAA,CAAoB;AAAA,EAGpC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,gBAAgBK,GAAe;AACvB,UAAAC,IAAc,KAAK;AACrB,QAAAC;AAEJ,WAAAD,EAAY,QAAQ,CAAC,EAAE,OAAOE,QAAe;AAC3C,UAAID;AACF;AAEI,YAAAE,IAAUP,EAAY,WAAWM,CAAQ;AAC/C,iBAAWE,KAAOT,GAAsB;AACtC,YAAIM;AACF;AAEI,cAAAI,IAAYC,EAAK,KAAKH,GAAS,GAAGJ,CAAK,GAAGK,CAAG,EAAE;AACjD,QAAAG,EAAG,WAAWF,CAAS,MAChBJ,IAAAI;AAAA,MAEb;AAAA,IAAA,CACD,GAEMJ;AAAA,EACT;AAAA;AAAA;AAAA;AAAA,EAKA,OAAe,WAAWO,GAAmB;AAC3C,QAAIL,IAAUK,EAAU,MAAM,IAAI,EAAE,CAAC;AACrC,QAAI,CAACL;AACG,YAAA,IAAI,MAAM,iDAAiD;AAEzD,WAAAA,IAAAA,EAAQ,QAAQ,OAAO,GAAG,GAC7BA;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,OAAO,iBAAiB;AAAA,IACtB,WAAAK;AAAA,IACA,QAAAP;AAAA,EAAA,GAIC;AACK,UAAAE,IAAU,KAAK,WAAWK,CAAS,GACnCC,IAAK,IAAI,OAAO,IAAIN,CAAO,WAAW,GACtCO,IAAQT,EAAO,MAAMQ,CAAE;AAEtB,WADWC,KAAA,gBAAAA,EAAO,GAAG;AAAA,EAE9B;AACF;AC9Da,MAAAC,IAAiB,CAC5BC,OACkB;AAAA,EAClB,WAAW,MAAM;AACT,UAAAC,wBAAe;AACb,WAAAD,EAAA;AAAA,MAAQ,CAAC,EAAE,aAAAE,GAAa,OAAAC,GAAO,gBAAAC,QACrC,OAAO,QAAQF,CAAW,EAAE,QAAQ,CAAC,CAACG,GAAMC,CAAa,MAAM;AACvD,cAAAC,IAAYvB,EAAY,iBAAiB;AAAA,UAC7C,WAAWmB;AAAA,UACX,QAAQE;AAAA,QAAA,CACT;AAEG,YAAA,CAACE,EAAW,QAAO;AAEnB,YAAAN,EAAS,IAAIM,CAAS;AAEhB,yBAAA;AAAA,YACN,kCAAkCA,CAAS,uBAAuBF,CAAI;AAAA,UAAA,GAEjE;AAGT,cAAMG,IAAqB;AAAA,UACzB,MAAMD;AAAA,UACN,MAAM,YAAY;AAChB,kBAAM,EAAE,SAAAE,EAAA,IAAY,MAAM,OAAO,uBAAc,GACzC,EAAE,SAASC,GAAW,GAAGC,EAAM,IAAI,MAAML;AACxC,mBAAA;AAAA,cACL,SACEM,gBAAAA,EAAA;AAAA,gBAACH;AAAA,gBAAA;AAAA,kBACC,
|
|
1
|
+
{"version":3,"file":"zudoku.plugin-markdown.js","sources":["../src/lib/plugins/markdown/resolver.ts","../src/lib/plugins/markdown/index.tsx"],"sourcesContent":["import fs from \"fs\";\nimport path from \"path\";\nimport {\n ZudokuConfig,\n ZudokuDocsConfig,\n} from \"../../../config/validators/validate.js\";\n\nconst DEFAULT_DOCS_FILES = \"/pages/**/*.{md,mdx}\";\n\n// TODO: This should be dynamic based on the glob selector\nconst SUPPORTED_EXTENSIONS = [\".md\", \".mdx\"];\n\n/**\n * Utilities for resolving markdown file paths and routes\n */\nexport class DocResolver {\n constructor(private config: ZudokuConfig) {}\n\n fileMap = new Map<string, string>();\n\n /**\n * Gets the default docs config from the zudoku config\n */\n getDocsConfigs() {\n const docsConfigs: ZudokuDocsConfig[] = this.config.docs\n ? Array.isArray(this.config.docs)\n ? this.config.docs\n : [this.config.docs]\n : [{ files: DEFAULT_DOCS_FILES }];\n\n return docsConfigs;\n }\n\n /**\n * Resolves the first matching file system path for a given docId\n * @param docId - The docId to resolve\n * @returns\n */\n resolveFilePath(docId: string) {\n const docsConfigs = this.getDocsConfigs();\n let fsPath: string | undefined;\n\n docsConfigs.forEach(({ files: fileGlob }) => {\n if (fsPath) {\n return;\n }\n const rootDir = DocResolver.getRootDir(fileGlob);\n for (const ext of SUPPORTED_EXTENSIONS) {\n if (fsPath) {\n return;\n }\n const checkPath = path.join(rootDir, `${docId}${ext}`);\n if (fs.existsSync(checkPath)) {\n fsPath = checkPath;\n }\n }\n });\n\n return fsPath;\n }\n\n /**\n * Gets the root directory from a files glob\n */\n private static getRootDir(filesGlob: string) {\n let rootDir = filesGlob.split(\"**\")[0];\n if (!rootDir) {\n throw new Error(\"Invalid files glob. Must have '**' in the path.\");\n }\n rootDir = rootDir.replace(\"/**\", \"/\");\n return rootDir;\n }\n\n /**\n * Resolves the route path for a given file system path\n * @param options - The options to resolve the route path\n * @returns The string route path\n */\n static resolveRoutePath({\n filesGlob,\n fsPath,\n }: {\n filesGlob: string;\n fsPath: string;\n }) {\n const rootDir = this.getRootDir(filesGlob);\n const re = new RegExp(`^${rootDir}(.*).mdx?`);\n const match = fsPath.match(re);\n const routePath = match?.at(1);\n return routePath;\n }\n}\n","import type { Toc } from \"@stefanprobst/rehype-extract-toc\";\nimport type { MDXProps } from \"mdx/types.js\";\nimport { RouteObject } from \"react-router-dom\";\nimport { ZudokuDocsConfig } from \"../../../config/validators/validate.js\";\nimport type { ZudokuPlugin } from \"../../core/plugins.js\";\nimport { DocResolver } from \"./resolver.js\";\n\nexport interface MarkdownPluginOptions extends ZudokuDocsConfig {\n fileImports: Record<string, () => Promise<MDXImport>>;\n}\nexport type MarkdownPluginDefaultOptions = Pick<\n Frontmatter,\n \"toc\" | \"disablePager\"\n>;\n\nexport type Frontmatter = {\n title?: string;\n description?: string;\n category?: string;\n toc?: boolean;\n disablePager?: boolean;\n};\n\nexport type MDXImport = {\n tableOfContents: Toc;\n frontmatter: Frontmatter;\n default: (props: MDXProps) => JSX.Element;\n};\n\nexport const markdownPlugin = (\n options: MarkdownPluginOptions[],\n): ZudokuPlugin => ({\n getRoutes: () => {\n const routeMap = new Map<string, RouteObject>();\n options.forEach(({ fileImports, files, defaultOptions }) =>\n Object.entries(fileImports).flatMap(([file, importPromise]) => {\n const routePath = DocResolver.resolveRoutePath({\n filesGlob: files,\n fsPath: file,\n });\n\n if (!routePath) return [];\n\n if (routeMap.has(routePath)) {\n // eslint-disable-next-line no-console\n console.warn(\n `Duplicate route path found for ${routePath}. Skipping file at '${file}'.`,\n );\n return [];\n }\n\n const route: RouteObject = {\n path: routePath,\n lazy: async () => {\n const { MdxPage } = await import(\"./MdxPage.js\");\n const { default: Component, ...props } = await importPromise();\n return {\n element: (\n <MdxPage\n file={file}\n mdxComponent={Component}\n {...props}\n defaultOptions={defaultOptions}\n />\n ),\n };\n },\n };\n routeMap.set(routePath, route);\n }),\n );\n return [...routeMap.values()];\n },\n});\n"],"names":["DEFAULT_DOCS_FILES","SUPPORTED_EXTENSIONS","DocResolver","config","__publicField","docId","docsConfigs","fsPath","fileGlob","rootDir","ext","checkPath","path","fs","filesGlob","re","match","markdownPlugin","options","routeMap","fileImports","files","defaultOptions","file","importPromise","routePath","route","MdxPage","Component","props","jsx"],"mappings":";;;;;AAOA,MAAMA,IAAqB,wBAGrBC,IAAuB,CAAC,OAAO,MAAM;AAKpC,MAAMC,EAAY;AAAA,EACvB,YAAoBC,GAAsB;AAE1C,IAAAC,EAAA,qCAAc;AAFM,SAAA,SAAAD;AAAA,EAAuB;AAAA;AAAA;AAAA;AAAA,EAO3C,iBAAiB;AAOR,WANiC,KAAK,OAAO,OAChD,MAAM,QAAQ,KAAK,OAAO,IAAI,IAC5B,KAAK,OAAO,OACZ,CAAC,KAAK,OAAO,IAAI,IACnB,CAAC,EAAE,OAAOH,EAAA,CAAoB;AAAA,EAGpC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,gBAAgBK,GAAe;AACvB,UAAAC,IAAc,KAAK;AACrB,QAAAC;AAEJ,WAAAD,EAAY,QAAQ,CAAC,EAAE,OAAOE,QAAe;AAC3C,UAAID;AACF;AAEI,YAAAE,IAAUP,EAAY,WAAWM,CAAQ;AAC/C,iBAAWE,KAAOT,GAAsB;AACtC,YAAIM;AACF;AAEI,cAAAI,IAAYC,EAAK,KAAKH,GAAS,GAAGJ,CAAK,GAAGK,CAAG,EAAE;AACjD,QAAAG,EAAG,WAAWF,CAAS,MAChBJ,IAAAI;AAAA,MAEb;AAAA,IAAA,CACD,GAEMJ;AAAA,EACT;AAAA;AAAA;AAAA;AAAA,EAKA,OAAe,WAAWO,GAAmB;AAC3C,QAAIL,IAAUK,EAAU,MAAM,IAAI,EAAE,CAAC;AACrC,QAAI,CAACL;AACG,YAAA,IAAI,MAAM,iDAAiD;AAEzD,WAAAA,IAAAA,EAAQ,QAAQ,OAAO,GAAG,GAC7BA;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,OAAO,iBAAiB;AAAA,IACtB,WAAAK;AAAA,IACA,QAAAP;AAAA,EAAA,GAIC;AACK,UAAAE,IAAU,KAAK,WAAWK,CAAS,GACnCC,IAAK,IAAI,OAAO,IAAIN,CAAO,WAAW,GACtCO,IAAQT,EAAO,MAAMQ,CAAE;AAEtB,WADWC,KAAA,gBAAAA,EAAO,GAAG;AAAA,EAE9B;AACF;AC9Da,MAAAC,IAAiB,CAC5BC,OACkB;AAAA,EAClB,WAAW,MAAM;AACT,UAAAC,wBAAe;AACb,WAAAD,EAAA;AAAA,MAAQ,CAAC,EAAE,aAAAE,GAAa,OAAAC,GAAO,gBAAAC,QACrC,OAAO,QAAQF,CAAW,EAAE,QAAQ,CAAC,CAACG,GAAMC,CAAa,MAAM;AACvD,cAAAC,IAAYvB,EAAY,iBAAiB;AAAA,UAC7C,WAAWmB;AAAA,UACX,QAAQE;AAAA,QAAA,CACT;AAEG,YAAA,CAACE,EAAW,QAAO;AAEnB,YAAAN,EAAS,IAAIM,CAAS;AAEhB,yBAAA;AAAA,YACN,kCAAkCA,CAAS,uBAAuBF,CAAI;AAAA,UAAA,GAEjE;AAGT,cAAMG,IAAqB;AAAA,UACzB,MAAMD;AAAA,UACN,MAAM,YAAY;AAChB,kBAAM,EAAE,SAAAE,EAAA,IAAY,MAAM,OAAO,uBAAc,GACzC,EAAE,SAASC,GAAW,GAAGC,EAAM,IAAI,MAAML;AACxC,mBAAA;AAAA,cACL,SACEM,gBAAAA,EAAA;AAAA,gBAACH;AAAA,gBAAA;AAAA,kBACC,MAAAJ;AAAA,kBACA,cAAcK;AAAA,kBACb,GAAGC;AAAA,kBACJ,gBAAAP;AAAA,gBAAA;AAAA,cACF;AAAA,YAAA;AAAA,UAGN;AAAA,QAAA;AAEO,QAAAH,EAAA,IAAIM,GAAWC,CAAK;AAAA,MAAA,CAC9B;AAAA,IAAA,GAEI,CAAC,GAAGP,EAAS,OAAA,CAAQ;AAAA,EAC9B;AACF;"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "zudoku",
|
|
3
|
-
"version": "0.18.
|
|
3
|
+
"version": "0.18.4",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"homepage": "https://zudoku.dev",
|
|
6
6
|
"repository": {
|
|
@@ -82,6 +82,10 @@
|
|
|
82
82
|
"import": "./lib/zudoku.components.js",
|
|
83
83
|
"types": "./dist/lib/components/index.d.ts"
|
|
84
84
|
},
|
|
85
|
+
"./post-processors/*": {
|
|
86
|
+
"import": "./lib/post-processors/*.js",
|
|
87
|
+
"types": "./dist/lib/plugins/openapi/post-processors/*.d.ts"
|
|
88
|
+
},
|
|
85
89
|
"./icons": {
|
|
86
90
|
"import": "./lib/zudoku.icons.js",
|
|
87
91
|
"types": "./dist/lib/icons.d.ts"
|
|
@@ -100,6 +104,10 @@
|
|
|
100
104
|
},
|
|
101
105
|
"./app/*": {
|
|
102
106
|
"import": "./src/app/*"
|
|
107
|
+
},
|
|
108
|
+
"./with-zuplo": {
|
|
109
|
+
"require": "./dist/zuplo/with-zuplo.js",
|
|
110
|
+
"import": "./dist/zuplo/with-zuplo.js"
|
|
103
111
|
}
|
|
104
112
|
},
|
|
105
113
|
"dependencies": {
|
|
@@ -140,8 +148,8 @@
|
|
|
140
148
|
"@tanstack/react-query": "5.61.0",
|
|
141
149
|
"@types/react": "18.3.11",
|
|
142
150
|
"@types/react-dom": "18.3.1",
|
|
143
|
-
"@vitejs/plugin-react": "4.3.
|
|
144
|
-
"@zudoku/config": "0.18.
|
|
151
|
+
"@vitejs/plugin-react": "4.3.4",
|
|
152
|
+
"@zudoku/config": "0.18.4",
|
|
145
153
|
"@zudoku/httpsnippet": "10.0.9",
|
|
146
154
|
"@zudoku/react-helmet-async": "2.0.4",
|
|
147
155
|
"autoprefixer": "10.4.20",
|
|
@@ -217,11 +225,13 @@
|
|
|
217
225
|
"@types/react-is": "18.3.0",
|
|
218
226
|
"@types/semver": "7.5.8",
|
|
219
227
|
"@types/yargs": "17.0.33",
|
|
228
|
+
"@vitest/coverage-v8": "2.1.8",
|
|
220
229
|
"mdast-util-mdx": "3.0.0",
|
|
221
230
|
"react": "18.3.1",
|
|
222
231
|
"react-dom": "18.3.1",
|
|
223
232
|
"rollup-plugin-visualizer": "5.12.0",
|
|
224
|
-
"typescript": "5.7.2"
|
|
233
|
+
"typescript": "5.7.2",
|
|
234
|
+
"vitest": "2.1.8"
|
|
225
235
|
},
|
|
226
236
|
"peerDependencies": {
|
|
227
237
|
"react": ">=18",
|
|
@@ -239,7 +249,7 @@
|
|
|
239
249
|
"hack:fix-worker-paths": "node ./scripts/hack-worker.mjs",
|
|
240
250
|
"clean": "tsc --build --clean",
|
|
241
251
|
"codegen": "graphql-codegen --config ./src/codegen.ts",
|
|
242
|
-
"test": "
|
|
252
|
+
"test": "vitest run"
|
|
243
253
|
},
|
|
244
254
|
"module": "./dist/index.js",
|
|
245
255
|
"types": "./dist/index.d.ts"
|
|
@@ -87,7 +87,6 @@ export const Header = memo(function HeaderInner() {
|
|
|
87
87
|
loading="lazy"
|
|
88
88
|
/>
|
|
89
89
|
<img
|
|
90
|
-
data-hide-on-theme="light"
|
|
91
90
|
src={
|
|
92
91
|
/https?:\/\//.test(page.logo.src.dark)
|
|
93
92
|
? page.logo.src.dark
|
|
@@ -98,7 +97,7 @@ export const Header = memo(function HeaderInner() {
|
|
|
98
97
|
}
|
|
99
98
|
alt={page.logo.alt ?? page.pageTitle}
|
|
100
99
|
style={{ width: page.logo.width }}
|
|
101
|
-
className="h-10"
|
|
100
|
+
className="h-10 hidden dark:block"
|
|
102
101
|
loading="lazy"
|
|
103
102
|
/>
|
|
104
103
|
</>
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
2
|
+
import { type RecordAny, traverse } from "../../../util/traverse.js";
|
|
2
3
|
import type { OpenAPIDocument } from "../index.js";
|
|
3
4
|
/**
|
|
4
5
|
* Upgrade from OpenAPI 3.0.x to 3.1.0
|
|
@@ -6,30 +7,8 @@ import type { OpenAPIDocument } from "../index.js";
|
|
|
6
7
|
* Taken from https://github.com/scalar/openapi-parser/blob/main/packages/openapi-parser/src/utils/upgradeFromThreeToThreeOne.ts
|
|
7
8
|
* https://www.openapis.org/blog/2021/02/16/migrating-from-openapi-3-0-to-3-1-0
|
|
8
9
|
*/
|
|
9
|
-
export function traverse(
|
|
10
|
-
specification: Record<string, any>,
|
|
11
|
-
transform: (specification: Record<string, any>) => Record<string, any>,
|
|
12
|
-
) {
|
|
13
|
-
const result: Record<string, any> = {};
|
|
14
10
|
|
|
15
|
-
|
|
16
|
-
if (Array.isArray(value)) {
|
|
17
|
-
result[key] = value.map((item) =>
|
|
18
|
-
typeof item === "object" && item !== null
|
|
19
|
-
? traverse(item, transform)
|
|
20
|
-
: item,
|
|
21
|
-
);
|
|
22
|
-
} else if (typeof value === "object" && value !== null) {
|
|
23
|
-
result[key] = traverse(value, transform);
|
|
24
|
-
} else {
|
|
25
|
-
result[key] = value;
|
|
26
|
-
}
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
return transform(result);
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
export const upgradeSchema = (schema: Record<string, any>): OpenAPIDocument => {
|
|
11
|
+
export const upgradeSchema = (schema: RecordAny): OpenAPIDocument => {
|
|
33
12
|
if (schema.openapi?.startsWith("3.0")) {
|
|
34
13
|
schema.openapi = "3.1.0";
|
|
35
14
|
}
|
|
@@ -64,9 +43,7 @@ export const upgradeSchema = (schema: Record<string, any>): OpenAPIDocument => {
|
|
|
64
43
|
schema = traverse(schema, (sub) => {
|
|
65
44
|
if (sub.example !== undefined) {
|
|
66
45
|
sub.examples = {
|
|
67
|
-
default:
|
|
68
|
-
value: sub.example,
|
|
69
|
-
},
|
|
46
|
+
default: sub.example,
|
|
70
47
|
};
|
|
71
48
|
delete sub.example;
|
|
72
49
|
}
|
|
@@ -77,7 +54,7 @@ export const upgradeSchema = (schema: Record<string, any>): OpenAPIDocument => {
|
|
|
77
54
|
schema = traverse(schema, (sub) => {
|
|
78
55
|
if (sub.type === "object" && sub.properties !== undefined) {
|
|
79
56
|
for (const [, value] of Object.entries(sub.properties)) {
|
|
80
|
-
const v = (value ?? {}) as
|
|
57
|
+
const v = (value ?? {}) as RecordAny;
|
|
81
58
|
if (v.type === "string" && v.format === "binary") {
|
|
82
59
|
v.contentEncoding = "application/octet-stream";
|
|
83
60
|
delete v.format;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { useMDXComponents } from "@mdx-js/react";
|
|
2
2
|
import slugify from "@sindresorhus/slugify";
|
|
3
3
|
import { Helmet } from "@zudoku/react-helmet-async";
|
|
4
|
-
import { type PropsWithChildren } from "react";
|
|
4
|
+
import { type PropsWithChildren, useEffect } from "react";
|
|
5
5
|
import { Link } from "react-router-dom";
|
|
6
6
|
import { CategoryHeading } from "../../components/CategoryHeading.js";
|
|
7
7
|
import { Heading } from "../../components/Heading.js";
|
|
@@ -15,6 +15,14 @@ import { cn } from "../../util/cn.js";
|
|
|
15
15
|
import { Toc } from "./Toc.js";
|
|
16
16
|
import { MarkdownPluginDefaultOptions, MDXImport } from "./index.js";
|
|
17
17
|
|
|
18
|
+
declare global {
|
|
19
|
+
interface Window {
|
|
20
|
+
__getReactRefreshIgnoredExports?: (args: {
|
|
21
|
+
id: string;
|
|
22
|
+
}) => string[] | undefined;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
18
26
|
const MarkdownHeadings = {
|
|
19
27
|
h2: ({ children, id }) => (
|
|
20
28
|
<Heading level={2} id={id} registerSidebarAnchor>
|
|
@@ -30,11 +38,13 @@ const MarkdownHeadings = {
|
|
|
30
38
|
|
|
31
39
|
export const MdxPage = ({
|
|
32
40
|
mdxComponent: MdxComponent,
|
|
41
|
+
file,
|
|
33
42
|
frontmatter = {},
|
|
34
43
|
defaultOptions,
|
|
35
44
|
tableOfContents,
|
|
36
45
|
}: PropsWithChildren<
|
|
37
46
|
Omit<MDXImport, "default"> & {
|
|
47
|
+
file: string;
|
|
38
48
|
mdxComponent: MDXImport["default"];
|
|
39
49
|
defaultOptions?: MarkdownPluginDefaultOptions;
|
|
40
50
|
}
|
|
@@ -57,6 +67,20 @@ export const MdxPage = ({
|
|
|
57
67
|
|
|
58
68
|
const { prev, next } = usePrevNext();
|
|
59
69
|
|
|
70
|
+
useEffect(() => {
|
|
71
|
+
if (process.env.NODE_ENV === "development") {
|
|
72
|
+
window.__getReactRefreshIgnoredExports = ({ id }) => {
|
|
73
|
+
if (!id.endsWith(file)) return;
|
|
74
|
+
|
|
75
|
+
return ["frontmatter", "tableOfContents"];
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
return () => {
|
|
79
|
+
window.__getReactRefreshIgnoredExports = undefined;
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
}, [file]);
|
|
83
|
+
|
|
60
84
|
return (
|
|
61
85
|
<div className="xl:grid grid-cols-[--sidecar-grid-cols] gap-8 justify-between">
|
|
62
86
|
<Helmet>
|
|
@@ -15,6 +15,20 @@ type GraphQLResponse<TResult> = {
|
|
|
15
15
|
data: TResult;
|
|
16
16
|
};
|
|
17
17
|
|
|
18
|
+
const resolveVariables = async (variables?: unknown) => {
|
|
19
|
+
if (!variables) return;
|
|
20
|
+
|
|
21
|
+
if (
|
|
22
|
+
typeof variables === "object" &&
|
|
23
|
+
"type" in variables &&
|
|
24
|
+
variables.type === "file" &&
|
|
25
|
+
"input" in variables &&
|
|
26
|
+
typeof variables.input === "function"
|
|
27
|
+
) {
|
|
28
|
+
variables.input = await variables.input();
|
|
29
|
+
}
|
|
30
|
+
};
|
|
31
|
+
|
|
18
32
|
const throwIfError = (response: GraphQLResponse<unknown>) => {
|
|
19
33
|
if (!response.errors?.[0]) return;
|
|
20
34
|
|
|
@@ -47,6 +61,9 @@ export class GraphQLClient {
|
|
|
47
61
|
...[variables]: TVariables extends Record<string, never> ? [] : [TVariables]
|
|
48
62
|
) => {
|
|
49
63
|
const operationName = query.match(/query (\w+)/)?.[1];
|
|
64
|
+
|
|
65
|
+
await resolveVariables(variables);
|
|
66
|
+
|
|
50
67
|
const body = JSON.stringify({ query, variables, operationName });
|
|
51
68
|
|
|
52
69
|
switch (this.#mode) {
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import
|
|
1
|
+
import hashit from "object-hash";
|
|
2
|
+
import { useContext, useMemo } from "react";
|
|
2
3
|
import type { TypedDocumentString } from "../graphql/graphql.js";
|
|
3
4
|
import { GraphQLContext } from "./GraphQLContext.js";
|
|
4
5
|
|
|
@@ -11,8 +12,10 @@ export const useCreateQuery = <TResult, TVariables>(
|
|
|
11
12
|
throw new Error("useGraphQL must be used within a GraphQLProvider");
|
|
12
13
|
}
|
|
13
14
|
|
|
15
|
+
const hash = useMemo(() => hashit(variables[0] ?? {}), [variables]);
|
|
16
|
+
|
|
14
17
|
return {
|
|
15
18
|
queryFn: () => graphQLClient.fetch(query, ...variables),
|
|
16
|
-
queryKey: [query,
|
|
19
|
+
queryKey: [query, hash],
|
|
17
20
|
} as const;
|
|
18
21
|
};
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
import { describe, expect, it } from "vitest";
|
|
2
|
+
import { removeExtensions } from "./removeExtensions.js";
|
|
3
|
+
|
|
4
|
+
const baseDoc = {
|
|
5
|
+
openapi: "3.1.0",
|
|
6
|
+
"x-root-ext": "remove me",
|
|
7
|
+
info: {
|
|
8
|
+
title: "Test API",
|
|
9
|
+
version: "1.0.0",
|
|
10
|
+
"x-info-ext": "remove me",
|
|
11
|
+
},
|
|
12
|
+
paths: {
|
|
13
|
+
"/test": {
|
|
14
|
+
"x-path-ext": "remove me",
|
|
15
|
+
parameters: [
|
|
16
|
+
{
|
|
17
|
+
name: "param1",
|
|
18
|
+
in: "query",
|
|
19
|
+
schema: { type: "string" },
|
|
20
|
+
"x-param-ext": "remove me",
|
|
21
|
+
},
|
|
22
|
+
],
|
|
23
|
+
get: {
|
|
24
|
+
"x-operation-ext": "remove me",
|
|
25
|
+
responses: {
|
|
26
|
+
"200": {
|
|
27
|
+
description: "OK",
|
|
28
|
+
"x-response-ext": "remove me",
|
|
29
|
+
},
|
|
30
|
+
},
|
|
31
|
+
parameters: [
|
|
32
|
+
{
|
|
33
|
+
name: "opParam1",
|
|
34
|
+
in: "header",
|
|
35
|
+
schema: { type: "string" },
|
|
36
|
+
"x-op-param-ext": "remove me",
|
|
37
|
+
},
|
|
38
|
+
],
|
|
39
|
+
},
|
|
40
|
+
},
|
|
41
|
+
},
|
|
42
|
+
tags: [
|
|
43
|
+
{
|
|
44
|
+
name: "example",
|
|
45
|
+
"x-tag-ext": "remove me",
|
|
46
|
+
},
|
|
47
|
+
],
|
|
48
|
+
components: {
|
|
49
|
+
securitySchemes: {
|
|
50
|
+
ApiKeyAuth: {
|
|
51
|
+
type: "apiKey",
|
|
52
|
+
name: "api_key",
|
|
53
|
+
in: "header",
|
|
54
|
+
"x-security-ext": "remove me",
|
|
55
|
+
},
|
|
56
|
+
},
|
|
57
|
+
},
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
describe("removeExtensions", () => {
|
|
61
|
+
it("removes all x- extensions by default", () => {
|
|
62
|
+
const processed = removeExtensions()(baseDoc);
|
|
63
|
+
|
|
64
|
+
const removedExtensions = [
|
|
65
|
+
"x-root-ext",
|
|
66
|
+
"info.x-info-ext",
|
|
67
|
+
"paths./test.x-path-ext",
|
|
68
|
+
"paths./test.parameters[0].x-param-ext",
|
|
69
|
+
"paths./test.get.x-operation-ext",
|
|
70
|
+
"paths./test.get.responses.200.x-response-ext",
|
|
71
|
+
"paths./test.get.parameters[0].x-op-param-ext",
|
|
72
|
+
"tags[0].x-tag-ext",
|
|
73
|
+
"components.securitySchemes.ApiKeyAuth.x-security-ext",
|
|
74
|
+
];
|
|
75
|
+
|
|
76
|
+
removedExtensions.forEach((ext) => {
|
|
77
|
+
expect(processed).not.toHaveProperty(ext.split("."));
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
// Assert that non-x- fields remain unchanged
|
|
81
|
+
expect(processed.openapi).toBe("3.1.0");
|
|
82
|
+
expect(processed.info.title).toBe("Test API");
|
|
83
|
+
expect(processed).toHaveProperty(
|
|
84
|
+
["paths", "/test", "get", "responses", "200", "description"],
|
|
85
|
+
"OK",
|
|
86
|
+
);
|
|
87
|
+
expect(processed.tags[0].name).toBe("example");
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
it("removes only specified x- extensions when names are provided", () => {
|
|
91
|
+
const docWithExtraExtensions = {
|
|
92
|
+
...baseDoc,
|
|
93
|
+
info: { ...baseDoc.info, "x-other-ext": "keep me" },
|
|
94
|
+
};
|
|
95
|
+
|
|
96
|
+
const processed = removeExtensions({
|
|
97
|
+
keys: ["x-path-ext", "x-param-ext"],
|
|
98
|
+
})(docWithExtraExtensions);
|
|
99
|
+
|
|
100
|
+
// Assert specified extensions are removed
|
|
101
|
+
expect(processed.paths["/test"]["x-path-ext"]).toBeUndefined();
|
|
102
|
+
expect(
|
|
103
|
+
processed.paths["/test"].parameters[0]["x-param-ext"],
|
|
104
|
+
).toBeUndefined();
|
|
105
|
+
|
|
106
|
+
// Assert other x- fields remain
|
|
107
|
+
expect(processed["x-root-ext"]).toBe("remove me");
|
|
108
|
+
expect(processed.info["x-info-ext"]).toBe("remove me");
|
|
109
|
+
expect(processed.info["x-other-ext"]).toBe("keep me");
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
it("handles deeply nested extensions", () => {
|
|
113
|
+
const deeplyNested = {
|
|
114
|
+
a: {
|
|
115
|
+
b: {
|
|
116
|
+
c: {
|
|
117
|
+
"x-deep-ext": "remove me",
|
|
118
|
+
d: {
|
|
119
|
+
e: "value",
|
|
120
|
+
"x-another-ext": "remove me",
|
|
121
|
+
},
|
|
122
|
+
},
|
|
123
|
+
},
|
|
124
|
+
},
|
|
125
|
+
};
|
|
126
|
+
|
|
127
|
+
const processed = removeExtensions()(deeplyNested);
|
|
128
|
+
|
|
129
|
+
expect(processed.a.b.c["x-deep-ext"]).toBeUndefined();
|
|
130
|
+
expect(processed.a.b.c.d["x-another-ext"]).toBeUndefined();
|
|
131
|
+
expect(processed.a.b.c.d.e).toBe("value");
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
it("does nothing if no x- extensions are present", () => {
|
|
135
|
+
const docWithoutExtensions = {
|
|
136
|
+
openapi: "3.1.0",
|
|
137
|
+
info: { title: "API without extensions" },
|
|
138
|
+
};
|
|
139
|
+
|
|
140
|
+
const processed = removeExtensions()(docWithoutExtensions);
|
|
141
|
+
|
|
142
|
+
expect(processed).toEqual(docWithoutExtensions);
|
|
143
|
+
});
|
|
144
|
+
});
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { type RecordAny, traverse } from "./traverse.js";
|
|
2
|
+
|
|
3
|
+
interface RemoveExtensionsOptions {
|
|
4
|
+
keys?: string[];
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
// Remove all `x-` prefixed key/value pairs, or filter by names if provided
|
|
8
|
+
export const removeExtensions =
|
|
9
|
+
({ keys }: RemoveExtensionsOptions = {}) =>
|
|
10
|
+
(doc: RecordAny): RecordAny =>
|
|
11
|
+
traverse(doc, (spec) => {
|
|
12
|
+
const result: RecordAny = {};
|
|
13
|
+
|
|
14
|
+
for (const [key, value] of Object.entries(spec)) {
|
|
15
|
+
const isExtension = key.startsWith("x-");
|
|
16
|
+
const shouldRemove =
|
|
17
|
+
isExtension && (keys === undefined || keys.includes(key));
|
|
18
|
+
|
|
19
|
+
if (shouldRemove) continue;
|
|
20
|
+
|
|
21
|
+
result[key] = value;
|
|
22
|
+
}
|
|
23
|
+
return result;
|
|
24
|
+
});
|