@ps-aux/api-client-gen 0.0.5-rc2 → 0.0.6-rc1

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.esm.js CHANGED
@@ -1,5 +1,5 @@
1
1
  #!/bin/env node
2
- import { generateApiClient } from './index.esm.js';
2
+ import { g as generateApiClient } from './generateApiClient-BSM0HlFv.js';
3
3
  import { cosmiconfigSync } from 'cosmiconfig';
4
4
  import 'path';
5
5
  import 'fs';
package/dist/bin.js CHANGED
@@ -1,7 +1,7 @@
1
1
  #!/bin/env node
2
2
  'use strict';
3
3
 
4
- var index = require('./index.js');
4
+ var generateApiClient = require('./generateApiClient-DBkHX8se.js');
5
5
  var cosmiconfig = require('cosmiconfig');
6
6
  require('path');
7
7
  require('fs');
@@ -21,4 +21,4 @@ if (!conf)
21
21
  const profileConf = conf.config[profile];
22
22
  if (!profile)
23
23
  throw new Error(`Profile ${profile} not present in the configuration`);
24
- index.generateApiClient(profileConf).catch(console.error);
24
+ generateApiClient.generateApiClient(profileConf).catch(console.error);
@@ -0,0 +1,162 @@
1
+ import Path from 'path';
2
+ import * as fs from 'fs';
3
+ import fs__default from 'fs';
4
+ import axios from 'axios';
5
+ import fs$1 from 'fs/promises';
6
+ import { generateApi } from 'swagger-typescript-api';
7
+ import { generate } from 'ts-to-zod';
8
+
9
+ const downloadSpec = async (path, url) => {
10
+ const res = await axios({
11
+ url,
12
+ method: 'GET',
13
+ responseType: 'arraybuffer',
14
+ }).catch(err => {
15
+ throw err.toString();
16
+ });
17
+ const content = res.data.toString();
18
+ await fs$1.writeFile(path, format(content));
19
+ };
20
+ const format = (content) => JSON.stringify(JSON.parse(content), null, 4);
21
+
22
+ const generateOpenApiModel = async ({ input, isNode, responseWrapper, name, outputDir }, log) => {
23
+ log(`Will generate API client name=${name} to ${outputDir}, node=${isNode}`);
24
+ const specialMapping = isNode
25
+ ? {
26
+ File: '{file: Buffer | stream.Readable, name: string}',
27
+ }
28
+ : {};
29
+ const fileName = 'index';
30
+ const dstFile = Path.join(outputDir, `${fileName}.ts`);
31
+ await generateApi({
32
+ name: 'index',
33
+ output: outputDir,
34
+ input: input,
35
+ // modular: true,
36
+ httpClientType: 'axios',
37
+ templates: Path.resolve(getThisScriptDirname(), '../templates'),
38
+ defaultResponseAsSuccess: true,
39
+ // generateRouteTypes: true,
40
+ singleHttpClient: true,
41
+ // extractRequestBody: true,
42
+ cleanOutput: true,
43
+ moduleNameFirstTag: true,
44
+ apiClassName: capitalize(name) + 'Api',
45
+ // @ts-ignore
46
+ codeGenConstructs: (struct) => ({
47
+ // @ts-ignore
48
+ Keyword: specialMapping,
49
+ }),
50
+ // extractRequestParams: true,
51
+ });
52
+ const addImports = [];
53
+ const replaces = [];
54
+ if (isNode) {
55
+ addImports.push('import stream from \'node:stream\'');
56
+ }
57
+ if (responseWrapper) {
58
+ log(`Will use response wrapper '${JSON.stringify(responseWrapper)}'`);
59
+ if (responseWrapper.import)
60
+ addImports.push(responseWrapper.import);
61
+ replaces.push(['Promise', responseWrapper.symbol]);
62
+ }
63
+ // Remove unnecessary generics
64
+ replaces.push(['<SecurityDataType extends unknown>', '']);
65
+ replaces.push(['<SecurityDataType>', '']);
66
+ log(`Will modify the outputs ${JSON.stringify({
67
+ addImports,
68
+ replaces,
69
+ })}`);
70
+ await modifyOutput(dstFile, {
71
+ addImports,
72
+ replaces,
73
+ });
74
+ return dstFile;
75
+ };
76
+ const modifyOutput = async (path, cmd) => {
77
+ let content = await fs$1.readFile(path).then((r) => r.toString());
78
+ if (cmd.addImports.length) {
79
+ content = cmd.addImports.join('\n') + '\n' + content;
80
+ }
81
+ cmd.replaces.forEach(([from, to]) => {
82
+ content = content.replaceAll(from, to);
83
+ });
84
+ await fs$1.writeFile(path, content);
85
+ };
86
+ const getThisScriptDirname = () => {
87
+ // Might be problem in ESM mode
88
+ return __dirname;
89
+ };
90
+ const capitalize = (str) => {
91
+ return str[0].toUpperCase() + str.substring(1);
92
+ };
93
+
94
+ // TODO needed?
95
+ const removeGenericTypes = (code) => {
96
+ const regex = /export (interface|enum|type) [^<]* \{\n(.*\n)*?}/mg;
97
+ const matches = code.matchAll(regex);
98
+ const types = Array.from(matches).map(m => m[0]);
99
+ return types.join('\n');
100
+ };
101
+ const generateSchemas = (inputFile, outputFile, opts = {}) => {
102
+ const code = fs__default.readFileSync(inputFile).toString();
103
+ const { getZodSchemasFile } = generate({
104
+ sourceText: removeGenericTypes(code),
105
+ customJSDocFormatTypes: {
106
+ // Custom mapping
107
+ // 'date-time': 'blablaj' - regex
108
+ },
109
+ });
110
+ const fileName = Path.basename(inputFile);
111
+ let f = getZodSchemasFile(`./${fileName.replace('.ts', '')}`);
112
+ // Backend sends nulls not undefined - should be properly set in the OpenAPI TS types generation!
113
+ f = f.replaceAll('.optional()', '.nullable()');
114
+ if (opts.localDateTimes) {
115
+ f = f.replaceAll('z.string().datetime()', 'z.string().datetime({local: true})');
116
+ }
117
+ fs__default.writeFileSync(outputFile, f);
118
+ };
119
+
120
+ const generateApiClient = async ({ dstDir, apiName, env, srcSpec, responseWrapper, zodSchemas, }, log = console.log) => {
121
+ if (!srcSpec)
122
+ throw new Error(`Url or path ('srcSpec' not specified`);
123
+ if (!apiName)
124
+ throw new Error('apiName not specified');
125
+ if (!dstDir)
126
+ throw new Error('dstDir not specified');
127
+ if (!env)
128
+ throw new Error('env not specified');
129
+ const dir = Path.resolve(dstDir, apiName);
130
+ if (!fs.existsSync(dir)) {
131
+ log(`Creating dir ${dir}`);
132
+ fs.mkdirSync(dir, {
133
+ recursive: true,
134
+ });
135
+ }
136
+ const clientDir = Path.resolve(dir, 'client');
137
+ const specPath = await getSpecPath(srcSpec, dir, log);
138
+ const clientFile = await generateOpenApiModel({
139
+ name: apiName,
140
+ input: specPath,
141
+ outputDir: clientDir,
142
+ isNode: env === 'node',
143
+ responseWrapper,
144
+ }, log);
145
+ if (zodSchemas?.enabled === false)
146
+ return;
147
+ log('Generating Zod schemas');
148
+ generateSchemas(Path.resolve(dir, clientFile), Path.resolve(dir, './zod.ts'), zodSchemas);
149
+ };
150
+ const getSpecPath = async (urlOrPath, dir, log) => {
151
+ if (!urlOrPath.startsWith('http')) {
152
+ if (!fs.existsSync(urlOrPath))
153
+ throw new Error(`Spec file ${urlOrPath} does not exists`);
154
+ return urlOrPath;
155
+ }
156
+ const specPath = Path.resolve(dir, `spec.json`);
157
+ log(`Will download the API spec from ${urlOrPath} to ${specPath}`);
158
+ await downloadSpec(specPath, urlOrPath);
159
+ return specPath;
160
+ };
161
+
162
+ export { generateApiClient as g };
@@ -0,0 +1,182 @@
1
+ 'use strict';
2
+
3
+ var Path = require('path');
4
+ var fs$1 = require('fs');
5
+ var axios = require('axios');
6
+ var fs = require('fs/promises');
7
+ var swaggerTypescriptApi = require('swagger-typescript-api');
8
+ var tsToZod = require('ts-to-zod');
9
+
10
+ function _interopNamespaceDefault(e) {
11
+ var n = Object.create(null);
12
+ if (e) {
13
+ Object.keys(e).forEach(function (k) {
14
+ if (k !== 'default') {
15
+ var d = Object.getOwnPropertyDescriptor(e, k);
16
+ Object.defineProperty(n, k, d.get ? d : {
17
+ enumerable: true,
18
+ get: function () { return e[k]; }
19
+ });
20
+ }
21
+ });
22
+ }
23
+ n.default = e;
24
+ return Object.freeze(n);
25
+ }
26
+
27
+ var fs__namespace = /*#__PURE__*/_interopNamespaceDefault(fs$1);
28
+
29
+ const downloadSpec = async (path, url) => {
30
+ const res = await axios({
31
+ url,
32
+ method: 'GET',
33
+ responseType: 'arraybuffer',
34
+ }).catch(err => {
35
+ throw err.toString();
36
+ });
37
+ const content = res.data.toString();
38
+ await fs.writeFile(path, format(content));
39
+ };
40
+ const format = (content) => JSON.stringify(JSON.parse(content), null, 4);
41
+
42
+ const generateOpenApiModel = async ({ input, isNode, responseWrapper, name, outputDir }, log) => {
43
+ log(`Will generate API client name=${name} to ${outputDir}, node=${isNode}`);
44
+ const specialMapping = isNode
45
+ ? {
46
+ File: '{file: Buffer | stream.Readable, name: string}',
47
+ }
48
+ : {};
49
+ const fileName = 'index';
50
+ const dstFile = Path.join(outputDir, `${fileName}.ts`);
51
+ await swaggerTypescriptApi.generateApi({
52
+ name: 'index',
53
+ output: outputDir,
54
+ input: input,
55
+ // modular: true,
56
+ httpClientType: 'axios',
57
+ templates: Path.resolve(getThisScriptDirname(), '../templates'),
58
+ defaultResponseAsSuccess: true,
59
+ // generateRouteTypes: true,
60
+ singleHttpClient: true,
61
+ // extractRequestBody: true,
62
+ cleanOutput: true,
63
+ moduleNameFirstTag: true,
64
+ apiClassName: capitalize(name) + 'Api',
65
+ // @ts-ignore
66
+ codeGenConstructs: (struct) => ({
67
+ // @ts-ignore
68
+ Keyword: specialMapping,
69
+ }),
70
+ // extractRequestParams: true,
71
+ });
72
+ const addImports = [];
73
+ const replaces = [];
74
+ if (isNode) {
75
+ addImports.push('import stream from \'node:stream\'');
76
+ }
77
+ if (responseWrapper) {
78
+ log(`Will use response wrapper '${JSON.stringify(responseWrapper)}'`);
79
+ if (responseWrapper.import)
80
+ addImports.push(responseWrapper.import);
81
+ replaces.push(['Promise', responseWrapper.symbol]);
82
+ }
83
+ // Remove unnecessary generics
84
+ replaces.push(['<SecurityDataType extends unknown>', '']);
85
+ replaces.push(['<SecurityDataType>', '']);
86
+ log(`Will modify the outputs ${JSON.stringify({
87
+ addImports,
88
+ replaces,
89
+ })}`);
90
+ await modifyOutput(dstFile, {
91
+ addImports,
92
+ replaces,
93
+ });
94
+ return dstFile;
95
+ };
96
+ const modifyOutput = async (path, cmd) => {
97
+ let content = await fs.readFile(path).then((r) => r.toString());
98
+ if (cmd.addImports.length) {
99
+ content = cmd.addImports.join('\n') + '\n' + content;
100
+ }
101
+ cmd.replaces.forEach(([from, to]) => {
102
+ content = content.replaceAll(from, to);
103
+ });
104
+ await fs.writeFile(path, content);
105
+ };
106
+ const getThisScriptDirname = () => {
107
+ // Might be problem in ESM mode
108
+ return __dirname;
109
+ };
110
+ const capitalize = (str) => {
111
+ return str[0].toUpperCase() + str.substring(1);
112
+ };
113
+
114
+ // TODO needed?
115
+ const removeGenericTypes = (code) => {
116
+ const regex = /export (interface|enum|type) [^<]* \{\n(.*\n)*?}/mg;
117
+ const matches = code.matchAll(regex);
118
+ const types = Array.from(matches).map(m => m[0]);
119
+ return types.join('\n');
120
+ };
121
+ const generateSchemas = (inputFile, outputFile, opts = {}) => {
122
+ const code = fs$1.readFileSync(inputFile).toString();
123
+ const { getZodSchemasFile } = tsToZod.generate({
124
+ sourceText: removeGenericTypes(code),
125
+ customJSDocFormatTypes: {
126
+ // Custom mapping
127
+ // 'date-time': 'blablaj' - regex
128
+ },
129
+ });
130
+ const fileName = Path.basename(inputFile);
131
+ let f = getZodSchemasFile(`./${fileName.replace('.ts', '')}`);
132
+ // Backend sends nulls not undefined - should be properly set in the OpenAPI TS types generation!
133
+ f = f.replaceAll('.optional()', '.nullable()');
134
+ if (opts.localDateTimes) {
135
+ f = f.replaceAll('z.string().datetime()', 'z.string().datetime({local: true})');
136
+ }
137
+ fs$1.writeFileSync(outputFile, f);
138
+ };
139
+
140
+ const generateApiClient = async ({ dstDir, apiName, env, srcSpec, responseWrapper, zodSchemas, }, log = console.log) => {
141
+ if (!srcSpec)
142
+ throw new Error(`Url or path ('srcSpec' not specified`);
143
+ if (!apiName)
144
+ throw new Error('apiName not specified');
145
+ if (!dstDir)
146
+ throw new Error('dstDir not specified');
147
+ if (!env)
148
+ throw new Error('env not specified');
149
+ const dir = Path.resolve(dstDir, apiName);
150
+ if (!fs__namespace.existsSync(dir)) {
151
+ log(`Creating dir ${dir}`);
152
+ fs__namespace.mkdirSync(dir, {
153
+ recursive: true,
154
+ });
155
+ }
156
+ const clientDir = Path.resolve(dir, 'client');
157
+ const specPath = await getSpecPath(srcSpec, dir, log);
158
+ const clientFile = await generateOpenApiModel({
159
+ name: apiName,
160
+ input: specPath,
161
+ outputDir: clientDir,
162
+ isNode: env === 'node',
163
+ responseWrapper,
164
+ }, log);
165
+ if (zodSchemas?.enabled === false)
166
+ return;
167
+ log('Generating Zod schemas');
168
+ generateSchemas(Path.resolve(dir, clientFile), Path.resolve(dir, './zod.ts'), zodSchemas);
169
+ };
170
+ const getSpecPath = async (urlOrPath, dir, log) => {
171
+ if (!urlOrPath.startsWith('http')) {
172
+ if (!fs__namespace.existsSync(urlOrPath))
173
+ throw new Error(`Spec file ${urlOrPath} does not exists`);
174
+ return urlOrPath;
175
+ }
176
+ const specPath = Path.resolve(dir, `spec.json`);
177
+ log(`Will download the API spec from ${urlOrPath} to ${specPath}`);
178
+ await downloadSpec(specPath, urlOrPath);
179
+ return specPath;
180
+ };
181
+
182
+ exports.generateApiClient = generateApiClient;
@@ -1,3 +1,4 @@
1
+ import { GenerateSchemasOptions } from './generateSchemas';
1
2
  export type Params = {
2
3
  srcSpec: string;
3
4
  dstDir: string;
@@ -7,6 +8,8 @@ export type Params = {
7
8
  symbol: string;
8
9
  import?: string;
9
10
  };
10
- zodSchemas?: boolean;
11
+ zodSchemas?: GenerateSchemasOptions & {
12
+ enabled?: boolean;
13
+ };
11
14
  };
12
15
  export declare const generateApiClient: ({ dstDir, apiName, env, srcSpec, responseWrapper, zodSchemas, }: Params, log?: (msg: string) => void) => Promise<void>;
@@ -1 +1,4 @@
1
- export declare const generateSchemas: (inputFile: string, outputFile: string) => void;
1
+ export type GenerateSchemasOptions = {
2
+ localDateTimes?: boolean;
3
+ };
4
+ export declare const generateSchemas: (inputFile: string, outputFile: string, opts?: GenerateSchemasOptions) => void;
package/dist/index.esm.js CHANGED
@@ -1,153 +1,7 @@
1
- import Path from 'path';
2
- import fs$1 from 'fs';
3
- import axios from 'axios';
4
- import fs from 'fs/promises';
5
- import { generateApi } from 'swagger-typescript-api';
6
- import { generate } from 'ts-to-zod';
7
-
8
- const downloadSpec = async (path, url) => {
9
- const res = await axios({
10
- url,
11
- method: 'GET',
12
- responseType: 'arraybuffer',
13
- }).catch(err => {
14
- throw err.toString();
15
- });
16
- const content = res.data.toString();
17
- await fs.writeFile(path, format(content));
18
- };
19
- const format = (content) => JSON.stringify(JSON.parse(content), null, 4);
20
-
21
- const generateOpenApiModel = async ({ input, isNode, responseWrapper, name, outputDir }, log) => {
22
- log(`Will generate API client name=${name} to ${outputDir}, node=${isNode}`);
23
- const specialMapping = isNode
24
- ? {
25
- File: '{file: Buffer | stream.Readable, name: string}',
26
- }
27
- : {};
28
- const fileName = 'index';
29
- const dstFile = Path.join(outputDir, `${fileName}.ts`);
30
- await generateApi({
31
- name: 'index',
32
- output: outputDir,
33
- input: input,
34
- // modular: true,
35
- httpClientType: 'axios',
36
- templates: Path.resolve(getThisScriptDirname(), '../templates'),
37
- defaultResponseAsSuccess: true,
38
- // generateRouteTypes: true,
39
- singleHttpClient: true,
40
- // extractRequestBody: true,
41
- cleanOutput: true,
42
- moduleNameFirstTag: true,
43
- apiClassName: capitalize(name) + 'Api',
44
- // @ts-ignore
45
- codeGenConstructs: (struct) => ({
46
- // @ts-ignore
47
- Keyword: specialMapping,
48
- }),
49
- // extractRequestParams: true,
50
- });
51
- const addImports = [];
52
- const replaces = [];
53
- if (isNode) {
54
- addImports.push('import stream from \'node:stream\'');
55
- }
56
- if (responseWrapper) {
57
- log(`Will use response wrapper '${JSON.stringify(responseWrapper)}'`);
58
- if (responseWrapper.import)
59
- addImports.push(responseWrapper.import);
60
- replaces.push(['Promise', responseWrapper.symbol]);
61
- }
62
- // Remove unnecessary generics
63
- replaces.push(['<SecurityDataType extends unknown>', '']);
64
- replaces.push(['<SecurityDataType>', '']);
65
- log(`Will modify the outputs ${JSON.stringify({
66
- addImports,
67
- replaces,
68
- })}`);
69
- await modifyOutput(dstFile, {
70
- addImports,
71
- replaces,
72
- });
73
- return dstFile;
74
- };
75
- const modifyOutput = async (path, cmd) => {
76
- let content = await fs.readFile(path).then((r) => r.toString());
77
- if (cmd.addImports.length) {
78
- content = cmd.addImports.join('\n') + '\n' + content;
79
- }
80
- cmd.replaces.forEach(([from, to]) => {
81
- content = content.replaceAll(from, to);
82
- });
83
- await fs.writeFile(path, content);
84
- };
85
- const getThisScriptDirname = () => {
86
- // Might be problem in ESM mode
87
- return __dirname;
88
- };
89
- const capitalize = (str) => {
90
- return str[0].toUpperCase() + str.substring(1);
91
- };
92
-
93
- const removeGenericTypes = (code) => {
94
- const regex = /export (interface|enum|type) [^<]* \{\n(.*\n)*?}/mg;
95
- const matches = code.matchAll(regex);
96
- const types = Array.from(matches).map(m => m[0]);
97
- return types.join('\n');
98
- };
99
- const generateSchemas = (inputFile, outputFile) => {
100
- const code = fs$1.readFileSync(inputFile).toString();
101
- const { getZodSchemasFile } = generate({
102
- sourceText: removeGenericTypes(code),
103
- });
104
- const fileName = Path.basename(inputFile);
105
- let f = getZodSchemasFile(`./${fileName.replace('.ts', '')}`);
106
- // Backend sends nulls not undefined - should be properly set in the OpenAPI TS types generation!
107
- f = f.replaceAll('.optional()', '.nullable()');
108
- fs$1.writeFileSync(outputFile, f);
109
- };
110
-
111
- const generateApiClient = async ({ dstDir, apiName, env, srcSpec, responseWrapper, zodSchemas, }, log = console.log) => {
112
- if (!srcSpec)
113
- throw new Error(`Url or path ('srcSpec' not specified`);
114
- if (!apiName)
115
- throw new Error('apiName not specified');
116
- if (!dstDir)
117
- throw new Error('dstDir not specified');
118
- if (!env)
119
- throw new Error('env not specified');
120
- const dir = Path.resolve(dstDir, apiName);
121
- if (!fs$1.existsSync(dir)) {
122
- log(`Creating dir ${dir}`);
123
- fs$1.mkdirSync(dir, {
124
- recursive: true,
125
- });
126
- }
127
- const clientDir = Path.resolve(dir, 'client');
128
- const specPath = await getSpecPath(srcSpec, dir, log);
129
- const clientFile = await generateOpenApiModel({
130
- name: apiName,
131
- input: specPath,
132
- outputDir: clientDir,
133
- isNode: env === 'node',
134
- responseWrapper,
135
- }, log);
136
- if (zodSchemas === false)
137
- return;
138
- log('Generating Zod schemas');
139
- generateSchemas(Path.resolve(dir, clientFile), Path.resolve(dir, './zod.ts'));
140
- };
141
- const getSpecPath = async (urlOrPath, dir, log) => {
142
- if (!urlOrPath.startsWith('http')) {
143
- if (!fs$1.existsSync(urlOrPath))
144
- throw new Error(`Spec file ${urlOrPath} does not exists`);
145
- return urlOrPath;
146
- }
147
- const specPath = Path.resolve(dir, `spec.json`);
148
- log(`Will download the API spec from ${urlOrPath} to ${specPath}`);
149
- await downloadSpec(specPath, urlOrPath);
150
- return specPath;
151
- };
152
-
153
- export { generateApiClient };
1
+ export { g as generateApiClient } from './generateApiClient-BSM0HlFv.js';
2
+ import 'path';
3
+ import 'fs';
4
+ import 'axios';
5
+ import 'fs/promises';
6
+ import 'swagger-typescript-api';
7
+ import 'ts-to-zod';
package/dist/index.js CHANGED
@@ -1,155 +1,13 @@
1
1
  'use strict';
