Fork me on GitHub
#shadow-cljs
<
2023-10-26
>
kenny01:10:12

Hi 👋:skin-tone-2: I am working on a cljs project and attempting to upgrade the version of @clerk/clerk-react from 4.26.6 to the next version 4.27.0. Typically, upgrades for this package have been seamless. After upgrading to 4.27.0, I receive the following message after running shadow’s watch.

The required JS dependency "@clerk/shared/keys" is not available, it was required by "node_modules/@clerk/clerk-react/dist/cjs/contexts/ClerkProvider.js".
Taking a look at the release notes, I see they upgrade to a new, breaking version of their @clerk/shared package 1.0.0. As a result, code changes for this keys dependency change. e.g.,
-import { createDevOrStagingUrlCache } from '@clerk/shared';
+import { createDevOrStagingUrlCache } from '@clerk/shared/keys';
This was executed in https://github.com/clerkinc/javascript/pull/1915 of the @clerk/shared package: “The package was reworked to allow for better isomorphic use cases and ESM support, resulting in some breaking changes. It now allows for https://nodejs.org/api/packages.html#subpath-exports and restricts some imports to specific subpaths.” Does shadow-cljs support subpath exports? Is there a way I can workaround this? Or maybe this is actually an upstream misconfiguration?

thheller06:10:21

subpath exports are supported, but were a pretty recent addition. maybe you are on an older shadow-cljs version?

kenny22:10:13

Sorry for the delay — taking a look now. I have updated to the most recent version 2.25.10. I’m stopped with a new error, so I do not know if the version update fixed it. The new error occurs after running watch:

