Fork me on GitHub
#shadow-cljs
<
2022-06-14
>
hanDerPeder12:06:17

I’m trying to set up a UI library using React+Typescript+Storybook+MaterialUI and consume it as a library in clojurescript. I’ve been able to export a commonjs file and require it from my reagent app, but the styles are not applied. Seems material ui uses css-in-js which I’m not familiar with. Grepping the dist file the styles seem to be present. Any idea how to debug this?

hanDerPeder12:06:58

changing from commonjs output to esm seems to have worked. only snag is I have to edit the output file and replace import * as React from "react"; with import React from "react";

hanDerPeder12:06:22

the user guide says: > Due to strict checking of the Closure Compiler it is not possible to use the import * as X from "npm"; syntax when requiring CLJS or npm code. It is fine to use when requiring other JS files. I output an es module into a file foo.js and require it like: (ns my.ns (:require ["/foo" :as foo])) . From the text quoted above shouldn’t this be fine?

thheller18:06:41

I do not recommend including regular typescript code this way

thheller18:06:02

and what do you mean by storybook? doesn't that have its own tooling and would require integrating the CLJS code in the JS code

thheller18:06:07

rather than the JS code in the CLJS?

hanDerPeder18:06:25

yes, storybook is not relevant here.

hanDerPeder18:06:33

i’m currently attempting to create a bundle using esbuild and including that. inspired by your usage of babel here: https://shadow-cljs.github.io/docs/UsersGuide.html#_javascript_dialects

hanDerPeder18:06:03

not sure if this is fruitful or not, any suggestions would be greatly appreciated.

hanDerPeder18:06:41

my goal is to have a setup inspired by nolan’s talk ClojureScript in the Age of TypeScript. I.e. frontend guys writes react components in typescript on storybook which they enjoy because of intelli-sense. and us few that swear to clojure can tie it all together in a small reagent app

thheller18:06:26

ok then I suggest the following route. you build all your TS/JS whatever code into a folder

thheller18:06:39

say packages/your-js-stuff

thheller18:06:57

then in the shadow-cljs build config you set :js-options {:js-package-dirs ["packages" "node_modules"]}

thheller18:06:15

then you just include (:require ["your-js-stuff" ...]) like any other npm package

thheller18:06:41

the packages/your-js-stuff dir ideally also has a proper package.json

thheller18:06:06

basically you build the JS stuff as an npm package, without actually needing to publish it to npm

thheller18:06:44

including it directly from the classpath I don't recommend since that gets different treatment and likely won't work without specific care

hanDerPeder18:06:56

that sounds great, could you give me some pointers regarding build all your js code ? Could this be: 1. define an entrypoint 2. run it through esbuild?

thheller18:06:31

I don't know if it needs any building. you mentioned esbuild so I thought you had the JS build part figured out

thheller18:06:00

shadow-cljs will minify it anyways so you don't need to actually build anything as long as its JS code

hanDerPeder18:06:09

far from it 🙂 learning though, but it’s a steep climb

thheller18:06:10

ts/jsx/etc you however need to build first

hanDerPeder18:06:38

ok, so a .jsx file would be fine?

thheller18:06:59

no, see my sentence above 😛

thheller18:06:13

see the babel section in the docs. it used jsx as an example

hanDerPeder18:06:02

:thumbsup: thanks, I’ll give it a stab

hanDerPeder19:06:14

probably unsurprising to you, but this worked with a trivial react component. thanks a lot!

henryw37415:06:02

hello. an FYI for ppl using devcards. I found that upgrading shadow to 2.19.3 my previously working devcards setup had a couple of problems. I have some initial workarounds I wrote up here: https://github.com/jacekschae/shadow-cljs-devcards/issues/1

👁️ 1
yedi18:06:02

anyone using jspdf with shadow-cljs? I’m seeing this build failure after npm installing it. Seems that the closure compiler isn’t a fan of jsPDF’s minifier?

[:app] Build failure:
Closure compilation failed with 1 errors
--- node_modules/jsPDF/dist/jspdf.es.min.js:10686
Block-scoped variable Vt declared more than once. First occurrence: node_modules/jsPDF/dist/jspdf.es.min.js:9530:13

thheller18:06:54

hmm yeah I've seen those errors on occasion

thheller18:06:08

never can tell if they are actual legit errors or the closure compiler just parsing things wrong

thheller18:06:22

unfortunately nothing I can do from the shadow-cljs side of things

thheller18:06:27

you can try the non-minified version by requiring "jspdf/dist/jspdf.es.js" instead of just jspdf

thheller18:06:07

shadow-cljs will minify it anyways so nothing lost

yedi18:06:48

seems like the issue is with how jspdf packages itself and not just its minifier

[BABEL] Note: The code generator has deoptimised the styling of /Users/yedi/Documents/workspace/mobot/mobot-web/node_modules/jspdf/dist/jspdf.es.js as it exceeds the max of 500KB.
[:app] Build failure:
Closure compilation failed with 1 errors
--- node_modules/jspdf/dist/jspdf.es.js:3834
Block-scoped variable ga declared more than once. First occurrence: node_modules/jspdf/dist/jspdf.es.js:3834:4095

yedi18:06:36

i wonder if it makes sense to use the cljsjs jspdf artifact if requiring it through npm doesn’t work… though i can’t imagine noone is using jspdf with shadow-cljs

thheller18:06:39

you can also try the jspdf.umd.js file

thheller18:06:01

you can use the cdn artifact

thheller18:06:19

cljsjs is not supported so that won't work

1
yedi18:06:53

hm new build failure with umd

