shadow-cljs

oxalorg (Mitesh) 2025-02-28T09:59:03.050869Z

Hey folks, I'm working on a chrome-extension (built with shadow-cljs) which loads on every website the user visits. Some of our clients are reporting that it breaks Google docs completely (and disabling our extension fixes it), and I can't get any meaningful errors from them. I can't reproduce it either. Is there a possibility that closure / goog.global loaded on http://docs.google.com is somehow interfering the with the closure build we inject on the page as a part of the extension? πŸ€”

Karol WΓ³jcik 2025-02-28T10:32:38.188989Z

I had something similar when my namespace was called β€œcore”. It was then clashing with some β€œcore” package which was polyfilling window object.

1
thheller 2025-02-28T14:12:36.734839Z

uhm define "loads on every website"? usually when you inject code it runs in its own context. isolated to prevent exactly this problem?

thheller 2025-02-28T14:13:29.507999Z

but yes, it is totally possible that the code will conflict with something is injected into the same global context

thheller 2025-02-28T14:14:25.970879Z

chrome.scripting.executeScript is supposed to be safe against such conflicts? do you use that or some other method?

thheller 2025-02-28T14:22:21.902189Z

hmm or I'm confusing it with content scripts. been a while since I looked at any of this

oxalorg (Mitesh) 2025-02-28T14:25:02.283199Z

Thanks @thheller for looking into this. We load content script on every tab / webpage the user visits. This content script inturn loads an extra script which creates a "sidebar". This is injected directly into the dom, so it's one with the host page (no chrome.runtime access but full DOM access). This is roughly what we do:

// The sidebar always runs in the execution context of the page, rather than in the extension's isolated world
const sidebarScript = document.createElement('script')
sidebarScript.setAttribute("type", "module")
sidebarScript.setAttribute("src", chrome.runtime.getURL('js/sidebar/main.js'))

thheller 2025-02-28T14:25:37.124769Z

so its a :target :esm build?

βœ… 1
oxalorg (Mitesh) 2025-02-28T14:26:55.195479Z

It might be worthwhile to try and use chrome.scripting.executeScript to see if we can inject this in, but then I am not sure if we will have full dom access think_beret

:sidebar
  {:target      :esm
   :output-dir  "ext/js/sidebar"
   :modules     {:main {:init-fn chrome-ext.sidebar/init
                        :entries [chrome-ext.cli]}}
   :closure-defines {chrome-ext.build-info/BUILD_ID "sidebar"
                     chrome-ext.build-info/PRIVILEGED false}
   :release {:closure-defines {lambdaisland.glogi.console/colorize "false"}}
   :dev {:closure-defines
         {goog.DEBUG true}}}

thheller 2025-02-28T14:28:22.235029Z

ESM should be fully isolated and not clash with anything else but there are some thing that might modify the global scope

thheller 2025-02-28T14:28:31.111809Z

for example using ^:export, which would create the chrome_ext.sidebar.whatever "global", which may clash with something else

πŸ€” 1
thheller 2025-02-28T14:29:58.691549Z

it could also just be a different browser extension that just doesn't expect your sidebar to be there and then causes the actual breakage?

thheller 2025-02-28T14:31:30.740739Z

^:export is pretty unlikely to cause problems, but who knows whats going on in the global scope πŸ˜›

πŸ™ˆ 1
thheller 2025-02-28T14:32:26.758719Z

I'm assuming here we are talking about a release build. watch/compile I could easily see causing a whole bunch of issues πŸ˜›

βœ… 1
thheller 2025-02-28T14:34:11.116159Z

basically one of the first lines in any closure build is var goog = goog || {};, it could just be that http://docs.google.com already has a non isolated goog defined. so everything ends up living on the same object. that will for sure cause issues

πŸ€” 1
thheller 2025-02-28T14:34:58.621349Z

but I get Uncaught ReferenceError: goog is not defined if I tests on a google docs page. so that doesn't seem be the case

oxalorg (Mitesh) 2025-02-28T14:35:20.191409Z

I can't find any ^:exports in the codebase. I've isolated it down to only our extension (all other uninstalled / disabled by the clients who have this issue). This was the error I was able to get from a client who had this issue, and this was the only log/error in console:

