Skip to content

Commit

Permalink
Create smaller bundles using deferred plugins loading per page (#1241)
Browse files Browse the repository at this point in the history
* Partially fixes #1202: created smaller bundles to minimize big dependencies impact

* rendering update fix

* Added unit tests

* Small fixes
  • Loading branch information
mbarto authored Nov 4, 2016
1 parent 22b5c46 commit be45663
Show file tree
Hide file tree
Showing 19 changed files with 245 additions and 77 deletions.
21 changes: 21 additions & 0 deletions web/client/actions/__tests__/plugins-test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
/**
* Copyright 2016, GeoSolutions Sas.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree.
*/

const expect = require('expect');
const {loadPlugins, LOAD_PLUGINS} = require('../plugins');

describe('Test plugins related actions', () => {
it('test load plugins action', () => {
const action = loadPlugins([{}]);

expect(action).toExist();
expect(action.type).toBe(LOAD_PLUGINS);
expect(action.plugins).toExist();
expect(action.plugins.length).toBe(1);
});
});
16 changes: 16 additions & 0 deletions web/client/actions/plugins.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
/**
* Copyright 2016, GeoSolutions Sas.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree.
*/
const LOAD_PLUGINS = 'LOAD_PLUGINS';

function loadPlugins(plugins) {
return {
type: LOAD_PLUGINS,
plugins
};
}
module.exports = {LOAD_PLUGINS, loadPlugins};
12 changes: 11 additions & 1 deletion web/client/components/app/StandardApp.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ const StandardApp = React.createClass({
pluginsDef: {plugins: {}, requires: {}},
initialActions: [],
printingEnabled: false,
appStore: () => ({dispatch: () => {}}),
appStore: () => ({dispatch: () => {}, subscribe: () => {}}),
appComponent: () => <span/>
};
},
Expand All @@ -57,6 +57,16 @@ const StandardApp = React.createClass({
onPersist: onInit
});
this.store = this.props.appStore(this.props.pluginsDef.plugins, opts);
let newPlugins;
this.store.subscribe(() => {
const state = this.store.getState();
if (state.plugins && Object.keys(state.plugins).length > 0) {
if (state.plugins !== newPlugins) {
newPlugins = state.plugins;
this.store.replaceReducer(this.props.appStore(assign({}, this.props.pluginsDef.plugins, newPlugins), {updateReducers: true}));
}
}
});
if (!opts.persist) {
onInit();
}
Expand Down
23 changes: 19 additions & 4 deletions web/client/components/app/StandardRouter.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,28 +13,43 @@ const Debug = require('../development/Debug');
const {Router, Route, hashHistory} = require('react-router');

const Localized = require('../I18N/Localized');
const assign = require('object-assign');
const PluginsUtils = require('../../utils/PluginsUtils');

const StandardRouter = React.createClass({
propTypes: {
plugins: React.PropTypes.object,
locale: React.PropTypes.object,
pages: React.PropTypes.array
pages: React.PropTypes.array,
loadPlugins: React.PropTypes.func
},
getDefaultProps() {
return {
plugins: {},
locale: {messages: {}, current: ''},
pages: []
pages: [],
loadPlugins: () => {}
};
},
getInitialState() {
return {plugins: {}};
},
renderPages() {
return this.props.pages.map((page) => {
const pageConfig = page.pageConfig || {};
const Component = connect(() => ({
plugins: this.props.plugins,
plugins: assign({}, this.props.plugins, this.state.plugins),
...pageConfig
}))(page.component);
return (<Route key={page.name || page.path} path={page.path} component={Component}/>);
return (<Route key={page.name || page.path} path={page.path} component={Component} onEnter={page.plugins ? (nextState, replace, callback) => {
page.plugins((newPlugins) => {
this.setState({
plugins: assign({}, this.state.plugins, PluginsUtils.getPlugins(newPlugins))
});
this.props.loadPlugins(newPlugins);
callback();
});
} : null}/>);
});
},
render() {
Expand Down
6 changes: 4 additions & 2 deletions web/client/components/app/__tests__/StandardApp-test.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,8 @@ describe('StandardApp', () => {
const store = () => ({
dispatch() {
dispatched++;
}
},
subscribe() {}
});


Expand All @@ -75,7 +76,8 @@ describe('StandardApp', () => {
if (value === 10) {
done();
}
}
},
subscribe() {}
});


Expand Down
32 changes: 31 additions & 1 deletion web/client/components/app/__tests__/StandardRouter-test.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ const mycomponent = React.createClass({
}
});

describe('StandardApp', () => {
describe('StandardRouter', () => {
beforeEach((done) => {
document.body.innerHTML = '<div id="container"></div>';
ConfigUtils.setLocalConfigurationFile('base/web/client/test-resources/localConfig.json');
Expand Down Expand Up @@ -93,4 +93,34 @@ describe('StandardApp', () => {

expect(dom.getElementsByClassName('MyPlugin').length).toBe(1);
});

it('creates a default router app with pages and on page plugins', () => {
const handlers = {
loadPlugins: () => {}
};
const plugins = {
MyPlugin: {}
};
const spy = expect.spyOn(handlers, "loadPlugins");
const pluginsFunc = (callback) => {
callback(plugins);
};

const store = {
dispatch: () => {},
subscribe: () => {},
getState: () => ({})
};
const pages = [{
name: 'mypage',
path: '/',
component: mycomponent,
plugins: pluginsFunc
}];
const app = ReactDOM.render(<Provider store={store}><StandardRouter loadPlugins={handlers.loadPlugins} pages={pages}/></Provider>, document.getElementById("container"));
expect(app).toExist();

expect(spy.calls.length).toBe(1);
expect(spy.calls[0].arguments[0]).toBe(plugins);
});
});
1 change: 1 addition & 0 deletions web/client/components/map/leaflet/Layer.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,7 @@ const LeafletLayer = React.createClass({
if (this.layer) {
this.layer.layerName = options.name;
this.layer.layerId = options.id;
this.setState({});
}
}
},
Expand Down
7 changes: 5 additions & 2 deletions web/client/components/map/openlayers/Layer.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -101,8 +101,11 @@ const OpenlayersLayer = React.createClass({
if (type) {
const layerOptions = this.generateOpts(options, position, CoordinatesUtils.normalizeSRS(this.props.srs));
this.layer = Layers.createLayer(type, layerOptions, this.props.map, this.props.mapId);
if (this.layer && !this.layer.detached) {
this.addLayer(options);
if (this.layer) {
this.setState({});
if (!this.layer.detached) {
this.addLayer(options);
}
}
}
},
Expand Down
2 changes: 1 addition & 1 deletion web/client/containers/MapViewer.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ const urlQuery = url.parse(window.location.href, true).query;
const ConfigUtils = require('../utils/ConfigUtils');

const PluginsContainer = connect((state) => ({
pluginsConfig: state.plugins || ConfigUtils.getConfigProp('plugins') || null,
pluginsConfig: ConfigUtils.getConfigProp('plugins') || null,
mode: (urlQuery.mode || (state.browser && state.browser.mobile ? 'mobile' : 'desktop')),
pluginsState: state && state.controls || {}
}))(require('../components/plugins/PluginsContainer'));
Expand Down
2 changes: 1 addition & 1 deletion web/client/examples/rasterstyler/pages/MapViewer.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ const {resetControls} = require('../../../actions/controls');
const urlQuery = url.parse(window.location.href, true).query;

const PluginsContainer = connect((state) => ({
pluginsConfig: state.plugins || ConfigUtils.getConfigProp('plugins') || null,
pluginsConfig: ConfigUtils.getConfigProp('plugins') || null,
mode: (urlQuery.mobile || (state.browser && state.browser.touch)) ? 'mobile' : 'desktop'
}))(require('../../../components/plugins/PluginsContainer'));

Expand Down
3 changes: 1 addition & 2 deletions web/client/examples/styler/pages/MapViewer.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ const {resetControls} = require('../../../actions/controls');
const urlQuery = url.parse(window.location.href, true).query;

const PluginsContainer = connect((state) => ({
pluginsConfig: state.plugins || ConfigUtils.getConfigProp('plugins') || null,
pluginsConfig: ConfigUtils.getConfigProp('plugins') || null,
mode: (urlQuery.mobile || (state.browser && state.browser.touch)) ? 'mobile' : 'desktop'
}))(require('../../../components/plugins/PluginsContainer'));

Expand Down Expand Up @@ -65,4 +65,3 @@ module.exports = connect((state) => ({
loadMapConfig,
reset: resetControls
})(MapViewer);

5 changes: 4 additions & 1 deletion web/client/plugins/Save.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -145,5 +145,8 @@ module.exports = {
return { style: {display: "none"} };
}
}
}))
})),
reducers: {
currentMap: require('../reducers/currentMap')
}
};
5 changes: 4 additions & 1 deletion web/client/plugins/SaveAs.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -186,5 +186,8 @@ module.exports = {
return state && state.security && state.security.user ? {} : { style: {display: "none"} };
}
}
}))
})),
reducers: {
currentMap: require('../reducers/currentMap')
}
};
5 changes: 4 additions & 1 deletion web/client/product/app.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ const React = require('react');
const ReactDOM = require('react-dom');
const {connect} = require('react-redux');
const LocaleUtils = require('../utils/LocaleUtils');
const {loadPlugins} = require('../actions/plugins');

