@itrocks/lazy-loading
Version:
Integrates lazy loading for objects and collections in TypeScript classes
211 lines • 8.44 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.PROTECT_GET = void 0;
exports.initClass = initClass;
exports.initLazyLoading = initLazyLoading;
const class_type_1 = require("@itrocks/class-type");
const property_type_1 = require("@itrocks/property-type");
const property_type_2 = require("@itrocks/property-type");
const reflect_1 = require("@itrocks/reflect");
const storage_1 = require("@itrocks/storage");
const store_1 = require("@itrocks/store");
exports.PROTECT_GET = Symbol('protectGet');
const deferredActions = new Array;
const deferredModules = new Array;
function defineCollectionProperty(elementType, property, builtClass) {
const descriptor = {
configurable: true,
enumerable: true,
async get() {
const ids = this[property + 'Ids'];
return this[property] = ids
? await (0, storage_1.dataSource)().readMultiple(elementType, ids)
: await (0, storage_1.dataSource)().readCollection(this, property, elementType);
},
set(value) {
delete this[property + 'Ids'];
Object.defineProperty(this, property, { configurable: true, enumerable: true, value, writable: true });
Reflect.defineMetadata(exports.PROTECT_GET, false, this, property);
}
};
Object.defineProperty(builtClass.prototype, property, descriptor);
Reflect.defineMetadata(exports.PROTECT_GET, true, builtClass.prototype, property);
return property;
}
function defineObjectProperty(type, property, builtClass) {
const descriptor = {
configurable: true,
enumerable: true,
async get() {
const id = this[property + 'Id'];
return this[property] = id ? await (0, storage_1.dataSource)().read(type, id) : undefined;
},
set(value) {
delete this[property + 'Id'];
Object.defineProperty(this, property, { configurable: true, enumerable: true, value, writable: true });
Reflect.defineMetadata(exports.PROTECT_GET, false, this, property);
}
};
Object.defineProperty(builtClass.prototype, property, descriptor);
Reflect.defineMetadata(exports.PROTECT_GET, true, builtClass.prototype, property);
return property;
}
function defineCollectionPropertyAction(BuiltClass, properties, property) {
const type = property.collectionType.elementType.lead;
if ((0, class_type_1.isAnyType)(type)) {
properties.push(defineCollectionProperty(type, property.name, BuiltClass));
return true;
}
return false;
}
function defineObjectPropertyAction(BuiltClass, properties, property) {
const type = property.type.lead;
if ((0, class_type_1.isAnyType)(type)) {
properties.push(defineObjectProperty(type, property.name, BuiltClass));
return true;
}
return false;
}
function initClass(classType) {
try {
if (!(0, store_1.storeOf)(classType))
return;
}
catch {
return;
}
const properties = [];
// @ts-ignore TS2415 classType is always a heritable class, not a function.
const BuiltClass = (() => class extends classType {
constructor(...args) {
super(...args);
for (const property of properties) {
const value = Object.getOwnPropertyDescriptor(this, property)?.value;
if ((value === undefined) || (Array.isArray(value) && !value.length)) {
delete this[property];
}
else {
Reflect.defineMetadata(exports.PROTECT_GET, false, this, property);
}
}
}
})();
let resultingBuiltClass = undefined;
for (const property of new reflect_1.ReflectClass(classType).properties) {
const propertyType = property.type;
if (!propertyType)
continue;
if (propertyType instanceof property_type_1.CollectionType) {
if (defineCollectionPropertyAction(BuiltClass, properties, property)) {
resultingBuiltClass = BuiltClass;
}
else if (propertyType.elementType.type instanceof property_type_2.DeferredType) {
resultingBuiltClass = BuiltClass;
deferredActions.push(() => defineCollectionPropertyAction(BuiltClass, properties, property));
}
}
else if (defineObjectPropertyAction(BuiltClass, properties, property)) {
resultingBuiltClass = BuiltClass;
}
else if (propertyType?.type instanceof property_type_2.DeferredType) {
resultingBuiltClass = BuiltClass;
deferredActions.push(() => defineObjectPropertyAction(BuiltClass, properties, property));
}
}
return resultingBuiltClass;
}
function initLazyLoading() {
const already = new Map;
let defer;
const Module = require('module');
const superRequire = Module.prototype.require;
function initModule(module, original) {
defer = undefined;
let replacements = new Map;
for (const [name, type] of Object.entries(original)) {
defer = (defer === undefined)
? (type === undefined)
: (defer && (type === undefined));
if (!(0, class_type_1.isAnyType)(type))
continue;
if (module && replacements) {
const replacement = replacements.get(type);
if (replacement) {
module[name] = replacement;
continue;
}
}
const withORM = initClass(type);
if (!withORM)
continue;
replacements.set(type, withORM);
if (!module) {
module = { ...original };
already.set(original, module);
}
module[name] = withORM;
}
return (defer || replacements.size) ? (module ?? { ...original }) : original;
}
function resolveDeferredActions() {
while (deferredActions.length) {
if (deferredActions[deferredActions.length - 1]())
deferredActions.pop();
else
break;
}
}
function resolveDeferredModules() {
while (deferredModules.length) {
const [original, module, file] = deferredModules[deferredModules.length - 1];
if (initModule(module, original) === original) {
Object.assign(module, original);
}
if (defer)
break;
else {
deferredModules.pop();
}
}
}
Module.prototype.require = function () {
const original = superRequire.call(this, ...arguments);
const alreadyModule = already.get(original);
if (alreadyModule) {
return alreadyModule;
}
already.set(original, original);
const module = initModule(alreadyModule, original);
if (defer) {
// { ...original } into module may be enough. \/ this allows resolving during accessing value, if it is late.
for (const [name, value] of Object.entries(original)) {
Object.defineProperty(module, name, {
configurable: true,
enumerable: true,
get() {
if (original[name] !== undefined) {
resolveDeferredModules();
resolveDeferredActions();
return module[name];
}
return value;
},
set(value) {
let descriptor = Object.getOwnPropertyDescriptor(original, name)
?? { configurable: true, enumerable: true };
delete descriptor.get;
delete descriptor.set;
descriptor.value = value;
Object.defineProperty(module, name, descriptor);
}
});
}
already.set(original, module);
deferredModules.push([original, module, arguments[0]]);
}
resolveDeferredModules();
resolveDeferredActions();
return module ?? original;
};
}
//# sourceMappingURL=lazy-loading.js.map