UNPKG

reactant-module

Version:

A module model for Reactant

1,248 lines (1,214 loc) 69.5 kB
import { create } from 'mutative'; export { apply as applyPatches, current, original, unsafe } from 'mutative'; import { injectable as injectable$1, getLazyDecorator, multiOptional, getMetadata, METADATA_KEY } from 'reactant-di'; export { METADATA_KEY, ModuleRef, Optional, bindModules, createContainer, forwardRef, getLazyDecorator, getMetadata, inject, multiInject, multiOptional, optional } from 'reactant-di'; import { compose as compose$1, createStore as createStore$1, applyMiddleware as applyMiddleware$1, combineReducers } from 'redux'; /****************************************************************************** Copyright (c) Microsoft Corporation. Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ***************************************************************************** */ /* global Reflect, Promise, SuppressedError, Symbol, Iterator */ var extendStatics = function(d, b) { extendStatics = Object.setPrototypeOf || ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; }; return extendStatics(d, b); }; function __extends(d, b) { if (typeof b !== "function" && b !== null) throw new TypeError("Class extends value " + String(b) + " is not a constructor or null"); extendStatics(d, b); function __() { this.constructor = d; } d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); } var __assign = function() { __assign = Object.assign || function __assign(t) { for (var s, i = 1, n = arguments.length; i < n; i++) { s = arguments[i]; for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p]; } return t; }; return __assign.apply(this, arguments); }; function __decorate(decorators, target, key, desc) { var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; return c > 3 && r && Object.defineProperty(target, key, r), r; } function __metadata(metadataKey, metadataValue) { if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(metadataKey, metadataValue); } function __values(o) { var s = typeof Symbol === "function" && Symbol.iterator, m = s && o[s], i = 0; if (m) return m.call(o); if (o && typeof o.length === "number") return { next: function () { if (o && i >= o.length) o = void 0; return { value: o && o[i++], done: !o }; } }; throw new TypeError(s ? "Object is not iterable." : "Symbol.iterator is not defined."); } function __read(o, n) { var m = typeof Symbol === "function" && o[Symbol.iterator]; if (!m) return o; var i = m.call(o), r, ar = [], e; try { while ((n === void 0 || n-- > 0) && !(r = i.next()).done) ar.push(r.value); } catch (error) { e = { error: error }; } finally { try { if (r && !r.done && (m = i["return"])) m.call(i); } finally { if (e) throw e.error; } } return ar; } function __spreadArray(to, from, pack) { if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) { if (ar || !(i in from)) { if (!ar) ar = Array.prototype.slice.call(from, 0, i); ar[i] = from[i]; } } return to.concat(ar || Array.prototype.slice.call(from)); } typeof SuppressedError === "function" ? SuppressedError : function (error, suppressed, message) { var e = new Error(message); return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e; }; var storeKey = Symbol('store'); var loaderKey = Symbol('loader'); var subscriptionsKey = Symbol('subscriptions'); var unsubscriptionsKey = Symbol('unsubscriptions'); var stateKey = Symbol('state'); var defaultStateKey = Symbol('defaultState'); var signalMapKey = Symbol('signalMap'); var enablePatchesKey = Symbol('enablePatches'); var enableAutoComputedKey = Symbol('enableAutoComputed'); var enableAutoFreezeKey = Symbol('enableAutoFreeze'); var strictKey = Symbol('strict'); var enableInspectorKey = Symbol('enableInspector'); var checkActionKey = Symbol('checkAction'); var actionIdentifier = 'REACTANT_ACTION'; var containerKey = Symbol('container'); var identifierKey = Symbol('identifier'); var modulesKey = Symbol('modules'); var nameKey = Symbol('name'); var initStateKey = Symbol('initState'); var dynamicModulesKey = Symbol('dynamicModules'); var ViewModule = /** @class */ (function () { function ViewModule() { // It needs to ensure that the default props of the component in the current instance can be assigned values, // and have the correct 'this' binding. if (typeof this.component !== 'function') { throw new Error("'".concat(Object.getPrototypeOf(this).constructor.name, "' ViewModule 'component' property should be defined class 'method'.")); } this.component = this.component.bind(this); } ViewModule = __decorate([ injectable$1(), __metadata("design:paramtypes", []) ], ViewModule); return ViewModule; }()); var stagedState; var getStagedState = function () { return stagedState; }; /** * ## Description * * `@action` is used to decorate a class method as a action method. * * ## Example * * ```ts * @injectable() * class Counter { * @state * count = 0; * * @action * increase() { * this.count += 1; * } * } * * const app = testBed({ * modules: [], * main: Counter, * }); * * app.instance.increase(); * expect(app.instance.count).toBe(1); * ``` */ var action = function (target, key, descriptor) { var fn = descriptor.value; if (process.env.NODE_ENV !== 'production' && typeof fn !== 'function') { throw new Error("".concat(String(key), " can only be decorated by '@action' as a class method.")); } var value = function () { var _a; var _this = this; var args = []; for (var _i = 0; _i < arguments.length; _i++) { args[_i] = arguments[_i]; } var ref = getRef(this); if (typeof ref.store === 'undefined') { throw new Error("'this' in method '".concat(key.toString(), "' of class '").concat(target.constructor.name, "' decorated by '@action' must be bound to the current class instance.")); } if (typeof ref.checkAction === 'function') { try { ref.checkAction({ target: this, ref: ref, method: key, args: args, }); } catch (error) { console.error(error); } } else if (process.env.NODE_ENV !== 'production' && ref.checkAction !== undefined) { throw new Error("The method '".concat(key, "' decorated by '@action' must be checked by 'checkAction' option function.")); } if (typeof stagedState === 'undefined') { try { var lastState = ref.store.getState(); var state = void 0; var patches = void 0; var inversePatches = void 0; if (ref.enablePatches) { _a = __read(create(lastState, function (draftState) { stagedState = draftState; var result = fn.apply(_this, args); if (process.env.NODE_ENV !== 'production' && result !== undefined) { throw new Error("The return value of the method '".concat(key, "' is not allowed.")); } }, { enablePatches: true, strict: ref.strict, enableAutoFreeze: ref.enableAutoFreeze, }), 3), state = _a[0], patches = _a[1], inversePatches = _a[2]; } else { state = create(lastState, function (draftState) { stagedState = draftState; var result = fn.apply(_this, args); if (process.env.NODE_ENV !== 'production' && result !== undefined) { throw new Error("The return value of the method '".concat(key, "' is not allowed.")); } }, { strict: ref.strict, enableAutoFreeze: ref.enableAutoFreeze, }); } stagedState = undefined; if (process.env.NODE_ENV !== 'production') { var methodName = "".concat(ref.identifier, ".").concat(key.toString()); if (ref.enableInspector && lastState === state) { console.warn("There are no state updates to method '".concat(methodName, "'")); } } ref.store.dispatch(__assign({ type: ref.identifier, method: key, params: args, state: state, _reactant: actionIdentifier }, (ref.enablePatches ? { _patches: patches, _inversePatches: inversePatches, } : {}))); } finally { stagedState = undefined; } } else { // enable staged state mode. fn.apply(this, args); } }; return __assign(__assign({}, descriptor), { value: value }); }; function assign(target, key, value, options) { Object.defineProperty(target, key, __assign({ configurable: true, writable: true, enumerable: true, value: value }, options)); } var isEqual = function (x, y) { if (x === y) { return x !== 0 || y !== 0 || 1 / x === 1 / y; } // eslint-disable-next-line no-self-compare return x !== x && y !== y; }; var isEqualExceptFunction = function (x, y) { if (typeof x === 'function' && typeof y === 'function') return true; return isEqual(x, y); }; var areShallowEqualWithObject = function (objA, objB) { if (isEqualExceptFunction(objA, objB)) return true; if (typeof objA !== 'object' || objA === null || typeof objB !== 'object' || objB === null) { return false; } var keysA = Object.keys(objA); var keysB = Object.keys(objB); if (keysA.length !== keysB.length) return false; for (var i = 0; i < keysA.length; i += 1) { if (!Object.prototype.hasOwnProperty.call(objB, keysA[i]) || !isEqualExceptFunction(objA[keysA[i]], objB[keysA[i]])) { return false; } } return true; }; function areShallowEqualWithArray(prev, next) { if (prev === null || next === null || prev.length !== next.length) { return false; } var length = prev.length; for (var i = 0; i < length; i += 1) { if (!isEqual(prev[i], next[i])) { return false; } } return true; } var getStageName = function (className) { return "@@reactant/".concat(className, "/").concat(Math.random().toString(36)); }; var perform = function (funs, parameter) { return funs.reduce(function (param, fun) { return fun(param); }, parameter); }; var getComposeEnhancers = function (enableReduxDevTools, reduxDevToolsOptions) { try { var reduxDevToolsCompose = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__; return typeof reduxDevToolsCompose === 'function' && enableReduxDevTools ? reduxDevToolsCompose(__assign({ serialize: true, actionSanitizer: function (action) { return action._reactant === actionIdentifier ? __assign(__assign({}, action), { type: "@@reactant/".concat(action.type, "/").concat(action.method) }) : action; } }, reduxDevToolsOptions)) : compose$1; } catch (e) { return compose$1; } }; var compose = compose$1; /* eslint-disable @typescript-eslint/no-explicit-any */ /* eslint-disable func-names */ /* eslint-disable prefer-rest-params */ function defaultMemoize(func) { var lastArgs = new Map(); var lastResult = new Map(); return function () { var _a; if (!areShallowEqualWithArray((_a = lastArgs.get(this)) !== null && _a !== void 0 ? _a : [], arguments)) { lastResult.set(this, func.apply(this, arguments)); } lastArgs.set(this, arguments); return lastResult.get(this); }; } var createSelectorCreatorWithArray = function (memoize) { if (memoize === void 0) { memoize = defaultMemoize; } return function (dependenciesFunc, resultFunc) { var memoizedResultFunc = memoize(function () { return resultFunc.apply(this, arguments); }); return function () { return memoizedResultFunc.apply(this, dependenciesFunc.apply(null, [this])); }; }; }; var createSelectorWithArray = createSelectorCreatorWithArray(); /** * ## Description * * You can use `@autobind` and decorate any class method that binds the instance of the current class as its `this`, * it can also be used with `@action`. * * ## Example * * ```ts * class Shop { * @state * count = 0; * * list: string[] = []; * * @autobind * @action * increase() { * this.count += 0; * } * * @autobind * addGood(text) { * this.list.push(text); * } * } * * const app = testBed({ * modules: [], * main: Shop, * }); * * const { increase, addGood } = app.instance; * increase(); * addGood('apple'); * expect(app.instance.count).toBe(1); * expect(app.instance.list).toEqual(['apple']); * ``` */ function autobind(target, key, _a) { var value = _a.value, configurable = _a.configurable, enumerable = _a.enumerable; if (process.env.NODE_ENV !== 'production' && typeof value !== 'function') { throw new SyntaxError("@autobind decorator must be applied to functions not: ".concat(typeof value)); } return { configurable: configurable, enumerable: enumerable, get: function () { if (this === target) { return value; } if (target.constructor !== this.constructor && target.constructor === Object.getPrototypeOf(this).constructor) { return value; } var boundFn = value.bind(this); assign(this, key, boundFn, { enumerable: false }); return boundFn; }, set: function (setValue) { assign(this, key, setValue); }, }; } /** * ## Description * * `@state` is used to decorate a class property as a state field. * * ## Example * * ```ts * @injectable() * class Counter { * @state * count = 0; * } * * const app = testBed({ * modules: [], * main: Counter, * }); * * expect(app.instance.count).toBe(0); * ``` */ function state(target, key, descriptor) { var _a, _b; var service = target; if (process.env.NODE_ENV !== 'production' && typeof key !== 'string') { throw new Error("'@state' decorate ".concat(key.toString(), " error in ").concat(target.constructor.name, " class, it only supports class properties that decorate keys for string types.")); } Object.assign(target, (_a = {}, _a[stateKey] = __assign(__assign({}, service[stateKey]), (_b = {}, _b[key] = undefined, _b)), _a)); } var evalContext; var globalVersion = 0; var untrackedDepth = 0; var untracked = function (callback) { if (untrackedDepth > 0) { return callback(); } var prevContext = evalContext; evalContext = undefined; untrackedDepth += 1; try { return callback(); } finally { untrackedDepth -= 1; evalContext = prevContext; } }; var Signal = /** @class */ (function () { function Signal(value) { this._version = 0; this._value = value; } Signal.prototype._refresh = function () { return true; }; Object.defineProperty(Signal.prototype, "value", { get: function () { var node = this.addDependency(); if (node !== undefined) { node._version = this._version; } return this._value; }, set: function (value) { if (evalContext instanceof Computed) { throw new Error('Computed cannot have side-effects'); } if (value !== this._value) { this._value = value; this._version += 1; globalVersion += 1; } }, enumerable: false, configurable: true }); Signal.prototype.addDependency = function () { if (evalContext === undefined) { return; } var node = this._node; if (node === undefined || node._target !== evalContext) { node = { _version: 0, _source: this, _prevSource: evalContext._sources, _nextSource: undefined, _target: evalContext, _prevTarget: undefined, _nextTarget: undefined, _rollbackNode: node, }; if (evalContext._sources !== undefined) { evalContext._sources._nextSource = node; } evalContext._sources = node; this._node = node; return node; } else if (node._version === -1) { node._version = 0; if (node._nextSource !== undefined) { node._nextSource._prevSource = node._prevSource; if (node._prevSource !== undefined) { node._prevSource._nextSource = node._nextSource; } node._prevSource = evalContext._sources; node._nextSource = undefined; evalContext._sources._nextSource = node; evalContext._sources = node; } return node; } }; return Signal; }()); var signal = function (value) { return new Signal(value); }; var Computed = /** @class */ (function (_super) { __extends(Computed, _super); function Computed(compute) { var _this = _super.call(this, undefined) || this; _this._globalVersion = globalVersion - 1; // TODO: refactor for improving performance _this._flags = new Set([2 /* ComputedFlags.Outdated */]); _this._compute = compute; return _this; } Computed.prototype._refresh = function () { this._flags.delete(1 /* ComputedFlags.Notified */); if (this._flags.has(0 /* ComputedFlags.Running */)) { return false; } if (!this._flags.has(2 /* ComputedFlags.Outdated */) && this._flags.has(4 /* ComputedFlags.Tracking */)) { return true; } this._flags.delete(2 /* ComputedFlags.Outdated */); if (this._globalVersion === globalVersion) { return true; } this._globalVersion = globalVersion; this._flags.add(0 /* ComputedFlags.Running */); if (this._version > 0 && !this.needsToRecompute()) { this._flags.delete(0 /* ComputedFlags.Running */); return true; } var prevContext = evalContext; try { this.prepareSources(); evalContext = this; var value = this._compute(); if (this._flags.has(3 /* ComputedFlags.HasError */) || this._value !== value || this._version === 0) { this._value = value; this._flags.delete(3 /* ComputedFlags.HasError */); this._version += 1; } } catch (err) { this._value = err; this._flags.add(3 /* ComputedFlags.HasError */); this._version += 1; } evalContext = prevContext; this.cleanupSources(); this._flags.delete(0 /* ComputedFlags.Running */); return true; }; Object.defineProperty(Computed.prototype, "value", { get: function () { if (this._flags.has(0 /* ComputedFlags.Running */)) { throw new Error('Cycle detected'); } var node = this.addDependency(); this._refresh(); if (node !== undefined) { node._version = this._version; } if (this._flags.has(3 /* ComputedFlags.HasError */)) { throw this._value; } return this._value; }, enumerable: false, configurable: true }); Computed.prototype.needsToRecompute = function () { for (var node = this._sources; node !== undefined; node = node._nextSource) { if (node._source._version !== node._version || !node._source._refresh() || node._source._version !== node._version) { return true; } } return false; }; Computed.prototype.prepareSources = function () { for (var node = this._sources; node !== undefined; node = node._nextSource) { var rollbackNode = node._source._node; if (rollbackNode !== undefined) { node._rollbackNode = rollbackNode; } node._source._node = node; node._version = -1; if (node._nextSource === undefined) { this._sources = node; break; } } }; Computed.prototype.cleanupSources = function () { var node = this._sources; var head; while (node !== undefined) { var prev = node._prevSource; if (node._version === -1) { if (prev !== undefined) { prev._nextSource = node._nextSource; } if (node._nextSource !== undefined) { node._nextSource._prevSource = prev; } } else { head = node; } node._source._node = node._rollbackNode; if (node._rollbackNode !== undefined) { node._rollbackNode = undefined; } node = prev; } this._sources = head; }; return Computed; }(Signal)); var computed$1 = function (compute) { return new Computed(compute); }; /** * ## Description * * You can use `@computed` to decorate a getter function for derived data, * which quickly solves performance problems for computing derived data. * * if you want to use `@computed` with non-manually maintained dependencies, * you should enable auto computed feature by setting 'autoComputed' to 'true' in the dev options. * * ## Example * * ```ts * class Shop { * @state * fruits = []; * * @state * vegetables = []; * * @computed(({ fruits, vegetables }: Shop) => [fruits, fruits]) * get sum() { * return this.fruits.length + this.vegetables.length; * } * } * ``` */ function computed() { var args = []; for (var _i = 0; _i < arguments.length; _i++) { args[_i] = arguments[_i]; } if (args.length === 1 && typeof args[0] === 'function') { return function (target, key, descriptor) { var depsCallback = args[0]; if (process.env.NODE_ENV !== 'production') { if (typeof descriptor.get !== 'function') { throw new Error("'@computed' should decorate a getter."); } if (typeof depsCallback !== 'function') { throw new Error("@computed() parameter should be a selector function for dependencies collection."); } } var depsCallbackSelector = createSelectorWithArray( // for performance improvement function (that) { var _a; return [(_a = that[storeKey]) === null || _a === void 0 ? void 0 : _a.getState()]; }, // eslint-disable-next-line func-names function () { return depsCallback(this); }); var selector = createSelectorWithArray(function (that) { var stagedState = getStagedState(); if (that[enableAutoComputedKey] && !stagedState) { depsCallback(that); } return depsCallbackSelector.call(that); }, descriptor.get); return __assign(__assign({}, descriptor), { get: function () { return selector.call(this); } }); }; } var descriptor = args[2]; if (process.env.NODE_ENV !== 'production') { if (typeof descriptor.get !== 'function') { throw new Error("'@computed' should decorate a getter."); } } var computedMap = new WeakMap(); return __assign(__assign({}, descriptor), { get: function () { if (!this[enableAutoComputedKey]) { // if the auto computed feature is disabled, return the computed value directly. if (process.env.NODE_ENV !== 'production' && this[storeKey]) { console.warn("You should enable auto computed feature by setting 'autoComputed' to 'true' in the dev options."); } return descriptor.get.call(this); } var stagedState = getStagedState(); if (!this[storeKey]) { return descriptor.get.call(this); } var currentComputed = computedMap.get(this); if (stagedState) { // if the state is staged and the cache value is computed with the current store state, return the cache value. if ((currentComputed === null || currentComputed === void 0 ? void 0 : currentComputed.storeState) === this[storeKey].getState()) { return currentComputed.value; } // because the state is staged and it's a draft, so the cache value is invalid, so we need to recompute the value without signal computed instance. return descriptor.get.call(this); } if (!currentComputed) { var instance = computed$1(descriptor.get.bind(this)); currentComputed = { instance: instance, }; computedMap.set(this, currentComputed); } var currentValue = currentComputed.instance.value; // update the cache value and store state currentComputed.value = currentValue; currentComputed.storeState = this[storeKey].getState(); return currentValue; } }); } // https://github.com/microsoft/TypeScript/issues/338 var lazy = getLazyDecorator(function (serviceIdentifier, target) { try { var services = target[containerKey].getAll(serviceIdentifier); return services.length === 1 ? services[0] : services; } catch (e) { if (process.env.NODE_ENV !== 'production' && (target === null || target === void 0 ? void 0 : target[storeKey])) { console.warn("Failed to get instance of lazy loading module ".concat(serviceIdentifier.toString(), ".")); } } return null; }); /* eslint-disable no-undef-init */ /* eslint-disable @typescript-eslint/no-unused-vars */ /* eslint-disable @typescript-eslint/no-explicit-any */ var dynamic = function (serviceIdentifierOrName, options) { return function (target, key) { var _a, _b; var multipleInject = (_a = options === null || options === void 0 ? void 0 : options.multiple) !== null && _a !== void 0 ? _a : false; var useToken = (_b = options === null || options === void 0 ? void 0 : options.useToken) !== null && _b !== void 0 ? _b : false; if (multipleInject) { multiOptional(serviceIdentifierOrName)(/** @class */ (function () { function class_1() { } return class_1; }())); } function getter() { var _a, _b; if (!useToken) { return (_a = this[modulesKey]) === null || _a === void 0 ? void 0 : _a[serviceIdentifierOrName]; } var dynamicModules = this[dynamicModulesKey]; if (process.env.NODE_ENV !== 'production' && !dynamicModules) { throw new Error("The property '".concat(key.toString(), "' is not readable when class ").concat((_b = this[identifierKey]) !== null && _b !== void 0 ? _b : this.constructor.name, " is constructing.")); } if (dynamicModules.has(serviceIdentifierOrName)) { return dynamicModules.get(serviceIdentifierOrName).value; } var value = undefined; try { var services = this[containerKey].getAll(serviceIdentifierOrName); value = !multipleInject ? services[0] : services; } catch (e) { // } dynamicModules.set(serviceIdentifierOrName, { multiple: multipleInject, value: value, }); return value; } function setter(_) { var _a; throw new Error("Cannot assign to read only 'dynamic' injection property '".concat(key.toString(), "' of class '").concat((_a = this[identifierKey]) !== null && _a !== void 0 ? _a : this.constructor.name, "'")); } // It should be compatible with the TS decorator and the babel decorator return { configurable: true, enumerable: true, get: getter, set: setter, }; }; }; /** * ## Description * * You can use `@injectable()` to decorate an injectable module, which will also allow `emitDecoratorMetadata` to take effect in the decorated class, so the corresponding `@inject()` is optional. * * But if you don't want to use `@injectable()`, then the dependency injection decorator for constructors such as `@inject()` is required, and it must be imported in the corresponding `modules` startup configuration. Therefore, in most cases, it is recommended to use `@injectable()`. * * ## Example * * ```ts * @injectable() * class Bar { * getValue() { * return 'bar'; * } * } * * class Foo { * constructor(@inject() public bar: Bar) {} * } * * @injectable() * class FooBar { * constructor(public bar: Bar, public foo: Foo) {} * } * * const fooBar = testBed({ * modules: [ * Foo // `Foo` is required, but `Bar` will be injected automatically. * ], * main: FooBar, * }); * * expect(fooBar.instance.foo.getValue()).toBe('foo'); * ``` * * If you use JavaScript, then you can only use `@injectable()` to define the full dependency metadata. * * ```js * @injectable() * class Bar { * getValue() { * return 'bar'; * } * } * * @injectable() * class Foo { * getValue() { * return 'foo'; * } * } * * @injectable({ * name: 'fooBar', * deps: [Bar, { provide: 'foo' }], * }) * class FooBar { * constructor(bar, foo) { * this.bar = bar; * this.foo = foo; * } * } * * const fooBar = testBed({ * modules: [ * Bar, * { provide: 'foo', useClass: Foo }, * ], * main: FooBar, * }); * * expect(fooBar.instance.foo.getValue()).toBe('foo'); * ``` */ function injectable(options) { if (options === void 0) { options = {}; } var decorate = injectable$1(options); return function (target) { var name = options.name; if (typeof name === 'string') { Object.defineProperty(target.prototype, nameKey, { enumerable: false, configurable: false, writable: false, value: name, }); } else if (process.env.NODE_ENV !== 'production' && typeof name !== 'undefined') { console.warn("The parameter 'name' of the decorator @injectable(options) used in '".concat(target.name, "' class must be a string.")); } return decorate(target); }; } var PluginModule = /** @class */ (function () { function PluginModule() { } PluginModule = __decorate([ injectable$1() ], PluginModule); return PluginModule; }()); var generatePluginHooks = function () { return ({ middleware: [], beforeCombineRootReducers: [], afterCombineRootReducers: [], enhancer: [], preloadedStateHandler: [], afterCreateStore: [], provider: [], }); }; var mergePluginHooks = function (pluginHooks, _assignPluginHooks, _pushPluginHooks) { Object.keys(pluginHooks).forEach(function (key) { var _a; var assignPluginHooks = _assignPluginHooks[key].filter(Boolean); var pushPluginHooks = _pushPluginHooks[key]; (_a = pluginHooks[key]).push.apply(_a, __spreadArray(__spreadArray([], __read(assignPluginHooks), false), __read(pushPluginHooks), false)); }); }; var assignPlugin = function (service, pluginHooks, index) { if (service instanceof PluginModule && typeof index === 'number') { if (typeof service.beforeCombineRootReducers === 'function') { pluginHooks.beforeCombineRootReducers[index] = function (reducers) { return service.beforeCombineRootReducers(reducers); }; } if (typeof service.afterCombineRootReducers === 'function') { pluginHooks.afterCombineRootReducers[index] = function (rootReducer) { return service.afterCombineRootReducers(rootReducer); }; } if (typeof service.preloadedStateHandler === 'function') { pluginHooks.preloadedStateHandler[index] = function (preloadedState) { return service.preloadedStateHandler(preloadedState); }; } if (typeof service.enhancer === 'function') { pluginHooks.enhancer[index] = service.enhancer.bind(service); } if (typeof service.middleware === 'function') { pluginHooks.middleware[index] = service.middleware.bind(service); } if (typeof service.afterCreateStore === 'function') { pluginHooks.afterCreateStore[index] = service.afterCreateStore.bind(service); } if (typeof service.provider === 'function') { pluginHooks.provider[index] = service.provider.bind(service); } } }; var pushPlugin = function (service, pluginHooks) { if (service instanceof PluginModule) { if (typeof service.beforeCombineRootReducers === 'function') { pluginHooks.beforeCombineRootReducers.push(function (reducers) { return service.beforeCombineRootReducers(reducers); }); } if (typeof service.afterCombineRootReducers === 'function') { pluginHooks.afterCombineRootReducers.push(function (rootReducer) { return service.afterCombineRootReducers(rootReducer); }); } if (typeof service.preloadedStateHandler === 'function') { pluginHooks.preloadedStateHandler.push(function (preloadedState) { return service.preloadedStateHandler(preloadedState); }); } if (typeof service.enhancer === 'function') { pluginHooks.enhancer.push(service.enhancer.bind(service)); } if (typeof service.middleware === 'function') { pluginHooks.middleware.push(service.middleware.bind(service)); } if (typeof service.afterCreateStore === 'function') { pluginHooks.afterCreateStore.push(service.afterCreateStore.bind(service)); } if (typeof service.provider === 'function') { pluginHooks.provider.push(service.provider.bind(service)); } } }; function createStore(_a) { var e_1, _b, e_2, _c; var _d, _e, _f, _g, _h, _j; var modules = _a.modules, container = _a.container, ServiceIdentifiers = _a.ServiceIdentifiers, loadedModules = _a.loadedModules, load = _a.load, dynamicModules = _a.dynamicModules, pluginHooks = _a.pluginHooks, preloadedState = _a.preloadedState, _k = _a.devOptions, devOptions = _k === void 0 ? {} : _k, originalStore = _a.originalStore, beforeReplaceReducer = _a.beforeReplaceReducer, _l = _a.modulesMap, modulesMap = _l === void 0 ? {} : _l; var isExistReducer = false; var store = originalStore; var reducers = {}; var subscriptions = []; var enableAutoFreeze = (_d = devOptions.autoFreeze) !== null && _d !== void 0 ? _d : false; var enableAutoComputed = (_e = devOptions.autoComputed) !== null && _e !== void 0 ? _e : false; var enableReduxDevTools = (_f = devOptions.reduxDevTools) !== null && _f !== void 0 ? _f : process.env.NODE_ENV !== 'production'; var enablePatches = (_g = devOptions.enablePatches) !== null && _g !== void 0 ? _g : false; var enableInspector = (_h = devOptions.enableInspector) !== null && _h !== void 0 ? _h : false; var strict = (_j = devOptions.strict) !== null && _j !== void 0 ? _j : false; var checkAction = devOptions.checkAction; var _pushPluginHooks = generatePluginHooks(); var _assignPluginHooks = generatePluginHooks(); dynamicModules.forEach(function (module, key) { try { var services = container.getAll(key); module.value = !module.multiple ? services[0] : services; } catch (e) { // } }); try { // add Non-dependent `modules` to ServiceIdentifiers config. for (var modules_1 = __values(modules), modules_1_1 = modules_1.next(); !modules_1_1.done; modules_1_1 = modules_1.next()) { var module_1 = modules_1_1.value; var moduleIdentifier = typeof module_1 === 'function' ? module_1 : module_1.provide; if (!ServiceIdentifiers.has(moduleIdentifier)) { ServiceIdentifiers.set(moduleIdentifier, []); } } } catch (e_1_1) { e_1 = { error: e_1_1 }; } finally { try { if (modules_1_1 && !modules_1_1.done && (_b = modules_1.return)) _b.call(modules_1); } finally { if (e_1) throw e_1.error; } } var multipleInjectMap = getMetadata(METADATA_KEY.multiple); // #region sort ServiceIdentifiers // it's just a workaround for the issue of `ServiceIdentifiers` order. // InversifyJS issue: https://github.com/inversify/InversifyJS/issues/1578 var allServiceIdentifiers = Array.from(ServiceIdentifiers); var allServiceIdentifierKeys = Array.from(ServiceIdentifiers.keys()); allServiceIdentifiers.sort(function (_a, _b) { var _c, _d; var _e = __read(_a, 1), a = _e[0]; var _f = __read(_b, 1), b = _f[0]; var aDeps = []; try { aDeps = (_c = Reflect.getMetadata(METADATA_KEY.paramtypes, a)) !== null && _c !== void 0 ? _c : []; } catch (e) { // } var bDeps = []; try { bDeps = (_d = Reflect.getMetadata(METADATA_KEY.paramtypes, b)) !== null && _d !== void 0 ? _d : []; } catch (e) { // } return aDeps.length - bDeps.length; }); ServiceIdentifiers.clear(); // ServiceIdentifiers is sorted by the number of dependencies. // The purpose is to ensure that the module is loaded in the correct order. // The module with fewer dependencies is loaded first. // ServiceIdentifiers is a mutable object, so it needs to be redefined. allServiceIdentifiers.forEach(function (_a) { var _b = __read(_a, 2), ServiceIdentifier = _b[0], value = _b[1]; ServiceIdentifiers.set(ServiceIdentifier, value); }); var _loop_1 = function (ServiceIdentifier) { // `Service` should be bound before `createStore`. var isMultipleInjection = multipleInjectMap.has(ServiceIdentifier); var shouldInstantiate = container.isBound(ServiceIdentifier) && (isMultipleInjection || (!isMultipleInjection && !loadedModules.has(ServiceIdentifier))); if (shouldInstantiate) { var services_1 = container.getAll(ServiceIdentifier); loadedModules.add(ServiceIdentifier); services_1.forEach(function (service, index) { var _a, _b, _c, _d, _e, _f, _g; var indexPlugin = allServiceIdentifierKeys.indexOf(ServiceIdentifier); if (indexPlugin === -1) { pushPlugin(service, _pushPluginHooks); } else { assignPlugin(service, _assignPluginHooks, indexPlugin); } var className = ServiceIdentifier.name; var identifier = typeof ServiceIdentifier === 'string' ? ServiceIdentifier : service[nameKey]; // module identifier prioritizes using DI module token // The `options.name` property of the decorator `@injectable(options)` parameter must be specified as a string, otherwise a staged string will be generated. // this solution replaces the `combineReducers` need `Object.keys` get keys without `symbol` keys. identifier !== null && identifier !== void 0 ? identifier : (identifier = getStageName(className !== null && className !== void 0 ? className : String(ServiceIdentifier))); if (typeof identifier !== 'string') { if (process.env.NODE_ENV !== 'production') { console.error("\n Since '".concat(className, "' module has set the module state, '").concat(className, "' module must set a unique and valid class property 'nameKey' to be used as the module index.\n Example:\n @injectable({ name: 'fooBar' }) // <- add identifier for modules.\n class FooBar {}\n ")); } else { throw new Error("'".concat(className, "' module 'options.name' property in '@injectable(options)' should be defined as a valid 'string'.")); } } if (services_1.length > 1) { // injection about multi-instances identifier += ":".concat(index); } if (modulesMap[identifier]) { throw new Error("Unexpected multiple instances, provider: '".concat(className !== null && className !== void 0 ? className : ServiceIdentifier, "' module '").concat(identifier, "' property and other module conflicts. Please check the token for dependency injection.")); } Object.assign(modulesMap, (_a = {}, _a[identifier] = service, _a)); if (typeof service !== 'object' || service === null || (service[modulesKey] && isMultipleInjection)) { return; } var hasState = toString.call(service[stateKey]) === '[object Object]'; if (hasState) { var signalMap_1 = {}; var isEmptyObject = Object.keys(service[stateKey]).length === 0; if (!isEmptyObject) { var descriptors = (_b = {}, _b[signalMapKey] = { enumerable: false, configurable: false, writable: false, value: signalMap_1, }, _b); var _loop_2 = function (key) { var _h; var descriptor = Object.getOwnPropertyDescriptor(service, key); // eslint-disable-next-line no-continue if (typeof descriptor === 'undefined') return "continue"; Object.assign(service[stateKey], (_h = {}, _h[key] = descriptor.value, _h)); descriptors[key] = { enumerable: true, configurable: true, get: function () { var current = this[stateKey][key]; if (!enableAutoComputed) return current; var stagedState = getStagedState(); if (!stagedState && signalMap_1[key] && !isEqual(signalMap_1[key].value, current)) { try { // Manual update signal value when the state is changed outside the common reducer. signalMap_1[key].value = current; } catch (e) { if (JSON.stringify(signalMap_1[key].value) !== JSON.stringify(current)) { console.error("[Reactant] The '".concat(key, "' state value of the module '").concat(className, "' has been changed outside the common reducer, which may cause the state to be out of sync. Please check middleware to update the state value without signal updating.")); } } } return current; }, set: function (value) { this[stateKey][key] = value; }, }; }; for (var key in service[stateKey]) { _loop_2(key); } var initState_1 = enableAutoFreeze ? create(__assign({}, service[stateKey]), function () { }, { enableAutoFreeze: enableAutoFreeze }) // freeze init state : service[stateKey]; Object.assign(descriptors, (_c = {}, _c[initStateKey] = { enumerable: false, configurable: false, writable: false, value: initState_1, }, _c)); var serviceReducers = Object.keys(initState_1).reduce(function (serviceReducersMapObject, key) { var _a, _b; var value = service[defaultStateKey] && // for custom reducer default state Object.hasOwnProperty.call(service[defaultStateKey], key) ? service[defaultStateKey][key] : initState_1[key]; // support pure reducer if (typeof value === 'function') { var pureReducer_1 = value; var _initState_1 = pureReducer_1(undefined, {}); var reducer_1 = function (state, action) { if (state === void 0) { state = _initState_1; } return action._reactant === actionIdentifier && action.state[identifier] ? action.state[identifier][key] : pureReducer_1(state, action); }; return Object.assign(serviceReducersMapObject, (_a = {}, _a[key] = reducer_1, _a)); } signalMap_1[key] = signal(value); var current = signalMap_1[key]; var reducer = function (state, action) { if (state === void 0) { state = value; } if (action._reactant === actionIdentifier &&