Skip to content

Commit 1ef9b61

Browse files
committed
feat: new hook onPreParseSchema;
BREAKING_CHANGE: add ability to custom prefix for autofix invalid enum keys, invalid type names with nodejs options (`fixInvalidTypeNamePrefix: string`, `fixInvalidEnumKeyPrefix: string`); (#344) BREAKING_CHANGE: by default all component enum schemas (even numeric) extracting as `enum` TS constructions; (#344) feature: ability to extract all enums from nested types\interfaces to `enum` TS construction using `--extract-enums` option (#344)
1 parent 02658ce commit 1ef9b61

File tree

28 files changed

+629
-93
lines changed

28 files changed

+629
-93
lines changed

CHANGELOG.md

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,12 @@ onPreBuildRoutePath: (routePath: string) => string | void;
88
onBuildRoutePath: (data: BuildRoutePath) => BuildRoutePath | void;
99
/** calls before insert path param name into string path interpolation */
1010
onInsertPathParam: (paramName: string, index: number, arr: BuildRouteParam[], resultRoute: string) => string | void;
11-
```
11+
/** calls before parse any kind of schema */
12+
onPreParseSchema: (originalSchema: any, typeName: string, schemaType: string) => any;
13+
```
14+
BREAKING_CHANGE: add ability to custom prefix for autofix invalid enum keys, invalid type names with nodejs options (`fixInvalidTypeNamePrefix: string`, `fixInvalidEnumKeyPrefix: string`)
15+
BREAKING_CHANGE: by default all component enum schemas (even numeric) extracting as `enum` TS constructions (#344)
16+
feature: ability to extract all enums from nested types\interfaces to `enum` TS construction using `--extract-enums` option (#344)
1217
feature: ability to modify route path params before insert them into string (request url, #446, with using hook `onInsertPathParam`)
1318
docs: update docs for `extraTemplates` option
1419

@@ -468,7 +473,7 @@ Features:
468473
- `--type-suffix` option. Allows to set suffix for data contract name. (issue #191, thanks @the-ult)
469474
- `--type-prefix` option. Allows to set prefix for data contract name. (issue #191, thanks @the-ult)
470475
Examples [here](./spec/typeSuffixPrefix/schema.ts)
471-
- `onFormatTypeName(usageTypeName, rawTypeName)` hook. Allow to format data contract names as you want.
476+
- `onFormatTypeName(usageTypeName, rawTypeName, schemaType)` hook. Allow to format data contract names as you want.
472477

473478
Internal:
474479
- rename and split `checkAndRenameModelName` -> `formatModelName`, `fixModelName`

README.md

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -70,11 +70,12 @@ Options:
7070
--type-prefix <string> data contract name prefix (default: "")
7171
--type-suffix <string> data contract name suffix (default: "")
7272
--clean-output clean output folder before generate api. WARNING: May cause data loss (default: false)
73-
--api-class-name <string> name of the api class
73+
--api-class-name <string> name of the api class (default: "Api")
7474
--patch fix up small errors in the swagger source definition (default: false)
7575
--debug additional information about processes inside this tool (default: false)
7676
--another-array-type generate array types as Array<Type> (by default Type[]) (default: false)
7777
--sort-types sort fields and types (default: false)
78+
--extract-enums extract all enums from inline interface\type content to typescript enum construction (default: false)
7879
-h, --help display help for command
7980
8081
Commands:
@@ -141,7 +142,9 @@ generateApi({
141142
addReadonly: false,
142143
/** allow to generate extra files based with this extra templates, see more below */
143144
extraTemplates: [],
144-
anotherArrayType: false,
145+
anotherArrayType: false,
146+
fixInvalidTypeNamePrefix: "Type",
147+
fixInvalidEnumKeyPrefix: "Value",
145148
codeGenConstructs: (constructs) => ({
146149
...constructs,
147150
RecordType: (key, value) => `MyRecord<key, value>`
@@ -158,8 +161,9 @@ generateApi({
158161
onCreateRoute: (routeData) => {},
159162
onCreateRouteName: (routeNameInfo, rawRouteInfo) => {},
160163
onFormatRouteName: (routeInfo, templateRouteName) => {},
161-
onFormatTypeName: (typeName, rawTypeName) => {},
164+
onFormatTypeName: (typeName, rawTypeName, schemaType) => {},
162165
onInit: (configuration) => {},
166+
onPreParseSchema: (originalSchema, typeName, schemaType) => {},
163167
onParseSchema: (originalSchema, parsedSchema) => {},
164168
onPrepareConfig: (currentConfiguration) => {},
165169
}

index.d.ts

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,13 @@ interface GenerateApiParamsBase {
136136
primitiveTypeConstructs?: (struct: PrimitiveTypeStruct) => Partial<PrimitiveTypeStruct>;
137137

138138
codeGenConstructs?: (struct: CodeGenConstruct) => Partial<CodeGenConstruct>;
139+
140+
/** extract all enums from nested types\interfaces to `enum` construction */
141+
extractEnums?: boolean;
142+
/** prefix string value needed to fix invalid type names (default: 'Type') */
143+
fixInvalidTypeNamePrefix?: string;
144+
/** prefix string value needed to fix invalid enum keys (default: 'Value') */
145+
fixInvalidEnumKeyPrefix?: string;
139146
}
140147

141148
type CodeGenConstruct = {
@@ -243,6 +250,8 @@ export interface Hooks {
243250
onInsertPathParam: (paramName: string, index: number, arr: BuildRouteParam[], resultRoute: string) => string | void;
244251
/** calls after parse schema component */
245252
onCreateComponent: (component: SchemaComponent) => SchemaComponent | void;
253+
/** calls before parse any kind of schema */
254+
onPreParseSchema: (originalSchema: any, typeName: string, schemaType: string) => any;
246255
/** calls after parse any kind of schema */
247256
onParseSchema: (originalSchema: any, parsedSchema: any) => any | void;
248257
/** calls after parse route (return type: customized route (ParsedRoute), nothing change (void), false (ignore this route)) */
@@ -256,7 +265,7 @@ export interface Hooks {
256265
/** customize request params (path params, query params) */
257266
onCreateRequestParams?: (rawType: SchemaComponent["rawTypeData"]) => SchemaComponent["rawTypeData"] | void;
258267
/** customize name of model type */
259-
onFormatTypeName?: (typeName: string, rawTypeName?: string) => string | void;
268+
onFormatTypeName?: (typeName: string, rawTypeName?: string, schemaType?: "type-name" | "enum-key") => string | void;
260269
/** customize name of route (operationId), you can do it with using onCreateRouteName too */
261270
onFormatRouteName?: (routeInfo: RawRouteInfo, templateRouteName: string) => string | void;
262271
}
@@ -448,6 +457,9 @@ export interface GenerateApiConfiguration {
448457
addReadonly: boolean;
449458
extractResponseBody: boolean;
450459
extractResponseError: boolean;
460+
extractEnums: boolean;
461+
fixInvalidTypeNamePrefix: string;
462+
fixInvalidEnumKeyPrefix: string;
451463
defaultResponseType: boolean;
452464
toJS: boolean;
453465
disableThrowOnError: boolean;

index.js

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -202,6 +202,11 @@ const program = cli({
202202
description: "sort fields and types",
203203
default: codeGenBaseConfig.sortTypes,
204204
},
205+
{
206+
flags: "--extract-enums",
207+
description: "extract all enums from inline interface\\type content to typescript enum construction",
208+
default: codeGenBaseConfig.extractEnums,
209+
},
205210
],
206211
});
207212

package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,8 @@
5959
"test:enums2.0": "node tests/spec/enums-2.0/test.js",
6060
"test:another-query-params": "node tests/spec/another-query-params/test.js",
6161
"test:on-insert-path-param": "node tests/spec/on-insert-path-param/test.js",
62-
"test:extra-templates": "node tests/spec/extra-templates/test.js"
62+
"test:extra-templates": "node tests/spec/extra-templates/test.js",
63+
"test:extract-enums": "node tests/spec/extract-enums/test.js"
6364
},
6465
"author": "acacode",
6566
"license": "MIT",

src/configuration.js

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,7 @@ class CodeGenConfig {
7272
extractRequestBody = false;
7373
extractResponseBody = false;
7474
extractResponseError = false;
75+
extractEnums = false;
7576
fileNames = {
7677
dataContracts: "data-contracts",
7778
routeTypes: "route-types",
@@ -85,13 +86,14 @@ class CodeGenConfig {
8586
onBuildRoutePath: (routeData) => void 0,
8687
onInsertPathParam: (pathParam) => void 0,
8788
onCreateComponent: (schema) => schema,
89+
onPreParseSchema: (originalSchema, typeName, schemaType) => void 0,
8890
onParseSchema: (originalSchema, parsedSchema) => parsedSchema,
8991
onCreateRoute: (routeData) => routeData,
9092
onInit: (config) => config,
9193
onPrepareConfig: (apiConfig) => apiConfig,
9294
onCreateRequestParams: (rawType) => {},
9395
onCreateRouteName: () => {},
94-
onFormatTypeName: (typeName, rawTypeName) => {},
96+
onFormatTypeName: (typeName, rawTypeName, schemaType) => {},
9597
onFormatRouteName: (routeInfo, templateRouteName) => {},
9698
};
9799
defaultResponseType;
@@ -151,6 +153,8 @@ class CodeGenConfig {
151153

152154
jsPrimitiveTypes = [];
153155
jsEmptyTypes = [];
156+
fixInvalidTypeNamePrefix = "Type";
157+
fixInvalidEnumKeyPrefix = "Value";
154158

155159
successResponseStatusRange = [200, 299];
156160

src/schema-parser/schema-formatters.js

Lines changed: 14 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -28,10 +28,7 @@ class SchemaFormatters {
2828

2929
base = {
3030
[SCHEMA_TYPES.ENUM]: (parsedSchema) => {
31-
const isNumberEnum = _.some(parsedSchema.content, (content) => typeof content.key === "number");
32-
const formatAsUnionType = !!(isNumberEnum || this.config.generateUnionEnums);
33-
34-
if (formatAsUnionType) {
31+
if (this.config.generateUnionEnums) {
3532
return {
3633
...parsedSchema,
3734
$content: parsedSchema.content,
@@ -61,6 +58,19 @@ class SchemaFormatters {
6158
},
6259
};
6360
inline = {
61+
[SCHEMA_TYPES.ENUM]: (parsedSchema) => {
62+
return {
63+
...parsedSchema,
64+
content: parsedSchema.$ref
65+
? parsedSchema.typeName
66+
: this.config.Ts.UnionType(
67+
_.compact([
68+
..._.map(parsedSchema.content, ({ value }) => `${value}`),
69+
parsedSchema.nullable && this.config.Ts.Keyword.Null,
70+
]),
71+
),
72+
};
73+
},
6474
[SCHEMA_TYPES.OBJECT]: (parsedSchema) => {
6575
if (_.isString(parsedSchema.content)) {
6676
return {
@@ -81,19 +91,6 @@ class SchemaFormatters {
8191
),
8292
};
8393
},
84-
[SCHEMA_TYPES.ENUM]: (parsedSchema) => {
85-
return {
86-
...parsedSchema,
87-
content: parsedSchema.$ref
88-
? parsedSchema.typeName
89-
: this.config.Ts.UnionType(
90-
_.compact([
91-
..._.map(parsedSchema.content, ({ value }) => `${value}`),
92-
parsedSchema.nullable && this.config.Ts.Keyword.Null,
93-
]),
94-
),
95-
};
96-
},
9794
};
9895

9996
/**

src/schema-parser/schema-parser.js

Lines changed: 46 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@ const _ = require("lodash");
33
const { SchemaFormatters } = require("./schema-formatters");
44
const { internalCase } = require("../util/internal-case");
55
const { SchemaUtils } = require("./schema-utils");
6+
const { camelCase } = require("lodash");
7+
const { pascalCase } = require("../util/pascal-case");
68

79
class SchemaParser {
810
/**
@@ -28,6 +30,8 @@ class SchemaParser {
2830
*/
2931
schemaUtils;
3032

33+
$processingSchemaPath = [];
34+
3135
constructor(config, logger, templates, schemaComponentsMap, typeName) {
3236
this.config = config;
3337
this.schemaComponentsMap = schemaComponentsMap;
@@ -87,13 +91,16 @@ class SchemaParser {
8791

8892
baseSchemaParsers = {
8993
[SCHEMA_TYPES.ENUM]: (schema, typeName) => {
94+
if (this.config.extractEnums && !typeName) {
95+
const generatedTypeName = this.config.componentTypeNameResolver.resolve([this.buildTypeNameFromPath()]);
96+
const schemaComponent = this.schemaComponentsMap.createComponent("schemas", generatedTypeName, { ...schema });
97+
return this.parseSchema(schemaComponent, generatedTypeName);
98+
}
99+
90100
const refType = this.schemaUtils.getSchemaRefType(schema);
91101
const $ref = (refType && refType.$ref) || null;
92-
const enumNamesAsValues = this.config.enumNamesAsValues;
93102
const keyType = this.getSchemaType(schema);
94103
const enumNames = this.schemaUtils.getEnumNames(schema);
95-
const isIntegerOrBooleanEnum =
96-
keyType === this.getSchemaType({ type: "number" }) || keyType === this.getSchemaType({ type: "boolean" });
97104
let content = null;
98105

99106
const formatValue = (value) => {
@@ -114,10 +121,19 @@ class SchemaParser {
114121
content = _.map(enumNames, (enumName, index) => {
115122
const enumValue = _.get(schema.enum, index);
116123
const formattedKey =
117-
(enumName && this.typeName.format(enumName, { ignorePrefix: true, ignoreSuffix: true })) ||
118-
this.typeName.format(enumValue, { ignorePrefix: true, ignoreSuffix: true });
119-
120-
if (enumNamesAsValues || _.isUndefined(enumValue)) {
124+
(enumName &&
125+
this.typeName.format(enumName, {
126+
ignorePrefix: true,
127+
ignoreSuffix: true,
128+
type: "enum-key",
129+
})) ||
130+
this.typeName.format(`${enumValue}`, {
131+
ignorePrefix: true,
132+
ignoreSuffix: true,
133+
type: "enum-key",
134+
});
135+
136+
if (this.config.enumNamesAsValues || _.isUndefined(enumValue)) {
121137
return {
122138
key: formattedKey,
123139
type: this.config.Ts.Keyword.String,
@@ -134,7 +150,11 @@ class SchemaParser {
134150
} else {
135151
content = _.map(schema.enum, (key) => {
136152
return {
137-
key: isIntegerOrBooleanEnum ? key : this.typeName.format(key, { ignorePrefix: true, ignoreSuffix: true }),
153+
key: this.typeName.format(`${key}`, {
154+
ignorePrefix: true,
155+
ignoreSuffix: true,
156+
type: "enum-key",
157+
}),
138158
type: keyType,
139159
value: formatValue(key),
140160
};
@@ -149,10 +169,7 @@ class SchemaParser {
149169
schemaType: SCHEMA_TYPES.ENUM,
150170
type: SCHEMA_TYPES.ENUM,
151171
keyType: keyType,
152-
typeIdentifier:
153-
this.config.generateUnionEnums || (!enumNames && isIntegerOrBooleanEnum)
154-
? this.config.Ts.Keyword.Type
155-
: this.config.Ts.Keyword.Enum,
172+
typeIdentifier: this.config.generateUnionEnums ? this.config.Ts.Keyword.Type : this.config.Ts.Keyword.Enum,
156173
name: typeName,
157174
description: this.schemaFormatters.formatDescription(schema.description),
158175
content,
@@ -276,13 +293,16 @@ class SchemaParser {
276293
const { properties, additionalProperties } = schema || {};
277294

278295
const propertiesContent = _.map(properties, (property, name) => {
296+
this.$processingSchemaPath.push(name);
279297
const required = this.schemaUtils.isPropertyRequired(name, property, schema);
280298
const rawTypeData = _.get(this.schemaUtils.getSchemaRefType(property), "rawTypeData", {});
281299
const nullable = !!(rawTypeData.nullable || property.nullable);
282300
const fieldName = this.typeName.isValidName(name) ? name : this.config.Ts.StringValue(name);
283301
const fieldValue = this.getInlineParseContent(property);
284302
const readOnly = property.readOnly;
285303

304+
this.$processingSchemaPath.pop();
305+
286306
return {
287307
...property,
288308
$$raw: property,
@@ -353,12 +373,17 @@ class SchemaParser {
353373
if (schema.items && !schema.type) {
354374
schema.type = SCHEMA_TYPES.ARRAY;
355375
}
356-
357376
schemaType = this.getInternalSchemaType(schema);
377+
378+
this.$processingSchemaPath.push(typeName);
379+
380+
_.merge(schema, this.config.hooks.onPreParseSchema(schema, typeName, schemaType));
358381
parsedSchema = this.baseSchemaParsers[schemaType](schema, typeName);
359382
schema.$parsed = this.config.hooks.onParseSchema(schema, parsedSchema) || parsedSchema;
360383
}
361384

385+
this.$processingSchemaPath.pop();
386+
362387
return schema.$parsed;
363388
};
364389

@@ -373,6 +398,14 @@ class SchemaParser {
373398
const formattedSchema = this.schemaFormatters.formatSchema(parsedSchema, "base");
374399
return formattedSchema.content;
375400
};
401+
402+
buildTypeNameFromPath = () => {
403+
const schemaPath = _.uniq(_.compact(this.$processingSchemaPath));
404+
405+
if (!schemaPath || !schemaPath[0]) return null;
406+
407+
return internalCase(camelCase(`${schemaPath[0]}_${schemaPath[schemaPath.length - 1]}`));
408+
};
376409
}
377410

378411
module.exports = {

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