ajsc 1.0.11 → 3.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.
Files changed (38) hide show
  1. package/dist/JSONSchemaConverter.d.ts +4 -12
  2. package/dist/JSONSchemaConverter.js +146 -35
  3. package/dist/JSONSchemaConverter.js.map +1 -1
  4. package/dist/JSONSchemaConverter.test.js +195 -0
  5. package/dist/JSONSchemaConverter.test.js.map +1 -1
  6. package/dist/Typebox.test.js +28 -2
  7. package/dist/Typebox.test.js.map +1 -1
  8. package/dist/TypescriptBaseConverter.d.ts +29 -38
  9. package/dist/TypescriptBaseConverter.js +186 -88
  10. package/dist/TypescriptBaseConverter.js.map +1 -1
  11. package/dist/TypescriptConverter.d.ts +3 -6
  12. package/dist/TypescriptConverter.js +14 -88
  13. package/dist/TypescriptConverter.js.map +1 -1
  14. package/dist/TypescriptConverter.test.js +114 -0
  15. package/dist/TypescriptConverter.test.js.map +1 -1
  16. package/dist/TypescriptProcedureConverter.d.ts +6 -7
  17. package/dist/TypescriptProcedureConverter.js +19 -94
  18. package/dist/TypescriptProcedureConverter.js.map +1 -1
  19. package/dist/{TypescriptProceduresConverter.test.js → TypescriptProcedureConverter.test.js} +1 -2
  20. package/dist/TypescriptProcedureConverter.test.js.map +1 -0
  21. package/dist/types.d.ts +51 -6
  22. package/dist/utils/path-utils.test.js.map +1 -1
  23. package/package.json +6 -6
  24. package/dist/TypescriptProceduresConverter.test.js.map +0 -1
  25. package/src/JSONSchemaConverter.test.ts +0 -342
  26. package/src/JSONSchemaConverter.ts +0 -459
  27. package/src/Typebox.test.ts +0 -93
  28. package/src/TypescriptBaseConverter.ts +0 -161
  29. package/src/TypescriptConverter.test.ts +0 -275
  30. package/src/TypescriptConverter.ts +0 -161
  31. package/src/TypescriptProcedureConverter.ts +0 -180
  32. package/src/TypescriptProceduresConverter.test.ts +0 -1013
  33. package/src/index.ts +0 -4
  34. package/src/types.ts +0 -101
  35. package/src/utils/path-utils.test.ts +0 -102
  36. package/src/utils/path-utils.ts +0 -89
  37. package/src/utils/to-pascal-case.ts +0 -10
  38. /package/dist/{TypescriptProceduresConverter.test.d.ts → TypescriptProcedureConverter.test.d.ts} +0 -0
