@typespec/emitter-framework 0.8.0-dev.2 → 0.8.0-dev.4

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 (47) hide show
  1. package/CHANGELOG.md +11 -0
  2. package/dist/src/typescript/components/enum-declaration.d.ts +1 -0
  3. package/dist/src/typescript/components/enum-declaration.d.ts.map +1 -1
  4. package/dist/src/typescript/components/enum-declaration.js +7 -0
  5. package/dist/src/typescript/components/function-declaration.d.ts.map +1 -1
  6. package/dist/src/typescript/components/function-declaration.js +8 -3
  7. package/dist/src/typescript/components/interface-declaration.d.ts.map +1 -1
  8. package/dist/src/typescript/components/interface-declaration.js +3 -0
  9. package/dist/src/typescript/components/interface-member.d.ts +3 -1
  10. package/dist/src/typescript/components/interface-member.d.ts.map +1 -1
  11. package/dist/src/typescript/components/interface-member.js +2 -0
  12. package/dist/src/typescript/components/interface-method.d.ts +3 -1
  13. package/dist/src/typescript/components/interface-method.d.ts.map +1 -1
  14. package/dist/src/typescript/components/interface-method.js +9 -2
  15. package/dist/src/typescript/components/type-alias-declaration.d.ts.map +1 -1
  16. package/dist/src/typescript/components/type-alias-declaration.js +5 -2
  17. package/dist/src/typescript/components/type-declaration.d.ts.map +1 -1
  18. package/dist/src/typescript/components/type-declaration.js +11 -1
  19. package/dist/src/typescript/components/union-declaration.d.ts +3 -1
  20. package/dist/src/typescript/components/union-declaration.d.ts.map +1 -1
  21. package/dist/src/typescript/components/union-declaration.js +4 -1
  22. package/dist/src/typescript/components/union-expression.d.ts.map +1 -1
  23. package/dist/src/typescript/components/union-expression.js +103 -10
  24. package/dist/src/typescript/utils/operation.d.ts +3 -1
  25. package/dist/src/typescript/utils/operation.d.ts.map +1 -1
  26. package/dist/src/typescript/utils/operation.js +10 -3
  27. package/dist/test/typescript/components/enum-declaration.test.js +79 -0
  28. package/dist/test/typescript/components/function-declaration.test.js +81 -0
  29. package/dist/test/typescript/components/interface-declaration.test.js +232 -9
  30. package/dist/test/typescript/components/type-alias-declaration.test.js +75 -0
  31. package/dist/test/typescript/components/union-declaration.test.js +358 -106
  32. package/package.json +6 -6
  33. package/src/typescript/components/enum-declaration.tsx +12 -1
  34. package/src/typescript/components/function-declaration.tsx +6 -1
  35. package/src/typescript/components/interface-declaration.tsx +3 -1
  36. package/src/typescript/components/interface-member.tsx +4 -0
  37. package/src/typescript/components/interface-method.tsx +7 -1
  38. package/src/typescript/components/type-alias-declaration.tsx +3 -2
  39. package/src/typescript/components/type-declaration.tsx +9 -7
  40. package/src/typescript/components/union-declaration.tsx +4 -1
  41. package/src/typescript/components/union-expression.tsx +100 -7
  42. package/src/typescript/utils/operation.ts +14 -3
  43. package/test/typescript/components/enum-declaration.test.tsx +72 -0
  44. package/test/typescript/components/function-declaration.test.tsx +78 -0
  45. package/test/typescript/components/interface-declaration.test.tsx +223 -9
  46. package/test/typescript/components/type-alias-declaration.test.tsx +72 -0
  47. package/test/typescript/components/union-declaration.test.tsx +330 -102
@@ -1,182 +1,410 @@
1
1
  import { render } from "@alloy-js/core";
2
+ import { d } from "@alloy-js/core/testing";
2
3
  import { SourceFile } from "@alloy-js/typescript";
3
- import { Namespace } from "@typespec/compiler";
4
- import { format } from "prettier";
5
- import { assert, describe, expect, it } from "vitest";
4
+ import { Enum, Model, Union } from "@typespec/compiler";
5
+ import { BasicTestRunner } from "@typespec/compiler/testing";
6
+ import { beforeEach, describe, it } from "vitest";
6
7
  import { Output } from "../../../src/core/components/output.jsx";
