Fork me on GitHub
#scittle
<
2023-08-21
>
Andrea15:08:13

Hi, would it be possible to configure scittle to resolve external libraries imported via <script> tags, like so?

<script src="" type="application/javascript"></script>
    <script src="" type="application/javascript"></script>
    <script type="application/x-scittle">
      (ns my-ns
        (:require [clojure.string :as str]
                  ["@codemirror/state" :refer [EditorState]]))

      ...
    </script> 

borkdude15:08:22

I don't think this is possible since "@codemirror/state" refers to something external which is not known by scittle or the JS environment, right?

borkdude15:08:44

should work with cherry though if you stick the complete CDN url in there: https://dev.livecodes.io/?template=clojurescript

borkdude15:08:05

but usually when you use a script tag in this way, the script defines a global which you can then use

Andrea15:08:29

oh, would squint-js + an import map, more appropriate?

Andrea15:08:19

oh, your example has na import map

borkdude15:08:47

scittle will work, but you need to access the codemirror stuff via the globals it defines (see example)

borkdude15:08:28

or if it only defines modules, you even need to define globals yourself:

<script type="importmap">
      {
        "imports": {
          "codemirror": "",
          "@codemirror/commands": "",
          "@codemirror/search": "",
          "@codemirror/autocomplete": "",
          "@codemirror/lint": "",
          "crelt": "",
          "@nextjournal/lang-clojure": "",
          "@nextjournal/lezer-clojure": "",
          "@lezer/highlight": "",
          "@lezer/lr": "",
          "@lezer/common": "",
          "@codemirror/language": "",
          "@codemirror/state": "",
          "@codemirror/view": "",
          "style-mod": "",
          "w3c-keyname": ""
        }
      }
    </script>
    <script type="module" type="application/javascript">
      import * as cm from 'codemirror';
      import * as lc from '@nextjournal/lang-clojure'
      import * as cv from '@codemirror/view';
      import * as cs from '@codemirror/state';
       = cm;
       = lc;
       = cv;
      globalThis.cs = cs;
      scittle.core.eval_script_tags();
    </script>

Andrea15:08:12

yeah I saw that... I was thinking more of keeping the cljs code as is to suit different targets (e.g. shadow)

borkdude15:08:35

squint or cherry should work I think

borkdude15:08:51

this is a minimal example of how to compile and run: https://squint-cljs.github.io/squint/

Andrea15:08:29

nice thank you!

Andrea15:08:18

I see, the eval part is done via an import of url encoded data

borkdude16:08:42

I think you could theoretically use eval if you would wrap it in a self-executing async function

borkdude16:08:03

hmm, but then import doesn't work as is

borkdude16:08:10

so yeah, that's why it's done this way

Andrea16:08:41

I’ll check it out

Andrea16:08:44

thank you!

borkdude16:08:03

I'm contemplating an output that is more suited for hot-reloading / nREPL, then I'd have to emit import() instead and some global stuff where (defn foo []) becomes the_namespace.foo = function and then you could likely also also use eval

mkvlr16:08:36

could sci async also support require of js modules using import maps or does it already maybe?

borkdude17:08:00

yes, sci async can

borkdude17:08:45

if you insert the right import maps in that page, dynamic import using those aliases will automatically work, this is just browser behavior

Andrea12:08:37

would that require a custom build of scittle, like you do with plugins? I mean to tell scittle to use the :async-load-fn option...

borkdude12:08:40

yeah, it would require some (breaking) changes, but I wonder if I can accomodate both ways in one build, both have their trade-offs. Perhaps it's possible to include scittle in a second way that does it the ESM + async way

Andrea13:08:53

amazing this already works:

(ns scittle-build
  (:require [sci.core :as sci]
            [sci.async :as scia]
            [scittle.core :as scit]
            [shadow.esm :refer [dynamic-import]]
            [scittle.impl.error :as error]))

(swap! scit/!sci-ctx
       sci/merge-opts {:async-load-fn (fn [{:keys [ctx libname]}]
                                        (.then (dynamic-import libname)
                                               (fn [mod]
                                                 (js/console.log :require libname
                                                                 :mod mod)))
                                        {:handled true})})

(set! scit/eval-string (fn [txt]
                         (try
                           (scia/eval-string* @scit/!sci-ctx txt)
                           (catch :default e
                             (error/error-handler e (:src @scit/!sci-ctx))
                             (throw e)))))

Andrea13:08:44

with the import mapping eg. “@codemirror/state”: “https://cdn.jsdelivr.net/npm/@codemirror/state/dist/index.js”, then it can eval

