@typespec/http-server-js
Version:
TypeSpec HTTP server code generator for JavaScript
115 lines • 4.94 kB
JavaScript
// Copyright (c) Microsoft Corporation
// Licensed under the MIT license.
import { getFriendlyName, isTemplateDeclaration, isTemplateInstance, } from "@typespec/compiler";
import { isUnspeakable, parseCase } from "../util/case.js";
import { indent } from "../util/iter.js";
import { KEYWORDS } from "../util/keywords.js";
import { getFullyQualifiedTypeName } from "../util/name.js";
import { asArrayType, getArrayElementName, getRecordValueName } from "../util/pluralism.js";
import { emitDocumentation } from "./documentation.js";
import { emitTypeReference } from "./reference.js";
/**
* Emit a model declaration.
*
* @param ctx - The emitter context.
* @param model - The model to emit.
* @param module - The module that this model is written into.
* @param altName - An alternative name to use for the model if it is not named.
*/
export function* emitModel(ctx, model, module, altName) {
const isTemplate = isTemplateInstance(model);
const friendlyName = getFriendlyName(ctx.program, model);
if (isTemplateDeclaration(model)) {
return;
}
const modelNameCase = parseCase(friendlyName
? friendlyName
: isTemplate
? model.templateMapper.args.map((a) => ("name" in a ? String(a.name) : "")).join("_") +
model.name
: model.name);
if (model.name === "" && !altName) {
throw new Error("UNREACHABLE: Anonymous model with no altName");
}
yield* emitDocumentation(ctx, model);
const ifaceName = model.name === "" ? altName : modelNameCase.pascalCase;
const extendsClause = model.baseModel
? `extends ${emitTypeReference(ctx, model.baseModel, model, module)} `
: "";
yield `export interface ${ifaceName} ${extendsClause}{`;
for (const field of model.properties.values()) {
// Skip properties with unspeakable names.
if (isUnspeakable(field.name)) {
continue;
}
const nameCase = parseCase(field.name);
const basicName = nameCase.camelCase;
const typeReference = emitTypeReference(ctx, field.type, field, module, {
altName: modelNameCase.pascalCase + nameCase.pascalCase,
});
const name = KEYWORDS.has(basicName) ? `_${basicName}` : basicName;
yield* indent(emitDocumentation(ctx, field));
const questionMark = field.optional ? "?" : "";
yield ` ${name}${questionMark}: ${typeReference};`;
yield "";
}
yield "}";
yield "";
}
export function emitModelLiteral(ctx, model, module) {
const properties = [...model.properties.values()]
.map((prop) => {
if (isUnspeakable(prop.name)) {
return undefined;
}
const nameCase = parseCase(prop.name);
const questionMark = prop.optional ? "?" : "";
const name = KEYWORDS.has(nameCase.camelCase) ? `_${nameCase.camelCase}` : nameCase.camelCase;
return `${name}${questionMark}: ${emitTypeReference(ctx, prop.type, prop, module)}`;
})
.filter((p) => !!p);
return `{ ${properties.join("; ")} }`;
}
/**
* Determines whether a model is an instance of a well-known model, such as TypeSpec.Record or TypeSpec.Array.
*/
export function isWellKnownModel(ctx, type) {
const fullName = getFullyQualifiedTypeName(type);
return ["TypeSpec.Record", "TypeSpec.Array", "TypeSpec.Http.HttpPart"].includes(fullName);
}
/**
* Emits a well-known model, such as TypeSpec.Record or TypeSpec.Array.
*
* @param ctx - The emitter context.
* @param type - The model to emit.
* @param module - The module that this model is written into.
* @param preferredAlternativeName - An alternative name to use for the model if it is not named.
*/
export function emitWellKnownModel(ctx, type, module, preferredAlternativeName) {
switch (type.name) {
case "Record": {
const arg = type.indexer.value;
return `Record<string, ${emitTypeReference(ctx, arg, type, module, {
altName: preferredAlternativeName && getRecordValueName(preferredAlternativeName),
})}>`;
}
case "Array": {
const arg = type.indexer.value;
return asArrayType(emitTypeReference(ctx, arg, type, module, {
altName: preferredAlternativeName && getArrayElementName(preferredAlternativeName),
}));
}
case "HttpPart": {
const argument = type.templateMapper.args[0];
if (!(argument.entityKind === "Type" && argument.kind === "Model")) {
throw new Error("UNREACHABLE: HttpPart must have a Model argument");
}
return emitTypeReference(ctx, argument, type, module, {
altName: preferredAlternativeName && `${preferredAlternativeName}HttpPart`,
});
}
default:
throw new Error(`UNREACHABLE: ${type.name}`);
}
}
//# sourceMappingURL=model.js.map