123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105 |
- 'use strict';
- const assert = require('assert');
- const is = require('is-type-of');
- const FileLoader = require('./file_loader');
- const CLASSLOADER = Symbol('classLoader');
- const EXPORTS = FileLoader.EXPORTS;
- class ClassLoader {
- constructor(options) {
- assert(options.ctx, 'options.ctx is required');
- const properties = options.properties;
- this._cache = new Map();
- this._ctx = options.ctx;
- for (const property in properties) {
- this.defineProperty(property, properties[property]);
- }
- }
- defineProperty(property, values) {
- Object.defineProperty(this, property, {
- get() {
- let instance = this._cache.get(property);
- if (!instance) {
- instance = getInstance(values, this._ctx);
- this._cache.set(property, instance);
- }
- return instance;
- },
- });
- }
- }
- /**
- * Same as {@link FileLoader}, but it will attach file to `inject[fieldClass]`. The exports will be lazy loaded, such as `ctx.group.repository`.
- * @extends FileLoader
- * @since 1.0.0
- */
- class ContextLoader extends FileLoader {
- /**
- * @class
- * @param {Object} options - options same as {@link FileLoader}
- * @param {String} options.fieldClass - determine the field name of inject object.
- */
- constructor(options) {
- assert(options.property, 'options.property is required');
- assert(options.inject, 'options.inject is required');
- const target = options.target = {};
- if (options.fieldClass) {
- options.inject[options.fieldClass] = target;
- }
- super(options);
- const app = this.options.inject;
- const property = options.property;
- // define ctx.service
- Object.defineProperty(app.context, property, {
- get() {
- // distinguish property cache,
- // cache's lifecycle is the same with this context instance
- // e.x. ctx.service1 and ctx.service2 have different cache
- if (!this[CLASSLOADER]) {
- this[CLASSLOADER] = new Map();
- }
- const classLoader = this[CLASSLOADER];
- let instance = classLoader.get(property);
- if (!instance) {
- instance = getInstance(target, this);
- classLoader.set(property, instance);
- }
- return instance;
- },
- });
- }
- }
- module.exports = ContextLoader;
- function getInstance(values, ctx) {
- // it's a directory when it has no exports
- // then use ClassLoader
- const Class = values[EXPORTS] ? values : null;
- let instance;
- if (Class) {
- if (is.class(Class)) {
- instance = new Class(ctx);
- } else {
- // it's just an object
- instance = Class;
- }
- // Can't set property to primitive, so check again
- // e.x. module.exports = 1;
- } else if (is.primitive(values)) {
- instance = values;
- } else {
- instance = new ClassLoader({ ctx, properties: values });
- }
- return instance;
- }
|