Skip to content

Commit

Permalink
feat: Use import maps in build
Browse files Browse the repository at this point in the history
  • Loading branch information
manzt committed Mar 30, 2024
1 parent 1f10f4a commit d77eb75
Show file tree
Hide file tree
Showing 2 changed files with 53 additions and 75 deletions.
71 changes: 24 additions & 47 deletions gosling/display.py
Original file line number Diff line number Diff line change
@@ -1,39 +1,30 @@
from __future__ import annotations

import json
import uuid
from dataclasses import dataclass
from typing import Any, Dict, Optional
from typing import Any, Dict

import jinja2

from gosling.plugin_registry import PluginRegistry
from gosling.schema import SCHEMA_VERSION, THEMES

# TODO: This is kind of a mess. Gosling.js can be very finky with its
# peer dependencies in various Jupyter-like environments. This is a
# hacky way to get things working in JupyterLab, Jupyter Notebook,
# TODO: This is kind of a mess. Gosling.js can be very finky with its
# peer dependencies in various Jupyter-like environments. This is a
# hacky way to get things working in JupyterLab, Jupyter Notebook,
# VSCode, and Colab consistently.
HTML_TEMPLATE = jinja2.Template(
"""
<!DOCTYPE html>
<html>
<head>
<style>.error { color: red; }</style>
<link rel="stylesheet" href="{{ higlass_css_url }}">
</head>
<body>
<div id="{{ output_div }}"></div>
<script type="module">
import { importWithMap } from "https://unpkg.com/[email protected]";
let importMap = {
imports: {
"react": "https://esm.sh/react@18",
"react-dom": "https://esm.sh/react-dom@18",
"pixi": "https://esm.sh/pixi.js@6",
"higlass": "https://esm.sh/[email protected]?external=react,react-dom,pixi",
"gosling.js":
"https://esm.sh/[email protected]?external=react,react-dom,pixi,higlass",
},
};
let importMap = {{ import_map }};
let gosling = await importWithMap("gosling.js", importMap);
let el = document.getElementById('{{ output_div }}');
let spec = {{ spec }};
Expand All @@ -55,54 +46,40 @@
GoslingSpec = Dict[str, Any]


@dataclass
class GoslingBundle:
react: str
react_dom: str
pixijs: str
higlass_js: str
higlass_css: str
gosling: str


def get_display_dependencies(
def get_gosling_import_map(
gosling_version: str = SCHEMA_VERSION.lstrip("v"),
higlass_version: str = "~1.12",
react_version: str = "17",
higlass_version: str = "1.13",
react_version: str = "18",
pixijs_version: str = "6",
base_url: str = "https://unpkg.com",
) -> GoslingBundle:
return GoslingBundle(
react=f"{ base_url }/react@{ react_version }/umd/react.production.min.js",
react_dom=f"{ base_url }/react-dom@{ react_version }/umd/react-dom.production.min.js",
pixijs=f"{ base_url }/pixi.js@{ pixijs_version }/dist/browser/pixi.min.js",
higlass_js=f"{ base_url }/higlass@{ higlass_version }/dist/hglib.js",
higlass_css=f"{ base_url }/higlass@{ higlass_version }/dist/hglib.css",
gosling=f"{ base_url }/gosling.js@{ gosling_version }/dist/gosling.js",
)
) -> dict:
return {
"imports": {
"react": f"https://esm.sh/react@{react_version}",
"react-dom": f"https://esm.sh/react-dom@{react_version}",
"pixi": f"https://esm.sh/pixi.js@{pixijs_version}",
"higlass": f"https://esm.sh/higlass@{higlass_version}?external=react,react-dom,pixi",
"gosling.js": f"https://esm.sh/gosling.js@{gosling_version}?external=react,react-dom,pixi,higlass",
}
}


def spec_to_html(
spec: GoslingSpec,
output_div: str = "vis",
embed_options: Optional[Dict[str, Any]] = None,
embed_options: Dict[str, Any] | None = None,
**kwargs,
):
embed_options = embed_options or dict(padding=0, theme=themes.get())
deps = get_display_dependencies(**kwargs)
deps = get_gosling_import_map(**kwargs)

return HTML_TEMPLATE.render(
spec=json.dumps(spec),
embed_options=json.dumps(embed_options),
output_div=output_div,
react_url=deps.react,
react_dom_url=deps.react_dom,
pixijs_url=deps.pixijs,
higlass_js_url=deps.higlass_js,
higlass_css_url=deps.higlass_css,
gosling_url=deps.gosling,
import_map=json.dumps(deps),
)


class Renderer:
def __init__(self, output_div: str = "jupyter-gosling-{}", **kwargs: Any):
self._output_div = output_div
Expand Down
57 changes: 29 additions & 28 deletions gosling/sphinxext/plot.py
Original file line number Diff line number Diff line change
@@ -1,24 +1,32 @@
from __future__ import annotations

import json
import pathlib
import warnings
import typing

import jinja2
from docutils import nodes
from docutils.parsers.rst import Directive
from docutils.parsers.rst.directives import flag

from gosling.display import get_display_dependencies
from gosling.display import get_gosling_import_map
from gosling.schemapi import SchemaValidationError
from gosling.utils.execeval import eval_block

if typing.TYPE_CHECKING:
from sphinx.application import Sphinx

TEMPLATE = jinja2.Template(
"""
<div id="{{ div_id }}">
<script>
document.addEventListener("DOMContentLoaded", function(event) {
var spec = {{ spec }};
<script type="module">
import * as gosling from "gosling.js";
document.addEventListener("DOMContentLoaded", async () => {
let el = document.querySelector('#{{ div_id }}');
let spec = {{ spec }};
let opt = { padding: 0 };
console.log(spec);
var opt = { padding: 0 };
var el = document.querySelector('#{{ div_id }}');
gosling.embed(el, spec, opt).catch(console.err);
});
</script>
Expand Down Expand Up @@ -84,14 +92,25 @@ def run(self):
return result


def add_custom_head(
app: Sphinx, pagename: str, templatename: str, context: dict, doctree
):
custom_html = (
f'<script type="importmap">{json.dumps(get_gosling_import_map())}</script>'
)
if context.get("metatags, None"):
context["metatags"] += custom_html
else:
context["metatags"] = custom_html


def html_visit_gosling_plot(self, node):
# Execute the code, saving output and namespace
try:
chart = eval_block(node["code"])
except Exception as e:
warnings.warn(
"gosling-plot: {}:{} Code Execution failed:"
"{}: {}".format(
"gosling-plot: {}:{} Code Execution failed:" "{}: {}".format(
node["rst_source"], node["rst_lineno"], e.__class__.__name__, str(e)
)
)
Expand All @@ -112,26 +131,8 @@ def depart_gosling_plot(self, node):
return


def builder_inited(app):
app.add_css_file(app.config.higlass_css_url)
app.add_js_file(app.config.react_url)
app.add_js_file(app.config.reactdom_url)
app.add_js_file(app.config.pixi_url)
app.add_js_file(app.config.higlass_js_url)
app.add_js_file(app.config.gosling_url)


def setup(app):
deps = get_display_dependencies()
app.add_config_value("react_url", deps.react, "html")
app.add_config_value("reactdom_url", deps.react_dom, "html")
app.add_config_value("pixi_url", deps.pixijs, "html")
app.add_config_value("higlass_js_url", deps.higlass_js, "html")
app.add_config_value("higlass_css_url", deps.higlass_css, "html")
app.add_config_value("gosling_url", deps.gosling, "html")

def setup(app: Sphinx):
app.connect("html-page-context", add_custom_head)
app.add_directive("gosling-plot", GoslingPlotDirective)
app.add_node(gosling_plot, html=(html_visit_gosling_plot, depart_gosling_plot))

app.connect("builder-inited", builder_inited)
return {"version": "0.1"}

0 comments on commit d77eb75

Please sign in to comment.