2
2
 
3
- var Path = require('path');
4
- var fs$1 = require('fs');
5
- var axios = require('axios');
6
- var fs = require('fs/promises');
7
- var swaggerTypescriptApi = require('swagger-typescript-api');
8
- var tsToZod = require('ts-to-zod');
3
+ var generateApiClient = require('./generateApiClient-DBkHX8se.js');
4
+ require('path');
5
+ require('fs');
6
+ require('axios');
7
+ require('fs/promises');
8
+ require('swagger-typescript-api');
9
+ require('ts-to-zod');
9
10
 
10
- const downloadSpec = async (path, url) => {
11
- const res = await axios({
12
- url,
13
- method: 'GET',
14
- responseType: 'arraybuffer',
15
- }).catch(err => {
16
- throw err.toString();
17
- });
18
- const content = res.data.toString();
19
- await fs.writeFile(path, format(content));
20
- };
21
- const format = (content) => JSON.stringify(JSON.parse(content), null, 4);
22
11
 
23
- const generateOpenApiModel = async ({ input, isNode, responseWrapper, name, outputDir }, log) => {
24
- log(`Will generate API client name=${name} to ${outputDir}, node=${isNode}`);
25
- const specialMapping = isNode
26
- ? {
27
- File: '{file: Buffer | stream.Readable, name: string}',
28
- }
29
- : {};
30
- const fileName = 'index';
31
- const dstFile = Path.join(outputDir, `${fileName}.ts`);
32
- await swaggerTypescriptApi.generateApi({
33
- name: 'index',
34
- output: outputDir,
35
- input: input,
36
- // modular: true,
37
- httpClientType: 'axios',
38
- templates: Path.resolve(getThisScriptDirname(), '../templates'),
39
- defaultResponseAsSuccess: true,
40
- // generateRouteTypes: true,
41
- singleHttpClient: true,
42
- // extractRequestBody: true,
43
- cleanOutput: true,
44
- moduleNameFirstTag: true,
45
- apiClassName: capitalize(name) + 'Api',
46
- // @ts-ignore
47
- codeGenConstructs: (struct) => ({
48
- // @ts-ignore
49
- Keyword: specialMapping,
50
- }),
51
- // extractRequestParams: true,
52
- });
53
- const addImports = [];
54
- const replaces = [];
55
- if (isNode) {
56
- addImports.push('import stream from \'node:stream\'');
57
- }
58
- if (responseWrapper) {
59
- log(`Will use response wrapper '${JSON.stringify(responseWrapper)}'`);
60
- if (responseWrapper.import)
61
- addImports.push(responseWrapper.import);
62
- replaces.push(['Promise', responseWrapper.symbol]);
63
- }
64
- // Remove unnecessary generics
65
- replaces.push(['<SecurityDataType extends unknown>', '']);
66
- replaces.push(['<SecurityDataType>', '']);
67
- log(`Will modify the outputs ${JSON.stringify({
68
- addImports,
69
- replaces,
70
- })}`);
71
- await modifyOutput(dstFile, {
72
- addImports,
73
- replaces,
74
- });
75
- return dstFile;
76
- };
77
- const modifyOutput = async (path, cmd) => {
78
- let content = await fs.readFile(path).then((r) => r.toString());
79
- if (cmd.addImports.length) {
80
- content = cmd.addImports.join('\n') + '\n' + content;
81
- }
82
- cmd.replaces.forEach(([from, to]) => {
83
- content = content.replaceAll(from, to);
84
- });
85
- await fs.writeFile(path, content);
86
- };
87
- const getThisScriptDirname = () => {
88
- // Might be problem in ESM mode
89
- return __dirname;
90
- };
91
- const capitalize = (str) => {
92
- return str[0].toUpperCase() + str.substring(1);
93
- };
94
12
 
