Fork me on GitHub
#shadow-cljs
<
2018-07-20
>
Hukka07:07:06

@richiardiandrea I've had to inject an env param on clj side, but haven't had to work on that on cljs side. Have you checked what is inside the environ? If nothing, perhaps there's a param to put something in there depending on which profile you are running

thheller07:07:05

@richiardiandrea no there is no built-in closure define for that. why do you want a :node-script without a :main though? wouldn't the generic node-repl work then?

Hukka07:07:00

Does shadow do something different with macros? I'm getting "undeclared Var" for using JS module from inside a macro, though the actual use is in #?(:cljs)

Hukka07:07:31

26 | #?(:cljs (deficon postilaatikko "0 0 46 54"))
----------------^---------------------------------------------------------------
 Use of undeclared Var reititin.icons/SvgIcon

thheller07:07:38

try ~'SvgIcon otherwise the unquote willl qualify the symbol

thheller07:07:06

and the clojure side can't see the SvgIcon

thheller07:07:00

to answer your question though: no, shadow-cljs does not do something different with macros

Hukka07:07:20

Hey, awesome, thanks! No idea how that worked before, again

Hukka07:07:44

Though then I did use mui/SvgIcon instead, since cljsjs used a big bundle anyway :thinking_face:

thheller07:07:47

wait it did work in standard CLJS?

Hukka07:07:21

But using the symbol name with ns qualifier shouldn't matter, right?

thheller07:07:22

you mean js/MaterialUI...?

Hukka07:07:03

No, just mui/SvgIcon, not going through js globals

thheller07:07:05

if a symbol is with a namespace the macroexpand will leave it alone. if its without symbol the unquote will try to qualify it

Hukka07:07:27

requiring from "material-ui" :as mui, which cljsjs provides

thheller07:07:32

hmm what was mui?

Hukka07:07:59

But ok, that explains. I hadn't realized the namespace affects macroexpand

Hukka09:07:50

@thheller What would be the recommended way to load css files in npm modules, what in webpack world would be handled by the file loaders?

thheller09:07:56

currently thats not supported. I have a few plans for that but didn't have time to work on it

Hukka09:07:06

Ah 😞

thheller09:07:28

I currently just copy the styles out manually with a script

thheller09:07:23

or copy by hand since most of the time the css isn't even linked from the JS

thheller09:07:44

ie. codemirror or react-table. they just contain css files in the npm module but don't refer to them from the JS

thheller09:07:32

its a webpack-only thing to refer to css files from JS files and I hope it doesn't become more widespread

Hukka09:07:39

Yes, it would make more sense to use the style field in package.json

Hukka09:07:48

Leaflet does that, though, so it could be read from there

Hukka09:07:12

I suppose so that if anything requires the module, then the style is injected into head first

Hukka09:07:24

In any case, that was the last missing piece. I just downloaded the css as a static file into the project, which is a bit ugly. Have to remember to update it, if I ever update leaflet. But... things work now!

thheller09:07:43

extracting the require("./thing.css") is easy enough as they are already filtered out

thheller09:07:52

keeping them somewhere to process later would be easy

Hukka09:07:11

I'm not sure if leaflet requires the css anywhere, didn't check

thheller09:07:31

yeah but if its referenced in package.json is equally easy to extract

Hukka09:07:08

How hard you think that might be, if/when I'd delved into shadow-cljs?

thheller09:07:11

just copying some css is super easy. the problem comes when you need to process scss or @import "./another-file.scss"

thheller09:07:48

not hard at all. you can do it all via build hooks. https://shadow-cljs.github.io/docs/UsersGuide.html#build-hooks

Hukka09:07:18

Hm, so this wouldn't even require modifying shadow-cljs, and could be done via pure config?

thheller09:07:22

doing it performant without repeating all the work constantly in watch is a bit more complicated though

thheller09:07:12

no modifications to shadow-cljs are required. all the build data is available in the build hooks. you just extract what you need.

thheller09:07:22

granted that basically nothing of the build data is documented and finding the right thing to extract might be difficult

Hukka09:07:44

Somehow I have the habit of always ending up on the rocky paths, so to speak, when it comes to Clojure

Hukka09:07:16

A coworker keeps getting amazed at it, always talking how his experiences have been so smooth

thheller09:07:42

well its rocky if you expect things to work like webpack. Clojure isn't like webpack or npm

thheller09:07:40

and my past experiences with either npm or webpack have been far from smooth. quite the opposite actually.

Hukka09:07:41

This is a rather more common than just working node modules, or even clojurescript in general.

Hukka09:07:03

It took me less than a month to end up looking at clojure source itself to figure out what's happening in a rare case

