api 7.0.0-alpha.1 → 7.0.0-alpha.3
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/bin.js +16 -62
- package/dist/codegen/index.js +2 -2
- package/dist/codegen/language.js +13 -9
- package/dist/codegen/languages/typescript/util.js +15 -20
- package/dist/codegen/languages/typescript.d.ts +3 -3
- package/dist/codegen/languages/typescript.js +324 -397
- package/dist/commands/index.js +1 -1
- package/dist/commands/install.js +155 -215
- package/dist/fetcher.d.ts +3 -3
- package/dist/fetcher.js +54 -95
- package/dist/lib/prompt.js +15 -67
- package/dist/logger.js +1 -1
- package/dist/packageInfo.d.ts +1 -1
- package/dist/packageInfo.js +1 -1
- package/dist/storage.d.ts +1 -1
- package/dist/storage.js +79 -133
- package/package.json +9 -8
- package/src/codegen/languages/typescript.ts +2 -2
- package/src/fetcher.ts +1 -1
- package/src/packageInfo.ts +1 -1
- package/src/storage.ts +1 -1
- package/tsconfig.json +2 -0
|
@@ -1,101 +1,37 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
var __extends = (this && this.__extends) || (function () {
|
|
3
|
-
var extendStatics = function (d, b) {
|
|
4
|
-
extendStatics = Object.setPrototypeOf ||
|
|
5
|
-
({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
|
|
6
|
-
function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; };
|
|
7
|
-
return extendStatics(d, b);
|
|
8
|
-
};
|
|
9
|
-
return function (d, b) {
|
|
10
|
-
if (typeof b !== "function" && b !== null)
|
|
11
|
-
throw new TypeError("Class extends value " + String(b) + " is not a constructor or null");
|
|
12
|
-
extendStatics(d, b);
|
|
13
|
-
function __() { this.constructor = d; }
|
|
14
|
-
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
|
|
15
|
-
};
|
|
16
|
-
})();
|
|
17
|
-
var __assign = (this && this.__assign) || function () {
|
|
18
|
-
__assign = Object.assign || function(t) {
|
|
19
|
-
for (var s, i = 1, n = arguments.length; i < n; i++) {
|
|
20
|
-
s = arguments[i];
|
|
21
|
-
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
|
|
22
|
-
t[p] = s[p];
|
|
23
|
-
}
|
|
24
|
-
return t;
|
|
25
|
-
};
|
|
26
|
-
return __assign.apply(this, arguments);
|
|
27
|
-
};
|
|
28
|
-
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
29
|
-
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
30
|
-
return new (P || (P = Promise))(function (resolve, reject) {
|
|
31
|
-
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
32
|
-
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
33
|
-
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
34
|
-
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
35
|
-
});
|
|
36
|
-
};
|
|
37
|
-
var __generator = (this && this.__generator) || function (thisArg, body) {
|
|
38
|
-
var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
|
|
39
|
-
return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
|
|
40
|
-
function verb(n) { return function (v) { return step([n, v]); }; }
|
|
41
|
-
function step(op) {
|
|
42
|
-
if (f) throw new TypeError("Generator is already executing.");
|
|
43
|
-
while (g && (g = 0, op[0] && (_ = 0)), _) try {
|
|
44
|
-
if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
|
|
45
|
-
if (y = 0, t) op = [op[0] & 2, t.value];
|
|
46
|
-
switch (op[0]) {
|
|
47
|
-
case 0: case 1: t = op; break;
|
|
48
|
-
case 4: _.label++; return { value: op[1], done: false };
|
|
49
|
-
case 5: _.label++; y = op[1]; op = [0]; continue;
|
|
50
|
-
case 7: op = _.ops.pop(); _.trys.pop(); continue;
|
|
51
|
-
default:
|
|
52
|
-
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
|
|
53
|
-
if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
|
|
54
|
-
if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
|
|
55
|
-
if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
|
|
56
|
-
if (t[2]) _.ops.pop();
|
|
57
|
-
_.trys.pop(); continue;
|
|
58
|
-
}
|
|
59
|
-
op = body.call(thisArg, _);
|
|
60
|
-
} catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
|
|
61
|
-
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
|
|
62
|
-
}
|
|
63
|
-
};
|
|
64
|
-
var __spreadArray = (this && this.__spreadArray) || function (to, from, pack) {
|
|
65
|
-
if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) {
|
|
66
|
-
if (ar || !(i in from)) {
|
|
67
|
-
if (!ar) ar = Array.prototype.slice.call(from, 0, i);
|
|
68
|
-
ar[i] = from[i];
|
|
69
|
-
}
|
|
70
|
-
}
|
|
71
|
-
return to.concat(ar || Array.prototype.slice.call(from));
|
|
72
|
-
};
|
|
73
2
|
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
74
3
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
75
4
|
};
|
|
76
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
6
|
+
const node_fs_1 = __importDefault(require("node:fs"));
|
|
7
|
+
const node_path_1 = __importDefault(require("node:path"));
|
|
8
|
+
const execa_1 = __importDefault(require("execa"));
|
|
9
|
+
const lodash_setwith_1 = __importDefault(require("lodash.setwith"));
|
|
10
|
+
const semver_1 = __importDefault(require("semver"));
|
|
11
|
+
const ts_morph_1 = require("ts-morph");
|
|
12
|
+
const logger_1 = __importDefault(require("../../logger"));
|
|
13
|
+
const language_1 = __importDefault(require("../language"));
|
|
14
|
+
const util_1 = require("./typescript/util");
|
|
15
|
+
class TSGenerator extends language_1.default {
|
|
16
|
+
project;
|
|
17
|
+
outputJS;
|
|
18
|
+
compilerTarget;
|
|
19
|
+
types;
|
|
20
|
+
sdk;
|
|
21
|
+
schemas;
|
|
22
|
+
usesHTTPMethodRangeInterface = false;
|
|
23
|
+
constructor(spec, specPath, identifier, opts = {}) {
|
|
24
|
+
const options = {
|
|
25
|
+
outputJS: false,
|
|
26
|
+
compilerTarget: 'cjs',
|
|
27
|
+
...opts,
|
|
28
|
+
};
|
|
92
29
|
if (!options.outputJS) {
|
|
93
30
|
// TypeScript compilation will always target towards ESM-like imports and exports.
|
|
94
31
|
options.compilerTarget = 'esm';
|
|
95
32
|
}
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
_this.requiredPackages = {
|
|
33
|
+
super(spec, specPath, identifier);
|
|
34
|
+
this.requiredPackages = {
|
|
99
35
|
api: {
|
|
100
36
|
reason: "Required for the `@readme/api-core` library that the codegen'd SDK uses for making requests.",
|
|
101
37
|
url: 'https://npm.im/api',
|
|
@@ -109,187 +45,178 @@ var TSGenerator = /** @class */ (function (_super) {
|
|
|
109
45
|
url: 'https://npm.im/oas',
|
|
110
46
|
},
|
|
111
47
|
};
|
|
112
|
-
|
|
48
|
+
this.project = new ts_morph_1.Project({
|
|
113
49
|
manipulationSettings: {
|
|
114
50
|
indentationText: ts_morph_1.IndentationText.TwoSpaces,
|
|
115
51
|
quoteKind: ts_morph_1.QuoteKind.Single,
|
|
116
52
|
},
|
|
117
|
-
compilerOptions:
|
|
53
|
+
compilerOptions: {
|
|
118
54
|
// If we're exporting a TypeScript SDK then we don't need to pollute the codegen directory
|
|
119
55
|
// with unnecessary declaration `.d.ts` files.
|
|
120
|
-
declaration: options.outputJS,
|
|
56
|
+
declaration: options.outputJS,
|
|
57
|
+
outDir: 'dist',
|
|
58
|
+
resolveJsonModule: true,
|
|
59
|
+
target: options.compilerTarget === 'cjs' ? ts_morph_1.ScriptTarget.ES5 : ts_morph_1.ScriptTarget.ES2020,
|
|
60
|
+
// If we're compiling to a CJS target then we need to include this compiler option
|
|
61
|
+
// otherwise TS will attempt to load our `openapi.json` import with a `.default` property
|
|
62
|
+
// which doesn't exist. `esModuleInterop` wraps imports in a small `__importDefault`
|
|
63
|
+
// function that does some determination to see if the module has a default export or not.
|
|
64
|
+
//
|
|
65
|
+
// Basically without this option CJS code will fail.
|
|
66
|
+
...(options.compilerTarget === 'cjs' ? { esModuleInterop: true } : {}),
|
|
67
|
+
},
|
|
121
68
|
});
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
return _this;
|
|
69
|
+
this.compilerTarget = options.compilerTarget;
|
|
70
|
+
this.outputJS = options.outputJS;
|
|
71
|
+
this.types = new Map();
|
|
72
|
+
this.schemas = {};
|
|
127
73
|
}
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
(opts.logger ? opts.logger : logger_1.default)(res.stdout);
|
|
171
|
-
}
|
|
172
|
-
})
|
|
173
|
-
.catch(function (err) {
|
|
174
|
-
if (opts.dryRun) {
|
|
175
|
-
(opts.logger ? opts.logger : logger_1.default)(err.message);
|
|
176
|
-
return;
|
|
177
|
-
}
|
|
178
|
-
throw err;
|
|
179
|
-
})];
|
|
180
|
-
}
|
|
181
|
-
});
|
|
74
|
+
async installer(storage, opts = {}) {
|
|
75
|
+
const installDir = storage.getIdentifierStorageDir();
|
|
76
|
+
const info = this.spec.getDefinition().info;
|
|
77
|
+
let pkgVersion = semver_1.default.coerce(info.version);
|
|
78
|
+
if (!pkgVersion) {
|
|
79
|
+
// If the version that's in `info.version` isn't compatible with semver NPM won't be able to
|
|
80
|
+
// handle it properly so we need to fallback to something it can.
|
|
81
|
+
pkgVersion = semver_1.default.coerce('0.0.0');
|
|
82
|
+
}
|
|
83
|
+
const pkg = {
|
|
84
|
+
name: `@api/${storage.identifier}`,
|
|
85
|
+
version: pkgVersion.version,
|
|
86
|
+
main: `./index.${this.outputJS ? 'js' : 'ts'}`,
|
|
87
|
+
types: './index.d.ts', // Types are always present regardless if you're getting compiled JS.
|
|
88
|
+
};
|
|
89
|
+
node_fs_1.default.writeFileSync(node_path_1.default.join(installDir, 'package.json'), JSON.stringify(pkg, null, 2));
|
|
90
|
+
const npmInstall = ['install', '--save', opts.dryRun ? '--dry-run' : ''].filter(Boolean);
|
|
91
|
+
// This will install packages required for the SDK within its installed directory in `.apis/`.
|
|
92
|
+
await (0, execa_1.default)('npm', [...npmInstall, ...Object.keys(this.requiredPackages)].filter(Boolean), {
|
|
93
|
+
cwd: installDir,
|
|
94
|
+
}).then(res => {
|
|
95
|
+
if (opts.dryRun) {
|
|
96
|
+
(opts.logger ? opts.logger : logger_1.default)(res.command);
|
|
97
|
+
(opts.logger ? opts.logger : logger_1.default)(res.stdout);
|
|
98
|
+
}
|
|
99
|
+
});
|
|
100
|
+
// This will install the installed SDK as a dependency within the current working directory,
|
|
101
|
+
// adding `@api/<sdk identifier>` as a dependency there so you can load it with
|
|
102
|
+
// `require('@api/<sdk identifier>)`.
|
|
103
|
+
return (0, execa_1.default)('npm', [...npmInstall, installDir].filter(Boolean))
|
|
104
|
+
.then(res => {
|
|
105
|
+
if (opts.dryRun) {
|
|
106
|
+
(opts.logger ? opts.logger : logger_1.default)(res.command);
|
|
107
|
+
(opts.logger ? opts.logger : logger_1.default)(res.stdout);
|
|
108
|
+
}
|
|
109
|
+
})
|
|
110
|
+
.catch(err => {
|
|
111
|
+
if (opts.dryRun) {
|
|
112
|
+
(opts.logger ? opts.logger : logger_1.default)(err.message);
|
|
113
|
+
return;
|
|
114
|
+
}
|
|
115
|
+
throw err;
|
|
182
116
|
});
|
|
183
|
-
}
|
|
117
|
+
}
|
|
184
118
|
/**
|
|
185
119
|
* Compile the current OpenAPI definition into a TypeScript library.
|
|
186
120
|
*
|
|
187
121
|
*/
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
122
|
+
async generator() {
|
|
123
|
+
const sdkSource = this.createSourceFile();
|
|
124
|
+
if (Object.keys(this.schemas).length) {
|
|
125
|
+
this.createSchemasFile();
|
|
126
|
+
this.createTypesFile();
|
|
127
|
+
/**
|
|
128
|
+
* Export all of our available types so they can be used in SDK implementations. Types are
|
|
129
|
+
* exported individually because TS has no way right now of allowing us to do
|
|
130
|
+
* `export type * from './types'` on a non-named entry.
|
|
131
|
+
*
|
|
132
|
+
* Types in the main entry point are only being exported for TS outputs as JS users won't be
|
|
133
|
+
* able to use them and it clashes with the default SDK export present.
|
|
134
|
+
*
|
|
135
|
+
* @see {@link https://github.com/microsoft/TypeScript/issues/37238}
|
|
136
|
+
* @see {@link https://github.com/readmeio/api/issues/588}
|
|
137
|
+
*/
|
|
138
|
+
if (!this.outputJS) {
|
|
139
|
+
const types = Array.from(this.types.keys());
|
|
140
|
+
types.sort();
|
|
141
|
+
sdkSource.addExportDeclarations([
|
|
142
|
+
{
|
|
143
|
+
isTypeOnly: true,
|
|
144
|
+
namedExports: types,
|
|
145
|
+
moduleSpecifier: './types',
|
|
146
|
+
},
|
|
147
|
+
]);
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
else {
|
|
151
|
+
// If we don't have any schemas then we shouldn't import a `types` file that doesn't exist.
|
|
152
|
+
sdkSource
|
|
153
|
+
.getImportDeclarations()
|
|
154
|
+
.find(id => id.getText() === "import type * as types from './types';")
|
|
155
|
+
?.remove();
|
|
156
|
+
}
|
|
157
|
+
// If this SDK doesn't use the `HTTPMethodRange` interface for handling `2XX` response status
|
|
158
|
+
// codes then we should remove it from being imported.
|
|
159
|
+
if (!this.usesHTTPMethodRangeInterface) {
|
|
160
|
+
sdkSource
|
|
161
|
+
.getImportDeclarations()
|
|
162
|
+
.find(id => id.getText().includes('HTTPMethodRange'))
|
|
163
|
+
?.replaceWithText("import type { ConfigOptions, FetchResponse } from '@readme/api-core';");
|
|
164
|
+
}
|
|
165
|
+
if (this.outputJS) {
|
|
166
|
+
return this.project
|
|
167
|
+
.emitToMemory()
|
|
168
|
+
.getFiles()
|
|
169
|
+
.map(sourceFile => {
|
|
170
|
+
const file = node_path_1.default.basename(sourceFile.filePath);
|
|
171
|
+
if (file === 'schemas.js' || file === 'types.js') {
|
|
172
|
+
// If we're generating a JS SDK then we don't need to generate these two files as the
|
|
173
|
+
// user will have `.d.ts` files for them instead.
|
|
174
|
+
return {};
|
|
175
|
+
}
|
|
176
|
+
let code = sourceFile.text;
|
|
177
|
+
if (file === 'index.js' && this.compilerTarget === 'cjs') {
|
|
198
178
|
/**
|
|
199
|
-
*
|
|
200
|
-
*
|
|
201
|
-
*
|
|
179
|
+
* There's an annoying quirk with `ts-morph` where if we're exporting a default export
|
|
180
|
+
* to a CJS environment, it'll export it as `exports.default`. Because we don't want
|
|
181
|
+
* folks in these environments to have to load their SDKs with
|
|
182
|
+
* `require('@api/sdk').default` we're overriding that here to change it to being the
|
|
183
|
+
* module exports.
|
|
202
184
|
*
|
|
203
|
-
*
|
|
204
|
-
*
|
|
205
|
-
*
|
|
206
|
-
* @see {@link https://github.com/microsoft/TypeScript/issues/37238}
|
|
207
|
-
* @see {@link https://github.com/readmeio/api/issues/588}
|
|
185
|
+
* `ts-morph` unfortunately doesn't give us any options for programatically doing this
|
|
186
|
+
* so we need to resort to modifying the emitted JS code.
|
|
208
187
|
*/
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
sdkSource.addExportDeclarations([
|
|
213
|
-
{
|
|
214
|
-
isTypeOnly: true,
|
|
215
|
-
namedExports: types,
|
|
216
|
-
moduleSpecifier: './types',
|
|
217
|
-
},
|
|
218
|
-
]);
|
|
219
|
-
}
|
|
220
|
-
}
|
|
221
|
-
else {
|
|
222
|
-
// If we don't have any schemas then we shouldn't import a `types` file that doesn't exist.
|
|
223
|
-
(_a = sdkSource
|
|
224
|
-
.getImportDeclarations()
|
|
225
|
-
.find(function (id) { return id.getText() === "import type * as types from './types';"; })) === null || _a === void 0 ? void 0 : _a.remove();
|
|
226
|
-
}
|
|
227
|
-
// If this SDK doesn't use the `HTTPMethodRange` interface for handling `2XX` response status
|
|
228
|
-
// codes then we should remove it from being imported.
|
|
229
|
-
if (!this.usesHTTPMethodRangeInterface) {
|
|
230
|
-
(_b = sdkSource
|
|
231
|
-
.getImportDeclarations()
|
|
232
|
-
.find(function (id) { return id.getText().includes('HTTPMethodRange'); })) === null || _b === void 0 ? void 0 : _b.replaceWithText("import type { ConfigOptions, FetchResponse } from '@readme/api-core';");
|
|
188
|
+
code = code
|
|
189
|
+
.replace(/Object\.defineProperty\(exports, '__esModule', { value: true }\);\n/, '')
|
|
190
|
+
.replace('exports.default = createSDK;', 'module.exports = createSDK;');
|
|
233
191
|
}
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
* `ts-morph` unfortunately doesn't give us any options for programatically doing this
|
|
256
|
-
* so we need to resort to modifying the emitted JS code.
|
|
257
|
-
*/
|
|
258
|
-
code = code
|
|
259
|
-
.replace(/Object\.defineProperty\(exports, '__esModule', { value: true }\);\n/, '')
|
|
260
|
-
.replace('exports.default = createSDK;', 'module.exports = createSDK;');
|
|
261
|
-
}
|
|
262
|
-
return _a = {},
|
|
263
|
-
_a[file] = code,
|
|
264
|
-
_a;
|
|
265
|
-
})
|
|
266
|
-
.reduce(function (prev, next) { return Object.assign(prev, next); })];
|
|
267
|
-
}
|
|
268
|
-
return [2 /*return*/, __spreadArray(__spreadArray([], this.project.getSourceFiles().map(function (sourceFile) {
|
|
269
|
-
var _a;
|
|
270
|
-
return (_a = {},
|
|
271
|
-
_a[sourceFile.getBaseName()] = sourceFile.getFullText(),
|
|
272
|
-
_a);
|
|
273
|
-
}), true), this.project
|
|
274
|
-
.emitToMemory({ emitOnlyDtsFiles: true })
|
|
275
|
-
.getFiles()
|
|
276
|
-
.map(function (sourceFile) {
|
|
277
|
-
var _a;
|
|
278
|
-
return (_a = {},
|
|
279
|
-
_a[node_path_1.default.basename(sourceFile.filePath)] = sourceFile.text,
|
|
280
|
-
_a);
|
|
281
|
-
}), true).reduce(function (prev, next) { return Object.assign(prev, next); })];
|
|
282
|
-
});
|
|
283
|
-
});
|
|
284
|
-
};
|
|
192
|
+
return {
|
|
193
|
+
[file]: code,
|
|
194
|
+
};
|
|
195
|
+
})
|
|
196
|
+
.reduce((prev, next) => Object.assign(prev, next));
|
|
197
|
+
}
|
|
198
|
+
return [
|
|
199
|
+
...this.project.getSourceFiles().map(sourceFile => ({
|
|
200
|
+
[sourceFile.getBaseName()]: sourceFile.getFullText(),
|
|
201
|
+
})),
|
|
202
|
+
// Because we're returning the raw source files for TS generation we also need to separately
|
|
203
|
+
// emit out our declaration files so we can put those into a separate file in the installed
|
|
204
|
+
// SDK directory.
|
|
205
|
+
...this.project
|
|
206
|
+
.emitToMemory({ emitOnlyDtsFiles: true })
|
|
207
|
+
.getFiles()
|
|
208
|
+
.map(sourceFile => ({
|
|
209
|
+
[node_path_1.default.basename(sourceFile.filePath)]: sourceFile.text,
|
|
210
|
+
})),
|
|
211
|
+
].reduce((prev, next) => Object.assign(prev, next));
|
|
212
|
+
}
|
|
285
213
|
/**
|
|
286
214
|
* Create our main SDK source file.
|
|
287
215
|
*
|
|
288
216
|
*/
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
var sourceFile = this.project.createSourceFile('index.ts', '');
|
|
217
|
+
createSourceFile() {
|
|
218
|
+
const { operations } = this.loadOperationsAndMethods();
|
|
219
|
+
const sourceFile = this.project.createSourceFile('index.ts', '');
|
|
293
220
|
sourceFile.addImportDeclarations([
|
|
294
221
|
// This import will be automatically removed later if the SDK ends up not having any types.
|
|
295
222
|
{ defaultImport: 'type * as types', moduleSpecifier: './types' },
|
|
@@ -311,9 +238,9 @@ var TSGenerator = /** @class */ (function (_super) {
|
|
|
311
238
|
],
|
|
312
239
|
});
|
|
313
240
|
this.sdk.addConstructor({
|
|
314
|
-
statements:
|
|
241
|
+
statements: writer => {
|
|
315
242
|
writer.writeLine('this.spec = Oas.init(definition);');
|
|
316
|
-
writer.write('this.core = new APICore(this.spec, ').quote(
|
|
243
|
+
writer.write('this.core = new APICore(this.spec, ').quote(this.userAgent).write(');');
|
|
317
244
|
return writer;
|
|
318
245
|
},
|
|
319
246
|
});
|
|
@@ -322,12 +249,10 @@ var TSGenerator = /** @class */ (function (_super) {
|
|
|
322
249
|
{
|
|
323
250
|
name: 'config',
|
|
324
251
|
parameters: [{ name: 'config', type: 'ConfigOptions' }],
|
|
325
|
-
statements:
|
|
252
|
+
statements: writer => writer.writeLine('this.core.setConfig(config);'),
|
|
326
253
|
docs: [
|
|
327
254
|
{
|
|
328
|
-
description:
|
|
329
|
-
return writer.writeLine((0, util_1.wordWrap)('Optionally configure various options that the SDK allows.'));
|
|
330
|
-
},
|
|
255
|
+
description: writer => writer.writeLine((0, util_1.wordWrap)('Optionally configure various options that the SDK allows.')),
|
|
331
256
|
tags: [
|
|
332
257
|
{ tagName: 'param', text: 'config Object of supported SDK options and toggles.' },
|
|
333
258
|
{
|
|
@@ -341,16 +266,25 @@ var TSGenerator = /** @class */ (function (_super) {
|
|
|
341
266
|
{
|
|
342
267
|
name: 'auth',
|
|
343
268
|
parameters: [{ name: '...values', type: 'string[] | number[]' }],
|
|
344
|
-
statements:
|
|
269
|
+
statements: writer => {
|
|
345
270
|
writer.writeLine('this.core.setAuth(...values);');
|
|
346
271
|
writer.writeLine('return this;');
|
|
347
272
|
return writer;
|
|
348
273
|
},
|
|
349
274
|
docs: [
|
|
350
275
|
{
|
|
351
|
-
description:
|
|
352
|
-
|
|
353
|
-
|
|
276
|
+
description: writer => writer.writeLine((0, util_1.wordWrap)(`If the API you're using requires authentication you can supply the required credentials through this method and the library will magically determine how they should be used within your API request.
|
|
277
|
+
|
|
278
|
+
With the exception of OpenID and MutualTLS, it supports all forms of authentication supported by the OpenAPI specification.
|
|
279
|
+
|
|
280
|
+
@example <caption>HTTP Basic auth</caption>
|
|
281
|
+
sdk.auth('username', 'password');
|
|
282
|
+
|
|
283
|
+
@example <caption>Bearer tokens (HTTP or OAuth 2)</caption>
|
|
284
|
+
sdk.auth('myBearerToken');
|
|
285
|
+
|
|
286
|
+
@example <caption>API Keys</caption>
|
|
287
|
+
sdk.auth('myApiKey');`)),
|
|
354
288
|
tags: [
|
|
355
289
|
{ tagName: 'see', text: '{@link https://spec.openapis.org/oas/v3.0.3#fixed-fields-22}' },
|
|
356
290
|
{ tagName: 'see', text: '{@link https://spec.openapis.org/oas/v3.1.0#fixed-fields-22}' },
|
|
@@ -368,12 +302,19 @@ var TSGenerator = /** @class */ (function (_super) {
|
|
|
368
302
|
{ name: 'url', type: 'string' },
|
|
369
303
|
{ name: 'variables', initializer: '{}' },
|
|
370
304
|
],
|
|
371
|
-
statements:
|
|
305
|
+
statements: writer => writer.writeLine('this.core.setServer(url, variables);'),
|
|
372
306
|
docs: [
|
|
373
307
|
{
|
|
374
|
-
description:
|
|
375
|
-
|
|
376
|
-
|
|
308
|
+
description: writer => writer.writeLine((0, util_1.wordWrap)(`If the API you're using offers alternate server URLs, and server variables, you can tell the SDK which one to use with this method. To use it you can supply either one of the server URLs that are contained within the OpenAPI definition (along with any server variables), or you can pass it a fully qualified URL to use (that may or may not exist within the OpenAPI definition).
|
|
309
|
+
|
|
310
|
+
@example <caption>Server URL with server variables</caption>
|
|
311
|
+
sdk.server('https://{region}.api.example.com/{basePath}', {
|
|
312
|
+
name: 'eu',
|
|
313
|
+
basePath: 'v14',
|
|
314
|
+
});
|
|
315
|
+
|
|
316
|
+
@example <caption>Fully qualified server URL</caption>
|
|
317
|
+
sdk.server('https://eu.api.example.com/v14');`)),
|
|
377
318
|
tags: [
|
|
378
319
|
{ tagName: 'param', text: 'url Server URL' },
|
|
379
320
|
{ tagName: 'param', text: 'variables An object of variables to replace into the server URL.' },
|
|
@@ -383,9 +324,8 @@ var TSGenerator = /** @class */ (function (_super) {
|
|
|
383
324
|
},
|
|
384
325
|
]);
|
|
385
326
|
// Add all available operation ID accessors into the SDK.
|
|
386
|
-
Object.entries(operations).forEach(
|
|
387
|
-
|
|
388
|
-
_this.createOperationAccessor(data.operation, operationId, data.types.params, data.types.responses);
|
|
327
|
+
Object.entries(operations).forEach(([operationId, data]) => {
|
|
328
|
+
this.createOperationAccessor(data.operation, operationId, data.types.params, data.types.responses);
|
|
389
329
|
});
|
|
390
330
|
// Export our SDK into the source file.
|
|
391
331
|
sourceFile.addVariableStatement({
|
|
@@ -393,7 +333,7 @@ var TSGenerator = /** @class */ (function (_super) {
|
|
|
393
333
|
declarations: [
|
|
394
334
|
{
|
|
395
335
|
name: 'createSDK',
|
|
396
|
-
initializer:
|
|
336
|
+
initializer: writer => {
|
|
397
337
|
// `ts-morph` doesn't have any way to cleanly create an IFEE.
|
|
398
338
|
writer.writeLine('(() => { return new SDK(); })()');
|
|
399
339
|
return writer;
|
|
@@ -410,23 +350,22 @@ var TSGenerator = /** @class */ (function (_super) {
|
|
|
410
350
|
expression: 'createSDK',
|
|
411
351
|
});
|
|
412
352
|
return sourceFile;
|
|
413
|
-
}
|
|
353
|
+
}
|
|
414
354
|
/**
|
|
415
355
|
* Create our main schemas file. This is where all of the JSON Schema that our TypeScript typing
|
|
416
356
|
* infrastructure sources its data from. Without this there are no types.
|
|
417
357
|
*
|
|
418
358
|
*/
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
Array.from(sortedSchemas).forEach(
|
|
423
|
-
var schemaName = _a[0], schema = _a[1];
|
|
359
|
+
createSchemasFile() {
|
|
360
|
+
const sourceFile = this.project.createSourceFile('schemas.ts', '');
|
|
361
|
+
const sortedSchemas = new Map(Array.from(Object.entries(this.schemas)).sort());
|
|
362
|
+
Array.from(sortedSchemas).forEach(([schemaName, schema]) => {
|
|
424
363
|
sourceFile.addVariableStatement({
|
|
425
364
|
declarationKind: ts_morph_1.VariableDeclarationKind.Const,
|
|
426
365
|
declarations: [
|
|
427
366
|
{
|
|
428
367
|
name: schemaName,
|
|
429
|
-
initializer:
|
|
368
|
+
initializer: writer => {
|
|
430
369
|
/**
|
|
431
370
|
* This is the conversion prefix that we add to all `$ref` pointers we find in
|
|
432
371
|
* generated JSON Schema.
|
|
@@ -440,18 +379,18 @@ var TSGenerator = /** @class */ (function (_super) {
|
|
|
440
379
|
* And because our TypeScript type name generator properly ignores `:`, this is safe
|
|
441
380
|
* to prepend to all generated type names.
|
|
442
381
|
*/
|
|
443
|
-
|
|
382
|
+
let str = JSON.stringify(schema);
|
|
444
383
|
str = str.replace(/"::convert::([a-zA-Z_$\\d]*)"/g, '$1');
|
|
445
|
-
writer.writeLine(
|
|
384
|
+
writer.writeLine(`${str} as const`);
|
|
446
385
|
return writer;
|
|
447
386
|
},
|
|
448
387
|
},
|
|
449
388
|
],
|
|
450
389
|
});
|
|
451
390
|
});
|
|
452
|
-
sourceFile.addStatements(
|
|
391
|
+
sourceFile.addStatements(`export { ${Array.from(sortedSchemas.keys()).join(', ')} }`);
|
|
453
392
|
return sourceFile;
|
|
454
|
-
}
|
|
393
|
+
}
|
|
455
394
|
/**
|
|
456
395
|
* Create our main types file. This sources its data from the JSON Schema `schemas.ts` file and
|
|
457
396
|
* will re-export types to be used in TypeScript implementations and IDE intellisense. This
|
|
@@ -459,42 +398,42 @@ var TSGenerator = /** @class */ (function (_super) {
|
|
|
459
398
|
*
|
|
460
399
|
* @see {@link https://npm.im/json-schema-to-ts}
|
|
461
400
|
*/
|
|
462
|
-
|
|
463
|
-
|
|
401
|
+
createTypesFile() {
|
|
402
|
+
const sourceFile = this.project.createSourceFile('types.ts', '');
|
|
464
403
|
sourceFile.addImportDeclarations([
|
|
465
404
|
{ defaultImport: 'type { FromSchema }', moduleSpecifier: 'json-schema-to-ts' },
|
|
466
405
|
{ defaultImport: '* as schemas', moduleSpecifier: './schemas' },
|
|
467
406
|
]);
|
|
468
|
-
Array.from(new Map(Array.from(this.types.entries()).sort())).forEach(
|
|
469
|
-
var typeName = _a[0], typeExpression = _a[1];
|
|
407
|
+
Array.from(new Map(Array.from(this.types.entries()).sort())).forEach(([typeName, typeExpression]) => {
|
|
470
408
|
sourceFile.addTypeAlias({ isExported: true, name: typeName, type: typeExpression });
|
|
471
409
|
});
|
|
472
410
|
return sourceFile;
|
|
473
|
-
}
|
|
411
|
+
}
|
|
474
412
|
/**
|
|
475
413
|
* Add a new JSDoc `@tag` to an existing docblock.
|
|
476
414
|
*
|
|
477
415
|
*/
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
var tags = (_a = docblock.tags) !== null && _a !== void 0 ? _a : [];
|
|
416
|
+
static addTagToDocblock(docblock, tag) {
|
|
417
|
+
const tags = docblock.tags ?? [];
|
|
481
418
|
tags.push(tag);
|
|
482
|
-
return
|
|
483
|
-
|
|
419
|
+
return {
|
|
420
|
+
...docblock,
|
|
421
|
+
tags,
|
|
422
|
+
};
|
|
423
|
+
}
|
|
484
424
|
/**
|
|
485
425
|
* Create operation accessors on the SDK.
|
|
486
426
|
*
|
|
487
427
|
*/
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
var description = operation.getDescription();
|
|
428
|
+
createOperationAccessor(operation, operationId, paramTypes, responseTypes) {
|
|
429
|
+
let docblock = {};
|
|
430
|
+
const summary = operation.getSummary();
|
|
431
|
+
const description = operation.getDescription();
|
|
493
432
|
if (summary || description) {
|
|
494
433
|
// To keep our generated docblocks clean we should only add the `@summary` tag if we've
|
|
495
434
|
// got both a summary and a description present on the operation, otherwise we can alternate
|
|
496
435
|
// what we surface the main docblock description.
|
|
497
|
-
docblock.description =
|
|
436
|
+
docblock.description = writer => {
|
|
498
437
|
if (description) {
|
|
499
438
|
writer.writeLine((0, util_1.docblockEscape)((0, util_1.wordWrap)(description)));
|
|
500
439
|
}
|
|
@@ -511,9 +450,9 @@ var TSGenerator = /** @class */ (function (_super) {
|
|
|
511
450
|
});
|
|
512
451
|
}
|
|
513
452
|
}
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
453
|
+
let hasOptionalBody = false;
|
|
454
|
+
let hasOptionalMetadata = false;
|
|
455
|
+
const parameters = {};
|
|
517
456
|
if (paramTypes) {
|
|
518
457
|
// If an operation has a request body payload it will only ever have `body` or `formData`,
|
|
519
458
|
// never both, as these are determined upon the media type that's in use.
|
|
@@ -534,70 +473,69 @@ var TSGenerator = /** @class */ (function (_super) {
|
|
|
534
473
|
};
|
|
535
474
|
}
|
|
536
475
|
}
|
|
537
|
-
|
|
476
|
+
let returnType = 'Promise<FetchResponse<number, unknown>>';
|
|
538
477
|
if (responseTypes) {
|
|
539
|
-
|
|
540
|
-
.map(
|
|
541
|
-
var status = _a[0], _b = _a[1], responseDescription = _b.description, responseType = _b.type;
|
|
478
|
+
const returnTypes = Object.entries(responseTypes)
|
|
479
|
+
.map(([status, { description: responseDescription, type: responseType }]) => {
|
|
542
480
|
if (status.toLowerCase() === 'default') {
|
|
543
|
-
return
|
|
481
|
+
return `FetchResponse<number, ${responseType}>`;
|
|
544
482
|
}
|
|
545
483
|
else if (status.length === 3 && status.toUpperCase().endsWith('XX')) {
|
|
546
|
-
|
|
484
|
+
const statusPrefix = status.slice(0, 1);
|
|
547
485
|
if (!Number.isInteger(Number(statusPrefix))) {
|
|
548
486
|
// If this matches the `_XX` format, but it isn't `{number}XX` then we can't handle
|
|
549
487
|
// it and should instead fall back to treating it as an unknown number.
|
|
550
|
-
return
|
|
488
|
+
return `FetchResponse<number, ${responseType}>`;
|
|
551
489
|
}
|
|
552
490
|
if (Number(statusPrefix) >= 4) {
|
|
553
491
|
docblock = TSGenerator.addTagToDocblock(docblock, {
|
|
554
492
|
tagName: 'throws',
|
|
555
|
-
text:
|
|
493
|
+
text: `FetchError<${status}, ${responseType}>${responseDescription ? (0, util_1.docblockEscape)((0, util_1.wordWrap)(` ${responseDescription}`)) : ''}`,
|
|
556
494
|
});
|
|
557
495
|
return false;
|
|
558
496
|
}
|
|
559
|
-
|
|
560
|
-
return
|
|
497
|
+
this.usesHTTPMethodRangeInterface = true;
|
|
498
|
+
return `FetchResponse<HTTPMethodRange<${statusPrefix}00, ${statusPrefix}99>, ${responseType}>`;
|
|
561
499
|
}
|
|
562
500
|
// 400 and 500 status code families are thrown as exceptions so adding them as a possible
|
|
563
501
|
// return type isn't valid.
|
|
564
502
|
if (Number(status) >= 400) {
|
|
565
503
|
docblock = TSGenerator.addTagToDocblock(docblock, {
|
|
566
504
|
tagName: 'throws',
|
|
567
|
-
text:
|
|
505
|
+
text: `FetchError<${status}, ${responseType}>${responseDescription ? (0, util_1.docblockEscape)((0, util_1.wordWrap)(` ${responseDescription}`)) : ''}`,
|
|
568
506
|
});
|
|
569
507
|
return false;
|
|
570
508
|
}
|
|
571
|
-
return
|
|
509
|
+
return `FetchResponse<${status}, ${responseType}>`;
|
|
572
510
|
})
|
|
573
511
|
.filter(Boolean)
|
|
574
512
|
.join(' | ');
|
|
575
513
|
// If all of our documented responses are for error status codes then all we can document for
|
|
576
514
|
// anything else that might happen is `unknown`.
|
|
577
|
-
returnType =
|
|
515
|
+
returnType = `Promise<${returnTypes.length ? returnTypes : 'FetchResponse<number, unknown>'}>`;
|
|
578
516
|
}
|
|
579
|
-
|
|
580
|
-
|
|
517
|
+
const shouldAddAltTypedOverloads = Object.keys(parameters).length === 2 && hasOptionalBody && !hasOptionalMetadata;
|
|
518
|
+
const operationIdAccessor = this.sdk.addMethod({
|
|
581
519
|
name: operationId,
|
|
582
|
-
returnType
|
|
520
|
+
returnType,
|
|
583
521
|
// If we're going to be creating typed method overloads for optional body an metadata handling
|
|
584
522
|
// we should only add a docblock to the first overload we create because IDE Intellisense will
|
|
585
523
|
// always use that and adding a docblock to all three will bloat the SDK with unused and
|
|
586
524
|
// unsurfaced method documentation.
|
|
587
525
|
docs: shouldAddAltTypedOverloads ? undefined : Object.keys(docblock).length ? [docblock] : undefined,
|
|
588
|
-
statements:
|
|
526
|
+
statements: writer => {
|
|
589
527
|
/**
|
|
590
528
|
* @example return this.core.fetch('/pet/findByStatus', 'get', body, metadata);
|
|
591
529
|
* @example return this.core.fetch('/pet/findByStatus', 'get', metadata);
|
|
592
530
|
*/
|
|
593
|
-
|
|
531
|
+
const fetchStmt = writer
|
|
594
532
|
.write('return this.core.fetch(')
|
|
595
533
|
.quote(operation.path)
|
|
596
534
|
.write(', ')
|
|
597
535
|
.quote(operation.method);
|
|
598
|
-
|
|
536
|
+
const totalParams = Object.keys(parameters).length;
|
|
599
537
|
if (totalParams) {
|
|
600
|
-
Object.values(parameters).forEach(
|
|
538
|
+
Object.values(parameters).forEach((arg, i) => {
|
|
601
539
|
if (i === 0) {
|
|
602
540
|
fetchStmt.write(', ');
|
|
603
541
|
}
|
|
@@ -618,16 +556,16 @@ var TSGenerator = /** @class */ (function (_super) {
|
|
|
618
556
|
// Create an overload that has both `body` and `metadata` parameters as required.
|
|
619
557
|
operationIdAccessor.addOverload({
|
|
620
558
|
parameters: [
|
|
621
|
-
|
|
622
|
-
|
|
559
|
+
{ ...parameters.body, hasQuestionToken: false },
|
|
560
|
+
{ ...parameters.metadata, hasQuestionToken: false },
|
|
623
561
|
],
|
|
624
|
-
returnType
|
|
562
|
+
returnType,
|
|
625
563
|
docs: Object.keys(docblock).length ? [docblock] : undefined,
|
|
626
564
|
});
|
|
627
565
|
// Create an overload that just has a single `metadata` parameter.
|
|
628
566
|
operationIdAccessor.addOverload({
|
|
629
|
-
parameters: [
|
|
630
|
-
returnType
|
|
567
|
+
parameters: [{ ...parameters.metadata }],
|
|
568
|
+
returnType,
|
|
631
569
|
});
|
|
632
570
|
// Create an overload that has both `body` and `metadata` parameters as optional. Even though
|
|
633
571
|
// our `metadata` parameter is actually required for this operation this is the only way we're
|
|
@@ -637,47 +575,47 @@ var TSGenerator = /** @class */ (function (_super) {
|
|
|
637
575
|
// see if what the user is supplying is `metadata` or `body` content when they supply one or
|
|
638
576
|
// both.
|
|
639
577
|
operationIdAccessor.addParameters([
|
|
640
|
-
|
|
578
|
+
{
|
|
579
|
+
...parameters.body,
|
|
641
580
|
// Overloads have to be the most distilled version of the method so that's why we need to
|
|
642
581
|
// type `body` as either `body` or `metadata`. If we didn't do this, if `body` was a JSON
|
|
643
582
|
// Schema type that didn't allow `additionalProperties` then the implementation overload
|
|
644
583
|
// would throw type errors.
|
|
645
|
-
type:
|
|
646
|
-
|
|
584
|
+
type: `${parameters.body.type} | ${parameters.metadata.type}`,
|
|
585
|
+
hasQuestionToken: true,
|
|
586
|
+
},
|
|
587
|
+
{ ...parameters.metadata, hasQuestionToken: true },
|
|
647
588
|
]);
|
|
648
589
|
}
|
|
649
590
|
else {
|
|
650
591
|
operationIdAccessor.addParameters(Object.values(parameters));
|
|
651
592
|
}
|
|
652
|
-
}
|
|
593
|
+
}
|
|
653
594
|
/**
|
|
654
595
|
* Scour through the current OpenAPI definition and compile a store of every operation, along
|
|
655
596
|
* with every HTTP method that's in use, and their available TypeScript types that we can use,
|
|
656
597
|
* along with every HTTP method that's in use.
|
|
657
598
|
*
|
|
658
599
|
*/
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
var methods = new Set();
|
|
600
|
+
loadOperationsAndMethods() {
|
|
601
|
+
const operations = {};
|
|
602
|
+
const methods = new Set();
|
|
663
603
|
// Prepare all of the schemas that we need to process for every operation within this API
|
|
664
604
|
// definition.
|
|
665
|
-
Object.entries(this.spec.getPaths()).forEach(
|
|
666
|
-
|
|
667
|
-
Object.entries(ops).forEach(function (_a) {
|
|
668
|
-
var method = _a[0], operation = _a[1];
|
|
605
|
+
Object.entries(this.spec.getPaths()).forEach(([, ops]) => {
|
|
606
|
+
Object.entries(ops).forEach(([method, operation]) => {
|
|
669
607
|
methods.add(method);
|
|
670
|
-
|
|
608
|
+
const operationId = operation.getOperationId({
|
|
671
609
|
// This `camelCase` option will clean up any weird characters that might be present in
|
|
672
610
|
// the `operationId` so as we don't break TS compilation with an invalid method accessor.
|
|
673
611
|
camelCase: true,
|
|
674
612
|
});
|
|
675
613
|
operations[operationId] = {
|
|
676
614
|
types: {
|
|
677
|
-
params:
|
|
678
|
-
responses:
|
|
615
|
+
params: this.prepareParameterTypesForOperation(operation, operationId),
|
|
616
|
+
responses: this.prepareResponseTypesForOperation(operation, operationId),
|
|
679
617
|
},
|
|
680
|
-
operation
|
|
618
|
+
operation,
|
|
681
619
|
};
|
|
682
620
|
});
|
|
683
621
|
});
|
|
@@ -685,28 +623,27 @@ var TSGenerator = /** @class */ (function (_super) {
|
|
|
685
623
|
throw new Error('Sorry, this OpenAPI definition does not have any operation paths to generate an SDK for.');
|
|
686
624
|
}
|
|
687
625
|
return {
|
|
688
|
-
operations
|
|
689
|
-
methods
|
|
626
|
+
operations,
|
|
627
|
+
methods,
|
|
690
628
|
};
|
|
691
|
-
}
|
|
629
|
+
}
|
|
692
630
|
/**
|
|
693
631
|
* Compile the parameter (path, query, cookie, and header) schemas for an API operation into
|
|
694
632
|
* usable TypeScript types.
|
|
695
633
|
*
|
|
696
634
|
*/
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
var schemas = operation.getParametersAsJSONSchema({
|
|
635
|
+
prepareParameterTypesForOperation(operation, operationId) {
|
|
636
|
+
const schemas = operation.getParametersAsJSONSchema({
|
|
700
637
|
includeDiscriminatorMappingRefs: false,
|
|
701
638
|
mergeIntoBodyAndMetadata: true,
|
|
702
639
|
retainDeprecatedProperties: true,
|
|
703
|
-
transformer:
|
|
640
|
+
transformer: (s) => {
|
|
704
641
|
// As our schemas are dereferenced in the `oas` library we don't want to pollute our
|
|
705
642
|
// codegen'd schemas file with duplicate schemas.
|
|
706
643
|
if ('x-readme-ref-name' in s && typeof s['x-readme-ref-name'] !== 'undefined') {
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
return
|
|
644
|
+
const typeName = (0, util_1.generateTypeName)(s['x-readme-ref-name']);
|
|
645
|
+
this.addSchemaToExport(s, typeName, typeName);
|
|
646
|
+
return `::convert::${typeName}`;
|
|
710
647
|
}
|
|
711
648
|
return s;
|
|
712
649
|
},
|
|
@@ -714,17 +651,12 @@ var TSGenerator = /** @class */ (function (_super) {
|
|
|
714
651
|
if (!schemas || !schemas.length) {
|
|
715
652
|
return false;
|
|
716
653
|
}
|
|
717
|
-
|
|
718
|
-
.map(
|
|
719
|
-
|
|
720
|
-
return (_a = {}, _a[param.type] = param.schema, _a);
|
|
721
|
-
})
|
|
722
|
-
.reduce(function (prev, next) { return Object.assign(prev, next); });
|
|
654
|
+
const res = schemas
|
|
655
|
+
.map(param => ({ [param.type]: param.schema }))
|
|
656
|
+
.reduce((prev, next) => Object.assign(prev, next));
|
|
723
657
|
return Object.entries(res)
|
|
724
|
-
.map(
|
|
725
|
-
|
|
726
|
-
var paramType = _a[0], schema = _a[1];
|
|
727
|
-
var typeName;
|
|
658
|
+
.map(([paramType, schema]) => {
|
|
659
|
+
let typeName;
|
|
728
660
|
if (typeof schema === 'string' && schema.startsWith('::convert::')) {
|
|
729
661
|
// If this schema is a string and has our conversion prefix then we've already created
|
|
730
662
|
// a type for it.
|
|
@@ -732,38 +664,36 @@ var TSGenerator = /** @class */ (function (_super) {
|
|
|
732
664
|
}
|
|
733
665
|
else {
|
|
734
666
|
typeName = (0, util_1.generateTypeName)(operationId, paramType, 'param');
|
|
735
|
-
|
|
667
|
+
this.addSchemaToExport(schema, typeName, `${(0, util_1.generateTypeName)(operationId)}.${paramType}`);
|
|
736
668
|
}
|
|
737
|
-
return
|
|
669
|
+
return {
|
|
738
670
|
// Types are prefixed with `types.` because that's how we're importing them from
|
|
739
671
|
// `types.d.ts`.
|
|
740
|
-
|
|
741
|
-
|
|
672
|
+
[paramType]: `types.${typeName}`,
|
|
673
|
+
};
|
|
742
674
|
})
|
|
743
|
-
.reduce(
|
|
744
|
-
}
|
|
675
|
+
.reduce((prev, next) => Object.assign(prev, next), {});
|
|
676
|
+
}
|
|
745
677
|
/**
|
|
746
678
|
* Compile the response schemas for an API operation into usable TypeScript types.
|
|
747
679
|
*
|
|
748
680
|
*/
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
var responseStatusCodes = operation.getResponseStatusCodes();
|
|
681
|
+
prepareResponseTypesForOperation(operation, operationId) {
|
|
682
|
+
const responseStatusCodes = operation.getResponseStatusCodes();
|
|
752
683
|
if (!responseStatusCodes.length) {
|
|
753
684
|
return undefined;
|
|
754
685
|
}
|
|
755
|
-
|
|
756
|
-
.map(
|
|
757
|
-
|
|
758
|
-
var schema = operation.getResponseAsJSONSchema(status, {
|
|
686
|
+
const schemas = responseStatusCodes
|
|
687
|
+
.map(status => {
|
|
688
|
+
const schema = operation.getResponseAsJSONSchema(status, {
|
|
759
689
|
includeDiscriminatorMappingRefs: false,
|
|
760
|
-
transformer:
|
|
690
|
+
transformer: (s) => {
|
|
761
691
|
// As our schemas are dereferenced in the `oas` library we don't want to pollute our
|
|
762
692
|
// codegen'd schemas file with duplicate schemas.
|
|
763
693
|
if ('x-readme-ref-name' in s && typeof s['x-readme-ref-name'] !== 'undefined') {
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
return
|
|
694
|
+
const typeName = (0, util_1.generateTypeName)(s['x-readme-ref-name']);
|
|
695
|
+
this.addSchemaToExport(s, typeName, `${typeName}`);
|
|
696
|
+
return `::convert::${typeName}`;
|
|
767
697
|
}
|
|
768
698
|
return s;
|
|
769
699
|
},
|
|
@@ -771,16 +701,14 @@ var TSGenerator = /** @class */ (function (_super) {
|
|
|
771
701
|
if (!schema) {
|
|
772
702
|
return false;
|
|
773
703
|
}
|
|
774
|
-
return
|
|
775
|
-
|
|
776
|
-
|
|
704
|
+
return {
|
|
705
|
+
[status]: schema.shift(),
|
|
706
|
+
};
|
|
777
707
|
})
|
|
778
|
-
.reduce(
|
|
779
|
-
|
|
780
|
-
.map(
|
|
781
|
-
|
|
782
|
-
var status = _a[0], _c = _a[1], description = _c.description, schema = _c.schema;
|
|
783
|
-
var typeName;
|
|
708
|
+
.reduce((prev, next) => Object.assign(prev, next));
|
|
709
|
+
const res = Object.entries(schemas)
|
|
710
|
+
.map(([status, { description, schema }]) => {
|
|
711
|
+
let typeName;
|
|
784
712
|
if (typeof schema === 'string' && schema.startsWith('::convert::')) {
|
|
785
713
|
// If this schema is a string and has our conversion prefix then we've already created
|
|
786
714
|
// a type for it.
|
|
@@ -791,31 +719,30 @@ var TSGenerator = /** @class */ (function (_super) {
|
|
|
791
719
|
// Because `status` will usually be a number here we need to set the pointer for it
|
|
792
720
|
// within an `[]` as if we do `FromSchema<typeof schemas.operation.response.200>`,
|
|
793
721
|
// TypeScript will throw a compilation error.
|
|
794
|
-
|
|
722
|
+
this.addSchemaToExport(schema, typeName, `${(0, util_1.generateTypeName)(operationId)}.response['${status}']`);
|
|
795
723
|
}
|
|
796
|
-
return
|
|
724
|
+
return {
|
|
797
725
|
// Types are prefixed with `types.` because that's how we're importing them from
|
|
798
726
|
// `types.d.ts`.
|
|
799
|
-
|
|
800
|
-
type:
|
|
801
|
-
description
|
|
727
|
+
[status]: {
|
|
728
|
+
type: `types.${typeName}`,
|
|
729
|
+
description,
|
|
802
730
|
},
|
|
803
|
-
|
|
731
|
+
};
|
|
804
732
|
})
|
|
805
|
-
.reduce(
|
|
733
|
+
.reduce((prev, next) => Object.assign(prev, next), {});
|
|
806
734
|
return Object.keys(res).length ? res : undefined;
|
|
807
|
-
}
|
|
735
|
+
}
|
|
808
736
|
/**
|
|
809
737
|
* Add a given schema into our schema dataset that we'll be be exporting as types.
|
|
810
738
|
*
|
|
811
739
|
*/
|
|
812
|
-
|
|
740
|
+
addSchemaToExport(schema, typeName, pointer) {
|
|
813
741
|
if (this.types.has(typeName)) {
|
|
814
742
|
return;
|
|
815
743
|
}
|
|
816
744
|
(0, lodash_setwith_1.default)(this.schemas, pointer, schema, Object);
|
|
817
|
-
this.types.set(typeName,
|
|
818
|
-
}
|
|
819
|
-
|
|
820
|
-
}(language_1.default));
|
|
745
|
+
this.types.set(typeName, `FromSchema<typeof schemas.${pointer}>`);
|
|
746
|
+
}
|
|
747
|
+
}
|
|
821
748
|
exports.default = TSGenerator;
|