UNPKG

@itrocks/lazy-loading

Version:

Integrates lazy loading for objects and collections in TypeScript classes

211 lines 8.44 kB
"use strict"; 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