I noticed that shadow moved cljs constants table into a private Java package, so no way to extend constants table from Clojure?
there is no such thing as a constants table in shadow-cljs?
there is a closure compiler pass that basically achieves the same end result, but there is no equivalent to the cljs constant-table thing
maybe it's that, but anyway, in cljs it's possible to write into constants table from a macro, seems like it's not possible with shadow? https://github.com/thheller/shadow-cljs/blob/master/src/main/shadow/build/closure/ReplaceCLJSConstants.java
I do not know what that would do?
Shadow / Closure seems to be adding some goog.provide calls in the output JS before goog itself is setup. This only happens in release. Details in ๐งต
Here's the first bit of shared.js. Those provide calls look out of place to me:
var shadow$provide = {};
var $APP = {};
(function() {
'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,
"goog.LOCALE": "en",
"goog.TRANSPILE": "never",
"goog.ENABLE_DEBUG_LOADER": false
};
var COMPILED = false;
var goog = goog || {};
goog.global = this || self;
goog.global.CLOSURE_UNCOMPILED_DEFINES;
goog.global.CLOSURE_DEFINES;shadow-cljs.edn:
{:deps true
:builds {:frontend {:target :browser
:modules {:shared {:entries []}
:main {:init-fn my.project.main/main
:depends-on #{:shared}}
:a {:init-fn my.project.main-a/main
:depends-on #{:shared}}
:b {:init-fn my.project.main-b/main
:depends-on #{:shared}}}
:devtools {:watch-dir "../dist/"}
:compiler-options {:optimizations :whitespace ;;TODO: get advanced working?
:infer-externs false
:externs ["datascript/externs.js"]
:warnings {:extending-base-js-type false
:fn-deprecated false
;;ignore warning from loom/alg_generic.cljc:494:19
:invalid-arithmetic false}}
:output-dir "../dist/js/"
:asset-path "js"}}}
deps.edn:
{:paths ["src/"]
:deps {org.clojure/clojure {:mvn/version "1.12.0"}
org.clojure/core.match {:mvn/version "1.0.1"}
org.clojure/core.async {:mvn/version "1.7.701"}
fipp/fipp {:mvn/version "0.6.24"}
lonocloud/synthread {:mvn/version "1.0.4"}
no.cjohansen/replicant {:mvn/version "2025.02.02"}
org.suskalo/coffi {:mvn/version "1.0.486"}
cheshire/cheshire {:mvn/version "5.13.0"}
datascript/datascript {:mvn/version "1.7.3"}
com.rpl/specter {:mvn/version "1.1.4"}
instaparse/instaparse {:mvn/version "1.5.0"}
aysylu/loom {:mvn/version "1.0.2"}
com.taoensso/tufte {:mvn/version "2.6.3"}}
:aliases {:shadow-cljs {:extra-deps {thheller/shadow-cljs {:mvn/version "2.28.20"}
binaryage/devtools {:mvn/version "1.0.7"}
cider/cider-nrepl {:mvn/version "0.50.2"}}
:main-opts ["-m" "shadow.cljs.devtools.cli"]}}}The shared.js was built with clojure -M:shadow-cljs release frontend
This is from a clean build too. I'm running rm -rf dist/js/ before I run shadow.
Interestingly, I get different errors when :simple optimizations are used
:whitespace is not supported. use :simple
Ah, I didn't realize that. Want me to open a PR that throws an error if :whitespace is used?
and you might need to add :compiler-options {:output-wrapper false} when using multiple modules (and :simple)
Ah, that did the trick, thanks!
Would you like me to do a separate PR for this situation as well? Two behaviors I can think of:
โข Shadow should set output-wrapper false if there are multiple modules and simple optimizations, warning/erroring if the explicit config had it set otherwise
โข Just always error, so that the user is forced to set it
Thanks for all of your work on shadow-cljs, btw. I've been using ClojureScript since 2012 and your tooling makes everything so much better!
output wrapper is a bit tricky, since generally it shouldn't be a problem. I'm not entirely sure :simple would always require it
throwing for :whitespace would be fine
no need for a PR though, fixed it https://github.com/thheller/shadow-cljs/commit/e73b2666506362532227606ae3b8ad81e1ef1454
Sweet! I don't think I'd ever have figured out the output wrapper thing myself, though, and so if possible I'd love to sand down that rough edge somehow. I always get such whiplash from enjoying the cljs dev experience, then having to debug weirdness when it comes to making a production build. Would love to save other people those, uh, adventures.
just go with :advanced, that is definitely the most common way and has the most information about it. I wouldn't recommend :simple for any browser targeted build
just remove :infer-externs false and fix the warnings ๐
Aight, challenge accepted. I'll make myself lunch, harden my soul, and set a 1 hour timer. Wish me luck =D
feel free to ask if you have questions/problems
If you are ever around Amsterdam, btw, drop me a line so I can buy you a coffee/beer/lunch.
Any tips for how to debug some inference errors that show up in my main project but don't show up in a minimal reproduction project? Both have the same versions of shadow and core.async. Here's an example: This namespace compiles fine in the minimal project:
(ns repro.main
(:require [cljs.core.async :refer [go <! chan]]
[cljs.core.async.interop :refer [<p!]]))
(defonce !tray-icon (atom nil))
(def Menu
(aget js/window "__TAURI__" "menu" "Menu" "new"))
(defn update-tray-menu!
[]
(go
(let [^js tray-icon @!tray-icon
menu (<p! (Menu (clj->js {:items []})))]
(.setMenu tray-icon menu))))
(defn main
[]
(update-tray-menu!))
but if I take the exact same code and put it in a namespace in my full project (and reference the main fn, so it doesn't get optimized out), I get an inference warning:
------ WARNING #1 - :infer-warning ---------------------------------------------
File: ...foo.cljs:14:3
--------------------------------------------------------------------------------
11 |
12 | (defn update-tray-menu!
13 | []
14 | (go
---------^----------------------------------------------------------------------
Cannot infer target type in expression (. inst_37022 (setMenu inst_37044))
--------------------------------------------------------------------------------
15 | (let [^js tray-icon @!tray-icon
16 | menu (<p! (Menu (clj->js {:items []})))]
17 | (.setMenu tray-icon menu))))
18 |
--------------------------------------------------------------------------------go unfortunately notorious for loosing type hint info. so typehints in there are rather unreliable
hah, good timing I just finished dinner and just now threw together a minimal repro: https://github.com/lynaghk/repro-cljs-go-unreachable
FWIW you shouldn't be using aget there
doh, actually this repro is for a separate issue I ran into about "unreachable" warning. not related to type hints as far as I know
dunno what that is but yeah avoid go as much as possible ๐
what's the appropriate alternative? nested property access like
(def Menu
(-> js/window
.-__TAURI__
.-menu
.-Menu
.-new))not sure what the new is doing there, but just js/__TAURI__.menu.Menu is fine
if that is supposed to be a constructor js/__TAURI__.menu.Menu.
they have a method called new on the object https://v2.tauri.app/learn/window-menu/
> js/__TAURI__.menu.Menu
Ah, so it's okay to do dotted property access like this? I couldn't remember if that was allowed or just a "happens to work sometimes" thing.
ah ok, looks like a regular function then
yes, this dotted property access is fine. externs are generated for all properties
yeah, I did a double take myself when I was first writing the code
externs are generated for all js/ forms
Aight, well I switched to the dotted property access but have the same type hint warning. Lifting it out of the go block resolves the problem, so I guess I'll rewrite to use explicit promise callbacks and open an issue with cljs / core.async and/or settle down for some quality time with the macroexpansion to see if I can track down the bug myself.
the problem has been around for a while, not sure anyone ever looked into it. I don't really use core.async for frontend stuff so dunno