create-mashup-app

3.2.0 • Public • Published

Create Mashup App

Application bootstrapper and build abstraction for rapid prototyping of Targetprocess UI mashups & integration mashups.

Main philosophy is 'run 1 command and start hacking'.

While your build is performed by create-mashup-app, you are guaranteed to have a stable and tried build and dev server configuration that just works (hopefully) out of the box and covers most of the cases.

In other words, you:

  • don't have to be familiar with tau.mashups API
  • don't need to write long and tiresome typescript, babel, PostCSS and other configurations
  • don't need to bang your head against the wall thinking about how to exclude modules provided by mashup API from your resulting bundle.

Moreover, you can build your front-end code and serve it statically from a separate server instance running in the cluster, and manage mashup deployments by yourself.

What's included

  • babel 7 with latest env and react presets for using latest ES stuff and JSX, respectively. Just create a .babelrc in your root if you want to customize it.
  • TypeScript via rollup-plugin-typescript2 that works out of the box. You can also specify your own tsconfig.json and .d.ts type definitions. You can include the following type definiton to make it work with CSS modules:
declare module '*.css' {
  const cssModule: Record<string, string>;
  export default cssModule;
}
  • postcss for your CSS-related stuff, with a sprinkle of useful plugins for importing, nesting, variables, mixins and basically everything else you might want to expect from modern CSS. It also has css modules enabled, and appends name and version from your package.json to each CSS class you import so that you avoid polluting global CSS scope. It also has inject option set to true, so any styles you import magically appear as style tags on the page. Having your own postcss.config.js (or .postcssrc or any other supported config format) in root allows you to specify your own configuration.
  • Entry point wrapper for your code that calls tau.mashups API for you behind the scenes, adds all dependencies you list, and replaces imports of modules with appropriate TP mashup dependencies you mark with provideModule.
  • Tree-shaking that works out of the box and fast build times brought to you by rollup.
  • (beta) Code splitting support and minimal external chunks loader for your mashup. You can use dynamic import() syntax anywhere in your code - just make sure to provide correct basePath that points to a location where your chunks are avaliable.

Installation

Install globally (to generate new application):

> npm install -g create-mashup-app

Install as dependency (to handle build and dev server):

> cd <your-project-folder>
> npm install --save-dev create-mashup-app

Usage

Create new app from scratch:

> create-mashup-app generate [folder]

This command generates a barebones mashup, with all build configuration abstracted away behind create-mashup-app dependency.

3 templates are supported as of the moment:

  • minimal

package.json, mashup.json and nothing else but your code in src/index.js. Bare minumum for when you need to start fresh, or when you're implimenting something relatively simple.

  • react

Generates a bit more complex mashup with dummy react component that gets inserted into document when mashup is loaded. Includes a configured test environment and 1 dummy test, basic rules for linting and useful commands for executing them in package.json.

  • typescript

Same as minimal, but with support for typescript, and index.ts as entry point.

Build existing app:

> create-mashup-app build [folder] [--no-minify] [--no-dependency-replace] [--mashup] [--basePath=<url>]

This assumes that [folder] or current directory has correct mashup config.

Build output is written to dist folder in [folder].

Build by default assumes that created mashup is used as an integration, i.e. has an integration on TP side that registers js file produced in build as registerExternalModule.

If you want to build a mashup to be included directly into TP straight away, you can use --mashup with create-mashup-app build, or specify "target": "mashup" in your mashup.json.

If your mashup uses dynamic imports and code splitting, be sure to provide correct --base-url of your chunks.

Run development server on existing app:

> create-mashup-app devServer [folder] [--port <port>] [--no-minify] [--no-dependency-replace]

This assumes that [folder] or current directory has correct mashup config.

Dev server serves your dist directory statically. As such, any previous build assets may be overwritten.

Mashup configuration

Your app should provide mashup configuration in any of the ways supported by cosmiconfig, i.e. via:

  • mashup property in your package.json
  • .mashuprc/.mashuprc.json/.mashuprc.yaml/mashuprc.yml file
  • .mashuprc.js or mashup.config.js file that exports an object

mashup.config.js is the root configuration file for your mashup while it's controlled by create-mashup-app. It is generated automatically when you create a new app, and consumed every time you build or run dev server with create-mashup-app on an existing app.

A typical mashup config will look like this:

