@twin.org/ts-to-openapi 0.0.1-next.9 → 0.0.2-next.1

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/bin/index.js CHANGED
File without changes
@@ -5,6 +5,7 @@ var node_url = require('node:url');
5
5
  var cliCore = require('@twin.org/cli-core');
6
6
  var promises = require('node:fs/promises');
7
7
  var core = require('@twin.org/core');
8
+ var toolsCore = require('@twin.org/tools-core');
8
9
  var web = require('@twin.org/web');
9
10
  var tsJsonSchemaGenerator = require('ts-json-schema-generator');
10
11
 
@@ -239,7 +240,20 @@ async function tsToOpenApi(config, outputFile, workingDirectory) {
239
240
  }
240
241
  }
241
242
  cliCore.CLIDisplay.task(core.I18n.formatMessage("commands.ts-to-openapi.progress.generatingSchemas"));
242
- const schemas = await generateSchemas(typeRoots, types, workingDirectory);
243
+ const autoExpandTypes = config.autoExpandTypes ?? [];
244
+ const defaultExpandTypes = ["ObjectOrArray<.*>"];
245
+ for (const defaultType of defaultExpandTypes) {
246
+ if (!autoExpandTypes.includes(defaultType)) {
247
+ autoExpandTypes.push(defaultType);
248
+ }
249
+ }
250
+ const schemas = await generateSchemas(typeRoots, types, autoExpandTypes, workingDirectory);
251
+ for (const type in schemas) {
252
+ if (core.Is.object(config.overrides?.[type])) {
253
+ cliCore.CLIDisplay.task(core.I18n.formatMessage("commands.ts-to-openapi.progress.overridingSchema"));
254
+ schemas[type] = config.overrides?.[type];
255
+ }
256
+ }
243
257
  const usedCommonResponseTypes = [];
244
258
  for (let i = 0; i < inputResults.length; i++) {
245
259
  const result = inputResults[i];
@@ -659,8 +673,13 @@ async function finaliseOutput(usedCommonResponseTypes, schemas, openApi, securit
659
673
  }
660
674
  }
661
675
  }