[build-cljs:dev] Failed to inspect file
[build-cljs:dev]   /Users/kenny/work/kwllc/deeporigin/data-mgmt/projects/ui/node_modules/@tanstack/query-core/build/modern/focusManager.cjs
[build-cljs:dev]
[build-cljs:dev] Errors encountered while trying to parse file
[build-cljs:dev]   /Users/kenny/work/kwllc/deeporigin/data-mgmt/projects/ui/node_modules/@tanstack/query-core/build/modern/focusManager.cjs
[build-cljs:dev]   {:line 30, :column 2, :message "'}' expected"}
That is pointing to a file in the react-query library. The line it’s pointing to looks like this.
#focused;
And here it is with more context:
var FocusManager = class extends import_subscribable.Subscribable {
  #focused;
  #cleanup;
  #setup;
  constructor() {
    super();
...

thheller07:10:25

those are unfortunately still not supported by the closure compiler https://github.com/google/closure-compiler/issues/2731

thheller07:10:38

unfortunately nothing I can do about that from the shadow-cljs side. you can use webpack (or any other JS bundler) via https://shadow-cljs.github.io/docs/UsersGuide.html#js-provider-external

kenny08:10:26

Ah got it, ok. Given the upcoming maintenance mode of the closure compiler, do you think it’s pretty unlikely this will get in?

thheller08:10:13

the closure library is going into maintenance mode

thheller08:10:19

I don't think the closure compiler is. that would surprise me.

kenny19:10:52

Ah, phew - ok. Thanks

kenny19:10:35

Hmm curious - this library provides a “modern” and “legacy” build. Would I be able to use the “legacy” build for just this library? It seems to not use the class field syntax.

kenny19:10:25

The focusManager code snippet looks like this in the legacy build.

var import_utils = require("./utils.cjs");
var _focused, _cleanup, _setup;
var FocusManager = class extends import_subscribable.Subscribable {
  constructor() {
    super();
    __privateAdd(this, _focused, void 0);
    __privateAdd(this, _cleanup, void 0);
    __privateAdd(this, _setup, void 0);
    __privateSet(this, _setup, (onFocus) => {

thheller19:10:54

well ... the package appears to be using "exports" for the modern version

thheller19:10:11

so you could turn of exports, which in turn breaks the other package again 😛

thheller19:10:41

so no, not really. apart from manually changing that package.json of course

thheller19:10:25

change line 29 to build/legacy/index.cjs and it might work

kenny19:10:45

It feels naughty to modify something in node_modules 😅

kenny19:10:50

It seems like the webpack workaround is fairly straightforward. Thank you for the clear writings on it 🙂

kenny19:10:46

I’m somewhat surprised I’ve never run into this in the many years of cljs work. I suppose it’s somewhat new. Are folks tending to transition to it now? Or did I just get “lucky” with this tanstack library?

thheller19:10:38

its come up a couple of times recently. for whatever reason the JS world jumps on any new syntax feature as soon as webpack supports it

thheller19:10:47

even it the standards aren't even out or finished yet

thheller19:10:15

the more trendy the library the more cutting edge the code 😛

😩 1
kenny19:10:30

Circling back - I’m writing an internal note about this issue since I’ll likely tackle in in the next couple weeks. Any guess on why upgrading shadow from 2.23.3 to 2.25.10 causes the class field error? i.e., why wasn’t that error thrown before?

thheller19:10:25

the older shadow-cljs didn't support "exports", thus it took the legacy version of the tanstack thing

✔️ 1
thheller19:10:49

I mean I guess I could add an option to :ignore-exports #{"tanstack"} or something to ignore it selectively, but not too sure thats a good idea

thheller20:10:22

"exports" is another thing that is getting quite more common and relied upon

kenny20:10:51

This is also a relatively new feature, right?

thheller20:10:15

hmm not that new anymore no, been a couple years now

thheller20:10:34

not many packages used it though, so didn't appear often until recently

kenny20:10:52

Did webpack recently add support for it? 😄

thheller20:10:26

I think it was like 2020 or so

thheller20:10:44

whenever webpack5 was released

kenny20:10:57

Hmm. I was wondering if the two were linked — increased use of subpath exports means more likely more code to use the class field syntax, haha

thheller20:10:24

exports kinda make sense, although its a total mess

thheller20:10:40

class fields, or especially the "private" syntax it total nonsense

thheller20:10:06

looks stupid and breaks every parser. gotta love the JS world for breaking changes

thheller20:10:29

convention for private used to be _foo, now its #foo. totally worth breaking the syntax for that

😭 1
kenny20:10:27

Do they write justification on why a change like that is worth making that tradeoff?

thheller20:10:35

reasons are not part of the standard process I think 😛

thheller20:10:40

well some blame is also on the closure compiler folks. I don't get why just parsing the # and discarding it is not a valid path forward 😛

thheller20:10:01

don't know why this is taking so long to implement

borkdude18:10:57

I have a custom defmacro macro which I'm bringing in like this:

(ns sci.configs.hoplon.javelin
  (:refer-clojure :exclude [dosync defmacro])
  (:require [sci.core :as sci]
            [clojure.set]
            [sci.ctx-store :as ctx-store]
            [clojure.walk :refer [prewalk]]
            [cljs.pprint :as p]
            [clojure.string :as str]
            [javelin.core])
  (:require-macros [sci.configs.macros :as m :refer [defmacro]]
                   [sci.configs.hoplon.javelin :refer [with-let*]]))
It works in vanilla ClojureScript but in shadow-cljs (latest) it doesn't seem to be called

borkdude18:10:00

For now I can just rename it to something else

borkdude18:10:11

but then I have to touch lots of source code

thheller18:10:16

I guess I didn't account for anyone making their own defmacro? 😛

borkdude18:10:17

no worries, I can work around it ;)

thheller18:10:47

happy to make that smarter and respect :refer-clojure :exclude

👍 1
borkdude18:10:02

yeah I think logging an issue and making it more robust makes sense

borkdude18:10:07

shall I log one?

thheller18:10:11

out of curiosity though. what does this do that makes it relevant to be called in CLJS?

thheller18:10:13

I mean since macros run in their own world, keeping defmacro in the regular CLJS world serves no purpose other than creating bugs

thheller18:10:57

or is this not related to what actual defmacro is and something entirely different?

borkdude18:10:53

it defines a SCI macro

borkdude18:10:13

(defn add-macro-args [[args body]]
  (list (into '[&form &env] args) body))

(clojure.core/defmacro defmacro [name & body]
  (let [[?doc body] (if (and (string? (first body))
                            (> (count body) 2))
                      [(first body) (rest body)]
                      [nil body])
        bodies (if (vector? (first body))
                 (list body)
                 body)]
    `(defn ~(vary-meta name assoc :sci/macro true)
       ~@(when ?doc [?doc])
       ~@(map add-macro-args bodies))))

borkdude18:10:35

So it keeps the macro around as a normal function with two extra arguments and I can more or less just copy existing macros to a .cljs file which then work in SCI