How was the :bundle
target intended to be used? I have run into some interesting scenarios when interacting with the npm ecosystem and the :bundle
target (as have others in the community) so to help myself, and others like me, i'm looking for a little more info into the :bundle
target's intended use.
I successfully followed official cljs webpack guide and used the new :bundle target. Everything worked perfectly.
The issue came when I tried to use google's firebase npm package.
The idea is that the firebase
pacakge is large and you only want to include the modules you actually use. To do this, you can use either CommonJS
or ES6 Module
requires. Of course, if your using the :bundle target
you have to use CommonJS
. For firebase
, this looks like this:
var firebase = require('firebase/app');
require('firebase/auth');
Following the webpack guide
mentioned earlier, the equivalent CLJS require would look like this
(ns your.ns
(:require
["firebase/app" :as firebase]
["firebase/auth"]))
The firebase/app
and firebase/auth
require result in a compiler warning like:
No such namespace: firebase/auth, could not locate firebase_SLASH_auth.cljs...
This led me to explore the node_modules
directory to see what the structure of the firebase
repo looks like. My solution was
(ns your.ns
(:require
["@firebase/app" :as firebase]
["@firebase/auth"]))
The above now works 🎉.
Before I jump into the questions, I want to align on the benefits of the :bundle
tool:
- CLJS Libraries can confidently depend on node modules
- CLJS Apps can easily depend on node modules (less configuration + ceremony)
- CLJS compiled JS can be passed to any JS bundler (webpack, metro, rollup) or tool which understands CommonJS
- CLJS apps don't have to manually setup a
foreign library
or other similar configurations - Requires have to be done in CommonJS style
Which leads me to my questions which come from working through my experience outlined in the Background:
Answers so far are the ones sourced from the clojure community combined with my take.
-
When would a user not want to use the
:bundle
target?- unknown at this point.
-
Does the CLJS compiled JS go through optimizations before it gets to webpack?
- Yes. When you set your build to release mode it will be go through advanced optimizations before it gets to webpack.
-
Would it be fair to say that there are times when we would need a
foreign library
, like how the older webpack guide worked, in combination with the:bundle
target?- unknown at this point. However, it might be valuable to note that you could declare a foreign-lib and the
:bundle
command if there were scenarios where:bundle
was not cutting it. This is speculative. Not sure If needed.
- unknown at this point. However, it might be valuable to note that you could declare a foreign-lib and the
NOTE: please know that I understand that shadow-cljs has a sweet way of handling this. My questions are driven from an interest in understanding how one could successfully navigate this scenario using a more basic toolset.
The debug technique I used was to build a quick vanilla JS project. Setup webpack and require things in the way that :bundle
wants us to. When this is done, you can take a look at how webpack requires the files which can provide insight into what you could do to make your require work.
Handling the object returned by your require...
You may see a lot of warnings in the console. you can resolve these by enabling sourcemaps in webpack: devtool: "eval-cheap-source-map"
Uncaught SyntaxError: Cannot use import statement outside a module
The above is a message you might see in the browser console. This means that you are referencing the output of the CLJS compiler as opposed to the output of webpack. Update the script tag in your HTML
with the webpack setup your going to get a lot of noise in the browser console. avoid this by setting webpack config to enable sourcemap.
- foreign libs makes the dev flow more cumbersome
- stop, update index.js, update all build files, rebuild webpack file, start system again
- we can automate this with a bb task perhaps.
- foreign libs means you can have smaller bundle sizes
- These are like fully namespaces
window.module.other-name = Player
You might want the above to "protect" your extern, but this would not be accessible as
[module :as m]