@scalar/nextjs-openapi 0.3.3 → 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 +6 -0
- package/dist/index.js +1 -5
- package/dist/openapi.js +74 -63
- package/dist/path.js +74 -64
- package/package.json +7 -11
- package/dist/index.js.map +0 -7
- package/dist/openapi.js.map +0 -7
- package/dist/path.js.map +0 -7
package/CHANGELOG.md
CHANGED
package/dist/index.js
CHANGED
package/dist/openapi.js
CHANGED
|
@@ -1,69 +1,80 @@
|
|
|
1
|
-
import { readFileSync } from
|
|
2
|
-
import { ApiReference } from
|
|
3
|
-
import { sync } from
|
|
4
|
-
import {
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
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
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
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
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
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
|
-
|
|
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
|
-
|
|
67
|
-
|
|
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
|
|
2
|
-
import { generateResponses, getJSDocFromNode, getSchemaFromTypeNode } from
|
|
3
|
-
import {
|
|
4
|
-
|
|
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
|
-
|
|
13
|
-
|
|
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
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
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
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
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
|
-
|
|
70
|
-
|
|
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.
|
|
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/nextjs-api-reference": "0.10.
|
|
38
|
-
"@scalar/ts-to-openapi": "0.2.
|
|
39
|
-
"@scalar/types": "0.7.
|
|
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": "
|
|
49
|
+
"build": "tsc -p tsconfig.build.json && tsc-alias -p tsconfig.build.json",
|
|
51
50
|
"dev": "next dev playground -p 5066",
|
|
52
|
-
"
|
|
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
package/dist/openapi.js.map
DELETED
|
@@ -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
|
-
}
|