Fork me on GitHub
#shadow-cljs
<
2022-10-26
>
Rambabu Patina04:10:47

Hi Team, I need help with shadow-cljs. I want to exclude particular library while building. The shadow-cljs.edn looks such as

:app {:target :browser
   ....
   :modules {:shared {:entries []}
             :other {:entries ..}
   ...
My assumption is if the entries is empty (i.e []) then bundling all dependencies in public/shared.js file. I want to exclude a library which is not required at client side. I appreciate your time. Thanks

thheller05:10:14

shadow-cljs includes everything you :require in the ns forms, thus you need to remove the require to get rid of it?

Rambabu Patina05:10:43

Thanks @U05224H0W for your quick response. I cannot remove that because it is required for one of the backend service

Rambabu Patina05:10:52

And I want to exclude it in shared.js, because it is not needed in client side

thheller05:10:32

then you may need to split a namespace that contains the backend code into one that is one included on the backend and one that contains the client side

thheller05:10:28

if the code is not needed it should not be required

thheller05:10:42

or are you asking about a specific npm library?

Rambabu Patina05:10:53

I was facing issues with https://www.npmjs.com/package/aws4-axios. I too have other npm modules such as axios, Luxon etc, there are fine.

Rambabu Patina05:10:52

FYI, I have separate name space for UI and Server

thheller05:10:51

in general server side code should just not be included in a client side build. your namespaces should be structured accordingly

🆗 1
thheller05:10:52

and only your namespace :require controls what is included, so thats what you need to work out. can't give many tips besides that

thheller05:10:36

you can disable the inclusion of certain npm packages via :js-options {:resolve {"package-you-dont-want" false}} but that won't affect the CLJS code getting included

thheller05:10:08

so best option is clearly structuring namespaces so certain stuff just isn't required in the first place

Rambabu Patina05:10:59

Sure I will re-check the structure

thheller06:10:00

just to confirm: you are not using :modules and a single build to compile client and server code together? so you don't have a :shared module and then a :client and :server?

Rambabu Patina07:10:07

I do have :shared module for client. For server we have separate build. Please find the shadow-cljs.edn file

thheller07:10:33

ok, that should be fine

thheller07:10:24

FYI you do not need :entries [our-app.ui.pages.getting-started] if :init-fn is the same namespace. :init-fn desugars to that :entries, so specifying it again is redundant. just :init-fn is fine.

Rambabu Patina09:10:08

Great @U05224H0W. It worked. I appreciate your help. Thanks

simongray13:10:04

Is it true that I can't have local dependencies in my shadow-cljs.edn? When I Google it, people say to use deps.edn as a fallback, but then I won't be able to use NPM packages. I feel stuck between a rock and a hard place.

thheller13:10:48

using deps.edn has no impact on the ability to use npm packages. that continues to work just fine

1
thheller13:10:14

by "local dependencies" I assume you mean :local/root from deps.edn?

thheller13:10:45

you are just moving your CLJ/CLJS dependencies to deps.edn and thats it

thheller13:10:00

npm stuff just stays as it is

Richie13:10:48

I'm trying to follow the electron instructions for https://www.electronjs.org/docs/latest/tutorial/ipc and I added a build for the preload.js file.

{:builds {:preload {:target :node-script
                    :output-to "resources/preload.js"
                    :main app.preload.core/main
                    :hashbang false}
          ...}
 ...}
Now, I'm getting tons of warnings like DevTools failed to load source map: Could not parse content for file:///C:/Users/richie/Documents/org/projects/electric-clj-play/resources/public/goog.debug.error.js.map: Unexpected end of JSON input It's not at resources/public/goog.debug.error.js.map , it's at resources/public/js/cljs-runtime/goog.debug.error.js.map. How can I fix this? Thanks!

thheller13:10:44

as far as I know the preload script is not actually node? its been many years since I even looked at electron so I'm not actually sure what this needs

Richie13:10:10

I think you're right. It was described as being the same as the "renderer" but runs with more permissions. I first tried :target :browser but had some trouble and then found https://clojurians.slack.com/archives/C6N245JGG/p1634150800435700 thread. I'll try :target :browser again since now I don't remember what the problem was.

thheller14:10:31

yeah :node-script really assumes node and at least watch/compile builds will probably only run in actual node

thheller14:10:58

:target :esm might also be an option. last time I looked at electron it didn't support ESM but its been a while

Richie14:10:54

I don't get results in https://grep.app/search?q=exposeInMainWorld&amp;filter[lang][0]=Clojure and I haven't found examples e.g. https://github.com/logseq/logseq/blob/master/resources/js/preload.js just does it in javascript. I tried adding a new browser target

:preload {:target :browser
                    :devtools {:watch-dir "resources/public"}
                    :output-dir "resources/public/js/preload"
                    :asset-path "js/preload"
                    :modules {:preload {:entries [app.preload.core]
                                        :init-fn app.preload.core/main}}}

          :renderer {:target :browser
                     :devtools {:watch-dir "resources/public"}
                     :output-dir "resources/public/js"
                     :asset-path "js"
                     :modules {:renderer {:entries [user]
                                          :init-fn app.renderer.core/start!}}}
which gives me a bunch of errors like ReferenceError: goog is not defined and I tried not adding a browser target for preload and instead just adding a module
:renderer {:target :browser
                     :devtools {:watch-dir "resources/public"}
                     :output-dir "resources/public/js"
                     :asset-path "js"
                     :modules {:shared {:entries []}
                               :renderer {:entries [user]
                                          :init-fn app.renderer.core/start!
                                          :depends-on #{:shared}}
                               :preload {:entries [app.preload.core]
                                         :init-fn app.preload.core/main
                                         :depends-on #{:shared}}}}}
but that gives me
Unable to load preload script: C:\Users\richie\Documents\org\projects\electric-clj-play\resources\public\js\preload.js
ReferenceError: SHADOW_ENV is not defined
Uncaught ReferenceError: SHADOW_ENV is not defined

Richie15:10:26

:target :esm didn't work right away although now I can't remember what was wrong... shoot.

Richie01:10:29

Aww man. When I started out, it was working but had warnings. Now, it's broken and I can't make it work at all. I'm sad. I think I'm going to give up on electron.

thheller04:10:13

if you want to setup a repo with the failing setup I can probably tell you where the error is

Richie12:10:02

I tried to get everything back to where I think it was when I started. The time where I was just complaining about warnings. Now I have ReferenceError: __dirname is not defined coming from the preload.js file. https://github.com/rgkirch/electron-help Thanks a million! My goal was to add a second electron window to help debug things. I have some tracing in my main app that I wanted to send over. Initially I assumed they'd share my global state but it looks like I have to use ipc.

Richie13:10:36

Alright. I must have had nodeIntegration enabled. e.g. :webPreferences {:nodeIntegration true I'm back to just having the warnings. That means I just don't know the "right" way to do this since I'm override the default behavior and what I'm doing here is discouraged for security reasons.

Richie13:10:24

I feel a little better though.

Richie13:10:21

After I got discouraged I started reading the docs on cljfx but I might keep trucking with this for a bit. Since :target shouldn't be a :node-script, do you have any suggestions on how to make it work as :esm or :browser? Thanks again!

Richie14:10:37

I tried :esm https://github.com/rgkirch/electron-help/compare/master...target-esm. I get SyntaxError: Cannot use import statement outside a module.

thheller17:10:47

I setup a full repo with a working setup

1
thheller17:10:03

yours had a little too much extra chaos in it 😉

thheller13:10:19

:target :browser might be a better fit, but running with :js-options {:js-provider :require} so it doesn't attempt to bundle stuff

Richie17:10:26

I misunderstood this comment. I read it as suggesting either :target :browser or :target :node-script with :js-options {:js-provider :require} I tried

:preload {:target :browser
                    :devtools {:watch-dir "resources/public"}
                    :output-dir "resources/public/js"
                    :asset-path "js"
                    :modules {:preload {:entries [app.preload.core]
                                        :init-fn app.preload.core/main}}
                    :js-options {:js-provider :require}}

          :renderer {:target :browser
                     :devtools {:watch-dir "resources/public"}
                     :output-dir "resources/public/js"
                     :asset-path "js"
                     :modules {:renderer {:entries [user]
                                          :init-fn app.renderer.core/start!}}}
but get ReferenceError: goog is not defined

Lone Ranger19:10:04

Is there a "cannonical" alternative...?

thheller20:10:27

to do what exactly?

Lone Ranger21:10:46

(:require ["/my/awesome/stuff" :as awesome-stuff]) Previously I'd been using figwheel with webpack to make an index.bundle.js and then

(:require awesome-stuff)
with
// index.js
awesomeStuff = require('src/js/my/awesome/stuff.js')
window.awesomeStuff = awesomeStuff
then yarn webpack etc

Lone Ranger21:10:14

this is way better

thheller04:10:54

well, they are not really solving the same problem

Lone Ranger21:10:22

Ok I have a flagrantly stupid question but if anyone is up for a little typescript/javascript/clojurescript/shadow-cljs to challenge themselves, here goes... one of my javascript imports is not being resolved.

import {Config} from "../config";  // this explodes in the browser
Ironically, if I do this:
(ns app.lib.ext.config
  (:require ["/app/config" :as app-config]))

(def ^:export Config app-config/Config)
and then in my javascript I do
import config from "goog:app.lib.ext.config";
const Config = config.Config; 
it works great

Lone Ranger22:10:48

Ok I think this question resolves to “how does goog.require work “

thheller04:10:43

your description is somewhat hard to understand

thheller04:10:48

> import {Config} from "../config"; // this explodes in the browser

thheller04:10:57

what does "explodes in the browser" mean?

thheller04:10:15

if shadow-cljs compiles this code it'll never get to the browser in that shape?

thheller04:10:34

(def ^:export Config app-config/Config) why is there an ^:export?

thheller04:10:02

import config from "goog:app.lib.ext.config"; why is this importing the CLJS ns instead of just the config directly?

thheller04:10:08

why is config JS in the first place?

thheller04:10:12

so many questions 😛

thheller04:10:22

goog.require is no-op and does nothing at runtime. it merely serves as static information for the compiler so it can sort sources in the proper order

thheller04:10:30

it serves no other purpose in shadow-cljs. in regular CLJS it is used by the debug loader and actually does something, in shadow-cljs it does not since it uses its own loader.

Lone Ranger12:10:34

Sorry, yeah I know it’s confusing I wasn’t sure how much info to include

Lone Ranger12:10:17

In the browser that JavaScript expands to ”/app/config”

Lone Ranger12:10:41

the import statement

Lone Ranger13:10:52

here's what the error looks like, expanded:

Lone Ranger13:10:05

Uncaught (in promise) ReferenceError: Config$$module$app$config is not defined

Lone Ranger13:10:12

I should also add this is executing in a webworker context

Lone Ranger13:10:30

when I set a break point it still comes up as undefined which is odd considering the import seems to be working

Lone Ranger13:10:31

I'm wondering, should

"/app/config.js"
expand to
"/js/compiled/cljs-runtime/app/config.js"
instead?

thheller16:10:11

I don't know what this output is. it is not output of a shadow-cljs build

thheller16:10:55

and no, it should not expand that "/js/compiled/cljs-runtime/app/config.js"

thheller16:10:14

in fact there should not be any import anymore whatsoever

thheller16:10:32

so I'm not entirely sure what you are doing but the code you showed is not generated by shadow-cljs

Lone Ranger18:10:09

If you don't think this is insanity I can DM you some more detailed code, it's company code so I can't really put in public chat. But here's the setup: • shadow-clj project • three typescript files, (config module config and module that calls a webworker, thread-utils) and (webworker worker) ◦ thread-utils depends on config (this is where the error is happening) • tsconfig file that compiles the typescript into a location in src/gen • shadow project builds :main, :base, and :worker • clojurescript code from :main calls thread-utils from src/gen • crash

Lone Ranger18:10:41

so you are right that shadow-cljs is not generating the javascript

Lone Ranger18:10:41

the generated javascript from the tsconfig is being called with

(:require ["/classpath/to/thread-utils" :as thread-utils])

(thread-utils/testAdd)

Lone Ranger18:10:47

I realize this is insanity, yes.

Lone Ranger18:10:22

mostly I'm trying to localize if this is a google-closure-compiler issue, a misconfigured typescript issue, or a misconfigured shadow-cljs issue

thheller19:10:11

so what does the JS file header look like after ts is done with it? just the import statements is fine

thheller19:10:23

so the actual file shadow-cljs is importing in src/gen I guess

thheller19:10:46

TS might not like the absolute includes, so instead try relative ones. so just ./config.js if they are all in the same dir

thheller19:10:20

I'm really only guessing here. I do not understand your setup, so my guesses will not be very accurate or helpful

Lone Ranger13:10:21

ok just to close the loop, the error was very strange

Lone Ranger13:10:24

// case1.js
export class Config {}
Config.fooPath = Paths.foo
Config.barPath = Paths.bar
vs
// case2.js
export class Config {
   fooPath = Paths.foo
   barPath = Paths.bar
}
the top one does not work with
import {Config} from "/path/to/config";
while the bottom on does. I have no idea why this is the case. But this is the result of:
// case1.tsx
export class Config {
   static readonly fooPath = Paths.foo
   static readonly barPath = Paths.bar
}
// case2.tsx
export class Config {
   fooPath = Paths.foo
   barPath = Paths.bar
}

Lone Ranger13:10:03

The only thing I can think of here is that since the export is declared in the generated code before the static methods are added, the static methods are not exported

Lone Ranger13:10:35

so it wasn't a config error at all, just a masking bug

zhuxun222:10:57

How would you debug when your project runs fine in development mode but when in release mode, the compiled js code crashes with a Error: xx is not a function?

zhuxun222:10:43

For me the difficulty is the code has already been minified and it's hard for me to find out what xx originally was in the source code

zhuxun222:10:54

Is there a way to turn off minification in release mode?

dpsutton22:10:43

> :pretty-print and :pseudo-names default to false. You can use shadow-cljs release app --debug to enable both temporarily without touching your config. This is very useful when running into problem with release builds from the https://shadow-cljs.github.io/docs/UsersGuide.html#compiler-options Information about https://clojurescript.org/reference/compiler-options#pseudo-names

🤯 1
❤️ 1
thheller04:10:55

there is also shadow-cljs release app --pseudo-names, which is without source maps. sometimes some optimizations make the source maps inaccurate and confusing. then its just better to decipher the generated JS, which pseudo names make possible somewhat

dpsutton05:10:11

I thought i remembered that being an option but didn't see it in the users guide. The only mention of pseudo names was what i found unless i missed a reference