(ns foo
  (:require ["@codemirror/state"])

borkdude13:08:29

note that scia/eval-string* returns a promise, so you should handle the promise error probably

👍 4
Andrea13:08:45

now I guess I’ll go find the 1000 implementation of the async-load-fn we did already 🙂

Andrea13:08:05

well not 1000 probably...

borkdude13:08:34

1000 + 1 Promise pending

borkdude13:08:54

what are you working on?

Andrea13:08:56

extracting with Philippa the stuff behind the split-view editor from Clerk

Andrea13:08:46

trying to write code once but target more build envs like shadow/scittle/squint possibly 🤞

Andrea14:08:39

I’m a bit confused now, it seems forms in the namespace gets evaluated before the requires are resolved (via async-load-fn), this kind of makes sense in an async setting, but how can forms refer to the required names?

borkdude14:08:54

you should return handled true from the promise

borkdude14:08:05

then the code will run after the promise, not parallel

Andrea14:08:26

ah, ok, I was returning it from the the load-fn body

Andrea15:08:57

I now have two tags:

<script type="application/x-scittle" src="folio.cljs"></script>
    <script type="application/x-scittle" src="demo.cljs"></script>
first loads just fine, the second however doesn’t seem able to require the namespace defined by the first

borkdude15:08:00

because async

borkdude15:08:29

the code that loads the script tags must handle the promises in order

Andrea15:08:15

ok, then I also need to change eval-script-tags*

Andrea15:08:29

or I also copy the ns defined in the first into the sci ctx

Andrea15:08:42

it’s not clear at this point what goes where

borkdude15:08:16

it's not clear to me what you mean with "or I also copy the ns defined in the first into the sci ctx"

borkdude15:08:55

if you change scittle.core/eval_string to be async, you also need to account for that in other places

Andrea15:08:07

I need to swap a bunch of nss into scittle.core/!sci-ctx in order for the example above to work at all

borkdude15:08:18

btw I wouldn't merge this since that is a breaking change, but for experimentation it's ok

borkdude15:08:43

the order will work once you fix the eval-script-tags to work with promises

Andrea15:08:51

sure, I can experiment with our build so far

Andrea15:08:54

thank you!

borkdude15:08:35

once you're finished, please let me know, perhaps we can find a way to make scittle1 work with scittle async so we don't have breakages and we can have both behaviors at once

Andrea15:08:00

ok, will do!

borkdude15:08:15

I think this will mean a new var scittle.core/eval-string-async and perhaps a new attribute on the script tags to indicate that it's ES6 code

borkdude15:08:27

but we can figure that out later

borkdude15:08:04

I think we can make eval-script-tags work with both sync and async code by just using Promise.resolve on the return value and always work in a promise-like fashion

borkdude15:08:32

then we'll just leave eval-string synchronous for people who already used it and introduce a new var for async evaluation

Andrea09:08:21

Got the sequential eval working 🙂

Andrea09:08:09

but still cannot demo the editor because of a react mismatch error when using hooks

borkdude09:08:06

oh yeah, that again

borkdude09:08:33

perhaps you can pin the react version using import-maps

Andrea09:08:45

could it be react version shipped with reagent the problem

Andrea09:08:54

I do pin them to 18.2.0

borkdude09:08:02

no, react isn't shipped with reagent, with scittle you provide your own react version

borkdude09:08:04

in the script tag

Andrea09:08:49

I’m adding reagent to the sci context

Andrea09:08:16

and reagent requires react from cljcljs

borkdude09:08:18

scittle already has a reagent module

borkdude10:08:45

this means that react isn't packaged along with scittle, you need to provide your own (global) version

Andrea10:08:46

hmm, but I’m using my own build

borkdude10:08:58

are you using target esm?

Andrea10:08:32

in our code I expect requires ["react" :as react] to be picked by the import map now

borkdude10:08:45

ok, you can check nbb which also packages reagent without react with esm

borkdude10:08:56

I've made a specific version of reagent there to work around some of the issues

Andrea10:08:12

yeah, I was thinking of forking it with the string requires

borkdude10:08:24

I already did that :)

Andrea10:08:34

cool, under which repo?

Andrea10:08:32

but it’s based on reagent 1.1.1 which expects react 17 I think?

borkdude10:08:50

I didn't even use string requires there, I think I ran into specific issues so I just made it depend on a global being defined

borkdude10:08:00

could be, it's been a while. I don't follow the react trends

Andrea10:08:01

yeah, I’m seeing that, I’ll try with the string require I’m not super keen on this global stuff

Andrea10:08:31

Is there any specific reason to prefer sci.configs for reagent than to use sci.core/copy-ns? I see e.g. set-default-compiler! is missing in sci.configs ?

borkdude10:08:52

just try it and you will find out

Andrea10:08:28

the string require didn’t fix my issue, and also if I’m providing reagent through :namespaces in the sci context, it will pick the one compiled by shadow right? so string requires will be sourced from npm_modules right?

borkdude10:08:11

yes, but just look at nbb for how to configure so that it won't pull in react from your project

Andrea10:08:41

then I have to setup the goog/global thing

borkdude10:08:59

no, I mean the shadow config

Andrea10:08:49

or wait, :keep-as-import as we did before

borkdude10:08:12

you need both the string require + keep-as-import

Andrea10:08:23

yeah, trying that