Application Structure¶
Files and Folders¶
The folders in the root are related to the project and the folders in web/client contains all the front-end framework files (javascript)
index.html
: demo application home page (for testing purposes)indexTemplate.html
: file used as template to inject the effective bundle scriptjs
: is where your main entries of the application are placed, you can think of it as a similar role ofweb/client
folder where you want to add your custom code (plugins, pages …)translations
: here you can place your custom locale translations or you can override some texts onlyconfigs
: here you can find the main configuration file,localConfig.json
or the configuration of the new map, new.json or the list of plugins available for contexts.(*1)MapStore2
:: this is the git sub-module that contains MapStore2 (*2)In
/web/client/
folder ofMapStore2
you will find all the client part (React components, redux files, plugins, utilities …)index.html
: MapStore product demo application home pagecomponents
: ReactJS dumb componentsactions
: Redux actionsselectors
: Redux actionsreducers
: Redux reducersepics
: redux-observable epicsutils
: utility files for reusable elements, functions, constants, etcstores
: redux storesplugins
: ReactJS smart components with required reducersproduct
: contains the configuration and the plugins that belongs to MapStore product (even the set of plugins).product/pages
: (managed by react-router) + most of them render plugins
In
java
folder ofMapStore2
you will find all java backend modulesIn
build
older ofMapStore2
you can find the build related files.In
MapStore2
directory you will also find build related filesbuildConfig.js
: the mail utility function to create the final webpack configuration.webpack.config.js
: the main webpack configuration file (for local running)prod-webpack.config.js
: the production webpack configuration
Warning
You will need to keep in sync files like index.html
and indexTemplate.html
, or prod-webpack.config.js
and webpack.config.js
, that differ only for a few lines.
This to have your changes both in test environment or production.
Note
For versions < 2021.02.xx this folder is not present yet. The configuration files are instead in the root of the project.
future versions of MapStore will try to reduce the boilerplate required for a MapStore project, and we are also trying to remove the git sub-module in favor of npm packages, and reducing the duplication and files to keep in sync.
WebPack Configuration¶
The webpack configuration is the main file you need to analyze if you want to understand a MapStore application.
Inspecting package.json
file you will see the webpack command scripts launched by start
and fe:build
scripts.
{
"scripts": {
"start": "npm run app:start",
"app:start": "concurrently -n frontend,backend -c green,blue \"npm:fe:start\" \"npm:be:start\"",
"fe:start": "webpack serve --progress --color --port 8081 --hot --inline --config webpack.config.js --content-base .",
"be:start": "npm run be:build && mvn cargo:run -f ./web/pom.xml",
"be:build": "mvn clean install -Pprinting",
"fe:build": "npm run clean && mkdirp ./dist && webpack build --progress --color --config prod-webpack.config.js"
}
}
Note
here the commands are simplified, removing all the options that are not important to highlight the main concepts.
If
--config
is not specified, webpack looks by default a file named``./webpack.config.js``, that is the case of thestart
commandIn the project or core you may find different paths.
As you can see the fe:start
script, that starts the development server locally for the front end, runs webpack serve
with the configuration build/webpack.config.js
.
The be:start
script runs the back-end server, that is a maven project, and the fe:build
script runs the production build.
You can run npm start
to run both the front-end and the back-end servers, or npm run fe:start
to run only the front-end server.
Note
If you have your back-end running on your test server, you can change the URL for your back-end service by redefining the environment variable MAPSTORE_BACKEND_URL :
export MAPSTORE_BACKEND_URL"=http://myserver.com:8080"
npm run fe:start
looking at the webpack configuration file you will se something like this:
const path = require("path");
const themeEntries = require('./MapStore2/build/themes.js').themeEntries;
const extractThemesPlugin = require('./MapStore2/build/themes.js').extractThemesPlugin;
const ModuleFederationPlugin = require('./MapStore2/build/moduleFederation').plugin;
module.exports = require('./MapStore2/build/buildConfig')({
bundles: {
'App': path.join(__dirname, "js", "app"),
'App-embedded': path.join(__dirname, "js", "embedded"),
'App-api': path.join(__dirname, "MapStore2", "web", "client", "product", "api"),
'geostory-embedded': path.join(__dirname, "js", "geostoryEmbedded"),
"dashboard-embedded": path.join(__dirname, "js", "dashboardEmbedded")
},
themeEntries,
paths: {
base: __dirname,
dist: path.join(__dirname, "dist"),
framework: path.join(__dirname, "MapStore2", "web", "client"),
code: [path.join(__dirname, "js"), path.join(__dirname, "MapStore2", "web", "client")]
},
plugins: [extractThemesPlugin, ModuleFederationPlugin],
prod: false,
publicPath: undefined,
cssPrefix: '.App',
prodPlugins: [],
alias: {
"@mapstore/patcher": path.resolve(__dirname, "node_modules", "@mapstore", "patcher"),
"@mapstore": path.resolve(__dirname, "MapStore2", "web", "client"),
"@js": path.resolve(__dirname, "js")
}
});
Basically this configuration is created by calling buildConfig
utility, that takes care to create for you a webpack configuration for MapStore.
The arguments of this buildConfig
are, in order:
entries
: JS entries for webpack. Webpack will create a JS bundle for each entry of this object, starting from the file indicated as value. You can see it creates an entry for the app, one entry (generally reduced in size) for embedded, one for the JS API and other entries for embedding stories and dashboards.themeEntries
: other entries to build the themepaths
: paths to the main parts of the frameworkplugins
: additional webpack plugins that are valid for development and production mode.production
: a flag, if true it will build in production mode.publicPath
: the public path for the build filescssPrefix
: a prefix to add to the css theme, to wrap all the CSS of MapStore under a certain class and so avoid conflicts when used not as a single page application, but as JS APIprodPlugins
: plugins that have to be added only if theproduction
flag is true.alias
: a set of alias for the code paths (e.g. you can refer all core objects usingimport from '@mapstore/actions/map';
. instead of using a relative paths.proxy
: configuration of th dev proxy (usually you can add it as last parameter to you project to customize).
The main entry of web pack is app.jsx
. This represents the main bundle of the single page application and will be included and executed inside the index.html
.
Note
The build configuration for production is very similar, but includes also some additional webpack plugins to build the html files from indexTemplate.html
(and other for embedded entries) and create the effective index.html
used in production.
This is required to produce js bundles with the name versioned (to manage browser cache efficiently), but it will be transparent for the user and the developer.
The only thing you have to keep in mind about this is when you customize the index.html
you will have to customize also the indexTemplate.html
to have your changes to be applied also in compiled version.
It duplication will be removed in the future project system by replacing the html files with ejs
template files.
Entries¶
The main entry point of MapStore product is web/client/product/app.jsx
. It is the main entry
for webpack used by the main MapStore application as a single page.
Note
There are also other entry points: embedded.jsx
, dashboardEmbedded.jsx
, geostoryEmbedded
. They are used to create smaller bundles
to use in embedded or API context, only with the JS required for that particular use case.
For a standard project instead the entry point can be found in js/app.jsx
Let’s have a look at the app.jsx
In app.jsx
file you can see there are some configuration settings for:
Translation paths
theme prefix
main configuration file URL
(you can add more configurations here)
Then it retrieves:
appConfig
: that contains pages definitions and initial state setup and optionsplugins
: that contains all the plugins definitions
and then calls main(appConfig, plugins)
this method takes care to initialize the application by calling the effective ReactDOM.render
of the StandardApp
component.
StandardApp
provides the base functionalities to the system:
Redux store initialization (
StandardStore
)Base routing (
StandardRouter
)i18n (Internationalization) support
Extensions support
Early Customizations¶
In the entry file you can customize some key properties like:
localConfig.json
translationsPath
appConfig
plugins
localConfig.json¶
This is where you can customize many aspect of MapStore. Most of the times you may want to customize some plugins, adding, removing or configuring them.
In a custom project the app.jsx
by default the path to localConfig.json
uses the one of the main product.
If you want to use a customize these settings you need to copy it from the product folder into the configs folder and then change
the path in the app.jsx
file
cp MapStore2/web/client/configs/localConfig.json configs/localConfig.json
// ConfigUtils.setLocalConfigurationFile('MapStore2/web/client/configs/localConfig.json');
ConfigUtils.setLocalConfigurationFile('configs/localConfig.json');
The main part of the localConfig.json you wanna customize is the plugins array
Example
// optional state initializer (it will override the one defined in appConfig.js)
"initialState": {
// default initial state for every mode (will override initialState imposed by plugins reducers)
"defaultState": {
// if you want to customize the supported locales put here the languages you want and follow instruction linked below
"locales": {
...,
"supportedLocales": {
"it": {
"code": "it-IT",
"description": "Italiano"
},
"en": {
"code": "en-US",
"description": "English"
}
}
}
}
},
"plugins": {
// plugins to load for the mobile mode
"mobile": [...]
// plugins to load for the desktop mode
"desktop": [
{
"name": "MyPlugin"
"cfg": {
"pluginPropToCustomize": "put a value here"
}
}
...
]
// plugins to load for the embedded mode
"embedded": [...]
// plugins to load for the myMode mode
"myMode": [...]
}
Here you will find more information about configuring
localConfig.json
.
In later sections we will see how to add and configure plugins more in detail
translationsPath¶
In order to apply some overrides you can:
configure like this the translationsPath
ConfigUtils.setConfigProp('translationsPath', ['./MapStore2/web/client/translations', './translations']);
create a translations folder in the root
clone there only the locales files you want to override from
MapStore2/web/client/translations
remove everything inside messages object and leave only the paths you want to override
appConfig¶
This file defines the configuration at application level. Optionally you can extend the pages present here with some of your custom ones
Example 1: make 1 page only in the root, as the default map viewer
Create the
configs/config.json
in the project folder to have a configuration to load. Copyingnew.json
is ok.
cp configs/new.json configs/config.json
Apply the follwoing changes to
app.jsx
// const appConfig = require('@mapstore/product/appConfig') // replace this with the following
const mapStoreAppConfig = require('@mapstore/product/appConfig').default;
const appConfig = {
...mapStoreAppConfig,
pages: [{
name: "mapviewer",
path: "/",
component: require('@mapstore/product/pages/MapViewer').default
}]
};
Example 2: Create an additional custom page, keeping all the rest of the default pages
app.jsx
// const appConfig = require('@mapstore/product/appConfig') // replace this with the following
const mapStoreAppConfig = require('@mapstore/product/appConfig').default;
const appConfig = {
...mapStoreAppConfig,
pages: [...mapStoreAppConfig.pages, {
name: "mapviewer",
path: "/page",
component: () => <div>this is a custom page</div>
}]
};
Note
you will have to import react in the file too.
import React from 'react';
Pages¶
Pages in MapStore are the main routes for react router.
Each page is designed to respond to a particular path (or range of paths), implemented adding a path after the #
in the URL.
They are often implemented as PluginsContainer
(this is not mandatory), so they can be configured to render a set of plugins.
Note
PluginsContainer
is the main component that takes care of rendering plugins. It accepts:
A configuration, that is the list of plugins to render, with the specific “cfg” of any of them.
The plugins definitions (all the objects exported by the plugin main file, containing the main component to render and other configurations, for each plugin)
Typically the configuration is the array contained in one of the entries of plugins
attribute of localConfig.json
.
In localConfig.json you can find several configuration used by the pages of MapStore product, in the plugins
section:
desktop
,embedded
,mobile
: lists of plugins configurations dedicated to the map viewer.maps
: plugins of the home pagedashboard
,geostory
,… : Plugins for the pages that shows dashboard or GeoStory contents.
The pages are configured in the app.jsx
using the appConfig
configuration.
Plugins¶
This file contains all the plugins that are imported in the final build. You may extend it with some custom plugins present in your js/plugins folder
export default {
plugins: {
// my custom plugins
AddGroupPlugin: require('@js/plugins/AddGroup').default,
// mapstore plugins
AddGroupPlugin: require('@mapstore/plugins/AddGroup').default,
AnnotationsPlugin: require('@mapstore/plugins/Annotations').default,
AutoMapUpdatePlugin: require('@mapstore/plugins/AutoMapUpdate').default,
...
}
}
@js
is the alias for thejs
folder@mapstore
is the alias forMapStore2/web/client