Skip to content

Commit 88cba4c

Browse files
committed
feature: ability to customize resolving process of the extracting type names (extractingOptions nodejs option)
docs: update docs for `extraTemplates` option fix: problem with default name of single api file (Api.ts) fix: problem based with tuple types (#445) fix: problem with `defaultResponseType` declaration type
1 parent f69b156 commit 88cba4c

File tree

15 files changed

+244
-73
lines changed

15 files changed

+244
-73
lines changed

CHANGELOG.md

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,31 @@ typeSuffix?: string;
2222
enumKeyPrefix?: string;
2323
enumKeySuffix?: string;
2424
```
25+
feature: ability to customize resolving process of the extracting type names (`extractingOptions` nodejs option)
26+
```ts
27+
extractingOptions = {
28+
// requestBodySuffix: ["Payload", "Body", "Input"],
29+
// or
30+
// requestBodyNameResolver: (typeName, reservedNames) => string;
31+
32+
// requestParamsSuffix: ["Params"],
33+
// or
34+
// requestParamsNameResolver: (typeName, reservedNames) => string;
35+
36+
// responseBodySuffix: ["Data", "Result", "Output"],
37+
// or
38+
// responseBodyNameResolver: (typeName, reservedNames) => string;
39+
40+
// responseErrorSuffix: ["Error", "Fail", "Fails", "ErrorData", "HttpError", "BadResponse"],
41+
// or
42+
// responseErrorNameResolver: (typeName, reservedNames) => string;
43+
}
44+
```
2545
docs: update docs for `extraTemplates` option
46+
fix: problem with default name of single api file (Api.ts)
47+
fix: problem based with tuple types (#445)
48+
fix: problem with `defaultResponseType` declaration type
49+
2650

2751
# 11.1.3
2852

README.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -139,7 +139,15 @@ generateApi({
139139
generateUnionEnums: false,
140140
typePrefix: '',
141141
typeSuffix: '',
142+
enumKeyPrefix: '',
143+
enumKeySuffix: '',
142144
addReadonly: false,
145+
extractingOptions: {
146+
requestBodySuffix: ["Payload", "Body", "Input"],
147+
requestParamsSuffix: ["Params"],
148+
responseBodySuffix: ["Data", "Result", "Output"],
149+
responseErrorSuffix: ["Error", "Fail", "Fails", "ErrorData", "HttpError", "BadResponse"],
150+
},
143151
/** allow to generate extra files based with this extra templates, see more below */
144152
extraTemplates: [],
145153
anotherArrayType: false,

index.d.ts

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,7 @@ interface GenerateApiParamsBase {
106106
/**
107107
* default type for empty response schema (default: "void")
108108
*/
109-
defaultResponseType?: boolean;
109+
defaultResponseType?: string;
110110
/**
111111
* Ability to send HttpClient instance to Api constructor
112112
*/
@@ -151,6 +151,7 @@ interface GenerateApiParamsBase {
151151
typePrefix?: string;
152152
/** suffix string value for type names */
153153
typeSuffix?: string;
154+
extractingOptions?: Partial<ExtractingOptions>;
154155
}
155156

156157
type CodeGenConstruct = {
@@ -421,6 +422,17 @@ export enum SCHEMA_TYPES {
421422

422423
type MAIN_SCHEMA_TYPES = SCHEMA_TYPES.PRIMITIVE | SCHEMA_TYPES.OBJECT | SCHEMA_TYPES.ENUM;
423424

425+
type ExtractingOptions = {
426+
requestBodySuffix: string[];
427+
responseBodySuffix: string[];
428+
responseErrorSuffix: string[];
429+
requestParamsSuffix: string[];
430+
requestBodyNameResolver: (name: string, reservedNames: string) => string | undefined;
431+
responseBodyNameResolver: (name: string, reservedNames: string) => string | undefined;
432+
responseErrorNameResolver: (name: string, reservedNames: string) => string | undefined;
433+
requestParamsNameResolver: (name: string, reservedNames: string) => string | undefined;
434+
};
435+
424436
export interface GenerateApiConfiguration {
425437
apiConfig: {
426438
baseUrl: string;
@@ -470,7 +482,7 @@ export interface GenerateApiConfiguration {
470482
extractEnums: boolean;
471483
fixInvalidTypeNamePrefix: string;
472484
fixInvalidEnumKeyPrefix: string;
473-
defaultResponseType: boolean;
485+
defaultResponseType: string;
474486
toJS: boolean;
475487
disableThrowOnError: boolean;
476488
silent: boolean;
@@ -502,6 +514,7 @@ export interface GenerateApiConfiguration {
502514
routeNameDuplicatesMap: Map<string, string>;
503515
apiClassName: string;
504516
requestOptions?: import("node-fetch").RequestInit;
517+
extractingOptions: ExtractingOptions;
505518
};
506519
modelTypes: ModelType[];
507520
rawModelTypes: SchemaComponent[];

index.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ const program = cli({
3737
{
3838
flags: "-n, --name <string>",
3939
description: "name of output typescript api file",
40-
default: `${codeGenBaseConfig.apiClassName}.ts`,
40+
default: codeGenBaseConfig.fileName,
4141
},
4242
{
4343
flags: "-t, --templates <string>",

src/code-gen-process.js

Lines changed: 9 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,7 @@ class CodeGenProcess {
8181
this.templates,
8282
this.typeName,
8383
);
84+
this.config.componentTypeNameResolver.logger = this.logger;
8485
}
8586

8687
async start() {
@@ -169,9 +170,11 @@ class CodeGenProcess {
169170

170171
if (this.fileSystem.pathIsExist(this.config.output)) {
171172
if (this.config.cleanOutput) {
173+
this.logger.debug(`cleaning dir ${this.config.output}`);
172174
this.fileSystem.cleanDir(this.config.output);
173175
}
174176
} else {
177+
this.logger.debug(`path ${this.config.output} is not exist. creating dir by this path`);
175178
this.fileSystem.createDir(this.config.output);
176179
}
177180

@@ -398,15 +401,11 @@ class CodeGenProcess {
398401
if (configuration.translateToJavaScript) {
399402
const { sourceContent, declarationContent } = translateToJS(`${fixedFileName}${ts.Extension.Ts}`, content);
400403

401-
if (this.config.debug) {
402-
console.info("generating output for", `${fixedFileName}${ts.Extension.Js}`);
403-
console.info(sourceContent);
404-
}
404+
this.logger.debug("generating output for", `${fixedFileName}${ts.Extension.Js}`);
405+
this.logger.debug(sourceContent);
405406

406-
if (this.config.debug) {
407-
console.info("generating output for", `${fixedFileName}${ts.Extension.Dts}`);
408-
console.info(declarationContent);
409-
}
407+
this.logger.debug("generating output for", `${fixedFileName}${ts.Extension.Js}`);
408+
this.logger.debug(declarationContent);
410409

411410
return {
412411
name: `${fixedFileName}${ts.Extension.Js}`,
@@ -418,10 +417,8 @@ class CodeGenProcess {
418417
};
419418
}
420419

421-
if (this.config.debug) {
422-
console.info("generating output for", `${fixedFileName}${ts.Extension.Ts}`);
423-
console.info(content);
424-
}
420+
this.logger.debug("generating output for", `${fixedFileName}${ts.Extension.Js}`);
421+
this.logger.debug(content);
425422

426423
return {
427424
name: `${fixedFileName}${ts.Extension.Ts}`,

src/configuration.js

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -134,7 +134,7 @@ class CodeGenConfig {
134134
enumKeyPrefix = "";
135135
enumKeySuffix = "";
136136
patch = false;
137-
componentTypeNameResolver = new ComponentTypeNameResolver([]);
137+
componentTypeNameResolver = new ComponentTypeNameResolver(null, []);
138138
/** name of the main exported class */
139139
apiClassName = "Api";
140140
debug = false;
@@ -149,7 +149,7 @@ class CodeGenConfig {
149149
url = "";
150150
cleanOutput = false;
151151
spec = null;
152-
fileName = "";
152+
fileName = "Api.ts";
153153
authorizationToken = void 0;
154154
requestOptions = null;
155155

@@ -160,6 +160,14 @@ class CodeGenConfig {
160160

161161
successResponseStatusRange = [200, 299];
162162

163+
/** @type {ExtractingOptions} */
164+
extractingOptions = {
165+
requestBodySuffix: ["Payload", "Body", "Input"],
166+
requestParamsSuffix: ["Params"],
167+
responseBodySuffix: ["Data", "Result", "Output"],
168+
responseErrorSuffix: ["Error", "Fail", "Fails", "ErrorData", "HttpError", "BadResponse"],
169+
};
170+
163171
Ts = {
164172
Keyword: _.cloneDeep(TsKeyword),
165173
CodeGenKeyword: _.cloneDeep(TsCodeGenKeyword),
@@ -244,6 +252,12 @@ class CodeGenConfig {
244252
TypeWithGeneric: (typeName, genericArgs) => {
245253
return `${typeName}${genericArgs.length ? `<${genericArgs.join(",")}>` : ""}`;
246254
},
255+
/**
256+
* [$A1, $A2, ...$AN]
257+
*/
258+
Tuple: (values) => {
259+
return `[${values.join(", ")}]`;
260+
},
247261
};
248262

249263
/**

src/schema-parser/schema-parser.js

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,19 @@ class SchemaParser {
9999

100100
const refType = this.schemaUtils.getSchemaRefType(schema);
101101
const $ref = (refType && refType.$ref) || null;
102+
103+
if (Array.isArray(schema.enum) && Array.isArray(schema.enum[0])) {
104+
return this.parseSchema(
105+
{
106+
oneOf: schema.enum.map((enumNames) => ({
107+
type: "array",
108+
items: enumNames.map((enumName) => ({ type: "string", enum: [enumName] })),
109+
})),
110+
},
111+
typeName,
112+
);
113+
}
114+
102115
const keyType = this.getSchemaType(schema);
103116
const enumNames = this.schemaUtils.getEnumNames(schema);
104117
let content = null;
@@ -211,7 +224,7 @@ class SchemaParser {
211224
},
212225
[SCHEMA_TYPES.PRIMITIVE]: (schema, typeName) => {
213226
let contentType = null;
214-
const { additionalProperties, type, description, $$requiredKeys } = schema || {};
227+
const { additionalProperties, type, description, items } = schema || {};
215228

216229
if (type === this.config.Ts.Keyword.Object && additionalProperties) {
217230
const fieldType = _.isObject(additionalProperties)
@@ -227,6 +240,10 @@ class SchemaParser {
227240
});
228241
}
229242

243+
if (_.isArray(items) && type === SCHEMA_TYPES.ARRAY) {
244+
contentType = this.config.Ts.Tuple(items.map((item) => this.getInlineParseContent(item)));
245+
}
246+
230247
return {
231248
...(_.isObject(schema) ? schema : {}),
232249
$parsedSchema: true,
@@ -364,7 +381,7 @@ class SchemaParser {
364381
typeName = this.getSchemaType(schema);
365382
}
366383

367-
if (schema.items && !schema.type) {
384+
if (schema.items && !Array.isArray(schema.items) && !schema.type) {
368385
schema.type = SCHEMA_TYPES.ARRAY;
369386
}
370387
schemaType = this.getInternalSchemaType(schema);

src/schema-parser/schema-routes.js

Lines changed: 26 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,10 @@ class SchemaRoutes {
2828
* @type {SchemaParser}
2929
*/
3030
schemaParser;
31+
/**
32+
* @type {SchemaUtils}
33+
*/
34+
schemaUtils;
3135
/**
3236
* @type {TypeName}
3337
*/
@@ -55,6 +59,7 @@ class SchemaRoutes {
5559
constructor(config, schemaParser, schemaComponentMap, logger, templates, typeName) {
5660
this.config = config;
5761
this.schemaParser = schemaParser;
62+
this.schemaUtils = this.schemaParser.schemaUtils;
5863
this.typeName = typeName;
5964
this.schemaComponentMap = schemaComponentMap;
6065
this.logger = logger;
@@ -470,11 +475,11 @@ class SchemaRoutes {
470475
let typeName = null;
471476

472477
if (this.config.extractRequestBody) {
473-
typeName = this.config.componentTypeNameResolver.resolve([
474-
pascalCase(`${routeName.usage} Payload`),
475-
pascalCase(`${routeName.usage} Body`),
476-
pascalCase(`${routeName.usage} Input`),
477-
]);
478+
typeName = this.schemaUtils.resolveTypeName(
479+
routeName.usage,
480+
this.config.extractingOptions.requestBodySuffix,
481+
this.config.extractingOptions.requestBodyNameResolver,
482+
);
478483
}
479484

480485
if (routeParams.formData.length) {
@@ -571,7 +576,11 @@ class SchemaRoutes {
571576
if (fixedSchema) return fixedSchema;
572577

573578
if (extractRequestParams) {
574-
const typeName = this.config.componentTypeNameResolver.resolve([pascalCase(`${routeName.usage} Params`)]);
579+
const typeName = this.schemaUtils.resolveTypeName(
580+
routeName.usage,
581+
this.config.extractingOptions.requestParamsSuffix,
582+
this.config.extractingOptions.requestParamsNameResolver,
583+
);
575584

576585
return this.schemaComponentMap.createComponent("schemas", typeName, { ...schema });
577586
}
@@ -581,11 +590,11 @@ class SchemaRoutes {
581590

582591
extractResponseBodyIfItNeeded = (routeInfo, responseBodyInfo, routeName) => {
583592
if (responseBodyInfo.responses.length && responseBodyInfo.success && responseBodyInfo.success.schema) {
584-
const typeName = this.config.componentTypeNameResolver.resolve([
585-
pascalCase(`${routeName.usage} Data`),
586-
pascalCase(`${routeName.usage} Result`),
587-
pascalCase(`${routeName.usage} Output`),
588-
]);
593+
const typeName = this.schemaUtils.resolveTypeName(
594+
routeName.usage,
595+
this.config.extractingOptions.responseBodySuffix,
596+
this.config.extractingOptions.responseBodyNameResolver,
597+
);
589598

590599
const idx = responseBodyInfo.responses.indexOf(responseBodyInfo.success.schema);
591600

@@ -608,14 +617,11 @@ class SchemaRoutes {
608617

609618
extractResponseErrorIfItNeeded = (routeInfo, responseBodyInfo, routeName) => {
610619
if (responseBodyInfo.responses.length && responseBodyInfo.error.schemas && responseBodyInfo.error.schemas.length) {
611-
const typeName = this.config.componentTypeNameResolver.resolve([
612-
pascalCase(`${routeName.usage} Error`),
613-
pascalCase(`${routeName.usage} Fail`),
614-
pascalCase(`${routeName.usage} Fails`),
615-
pascalCase(`${routeName.usage} ErrorData`),
616-
pascalCase(`${routeName.usage} HttpError`),
617-
pascalCase(`${routeName.usage} BadResponse`),
618-
]);
620+
const typeName = this.schemaUtils.resolveTypeName(
621+
routeName.usage,
622+
this.config.extractingOptions.responseErrorSuffix,
623+
this.config.extractingOptions.responseErrorNameResolver,
624+
);
619625

620626
const errorSchemas = responseBodyInfo.error.schemas.map(this.getSchemaFromRequestType).filter(Boolean);
621627

@@ -765,7 +771,7 @@ class SchemaRoutes {
765771
const pathType = routeParams.path.length ? this.schemaParser.getInlineParseContent(pathObjectSchema) : null;
766772
const headersType = routeParams.header.length ? this.schemaParser.getInlineParseContent(headersObjectSchema) : null;
767773

768-
const nameResolver = new SpecificArgNameResolver(pathArgsNames);
774+
const nameResolver = new SpecificArgNameResolver(this.logger, pathArgsNames);
769775

770776
const specificArgs = {
771777
query: queryType

src/schema-parser/schema-utils.js

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
const _ = require("lodash");
22
const { SCHEMA_TYPES } = require("../constants");
33
const { internalCase } = require("../util/internal-case");
4+
const { pascalCase } = require("../util/pascal-case");
45

56
class SchemaUtils {
67
/**
@@ -143,6 +144,20 @@ class SchemaUtils {
143144
filterSchemaContents = (contents, filterFn) => {
144145
return _.uniq(_.filter(contents, (type) => filterFn(type)));
145146
};
147+
148+
resolveTypeName = (typeName, suffixes, resolver) => {
149+
if (resolver) {
150+
return this.config.componentTypeNameResolver.resolve((reserved) => {
151+
const variant = resolver(pascalCase(typeName), reserved);
152+
if (variant == null) return variant;
153+
return pascalCase(variant);
154+
});
155+
} else {
156+
return this.config.componentTypeNameResolver.resolve(
157+
suffixes.map((suffix) => pascalCase(`${typeName} ${suffix}`)),
158+
);
159+
}
160+
};
146161
}
147162

148163
module.exports = {

src/templates.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -164,8 +164,8 @@ class Templates {
164164
{
165165
async: false,
166166
...(options || {}),
167-
includeFile: (path, payload, options) => {
168-
return this.renderTemplate(this.getTemplateContent(path), payload, options);
167+
includeFile: (path, configuration, options) => {
168+
return this.renderTemplate(this.getTemplateContent(path), configuration, options);
169169
},
170170
},
171171
);

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy