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. .. figure:: img/folder.png :alt: 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. .. code-block:: json { "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` : .. code-block:: bash export MAPSTORE_BACKEND_URL"=http://myserver.com:8080" npm run fe:start looking at the webpack configuration file you will se something like this: .. code-block:: jsx 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`` .. figure:: img/js.png :alt: js Folder :align: center ``js`` folder example Let's have a look at the ``app.jsx`` .. figure:: img/app.jsx.png :alt: example of main entry point. :align: center 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 .. code-block:: sh cp MapStore2/web/client/configs/localConfig.json configs/localConfig.json .. code-block:: javascript // 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 .. code-block:: javascript // 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. .. code-block:: bash cp configs/new.json configs/config.json 2. Apply the follwoing changes to ``app.jsx`` .. code-block:: 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** .. code-block:: 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: () =>
this is a custom page
}] }; .. note :: you will have to import react in the file too. .. code-block:: javascript 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 .. code-block:: javascript 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``