bhauman / figwheel-main

Figwheel Main provides tooling for developing ClojureScript applications
https://figwheel.org
Eclipse Public License 1.0
640 stars 93 forks source link

Use code-splitting and npm bundling together with figwheel-main #308

Open b-boiko opened 2 years ago

b-boiko commented 2 years ago

I have a number of clojurescript applications that already use npm bundling, but as they grow in size I want to see if I can use code splitting as well. I ran through the demo on code splitting and had no issues there. But when I added react dependencies with npm bundling I was running into issues. I tried arranging my dev.cljs.edn in a few different ways, but it seems when I add a :target to the config the modules get ignored entirely.

My first attempt was to just add the bundling options:

^{:watch-dirs ["src"]
  :open-url   false}
{:output-dir    "resources/public/cljs-out"
 :modules       {:foo {:entries #{foo.core}}
                 :bar {:entries #{bar.core}}}
 :target        :bundle
 :bundle-cmd    {:none ["yarn" "webpack" "--mode=development" "--entry"
                        :output-to
                        "--output-path" :final-output-dir
                        "--output-filename" :final-output-filename]}
 :bundle-freq   :smart
 :optimizations :none}

This results in the following error:

Execution error (NullPointerException) at figwheel.main/append-to-filename-before-ext (main.cljc:1273).
null

Full report at:
/tmp/clojure-4968800853018646518.edn

I realize the issue is that using :modules ignores the top-level :output-to so I suppose when combining it with :bundle-cmd the various configurations. I tried to merge the bundle configs in with each individual module on a random whim, but nothing happened.

Next I tried removing the bundling and leave just the code splitting configs when I built the resources. I tried to run the bundling separately from the terminal. I set up my webpack.config.js like so:

const path = require('path');

module.exports = {
  mode: 'development',
  entry: {
    cljs_base: './resources/public/cljs-out/cljs_base.js',
    foo: './resources/public/cljs-out/foo.js',
    bar: './resources/public/cljs-out/bar.js'
  },
  output: {
    filename: '[name]_bundle.js',
    path: path.resolve(__dirname, 'resources/public/cljs-out')
  }
};

I thought perhaps it would work if I bundled each outputted moduled with the npm dependencies, and while it seems to work the app itself is still unable to resolve any npm modules when I run it.

Ideally, when setting both :modules and :target the resulting modules would be individually compiled and bundled, but there are probably more complicated things to consider I am unaware of.

bhauman commented 2 years ago

One recommendation here would be that you can create a build script to do what you want it to do and hard code the file names and call that build script from the bundle-cmd. You would need to bundle twice for the example above. I've never don't this so I'm speculating here.

Another idea to consider is that you can have two completely separate configurations for dev and prod. You can do the code splitting in prod and have a much simpler config for dev.

A different entry namespace for dev and prod can simplify a lot of this stuff for you.