Fork me on GitHub
#shadow-cljs
<
2022-09-21
>
Logan Powell02:09:11

I read the https://shadow-cljs.github.io/docs/UsersGuide.html#_third_party_tool_integration section in the docs and know that one of my deps (`cljs-ajax`) is doing a dynamic js/require as a https://github.com/JulianBirch/cljs-ajax/blob/master/src/ajax/xml_http_request.cljs#L35, so I'm at a loss for how I should handle this with ESM... any pointers would be appreciated 🙏

Logan Powell02:09:48

My compiled code throws an error here:

ajax.xml_http_request.xmlhttprequest="undefined"!==typeof goog&&"undefined"!==typeof goog.global&&"undefined"!==typeof goog.global.XMLHttpRequest?goog.global.XMLHttpRequest:"undefined"!==typeof require?function(){var a=require;return(a.cljs$core$IFn$_invoke$arity$1?a.cljs$core$IFn$_invoke$arity$1("xmlhttprequest"):a.call(null,"xmlhttprequest")).XMLHttpRequest}():null;ajax.xml_http_request.xmlhttprequest.prototype.ajax$protocols$AjaxImpl$=cljs.core.PROTOCOL_SENTINEL;
ajax.xml_http_request.xmlhttprequest.prototype. <<-- "Cannot read properties of null (reading 'prototype')

thheller05:09:24

@loganpowell require doesn't exist in ESM so the conditional shouldn't do anything? and yes :target :esm is an official target now

Logan Powell11:09:26

Hi @U05224H0W good to hear from you. It looks like it might be skipping the require and just returning the string for the requirement:

ajax.xml_http_request.xmlhttprequest="undefined"

Logan Powell11:09:35

actually, sorry, it looks like it's resolving to null

Logan Powell11:09:40

let me see if I can shim it

Logan Powell12:09:57

could you use something like https://github.com/ts-smith/browserify-shim-cljs/blob/master/src/leiningen/browserify_shim.clj in shadow to change (js/require) statements to (js/import)s?

thheller16:09:23

sorry I don't know what you are doing so I cannot provide any tips

thheller16:09:51

you definitely cannot change any js/require into import. that just doesn't work, they do entirely different things

Logan Powell16:09:41

but I'm getting an error

; SHADOW import error D:\projects\census\census-geojson\.shadow-cljs\builds\node-repl\dev\out\cljs-runtime\shadow.js.shim.module$node_fetch.js
; 
; Execution error (Error) at (<cljs repl>:1).
require() of ES Module D:\projects\census\census-geojson\node_modules\node-fetch\src\index.js from D:\projects\census\census-geojson\[stdin] not supported.
Instead change the require of index.js in D:\projects\census\census-geojson\[stdin] to a dynamic import() which is available in all CommonJS modules.
:repl/exception!

thheller17:09:05

yes, that is node telling you that you cannot require ESM packages

thheller17:09:14

maybe its enough to change your node-fetch version? older versions did ship CJS code

Logan Powell17:09:55

true, but would that work with the :esm target?

thheller17:09:52

:esm can import ESM code yes

Logan Powell17:09:20

that's the target I'm using...

Logan Powell17:09:48

let me write up a gist

Logan Powell17:09:03

here's my setup

thheller17:09:18

> ; Connected session: cljs, repl: node-repl

thheller17:09:48

and ESM doesn't support a REPL in node currently

thheller17:09:17

so you are not using the :esm build at all

Logan Powell17:09:37

when I do:

clj꞉shadow.user꞉> 
; Creating cljs repl session...
; Connecting cljs repl: shadow-cljs...
;   The Calva Connection Log might have more connection progress information.
; Connected session: cljs, repl: :lib
; TIPS: You can choose which REPL to use (clj or cljs):
;    *Calva: Toggle REPL connection*
;    (There is a button in the status bar for this)
; Evaluating file: tests.cljs
; No available JS runtime.
; See 
nil
cljs꞉cljs.user꞉> 

Logan Powell17:09:54

I get No available JS runtime

thheller17:09:05

yes, because you need to run the generated JS

thheller17:09:45

but as I just said. ESM does not support a node REPL

thheller17:09:54

so you won't get a runtime no matter what

Logan Powell17:09:26

so I can't REPL with :esm?

thheller17:09:47

no, since you are supposed to use node-repl for that. ESM just gets in the way

thheller17:09:52

I don't have a solution for that currently

Logan Powell17:09:38

gotcha. so, no ESM while REPL'ng

thheller17:09:01

well the ESM browser has a REPL

Logan Powell17:09:16

:browser target?

thheller17:09:16

node just isn't implemented

thheller17:09:32

no :esm loaded in the browser, not node

Logan Powell17:09:13

let me try to find an older version of node-fetch 😄

Logan Powell17:09:43

seems like a good hack: use a commonjs version of the lib during development ("devDependencies") and then swap it out for esm for the :esm build

Logan Powell01:09:45

Ok, I think I'm almost there! I have a solution that should work in Node and the browser using lambdaisland.fetch and the following conditional :

(when isNode (set! js/fetch node-fetch))
This works in the node-repl but when I release it (`:esm` target), I get
fetch is not defined 
in the bundled code, it looks like:
// looks like it's referencing the lib:
Mi(In,rn,function(a,b,c){try{return Km(Pm(b.text.call(b),function(d){var e=Rs.h(c,C(Mt)).read(d);if(null!=e?e.v&262144||D===e.Lf||(e.v?0:y(Ib,e)):y(Ib,e)){var f=qd(e);d=Hf.j?Hf.j(f,Ps,d):Hf.call(null,f,Ps,d);e=pd(e,d)}return e}))}catch(d){return Nm(d)}});Mi(In,Wl,function(a,b){return b.json.call(b)});var Nt=esm_import$node_fetch <<------- :)

// ... but further down
Gh("ąàáäâãåæăćčĉęèéëêĝĥìíïîĵłľńňòóöőôõðøśșšŝťțŭùúüűûñÿýçżźž","aaaaaaaaaccceeeeeghiiiijllnnoooooooossssttuuuuuunyyczzz");var Tt="undefined"!==typeof process&&"undefined"!==typeof process&&"undefined"!==typeof process.versions&&"undefined"!==typeof process&&"undefined"!==typeof process.versions&&"undefined"!==typeof process.versions.node,fq=Tt?"":"",dq=Error;Tt&&(fetch=Nt) <<------- why?
Is it because I set the global variable shim in Node wrong?

Logan Powell01:09:49

Here's my shadow-cls.edn build setup:

:builds       {:lib {:target           :esm
                      :output-dir       "public/census"
                      :modules {:census {:exports {default census.core/census}}}
                      :js-options {:js-provider :import}
                      :compiler-options {:optimizations :advanced}}

Fredrik Andersson08:09:54

Does anybody know of a hiccup -> html build hook for static pages?

Dustin Getz17:09:21

How do I hook shadow watch (before and after compiliation) to run some code? And is there a document somewhere that I've missed that simply lists the config options?