1

It kills me that in my universal JavaScript app, I have DLL libraries extracted via webpack DLL plugin, but I have to choose between the default export (for use by browsers) or commonjs (for use by Node).

In all the thousands of lines of vendor code, literally the only difference between the two exports is the very first line:

- module.exports =
+ var vendor = 

My express app will only allow me to require the module.exports variant because it expects commonjs modules, and my browser has no idea what to do unless it gets the var vendor variant.

I don't want 99.999999...% identical copies of the same file.

Instinctively, this feels insane: I think both environments should be able to consume the same DLL libraries, since the app code is identical between them.

So I've been brainstorming options, and I've come up with the following possibilities:

  • Use a client-side CommonJS loader like RequireJS (really don't like this option, as I'd have to force the download on the client at the cost of load time)
  • Transform the DLLs on-the-fly to replace the first line when the client requests them, via Express (or Nginx, if that's possible); unsure what performance penalties I'd incur with this approach.
  • Require the DLLs from a FileStream that does the find/replace when the Express app starts (again, no idea if its possible, but this feels like the least-invasive of the three options).
  • Accept the fact that there's no way to avoid having two copies of the DLLs, and that each new dependency I add to my DLLs will require × 2 disk space et al.

I've been unsuccessful in finding implementation details to the middle two points, or in finding alternate approaches. I'm looking for answers beyond "just have two copies and move on with your life." If you know how to do the middle two points, I'd love to get implementation details from you. If you have an idea or approach I haven't listed here, I'm all ears!

Pang
  • 9,564
  • 146
  • 81
  • 122
neezer
  • 19,720
  • 33
  • 121
  • 220

2 Answers2

0

So of course as soon as I post my SO question I happen upon the right Google search that leads me to this solution. I'd never heard of the vm module before an hour ago, but it seems to offer exactly what I'm looking for.

Build the DLLs for the browser with var NAME = (this is the default behavior). Then, on the server, evaluate the files from the disk using vm.runInNewContext and return the new context, which will have keys matching your var statement from your DLL. Lastly, just assign those keys to global.NAME and boom: server understands the files.

Here's what my code (currently) looks like:

const vm = require('vm');
const fs = require('fs');

// from a custom webpack plugin I wrote, where keys are the name of the chunk
// and the value is a relative filepath to that chunk
const chunks = require('path/to/my/chunks.json');

if (!chunks.js.serverDLL || !chunks.js.appDLL) {
  throw new Error('Chunks could not be loaded!!');
}

const dllContext = ((pathMap, context = {}) => {
  Object.keys(pathMap).forEach((key) => {
    vm.runInNewContext(fs.readFileSync(pathMap[key]), context, pathMap[key]);
  });

  return context;
})({ serverDLL: `.${chunks.js.serverDLL}`, appDLL: `.${chunks.js.appDLL}` });

global.serverDLL = dllContext.serverDLL;
global.appDLL = dllContext.appDLL;

There's room for improvement above, in that I'll probably use a HOC function to iterate over my chunks input instead of hard-coding keys. Also, my publicPath is set to /, but the paths passed into my vm.runInNewContext need to be relative file paths. Since I'm doing this in production only (not development), my build directory is flat (for these files anyways), so I prepend a . which turns the client URL routes into the appropriate relative file paths. Also also, this is my first time using vm, so I'm sure someone will point out ways in which I could be doing the above better. :D

Community
  • 1
  • 1
neezer
  • 19,720
  • 33
  • 121
  • 220
0

It sounds like this change to the start and end of the file may work:

// start of file
try {
  module.exports;
} catch (err) {
  var module = {};
}
var vendor = // ... the duplicated code

// end of file
module.exports = vendor;

I am sure both nodejs and a browser can get their correct values this way in general, but I am not using DLL libraries with webpack myself, so I apologize in advance if this won't fit that situation. I thought I'd throw it out there just in case.

curtwphillips
  • 5,441
  • 1
  • 20
  • 19