UNPKG

@azure-tools/typespec-apiview

Version:

Library for emitting APIView token files from TypeSpec

250 lines 8.58 kB
import { SyntaxKind, visitChildren } from "@typespec/compiler/ast"; export class NamespaceModel { constructor(name, ns, program) { this.kind = SyntaxKind.NamespaceStatement; this.operations = new Map(); this.resources = new Map(); this.models = new Map(); this.aliases = new Map(); this.augmentDecorators = new Array(); this.constants = new Array(); this.name = name; this.node = ns.node; // Assuming ns.node is never undefined // Gather operations for (const [opName, op] of ns.operations) { if (op.node) { this.operations.set(opName, op.node); } else { throw new Error(`Operation node for ${opName} is undefined.`); } } for (const [intName, int] of ns.interfaces) { if (int.node) { this.operations.set(intName, int.node); } else { throw new Error(`Interface node for ${intName} is undefined.`); } } // Gather models and resources for (const [modelName, model] of ns.models) { if (model.node !== undefined) { let isResource = false; for (const dec of model.decorators) { if (dec.decorator.name === "$resource") { isResource = true; break; } } if (isResource) { this.resources.set(modelName, model.node); } else { this.models.set(modelName, model.node); } } else { throw new Error("Unexpectedly found undefined model node."); } } for (const [enumName, en] of ns.enums) { if (en.node) { this.models.set(enumName, en.node); } else { throw new Error(`Enum node for ${enumName} is undefined.`); } } for (const [unionName, un] of ns.unions) { if (un.node) { this.models.set(unionName, un.node); } else { throw new Error(`Union node for ${unionName} is undefined.`); } } for (const [scalarName, sc] of ns.scalars) { if (sc.node) { this.models.set(scalarName, sc.node); } else { throw new Error(`Scalar node for ${scalarName} is undefined.`); } } // Gather aliases for (const alias of findNodes(SyntaxKind.AliasStatement, program, ns)) { this.aliases.set(alias.id.sv, alias); } // collect augment decorators for (const augment of findNodes(SyntaxKind.AugmentDecoratorStatement, program, ns)) { this.augmentDecorators.push(augment); } // collect constants for (const constant of findNodes(SyntaxKind.ConstStatement, program, ns)) { this.constants.push(constant); } // sort operations and models this.operations = new Map([...this.operations].sort(caseInsensitiveSort)); this.resources = new Map([...this.resources].sort(caseInsensitiveSort)); this.models = new Map([...this.models].sort(caseInsensitiveSort)); this.aliases = new Map([...this.aliases].sort(caseInsensitiveSort)); } /** * Don't emit an empty namespace * @returns true if there are models, resources or operations */ shouldEmit() { return (this.node.decorators !== undefined || this.models.size > 0 || this.operations.size > 0 || this.resources.size > 0); } } function findNodes(kind, program, namespace) { const nodes = []; for (const file of program.sourceFiles.values()) { visitChildren(file, function visit(node) { if (node.kind === kind && inNamespace(node, program, namespace)) { nodes.push(node); } visitChildren(node, visit); }); } return nodes; } function inNamespace(node, program, namespace) { for (let n = node; n; n = n.parent) { switch (n.kind) { case SyntaxKind.NamespaceStatement: return program.checker.getTypeForNode(n) === namespace; case SyntaxKind.TypeSpecScript: if (n.inScopeNamespaces.length > 0 && inNamespace(n.inScopeNamespaces[0], program, namespace)) { return true; } return false; } } return false; } export function generateId(obj) { var _a, _b; let node; if (obj === undefined) { return undefined; } if (obj instanceof NamespaceModel) { return obj.name; } let name; let parentId; switch (obj.kind) { case SyntaxKind.NamespaceStatement: node = obj; name = node.id.sv; parentId = generateId(node.parent); break; case SyntaxKind.DecoratorExpression: node = obj; switch (node.target.kind) { case SyntaxKind.Identifier: return `@${node.target.sv}`; case SyntaxKind.MemberExpression: return generateId(node.target); } case SyntaxKind.EnumMember: node = obj; name = node.id.sv; parentId = generateId(node.parent); break; case SyntaxKind.EnumSpreadMember: node = obj; return generateId(node.target); case SyntaxKind.EnumStatement: node = obj; name = node.id.sv; parentId = generateId(node.parent); break; case SyntaxKind.Identifier: node = obj; return node.sv; case SyntaxKind.InterfaceStatement: node = obj; name = node.id.sv; parentId = generateId(node.parent); break; case SyntaxKind.MemberExpression: node = obj; name = node.id.sv; parentId = generateId(node.base); break; case SyntaxKind.ModelProperty: node = obj; name = node.id.sv; parentId = generateId(node.parent); break; case SyntaxKind.ModelSpreadProperty: node = obj; name = generateId(node.target); parentId = generateId(node.parent); break; case SyntaxKind.ModelStatement: node = obj; name = node.id.sv; parentId = generateId(node.parent); break; case SyntaxKind.OperationStatement: node = obj; name = node.id.sv; parentId = generateId(node.parent); break; case SyntaxKind.StringLiteral: node = obj; name = node.value; parentId = undefined; break; case SyntaxKind.UnionStatement: node = obj; name = node.id.sv; parentId = generateId(node.parent); break; case SyntaxKind.UnionVariant: node = obj; if (((_a = node.id) === null || _a === void 0 ? void 0 : _a.sv) !== undefined) { name = node.id.sv; } else { // TODO: Should never have default value of _ name = (_b = generateId(node.value)) !== null && _b !== void 0 ? _b : "_"; } parentId = generateId(node.parent); break; case SyntaxKind.TypeReference: node = obj; name = generateId(node.target); parentId = undefined; break; case SyntaxKind.DirectiveExpression: node = obj; name = `#${generateId(node.target)}`; for (const arg of node.arguments) { name += `_${generateId(arg)}`; } parentId = generateId(node.parent); break; default: return undefined; } if (parentId !== undefined) { return `${parentId}.${name}`; } else { return name; } } function caseInsensitiveSort(a, b) { const aLower = a[0].toLowerCase(); const bLower = b[0].toLowerCase(); return aLower > bLower ? 1 : aLower < bLower ? -1 : 0; } //# sourceMappingURL=namespace-model.js.map