4

Short version

TL;DR I want to be able to expose arbitrary objects and functions from a Webpack bundle (let's call it Bundle2), which I can use in another (independent and unrelated) bundle (Bundle1).

For example, Bundle2 could be a React app that acts as a widget in Bundle1. Bundle2 would then expose the React component, actions, and reducers somehow so that Bundle1 can use it (like for combining the reducers, rendering the Bundle2 component, etc.).

But I also want Bundle2 to use some libraries that are already included in Bundle1, to save space.

I know I can use Webpack options.externals to exclude libs from the bundle, and I got that to work. But it expects externals to be on the global object. I want to be able to inject dependencies from Bundle1 into Bundle2 somehow, like by passing through props. I do not want to have to set window.React = require('react') in Bundle1 so that Bundle2 can use React.

EDIT: A clarifying point... My use case is I want users to be able to add widgets from a store or something like that. Since there could be potentially thousands of widgets, I don't think it's scalable or feasible for all those widgets (which can be user generated) to be included in the main bundle at compile time (even when using Webpack's chunks or code splitting features). Also, I think it's dangerous to run other people's code through my webpack ,since they can basically run arbitrary native code that way. Instead, I want other people's widgets to reside somewhere on my server and when they "install them", I basicallyload the widget code asynchronously into their dashboard.

Long version

Let's say I have the following Webpack bundles:

  • dashboard.bundle.js, the main application bundle. It is a React app with Redux.
  • clockWidget.bundle.js, which defines a clock React component and uses Redux to update itself based on its state. Thus, it has a root component, a reducer, and some actions.

At runtime, clockWidget.bundle.js is loaded asyncronously into dashboard.bundle.js using require.js, kind of like this:

// Inside dashboard.bundle.js
requirejs(['clockWidget.bundle.js'], (widgetFunc) => {
  // I want to be able to do this:
  const widget = widgetFunc();
  const { actions, reducer, Component } = widget;

// Where:
// Component - is a React component I can render either server-side or client-side inside the dashboard bundle.
// actions & reducer - are Redux actions and reducers
});

Basically, clockWidget will get pulled in asyncronously and rendered somewhere inside the dashboard using the Component given by the clockWidget bundle.

To reduce the size of the clockWidget bundle, I use Webpack's options.externals feature to exclude React from the bundle, since dashboard.bundle.js already has it.

I want to be able to do something like this:

// inside dashboard.bundle.js:

import React from 'react';
//...
requirejs(['clockWidget.bundle.js'], (widgetFunc) => {
  // Give the clockWidget bundle access to React:
  const widget = widgetFunc({ React })();
  // Then calling that gives me all the stuff I need, like before:
  const { actions, reducer, Component } = widget;
  // ...rendering the component, etc...
});

But I can't quite figure out how to do that. I've tried something like this:

// Inside the compiled clockWidget.bundle.js:

// Wrap the Webpack output with a RequireJS define,
// and return an object with the actions, reducer, and component from inside the bundle.
define(() => {

  //  I can make Webpack generate this code using 
  // output: {
  //    libraryTarget: 'var'
  // }
  var bundle = function() {
    // imagine minified webpack code in here...
  };
  // ^ I want to somehow pass down instances to React and other libs
  // so this bundle can consume it. If I use webpack's options.externals,
  // I can simply expose libs globally in dashboard.bundle.js to do the trick, but that smells bad to me.

  // Expose these keys to RequireJS:
  return {
    Actions: {}, // somehow point Actions to the webpack compiled actions
    Reducer: {}, // point Reducer to the webpack compiled reducer
    Component: {}, // point Component to the webpack compiled component
  };
});

Any input or insight on how I can approach this wold be greatly appreciated. Thank you :)

Titus
  • 4,487
  • 6
  • 32
  • 42
  • Why do you want to include the widget bundle instead of its module? – E_net4 Oct 14 '16 at 12:29
  • @E_net4 It's because I want users to be able to add widgets from a store or something like that. Since there could be potentially thousands of widgets, I don't think it's scalable or feasible for all those widgets (which can be user generated) to be included in the main bundle (even when using Webpack's chunks or code splitting features). Also, I think it's dangerous to run other people's code through my webpack ,since they can basically run arbitrary native code that way. Also thanks, I will update the answer to clarify this point :) – Titus Oct 14 '16 at 17:25
  • 1
    No problem. Either me or one of my colleagues might find an answer to that in the future. I've done something slightly similar with Browserify, in fact. – E_net4 Oct 14 '16 at 17:41
  • Just in case you're still searching for a solution: I had basically the same problem and describe my solution in https://stackoverflow.com/questions/43163909/solution-load-independently-compiled-webpack-2-bundles-dynamically – Jörg Richter Apr 02 '17 at 13:50

0 Answers0