UNPKG

apibuilder-js

Version:

A reference implementation of API Builder for JavaScript

177 lines (151 loc) 5.04 kB
/* eslint-disable import/no-cycle */ import invariant from 'invariant'; import { AstNode, EnclosingTypeNode, astFromTypeName } from './ast'; import { Regex } from './constants'; import { isArrayTypeName, isMapTypeName, isPrimitiveTypeName } from './predicates'; /** * API Builder types can be complex (e.g. array of strings, map of strings, * maps of array of strings etc.). By design, all entries in an array or map * must be of the same type: this is called the base type. * @example * getBaseTypeName("map[string]") * //=> "string" * getBaseTypeName("map[[string]]") * //=> "string" */ export function getBaseTypeName(type: string | AstNode): string { if (typeof type === 'string') { return getBaseTypeName(astFromTypeName(type)); } if ((<EnclosingTypeNode>type).type) { return getBaseTypeName((<EnclosingTypeNode>type).type); } return type.name; } /** * Given the name of an enclosing type as it appears in an API builder schema, * returns the API builder type name of the underlying type. * @example * getNestedTypeName("map[string]"); * //=> "string" * getNestedTypeName("map[[string]]"); * //=> "[string]" */ export function getNestedTypeName(type: string): string { const mapMatch = Regex.OBJECTOF.exec(type); if (mapMatch) { const [, $1] = mapMatch; return $1; } const arrayMatch = Regex.ARRAYOF.exec(type); if (arrayMatch) { const [, $1] = arrayMatch; return $1; } return type; } export function isFullyQualifiedName(identifier: string): boolean { return isPrimitiveTypeName(identifier) || getBaseTypeName(identifier).lastIndexOf('.') >= 0; } export function assertFullyQualifiedName(fullyQualifiedName: string): void { invariant( isFullyQualifiedName(fullyQualifiedName), `"${fullyQualifiedName}" is not a valid fully qualified name. ` + 'A fully qualified name may be the name of a primitive type, ' + 'or a string consisting of a package name followed by the base ' + 'short name. (e.g. "com.bryzek.apidoc.common.v0.models.reference").', ); } export class FullyQualifiedName { /** * The fully qualified name of the type, including its package name. */ public fullyQualifiedName: string; /** * Create a fully qualified type. * @example * new FullyQualifiedName("string"); * new FullyQualifiedName("[string]"); * new FullyQualifiedName("map[string]"); * new FullyQualifiedName("com.bryzek.apidoc.common.v0.models.reference"); * new FullyQualifiedName("[com.bryzek.apidoc.common.v0.models.reference]"); * new FullyQualifiedName("map[com.bryzek.apidoc.common.v0.models.reference]"); */ constructor(fullyQualifiedName: string) { assertFullyQualifiedName(fullyQualifiedName); this.fullyQualifiedName = fullyQualifiedName; } /** * This property holds the fully qualified name of the type, * including its namespace. */ get fullName(): string { return this.fullyQualifiedName; } /** * This property holds the base name of the type. */ get baseTypeName(): string { return getBaseTypeName(this.fullyQualifiedName); } /** * This property holds the nested type. A nested type is a type defined * within the scope of another type, which is called the enclosing type. * Only array or map types can enclose another type, which may be any of the * supported API builder types, including another array or map. */ get nestedTypeName(): string { return getNestedTypeName(this.fullyQualifiedName); } /** * This property holds the base short name, that is the type name * without its package name. */ get shortName(): string { const lastIndex = this.baseTypeName.lastIndexOf('.'); if (lastIndex === -1) { return this.baseTypeName; } return this.baseTypeName.substring(lastIndex + 1); } /** * This property holds the package name. */ get packageName(): string { const lastIndex = this.baseTypeName.lastIndexOf('.'); if (this.isPrimitiveType || lastIndex === -1) { return ''; } return this.baseTypeName.substring(0, lastIndex); } /** * This property holds whether this is an array. */ get isArrayType(): boolean { return isArrayTypeName(this.fullyQualifiedName); } /** * This property holds whether this is a map. */ get isMapType(): boolean { return isMapTypeName(this.fullyQualifiedName); } /** * This property holds whether this type is an enclosing type. An enclosing * type is a type that encloses another type, which is called the nested type. * Only array or map types can enclose another type, which may be one of the * supported API builder types, including another array or map. */ get isEnclosingType(): boolean { return this.isArrayType || this.isMapType; } /** * This property holds whether this is a primitive type. */ get isPrimitiveType(): boolean { return isPrimitiveTypeName(this.fullName); } public toString(): string { return this.fullyQualifiedName; } }