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
@@ -0,0 +1,113 @@
1
+ import { Model } from "@typespec/compiler";
2
+ import { Resolvable } from "../Resolvable.js";
3
+ import {
4
+ Resolver,
5
+ ResolverOptions,
6
+ ResolverResult,
7
+ } from "../Resolvable_helpers.js";
8
+
9
+ enum Type {
10
+ Array,
11
+ Record,
12
+ }
13
+
14
+ export class IndexedModel extends Resolvable<Model> {
15
+ constructor(t: Model, r: Resolver) {
16
+ super(t, r);
17
+ if (t.name === "Array") {
18
+ this._indexedModelKind = Type.Array;
19
+ } else if (t.name === "Record") {
20
+ this._indexedModelKind = Type.Record;
21
+ } else
22
+ throw new TypeError(
23
+ `Cannot create IndexedModel on type of name ${t.name}`,
24
+ );
25
+ }
26
+
27
+ private _indexedModelKind: Type;
28
+ protected expectedTypeKind: string = "Model";
29
+
30
+ public override async hasVisibility(
31
+ opts: ResolverOptions<Resolver>,
32
+ out: ResolverResult<Resolver>,
33
+ ): Promise<boolean> {
34
+ if (await super.hasVisibility(opts, out)) return true;
35
+ if (
36
+ await Resolvable.hasVisibility(this._t.indexer!.value, opts, out, this._t)
37
+ )
38
+ return true;
39
+ return false;
40
+ }
41
+
42
+ protected async type(
43
+ opts: ResolverOptions<Resolver.Type>,
44
+ out: ResolverResult<Resolver.Type>,
45
+ ): Promise<void> {
46
+ const resolved = await this.resolveNested(
47
+ this._t.indexer!.value,
48
+ opts,
49
+ out,
50
+ false,
51
+ );
52
+ switch (this._indexedModelKind) {
53
+ case Type.Array:
54
+ out.resolved.append(`(${resolved.resolved})[]`);
55
+ break;
56
+ case Type.Record:
57
+ out.resolved.append(`{[k: string]: ${resolved.resolved}}`);
58
+ break;
59
+ }
60
+ out.imports.push(...resolved.imports);
61
+ }
62
+
63
+ protected async typeguard(
64
+ opts: ResolverOptions<Resolver.Typeguard>,
65
+ out: ResolverResult<Resolver.Typeguard>,
66
+ ): Promise<void> {
67
+ const oldAccessor = opts.accessor;
68
+ opts.accessor = this._indexedModelKind === Type.Array ? "v" : "v[1]";
69
+ const resolved = await this.resolveNested(
70
+ this._t.indexer!.value,
71
+ opts,
72
+ out,
73
+ );
74
+ opts.accessor = oldAccessor;
75
+ if (resolved.resolved.value.endsWith("\n")) resolved.resolved.dropLast();
76
+
77
+ switch (this._indexedModelKind) {
78
+ case Type.Array:
79
+ out.resolved.append(
80
+ `Array.isArray(${opts.accessor}) && ${opts.accessor}.every((v) => ${resolved.resolved})`,
81
+ );
82
+ break;
83
+ case Type.Record:
84
+ out.resolved.append(
85
+ `typeof ${opts.accessor} === 'object' && Object.entries(${opts.accessor} as Record<string, any>).every((v) => ${resolved.resolved})`,
86
+ );
87
+ break;
88
+ }
89
+ out.imports.push(...resolved.imports);
90
+ }
91
+
92
+ protected validate(): void {
93
+ if (this._t.name !== "Array" && this._t.name !== "Record") {
94
+ this.diagnostic(
95
+ "typeclass-modeltype-mismatch",
96
+ "Tried creating IndexedModel for non-array and non-record model type",
97
+ "error",
98
+ );
99
+ throw new Error(
100
+ "Tried creating IndexedModel for non-array and non-record model type",
101
+ );
102
+ }
103
+ if (!this._t.indexer) {
104
+ this.diagnostic(
105
+ "no-indexer",
106
+ "Tried creating IndexedModel for type without indexer",
107
+ "error",
108
+ );
109
+ throw new Error("Tried creating IndexedModel for type without indexer");
110
+ }
111
+ super.validate();
112
+ }
113
+ }
@@ -0,0 +1,291 @@
1
+ import { getDoc, Model, ModelProperty, Program } from "@typespec/compiler";
2
+ import { isVisible, Visibility } from "@typespec/http";
3
+ import { AppendableString } from "../../helpers/appendableString.js";
4
+ import { compareArrays } from "../../helpers/arrays.js";
5
+ import { namespaceListFromNamespace } from "../../helpers/namespaces.js";
6
+ import { Resolvable } from "../Resolvable.js";
7
+ import {
8
+ Resolver,
9
+ ResolverOptions,
10
+ ResolverResult,
11
+ } from "../Resolvable_helpers.js";
12
+
13
+ type VisibilityType = "Read" | "Create" | "Update" | "Delete" | "Query";
14
+
15
+ export class ShapedModel extends Resolvable<Model> {
16
+ protected expectedTypeKind: string = "Model";
17
+
18
+ private getProps(): ModelProperty[] {
19
+ return Array.from(this._t.properties).map((p) => p[1]);
20
+ }
21
+
22
+ private getPropVisibilityType(
23
+ program: Program,
24
+ prop: ModelProperty,
25
+ ): VisibilityType[] | null {
26
+ if (
27
+ !prop.decorators.some(
28
+ (decorator) => decorator.definition?.name === "@visibility",
29
+ )
30
+ )
31
+ return null;
32
+ const ret: VisibilityType[] = [];
33
+ if (isVisible(program, prop, Visibility.Read)) ret.push("Read");
34
+ if (isVisible(program, prop, Visibility.Create)) ret.push("Create");
35
+ if (isVisible(program, prop, Visibility.Update)) ret.push("Update");
36
+ if (isVisible(program, prop, Visibility.Delete)) ret.push("Delete");
37
+ if (isVisible(program, prop, Visibility.Query)) ret.push("Query");
38
+ return ret;
39
+ }
40
+
41
+ /**
42
+ * Checks whether or not any of the model's properties has @visibility modifier.
43
+ * Also returns true if any base model (in the entire chain) has a visibility OR if
44
+ * any nested type (in the model's properties) has a visibility.
45
+ */
46
+ public override async hasVisibility(
47
+ opts: ResolverOptions<Resolver>,
48
+ out: ResolverResult<Resolver>,
49
+ ): Promise<boolean> {
50
+ if (await super.hasVisibility(opts, out)) return true;
51
+ // resolve base
52
+ if (
53
+ this._t.baseModel &&
54
+ (await Resolvable.hasVisibility(this._t.baseModel, opts, out, this._t))
55
+ )
56
+ return true;
57
+ // resolve props: check for decorators
58
+ const props = this.getProps();
59
+ if (
60
+ props.some((prop) =>
61
+ prop.decorators.some(
62
+ (decorator) => decorator.definition?.name === "@visibility",
63
+ ),
64
+ )
65
+ )
66
+ return true;
67
+ // resolve props: check if any resolves to visibility
68
+ for (const prop of props) {
69
+ // check if this prop is somewhere up the resolution chain -> recursion -> skip (defaults to false)
70
+ const propNamespaces = namespaceListFromNamespace(
71
+ (prop.type as any).namespace,
72
+ );
73
+ if (
74
+ opts.parents &&
75
+ opts.parents.some((parent) => {
76
+ const parentNamespaces = namespaceListFromNamespace(
77
+ (parent as any).namespace,
78
+ );
79
+ return (
80
+ parent.kind === prop.type.kind &&
81
+ (prop.type as any).name &&
82
+ (parent as any).name === (prop.type as any).name &&
83
+ propNamespaces &&
84
+ parentNamespaces &&
85
+ compareArrays(propNamespaces, parentNamespaces)
86
+ );
87
+ })
88
+ )
89
+ continue;
90
+ // not recursion, resolve
91
+ if (await Resolvable.hasVisibility(prop.type, opts, out, this._t))
92
+ return true;
93
+ }
94
+ // nothing true
95
+ return false;
96
+ }
97
+
98
+ protected async type(
99
+ opts: ResolverOptions<Resolver.Type>,
100
+ out: ResolverResult<Resolver.Type>,
101
+ ): Promise<void> {
102
+ if (opts.emitDocs) {
103
+ out.doc = getDoc(opts.program, this._t);
104
+ }
105
+ const resolution = new AppendableString();
106
+ // check for base model (extended)
107
+ if (this._t.baseModel) {
108
+ const resolvedBase = await this.resolveNested(
109
+ this._t.baseModel,
110
+ opts,
111
+ out,
112
+ );
113
+ out.imports.push(...resolvedBase.imports);
114
+ resolution.append(`${resolvedBase.resolved} & `);
115
+ }
116
+
117
+ const props = this.getProps();
118
+
119
+ const propVisibilityTypes: Record<
120
+ string,
121
+ { type: VisibilityType[] | null; nested: string }
122
+ > = {};
123
+
124
+ if (props.length === 0) {
125
+ out.resolved.append("{}");
126
+ } else {
127
+ resolution.append("{\n");
128
+ for (let i = 0; i < props.length; i++) {
129
+ const prop = props[i];
130
+ // add doc strings
131
+ if (opts.emitDocs) {
132
+ const doc = getDoc(opts.program, prop);
133
+ if (doc) resolution.addLine(`/** ${doc} */`, opts.nestlevel + 1);
134
+ }
135
+ // resolve prop
136
+ const resolved = await this.resolveNested(prop.type, opts, out);
137
+ resolution.addLine(
138
+ `${prop.name}${prop.optional ? "?" : ""}: ${resolved.resolved}${i + 1 < this._t.properties.size ? "," : ""}`,
139
+ opts.nestlevel + 1,
140
+ );
141
+ out.imports.push(...resolved.imports);
142
+ // if prop has visibility, save to vismap
143
+ const propVis = this.getPropVisibilityType(opts.program, prop);
144
+ if (propVis !== null || resolved.hasVisibility) {
145
+ propVisibilityTypes[prop.name] = { type: null, nested: "" };
146
+ propVisibilityTypes[prop.name].type = propVis;
147
+ propVisibilityTypes[prop.name].nested = resolved.visibilityMap;
148
+ }
149
+ }
150
+
151
+ // build prop visibility map
152
+ const visMap = new AppendableString();
153
+ const propVisibilityTypesArr = Object.entries(propVisibilityTypes).filter(
154
+ (p) => p[1].type !== null || p[1].nested,
155
+ );
156
+ if (propVisibilityTypesArr.length > 0) {
157
+ visMap.addLine("{");
158
+ let i = 1;
159
+ for (const [prop, propvis] of propVisibilityTypesArr) {
160
+ const visLine = `vis: [${(propvis.type ?? []).map((t) => `Lifecycle.${t}`).join(", ")}]`;
161
+ if (propvis.type !== null && !propvis.nested) {
162
+ visMap.addLine(
163
+ `'${prop}': {${visLine}}`,
164
+ opts.nestlevel + 1,
165
+ "continued",
166
+ );
167
+ }
168
+ if (propvis.nested) {
169
+ visMap.addLine(`'${prop}': {`, opts.nestlevel + 1);
170
+ if (propvis.type) visMap.addLine(`${visLine},`, opts.nestlevel + 2);
171
+ visMap.addLine(
172
+ `nested: ${propvis.nested
173
+ .split("\n")
174
+ .map((l) => ` ${l}`)
175
+ .join("\n")
176
+ .trim()}`,
177
+ opts.nestlevel + 2,
178
+ );
179
+ visMap.addLine("}", opts.nestlevel + 1, "continued");
180
+ }
181
+ visMap.addLine(i < propVisibilityTypesArr.length ? "," : "");
182
+ i++;
183
+ }
184
+ visMap.addLine("}", opts.nestlevel, "continued");
185
+ }
186
+
187
+ resolution.addLine("}", opts.nestlevel, "continued");
188
+
189
+ out.visibilityMap = visMap.value;
190
+
191
+ if (
192
+ opts.parents &&
193
+ opts.parents.every(
194
+ (p) =>
195
+ p.kind !== "Model" || p.name === "Array" || p.name === "Record",
196
+ ) &&
197
+ visMap.value
198
+ ) {
199
+ // this model is nested in something that's not a model and has visibility; inline filters
200
+ out.resolved.append(`FilterLifecycle<${resolution}, ${visMap}, V>`);
201
+ // this model is the root type or nested in a model; do not inline visibility filter
202
+ } else {
203
+ out.resolved.append(resolution);
204
+ }
205
+ }
206
+ }
207
+
208
+ protected async typeguard(
209
+ opts: ResolverOptions<Resolver.Typeguard>,
210
+ out: ResolverResult<Resolver.Typeguard>,
211
+ ): Promise<void> {
212
+ out.resolved.append("\n");
213
+
214
+ if (this._t.baseModel) {
215
+ const resolvedBase = await this.resolveNested(
216
+ this._t.baseModel,
217
+ opts,
218
+ out,
219
+ );
220
+ out.resolved.append(
221
+ `${" ".repeat(opts.nestlevel)}${resolvedBase.resolved} &&`,
222
+ );
223
+ out.imports.push(...resolvedBase.imports);
224
+ // derived type does not have extended properties; fix by casting
225
+ opts.accessor = `(${opts.accessor} as any)`;
226
+ }
227
+
228
+ const props = this.getProps();
229
+ const propGuards: string[] = [];
230
+ for (const prop of props) {
231
+ const oldAccessor = opts.accessor;
232
+ opts.accessor = `${opts.accessor}['${prop.name}']`;
233
+ const resolved = await this.resolveNested(prop.type, opts, out);
234
+ opts.accessor = oldAccessor;
235
+ out.imports.push(...resolved.imports);
236
+
237
+ const visibility = this.getPropVisibilityType(opts.program, prop);
238
+
239
+ let guard = " ".repeat(
240
+ out.resolved.value.endsWith("&") ? 1 : opts.nestlevel * 2,
241
+ );
242
+ if (visibility !== null) {
243
+ guard += `((vis as any) !== Lifecycle.All && ![${(visibility ?? []).map((t) => `Lifecycle.${t}`).join(", ")}].includes(vis) ? !('${prop.name}' in ${opts.accessor}) : (`;
244
+ }
245
+ if (
246
+ prop.type.kind === "Intrinsic" &&
247
+ ["never", "void"].includes(prop.type.name)
248
+ ) {
249
+ // some special intrinsic types need special treatment
250
+ switch (prop.type.name) {
251
+ case "never":
252
+ guard += `!('${prop.name}' in ${opts.accessor})`;
253
+ break;
254
+ case "void":
255
+ guard += `${opts.accessor}['${prop.name}'] === undefined`;
256
+ break;
257
+ }
258
+ } else {
259
+ guard += prop.optional
260
+ ? `${opts.accessor}['${prop.name}'] === undefined || `
261
+ : `${opts.accessor}['${prop.name}'] !== undefined && `;
262
+ guard += `(${resolved.resolved}`;
263
+ guard += resolved.resolved.value.endsWith("\n")
264
+ ? " ".repeat(opts.nestlevel)
265
+ : "";
266
+ guard += ")";
267
+ }
268
+ if (visibility !== null) guard += "))";
269
+ propGuards.push(guard);
270
+ }
271
+ out.resolved.append(propGuards.join(" &&\n"));
272
+ out.resolved.addLine("", opts.nestlevel);
273
+ }
274
+
275
+ protected transformKnownType(
276
+ opts: ResolverOptions<Resolver>,
277
+ out: ResolverResult<Resolver>,
278
+ ): void {
279
+ // If this is a known type, it can't be the root of this resolution tree,
280
+ // because of the "originalTypeReady" flag.
281
+ // If it also has visibility, it must have its own visibility map,
282
+ // which is baked into the type definition, so it shouldn't be returned now.
283
+
284
+ if (this._r === Resolver.Type && out.hasVisibility) {
285
+ out.visibilityMap = "";
286
+ const newResolved = `${out.resolved}<V>`;
287
+ out.resolved.clear().append(newResolved);
288
+ // ret.visibilityMap = `${(t as any).name}_VisMap`;
289
+ }
290
+ }
291
+ }
@@ -0,0 +1,140 @@
1
+ import { Scalar } from "@typespec/compiler";
2
+ import { Resolvable } from "../Resolvable.js";
3
+ import {
4
+ Resolver,
5
+ ResolverOptions,
6
+ ResolverResult,
7
+ } from "../Resolvable_helpers.js";
8
+
9
+ export class ResolvableScalar extends Resolvable<Scalar> {
10
+ protected expectedTypeKind = "Scalar";
11
+ private dateTypes = [
12
+ "offsetDateTime",
13
+ "plainDate",
14
+ "utcDateTime",
15
+ "unixTimestamp32",
16
+ ];
17
+
18
+ protected async type(
19
+ opts: ResolverOptions<Resolver.Type>,
20
+ out: ResolverResult<Resolver.Type>,
21
+ ): Promise<void> {
22
+ if (this.dateTypes.includes(this._t.name)) {
23
+ switch (this._t.name) {
24
+ case "offsetDateTime":
25
+ case "plainDate":
26
+ case "utcDateTime":
27
+ out.resolved.append(
28
+ opts.options["serializable-date-types"] ? "string" : "Date",
29
+ );
30
+ break;
31
+ case "unixTimestamp32":
32
+ out.resolved.append(
33
+ opts.options["serializable-date-types"] ? "number" : "Date",
34
+ );
35
+ break;
36
+ }
37
+ } else if (this._t.baseScalar) {
38
+ out.resolved.append(
39
+ (await this.resolveNested(this._t.baseScalar, opts, out)).resolved,
40
+ );
41
+ } else {
42
+ switch (this._t.name) {
43
+ case "boolean":
44
+ out.resolved.append("boolean");
45
+ break;
46
+ case "bytes":
47
+ out.resolved.append("Uint8Array");
48
+ break;
49
+ case "duration":
50
+ out.resolved.append("number");
51
+ break;
52
+ case "numeric":
53
+ out.resolved.append("number");
54
+ break;
55
+ case "plainTime":
56
+ out.resolved.append("string");
57
+ break;
58
+ case "string":
59
+ out.resolved.append("string");
60
+ break;
61
+ case "url":
62
+ out.resolved.append("string");
63
+ break;
64
+
65
+ default:
66
+ this.diagnostic(
67
+ "resolve-unresolved-scalar",
68
+ `Could not resolve scalar ${this._t.name}`,
69
+ "error",
70
+ );
71
+ out.resolved.append("unknown");
72
+ break;
73
+ }
74
+ }
75
+ }
76
+
77
+ protected async typeguard(
78
+ opts: ResolverOptions<Resolver.Typeguard>,
79
+ out: ResolverResult<Resolver.Typeguard>,
80
+ ): Promise<void> {
81
+ if (this.dateTypes.includes(this._t.name)) {
82
+ switch (this._t.name) {
83
+ case "offsetDateTime":
84
+ case "plainDate":
85
+ case "utcDateTime":
86
+ out.resolved.append(
87
+ opts.options["serializable-date-types"]
88
+ ? `typeof ${opts.accessor} === 'string'`
89
+ : `${opts.accessor} instanceof Date`,
90
+ );
91
+ break;
92
+ case "unixTimestamp32":
93
+ out.resolved.append(
94
+ opts.options["serializable-date-types"]
95
+ ? `typeof ${opts.accessor} === 'number'`
96
+ : `${opts.accessor} instanceof Date`,
97
+ );
98
+ break;
99
+ }
100
+ } else if (this._t.baseScalar) {
101
+ out.resolved.append(
102
+ (await this.resolveNested(this._t.baseScalar, opts, out)).resolved,
103
+ );
104
+ } else if (this._t.name === "bytes") {
105
+ out.resolved.append(`${opts.accessor} instanceof Uint8Array`);
106
+ } else {
107
+ out.resolved.append(`typeof ${opts.accessor} === '`);
108
+ switch (this._t.name) {
109
+ case "boolean":
110
+ out.resolved.append("boolean");
111
+ break;
112
+ case "duration":
113
+ out.resolved.append("number");
114
+ break;
115
+ case "numeric":
116
+ out.resolved.append("number");
117
+ break;
118
+ case "plainTime":
119
+ out.resolved.append("string");
120
+ break;
121
+ case "string":
122
+ out.resolved.append("string");
123
+ break;
124
+ case "url":
125
+ out.resolved.append("string");
126
+ break;
127
+
128
+ default:
129
+ this.diagnostic(
130
+ "resolve-unresolved-scalar",
131
+ `Could not create typeguard for scalar ${this._t.name}`,
132
+ "error",
133
+ );
134
+ out.resolved.append("unknown");
135
+ break;
136
+ }
137
+ out.resolved.append("'");
138
+ }
139
+ }
140
+ }
@@ -0,0 +1,88 @@
1
+ import {
2
+ IntrinsicType,
3
+ NumericLiteral,
4
+ StringLiteral,
5
+ Type,
6
+ } from "@typespec/compiler";
7
+ import { Resolvable } from "../Resolvable.js";
8
+ import {
9
+ Resolver,
10
+ ResolverOptions,
11
+ ResolverResult,
12
+ } from "../Resolvable_helpers.js";
13
+
14
+ const allowedTypeKinds = [
15
+ "Boolean",
16
+ "Intrinsic",
17
+ "Number",
18
+ "String",
19
+ "TemplateParameter",
20
+ ] as const;
21
+
22
+ export class ResolvableSimple extends Resolvable<Type> {
23
+ protected expectedTypeKind = ""; // placeholder as required; validate is overridden
24
+ public static AllowedTypeKinds = allowedTypeKinds;
25
+
26
+ private static typeMap: Record<
27
+ (typeof allowedTypeKinds)[number],
28
+ (t: Type) => string
29
+ > = {
30
+ Boolean: (t) => ((t as any).value ? "true" : "false"),
31
+ Intrinsic: (t) => (t as IntrinsicType).name,
32
+ Number: (t) => (t as NumericLiteral).valueAsString,
33
+ String: (t) => `'${(t as StringLiteral).value}'`,
34
+ TemplateParameter: () => "unknown",
35
+ };
36
+
37
+ protected async type(
38
+ opts: ResolverOptions<Resolver.Type>,
39
+ out: ResolverResult<Resolver.Type>,
40
+ ): Promise<void> {
41
+ const mapped =
42
+ ResolvableSimple.typeMap[
43
+ this._t.kind as (typeof allowedTypeKinds)[number]
44
+ ]; // check is in validate()
45
+ out.resolved.append(mapped(this._t));
46
+ }
47
+
48
+ protected async typeguard(
49
+ opts: ResolverOptions<Resolver.Typeguard>,
50
+ out: ResolverResult<Resolver.Typeguard>,
51
+ ): Promise<void> {
52
+ const mapped =
53
+ ResolvableSimple.typeMap[
54
+ this._t.kind as (typeof allowedTypeKinds)[number]
55
+ ]; // check is in validate()
56
+ const v = mapped(this._t);
57
+
58
+ // some special intrinsic types are handled directly in the model property
59
+ switch (v) {
60
+ case "unknown":
61
+ out.resolved.append("true");
62
+ break;
63
+ case "never":
64
+ out.resolved.append("false");
65
+ break; // instantly fails all type checks
66
+ case "void":
67
+ out.resolved.append(`${opts.accessor} === undefined`);
68
+ break;
69
+
70
+ default:
71
+ out.resolved.append(`${opts.accessor} === ${v}`);
72
+ break;
73
+ }
74
+ }
75
+
76
+ protected validate(): void {
77
+ if (!ResolvableSimple.AllowedTypeKinds.includes(this._t.kind as any)) {
78
+ this.diagnostic(
79
+ "typeclass-mismatch",
80
+ `Tried resolving type ${this._t.kind} as primitive`,
81
+ "error",
82
+ );
83
+ throw new TypeError(
84
+ `Type-kind/class mismatch: tried resolving type ${this._t.kind} as primitive`,
85
+ );
86
+ }
87
+ }
88
+ }
@@ -0,0 +1,56 @@
1
+ import { Tuple } from "@typespec/compiler";
2
+ import { Resolvable } from "../Resolvable.js";
3
+ import {
4
+ Resolver,
5
+ ResolverOptions,
6
+ ResolverResult,
7
+ } from "../Resolvable_helpers.js";
8
+
9
+ export class ResolvableTuple extends Resolvable<Tuple> {
10
+ protected expectedTypeKind = "Tuple";
11
+
12
+ public override async hasVisibility(
13
+ opts: ResolverOptions<Resolver>,
14
+ out: ResolverResult<Resolver>,
15
+ ): Promise<boolean> {
16
+ if (await super.hasVisibility(opts, out)) return true;
17
+ for (const v of this._t.values) {
18
+ if (await Resolvable.hasVisibility(v, opts, out, this._t)) return true;
19
+ }
20
+ return false;
21
+ }
22
+
23
+ protected async type(
24
+ opts: ResolverOptions<Resolver.Type>,
25
+ out: ResolverResult<Resolver.Type>,
26
+ ): Promise<void> {
27
+ const results: string[] = [];
28
+ for (const v of this._t.values) {
29
+ const resolved = await this.resolveNested(v, opts, out);
30
+ out.imports.push(...resolved.imports);
31
+ results.push(resolved.resolved.value);
32
+ }
33
+ out.resolved.append(`[${results.join(", ")}]`);
34
+ }
35
+
36
+ protected async typeguard(
37
+ opts: ResolverOptions<Resolver.Typeguard>,
38
+ out: ResolverResult<Resolver.Typeguard>,
39
+ ): Promise<void> {
40
+ const results: string[] = [];
41
+ const oldAccessor = opts.accessor;
42
+ let i = 0;
43
+ for (const v of this._t.values) {
44
+ opts.accessor = `${opts.accessor}[${i}]`;
45
+ const resolved = await this.resolveNested(v, opts, out);
46
+ opts.accessor = oldAccessor;
47
+ out.imports.push(...resolved.imports);
48
+ results.push(resolved.resolved.value);
49
+ i++;
50
+ }
51
+ opts.accessor = oldAccessor;
52
+ out.resolved.append(
53
+ `Array.isArray(${opts.accessor}) && ${results.map((g) => `(${g})`).join(" && ")}`,
54
+ );
55
+ }
56
+ }