apigen-ts 0.1.0 → 0.1.2

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/_template.ts CHANGED
@@ -1,11 +1,11 @@
1
1
  // Note: Use uppercase for names in ApiClient to avoid conflict with the generated code
2
2
 
3
- interface ApigenConfig {
3
+ export interface ApigenConfig {
4
4
  baseUrl: string
5
5
  headers: Record<string, string>
6
6
  }
7
7
 
8
- interface ApigenRequest extends Omit<RequestInit, "body"> {
8
+ export interface ApigenRequest extends Omit<RequestInit, "body"> {
9
9
  search?: Record<string, unknown>
10
10
  body?: unknown
11
11
  }
@@ -42,8 +42,10 @@ export class ApiClient {
42
42
 
43
43
  async Fetch<T>(method: string, path: string, opts: ApigenRequest = {}): Promise<T> {
44
44
  let base = this.Config.baseUrl
45
- if (globalThis.location && (base === "" || base.startsWith("/"))) {
46
- base = `${globalThis.location.origin}${base.endsWith("/") ? base : `/${base}`}`
45
+ if ("location" in globalThis && (base === "" || base.startsWith("/"))) {
46
+ // make ts happy in pure nodejs environment, should never pass here
47
+ const { location } = globalThis as unknown as { location: { origin: string } }
48
+ base = `${location.origin}${base.endsWith("/") ? base : `/${base}`}`
47
49
  }
48
50
 
49
51
  const url = new URL(path, base)
@@ -57,7 +59,8 @@ export class ApiClient {
57
59
  let body: FormData | URLSearchParams | string | undefined = undefined
58
60
 
59
61
  if (ct === "multipart/form-data" || ct === "application/x-www-form-urlencoded") {
60
- headers.delete("content-type") // https://stackoverflow.com/a/61053359/3664464
62
+ // https://stackoverflow.com/a/61053359/3664464
63
+ headers.delete("content-type")
61
64
  body = ct === "multipart/form-data" ? new FormData() : new URLSearchParams()
62
65
  for (const [k, v] of Object.entries(opts.body as Record<string, string>)) {
63
66
  body.append(k, v)
@@ -75,7 +78,7 @@ export class ApiClient {
75
78
 
76
79
  const rs = await rep.text()
77
80
  try {
78
- return this.PopulateDates(JSON.parse(rs))
81
+ return this.PopulateDates(JSON.parse(rs) as T)
79
82
  } catch (e) {
80
83
  return rs as unknown as T
81
84
  }
package/dist/cli.cjs CHANGED
@@ -1,6 +1,6 @@
1
1
  'use strict';
2
2
 
3
- var main$1 = require('./main-0ae61014.cjs');
3
+ var main$1 = require('./main-c2426ec2.cjs');
4
4
  require('fs/promises');
5
5
  require('path');
6
6
  require('url');
@@ -10,7 +10,7 @@ require('array-utils-ts');
10
10
  require('lodash-es');
11
11
  require('swagger2openapi');
12
12
  require('typescript');
13
- require('prettier');
13
+ require('node:path');
14
14
 
15
15
  const main = async () => {
16
16
  await main$1.apigen(main$1.getCliConfig());
package/dist/cli.js CHANGED
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- import { a as apigen, g as getCliConfig } from './main-0c2fa229.js';
2
+ import { a as apigen, g as getCliConfig } from './main-bbe33f1b.js';
3
3
  import 'fs/promises';
4
4
  import 'path';
5
5
  import 'url';
@@ -9,7 +9,7 @@ import 'array-utils-ts';
9
9
  import 'lodash-es';
10
10
  import 'swagger2openapi';
11
11
  import 'typescript';
12
- import 'prettier';
12
+ import 'node:path';
13
13
 
14
14
  const main = async () => {
15
15
  await apigen(getCliConfig());
package/dist/cli.mjs CHANGED
@@ -1,4 +1,4 @@
1
- import { a as apigen, g as getCliConfig } from './main-0c2fa229.mjs';
1
+ import { a as apigen, g as getCliConfig } from './main-bbe33f1b.mjs';
2
2
  import 'fs/promises';
3
3
  import 'path';
4
4
  import 'url';
@@ -8,7 +8,7 @@ import 'array-utils-ts';
8
8
  import 'lodash-es';
9
9
  import 'swagger2openapi';
10
10
  import 'typescript';
11
- import 'prettier';
11
+ import 'node:path';
12
12
 
13
13
  const main = async () => {
14
14
  await apigen(getCliConfig());
@@ -7,7 +7,7 @@ import { filterEmpty, filterNullable } from 'array-utils-ts';
7
7
  import { get, uniq, upperFirst, isArray, isObject, sortBy, lowerFirst, uniqBy } from 'lodash-es';
8
8
  import { convertObj } from 'swagger2openapi';
9
9
  import ts from 'typescript';
10
- import * as prettier from 'prettier';
10
+ import path from 'node:path';
11
11
 
12
12
  const initCtx = (config) => {
13
13
  return {
@@ -24,8 +24,8 @@ const initCtx = (config) => {
24
24
  const getCliConfig = () => {
25
25
  const argv = cli({
26
26
  name: "apigen",
27
- // version: "0.0.1",
28
- parameters: ["<source>", "<output>"],
27
+ version: "0.1.1",
28
+ parameters: ["<source>", "[output]"],
29
29
  flags: {
30
30
  name: {
31
31
  type: String,
@@ -41,7 +41,7 @@ const getCliConfig = () => {
41
41
  });
42
42
  const config = {
43
43
  source: argv._.source,
44
- output: argv._.output,
44
+ output: argv._.output ?? null,
45
45
  name: argv.flags.name,
46
46
  parseDates: argv.flags.parseDates
47
47
  };
@@ -163,7 +163,8 @@ const Keywords = /* @__PURE__ */ new Set([
163
163
  "Extract",
164
164
  // ts keywords
165
165
  "Date",
166
- "object"
166
+ "object",
167
+ "Response"
167
168
  // ts type names
168
169
  ]);
169
170
  const normalizeIdentifier = (val, asVar = false) => {
@@ -498,6 +499,8 @@ const generateAst = async (ctx) => {
498
499
  return { modules, types };
499
500
  };
500
501
  const loadSchema = async (url, upgrade = true) => {
502
+ if (url.startsWith("file://"))
503
+ url = url.substring(7);
501
504
  const { bundle } = await redocly.bundle({
502
505
  ref: url,
503
506
  config: await redocly.createConfig({}),
@@ -529,8 +532,13 @@ const printCode = (nodes) => {
529
532
  ).replaceAll("}, ", "},\n\n");
530
533
  };
531
534
  const formatCode = async (code) => {
532
- const options = await prettier.resolveConfig(import.meta.url);
533
- return prettier.format(code, { ...options, parser: "typescript" });
535
+ try {
536
+ const prettier = await import('prettier');
537
+ const options = await prettier.resolveConfig(path.join(process.cwd(), "file"));
538
+ return prettier.format(code, { ...options, parser: "typescript" });
539
+ } catch (e) {
540
+ return code;
541
+ }
534
542
  };
535
543
 
536
544
  const apigen = async (config) => {
@@ -543,17 +551,24 @@ const apigen = async (config) => {
543
551
  `// Auto-generated by https://github.com/vladkens/apigen-ts`,
544
552
  `// Source: ${config.source}
545
553
  `,
546
- ...file.split("\n").filter((x) => !x.startsWith("// Note: "))
554
+ // remove all comments expect which starts with "// apigen:"
555
+ ...file.split("\n").filter((x) => !/\s*\/\/\s(?!apigen:)/.test(x))
547
556
  ].join("\n");
548
557
  if (!ctx.parseDates) {
558
+ code = code.replace(/\s*ISO_FORMAT\s=.+$/gm, "");
549
559
  code = code.replace(/PopulateDates.+?\n\s{2}\}/s, "");
550
560
  code = code.replace(/this.PopulateDates\((.+)\)/, "$1");
551
561
  }
552
562
  code = code.replace("// apigen:modules", printCode(modules));
553
563
  code = code.replace("// apigen:types", printCode(types));
554
564
  code = code.replace("class ApiClient", `class ${ctx.name}`);
555
- await fs.mkdir(dirname(config.output), { recursive: true });
556
- await fs.writeFile(config.output, await formatCode(code));
565
+ const result = await formatCode(code);
566
+ if (config.output === null) {
567
+ process.stdout.write(result);
568
+ } else {
569
+ await fs.mkdir(dirname(config.output), { recursive: true });
570
+ await fs.writeFile(config.output, result);
571
+ }
557
572
  };
558
573
 
559
574
  export { apigen as a, getCliConfig as g };
@@ -7,7 +7,7 @@ import { filterEmpty, filterNullable } from 'array-utils-ts';
7
7
  import { get, uniq, upperFirst, isArray, isObject, sortBy, lowerFirst, uniqBy } from 'lodash-es';
8
8
  import { convertObj } from 'swagger2openapi';
9
9
  import ts from 'typescript';
10
- import * as prettier from 'prettier';
10
+ import path from 'node:path';
11
11
 
12
12
  const initCtx = (config) => {
13
13
  return {
@@ -24,8 +24,8 @@ const initCtx = (config) => {
24
24
  const getCliConfig = () => {
25
25
  const argv = cli({
26
26
  name: "apigen",
27
- // version: "0.0.1",
28
- parameters: ["<source>", "<output>"],
27
+ version: "0.1.1",
28
+ parameters: ["<source>", "[output]"],
29
29
  flags: {
30
30
  name: {
31
31
  type: String,
@@ -41,7 +41,7 @@ const getCliConfig = () => {
41
41
  });
42
42
  const config = {
43
43
  source: argv._.source,
44
- output: argv._.output,
44
+ output: argv._.output ?? null,
45
45
  name: argv.flags.name,
46
46
  parseDates: argv.flags.parseDates
47
47
  };
@@ -163,7 +163,8 @@ const Keywords = /* @__PURE__ */ new Set([
163
163
  "Extract",
164
164
  // ts keywords
165
165
  "Date",
166
- "object"
166
+ "object",
167
+ "Response"
167
168
  // ts type names
168
169
  ]);
169
170
  const normalizeIdentifier = (val, asVar = false) => {
@@ -498,6 +499,8 @@ const generateAst = async (ctx) => {
498
499
  return { modules, types };
499
500
  };
500
501
  const loadSchema = async (url, upgrade = true) => {
502
+ if (url.startsWith("file://"))
503
+ url = url.substring(7);
501
504
  const { bundle } = await redocly.bundle({
502
505
  ref: url,
503
506
  config: await redocly.createConfig({}),
@@ -529,8 +532,13 @@ const printCode = (nodes) => {
529
532
  ).replaceAll("}, ", "},\n\n");
530
533
  };
531
534
  const formatCode = async (code) => {
532
- const options = await prettier.resolveConfig(import.meta.url);
533
- return prettier.format(code, { ...options, parser: "typescript" });
535
+ try {
536
+ const prettier = await import('prettier');
537
+ const options = await prettier.resolveConfig(path.join(process.cwd(), "file"));
538
+ return prettier.format(code, { ...options, parser: "typescript" });
539
+ } catch (e) {
540
+ return code;
541
+ }
534
542
  };
535
543
 
536
544
  const apigen = async (config) => {
@@ -543,17 +551,24 @@ const apigen = async (config) => {
543
551
  `// Auto-generated by https://github.com/vladkens/apigen-ts`,
544
552
  `// Source: ${config.source}
545
553
  `,
546
- ...file.split("\n").filter((x) => !x.startsWith("// Note: "))
554
+ // remove all comments expect which starts with "// apigen:"
555
+ ...file.split("\n").filter((x) => !/\s*\/\/\s(?!apigen:)/.test(x))
547
556
  ].join("\n");
548
557
  if (!ctx.parseDates) {
558
+ code = code.replace(/\s*ISO_FORMAT\s=.+$/gm, "");
549
559
  code = code.replace(/PopulateDates.+?\n\s{2}\}/s, "");
550
560
  code = code.replace(/this.PopulateDates\((.+)\)/, "$1");
551
561
  }
552
562
  code = code.replace("// apigen:modules", printCode(modules));
553
563
  code = code.replace("// apigen:types", printCode(types));
554
564
  code = code.replace("class ApiClient", `class ${ctx.name}`);
555
- await fs.mkdir(dirname(config.output), { recursive: true });
556
- await fs.writeFile(config.output, await formatCode(code));
565
+ const result = await formatCode(code);
566
+ if (config.output === null) {
567
+ process.stdout.write(result);
568
+ } else {
569
+ await fs.mkdir(dirname(config.output), { recursive: true });
570
+ await fs.writeFile(config.output, result);
571
+ }
557
572
  };
558
573
 
559
574
  export { apigen as a, getCliConfig as g };
@@ -1,7 +1,7 @@
1
1
  'use strict';
2
2
 
3
3
  var fs = require('fs/promises');
4
- var path = require('path');
4
+ var path$1 = require('path');
5
5
  var url = require('url');
6
6
  var cleye = require('cleye');
7
7
  var redocly = require('@redocly/openapi-core');
@@ -9,28 +9,9 @@ var arrayUtilsTs = require('array-utils-ts');
9
9
  var lodashEs = require('lodash-es');
10
10
  var swagger2openapi = require('swagger2openapi');
11
11
  var ts = require('typescript');
12
- var prettier = require('prettier');
12
+ var path = require('node:path');
13
13
 
14
14
  var _documentCurrentScript = typeof document !== 'undefined' ? document.currentScript : null;
15
- function _interopNamespaceDefault(e) {
16
- var n = Object.create(null);
17
- if (e) {
18
- Object.keys(e).forEach(function (k) {
19
- if (k !== 'default') {
20
- var d = Object.getOwnPropertyDescriptor(e, k);
21
- Object.defineProperty(n, k, d.get ? d : {
22
- enumerable: true,
23
- get: function () { return e[k]; }
24
- });
25
- }
26
- });
27
- }
28
- n.default = e;
29
- return Object.freeze(n);
30
- }
31
-
32
- var prettier__namespace = /*#__PURE__*/_interopNamespaceDefault(prettier);
33
-
34
15
  const initCtx = (config) => {
35
16
  return {
36
17
  source: "",
@@ -46,8 +27,8 @@ const initCtx = (config) => {
46
27
  const getCliConfig = () => {
47
28
  const argv = cleye.cli({
48
29
  name: "apigen",
49
- // version: "0.0.1",
50
- parameters: ["<source>", "<output>"],
30
+ version: "0.1.1",
31
+ parameters: ["<source>", "[output]"],
51
32
  flags: {
52
33
  name: {
53
34
  type: String,
@@ -63,7 +44,7 @@ const getCliConfig = () => {
63
44
  });
64
45
  const config = {
65
46
  source: argv._.source,
66
- output: argv._.output,
47
+ output: argv._.output ?? null,
67
48
  name: argv.flags.name,
68
49
  parseDates: argv.flags.parseDates
69
50
  };
@@ -185,7 +166,8 @@ const Keywords = /* @__PURE__ */ new Set([
185
166
  "Extract",
186
167
  // ts keywords
187
168
  "Date",
188
- "object"
169
+ "object",
170
+ "Response"
189
171
  // ts type names
190
172
  ]);
191
173
  const normalizeIdentifier = (val, asVar = false) => {
@@ -520,6 +502,8 @@ const generateAst = async (ctx) => {
520
502
  return { modules, types };
521
503
  };
522
504
  const loadSchema = async (url, upgrade = true) => {
505
+ if (url.startsWith("file://"))
506
+ url = url.substring(7);
523
507
  const { bundle } = await redocly.bundle({
524
508
  ref: url,
525
509
  config: await redocly.createConfig({}),
@@ -551,31 +535,43 @@ const printCode = (nodes) => {
551
535
  ).replaceAll("}, ", "},\n\n");
552
536
  };
553
537
  const formatCode = async (code) => {
554
- const options = await prettier__namespace.resolveConfig((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.src || new URL('main-0ae61014.cjs', document.baseURI).href)));
555
- return prettier__namespace.format(code, { ...options, parser: "typescript" });
538
+ try {
539
+ const prettier = await import('prettier');
540
+ const options = await prettier.resolveConfig(path.join(process.cwd(), "file"));
541
+ return prettier.format(code, { ...options, parser: "typescript" });
542
+ } catch (e) {
543
+ return code;
544
+ }
556
545
  };
557
546
 
558
547
  const apigen = async (config) => {
559
548
  const doc = await loadSchema(config.source);
560
549
  const ctx = initCtx({ ...config, doc });
561
550
  const { modules, types } = await generateAst(ctx);
562
- const filepath = path.join(path.dirname(url.fileURLToPath((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.src || new URL('main-0ae61014.cjs', document.baseURI).href)))), "_template.ts");
551
+ const filepath = path$1.join(path$1.dirname(url.fileURLToPath((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.src || new URL('main-c2426ec2.cjs', document.baseURI).href)))), "_template.ts");
563
552
  const file = await fs.readFile(filepath, "utf-8");
564
553
  let code = [
565
554
  `// Auto-generated by https://github.com/vladkens/apigen-ts`,
566
555
  `// Source: ${config.source}
567
556
  `,
568
- ...file.split("\n").filter((x) => !x.startsWith("// Note: "))
557
+ // remove all comments expect which starts with "// apigen:"
558
+ ...file.split("\n").filter((x) => !/\s*\/\/\s(?!apigen:)/.test(x))
569
559
  ].join("\n");
570
560
  if (!ctx.parseDates) {
561
+ code = code.replace(/\s*ISO_FORMAT\s=.+$/gm, "");
571
562
  code = code.replace(/PopulateDates.+?\n\s{2}\}/s, "");
572
563
  code = code.replace(/this.PopulateDates\((.+)\)/, "$1");
573
564
  }
574
565
  code = code.replace("// apigen:modules", printCode(modules));
575
566
  code = code.replace("// apigen:types", printCode(types));
576
567
  code = code.replace("class ApiClient", `class ${ctx.name}`);
577
- await fs.mkdir(path.dirname(config.output), { recursive: true });
578
- await fs.writeFile(config.output, await formatCode(code));
568
+ const result = await formatCode(code);
569
+ if (config.output === null) {
570
+ process.stdout.write(result);
571
+ } else {
572
+ await fs.mkdir(path$1.dirname(config.output), { recursive: true });
573
+ await fs.writeFile(config.output, result);
574
+ }
579
575
  };
580
576
 
581
577
  exports.apigen = apigen;
package/dist/main.cjs CHANGED
@@ -3,14 +3,14 @@
3
3
  require('fs/promises');
4
4
  require('path');
5
5
  require('url');
6
- var main = require('./main-0ae61014.cjs');
6
+ var main = require('./main-c2426ec2.cjs');
7
7
  require('cleye');
8
8
  require('@redocly/openapi-core');
9
9
  require('array-utils-ts');
10
10
  require('lodash-es');
11
11
  require('swagger2openapi');
12
12
  require('typescript');
13
- require('prettier');
13
+ require('node:path');
14
14
 
15
15
 
16
16
 
package/dist/main.d.cts CHANGED
@@ -8,7 +8,7 @@ type OpConfig = Oas3Operation & {
8
8
  type OpName = [string, string];
9
9
  type Config = {
10
10
  source: string;
11
- output: string;
11
+ output: string | null;
12
12
  name: string;
13
13
  parseDates: boolean;
14
14
  resolveName?: (ctx: Context, op: OpConfig, proposal: OpName) => OpName | undefined;
package/dist/main.d.mts CHANGED
@@ -8,7 +8,7 @@ type OpConfig = Oas3Operation & {
8
8
  type OpName = [string, string];
9
9
  type Config = {
10
10
  source: string;
11
- output: string;
11
+ output: string | null;
12
12
  name: string;
13
13
  parseDates: boolean;
14
14
  resolveName?: (ctx: Context, op: OpConfig, proposal: OpName) => OpName | undefined;
package/dist/main.js CHANGED
@@ -1,11 +1,11 @@
1
1
  import 'fs/promises';
2
2
  import 'path';
3
3
  import 'url';
4
- export { a as apigen } from './main-0c2fa229.js';
4
+ export { a as apigen } from './main-bbe33f1b.js';
5
5
  import 'cleye';
6
6
  import '@redocly/openapi-core';
7
7
  import 'array-utils-ts';
8
8
  import 'lodash-es';
9
9
  import 'swagger2openapi';
10
10
  import 'typescript';
11
- import 'prettier';
11
+ import 'node:path';
package/dist/main.mjs CHANGED
@@ -1,11 +1,11 @@
1
1
  import 'fs/promises';
2
2
  import 'path';
3
3
  import 'url';
4
- export { a as apigen } from './main-0c2fa229.mjs';
4
+ export { a as apigen } from './main-bbe33f1b.mjs';
5
5
  import 'cleye';
6
6
  import '@redocly/openapi-core';
7
7
  import 'array-utils-ts';
8
8
  import 'lodash-es';
9
9
  import 'swagger2openapi';
10
10
  import 'typescript';
11
- import 'prettier';
11
+ import 'node:path';
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "type": "module",
3
3
  "name": "apigen-ts",
4
- "version": "0.1.0",
4
+ "version": "0.1.2",
5
5
  "license": "MIT",
6
6
  "author": "Vlad Pronsky <v.pronsky@gmail.com>",
7
7
  "repository": "vladkens/apigen-ts",
@@ -22,7 +22,7 @@
22
22
  "ci": "tsc --noEmit && yarn test-cov && yarn build"
23
23
  },
24
24
  "dependencies": {
25
- "@redocly/openapi-core": "^1.4.1",
25
+ "@redocly/openapi-core": "^1.6.0",
26
26
  "@types/lodash-es": "^4.17.12",
27
27
  "@types/swagger2openapi": "^7.0.4",
28
28
  "array-utils-ts": "^0.1.2",
@@ -31,8 +31,8 @@
31
31
  "swagger2openapi": "^7.0.8"
32
32
  },
33
33
  "devDependencies": {
34
- "@types/node": "^20.10.5",
35
- "c8": "^8.0.1",
34
+ "@types/node": "^20.10.8",
35
+ "c8": "^9.0.0",
36
36
  "fetch-mock": "^9.11.0",
37
37
  "pkgroll": "^2.0.1",
38
38
  "prettier": "^3.1.0",
@@ -48,6 +48,7 @@
48
48
  "files": [
49
49
  "dist"
50
50
  ],
51
+ "main": "./dist/main.js",
51
52
  "types": "./dist/main.d.cts",
52
53
  "exports": {
53
54
  "require": {
package/readme.md CHANGED
@@ -3,31 +3,28 @@
3
3
  <div align="center">
4
4
 
5
5
  [<img src="https://badgen.net/npm/v/apigen-ts" alt="version" />](https://npmjs.org/package/apigen-ts)
6
- [<img src="https://github.com/vladkens/apigen-ts/workflows/test/badge.svg" alt="test status" />](https://github.com/vladkens/apigen-ts/actions)
7
6
  [<img src="https://badgen.net/packagephobia/publish/apigen-ts" alt="size" />](https://packagephobia.now.sh/result?p=apigen-ts)
8
7
  [<img src="https://badgen.net/npm/dm/apigen-ts" alt="downloads" />](https://npmjs.org/package/apigen-ts)
9
8
  [<img src="https://badgen.net/github/license/vladkens/apigen-ts" alt="license" />](https://github.com/vladkens/apigen-ts/blob/main/LICENSE)
9
+ [<img src="https://badgen.net/static/-/buy%20me%20a%20coffee/ff813f?icon=buymeacoffee&label" alt="donate" />](https://buymeacoffee.com/vladkens)
10
10
 
11
11
  </div>
12
12
 
13
13
  <div align="center">
14
14
  <img src="./logo.svg" alt="apigen-ts logo" height="80" />
15
- </div>
16
-
17
- <div align="center">
18
- TypeScript api client generator from OpenAPI specification
15
+ <div>TypeScript client generator from OpenAPI schema</div>
19
16
  </div>
20
17
 
21
18
  ## Features
22
19
 
23
20
  - Generates ready to use `ApiClient` with types (using `fetch`)
24
21
  - Single output file, minimal third-party code
25
- - Load schema from JSON / YAML, locally and remote
22
+ - Loads schemas from JSON / YAML, locally and remote
26
23
  - Ability to customize `fetch` with your custom function
27
- - Uses `type` instead of `interface`, no problem with declaration merging
28
24
  - Automatic formating with Prettier
29
25
  - Can parse dates from date-time format (`--parse-dates` flag)
30
26
  - Support OpenAPI v2, v3, v3.1
27
+ - Can be used with npx as well
31
28
 
32
29
  ## Install
33
30
 
@@ -37,7 +34,7 @@ yarn install -D apigen-ts
37
34
 
38
35
  ## Usage
39
36
 
40
- ### Generate
37
+ ### 1. Generate
41
38
 
42
39
  ```sh
43
40
  # From url
@@ -47,11 +44,11 @@ yarn apigen-ts https://petstore3.swagger.io/api/v3/openapi.json ./api-client.ts
47
44
  yarn apigen-ts ./openapi.json ./api-client.ts
48
45
  ```
49
46
 
50
- Run `yarn apigen-ts --help` for more options. See examples of generated clients [here](./examples/).
47
+ Run `yarn apigen-ts --help` for more options. Examples of generated clients [here](./examples/).
51
48
 
52
- ### Import
49
+ ### 2. Import
53
50
 
54
- ```typescript
51
+ ```ts
55
52
  import { ApiClient } from "./api-client"
56
53
 
57
54
  const api = new ApiClient({
@@ -60,9 +57,9 @@ const api = new ApiClient({
60
57
  })
61
58
  ```
62
59
 
63
- ### Use
60
+ ### 3. Use
64
61
 
65
- ```typescript
62
+ ```ts
66
63
  // GET /pet/{petId}
67
64
  await api.pet.getPetById(1) // -> Pet
68
65
 
@@ -77,18 +74,59 @@ await api.user.updateUser("username", { firstName: "John" })
77
74
 
78
75
  ### Login flow
79
76
 
80
- ```typescript
77
+ ```ts
81
78
  const { token } = await api.auth.login({ usename, password })
82
79
  api.Config.headers = { Authorization: token }
83
80
 
84
81
  await api.protectedRoute.get() // here authenticated
85
82
  ```
86
83
 
87
- ### NodeJS API
84
+ ### Automatic date parsing
85
+
86
+ ```sh
87
+ yarn apigen-ts ./openapi.json ./api-client.ts --parse-dates
88
+ ```
89
+
90
+ ```ts
91
+ const pet = await api.pet.getPetById(1)
92
+ const createdAt: Date = pet.createdAt // date parsed from string with format=date-time
93
+ ```
94
+
95
+ ### Errors handling
96
+
97
+ An exception will be thrown for all unsuccessful return codes.
98
+
99
+ ```ts
100
+ try {
101
+ const pet = await api.pet.getPetById(404)
102
+ } catch (e) {
103
+ console.log(e) // parse error depend of your domain model, e is awaited response.json()
104
+ }
105
+ ```
106
+
107
+ Also you can define custom function to parse error:
108
+
109
+ ```ts
110
+ class MyClient extends ApiClient {
111
+ async ParseError(rep: Response) {
112
+ // do what you want
113
+ return { code: "API_ERROR" }
114
+ }
115
+ }
116
+
117
+ try {
118
+ const api = MyClient()
119
+ const pet = await api.pet.getPetById(404)
120
+ } catch (e) {
121
+ console.log(e) // e is { code: "API_ERROR" }
122
+ }
123
+ ```
124
+
125
+ ### Node.js API
88
126
 
89
127
  Create file like `apigen.mjs` with content:
90
128
 
91
- ```javascript
129
+ ```js
92
130
  import { apigen } from "apigen-ts"
93
131
 
94
132
  await apigen({
@@ -109,7 +147,14 @@ await apigen({
109
147
 
110
148
  Then run with: `node apigen.mjs`
111
149
 
112
- ## Useful for development
150
+ ## Compare
151
+
152
+ - [openapi-typescript-codegen](https://github.com/ferdikoomen/openapi-typescript-codegen) ([npm](https://www.npmjs.com/package/openapi-typescript-codegen)): no single file mode [#1263](https://github.com/ferdikoomen/openapi-typescript-codegen/issues/1263#issuecomment-1502890838)
153
+ - [openapi-typescript](https://github.com/drwpow/openapi-typescript) ([npm](https://www.npmjs.com/package/openapi-typescript)): low level api; no named types export to use in client code
154
+ - [openapi-generator-cli](https://github.com/OpenAPITools/openapi-generator-cli) ([npm](https://www.npmjs.com/package/@openapitools/openapi-generator-cli)): wrapper around java lib
155
+ - [swagger-typescript-api](https://github.com/acacode/swagger-typescript-api) ([npm](https://www.npmjs.com/package/swagger-typescript-api)): complicated configuration; user-api breaking changes between versions
156
+
157
+ ## Development
113
158
 
114
159
  - https://ts-ast-viewer.com
115
160
  - https://jsonschemalint.com