@azure-tools/typespec-apiview
Version:
Library for emitting APIView token files from TypeSpec
250 lines • 8.58 kB
JavaScript
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