-
-
Notifications
You must be signed in to change notification settings - Fork 498
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
9 changed files
with
353 additions
and
21 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,98 @@ | ||
const lodashGet = require("lodash/get"); | ||
const lodashSet = require("lodash/set"); | ||
const DependencyGraph = require("dependency-graph").DepGraph; | ||
|
||
class ComputedData { | ||
constructor() { | ||
this.computed = {}; | ||
this.computedKeys = new Set(); | ||
this.declaredDependencies = {}; | ||
|
||
// is this ¯\_(lisp)_/¯ | ||
// must be strings that won’t be escaped by template languages | ||
this.prefix = "(((((hi((((("; | ||
this.suffix = ")))))hi)))))"; | ||
} | ||
|
||
add(key, fn, declaredDependencies = []) { | ||
this.computedKeys.add(key); | ||
this.declaredDependencies[key] = declaredDependencies; | ||
lodashSet(this.computed, key, fn); | ||
} | ||
|
||
getProxyData(data) { | ||
let proxyData = {}; | ||
|
||
// use these special strings as a workaround to check the rendered output | ||
// can’t use proxies here as some template languages trigger proxy for all | ||
// keys in data | ||
for (let key of this.computedKeys) { | ||
// TODO don’t allow to set eleventyComputed.page? other disallowed computed things? | ||
lodashSet(proxyData, key, this.prefix + key + this.suffix); | ||
} | ||
|
||
return proxyData; | ||
} | ||
|
||
findVarsInOutput(output = "") { | ||
let vars = new Set(); | ||
let splits = output.split(this.prefix); | ||
for (let split of splits) { | ||
let varName = split.substr(0, split.indexOf(this.suffix)); | ||
if (varName) { | ||
vars.add(varName); | ||
} | ||
} | ||
return Array.from(vars); | ||
} | ||
|
||
async getVarOrder(data) { | ||
if (this.computedKeys.size > 0) { | ||
let graph = new DependencyGraph(); | ||
|
||
let proxyData = this.getProxyData(data); | ||
|
||
for (let key of this.computedKeys) { | ||
let computed = lodashGet(this.computed, key); | ||
if (typeof computed === "function") { | ||
graph.addNode(key); | ||
if (this.declaredDependencies[key].length) { | ||
for (let dep of this.declaredDependencies[key]) { | ||
graph.addNode(dep); | ||
graph.addDependency(key, dep); | ||
} | ||
} | ||
|
||
let output = await computed(proxyData); | ||
|
||
let vars = this.findVarsInOutput(output); | ||
for (let usesVar of vars) { | ||
if (usesVar !== key && this.computedKeys.has(usesVar)) { | ||
graph.addNode(usesVar); | ||
graph.addDependency(key, usesVar); | ||
} | ||
} | ||
} | ||
} | ||
|
||
return graph.overallOrder(); | ||
} | ||
|
||
return []; | ||
} | ||
|
||
async setupData(data) { | ||
let order = await this.getVarOrder(data); | ||
for (let key of order) { | ||
let computed = lodashGet(this.computed, key); | ||
|
||
if (typeof computed === "function") { | ||
lodashSet(data, key, await computed(data)); | ||
} else if (computed) { | ||
lodashSet(data, key, computed); | ||
} | ||
} | ||
} | ||
} | ||
|
||
module.exports = ComputedData; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,123 @@ | ||
import test from "ava"; | ||
import ComputedData from "../src/ComputedData"; | ||
|
||
test("Get fake proxy data", t => { | ||
let cd = new ComputedData(); | ||
cd.add("key1", () => {}); | ||
cd.add("key2", () => {}); | ||
t.deepEqual(cd.getProxyData({}), { | ||
key1: `${cd.prefix}key1${cd.suffix}`, | ||
key2: `${cd.prefix}key2${cd.suffix}` | ||
}); | ||
}); | ||
|
||
test("Get nested fake proxy data", t => { | ||
let cd = new ComputedData(); | ||
cd.add("key1.nested", () => {}); | ||
cd.add("key2", () => {}); | ||
t.deepEqual(cd.getProxyData({}), { | ||
key1: { | ||
nested: `${cd.prefix}key1.nested${cd.suffix}` | ||
}, | ||
key2: `${cd.prefix}key2${cd.suffix}` | ||
}); | ||
}); | ||
|
||
test("Get vars from output", t => { | ||
let cd = new ComputedData(); | ||
t.deepEqual(cd.findVarsInOutput(""), []); | ||
t.deepEqual(cd.findVarsInOutput("slkdjfkljdsf"), []); | ||
t.deepEqual( | ||
cd.findVarsInOutput(`slkdjfkljdsf${cd.prefix}${cd.suffix}sldkjflkds`), | ||
[] | ||
); | ||
t.deepEqual( | ||
cd.findVarsInOutput( | ||
`slkdjfkljdsf${cd.prefix}firstVar${cd.suffix}sldkjflkds` | ||
), | ||
["firstVar"] | ||
); | ||
t.deepEqual( | ||
cd.findVarsInOutput( | ||
`slkdjfkljdsf${cd.prefix}firstVar${cd.suffix}test${cd.prefix}firstVar${cd.suffix}sldkjflkds` | ||
), | ||
["firstVar"] | ||
); | ||
t.deepEqual( | ||
cd.findVarsInOutput( | ||
`slkdjfkljdsf${cd.prefix}firstVar${cd.suffix}test${cd.prefix}secondVar${cd.suffix}sldkjflkds` | ||
), | ||
["firstVar", "secondVar"] | ||
); | ||
}); | ||
|
||
test("Basic get/set", async t => { | ||
let cd = new ComputedData(); | ||
|
||
cd.add("keystr", `this is a str`); | ||
cd.add("key1", data => { | ||
return `this is a test ${data.key2}${data.keystr}`; | ||
}); | ||
|
||
let data = { | ||
key2: "inject me" | ||
}; | ||
await cd.setupData(data); | ||
|
||
t.is(data.key1, "this is a test inject methis is a str"); | ||
t.is(data.key2, "inject me"); | ||
t.is(data.keystr, "this is a str"); | ||
}); | ||
|
||
test("use a computed value in another computed", async t => { | ||
let cd = new ComputedData(); | ||
cd.add("keyComputed", data => { | ||
return `this is a test ${data.keyOriginal}`; | ||
}); | ||
cd.add("keyComputed2nd", data => { | ||
return `using computed ${data.keyComputed}`; | ||
}); | ||
|
||
let data = { | ||
keyOriginal: "inject me" | ||
}; | ||
await cd.setupData(data); | ||
|
||
t.is(data.keyComputed2nd, "using computed this is a test inject me"); | ||
}); | ||
|
||
test("use a computed value in another computed (out of order)", async t => { | ||
let cd = new ComputedData(); | ||
cd.add("keyComputed2nd", data => { | ||
return `using computed ${data.keyComputed}`; | ||
}); | ||
cd.add("keyComputed", data => { | ||
return `this is a test ${data.keyOriginal}`; | ||
}); | ||
|
||
let data = { | ||
keyOriginal: "inject me" | ||
}; | ||
await cd.setupData(data); | ||
|
||
t.is(data.keyComputed2nd, "using computed this is a test inject me"); | ||
}); | ||
|
||
test("use a computed value in another computed (out of order), async callbacks", async t => { | ||
let cd = new ComputedData(); | ||
cd.add("keyComputed2nd", async data => { | ||
// await in data.keyComputed is optional 👀 | ||
return `using computed ${data.keyComputed}`; | ||
}); | ||
cd.add("keyComputed", async data => { | ||
// await in data.keyOriginal is optional 👀 | ||
return `this is a test ${await data.keyOriginal}`; | ||
}); | ||
|
||
let data = { | ||
keyOriginal: "inject me" | ||
}; | ||
await cd.setupData(data); | ||
|
||
t.is(data.keyComputed2nd, "using computed this is a test inject me"); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.