@twin.org/ts-to-schema 0.0.1-next.24 → 0.0.1-next.26

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.
@@ -10,6 +10,7 @@ var tsJsonSchemaGenerator = require('ts-json-schema-generator');
10
10
  var _documentCurrentScript = typeof document !== 'undefined' ? document.currentScript : null;
11
11
  // Copyright 2024 IOTA Stiftung.
12
12
  // SPDX-License-Identifier: Apache-2.0.
13
+ const SCHEMA_VERSION = "https://json-schema.org/draft/2020-12/schema";
13
14
  /**
14
15
  * Build the root command to be consumed by the CLI.
15
16
  * @param program The command to build on.
@@ -79,14 +80,23 @@ async function tsToSchema(config, outputFolder, workingDirectory) {
79
80
  cliCore.CLIDisplay.break();
80
81
  cliCore.CLIDisplay.task(core.I18n.formatMessage("commands.ts-to-schema.progress.writingSchemas"));
81
82
  for (const typeSource of config.types) {
82
- cliCore.CLIDisplay.task(core.I18n.formatMessage("commands.ts-to-schema.progress.generatingSchema"));
83
83
  const typeSourceParts = typeSource.split("/");
84
84
  const type = core.StringHelper.pascalCase(typeSourceParts[typeSourceParts.length - 1].replace(/(\.d)?\.ts$/, ""), false);
85
- const schemas = await generateSchemas(typeSource, type, workingDirectory);
86
- if (core.Is.empty(schemas[type])) {
87
- throw new core.GeneralError("commands", "commands.ts-to-schema.schemaNotFound", { type });
85
+ let schemaObject;
86
+ if (core.Is.object(config.overrides?.[type])) {
87
+ cliCore.CLIDisplay.task(core.I18n.formatMessage("commands.ts-to-schema.progress.overridingSchema"));
88
+ schemaObject = config.overrides?.[type];
88
89
  }
89
- let content = JSON.stringify(schemas[type], undefined, "\t");
90
+ else {
91
+ cliCore.CLIDisplay.task(core.I18n.formatMessage("commands.ts-to-schema.progress.generatingSchema"));
92
+ const schemas = await generateSchemas(typeSource, type, workingDirectory);
93
+ if (core.Is.empty(schemas[type])) {
94
+ throw new core.GeneralError("commands", "commands.ts-to-schema.schemaNotFound", { type });
95
+ }
96
+ schemaObject = schemas[type];
97
+ }
98
+ schemaObject = finaliseSchema(schemaObject, config.baseUrl, type);
99
+ let content = JSON.stringify(schemaObject, undefined, "\t");
90
100
  if (core.Is.objectValue(config.externalReferences)) {
91
101
  for (const external in config.externalReferences) {
92
102
  content = content.replace(new RegExp(`#/definitions/${external}`, "g"), config.externalReferences[external]);
@@ -201,6 +211,76 @@ function extractTypesFromSchema(allTypes, schema, output) {
201
211
  extractTypes(allTypes, additionalTypes, output);
202
212
  }
203
213
  }
214
+ /**
215
+ * Process the schema object to ensure it has the correct properties.
216
+ * @param schemaObject The schema object to process.
217
+ * @param baseUrl The base URL for the schema references.
218
+ * @param type The type of the schema object.
219
+ * @returns The finalised schema object.
220
+ */
221
+ function finaliseSchema(schemaObject, baseUrl, type) {
222
+ processArrays(schemaObject);
223
+ const { description, ...rest } = schemaObject;
224
+ return {
225
+ $schema: SCHEMA_VERSION,
226
+ $id: `${baseUrl}${core.StringHelper.stripPrefix(type)}`,
227
+ description,
228
+ ...rest
229
+ };
230
+ }
231
+ /**
232
+ * Process arrays in the schema object.
233
+ * @param schemaObject The schema object to process.
234
+ */
235
+ function processArrays(schemaObject) {
236
+ if (core.Is.object(schemaObject)) {
237
+ // latest specs have singular items in `items` property
238
+ // and multiple items in prefixItems, so update the schema accordingly
239
+ // https://www.learnjsonschema.com/2020-12/applicator/items/
240
+ // https://www.learnjsonschema.com/2020-12/applicator/prefixitems/
241
+ const schemaItems = schemaObject.items;
242
+ if (core.Is.array(schemaItems) || core.Is.object(schemaItems)) {
243
+ schemaObject.prefixItems = core.ArrayHelper.fromObjectOrArray(schemaItems);
244
+ schemaObject.items = false;
245
+ }
246
+ const additionalItems = schemaObject.additionalItems;
247
+ if (core.Is.array(additionalItems) || core.Is.object(additionalItems)) {
248
+ schemaObject.items = core.ArrayHelper.fromObjectOrArray(additionalItems)[0];
249
+ delete schemaObject.additionalItems;
250
+ }
251
+ processSchemaDictionary(schemaObject.properties);
252
+ processArrays(schemaObject.additionalProperties);
253
+ processSchemaArray(schemaObject.allOf);
254
+ processSchemaArray(schemaObject.anyOf);
255
+ processSchemaArray(schemaObject.oneOf);
256
+ }
257
+ }
258
+ /**
259
+ * Process arrays in the schema object.
260
+ * @param schemaDictionary The schema object to process.
261
+ */
262
+ function processSchemaDictionary(schemaDictionary) {
263
+ if (core.Is.object(schemaDictionary)) {
264
+ for (const item of Object.values(schemaDictionary)) {
265
+ if (core.Is.object(item)) {
266
+ processArrays(item);
267
+ }
268
+ }
269
+ }
270
+ }
271
+ /**
272
+ * Process arrays in the schema object.
273
+ * @param schemaArray The schema object to process.
274
+ */
275
+ function processSchemaArray(schemaArray) {
276
+ if (core.Is.arrayValue(schemaArray)) {
277
+ for (const item of schemaArray) {
278
+ if (core.Is.object(item)) {
279
+ processArrays(item);
280
+ }
281
+ }
282
+ }
283
+ }
204
284
 