{
    "name": "My Awesome Mashup",
    "moduleName": "tp3/integrations/my-awesome-mashup",
    "entry": "src/index.js",
    "cssVariables": {
        "spacing": "5px"
    },
    "dependencies": [
        {"mashupDependency": "react", "provideModule": "react"},
        {"mashupDependency": "tp3/api/settings/v1"},
        {"mashupDependency": "Underscore", "provideModule": "underscore"},
        ...
    ]
}

Below is the breakdown for each of the fields:

  • name - This is the visible user-friendly name for your mashup. It will only be used as mashup name in Mashup Manager, so feel free to choose whatever you seem fit.

  • moduleName - This is module identifier for your mashup. While there are no theoretical boundaries on what you can use as module name, you should try to provide a technically succint namespaced value here.

  • entry - This field points to the entry point of your mashup. Typically, mashups are created by calling tau.mashups.addDependency(...).addMashup('awesome-mashup', function(dependency) {...}). create-mashup-app, however, abstracts that API away, by using dependencies from your mashup.json, simplifying their usage and adding support for bundle module replacement. The file specified in entry must contain a named export function initialize:

  • cssVariables can be used to provide additional variables for poscss-simple-vars. You can use a string path to constants file (it is expected to export an object with variables via module.exports). You can also use a function from mashup.config.js that returns an object, or provide the object in-place.

export function initialize() {
  console.log('I am a mashup!');
}

or

module.exports = {
  initialize() {
    console.log('I am a mashup that is afraid of using ES2015 import/export!');
  }
};

This function is called when your mashup and all of its dependencies are loaded.

  • dependencies - An array of your mashup dependencies. The list of all avaliable dependencies can be found in targetprocess publicModules.registry.js You can specify each dependency as a string or as an object with mashupDependency key:
...
"dependencies": [
    "react",
    {"mashupDependency": "tp3/api/settings/v1"}
]
...

All mashup dependencies are available for importing as if they were regular modules. If you have a module with the same name you are importing from node_modules, it will be replaced:

import TpSettings from 'tp3/api/settings/v1';
import React from 'react';
...

You can additionally specify provideModule key for your dependency object to create a different alias for that particular mashp module:

...
"dependencies": [
    {"mashupDependency": "Underscore", "provideModule": "underscore"},
    {"mashupDependency": "react", "provideModule": "tp-react"},
    {"mashupDependency": "tp3/api/settings/v1", "provideModule": "@targetprocess/api/settings"}
]
...
import _ from 'underscore'; // TP module 'Underscore' is used
import React from 'react';  // react from your own node_modules is used
import TpReact from 'tp-react'; // TP module 'react' is used
import SettingsApi from '@targetprocess/api/settings';  // TP module 'tp3/api/settings/v1' is used

When your code is bundled normally, all node_modules imports are included with your bundle. For example, with the above code you get entire react codebase in your bundle.

Some library dependencies, including react and react-dom, are already provided by TP mashup API, so carrying your own copy of React or jQuery with your mashup is very inefficient.

Tree-shaking

create-mashup-app uses rollup for bundle generation, which statically analyzes your code for imports and exports. Refer to https://rollupjs.org/guide/en#tree-shaking for more details.

If your bundle size is still too large, you can try to use pureExternalModules and propertyReadSideEffects rollup config options (more on them here) to perform more aggressive tree-shaking.

Code splitting

Code splitting occurs whenever you use dynamic import statement in your code. Each dynamic import spawns a separate chunk that will be loaded on runtime when requested. create-mashup-app bundles a tiny dependency loader with your code that can load dynamic imports. It also requires that you build your app with --basePath argument or basePath mashup config option, containing a correct base URL from your dynamically-loaded chunks. For TP usage, this is most commonly a relative URL, like /svc/my-awesome-mashup/.

When determining chunk URLs, our tiny dependency loader will try to use tau.mashups.resolveIntegrationUrl, if it's present (for more info on how it works, see this lengthy but very important doc). For it to version your mashup URLs correctly, you also need to specify integrationName in your mashup config. If you don't, mashup name (from mashup config name or pakcage.json package name) will be used instead, but a warning will be issued on build since it is recommended to specify integration URL explicitly to avoid unexpected behavior because of some minor differences between mashup name and integration name in your mashup's docker image config.

