The VISr Blog

Using Dependencies in VISr

tutorial

When creating and using visual and interactive syntax extensions with VISr, you have access to the entire catalog of JavaScript (including WebAssembly and TypeScript) libraries found on NPM. This means your interactive-syntax extensions can use the wide range of existing code, from network layout to advanced cad libraries.

This post is a tutorial on how to add, and use, JavaScript dependencies to your code. It shows you how to convert an NPM package for elIDE, the predominant VISr IDE. Additionally, this post points you to a directory of pre-existing packages. By the end of this tutorial, you will have created a small extension that stores tree-literals in your code.

If you are new to VISr or elIDE, I recommend you follow this introduction tutorial first. It covers the basics of how to add interactive-syntax extensions to your code, as well as how to create a simple counter extension. You can also watch this video to learn about the basics of visual and interactive syntax in general.

The Basics

First thing first, you can find a list of the current set of available dependencies by going to the VISr dependency page:

VISr Dependency Page

These are all bundles from NPM that have been pre-packaged for elIDE. Later in this tutorial we will cover how to package new bundles. For now though, we will install dependencies from this list.

Adding Dependencies to elIDE

The dependency manager can be found in the Project menu:

Project Menu

When opened, you will be greeted with a window like this:

Deps Dialog

This is a list of all of the dependencies in your current project. New dependencies can be added with the + button, and unneeded dependencies can be removed with the - button.

The Name field is what you will require in your code, while the URL field instructs elIDE where to fetch the dependency from. If no URL is provided, then a URL is referred from the dependency list at https://deps.visr.pl.

The Load? option tells elIDE if it should load the dependency when running the program. Generally this option should be left on. The only time to turn it off is when adding packages that contain only data and no executable code. At the time of writing this, only one package fits this description: opencascade.wasm.

For this tutorial we will need two packages: react-graph-vis and vis-network-css. Once you hav added those packages press Update, and elIDE will download those packages into your project.

Using in Code

With these dependencies added, you can now refer to them as you would any ClojureScript module:

(ns tree.core
(:require [react-graph-vis]))

This means you also have full access to the :as and :refer keywords as well. Do note, however, that many JavaScript modules provide a default export. Unfortunately, those can't be accessed directly through this mechanism. Rather, you can simply access it as a field from your required module:

(def Graph (.-default react-graph-vis))

Here, Graph comes from the Vis.js Network Library. We can now construct our VISr as normal:

;; A Node is one of:
;; - {:left Node :right Node :label String}
;; - {:label String}

;; A Tree is: {:root Node}

(defvisr Tree
(state root {})
(elaborate-fn [this]
root)
(render [this]
[:> Graph
{:options {:layout {:hierarchical {:sortMethod "directed"}}}
:graph (convert-tree @root)}]))

For this tutorial, the elaborator is nothing special, it just returns the tree as a run-time data structure. The renderer is a little more complicated, but offloads most of the work to a convert-tree helper:

(defn convert-tree [{label :label
{l-label :label :as left} :left
{r-label :label :as right} :right}]
(if (and left right)
(let [{l-nodes :nodes l-edges :edges} (convert-tree left)
{r-nodes :nodes r-edges :edges} (convert-tree right)]
{:nodes (concat l-nodes r-nodes #{{:id label :label label}})
:edges (concat l-edges r-edges #{{:from label :to l-label}
{:from label :to r-label}})})
{:nodes #{{:id label :label label}}}))

This helper can be used to draw the tree at both during edit time, and run time. For example, here is a tree with a root node A, and two leaf nodes x, and y:

A tree created with interactive syntax

The astute reader may have noticed that while this project uses vis-network-css, its not actually used in the code. Instead, it is used by react-graph-vis as a peer dependency. Removing it from the dependency list causes the tree to render improperly.

Packaging New Dependencies

Using pre-packaged dependencies is useful, but the entire catalog of existing, and future, JavaScript libraries is much bigger. Thus it is useful to know how to bundle nwe packages for elIDE. If you do create a package, feel free to submit a pull request to add it the official dependency repository.

As of right now, you can not create new elIDE packages from within elIDE.[1] You will need to install three packages:

  1. Racket
  2. nodejs
  3. npm

You can download them seperately, or you can get them through your package manger, such as Homebrew for Linux or macOS:

$ brew install node
$ brew install npm
$ brew install racket

You will also need the sml package from the Racket repository:

$ raco pkg install sml

Finally, you will need the scripts/shop folder from the [VISr][visr-source] repository:

$ git clone https://github.com/LeifAndersen/interactive-syntax-clojure
$ cd interactive-syntax-clojure/scripts/shop

In this folder, you'll notice two important files, database.sml and build.rkt. The former contains the requiremets to build each package, while the latter contains the build script itself. Enteries in database.sml consist of the package name, a corrosponding NPM package, and a javascript path within that package. For example, the entry for react-graph-vis is:

$ cat database.sml
[...]
react-graph-vis:
{package: "react-graph-vis" path: "lib/index.js"}
[...]

You won't need to modify the contents of build.rkt, but you will need to add your package to database.sml. Once you have added the entry into the database, you can now build the dependency with build.rkt:

$ racket build.rkt -v react-graph-vis

Once built, you will find the dependency in the newly created deps folder.

$ ls deps/
react-graph-vis.js
react-graph-vis.js.LICENSE.txt

The first file is the dependency itself, while the second file contains all of the licenses of the libraries used to create that dependency.

Wrapping Up

And that's it. Now you know how to add dependencies to your projects, how to use those dependencies when creating interactive-syntax extensions, and how to package new JavaScript libraries into dependencies.

Of course, this entire ecosystem is still under active development. We are always interested in conributors, from simple additions to the dependency directory, to a complete rework of the how dependencies are built. At the end of the day, what counts is all of the cool things we can build using visual and interactive-syntax extensions.


  1. This is not by design. I simply haven't figured out how to run Webpack, the crutial component, from within a web browser. If you know how to do this, please get in contact with me! ↩︎