dependable-react
Advanced tools
Comparing version 0.1.0 to 0.2.0
@@ -1,1 +0,1 @@ | ||
"use strict";Object.defineProperty(exports,"__esModule",{value:!0});var react=require("react"),InjectionToken=function(){function e(e){void 0===e&&(e=""),this.key=new String(e)}return e.prototype.toString=function(){return"InjectionToken("+this.key+")"},e}(),ClearableWeakMap=function(){function e(e){this._wm=new WeakMap(e)}return e.prototype.clear=function(){this._wm=new WeakMap},e.prototype.get=function(e){return this._wm.get(e)},e.prototype.has=function(e){return this._wm.has(e)},e.prototype.set=function(e,n){return this._wm.set(e,n),this},e}(),mappings=new ClearableWeakMap([]);function useInject(e){return react.useMemo(function(){return inject(e)},[e])}function inject(e){if(!mappings.has(e))throw new Error("Injectables have to be provided in a module.");return mappings.get(e)}function isSingleProvider(e){return!("initValue"in e||"initFactory"in e||"initClass"in e)}function preprocessProvider(e){return isSingleProvider(e)?{initClass:e,provider:e}:e}function DefineModule(e){for(var n=0,t=e;n<t.length;n++){var i=preprocessProvider(t[n]),r=i.provider;"initClass"in i?mappings.set(r,new i.initClass):"initValue"in i?mappings.set(r,i.initValue):"initFactory"in i&&mappings.set(r,i.initFactory())}}function GenerateTestBed(e){return mappings.clear(),DefineModule(e)}exports.DefineModule=DefineModule,exports.GenerateTestBed=GenerateTestBed,exports.InjectionToken=InjectionToken,exports.inject=inject,exports.useInject=useInject; | ||
"use strict";Object.defineProperty(exports,"__esModule",{value:!0});var e=require("react"),t=function(){function e(e){void 0===e&&(e=""),this.key=new String(e)}return e.prototype.toString=function(){return"InjectionToken("+this.key+")"},e}(),n=function(){function e(e){void 0===e&&(e=""),this.key=new String(e)}return e.prototype.toString=function(){return"ScopeToken("+this.key+")"},e}(),r=new n("DEFAULT_SCOPE"),i=new t("PARENT_SCOPE"),o=function(){function e(){this._wm=new WeakMap}return e.prototype.get=function(e){return this._wm.get(e)},e.prototype.has=function(e){return this._wm.has(e)},e.prototype.set=function(e,t){return this._wm.set(e,t),this},e}(),s=new WeakMap,u=e.createContext(r);function a(e,t){void 0===t&&(t=r);var n=s.get(t);if(!n)throw new Error("The given scope is not defined");if(!n.has(e)){if(n.has(i))return a(e,n.get(i));throw new Error("Injectables have to be provided in a module.")}return n.get(e)}function c(e){return function(e){return!("initValue"in e||"initFactory"in e||"initClass"in e)}(e)?{initClass:e,provider:e}:e}function f(e,t,u){void 0===t&&(t=r);var a=t instanceof n?t:new n(t);s.has(a)||s.set(a,new o);var f=s.get(a);if(!f)throw new Error("Something went wrong - Dependency map couldn't be generated");if(u){if(f.has(i))throw new Error("Parent scope can't be redefined");f.set(i,u)}for(var p=0,d=e;p<d.length;p++){var v=c(d[p]),w=v.provider;"initClass"in v?f.set(w,new v.initClass(a)):"initValue"in v?f.set(w,v.initValue):"initFactory"in v&&f.set(w,v.initFactory(a))}return a}function p(e,t,i){void 0===t&&(t=r);var u=t instanceof n?t:new n(t);return s.set(u,new o),f(e,u,i)}exports.DefineModule=f,exports.GenerateTestBed=p,exports.InjectionProvider=function(t){var r=t.children,i=t.parentScope,o=t.providers,s=t.scope,a=t.test,c=e.useContext(u),d=i||(a?void 0:c),v=s||(a?void 0:new n("CONTEXT_SCOPE")),w=a?p(o,v,d):f(o,v,d);return e.createElement(u.Provider,{value:w},r)},exports.InjectionToken=t,exports.inject=a,exports.useInject=function(t,n){var r=e.useContext(u),i=n||r;return e.useMemo((function(){return a(t,i)}),[t,i])}; |
export { InjectionToken } from './InjectionToken'; | ||
export { inject, useInject } from './inject'; | ||
export { DefineModule, GenerateTestBed } from './module'; | ||
export { Provider as InjectionProvider } from './Provider'; |
@@ -1,2 +0,2 @@ | ||
import { useMemo } from 'react'; | ||
import { createContext, useContext, useMemo, createElement } from 'react'; | ||
@@ -14,39 +14,60 @@ var InjectionToken = /** @class */ (function () { | ||
var ClearableWeakMap = /** @class */ (function () { | ||
function ClearableWeakMap(init) { | ||
this._wm = new WeakMap(init); | ||
var ScopeToken = /** @class */ (function () { | ||
function ScopeToken(key) { | ||
if (key === void 0) { key = ''; } | ||
this.key = new String(key); | ||
} | ||
ClearableWeakMap.prototype.clear = function () { | ||
ScopeToken.prototype.toString = function () { | ||
return "ScopeToken(" + this.key + ")"; | ||
}; | ||
return ScopeToken; | ||
}()); | ||
var DEFAULT_SCOPE = new ScopeToken('DEFAULT_SCOPE'); | ||
var PARENT_SCOPE_KEY = new InjectionToken('PARENT_SCOPE'); | ||
var DependencyMap = /** @class */ (function () { | ||
function DependencyMap() { | ||
this._wm = new WeakMap(); | ||
}; | ||
ClearableWeakMap.prototype.get = function (k) { | ||
} | ||
DependencyMap.prototype.get = function (k) { | ||
return this._wm.get(k); | ||
}; | ||
ClearableWeakMap.prototype.has = function (k) { | ||
DependencyMap.prototype.has = function (k) { | ||
return this._wm.has(k); | ||
}; | ||
ClearableWeakMap.prototype.set = function (k, v) { | ||
DependencyMap.prototype.set = function (k, v) { | ||
this._wm.set(k, v); | ||
return this; | ||
}; | ||
return ClearableWeakMap; | ||
return DependencyMap; | ||
}()); | ||
var mappings = new ClearableWeakMap([]); | ||
var mappings = new WeakMap(); | ||
function useInject(cls) { | ||
var InjectionContext = createContext(DEFAULT_SCOPE); | ||
function useInject(cls, scope) { | ||
var contextScope = useContext(InjectionContext); | ||
var moduleScope = scope || contextScope; | ||
/* istanbul ignore next */ | ||
return useMemo(function () { return inject(cls); }, [cls]); | ||
return useMemo(function () { return inject(cls, moduleScope); }, [cls, moduleScope]); | ||
} | ||
function inject(cls) { | ||
var isInitialized = mappings.has(cls); | ||
function inject(cls, scope) { | ||
if (scope === void 0) { scope = DEFAULT_SCOPE; } | ||
var map = mappings.get(scope); | ||
if (!map) { | ||
throw new Error('The given scope is not defined'); | ||
} | ||
var isInitialized = map.has(cls); | ||
if (!isInitialized) { | ||
if (map.has(PARENT_SCOPE_KEY)) { | ||
return inject(cls, map.get(PARENT_SCOPE_KEY)); | ||
} | ||
throw new Error('Injectables have to be provided in a module.'); | ||
} | ||
return mappings.get(cls); | ||
return map.get(cls); | ||
} | ||
function isSingleProvider(provider) { | ||
return (!('initValue' in provider) && | ||
!('initFactory' in provider) && | ||
!('initClass' in provider)); | ||
return !('initValue' in provider) && !('initFactory' in provider) && !('initClass' in provider); | ||
} | ||
@@ -63,3 +84,19 @@ function preprocessProvider(provider) { | ||
function DefineModule(providers) { | ||
function DefineModule(providers, scope, parentScope) { | ||
if (scope === void 0) { scope = DEFAULT_SCOPE; } | ||
var scopeToken = scope instanceof ScopeToken ? scope : new ScopeToken(scope); | ||
if (!mappings.has(scopeToken)) { | ||
mappings.set(scopeToken, new DependencyMap()); | ||
} | ||
var map = mappings.get(scopeToken); | ||
if (!map) { | ||
/* istanbul ignore next */ | ||
throw new Error("Something went wrong - Dependency map couldn't be generated"); | ||
} | ||
if (parentScope) { | ||
if (map.has(PARENT_SCOPE_KEY)) { | ||
throw new Error("Parent scope can't be redefined"); | ||
} | ||
map.set(PARENT_SCOPE_KEY, parentScope); | ||
} | ||
for (var _i = 0, providers_1 = providers; _i < providers_1.length; _i++) { | ||
@@ -70,17 +107,31 @@ var provider = providers_1[_i]; | ||
if ('initClass' in prov) { | ||
mappings.set(providerToken, new prov.initClass()); | ||
map.set(providerToken, new prov.initClass(scopeToken)); | ||
} | ||
else if ('initValue' in prov) { | ||
mappings.set(providerToken, prov.initValue); | ||
map.set(providerToken, prov.initValue); | ||
} | ||
else if ('initFactory' in prov) { | ||
mappings.set(providerToken, prov.initFactory()); | ||
map.set(providerToken, prov.initFactory(scopeToken)); | ||
} | ||
} | ||
return scopeToken; | ||
} | ||
function GenerateTestBed(providers) { | ||
mappings.clear(); | ||
return DefineModule(providers); | ||
function GenerateTestBed(providers, scope, parentScope) { | ||
if (scope === void 0) { scope = DEFAULT_SCOPE; } | ||
var scopeToken = scope instanceof ScopeToken ? scope : new ScopeToken(scope); | ||
mappings.set(scopeToken, new DependencyMap()); | ||
return DefineModule(providers, scopeToken, parentScope); | ||
} | ||
export { DefineModule, GenerateTestBed, InjectionToken, inject, useInject }; | ||
var Provider = function (_a) { | ||
var children = _a.children, parentScope = _a.parentScope, providers = _a.providers, scope = _a.scope, test = _a.test; | ||
var contextScope = useContext(InjectionContext); | ||
var parentScopeId = parentScope || (test ? undefined : contextScope); | ||
var moduleScopeId = scope || (test ? undefined : new ScopeToken('CONTEXT_SCOPE')); | ||
var moduleScope = test | ||
? GenerateTestBed(providers, moduleScopeId, parentScopeId) | ||
: DefineModule(providers, moduleScopeId, parentScopeId); | ||
return createElement(InjectionContext.Provider, { value: moduleScope }, children); | ||
}; | ||
export { DefineModule, GenerateTestBed, Provider as InjectionProvider, InjectionToken, inject, useInject }; |
@@ -0,3 +1,4 @@ | ||
import { ScopeToken } from './ScopeToken'; | ||
import { TKey } from './types'; | ||
export declare function useInject<T>(cls: TKey<T>): T; | ||
export declare function inject<T>(cls: TKey<T>): T; | ||
export declare function useInject<T>(cls: TKey<T>, scope?: ScopeToken): T; | ||
export declare function inject<T>(cls: TKey<T>, scope?: ScopeToken): T; |
@@ -0,6 +1,6 @@ | ||
import { ScopeToken } from './ScopeToken'; | ||
import { TKey } from './types'; | ||
declare class ClearableWeakMap<T = any> { | ||
export declare class DependencyMap<T = any> { | ||
private _wm; | ||
constructor(init: Iterable<any>); | ||
clear(): void; | ||
constructor(); | ||
get<U extends T>(k: TKey<U>): U | undefined; | ||
@@ -10,3 +10,2 @@ has<U extends T>(k: TKey<U>): boolean; | ||
} | ||
export declare const mappings: ClearableWeakMap<any>; | ||
export {}; | ||
export declare const mappings: WeakMap<ScopeToken, DependencyMap<any>>; |
@@ -0,3 +1,4 @@ | ||
import { ScopeToken } from './ScopeToken'; | ||
import { TProvider } from './types'; | ||
export declare function DefineModule(providers: Array<TProvider>): void; | ||
export declare function GenerateTestBed(providers: Array<TProvider>): void; | ||
export declare function DefineModule(providers: Array<TProvider>, scope?: ScopeToken | string, parentScope?: ScopeToken): ScopeToken; | ||
export declare function GenerateTestBed(providers: Array<TProvider>, scope?: ScopeToken | string, parentScope?: ScopeToken): ScopeToken; |
import { InjectionToken } from './InjectionToken'; | ||
export declare type IConstructor<T = any> = new () => T; | ||
import { ScopeToken } from './ScopeToken'; | ||
export declare type IConstructor<T = any> = new (scope?: ScopeToken) => T; | ||
export declare type TKey<T = any> = InjectionToken<T> | IConstructor<T>; | ||
@@ -9,3 +10,3 @@ export declare type TComplexProvider<T = any> = { | ||
provider: TKey<T>; | ||
initFactory(): T; | ||
initFactory(scope?: ScopeToken): T; | ||
} | { | ||
@@ -12,0 +13,0 @@ provider: TKey<T>; |
{ | ||
"name": "dependable-react", | ||
"version": "0.1.0", | ||
"version": "0.2.0", | ||
"description": "", | ||
@@ -10,19 +10,23 @@ "main": "dist/index.cjs.js", | ||
"devDependencies": { | ||
"@types/jest": "^24.0.11", | ||
"@types/react": "^16.8.14", | ||
"husky": "^1.3.1", | ||
"jest": "^24.7.1", | ||
"react": "^16.8.6", | ||
"rollup": "^1.10.1", | ||
"rollup-plugin-typescript2": "^0.20.1", | ||
"rollup-plugin-uglify": "^6.0.2", | ||
"ts-jest": "^24.0.2", | ||
"typescript": "^3.4.4" | ||
"@types/jest": "^24.0.21", | ||
"@types/react": "^16.9.11", | ||
"@types/react-test-renderer": "^16.9.1", | ||
"husky": "^3.0.9", | ||
"jest": "^24.9.0", | ||
"prettier": "^1.18.2", | ||
"react": "^16.11.0", | ||
"react-test-renderer": "^16.11.0", | ||
"rollup": "^1.26.3", | ||
"rollup-plugin-terser": "^5.1.2", | ||
"rollup-plugin-typescript2": "^0.24.3", | ||
"rollup-plugin-uglify": "^6.0.3", | ||
"ts-jest": "^24.1.0", | ||
"typescript": "^3.6.4" | ||
}, | ||
"peerDependencies": { | ||
"react": "^16.8.0" | ||
"react": "^16.10.2" | ||
}, | ||
"husky": { | ||
"hooks": { | ||
"pre-commit": "npm test" | ||
"pre-commit": "npm run lint && npm test" | ||
} | ||
@@ -33,3 +37,4 @@ }, | ||
"build": "rollup -c", | ||
"prepublish": "npm run build" | ||
"prepare": "npm run build", | ||
"lint": "prettier --check **/*.{ts,tsx} !dist/**" | ||
}, | ||
@@ -36,0 +41,0 @@ "repository": { |
@@ -21,4 +21,6 @@ # Dependable React | ||
You can also define a parent scope to enable scope nesting. | ||
```typescript | ||
function DefineModule(providers: Array<TProvider>): void; | ||
function DefineModule(providers: Array<TProvider>, scope?: ScopeToken | string, parentScope?: ScopeToken): ScopeToken; | ||
``` | ||
@@ -40,2 +42,22 @@ | ||
### InjectionProvider | ||
A provider that creates a new scope and passes it to the `useInject` automatically. This enables structural scoping without worying about the scope tokens. | ||
```typescript | ||
function InjectionProvider(options: { | ||
providers: Array<TProvider>; | ||
scope?: ScopeToken | string; | ||
parentScope?: ScopeToken; | ||
}); | ||
``` | ||
```typescript | ||
const App = () => ( | ||
<InjectionProvider provider={[DataService]}> | ||
<SomeComponent /> | ||
</InjectionProvider> | ||
); | ||
``` | ||
### GenerateTestBed | ||
@@ -46,3 +68,7 @@ | ||
```typescript | ||
function GenerateTestBed(providers: Array<TProvider>): void; | ||
function GenerateTestBed( | ||
providers: Array<TProvider>, | ||
scope?: ScopeToken | string, | ||
parentScope?: ScopeToken, | ||
): ScopeToken; | ||
``` | ||
@@ -101,3 +127,3 @@ | ||
```typescript | ||
function inject<T>(cls: IConstructor<T> | InjectionToken<T>): T; | ||
function inject<T>(cls: IConstructor<T> | InjectionToken<T>, scope?: ScopeToken): T; | ||
``` | ||
@@ -122,5 +148,19 @@ | ||
```typescript | ||
function useInject<T>(cls: IConstructor<T> | InjectionToken<T>): T; | ||
function useInject<T>(cls: IConstructor<T> | InjectionToken<T>, scope?: ScopeToken): T; | ||
``` | ||
## Scope & SSR support | ||
In order to support Server-side rendering and other use cases that require multiple scopes, there is a possibility to define a specific scope. | ||
The scope will be passed to the class as the constructor argument or to a factory as a function argument. That way the class or factory can pass it to inject if they need it. | ||
### useContextInject | ||
```typescript | ||
function useContextInject<T>(cls: TKey<T>, contextKey: Context<ScopeToken>): T; | ||
``` | ||
To make things simpler with React, the most likely case is to pass the scope through the React context. To avoid two operations (`useContext` to get the scope, and then `useInject`), there is one more react hook called `useContextInject`. | ||
TODO | ||
@@ -127,0 +167,0 @@ |
17036
16
199
186
14