Skip to content

Instantly share code, notes, and snippets.

@MaxKramnik
Forked from mridgway/BundlePlugin.js
Created June 13, 2016 05:59
Show Gist options
  • Save MaxKramnik/7a82d486151ee2078bbf85ca7c384d6c to your computer and use it in GitHub Desktop.
Save MaxKramnik/7a82d486151ee2078bbf85ca7c384d6c to your computer and use it in GitHub Desktop.
Bundle Plugin for Fluxible
/**
* On server, just register all of the resources directly to the plugin. On the client, pass in a loader file that maps bundle
* name to webpack bundle require (a separate entry point). Then from your action, you can call `context.loadBundle` to load
* the bundle resources. On the server, the resources are already available but on the client they will be lazy loaded by
* webpack.
*/
'use strict';
var debug = require('debug')('BundlePlugin');
var PromiseLib = global.Promise || require('es6-promise').Promise;
module.exports = function bundleLoaderFactory(options) {
return new BundleLoader(options);
};
function BundleLoader(options) {
options = options || {};
this._app = options.app;
this._mainBundle = options.mainBundle;
this._enableMainBundle = typeof options.enableMainBundle !== 'undefined' ? options.enableMainBundle : true;
this._bundleLoader = options.bundleLoader;
// Registered later
this._actions = {};
this._components = {};
this._loadedBundles = {};
}
BundleLoader.prototype.name = 'BundlePlugin';
BundleLoader.prototype.plugContext = function () {
var context = new BundleLoaderContext(this);
return {
plugActionContext: function (actionContext) {
actionContext.getAction = context.getAction.bind(context);
actionContext.getComponent = context.getComponent.bind(context);
actionContext.getMainBundle = context.getMainBundle.bind(context);
actionContext.loadBundle = context.loadBundle.bind(context);
actionContext.loadBundles = context.loadBundles.bind(context);
},
plugComponentContext: function (componentContext) {
componentContext.getComponent = context.getComponent.bind(context);
},
dehydrate: function () {
return context.dehydrate();
},
rehydrate: function (state, callback) {
return context.rehydrate(state, callback);
}
};
};
BundleLoader.prototype.getMainBundle = function () {
return this._mainBundle;
};
/**
* Makes an action available for use in page routes
* @method registerAction
* @param {String} name Name of the action
* @param {Function} action action function
*/
BundleLoader.prototype.registerAction = function registerAction(name, action) {
debug(name + ' action registered');
this._actions[name] = action;
};
/**
* Makes a bundle lazy loadable
* @method registerBundle
* @param {String} name Name of the bundle
* @param {Function} bundle bundle object
*/
BundleLoader.prototype.registerBundle = function registerBundle(name, bundle) {
var self = this;
debug(name + ' bundle registered');
self._loadedBundles[name] = bundle;
if (bundle.actions) {
Object.keys(bundle.actions).forEach(function eachAction(actionName) {
self.registerAction(actionName, bundle.actions[actionName]);
});
}
if (bundle.controllerViews) {
Object.keys(bundle.controllerViews).forEach(function eachControllerViews(componentName) {
self.registerComponent(componentName, bundle.controllerViews[componentName]);
});
}
if (bundle.stores) {
Object.keys(bundle.stores).forEach(function eachStore(storeName) {
var store = bundle.stores[storeName];
self._app.registerStore(store);
});
}
};
/**
* Makes a component available for use in composites
* @method registerBundle
* @param {String} name Name of the component
* @param {Function} component React component
*/
BundleLoader.prototype.registerComponent = function registerComponent(name, component) {
debug(name + ' component registered');
this._components[name] = component;
};
/**
* Creates a serializable state of the plugin
* @method dehydrate
* @returns {Object}
*/
BundleLoader.prototype.dehydrate = function dehydrate() {
debug('dehydrate');
return {
mainBundle: this._mainBundle
};
};
/**
* Rehydrates the application and creates a new context with the state from the server
* @method rehydrate
* @param {Object} state
* @async
*/
BundleLoader.prototype.rehydrate = function rehydrate(state) {
debug('rehydrate', state);
var self = this;
self._mainBundle = state.mainBundle;
};
/**
* @class BundleLoaderContext
* @param {object} bundleLoader
* @constructor
*/
function BundleLoaderContext(bundleLoader) {
this.bundleLoader = bundleLoader;
this._bundleLoadPromises = {};
}
BundleLoaderContext.prototype.getMainBundle = function () {
return this.bundleLoader._mainBundle;
};
BundleLoaderContext.prototype.getAction = function (actionName) {
return this.bundleLoader._actions[actionName];
};
BundleLoaderContext.prototype.getComponent = function (componentName) {
return this.bundleLoader._components[componentName];
};
/**
* Loads a bundle rollup
* @method loadBundle
* @param {String} bundleName Bundle name
* @param {Function} [callback] Callback to be called after bundle is loaded
* @return {Promise}
* @async
*/
BundleLoaderContext.prototype.loadBundle = function loadBundle(bundleName, callback) {
debug('load ' + bundleName + ' bundle');
var self = this;
// Make sure we don't try to load the same bundle twice at the same time
if (!self._bundleLoadPromises[bundleName]) {
self._bundleLoadPromises[bundleName] = new PromiseLib(function (resolve, reject) {
// Check if the bundle is already registered first
if (self.bundleLoader._loadedBundles[bundleName]) {
resolve(self.bundleLoader._loadedBundles[bundleName]);
return;
}
// Now attempt to use the loader to lazy load the bundle
if (!self.bundleLoader._bundleLoader) {
reject(new Error('Loader does not exist'));
return;
}
if (!self.bundleLoader._bundleLoader[bundleName]) {
reject(new Error('Bundle ' + bundleName + ' is not available in the loader'));
return;
}
self.bundleLoader._bundleLoader[bundleName](function (err, bundle) {
if (err) {
reject(err);
return;
}
// Register the bundle and its resources
self.bundleLoader.registerBundle(bundleName, bundle);
resolve(bundle);
});
});
}
if (callback) {
self._bundleLoadPromises[bundleName].then(function (result) {
setImmediate(callback, undefined, result);
})['catch'](function (e) {
setImmediate(callback, e);
});
}
return self._bundleLoadPromises[bundleName];
};
/**
* Loads bundles via the loader method
* @method loadBundles
* @param {String|Array} bundleNames Names of bundles to load
* @param {Function} callback Called after all bundles have been loaded
* @async
*/
BundleLoaderContext.prototype.loadBundles = function loadBundles(bundleNames, callback) {
var self = this;
var currentLoadingPromises = [];
if (!Array.isArray(bundleNames)) {
bundleNames = [bundleNames];
}
debug('loading ' + bundleNames.join(', '));
bundleNames.forEach(function (bundleName) {
currentLoadingPromises.push(self.loadBundle(bundleName));
});
PromiseLib.all(currentLoadingPromises).then(function (result) {
debug('loaded ' + bundleNames.join(', '));
setImmediate(callback, null, result);
})['catch'](function (e) {
setImmediate(callback, e);
});
};
/**
* Creates a serializable state of the plugin
* @method dehydrate
* @returns {Object}
*/
BundleLoaderContext.prototype.dehydrate = function dehydrate() {
debug('dehydrate');
var bundlesLoaded = Object.keys(this._bundleLoadPromises);
if (this.bundleLoader._enableMainBundle) {
bundlesLoaded.push(this.bundleLoader._mainBundle); // Always load the main bundle
}
return {
loadedBundles: bundlesLoaded
};
};
/**
* Rehydrates the application and creates a new context with the state from the server
* @method rehydrate
* @param {Object} state
* @param {Function} callback
* @async
*/
BundleLoaderContext.prototype.rehydrate = function rehydrate(state, callback) {
debug('rehydrate', state);
var self = this;
self.loadBundles(state.loadedBundles, function (err) {
if (err) {
callback && callback(err);
return;
}
callback();
});
};
module.exports = function (context, payload, done) {
context.loadBundle('Nest', function () {
//Ensures that Nest components are loaded
context.executeAction(loadPageData, {}, function (err, pageData) {
context.dispatch('NEW_PAGE', pageData);
done();
});
});
}
module.exports = {
actions: {
getAbout: require('./actions/getAbout')
},
controllerViews: {
About: require('./components/About'),
BasicApp: require('./components/Application'),
Followers: require('./components/Followers'),
Home: require('./components/Home')
},
stores: {
AboutStore: require('./stores/AboutStore'),
ApplicationStore: require('./stores/ApplicationStore'),
FollowersStore: require('./stores/FollowersStore'),
TimeStore: require('./stores/TimeStore')
}
};
'use strict';
var debug = require('debug')('Loader');
module.exports = {
'basic': function (callback) {
debug('loading basic');
require('bundle!../bundle.js')(function (bundle) {
debug('loaded basic');
callback(null, bundle);
});
},
'Nest': function (callback) {
debug('loading Nest');
require('bundle!../bundles/Nest/bundle.js')(function (bundle) {
debug('loaded Nest');
callback(null, bundle);
});
}
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment