This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2023-09-29
Channels
- # announcements (4)
- # babashka (66)
- # beginners (7)
- # cljs-dev (6)
- # clojure (12)
- # clojure-europe (28)
- # clojure-nl (1)
- # clojure-norway (75)
- # clojure-uk (16)
- # clojuredesign-podcast (1)
- # clojurescript (16)
- # datascript (6)
- # deps-new (2)
- # dev-tooling (40)
- # exercism (1)
- # fulcro (92)
- # hyperfiddle (25)
- # lsp (19)
- # malli (1)
- # meander (2)
- # nrepl (9)
- # off-topic (5)
- # pathom (1)
- # practicalli (1)
- # re-frame (20)
- # reitit (14)
- # releases (1)
- # sci (86)
- # shadow-cljs (216)
- # sql (13)
- # testing (4)
- # tools-deps (4)
- # vscode (3)
Hey all, I'm facing a problem with shadow-cljs release :dev
running with watch :dev
and webpack
the app works.
Executing shadow-cljs release :dev && yarn webpack
results in the following error:
main.js:2050 Uncaught TypeError: Cannot read properties of undefined (reading 'Fj')
at main.js:2050:75
at main.js:3074:4
from:
var Faa = new google.Kb.Fj.pb;
webpack:
const path = require("path");
const Dotenv = require("dotenv-webpack");
module.exports = {
mode: process.env["NODE_ENV"] || "development",
optimization: {
minimize: process.env["NODE_ENV"] === "development",
},
entry: "./target/index.js", // Entry point for your application
output: {
filename: "libs.js", // Output bundle file name
path: path.resolve(__dirname, "public/js"), // Output directory
},
resolve: {
extensions: [".js", ".mjs"], // Allow importing both .js and .mjs files without specifying the extension
},
plugins: [new Dotenv()],
module: {
rules: [
{ test: /\.json$/, type: "json", use: ["json-loader"] },
{
test: /\.css$/i,
use: ["style-loader", "css-loader"],
},
{
test: /\.m?js/,
type: "javascript/auto", // Treat .mjs files as JavaScript modules
resolve: {
fullySpecified: false,
},
},
// {
// test: /\.js$/, // Match JavaScript files
// use: "babel-loader", // Example loader (replace with your specific loader)
// exclude: /node_modules/, // Exclude node_modules
// },
],
},
};
shadow:
:dev {:target :browser
:modules {:main {:init-fn main/init}}
:build-hooks [#_(portal.shadow.remote/hook)
(dev.core/rcf-shadow-hook)]
:devtools {:preloads [flow-storm.api
#_portal.shadow.preload]}
:js-options
{:js-provider :external
:external-index "target/index.js"
:ignore-asset-requires true}}
these optimizations sometimes require "externs", to prevent renaming/shortening of certain stuff
so that is why I'm asking whether you get compilation warnings when making the build
I have set the externs only when I had warnings, is there any other place required, like a global place?
you can set shadow-cljs release :dev --pseudo-names
to make it a little more apparent where the error is
no, I'm not currently aware of cases where externs are required but no warnings are generated
except I guess when you are actively writing bad code to circumvent the externs handling
var $locations$locations_service$$ = new google.$maps$.$places$.$AutocompleteService$();
cljs file:
(def locations-service (new google.maps.places.AutocompleteService))
index.html
<script
src=""
type="text/javascript"
></script>
since you are accessing the global google
variable that should be js/google.maps.places.AutocompleteService
(def ^js locations-service
hints the locations-service
as a JS object, but not the call that produces it
hehe because of the closure compiler having some default google
externs shadow-cljs doesn't complain about anything starting with google.
😛
there's another weird behavior I experienced a few days back. Locally the release works, but building it via CI it breaks with:
Uncaught ReferenceError: shadow$bridge is not defined
at main.js:684:61
at main.js:3075:4
I have the same versions of java, clojure and node on the container, and the differences of main.js
and libs.js
are a few bytes.
It's difficult to find the differences due to the sizes of the files.
Do you have any idea what could cause it before I'm digging in randomly?shadow$bridge
is the variable defined by the external output, so I guess thats not loaded correctly?
It's the same code and executes. Only difference is running on my machine vs a container
I don't know your CI setup, so how does that execute? I mean is it a full browser driven remotely or what does that do?
just because there is the same files on disk, does not mean they are loaded the same way 😛
Is shadow-cljs sufficient for everything I wish to do with CLJS-based web apps, to the degree that leiningen is not needed anymore? In other words, are there still things that leiningen can do for CLJS-based web apps, but shadow-cljs can't?
if you want a CLJ webserver or anything server side that is CLJ you likely want leiningen or deps.edn
FWIW at work we use shadow-cljs "by itself", no leiningen or deps.edn For a simple reason: the shadow-cljs release from npm provides everything we need out of the box. The built-in web server with live reloading CSS is pretty useful (in addition to shadow's cljs reloading)
Hello! When I use my app with :optimizations :none
(w/ watch) or :simple
(w/ release) then it works, but if I change the latter to :whitespace
and load the code, it fails with
> TypeError: goog is undefined
Why could that be, and how to fix it? Could it be that e.g. some macro is checking for dev vs prod mode, and tries to use goog
b/c it incorrectly concludes this is dev mode? 🙏
(Using shadow-cljs 2.25.6, which pulls in google-closure-library 0.0-20230227-c7c0a541 and org.clojure/clojurescript 1.11.60)
it is the goog.provide
in
...
E.parser = qa // this seems to come from CodeMirror
};
'use strict';
goog.provide('goog.events.EventWrapper');
goog.provide('goog.events.EventLike');
goog.provide('goog.debug.ErrorHandler'); /*
Copyright The Closure Library Authors.
SPDX-License-Identifier: Apache-2.0
*/
var CLOSURE_NO_DEPS = true;
var CLOSURE_BASE_PATH = 'js/cljs-runtime/';
var CLOSURE_DEFINES = {
'goog.DEBUG': false,
...
The error happens at ☝️ line 37141, but a little later at 37157 there is var goog = goog || {}
and at 37203 there is goog.provide = function ...
. If the three goog.provide weren’t there, or were moved down by some 100 lines, it would likely work.Sadly no, it doesn’t help.
Shadow 2.20.0 has the same problem
I am trying to troubleshoot https://clojurians.slack.com/archives/C015LCR9MHD/p1695985193245379 - with :simple
it does now work, but with :none
it does.
By “it” I mean the following shadow init fn:
(defn init []
(println "Init run")
(sci/eval-string* full-ctx "
(do
(ns test (:require
[com.fulcrologic.fulcro.dom :as dom]
[com.fulcrologic.fulcro.application :as app]
[com.fulcrologic.fulcro.components :as comp :refer [defsc]]))
(defonce app2 (do (println \"Def app!\") (app/fulcro-app)))
(comp/defsc Root [_ _] (dom/div (dom/h3 \"One\")))
(app/mount! app2 Root \"app\"))
")
(sci/eval-string* full-ctx "
(do
(ns test (:require
[com.fulcrologic.fulcro.dom :as dom]
[com.fulcrologic.fulcro.application :as app]
[com.fulcrologic.fulcro.components :as comp :refer [defsc]]))
(defonce app2 (do (println \"Def app!\") (app/fulcro-app)))
(comp/defsc Root [_ _] (dom/div (dom/h3 \"TWO\")))
(app/mount! app2 Root \"app\"))
")
)
In :none
, the dom with show the heading with TWO but with :simple
it will show OneNot sure if we should bug thheller with this @U0522TWDA
currently
:builds {:dev {:compiler-options {:output-feature-set :es8
:optimizations :simple
:source-map true}
:target :browser
:output-dir "www/js/dev"
;:asset-path "/js/dev"
:modules {:dev {:init-fn development/init}}
:devtools {:after-load development/reload}}}
and building with (clojure "-M:test:dev:shadow-cli release dev --debug")
I did, doesn’t seem to have any effect.
Michiel suggested that in my SCI macro I must be emitting something which is prone to renaming. But simple
only renames local vars and fn params, right? I wanted to try whitespace
to be sure whether it is renaming or st. else causing the problem…
yes, simple only renames locals. whitespace keeps all the other dev stuff, so it is not recommended or by my account supported
the goog is undefined stuff usually means that something was eval'd in the wrong scope
Then I will look at the variables and params in the functions to see which one could it be…
you also should keep :asset-path
, don't know why @U04V15CAJ recommended to remove it?
so I figured it was one less mis-configuration to worry about (in addition to the simple stuff which I know isn't support in shadow)
This is my config which works:
{:deps {:aliases [:test :dev]}
:nrepl {:port 9000}
:dev-http {8081 "www"}
:builds {:dev {#_#_:compiler-options {:output-feature-set :es8
:optimizations :none #_whitespace}
:target :browser
:output-dir "www/js"
#_#_:asset-path "/js/dev"
:modules {:dev {:init-fn development/init}}
:devtools {:after-load development/reload}}}}
<script src="js/dev.js" type="text/javascript"></script>
yes, this is using /js
as the output folder, then the :asset-path "/js"
default is correct. in the above config it was not. :asset-path "/js/dev"
in your config would have been incorrect
I just wanted to go with the defaults, not to conflate the issue any further. can add those asset path stuff back in later
FYI Michiel figured out that if we modify the script by renaming Root to Root2 in the second eval call, then it will work and correctly show the second app. So it looks like we can define & mount new classes, but not change existing ones, somehow, when optimizations are on. I need to dig into it…
Well, the whole adventure started with https://blog.jakubholy.net/2023/interactive-code-snipets-fulcro/#_demo - the point is having a code editor in a web page, and being able to change the code, reeval, and see the output change. The last part - seeing the effect - is broken with optimizations right now.
sorry, fixed link
Let me release a new version, which exposes it
if I name it to Root2
and change the mount arg accordingly it renders the "new" code fine
yeah, that's what I also figured :-) https://clojurians.slack.com/archives/C015LCR9MHD/p1695992591883249?thread_ts=1695985193.245379&cid=C015LCR9MHD
The problem is that for the editor to be really useful, I need to be able to change existing code/components 😭
The defsc macro is something I had to rewrite into a SCI fn so most likely I do st. wrong there…
(defonce ~(vary-meta sym assoc :doc doc :jsdoc ["@constructor"])
(comp/react-constructor (get options# :initLocalState)))
Thank you both ♾️ times! I will dig into the macro-fn thingy…
FYI https://blog.jakubholy.net/2023/interactive-code-snippets-fulcro/ now supports js/
shadow does this https://github.com/thheller/shadow-cljs/blob/cf5e87d48f4ca381e9c0521616f3b87da78de9db/src/main/shadow/build/cljs_hacks.cljc#L704-L712
ah nvm .. its not shadow-cljs doing the compilation so this has no effect whatsoever 😛
Thank you both so much, for spending your valuable time to help me out! I couldn’t do this on my own. Thank you, and have a great weekend!
Jakub:, when I add back the defonce:
(defonce ~(vary-meta sym assoc :doc doc :jsdoc ["@constructor"])
(do
(prn :redefine)
(comp/react-constructor (get options# :initLocalState))))
in non-advanced I do get to the the second component, although I see only once redefine printed. So there may be a bug elsewhere. Shall we take it back to the SCI channel, unless Thomas is interested in following? ;)Thank you! Yes, let’s do that. I have to leave now to arrange stuff, but will get back to it in the evening.
using --debug
output and rendering the same code through the normal CLJS compiler, I get:
var $options__47309__auto___58136$$ = new $cljs$core$PersistentArrayMap$$(null, 1, [$cljs$cst$2826$render$$, function($_$jscomp$693$$) {
return $com$fulcrologic$fulcro$components$wrapped_render$$($_$jscomp$693$$, function() {
$com$fulcrologic$fulcro$raw$components$props$$.$cljs$core$IFn$_invoke$arity$1$ ? $com$fulcrologic$fulcro$raw$components$props$$.$cljs$core$IFn$_invoke$arity$1$($_$jscomp$693$$) : $com$fulcrologic$fulcro$raw$components$props$$.call(null, $_$jscomp$693$$);
return $com$fulcrologic$fulcro$dom$macro_create_element$$.$cljs$core$IFn$_invoke$arity$3$("div", new $cljs$core$PersistentVector$$(null, 1, 5, $cljs$core$PersistentVector$EMPTY_NODE$$, [$com$fulcrologic$fulcro$dom$macro_create_element_STAR_$$(["h3", {}, "One"])], null), null);
});
}], null), $development$Root$$ = $com$fulcrologic$fulcro$components$react_constructor$$($cljs$core$get$$.$cljs$core$IFn$_invoke$arity$2$($options__47309__auto___58136$$, $cljs$cst$2816$initLocalState$$));
$com$fulcrologic$fulcro$components$configure_component_BANG_$$($development$Root$$, $cljs$cst$5218$development_SLASH_Root$$, $options__47309__auto___58136$$);
$cljs$core$prn$$.$cljs$core$IFn$_invoke$arity$variadic$($cljs$core$prim_seq$cljs$0core$0IFn$0_invoke$0arity$02$$([$development$Root$$]));
$com$fulcrologic$fulcro$application$mount_BANG_$$.$cljs$core$IFn$_invoke$arity$3$($development$app2$$, $development$Root$$, "app");
var $options__47309__auto___58137$$ = new $cljs$core$PersistentArrayMap$$(null, 1, [$cljs$cst$2826$render$$, function($_$jscomp$694$$) {
return $com$fulcrologic$fulcro$components$wrapped_render$$($_$jscomp$694$$, function() {
$com$fulcrologic$fulcro$raw$components$props$$.$cljs$core$IFn$_invoke$arity$1$ ? $com$fulcrologic$fulcro$raw$components$props$$.$cljs$core$IFn$_invoke$arity$1$($_$jscomp$694$$) : $com$fulcrologic$fulcro$raw$components$props$$.call(null, $_$jscomp$694$$);
return $com$fulcrologic$fulcro$dom$macro_create_element$$.$cljs$core$IFn$_invoke$arity$3$("div", new $cljs$core$PersistentVector$$(null, 1, 5, $cljs$core$PersistentVector$EMPTY_NODE$$, [$com$fulcrologic$fulcro$dom$macro_create_element_STAR_$$(["h3", {}, "TWO"])], null), null);
});
}], null);
$development$Root$$ = $com$fulcrologic$fulcro$components$react_constructor$$($cljs$core$get$$.$cljs$core$IFn$_invoke$arity$2$($options__47309__auto___58137$$, $cljs$cst$2816$initLocalState$$));
Note that we see $development$Root$$ =
twice which means Root is getting initialized twice. It seems defonce
is behaving a bit different in normal CLJS then which may explain the difference?which is probably due to CLJS hacks in shadow:
release builds will never overwrite a defonce, skip DCE-unfriendly verbose code
still a mystery why there is a difference between dev and release with SCI but I'm willing to let this detail slide now
although I don't get the comment in shadow: isn't it the case that a defonce is always redefined, instead of "never overwrite"?
hi! Is there a way of forcing an entire recompilation of my cljs project from shadow-ui, cljs repl or clj repl? even when no cljs file has been modified
yeah, so, recompiling all my files, same as when I restart the watch
I'm not sure about how the cache plays here, I was looking for a way of recompiling an entire project without restarting the watch, but that is independent of cljs files being modified or not
the reason I'm talking about cache is that there will no no compilation happening if no files were modified
when I click the Force Recompile of my build in the UI it returns immediately and doesn't recompile
yes, thats mostly there to trigger a hot-reload cycle. with no actual code compilation, besides "always compile" files
got it
so I have this dev compiler, ClojureScriptStorm, which is a fork of the cljs compiler that adds automatic instrumentation when it compiles anything. You can change what ns get instrumented by changing a clj var. So lets say I want to change what gets instrumented, I can change that var, but then I need to recompile everything, to bring the new instrumented/uninstrumented code the browser
does it make sense?
maybe you want to turn of caching entirely for that case? https://shadow-cljs.github.io/docs/UsersGuide.html#_compiler_cache
so shadow-cljs is still doing the compilation, just not using actual cljs.analyzer anymore? is that right?
interesting, I guess I can try cache-level :jar, which will always recompile my app but no other lib?
like some middle ground
or maybe leave normal cache an run a shell command that touches every cljs file? so it will force a full recompilation?
I guess you could use [:compiler-options :tooling-config]
or :external-config
? or however is all is controlled?
sorry, not sure I follow, you mean changing one of those in my shadow-cljs.edn will fire a full recompilation?
so, what I'm looking for is a way of recompiling all without restarting the watch. The compiler will emit different output depending on the contents of a global var (clj land). So I can go and change that var from the clj repl, but then I would like to re-compile all my app so I get the newly compiled code. Since this is a dev tool I would like to do all this interactively, as part of your dev flow, that is why I don't want to restart anything
recompile everything is a bit tricky, as reloading cljs.core
for example is not safe
also reloading any of the shadow.cljs.devtools things will cause a reconnect, which may confuse the REPL
yeah, got it, I was looking for recompiling my app stuff only
I can give you shadow-cljs specifc way of doing this, that basically abuses the hot-reload mechanism?
I think this made the trick find src -name "*cljs" -exec touch {} +
the main issue is that shadow-cljs is not aware of your CLJ var doing stuff to the compilation
but that find command did exactly what I need
so hacky as it gets but is an easy way of forcing only your app recompilation with one command
what do you think could go wrong with touching all my cljs files and make shadow recompile them?
I mean, this compiler is aware, because is a fork of the ClojureScript compiler, which looks at those vars when emitting code
but shadow-cljs is just calling the current cljs compiler
in this case ClojureScriptStorm
oh sorry
shadow-cljs when starting a build decides whether it can use the cache for a file or whether it needs to recompile
but again ... I don't know what you are doing .. so this is just a general warning about cache. there be dragons.
so, the only thing I'm doing is that I'm swapping the official Cljs compiler, by a fork that just emits some extra code when emitting js, that is it, think of like adding js/console.logs everywhere, no other magic. I understand that shadow-cljs will cache the compiled output, but if I have a simple way of touching every cljs file under my project, I can fire that whenever I want to be sure that everything is being recompiled
make sense?
again .. just a warning. its always weird to have "only works after I do this" behavior
should be fine but you are messing with shadow-cljs in ways it doesn't like to be messed with 😛
but you mean by running a touch on all cljs files?
I don't think it isn't messing with it in any other way
you are changing the cljs.analyzer and compiler under the hood, so I'd say that qualifies as messing with it 😉
but if you are careful everything should be fine. I'll just direct users your way if they run into problems 😉
well, yeah, BUT since shadow-cljs support is my main concern, all I'm touching is always after I'm sure it works with shadow-cljs
so, you can think of it as a shadow-cljs plugin thing at the compiler level
I'm aware of the shadow hacks ns, and stuff like that, so I'm designing the compiler with that in mind, and works great so far
> I'll just direct users your way if they run into problems 😉 hahah yeah, of course, I mean, is a thing with this dev compilers, same thing with ClojureStorm, I don't want people to go complaining with the Clojure team for a dev compiler error, but since swapping compilers is a keyword away, is very easy to know if an error is coming from the dev compiler or the original one
anyway, thanks for your time!
and is there a way of jumping that cache from the clj repl? some shadow fns?