95
- const removeGenericTypes = (code) => {
96
- const regex = /export (interface|enum|type) [^<]* \{\n(.*\n)*?}/mg;
97
- const matches = code.matchAll(regex);
98
- const types = Array.from(matches).map(m => m[0]);
99
- return types.join('\n');
100
- };
101
- const generateSchemas = (inputFile, outputFile) => {
102
- const code = fs$1.readFileSync(inputFile).toString();
103
- const { getZodSchemasFile } = tsToZod.generate({
104
- sourceText: removeGenericTypes(code),
105
- });
106
- const fileName = Path.basename(inputFile);
107
- let f = getZodSchemasFile(`./${fileName.replace('.ts', '')}`);
108
- // Backend sends nulls not undefined - should be properly set in the OpenAPI TS types generation!
109
- f = f.replaceAll('.optional()', '.nullable()');
110
- fs$1.writeFileSync(outputFile, f);
111
- };
112
-
113
- const generateApiClient = async ({ dstDir, apiName, env, srcSpec, responseWrapper, zodSchemas, }, log = console.log) => {
114
- if (!srcSpec)
115
- throw new Error(`Url or path ('srcSpec' not specified`);
116
- if (!apiName)
117
- throw new Error('apiName not specified');
118
- if (!dstDir)
119
- throw new Error('dstDir not specified');
120
- if (!env)
121
- throw new Error('env not specified');
122
- const dir = Path.resolve(dstDir, apiName);
123
- if (!fs$1.existsSync(dir)) {
124
- log(`Creating dir ${dir}`);
125
- fs$1.mkdirSync(dir, {
126
- recursive: true,
127
- });
128
- }
129
- const clientDir = Path.resolve(dir, 'client');
130
- const specPath = await getSpecPath(srcSpec, dir, log);
131
- const clientFile = await generateOpenApiModel({
132
- name: apiName,
133
- input: specPath,
134
- outputDir: clientDir,
135
- isNode: env === 'node',
136
- responseWrapper,
137
- }, log);
138
- if (zodSchemas === false)
139
- return;
140
- log('Generating Zod schemas');
141
- generateSchemas(Path.resolve(dir, clientFile), Path.resolve(dir, './zod.ts'));
142
- };
143
- const getSpecPath = async (urlOrPath, dir, log) => {
144
- if (!urlOrPath.startsWith('http')) {
145
- if (!fs$1.existsSync(urlOrPath))
146
- throw new Error(`Spec file ${urlOrPath} does not exists`);
147
- return urlOrPath;
148
- }
149
- const specPath = Path.resolve(dir, `spec.json`);
150
- log(`Will download the API spec from ${urlOrPath} to ${specPath}`);
151
- await downloadSpec(specPath, urlOrPath);
152
- return specPath;
153
- };
154
-
155
- exports.generateApiClient = generateApiClient;
13
+ exports.generateApiClient = generateApiClient.generateApiClient;
package/package.json CHANGED
@@ -1,8 +1,9 @@
1
1
  {
2
2
  "name": "@ps-aux/api-client-gen",
3
- "version": "0.0.5-rc2",
3
+ "version": "0.0.6-rc1",
4
4
  "main": "dist/index.js",
5
5
  "module": "dist/index.esm.js",
6
+ "type": "module",
6
7
  "types": "dist/index.d.ts",
7
8
  "bin": {
8
9
  "gen-api-client": "dist/bin.js"
@@ -10,7 +11,8 @@
10
11
  "scripts": {
11
12
  "build": "rollup -c",
12
13
  "dev": "rollup -c --watch",
13
- "publish": "npm run build && npm publish",
14
+ "pub": "npm test && npm run build && npm publish",
15
+ "test": "vitest",
14
16
  "tc": "tsc",
15
17
  "eh": "foo"
16
18
  },
@@ -19,7 +21,10 @@
19
21
  "dependencies": {
20
22
  "axios": "^1.5.0",
21
23
  "cosmiconfig": "^8.3.6",
22
- "swagger-typescript-api": "^13.0.3",
23
- "ts-to-zod": "^3.2.0"
24
+ "swagger-typescript-api": "^13.0.23",
25
+ "ts-to-zod": "^3.15.0"
26
+ },
27
+ "devDependencies": {
28
+ "vitest": "^3.1.1"
24
29
  }
25
30
  }
@@ -1,8 +1,8 @@
1
1
  import Path from 'path'
2
- import fs from 'fs'
2
+ import * as fs from 'fs'
3
3
  import { downloadSpec } from './downloadSpec'
4
4
  import { generateOpenApiModel } from './generateOpenApiModel'
5
- import { generateSchemas } from './generateSchemas'
5
+ import { generateSchemas, GenerateSchemasOptions } from './generateSchemas'
6
6
 
7
7
  export type Params = {
8
8
  srcSpec: string, // path or http(s) URL
@@ -13,7 +13,9 @@ export type Params = {
13
13
  symbol: string
14
14
  import?: string
15
15
  },
16
- zodSchemas?: boolean
16
+ zodSchemas?: GenerateSchemasOptions & {
17
+ enabled?: boolean
18
+ }
17
19
  }
18
20
 
19
21
  export const generateApiClient = async ({
@@ -51,12 +53,12 @@ export const generateApiClient = async ({
51
53
  log,
52
54
  )
53
55
 
54
- if (zodSchemas === false)
56
+ if (zodSchemas?.enabled === false)
55
57
  return
56
58
 
57
59
 
58
60
  log('Generating Zod schemas')
59
- generateSchemas(Path.resolve(dir, clientFile), Path.resolve(dir, './zod.ts'))
61
+ generateSchemas(Path.resolve(dir, clientFile), Path.resolve(dir, './zod.ts'), zodSchemas)
60
62
  }
61
63
 
62
64
  const getSpecPath = async (urlOrPath: string, dir: string, log: (str: string) => void): Promise<string> => {
@@ -1,7 +1,8 @@
1
1
  import fs from 'fs'
2
- import {generate} from 'ts-to-zod'
2
+ import { generate } from 'ts-to-zod'
3
3
  import Path from 'path'
4
4
 
5
+ // TODO needed?
5
6
  const removeGenericTypes = (code: string): string => {
6
7
  const regex = /export (interface|enum|type) [^<]* \{\n(.*\n)*?}/mg
7
8
  const matches = code.matchAll(regex)
@@ -10,11 +11,19 @@ const removeGenericTypes = (code: string): string => {
10
11
  return types.join('\n')
11
12
  }
12
13
 
13
- export const generateSchemas = (inputFile: string, outputFile: string) => {
14
+ export type GenerateSchemasOptions = {
15
+ localDateTimes?: boolean
16
+ }
17
+
18
+ export const generateSchemas = (inputFile: string, outputFile: string, opts: GenerateSchemasOptions = {}) => {
14
19
  const code = fs.readFileSync(inputFile).toString()
15
20
 
16
- const {getZodSchemasFile} = generate({
21
+ const { getZodSchemasFile } = generate({
17
22
  sourceText: removeGenericTypes(code),
23
+ customJSDocFormatTypes: {
24
+ // Custom mapping
25
+ // 'date-time': 'blablaj' - regex
26
+ },
18
27
  })
19
28
 
20
29
  const fileName = Path.basename(inputFile)
@@ -23,6 +32,9 @@ export const generateSchemas = (inputFile: string, outputFile: string) => {
23
32
 
24
33
  // Backend sends nulls not undefined - should be properly set in the OpenAPI TS types generation!
25
34
  f = f.replaceAll('.optional()', '.nullable()')
35
+ if (opts.localDateTimes) {
36
+ f = f.replaceAll('z.string().datetime()', 'z.string().datetime({local: true})')
37
+ }
26
38
  fs.writeFileSync(outputFile, f)
27
39
 
28
40
  }
@@ -0,0 +1,16 @@
1
+ import { generateApiClient } from '../src'
2
+ import * as Path from 'path'
3
+ import { test } from 'vitest'
4
+
5
+
6
+ test('generate', async () => {
7
+ await generateApiClient({
8
+ srcSpec: Path.resolve(__dirname, 'schema.json'),
9
+ dstDir: Path.resolve(__dirname, 'out'),
10
+ env: 'browser',
11
+ apiName: 'test',
12
+ zodSchemas: {
13
+ localDateTimes: true,
14
+ },
15
+ })
16
+ })
@@ -0,0 +1,118 @@
1
+ {
2
+ "openapi": "3.0.0",
3
+ "info": {
4
+ "title": "Simple API",
5
+ "description": "A basic API example",
6
+ "version": "1.0.0"
7
+ },
8
+ "servers": [
9
+ {
10
+ "url": "https://api.example.com/v1",
11
+ "description": "Main production server"
12
+ }
13
+ ],
14
+ "paths": {
15
+ "/users": {
16
+ "get": {
17
+ "summary": "Get list of users",
18
+ "responses": {
19
+ "200": {
20
+ "description": "Successful response",
21
+ "content": {
22
+ "application/json": {
23
+ "schema": {
24
+ "type": "array",
25
+ "items": {
26
+ "$ref": "#/components/schemas/User"
27
+ }
28
+ }
29
+ }
30
+ }
31
+ }
32
+ }
33
+ },
34
+ "post": {
35
+ "summary": "Create a new user",
36
+ "requestBody": {
37
+ "required": true,
38
+ "content": {
39
+ "application/json": {
40
+ "schema": {
41
+ "$ref": "#/components/schemas/User"
42
+ }
43
+ }
44
+ }
45
+ },
46
+ "responses": {
47
+ "201": {
48
+ "description": "User created successfully",
49
+ "content": {
50
+ "application/json": {
51
+ "schema": {
52
+ "$ref": "#/components/schemas/User"
53
+ }
54
+ }
55
+ }
56
+ }
57
+ }
58
+ }
59
+ },
60
+ "/users/{id}": {
61
+ "get": {
62
+ "summary": "Get user by ID",
63
+ "parameters": [
64
+ {
65
+ "name": "id",
66
+ "in": "path",
67
+ "required": true,
68
+ "schema": {
69
+ "type": "string"
70
+ }
71
+ }
72
+ ],
73
+ "responses": {
74
+ "200": {
75
+ "description": "Successful response",
76
+ "content": {
77
+ "application/json": {
78
+ "schema": {
79
+ "$ref": "#/components/schemas/User"
80
+ }
81
+ }
82
+ }
83
+ }
84
+ }
85
+ }
86
+ }
87
+ },
88
+ "components": {
89
+ "schemas": {
90
+ "User": {
91
+ "type": "object",
92
+ "required": [
93
+ "id",
94
+ "name"
95
+ ],
96
+ "properties": {
97
+ "id": {
98
+ "type": "string",
99
+ "example": "123"
100
+ },
101
+ "name": {
102
+ "type": "string",
103
+ "example": "John Doe"
104
+ },
105
+ "email": {
106
+ "type": "string",
107
+ "format": "email",
108
+ "example": "john@example.com"
109
+ },
110
+ "createdAt": {
111
+ "type": "string",
112
+ "format": "date-time"
113
+ }
114
+ }
115
+ }
116
+ }
117
+ }
118
+ }
@@ -0,0 +1,8 @@
1
+ import { defineConfig } from 'vitest/config'
2
+
3
+ export default defineConfig({
4
+ test: {
5
+ watch: false,
6
+ exclude: [],
7
+ },
8
+ })
package/jest.config.js DELETED
@@ -1,17 +0,0 @@
1
- /** @type {import('ts-jest').JestConfigWithTsJest} */
2
- module.exports = {
3
- preset: 'ts-jest/presets/default-esm', // or other ESM presets
4
- moduleNameMapper: {
5
- '^(\\.{1,2}/.*)\\.js$': '$1',
6
- },
7
- transform: {
8
- // '^.+\\.[tj]sx?$' to process js/ts with `ts-jest`
9
- // '^.+\\.m?[tj]sx?$' to process js/ts/mjs/mts with `ts-jest`
10
- '^.+\\.tsx?$': [
11
- 'ts-jest',
12
- {
13
- useESM: true,
14
- },
15
- ],
16
- },
17
- }