662
- if (finalSchemas.HttpStatusCode) {
663
- delete finalSchemas.HttpStatusCode;
676
+ // Remove standard types that we don't want in the final output
677
+ const removeTypes = ["HttpStatusCode", "Uint8Array", "ArrayBuffer"];
678
+ for (const type of removeTypes) {
679
+ delete finalSchemas[type];
680
+ }
681
+ for (const type in finalSchemas) {
682
+ toolsCore.JsonSchemaHelper.processArrays(finalSchemas[type]);
664
683
  }
665
684
  const schemaKeys = Object.keys(finalSchemas);
666
685
  schemaKeys.sort();
@@ -672,7 +691,7 @@ async function finaliseOutput(usedCommonResponseTypes, schemas, openApi, securit
672
691
  schemas: sortedSchemas,
673
692
  securitySchemes
674
693
  };
675
- let json = JSON.stringify(openApi, undefined, " ");
694
+ let json = JSON.stringify(openApi, undefined, "\t");
676
695
  // Remove the reference only schemas, repeating until no more substitutions
677
696
  let performedSubstitution;
678
697
  do {
@@ -693,10 +712,7 @@ async function finaliseOutput(usedCommonResponseTypes, schemas, openApi, securit
693
712
  // Remove the array [] from the type names
694
713
  // eslint-disable-next-line unicorn/better-regex
695
714
  json = json.replace(/#\/components\/schemas\/(.*)\[\]/g, "#/components/schemas/ListOf$1");
696
- // Remove the partial markers
697
- json = json.replace(/Partial%3CI(.*?)%3E/g, "$1");
698
- // Cleanup the generic markers
699
- json = json.replace(/%3Cunknown%3E/g, "");
715
+ json = toolsCore.JsonSchemaHelper.normaliseTypeName(json);
700
716
  // Remove external references
701
717
  for (const finalExternal in finalExternals) {
702
718
  json = json.replace(new RegExp(`"#/components/schemas/${core.StringHelper.stripPrefix(finalExternal)}"`, "g"), `"${finalExternals[finalExternal]}"`);
@@ -706,7 +722,7 @@ async function finaliseOutput(usedCommonResponseTypes, schemas, openApi, securit
706
722
  await promises.mkdir(path.dirname(outputFile), { recursive: true });
707
723
  }
708
724
  catch { }
709
- await promises.writeFile(outputFile, json);
725
+ await promises.writeFile(outputFile, `${json}\n`);
710
726
  }
711
727
  /**
712
728
  * Build the security schemas from the config.
@@ -826,7 +842,7 @@ async function processPackageRestDetails(restRoutes) {
826
842
  * @returns Nothing.
827
843
  * @internal
828
844
  */
829
- async function generateSchemas(modelDirWildcards, types, outputWorkingDir) {
845
+ async function generateSchemas(modelDirWildcards, types, autoExpandTypes, outputWorkingDir) {
830
846
  const allSchemas = {};
831
847
  const arraySingularTypes = [];
832
848
  for (const type of types) {
@@ -850,16 +866,14 @@ async function generateSchemas(modelDirWildcards, types, outputWorkingDir) {
850
866
  const schema = generator.createSchema("*");
851
867
  if (schema.definitions) {
852
868
  for (const def in schema.definitions) {
853
- // Remove the partial markers
854
- let defSub = def.replace(/^Partial<(.*?)>/g, "$1");
855
- // Cleanup the generic markers
856
- defSub = defSub.replace(/</g, "%3C").replace(/>/g, "%3E");
869
+ const defSub = toolsCore.JsonSchemaHelper.normaliseTypeName(def);
857
870
  allSchemas[defSub] = schema.definitions[def];
858
871
  }
859
872
  }
860
873
  }
861
874
  const referencedSchemas = {};
862
- extractTypes(allSchemas, types, referencedSchemas);
875
+ toolsCore.JsonSchemaHelper.extractTypes(allSchemas, [...types, ...autoExpandTypes], referencedSchemas);
876
+ toolsCore.JsonSchemaHelper.expandTypes(referencedSchemas, autoExpandTypes);
863
877
  for (const arraySingularType of arraySingularTypes) {
864
878
  referencedSchemas[`${arraySingularType}[]`] = {
865
879
  type: "array",
@@ -870,74 +884,6 @@ async function generateSchemas(modelDirWildcards, types, outputWorkingDir) {
870
884
  }
871
885
  return referencedSchemas;
872
886
  }
873
- /**
874
- * Extract the required types from all the known schemas.
875
- * @param allSchemas All the known schemas.
876
- * @param requiredTypes The required types.
877
- * @param referencedSchemas The references schemas.
878
- * @internal
879
- */
880
- function extractTypes(allSchemas, requiredTypes, referencedSchemas) {
881
- for (const type of requiredTypes) {
882
- if (allSchemas[type] && !referencedSchemas[type]) {
883
- referencedSchemas[type] = allSchemas[type];
884
- extractTypesFromSchema(allSchemas, allSchemas[type], referencedSchemas);
885
- }
886
- }
887
- }
888
- /**
889
- * Extract type from properties definition.
890
- * @param allTypes All the known types.
891
- * @param schema The schema to extract from.
892
- * @param output The output types.
893
- * @internal
894
- */
895
- function extractTypesFromSchema(allTypes, schema, output) {
896
- const additionalTypes = [];
897
- if (core.Is.stringValue(schema.$ref)) {
898
- additionalTypes.push(schema.$ref.replace("#/definitions/", "").replace(/^Partial%3C(.*?)%3E/g, "$1"));
899
- }
900
- else if (core.Is.object(schema.items)) {
901
- if (core.Is.arrayValue(schema.items)) {
902
- for (const itemSchema of schema.items) {
903
- extractTypesFromSchema(allTypes, itemSchema, output);
904
- }
905
- }
906
- else {
907
- extractTypesFromSchema(allTypes, schema.items, output);
908
- }
909
- }
910
- else if (core.Is.object(schema.properties) || core.Is.object(schema.additionalProperties)) {
911
- if (core.Is.object(schema.properties)) {
912
- for (const prop in schema.properties) {
913
- const p = schema.properties[prop];
914
- if (core.Is.object(p)) {
915
- extractTypesFromSchema(allTypes, p, output);
916
- }
917
- }
918
- }
919
- if (core.Is.object(schema.additionalProperties)) {
920
- extractTypesFromSchema(allTypes, schema.additionalProperties, output);
921
- }
922
- }
923
- else if (core.Is.arrayValue(schema.anyOf)) {
924
- for (const prop of schema.anyOf) {
925
- if (core.Is.object(prop)) {
926
- extractTypesFromSchema(allTypes, prop, output);
927
- }
928
- }
929
- }
930
- else if (core.Is.arrayValue(schema.oneOf)) {
931
- for (const prop of schema.oneOf) {
932
- if (core.Is.object(prop)) {
933
- extractTypesFromSchema(allTypes, prop, output);
934
- }
935
- }
936
- }
937
- if (additionalTypes.length > 0) {
938
- extractTypes(allTypes, additionalTypes, output);
939
- }
940
- }
941
887
  /**
942
888
  * Tidy up the schemas for use in OpenAPI context.
943
889
  * @param props The properties to tidy up.
@@ -1107,7 +1053,7 @@ class CLI extends cliCore.CLIBase {
1107
1053
  return this.execute({
1108
1054
  title: "TWIN TypeScript To OpenAPI",
1109
1055
  appName: "ts-to-openapi",
1110
- version: "0.0.1-next.9",
1056
+ version: "0.0.2-next.1", // x-release-please-version
1111
1057
  icon: "⚙️ ",
1112
1058
  supportsEnvFiles: false,
1113
1059
  overrideOutputWidth: options?.overrideOutputWidth
@@ -3,6 +3,7 @@ import { fileURLToPath } from 'node:url';
3
3
  import { CLIDisplay, CLIUtils, CLIBase } from '@twin.org/cli-core';
4
4
  import { mkdir, rm, writeFile } from 'node:fs/promises';
5
5
  import { I18n, GeneralError, Is, StringHelper, ObjectHelper } from '@twin.org/core';
6
+ import { JsonSchemaHelper } from '@twin.org/tools-core';
6
7
  import { HttpStatusCode, MimeTypes } from '@twin.org/web';
7
8
  import { createGenerator } from 'ts-json-schema-generator';
8
9
 
@@ -236,7 +237,20 @@ async function tsToOpenApi(config, outputFile, workingDirectory) {
236
237
  }
237
238
  }
238
239
  CLIDisplay.task(I18n.formatMessage("commands.ts-to-openapi.progress.generatingSchemas"));
239
- const schemas = await generateSchemas(typeRoots, types, workingDirectory);
240
+ const autoExpandTypes = config.autoExpandTypes ?? [];
241
+ const defaultExpandTypes = ["ObjectOrArray<.*>"];
242
+ for (const defaultType of defaultExpandTypes) {
243
+ if (!autoExpandTypes.includes(defaultType)) {
244
+ autoExpandTypes.push(defaultType);
245
+ }
246
+ }
247
+ const schemas = await generateSchemas(typeRoots, types, autoExpandTypes, workingDirectory);
248
+ for (const type in schemas) {
249
+ if (Is.object(config.overrides?.[type])) {
250
+ CLIDisplay.task(I18n.formatMessage("commands.ts-to-openapi.progress.overridingSchema"));
251
+ schemas[type] = config.overrides?.[type];
252
+ }
253
+ }
240
254
  const usedCommonResponseTypes = [];
241
255
  for (let i = 0; i < inputResults.length; i++) {
242
256
  const result = inputResults[i];
@@ -656,8 +670,13 @@ async function finaliseOutput(usedCommonResponseTypes, schemas, openApi, securit
656
670
  }
657
671
  }
658
672
  }
659
- if (finalSchemas.HttpStatusCode) {
660
- delete finalSchemas.HttpStatusCode;
673
+ // Remove standard types that we don't want in the final output
674
+ const removeTypes = ["HttpStatusCode", "Uint8Array", "ArrayBuffer"];
675
+ for (const type of removeTypes) {
676
+ delete finalSchemas[type];
677
+ }
678
+ for (const type in finalSchemas) {
679
+ JsonSchemaHelper.processArrays(finalSchemas[type]);
661
680
  }
662
681
  const schemaKeys = Object.keys(finalSchemas);
663
682
  schemaKeys.sort();
@@ -669,7 +688,7 @@ async function finaliseOutput(usedCommonResponseTypes, schemas, openApi, securit
669
688
  schemas: sortedSchemas,
670
689
  securitySchemes
671
690
  };
672
- let json = JSON.stringify(openApi, undefined, " ");
691
+ let json = JSON.stringify(openApi, undefined, "\t");
673
692
  // Remove the reference only schemas, repeating until no more substitutions
674
693
  let performedSubstitution;
675
694
  do {
@@ -690,10 +709,7 @@ async function finaliseOutput(usedCommonResponseTypes, schemas, openApi, securit
690
709
  // Remove the array [] from the type names
691
710
  // eslint-disable-next-line unicorn/better-regex
692
711
  json = json.replace(/#\/components\/schemas\/(.*)\[\]/g, "#/components/schemas/ListOf$1");
693
- // Remove the partial markers
694
- json = json.replace(/Partial%3CI(.*?)%3E/g, "$1");
695
- // Cleanup the generic markers
696
- json = json.replace(/%3Cunknown%3E/g, "");
712
+ json = JsonSchemaHelper.normaliseTypeName(json);
697
713
  // Remove external references
698
714
  for (const finalExternal in finalExternals) {
699
715
  json = json.replace(new RegExp(`"#/components/schemas/${StringHelper.stripPrefix(finalExternal)}"`, "g"), `"${finalExternals[finalExternal]}"`);
@@ -703,7 +719,7 @@ async function finaliseOutput(usedCommonResponseTypes, schemas, openApi, securit
703
719
  await mkdir(path.dirname(outputFile), { recursive: true });
704
720
  }
705
721
  catch { }
706
- await writeFile(outputFile, json);
722
+ await writeFile(outputFile, `${json}\n`);
707
723
  }
708
724
  /**
709
725
  * Build the security schemas from the config.
@@ -823,7 +839,7 @@ async function processPackageRestDetails(restRoutes) {
823
839
  * @returns Nothing.
824
840
  * @internal
825
841
  */
826
- async function generateSchemas(modelDirWildcards, types, outputWorkingDir) {
842
+ async function generateSchemas(modelDirWildcards, types, autoExpandTypes, outputWorkingDir) {
827
843
  const allSchemas = {};
828
844
  const arraySingularTypes = [];
829
845
  for (const type of types) {
@@ -847,16 +863,14 @@ async function generateSchemas(modelDirWildcards, types, outputWorkingDir) {
847
863
  const schema = generator.createSchema("*");
848
864
  if (schema.definitions) {
849
865
  for (const def in schema.definitions) {
850
- // Remove the partial markers
851
- let defSub = def.replace(/^Partial<(.*?)>/g, "$1");
852
- // Cleanup the generic markers
853
- defSub = defSub.replace(/</g, "%3C").replace(/>/g, "%3E");
866
+ const defSub = JsonSchemaHelper.normaliseTypeName(def);
854
867
  allSchemas[defSub] = schema.definitions[def];
855
868
  }
856
869
  }
857
870
  }
858
871
  const referencedSchemas = {};
859
- extractTypes(allSchemas, types, referencedSchemas);
872
+ JsonSchemaHelper.extractTypes(allSchemas, [...types, ...autoExpandTypes], referencedSchemas);
873
+ JsonSchemaHelper.expandTypes(referencedSchemas, autoExpandTypes);
860
874
  for (const arraySingularType of arraySingularTypes) {
861
875
  referencedSchemas[`${arraySingularType}[]`] = {
862
876
  type: "array",
@@ -867,74 +881,6 @@ async function generateSchemas(modelDirWildcards, types, outputWorkingDir) {
867
881
  }
868
882
  return referencedSchemas;
869
883
  }
870
- /**
871
- * Extract the required types from all the known schemas.
872
- * @param allSchemas All the known schemas.
873
- * @param requiredTypes The required types.
874
- * @param referencedSchemas The references schemas.
875
- * @internal
876
- */
877
- function extractTypes(allSchemas, requiredTypes, referencedSchemas) {
878
- for (const type of requiredTypes) {
879
- if (allSchemas[type] && !referencedSchemas[type]) {
880
- referencedSchemas[type] = allSchemas[type];
881
- extractTypesFromSchema(allSchemas, allSchemas[type], referencedSchemas);
882
- }
883
- }
884
- }
885
- /**
886
- * Extract type from properties definition.
887
- * @param allTypes All the known types.
888
- * @param schema The schema to extract from.
889
- * @param output The output types.
890
- * @internal
891
- */
892
- function extractTypesFromSchema(allTypes, schema, output) {
893
- const additionalTypes = [];
894
- if (Is.stringValue(schema.$ref)) {
895
- additionalTypes.push(schema.$ref.replace("#/definitions/", "").replace(/^Partial%3C(.*?)%3E/g, "$1"));
896
- }
897
- else if (Is.object(schema.items)) {
898
- if (Is.arrayValue(schema.items)) {
899
- for (const itemSchema of schema.items) {
900
- extractTypesFromSchema(allTypes, itemSchema, output);
901
- }
902
- }
903
- else {
904
- extractTypesFromSchema(allTypes, schema.items, output);
905
- }
906
- }
907
- else if (Is.object(schema.properties) || Is.object(schema.additionalProperties)) {
908
- if (Is.object(schema.properties)) {
909
- for (const prop in schema.properties) {
910
- const p = schema.properties[prop];
911
- if (Is.object(p)) {
912
- extractTypesFromSchema(allTypes, p, output);
913
- }
914
- }
915
- }
916
- if (Is.object(schema.additionalProperties)) {
917
- extractTypesFromSchema(allTypes, schema.additionalProperties, output);
918
- }
919
- }
920
- else if (Is.arrayValue(schema.anyOf)) {
921
- for (const prop of schema.anyOf) {
922
- if (Is.object(prop)) {
923
- extractTypesFromSchema(allTypes, prop, output);
924
- }
925
- }
926
- }
927
- else if (Is.arrayValue(schema.oneOf)) {
928
- for (const prop of schema.oneOf) {
929
- if (Is.object(prop)) {
930
- extractTypesFromSchema(allTypes, prop, output);
931
- }
932
- }
933
- }
934
- if (additionalTypes.length > 0) {
935
- extractTypes(allTypes, additionalTypes, output);
936
- }
937
- }
938
884
  /**
939
885
  * Tidy up the schemas for use in OpenAPI context.
940
886
  * @param props The properties to tidy up.
@@ -1104,7 +1050,7 @@ class CLI extends CLIBase {
1104
1050
  return this.execute({
1105
1051
  title: "TWIN TypeScript To OpenAPI",
1106
1052
  appName: "ts-to-openapi",
1107
- version: "0.0.1-next.9",
1053
+ version: "0.0.2-next.1", // x-release-please-version
1108
1054
  icon: "⚙️ ",
1109
1055
  supportsEnvFiles: false,
1110
1056
  overrideOutputWidth: options?.overrideOutputWidth