Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

react-magnetic-di

Package Overview
Dependencies
Maintainers
1
Versions
36
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

react-magnetic-di - npm Package Compare versions

Comparing version 2.0.0 to 2.0.1

babel-plugin/package.json

46

lib/cjs/babel/index.js

@@ -5,2 +5,14 @@ "use strict";

var PACKAGE_FUNCTION = 'di';
var ENABLED_ENVS = ['development', 'test'];
var getComponentDeclaration = function getComponentDeclaration(t, scope) {
// function declarations
if (scope.parentBlock.declaration) return scope.parentBlock.declaration.id;
if (scope.getBlockParent().block.id) return scope.getBlockParent().block.id; // variable declaration
if (scope.parentBlock.id) return scope.parentBlock.id; // class declarations
if (scope.parentBlock.type.includes('Class')) return scope.parent.block.id;
};
var assert = {

@@ -10,4 +22,4 @@ isValidBlock: function isValidBlock(t, ref) {

if (!t.isFunctionDeclaration(block) && !t.isArrowFunctionExpression(block) && !t.isClassMethod(block)) {
throw ref.buildCodeFrameError('Invalid di(...) call. Must be inside a render function of a component. ');
if (!t.isFunctionDeclaration(block) && !t.isFunctionExpression(block) && !t.isArrowFunctionExpression(block) && !t.isClassMethod(block)) {
throw ref.buildCodeFrameError('Invalid di(...) call: must be inside a render function of a component. ');
}

@@ -17,3 +29,3 @@ },

if (!ref.container.arguments.length) {
throw ref.buildCodeFrameError('Invalid di(...) arguments. Must be called with at least one argument. ');
throw ref.buildCodeFrameError('Invalid di(...) arguments: must be called with at least one argument. ');
}

@@ -24,4 +36,12 @@

})) {
throw ref.buildCodeFrameError('Invalid di(...) arguments. Must be called with plain identifiers. ');
throw ref.buildCodeFrameError('Invalid di(...) arguments: must be called with plain identifiers. ');
}
var decl = getComponentDeclaration(t, ref.scope);
if (decl && ref.container.arguments.some(function (v) {
return v.name === decl.name;
})) {
throw ref.buildCodeFrameError('Invalid di(...) call: cannot inject self.');
}
}

@@ -32,5 +52,8 @@ };

var t = babel.types;
var isEnabledEnv = babel.env(ENABLED_ENVS);
return {
visitor: {
ImportDeclaration: function ImportDeclaration(path) {
ImportDeclaration: function ImportDeclaration(path, _ref) {
var _ref$opts = _ref.opts,
opts = _ref$opts === void 0 ? {} : _ref$opts;
// first we look at the imports:

@@ -49,3 +72,4 @@ // if not our package and not the right function, ignore

return t.isCallExpression(ref.container);
}); // for each of that location we apply a tranformation
});
var isEnabled = isEnabledEnv || Boolean(opts.forceEnable); // for each of that location we apply a tranformation

@@ -60,9 +84,15 @@ references.forEach(function (ref) {

});
var statement = ref.getStatementParent(); // generating variable declarations with array destructuring
var statement = ref.getStatementParent(); // if should not be enabled, just remove the statement and exit
if (!isEnabled) {
statement.remove();
return;
} // generating variable declarations with array destructuring
// assigning them the result of the method call, with arguments
// now wrapped in an array
ref.scope.push({
id: t.arrayPattern(dependencyIdentifiers),
init: t.callExpression(ref.node, [t.arrayExpression(args)])
init: t.callExpression(ref.node, [t.arrayExpression(args), getComponentDeclaration(t, ref.scope) || t.nullLiteral()])
});

