summaryrefslogtreecommitdiff
path: root/node_modules/@11ty/eleventy/src/Data/ComputedData.js
blob: 5350475ca205e8a730b1dab5b8f267d41a0174f3 (plain)
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
import lodash from "@11ty/lodash-custom";
import debugUtil from "debug";

import ComputedDataQueue from "./ComputedDataQueue.js";
import ComputedDataTemplateString from "./ComputedDataTemplateString.js";
import ComputedDataProxy from "./ComputedDataProxy.js";

const { set: lodashSet, get: lodashGet } = lodash;
const debug = debugUtil("Eleventy:ComputedData");

class ComputedData {
	constructor(config) {
		this.computed = {};
		this.symbolParseFunctions = {};
		this.templateStringKeyLookup = {};
		this.computedKeys = new Set();
		this.declaredDependencies = {};
		this.queue = new ComputedDataQueue();
		this.config = config;
	}

	add(key, renderFn, declaredDependencies = [], symbolParseFn, templateInstance) {
		this.computedKeys.add(key);
		this.declaredDependencies[key] = declaredDependencies;

		// bind config filters/JS functions
		if (typeof renderFn === "function") {
			let fns = {};
			// TODO bug? no access to non-universal config things?
			if (this.config) {
				fns = {
					...this.config.javascriptFunctions,
				};
			}
			fns.tmpl = templateInstance;

			renderFn = renderFn.bind(fns);
		}

		lodashSet(this.computed, key, renderFn);

		if (symbolParseFn) {
			lodashSet(this.symbolParseFunctions, key, symbolParseFn);
		}
	}

	addTemplateString(key, renderFn, declaredDependencies = [], symbolParseFn, templateInstance) {
		this.add(key, renderFn, declaredDependencies, symbolParseFn, templateInstance);
		this.templateStringKeyLookup[key] = true;
	}

	async resolveVarOrder(data) {
		let proxyByTemplateString = new ComputedDataTemplateString(this.computedKeys);
		let proxyByProxy = new ComputedDataProxy(this.computedKeys);

		for (let key of this.computedKeys) {
			let computed = lodashGet(this.computed, key);

			if (typeof computed !== "function") {
				// add nodes for non functions (primitives like booleans, etc)
				// This will not handle template strings, as they are normalized to functions
				this.queue.addNode(key);
			} else {
				this.queue.uses(key, this.declaredDependencies[key]);

				let symbolParseFn = lodashGet(this.symbolParseFunctions, key);
				let varsUsed = [];
				if (symbolParseFn) {
					// use the parseForSymbols function in the TemplateEngine
					varsUsed = symbolParseFn();
				} else if (symbolParseFn !== false) {
					// skip resolution is this is false (just use declaredDependencies)
					let isTemplateString = !!this.templateStringKeyLookup[key];
					let proxy = isTemplateString ? proxyByTemplateString : proxyByProxy;
					varsUsed = await proxy.findVarsUsed(computed, data);
				}

				debug("%o accesses %o variables", key, varsUsed);
				let filteredVarsUsed = varsUsed.filter((varUsed) => {
					return (
						(varUsed !== key && this.computedKeys.has(varUsed)) ||
						varUsed.startsWith("collections.")
					);
				});
				this.queue.uses(key, filteredVarsUsed);
			}
		}
	}

	async _setupDataEntry(data, order) {
		debug("Computed data order of execution: %o", order);
		for (let key of order) {
			let computed = lodashGet(this.computed, key);

			if (typeof computed === "function") {
				let ret = await computed(data);
				lodashSet(data, key, ret);
			} else if (computed !== undefined) {
				lodashSet(data, key, computed);
			}
		}
	}

	async setupData(data, orderFilter) {
		await this.resolveVarOrder(data);

		await this.processRemainingData(data, orderFilter);
	}

	async processRemainingData(data, orderFilter) {
		// process all variables
		let order = this.queue.getOrder();
		if (orderFilter && typeof orderFilter === "function") {
			order = order.filter(orderFilter.bind(this.queue));
		}

		await this._setupDataEntry(data, order);
		this.queue.markComputed(order);
	}
}

export default ComputedData;