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 script

  • js : is where your main entries of the application are placed, you can think of it as a similar role of web/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 only

  • configs : 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 of MapStore2 you will find all the client part (React components, redux files, plugins, utilities …)

      • index.html : MapStore product demo application home page

      • components : ReactJS dumb components

      • actions : Redux actions

      • selectors : Redux actions

      • reducers : Redux reducers

      • epics : redux-observable epics

      • utils : utility files for reusable elements, functions, constants, etc

      • stores : redux stores

      • plugins : ReactJS smart components with required reducers

      • product : 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 of MapStore2 you will find all java backend modules

    • In build older of MapStore2 you can find the build related files.

    • In MapStore2 directory you will also find build related files

      • buildConfig.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

  1. For versions < 2021.02.xx this folder is not present yet. The configuration files are instead in the root of the project.

  2. 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.

representation of a standard project folder structure.

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 the start command

  • In 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 theme

  • paths: paths to the main parts of the framework

  • plugins : 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 files

  • cssPrefix: 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 API

  • prodPlugins: plugins that have to be added only if the production flag is true.

  • alias: a set of alias for the code paths (e.g. you can refer all core objects using import 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

js Folder

js folder example

Let’s have a look at the app.jsx

example of main entry point.

app.jsx example

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 options

  • plugins: 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

This configuration property defines the list of paths where to find the locale translations.
You can use the main mapstore translations locales

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

  1. Create the configs/config.json in the project folder to have a configuration to load. Copying new.json is ok.

cp configs/new.json configs/config.json
  1. 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 page

  • dashboard, 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 the js folder

  • @mapstore is the alias for MapStore2/web/client