Skip to content

Commit 70c3642

Browse files
committed
reorganize client code + add install func to dynamically install es6 modules
1 parent d9128ff commit 70c3642

File tree

14 files changed

+1347
-618
lines changed

14 files changed

+1347
-618
lines changed

MANIFEST.in

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1-
graft src/py/idom/static
2-
include src/py/idom/py.typed
3-
include src/py/LICENSE
1+
graft src/idom/client/
2+
prune src/idom/client/node_modules
3+
4+
include src/idom/py.typed
5+
include LICENSE

examples/chart.js

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
import React from '../react.js';
2+
import { VictoryBar, VictoryChart, VictoryAxis } from '../victory.js';
3+
import htm from "../htm.js";
4+
5+
const html = htm.bind(React.createElement);
6+
7+
const data = [
8+
{quarter: 1, earnings: 13000},
9+
{quarter: 2, earnings: 16500},
10+
{quarter: 3, earnings: 14250},
11+
{quarter: 4, earnings: 19000}
12+
];
13+
14+
class Chart extends React.Component {
15+
render() {
16+
return html`
17+
<${VictoryChart} domainPadding=20>
18+
<${VictoryAxis}
19+
tickValues=${[1, 2, 3, 4]}
20+
tickFormat=${["Quarter 1", "Quarter 2", "Quarter 3", "Quarter 4"]}
21+
/>
22+
<${VictoryAxis}
23+
dependentAxis
24+
tickFormat=${(x) => (`$${x / 1000}k`)}
25+
/>
26+
<${VictoryBar}
27+
data=${data}
28+
x="quarter"
29+
y="earnings"
30+
/>
31+
</${VictoryChart}>
32+
`
33+
}
34+
}
35+
36+
export default {Chart: Chart}

scripts/build.sh

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
#!/bin/bash
22
cd src/idom/client
3+
rm -rf node_modules
4+
rm -rf web_modules
35
npm install
46
npm run snowpack
57
cd ../../../

src/idom/client/__init__.py

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,100 @@
1+
import json
2+
import shutil
3+
import subprocess
4+
from loguru import logger
15
from pathlib import Path
6+
from tempfile import TemporaryDirectory
7+
from typing import Optional, List, Union
28

39

410
CLIENT_DIR = Path(__file__).parent
11+
12+
13+
def import_path(name: str) -> Optional[str]:
14+
path = CLIENT_DIR / "web_modules"
15+
for name_part in name.split("/"):
16+
if not path.is_dir():
17+
return None
18+
path /= name_part
19+
full_path = path.with_suffix(".js")
20+
if not full_path.is_file():
21+
return None
22+
return _web_module(name)
23+
24+
25+
def define_module(name: str, source: str) -> str:
26+
path = CLIENT_DIR / "web_modules"
27+
for n in name.split("/"):
28+
if not path.exists():
29+
path.mkdir()
30+
path /= n
31+
module = path.with_suffix(".js")
32+
with module.open("w+") as f:
33+
f.write(source)
34+
return _web_module(name)
35+
36+
37+
def install(*dependencies: str) -> None:
38+
pkg = _package_json()
39+
40+
npm_install = []
41+
for dep in dependencies:
42+
install_spec, *import_paths = dep.split(" ")
43+
if not import_paths:
44+
raise ValueError(
45+
"Expected a space seperated string where an installation "
46+
f"spec is followed by at least on import path, not '{dep}'"
47+
)
48+
pkg["snowpack"]["webDependencies"].extend(import_paths)
49+
npm_install.append(install_spec)
50+
51+
with TemporaryDirectory() as tempdir:
52+
with (Path(tempdir) / "package.json").open("w+") as f:
53+
json.dump(pkg, f)
54+
55+
_run_subprocess(["npm", "install"], tempdir)
56+
if npm_install:
57+
_run_subprocess(["npm", "install"] + npm_install, tempdir)
58+
_run_subprocess(["npm", "run", "snowpack"], tempdir)
59+
60+
61+
def restore() -> None:
62+
for path in ["web_modules", "node_modules"]:
63+
full_path = CLIENT_DIR.joinpath(*path.split("/"))
64+
if full_path.is_file():
65+
full_path.unlink()
66+
elif full_path.is_dir():
67+
shutil.rmtree(full_path)
68+
install()
69+
70+
71+
def _package_json():
72+
with (CLIENT_DIR / "package.json").open("r") as f:
73+
dependencies = json.load(f)["dependencies"]
74+
75+
return {
76+
"dependencies": dependencies,
77+
"scripts": {"snowpack": "./node_modules/.bin/snowpack"},
78+
"devDependencies": {"snowpack": "^1.6.0"},
79+
"snowpack": {
80+
"installOptions": {
81+
"dest": str(CLIENT_DIR / "web_modules"),
82+
"include": str(CLIENT_DIR / "modules" / "**" / "*.js"),
83+
},
84+
"webDependencies": [],
85+
},
86+
}
87+
88+
89+
def _run_subprocess(args: List[str], cwd: Union[str, Path]):
90+
try:
91+
subprocess.run(
92+
args, cwd=cwd, check=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE
93+
)
94+
except subprocess.CalledProcessError as error:
95+
logger.error(error.stderr.decode())
96+
raise
97+
98+
99+
def _web_module(name: str) -> str:
100+
return f"../web_modules/{name}.js"

src/idom/client/src/index.js renamed to src/idom/client/core_modules/layout.js

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ function Layout({ endpoint }) {
5252
});
5353
};
5454

55-
if (modelState.root) {
55+
if (modelState.root && modelState.root) {
5656
return html`<${Element}
5757
modelState=${modelState}
5858
model=${modelState.models[modelState.root]}
@@ -64,6 +64,7 @@ function Layout({ endpoint }) {
6464
}
6565

6666
function Element({ modelState, model, sendEvent }) {
67+
console.log(model)
6768
const children = elementChildren(modelState, model, sendEvent);
6869
const attributes = elementAttributes(model, sendEvent);
6970
if (model.importSource) {
@@ -145,7 +146,7 @@ function elementAttributes(model, sendEvent) {
145146
}
146147

147148
function renderLayout(mountElement, endpoint) {
148-
const cmpt = html`<$Layout endpoint=${endpoint} />`;
149+
const cmpt = html`<${Layout} endpoint=${endpoint} />`;
149150
return ReactDOM.render(cmpt, mountElement);
150151
}
151152

src/idom/client/html/simple-view.html

Lines changed: 0 additions & 28 deletions
This file was deleted.
159 KB
Binary file not shown.

src/idom/client/index.html

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
<!DOCTYPE html>
2+
<html lang="en">
3+
4+
<head>
5+
<meta charset="utf-8" />
6+
<link rel="shortcut icon" href="favicon.ico" />
7+
<title>IDOM</title>
8+
</head>
9+
10+
<body>
11+
<div id="app"></div>
12+
<script type="module">
13+
import { renderLayout } from "./core_modules/layout.js";
14+
15+
const uri = document.location.hostname + ":" + document.location.port;
16+
const url = (uri + document.location.pathname).split("/").slice(0, -1);
17+
url[url.length - 1] = "stream";
18+
const secure = document.location.protocol === "https:";
19+
20+
let protocol;
21+
if (secure) {
22+
protocol = "wss:";
23+
} else {
24+
protocol = "ws:";
25+
}
26+
let endpoint = protocol + "//" + url.join("/");
27+
28+
renderLayout(document.getElementById("app"), endpoint);
29+
</script>
30+
</body>
31+
32+
</html>

0 commit comments

Comments
 (0)