Reactium Core

Reactium is a Node/Express + React.js framework for creating front-end apps.

Reactium is built on a core framework. The local development and build configuration that comes out of the box is meant to be upgradeable, so long as your application was built off a semver that is minor-version compatible with the current.

Even for larger version steps, we are going to attempt to describe (or automate) much of the migration from one version of Reactium core to another.

Updating core is performed with the reactium command:

npx reactium update

See: Updating for details

With a number of other front-end frameworks, even those based on React, the philosophy is to entirely hide the local development/build configuration from the developer, sometimes with an eject feature to get raw configuration / build files.

Our philosophy is to create a strong opinion for building a React application, from application structure, to out of the box capabilities such as routing, plugins, and state management, while giving the code maintainer, lead-dev, and ops roles on your team the power to replace, augment, or override behaviors of the build.

Hacking Core

Should you hack reactium_modules? Short answer: no

If you hack on the files in the reactium_modules directory of your project, those changes will be overwritten when executing routing reactium install operations, similar to npm install for node_modules. Instead of modifying these files directly, fork Reactium-Core-Plugins on Github and send us a pull-request. We'll either add your update if appropriate.

That being said, there are a number of build and development overrides built-in to core for your use.

Reason

Purpose

Where

DDD (domain-driven design) artifact to register/unregister gulp tasks with the ReactiumGulp registry, used to generate gulp tasks at build-time. You can use this in a Reactium plugin to alter gulp tasks within your own domain. Final tasks can be overriden by gulp.tasks.override.js.

Create this file in any of the following:

  • In any domain directory under src/

  • In any node module's reactium-plugin/ directory.

  • In any directory within a module under reactium_modules/

DDD (domain-driven design) artifact to interact with the ReactiumWebpack registry, used to generate the webpack configuration at build-time. Final configuration can be overridden by webpack.override.js

Create this file in any of the following:

  • In any domain directory under src/

  • In any node module's reactium-plugin/ directory.

  • In any directory within a module under reactium_modules/

gulp.config.override.js

Override: .core/gulp.config.js

  • Change build src/dest and port

Create this file in any of the following:

  • Project root

  • In any node module's reactium-plugin/ directory.

  • In any directory under src/

  • In any directory within a module under reactium_modules/

gulp.tasks.override.js

Override: .core/gulp.tasks.js

  • Add pre or post build tasks. Replace built-in tasks.

  • See reactium-gulp.js DDD artifact for more portable / modular way to approach changing gulp tasks.

Create this file in the project root.

manifest.config.override.js

Override: .core/reactium-config.js

  • Add component search context.

  • Add application dependencies to dependency module.

Create this file in any of the following:

  • Project root

  • In any node module's reactium-plugin/ directory.

  • In any directory under src/

  • In any directory within a module under reactium_modules/

webpack.override.js

Override: .core/webpack.config.js

  • Make changes to the Webpack bundling process

  • Use this when you want access directly to change the webpack configuration data structure.

  • See reactium-webpack.js DDD artifact for more portable / modular way to approach manipulating webpack configuration.

Create this file in any of the following:

  • Project root

  • In any node module's reactium-plugin/ directory.

  • In any directory under src/

  • In any directory within a module under reactium_modules/

webpack.umd.override.js

Override: .core/webpack.config.umd.js

  • Make changes to the Webpack bundling process for UMD (Universal Module Definition) modules, created automatically by umd.js DDD artifacts.

  • Use this when you want access directly to change the webpack configuration data structure.

  • See reactium-webpack.js DDD artifact for more portable / modular way to approach manipulating webpack configuration.

Create this file in any of the following:

  • Project root

  • In any node module's reactium-plugin/ directory.

  • In any directory under src/

  • In any directory within a module under reactium_modules/

babel.config.js

Override: .core/babel.config.js

  • Add Babel presets / plugins.

  • Add module alias for both server / compiled front-end.

Must exist in the project root.

gulp.config.override.js

Exports a function that takes core gulp configuration object as a parameter, and returns configuration object used by gulp tasks.

In some case Webpack uses the gulp config.

/gulp.config.override.js
module.exports = config => {

    // Electron configuration
    config.dest.electron = 'build-electron';
    config.dest.static = 'build-electron/app/public';
    config.electron = {
        config: {
            width: 1280,
            height: 1024,
            show: false,
            title: 'App Title',
            backgroundColor: '#000000',
        },
        devtools: true,
    };

    // Disable auto launch of default browser
    config.open = false;

    return config;
};

gulp.tasks.override.js

Exports a function that takes an object defining core gulp tasks as a parameter, and returns and object whose properties define the tasks run by gulp.

Code example