[:app] Compiling ...
[2022-06-14 14:33:09.670 - INFO] :shadow.build.npm/js-invalid-requires - {:resource-name "node_modules/jspdf/dist/jspdf.umd.js", :requires [{:line 15743, :column 14} {:line 15777, :column 14} {:line 25168, :column 14}]}
[:app] Build failure:
The required JS dependency "worker_threads" is not available, it was required by "node_modules/jspdf/dist/jspdf.umd.js".

thheller18:06:52

hmm yeah thats not any better. you can set :js-options {:resolve {"worker_threads" false}} in your build config

yedi20:06:13

no luck, we’re back to the ga issue

[2022-06-14 15:36:04.439 - INFO] :shadow.build.npm/js-invalid-requires - {:resource-name "node_modules/jspdf/dist/jspdf.umd.js", :requires [{:line 15743, :column 14} {:line 15777, :column 14} {:line 25168, :column 14}]}
[:app] Build failure:
Closure compilation failed with 1 errors
--- node_modules/jspdf/dist/jspdf.umd.js:23062
Block-scoped variable ga declared more than once. First occurrence: node_modules/jspdf/dist/jspdf.umd.js:21353:15

thheller18:06:55

maybe that helps

hanDerPeder20:06:26

not sure if this is relevant to shadow but I’m trying to require a module i’ve created:

// src/stack.jsx
import * as React from "react";
import Button from "@mui/material/Button";
function stack_default() {
  return /* @__PURE__ */ React.createElement(Button, {
    variant: "contained"
  }, "Hello World");
}
export {
  stack_default as stack
};
and render it using reagent:
(ns 
  (:require
   [reagent.core :as r]
   [reagent.dom :as rdom]
   ["components" :as c]))

(let [elem (.getElementById js/document "app")]
    (rdom/render [:f> c/stack] elem))
it compiles fine but blows up with a lot of errors, notably: • Warning: Invalid hook call. Hooks can only be called inside of the body of a function component. • Uncaught TypeError: dispatcher is null • The above error occurred in the <ForwardRef> component: anybody seem something similar to this?

hanDerPeder20:06:49

this works though, which I don’t understand:

(ns 
  (:require
   [reagent.core :as r]
   [reagent.dom :as rdom]
   ["@mui/material" :as mui]))
(let [elem (.getElementById js/document "app")]
  (rdom/render [:> mui/Button {:variant "contained"} "Hello World"] elem))

hanDerPeder20:06:53

adding :entry-keys ["module" "browser" "main"] seems to have fixed it :thumbsup:

hanDerPeder20:06:07

no, still issues 😕 I have the following layout:

src/ <- on classpath
packages/foo <- a npm package
packages/foo/dist/index.js <- file I want
packages/foo/node_modules <- foo's dependencies
foo’s package.json has:
{
  ...
  "module": "dist/index.js",
  ...
}
shadow-cljs.edn:
{:dependencies
 [[reagent "1.1.1"]]

 :source-paths
 ["src"]

 :dev-http {8080 {:root "public"}},
 :builds
 {:app {:target :browser
        :output-dir "public/js"
        :asset-path "/js"
        :js-options {:js-package-dirs ["packages" "node_modules"]
                     :entry-keys ["module" "browser" "main"]}
        :modules {:main {:entries []}}}}}
seems shadow runs code from foo/node_modules. what i’m trying to say is: 1. look in packages before node_modules, and 2. try to import as es module first anybody know how to achieve this?

hanDerPeder20:06:01

I think there’s an issue with js-package-dirs being searched recursively, so my js module gets react from packages/foo/node_modules while reagent gets it from node_modules

lilactown23:06:32

why do you have nested node_modules?

hanDerPeder06:06:37

packages/foo is a react components library written in typescript, developed with storybook.

thheller06:06:14

it shouldn't have any node_modules at all though. it should just be the output files of your JS build and a package.json

thheller06:06:16

nothing else

thheller06:06:35

cause yes, otherwise it'll follow the node resolve rules and use nested packages if present

hanDerPeder06:06:38

Understood. Was hoping to be able to develop in packages/foo. What if the resolver excluded peer and dev dependencies found? Think that would work for this case.

thheller06:06:47

you can develop whereever you want

thheller06:06:03

the point is having the build output go into a dedicated directory

thheller06:06:51

and no I'm not changing the resolver rules in any way

hanDerPeder06:06:46

😁 worth a shot. thanks for your input on this.

thheller06:06:55

just have the package build into packages/foo/out/foo/index.js or something

thheller06:06:07

and then use packages/foo/out

thheller06:06:11

or just symlink it

thheller06:06:51

there is also :js-options {:allow-nested-packages false} but I strongly recommend not setting it

thheller06:06:28

it is very likely that you actually want nested packages

thheller06:06:06

in your main project you can also just npm install ./packages/foo I believe. which would take care of copying stuff over to the local node_modules. no need to :js-package-dirs then

hanDerPeder06:06:49

No. At least by default it symlinks, so same problem.

thheller06:06:52

then in the package you can npm package or whatever the command is and get a .tgz file which you can then install

hanDerPeder06:06:53

But your former suggestion sounds good. Do i need to generate a package.json in out/foo?

thheller06:06:19

you don't need it at all if you just generate a out/foo/index.js

thheller06:06:35

but I still recommend creating one. "how" is up to you.

thheller06:06:53

could just always have it there manually written and generate into a dist folder like you had

hanDerPeder07:06:05

Asking because im sure ill get the question 😄

hanDerPeder07:06:44

Include a package.json file in out/foo. Seems it's not needed. Future proofing?

thheller07:06:51

it is used for stuff like build reports

thheller07:06:06

basically it is looked for first

thheller07:06:14

since npm packages are supposed to have it

thheller07:06:42

so without it resolve is missing an important piece and falling back to index.js is just the last looked at option

hanDerPeder07:06:09

Makes sense. Again, thank you for the guidance.