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.
- package/.husky/pre-commit +2 -1
- package/.prettierignore +2 -1
- package/CHANGELOG.md +19 -24
- package/CODE_OF_CONDUCT.md +128 -0
- package/README.md +362 -219
- package/dist/src/emit_routedTypemap.d.ts +4 -0
- package/dist/src/emit_routedTypemap.js +83 -0
- package/dist/src/emit_routedTypemap.js.map +1 -0
- package/dist/src/emit_routes.d.ts +3 -2
- package/dist/src/emit_routes.js +73 -47
- package/dist/src/emit_routes.js.map +1 -1
- package/dist/src/emit_types.d.ts +3 -9
- package/dist/src/emit_types.js +109 -74
- package/dist/src/emit_types.js.map +1 -1
- package/dist/src/emitter.d.ts +2 -6
- package/dist/src/emitter.js +18 -76
- package/dist/src/emitter.js.map +1 -1
- package/dist/src/helpers/appendableString.d.ts +15 -0
- package/dist/src/helpers/appendableString.js +41 -0
- package/dist/src/helpers/appendableString.js.map +1 -0
- package/dist/src/helpers/arrays.d.ts +3 -0
- package/dist/src/helpers/arrays.js +23 -0
- package/dist/src/helpers/arrays.js.map +1 -0
- package/dist/src/{helper_autogenerateWarning.d.ts → helpers/autogenerateWarning.d.ts} +1 -1
- package/dist/src/{helper_autogenerateWarning.js → helpers/autogenerateWarning.js} +1 -2
- package/dist/src/helpers/autogenerateWarning.js.map +1 -0
- package/dist/src/helpers/buildTypeMap.d.ts +12 -0
- package/dist/src/helpers/buildTypeMap.js +44 -0
- package/dist/src/helpers/buildTypeMap.js.map +1 -0
- package/dist/src/helpers/diagnostics.d.ts +4 -0
- package/dist/src/helpers/diagnostics.js +15 -0
- package/dist/src/helpers/diagnostics.js.map +1 -0
- package/dist/src/helpers/getImports.d.ts +2 -0
- package/dist/src/helpers/getImports.js +3 -0
- package/dist/src/helpers/getImports.js.map +1 -0
- package/dist/src/helpers/namespaces.d.ts +4 -0
- package/dist/src/helpers/namespaces.js +14 -0
- package/dist/src/helpers/namespaces.js.map +1 -0
- package/dist/src/helpers/visibilityHelperFile.d.ts +4 -0
- package/dist/src/helpers/visibilityHelperFile.js +57 -0
- package/dist/src/helpers/visibilityHelperFile.js.map +1 -0
- package/dist/src/lib.d.ts +4 -1
- package/dist/src/lib.js +14 -3
- package/dist/src/lib.js.map +1 -1
- package/dist/src/parseOptions.d.ts +8 -0
- package/dist/src/parseOptions.js +26 -0
- package/dist/src/parseOptions.js.map +1 -0
- package/dist/src/resolve/Resolvable.d.ts +32 -0
- package/dist/src/resolve/Resolvable.js +180 -0
- package/dist/src/resolve/Resolvable.js.map +1 -0
- package/dist/src/resolve/Resolvable_helpers.d.ts +42 -0
- package/dist/src/resolve/Resolvable_helpers.js +19 -0
- package/dist/src/resolve/Resolvable_helpers.js.map +1 -0
- package/dist/src/resolve/operationTypemap.d.ts +21 -0
- package/dist/src/resolve/operationTypemap.js +138 -0
- package/dist/src/resolve/operationTypemap.js.map +1 -0
- package/dist/src/resolve/types/Enum.d.ts +21 -0
- package/dist/src/resolve/types/Enum.js +61 -0
- package/dist/src/resolve/types/Enum.js.map +1 -0
- package/dist/src/resolve/types/Model.Indexed.d.ts +12 -0
- package/dist/src/resolve/types/Model.Indexed.js +69 -0
- package/dist/src/resolve/types/Model.Indexed.js.map +1 -0
- package/dist/src/resolve/types/Model.Shaped.d.ts +17 -0
- package/dist/src/resolve/types/Model.Shaped.js +210 -0
- package/dist/src/resolve/types/Model.Shaped.js.map +1 -0
- package/dist/src/resolve/types/Scalar.d.ts +9 -0
- package/dist/src/resolve/types/Scalar.js +109 -0
- package/dist/src/resolve/types/Scalar.js.map +1 -0
- package/dist/src/resolve/types/Simple.d.ts +11 -0
- package/dist/src/resolve/types/Simple.js +49 -0
- package/dist/src/resolve/types/Simple.js.map +1 -0
- package/dist/src/resolve/types/Tuple.d.ts +9 -0
- package/dist/src/resolve/types/Tuple.js +38 -0
- package/dist/src/resolve/types/Tuple.js.map +1 -0
- package/dist/src/resolve/types/Union.d.ts +9 -0
- package/dist/src/resolve/types/Union.js +36 -0
- package/dist/src/resolve/types/Union.js.map +1 -0
- package/eslint.config.js +12 -4
- package/package.json +2 -2
- package/src/emit_routedTypemap.ts +128 -0
- package/src/emit_routes.ts +108 -61
- package/src/emit_types.ts +137 -92
- package/src/emitter.ts +19 -107
- package/src/helpers/appendableString.ts +52 -0
- package/src/helpers/arrays.ts +21 -0
- package/src/{helper_autogenerateWarning.ts → helpers/autogenerateWarning.ts} +0 -1
- package/src/helpers/buildTypeMap.ts +72 -0
- package/src/helpers/diagnostics.ts +19 -0
- package/src/helpers/getImports.ts +9 -0
- package/src/helpers/namespaces.ts +18 -0
- package/src/helpers/visibilityHelperFile.ts +63 -0
- package/src/lib.ts +25 -4
- package/src/parseOptions.ts +26 -0
- package/src/resolve/Resolvable.ts +267 -0
- package/src/resolve/Resolvable_helpers.ts +65 -0
- package/src/resolve/operationTypemap.ts +212 -0
- package/src/resolve/types/Enum.ts +92 -0
- package/src/resolve/types/Model.Indexed.ts +113 -0
- package/src/resolve/types/Model.Shaped.ts +291 -0
- package/src/resolve/types/Scalar.ts +140 -0
- package/src/resolve/types/Simple.ts +88 -0
- package/src/resolve/types/Tuple.ts +56 -0
- package/src/resolve/types/Union.ts +52 -0
- package/test/helpers/integrationTest-novis.tsp +51 -0
- package/test/helpers/integrationTest.tsp +53 -0
- package/test/helpers/largeModel.tsp +40 -0
- package/test/{runner.ts → helpers/runner.ts} +1 -1
- package/test/helpers/ts.ts +11 -0
- package/test/helpers/wrapper.ts +144 -0
- package/test/routes/routes.target.ts +35 -0
- package/test/routes/routes.test.ts +22 -0
- package/test/typeguards/combined.test.ts +78 -0
- package/test/typeguards/enum.test.ts +10 -0
- package/test/typeguards/model.indexed.test.ts +68 -0
- package/test/typeguards/model.shaped.test.ts +38 -0
- package/test/typeguards/scalar.test.ts +62 -0
- package/test/typeguards/simple.test.ts +35 -0
- package/test/typeguards/tuple.test.ts +34 -0
- package/test/typeguards/union.test.ts +29 -0
- package/test/typemap/typemap-novis.target.ts +38 -0
- package/test/typemap/typemap.target.ts +39 -0
- package/test/typemap/typemap.test.ts +48 -0
- package/test/types/combined.test.ts +71 -0
- package/test/types/enum.test.ts +57 -0
- package/test/types/model.indexed.test.ts +46 -0
- package/test/types/model.shaped.test.ts +23 -0
- package/test/types/scalar.test.ts +53 -0
- package/test/types/simple.test.ts +20 -0
- package/test/types/tuple.test.ts +29 -0
- package/test/types/union.test.ts +20 -0
- package/tsconfig.json +1 -0
- package/dist/src/emit_mapped_types.d.ts +0 -2
- package/dist/src/emit_mapped_types.js +0 -124
- package/dist/src/emit_mapped_types.js.map +0 -1
- package/dist/src/emit_types_resolve.d.ts +0 -22
- package/dist/src/emit_types_resolve.js +0 -217
- package/dist/src/emit_types_resolve.js.map +0 -1
- package/dist/src/emit_types_typeguards.d.ts +0 -17
- package/dist/src/emit_types_typeguards.js +0 -121
- package/dist/src/emit_types_typeguards.js.map +0 -1
- package/dist/src/helper_autogenerateWarning.js.map +0 -1
- package/src/emit_mapped_types.ts +0 -155
- package/src/emit_types_resolve.ts +0 -280
- package/src/emit_types_typeguards.ts +0 -178
- package/test/main.test.ts +0 -83
- package/test/out/.gitkeep +0 -0
- package/test/targets/enum.routed-types.ts +0 -59
- package/test/targets/enum.routes.ts +0 -29
- package/test/targets/enum.target.ts +0 -39
- package/test/targets/enum.tsp +0 -36
- package/test/targets/pr8.routed-types.ts +0 -131
- package/test/targets/pr8.routes.ts +0 -30
- package/test/targets/pr8.target.ts +0 -97
- package/test/targets/pr8.tsp +0 -62
- package/test/targets/simple-routes.routed-types.ts +0 -64
- package/test/targets/simple-routes.routes.ts +0 -29
- package/test/targets/simple-routes.target.ts +0 -21
- package/test/targets/simple-routes.tsp +0 -23
- package/test/targets/union.routed-types.ts +0 -59
- package/test/targets/union.routes.ts +0 -23
- package/test/targets/union.target.ts +0 -59
- package/test/targets/union.tsp +0 -38
- package/test/targets/visibility.routed-types.ts +0 -81
- package/test/targets/visibility.routes.ts +0 -38
- package/test/targets/visibility.target.ts +0 -36
- package/test/targets/visibility.tsp +0 -49
package/src/emitter.ts
CHANGED
|
@@ -1,114 +1,26 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
emitFile,
|
|
4
|
-
navigateProgram,
|
|
5
|
-
resolvePath,
|
|
6
|
-
} from "@typespec/compiler";
|
|
7
|
-
import { emitRoutedTypemap } from "./emit_mapped_types.js";
|
|
1
|
+
import { EmitContext } from "@typespec/compiler";
|
|
2
|
+
import { emitRoutedTypemap } from "./emit_routedTypemap.js";
|
|
8
3
|
import { emitRoutes } from "./emit_routes.js";
|
|
9
|
-
import emitTypes from "./emit_types.js";
|
|
10
|
-
import
|
|
4
|
+
import { emitTypes } from "./emit_types.js";
|
|
5
|
+
import { buildTypeMap } from "./helpers/buildTypeMap.js";
|
|
6
|
+
import { setContext } from "./helpers/diagnostics.js";
|
|
7
|
+
import { emitVisibilityHelperFile } from "./helpers/visibilityHelperFile.js";
|
|
11
8
|
import { EmitterOptions } from "./lib.js";
|
|
9
|
+
import { parseOptions } from "./parseOptions.js";
|
|
12
10
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
}
|
|
18
|
-
}
|
|
19
|
-
String.prototype.addLine = function (
|
|
20
|
-
this: string,
|
|
21
|
-
str: string,
|
|
22
|
-
tabs?: number,
|
|
23
|
-
continued?: boolean,
|
|
24
|
-
): string {
|
|
25
|
-
return `${this}${" ".repeat(tabs ?? 0)}${str}${continued ? "" : "\n"}`;
|
|
26
|
-
};
|
|
27
|
-
|
|
28
|
-
export async function $onEmit(context: EmitContext) {
|
|
29
|
-
if (!context.program.compilerOptions.noEmit) {
|
|
30
|
-
const options: EmitterOptions = {
|
|
31
|
-
"root-namespace": context.options["root-namespace"],
|
|
32
|
-
"out-dir": context.options["out-dir"] ?? context.emitterOutputDir,
|
|
33
|
-
"enable-types": context.options["enable-types"] ?? true,
|
|
34
|
-
"enable-typeguards":
|
|
35
|
-
(context.options["enable-types"] ?? true) &&
|
|
36
|
-
(context.options["enable-typeguards"] ?? false),
|
|
37
|
-
"enable-routes": context.options["enable-routes"] ?? false,
|
|
38
|
-
"enable-routed-typemap":
|
|
39
|
-
context.options["enable-routed-typemap"] ?? false,
|
|
40
|
-
"string-nominal-enums":
|
|
41
|
-
(context.options["enable-routed-typemap"] ?? false) &&
|
|
42
|
-
(context.options["string-nominal-enums"] ?? false),
|
|
43
|
-
"serializable-date-types":
|
|
44
|
-
context.options["serializable-date-types"] ?? false,
|
|
45
|
-
};
|
|
46
|
-
|
|
47
|
-
console.log(`Writing routes to ${options["out-dir"]}`);
|
|
48
|
-
|
|
49
|
-
let targetNamespaceFound = false;
|
|
50
|
-
let routesObject = "";
|
|
51
|
-
let routedTypemap = "";
|
|
52
|
-
let typeFiles: ReturnType<typeof emitTypes> = {
|
|
53
|
-
files: {},
|
|
54
|
-
typeguardedNames: [],
|
|
55
|
-
};
|
|
56
|
-
navigateProgram(context.program, {
|
|
57
|
-
namespace(n) {
|
|
58
|
-
if (
|
|
59
|
-
!targetNamespaceFound &&
|
|
60
|
-
n.name === context.options["root-namespace"]
|
|
61
|
-
) {
|
|
62
|
-
targetNamespaceFound = true;
|
|
63
|
-
if (options["enable-types"] || options["enable-typeguards"])
|
|
64
|
-
typeFiles = emitTypes(context, n, options);
|
|
65
|
-
if (options["enable-routes"]) {
|
|
66
|
-
routesObject = emitRoutes(context, n);
|
|
67
|
-
}
|
|
68
|
-
if (options["enable-routed-typemap"]) {
|
|
69
|
-
routedTypemap = emitRoutedTypemap(context, n);
|
|
70
|
-
}
|
|
71
|
-
}
|
|
72
|
-
},
|
|
73
|
-
});
|
|
74
|
-
|
|
75
|
-
if (!targetNamespaceFound)
|
|
76
|
-
throw new Error("Targeted root namespace not found.");
|
|
11
|
+
export async function $onEmit(context: EmitContext<EmitterOptions>) {
|
|
12
|
+
if (context.program.compilerOptions.noEmit) return;
|
|
13
|
+
setContext(context);
|
|
14
|
+
parseOptions(context);
|
|
77
15
|
|
|
78
|
-
|
|
79
|
-
if (options["enable-routes"]) {
|
|
80
|
-
if (!routesObject) throw new Error("Routes object empty.");
|
|
81
|
-
await emitFile(context.program, {
|
|
82
|
-
path: resolvePath(
|
|
83
|
-
options["out-dir"],
|
|
84
|
-
`routes_${options["root-namespace"]}.ts`,
|
|
85
|
-
),
|
|
86
|
-
content: `/* eslint-disable */\n\n${autogenerateWarning}${routesObject}`,
|
|
87
|
-
});
|
|
88
|
-
}
|
|
16
|
+
const typeMap = buildTypeMap(context.program, context.options);
|
|
89
17
|
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
`routedTypemap_${options["root-namespace"]}.ts`,
|
|
97
|
-
),
|
|
98
|
-
content: `/* eslint-disable */\n\n${autogenerateWarning}${routedTypemap}`,
|
|
99
|
-
});
|
|
100
|
-
}
|
|
18
|
+
if (context.options["enable-types"])
|
|
19
|
+
await emitTypes(context.program, context.options, typeMap);
|
|
20
|
+
if (context.options["enable-routed-typemap"])
|
|
21
|
+
await emitRoutedTypemap(context.program, context.options, typeMap);
|
|
22
|
+
if (context.options["enable-routes"])
|
|
23
|
+
await emitRoutes(context.program, context.options);
|
|
101
24
|
|
|
102
|
-
|
|
103
|
-
if (options["enable-types"] || options["enable-typeguards"]) {
|
|
104
|
-
const typeFileArr = Object.entries(typeFiles.files);
|
|
105
|
-
for (let i = 0; i < typeFileArr.length; i++) {
|
|
106
|
-
if (typeFileArr[i][1])
|
|
107
|
-
await emitFile(context.program, {
|
|
108
|
-
path: resolvePath(options["out-dir"], `${typeFileArr[i][0]}.ts`),
|
|
109
|
-
content: `/* eslint-disable */\n\n${autogenerateWarning}${typeFileArr[i][1]}`,
|
|
110
|
-
});
|
|
111
|
-
}
|
|
112
|
-
}
|
|
113
|
-
}
|
|
25
|
+
await emitVisibilityHelperFile(context.program, context.options);
|
|
114
26
|
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* This class provides a string type which can only be appended to but not overwritten.
|
|
3
|
+
*/
|
|
4
|
+
export class AppendableString {
|
|
5
|
+
constructor(init?: string) {
|
|
6
|
+
this._content = init ?? "";
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
private _content: string;
|
|
10
|
+
|
|
11
|
+
get value(): string {
|
|
12
|
+
return this._content;
|
|
13
|
+
}
|
|
14
|
+
append(s?: string | AppendableString) {
|
|
15
|
+
this._content += s ?? "";
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/** Changes in place, returning the new value */
|
|
19
|
+
addLine(
|
|
20
|
+
str: string | AppendableString,
|
|
21
|
+
tabs?: number,
|
|
22
|
+
continued: "continued" | "line-end" = "line-end",
|
|
23
|
+
): this {
|
|
24
|
+
this._content += `${" ".repeat(tabs ?? 0)}${str}${continued === "continued" ? "" : "\n"}`;
|
|
25
|
+
return this;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
[Symbol.toPrimitive](hint: string) {
|
|
29
|
+
switch (hint) {
|
|
30
|
+
case "default":
|
|
31
|
+
case "string":
|
|
32
|
+
return this.value;
|
|
33
|
+
|
|
34
|
+
case "boolean":
|
|
35
|
+
return !!this.value;
|
|
36
|
+
|
|
37
|
+
default:
|
|
38
|
+
throw new TypeError(`Appendable string cannot be coerced to ${hint}`);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/** Removes the last x characters */
|
|
43
|
+
dropLast(n: number = 1): this {
|
|
44
|
+
this._content = this._content.substring(0, this._content.length - n);
|
|
45
|
+
return this;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
clear(): this {
|
|
49
|
+
this._content = "";
|
|
50
|
+
return this;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
export const compareArrays = (a: any[], b: any[]): boolean => {
|
|
2
|
+
if (a.length !== b.length) return false;
|
|
3
|
+
for (let i = 0; i < a.length; i++) {
|
|
4
|
+
if (a[i] !== b[i]) return false;
|
|
5
|
+
}
|
|
6
|
+
return true;
|
|
7
|
+
};
|
|
8
|
+
|
|
9
|
+
/** Unique-ifies a 2D array of strings (imported namespaces list). */
|
|
10
|
+
export const unique2D = (rows: string[][]): string[][] => {
|
|
11
|
+
const seen = new Set<string>();
|
|
12
|
+
const out: string[][] = [];
|
|
13
|
+
for (const r of rows) {
|
|
14
|
+
const key = r.join("\u0000"); // safe separator
|
|
15
|
+
if (!seen.has(key)) {
|
|
16
|
+
seen.add(key);
|
|
17
|
+
out.push(r);
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
return out;
|
|
21
|
+
};
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import {
|
|
2
|
+
Enum,
|
|
3
|
+
Model,
|
|
4
|
+
Namespace,
|
|
5
|
+
navigateProgram,
|
|
6
|
+
navigateTypesInNamespace,
|
|
7
|
+
Program,
|
|
8
|
+
Scalar,
|
|
9
|
+
Union,
|
|
10
|
+
} from "@typespec/compiler";
|
|
11
|
+
import { EmitterOptions } from "../lib.js";
|
|
12
|
+
|
|
13
|
+
export type TTypeMap = {
|
|
14
|
+
type: Enum | Model | Scalar | Union;
|
|
15
|
+
namespaces: string[];
|
|
16
|
+
hasVisibility: boolean | undefined;
|
|
17
|
+
}[];
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Traverses all namespaces in the program and produces
|
|
21
|
+
* a map of all types-to-be-emitted and their namespace hierarchy.
|
|
22
|
+
*/
|
|
23
|
+
export const buildTypeMap = (
|
|
24
|
+
program: Program,
|
|
25
|
+
options: EmitterOptions,
|
|
26
|
+
): TTypeMap => {
|
|
27
|
+
// save original targeted namespaces array because it's mutated here
|
|
28
|
+
const targetedNamespaces = structuredClone(options["root-namespaces"]);
|
|
29
|
+
const map: TTypeMap = [];
|
|
30
|
+
|
|
31
|
+
const pushType = (t: TTypeMap[number]["type"], hierarchy: string[]): void => {
|
|
32
|
+
if (!t.name) return;
|
|
33
|
+
map.push({
|
|
34
|
+
type: t,
|
|
35
|
+
namespaces: hierarchy,
|
|
36
|
+
hasVisibility: undefined,
|
|
37
|
+
});
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
const traverseNamespace = (n: Namespace, hierarchy: string[]) => {
|
|
41
|
+
navigateTypesInNamespace(
|
|
42
|
+
n,
|
|
43
|
+
{
|
|
44
|
+
enum: (t) => pushType(t, [...hierarchy, n.name]),
|
|
45
|
+
scalar: (t) => pushType(t, [...hierarchy, n.name]),
|
|
46
|
+
model: (t) => pushType(t, [...hierarchy, n.name]),
|
|
47
|
+
union: (t) => pushType(t, [...hierarchy, n.name]),
|
|
48
|
+
},
|
|
49
|
+
{
|
|
50
|
+
skipSubNamespaces: true,
|
|
51
|
+
},
|
|
52
|
+
);
|
|
53
|
+
n.namespaces.forEach((ns) => traverseNamespace(ns, [...hierarchy, n.name]));
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
navigateProgram(program, {
|
|
57
|
+
namespace(n) {
|
|
58
|
+
const nsIndex = options["root-namespaces"].findIndex(
|
|
59
|
+
(ns) => n.name === ns,
|
|
60
|
+
);
|
|
61
|
+
if (nsIndex === -1) return;
|
|
62
|
+
// for some reason, navigateProgram visits each namespace multiple times; this prevents that
|
|
63
|
+
delete options["root-namespaces"][nsIndex];
|
|
64
|
+
|
|
65
|
+
traverseNamespace(n, []);
|
|
66
|
+
},
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
// restore un-mutated version
|
|
70
|
+
options["root-namespaces"] = targetedNamespaces;
|
|
71
|
+
return map;
|
|
72
|
+
};
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { Diagnostic, EmitContext, NoTarget } from "@typespec/compiler";
|
|
2
|
+
import { EmitterOptions } from "../lib.js";
|
|
3
|
+
|
|
4
|
+
let context: EmitContext<EmitterOptions> | null = null;
|
|
5
|
+
|
|
6
|
+
export const setContext = (c: EmitContext<EmitterOptions>): void => {
|
|
7
|
+
context = c;
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
export const reportDiagnostic = (
|
|
11
|
+
diagnostic: Pick<Diagnostic, "code" | "message" | "severity">,
|
|
12
|
+
): void => {
|
|
13
|
+
if (!context) throw new Error("Couldn't report diagnostic: context not set");
|
|
14
|
+
context.program.reportDiagnostic({
|
|
15
|
+
...diagnostic,
|
|
16
|
+
code: `typespec-emitter-${diagnostic.code}`,
|
|
17
|
+
target: NoTarget,
|
|
18
|
+
});
|
|
19
|
+
};
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { TTypeMap } from "./buildTypeMap.js";
|
|
2
|
+
import { filenameFromNamespaces } from "./namespaces.js";
|
|
3
|
+
|
|
4
|
+
export const getImports = (
|
|
5
|
+
imports: TTypeMap[number]["namespaces"][],
|
|
6
|
+
): string[] =>
|
|
7
|
+
imports.map(
|
|
8
|
+
(i) => `import * as ${i.join("_")} from './${filenameFromNamespaces(i)}';`,
|
|
9
|
+
);
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { Namespace } from "@typespec/compiler";
|
|
2
|
+
|
|
3
|
+
/** Traverses *up* a namespace, generating a hierarchy array from itself and its parents. */
|
|
4
|
+
export const namespaceListFromNamespace = (
|
|
5
|
+
n: Namespace | undefined,
|
|
6
|
+
): string[] | null => {
|
|
7
|
+
if (!n) return null;
|
|
8
|
+
const ret: string[] = [];
|
|
9
|
+
let cur: Namespace | undefined = n;
|
|
10
|
+
while (cur && cur.name) {
|
|
11
|
+
ret.unshift(cur.name);
|
|
12
|
+
cur = cur.namespace;
|
|
13
|
+
}
|
|
14
|
+
return ret;
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
export const filenameFromNamespaces = (ns: string[]): string =>
|
|
18
|
+
`${ns.join(".")}.ts`;
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { emitFile, Program, resolvePath } from "@typespec/compiler";
|
|
2
|
+
import { EmitterOptions } from "../lib.js";
|
|
3
|
+
|
|
4
|
+
const visibilityHelperFile = `
|
|
5
|
+
/* eslint-disable */
|
|
6
|
+
|
|
7
|
+
/*
|
|
8
|
+
This file is auto-generated by typespec-typescript-emitter and
|
|
9
|
+
contains helpers for accessing lifecycle-visibility-modified
|
|
10
|
+
variants of the emitted types.
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
export enum Lifecycle {
|
|
14
|
+
Read, Create, Update, Delete, Query,
|
|
15
|
+
None, All
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export type VisibilityMap<T> = {
|
|
19
|
+
[K in keyof T]?: {
|
|
20
|
+
vis?: readonly Lifecycle[]
|
|
21
|
+
nested?: VisibilityMap<T[K]>
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export type FilterLifecycle<
|
|
26
|
+
T,
|
|
27
|
+
M extends VisibilityMap<T>,
|
|
28
|
+
Current extends Lifecycle
|
|
29
|
+
> = {
|
|
30
|
+
[K in keyof T as (
|
|
31
|
+
Current extends Lifecycle.All
|
|
32
|
+
? K
|
|
33
|
+
: Current extends Lifecycle.None
|
|
34
|
+
? never
|
|
35
|
+
: K extends keyof M
|
|
36
|
+
? M[K] extends { vis?: readonly (infer L)[]; }
|
|
37
|
+
? (undefined extends M[K]['vis']
|
|
38
|
+
? K
|
|
39
|
+
: (Current extends L ? K : never)
|
|
40
|
+
)
|
|
41
|
+
: K
|
|
42
|
+
: K
|
|
43
|
+
)]: K extends keyof M
|
|
44
|
+
? M[K] extends {nested?: infer N}
|
|
45
|
+
? N extends VisibilityMap<T[K]>
|
|
46
|
+
? FilterLifecycle<T[K], N, Current>
|
|
47
|
+
: T[K]
|
|
48
|
+
: T[K]
|
|
49
|
+
: T[K]
|
|
50
|
+
}
|
|
51
|
+
`;
|
|
52
|
+
|
|
53
|
+
export const visibilityHelperFileName = "lifecycleVisibility.ts";
|
|
54
|
+
|
|
55
|
+
export const emitVisibilityHelperFile = async (
|
|
56
|
+
program: Program,
|
|
57
|
+
options: EmitterOptions,
|
|
58
|
+
): Promise<void> => {
|
|
59
|
+
await emitFile(program, {
|
|
60
|
+
path: resolvePath(options["out-dir"], visibilityHelperFileName),
|
|
61
|
+
content: visibilityHelperFile,
|
|
62
|
+
});
|
|
63
|
+
};
|
package/src/lib.ts
CHANGED
|
@@ -1,7 +1,11 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
createTypeSpecLibrary,
|
|
3
|
+
EmitContext,
|
|
4
|
+
JSONSchemaType,
|
|
5
|
+
} from "@typespec/compiler";
|
|
2
6
|
|
|
3
7
|
export interface EmitterOptions {
|
|
4
|
-
"root-
|
|
8
|
+
"root-namespaces": string[];
|
|
5
9
|
"out-dir": string;
|
|
6
10
|
"enable-types": boolean;
|
|
7
11
|
"enable-typeguards": boolean;
|
|
@@ -15,7 +19,7 @@ const EmitterOptionsSchema: JSONSchemaType<EmitterOptions> = {
|
|
|
15
19
|
type: "object",
|
|
16
20
|
additionalProperties: false,
|
|
17
21
|
properties: {
|
|
18
|
-
"root-
|
|
22
|
+
"root-namespaces": { type: "array", items: { type: "string" } },
|
|
19
23
|
"out-dir": { type: "string", format: "absolute-path" },
|
|
20
24
|
"enable-types": { type: "boolean" },
|
|
21
25
|
"enable-typeguards": { type: "boolean" },
|
|
@@ -24,9 +28,26 @@ const EmitterOptionsSchema: JSONSchemaType<EmitterOptions> = {
|
|
|
24
28
|
"string-nominal-enums": { type: "boolean" },
|
|
25
29
|
"serializable-date-types": { type: "boolean" },
|
|
26
30
|
},
|
|
27
|
-
required: ["root-
|
|
31
|
+
required: ["root-namespaces"],
|
|
28
32
|
};
|
|
29
33
|
|
|
34
|
+
/** Maps option to its default value and options that must be set to `true` for this one to work */
|
|
35
|
+
export const optionDependencies = (
|
|
36
|
+
context: EmitContext,
|
|
37
|
+
): {
|
|
38
|
+
[K in keyof EmitterOptions]: [EmitterOptions[K], (keyof EmitterOptions)[]];
|
|
39
|
+
} => ({
|
|
40
|
+
["root-namespaces"]: [[], []],
|
|
41
|
+
["out-dir"]: [context.emitterOutputDir, []],
|
|
42
|
+
["enable-types"]: [false, []],
|
|
43
|
+
["enable-routes"]: [false, []],
|
|
44
|
+
["string-nominal-enums"]: [false, []],
|
|
45
|
+
["serializable-date-types"]: [false, []],
|
|
46
|
+
|
|
47
|
+
["enable-typeguards"]: [false, ["enable-types"]],
|
|
48
|
+
["enable-routed-typemap"]: [false, ["enable-types"]],
|
|
49
|
+
});
|
|
50
|
+
|
|
30
51
|
export const $lib = createTypeSpecLibrary({
|
|
31
52
|
name: "typespec-typescript-emitter",
|
|
32
53
|
diagnostics: {},
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { EmitContext } from "@typespec/compiler";
|
|
2
|
+
import { reportDiagnostic } from "./helpers/diagnostics.js";
|
|
3
|
+
import { EmitterOptions, optionDependencies } from "./lib.js";
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Parses config-file options including inter-option dependencies, setting defaults,
|
|
7
|
+
* emitting warnings/errors as required,
|
|
8
|
+
* replacing options in context with parsed versions.
|
|
9
|
+
*/
|
|
10
|
+
export const parseOptions = (context: EmitContext<EmitterOptions>): void => {
|
|
11
|
+
Object.entries(optionDependencies).forEach(([k, val]) => {
|
|
12
|
+
const key: keyof EmitterOptions = k as any;
|
|
13
|
+
const [def, deps]: [unknown, (keyof EmitterOptions)[]] = val;
|
|
14
|
+
if (deps.length === 0) {
|
|
15
|
+
(context.options[key] as unknown) = context.options[key] ?? def;
|
|
16
|
+
} else {
|
|
17
|
+
if (context.options[key] && !deps.every((d) => context.options[d])) {
|
|
18
|
+
reportDiagnostic({
|
|
19
|
+
code: `opts-dependency-${key}`,
|
|
20
|
+
severity: "error",
|
|
21
|
+
message: `Option ${key} requires ${deps.join(", ")}`,
|
|
22
|
+
});
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
});
|
|
26
|
+
};
|