@scalar/nextjs-openapi 0.3.2 → 0.3.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/CHANGELOG.md CHANGED
@@ -1,5 +1,21 @@
1
1
  # @scalar/nextjs-openapi
2
2
 
3
+ ## 0.3.4
4
+
5
+ ### Patch Changes
6
+
7
+ - [#8466](https://github.com/scalar/scalar/pull/8466): chore: new build pipeline
8
+
9
+ ## 0.3.3
10
+
11
+ ### Patch Changes
12
+
13
+ #### Updated Dependencies
14
+
15
+ - **@scalar/types@0.7.3**
16
+
17
+ - **@scalar/nextjs-api-reference@0.10.3**
18
+
3
19
  ## 0.3.2
4
20
 
5
21
  ### Patch Changes
package/dist/index.js CHANGED
@@ -1,5 +1 @@
1
- import { OpenAPI } from "./openapi.js";
2
- export {
3
- OpenAPI
4
- };
5
- //# sourceMappingURL=index.js.map
1
+ export { OpenAPI } from './openapi.js';
package/dist/openapi.js CHANGED
@@ -1,69 +1,80 @@
1
- import { readFileSync } from "node:fs";
2
- import { ApiReference } from "@scalar/nextjs-api-reference";
3
- import { sync } from "fast-glob";
4
- import {
5
- JSDocParsingMode,
6
- ScriptKind,
7
- ScriptTarget,
8
- createProgram,
9
- createSourceFile
10
- } from "typescript";
11
- import { getPathSchema } from "./path.js";
1
+ import { readFileSync } from 'node:fs';
2
+ import { ApiReference } from '@scalar/nextjs-api-reference';
3
+ import { sync } from 'fast-glob';
4
+ import { JSDocParsingMode, ScriptKind, ScriptTarget, createProgram, createSourceFile, } from 'typescript';
5
+ import { getPathSchema } from './path.js';
6
+ // TODO switch to watcher
7
+ // @see https://github.com/microsoft/TypeScript-wiki/blob/main/Using-the-Compiler-API.md#writing-an-incremental-program-watcher
12
8
  const compilerHost = {
13
- fileExists: () => true,
14
- getCanonicalFileName: (filename) => filename,
15
- getCurrentDirectory: () => "",
16
- getDefaultLibFileName: () => "",
17
- getNewLine: () => "\n",
18
- getSourceFile: (filename) => createSourceFile(filename, readFileSync(filename).toString(), ScriptTarget.Latest, false, ScriptKind.TS),
19
- jsDocParsingMode: JSDocParsingMode.ParseAll,
20
- readFile: () => void 0,
21
- useCaseSensitiveFileNames: () => true,
22
- writeFile: () => null
9
+ fileExists: () => true,
10
+ getCanonicalFileName: (filename) => filename,
11
+ getCurrentDirectory: () => '',
12
+ getDefaultLibFileName: () => '',
13
+ getNewLine: () => '\n',
14
+ getSourceFile: (filename) => createSourceFile(filename, readFileSync(filename).toString(), ScriptTarget.Latest, false, ScriptKind.TS),
15
+ jsDocParsingMode: JSDocParsingMode.ParseAll,
16
+ readFile: () => undefined,
17
+ useCaseSensitiveFileNames: () => true,
18
+ writeFile: () => null,
23
19
  };
20
+ /** OpenAPI 3.1.0 Spec */
24
21
  const spec = {
25
- openapi: "3.1.0",
26
- info: {
27
- title: `${process.env.npm_package_name} - Next.js OpenAPI Spec by Scalar`,
28
- description: process.env.npm_package_description || "This file has been autogenerated, check out the docs to see how to customize these options",
29
- version: process.env.npm_package_version || "0.0.0"
30
- },
31
- paths: {}
32
- };
33
- const OpenAPI = (config = {}) => {
34
- const apiDirectory = config.apiDirectory ?? "app/api";
35
- const programFileNames = sync(apiDirectory + "/**/*.ts");
36
- const routeFileNames = sync(apiDirectory + "/**/route.ts");
37
- const program = createProgram(
38
- programFileNames,
39
- {
40
- noResolve: true,
41
- target: ScriptTarget.Latest
22
+ openapi: '3.1.0',
23
+ info: {
24
+ title: `${process.env.npm_package_name} - Next.js OpenAPI Spec by Scalar`,
25
+ description: process.env.npm_package_description ||
26
+ 'This file has been autogenerated, check out the docs to see how to customize these options',
27
+ version: process.env.npm_package_version || '0.0.0',
42
28
  },
43
- compilerHost
44
- );
45
- routeFileNames.forEach((fileName) => {
46
- const sourceFile = program.getSourceFile(fileName);
47
- if (sourceFile) {
48
- const resp = getPathSchema(sourceFile, program);
49
- const rawPath = fileName.replace(/^(?:src\/)?app|\/route\.ts$/g, "").replace(/\[/g, "{").replace(/\/\(\w+\)/g, "").replace(/]/g, "}");
50
- const path = rawPath.startsWith("/") ? rawPath : "/" + rawPath;
51
- spec.paths[path] = resp;
52
- }
53
- });
54
- return {
55
- GET: async (req) => {
56
- if (req.nextUrl.pathname.endsWith(".json")) {
57
- spec.servers = [{ url: req.nextUrl.origin }];
58
- return Response.json(spec);
59
- }
60
- return await ApiReference({
61
- url: req.nextUrl.pathname + "/schema.json"
62
- })();
63
- }
64
- };
29
+ paths: {},
65
30
  };
66
- export {
67
- OpenAPI
31
+ /**
32
+ * Scalar Next.js OpenAPI schema generation
33
+ *
34
+ * Handles both the schema generation as well as the references
35
+ *
36
+ * TODO:
37
+ * - Docs
38
+ * - file watcher
39
+ * - caching
40
+ */
41
+ export const OpenAPI = (config = {}) => {
42
+ const apiDirectory = config.apiDirectory ?? 'app/api';
43
+ /** All ts files required for the schema */
44
+ const programFileNames = sync(apiDirectory + '/**/*.ts');
45
+ /** Specific route files to generate paths */
46
+ const routeFileNames = sync(apiDirectory + '/**/route.ts');
47
+ const program = createProgram(programFileNames, {
48
+ noResolve: true,
49
+ target: ScriptTarget.Latest,
50
+ }, compilerHost);
51
+ // Loop over all non scalar files to extract openapi data using AST
52
+ routeFileNames.forEach((fileName) => {
53
+ const sourceFile = program.getSourceFile(fileName);
54
+ if (sourceFile) {
55
+ const resp = getPathSchema(sourceFile, program);
56
+ // Grab the path from the fileName
57
+ const rawPath = fileName
58
+ .replace(/^(?:src\/)?app|\/route\.ts$/g, '')
59
+ .replace(/\[/g, '{')
60
+ .replace(/\/\(\w+\)/g, '')
61
+ .replace(/]/g, '}');
62
+ const path = rawPath.startsWith('/') ? rawPath : '/' + rawPath;
63
+ spec.paths[path] = resp;
64
+ }
65
+ });
66
+ return {
67
+ GET: async (req) => {
68
+ // OpenAPI Schema JSON
69
+ if (req.nextUrl.pathname.endsWith('.json')) {
70
+ // Just grabbing the origin here but we can be smarter and look for the greatest common few segments
71
+ spec.servers = [{ url: req.nextUrl.origin }];
72
+ return Response.json(spec);
73
+ }
74
+ // References
75
+ return await ApiReference({
76
+ url: req.nextUrl.pathname + '/schema.json',
77
+ })();
78
+ },
79
+ };
68
80
  };
69
- //# sourceMappingURL=openapi.js.map
package/dist/path.js CHANGED
@@ -1,72 +1,82 @@
1
- import { extname, join } from "node:path";
2
- import { generateResponses, getJSDocFromNode, getSchemaFromTypeNode } from "@scalar/ts-to-openapi";
3
- import {
4
- isFunctionDeclaration,
5
- isIdentifier,
6
- isParameter,
7
- isPropertySignature,
8
- isTypeLiteralNode,
9
- isVariableStatement
10
- } from "typescript";
1
+ import { extname, join } from 'node:path';
2
+ import { generateResponses, getJSDocFromNode, getSchemaFromTypeNode } from '@scalar/ts-to-openapi';
3
+ import { isFunctionDeclaration, isIdentifier, isParameter, isPropertySignature, isTypeLiteralNode, isVariableStatement, } from 'typescript';
4
+ /** Check if identifier is a supported http method */
11
5
  const checkForMethod = (identifier) => {
12
- const method = identifier?.escapedText?.toLowerCase();
13
- return method?.match(/^(get|post|put|patch|delete|head|options)$/) ? method : null;
6
+ const method = identifier?.escapedText?.toLowerCase();
7
+ return method?.match(/^(get|post|put|patch|delete|head|options)$/) ? method : null;
14
8
  };
15
9
  const fileNameResolver = (source, target) => {
16
- const sourceExt = extname(source);
17
- const targetExt = extname(target);
18
- const targetRelative = target + (targetExt ? "" : sourceExt);
19
- const targetPath = join(source.replace(/\/([^/]+)$/, ""), targetRelative);
20
- return targetPath;
10
+ const sourceExt = extname(source);
11
+ const targetExt = extname(target);
12
+ const targetRelative = target + (targetExt ? '' : sourceExt);
13
+ const targetPath = join(source.replace(/\/([^/]+)$/, ''), targetRelative);
14
+ return targetPath;
21
15
  };
16
+ /**
17
+ * Takes a parameter node and returns a path parameter schema
18
+ */
22
19
  const extractPathParams = (node, program) => {
23
- if (node && isParameter(node) && node.type && isTypeLiteralNode(node.type) && node.type.members[0] && isPropertySignature(node.type.members[0]) && isIdentifier(node.type.members[0].name) && node.type.members[0].name.escapedText === "params" && node.type.members[0].type && isTypeLiteralNode(node.type.members[0].type)) {
24
- return node.type.members[0].type?.members.flatMap((member) => {
25
- if (!isPropertySignature(member) || !member.type) {
26
- return [];
27
- }
28
- return {
29
- name: member.name?.getText(),
30
- schema: getSchemaFromTypeNode(member.type, program, fileNameResolver),
31
- in: "path"
32
- };
33
- });
34
- }
35
- return [];
36
- };
37
- const getPathSchema = (sourceFile, program) => {
38
- const path = {};
39
- const typeChecker = program.getTypeChecker();
40
- sourceFile.statements.forEach((statement) => {
41
- if (isFunctionDeclaration(statement) && statement.name) {
42
- const method = checkForMethod(statement.name);
43
- if (method) {
44
- const { title, description } = getJSDocFromNode(statement);
45
- const parameters = extractPathParams(statement.parameters[1], program);
46
- const responses = generateResponses(statement.body, typeChecker);
47
- path[method] = {
48
- summary: title,
49
- description,
50
- parameters,
51
- responses
52
- };
53
- }
54
- } else if (isVariableStatement(statement)) {
55
- const method = checkForMethod(statement.declarationList.declarations[0]?.name);
56
- if (method) {
57
- const { title, description } = getJSDocFromNode(statement);
58
- const responses = generateResponses(statement, typeChecker);
59
- path[method] = {
60
- summary: title,
61
- description,
62
- responses
63
- };
64
- }
20
+ // Traverse to the params with type guards
21
+ if (node &&
22
+ isParameter(node) &&
23
+ node.type &&
24
+ isTypeLiteralNode(node.type) &&
25
+ node.type.members[0] &&
26
+ isPropertySignature(node.type.members[0]) &&
27
+ isIdentifier(node.type.members[0].name) &&
28
+ node.type.members[0].name.escapedText === 'params' &&
29
+ node.type.members[0].type &&
30
+ isTypeLiteralNode(node.type.members[0].type)) {
31
+ return node.type.members[0].type?.members.flatMap((member) => {
32
+ if (!isPropertySignature(member) || !member.type) {
33
+ return [];
34
+ }
35
+ return {
36
+ name: member.name?.getText(),
37
+ schema: getSchemaFromTypeNode(member.type, program, fileNameResolver),
38
+ in: 'path',
39
+ };
40
+ });
65
41
  }
66
- });
67
- return path;
42
+ return [];
68
43
  };
69
- export {
70
- getPathSchema
44
+ /**
45
+ * Traverse the typescript file and extract as much info as we can for the openapi spec
46
+ */
47
+ export const getPathSchema = (sourceFile, program) => {
48
+ const path = {};
49
+ const typeChecker = program.getTypeChecker();
50
+ sourceFile.statements.forEach((statement) => {
51
+ // Function
52
+ if (isFunctionDeclaration(statement) && statement.name) {
53
+ const method = checkForMethod(statement.name);
54
+ if (method) {
55
+ const { title, description } = getJSDocFromNode(statement);
56
+ const parameters = extractPathParams(statement.parameters[1], program);
57
+ const responses = generateResponses(statement.body, typeChecker);
58
+ path[method] = {
59
+ summary: title,
60
+ description,
61
+ parameters,
62
+ responses,
63
+ };
64
+ }
65
+ }
66
+ // TODO: variables
67
+ else if (isVariableStatement(statement)) {
68
+ // TODO: Remove this typecast. It looks totally incompatible
69
+ const method = checkForMethod(statement.declarationList.declarations[0]?.name);
70
+ if (method) {
71
+ const { title, description } = getJSDocFromNode(statement);
72
+ const responses = generateResponses(statement, typeChecker);
73
+ path[method] = {
74
+ summary: title,
75
+ description,
76
+ responses,
77
+ };
78
+ }
79
+ }
80
+ });
81
+ return path;
71
82
  };
72
- //# sourceMappingURL=path.js.map
package/package.json CHANGED
@@ -16,7 +16,7 @@
16
16
  "scalar",
17
17
  "references"
18
18
  ],
19
- "version": "0.3.2",
19
+ "version": "0.3.4",
20
20
  "engines": {
21
21
  "node": ">=22"
22
22
  },
@@ -34,24 +34,20 @@
34
34
  ],
35
35
  "dependencies": {
36
36
  "fast-glob": "^3.3.3",
37
- "@scalar/ts-to-openapi": "0.2.0",
38
- "@scalar/nextjs-api-reference": "0.10.2",
39
- "@scalar/types": "0.7.2"
37
+ "@scalar/nextjs-api-reference": "0.10.4",
38
+ "@scalar/ts-to-openapi": "0.2.1",
39
+ "@scalar/types": "0.7.4"
40
40
  },
41
41
  "devDependencies": {
42
42
  "@types/node": "^24.1.0",
43
43
  "@types/react": "^19.2.7",
44
44
  "@types/react-dom": "^19.2.3",
45
45
  "next": "^15.5.10",
46
- "openapi-types": "^12.1.3",
47
- "@scalar/build-tooling": "0.5.0"
46
+ "openapi-types": "^12.1.3"
48
47
  },
49
48
  "scripts": {
50
- "build": "scalar-build-esbuild",
49
+ "build": "tsc -p tsconfig.build.json && tsc-alias -p tsconfig.build.json",
51
50
  "dev": "next dev playground -p 5066",
52
- "lint:check": "scalar-lint-check",
53
- "lint:fix": "scalar-lint-fix",
54
- "types:build": "scalar-types-build",
55
- "types:check": "scalar-types-check"
51
+ "types:check": "tsc --noEmit"
56
52
  }
57
53
  }
package/dist/index.js.map DELETED
@@ -1,7 +0,0 @@
1
- {
2
- "version": 3,
3
- "sources": ["../src/index.ts"],
4
- "sourcesContent": ["export type { OpenAPIConfig } from './openapi'\nexport { OpenAPI } from './openapi'\n"],
5
- "mappings": "AACA,SAAS,eAAe;",
6
- "names": []
7
- }
@@ -1,7 +0,0 @@
1
- {
2
- "version": 3,
3
- "sources": ["../src/openapi.ts"],
4
- "sourcesContent": ["import { readFileSync } from 'node:fs'\n\nimport { ApiReference } from '@scalar/nextjs-api-reference'\nimport type { ApiReferenceConfiguration } from '@scalar/types/api-reference'\nimport { sync } from 'fast-glob'\nimport type { NextRequest } from 'next/server'\nimport type { OpenAPIV3_1 } from 'openapi-types'\nimport {\n type CompilerHost,\n JSDocParsingMode,\n ScriptKind,\n ScriptTarget,\n createProgram,\n createSourceFile,\n} from 'typescript'\n\nimport { getPathSchema } from './path'\n\n/**\n * Scalar OpenAPI config\n */\nexport type OpenAPIConfig = {\n /**\n * The base directory for your API files\n * @default 'app/api'\n */\n apiDirectory?: string\n} & Partial<ApiReferenceConfiguration>\n\n// TODO switch to watcher\n// @see https://github.com/microsoft/TypeScript-wiki/blob/main/Using-the-Compiler-API.md#writing-an-incremental-program-watcher\nconst compilerHost: CompilerHost = {\n fileExists: () => true,\n getCanonicalFileName: (filename) => filename,\n getCurrentDirectory: () => '',\n getDefaultLibFileName: () => '',\n getNewLine: () => '\\n',\n getSourceFile: (filename) =>\n createSourceFile(filename, readFileSync(filename).toString(), ScriptTarget.Latest, false, ScriptKind.TS),\n jsDocParsingMode: JSDocParsingMode.ParseAll,\n readFile: () => undefined,\n useCaseSensitiveFileNames: () => true,\n writeFile: () => null,\n}\n\n/** OpenAPI 3.1.0 Spec */\nconst spec: OpenAPIV3_1.Document = {\n openapi: '3.1.0',\n info: {\n title: `${process.env.npm_package_name} - Next.js OpenAPI Spec by Scalar`,\n description:\n process.env.npm_package_description ||\n 'This file has been autogenerated, check out the docs to see how to customize these options',\n version: process.env.npm_package_version || '0.0.0',\n },\n paths: {},\n}\n\n/**\n * Scalar Next.js OpenAPI schema generation\n *\n * Handles both the schema generation as well as the references\n *\n * TODO:\n * - Docs\n * - file watcher\n * - caching\n */\nexport const OpenAPI = (config: OpenAPIConfig = {}) => {\n const apiDirectory = config.apiDirectory ?? 'app/api'\n /** All ts files required for the schema */\n const programFileNames = sync(apiDirectory + '/**/*.ts')\n /** Specific route files to generate paths */\n const routeFileNames = sync(apiDirectory + '/**/route.ts')\n\n const program = createProgram(\n programFileNames,\n {\n noResolve: true,\n target: ScriptTarget.Latest,\n },\n compilerHost,\n )\n\n // Loop over all non scalar files to extract openapi data using AST\n routeFileNames.forEach((fileName) => {\n const sourceFile = program.getSourceFile(fileName)\n\n if (sourceFile) {\n const resp = getPathSchema(sourceFile, program)\n\n // Grab the path from the fileName\n const rawPath = fileName\n .replace(/^(?:src\\/)?app|\\/route\\.ts$/g, '')\n .replace(/\\[/g, '{')\n .replace(/\\/\\(\\w+\\)/g, '')\n .replace(/]/g, '}')\n const path = rawPath.startsWith('/') ? rawPath : '/' + rawPath\n\n spec.paths[path] = resp\n }\n })\n\n return {\n GET: async (req: NextRequest) => {\n // OpenAPI Schema JSON\n if (req.nextUrl.pathname.endsWith('.json')) {\n // Just grabbing the origin here but we can be smarter and look for the greatest common few segments\n spec.servers = [{ url: req.nextUrl.origin }]\n\n return Response.json(spec)\n }\n // References\n\n return await ApiReference({\n url: req.nextUrl.pathname + '/schema.json',\n })()\n },\n }\n}\n"],
5
- "mappings": "AAAA,SAAS,oBAAoB;AAE7B,SAAS,oBAAoB;AAE7B,SAAS,YAAY;AAGrB;AAAA,EAEE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AAEP,SAAS,qBAAqB;AAe9B,MAAM,eAA6B;AAAA,EACjC,YAAY,MAAM;AAAA,EAClB,sBAAsB,CAAC,aAAa;AAAA,EACpC,qBAAqB,MAAM;AAAA,EAC3B,uBAAuB,MAAM;AAAA,EAC7B,YAAY,MAAM;AAAA,EAClB,eAAe,CAAC,aACd,iBAAiB,UAAU,aAAa,QAAQ,EAAE,SAAS,GAAG,aAAa,QAAQ,OAAO,WAAW,EAAE;AAAA,EACzG,kBAAkB,iBAAiB;AAAA,EACnC,UAAU,MAAM;AAAA,EAChB,2BAA2B,MAAM;AAAA,EACjC,WAAW,MAAM;AACnB;AAGA,MAAM,OAA6B;AAAA,EACjC,SAAS;AAAA,EACT,MAAM;AAAA,IACJ,OAAO,GAAG,QAAQ,IAAI,gBAAgB;AAAA,IACtC,aACE,QAAQ,IAAI,2BACZ;AAAA,IACF,SAAS,QAAQ,IAAI,uBAAuB;AAAA,EAC9C;AAAA,EACA,OAAO,CAAC;AACV;AAYO,MAAM,UAAU,CAAC,SAAwB,CAAC,MAAM;AACrD,QAAM,eAAe,OAAO,gBAAgB;AAE5C,QAAM,mBAAmB,KAAK,eAAe,UAAU;AAEvD,QAAM,iBAAiB,KAAK,eAAe,cAAc;AAEzD,QAAM,UAAU;AAAA,IACd;AAAA,IACA;AAAA,MACE,WAAW;AAAA,MACX,QAAQ,aAAa;AAAA,IACvB;AAAA,IACA;AAAA,EACF;AAGA,iBAAe,QAAQ,CAAC,aAAa;AACnC,UAAM,aAAa,QAAQ,cAAc,QAAQ;AAEjD,QAAI,YAAY;AACd,YAAM,OAAO,cAAc,YAAY,OAAO;AAG9C,YAAM,UAAU,SACb,QAAQ,gCAAgC,EAAE,EAC1C,QAAQ,OAAO,GAAG,EAClB,QAAQ,cAAc,EAAE,EACxB,QAAQ,MAAM,GAAG;AACpB,YAAM,OAAO,QAAQ,WAAW,GAAG,IAAI,UAAU,MAAM;AAEvD,WAAK,MAAM,IAAI,IAAI;AAAA,IACrB;AAAA,EACF,CAAC;AAED,SAAO;AAAA,IACL,KAAK,OAAO,QAAqB;AAE/B,UAAI,IAAI,QAAQ,SAAS,SAAS,OAAO,GAAG;AAE1C,aAAK,UAAU,CAAC,EAAE,KAAK,IAAI,QAAQ,OAAO,CAAC;AAE3C,eAAO,SAAS,KAAK,IAAI;AAAA,MAC3B;AAGA,aAAO,MAAM,aAAa;AAAA,QACxB,KAAK,IAAI,QAAQ,WAAW;AAAA,MAC9B,CAAC,EAAE;AAAA,IACL;AAAA,EACF;AACF;",
6
- "names": []
7
- }
package/dist/path.js.map DELETED
@@ -1,7 +0,0 @@
1
- {
2
- "version": 3,
3
- "sources": ["../src/path.ts"],
4
- "sourcesContent": ["import { extname, join } from 'node:path'\n\nimport { generateResponses, getJSDocFromNode, getSchemaFromTypeNode } from '@scalar/ts-to-openapi'\nimport type { OpenAPIV3_1 } from 'openapi-types'\nimport {\n type Identifier,\n type ParameterDeclaration,\n type Program,\n type SourceFile,\n isFunctionDeclaration,\n isIdentifier,\n isParameter,\n isPropertySignature,\n isTypeLiteralNode,\n isVariableStatement,\n} from 'typescript'\n\n/** Check if identifier is a supported http method */\nconst checkForMethod = (identifier: Pick<Identifier, 'escapedText'> | undefined): OpenAPIV3_1.HttpMethods | null => {\n const method = identifier?.escapedText?.toLowerCase()\n\n return method?.match(/^(get|post|put|patch|delete|head|options)$/) ? (method as OpenAPIV3_1.HttpMethods) : null\n}\n\nconst fileNameResolver = (source: string, target: string): string => {\n const sourceExt = extname(source)\n const targetExt = extname(target)\n\n const targetRelative = target + (targetExt ? '' : sourceExt)\n const targetPath = join(source.replace(/\\/([^/]+)$/, ''), targetRelative)\n\n return targetPath\n}\n\n/**\n * Takes a parameter node and returns a path parameter schema\n */\nconst extractPathParams = (node: ParameterDeclaration | undefined, program: Program): OpenAPIV3_1.ParameterObject[] => {\n // Traverse to the params with type guards\n if (\n node &&\n isParameter(node) &&\n node.type &&\n isTypeLiteralNode(node.type) &&\n node.type.members[0] &&\n isPropertySignature(node.type.members[0]) &&\n isIdentifier(node.type.members[0].name) &&\n node.type.members[0].name.escapedText === 'params' &&\n node.type.members[0].type &&\n isTypeLiteralNode(node.type.members[0].type)\n ) {\n return node.type.members[0].type?.members.flatMap((member) => {\n if (!isPropertySignature(member) || !member.type) {\n return []\n }\n\n return {\n name: member.name?.getText(),\n schema: getSchemaFromTypeNode(member.type, program, fileNameResolver),\n in: 'path',\n } as OpenAPIV3_1.ParameterObject\n })\n }\n\n return []\n}\n\n/**\n * Traverse the typescript file and extract as much info as we can for the openapi spec\n */\nexport const getPathSchema = (sourceFile: SourceFile, program: Program): OpenAPIV3_1.PathsObject => {\n const path: OpenAPIV3_1.PathsObject = {}\n const typeChecker = program.getTypeChecker()\n\n sourceFile.statements.forEach((statement) => {\n // Function\n if (isFunctionDeclaration(statement) && statement.name) {\n const method = checkForMethod(statement.name)\n if (method) {\n const { title, description } = getJSDocFromNode(statement)\n const parameters = extractPathParams(statement.parameters[1], program)\n const responses = generateResponses(statement.body, typeChecker)\n\n path[method] = {\n summary: title,\n description,\n parameters,\n responses,\n } as OpenAPIV3_1.OperationObject\n }\n }\n\n // TODO: variables\n else if (isVariableStatement(statement)) {\n // TODO: Remove this typecast. It looks totally incompatible\n const method = checkForMethod(statement.declarationList.declarations[0]?.name as Identifier)\n if (method) {\n const { title, description } = getJSDocFromNode(statement)\n const responses = generateResponses(statement, typeChecker)\n path[method] = {\n summary: title,\n description,\n responses,\n } as OpenAPIV3_1.OperationObject\n }\n }\n })\n\n return path\n}\n"],
5
- "mappings": "AAAA,SAAS,SAAS,YAAY;AAE9B,SAAS,mBAAmB,kBAAkB,6BAA6B;AAE3E;AAAA,EAKE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AAGP,MAAM,iBAAiB,CAAC,eAA4F;AAClH,QAAM,SAAS,YAAY,aAAa,YAAY;AAEpD,SAAO,QAAQ,MAAM,4CAA4C,IAAK,SAAqC;AAC7G;AAEA,MAAM,mBAAmB,CAAC,QAAgB,WAA2B;AACnE,QAAM,YAAY,QAAQ,MAAM;AAChC,QAAM,YAAY,QAAQ,MAAM;AAEhC,QAAM,iBAAiB,UAAU,YAAY,KAAK;AAClD,QAAM,aAAa,KAAK,OAAO,QAAQ,cAAc,EAAE,GAAG,cAAc;AAExE,SAAO;AACT;AAKA,MAAM,oBAAoB,CAAC,MAAwC,YAAoD;AAErH,MACE,QACA,YAAY,IAAI,KAChB,KAAK,QACL,kBAAkB,KAAK,IAAI,KAC3B,KAAK,KAAK,QAAQ,CAAC,KACnB,oBAAoB,KAAK,KAAK,QAAQ,CAAC,CAAC,KACxC,aAAa,KAAK,KAAK,QAAQ,CAAC,EAAE,IAAI,KACtC,KAAK,KAAK,QAAQ,CAAC,EAAE,KAAK,gBAAgB,YAC1C,KAAK,KAAK,QAAQ,CAAC,EAAE,QACrB,kBAAkB,KAAK,KAAK,QAAQ,CAAC,EAAE,IAAI,GAC3C;AACA,WAAO,KAAK,KAAK,QAAQ,CAAC,EAAE,MAAM,QAAQ,QAAQ,CAAC,WAAW;AAC5D,UAAI,CAAC,oBAAoB,MAAM,KAAK,CAAC,OAAO,MAAM;AAChD,eAAO,CAAC;AAAA,MACV;AAEA,aAAO;AAAA,QACL,MAAM,OAAO,MAAM,QAAQ;AAAA,QAC3B,QAAQ,sBAAsB,OAAO,MAAM,SAAS,gBAAgB;AAAA,QACpE,IAAI;AAAA,MACN;AAAA,IACF,CAAC;AAAA,EACH;AAEA,SAAO,CAAC;AACV;AAKO,MAAM,gBAAgB,CAAC,YAAwB,YAA8C;AAClG,QAAM,OAAgC,CAAC;AACvC,QAAM,cAAc,QAAQ,eAAe;AAE3C,aAAW,WAAW,QAAQ,CAAC,cAAc;AAE3C,QAAI,sBAAsB,SAAS,KAAK,UAAU,MAAM;AACtD,YAAM,SAAS,eAAe,UAAU,IAAI;AAC5C,UAAI,QAAQ;AACV,cAAM,EAAE,OAAO,YAAY,IAAI,iBAAiB,SAAS;AACzD,cAAM,aAAa,kBAAkB,UAAU,WAAW,CAAC,GAAG,OAAO;AACrE,cAAM,YAAY,kBAAkB,UAAU,MAAM,WAAW;AAE/D,aAAK,MAAM,IAAI;AAAA,UACb,SAAS;AAAA,UACT;AAAA,UACA;AAAA,UACA;AAAA,QACF;AAAA,MACF;AAAA,IACF,WAGS,oBAAoB,SAAS,GAAG;AAEvC,YAAM,SAAS,eAAe,UAAU,gBAAgB,aAAa,CAAC,GAAG,IAAkB;AAC3F,UAAI,QAAQ;AACV,cAAM,EAAE,OAAO,YAAY,IAAI,iBAAiB,SAAS;AACzD,cAAM,YAAY,kBAAkB,WAAW,WAAW;AAC1D,aAAK,MAAM,IAAI;AAAA,UACb,SAAS;AAAA,UACT;AAAA,UACA;AAAA,QACF;AAAA,MACF;AAAA,IACF;AAAA,EACF,CAAC;AAED,SAAO;AACT;",
6
- "names": []
7
- }