react-magnetic-di
Version:
Context driven dependency injection
110 lines (105 loc) • 5.65 kB
JavaScript
;
function _toConsumableArray(r) { return _arrayWithoutHoles(r) || _iterableToArray(r) || _unsupportedIterableToArray(r) || _nonIterableSpread(); }
function _nonIterableSpread() { throw new TypeError("Invalid attempt to spread non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); }
function _unsupportedIterableToArray(r, a) { if (r) { if ("string" == typeof r) return _arrayLikeToArray(r, a); var t = {}.toString.call(r).slice(8, -1); return "Object" === t && r.constructor && (t = r.constructor.name), "Map" === t || "Set" === t ? Array.from(r) : "Arguments" === t || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(t) ? _arrayLikeToArray(r, a) : void 0; } }
function _iterableToArray(r) { if ("undefined" != typeof Symbol && null != r[Symbol.iterator] || null != r["@@iterator"]) return Array.from(r); }
function _arrayWithoutHoles(r) { if (Array.isArray(r)) return _arrayLikeToArray(r); }
function _arrayLikeToArray(r, a) { (null == a || a > r.length) && (a = r.length); for (var e = 0, n = Array(a); e < a; e++) n[e] = r[e]; return n; }
var _require = require('./utils'),
assert = _require.assert,
getComponentDeclaration = _require.getComponentDeclaration;
function processReference(t, path, locationValue, state) {
var _locationValue$diRef;
var self = getComponentDeclaration(t, path.scope);
var bodyPath = path.get('body');
var shadowsOwnName = false;
if (self) {
var selfShadow = bodyPath.scope.getBinding(self.name);
if (selfShadow && selfShadow.scope === bodyPath.scope && selfShadow.path.node.id !== self) {
shadowsOwnName = true;
}
}
// Build list of dependencies
// combining used imports/exports in this function block
// with existing di expression (if any)
var depNames = [];
locationValue.dependencyRefs.forEach(function (n) {
var _n$node;
var name = (_n$node = n.node) === null || _n$node === void 0 ? void 0 : _n$node.name;
// quick check that the path is not detached
if (!name || !n.parentPath) return;
// Some babel plugins might rename imports (eg emotion) and references break
// For now we skip, but ideally we would refresh the reference
if (!bodyPath.scope.getBinding(name)) return;
// Ensure we do not di() self name
if (name === (self === null || self === void 0 ? void 0 : self.name)) {
shadowsOwnName = true;
return;
}
// Ensure we do not duplicate
if (depNames.includes(name)) return;
depNames.push(name);
});
(_locationValue$diRef = locationValue.diRef) === null || _locationValue$diRef === void 0 || (_locationValue$diRef = _locationValue$diRef.container) === null || _locationValue$diRef === void 0 || (_locationValue$diRef = _locationValue$diRef.arguments) === null || _locationValue$diRef === void 0 || _locationValue$diRef.forEach(function (n) {
assert.isValidArgument(t, n, locationValue.diRef, self);
if (!depNames.includes(n.name)) depNames.push(n.name);
});
depNames.sort();
// if there are no valid candidates, exit
if (!depNames.length) return;
var elements = depNames.map(function (v) {
return t.identifier(v);
});
var args = depNames.map(function (v) {
return t.identifier(v);
});
// add di there
var declaration = t.variableDeclaration('const', [t.variableDeclarator(t.arrayPattern(elements), t.callExpression(t.identifier(state.diIdentifier.name), [self && !shadowsOwnName ? t.identifier(self.name) : t.nullLiteral()].concat(_toConsumableArray(args))))]);
// We inject the new declaration either by replacing existing di
// or by replacing and adding the statement at the top.
// We need replacing to ensure we get a path so that registerDeclaration works
var declarationPath;
if (locationValue.diRef) {
declarationPath = locationValue.diRef.getStatementParent();
declarationPath.replaceWith(declaration);
} else {
bodyPath.unshiftContainer('body', declaration);
declarationPath = bodyPath.get('body.0');
}
bodyPath.scope.registerDeclaration(declarationPath);
var argsPaths = declarationPath.get('declarations.0.init.arguments');
argsPaths.forEach(function (argPath, idx) {
var _bodyPath$parentPath;
// the first argument is the self reference
if (idx === 0) return;
// For each argument we get the dependency variable name
// then we rename it locally so we get a new unique identifier.
// Then we manually revert just the argument identifier name back,
// so it still points to the original dependency identifier name
var name = argPath.node.name;
var newName = state.getAlias(name, bodyPath.scope);
bodyPath.scope.rename(name, newName);
argPath.replaceWith(t.identifier(name));
// this is ugly but scope also renames dynamic object computed props
// so we revert that change too
if ((_bodyPath$parentPath = bodyPath.parentPath) !== null && _bodyPath$parentPath !== void 0 && (_bodyPath$parentPath = _bodyPath$parentPath.node) !== null && _bodyPath$parentPath !== void 0 && _bodyPath$parentPath.computed) {
var key = bodyPath.parentPath.get('key');
if (key.isIdentifier() && key.node.name === newName) {
// get [foo] ()
key.replaceWith(t.identifier(name));
} else {
// get [foo()] {} / get [foo.bar] {} / ...
key.traverse({
Identifier: function Identifier(keyPath) {
if (keyPath.node.name === newName) {
keyPath.replaceWith(t.identifier(name));
}
}
});
}
}
});
// ensure we add di import
state.prependDiImport(t);
}
module.exports = processReference;