typespec-typescript-emitter 1.2.0 → 2.0.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.
Files changed (166) hide show
  1. package/.husky/pre-commit +2 -1
  2. package/.prettierignore +2 -1
  3. package/CHANGELOG.md +19 -24
  4. package/CODE_OF_CONDUCT.md +128 -0
  5. package/README.md +362 -219
  6. package/dist/src/emit_routedTypemap.d.ts +4 -0
  7. package/dist/src/emit_routedTypemap.js +83 -0
  8. package/dist/src/emit_routedTypemap.js.map +1 -0
  9. package/dist/src/emit_routes.d.ts +3 -2
  10. package/dist/src/emit_routes.js +73 -47
  11. package/dist/src/emit_routes.js.map +1 -1
  12. package/dist/src/emit_types.d.ts +3 -9
  13. package/dist/src/emit_types.js +109 -74
  14. package/dist/src/emit_types.js.map +1 -1
  15. package/dist/src/emitter.d.ts +2 -6
  16. package/dist/src/emitter.js +18 -76
  17. package/dist/src/emitter.js.map +1 -1
  18. package/dist/src/helpers/appendableString.d.ts +15 -0
  19. package/dist/src/helpers/appendableString.js +41 -0
  20. package/dist/src/helpers/appendableString.js.map +1 -0
  21. package/dist/src/helpers/arrays.d.ts +3 -0
  22. package/dist/src/helpers/arrays.js +23 -0
  23. package/dist/src/helpers/arrays.js.map +1 -0
  24. package/dist/src/{helper_autogenerateWarning.d.ts → helpers/autogenerateWarning.d.ts} +1 -1
  25. package/dist/src/{helper_autogenerateWarning.js → helpers/autogenerateWarning.js} +1 -2
  26. package/dist/src/helpers/autogenerateWarning.js.map +1 -0
  27. package/dist/src/helpers/buildTypeMap.d.ts +12 -0
  28. package/dist/src/helpers/buildTypeMap.js +44 -0
  29. package/dist/src/helpers/buildTypeMap.js.map +1 -0
  30. package/dist/src/helpers/diagnostics.d.ts +4 -0
  31. package/dist/src/helpers/diagnostics.js +15 -0
  32. package/dist/src/helpers/diagnostics.js.map +1 -0
  33. package/dist/src/helpers/getImports.d.ts +2 -0
  34. package/dist/src/helpers/getImports.js +3 -0
  35. package/dist/src/helpers/getImports.js.map +1 -0
  36. package/dist/src/helpers/namespaces.d.ts +4 -0
  37. package/dist/src/helpers/namespaces.js +14 -0
  38. package/dist/src/helpers/namespaces.js.map +1 -0
  39. package/dist/src/helpers/visibilityHelperFile.d.ts +4 -0
  40. package/dist/src/helpers/visibilityHelperFile.js +57 -0
  41. package/dist/src/helpers/visibilityHelperFile.js.map +1 -0
  42. package/dist/src/lib.d.ts +4 -1
  43. package/dist/src/lib.js +14 -3
  44. package/dist/src/lib.js.map +1 -1
  45. package/dist/src/parseOptions.d.ts +8 -0
  46. package/dist/src/parseOptions.js +26 -0
  47. package/dist/src/parseOptions.js.map +1 -0
  48. package/dist/src/resolve/Resolvable.d.ts +32 -0
  49. package/dist/src/resolve/Resolvable.js +180 -0
  50. package/dist/src/resolve/Resolvable.js.map +1 -0
  51. package/dist/src/resolve/Resolvable_helpers.d.ts +42 -0
  52. package/dist/src/resolve/Resolvable_helpers.js +19 -0
  53. package/dist/src/resolve/Resolvable_helpers.js.map +1 -0
  54. package/dist/src/resolve/operationTypemap.d.ts +21 -0
  55. package/dist/src/resolve/operationTypemap.js +138 -0
  56. package/dist/src/resolve/operationTypemap.js.map +1 -0
  57. package/dist/src/resolve/types/Enum.d.ts +21 -0
  58. package/dist/src/resolve/types/Enum.js +61 -0
  59. package/dist/src/resolve/types/Enum.js.map +1 -0
  60. package/dist/src/resolve/types/Model.Indexed.d.ts +12 -0
  61. package/dist/src/resolve/types/Model.Indexed.js +69 -0
  62. package/dist/src/resolve/types/Model.Indexed.js.map +1 -0
  63. package/dist/src/resolve/types/Model.Shaped.d.ts +17 -0
  64. package/dist/src/resolve/types/Model.Shaped.js +210 -0
  65. package/dist/src/resolve/types/Model.Shaped.js.map +1 -0
  66. package/dist/src/resolve/types/Scalar.d.ts +9 -0
  67. package/dist/src/resolve/types/Scalar.js +109 -0
  68. package/dist/src/resolve/types/Scalar.js.map +1 -0
  69. package/dist/src/resolve/types/Simple.d.ts +11 -0
  70. package/dist/src/resolve/types/Simple.js +49 -0
  71. package/dist/src/resolve/types/Simple.js.map +1 -0
  72. package/dist/src/resolve/types/Tuple.d.ts +9 -0
  73. package/dist/src/resolve/types/Tuple.js +38 -0
  74. package/dist/src/resolve/types/Tuple.js.map +1 -0
  75. package/dist/src/resolve/types/Union.d.ts +9 -0
  76. package/dist/src/resolve/types/Union.js +36 -0
  77. package/dist/src/resolve/types/Union.js.map +1 -0
  78. package/eslint.config.js +12 -4
  79. package/package.json +2 -2
  80. package/src/emit_routedTypemap.ts +128 -0
  81. package/src/emit_routes.ts +108 -61
  82. package/src/emit_types.ts +137 -92
  83. package/src/emitter.ts +19 -107
  84. package/src/helpers/appendableString.ts +52 -0
  85. package/src/helpers/arrays.ts +21 -0
  86. package/src/{helper_autogenerateWarning.ts → helpers/autogenerateWarning.ts} +0 -1
  87. package/src/helpers/buildTypeMap.ts +72 -0
  88. package/src/helpers/diagnostics.ts +19 -0
  89. package/src/helpers/getImports.ts +9 -0
  90. package/src/helpers/namespaces.ts +18 -0
  91. package/src/helpers/visibilityHelperFile.ts +63 -0
  92. package/src/lib.ts +25 -4
  93. package/src/parseOptions.ts +26 -0
  94. package/src/resolve/Resolvable.ts +267 -0
  95. package/src/resolve/Resolvable_helpers.ts +65 -0
  96. package/src/resolve/operationTypemap.ts +212 -0
  97. package/src/resolve/types/Enum.ts +92 -0
  98. package/src/resolve/types/Model.Indexed.ts +113 -0
  99. package/src/resolve/types/Model.Shaped.ts +291 -0
  100. package/src/resolve/types/Scalar.ts +140 -0
  101. package/src/resolve/types/Simple.ts +88 -0
  102. package/src/resolve/types/Tuple.ts +56 -0
  103. package/src/resolve/types/Union.ts +52 -0
  104. package/test/helpers/integrationTest-novis.tsp +51 -0
  105. package/test/helpers/integrationTest.tsp +53 -0
  106. package/test/helpers/largeModel.tsp +40 -0
  107. package/test/{runner.ts → helpers/runner.ts} +1 -1
  108. package/test/helpers/ts.ts +11 -0
  109. package/test/helpers/wrapper.ts +144 -0
  110. package/test/routes/routes.target.ts +35 -0
  111. package/test/routes/routes.test.ts +22 -0
  112. package/test/typeguards/combined.test.ts +78 -0
  113. package/test/typeguards/enum.test.ts +10 -0
  114. package/test/typeguards/model.indexed.test.ts +68 -0
  115. package/test/typeguards/model.shaped.test.ts +38 -0
  116. package/test/typeguards/scalar.test.ts +62 -0
  117. package/test/typeguards/simple.test.ts +35 -0
  118. package/test/typeguards/tuple.test.ts +34 -0
  119. package/test/typeguards/union.test.ts +29 -0
  120. package/test/typemap/typemap-novis.target.ts +38 -0
  121. package/test/typemap/typemap.target.ts +39 -0
  122. package/test/typemap/typemap.test.ts +48 -0
  123. package/test/types/combined.test.ts +71 -0
  124. package/test/types/enum.test.ts +57 -0
  125. package/test/types/model.indexed.test.ts +46 -0
  126. package/test/types/model.shaped.test.ts +23 -0
  127. package/test/types/scalar.test.ts +53 -0
  128. package/test/types/simple.test.ts +20 -0
  129. package/test/types/tuple.test.ts +29 -0
  130. package/test/types/union.test.ts +20 -0
  131. package/tsconfig.json +1 -0
  132. package/dist/src/emit_mapped_types.d.ts +0 -2
  133. package/dist/src/emit_mapped_types.js +0 -124
  134. package/dist/src/emit_mapped_types.js.map +0 -1
  135. package/dist/src/emit_types_resolve.d.ts +0 -22
  136. package/dist/src/emit_types_resolve.js +0 -217
  137. package/dist/src/emit_types_resolve.js.map +0 -1
  138. package/dist/src/emit_types_typeguards.d.ts +0 -17
  139. package/dist/src/emit_types_typeguards.js +0 -121
  140. package/dist/src/emit_types_typeguards.js.map +0 -1
  141. package/dist/src/helper_autogenerateWarning.js.map +0 -1
  142. package/src/emit_mapped_types.ts +0 -155
  143. package/src/emit_types_resolve.ts +0 -280
  144. package/src/emit_types_typeguards.ts +0 -178
  145. package/test/main.test.ts +0 -83
  146. package/test/out/.gitkeep +0 -0
  147. package/test/targets/enum.routed-types.ts +0 -59
  148. package/test/targets/enum.routes.ts +0 -29
  149. package/test/targets/enum.target.ts +0 -39
  150. package/test/targets/enum.tsp +0 -36
  151. package/test/targets/pr8.routed-types.ts +0 -131
  152. package/test/targets/pr8.routes.ts +0 -30
  153. package/test/targets/pr8.target.ts +0 -97
  154. package/test/targets/pr8.tsp +0 -62
  155. package/test/targets/simple-routes.routed-types.ts +0 -64
  156. package/test/targets/simple-routes.routes.ts +0 -29
  157. package/test/targets/simple-routes.target.ts +0 -21
  158. package/test/targets/simple-routes.tsp +0 -23
  159. package/test/targets/union.routed-types.ts +0 -59
  160. package/test/targets/union.routes.ts +0 -23
  161. package/test/targets/union.target.ts +0 -59
  162. package/test/targets/union.tsp +0 -38
  163. package/test/targets/visibility.routed-types.ts +0 -81
  164. package/test/targets/visibility.routes.ts +0 -38
  165. package/test/targets/visibility.target.ts +0 -36
  166. package/test/targets/visibility.tsp +0 -49
@@ -1,155 +0,0 @@
1
- import {
2
- EmitContext,
3
- isTemplateDeclaration,
4
- Namespace,
5
- Operation,
6
- Type,
7
- } from "@typespec/compiler";
8
- import {
9
- getHttpOperation,
10
- resolveRequestVisibility,
11
- Visibility,
12
- } from "@typespec/http";
13
- import { resolveType } from "./emit_types_resolve.js";
14
-
15
- export const emitRoutedTypemap = (
16
- context: EmitContext,
17
- namespace: Namespace,
18
- ): string => {
19
- const ops: {
20
- [path: string]: {
21
- [verb: string]: {
22
- // "string" in these does not refer to the type "string"! It's the typescript code as string.
23
- request: string;
24
- response: Array<{ status: number | "unknown"; body: string }>;
25
- };
26
- };
27
- } = {};
28
-
29
- const traverseNamespace = (n: Namespace): void => {
30
- // operations
31
- const processOp = (op: Operation) => {
32
- const httpOp = getHttpOperation(context.program, op);
33
- const path = httpOp[0].path;
34
- const verb = httpOp[0].verb.toUpperCase();
35
- if (!ops[path]) ops[path] = {};
36
- ops[path][verb] = {
37
- request: "null",
38
- response: [{ status: 200, body: "{}" }],
39
- };
40
-
41
- // request
42
- let request = "null";
43
- if (httpOp[0].parameters.body) {
44
- request = resolveType(httpOp[0].parameters.body.type, {
45
- nestlevel: 2,
46
- currentNamespace: namespace,
47
- context,
48
- visibility: resolveRequestVisibility(
49
- context.program,
50
- op,
51
- httpOp[0].verb,
52
- ),
53
- resolveEvenWithName: true,
54
- }).replaceAll("\n", "\n ");
55
- }
56
- ops[path][verb].request = request;
57
-
58
- // response
59
- if (op.returnType && op.returnType.kind) {
60
- const getReturnType = (
61
- t: Type,
62
- ): (typeof ops)[string][string]["response"] => {
63
- const ret: (typeof ops)[string][string]["response"] = [];
64
- if (t.kind === "Model") {
65
- // if the return type is a model, it may have a fully qualified body
66
- const modelret: (typeof ret)[number] = {
67
- status: 200,
68
- body: "{}",
69
- };
70
- let wasQualifiedBody = false;
71
- t.properties.forEach((prop) => {
72
- prop.decorators.forEach((dec) => {
73
- // one of the properties may be the status code
74
- if (
75
- dec.definition?.name === "@statusCode" &&
76
- prop.type.kind === "Number"
77
- )
78
- modelret.status = prop.type.value;
79
- // one of the properties may be the body definition
80
- if (dec.definition?.name === "@body") {
81
- modelret.body = resolveType(prop.type, {
82
- nestlevel: 2,
83
- currentNamespace: namespace,
84
- context,
85
- visibility: Visibility.Read,
86
- resolveEvenWithName: true,
87
- }).replaceAll("\n", "\n ");
88
- wasQualifiedBody = true;
89
- }
90
- });
91
- });
92
- // ... if not, we assume status 200 and treat the model as the body
93
- if (!wasQualifiedBody) {
94
- modelret.body = resolveType(t, {
95
- nestlevel: 2,
96
- currentNamespace: namespace,
97
- context,
98
- visibility: Visibility.Read,
99
- resolveEvenWithName: true,
100
- }).replaceAll("\n", "\n ");
101
- }
102
- ret.push(modelret);
103
- } else if (t.kind === "Union") {
104
- // if the return type is a union, we have to check and resolve all variants
105
- // the union could either be a body-only definition or fully qualified (see above)
106
- t.variants.forEach((variant) => {
107
- ret.push(...getReturnType(variant.type));
108
- });
109
- } else
110
- ret.push({
111
- status: 200,
112
- body: resolveType(t, {
113
- nestlevel: 1,
114
- currentNamespace: namespace,
115
- context,
116
- visibility: Visibility.Read,
117
- resolveEvenWithName: true,
118
- }),
119
- });
120
- return ret;
121
- };
122
- ops[path][verb].response = getReturnType(op.returnType);
123
- }
124
- }; // end operations
125
-
126
- n.operations.forEach(processOp);
127
- n.interfaces.forEach((itf) => {
128
- if (!isTemplateDeclaration(itf)) itf.operations.forEach(processOp);
129
- });
130
-
131
- // get and traverse all namespaces
132
- n.namespaces.forEach((ns) => traverseNamespace(ns));
133
- };
134
-
135
- traverseNamespace(namespace);
136
- let out = `export type types_${context.options["root-namespace"]} = {\n`;
137
- out += Object.entries(ops)
138
- .map((path) => {
139
- let pathret = ` ['${path[0]}']: {\n`;
140
- pathret += Object.entries(path[1])
141
- .map((verb) => {
142
- let verbret = ` ['${verb[0]}']: {\n`;
143
- verbret += ` request: ${verb[1].request}\n`;
144
- verbret += ` response: ${verb[1].response.map((res) => `{status: ${res.status}, body: ${res.body}}`).join(" | ")}\n`;
145
- verbret += " }";
146
- return verbret;
147
- })
148
- .join(",\n");
149
- pathret += "\n }";
150
- return pathret;
151
- })
152
- .join(",\n");
153
- out += "\n};\n";
154
- return out;
155
- };
@@ -1,280 +0,0 @@
1
- import {
2
- ArrayModelType,
3
- EmitContext,
4
- Enum,
5
- getDoc,
6
- Model,
7
- Namespace,
8
- RecordModelType,
9
- Scalar,
10
- Tuple,
11
- Type,
12
- Union,
13
- } from "@typespec/compiler";
14
- import { isVisible, Visibility } from "@typespec/http";
15
-
16
- type CommonOptions = {
17
- nestlevel: number;
18
- currentNamespace: Namespace;
19
- context: EmitContext;
20
- visibility?: Visibility;
21
- resolveEvenWithName?: boolean;
22
- isNamespaceRoot?: boolean;
23
- };
24
-
25
- export const resolveType = (t: Type, opts: CommonOptions): string => {
26
- let typeStr = "unknown";
27
- switch (t.kind) {
28
- case "Model":
29
- if (t.name === "Array") {
30
- typeStr = resolveArray(t as ArrayModelType, opts);
31
- } else if (t.name === "Record") {
32
- typeStr = resolveRecord(t as RecordModelType, opts);
33
- } else typeStr = resolveModel(t, opts);
34
- break;
35
- case "Boolean":
36
- typeStr = "boolean";
37
- break;
38
- case "Enum":
39
- typeStr = resolveEnum(t, opts);
40
- break;
41
- case "Intrinsic":
42
- typeStr = t.name;
43
- break;
44
- case "Number":
45
- typeStr = t.valueAsString;
46
- break;
47
- case "Scalar":
48
- typeStr = resolveScalar(
49
- t,
50
- !!opts.context.options["serializable-date-types"],
51
- );
52
- break;
53
- case "String":
54
- typeStr = `'${t.value}'`;
55
- break;
56
- case "Tuple":
57
- typeStr = resolveTuple(t, opts);
58
- break;
59
- case "Union":
60
- typeStr = resolveUnion(t, opts);
61
- break;
62
- case "EnumMember":
63
- if (opts.resolveEvenWithName) {
64
- // If we're at routed typemap we will emit enum either value or index / name (as string if configured)
65
- const value = resolveEnumMemberValue(t.enum, t.name);
66
- if (value) {
67
- typeStr = typeof value === "string" ? `'${value}'` : value.toString();
68
- } else if (opts.context.options["string-nominal-enums"]) {
69
- typeStr = `'${t.name}'`;
70
- } else {
71
- const index = resolveEnumMemberIndex(t.enum, t.name);
72
- if (index !== undefined) {
73
- typeStr = index.toString();
74
- }
75
- }
76
- break;
77
- }
78
- typeStr = `${t.enum.name}.${t.name}`;
79
- break;
80
- default:
81
- console.warn("Could not resolve type:", t.kind);
82
- }
83
- return typeStr;
84
- };
85
-
86
- export const resolveArray = (
87
- a: ArrayModelType,
88
- opts: CommonOptions,
89
- ): string => {
90
- if (a.name !== "Array")
91
- throw new Error(`Trying to parse model ${a.name} as Array`);
92
- return `(${resolveType(a.indexer.value, { ...opts, isNamespaceRoot: false })})[]`;
93
- };
94
-
95
- export const resolveRecord = (
96
- a: RecordModelType,
97
- opts: CommonOptions,
98
- ): string => {
99
- if (a.name !== "Record")
100
- throw new Error(`Trying to parse model ${a.name} as Record`);
101
- return `{[k: string]: ${resolveType(a.indexer.value, opts)}}`;
102
- };
103
-
104
- export const resolveEnum = (e: Enum, opts: CommonOptions): string => {
105
- if (
106
- e.name &&
107
- !opts.isNamespaceRoot &&
108
- opts.currentNamespace.enums.has(e.name) &&
109
- !opts.resolveEvenWithName
110
- ) {
111
- return e.name;
112
- }
113
-
114
- if (opts.resolveEvenWithName) {
115
- return resolveEnumAsUnion(e, opts);
116
- }
117
-
118
- let ret = "{\n";
119
- let i = 1;
120
- e.members.forEach((p) => {
121
- const val =
122
- p.value === undefined
123
- ? ""
124
- : " = " +
125
- (typeof p.value === "string" ? `'${p.value}'` : p.value.toString());
126
- ret = ret.addLine(
127
- `${p.name.includes("-") ? `'${p.name}'` : p.name}${val}${i < e.members.size ? "," : ""}`,
128
- opts.nestlevel + 1,
129
- );
130
- i++;
131
- });
132
- ret = ret.addLine("}", opts.nestlevel, true);
133
- return ret;
134
- };
135
-
136
- export const resolveTuple = (t: Tuple, opts: CommonOptions): string => {
137
- return `[${t.values.map((v) => resolveType(v, opts)).join(", ")}]`;
138
- };
139
-
140
- export const resolveUnion = (u: Union, opts: CommonOptions): string => {
141
- if (
142
- u.name &&
143
- !opts.isNamespaceRoot &&
144
- u.namespace?.unions.has(u.name) &&
145
- !opts.resolveEvenWithName
146
- )
147
- return u.name;
148
- return Array.from(u.variants)
149
- .map((v) => {
150
- const variantType = v[1].type;
151
- // If variant is a named model in the current namespace
152
- // and it's not in routed typemap, reference it by name:
153
- if (
154
- !opts.resolveEvenWithName &&
155
- variantType.kind === "Model" &&
156
- variantType.name &&
157
- opts.currentNamespace.models.has(variantType.name)
158
- ) {
159
- return variantType.name;
160
- }
161
- // Otherwise resolve type inline
162
- return resolveType(variantType, { ...opts, isNamespaceRoot: false });
163
- })
164
- .join(" | ");
165
- };
166
- export const resolveScalar = (
167
- s: Scalar,
168
- serializableDates: boolean,
169
- ): string => {
170
- let ret = "unknown";
171
- switch (s.name) {
172
- case "boolean":
173
- ret = "boolean";
174
- break;
175
- case "bytes":
176
- ret = "Uint8Array";
177
- break;
178
- case "duration":
179
- case "numeric":
180
- ret = "number";
181
- break;
182
- case "plainTime":
183
- case "string":
184
- case "url":
185
- ret = "string";
186
- break;
187
- case "offsetDateTime":
188
- case "plainDate":
189
- case "utcDateTime":
190
- ret = serializableDates ? "string" : "Date";
191
- break;
192
- case "unixTimestamp32":
193
- ret = serializableDates ? "number" : "Date";
194
- break;
195
- default:
196
- console.warn("Could not resolve scalar:", s.name);
197
- }
198
-
199
- if (serializableDates && s.name === "unixTimestamp32") {
200
- // If we want to use serializable date types baseScalar should be skipped
201
- // for unixTimestamp32 (since it is utcDateTime and would emit a string)
202
- return ret;
203
- }
204
-
205
- return s.baseScalar ? resolveScalar(s.baseScalar, serializableDates) : ret;
206
- };
207
-
208
- export const resolveModel = (m: Model, opts: CommonOptions): string => {
209
- if (
210
- m.name &&
211
- !opts.isNamespaceRoot &&
212
- opts.currentNamespace.models.has(m.name) &&
213
- !opts.resolveEvenWithName
214
- )
215
- return m.name;
216
- let ret = "{\n";
217
- let i = 1;
218
- m.properties.forEach((p) => {
219
- if (
220
- opts.visibility === undefined ||
221
- isVisible(opts.context.program, p, opts.visibility)
222
- ) {
223
- if (opts.context) {
224
- const doc = getDoc(opts.context.program, p);
225
- if (doc) ret = ret.addLine(`/** ${doc} */`, opts.nestlevel! + 1);
226
- }
227
- const typeStr = resolveType(p.type, {
228
- ...opts,
229
- nestlevel: opts.nestlevel + 1,
230
- isNamespaceRoot: false,
231
- });
232
- if (typeStr.includes("unknown"))
233
- console.warn(`Could not resolve property ${p.name} on ${m.name}`);
234
- ret = ret.addLine(
235
- `${p.name}${p.optional ? "?" : ""}: ${typeStr}${i < m.properties.size ? "," : ""}`,
236
- opts.nestlevel + 1,
237
- );
238
- }
239
- i++;
240
- });
241
- ret = ret.addLine("}", opts.nestlevel, true);
242
- return ret;
243
- };
244
-
245
- export const resolveEnumMemberValue = (
246
- e: Enum,
247
- name: string,
248
- ): string | number | undefined => {
249
- const member = e.members.get(name);
250
- if (!member) {
251
- console.warn("Missing enum member under name:", name);
252
- return;
253
- }
254
- return member?.value;
255
- };
256
-
257
- export const resolveEnumAsUnion = (e: Enum, opts: CommonOptions): string => {
258
- return Array.from(e.members.values())
259
- .map((member, index) => {
260
- const fallback = opts.context.options["string-nominal-enums"]
261
- ? member.name
262
- : index;
263
- const value = resolveEnumMemberValue(e, member.name) ?? fallback;
264
- return typeof value === "string" ? `'${value}'` : value.toString();
265
- })
266
- .join(" | ");
267
- };
268
-
269
- export const resolveEnumMemberIndex = (
270
- e: Enum,
271
- name: string,
272
- ): number | undefined => {
273
- const index = Array.from(e.members.values()).findIndex((member) => {
274
- return member.name === name;
275
- });
276
- if (index === -1) {
277
- return;
278
- }
279
- return index;
280
- };
@@ -1,178 +0,0 @@
1
- import { Model, Namespace, Type } from "@typespec/compiler";
2
- import { resolveScalar } from "./emit_types_resolve.js";
3
-
4
- export const getTypeguardModel = (
5
- m: Model,
6
- accessor: string,
7
- nestingLevel = 1,
8
- currentNamespace: Namespace,
9
- serializableDates: boolean,
10
- knownGuards?: Array<{ filename: string; name: string }>,
11
- ): [string, string[]] => {
12
- const imports: string[] = [];
13
- return [
14
- Array.from(m.properties)
15
- .map((property) => {
16
- const guard = getTypeguard(
17
- property[1].type,
18
- `${accessor}['${property[1].name}']`,
19
- nestingLevel + 1,
20
- currentNamespace,
21
- serializableDates,
22
- knownGuards,
23
- );
24
- imports.push(...guard[1]);
25
- let ret = " ".repeat(nestingLevel);
26
- ret += property[1].optional
27
- ? `${accessor}['${property[1].name}'] === undefined || `
28
- : `${accessor}['${property[1].name}'] !== undefined && `;
29
- ret += `(${guard[0]})`;
30
- return ret;
31
- })
32
- .filter((x) => !!x)
33
- .join(" &&\n"),
34
- imports,
35
- ];
36
- };
37
-
38
- /**
39
- * Creates the function body for a typeguard
40
- * @param t Type to create guards for
41
- * @param accessor String by which the type-to-test can be accessed by the code
42
- * @param nestingLevel
43
- * @param knownGuards Array of names of known typeguards; if type is found in those, no new typeguard will be created and instead a reference to the existing one is produced
44
- * @returns Tuple: [function body of the typeguard, array of import filenames (not unique!)]
45
- */
46
- export const getTypeguard = (
47
- t: Type,
48
- accessor: string,
49
- nestingLevel = 1,
50
- currentNamespace: Namespace,
51
- serializableDates: boolean,
52
- knownGuards?: Array<{ filename: string; name: string }>,
53
- ): [string, string[]] => {
54
- switch (t.kind) {
55
- case "Model":
56
- if (t.name === "Array") {
57
- const guard = getTypeguard(
58
- t.indexer!.value,
59
- "v",
60
- nestingLevel,
61
- currentNamespace,
62
- serializableDates,
63
- knownGuards,
64
- );
65
- if (guard[0].endsWith("\n"))
66
- guard[0] = guard[0].substring(0, guard[0].length - 1);
67
- return [
68
- `Array.isArray(${accessor}) && ${accessor}.every((v) => ${guard[0]})`,
69
- guard[1],
70
- ];
71
- } else if (t.name === "Record") {
72
- const guard = getTypeguard(
73
- t.indexer!.value,
74
- "v",
75
- nestingLevel,
76
- currentNamespace,
77
- serializableDates,
78
- knownGuards,
79
- );
80
- if (guard[0].endsWith("\n"))
81
- guard[0] = guard[0].substring(0, guard[0].length - 1);
82
- return [
83
- `typeof ${accessor} === 'object' && (Object.entries(${accessor}).length > 0 ? Object.values(${accessor} as Record<string, any>).every((v) => ${guard[0]}) : true)`,
84
- guard[1],
85
- ];
86
- } else if (knownGuards && knownGuards.some((x) => x.name === t.name)) {
87
- return [
88
- `is${t.name}(${accessor})`,
89
- [
90
- `import {is${t.name}} from './${knownGuards.find((x) => x.name === t.name)!.filename}';`,
91
- ],
92
- ];
93
- } else if (
94
- t.name &&
95
- !knownGuards &&
96
- currentNamespace.name === t.namespace!.name
97
- ) {
98
- return [`is${t.name}(${accessor})`, []];
99
- } else {
100
- const guard = getTypeguardModel(
101
- t,
102
- accessor,
103
- nestingLevel,
104
- currentNamespace,
105
- serializableDates,
106
- knownGuards,
107
- );
108
- return [
109
- `(\n${guard[0]}\n${" ".repeat(Math.max(nestingLevel - 1, 0))})`,
110
- guard[1],
111
- ];
112
- }
113
- case "Boolean":
114
- return [`typeof ${accessor} === 'boolean'`, []];
115
- case "Intrinsic":
116
- return [`${accessor} === ${t.name}`, []];
117
- case "Number":
118
- return [`typeof ${accessor} === 'number'`, []];
119
- case "Scalar":
120
- if (
121
- // TODO: figure out how to support all the varieties of dates
122
- // TODO: support byte arrays
123
- resolveScalar(t, serializableDates) !== "Date" &&
124
- resolveScalar(t, serializableDates) !== "Uint8Array"
125
- )
126
- return [
127
- `typeof ${accessor} === '${resolveScalar(t, serializableDates)}'`,
128
- [],
129
- ];
130
- break;
131
- case "String":
132
- return [`typeof ${accessor} === 'string'`, []];
133
- case "Tuple": {
134
- // TODO: ['string1', 'string2'] gets resolved as [string, string] instead of literals. Why?
135
- const imports: string[] = [];
136
- return [
137
- t.values
138
- .map((v, i) => {
139
- const guard = getTypeguard(
140
- v,
141
- `${accessor}[${i}]`,
142
- nestingLevel,
143
- currentNamespace,
144
- serializableDates,
145
- knownGuards,
146
- );
147
- imports.push(...guard[1]);
148
- return `(${guard[0]})`;
149
- })
150
- .join(" && "),
151
- imports,
152
- ];
153
- }
154
- case "Union": {
155
- const imports: string[] = [];
156
- return [
157
- Array.from(t.variants)
158
- .map((v) => {
159
- const guard = getTypeguard(
160
- v[1].type,
161
- `${accessor}`,
162
- nestingLevel,
163
- currentNamespace,
164
- serializableDates,
165
- knownGuards,
166
- );
167
- imports.push(...guard[1]);
168
- return `(${guard[0]})`;
169
- })
170
- .join(" || "),
171
- imports,
172
- ];
173
- }
174
- default:
175
- console.warn("Could not resolve type:", t.kind);
176
- }
177
- return ["true", []]; // fallback to not break everything in case of errors
178
- };
package/test/main.test.ts DELETED
@@ -1,83 +0,0 @@
1
- import { readFileSync, writeFileSync } from "fs";
2
- import { join } from "path";
3
- import { expect, it } from "vitest";
4
- import { runner } from "./runner.js";
5
-
6
- const tests = ["simple-routes", "enum", "pr8", "union", "visibility"];
7
-
8
- const getTestData = (
9
- testName: (typeof tests)[number],
10
- ): {
11
- input: string;
12
- output: {
13
- ts: string;
14
- routes: string;
15
- routedTypes: string;
16
- };
17
- } => {
18
- const targetsFolder = "targets";
19
- return {
20
- input: readFileSync(join(__dirname, `${targetsFolder}/${testName}.tsp`), {
21
- encoding: "utf8",
22
- }),
23
- output: {
24
- ts: readFileSync(
25
- join(__dirname, `${targetsFolder}/${testName}.target.ts`),
26
- { encoding: "utf8" },
27
- )
28
- .trim()
29
- .replaceAll("\r\n", "\n"),
30
- routes: readFileSync(
31
- join(__dirname, `${targetsFolder}/${testName}.routes.ts`),
32
- { encoding: "utf8" },
33
- )
34
- .trim()
35
- .replaceAll("\r\n", "\n"),
36
- routedTypes: readFileSync(
37
- join(__dirname, `${targetsFolder}/${testName}.routed-types.ts`),
38
- { encoding: "utf8" },
39
- )
40
- .trim()
41
- .replaceAll("\r\n", "\n"),
42
- },
43
- };
44
- };
45
-
46
- const emitterOptions = {
47
- "root-namespace": "test",
48
- "enable-routes": true,
49
- "enable-types": true,
50
- "enable-typeguards": true,
51
- "enable-routed-typemap": true,
52
- };
53
-
54
- tests.forEach((test) => {
55
- it(`works for ${test}`, async () => {
56
- const data = getTestData(test);
57
- const emitter = await runner.emit(
58
- "typespec-typescript-emitter",
59
- emitterOptions,
60
- );
61
- const result = await emitter.compileAndDiagnose(data.input);
62
-
63
- writeFileSync(
64
- join(__dirname, `out/${test}.target.ts`),
65
- result[0].outputs["Test.ts"],
66
- );
67
- writeFileSync(
68
- join(__dirname, `out/${test}.routes.ts`),
69
- result[0].outputs["routes_test.ts"],
70
- );
71
- writeFileSync(
72
- join(__dirname, `out/${test}.routed-types.ts`),
73
- result[0].outputs["routedTypemap_test.ts"],
74
- );
75
-
76
- expect(result[1].length).toBe(0);
77
- expect(result[0].outputs["Test.ts"].trim()).toBe(data.output.ts);
78
- expect(result[0].outputs["routes_test.ts"].trim()).toBe(data.output.routes);
79
- expect(result[0].outputs["routedTypemap_test.ts"].trim()).toBe(
80
- data.output.routedTypes,
81
- );
82
- });
83
- });
package/test/out/.gitkeep DELETED
File without changes