const startApp = () => {
const ConfigUtils = require('../utils/ConfigUtils');
Expand All @@ -22,7 +23,9 @@ const startApp = () => {
const StandardRouter = connect((state) => ({
locale: state.locale || {},
pages
}))(require('../components/app/StandardRouter'));
}), {
loadPlugins
})(require('../components/app/StandardRouter'));

const appStore = require('../stores/StandardStore').bind(null, initialState, {
home: require('./reducers/home'),
Expand Down
76 changes: 64 additions & 12 deletions web/client/product/appConfig.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,23 +10,75 @@ module.exports = {
pages: [{
name: "home",
path: "/",
component: require('./pages/Maps')
}, {
name: "maps",
path: "/maps",
component: require('./pages/Maps')
component: require('./pages/Maps'),
plugins: (resolve) => {
return require.ensure([], () => {
resolve({
MapsPlugin: require('../plugins/Maps'),
MapSearchPlugin: require('../plugins/MapSearch'),
HomeDescriptionPlugin: require('./plugins/HomeDescription'),
ExamplesPlugin: require('./plugins/Examples'),
MapTypePlugin: require('./plugins/MapType'),
ForkPlugin: require('./plugins/Fork'),
ManagerPlugin: require('../plugins/manager/Manager'),
CreateNewMapPlugin: require('../plugins/CreateNewMap')
});
});
}
}, {
name: "mapviewer",
path: "/viewer/:mapType/:mapId",
component: require('./pages/MapViewer')
}, {
name: "manager",
path: "/manager",
component: require('./pages/Manager')
component: require('./pages/MapViewer'),
plugins: (resolve) => {
return require.ensure([], () => {
resolve({
MousePositionPlugin: require('../plugins/MousePosition'),
PrintPlugin: require('../plugins/Print'),
IdentifyPlugin: require('../plugins/Identify'),
TOCPlugin: require('../plugins/TOC'),
BackgroundSwitcherPlugin: require('../plugins/BackgroundSwitcher'),
MeasurePlugin: require('../plugins/Measure'),
MeasureResultsPlugin: require('../plugins/MeasureResults'),
MapPlugin: require('../plugins/Map'),
ToolbarPlugin: require('../plugins/Toolbar'),
DrawerMenuPlugin: require('../plugins/DrawerMenu'),
ShapeFilePlugin: require('../plugins/ShapeFile'),
SnapshotPlugin: require('../plugins/Snapshot'),
SettingsPlugin: require('../plugins/Settings'),
ExpanderPlugin: require('../plugins/Expander'),
SearchPlugin: require('../plugins/Search'),
ScaleBoxPlugin: require('../plugins/ScaleBox'),
LocatePlugin: require('../plugins/Locate'),
ZoomInPlugin: require('../plugins/ZoomIn'),
ZoomOutPlugin: require('../plugins/ZoomOut'),
ZoomAllPlugin: require('../plugins/ZoomAll'),
MapLoadingPlugin: require('../plugins/MapLoading'),
AboutPlugin: require('./plugins/About'),
HelpPlugin: require('../plugins/Help'),
MadeWithLovePlugin: require('./plugins/MadeWithLove'),
MetadataExplorerPlugin: require('../plugins/MetadataExplorer'),
BurgerMenuPlugin: require('../plugins/BurgerMenu'),
UndoPlugin: require('../plugins/History'),
RedoPlugin: require('../plugins/History'),
SavePlugin: require('../plugins/Save'),
SaveAsPlugin: require('../plugins/SaveAs'),
SharePlugin: require('../plugins/Share')
});
});
}
}, {
name: "manager",
path: "/manager/:tool",
component: require('./pages/Manager')
path: "/manager(/:tool)",
component: require('./pages/Manager'),
plugins: (resolve) => {
return require.ensure([], () => {
resolve({
UserManagerPlugin: require('../plugins/manager/UserManager'),
RulesManagerPlugin: require('../plugins/manager/RulesManager'),
ManagerPlugin: require('../plugins/manager/Manager')
});
});
}
}],
pluginsDef: require('./plugins.js'),
initialState: {
Expand Down
Loading

0 comments on commit be45663

Please sign in to comment.