@spec2ts/openapi 3.1.3 → 4.0.1

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/README.md CHANGED
@@ -8,15 +8,16 @@
8
8
 
9
9
  ## Features
10
10
 
11
- * **AST-based:** Unlike other code generators `@spec2ts/openapi` does not use templates to generate code but uses TypeScript's built-in API to generate and pretty-print an abstract syntax tree.
12
- * **Tree-shakeable:** Individually exported types allows you to bundle only the ones you actually use.
13
- * **YAML or JSON:** Use YAML or JSON for your OpenAPI v3 specification.
14
- * **External references:** Resolves automatically external references and bundle or import them in generated files.
15
- * **Implementation agnostic:** Use generated types in any projet or framework.
11
+ - **AST-based:** Unlike other code generators `@spec2ts/openapi` does not use templates to generate code but uses TypeScript's built-in API to generate and pretty-print an abstract syntax tree.
12
+ - **Tree-shakeable:** Individually exported types allows you to bundle only the ones you actually use.
13
+ - **YAML or JSON:** Use YAML or JSON for your OpenAPI v3 specification.
14
+ - **External references:** Resolves automatically external references and bundle or import them in generated files.
15
+ - **Implementation agnostic:** Use generated types in any projet or framework.
16
16
 
17
17
  ## Installation
18
18
 
19
19
  Install in your project:
20
+
20
21
  ```bash
21
22
  npm install @spec2ts/openapi
22
23
  ```