7
8
  import { UnionDeclaration } from "../../../src/typescript/components/union-declaration.js";
8
9
  import { UnionExpression } from "../../../src/typescript/components/union-expression.js";
9
- import { getProgram } from "../test-host.js";
10
+ import { InterfaceDeclaration } from "../../../src/typescript/index.js";
11
+ import { assertFileContents } from "../../utils.js";
12
+ import { createEmitterFrameworkTestRunner } from "../test-host.js";
10
13
 
11
14
  describe("Typescript Union Declaration", () => {
15
+ let runner: BasicTestRunner;
16
+
17
+ beforeEach(async () => {
18
+ runner = await createEmitterFrameworkTestRunner();
19
+ });
20
+
12
21
  describe("Union not bound to Typespec Types", () => {
13
22
  it("creates a union declaration", async () => {
14
- const program = await getProgram("");
23
+ await runner.compile(``);
15
24
  const res = render(
16
- <Output program={program}>
25
+ <Output program={runner.program}>
17
26
  <SourceFile path="test.ts">
18
27
  <UnionDeclaration name="MyUnion">"red" | "blue"</UnionDeclaration>
19
28
  </SourceFile>
20
29
  </Output>,
21
30
  );
22
31
 
23
- const testFile = res.contents.find((file) => file.path === "test.ts");
24
- assert(testFile, "test.ts file not rendered");
25
- const actualContent = await format(testFile.contents as string, { parser: "typescript" });
26
- const expectedContent = await format(`type MyUnion = "red" | "blue"`, {
27
- parser: "typescript",
28
- });
29
- expect(actualContent).toBe(expectedContent);
32
+ assertFileContents(
33
+ res,
34
+ d`
35
+ type MyUnion = "red" | "blue";
36
+ `,
37
+ );
30
38
  });
31
39
  });
32
40
 
33
41
  describe("Union bound to Typespec Types", () => {
34
42
  describe("Bound to Union", () => {
35
43
  it("creates a union declaration", async () => {
36
- const program = await getProgram(`
37
- namespace DemoService;
38
- union TestUnion {
39
- one: "one",
40
- two: "two"
41
- }
42
- `);
44
+ const { TestUnion } = (await runner.compile(`
45
+ namespace DemoService;
46
+ @test union TestUnion {
47
+ one: "one",
48
+ two: "two"
49
+ }
50
+ `)) as { TestUnion: Union };
43
51
 
44
- const [namespace] = program.resolveTypeReference("DemoService");
45
- const union = Array.from((namespace as Namespace).unions.values())[0];
52
+ const res = render(
53
+ <Output program={runner.program}>
54
+ <SourceFile path="test.ts">
55
+ <UnionDeclaration type={TestUnion} />
56
+ </SourceFile>
57
+ </Output>,
58
+ );
59
+
60
+ assertFileContents(
61
+ res,
62
+ d`
63
+ type TestUnion = "one" | "two";
64
+ `,
65
+ );
66
+ });
67
+
68
+ it("creates a union declaration with JSDoc", async () => {
69
+ const { TestUnion } = (await runner.compile(`
70
+ namespace DemoService;
71
+ /**
72
+ * Test Union
73
+ */
74
+ @test union TestUnion {
75
+ one: "one",
76
+ two: "two"
77
+ }
78
+ `)) as { TestUnion: Union };
46
79
 
47
80
  const res = render(
48
- <Output program={program}>
81
+ <Output program={runner.program}>
49
82
  <SourceFile path="test.ts">
50
- <UnionDeclaration type={union} />
83
+ <UnionDeclaration type={TestUnion} />
51
84
  </SourceFile>
52
85
  </Output>,
53
86
  );
54
87
 
55
- const testFile = res.contents.find((file) => file.path === "test.ts");
56
- assert(testFile, "test.ts file not rendered");
57
- const actualContent = await format(testFile.contents as string, { parser: "typescript" });
58
- const expectedContent = await format(`type TestUnion = "one" | "two"`, {
59
- parser: "typescript",
60
- });
61
- expect(actualContent).toBe(expectedContent);
88
+ assertFileContents(
89
+ res,
90
+ d`
91
+ /**
92
+ * Test Union
93
+ */
94
+ type TestUnion = "one" | "two";
95
+ `,
96
+ );
62
97
  });
63
98
 
64
99
  it("creates a union declaration with name override", async () => {
65
- const program = await getProgram(`
66
- namespace DemoService;
67
- union TestUnion {
68
- one: "one",
69
- two: "two"
70
- }
71
- `);
72
-
73
- const [namespace] = program.resolveTypeReference("DemoService");
74
- const union = Array.from((namespace as Namespace).unions.values())[0];
100
+ const { TestUnion } = (await runner.compile(`
101
+ namespace DemoService;
102
+ @test union TestUnion {
103
+ one: "one",
104
+ two: "two"
105
+ }
106
+ `)) as { TestUnion: Union };
75
107
 
76
108
  const res = render(
77
- <Output program={program}>
109
+ <Output program={runner.program}>
78
110
  <SourceFile path="test.ts">
79
- <UnionDeclaration export type={union} name="MyUnion" />
111
+ <UnionDeclaration export type={TestUnion} name="MyUnion" />
80
112
  </SourceFile>
81
113
  </Output>,
82
114
  );
83
115
 
84
- const testFile = res.contents.find((file) => file.path === "test.ts");
85
- assert(testFile, "test.ts file not rendered");
86
- const actualContent = await format(testFile.contents as string, { parser: "typescript" });
87
- const expectedContent = await format(`export type MyUnion = "one" | "two"`, {
88
- parser: "typescript",
89
- });
90
- expect(actualContent).toBe(expectedContent);
116
+ assertFileContents(
117
+ res,
118
+ d`
119
+ export type MyUnion = "one" | "two";
120
+ `,
121
+ );
91
122
  });
92
123
 
93
124
  it("creates a union declaration with extra children", async () => {
94
- const program = await getProgram(`
95
- namespace DemoService;
96
- union TestUnion {
97
- one: "one",
98
- two: "two"
99
- }
100
- `);
101
-
102
- const [namespace] = program.resolveTypeReference("DemoService");
103
- const union = Array.from((namespace as Namespace).unions.values())[0];
125
+ const { TestUnion } = (await runner.compile(`
126
+ namespace DemoService;
127
+ @test union TestUnion {
128
+ one: "one",
129
+ two: "two"
130
+ }
131
+ `)) as { TestUnion: Union };
104
132
 
105
133
  const res = render(
106
- <Output program={program}>
134
+ <Output program={runner.program}>
107
135
  <SourceFile path="test.ts">
108
- <UnionDeclaration type={union}>"three"</UnionDeclaration>
136
+ <UnionDeclaration type={TestUnion}>"three"</UnionDeclaration>
109
137
  </SourceFile>
110
138
  </Output>,
111
139
  );
112
140
 
113
- const testFile = res.contents.find((file) => file.path === "test.ts");
114
- assert(testFile, "test.ts file not rendered");
115
- const actualContent = await format(testFile.contents as string, { parser: "typescript" });
116
- const expectedContent = await format(`type TestUnion = "one" | "two" | "three"`, {
117
- parser: "typescript",
118
- });
119
- expect(actualContent).toBe(expectedContent);
141
+ assertFileContents(
142
+ res,
143
+ d`
144
+ type TestUnion = "one" | "two" | "three";
145
+ `,
146
+ );
120
147
  });
121
148
 
122
- it("renders an union expression", async () => {
123
- const program = await getProgram(`
149
+ it("renders a union expression", async () => {
150
+ const { TestUnion } = (await runner.compile(`
124
151
  namespace DemoService;
125
- union TestUnion {
152
+ @test union TestUnion {
126
153
  one: "one",
127
154
  two: "two"
128
155
  }
129
- `);
156
+ `)) as { TestUnion: Union };
157
+
158
+ const res = render(
159
+ <Output program={runner.program}>
160
+ <SourceFile path="test.ts">
161
+ let x: <UnionExpression type={TestUnion} /> = "one";
162
+ </SourceFile>
163
+ </Output>,
164
+ );
165
+
166
+ assertFileContents(
167
+ res,
168
+ d`
169
+ let x: "one" | "two" = "one";
170
+ `,
171
+ );
172
+ });
130
173
 
131
- const [namespace] = program.resolveTypeReference("DemoService");
132
- const union = Array.from((namespace as Namespace).unions.values())[0];
174
+ describe("Discriminated Union", () => {
175
+ it("renders a discriminated union", async () => {
176
+ const { TestUnion } = (await runner.compile(`
177
+ namespace DemoService;
178
+ @discriminated
179
+ @test union TestUnion {
180
+ one: { oneItem: true },
181
+ two: true
182
+ }
183
+ `)) as { TestUnion: Union };
184
+
185
+ const res = render(
186
+ <Output program={runner.program}>
187
+ <SourceFile path="test.ts">
188
+ <UnionDeclaration type={TestUnion} />
189
+ </SourceFile>
190
+ </Output>,
191
+ );
192
+
193
+ assertFileContents(
194
+ res,
195
+ d`
196
+ type TestUnion = {
197
+ kind: "one";
198
+ value: {
199
+ oneItem: true;
200
+ };
201
+ } | {
202
+ kind: "two";
203
+ value: true;
204
+ };
205
+ `,
206
+ );
207
+ });
208
+ });
209
+
210
+ it("renders a discriminated union with custom properties", async () => {
211
+ const { TestUnion } = (await runner.compile(`
212
+ namespace DemoService;
213
+ @discriminated(#{ discriminatorPropertyName: "dataKind", envelopePropertyName: "data" })
214
+ @test union TestUnion {
215
+ one: { oneItem: true },
216
+ two: true
217
+ }
218
+ `)) as { TestUnion: Union };
133
219
 
134
220
  const res = render(
135
- <Output program={program}>
221
+ <Output program={runner.program}>
136
222
  <SourceFile path="test.ts">
137
- let x: <UnionExpression type={union} /> = "one";
223
+ <UnionDeclaration type={TestUnion} />
138
224
  </SourceFile>
139
225
  </Output>,
140
226
  );
227
+ assertFileContents(
228
+ res,
229
+ d`
230
+ type TestUnion = {
231
+ dataKind: "one";
232
+ data: {
233
+ oneItem: true;
234
+ };
235
+ } | {
236
+ dataKind: "two";
237
+ data: true;
238
+ };
239
+ `,
240
+ );
241
+ });
242
+
243
+ it("renders a discriminated union with named models", async () => {
244
+ const { Pet, Cat, Dog } = (await runner.compile(`
245
+ namespace DemoService;
246
+ @test model Cat {
247
+ name: string;
248
+ meow: boolean;
249
+ }
250
+
251
+ @test model Dog {
252
+ name: string;
253
+ bark: boolean;
254
+ }
141
255
 
142
- const testFile = res.contents.find((file) => file.path === "test.ts");
143
- assert(testFile, "test.ts file not rendered");
144
- const actualContent = await format(testFile.contents as string, { parser: "typescript" });
145
- const expectedContent = await format(`let x:"one" | "two" = "one"`, {
146
- parser: "typescript",
256
+ @discriminated
257
+ @test union Pet {
258
+ cat: Cat,
259
+ dog: Dog,
260
+ }
261
+ `)) as { Pet: Union; Cat: Model; Dog: Model };
262
+
263
+ const res = render(
264
+ <Output program={runner.program}>
265
+ <SourceFile path="test.ts">
266
+ <InterfaceDeclaration type={Cat} />
267
+ <hbr />
268
+ <InterfaceDeclaration type={Dog} />
269
+ <hbr />
270
+ <UnionDeclaration type={Pet} />
271
+ </SourceFile>
272
+ </Output>,
273
+ );
274
+ assertFileContents(
275
+ res,
276
+ d`
277
+ interface Cat {
278
+ name: string;
279
+ meow: boolean;
280
+ }
281
+ interface Dog {
282
+ name: string;
283
+ bark: boolean;
284
+ }
285
+ type Pet = {
286
+ kind: "cat";
287
+ value: Cat;
288
+ } | {
289
+ kind: "dog";
290
+ value: Dog;
291
+ };
292
+ `,
293
+ );
294
+ });
295
+
296
+ describe("Discriminated Union with no envelope", () => {
297
+ it("renders named discriminated union", async () => {
298
+ const { Pet, Cat, Dog } = (await runner.compile(`
299
+ namespace DemoService;
300
+
301
+ @test model Cat {
302
+ name: string;
303
+ meow: boolean;
304
+ }
305
+
306
+ @test model Dog {
307
+ name: string;
308
+ bark: boolean;
309
+ }
310
+
311
+ @discriminated(#{ envelope: "none", discriminatorPropertyName: "dataKind" })
312
+ @test union Pet {
313
+ cat: Cat,
314
+ dog: Dog,
315
+ }
316
+ `)) as { Pet: Union; Cat: Model; Dog: Model };
317
+
318
+ const res = render(
319
+ <Output program={runner.program}>
320
+ <SourceFile path="test.ts">
321
+ <InterfaceDeclaration type={Cat} />
322
+ <hbr />
323
+ <InterfaceDeclaration type={Dog} />
324
+ <hbr />
325
+ <UnionDeclaration type={Pet} />
326
+ </SourceFile>
327
+ </Output>,
328
+ );
329
+
330
+ assertFileContents(
331
+ res,
332
+ d`
333
+ interface Cat {
334
+ name: string;
335
+ meow: boolean;
336
+ }
337
+ interface Dog {
338
+ name: string;
339
+ bark: boolean;
340
+ }
341
+ type Pet = {
342
+ dataKind: "cat"
343
+ } & Cat | {
344
+ dataKind: "dog"
345
+ } & Dog;
346
+ `,
347
+ );
348
+ });
349
+
350
+ it("renders anonymous discriminated union", async () => {
351
+ const { TestUnion } = (await runner.compile(`
352
+ namespace DemoService;
353
+ @discriminated(#{ envelope: "none", discriminatorPropertyName: "dataKind" })
354
+ @test union TestUnion {
355
+ one: { oneItem: true },
356
+ two: { secondItem: false }
357
+ }
358
+ `)) as { TestUnion: Union };
359
+
360
+ const res = render(
361
+ <Output program={runner.program}>
362
+ <SourceFile path="test.ts">
363
+ <UnionDeclaration type={TestUnion} />
364
+ </SourceFile>
365
+ </Output>,
366
+ );
367
+
368
+ assertFileContents(
369
+ res,
370
+ d`
371
+ type TestUnion = {
372
+ dataKind: "one";
373
+ oneItem: true;
374
+ } | {
375
+ dataKind: "two";
376
+ secondItem: false;
377
+ };
378
+ `,
379
+ );
147
380
  });
148
- expect(actualContent).toBe(expectedContent);
149
381
  });
150
382
  });
151
383
 
152
384
  describe("Bound to Enum", () => {
153
385
  it("creates a union declaration", async () => {
154
- const program = await getProgram(`
155
- namespace DemoService;
156
- enum TestEnum {
157
- one: "one",
158
- two: "two"
159
- }
160
- `);
161
-
162
- const [namespace] = program.resolveTypeReference("DemoService");
163
- const union = Array.from((namespace as Namespace).enums.values())[0];
386
+ const { TestEnum } = (await runner.compile(`
387
+ namespace DemoService;
388
+ @test enum TestEnum {
389
+ one: "one",
390
+ two: "two"
391
+ }
392
+ `)) as { TestEnum: Enum };
164
393
 
165
394
  const res = render(
166
- <Output program={program}>
395
+ <Output program={runner.program}>
167
396
  <SourceFile path="test.ts">
168
- <UnionDeclaration type={union} />
397
+ <UnionDeclaration type={TestEnum} />
169
398
  </SourceFile>
170
399
  </Output>,
171
400
  );
172
401
 
173
- const testFile = res.contents.find((file) => file.path === "test.ts");
174
- assert(testFile, "test.ts file not rendered");
175
- const actualContent = await format(testFile.contents as string, { parser: "typescript" });
176
- const expectedContent = await format(`type TestEnum = "one" | "two"`, {
177
- parser: "typescript",
178
- });
179
- expect(actualContent).toBe(expectedContent);
402
+ assertFileContents(
403
+ res,
404
+ d`
405
+ type TestEnum = "one" | "two";
406
+ `,
407
+ );
180
408
  });
181
409
  });
182
410
  });