@@ -69,0 +99,0 @@ args.forEach(function (argIdentifier) {

6

lib/cjs/react/consumer.js

@@ -12,3 +12,3 @@ "use strict";

function di(deps) {
function di(deps, target) {
// check if babel plugin has been added

@@ -24,5 +24,5 @@ if (Array.isArray(deps)) {

return getDependencies(deps);
return getDependencies(deps, target);
} else {
(0, _utils.warnOnce)("Seems like you are using react-magnetic-di without babel plugin. " + "Please add 'react-magnetic-di/babel' to your babel config to enabled dependency injection. " + 'Without that, di(...) is a no-op.');
(0, _utils.warnOnce)("Seems like you are using react-magnetic-di without Babel plugin. " + "Please add 'react-magnetic-di/babel' to your Babel config to enabled dependency injection. " + 'Without such plugin di(...) is a no-op.');
}

@@ -29,0 +29,0 @@ }

"use strict";
function _typeof(obj) { "@babel/helpers - typeof"; if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") { _typeof = function _typeof(obj) { return typeof obj; }; } else { _typeof = function _typeof(obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; } return _typeof(obj); }
Object.defineProperty(exports, "__esModule", {

@@ -17,2 +19,4 @@ value: true

var _utils = require("./utils");
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { "default": obj }; }

@@ -24,92 +28,57 @@

function _typeof(obj) { "@babel/helpers - typeof"; if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") { _typeof = function _typeof(obj) { return typeof obj; }; } else { _typeof = function _typeof(obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; } return _typeof(obj); }
var DiProvider = function DiProvider(_ref) {
var children = _ref.children,
use = _ref.use,
target = _ref.target;
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
var _useContext = (0, _react.useContext)(_context.Context),
_getDependencies = _useContext.getDependencies; // memo provider value so gets computed only once
function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } }
function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); return Constructor; }
var value = (0, _react.useMemo)(function () {
// create a map of dependency real -> mock components for fast lookup
var useMap = use.reduce(function (m, d) {
return m.set(d[_constants.KEY], d);
}, new Map()); // support single or multiple targets
function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function"); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, writable: true, configurable: true } }); if (superClass) _setPrototypeOf(subClass, superClass); }
var targets = target && (Array.isArray(target) ? target : [target]);
return {
getDependencies: function getDependencies(realDeps, targetChild) {
// First we collect dependencies from parent providers (if any)
// If a dependency is not defined we return the original
var dependencies = _getDependencies(realDeps, targetChild);
function _setPrototypeOf(o, p) { _setPrototypeOf = Object.setPrototypeOf || function _setPrototypeOf(o, p) { o.__proto__ = p; return o; }; return _setPrototypeOf(o, p); }
if (!targetChild || !targets || targets.includes(targetChild)) {
return dependencies.map(function (dep) {
return useMap.get(dep) || dep;
});
}
function _createSuper(Derived) { var hasNativeReflectConstruct = _isNativeReflectConstruct(); return function () { var Super = _getPrototypeOf(Derived), result; if (hasNativeReflectConstruct) { var NewTarget = _getPrototypeOf(this).constructor; result = Reflect.construct(Super, arguments, NewTarget); } else { result = Super.apply(this, arguments); } return _possibleConstructorReturn(this, result); }; }
function _possibleConstructorReturn(self, call) { if (call && (_typeof(call) === "object" || typeof call === "function")) { return call; } return _assertThisInitialized(self); }
function _assertThisInitialized(self) { if (self === void 0) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return self; }
function _isNativeReflectConstruct() { if (typeof Reflect === "undefined" || !Reflect.construct) return false; if (Reflect.construct.sham) return false; if (typeof Proxy === "function") return true; try { Date.prototype.toString.call(Reflect.construct(Date, [], function () {})); return true; } catch (e) { return false; } }
function _getPrototypeOf(o) { _getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf : function _getPrototypeOf(o) { return o.__proto__ || Object.getPrototypeOf(o); }; return _getPrototypeOf(o); }
function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
var DiProvider = /*#__PURE__*/function (_Component) {
_inherits(DiProvider, _Component);
var _super = _createSuper(DiProvider);
function DiProvider(props) {
var _this;
_classCallCheck(this, DiProvider);
_this = _super.call(this, props);
_defineProperty(_assertThisInitialized(_this), "getDependencies", function (realDeps) {
var _assertThisInitialize = _assertThisInitialized(_this),
useMap = _assertThisInitialize.useMap; // First we collect dependencies from parent providers (if any)
// If a dependency is not defined we return the original
var dependencies = _this.context.getDependencies(realDeps);
return dependencies.map(function (dep) {
return useMap.get(dep) || dep;
});
});
_this.useMap = new Map();
props.use.forEach(function (d) {
return _this.useMap.set(d[_constants.KEY], d);
});
_this.state = {
getDependencies: _this.getDependencies
return dependencies;
}
};
return _this;
}
}, [_getDependencies]); // ignore use & target props
_createClass(DiProvider, [{
key: "render",
value: function render() {
var children = this.props.children;
return /*#__PURE__*/_react["default"].createElement(_context.Context.Provider, {
value: this.state
}, children);
}
}]);
return /*#__PURE__*/_react["default"].createElement(_context.Context.Provider, {
value: value
}, children);
};
return DiProvider;
}(_react.Component);
exports.DiProvider = DiProvider;
DiProvider.propTypes = {
children: _propTypes["default"].oneOfType([_propTypes["default"].func, _propTypes["default"].node]),
target: _propTypes["default"].oneOfType([_propTypes["default"].func, _propTypes["default"].arrayOf(_propTypes["default"].func)]),
use: _propTypes["default"].arrayOf(_propTypes["default"].func).isRequired
};
_defineProperty(DiProvider, "contextType", _context.Context);
_defineProperty(DiProvider, "propTypes", {
use: _propTypes["default"].arrayOf(_propTypes["default"].func).isRequired,
children: _propTypes["default"].oneOfType([_propTypes["default"].func, _propTypes["default"].node])
});
function withDi(Comp, deps) {
function withDi(Comp, deps, target) {
var WrappedComponent = function WrappedComponent() {
return /*#__PURE__*/_react["default"].createElement(DiProvider, {
use: deps
use: deps,
target: target
}, /*#__PURE__*/_react["default"].createElement(Comp, null));
};
WrappedComponent.displayName = "withDi(".concat(Comp.displayName || '', ")");
WrappedComponent.displayName = "withDi(".concat((0, _utils.getDisplayName)(Comp), ")");
return WrappedComponent;
}

@@ -6,4 +6,5 @@ "use strict";

});
exports.warnOnce = warnOnce;
exports.getDisplayName = getDisplayName;
exports.mock = mock;
exports.warnOnce = void 0;

@@ -14,3 +15,3 @@ var _constants = require("./constants");

var warnOnce = function warnOnce(message) {
function warnOnce(message) {
if (!hasWarned) {

@@ -21,10 +22,12 @@ // eslint-disable-next-line no-console

}
};
}
exports.warnOnce = warnOnce;
function getDisplayName(Comp) {
return Comp.displayName || Comp.name || 'Unknown';
}
function mock(original, mockImpl) {
mockImpl.displayName = mockImpl.displayName || "di(".concat(original.displayName || original.name, ")");
mockImpl.displayName = mockImpl.displayName || "di(".concat(getDisplayName(original), ")");
mockImpl[_constants.KEY] = original;
return mockImpl;
}
var PACKAGE_NAME = 'react-magnetic-di';
var PACKAGE_FUNCTION = 'di';
var ENABLED_ENVS = ['development', 'test'];
var getComponentDeclaration = function getComponentDeclaration(t, scope) {
// function declarations
if (scope.parentBlock.declaration) return scope.parentBlock.declaration.id;
if (scope.getBlockParent().block.id) return scope.getBlockParent().block.id; // variable declaration
if (scope.parentBlock.id) return scope.parentBlock.id; // class declarations
if (scope.parentBlock.type.includes('Class')) return scope.parent.block.id;
};
var assert = {

@@ -7,4 +19,4 @@ isValidBlock: function isValidBlock(t, ref) {

if (!t.isFunctionDeclaration(block) && !t.isArrowFunctionExpression(block) && !t.isClassMethod(block)) {
throw ref.buildCodeFrameError('Invalid di(...) call. Must be inside a render function of a component. ');
if (!t.isFunctionDeclaration(block) && !t.isFunctionExpression(block) && !t.isArrowFunctionExpression(block) && !t.isClassMethod(block)) {
throw ref.buildCodeFrameError('Invalid di(...) call: must be inside a render function of a component. ');
}

@@ -14,3 +26,3 @@ },

if (!ref.container.arguments.length) {
throw ref.buildCodeFrameError('Invalid di(...) arguments. Must be called with at least one argument. ');
throw ref.buildCodeFrameError('Invalid di(...) arguments: must be called with at least one argument. ');
}

@@ -21,4 +33,12 @@

})) {
throw ref.buildCodeFrameError('Invalid di(...) arguments. Must be called with plain identifiers. ');
throw ref.buildCodeFrameError('Invalid di(...) arguments: must be called with plain identifiers. ');
}
var decl = getComponentDeclaration(t, ref.scope);
if (decl && ref.container.arguments.some(function (v) {
return v.name === decl.name;
})) {
throw ref.buildCodeFrameError('Invalid di(...) call: cannot inject self.');
}
}