Hukka09:07:12

Do you think documenting an example build-hook would be a good solution, or would it be better still to do some changes into shadow-cljs that would handle it automatically, when it sees a package.json with "style"?

thheller09:07:24

as I said: I do have plans for a built-in solution but that will take some time. currently too busy with other stuff.

thheller09:07:36

maybe next month

thheller09:07:23

I think that a build-hook would be useful too though and it will be impossible to cover every single scenario

thheller09:07:18

I never saw a style field in package.json for example

thheller09:07:32

so it might be only leaflet doing something like that

Hukka09:07:59

Sorry, didn't mean that you should reprioritize, but I was wondering if this is something I might be able to do

Hukka09:07:13

And if it would be more worthwhile than solving it via the build hooks

thheller09:07:12

what exactly do you want to do? build-hooks may be one solution but you might not even need those

Hukka09:07:25

Not sure if it's exactly common, but for example bootstrap and normalize.css do it do

Hukka09:07:15

If you think it makes sense for shadow-cljs to automatically inject the css files for modules it sees having that in package.json, without any extra config, I could try to implement that

thheller10:07:24

inject where?

Hukka10:07:34

You said you had plans or ideas already, so if with those I wouldn't even need the build-hooks, maybe I could just as well help solve this for everyone

thheller10:07:53

plans for extracting the data and making it available to other tools (such as build hooks)

thheller10:07:28

(ns demo.hooks
  (:require [shadow.build.data :as data]
            [shadow.build.npm :as npm]))