manifest.config.override.js

Exports a function that takes configuration the manifest-tools use to build src/manifest.js as a parameter, and returns new manifest configuration.

/manifest.config.override.js
module.exports = config => {
    // Disable code splitting for Electron projects
    config.contexts.components.mode = 'sync';
    config.contexts.common.mode = 'sync';
    config.contexts.toolkit.mode = 'sync';
    config.contexts.core.mode = 'sync';

    return config;
};

webpack.override.js

Exports a function that takes the core webpack configuration as a parameter, and returns an object that will be used for webpack to build the js bundle.

/webpack.override.js
const webpack = require('webpack');
const path = require('path');

/**
 * Passed the current webpack configuration from core
 * @param  {Object} webpackConfig the .core webpack configuration
 * @return {Object} your webpack configuration override
 */
module.exports = webpackConfig => {
    const newWebpackConfig = Object.assign({}, webpackConfig);

    newWebpackConfig.entries['entry'] = path.resolve('/path/to/my/entry');
  

    newWebpackConfig.plugins.push(new webpack.ContextReplacementPlugin(/^my-context/, context => {
      context.request = path.resolve('./src/app/my-context');
    }));

    return newWebpackConfig;
};

webpack.umd.override.js

Serves the same purpose at the webpack.override.js except overrides the configuration used for each UMD (Universal Module Definition) module that is created by making a umd.js DDD artifact in your project. The callback function you export will receive at separate webpack configuration for each umd.js module to be created.

/webpack.umd.override.js
/**
 * Passed the current webpack configuration from core
 * @param  {Object} umd module manifest configuration, generated when
 * umd files are located.
 * @param  {Object} webpackConfig the .core webpack configuration
 * @return {Object} your webpack configuration override
 */
module.exports = (umd, webpackConfig) => {
    if (umd.libraryName === 'media-uploader') {
        delete webpackConfig.module.rules;
    }
    return webpackConfig;
};

To see what is generated for each umd in the manifest, look in .tmp/umd-manifest.json after running the build (npm run build). This will contain all the objects generated when looking for umd.js DDD artifacts. See the umd-config.json DDD artifact for more information on controlling this behavior.

babel.config.js

Required and provided by default.

Imports babel configuration and exports the configuration used by Webpack and babel-node.

/babel.config.js
const config = require('./.core/babel.config');

// To add a module resolver for node and webpack
const path = require('path');

const moduleResolver = config.plugins.find(plugin => plugin[0] === 'module-resolver');
moduleResolver[1].alias['redux-addons'] = './src/app/redux-addons';

module.exports = config;

Node/Express

Important to dev-leads, ops and backend devs, there are a number of ways you can change the behavior of the core express server without hacking .core.

Express Middleware

To add or change the stack of Node / Express middleware for the running server, create a src/app/server/middleware.js file, which should export a function taking an array of express middlewares as an argument, and returns the modified list of middlewares.

In this way, you can add/change routing, security configuration, etc to your hearts content.

Express Middleware Example #1:

/src/app/server/middleware.js
module.exports = expressMiddlewares => {

    // Simple Logger
    const mySimpleRequestLogger = (req, res, next) => {
        console.log('SIMPLE LOGGER: REQUEST '+req.path);
        next();
    };

    return [
        {
            name: 'mySimpleRequestLogger',
            use: mySimpleRequestLogger,
        },
        ...expressMiddlewares,
    ];
};

Express Middleware Example #2:

/src/app/server/middleware.js
// Health check route handler

const express = require('express');
const router = express.Router();

const healthy = router.get('/healthcheck', (req, res) => {
    res.status(200).send('ok');
});

module.exports = expressMiddlewares => {
    return [
        {
            name: 'myRouteHandler',
            use: router,
        },
        ...expressMiddlewares,
    ];
};

Express Middleware Example #3:

/src/app/server/middleware.js
// More secure Cross Origin Request Sharing for production:

const cors = require('cors');

module.exports = expressMiddlewares => {
    return expressMiddlewares.map(mw => {
        // no change
        if (nw.name !== 'cors' || !('CORS_ORIGIN' in process.env)) {
            return mw;
        }

        // enforce origin
        return {
            name: 'cors',
            use: cors({
                origin: process.env.CORS_ORIGIN,
            }),
        };
    });
};

Application Defines

Node/Express global.defines variables can be set by creating a src/app/server/defines.js file that exports a JavaScript object.

The file will also be used in constructing a Webpack defines plugin values.

Theoretically, your server-side and FE (front-end) code could make reference to values specified here.

Contrived src/app/server/defines.js:

/src/app/server/defines.js
module.exports = {
    foo: 'bar',
};