@@ -29,5 +49,8 @@ };

var t = babel.types;
var isEnabledEnv = babel.env(ENABLED_ENVS);
return {
visitor: {
ImportDeclaration: function ImportDeclaration(path) {
ImportDeclaration: function ImportDeclaration(path, _ref) {
var _ref$opts = _ref.opts,
opts = _ref$opts === void 0 ? {} : _ref$opts;
// first we look at the imports:

@@ -46,3 +69,4 @@ // if not our package and not the right function, ignore

return t.isCallExpression(ref.container);
}); // for each of that location we apply a tranformation
});
var isEnabled = isEnabledEnv || Boolean(opts.forceEnable); // for each of that location we apply a tranformation

@@ -57,9 +81,15 @@ references.forEach(function (ref) {

});
var statement = ref.getStatementParent(); // generating variable declarations with array destructuring
var statement = ref.getStatementParent(); // if should not be enabled, just remove the statement and exit
if (!isEnabled) {
statement.remove();
return;
} // generating variable declarations with array destructuring
// assigning them the result of the method call, with arguments
// now wrapped in an array
ref.scope.push({
id: t.arrayPattern(dependencyIdentifiers),
init: t.callExpression(ref.node, [t.arrayExpression(args)])
init: t.callExpression(ref.node, [t.arrayExpression(args), getComponentDeclaration(t, ref.scope) || t.nullLiteral()])
});

