127 lines
3.4 KiB
JavaScript
127 lines
3.4 KiB
JavaScript
|
const lodashSet = require("lodash/set");
|
||
|
const lodashGet = require("lodash/get");
|
||
|
const lodashIsPlainObject = require("lodash/isPlainObject");
|
||
|
|
||
|
/* Calculates computed data using Proxies */
|
||
|
class ComputedDataProxy {
|
||
|
constructor(computedKeys) {
|
||
|
if (Array.isArray(computedKeys)) {
|
||
|
this.computedKeys = new Set(computedKeys);
|
||
|
} else {
|
||
|
this.computedKeys = computedKeys;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
isArrayOrPlainObject(data) {
|
||
|
return Array.isArray(data) || lodashIsPlainObject(data);
|
||
|
}
|
||
|
|
||
|
getProxyData(data, keyRef) {
|
||
|
// Set defaults for keys not already set on parent data
|
||
|
let undefinedValue = "__11TY_UNDEFINED__";
|
||
|
if (this.computedKeys) {
|
||
|
for (let key of this.computedKeys) {
|
||
|
if (lodashGet(data, key, undefinedValue) === undefinedValue) {
|
||
|
lodashSet(data, key, "");
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
let proxyData = this._getProxyData(data, keyRef);
|
||
|
return proxyData;
|
||
|
}
|
||
|
|
||
|
_getProxyForObject(dataObj, keyRef, parentKey = "") {
|
||
|
return new Proxy(
|
||
|
{},
|
||
|
{
|
||
|
get: (obj, key) => {
|
||
|
if (typeof key !== "string") {
|
||
|
return obj[key];
|
||
|
}
|
||
|
|
||
|
let newKey = `${parentKey ? `${parentKey}.` : ""}${key}`;
|
||
|
|
||
|
// Issue #1137
|
||
|
// Special case for Collections, always return an Array for collection keys
|
||
|
// so they it works fine with Array methods like `filter`, `map`, etc
|
||
|
if (newKey === "collections") {
|
||
|
keyRef.add(newKey);
|
||
|
return new Proxy(
|
||
|
{},
|
||
|
{
|
||
|
get: (target, key) => {
|
||
|
if (typeof key === "string") {
|
||
|
keyRef.add(`collections.${key}`);
|
||
|
return [];
|
||
|
}
|
||
|
return target[key];
|
||
|
},
|
||
|
}
|
||
|
);
|
||
|
}
|
||
|
|
||
|
let newData = this._getProxyData(dataObj[key], keyRef, newKey);
|
||
|
if (!this.isArrayOrPlainObject(newData)) {
|
||
|
keyRef.add(newKey);
|
||
|
}
|
||
|
return newData;
|
||
|
},
|
||
|
}
|
||
|
);
|
||
|
}
|
||
|
|
||
|
_getProxyForArray(dataArr, keyRef, parentKey = "") {
|
||
|
return new Proxy(new Array(dataArr.length), {
|
||
|
get: (obj, key) => {
|
||
|
if (Array.prototype.hasOwnProperty(key)) {
|
||
|
// remove `filter`, `constructor`, `map`, etc
|
||
|
keyRef.add(parentKey);
|
||
|
return obj[key];
|
||
|
}
|
||
|
|
||
|
// Hm, this needs to be better
|
||
|
if (key === "then") {
|
||
|
keyRef.add(parentKey);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
let newKey = `${parentKey}[${key}]`;
|
||
|
let newData = this._getProxyData(dataArr[key], keyRef, newKey);
|
||
|
if (!this.isArrayOrPlainObject(newData)) {
|
||
|
keyRef.add(newKey);
|
||
|
}
|
||
|
return newData;
|
||
|
},
|
||
|
});
|
||
|
}
|
||
|
|
||
|
_getProxyData(data, keyRef, parentKey = "") {
|
||
|
if (lodashIsPlainObject(data)) {
|
||
|
return this._getProxyForObject(data, keyRef, parentKey);
|
||
|
} else if (Array.isArray(data)) {
|
||
|
return this._getProxyForArray(data, keyRef, parentKey);
|
||
|
}
|
||
|
|
||
|
// everything else!
|
||
|
return data;
|
||
|
}
|
||
|
|
||
|
async findVarsUsed(fn, data = {}) {
|
||
|
let keyRef = new Set();
|
||
|
|
||
|
// careful, logging proxyData will mess with test results!
|
||
|
let proxyData = this.getProxyData(data, keyRef);
|
||
|
|
||
|
// squelch console logs for this fake proxy data pass 😅
|
||
|
// let savedLog = console.log;
|
||
|
// console.log = () => {};
|
||
|
await fn(proxyData);
|
||
|
// console.log = savedLog;
|
||
|
|
||
|
return Array.from(keyRef);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
module.exports = ComputedDataProxy;
|