Fork me on GitHub
#clojurescript
<
2022-03-01
>
Benjamin11:03:27

Does anybody use tailwindcss and what is your development tooling story? A coworker keeps mentioning the lsp server for it decided I just look at the docs for now

Lone Ranger16:03:19

clojure-lsp is absolutely amazing

Pepijn de Vos12:03:08

For some reason I have to add ^js in front of PAtom to make this not give a warning. Weird.

12 | (extend-type nyancad.hipflask/PAtom reagent.ratom/IReactiveAtom)
-------^------------------------------------------------------------------------
 Cannot infer target type in expression (. (. nyancad.hipflask/PAtom -prototype) -reagent$ratom$IReactiveAtom$)

p-himik12:03:35

Did you not require [nyancad.hipflask] and/or [reagent.ratom]?

Pepijn de Vos12:03:30

I have required both

p-himik12:03:17

Weird, I can't reproduce it in a toy project, even if I place the protocol and the type in their own namespaces. The only way I see that error is if I remove the requires.

p-himik12:03:48

Make sure you're using the latest CLJS release. If you can still reproduce the error there, I'd say it's worth coming up with a minimal reproducible example and posting it on http://ask.clojure.org.

✅ 1
Lone Ranger16:03:33

I'm assuming this is one of those corner cases there really isn't a good answer for but... I'm having a hell of a time trying to get advanced compilation to play nicely with pouchdb dynamic plugins. https://pouchdb.com/custom.html Does anyone have any thoughts on how they would tackle this? Example of breaking code:

(.. local-pouch
      (createIndex
       (clj->js  {:index {:fields ["type"]}})))
createIndex not found etc Currently using figwheel with the :npm feature.
:npm      {:bundles {"dist/index.bundle.js" "src/js/index.js"}}
Not sure if I should be looking at making a foreign-libs file or using type annotations or some other magic.

p-himik17:03:49

How do you use those .plugin calls in CLJS?

Lone Ranger17:03:01

(.. pouchdb
      (plugin (.. js/window -auth -default))
      (plugin (.. js/window -perms))
      (plugin (.. js/window -pfind -default)))
where pouchdb comes from
(ns some.ns
  (:require pouchdb))
where pouchdb comes from
# ./src/js/index.js
var PouchDB = require('pouchdb').default;
window.pouchdb = PouchDB;
which gets compiled to dist/index.bundle.js via yarn webpack to be used by figwheel's :npm feature
:npm      {:bundles {"dist/index.bundle.js" "src/js/index.js"}}
(which makes it available in the :require of the ns form)

Lone Ranger17:03:03

I tried doing the plugins in the index.js file, but that didn't work either 😞 that also didn't work with :simple optimizations, but I may have made a mistake if that was supposed to work.

p-himik17:03:24

Why do you use js/window and not regular :require? Is Webpack really relevant here? Unclear to which src and dist you're referring to.

Lone Ranger17:03:28

good questions. On js/window ... it worked and I didn't question it. There was a lot of trial and error. The purpose of webpack is to make the javascript dependencies available to the clojurescript code. The output of the webpack compilation is fed to the input of the google closure compiler. dist/index.bundle.js is the output of the webpack compilation. src/index.js is the source file used by webpack to generate dist/index.bundle.js. This is because I'm including the pouchdb dependencies via npm. Although now that you mention it, maybe I could avoid all this by just using script tags

p-himik17:03:35

> The purpose of webpack is to make the javascript dependencies available to the clojurescript code. The output of the webpack compilation is fed to the input of the google closure compiler. Why?.. Is it a limitation of Figwheel? With shadow-cljs, I can just use NPM dependencies without any hassle in 95% of the cases.

Lone Ranger17:03:30

You don't have an index.js file? :thinking_face: interesting

p-himik17:03:56

If I were in your place, I would definitely try to see if shadow-cljs works in this case. You wouldn't need script tags, js/window, webpack,.. - none of that stuff. You'd just use an NPM dependency almost as if it were a CLJ dependency.

Lone Ranger17:03:53

so let me get this right... you just npm install whatever and then do (:require whatever) in your ns form and that's all there is to it for you?

p-himik17:03:20

As an example, I use mousetrap in one of my projects. So I did npm i -E mousetrap at some point, I have (:require ["mousetrap" :as mousetrap]) in one of the files, and then I use that mousetrap via regular JS interop.

p-himik17:03:16

> and that's all there is to it for you? Most of the time, yes. Although I put double quotes in the :require for NPM dependencies. I don't think it's strictly needed for any library names/paths that are valid CLJS symbols, but I still prefer to do it to make it clear that the dependency comes from NPM.

Lone Ranger17:03:09

interesting :thinking_face:. Well the main issue for me is runtime dynamic advanced compilation. If shadow can handle that, then I suppose it's time to switch tooling

p-himik17:03:11

I think that nowadays figwheel-main has a similar way of working with NPM dependencies, but I haven't used it.

Lone Ranger17:03:22

I've tried it but I can't figure it out

Lone Ranger17:03:27

so I'm still using the old way

p-himik17:03:41

> runtime dynamic advanced compilation I understand the words "advanced compilation". What does "runtime compilation" mean in your case? And what is "dynamic compilation"?

Lone Ranger17:03:54

It's the "plugin" thing.

p-himik17:03:08

Do you need to be able to switch between plugins in run time? Turn some things off?

Lone Ranger17:03:16

