UNPKG

@typespec/compiler

Version:

TypeSpec compiler and standard library

216 lines 9.46 kB
import { getSymNode } from "../core/binder.js"; import { compilerAssert } from "../core/diagnostics.js"; import { printTypeSpecNode } from "../core/formatter.js"; import { getRawTextWithCache } from "../core/helpers/raw-text-cache.js"; import { printIdentifier } from "../core/helpers/syntax-utils.js"; import { getEntityName, getTypeName, isStdNamespace } from "../core/helpers/type-name-utils.js"; import { getFullyQualifiedSymbolName } from "../core/type-utils.js"; import { SyntaxKind, } from "../core/types.js"; import { walkPropertiesInherited } from "../index.js"; /** @internal */ export async function getSymbolSignature(program, sym, options = { includeBody: false, }) { const decl = getSymNode(sym); switch (decl?.kind) { case SyntaxKind.AliasStatement: return fence(`alias ${await getAliasSignature(decl)}`); } const entity = sym.type ?? (decl ? program.checker.getTypeOrValueForNode(decl) : null); return getEntitySignature(sym, entity, options); } function getEntitySignature(sym, entity, options) { if (entity === null) { return "(error)"; } if ("valueKind" in entity) { if (sym.flags & 2048 /* SymbolFlags.Function */ && entity.valueKind === "Function") { const parameters = [...entity.parameters].map((x) => `${x.rest ? "..." : ""}${x.name}${x.optional ? "?" : ""}: ${getEntityName(x.type)}`); return fence(`fn ${entity.name ?? "<anonymous>"}(${parameters.join(", ")}) => ${getEntityName(entity.returnType)}`); } return fence(`const ${sym.name}: ${getTypeName(entity.type)}`); } return getTypeSignature(entity, options); } function getTypeSignature(type, options) { switch (type.kind) { case "Scalar": case "Enum": case "Union": case "Namespace": return fence(`${type.kind.toLowerCase()} ${getPrintableTypeName(type)}`); case "Interface": return fence(getInterfaceSignature(type, options.includeBody)); case "Model": return fence(getModelSignature(type, options.includeBody)); case "ScalarConstructor": return fence(`init ${getTypeSignature(type.scalar, options)}.${type.name}`); case "Decorator": return fence(getDecoratorSignature(type)); case "Operation": return fence(getOperationSignature(type)); case "String": // BUG: https://github.com/microsoft/typespec/issues/1350 - should escape string literal values return `(string)\n${fence(`"${type.value}"`)}`; case "Boolean": return `(boolean)\n${fence(type.value ? "true" : "false")}`; case "Number": return `(number)\n${fence(type.value.toString())}`; case "StringTemplate": return `(string template)\n${fence(getStringTemplateSignature(type))}`; case "StringTemplateSpan": return `(string template span)\n${fence(getTypeName(type.type))}`; case "Intrinsic": return ""; case "FunctionParameter": return `(function parameter)\n${fence(getFunctionParameterSignature(type))}`; case "ModelProperty": return `(model property)\n${fence(getModelPropertySignature(type))}`; case "EnumMember": return `(enum member)\n${fence(getEnumMemberSignature(type))}`; case "TemplateParameter": return `(template parameter)\n${fence(getTemplateConstraintSignature(type.node.id.sv, type.constraint))}`; case "TemplateParameterAccess": return `(template access)\n${fence(getTemplateConstraintSignature(type.path, type.constraint))}`; case "UnionVariant": return `(union variant)\n${fence(getUnionVariantSignature(type))}`; case "Tuple": return `(tuple)\n[${fence(type.values.map((v) => getTypeSignature(v, options)).join(", "))}]`; case "FunctionType": return fence(`fn (${type.parameters.map((p) => getFunctionParameterSignature(p)).join(", ")}) => ${getEntityName(type.returnType)}`); default: const _assertNever = type; compilerAssert(false, "Unexpected type kind"); } } /** Format `T extends ...` style signatures for template parameters/access paths. */ function getTemplateConstraintSignature(nameOrPath, constraint) { if (!constraint) { return nameOrPath; } const parts = []; if (constraint.type) { parts.push(getTypeName(constraint.type, { printable: true })); } if (constraint.valueType) { parts.push(`valueof ${getTypeName(constraint.valueType, { printable: true })}`); } return parts.length > 0 ? `${nameOrPath} extends ${parts.join(" | ")}` : nameOrPath; } function getDecoratorSignature(type) { const ns = getQualifier(type.namespace); const name = type.name.slice(1); const parameters = [type.target, ...type.parameters].map((x) => getFunctionParameterSignature(x)); return `dec ${ns}${name}(${parameters.join(", ")})`; } function getOperationSignature(type, includeQualifier = true) { const parameters = [...type.parameters.properties.values()].map((p) => getModelPropertySignature(p, false /* includeQualifier */)); return `op ${getTypeName(type, { nameOnly: !includeQualifier, })}(${parameters.join(", ")}): ${getPrintableTypeName(type.returnType)}`; } function getInterfaceSignature(type, includeBody) { if (includeBody) { const INDENT = " "; const opDesc = Array.from(type.operations).map(([name, op]) => INDENT + getOperationSignature(op, false /* includeQualifier */) + ";"); return `${type.kind.toLowerCase()} ${getPrintableTypeName(type)} {\n${opDesc.join("\n")}\n}`; } else { if (type.node && type.node.kind === SyntaxKind.InterfaceStatement && type.node.templateParameters) { type.node.templateParameters.forEach((t) => { if (t.default) { getRawTextWithCache(t); } }); } } return `${type.kind.toLowerCase()} ${getPrintableTypeName(type)}`; } /** * All properties from 'extends' and 'is' will be included if includeBody is true. */ function getModelSignature(type, includeBody) { if (includeBody) { const propDesc = []; const INDENT = " "; for (const prop of walkPropertiesInherited(type)) { propDesc.push(INDENT + getModelPropertySignature(prop, false /*includeQualifier*/)); } return `${type.kind.toLowerCase()} ${getPrintableTypeName(type)}{\n${propDesc.map((d) => `${d};`).join("\n")}\n}`; } else { if (type.node && type.node.kind === SyntaxKind.ModelStatement && type.node.templateParameters) { type.node.templateParameters.forEach((t) => { if (t.default) { getRawTextWithCache(t); } }); } } return `${type.kind.toLowerCase()} ${getPrintableTypeName(type)}`; } function getFunctionParameterSignature(parameter) { const rest = parameter.rest ? "..." : ""; const optional = parameter.optional ? "?" : ""; return `${rest}${printIdentifier(parameter.name, "allow-reserved")}${optional}: ${getEntityName(parameter.type)}`; } function getStringTemplateSignature(stringTemplate) { return ("`" + [ stringTemplate.spans.map((span) => { return span.isInterpolated ? "${" + getTypeName(span.type) + "}" : span.type.value; }), ].join("") + "`"); } function getModelPropertySignature(property, includeQualifier = true) { const ns = includeQualifier ? getQualifier(property.model) : ""; return `${ns}${printIdentifier(property.name, "allow-reserved")}: ${getPrintableTypeName(property.type)}`; } function getUnionVariantSignature(variant) { if (typeof variant.name !== "string") { return getPrintableTypeName(variant.type); } const ns = getQualifier(variant.union); return `${ns}${printIdentifier(variant.name, "allow-reserved")}: ${getPrintableTypeName(variant.type)}`; } function getEnumMemberSignature(member) { const ns = getQualifier(member.enum); const value = typeof member.value === "string" ? `"${member.value}"` : member.value; return value === undefined ? `${ns}${printIdentifier(member.name, "allow-reserved")}` : `${ns}${printIdentifier(member.name, "allow-reserved")}: ${value}`; } async function getAliasSignature(alias) { const fullName = getFullyQualifiedSymbolName(alias.symbol); const args = await Promise.all(alias.templateParameters.map(async (t, index) => { if (t.default) { getRawTextWithCache(t.default); } return await printTypeSpecNode(t); })); return args.length === 0 ? fullName : `${fullName}<${args.join(", ")}>`; } function getQualifier(parent) { if (!parent?.name || typeof parent.name !== "string" || (parent.kind === "Namespace" && isStdNamespace(parent))) { return ""; } const parentName = getPrintableTypeName(parent); if (!parentName) { return ""; } return parentName + "."; } function getPrintableTypeName(type) { return getTypeName(type, { printable: true, }); } function fence(code) { return `\`\`\`typespec\n${code}\n\`\`\``; } //# sourceMappingURL=type-signature.js.map