@@ -56,32 +57,34 @@ async function generateSpec(path: string): Promise<string> {
56
57
  ## Implementations
57
58
 
58
59
  - [x] Types for parameters:
59
- - [x] path
60
- - [x] header
61
- - [x] query
62
- - [x] cookie
60
+ - [x] path
61
+ - [x] header
62
+ - [x] query
63
+ - [x] cookie
63
64
  - [x] Types for requestBody
64
65
  - [x] Types for responses
65
66
  - [x] Automatic naming
66
- - [x] From operationId
67
- - [x] From path
67
+ - [x] From operationId
68
+ - [x] From path
68
69
  - [x] Parameters merging
69
- - [x] From path item
70
- - [x] From operation
71
- - [x] Override from operation
70
+ - [x] From path item
71
+ - [x] From operation
72
+ - [x] Override from operation
72
73
  - [x] [Schema references](http://json-schema.org/latest/json-schema-core.html#rfc.section.7.2.2)
73
- - [x] Local (filesystem) schema references
74
- - [x] External (network) schema references
74
+ - [x] Local (filesystem) schema references
75
+ - [x] External (network) schema references
75
76
  - [x] Modular architecture
76
- - [x] Import local references
77
- - [x] Embed external references
77
+ - [x] Import local references
78
+ - [x] Embed external references
78
79
 
79
80
  ## Compatibility Matrix
80
81
 
81
82
  | TypeScript version | spec2ts version |
82
- |--------------------|-----------------|
83
- | v3.x.x | v1 |
84
- | v4.x.x | v2 |
83
+ | ------------------ | --------------- |
84
+ | v3.x.x | v1 |
85
+ | v4.x.x | v2 |
86
+ | v5.x.x | v3 |
87
+ | v6.x.x | v4 |
85
88
 
86
89
  ## License
87
90
 
@@ -0,0 +1 @@
1
+ export { };
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env node
2
+ import { i as usage, n as describe, r as handler, t as builder } from "../command-BDMCeG0A.mjs";
3
+ import yargs from "yargs";
4
+ import { hideBin } from "yargs/helpers";
5
+ //#region src/bin/oapi2ts.ts
6
+ yargs(hideBin(process.argv)).command(usage, describe, builder, handler).help("help", "Show help usage").demandCommand().argv;
7
+ //#endregion
8
+ export {};
@@ -0,0 +1,15 @@
1
+ import { t as ParseOpenApiOptions } from "../openapi-parser-BJ3RQyRq.mjs";
2
+ import { Argv } from "yargs";
3
+ //#region src/cli/command.d.ts
4
+ interface BuildTsFromOpenApiOptions extends ParseOpenApiOptions {
5
+ input: string | string[];
6
+ output?: string;
7
+ ext?: string;
8
+ banner?: string;
9
+ }
10
+ declare const usage = "$0 <input..>";
11
+ declare const describe = "Generate TypeScript types from OpenAPI specification";
12
+ declare function builder(argv: Argv): Argv<BuildTsFromOpenApiOptions>;
13
+ declare function handler(options: BuildTsFromOpenApiOptions): Promise<void>;
14
+ //#endregion
15
+ export { BuildTsFromOpenApiOptions, builder, describe, handler, usage };
@@ -0,0 +1,2 @@
1
+ import { i as usage, n as describe, r as handler, t as builder } from "../command-BDMCeG0A.mjs";
2
+ export { builder, describe, handler, usage };
@@ -0,0 +1,64 @@
1
+ import { n as parseOpenApiFile } from "./openapi-parser-3wx-i5_G.mjs";
2
+ import { cli, printer } from "@spec2ts/core";
3
+ //#region src/cli/command.ts
4
+ const usage = "$0 <input..>";
5
+ const describe = "Generate TypeScript types from OpenAPI specification";
6
+ function builder(argv) {
7
+ return argv.positional("input", {
8
+ array: true,
9
+ type: "string",
10
+ describe: "Path to OpenAPI Specification(s) to convert to TypeScript",
11
+ demandOption: true
12
+ }).option("output", {
13
+ type: "string",
14
+ alias: "o",
15
+ describe: "Output directory for generated types"
16
+ }).option("ext", {
17
+ type: "string",
18
+ alias: "e",
19
+ describe: "Output extension for generated types",
20
+ choices: [".d.ts", ".ts"]
21
+ }).option("cwd", {
22
+ type: "string",
23
+ alias: "c",
24
+ describe: "Root directory for resolving $refs"
25
+ }).option("avoidAny", {
26
+ type: "boolean",
27
+ describe: "Avoid the `any` type and use `unknown` instead"
28
+ }).option("enableDate", {
29
+ choices: [
30
+ true,
31
+ "strict",
32
+ "lax"
33
+ ],
34
+ describe: "Build `Date` for format `date` and `date-time`"
35
+ }).option("lowerHeaders", {
36
+ type: "boolean",
37
+ describe: "Lowercase headers keys to match Node.js standard"
38
+ }).option("banner", {
39
+ type: "string",
40
+ alias: "b",
41
+ describe: "Comment prepended to the top of each generated file"
42
+ });
43
+ }
44
+ async function handler(options) {
45
+ const files = await cli.findFiles(options.input);
46
+ for (const file of files) {
47
+ const ast = await parseOpenApiFile(file, options);
48
+ const content = printer.printNodes(ast.all);
49
+ const output = cli.getOutputPath(file, options);
50
+ await cli.mkdirp(output);
51
+ await cli.writeFile(output, (options.banner || defaultBanner()) + "\n\n" + content);
52
+ }
53
+ }
54
+ function defaultBanner() {
55
+ return `/**
56
+ * DO NOT MODIFY
57
+ * Generated using @spec2ts/openapi.
58
+ * See https://www.npmjs.com/package/@spec2ts/openapi
59
+ */
60
+
61
+ /* eslint-disable */`;
62
+ }
63
+ //#endregion
64
+ export { usage as i, describe as n, handler as r, builder as t };
@@ -0,0 +1,2 @@
1
+ import { _ as parseParameters, a as ParamType, c as createOpenApiResult, d as getOperationName, f as getParamType, g as parseOperation, h as getSchemaFromContent, i as OApiParserContext, l as getContentDeclaration, m as getResponseName, n as parseOpenApi, o as ParseOpenApiResult, p as getPathName, r as parseOpenApiFile, s as addToOpenApiResult, t as ParseOpenApiOptions, u as getOperationIdentifier, v as parsePathItem, y as parseReference } from "./openapi-parser-BJ3RQyRq.mjs";
2
+ export { OApiParserContext, ParamType, ParseOpenApiOptions, ParseOpenApiResult, addToOpenApiResult, createOpenApiResult, getContentDeclaration, getOperationIdentifier, getOperationName, getParamType, getPathName, getResponseName, getSchemaFromContent, parseOpenApi, parseOpenApiFile, parseOperation, parseParameters, parsePathItem, parseReference };
package/dist/index.mjs ADDED
@@ -0,0 +1,2 @@
1
+ import { a as getContentDeclaration, c as getParamType, d as getSchemaFromContent, f as parseOperation, h as parseReference, i as createOpenApiResult, l as getPathName, m as parsePathItem, n as parseOpenApiFile, o as getOperationIdentifier, p as parseParameters, r as addToOpenApiResult, s as getOperationName, t as parseOpenApi, u as getResponseName } from "./openapi-parser-3wx-i5_G.mjs";
2
+ export { addToOpenApiResult, createOpenApiResult, getContentDeclaration, getOperationIdentifier, getOperationName, getParamType, getPathName, getResponseName, getSchemaFromContent, parseOpenApi, parseOpenApiFile, parseOperation, parseParameters, parsePathItem, parseReference };
@@ -0,0 +1,184 @@
1
+ import * as core from "@spec2ts/core";
2
+ import { createContext, createRefContext, getTypeFromProperties, getTypeFromSchema, pascalCase, resolveReference } from "@spec2ts/jsonschema";
3
+ import ts from "typescript";
4
+ import $RefParser from "@apidevtools/json-schema-ref-parser";
5
+ import path from "node:path";
6
+ //#region src/lib/core-parser.ts
7
+ const VERBS = [
8
+ "GET",
9
+ "PUT",
10
+ "POST",
11
+ "DELETE",
12
+ "OPTIONS",
13
+ "HEAD",
14
+ "PATCH",
15
+ "TRACE"
16
+ ];
17
+ function parsePathItem(path, item, context, result) {
18
+ const baseParams = item.parameters && parseParameters(getPathName(path, context), item.parameters, void 0, context, result);
19
+ Object.entries(item).filter(([verb]) => VERBS.includes(verb.toUpperCase())).forEach(([verb, entry]) => parseOperation(path, verb, entry, baseParams, context, result));
20
+ }
21
+ function parseOperation(path, verb, operation, baseParams, context, result) {
22
+ const name = getOperationName(verb, path, operation.operationId, context);
23
+ if (operation.parameters) parseParameters(name, operation.parameters, baseParams, context, result);
24
+ if (operation.requestBody) {
25
+ const requestBody = resolveReference(operation.requestBody, context);
26
+ const decla = getContentDeclaration(name + "Body", requestBody.content, context);
27
+ if (decla) addToOpenApiResult(result, "body", decla);
28
+ }
29
+ if (operation.responses) {
30
+ const responses = resolveReference(operation.responses, context);
31
+ Object.entries(responses).forEach(([status, responseObj]) => {
32
+ const response = resolveReference(responseObj, context);
33
+ const decla = getContentDeclaration(getResponseName(name, status, context), response.content, context);
34
+ if (decla) addToOpenApiResult(result, "responses", decla);
35
+ });
36
+ }
37
+ }
38
+ function parseParameters(baseName, data, baseParams = {}, context, result) {
39
+ const params = [];
40
+ const query = [];
41
+ const headers = [];
42
+ const cookie = [];
43
+ const res = {};
44
+ data.forEach((item) => {
45
+ item = resolveReference(item, context);
46
+ switch (item.in) {
47
+ case "path":
48
+ params.push(item);
49
+ break;
50
+ case "header":
51
+ headers.push(item);
52
+ break;
53
+ case "query":
54
+ query.push(item);
55
+ break;
56
+ case "cookie":
57
+ cookie.push(item);
58
+ break;
59
+ }
60
+ });
61
+ addParams(params, "params");
62
+ addParams(headers, "headers");
63
+ addParams(query, "query");
64
+ addParams(cookie, "cookie");
65
+ return res;
66
+ function addParams(params, paramType) {
67
+ if (!params.length) return;
68
+ const name = baseName + pascalCase(paramType);
69
+ const type = getParamType(paramType, params, baseParams[paramType], context);
70
+ addToOpenApiResult(result, paramType, core.createTypeOrInterfaceDeclaration({
71
+ modifiers: [core.modifier.export],
72
+ name,
73
+ type
74
+ }));
75
+ res[paramType] = ts.factory.createTypeReferenceNode(name, void 0);
76
+ }
77
+ }
78
+ function parseReference(ref, context) {
79
+ const type = getTypeFromSchema(ref.schema, createRefContext(ref, context));
80
+ context.aliases.push(core.createTypeOrInterfaceDeclaration({
81
+ modifiers: [core.modifier.export],
82
+ name: ref.name,
83
+ type
84
+ }));
85
+ }
86
+ function getContentDeclaration(name, content, context) {
87
+ if (!content) return;
88
+ content = resolveReference(content, context);
89
+ const schema = getSchemaFromContent(content);
90
+ if (!schema) return;
91
+ const type = getTypeFromSchema(schema, context);
92
+ return core.createTypeOrInterfaceDeclaration({
93
+ modifiers: [core.modifier.export],
94
+ name,
95
+ type
96
+ });
97
+ }
98
+ function getParamType(paramType, data, baseType, context) {
99
+ const required = [];
100
+ const props = {};
101
+ data.forEach((m) => {
102
+ let name = m.name;
103
+ if (paramType === "headers" && context.options.lowerHeaders) name = name.toLowerCase();
104
+ props[name] = m.schema || {};
105
+ if (m.required) required.push(name);
106
+ });
107
+ const type = getTypeFromProperties(props, required, false, paramType === "query" && typeof context.options.enableDateForQueryParams !== "undefined" ? {
108
+ ...context,
109
+ options: {
110
+ ...context.options,
111
+ enableDate: context.options.enableDateForQueryParams
112
+ }
113
+ } : context);
114
+ if (baseType) return ts.factory.createIntersectionTypeNode([baseType, type]);
115
+ return type;
116
+ }
117
+ function getSchemaFromContent(content) {
118
+ return content?.["application/json"]?.schema || content?.["application/x-www-form-urlencoded"]?.schema || content?.["multipart/form-data"]?.schema || content?.["*/*"]?.schema;
119
+ }
120
+ function getResponseName(operationName, statusCode, context) {
121
+ let name = operationName + "Response";
122
+ const status = parseInt(statusCode);
123
+ if (status >= 200 && status < 300) {
124
+ if ((context.names[name] = (context.names[name] || 0) + 1) > 1) name += statusCode;
125
+ } else if (!isNaN(status)) name += statusCode;
126
+ else name += pascalCase(statusCode);
127
+ return name;
128
+ }
129
+ function getOperationName(verb, path, operationId, context) {
130
+ const id = getOperationIdentifier(operationId);
131
+ if (id) return id;
132
+ return getPathName(`${verb} ${path}`, context);
133
+ }
134
+ function getPathName(path, context) {
135
+ path = path.replace(/\{(.+?)\}/, "by $1").replace(/\{(.+?)\}/g, "and $1");
136
+ let name = pascalCase(path);
137
+ const count = context.names[name] = (context.names[name] || 0) + 1;
138
+ if (count > 1) name += count;
139
+ return name;
140
+ }
141
+ function getOperationIdentifier(id) {
142
+ if (!id) return;
143
+ if (id.match(/[^\w\s]/)) return;
144
+ id = pascalCase(id);
145
+ if (core.isValidIdentifier(id)) return id;
146
+ }
147
+ function addToOpenApiResult(result, prop, statement) {
148
+ const statements = Array.isArray(statement) ? statement : [statement];
149
+ result[prop].push(...statements);
150
+ result.all.push(...statements);
151
+ }
152
+ function createOpenApiResult() {
153
+ return {
154
+ params: [],
155
+ query: [],
156
+ headers: [],
157
+ body: [],
158
+ responses: [],
159
+ models: [],
160
+ cookie: [],
161
+ import: [],
162
+ all: []
163
+ };
164
+ }
165
+ //#endregion
166
+ //#region src/lib/openapi-parser.ts
167
+ async function parseOpenApiFile(file, options = {}) {
168
+ return parseOpenApi(await $RefParser.parse(file), {
169
+ cwd: path.resolve(path.dirname(file)) + "/",
170
+ ...options
171
+ });
172
+ }
173
+ async function parseOpenApi(spec, options = {}) {
174
+ if (!options.parseReference) options.parseReference = parseReference;
175
+ const context = await createContext(spec, options);
176
+ const result = createOpenApiResult();
177
+ Object.entries(spec.paths ?? {}).forEach(([path, item]) => {
178
+ parsePathItem(path, item, context, result);
179
+ });
180
+ addToOpenApiResult(result, "models", context.aliases);
181
+ return result;
182
+ }
183
+ //#endregion
184
+ export { getContentDeclaration as a, getParamType as c, getSchemaFromContent as d, parseOperation as f, parseReference as h, createOpenApiResult as i, getPathName as l, parsePathItem as m, parseOpenApiFile as n, getOperationIdentifier as o, parseParameters as p, addToOpenApiResult as r, getOperationName as s, parseOpenApi as t, getResponseName as u };
@@ -0,0 +1,48 @@
1
+ import { ParsedReference, ParserContext, ParserOptions } from "@spec2ts/jsonschema";
2
+ import ts from "typescript";
3
+ import { ContentObject, OpenAPIObject, OperationObject, ParameterObject, PathItemObject, ReferenceObject, SchemaObject } from "openapi3-ts/oas31";
4
+ //#region src/lib/core-parser.d.ts
5
+ interface ParseOpenApiResult {
6
+ import: ts.Statement[];
7
+ params: ts.Statement[];
8
+ query: ts.Statement[];
9
+ headers: ts.Statement[];
10
+ body: ts.Statement[];
11
+ responses: ts.Statement[];
12
+ models: ts.Statement[];
13
+ cookie: ts.Statement[];
14
+ all: ts.Statement[];
15
+ }
16
+ interface OApiParserContext extends ParserContext {
17
+ options: ParseOpenApiOptions;
18
+ }
19
+ declare function parsePathItem(path: string, item: PathItemObject, context: OApiParserContext, result: ParseOpenApiResult): void;
20
+ declare function parseOperation(path: string, verb: string, operation: OperationObject, baseParams: ParsedParams | undefined, context: OApiParserContext, result: ParseOpenApiResult): void;
21
+ type ParamType = "params" | "headers" | "query" | "cookie";
22
+ declare function parseParameters(baseName: string, data: Array<ReferenceObject | ParameterObject>, baseParams: ParsedParams | undefined, context: OApiParserContext, result: ParseOpenApiResult): ParsedParams;
23
+ declare function parseReference(ref: ParsedReference, context: ParserContext): void;
24
+ declare function getContentDeclaration(name: string, content: ReferenceObject | ContentObject | undefined, context: OApiParserContext): ts.Statement | undefined;
25
+ declare function getParamType(paramType: ParamType, data: ParameterObject[], baseType: ts.TypeReferenceNode | undefined, context: OApiParserContext): ts.TypeNode;
26
+ declare function getSchemaFromContent(content: ContentObject): SchemaObject | ReferenceObject | undefined;
27
+ declare function getResponseName(operationName: string, statusCode: string, context: OApiParserContext): string;
28
+ declare function getOperationName(verb: string, path: string, operationId: string | undefined, context: OApiParserContext): string;
29
+ declare function getPathName(path: string, context: OApiParserContext): string;
30
+ declare function getOperationIdentifier(id?: string): string | void;
31
+ declare function addToOpenApiResult(result: ParseOpenApiResult, prop: keyof ParseOpenApiResult, statement: ts.Statement | ts.Statement[]): void;
32
+ declare function createOpenApiResult(): ParseOpenApiResult;
33
+ interface ParsedParams {
34
+ params?: ts.TypeReferenceNode;
35
+ query?: ts.TypeReferenceNode;
36
+ headers?: ts.TypeReferenceNode;
37
+ cookie?: ts.TypeReferenceNode;
38
+ }
39
+ //#endregion
40
+ //#region src/lib/openapi-parser.d.ts
41
+ interface ParseOpenApiOptions extends ParserOptions {
42
+ lowerHeaders?: boolean;
43
+ enableDateForQueryParams?: boolean | "strict" | "lax";
44
+ }
45
+ declare function parseOpenApiFile(file: string, options?: ParseOpenApiOptions): Promise<ParseOpenApiResult>;
46
+ declare function parseOpenApi(spec: OpenAPIObject, options?: ParseOpenApiOptions): Promise<ParseOpenApiResult>;
47
+ //#endregion
48
+ export { parseParameters as _, ParamType as a, createOpenApiResult as c, getOperationName as d, getParamType as f, parseOperation as g, getSchemaFromContent as h, OApiParserContext as i, getContentDeclaration as l, getResponseName as m, parseOpenApi as n, ParseOpenApiResult as o, getPathName as p, parseOpenApiFile as r, addToOpenApiResult as s, ParseOpenApiOptions as t, getOperationIdentifier as u, parsePathItem as v, parseReference as y };
package/package.json CHANGED
@@ -1,69 +1,77 @@
1
1
  {
2
- "name": "@spec2ts/openapi",
3
- "version": "3.1.3",
4
- "description": "Utility to convert OpenAPI v3 specifications to Typescript using TypeScript native compiler",
5
- "author": "Touchify <dev@touchify.co>",
6
- "license": "MIT",
7
- "main": "index.js",
8
- "homepage": "https://github.com/touchifyapp/spec2ts/blob/master/packages/openapi#readme",
9
- "repository": {
10
- "type": "git",
11
- "url": "https://github.com/touchifyapp/spec2ts"
2
+ "name": "@spec2ts/openapi",
3
+ "version": "4.0.1",
4
+ "description": "Utility to convert OpenAPI v3 specifications to Typescript using TypeScript native compiler",
5
+ "keywords": [
6
+ "ast",
7
+ "compile",
8
+ "compiler",
9
+ "interface",
10
+ "openapi",
11
+ "openapi3",
12
+ "share",
13
+ "spec",
14
+ "spec2ts",
15
+ "specification",
16
+ "transpile",
17
+ "typescript",
18
+ "typing"
19
+ ],
20
+ "homepage": "https://github.com/touchifyapp/spec2ts/blob/master/packages/openapi#readme",
21
+ "license": "MIT",
22
+ "author": "Touchify <dev@touchify.co>",
23
+ "repository": {
24
+ "type": "git",
25
+ "url": "https://github.com/touchifyapp/spec2ts"
26
+ },
27
+ "bin": {
28
+ "oapi2ts": "./dist/bin/oapi2ts.mjs"
29
+ },
30
+ "files": [
31
+ "dist/**/*.mjs",
32
+ "dist/**/*.d.mts"
33
+ ],
34
+ "type": "module",
35
+ "main": "dist/index.mjs",
36
+ "types": "dist/index.d.mts",
37
+ "exports": {
38
+ ".": {
39
+ "import": "./dist/index.mjs",
40
+ "types": "./dist/index.d.mts"
12
41
  },
13
- "publishConfig": {
14
- "access": "public"
42
+ "./cli": {
43
+ "import": "./dist/cli/index.mjs",
44
+ "types": "./dist/cli/index.d.mts"
15
45
  },
16
- "bin": {
17
- "oapi2ts": "./bin/oapi2ts.js"
46
+ "./bin/oapi2ts": {
47
+ "import": "./dist/bin/oapi2ts.mjs",
48
+ "types": "./dist/bin/oapi2ts.d.mts"
18
49
  },
19
- "files": [
20
- "*.js",
21
- "*.d.ts",
22
- "bin/**/*.js",
23
- "cli/**/*.js",
24
- "cli/**/*.d.ts",
25
- "lib/**/*.js",
26
- "lib/**/*.d.ts"
27
- ],
28
- "scripts": {
29
- "build": "npm run clean && npm run lint && npm run build:ts",
30
- "build:ts": "tsc -p .",
31
- "test": "npm run clean && npm run lint && npm run test:jest",
32
- "test:jest": "jest -c ../../jest.config.js --rootDir .",
33
- "test:coverage": "npm run test -- -- --coverage",
34
- "lint": "npm run lint:ts",
35
- "lint:ts": "eslint '*.ts' '{bin,cli,lib}/**/*.ts'",
36
- "lint:fix": "npm run lint -- -- --fix",
37
- "clean": "npm run clean:ts",
38
- "clean:ts": "del '*.{js,d.ts}' '{bin,cli,lib}/**/*.{js,d.ts}'",
39
- "prepublishOnly": "npm test && npm run build"
40
- },
41
- "dependencies": {
42
- "@spec2ts/core": "^3.0.2",
43
- "@spec2ts/jsonschema": "^3.0.5",
44
- "openapi3-ts": "^4.1.2",
45
- "typescript": "^5.0.0",
46
- "yargs": "^17.7.2"
47
- },
48
- "devDependencies": {
49
- "del-cli": "^5.1.0",
50
- "eslint": "^8.49.0",
51
- "jest": "^29.6.4"
52
- },
53
- "keywords": [
54
- "openapi",
55
- "specification",
56
- "openapi3",
57
- "spec",
58
- "typescript",
59
- "compile",
60
- "compiler",
61
- "ast",
62
- "transpile",
63
- "interface",
64
- "typing",
65
- "spec2ts",
66
- "share"
67
- ],
68
- "gitHead": "81f76779f541e0bc2ef8d7051a0121fff07916ae"
50
+ "./package.json": "./package.json"
51
+ },
52
+ "publishConfig": {
53
+ "access": "public"
54
+ },
55
+ "scripts": {
56
+ "build": "npm run lint && npm run build:ts",
57
+ "build:ts": "tsdown",
58
+ "test": "npm run lint && npm run format:check && npm run test:vitest",
59
+ "test:prepublish": "npm run lint && npm run test:vitest",
60
+ "test:vitest": "vitest run -c ../../vitest.config.ts",
61
+ "test:coverage": "npm run test -- -- --coverage",
62
+ "lint": "npm run lint:ts",
63
+ "lint:ts": "oxlint -c ../../oxlint.config.ts",
64
+ "lint:fix": "npm run lint -- -- --fix",
65
+ "format": "oxfmt -c ../../oxfmt.config.ts",
66
+ "format:check": "oxfmt -c ../../oxfmt.config.ts --check",
67
+ "prepublishOnly": "npm run test:prepublish && npm run build"
68
+ },
69
+ "dependencies": {
70
+ "@apidevtools/json-schema-ref-parser": "^10.1.0",
71
+ "@spec2ts/core": "^4.0.1",
72
+ "@spec2ts/jsonschema": "^4.0.1",
73
+ "openapi3-ts": "^4.6.0",
74
+ "typescript": "^6.0.0",
75
+ "yargs": "^18.0.0"
76
+ }
69
77
  }
package/bin/oapi2ts.js DELETED
@@ -1,4 +0,0 @@
1
- #!/usr/bin/env node
2
- "use strict";
3
- Object.defineProperty(exports, "__esModule", { value: true });
4
- require("../cli");
package/cli/command.d.ts DELETED
@@ -1,12 +0,0 @@
1
- import { Argv } from "yargs";
2
- import { ParseOpenApiOptions } from "../lib/openapi-parser";
3
- export interface BuildTsFromOpenApiOptions extends ParseOpenApiOptions {
4
- input: string | string[];
5
- output?: string;
6
- ext?: string;
7
- banner?: string;
8
- }
9
- export declare const usage = "$0 <input..>";
10
- export declare const describe = "Generate TypeScript types from OpenAPI specification";
11
- export declare function builder(argv: Argv): Argv<BuildTsFromOpenApiOptions>;
12
- export declare function handler(options: BuildTsFromOpenApiOptions): Promise<void>;
package/cli/command.js DELETED
@@ -1,72 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.handler = exports.builder = exports.describe = exports.usage = void 0;
4
- const core_1 = require("@spec2ts/core");
5
- const openapi_parser_1 = require("../lib/openapi-parser");
6
- exports.usage = "$0 <input..>";
7
- exports.describe = "Generate TypeScript types from OpenAPI specification";
8
- function builder(argv) {
9
- return argv
10
- .positional("input", {
11
- array: true,
12
- type: "string",
13
- describe: "Path to OpenAPI Specification(s) to convert to TypeScript",
14
- demandOption: true
15
- })
16
- .option("output", {
17
- type: "string",
18
- alias: "o",
19
- describe: "Output directory for generated types"
20
- })
21
- .option("ext", {
22
- type: "string",
23
- alias: "e",
24
- describe: "Output extension for generated types",
25
- choices: [".d.ts", ".ts"]
26
- })
27
- .option("cwd", {
28
- type: "string",
29
- alias: "c",
30
- describe: "Root directory for resolving $refs"
31
- })
32
- .option("avoidAny", {
33
- type: "boolean",
34
- describe: "Avoid the `any` type and use `unknown` instead"
35
- })
36
- .option("enableDate", {
37
- choices: [true, "strict", "lax"],
38
- describe: "Build `Date` for format `date` and `date-time`"
39
- })
40
- .option("lowerHeaders", {
41
- type: "boolean",
42
- describe: "Lowercase headers keys to match Node.js standard"
43
- })
44
- .option("banner", {
45
- type: "string",
46
- alias: "b",
47
- describe: "Comment prepended to the top of each generated file"
48
- });
49
- }
50
- exports.builder = builder;
51
- async function handler(options) {
52
- const files = await core_1.cli.findFiles(options.input);
53
- for (const file of files) {
54
- const ast = await (0, openapi_parser_1.parseOpenApiFile)(file, options);
55
- const content = core_1.printer.printNodes(ast.all);
56
- const output = core_1.cli.getOutputPath(file, options);
57
- await core_1.cli.mkdirp(output);
58
- await core_1.cli.writeFile(output, (options.banner || defaultBanner()) +
59
- "\n\n" +
60
- content);
61
- }
62
- }
63
- exports.handler = handler;
64
- function defaultBanner() {
65
- return `/**
66
- * DO NOT MODIFY
67
- * Generated using @spec2ts/openapi.
68
- * See https://www.npmjs.com/package/@spec2ts/openapi
69
- */
70
-
71
- /* eslint-disable */`;
72
- }
package/cli/index.d.ts DELETED
@@ -1 +0,0 @@
1
- export {};
package/cli/index.js DELETED
@@ -1,9 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- const yargs = require("yargs");
4
- const command_1 = require("../cli/command");
5
- yargs
6
- .command(command_1.usage, command_1.describe, command_1.builder, command_1.handler)
7
- .help("help", "Show help usage")
8
- .demandCommand()
9
- .argv;
package/index.d.ts DELETED
@@ -1 +0,0 @@
1
- export * from "./lib/openapi-parser";
package/index.js DELETED
@@ -1,17 +0,0 @@
1
- "use strict";
2
- var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
- if (k2 === undefined) k2 = k;
4
- var desc = Object.getOwnPropertyDescriptor(m, k);
5
- if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
- desc = { enumerable: true, get: function() { return m[k]; } };
7
- }
8
- Object.defineProperty(o, k2, desc);
9
- }) : (function(o, m, k, k2) {
10
- if (k2 === undefined) k2 = k;
11
- o[k2] = m[k];
12
- }));
13
- var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
- for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
- };
16
- Object.defineProperty(exports, "__esModule", { value: true });
17
- __exportStar(require("./lib/openapi-parser"), exports);
@@ -1,39 +0,0 @@
1
- import * as ts from "typescript";
2
- import type { SchemaObject, ReferenceObject, PathItemObject, OperationObject, ParameterObject, ContentObject } from "openapi3-ts/oas31";
3
- import { ParserContext, ParsedReference } from "@spec2ts/jsonschema/lib/core-parser";
4
- import type { ParseOpenApiOptions } from "./openapi-parser";
5
- export interface ParseOpenApiResult {
6
- import: ts.Statement[];
7
- params: ts.Statement[];
8
- query: ts.Statement[];
9
- headers: ts.Statement[];
10
- body: ts.Statement[];
11
- responses: ts.Statement[];
12
- models: ts.Statement[];
13
- cookie: ts.Statement[];
14
- all: ts.Statement[];
15
- }
16
- export interface OApiParserContext extends ParserContext {
17
- options: ParseOpenApiOptions;
18
- }
19
- export declare function parsePathItem(path: string, item: PathItemObject, context: OApiParserContext, result: ParseOpenApiResult): void;
20
- export declare function parseOperation(path: string, verb: string, operation: OperationObject, baseParams: ParsedParams | undefined, context: OApiParserContext, result: ParseOpenApiResult): void;
21
- export type ParamType = "params" | "headers" | "query" | "cookie";
22
- export declare function parseParameters(baseName: string, data: Array<ReferenceObject | ParameterObject>, baseParams: ParsedParams | undefined, context: OApiParserContext, result: ParseOpenApiResult): ParsedParams;
23
- export declare function parseReference(ref: ParsedReference, context: ParserContext): void;
24
- export declare function getContentDeclaration(name: string, content: ReferenceObject | ContentObject | undefined, context: OApiParserContext): ts.Statement | undefined;
25
- export declare function getParamType(paramType: ParamType, data: ParameterObject[], baseType: ts.TypeReferenceNode | undefined, context: OApiParserContext): ts.TypeNode;
26
- export declare function getSchemaFromContent(content: ContentObject): SchemaObject | ReferenceObject | undefined;
27
- export declare function getResponseName(operationName: string, statusCode: string, context: OApiParserContext): string;
28
- export declare function getOperationName(verb: string, path: string, operationId: string | undefined, context: OApiParserContext): string;
29
- export declare function getPathName(path: string, context: OApiParserContext): string;
30
- export declare function getOperationIdentifier(id?: string): string | void;
31
- export declare function addToOpenApiResult(result: ParseOpenApiResult, prop: keyof ParseOpenApiResult, statement: ts.Statement | ts.Statement[]): void;
32
- export declare function createOpenApiResult(): ParseOpenApiResult;
33
- interface ParsedParams {
34
- params?: ts.TypeReferenceNode;
35
- query?: ts.TypeReferenceNode;
36
- headers?: ts.TypeReferenceNode;
37
- cookie?: ts.TypeReferenceNode;
38
- }
39
- export {};
@@ -1,194 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.createOpenApiResult = exports.addToOpenApiResult = exports.getOperationIdentifier = exports.getPathName = exports.getOperationName = exports.getResponseName = exports.getSchemaFromContent = exports.getParamType = exports.getContentDeclaration = exports.parseReference = exports.parseParameters = exports.parseOperation = exports.parsePathItem = void 0;
4
- const ts = require("typescript");
5
- const core = require("@spec2ts/core");
6
- const core_parser_1 = require("@spec2ts/jsonschema/lib/core-parser");
7
- const VERBS = ["GET", "PUT", "POST", "DELETE", "OPTIONS", "HEAD", "PATCH", "TRACE"];
8
- function parsePathItem(path, item, context, result) {
9
- const baseParams = item.parameters && parseParameters(getPathName(path, context), item.parameters, undefined, context, result);
10
- Object.entries(item)
11
- .filter(([verb,]) => VERBS.includes(verb.toUpperCase()))
12
- .forEach(([verb, entry]) => parseOperation(path, verb, entry, baseParams, context, result));
13
- }
14
- exports.parsePathItem = parsePathItem;
15
- function parseOperation(path, verb, operation, baseParams, context, result) {
16
- const name = getOperationName(verb, path, operation.operationId, context);
17
- if (operation.parameters) {
18
- parseParameters(name, operation.parameters, baseParams, context, result);
19
- }
20
- if (operation.requestBody) {
21
- const requestBody = (0, core_parser_1.resolveReference)(operation.requestBody, context);
22
- const decla = getContentDeclaration(name + "Body", requestBody.content, context);
23
- if (decla) {
24
- addToOpenApiResult(result, "body", decla);
25
- }
26
- }
27
- if (operation.responses) {
28
- const responses = (0, core_parser_1.resolveReference)(operation.responses, context);
29
- Object.entries(responses).forEach(([status, responseObj]) => {
30
- const response = (0, core_parser_1.resolveReference)(responseObj, context);
31
- const decla = getContentDeclaration(getResponseName(name, status, context), response.content, context);
32
- if (decla) {
33
- addToOpenApiResult(result, "responses", decla);
34
- }
35
- });
36
- }
37
- }
38
- exports.parseOperation = parseOperation;
39
- function parseParameters(baseName, data, baseParams = {}, context, result) {
40
- const params = [];
41
- const query = [];
42
- const headers = [];
43
- const cookie = [];
44
- const res = {};
45
- data.forEach(item => {
46
- item = (0, core_parser_1.resolveReference)(item, context);
47
- switch (item.in) {
48
- case "path":
49
- params.push(item);
50
- break;
51
- case "header":
52
- headers.push(item);
53
- break;
54
- case "query":
55
- query.push(item);
56
- break;
57
- case "cookie":
58
- cookie.push(item);
59
- break;
60
- }
61
- });
62
- addParams(params, "params");
63
- addParams(headers, "headers");
64
- addParams(query, "query");
65
- addParams(cookie, "cookie");
66
- return res;
67
- function addParams(params, paramType) {
68
- if (!params.length)
69
- return;
70
- const name = baseName + (0, core_parser_1.pascalCase)(paramType);
71
- const type = getParamType(paramType, params, baseParams[paramType], context);
72
- addToOpenApiResult(result, paramType, core.createTypeOrInterfaceDeclaration({
73
- modifiers: [core.modifier.export],
74
- name,
75
- type
76
- }));
77
- res[paramType] = ts.factory.createTypeReferenceNode(name, undefined);
78
- }
79
- }
80
- exports.parseParameters = parseParameters;
81
- function parseReference(ref, context) {
82
- const type = (0, core_parser_1.getTypeFromSchema)(ref.schema, (0, core_parser_1.createRefContext)(ref, context));
83
- context.aliases.push(core.createTypeOrInterfaceDeclaration({
84
- modifiers: [core.modifier.export],
85
- name: ref.name,
86
- type
87
- }));
88
- }
89
- exports.parseReference = parseReference;
90
- //#endregion
91
- //#region Utils
92
- function getContentDeclaration(name, content, context) {
93
- if (!content)
94
- return;
95
- content = (0, core_parser_1.resolveReference)(content, context);
96
- const schema = getSchemaFromContent(content);
97
- if (!schema)
98
- return;
99
- const type = (0, core_parser_1.getTypeFromSchema)(schema, context);
100
- return core.createTypeOrInterfaceDeclaration({
101
- modifiers: [core.modifier.export],
102
- name,
103
- type
104
- });
105
- }
106
- exports.getContentDeclaration = getContentDeclaration;
107
- function getParamType(paramType, data, baseType, context) {
108
- const required = [];
109
- const props = {};
110
- data.forEach(m => {
111
- let name = m.name;
112
- if (paramType === "headers" && context.options.lowerHeaders) {
113
- name = name.toLowerCase();
114
- }
115
- props[name] = m.schema || {};
116
- if (m.required) {
117
- required.push(name);
118
- }
119
- });
120
- const ctx = paramType === "query" && typeof context.options.enableDateForQueryParams !== "undefined"
121
- ? { ...context, options: { ...context.options, enableDate: context.options.enableDateForQueryParams } }
122
- : context;
123
- const type = (0, core_parser_1.getTypeFromProperties)(props, required, false, ctx);
124
- if (baseType) {
125
- return ts.factory.createIntersectionTypeNode([baseType, type]);
126
- }
127
- return type;
128
- }
129
- exports.getParamType = getParamType;
130
- function getSchemaFromContent(content) {
131
- var _a, _b, _c, _d;
132
- return ((_a = content === null || content === void 0 ? void 0 : content["application/json"]) === null || _a === void 0 ? void 0 : _a.schema) ||
133
- ((_b = content === null || content === void 0 ? void 0 : content["application/x-www-form-urlencoded"]) === null || _b === void 0 ? void 0 : _b.schema) ||
134
- ((_c = content === null || content === void 0 ? void 0 : content["multipart/form-data"]) === null || _c === void 0 ? void 0 : _c.schema) ||
135
- ((_d = content === null || content === void 0 ? void 0 : content["*/*"]) === null || _d === void 0 ? void 0 : _d.schema);
136
- }
137
- exports.getSchemaFromContent = getSchemaFromContent;
138
- function getResponseName(operationName, statusCode, context) {
139
- let name = operationName + "Response";
140
- const status = parseInt(statusCode);
141
- if (status >= 200 && status < 300) {
142
- const count = (context.names[name] = (context.names[name] || 0) + 1);
143
- if (count > 1) {
144
- name += statusCode;
145
- }
146
- }
147
- else if (!isNaN(status)) {
148
- name += statusCode;
149
- }
150
- else {
151
- // default
152
- name += (0, core_parser_1.pascalCase)(statusCode);
153
- }
154
- return name;
155
- }
156
- exports.getResponseName = getResponseName;
157
- function getOperationName(verb, path, operationId, context) {
158
- const id = getOperationIdentifier(operationId);
159
- if (id) {
160
- return id;
161
- }
162
- return getPathName(`${verb} ${path}`, context);
163
- }
164
- exports.getOperationName = getOperationName;
165
- function getPathName(path, context) {
166
- path = path.replace(/\{(.+?)\}/, "by $1").replace(/\{(.+?)\}/g, "and $1");
167
- let name = (0, core_parser_1.pascalCase)(path);
168
- const count = (context.names[name] = (context.names[name] || 0) + 1);
169
- if (count > 1) {
170
- name += count;
171
- }
172
- return name;
173
- }
174
- exports.getPathName = getPathName;
175
- function getOperationIdentifier(id) {
176
- if (!id)
177
- return;
178
- if (id.match(/[^\w\s]/))
179
- return;
180
- id = (0, core_parser_1.pascalCase)(id);
181
- if (core.isValidIdentifier(id))
182
- return id;
183
- }
184
- exports.getOperationIdentifier = getOperationIdentifier;
185
- function addToOpenApiResult(result, prop, statement) {
186
- const statements = Array.isArray(statement) ? statement : [statement];
187
- result[prop].push(...statements);
188
- result.all.push(...statements);
189
- }
190
- exports.addToOpenApiResult = addToOpenApiResult;
191
- function createOpenApiResult() {
192
- return { params: [], query: [], headers: [], body: [], responses: [], models: [], cookie: [], import: [], all: [] };
193
- }
194
- exports.createOpenApiResult = createOpenApiResult;
@@ -1,10 +0,0 @@
1
- import type { OpenAPIObject } from "openapi3-ts/oas31";
2
- import { ParserOptions } from "@spec2ts/jsonschema/lib/core-parser";
3
- import { ParseOpenApiResult } from "./core-parser";
4
- export { ParseOpenApiResult };
5
- export interface ParseOpenApiOptions extends ParserOptions {
6
- lowerHeaders?: boolean;
7
- enableDateForQueryParams?: boolean | "strict" | "lax";
8
- }
9
- export declare function parseOpenApiFile(file: string, options?: ParseOpenApiOptions): Promise<ParseOpenApiResult>;
10
- export declare function parseOpenApi(spec: OpenAPIObject, options?: ParseOpenApiOptions): Promise<ParseOpenApiResult>;
@@ -1,29 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.parseOpenApi = exports.parseOpenApiFile = void 0;
4
- const path = require("path");
5
- const json_schema_ref_parser_1 = require("@apidevtools/json-schema-ref-parser");
6
- const core_parser_1 = require("@spec2ts/jsonschema/lib/core-parser");
7
- const core_parser_2 = require("./core-parser");
8
- async function parseOpenApiFile(file, options = {}) {
9
- const schema = await json_schema_ref_parser_1.default.parse(file);
10
- return parseOpenApi(schema, {
11
- cwd: path.resolve(path.dirname(file)) + "/",
12
- ...options
13
- });
14
- }
15
- exports.parseOpenApiFile = parseOpenApiFile;
16
- async function parseOpenApi(spec, options = {}) {
17
- var _a;
18
- if (!options.parseReference) {
19
- options.parseReference = core_parser_2.parseReference;
20
- }
21
- const context = await (0, core_parser_1.createContext)(spec, options);
22
- const result = (0, core_parser_2.createOpenApiResult)();
23
- Object.entries((_a = spec.paths) !== null && _a !== void 0 ? _a : {}).forEach(([path, item]) => {
24
- (0, core_parser_2.parsePathItem)(path, item, context, result);
25
- });
26
- (0, core_parser_2.addToOpenApiResult)(result, "models", context.aliases);
27
- return result;
28
- }
29
- exports.parseOpenApi = parseOpenApi;