metadata-booster
Version:
Emits detailed metadata of your types. You can then get it in runtime to deal with schema-aware operation, like defining GraphQL schemas, ORM operations, etc.
115 lines • 4.75 kB
JavaScript
;
/* eslint-disable @typescript-eslint/ban-ts-comment */
Object.defineProperty(exports, "__esModule", { value: true });
exports.getClassInfo = void 0;
const ts_morph_1 = require("ts-morph");
function getClassInfo(classNode, checker) {
if (!classNode.name)
return;
const node = (0, ts_morph_1.createWrappedNode)(classNode, { typeChecker: checker }).asKindOrThrow(ts_morph_1.SyntaxKind.ClassDeclaration);
return {
name: node.getNameOrThrow(),
fields: getInstanceProperties(node).map((p) => ({ name: p.getName(), typeInfo: getTypeInfo(p.getType(), p) })),
methods: node.getMethods().map((m) => ({ name: m.getName(), typeInfo: getTypeInfo(m.getReturnType(), m) })),
};
}
exports.getClassInfo = getClassInfo;
function getInstanceProperties(classDeclaration) {
if (classDeclaration == undefined) {
return [];
}
// Ensure to get the properties of the base classes too
return [...getInstanceProperties(classDeclaration.getBaseClass()), ...classDeclaration.getInstanceProperties()];
}
function hasQuestionTokenNode(node) {
// @ts-ignore
if (node && typeof node['hasQuestionToken'] === 'function') {
// @ts-ignore
return node === null || node === void 0 ? void 0 : node.hasQuestionToken();
}
return false;
}
function getTypeInfo(type, node) {
var _a, _b, _c, _d;
const typeGroupTuples = [
[(t) => t.isString(), 'String'],
[(t) => t.isNumber(), 'Number'],
[(t) => t.isBoolean(), 'Boolean'],
[(t) => t.isEnum(), 'Enum'],
[(t) => t.isUnion(), 'Union'],
[(t) => t.isIntersection(), 'Intersection'],
[(t) => t.isClass(), 'Class'],
[(t) => t.isInterface(), 'Interface'],
[(t) => t.getAliasSymbol() != null, 'Type'],
[(t) => t.isArray(), 'Array'],
[(t) => t.getCallSignatures().length > 0, 'Function'],
[(t) => isReadonlyArray(t), 'ReadonlyArray'],
[(t) => t.isObject(), 'Object'],
];
const hasQuestionToken = hasQuestionTokenNode(node);
const isNullable = type.isNullable() || hasQuestionToken;
type = type.getNonNullableType();
const typeInfo = {
name: type.getText(node),
typeName: '',
typeGroup: ((_a = typeGroupTuples.find(([fn]) => fn(type))) === null || _a === void 0 ? void 0 : _a[1]) || 'Other',
isNullable,
parameters: [],
};
switch (typeInfo.typeGroup) {
case 'Enum':
typeInfo.parameters = type.getUnionTypes().map((t) => getTypeInfo(t));
break;
case 'Union':
typeInfo.parameters = type.getUnionTypes().map((t) => getTypeInfo(t, node));
break;
case 'Intersection':
typeInfo.parameters = type.getIntersectionTypes().map((t) => getTypeInfo(t, node));
break;
default:
typeInfo.parameters = type.getTypeArguments().map((a) => getTypeInfo(a, node));
}
// typeName is used for referencing the type in the metadata
switch (typeInfo.typeGroup) {
case 'String':
case 'Number':
case 'Boolean':
typeInfo.typeName = typeInfo.typeGroup;
break;
case 'Union':
case 'Intersection':
typeInfo.typeName = null;
break;
case 'Enum':
case 'Class':
case 'ReadonlyArray':
case 'Array':
// getSymbol() is used for complex types, in which cases getText() returns too much information (e.g. Map<User> instead of just Map)
typeInfo.typeName = ((_b = type.getSymbol()) === null || _b === void 0 ? void 0 : _b.getName()) || '';
break;
case 'Object':
typeInfo.typeName = ((_c = type.getSymbol()) === null || _c === void 0 ? void 0 : _c.getName()) || '';
if (typeInfo.typeName === '__type') {
// This happens for literal objects like `{ a: string, b: { c: string } }`
typeInfo.typeName = 'Object';
}
break;
case 'Interface':
case 'Type':
case 'Function':
case 'Other':
if (type.isEnumLiteral()) {
typeInfo.name = ((_d = type.getSymbol()) === null || _d === void 0 ? void 0 : _d.getName()) || ''; // e.g. "Small"
}
typeInfo.typeName = null;
break;
}
if (typeInfo.typeName === '')
throw new Error(`Could not extract typeName for type ${JSON.stringify(typeInfo)}`);
return typeInfo;
}
function isReadonlyArray(t) {
var _a;
return t.isObject() && (((_a = t.getSymbol()) === null || _a === void 0 ? void 0 : _a.getName()) || '') === 'ReadonlyArray';
}
//# sourceMappingURL=metadata-extractors.js.map