swagger-typescript-api 9.2.0 → 10.0.0

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/src/index.js CHANGED
@@ -21,6 +21,7 @@ const { generateOutputFiles } = require("./output");
21
21
  const formatFileContent = require("./formatFileContent");
22
22
  const { logger } = require("./logger");
23
23
  const { ComponentTypeNameResolver } = require("./utils/resolveName");
24
+ const { getPrettierOptions } = require("./prettierOptions");
24
25
 
25
26
  module.exports = {
26
27
  constants: constants,
@@ -39,14 +40,19 @@ module.exports = {
39
40
  generateClient = config.generateClient,
40
41
  httpClientType = config.httpClientType,
41
42
  generateUnionEnums = config.generateUnionEnums,
43
+ addReadonly = config.addReadonly,
42
44
  moduleNameIndex = config.moduleNameIndex,
43
45
  moduleNameFirstTag = config.moduleNameFirstTag,
44
46
  extractRequestParams = config.extractRequestParams,
45
47
  extractRequestBody = config.extractRequestBody,
48
+ extractResponseBody = config.extractResponseBody,
49
+ extractResponseError = config.extractResponseError,
46
50
  defaultResponseType = config.defaultResponseType,
47
51
  unwrapResponseData = config.unwrapResponseData,
52
+ disableThrowOnError = config.disableThrowOnError,
53
+ sortTypes = config.sortTypes,
48
54
  singleHttpClient = config.singleHttpClient,
49
- prettier: prettierOptions = constants.PRETTIER_OPTIONS,
55
+ prettier: prettierOptions = getPrettierOptions(),
50
56
  hooks: rawHooks,
51
57
  extraTemplates,
52
58
  enumNamesAsValues,
@@ -56,6 +62,9 @@ module.exports = {
56
62
  silent = config.silent,
57
63
  typePrefix = config.typePrefix,
58
64
  typeSuffix = config.typeSuffix,
65
+ patch = config.patch,
66
+ authorizationToken,
67
+ apiClassName = config.apiClassName,
59
68
  }) =>
60
69
  new Promise((resolve, reject) => {
61
70
  addToConfig({
@@ -66,12 +75,15 @@ module.exports = {
66
75
  generateResponses,
67
76
  templates,
68
77
  generateUnionEnums,
78
+ addReadonly,
69
79
  moduleNameIndex,
70
80
  moduleNameFirstTag,
71
81
  prettierOptions,
72
82
  modular,
73
83
  extractRequestParams,
74
84
  extractRequestBody,
85
+ extractResponseBody,
86
+ extractResponseError,
75
87
  hooks: _.merge(config.hooks, rawHooks || {}),
76
88
  enumNamesAsValues,
77
89
  disableStrictSSL,
@@ -79,16 +91,20 @@ module.exports = {
79
91
  cleanOutput,
80
92
  defaultResponseType,
81
93
  unwrapResponseData,
94
+ disableThrowOnError,
95
+ sortTypes,
82
96
  singleHttpClient,
83
97
  constants,
84
98
  silent,
85
99
  toJS: translateToJavaScript,
86
100
  typePrefix,
87
101
  typeSuffix,
102
+ patch,
103
+ apiClassName,
88
104
  });
89
105
  (spec
90
- ? convertSwaggerObject(spec)
91
- : getSwaggerObject(input, url, disableStrictSSL, disableProxy)
106
+ ? convertSwaggerObject(spec, { patch })
107
+ : getSwaggerObject(input, url, disableStrictSSL, disableProxy, authorizationToken, { patch })
92
108
  )
93
109
  .then(({ usageSchema, originalSchema }) => {
94
110
  const templatePaths = getTemplatePaths(config);
@@ -113,9 +129,7 @@ module.exports = {
113
129
 
114
130
  const componentsMap = createComponentsMap(components);
115
131
 
116
- const componentSchemasNames = filterComponentsMap(componentsMap, "schemas").map(
117
- (c) => c.typeName,
118
- );
132
+ const componentSchemasNames = filterComponentsMap(componentsMap, "schemas").map((c) => c.typeName);
119
133
 
120
134
  addToConfig({
121
135
  componentTypeNameResolver: new ComponentTypeNameResolver(componentSchemasNames),
@@ -138,11 +152,44 @@ module.exports = {
138
152
  const hasFormDataRoutes = routes.some((route) => route.hasFormDataParams);
139
153
 
140
154
  const usageComponentSchemas = filterComponentsMap(componentsMap, "schemas");
155
+ const sortByProperty = (o1, o2, propertyName) => {
156
+ if (o1[propertyName] > o2[propertyName]) {
157
+ return 1;
158
+ }
159
+ if (o1[propertyName] < o2[propertyName]) {
160
+ return -1;
161
+ }
162
+ return 0;
163
+ };
164
+ const sortByTypeName = (o1, o2) => sortByProperty(o1, o2, "typeName");
165
+
166
+ const sortByName = (o1, o2) => sortByProperty(o1, o2, "name");
167
+
168
+ const sortSchemas = (schemas) => {
169
+ if (config.sortTypes) {
170
+ return schemas.sort(sortByTypeName).map((schema) => {
171
+ if (schema.rawTypeData?.properties) {
172
+ return {
173
+ ...schema,
174
+ rawTypeData: {
175
+ ...schema.rawTypeData,
176
+ $parsed: {
177
+ ...schema.rawTypeData["$parsed"],
178
+ content: schema.rawTypeData["$parsed"].content.sort(sortByName),
179
+ },
180
+ },
181
+ };
182
+ }
183
+ return schema;
184
+ });
185
+ }
186
+ return schemas;
187
+ };
141
188
 
142
189
  const rawConfiguration = {
143
190
  apiConfig: createApiConfig(usageSchema),
144
191
  config,
145
- modelTypes: _.map(usageComponentSchemas, prepareModelType),
192
+ modelTypes: _.map(sortSchemas(usageComponentSchemas), prepareModelType),
146
193
  rawModelTypes: usageComponentSchemas,
147
194
  hasFormDataRoutes,
148
195
  hasSecurityRoutes,
@@ -0,0 +1,23 @@
1
+ const { cosmiconfigSync } = require("cosmiconfig");
2
+ const constants = require("./constants");
3
+
4
+ /**
5
+ * Get prettier options from user's project or return the default one
6
+ * @return {import('prettier').Options} Prettier options
7
+ */
8
+ function getPrettierOptions() {
9
+ const prettier = cosmiconfigSync("prettier").search();
10
+
11
+ if (prettier) {
12
+ return {
13
+ ...prettier.config,
14
+ parser: "typescript",
15
+ };
16
+ }
17
+
18
+ return constants.PRETTIER_OPTIONS;
19
+ }
20
+
21
+ module.exports = {
22
+ getPrettierOptions,
23
+ };
package/src/routeNames.js CHANGED
@@ -13,19 +13,15 @@ const getRouteName = (routeInfo) => {
13
13
  config,
14
14
  });
15
15
 
16
- const routeName =
17
- config.hooks.onFormatRouteName(routeInfo, routeNameFromTemplate) || routeNameFromTemplate;
16
+ const routeName = config.hooks.onFormatRouteName(routeInfo, routeNameFromTemplate) || routeNameFromTemplate;
18
17
 
19
18
  const duplicateIdentifier = `${moduleName}|${routeName}`;
20
19
 
21
20
  if (routeNameDuplicatesMap.has(duplicateIdentifier)) {
22
- routeNameDuplicatesMap.set(
23
- duplicateIdentifier,
24
- routeNameDuplicatesMap.get(duplicateIdentifier) + 1,
25
- );
21
+ routeNameDuplicatesMap.set(duplicateIdentifier, routeNameDuplicatesMap.get(duplicateIdentifier) + 1);
26
22
 
27
23
  logger.warn(
28
- `Module "${moduleName}" already have method "${routeName}()"`,
24
+ `Module "${moduleName}" already has method "${routeName}()"`,
29
25
  `\nThis method has been renamed to "${
30
26
  routeName + routeNameDuplicatesMap.get(duplicateIdentifier)
31
27
  }()" to solve conflict names.`,
package/src/routes.js CHANGED
@@ -1,12 +1,5 @@
1
1
  const _ = require("lodash");
2
- const {
3
- types,
4
- parseSchema,
5
- getType,
6
- getRefType,
7
- getInlineParseContent,
8
- checkAndAddNull,
9
- } = require("./schema");
2
+ const { types, parseSchema, getType, getRefType, getInlineParseContent, checkAndAddNull } = require("./schema");
10
3
  const { formatModelName } = require("./modelNames");
11
4
  const {
12
5
  DEFAULT_BODY_ARG_NAME,
@@ -19,7 +12,7 @@ const {
19
12
  } = require("./constants");
20
13
  const { formatDescription, classNameCase } = require("./common");
21
14
  const { config } = require("./config");
22
- const { nanoid } = require("nanoid");
15
+ const { generateId } = require("./utils/id");
23
16
  const { getRouteName } = require("./routeNames");
24
17
  const { createComponent } = require("./components");
25
18
  const { logger } = require("./logger");
@@ -47,26 +40,15 @@ const getSchemaFromRequestType = (requestInfo) => {
47
40
  return null;
48
41
  };
49
42
 
50
- const getTypeFromRequestInfo = ({
51
- requestInfo,
52
- parsedSchemas,
53
- operationId,
54
- defaultType,
55
- typeName,
56
- }) => {
43
+ const getTypeFromRequestInfo = ({ requestInfo, parsedSchemas, operationId, defaultType, typeName }) => {
57
44
  // TODO: make more flexible pick schema without content type
58
45
  const schema = getSchemaFromRequestType(requestInfo);
59
46
  const refTypeInfo = getRefType(requestInfo);
60
47
 
61
48
  if (schema) {
62
49
  const content = getInlineParseContent(schema, typeName);
63
- const foundedSchemaByName = _.find(
64
- parsedSchemas,
65
- (parsedSchema) => formatModelName(parsedSchema.name) === content,
66
- );
67
- const foundSchemaByContent = _.find(parsedSchemas, (parsedSchema) =>
68
- _.isEqual(parsedSchema.content, content),
69
- );
50
+ const foundedSchemaByName = _.find(parsedSchemas, (parsedSchema) => formatModelName(parsedSchema.name) === content);
51
+ const foundSchemaByContent = _.find(parsedSchemas, (parsedSchema) => _.isEqual(parsedSchema.content, content));
70
52
 
71
53
  const foundSchema = foundedSchemaByName || foundSchemaByContent;
72
54
 
@@ -88,10 +70,7 @@ const getTypeFromRequestInfo = ({
88
70
  return formatModelName(refTypeInfo.typeName);
89
71
  case "responses":
90
72
  case "requestBodies":
91
- return getInlineParseContent(
92
- getSchemaFromRequestType(refTypeInfo.rawTypeData),
93
- refTypeInfo.typeName || null,
94
- );
73
+ return getInlineParseContent(getSchemaFromRequestType(refTypeInfo.rawTypeData), refTypeInfo.typeName || null);
95
74
  default:
96
75
  return getInlineParseContent(refTypeInfo.rawTypeData, refTypeInfo.typeName || null);
97
76
  }
@@ -344,9 +323,7 @@ const createRequestParamsSchema = ({
344
323
  if (fixedSchema) return fixedSchema;
345
324
 
346
325
  if (extractRequestParams) {
347
- const typeName = config.componentTypeNameResolver.resolve([
348
- classNameCase(`${routeName.usage} Params`),
349
- ]);
326
+ const typeName = config.componentTypeNameResolver.resolve([classNameCase(`${routeName.usage} Params`)]);
350
327
 
351
328
  return createComponent("schemas", typeName, { ...schema });
352
329
  }
@@ -358,9 +335,7 @@ const getContentTypes = (requestInfo, extraContentTypes) =>
358
335
  _.uniq(
359
336
  _.compact([
360
337
  ...(extraContentTypes || []),
361
- ..._.flatten(
362
- _.map(requestInfo, (requestInfoData) => requestInfoData && _.keys(requestInfoData.content)),
363
- ),
338
+ ..._.flatten(_.map(requestInfo, (requestInfoData) => requestInfoData && _.keys(requestInfoData.content))),
364
339
  ]),
365
340
  );
366
341
 
@@ -400,10 +375,7 @@ const getRequestBodyInfo = (routeInfo, routeParams, parsedSchemas, routeName) =>
400
375
  let schema = null;
401
376
  let type = null;
402
377
 
403
- const contentTypes = getContentTypes(
404
- [requestBody],
405
- [...(consumes || []), routeInfo["x-contentType"]],
406
- );
378
+ const contentTypes = getContentTypes([requestBody], [...(consumes || []), routeInfo["x-contentType"]]);
407
379
  let contentKind = getContentKind(contentTypes);
408
380
 
409
381
  let typeName = null;
@@ -454,8 +426,7 @@ const getRequestBodyInfo = (routeInfo, routeParams, parsedSchemas, routeName) =>
454
426
  contentKind,
455
427
  schema,
456
428
  type,
457
- required:
458
- requestBody && (typeof requestBody.required === "undefined" || !!requestBody.required),
429
+ required: requestBody && (typeof requestBody.required === "undefined" || !!requestBody.required),
459
430
  };
460
431
  };
461
432
 
@@ -472,9 +443,7 @@ const getResponseBodyInfo = (routeInfo, routeParams, parsedSchemas) => {
472
443
  });
473
444
 
474
445
  const successResponse = responseInfos.find((response) => response.isSuccess);
475
- const errorResponses = responseInfos.filter(
476
- (response) => !response.isSuccess && response.type !== TS_KEYWORDS.ANY,
477
- );
446
+ const errorResponses = responseInfos.filter((response) => !response.isSuccess && response.type !== TS_KEYWORDS.ANY);
478
447
 
479
448
  const handleResponseHeaders = (src) => {
480
449
  if (!src) {
@@ -507,24 +476,16 @@ const getResponseBodyInfo = (routeInfo, routeParams, parsedSchemas) => {
507
476
  responseInfos
508
477
  .map(
509
478
  (response) => `{
510
- data: ${response.type}, status: ${response.status}, statusCode: ${
511
- response.status
512
- }, statusText: "${response.description}", ${handleResponseHeaders(
513
- response.headers,
514
- )} config: {} }`,
479
+ data: ${response.type}, status: ${response.status}, statusCode: ${response.status}, statusText: "${
480
+ response.description
481
+ }", ${handleResponseHeaders(response.headers)} config: {} }`,
515
482
  )
516
483
  .join(" | ") || TS_KEYWORDS.ANY,
517
484
  },
518
485
  };
519
486
  };
520
487
 
521
- const parseRoutes = ({
522
- usageSchema,
523
- parsedSchemas,
524
- moduleNameIndex,
525
- moduleNameFirstTag,
526
- extractRequestParams,
527
- }) => {
488
+ const parseRoutes = ({ usageSchema, parsedSchemas, moduleNameIndex, moduleNameFirstTag, extractRequestParams }) => {
528
489
  const { paths, security: globalSecurity } = usageSchema;
529
490
  const pathsEntries = _.entries(paths);
530
491
 
@@ -553,16 +514,13 @@ const parseRoutes = ({
553
514
  } = routeInfo;
554
515
  const { route, pathParams } = parseRoute(rawRoute);
555
516
 
556
- const routeId = nanoid(12);
517
+ const routeId = generateId();
557
518
  const firstTag = tags && tags.length > 0 ? tags[0] : null;
558
519
  const moduleName =
559
520
  moduleNameFirstTag && firstTag
560
521
  ? _.camelCase(firstTag)
561
522
  : _.camelCase(_.compact(_.split(route, "/"))[moduleNameIndex]);
562
- const hasSecurity = !!(
563
- (globalSecurity && globalSecurity.length) ||
564
- (security && security.length)
565
- );
523
+ const hasSecurity = !!((globalSecurity && globalSecurity.length) || (security && security.length));
566
524
 
567
525
  const routeParams = getRouteParams(routeInfo, pathParams);
568
526
 
@@ -599,12 +557,7 @@ const parseRoutes = ({
599
557
 
600
558
  const routeName = getRouteName(rawRouteInfo);
601
559
 
602
- const requestBodyInfo = getRequestBodyInfo(
603
- routeInfo,
604
- routeParams,
605
- parsedSchemas,
606
- routeName,
607
- );
560
+ const requestBodyInfo = getRequestBodyInfo(routeInfo, routeParams, parsedSchemas, routeName);
608
561
 
609
562
  const requestParamsSchema = createRequestParamsSchema({
610
563
  queryParams: routeParams.query,
@@ -614,13 +567,12 @@ const parseRoutes = ({
614
567
  routeName,
615
568
  });
616
569
 
617
- const queryType = routeParams.query.length
618
- ? getInlineParseContent(queryObjectSchema)
619
- : null;
570
+ extractResponseBodyIfItNeeded(routeInfo, responseBodyInfo, routeParams, rawRouteInfo, routeName);
571
+ extractResponseErrorIfItNeeded(routeInfo, responseBodyInfo, routeParams, rawRouteInfo, routeName);
572
+
573
+ const queryType = routeParams.query.length ? getInlineParseContent(queryObjectSchema) : null;
620
574
  const pathType = routeParams.path.length ? getInlineParseContent(pathObjectSchema) : null;
621
- const headersType = routeParams.header.length
622
- ? getInlineParseContent(headersObjectSchema)
623
- : null;
575
+ const headersType = routeParams.header.length ? getInlineParseContent(headersObjectSchema) : null;
624
576
 
625
577
  const nameResolver = new SpecificArgNameResolver(pathArgsNames);
626
578
 
@@ -634,10 +586,7 @@ const parseRoutes = ({
634
586
  : void 0,
635
587
  body: requestBodyInfo.type
636
588
  ? {
637
- name: nameResolver.resolve([
638
- requestBodyInfo.paramName,
639
- ...RESERVED_BODY_ARG_NAMES,
640
- ]),
589
+ name: nameResolver.resolve([requestBodyInfo.paramName, ...RESERVED_BODY_ARG_NAMES]),
641
590
  optional: !requestBodyInfo.required,
642
591
  type: requestBodyInfo.type,
643
592
  }
@@ -764,10 +713,7 @@ const groupRoutes = (routes) => {
764
713
  if (
765
714
  packRoutes.length > 1 &&
766
715
  usageName !== originalName &&
767
- !_.some(
768
- packRoutes,
769
- ({ routeName, id }) => id !== route.id && originalName === routeName.original,
770
- )
716
+ !_.some(packRoutes, ({ routeName, id }) => id !== route.id && originalName === routeName.original)
771
717
  ) {
772
718
  return {
773
719
  ...route,
@@ -788,6 +734,72 @@ const groupRoutes = (routes) => {
788
734
  );
789
735
  };
790
736
 
737
+ const extractResponseBodyIfItNeeded = (routeInfo, responseBodyInfo, routeParams, rawRouteInfo, routeName) => {
738
+ if (
739
+ config.extractResponseBody &&
740
+ responseBodyInfo.responses.length &&
741
+ responseBodyInfo.success &&
742
+ responseBodyInfo.success.schema
743
+ ) {
744
+ const typeName = config.componentTypeNameResolver.resolve([
745
+ classNameCase(`${routeName.usage} Data`),
746
+ classNameCase(`${routeName.usage} Result`),
747
+ classNameCase(`${routeName.usage} Output`),
748
+ ]);
749
+
750
+ const idx = responseBodyInfo.responses.indexOf(responseBodyInfo.success.schema);
751
+
752
+ let successResponse = responseBodyInfo.success;
753
+
754
+ if (successResponse.schema && !successResponse.schema.$ref) {
755
+ const schema = getSchemaFromRequestType(successResponse.schema);
756
+ successResponse.schema = createComponent("schemas", typeName, { ...schema });
757
+ successResponse.type = getInlineParseContent(successResponse.schema);
758
+
759
+ if (idx > -1) {
760
+ responseBodyInfo.responses[idx] = successResponse.schema;
761
+ }
762
+ }
763
+ }
764
+ };
765
+
766
+ const extractResponseErrorIfItNeeded = (routeInfo, responseBodyInfo, routeParams, rawRouteInfo, routeName) => {
767
+ if (
768
+ config.extractResponseError &&
769
+ responseBodyInfo.responses.length &&
770
+ responseBodyInfo.error.schemas &&
771
+ responseBodyInfo.error.schemas.length
772
+ ) {
773
+ const typeName = config.componentTypeNameResolver.resolve([
774
+ classNameCase(`${routeName.usage} Error`),
775
+ classNameCase(`${routeName.usage} Fail`),
776
+ classNameCase(`${routeName.usage} Fails`),
777
+ classNameCase(`${routeName.usage} ErrorData`),
778
+ classNameCase(`${routeName.usage} HttpError`),
779
+ classNameCase(`${routeName.usage} BadResponse`),
780
+ ]);
781
+
782
+ const errorSchemas = responseBodyInfo.error.schemas.map(getSchemaFromRequestType).filter(Boolean);
783
+
784
+ if (!errorSchemas.length) return;
785
+
786
+ const schema = parseSchema({
787
+ oneOf: errorSchemas,
788
+ title: errorSchemas
789
+ .map((schema) => schema.title)
790
+ .filter(Boolean)
791
+ .join(" "),
792
+ description: errorSchemas
793
+ .map((schema) => schema.description)
794
+ .filter(Boolean)
795
+ .join("\n"),
796
+ });
797
+ const component = createComponent("schemas", typeName, { ...schema });
798
+ responseBodyInfo.error.schemas = [component];
799
+ responseBodyInfo.error.type = formatModelName(component.typeName);
800
+ }
801
+ };
802
+
791
803
  module.exports = {
792
804
  parseRoutes,
793
805
  groupRoutes,
package/src/schema.js CHANGED
@@ -63,12 +63,7 @@ const getTypeAlias = (rawSchema) => {
63
63
  };
64
64
 
65
65
  const getEnumNames = (schema) => {
66
- return (
67
- schema["x-enumNames"] ||
68
- schema["xEnumNames"] ||
69
- schema["x-enumnames"] ||
70
- schema["x-enum-varnames"]
71
- );
66
+ return schema["x-enumNames"] || schema["xEnumNames"] || schema["x-enumnames"] || schema["x-enum-varnames"];
72
67
  };
73
68
 
74
69
  const getInternalSchemaType = (schema) => {
@@ -137,29 +132,36 @@ const getObjectTypeContent = (schema) => {
137
132
  const nullable = !!(rawTypeData.nullable || property.nullable);
138
133
  const fieldName = isValidName(name) ? name : `"${name}"`;
139
134
  const fieldValue = getInlineParseContent(property);
135
+ const readOnly = property.readOnly;
140
136
 
141
137
  return {
142
138
  $$raw: property,
139
+ title: property.title,
143
140
  description: _.compact([
144
141
  property.description ||
145
142
  _.compact(_.map(property[getComplexType(property)], "description"))[0] ||
146
143
  rawTypeData.description ||
147
144
  _.compact(_.map(rawTypeData[getComplexType(rawTypeData)], "description"))[0] ||
148
145
  "",
146
+ !_.isUndefined(property.deprecated) && `@deprecated`,
149
147
  !_.isUndefined(property.format) && `@format ${property.format}`,
150
148
  !_.isUndefined(property.minimum) && `@min ${property.minimum}`,
151
149
  !_.isUndefined(property.maximum) && `@max ${property.maximum}`,
152
150
  !_.isUndefined(property.pattern) && `@pattern ${property.pattern}`,
153
151
  !_.isUndefined(property.example) &&
154
- `@example ${
155
- _.isObject(property.example) ? JSON.stringify(property.example) : property.example
156
- }`,
152
+ `@example ${_.isObject(property.example) ? JSON.stringify(property.example) : property.example}`,
157
153
  ]).join("\n"),
158
154
  isRequired: required,
159
155
  isNullable: nullable,
160
156
  name: fieldName,
161
157
  value: fieldValue,
162
- field: _.compact([fieldName, !required && "?", ": ", fieldValue]).join(""),
158
+ field: _.compact([
159
+ readOnly && config.addReadonly && "readonly ",
160
+ fieldName,
161
+ !required && "?",
162
+ ": ",
163
+ fieldValue,
164
+ ]).join(""),
163
165
  };
164
166
  });
165
167
 
@@ -178,39 +180,73 @@ const getObjectTypeContent = (schema) => {
178
180
  const complexTypeGetter = (schema) => getInlineParseContent(schema);
179
181
  const filterContents = (contents, types) => _.filter(contents, (type) => !_.includes(types, type));
180
182
 
183
+ const makeAddRequiredToChildSchema = (parentSchema) => (childSchema) => {
184
+ let required = childSchema.required || [];
185
+ let properties = childSchema.properties || {};
186
+
187
+ // Inherit all the required fields from the parent schema that are defined
188
+ // either on the parent schema or on the child schema
189
+ // TODO: any that are defined at grandparents or higher are ignored
190
+ required = required.concat(
191
+ (parentSchema.required || []).filter(
192
+ (key) =>
193
+ !required.includes(key) && (_.keys(properties).includes(key) || _.keys(parentSchema.properties).includes(key)),
194
+ ),
195
+ );
196
+
197
+ // Identify properties that are required in the child schema, but
198
+ // defined only in the parent schema (TODO: this only works one level deep)
199
+ const parentPropertiesRequiredByChild = required.filter(
200
+ (key) => !_.keys(childSchema.properties).includes(key) && _.keys(parentSchema.properties).includes(key),
201
+ );
202
+
203
+ // Add such properties to the child so that they can be overriden and made required
204
+ properties = {
205
+ ...properties,
206
+ ...parentPropertiesRequiredByChild.reduce(
207
+ (additionalProperties, key) => ({
208
+ ...additionalProperties,
209
+ [key]: (parentSchema.properties || {})[key],
210
+ }),
211
+ {},
212
+ ),
213
+ };
214
+
215
+ return _.merge(
216
+ {
217
+ required: required,
218
+ properties: properties,
219
+ },
220
+ childSchema,
221
+ );
222
+ };
223
+
181
224
  const complexSchemaParsers = {
182
225
  [SCHEMA_TYPES.COMPLEX_ONE_OF]: (schema) => {
183
226
  // T1 | T2
184
- const combined = _.map(schema.oneOf, complexTypeGetter);
227
+ const combined = _.map(_.map(schema.oneOf, makeAddRequiredToChildSchema(schema)), complexTypeGetter);
185
228
 
186
229
  return checkAndAddNull(schema, filterContents(combined, [TS_KEYWORDS.ANY]).join(" | "));
187
230
  },
188
231
  [SCHEMA_TYPES.COMPLEX_ALL_OF]: (schema) => {
189
232
  // T1 & T2
190
- const combined = _.map(schema.allOf, complexTypeGetter);
233
+ const combined = _.map(_.map(schema.allOf, makeAddRequiredToChildSchema(schema)), complexTypeGetter);
191
234
  return checkAndAddNull(
192
235
  schema,
193
- filterContents(combined, [...JS_EMPTY_TYPES, ...JS_PRIMITIVE_TYPES, TS_KEYWORDS.ANY]).join(
194
- " & ",
195
- ),
236
+ filterContents(combined, [...JS_EMPTY_TYPES, ...JS_PRIMITIVE_TYPES, TS_KEYWORDS.ANY]).join(" & "),
196
237
  );
197
238
  },
198
239
  [SCHEMA_TYPES.COMPLEX_ANY_OF]: (schema) => {
199
240
  // T1 | T2 | (T1 & T2)
200
- const combined = _.map(schema.anyOf, complexTypeGetter);
201
- const nonEmptyTypesCombined = filterContents(combined, [
202
- ...JS_EMPTY_TYPES,
203
- ...JS_PRIMITIVE_TYPES,
204
- TS_KEYWORDS.ANY,
205
- ]);
241
+ const combined = _.map(_.map(schema.anyOf, makeAddRequiredToChildSchema(schema)), complexTypeGetter);
242
+ const nonEmptyTypesCombined = filterContents(combined, [...JS_EMPTY_TYPES, ...JS_PRIMITIVE_TYPES, TS_KEYWORDS.ANY]);
206
243
  return checkAndAddNull(
207
244
  schema,
208
- `${combined.join(" | ")}` +
209
- (nonEmptyTypesCombined.length > 1 ? ` | (${nonEmptyTypesCombined.join(" & ")})` : ""),
245
+ `${combined.join(" | ")}` + (nonEmptyTypesCombined.length > 1 ? ` | (${nonEmptyTypesCombined.join(" & ")})` : ""),
210
246
  );
211
247
  },
212
248
  // TODO
213
- [SCHEMA_TYPES.COMPLEX_NOT_OF]: (schema) => {
249
+ [SCHEMA_TYPES.COMPLEX_NOT]: (schema) => {
214
250
  // TODO
215
251
  return TS_KEYWORDS.ANY;
216
252
  },
@@ -227,8 +263,7 @@ const getComplexType = (schema) => {
227
263
  };
228
264
 
229
265
  const attachParsedRef = (originalSchema, parsedSchema) => {
230
- const parsedSchemaAfterHook =
231
- config.hooks.onParseSchema(originalSchema, parsedSchema) || parsedSchema;
266
+ const parsedSchemaAfterHook = config.hooks.onParseSchema(originalSchema, parsedSchema) || parsedSchema;
232
267
 
233
268
  if (originalSchema) {
234
269
  originalSchema.$parsed = parsedSchemaAfterHook;
@@ -263,12 +298,7 @@ const schemaParsers = {
263
298
  return {
264
299
  key: formattedKey,
265
300
  type: keyType,
266
- value:
267
- enumValue === null
268
- ? enumValue
269
- : isIntegerOrBooleanEnum
270
- ? `${enumValue}`
271
- : `"${enumValue}"`,
301
+ value: enumValue === null ? enumValue : isIntegerOrBooleanEnum ? `${enumValue}` : `"${enumValue}"`,
272
302
  };
273
303
  });
274
304
  } else {
@@ -290,9 +320,7 @@ const schemaParsers = {
290
320
  type: SCHEMA_TYPES.ENUM,
291
321
  keyType: keyType,
292
322
  typeIdentifier:
293
- config.generateUnionEnums || (!enumNames && isIntegerOrBooleanEnum)
294
- ? TS_KEYWORDS.TYPE
295
- : TS_KEYWORDS.ENUM,
323
+ config.generateUnionEnums || (!enumNames && isIntegerOrBooleanEnum) ? TS_KEYWORDS.TYPE : TS_KEYWORDS.ENUM,
296
324
  name: typeName,
297
325
  description: formatDescription(schema.description),
298
326
  content,
@@ -331,8 +359,7 @@ const schemaParsers = {
331
359
  content:
332
360
  _.compact([
333
361
  complexSchemaContent && `(${complexSchemaContent})`,
334
- getInternalSchemaType(simpleSchema) === TS_KEYWORDS.OBJECT &&
335
- getInlineParseContent(simpleSchema),
362
+ getInternalSchemaType(simpleSchema) === TS_KEYWORDS.OBJECT && getInlineParseContent(simpleSchema),
336
363
  ]).join(" & ") || TS_KEYWORDS.ANY,
337
364
  });
338
365
  },
@@ -400,10 +427,7 @@ const parseSchema = (rawSchema, typeName, formattersMap) => {
400
427
  parsedSchema = schemaParsers[schemaType](fixedRawSchema, typeName);
401
428
  }
402
429
 
403
- return (
404
- (formattersMap && formattersMap[schemaType] && formattersMap[schemaType](parsedSchema)) ||
405
- parsedSchema
406
- );
430
+ return (formattersMap && formattersMap[schemaType] && formattersMap[schemaType](parsedSchema)) || parsedSchema;
407
431
  };
408
432
 
409
433
  const parseSchemas = (components) =>
@@ -412,8 +436,7 @@ const parseSchemas = (components) =>
412
436
  const getInlineParseContent = (rawTypeData, typeName = null) =>
413
437
  parseSchema(rawTypeData, typeName, inlineExtraFormatters).content;
414
438
 
415
- const getParseContent = (rawTypeData, typeName = null) =>
416
- parseSchema(rawTypeData, typeName).content;
439
+ const getParseContent = (rawTypeData, typeName = null) => parseSchema(rawTypeData, typeName).content;
417
440
 
418
441
  module.exports = {
419
442
  types,