139 lines
5.5 KiB
JavaScript
139 lines
5.5 KiB
JavaScript
|
"use strict";
|
||
|
Object.defineProperty(exports, "__esModule", { value: true });
|
||
|
// This currentContext variable will only be used if the makeSlotClass
|
||
|
// function is called, which happens only if this is the first copy of the
|
||
|
// @wry/context package to be imported.
|
||
|
var currentContext = null;
|
||
|
// This unique internal object is used to denote the absence of a value
|
||
|
// for a given Slot, and is never exposed to outside code.
|
||
|
var MISSING_VALUE = {};
|
||
|
var idCounter = 1;
|
||
|
// Although we can't do anything about the cost of duplicated code from
|
||
|
// accidentally bundling multiple copies of the @wry/context package, we can
|
||
|
// avoid creating the Slot class more than once using makeSlotClass.
|
||
|
var makeSlotClass = function () { return /** @class */ (function () {
|
||
|
function Slot() {
|
||
|
// If you have a Slot object, you can find out its slot.id, but you cannot
|
||
|
// guess the slot.id of a Slot you don't have access to, thanks to the
|
||
|
// randomized suffix.
|
||
|
this.id = [
|
||
|
"slot",
|
||
|
idCounter++,
|
||
|
Date.now(),
|
||
|
Math.random().toString(36).slice(2),
|
||
|
].join(":");
|
||
|
}
|
||
|
Slot.prototype.hasValue = function () {
|
||
|
for (var context_1 = currentContext; context_1; context_1 = context_1.parent) {
|
||
|
// We use the Slot object iself as a key to its value, which means the
|
||
|
// value cannot be obtained without a reference to the Slot object.
|
||
|
if (this.id in context_1.slots) {
|
||
|
var value = context_1.slots[this.id];
|
||
|
if (value === MISSING_VALUE)
|
||
|
break;
|
||
|
if (context_1 !== currentContext) {
|
||
|
// Cache the value in currentContext.slots so the next lookup will
|
||
|
// be faster. This caching is safe because the tree of contexts and
|
||
|
// the values of the slots are logically immutable.
|
||
|
currentContext.slots[this.id] = value;
|
||
|
}
|
||
|
return true;
|
||
|
}
|
||
|
}
|
||
|
if (currentContext) {
|
||
|
// If a value was not found for this Slot, it's never going to be found
|
||
|
// no matter how many times we look it up, so we might as well cache
|
||
|
// the absence of the value, too.
|
||
|
currentContext.slots[this.id] = MISSING_VALUE;
|
||
|
}
|
||
|
return false;
|
||
|
};
|
||
|
Slot.prototype.getValue = function () {
|
||
|
if (this.hasValue()) {
|
||
|
return currentContext.slots[this.id];
|
||
|
}
|
||
|
};
|
||
|
Slot.prototype.withValue = function (value, callback,
|
||
|
// Given the prevalence of arrow functions, specifying arguments is likely
|
||
|
// to be much more common than specifying `this`, hence this ordering:
|
||
|
args, thisArg) {
|
||
|
var _a;
|
||
|
var slots = (_a = {
|
||
|
__proto__: null
|
||
|
},
|
||
|
_a[this.id] = value,
|
||
|
_a);
|
||
|
var parent = currentContext;
|
||
|
currentContext = { parent: parent, slots: slots };
|
||
|
try {
|
||
|
// Function.prototype.apply allows the arguments array argument to be
|
||
|
// omitted or undefined, so args! is fine here.
|
||
|
return callback.apply(thisArg, args);
|
||
|
}
|
||
|
finally {
|
||
|
currentContext = parent;
|
||
|
}
|
||
|
};
|
||
|
// Capture the current context and wrap a callback function so that it
|
||
|
// reestablishes the captured context when called.
|
||
|
Slot.bind = function (callback) {
|
||
|
var context = currentContext;
|
||
|
return function () {
|
||
|
var saved = currentContext;
|
||
|
try {
|
||
|
currentContext = context;
|
||
|
return callback.apply(this, arguments);
|
||
|
}
|
||
|
finally {
|
||
|
currentContext = saved;
|
||
|
}
|
||
|
};
|
||
|
};
|
||
|
// Immediately run a callback function without any captured context.
|
||
|
Slot.noContext = function (callback,
|
||
|
// Given the prevalence of arrow functions, specifying arguments is likely
|
||
|
// to be much more common than specifying `this`, hence this ordering:
|
||
|
args, thisArg) {
|
||
|
if (currentContext) {
|
||
|
var saved = currentContext;
|
||
|
try {
|
||
|
currentContext = null;
|
||
|
// Function.prototype.apply allows the arguments array argument to be
|
||
|
// omitted or undefined, so args! is fine here.
|
||
|
return callback.apply(thisArg, args);
|
||
|
}
|
||
|
finally {
|
||
|
currentContext = saved;
|
||
|
}
|
||
|
}
|
||
|
else {
|
||
|
return callback.apply(thisArg, args);
|
||
|
}
|
||
|
};
|
||
|
return Slot;
|
||
|
}()); };
|
||
|
// We store a single global implementation of the Slot class as a permanent
|
||
|
// non-enumerable symbol property of the Array constructor. This obfuscation
|
||
|
// does nothing to prevent access to the Slot class, but at least it ensures
|
||
|
// the implementation (i.e. currentContext) cannot be tampered with, and all
|
||
|
// copies of the @wry/context package (hopefully just one) will share the
|
||
|
// same Slot implementation. Since the first copy of the @wry/context package
|
||
|
// to be imported wins, this technique imposes a very high cost for any
|
||
|
// future breaking changes to the Slot class.
|
||
|
var globalKey = "@wry/context:Slot";
|
||
|
var host = Array;
|
||
|
exports.Slot = host[globalKey] || function () {
|
||
|
var Slot = makeSlotClass();
|
||
|
try {
|
||
|
Object.defineProperty(host, globalKey, {
|
||
|
value: host[globalKey] = Slot,
|
||
|
enumerable: false,
|
||
|
writable: false,
|
||
|
configurable: false,
|
||
|
});
|
||
|
}
|
||
|
finally {
|
||
|
return Slot;
|
||
|
}
|
||
|
}();
|
||
|
//# sourceMappingURL=slot.js.map
|