Press n or j to go to the next uncovered block, b, p or k for the previous block.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 | 116x 1x 556x 116x 440x 440x 440x 440x 1x 116x 116x 116x 116x 116x 116x 5x 5x 1x 4x 4x 116x 109x 5x 1x 8x 116x 11986x 116x 33396x 33396x 1x 33396x 110x 4x 106x 4x 106x 97x 116x 1x | /* Class wrapper throwing errors when getting/setting undeclared properties. ```javascript const obj = proxify({ foo: 42 }); console.log(obj.foo); console.log(obj.bar); // throw ``` Some configuration: ```javascript proxify({ foo: 42 }, { name: 'object', seal: true, sealGet: false, deprecated: [], exposeApi: false, apiNamespace: '__proxy__', }); ``` - `name` : the name of the proxified object for warnings - `seal`: does `proxify` throw error on set undeclared properties? - `sealGet`: does `proxify` throw error on get undeclared properties? this options is separated from `seal` because getting undefined properties can be usefull for type checking for example - `deprecated`: array of property names which produce a deprecate warning on get/set - `warnDeprecationOnce`: only warn once per deprecated property - `exposeApi`: expose an api in the object to manipulate properties (described below) - `apiNamespace`: in which namespace api is exposed ##### proxify API ```javascript const obj = proxify({ foo: 42 }, { exposeApi: true }); console.log(obj.foo); // OK console.log(obj.bar); // throw error obj.__proxy__.registerProp('bar'); console.log(obj.bar); // OK const hasProp = obj.__proxy__.hasProp('baz'); // check WITHOUT warning obj.__proxy__.unregisterProp('bar'); console.log(obj.bar); // throw error ``` */ const getOptions = options => (Object.assign({}, { name: 'object', seal: true, sealGet: false, deprecated: [], warnDeprecationOnce: true, exposeApi: false, apiNamespace: '__proxy__' }, options)); const getPropertyNames = obj => { if (!obj) { return []; } const methods = Object.getOwnPropertyNames(obj.prototype || obj); const proto = Object.getPrototypeOf(obj); return deleteDuplicates([ ...methods, ...getPropertyNames(proto) ]); }; const deleteDuplicates = arr => [...new Set(arr)]; const proxify = (obj, opts = {}) => { Iif (!obj || (typeof obj !== 'object' && typeof obj !== 'function')) { throw Error('proxify only applies on non-null object'); } const options = getOptions(opts); const properties = new Set(['inspect']); const deprecated = new Set(options.deprecated); const warnedDeprecation = new Set(); const warnDeprecation = name => { const hash = `${options.name}.${name}`; if (options.warnDeprecationOnce && warnedDeprecation.has(hash)) { return; } warnedDeprecation.add(hash); // eslint-disable-next-line no-console console.warn(`Warning: ${hash} is deprecated`); }; if (options.exposeApi) { obj[options.apiNamespace] = { registerProp: name => properties.add(name), unregisterProp: name => properties.delete(name), hasProp: name => properties.has(name) }; } [ ...Object.getOwnPropertyNames(obj), ...getPropertyNames(obj) ].forEach(prop => properties.add(prop)); const handler = { get: (target, name) => { Iif (options.sealGet && !properties.has(name)) { throw new Error(`${options.name}.${name} is not defined`); } if (deprecated.has(name)) { warnDeprecation(name); } return target[name]; }, set: (target, name, value) => { if (options.seal && !properties.has(name)) { throw new Error(`Cannot set a value to the undefined '${name}' property in '${options.name}'`); } if (deprecated.has(name)) { warnDeprecation(name); } target[name] = value; return true; } }; return new Proxy(obj, handler); }; module.exports = proxify; |