hardhat
Version:
Hardhat is an extensible developer tool that helps smart contract developers increase productivity by reliably bringing together the tools they want.
189 lines • 8.8 kB
JavaScript
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.lazyFunction = exports.lazyObject = void 0;
const util_1 = __importDefault(require("util"));
const errors_1 = require("../core/errors");
const errors_list_1 = require("../core/errors-list");
const inspect = Symbol.for("nodejs.util.inspect.custom");
/**
* This module provides function to implement proxy-based object, functions, and
* classes (they are functions). They receive an initializer function that it's
* not used until someone interacts with the lazy element.
*
* This functions can also be used like a lazy `require`, creating a proxy that
* doesn't require the module until needed.
*
* The disadvantage of using this technique is that the type information is
* lost wrt `import`, as `require` returns an `any. If done with enough care,
* this can be manually fixed.
*
* TypeScript doesn't emit `require` calls for modules that are imported only
* because of their types. So if one uses lazyObject or lazyFunction along with
* a normal ESM import you can pass the module's type to this function.
*
* An example of this can be:
*
* import findUpT from "find-up";
* export const findUp = lazyFunction<typeof findUpT>(() => require("find-up"));
*
* You can also use it with named exports:
*
* import { EthT } from "web3x/eth";
* const Eth = lazyFunction<typeof EthT>(() => require("web3x/eth").Eth);
*/
function lazyObject(objectCreator) {
return createLazyProxy(objectCreator, (getRealTarget) => ({
[inspect](depth, options, inspectFn = util_1.default.inspect) {
const realTarget = getRealTarget();
const newOptions = { ...options, depth };
return inspectFn(realTarget, newOptions);
},
}), (object) => {
if (object instanceof Function) {
throw new errors_1.HardhatError(errors_list_1.ERRORS.GENERAL.UNSUPPORTED_OPERATION, {
operation: "Creating lazy functions or classes with lazyObject",
});
}
if (typeof object !== "object" || object === null) {
throw new errors_1.HardhatError(errors_list_1.ERRORS.GENERAL.UNSUPPORTED_OPERATION, {
operation: "Using lazyObject with anything other than objects",
});
}
});
}
exports.lazyObject = lazyObject;
// eslint-disable-next-line @typescript-eslint/ban-types
function lazyFunction(functionCreator) {
return createLazyProxy(functionCreator, (getRealTarget) => {
function dummyTarget() { }
dummyTarget[inspect] = function (depth, options, inspectFn = util_1.default.inspect) {
const realTarget = getRealTarget();
const newOptions = { ...options, depth };
return inspectFn(realTarget, newOptions);
};
return dummyTarget;
}, (object) => {
if (!(object instanceof Function)) {
throw new errors_1.HardhatError(errors_list_1.ERRORS.GENERAL.UNSUPPORTED_OPERATION, {
operation: "Using lazyFunction with anything other than functions or classes",
});
}
});
}
exports.lazyFunction = lazyFunction;
function createLazyProxy(targetCreator, dummyTargetCreator, validator) {
let realTarget;
const dummyTarget = dummyTargetCreator(getRealTarget);
function getRealTarget() {
if (realTarget === undefined) {
const target = targetCreator();
validator(target);
// We copy all properties. We won't use them, but help us avoid Proxy
// invariant violations
const properties = Object.getOwnPropertyNames(target);
for (const property of properties) {
const descriptor = Object.getOwnPropertyDescriptor(target, property);
Object.defineProperty(dummyTarget, property, descriptor);
}
Object.setPrototypeOf(dummyTarget, Object.getPrototypeOf(target));
// Using a null prototype seems to tirgger a V8 bug, so we forbid it
// See: https://github.com/nodejs/node/issues/29730
if (Object.getPrototypeOf(target) === null) {
throw new errors_1.HardhatError(errors_list_1.ERRORS.GENERAL.UNSUPPORTED_OPERATION, {
operation: "Using lazyFunction or lazyObject to construct objects/functions with prototype null",
});
}
if (!Object.isExtensible(target)) {
Object.preventExtensions(dummyTarget);
}
realTarget = target;
}
return realTarget;
}
const handler = {
defineProperty(target, property, descriptor) {
Reflect.defineProperty(dummyTarget, property, descriptor);
return Reflect.defineProperty(getRealTarget(), property, descriptor);
},
deleteProperty(target, property) {
Reflect.deleteProperty(dummyTarget, property);
return Reflect.deleteProperty(getRealTarget(), property);
},
get(target, property, receiver) {
// We have this short-circuit logic here to avoid a cyclic require when
// loading Web3.js.
//
// If a lazy object is somehow accessed while its real target is being
// created, it would trigger an endless loop of recreation, which node
// detects and resolve to an empty object.
//
// This happens with Web3.js because we a lazyObject that loads it,
// and expose it as `global.web3`. This Web3.js file accesses
// `global.web3` when it's being loaded, triggering the loop we mentioned
// before: https://github.com/ethereum/web3.js/blob/8574bd3bf11a2e9cf4bcf8850cab13e1db56653f/packages/web3-core-requestmanager/src/givenProvider.js#L41
//
// We just return `undefined` in that case, to not enter into the loop.
//
// **SUPER IMPORTANT NOTE:** Removing this is very tempting, I know. This
// is a horrible hack. The most obvious approach for doing so is to
// remove the `global` elements that trigger this crazy behavior right
// before doing our `require("web3")`, and restore them afterwards.
// **THIS IS NOT ENOUGH** Users, and libraries (!!!!), will have their own
// `require`s that we can't control and will trigger the same bug.
const stack = new Error().stack;
if (stack !== undefined &&
stack.includes("givenProvider.js") &&
realTarget === undefined) {
return undefined;
}
return Reflect.get(getRealTarget(), property, receiver);
},
getOwnPropertyDescriptor(target, property) {
const descriptor = Reflect.getOwnPropertyDescriptor(getRealTarget(), property);
if (descriptor !== undefined) {
Object.defineProperty(dummyTarget, property, descriptor);
}
return descriptor;
},
getPrototypeOf(_target) {
return Reflect.getPrototypeOf(getRealTarget());
},
has(target, property) {
return Reflect.has(getRealTarget(), property);
},
isExtensible(_target) {
return Reflect.isExtensible(getRealTarget());
},
ownKeys(_target) {
return Reflect.ownKeys(getRealTarget());
},
preventExtensions(_target) {
Object.preventExtensions(dummyTarget);
return Reflect.preventExtensions(getRealTarget());
},
set(target, property, value, receiver) {
Reflect.set(dummyTarget, property, value, receiver);
return Reflect.set(getRealTarget(), property, value, receiver);
},
setPrototypeOf(target, prototype) {
Reflect.setPrototypeOf(dummyTarget, prototype);
return Reflect.setPrototypeOf(getRealTarget(), prototype);
},
};
if (dummyTarget instanceof Function) {
// If dummy target is a function, the actual target must be a function too.
handler.apply = (target, thisArg, argArray) => {
// eslint-disable-next-line @typescript-eslint/ban-types
return Reflect.apply(getRealTarget(), thisArg, argArray);
};
handler.construct = (target, argArray, _newTarget) => {
// eslint-disable-next-line @typescript-eslint/ban-types
return Reflect.construct(getRealTarget(), argArray);
};
}
return new Proxy(dummyTarget, handler);
}
//# sourceMappingURL=lazy.js.map
;