Skip to content

Instantly share code, notes, and snippets.

@athomasoriginal
Last active November 7, 2021 14:38
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save athomasoriginal/fd00691bde0be8d9a52c33f3ca968bdc to your computer and use it in GitHub Desktop.
Save athomasoriginal/fd00691bde0be8d9a52c33f3ca968bdc to your computer and use it in GitHub Desktop.

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.

Background

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 🎉.

Questions

Before I jump into the questions, I want to align on the benefits of the :bundle tool:

  1. CLJS Libraries can confidently depend on node modules
  2. CLJS Apps can easily depend on node modules (less configuration + ceremony)
  3. CLJS compiled JS can be passed to any JS bundler (webpack, metro, rollup) or tool which understands CommonJS
  4. CLJS apps don't have to manually setup a foreign library or other similar configurations
  5. 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.

  1. When would a user not want to use the :bundle target?

    • unknown at this point.
  2. 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.
  3. 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.

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.

CommonJS Import Tips

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.

Pros and Cons of each approach

  • 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

Foreign Lib Notes

  • 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]

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment