Skip to content

Commit eeb061b

Browse files
committed
Complete the NPM bundling
1 parent 9935402 commit eeb061b

17 files changed

+947
-18
lines changed

Diff for: .vscode/settings.json

+2-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,8 @@
22
"deno.enablePaths": [
33
"demo/terminal/server.ts",
44
"components/scripts",
5-
"x/scripts"
5+
"x/scripts",
6+
"graph/scripts"
67
],
78
"cSpell.words": [
89
"currentcolor",

Diff for: graph/README.md

+57
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
# **VanGraph**: Helper Library to Visualize Dependency Graph Among States and DOM Nodes
2+
3+
**VanGraph** is a library that helps you visualize dependency graph among states and DOM nodes with the help of [Graphviz](https://graphviz.org/). Here is the sample usage:
4+
5+
```js
6+
const firstName = van.state("Tao"), lastName = van.state("Xin")
7+
const fullName = van.derive(() => `${firstName.val} ${lastName.val}`)
8+
9+
// Build the DOM tree...
10+
11+
// To visualize the dependency graph among `firstName`, `lastName`, `fullName`, and all the
12+
// derived states and DOM nodes from them.
13+
vanGraph.show({firstName, lastName, fullName})
14+
```
15+
16+
## Installation
17+
18+
### Via NPM
19+
20+
The library is published as NPM package [vanjs-graph](https://www.npmjs.com/package/vanjs-graph). Run the following command to install the package:
21+
22+
```shell
23+
npm install vanjs-graph
24+
```
25+
26+
To use the NPM package, add this line to your script:
27+
28+
```js
29+
import * as vanGraph from "vanjs-graph"
30+
```
31+
32+
### Via a Script Tag
33+
34+
Alternatively, you can import **VanGraph** from CDN via a `<script type="text/javascript">` tag:
35+
36+
```html
37+
<script type="text/javascript" src="https://cdn.jsdelivr.net/npm/[email protected]/dist/van-graph.nomodule.min.js"></script>
38+
```
39+
40+
`https://cdn.jsdelivr.net/npm/[email protected]/dist/van-graph.nomodule.js` can be used for the non-minified version.
41+
42+
Note that: you need to import **VanJS** and `@viz-js/viz` before **VanGraph** for it to be used properly:
43+
44+
```html
45+
<script type="text/javascript" src="https://cdn.jsdelivr.net/gh/vanjs-org/van/public/van-1.5.2.nomodule.min.js"></script>
46+
<script type="text/javascript" src="https://cdn.jsdelivr.net/npm/@viz-js/[email protected]/lib/viz-standalone.js"></script>
47+
```
48+
49+
[Try on jsfiddle](https://jsfiddle.net/1u69nbek/2/)
50+
51+
## Documentation
52+
53+
```js
54+
vanGraph.show(states, options) => Promise<SVGSVGElement>
55+
```
56+
57+
The parameter `states` represents a collection of `State` objects whose dependency graph we want to visualize. All the `State` objects and their dependents will be rendered in the dependency graph. `states` can either be specified as a plain object, e.g.: `{firstName, lastName, fullName}`, or as an array, e.g.: `[firstName, lastName, fullName]`.

Diff for: graph/dist/van-graph.js

+7-5
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import * as Viz from "@viz-js/viz";
22
import van from "vanjs-core";
3+
const { b, table, td, tr } = van.tags;
34
let bindingsPropKey, listenersPropKey, domPropKey;
45
const init = () => {
56
const viz = Viz.instance();
@@ -9,12 +10,12 @@ const init = () => {
910
listenersPropKey = Object.entries(s)
1011
.filter(([_, v]) => Array.isArray(v))[1][0];
1112
van.tags.span(s);
12-
domPropKey = Object.keys(s[bindingsPropKey]).find(k => k !== "f");
13+
domPropKey = Object.keys(s[bindingsPropKey][0]).find(k => k !== "f");
1314
return viz;
1415
};
1516
const { promise: viz, resolve: resolveViz } = Promise.withResolvers();
1617
init().then(viz => resolveViz(viz));
17-
const getLabel = (node) => [node._name].concat(node._f?.toString() ?? [], node._state ? `State {val: ${node._state.rawVal}}` : [], node._dom ? node._dom.nodeName : []).join(" | ");
18+
const getLabelHtml = (node) => table({ border: 0, cellborder: 1, cellspacing: 0 }, !node._name.startsWith(unnamedPrefix) ? tr(td(b(node._name))) : undefined, node._dom ? tr(td(node._dom.nodeName)) : undefined, node._state ? tr(td(`State {val: ${JSON.stringify(node._state.rawVal)}}`)) : undefined, node._f ? tr(td(node._f.toString() + "\n")) : undefined).outerHTML.replaceAll("\n", '<br align="left"/>');
1819
const unnamedPrefix = "<unnamed>_", stateProto = Object.getPrototypeOf(van.state());
1920
const keepConnected = (l) => l.filter((b) => b[domPropKey]?.isConnected);
2021
const show = async (states, { rankdir = "TB", } = {}) => {
@@ -43,16 +44,17 @@ const show = async (states, { rankdir = "TB", } = {}) => {
4344
for (const l of s[listenersPropKey]) {
4445
const newS = l.s;
4546
let newNode = stateOrDomToNode.get(newS);
46-
newNode || stateOrDomToNode.set(newS, newNode = { _name: newName(), _f: l.f, _state: newS });
47+
newNode || stateOrDomToNode.set(newS, newNode = { _name: newName(), _state: newS });
48+
newNode._f = l.f;
4749
edges.push([node, newNode]);
4850
}
4951
}
5052
return (await viz).renderSVGElement({
5153
graphAttributes: { rankdir },
52-
nodeAttributes: { shape: "record" },
54+
nodeAttributes: { shape: "plain", fontname: "Courier", fontsize: 10 },
5355
nodes: [...stateOrDomToNode.values()].map(node => ({
5456
name: node._name,
55-
attributes: { label: getLabel(node) },
57+
attributes: { label: { html: getLabelHtml(node) } },
5658
})),
5759
edges: edges.map(([a, b]) => ({ tail: a._name, head: b._name }))
5860
});

Diff for: graph/dist/van-graph.nomodule.js

+62
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
{
2+
const { b, table, td, tr } = van.tags;
3+
let bindingsPropKey, listenersPropKey, domPropKey;
4+
const init = () => {
5+
const viz = Viz.instance();
6+
const s = van.state(0);
7+
bindingsPropKey = Object.entries(s)
8+
.find(([_, v]) => Array.isArray(v))[0];
9+
listenersPropKey = Object.entries(s)
10+
.filter(([_, v]) => Array.isArray(v))[1][0];
11+
van.tags.span(s);
12+
domPropKey = Object.keys(s[bindingsPropKey][0]).find(k => k !== "f");
13+
return viz;
14+
};
15+
const { promise: viz, resolve: resolveViz } = Promise.withResolvers();
16+
init().then(viz => resolveViz(viz));
17+
const getLabelHtml = (node) => table({ border: 0, cellborder: 1, cellspacing: 0 }, !node._name.startsWith(unnamedPrefix) ? tr(td(b(node._name))) : undefined, node._dom ? tr(td(node._dom.nodeName)) : undefined, node._state ? tr(td(`State {val: ${JSON.stringify(node._state.rawVal)}}`)) : undefined, node._f ? tr(td(node._f.toString() + "\n")) : undefined).outerHTML.replaceAll("\n", '<br align="left"/>');
18+
const unnamedPrefix = "<unnamed>_", stateProto = Object.getPrototypeOf(van.state());
19+
const keepConnected = (l) => l.filter((b) => b[domPropKey]?.isConnected);
20+
const show = async (states, { rankdir = "TB", } = {}) => {
21+
let id = 0;
22+
const newName = () => unnamedPrefix + ++id;
23+
const stateOrDomToNode = new Map(), edges = Array();
24+
if (Array.isArray(states))
25+
for (const s of states)
26+
stateOrDomToNode.set(s, { _name: newName(), _state: s });
27+
else
28+
for (const [name, s] of Object.entries(states))
29+
stateOrDomToNode.set(s, { _name: name, _state: s });
30+
let iter = stateOrDomToNode.entries();
31+
for (let item = iter.next(); !item.done; item = iter.next()) {
32+
const [s, node] = item.value;
33+
if (Object.getPrototypeOf(s) !== stateProto)
34+
continue;
35+
s[bindingsPropKey] = keepConnected(s[bindingsPropKey]);
36+
s[listenersPropKey] = keepConnected(s[listenersPropKey]);
37+
for (const b of s[bindingsPropKey]) {
38+
const dom = b[domPropKey];
39+
let newNode = stateOrDomToNode.get(dom);
40+
newNode || stateOrDomToNode.set(dom, newNode = { _name: newName(), _f: b.f, _dom: dom });
41+
edges.push([node, newNode]);
42+
}
43+
for (const l of s[listenersPropKey]) {
44+
const newS = l.s;
45+
let newNode = stateOrDomToNode.get(newS);
46+
newNode || stateOrDomToNode.set(newS, newNode = { _name: newName(), _state: newS });
47+
newNode._f = l.f;
48+
edges.push([node, newNode]);
49+
}
50+
}
51+
return (await viz).renderSVGElement({
52+
graphAttributes: { rankdir },
53+
nodeAttributes: { shape: "plain", fontname: "Courier", fontsize: 10 },
54+
nodes: [...stateOrDomToNode.values()].map(node => ({
55+
name: node._name,
56+
attributes: { label: { html: getLabelHtml(node) } },
57+
})),
58+
edges: edges.map(([a, b]) => ({ tail: a._name, head: b._name }))
59+
});
60+
};
61+
window.vanGraph = { show };
62+
}

Diff for: graph/dist/van-graph.nomodule.min.js

+1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Diff for: graph/examples/basic/package-lock.json

+16-1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Diff for: graph/examples/basic/package.json

+2-1
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
"terser": "^5.31.6"
1414
},
1515
"dependencies": {
16-
"vanjs-core": "^1.5.2"
16+
"vanjs-core": "^1.5.2",
17+
"vanjs-graph": "^0.1.0-rc.3"
1718
}
1819
}

Diff for: graph/examples/basic/src/main.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import van from "vanjs-core"
2-
import * as vanGraph from "../../../src/van-graph"
2+
import * as vanGraph from "vanjs-graph"
33

44
const {button, div, input, option, pre, select} = van.tags
55

0 commit comments

Comments
 (0)