@typespec/protobuf 0.43.0
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/.c8rc.json +3 -0
- package/.eslintrc.cjs +7 -0
- package/.mocharc.yaml +4 -0
- package/.rush/temp/operation/build/state.json +3 -0
- package/.rush/temp/operation/test-official/state.json +3 -0
- package/.rush/temp/package-deps_build.json +81 -0
- package/.rush/temp/package-deps_test-official.json +81 -0
- package/.rush/temp/shrinkwrap-deps.json +177 -0
- package/CHANGELOG.json +4 -0
- package/LICENSE +21 -0
- package/README.md +30 -0
- package/coverage/cobertura-coverage.xml +2291 -0
- package/coverage/coverage-final.json +7 -0
- package/coverage/tmp/coverage-6408-1683144315012-0.json +1 -0
- package/dist/src/ast.d.ts +199 -0
- package/dist/src/ast.d.ts.map +1 -0
- package/dist/src/ast.js +60 -0
- package/dist/src/ast.js.map +1 -0
- package/dist/src/index.d.ts +62 -0
- package/dist/src/index.d.ts.map +1 -0
- package/dist/src/index.js +5 -0
- package/dist/src/index.js.map +1 -0
- package/dist/src/lib.d.ts +197 -0
- package/dist/src/lib.d.ts.map +1 -0
- package/dist/src/lib.js +134 -0
- package/dist/src/lib.js.map +1 -0
- package/dist/src/proto.d.ts +62 -0
- package/dist/src/proto.d.ts.map +1 -0
- package/dist/src/proto.js +162 -0
- package/dist/src/proto.js.map +1 -0
- package/dist/src/transform/index.d.ts +7 -0
- package/dist/src/transform/index.d.ts.map +1 -0
- package/dist/src/transform/index.js +744 -0
- package/dist/src/transform/index.js.map +1 -0
- package/dist/src/write.d.ts +12 -0
- package/dist/src/write.d.ts.map +1 -0
- package/dist/src/write.js +204 -0
- package/dist/src/write.js.map +1 -0
- package/dist/test/scenarios.test.d.ts +2 -0
- package/dist/test/scenarios.test.d.ts.map +1 -0
- package/dist/test/scenarios.test.js +165 -0
- package/dist/test/scenarios.test.js.map +1 -0
- package/lib/proto.tsp +303 -0
- package/package.json +49 -0
- package/protobuf.build.log +48 -0
- package/src/ast.ts +279 -0
- package/src/index.ts +7 -0
- package/src/lib.ts +161 -0
- package/src/proto.ts +217 -0
- package/src/transform/index.ts +980 -0
- package/src/write.ts +244 -0
- package/temp/tsconfig.tsbuildinfo +1 -0
- package/test/include/foo/bar.proto +7 -0
- package/test/scenarios/addressbook/input/addressbook.tsp +27 -0
- package/test/scenarios/addressbook/input/main.tsp +13 -0
- package/test/scenarios/addressbook/output/@typespec/protobuf/addressbook.proto +26 -0
- package/test/scenarios/addressbook/output/@typespec/protobuf/main.proto +14 -0
- package/test/scenarios/anonymous-model/diagnostics.txt +1 -0
- package/test/scenarios/anonymous-model/input/main.tsp +24 -0
- package/test/scenarios/anonymous-package/input/main.tsp +19 -0
- package/test/scenarios/anonymous-package/output/@typespec/protobuf/main.proto +15 -0
- package/test/scenarios/array/input/main.tsp +22 -0
- package/test/scenarios/array/output/@typespec/protobuf/com/azure/test.proto +18 -0
- package/test/scenarios/array-nested/diagnostics.txt +1 -0
- package/test/scenarios/array-nested/input/main.tsp +20 -0
- package/test/scenarios/cross package references/input/main.tsp +27 -0
- package/test/scenarios/cross package references/output/@typespec/protobuf/A.proto +10 -0
- package/test/scenarios/cross package references/output/@typespec/protobuf/B.proto +15 -0
- package/test/scenarios/derived-scalar/input/main.tsp +24 -0
- package/test/scenarios/derived-scalar/output/@typespec/protobuf/com/azure/Test.proto +18 -0
- package/test/scenarios/enum/input/main.tsp +33 -0
- package/test/scenarios/enum/output/@typespec/protobuf/main.proto +31 -0
- package/test/scenarios/enum-nonintegral/diagnostics.txt +4 -0
- package/test/scenarios/enum-nonintegral/input/main.tsp +31 -0
- package/test/scenarios/enum-nozero/diagnostics.txt +1 -0
- package/test/scenarios/enum-nozero/input/main.tsp +25 -0
- package/test/scenarios/extern/input/main.tsp +17 -0
- package/test/scenarios/extern/output/@typespec/protobuf/main.proto +19 -0
- package/test/scenarios/illegal field reservations/diagnostics.txt +2 -0
- package/test/scenarios/illegal field reservations/input/main.tsp +16 -0
- package/test/scenarios/inferred-message-names/input/main.tsp +16 -0
- package/test/scenarios/inferred-message-names/output/@typespec/protobuf/com/azure/test.proto +18 -0
- package/test/scenarios/intrinsics/input/main.tsp +17 -0
- package/test/scenarios/intrinsics/output/@typespec/protobuf/com/azure/Test.proto +16 -0
- package/test/scenarios/map/input/main.tsp +15 -0
- package/test/scenarios/map/output/@typespec/protobuf/main.proto +13 -0
- package/test/scenarios/model-no-package/diagnostics.txt +2 -0
- package/test/scenarios/model-no-package/input/main.tsp +19 -0
- package/test/scenarios/name-collision/input/main.tsp +24 -0
- package/test/scenarios/name-collision/output/@typespec/protobuf/main.proto +24 -0
- package/test/scenarios/options/input/main.tsp +25 -0
- package/test/scenarios/options/output/@typespec/protobuf/com/azure/Test.proto +20 -0
- package/test/scenarios/options-invalid/diagnostics.txt +1 -0
- package/test/scenarios/options-invalid/input/main.tsp +25 -0
- package/test/scenarios/reserved field collisions/diagnostics.txt +5 -0
- package/test/scenarios/reserved field collisions/input/main.tsp +19 -0
- package/test/scenarios/reserved fields/input/main.tsp +16 -0
- package/test/scenarios/reserved fields/output/@typespec/protobuf/main.proto +16 -0
- package/test/scenarios/simple/input/main.tsp +22 -0
- package/test/scenarios/simple/output/@typespec/protobuf/com/azure/Test.proto +18 -0
- package/test/scenarios/simple-error/diagnostics.txt +6 -0
- package/test/scenarios/simple-error/input/main.tsp +22 -0
- package/test/scenarios/simple-no-service/input/main.tsp +22 -0
- package/test/scenarios/simple-no-service/output/@typespec/protobuf/com/azure/Test.proto +18 -0
- package/test/scenarios/streams/input/main.tsp +30 -0
- package/test/scenarios/streams/output/@typespec/protobuf/main.proto +19 -0
- package/test/scenarios/type-validation/diagnostics.txt +1 -0
- package/test/scenarios/type-validation/input/main.tsp +11 -0
- package/test/scenarios/union/diagnostics.txt +1 -0
- package/test/scenarios/union/input/main.tsp +33 -0
- package/test/scenarios.test.ts +226 -0
- package/tsconfig.json +15 -0
|
@@ -0,0 +1,744 @@
|
|
|
1
|
+
// Copyright (c) Microsoft Corporation.
|
|
2
|
+
// Licensed under the MIT license.
|
|
3
|
+
import { formatDiagnostic, getEffectiveModelType, getTypeName, isDeclaredInNamespace, resolvePath, SyntaxKind, } from "@typespec/compiler";
|
|
4
|
+
import { map, matchType, ref, scalar, unreachable, } from "../ast.js";
|
|
5
|
+
import { reportDiagnostic, state } from "../lib.js";
|
|
6
|
+
import { $field, isMap } from "../proto.js";
|
|
7
|
+
import { writeProtoFile } from "../write.js";
|
|
8
|
+
// Cache for scalar -> ProtoScalar map
|
|
9
|
+
const _protoScalarsMap = new WeakMap();
|
|
10
|
+
const _protoExternMap = new WeakMap();
|
|
11
|
+
/**
|
|
12
|
+
* Create a worker function that converts the TypeSpec program to Protobuf and writes it to the file system.
|
|
13
|
+
*/
|
|
14
|
+
export function createProtobufEmitter(program) {
|
|
15
|
+
return async function doEmit(outDir, options) {
|
|
16
|
+
var _a, _b;
|
|
17
|
+
// Convert the program to a set of proto files.
|
|
18
|
+
const files = tspToProto(program);
|
|
19
|
+
if (!program.compilerOptions.noEmit && !(options === null || options === void 0 ? void 0 : options.noEmit) && !program.hasError()) {
|
|
20
|
+
for (const file of files) {
|
|
21
|
+
// If the file has a package, emit it to a path that is shaped like the package name. Otherwise emit to
|
|
22
|
+
// main.proto
|
|
23
|
+
// Collisions have already been detected.
|
|
24
|
+
const packageSlug = (_b = (_a = file.package) === null || _a === void 0 ? void 0 : _a.split(".")) !== null && _b !== void 0 ? _b : ["main"];
|
|
25
|
+
const filePath = resolvePath(outDir, ...packageSlug.slice(0, -1));
|
|
26
|
+
await program.host.mkdirp(filePath);
|
|
27
|
+
await program.host.writeFile(resolvePath(filePath, packageSlug[packageSlug.length - 1] + ".proto"), writeProtoFile(file));
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Create a set of proto files that represent the TypeSpec program.
|
|
34
|
+
*
|
|
35
|
+
* This is the meat of the emitter.
|
|
36
|
+
*/
|
|
37
|
+
function tspToProto(program) {
|
|
38
|
+
const packages = new Set(program.stateMap(state.package).keys());
|
|
39
|
+
const serviceInterfaces = [...program.stateSet(state.service)];
|
|
40
|
+
const declarationMap = new Map([...packages].map((p) => [p, []]));
|
|
41
|
+
const visitedTypes = new Set();
|
|
42
|
+
/**
|
|
43
|
+
* Visits a model type, converting it into a message definition and adding it if it has not already been visited.
|
|
44
|
+
* @param model - the model type to consider
|
|
45
|
+
*/
|
|
46
|
+
function visitModel(model, source) {
|
|
47
|
+
const modelPackage = getPackageOfType(program, model);
|
|
48
|
+
const declarations = modelPackage && declarationMap.get(modelPackage);
|
|
49
|
+
if (!declarations) {
|
|
50
|
+
reportDiagnostic(program, {
|
|
51
|
+
target: source,
|
|
52
|
+
code: "model-not-in-package",
|
|
53
|
+
format: { name: model.name },
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
if (!visitedTypes.has(model)) {
|
|
57
|
+
visitedTypes.add(model);
|
|
58
|
+
declarations === null || declarations === void 0 ? void 0 : declarations.push(toMessage(model));
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Visits an enum type, converting it into a Protobuf enum definition and adding it if it has not already been visited.
|
|
63
|
+
*/
|
|
64
|
+
function visitEnum(e) {
|
|
65
|
+
const modelPackage = getPackageOfType(program, e);
|
|
66
|
+
const declarations = modelPackage && declarationMap.get(modelPackage);
|
|
67
|
+
if (!visitedTypes.has(e)) {
|
|
68
|
+
visitedTypes.add(e);
|
|
69
|
+
const members = [...e.members.values()];
|
|
70
|
+
// We only support enums where every variant is explicitly assigned an integer value
|
|
71
|
+
if (members.some(({ value: v }) => v === undefined || typeof v !== "number" || !Number.isInteger(v))) {
|
|
72
|
+
reportDiagnostic(program, {
|
|
73
|
+
target: e,
|
|
74
|
+
code: "unconvertible-enum",
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
// we also only support enums where the first value is zero.
|
|
78
|
+
if (members[0].value !== 0) {
|
|
79
|
+
reportDiagnostic(program, {
|
|
80
|
+
target: members[0],
|
|
81
|
+
code: "unconvertible-enum",
|
|
82
|
+
messageId: "no-zero-first",
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
declarations === null || declarations === void 0 ? void 0 : declarations.push(toEnum(e));
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
const importMap = new Map([...packages].map((ns) => [ns, new Set()]));
|
|
89
|
+
function typeWantsImport(program, t, path) {
|
|
90
|
+
var _a;
|
|
91
|
+
const packageNs = getPackageOfType(program, t);
|
|
92
|
+
if (packageNs) {
|
|
93
|
+
(_a = importMap.get(packageNs)) === null || _a === void 0 ? void 0 : _a.add(path);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
const mapImportSourceInformation = new WeakMap();
|
|
97
|
+
const effectiveModelCache = new Map();
|
|
98
|
+
for (const packageNs of packages) {
|
|
99
|
+
addDeclarationsOfPackage(packageNs);
|
|
100
|
+
}
|
|
101
|
+
// Emit a file per package.
|
|
102
|
+
const files = [...packages].map((namespace) => {
|
|
103
|
+
var _a, _b, _c, _d, _e;
|
|
104
|
+
const details = program.stateMap(state.package).get(namespace);
|
|
105
|
+
const packageOptionsRaw = (_a = details === null || details === void 0 ? void 0 : details.properties.get("options")) === null || _a === void 0 ? void 0 : _a.type;
|
|
106
|
+
const packageOptions = [...((_b = packageOptionsRaw === null || packageOptionsRaw === void 0 ? void 0 : packageOptionsRaw.properties.entries()) !== null && _b !== void 0 ? _b : [])]
|
|
107
|
+
.map(([k, { type }]) => {
|
|
108
|
+
// This condition is enforced by the definition of `dec package`
|
|
109
|
+
if (type.kind === "Boolean" || type.kind === "String" || type.kind === "Number") {
|
|
110
|
+
return [k, type.value];
|
|
111
|
+
}
|
|
112
|
+
else
|
|
113
|
+
throw new Error(`Unexpected option type ${type.kind}`);
|
|
114
|
+
})
|
|
115
|
+
.filter((v) => !!v);
|
|
116
|
+
return {
|
|
117
|
+
package: (_d = (_c = details === null || details === void 0 ? void 0 : details.properties.get("name")) === null || _c === void 0 ? void 0 : _c.type) === null || _d === void 0 ? void 0 : _d.value,
|
|
118
|
+
options: Object.fromEntries(packageOptions),
|
|
119
|
+
imports: [...((_e = importMap.get(namespace)) !== null && _e !== void 0 ? _e : [])],
|
|
120
|
+
declarations: declarationMap.get(namespace),
|
|
121
|
+
source: namespace,
|
|
122
|
+
};
|
|
123
|
+
});
|
|
124
|
+
checkForNamespaceCollisions(files);
|
|
125
|
+
return files;
|
|
126
|
+
/**
|
|
127
|
+
* Recursively searches a namespace for declarations that should be reified as Protobuf.
|
|
128
|
+
*
|
|
129
|
+
* @param namespace - the namespace to analyze
|
|
130
|
+
* @returns an array of declarations
|
|
131
|
+
*/
|
|
132
|
+
function addDeclarationsOfPackage(namespace) {
|
|
133
|
+
const models = [...namespace.models.values()];
|
|
134
|
+
// Eagerly visit all models in the namespace.
|
|
135
|
+
for (const model of models) {
|
|
136
|
+
// Don't eagerly visit externs
|
|
137
|
+
if (
|
|
138
|
+
// Don't eagerly visit externs
|
|
139
|
+
!program.stateMap(state.externRef).has(model) &&
|
|
140
|
+
// Only eagerly visit models where every field has a field index annotation.
|
|
141
|
+
([...model.properties.values()].every((p) => program.stateMap(state.fieldIndex).has(p)) ||
|
|
142
|
+
// OR where the model has been explicitly marked as a message.
|
|
143
|
+
program.stateSet(state.message).has(model))) {
|
|
144
|
+
visitModel(model, model);
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
const interfacesInNamespace = new Set(serviceInterfaces.filter((iface) => isDeclaredInNamespace(iface, namespace)));
|
|
148
|
+
// Each interface will be reified as a `service` declaration.
|
|
149
|
+
const declarations = declarationMap.get(namespace);
|
|
150
|
+
for (const iface of interfacesInNamespace) {
|
|
151
|
+
declarations.push({
|
|
152
|
+
kind: "service",
|
|
153
|
+
name: iface.name,
|
|
154
|
+
// The service's methods are just projections of the interface operations.
|
|
155
|
+
operations: [...iface.operations.values()].map(toMethodFromOperation),
|
|
156
|
+
});
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
// #region inline helpers
|
|
160
|
+
/**
|
|
161
|
+
* @param operation - the operation to convert
|
|
162
|
+
* @returns a corresponding method declaration
|
|
163
|
+
*/
|
|
164
|
+
function toMethodFromOperation(operation) {
|
|
165
|
+
var _a;
|
|
166
|
+
const streamingMode = (_a = program.stateMap(state.stream).get(operation)) !== null && _a !== void 0 ? _a : 0 /* StreamingMode.None */;
|
|
167
|
+
return {
|
|
168
|
+
kind: "method",
|
|
169
|
+
stream: streamingMode,
|
|
170
|
+
name: capitalize(operation.name),
|
|
171
|
+
input: addImportSourceForProtoIfNeeded(program, addInputParams(operation.parameters, operation), operation, operation.parameters),
|
|
172
|
+
returns: addImportSourceForProtoIfNeeded(program, addReturnType(operation.returnType, operation), operation, operation.returnType),
|
|
173
|
+
};
|
|
174
|
+
}
|
|
175
|
+
/**
|
|
176
|
+
* Checks a parameter Model satisfies the constraints for a Protobuf method input and adds it to the declarations,
|
|
177
|
+
* returning a ProtoRef to the generated named message.
|
|
178
|
+
*
|
|
179
|
+
* @param model - the model to add
|
|
180
|
+
* @returns a reference to the model's message
|
|
181
|
+
*/
|
|
182
|
+
function addInputParams(paramsModel, operation) {
|
|
183
|
+
const effectiveModel = computeEffectiveModel(paramsModel, capitalize(operation.name) + "Request");
|
|
184
|
+
/* c8 ignore start */
|
|
185
|
+
// Not sure if this can or can't actually happen at runtime, but we'll defensively handle it anyway.
|
|
186
|
+
if (!effectiveModel) {
|
|
187
|
+
reportDiagnostic(program, {
|
|
188
|
+
code: "unsupported-input-type",
|
|
189
|
+
messageId: "unconvertible",
|
|
190
|
+
target: paramsModel,
|
|
191
|
+
});
|
|
192
|
+
return unreachable("unsupported input type");
|
|
193
|
+
}
|
|
194
|
+
/* c8 ignore stop */
|
|
195
|
+
return checkExtern(effectiveModel, operation);
|
|
196
|
+
}
|
|
197
|
+
/**
|
|
198
|
+
* Returns an extern ref if the given type is an instance of `Extern`, otherwise returns a ref to the model's name.
|
|
199
|
+
*/
|
|
200
|
+
function checkExtern(model, relativeSource) {
|
|
201
|
+
const extern = program.stateMap(state.externRef).get(model);
|
|
202
|
+
if (extern) {
|
|
203
|
+
typeWantsImport(program, relativeSource, extern[0]);
|
|
204
|
+
return ref(extern[1]);
|
|
205
|
+
}
|
|
206
|
+
return ref(model.name);
|
|
207
|
+
}
|
|
208
|
+
/**
|
|
209
|
+
* Gets a cached intrinsic type. This will also attach desired imports to the relative reference source.
|
|
210
|
+
*/
|
|
211
|
+
function getCachedExternType(program, relativeSource, name) {
|
|
212
|
+
let cache = _protoExternMap.get(program);
|
|
213
|
+
if (!cache) {
|
|
214
|
+
cache = new Map();
|
|
215
|
+
_protoExternMap.set(program, cache);
|
|
216
|
+
}
|
|
217
|
+
const cachedRef = cache.get(name);
|
|
218
|
+
if (cachedRef) {
|
|
219
|
+
const [source, ref] = cachedRef;
|
|
220
|
+
typeWantsImport(program, relativeSource, source);
|
|
221
|
+
return ref;
|
|
222
|
+
}
|
|
223
|
+
const [emptyType, diagnostics] = program.resolveTypeReference(name);
|
|
224
|
+
if (!emptyType) {
|
|
225
|
+
throw new Error(`Could not resolve the empty type: ${diagnostics.map(formatDiagnostic).join("\n")}`);
|
|
226
|
+
}
|
|
227
|
+
const extern = program.stateMap(state.externRef).get(emptyType);
|
|
228
|
+
if (!extern) {
|
|
229
|
+
throw new Error(`Unexpected: '${name}' was resolved but is not an extern type.`);
|
|
230
|
+
}
|
|
231
|
+
const [source, protoName] = extern;
|
|
232
|
+
typeWantsImport(program, relativeSource, source);
|
|
233
|
+
const result = ref(protoName);
|
|
234
|
+
cache.set(name, [source, result]);
|
|
235
|
+
return result;
|
|
236
|
+
}
|
|
237
|
+
/**
|
|
238
|
+
* Checks that a return type is a Model and converts it to a message, adding it to the declarations and returning
|
|
239
|
+
* a reference to its name.
|
|
240
|
+
*
|
|
241
|
+
* @param t - the model to add
|
|
242
|
+
* @param operationName - the name of the originating operation, used to compute a synthetic model name if required
|
|
243
|
+
* @returns a reference to the model's message
|
|
244
|
+
*/
|
|
245
|
+
function addReturnType(t, operation) {
|
|
246
|
+
switch (t.kind) {
|
|
247
|
+
case "Model":
|
|
248
|
+
return addReturnModel(t, operation);
|
|
249
|
+
case "Intrinsic":
|
|
250
|
+
return addIntrinsicType(t, operation);
|
|
251
|
+
/* eslint-ignore-next-line no-fallthrough */
|
|
252
|
+
default:
|
|
253
|
+
reportDiagnostic(program, {
|
|
254
|
+
code: "unsupported-return-type",
|
|
255
|
+
target: getOperationReturnSyntaxTarget(operation),
|
|
256
|
+
});
|
|
257
|
+
return unreachable("unsupported return type");
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
/**
|
|
261
|
+
* Adds an intrinsic type. Intrinsics are assumed to map to Extern types, so this will add the appropriate import.
|
|
262
|
+
*
|
|
263
|
+
* @param t - the intrinsic type to add
|
|
264
|
+
* @param relativeSource - the relative source of the type
|
|
265
|
+
* @returns a reference to the type's message
|
|
266
|
+
*/
|
|
267
|
+
function addIntrinsicType(t, relativeSource) {
|
|
268
|
+
switch (t.name) {
|
|
269
|
+
case "unknown":
|
|
270
|
+
return getCachedExternType(program, relativeSource, "TypeSpec.Protobuf.WellKnown.Any");
|
|
271
|
+
case "void": {
|
|
272
|
+
return getCachedExternType(program, relativeSource, "TypeSpec.Protobuf.WellKnown.Empty");
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
reportDiagnostic(program, {
|
|
276
|
+
code: "unsupported-intrinsic",
|
|
277
|
+
format: { type: t.name },
|
|
278
|
+
target: t,
|
|
279
|
+
});
|
|
280
|
+
return unreachable("unsupported intrinsic type");
|
|
281
|
+
}
|
|
282
|
+
/**
|
|
283
|
+
* Converts a TypeSpec Model to a Protobuf Ref in return position, adding a corresponding message if necessary.
|
|
284
|
+
*
|
|
285
|
+
* @param m - the model to add to the Protofile.
|
|
286
|
+
* @returns a Protobuf reference to the model
|
|
287
|
+
*/
|
|
288
|
+
function addReturnModel(m, operation) {
|
|
289
|
+
const extern = program.stateMap(state.externRef).get(m);
|
|
290
|
+
if (extern) {
|
|
291
|
+
typeWantsImport(program, operation, extern[0]);
|
|
292
|
+
return ref(extern[1]);
|
|
293
|
+
}
|
|
294
|
+
const effectiveModel = computeEffectiveModel(m, capitalize(operation.name) + "Response");
|
|
295
|
+
if (effectiveModel) {
|
|
296
|
+
return ref(effectiveModel.name);
|
|
297
|
+
}
|
|
298
|
+
reportDiagnostic(program, {
|
|
299
|
+
code: "unsupported-return-type",
|
|
300
|
+
target: getOperationReturnSyntaxTarget(operation),
|
|
301
|
+
});
|
|
302
|
+
return unreachable("unsupported return type");
|
|
303
|
+
}
|
|
304
|
+
/**
|
|
305
|
+
* Converts a TypeSpec type to a Protobuf type, adding a corresponding message if necessary.
|
|
306
|
+
*
|
|
307
|
+
* @param t - the type to add to the ProtoFile.
|
|
308
|
+
* @returns a Protobuf type corresponding to the given type
|
|
309
|
+
*/
|
|
310
|
+
function addType(t, relativeSource) {
|
|
311
|
+
// Exit early if this type is an extern.
|
|
312
|
+
const extern = program.stateMap(state.externRef).get(t);
|
|
313
|
+
if (extern) {
|
|
314
|
+
typeWantsImport(program, relativeSource, extern[0]);
|
|
315
|
+
return ref(extern[1]);
|
|
316
|
+
}
|
|
317
|
+
if (isMap(program, t)) {
|
|
318
|
+
const mapType = mapToProto(t, relativeSource);
|
|
319
|
+
mapImportSourceInformation.set(mapType, [relativeSource, t]);
|
|
320
|
+
return mapType;
|
|
321
|
+
}
|
|
322
|
+
// Arrays transform into repeated fields, so we'll silently replace `t` with the array's member if this is an array.
|
|
323
|
+
// The `repeated` keyword will be added when the field is composed.
|
|
324
|
+
if (isArray(t)) {
|
|
325
|
+
return arrayToProto(t, relativeSource);
|
|
326
|
+
}
|
|
327
|
+
switch (t.kind) {
|
|
328
|
+
case "Model":
|
|
329
|
+
// If we came from another model and this model is anonymous, then we can't reference it by name.
|
|
330
|
+
if (t.name === "" && relativeSource.kind === "Model") {
|
|
331
|
+
reportDiagnostic(program, {
|
|
332
|
+
code: "anonymous-model",
|
|
333
|
+
target: t,
|
|
334
|
+
});
|
|
335
|
+
return unreachable("anonymous model");
|
|
336
|
+
}
|
|
337
|
+
visitModel(t, relativeSource);
|
|
338
|
+
return ref(t.name);
|
|
339
|
+
case "Enum":
|
|
340
|
+
visitEnum(t);
|
|
341
|
+
return ref(t.name);
|
|
342
|
+
case "Scalar":
|
|
343
|
+
return scalarToProto(t);
|
|
344
|
+
case "Intrinsic":
|
|
345
|
+
return addIntrinsicType(t, relativeSource);
|
|
346
|
+
default:
|
|
347
|
+
reportDiagnostic(program, {
|
|
348
|
+
code: "unsupported-field-type",
|
|
349
|
+
messageId: "unconvertible",
|
|
350
|
+
format: {
|
|
351
|
+
type: t.kind,
|
|
352
|
+
},
|
|
353
|
+
target: t,
|
|
354
|
+
});
|
|
355
|
+
return unreachable("unsupported field type");
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
function mapToProto(t, relativeSource) {
|
|
359
|
+
const [keyType, valueType] = t.templateMapper.args;
|
|
360
|
+
// A map's value cannot be another map.
|
|
361
|
+
if (isMap(program, valueType)) {
|
|
362
|
+
reportDiagnostic(program, {
|
|
363
|
+
code: "unsupported-field-type",
|
|
364
|
+
messageId: "recursive-map",
|
|
365
|
+
target: valueType,
|
|
366
|
+
});
|
|
367
|
+
return unreachable("recursive map");
|
|
368
|
+
}
|
|
369
|
+
// This is a core compile error.
|
|
370
|
+
if (!keyType || !valueType)
|
|
371
|
+
return unreachable("nonexistent map key or value type");
|
|
372
|
+
// Key constraint (integral | string) is enforced by the type constraint on the `Map<>` type.
|
|
373
|
+
const keyProto = addType(keyType, relativeSource);
|
|
374
|
+
const valueProto = addType(valueType, relativeSource);
|
|
375
|
+
return map(keyProto[1], valueProto);
|
|
376
|
+
}
|
|
377
|
+
function arrayToProto(t, relativeSource) {
|
|
378
|
+
const valueType = t.templateMapper.args[0];
|
|
379
|
+
// Nested arrays are not supported.
|
|
380
|
+
if (isArray(valueType)) {
|
|
381
|
+
reportDiagnostic(program, {
|
|
382
|
+
code: "nested-array",
|
|
383
|
+
target: valueType,
|
|
384
|
+
});
|
|
385
|
+
return ref("<unreachable>");
|
|
386
|
+
}
|
|
387
|
+
return addType(valueType, relativeSource);
|
|
388
|
+
}
|
|
389
|
+
function getProtoScalarsMap(program) {
|
|
390
|
+
// The type references are different object identities in different programs, so we need to cache the map per program.
|
|
391
|
+
// This really only affects tests in our current use case, but someone could be using the compiler API to compile
|
|
392
|
+
// multiple programs and it also affects that.
|
|
393
|
+
let scalarMap;
|
|
394
|
+
if (_protoScalarsMap.has(program)) {
|
|
395
|
+
scalarMap = _protoScalarsMap.get(program);
|
|
396
|
+
}
|
|
397
|
+
else {
|
|
398
|
+
const entries = [
|
|
399
|
+
[program.resolveTypeReference("TypeSpec.bytes"), scalar("bytes")],
|
|
400
|
+
[program.resolveTypeReference("TypeSpec.boolean"), scalar("bool")],
|
|
401
|
+
[program.resolveTypeReference("TypeSpec.string"), scalar("string")],
|
|
402
|
+
[program.resolveTypeReference("TypeSpec.int32"), scalar("int32")],
|
|
403
|
+
[program.resolveTypeReference("TypeSpec.int64"), scalar("int64")],
|
|
404
|
+
[program.resolveTypeReference("TypeSpec.uint32"), scalar("uint32")],
|
|
405
|
+
[program.resolveTypeReference("TypeSpec.uint64"), scalar("uint64")],
|
|
406
|
+
[program.resolveTypeReference("TypeSpec.float32"), scalar("float")],
|
|
407
|
+
[program.resolveTypeReference("TypeSpec.float64"), scalar("double")],
|
|
408
|
+
[program.resolveTypeReference("TypeSpec.Protobuf.sfixed32"), scalar("sfixed32")],
|
|
409
|
+
[program.resolveTypeReference("TypeSpec.Protobuf.sfixed64"), scalar("sfixed64")],
|
|
410
|
+
[program.resolveTypeReference("TypeSpec.Protobuf.sint32"), scalar("sint32")],
|
|
411
|
+
[program.resolveTypeReference("TypeSpec.Protobuf.sint64"), scalar("sint64")],
|
|
412
|
+
[program.resolveTypeReference("TypeSpec.Protobuf.fixed32"), scalar("fixed32")],
|
|
413
|
+
[program.resolveTypeReference("TypeSpec.Protobuf.fixed64"), scalar("fixed64")],
|
|
414
|
+
];
|
|
415
|
+
for (const [[type, diagnostics]] of entries) {
|
|
416
|
+
if (!type) {
|
|
417
|
+
const diagnosticString = diagnostics.map(formatDiagnostic).join("\n");
|
|
418
|
+
throw new Error(`Failed to construct TypeSpec -> Protobuf scalar map. Unexpected failure to resolve TypeSpec scalar: ${diagnosticString}`);
|
|
419
|
+
}
|
|
420
|
+
}
|
|
421
|
+
scalarMap = new Map(entries.map(([[type], scalar]) => [type, scalar]));
|
|
422
|
+
_protoScalarsMap.set(program, scalarMap);
|
|
423
|
+
}
|
|
424
|
+
// Lazy initialize this map of known proto scalars.
|
|
425
|
+
return scalarMap;
|
|
426
|
+
}
|
|
427
|
+
function scalarToProto(t) {
|
|
428
|
+
const fullName = getTypeName(t);
|
|
429
|
+
const protoType = getProtoScalarsMap(program).get(t);
|
|
430
|
+
if (!protoType) {
|
|
431
|
+
if (t.baseScalar) {
|
|
432
|
+
return scalarToProto(t.baseScalar);
|
|
433
|
+
}
|
|
434
|
+
else {
|
|
435
|
+
reportDiagnostic(program, {
|
|
436
|
+
code: "unsupported-field-type",
|
|
437
|
+
messageId: "unknown-scalar",
|
|
438
|
+
format: {
|
|
439
|
+
name: fullName,
|
|
440
|
+
},
|
|
441
|
+
target: t,
|
|
442
|
+
});
|
|
443
|
+
return unreachable("unknown scalar");
|
|
444
|
+
}
|
|
445
|
+
}
|
|
446
|
+
return protoType;
|
|
447
|
+
}
|
|
448
|
+
function computeEffectiveModel(model, anonymousModelName) {
|
|
449
|
+
if (effectiveModelCache.has(model))
|
|
450
|
+
return effectiveModelCache.get(model);
|
|
451
|
+
let effectiveModel = getEffectiveModelType(program, model);
|
|
452
|
+
if (effectiveModel.name === "") {
|
|
453
|
+
// Name the model automatically if it is anonymous
|
|
454
|
+
effectiveModel = program.checker.createAndFinishType({
|
|
455
|
+
...model,
|
|
456
|
+
name: anonymousModelName,
|
|
457
|
+
});
|
|
458
|
+
}
|
|
459
|
+
if (!program.stateMap(state.externRef).has(effectiveModel)) {
|
|
460
|
+
visitModel(effectiveModel, model);
|
|
461
|
+
}
|
|
462
|
+
effectiveModelCache.set(model, effectiveModel);
|
|
463
|
+
return effectiveModel;
|
|
464
|
+
}
|
|
465
|
+
// #endregion
|
|
466
|
+
function checkForNamespaceCollisions(files) {
|
|
467
|
+
var _a;
|
|
468
|
+
const namespaces = new Set();
|
|
469
|
+
for (const file of files) {
|
|
470
|
+
if (namespaces.has(file.package)) {
|
|
471
|
+
reportDiagnostic(program, {
|
|
472
|
+
code: "namespace-collision",
|
|
473
|
+
format: {
|
|
474
|
+
name: (_a = `"${file.package}"`) !== null && _a !== void 0 ? _a : "<empty>",
|
|
475
|
+
},
|
|
476
|
+
target: file.source,
|
|
477
|
+
});
|
|
478
|
+
}
|
|
479
|
+
namespaces.add(file.package);
|
|
480
|
+
}
|
|
481
|
+
}
|
|
482
|
+
/**
|
|
483
|
+
* @param model - the Model to convert
|
|
484
|
+
* @returns a corresponding message declaration
|
|
485
|
+
*/
|
|
486
|
+
function toMessage(model) {
|
|
487
|
+
return {
|
|
488
|
+
kind: "message",
|
|
489
|
+
name: model.name,
|
|
490
|
+
reservations: program.stateMap(state.reserve).get(model),
|
|
491
|
+
declarations: [...model.properties.values()].map((f) => toMessageBodyDeclaration(f, model)),
|
|
492
|
+
};
|
|
493
|
+
}
|
|
494
|
+
/**
|
|
495
|
+
* @param property - the ModelProperty to convert
|
|
496
|
+
* @returns a corresponding declaration
|
|
497
|
+
*/
|
|
498
|
+
function toMessageBodyDeclaration(property, model) {
|
|
499
|
+
var _a;
|
|
500
|
+
if (property.type.kind === "Union") {
|
|
501
|
+
// Unions are difficult to represent in protobuf, so for now we don't support them.
|
|
502
|
+
// See : https://github.com/microsoft/typespec/issues/1854
|
|
503
|
+
reportDiagnostic(program, {
|
|
504
|
+
code: "unsupported-field-type",
|
|
505
|
+
messageId: "union",
|
|
506
|
+
target: property,
|
|
507
|
+
});
|
|
508
|
+
return unreachable("union");
|
|
509
|
+
}
|
|
510
|
+
const fieldIndex = program.stateMap(state.fieldIndex).get(property);
|
|
511
|
+
const fieldIndexNode = (_a = property.decorators.find((d) => d.decorator === $field)) === null || _a === void 0 ? void 0 : _a.args[0].node;
|
|
512
|
+
if (fieldIndex === undefined) {
|
|
513
|
+
reportDiagnostic(program, {
|
|
514
|
+
code: "field-index",
|
|
515
|
+
messageId: "missing",
|
|
516
|
+
format: {
|
|
517
|
+
name: property.name,
|
|
518
|
+
},
|
|
519
|
+
target: property,
|
|
520
|
+
});
|
|
521
|
+
}
|
|
522
|
+
if (fieldIndex && !fieldIndexNode)
|
|
523
|
+
throw new Error("Failed to recover field decorator argument.");
|
|
524
|
+
const reservations = program.stateMap(state.reserve).get(model);
|
|
525
|
+
if (reservations) {
|
|
526
|
+
for (const reservation of reservations) {
|
|
527
|
+
if (typeof reservation === "string" && reservation === property.name) {
|
|
528
|
+
reportDiagnostic(program, {
|
|
529
|
+
code: "field-name",
|
|
530
|
+
messageId: "user-reserved",
|
|
531
|
+
format: {
|
|
532
|
+
name: property.name,
|
|
533
|
+
},
|
|
534
|
+
target: getPropertyNameSyntaxTarget(property),
|
|
535
|
+
});
|
|
536
|
+
}
|
|
537
|
+
else if (fieldIndex !== undefined &&
|
|
538
|
+
typeof reservation === "number" &&
|
|
539
|
+
reservation === fieldIndex) {
|
|
540
|
+
reportDiagnostic(program, {
|
|
541
|
+
code: "field-index",
|
|
542
|
+
messageId: "user-reserved",
|
|
543
|
+
format: {
|
|
544
|
+
index: fieldIndex.toString(),
|
|
545
|
+
},
|
|
546
|
+
// Fail over to using the model if the field index node is missing... this should never occur but it's the
|
|
547
|
+
// simplest way to satisfy the type system.
|
|
548
|
+
target: fieldIndexNode !== null && fieldIndexNode !== void 0 ? fieldIndexNode : model,
|
|
549
|
+
});
|
|
550
|
+
}
|
|
551
|
+
else if (fieldIndex !== undefined &&
|
|
552
|
+
Array.isArray(reservation) &&
|
|
553
|
+
fieldIndex >= reservation[0] &&
|
|
554
|
+
fieldIndex <= reservation[1]) {
|
|
555
|
+
reportDiagnostic(program, {
|
|
556
|
+
code: "field-index",
|
|
557
|
+
messageId: "user-reserved-range",
|
|
558
|
+
format: {
|
|
559
|
+
index: fieldIndex.toString(),
|
|
560
|
+
},
|
|
561
|
+
target: fieldIndexNode !== null && fieldIndexNode !== void 0 ? fieldIndexNode : model,
|
|
562
|
+
});
|
|
563
|
+
}
|
|
564
|
+
}
|
|
565
|
+
}
|
|
566
|
+
const field = {
|
|
567
|
+
kind: "field",
|
|
568
|
+
name: property.name,
|
|
569
|
+
type: addImportSourceForProtoIfNeeded(program, addType(property.type, model), model, property.type),
|
|
570
|
+
index: program.stateMap(state.fieldIndex).get(property),
|
|
571
|
+
};
|
|
572
|
+
// Determine if the property type is an array
|
|
573
|
+
if (isArray(property.type))
|
|
574
|
+
field.repeated = true;
|
|
575
|
+
return field;
|
|
576
|
+
}
|
|
577
|
+
/**
|
|
578
|
+
* @param e - the Enum to convert
|
|
579
|
+
* @returns a corresponding protobuf enum declaration
|
|
580
|
+
*
|
|
581
|
+
* INVARIANT: the enum's members must be integer values
|
|
582
|
+
*/
|
|
583
|
+
function toEnum(e) {
|
|
584
|
+
const needsAlias = new Set([...e.members.values()].map((v) => v.value)).size !== e.members.size;
|
|
585
|
+
return {
|
|
586
|
+
kind: "enum",
|
|
587
|
+
name: e.name,
|
|
588
|
+
allowAlias: needsAlias,
|
|
589
|
+
variants: [...e.members.values()].map(({ name, value }) => [name, value]),
|
|
590
|
+
};
|
|
591
|
+
}
|
|
592
|
+
function getPackageOfType(program, t) {
|
|
593
|
+
/* c8 ignore start */
|
|
594
|
+
var _a;
|
|
595
|
+
// Most of this should be unreachable, but we'll guard it with diagnostics anyway in case of eventual synthetic types.
|
|
596
|
+
switch (t.kind) {
|
|
597
|
+
case "Intrinsic":
|
|
598
|
+
// Intrinsics are all handled explicitly.
|
|
599
|
+
return null;
|
|
600
|
+
case "Enum":
|
|
601
|
+
case "Model":
|
|
602
|
+
case "Union":
|
|
603
|
+
case "Interface":
|
|
604
|
+
if (!t.namespace) {
|
|
605
|
+
return null;
|
|
606
|
+
}
|
|
607
|
+
else {
|
|
608
|
+
return getPackageOfType(program, t.namespace);
|
|
609
|
+
}
|
|
610
|
+
case "Operation": {
|
|
611
|
+
const logicalParent = (_a = t.interface) !== null && _a !== void 0 ? _a : t.namespace;
|
|
612
|
+
if (!logicalParent) {
|
|
613
|
+
return null;
|
|
614
|
+
}
|
|
615
|
+
else {
|
|
616
|
+
return getPackageOfType(program, logicalParent);
|
|
617
|
+
}
|
|
618
|
+
}
|
|
619
|
+
case "Namespace":
|
|
620
|
+
if (packages.has(t))
|
|
621
|
+
return t;
|
|
622
|
+
if (!t.namespace) {
|
|
623
|
+
return null;
|
|
624
|
+
}
|
|
625
|
+
else {
|
|
626
|
+
return getPackageOfType(program, t.namespace);
|
|
627
|
+
}
|
|
628
|
+
}
|
|
629
|
+
/* c8 ignore stop */
|
|
630
|
+
}
|
|
631
|
+
function addImportSourceForProtoIfNeeded(program, pt, dependent, dependency) {
|
|
632
|
+
{
|
|
633
|
+
// Early escape for intrinsics
|
|
634
|
+
if (dependency.kind === "Intrinsic") {
|
|
635
|
+
// Intrinsics and imports are handled explicitly by the emitter.
|
|
636
|
+
return pt;
|
|
637
|
+
}
|
|
638
|
+
}
|
|
639
|
+
{
|
|
640
|
+
// Early escape for externs
|
|
641
|
+
let effectiveModel;
|
|
642
|
+
if (program.stateMap(state.externRef).has(dependency) ||
|
|
643
|
+
(dependency.kind === "Model" &&
|
|
644
|
+
(effectiveModel = effectiveModelCache.get(dependency)) &&
|
|
645
|
+
program.stateMap(state.externRef).has(effectiveModel))) {
|
|
646
|
+
return pt;
|
|
647
|
+
}
|
|
648
|
+
}
|
|
649
|
+
if (isArray(dependency)) {
|
|
650
|
+
return addImportSourceForProtoIfNeeded(program, pt, dependent, dependency.templateMapper.args[0]);
|
|
651
|
+
}
|
|
652
|
+
try {
|
|
653
|
+
// If we had an error producing an "unreachable" type, we would actually reach it during validation below, so the
|
|
654
|
+
// try/catch allows us to pass the unreachable back up the chain.
|
|
655
|
+
return matchType(pt, {
|
|
656
|
+
map(k, v) {
|
|
657
|
+
const mapInfo = mapImportSourceInformation.get(pt);
|
|
658
|
+
return mapInfo !== undefined
|
|
659
|
+
? map(k, addImportSourceForProtoIfNeeded(program, v, mapInfo[0], mapInfo[1])
|
|
660
|
+
// Anything else is unreachable by construction.
|
|
661
|
+
)
|
|
662
|
+
: pt;
|
|
663
|
+
},
|
|
664
|
+
scalar() {
|
|
665
|
+
return pt;
|
|
666
|
+
},
|
|
667
|
+
ref(r) {
|
|
668
|
+
var _a, _b, _c, _d;
|
|
669
|
+
const [dependentPackage, dependencyPackage] = [
|
|
670
|
+
getPackageOfType(program, dependent),
|
|
671
|
+
getPackageOfType(program, dependency),
|
|
672
|
+
];
|
|
673
|
+
if (dependentPackage === null ||
|
|
674
|
+
dependencyPackage === null ||
|
|
675
|
+
dependentPackage === dependencyPackage)
|
|
676
|
+
return pt;
|
|
677
|
+
const dependencyDetails = program.stateMap(state.package).get(dependencyPackage);
|
|
678
|
+
const dependencyPackageName = (_b = (_a = dependencyDetails === null || dependencyDetails === void 0 ? void 0 : dependencyDetails.properties.get("name")) === null || _a === void 0 ? void 0 : _a.type) === null || _b === void 0 ? void 0 : _b.value;
|
|
679
|
+
const dependencyPackagePrefix = dependencyPackageName === undefined || dependencyPackageName === ""
|
|
680
|
+
? ""
|
|
681
|
+
: dependencyPackageName + ".";
|
|
682
|
+
const dependencyFileName = ((_c = dependencyPackageName === null || dependencyPackageName === void 0 ? void 0 : dependencyPackageName.split(".")) !== null && _c !== void 0 ? _c : ["main"]).join("/") + ".proto";
|
|
683
|
+
(_d = importMap.get(dependentPackage)) === null || _d === void 0 ? void 0 : _d.add(dependencyFileName);
|
|
684
|
+
return ref(dependencyPackagePrefix + r);
|
|
685
|
+
},
|
|
686
|
+
});
|
|
687
|
+
}
|
|
688
|
+
catch {
|
|
689
|
+
return pt;
|
|
690
|
+
}
|
|
691
|
+
}
|
|
692
|
+
}
|
|
693
|
+
function isArray(t) {
|
|
694
|
+
var _a;
|
|
695
|
+
return t.kind === "Model" && t.name === "Array" && ((_a = t.namespace) === null || _a === void 0 ? void 0 : _a.name) === "TypeSpec";
|
|
696
|
+
}
|
|
697
|
+
/**
|
|
698
|
+
* Simple utility function to capitalize a string.
|
|
699
|
+
*/
|
|
700
|
+
function capitalize(s) {
|
|
701
|
+
return (s.slice(0, 1).toUpperCase() + s.slice(1));
|
|
702
|
+
}
|
|
703
|
+
/**
|
|
704
|
+
* Gets the syntactic return type target for an operation.
|
|
705
|
+
*
|
|
706
|
+
* Helps us squiggle the right things for operation return types.
|
|
707
|
+
*
|
|
708
|
+
* See https://github.com/microsoft/typespec/issues/1650. This issue tracks helpers for doing this without requiring
|
|
709
|
+
* emitters to implement this functionality.
|
|
710
|
+
*/
|
|
711
|
+
function getOperationReturnSyntaxTarget(op) {
|
|
712
|
+
const signature = op.node.signature;
|
|
713
|
+
switch (signature.kind) {
|
|
714
|
+
case SyntaxKind.OperationSignatureDeclaration:
|
|
715
|
+
return signature.returnType;
|
|
716
|
+
case SyntaxKind.OperationSignatureReference:
|
|
717
|
+
return op;
|
|
718
|
+
default:
|
|
719
|
+
const __exhaust = signature;
|
|
720
|
+
throw new Error(`Internal Emitter Error: reached unreachable operation signature: ${op.node.signature.kind}`);
|
|
721
|
+
}
|
|
722
|
+
}
|
|
723
|
+
/**
|
|
724
|
+
* Gets the syntactic position of a model property name.
|
|
725
|
+
*
|
|
726
|
+
* See https://github.com/microsoft/typespec/issues/1650. This issue tracks helpers for doing this without requiring
|
|
727
|
+
* emitters to implement this functionality.
|
|
728
|
+
*/
|
|
729
|
+
function getPropertyNameSyntaxTarget(property) {
|
|
730
|
+
const node = property.node;
|
|
731
|
+
switch (node.kind) {
|
|
732
|
+
case SyntaxKind.ModelProperty:
|
|
733
|
+
return node.id;
|
|
734
|
+
case SyntaxKind.ModelSpreadProperty:
|
|
735
|
+
return node;
|
|
736
|
+
case SyntaxKind.ProjectionModelProperty:
|
|
737
|
+
case SyntaxKind.ProjectionModelSpreadProperty:
|
|
738
|
+
return property;
|
|
739
|
+
default:
|
|
740
|
+
const __exhaust = node;
|
|
741
|
+
throw new Error(`Internal Emitter Error: reached unreachable model property node: ${property.node.kind}`);
|
|
742
|
+
}
|
|
743
|
+
}
|
|
744
|
+
//# sourceMappingURL=index.js.map
|