112 lines
2.5 KiB
JavaScript
112 lines
2.5 KiB
JavaScript
import types from "node:util/types";
|
|
import debugUtil from "debug";
|
|
import { isPlainObject } from "@11ty/eleventy-utils";
|
|
|
|
const debug = debugUtil("Dev:Eleventy:Proxy");
|
|
|
|
function wrapObject(target, fallback) {
|
|
if (Object.isFrozen(target)) {
|
|
return target;
|
|
}
|
|
|
|
return new Proxy(target, {
|
|
getOwnPropertyDescriptor(target, prop) {
|
|
let ret;
|
|
|
|
if (Reflect.has(target, prop)) {
|
|
ret = Reflect.getOwnPropertyDescriptor(target, prop);
|
|
} else if (Reflect.has(fallback, prop)) {
|
|
ret = Reflect.getOwnPropertyDescriptor(fallback, prop);
|
|
}
|
|
|
|
return ret;
|
|
},
|
|
has(target, prop) {
|
|
if (Reflect.has(target, prop)) {
|
|
return true;
|
|
}
|
|
|
|
return Reflect.has(fallback, prop);
|
|
},
|
|
ownKeys(target) {
|
|
let s = new Set();
|
|
for (let k of Reflect.ownKeys(target)) {
|
|
s.add(k);
|
|
}
|
|
if (isPlainObject(fallback)) {
|
|
for (let k of Reflect.ownKeys(fallback)) {
|
|
s.add(k);
|
|
}
|
|
}
|
|
return Array.from(s);
|
|
},
|
|
get(target, prop) {
|
|
debug("handler:get", prop);
|
|
|
|
let value = Reflect.get(target, prop);
|
|
|
|
if (Reflect.has(target, prop)) {
|
|
// Already proxied
|
|
if (types.isProxy(value)) {
|
|
return value;
|
|
}
|
|
|
|
if (isPlainObject(value) && Reflect.has(fallback, prop)) {
|
|
if (Object.isFrozen(value)) {
|
|
return value;
|
|
}
|
|
|
|
let ret = wrapObject(value, Reflect.get(fallback, prop));
|
|
debug("handler:get (primary, object)", prop);
|
|
return ret;
|
|
}
|
|
|
|
debug("handler:get (primary)", prop);
|
|
return value;
|
|
}
|
|
|
|
// Does not exist in primary
|
|
if (Reflect.has(fallback, prop)) {
|
|
// fallback has prop
|
|
let fallbackValue = Reflect.get(fallback, prop);
|
|
|
|
if (isPlainObject(fallbackValue)) {
|
|
if (Object.isFrozen(fallbackValue)) {
|
|
return fallbackValue;
|
|
}
|
|
|
|
debug("handler:get (fallback, object)", prop);
|
|
// set empty object on primary
|
|
let emptyObject = {};
|
|
Reflect.set(target, prop, emptyObject);
|
|
|
|
return wrapObject(emptyObject, fallbackValue);
|
|
}
|
|
|
|
debug("handler:get (fallback)", prop);
|
|
return fallbackValue;
|
|
}
|
|
|
|
// primary *and* fallback do _not_ have prop
|
|
debug("handler:get (not on primary or fallback)", prop);
|
|
|
|
return value;
|
|
},
|
|
set(target, prop, value) {
|
|
debug("handler:set", prop);
|
|
|
|
return Reflect.set(target, prop, value);
|
|
},
|
|
});
|
|
}
|
|
|
|
function ProxyWrap(target, fallback) {
|
|
if (!isPlainObject(target) || !isPlainObject(fallback)) {
|
|
throw new Error("ProxyWrap expects objects for both the target and fallback");
|
|
}
|
|
|
|
return wrapObject(target, fallback);
|
|
}
|
|
|
|
export { ProxyWrap };
|