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,52 @@
1
+ import { getDoc, Union } 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 ResolvableUnion extends Resolvable<Union> {
10
+ protected expectedTypeKind = "Union";
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 Array.from(this._t.variants)) {
18
+ if (await Resolvable.hasVisibility(v[1].type, opts, out, this._t))
19
+ return true;
20
+ }
21
+ return false;
22
+ }
23
+
24
+ protected async type(
25
+ opts: ResolverOptions<Resolver.Type>,
26
+ out: ResolverResult<Resolver.Type>,
27
+ ): Promise<void> {
28
+ if (opts.emitDocs) {
29
+ out.doc = getDoc(opts.program, this._t);
30
+ }
31
+ const results: string[] = [];
32
+ for (const v of Array.from(this._t.variants)) {
33
+ const resolved = await this.resolveNested(v[1].type, opts, out, false);
34
+ out.imports.push(...resolved.imports);
35
+ results.push(resolved.resolved.value);
36
+ }
37
+ out.resolved.append(results.join(" | "));
38
+ }
39
+
40
+ protected async typeguard(
41
+ opts: ResolverOptions<Resolver.Typeguard>,
42
+ out: ResolverResult<Resolver.Typeguard>,
43
+ ): Promise<void> {
44
+ const results: string[] = [];
45
+ for (const v of Array.from(this._t.variants)) {
46
+ const resolved = await this.resolveNested(v[1].type, opts, out);
47
+ out.imports.push(...resolved.imports);
48
+ results.push(`(${resolved.resolved.value})`);
49
+ }
50
+ out.resolved.append(results.join(" || "));
51
+ }
52
+ }
@@ -0,0 +1,51 @@
1
+ import "@typespec/http";
2
+
3
+ using TypeSpec.Http;
4
+
5
+ @service()
6
+ namespace test {
7
+ enum AdminLevel {
8
+ Level1,
9
+ Level2
10
+ }
11
+
12
+ scalar Name extends string;
13
+ alias Timestamp = unixTimestamp32;
14
+
15
+ model Resource {
16
+ id_onlyRead: uint32,
17
+ name: Name,
18
+ metadata: {
19
+ created_onlyRead: Timestamp,
20
+ user: uint32,
21
+ username: string,
22
+ adminLevel?: AdminLevel | null
23
+ }
24
+ }
25
+
26
+ /** Retrieves an instance of the ressource */
27
+ @get
28
+ op getResource(): {
29
+ @statusCode status: 200,
30
+ @body body: Resource
31
+ };
32
+
33
+ /** Creates a resource */
34
+ @put
35
+ op postResource(@body body: Resource | inner.InnerModel): OkResponse;
36
+
37
+ @route("/inner")
38
+ namespace inner {
39
+ model InnerModel {
40
+ resourceName: string,
41
+ resource: test.Resource
42
+ }
43
+
44
+ model InnerModel2 {
45
+ level: AdminLevel
46
+ }
47
+
48
+ @delete
49
+ op del(@body body: InnerModel | InnerModel2): OkResponse | UnauthorizedResponse;
50
+ }
51
+ }
@@ -0,0 +1,53 @@
1
+ import "@typespec/http";
2
+
3
+ using TypeSpec.Http;
4
+
5
+ @service()
6
+ namespace test {
7
+ enum AdminLevel {
8
+ Level1,
9
+ Level2
10
+ }
11
+
12
+ scalar Name extends string;
13
+ alias Timestamp = unixTimestamp32;
14
+
15
+ model Resource {
16
+ @visibility(Lifecycle.Read)
17
+ id_onlyRead: uint32,
18
+ name: Name,
19
+ metadata: {
20
+ @visibility
21
+ created_onlyRead: Timestamp,
22
+ user: uint32,
23
+ username: string,
24
+ adminLevel?: AdminLevel | null
25
+ }
26
+ }
27
+
28
+ /** Retrieves an instance of the ressource */
29
+ @get
30
+ op getResource(): {
31
+ @statusCode status: 200,
32
+ @body body: Resource
33
+ };
34
+
35
+ /** Creates a resource */
36
+ @put
37
+ op postResource(@body body: Resource | inner.InnerModel): OkResponse;
38
+
39
+ @route("/inner")
40
+ namespace inner {
41
+ model InnerModel {
42
+ resourceName: string,
43
+ resource: test.Resource
44
+ }
45
+
46
+ model InnerModel2 {
47
+ level: AdminLevel
48
+ }
49
+
50
+ @delete
51
+ op del(@body body: InnerModel | InnerModel2): OkResponse | UnauthorizedResponse;
52
+ }
53
+ }
@@ -0,0 +1,40 @@
1
+ scalar dateType extends unixTimestamp32;
2
+ alias aliasedString = string;
3
+ alias aliasedModel = {str: string, fl: float, nested: {int: int32}};
4
+
5
+ model test {
6
+ @visibility(Lifecycle.Read)
7
+ id_onlyRead: uint64,
8
+ name: string,
9
+ aliasedName?: aliasedString,
10
+ date: utcDateTime,
11
+ time: plainTime,
12
+ @visibility(Lifecycle.Create)
13
+ created_onlyCreate: unixTimestamp32,
14
+ @visibility(Lifecycle.Read, Lifecycle.Query)
15
+ tuple_strIntModel_readQuery: [string, int16, {inmodel_union: null | unknown}],
16
+ nested?: {
17
+ availability: boolean
18
+ },
19
+ array: string[],
20
+ array_union: (string | int16)[],
21
+ record: Record<string>,
22
+ record_union: Record<string | int16>,
23
+ @visibility(Lifecycle.Delete, Lifecycle.Query)
24
+ nested_deleteQuery: {
25
+ @visibility(Lifecycle.Query)
26
+ str_onlyQuery: string,
27
+ aliasedModel: aliasedModel,
28
+ modelWithVis: {
29
+ a: "a",
30
+ @visibility(Lifecycle.Delete)
31
+ seven_onlyDelete: 7
32
+ }
33
+ },
34
+ nestedInnerVis: {
35
+ unk?: unknown,
36
+ @visibility(Lifecycle.Update)
37
+ void_onlyUpdate: void
38
+ },
39
+ tupleWithUnion: [string, int32 | boolean]
40
+ }
@@ -2,4 +2,4 @@ import { createTester } from "@typespec/compiler/testing";
2
2
 
3
3
  export const runner = createTester(".", {
4
4
  libraries: ["@typespec/http", "typespec-typescript-emitter"], // Add other libraries you depend on in your tests
5
- });
5
+ }).importLibraries();
@@ -0,0 +1,11 @@
1
+ import ts from "typescript";
2
+
3
+ export const validateTS = (code: string): true | string => {
4
+ const out = ts.transpileModule(code, {
5
+ reportDiagnostics: true,
6
+ compilerOptions: { noEmit: true },
7
+ });
8
+ return out.diagnostics === undefined || out.diagnostics.length === 0
9
+ ? true
10
+ : out.diagnostics.map((d) => d.messageText).join(", ");
11
+ };
@@ -0,0 +1,144 @@
1
+ import { Type } from "@typespec/compiler";
2
+ import { resolveVirtualPath } from "@typespec/compiler/testing";
3
+ import { expect, it } from "vitest";
4
+ import { EmitterOptions } from "../../src/lib";
5
+ import { Resolvable } from "../../src/resolve/Resolvable";
6
+ import { Resolver, ResolverResult } from "../../src/resolve/Resolvable_helpers";
7
+ import { runner } from "./runner";
8
+ import { validateTS } from "./ts";
9
+
10
+ export const defaultConfig: Omit<EmitterOptions, "out-dir"> = {
11
+ "root-namespaces": ["test"],
12
+ "enable-types": true,
13
+ "enable-typeguards": false,
14
+ "enable-routes": false,
15
+ "enable-routed-typemap": false,
16
+ "string-nominal-enums": true,
17
+ "serializable-date-types": true,
18
+ };
19
+
20
+ type Filename = string;
21
+ export const expectEmit = <T extends string | Record<Filename, string>>(
22
+ desc: string,
23
+ input: T,
24
+ target: T,
25
+ config?: Partial<typeof defaultConfig>,
26
+ outFilename?: T extends string ? string : never,
27
+ ): void => {
28
+ it(desc, async () => {
29
+ if (!outFilename) outFilename = "test.ts" as any;
30
+ const emitter = await runner.emit("typespec-typescript-emitter", {
31
+ ...structuredClone(defaultConfig),
32
+ ...(config ?? {}),
33
+ });
34
+ const instance = await emitter.createInstance();
35
+ const result = await instance.compileAndDiagnose(input);
36
+
37
+ // check for diagnostics
38
+ if (result[1].length > 0) console.error(result[1]);
39
+ expect(result[1].length).toBe(0);
40
+
41
+ // check all files against target
42
+ if (typeof target === "string") {
43
+ if (!result[0].outputs[outFilename!])
44
+ console.error(
45
+ `${outFilename!} not found in ${Object.keys(result[0].outputs)}`,
46
+ );
47
+ expect(result[0].outputs[outFilename!].replaceAll("\r", "").trim()).toBe(
48
+ target.replaceAll("\r", "").trim(),
49
+ );
50
+ } else {
51
+ for (const t of Object.entries(target)) {
52
+ expect(
53
+ (
54
+ await result[0].program.host.readFile(
55
+ resolveVirtualPath(t[0]).replaceAll("\r", "").trim(),
56
+ )
57
+ ).text.trim(),
58
+ ).toBe(t[1].replaceAll("\r", "").trim());
59
+ }
60
+ }
61
+ });
62
+ };
63
+
64
+ export const expectResolution = (
65
+ r: Resolver,
66
+ args: {
67
+ /** Kind of the type we expect to get (@typespec/compiler.Type['kind']) */
68
+ type: string;
69
+ /** Description of the test, only used for test output */
70
+ desc?: string;
71
+ /** Source TSP */
72
+ source: string;
73
+ /** Expected TS output */
74
+ target: string;
75
+ /** Either `true` (all okay) or error message */
76
+ test?: (t: Type, r: ResolverResult<Resolver.Type>) => true | string;
77
+ /** Name of the type to compile; used to get type from program. */
78
+ typename?: string;
79
+ /** Partion EmitterOptions config object */
80
+ config?: Partial<typeof defaultConfig>;
81
+ /**
82
+ * If set, the TSP output will be put through this before checking typescript validity.
83
+ * Default is `type test = ${output}`.
84
+ * If set to null, typescript syntax will not be checked.
85
+ */
86
+ typescriptTransformer?: ((tsp: string) => string) | null;
87
+ /** If set, does not check if resolution output is "truthy" */
88
+ noTruthyCheck?: boolean;
89
+ },
90
+ ) => {
91
+ if (!args.typename) args.typename = "test";
92
+ if (!args.config) {
93
+ args.config = defaultConfig;
94
+ } else {
95
+ args.config = { ...defaultConfig, ...args.config };
96
+ }
97
+
98
+ /** Removes all leading and trailing whitespace on each line. */
99
+ const transformResult = (s: string): string =>
100
+ s
101
+ .split("\n")
102
+ .map((l) => l.trim())
103
+ .join("");
104
+ if (args.typescriptTransformer === undefined)
105
+ args.typescriptTransformer = (tsp) => `type test = ${tsp};`;
106
+
107
+ it(`${r === Resolver.Type ? "type" : "typeguard"}: ${args.type} ${args.desc ?? ""}`, async () => {
108
+ const { program } = await runner.compile(
109
+ args.source,
110
+ args.config as Record<string, any>,
111
+ );
112
+ // get compiled type
113
+ const result = program.resolveTypeReference(args.typename!);
114
+ // check for diagnostics
115
+ if (result[1].length > 0) console.error(result[1]);
116
+ expect(result[1].length).toBe(0); // no diagnostics
117
+ // type compiled and expected type.kind?
118
+ expect(result[0]).toBeDefined();
119
+ expect(result[0]?.kind).toBe(args.type);
120
+ // resolve to typescript
121
+ const resolved = await Resolvable.resolve(r, result[0]!, {
122
+ program,
123
+ options: args.config as EmitterOptions,
124
+ nestlevel: 0,
125
+ rootType: null,
126
+ typemap: [],
127
+ emitDocs: false,
128
+ });
129
+ // check if valid typescript
130
+ if (args.typescriptTransformer !== null) {
131
+ const tsCode = args.typescriptTransformer!(resolved.resolved.value);
132
+ let tsValidity = validateTS(tsCode);
133
+ if (tsValidity !== true) tsValidity += `\nTypescript: ${tsCode}`;
134
+ expect(tsValidity).toBe(true);
135
+ }
136
+ if (!args.noTruthyCheck) expect(resolved.resolved.value).toBeTruthy();
137
+ // check generated typescript content
138
+ expect(transformResult(resolved.resolved.value)).toBe(
139
+ transformResult(transformResult(args.target)),
140
+ );
141
+ if (args.test !== undefined)
142
+ expect(await args.test(result[0]!, resolved)).toBe(true);
143
+ });
144
+ };
@@ -0,0 +1,35 @@
1
+
2
+
3
+
4
+ /*
5
+ * This file is automatically generated by typespec-typescript-emitter.
6
+ *
7
+ * You should not change or manipulate this file, as it will be overwritten.
8
+ * Instead, change the underlying spec.
9
+ */
10
+
11
+
12
+ export const routes_test = {
13
+ /** Retrieves an instance of the ressource */
14
+ getResource: {
15
+ verb: 'GET',
16
+ path: '/',
17
+ getUrl: (): string => `/`,
18
+ auth: [null]
19
+ },
20
+ /** Creates a resource */
21
+ postResource: {
22
+ verb: 'PUT',
23
+ path: '/',
24
+ getUrl: (): string => `/`,
25
+ auth: [null]
26
+ },
27
+ inner: {
28
+ del: {
29
+ verb: 'DELETE',
30
+ path: '/inner',
31
+ getUrl: (): string => `/inner`,
32
+ auth: [null]
33
+ }
34
+ }
35
+ } as const;
@@ -0,0 +1,22 @@
1
+ import { readFileSync } from "fs";
2
+ import { join } from "path";
3
+ import { expectEmit } from "../helpers/wrapper";
4
+
5
+ const files: Record<string, string> = {
6
+ ["main.tsp"]: readFileSync(
7
+ join(__dirname, "..", "helpers", "integrationTest.tsp"),
8
+ "utf8",
9
+ ),
10
+ };
11
+
12
+ expectEmit(
13
+ "route object",
14
+ files,
15
+ {
16
+ "routes_test.ts": `/* eslint-disable */${readFileSync(
17
+ join(__dirname, "routes.target.ts"),
18
+ "utf8",
19
+ )}`,
20
+ },
21
+ { "enable-routes": true },
22
+ );
@@ -0,0 +1,78 @@
1
+ import { readFileSync } from "fs";
2
+ import { join } from "path";
3
+ import { Resolver } from "../../src/resolve/Resolvable_helpers";
4
+ import { expectResolution } from "../helpers/wrapper";
5
+
6
+ const input = readFileSync(
7
+ join(__dirname, "..", "helpers", "largeModel.tsp"),
8
+ "utf8",
9
+ );
10
+
11
+ const target = `
12
+ ((vis as any) !== Lifecycle.All && ![Lifecycle.Read].includes(vis) ? !('id_onlyRead' in undefined) : (undefined['id_onlyRead'] !== undefined && (typeof undefined['id_onlyRead'] === 'number'))) &&
13
+ undefined['name'] !== undefined && (typeof undefined['name'] === 'string') &&
14
+ undefined['aliasedName'] === undefined || (typeof undefined['aliasedName'] === 'string') &&
15
+ undefined['date'] !== undefined && (undefined['date'] instanceof Date) &&
16
+ undefined['time'] !== undefined && (typeof undefined['time'] === 'string') &&
17
+ ((vis as any) !== Lifecycle.All && ![Lifecycle.Create].includes(vis) ? !('created_onlyCreate' in undefined) : (
18
+ undefined['created_onlyCreate'] !== undefined && (undefined['created_onlyCreate'] instanceof Date)
19
+ )) &&
20
+ ((vis as any) !== Lifecycle.All && ![Lifecycle.Read, Lifecycle.Query].includes(vis) ? !('tuple_strIntModel_readQuery' in undefined) : (
21
+ undefined['tuple_strIntModel_readQuery'] !== undefined && (
22
+ Array.isArray(undefined['tuple_strIntModel_readQuery']) && (
23
+ typeof undefined['tuple_strIntModel_readQuery'][0] === 'string'
24
+ ) && (
25
+ typeof undefined['tuple_strIntModel_readQuery'][1] === 'number'
26
+ ) && (
27
+ undefined['tuple_strIntModel_readQuery'][2]['inmodel_union'] !== undefined && (
28
+ (undefined['tuple_strIntModel_readQuery'][2]['inmodel_union'] === null) || (true)
29
+ )
30
+ )
31
+ )
32
+ )) &&
33
+ undefined['nested'] === undefined || (undefined['nested']['availability'] !== undefined && (typeof undefined['nested']['availability'] === 'boolean')) &&
34
+ undefined['array'] !== undefined && (Array.isArray(undefined['array']) && undefined['array'].every((v) => typeof v === 'string')) &&
35
+ undefined['array_union'] !== undefined && (Array.isArray(undefined['array_union']) && undefined['array_union'].every((v) => (typeof v === 'string') || (typeof v === 'number'))) &&
36
+ undefined['record'] !== undefined && (typeof undefined['record'] === 'object' && Object.entries(undefined['record'] as Record<string, any>).every((v) => typeof v[1] === 'string')) &&
37
+ undefined['record_union'] !== undefined && (typeof undefined['record_union'] === 'object' && Object.entries(undefined['record_union'] as Record<string, any>).every((v) => (typeof v[1] === 'string') || (typeof v[1] === 'number'))) &&
38
+ ((vis as any) !== Lifecycle.All && ![Lifecycle.Delete, Lifecycle.Query].includes(vis) ? !('nested_deleteQuery' in undefined) : (
39
+ undefined['nested_deleteQuery'] !== undefined && (
40
+ ((vis as any) !== Lifecycle.All && ![Lifecycle.Query].includes(vis) ? !('str_onlyQuery' in undefined['nested_deleteQuery']) : (
41
+ undefined['nested_deleteQuery']['str_onlyQuery'] !== undefined && (typeof undefined['nested_deleteQuery']['str_onlyQuery'] === 'string')
42
+ )) &&
43
+ undefined['nested_deleteQuery']['aliasedModel'] !== undefined && (
44
+ undefined['nested_deleteQuery']['aliasedModel']['str'] !== undefined && (typeof undefined['nested_deleteQuery']['aliasedModel']['str'] === 'string') &&
45
+ undefined['nested_deleteQuery']['aliasedModel']['fl'] !== undefined && (typeof undefined['nested_deleteQuery']['aliasedModel']['fl'] === 'number') &&
46
+ undefined['nested_deleteQuery']['aliasedModel']['nested'] !== undefined && (
47
+ undefined['nested_deleteQuery']['aliasedModel']['nested']['int'] !== undefined && (typeof undefined['nested_deleteQuery']['aliasedModel']['nested']['int'] === 'number')
48
+ )
49
+ ) &&
50
+ undefined['nested_deleteQuery']['modelWithVis'] !== undefined && (
51
+ undefined['nested_deleteQuery']['modelWithVis']['a'] !== undefined && (undefined['nested_deleteQuery']['modelWithVis']['a'] === 'a') &&
52
+ ((vis as any) !== Lifecycle.All && ![Lifecycle.Delete].includes(vis) ? !('seven_onlyDelete' in undefined['nested_deleteQuery']['modelWithVis']) : (
53
+ undefined['nested_deleteQuery']['modelWithVis']['seven_onlyDelete'] !== undefined && (undefined['nested_deleteQuery']['modelWithVis']['seven_onlyDelete'] === 7)
54
+ ))
55
+ )
56
+ )
57
+ )) &&
58
+ undefined['nestedInnerVis'] !== undefined && (
59
+ undefined['nestedInnerVis']['unk'] === undefined || (true) &&
60
+ ((vis as any) !== Lifecycle.All && ![Lifecycle.Update].includes(vis) ? !('void_onlyUpdate' in undefined['nestedInnerVis']) : (
61
+ undefined['nestedInnerVis']['void_onlyUpdate'] === undefined
62
+ ))
63
+ ) &&
64
+ undefined['tupleWithUnion'] !== undefined && (
65
+ Array.isArray(undefined['tupleWithUnion']) && (typeof undefined['tupleWithUnion'][0] === 'string') && ((typeof undefined['tupleWithUnion'][1] === 'number') || (typeof undefined['tupleWithUnion'][1] === 'boolean'))
66
+ )
67
+ `;
68
+
69
+ expectResolution(Resolver.Typeguard, {
70
+ type: "Model",
71
+ desc: "combined",
72
+ source: input,
73
+ target: target,
74
+ config: {
75
+ "serializable-date-types": false,
76
+ },
77
+ typescriptTransformer: null,
78
+ });
@@ -0,0 +1,10 @@
1
+ import { Resolver } from "../../src/resolve/Resolvable_helpers";
2
+ import { expectResolution } from "../helpers/wrapper";
3
+
4
+ expectResolution(Resolver.Typeguard, {
5
+ type: "Enum",
6
+ source: "enum test {val1, val2}",
7
+ target: "", // enums don't have typeguards
8
+ typescriptTransformer: null,
9
+ noTruthyCheck: true,
10
+ });
@@ -0,0 +1,68 @@
1
+ import { Resolver } from "../../src/resolve/Resolvable_helpers";
2
+ import { expectResolution } from "../helpers/wrapper";
3
+
4
+ type Name = string;
5
+ type Source = string;
6
+ type Target = string;
7
+
8
+ // because there is no accessor, we are "testing" `undefined`
9
+ const tests: [Name, string, Source, Target][] = [
10
+ [
11
+ "Array",
12
+ "simple",
13
+ "string[]",
14
+ "Array.isArray(undefined) && undefined.every((v) => typeof v === 'string')",
15
+ ],
16
+ [
17
+ "Array",
18
+ "union",
19
+ "(string | int32)[]",
20
+ "Array.isArray(undefined) && undefined.every((v) => (typeof v === 'string') || (typeof v === 'number'))",
21
+ ],
22
+ [
23
+ "Array",
24
+ "tuple",
25
+ "[string, int32][]",
26
+ "Array.isArray(undefined) && undefined.every((v) => Array.isArray(v) && (typeof v[0] === 'string') && (typeof v[1] === 'number'))",
27
+ ],
28
+ [
29
+ "Array",
30
+ "model",
31
+ "{str: string}[]",
32
+ "Array.isArray(undefined) && undefined.every((v) =>v['str'] !== undefined && (typeof v['str'] === 'string') )",
33
+ ],
34
+ [
35
+ "Record",
36
+ "simple",
37
+ "Record<string>",
38
+ "typeof undefined === 'object' && Object.entries(undefined as Record<string, any>).every((v) => typeof v[1] === 'string')",
39
+ ],
40
+ [
41
+ "Record",
42
+ "union",
43
+ "Record<string | int32>",
44
+ "typeof undefined === 'object' && Object.entries(undefined as Record<string, any>).every((v) => (typeof v[1] === 'string') || (typeof v[1] === 'number'))",
45
+ ],
46
+ [
47
+ "Record",
48
+ "tuple",
49
+ "Record<[string, int32]>",
50
+ "typeof undefined === 'object' && Object.entries(undefined as Record<string, any>).every((v) => Array.isArray(v[1]) && (typeof v[1][0] === 'string') && (typeof v[1][1] === 'number'))",
51
+ ],
52
+ [
53
+ "Record",
54
+ "model",
55
+ "Record<{str?: string}>",
56
+ "typeof undefined === 'object' && Object.entries(undefined as Record<string, any>).every((v) =>v[1]['str'] === undefined || (typeof v[1]['str'] === 'string') )",
57
+ ],
58
+ ];
59
+
60
+ tests.forEach((test) => {
61
+ expectResolution(Resolver.Typeguard, {
62
+ type: "Model",
63
+ desc: `(indexed: ${test[0]}): ${test[1]}`,
64
+ source: `alias test = ${test[2]};`,
65
+ target: test[3],
66
+ typescriptTransformer: null,
67
+ });
68
+ });
@@ -0,0 +1,38 @@
1
+ import { Resolver } from "../../src/resolve/Resolvable_helpers";
2
+ import { expectResolution } from "../helpers/wrapper";
3
+
4
+ const tests: [string, string][] = [
5
+ [
6
+ "{s: string}",
7
+ "undefined['s'] !== undefined && (typeof undefined['s'] === 'string')",
8
+ ],
9
+ [
10
+ "{s?: string}",
11
+ "undefined['s'] === undefined || (typeof undefined['s'] === 'string')",
12
+ ],
13
+ [
14
+ "{m: {s: string | int32}}",
15
+ "undefined['m'] !== undefined && (undefined['m']['s'] !== undefined && ((typeof undefined['m']['s'] === 'string') || (typeof undefined['m']['s'] === 'number')))",
16
+ ],
17
+ [
18
+ "{a: {s: utcDateTime}[]}",
19
+ "undefined['a'] !== undefined && (Array.isArray(undefined['a']) && undefined['a'].every((v) =>v['s'] !== undefined && (v['s'] instanceof Date) ))",
20
+ ],
21
+ [
22
+ "{r: Record<[string, int32]>, a: string[], f: never}",
23
+ "undefined['r'] !== undefined && (typeof undefined['r'] === 'object' && Object.entries(undefined['r'] as Record<string, any>).every((v) => Array.isArray(v[1]) && (typeof v[1][0] === 'string') && (typeof v[1][1] === 'number'))) &&" +
24
+ "undefined['a'] !== undefined && (Array.isArray(undefined['a']) && undefined['a'].every((v) => typeof v === 'string')) &&" +
25
+ "!('f' in undefined)",
26
+ ],
27
+ ];
28
+
29
+ tests.forEach((test) => {
30
+ expectResolution(Resolver.Typeguard, {
31
+ type: "Model",
32
+ desc: test[0],
33
+ source: `alias test = ${test[0]};`,
34
+ target: test[1],
35
+ typescriptTransformer: null,
36
+ config: { "serializable-date-types": false },
37
+ });
38
+ });
@@ -0,0 +1,62 @@
1
+ import { Scalar } from "@typespec/compiler";
2
+ import { EmitterOptions } from "../../src/lib";
3
+ import { Resolver } from "../../src/resolve/Resolvable_helpers";
4
+ import { expectResolution } from "../helpers/wrapper";
5
+
6
+ type Name = string;
7
+ type Target = string;
8
+
9
+ const tests: [Name, Target, Partial<EmitterOptions>?][] = [
10
+ ["boolean", "typeof undefined === 'boolean'"],
11
+ ["bytes", "undefined instanceof Uint8Array"],
12
+ ["duration", "typeof undefined === 'number'"],
13
+ ["numeric", "typeof undefined === 'number'"],
14
+ ["plainTime", "typeof undefined === 'string'"],
15
+ ["string", "typeof undefined === 'string'"],
16
+ ["url", "typeof undefined === 'string'"],
17
+
18
+ // date / time types
19
+ ...["offsetDateTime", "plainDate", "utcDateTime"].map(
20
+ (t) =>
21
+ [
22
+ t,
23
+ "undefined instanceof Date",
24
+ { "serializable-date-types": false },
25
+ ] as [string, string, any],
26
+ ),
27
+ ...["offsetDateTime", "plainDate", "utcDateTime"].map(
28
+ (t) =>
29
+ [
30
+ t,
31
+ "typeof undefined === 'string'",
32
+ { "serializable-date-types": true },
33
+ ] as [string, string, any],
34
+ ),
35
+ [
36
+ "unixTimestamp32",
37
+ "undefined instanceof Date",
38
+ { "serializable-date-types": false },
39
+ ],
40
+ [
41
+ "unixTimestamp32",
42
+ "typeof undefined === 'number'",
43
+ { "serializable-date-types": true },
44
+ ],
45
+ ];
46
+
47
+ tests.forEach((test) => {
48
+ expectResolution(Resolver.Typeguard, {
49
+ type: "Scalar",
50
+ desc: test[2]
51
+ ? `{${Object.entries(test[2]).map((p) => `${p[0]}: ${p[1]}`)}}`
52
+ : undefined,
53
+ source: `alias test = ${test[0]};`,
54
+ target: test[1],
55
+ test: (t) =>
56
+ (t as Scalar).name === test[0]
57
+ ? true
58
+ : `scalar name was ${(t as Scalar).name}`,
59
+ config: test[2],
60
+ typescriptTransformer: null,
61
+ });
62
+ });