205
285
  // Copyright 2024 IOTA Stiftung.
206
286
  // SPDX-License-Identifier: Apache-2.0.
@@ -220,7 +300,7 @@ class CLI extends cliCore.CLIBase {
220
300
  return this.execute({
221
301
  title: "TWIN TypeScript To Schema",
222
302
  appName: "ts-to-schema",
223
- version: "0.0.1-next.24", // x-release-please-version
303
+ version: "0.0.1-next.26", // x-release-please-version
224
304
  icon: "⚙️ ",
225
305
  supportsEnvFiles: false,
226
306
  overrideOutputWidth: options?.overrideOutputWidth
@@ -2,11 +2,12 @@ import path from 'node:path';
2
2
  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
- import { I18n, GeneralError, Is, StringHelper } from '@twin.org/core';
5
+ import { I18n, GeneralError, Is, StringHelper, ArrayHelper } from '@twin.org/core';
6
6
  import { createGenerator } from 'ts-json-schema-generator';
7
7
 
8
8
  // Copyright 2024 IOTA Stiftung.
9
9
  // SPDX-License-Identifier: Apache-2.0.
10
+ const SCHEMA_VERSION = "https://json-schema.org/draft/2020-12/schema";
10
11
  /**
11
12
  * Build the root command to be consumed by the CLI.
12
13
  * @param program The command to build on.
@@ -76,14 +77,23 @@ async function tsToSchema(config, outputFolder, workingDirectory) {
76
77
  CLIDisplay.break();
77
78
  CLIDisplay.task(I18n.formatMessage("commands.ts-to-schema.progress.writingSchemas"));
78
79
  for (const typeSource of config.types) {
79
- CLIDisplay.task(I18n.formatMessage("commands.ts-to-schema.progress.generatingSchema"));
80
80
  const typeSourceParts = typeSource.split("/");
81
81
  const type = StringHelper.pascalCase(typeSourceParts[typeSourceParts.length - 1].replace(/(\.d)?\.ts$/, ""), false);
82
- const schemas = await generateSchemas(typeSource, type, workingDirectory);
83
- if (Is.empty(schemas[type])) {
84
- throw new GeneralError("commands", "commands.ts-to-schema.schemaNotFound", { type });
82
+ let schemaObject;
83
+ if (Is.object(config.overrides?.[type])) {
84
+ CLIDisplay.task(I18n.formatMessage("commands.ts-to-schema.progress.overridingSchema"));
85
+ schemaObject = config.overrides?.[type];
85
86
  }
86
- let content = JSON.stringify(schemas[type], undefined, "\t");
87
+ else {
88
+ CLIDisplay.task(I18n.formatMessage("commands.ts-to-schema.progress.generatingSchema"));
89
+ const schemas = await generateSchemas(typeSource, type, workingDirectory);
90
+ if (Is.empty(schemas[type])) {
91
+ throw new GeneralError("commands", "commands.ts-to-schema.schemaNotFound", { type });
92
+ }
93
+ schemaObject = schemas[type];
94
+ }
95
+ schemaObject = finaliseSchema(schemaObject, config.baseUrl, type);
96
+ let content = JSON.stringify(schemaObject, undefined, "\t");
87
97
  if (Is.objectValue(config.externalReferences)) {
88
98
  for (const external in config.externalReferences) {
89
99
  content = content.replace(new RegExp(`#/definitions/${external}`, "g"), config.externalReferences[external]);
@@ -198,6 +208,76 @@ function extractTypesFromSchema(allTypes, schema, output) {
198
208
  extractTypes(allTypes, additionalTypes, output);
199
209
  }
200
210
  }
211
+ /**
212
+ * Process the schema object to ensure it has the correct properties.
213
+ * @param schemaObject The schema object to process.
214
+ * @param baseUrl The base URL for the schema references.
215
+ * @param type The type of the schema object.
216
+ * @returns The finalised schema object.
217
+ */
218
+ function finaliseSchema(schemaObject, baseUrl, type) {
219
+ processArrays(schemaObject);
220
+ const { description, ...rest } = schemaObject;
221
+ return {
222
+ $schema: SCHEMA_VERSION,
223
+ $id: `${baseUrl}${StringHelper.stripPrefix(type)}`,
224
+ description,
225
+ ...rest
226
+ };
227
+ }
228
+ /**
229
+ * Process arrays in the schema object.
230
+ * @param schemaObject The schema object to process.
231
+ */
232
+ function processArrays(schemaObject) {
233
+ if (Is.object(schemaObject)) {
234
+ // latest specs have singular items in `items` property
235
+ // and multiple items in prefixItems, so update the schema accordingly
236
+ // https://www.learnjsonschema.com/2020-12/applicator/items/
237
+ // https://www.learnjsonschema.com/2020-12/applicator/prefixitems/
238
+ const schemaItems = schemaObject.items;
239
+ if (Is.array(schemaItems) || Is.object(schemaItems)) {
240
+ schemaObject.prefixItems = ArrayHelper.fromObjectOrArray(schemaItems);
241
+ schemaObject.items = false;
242
+ }
243
+ const additionalItems = schemaObject.additionalItems;
244
+ if (Is.array(additionalItems) || Is.object(additionalItems)) {
245
+ schemaObject.items = ArrayHelper.fromObjectOrArray(additionalItems)[0];
246
+ delete schemaObject.additionalItems;
247
+ }
248
+ processSchemaDictionary(schemaObject.properties);
249
+ processArrays(schemaObject.additionalProperties);
250
+ processSchemaArray(schemaObject.allOf);
251
+ processSchemaArray(schemaObject.anyOf);
252
+ processSchemaArray(schemaObject.oneOf);
253
+ }
254
+ }
255
+ /**
256
+ * Process arrays in the schema object.
257
+ * @param schemaDictionary The schema object to process.
258
+ */
259
+ function processSchemaDictionary(schemaDictionary) {
260
+ if (Is.object(schemaDictionary)) {
261
+ for (const item of Object.values(schemaDictionary)) {
262
+ if (Is.object(item)) {
263
+ processArrays(item);
264
+ }
265
+ }
266
+ }
267
+ }
268
+ /**
269
+ * Process arrays in the schema object.
270
+ * @param schemaArray The schema object to process.
271
+ */
272
+ function processSchemaArray(schemaArray) {
273
+ if (Is.arrayValue(schemaArray)) {
274
+ for (const item of schemaArray) {
275
+ if (Is.object(item)) {
276
+ processArrays(item);
277
+ }
278
+ }
279
+ }
280
+ }
201
281
 
202
282
  // Copyright 2024 IOTA Stiftung.
203
283
  // SPDX-License-Identifier: Apache-2.0.
@@ -217,7 +297,7 @@ class CLI extends CLIBase {
217
297
  return this.execute({
218
298
  title: "TWIN TypeScript To Schema",
219
299
  appName: "ts-to-schema",
220
- version: "0.0.1-next.24", // x-release-please-version
300
+ version: "0.0.1-next.26", // x-release-please-version
221
301
  icon: "⚙️ ",
222
302
  supportsEnvFiles: false,
223
303
  overrideOutputWidth: options?.overrideOutputWidth
@@ -80,6 +80,8 @@
80
80
  "array": "Property \"{property}\" must be an array, it is \"{value}\"",
81
81
  "arrayValue": "Property \"{property}\" must be an array with at least one item",
82
82
  "arrayOneOf": "Property \"{property}\" must be one of [{options}], it is \"{value}\"",
83
+ "arrayStartsWith": "Property \"{property}\" must be an array starting with [{startValues}], it is \"{value}\"",
84
+ "arrayEndsWith": "Property \"{property}\" must be an array ending with [{endValues}], it is \"{value}\"",
83
85
  "uint8Array": "Property \"{property}\" must be a Uint8Array, it is \"{value}\"",
84
86
  "function": "Property \"{property}\" must be a function, it is \"{value}\"",
85
87
  "urn": "Property \"{property}\" must be a Urn formatted string, it is \"{value}\"",
@@ -250,6 +252,7 @@
250
252
  "loadingConfigJson": "Loading Config JSON",
251
253
  "creatingWorkingDir": "Creating Working Directory",
252
254
  "generatingSchema": "Generating Schema",
255
+ "overridingSchema": "Overriding Schema",
253
256
  "writingSchemas": "Writing Schemas",
254
257
  "writingSchema": "Writing Schema",
255
258
  "models": "Models"
@@ -0,0 +1,5 @@
1
+ import type { AnySchemaObject } from "ajv/dist/2020.js";
2
+ /**
3
+ * Default schema type.
4
+ */
5
+ export type IJsonSchema = AnySchemaObject;
@@ -1,3 +1,4 @@
1
+ import type { IJsonSchema } from "./IJsonSchema";
1
2
  /**
2
3
  * Configuration for the tool.
3
4
  */
@@ -16,4 +17,10 @@ export interface ITsToSchemaConfig {
16
17
  externalReferences?: {
17
18
  [id: string]: string;
18
19
  };
20
+ /**
21
+ * Override for specific types, to be used when the type cannot be generated automatically, or is generated incorrectly.
22
+ */
23
+ overrides?: {
24
+ [id: string]: IJsonSchema;
25
+ };
19
26
  }
package/docs/changelog.md CHANGED
@@ -1,5 +1,41 @@
1
1
  # @twin.org/ts-to-schema - Changelog
2
2
 
3
+ ## [0.0.1-next.26](https://github.com/twinfoundation/tools/compare/ts-to-schema-v0.0.1-next.25...ts-to-schema-v0.0.1-next.26) (2025-06-11)
4
+
5
+
6
+ ### Features
7
+
8
+ * use most recent JSON schema specs ([4598cbf](https://github.com/twinfoundation/tools/commit/4598cbf29f7b82dba4a9f3b19f81dfe66f5a6060))
9
+
10
+
11
+ ### Dependencies
12
+
13
+ * The following workspace dependencies were updated
14
+ * dependencies
15
+ * @twin.org/nameof bumped from 0.0.1-next.25 to 0.0.1-next.26
16
+ * devDependencies
17
+ * @twin.org/merge-locales bumped from 0.0.1-next.25 to 0.0.1-next.26
18
+ * @twin.org/nameof-transformer bumped from 0.0.1-next.25 to 0.0.1-next.26
19
+ * @twin.org/nameof-vitest-plugin bumped from 0.0.1-next.25 to 0.0.1-next.26
20
+
21
+ ## [0.0.1-next.25](https://github.com/twinfoundation/tools/compare/ts-to-schema-v0.0.1-next.24...ts-to-schema-v0.0.1-next.25) (2025-06-10)
22
+
23
+
24
+ ### Features
25
+
26
+ * add ts-to-schema overrides ([3c54504](https://github.com/twinfoundation/tools/commit/3c5450468eb998204a75576b7791a7ca4027da62))
27
+
28
+
29
+ ### Dependencies
30
+
31
+ * The following workspace dependencies were updated
32
+ * dependencies
33
+ * @twin.org/nameof bumped from 0.0.1-next.24 to 0.0.1-next.25
34
+ * devDependencies
35
+ * @twin.org/merge-locales bumped from 0.0.1-next.24 to 0.0.1-next.25
36
+ * @twin.org/nameof-transformer bumped from 0.0.1-next.24 to 0.0.1-next.25
37
+ * @twin.org/nameof-vitest-plugin bumped from 0.0.1-next.24 to 0.0.1-next.25
38
+
3
39
  ## [0.0.1-next.24](https://github.com/twinfoundation/tools/compare/ts-to-schema-v0.0.1-next.23...ts-to-schema-v0.0.1-next.24) (2025-06-05)
4
40
 
5
41
 
@@ -29,3 +29,15 @@ External type references
29
29
  #### Index Signature
30
30
 
31
31
  \[`id`: `string`\]: `string`
32
+
33
+ ***
34
+
35
+ ### overrides?
36
+
37
+ > `optional` **overrides**: `object`
38
+
39
+ Override for specific types, to be used when the type cannot be generated automatically, or is generated incorrectly.
40
+
41
+ #### Index Signature
42
+
43
+ \[`id`: `string`\]: `AnySchemaObject`
package/locales/en.json CHANGED
@@ -23,6 +23,7 @@
23
23
  "loadingConfigJson": "Loading Config JSON",
24
24
  "creatingWorkingDir": "Creating Working Directory",
25
25
  "generatingSchema": "Generating Schema",
26
+ "overridingSchema": "Overriding Schema",
26
27
  "writingSchemas": "Writing Schemas",
27
28
  "writingSchema": "Writing Schema",
28
29
  "models": "Models"
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@twin.org/ts-to-schema",
3
- "version": "0.0.1-next.24",
3
+ "version": "0.0.1-next.26",
4
4
  "description": "Tool to convert TypeScript definitions to JSON schemas",
5
5
  "repository": {
6
6
  "type": "git",
@@ -16,10 +16,10 @@
16
16
  "dependencies": {
17
17
  "@twin.org/cli-core": "next",
18
18
  "@twin.org/core": "next",
19
- "@twin.org/nameof": "0.0.1-next.24",
20
- "commander": "13.1.0",
21
- "glob": "11.0.1",
22
- "jsonschema": "1.5.0",
19
+ "@twin.org/nameof": "0.0.1-next.26",
20
+ "ajv": "8.17.1",
21
+ "commander": "14.0.0",
22
+ "glob": "11.0.2",
23
23
  "ts-json-schema-generator": "2.4.0"
24
24
  },
25
25
  "main": "./dist/cjs/index.cjs",