1 | # Create Mashup App
|
2 |
|
3 | Application bootstrapper and build abstraction for rapid prototyping of Targetprocess UI mashups & integration mashups.
|
4 |
|
5 | Main philosophy is _'run 1 command and start hacking'_.
|
6 |
|
7 | 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.
|
8 |
|
9 | In other words, you:
|
10 |
|
11 | - don't have to be familiar with [`tau.mashups`](https://dev.targetprocess.com/docs/create-first-mashup) API
|
12 | - don't need to write long and tiresome typescript, babel, PostCSS and other configurations
|
13 | - don't need to bang your head against the wall thinking about how to exclude modules provided by mashup API from your resulting bundle.
|
14 |
|
15 | 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.
|
16 |
|
17 | ### What's included
|
18 |
|
19 | - [`babel 7`](https://github.com/rollup/rollup-plugin-babel) 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.
|
20 | - [TypeScript](https://www.typescriptlang.org/) via [`@babel/preset-typescript`](https://babeljs.io/docs/en/babel-preset-typescript) that works out of the box. You can also specify your own `tsconfig.json`.
|
21 | - [`postcss`](https://github.com/egoist/rollup-plugin-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](https://github.com/css-modules/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.
|
22 | - Entry point wrapper for your code that calls `tau.mashups` API for you behind the scenes, adds all dependencies you list, and replaces `import`s of modules with appropriate TP mashup dependencies you mark with `provideModule`.
|
23 | - Tree-shaking that works out of the box and fast build times brought to you by [rollup](https://rollupjs.org).
|
24 | - (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 `baseUrl` that points to a location where your chunks are avaliable.
|
25 |
|
26 | ### Installation
|
27 |
|
28 | Install globally (to generate new application):
|
29 |
|
30 | ```bash
|
31 | > npm install -g create-mashup-app
|
32 | ```
|
33 |
|
34 | Install as dependency (to handle build and dev server):
|
35 |
|
36 | ```bash
|
37 | > cd <your-project-folder>
|
38 | > npm install --save-dev create-mashup-app
|
39 | ```
|
40 |
|
41 | ### Usage
|
42 |
|
43 | #### Create new app from scratch:
|
44 |
|
45 | ```bash
|
46 | > create-mashup-app generate [folder]
|
47 | ```
|
48 |
|
49 | This command generates a barebones mashup, with all build configuration abstracted away behind `create-mashup-app` dependency.
|
50 |
|
51 | 3 templates are supported as of the moment:
|
52 |
|
53 | - **minimal**
|
54 |
|
55 | `package.json`, [`mashup.json`](#mashupjson) 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.
|
56 |
|
57 | - **react**
|
58 |
|
59 | 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`.
|
60 |
|
61 | - **typescript**
|
62 |
|
63 | Same as minimal, but with support for typescript enabled via [custom webpack config transform](#custom-webpack-config), appropriate `tsconfig.json` and `index.ts` as entry point.
|
64 |
|
65 | #### Build existing app:
|
66 |
|
67 | ```bash
|
68 | > create-mashup-app build [folder] [--no-minify] [--no-dependency-replace] [--target=mashup] [--baseUrl=<url>]
|
69 | ```
|
70 |
|
71 | This assumes that `[folder]` or current directory has correct mashup config.
|
72 |
|
73 | Build output is written to `dist` folder in `[folder]`.
|
74 |
|
75 | 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`.
|
76 |
|
77 | If you want to build a mashup to be included directly into TP straight away, you can use `--target=mashup` with `create-mashup-app build`, or specify `"target": "mashup"` in your `mashup.json`.
|
78 |
|
79 | If your mashup uses dynamic imports and code splitting, be sure to provide correct `--base-url` of your chunks.
|
80 |
|
81 | #### Run development server on existing app:
|
82 |
|
83 | ```bash
|
84 | > create-mashup-app devServer [folder] [--port <port>] [--no-minify] [--no-dependency-replace]
|
85 | ```
|
86 |
|
87 | This assumes that `[folder]` or current directory has correct mashup config.
|
88 |
|
89 | Dev server serves your `dist` directory statically. As such, any previous build assets may be overwritten.
|
90 |
|
91 | #### Mashup configuration
|
92 |
|
93 | Your app should provide mashup configuration in any of the ways supported by [`cosmiconfig`](https://github.com/davidtheclark/cosmiconfig), i.e. via:
|
94 |
|
95 | - `mashup` property in your `package.json`
|
96 | - `.mashuprc`/`.mashuprc.json`/`.mashuprc.yaml`/`mashuprc.yml` file
|
97 | - `.mashuprc.js` or `mashup.config.js` file that exports an object
|
98 |
|
99 | `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.
|
100 |
|
101 | A typical mashup config will look like this:
|
102 |
|
103 | ```json
|
104 | {
|
105 | "name": "My Awesome Mashup",
|
106 | "moduleName": "tp3/integrations/my-awesome-mashup",
|
107 | "entry": "src/index.js",
|
108 | "dependencies": [
|
109 | {"mashupDependency": "react", "provideModule": "react"},
|
110 | {"mashupDependency": "tp3/api/settings/v1"},
|
111 | {"mashupDependency": "Underscore", "provideModule": "underscore"},
|
112 | ...
|
113 | ]
|
114 | }
|
115 | ```
|
116 |
|
117 | Below is the breakdown for each of the fields:
|
118 |
|
119 | - `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.
|
120 |
|
121 | - `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.
|
122 |
|
123 | - `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`**:
|
124 |
|
125 | ```js
|
126 | export function initialize() {
|
127 | console.log('I am a mashup!');
|
128 | }
|
129 | ```
|
130 |
|
131 | or
|
132 |
|
133 | ```js
|
134 | module.exports = {
|
135 | initialize() {
|
136 | console.log('I am a mashup that is afraid of using ES2015 import/export!');
|
137 | }
|
138 | };
|
139 | ```
|
140 |
|
141 | This function is called when your mashup and all of its dependencies are loaded.
|
142 |
|
143 | - `dependencies` - An array of your mashup dependencies. The list of all avaliable dependencies can be found in targetprocess [`publicModules.registry.js`](https://github.com/TargetProcess/TP/blob/develop/Code/Main/Tp.Web/JavaScript/tau/scripts/tp/publicModules.registry.js) You can specify each dependency as a string or as an object with `mashupDependency` key:
|
144 |
|
145 | ```js
|
146 | ...
|
147 | "dependencies": [
|
148 | "react",
|
149 | {"mashupDependency": "tp3/api/settings/v1"}
|
150 | ]
|
151 | ...
|
152 | ```
|
153 |
|
154 | 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:
|
155 |
|
156 | ```js
|
157 | import TpSettings from 'tp3/api/settings/v1';
|
158 | import React from 'react';
|
159 | ...
|
160 | ```
|
161 |
|
162 | You can additionally specify `provideModule` key for your dependency object to create a different alias for that particular mashp module:
|
163 |
|
164 | ```js
|
165 | ...
|
166 | "dependencies": [
|
167 | {"mashupDependency": "Underscore", "provideModule": "underscore"},
|
168 | {"mashupDependency": "react", "provideModule": "tp-react"},
|
169 | {"mashupDependency": "tp3/api/settings/v1", "provideModule": "@targetprocess/api/settings"}
|
170 | ]
|
171 | ...
|
172 | import _ from 'underscore'; // TP module 'Underscore' is used
|
173 | import React from 'react'; // react from your own node_modules is used
|
174 | import TpReact from 'tp-react'; // TP module 'react' is used
|
175 | import SettingsApi from '@targetprocess/api/settings'; // TP module 'tp3/api/settings/v1' is used
|
176 | ```
|
177 |
|
178 | 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.
|
179 |
|
180 | 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.
|
181 |
|
182 | #### Tree-shaking
|
183 |
|
184 | As of 2.0, `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.
|
185 |
|
186 | If your bundle size is still too large, you can try to use `pureExternalModules` and `propertyReadSideEffects` rollup config options (more on them [here](https://rollupjs.org/guide/en#danger-zone)) to perform more aggressive tree-shaking.
|
187 |
|
188 | #### Code splitting
|
189 |
|
190 | 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.
|
191 | `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 and specify a correct URL where your dynamically-loaded chunks are available.
|
192 |
|
193 | Code splitting will also produce commons chunks, i.e. chunks with code that is imported by several of dynamically-loaded modules. Since there is no way to verify if commons chunk is used by your entry point chunk as of 2.0, all commons chunks are inlined with your entry point bundle, and reused lated by all dynamically loaded chunks.
|
194 |
|
195 | **(hopefully temporary) Code splitting assumes that webpage has a correct implementation of A+ Promises available from `window.Promise`. If you cannot make sure Promise is polyfilled, avoid using dynamic imports in your code.**
|
196 |
|
197 | #### ~~Providing public API~~ (temporarily broken as of 2.0 release)
|
198 |
|
199 | 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:
|
200 |
|
201 | 1. Create a module which will be exposed publicly e.g. `src/api/mashup-public-api.js` with the following content:
|
202 |
|
203 | ```js
|
204 | export default {
|
205 | sayHello() {
|
206 | console.log('hello from mashup public API');
|
207 | }
|
208 | };
|
209 | ```
|
210 |
|
211 | 2. 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:
|
212 |
|
213 | ```js
|
214 | export default {
|
215 | 'tau/api/hello/v1': () => require('./mashup-public-api').default
|
216 | // any other public modules can go here
|
217 | };
|
218 | ```
|
219 |
|
220 | Keys of the exported objects are the names of the modules which can be used by other mashups.
|
221 |
|
222 | **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)**
|
223 |
|
224 | 3. Add `publicApi` property in the `mashup.json` file:
|
225 |
|
226 | ```json
|
227 | {
|
228 | ...
|
229 | "publicApi": "src/api/index.js"
|
230 | ...
|
231 | }
|
232 | ```
|
233 |
|
234 | Now you can use this module in another mashup:
|
235 |
|
236 | ```js
|
237 | tau.mashups.addDependency('tau/api/hello/v1').addMashup(function(api) {
|
238 | api.sayHello();
|
239 | });
|
240 | ```
|
241 |
|
242 | #### Customizing
|
243 |
|
244 | `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.
|
245 |
|
246 | ##### Custom `.babelrc`
|
247 |
|
248 | You can place `.babelrc` file in your root folder and provide your own [babel configuration](https://babeljs.io/docs/usage/babelrc/). 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.
|
249 |
|
250 | `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`.
|
251 |
|
252 | ##### Custom PostCSS config
|
253 |
|
254 | Much like `.babelrc`, you can provide your own custom [postcss config](https://github.com/michael-ciniawsky/postcss-load-config) in any manner you seem fit. Preferred way is through `postcss.js.config`.
|
255 |
|
256 | ##### Custom rollup config
|
257 |
|
258 | _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`](#eject) instead._
|
259 |
|
260 | You can specify `configTransform` property in mashup config that points to JS file:
|
261 |
|
262 | ```json
|
263 | {
|
264 | ...
|
265 | "configTransform": "./webpack.config.transform.js",
|
266 | }
|
267 | ```
|
268 |
|
269 | or you can provide it as a function in `mashup.config.js`/`.mashuprc.js`:
|
270 |
|
271 | ```js
|
272 | module.exports = {
|
273 | name: '...',
|
274 | ...
|
275 | configTransform: function(config) {
|
276 | ...
|
277 | }
|
278 | }
|
279 | ```
|
280 |
|
281 | Config transform will be invoked with basic rollup config object as parameter and is expected to return a new, transformed rollup config.
|
282 |
|
283 | For example, if youo want to add `rollup-plugin-string`, you can do it with the following transform:
|
284 |
|
285 | ```js
|
286 | const string = require('rollup-plugin-string');
|
287 |
|
288 | module.exports = function(config) {
|
289 | return {
|
290 | ...config,
|
291 | plugins: [
|
292 | ...config.plugins,
|
293 | string({
|
294 | include: '**/*.html'
|
295 | })
|
296 | ]
|
297 | };
|
298 | };
|
299 | ```
|
300 |
|
301 | ##### Eject
|
302 |
|
303 | **Eject is currently not supported, but will be added in future versions.**
|
304 |
|
305 | `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`.
|
306 |
|
307 | This is irreversible.
|