Code splitting will also try to isolate common chunks and inline common chunks used by entry point with entry point bundle. That means that you should try to minimize usage of common imports and utility modules within your entry point to make your entry point bundle as tiny as possible. See code splitting 101 in docs for more info.

Code splitting assumes that webpage has a correct implementation of A+ Promises available from window.Promise, which currently shouldn't be an issue.

Providing public API

A mashup created by create-mashup-app can register public modules which can be used by other mashups. In order to provide one or more public modules, you need to perform a few things:

  1. Create a module which will be exposed publicly e.g. src/api/mashup-public-api.js with the following content:
export default {
  sayHello() {
    console.log('hello from mashup public API');
  }
};
  1. Create a javascript file which provides all public API modules exposed by the mashup, e.g. a file src/api/index.js with the following content:
module.exports = {
  'tau/api/hello/v1': () => require('./mashup-public-api').default
  // any other public modules can go here
};

Only CommonJS is allowed inside public api entry file.

Keys of the exported objects are the names of the modules which can be used by other mashups.

Please note that this file should not have any dependencies in a form of require or import statements (the file is loaded and evaluated before dependencies specified in mashup.json are initialized)

  1. Add publicApi property in the mashup.json file:
{
    ...
    "publicApi": "src/api/index.js"
    ...
}

Now you can use this module in another mashup:

tau.mashups.addDependency('tau/api/hello/v1').addMashup(function(api) {
  api.sayHello();
});

Customizing

create-mashup-app is quite narrow in the way of customizing your build process. This is intentional. The basic idea is that you get a default configuration that just works, along with some best practices on how to handle your mashup integration.

Custom .babelrc

You can place .babelrc file in your root folder and provide your own babel configuration. Note that doing so will override default presets, so you'll have to include env, react and other transforms you consider useful for your app.

create-mashup-app uses babel 7. Make sure you use correct plugin versions, e.g. @babel/plugin-proposal-object-rest-spread and not babel-plugin-transform-object-rest-spread.

Custom PostCSS config

Much like .babelrc, you can provide your own custom postcss config in any manner you seem fit. Preferred way is through postcss.js.config.

Custom rollup config

Customizing rollup config generally goes a bit against the main philosophy of create-mashup-app. It's fine for smaller tweaks, like adding new plugins, but if you need to drastically customize build process, consider using eject instead.

You can specify configTransform property in mashup config that points to JS file:

{
    ...
    "configTransform": "./rollup.config.transform.js",
}

or you can provide it as a function in mashup.config.js/.mashuprc.js:

module.exports = {
    name: '...',
    ...
    configTransform: function(config) {
        ...
    }
}

Config transform will be invoked with basic rollup config object as parameter and is expected to return a new, transformed rollup config.

For example, if youo want to add rollup-plugin-string, you can do it with the following transform:

const string = require('rollup-plugin-string');

module.exports = function(config) {
  return {
    ...config,
    plugins: [
      ...config.plugins,
      string({
        include: '**/*.html'
      })
    ]
  };
};
Eject

Eject is currently not supported, but will be added in future versions.

create-mashup-app eject will remove create-mashup-app from your package dependencies, transfer all required transitive dependencies to your own package.json and will generate all configuration files required to run build and dev server without it, much like eject in create-react-app.

This is irreversible.

Known issues

  1. Sourcemaps are broken as of 3.0.0-alpha.1 version.

  2. Dependencies are replaced incorrectly in object literals with concise property syntax. An expression like

import configurator from 'tau/configurator';

const obj = {
  configurator
}

will be replaced with something like

const obj = {
  window.__layoutrenderer001_we5eh_deps['configurator']
}

and such replacement will lead to syntax error during build. The error also may occur due to code-splitting in places where reference to variable with content of dynamic chunk is used. 3. create-mashup-app won't work nicely with React.lazy. React internal check expects that default export will be placed into default key of module object. Rollup will generate an object with default key only if some named exports are placed in the same file. If file contains only default export, Rollup may return it directly from generated module close. In such case it will be necessary to adjust dynamic import as follows:

    const LazyComponent = React.lazy(() => import('./Component').then(component => ({default: component})))

Readme

Keywords

none

Package Sidebar

Install

npm i create-mashup-app

Weekly Downloads

116

Version

3.2.0

License

MIT

Unpacked Size

141 kB

Total Files

108

Last publish

Collaborators

  • targetprocess-user