(defn dummy
  {:shadow.build/stage :flush}
  [{:keys [npm] :as state} & args]
  (let [used-npm-modules
        (->> (data/get-build-sources state)
             (map :package-name)
             (remove nil?)
             (into #{}))
        
        styles
        (->> used-npm-modules
             (map #(npm/find-package npm %))
             (map :package-json)
             (filter #(get % "style"))
             (into []))]

    (prn [:styles styles])
    (prn [::flush args used-npm-modules]))
  state)

thheller10:07:48

:build-hooks
   [(demo.hooks/dummy 1 2 3)]

thheller10:07:24

this will find all the npm packages that were used in the build and give you the package.json data to work with

thheller10:07:38

from there you can extract the style and post-process it however you want

thheller10:07:06

its just a dummy test hook I had but maybe that can help you get started

Hukka10:07:11

Certainly will. Can build-hooks work on closure defines, or have some other way to communicate to CLJS/JS code that "here's the list of CSS files you should inject before doing anything else"?

thheller10:07:11

define what you mean by inject? inject where? and what?

Hukka10:07:03

Inject into <head>, either the links to the static CSS files copied to the output dir, or as <style> tags inlining the CSS

thheller10:07:10

you could copy the css files to the output dir yes. how that ends up in the HTML is up to you

Hukka10:07:16

Of course the best (perhaps the "right") way to do it would only trigger when any code actually requiring the given npm lib is loaded, so that big CSS files are not downloaded without actual need

thheller10:07:58

the above snippet only include npm modules that where actually required

Hukka10:07:07

Yes, I'm trying to figure out if the build-hooks have a way to set some environment where the other part could pick it up, for example some code in the main cljs code

thheller10:07:29

if you write the CLJS code to inject something I can tell you how to get the data there

Hukka10:07:33

Mm, I'll try to rephrase that... if using code splitting, only at the point the relevant parts are loaded

thheller10:07:55

if you code split the above code is only a tiny bit more complicated

thheller10:07:13

just have to work through :build-modules instead of :build-sources. :build-modules is a vector of maps. each will have a :sources key which lists all the sources included in that module

thheller10:07:24

but right now I don't understand how you are planning to inject the CSS files via CLJS

thheller10:07:40

webpack commonly writes a HTML file directly and does not inject the styles via JS

thheller10:07:54

so I wouldn't inject the styles via JS either since thats just going to be slow

Hukka10:07:33

I was thinking that the CSS files are static, but the tag to load them would be loaded just in time for the code that needs it. Or alternatively I was really thinking about inlining the whole thing, as I think JSS does it.

Hukka10:07:12

So when the cljs requiring leaflet is loaded, <link href="vendor/leaflet/leaflet.css" rel="stylesheet" type="text/css"> is added to head just before

Hukka10:07:19

Or whatever the url is

thheller10:07:41

yeah but you first have to load the JS, parse it, execute it, then that adds the css which the browser then downloads etc

thheller10:07:01

thats a gazillion times slower than having the browser download it in parallel with your JS

thheller10:07:18

so really the css should be included via your HTML statically

Hukka10:07:22

If it's inlined in the JS, it's downloaded at the same time as the code

thheller10:07:39

but you still have to wait for parse&execute

Hukka10:07:42

But yes, code splitting + static CSS files would add roundtrips

thheller10:07:52

plus caching will suck because you invalidate more

Hukka10:07:42

I still wouldn't want to add it manually, even if it's in the static index.html. So, you'd say that serving the html via template, or building it via template into a static file would make more sense?

thheller10:07:49

what I would do it extract all the referenced styles and then per module create a corresponding .css file. so for :modules {:main {:entries ...}} which creates a main.js I would create a main.css and include that statically via the HTML when including the main.js

thheller10:07:25

rewriting a .html file is equally trivial

thheller10:07:45

but it all completely depends on what you are building. your requirements might be different.

Hukka10:07:18

Caching isn't that important at this point for the CSS files, so bundling into a single main.css could work. @imports in the main.css would add roundtrips as well, so I don't think I'd go that route. But might as well build the html file when building the js bundle, so that it would include all the separate css files too.

thheller10:07:52

I would try to resolve the imports and so that you only have one css file per module

thheller10:07:06

in my work projects I use a very simple node-sass setup which has been good enough for me for years

Hukka10:07:54

Thanks for your input and help. I'll save these snippets and the discussion, and I'll get back to you after I've worked on a good enough start for the tutorial to get some feedback on, and include the build-hook there too.

Hukka11:07:22

Before: cljsjs, muiv0 – 4MB. Now: shadow and npm es modules, muiv1 – 1MB

thheller12:07:50

small improvement 😉

plamen16:07:10

Hello all again - I have another question (I don’t know if there is a short answer or not): I came to shadow-cljs because of the need to convert an older Vaadin based application part of which I would like to reimplement in ClojureScript, but still using at least one Vaadin grid component from their JS/React based framework which because of that would theoretically be usable from ClojureScript. The part which I don’t know is (by lack of enough experience in later years around all facets of JS and its build processes/frameworks) if it is possible to use it through shadow-cljs (or vanila cljs) while it is delivered under a NPM (as a wrapper) and (the actual build recipe) Bower build. I understand that the problem has several steps in the doability (can Closure process it, if not, how does it present its namespaces to Closure/cljs/shadow-cljs, is that understood from the build chain, the Bower config is not a single module description, but a bunch of dozens smaller components from the framework and related js libraries, is there a single js file produced at the end of Bower which can be accessed from cljs/Closure etc etc.) If anyone can shed some light into it I would be thankful.

Hukka18:07:08

Well it probably doesn't make any sense to somehow try to build the library component with shadow. But there's no need, also. As it works as npm module, it should work just fine.

Hukka18:07:35

Unless there are some "interesting" output formats where a npm lib is supposed to only work with other bower projects, and not offering a built ES, commonjs, AMD, or UMD module. Or just a var in global scope

thheller19:07:10

@plamen I'm gonna need more details about the build setup otherwise I can't make any suggestions. I don't know Vaadin or how bower is involved. If everything is normal JS it should be easy to consume but looking at the demo application it seems to construct everything together from HTML files, eg. https://github.com/vaadin/expense-manager-demo/blob/master/src/content-panel.html

thheller19:07:29

if bower produces commonjs out of that its easy

thheller19:07:35

but I don't know

thheller19:07:20

closure has some built-in support for polymer but I never used that so it might not work properly

eoliphant23:07:12

hi, I’ve seen some notes around pulling in aws-sdk. Not sure if there’s a new issue or i’m doing something wrong ["aws-sdk" :refer [AWS]] or ["aws-sdk" :default AWS] are resulting in a null AWS

eoliphant23:07:01

Hi @plamen I’ve actualy done quite a bit of Vaadin development, but need a little more info. How old is the app? Prior to v10 they had both the legacy java/gwt stuff and then introduced the Polymer (not React) based ‘elements’ in the v8 timeframe as a separate sort of parallel option. That was a stepping stone to the new Flow architecture in v10 that basically unified all of that. The Polymer based stuff is ultimately just javascript so you could potentially use it from clojurescript, but I’ve not tried it myself. There was a clojure wrapper for the v7/8 java stuff, i’m not aware of anything for the client stuff. But it’s again, just javascript. One word of caution though. While clojure/script tends to make everything more wonderful, Vaadin’s (and Polymer’s) programming models are not functional/reactive, but object based, and even in Clojure(script) don’t feel nearly as nice as the react-based stuff in the clojurescript ecosystem. That Clojure wrapper for the java stuff, did reduce some of the cruft and ceremony, but you were still very much ‘programming objects’. There have been a couple libs, like RxVaadin, that tried to make it more FRP’ish, but those were also pretty clunky to implement.