mocha-webpack icon indicating copy to clipboard operation
mocha-webpack copied to clipboard

Is it possible to use the DllReferencePlugin with mocha-webpack?

Open derekdon opened this issue 7 years ago • 12 comments

This kind of thing...

const vendorDll = paths.project.dll('vendor.dll.js');
...
const child = spawn(
    'node',
    [
        mochaWebpack,
        '--opts', mochaOptsPath,
        '--colors',
        '--webpack-config', testConfig,
        '--require', vendorDll,
        '--require', 'source-map-support/register',
        '--require', setup,
        testGlob
    ],
    ...
);
module.exports = {
    target: 'node',
    externals: [nodeExternals()],
    plugins: [
        new webpack.DllReferencePlugin({
            context: paths.project.dll(),
            manifest: require(paths.project.dll('vendor.manifest.json'))
        }),
    ],

derekdon avatar Jun 15 '17 12:06 derekdon

Anyone, Bueller?

derekdon avatar Jun 26 '17 10:06 derekdon

I'm not entirely sure I understand what you're trying to do, but we're using DllReferencePlugin in a project I'm working on and we didn't have to do anything special to get i working, just using the --webpack-config option.

ahstro avatar Jul 03 '17 12:07 ahstro

I'm trying to get this to work too, but seems to be something I'm missing. I get errors saying that the variable that the DLL bundle exports (and that the main bundle is looking for) is undefined, like:

ReferenceError: cesiumDll_7a13ade5de4dfdb862be is not defined

Just tried several different approaches to getting the DLL bundle loaded into the test code's global namespace, including require()ing it, using --require and --include as CLI args, etc, with no luck.

Any tips?

markerikson avatar Jul 17 '17 22:07 markerikson

May I ask you why you need to use DLL bundles with mocha-webpack? I always thought that it's recommended for bundling node_modules.

zinserjan avatar Jul 19 '17 18:07 zinserjan

The main point of DLL bundles is to remove the need for Webpack to always process a given set of files, as well as build cacheable artifacts. I'm using the Cesium 3D globe library, which is a very large library (the bundle is around 2MB minified). At the moment, I'm pretty sure that the basic mocha-webpack test setup is having to process all of Cesium when it does compiles for tests, and I'd like to see if I can avoid that.

markerikson avatar Jul 19 '17 19:07 markerikson

My recommendation is to completely avoid bundling node_modules with webpack's externals option. That's the fastest option.

But if this doesn't work for your library, then is DLL probably the way to go.

zinserjan avatar Jul 19 '17 19:07 zinserjan

We're using Webpack DLL on our project and they are a must when your project scales.
You first need to create the DLL, then when you start/test/coverage/build your app, some part of the Webpack generation is already saved on disk (minified, dead code removed...).
It's perfectly working with mocha-webpack. Just use a DLLReferencePlugin on your webpack test file and it should work. Another ways to improve compilation time are :

And of course DLLPlugin

serut avatar Jul 19 '17 20:07 serut

Yeah, per my comment, "just adding DLLReferencePlugin" is not working :( I definitely have the DLL bundle on disk, but when I try to run a test, there's a JS error saying that the bundle export variable isn't defined. Not sure what I'm missing there.

markerikson avatar Jul 19 '17 20:07 markerikson

Do you have a configuration file webpack.test.config.js like that ?

{
    target: 'node', // in order to ignore built-in modules like path, fs, etc.
    externals: [nodeExternals({
      // this WILL include these in the bundle
      whitelist: [/react-material-color-picker/],
    })],
    // Enable sourcemaps for debugging webpack's output.
    devtool: 'inline-source-map',
    stats: {
      chunks: false,
      colors: true,
      reasons: true,
    },
    module: {
      noParse: [
        /sinon/,
        /iconv-loader/,
        /enzyme/,
      ],
    },
    plugins: [
      new webpack.DllReferencePlugin({
        // The path to the manifest file which maps between
        // modules included in a bundle and the internal IDs
        // within that bundle
        manifest: require(`${__dirname}/my-manifest.json`),
        context: __dirname,
      })
    ],
    // enable sourcemaps support
    output: {
      devtoolModuleFilenameTemplate: '[absolute-resource-path]',
      devtoolFallbackModuleFilenameTemplate: '[absolute-resource-path]?[hash]',
    },
  }

My command in the package.json

"test:mocha": "mocha-webpack --timeout 20000 --webpack-config webpack.test.config.js \"@(tests|modules)/**/*.test.js{,x}\" --inline-diffs",

Edit: you have defined context: paths.project.dll(), but It should be the root directory of your app

serut avatar Jul 20 '17 08:07 serut

I was able to find a solution for this, using the node-hook library. Here's the implementation:


/* HACK

    This next chunk of setup is a _really_ nasty hack, but we need it to sucessfully
    use the prebuilt DLL bundles with our third-party libs in our tests.

     The problem is that the DLL bundles look like this:

     var abc123 = function() {
        // bundle contents here
     };

     When our application code runs, Webpack expects that "abc123" will be a variable
     in the global namespace so that it can initialize the bundled code.  However, when
     run under Node, that module is isolated, so "var abc123" will _not_ be added to the
     global namespace.

     Here's the workaround:

     1) Read the Webpack-generated manifest files for the bundles, and read out the "name"
        field, which gives us the expected global variable name
     2) Use a lib that hooks into Node's file resolution process, and gives us the actual
        source code of the module after it's been read off disk.  We can then add a new line
        of code that exports the variable as the default export of the module.
     3) Import each DLL bundle and save a reference to the contents
     4) Turn off the require hook
     5) Add the imported bundle function into the "global" object that will become the
        actual global namespace when the application code is run in the test.

     Ugly? Yep. Does it work? Yep :)
 */
 
const vendorManifest = require("../distdll/vendor-manifest.json");
const vendorBundleName = vendorManifest.name;

const nodeHook = require("node-hook");

nodeHook.hook('.js', function(source, filename) {
    const newSource = `${source}\n\nmodule.exports = ${vendorBundleName};`;
    return newSource
});

const vendorExport = require("../distdll/vendor.dll");

// Temporarily disable console logging to quiet nodeHook
const {log} = console;
console.log = () => {};

nodeHook.unhook('.js');

// reenable console .logging
console.log = log;

global[vendorBundleName] = vendorExport;

With this in place, my tests can now use the actual bundled libs from the DLL bundle.

markerikson avatar Jan 05 '18 23:01 markerikson

when you're generating your DLL bundle, can you set libraryTarget to commonjs2 or umd? it should eliminate the need to worry about this. I might be missing something however what you're describing seems to be related to the fact that the DLL reference has some mismatch with the bundle it is referencing.

soederpop avatar Jun 08 '18 17:06 soederpop

No, you can compile your DLL correctly (it works when used with webpack-dev-server) but while testing it fails to import it. It happens too on webpack-dev-server, it fails to import it suddently while it worked for last 20 runs.
So we're not using DLLReference while testing anymore (with >2K tests).

serut avatar Jun 11 '18 08:06 serut