So there's a pouchdb object, right? And the "plugin" just adds methods onto the pouchdb object.

p-himik17:03:38

If pouchdb is written correctly, that should be irrelevant.

p-himik17:03:12

If it's not, you can stop the needed methods from being munged by optimization via externs.

p-himik17:03:58

mousetrap also has plugins - and they work just fine, I just require every plugin I use and IIRC register them with Mousetrap, no issues whatsoever.

Lone Ranger17:03:58

Perhaps, but I think the issue is that when the advanced compiler sees (.. pouchdb-instance -some-plugin some-plugin-function) it may not be obvious what it is and isn't supposed to rename

p-himik17:03:15

That's what externs are for.

Lone Ranger17:03:48

> mousetrap also has plugins - and they work just fine, I just require every plugin I use and IIRC register them with Mousetrap, no issues whatsoever. This might be what I'm looking for. Is that a shadow specific thing or is that the clojurescript notion of externs?

Lone Ranger17:03:23

I wasn't sure if shadow/figwheel and the externs played nicely or if they were incompatible

p-himik17:03:44

If something comes from a :require of an NPM dependency, every method call and property access on it, including chained ones, will be used in automatic externs inference. Meaning, such things won't be renamed. If the compiler doesn't know about whether something is a JS, a CLJS, or a goog object, and you're using JS interop on it, it will warn you that the type is unknown - most of the time, it means that you're trying to use interop on a JS object where the fields/methods must not be renamed. In that case, just put ^js in front of the binding. There has been a recent discussion about it, but ^js doesn't survive built-in CLJS functions. So e.g. in (first #js [#js {}]) CLJS will not know that its result is a JS value. Personally, I prefer to simply put ^js on all bindings for JS objects that don't come from goog or CLJS.

p-himik17:03:29

> I wasn't sure if shadow/figwheel and the externs played nicely or if they were incompatible Both shadow-cljs and figwheel[-main] are just wrappers+modifications of the CLJS compiler. They do work with and rely on externs.

p-himik17:03:14

But note that manually writing externs files is pretty much an outdated phenomenon by now, at least in my understanding. I haven't had to do it for a few years now. Literally all you need is to put ^js in the right places.

p-himik17:03:48

A rather old but still relevant article from the author of shadow-cljs: https://code.thheller.com/blog/shadow-cljs/2017/11/06/improved-externs-inference.html

Lone Ranger17:03:20

> But note that manually writing externs files is pretty much an outdated phenomenon by now, at least in my understanding. I haven't had to do it for a few years now. Literally all you need is to put ^js in the right places. yeah exactly, struggling to remember how to do it. Was assuming I must be doing something else wrong.

Lone Ranger17:03:29

Would you try externs first or switching to shadow first?

p-himik17:03:39

Oh, apart from automatic externs inference from all things that come from NPM, you also already have externs inference for all things that come from js/*. So location will never be renamed in (.-location js/window) or js/window.location.

p-himik17:03:25

> Would you try externs first or switching to shadow first? Definitely switching, because the tool has been a fantastic asset in my projects, and its author is one of the most responsive people on this server.

Lone Ranger17:03:47

fair enough! Ok I'll give it a shot and report back 🙂 thanks so much for your insight!

👍 1
p-himik17:03:50

Also, its documentation is hands down the best among other similar tools.

Lone Ranger18:03:06

Okay I ended up getting it to work with the externs route, just b/c it was quicker to try than converting tooling. I guess I'm stuck on FW awhile longer 😅

Lone Ranger18:03:58

for posterity,

# src/js/pouchdb-externs.js

var pouchdb = function() {};
pouchdb.plugin = function() {};
pouchdb.plugin().auth = {};

var auth = {};
auth.default = {};

var perms = {};
var pfind = {};
pfind.default = {};

pouchdb.logIn = function() {};
pouchdb.changes = function() {};
pouchdb.changes().on = function() {};
pouchdb.createIndex = function() {};
pouchdb.get = function() {};
pouchdb.put = function() {};
 = function() {};
pouchdb.remove = function() {};
pouchdb.allDocs = function() {};
pouchdb.find = function() {};
pouchdb.sync = function() {};
pouchdb.sync().on = function() {};
pouchdb.destroy = function() {};
with
:externs ["src/js/pouchdb-externs.js"]
did the trick

👍 1
Sam Ritchie19:03:21

Hey all: with the latest version of cljs 1.11.4, I am seeing this warning at the REPL:

------ WARNING #6 - :infer-warning ---------------------------------------------
 File: /Users/sritchie/code/clj/sicmutils/src/sicmutils/numbers.cljc:372:31
--------------------------------------------------------------------------------
 369 |      ;; 
 370 |      (letfn [(long-expt [base pow]
 371 |                (loop [^Long n pow
 372 |                       ^Long y (.getOne Long)
-------------------------------------^------------------------------------------
 Cannot infer target type in expression (. Long getOne)
shadow-cljs does not give me that warning anymore when I replace that code with (Long/getOne), but then at the cider repl I DO see:
sicmutils.value> (goog.math.Long/getOne)
WARNING: Use of undeclared Var goog.math.Long/getOne at line 1 <cljs repl>
#object[Long 1]
the cider repl in emacs is using the same cljs version. Has anyone seen this before?

Sam Ritchie20:03:54

Thanks @U05224H0W! I’ll look out for a release and finish up my cljs upgrade down the road.