Fork me on GitHub
#shadow-cljs
<
2019-07-12
>
jlmr12:07:55

Hi, I’m trying to make a Jupyter Lab Extension using ClojureScript and shadow-cljs. However, I’m stuck right at the beginning and am unsure about which environment to target (`:browser`, :node-library or :npm-module), and how to configure shadow-cljs.edn and package.json. A template for a Javascript Extension can be found here: https://github.com/jupyterlab/extension-cookiecutter-js/tree/master/%7B%7Bcookiecutter.extension_name%7D%7D. Furthermore the JupyterLab documentation states the following: “An Extension is a valid npm package that meets the following criteria: • Exports one or more JupyterLab plugins as the default export in its main file. • Has a jupyterlab key in its package.json which has “extension” metadata. The value can be true to use the main module of the package, or a string path to a specific module (e.g. “lib/foo”). • It is also recommended to include the keyword jupyterlab-extension in the package.json, to aid with discovery (e.g. by the extension manager). It would be great if someone could point me in the right direction!

thheller13:07:42

looks like :node-library would be what you want

jlmr13:07:31

Thanks, will try to get it going this way

jlmr13:07:14

@thheller I’m now getting TypeError: "FS.readFileSync is not a function" in the browser console

jlmr13:07:56

I think it is because shadow-cljs is somehow invoking that function

thheller13:07:27

no its because it isn't running in node

thheller13:07:36

a release build will probably work

thheller13:07:40

just not watch or compile

thheller13:07:01

:node-library as the name implies is meant for things that run in node

thheller13:07:14

:npm-module is probably a better fit then

jlmr13:07:08

I’ve been trying this way a couple of hours ago, but had trouble with getting the js exports right

thheller13:07:33

I can take a look at this stuff a bit later and probably create an example

jlmr13:07:20

That would be great of course!

thheller13:07:42

never used the jupyter stuff before though

jlmr13:07:08

I’ve been trying this way a couple of hours ago, but had trouble with getting the js exports right

thheller13:07:44

yeah it works a bit differntly there

thheller13:07:53

basically you create a namespace

thheller13:07:08

with a bunch of (def ^:export thing ...)

thheller13:07:33

and then use the JS file for that ns as the main in package.json

thheller13:07:51

so "main":"./lib/your.ns.js" or so

thheller13:07:27

should be {:target :npm-module :entries [your.ns] :runtime :browser}

jlmr13:07:48

Thanks, I will try it this way. It could be that :runtime :browser was the part I was missing earlier

thheller13:07:32

there could be a dedicated :target :jupyter that takes care of all of that but I never used it before

thheller13:07:43

but thats the intent of the :target abstraction

jlmr13:07:05

@thheller one more question: I think I have the basics configured correctly now, because Jupyter is no longer complaining when I build and import the extension. However I don’t think my code is actually executed when I open Jupyter. Jupyter expects the extension object as the “default export in the extension main file”. How can I set my (def ^:export extension #js {:stuff :here}) to be the default export?

thheller13:07:17

uhm that might not work because CLJS renames default but maybe it does. so it would just be (def ^:export default ...)

jlmr13:07:49

Nope that did’nt do the trick unfortunately

thheller13:07:21

then CLJS probably renamed it to default$ since default used to be a reserved symbol

thheller13:07:44

but you can create a manual extra .js file and use that as main

thheller13:07:14

and just do something like import { x } from "./lib/your.ns.js" and export default x;

thheller14:07:24

with (def ^:export x ...) in your CLJS ns of course

jlmr14:07:37

Awesome, that did the trick! 🎉 Thanks a lot for all the help. Now on to the actual work of writing the extension 🙂

👍 4
neupsh17:07:49

it would help others if you may be create a project template/scaffolding if you have spare time 🙂

jlmr08:07:17

@U3MRWH35M might do that if I manage to get it working and packaged correctly

neupsh13:07:21

nice. Thanks 🙂

martinklepsch16:07:03

how do you require e.g. the crypto or fs module from node?

thheller19:07:23

(:require ["fs" :as fs]) and (fs/readFileSync ...) or so?

martinklepsch16:07:32

or should I just use goog.crypt? :thinking_face:

martinklepsch17:07:44

ended up just using goog.crypt 🎉

wilkerlucio17:07:43

@martinklepsch on node you can call (js/require ...) directly, but I guess if you use strings on the :require block it works as well

currentoor18:07:27

i’m having trouble figuring out how to map var { createCanvas } = require("canvas"); to the namespace require in cljs

currentoor18:07:44

I tried

(ns ucv.controller.barcode
  (:require
    ["canvas" :as createCanvas]))

currentoor18:07:59

and

(ns ucv.controller.barcode
  (:require
    ["canvas" :refer [createCanvas]]))

currentoor18:07:05

but neither work

neupsh18:07:58

@currentoor the second one looks correct

neupsh18:07:00

if createCanvas was exported default, (which does not look like the case looking at var { createCanvas } = require("canvas");, it would have been (:require ["canvas" :as canvas :default createCanvas])

neupsh18:07:06

can you try `

currentoor18:07:19

ah yeah i’ll try

neupsh18:07:44

(ns ucv.controller.barcode
  (:require
    ["canvas" :as canvas]))

(def createCanvas (.-createCanvas canvas))

neupsh18:07:18

i had success with this second approach (instead of :refer) with react-native/expo components

currentoor18:07:00

nope that didn’t work either

currentoor18:07:46

this does work though

(def canvas (js/require "canvas"))
(js/console.log (.-createCanvas canvas))

thheller19:07:08

@currentoor (:require ["canvas" :refer [createCanvas]]) would be the correct translation

currentoor19:07:54

@thheller yup that works, thank you!

neupsh20:07:50

@currentoor what is different between your second form and @thheller's answer? (just curious, i see both as same)

currentoor20:07:43

nothing, i think when trying different requires i got my repl into a bad state

nate21:07:52

it's declared as export default function WalletConnectProvider (opts) {}

nate21:07:27

I've required it as ["@walletconnect/web3-provider" :default WalletConnectProvider] and used (new WalletConnectProvider ...)

nate21:07:11

but I get this error message:

nate21:07:21

any ideas ?

thheller21:07:01

hard to say what that is but it always helps to use ["@walletconnect/web3-provider" :as x :default WalletConnectProvider] and then logging (js/console.dir x) to verify if the properties you are looking for actually exist

nate21:07:14

logging x gives me:

thheller21:07:47

ok so :default is probably correct then

thheller21:07:09

ie. if there is a .default property

estevam21:07:50

Has anyone ever had a problem running shadow-cljs with chrome 75? My browser freezes in a project we have in my company. It works with previous versions of chrome, though. In chrome's network, almost all requests show (pending) forever :thinking_face:

thheller21:07:00

@estevam.machado if you import a whole lot of files it can get a bit slow to use. never seen a full freeze though

teawaterwire22:07:55

will :loader-mode :eval be set as default in the future?

wilkerlucio18:07:27

@thheller why? does thigns are breaking?

wilkerlucio18:07:28

I'm wondering because of @estevam.machado, this seems to happen to a lot of Linux users at my workplace, my guess our app is getting too large, getting from slow down to total freeze

estevam18:07:18

@thheller thank you very much, it worked! But I am also wondering why will it not be the default in the future :thinking_face:

thheller19:07:05

it makes the compile cycle slower and only benefits builds with a lot of files

thheller19:07:38

I have some other ideas about reducing the number of files which should be better in all cases