Error: VD`CustomError: Error in protected function: Deferred has already fired at D.onError () at tKe () at sL () at 

oxalorg (Mitesh) 2025-02-28T14:35:37.951269Z

Both of these scripts don't have a global goog. Yup google docs has no global goog

oxalorg (Mitesh) 2025-02-28T14:37:22.901299Z

@thheller Thanks for helping me investigate this, I'll try to pursue this further and see what I can find! For the time being we might just disable our extension on google docs page.

thheller 2025-02-28T14:37:27.935409Z

yeah :advanced typically renames it, so it could just be a different var name that happens to clash. but it would have to be put onto globalThis explicitely. don't know why anyone would do that πŸ˜›

πŸ’― 1
1
oxalorg (Mitesh) 2025-02-28T14:37:54.978099Z

Thanks a lot, really appreciate it 🌸 🫑

thheller 2025-02-28T14:38:39.957529Z

you could try a npx shadow-cljs release sidebar --pseudo-names build

πŸ€” 1
thheller 2025-02-28T14:39:07.346569Z

that uses very long names, which reduces the chances of actually clashing with anything. just for debugging that might be useful

thheller 2025-02-28T14:39:37.362779Z

if the problem goes away that would at least confirm its a JS name clash, not something else

oxalorg (Mitesh) 2025-02-28T14:40:16.349459Z

That's a great idea 🀩! I'll try to debug it this way

thheller 2025-02-28T14:40:34.850039Z

but the build will be huge, so don't use that for anything other than debugging aid

πŸ’― 1
🫑 1
oxalorg (Mitesh) 2025-02-28T14:41:02.033889Z

Gotcha! Let me try it now πŸ˜„

Kari Marttila 2025-02-28T17:58:29.885289Z

Is there some way in shadow-cljs to pass some configuration info to the app so that it knows whether it is running :dev or :release mode? I am reading https://shadow-cljs.github.io/docs/UsersGuide.html and experimenting with build-hooks. I can print the (prn "Build mode: " (:shadow.build/mode build-state)) ; => :dev in the build-hook, but I have no idea, how to store that to my app config (or whether it is event meant for that). Is goog.DEBUG the only viable way in the running frontend app to know if it is running in :dev or in :release ? I'd like to have some mechanism which I could store that info to my cljc file (and not in cljs).

Kari Marttila 2025-02-28T17:59:33.007439Z

I am not that experienced frontend developer, so the question might be a bit stupid. πŸ™‚

thheller 2025-02-28T18:06:35.208629Z

build hooks are not meant for something like this

thheller 2025-02-28T18:07:16.502739Z

goog.DEBUG is also just an implicit closure define variable, but you can just setup your own wherever you want

thheller 2025-02-28T18:08:49.272279Z

macros can also get the :shadow.build/mode from their &env if you must, but the closure define way is preferred

Kari Marttila 2025-02-28T18:28:50.393229Z

Thanks! ❀️

cormacc 2025-02-28T18:29:12.826059Z

Anyone have experience including plotly.js in a shadow project? I'm getting the following when adding any version (well, have tried 3 -- the current 3.0.1, and 2.35.2 and 2.14.0)....

Failed to inspect file
  /home/cormacc/nmd/products/connect/portal/node_modules/maplibre-gl/dist/maplibre-gl.css

Errors encountered while trying to parse file
  /home/cormacc/nmd/products/connect/portal/node_modules/maplibre-gl/dist/maplibre-gl.css
  {:line 1, :column 1, :message "primary expression expected"}

cormacc 2025-03-12T12:14:45.549699Z

As a signpost to future travellers down this path... I had a facepalm moment after realising that using the the '-dist' variants of the plotly packages from npm eliminate this problem. So use 'plotly.js-dist' or 'plotly.js-dist-min' rather than plain 'plotly.js' in your package.json and require statements. On the plus side the ever-excellent shadow-cljs documentation helped me get js-processing with webpack working very easily, and good to know how it's done.

thheller 2025-03-12T14:25:25.596999Z

FWIW going with just the https://github.com/plotly/plotly.js?tab=readme-ov-file#load-via-script-tag from the docs is also valid, so just and (js/Plotly...)

thheller 2025-03-12T14:26:38.202279Z

then its not part of the build at all, so can't cause any trouble πŸ˜‰

cormacc 2025-03-12T21:13:34.055259Z

Yeah before my 'dist' revelation I'd set my dev build to use the CDN for convenience and a placeholder release build to use the split/webpack setup. Working within medical regulatory constraints so being able to package up all the deps at build time is a small win from the perspective of regulatory accountancy

thheller 2025-02-28T18:30:27.496379Z

shadow does not support processing css, so this won't work

thheller 2025-02-28T18:31:04.433359Z

you can offload the JS processing to webpack via https://shadow-cljs.github.io/docs/UsersGuide.html#js-provider-external

cormacc 2025-02-28T18:31:35.573429Z

Great thanks - I'll take a look at that