Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

With WebPack, how do I create ready to use "split" bundles with one depending on the other?

My question is very close to others which' answers I believe still require another WebPack-step which I want to avoid. But here is the story first:

I have a Node module (let's call it libfoo) which provides some functionality and requires some third party modules, and a small script main.js which provides the main entry point and requires libfoo:

main.js:

const foo = require('foo');
function main() {
    foo.bar();
}
main();

I now want to turn libfoo and main.js into browser executable deliverables using WebPack. And I want libfoo (which is quite large) to reside statically on the target systems while main.js is very small and changes quickly (just imagine a test scenario where libfoo is a module I want to test and main.js contains changing code snippets)

I managed to create two packages - let's call them foo.browser.js and main.browser.js - which contain all the needed functionality, but I can't manage to make main.browser.js correctly import foo.browser.js.

I'm not very into WebPack yet - and up to now I couldn't figure out what's happening. My current approach looks like this: I build foo.browser.js by running the following command:

webpack --output-filename foo.browser.js foo.js

And I have a webpack.config.js for main.js which looks like this:

module.exports = {
  externals: {'foo': 'foo'},  // don't know what I'm doing here - added `commonjs` and `root` randomly
}

I turn main.js into main.browser.js with a very similar command: webpack --output-filename main.browser.js main.js

Now I try to use those both files in file called foo.html containing these lines:

<script src="dist/foo.browser.js"></script>
<script src="dist/main.browser.js"></script>

But when I now open foo.html in a browser I get

external "foo":1 Uncaught ReferenceError: foo is not defined
    at Object.foo (external "foo":1)
    at __webpack_require__ (bootstrap:19)  
    at Object../main.js (main.js:3)  
    at __webpack_require__ (bootstrap:19)
    at bootstrap:83
    at bootstrap:83

I fiddled around a little but (only randomly I'm afraid) but with no luck.

There is one constraint in my scenario which might be a difference to the other (working) examples I found: I need foo.browser.js and main.browser.js to be "final" i.E. they must run on the target system without any further postprocessing (like running WebPack again to turn them into a single bundle).

like image 210
frans Avatar asked Mar 24 '19 20:03

frans


1 Answers

You can do it with this type of configuration:

module.exports = [{
  resolve: {
    modules: ["."],
  },
  entry: {
    "foo": "foo.js",
  },
  output: {
    path: `${__dirname}/build`,
    filename: "[name].js",
    sourceMapFilename: "[name].js.map",
    library: "foo",
    // libraryTarget: "umd",
  }
},{
  resolve: {
    modules: ["."],
  },
  entry: {
    "main": "main.js",
  },
  externals: {
    "foo": "foo",
  },
  output: {
    path: `${__dirname}/build`,
    filename: "[name].js",
    sourceMapFilename: "[name].js.map",
  }
}];

This will produce two bundles in the build/ subdirectory. The key to get main to use foo is:

  1. The "foo": "foo" entry in externals for creating the main bundle. Whenever main requests foo it looks for it externally in a "module" named foo. I've put "module" into quotes because when you have bundles in the UMD format and you load them with script, there's no module system. Instead of looking for an actual module, the code will look for a global variable named foo.

  2. The foo bundle exports itself into the global space as the variable foo, which allows it to be used by main.

like image 197
Louis Avatar answered Oct 16 '22 23:10

Louis