@itrocks/reflect
Version:
Runtime introspection of TypeScript classes and their properties, including property type
118 lines • 4.68 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.ReflectClass = void 0;
require("reflect-metadata");
const class_file_1 = require("@itrocks/class-file");
const class_type_1 = require("@itrocks/class-type");
const class_type_2 = require("@itrocks/class-type");
const class_type_3 = require("@itrocks/class-type");
const property_default_1 = require("@itrocks/property-default");
const property_type_1 = require("@itrocks/property-type");
const sorted_array_1 = require("@itrocks/sorted-array");
const property_1 = require("./property");
const DEFAULTS = Symbol('defaults');
const TYPES = Symbol('types');
class SortedPropertyNames extends sorted_array_1.SortedArray {
static get [Symbol.species]() {
return Array;
}
constructor(object) {
super(...Object.getOwnPropertyNames(object).sort());
}
}
class ReflectClass {
name;
object;
type;
constructor(object) {
this.object = (0, class_type_2.isObject)(object) ? object : undefined;
this.type = (0, class_type_3.typeOf)(object);
this.name = this.type.name;
}
inheritPropertyDefaults(propertyDefaults) {
const parent = this.parent;
if (parent) {
Object.assign(propertyDefaults, parent.propertyDefaults);
}
}
inheritPropertyTypes(propertyTypes) {
const parent = this.parent;
if (parent) {
Object.assign(propertyTypes, parent.propertyTypes);
}
}
get parent() {
const parentType = Object.getPrototypeOf(this.type);
const parent = (parentType === Function.prototype) ? null : new ReflectClass(parentType);
Object.defineProperty(this, 'parent', { configurable: true, enumerable: false, value: parent, writable: true });
return parent;
}
get properties() {
const properties = new Array;
for (const name of this.propertyNames) {
properties.push(new property_1.ReflectProperty(this, name));
}
Object.defineProperty(this, 'properties', { configurable: true, enumerable: false, value: properties, writable: true });
return properties;
}
get property() {
const properties = {};
for (const property of this.properties) {
properties[property.name] = property;
}
Object.defineProperty(this, 'property', { configurable: true, enumerable: false, value: properties, writable: true });
return properties;
}
get propertyDefaults() {
let value = Reflect.getOwnMetadata(DEFAULTS, this.type);
if (!value) {
value = {};
Reflect.defineMetadata(DEFAULTS, value, this.type);
this.inheritPropertyDefaults(value);
if (this.type === (0, class_type_1.baseType)(this.type)) {
Object.assign(value, (0, property_default_1.propertyDefaultsFromFile)((0, class_file_1.fileOf)(this.type)));
}
return value;
}
Object.defineProperty(this, 'propertyDefaults', { configurable: true, enumerable: false, value, writable: true });
return value;
}
get propertyNames() {
let object;
// TODO: replace runtime instantiation with .d.ts AST analysis
try {
object = new this.type;
}
catch {
return [];
}
const propertyNames = new SortedPropertyNames(object);
propertyNames.distinct = true;
while (object) {
Object.entries(Object.getOwnPropertyDescriptors(object)).forEach(([name, descriptor]) => {
if (!descriptor.get || (name[0] === '_'))
return;
propertyNames.push(name);
});
object = Object.getPrototypeOf(object);
}
Object.defineProperty(this, 'propertyNames', { configurable: true, enumerable: false, value: propertyNames, writable: true });
return propertyNames;
}
get propertyTypes() {
let value = Reflect.getOwnMetadata(TYPES, this.type);
if (!value) {
value = {};
Reflect.defineMetadata(TYPES, value, this.type);
this.inheritPropertyTypes(value);
if (this.type === (0, class_type_1.baseType)(this.type)) {
Object.assign(value, (0, property_type_1.propertyTypesFromFile)((0, class_file_1.fileOf)(this.type)));
}
return value;
}
Object.defineProperty(this, 'propertyTypes', { configurable: true, enumerable: false, value, writable: true });
return value;
}
}
exports.ReflectClass = ReflectClass;
//# sourceMappingURL=class.js.map