@@ -1,275 +0,0 @@
1
- import { describe, it, expect } from "vitest";
2
- import { TypescriptConverter } from "./TypescriptConverter.js";
3
- import { JSONSchemaConverter } from "./JSONSchemaConverter.js";
4
- import { Type } from "@sinclair/typebox";
5
-
6
- describe("TypeScriptLanguagePlugin", () => {
7
- it("should convert strings", () => {
8
- expect(new TypescriptConverter({ type: "string" }).code).toMatch("string");
9
- });
10
-
11
- it("should convert numbers", () => {
12
- expect(new TypescriptConverter({ type: "number" }).code).toMatch("number");
13
- });
14
-
15
- it("should convert booleans", () => {
16
- expect(new TypescriptConverter({ type: "boolean" }).code).toMatch(
17
- "boolean",
18
- );
19
- });
20
-
21
- it("should convert null", () => {
22
- expect(new TypescriptConverter({ type: "null" }).code).toMatch("null");
23
- });
24
-
25
- it("should convert literals", () => {
26
- expect(new TypescriptConverter({ const: "fixedValue" }).code).toMatch(
27
- "fixedValue",
28
- );
29
- });
30
-
31
- it("should convert enums", () => {
32
- expect(
33
- new TypescriptConverter({
34
- type: "string",
35
- enum: ["value1", "value2", "value3"],
36
- }).code,
37
- ).toMatch(`"value1" | "value2" | "value3"`);
38
- });
39
-
40
- it("should convert unions", () => {
41
- expect(
42
- new TypescriptConverter({ type: ["string", "number"] }).code,
43
- ).toMatch(`string | number`);
44
- });
45
-
46
- it("should convert intersections", () => {
47
- expect(
48
- new TypescriptConverter({
49
- allOf: [{ type: "string" }, { type: "number" }],
50
- }).code,
51
- ).toMatch(`string & number`);
52
- });
53
-
54
- it("should convert arrays", () => {
55
- expect(
56
- new TypescriptConverter(
57
- {
58
- type: "array",
59
- items: {
60
- type: "object",
61
- properties: { name: { type: "string" } },
62
- required: ["name"],
63
- },
64
- },
65
- {
66
- inlineTypes: true,
67
- },
68
- ).code.replace(/\s/g, ""),
69
- ).toEqual("Array<{name:string;}>".replace(/\s/g, ""));
70
- });
71
-
72
- it("should convert objects", () => {
73
- expect(
74
- new TypescriptConverter({
75
- type: "object",
76
- properties: {
77
- title: { type: "string" },
78
- year: { type: "number" },
79
- },
80
- required: ["title"],
81
- }).code,
82
- ).toMatch(`{ title: string; year?: number; }`);
83
- });
84
-
85
- it("should convert an array of objects", () => {
86
- expect(
87
- new TypescriptConverter(
88
- {
89
- type: "array",
90
- items: {
91
- anyOf: [
92
- {
93
- type: "object",
94
- properties: {
95
- year: { type: "number" },
96
- },
97
- required: ["year"],
98
- },
99
- {
100
- type: "object",
101
- properties: {
102
- title: { type: "string" },
103
- },
104
- },
105
- ],
106
- },
107
- },
108
- {
109
- inlineTypes: true,
110
- },
111
- ).code,
112
- ).toMatch(`Array<{ year: number; } | { title?: string; }>`);
113
- });
114
-
115
- it("should convert a simple JSON schema to Typescript", () => {
116
- expect(
117
- new TypescriptConverter(
118
- {
119
- type: "object",
120
- properties: {
121
- name: { type: "string" },
122
- age: { type: "number" },
123
- contacts: {
124
- type: "array",
125
- items: {
126
- type: "object",
127
- properties: {
128
- email: { type: "string" },
129
- },
130
- required: ["email"],
131
- },
132
- },
133
- profile: {
134
- type: "object",
135
- properties: {
136
- email: { type: "string" },
137
- },
138
- required: ["email"],
139
- },
140
- },
141
- required: ["name", "age"],
142
- },
143
- {
144
- inlineTypes: true,
145
- },
146
- ).code.replace(/\s/g, ""),
147
- ).toMatch(
148
- `{
149
- name: string;
150
- age: number;
151
- contacts?: Array<{ email: string; }>;
152
- profile?: { email: string; };
153
- }`.replace(/\s/g, ""),
154
- );
155
- });
156
-
157
- it("should convert a named JSON schema top-level object to Typescript", () => {
158
- expect(
159
- new TypescriptConverter(
160
- {
161
- $schema: "http://json-schema.org/draft-07/schema#",
162
- title: "Person",
163
- type: "object",
164
- properties: {
165
- name: { type: "string" },
166
- age: { type: "number" },
167
- contacts: {
168
- type: "array",
169
- items: {
170
- type: "object",
171
- properties: {
172
- email: { type: "string" },
173
- },
174
- required: ["email"],
175
- },
176
- },
177
- profile: {
178
- type: "object",
179
- properties: {
180
- email: { type: "string" },
181
- },
182
- required: ["email"],
183
- },
184
- },
185
- required: ["name", "age"],
186
- },
187
- {
188
- inlineTypes: true,
189
- },
190
- ).code.replace(/\s/g, ""),
191
- ).toEqual(
192
- `{ name: string; age: number; contacts?: Array<{ email: string; }>; profile?: { email: string; }; }`.replace(
193
- /\s/g,
194
- "",
195
- ),
196
- );
197
- });
198
-
199
- it("should convert a named JSON schema top-level array to Typescript", () => {
200
- expect(
201
- new TypescriptConverter(
202
- {
203
- $schema: "http://json-schema.org/draft-07/schema#",
204
- title: "People",
205
- type: "array",
206
- items: {
207
- type: "object",
208
- properties: {
209
- name: { type: "string" },
210
- age: { type: "number" },
211
- },
212
- required: ["name", "age"],
213
- },
214
- },
215
- {
216
- inlineTypes: true,
217
- },
218
- ).code.replace(/\s/g, ""),
219
- ).toEqual(`Array<{ name: string; age: number; }>`.replace(/\s/g, ""));
220
- });
221
-
222
- it("should convert JSON schema with top-level re-used objects in Typescript", async () => {
223
- expect(
224
- new TypescriptConverter(
225
- {
226
- $schema: "http://json-schema.org/draft-07/schema#",
227
- title: "Person",
228
- type: "object",
229
- properties: {
230
- contacts: {
231
- type: "array",
232
- items: {
233
- type: "object",
234
- properties: {
235
- email: { type: "string" },
236
- },
237
- required: ["email"],
238
- },
239
- },
240
- profile: {
241
- type: "object",
242
- properties: {
243
- email: { type: "string" },
244
- },
245
- required: ["email"],
246
- },
247
- contact: {
248
- type: "object",
249
- properties: {
250
- email: { type: "string" },
251
- },
252
- required: ["email"],
253
- },
254
- email: {
255
- type: "object",
256
- properties: {
257
- email: { type: "string" },
258
- },
259
- required: ["email"],
260
- },
261
- },
262
- required: ["name", "age"],
263
- },
264
- {
265
- inlineTypes: true,
266
- },
267
- ).code.replace(/\s/g, ""),
268
- ).toEqual(
269
- `{contacts?:Array<{email:string;}>;profile?:{email:string;};contact?:{email:string;};email?:{email:string;};}`.replace(
270
- /\s/g,
271
- "",
272
- ),
273
- );
274
- });
275
- });
@@ -1,161 +0,0 @@
1
- import { IRNode, LanguagePlugin } from "./types.js";
2
- import {
3
- RefTypeName,
4
- RefTypes,
5
- TypescriptBaseConverter,
6
- } from "./TypescriptBaseConverter.js";
7
- import { JSONSchema7Definition } from "json-schema";
8
- import { JSONSchemaConverter } from "./JSONSchemaConverter.js";
9
- import { PathUtils } from "./utils/path-utils.js";
10
- import { toPascalCase } from "./utils/to-pascal-case.js";
11
-
12
- /**
13
- * A TypeScript language converter plugin.
14
- */
15
- export class TypescriptConverter
16
- extends TypescriptBaseConverter
17
- implements LanguagePlugin
18
- {
19
- public readonly language = "typescript";
20
-
21
- private ir: JSONSchemaConverter;
22
- private refTypes: RefTypes = [];
23
-
24
- readonly code: string;
25
-
26
- constructor(
27
- schema: JSONSchema7Definition,
28
- opts?: {
29
- /**
30
- * If true, referenced types will not be created for objects and instead
31
- * the object type will be inlined.
32
- */
33
- inlineTypes?: boolean;
34
- },
35
- ) {
36
- super();
37
-
38
- this.ir = new JSONSchemaConverter(schema);
39
-
40
- const code = this.generateType(this.ir.irNode, {
41
- getReferencedType: opts?.inlineTypes
42
- ? () => undefined
43
- : this.getReferencedType.bind(this),
44
- });
45
-
46
- const rootName = this.ir.irNode.name ? this.ir.irNode.name : "Root";
47
-
48
- this.code = `${this.refTypes
49
- .map(([sig, name, { code }]) => `export type ${name} = ${code}`)
50
- .join("\n")}
51
-
52
- ${code}`;
53
- }
54
-
55
- private getUniqueRefTypeName(
56
- signature: string,
57
- nodePath: string,
58
- ): RefTypeName {
59
- const path = PathUtils.parsePath(nodePath);
60
-
61
- // if the path ends in "0" it's an array item
62
- const isArrayItem = nodePath.split(".").slice(-1)[0] === "0";
63
-
64
- const postFixes = [
65
- "Type",
66
- "Element",
67
- "Schema",
68
- "Object",
69
- "Shape",
70
- "1",
71
- "2",
72
- "3",
73
- "4",
74
- "5",
75
- "6",
76
- ];
77
-
78
- if (isArrayItem) {
79
- postFixes.unshift("Item");
80
- } else {
81
- postFixes.unshift("");
82
- }
83
-
84
- let pathsSegmentsToInclude = 1;
85
- let name = "";
86
- let postFixIndexToTry = 0;
87
-
88
- while (!name) {
89
- const proposedName =
90
- path.slice(-pathsSegmentsToInclude).map(toPascalCase).join("") +
91
- postFixes[postFixIndexToTry];
92
-
93
- const foundSignatureMatch = this.refTypes.find(([sig, name]) => {
94
- return sig === signature && name === proposedName;
95
- });
96
-
97
- if (foundSignatureMatch) {
98
- return foundSignatureMatch[1];
99
- }
100
-
101
- const nameAlreadyUsed = this.refTypes.find(([_, name]) => {
102
- return name === proposedName;
103
- });
104
-
105
- if (nameAlreadyUsed) {
106
- pathsSegmentsToInclude++;
107
-
108
- if (pathsSegmentsToInclude >= path.length) {
109
- // we're out of unique paths, increment a postfix and start the loop again
110
- postFixIndexToTry++;
111
- pathsSegmentsToInclude = 1;
112
-
113
- // absolute fallback that should never/very-rarely happen
114
- if (postFixIndexToTry === postFixes.length) {
115
- name = proposedName + Math.floor(Math.random() * 1000);
116
- }
117
- }
118
- } else {
119
- name = proposedName;
120
- }
121
- }
122
-
123
- return name;
124
- }
125
-
126
- protected getReferencedType(ir: IRNode): string | undefined {
127
- const signature = ir.signature;
128
-
129
- if (!signature) {
130
- return undefined;
131
- }
132
-
133
- const name = this.getUniqueRefTypeName(signature, ir.path);
134
-
135
- // account for recursion, the ref type could have already been created
136
- if (
137
- this.refTypes.find(([sig, _name]) => sig === signature && _name === name)
138
- ) {
139
- return name;
140
- }
141
-
142
- // push the sig/name pair to the refTypes array for recursion to avoid duplicates
143
- this.refTypes.push([
144
- signature,
145
- name,
146
- {
147
- code: "",
148
- },
149
- ]);
150
-
151
- const code = this.generateObjectType(ir, {
152
- getReferencedType: this.getReferencedType.bind(this),
153
- });
154
-
155
- this.refTypes.find(
156
- ([sig, _name]) => sig === signature && _name === name,
157
- )![2].code = code;
158
-
159
- return name;
160
- }
161
- }
@@ -1,180 +0,0 @@
1
- import { IRNode, SignatureOccurrenceValue } from "./types.js";
2
- import { JSONSchema7Definition } from "json-schema";
3
- import { JSONSchemaConverter } from "./JSONSchemaConverter.js";
4
- import {
5
- RefTypeName,
6
- RefTypes,
7
- TypescriptBaseConverter,
8
- } from "./TypescriptBaseConverter.js";
9
- import { PathUtils } from "./utils/path-utils.js";
10
- import { toPascalCase } from "./utils/to-pascal-case.js";
11
-
12
- /**
13
- * A TypeScript language converter plugin that implements namespace scoping.
14
- *
15
- * Referenced/Shared typings are moved to the top level of the namespace scope
16
- * that is created for each IR node.
17
- */
18
- export class TypescriptProcedureConverter extends TypescriptBaseConverter {
19
- private ir: {
20
- args: JSONSchemaConverter | undefined;
21
- data: JSONSchemaConverter | undefined;
22
- };
23
- private refTypes: RefTypes = [];
24
-
25
- readonly code: string;
26
-
27
- constructor(
28
- scopeName: string,
29
- rpcJsonSchemas: {
30
- args: JSONSchema7Definition | undefined;
31
- data: JSONSchema7Definition | undefined;
32
- },
33
- ) {
34
- super();
35
-
36
- this.ir = {
37
- args: rpcJsonSchemas.args
38
- ? new JSONSchemaConverter(rpcJsonSchemas.args)
39
- : undefined,
40
- data: rpcJsonSchemas.data
41
- ? new JSONSchemaConverter(rpcJsonSchemas.data)
42
- : undefined,
43
- };
44
-
45
- const argsCode = this.ir.args
46
- ? this.generateType(this.ir.args.irNode, {
47
- getReferencedType: this.getReferencedType.bind(this),
48
- })
49
- : undefined;
50
-
51
- const dataCode = this.ir.data
52
- ? this.generateType(this.ir.data.irNode, {
53
- getReferencedType: this.getReferencedType.bind(this),
54
- })
55
- : undefined;
56
-
57
- // use 'type' instead of 'interface' for the generated types because we can get `{} & {}` or `Record<string,string>`
58
- this.code = `export namespace ${scopeName} {
59
- ${this.refTypes
60
- .map(([sig, name, { code }]) => `export type ${name} = ${code}`)
61
- .join("\n")}
62
-
63
- export type Args = ${argsCode ? argsCode : "unknown"}
64
-
65
- export type Data = ${dataCode ? dataCode : "unknown"}
66
- }`;
67
- }
68
-
69
- private getUniqueRefTypeName(
70
- signature: string,
71
- nodePath: string,
72
- ): RefTypeName {
73
- const path = PathUtils.parsePath(nodePath);
74
-
75
- // if the path ends in "0" it's an array item
76
- const isArrayItem = nodePath.split(".").slice(-1)[0] === "0";
77
-
78
- const postFixes = [
79
- "Type",
80
- "Element",
81
- "Schema",
82
- "Object",
83
- "Shape",
84
- "1",
85
- "2",
86
- "3",
87
- "4",
88
- "5",
89
- "6",
90
- ];
91
-
92
- if (isArrayItem) {
93
- postFixes.unshift("Item");
94
-
95
- // remove pluralization from the last segment if applicable
96
- if (path[path.length - 1]) {
97
- path[path.length - 1] = path[path.length - 1].replace(/s$/, "");
98
- }
99
- } else {
100
- postFixes.unshift("");
101
- }
102
-
103
- let pathsSegmentsToInclude = 1;
104
- let name = "";
105
- let postFixIndexToTry = 0;
106
-
107
- while (!name) {
108
- const proposedName =
109
- path.slice(-pathsSegmentsToInclude).map(toPascalCase).join("") +
110
- postFixes[postFixIndexToTry];
111
-
112
- const foundSignatureMatch = this.refTypes.find(([sig, name]) => {
113
- return sig === signature && name === proposedName;
114
- });
115
-
116
- if (foundSignatureMatch) {
117
- return foundSignatureMatch[1];
118
- }
119
-
120
- const nameAlreadyUsed = this.refTypes.find(([_, name]) => {
121
- return name === proposedName;
122
- });
123
-
124
- if (nameAlreadyUsed) {
125
- pathsSegmentsToInclude++;
126
-
127
- if (pathsSegmentsToInclude >= path.length) {
128
- // we're out of unique paths, increment a postfix and start the loop again
129
- postFixIndexToTry++;
130
- pathsSegmentsToInclude = 1;
131
-
132
- // absolute fallback that should never/very-rarely happen
133
- if (postFixIndexToTry === postFixes.length) {
134
- name = proposedName + Math.floor(Math.random() * 1000);
135
- }
136
- }
137
- } else {
138
- name = proposedName;
139
- }
140
- }
141
-
142
- return name;
143
- }
144
-
145
- protected getReferencedType(ir: IRNode): string | undefined {
146
- const signature = ir.signature;
147
-
148
- if (!signature) {
149
- return undefined;
150
- }
151
-
152
- const name = this.getUniqueRefTypeName(signature, ir.path);
153
-
154
- // account for recursion, the ref type could have already been created
155
- if (
156
- this.refTypes.find(([sig, _name]) => sig === signature && _name === name)
157
- ) {
158
- return name;
159
- }
160
-
161
- // push the sig/name pair to the refTypes array for recursion to avoid duplicates
162
- this.refTypes.push([
163
- signature,
164
- name,
165
- {
166
- code: "",
167
- },
168
- ]);
169
-
170
- const code = this.generateObjectType(ir, {
171
- getReferencedType: this.getReferencedType.bind(this),
172
- });
173
-
174
- this.refTypes.find(
175
- ([sig, _name]) => sig === signature && _name === name,
176
- )![2].code = code;
177
-
178
- return name;
179
- }
180
- }