How do I include npm modules in webpack DLLs?

EDIT:

Originally I only made the given example work but had no real idea how everything was supposed to be used. I wrote this (new content below the block):


Ok, so I think I got at least a part of it. I will tell you what you have to do to make your example work.

There are two ways to make it work:

  1. Remove preact from the package.json (and node_modules if necessary) of the containing folder i.e. the top-level. Now you have preact only in the dll folder.
    Then change the require call in example.js within the dll-user folder to
    require("../dll/node_modules/preact")
    That should work but is not exactly what we want.

  2. Now the other way around. Remove preact from the dll folder but install it only to the containing folder.
    Run both of the build scripts and see that in output.js is everything delegated as it should including preact.


New:

Ok, so after a bit more poking around here is how I think it works. (Since we know each other and sort-of work together fewer words would do but I think this might also help others if I am a bit more explicit on details, so bear with me.)

Preliminary remarks: I assume you want to create a dll file that you 1) can install into the project with npm and 2) somehow include with a separate script tag into your HTML. That script creates on execution a global variable that exposes a function which in turn is used by your application script to resolve dependencies. Further, I assume that you have already a directory for the dll bundle set up with only a package.json and webpack ready installed.

First you create a webpack.config.js like this:

var webpack = require("webpack");
var path = require("path");

module.exports= {
  entry: ["preact"], // put here every module that goes into the dll
  output: {
    path: __dirname,
    filename: "index.js",
    library: "[name]_[hash]"
  },
  plugins: [
    new webpack.DllPlugin({
      path: path.join(__dirname, "[name]-manifest.json"),
      name: "[name]_[hash]" // (keep consistent with output.library)
    })
  ]
};

Now create the bundle and its manifest with

$ webpack

The structure of the dll project folder is now:

dll-project
|_ node_modules
| |_ preact
|_ main.js
|_ main-mainifest.json
|_ package.json
|_ webpack.config.js

Now you have installed this package into another project, your app:

app
|_ node_modules
| |_ dll-project
|_ index.js
|_ package.json
|_ webpack.config.js

This webpack.config.js looks like (or similar to) this:

var webpack = require("webpack");

module.exports= {
  entry: "./index.js",
  output: {
    path: __dirname,
    filename: "app.js"
  },
  plugins: [
    new webpack.DllReferencePlugin({
      scope: mydll,
      manifest: require("./node_modules/dll-project/main-manifest.json")
    })
  ]
};

In your index.js that is, your app code, you require modules which are in the dll bundle in this way:

var React = require("mydll/node_modules/preact/dist/preact");

If you run webpack -d you'll see something like the following in the generated app.js:

/* 1 */
/*!***************************************************************************************************!*\
  !*** delegated ./node_modules/preact/dist/preact.js from dll-reference main_2057857de340fdcfd8aa ***!
  \***************************************************************************************************/

One might ask "Why can't I just use my standard requires like require("preact")?". The answer is: you can, but. But in that case you would have to install all these dependencies you have in your dll bundle also in your app. Because in this case you would be using "mapped mode" instead of "scoped mode" (see Webpack Docs).

In scoped mode you have to explicitly require the path to the module relative to the manifest. The upside is: You don't have to install the module (and have it as a dependency in the package.json) in your app.

In mapped mode you can require the module as usual (as if it were installed in your app's node_modules) but you have to install it also in the dll using app. That is because Webpack will evaluate the require call first and then realize that the same module is also in the dll bundle and thus render only an alias ("delegated ...") into the output.


Now I think there are usescases for both of these modes. The mapped mode is cool if you are only building an app-local dll to speed up your builds. In that case you would install and save all the deps that go into the dll locally anyway. But if you want to build a dll bundle as an installable module and share it between apps - and you don't want to keep track of all the modules in the dll in every single of these apps - you will most probably want to use scoped mode paying the price of more verbose require calls.


You can try this way;

new webpack.DllReferencePlugin({
  context: process.cwd(), // Important
  manifest: manifest.json
}),

Tags:

Webpack