@typespec/http-server-js 0.58.0-alpha.13-dev.2 → 0.58.0-alpha.13-dev.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.testignore +0 -4
- package/dist/src/common/enum.d.ts.map +1 -1
- package/dist/src/common/enum.js +2 -1
- package/dist/src/common/enum.js.map +1 -1
- package/dist/src/common/serialization/index.d.ts +2 -2
- package/dist/src/common/serialization/index.d.ts.map +1 -1
- package/dist/src/common/serialization/index.js +5 -1
- package/dist/src/common/serialization/index.js.map +1 -1
- package/dist/src/common/serialization/json.d.ts.map +1 -1
- package/dist/src/common/serialization/json.js +20 -6
- package/dist/src/common/serialization/json.js.map +1 -1
- package/dist/src/http/server/index.d.ts.map +1 -1
- package/dist/src/http/server/index.js +75 -1
- package/dist/src/http/server/index.js.map +1 -1
- package/dist/src/http/server/router.d.ts.map +1 -1
- package/dist/src/http/server/router.js +35 -43
- package/dist/src/http/server/router.js.map +1 -1
- package/dist/src/lib.d.ts +10 -1
- package/dist/src/lib.d.ts.map +1 -1
- package/dist/src/lib.js +6 -0
- package/dist/src/lib.js.map +1 -1
- package/dist/src/util/differentiate.d.ts +25 -2
- package/dist/src/util/differentiate.d.ts.map +1 -1
- package/dist/src/util/differentiate.js +25 -33
- package/dist/src/util/differentiate.js.map +1 -1
- package/package.json +1 -1
- package/src/common/enum.ts +2 -1
- package/src/common/serialization/index.ts +9 -3
- package/src/common/serialization/json.ts +25 -8
- package/src/http/server/index.ts +83 -1
- package/src/http/server/router.ts +45 -58
- package/src/lib.ts +6 -0
- package/src/util/differentiate.ts +58 -23
- package/temp/tsconfig.tsbuildinfo +1 -1
package/src/http/server/index.ts
CHANGED
|
@@ -41,6 +41,7 @@ import { module as headerHelpers } from "../../../generated-defs/helpers/header.
|
|
|
41
41
|
import { module as httpHelpers } from "../../../generated-defs/helpers/http.js";
|
|
42
42
|
import { getJsScalar } from "../../common/scalar.js";
|
|
43
43
|
import { requiresJsonSerialization } from "../../common/serialization/json.js";
|
|
44
|
+
import { getFullyQualifiedTypeName } from "../../util/name.js";
|
|
44
45
|
|
|
45
46
|
const DEFAULT_CONTENT_TYPE = "application/json";
|
|
46
47
|
|
|
@@ -296,7 +297,7 @@ function* emitRawServerOperation(
|
|
|
296
297
|
|
|
297
298
|
break;
|
|
298
299
|
}
|
|
299
|
-
case "multipart/form-data":
|
|
300
|
+
case "multipart/form-data": {
|
|
300
301
|
if (body.bodyKind === "multipart") {
|
|
301
302
|
yield* indent(
|
|
302
303
|
emitMultipart(ctx, module, operation, body, names.ctx, bodyName, bodyTypeName),
|
|
@@ -305,7 +306,88 @@ function* emitRawServerOperation(
|
|
|
305
306
|
yield* indent(emitMultipartLegacy(names.ctx, bodyName, bodyTypeName));
|
|
306
307
|
}
|
|
307
308
|
break;
|
|
309
|
+
}
|
|
310
|
+
case "text/plain": {
|
|
311
|
+
const string = ctx.program.checker.getStdType("string");
|
|
312
|
+
const [assignable] = ctx.program.checker.isTypeAssignableTo(
|
|
313
|
+
body.type,
|
|
314
|
+
string,
|
|
315
|
+
body.property ?? body.type,
|
|
316
|
+
);
|
|
317
|
+
if (!assignable) {
|
|
318
|
+
const name =
|
|
319
|
+
("namespace" in body.type &&
|
|
320
|
+
body.type.namespace &&
|
|
321
|
+
getFullyQualifiedTypeName(body.type)) ||
|
|
322
|
+
("name" in body.type && typeof body.type.name === "string" && body.type.name) ||
|
|
323
|
+
"<unknown>";
|
|
324
|
+
reportDiagnostic(ctx.program, {
|
|
325
|
+
code: "unrecognized-media-type",
|
|
326
|
+
target: body.property ?? body.type,
|
|
327
|
+
format: {
|
|
328
|
+
mediaType: contentType,
|
|
329
|
+
type: name,
|
|
330
|
+
},
|
|
331
|
+
});
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
yield ` const ${bodyName} = await new Promise(function parse${bodyNameCase.pascalCase}(resolve, reject) {`;
|
|
335
|
+
yield ` const chunks: Array<Buffer> = [];`;
|
|
336
|
+
yield ` ${names.ctx}.request.on("data", function appendChunk(chunk) { chunks.push(chunk); });`;
|
|
337
|
+
yield ` ${names.ctx}.request.on("end", function finalize() {`;
|
|
338
|
+
yield ` try {`;
|
|
339
|
+
yield ` const body = Buffer.concat(chunks).toString();`;
|
|
340
|
+
yield ` resolve(body);`;
|
|
341
|
+
yield ` } catch (e) {`;
|
|
342
|
+
yield ` ${names.ctx}.errorHandlers.onInvalidRequest(`;
|
|
343
|
+
yield ` ${names.ctx},`;
|
|
344
|
+
yield ` ${JSON.stringify(operation.path)},`;
|
|
345
|
+
yield ` "invalid text in request body",`;
|
|
346
|
+
yield ` );`;
|
|
347
|
+
yield ` reject(e);`;
|
|
348
|
+
yield ` }`;
|
|
349
|
+
yield ` });`;
|
|
350
|
+
yield ` ${names.ctx}.request.on("error", reject);`;
|
|
351
|
+
yield ` }) as string;`;
|
|
352
|
+
yield "";
|
|
353
|
+
break;
|
|
354
|
+
}
|
|
355
|
+
case "application/octet-stream":
|
|
308
356
|
default:
|
|
357
|
+
{
|
|
358
|
+
if (!ctx.program.checker.isStdType(body.type, "bytes")) {
|
|
359
|
+
const name =
|
|
360
|
+
("namespace" in body.type &&
|
|
361
|
+
body.type.namespace &&
|
|
362
|
+
getFullyQualifiedTypeName(body.type)) ||
|
|
363
|
+
("name" in body.type && typeof body.type.name === "string" && body.type.name) ||
|
|
364
|
+
"<unknown>";
|
|
365
|
+
|
|
366
|
+
reportDiagnostic(ctx.program, {
|
|
367
|
+
code: "unrecognized-media-type",
|
|
368
|
+
target: body.property ?? body.type,
|
|
369
|
+
format: {
|
|
370
|
+
mediaType: contentType,
|
|
371
|
+
type: name,
|
|
372
|
+
},
|
|
373
|
+
});
|
|
374
|
+
}
|
|
375
|
+
yield ` const ${bodyName} = await new Promise(function parse${bodyNameCase.pascalCase}(resolve, reject) {`;
|
|
376
|
+
yield ` const chunks: Array<Buffer> = [];`;
|
|
377
|
+
yield ` ${names.ctx}.request.on("data", function appendChunk(chunk) { chunks.push(chunk); });`;
|
|
378
|
+
yield ` ${names.ctx}.request.on("end", function finalize() {`;
|
|
379
|
+
yield ` try {`;
|
|
380
|
+
yield ` const body = Buffer.concat(chunks);`;
|
|
381
|
+
yield ` resolve(body);`;
|
|
382
|
+
yield ` } catch (e) {`;
|
|
383
|
+
yield ` reject(e);`;
|
|
384
|
+
yield ` }`;
|
|
385
|
+
yield ` });`;
|
|
386
|
+
yield ` ${names.ctx}.request.on("error", reject);`;
|
|
387
|
+
yield ` }) as Buffer;`;
|
|
388
|
+
yield "";
|
|
389
|
+
break;
|
|
390
|
+
}
|
|
309
391
|
throw new UnimplementedError(`request deserialization for content-type: '${contentType}'`);
|
|
310
392
|
}
|
|
311
393
|
|
|
@@ -7,7 +7,8 @@ import {
|
|
|
7
7
|
HttpService,
|
|
8
8
|
HttpVerb,
|
|
9
9
|
OperationContainer,
|
|
10
|
-
|
|
10
|
+
getHeaderFieldName,
|
|
11
|
+
isHeader,
|
|
11
12
|
} from "@typespec/http";
|
|
12
13
|
import {
|
|
13
14
|
createOrGetModuleForNamespace,
|
|
@@ -22,9 +23,7 @@ import { HttpContext } from "../index.js";
|
|
|
22
23
|
|
|
23
24
|
import { module as headerHelpers } from "../../../generated-defs/helpers/header.js";
|
|
24
25
|
import { module as routerHelper } from "../../../generated-defs/helpers/router.js";
|
|
25
|
-
import {
|
|
26
|
-
import { reportDiagnostic } from "../../lib.js";
|
|
27
|
-
import { UnimplementedError } from "../../util/error.js";
|
|
26
|
+
import { differentiateModelTypes, writeCodeTree } from "../../util/differentiate.js";
|
|
28
27
|
|
|
29
28
|
/**
|
|
30
29
|
* Emit a router for the HTTP operations defined in a given service.
|
|
@@ -261,7 +260,9 @@ function* emitRouteHandler(
|
|
|
261
260
|
|
|
262
261
|
yield `if (path.length === 0) {`;
|
|
263
262
|
if (routeTree.operations.size > 0) {
|
|
264
|
-
yield* indent(
|
|
263
|
+
yield* indent(
|
|
264
|
+
emitRouteOperationDispatch(ctx, routeHandlers, routeTree.operations, backends, module),
|
|
265
|
+
);
|
|
265
266
|
} else {
|
|
266
267
|
// Not found
|
|
267
268
|
yield ` return ${onRouteNotFound}(ctx);`;
|
|
@@ -318,6 +319,7 @@ function* emitRouteOperationDispatch(
|
|
|
318
319
|
routeHandlers: string,
|
|
319
320
|
operations: Map<HttpVerb, RouteOperation[]>,
|
|
320
321
|
backends: Map<OperationContainer, [ReCase, string]>,
|
|
322
|
+
module: Module,
|
|
321
323
|
): Iterable<string> {
|
|
322
324
|
yield `switch (request.method) {`;
|
|
323
325
|
for (const [verb, operationList] of operations.entries()) {
|
|
@@ -339,11 +341,10 @@ function* emitRouteOperationDispatch(
|
|
|
339
341
|
yield ` return ${routeHandlers}.${operationName}(ctx, ${backendMemberName}${parameters});`;
|
|
340
342
|
} else {
|
|
341
343
|
// Shared route
|
|
342
|
-
const route = getHttpOperation(ctx.program, operationList[0].operation)[0].path;
|
|
343
344
|
yield ` case ${JSON.stringify(verb.toUpperCase())}:`;
|
|
344
345
|
yield* indent(
|
|
345
346
|
indent(
|
|
346
|
-
emitRouteOperationDispatchMultiple(ctx, routeHandlers, operationList,
|
|
347
|
+
emitRouteOperationDispatchMultiple(ctx, routeHandlers, operationList, backends, module),
|
|
347
348
|
),
|
|
348
349
|
);
|
|
349
350
|
}
|
|
@@ -366,64 +367,50 @@ function* emitRouteOperationDispatchMultiple(
|
|
|
366
367
|
ctx: HttpContext,
|
|
367
368
|
routeHandlers: string,
|
|
368
369
|
operations: RouteOperation[],
|
|
369
|
-
route: string,
|
|
370
370
|
backends: Map<OperationContainer, [ReCase, string]>,
|
|
371
|
+
module: Module,
|
|
371
372
|
): Iterable<string> {
|
|
372
|
-
const
|
|
373
|
-
|
|
373
|
+
const differentiated = differentiateModelTypes(
|
|
374
|
+
ctx,
|
|
375
|
+
module,
|
|
376
|
+
new Set(operations.map((op) => op.operation.parameters)),
|
|
377
|
+
{
|
|
378
|
+
renderPropertyName(prop): string {
|
|
379
|
+
return getHeaderFieldName(ctx.program, prop);
|
|
380
|
+
},
|
|
381
|
+
filter(prop): boolean {
|
|
382
|
+
return isHeader(ctx.program, prop);
|
|
383
|
+
},
|
|
384
|
+
else: {
|
|
385
|
+
kind: "verbatim",
|
|
386
|
+
body: [`return ctx.errorHandlers.onRequestNotFound(ctx);`],
|
|
387
|
+
},
|
|
388
|
+
},
|
|
389
|
+
);
|
|
374
390
|
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
391
|
+
yield* writeCodeTree(ctx, differentiated, {
|
|
392
|
+
referenceModelProperty(p) {
|
|
393
|
+
const headerName = getHeaderFieldName(ctx.program, p);
|
|
394
|
+
return `request.headers["${headerName}"]`;
|
|
395
|
+
},
|
|
396
|
+
*renderResult(type) {
|
|
397
|
+
const operation = operations.find((op) => op.operation.parameters === type)!;
|
|
398
|
+
const [backend] = backends.get(operation.container)!;
|
|
399
|
+
const operationName = keywordSafe(
|
|
400
|
+
backend.snakeCase + "_" + parseCase(operation.operation.name).snakeCase,
|
|
384
401
|
);
|
|
385
|
-
}
|
|
386
|
-
|
|
387
|
-
if (usedContentTypes.has(operationContentType.value)) {
|
|
388
|
-
reportDiagnostic(ctx.program, {
|
|
389
|
-
code: "undifferentiable-route",
|
|
390
|
-
target: httpOperation.operation,
|
|
391
|
-
});
|
|
392
|
-
}
|
|
393
|
-
|
|
394
|
-
usedContentTypes.add(operationContentType.value);
|
|
395
|
-
|
|
396
|
-
contentTypeMap.set(operation, operationContentType.value);
|
|
397
|
-
}
|
|
398
|
-
|
|
399
|
-
const contentTypeName = ctx.gensym("contentType");
|
|
400
402
|
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
yield `switch (${contentTypeName}) {`;
|
|
404
|
-
|
|
405
|
-
for (const [operation, contentType] of contentTypeMap.entries()) {
|
|
406
|
-
const [backend] = backends.get(operation.container)!;
|
|
407
|
-
const operationName = keywordSafe(
|
|
408
|
-
backend.snakeCase + "_" + parseCase(operation.operation.name).snakeCase,
|
|
409
|
-
);
|
|
410
|
-
|
|
411
|
-
const backendMemberName = keywordSafe(backend.camelCase);
|
|
412
|
-
|
|
413
|
-
const parameters =
|
|
414
|
-
operation.parameters.length > 0
|
|
415
|
-
? ", " + operation.parameters.map((param) => parseCase(param.name).camelCase).join(", ")
|
|
416
|
-
: "";
|
|
417
|
-
|
|
418
|
-
const contentTypeValue = parseHeaderValueParameters(contentType).value;
|
|
403
|
+
const backendMemberName = keywordSafe(backend.camelCase);
|
|
419
404
|
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
405
|
+
const parameters =
|
|
406
|
+
operation.parameters.length > 0
|
|
407
|
+
? ", " + operation.parameters.map((param) => parseCase(param.name).camelCase).join(", ")
|
|
408
|
+
: "";
|
|
423
409
|
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
410
|
+
yield `return ${routeHandlers}.${operationName}(ctx, ${backendMemberName}${parameters});`;
|
|
411
|
+
},
|
|
412
|
+
subject: "(request.headers)",
|
|
413
|
+
});
|
|
427
414
|
}
|
|
428
415
|
|
|
429
416
|
/**
|
package/src/lib.ts
CHANGED
|
@@ -148,6 +148,12 @@ export const $lib = createTypeSpecLibrary({
|
|
|
148
148
|
default: paramMessage`Unknown encoding '${"encoding"}' to type '${"target"}' for type '${"type"}'.`,
|
|
149
149
|
},
|
|
150
150
|
},
|
|
151
|
+
"unrecognized-media-type": {
|
|
152
|
+
severity: "error",
|
|
153
|
+
messages: {
|
|
154
|
+
default: paramMessage`unrecognized media (MIME) type '${"mediaType"}' for type '${"type"}'.`,
|
|
155
|
+
},
|
|
156
|
+
},
|
|
151
157
|
},
|
|
152
158
|
});
|
|
153
159
|
|
|
@@ -112,7 +112,7 @@ export interface Switch {
|
|
|
112
112
|
*/
|
|
113
113
|
export interface Verbatim {
|
|
114
114
|
kind: "verbatim";
|
|
115
|
-
body: Iterable<string
|
|
115
|
+
body: Iterable<string> | (() => Iterable<string>);
|
|
116
116
|
}
|
|
117
117
|
|
|
118
118
|
/**
|
|
@@ -380,7 +380,7 @@ export function differentiateTypes(
|
|
|
380
380
|
const intrinsics = (categories.Intrinsic as (VoidType | NullType)[]) ?? [];
|
|
381
381
|
|
|
382
382
|
if (literals.length + scalars.length + intrinsics.length === 0) {
|
|
383
|
-
return differentiateModelTypes(ctx, module, select(models, cases), renderPropertyName);
|
|
383
|
+
return differentiateModelTypes(ctx, module, select(models, cases), { renderPropertyName });
|
|
384
384
|
} else {
|
|
385
385
|
const branches: IfBranch[] = [];
|
|
386
386
|
|
|
@@ -505,7 +505,7 @@ export function differentiateTypes(
|
|
|
505
505
|
branches,
|
|
506
506
|
else:
|
|
507
507
|
models.length > 0
|
|
508
|
-
? differentiateModelTypes(ctx, module, select(models, cases), renderPropertyName)
|
|
508
|
+
? differentiateModelTypes(ctx, module, select(models, cases), { renderPropertyName })
|
|
509
509
|
: undefined,
|
|
510
510
|
};
|
|
511
511
|
}
|
|
@@ -589,6 +589,38 @@ function overlaps(range: IntegerRange, other: IntegerRange): boolean {
|
|
|
589
589
|
return range[0] <= other[1] && range[1] >= other[0];
|
|
590
590
|
}
|
|
591
591
|
|
|
592
|
+
/**
|
|
593
|
+
* Optional paramters for model differentiation.
|
|
594
|
+
*/
|
|
595
|
+
interface DifferentiateModelOptions {
|
|
596
|
+
/**
|
|
597
|
+
* A function that converts a model property reference over the subject to a string.
|
|
598
|
+
*
|
|
599
|
+
* Default: `(prop) => prop.name`
|
|
600
|
+
*/
|
|
601
|
+
renderPropertyName?: (prop: ModelProperty) => string;
|
|
602
|
+
|
|
603
|
+
/**
|
|
604
|
+
* A filter function that determines which properties to consider for differentiation.
|
|
605
|
+
*
|
|
606
|
+
* Default: `() => true`
|
|
607
|
+
*/
|
|
608
|
+
filter?: (prop: ModelProperty) => boolean;
|
|
609
|
+
|
|
610
|
+
/**
|
|
611
|
+
* The default case to use if no other cases match.
|
|
612
|
+
*
|
|
613
|
+
* Default: undefined.
|
|
614
|
+
*/
|
|
615
|
+
else?: CodeTree | undefined;
|
|
616
|
+
}
|
|
617
|
+
|
|
618
|
+
const DEFAULT_DIFFERENTIATE_OPTIONS = {
|
|
619
|
+
renderPropertyName: PROPERTY_ID,
|
|
620
|
+
filter: () => true,
|
|
621
|
+
else: undefined,
|
|
622
|
+
} as const;
|
|
623
|
+
|
|
592
624
|
/**
|
|
593
625
|
* Differentiate a set of model types based on their properties. This function returns a CodeTree that will test an input
|
|
594
626
|
* "subject" and determine which of the cases it matches, executing the corresponding code block.
|
|
@@ -602,8 +634,15 @@ export function differentiateModelTypes(
|
|
|
602
634
|
ctx: JsContext,
|
|
603
635
|
module: Module,
|
|
604
636
|
models: Set<Model>,
|
|
605
|
-
|
|
637
|
+
options?: DifferentiateModelOptions,
|
|
638
|
+
): CodeTree;
|
|
639
|
+
export function differentiateModelTypes(
|
|
640
|
+
ctx: JsContext,
|
|
641
|
+
module: Module,
|
|
642
|
+
models: Set<Model>,
|
|
643
|
+
_options: DifferentiateModelOptions = {},
|
|
606
644
|
): CodeTree {
|
|
645
|
+
const options = { ...DEFAULT_DIFFERENTIATE_OPTIONS, ..._options };
|
|
607
646
|
// Horrible n^2 operation to get the unique properties of all models in the map, but hopefully n is small, so it should
|
|
608
647
|
// be okay until you have a lot of models to differentiate.
|
|
609
648
|
|
|
@@ -623,14 +662,14 @@ export function differentiateModelTypes(
|
|
|
623
662
|
for (const model of models) {
|
|
624
663
|
const props = new Set<string>();
|
|
625
664
|
|
|
626
|
-
for (const prop of getAllProperties(model)) {
|
|
665
|
+
for (const prop of getAllProperties(model).filter(options.filter)) {
|
|
627
666
|
// Don't consider optional properties for differentiation.
|
|
628
667
|
if (prop.optional) continue;
|
|
629
668
|
|
|
630
669
|
// Ignore properties that have no parseable name.
|
|
631
670
|
if (isUnspeakable(prop.name)) continue;
|
|
632
671
|
|
|
633
|
-
const renderedPropName = renderPropertyName(prop) as RenderedPropertyName;
|
|
672
|
+
const renderedPropName = options.renderPropertyName(prop) as RenderedPropertyName;
|
|
634
673
|
|
|
635
674
|
// CASE - literal value
|
|
636
675
|
|
|
@@ -716,7 +755,7 @@ export function differentiateModelTypes(
|
|
|
716
755
|
|
|
717
756
|
const branches: IfBranch[] = [];
|
|
718
757
|
|
|
719
|
-
let defaultCase:
|
|
758
|
+
let defaultCase: CodeTree | undefined = options.else;
|
|
720
759
|
|
|
721
760
|
for (const [model, unique] of uniqueProps) {
|
|
722
761
|
const literals = uniqueLiterals.get(model);
|
|
@@ -727,14 +766,11 @@ export function differentiateModelTypes(
|
|
|
727
766
|
code: "undifferentiable-model",
|
|
728
767
|
target: model,
|
|
729
768
|
});
|
|
730
|
-
return
|
|
731
|
-
kind: "result",
|
|
732
|
-
type: defaultCase,
|
|
733
|
-
};
|
|
769
|
+
return defaultCase;
|
|
734
770
|
} else {
|
|
735
771
|
// Allow a single default case. This covers more APIs that have a single model that is not differentiated by a
|
|
736
772
|
// unique property, in which case we can make it the `else` case.
|
|
737
|
-
defaultCase = model;
|
|
773
|
+
defaultCase = { kind: "result", type: model };
|
|
738
774
|
continue;
|
|
739
775
|
}
|
|
740
776
|
}
|
|
@@ -744,7 +780,7 @@ export function differentiateModelTypes(
|
|
|
744
780
|
const firstUniqueLiteral = literals.values().next().value as RenderedPropertyName;
|
|
745
781
|
|
|
746
782
|
const property = [...model.properties.values()].find(
|
|
747
|
-
(p) => (renderPropertyName(p) as RenderedPropertyName) === firstUniqueLiteral,
|
|
783
|
+
(p) => (options.renderPropertyName(p) as RenderedPropertyName) === firstUniqueLiteral,
|
|
748
784
|
)!;
|
|
749
785
|
|
|
750
786
|
branches.push({
|
|
@@ -752,7 +788,7 @@ export function differentiateModelTypes(
|
|
|
752
788
|
kind: "binary-op",
|
|
753
789
|
left: {
|
|
754
790
|
kind: "binary-op",
|
|
755
|
-
left: { kind: "literal", value: renderPropertyName(property) },
|
|
791
|
+
left: { kind: "literal", value: options.renderPropertyName(property) },
|
|
756
792
|
operator: "in",
|
|
757
793
|
right: SUBJECT,
|
|
758
794
|
},
|
|
@@ -774,7 +810,7 @@ export function differentiateModelTypes(
|
|
|
774
810
|
const firstUniqueRange = ranges.values().next().value as RenderedPropertyName;
|
|
775
811
|
|
|
776
812
|
const property = [...model.properties.values()].find(
|
|
777
|
-
(p) => renderPropertyName(p) === firstUniqueRange,
|
|
813
|
+
(p) => options.renderPropertyName(p) === firstUniqueRange,
|
|
778
814
|
)!;
|
|
779
815
|
|
|
780
816
|
const range = [...propertyRanges.get(firstUniqueRange)!.entries()].find(
|
|
@@ -786,7 +822,7 @@ export function differentiateModelTypes(
|
|
|
786
822
|
kind: "binary-op",
|
|
787
823
|
left: {
|
|
788
824
|
kind: "binary-op",
|
|
789
|
-
left: { kind: "literal", value: renderPropertyName(property) },
|
|
825
|
+
left: { kind: "literal", value: options.renderPropertyName(property) },
|
|
790
826
|
operator: "in",
|
|
791
827
|
right: SUBJECT,
|
|
792
828
|
},
|
|
@@ -817,12 +853,7 @@ export function differentiateModelTypes(
|
|
|
817
853
|
return {
|
|
818
854
|
kind: "if-chain",
|
|
819
855
|
branches,
|
|
820
|
-
else: defaultCase
|
|
821
|
-
? {
|
|
822
|
-
kind: "result",
|
|
823
|
-
type: defaultCase,
|
|
824
|
-
}
|
|
825
|
-
: undefined,
|
|
856
|
+
else: defaultCase,
|
|
826
857
|
};
|
|
827
858
|
}
|
|
828
859
|
|
|
@@ -903,7 +934,11 @@ export function* writeCodeTree(
|
|
|
903
934
|
break;
|
|
904
935
|
}
|
|
905
936
|
case "verbatim":
|
|
906
|
-
|
|
937
|
+
if (typeof tree.body === "function") {
|
|
938
|
+
yield* tree.body();
|
|
939
|
+
} else {
|
|
940
|
+
yield* tree.body;
|
|
941
|
+
}
|
|
907
942
|
break;
|
|
908
943
|
default:
|
|
909
944
|
throw new UnreachableError("writeCodeTree for " + (tree satisfies never as CodeTree).kind, {
|