UNPKG

@steambrew/client

Version:
113 lines (112 loc) 3.96 kB
// TODO: implement storing patches as an option so we can offer unpatchAll selectively // Return this in a replacePatch to call the original method (can still modify args). export let callOriginal = Symbol('MILLENNIUM_CALL_ORIGINAL'); // let patches = new Set<Patch>(); export function beforePatch(object, property, handler, options = {}) { const orig = object[property]; object[property] = function (...args) { handler.call(this, args); const ret = patch.original.call(this, ...args); if (options.singleShot) { patch.unpatch(); } return ret; }; const patch = processPatch(object, property, handler, object[property], orig); return patch; } export function afterPatch(object, property, handler, options = {}) { const orig = object[property]; object[property] = function (...args) { let ret = patch.original.call(this, ...args); ret = handler.call(this, args, ret); if (options.singleShot) { patch.unpatch(); } return ret; }; const patch = processPatch(object, property, handler, object[property], orig); return patch; } export function replacePatch(object, property, handler, options = {}) { const orig = object[property]; object[property] = function (...args) { const ret = handler.call(this, args); // console.debug('[Patcher] replacePatch', patch); if (ret == callOriginal) return patch.original.call(this, ...args); if (options.singleShot) { patch.unpatch(); } return ret; }; const patch = processPatch(object, property, handler, object[property], orig); return patch; } function processPatch(object, property, handler, patchedFunction, original) { // Assign all props of original function to new one Object.assign(object[property], original); // Allow toString webpack filters to continue to work object[property].toString = () => original.toString(); // HACK: for compatibility, remove when all plugins are using new patcher Object.defineProperty(object[property], '__millenniumOrig', { get: () => patch.original, set: (val) => (patch.original = val), }); // Build a Patch object of this patch const patch = { object, property, handler, patchedFunction, original, hasUnpatched: false, unpatch: () => unpatch(patch), }; object[property].__millenniumPatch = patch; return patch; } function unpatch(patch) { const { object, property, handler, patchedFunction, original } = patch; if (patch.hasUnpatched) throw new Error('Function is already unpatched.'); let realProp = property; let realObject = object; console.debug('[Patcher] unpatching', { realObject, realProp, object, property, handler, patchedFunction, original, isEqual: realObject[realProp] === patchedFunction, }); // If another patch has been applied to this function after this one, move down until we find the correct patch while (realObject[realProp] && realObject[realProp] !== patchedFunction) { realObject = realObject[realProp].__millenniumPatch; realProp = 'original'; console.debug('[Patcher] moved to next', { realObject, realProp, object, property, handler, patchedFunction, original, isEqual: realObject[realProp] === patchedFunction, }); } realObject[realProp] = realObject[realProp].__millenniumPatch.original; patch.hasUnpatched = true; console.debug('[Patcher] unpatched', { realObject, realProp, object, property, handler, patchedFunction, original, isEqual: realObject[realProp] === patchedFunction, }); }