Isomorphic Define JS somewhere in front-end React code:

/src/app/server/defines.js
let fooValue;
if (typeof window !== 'undefined') {
    fooValue = foo; // webpack define plugin
} else {
    fooValue = defines.foo; // node express global
}

Environment Variables

Reactium uses the following environment variables:

Variable

Description

PORT

For environments where running application is specified by PORT var

APP_PORT

For environments where running application is specified by APP_PORT var

PORT_VAR

Tells express where to look in environment variable to get the HTTP port setting

DEBUG

Enable or Disable logging

WEBPACK_RESOURCE_BASE

Used to dynamically define the base URL where webpack bundles are served.

PUBLIC_DIRECTORY

Change the directory that express will serve static assets from

ACTINIUM_APP_ID

Used to identify the application when connecting Reactium to Actinium API. Defaults to Actinium

REST_API_URL

Used when connecting Reactium to a REST API or Actinium

PROXY_ACTINIUM_API

When set to off, disables api proxy, and API calls will got direct to the API server.

PROXY_API_PATH

Defaults to /api. The path used to proxy to the API server. Use in tandem with REST_API_URL to control API behavior.

ACTINIUM_API_ENABLED

When set to off, Actinium/Parse API will not be loaded.

WEBPACK_RESOURCE_BASE

Use this environment variable when you plan to upload your webpack assets to a CDN. It defaults to serving webpack assets from the document root at /assets/js/. If you wanted to upload the js folder to cdn.example.com for example, you might start the server like so:

WEBPACK_RESOURCE_BASE=https://cdn.example.com/js/ npm start

Default: /assets/js/

PORT

Any TCP port appropriate for HTTP protocol (port.proxy in ./core/gulp.config.js) where running application port is specified by PORT var.

Default: 3030

Some environments like Heroku automatically set this value

APP_PORT

Any TCP port appropriate for HTTP protocol (port.proxy in ./core/gulp.config.js) where running application port is specified by APP_PORT var.

Default: 3030

Some environments automatically set this value

DEBUG

Logging can be helpful for troubleshooting server-side rendering issues.

Server-side logging can drive front-end devs batty

  • off to suppress logging to STDOUT

  • on to enable logging

Default: off

PUBLIC_DIRECTORY

Full-path to public assets directory used the static middleware module.

If you have changed the build process to output static assets (js/css/images, etc) you will want to specify this value.

Default: ./public

ACTINIUM_APP_ID

Used by @atomic-reactor/reactium-api Reactium module, specify the Actinium Application ID used by Actinium API.

Default: Actinium

REST_API_URL

Used by @atomic-reactor/reactium-api Reactium module, specify the fully qualified URL to the Actinium API server.

By default, this URL is proxied from any request on the Reactium host with path prefix /api. e.g. https://myreactiumhost/api proxies requests to https://myactinumhost/api

Default: http://localhost:9000/api

While this default value eases local development, it is an unlikely value for production.

PROXY_ACTINIUM_API

Used by @atomic-reactor/reactium-api Reactium module, can be used to disable the proxy middleware to the Actinium API.

  • on: Actinium API proxy is enabled. API calls direct through Reactium back-end and are proxied to Actinium API.

  • off: Actinium API proxy is disabled. API calls go direct to Actinium API from the Reactium front-end.

Default: on

By proxying through the Reactium back-end, you won't have to worry about CORS.

PROXY_API_PATH

Used by @atomic-reactor/reactium-api Reactium module, can be used to set the front-end path that proxies to the Actinium API.

Default: /api

ACTINIUM_API_ENABLED

Used by @atomic-reactor/reactium-api Reactium module, can be used to disable just the Actinium API.

  • on: Actinium API is enabled

  • off: Actinium API is disabled

Default: on

You can also uninstall the module, using arcli.

Single Page App Template

The default templates are good for simple SPAs, but inevitably you will need to provide a different template for rendering your application's index.html.

To do so, generate server templates using ARCLI:

reactium server template

Modify the newly created templates found in: /src/app/server/template directory

Reactium core will use your custom templates to serve your SPA so long as your template version string satisfies the semver property found for your @atomic-reactor/reactium-core, in your reactiumDependencies section of package.json.

You may need to update these templates after major and minor version updates of core.

To find out the version and semver:

reactium version

Important: replace the version property string found in the SPA templates with the version number found in reactium_modules/@atomic-reactor/reactium-core/reactium-config.js

Static Build Template

There is a /src/index-static.html file provided, which can be used to compile a static index.html file when running static build:

$ npm run static

This is for Reactium applications that will be served by another web-server like Apache, Nginx, Tomcat, etc.

Supports only front-end rendered React

Last updated