@@ -66,0 +96,0 @@ args.forEach(function (argIdentifier) {

import { Context } from './context';
import { warnOnce, mock } from './utils';
function di(deps) {
function di(deps, target) {
// check if babel plugin has been added

@@ -15,5 +15,5 @@ if (Array.isArray(deps)) {

return getDependencies(deps);
return getDependencies(deps, target);
} else {
warnOnce("Seems like you are using react-magnetic-di without babel plugin. " + "Please add 'react-magnetic-di/babel' to your babel config to enabled dependency injection. " + 'Without that, di(...) is a no-op.');
warnOnce("Seems like you are using react-magnetic-di without Babel plugin. " + "Please add 'react-magnetic-di/babel' to your Babel config to enabled dependency injection. " + 'Without such plugin di(...) is a no-op.');
}

@@ -20,0 +20,0 @@ }

@@ -1,93 +0,58 @@

function _typeof(obj) { "@babel/helpers - typeof"; if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") { _typeof = function _typeof(obj) { return typeof obj; }; } else { _typeof = function _typeof(obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; } return _typeof(obj); }
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } }
function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); return Constructor; }
function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function"); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, writable: true, configurable: true } }); if (superClass) _setPrototypeOf(subClass, superClass); }
function _setPrototypeOf(o, p) { _setPrototypeOf = Object.setPrototypeOf || function _setPrototypeOf(o, p) { o.__proto__ = p; return o; }; return _setPrototypeOf(o, p); }
function _createSuper(Derived) { var hasNativeReflectConstruct = _isNativeReflectConstruct(); return function () { var Super = _getPrototypeOf(Derived), result; if (hasNativeReflectConstruct) { var NewTarget = _getPrototypeOf(this).constructor; result = Reflect.construct(Super, arguments, NewTarget); } else { result = Super.apply(this, arguments); } return _possibleConstructorReturn(this, result); }; }
function _possibleConstructorReturn(self, call) { if (call && (_typeof(call) === "object" || typeof call === "function")) { return call; } return _assertThisInitialized(self); }
function _assertThisInitialized(self) { if (self === void 0) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return self; }
function _isNativeReflectConstruct() { if (typeof Reflect === "undefined" || !Reflect.construct) return false; if (Reflect.construct.sham) return false; if (typeof Proxy === "function") return true; try { Date.prototype.toString.call(Reflect.construct(Date, [], function () {})); return true; } catch (e) { return false; } }
function _getPrototypeOf(o) { _getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf : function _getPrototypeOf(o) { return o.__proto__ || Object.getPrototypeOf(o); }; return _getPrototypeOf(o); }
function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
import React, { Component } from 'react';
import React, { useContext, useMemo } from 'react';
import PropTypes from 'prop-types';
import { KEY } from './constants';
import { Context } from './context';
export var DiProvider = /*#__PURE__*/function (_Component) {
_inherits(DiProvider, _Component);
import { getDisplayName } from './utils';
export var DiProvider = function DiProvider(_ref) {
var children = _ref.children,
use = _ref.use,
target = _ref.target;
var _super = _createSuper(DiProvider);
var _useContext = useContext(Context),
_getDependencies = _useContext.getDependencies; // memo provider value so gets computed only once
function DiProvider(props) {
var _this;
_classCallCheck(this, DiProvider);
var value = useMemo(function () {
// create a map of dependency real -> mock components for fast lookup
var useMap = use.reduce(function (m, d) {
return m.set(d[KEY], d);
}, new Map()); // support single or multiple targets
_this = _super.call(this, props);
var targets = target && (Array.isArray(target) ? target : [target]);
return {
getDependencies: function getDependencies(realDeps, targetChild) {
// First we collect dependencies from parent providers (if any)
// If a dependency is not defined we return the original
var dependencies = _getDependencies(realDeps, targetChild);
_defineProperty(_assertThisInitialized(_this), "getDependencies", function (realDeps) {
var _assertThisInitialize = _assertThisInitialized(_this),
useMap = _assertThisInitialize.useMap; // First we collect dependencies from parent providers (if any)
// If a dependency is not defined we return the original
if (!targetChild || !targets || targets.includes(targetChild)) {
return dependencies.map(function (dep) {
return useMap.get(dep) || dep;
});
}
var dependencies = _this.context.getDependencies(realDeps);
return dependencies.map(function (dep) {
return useMap.get(dep) || dep;
});
});
_this.useMap = new Map();
props.use.forEach(function (d) {
return _this.useMap.set(d[KEY], d);
});
_this.state = {
getDependencies: _this.getDependencies
return dependencies;
}
};
return _this;
}
}, [_getDependencies]); // ignore use & target props
_createClass(DiProvider, [{
key: "render",
value: function render() {
var children = this.props.children;
return /*#__PURE__*/React.createElement(Context.Provider, {
value: this.state
}, children);
}
}]);
return DiProvider;
}(Component);
_defineProperty(DiProvider, "contextType", Context);
_defineProperty(DiProvider, "propTypes", {
use: PropTypes.arrayOf(PropTypes.func).isRequired,
children: PropTypes.oneOfType([PropTypes.func, PropTypes.node])
});
export function withDi(Comp, deps) {
return /*#__PURE__*/React.createElement(Context.Provider, {
value: value
}, children);
};
DiProvider.propTypes = {
children: PropTypes.oneOfType([PropTypes.func, PropTypes.node]),
target: PropTypes.oneOfType([PropTypes.func, PropTypes.arrayOf(PropTypes.func)]),
use: PropTypes.arrayOf(PropTypes.func).isRequired
};
export function withDi(Comp, deps, target) {
var WrappedComponent = function WrappedComponent() {
return /*#__PURE__*/React.createElement(DiProvider, {
use: deps
use: deps,
target: target
}, /*#__PURE__*/React.createElement(Comp, null));
};
WrappedComponent.displayName = "withDi(".concat(Comp.displayName || '', ")");
WrappedComponent.displayName = "withDi(".concat(getDisplayName(Comp), ")");
return WrappedComponent;
}
import { KEY } from './constants';
var hasWarned = false;
export var warnOnce = function warnOnce(message) {
export function warnOnce(message) {
if (!hasWarned) {

@@ -9,7 +9,10 @@ // eslint-disable-next-line no-console

}
};
}
export function getDisplayName(Comp) {
return Comp.displayName || Comp.name || 'Unknown';
}
export function mock(original, mockImpl) {
mockImpl.displayName = mockImpl.displayName || "di(".concat(original.displayName || original.name, ")");
mockImpl.displayName = mockImpl.displayName || "di(".concat(getDisplayName(original), ")");
mockImpl[KEY] = original;
return mockImpl;
}
{
"name": "react-magnetic-di",
"version": "2.0.0",
"version": "2.0.1",
"description": "Context driven dependency injection",

@@ -27,8 +27,7 @@ "keywords": [

"build:flow": "echo lib/cjs lib/esm | xargs -n 1 cp src/index.js.flow",
"build:babel": "mkdir babel && cp src/babel/index.js babel",
"build": "npm run clean:build -s && npm run build:cjs -s && npm run build:esm -s && npm run build:flow -s",
"test": "jest",
"flow": "flow --max-warnings=0",
"types": "dtslint --expectOnly --localTs node_modules/typescript/lib ./types && tsc && flow --max-warnings=0",
"lint": "eslint ./src",
"preversion": "npm run lint -s && npm run flow -s && npm run test -s",
"preversion": "npm run lint -s && npm run types -s && npm run test -s",
"prepack": "npm run preversion -s && npm run build -s",

@@ -43,20 +42,24 @@ "start": "webpack-dev-server"

"devDependencies": {
"@babel/cli": "^7.8.4",
"@babel/core": "^7.9.6",
"@babel/plugin-proposal-class-properties": "^7.8.3",
"@babel/plugin-transform-runtime": "^7.9.6",
"@babel/preset-env": "^7.9.6",
"@babel/preset-flow": "^7.9.0",
"@babel/preset-react": "^7.9.4",
"@babel/runtime": "^7.9.6",
"@babel/cli": "^7.10.1",
"@babel/core": "^7.10.2",
"@babel/plugin-proposal-class-properties": "^7.10.1",
"@babel/plugin-transform-runtime": "^7.10.1",
"@babel/preset-env": "^7.10.2",
"@babel/preset-flow": "^7.10.1",
"@babel/preset-react": "^7.10.1",
"@babel/runtime": "^7.10.2",
"@testing-library/react": "^10.2.1",
"@types/react": "^16.9.35",
"@types/react-dom": "^16.9.8",
"babel-eslint": "^10.1.0",
"babel-jest": "^26.0.1",
"babel-loader": "^8.1.0",
"dtslint": "^3.6.10",
"enzyme": "~3.11.0",
"enzyme-adapter-react-16": "^1.15.0",
"eslint": "^7.0.0",
"eslint-plugin-flowtype": "^5.1.0",
"eslint": "^7.2.0",
"eslint-plugin-flowtype": "^5.1.3",
"eslint-plugin-import": "^2.20.2",
"eslint-plugin-react": "^7.20.0",
"eslint-plugin-react-hooks": "^4.0.2",
"eslint-plugin-react-hooks": "^4.0.4",
"flow-bin": "^0.125.1",

@@ -69,2 +72,3 @@ "flow-copy-source": "^2.0.9",

"react-dom": "^16.13.1",
"typescript": "^3.9.5",
"webpack": "^4.43.0",

@@ -71,0 +75,0 @@ "webpack-cli": "^3.3.11",

@@ -1,17 +0,22 @@

# react-magnetic-di
<p align="center">
<img src="https://user-images.githubusercontent.com/84136/83958267-1c8f7f00-a8b3-11ea-9725-1d3530af5f8d.png" alt="react-magnetic-di logo" height="150" />
</p>
<h1 align="center">react-magnetic-di</h1>
<p align="center">
<a href="https://www.npmjs.com/package/react-magnetic-di"><img src="https://img.shields.io/npm/v/react-magnetic-di.svg"></a>
<a href="https://bundlephobia.com/result?p=react-magnetic-di"><img src="https://img.shields.io/bundlephobia/minzip/react-magnetic-di.svg" /></a>
<a href="https://codecov.io/gh/albertogasparin/react-magnetic-di"><img src="https://codecov.io/gh/albertogasparin/react-magnetic-di/branch/master/graph/badge.svg" />
<a href="LICENSE"><img src="https://img.shields.io/badge/license-MIT-blue.svg"></a>
<!--a href="CONTRIBUTING"><img src="https://img.shields.io/badge/PRs-welcome-brightgreen.svg" /></a-->
</p>
[![npm](https://img.shields.io/npm/v/react-magnetic-di.svg)](https://www.npmjs.com/package/react-magnetic-di)
[![npm bundle size (minified + gzip)](https://img.shields.io/bundlephobia/minzip/react-magnetic-di.svg)](https://bundlephobia.com/result?p=react-magnetic-di)
[![License](https://img.shields.io/:license-MIT-blue.svg)](http://albertogasparin.mit-license.org)
[![CircleCI](https://circleci.com/gh/albertogasparin/react-magnetic-di.svg?style=shield&circle-token=cc7bd7e07aae2bb3fcde0a2bfb148b5c2208af84)](https://circleci.com/gh/albertogasparin/react-magnetic-di)
[![codecov](https://codecov.io/gh/albertogasparin/react-magnetic-di/branch/master/graph/badge.svg)](https://codecov.io/gh/albertogasparin/react-magnetic-di)
A new take for dependency injection in React for your tests, storybooks and even experiments in production.
- Close-to-zero performance overhead
- Close-to-zero performance overhead on dev/testing
- **Zero** performance overhead on production (code gets stripped unless told otherwise)
- Works with any kind of functions/classes (not only components) and in both class and functional components
- Replaces dependencies across the entire tree
- Replaces dependencies at any depth of the React tree
- Allows selective injection
- Enforces separation of concerns
- Uses Context in light way (not messing up with React internals)
- Enforces separation of concerns, keeps your component API clean
- Just uses Context, it does not mess up with React internals or require

@@ -22,5 +27,5 @@ ## Philosophy

A common pattern to solve this problem is injecting those "dependencies" in the component via props. However, that approach has a some of downsides, like leaking internal implementation details, mixing together injected dependencies with other props and introducing additional complexity when typing props.
A common pattern to solve this problem is injecting those "dependencies" via props or using mocking libraries at import/require level. Those approaches however have some of downsides, like leaking internal implementation details into the component's public API, being quite fragile or introducing additional typing complexity.
`react-magnetic-di` takes inspiration from decorators, and with a light touch of Babel magic and React Context allows you to optionally override such dependencies, with nearly-zero performane overhead (when not using it).
`react-magnetic-di` takes inspiration from decorators, and with a touch of Babel magic and React Context allows you to optionally override such dependencies, with nearly-zero performance overhead while developing/testing (it's basically a function call and a map lookup) and it is fully removed (by default) on production builds.

@@ -37,3 +42,3 @@ ## Usage

Edit your Babel config file (`.babel.rc` / `babel.config.js` / ...) and add:
Edit your Babel config file (`.babelrc` / `babel.config.js` / ...) and add:

@@ -44,3 +49,3 @@ ```js

// ... other plugins
'react-magnetic-di/babel',
'react-magnetic-di/babel-plugin',
],

@@ -103,3 +108,4 @@ ```

// and the replacement implementation as second
const ModalOpen = di.mock(Modal, () => <div />);
// (you can also import { mock } if don't like di prefix)
const ModalOpenMock = di.mock(Modal, () => <div />);
const useQueryMock = di.mock(useQuery, () => ({ data: null }));

@@ -111,5 +117,5 @@

wrappingComponent: DiProvider,
wrappingComponentProps: { use: [ModalOpen, useQuery] },
wrappingComponentProps: { use: [ModalOpenMock, useQueryMock] },
});
expect(container).toMatchSnapshot();
expect(container.html()).toMatchSnapshot();
});

@@ -120,3 +126,3 @@

const { container } = render(<MyComponent />, {
wrapper: (p) => <DiProvider use={[ModalOpen, useQuery]} {...p} />,
wrapper: (p) => <DiProvider use={[ModalOpenMock, useQueryMock]} {...p} />,
});

@@ -128,3 +134,3 @@ expect(container).toMatchSnapshot();

storiesOf('Modal content', module).add('with text', () => (
<DiProvider use={[ModalOpen, useQuery]}>
<DiProvider use={[ModalOpenMock, useQueryMock]}>
<MyComponent />

@@ -135,8 +141,38 @@ </DiProvider>

In the example above we replace all `Modal` and `useQuery` dependencies across all components in the tree with the custom versions.
In the example above we replace all `Modal` and `useQuery` dependencies across all components in the tree with the custom versions. If you want to replace dependencies **only** for a specific component (or set of components) you can use the `target` prop:
## FAQ
```jsx
// story.js
storiesOf('Modal content', module).add('with text', () => (
<DiProvider target={[MyComponent, MyOtherComponent]} use={[ModalOpen]}>
<DiProvider target={MyComponent} use={[useQuery]}>
<MyComponent />
<MyOtherComponent>
</DiProvider>
</DiProvider>
));
```
- Can I replace only one instance for one component? Currently no, but it could be possible.
In the example above `MyComponent` will have both `ModalOpen` and `useQuery` replaced while `MyOtherComponent` only `ModalOpen`. Be aware that `target` needs an actual component declaration to work, so will not work in cases where the component is fully anonymous (eg: `export default () => ...` or `forwardRef(() => ...)`).
### Configuration Options
#### Enable depepndency injection on production (or custom env)
By default dependency injection is enabled on `development` and `test` environments only, which means `di(...)` is removed on production builds. If you want to allow depepency injection on production too (or on a custom env) you can use the `forceEnable` option:
```
// In your .babelrc / babel.config.js
// ... other stuff like presets
plugins: [
// ... other plugins
['react-magnetic-di/babel-plugin', { forceEnable: true }],
],
```
## Current limitations
- Does not support Enzyme shallow ([due to shallow not fully supporting context](https://github.com/enzymejs/enzyme/issues/2176)). If you wish to shallow anyway, you could mock `di` and manually return the array of mocked dependencies, but it is not recommended.
- Does not support dynamic `use` and `target` props
## Contributing

@@ -143,0 +179,0 @@

@@ -19,3 +19,5 @@ declare module 'react-magnetic-di' {

function di(...dependencies: Dependency[]): void;
di.mock = mock;
class di {
static mock: typeof mock;
}
}

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc