{"_id":"@airy/maleo","_rev":"77-609d885384ef3686e5a34263c4b65992","name":"@airy/maleo","dist-tags":{"canary":"0.3.1-canary.36","latest":"0.3.0","next":"0.0.4-canary.49"},"versions":{"0.0.1-canary.51":{"name":"@airy/maleo","version":"0.0.1-canary.51","description":"Un-opinionated Universal Rendering Javascript Framework","repository":{"type":"git","url":"git+https://github.com/airyrooms/maleo.js","bugs":"https://github.com/airyrooms/maleo.js/issues"},"author":"Airy","license":"MIT","private":false,"sideEffects":false,"scripts":{"build":"rm -rf dist && gulp","watch":"gulp watch","prepublish":"yarn build"},"bin":"lib/bin/maleo.js","dependencies":{"@babel/core":"7.1.2","@babel/plugin-proposal-class-properties":"7.1.0","@babel/plugin-proposal-decorators":"7.1.2","@babel/plugin-proposal-object-rest-spread":"7.0.0","@babel/plugin-syntax-dynamic-import":"7.0.0","@babel/plugin-transform-runtime":"7.1.0","@babel/preset-env":"7.1.0","@babel/preset-react":"7.0.0","@babel/runtime":"7.1.2","@babel/runtime-corejs2":"7.2.0","babel-loader":"8.0.4","case-sensitive-paths-webpack-plugin":"2.1.2","es6-promise":"4.2.5","eventsource":"^1.0.7","express":"4.16.4","figlet":"1.2.1","friendly-errors-webpack-plugin":"1.7.0","hard-source-webpack-plugin":"0.13.1","helmet":"3.14.0","isomorphic-fetch":"2.2.1","loader-utils":"1.1.0","node-notifier":"5.2.1","nodemon":"1.18.4","react":"^16.8.4","react-dom":"16.8.4","react-helmet":"5.2.0","react-loadable":"5.5.0","react-router":"4.4.0-beta.1","react-router-config":"4.4.0-beta.6","rimraf":"2.6.3","source-map-support":"0.5.9","terser-webpack-plugin":"1.1.0","webpack":"4.29.0","webpack-bundle-analyzer":"3.0.3","webpack-cli":"3.1.2","webpack-dev-middleware":"3.4.0","webpack-hot-middleware":"2.24.3","webpackbar":"2.6.3"},"peerDependencies":{"react":"^16.8.4","react-router-dom":"^4.4.0-beta.1"},"devDependencies":{"@babel/preset-typescript":"^7.1.0","@types/webpack-bundle-analyzer":"^2.13.0","babel-plugin-module-resolver":"^3.1.1","gulp":"gulpjs/gulp.git#4.0","gulp-babel":"^8.0.0","gulp-clean":"^0.4.0","gulp-sourcemaps":"^2.6.4","gulp-uglify":"^3.0.1","gulp-watch":"^5.0.1","react":"^16.8.4","react-router-dom":"^4.4.0-beta.1"},"gitHead":"8c8b6c5839d9249b81dbcad9dc895787e9b3ef69","_nodeVersion":"10.11.0","_npmVersion":"lerna/3.7.2/node@v10.11.0+x64 (darwin)","_npmUser":{"name":"airyrooms-engineering","email":"engineering@airyrooms.com"},"_id":"@airy/maleo@0.0.1-canary.51","dist":{"integrity":"sha512-tPIxXd5NdlGeN0RITWIvD0i+n/9Q1tCVsPhzsaXbHQMAzZ9aIm92BoIsJTSUwwewY19qvZabYVrhRfor5p+qDQ==","shasum":"c33665b4efffe051c135e857d40387d3d7687e6d","tarball":"https://registry.npmjs.org/@airy/maleo/-/maleo-0.0.1-canary.51.tgz","fileCount":82,"unpackedSize":222451,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJciKbqCRA9TVsSAnZWagAAZFQP/R774AxDFZfuCy64xpqk\ntwLLNljHWKO3je8NyI0O+rop4gOz303ENuHO26nSMxQfkgx/Jrw8wNCu1tIC\ngbvMjaHFwsulvh3Obc2b/h8/YUy34OLaQoCvfsEYomPgxF7iATcAGB0JGA0M\nQjnq4qbh1dRmIt7OSRsx5w9467hGQh51U74QpPOwCsQNUKuLzCVqAf2NKJfk\nZ5YyeWW73sTsj2yOzAarSdP+/rzeT3ZLP/KHiF27bDp/d47nfkugguRYa+SS\nOmoMkR4SE8LTNfWbb692Je5P5DGNvZJAH0Dqd3PwN9GhfC97pFTa1xoJwtU7\n3fHjaEcQCIAOuwYMHfh8w6qxC8w/qEtfoicG8xmmSs6Lxr8wFzC0Bpre2YgE\nBolV+OwUBsfzUB3rdhGNUnQ5A6KRQMU7EO528YR75Vh3YgIdY3RMjxNbIu4S\nR3M7BEOC1kmbTxLBa6M0HS/7DDsl5lmi6pfgd2nMIUYhNYgsmX2Wj/cncdZw\nD1wvcH7HFYiIPyvdt6do2S9wxKiRe7MTSW9Ib+VnkdoM/mk8tFhfDJiLHX3V\nlZgiqreB0euetCBfQ83oyHZ8HcDWHPXtgb0uTBslQoEsC0Ajrp5HTvWrEo73\n9uk4eBnnZ+mQ7u9hWHYHbMFU5Yp30MYGojFO136gc+Yw8V6TZbenCYT9sQGO\n39la\r\n=Dxn8\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCQifYX4aIYfexnqQmU/ZdxXFUMG9yltWdrayJmOQm0gAIgJMrxaYuK7veiBuNlXi9j5DjjwlvtQ4faU2rT14rUQNY="}]},"maintainers":[{"name":"airyrooms-engineering","email":"engineering@airyrooms.com"}],"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/maleo_0.0.1-canary.51_1552459498540_0.26292496804297305"},"_hasShrinkwrap":false},"0.0.1-canary.49":{"name":"@airy/maleo","version":"0.0.1-canary.49","description":"Un-opinionated Universal Rendering Javascript Framework","repository":{"type":"git","url":"git+https://github.com/airyrooms/maleo.js.git","bugs":"https://github.com/airyrooms/maleo.js/issues"},"author":{"name":"Airy"},"license":"MIT","private":false,"sideEffects":false,"scripts":{"build":"rm -rf dist && gulp","watch":"gulp watch","prepublish":"yarn build","start":"node server.js"},"bin":{"maleo":"lib/bin/maleo.js"},"dependencies":{"@babel/core":"7.1.2","@babel/plugin-proposal-class-properties":"7.1.0","@babel/plugin-proposal-decorators":"7.1.2","@babel/plugin-proposal-object-rest-spread":"7.0.0","@babel/plugin-syntax-dynamic-import":"7.0.0","@babel/plugin-transform-runtime":"7.1.0","@babel/preset-env":"7.1.0","@babel/preset-react":"7.0.0","@babel/runtime":"7.1.2","@babel/runtime-corejs2":"7.2.0","babel-loader":"8.0.4","case-sensitive-paths-webpack-plugin":"2.1.2","es6-promise":"4.2.5","eventsource":"^1.0.7","express":"4.16.4","figlet":"1.2.1","friendly-errors-webpack-plugin":"1.7.0","hard-source-webpack-plugin":"0.13.1","helmet":"3.14.0","isomorphic-fetch":"2.2.1","loader-utils":"1.1.0","node-notifier":"5.2.1","nodemon":"1.18.4","react":"^16.8.4","react-dom":"16.8.4","react-helmet":"5.2.0","react-loadable":"5.5.0","react-router":"4.4.0-beta.1","react-router-config":"4.4.0-beta.6","rimraf":"2.6.3","source-map-support":"0.5.9","terser-webpack-plugin":"1.1.0","webpack":"4.29.0","webpack-bundle-analyzer":"3.0.3","webpack-cli":"3.1.2","webpack-dev-middleware":"3.4.0","webpack-hot-middleware":"2.24.3","webpackbar":"2.6.3"},"peerDependencies":{"react":"^16.8.4","react-router-dom":"^4.4.0-beta.1"},"devDependencies":{"@babel/preset-typescript":"^7.1.0","@types/webpack-bundle-analyzer":"^2.13.0","babel-plugin-module-resolver":"^3.1.1","gulp":"github:gulpjs/gulp#4.0","gulp-babel":"^8.0.0","gulp-clean":"^0.4.0","gulp-sourcemaps":"^2.6.4","gulp-uglify":"^3.0.1","gulp-watch":"^5.0.1","react":"^16.8.4","react-router-dom":"^4.4.0-beta.1"},"gitHead":"af0e177182d3150076bb62e33926bcc4838eb651","readme":"[![Build Status](https://travis-ci.org/airyrooms/maleo.js.svg?branch=master)](https://travis-ci.org/airyrooms/maleo.js)\n[![Coverage Status](https://coveralls.io/repos/github/airyrooms/maleo.js/badge.svg?branch=master)](https://coveralls.io/github/airyrooms/maleo.js?branch=master)\n[![MIT license](https://img.shields.io/badge/License-MIT-blue.svg)](https://github.com/airyrooms/maleo.js/blob/canary/LICENSE)\n[![Discord Chat](https://img.shields.io/discord/550498214789513218.svg)](https://discord.gg/9eArCQn)\n\n\n# Welcome to Maleo.js\n\nMaleo.js is an un-opinionated framework to enable Universal Rendering in JavaScript using React with no hassle.\n\nWe are here to solve the time consuming setups Universal Rendering Required.\n\n---\n\nReadme below is the documentation for the `canary` (prerelease) branch. To view the documentation for the latest stable Maleo.js version change branch to `master`\n\n---\n\n# Table of Contents\n- [Features](#features)\n- [Setup](#setup)\n- [Component Lifecycle](#component-lifecycle)\n- [Routing](#routing)\n- [Dynamic Import Component](#dynamic-import-component)\n  - [Tips](#tips)\n  - [Preloading](#preloading)\n- [Customizable Component](#customizable-component)\n  - [Custom Document](#custom-document)\n  - [Custom Wrap](#custom-wrap)\n- [Custom Configuration](#custom-configuration)\n  - [Customize Server](#customize-server)\n  - [Customize Webpack](#customize-webpack)\n  - [Customize Babel Config](#customize-babel-config)\n- [CDN Support](#cdn-support)\n- [Plugins](#plugins)\n- [FAQ](#faq)\n- [Contributing](#contributing)\n\n---\n\n## Features\n- Universal Rendering\n- Plugin based framework\n- Customizable\n\n## Setup\n\nInstall Maleo.js \n\n**NPM**\n```bash\n$ npm install --save @airy/maleo react react-router-dom\n```\n**Yarn**\n```bash\n$ yarn add @airy/maleo react react-router-dom\n```\n\nAdd this script to your `package.json`\n```json\n{\n  \"scripts\": {\n    \"dev\": \"maleo\",\n    \"build\": \"export NODE_ENV=production && maleo build\",\n    \"start\": \"export NODE_ENV=production && node .maleo/server.js\"\n  }\n}\n```\n\nCreate a routing file called `routes.js`\n```jsx\nimport React from 'react';\n\nconst App = () => (<h1>Hello World</h1>);\n\nexport default [\n  {\n    path: '/',\n    component: App,\n    key: 'root',\n  },\n];\n```\n\nAfter that you can now run `$ npm run dev` and go to `http://localhost:3000`.\n\nYou should now see your app running on your browser.\n\nBy now you should see \n- Automatic transpilation and bundling (with webpack and babel)\n- ~~Hot code reloading~~ [#17](https://github.com/airyrooms/maleo.js/issues/17)\n- Server rendering\n\nTo see how simple this is, check out the sample app!\n\n## Component Lifecycle \n\nMaleo.js added a new component lifecycle hook called `getInitialProps`, this function is called during Server Side Rendering (SSR).\n\nThis is useful especially for SEO purposes. \n\nExample for stateful component:\n```jsx\nimport React from 'react';\n\nexport default class extends React.Component {\n  static getInitialProps = async (ctx) => {\n    const { req } = ctx;\n\n    const userAgent = req ? req.headers['user-agent'] : navigator.userAgent;\n\n    // the return value will be passed as props for this component\n    return { userAgent };\n  }\n\n  render() {\n    return (\n      <div>\n        Hello World {this.props.userAgent}\n      </div>\n    );\n  }\n}\n```\n\nExample for stateless component:\n```jsx\nconst Component = (props) => (\n  <div>\n    Hello World {props.userAgent}\n  </div>\n);\n\nComponent.getInitialprops = async (ctx) => {\n  const { req } = ctx;\n\n  const userAgent = req ? req.headers['user-agent'] : navigator.userAgent;\n\n  return { userAgent };\n};\n\nexport default Component;\n```\n\n`getInitialProps` receives a context object with the following properties:\n- `req` - HTTP request object (server only)\n- `res` - HTTP response object (server only)\n- `...wrapProps` - Spreaded properties from custom Wrap\n- `...appProps` - Spreaded properties from custom App\n\n## Routing\n\nRouting is declared in a centralized route config.\nRegister all the route config in `maleo-routes.json` file.\n\nIf you put the `maleo-routes.json` files on root directory, Maleo will automatically register your route. Otherwise put path to your routes on [Maleo config](#custom-configuration).\n\nRoutes file has to export default the route configuration.\nThe route object **expected to have distinct key** to indicate the route.\n\n<table>\n  <tr>\n    <td>Key</td>\n    <td>Type</td>\n    <td>Description</td>\n  </tr>\n  <tr>\n    <td><code>path</code></td>\n    <td><code>String!</code></td>\n    <td>Routes path</td>\n  </tr>\n  <tr>\n    <td><code>page</code></td>\n    <td><code>String!</code></td>\n    <td>Path to React Component for this route path</td>\n  </tr>\n  <tr>\n    <td><code>exact</code></td>\n    <td><code>Boolean?</code> [<code>false</code>]</td>\n    <td>To make url has to match exactly the path in order to render the component. Give <code>false</code> value if the route is a wrapper route component</td>\n  </tr>\n  <tr>\n    <td><code>routes</code></td>\n    <td><code>RouteObject?</code></td>\n    <td>Nested route</td>\n  </tr>\n</table>\n\n\nFor example:\n```json\n[\n  {\n    \"page\": \"./src/MainApp\",\n    \"routes\": [\n      {\n        \"path\": \"/\",\n        \"page\": \"./src/Search\",\n        \"exact\": true\n      },\n      {\n        \"path\": \"/search\",\n        \"page\": \"./src/Search\",\n        \"routes\": [\n          {\n            \"path\": \"/search/hello\",\n            \"page\": \"./src/Detail\",\n            \"exact\": true\n          }\n        ]\n      },\n      {\n        \"path\": \"/detail\",\n        \"page\": \"./src/Detail\",\n        \"exact\": true\n      }\n    ]\n  }\n]\n```\n\n## Dynamic Import Component\nMaleo.js supports TC39 [dynamic import proposal](https://github.com/tc39/proposal-dynamic-import) for JavaScript.\n\nYou can think dynamic import as another way to split your code into manageable chunks. You can use our `Dynamic` function which utilizes [react loadable](https://github.com/jamiebuilds/react-loadable)\n\nFor Example\n```js\n// DynamicComponent.js\nimport Dynamic from '@airy/maleo/dynamic';\n\nexport default Dynamic({\n  loader: () => import( /* webpackChunkName:\"DynamicComponent\" */ './component/DynamicLoad'),\n})\n```\n\n### **Preloading**\n\nFor optimization purposes, you can also preload a component even before the component got rendered.\n\nFor example, if you want to load component when a button get pressed, you can start preloading the component when the user hovers the mouse over the button.\n\nThe component created by `Dynamic` exposes a [static `preload` method](https://github.com/jamiebuilds/react-loadable#loadablecomponentpreload).\n\n```jsx\nimport React from 'react';\nimport Dynamic from '@airy/maleo/dynamic';\n\nconst DynamicBar = Dynamic({\n  loader: () => import('./Bar'),\n  loading: LoadingCompoinent\n});\n\nclass MyComponent extends React.Component {\n  state = { showBar: false };\n\n  onClick = () => {\n    this.setState({ showBar: true });\n  };\n\n  onMouseOver = () => DynamicBar.preload();\n\n  render() {\n    return (\n      <div>\n        <button\n          onClick={this.onClick}\n          onMouseOver={this.onMouseOver}>\n          Show Bar\n        </button>\n        { this.state.showBar && <DynamicBar /> }\n      </div>\n    )\n  }\n}\n```\n\n## Customizable Component\n\n### Custom Document\n\nHighly inspired by what [Next.js](https://github.com/zeit/next.js) has done on their awesome template customization.\n\nMaleo.js also enable customization on `Document` as document's markup. So you don't need to include tags like `<html>`, `<head>`, `<body>`, etc. \n\nTo override the default behavior, you'll need to create a component that extends the `Document` React class provided by Maleo.\n\n```jsx\n// document.jsx\nimport React from 'react';\nimport { Document, Header, Main, Scripts } from '@airy/maleo/document';\n\nexport default class extends Document {\n  render() {\n    return (\n      <html>\n        <Header>\n          <title>Maleo JS</title>\n          <meta charset=\"utf-8\" />\n          <meta name=\"description\" content=\"Maleo.js is awesome!\" />\n\n          <style>\n            {` body { background-color: #fff } `}\n          </style>\n        </Header>\n\n        <body>\n          <Main />\n\n          <Scripts />\n\n          <ReduxScript />\n        </body>\n      </html>\n    );\n  }\n}\n```\n\n### Custom Wrap\n\nMaleo.js uses the `Wrap` component to initialize pages. `Wrap` contains React Router's Component. You can add HoC here to wrap the application and control the page initialization. Which allows you to do amazing things like:\n- Persisting layour between page changes\n- Keeping state when navigating pages\n- Custom error handling using `componentDidCatch`\n- Inject additional data into pages (like Redux provider, etc)\n\nTo override the default behavior, you'll need to create a component that extends the `Wrap` React class provided by Maleo.\n\n```jsx\n// wrap.jsx\nimport React from 'react';\nimport { Wrap } from '@airy/maleo/wrap';\n\n// Redux plugin for Maleo.js\n// Hoc that creates store and add Redux Provider\nimport { withRedux } from '@airy/maleo-redux-plugin';\n\n// Custom Wrapper that will be rendered for the whole Application\nimport CustomWrapper from './component/CustomWrapper';\n\nimport { createStore } from './store';\n\n@withRedux(createStore)\nexport default class extends Wrap {\n  static getInitialProps = (ctx) => {\n    const { store } = ctx\n    // you receive store from context\n    // you can access or do something with the store here\n    console.log('Initialized Redux Store', store);\n    return {}\n  }\n\n  render() {\n    return (\n      <CustomWrapper>\n        {super.render()}\n      </CustomWrapper>\n    )\n  }\n}\n\n```\nIf you put `document.jsx` and `wrap.jsx` on root directory (the same level with `package.json`), then Maleo will automatically register your custom Document and Wrap. Otherwise, you can add the path to your custom Document and Wrap on [Maleo config](#custom-configuration)\n\n---\n***We are also working on adding default and customizable `Error` component page***\n\n---\n\n## Custom Configuration\n\nFor more advanced configuration of Maleo.js, like `webpack` config, registering `plugins`, path to your routes, custom Document and Wrap, and adding `path alias`, you can create a `maleo.config.js` in the root of your project directory. (same directory with `package.json`)\n\n```js\n// maleo.config.js\n\nmodule.exports = {\n  /* config options here */\n}\n```\n\nHere are the API's for the configuration:\n\n<table>\n  <tr>\n    <td>Key</td>\n    <td>Type</td>\n    <td>Description</td>\n  </tr>\n  <tr>\n    <td><code>buildDir</code></td>\n    <td><code>String?</code> [<code>.maleo</code>]</td>\n    <td>Directory to put Maleo.js' build assets</td>\n  </tr>\n  <tr>\n    <td><code>cache</code></td>\n    <td><code>Boolean?</code> [<code>true</code>]</td>\n    <td>Enable webpack build caching</td>\n  </tr>\n  <tr>\n    <td><code>sourceMaps</code></td>\n    <td><code>Boolean?</code> [<code>true</code>]</td>\n    <td>Enable webpack to generate source maps</td>\n  </tr>\n  <tr>\n    <td><code>alias</code></td>\n    <td><code>Object?</code></td>\n    <td>A key value pair for aliasing path directory\n      <br>\n      <code>\n      { 'component': './src/component' }\n      </code>\n    </td>\n  </tr>\n  <tr>\n    <td><code>publicPath</code></td>\n    <td><code>String?</code> [<code>/_assets/</code>]</td>\n    <td>To customize webpack's <code>publicPath</code> \n    <br>Comes in handy if using CDN to put built assets</td>\n  </tr>\n  <tr>\n    <td><code>analyzeBundle</code></td>\n    <td><code>Boolean?</code> [<code>false</code>]</td>\n    <td>To enable webpack's bundle analyzer, for analyzing bundle sizes during bundle debugging should Maleo.js' build process got slow</td>\n  </tr>\n  <tr>\n    <td><code>webpack</code></td>\n    <td><code>Function?</code></td>\n    <td>To customize webpack configuration, more details <a href=\"#customize-webpack\">here</a></td>\n  </tr>\n  <tr>\n    <td><code>routes</code></td>\n    <td><code>string?</code> [<code>rootDir/routes.jsx</code>]</td>\n    <td>Path to your routes file</td>\n  </tr>\n  <tr>\n    <td><code>customDocument</code></td>\n    <td><code>string?</code> [<code>rootDir/document.jsx</code>]</td>\n    <td>Path to your custom document file</td>\n  </tr>\n  <tr>\n    <td><code>customWrap</code></td>\n    <td><code>string?</code> [<code>rootDir/wrap.jsx</code>]</td>\n    <td>Path to your custom wrap file</td>\n  </tr>\n  <tr>\n    <td><code>customApp</code></td>\n    <td><code>string?</code> [<code>rootDir/app.jsx</code>]</td>\n    <td>Path to your custom app file</td>\n  </tr>\n</table>\n\n#### Customize Server\n\nCreate a `server.js` file on root directory where your `package.json` lives.\nHere you can customize Maleo's server.\n```js\nimport { Server } from '@airy/maleo/server';\nimport path from 'path';\n\nimport routeConfig from './routes';\n\nconst PORT = process.env.PORT || 8080;\n\nconst maleoServer = Server.init({\n  port: PORT,\n});\n\nmaleoServer.run(() => {\n  console.log('Server running on port :', PORT);\n});\n```\n\nHere are the API's for the configuration:\n\n<table>\n  <tr>\n    <td>Key</td>\n    <td>Type</td>\n    <td>Description</td>\n  </tr>\n  <tr>\n    <td><code>port</code></td>\n    <td><code>Number?</code> [<code>3000</code>]</td>\n    <td>Port to run Maleo server</td>\n  </tr>\n  <tr>\n    <td><code>assetDir</code></td>\n    <td><code>String?</code> [<code>'<root>/.maleo/client'</code>]</td>\n    <td>Directory for all client related assets</td>\n  </tr>\n</table>\n\n#### Customize Webpack\n\nYou are able to extend Maleo.js' default webpack configuration by defining a function on `maleo.config.js`\n\n```js\n// maleo.config.js\n\nmodule.exports = {\n  webpack(config, context, next) {\n    // Perform customizations to webpack config\n    // Important: This call is required in order to let Maleo pass the config to webpack\n    return next(); \n  },\n};\n```\n\nWebpack function will receive three arguments:\n\n<table>\n  <tr>\n    <td>Argument</td>\n    <td>Details</td>\n  <tr>\n  <tr>\n    <td><code>config</code></td>\n    <td>This contains webpack configuration object that you can manipulate</td>\n  <tr>\n  <tr>\n    <td><code>context</code></td>\n    <td>This contains some keys that are useful for the build context\n      <br>\n      <table>\n        <tr>\n          <td><code>isDev</code></td>\n          <td><code>Boolean</code></td>\n          <td>Check if current build is for development</td>\n        </tr>\n        <tr>\n          <td><code>publicPath</code></td>\n          <td><code>String</code></td>\n          <td>Public Path of user defined or default's value</td>\n        </tr>\n        <tr>\n          <td><code>analyzeBundle</code></td>\n          <td><code>Boolean</code></td>\n          <td>Check if analyze bundle is enabled</td>\n        </tr>\n        <tr>\n          <td><code>buildDirectory</code></td>\n          <td><code>String</code></td>\n          <td>Build Directory of user defined or default's value</td>\n        </tr>\n        <tr>\n          <td><code>name</code></td>\n          <td><code>String</code></td>\n          <td>Build name <code>'server' || 'client'</code></td>\n        </tr>\n      </table>\n    </td>\n  <tr>\n  <tr>\n    <td><code>next</code></td>\n    <td>A callback required to be called to pass the custom configuration</td>\n  <tr>\n</table>\n\nExample of adding `ts-loader` through `maleo.config.js`:\n\n```js\n// maleo.config.js\n\n// Partly taken and modified from @airy/maleo-ts-plugin source\n// for simplicity purposes\n\nmodule.exports = {\n  webpack(config, context, next) {\n    const { isDev } = context\n\n    config.module.rules.push({\n      test: /\\.tsx?/,\n      exclude: /node_modules/,\n      use: [\n        require.resolve('@airy/maleo/lib/build/loaders/maleo-babel-loader'),\n        {\n          loader: 'ts-loader',\n          options: {\n            transpileOnly: true,\n          },\n        },\n      ],\n\n      if (isDev) {\n        config.plugins.push(new ForkTSCheckerWebpackPlugin());\n      }\n\n      return next();\n    })\n  },\n};\n```\n\n#### Customize Babel Config\n\nMaleo.js also let you have your own babel config. Just simply add `.babelrc` file at the root directory of your app.\n\nYou can include Maleo.js' babel preset in order to have latest JavaScript preset.\n\nHere's an example of `.babelrc` file:\n```json\n{\n  \"presets\": [\"@airy/maleo/babel\"],\n  \"plugins\": []\n}\n```\n\nThe `@airy/maleo/babel` preset includes everything you need to get your development started. The preset includes:\n- `@babel/preset-env`\n- `@babel/preset-react`\n- `@babel/plugin-proposal-class-properties`\n- `@babel/plugin-proposal-decorators`\n- `@babel/plugin-proposal-object-rest-spread`\n- `@babel/plugin-transform-runtime`\n- `react-loadable/babel`\n\n## CDN Support\n\nIf you are using a CDN, you can set up the `publicPath` setting and configure your CDN's origin to resolve to the domain that Maleo.js is hosted on.\n\n```js\n// maleo.config.js\n\nconst isProd = process.env.NODE_ENV === 'production';\n\nmodule.exports = {\n  assetPrefix: isProd && 'https://cdn.example.com';\n}\n```\n\n## FAQ\n\n== TO BE DETERMINED == \n\n## Plugins\n- [css-plugin](https://github.com/airyrooms/maleo.js/tree/canary/packages/plugins/css-plugin)\n- [typescript-plugin](https://github.com/airyrooms/maleo.js/tree/canary/packages/plugins/typescript-plugin)\n- [redux-plugin](https://github.com/airyrooms/maleo.js/tree/canary/packages/plugins/redux-plugin)\n\n\n## Contributing\n[Please follow these steps to contribute to Maleo.js](https://github.com/airyrooms/maleo.js/blob/canary/CONTRIBUTING.md)\n\n[Please follow these steps to contribute to Maleo.js' plugins](https://github.com/airyrooms/maleo.js/tree/canary/packages/plugins)\n\n\n## Contributors\n\nThis project exists thanks to all the people who contribute. [Contribute](CONTRIBUTING.md).\n[![contributors][oc-contributors-img]](https://github.com/airyrooms/maleo.js/graphs/contributors)\n\n\n## License\n\nMIT\n\n[oc-contributors-img]: https://opencollective.com/maleo.js/contributors.svg?width=890&button=false\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/airyrooms/maleo.js/issues"},"homepage":"https://github.com/airyrooms/maleo.js#readme","_id":"@airy/maleo@0.0.1-canary.49","_nodeVersion":"10.11.0","_npmVersion":"lerna/3.13.1/node@v10.11.0+x64 (linux)","dist":{"integrity":"sha512-WSByosSsIdz7Hh1t0m63UxGjF2YRfB6WAIUfdUTttd+BT3oGjxA54DHvlsMKmquiqL7dJlMCBkGyY/6VtW/HMg==","shasum":"6a5fc84f47d8088dc4b47a70d342ba40b20e37d7","tarball":"https://registry.npmjs.org/@airy/maleo/-/maleo-0.0.1-canary.49.tgz","fileCount":82,"unpackedSize":222311,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJciKm9CRA9TVsSAnZWagAAgA0P/29XVGQ+J1hr7GGkf/8N\nYwyIQjTU8tTswgdLqIe4yZ3i2tP4AkBHulCCs+6sX8UCOgeOqEQy00fiGomn\nE35k3xUMZIDdzxQcGxJGRFGkP08kqNXqeYWp/pBcU2DkcFX1HuYZnA01y31/\nQW+UuaYs3ftKoTU0ED8beIrgwttN5iCUwYJhvIJvbW0Le3iIMVoDqYsEFhTG\nctsptH2EOEej2+7ASl+QgS+TJkFkdWdELN3WOXbB6Lh0TVpqww7tfcYs1xYQ\nMWAOa1Zoi3iJ81Z3/6rUJ18KCdQ3Hf1u8hkRrn+ZheqaLHNhBviuua0p4sLX\ntCleOLk1W13RjsM4v1rBg3wWdIy8qLjiGvcHfdfueQ4kLljBtX/AsghfvV4O\nRG3zXbrRAr060GOpAQhdDRgYeX3mIbewt12iBuk01tZMKNGvrQd6Qdqc6RDi\n/nCiiPUp+Y4Sz+TM8VUS5udQNgh6pSpzWQ6SVcMFSsO/krKQPYkSb34bTrel\nHCs1FgofW3HCtHMoH/jO+gSjrHPSUZk4Vb9u7y/I7hwOCwvdSVuAVgkhPDeI\nAsp8eSiMf+ucv6B4JH/PpjM+/QtM19lzRc0lofo/YRVvDQeOrwItwTv3j+Mn\nVLXJQV8grcmlzfpzGQSaUUwjhEY6RvEueIT0vssZ+B22UkqBf+SlfbIE98gW\nSDmi\r\n=L4lO\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIE/qZ6aTwNqFM3mtyv1WaYmJc+RV7pWClHS3P5bxwtJAAiEAhwyIw8dTLC6TX/F0kjDBiXGhHpwFGqvHAlrwNbNV5/I="}]},"maintainers":[{"name":"airyrooms-engineering","email":"engineering@airyrooms.com"}],"_npmUser":{"name":"airyrooms-engineering","email":"engineering@airyrooms.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/maleo_0.0.1-canary.49_1552460220916_0.12656726028546306"},"_hasShrinkwrap":false},"0.0.3-canary.49":{"name":"@airy/maleo","version":"0.0.3-canary.49","description":"Un-opinionated Universal Rendering Javascript Framework","repository":{"type":"git","url":"git+https://github.com/airyrooms/maleo.js.git","bugs":"https://github.com/airyrooms/maleo.js/issues"},"author":{"name":"Airy"},"license":"MIT","private":false,"sideEffects":false,"scripts":{"build":"rm -rf dist && gulp","watch":"gulp watch","prepublish":"yarn build","start":"node server.js"},"bin":{"maleo":"lib/bin/maleo.js"},"dependencies":{"@babel/core":"7.1.2","@babel/plugin-proposal-class-properties":"7.1.0","@babel/plugin-proposal-decorators":"7.1.2","@babel/plugin-proposal-object-rest-spread":"7.0.0","@babel/plugin-syntax-dynamic-import":"7.0.0","@babel/plugin-transform-runtime":"7.1.0","@babel/preset-env":"7.1.0","@babel/preset-react":"7.0.0","@babel/runtime":"7.1.2","@babel/runtime-corejs2":"7.2.0","babel-loader":"8.0.4","case-sensitive-paths-webpack-plugin":"2.1.2","es6-promise":"4.2.5","eventsource":"^1.0.7","express":"4.16.4","figlet":"1.2.1","friendly-errors-webpack-plugin":"1.7.0","hard-source-webpack-plugin":"0.13.1","helmet":"3.14.0","isomorphic-fetch":"2.2.1","loader-utils":"1.1.0","node-notifier":"5.2.1","nodemon":"1.18.4","react":"^16.8.4","react-dom":"16.8.4","react-helmet":"5.2.0","react-loadable":"5.5.0","react-router":"4.4.0-beta.1","react-router-config":"4.4.0-beta.6","rimraf":"2.6.3","source-map-support":"0.5.9","terser-webpack-plugin":"1.1.0","webpack":"4.29.0","webpack-bundle-analyzer":"3.0.3","webpack-cli":"3.1.2","webpack-dev-middleware":"3.4.0","webpack-hot-middleware":"2.24.3","webpackbar":"2.6.3"},"peerDependencies":{"react":"^16.8.4","react-router-dom":"^4.4.0-beta.1"},"devDependencies":{"@babel/preset-typescript":"^7.1.0","@types/webpack-bundle-analyzer":"^2.13.0","babel-plugin-module-resolver":"^3.1.1","gulp":"github:gulpjs/gulp#4.0","gulp-babel":"^8.0.0","gulp-clean":"^0.4.0","gulp-sourcemaps":"^2.6.4","gulp-uglify":"^3.0.1","gulp-watch":"^5.0.1","react":"^16.8.4","react-router-dom":"^4.4.0-beta.1"},"gitHead":"215d7b9270938dd880cddb069dbe5ca6a00d7546","readme":"[![Build Status](https://travis-ci.org/airyrooms/maleo.js.svg?branch=master)](https://travis-ci.org/airyrooms/maleo.js)\n[![Coverage Status](https://coveralls.io/repos/github/airyrooms/maleo.js/badge.svg?branch=master)](https://coveralls.io/github/airyrooms/maleo.js?branch=master)\n[![MIT license](https://img.shields.io/badge/License-MIT-blue.svg)](https://github.com/airyrooms/maleo.js/blob/canary/LICENSE)\n[![Discord Chat](https://img.shields.io/discord/550498214789513218.svg)](https://discord.gg/9eArCQn)\n\n\n# Welcome to Maleo.js\n\nMaleo.js is an un-opinionated framework to enable Universal Rendering in JavaScript using React with no hassle.\n\nWe are here to solve the time consuming setups Universal Rendering Required.\n\n---\n\nReadme below is the documentation for the `canary` (prerelease) branch. To view the documentation for the latest stable Maleo.js version change branch to `master`\n\n---\n\n# Table of Contents\n- [Features](#features)\n- [Setup](#setup)\n- [Component Lifecycle](#component-lifecycle)\n- [Routing](#routing)\n- [Dynamic Import Component](#dynamic-import-component)\n  - [Tips](#tips)\n  - [Preloading](#preloading)\n- [Customizable Component](#customizable-component)\n  - [Custom Document](#custom-document)\n  - [Custom Wrap](#custom-wrap)\n- [Custom Configuration](#custom-configuration)\n  - [Customize Server](#customize-server)\n  - [Customize Webpack](#customize-webpack)\n  - [Customize Babel Config](#customize-babel-config)\n- [CDN Support](#cdn-support)\n- [Plugins](#plugins)\n- [FAQ](#faq)\n- [Contributing](#contributing)\n\n---\n\n## Features\n- Universal Rendering\n- Plugin based framework\n- Customizable\n\n## Setup\n\nInstall Maleo.js \n\n**NPM**\n```bash\n$ npm install --save @airy/maleo react react-router-dom\n```\n**Yarn**\n```bash\n$ yarn add @airy/maleo react react-router-dom\n```\n\nAdd this script to your `package.json`\n```json\n{\n  \"scripts\": {\n    \"dev\": \"maleo\",\n    \"build\": \"export NODE_ENV=production && maleo build\",\n    \"start\": \"export NODE_ENV=production && node .maleo/server.js\"\n  }\n}\n```\n\nCreate a routing file called `routes.js`\n```jsx\nimport React from 'react';\n\nconst App = () => (<h1>Hello World</h1>);\n\nexport default [\n  {\n    path: '/',\n    component: App,\n    key: 'root',\n  },\n];\n```\n\nAfter that you can now run `$ npm run dev` and go to `http://localhost:3000`.\n\nYou should now see your app running on your browser.\n\nBy now you should see \n- Automatic transpilation and bundling (with webpack and babel)\n- ~~Hot code reloading~~ [#17](https://github.com/airyrooms/maleo.js/issues/17)\n- Server rendering\n\nTo see how simple this is, check out the sample app!\n\n## Component Lifecycle \n\nMaleo.js added a new component lifecycle hook called `getInitialProps`, this function is called during Server Side Rendering (SSR).\n\nThis is useful especially for SEO purposes. \n\nExample for stateful component:\n```jsx\nimport React from 'react';\n\nexport default class extends React.Component {\n  static getInitialProps = async (ctx) => {\n    const { req } = ctx;\n\n    const userAgent = req ? req.headers['user-agent'] : navigator.userAgent;\n\n    // the return value will be passed as props for this component\n    return { userAgent };\n  }\n\n  render() {\n    return (\n      <div>\n        Hello World {this.props.userAgent}\n      </div>\n    );\n  }\n}\n```\n\nExample for stateless component:\n```jsx\nconst Component = (props) => (\n  <div>\n    Hello World {props.userAgent}\n  </div>\n);\n\nComponent.getInitialprops = async (ctx) => {\n  const { req } = ctx;\n\n  const userAgent = req ? req.headers['user-agent'] : navigator.userAgent;\n\n  return { userAgent };\n};\n\nexport default Component;\n```\n\n`getInitialProps` receives a context object with the following properties:\n- `req` - HTTP request object (server only)\n- `res` - HTTP response object (server only)\n- `...wrapProps` - Spreaded properties from custom Wrap\n- `...appProps` - Spreaded properties from custom App\n\n## Routing\n\nRouting is declared in a centralized route config.\nRegister all the route config in `maleo-routes.json` file.\n\nIf you put the `maleo-routes.json` files on root directory, Maleo will automatically register your route. Otherwise put path to your routes on [Maleo config](#custom-configuration).\n\nRoutes file has to export default the route configuration.\nThe route object **expected to have distinct key** to indicate the route.\n\n<table>\n  <tr>\n    <td>Key</td>\n    <td>Type</td>\n    <td>Description</td>\n  </tr>\n  <tr>\n    <td><code>path</code></td>\n    <td><code>String!</code></td>\n    <td>Routes path</td>\n  </tr>\n  <tr>\n    <td><code>page</code></td>\n    <td><code>String!</code></td>\n    <td>Path to React Component for this route path</td>\n  </tr>\n  <tr>\n    <td><code>exact</code></td>\n    <td><code>Boolean?</code> [<code>false</code>]</td>\n    <td>To make url has to match exactly the path in order to render the component. Give <code>false</code> value if the route is a wrapper route component</td>\n  </tr>\n  <tr>\n    <td><code>routes</code></td>\n    <td><code>RouteObject?</code></td>\n    <td>Nested route</td>\n  </tr>\n</table>\n\n\nFor example:\n```json\n[\n  {\n    \"page\": \"./src/MainApp\",\n    \"routes\": [\n      {\n        \"path\": \"/\",\n        \"page\": \"./src/Search\",\n        \"exact\": true\n      },\n      {\n        \"path\": \"/search\",\n        \"page\": \"./src/Search\",\n        \"routes\": [\n          {\n            \"path\": \"/search/hello\",\n            \"page\": \"./src/Detail\",\n            \"exact\": true\n          }\n        ]\n      },\n      {\n        \"path\": \"/detail\",\n        \"page\": \"./src/Detail\",\n        \"exact\": true\n      }\n    ]\n  }\n]\n```\n\n## Dynamic Import Component\nMaleo.js supports TC39 [dynamic import proposal](https://github.com/tc39/proposal-dynamic-import) for JavaScript.\n\nYou can think dynamic import as another way to split your code into manageable chunks. You can use our `Dynamic` function which utilizes [react loadable](https://github.com/jamiebuilds/react-loadable)\n\nFor Example\n```js\n// DynamicComponent.js\nimport Dynamic from '@airy/maleo/dynamic';\n\nexport default Dynamic({\n  loader: () => import( /* webpackChunkName:\"DynamicComponent\" */ './component/DynamicLoad'),\n})\n```\n\n### **Preloading**\n\nFor optimization purposes, you can also preload a component even before the component got rendered.\n\nFor example, if you want to load component when a button get pressed, you can start preloading the component when the user hovers the mouse over the button.\n\nThe component created by `Dynamic` exposes a [static `preload` method](https://github.com/jamiebuilds/react-loadable#loadablecomponentpreload).\n\n```jsx\nimport React from 'react';\nimport Dynamic from '@airy/maleo/dynamic';\n\nconst DynamicBar = Dynamic({\n  loader: () => import('./Bar'),\n  loading: LoadingCompoinent\n});\n\nclass MyComponent extends React.Component {\n  state = { showBar: false };\n\n  onClick = () => {\n    this.setState({ showBar: true });\n  };\n\n  onMouseOver = () => DynamicBar.preload();\n\n  render() {\n    return (\n      <div>\n        <button\n          onClick={this.onClick}\n          onMouseOver={this.onMouseOver}>\n          Show Bar\n        </button>\n        { this.state.showBar && <DynamicBar /> }\n      </div>\n    )\n  }\n}\n```\n\n## Customizable Component\n\n### Custom Document\n\nHighly inspired by what [Next.js](https://github.com/zeit/next.js) has done on their awesome template customization.\n\nMaleo.js also enable customization on `Document` as document's markup. So you don't need to include tags like `<html>`, `<head>`, `<body>`, etc. \n\nTo override the default behavior, you'll need to create a component that extends the `Document` React class provided by Maleo.\n\n```jsx\n// document.jsx\nimport React from 'react';\nimport { Document, Header, Main, Scripts } from '@airy/maleo/document';\n\nexport default class extends Document {\n  render() {\n    return (\n      <html>\n        <Header>\n          <title>Maleo JS</title>\n          <meta charset=\"utf-8\" />\n          <meta name=\"description\" content=\"Maleo.js is awesome!\" />\n\n          <style>\n            {` body { background-color: #fff } `}\n          </style>\n        </Header>\n\n        <body>\n          <Main />\n\n          <Scripts />\n\n          <ReduxScript />\n        </body>\n      </html>\n    );\n  }\n}\n```\n\n### Custom Wrap\n\nMaleo.js uses the `Wrap` component to initialize pages. `Wrap` contains React Router's Component. You can add HoC here to wrap the application and control the page initialization. Which allows you to do amazing things like:\n- Persisting layour between page changes\n- Keeping state when navigating pages\n- Custom error handling using `componentDidCatch`\n- Inject additional data into pages (like Redux provider, etc)\n\nTo override the default behavior, you'll need to create a component that extends the `Wrap` React class provided by Maleo.\n\n```jsx\n// wrap.jsx\nimport React from 'react';\nimport { Wrap } from '@airy/maleo/wrap';\n\n// Redux plugin for Maleo.js\n// Hoc that creates store and add Redux Provider\nimport { withRedux } from '@airy/maleo-redux-plugin';\n\n// Custom Wrapper that will be rendered for the whole Application\nimport CustomWrapper from './component/CustomWrapper';\n\nimport { createStore } from './store';\n\n@withRedux(createStore)\nexport default class extends Wrap {\n  static getInitialProps = (ctx) => {\n    const { store } = ctx\n    // you receive store from context\n    // you can access or do something with the store here\n    console.log('Initialized Redux Store', store);\n    return {}\n  }\n\n  render() {\n    return (\n      <CustomWrapper>\n        {super.render()}\n      </CustomWrapper>\n    )\n  }\n}\n\n```\nIf you put `document.jsx` and `wrap.jsx` on root directory (the same level with `package.json`), then Maleo will automatically register your custom Document and Wrap. Otherwise, you can add the path to your custom Document and Wrap on [Maleo config](#custom-configuration)\n\n---\n***We are also working on adding default and customizable `Error` component page***\n\n---\n\n## Custom Configuration\n\nFor more advanced configuration of Maleo.js, like `webpack` config, registering `plugins`, path to your routes, custom Document and Wrap, and adding `path alias`, you can create a `maleo.config.js` in the root of your project directory. (same directory with `package.json`)\n\n```js\n// maleo.config.js\n\nmodule.exports = {\n  /* config options here */\n}\n```\n\nHere are the API's for the configuration:\n\n<table>\n  <tr>\n    <td>Key</td>\n    <td>Type</td>\n    <td>Description</td>\n  </tr>\n  <tr>\n    <td><code>buildDir</code></td>\n    <td><code>String?</code> [<code>.maleo</code>]</td>\n    <td>Directory to put Maleo.js' build assets</td>\n  </tr>\n  <tr>\n    <td><code>cache</code></td>\n    <td><code>Boolean?</code> [<code>true</code>]</td>\n    <td>Enable webpack build caching</td>\n  </tr>\n  <tr>\n    <td><code>sourceMaps</code></td>\n    <td><code>Boolean?</code> [<code>true</code>]</td>\n    <td>Enable webpack to generate source maps</td>\n  </tr>\n  <tr>\n    <td><code>alias</code></td>\n    <td><code>Object?</code></td>\n    <td>A key value pair for aliasing path directory\n      <br>\n      <code>\n      { 'component': './src/component' }\n      </code>\n    </td>\n  </tr>\n  <tr>\n    <td><code>publicPath</code></td>\n    <td><code>String?</code> [<code>/_assets/</code>]</td>\n    <td>To customize webpack's <code>publicPath</code> \n    <br>Comes in handy if using CDN to put built assets</td>\n  </tr>\n  <tr>\n    <td><code>analyzeBundle</code></td>\n    <td><code>Boolean?</code> [<code>false</code>]</td>\n    <td>To enable webpack's bundle analyzer, for analyzing bundle sizes during bundle debugging should Maleo.js' build process got slow</td>\n  </tr>\n  <tr>\n    <td><code>webpack</code></td>\n    <td><code>Function?</code></td>\n    <td>To customize webpack configuration, more details <a href=\"#customize-webpack\">here</a></td>\n  </tr>\n  <tr>\n    <td><code>routes</code></td>\n    <td><code>string?</code> [<code>rootDir/routes.jsx</code>]</td>\n    <td>Path to your routes file</td>\n  </tr>\n  <tr>\n    <td><code>customDocument</code></td>\n    <td><code>string?</code> [<code>rootDir/document.jsx</code>]</td>\n    <td>Path to your custom document file</td>\n  </tr>\n  <tr>\n    <td><code>customWrap</code></td>\n    <td><code>string?</code> [<code>rootDir/wrap.jsx</code>]</td>\n    <td>Path to your custom wrap file</td>\n  </tr>\n  <tr>\n    <td><code>customApp</code></td>\n    <td><code>string?</code> [<code>rootDir/app.jsx</code>]</td>\n    <td>Path to your custom app file</td>\n  </tr>\n</table>\n\n#### Customize Server\n\nCreate a `server.js` file on root directory where your `package.json` lives.\nHere you can customize Maleo's server.\n```js\nimport { Server } from '@airy/maleo/server';\nimport path from 'path';\n\nimport routeConfig from './routes';\n\nconst PORT = process.env.PORT || 8080;\n\nconst maleoServer = Server.init({\n  port: PORT,\n});\n\nmaleoServer.run(() => {\n  console.log('Server running on port :', PORT);\n});\n```\n\nHere are the API's for the configuration:\n\n<table>\n  <tr>\n    <td>Key</td>\n    <td>Type</td>\n    <td>Description</td>\n  </tr>\n  <tr>\n    <td><code>port</code></td>\n    <td><code>Number?</code> [<code>3000</code>]</td>\n    <td>Port to run Maleo server</td>\n  </tr>\n  <tr>\n    <td><code>assetDir</code></td>\n    <td><code>String?</code> [<code>'<root>/.maleo/client'</code>]</td>\n    <td>Directory for all client related assets</td>\n  </tr>\n</table>\n\n#### Customize Webpack\n\nYou are able to extend Maleo.js' default webpack configuration by defining a function on `maleo.config.js`\n\n```js\n// maleo.config.js\n\nmodule.exports = {\n  webpack(config, context, next) {\n    // Perform customizations to webpack config\n    // Important: This call is required in order to let Maleo pass the config to webpack\n    return next(); \n  },\n};\n```\n\nWebpack function will receive three arguments:\n\n<table>\n  <tr>\n    <td>Argument</td>\n    <td>Details</td>\n  <tr>\n  <tr>\n    <td><code>config</code></td>\n    <td>This contains webpack configuration object that you can manipulate</td>\n  <tr>\n  <tr>\n    <td><code>context</code></td>\n    <td>This contains some keys that are useful for the build context\n      <br>\n      <table>\n        <tr>\n          <td><code>isDev</code></td>\n          <td><code>Boolean</code></td>\n          <td>Check if current build is for development</td>\n        </tr>\n        <tr>\n          <td><code>publicPath</code></td>\n          <td><code>String</code></td>\n          <td>Public Path of user defined or default's value</td>\n        </tr>\n        <tr>\n          <td><code>analyzeBundle</code></td>\n          <td><code>Boolean</code></td>\n          <td>Check if analyze bundle is enabled</td>\n        </tr>\n        <tr>\n          <td><code>buildDirectory</code></td>\n          <td><code>String</code></td>\n          <td>Build Directory of user defined or default's value</td>\n        </tr>\n        <tr>\n          <td><code>name</code></td>\n          <td><code>String</code></td>\n          <td>Build name <code>'server' || 'client'</code></td>\n        </tr>\n      </table>\n    </td>\n  <tr>\n  <tr>\n    <td><code>next</code></td>\n    <td>A callback required to be called to pass the custom configuration</td>\n  <tr>\n</table>\n\nExample of adding `ts-loader` through `maleo.config.js`:\n\n```js\n// maleo.config.js\n\n// Partly taken and modified from @airy/maleo-ts-plugin source\n// for simplicity purposes\n\nmodule.exports = {\n  webpack(config, context, next) {\n    const { isDev } = context\n\n    config.module.rules.push({\n      test: /\\.tsx?/,\n      exclude: /node_modules/,\n      use: [\n        require.resolve('@airy/maleo/lib/build/loaders/maleo-babel-loader'),\n        {\n          loader: 'ts-loader',\n          options: {\n            transpileOnly: true,\n          },\n        },\n      ],\n\n      if (isDev) {\n        config.plugins.push(new ForkTSCheckerWebpackPlugin());\n      }\n\n      return next();\n    })\n  },\n};\n```\n\n#### Customize Babel Config\n\nMaleo.js also let you have your own babel config. Just simply add `.babelrc` file at the root directory of your app.\n\nYou can include Maleo.js' babel preset in order to have latest JavaScript preset.\n\nHere's an example of `.babelrc` file:\n```json\n{\n  \"presets\": [\"@airy/maleo/babel\"],\n  \"plugins\": []\n}\n```\n\nThe `@airy/maleo/babel` preset includes everything you need to get your development started. The preset includes:\n- `@babel/preset-env`\n- `@babel/preset-react`\n- `@babel/plugin-proposal-class-properties`\n- `@babel/plugin-proposal-decorators`\n- `@babel/plugin-proposal-object-rest-spread`\n- `@babel/plugin-transform-runtime`\n- `react-loadable/babel`\n\n## CDN Support\n\nIf you are using a CDN, you can set up the `publicPath` setting and configure your CDN's origin to resolve to the domain that Maleo.js is hosted on.\n\n```js\n// maleo.config.js\n\nconst isProd = process.env.NODE_ENV === 'production';\n\nmodule.exports = {\n  assetPrefix: isProd && 'https://cdn.example.com';\n}\n```\n\n## FAQ\n\n== TO BE DETERMINED == \n\n## Plugins\n- [css-plugin](https://github.com/airyrooms/maleo.js/tree/canary/packages/plugins/css-plugin)\n- [typescript-plugin](https://github.com/airyrooms/maleo.js/tree/canary/packages/plugins/typescript-plugin)\n- [redux-plugin](https://github.com/airyrooms/maleo.js/tree/canary/packages/plugins/redux-plugin)\n\n\n## Contributing\n[Please follow these steps to contribute to Maleo.js](https://github.com/airyrooms/maleo.js/blob/canary/CONTRIBUTING.md)\n\n[Please follow these steps to contribute to Maleo.js' plugins](https://github.com/airyrooms/maleo.js/tree/canary/packages/plugins)\n\n\n## Contributors\n\nThis project exists thanks to all the people who contribute. [Contribute](CONTRIBUTING.md).\n[![contributors][oc-contributors-img]](https://github.com/airyrooms/maleo.js/graphs/contributors)\n\n\n## License\n\nMIT\n\n[oc-contributors-img]: https://opencollective.com/maleo.js/contributors.svg?width=890&button=false\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/airyrooms/maleo.js/issues"},"homepage":"https://github.com/airyrooms/maleo.js#readme","_id":"@airy/maleo@0.0.3-canary.49","_nodeVersion":"10.11.0","_npmVersion":"lerna/3.13.1/node@v10.11.0+x64 (linux)","dist":{"integrity":"sha512-t09t8GZhIN4J3tCryRJEDxGgz8nS4bxPUWarI9l20xxrjB15QchPJ+Li6i9p6TTVHg8hmOhmAJPAQ44h8oNV8A==","shasum":"351493c44bcbc285001e456a0461f88de72a1f5b","tarball":"https://registry.npmjs.org/@airy/maleo/-/maleo-0.0.3-canary.49.tgz","fileCount":82,"unpackedSize":222311,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJciLl2CRA9TVsSAnZWagAAS1UP/0g1FKbb16aeZQi/Hazy\ng0yvRI0NcLV/P3QL1APGkVQEsH/BAOwMY5yTtNCUvWGri+8U2uQRyc2Y+ttu\n+b9d/Z8TJf2+nziZQ64GuhCvuGH4BGdbaaX72asFrizAOmOGzbVdHZg/tRGG\nBhORJL+Yv2acTYAXzh1UC/oXqusq4h1at8wamKp1ARHolQM3a/v9bgg6q0ri\ntLW3pZn+QV92YU6jiQJgK/2Gb7hpCK/PEWw1IeOSBrN+oww+oklrfWolZMkW\ni3pqmKNqWHKg2IOAhr25zkUJPPfyMCJ17FDkBPM8u8+3Ifgu6WfgVcwY/8Q+\n5eB8LE1bgwK65gnb0S5SEnhiL4dn5s2VgGsYsO1zMXo4egROvlh4JGBrTxIr\nUbUyhZjtXZ+2rsJSU5vs05KYobfqFUleJsNwvJXbIJILfb+MedY8tjvSza/W\naHqI5PkIPc5z9npV2OQ8CCVBi2ARdJWgDDnpj8konYBJXgYr0gO4IJXU3W/V\nlje1p24EHA9BhiT+jkFV78liDCPG30U61UJVid43cViIYJvLJY32IMi54PV2\n5XwqzJKnHlCtXfzE+ai7M2XD4RD+yixNFBR1rJl12biHwPSD7YXFnviZIOqn\nW5tfq72bYEzAj/+w1dt6l164vKj0FuJY0sEx5p2Qdvxgm2IOLFvcJDalVHav\ny+fp\r\n=vFLu\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIHoP66klARKWBHrYqAY8OAEv+YvdF40hGP4SknND0BVbAiEAsFN3RTpVQDJuDGxLIBanll1v1BFkQJ7JxLy1m0yJpIc="}]},"maintainers":[{"name":"airyrooms-engineering","email":"engineering@airyrooms.com"}],"_npmUser":{"name":"airyrooms-engineering","email":"engineering@airyrooms.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/maleo_0.0.3-canary.49_1552464245510_0.07115977990125422"},"_hasShrinkwrap":false},"0.0.4-canary.49":{"name":"@airy/maleo","version":"0.0.4-canary.49","description":"Un-opinionated Universal Rendering Javascript Framework","repository":{"type":"git","url":"git+https://github.com/airyrooms/maleo.js.git","bugs":"https://github.com/airyrooms/maleo.js/issues"},"author":{"name":"Airy"},"license":"MIT","private":false,"sideEffects":false,"scripts":{"build":"rm -rf dist && gulp","watch":"gulp watch","prepublish":"yarn build","start":"node server.js"},"bin":{"maleo":"lib/bin/maleo.js"},"dependencies":{"@babel/core":"7.1.2","@babel/plugin-proposal-class-properties":"7.1.0","@babel/plugin-proposal-decorators":"7.1.2","@babel/plugin-proposal-object-rest-spread":"7.0.0","@babel/plugin-syntax-dynamic-import":"7.0.0","@babel/plugin-transform-runtime":"7.1.0","@babel/preset-env":"7.1.0","@babel/preset-react":"7.0.0","@babel/runtime":"7.1.2","@babel/runtime-corejs2":"7.2.0","babel-loader":"8.0.4","case-sensitive-paths-webpack-plugin":"2.1.2","es6-promise":"4.2.5","eventsource":"^1.0.7","express":"4.16.4","figlet":"1.2.1","friendly-errors-webpack-plugin":"1.7.0","hard-source-webpack-plugin":"0.13.1","helmet":"3.14.0","isomorphic-fetch":"2.2.1","loader-utils":"1.1.0","node-notifier":"5.2.1","nodemon":"1.18.4","react":"^16.8.4","react-dom":"16.8.4","react-helmet":"5.2.0","react-loadable":"5.5.0","react-router":"4.4.0-beta.1","react-router-config":"4.4.0-beta.6","rimraf":"2.6.3","source-map-support":"0.5.9","terser-webpack-plugin":"1.1.0","webpack":"4.29.0","webpack-bundle-analyzer":"3.0.3","webpack-cli":"3.1.2","webpack-dev-middleware":"3.4.0","webpack-hot-middleware":"2.24.3","webpackbar":"2.6.3"},"peerDependencies":{"react":"^16.8.4","react-router-dom":"^4.4.0-beta.1"},"devDependencies":{"@babel/preset-typescript":"^7.1.0","@types/webpack-bundle-analyzer":"^2.13.0","babel-plugin-module-resolver":"^3.1.1","gulp":"github:gulpjs/gulp#4.0","gulp-babel":"^8.0.0","gulp-clean":"^0.4.0","gulp-sourcemaps":"^2.6.4","gulp-uglify":"^3.0.1","gulp-watch":"^5.0.1","react":"^16.8.4","react-router-dom":"^4.4.0-beta.1"},"gitHead":"8b1d3bebcc45c851748c937c8b40d7a15606293a","readme":"[![Build Status](https://travis-ci.org/airyrooms/maleo.js.svg?branch=master)](https://travis-ci.org/airyrooms/maleo.js)\n[![Coverage Status](https://coveralls.io/repos/github/airyrooms/maleo.js/badge.svg?branch=master)](https://coveralls.io/github/airyrooms/maleo.js?branch=master)\n[![MIT license](https://img.shields.io/badge/License-MIT-blue.svg)](https://github.com/airyrooms/maleo.js/blob/canary/LICENSE)\n[![Discord Chat](https://img.shields.io/discord/550498214789513218.svg)](https://discord.gg/9eArCQn)\n\n\n# Welcome to Maleo.js\n\nMaleo.js is an un-opinionated framework to enable Universal Rendering in JavaScript using React with no hassle.\n\nWe are here to solve the time consuming setups Universal Rendering Required.\n\n---\n\nReadme below is the documentation for the `canary` (prerelease) branch. To view the documentation for the latest stable Maleo.js version change branch to `master`\n\n---\n\n# Table of Contents\n- [Features](#features)\n- [Setup](#setup)\n- [Component Lifecycle](#component-lifecycle)\n- [Routing](#routing)\n- [Dynamic Import Component](#dynamic-import-component)\n  - [Tips](#tips)\n  - [Preloading](#preloading)\n- [Customizable Component](#customizable-component)\n  - [Custom Document](#custom-document)\n  - [Custom Wrap](#custom-wrap)\n- [Custom Configuration](#custom-configuration)\n  - [Customize Server](#customize-server)\n  - [Customize Webpack](#customize-webpack)\n  - [Customize Babel Config](#customize-babel-config)\n- [CDN Support](#cdn-support)\n- [Plugins](#plugins)\n- [FAQ](#faq)\n- [Contributing](#contributing)\n\n---\n\n## Features\n- Universal Rendering\n- Plugin based framework\n- Customizable\n\n## Setup\n\nInstall Maleo.js \n\n**NPM**\n```bash\n$ npm install --save @airy/maleo react react-router-dom\n```\n**Yarn**\n```bash\n$ yarn add @airy/maleo react react-router-dom\n```\n\nAdd this script to your `package.json`\n```json\n{\n  \"scripts\": {\n    \"dev\": \"maleo\",\n    \"build\": \"export NODE_ENV=production && maleo build\",\n    \"start\": \"export NODE_ENV=production && node .maleo/server.js\"\n  }\n}\n```\n\nCreate a routing file called `routes.js`\n```jsx\nimport React from 'react';\n\nconst App = () => (<h1>Hello World</h1>);\n\nexport default [\n  {\n    path: '/',\n    component: App,\n    key: 'root',\n  },\n];\n```\n\nAfter that you can now run `$ npm run dev` and go to `http://localhost:3000`.\n\nYou should now see your app running on your browser.\n\nBy now you should see \n- Automatic transpilation and bundling (with webpack and babel)\n- ~~Hot code reloading~~ [#17](https://github.com/airyrooms/maleo.js/issues/17)\n- Server rendering\n\nTo see how simple this is, check out the sample app!\n\n## Component Lifecycle \n\nMaleo.js added a new component lifecycle hook called `getInitialProps`, this function is called during Server Side Rendering (SSR).\n\nThis is useful especially for SEO purposes. \n\nExample for stateful component:\n```jsx\nimport React from 'react';\n\nexport default class extends React.Component {\n  static getInitialProps = async (ctx) => {\n    const { req } = ctx;\n\n    const userAgent = req ? req.headers['user-agent'] : navigator.userAgent;\n\n    // the return value will be passed as props for this component\n    return { userAgent };\n  }\n\n  render() {\n    return (\n      <div>\n        Hello World {this.props.userAgent}\n      </div>\n    );\n  }\n}\n```\n\nExample for stateless component:\n```jsx\nconst Component = (props) => (\n  <div>\n    Hello World {props.userAgent}\n  </div>\n);\n\nComponent.getInitialprops = async (ctx) => {\n  const { req } = ctx;\n\n  const userAgent = req ? req.headers['user-agent'] : navigator.userAgent;\n\n  return { userAgent };\n};\n\nexport default Component;\n```\n\n`getInitialProps` receives a context object with the following properties:\n- `req` - HTTP request object (server only)\n- `res` - HTTP response object (server only)\n- `...wrapProps` - Spreaded properties from custom Wrap\n- `...appProps` - Spreaded properties from custom App\n\n## Routing\n\nRouting is declared in a centralized route config.\nRegister all the route config in `maleo-routes.json` file.\n\nIf you put the `maleo-routes.json` files on root directory, Maleo will automatically register your route. Otherwise put path to your routes on [Maleo config](#custom-configuration).\n\nRoutes file has to export default the route configuration.\nThe route object **expected to have distinct key** to indicate the route.\n\n<table>\n  <tr>\n    <td>Key</td>\n    <td>Type</td>\n    <td>Description</td>\n  </tr>\n  <tr>\n    <td><code>path</code></td>\n    <td><code>String!</code></td>\n    <td>Routes path</td>\n  </tr>\n  <tr>\n    <td><code>page</code></td>\n    <td><code>String!</code></td>\n    <td>Path to React Component for this route path</td>\n  </tr>\n  <tr>\n    <td><code>exact</code></td>\n    <td><code>Boolean?</code> [<code>false</code>]</td>\n    <td>To make url has to match exactly the path in order to render the component. Give <code>false</code> value if the route is a wrapper route component</td>\n  </tr>\n  <tr>\n    <td><code>routes</code></td>\n    <td><code>RouteObject?</code></td>\n    <td>Nested route</td>\n  </tr>\n</table>\n\n\nFor example:\n```json\n[\n  {\n    \"page\": \"./src/MainApp\",\n    \"routes\": [\n      {\n        \"path\": \"/\",\n        \"page\": \"./src/Search\",\n        \"exact\": true\n      },\n      {\n        \"path\": \"/search\",\n        \"page\": \"./src/Search\",\n        \"routes\": [\n          {\n            \"path\": \"/search/hello\",\n            \"page\": \"./src/Detail\",\n            \"exact\": true\n          }\n        ]\n      },\n      {\n        \"path\": \"/detail\",\n        \"page\": \"./src/Detail\",\n        \"exact\": true\n      }\n    ]\n  }\n]\n```\n\n## Dynamic Import Component\nMaleo.js supports TC39 [dynamic import proposal](https://github.com/tc39/proposal-dynamic-import) for JavaScript.\n\nYou can think dynamic import as another way to split your code into manageable chunks. You can use our `Dynamic` function which utilizes [react loadable](https://github.com/jamiebuilds/react-loadable)\n\nFor Example\n```js\n// DynamicComponent.js\nimport Dynamic from '@airy/maleo/dynamic';\n\nexport default Dynamic({\n  loader: () => import( /* webpackChunkName:\"DynamicComponent\" */ './component/DynamicLoad'),\n})\n```\n\n### **Preloading**\n\nFor optimization purposes, you can also preload a component even before the component got rendered.\n\nFor example, if you want to load component when a button get pressed, you can start preloading the component when the user hovers the mouse over the button.\n\nThe component created by `Dynamic` exposes a [static `preload` method](https://github.com/jamiebuilds/react-loadable#loadablecomponentpreload).\n\n```jsx\nimport React from 'react';\nimport Dynamic from '@airy/maleo/dynamic';\n\nconst DynamicBar = Dynamic({\n  loader: () => import('./Bar'),\n  loading: LoadingCompoinent\n});\n\nclass MyComponent extends React.Component {\n  state = { showBar: false };\n\n  onClick = () => {\n    this.setState({ showBar: true });\n  };\n\n  onMouseOver = () => DynamicBar.preload();\n\n  render() {\n    return (\n      <div>\n        <button\n          onClick={this.onClick}\n          onMouseOver={this.onMouseOver}>\n          Show Bar\n        </button>\n        { this.state.showBar && <DynamicBar /> }\n      </div>\n    )\n  }\n}\n```\n\n## Customizable Component\n\n### Custom Document\n\nHighly inspired by what [Next.js](https://github.com/zeit/next.js) has done on their awesome template customization.\n\nMaleo.js also enable customization on `Document` as document's markup. So you don't need to include tags like `<html>`, `<head>`, `<body>`, etc. \n\nTo override the default behavior, you'll need to create a component that extends the `Document` React class provided by Maleo.\n\n```jsx\n// document.jsx\nimport React from 'react';\nimport { Document, Header, Main, Scripts } from '@airy/maleo/document';\n\nexport default class extends Document {\n  render() {\n    return (\n      <html>\n        <Header>\n          <title>Maleo JS</title>\n          <meta charset=\"utf-8\" />\n          <meta name=\"description\" content=\"Maleo.js is awesome!\" />\n\n          <style>\n            {` body { background-color: #fff } `}\n          </style>\n        </Header>\n\n        <body>\n          <Main />\n\n          <Scripts />\n\n          <ReduxScript />\n        </body>\n      </html>\n    );\n  }\n}\n```\n\n### Custom Wrap\n\nMaleo.js uses the `Wrap` component to initialize pages. `Wrap` contains React Router's Component. You can add HoC here to wrap the application and control the page initialization. Which allows you to do amazing things like:\n- Persisting layour between page changes\n- Keeping state when navigating pages\n- Custom error handling using `componentDidCatch`\n- Inject additional data into pages (like Redux provider, etc)\n\nTo override the default behavior, you'll need to create a component that extends the `Wrap` React class provided by Maleo.\n\n```jsx\n// wrap.jsx\nimport React from 'react';\nimport { Wrap } from '@airy/maleo/wrap';\n\n// Redux plugin for Maleo.js\n// Hoc that creates store and add Redux Provider\nimport { withRedux } from '@airy/maleo-redux-plugin';\n\n// Custom Wrapper that will be rendered for the whole Application\nimport CustomWrapper from './component/CustomWrapper';\n\nimport { createStore } from './store';\n\n@withRedux(createStore)\nexport default class extends Wrap {\n  static getInitialProps = (ctx) => {\n    const { store } = ctx\n    // you receive store from context\n    // you can access or do something with the store here\n    console.log('Initialized Redux Store', store);\n    return {}\n  }\n\n  render() {\n    return (\n      <CustomWrapper>\n        {super.render()}\n      </CustomWrapper>\n    )\n  }\n}\n\n```\nIf you put `document.jsx` and `wrap.jsx` on root directory (the same level with `package.json`), then Maleo will automatically register your custom Document and Wrap. Otherwise, you can add the path to your custom Document and Wrap on [Maleo config](#custom-configuration)\n\n---\n***We are also working on adding default and customizable `Error` component page***\n\n---\n\n## Custom Configuration\n\nFor more advanced configuration of Maleo.js, like `webpack` config, registering `plugins`, path to your routes, custom Document and Wrap, and adding `path alias`, you can create a `maleo.config.js` in the root of your project directory. (same directory with `package.json`)\n\n```js\n// maleo.config.js\n\nmodule.exports = {\n  /* config options here */\n}\n```\n\nHere are the API's for the configuration:\n\n<table>\n  <tr>\n    <td>Key</td>\n    <td>Type</td>\n    <td>Description</td>\n  </tr>\n  <tr>\n    <td><code>buildDir</code></td>\n    <td><code>String?</code> [<code>.maleo</code>]</td>\n    <td>Directory to put Maleo.js' build assets</td>\n  </tr>\n  <tr>\n    <td><code>cache</code></td>\n    <td><code>Boolean?</code> [<code>true</code>]</td>\n    <td>Enable webpack build caching</td>\n  </tr>\n  <tr>\n    <td><code>sourceMaps</code></td>\n    <td><code>Boolean?</code> [<code>true</code>]</td>\n    <td>Enable webpack to generate source maps</td>\n  </tr>\n  <tr>\n    <td><code>alias</code></td>\n    <td><code>Object?</code></td>\n    <td>A key value pair for aliasing path directory\n      <br>\n      <code>\n      { 'component': './src/component' }\n      </code>\n    </td>\n  </tr>\n  <tr>\n    <td><code>publicPath</code></td>\n    <td><code>String?</code> [<code>/_assets/</code>]</td>\n    <td>To customize webpack's <code>publicPath</code> \n    <br>Comes in handy if using CDN to put built assets</td>\n  </tr>\n  <tr>\n    <td><code>analyzeBundle</code></td>\n    <td><code>Boolean?</code> [<code>false</code>]</td>\n    <td>To enable webpack's bundle analyzer, for analyzing bundle sizes during bundle debugging should Maleo.js' build process got slow</td>\n  </tr>\n  <tr>\n    <td><code>webpack</code></td>\n    <td><code>Function?</code></td>\n    <td>To customize webpack configuration, more details <a href=\"#customize-webpack\">here</a></td>\n  </tr>\n  <tr>\n    <td><code>routes</code></td>\n    <td><code>string?</code> [<code>rootDir/routes.jsx</code>]</td>\n    <td>Path to your routes file</td>\n  </tr>\n  <tr>\n    <td><code>customDocument</code></td>\n    <td><code>string?</code> [<code>rootDir/document.jsx</code>]</td>\n    <td>Path to your custom document file</td>\n  </tr>\n  <tr>\n    <td><code>customWrap</code></td>\n    <td><code>string?</code> [<code>rootDir/wrap.jsx</code>]</td>\n    <td>Path to your custom wrap file</td>\n  </tr>\n  <tr>\n    <td><code>customApp</code></td>\n    <td><code>string?</code> [<code>rootDir/app.jsx</code>]</td>\n    <td>Path to your custom app file</td>\n  </tr>\n</table>\n\n#### Customize Server\n\nCreate a `server.js` file on root directory where your `package.json` lives.\nHere you can customize Maleo's server.\n```js\nimport { Server } from '@airy/maleo/server';\nimport path from 'path';\n\nimport routeConfig from './routes';\n\nconst PORT = process.env.PORT || 8080;\n\nconst maleoServer = Server.init({\n  port: PORT,\n});\n\nmaleoServer.run(() => {\n  console.log('Server running on port :', PORT);\n});\n```\n\nHere are the API's for the configuration:\n\n<table>\n  <tr>\n    <td>Key</td>\n    <td>Type</td>\n    <td>Description</td>\n  </tr>\n  <tr>\n    <td><code>port</code></td>\n    <td><code>Number?</code> [<code>3000</code>]</td>\n    <td>Port to run Maleo server</td>\n  </tr>\n  <tr>\n    <td><code>assetDir</code></td>\n    <td><code>String?</code> [<code>'<root>/.maleo/client'</code>]</td>\n    <td>Directory for all client related assets</td>\n  </tr>\n</table>\n\n#### Customize Webpack\n\nYou are able to extend Maleo.js' default webpack configuration by defining a function on `maleo.config.js`\n\n```js\n// maleo.config.js\n\nmodule.exports = {\n  webpack(config, context, next) {\n    // Perform customizations to webpack config\n    // Important: This call is required in order to let Maleo pass the config to webpack\n    return next(); \n  },\n};\n```\n\nWebpack function will receive three arguments:\n\n<table>\n  <tr>\n    <td>Argument</td>\n    <td>Details</td>\n  <tr>\n  <tr>\n    <td><code>config</code></td>\n    <td>This contains webpack configuration object that you can manipulate</td>\n  <tr>\n  <tr>\n    <td><code>context</code></td>\n    <td>This contains some keys that are useful for the build context\n      <br>\n      <table>\n        <tr>\n          <td><code>isDev</code></td>\n          <td><code>Boolean</code></td>\n          <td>Check if current build is for development</td>\n        </tr>\n        <tr>\n          <td><code>publicPath</code></td>\n          <td><code>String</code></td>\n          <td>Public Path of user defined or default's value</td>\n        </tr>\n        <tr>\n          <td><code>analyzeBundle</code></td>\n          <td><code>Boolean</code></td>\n          <td>Check if analyze bundle is enabled</td>\n        </tr>\n        <tr>\n          <td><code>buildDirectory</code></td>\n          <td><code>String</code></td>\n          <td>Build Directory of user defined or default's value</td>\n        </tr>\n        <tr>\n          <td><code>name</code></td>\n          <td><code>String</code></td>\n          <td>Build name <code>'server' || 'client'</code></td>\n        </tr>\n      </table>\n    </td>\n  <tr>\n  <tr>\n    <td><code>next</code></td>\n    <td>A callback required to be called to pass the custom configuration</td>\n  <tr>\n</table>\n\nExample of adding `ts-loader` through `maleo.config.js`:\n\n```js\n// maleo.config.js\n\n// Partly taken and modified from @airy/maleo-ts-plugin source\n// for simplicity purposes\n\nmodule.exports = {\n  webpack(config, context, next) {\n    const { isDev } = context\n\n    config.module.rules.push({\n      test: /\\.tsx?/,\n      exclude: /node_modules/,\n      use: [\n        require.resolve('@airy/maleo/lib/build/loaders/maleo-babel-loader'),\n        {\n          loader: 'ts-loader',\n          options: {\n            transpileOnly: true,\n          },\n        },\n      ],\n\n      if (isDev) {\n        config.plugins.push(new ForkTSCheckerWebpackPlugin());\n      }\n\n      return next();\n    })\n  },\n};\n```\n\n#### Customize Babel Config\n\nMaleo.js also let you have your own babel config. Just simply add `.babelrc` file at the root directory of your app.\n\nYou can include Maleo.js' babel preset in order to have latest JavaScript preset.\n\nHere's an example of `.babelrc` file:\n```json\n{\n  \"presets\": [\"@airy/maleo/babel\"],\n  \"plugins\": []\n}\n```\n\nThe `@airy/maleo/babel` preset includes everything you need to get your development started. The preset includes:\n- `@babel/preset-env`\n- `@babel/preset-react`\n- `@babel/plugin-proposal-class-properties`\n- `@babel/plugin-proposal-decorators`\n- `@babel/plugin-proposal-object-rest-spread`\n- `@babel/plugin-transform-runtime`\n- `react-loadable/babel`\n\n## CDN Support\n\nIf you are using a CDN, you can set up the `publicPath` setting and configure your CDN's origin to resolve to the domain that Maleo.js is hosted on.\n\n```js\n// maleo.config.js\n\nconst isProd = process.env.NODE_ENV === 'production';\n\nmodule.exports = {\n  assetPrefix: isProd && 'https://cdn.example.com';\n}\n```\n\n## FAQ\n\n== TO BE DETERMINED == \n\n## Plugins\n- [css-plugin](https://github.com/airyrooms/maleo.js/tree/canary/packages/plugins/css-plugin)\n- [typescript-plugin](https://github.com/airyrooms/maleo.js/tree/canary/packages/plugins/typescript-plugin)\n- [redux-plugin](https://github.com/airyrooms/maleo.js/tree/canary/packages/plugins/redux-plugin)\n\n\n## Contributing\n[Please follow these steps to contribute to Maleo.js](https://github.com/airyrooms/maleo.js/blob/canary/CONTRIBUTING.md)\n\n[Please follow these steps to contribute to Maleo.js' plugins](https://github.com/airyrooms/maleo.js/tree/canary/packages/plugins)\n\n\n## Contributors\n\nThis project exists thanks to all the people who contribute. [Contribute](CONTRIBUTING.md).\n[![contributors][oc-contributors-img]](https://github.com/airyrooms/maleo.js/graphs/contributors)\n\n\n## License\n\nMIT\n\n[oc-contributors-img]: https://opencollective.com/maleo.js/contributors.svg?width=890&button=false\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/airyrooms/maleo.js/issues"},"homepage":"https://github.com/airyrooms/maleo.js#readme","_id":"@airy/maleo@0.0.4-canary.49","_nodeVersion":"10.11.0","_npmVersion":"lerna/3.13.1/node@v10.11.0+x64 (linux)","dist":{"integrity":"sha512-oRqtT16EWAu3XXmAaoN4jZzx6dKrGiELX/WS7JoUeqWeFQQ0t6rMuw6SekryhJ3y2mRwqiSi/MlucTEb1b7O2w==","shasum":"78e515e5e9ee07ab4b9b467e2465f1a2160d315f","tarball":"https://registry.npmjs.org/@airy/maleo/-/maleo-0.0.4-canary.49.tgz","fileCount":82,"unpackedSize":222311,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJciL6BCRA9TVsSAnZWagAAIvoP/0p/WK5inHeARysbyPAv\nTSqRQQIKvBqkILoyMvk2od2bHRhpqeKtlu6SqcbsBMHH1UOoNyk7tXLRyF/k\nC2OMec73Kwww1S1GLDkfMCLT6u4wtdCl57Imsiq2ewyd+HpfhKpzEDG06BoB\nz4uimW915834YSXyHHP2MUML28MMSuZyo5RlOuLXHYK/6TN2eDGj63Pp6y+g\n0edmMeHoGhRdM+JhmAzBP222DCzZM9SwcDkG4xwIS+6cHwC/GlCJGwhz50X9\nSjVwBnEzmz2G35jWyGs2EQEUlEHHc7fYpdAWQxvwBvmC4SpS63ZINN+Ruv3y\nCXVAiv230eYTQNFYOoNy7B6nV+IGgE66nP5gtxQ4JjQukwThWF6L7nGtVu6i\n46NM5YxTnN7DNCxfrfG4WGzTN8l9O2dPWJsEXfICfk4n2fyihs2O3v28NDhj\nrPgydvVOvk4f1VI9ArX9ffKTdX3iGmJkgMe8bk0scahJZHkb9bBmMMMFYXMD\nrLB5cyHPl98EbA6tSO2TzfQtd4a+bmtPmSg5EbslrE7B3GJfp+b+qaC9kaN9\nc0cK+dEtBXm/nYY+z6NMyDOeqRXsXKdzQh7/hNPZ04k6PYyJLdK4WUlflt2Q\nO7uijp2xVuNVEUxZ1NCdaGnM1uZ6DO5iVgE6RxP9INelDKqIb02lxidwZs/5\nCZQg\r\n=c3wX\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIEAJv3TlJxsI/3gNieqvParWcE/nQOjnvjpTFUuE95GVAiEAmfw6iV8Zqa37dN1r6J9YXrzbivKZsuDLsBEhUdb+0eM="}]},"maintainers":[{"name":"airyrooms-engineering","email":"engineering@airyrooms.com"}],"_npmUser":{"name":"airyrooms-engineering","email":"engineering@airyrooms.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/maleo_0.0.4-canary.49_1552465536895_0.3530731568670298"},"_hasShrinkwrap":false},"0.0.5-canary.49":{"name":"@airy/maleo","version":"0.0.5-canary.49","description":"Un-opinionated Universal Rendering Javascript Framework","repository":{"type":"git","url":"git+https://github.com/airyrooms/maleo.js","bugs":"https://github.com/airyrooms/maleo.js/issues"},"author":"Airy","license":"MIT","private":false,"sideEffects":false,"scripts":{"build":"rm -rf dist && gulp","watch":"gulp watch","prepublish":"yarn build"},"bin":"lib/bin/maleo.js","dependencies":{"@babel/core":"7.1.2","@babel/plugin-proposal-class-properties":"7.1.0","@babel/plugin-proposal-decorators":"7.1.2","@babel/plugin-proposal-object-rest-spread":"7.0.0","@babel/plugin-syntax-dynamic-import":"7.0.0","@babel/plugin-transform-runtime":"7.1.0","@babel/preset-env":"7.1.0","@babel/preset-react":"7.0.0","@babel/runtime":"7.1.2","@babel/runtime-corejs2":"7.2.0","babel-loader":"8.0.4","case-sensitive-paths-webpack-plugin":"2.1.2","es6-promise":"4.2.5","eventsource":"^1.0.7","express":"4.16.4","figlet":"1.2.1","friendly-errors-webpack-plugin":"1.7.0","hard-source-webpack-plugin":"0.13.1","helmet":"3.14.0","isomorphic-fetch":"2.2.1","loader-utils":"1.1.0","node-notifier":"5.2.1","nodemon":"1.18.4","react":"^16.8.4","react-dom":"16.8.4","react-helmet":"5.2.0","react-loadable":"5.5.0","react-router":"4.4.0-beta.1","react-router-config":"4.4.0-beta.6","rimraf":"2.6.3","source-map-support":"0.5.9","terser-webpack-plugin":"1.1.0","webpack":"4.29.0","webpack-bundle-analyzer":"3.0.3","webpack-cli":"3.1.2","webpack-dev-middleware":"3.4.0","webpack-hot-middleware":"2.24.3","webpackbar":"2.6.3"},"peerDependencies":{"react":"^16.8.4","react-router-dom":"^4.4.0-beta.1"},"devDependencies":{"@babel/preset-typescript":"^7.1.0","@types/webpack-bundle-analyzer":"^2.13.0","babel-plugin-module-resolver":"^3.1.1","gulp":"gulpjs/gulp.git#4.0","gulp-babel":"^8.0.0","gulp-clean":"^0.4.0","gulp-sourcemaps":"^2.6.4","gulp-uglify":"^3.0.1","gulp-watch":"^5.0.1","react":"^16.8.4","react-router-dom":"^4.4.0-beta.1"},"gitHead":"020fc086d23f46c357d61c0d5693dc7f63f9828a","_nodeVersion":"10.11.0","_npmVersion":"lerna/3.7.2/node@v10.11.0+x64 (linux)","_npmUser":{"name":"airyrooms-engineering","email":"engineering@airyrooms.com"},"_id":"@airy/maleo@0.0.5-canary.49","dist":{"integrity":"sha512-cJscrE5VbsQ6riSve5hiwewt3U3I5xU0m0g7xl5gKNvAuR98NXb6giZz9ZF/fPBDgx9maPw3bq3TrYpSn6rJnQ==","shasum":"c86d70008dedef19934ce49267a8d6e3632e00c6","tarball":"https://registry.npmjs.org/@airy/maleo/-/maleo-0.0.5-canary.49.tgz","fileCount":82,"unpackedSize":222451,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJciOi5CRA9TVsSAnZWagAARpMP/iLHUeYmJy8uYodMFJbl\nF9LFfLuKh7zHT6kBQf/7Q0Zs6wAF49h6axO2oGkkwV2aPo37bmxUkp/TBcbB\nxzaz87hggvu6OA5k9IbBiVdoFkYO4s0So3YoF9KZE9joXieHe/LWJnsh7uUO\nj/gW75/eb48GY2EhvSTWDcg5KnZWY1duRl18HPUDp35Azwpb/lBK8DyaaOA1\n32FiIAjxTXc2UF+5LtOESkdhAQlA2Nma2y55YRV1OOsAdzkERC3IM762kvh3\n5yY1wV/PD2WLJm3jMtXBT/UBrmb8K1Pj+sfpvobqL1kjAHaYBflvDMKluSF9\n4ojL4SqKUSGPVOFsaRgJ5GURYmvaaEGJN3kza2j74097XiXAoNFnxuxCehUG\nw2/SJO555iFaeINE5zCDpsN4xhKaUxrnhjKx+OUL0FBilYMu800yt+VI3O3C\nLCsFaIHomF3PDjMHG1sAXfc47rHYd7mEd3WS5t12FiZFZmjR0SnHWGoZM+y1\nLdaLDMAWgGD7iPQMY0TTBOQXsbijKZWDY7TocCyfAZq3EFIXZQlj+JAiZhBD\n+LBLgcitbtMdC5tYnESIALmS0qkylbYgBcuaibggdopaw9QS7/HNjlcICFqN\nVSNJ6nJze84cVMDaPAgh4ZbD5GGZkep0wWz9XQfMMaMRG0gvqGZIhUFlJCY8\n/xCC\r\n=6SV2\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIBBedi4ctPCOwoRCD55RyY313YcwlJmwkEYjCplt/cZuAiEA71CjsJoX2vYVqRijYJBApmui2u/+PGeStyY/Tputm8M="}]},"maintainers":[{"name":"airyrooms-engineering","email":"engineering@airyrooms.com"}],"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/maleo_0.0.5-canary.49_1552476344911_0.12413317511888078"},"_hasShrinkwrap":false},"0.0.6-canary.49":{"name":"@airy/maleo","version":"0.0.6-canary.49","description":"Un-opinionated Universal Rendering Javascript Framework","repository":{"type":"git","url":"git+https://github.com/airyrooms/maleo.js","bugs":"https://github.com/airyrooms/maleo.js/issues"},"author":"Airy","license":"MIT","private":false,"sideEffects":false,"scripts":{"build":"rm -rf dist && gulp","watch":"gulp watch","prepublish":"yarn build"},"bin":"lib/bin/maleo.js","dependencies":{"@babel/core":"7.1.2","@babel/plugin-proposal-class-properties":"7.1.0","@babel/plugin-proposal-decorators":"7.1.2","@babel/plugin-proposal-object-rest-spread":"7.0.0","@babel/plugin-syntax-dynamic-import":"7.0.0","@babel/plugin-transform-runtime":"7.1.0","@babel/preset-env":"7.1.0","@babel/preset-react":"7.0.0","@babel/runtime":"7.1.2","@babel/runtime-corejs2":"7.2.0","babel-loader":"8.0.4","case-sensitive-paths-webpack-plugin":"2.1.2","es6-promise":"4.2.5","eventsource":"^1.0.7","express":"4.16.4","figlet":"1.2.1","friendly-errors-webpack-plugin":"1.7.0","hard-source-webpack-plugin":"0.13.1","helmet":"3.14.0","isomorphic-fetch":"2.2.1","loader-utils":"1.1.0","node-notifier":"5.2.1","nodemon":"1.18.4","react":"^16.8.4","react-dom":"16.8.4","react-helmet":"5.2.0","react-loadable":"5.5.0","react-router":"4.4.0-beta.1","react-router-config":"4.4.0-beta.6","rimraf":"2.6.3","source-map-support":"0.5.9","terser-webpack-plugin":"1.1.0","webpack":"4.29.0","webpack-bundle-analyzer":"3.0.3","webpack-cli":"3.1.2","webpack-dev-middleware":"3.4.0","webpack-hot-middleware":"2.24.3","webpackbar":"2.6.3"},"peerDependencies":{"react":"^16.8.4","react-router-dom":"^4.4.0-beta.1"},"devDependencies":{"@babel/preset-typescript":"^7.1.0","@types/webpack-bundle-analyzer":"^2.13.0","babel-plugin-module-resolver":"^3.1.1","gulp":"gulpjs/gulp.git#4.0","gulp-babel":"^8.0.0","gulp-clean":"^0.4.0","gulp-sourcemaps":"^2.6.4","gulp-uglify":"^3.0.1","gulp-watch":"^5.0.1","react":"^16.8.4","react-router-dom":"^4.4.0-beta.1"},"gitHead":"3419fc9f07995924bdd24c45d9dcd1cba60c2903","_nodeVersion":"10.11.0","_npmVersion":"lerna/3.7.2/node@v10.11.0+x64 (linux)","_npmUser":{"name":"airyrooms-engineering","email":"engineering@airyrooms.com"},"_id":"@airy/maleo@0.0.6-canary.49","dist":{"integrity":"sha512-Aey5tVXr+eclyv5BkkKmdSZ/tbFxZ9p8Lrp/19Mx31UmYqwnB/H7rK4BS2z3NiK9ZoboHQbrYkaTFmxyRblQrg==","shasum":"6e188c18c6c331098cee0372e6bb7a670ec8d44e","tarball":"https://registry.npmjs.org/@airy/maleo/-/maleo-0.0.6-canary.49.tgz","fileCount":82,"unpackedSize":222451,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJcjwviCRA9TVsSAnZWagAAZugP/RAZCLQNd7pV0gitngj+\nLJEL4cwo+QXYTE7SeFAIFZCVy7rEnm4umyZx+Cu76Evx1neIB8slPK8OcVtB\n0W04H9PvDwqrSIkEIlweMn2WDDXpuhBfy2TP2sK8JZ+hQ3Jjoo52eN7wS7m1\n3CTvfhwbsCDFOBXWQ+exZ+/qWEoMkWXTzb0erFZztN83bkiaZopqbOfCv+I4\n5VmkudAMor4BDZvYsPsFHJwsmyrVSUD60F8c5zQqxPflOOTw3sI2bGtGsEKI\noyTWV9x6Jx+b/zgGNWQIs9K6P/G6si4E9yDBpun9uTbicJTSNPc+Rrejh5jQ\n7Deu2txNYJJbC+YWRTU9LdshUsrDmV1RIUrw6EkKVJAPz7dcTCfuxIZASCbm\n039pwDPT5hqgSwSYgS8LWDvxMAYybpfka4+dTXAH7sHAM0zYQrIDej8ZSMpq\nr0m8IfJI7aqxu6c2aRhoybiIEH3JoLVzWUHFk1PaDpiR0R941Mo3grXzMytX\nRKtvf5OyoxJ8aloiVrZC1v8rbaWv4afq0mNrJcVU9ale3wK4ZLDI+MwOgL+/\nVUoDHQZj/CUPXbIa9E+mRICW6PxY7CtZ1BE5nOqdkZxtmpoTaeRxfOXsLf3W\n77Dw0nkK0TBy0DeC044Uy71qsDKtuLKct+lBgiYdoZtzfTzxs4CmNAANoYAr\nzLhA\r\n=sQVD\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIETBt72q1LYyDNvjbcAHLvC8G9fC+kSz0FXKMeT+swdVAiAjPh8QMaBl/DL+LI9IYUsVKldKwxkjxx4Uk1iaigbVng=="}]},"maintainers":[{"name":"airyrooms-engineering","email":"engineering@airyrooms.com"}],"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/maleo_0.0.6-canary.49_1552878561368_0.8379111767736467"},"_hasShrinkwrap":false},"0.0.6-canary.0":{"name":"@airy/maleo","version":"0.0.6-canary.0","description":"Un-opinionated Universal Rendering Javascript Framework","repository":{"type":"git","url":"git+https://github.com/airyrooms/maleo.js","bugs":"https://github.com/airyrooms/maleo.js/issues"},"author":"Airy","license":"MIT","private":false,"sideEffects":false,"scripts":{"build":"rm -rf dist && gulp","watch":"gulp watch","prepublish":"yarn build"},"bin":"lib/bin/maleo.js","dependencies":{"@babel/core":"7.1.2","@babel/plugin-proposal-class-properties":"7.1.0","@babel/plugin-proposal-decorators":"7.1.2","@babel/plugin-proposal-object-rest-spread":"7.0.0","@babel/plugin-syntax-dynamic-import":"7.0.0","@babel/plugin-transform-runtime":"7.1.0","@babel/preset-env":"7.1.0","@babel/preset-react":"7.0.0","@babel/runtime":"7.1.2","@babel/runtime-corejs2":"7.2.0","babel-loader":"8.0.4","case-sensitive-paths-webpack-plugin":"2.1.2","es6-promise":"4.2.5","eventsource":"^1.0.7","express":"4.16.4","figlet":"1.2.1","friendly-errors-webpack-plugin":"1.7.0","hard-source-webpack-plugin":"0.13.1","helmet":"3.14.0","isomorphic-fetch":"2.2.1","loader-utils":"1.1.0","node-notifier":"5.2.1","nodemon":"1.18.4","react":"^16.8.4","react-dom":"16.8.4","react-helmet":"5.2.0","react-loadable":"5.5.0","react-router":"4.4.0-beta.1","react-router-config":"4.4.0-beta.6","rimraf":"2.6.3","source-map-support":"0.5.9","terser-webpack-plugin":"1.1.0","webpack":"4.29.0","webpack-bundle-analyzer":"3.0.3","webpack-cli":"3.1.2","webpack-dev-middleware":"3.4.0","webpack-hot-middleware":"2.24.3","webpackbar":"2.6.3"},"peerDependencies":{"react":"^16.8.4","react-router-dom":"^4.4.0-beta.1"},"devDependencies":{"@babel/preset-typescript":"^7.1.0","@types/webpack-bundle-analyzer":"^2.13.0","babel-plugin-module-resolver":"^3.1.1","gulp":"gulpjs/gulp.git#4.0","gulp-babel":"^8.0.0","gulp-clean":"^0.4.0","gulp-sourcemaps":"^2.6.4","gulp-uglify":"^3.0.1","gulp-watch":"^5.0.1","react":"^16.8.4","react-router-dom":"^4.4.0-beta.1"},"gitHead":"9919fa3f16cda398c3a0f7975628004a784d349f","_nodeVersion":"10.11.0","_npmVersion":"lerna/3.7.2/node@v10.11.0+x64 (linux)","_npmUser":{"name":"airyrooms-engineering","email":"engineering@airyrooms.com"},"_id":"@airy/maleo@0.0.6-canary.0","dist":{"integrity":"sha512-N/JYY0ckKQwA4X4aYRviGugf/w1e7BLADlT+sG3vk+n8LbG6mxZGtmu4tvnqa6BlMGTMDoxfIEQ4vM0J7t8oIQ==","shasum":"e063bf582afcec6bdea2dbc7ab39a79ecb63865b","tarball":"https://registry.npmjs.org/@airy/maleo/-/maleo-0.0.6-canary.0.tgz","fileCount":83,"unpackedSize":225604,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJcjx5GCRA9TVsSAnZWagAA7NwP/RcrLnf81+72jwTisQhE\n3/CPc+WJ0WNcOTUjsLLfZBVgt4CrF8bu3rsn6isqJ2foncAfnjajouAylpAb\nKpjI6i2HDFxaqZdI5No4oxcoRQNHmk6Wum8KEbVdBv0fUEd4NPprXXgIokDQ\nndjnQzbnN9xBU+re33tpj1PQsgfRF1mp9nzk/866t3j+4gMur0uUqFP2BA5Q\n0s/RPjm4+OL+XOI+F8eQWgCvLFDoBwZmTKnmcuoqSxDFn+VDFwBIIU9/wCsn\n8KNmE2+44bazDLc4NFaWLhMw+gSamUgIb2+MtWZN5UyNZQZfhr/j5Qk9/MRk\nanlQjNoFa9KVg1KjYCUp+ej65lDOgDjmpqmCC3fQXlAKRYk+0tkf0MRhJIGK\nNsVo36TzMc6V4cO1b6GDw18ZAw7LiG4E8IDJhEoGaB8JZ3cobWRH/ZhceVqo\ncynnkucftxGUFg3oaFbMi1DTu5/RAcjs5gM6hbBvVT3Jp0P9CP3yxpnNpnLf\nP7vt+cva1KqMC9CPiI2+toeu/1t7aK02PeHhYsH7yO5GO0Dav7x2/UFZJ6gI\nsaNI1agpaO+/abuVtF3iacGmCxHQya6mrzX00yUvV3X3CuN2B1gxGNsmtofQ\nbC23envxBftm8Hlet0jXxn4LcioeqfSrqE9vgPtxIvpSwYhGdq2pxzWdAqVa\nHe1g\r\n=O5jn\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIAc0ux0HY0hMJgVmiCzfyU4JeMVG7huO7MtMP9Bo69GMAiBaegRDdLjVrGH6b8JiwTIiWLM5qnvbiYRT+Ed2rAq9KQ=="}]},"maintainers":[{"name":"airyrooms-engineering","email":"engineering@airyrooms.com"}],"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/maleo_0.0.6-canary.0_1552883269936_0.19036136847110718"},"_hasShrinkwrap":false},"0.0.8-canary.0":{"name":"@airy/maleo","version":"0.0.8-canary.0","description":"Un-opinionated Universal Rendering Javascript Framework","repository":{"type":"git","url":"git+https://github.com/airyrooms/maleo.js","bugs":"https://github.com/airyrooms/maleo.js/issues"},"author":"Airy","license":"MIT","private":false,"sideEffects":false,"scripts":{"build":"rm -rf dist && gulp","watch":"gulp watch","prepublish":"yarn build"},"bin":"lib/bin/maleo.js","dependencies":{"@babel/core":"7.1.2","@babel/plugin-proposal-class-properties":"7.1.0","@babel/plugin-proposal-decorators":"7.1.2","@babel/plugin-proposal-object-rest-spread":"7.0.0","@babel/plugin-syntax-dynamic-import":"7.0.0","@babel/plugin-transform-runtime":"7.1.0","@babel/preset-env":"7.1.0","@babel/preset-react":"7.0.0","@babel/runtime":"7.1.2","@babel/runtime-corejs2":"7.2.0","babel-loader":"8.0.4","case-sensitive-paths-webpack-plugin":"2.1.2","es6-promise":"4.2.5","eventsource":"^1.0.7","express":"4.16.4","figlet":"1.2.1","friendly-errors-webpack-plugin":"1.7.0","hard-source-webpack-plugin":"0.13.1","helmet":"3.14.0","isomorphic-fetch":"2.2.1","loader-utils":"1.1.0","node-notifier":"5.2.1","nodemon":"1.18.4","react-dom":"16.8.4","react-helmet":"5.2.0","react-loadable":"5.5.0","react-router-config":"4.4.0","react-router-dom":"4.4.0","rimraf":"2.6.3","source-map-support":"0.5.9","terser-webpack-plugin":"1.1.0","webpack":"4.29.0","webpack-bundle-analyzer":"3.0.3","webpack-cli":"3.1.2","webpack-dev-middleware":"3.4.0","webpack-hot-middleware":"2.24.3","webpackbar":"2.6.3"},"peerDependencies":{"react":"^16.8.4"},"devDependencies":{"@babel/preset-typescript":"^7.1.0","@types/webpack-bundle-analyzer":"^2.13.0","babel-plugin-module-resolver":"^3.1.1","gulp":"gulpjs/gulp.git#4.0","gulp-babel":"^8.0.0","gulp-clean":"^0.4.0","gulp-sourcemaps":"^2.6.4","gulp-uglify":"^3.0.1","gulp-watch":"^5.0.1","react":"^16.8.4"},"gitHead":"bc3f3f9e22577a83803d786c0637e891290dfa28","_nodeVersion":"10.11.0","_npmVersion":"lerna/3.7.2/node@v10.11.0+x64 (linux)","_npmUser":{"name":"airyrooms-engineering","email":"engineering@airyrooms.com"},"_id":"@airy/maleo@0.0.8-canary.0","dist":{"integrity":"sha512-o64fbDWpJXejN/qzHUJIrxXDgnuEA9/JrJVIX5A4Sy7wjUOz+zAn9mipwlP33FEPwRNOA3ehhvrlbgQis7uKEg==","shasum":"d609cff0d058cd5714857d2e7fec219f114e1462","tarball":"https://registry.npmjs.org/@airy/maleo/-/maleo-0.0.8-canary.0.tgz","fileCount":82,"unpackedSize":222646,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJcj2LHCRA9TVsSAnZWagAACckP/RwNJWrq9mbqyz2iz80g\nL6SbHLpvwPtawMF3IKnvQBPt7rD3HOHkLPo/hS68+/buDfxdmfWq7eep1Et0\nLm4lwmsQJK/Y444w6qPoVXnayqf+eIK4rPOB/UvHg7bRENCSrHu33SV/0Ifb\neGLEYQ//yJdTYukjDgmBdY6JwKIi1dlvT1nOOEqML7emY3tLtNxuEhjPI4E+\nupgf95yMvL1p7ru6rF8LJQ+k0hlNnBYiM7kj0gFgMMjrOWmn7t0wSeHwoot+\nhOGCaQrjMPHD2JRP1DS6pBkVeM5RCHsVEnPdFT5gosiwE/vjgnb7AZtcoMSP\n1Jza0jTtEytq+E3fqazzEvE+7aBPhL6R59m7PLnGFZAu7KroW4g3gL5Le8Y1\nSAue4t2rB8M2W6oDFN06hbwEGkm2uNrzoj7Rs4gyZQpMbN+MlWMMvJiTUKQj\nNjyWrBXQjLKy3gVM/ZRbbYBz4AsLcsUB6Fa9T3ada21iARNsWqkpC14vkjf/\ngBJMSy+70F8YIkbxcC4jm550qJJxxwaSWsaAsfht86Qk/lwN0X/MsGaS/79m\nykdx05S5jWxE2ua4BVm4fhgtU256cLa6Y6luQlcLpt3JEkW63W+tIZAjEfHC\n0P92/yMGyRf2/Zy7MJBo1EwPEA0USMqvjw+QdJ3EeqpeXGsR76MmZT5xqQ8B\n5iGG\r\n=W8Pu\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQD1oSCZE1Q/3EA2fLAZqIJRntK5Wac7i2KDzdlzKdq8OQIgcpmoRREpMUhz0ey5lkGvKtKEp2CgK0xTUq50cqMidio="}]},"maintainers":[{"name":"airyrooms-engineering","email":"engineering@airyrooms.com"}],"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/maleo_0.0.8-canary.0_1552900807315_0.12173671837987254"},"_hasShrinkwrap":false},"0.0.8-canary.2":{"name":"@airy/maleo","version":"0.0.8-canary.2","description":"Un-opinionated Universal Rendering Javascript Framework","repository":{"type":"git","url":"git+https://github.com/airyrooms/maleo.js","bugs":"https://github.com/airyrooms/maleo.js/issues"},"author":"Airy","license":"MIT","private":false,"sideEffects":false,"scripts":{"build":"rm -rf dist && gulp","watch":"gulp watch","prepublish":"yarn build"},"bin":"lib/bin/maleo.js","dependencies":{"@babel/core":"7.1.2","@babel/plugin-proposal-class-properties":"7.1.0","@babel/plugin-proposal-decorators":"7.1.2","@babel/plugin-proposal-object-rest-spread":"7.0.0","@babel/plugin-syntax-dynamic-import":"7.0.0","@babel/plugin-transform-runtime":"7.1.0","@babel/preset-env":"7.1.0","@babel/preset-react":"7.0.0","@babel/runtime":"7.1.2","@babel/runtime-corejs2":"7.2.0","babel-loader":"8.0.4","case-sensitive-paths-webpack-plugin":"2.1.2","es6-promise":"4.2.5","eventsource":"^1.0.7","express":"4.16.4","figlet":"1.2.1","friendly-errors-webpack-plugin":"1.7.0","hard-source-webpack-plugin":"0.13.1","helmet":"3.14.0","isomorphic-fetch":"2.2.1","loader-utils":"1.1.0","node-notifier":"5.2.1","nodemon":"1.18.4","react-dom":"16.8.4","react-helmet":"5.2.0","react-loadable":"5.5.0","react-router-config":"4.4.0","react-router-dom":"4.4.0","rimraf":"2.6.3","source-map-support":"0.5.9","terser-webpack-plugin":"1.1.0","webpack":"4.29.0","webpack-bundle-analyzer":"3.0.3","webpack-cli":"3.1.2","webpack-dev-middleware":"3.4.0","webpack-hot-middleware":"2.24.3","webpackbar":"2.6.3"},"peerDependencies":{"react":"^16.8.4"},"devDependencies":{"@babel/preset-typescript":"^7.1.0","@types/webpack-bundle-analyzer":"^2.13.0","babel-plugin-module-resolver":"^3.1.1","gulp":"gulpjs/gulp.git#4.0","gulp-babel":"^8.0.0","gulp-clean":"^0.4.0","gulp-sourcemaps":"^2.6.4","gulp-uglify":"^3.0.1","gulp-watch":"^5.0.1","react":"^16.8.4"},"gitHead":"95dcd686aecd1888c004397d49cea77cdc5aafc1","_nodeVersion":"10.11.0","_npmVersion":"lerna/3.7.2/node@v10.11.0+x64 (linux)","_npmUser":{"name":"airyrooms-engineering","email":"engineering@airyrooms.com"},"_id":"@airy/maleo@0.0.8-canary.2","dist":{"integrity":"sha512-WDGS88BKgHMsQgTqw17uE81JC3J/ss3JYjJRrZBWhpeTeYzezOKGZKMwHl8lOlUeQDoFbT5anGzMLi8BGNPrPQ==","shasum":"ce082ac0066653cbf50471f4bc29bd6144a16e98","tarball":"https://registry.npmjs.org/@airy/maleo/-/maleo-0.0.8-canary.2.tgz","fileCount":83,"unpackedSize":223001,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJcj2S2CRA9TVsSAnZWagAAE4AP/1QnNF0+OxeLoPhXeNQV\ns9UnlRRHBck3dFwHGkIq/44kQpEy9XAGDX3n+fGLqkH0B8HLgQJ0ZLummGnb\nEI7e+Ow+KNByA1VmPC3FGUM1V3DGG6ThPLfI8flNNWbrKcaFDYER+e2qtnxx\nAuRZcfW8kKE+KqMi8mi0QPRjHrprHcPRBM8OjrUtaz9KAurYxc6sbrillKjR\nSeMra5YP3zEXHY+qw2uYPc8+YFsVMXa+k98BfLhJanm4kjNSi2aqTdJvY5wM\n40dY3xU5cRP+ifIpkgudV8rCxXHblh4KcXETiiCzY4h8NeFrQyPTSL4NGCUk\nL5y6Xn5YGV79CwE+BGMZYq+JblnDGB8cvTFOiM5QMu6EovPtHN2ERzqjIOQB\n3OZaWN94yx4W+tXbDHrgjY+77PAANbvvMujP8ZKjjqUasoeYEaXjm4vPkQvG\nrSOdP3GU6dfhIrO45z3MK3bxCUrEcS4BeCzGiabdDRZ1iz/jXJBPwmvh6vDU\nGO/CXLZ5DSS2+thX28WvzHUiYfhjD1jmNrOq4ZLrp+4Xpmdems/FCLw3Viq1\nwFtn1pQKl/W5ZUIfL5ilAFVcoGv7pzR5Vv8VnitSaPe+u385j7YK+z63cPtc\nMMN+l2qQqHJ146De1NlaTrLNQXhOyn0j2xY4yaKgyPCGz1C1ZGlSNmnt+frE\nFHiH\r\n=WTkG\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIEQIyIMRSNbxBSM4ZsJWUslczwlihkPXl5kbBo2xxYGvAiBQKRUSc+Xc/7dMV+hJi/I3fkxnQuLEaBP9LQDJj6xg5Q=="}]},"maintainers":[{"name":"airyrooms-engineering","email":"engineering@airyrooms.com"}],"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/maleo_0.0.8-canary.2_1552901300943_0.9679070102698586"},"_hasShrinkwrap":false},"0.0.8-canary.3":{"name":"@airy/maleo","version":"0.0.8-canary.3","description":"Un-opinionated Universal Rendering Javascript Framework","repository":{"type":"git","url":"git+https://github.com/airyrooms/maleo.js","bugs":"https://github.com/airyrooms/maleo.js/issues"},"author":"Airy","license":"MIT","private":false,"sideEffects":false,"scripts":{"build":"rm -rf dist && gulp","watch":"gulp watch","prepublish":"yarn build"},"bin":"lib/bin/maleo.js","dependencies":{"@babel/core":"7.1.2","@babel/plugin-proposal-class-properties":"7.1.0","@babel/plugin-proposal-decorators":"7.1.2","@babel/plugin-proposal-object-rest-spread":"7.0.0","@babel/plugin-syntax-dynamic-import":"7.0.0","@babel/plugin-transform-runtime":"7.1.0","@babel/preset-env":"7.1.0","@babel/preset-react":"7.0.0","@babel/runtime":"7.1.2","@babel/runtime-corejs2":"7.2.0","babel-loader":"8.0.4","case-sensitive-paths-webpack-plugin":"2.1.2","es6-promise":"4.2.5","eventsource":"^1.0.7","express":"4.16.4","figlet":"1.2.1","friendly-errors-webpack-plugin":"1.7.0","hard-source-webpack-plugin":"0.13.1","helmet":"3.14.0","isomorphic-fetch":"2.2.1","loader-utils":"1.1.0","node-notifier":"5.2.1","nodemon":"1.18.4","react-dom":"16.8.4","react-helmet":"5.2.0","react-loadable":"5.5.0","react-router-config":"5.0.0","react-router-dom":"5.0.0","rimraf":"2.6.3","source-map-support":"0.5.9","terser-webpack-plugin":"1.1.0","webpack":"4.29.0","webpack-bundle-analyzer":"3.0.3","webpack-cli":"3.1.2","webpack-dev-middleware":"3.4.0","webpack-hot-middleware":"2.24.3","webpackbar":"2.6.3"},"peerDependencies":{"react":"^16.8.4"},"devDependencies":{"@babel/preset-typescript":"^7.1.0","@types/webpack-bundle-analyzer":"^2.13.0","babel-plugin-module-resolver":"^3.1.1","gulp":"gulpjs/gulp.git#4.0","gulp-babel":"^8.0.0","gulp-clean":"^0.4.0","gulp-sourcemaps":"^2.6.4","gulp-uglify":"^3.0.1","gulp-watch":"^5.0.1","react":"^16.8.4"},"gitHead":"eb9bf1801764d66f65afd88f22115d07fe5fecce","_nodeVersion":"10.11.0","_npmVersion":"lerna/3.7.2/node@v10.11.0+x64 (linux)","_npmUser":{"name":"airyrooms-engineering","email":"engineering@airyrooms.com"},"_id":"@airy/maleo@0.0.8-canary.3","dist":{"integrity":"sha512-ZnKL2Nq37VWC4E7u1wR2tetO3JuGaEHHyj1u07qF2yOi0uING+7GjNUwhgpzeXe6XUn8mTG8QZHCLJAA5D4H2Q==","shasum":"2c6802db929243dd60900d8598f2b9b1e9766425","tarball":"https://registry.npmjs.org/@airy/maleo/-/maleo-0.0.8-canary.3.tgz","fileCount":83,"unpackedSize":223432,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJckGxgCRA9TVsSAnZWagAAlIoP/izyD+1WYhEFm7Lj00aV\ns2+p+UPlmARZxGs42a4bW8Vc64zM1mT82KATyatGxggMI4eLJs2j7QaOoBYu\nhFl0nIZ35TsLAt7ZaOVqXLzoIHnN+wv49q7lFH+EPC5cWhQG8lEspP8xFYUx\nYcCZ+CYCLY6HlvwYpLSb+ofo7vs2KdZZHzW5CR/6Qr9BGFwMzP1W2gAFkSuo\nwNsnbl4Yi1lc133ZiKFaZD3M7EqqHpOn+30yEvcGEeosQ7Lc6zUWIDYxJx+F\nyD5O7xjxw1lxBMPtugADejFdnKf4YT/NE4oWG2rkwvu17QVm1bRvtnoYIazt\nnoVbvyjt6jq50UKTNlmzuZo/x7bsFZCU7zXvycF+h4oyMaqMDUMKxuWL9yxf\nO3ZxOHYj9pfwCWtohw+DVOs8g30m/pqwty0YemeW3q6FSW+ETwBOtdn+/keF\nKpoQRaB1IT4QS33sFK1BBL9kNR00AhxIau84P/Y+I5T7yq2bsykjdhdb1o9r\ntjPp/HrbhL9p3AbsjHryLUuwcik+XtvClviUwtN+0GFEbNdvO1QVRNp1Wgvo\nhvH61comUaYc1u3REgrsFy0CyBoyq28COf7oArCowS35i5EbD0gUVD2QXBRA\nDADLawhwdKyp7QkRWQiLodYNqx2NDVHDO7gZb00YxGtH2AT5JtpAAruC12dA\nTsDj\r\n=VupW\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDoMpEAIQAftqlkL3jpeJqmwJQIcUTZUcfuUnA1f7vz2AIgaBKdOjGfQOY/Vb1pzvoVpleq/+Q2ywzefEb4/pndmeU="}]},"maintainers":[{"name":"airyrooms-engineering","email":"engineering@airyrooms.com"}],"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/maleo_0.0.8-canary.3_1552968799588_0.8926283699334154"},"_hasShrinkwrap":false},"0.0.8":{"name":"@airy/maleo","version":"0.0.8","description":"Un-opinionated Universal Rendering Javascript Framework","repository":{"type":"git","url":"git+https://github.com/airyrooms/maleo.js","bugs":"https://github.com/airyrooms/maleo.js/issues"},"author":"Airy","license":"MIT","private":false,"sideEffects":false,"scripts":{"build":"rm -rf dist && gulp","watch":"gulp watch","prepublish":"yarn build"},"bin":"lib/bin/maleo.js","dependencies":{"@babel/core":"7.1.2","@babel/plugin-proposal-class-properties":"7.1.0","@babel/plugin-proposal-decorators":"7.1.2","@babel/plugin-proposal-object-rest-spread":"7.0.0","@babel/plugin-syntax-dynamic-import":"7.0.0","@babel/plugin-transform-runtime":"7.1.0","@babel/preset-env":"7.1.0","@babel/preset-react":"7.0.0","@babel/runtime":"7.1.2","@babel/runtime-corejs2":"7.2.0","babel-loader":"8.0.4","case-sensitive-paths-webpack-plugin":"2.1.2","es6-promise":"4.2.5","eventsource":"^1.0.7","express":"4.16.4","figlet":"1.2.1","friendly-errors-webpack-plugin":"1.7.0","hard-source-webpack-plugin":"0.13.1","helmet":"3.14.0","isomorphic-fetch":"2.2.1","loader-utils":"1.1.0","node-notifier":"5.2.1","nodemon":"1.18.4","react-dom":"16.8.4","react-helmet":"5.2.0","react-loadable":"5.5.0","react-router-config":"5.0.0","react-router-dom":"5.0.0","rimraf":"2.6.3","source-map-support":"0.5.9","terser-webpack-plugin":"1.1.0","webpack":"4.29.0","webpack-bundle-analyzer":"3.0.3","webpack-cli":"3.1.2","webpack-dev-middleware":"3.4.0","webpack-hot-middleware":"2.24.3","webpackbar":"2.6.3"},"peerDependencies":{"react":"^16.8.4"},"devDependencies":{"@babel/preset-typescript":"^7.1.0","@types/webpack-bundle-analyzer":"^2.13.0","babel-plugin-module-resolver":"^3.1.1","gulp":"gulpjs/gulp.git#4.0","gulp-babel":"^8.0.0","gulp-clean":"^0.4.0","gulp-sourcemaps":"^2.6.4","gulp-uglify":"^3.0.1","gulp-watch":"^5.0.1","react":"^16.8.4"},"gitHead":"405c22958b6e533f5871ddffe666b5d34dd1c6b7","_nodeVersion":"10.11.0","_npmVersion":"lerna/3.7.2/node@v10.11.0+x64 (darwin)","_npmUser":{"name":"airyrooms-engineering","email":"engineering@airyrooms.com"},"_id":"@airy/maleo@0.0.8","dist":{"integrity":"sha512-vTORXVeMzxNBQIrqU0rdo6oZ3rc2LJiTB1/54aiYk1KZqKQaDAgj4N9qKMuVIgI4UwgVZv1+XhKy+9mxe/ZGDw==","shasum":"25f21ba4e76cfe45b360e64d18b1f7adb01855ac","tarball":"https://registry.npmjs.org/@airy/maleo/-/maleo-0.0.8.tgz","fileCount":83,"unpackedSize":223582,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJckbjbCRA9TVsSAnZWagAAY+0P/3BAHuAFl/bB3kb4KraD\nswqJuUDyZW2HF7nPNolu+F5Wcfua4i8ksJHgbbuw3EofTZB2lWXLWKU/hIIP\n+QnRrYLmNOEUHjCtc4jVy4ZFWNLX7odMyLJ8gQme0j7IWEn/7cfCbfzcB/Tv\nWR1GQgGDcNkVGgilYf/ImYE4aHPeM8w9YVBQ2vTsdG5+bRF2kvMbhSFqEqsE\nnk8DTkjjAnTW7tcCrwbJ9aajCW41Hc6GlnaHEmBNBLSr8OeppniHkBfyBVXi\nvRvYceyxK3sdXA4vBxiySWk8Cf1EZiDE5sNjAfA0PukKg1chS6H1puK4Xo9o\ntX23vvDSL1154g99gume6+meOCG4mnfIY0S9sGaeFcSFVj6k0rA02I3Eh86q\nyRXuddnUIhrS0n/c33YtOFLxo5MRpaLzoXeRPYJ5VHdZhofaOTFHQxX3CIRT\nssob8szx+WrSpwZsUM/xeqiBjgcTYSnUiE5pWnmDOQlNutgHaQ2LK0guQnKh\n9Vx4YEnzzO4fMkmatEJKbZtXogLkfGV4Kj+DFhem08CFsoXwHYfzCvQLIQuQ\nAYULf7TJXKJauQfalWW3GCKlXEGudCLaeFE/nSu4cosAp3d8XYXS0ogawv6h\nDUF2w7pUNzWb47WFweZG+rc1A/FeYszUsGx7vxs1Y9mABxDBRI8r3KG/yZ2x\nYs+n\r\n=prOS\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIA5U6UYIa/Yu9l9jizbJ/Z60p+fL04FKhWDPkRXu2DDZAiB8BeEY7V2MnTWETNPVo2EiFcTBfnRSSgKHupvF4OeSRQ=="}]},"maintainers":[{"name":"airyrooms-engineering","email":"engineering@airyrooms.com"}],"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/maleo_0.0.8_1553053915159_0.49844973140784066"},"_hasShrinkwrap":false},"0.0.9-canary.0":{"name":"@airy/maleo","version":"0.0.9-canary.0","description":"Un-opinionated Universal Rendering Javascript Framework","repository":{"type":"git","url":"git+https://github.com/airyrooms/maleo.js","bugs":"https://github.com/airyrooms/maleo.js/issues"},"author":"Airy","license":"MIT","private":false,"sideEffects":false,"scripts":{"build":"rm -rf dist && gulp","watch":"gulp watch","prepublish":"yarn build"},"bin":"lib/bin/maleo.js","dependencies":{"@babel/core":"7.1.2","@babel/plugin-proposal-class-properties":"7.1.0","@babel/plugin-proposal-decorators":"7.1.2","@babel/plugin-proposal-object-rest-spread":"7.0.0","@babel/plugin-syntax-dynamic-import":"7.0.0","@babel/plugin-transform-runtime":"7.1.0","@babel/preset-env":"7.1.0","@babel/preset-react":"7.0.0","@babel/runtime":"7.1.2","@babel/runtime-corejs2":"7.2.0","babel-loader":"8.0.4","case-sensitive-paths-webpack-plugin":"2.1.2","es6-promise":"4.2.5","eventsource":"^1.0.7","express":"4.16.4","figlet":"1.2.1","friendly-errors-webpack-plugin":"1.7.0","hard-source-webpack-plugin":"0.13.1","helmet":"3.14.0","isomorphic-fetch":"2.2.1","loader-utils":"1.1.0","node-notifier":"5.2.1","nodemon":"1.18.4","react-dom":"16.8.4","react-helmet":"5.2.0","react-loadable":"5.5.0","react-router-config":"5.0.0","react-router-dom":"5.0.0","rimraf":"2.6.3","source-map-support":"0.5.9","terser-webpack-plugin":"1.1.0","webpack":"4.29.0","webpack-bundle-analyzer":"3.0.3","webpack-cli":"3.1.2","webpack-dev-middleware":"3.4.0","webpack-hot-middleware":"2.24.3","webpackbar":"2.6.3"},"peerDependencies":{"react":"^16.8.4"},"devDependencies":{"@babel/preset-typescript":"^7.1.0","@types/webpack-bundle-analyzer":"^2.13.0","babel-plugin-module-resolver":"^3.1.1","gulp":"gulpjs/gulp.git#4.0","gulp-babel":"^8.0.0","gulp-clean":"^0.4.0","gulp-sourcemaps":"^2.6.4","gulp-uglify":"^3.0.1","gulp-watch":"^5.0.1","react":"^16.8.4"},"gitHead":"c2e51b0f8a1389a506daac22fe63c96b3df920ef","_nodeVersion":"10.11.0","_npmVersion":"lerna/3.7.2/node@v10.11.0+x64 (linux)","_npmUser":{"name":"airyrooms-engineering","email":"engineering@airyrooms.com"},"_id":"@airy/maleo@0.0.9-canary.0","dist":{"integrity":"sha512-Q8cSkT1Xs5J1KdoGiMyCkVnUKQXx+DkpfOlA8Kka+6qwVxk4dGknNBAYgEdx9Dm2wtEQd65xz6z3GSklY37UYw==","shasum":"100462f5f4249254293f67d1eb6e8d319e7f2a05","tarball":"https://registry.npmjs.org/@airy/maleo/-/maleo-0.0.9-canary.0.tgz","fileCount":83,"unpackedSize":223785,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJckbq7CRA9TVsSAnZWagAAED8P+wcrtUlnCOwjH7SBUHZv\n8PIKAS0FOQAIRfQ6rfx3pxuwVxNrREOVWNNEHFTZkOA3ujdDnn+MFEwlgU4w\nePqaUBVkzs8dgH53hi1p/DYRJ5nNxDh8VwG1iHO1mRiRHDTDyPuqotpFCRBl\nTkPcCaxylitAV60ORbcZdpPubH7fek8kdi/ZHUMa/yr1UfpbV55O3tm+uLRN\n28vGNb6xD+XFj6Zp6qIviMS1GHmVA34DfULLg/BrCfiIzvejesGu+KyXPnh4\nwAFGsoHQ5rkWMyf0MZAmTz4q661+Axhsxaa5rrLTJmiNFqYTMiX+smVAoFKT\nb0WR9VrXUBSosxXPw6u8YCOZ9cOtfdXmAh+G7sfGVns+dzWxevgIySg8k9lj\nBi3neRQHftz82MVP0q/5jsyGEm/I4rejfiZQaT/A6wsZZ3QEK4td/qbClCEy\n7P6eL74LQFb9WKPYt4hssR5p42M6FwpmTbo2u6CeR151O6V/NJc0reppeIMh\nBO+HSS6LZ7HC7jqBV0eSLP3viTMSDJ38SoQjPAbLOKxnj3kyxZlm9VAkVpK3\nToOj7iy6pC5iU/ycoLEuu9OpMUusbyCwsV/WTVuqge9dxluYuU9YHjbjqtYq\nYmOgWSqmd2nYPh9FfVJFlqgy1Uc/tomMpkGYoGn0Gfg7iDzJtkTejy29VFr8\nXC+n\r\n=bc8n\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDSpYStblJLnTeCZBtlpCh5YvVVfP4JRR0omJSbGdYo6wIhAM1qFqHtvYJY4WFQEvHjfmlBPS/P/AdvpX4aqb/WFm4m"}]},"maintainers":[{"name":"airyrooms-engineering","email":"engineering@airyrooms.com"}],"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/maleo_0.0.9-canary.0_1553054394466_0.3032320163878399"},"_hasShrinkwrap":false},"0.0.9-canary.1":{"name":"@airy/maleo","version":"0.0.9-canary.1","description":"Un-opinionated Universal Rendering Javascript Framework","repository":{"type":"git","url":"git+https://github.com/airyrooms/maleo.js","bugs":"https://github.com/airyrooms/maleo.js/issues"},"author":"Airy","license":"MIT","private":false,"sideEffects":false,"scripts":{"build":"rm -rf dist && gulp","watch":"gulp watch","prepublish":"yarn build"},"bin":"lib/bin/maleo.js","dependencies":{"@babel/core":"7.1.2","@babel/plugin-proposal-class-properties":"7.1.0","@babel/plugin-proposal-decorators":"7.1.2","@babel/plugin-proposal-object-rest-spread":"7.0.0","@babel/plugin-syntax-dynamic-import":"7.0.0","@babel/plugin-transform-runtime":"7.1.0","@babel/preset-env":"7.1.0","@babel/preset-react":"7.0.0","@babel/runtime":"7.1.2","@babel/runtime-corejs2":"7.2.0","babel-loader":"8.0.4","case-sensitive-paths-webpack-plugin":"2.1.2","es6-promise":"4.2.5","eventsource":"^1.0.7","express":"4.16.4","figlet":"1.2.1","friendly-errors-webpack-plugin":"1.7.0","hard-source-webpack-plugin":"0.13.1","helmet":"3.14.0","isomorphic-fetch":"2.2.1","loader-utils":"1.1.0","node-notifier":"5.2.1","nodemon":"1.18.4","react-dom":"16.8.4","react-helmet":"5.2.0","react-loadable":"5.5.0","react-router-config":"5.0.0","react-router-dom":"5.0.0","rimraf":"2.6.3","source-map-support":"0.5.9","terser-webpack-plugin":"1.1.0","webpack":"4.29.0","webpack-bundle-analyzer":"3.0.3","webpack-cli":"3.1.2","webpack-dev-middleware":"3.4.0","webpack-hot-middleware":"2.24.3","webpackbar":"2.6.3"},"peerDependencies":{"react":"^16.8.4"},"devDependencies":{"@babel/preset-typescript":"^7.1.0","@types/webpack-bundle-analyzer":"^2.13.0","babel-plugin-module-resolver":"^3.1.1","gulp":"gulpjs/gulp.git#4.0","gulp-babel":"^8.0.0","gulp-clean":"^0.4.0","gulp-sourcemaps":"^2.6.4","gulp-uglify":"^3.0.1","gulp-watch":"^5.0.1","react":"^16.8.4"},"gitHead":"8afdd80d26e7dc84564f6d0d21eb53b738b5e1c8","_nodeVersion":"10.11.0","_npmVersion":"lerna/3.7.2/node@v10.11.0+x64 (linux)","_npmUser":{"name":"airyrooms-engineering","email":"engineering@airyrooms.com"},"_id":"@airy/maleo@0.0.9-canary.1","dist":{"integrity":"sha512-4KB3IrB5sRwH6KfqlqXJ6VEtVSWQb55z/IjQXNyzQxjtiCRSg2RnXG2CkaCIJKXLzWAf7mHnvpnvCXfzJz981g==","shasum":"f79396e435e3e3104d113cf4561354c59e26620b","tarball":"https://registry.npmjs.org/@airy/maleo/-/maleo-0.0.9-canary.1.tgz","fileCount":83,"unpackedSize":223979,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJckekcCRA9TVsSAnZWagAAW3kP/RYP8lcF/HmXDKFNRWmF\nZyRRTzMZvpLK1MZPLrIofgUJIB7zHthqDMpZUPOLHbPdyRPi7goYqezSMTBi\nby56vSSJ39dfluRDBvBGrV+B5ZcLJT+iu6u+ngbdJt+q/uddZk8rU+w2hEX4\nZh4ybyLZ0fpxpIXCZitSstrSvcc9nrfULcJ+fonuzLmdGKUmSWy9UDyWXB9h\n+r2p5P36Qw5zpSaPhfO0wYen7nunAxGiSmjOjY7c9wZjqqSmXcED3UpLotDq\n0+fmEcn0xH3KX+2ColpP/NgPCjwczKnDQPRgUBsgWC7iXdqRaxpBGKa8lKLL\nMKPzn7tyBjZdcqBZ+v5leokYZ2qhbPZp7/B5jSBveLFJxdXKQc4T9kLW0Wvi\nAVVq5isdlgBBOdWyEvo38wR4dZRNrRyYszQMAdwQRVp80x5TmJqf1jLfWbvx\nFQIjZOXdNXry8KdEEohiSnrabWK6wEKdu/yfKIfbDlSF4bqiqLRFSWgIUUsw\nIZJbM2pabr++BJyG7heV96QJQuftn3MJ9tacMt7iyd85DpFxf7SiWRaeGlSJ\n/2BFSYMnjF3cSLIX6g6lkSEOEdw8zWQYd+qM5Sep9iNUADQKYvxuM2wPvX0D\n7FcW1+Qn9H/zaPLcF7KMYK+OZP5PdfVLtBE4nrUAfeF1055VIfl2FDUUDg/A\ngQLU\r\n=iG0+\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIGXI9PXb8GdPwFUX2CqzUY0gR4f0Rq9oF7IlAuPB/5MpAiEA4/8MaF7NNnL3EftBlqOIUTiyxf6Hyc5XjWKwSokjnEE="}]},"maintainers":[{"name":"airyrooms-engineering","email":"engineering@airyrooms.com"}],"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/maleo_0.0.9-canary.1_1553066267589_0.3913130406774068"},"_hasShrinkwrap":false},"0.0.9":{"name":"@airy/maleo","version":"0.0.9","description":"Un-opinionated Universal Rendering Javascript Framework","repository":{"type":"git","url":"git+https://github.com/airyrooms/maleo.js","bugs":"https://github.com/airyrooms/maleo.js/issues"},"author":"Airy","license":"MIT","private":false,"sideEffects":false,"scripts":{"build":"rm -rf dist && gulp","watch":"gulp watch","prepublish":"yarn build"},"bin":"lib/bin/maleo.js","dependencies":{"@babel/core":"7.1.2","@babel/plugin-proposal-class-properties":"7.1.0","@babel/plugin-proposal-decorators":"7.1.2","@babel/plugin-proposal-object-rest-spread":"7.0.0","@babel/plugin-syntax-dynamic-import":"7.0.0","@babel/plugin-transform-runtime":"7.1.0","@babel/preset-env":"7.1.0","@babel/preset-react":"7.0.0","@babel/runtime":"7.1.2","@babel/runtime-corejs2":"7.2.0","babel-loader":"8.0.4","case-sensitive-paths-webpack-plugin":"2.1.2","es6-promise":"4.2.5","eventsource":"^1.0.7","express":"4.16.4","figlet":"1.2.1","friendly-errors-webpack-plugin":"1.7.0","hard-source-webpack-plugin":"0.13.1","helmet":"3.14.0","isomorphic-fetch":"2.2.1","loader-utils":"1.1.0","node-notifier":"5.2.1","nodemon":"1.18.4","react-dom":"16.8.4","react-helmet":"5.2.0","react-loadable":"5.5.0","react-router-config":"5.0.0","react-router-dom":"5.0.0","rimraf":"2.6.3","source-map-support":"0.5.9","terser-webpack-plugin":"1.1.0","webpack":"4.29.0","webpack-bundle-analyzer":"3.0.3","webpack-cli":"3.1.2","webpack-dev-middleware":"3.4.0","webpack-hot-middleware":"2.24.3","webpackbar":"2.6.3"},"peerDependencies":{"react":"^16.8.4"},"devDependencies":{"@babel/preset-typescript":"^7.1.0","@types/webpack-bundle-analyzer":"^2.13.0","babel-plugin-module-resolver":"^3.1.1","gulp":"gulpjs/gulp.git#4.0","gulp-babel":"^8.0.0","gulp-clean":"^0.4.0","gulp-sourcemaps":"^2.6.4","gulp-uglify":"^3.0.1","gulp-watch":"^5.0.1","react":"^16.8.4"},"gitHead":"aeef6690154b4f9c2bc9d417a36130349204b0fc","_nodeVersion":"10.11.0","_npmVersion":"lerna/3.7.2/node@v10.11.0+x64 (darwin)","_npmUser":{"name":"airyrooms-engineering","email":"engineering@airyrooms.com"},"_id":"@airy/maleo@0.0.9","dist":{"integrity":"sha512-7WCtgDisQNncbE5t7jArRkd6cSnpeCRPQLE2kY07DB9Uv5DLB4pnzfKU+/ZAbcV6uN/+yciIHiNO9T83eEuNIg==","shasum":"03ca49540181246e3356f133ec1c1b87505a8570","tarball":"https://registry.npmjs.org/@airy/maleo/-/maleo-0.0.9.tgz","fileCount":83,"unpackedSize":224146,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJcke14CRA9TVsSAnZWagAArl0P/2BwTE/Ll9LLDvneMkje\nRQuYFmxFKMfwu0S1Bd50pxDB1hTkQudfV5tH+EaP3hcd+dmCouYCchjJtXeB\nR0wSDip8GaVfd3wHePBSUtLOqTKIxzrVb85xZi9cnHtPm+r990JfIXMzu8Yf\nrol8pAZKMaJ0LDuaaq6pUxYEa7XMZwBAoT8BquiBQpevouTWRX9rg7Gm0P/j\nJVbpvbK0uCiqzSVGmdoTxGPJiwloEoia42sI9bm5Ni9lDzc1FTkfNeQMrqoS\nPQoptQ0XDsMTfR3r4ehYuwqc1qrQlRtwKbktfaK9Y+PcLWc3rUlXsVWS3YYf\nIEnQ+yyt5GRHdk4TBxrB6W5BTB3ybxSxvQcS/c7h/pjnBjEtVViL+EfOuNS3\nDnvTcacuXgg2n6+6Uf+48EHew1wnfNws6VNDoXmRpNWsx6SvcuPvYqfseDPs\nFg0A00T6TEQwgNauh0O/Hmftn+uTRXP52VEZP8jZe7mhhsULdfi0vOEzpgHx\n4qVyTac7redZnHY+5DTuBldrHanggiumEGDCWqQCg1Q9tV2Xyafn9B8aZGn1\n0y/ZAAhie/Oq1mFbjl8BCvjvEevQTQVROxN93R/NQZZ7IcYLinLi+MvidZvB\npRXFBKsg98CN+pjtzASVbRl/exwfz7CxCC3YGaxT//br1y2GajfDa0rFDXTN\nwUNO\r\n=UuNF\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIFvEcpBXzcKCNwzJ8d9Obj37nabxSVrJkE1ItZfSOIRnAiB4ohFCoVFqbkQojb5PuyEmGqvH6AvjDlh1dLIlXQDG7g=="}]},"maintainers":[{"name":"airyrooms-engineering","email":"engineering@airyrooms.com"}],"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/maleo_0.0.9_1553067383535_0.3705263375038128"},"_hasShrinkwrap":false},"0.0.10-canary.0":{"name":"@airy/maleo","version":"0.0.10-canary.0","description":"Un-opinionated Universal Rendering Javascript Framework","repository":{"type":"git","url":"git+https://github.com/airyrooms/maleo.js","bugs":"https://github.com/airyrooms/maleo.js/issues"},"author":"Airy","license":"MIT","private":false,"sideEffects":false,"scripts":{"build":"rm -rf dist && gulp","watch":"gulp watch","prepublish":"yarn build"},"bin":"lib/bin/maleo.js","dependencies":{"@babel/core":"7.1.2","@babel/plugin-proposal-class-properties":"7.1.0","@babel/plugin-proposal-decorators":"7.1.2","@babel/plugin-proposal-object-rest-spread":"7.0.0","@babel/plugin-syntax-dynamic-import":"7.0.0","@babel/plugin-transform-runtime":"7.1.0","@babel/preset-env":"7.1.0","@babel/preset-react":"7.0.0","@babel/runtime":"7.1.2","@babel/runtime-corejs2":"7.2.0","babel-loader":"8.0.4","case-sensitive-paths-webpack-plugin":"2.1.2","es6-promise":"4.2.5","eventsource":"^1.0.7","express":"4.16.4","figlet":"1.2.1","friendly-errors-webpack-plugin":"1.7.0","hard-source-webpack-plugin":"0.13.1","helmet":"3.14.0","isomorphic-fetch":"2.2.1","loader-utils":"1.1.0","node-notifier":"5.2.1","nodemon":"1.18.4","react-dom":"16.8.4","react-helmet":"5.2.0","react-loadable":"5.5.0","react-router-config":"5.0.0","react-router-dom":"5.0.0","rimraf":"2.6.3","source-map-support":"0.5.9","terser-webpack-plugin":"1.1.0","webpack":"4.29.0","webpack-bundle-analyzer":"3.0.3","webpack-cli":"3.1.2","webpack-dev-middleware":"3.4.0","webpack-hot-middleware":"2.24.3","webpackbar":"2.6.3"},"peerDependencies":{"react":"^16.8.4"},"devDependencies":{"@babel/preset-typescript":"^7.1.0","@types/webpack-bundle-analyzer":"^2.13.0","babel-plugin-module-resolver":"^3.1.1","gulp":"gulpjs/gulp.git#4.0","gulp-babel":"^8.0.0","gulp-clean":"^0.4.0","gulp-sourcemaps":"^2.6.4","gulp-uglify":"^3.0.1","gulp-watch":"^5.0.1","react":"^16.8.4"},"gitHead":"9fa9ef707a9897046db8aeb21b4a7e5b37e1dbb9","_nodeVersion":"10.11.0","_npmVersion":"lerna/3.7.2/node@v10.11.0+x64 (linux)","_npmUser":{"name":"airyrooms-engineering","email":"engineering@airyrooms.com"},"_id":"@airy/maleo@0.0.10-canary.0","dist":{"integrity":"sha512-k2h2M1MwMMjwbLNbZ1p0Bgf4BSgxl5b1DjhCBts9e5GuJvQ34MVAsH+llAAzVAdigoE2UHNgPyf02Za7w+mQsA==","shasum":"db8914ca1894fc812dad61a4894f2ef71bcd90d6","tarball":"https://registry.npmjs.org/@airy/maleo/-/maleo-0.0.10-canary.0.tgz","fileCount":83,"unpackedSize":224352,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJcke+mCRA9TVsSAnZWagAAdVgP/1nTkEGKWgAFABIWkWR/\nq1wb1IaoBVbAMlwO3JeWP0pkwjBag5iEp+oUpWkLXLQsH+A/dPvslPeLdJaL\nL2znQDGM8E10Ce8eb1Gsvf5b1A0feT7D6+Hq09fs+ZwIgXJffW+lQP/aXxzK\nHm0WcV0nHQeFu2WADddMe/Njr0Oq6Iwj2Ks1KdgfA650Ma/6Sh0jUcYdUf39\nLVFQqqU8nU/Pu2F3KbkHq5VwUZq8TpLVvKJKjNbsrAm6rb1YuLVnmyPbgANq\nJZHXQskYiw56TNoRiCZ1PFWexRrkbymj+51v61lqjHnQToJs+0Sp1LU7BSfz\nX/2ZeGuerR0uzZEpdVk3p448uV3WLrw6F9yB0uoBvwds7NDW6dR02+9zEol0\n/r8XFpCJi9BtlqsNNzEBnxxztIesUidlrvBU8WfqSQMASxyY75qV3Znghzzq\nmnmTdunC0Tj0ZEO3b99mKHaHpuvverRG1Q51Zjz3Hi0qBBmQIQ3+xBsfW4wt\nSGuPvTP+b5M9hgk82PAls/WFCoRhLaJYVhIUNqVJTpcwhjXvbgKxpunN8XJW\nxf62N0IxVApQQTqXksVw4gkadP+SztDNNuxX4vNez74HwNtga4HzszXdEn3D\n10k3FiTTz2jrdBv7UtO76JNiLMPIfiNqXU6r8GAzeBhP/RLG6U96/G/R7Pw6\nMMUL\r\n=u3b6\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIDIKSNV6KO+Ds2zMD/kYc/75dsfEsnvIDQ0T9dZ5F7w8AiEA6olY8iPl2H67THjK0PyAtZzH+sLciwrXu7qzo5pITn4="}]},"maintainers":[{"name":"airyrooms-engineering","email":"engineering@airyrooms.com"}],"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/maleo_0.0.10-canary.0_1553067942005_0.5759116748591773"},"_hasShrinkwrap":false},"0.0.10":{"name":"@airy/maleo","version":"0.0.10","description":"Un-opinionated Universal Rendering Javascript Framework","repository":{"type":"git","url":"git+https://github.com/airyrooms/maleo.js","bugs":"https://github.com/airyrooms/maleo.js/issues"},"author":"Airy","license":"MIT","private":false,"sideEffects":false,"scripts":{"build":"rm -rf dist && gulp","watch":"gulp watch","prepublish":"yarn build"},"bin":"lib/bin/maleo.js","dependencies":{"@babel/core":"7.1.2","@babel/plugin-proposal-class-properties":"7.1.0","@babel/plugin-proposal-decorators":"7.1.2","@babel/plugin-proposal-object-rest-spread":"7.0.0","@babel/plugin-syntax-dynamic-import":"7.0.0","@babel/plugin-transform-runtime":"7.1.0","@babel/preset-env":"7.1.0","@babel/preset-react":"7.0.0","@babel/runtime":"7.1.2","@babel/runtime-corejs2":"7.2.0","babel-loader":"8.0.4","case-sensitive-paths-webpack-plugin":"2.1.2","es6-promise":"4.2.5","eventsource":"^1.0.7","express":"4.16.4","figlet":"1.2.1","friendly-errors-webpack-plugin":"1.7.0","hard-source-webpack-plugin":"0.13.1","helmet":"3.14.0","isomorphic-fetch":"2.2.1","loader-utils":"1.1.0","node-notifier":"5.2.1","nodemon":"1.18.4","react-dom":"16.8.4","react-helmet":"5.2.0","react-loadable":"5.5.0","react-router-config":"5.0.0","react-router-dom":"5.0.0","rimraf":"2.6.3","source-map-support":"0.5.9","terser-webpack-plugin":"1.1.0","webpack":"4.29.0","webpack-bundle-analyzer":"3.0.3","webpack-cli":"3.1.2","webpack-dev-middleware":"3.4.0","webpack-hot-middleware":"2.24.3","webpackbar":"2.6.3"},"peerDependencies":{"react":"^16.8.4"},"devDependencies":{"@babel/preset-typescript":"^7.1.0","@types/webpack-bundle-analyzer":"^2.13.0","babel-plugin-module-resolver":"^3.1.1","gulp":"gulpjs/gulp.git#4.0","gulp-babel":"^8.0.0","gulp-clean":"^0.4.0","gulp-sourcemaps":"^2.6.4","gulp-uglify":"^3.0.1","gulp-watch":"^5.0.1","react":"^16.8.4"},"gitHead":"0389dc13d331ef0498edeb9ca65c1ba785a8d02a","_nodeVersion":"10.11.0","_npmVersion":"lerna/3.7.2/node@v10.11.0+x64 (darwin)","_npmUser":{"name":"airyrooms-engineering","email":"engineering@airyrooms.com"},"_id":"@airy/maleo@0.0.10","dist":{"integrity":"sha512-YpGtIsUQkin7jxd+iYq1eVPgQeCCkJ1WytTUIug/NjqxFZVmGrctrIVCG5mF/a8r4NkY4vrvvfJWFhSS5NFgAg==","shasum":"0293a01e5e61311bb1756230c4594fc05e17e35a","tarball":"https://registry.npmjs.org/@airy/maleo/-/maleo-0.0.10.tgz","fileCount":83,"unpackedSize":227325,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJclKATCRA9TVsSAnZWagAArEgP/j9hnlgehc4INcolKWxL\nElMt+iD2Z0mApPO9M8DsfhTN6iGKGz5MzVexZiZrsCmtQ/9SfXA8zi3dcLpH\n4/araKQWQEzlnbaoqh56FTGgBa2/ago0+pS9tD8A/GAZsSi0jf+tKAzNpiNg\nukvs0Evs+lWsRUx7dDp/C8BO8utzqvWE48cnO6YhBf115RRgmZ+oaQ92QTRC\nFfWrJNwha6+PGs2/5gkchmiSVpYlq6efPhzrkOOD5d7zPumW+67GBska6VhF\nrigDV9h9N+11RDeGNwUmTeCV437z1eiUe++jhxMv4X9Dt4erPDyckzUdF+B3\nSqyjt7UfZA2b+3FbN9IFZ6pfse2KJ6uAh11R3Th++NSp9U34y0rhEOGhtIge\nreaiPsd1qxU+ktUqn1wB1itznJQEtK7WvysSwx93nW2RvvwNCLtovuocFWm8\n4rmy+c1WcxU0csIDb/ye23eHSSa9NktKLBEBZxD53p+4Mt6cVKHGuAEzDrNN\nRwgVv25pQwxKyjcDW0/MvTvh9UR5qNzv8rLdn4PvEhoZTyKy2LFAjPWKQHNp\nLClYJdXki0YZw+r3xf52G/Baxj0YzHl/PhcamFOLqW3ENtqv2wbeqjto0yWD\nBJBG+aTztMiUNBfDCpKn6ux/vq24PW/pqCyzh7Gpzw9ar+eZkNfc3B8KmRZX\nJHeL\r\n=9e5x\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIE126k5pL2JPbiA9ECbm2itbaNHxdXMXpBQDxAXhX2hMAiEAsXiJv03PdYujOL/+aCBolqQEVU/EF0T63IdEYxO8zvA="}]},"maintainers":[{"name":"airyrooms-engineering","email":"engineering@airyrooms.com"}],"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/maleo_0.0.10_1553244178892_0.8144054882117819"},"_hasShrinkwrap":false},"0.0.11":{"name":"@airy/maleo","version":"0.0.11","description":"Un-opinionated Universal Rendering Javascript Framework","repository":{"type":"git","url":"git+https://github.com/airyrooms/maleo.js","bugs":"https://github.com/airyrooms/maleo.js/issues"},"author":"Airy","license":"MIT","private":false,"sideEffects":false,"scripts":{"build":"rm -rf dist && gulp","watch":"gulp watch","prepublish":"yarn build"},"bin":"lib/bin/maleo.js","dependencies":{"@babel/core":"7.1.2","@babel/plugin-proposal-class-properties":"7.1.0","@babel/plugin-proposal-decorators":"7.1.2","@babel/plugin-proposal-object-rest-spread":"7.0.0","@babel/plugin-syntax-dynamic-import":"7.0.0","@babel/plugin-transform-runtime":"7.1.0","@babel/preset-env":"7.1.0","@babel/preset-react":"7.0.0","@babel/runtime":"7.1.2","@babel/runtime-corejs2":"7.2.0","babel-loader":"8.0.4","case-sensitive-paths-webpack-plugin":"2.1.2","es6-promise":"4.2.5","eventsource":"^1.0.7","express":"4.16.4","figlet":"1.2.1","friendly-errors-webpack-plugin":"1.7.0","hard-source-webpack-plugin":"0.13.1","helmet":"3.14.0","isomorphic-fetch":"2.2.1","loader-utils":"1.1.0","node-notifier":"5.2.1","nodemon":"1.18.4","react-dom":"16.8.4","react-helmet":"5.2.0","react-loadable":"5.5.0","react-router-config":"5.0.0","react-router-dom":"5.0.0","rimraf":"2.6.3","source-map-support":"0.5.9","terser-webpack-plugin":"1.1.0","webpack":"4.29.0","webpack-bundle-analyzer":"3.0.3","webpack-cli":"3.1.2","webpack-dev-middleware":"3.4.0","webpack-hot-middleware":"2.24.3","webpackbar":"2.6.3"},"peerDependencies":{"react":"^16.8.4"},"devDependencies":{"@babel/preset-typescript":"^7.1.0","@types/webpack-bundle-analyzer":"^2.13.0","babel-plugin-module-resolver":"^3.1.1","gulp":"gulpjs/gulp.git#4.0","gulp-babel":"^8.0.0","gulp-clean":"^0.4.0","gulp-sourcemaps":"^2.6.4","gulp-uglify":"^3.0.1","gulp-watch":"^5.0.1","react":"^16.8.4"},"gitHead":"49bd44e3982f52dd2d066ce5ecd4b3ebf86bc344","_nodeVersion":"10.11.0","_npmVersion":"lerna/3.7.2/node@v10.11.0+x64 (darwin)","_npmUser":{"name":"airyrooms-engineering","email":"engineering@airyrooms.com"},"_id":"@airy/maleo@0.0.11","dist":{"integrity":"sha512-DY+uasOUXDjj71K0iVxqts5sV5LZv14+PEQ0UT9+6rwYwU8qpazMf0MTw9vXmbkrkpiIcJ9FRkyy/Cr3PKJ5bA==","shasum":"f88009337f6c798e7fdbf079fc72de3338bcdbd2","tarball":"https://registry.npmjs.org/@airy/maleo/-/maleo-0.0.11.tgz","fileCount":83,"unpackedSize":227597,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJclO8zCRA9TVsSAnZWagAAzD4P/iH0c6qzfE+eLBBGrQBe\nqb0fzQ4WM/lx8cdDJ07B1rhyFVqPToCNeN1ixwHcxEKk0n0PFH12WtRU3v5A\n6W2hIr3JRI84/lpr+kTjxN8CFSYG3J5vuPQ+KKABAGYdbu17zQB5K22bCR/P\n8IEw4/2QRWpLKUgEvIKgcSDBB/8YmLmpMsIG/LNwokExC4PV2CKTkruLsPgt\nkOwM6dugahC6rdA1ai6KayMcKX66G+EiGWMLSBlQXGzoh3f/bXZQhDDdYVfJ\n8SuuRdS0Cl0zgXjJ/CBKyp+T2bQySmHuaDWIHuXnSSdcXAI0Dh3woIWi6bES\nrug2ZSwuwgSIN3LkHzuFEvfPMIoe+k/jNhWeWyIhq5vYpfQiGcV2ixs7dlbG\nK28hJ+XdkQTqfpfsxlINdcpU7BnDhFDIrGl9H1yacK/bhR+4FVLuThEp8f9Z\n2MPexcO5rOAhW2/6gQolHm39vs+RczasFMyhf0h09btZbkQgHmjeOOWvFdDv\nzrOa1JNzKf6hvkl6MiumTptgwg/hVu0rpER0NQN3T7aO74pREEUUzdB26vDg\ndn7P501O+B/Adani3NYk+2y/eUlIdsztvBJ2pOvR3in1FzdNkmxOcDk8VIHU\nn/TFbXQ/KG+AOrI4LetP5X3GpvalI4B9Q0kbLuwWBOwBR7SgGRPhz+IKW1+a\nkEM5\r\n=V5PC\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCCqt5az4PiEHtWwyDY1mdcyrBseXWbzNKIYpj6BrjVGQIgdWk6fgvtcdASntS6E2Pi/FUAJyX6by15EdDspoovITA="}]},"maintainers":[{"name":"airyrooms-engineering","email":"engineering@airyrooms.com"}],"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/maleo_0.0.11_1553264435454_0.3768999170200118"},"_hasShrinkwrap":false},"0.0.12-canary.0":{"name":"@airy/maleo","version":"0.0.12-canary.0","description":"Un-opinionated Universal Rendering Javascript Framework","repository":{"type":"git","url":"git+https://github.com/airyrooms/maleo.js.git","bugs":"https://github.com/airyrooms/maleo.js/issues"},"author":{"name":"Airy"},"license":"MIT","private":false,"sideEffects":false,"scripts":{"build":"rm -rf dist && gulp","watch":"gulp watch","prepublish":"yarn build","start":"node server.js"},"bin":{"maleo":"lib/bin/maleo.js"},"dependencies":{"@babel/core":"7.1.2","@babel/plugin-proposal-class-properties":"7.1.0","@babel/plugin-proposal-decorators":"7.1.2","@babel/plugin-proposal-object-rest-spread":"7.0.0","@babel/plugin-syntax-dynamic-import":"7.0.0","@babel/plugin-transform-runtime":"7.1.0","@babel/preset-env":"7.1.0","@babel/preset-react":"7.0.0","@babel/runtime":"7.1.2","@babel/runtime-corejs2":"7.2.0","babel-loader":"8.0.4","case-sensitive-paths-webpack-plugin":"2.1.2","es6-promise":"4.2.5","eventsource":"^1.0.7","express":"4.16.4","figlet":"1.2.1","friendly-errors-webpack-plugin":"1.7.0","hard-source-webpack-plugin":"0.13.1","helmet":"3.14.0","isomorphic-fetch":"2.2.1","loader-utils":"1.1.0","node-notifier":"5.2.1","nodemon":"1.18.4","react-dom":"16.8.4","react-helmet":"5.2.0","react-loadable":"5.5.0","react-router-config":"5.0.0","react-router-dom":"5.0.0","rimraf":"2.6.3","source-map-support":"0.5.9","terser-webpack-plugin":"1.1.0","webpack":"4.29.0","webpack-bundle-analyzer":"3.0.3","webpack-cli":"3.1.2","webpack-dev-middleware":"3.4.0","webpack-hot-middleware":"2.24.3","webpackbar":"2.6.3"},"peerDependencies":{"react":"^16.8.4"},"devDependencies":{"@babel/preset-typescript":"^7.1.0","@types/webpack-bundle-analyzer":"^2.13.0","babel-plugin-module-resolver":"^3.1.1","gulp":"github:gulpjs/gulp#4.0","gulp-babel":"^8.0.0","gulp-clean":"^0.4.0","gulp-sourcemaps":"^2.6.4","gulp-uglify":"^3.0.1","gulp-watch":"^5.0.1","react":"^16.8.4"},"gitHead":"8788f39ae842358ae2fd6d72b8083c84feec636d","readme":"[![Build Status](https://travis-ci.org/airyrooms/maleo.js.svg?branch=master)](https://travis-ci.org/airyrooms/maleo.js)\n[![Coverage Status](https://coveralls.io/repos/github/airyrooms/maleo.js/badge.svg?branch=master)](https://coveralls.io/github/airyrooms/maleo.js?branch=master)\n[![npm version](https://badge.fury.io/js/%40airy%2Fmaleo.svg)](https://badge.fury.io/js/%40airy%2Fmaleo)\n[![Issues](http://img.shields.io/github/issues/airyrooms/maleo.js.svg)]( https://github.com/airyrooms/maleo.js/issues)\n[![MIT license](https://img.shields.io/badge/License-MIT-blue.svg)](https://github.com/airyrooms/maleo.js/blob/canary/LICENSE)\n[![Discord Chat](https://img.shields.io/discord/550498214789513218.svg)](https://discord.gg/9eArCQn)\n\n\n# Welcome to Maleo.js\n\nMaleo.js is an un-opinionated framework to enable Universal Rendering in JavaScript using React with no hassle.\n\nWe are here to solve the time consuming setups Universal Rendering Required.\n\n---\n\nReadme below is the documentation for the `canary` (prerelease) branch. To view the documentation for the latest stable Maleo.js version change branch to `master`\n\n---\n\n# Table of Contents\n- [Features](#features)\n- [Setup](#setup)\n- [Component Lifecycle](#component-lifecycle)\n- [Routing](#routing)\n- [Dynamic Import Component](#dynamic-import-component)\n  - [Tips](#tips)\n  - [Preloading](#preloading)\n- [Customizable Component](#customizable-component)\n  - [Custom Document](#custom-document)\n  - [Custom Wrap](#custom-wrap)\n- [Custom Configuration](#custom-configuration)\n  - [Customize Server](#customize-server)\n  - [Customize Webpack](#customize-webpack)\n  - [Customize Babel Config](#customize-babel-config)\n- [CDN Support](#cdn-support)\n- [Plugins](#plugins)\n- [FAQ](#faq)\n- [Contributing](#contributing)\n\n---\n\n## Features\n- Universal Rendering\n- Plugin based framework\n- Customizable\n\n## Setup\n\nInstall Maleo.js \n\n>*for now change `@airy/maleo` to `@airy/maleo@canary` until we publish the stable version of this package*\n\n\n**NPM**\n```bash\n$ npm install --save @airy/maleo react\n```\n**Yarn**\n```bash\n$ yarn add @airy/maleo react\n```\n\nAdd this script to your `package.json`\n```json\n{\n  \"scripts\": {\n    \"dev\": \"maleo dev\",\n    \"build\": \"export NODE_ENV=production && maleo build\",\n    \"start\": \"export NODE_ENV=production && node .maleo/server.js\"\n  }\n}\n```\n\nCreate a page Root component\n```jsx\n// ./src/Root.jsx\nimport React from 'react';\n\n// Export default is required for registering page\nexport default class RootComponent extends React.Component {\n  render() {\n    return (\n      <h1>Hello World!</h1>\n    )\n  }\n}\n```\n\nAnd lastly, create a routing file on your project root directory called `routes.json` and register your page component\n```json\n[\n  {\n    \"path\": \"/\",\n    \"page\": \"./src/Root\"\n  }\n]\n```\n\nAfter that you can now run `$ npm run dev` and go to `http://localhost:3000`.\n\nYou should now see your app running on your browser.\n\nBy now you should see \n- Automatic transpilation and bundling (with webpack and babel)\n- ~~Hot code reloading~~ [#17](https://github.com/airyrooms/maleo.js/issues/17)\n- Server rendering\n\nTo see how simple this is, check out the sample app!\n\n## Component Lifecycle \n\nMaleo.js added a new component lifecycle hook called `getInitialProps`, this function is called during Server Side Rendering (SSR).\n\nThis is useful especially for SEO purposes. \n\nExample for stateful component:\n```jsx\nimport React from 'react';\n\nexport default class extends React.Component {\n  static getInitialProps = async (ctx) => {\n    const { req } = ctx;\n\n    const userAgent = req ? req.headers['user-agent'] : navigator.userAgent;\n\n    // the return value will be passed as props for this component\n    return { userAgent };\n  }\n\n  render() {\n    return (\n      <div>\n        Hello World {this.props.userAgent}\n      </div>\n    );\n  }\n}\n```\n\nExample for stateless component:\n```jsx\nconst Component = (props) => (\n  <div>\n    Hello World {props.userAgent}\n  </div>\n);\n\nComponent.getInitialprops = async (ctx) => {\n  const { req } = ctx;\n\n  const userAgent = req ? req.headers['user-agent'] : navigator.userAgent;\n\n  return { userAgent };\n};\n\nexport default Component;\n```\n\n`getInitialProps` receives a context object with the following properties:\n- `req` - HTTP request object (server only)\n- `res` - HTTP response object (server only)\n- `...wrapProps` - Spreaded properties from custom Wrap\n- `...appProps` - Spreaded properties from custom App\n\n## Routing\n\nRouting is declared in a centralized route config.\nRegister all the route config in `routes.json` file.\n\nIf you put the `routes.json` files on root directory, Maleo will automatically register your route. Otherwise put path to your routes on [Maleo config](#custom-configuration).\n\nRoutes file has to export default the route configuration.\nThe route object **expected to have distinct key** to indicate the route.\n\n<table>\n  <tr>\n    <td>Key</td>\n    <td>Type</td>\n    <td>Description</td>\n  </tr>\n  <tr>\n    <td><code>path</code></td>\n    <td><code>String!</code></td>\n    <td>Routes path</td>\n  </tr>\n  <tr>\n    <td><code>page</code></td>\n    <td><code>String!</code></td>\n    <td>Path to React Component for this route path</td>\n  </tr>\n  <tr>\n    <td><code>exact</code></td>\n    <td><code>Boolean?</code> [<code>false</code>]</td>\n    <td>To make url has to match exactly the path in order to render the component. Give <code>false</code> value if the route is a wrapper route component</td>\n  </tr>\n  <tr>\n    <td><code>routes</code></td>\n    <td><code>RouteObject?</code></td>\n    <td>Nested route</td>\n  </tr>\n</table>\n\n\nFor example:\n```json\n[\n  {\n    \"page\": \"./src/MainApp\",\n    \"routes\": [\n      {\n        \"path\": \"/\",\n        \"page\": \"./src/Search\",\n        \"exact\": true\n      },\n      {\n        \"path\": \"/search\",\n        \"page\": \"./src/Search\",\n        \"routes\": [\n          {\n            \"path\": \"/search/hello\",\n            \"page\": \"./src/Detail\",\n            \"exact\": true\n          }\n        ]\n      },\n      {\n        \"path\": \"/detail\",\n        \"page\": \"./src/Detail\",\n        \"exact\": true\n      }\n    ]\n  }\n]\n```\n\n## Dynamic Import Component\nMaleo.js supports TC39 [dynamic import proposal](https://github.com/tc39/proposal-dynamic-import) for JavaScript.\n\nYou can think dynamic import as another way to split your code into manageable chunks. You can use our `Dynamic` function which utilizes [react loadable](https://github.com/jamiebuilds/react-loadable)\n\nFor Example\n```js\n// DynamicComponent.js\nimport Dynamic from '@airy/maleo/dynamic';\n\nexport default Dynamic({\n  loader: () => import( /* webpackChunkName:\"DynamicComponent\" */ './component/DynamicLoad'),\n})\n```\n\n### **Preloading**\n\nFor optimization purposes, you can also preload a component even before the component got rendered.\n\nFor example, if you want to load component when a button get pressed, you can start preloading the component when the user hovers the mouse over the button.\n\nThe component created by `Dynamic` exposes a [static `preload` method](https://github.com/jamiebuilds/react-loadable#loadablecomponentpreload).\n\n```jsx\nimport React from 'react';\nimport Dynamic from '@airy/maleo/dynamic';\n\nconst DynamicBar = Dynamic({\n  loader: () => import('./Bar'),\n  loading: LoadingCompoinent\n});\n\nclass MyComponent extends React.Component {\n  state = { showBar: false };\n\n  onClick = () => {\n    this.setState({ showBar: true });\n  };\n\n  onMouseOver = () => DynamicBar.preload();\n\n  render() {\n    return (\n      <div>\n        <button\n          onClick={this.onClick}\n          onMouseOver={this.onMouseOver}>\n          Show Bar\n        </button>\n        { this.state.showBar && <DynamicBar /> }\n      </div>\n    )\n  }\n}\n```\n\n## Customizable Component\n\n### Custom Document\n\nHighly inspired by what [Next.js](https://github.com/zeit/next.js) has done on their awesome template customization.\n\nMaleo.js also enable customization on `Document` as document's markup. So you don't need to include tags like `<html>`, `<head>`, `<body>`, etc. \n\nTo override the default behavior, you'll need to create a component that extends the `Document` React class provided by Maleo.\n\n```jsx\n// document.jsx\nimport React from 'react';\nimport { Document, Header, Main, Scripts } from '@airy/maleo/document';\n\nexport default class extends Document {\n  render() {\n    return (\n      <html>\n        <Header>\n          <title>Maleo JS</title>\n          <meta charset=\"utf-8\" />\n          <meta name=\"description\" content=\"Maleo.js is awesome!\" />\n\n          <style>\n            {` body { background-color: #fff } `}\n          </style>\n        </Header>\n\n        <body>\n          <Main />\n\n          <Scripts />\n\n          <ReduxScript />\n        </body>\n      </html>\n    );\n  }\n}\n```\n\n### Custom Wrap\n\nMaleo.js uses the `Wrap` component to initialize pages. `Wrap` contains React Router's Component. You can add HoC here to wrap the application and control the page initialization. Which allows you to do amazing things like:\n- Persisting layour between page changes\n- Keeping state when navigating pages\n- Custom error handling using `componentDidCatch`\n- Inject additional data into pages (like Redux provider, etc)\n\nTo override the default behavior, you'll need to create a component that extends the `Wrap` React class provided by Maleo.\n\n```jsx\n// wrap.jsx\nimport React from 'react';\nimport { Wrap } from '@airy/maleo/wrap';\n\n// Redux plugin for Maleo.js\n// Hoc that creates store and add Redux Provider\nimport { withRedux } from '@airy/maleo-redux-plugin';\n\n// Custom Wrapper that will be rendered for the whole Application\nimport CustomWrapper from './component/CustomWrapper';\n\nimport { createStore } from './store';\n\n@withRedux(createStore)\nexport default class extends Wrap {\n  static getInitialProps = (ctx) => {\n    const { store } = ctx\n    // you receive store from context\n    // you can access or do something with the store here\n    console.log('Initialized Redux Store', store);\n    return {}\n  }\n\n  render() {\n    return (\n      <CustomWrapper>\n        {super.render()}\n      </CustomWrapper>\n    )\n  }\n}\n\n```\nIf you put `document.jsx` and `wrap.jsx` on root directory (the same level with `package.json`), then Maleo will automatically register your custom Document and Wrap. Otherwise, you can add the path to your custom Document and Wrap on [Maleo config](#custom-configuration)\n\n---\n***We are also working on adding default and customizable `Error` component page***\n\n---\n\n## Custom Configuration\n\nFor more advanced configuration of Maleo.js, like `webpack` config, registering `plugins`, path to your routes, custom Document and Wrap, and adding `path alias`, you can create a `maleo.config.js` in the root of your project directory. (same directory with `package.json`)\n\n```js\n// maleo.config.js\n\nmodule.exports = {\n  /* config options here */\n}\n```\n\nHere are the API's for the configuration:\n\n<table>\n  <tr>\n    <td>Key</td>\n    <td>Type</td>\n    <td>Description</td>\n  </tr>\n  <tr>\n    <td><code>buildDir</code></td>\n    <td><code>String?</code> [<code>.maleo</code>]</td>\n    <td>Directory to put Maleo.js' build assets</td>\n  </tr>\n  <tr>\n    <td><code>cache</code></td>\n    <td><code>Boolean?</code> [<code>true</code>]</td>\n    <td>Enable webpack build caching</td>\n  </tr>\n  <tr>\n    <td><code>sourceMaps</code></td>\n    <td><code>Boolean?</code> [<code>true</code>]</td>\n    <td>Enable webpack to generate source maps</td>\n  </tr>\n  <tr>\n    <td><code>alias</code></td>\n    <td><code>Object?</code></td>\n    <td>A key value pair for aliasing path directory\n      <br>\n      <code>\n      { 'component': './src/component' }\n      </code>\n    </td>\n  </tr>\n  <tr>\n    <td><code>publicPath</code></td>\n    <td><code>String?</code> [<code>/_assets/</code>]</td>\n    <td>To customize webpack's <code>publicPath</code> \n    <br>Comes in handy if using CDN to put built assets</td>\n  </tr>\n  <tr>\n    <td><code>analyzeBundle</code></td>\n    <td><code>Boolean?</code> [<code>false</code>]</td>\n    <td>To enable webpack's bundle analyzer, for analyzing bundle sizes during bundle debugging should Maleo.js' build process got slow</td>\n  </tr>\n  <tr>\n    <td><code>webpack</code></td>\n    <td><code>Function?</code></td>\n    <td>To customize webpack configuration, more details <a href=\"#customize-webpack\">here</a></td>\n  </tr>\n  <tr>\n    <td><code>routes</code></td>\n    <td><code>string?</code> [<code>rootDir/routes.jsx</code>]</td>\n    <td>Path to your routes file</td>\n  </tr>\n  <tr>\n    <td><code>customDocument</code></td>\n    <td><code>string?</code> [<code>rootDir/document.jsx</code>]</td>\n    <td>Path to your custom document file</td>\n  </tr>\n  <tr>\n    <td><code>customWrap</code></td>\n    <td><code>string?</code> [<code>rootDir/wrap.jsx</code>]</td>\n    <td>Path to your custom wrap file</td>\n  </tr>\n  <tr>\n    <td><code>customApp</code></td>\n    <td><code>string?</code> [<code>rootDir/app.jsx</code>]</td>\n    <td>Path to your custom app file</td>\n  </tr>\n</table>\n\n#### Customize Server\n\nCreate a `server.js` file on root directory where your `package.json` lives.\nHere you can customize Maleo's server.\n```js\nimport { Server } from '@airy/maleo/server';\nimport path from 'path';\n\nimport routeConfig from './routes';\n\nconst PORT = process.env.PORT || 8080;\n\nconst maleoServer = Server.init({\n  port: PORT,\n});\n\nmaleoServer.run(() => {\n  console.log('Server running on port :', PORT);\n});\n```\n\nHere are the API's for the configuration:\n\n<table>\n  <tr>\n    <td>Key</td>\n    <td>Type</td>\n    <td>Description</td>\n  </tr>\n  <tr>\n    <td><code>port</code></td>\n    <td><code>Number?</code> [<code>3000</code>]</td>\n    <td>Port to run Maleo server</td>\n  </tr>\n  <tr>\n    <td><code>assetDir</code></td>\n    <td><code>String?</code> [<code>'<root>/.maleo/client'</code>]</td>\n    <td>Directory for all client related assets</td>\n  </tr>\n</table>\n\n#### Customize Webpack\n\nYou are able to extend Maleo.js' default webpack configuration by defining a function on `maleo.config.js`\n\n```js\n// maleo.config.js\n\nmodule.exports = {\n  webpack(config, context, next) {\n    // Perform customizations to webpack config\n    // Important: This call is required in order to let Maleo pass the config to webpack\n    return next(); \n  },\n};\n```\n\nWebpack function will receive three arguments:\n\n<table>\n  <tr>\n    <td>Argument</td>\n    <td>Details</td>\n  <tr>\n  <tr>\n    <td><code>config</code></td>\n    <td>This contains webpack configuration object that you can manipulate</td>\n  <tr>\n  <tr>\n    <td><code>context</code></td>\n    <td>This contains some keys that are useful for the build context\n      <br>\n      <table>\n        <tr>\n          <td><code>isDev</code></td>\n          <td><code>Boolean</code></td>\n          <td>Check if current build is for development</td>\n        </tr>\n        <tr>\n          <td><code>publicPath</code></td>\n          <td><code>String</code></td>\n          <td>Public Path of user defined or default's value</td>\n        </tr>\n        <tr>\n          <td><code>analyzeBundle</code></td>\n          <td><code>Boolean</code></td>\n          <td>Check if analyze bundle is enabled</td>\n        </tr>\n        <tr>\n          <td><code>buildDirectory</code></td>\n          <td><code>String</code></td>\n          <td>Build Directory of user defined or default's value</td>\n        </tr>\n        <tr>\n          <td><code>name</code></td>\n          <td><code>String</code></td>\n          <td>Build name <code>'server' || 'client'</code></td>\n        </tr>\n      </table>\n    </td>\n  <tr>\n  <tr>\n    <td><code>next</code></td>\n    <td>A callback required to be called to pass the custom configuration</td>\n  <tr>\n</table>\n\nExample of adding `ts-loader` through `maleo.config.js`:\n\n```js\n// maleo.config.js\n\n// Partly taken and modified from @airy/maleo-ts-plugin source\n// for simplicity purposes\n\nmodule.exports = {\n  webpack(config, context, next) {\n    const { isDev } = context\n\n    config.module.rules.push({\n      test: /\\.tsx?/,\n      exclude: /node_modules/,\n      use: [\n        require.resolve('@airy/maleo/lib/build/loaders/maleo-babel-loader'),\n        {\n          loader: 'ts-loader',\n          options: {\n            transpileOnly: true,\n          },\n        },\n      ],\n\n      if (isDev) {\n        config.plugins.push(new ForkTSCheckerWebpackPlugin());\n      }\n\n      return next();\n    })\n  },\n};\n```\n\n#### Customize Babel Config\n\nMaleo.js also let you have your own babel config. Just simply add `.babelrc` file at the root directory of your app.\n\nYou can include Maleo.js' babel preset in order to have latest JavaScript preset.\n\nHere's an example of `.babelrc` file:\n```json\n{\n  \"presets\": [\"@airy/maleo/babel\"],\n  \"plugins\": []\n}\n```\n\nThe `@airy/maleo/babel` preset includes everything you need to get your development started. The preset includes:\n- `@babel/preset-env`\n- `@babel/preset-react`\n- `@babel/plugin-proposal-class-properties`\n- `@babel/plugin-proposal-decorators`\n- `@babel/plugin-proposal-object-rest-spread`\n- `@babel/plugin-transform-runtime`\n- `react-loadable/babel`\n\n## CDN Support\n\nIf you are using a CDN, you can set up the `publicPath` setting and configure your CDN's origin to resolve to the domain that Maleo.js is hosted on.\n\n```js\n// maleo.config.js\n\nconst isProd = process.env.NODE_ENV === 'production';\n\nmodule.exports = {\n  assetPrefix: isProd && 'https://cdn.example.com';\n}\n```\n\n## FAQ\n\n== TO BE DETERMINED == \n\n## Plugins\n- [css-plugin](https://github.com/airyrooms/maleo.js/tree/canary/packages/plugins/css-plugin)\n- [typescript-plugin](https://github.com/airyrooms/maleo.js/tree/canary/packages/plugins/typescript-plugin)\n- [redux-plugin](https://github.com/airyrooms/maleo.js/tree/canary/packages/plugins/redux-plugin)\n\n\n## Contributing\n[Please follow these steps to contribute to Maleo.js](https://github.com/airyrooms/maleo.js/blob/canary/CONTRIBUTING.md)\n\n[Please follow these steps to contribute to Maleo.js' plugins](https://github.com/airyrooms/maleo.js/tree/canary/packages/plugins)\n\n\n## Contributors\n\nThis project exists thanks to all the people who contribute. [Contribute](CONTRIBUTING.md).\n\nMany thanks to our **[contributors](https://github.com/airyrooms/maleo.js/graphs/contributors)**!\n\n\n\n## License\n\nMIT","readmeFilename":"README.md","bugs":{"url":"https://github.com/airyrooms/maleo.js/issues"},"homepage":"https://github.com/airyrooms/maleo.js#readme","_id":"@airy/maleo@0.0.12-canary.0","_nodeVersion":"11.12.0","_npmVersion":"lerna/3.13.1/node@v11.12.0+x64 (linux)","dist":{"integrity":"sha512-nfvGdGx7ECp+kJyXQdkT70WHX2KLihq1Iz8sDAL4QihcWnM5Z5JAmkkLlA+iQ/47BpqhKMaU42SOVzmtWbMLaA==","shasum":"3441b2438538b5f04056d82589c3fb4a340b8bc2","tarball":"https://registry.npmjs.org/@airy/maleo/-/maleo-0.0.12-canary.0.tgz","fileCount":83,"unpackedSize":228176,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJcmDpNCRA9TVsSAnZWagAAtqsP+gNy7jNsnwY09eAF4xxD\n1+BCAFQgcd6+m1el/1204XGnaefAVhv0BYXG+EoWzIP3IlEgkt4pVE2hOzw6\nnq9MUcU3TanoItXa9hZgbXSsXh7y8KAQl50FPx6rpL7Q+B3RnK6VyCfNPSW1\n9wgyTWVL/mDh6NZfihvQhNXVrUb1cj67VM9z/63d6BkYJHRkmp/u6q8zwp3f\numhKxf5QCAo0Ay8E8YDrhjrnuenrT19XQxDCAl8rE3yYDjKa7tTXNKb5ZouT\ncte5xHJg4m5ipAPaPBBYBJaAWzXDhDkbTWpAFSFoVS3xGaehFZBolzCUWM7c\nqsAujCPpgTcd20nfmwLGhBKKve/mPPkcXkSwSGAuLqy+gN89IF4Nq8qPD0+T\nCZ/IEznWy9UucnNMsHM9YJEBdXvUFwZ0tjNCWFMeaC7BcHwvtf1H2j3SlEIj\niDeaNKbaXpS2FKpwxi4qfCo/CJ++j0K5DnZmCFzVjM380knpq4hbNUUN+A+a\n9l+tKERvry4PXEcxz2ibtPz1vyc0/u4TS5hg6qY8YOZQ4T4z6u1BzObHJynA\n8QlrPbxXs9rb3P4Nn35StOEi1vkAtm7DUBkxmDoDa3qBsrhALujJ1ZokFuM9\n957RuCBOo9armIKz2Pn2jmmq8b5sQFHEHeL7WKd+Z7C7I+Z2kSyyo7iMKwUP\n94Cy\r\n=/Kln\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQC+UZkowkUkLf5umE5jnY9TRVtkQF7PV8GDN6Z5ifL5WgIhAIJM3VhJQ7f2ZmKXi8GT6BSP/qGRmQIqJK6LCBqYGE9T"}]},"maintainers":[{"name":"airyrooms-engineering","email":"engineering@airyrooms.com"}],"_npmUser":{"name":"airyrooms-engineering","email":"engineering@airyrooms.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/maleo_0.0.12-canary.0_1553480268539_0.37582252222994494"},"_hasShrinkwrap":false},"0.0.12-canary.1":{"name":"@airy/maleo","version":"0.0.12-canary.1","description":"Un-opinionated Universal Rendering Javascript Framework","repository":{"type":"git","url":"git+https://github.com/airyrooms/maleo.js.git","bugs":"https://github.com/airyrooms/maleo.js/issues"},"author":{"name":"Airy"},"license":"MIT","private":false,"sideEffects":false,"scripts":{"build":"rm -rf dist && gulp","watch":"gulp watch","prepublish":"yarn build","start":"node server.js"},"bin":{"maleo":"lib/bin/maleo.js"},"dependencies":{"@babel/core":"7.1.2","@babel/plugin-proposal-class-properties":"7.1.0","@babel/plugin-proposal-decorators":"7.1.2","@babel/plugin-proposal-object-rest-spread":"7.0.0","@babel/plugin-syntax-dynamic-import":"7.0.0","@babel/plugin-transform-runtime":"7.1.0","@babel/preset-env":"7.1.0","@babel/preset-react":"7.0.0","@babel/runtime":"7.1.2","@babel/runtime-corejs2":"7.2.0","babel-loader":"8.0.4","case-sensitive-paths-webpack-plugin":"2.1.2","es6-promise":"4.2.5","eventsource":"^1.0.7","express":"4.16.4","figlet":"1.2.1","friendly-errors-webpack-plugin":"1.7.0","hard-source-webpack-plugin":"0.13.1","helmet":"3.14.0","isomorphic-fetch":"2.2.1","loader-utils":"1.1.0","node-notifier":"5.2.1","nodemon":"1.18.4","react-dom":"16.8.4","react-helmet":"5.2.0","react-loadable":"5.5.0","react-router-config":"5.0.0","react-router-dom":"5.0.0","rimraf":"2.6.3","source-map-support":"0.5.9","terser-webpack-plugin":"1.1.0","webpack":"4.29.0","webpack-bundle-analyzer":"3.0.3","webpack-cli":"3.1.2","webpack-dev-middleware":"3.4.0","webpack-hot-middleware":"2.24.3","webpackbar":"2.6.3"},"peerDependencies":{"react":"^16.8.4"},"devDependencies":{"@babel/preset-typescript":"^7.1.0","@types/webpack-bundle-analyzer":"^2.13.0","babel-plugin-module-resolver":"^3.1.1","gulp":"github:gulpjs/gulp#4.0","gulp-babel":"^8.0.0","gulp-clean":"^0.4.0","gulp-sourcemaps":"^2.6.4","gulp-uglify":"^3.0.1","gulp-watch":"^5.0.1","react":"^16.8.4"},"gitHead":"b960f6c3e23bb2d55dc7a43bed6ea0ca08022999","readme":"[![Build Status](https://travis-ci.org/airyrooms/maleo.js.svg?branch=master)](https://travis-ci.org/airyrooms/maleo.js)\n[![Coverage Status](https://coveralls.io/repos/github/airyrooms/maleo.js/badge.svg?branch=master)](https://coveralls.io/github/airyrooms/maleo.js?branch=master)\n[![npm version](https://badge.fury.io/js/%40airy%2Fmaleo.svg)](https://badge.fury.io/js/%40airy%2Fmaleo)\n[![Issues](http://img.shields.io/github/issues/airyrooms/maleo.js.svg)]( https://github.com/airyrooms/maleo.js/issues)\n[![MIT license](https://img.shields.io/badge/License-MIT-blue.svg)](https://github.com/airyrooms/maleo.js/blob/canary/LICENSE)\n[![Discord Chat](https://img.shields.io/discord/550498214789513218.svg)](https://discord.gg/9eArCQn)\n\n\n# Welcome to Maleo.js\n\nMaleo.js is an un-opinionated framework to enable Universal Rendering in JavaScript using React with no hassle.\n\nWe are here to solve the time consuming setups Universal Rendering Required.\n\n---\n\nReadme below is the documentation for the `canary` (prerelease) branch. To view the documentation for the latest stable Maleo.js version change branch to `master`\n\n---\n\n# Table of Contents\n- [Features](#features)\n- [Setup](#setup)\n- [Component Lifecycle](#component-lifecycle)\n- [Routing](#routing)\n- [Dynamic Import Component](#dynamic-import-component)\n  - [Tips](#tips)\n  - [Preloading](#preloading)\n- [Customizable Component](#customizable-component)\n  - [Custom Document](#custom-document)\n  - [Custom Wrap](#custom-wrap)\n- [Custom Configuration](#custom-configuration)\n  - [Customize Server](#customize-server)\n  - [Customize Webpack](#customize-webpack)\n  - [Customize Babel Config](#customize-babel-config)\n- [CDN Support](#cdn-support)\n- [Plugins](#plugins)\n- [FAQ](#faq)\n- [Contributing](#contributing)\n\n---\n\n## Features\n- Universal Rendering\n- Plugin based framework\n- Customizable\n\n## Setup\n\nInstall Maleo.js \n\n>*for now change `@airy/maleo` to `@airy/maleo@canary` until we publish the stable version of this package*\n\n\n**NPM**\n```bash\n$ npm install --save @airy/maleo react\n```\n**Yarn**\n```bash\n$ yarn add @airy/maleo react\n```\n\nAdd this script to your `package.json`\n```json\n{\n  \"scripts\": {\n    \"dev\": \"maleo dev\",\n    \"build\": \"export NODE_ENV=production && maleo build\",\n    \"start\": \"export NODE_ENV=production && node .maleo/server.js\"\n  }\n}\n```\n\nCreate a page Root component\n```jsx\n// ./src/Root.jsx\nimport React from 'react';\n\n// Export default is required for registering page\nexport default class RootComponent extends React.Component {\n  render() {\n    return (\n      <h1>Hello World!</h1>\n    )\n  }\n}\n```\n\nAnd lastly, create a routing file on your project root directory called `routes.json` and register your page component\n```json\n[\n  {\n    \"path\": \"/\",\n    \"page\": \"./src/Root\"\n  }\n]\n```\n\nAfter that you can now run `$ npm run dev` and go to `http://localhost:3000`.\n\nYou should now see your app running on your browser.\n\nBy now you should see \n- Automatic transpilation and bundling (with webpack and babel)\n- ~~Hot code reloading~~ [#17](https://github.com/airyrooms/maleo.js/issues/17)\n- Server rendering\n\nTo see how simple this is, check out the sample app!\n\n## Component Lifecycle \n\nMaleo.js added a new component lifecycle hook called `getInitialProps`, this function is called during Server Side Rendering (SSR).\n\nThis is useful especially for SEO purposes. \n\nExample for stateful component:\n```jsx\nimport React from 'react';\n\nexport default class extends React.Component {\n  static getInitialProps = async (ctx) => {\n    const { req } = ctx;\n\n    const userAgent = req ? req.headers['user-agent'] : navigator.userAgent;\n\n    // the return value will be passed as props for this component\n    return { userAgent };\n  }\n\n  render() {\n    return (\n      <div>\n        Hello World {this.props.userAgent}\n      </div>\n    );\n  }\n}\n```\n\nExample for stateless component:\n```jsx\nconst Component = (props) => (\n  <div>\n    Hello World {props.userAgent}\n  </div>\n);\n\nComponent.getInitialprops = async (ctx) => {\n  const { req } = ctx;\n\n  const userAgent = req ? req.headers['user-agent'] : navigator.userAgent;\n\n  return { userAgent };\n};\n\nexport default Component;\n```\n\n`getInitialProps` receives a context object with the following properties:\n- `req` - HTTP request object (server only)\n- `res` - HTTP response object (server only)\n- `...wrapProps` - Spreaded properties from custom Wrap\n- `...appProps` - Spreaded properties from custom App\n\n## Routing\n\nRouting is declared in a centralized route config.\nRegister all the route config in `routes.json` file.\n\nIf you put the `routes.json` files on root directory, Maleo will automatically register your route. Otherwise put path to your routes on [Maleo config](#custom-configuration).\n\nRoutes file has to export default the route configuration.\nThe route object **expected to have distinct key** to indicate the route.\n\n<table>\n  <tr>\n    <td>Key</td>\n    <td>Type</td>\n    <td>Description</td>\n  </tr>\n  <tr>\n    <td><code>path</code></td>\n    <td><code>String!</code></td>\n    <td>Routes path</td>\n  </tr>\n  <tr>\n    <td><code>page</code></td>\n    <td><code>String!</code></td>\n    <td>Path to React Component for this route path</td>\n  </tr>\n  <tr>\n    <td><code>exact</code></td>\n    <td><code>Boolean?</code> [<code>false</code>]</td>\n    <td>To make url has to match exactly the path in order to render the component. Give <code>false</code> value if the route is a wrapper route component</td>\n  </tr>\n  <tr>\n    <td><code>routes</code></td>\n    <td><code>RouteObject?</code></td>\n    <td>Nested route</td>\n  </tr>\n</table>\n\n\nFor example:\n```json\n[\n  {\n    \"page\": \"./src/MainApp\",\n    \"routes\": [\n      {\n        \"path\": \"/\",\n        \"page\": \"./src/Search\",\n        \"exact\": true\n      },\n      {\n        \"path\": \"/search\",\n        \"page\": \"./src/Search\",\n        \"routes\": [\n          {\n            \"path\": \"/search/hello\",\n            \"page\": \"./src/Detail\",\n            \"exact\": true\n          }\n        ]\n      },\n      {\n        \"path\": \"/detail\",\n        \"page\": \"./src/Detail\",\n        \"exact\": true\n      }\n    ]\n  }\n]\n```\n\n## Dynamic Import Component\nMaleo.js supports TC39 [dynamic import proposal](https://github.com/tc39/proposal-dynamic-import) for JavaScript.\n\nYou can think dynamic import as another way to split your code into manageable chunks. You can use our `Dynamic` function which utilizes [react loadable](https://github.com/jamiebuilds/react-loadable)\n\nFor Example\n```js\n// DynamicComponent.js\nimport Dynamic from '@airy/maleo/dynamic';\n\nexport default Dynamic({\n  loader: () => import( /* webpackChunkName:\"DynamicComponent\" */ './component/DynamicLoad'),\n})\n```\n\n### **Preloading**\n\nFor optimization purposes, you can also preload a component even before the component got rendered.\n\nFor example, if you want to load component when a button get pressed, you can start preloading the component when the user hovers the mouse over the button.\n\nThe component created by `Dynamic` exposes a [static `preload` method](https://github.com/jamiebuilds/react-loadable#loadablecomponentpreload).\n\n```jsx\nimport React from 'react';\nimport Dynamic from '@airy/maleo/dynamic';\n\nconst DynamicBar = Dynamic({\n  loader: () => import('./Bar'),\n  loading: LoadingCompoinent\n});\n\nclass MyComponent extends React.Component {\n  state = { showBar: false };\n\n  onClick = () => {\n    this.setState({ showBar: true });\n  };\n\n  onMouseOver = () => DynamicBar.preload();\n\n  render() {\n    return (\n      <div>\n        <button\n          onClick={this.onClick}\n          onMouseOver={this.onMouseOver}>\n          Show Bar\n        </button>\n        { this.state.showBar && <DynamicBar /> }\n      </div>\n    )\n  }\n}\n```\n\n## Customizable Component\n\n### Custom Document\n\nHighly inspired by what [Next.js](https://github.com/zeit/next.js) has done on their awesome template customization.\n\nMaleo.js also enable customization on `Document` as document's markup. So you don't need to include tags like `<html>`, `<head>`, `<body>`, etc. \n\nTo override the default behavior, you'll need to create a component that extends the `Document` React class provided by Maleo.\n\n```jsx\n// document.jsx\nimport React from 'react';\nimport { Document, Header, Main, Scripts } from '@airy/maleo/document';\n\nexport default class extends Document {\n  render() {\n    return (\n      <html>\n        <Header>\n          <title>Maleo JS</title>\n          <meta charset=\"utf-8\" />\n          <meta name=\"description\" content=\"Maleo.js is awesome!\" />\n\n          <style>\n            {` body { background-color: #fff } `}\n          </style>\n        </Header>\n\n        <body>\n          <Main />\n\n          <Scripts />\n\n          <ReduxScript />\n        </body>\n      </html>\n    );\n  }\n}\n```\n\n### Custom Wrap\n\nMaleo.js uses the `Wrap` component to initialize pages. `Wrap` contains React Router's Component. You can add HoC here to wrap the application and control the page initialization. Which allows you to do amazing things like:\n- Persisting layour between page changes\n- Keeping state when navigating pages\n- Custom error handling using `componentDidCatch`\n- Inject additional data into pages (like Redux provider, etc)\n\nTo override the default behavior, you'll need to create a component that extends the `Wrap` React class provided by Maleo.\n\n```jsx\n// wrap.jsx\nimport React from 'react';\nimport { Wrap } from '@airy/maleo/wrap';\n\n// Redux plugin for Maleo.js\n// Hoc that creates store and add Redux Provider\nimport { withRedux } from '@airy/maleo-redux-plugin';\n\n// Custom Wrapper that will be rendered for the whole Application\nimport CustomWrapper from './component/CustomWrapper';\n\nimport { createStore } from './store';\n\n@withRedux(createStore)\nexport default class extends Wrap {\n  static getInitialProps = (ctx) => {\n    const { store } = ctx\n    // you receive store from context\n    // you can access or do something with the store here\n    console.log('Initialized Redux Store', store);\n    return {}\n  }\n\n  render() {\n    return (\n      <CustomWrapper>\n        {super.render()}\n      </CustomWrapper>\n    )\n  }\n}\n\n```\nIf you put `document.jsx` and `wrap.jsx` on root directory (the same level with `package.json`), then Maleo will automatically register your custom Document and Wrap. Otherwise, you can add the path to your custom Document and Wrap on [Maleo config](#custom-configuration)\n\n---\n***We are also working on adding default and customizable `Error` component page***\n\n---\n\n## Custom Configuration\n\nFor more advanced configuration of Maleo.js, like `webpack` config, registering `plugins`, path to your routes, custom Document and Wrap, and adding `path alias`, you can create a `maleo.config.js` in the root of your project directory. (same directory with `package.json`)\n\n```js\n// maleo.config.js\n\nmodule.exports = {\n  /* config options here */\n}\n```\n\nHere are the API's for the configuration:\n\n<table>\n  <tr>\n    <td>Key</td>\n    <td>Type</td>\n    <td>Description</td>\n  </tr>\n  <tr>\n    <td><code>buildDir</code></td>\n    <td><code>String?</code> [<code>.maleo</code>]</td>\n    <td>Directory to put Maleo.js' build assets</td>\n  </tr>\n  <tr>\n    <td><code>cache</code></td>\n    <td><code>Boolean?</code> [<code>true</code>]</td>\n    <td>Enable webpack build caching</td>\n  </tr>\n  <tr>\n    <td><code>sourceMaps</code></td>\n    <td><code>Boolean?</code> [<code>true</code>]</td>\n    <td>Enable webpack to generate source maps</td>\n  </tr>\n  <tr>\n    <td><code>alias</code></td>\n    <td><code>Object?</code></td>\n    <td>A key value pair for aliasing path directory\n      <br>\n      <code>\n      { 'component': './src/component' }\n      </code>\n    </td>\n  </tr>\n  <tr>\n    <td><code>publicPath</code></td>\n    <td><code>String?</code> [<code>/_assets/</code>]</td>\n    <td>To customize webpack's <code>publicPath</code> \n    <br>Comes in handy if using CDN to put built assets</td>\n  </tr>\n  <tr>\n    <td><code>analyzeBundle</code></td>\n    <td><code>Boolean?</code> [<code>false</code>]</td>\n    <td>To enable webpack's bundle analyzer, for analyzing bundle sizes during bundle debugging should Maleo.js' build process got slow</td>\n  </tr>\n  <tr>\n    <td><code>webpack</code></td>\n    <td><code>Function?</code></td>\n    <td>To customize webpack configuration, more details <a href=\"#customize-webpack\">here</a></td>\n  </tr>\n  <tr>\n    <td><code>routes</code></td>\n    <td><code>string?</code> [<code>rootDir/routes.jsx</code>]</td>\n    <td>Path to your routes file</td>\n  </tr>\n  <tr>\n    <td><code>customDocument</code></td>\n    <td><code>string?</code> [<code>rootDir/document.jsx</code>]</td>\n    <td>Path to your custom document file</td>\n  </tr>\n  <tr>\n    <td><code>customWrap</code></td>\n    <td><code>string?</code> [<code>rootDir/wrap.jsx</code>]</td>\n    <td>Path to your custom wrap file</td>\n  </tr>\n  <tr>\n    <td><code>customApp</code></td>\n    <td><code>string?</code> [<code>rootDir/app.jsx</code>]</td>\n    <td>Path to your custom app file</td>\n  </tr>\n</table>\n\n#### Customize Server\n\nCreate a `server.js` file on root directory where your `package.json` lives.\nHere you can customize Maleo's server.\n```js\nimport { Server } from '@airy/maleo/server';\nimport path from 'path';\n\nimport routeConfig from './routes';\n\nconst PORT = process.env.PORT || 8080;\n\nconst maleoServer = Server.init({\n  port: PORT,\n});\n\nmaleoServer.run(() => {\n  console.log('Server running on port :', PORT);\n});\n```\n\nHere are the API's for the configuration:\n\n<table>\n  <tr>\n    <td>Key</td>\n    <td>Type</td>\n    <td>Description</td>\n  </tr>\n  <tr>\n    <td><code>port</code></td>\n    <td><code>Number?</code> [<code>3000</code>]</td>\n    <td>Port to run Maleo server</td>\n  </tr>\n  <tr>\n    <td><code>assetDir</code></td>\n    <td><code>String?</code> [<code>'<root>/.maleo/client'</code>]</td>\n    <td>Directory for all client related assets</td>\n  </tr>\n</table>\n\n#### Customize Webpack\n\nYou are able to extend Maleo.js' default webpack configuration by defining a function on `maleo.config.js`\n\n```js\n// maleo.config.js\n\nmodule.exports = {\n  webpack(config, context, next) {\n    // Perform customizations to webpack config\n    // Important: This call is required in order to let Maleo pass the config to webpack\n    return next(); \n  },\n};\n```\n\nWebpack function will receive three arguments:\n\n<table>\n  <tr>\n    <td>Argument</td>\n    <td>Details</td>\n  <tr>\n  <tr>\n    <td><code>config</code></td>\n    <td>This contains webpack configuration object that you can manipulate</td>\n  <tr>\n  <tr>\n    <td><code>context</code></td>\n    <td>This contains some keys that are useful for the build context\n      <br>\n      <table>\n        <tr>\n          <td><code>isDev</code></td>\n          <td><code>Boolean</code></td>\n          <td>Check if current build is for development</td>\n        </tr>\n        <tr>\n          <td><code>publicPath</code></td>\n          <td><code>String</code></td>\n          <td>Public Path of user defined or default's value</td>\n        </tr>\n        <tr>\n          <td><code>analyzeBundle</code></td>\n          <td><code>Boolean</code></td>\n          <td>Check if analyze bundle is enabled</td>\n        </tr>\n        <tr>\n          <td><code>buildDirectory</code></td>\n          <td><code>String</code></td>\n          <td>Build Directory of user defined or default's value</td>\n        </tr>\n        <tr>\n          <td><code>name</code></td>\n          <td><code>String</code></td>\n          <td>Build name <code>'server' || 'client'</code></td>\n        </tr>\n      </table>\n    </td>\n  <tr>\n  <tr>\n    <td><code>next</code></td>\n    <td>A callback required to be called to pass the custom configuration</td>\n  <tr>\n</table>\n\nExample of adding `ts-loader` through `maleo.config.js`:\n\n```js\n// maleo.config.js\n\n// Partly taken and modified from @airy/maleo-ts-plugin source\n// for simplicity purposes\n\nmodule.exports = {\n  webpack(config, context, next) {\n    const { isDev } = context\n\n    config.module.rules.push({\n      test: /\\.tsx?/,\n      exclude: /node_modules/,\n      use: [\n        require.resolve('@airy/maleo/lib/build/loaders/maleo-babel-loader'),\n        {\n          loader: 'ts-loader',\n          options: {\n            transpileOnly: true,\n          },\n        },\n      ],\n\n      if (isDev) {\n        config.plugins.push(new ForkTSCheckerWebpackPlugin());\n      }\n\n      return next();\n    })\n  },\n};\n```\n\n#### Customize Babel Config\n\nMaleo.js also let you have your own babel config. Just simply add `.babelrc` file at the root directory of your app.\n\nYou can include Maleo.js' babel preset in order to have latest JavaScript preset.\n\nHere's an example of `.babelrc` file:\n```json\n{\n  \"presets\": [\"@airy/maleo/babel\"],\n  \"plugins\": []\n}\n```\n\nThe `@airy/maleo/babel` preset includes everything you need to get your development started. The preset includes:\n- `@babel/preset-env`\n- `@babel/preset-react`\n- `@babel/plugin-proposal-class-properties`\n- `@babel/plugin-proposal-decorators`\n- `@babel/plugin-proposal-object-rest-spread`\n- `@babel/plugin-transform-runtime`\n- `react-loadable/babel`\n\n## CDN Support\n\nIf you are using a CDN, you can set up the `publicPath` setting and configure your CDN's origin to resolve to the domain that Maleo.js is hosted on.\n\n```js\n// maleo.config.js\n\nconst isProd = process.env.NODE_ENV === 'production';\n\nmodule.exports = {\n  assetPrefix: isProd && 'https://cdn.example.com';\n}\n```\n\n## FAQ\n\n== TO BE DETERMINED == \n\n## Plugins\n- [css-plugin](https://github.com/airyrooms/maleo.js/tree/canary/packages/plugins/css-plugin)\n- [typescript-plugin](https://github.com/airyrooms/maleo.js/tree/canary/packages/plugins/typescript-plugin)\n- [redux-plugin](https://github.com/airyrooms/maleo.js/tree/canary/packages/plugins/redux-plugin)\n\n\n## Contributing\n[Please follow these steps to contribute to Maleo.js](https://github.com/airyrooms/maleo.js/blob/canary/CONTRIBUTING.md)\n\n[Please follow these steps to contribute to Maleo.js' plugins](https://github.com/airyrooms/maleo.js/tree/canary/packages/plugins)\n\n\n## Contributors\n\nThis project exists thanks to all the people who contribute. [Contribute](CONTRIBUTING.md).\n\nMany thanks to our **[contributors](https://github.com/airyrooms/maleo.js/graphs/contributors)**!\n\n\n\n## License\n\nMIT","readmeFilename":"README.md","bugs":{"url":"https://github.com/airyrooms/maleo.js/issues"},"homepage":"https://github.com/airyrooms/maleo.js#readme","_id":"@airy/maleo@0.0.12-canary.1","_nodeVersion":"11.12.0","_npmVersion":"lerna/3.13.1/node@v11.12.0+x64 (linux)","dist":{"integrity":"sha512-DGbHT8awddxpoQ/jow9Qs0v54gPTgo5Izjuw9oro9QCXXGxxb6QmearPJ3qCpmL1vsFxw5gFO4+RdtFjSCuzQg==","shasum":"f1e2ad4db0775d847fd6b1bae0299e6974ed577c","tarball":"https://registry.npmjs.org/@airy/maleo/-/maleo-0.0.12-canary.1.tgz","fileCount":83,"unpackedSize":229711,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJcmIGcCRA9TVsSAnZWagAAF9oP+gPC0McVSduO+8HXrNqz\nwErQNEE+zf8T5xnnxsUBq9UMFZGPal8GO10M61PV3WxIGTo6vYKrb0EKa1zr\nOGmZKQoX4ODY2A/QDrm4Fx0HR1eNoCl8r/iAtX4VeukjSgf/ZgMobkPhdXf5\ndqSlT1z1G4//ZQa3xDnwQGog0lG8Hn9UXs9YmlWbKc3kRUgLeJF7WUZrFMDp\noH8q4TsVD/wP6x8lFxpLOd5xsF28UUTPwnNKJqYhAeeNnWkwio15B8X6TnU5\ncvnfmlrtC2Oq5bmRvB7ov8rBTfy9TF9NyPAZ4oNEJGhYn+OYPtIUjP/mdp4P\nPD8z4KQdJ766/+UknQ+O/tHcLbfNeE1OKqXrOvU5g0O3MKuV5wjPBds6p2iz\nsEU27b/DE3YA1cZg9jUOshqp4ISzar0ThI36QvyEhBD2p5VQojIZuLuOSIOp\nIoAQyVyZeHnJb33R1mbzVqlGlwy0R0JWQx/mo5uwPY4kaXJZuq0Qy20YJWdT\nkWwcwiXJ1iIk1fssIQCe26BV1xuVbz5Bp2qeE35eJSpU2g8cAaHtkimTdB2L\nQMWTg5iVazHTGJBR2F63U3+8Kr7ep/2ilP+hZIsAaJWt/oxYC1T8bNFowxfc\nHQ/M7dni0U54ThIrLMIyrvjpEX07yxorBDI+Tm1LBzGqcx99JM7zRNLDF+Ld\nXsCH\r\n=SX1H\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCc8mTYmRCmEa6GynRmhaQ6lolK711p4cHB7ZTYqzXRLAIgXMtQEgkG7lweqntyjwWPAyM3h5hJZ8sCB1x9OPuGonE="}]},"maintainers":[{"name":"airyrooms-engineering","email":"engineering@airyrooms.com"}],"_npmUser":{"name":"airyrooms-engineering","email":"engineering@airyrooms.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/maleo_0.0.12-canary.1_1553498524385_0.39738157675492447"},"_hasShrinkwrap":false},"0.0.12":{"name":"@airy/maleo","version":"0.0.12","description":"Un-opinionated Universal Rendering Javascript Framework","repository":{"type":"git","url":"git+https://github.com/airyrooms/maleo.js.git","bugs":"https://github.com/airyrooms/maleo.js/issues"},"author":{"name":"Airy"},"license":"MIT","private":false,"sideEffects":false,"scripts":{"build":"rm -rf dist && gulp","watch":"gulp watch","prepublish":"yarn build","start":"node server.js"},"bin":{"maleo":"lib/bin/maleo.js"},"dependencies":{"@babel/core":"7.1.2","@babel/plugin-proposal-class-properties":"7.1.0","@babel/plugin-proposal-decorators":"7.1.2","@babel/plugin-proposal-object-rest-spread":"7.0.0","@babel/plugin-syntax-dynamic-import":"7.0.0","@babel/plugin-transform-runtime":"7.1.0","@babel/preset-env":"7.1.0","@babel/preset-react":"7.0.0","@babel/runtime":"7.1.2","@babel/runtime-corejs2":"7.2.0","babel-loader":"8.0.4","case-sensitive-paths-webpack-plugin":"2.1.2","es6-promise":"4.2.5","eventsource":"^1.0.7","express":"4.16.4","figlet":"1.2.1","friendly-errors-webpack-plugin":"1.7.0","hard-source-webpack-plugin":"0.13.1","helmet":"3.14.0","isomorphic-fetch":"2.2.1","loader-utils":"1.1.0","node-notifier":"5.2.1","nodemon":"1.18.4","react-dom":"16.8.4","react-helmet":"5.2.0","react-loadable":"5.5.0","react-router-config":"5.0.0","react-router-dom":"5.0.0","rimraf":"2.6.3","source-map-support":"0.5.9","terser-webpack-plugin":"1.1.0","webpack":"4.29.0","webpack-bundle-analyzer":"3.0.3","webpack-cli":"3.1.2","webpack-dev-middleware":"3.4.0","webpack-hot-middleware":"2.24.3","webpackbar":"2.6.3"},"peerDependencies":{"react":"^16.8.4"},"devDependencies":{"@babel/preset-typescript":"^7.1.0","@types/webpack-bundle-analyzer":"^2.13.0","babel-plugin-module-resolver":"^3.1.1","gulp":"github:gulpjs/gulp#4.0","gulp-babel":"^8.0.0","gulp-clean":"^0.4.0","gulp-sourcemaps":"^2.6.4","gulp-uglify":"^3.0.1","gulp-watch":"^5.0.1","react":"^16.8.4"},"gitHead":"03e32998afe75f07c1b65c40080565fef1209867","bugs":{"url":"https://github.com/airyrooms/maleo.js/issues"},"homepage":"https://github.com/airyrooms/maleo.js#readme","_id":"@airy/maleo@0.0.12","_nodeVersion":"10.11.0","_npmVersion":"lerna/3.13.1/node@v10.11.0+x64 (darwin)","dist":{"integrity":"sha512-+NGiPLVSBs/m8dUgeuGDTUEYtNBcnm3SW2aQI14awcuVFVGP9Oot7Ua69U+isHMNyG0l53VUNpfcyCmGawQBvg==","shasum":"c97e0d5d6eeea5b281e090db953b229162b504bd","tarball":"https://registry.npmjs.org/@airy/maleo/-/maleo-0.0.12.tgz","fileCount":83,"unpackedSize":229881,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJcmIppCRA9TVsSAnZWagAAzZIP/jFoppEWabYrR0YgC6Jt\n4sA2aTSaL3FTExdmF+8RU9ZLF4xy42egeeHFgTT8lMDCUGyd9jL2DaTbySA1\nck/1vQVMa+DdOJBVAar4TEmKRHNCtQJEqu2nqKv8cgd9GEUYwYXHXNV8O2KQ\n3EUDtdfRQvqXYh8RqKyKofX7I0pYMzZ4yCANiZegupl74hOWJ5rrb0+O0D6v\nUhMXPikNBdHueGegqZ3Vas9194u5iZJZz8qq4zveEaGB7aqdt7GOVf6Xi+bc\nX7E7dozK4xAaKQLJKhajb+pVfmvL+PdcagxTB/3AhxchLo3Gqs05fubk8hcl\n3vspdnDTr1Z8g+N1cg6y4TBXWUzPbLPeUsZBdYXxVfq3GRrGY9NgrP8ntQ10\nIg+vMkCGEgDjM/ZVFef+BN9jrd9ZDdSS4ya7RsEnkaMY3L1ITgfSG/FLKsFd\nELFsno/EQYB+onEdUWNvPZWOPKhf+qipcGs7Wszrsjs11WqMsnp6I/eyKDa5\nGH9/qN6RNSpx66uEoQc8N2MUMJRaRmVcSUV2VpUhPGNw92hRNYsHyOZkBN4Q\nGtMgKvwE0h4lFWhRfYIFXmYHp8JS6l9ZuqvgdBkC1w4u2DR4WN4DckUGGEW7\njZjHZbzf2v/OOTFXSC/HksmEiZiTmhuTrQQyOjjhVh+vaO4Ry+UoGIVYXhQc\nq1Q1\r\n=Prz3\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDpAYQz377MI8T0eq5t8S1n0mlDLzWp3bJssWjcYM5VzgIgbTzqNKyzoGKqlch4YK2H8/49KxM/a3eNW3NUFiuvvX8="}]},"maintainers":[{"name":"airyrooms-engineering","email":"engineering@airyrooms.com"}],"_npmUser":{"name":"airyrooms-engineering","email":"engineering@airyrooms.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/maleo_0.0.12_1553500776386_0.13360297272323618"},"_hasShrinkwrap":false},"0.0.13-canary.0":{"name":"@airy/maleo","version":"0.0.13-canary.0","description":"Un-opinionated Universal Rendering Javascript Framework","repository":{"type":"git","url":"git+https://github.com/airyrooms/maleo.js.git","bugs":"https://github.com/airyrooms/maleo.js/issues"},"author":{"name":"Airy"},"license":"MIT","private":false,"sideEffects":false,"scripts":{"build":"rm -rf dist && gulp","watch":"gulp watch","prepublish":"yarn build","start":"node server.js"},"bin":{"maleo":"lib/bin/maleo.js"},"dependencies":{"@babel/core":"7.1.2","@babel/plugin-proposal-class-properties":"7.1.0","@babel/plugin-proposal-decorators":"7.1.2","@babel/plugin-proposal-object-rest-spread":"7.0.0","@babel/plugin-syntax-dynamic-import":"7.0.0","@babel/plugin-transform-runtime":"7.1.0","@babel/preset-env":"7.1.0","@babel/preset-react":"7.0.0","@babel/runtime":"7.1.2","@babel/runtime-corejs2":"7.2.0","babel-loader":"8.0.4","case-sensitive-paths-webpack-plugin":"2.1.2","es6-promise":"4.2.5","eventsource":"^1.0.7","express":"4.16.4","figlet":"1.2.1","friendly-errors-webpack-plugin":"1.7.0","hard-source-webpack-plugin":"0.13.1","helmet":"3.14.0","isomorphic-fetch":"2.2.1","loader-utils":"1.1.0","node-notifier":"5.2.1","nodemon":"1.18.4","react-dom":"16.8.4","react-helmet":"5.2.0","react-loadable":"5.5.0","react-router-config":"5.0.0","react-router-dom":"5.0.0","rimraf":"2.6.3","source-map-support":"0.5.9","terser-webpack-plugin":"1.1.0","webpack":"4.29.0","webpack-bundle-analyzer":"3.0.3","webpack-cli":"3.1.2","webpack-dev-middleware":"3.4.0","webpack-hot-middleware":"2.24.3","webpackbar":"2.6.3"},"peerDependencies":{"react":"^16.8.4"},"devDependencies":{"@babel/preset-typescript":"^7.1.0","@types/webpack-bundle-analyzer":"^2.13.0","babel-plugin-module-resolver":"^3.1.1","gulp":"github:gulpjs/gulp#4.0","gulp-babel":"^8.0.0","gulp-clean":"^0.4.0","gulp-sourcemaps":"^2.6.4","gulp-uglify":"^3.0.1","gulp-watch":"^5.0.1","react":"^16.8.4"},"gitHead":"a13463dad317a85e6c28cdd32bfd382fcdf9e47c","readme":"[![Build Status](https://travis-ci.org/airyrooms/maleo.js.svg?branch=master)](https://travis-ci.org/airyrooms/maleo.js)\n[![Coverage Status](https://coveralls.io/repos/github/airyrooms/maleo.js/badge.svg?branch=master)](https://coveralls.io/github/airyrooms/maleo.js?branch=master)\n[![npm version](https://badge.fury.io/js/%40airy%2Fmaleo.svg)](https://badge.fury.io/js/%40airy%2Fmaleo)\n[![Issues](http://img.shields.io/github/issues/airyrooms/maleo.js.svg)]( https://github.com/airyrooms/maleo.js/issues)\n[![MIT license](https://img.shields.io/badge/License-MIT-blue.svg)](https://github.com/airyrooms/maleo.js/blob/canary/LICENSE)\n[![Discord Chat](https://img.shields.io/discord/550498214789513218.svg)](https://discord.gg/9eArCQn)\n\n\n# Welcome to Maleo.js\n\nMaleo.js is an un-opinionated framework to enable Universal Rendering in JavaScript using React with no hassle.\n\nWe are here to solve the time consuming setups Universal Rendering Required.\n\n---\n\nReadme below is the documentation for the `canary` (prerelease) branch. To view the documentation for the latest stable Maleo.js version change branch to `master`\n\n---\n\n# Table of Contents\n- [Features](#features)\n- [Setup](#setup)\n- [Component Lifecycle](#component-lifecycle)\n- [Routing](#routing)\n- [Dynamic Import Component](#dynamic-import-component)\n  - [Tips](#tips)\n  - [Preloading](#preloading)\n- [Customizable Component](#customizable-component)\n  - [Custom Document](#custom-document)\n  - [Custom Wrap](#custom-wrap)\n- [Custom Configuration](#custom-configuration)\n  - [Customize Server](#customize-server)\n  - [Customize Webpack](#customize-webpack)\n  - [Customize Babel Config](#customize-babel-config)\n- [CDN Support](#cdn-support)\n- [Plugins](#plugins)\n- [FAQ](#faq)\n- [Contributing](#contributing)\n\n---\n\n## Features\n- Universal Rendering\n- Plugin based framework\n- Customizable\n\n## Setup\n\nInstall Maleo.js \n\n>*for now change `@airy/maleo` to `@airy/maleo@canary` until we publish the stable version of this package*\n\n\n**NPM**\n```bash\n$ npm install --save @airy/maleo react\n```\n**Yarn**\n```bash\n$ yarn add @airy/maleo react\n```\n\nAdd this script to your `package.json`\n```json\n{\n  \"scripts\": {\n    \"dev\": \"maleo dev\",\n    \"build\": \"export NODE_ENV=production && maleo build\",\n    \"start\": \"export NODE_ENV=production && node .maleo/server.js\"\n  }\n}\n```\n\nCreate a page Root component\n```jsx\n// ./src/Root.jsx\nimport React from 'react';\n\n// Export default is required for registering page\nexport default class RootComponent extends React.Component {\n  render() {\n    return (\n      <h1>Hello World!</h1>\n    )\n  }\n}\n```\n\nAnd lastly, create a routing file on your project root directory called `routes.json` and register your page component\n```json\n[\n  {\n    \"path\": \"/\",\n    \"page\": \"./src/Root\"\n  }\n]\n```\n\nAfter that you can now run `$ npm run dev` and go to `http://localhost:3000`.\n\nYou should now see your app running on your browser.\n\nBy now you should see \n- Automatic transpilation and bundling (with webpack and babel)\n- ~~Hot code reloading~~ [#17](https://github.com/airyrooms/maleo.js/issues/17)\n- Server rendering\n\nTo see how simple this is, check out the sample app!\n\n## Component Lifecycle \n\nMaleo.js added a new component lifecycle hook called `getInitialProps`, this function is called during Server Side Rendering (SSR).\n\nThis is useful especially for SEO purposes. \n\nExample for stateful component:\n```jsx\nimport React from 'react';\n\nexport default class extends React.Component {\n  static getInitialProps = async (ctx) => {\n    const { req } = ctx;\n\n    const userAgent = req ? req.headers['user-agent'] : navigator.userAgent;\n\n    // the return value will be passed as props for this component\n    return { userAgent };\n  }\n\n  render() {\n    return (\n      <div>\n        Hello World {this.props.userAgent}\n      </div>\n    );\n  }\n}\n```\n\nExample for stateless component:\n```jsx\nconst Component = (props) => (\n  <div>\n    Hello World {props.userAgent}\n  </div>\n);\n\nComponent.getInitialprops = async (ctx) => {\n  const { req } = ctx;\n\n  const userAgent = req ? req.headers['user-agent'] : navigator.userAgent;\n\n  return { userAgent };\n};\n\nexport default Component;\n```\n\n`getInitialProps` receives a context object with the following properties:\n- `req` - HTTP request object (server only)\n- `res` - HTTP response object (server only)\n- `...wrapProps` - Spreaded properties from custom Wrap\n- `...appProps` - Spreaded properties from custom App\n\n## Routing\n\nRouting is declared in a centralized route config.\nRegister all the route config in `routes.json` file.\n\nIf you put the `routes.json` files on root directory, Maleo will automatically register your route. Otherwise put path to your routes on [Maleo config](#custom-configuration).\n\nRoutes file has to export default the route configuration.\nThe route object **expected to have distinct key** to indicate the route.\n\n<table>\n  <tr>\n    <td>Key</td>\n    <td>Type</td>\n    <td>Description</td>\n  </tr>\n  <tr>\n    <td><code>path</code></td>\n    <td><code>String!</code></td>\n    <td>Routes path</td>\n  </tr>\n  <tr>\n    <td><code>page</code></td>\n    <td><code>String!</code></td>\n    <td>Path to React Component for this route path</td>\n  </tr>\n  <tr>\n    <td><code>exact</code></td>\n    <td><code>Boolean?</code> [<code>false</code>]</td>\n    <td>To make url has to match exactly the path in order to render the component. Give <code>false</code> value if the route is a wrapper route component</td>\n  </tr>\n  <tr>\n    <td><code>routes</code></td>\n    <td><code>RouteObject?</code></td>\n    <td>Nested route</td>\n  </tr>\n</table>\n\n\nFor example:\n```json\n[\n  {\n    \"page\": \"./src/MainApp\",\n    \"routes\": [\n      {\n        \"path\": \"/\",\n        \"page\": \"./src/Search\",\n        \"exact\": true\n      },\n      {\n        \"path\": \"/search\",\n        \"page\": \"./src/Search\",\n        \"routes\": [\n          {\n            \"path\": \"/search/hello\",\n            \"page\": \"./src/Detail\",\n            \"exact\": true\n          }\n        ]\n      },\n      {\n        \"path\": \"/detail\",\n        \"page\": \"./src/Detail\",\n        \"exact\": true\n      }\n    ]\n  }\n]\n```\n\n## Dynamic Import Component\nMaleo.js supports TC39 [dynamic import proposal](https://github.com/tc39/proposal-dynamic-import) for JavaScript.\n\nYou can think dynamic import as another way to split your code into manageable chunks. You can use our `Dynamic` function which utilizes [react loadable](https://github.com/jamiebuilds/react-loadable)\n\nFor Example\n```js\n// DynamicComponent.js\nimport Dynamic from '@airy/maleo/dynamic';\n\nexport default Dynamic({\n  loader: () => import( /* webpackChunkName:\"DynamicComponent\" */ './component/DynamicLoad'),\n})\n```\n\n### **Preloading**\n\nFor optimization purposes, you can also preload a component even before the component got rendered.\n\nFor example, if you want to load component when a button get pressed, you can start preloading the component when the user hovers the mouse over the button.\n\nThe component created by `Dynamic` exposes a [static `preload` method](https://github.com/jamiebuilds/react-loadable#loadablecomponentpreload).\n\n```jsx\nimport React from 'react';\nimport Dynamic from '@airy/maleo/dynamic';\n\nconst DynamicBar = Dynamic({\n  loader: () => import('./Bar'),\n  loading: LoadingCompoinent\n});\n\nclass MyComponent extends React.Component {\n  state = { showBar: false };\n\n  onClick = () => {\n    this.setState({ showBar: true });\n  };\n\n  onMouseOver = () => DynamicBar.preload();\n\n  render() {\n    return (\n      <div>\n        <button\n          onClick={this.onClick}\n          onMouseOver={this.onMouseOver}>\n          Show Bar\n        </button>\n        { this.state.showBar && <DynamicBar /> }\n      </div>\n    )\n  }\n}\n```\n\n## Customizable Component\n\n### Custom Document\n\nHighly inspired by what [Next.js](https://github.com/zeit/next.js) has done on their awesome template customization.\n\nMaleo.js also enable customization on `Document` as document's markup. So you don't need to include tags like `<html>`, `<head>`, `<body>`, etc. \n\nTo override the default behavior, you'll need to create a component that extends the `Document` React class provided by Maleo.\n\n```jsx\n// document.jsx\nimport React from 'react';\nimport { Document, Header, Main, Scripts } from '@airy/maleo/document';\n\nexport default class extends Document {\n  render() {\n    return (\n      <html>\n        <Header>\n          <title>Maleo JS</title>\n          <meta charset=\"utf-8\" />\n          <meta name=\"description\" content=\"Maleo.js is awesome!\" />\n\n          <style>\n            {` body { background-color: #fff } `}\n          </style>\n        </Header>\n\n        <body>\n          <Main />\n\n          <Scripts />\n\n          <ReduxScript />\n        </body>\n      </html>\n    );\n  }\n}\n```\n\n### Custom Wrap\n\nMaleo.js uses the `Wrap` component to initialize pages. `Wrap` contains React Router's Component. You can add HoC here to wrap the application and control the page initialization. Which allows you to do amazing things like:\n- Persisting layour between page changes\n- Keeping state when navigating pages\n- Custom error handling using `componentDidCatch`\n- Inject additional data into pages (like Redux provider, etc)\n\nTo override the default behavior, you'll need to create a component that extends the `Wrap` React class provided by Maleo.\n\n```jsx\n// wrap.jsx\nimport React from 'react';\nimport { Wrap } from '@airy/maleo/wrap';\n\n// Redux plugin for Maleo.js\n// Hoc that creates store and add Redux Provider\nimport { withRedux } from '@airy/maleo-redux-plugin';\n\n// Custom Wrapper that will be rendered for the whole Application\nimport CustomWrapper from './component/CustomWrapper';\n\nimport { createStore } from './store';\n\n@withRedux(createStore)\nexport default class extends Wrap {\n  static getInitialProps = (ctx) => {\n    const { store } = ctx\n    // you receive store from context\n    // you can access or do something with the store here\n    console.log('Initialized Redux Store', store);\n    return {}\n  }\n\n  render() {\n    return (\n      <CustomWrapper>\n        {super.render()}\n      </CustomWrapper>\n    )\n  }\n}\n\n```\nIf you put `document.jsx` and `wrap.jsx` on root directory (the same level with `package.json`), then Maleo will automatically register your custom Document and Wrap. Otherwise, you can add the path to your custom Document and Wrap on [Maleo config](#custom-configuration)\n\n---\n***We are also working on adding default and customizable `Error` component page***\n\n---\n\n## Custom Configuration\n\nFor more advanced configuration of Maleo.js, like `webpack` config, registering `plugins`, path to your routes, custom Document and Wrap, and adding `path alias`, you can create a `maleo.config.js` in the root of your project directory. (same directory with `package.json`)\n\n```js\n// maleo.config.js\n\nmodule.exports = {\n  /* config options here */\n}\n```\n\nHere are the API's for the configuration:\n\n<table>\n  <tr>\n    <td>Key</td>\n    <td>Type</td>\n    <td>Description</td>\n  </tr>\n  <tr>\n    <td><code>buildDir</code></td>\n    <td><code>String?</code> [<code>.maleo</code>]</td>\n    <td>Directory to put Maleo.js' build assets</td>\n  </tr>\n  <tr>\n    <td><code>cache</code></td>\n    <td><code>Boolean?</code> [<code>true</code>]</td>\n    <td>Enable webpack build caching</td>\n  </tr>\n  <tr>\n    <td><code>sourceMaps</code></td>\n    <td><code>Boolean?</code> [<code>true</code>]</td>\n    <td>Enable webpack to generate source maps</td>\n  </tr>\n  <tr>\n    <td><code>alias</code></td>\n    <td><code>Object?</code></td>\n    <td>A key value pair for aliasing path directory\n      <br>\n      <code>\n      { 'component': './src/component' }\n      </code>\n    </td>\n  </tr>\n  <tr>\n    <td><code>publicPath</code></td>\n    <td><code>String?</code> [<code>/_assets/</code>]</td>\n    <td>To customize webpack's <code>publicPath</code> \n    <br>Comes in handy if using CDN to put built assets</td>\n  </tr>\n  <tr>\n    <td><code>analyzeBundle</code></td>\n    <td><code>Boolean?</code> [<code>false</code>]</td>\n    <td>To enable webpack's bundle analyzer, for analyzing bundle sizes during bundle debugging should Maleo.js' build process got slow</td>\n  </tr>\n  <tr>\n    <td><code>webpack</code></td>\n    <td><code>Function?</code></td>\n    <td>To customize webpack configuration, more details <a href=\"#customize-webpack\">here</a></td>\n  </tr>\n  <tr>\n    <td><code>routes</code></td>\n    <td><code>string?</code> [<code>rootDir/routes.jsx</code>]</td>\n    <td>Path to your routes file</td>\n  </tr>\n  <tr>\n    <td><code>customDocument</code></td>\n    <td><code>string?</code> [<code>rootDir/document.jsx</code>]</td>\n    <td>Path to your custom document file</td>\n  </tr>\n  <tr>\n    <td><code>customWrap</code></td>\n    <td><code>string?</code> [<code>rootDir/wrap.jsx</code>]</td>\n    <td>Path to your custom wrap file</td>\n  </tr>\n  <tr>\n    <td><code>customApp</code></td>\n    <td><code>string?</code> [<code>rootDir/app.jsx</code>]</td>\n    <td>Path to your custom app file</td>\n  </tr>\n</table>\n\n#### Customize Server\n\nCreate a `server.js` file on root directory where your `package.json` lives.\nHere you can customize Maleo's server.\n```js\nimport { Server } from '@airy/maleo/server';\nimport path from 'path';\n\nimport routeConfig from './routes';\n\nconst PORT = process.env.PORT || 8080;\n\nconst maleoServer = Server.init({\n  port: PORT,\n});\n\nmaleoServer.run(() => {\n  console.log('Server running on port :', PORT);\n});\n```\n\nHere are the API's for the configuration:\n\n<table>\n  <tr>\n    <td>Key</td>\n    <td>Type</td>\n    <td>Description</td>\n  </tr>\n  <tr>\n    <td><code>port</code></td>\n    <td><code>Number?</code> [<code>3000</code>]</td>\n    <td>Port to run Maleo server</td>\n  </tr>\n  <tr>\n    <td><code>assetDir</code></td>\n    <td><code>String?</code> [<code>'<root>/.maleo/client'</code>]</td>\n    <td>Directory for all client related assets</td>\n  </tr>\n</table>\n\n#### Customize Webpack\n\nYou are able to extend Maleo.js' default webpack configuration by defining a function on `maleo.config.js`\n\n```js\n// maleo.config.js\n\nmodule.exports = {\n  webpack(config, context, next) {\n    // Perform customizations to webpack config\n    // Important: This call is required in order to let Maleo pass the config to webpack\n    return next(); \n  },\n};\n```\n\nWebpack function will receive three arguments:\n\n<table>\n  <tr>\n    <td>Argument</td>\n    <td>Details</td>\n  <tr>\n  <tr>\n    <td><code>config</code></td>\n    <td>This contains webpack configuration object that you can manipulate</td>\n  <tr>\n  <tr>\n    <td><code>context</code></td>\n    <td>This contains some keys that are useful for the build context\n      <br>\n      <table>\n        <tr>\n          <td><code>isDev</code></td>\n          <td><code>Boolean</code></td>\n          <td>Check if current build is for development</td>\n        </tr>\n        <tr>\n          <td><code>publicPath</code></td>\n          <td><code>String</code></td>\n          <td>Public Path of user defined or default's value</td>\n        </tr>\n        <tr>\n          <td><code>analyzeBundle</code></td>\n          <td><code>Boolean</code></td>\n          <td>Check if analyze bundle is enabled</td>\n        </tr>\n        <tr>\n          <td><code>buildDirectory</code></td>\n          <td><code>String</code></td>\n          <td>Build Directory of user defined or default's value</td>\n        </tr>\n        <tr>\n          <td><code>name</code></td>\n          <td><code>String</code></td>\n          <td>Build name <code>'server' || 'client'</code></td>\n        </tr>\n      </table>\n    </td>\n  <tr>\n  <tr>\n    <td><code>next</code></td>\n    <td>A callback required to be called to pass the custom configuration</td>\n  <tr>\n</table>\n\nExample of adding `ts-loader` through `maleo.config.js`:\n\n```js\n// maleo.config.js\n\n// Partly taken and modified from @airy/maleo-ts-plugin source\n// for simplicity purposes\n\nmodule.exports = {\n  webpack(config, context, next) {\n    const { isDev } = context\n\n    config.module.rules.push({\n      test: /\\.tsx?/,\n      exclude: /node_modules/,\n      use: [\n        require.resolve('@airy/maleo/lib/build/loaders/maleo-babel-loader'),\n        {\n          loader: 'ts-loader',\n          options: {\n            transpileOnly: true,\n          },\n        },\n      ],\n\n      if (isDev) {\n        config.plugins.push(new ForkTSCheckerWebpackPlugin());\n      }\n\n      return next();\n    })\n  },\n};\n```\n\n#### Customize Babel Config\n\nMaleo.js also let you have your own babel config. Just simply add `.babelrc` file at the root directory of your app.\n\nYou can include Maleo.js' babel preset in order to have latest JavaScript preset.\n\nHere's an example of `.babelrc` file:\n```json\n{\n  \"presets\": [\"@airy/maleo/babel\"],\n  \"plugins\": []\n}\n```\n\nThe `@airy/maleo/babel` preset includes everything you need to get your development started. The preset includes:\n- `@babel/preset-env`\n- `@babel/preset-react`\n- `@babel/plugin-proposal-class-properties`\n- `@babel/plugin-proposal-decorators`\n- `@babel/plugin-proposal-object-rest-spread`\n- `@babel/plugin-transform-runtime`\n- `react-loadable/babel`\n\n## CDN Support\n\nIf you are using a CDN, you can set up the `publicPath` setting and configure your CDN's origin to resolve to the domain that Maleo.js is hosted on.\n\n```js\n// maleo.config.js\n\nconst isProd = process.env.NODE_ENV === 'production';\n\nmodule.exports = {\n  assetPrefix: isProd && 'https://cdn.example.com';\n}\n```\n\n## FAQ\n\n== TO BE DETERMINED == \n\n## Plugins\n- [css-plugin](https://github.com/airyrooms/maleo.js/tree/canary/packages/plugins/css-plugin)\n- [typescript-plugin](https://github.com/airyrooms/maleo.js/tree/canary/packages/plugins/typescript-plugin)\n- [redux-plugin](https://github.com/airyrooms/maleo.js/tree/canary/packages/plugins/redux-plugin)\n\n\n## Contributing\n[Please follow these steps to contribute to Maleo.js](https://github.com/airyrooms/maleo.js/blob/canary/CONTRIBUTING.md)\n\n[Please follow these steps to contribute to Maleo.js' plugins](https://github.com/airyrooms/maleo.js/tree/canary/packages/plugins)\n\n\n## Contributors\n\nThis project exists thanks to all the people who contribute. [Contribute](CONTRIBUTING.md).\n\nMany thanks to our **[contributors](https://github.com/airyrooms/maleo.js/graphs/contributors)**!\n\n\n\n## License\n\nMIT","readmeFilename":"README.md","bugs":{"url":"https://github.com/airyrooms/maleo.js/issues"},"homepage":"https://github.com/airyrooms/maleo.js#readme","_id":"@airy/maleo@0.0.13-canary.0","_nodeVersion":"10.15.3","_npmVersion":"lerna/3.13.1/node@v10.15.3+x64 (linux)","dist":{"integrity":"sha512-DZ7KGo/T1Df7l3JF0LnwJEvqYeGbb0PlxABpDbpPf3ZaSQ6Zi0i3mcbaguYzqPGGIiuD+9BVjd2UQ5m2fgQiWw==","shasum":"05dc109baa27cf9f4f8c6b6feb73c264f03d747b","tarball":"https://registry.npmjs.org/@airy/maleo/-/maleo-0.0.13-canary.0.tgz","fileCount":83,"unpackedSize":230087,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJcmIzRCRA9TVsSAnZWagAAlwYP/0pq8Fpo/TaV/I9WWHWg\nMHdDds7eTQW7Op6la1lq/fbei1nkD0fZm57+l6GdoSUEcGBxl9W4P9dE1b8E\nojvoIqOvtUC9afqpL7orvt3/O5xM6n5B1v4y7WIMvniOGvoPmAHmK6YLBgDQ\nn6tsFSNdjVfz2rY2YyP6KjLm+oijwuPssEHLoPe4dkV5mG3ElclJZTciKBGM\nECG5Q+Ii951pwUt1RRKdo0KHXFnBqb6bjQYLpRQjrafhnNMDY6RNGZVzPgUa\n6sTJdUIygexDu//Ve0TWd6Pbtp14DsjVcFWskut2Nt+0qT4fIn7u0fzmtaIf\n4UktnIszcaSEX3yTv/E5W/gm/rqZT3MNkPnkKiFc+NaV76Kc/xVgSfTxHRp8\nRofuYWFgqRk+p/Hg6RuBJ1VExMnjg63P8it+oS84uaVCshyxC9e/+XBer3jz\nPjT/nYgjvLuSHpfkWHXLg7cvqpABQJFPfdXIzzyBN6PhOpsyLdrpco6BN3Kh\n3c60zSqFZDAKGIChHQJ+YdEeFettajAW2P3tnpuTVS1VlzPzsJKsDY/3XiOz\n/KJnSXocctF22vryOzM63qmBzuMqHUQ82nUxCCpJgJIn2hqDHEBq0XpZiNMv\n8oFENtOsneO0SMPneeeivuwY0fESGPvj+ttL8wtO7/OqMXtvju/bDThykan8\nmQaZ\r\n=9+mj\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIHkGPp2ajn+ad3BnxKpn/Rr0NpyD4CTkU+DTKlAHwNKrAiEAirPBsvzGaBjFaBkiyFBfsIvDYY+rI832jn2rJ9e+RW4="}]},"maintainers":[{"name":"airyrooms-engineering","email":"engineering@airyrooms.com"}],"_npmUser":{"name":"airyrooms-engineering","email":"engineering@airyrooms.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/maleo_0.0.13-canary.0_1553501393313_0.2814792238900117"},"_hasShrinkwrap":false},"0.0.13":{"name":"@airy/maleo","version":"0.0.13","description":"Un-opinionated Universal Rendering Javascript Framework","repository":{"type":"git","url":"git+https://github.com/airyrooms/maleo.js.git","bugs":"https://github.com/airyrooms/maleo.js/issues"},"author":{"name":"Airy"},"license":"MIT","private":false,"sideEffects":false,"scripts":{"build":"rm -rf dist && gulp","watch":"gulp watch","prepublish":"yarn build","start":"node server.js"},"bin":{"maleo":"lib/bin/maleo.js"},"dependencies":{"@babel/core":"7.1.2","@babel/plugin-proposal-class-properties":"7.1.0","@babel/plugin-proposal-decorators":"7.1.2","@babel/plugin-proposal-object-rest-spread":"7.0.0","@babel/plugin-syntax-dynamic-import":"7.0.0","@babel/plugin-transform-runtime":"7.1.0","@babel/preset-env":"7.1.0","@babel/preset-react":"7.0.0","@babel/runtime":"7.1.2","@babel/runtime-corejs2":"7.2.0","babel-loader":"8.0.4","case-sensitive-paths-webpack-plugin":"2.1.2","es6-promise":"4.2.5","eventsource":"^1.0.7","express":"4.16.4","figlet":"1.2.1","friendly-errors-webpack-plugin":"1.7.0","hard-source-webpack-plugin":"0.13.1","helmet":"3.14.0","isomorphic-fetch":"2.2.1","loader-utils":"1.1.0","node-notifier":"5.2.1","nodemon":"1.18.4","react-dom":"16.8.4","react-helmet":"5.2.0","react-loadable":"5.5.0","react-router-config":"5.0.0","react-router-dom":"5.0.0","rimraf":"2.6.3","source-map-support":"0.5.9","terser-webpack-plugin":"1.1.0","webpack":"4.29.0","webpack-bundle-analyzer":"3.0.3","webpack-cli":"3.1.2","webpack-dev-middleware":"3.4.0","webpack-hot-middleware":"2.24.3","webpackbar":"2.6.3"},"peerDependencies":{"react":"^16.8.4"},"devDependencies":{"@babel/preset-typescript":"^7.1.0","@types/webpack-bundle-analyzer":"^2.13.0","babel-plugin-module-resolver":"^3.1.1","gulp":"github:gulpjs/gulp#4.0","gulp-babel":"^8.0.0","gulp-clean":"^0.4.0","gulp-sourcemaps":"^2.6.4","gulp-uglify":"^3.0.1","gulp-watch":"^5.0.1","react":"^16.8.4"},"gitHead":"4d30934c4d56387cbac02a1333a54125b422722b","bugs":{"url":"https://github.com/airyrooms/maleo.js/issues"},"homepage":"https://github.com/airyrooms/maleo.js#readme","_id":"@airy/maleo@0.0.13","_nodeVersion":"10.11.0","_npmVersion":"lerna/3.13.1/node@v10.11.0+x64 (darwin)","dist":{"integrity":"sha512-6lwMYiDcdqtvzYL7CjiVFPD5RFCKkAy9hulsN6cuVfwc4TTWXFxXrE+le3rfYgB3Ux2Ekbz1vJ0XpcNqhzVJ7A==","shasum":"ac2b44f903b4cf801a45336cd582670b54c82390","tarball":"https://registry.npmjs.org/@airy/maleo/-/maleo-0.0.13.tgz","fileCount":83,"unpackedSize":230280,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJcmJyaCRA9TVsSAnZWagAAly8QAJ5SLwtkKhPob7W3hYzP\n92pUqZUI8EllxrqjPTBtgPcWG9fjPp1w0gn0kJosKa5NtrUS47LYu+UAgSi1\n7LR3Vnhe0vTspUedYK/G2aCkhgXIugA45Zy8bit8yBzMsvv70uYvWVyQCk3P\nk017A/SQDhBmbxjBUsKIJKhMADKl0K7rGUQIlAw2yxbLbUJ+WH24Nd/QrfxE\nYJEV0368+2AICVUEFvfjQY6Yb14QqqvmrCxHJH2vLL8TNEuxJnp3xRm8GPop\nMjWe5INt7d06hxo3RprGrWyiupMILdZVofsWEzVzFEj5VMGVGF/dK1wlI9BG\nHCaXmYjMjPjUL08RP6HQutXSdY1ybPT7jflrvBKKMIYiEUXxePU7bm1AQZJd\n9pBjW0qWpsHcgFA3qXkm+7L3DlWM9PuMHzpr6YHGyUooCwf4S0bRrpZlY3Of\nQWVgqvyPKHClRiryawX0Qk95A5w5hYFZJTzq4ILdtJSBulrPqj77oexd5bIj\n6SG6O0mY4I6ADuNxjdFYJjFXf8LDycDobqYr+to3cLjGoLsGBDevS4yhlXrL\ng5pxgYb0n8ENyIduAUDLeHA/i7mkNPlUF/sccuKlH7V555sW/0I9tBrpmhPL\nL5Hm908iJP+2zZpvmqGUD/QX479wxxReINwacZ2/6vpTeGV3s5FUHEQCdUja\nKpQI\r\n=zFZJ\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDW6L78UeFJmwOenLG5+LnoKsEjnV8wHW6yvZ48jXl//gIhALJNz/I+ZDWdmTy495tQFddN0PGXBj1EN9Wcvu+eoLKP"}]},"maintainers":[{"name":"airyrooms-engineering","email":"engineering@airyrooms.com"}],"_npmUser":{"name":"airyrooms-engineering","email":"engineering@airyrooms.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/maleo_0.0.13_1553505433676_0.7754030229450894"},"_hasShrinkwrap":false},"0.0.14":{"name":"@airy/maleo","version":"0.0.14","description":"Un-opinionated Universal Rendering Javascript Framework","repository":{"type":"git","url":"git+https://github.com/airyrooms/maleo.js.git","bugs":"https://github.com/airyrooms/maleo.js/issues"},"author":{"name":"Airy"},"license":"MIT","private":false,"sideEffects":false,"scripts":{"build":"rm -rf dist && gulp","watch":"gulp watch","prepublish":"yarn build","start":"node server.js"},"bin":{"maleo":"lib/bin/maleo.js"},"dependencies":{"@babel/core":"7.1.2","@babel/plugin-proposal-class-properties":"7.1.0","@babel/plugin-proposal-decorators":"7.1.2","@babel/plugin-proposal-object-rest-spread":"7.0.0","@babel/plugin-syntax-dynamic-import":"7.0.0","@babel/plugin-transform-runtime":"7.1.0","@babel/preset-env":"7.1.0","@babel/preset-react":"7.0.0","@babel/runtime":"7.1.2","@babel/runtime-corejs2":"7.2.0","babel-loader":"8.0.4","case-sensitive-paths-webpack-plugin":"2.1.2","es6-promise":"4.2.5","eventsource":"^1.0.7","express":"4.16.4","figlet":"1.2.1","friendly-errors-webpack-plugin":"1.7.0","hard-source-webpack-plugin":"0.13.1","helmet":"3.14.0","isomorphic-fetch":"2.2.1","loader-utils":"1.1.0","node-notifier":"5.2.1","nodemon":"1.18.4","react-dom":"16.8.4","react-helmet":"5.2.0","react-loadable":"5.5.0","react-router-config":"5.0.0","react-router-dom":"5.0.0","rimraf":"2.6.3","source-map-support":"0.5.9","terser-webpack-plugin":"1.1.0","webpack":"4.29.0","webpack-bundle-analyzer":"3.0.3","webpack-cli":"3.1.2","webpack-dev-middleware":"3.4.0","webpack-hot-middleware":"2.24.3","webpackbar":"2.6.3"},"peerDependencies":{"react":"^16.8.4"},"devDependencies":{"@babel/preset-typescript":"^7.1.0","@types/webpack-bundle-analyzer":"^2.13.0","babel-plugin-module-resolver":"^3.1.1","gulp":"github:gulpjs/gulp#4.0","gulp-babel":"^8.0.0","gulp-clean":"^0.4.0","gulp-sourcemaps":"^2.6.4","gulp-uglify":"^3.0.1","gulp-watch":"^5.0.1","react":"^16.8.4"},"gitHead":"a1ac0be142870f4f92a26ad44308f24309fe1859","bugs":{"url":"https://github.com/airyrooms/maleo.js/issues"},"homepage":"https://github.com/airyrooms/maleo.js#readme","_id":"@airy/maleo@0.0.14","_nodeVersion":"10.11.0","_npmVersion":"lerna/3.13.1/node@v10.11.0+x64 (darwin)","dist":{"integrity":"sha512-goUWA2H3YWGSdi8a3cLk9Q+HNfuaL/hHknxJP1/XnlAX5vbtX1iNzPhIo2u++fhHiaFKIelwsRvCh3SmI6VTiQ==","shasum":"1c12fa6f8a8e63afb00ada70aeff1687f91d1e41","tarball":"https://registry.npmjs.org/@airy/maleo/-/maleo-0.0.14.tgz","fileCount":83,"unpackedSize":230663,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJcmcaqCRA9TVsSAnZWagAAJhsP/1/0uUnetyUb/SEvcSnP\nyBbTJXPBvnTqT3V0pXJmA52+HBPBOWXaHorFlo6+djZF81I4A78sWmCloytB\nr0js8I7JAYX7vgInOjwhWVBQimZ9Tufk8++Nqyftj7pE+BfPk9pKDKuTUh8G\nyBX1skBqa8j3ermZMohxkQRIBPp9ndZpRhlvNdb8o1KBIfqHu33BDzC+QseS\n1r2BYEPPnPep6wHlWdNKLLQ/3YNEb+y8R4LHdaryfQ4suuZ8AYCIyDAoTw51\nnHOhMl5ZLrotIGWg/K7TkH6zwad4y02tsHedbxqAysWlLWMPWzI5+fFNDKfy\nY7f0Q8acRcTtYkU26U3AYjwIu7+hpNnDHVlfEeSrGWfklDT8F9ckIbhrNVL7\n6Do9crQjVDnwUEiHA0mAHuOnOUjB2JGwees9LaMj3gk7zUvBCFMDy4KnuMPY\nLTfwdAp1fHeet9G7+/MK9pBhl+/9xPD3kcQhONXCaa1YYec4EYFueTbgukpx\nl9gWq5wRKs5Pabez/GSkxaXUzpwMsH+AMLdVZCwBK9kXv1PhiS/zGKO3qdUq\npkPgmNOc2ySOXve955RCWzvuKZZ876cYt/5CgwcbVh1WDfdpMeBhaE2I9nut\nb9+AWgoOtiwdZkkQv+q9EH9RlfT2uGFqq7bffhEF9fJfUHRQLOeb8k0ftb9x\n/Gzx\r\n=FQ9C\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIFu9+cNPck/gbUbNT/FvP41BQGckm/y1/2cAJl9vV4ZvAiEAiES+lkfV9LnGtOqOw+pVAz4p/WCf5Xg4kiQAL6xtBEY="}]},"maintainers":[{"name":"airyrooms-engineering","email":"engineering@airyrooms.com"}],"_npmUser":{"name":"airyrooms-engineering","email":"engineering@airyrooms.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/maleo_0.0.14_1553581737950_0.8491323169367073"},"_hasShrinkwrap":false},"0.0.15-canary.0":{"name":"@airy/maleo","version":"0.0.15-canary.0","description":"Un-opinionated Universal Rendering Javascript Framework","repository":{"type":"git","url":"git+https://github.com/airyrooms/maleo.js.git","bugs":"https://github.com/airyrooms/maleo.js/issues"},"author":{"name":"Airy"},"license":"MIT","private":false,"sideEffects":false,"scripts":{"build":"rm -rf dist && gulp","watch":"gulp watch","prepublish":"yarn build","start":"node server.js"},"bin":{"maleo":"lib/bin/maleo.js"},"dependencies":{"@babel/core":"7.1.2","@babel/plugin-proposal-class-properties":"7.1.0","@babel/plugin-proposal-decorators":"7.1.2","@babel/plugin-proposal-object-rest-spread":"7.0.0","@babel/plugin-syntax-dynamic-import":"7.0.0","@babel/plugin-transform-runtime":"7.1.0","@babel/preset-env":"7.1.0","@babel/preset-react":"7.0.0","@babel/runtime":"7.1.2","@babel/runtime-corejs2":"7.2.0","babel-loader":"8.0.4","case-sensitive-paths-webpack-plugin":"2.1.2","es6-promise":"4.2.5","eventsource":"^1.0.7","express":"4.16.4","figlet":"1.2.1","friendly-errors-webpack-plugin":"1.7.0","hard-source-webpack-plugin":"0.13.1","helmet":"3.14.0","isomorphic-fetch":"2.2.1","loader-utils":"1.1.0","node-notifier":"5.2.1","nodemon":"1.18.4","react-dom":"16.8.4","react-helmet":"5.2.0","react-loadable":"5.5.0","react-router-config":"5.0.0","react-router-dom":"5.0.0","rimraf":"2.6.3","source-map-support":"0.5.9","terser-webpack-plugin":"1.1.0","webpack":"4.29.0","webpack-bundle-analyzer":"3.0.3","webpack-cli":"3.1.2","webpack-dev-middleware":"3.4.0","webpack-hot-middleware":"2.24.3","webpackbar":"2.6.3"},"peerDependencies":{"react":"^16.8.4"},"devDependencies":{"@babel/preset-typescript":"^7.1.0","@types/webpack-bundle-analyzer":"^2.13.0","babel-plugin-module-resolver":"^3.1.1","gulp":"github:gulpjs/gulp#4.0","gulp-babel":"^8.0.0","gulp-clean":"^0.4.0","gulp-sourcemaps":"^2.6.4","gulp-uglify":"^3.0.1","gulp-watch":"^5.0.1","react":"^16.8.4"},"gitHead":"fde23540a1814aeafdf8707d1533ee24f092d6a6","readme":"[![Build Status](https://travis-ci.org/airyrooms/maleo.js.svg?branch=master)](https://travis-ci.org/airyrooms/maleo.js)\n[![Coverage Status](https://coveralls.io/repos/github/airyrooms/maleo.js/badge.svg?branch=master)](https://coveralls.io/github/airyrooms/maleo.js?branch=master)\n[![npm version](https://badge.fury.io/js/%40airy%2Fmaleo.svg)](https://badge.fury.io/js/%40airy%2Fmaleo)\n[![Issues](http://img.shields.io/github/issues/airyrooms/maleo.js.svg)]( https://github.com/airyrooms/maleo.js/issues)\n[![MIT license](https://img.shields.io/badge/License-MIT-blue.svg)](https://github.com/airyrooms/maleo.js/blob/canary/LICENSE)\n[![Discord Chat](https://img.shields.io/discord/550498214789513218.svg)](https://discord.gg/9eArCQn)\n\n\n# Welcome to Maleo.js\n\nMaleo.js is an un-opinionated framework to enable Universal Rendering in JavaScript using React with no hassle.\n\nWe are here to solve the time consuming setups Universal Rendering Required.\n\n---\n\nReadme below is the documentation for the `canary` (prerelease) branch. To view the documentation for the latest stable Maleo.js version change branch to `master`\n\n---\n\n# Table of Contents\n- [Features](#features)\n- [Setup](#setup)\n- [Component Lifecycle](#component-lifecycle)\n- [Routing](#routing)\n- [Dynamic Import Component](#dynamic-import-component)\n  - [Tips](#tips)\n  - [Preloading](#preloading)\n- [Customizable Component](#customizable-component)\n  - [Custom Document](#custom-document)\n  - [Custom Wrap](#custom-wrap)\n- [Custom Configuration](#custom-configuration)\n  - [Customize Server](#customize-server)\n  - [Customize Webpack](#customize-webpack)\n  - [Customize Babel Config](#customize-babel-config)\n- [CDN Support](#cdn-support)\n- [Plugins](#plugins)\n- [FAQ](#faq)\n- [Contributing](#contributing)\n\n---\n\n## Features\n- Universal Rendering\n- Plugin based framework\n- Customizable\n\n## Setup\n\nInstall Maleo.js \n\n>*for now change `@airy/maleo` to `@airy/maleo@canary` until we publish the stable version of this package*\n\n\n**NPM**\n```bash\n$ npm install --save @airy/maleo react\n```\n**Yarn**\n```bash\n$ yarn add @airy/maleo react\n```\n\nAdd this script to your `package.json`\n```json\n{\n  \"scripts\": {\n    \"dev\": \"maleo dev\",\n    \"build\": \"export NODE_ENV=production && maleo build\",\n    \"start\": \"export NODE_ENV=production && node .maleo/server.js\"\n  }\n}\n```\n\nCreate a page Root component\n```jsx\n// ./src/Root.jsx\nimport React from 'react';\n\n// Export default is required for registering page\nexport default class RootComponent extends React.Component {\n  render() {\n    return (\n      <h1>Hello World!</h1>\n    )\n  }\n}\n```\n\nAnd lastly, create a routing file on your project root directory called `routes.json` and register your page component\n```json\n[\n  {\n    \"path\": \"/\",\n    \"page\": \"./src/Root\"\n  }\n]\n```\n\nAfter that you can now run `$ npm run dev` and go to `http://localhost:3000`.\n\nYou should now see your app running on your browser.\n\nBy now you should see \n- Automatic transpilation and bundling (with webpack and babel)\n- ~~Hot code reloading~~ [#17](https://github.com/airyrooms/maleo.js/issues/17)\n- Server rendering\n\nTo see how simple this is, check out the sample app!\n\n## Component Lifecycle \n\nMaleo.js added a new component lifecycle hook called `getInitialProps`, this function is called during Server Side Rendering (SSR).\n\nThis is useful especially for SEO purposes. \n\nExample for stateful component:\n```jsx\nimport React from 'react';\n\nexport default class extends React.Component {\n  static getInitialProps = async (ctx) => {\n    const { req } = ctx;\n\n    const userAgent = req ? req.headers['user-agent'] : navigator.userAgent;\n\n    // the return value will be passed as props for this component\n    return { userAgent };\n  }\n\n  render() {\n    return (\n      <div>\n        Hello World {this.props.userAgent}\n      </div>\n    );\n  }\n}\n```\n\nExample for stateless component:\n```jsx\nconst Component = (props) => (\n  <div>\n    Hello World {props.userAgent}\n  </div>\n);\n\nComponent.getInitialprops = async (ctx) => {\n  const { req } = ctx;\n\n  const userAgent = req ? req.headers['user-agent'] : navigator.userAgent;\n\n  return { userAgent };\n};\n\nexport default Component;\n```\n\n`getInitialProps` receives a context object with the following properties:\n- `req` - HTTP request object (server only)\n- `res` - HTTP response object (server only)\n- `...wrapProps` - Spreaded properties from custom Wrap\n- `...appProps` - Spreaded properties from custom App\n\n## Routing\n\nRouting is declared in a centralized route config.\nRegister all the route config in `routes.json` file.\n\nIf you put the `routes.json` files on root directory, Maleo will automatically register your route. Otherwise put path to your routes on [Maleo config](#custom-configuration).\n\nRoutes file has to export default the route configuration.\nThe route object **expected to have distinct key** to indicate the route.\n\n<table>\n  <tr>\n    <td>Key</td>\n    <td>Type</td>\n    <td>Description</td>\n  </tr>\n  <tr>\n    <td><code>path</code></td>\n    <td><code>String!</code></td>\n    <td>Routes path</td>\n  </tr>\n  <tr>\n    <td><code>page</code></td>\n    <td><code>String!</code></td>\n    <td>Path to React Component for this route path</td>\n  </tr>\n  <tr>\n    <td><code>exact</code></td>\n    <td><code>Boolean?</code> [<code>false</code>]</td>\n    <td>To make url has to match exactly the path in order to render the component. Give <code>false</code> value if the route is a wrapper route component</td>\n  </tr>\n  <tr>\n    <td><code>routes</code></td>\n    <td><code>RouteObject?</code></td>\n    <td>Nested route</td>\n  </tr>\n</table>\n\n\nFor example:\n```json\n[\n  {\n    \"page\": \"./src/MainApp\",\n    \"routes\": [\n      {\n        \"path\": \"/\",\n        \"page\": \"./src/Search\",\n        \"exact\": true\n      },\n      {\n        \"path\": \"/search\",\n        \"page\": \"./src/Search\",\n        \"routes\": [\n          {\n            \"path\": \"/search/hello\",\n            \"page\": \"./src/Detail\",\n            \"exact\": true\n          }\n        ]\n      },\n      {\n        \"path\": \"/detail\",\n        \"page\": \"./src/Detail\",\n        \"exact\": true\n      }\n    ]\n  }\n]\n```\n\n## Dynamic Import Component\nMaleo.js supports TC39 [dynamic import proposal](https://github.com/tc39/proposal-dynamic-import) for JavaScript.\n\nYou can think dynamic import as another way to split your code into manageable chunks. You can use our `Dynamic` function which utilizes [react loadable](https://github.com/jamiebuilds/react-loadable)\n\nFor Example\n```js\n// DynamicComponent.js\nimport Dynamic from '@airy/maleo/dynamic';\n\nexport default Dynamic({\n  loader: () => import( /* webpackChunkName:\"DynamicComponent\" */ './component/DynamicLoad'),\n})\n```\n\n### **Preloading**\n\nFor optimization purposes, you can also preload a component even before the component got rendered.\n\nFor example, if you want to load component when a button get pressed, you can start preloading the component when the user hovers the mouse over the button.\n\nThe component created by `Dynamic` exposes a [static `preload` method](https://github.com/jamiebuilds/react-loadable#loadablecomponentpreload).\n\n```jsx\nimport React from 'react';\nimport Dynamic from '@airy/maleo/dynamic';\n\nconst DynamicBar = Dynamic({\n  loader: () => import('./Bar'),\n  loading: LoadingCompoinent\n});\n\nclass MyComponent extends React.Component {\n  state = { showBar: false };\n\n  onClick = () => {\n    this.setState({ showBar: true });\n  };\n\n  onMouseOver = () => DynamicBar.preload();\n\n  render() {\n    return (\n      <div>\n        <button\n          onClick={this.onClick}\n          onMouseOver={this.onMouseOver}>\n          Show Bar\n        </button>\n        { this.state.showBar && <DynamicBar /> }\n      </div>\n    )\n  }\n}\n```\n\n## Customizable Component\n\n### Custom Document\n\nHighly inspired by what [Next.js](https://github.com/zeit/next.js) has done on their awesome template customization.\n\nMaleo.js also enable customization on `Document` as document's markup. So you don't need to include tags like `<html>`, `<head>`, `<body>`, etc. \n\nTo override the default behavior, you'll need to create a component that extends the `Document` React class provided by Maleo.\n\n```jsx\n// document.jsx\nimport React from 'react';\nimport { Document, Header, Main, Scripts } from '@airy/maleo/document';\n\nexport default class extends Document {\n  render() {\n    return (\n      <html>\n        <Header>\n          <title>Maleo JS</title>\n          <meta charset=\"utf-8\" />\n          <meta name=\"description\" content=\"Maleo.js is awesome!\" />\n\n          <style>\n            {` body { background-color: #fff } `}\n          </style>\n        </Header>\n\n        <body>\n          <Main />\n\n          <Scripts />\n\n          <ReduxScript />\n        </body>\n      </html>\n    );\n  }\n}\n```\n\n### Custom Wrap\n\nMaleo.js uses the `Wrap` component to initialize pages. `Wrap` contains React Router's Component. You can add HoC here to wrap the application and control the page initialization. Which allows you to do amazing things like:\n- Persisting layour between page changes\n- Keeping state when navigating pages\n- Custom error handling using `componentDidCatch`\n- Inject additional data into pages (like Redux provider, etc)\n\nTo override the default behavior, you'll need to create a component that extends the `Wrap` React class provided by Maleo.\n\n```jsx\n// wrap.jsx\nimport React from 'react';\nimport { Wrap } from '@airy/maleo/wrap';\n\n// Redux plugin for Maleo.js\n// Hoc that creates store and add Redux Provider\nimport { withRedux } from '@airy/maleo-redux-plugin';\n\n// Custom Wrapper that will be rendered for the whole Application\nimport CustomWrapper from './component/CustomWrapper';\n\nimport { createStore } from './store';\n\n@withRedux(createStore)\nexport default class extends Wrap {\n  static getInitialProps = (ctx) => {\n    const { store } = ctx\n    // you receive store from context\n    // you can access or do something with the store here\n    console.log('Initialized Redux Store', store);\n    return {}\n  }\n\n  render() {\n    return (\n      <CustomWrapper>\n        {super.render()}\n      </CustomWrapper>\n    )\n  }\n}\n\n```\nIf you put `document.jsx` and `wrap.jsx` on root directory (the same level with `package.json`), then Maleo will automatically register your custom Document and Wrap. Otherwise, you can add the path to your custom Document and Wrap on [Maleo config](#custom-configuration)\n\n---\n***We are also working on adding default and customizable `Error` component page***\n\n---\n\n## Custom Configuration\n\nFor more advanced configuration of Maleo.js, like `webpack` config, registering `plugins`, path to your routes, custom Document and Wrap, and adding `path alias`, you can create a `maleo.config.js` in the root of your project directory. (same directory with `package.json`)\n\n```js\n// maleo.config.js\n\nmodule.exports = {\n  /* config options here */\n}\n```\n\nHere are the API's for the configuration:\n\n<table>\n  <tr>\n    <td>Key</td>\n    <td>Type</td>\n    <td>Description</td>\n  </tr>\n  <tr>\n    <td><code>buildDir</code></td>\n    <td><code>String?</code> [<code>.maleo</code>]</td>\n    <td>Directory to put Maleo.js' build assets</td>\n  </tr>\n  <tr>\n    <td><code>cache</code></td>\n    <td><code>Boolean?</code> [<code>true</code>]</td>\n    <td>Enable webpack build caching</td>\n  </tr>\n  <tr>\n    <td><code>sourceMaps</code></td>\n    <td><code>Boolean?</code> [<code>true</code>]</td>\n    <td>Enable webpack to generate source maps</td>\n  </tr>\n  <tr>\n    <td><code>alias</code></td>\n    <td><code>Object?</code></td>\n    <td>A key value pair for aliasing path directory\n      <br>\n      <code>\n      { 'component': './src/component' }\n      </code>\n    </td>\n  </tr>\n  <tr>\n    <td><code>publicPath</code></td>\n    <td><code>String?</code> [<code>/_assets/</code>]</td>\n    <td>To customize webpack's <code>publicPath</code> \n    <br>Comes in handy if using CDN to put built assets</td>\n  </tr>\n  <tr>\n    <td><code>analyzeBundle</code></td>\n    <td><code>Boolean?</code> [<code>false</code>]</td>\n    <td>To enable webpack's bundle analyzer, for analyzing bundle sizes during bundle debugging should Maleo.js' build process got slow</td>\n  </tr>\n  <tr>\n    <td><code>webpack</code></td>\n    <td><code>Function?</code></td>\n    <td>To customize webpack configuration, more details <a href=\"#customize-webpack\">here</a></td>\n  </tr>\n  <tr>\n    <td><code>routes</code></td>\n    <td><code>string?</code> [<code>rootDir/routes.jsx</code>]</td>\n    <td>Path to your routes file</td>\n  </tr>\n  <tr>\n    <td><code>customDocument</code></td>\n    <td><code>string?</code> [<code>rootDir/document.jsx</code>]</td>\n    <td>Path to your custom document file</td>\n  </tr>\n  <tr>\n    <td><code>customWrap</code></td>\n    <td><code>string?</code> [<code>rootDir/wrap.jsx</code>]</td>\n    <td>Path to your custom wrap file</td>\n  </tr>\n  <tr>\n    <td><code>customApp</code></td>\n    <td><code>string?</code> [<code>rootDir/app.jsx</code>]</td>\n    <td>Path to your custom app file</td>\n  </tr>\n</table>\n\n#### Customize Server\n\nCreate a `server.js` file on root directory where your `package.json` lives.\nHere you can customize Maleo's server.\n```js\nimport { Server } from '@airy/maleo/server';\nimport path from 'path';\n\nimport routeConfig from './routes';\n\nconst PORT = process.env.PORT || 8080;\n\nconst maleoServer = Server.init({\n  port: PORT,\n});\n\nmaleoServer.run(() => {\n  console.log('Server running on port :', PORT);\n});\n```\n\nHere are the API's for the configuration:\n\n<table>\n  <tr>\n    <td>Key</td>\n    <td>Type</td>\n    <td>Description</td>\n  </tr>\n  <tr>\n    <td><code>port</code></td>\n    <td><code>Number?</code> [<code>3000</code>]</td>\n    <td>Port to run Maleo server</td>\n  </tr>\n  <tr>\n    <td><code>assetDir</code></td>\n    <td><code>String?</code> [<code>'<root>/.maleo/client'</code>]</td>\n    <td>Directory for all client related assets</td>\n  </tr>\n</table>\n\n#### Customize Webpack\n\nYou are able to extend Maleo.js' default webpack configuration by defining a function on `maleo.config.js`\n\n```js\n// maleo.config.js\n\nmodule.exports = {\n  webpack(config, context, next) {\n    // Perform customizations to webpack config\n    // Important: This call is required in order to let Maleo pass the config to webpack\n    return next(); \n  },\n};\n```\n\nWebpack function will receive three arguments:\n\n<table>\n  <tr>\n    <td>Argument</td>\n    <td>Details</td>\n  <tr>\n  <tr>\n    <td><code>config</code></td>\n    <td>This contains webpack configuration object that you can manipulate</td>\n  <tr>\n  <tr>\n    <td><code>context</code></td>\n    <td>This contains some keys that are useful for the build context\n      <br>\n      <table>\n        <tr>\n          <td><code>isDev</code></td>\n          <td><code>Boolean</code></td>\n          <td>Check if current build is for development</td>\n        </tr>\n        <tr>\n          <td><code>publicPath</code></td>\n          <td><code>String</code></td>\n          <td>Public Path of user defined or default's value</td>\n        </tr>\n        <tr>\n          <td><code>analyzeBundle</code></td>\n          <td><code>Boolean</code></td>\n          <td>Check if analyze bundle is enabled</td>\n        </tr>\n        <tr>\n          <td><code>buildDirectory</code></td>\n          <td><code>String</code></td>\n          <td>Build Directory of user defined or default's value</td>\n        </tr>\n        <tr>\n          <td><code>name</code></td>\n          <td><code>String</code></td>\n          <td>Build name <code>'server' || 'client'</code></td>\n        </tr>\n      </table>\n    </td>\n  <tr>\n  <tr>\n    <td><code>next</code></td>\n    <td>A callback required to be called to pass the custom configuration</td>\n  <tr>\n</table>\n\nExample of adding `ts-loader` through `maleo.config.js`:\n\n```js\n// maleo.config.js\n\n// Partly taken and modified from @airy/maleo-ts-plugin source\n// for simplicity purposes\n\nmodule.exports = {\n  webpack(config, context, next) {\n    const { isDev } = context\n\n    config.module.rules.push({\n      test: /\\.tsx?/,\n      exclude: /node_modules/,\n      use: [\n        require.resolve('@airy/maleo/lib/build/loaders/maleo-babel-loader'),\n        {\n          loader: 'ts-loader',\n          options: {\n            transpileOnly: true,\n          },\n        },\n      ],\n\n      if (isDev) {\n        config.plugins.push(new ForkTSCheckerWebpackPlugin());\n      }\n\n      return next();\n    })\n  },\n};\n```\n\n#### Customize Babel Config\n\nMaleo.js also let you have your own babel config. Just simply add `.babelrc` file at the root directory of your app.\n\nYou can include Maleo.js' babel preset in order to have latest JavaScript preset.\n\nHere's an example of `.babelrc` file:\n```json\n{\n  \"presets\": [\"@airy/maleo/babel\"],\n  \"plugins\": []\n}\n```\n\nThe `@airy/maleo/babel` preset includes everything you need to get your development started. The preset includes:\n- `@babel/preset-env`\n- `@babel/preset-react`\n- `@babel/plugin-proposal-class-properties`\n- `@babel/plugin-proposal-decorators`\n- `@babel/plugin-proposal-object-rest-spread`\n- `@babel/plugin-transform-runtime`\n- `react-loadable/babel`\n\n## CDN Support\n\nIf you are using a CDN, you can set up the `publicPath` setting and configure your CDN's origin to resolve to the domain that Maleo.js is hosted on.\n\n```js\n// maleo.config.js\n\nconst isProd = process.env.NODE_ENV === 'production';\n\nmodule.exports = {\n  assetPrefix: isProd && 'https://cdn.example.com';\n}\n```\n\n## FAQ\n\n== TO BE DETERMINED == \n\n## Plugins\n- [css-plugin](https://github.com/airyrooms/maleo-plugins/tree/master/packages/css-plugin)\n- [redux-plugin](https://github.com/airyrooms/maleo-plugins/tree/master/packages/redux-plugin)\n- [typescript-plugin](https://github.com/airyrooms/maleo-plugins/tree/master/packages/typescript-plugin)\n- [more](https://github.com/airyrooms/maleo-plugins)\n\n\n## Contributing\n[Please follow these steps to contribute to Maleo.js](https://github.com/airyrooms/maleo.js/blob/canary/CONTRIBUTING.md)\n\n[Please follow these steps to contribute to Maleo.js' plugins](https://github.com/airyrooms/maleo-plugins#contributing-guidelines)\n\n\n## Contributors\n\nThis project exists thanks to all the people who contribute. [Contribute](CONTRIBUTING.md).\n\nMany thanks to our **[contributors](https://github.com/airyrooms/maleo.js/graphs/contributors)**!\n\n\n## License\n\nMIT","readmeFilename":"README.md","bugs":{"url":"https://github.com/airyrooms/maleo.js/issues"},"homepage":"https://github.com/airyrooms/maleo.js#readme","_id":"@airy/maleo@0.0.15-canary.0","_nodeVersion":"10.15.3","_npmVersion":"lerna/3.13.1/node@v10.15.3+x64 (linux)","dist":{"integrity":"sha512-TyJHzdU3P3WblrIo5Aq2JfYeN2QBe+O9Y4N7wpjSdecamHdSqB9cuthQzgDz9NiHSmBUJp8rxpA0nmhudQTNPw==","shasum":"306b59aeb551a3df991bbdbffa14f7902a56bc96","tarball":"https://registry.npmjs.org/@airy/maleo/-/maleo-0.0.15-canary.0.tgz","fileCount":83,"unpackedSize":232045,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJcmfLqCRA9TVsSAnZWagAAYg0QAI3v0Kz1QJqbGJr3FgG2\nLbbs3lB5zy42yuh5I9DZ/TDQXTv9jVhuNEdAesINU/EA4E5Mw/ssufcSJJHR\nagtg/Fk6lRAhlTWky6vTYJioZ72i4SMmgaWrf6ui1Z3D8TsxXwfORscaUdjy\noybmvc1o8zUuxHNT9TMkG+wUIo0vU2zb3Ayp4IIKAszi0pHXxuTyHFP5uJsn\nVWI+CN1Lfi0DDXUOaF1MHNh3zjESDSufAzEUoO/O/rUS58CCtGo4dznvv4LM\ndiJQhifr/YG2OZ6eJ3/pONRruxyO1mXkVz+Ab+Kk9H+iBMpWGJz6F3dTtLz6\nxmKEMkB7y11rM//IcbRIeXwu4TxuqCuj06piueVTOcaYAd0c1YaiJf0p3L4Q\nqj1xYIF1/p47HLihNZvh0Y6EwUWYWEk6wWt0R1D3hLvBASxHn4lj1CFrJWR6\n6GckWcDVL5s73DGwbiIQS+4gRNMNgSCv0uSxfHzLgokn+CP6Y3rQLqHv2P6C\n2QkErzDXU7E1kDSZraF0dmPFvJHXMHbF8RJeKcs6/SREVHwsGjhpj/sUaFz4\ndFn2pl7lDgTwbOea0Ve/Hs6IMN7oUu0amQ8LAu8Xl+33l4JcAmQqwvpkBDTd\nd62J/+XQn7r3ag3revYK38sNJWyEs7FT+iO8sd1+dXQ1l3ow5MewxGc/BGyM\ni/vW\r\n=0yw+\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDvSjKsMQvX+gXMql8J308lzNglD5D0oEOFOLQoIdyjPgIgRO4lb8kPyt00b68TbiJbsNP9pIpl4iwAAyULW+tVYRo="}]},"maintainers":[{"name":"airyrooms-engineering","email":"engineering@airyrooms.com"}],"_npmUser":{"name":"airyrooms-engineering","email":"engineering@airyrooms.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/maleo_0.0.15-canary.0_1553593066275_0.89521355370436"},"_hasShrinkwrap":false},"0.0.15":{"name":"@airy/maleo","version":"0.0.15","description":"Un-opinionated Universal Rendering Javascript Framework","repository":{"type":"git","url":"git+https://github.com/airyrooms/maleo.js.git","bugs":"https://github.com/airyrooms/maleo.js/issues"},"author":{"name":"Airy"},"license":"MIT","private":false,"sideEffects":false,"scripts":{"build":"rm -rf dist && gulp","watch":"gulp watch","prepublish":"yarn build","start":"node server.js"},"bin":{"maleo":"lib/bin/maleo.js"},"dependencies":{"@babel/core":"7.1.2","@babel/plugin-proposal-class-properties":"7.1.0","@babel/plugin-proposal-decorators":"7.1.2","@babel/plugin-proposal-object-rest-spread":"7.0.0","@babel/plugin-syntax-dynamic-import":"7.0.0","@babel/plugin-transform-runtime":"7.1.0","@babel/preset-env":"7.1.0","@babel/preset-react":"7.0.0","@babel/runtime":"7.1.2","@babel/runtime-corejs2":"7.2.0","babel-loader":"8.0.4","case-sensitive-paths-webpack-plugin":"2.1.2","es6-promise":"4.2.5","eventsource":"^1.0.7","express":"4.16.4","figlet":"1.2.1","friendly-errors-webpack-plugin":"1.7.0","hard-source-webpack-plugin":"0.13.1","helmet":"3.14.0","isomorphic-fetch":"2.2.1","loader-utils":"1.1.0","node-notifier":"5.2.1","nodemon":"1.18.4","react-dom":"16.8.4","react-helmet":"5.2.0","react-loadable":"5.5.0","react-router-config":"5.0.0","react-router-dom":"5.0.0","rimraf":"2.6.3","source-map-support":"0.5.9","terser-webpack-plugin":"1.1.0","webpack":"4.29.0","webpack-bundle-analyzer":"3.0.3","webpack-cli":"3.1.2","webpack-dev-middleware":"3.4.0","webpack-hot-middleware":"2.24.3","webpackbar":"2.6.3"},"peerDependencies":{"react":"^16.8.4"},"devDependencies":{"@babel/preset-typescript":"^7.1.0","@types/webpack-bundle-analyzer":"^2.13.0","babel-plugin-module-resolver":"^3.1.1","gulp":"github:gulpjs/gulp#4.0","gulp-babel":"^8.0.0","gulp-clean":"^0.4.0","gulp-sourcemaps":"^2.6.4","gulp-uglify":"^3.0.1","gulp-watch":"^5.0.1","react":"^16.8.4"},"gitHead":"4411b6ed559d3be5801f8a3bb1aedee7739b09ad","bugs":{"url":"https://github.com/airyrooms/maleo.js/issues"},"homepage":"https://github.com/airyrooms/maleo.js#readme","_id":"@airy/maleo@0.0.15","_nodeVersion":"10.11.0","_npmVersion":"lerna/3.13.1/node@v10.11.0+x64 (darwin)","dist":{"integrity":"sha512-o64WCPB76Bv0R3QNvkjaDDsgrGz2mbv6TLPM7tJOWz/W6aaRoIILXQz4saEmroi+Sxxz0PtdaKoZtbx78xfXsg==","shasum":"fcf2cf76adfe1f3c5b45e09b503c7a1d75d5d41e","tarball":"https://registry.npmjs.org/@airy/maleo/-/maleo-0.0.15.tgz","fileCount":83,"unpackedSize":235155,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJcmyimCRA9TVsSAnZWagAA71QP/3+I0mP/5ncWnV+ozQXV\nsD3pZripK5iaCOxr6pL2AuhKbhwcY89G0NEMZAjauMRI1XwkniX531paDsu+\nYogidADVTZz6SUm1RkhXKNAB5Ju5agmEAvfKfqNnmIRuF2OJgsLQ815cXZGD\npw59kNtEQozco1BIw31owMKgiwigd7kry971tha7aV1fs1bHVoJIaEkiGNIk\nE97o0TvjZo8+p8sxVu3i7jDrMRQKqcrUIs9cb94Z3i8M0IznClx9xjEOIS6h\nJGwyuVKadAr6NUxtFSm6SpbMzZrJEZPLpSZvr2GlBdDK9XD2AUG+Ro5qa0+K\nGv4k6u9qaeknyHwa3vaDYsUqIAO7dIwiS+pvrhjGuGbK1jmxcfU9RRNWL6jq\nCbc/ttaIJmSwG0nfnV8whNQHHhON8s0V9NOopuRmKf2709NkVle8CECu1SvM\na6NZM8ZkqU5oA+yzH+vezDOd18haynI2AMX5JA7Q9sxPxDA8lGbIyqrNKT1h\nRyL7S8oUcL7kUsPXIoUTD4teOc41YG2W3huZpLg1iPinimhKGMEu22HmiogL\nxnOAxo9nxcICZuGdn5bAPYjsTRrNKnEv3WBa9gVYvrGFnVCSQ1rnR8dUJPkz\nbBWqVytYrt9UcsaKKGQg2n+iZ64fNrf81kg+5dTI4tKMeq6YbIxFPUdUvExw\nGuY+\r\n=jGN6\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIAh9XKVFlguzcyBm1q+y5sH9plEWKW4fA3ahwi2xJNkIAiBpaItdQ5Sblwg9iMP26zwP2ypgcfaL0C65b4FChK0xBw=="}]},"maintainers":[{"name":"airyrooms-engineering","email":"engineering@airyrooms.com"}],"_npmUser":{"name":"airyrooms-engineering","email":"engineering@airyrooms.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/maleo_0.0.15_1553672357302_0.9775878858111886"},"_hasShrinkwrap":false},"0.1.0":{"name":"@airy/maleo","version":"0.1.0","description":"Un-opinionated Universal Rendering Javascript Framework","repository":{"type":"git","url":"git+https://github.com/airyrooms/maleo.js.git","bugs":"https://github.com/airyrooms/maleo.js/issues"},"author":{"name":"Airy"},"license":"MIT","private":false,"sideEffects":false,"scripts":{"build":"rm -rf dist && gulp","watch":"gulp watch","prepublish":"yarn build","start":"node server.js"},"bin":{"maleo":"lib/bin/maleo.js"},"dependencies":{"@babel/core":"7.1.2","@babel/plugin-proposal-class-properties":"7.1.0","@babel/plugin-proposal-decorators":"7.1.2","@babel/plugin-proposal-object-rest-spread":"7.0.0","@babel/plugin-syntax-dynamic-import":"7.0.0","@babel/plugin-transform-runtime":"7.1.0","@babel/preset-env":"7.1.0","@babel/preset-react":"7.0.0","@babel/runtime":"7.1.2","@babel/runtime-corejs2":"7.2.0","babel-loader":"8.0.4","case-sensitive-paths-webpack-plugin":"2.1.2","compression":"1.7.4","es6-promise":"4.2.5","eventsource":"1.0.7","express":"4.16.4","figlet":"1.2.1","friendly-errors-webpack-plugin":"1.7.0","hard-source-webpack-plugin":"0.13.1","helmet":"3.14.0","isomorphic-fetch":"2.2.1","loader-utils":"1.1.0","node-notifier":"5.2.1","nodemon":"1.18.4","react-dom":"16.8.4","react-helmet":"5.2.0","react-loadable":"5.5.0","react-router-config":"5.0.0","react-router-dom":"5.0.0","rimraf":"2.6.3","source-map-support":"0.5.9","terser-webpack-plugin":"1.1.0","webpack":"4.29.0","webpack-bundle-analyzer":"3.0.3","webpack-cli":"3.1.2","webpack-dev-middleware":"3.4.0","webpack-hot-middleware":"2.24.3","webpackbar":"2.6.3"},"peerDependencies":{"react":"^16.8.4"},"devDependencies":{"@babel/preset-typescript":"^7.1.0","@types/webpack-bundle-analyzer":"^2.13.0","babel-plugin-module-resolver":"^3.1.1","gulp":"github:gulpjs/gulp#4.0","gulp-babel":"^8.0.0","gulp-clean":"^0.4.0","gulp-sourcemaps":"^2.6.4","gulp-uglify":"^3.0.1","gulp-watch":"^5.0.1","react":"^16.8.4"},"gitHead":"b71686c7a03a3ff45642463bc3ab0932f1689122","bugs":{"url":"https://github.com/airyrooms/maleo.js/issues"},"homepage":"https://github.com/airyrooms/maleo.js#readme","_id":"@airy/maleo@0.1.0","_nodeVersion":"10.11.0","_npmVersion":"lerna/3.13.1/node@v10.11.0+x64 (darwin)","dist":{"integrity":"sha512-MCEWcmJd2TuDYFg9ITJ20dW2UBdYExVaZvXYyUyqJG0IIK52QwO0wsms5oU80ROYDikoajs7UO4lJOsACebUHg==","shasum":"a83a5fee2ba3306d70f647a4232753fcca899bea","tarball":"https://registry.npmjs.org/@airy/maleo/-/maleo-0.1.0.tgz","fileCount":83,"unpackedSize":238621,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJcnF/gCRA9TVsSAnZWagAAK60P/3sZfsOzLmJ6RoG9s0+I\nXoeCuiNnXZ7fDulBLUKSejfhbqz6oCHT/3PFnBXeg3chDd1Jj9/D3m8LkC7C\neS16NIXc2PDlipphynUkAGZBY57PE2loPXMQ7aPKOhmg7NyQnOJYtEGUB9kG\n8wYy9rrAhGnsWYEfyqw7GpBbZ0+IfVtvp+ioxyobMtf9koeFLyFjrLbTzFzZ\n1XJf0QUWx5ZWY71nwz0PlId06wvIV9XUOMGeMKe8L1F6rk0cKO9MgKyLdq76\ncusSWqGLMjBYquYzIcPFi2GbadhcnY5sqsVU13cN2lcqH1+/Nr6Ida28st98\nvD9DXO1cWI4zP6NiwiEjyqryGqE0Yj2SOLkOULeNOf3G/kilc6gEFD475Mqf\n3FnuH07Lj+Pwohaxvg0lkvMcxBA5o3O4c+HhWvF5rBJiwRddUK8LZ0sbV+rg\nwArV+PZz2vwM6BuetbiNfOBXeviRPpYGSy08tejf+odJlTMrcdmoJ+uq8SUr\nd+0bKTnzakIGVjqlO3LYbMdfNO63vVW6vMhIg3D3R3sgcu73IJ8zVz7HpjiW\n9lrwen1vn9VDd7ioCmKpSK+fh9vViIvP6lLgE+7R3KSqP/nH234latA2bR6H\nRHr5wHVBbAiwpbilpEbBIaIpS+q4WWnHAxBpuQ14QtxZMq2QFn2XCg/T+nCY\nQjBp\r\n=O/y/\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIFCAVPwO7tnqaIzGd/2a7MSZEKQRj38cNRM1stzqE1KuAiEAkw7yOyQNRrlti4BSYH4Gu2nGxnXhgnE3/cz+7DmnPQc="}]},"maintainers":[{"name":"airyrooms-engineering","email":"engineering@airyrooms.com"}],"_npmUser":{"name":"airyrooms-engineering","email":"engineering@airyrooms.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/maleo_0.1.0_1553752031158_0.29098678017896473"},"_hasShrinkwrap":false},"0.1.1-canary.0":{"name":"@airy/maleo","version":"0.1.1-canary.0","description":"Un-opinionated Universal Rendering Javascript Framework","repository":{"type":"git","url":"git+https://github.com/airyrooms/maleo.js.git","bugs":"https://github.com/airyrooms/maleo.js/issues"},"author":{"name":"Airy"},"license":"MIT","private":false,"sideEffects":false,"scripts":{"build":"rm -rf dist && gulp","watch":"gulp watch","prepublish":"yarn build","start":"node server.js"},"bin":{"maleo":"lib/bin/maleo.js"},"dependencies":{"@babel/core":"7.1.2","@babel/plugin-proposal-class-properties":"7.1.0","@babel/plugin-proposal-decorators":"7.1.2","@babel/plugin-proposal-object-rest-spread":"7.0.0","@babel/plugin-syntax-dynamic-import":"7.0.0","@babel/plugin-transform-runtime":"7.1.0","@babel/preset-env":"7.1.0","@babel/preset-react":"7.0.0","@babel/runtime":"7.1.2","@babel/runtime-corejs2":"7.2.0","babel-loader":"8.0.4","case-sensitive-paths-webpack-plugin":"2.1.2","compression":"1.7.4","es6-promise":"4.2.5","eventsource":"1.0.7","express":"4.16.4","figlet":"1.2.1","friendly-errors-webpack-plugin":"1.7.0","hard-source-webpack-plugin":"0.13.1","helmet":"3.14.0","isomorphic-fetch":"2.2.1","loader-utils":"1.1.0","node-notifier":"5.2.1","nodemon":"1.18.4","react-dom":"16.8.4","react-helmet":"5.2.0","react-loadable":"5.5.0","react-router-config":"5.0.0","react-router-dom":"5.0.0","rimraf":"2.6.3","source-map-support":"0.5.9","terser-webpack-plugin":"1.1.0","webpack":"4.29.0","webpack-bundle-analyzer":"3.0.3","webpack-cli":"3.1.2","webpack-dev-middleware":"3.4.0","webpack-hot-middleware":"2.24.3","webpackbar":"2.6.3"},"peerDependencies":{"react":"^16.8.4"},"devDependencies":{"@babel/preset-typescript":"^7.1.0","@types/webpack-bundle-analyzer":"^2.13.0","babel-plugin-module-resolver":"^3.1.1","gulp":"github:gulpjs/gulp#4.0","gulp-babel":"^8.0.0","gulp-clean":"^0.4.0","gulp-sourcemaps":"^2.6.4","gulp-uglify":"^3.0.1","gulp-watch":"^5.0.1","react":"^16.8.4"},"gitHead":"14d0c181880c5d917aaf685cd58a3238cd62c3a7","readme":"[![Build Status](https://travis-ci.org/airyrooms/maleo.js.svg?branch=master)](https://travis-ci.org/airyrooms/maleo.js)\n[![Coverage Status](https://coveralls.io/repos/github/airyrooms/maleo.js/badge.svg?branch=master)](https://coveralls.io/github/airyrooms/maleo.js?branch=master)\n[![npm version](https://badge.fury.io/js/%40airy%2Fmaleo.svg)](https://badge.fury.io/js/%40airy%2Fmaleo)\n[![Issues](http://img.shields.io/github/issues/airyrooms/maleo.js.svg)]( https://github.com/airyrooms/maleo.js/issues)\n[![MIT license](https://img.shields.io/badge/License-MIT-blue.svg)](https://github.com/airyrooms/maleo.js/blob/canary/LICENSE)\n[![Discord Chat](https://img.shields.io/discord/550498214789513218.svg)](https://discord.gg/9eArCQn)\n\n\n# Welcome to Maleo.js\n\nMaleo.js is an un-opinionated framework to enable Universal Rendering in JavaScript using React with no hassle.\n\nWe are here to solve the time consuming setups Universal Rendering Required.\n\n---\n\nReadme below is the documentation for the `canary` (prerelease) branch. To view the documentation for the latest stable Maleo.js version change branch to `master`\n\n---\n\n# Table of Contents\n- [Features](#features)\n- [Setup](#setup)\n- [Component Lifecycle](#component-lifecycle)\n- [Routing](#routing)\n- [Dynamic Import Component](#dynamic-import-component)\n  - [Tips](#tips)\n  - [Preloading](#preloading)\n- [Customizable Component](#customizable-component)\n  - [Custom Document](#custom-document)\n  - [Custom Wrap](#custom-wrap)\n- [Custom Configuration](#custom-configuration)\n  - [Customize Server](#customize-server)\n  - [Customize Webpack](#customize-webpack)\n  - [Customize Babel Config](#customize-babel-config)\n- [CDN Support](#cdn-support)\n- [Plugins](#plugins)\n- [FAQ](#faq)\n- [Contributing](#contributing)\n\n---\n\n## Features\n- Universal Rendering\n- Plugin based framework\n- Customizable\n\n## Setup\n\nInstall Maleo.js \n\n>*for now change `@airy/maleo` to `@airy/maleo@canary` until we publish the stable version of this package*\n\n\n**NPM**\n```bash\n$ npm install --save @airy/maleo react\n```\n**Yarn**\n```bash\n$ yarn add @airy/maleo react\n```\n\nAdd this script to your `package.json`\n```json\n{\n  \"scripts\": {\n    \"dev\": \"maleo dev\",\n    \"build\": \"export NODE_ENV=production && maleo build\",\n    \"start\": \"export NODE_ENV=production && node .maleo/server.js\"\n  }\n}\n```\n\nCreate a page Root component\n```jsx\n// ./src/Root.jsx\nimport React from 'react';\n\n// Export default is required for registering page\nexport default class RootComponent extends React.Component {\n  render() {\n    return (\n      <h1>Hello World!</h1>\n    )\n  }\n}\n```\n\nAnd lastly, create a routing file on your project root directory called `routes.json` and register your page component\n```json\n[\n  {\n    \"path\": \"/\",\n    \"page\": \"./src/Root\"\n  }\n]\n```\n\nAfter that you can now run `$ npm run dev` and go to `http://localhost:3000`.\n\nYou should now see your app running on your browser.\n\nBy now you should see \n- Automatic transpilation and bundling (with webpack and babel)\n- ~~Hot code reloading~~ [#17](https://github.com/airyrooms/maleo.js/issues/17)\n- Server rendering\n\nTo see how simple this is, check out the sample app!\n\n## Component Lifecycle \n\nMaleo.js added a new component lifecycle hook called `getInitialProps`, this function is called during Server Side Rendering (SSR).\n\nThis is useful especially for SEO purposes. \n\nExample for stateful component:\n```jsx\nimport React from 'react';\n\nexport default class extends React.Component {\n  static getInitialProps = async (ctx) => {\n    const { req } = ctx;\n\n    const userAgent = req ? req.headers['user-agent'] : navigator.userAgent;\n\n    // the return value will be passed as props for this component\n    return { userAgent };\n  }\n\n  render() {\n    return (\n      <div>\n        Hello World {this.props.userAgent}\n      </div>\n    );\n  }\n}\n```\n\nExample for stateless component:\n```jsx\nconst Component = (props) => (\n  <div>\n    Hello World {props.userAgent}\n  </div>\n);\n\nComponent.getInitialprops = async (ctx) => {\n  const { req } = ctx;\n\n  const userAgent = req ? req.headers['user-agent'] : navigator.userAgent;\n\n  return { userAgent };\n};\n\nexport default Component;\n```\n\n`getInitialProps` receives a context object with the following properties:\n- `req` - HTTP request object (server only)\n- `res` - HTTP response object (server only)\n- `...wrapProps` - Spreaded properties from custom Wrap\n- `...appProps` - Spreaded properties from custom App\n\n## Routing\n\nRouting is declared in a centralized route config.\nRegister all the route config in `routes.json` file.\n\nIf you put the `routes.json` files on root directory, Maleo will automatically register your route. Otherwise put path to your routes on [Maleo config](#custom-configuration).\n\nRoutes file has to export default the route configuration.\nThe route object **expected to have distinct key** to indicate the route.\n\n<table>\n  <tr>\n    <td>Key</td>\n    <td>Type</td>\n    <td>Description</td>\n  </tr>\n  <tr>\n    <td><code>path</code></td>\n    <td><code>String!</code></td>\n    <td>Routes path</td>\n  </tr>\n  <tr>\n    <td><code>page</code></td>\n    <td><code>String!</code></td>\n    <td>Path to React Component for this route path</td>\n  </tr>\n  <tr>\n    <td><code>exact</code></td>\n    <td><code>Boolean?</code> [<code>false</code>]</td>\n    <td>To make url has to match exactly the path in order to render the component. Give <code>false</code> value if the route is a wrapper route component</td>\n  </tr>\n  <tr>\n    <td><code>routes</code></td>\n    <td><code>RouteObject?</code></td>\n    <td>Nested route</td>\n  </tr>\n</table>\n\n\nFor example:\n```json\n[\n  {\n    \"page\": \"./src/MainApp\",\n    \"routes\": [\n      {\n        \"path\": \"/\",\n        \"page\": \"./src/Search\",\n        \"exact\": true\n      },\n      {\n        \"path\": \"/search\",\n        \"page\": \"./src/Search\",\n        \"routes\": [\n          {\n            \"path\": \"/search/hello\",\n            \"page\": \"./src/Detail\",\n            \"exact\": true\n          }\n        ]\n      },\n      {\n        \"path\": \"/detail\",\n        \"page\": \"./src/Detail\",\n        \"exact\": true\n      }\n    ]\n  }\n]\n```\n\n## Dynamic Import Component\nMaleo.js supports TC39 [dynamic import proposal](https://github.com/tc39/proposal-dynamic-import) for JavaScript.\n\nYou can think dynamic import as another way to split your code into manageable chunks. You can use our `Dynamic` function which utilizes [react loadable](https://github.com/jamiebuilds/react-loadable)\n\nFor Example\n```js\n// DynamicComponent.js\nimport Dynamic from '@airy/maleo/dynamic';\n\nexport default Dynamic({\n  loader: () => import( /* webpackChunkName:\"DynamicComponent\" */ './component/DynamicLoad'),\n})\n```\n\n### **Preloading**\n\nFor optimization purposes, you can also preload a component even before the component got rendered.\n\nFor example, if you want to load component when a button get pressed, you can start preloading the component when the user hovers the mouse over the button.\n\nThe component created by `Dynamic` exposes a [static `preload` method](https://github.com/jamiebuilds/react-loadable#loadablecomponentpreload).\n\n```jsx\nimport React from 'react';\nimport Dynamic from '@airy/maleo/dynamic';\n\nconst DynamicBar = Dynamic({\n  loader: () => import('./Bar'),\n  loading: LoadingCompoinent\n});\n\nclass MyComponent extends React.Component {\n  state = { showBar: false };\n\n  onClick = () => {\n    this.setState({ showBar: true });\n  };\n\n  onMouseOver = () => DynamicBar.preload();\n\n  render() {\n    return (\n      <div>\n        <button\n          onClick={this.onClick}\n          onMouseOver={this.onMouseOver}>\n          Show Bar\n        </button>\n        { this.state.showBar && <DynamicBar /> }\n      </div>\n    )\n  }\n}\n```\n\n## Customizable Component\n\n### Custom Document\n\nHighly inspired by what [Next.js](https://github.com/zeit/next.js) has done on their awesome template customization.\n\nMaleo.js also enable customization on `Document` as document's markup. So you don't need to include tags like `<html>`, `<head>`, `<body>`, etc. \n\nTo override the default behavior, you'll need to create a component that extends the `Document` React class provided by Maleo.\n\n```jsx\n// document.jsx\nimport React from 'react';\nimport { Document, Header, Main, Scripts } from '@airy/maleo/document';\n\nexport default class extends Document {\n  render() {\n    return (\n      <html>\n        <Header>\n          <title>Maleo JS</title>\n          <meta charset=\"utf-8\" />\n          <meta name=\"description\" content=\"Maleo.js is awesome!\" />\n\n          <style>\n            {` body { background-color: #fff } `}\n          </style>\n        </Header>\n\n        <body>\n          <Main />\n\n          <Scripts />\n\n          <ReduxScript />\n        </body>\n      </html>\n    );\n  }\n}\n```\n\n### Custom Wrap\n\nMaleo.js uses the `Wrap` component to initialize pages. `Wrap` contains React Router's Component. You can add HoC here to wrap the application and control the page initialization. Which allows you to do amazing things like:\n- Persisting layour between page changes\n- Keeping state when navigating pages\n- Custom error handling using `componentDidCatch`\n- Inject additional data into pages (like Redux provider, etc)\n\nTo override the default behavior, you'll need to create a component that extends the `Wrap` React class provided by Maleo.\n\n```jsx\n// wrap.jsx\nimport React from 'react';\nimport { Wrap } from '@airy/maleo/wrap';\n\n// Redux plugin for Maleo.js\n// Hoc that creates store and add Redux Provider\nimport { withRedux } from '@airy/maleo-redux-plugin';\n\n// Custom Wrapper that will be rendered for the whole Application\nimport CustomWrapper from './component/CustomWrapper';\n\nimport { createStore } from './store';\n\n@withRedux(createStore)\nexport default class extends Wrap {\n  static getInitialProps = (ctx) => {\n    const { store } = ctx\n    // you receive store from context\n    // you can access or do something with the store here\n    console.log('Initialized Redux Store', store);\n    return {}\n  }\n\n  render() {\n    return (\n      <CustomWrapper>\n        {super.render()}\n      </CustomWrapper>\n    )\n  }\n}\n\n```\nIf you put `document.jsx` and `wrap.jsx` on root directory (the same level with `package.json`), then Maleo will automatically register your custom Document and Wrap. Otherwise, you can add the path to your custom Document and Wrap on [Maleo config](#custom-configuration)\n\n---\n***We are also working on adding default and customizable `Error` component page***\n\n---\n\n## Custom Configuration\n\nFor more advanced configuration of Maleo.js, like `webpack` config, registering `plugins`, path to your routes, custom Document and Wrap, and adding `path alias`, you can create a `maleo.config.js` in the root of your project directory. (same directory with `package.json`)\n\n```js\n// maleo.config.js\n\nmodule.exports = {\n  /* config options here */\n}\n```\n\nHere are the API's for the configuration:\n\n<table>\n  <tr>\n    <td>Key</td>\n    <td>Type</td>\n    <td>Description</td>\n  </tr>\n  <tr>\n    <td><code>buildDir</code></td>\n    <td><code>String?</code> [<code>.maleo</code>]</td>\n    <td>Directory to put Maleo.js' build assets</td>\n  </tr>\n  <tr>\n    <td><code>cache</code></td>\n    <td><code>Boolean?</code> [<code>true</code>]</td>\n    <td>Enable webpack build caching</td>\n  </tr>\n  <tr>\n    <td><code>sourceMaps</code></td>\n    <td><code>Boolean?</code> [<code>true</code>]</td>\n    <td>Enable webpack to generate source maps</td>\n  </tr>\n  <tr>\n    <td><code>alias</code></td>\n    <td><code>Object?</code></td>\n    <td>A key value pair for aliasing path directory\n      <br>\n      <code>\n      { 'component': './src/component' }\n      </code>\n    </td>\n  </tr>\n  <tr>\n    <td><code>publicPath</code></td>\n    <td><code>String?</code> [<code>/_assets/</code>]</td>\n    <td>To customize webpack's <code>publicPath</code> \n    <br>Comes in handy if using CDN to put built assets</td>\n  </tr>\n  <tr>\n    <td><code>analyzeBundle</code></td>\n    <td><code>Boolean?</code> [<code>false</code>]</td>\n    <td>To enable webpack's bundle analyzer, for analyzing bundle sizes during bundle debugging should Maleo.js' build process got slow</td>\n  </tr>\n  <tr>\n    <td><code>webpack</code></td>\n    <td><code>Function?</code></td>\n    <td>To customize webpack configuration, more details <a href=\"#customize-webpack\">here</a></td>\n  </tr>\n  <tr>\n    <td><code>routes</code></td>\n    <td><code>string?</code> [<code>rootDir/routes.jsx</code>]</td>\n    <td>Path to your routes file</td>\n  </tr>\n  <tr>\n    <td><code>customDocument</code></td>\n    <td><code>string?</code> [<code>rootDir/document.jsx</code>]</td>\n    <td>Path to your custom document file</td>\n  </tr>\n  <tr>\n    <td><code>customWrap</code></td>\n    <td><code>string?</code> [<code>rootDir/wrap.jsx</code>]</td>\n    <td>Path to your custom wrap file</td>\n  </tr>\n  <tr>\n    <td><code>customApp</code></td>\n    <td><code>string?</code> [<code>rootDir/app.jsx</code>]</td>\n    <td>Path to your custom app file</td>\n  </tr>\n</table>\n\n#### Customize Server\n\nCreate a `server.js` file on root directory where your `package.json` lives.\nHere you can customize Maleo's server.\n```js\nimport { Server } from '@airy/maleo/server';\nimport path from 'path';\n\nimport routeConfig from './routes';\n\nconst PORT = process.env.PORT || 8080;\n\nconst maleoServer = Server.init({\n  port: PORT,\n});\n\nmaleoServer.run(() => {\n  console.log('Server running on port :', PORT);\n});\n```\n\nHere are the API's for the configuration:\n\n<table>\n  <tr>\n    <td>Key</td>\n    <td>Type</td>\n    <td>Description</td>\n  </tr>\n  <tr>\n    <td><code>port</code></td>\n    <td><code>Number?</code> [<code>3000</code>]</td>\n    <td>Port to run Maleo server</td>\n  </tr>\n  <tr>\n    <td><code>assetDir</code></td>\n    <td><code>String?</code> [<code>'<root>/.maleo/client'</code>]</td>\n    <td>Directory for all client related assets</td>\n  </tr>\n</table>\n\n#### Customize Webpack\n\nYou are able to extend Maleo.js' default webpack configuration by defining a function on `maleo.config.js`\n\n```js\n// maleo.config.js\n\nmodule.exports = {\n  webpack(config, context, next) {\n    // Perform customizations to webpack config\n    // Important: This call is required in order to let Maleo pass the config to webpack\n    return next(); \n  },\n};\n```\n\nWebpack function will receive three arguments:\n\n<table>\n  <tr>\n    <td>Argument</td>\n    <td>Details</td>\n  <tr>\n  <tr>\n    <td><code>config</code></td>\n    <td>This contains webpack configuration object that you can manipulate</td>\n  <tr>\n  <tr>\n    <td><code>context</code></td>\n    <td>This contains some keys that are useful for the build context\n      <br>\n      <table>\n        <tr>\n          <td><code>isDev</code></td>\n          <td><code>Boolean</code></td>\n          <td>Check if current build is for development</td>\n        </tr>\n        <tr>\n          <td><code>publicPath</code></td>\n          <td><code>String</code></td>\n          <td>Public Path of user defined or default's value</td>\n        </tr>\n        <tr>\n          <td><code>analyzeBundle</code></td>\n          <td><code>Boolean</code></td>\n          <td>Check if analyze bundle is enabled</td>\n        </tr>\n        <tr>\n          <td><code>buildDirectory</code></td>\n          <td><code>String</code></td>\n          <td>Build Directory of user defined or default's value</td>\n        </tr>\n        <tr>\n          <td><code>name</code></td>\n          <td><code>String</code></td>\n          <td>Build name <code>'server' || 'client'</code></td>\n        </tr>\n      </table>\n    </td>\n  <tr>\n  <tr>\n    <td><code>next</code></td>\n    <td>A callback required to be called to pass the custom configuration</td>\n  <tr>\n</table>\n\nExample of adding `ts-loader` through `maleo.config.js`:\n\n```js\n// maleo.config.js\n\n// Partly taken and modified from @airy/maleo-ts-plugin source\n// for simplicity purposes\n\nmodule.exports = {\n  webpack(config, context, next) {\n    const { isDev } = context\n\n    config.module.rules.push({\n      test: /\\.tsx?/,\n      exclude: /node_modules/,\n      use: [\n        require.resolve('@airy/maleo/lib/build/loaders/maleo-babel-loader'),\n        {\n          loader: 'ts-loader',\n          options: {\n            transpileOnly: true,\n          },\n        },\n      ],\n\n      if (isDev) {\n        config.plugins.push(new ForkTSCheckerWebpackPlugin());\n      }\n\n      return next();\n    })\n  },\n};\n```\n\n#### Customize Babel Config\n\nMaleo.js also let you have your own babel config. Just simply add `.babelrc` file at the root directory of your app.\n\nYou can include Maleo.js' babel preset in order to have latest JavaScript preset.\n\nHere's an example of `.babelrc` file:\n```json\n{\n  \"presets\": [\"@airy/maleo/babel\"],\n  \"plugins\": []\n}\n```\n\nThe `@airy/maleo/babel` preset includes everything you need to get your development started. The preset includes:\n- `@babel/preset-env`\n- `@babel/preset-react`\n- `@babel/plugin-proposal-class-properties`\n- `@babel/plugin-proposal-decorators`\n- `@babel/plugin-proposal-object-rest-spread`\n- `@babel/plugin-transform-runtime`\n- `react-loadable/babel`\n\n## CDN Support\n\nIf you are using a CDN, you can set up the `publicPath` setting and configure your CDN's origin to resolve to the domain that Maleo.js is hosted on.\n\n```js\n// maleo.config.js\n\nconst isProd = process.env.NODE_ENV === 'production';\n\nmodule.exports = {\n  assetPrefix: isProd && 'https://cdn.example.com';\n}\n```\n\n## FAQ\n\n== TO BE DETERMINED == \n\n## Plugins\n- [css-plugin](https://github.com/airyrooms/maleo-plugins/tree/master/packages/css-plugin)\n- [redux-plugin](https://github.com/airyrooms/maleo-plugins/tree/master/packages/redux-plugin)\n- [typescript-plugin](https://github.com/airyrooms/maleo-plugins/tree/master/packages/typescript-plugin)\n- [more](https://github.com/airyrooms/maleo-plugins)\n\n\n## Contributing\n[Please follow these steps to contribute to Maleo.js](https://github.com/airyrooms/maleo.js/blob/canary/CONTRIBUTING.md)\n\n[Please follow these steps to contribute to Maleo.js' plugins](https://github.com/airyrooms/maleo-plugins#contributing-guidelines)\n\n\n## Contributors\n\nThis project exists thanks to all the people who contribute. [Contribute](CONTRIBUTING.md).\n\nMany thanks to our **[contributors](https://github.com/airyrooms/maleo.js/graphs/contributors)**!\n\n\n## License\n\nMIT","readmeFilename":"README.md","bugs":{"url":"https://github.com/airyrooms/maleo.js/issues"},"homepage":"https://github.com/airyrooms/maleo.js#readme","_id":"@airy/maleo@0.1.1-canary.0","_nodeVersion":"11.12.0","_npmVersion":"lerna/3.13.1/node@v11.12.0+x64 (linux)","dist":{"integrity":"sha512-aDc23A771F+epGGETMgVYkXUfvHdnHLJPLvGh6Z0bUz8GyK8DhFyCTS43YYBMlM2jsnkL/W2Orw8d+vOlQuBRQ==","shasum":"279937d0f69a733804c26c7b8c4fe132f31ac061","tarball":"https://registry.npmjs.org/@airy/maleo/-/maleo-0.1.1-canary.0.tgz","fileCount":83,"unpackedSize":238793,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJcnGnWCRA9TVsSAnZWagAAA+MP/jqbqHHkRpgYi7lMy+U6\naGdrZZJ5Nljt/LmyfKWAkFKaf560oRDXMPgRehqttAX8+XGsjCnalTV/GO+u\nmhO0/zLOQrjmLgdf+MAq1fS+amqw38lMfiCqe9kCAOnfnsqOmFxFduyI+l3B\nH8dS7aI4U34Lk4NJ0f7dnGfxOtarup63iJ6WoM0r7SjJYLB2199VfReb4vR3\nEyA2xzTSow4aaPPnlycZl+lTIbOcsh5aOpBH/DmVozO+PlYjApQ67JSm0rzU\ny4dsLRTwkvgdBUxbmaQBhWlqsLWsRAT7fLWvNVxQszKdu1Jfb7onuz4iQUry\nw2vDS+cDdYzSBzeaxCThidURCoi7wQ5Fge9uTW1M+RBDICcCaeaHOEtNCJrB\nj9KBCMx+yByqZGVpILwcGCqrjE4Kh8IVCH4exH2G4Qwx2LsBR6yzjEgtJeLz\n8ZM/WJDU05kmhC6IueZ6YAdvVkaQ9983+svxcPPsxPBh7Ut1DTlw4K0S/cos\n3w+5QTrwK2qpz22f7YzwU3idsJXby0VwZAu0J7tIFRvq22VbPLAoz1l3agzR\nvTao9VIVFuSz/vhI2eqTEIHx/r9F++6vLGdDRlMS+LIDuJCfWDIhPTHjnbmf\nJsifosAvmk5hiIyGMEd+UoQG8oVAf67sbkK3ZDv0yS5wMOnWbMScf/AhqyOQ\nUAq5\r\n=sXzB\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQD5ULH8F/THd73DVdyHAGm2eFwQ94+IQXyGjp8dS6O5/gIgB9VWhuQPj4WBX8vgBJk/KbuWFVsZ3hMcuynNFcM7IMU="}]},"maintainers":[{"name":"airyrooms-engineering","email":"engineering@airyrooms.com"}],"_npmUser":{"name":"airyrooms-engineering","email":"engineering@airyrooms.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/maleo_0.1.1-canary.0_1553754581509_0.9802535802481602"},"_hasShrinkwrap":false},"0.1.1-canary.1":{"name":"@airy/maleo","version":"0.1.1-canary.1","description":"Un-opinionated Universal Rendering Javascript Framework","repository":{"type":"git","url":"git+https://github.com/airyrooms/maleo.js.git","bugs":"https://github.com/airyrooms/maleo.js/issues"},"author":{"name":"Airy"},"license":"MIT","private":false,"sideEffects":false,"scripts":{"build":"rm -rf dist && gulp","watch":"gulp watch","prepublish":"yarn build","start":"node server.js"},"bin":{"maleo":"lib/bin/maleo.js"},"dependencies":{"@babel/core":"7.1.2","@babel/plugin-proposal-class-properties":"7.1.0","@babel/plugin-proposal-decorators":"7.1.2","@babel/plugin-proposal-object-rest-spread":"7.0.0","@babel/plugin-syntax-dynamic-import":"7.0.0","@babel/plugin-transform-runtime":"7.1.0","@babel/preset-env":"7.1.0","@babel/preset-react":"7.0.0","@babel/runtime":"7.1.2","@babel/runtime-corejs2":"7.2.0","babel-loader":"8.0.4","case-sensitive-paths-webpack-plugin":"2.1.2","compression":"1.7.4","es6-promise":"4.2.5","eventsource":"1.0.7","express":"4.16.4","figlet":"1.2.1","friendly-errors-webpack-plugin":"1.7.0","hard-source-webpack-plugin":"0.13.1","helmet":"3.14.0","isomorphic-fetch":"2.2.1","loader-utils":"1.1.0","node-notifier":"5.2.1","nodemon":"1.18.4","react-dom":"16.8.4","react-helmet":"5.2.0","react-loadable":"5.5.0","react-router-config":"5.0.0","react-router-dom":"5.0.0","rimraf":"2.6.3","source-map-support":"0.5.9","terser-webpack-plugin":"1.1.0","webpack":"4.29.0","webpack-bundle-analyzer":"3.0.3","webpack-cli":"3.1.2","webpack-dev-middleware":"3.4.0","webpack-hot-middleware":"2.24.3","webpackbar":"2.6.3"},"peerDependencies":{"react":"^16.8.4"},"devDependencies":{"@babel/preset-typescript":"^7.1.0","@types/webpack-bundle-analyzer":"^2.13.0","babel-plugin-module-resolver":"^3.1.1","gulp":"github:gulpjs/gulp#4.0","gulp-babel":"^8.0.0","gulp-clean":"^0.4.0","gulp-sourcemaps":"^2.6.4","gulp-uglify":"^3.0.1","gulp-watch":"^5.0.1","react":"^16.8.4"},"gitHead":"212e08532487544161e0f9b2ee68a01fdc25f8d9","readme":"[![Build Status](https://travis-ci.org/airyrooms/maleo.js.svg?branch=master)](https://travis-ci.org/airyrooms/maleo.js)\n[![Coverage Status](https://coveralls.io/repos/github/airyrooms/maleo.js/badge.svg?branch=master)](https://coveralls.io/github/airyrooms/maleo.js?branch=master)\n[![npm version](https://badge.fury.io/js/%40airy%2Fmaleo.svg)](https://badge.fury.io/js/%40airy%2Fmaleo)\n[![Issues](http://img.shields.io/github/issues/airyrooms/maleo.js.svg)]( https://github.com/airyrooms/maleo.js/issues)\n[![MIT license](https://img.shields.io/badge/License-MIT-blue.svg)](https://github.com/airyrooms/maleo.js/blob/canary/LICENSE)\n[![Discord Chat](https://img.shields.io/discord/550498214789513218.svg)](https://discord.gg/9eArCQn)\n\n\n# Welcome to Maleo.js\n\nMaleo.js is an un-opinionated framework to enable Universal Rendering in JavaScript using React with no hassle.\n\nWe are here to solve the time consuming setups Universal Rendering Required.\n\n---\n\nReadme below is the documentation for the `canary` (prerelease) branch. To view the documentation for the latest stable Maleo.js version change branch to `master`\n\n---\n\n# Table of Contents\n- [Features](#features)\n- [Setup](#setup)\n- [Component Lifecycle](#component-lifecycle)\n- [Routing](#routing)\n- [Dynamic Import Component](#dynamic-import-component)\n  - [Tips](#tips)\n  - [Preloading](#preloading)\n- [Customizable Component](#customizable-component)\n  - [Custom Document](#custom-document)\n  - [Custom Wrap](#custom-wrap)\n- [Custom Configuration](#custom-configuration)\n  - [Customize Server](#customize-server)\n  - [Customize Webpack](#customize-webpack)\n  - [Customize Babel Config](#customize-babel-config)\n- [CDN Support](#cdn-support)\n- [Plugins](#plugins)\n- [FAQ](#faq)\n- [Contributing](#contributing)\n\n---\n\n## Features\n- Universal Rendering\n- Plugin based framework\n- Customizable\n\n## Setup\n\nInstall Maleo.js \n\n>*for now change `@airy/maleo` to `@airy/maleo@canary` until we publish the stable version of this package*\n\n\n**NPM**\n```bash\n$ npm install --save @airy/maleo react\n```\n**Yarn**\n```bash\n$ yarn add @airy/maleo react\n```\n\nAdd this script to your `package.json`\n```json\n{\n  \"scripts\": {\n    \"dev\": \"maleo dev\",\n    \"build\": \"export NODE_ENV=production && maleo build\",\n    \"start\": \"export NODE_ENV=production && node .maleo/server.js\"\n  }\n}\n```\n\nCreate a page Root component\n```jsx\n// ./src/Root.jsx\nimport React from 'react';\n\n// Export default is required for registering page\nexport default class RootComponent extends React.Component {\n  render() {\n    return (\n      <h1>Hello World!</h1>\n    )\n  }\n}\n```\n\nAnd lastly, create a routing file on your project root directory called `routes.json` and register your page component\n```json\n[\n  {\n    \"path\": \"/\",\n    \"page\": \"./src/Root\"\n  }\n]\n```\n\nAfter that you can now run `$ npm run dev` and go to `http://localhost:3000`.\n\nYou should now see your app running on your browser.\n\nBy now you should see \n- Automatic transpilation and bundling (with webpack and babel)\n- ~~Hot code reloading~~ [#17](https://github.com/airyrooms/maleo.js/issues/17)\n- Server rendering\n\nTo see how simple this is, check out the sample app!\n\n## Component Lifecycle \n\nMaleo.js added a new component lifecycle hook called `getInitialProps`, this function is called during Server Side Rendering (SSR).\n\nThis is useful especially for SEO purposes. \n\nExample for stateful component:\n```jsx\nimport React from 'react';\n\nexport default class extends React.Component {\n  static getInitialProps = async (ctx) => {\n    const { req } = ctx;\n\n    const userAgent = req ? req.headers['user-agent'] : navigator.userAgent;\n\n    // the return value will be passed as props for this component\n    return { userAgent };\n  }\n\n  render() {\n    return (\n      <div>\n        Hello World {this.props.userAgent}\n      </div>\n    );\n  }\n}\n```\n\nExample for stateless component:\n```jsx\nconst Component = (props) => (\n  <div>\n    Hello World {props.userAgent}\n  </div>\n);\n\nComponent.getInitialprops = async (ctx) => {\n  const { req } = ctx;\n\n  const userAgent = req ? req.headers['user-agent'] : navigator.userAgent;\n\n  return { userAgent };\n};\n\nexport default Component;\n```\n\n`getInitialProps` receives a context object with the following properties:\n- `req` - HTTP request object (server only)\n- `res` - HTTP response object (server only)\n- `...wrapProps` - Spreaded properties from custom Wrap\n- `...appProps` - Spreaded properties from custom App\n\n## Routing\n\nRouting is declared in a centralized route config.\nRegister all the route config in `routes.json` file.\n\nIf you put the `routes.json` files on root directory, Maleo will automatically register your route. Otherwise put path to your routes on [Maleo config](#custom-configuration).\n\nRoutes file has to export default the route configuration.\nThe route object **expected to have distinct key** to indicate the route.\n\n<table>\n  <tr>\n    <td>Key</td>\n    <td>Type</td>\n    <td>Description</td>\n  </tr>\n  <tr>\n    <td><code>path</code></td>\n    <td><code>String!</code></td>\n    <td>Routes path</td>\n  </tr>\n  <tr>\n    <td><code>page</code></td>\n    <td><code>String!</code></td>\n    <td>Path to React Component for this route path</td>\n  </tr>\n  <tr>\n    <td><code>exact</code></td>\n    <td><code>Boolean?</code> [<code>false</code>]</td>\n    <td>To make url has to match exactly the path in order to render the component. Give <code>false</code> value if the route is a wrapper route component</td>\n  </tr>\n  <tr>\n    <td><code>routes</code></td>\n    <td><code>RouteObject?</code></td>\n    <td>Nested route</td>\n  </tr>\n</table>\n\n\nFor example:\n```json\n[\n  {\n    \"page\": \"./src/MainApp\",\n    \"routes\": [\n      {\n        \"path\": \"/\",\n        \"page\": \"./src/Search\",\n        \"exact\": true\n      },\n      {\n        \"path\": \"/search\",\n        \"page\": \"./src/Search\",\n        \"routes\": [\n          {\n            \"path\": \"/search/hello\",\n            \"page\": \"./src/Detail\",\n            \"exact\": true\n          }\n        ]\n      },\n      {\n        \"path\": \"/detail\",\n        \"page\": \"./src/Detail\",\n        \"exact\": true\n      }\n    ]\n  }\n]\n```\n\n## Dynamic Import Component\nMaleo.js supports TC39 [dynamic import proposal](https://github.com/tc39/proposal-dynamic-import) for JavaScript.\n\nYou can think dynamic import as another way to split your code into manageable chunks. You can use our `Dynamic` function which utilizes [react loadable](https://github.com/jamiebuilds/react-loadable)\n\nFor Example\n```js\n// DynamicComponent.js\nimport Dynamic from '@airy/maleo/dynamic';\n\nexport default Dynamic({\n  loader: () => import( /* webpackChunkName:\"DynamicComponent\" */ './component/DynamicLoad'),\n})\n```\n\n### **Preloading**\n\nFor optimization purposes, you can also preload a component even before the component got rendered.\n\nFor example, if you want to load component when a button get pressed, you can start preloading the component when the user hovers the mouse over the button.\n\nThe component created by `Dynamic` exposes a [static `preload` method](https://github.com/jamiebuilds/react-loadable#loadablecomponentpreload).\n\n```jsx\nimport React from 'react';\nimport Dynamic from '@airy/maleo/dynamic';\n\nconst DynamicBar = Dynamic({\n  loader: () => import('./Bar'),\n  loading: LoadingCompoinent\n});\n\nclass MyComponent extends React.Component {\n  state = { showBar: false };\n\n  onClick = () => {\n    this.setState({ showBar: true });\n  };\n\n  onMouseOver = () => DynamicBar.preload();\n\n  render() {\n    return (\n      <div>\n        <button\n          onClick={this.onClick}\n          onMouseOver={this.onMouseOver}>\n          Show Bar\n        </button>\n        { this.state.showBar && <DynamicBar /> }\n      </div>\n    )\n  }\n}\n```\n\n## Customizable Component\n\n### Custom Document\n\nHighly inspired by what [Next.js](https://github.com/zeit/next.js) has done on their awesome template customization.\n\nMaleo.js also enable customization on `Document` as document's markup. So you don't need to include tags like `<html>`, `<head>`, `<body>`, etc. \n\nTo override the default behavior, you'll need to create a component that extends the `Document` React class provided by Maleo.\n\n```jsx\n// document.jsx\nimport React from 'react';\nimport { Document, Header, Main, Scripts } from '@airy/maleo/document';\n\nexport default class extends Document {\n  render() {\n    return (\n      <html>\n        <Header>\n          <title>Maleo JS</title>\n          <meta charset=\"utf-8\" />\n          <meta name=\"description\" content=\"Maleo.js is awesome!\" />\n\n          <style>\n            {` body { background-color: #fff } `}\n          </style>\n        </Header>\n\n        <body>\n          <Main />\n\n          <Scripts />\n\n          <ReduxScript />\n        </body>\n      </html>\n    );\n  }\n}\n```\n\n### Custom Wrap\n\nMaleo.js uses the `Wrap` component to initialize pages. `Wrap` contains React Router's Component. You can add HoC here to wrap the application and control the page initialization. Which allows you to do amazing things like:\n- Persisting layour between page changes\n- Keeping state when navigating pages\n- Custom error handling using `componentDidCatch`\n- Inject additional data into pages (like Redux provider, etc)\n\nTo override the default behavior, you'll need to create a component that extends the `Wrap` React class provided by Maleo.\n\n```jsx\n// wrap.jsx\nimport React from 'react';\nimport { Wrap } from '@airy/maleo/wrap';\n\n// Redux plugin for Maleo.js\n// Hoc that creates store and add Redux Provider\nimport { withRedux } from '@airy/maleo-redux-plugin';\n\n// Custom Wrapper that will be rendered for the whole Application\nimport CustomWrapper from './component/CustomWrapper';\n\nimport { createStore } from './store';\n\n@withRedux(createStore)\nexport default class extends Wrap {\n  static getInitialProps = (ctx) => {\n    const { store } = ctx\n    // you receive store from context\n    // you can access or do something with the store here\n    console.log('Initialized Redux Store', store);\n    return {}\n  }\n\n  render() {\n    return (\n      <CustomWrapper>\n        {super.render()}\n      </CustomWrapper>\n    )\n  }\n}\n\n```\nIf you put `document.jsx` and `wrap.jsx` on root directory (the same level with `package.json`), then Maleo will automatically register your custom Document and Wrap. Otherwise, you can add the path to your custom Document and Wrap on [Maleo config](#custom-configuration)\n\n---\n***We are also working on adding default and customizable `Error` component page***\n\n---\n\n## Custom Configuration\n\nFor more advanced configuration of Maleo.js, like `webpack` config, registering `plugins`, path to your routes, custom Document and Wrap, and adding `path alias`, you can create a `maleo.config.js` in the root of your project directory. (same directory with `package.json`)\n\n```js\n// maleo.config.js\n\nmodule.exports = {\n  /* config options here */\n}\n```\n\nHere are the API's for the configuration:\n\n<table>\n  <tr>\n    <td>Key</td>\n    <td>Type</td>\n    <td>Description</td>\n  </tr>\n  <tr>\n    <td><code>buildDir</code></td>\n    <td><code>String?</code> [<code>.maleo</code>]</td>\n    <td>Directory to put Maleo.js' build assets</td>\n  </tr>\n  <tr>\n    <td><code>cache</code></td>\n    <td><code>Boolean?</code> [<code>true</code>]</td>\n    <td>Enable webpack build caching</td>\n  </tr>\n  <tr>\n    <td><code>sourceMaps</code></td>\n    <td><code>Boolean?</code> [<code>true</code>]</td>\n    <td>Enable webpack to generate source maps</td>\n  </tr>\n  <tr>\n    <td><code>alias</code></td>\n    <td><code>Object?</code></td>\n    <td>A key value pair for aliasing path directory\n      <br>\n      <code>\n      { 'component': './src/component' }\n      </code>\n    </td>\n  </tr>\n  <tr>\n    <td><code>publicPath</code></td>\n    <td><code>String?</code> [<code>/_assets/</code>]</td>\n    <td>To customize webpack's <code>publicPath</code> \n    <br>Comes in handy if using CDN to put built assets</td>\n  </tr>\n  <tr>\n    <td><code>analyzeBundle</code></td>\n    <td><code>Boolean?</code> [<code>false</code>]</td>\n    <td>To enable webpack's bundle analyzer, for analyzing bundle sizes during bundle debugging should Maleo.js' build process got slow</td>\n  </tr>\n  <tr>\n    <td><code>webpack</code></td>\n    <td><code>Function?</code></td>\n    <td>To customize webpack configuration, more details <a href=\"#customize-webpack\">here</a></td>\n  </tr>\n  <tr>\n    <td><code>routes</code></td>\n    <td><code>string?</code> [<code>rootDir/routes.jsx</code>]</td>\n    <td>Path to your routes file</td>\n  </tr>\n  <tr>\n    <td><code>customDocument</code></td>\n    <td><code>string?</code> [<code>rootDir/document.jsx</code>]</td>\n    <td>Path to your custom document file</td>\n  </tr>\n  <tr>\n    <td><code>customWrap</code></td>\n    <td><code>string?</code> [<code>rootDir/wrap.jsx</code>]</td>\n    <td>Path to your custom wrap file</td>\n  </tr>\n  <tr>\n    <td><code>customApp</code></td>\n    <td><code>string?</code> [<code>rootDir/app.jsx</code>]</td>\n    <td>Path to your custom app file</td>\n  </tr>\n</table>\n\n#### Customize Server\n\nCreate a `server.js` file on root directory where your `package.json` lives.\nHere you can customize Maleo's server.\n```js\nimport { Server } from '@airy/maleo/server';\nimport path from 'path';\n\nimport routeConfig from './routes';\n\nconst PORT = process.env.PORT || 8080;\n\nconst maleoServer = Server.init({\n  port: PORT,\n});\n\nmaleoServer.run(() => {\n  console.log('Server running on port :', PORT);\n});\n```\n\nHere are the API's for the configuration:\n\n<table>\n  <tr>\n    <td>Key</td>\n    <td>Type</td>\n    <td>Description</td>\n  </tr>\n  <tr>\n    <td><code>port</code></td>\n    <td><code>Number?</code> [<code>3000</code>]</td>\n    <td>Port to run Maleo server</td>\n  </tr>\n  <tr>\n    <td><code>assetDir</code></td>\n    <td><code>String?</code> [<code>'<root>/.maleo/client'</code>]</td>\n    <td>Directory for all client related assets</td>\n  </tr>\n</table>\n\n#### Customize Webpack\n\nYou are able to extend Maleo.js' default webpack configuration by defining a function on `maleo.config.js`\n\n```js\n// maleo.config.js\n\nmodule.exports = {\n  webpack(config, context, next) {\n    // Perform customizations to webpack config\n    // Important: This call is required in order to let Maleo pass the config to webpack\n    return next(); \n  },\n};\n```\n\nWebpack function will receive three arguments:\n\n<table>\n  <tr>\n    <td>Argument</td>\n    <td>Details</td>\n  <tr>\n  <tr>\n    <td><code>config</code></td>\n    <td>This contains webpack configuration object that you can manipulate</td>\n  <tr>\n  <tr>\n    <td><code>context</code></td>\n    <td>This contains some keys that are useful for the build context\n      <br>\n      <table>\n        <tr>\n          <td><code>isDev</code></td>\n          <td><code>Boolean</code></td>\n          <td>Check if current build is for development</td>\n        </tr>\n        <tr>\n          <td><code>publicPath</code></td>\n          <td><code>String</code></td>\n          <td>Public Path of user defined or default's value</td>\n        </tr>\n        <tr>\n          <td><code>analyzeBundle</code></td>\n          <td><code>Boolean</code></td>\n          <td>Check if analyze bundle is enabled</td>\n        </tr>\n        <tr>\n          <td><code>buildDirectory</code></td>\n          <td><code>String</code></td>\n          <td>Build Directory of user defined or default's value</td>\n        </tr>\n        <tr>\n          <td><code>name</code></td>\n          <td><code>String</code></td>\n          <td>Build name <code>'server' || 'client'</code></td>\n        </tr>\n      </table>\n    </td>\n  <tr>\n  <tr>\n    <td><code>next</code></td>\n    <td>A callback required to be called to pass the custom configuration</td>\n  <tr>\n</table>\n\nExample of adding `ts-loader` through `maleo.config.js`:\n\n```js\n// maleo.config.js\n\n// Partly taken and modified from @airy/maleo-ts-plugin source\n// for simplicity purposes\n\nmodule.exports = {\n  webpack(config, context, next) {\n    const { isDev } = context\n\n    config.module.rules.push({\n      test: /\\.tsx?/,\n      exclude: /node_modules/,\n      use: [\n        require.resolve('@airy/maleo/lib/build/loaders/maleo-babel-loader'),\n        {\n          loader: 'ts-loader',\n          options: {\n            transpileOnly: true,\n          },\n        },\n      ],\n\n      if (isDev) {\n        config.plugins.push(new ForkTSCheckerWebpackPlugin());\n      }\n\n      return next();\n    })\n  },\n};\n```\n\n#### Customize Babel Config\n\nMaleo.js also let you have your own babel config. Just simply add `.babelrc` file at the root directory of your app.\n\nYou can include Maleo.js' babel preset in order to have latest JavaScript preset.\n\nHere's an example of `.babelrc` file:\n```json\n{\n  \"presets\": [\"@airy/maleo/babel\"],\n  \"plugins\": []\n}\n```\n\nThe `@airy/maleo/babel` preset includes everything you need to get your development started. The preset includes:\n- `@babel/preset-env`\n- `@babel/preset-react`\n- `@babel/plugin-proposal-class-properties`\n- `@babel/plugin-proposal-decorators`\n- `@babel/plugin-proposal-object-rest-spread`\n- `@babel/plugin-transform-runtime`\n- `react-loadable/babel`\n\n## CDN Support\n\nIf you are using a CDN, you can set up the `publicPath` setting and configure your CDN's origin to resolve to the domain that Maleo.js is hosted on.\n\n```js\n// maleo.config.js\n\nconst isProd = process.env.NODE_ENV === 'production';\n\nmodule.exports = {\n  assetPrefix: isProd && 'https://cdn.example.com';\n}\n```\n\n## FAQ\n\n== TO BE DETERMINED == \n\n## Plugins\n- [css-plugin](https://github.com/airyrooms/maleo-plugins/tree/master/packages/css-plugin)\n- [redux-plugin](https://github.com/airyrooms/maleo-plugins/tree/master/packages/redux-plugin)\n- [typescript-plugin](https://github.com/airyrooms/maleo-plugins/tree/master/packages/typescript-plugin)\n- [more](https://github.com/airyrooms/maleo-plugins)\n\n\n## Contributing\n[Please follow these steps to contribute to Maleo.js](https://github.com/airyrooms/maleo.js/blob/canary/CONTRIBUTING.md)\n\n[Please follow these steps to contribute to Maleo.js' plugins](https://github.com/airyrooms/maleo-plugins#contributing-guidelines)\n\n\n## Contributors\n\nThis project exists thanks to all the people who contribute. [Contribute](CONTRIBUTING.md).\n\nMany thanks to our **[contributors](https://github.com/airyrooms/maleo.js/graphs/contributors)**!\n\n\n## License\n\nMIT","readmeFilename":"README.md","bugs":{"url":"https://github.com/airyrooms/maleo.js/issues"},"homepage":"https://github.com/airyrooms/maleo.js#readme","_id":"@airy/maleo@0.1.1-canary.1","_nodeVersion":"10.15.3","_npmVersion":"lerna/3.13.1/node@v10.15.3+x64 (linux)","dist":{"integrity":"sha512-zL5AqhSggYYwRkdxYKtaQ0bxLptJdJXYBvDgE5KcyNRGukCw7WpIX1Uz9b8im3MvZaZhvVuqAVWE0AZYOktsPQ==","shasum":"6aa5019621a2bb9ae15eec2b9be97a9e56587f9f","tarball":"https://registry.npmjs.org/@airy/maleo/-/maleo-0.1.1-canary.1.tgz","fileCount":83,"unpackedSize":239181,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJcnY/xCRA9TVsSAnZWagAAx7IP/iQ5WW5RXJzUvleQpz4T\niy/NiewyicyHwVBox+ZsfxJVXL8WYVyzxjlqz21gOwLCDoTefSOiT65pS7Ws\ncdXtEoWi7Dl5QiY5NaLrRVquULGUUwV+MhWsXW7/zwKeAzU7LLbu5Zl1oWwQ\nwuMcB3uksiPo+Ey7GrqBt1T9fhSgCWPSu7Mh/eAbQjU56cbozeuiy04wKFCc\n5it2HfGtT8reKXb9z98pNk0bor0STsRdaCb+r0Ood/ts1GgSrL3/3DYw/CXN\nHdNW3P2JNyJdseGqEF1cLqjxVnJI5okP76kxLooFlQSIHd+kOx/gKMdpteVI\n9+D4YWCHpdFaCgXwozr9uNWlr2MMTqYBOsUvRGP42nYyFng1bbDoCBrFr+T0\nlX5hbQDYaaX34B/EQPC1rdEHJYouou3w87VbSDPv95mKSwkEryezHL3D/OhE\ngKs1pP7CMtscwGFcczjlIxreegtlJ9FCLku3ij9QpkIXcQ8BI2zethz+ZK2H\nxCX1YCkOswQF1NxTDf9bHjagYSqkC0qZcun+rrLJnZkchhptVqp6xMwjEGft\niEsPMCOMOoIhPH6ysRTvndpVjj9pt/5DPuzVuhgR1eQk2qRfVoiOrA81yZ63\n9p8cEpDMMRMg0U9W2DQuL2UzqO0WsqMTmTTa7R/isy5uki6jPsq68pEqILpp\nQyVT\r\n=PKcw\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIG0Jbw1mrhKyo6tqLCOSV2BNHF6Nr3a/RWkdcT0X2JhGAiEAyzTnBCvz3Wxv/eiaqccvm9NFUhl9Kp0APnUnodjdvK8="}]},"maintainers":[{"name":"airyrooms-engineering","email":"engineering@airyrooms.com"}],"_npmUser":{"name":"airyrooms-engineering","email":"engineering@airyrooms.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/maleo_0.1.1-canary.1_1553829873351_0.1365972314556223"},"_hasShrinkwrap":false},"0.1.1-canary.2":{"name":"@airy/maleo","version":"0.1.1-canary.2","description":"Un-opinionated Universal Rendering Javascript Framework","repository":{"type":"git","url":"git+https://github.com/airyrooms/maleo.js.git","bugs":"https://github.com/airyrooms/maleo.js/issues"},"author":{"name":"Airy"},"license":"MIT","private":false,"sideEffects":false,"scripts":{"build":"rm -rf dist && gulp","watch":"gulp watch","prepublish":"yarn build","start":"node server.js"},"bin":{"maleo":"lib/bin/maleo.js"},"dependencies":{"@babel/core":"7.1.2","@babel/plugin-proposal-class-properties":"7.1.0","@babel/plugin-proposal-decorators":"7.1.2","@babel/plugin-proposal-object-rest-spread":"7.0.0","@babel/plugin-syntax-dynamic-import":"7.0.0","@babel/plugin-transform-runtime":"7.1.0","@babel/preset-env":"7.1.0","@babel/preset-react":"7.0.0","@babel/runtime":"7.1.2","@babel/runtime-corejs2":"7.2.0","babel-loader":"8.0.4","case-sensitive-paths-webpack-plugin":"2.1.2","compression":"1.7.4","es6-promise":"4.2.5","eventsource":"1.0.7","express":"4.16.4","figlet":"1.2.1","friendly-errors-webpack-plugin":"1.7.0","hard-source-webpack-plugin":"0.13.1","helmet":"3.14.0","isomorphic-fetch":"2.2.1","loader-utils":"1.1.0","node-notifier":"5.2.1","nodemon":"1.18.4","react-dom":"16.8.4","react-helmet":"5.2.0","react-loadable":"5.5.0","react-router-config":"5.0.0","react-router-dom":"5.0.0","rimraf":"2.6.3","source-map-support":"0.5.9","terser-webpack-plugin":"1.1.0","webpack":"4.29.0","webpack-bundle-analyzer":"3.0.3","webpack-cli":"3.1.2","webpack-dev-middleware":"3.4.0","webpack-hot-middleware":"2.24.3","webpackbar":"2.6.3"},"peerDependencies":{"react":"^16.8.4"},"devDependencies":{"@babel/preset-typescript":"^7.1.0","@types/webpack-bundle-analyzer":"^2.13.0","babel-plugin-module-resolver":"^3.1.1","gulp":"github:gulpjs/gulp#4.0","gulp-babel":"^8.0.0","gulp-clean":"^0.4.0","gulp-sourcemaps":"^2.6.4","gulp-uglify":"^3.0.1","gulp-watch":"^5.0.1","react":"^16.8.4"},"gitHead":"aeb823d0dcce5507d76494f97bc37c515983f3a3","readme":"[![Build Status](https://travis-ci.org/airyrooms/maleo.js.svg?branch=master)](https://travis-ci.org/airyrooms/maleo.js)\n[![Coverage Status](https://coveralls.io/repos/github/airyrooms/maleo.js/badge.svg?branch=master)](https://coveralls.io/github/airyrooms/maleo.js?branch=master)\n[![npm version](https://badge.fury.io/js/%40airy%2Fmaleo.svg)](https://badge.fury.io/js/%40airy%2Fmaleo)\n[![Issues](http://img.shields.io/github/issues/airyrooms/maleo.js.svg)]( https://github.com/airyrooms/maleo.js/issues)\n[![MIT license](https://img.shields.io/badge/License-MIT-blue.svg)](https://github.com/airyrooms/maleo.js/blob/canary/LICENSE)\n[![Discord Chat](https://img.shields.io/discord/550498214789513218.svg)](https://discord.gg/9eArCQn)\n\n\n# Welcome to Maleo.js\n\nMaleo.js is an un-opinionated framework to enable Universal Rendering in JavaScript using React with no hassle.\n\nWe are here to solve the time consuming setups Universal Rendering Required.\n\n---\n\nReadme below is the documentation for the `canary` (prerelease) branch. To view the documentation for the latest stable Maleo.js version change branch to `master`\n\n---\n\n# Table of Contents\n- [Features](#features)\n- [Setup](#setup)\n- [Component Lifecycle](#component-lifecycle)\n- [Routing](#routing)\n- [Dynamic Import Component](#dynamic-import-component)\n  - [Tips](#tips)\n  - [Preloading](#preloading)\n- [Customizable Component](#customizable-component)\n  - [Custom Document](#custom-document)\n  - [Custom Wrap](#custom-wrap)\n- [Custom Configuration](#custom-configuration)\n  - [Customize Server](#customize-server)\n  - [Customize Webpack](#customize-webpack)\n  - [Customize Babel Config](#customize-babel-config)\n- [CDN Support](#cdn-support)\n- [Plugins](#plugins)\n- [FAQ](#faq)\n- [Contributing](#contributing)\n\n---\n\n## Features\n- Universal Rendering\n- Plugin based framework\n- Customizable\n\n## Setup\n\nInstall Maleo.js \n\n>*for now change `@airy/maleo` to `@airy/maleo@canary` until we publish the stable version of this package*\n\n\n**NPM**\n```bash\n$ npm install --save @airy/maleo react\n```\n**Yarn**\n```bash\n$ yarn add @airy/maleo react\n```\n\nAdd this script to your `package.json`\n```json\n{\n  \"scripts\": {\n    \"dev\": \"maleo dev\",\n    \"build\": \"export NODE_ENV=production && maleo build\",\n    \"start\": \"export NODE_ENV=production && node .maleo/server.js\"\n  }\n}\n```\n\nCreate a page Root component\n```jsx\n// ./src/Root.jsx\nimport React from 'react';\n\n// Export default is required for registering page\nexport default class RootComponent extends React.Component {\n  render() {\n    return (\n      <h1>Hello World!</h1>\n    )\n  }\n}\n```\n\nAnd lastly, create a routing file on your project root directory called `routes.json` and register your page component\n```json\n[\n  {\n    \"path\": \"/\",\n    \"page\": \"./src/Root\"\n  }\n]\n```\n\nAfter that you can now run `$ npm run dev` and go to `http://localhost:3000`.\n\nYou should now see your app running on your browser.\n\nBy now you should see \n- Automatic transpilation and bundling (with webpack and babel)\n- ~~Hot code reloading~~ [#17](https://github.com/airyrooms/maleo.js/issues/17)\n- Server rendering\n\nTo see how simple this is, check out the sample app!\n\n## Component Lifecycle \n\nMaleo.js added a new component lifecycle hook called `getInitialProps`, this function is called during Server Side Rendering (SSR).\n\nThis is useful especially for SEO purposes. \n\nExample for stateful component:\n```jsx\nimport React from 'react';\n\nexport default class extends React.Component {\n  static getInitialProps = async (ctx) => {\n    const { req } = ctx;\n\n    const userAgent = req ? req.headers['user-agent'] : navigator.userAgent;\n\n    // the return value will be passed as props for this component\n    return { userAgent };\n  }\n\n  render() {\n    return (\n      <div>\n        Hello World {this.props.userAgent}\n      </div>\n    );\n  }\n}\n```\n\nExample for stateless component:\n```jsx\nconst Component = (props) => (\n  <div>\n    Hello World {props.userAgent}\n  </div>\n);\n\nComponent.getInitialprops = async (ctx) => {\n  const { req } = ctx;\n\n  const userAgent = req ? req.headers['user-agent'] : navigator.userAgent;\n\n  return { userAgent };\n};\n\nexport default Component;\n```\n\n`getInitialProps` receives a context object with the following properties:\n- `req` - HTTP request object (server only)\n- `res` - HTTP response object (server only)\n- `...wrapProps` - Spreaded properties from custom Wrap\n- `...appProps` - Spreaded properties from custom App\n\n## Routing\n\nRouting is declared in a centralized route config.\nRegister all the route config in `routes.json` file.\n\nIf you put the `routes.json` files on root directory, Maleo will automatically register your route. Otherwise put path to your routes on [Maleo config](#custom-configuration).\n\nRoutes file has to export default the route configuration.\nThe route object **expected to have distinct key** to indicate the route.\n\n<table>\n  <tr>\n    <td>Key</td>\n    <td>Type</td>\n    <td>Description</td>\n  </tr>\n  <tr>\n    <td><code>path</code></td>\n    <td><code>String!</code></td>\n    <td>Routes path</td>\n  </tr>\n  <tr>\n    <td><code>page</code></td>\n    <td><code>String!</code></td>\n    <td>Path to React Component for this route path</td>\n  </tr>\n  <tr>\n    <td><code>exact</code></td>\n    <td><code>Boolean?</code> [<code>false</code>]</td>\n    <td>To make url has to match exactly the path in order to render the component. Give <code>false</code> value if the route is a wrapper route component</td>\n  </tr>\n  <tr>\n    <td><code>routes</code></td>\n    <td><code>RouteObject?</code></td>\n    <td>Nested route</td>\n  </tr>\n</table>\n\n\nFor example:\n```json\n[\n  {\n    \"page\": \"./src/MainApp\",\n    \"routes\": [\n      {\n        \"path\": \"/\",\n        \"page\": \"./src/Search\",\n        \"exact\": true\n      },\n      {\n        \"path\": \"/search\",\n        \"page\": \"./src/Search\",\n        \"routes\": [\n          {\n            \"path\": \"/search/hello\",\n            \"page\": \"./src/Detail\",\n            \"exact\": true\n          }\n        ]\n      },\n      {\n        \"path\": \"/detail\",\n        \"page\": \"./src/Detail\",\n        \"exact\": true\n      }\n    ]\n  }\n]\n```\n\n## Dynamic Import Component\nMaleo.js supports TC39 [dynamic import proposal](https://github.com/tc39/proposal-dynamic-import) for JavaScript.\n\nYou can think dynamic import as another way to split your code into manageable chunks. You can use our `Dynamic` function which utilizes [react loadable](https://github.com/jamiebuilds/react-loadable)\n\nFor Example\n```js\n// DynamicComponent.js\nimport Dynamic from '@airy/maleo/dynamic';\n\nexport default Dynamic({\n  loader: () => import( /* webpackChunkName:\"DynamicComponent\" */ './component/DynamicLoad'),\n})\n```\n\n### **Preloading**\n\nFor optimization purposes, you can also preload a component even before the component got rendered.\n\nFor example, if you want to load component when a button get pressed, you can start preloading the component when the user hovers the mouse over the button.\n\nThe component created by `Dynamic` exposes a [static `preload` method](https://github.com/jamiebuilds/react-loadable#loadablecomponentpreload).\n\n```jsx\nimport React from 'react';\nimport Dynamic from '@airy/maleo/dynamic';\n\nconst DynamicBar = Dynamic({\n  loader: () => import('./Bar'),\n  loading: LoadingCompoinent\n});\n\nclass MyComponent extends React.Component {\n  state = { showBar: false };\n\n  onClick = () => {\n    this.setState({ showBar: true });\n  };\n\n  onMouseOver = () => DynamicBar.preload();\n\n  render() {\n    return (\n      <div>\n        <button\n          onClick={this.onClick}\n          onMouseOver={this.onMouseOver}>\n          Show Bar\n        </button>\n        { this.state.showBar && <DynamicBar /> }\n      </div>\n    )\n  }\n}\n```\n\n## Customizable Component\n\n### Custom Document\n\nHighly inspired by what [Next.js](https://github.com/zeit/next.js) has done on their awesome template customization.\n\nMaleo.js also enable customization on `Document` as document's markup. So you don't need to include tags like `<html>`, `<head>`, `<body>`, etc. \n\nTo override the default behavior, you'll need to create a component that extends the `Document` React class provided by Maleo.\n\n```jsx\n// document.jsx\nimport React from 'react';\nimport { Document, Header, Main, Scripts } from '@airy/maleo/document';\n\nexport default class extends Document {\n  render() {\n    return (\n      <html>\n        <Header>\n          <title>Maleo JS</title>\n          <meta charset=\"utf-8\" />\n          <meta name=\"description\" content=\"Maleo.js is awesome!\" />\n\n          <style>\n            {` body { background-color: #fff } `}\n          </style>\n        </Header>\n\n        <body>\n          <Main />\n\n          <Scripts />\n\n          <ReduxScript />\n        </body>\n      </html>\n    );\n  }\n}\n```\n\n### Custom Wrap\n\nMaleo.js uses the `Wrap` component to initialize pages. `Wrap` contains React Router's Component. You can add HoC here to wrap the application and control the page initialization. Which allows you to do amazing things like:\n- Persisting layour between page changes\n- Keeping state when navigating pages\n- Custom error handling using `componentDidCatch`\n- Inject additional data into pages (like Redux provider, etc)\n\nTo override the default behavior, you'll need to create a component that extends the `Wrap` React class provided by Maleo.\n\n```jsx\n// wrap.jsx\nimport React from 'react';\nimport { Wrap } from '@airy/maleo/wrap';\n\n// Redux plugin for Maleo.js\n// Hoc that creates store and add Redux Provider\nimport { withRedux } from '@airy/maleo-redux-plugin';\n\n// Custom Wrapper that will be rendered for the whole Application\nimport CustomWrapper from './component/CustomWrapper';\n\nimport { createStore } from './store';\n\n@withRedux(createStore)\nexport default class extends Wrap {\n  static getInitialProps = (ctx) => {\n    const { store } = ctx\n    // you receive store from context\n    // you can access or do something with the store here\n    console.log('Initialized Redux Store', store);\n    return {}\n  }\n\n  render() {\n    return (\n      <CustomWrapper>\n        {super.render()}\n      </CustomWrapper>\n    )\n  }\n}\n\n```\nIf you put `document.jsx` and `wrap.jsx` on root directory (the same level with `package.json`), then Maleo will automatically register your custom Document and Wrap. Otherwise, you can add the path to your custom Document and Wrap on [Maleo config](#custom-configuration)\n\n---\n***We are also working on adding default and customizable `Error` component page***\n\n---\n\n## Custom Configuration\n\nFor more advanced configuration of Maleo.js, like `webpack` config, registering `plugins`, path to your routes, custom Document and Wrap, and adding `path alias`, you can create a `maleo.config.js` in the root of your project directory. (same directory with `package.json`)\n\n```js\n// maleo.config.js\n\nmodule.exports = {\n  /* config options here */\n}\n```\n\nHere are the API's for the configuration:\n\n<table>\n  <tr>\n    <td>Key</td>\n    <td>Type</td>\n    <td>Description</td>\n  </tr>\n  <tr>\n    <td><code>buildDir</code></td>\n    <td><code>String?</code> [<code>.maleo</code>]</td>\n    <td>Directory to put Maleo.js' build assets</td>\n  </tr>\n  <tr>\n    <td><code>cache</code></td>\n    <td><code>Boolean?</code> [<code>true</code>]</td>\n    <td>Enable webpack build caching</td>\n  </tr>\n  <tr>\n    <td><code>sourceMaps</code></td>\n    <td><code>Boolean?</code> [<code>true</code>]</td>\n    <td>Enable webpack to generate source maps</td>\n  </tr>\n  <tr>\n    <td><code>alias</code></td>\n    <td><code>Object?</code></td>\n    <td>A key value pair for aliasing path directory\n      <br>\n      <code>\n      { 'component': './src/component' }\n      </code>\n    </td>\n  </tr>\n  <tr>\n    <td><code>publicPath</code></td>\n    <td><code>String?</code> [<code>/_assets/</code>]</td>\n    <td>To customize webpack's <code>publicPath</code> \n    <br>Comes in handy if using CDN to put built assets</td>\n  </tr>\n  <tr>\n    <td><code>analyzeBundle</code></td>\n    <td><code>Boolean?</code> [<code>false</code>]</td>\n    <td>To enable webpack's bundle analyzer, for analyzing bundle sizes during bundle debugging should Maleo.js' build process got slow</td>\n  </tr>\n  <tr>\n    <td><code>webpack</code></td>\n    <td><code>Function?</code></td>\n    <td>To customize webpack configuration, more details <a href=\"#customize-webpack\">here</a></td>\n  </tr>\n  <tr>\n    <td><code>routes</code></td>\n    <td><code>string?</code> [<code>rootDir/routes.jsx</code>]</td>\n    <td>Path to your routes file</td>\n  </tr>\n  <tr>\n    <td><code>customDocument</code></td>\n    <td><code>string?</code> [<code>rootDir/document.jsx</code>]</td>\n    <td>Path to your custom document file</td>\n  </tr>\n  <tr>\n    <td><code>customWrap</code></td>\n    <td><code>string?</code> [<code>rootDir/wrap.jsx</code>]</td>\n    <td>Path to your custom wrap file</td>\n  </tr>\n  <tr>\n    <td><code>customApp</code></td>\n    <td><code>string?</code> [<code>rootDir/app.jsx</code>]</td>\n    <td>Path to your custom app file</td>\n  </tr>\n</table>\n\n#### Customize Server\n\nCreate a `server.js` file on root directory where your `package.json` lives.\nHere you can customize Maleo's server.\n```js\nimport { Server } from '@airy/maleo/server';\nimport path from 'path';\n\nimport routeConfig from './routes';\n\nconst PORT = process.env.PORT || 8080;\n\nconst maleoServer = Server.init({\n  port: PORT,\n});\n\nmaleoServer.run(() => {\n  console.log('Server running on port :', PORT);\n});\n```\n\nHere are the API's for the configuration:\n\n<table>\n  <tr>\n    <td>Key</td>\n    <td>Type</td>\n    <td>Description</td>\n  </tr>\n  <tr>\n    <td><code>port</code></td>\n    <td><code>Number?</code> [<code>3000</code>]</td>\n    <td>Port to run Maleo server</td>\n  </tr>\n  <tr>\n    <td><code>assetDir</code></td>\n    <td><code>String?</code> [<code>'<root>/.maleo/client'</code>]</td>\n    <td>Directory for all client related assets</td>\n  </tr>\n</table>\n\n#### Customize Webpack\n\nYou are able to extend Maleo.js' default webpack configuration by defining a function on `maleo.config.js`\n\n```js\n// maleo.config.js\n\nmodule.exports = {\n  webpack(config, context, next) {\n    // Perform customizations to webpack config\n    // Important: This call is required in order to let Maleo pass the config to webpack\n    return next(); \n  },\n};\n```\n\nWebpack function will receive three arguments:\n\n<table>\n  <tr>\n    <td>Argument</td>\n    <td>Details</td>\n  <tr>\n  <tr>\n    <td><code>config</code></td>\n    <td>This contains webpack configuration object that you can manipulate</td>\n  <tr>\n  <tr>\n    <td><code>context</code></td>\n    <td>This contains some keys that are useful for the build context\n      <br>\n      <table>\n        <tr>\n          <td><code>isDev</code></td>\n          <td><code>Boolean</code></td>\n          <td>Check if current build is for development</td>\n        </tr>\n        <tr>\n          <td><code>publicPath</code></td>\n          <td><code>String</code></td>\n          <td>Public Path of user defined or default's value</td>\n        </tr>\n        <tr>\n          <td><code>analyzeBundle</code></td>\n          <td><code>Boolean</code></td>\n          <td>Check if analyze bundle is enabled</td>\n        </tr>\n        <tr>\n          <td><code>buildDirectory</code></td>\n          <td><code>String</code></td>\n          <td>Build Directory of user defined or default's value</td>\n        </tr>\n        <tr>\n          <td><code>name</code></td>\n          <td><code>String</code></td>\n          <td>Build name <code>'server' || 'client'</code></td>\n        </tr>\n      </table>\n    </td>\n  <tr>\n  <tr>\n    <td><code>next</code></td>\n    <td>A callback required to be called to pass the custom configuration</td>\n  <tr>\n</table>\n\nExample of adding `ts-loader` through `maleo.config.js`:\n\n```js\n// maleo.config.js\n\n// Partly taken and modified from @airy/maleo-ts-plugin source\n// for simplicity purposes\n\nmodule.exports = {\n  webpack(config, context, next) {\n    const { isDev } = context\n\n    config.module.rules.push({\n      test: /\\.tsx?/,\n      exclude: /node_modules/,\n      use: [\n        require.resolve('@airy/maleo/lib/build/loaders/maleo-babel-loader'),\n        {\n          loader: 'ts-loader',\n          options: {\n            transpileOnly: true,\n          },\n        },\n      ],\n\n      if (isDev) {\n        config.plugins.push(new ForkTSCheckerWebpackPlugin());\n      }\n\n      return next();\n    })\n  },\n};\n```\n\n#### Customize Babel Config\n\nMaleo.js also let you have your own babel config. Just simply add `.babelrc` file at the root directory of your app.\n\nYou can include Maleo.js' babel preset in order to have latest JavaScript preset.\n\nHere's an example of `.babelrc` file:\n```json\n{\n  \"presets\": [\"@airy/maleo/babel\"],\n  \"plugins\": []\n}\n```\n\nThe `@airy/maleo/babel` preset includes everything you need to get your development started. The preset includes:\n- `@babel/preset-env`\n- `@babel/preset-react`\n- `@babel/plugin-proposal-class-properties`\n- `@babel/plugin-proposal-decorators`\n- `@babel/plugin-proposal-object-rest-spread`\n- `@babel/plugin-transform-runtime`\n- `react-loadable/babel`\n\n## CDN Support\n\nIf you are using a CDN, you can set up the `publicPath` setting and configure your CDN's origin to resolve to the domain that Maleo.js is hosted on.\n\n```js\n// maleo.config.js\n\nconst isProd = process.env.NODE_ENV === 'production';\n\nmodule.exports = {\n  assetPrefix: isProd && 'https://cdn.example.com';\n}\n```\n\n## FAQ\n\n== TO BE DETERMINED == \n\n## Plugins\n- [css-plugin](https://github.com/airyrooms/maleo-plugins/tree/master/packages/css-plugin)\n- [redux-plugin](https://github.com/airyrooms/maleo-plugins/tree/master/packages/redux-plugin)\n- [typescript-plugin](https://github.com/airyrooms/maleo-plugins/tree/master/packages/typescript-plugin)\n- [more](https://github.com/airyrooms/maleo-plugins)\n\n\n## Contributing\n[Please follow these steps to contribute to Maleo.js](https://github.com/airyrooms/maleo.js/blob/canary/CONTRIBUTING.md)\n\n[Please follow these steps to contribute to Maleo.js' plugins](https://github.com/airyrooms/maleo-plugins#contributing-guidelines)\n\n\n## Contributors\n\nThis project exists thanks to all the people who contribute. [Contribute](CONTRIBUTING.md).\n\nMany thanks to our **[contributors](https://github.com/airyrooms/maleo.js/graphs/contributors)**!\n\n\n## License\n\nMIT","readmeFilename":"README.md","bugs":{"url":"https://github.com/airyrooms/maleo.js/issues"},"homepage":"https://github.com/airyrooms/maleo.js#readme","_id":"@airy/maleo@0.1.1-canary.2","_nodeVersion":"11.13.0","_npmVersion":"lerna/3.13.1/node@v11.13.0+x64 (linux)","dist":{"integrity":"sha512-hRaBbbcf+klJ0R1YijkBDPEAnFXXAjatZdlVc34Un7k4ozdb+nwXonG6Utz88hM0+htSApdBOBX17LSjkU3ZRg==","shasum":"ff56b9a5aae5ea3f49fdab18991135017fabed34","tarball":"https://registry.npmjs.org/@airy/maleo/-/maleo-0.1.1-canary.2.tgz","fileCount":83,"unpackedSize":239839,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJcotnSCRA9TVsSAnZWagAAZ7QQAKMcfJ4nsajmSdp9gmez\n4S339jhUFaMTtUmzY9pC4J4OXzSwDwcI+fSTg5ooCTHlJ5QtmCycfnJj4ocg\nWyYcSNQg5tgn8A4gVXJIttDTH00nyURQczESGHnpe0BssG4iYWjNxBjx+UPD\naFKsjejLRv9KIRXbHNXTOJAJtnkQDf3WrsDVptrWOOPyloFM87co/UJlsRpM\na0NStXIrsP/NwiSu+WPj90P8dqtryqnvQ7CtiLrbHixKfeCzPl9S33hiGE3V\nlOIHJL3rmyegAEBWoCPjtrFNhrkIETWnNBg0tkyX3Ds7Dw8GGiCfCfKkeS1M\nSIZueuZBamNIBF3QhrbgeOvislVDl/gCVBhqZXaQ4zduJsuaY8c534lrPxEU\n7PBc5ZgfXCPEPI3Wm8Ec1l3VfWgTKRchupooiDSd6wMPNs/EOy+TwcFPFq4/\n90rWpPMXMuydEILqFRzn04NiRJWpVB0qPSAt4ZN5zL0xkkj1gaJOeSz1No7b\nw+1/FMkAh//QM58MFb2taMwj4z3xW4dT/I8cDqhdDQBCCfKaHnyumB9AIAl/\nowdB5MNKlQlNBNlB8rJfW2MqAqG2mAo76LCA3Zv4d9FnRf7PP+Ye3NfdwnOB\nZYh0R6m34RAFuNZlLr6WQrTNyIGwpNWmpu6HxvYfFZTOrgKrHqQD3MKWp4Nq\nkAns\r\n=IdW4\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCdnHCXRATFoIe23wrUPnRnKoWqApmhSoMkRbms1qu49QIge+APrjBAZqm7jD0JmPfm01KOF6GVMLC0y6mTcA/oQ5w="}]},"maintainers":[{"name":"airyrooms-engineering","email":"engineering@airyrooms.com"}],"_npmUser":{"name":"airyrooms-engineering","email":"engineering@airyrooms.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/maleo_0.1.1-canary.2_1554176465998_0.5855727598472411"},"_hasShrinkwrap":false},"0.1.1-canary.3":{"name":"@airy/maleo","version":"0.1.1-canary.3","description":"Un-opinionated Universal Rendering Javascript Framework","repository":{"type":"git","url":"git+https://github.com/airyrooms/maleo.js.git","bugs":"https://github.com/airyrooms/maleo.js/issues"},"author":{"name":"Airy"},"license":"MIT","private":false,"sideEffects":false,"scripts":{"build":"rm -rf dist && gulp","watch":"gulp watch","prepublish":"yarn build","start":"node server.js"},"bin":{"maleo":"lib/bin/maleo.js"},"dependencies":{"@babel/core":"7.1.2","@babel/plugin-proposal-class-properties":"7.1.0","@babel/plugin-proposal-decorators":"7.1.2","@babel/plugin-proposal-object-rest-spread":"7.0.0","@babel/plugin-syntax-dynamic-import":"7.0.0","@babel/plugin-transform-runtime":"7.1.0","@babel/preset-env":"7.1.0","@babel/preset-react":"7.0.0","@babel/runtime":"7.1.2","@babel/runtime-corejs2":"7.2.0","babel-loader":"8.0.4","case-sensitive-paths-webpack-plugin":"2.1.2","compression":"1.7.4","es6-promise":"4.2.5","eventsource":"1.0.7","express":"4.16.4","figlet":"1.2.1","friendly-errors-webpack-plugin":"1.7.0","hard-source-webpack-plugin":"0.13.1","helmet":"3.14.0","isomorphic-fetch":"2.2.1","loader-utils":"1.1.0","node-notifier":"5.2.1","nodemon":"1.18.4","react-dom":"16.8.4","react-helmet":"5.2.0","react-loadable":"5.5.0","react-router-config":"5.0.0","react-router-dom":"5.0.0","rimraf":"2.6.3","source-map-support":"0.5.9","terser-webpack-plugin":"1.1.0","webpack":"4.29.0","webpack-bundle-analyzer":"3.0.3","webpack-cli":"3.1.2","webpack-dev-middleware":"3.4.0","webpack-hot-middleware":"2.24.3","webpackbar":"2.6.3"},"peerDependencies":{"react":"^16.8.4"},"devDependencies":{"@babel/preset-typescript":"^7.1.0","@types/webpack-bundle-analyzer":"^2.13.0","babel-plugin-module-resolver":"^3.1.1","gulp":"github:gulpjs/gulp#4.0","gulp-babel":"^8.0.0","gulp-clean":"^0.4.0","gulp-sourcemaps":"^2.6.4","gulp-uglify":"^3.0.1","gulp-watch":"^5.0.1","react":"^16.8.4"},"gitHead":"2496ad969b8ff892b30ebb5bc2a74e33d15a2a8b","readme":"[![Build Status](https://travis-ci.org/airyrooms/maleo.js.svg?branch=master)](https://travis-ci.org/airyrooms/maleo.js)\n[![Coverage Status](https://coveralls.io/repos/github/airyrooms/maleo.js/badge.svg?branch=master)](https://coveralls.io/github/airyrooms/maleo.js?branch=master)\n[![npm version](https://badge.fury.io/js/%40airy%2Fmaleo.svg)](https://badge.fury.io/js/%40airy%2Fmaleo)\n[![Issues](http://img.shields.io/github/issues/airyrooms/maleo.js.svg)]( https://github.com/airyrooms/maleo.js/issues)\n[![MIT license](https://img.shields.io/badge/License-MIT-blue.svg)](https://github.com/airyrooms/maleo.js/blob/canary/LICENSE)\n[![Discord Chat](https://img.shields.io/discord/550498214789513218.svg)](https://discord.gg/9eArCQn)\n\n\n# Welcome to Maleo.js\n\nMaleo.js is an un-opinionated framework to enable Universal Rendering in JavaScript using React with no hassle.\n\nWe are here to solve the time consuming setups Universal Rendering Required.\n\n---\n\nReadme below is the documentation for the `canary` (prerelease) branch. To view the documentation for the latest stable Maleo.js version change branch to `master`\n\n---\n\n# Table of Contents\n- [Features](#features)\n- [Setup](#setup)\n- [Component Lifecycle](#component-lifecycle)\n- [Routing](#routing)\n- [Dynamic Import Component](#dynamic-import-component)\n  - [Tips](#tips)\n  - [Preloading](#preloading)\n- [Customizable Component](#customizable-component)\n  - [Custom Document](#custom-document)\n  - [Custom Wrap](#custom-wrap)\n- [Custom Configuration](#custom-configuration)\n  - [Customize Server](#customize-server)\n  - [Customize Webpack](#customize-webpack)\n  - [Customize Babel Config](#customize-babel-config)\n- [CDN Support](#cdn-support)\n- [Plugins](#plugins)\n- [FAQ](#faq)\n- [Contributing](#contributing)\n\n---\n\n## Features\n- Universal Rendering\n- Plugin based framework\n- Customizable\n\n## Setup\n\nInstall Maleo.js \n\n>*for now change `@airy/maleo` to `@airy/maleo@canary` until we publish the stable version of this package*\n\n\n**NPM**\n```bash\n$ npm install --save @airy/maleo react\n```\n**Yarn**\n```bash\n$ yarn add @airy/maleo react\n```\n\nAdd this script to your `package.json`\n```json\n{\n  \"scripts\": {\n    \"dev\": \"maleo dev\",\n    \"build\": \"export NODE_ENV=production && maleo build\",\n    \"start\": \"export NODE_ENV=production && node .maleo/server.js\"\n  }\n}\n```\n\nCreate a page Root component\n```jsx\n// ./src/Root.jsx\nimport React from 'react';\n\n// Export default is required for registering page\nexport default class RootComponent extends React.Component {\n  render() {\n    return (\n      <h1>Hello World!</h1>\n    )\n  }\n}\n```\n\nAnd lastly, create a routing file on your project root directory called `routes.json` and register your page component\n```json\n[\n  {\n    \"path\": \"/\",\n    \"page\": \"./src/Root\"\n  }\n]\n```\n\nAfter that you can now run `$ npm run dev` and go to `http://localhost:3000`.\n\nYou should now see your app running on your browser.\n\nBy now you should see \n- Automatic transpilation and bundling (with webpack and babel)\n- ~~Hot code reloading~~ [#17](https://github.com/airyrooms/maleo.js/issues/17)\n- Server rendering\n\nTo see how simple this is, check out the sample app!\n\n## Component Lifecycle \n\nMaleo.js added a new component lifecycle hook called `getInitialProps`, this function is called during Server Side Rendering (SSR).\n\nThis is useful especially for SEO purposes. \n\nExample for stateful component:\n```jsx\nimport React from 'react';\n\nexport default class extends React.Component {\n  static getInitialProps = async (ctx) => {\n    const { req } = ctx;\n\n    const userAgent = req ? req.headers['user-agent'] : navigator.userAgent;\n\n    // the return value will be passed as props for this component\n    return { userAgent };\n  }\n\n  render() {\n    return (\n      <div>\n        Hello World {this.props.userAgent}\n      </div>\n    );\n  }\n}\n```\n\nExample for stateless component:\n```jsx\nconst Component = (props) => (\n  <div>\n    Hello World {props.userAgent}\n  </div>\n);\n\nComponent.getInitialprops = async (ctx) => {\n  const { req } = ctx;\n\n  const userAgent = req ? req.headers['user-agent'] : navigator.userAgent;\n\n  return { userAgent };\n};\n\nexport default Component;\n```\n\n`getInitialProps` receives a context object with the following properties:\n- `req` - HTTP request object (server only)\n- `res` - HTTP response object (server only)\n- `...wrapProps` - Spreaded properties from custom Wrap\n- `...appProps` - Spreaded properties from custom App\n\n## Routing\n\nRouting is declared in a centralized route config.\nRegister all the route config in `routes.json` file.\n\nIf you put the `routes.json` files on root directory, Maleo will automatically register your route. Otherwise put path to your routes on [Maleo config](#custom-configuration).\n\nRoutes file has to export default the route configuration.\nThe route object **expected to have distinct key** to indicate the route.\n\n<table>\n  <tr>\n    <td>Key</td>\n    <td>Type</td>\n    <td>Description</td>\n  </tr>\n  <tr>\n    <td><code>path</code></td>\n    <td><code>String!</code></td>\n    <td>Routes path</td>\n  </tr>\n  <tr>\n    <td><code>page</code></td>\n    <td><code>String!</code></td>\n    <td>Path to React Component for this route path</td>\n  </tr>\n  <tr>\n    <td><code>exact</code></td>\n    <td><code>Boolean?</code> [<code>false</code>]</td>\n    <td>To make url has to match exactly the path in order to render the component. Give <code>false</code> value if the route is a wrapper route component</td>\n  </tr>\n  <tr>\n    <td><code>routes</code></td>\n    <td><code>RouteObject?</code></td>\n    <td>Nested route</td>\n  </tr>\n</table>\n\n\nFor example:\n```json\n[\n  {\n    \"page\": \"./src/MainApp\",\n    \"routes\": [\n      {\n        \"path\": \"/\",\n        \"page\": \"./src/Search\",\n        \"exact\": true\n      },\n      {\n        \"path\": \"/search\",\n        \"page\": \"./src/Search\",\n        \"routes\": [\n          {\n            \"path\": \"/search/hello\",\n            \"page\": \"./src/Detail\",\n            \"exact\": true\n          }\n        ]\n      },\n      {\n        \"path\": \"/detail\",\n        \"page\": \"./src/Detail\",\n        \"exact\": true\n      }\n    ]\n  }\n]\n```\n\n## Dynamic Import Component\nMaleo.js supports TC39 [dynamic import proposal](https://github.com/tc39/proposal-dynamic-import) for JavaScript.\n\nYou can think dynamic import as another way to split your code into manageable chunks. You can use our `Dynamic` function which utilizes [react loadable](https://github.com/jamiebuilds/react-loadable)\n\nFor Example\n```js\n// DynamicComponent.js\nimport Dynamic from '@airy/maleo/dynamic';\n\nexport default Dynamic({\n  loader: () => import( /* webpackChunkName:\"DynamicComponent\" */ './component/DynamicLoad'),\n})\n```\n\n### **Preloading**\n\nFor optimization purposes, you can also preload a component even before the component got rendered.\n\nFor example, if you want to load component when a button get pressed, you can start preloading the component when the user hovers the mouse over the button.\n\nThe component created by `Dynamic` exposes a [static `preload` method](https://github.com/jamiebuilds/react-loadable#loadablecomponentpreload).\n\n```jsx\nimport React from 'react';\nimport Dynamic from '@airy/maleo/dynamic';\n\nconst DynamicBar = Dynamic({\n  loader: () => import('./Bar'),\n  loading: LoadingCompoinent\n});\n\nclass MyComponent extends React.Component {\n  state = { showBar: false };\n\n  onClick = () => {\n    this.setState({ showBar: true });\n  };\n\n  onMouseOver = () => DynamicBar.preload();\n\n  render() {\n    return (\n      <div>\n        <button\n          onClick={this.onClick}\n          onMouseOver={this.onMouseOver}>\n          Show Bar\n        </button>\n        { this.state.showBar && <DynamicBar /> }\n      </div>\n    )\n  }\n}\n```\n\n## Customizable Component\n\n### Custom Document\n\nHighly inspired by what [Next.js](https://github.com/zeit/next.js) has done on their awesome template customization.\n\nMaleo.js also enable customization on `Document` as document's markup. So you don't need to include tags like `<html>`, `<head>`, `<body>`, etc. \n\nTo override the default behavior, you'll need to create a component that extends the `Document` React class provided by Maleo.\n\n```jsx\n// document.jsx\nimport React from 'react';\nimport { Document, Header, Main, Scripts } from '@airy/maleo/document';\n\nexport default class extends Document {\n  render() {\n    return (\n      <html>\n        <Header>\n          <title>Maleo JS</title>\n          <meta charset=\"utf-8\" />\n          <meta name=\"description\" content=\"Maleo.js is awesome!\" />\n\n          <style>\n            {` body { background-color: #fff } `}\n          </style>\n        </Header>\n\n        <body>\n          <Main />\n\n          <Scripts />\n\n          <ReduxScript />\n        </body>\n      </html>\n    );\n  }\n}\n```\n\n### Custom Wrap\n\nMaleo.js uses the `Wrap` component to initialize pages. `Wrap` contains React Router's Component. You can add HoC here to wrap the application and control the page initialization. Which allows you to do amazing things like:\n- Persisting layour between page changes\n- Keeping state when navigating pages\n- Custom error handling using `componentDidCatch`\n- Inject additional data into pages (like Redux provider, etc)\n\nTo override the default behavior, you'll need to create a component that extends the `Wrap` React class provided by Maleo.\n\n```jsx\n// wrap.jsx\nimport React from 'react';\nimport { Wrap } from '@airy/maleo/wrap';\n\n// Redux plugin for Maleo.js\n// Hoc that creates store and add Redux Provider\nimport { withRedux } from '@airy/maleo-redux-plugin';\n\n// Custom Wrapper that will be rendered for the whole Application\nimport CustomWrapper from './component/CustomWrapper';\n\nimport { createStore } from './store';\n\n@withRedux(createStore)\nexport default class extends Wrap {\n  static getInitialProps = (ctx) => {\n    const { store } = ctx\n    // you receive store from context\n    // you can access or do something with the store here\n    console.log('Initialized Redux Store', store);\n    return {}\n  }\n\n  render() {\n    return (\n      <CustomWrapper>\n        {super.render()}\n      </CustomWrapper>\n    )\n  }\n}\n\n```\nIf you put `document.jsx` and `wrap.jsx` on root directory (the same level with `package.json`), then Maleo will automatically register your custom Document and Wrap. Otherwise, you can add the path to your custom Document and Wrap on [Maleo config](#custom-configuration)\n\n---\n***We are also working on adding default and customizable `Error` component page***\n\n---\n\n## Custom Configuration\n\nFor more advanced configuration of Maleo.js, like `webpack` config, registering `plugins`, path to your routes, custom Document and Wrap, and adding `path alias`, you can create a `maleo.config.js` in the root of your project directory. (same directory with `package.json`)\n\n```js\n// maleo.config.js\n\nmodule.exports = {\n  /* config options here */\n}\n```\n\nHere are the API's for the configuration:\n\n<table>\n  <tr>\n    <td>Key</td>\n    <td>Type</td>\n    <td>Description</td>\n  </tr>\n  <tr>\n    <td><code>buildDir</code></td>\n    <td><code>String?</code> [<code>.maleo</code>]</td>\n    <td>Directory to put Maleo.js' build assets</td>\n  </tr>\n  <tr>\n    <td><code>cache</code></td>\n    <td><code>Boolean?</code> [<code>true</code>]</td>\n    <td>Enable webpack build caching</td>\n  </tr>\n  <tr>\n    <td><code>sourceMaps</code></td>\n    <td><code>Boolean?</code> [<code>true</code>]</td>\n    <td>Enable webpack to generate source maps</td>\n  </tr>\n  <tr>\n    <td><code>alias</code></td>\n    <td><code>Object?</code></td>\n    <td>A key value pair for aliasing path directory\n      <br>\n      <code>\n      { 'component': './src/component' }\n      </code>\n    </td>\n  </tr>\n  <tr>\n    <td><code>publicPath</code></td>\n    <td><code>String?</code> [<code>/_assets/</code>]</td>\n    <td>To customize webpack's <code>publicPath</code> \n    <br>Comes in handy if using CDN to put built assets</td>\n  </tr>\n  <tr>\n    <td><code>analyzeBundle</code></td>\n    <td><code>Boolean?</code> [<code>false</code>]</td>\n    <td>To enable webpack's bundle analyzer, for analyzing bundle sizes during bundle debugging should Maleo.js' build process got slow</td>\n  </tr>\n  <tr>\n    <td><code>webpack</code></td>\n    <td><code>Function?</code></td>\n    <td>To customize webpack configuration, more details <a href=\"#customize-webpack\">here</a></td>\n  </tr>\n  <tr>\n    <td><code>routes</code></td>\n    <td><code>string?</code> [<code>rootDir/routes.jsx</code>]</td>\n    <td>Path to your routes file</td>\n  </tr>\n  <tr>\n    <td><code>customDocument</code></td>\n    <td><code>string?</code> [<code>rootDir/document.jsx</code>]</td>\n    <td>Path to your custom document file</td>\n  </tr>\n  <tr>\n    <td><code>customWrap</code></td>\n    <td><code>string?</code> [<code>rootDir/wrap.jsx</code>]</td>\n    <td>Path to your custom wrap file</td>\n  </tr>\n  <tr>\n    <td><code>customApp</code></td>\n    <td><code>string?</code> [<code>rootDir/app.jsx</code>]</td>\n    <td>Path to your custom app file</td>\n  </tr>\n</table>\n\n#### Customize Server\n\nCreate a `server.js` file on root directory where your `package.json` lives.\nHere you can customize Maleo's server.\n```js\nimport { Server } from '@airy/maleo/server';\nimport path from 'path';\n\nimport routeConfig from './routes';\n\nconst PORT = process.env.PORT || 8080;\n\nconst maleoServer = Server.init({\n  port: PORT,\n});\n\nmaleoServer.run(() => {\n  console.log('Server running on port :', PORT);\n});\n```\n\nHere are the API's for the configuration:\n\n<table>\n  <tr>\n    <td>Key</td>\n    <td>Type</td>\n    <td>Description</td>\n  </tr>\n  <tr>\n    <td><code>port</code></td>\n    <td><code>Number?</code> [<code>3000</code>]</td>\n    <td>Port to run Maleo server</td>\n  </tr>\n  <tr>\n    <td><code>assetDir</code></td>\n    <td><code>String?</code> [<code>'<root>/.maleo/client'</code>]</td>\n    <td>Directory for all client related assets</td>\n  </tr>\n</table>\n\n#### Customize Webpack\n\nYou are able to extend Maleo.js' default webpack configuration by defining a function on `maleo.config.js`\n\n```js\n// maleo.config.js\n\nmodule.exports = {\n  webpack(config, context, next) {\n    // Perform customizations to webpack config\n    // Important: This call is required in order to let Maleo pass the config to webpack\n    return next(); \n  },\n};\n```\n\nWebpack function will receive three arguments:\n\n<table>\n  <tr>\n    <td>Argument</td>\n    <td>Details</td>\n  <tr>\n  <tr>\n    <td><code>config</code></td>\n    <td>This contains webpack configuration object that you can manipulate</td>\n  <tr>\n  <tr>\n    <td><code>context</code></td>\n    <td>This contains some keys that are useful for the build context\n      <br>\n      <table>\n        <tr>\n          <td><code>isDev</code></td>\n          <td><code>Boolean</code></td>\n          <td>Check if current build is for development</td>\n        </tr>\n        <tr>\n          <td><code>publicPath</code></td>\n          <td><code>String</code></td>\n          <td>Public Path of user defined or default's value</td>\n        </tr>\n        <tr>\n          <td><code>analyzeBundle</code></td>\n          <td><code>Boolean</code></td>\n          <td>Check if analyze bundle is enabled</td>\n        </tr>\n        <tr>\n          <td><code>buildDirectory</code></td>\n          <td><code>String</code></td>\n          <td>Build Directory of user defined or default's value</td>\n        </tr>\n        <tr>\n          <td><code>name</code></td>\n          <td><code>String</code></td>\n          <td>Build name <code>'server' || 'client'</code></td>\n        </tr>\n      </table>\n    </td>\n  <tr>\n  <tr>\n    <td><code>next</code></td>\n    <td>A callback required to be called to pass the custom configuration</td>\n  <tr>\n</table>\n\nExample of adding `ts-loader` through `maleo.config.js`:\n\n```js\n// maleo.config.js\n\n// Partly taken and modified from @airy/maleo-ts-plugin source\n// for simplicity purposes\n\nmodule.exports = {\n  webpack(config, context, next) {\n    const { isDev } = context\n\n    config.module.rules.push({\n      test: /\\.tsx?/,\n      exclude: /node_modules/,\n      use: [\n        require.resolve('@airy/maleo/lib/build/loaders/maleo-babel-loader'),\n        {\n          loader: 'ts-loader',\n          options: {\n            transpileOnly: true,\n          },\n        },\n      ],\n\n      if (isDev) {\n        config.plugins.push(new ForkTSCheckerWebpackPlugin());\n      }\n\n      return next();\n    })\n  },\n};\n```\n\n#### Customize Babel Config\n\nMaleo.js also let you have your own babel config. Just simply add `.babelrc` file at the root directory of your app.\n\nYou can include Maleo.js' babel preset in order to have latest JavaScript preset.\n\nHere's an example of `.babelrc` file:\n```json\n{\n  \"presets\": [\"@airy/maleo/babel\"],\n  \"plugins\": []\n}\n```\n\nThe `@airy/maleo/babel` preset includes everything you need to get your development started. The preset includes:\n- `@babel/preset-env`\n- `@babel/preset-react`\n- `@babel/plugin-proposal-class-properties`\n- `@babel/plugin-proposal-decorators`\n- `@babel/plugin-proposal-object-rest-spread`\n- `@babel/plugin-transform-runtime`\n- `react-loadable/babel`\n\n## CDN Support\n\nIf you are using a CDN, you can set up the `publicPath` setting and configure your CDN's origin to resolve to the domain that Maleo.js is hosted on.\n\n```js\n// maleo.config.js\n\nconst isProd = process.env.NODE_ENV === 'production';\n\nmodule.exports = {\n  assetPrefix: isProd && 'https://cdn.example.com';\n}\n```\n\n## FAQ\n\n== TO BE DETERMINED == \n\n## Plugins\n- [css-plugin](https://github.com/airyrooms/maleo-plugins/tree/master/packages/css-plugin)\n- [redux-plugin](https://github.com/airyrooms/maleo-plugins/tree/master/packages/redux-plugin)\n- [typescript-plugin](https://github.com/airyrooms/maleo-plugins/tree/master/packages/typescript-plugin)\n- [more](https://github.com/airyrooms/maleo-plugins)\n\n\n## Contributing\n[Please follow these steps to contribute to Maleo.js](https://github.com/airyrooms/maleo.js/blob/canary/CONTRIBUTING.md)\n\n[Please follow these steps to contribute to Maleo.js' plugins](https://github.com/airyrooms/maleo-plugins#contributing-guidelines)\n\n\n## Contributors\n\nThis project exists thanks to all the people who contribute. [Contribute](CONTRIBUTING.md).\n\nMany thanks to our **[contributors](https://github.com/airyrooms/maleo.js/graphs/contributors)**!\n\n\n## License\n\nMIT","readmeFilename":"README.md","bugs":{"url":"https://github.com/airyrooms/maleo.js/issues"},"homepage":"https://github.com/airyrooms/maleo.js#readme","_id":"@airy/maleo@0.1.1-canary.3","_nodeVersion":"11.13.0","_npmVersion":"lerna/3.13.1/node@v11.13.0+x64 (linux)","dist":{"integrity":"sha512-q/rY73xox0hCUqt1NU15Pj6MfcKS/80cZqHiX7dUioA0E02LzpxjTcKDTrLqqP5tsYiPFWdOZe7RcNU7p1xUtg==","shasum":"ff0826ec2799b371e94cb3bfce5c575922f6f466","tarball":"https://registry.npmjs.org/@airy/maleo/-/maleo-0.1.1-canary.3.tgz","fileCount":85,"unpackedSize":253864,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJcowhsCRA9TVsSAnZWagAAm/MP/3CZTcUu1/MR5iThHgie\nz1HBcF7xcxAtOPdD5Ot+3h4qXdTgSaHlCencSW7TWjBqUIrJsWviTgPx5Pro\nJ/syBaZnATeJWvj1/CkMS2ekOxvqFmHVkDkmVmcuM0cOk6KLHLSx0sLMUzk7\nqXlBe7zdzOK0J1Em33EDeFBhzmoWXlLYfKUg+sJ5dIFhLV0LwqS0YB6lspgx\naz4EWGLhy87npmMpydfq/TBY29eEHqquEWPIhrCleB3J0Me0YxgKKHkfgiM/\ndvyRmEf1S1AKNcfzZy468dR/IzoAgqxEMnpOqOBFtYJFMYOp2Xm7sp6E4NIP\nnOLH8Rn3YCBlp9riSGGZ5io75Yc99wHOLphOTM0dg4LddUabhVqHC109FUqx\n9vQUGxrv0SKuVtuZGhnANLA5mvSwVQYeGeAtd4+oHGM5oxD8HjuGuJnBZKiM\nLuxIsPSDeHb4oYOIX3q4xEu5QSDtZACeDyHZTvXd0lDZvE9TMSVJbbsK7qGu\nFnUD17ltE6fZvUaShFXcxbojqld7ofYet6KwIoFGwuHNPXo69tmYSQnvoqSK\nPzJ8RRI8uQ3i4170PF3jr9O3lq9bJzwalvby1DpkqCRp1y5Bvt0+v72HkbH+\nwm5v+Pv+kunDOJNwhrPvex1YvMjQYSDDMKaH+CvXBiBVW0HtGfm+x1r3wPux\nCd99\r\n=DEoa\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDnwXDCs4MapAZvVWxhj/fLfnxMumdHPRKLAoCUY1OTMAIgPxznmd/7TdIX589A7e9KbxjhyJILPZmWxC7Y19Acjms="}]},"maintainers":[{"name":"airyrooms-engineering","email":"engineering@airyrooms.com"}],"_npmUser":{"name":"airyrooms-engineering","email":"engineering@airyrooms.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/maleo_0.1.1-canary.3_1554188395651_0.6891599634417203"},"_hasShrinkwrap":false},"0.1.1":{"name":"@airy/maleo","version":"0.1.1","description":"Un-opinionated Universal Rendering Javascript Framework","repository":{"type":"git","url":"git+https://github.com/airyrooms/maleo.js.git","bugs":"https://github.com/airyrooms/maleo.js/issues"},"author":{"name":"Airy"},"license":"MIT","private":false,"sideEffects":false,"scripts":{"build":"rm -rf dist && gulp","watch":"gulp watch","prepublish":"yarn build","start":"node server.js"},"bin":{"maleo":"lib/bin/maleo.js"},"dependencies":{"@babel/core":"7.1.2","@babel/plugin-proposal-class-properties":"7.1.0","@babel/plugin-proposal-decorators":"7.1.2","@babel/plugin-proposal-object-rest-spread":"7.0.0","@babel/plugin-syntax-dynamic-import":"7.0.0","@babel/plugin-transform-runtime":"7.1.0","@babel/preset-env":"7.1.0","@babel/preset-react":"7.0.0","@babel/runtime":"7.1.2","@babel/runtime-corejs2":"7.2.0","babel-loader":"8.0.4","case-sensitive-paths-webpack-plugin":"2.1.2","compression":"1.7.4","es6-promise":"4.2.5","eventsource":"1.0.7","express":"4.16.4","figlet":"1.2.1","friendly-errors-webpack-plugin":"1.7.0","hard-source-webpack-plugin":"0.13.1","helmet":"3.14.0","isomorphic-fetch":"2.2.1","loader-utils":"1.1.0","node-notifier":"5.2.1","nodemon":"1.18.4","react-dom":"16.8.4","react-helmet":"5.2.0","react-loadable":"5.5.0","react-router-config":"5.0.0","react-router-dom":"5.0.0","rimraf":"2.6.3","source-map-support":"0.5.9","terser-webpack-plugin":"1.1.0","webpack":"4.29.0","webpack-bundle-analyzer":"3.0.3","webpack-cli":"3.1.2","webpack-dev-middleware":"3.4.0","webpack-hot-middleware":"2.24.3","webpackbar":"2.6.3"},"peerDependencies":{"react":"^16.8.4"},"devDependencies":{"@babel/preset-typescript":"^7.1.0","@types/webpack-bundle-analyzer":"^2.13.0","babel-plugin-module-resolver":"^3.1.1","gulp":"github:gulpjs/gulp#4.0","gulp-babel":"^8.0.0","gulp-clean":"^0.4.0","gulp-sourcemaps":"^2.6.4","gulp-uglify":"^3.0.1","gulp-watch":"^5.0.1","react":"^16.8.4"},"gitHead":"281a48d51f2f2196262ccd1937a445ff67362fea","bugs":{"url":"https://github.com/airyrooms/maleo.js/issues"},"homepage":"https://github.com/airyrooms/maleo.js#readme","_id":"@airy/maleo@0.1.1","_nodeVersion":"10.11.0","_npmVersion":"lerna/3.13.1/node@v10.11.0+x64 (darwin)","dist":{"integrity":"sha512-bz17cQlZxa9QSWPM6lT4MX1CMPJdd8aybzgeSj2AqznDDVld5fgErjVbExMUSG70OnIEqa9MEA69dSigF3bk+g==","shasum":"ab4ab11f039e672dd2789038d748898d62d1ae36","tarball":"https://registry.npmjs.org/@airy/maleo/-/maleo-0.1.1.tgz","fileCount":89,"unpackedSize":285563,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJcpW9DCRA9TVsSAnZWagAAAaAP/2MOPE04/Z2ipNDjuOEt\nyU74iddOf6mL9+Kp05/YkSVZfpo1dAyQC1CH0ACwrKB5UoV17UddZUE4G4r3\nJlGhUwZ55mgbFdnJGd1cm0UcBw8B6CGAyZCBW+764Ui4luQEc0qMUZFxBNQm\nUCl61Zzb7enGzrT1PmjqyepgBrApClPti2DdIDLHSpocG4ru+K5w8jLt0uzv\nLUrUaDgaPpHXrvNcMmgNbutOsO3A81IOVzwn+Q15qPOA83QAy4UPZZUSMnuR\nCKR4sVSc8osLwnb/bpm4jt/v2vSALhuTsUl+IuuCI1M06IdLlqL8EJ+70i2e\nn/lLc3pLbNqrFLmhb7ZSt4/HGYX0lB5vRo0/zxQzUi5QYleU0A6gFJRky21n\n7NOUq7lmhYEX/CKz9B9a7oi3cqu+Ln0kYtPI10t9TwJRVL7eBzNRjyRxBcjh\nrCylk/X4QXCZz85vuUHEmnaWlW03l41NlhrAtPUCYwnoh8LFpqogy3sB9XeI\ng1/Q2+ghj9dEy+eTs/rQvOSfBAR6rgbK9fYOUppfPOhhNzOKg+k2chIBfajV\nxy2PhZN+Q9PamZc508mGULxGfqcimONthiMROgb3YcpNpZ2IwgsFbR5Lyjqf\nqdjN4tby3BgLWagU0DuZO4e8yKmuxSEGXhMhudj0g7em4NTd8U/axlCYccHs\nE3jY\r\n=ZZkE\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIBajZNjxyjfC98VVxjCLoE7NtP0sgwJfn741MzhDyMoyAiBhC9HeP0PGQCZ494EmtBYYl+mhoYuDNO17BVgBotk3pQ=="}]},"maintainers":[{"name":"airyrooms-engineering","email":"engineering@airyrooms.com"}],"_npmUser":{"name":"airyrooms-engineering","email":"engineering@airyrooms.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/maleo_0.1.1_1554345794989_0.0491974199518348"},"_hasShrinkwrap":false},"0.1.2-canary.0":{"name":"@airy/maleo","version":"0.1.2-canary.0","description":"Un-opinionated Universal Rendering Javascript Framework","repository":{"type":"git","url":"git+https://github.com/airyrooms/maleo.js.git","bugs":"https://github.com/airyrooms/maleo.js/issues"},"author":{"name":"Airy"},"license":"MIT","private":false,"sideEffects":false,"scripts":{"build":"rm -rf dist && gulp","watch":"gulp watch","prepublish":"yarn build","start":"node server.js"},"bin":{"maleo":"lib/bin/maleo.js"},"dependencies":{"@babel/core":"7.1.2","@babel/plugin-proposal-class-properties":"7.1.0","@babel/plugin-proposal-decorators":"7.1.2","@babel/plugin-proposal-object-rest-spread":"7.0.0","@babel/plugin-syntax-dynamic-import":"7.0.0","@babel/plugin-transform-runtime":"7.1.0","@babel/preset-env":"7.1.0","@babel/preset-react":"7.0.0","@babel/runtime":"7.1.2","@babel/runtime-corejs2":"7.2.0","babel-loader":"8.0.4","case-sensitive-paths-webpack-plugin":"2.1.2","compression":"1.7.4","es6-promise":"4.2.5","eventsource":"1.0.7","express":"4.16.4","figlet":"1.2.1","friendly-errors-webpack-plugin":"1.7.0","hard-source-webpack-plugin":"0.13.1","helmet":"3.14.0","isomorphic-fetch":"2.2.1","loader-utils":"1.1.0","node-notifier":"5.2.1","nodemon":"1.18.4","react-dom":"16.8.4","react-helmet":"5.2.0","react-loadable":"5.5.0","react-router-config":"5.0.0","react-router-dom":"5.0.0","rimraf":"2.6.3","source-map-support":"0.5.9","terser-webpack-plugin":"1.1.0","webpack":"4.29.0","webpack-bundle-analyzer":"3.0.3","webpack-cli":"3.1.2","webpack-dev-middleware":"3.4.0","webpack-hot-middleware":"2.24.3","webpackbar":"2.6.3"},"peerDependencies":{"react":"^16.8.4"},"devDependencies":{"@babel/preset-typescript":"^7.1.0","@types/webpack-bundle-analyzer":"^2.13.0","babel-plugin-module-resolver":"^3.1.1","gulp":"github:gulpjs/gulp#4.0","gulp-babel":"^8.0.0","gulp-clean":"^0.4.0","gulp-sourcemaps":"^2.6.4","gulp-uglify":"^3.0.1","gulp-watch":"^5.0.1","react":"^16.8.4"},"gitHead":"b9a1dbdf1daebf1f7d205d9c5d512f34e27102eb","readme":"[![Build Status](https://travis-ci.org/airyrooms/maleo.js.svg?branch=master)](https://travis-ci.org/airyrooms/maleo.js)\n[![Coverage Status](https://coveralls.io/repos/github/airyrooms/maleo.js/badge.svg?branch=master)](https://coveralls.io/github/airyrooms/maleo.js?branch=master)\n[![npm version](https://badge.fury.io/js/%40airy%2Fmaleo.svg)](https://badge.fury.io/js/%40airy%2Fmaleo)\n[![Issues](http://img.shields.io/github/issues/airyrooms/maleo.js.svg)]( https://github.com/airyrooms/maleo.js/issues)\n[![MIT license](https://img.shields.io/badge/License-MIT-blue.svg)](https://github.com/airyrooms/maleo.js/blob/canary/LICENSE)\n[![Discord Chat](https://img.shields.io/discord/550498214789513218.svg)](https://discord.gg/9eArCQn)\n[![Conventional Commits](https://img.shields.io/badge/Conventional%20Commits-1.0.0-green.svg)](https://conventionalcommits.org)\n\n\n# Welcome to Maleo.js\n\nMaleo.js is an un-opinionated framework to enable Universal Rendering in JavaScript using React with no hassle.\n\nWe are here to solve the time consuming setups Universal Rendering Required.\n\n---\n\nReadme below is the documentation for the `canary` (prerelease) branch. To view the documentation for the latest stable Maleo.js version change branch to `master`\n\n---\n\n# Table of Contents\n- [Features](#features)\n- [Setup](#setup)\n- [Component Lifecycle](#component-lifecycle)\n- [Routing](#routing)\n- [Dynamic Import Component](#dynamic-import-component)\n  - [Tips](#tips)\n  - [Preloading](#preloading)\n- [Customizable Component](#customizable-component)\n  - [Custom Document](#custom-document)\n  - [Custom Wrap](#custom-wrap)\n- [Custom Configuration](#custom-configuration)\n  - [Customize Server](#customize-server)\n  - [Customize Webpack](#customize-webpack)\n  - [Customize Babel Config](#customize-babel-config)\n- [CDN Support](#cdn-support)\n- [Plugins](#plugins)\n- [FAQ](#faq)\n- [Contributing](#contributing)\n\n---\n\n## Features\n- Universal Rendering\n- Plugin based framework\n- Customizable\n\n## Setup\n\nInstall Maleo.js \n\n>*for now change `@airy/maleo` to `@airy/maleo@canary` until we publish the stable version of this package*\n\n\n**NPM**\n```bash\n$ npm install --save @airy/maleo react\n```\n**Yarn**\n```bash\n$ yarn add @airy/maleo react\n```\n\nAdd this script to your `package.json`\n```json\n{\n  \"scripts\": {\n    \"dev\": \"maleo dev\",\n    \"build\": \"export NODE_ENV=production && maleo build\",\n    \"start\": \"export NODE_ENV=production && node .maleo/server.js\"\n  }\n}\n```\n\nCreate a page Root component\n```jsx\n// ./src/Root.jsx\nimport React from 'react';\n\n// Export default is required for registering page\nexport default class RootComponent extends React.Component {\n  render() {\n    return (\n      <h1>Hello World!</h1>\n    )\n  }\n}\n```\n\nAnd lastly, create a routing file on your project root directory called `routes.json` and register your page component\n```json\n[\n  {\n    \"path\": \"/\",\n    \"page\": \"./src/Root\"\n  }\n]\n```\n\nAfter that you can now run `$ npm run dev` and go to `http://localhost:3000`.\n\nYou should now see your app running on your browser.\n\nBy now you should see \n- Automatic transpilation and bundling (with webpack and babel)\n- ~~Hot code reloading~~ [#17](https://github.com/airyrooms/maleo.js/issues/17)\n- Server rendering\n\nTo see how simple this is, check out the sample app!\n\n## Component Lifecycle \n\nMaleo.js added a new component lifecycle hook called `getInitialProps`, this function is called during Server Side Rendering (SSR).\n\nThis is useful especially for SEO purposes. \n\nExample for stateful component:\n```jsx\nimport React from 'react';\n\nexport default class extends React.Component {\n  static getInitialProps = async (ctx) => {\n    const { req } = ctx;\n\n    const userAgent = req ? req.headers['user-agent'] : navigator.userAgent;\n\n    // the return value will be passed as props for this component\n    return { userAgent };\n  }\n\n  render() {\n    return (\n      <div>\n        Hello World {this.props.userAgent}\n      </div>\n    );\n  }\n}\n```\n\nExample for stateless component:\n```jsx\nconst Component = (props) => (\n  <div>\n    Hello World {props.userAgent}\n  </div>\n);\n\nComponent.getInitialprops = async (ctx) => {\n  const { req } = ctx;\n\n  const userAgent = req ? req.headers['user-agent'] : navigator.userAgent;\n\n  return { userAgent };\n};\n\nexport default Component;\n```\n\n`getInitialProps` receives a context object with the following properties:\n- `req` - HTTP request object (server only)\n- `res` - HTTP response object (server only)\n- `...wrapProps` - Spreaded properties from custom Wrap\n- `...appProps` - Spreaded properties from custom App\n\n## Routing\n\nRouting is declared in a centralized route config.\nRegister all the route config in `routes.json` file.\n\nIf you put the `routes.json` files on root directory, Maleo will automatically register your route. Otherwise put path to your routes on [Maleo config](#custom-configuration).\n\nRoutes file has to export default the route configuration.\nThe route object **expected to have distinct key** to indicate the route.\n\n<table>\n  <tr>\n    <td>Key</td>\n    <td>Type</td>\n    <td>Description</td>\n  </tr>\n  <tr>\n    <td><code>path</code></td>\n    <td><code>String!</code></td>\n    <td>Routes path</td>\n  </tr>\n  <tr>\n    <td><code>page</code></td>\n    <td><code>String!</code></td>\n    <td>Path to React Component for this route path</td>\n  </tr>\n  <tr>\n    <td><code>exact</code></td>\n    <td><code>Boolean?</code> [<code>false</code>]</td>\n    <td>To make url has to match exactly the path in order to render the component. Give <code>false</code> value if the route is a wrapper route component</td>\n  </tr>\n  <tr>\n    <td><code>routes</code></td>\n    <td><code>RouteObject?</code></td>\n    <td>Nested route</td>\n  </tr>\n</table>\n\n\nFor example:\n```json\n[\n  {\n    \"page\": \"./src/MainApp\",\n    \"routes\": [\n      {\n        \"path\": \"/\",\n        \"page\": \"./src/Search\",\n        \"exact\": true\n      },\n      {\n        \"path\": \"/search\",\n        \"page\": \"./src/Search\",\n        \"routes\": [\n          {\n            \"path\": \"/search/hello\",\n            \"page\": \"./src/Detail\",\n            \"exact\": true\n          }\n        ]\n      },\n      {\n        \"path\": \"/detail\",\n        \"page\": \"./src/Detail\",\n        \"exact\": true\n      }\n    ]\n  }\n]\n```\n\n## Dynamic Import Component\nMaleo.js supports TC39 [dynamic import proposal](https://github.com/tc39/proposal-dynamic-import) for JavaScript.\n\nYou can think dynamic import as another way to split your code into manageable chunks. You can use our `Dynamic` function which utilizes [react loadable](https://github.com/jamiebuilds/react-loadable)\n\nFor Example\n```js\n// DynamicComponent.js\nimport Dynamic from '@airy/maleo/dynamic';\n\nexport default Dynamic({\n  loader: () => import( /* webpackChunkName:\"DynamicComponent\" */ './component/DynamicLoad'),\n})\n```\n\n### **Preloading**\n\nFor optimization purposes, you can also preload a component even before the component got rendered.\n\nFor example, if you want to load component when a button get pressed, you can start preloading the component when the user hovers the mouse over the button.\n\nThe component created by `Dynamic` exposes a [static `preload` method](https://github.com/jamiebuilds/react-loadable#loadablecomponentpreload).\n\n```jsx\nimport React from 'react';\nimport Dynamic from '@airy/maleo/dynamic';\n\nconst DynamicBar = Dynamic({\n  loader: () => import('./Bar'),\n  loading: LoadingCompoinent\n});\n\nclass MyComponent extends React.Component {\n  state = { showBar: false };\n\n  onClick = () => {\n    this.setState({ showBar: true });\n  };\n\n  onMouseOver = () => DynamicBar.preload();\n\n  render() {\n    return (\n      <div>\n        <button\n          onClick={this.onClick}\n          onMouseOver={this.onMouseOver}>\n          Show Bar\n        </button>\n        { this.state.showBar && <DynamicBar /> }\n      </div>\n    )\n  }\n}\n```\n\n## Customizable Component\n\n### Custom Document\n\nHighly inspired by what [Next.js](https://github.com/zeit/next.js) has done on their awesome template customization.\n\nMaleo.js also enable customization on `Document` as document's markup. So you don't need to include tags like `<html>`, `<head>`, `<body>`, etc. \n\nTo override the default behavior, you'll need to create a component that extends the `Document` React class provided by Maleo.\n\n```jsx\n// document.jsx\nimport React from 'react';\nimport { Document, Header, Main, Scripts } from '@airy/maleo/document';\n\nexport default class extends Document {\n  render() {\n    return (\n      <html>\n        <Header>\n          <title>Maleo JS</title>\n          <meta charset=\"utf-8\" />\n          <meta name=\"description\" content=\"Maleo.js is awesome!\" />\n\n          <style>\n            {` body { background-color: #fff } `}\n          </style>\n        </Header>\n\n        <body>\n          <Main />\n\n          <Scripts />\n\n          <ReduxScript />\n        </body>\n      </html>\n    );\n  }\n}\n```\n\n### Custom Wrap\n\nMaleo.js uses the `Wrap` component to initialize pages. `Wrap` contains React Router's Component. You can add HoC here to wrap the application and control the page initialization. Which allows you to do amazing things like:\n- Persisting layour between page changes\n- Keeping state when navigating pages\n- Custom error handling using `componentDidCatch`\n- Inject additional data into pages (like Redux provider, etc)\n\nTo override the default behavior, you'll need to create a component that extends the `Wrap` React class provided by Maleo.\n\n```jsx\n// wrap.jsx\nimport React from 'react';\nimport { Wrap } from '@airy/maleo/wrap';\n\n// Redux plugin for Maleo.js\n// Hoc that creates store and add Redux Provider\nimport { withRedux } from '@airy/maleo-redux-plugin';\n\n// Custom Wrapper that will be rendered for the whole Application\nimport CustomWrapper from './component/CustomWrapper';\n\nimport { createStore } from './store';\n\n@withRedux(createStore)\nexport default class extends Wrap {\n  static getInitialProps = (ctx) => {\n    const { store } = ctx\n    // you receive store from context\n    // you can access or do something with the store here\n    console.log('Initialized Redux Store', store);\n    return {}\n  }\n\n  render() {\n    return (\n      <CustomWrapper>\n        {super.render()}\n      </CustomWrapper>\n    )\n  }\n}\n\n```\nIf you put `document.jsx` and `wrap.jsx` on root directory (the same level with `package.json`), then Maleo will automatically register your custom Document and Wrap. Otherwise, you can add the path to your custom Document and Wrap on [Maleo config](#custom-configuration)\n\n---\n***We are also working on adding default and customizable `Error` component page***\n\n---\n\n## Custom Configuration\n\nFor more advanced configuration of Maleo.js, like `webpack` config, registering `plugins`, path to your routes, custom Document and Wrap, and adding `path alias`, you can create a `maleo.config.js` in the root of your project directory. (same directory with `package.json`)\n\n```js\n// maleo.config.js\n\nmodule.exports = {\n  /* config options here */\n}\n```\n\nHere are the API's for the configuration:\n\n<table>\n  <tr>\n    <td>Key</td>\n    <td>Type</td>\n    <td>Description</td>\n  </tr>\n  <tr>\n    <td><code>buildDir</code></td>\n    <td><code>String?</code> [<code>.maleo</code>]</td>\n    <td>Directory to put Maleo.js' build assets</td>\n  </tr>\n  <tr>\n    <td><code>cache</code></td>\n    <td><code>Boolean?</code> [<code>true</code>]</td>\n    <td>Enable webpack build caching</td>\n  </tr>\n  <tr>\n    <td><code>sourceMaps</code></td>\n    <td><code>Boolean?</code> [<code>true</code>]</td>\n    <td>Enable webpack to generate source maps</td>\n  </tr>\n  <tr>\n    <td><code>alias</code></td>\n    <td><code>Object?</code></td>\n    <td>A key value pair for aliasing path directory\n      <br>\n      <code>\n      { 'component': './src/component' }\n      </code>\n    </td>\n  </tr>\n  <tr>\n    <td><code>publicPath</code></td>\n    <td><code>String?</code> [<code>/_assets/</code>]</td>\n    <td>To customize webpack's <code>publicPath</code> \n    <br>Comes in handy if using CDN to put built assets</td>\n  </tr>\n  <tr>\n    <td><code>analyzeBundle</code></td>\n    <td><code>Boolean?</code> [<code>false</code>]</td>\n    <td>To enable webpack's bundle analyzer, for analyzing bundle sizes during bundle debugging should Maleo.js' build process got slow</td>\n  </tr>\n  <tr>\n    <td><code>webpack</code></td>\n    <td><code>Function?</code></td>\n    <td>To customize webpack configuration, more details <a href=\"#customize-webpack\">here</a></td>\n  </tr>\n  <tr>\n    <td><code>routes</code></td>\n    <td><code>string?</code> [<code>rootDir/routes.jsx</code>]</td>\n    <td>Path to your routes file</td>\n  </tr>\n  <tr>\n    <td><code>customDocument</code></td>\n    <td><code>string?</code> [<code>rootDir/document.jsx</code>]</td>\n    <td>Path to your custom document file</td>\n  </tr>\n  <tr>\n    <td><code>customWrap</code></td>\n    <td><code>string?</code> [<code>rootDir/wrap.jsx</code>]</td>\n    <td>Path to your custom wrap file</td>\n  </tr>\n  <tr>\n    <td><code>customApp</code></td>\n    <td><code>string?</code> [<code>rootDir/app.jsx</code>]</td>\n    <td>Path to your custom app file</td>\n  </tr>\n</table>\n\n#### Customize Server\n\nCreate a `server.js` file on root directory where your `package.json` lives.\nHere you can customize Maleo's server.\n```js\nimport { Server } from '@airy/maleo/server';\nimport path from 'path';\n\nimport routeConfig from './routes';\n\nconst PORT = process.env.PORT || 8080;\n\nconst maleoServer = Server.init({\n  port: PORT,\n});\n\nmaleoServer.run(() => {\n  console.log('Server running on port :', PORT);\n});\n```\n\nHere are the API's for the configuration:\n\n<table>\n  <tr>\n    <td>Key</td>\n    <td>Type</td>\n    <td>Description</td>\n  </tr>\n  <tr>\n    <td><code>port</code></td>\n    <td><code>Number?</code> [<code>3000</code>]</td>\n    <td>Port to run Maleo server</td>\n  </tr>\n  <tr>\n    <td><code>assetDir</code></td>\n    <td><code>String?</code> [<code>'<root>/.maleo/client'</code>]</td>\n    <td>Directory for all client related assets</td>\n  </tr>\n</table>\n\n#### Customize Webpack\n\nYou are able to extend Maleo.js' default webpack configuration by defining a function on `maleo.config.js`\n\n```js\n// maleo.config.js\n\nmodule.exports = {\n  webpack(config, context, next) {\n    // Perform customizations to webpack config\n    // Important: This call is required in order to let Maleo pass the config to webpack\n    return next(); \n  },\n};\n```\n\nWebpack function will receive three arguments:\n\n<table>\n  <tr>\n    <td>Argument</td>\n    <td>Details</td>\n  <tr>\n  <tr>\n    <td><code>config</code></td>\n    <td>This contains webpack configuration object that you can manipulate</td>\n  <tr>\n  <tr>\n    <td><code>context</code></td>\n    <td>This contains some keys that are useful for the build context\n      <br>\n      <table>\n        <tr>\n          <td><code>isDev</code></td>\n          <td><code>Boolean</code></td>\n          <td>Check if current build is for development</td>\n        </tr>\n        <tr>\n          <td><code>publicPath</code></td>\n          <td><code>String</code></td>\n          <td>Public Path of user defined or default's value</td>\n        </tr>\n        <tr>\n          <td><code>analyzeBundle</code></td>\n          <td><code>Boolean</code></td>\n          <td>Check if analyze bundle is enabled</td>\n        </tr>\n        <tr>\n          <td><code>buildDirectory</code></td>\n          <td><code>String</code></td>\n          <td>Build Directory of user defined or default's value</td>\n        </tr>\n        <tr>\n          <td><code>name</code></td>\n          <td><code>String</code></td>\n          <td>Build name <code>'server' || 'client'</code></td>\n        </tr>\n      </table>\n    </td>\n  <tr>\n  <tr>\n    <td><code>next</code></td>\n    <td>A callback required to be called to pass the custom configuration</td>\n  <tr>\n</table>\n\nExample of adding `ts-loader` through `maleo.config.js`:\n\n```js\n// maleo.config.js\n\n// Partly taken and modified from @airy/maleo-ts-plugin source\n// for simplicity purposes\n\nmodule.exports = {\n  webpack(config, context, next) {\n    const { isDev } = context\n\n    config.module.rules.push({\n      test: /\\.tsx?/,\n      exclude: /node_modules/,\n      use: [\n        require.resolve('@airy/maleo/lib/build/loaders/maleo-babel-loader'),\n        {\n          loader: 'ts-loader',\n          options: {\n            transpileOnly: true,\n          },\n        },\n      ],\n\n      if (isDev) {\n        config.plugins.push(new ForkTSCheckerWebpackPlugin());\n      }\n\n      return next();\n    })\n  },\n};\n```\n\n#### Customize Babel Config\n\nMaleo.js also let you have your own babel config. Just simply add `.babelrc` file at the root directory of your app.\n\nYou can include Maleo.js' babel preset in order to have latest JavaScript preset.\n\nHere's an example of `.babelrc` file:\n```json\n{\n  \"presets\": [\"@airy/maleo/babel\"],\n  \"plugins\": []\n}\n```\n\nThe `@airy/maleo/babel` preset includes everything you need to get your development started. The preset includes:\n- `@babel/preset-env`\n- `@babel/preset-react`\n- `@babel/plugin-proposal-class-properties`\n- `@babel/plugin-proposal-decorators`\n- `@babel/plugin-proposal-object-rest-spread`\n- `@babel/plugin-transform-runtime`\n- `react-loadable/babel`\n\n## CDN Support\n\nIf you are using a CDN, you can set up the `publicPath` setting and configure your CDN's origin to resolve to the domain that Maleo.js is hosted on.\n\n```js\n// maleo.config.js\n\nconst isProd = process.env.NODE_ENV === 'production';\n\nmodule.exports = {\n  assetPrefix: isProd && 'https://cdn.example.com';\n}\n```\n\n## FAQ\n\n== TO BE DETERMINED == \n\n## Plugins\n- [css-plugin](https://github.com/airyrooms/maleo-plugins/tree/master/packages/css-plugin)\n- [redux-plugin](https://github.com/airyrooms/maleo-plugins/tree/master/packages/redux-plugin)\n- [typescript-plugin](https://github.com/airyrooms/maleo-plugins/tree/master/packages/typescript-plugin)\n- [more](https://github.com/airyrooms/maleo-plugins)\n\n\n## Contributing\n[Please follow these steps to contribute to Maleo.js](https://github.com/airyrooms/maleo.js/blob/canary/CONTRIBUTING.md)\n\n[Please follow these steps to contribute to Maleo.js' plugins](https://github.com/airyrooms/maleo-plugins#contributing-guidelines)\n\n\n## Contributors\n\nThis project exists thanks to all the people who contribute. [Contribute](CONTRIBUTING.md).\n\nMany thanks to our **[contributors](https://github.com/airyrooms/maleo.js/graphs/contributors)**!\n\n\n## License\n\nMIT","readmeFilename":"README.md","bugs":{"url":"https://github.com/airyrooms/maleo.js/issues"},"homepage":"https://github.com/airyrooms/maleo.js#readme","_id":"@airy/maleo@0.1.2-canary.0","_nodeVersion":"10.15.3","_npmVersion":"lerna/3.13.1/node@v10.15.3+x64 (linux)","dist":{"integrity":"sha512-QpHiHgKwAGAY5VtfiCTworGwbX3ekhSEG0oJPUkbA+jTkHbCqKMlJHsACrmMscJKZApXYvLn/+133HDge2J31Q==","shasum":"431776324961a9bef35e4e7d98559fdda8922667","tarball":"https://registry.npmjs.org/@airy/maleo/-/maleo-0.1.2-canary.0.tgz","fileCount":85,"unpackedSize":254309,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJcpXBTCRA9TVsSAnZWagAA16kP/3mhKtm2MTJzWlQWWKOx\nuQM8b5b+YR1xkSPdCu+0+iw/1O4eYjkgGNN+KdzEX/UBdI6kA7nRexg2X2oE\nwDJEx3d5LGpGBefPKV9FkEMpqfArAwetSFNeWL8czZDxtatgA7Fs3zp1igrL\noB9GBO7v0FMM32Le0owPrx+K66wO9eVm0YThPy8y5aQ7GFDj2V/PM3LilW2/\ndXK6n9mf5Xtz8jZtYQtydHxZ8OU//nRxxI4uIESVnCoaVdcH0yn9P/NTUAps\nzC4nsf9z9MPjCcFNKNeAKy8uv65/tbIfRZ7Vxbi9P9xgnq8P5sprCdNUutxJ\noIM60z0RiWWpfxctc05wdtsgDY/kPwMkljP2jsLz0/mH0RiriQlUXkRIICld\nrGs5jWA/aA8IkcL9ZAhHtsyQ4tLUno1BkXaczPT20TuMph+VS07iCG52U8Up\ni1n2Qfn9CpGeDoO0NIfDLJh/yMR0cIxJ2QDUg2999CvVAU3kZLdMDX29ZW+Q\neUMeOS3JwWTGLpQhq6yDaXkBtYYvRiwUyZd7KRHS3kOT/xlDWj4gEzVEV2lM\nC3Sba/6wHl/Flqfxosthfm0JvfDwq4uYQPCvbIjKZxokXuOwmg1npdpthxV0\nbFREowLqQyfCrbFc7WHsay1QaDl8bbd1XzrvW9rfCfUDzWPooPLSpqcIxjx9\n1YaT\r\n=BRdg\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDlXWBdktqZoiLtXXTJQQEVs2xcZUSdAx1gpuc6sBek7AIhAPgpoUqR0zAl4JH+4OW//tjF+XxmrA3T728P6EOXD79B"}]},"maintainers":[{"name":"airyrooms-engineering","email":"engineering@airyrooms.com"}],"_npmUser":{"name":"airyrooms-engineering","email":"engineering@airyrooms.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/maleo_0.1.2-canary.0_1554346066015_0.5262159047028372"},"_hasShrinkwrap":false},"0.2.0":{"name":"@airy/maleo","version":"0.2.0","description":"Un-opinionated Universal Rendering Javascript Framework","repository":{"type":"git","url":"git+https://github.com/airyrooms/maleo.js.git","bugs":"https://github.com/airyrooms/maleo.js/issues"},"author":{"name":"Airy"},"license":"MIT","private":false,"sideEffects":false,"scripts":{"build":"rm -rf dist && gulp","watch":"gulp watch","prepublish":"yarn build","start":"node server.js"},"bin":{"maleo":"lib/bin/maleo.js"},"dependencies":{"@babel/core":"7.1.2","@babel/plugin-proposal-class-properties":"7.1.0","@babel/plugin-proposal-decorators":"7.1.2","@babel/plugin-proposal-object-rest-spread":"7.0.0","@babel/plugin-syntax-dynamic-import":"7.0.0","@babel/plugin-transform-runtime":"7.1.0","@babel/preset-env":"7.1.0","@babel/preset-react":"7.0.0","@babel/runtime":"7.1.2","@babel/runtime-corejs2":"7.2.0","babel-loader":"8.0.4","case-sensitive-paths-webpack-plugin":"2.1.2","compression":"1.7.4","es6-promise":"4.2.5","eventsource":"1.0.7","express":"4.16.4","figlet":"1.2.1","friendly-errors-webpack-plugin":"1.7.0","hard-source-webpack-plugin":"0.13.1","helmet":"3.14.0","isomorphic-fetch":"2.2.1","loader-utils":"1.1.0","node-notifier":"5.2.1","nodemon":"1.18.4","react-dom":"16.8.4","react-helmet":"5.2.0","react-loadable":"5.5.0","react-router-config":"5.0.0","react-router-dom":"5.0.0","rimraf":"2.6.3","source-map-support":"0.5.9","terser-webpack-plugin":"1.1.0","webpack":"4.29.0","webpack-bundle-analyzer":"3.0.3","webpack-cli":"3.1.2","webpack-dev-middleware":"3.4.0","webpack-hot-middleware":"2.24.3","webpackbar":"2.6.3"},"peerDependencies":{"react":"^16.8.4"},"devDependencies":{"@babel/preset-typescript":"^7.1.0","@types/webpack-bundle-analyzer":"^2.13.0","babel-plugin-module-resolver":"^3.1.1","gulp":"github:gulpjs/gulp#4.0","gulp-babel":"^8.0.0","gulp-clean":"^0.4.0","gulp-sourcemaps":"^2.6.4","gulp-uglify":"^3.0.1","gulp-watch":"^5.0.1","react":"^16.8.4"},"gitHead":"a94e677b83a9663b3a78fe2711154d6f939296d4","bugs":{"url":"https://github.com/airyrooms/maleo.js/issues"},"homepage":"https://github.com/airyrooms/maleo.js#readme","_id":"@airy/maleo@0.2.0","_nodeVersion":"10.11.0","_npmVersion":"lerna/3.13.1/node@v10.11.0+x64 (darwin)","dist":{"integrity":"sha512-0Zq+p18Z247B6V/y6uwG2omns6Of94Yy0+s6FGMr2xBFZer1cL/ZBCr0HaLtnRIYsnge6LJB2pbTuk69Yz2pAw==","shasum":"0588ae0f88d68698266b44fcdf1a903715d37fb8","tarball":"https://registry.npmjs.org/@airy/maleo/-/maleo-0.2.0.tgz","fileCount":115,"unpackedSize":337115,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJcqGJICRA9TVsSAnZWagAAnt0P/RtmDQUlwdaulB3XxsBP\nrTuVgLxTFWqfDdKAInv0GaTMrjjTTGz2oCSITEh+F6cAvJrX1DUPFTo6LU7z\nW3mV54tYqaKDe9iPsLE0T90i22I2XO1xlzmB/2F7dMrQZz+BoQf6JrltD5UH\n88XLbYQKiQWCYkZ3es9F7z/9UPZcWMKIZMJeUPHb1OTKOgRXw5bm0FPlgBKH\n4GSbkSwNrsofgymE4GxZOivbJfLJgfgQ/HDoZMWofsjc6vzy7ij1VysufyAY\nbSd/sOfHHkIgNqflQTXhI7u9lB6W1qjOEwKDGtfob5BCjXteBcyiBurhECJB\n+csKrDU5hQN6eJF92IWEDSM/B/XLH2KXiPJYVOm4d4OnXC05YpqKdvsV/J31\nG+Mzf+t26gFO0p6qzBnH0OO/yP1quIARQsvZzwZA0nEflzwf2QsN65IwE0+p\nHcFv8ACMLVIRP9VABHH74kdrrL9uIcuJMeNuswXecBCFCfB3IZoiWIjVkleX\nmdjxafQYjarxgO96M12MJILr/Ilbk6v5UDbfOgaNdvQiQiSnA0vPGOeXtxkE\n+vK05E3nBmk7KlxQKgd/lFiEyDXe112QBSCpMcqzc6iadoqZw+GJ3KwQSEbF\nN9QL5w9QPnWDl3QAWH/ROMEPyVPQyoWm/UPm1WXxpeCU/+UvS+UcUj21AI+c\nF169\r\n=yZjn\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIBf9HWVc/X/61qEiAkOkr6tl07txquWfARGNI4ej6SR8AiAEcTI22TqrKfFe5rqEQXLOWh4ZaIyfA0il4b8c6HxfBg=="}]},"maintainers":[{"name":"airyrooms-engineering","email":"engineering@airyrooms.com"}],"_npmUser":{"name":"airyrooms-engineering","email":"engineering@airyrooms.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/maleo_0.2.0_1554539079565_0.4823582188140756"},"_hasShrinkwrap":false},"0.2.1-canary.0":{"name":"@airy/maleo","version":"0.2.1-canary.0","description":"Un-opinionated Universal Rendering Javascript Framework","repository":{"type":"git","url":"git+https://github.com/airyrooms/maleo.js.git","bugs":"https://github.com/airyrooms/maleo.js/issues"},"author":{"name":"Airy"},"license":"MIT","private":false,"sideEffects":false,"scripts":{"build":"rm -rf dist && gulp","watch":"gulp watch","prepublish":"yarn build","start":"node server.js"},"bin":{"maleo":"lib/bin/maleo.js"},"dependencies":{"@babel/core":"7.1.2","@babel/plugin-proposal-class-properties":"7.1.0","@babel/plugin-proposal-decorators":"7.1.2","@babel/plugin-proposal-object-rest-spread":"7.0.0","@babel/plugin-syntax-dynamic-import":"7.0.0","@babel/plugin-transform-runtime":"7.1.0","@babel/preset-env":"7.1.0","@babel/preset-react":"7.0.0","@babel/runtime":"7.1.2","@babel/runtime-corejs2":"7.2.0","babel-loader":"8.0.4","case-sensitive-paths-webpack-plugin":"2.1.2","compression":"1.7.4","es6-promise":"4.2.5","eventsource":"1.0.7","express":"4.16.4","figlet":"1.2.1","friendly-errors-webpack-plugin":"1.7.0","hard-source-webpack-plugin":"0.13.1","helmet":"3.14.0","isomorphic-fetch":"2.2.1","loader-utils":"1.1.0","node-notifier":"5.2.1","nodemon":"1.18.4","react-dom":"16.8.4","react-helmet":"5.2.0","react-loadable":"5.5.0","react-router-config":"5.0.0","react-router-dom":"5.0.0","rimraf":"2.6.3","source-map-support":"0.5.9","terser-webpack-plugin":"1.1.0","webpack":"4.29.0","webpack-bundle-analyzer":"3.0.3","webpack-cli":"3.1.2","webpack-dev-middleware":"3.4.0","webpack-hot-middleware":"2.24.3","webpackbar":"2.6.3"},"peerDependencies":{"react":"^16.8.4"},"devDependencies":{"@babel/preset-typescript":"^7.1.0","@types/webpack-bundle-analyzer":"^2.13.0","babel-plugin-module-resolver":"^3.1.1","gulp":"github:gulpjs/gulp#4.0","gulp-babel":"^8.0.0","gulp-clean":"^0.4.0","gulp-sourcemaps":"^2.6.4","gulp-uglify":"^3.0.1","gulp-watch":"^5.0.1","react":"^16.8.4"},"gitHead":"2c1d3ad0a00f610f0cc6b6895f35e53038e5893d","readme":"[![Build Status](https://travis-ci.org/airyrooms/maleo.js.svg?branch=master)](https://travis-ci.org/airyrooms/maleo.js)\n[![Coverage Status](https://coveralls.io/repos/github/airyrooms/maleo.js/badge.svg?branch=master)](https://coveralls.io/github/airyrooms/maleo.js?branch=master)\n[![npm version](https://badge.fury.io/js/%40airy%2Fmaleo.svg)](https://badge.fury.io/js/%40airy%2Fmaleo)\n[![Issues](http://img.shields.io/github/issues/airyrooms/maleo.js.svg)]( https://github.com/airyrooms/maleo.js/issues)\n[![MIT license](https://img.shields.io/badge/License-MIT-blue.svg)](https://github.com/airyrooms/maleo.js/blob/canary/LICENSE)\n[![Discord Chat](https://img.shields.io/discord/550498214789513218.svg)](https://discord.gg/9eArCQn)\n[![Conventional Commits](https://img.shields.io/badge/Conventional%20Commits-1.0.0-green.svg)](https://conventionalcommits.org)\n\n\n# Welcome to Maleo.js\n\nMaleo.js is an un-opinionated framework to enable Universal Rendering in JavaScript using React with no hassle.\n\nWe are here to solve the time consuming setups Universal Rendering Required.\n\n---\n\nReadme below is the documentation for the `canary` (prerelease) branch. To view the documentation for the latest stable Maleo.js version change branch to `master`\n\n---\n\n# Table of Contents\n- [Features](#features)\n- [Setup](#setup)\n- [Component Lifecycle](#component-lifecycle)\n- [Routing](#routing)\n- [Dynamic Import Component](#dynamic-import-component)\n  - [Tips](#tips)\n  - [Preloading](#preloading)\n- [Customizable Component](#customizable-component)\n  - [Custom Document](#custom-document)\n  - [Custom Wrap](#custom-wrap)\n- [Custom Configuration](#custom-configuration)\n  - [Customize Server](#customize-server)\n  - [Customize Webpack](#customize-webpack)\n  - [Customize Babel Config](#customize-babel-config)\n- [CDN Support](#cdn-support)\n- [Plugins](#plugins)\n- [FAQ](#faq)\n- [Contributing](#contributing)\n\n---\n\n## Features\n- Universal Rendering\n- Plugin based framework\n- Customizable\n\n## Setup\n\nInstall Maleo.js \n\n>*for now change `@airy/maleo` to `@airy/maleo@canary` until we publish the stable version of this package*\n\n\n**NPM**\n```bash\n$ npm install --save @airy/maleo react\n```\n**Yarn**\n```bash\n$ yarn add @airy/maleo react\n```\n\nAdd this script to your `package.json`\n```json\n{\n  \"scripts\": {\n    \"dev\": \"maleo dev\",\n    \"build\": \"export NODE_ENV=production && maleo build\",\n    \"start\": \"export NODE_ENV=production && node .maleo/server.js\"\n  }\n}\n```\n\nCreate a page Root component\n```jsx\n// ./src/Root.jsx\nimport React from 'react';\n\n// Export default is required for registering page\nexport default class RootComponent extends React.Component {\n  render() {\n    return (\n      <h1>Hello World!</h1>\n    )\n  }\n}\n```\n\nAnd lastly, create a routing file on your project root directory called `routes.json` and register your page component\n```json\n[\n  {\n    \"path\": \"/\",\n    \"page\": \"./src/Root\"\n  }\n]\n```\n\nAfter that you can now run `$ npm run dev` and go to `http://localhost:3000`.\n\nYou should now see your app running on your browser.\n\nBy now you should see \n- Automatic transpilation and bundling (with webpack and babel)\n- ~~Hot code reloading~~ [#17](https://github.com/airyrooms/maleo.js/issues/17)\n- Server rendering\n\nTo see how simple this is, check out the sample app!\n\n## Component Lifecycle \n\nMaleo.js added a new component lifecycle hook called `getInitialProps`, this function is called during Server Side Rendering (SSR).\n\nThis is useful especially for SEO purposes. \n\nExample for stateful component:\n```jsx\nimport React from 'react';\n\nexport default class extends React.Component {\n  static getInitialProps = async (ctx) => {\n    const { req } = ctx;\n\n    const userAgent = req ? req.headers['user-agent'] : navigator.userAgent;\n\n    // the return value will be passed as props for this component\n    return { userAgent };\n  }\n\n  render() {\n    return (\n      <div>\n        Hello World {this.props.userAgent}\n      </div>\n    );\n  }\n}\n```\n\nExample for stateless component:\n```jsx\nconst Component = (props) => (\n  <div>\n    Hello World {props.userAgent}\n  </div>\n);\n\nComponent.getInitialprops = async (ctx) => {\n  const { req } = ctx;\n\n  const userAgent = req ? req.headers['user-agent'] : navigator.userAgent;\n\n  return { userAgent };\n};\n\nexport default Component;\n```\n\n`getInitialProps` receives a context object with the following properties:\n- `req` - HTTP request object (server only)\n- `res` - HTTP response object (server only)\n- `...wrapProps` - Spreaded properties from custom Wrap\n- `...appProps` - Spreaded properties from custom App\n\n## Routing\n\nRouting is declared in a centralized route config.\nRegister all the route config in `routes.json` file.\n\nIf you put the `routes.json` files on root directory, Maleo will automatically register your route. Otherwise put path to your routes on [Maleo config](#custom-configuration).\n\nRoutes file has to export default the route configuration.\nThe route object **expected to have distinct key** to indicate the route.\n\n<table>\n  <tr>\n    <td>Key</td>\n    <td>Type</td>\n    <td>Description</td>\n  </tr>\n  <tr>\n    <td><code>path</code></td>\n    <td><code>String!</code></td>\n    <td>Routes path</td>\n  </tr>\n  <tr>\n    <td><code>page</code></td>\n    <td><code>String!</code></td>\n    <td>Path to React Component for this route path</td>\n  </tr>\n  <tr>\n    <td><code>exact</code></td>\n    <td><code>Boolean?</code> [<code>false</code>]</td>\n    <td>To make url has to match exactly the path in order to render the component. Give <code>false</code> value if the route is a wrapper route component</td>\n  </tr>\n  <tr>\n    <td><code>routes</code></td>\n    <td><code>RouteObject?</code></td>\n    <td>Nested route</td>\n  </tr>\n</table>\n\n\nFor example:\n```json\n[\n  {\n    \"page\": \"./src/MainApp\",\n    \"routes\": [\n      {\n        \"path\": \"/\",\n        \"page\": \"./src/Search\",\n        \"exact\": true\n      },\n      {\n        \"path\": \"/search\",\n        \"page\": \"./src/Search\",\n        \"routes\": [\n          {\n            \"path\": \"/search/hello\",\n            \"page\": \"./src/Detail\",\n            \"exact\": true\n          }\n        ]\n      },\n      {\n        \"path\": \"/detail\",\n        \"page\": \"./src/Detail\",\n        \"exact\": true\n      }\n    ]\n  }\n]\n```\n\n## Dynamic Import Component\nMaleo.js supports TC39 [dynamic import proposal](https://github.com/tc39/proposal-dynamic-import) for JavaScript.\n\nYou can think dynamic import as another way to split your code into manageable chunks. You can use our `Dynamic` function which utilizes [react loadable](https://github.com/jamiebuilds/react-loadable)\n\nFor Example\n```js\n// DynamicComponent.js\nimport Dynamic from '@airy/maleo/dynamic';\n\nexport default Dynamic({\n  loader: () => import( /* webpackChunkName:\"DynamicComponent\" */ './component/DynamicLoad'),\n})\n```\n\n### **Preloading**\n\nFor optimization purposes, you can also preload a component even before the component got rendered.\n\nFor example, if you want to load component when a button get pressed, you can start preloading the component when the user hovers the mouse over the button.\n\nThe component created by `Dynamic` exposes a [static `preload` method](https://github.com/jamiebuilds/react-loadable#loadablecomponentpreload).\n\n```jsx\nimport React from 'react';\nimport Dynamic from '@airy/maleo/dynamic';\n\nconst DynamicBar = Dynamic({\n  loader: () => import('./Bar'),\n  loading: LoadingCompoinent\n});\n\nclass MyComponent extends React.Component {\n  state = { showBar: false };\n\n  onClick = () => {\n    this.setState({ showBar: true });\n  };\n\n  onMouseOver = () => DynamicBar.preload();\n\n  render() {\n    return (\n      <div>\n        <button\n          onClick={this.onClick}\n          onMouseOver={this.onMouseOver}>\n          Show Bar\n        </button>\n        { this.state.showBar && <DynamicBar /> }\n      </div>\n    )\n  }\n}\n```\n\n## Customizable Component\n\n### Custom Document\n\nHighly inspired by what [Next.js](https://github.com/zeit/next.js) has done on their awesome template customization.\n\nMaleo.js also enable customization on `Document` as document's markup. So you don't need to include tags like `<html>`, `<head>`, `<body>`, etc. \n\nTo override the default behavior, you'll need to create a component that extends the `Document` React class provided by Maleo.\n\n```jsx\n// document.jsx\nimport React from 'react';\nimport { Document, Header, Main, Scripts } from '@airy/maleo/document';\n\nexport default class extends Document {\n  render() {\n    return (\n      <html>\n        <Header>\n          <title>Maleo JS</title>\n          <meta charset=\"utf-8\" />\n          <meta name=\"description\" content=\"Maleo.js is awesome!\" />\n\n          <style>\n            {` body { background-color: #fff } `}\n          </style>\n        </Header>\n\n        <body>\n          <Main />\n\n          <Scripts />\n\n          <ReduxScript />\n        </body>\n      </html>\n    );\n  }\n}\n```\n\n### Custom Wrap\n\nMaleo.js uses the `Wrap` component to initialize pages. `Wrap` contains React Router's Component. You can add HoC here to wrap the application and control the page initialization. Which allows you to do amazing things like:\n- Persisting layour between page changes\n- Keeping state when navigating pages\n- Custom error handling using `componentDidCatch`\n- Inject additional data into pages (like Redux provider, etc)\n\nTo override the default behavior, you'll need to create a component that extends the `Wrap` React class provided by Maleo.\n\n```jsx\n// wrap.jsx\nimport React from 'react';\nimport { Wrap } from '@airy/maleo/wrap';\n\n// Redux plugin for Maleo.js\n// Hoc that creates store and add Redux Provider\nimport { withRedux } from '@airy/maleo-redux-plugin';\n\n// Custom Wrapper that will be rendered for the whole Application\nimport Content from './component/Content';\nimport NavigationBar from './component/NavigationBar';\n\nimport { createStore } from './store';\n\n@withRedux(createStore)\nexport default class extends Wrap {\n  static getInitialProps = (ctx) => {\n    const { store } = ctx\n    // you receive store from context\n    // you can access or do something with the store here\n    console.log('Initialized Redux Store', store);\n    return {}\n  }\n\n  render() {\n    const { Container, containerProps, App, appProps } = this.props\n\n    return (\n      <Container {...containerProps}>\n        <NavigationBar />\n        <Content>\n          <App {...appProps}/>\n        </Content>\n      </Container>\n    )\n  }\n}\n\n```\nIf you put `document.jsx` and `wrap.jsx` on root directory (the same level with `package.json`), then Maleo will automatically register your custom Document and Wrap. Otherwise, you can add the path to your custom Document and Wrap on [Maleo config](#custom-configuration)\n\n---\n***We are also working on adding default and customizable `Error` component page***\n\n---\n\n## Custom Configuration\n\nFor more advanced configuration of Maleo.js, like `webpack` config, registering `plugins`, path to your routes, custom Document and Wrap, and adding `path alias`, you can create a `maleo.config.js` in the root of your project directory. (same directory with `package.json`)\n\n```js\n// maleo.config.js\n\nmodule.exports = {\n  /* config options here */\n}\n```\n\nHere are the API's for the configuration:\n\n<table>\n  <tr>\n    <td>Key</td>\n    <td>Type</td>\n    <td>Description</td>\n  </tr>\n  <tr>\n    <td><code>buildDir</code></td>\n    <td><code>String?</code> [<code>.maleo</code>]</td>\n    <td>Directory to put Maleo.js' build assets</td>\n  </tr>\n  <tr>\n    <td><code>cache</code></td>\n    <td><code>Boolean?</code> [<code>true</code>]</td>\n    <td>Enable webpack build caching</td>\n  </tr>\n  <tr>\n    <td><code>sourceMaps</code></td>\n    <td><code>Boolean?</code> [<code>true</code>]</td>\n    <td>Enable webpack to generate source maps</td>\n  </tr>\n  <tr>\n    <td><code>alias</code></td>\n    <td><code>Object?</code></td>\n    <td>A key value pair for aliasing path directory\n      <br>\n      <code>\n      { 'component': './src/component' }\n      </code>\n    </td>\n  </tr>\n  <tr>\n    <td><code>publicPath</code></td>\n    <td><code>String?</code> [<code>/_assets/</code>]</td>\n    <td>To customize webpack's <code>publicPath</code> \n    <br>Comes in handy if using CDN to put built assets</td>\n  </tr>\n  <tr>\n    <td><code>analyzeBundle</code></td>\n    <td><code>Boolean?</code> [<code>false</code>]</td>\n    <td>To enable webpack's bundle analyzer, for analyzing bundle sizes during bundle debugging should Maleo.js' build process got slow</td>\n  </tr>\n  <tr>\n    <td><code>webpack</code></td>\n    <td><code>Function?</code></td>\n    <td>To customize webpack configuration, more details <a href=\"#customize-webpack\">here</a></td>\n  </tr>\n  <tr>\n    <td><code>routes</code></td>\n    <td><code>string?</code> [<code>rootDir/routes.jsx</code>]</td>\n    <td>Path to your routes file</td>\n  </tr>\n  <tr>\n    <td><code>customDocument</code></td>\n    <td><code>string?</code> [<code>rootDir/document.jsx</code>]</td>\n    <td>Path to your custom document file</td>\n  </tr>\n  <tr>\n    <td><code>customWrap</code></td>\n    <td><code>string?</code> [<code>rootDir/wrap.jsx</code>]</td>\n    <td>Path to your custom wrap file</td>\n  </tr>\n  <tr>\n    <td><code>customApp</code></td>\n    <td><code>string?</code> [<code>rootDir/app.jsx</code>]</td>\n    <td>Path to your custom app file</td>\n  </tr>\n</table>\n\n#### Customize Server\n\nCreate a `server.js` file on root directory where your `package.json` lives.\nHere you can customize Maleo's server.\n```js\nimport { Server } from '@airy/maleo/server';\nimport path from 'path';\n\nimport routeConfig from './routes';\n\nconst PORT = process.env.PORT || 8080;\n\nconst maleoServer = Server.init({\n  port: PORT,\n});\n\nmaleoServer.run(() => {\n  console.log('Server running on port :', PORT);\n});\n```\n\nHere are the API's for the configuration:\n\n<table>\n  <tr>\n    <td>Key</td>\n    <td>Type</td>\n    <td>Description</td>\n  </tr>\n  <tr>\n    <td><code>port</code></td>\n    <td><code>Number?</code> [<code>3000</code>]</td>\n    <td>Port to run Maleo server</td>\n  </tr>\n  <tr>\n    <td><code>assetDir</code></td>\n    <td><code>String?</code> [<code>'<root>/.maleo/client'</code>]</td>\n    <td>Directory for all client related assets</td>\n  </tr>\n</table>\n\n#### Customize Webpack\n\nYou are able to extend Maleo.js' default webpack configuration by defining a function on `maleo.config.js`\n\n```js\n// maleo.config.js\n\nmodule.exports = {\n  webpack(config, context, next) {\n    // Perform customizations to webpack config\n    // Important: This call is required in order to let Maleo pass the config to webpack\n    return next(); \n  },\n};\n```\n\nWebpack function will receive three arguments:\n\n<table>\n  <tr>\n    <td>Argument</td>\n    <td>Details</td>\n  <tr>\n  <tr>\n    <td><code>config</code></td>\n    <td>This contains webpack configuration object that you can manipulate</td>\n  <tr>\n  <tr>\n    <td><code>context</code></td>\n    <td>This contains some keys that are useful for the build context\n      <br>\n      <table>\n        <tr>\n          <td><code>isDev</code></td>\n          <td><code>Boolean</code></td>\n          <td>Check if current build is for development</td>\n        </tr>\n        <tr>\n          <td><code>publicPath</code></td>\n          <td><code>String</code></td>\n          <td>Public Path of user defined or default's value</td>\n        </tr>\n        <tr>\n          <td><code>analyzeBundle</code></td>\n          <td><code>Boolean</code></td>\n          <td>Check if analyze bundle is enabled</td>\n        </tr>\n        <tr>\n          <td><code>buildDirectory</code></td>\n          <td><code>String</code></td>\n          <td>Build Directory of user defined or default's value</td>\n        </tr>\n        <tr>\n          <td><code>name</code></td>\n          <td><code>String</code></td>\n          <td>Build name <code>'server' || 'client'</code></td>\n        </tr>\n      </table>\n    </td>\n  <tr>\n  <tr>\n    <td><code>next</code></td>\n    <td>A callback required to be called to pass the custom configuration</td>\n  <tr>\n</table>\n\nExample of adding `ts-loader` through `maleo.config.js`:\n\n```js\n// maleo.config.js\n\n// Partly taken and modified from @airy/maleo-ts-plugin source\n// for simplicity purposes\n\nmodule.exports = {\n  webpack(config, context, next) {\n    const { isDev } = context\n\n    config.module.rules.push({\n      test: /\\.tsx?/,\n      exclude: /node_modules/,\n      use: [\n        require.resolve('@airy/maleo/lib/build/loaders/maleo-babel-loader'),\n        {\n          loader: 'ts-loader',\n          options: {\n            transpileOnly: true,\n          },\n        },\n      ],\n\n      if (isDev) {\n        config.plugins.push(new ForkTSCheckerWebpackPlugin());\n      }\n\n      return next();\n    })\n  },\n};\n```\n\n#### Customize Babel Config\n\nMaleo.js also let you have your own babel config. Just simply add `.babelrc` file at the root directory of your app.\n\nYou can include Maleo.js' babel preset in order to have latest JavaScript preset.\n\nHere's an example of `.babelrc` file:\n```json\n{\n  \"presets\": [\"@airy/maleo/babel\"],\n  \"plugins\": []\n}\n```\n\nThe `@airy/maleo/babel` preset includes everything you need to get your development started. The preset includes:\n- `@babel/preset-env`\n- `@babel/preset-react`\n- `@babel/plugin-proposal-class-properties`\n- `@babel/plugin-proposal-decorators`\n- `@babel/plugin-proposal-object-rest-spread`\n- `@babel/plugin-transform-runtime`\n- `react-loadable/babel`\n\n## CDN Support\n\nIf you are using a CDN, you can set up the `publicPath` setting and configure your CDN's origin to resolve to the domain that Maleo.js is hosted on.\n\n```js\n// maleo.config.js\n\nconst isProd = process.env.NODE_ENV === 'production';\n\nmodule.exports = {\n  assetPrefix: isProd && 'https://cdn.example.com';\n}\n```\n\n## FAQ\n\n== TO BE DETERMINED == \n\n## Plugins\n- [css-plugin](https://github.com/airyrooms/maleo-plugins/tree/master/packages/css-plugin)\n- [redux-plugin](https://github.com/airyrooms/maleo-plugins/tree/master/packages/redux-plugin)\n- [typescript-plugin](https://github.com/airyrooms/maleo-plugins/tree/master/packages/typescript-plugin)\n- [more](https://github.com/airyrooms/maleo-plugins)\n\n\n## Contributing\n[Please follow these steps to contribute to Maleo.js](https://github.com/airyrooms/maleo.js/blob/canary/CONTRIBUTING.md)\n\n[Please follow these steps to contribute to Maleo.js' plugins](https://github.com/airyrooms/maleo-plugins#contributing-guidelines)\n\n\n## Contributors\n\nThis project exists thanks to all the people who contribute. [Contribute](CONTRIBUTING.md).\n\nMany thanks to our **[contributors](https://github.com/airyrooms/maleo.js/graphs/contributors)**!\n\n\n## License\n\nMIT","readmeFilename":"README.md","bugs":{"url":"https://github.com/airyrooms/maleo.js/issues"},"homepage":"https://github.com/airyrooms/maleo.js#readme","_id":"@airy/maleo@0.2.1-canary.0","_nodeVersion":"11.13.0","_npmVersion":"lerna/3.13.1/node@v11.13.0+x64 (linux)","dist":{"integrity":"sha512-1Q5VXZW6Dr36G9UYvpnoNGHjNMed9GU8kk8pFUct0uhPnB5ZSvnyrdNCOSroTRYgaDqYqfIdwPp2KDL0CDm4zA==","shasum":"30d093df9096a4e41a71cbb58197e99871abd896","tarball":"https://registry.npmjs.org/@airy/maleo/-/maleo-0.2.1-canary.0.tgz","fileCount":87,"unpackedSize":258587,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJcqGPcCRA9TVsSAnZWagAADssP/3pttWBAiyo2cWTcRs3q\nesfOoVLKNT0KfZcNbTS/DIC2NwOp9nTXd8MgztZBbMR3h/+5riQ4Omrxzmkn\nSrq35tm2ZKKxXSqHE5fGMspgcD79SNbP84fjj6dbggTcs+xQ9TvMDbj8X62L\nG170N+FgUoDcz7H6Kkm80NNIMC2H4n9pWLoluPUzUej8/ofW2cb9nqhJlxb6\ntMh5yT8lLDl0YInV4lnr3OJN9MOGghcCh9DugIf0SfgZtzQAZ5pWj/FUXf9U\nK0PPT3cyxpMttT2YVlQa2cAXardsUTaNy+NCesddfejM2wu/9gHh64o23TEU\nabK/Nn/fTL5MALEA9g9uQ74UHOtJ3rNkfkHRik/EwK7mFu+gyzZkhw7HEORd\nDDHUTLjjBhr4ff/FtAL0gN1j1DO1KktOWvudDA87l3PrDnzNbvB1dEZvGQTA\nEdhXO/DP2sXcGid5qbIi37HkhvyF7WrKH8H9Zhi7GLyYsTGIkvF4o3rrVftr\nuWA6m2FYWg+HLYu2hUKqgNhDtslB5zlNe/FgzfLYU/0LkeUNanbd/TcTvMgF\nNWhBiz2LLCXzVlO0nG4ilKHDwBsCThuu3QdJ0WuZqglTCWAC/2oXiZBhq05e\nuatBFiMNyiAzQCL00xEJJkddk+zXSbcho92lwIJx/Vs5LW94KAmZCvkZmFBL\n24/K\r\n=IIIc\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIHZOBXsLLmmH3XEXAOVZeDYCsC/HLsfefZUctdp565fMAiA/2/lCRe7oxnCtoO1pHzmGmEJUvnV1kymeXkEeI4o7gQ=="}]},"maintainers":[{"name":"airyrooms-engineering","email":"engineering@airyrooms.com"}],"_npmUser":{"name":"airyrooms-engineering","email":"engineering@airyrooms.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/maleo_0.2.1-canary.0_1554539483341_0.40560476454960903"},"_hasShrinkwrap":false},"0.2.1-canary.1":{"name":"@airy/maleo","version":"0.2.1-canary.1","description":"Un-opinionated Universal Rendering Javascript Framework","repository":{"type":"git","url":"git+https://github.com/airyrooms/maleo.js.git","bugs":"https://github.com/airyrooms/maleo.js/issues"},"author":{"name":"Airy"},"license":"MIT","private":false,"sideEffects":false,"scripts":{"build":"rm -rf dist && gulp","watch":"gulp watch","prepublish":"yarn build","start":"node server.js"},"bin":{"maleo":"lib/bin/maleo.js"},"dependencies":{"@babel/core":"7.1.2","@babel/plugin-proposal-class-properties":"7.1.0","@babel/plugin-proposal-decorators":"7.1.2","@babel/plugin-proposal-object-rest-spread":"7.0.0","@babel/plugin-syntax-dynamic-import":"7.0.0","@babel/plugin-transform-runtime":"7.1.0","@babel/preset-env":"7.1.0","@babel/preset-react":"7.0.0","@babel/runtime":"7.1.2","@babel/runtime-corejs2":"7.2.0","babel-loader":"8.0.4","case-sensitive-paths-webpack-plugin":"2.1.2","compression":"1.7.4","es6-promise":"4.2.5","eventsource":"1.0.7","express":"4.16.4","figlet":"1.2.1","friendly-errors-webpack-plugin":"1.7.0","hard-source-webpack-plugin":"0.13.1","helmet":"3.14.0","isomorphic-fetch":"2.2.1","loader-utils":"1.1.0","node-notifier":"5.2.1","nodemon":"1.18.4","react-dom":"16.8.4","react-helmet":"5.2.0","react-loadable":"5.5.0","react-router-config":"5.0.0","react-router-dom":"5.0.0","rimraf":"2.6.3","source-map-support":"0.5.9","terser-webpack-plugin":"1.1.0","webpack":"4.29.0","webpack-bundle-analyzer":"3.0.3","webpack-cli":"3.1.2","webpack-dev-middleware":"3.4.0","webpack-hot-middleware":"2.24.3","webpackbar":"2.6.3"},"peerDependencies":{"react":"^16.8.4"},"devDependencies":{"@babel/preset-typescript":"^7.1.0","@types/webpack-bundle-analyzer":"^2.13.0","babel-plugin-module-resolver":"^3.1.1","gulp":"github:gulpjs/gulp#4.0","gulp-babel":"^8.0.0","gulp-clean":"^0.4.0","gulp-sourcemaps":"^2.6.4","gulp-uglify":"^3.0.1","gulp-watch":"^5.0.1","react":"^16.8.4"},"gitHead":"047f286ea2464c7aa60178b6da7245361ac27e33","readme":"[![Build Status](https://travis-ci.org/airyrooms/maleo.js.svg?branch=master)](https://travis-ci.org/airyrooms/maleo.js)\n[![Coverage Status](https://coveralls.io/repos/github/airyrooms/maleo.js/badge.svg?branch=master)](https://coveralls.io/github/airyrooms/maleo.js?branch=master)\n[![npm version](https://badge.fury.io/js/%40airy%2Fmaleo.svg)](https://badge.fury.io/js/%40airy%2Fmaleo)\n[![Issues](http://img.shields.io/github/issues/airyrooms/maleo.js.svg)]( https://github.com/airyrooms/maleo.js/issues)\n[![MIT license](https://img.shields.io/badge/License-MIT-blue.svg)](https://github.com/airyrooms/maleo.js/blob/canary/LICENSE)\n[![Discord Chat](https://img.shields.io/discord/550498214789513218.svg)](https://discord.gg/9eArCQn)\n[![Conventional Commits](https://img.shields.io/badge/Conventional%20Commits-1.0.0-green.svg)](https://conventionalcommits.org)\n\n\n# Welcome to Maleo.js\n\nMaleo.js is an un-opinionated framework to enable Universal Rendering in JavaScript using React with no hassle.\n\nWe are here to solve the time consuming setups Universal Rendering Required.\n\n---\n\nReadme below is the documentation for the `canary` (prerelease) branch. To view the documentation for the latest stable Maleo.js version change branch to `master`\n\n---\n\n# Table of Contents\n- [Features](#features)\n- [Setup](#setup)\n- [Component Lifecycle](#component-lifecycle)\n- [Routing](#routing)\n- [Dynamic Import Component](#dynamic-import-component)\n  - [Tips](#tips)\n  - [Preloading](#preloading)\n- [Customizable Component](#customizable-component)\n  - [Custom Document](#custom-document)\n  - [Custom Wrap](#custom-wrap)\n- [Custom Configuration](#custom-configuration)\n  - [Customize Server](#customize-server)\n  - [Customize Webpack](#customize-webpack)\n  - [Customize Babel Config](#customize-babel-config)\n- [CDN Support](#cdn-support)\n- [Plugins](#plugins)\n- [FAQ](#faq)\n- [Contributing](#contributing)\n\n---\n\n## Features\n- Universal Rendering\n- Plugin based framework\n- Customizable\n\n## Setup\n\nInstall Maleo.js \n\n>*for now change `@airy/maleo` to `@airy/maleo@canary` until we publish the stable version of this package*\n\n\n**NPM**\n```bash\n$ npm install --save @airy/maleo react\n```\n**Yarn**\n```bash\n$ yarn add @airy/maleo react\n```\n\nAdd this script to your `package.json`\n```json\n{\n  \"scripts\": {\n    \"dev\": \"maleo dev\",\n    \"build\": \"export NODE_ENV=production && maleo build\",\n    \"start\": \"export NODE_ENV=production && node .maleo/server.js\"\n  }\n}\n```\n\nCreate a page Root component\n```jsx\n// ./src/Root.jsx\nimport React from 'react';\n\n// Export default is required for registering page\nexport default class RootComponent extends React.Component {\n  render() {\n    return (\n      <h1>Hello World!</h1>\n    )\n  }\n}\n```\n\nAnd lastly, create a routing file on your project root directory called `routes.json` and register your page component\n```json\n[\n  {\n    \"path\": \"/\",\n    \"page\": \"./src/Root\"\n  }\n]\n```\n\nAfter that you can now run `$ npm run dev` and go to `http://localhost:3000`.\n\nYou should now see your app running on your browser.\n\nBy now you should see \n- Automatic transpilation and bundling (with webpack and babel)\n- ~~Hot code reloading~~ [#17](https://github.com/airyrooms/maleo.js/issues/17)\n- Server rendering\n\nTo see how simple this is, check out the sample app!\n\n## Component Lifecycle \n\nMaleo.js added a new component lifecycle hook called `getInitialProps`, this function is called during Server Side Rendering (SSR).\n\nThis is useful especially for SEO purposes. \n\nExample for stateful component:\n```jsx\nimport React from 'react';\n\nexport default class extends React.Component {\n  static getInitialProps = async (ctx) => {\n    const { req } = ctx;\n\n    const userAgent = req ? req.headers['user-agent'] : navigator.userAgent;\n\n    // the return value will be passed as props for this component\n    return { userAgent };\n  }\n\n  render() {\n    return (\n      <div>\n        Hello World {this.props.userAgent}\n      </div>\n    );\n  }\n}\n```\n\nExample for stateless component:\n```jsx\nconst Component = (props) => (\n  <div>\n    Hello World {props.userAgent}\n  </div>\n);\n\nComponent.getInitialprops = async (ctx) => {\n  const { req } = ctx;\n\n  const userAgent = req ? req.headers['user-agent'] : navigator.userAgent;\n\n  return { userAgent };\n};\n\nexport default Component;\n```\n\n`getInitialProps` receives a context object with the following properties:\n- `req` - HTTP request object (server only)\n- `res` - HTTP response object (server only)\n- `...wrapProps` - Spreaded properties from custom Wrap\n- `...appProps` - Spreaded properties from custom App\n\n## Routing\n\nRouting is declared in a centralized route config.\nRegister all the route config in `routes.json` file.\n\nIf you put the `routes.json` files on root directory, Maleo will automatically register your route. Otherwise put path to your routes on [Maleo config](#custom-configuration).\n\nRoutes file has to export default the route configuration.\nThe route object **expected to have distinct key** to indicate the route.\n\n<table>\n  <tr>\n    <td>Key</td>\n    <td>Type</td>\n    <td>Description</td>\n  </tr>\n  <tr>\n    <td><code>path</code></td>\n    <td><code>String!</code></td>\n    <td>Routes path</td>\n  </tr>\n  <tr>\n    <td><code>page</code></td>\n    <td><code>String!</code></td>\n    <td>Path to React Component for this route path</td>\n  </tr>\n  <tr>\n    <td><code>exact</code></td>\n    <td><code>Boolean?</code> [<code>false</code>]</td>\n    <td>To make url has to match exactly the path in order to render the component. Give <code>false</code> value if the route is a wrapper route component</td>\n  </tr>\n  <tr>\n    <td><code>routes</code></td>\n    <td><code>RouteObject?</code></td>\n    <td>Nested route</td>\n  </tr>\n</table>\n\n\nFor example:\n```json\n[\n  {\n    \"page\": \"./src/MainApp\",\n    \"routes\": [\n      {\n        \"path\": \"/\",\n        \"page\": \"./src/Search\",\n        \"exact\": true\n      },\n      {\n        \"path\": \"/search\",\n        \"page\": \"./src/Search\",\n        \"routes\": [\n          {\n            \"path\": \"/search/hello\",\n            \"page\": \"./src/Detail\",\n            \"exact\": true\n          }\n        ]\n      },\n      {\n        \"path\": \"/detail\",\n        \"page\": \"./src/Detail\",\n        \"exact\": true\n      }\n    ]\n  }\n]\n```\n\n## Dynamic Import Component\nMaleo.js supports TC39 [dynamic import proposal](https://github.com/tc39/proposal-dynamic-import) for JavaScript.\n\nYou can think dynamic import as another way to split your code into manageable chunks. You can use our `Dynamic` function which utilizes [react loadable](https://github.com/jamiebuilds/react-loadable)\n\nFor Example\n```js\n// DynamicComponent.js\nimport Dynamic from '@airy/maleo/dynamic';\n\nexport default Dynamic({\n  loader: () => import( /* webpackChunkName:\"DynamicComponent\" */ './component/DynamicLoad'),\n})\n```\n\n### **Preloading**\n\nFor optimization purposes, you can also preload a component even before the component got rendered.\n\nFor example, if you want to load component when a button get pressed, you can start preloading the component when the user hovers the mouse over the button.\n\nThe component created by `Dynamic` exposes a [static `preload` method](https://github.com/jamiebuilds/react-loadable#loadablecomponentpreload).\n\n```jsx\nimport React from 'react';\nimport Dynamic from '@airy/maleo/dynamic';\n\nconst DynamicBar = Dynamic({\n  loader: () => import('./Bar'),\n  loading: LoadingCompoinent\n});\n\nclass MyComponent extends React.Component {\n  state = { showBar: false };\n\n  onClick = () => {\n    this.setState({ showBar: true });\n  };\n\n  onMouseOver = () => DynamicBar.preload();\n\n  render() {\n    return (\n      <div>\n        <button\n          onClick={this.onClick}\n          onMouseOver={this.onMouseOver}>\n          Show Bar\n        </button>\n        { this.state.showBar && <DynamicBar /> }\n      </div>\n    )\n  }\n}\n```\n\n## Customizable Component\n\n### Custom Document\n\nHighly inspired by what [Next.js](https://github.com/zeit/next.js) has done on their awesome template customization.\n\nMaleo.js also enable customization on `Document` as document's markup. So you don't need to include tags like `<html>`, `<head>`, `<body>`, etc. \n\nTo override the default behavior, you'll need to create a component that extends the `Document` React class provided by Maleo.\n\n```jsx\n// document.jsx\nimport React from 'react';\nimport { Document, Header, Main, Scripts } from '@airy/maleo/document';\n\nexport default class extends Document {\n  render() {\n    return (\n      <html>\n        <Header>\n          <title>Maleo JS</title>\n          <meta charset=\"utf-8\" />\n          <meta name=\"description\" content=\"Maleo.js is awesome!\" />\n\n          <style>\n            {` body { background-color: #fff } `}\n          </style>\n        </Header>\n\n        <body>\n          <Main />\n\n          <Scripts />\n\n          <ReduxScript />\n        </body>\n      </html>\n    );\n  }\n}\n```\n\n### Custom Wrap\n\nMaleo.js uses the `Wrap` component to initialize pages. `Wrap` contains React Router's Component. You can add HoC here to wrap the application and control the page initialization. Which allows you to do amazing things like:\n- Persisting layour between page changes\n- Keeping state when navigating pages\n- Custom error handling using `componentDidCatch`\n- Inject additional data into pages (like Redux provider, etc)\n\nTo override the default behavior, you'll need to create a component that extends the `Wrap` React class provided by Maleo.\n\n```jsx\n// wrap.jsx\nimport React from 'react';\nimport { Wrap } from '@airy/maleo/wrap';\n\n// Redux plugin for Maleo.js\n// Hoc that creates store and add Redux Provider\nimport { withRedux } from '@airy/maleo-redux-plugin';\n\n// Custom Wrapper that will be rendered for the whole Application\nimport Content from './component/Content';\nimport NavigationBar from './component/NavigationBar';\n\nimport { createStore } from './store';\n\n@withRedux(createStore)\nexport default class extends Wrap {\n  static getInitialProps = (ctx) => {\n    const { store } = ctx\n    // you receive store from context\n    // you can access or do something with the store here\n    console.log('Initialized Redux Store', store);\n    return {}\n  }\n\n  render() {\n    const { Container, containerProps, App, appProps } = this.props\n\n    return (\n      <Container {...containerProps}>\n        <NavigationBar />\n        <Content>\n          <App {...appProps}/>\n        </Content>\n      </Container>\n    )\n  }\n}\n\n```\nIf you put `document.jsx` and `wrap.jsx` on root directory (the same level with `package.json`), then Maleo will automatically register your custom Document and Wrap. Otherwise, you can add the path to your custom Document and Wrap on [Maleo config](#custom-configuration)\n\n---\n***We are also working on adding default and customizable `Error` component page***\n\n---\n\n## Custom Configuration\n\nFor more advanced configuration of Maleo.js, like `webpack` config, registering `plugins`, path to your routes, custom Document and Wrap, and adding `path alias`, you can create a `maleo.config.js` in the root of your project directory. (same directory with `package.json`)\n\n```js\n// maleo.config.js\n\nmodule.exports = {\n  /* config options here */\n}\n```\n\nHere are the API's for the configuration:\n\n<table>\n  <tr>\n    <td>Key</td>\n    <td>Type</td>\n    <td>Description</td>\n  </tr>\n  <tr>\n    <td><code>buildDir</code></td>\n    <td><code>String?</code> [<code>.maleo</code>]</td>\n    <td>Directory to put Maleo.js' build assets</td>\n  </tr>\n  <tr>\n    <td><code>cache</code></td>\n    <td><code>Boolean?</code> [<code>true</code>]</td>\n    <td>Enable webpack build caching</td>\n  </tr>\n  <tr>\n    <td><code>sourceMaps</code></td>\n    <td><code>Boolean?</code> [<code>true</code>]</td>\n    <td>Enable webpack to generate source maps</td>\n  </tr>\n  <tr>\n    <td><code>alias</code></td>\n    <td><code>Object?</code></td>\n    <td>A key value pair for aliasing path directory\n      <br>\n      <code>\n      { 'component': './src/component' }\n      </code>\n    </td>\n  </tr>\n  <tr>\n    <td><code>publicPath</code></td>\n    <td><code>String?</code> [<code>/_assets/</code>]</td>\n    <td>To customize webpack's <code>publicPath</code> \n    <br>Comes in handy if using CDN to put built assets</td>\n  </tr>\n  <tr>\n    <td><code>analyzeBundle</code></td>\n    <td><code>Boolean?</code> [<code>false</code>]</td>\n    <td>To enable webpack's bundle analyzer, for analyzing bundle sizes during bundle debugging should Maleo.js' build process got slow</td>\n  </tr>\n  <tr>\n    <td><code>webpack</code></td>\n    <td><code>Function?</code></td>\n    <td>To customize webpack configuration, more details <a href=\"#customize-webpack\">here</a></td>\n  </tr>\n  <tr>\n    <td><code>routes</code></td>\n    <td><code>string?</code> [<code>rootDir/routes.jsx</code>]</td>\n    <td>Path to your routes file</td>\n  </tr>\n  <tr>\n    <td><code>customDocument</code></td>\n    <td><code>string?</code> [<code>rootDir/document.jsx</code>]</td>\n    <td>Path to your custom document file</td>\n  </tr>\n  <tr>\n    <td><code>customWrap</code></td>\n    <td><code>string?</code> [<code>rootDir/wrap.jsx</code>]</td>\n    <td>Path to your custom wrap file</td>\n  </tr>\n  <tr>\n    <td><code>customApp</code></td>\n    <td><code>string?</code> [<code>rootDir/app.jsx</code>]</td>\n    <td>Path to your custom app file</td>\n  </tr>\n</table>\n\n#### Customize Server\n\nCreate a `server.js` file on root directory where your `package.json` lives.\nHere you can customize Maleo's server.\n```js\nimport { Server } from '@airy/maleo/server';\nimport path from 'path';\n\nimport routeConfig from './routes';\n\nconst PORT = process.env.PORT || 8080;\n\nconst maleoServer = Server.init({\n  port: PORT,\n});\n\nmaleoServer.run(() => {\n  console.log('Server running on port :', PORT);\n});\n```\n\nHere are the API's for the configuration:\n\n<table>\n  <tr>\n    <td>Key</td>\n    <td>Type</td>\n    <td>Description</td>\n  </tr>\n  <tr>\n    <td><code>port</code></td>\n    <td><code>Number?</code> [<code>3000</code>]</td>\n    <td>Port to run Maleo server</td>\n  </tr>\n  <tr>\n    <td><code>assetDir</code></td>\n    <td><code>String?</code> [<code>'<root>/.maleo/client'</code>]</td>\n    <td>Directory for all client related assets</td>\n  </tr>\n</table>\n\n#### Customize Webpack\n\nYou are able to extend Maleo.js' default webpack configuration by defining a function on `maleo.config.js`\n\n```js\n// maleo.config.js\n\nmodule.exports = {\n  webpack(config, context, next) {\n    // Perform customizations to webpack config\n    // Important: This call is required in order to let Maleo pass the config to webpack\n    return next(); \n  },\n};\n```\n\nWebpack function will receive three arguments:\n\n<table>\n  <tr>\n    <td>Argument</td>\n    <td>Details</td>\n  <tr>\n  <tr>\n    <td><code>config</code></td>\n    <td>This contains webpack configuration object that you can manipulate</td>\n  <tr>\n  <tr>\n    <td><code>context</code></td>\n    <td>This contains some keys that are useful for the build context\n      <br>\n      <table>\n        <tr>\n          <td><code>isDev</code></td>\n          <td><code>Boolean</code></td>\n          <td>Check if current build is for development</td>\n        </tr>\n        <tr>\n          <td><code>publicPath</code></td>\n          <td><code>String</code></td>\n          <td>Public Path of user defined or default's value</td>\n        </tr>\n        <tr>\n          <td><code>analyzeBundle</code></td>\n          <td><code>Boolean</code></td>\n          <td>Check if analyze bundle is enabled</td>\n        </tr>\n        <tr>\n          <td><code>buildDirectory</code></td>\n          <td><code>String</code></td>\n          <td>Build Directory of user defined or default's value</td>\n        </tr>\n        <tr>\n          <td><code>name</code></td>\n          <td><code>String</code></td>\n          <td>Build name <code>'server' || 'client'</code></td>\n        </tr>\n      </table>\n    </td>\n  <tr>\n  <tr>\n    <td><code>next</code></td>\n    <td>A callback required to be called to pass the custom configuration</td>\n  <tr>\n</table>\n\nExample of adding `ts-loader` through `maleo.config.js`:\n\n```js\n// maleo.config.js\n\n// Partly taken and modified from @airy/maleo-ts-plugin source\n// for simplicity purposes\n\nmodule.exports = {\n  webpack(config, context, next) {\n    const { isDev } = context\n\n    config.module.rules.push({\n      test: /\\.tsx?/,\n      exclude: /node_modules/,\n      use: [\n        require.resolve('@airy/maleo/lib/build/loaders/maleo-babel-loader'),\n        {\n          loader: 'ts-loader',\n          options: {\n            transpileOnly: true,\n          },\n        },\n      ],\n\n      if (isDev) {\n        config.plugins.push(new ForkTSCheckerWebpackPlugin());\n      }\n\n      return next();\n    })\n  },\n};\n```\n\n#### Customize Babel Config\n\nMaleo.js also let you have your own babel config. Just simply add `.babelrc` file at the root directory of your app.\n\nYou can include Maleo.js' babel preset in order to have latest JavaScript preset.\n\nHere's an example of `.babelrc` file:\n```json\n{\n  \"presets\": [\"@airy/maleo/babel\"],\n  \"plugins\": []\n}\n```\n\nThe `@airy/maleo/babel` preset includes everything you need to get your development started. The preset includes:\n- `@babel/preset-env`\n- `@babel/preset-react`\n- `@babel/plugin-proposal-class-properties`\n- `@babel/plugin-proposal-decorators`\n- `@babel/plugin-proposal-object-rest-spread`\n- `@babel/plugin-transform-runtime`\n- `react-loadable/babel`\n\n## CDN Support\n\nIf you are using a CDN, you can set up the `publicPath` setting and configure your CDN's origin to resolve to the domain that Maleo.js is hosted on.\n\n```js\n// maleo.config.js\n\nconst isProd = process.env.NODE_ENV === 'production';\n\nmodule.exports = {\n  assetPrefix: isProd && 'https://cdn.example.com';\n}\n```\n\n## FAQ\n\n== TO BE DETERMINED == \n\n## Plugins\n- [css-plugin](https://github.com/airyrooms/maleo-plugins/tree/master/packages/css-plugin)\n- [redux-plugin](https://github.com/airyrooms/maleo-plugins/tree/master/packages/redux-plugin)\n- [typescript-plugin](https://github.com/airyrooms/maleo-plugins/tree/master/packages/typescript-plugin)\n- [more](https://github.com/airyrooms/maleo-plugins)\n\n\n## Contributing\n[Please follow these steps to contribute to Maleo.js](https://github.com/airyrooms/maleo.js/blob/canary/CONTRIBUTING.md)\n\n[Please follow these steps to contribute to Maleo.js' plugins](https://github.com/airyrooms/maleo-plugins#contributing-guidelines)\n\n\n## Contributors\n\nThis project exists thanks to all the people who contribute. [Contribute](CONTRIBUTING.md).\n\nMany thanks to our **[contributors](https://github.com/airyrooms/maleo.js/graphs/contributors)**!\n\n\n## License\n\nMIT","readmeFilename":"README.md","bugs":{"url":"https://github.com/airyrooms/maleo.js/issues"},"homepage":"https://github.com/airyrooms/maleo.js#readme","_id":"@airy/maleo@0.2.1-canary.1","_nodeVersion":"10.15.3","_npmVersion":"lerna/3.13.1/node@v10.15.3+x64 (linux)","dist":{"integrity":"sha512-76NDvqtv/MwJCJSi5KVLZ1uDBMC4e9FOP+fFfc6VNPKBXwk0bL3cpSBwxsgQGOAjw2ND0dkHXNmFAobQBKRAEA==","shasum":"6632b4a5d224372127618f012b88eedd5738e706","tarball":"https://registry.npmjs.org/@airy/maleo/-/maleo-0.2.1-canary.1.tgz","fileCount":91,"unpackedSize":293578,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJcrJCrCRA9TVsSAnZWagAAcfwP/07bkVjlBdSVCELow+8Z\n4fpokZlJAhciBjaLVVZqmCwVhf8b9/Wk2fXuDuCURof0Ns/VYVSZTZQPyZ17\nhAVX6KCbCYay13Zr7GfxuYqqtjpJYGHctAJWcEa/yxDfBS5J3x7+BW4mj2oA\naKbDRS0v3PupeYNfasutDwsDQRmzPd5bG5OcxR5voyOwL+ooFHb8yOz+EWRY\nelBHDJgYDyVuXIN1FZq1kDQRD1P+96SSsOm5WKD9bulUPgOSwHXhQC6mfNMb\nkumb8kXTq/Ieye7SJDx+N94q6zxRmrh91H2lYY6CH6QjzN+iIl3wbrlzuPO3\nW642YRnIyJKaqQP5wYnXyIW4lLDNviP0wpwXOjfQZT4sHrb4rtsGOG89b3Wf\nYvjZlukNMyXNzZ9tI3J0uUILqDxEG6EjLsxoKeKbhy0xOTSI2T0IBbvrNUWN\nxdYtxsdvLAXisvQK27ASTVrJpoUIsxzfi7LvSFHVUwPwEgu934j8gawMQyJf\nl+jDb3aNLrv+zZIqMYLfgEiU//o1f0fmyfRkFrjX/ZmyQfZ/A3n9wmRptNy4\nwZVvqOIgLgQ6OPs97jyV1NAXL316IxxQ4J6eEMIwlcqUqGEFkyHbyUDc9cei\nNW9mDbIYWa0ZIWwMRL+eskRQ1Pp0mxUIqLdLvrWVGFbQ3HPis3jvLsAOJ1EU\nDUF8\r\n=8OgA\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIA2RmU1ZjkRIrc1/X3DnAvIV9OhXfCtoap1F/9EB/zhGAiEA2ZVNbTcHqXeOpNvIfw9aYbXspDieBkgzgnFYX6UESyw="}]},"maintainers":[{"name":"airyrooms-engineering","email":"engineering@airyrooms.com"}],"_npmUser":{"name":"airyrooms-engineering","email":"engineering@airyrooms.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/maleo_0.2.1-canary.1_1554813098270_0.9217367308715536"},"_hasShrinkwrap":false},"0.2.1":{"name":"@airy/maleo","version":"0.2.1","description":"Un-opinionated Universal Rendering Javascript Framework","repository":{"type":"git","url":"git+https://github.com/airyrooms/maleo.js.git","bugs":"https://github.com/airyrooms/maleo.js/issues"},"author":{"name":"Airy"},"license":"MIT","private":false,"sideEffects":false,"scripts":{"build":"rm -rf dist && gulp","watch":"gulp watch","prepublish":"yarn build","start":"node server.js"},"bin":{"maleo":"lib/bin/maleo.js"},"dependencies":{"@babel/core":"7.1.2","@babel/plugin-proposal-class-properties":"7.1.0","@babel/plugin-proposal-decorators":"7.1.2","@babel/plugin-proposal-object-rest-spread":"7.0.0","@babel/plugin-syntax-dynamic-import":"7.0.0","@babel/plugin-transform-runtime":"7.1.0","@babel/preset-env":"7.1.0","@babel/preset-react":"7.0.0","@babel/runtime":"7.1.2","@babel/runtime-corejs2":"7.2.0","babel-loader":"8.0.4","case-sensitive-paths-webpack-plugin":"2.1.2","compression":"1.7.4","es6-promise":"4.2.5","eventsource":"1.0.7","express":"4.16.4","figlet":"1.2.1","friendly-errors-webpack-plugin":"1.7.0","hard-source-webpack-plugin":"0.13.1","helmet":"3.14.0","isomorphic-fetch":"2.2.1","loader-utils":"1.1.0","node-notifier":"5.2.1","nodemon":"1.18.4","react-dom":"16.8.4","react-helmet":"5.2.0","react-loadable":"5.5.0","react-router-config":"5.0.0","react-router-dom":"5.0.0","rimraf":"2.6.3","source-map-support":"0.5.9","terser-webpack-plugin":"1.1.0","webpack":"4.29.0","webpack-bundle-analyzer":"3.0.3","webpack-cli":"3.1.2","webpack-dev-middleware":"3.4.0","webpack-hot-middleware":"2.24.3","webpackbar":"2.6.3"},"peerDependencies":{"react":"^16.8.4"},"devDependencies":{"@babel/preset-typescript":"^7.1.0","@types/webpack-bundle-analyzer":"^2.13.0","babel-plugin-module-resolver":"^3.1.1","gulp":"github:gulpjs/gulp#4.0","gulp-babel":"^8.0.0","gulp-clean":"^0.4.0","gulp-sourcemaps":"^2.6.4","gulp-uglify":"^3.0.1","gulp-watch":"^5.0.1","react":"^16.8.4"},"gitHead":"c8e99d2791cbf82a4df06b32882157cd60e853d1","bugs":{"url":"https://github.com/airyrooms/maleo.js/issues"},"homepage":"https://github.com/airyrooms/maleo.js#readme","_id":"@airy/maleo@0.2.1","_nodeVersion":"10.11.0","_npmVersion":"lerna/3.13.1/node@v10.11.0+x64 (darwin)","dist":{"integrity":"sha512-7BIOBrpPwUj44EivuLxJW4SZwaRBaFxUphI7VbnQUn7rKtDn1pgpJmhkmhbjhi6VD4NLFjBCAs8nvt3s19k4SQ==","shasum":"2e7f7fafa28682f06c734d22a89105644e01aace","tarball":"https://registry.npmjs.org/@airy/maleo/-/maleo-0.2.1.tgz","fileCount":115,"unpackedSize":340809,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJcrJHWCRA9TVsSAnZWagAADh8P/1bKlHqmA1cwp0Byrjhe\n8IXWqXp9bCVULKMxeFE1tkI6XrijsXteJeYf+sScqz1zLwjklnTdVfZ1iDV5\nY2OvupHMQ5RExA1XWfkWr/q1o7ddtJiNKScdSkdgasuWc2+V9ySdtEDY6h69\nTG65bNKgmCh7yozWo6KDddvsytxHK5USdwJUzqDbAn9b1qWdUzvj77CDdwKP\nMN8Nzyuanw11u9Pt7tBhYyCD7jogSts8HE0NEt1qa4uL1tcRiNcxqceM4Iqn\nhhZVQTetoi8rsng9J+vcUaTTbCkHEA6crR/njBZIxbIKtyVxOESXx4wwfJv6\nMzSTM4I1seL5rRzh49n1eYLoMUfGNqtygINbP1x77w1A7uxiNA2Gwq96+7eT\nN0RVcokYQqJIFAF/fwVn8TZ8nn3Dj2SQmsfCAkMnW+XfE60KQFcVPh3tXpU2\n4AjGSB5CzuCEukmXtdbks0JDPZ4UkLQUxs1Kj18G692wvr7V9aHr3r9+OiBf\nIVTYNOHgI5KsM6Pc7BbNgXSTlusv0EcnMj2fNNPdCGhtEqC+QO2ADdxehcNK\nIyDYF+xrPLknfxc5RrWF4pQ/g7cVYpmIYh1p9zMuo8qkohuI/doxTJd78BGC\nLZ3iQDyj4M6RE8VeiP97XbZDbMCgghEAdjrTHk5X97hgnnEocg7zWxGGo5NH\nYVjf\r\n=cDVR\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIE1ZGbYYrc6z240E8rP9XKkAmmBEk1mExUJnTJOfZvxKAiBBbn+/aacMs7Hgdh/GrHsxn2sxVHGnDlhrjIcED+ucLQ=="}]},"maintainers":[{"name":"airyrooms-engineering","email":"engineering@airyrooms.com"}],"_npmUser":{"name":"airyrooms-engineering","email":"engineering@airyrooms.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/maleo_0.2.1_1554813397860_0.448391460212598"},"_hasShrinkwrap":false},"0.2.1-canary.2":{"name":"@airy/maleo","version":"0.2.1-canary.2","description":"Un-opinionated Universal Rendering Javascript Framework","repository":{"type":"git","url":"git+https://github.com/airyrooms/maleo.js.git","bugs":"https://github.com/airyrooms/maleo.js/issues"},"author":{"name":"Airy"},"license":"MIT","private":false,"sideEffects":false,"scripts":{"build":"rm -rf dist && gulp","watch":"gulp watch","prepublish":"yarn build","start":"node server.js"},"bin":{"maleo":"lib/bin/maleo.js"},"dependencies":{"@babel/core":"7.1.2","@babel/plugin-proposal-class-properties":"7.1.0","@babel/plugin-proposal-decorators":"7.1.2","@babel/plugin-proposal-object-rest-spread":"7.0.0","@babel/plugin-syntax-dynamic-import":"7.0.0","@babel/plugin-transform-runtime":"7.1.0","@babel/preset-env":"7.1.0","@babel/preset-react":"7.0.0","@babel/runtime":"7.1.2","@babel/runtime-corejs2":"7.2.0","babel-loader":"8.0.4","case-sensitive-paths-webpack-plugin":"2.1.2","compression":"1.7.4","es6-promise":"4.2.5","eventsource":"1.0.7","express":"4.16.4","figlet":"1.2.1","friendly-errors-webpack-plugin":"1.7.0","hard-source-webpack-plugin":"0.13.1","helmet":"3.14.0","isomorphic-fetch":"2.2.1","loader-utils":"1.1.0","node-notifier":"5.2.1","nodemon":"1.18.4","react-dom":"16.8.4","react-helmet":"5.2.0","react-loadable":"5.5.0","react-router-config":"5.0.0","react-router-dom":"5.0.0","rimraf":"2.6.3","source-map-support":"0.5.9","terser-webpack-plugin":"1.1.0","webpack":"4.29.0","webpack-bundle-analyzer":"3.0.3","webpack-cli":"3.1.2","webpack-dev-middleware":"3.4.0","webpack-hot-middleware":"2.24.3","webpackbar":"2.6.3"},"peerDependencies":{"react":"^16.8.4"},"devDependencies":{"@babel/preset-typescript":"^7.1.0","@types/webpack-bundle-analyzer":"^2.13.0","babel-plugin-module-resolver":"^3.1.1","gulp":"github:gulpjs/gulp#4.0","gulp-babel":"^8.0.0","gulp-clean":"^0.4.0","gulp-sourcemaps":"^2.6.4","gulp-uglify":"^3.0.1","gulp-watch":"^5.0.1","react":"^16.8.4"},"gitHead":"2ab398266527b52316e2033b3475fcea0ff6c208","readme":"[![Build Status](https://travis-ci.org/airyrooms/maleo.js.svg?branch=master)](https://travis-ci.org/airyrooms/maleo.js)\n[![Coverage Status](https://coveralls.io/repos/github/airyrooms/maleo.js/badge.svg?branch=master)](https://coveralls.io/github/airyrooms/maleo.js?branch=master)\n[![npm version](https://badge.fury.io/js/%40airy%2Fmaleo.svg)](https://badge.fury.io/js/%40airy%2Fmaleo)\n[![Issues](http://img.shields.io/github/issues/airyrooms/maleo.js.svg)]( https://github.com/airyrooms/maleo.js/issues)\n[![MIT license](https://img.shields.io/badge/License-MIT-blue.svg)](https://github.com/airyrooms/maleo.js/blob/canary/LICENSE)\n[![Discord Chat](https://img.shields.io/discord/550498214789513218.svg)](https://discord.gg/9eArCQn)\n[![Conventional Commits](https://img.shields.io/badge/Conventional%20Commits-1.0.0-green.svg)](https://conventionalcommits.org)\n\n\n# Welcome to Maleo.js\n\nMaleo.js is an un-opinionated framework to enable Universal Rendering in JavaScript using React with no hassle.\n\nWe are here to solve the time consuming setups Universal Rendering Required.\n\n---\n\nReadme below is the documentation for the `canary` (prerelease) branch. To view the documentation for the latest stable Maleo.js version change branch to `master`\n\n---\n\n# Table of Contents\n- [Features](#features)\n- [Setup](#setup)\n- [Component Lifecycle](#component-lifecycle)\n- [Routing](#routing)\n- [Dynamic Import Component](#dynamic-import-component)\n  - [Tips](#tips)\n  - [Preloading](#preloading)\n- [Customizable Component](#customizable-component)\n  - [Custom Document](#custom-document)\n  - [Custom Wrap](#custom-wrap)\n- [Custom Configuration](#custom-configuration)\n  - [Customize Server](#customize-server)\n  - [Customize Webpack](#customize-webpack)\n  - [Customize Babel Config](#customize-babel-config)\n- [CDN Support](#cdn-support)\n- [Plugins](#plugins)\n- [FAQ](#faq)\n- [Contributing](#contributing)\n\n---\n\n## Features\n- Universal Rendering\n- Plugin based framework\n- Customizable\n\n## Setup\n\nInstall Maleo.js \n\n>*for now change `@airy/maleo` to `@airy/maleo@canary` until we publish the stable version of this package*\n\n\n**NPM**\n```bash\n$ npm install --save @airy/maleo react\n```\n**Yarn**\n```bash\n$ yarn add @airy/maleo react\n```\n\nAdd this script to your `package.json`\n```json\n{\n  \"scripts\": {\n    \"dev\": \"maleo dev\",\n    \"build\": \"export NODE_ENV=production && maleo build\",\n    \"start\": \"export NODE_ENV=production && node .maleo/server.js\"\n  }\n}\n```\n\nCreate a page Root component\n```jsx\n// ./src/Root.jsx\nimport React from 'react';\n\n// Export default is required for registering page\nexport default class RootComponent extends React.Component {\n  render() {\n    return (\n      <h1>Hello World!</h1>\n    )\n  }\n}\n```\n\nAnd lastly, create a routing file on your project root directory called `routes.json` and register your page component\n```json\n[\n  {\n    \"path\": \"/\",\n    \"page\": \"./src/Root\"\n  }\n]\n```\n\nAfter that you can now run `$ npm run dev` and go to `http://localhost:3000`.\n\nYou should now see your app running on your browser.\n\nBy now you should see \n- Automatic transpilation and bundling (with webpack and babel)\n- ~~Hot code reloading~~ [#17](https://github.com/airyrooms/maleo.js/issues/17)\n- Server rendering\n\nTo see how simple this is, check out the sample app!\n\n## Component Lifecycle \n\nMaleo.js added a new component lifecycle hook called `getInitialProps`, this function is called during Server Side Rendering (SSR).\n\nThis is useful especially for SEO purposes. \n\nExample for stateful component:\n```jsx\nimport React from 'react';\n\nexport default class extends React.Component {\n  static getInitialProps = async (ctx) => {\n    const { req } = ctx;\n\n    const userAgent = req ? req.headers['user-agent'] : navigator.userAgent;\n\n    // the return value will be passed as props for this component\n    return { userAgent };\n  }\n\n  render() {\n    return (\n      <div>\n        Hello World {this.props.userAgent}\n      </div>\n    );\n  }\n}\n```\n\nExample for stateless component:\n```jsx\nconst Component = (props) => (\n  <div>\n    Hello World {props.userAgent}\n  </div>\n);\n\nComponent.getInitialprops = async (ctx) => {\n  const { req } = ctx;\n\n  const userAgent = req ? req.headers['user-agent'] : navigator.userAgent;\n\n  return { userAgent };\n};\n\nexport default Component;\n```\n\n`getInitialProps` receives a context object with the following properties:\n- `req` - HTTP request object (server only)\n- `res` - HTTP response object (server only)\n- `...wrapProps` - Spreaded properties from custom Wrap\n- `...appProps` - Spreaded properties from custom App\n\n## Routing\n\nRouting is declared in a centralized route config.\nRegister all the route config in `routes.json` file.\n\nIf you put the `routes.json` files on root directory, Maleo will automatically register your route. Otherwise put path to your routes on [Maleo config](#custom-configuration).\n\nRoutes file has to export default the route configuration.\nThe route object **expected to have distinct key** to indicate the route.\n\n<table>\n  <tr>\n    <td>Key</td>\n    <td>Type</td>\n    <td>Description</td>\n  </tr>\n  <tr>\n    <td><code>path</code></td>\n    <td><code>String!</code></td>\n    <td>Routes path</td>\n  </tr>\n  <tr>\n    <td><code>page</code></td>\n    <td><code>String!</code></td>\n    <td>Path to React Component for this route path</td>\n  </tr>\n  <tr>\n    <td><code>exact</code></td>\n    <td><code>Boolean?</code> [<code>false</code>]</td>\n    <td>To make url has to match exactly the path in order to render the component. Give <code>false</code> value if the route is a wrapper route component</td>\n  </tr>\n  <tr>\n    <td><code>routes</code></td>\n    <td><code>RouteObject?</code></td>\n    <td>Nested route</td>\n  </tr>\n</table>\n\n\nFor example:\n```json\n[\n  {\n    \"page\": \"./src/MainApp\",\n    \"routes\": [\n      {\n        \"path\": \"/\",\n        \"page\": \"./src/Search\",\n        \"exact\": true\n      },\n      {\n        \"path\": \"/search\",\n        \"page\": \"./src/Search\",\n        \"routes\": [\n          {\n            \"path\": \"/search/hello\",\n            \"page\": \"./src/Detail\",\n            \"exact\": true\n          }\n        ]\n      },\n      {\n        \"path\": \"/detail\",\n        \"page\": \"./src/Detail\",\n        \"exact\": true\n      }\n    ]\n  }\n]\n```\n\n## Dynamic Import Component\nMaleo.js supports TC39 [dynamic import proposal](https://github.com/tc39/proposal-dynamic-import) for JavaScript.\n\nYou can think dynamic import as another way to split your code into manageable chunks. You can use our `Dynamic` function which utilizes [react loadable](https://github.com/jamiebuilds/react-loadable)\n\nFor Example\n```js\n// DynamicComponent.js\nimport Dynamic from '@airy/maleo/dynamic';\n\nexport default Dynamic({\n  loader: () => import( /* webpackChunkName:\"DynamicComponent\" */ './component/DynamicLoad'),\n})\n```\n\n### **Preloading**\n\nFor optimization purposes, you can also preload a component even before the component got rendered.\n\nFor example, if you want to load component when a button get pressed, you can start preloading the component when the user hovers the mouse over the button.\n\nThe component created by `Dynamic` exposes a [static `preload` method](https://github.com/jamiebuilds/react-loadable#loadablecomponentpreload).\n\n```jsx\nimport React from 'react';\nimport Dynamic from '@airy/maleo/dynamic';\n\nconst DynamicBar = Dynamic({\n  loader: () => import('./Bar'),\n  loading: LoadingCompoinent\n});\n\nclass MyComponent extends React.Component {\n  state = { showBar: false };\n\n  onClick = () => {\n    this.setState({ showBar: true });\n  };\n\n  onMouseOver = () => DynamicBar.preload();\n\n  render() {\n    return (\n      <div>\n        <button\n          onClick={this.onClick}\n          onMouseOver={this.onMouseOver}>\n          Show Bar\n        </button>\n        { this.state.showBar && <DynamicBar /> }\n      </div>\n    )\n  }\n}\n```\n\n## Customizable Component\n\n### Custom Document\n\nHighly inspired by what [Next.js](https://github.com/zeit/next.js) has done on their awesome template customization.\n\nMaleo.js also enable customization on `Document` as document's markup. So you don't need to include tags like `<html>`, `<head>`, `<body>`, etc. \n\nTo override the default behavior, you'll need to create a component that extends the `Document` React class provided by Maleo.\n\n```jsx\n// document.jsx\nimport React from 'react';\nimport { Document, Header, Main, Scripts } from '@airy/maleo/document';\n\nexport default class extends Document {\n  render() {\n    return (\n      <html>\n        <Header>\n          <title>Maleo JS</title>\n          <meta charset=\"utf-8\" />\n          <meta name=\"description\" content=\"Maleo.js is awesome!\" />\n\n          <style>\n            {` body { background-color: #fff } `}\n          </style>\n        </Header>\n\n        <body>\n          <Main />\n\n          <Scripts />\n\n          <ReduxScript />\n        </body>\n      </html>\n    );\n  }\n}\n```\n\n### Custom Wrap\n\nMaleo.js uses the `Wrap` component to initialize pages. `Wrap` contains React Router's Component. You can add HoC here to wrap the application and control the page initialization. Which allows you to do amazing things like:\n- Persisting layour between page changes\n- Keeping state when navigating pages\n- Custom error handling using `componentDidCatch`\n- Inject additional data into pages (like Redux provider, etc)\n\nTo override the default behavior, you'll need to create a component that extends the `Wrap` React class provided by Maleo.\n\n```jsx\n// wrap.jsx\nimport React from 'react';\nimport { Wrap } from '@airy/maleo/wrap';\n\n// Redux plugin for Maleo.js\n// Hoc that creates store and add Redux Provider\nimport { withRedux } from '@airy/maleo-redux-plugin';\n\n// Custom Wrapper that will be rendered for the whole Application\nimport Content from './component/Content';\nimport NavigationBar from './component/NavigationBar';\n\nimport { createStore } from './store';\n\n@withRedux(createStore)\nexport default class extends Wrap {\n  static getInitialProps = (ctx) => {\n    const { store } = ctx\n    // you receive store from context\n    // you can access or do something with the store here\n    console.log('Initialized Redux Store', store);\n    return {}\n  }\n\n  render() {\n    const { Container, containerProps, App, appProps } = this.props\n\n    return (\n      <Container {...containerProps}>\n        <NavigationBar />\n        <Content>\n          <App {...appProps}/>\n        </Content>\n      </Container>\n    )\n  }\n}\n\n```\nIf you put `document.jsx` and `wrap.jsx` on root directory (the same level with `package.json`), then Maleo will automatically register your custom Document and Wrap. Otherwise, you can add the path to your custom Document and Wrap on [Maleo config](#custom-configuration)\n\n---\n***We are also working on adding default and customizable `Error` component page***\n\n---\n\n## Custom Configuration\n\nFor more advanced configuration of Maleo.js, like `webpack` config, registering `plugins`, path to your routes, custom Document and Wrap, and adding `path alias`, you can create a `maleo.config.js` in the root of your project directory. (same directory with `package.json`)\n\n```js\n// maleo.config.js\n\nmodule.exports = {\n  /* config options here */\n}\n```\n\nHere are the API's for the configuration:\n\n<table>\n  <tr>\n    <td>Key</td>\n    <td>Type</td>\n    <td>Description</td>\n  </tr>\n  <tr>\n    <td><code>buildDir</code></td>\n    <td><code>String?</code> [<code>.maleo</code>]</td>\n    <td>Directory to put Maleo.js' build assets</td>\n  </tr>\n  <tr>\n    <td><code>cache</code></td>\n    <td><code>Boolean?</code> [<code>true</code>]</td>\n    <td>Enable webpack build caching</td>\n  </tr>\n  <tr>\n    <td><code>sourceMaps</code></td>\n    <td><code>Boolean?</code> [<code>true</code>]</td>\n    <td>Enable webpack to generate source maps</td>\n  </tr>\n  <tr>\n    <td><code>alias</code></td>\n    <td><code>Object?</code></td>\n    <td>A key value pair for aliasing path directory\n      <br>\n      <code>\n      { 'component': './src/component' }\n      </code>\n    </td>\n  </tr>\n  <tr>\n    <td><code>publicPath</code></td>\n    <td><code>String?</code> [<code>/_assets/</code>]</td>\n    <td>To customize webpack's <code>publicPath</code> \n    <br>Comes in handy if using CDN to put built assets</td>\n  </tr>\n  <tr>\n    <td><code>analyzeBundle</code></td>\n    <td><code>Boolean?</code> [<code>false</code>]</td>\n    <td>To enable webpack's bundle analyzer, for analyzing bundle sizes during bundle debugging should Maleo.js' build process got slow</td>\n  </tr>\n  <tr>\n    <td><code>webpack</code></td>\n    <td><code>Function?</code></td>\n    <td>To customize webpack configuration, more details <a href=\"#customize-webpack\">here</a></td>\n  </tr>\n  <tr>\n    <td><code>routes</code></td>\n    <td><code>string?</code> [<code>rootDir/routes.jsx</code>]</td>\n    <td>Path to your routes file</td>\n  </tr>\n  <tr>\n    <td><code>customDocument</code></td>\n    <td><code>string?</code> [<code>rootDir/document.jsx</code>]</td>\n    <td>Path to your custom document file</td>\n  </tr>\n  <tr>\n    <td><code>customWrap</code></td>\n    <td><code>string?</code> [<code>rootDir/wrap.jsx</code>]</td>\n    <td>Path to your custom wrap file</td>\n  </tr>\n  <tr>\n    <td><code>customApp</code></td>\n    <td><code>string?</code> [<code>rootDir/app.jsx</code>]</td>\n    <td>Path to your custom app file</td>\n  </tr>\n</table>\n\n#### Customize Server\n\nCreate a `server.js` file on root directory where your `package.json` lives.\nHere you can customize Maleo's server.\n```js\nimport { Server } from '@airy/maleo/server';\nimport path from 'path';\n\nimport routeConfig from './routes';\n\nconst PORT = process.env.PORT || 8080;\n\nconst maleoServer = Server.init({\n  port: PORT,\n});\n\nmaleoServer.run(() => {\n  console.log('Server running on port :', PORT);\n});\n```\n\nHere are the API's for the configuration:\n\n<table>\n  <tr>\n    <td>Key</td>\n    <td>Type</td>\n    <td>Description</td>\n  </tr>\n  <tr>\n    <td><code>port</code></td>\n    <td><code>Number?</code> [<code>3000</code>]</td>\n    <td>Port to run Maleo server</td>\n  </tr>\n  <tr>\n    <td><code>assetDir</code></td>\n    <td><code>String?</code> [<code>'<root>/.maleo/client'</code>]</td>\n    <td>Directory for all client related assets</td>\n  </tr>\n</table>\n\n#### Customize Webpack\n\nYou are able to extend Maleo.js' default webpack configuration by defining a function on `maleo.config.js`\n\n```js\n// maleo.config.js\n\nmodule.exports = {\n  webpack(config, context, next) {\n    // Perform customizations to webpack config\n    // Important: This call is required in order to let Maleo pass the config to webpack\n    return next(); \n  },\n};\n```\n\nWebpack function will receive three arguments:\n\n<table>\n  <tr>\n    <td>Argument</td>\n    <td>Details</td>\n  <tr>\n  <tr>\n    <td><code>config</code></td>\n    <td>This contains webpack configuration object that you can manipulate</td>\n  <tr>\n  <tr>\n    <td><code>context</code></td>\n    <td>This contains some keys that are useful for the build context\n      <br>\n      <table>\n        <tr>\n          <td><code>isDev</code></td>\n          <td><code>Boolean</code></td>\n          <td>Check if current build is for development</td>\n        </tr>\n        <tr>\n          <td><code>publicPath</code></td>\n          <td><code>String</code></td>\n          <td>Public Path of user defined or default's value</td>\n        </tr>\n        <tr>\n          <td><code>analyzeBundle</code></td>\n          <td><code>Boolean</code></td>\n          <td>Check if analyze bundle is enabled</td>\n        </tr>\n        <tr>\n          <td><code>buildDirectory</code></td>\n          <td><code>String</code></td>\n          <td>Build Directory of user defined or default's value</td>\n        </tr>\n        <tr>\n          <td><code>name</code></td>\n          <td><code>String</code></td>\n          <td>Build name <code>'server' || 'client'</code></td>\n        </tr>\n      </table>\n    </td>\n  <tr>\n  <tr>\n    <td><code>next</code></td>\n    <td>A callback required to be called to pass the custom configuration</td>\n  <tr>\n</table>\n\nExample of adding `ts-loader` through `maleo.config.js`:\n\n```js\n// maleo.config.js\n\n// Partly taken and modified from @airy/maleo-ts-plugin source\n// for simplicity purposes\n\nmodule.exports = {\n  webpack(config, context, next) {\n    const { isDev } = context\n\n    config.module.rules.push({\n      test: /\\.tsx?/,\n      exclude: /node_modules/,\n      use: [\n        require.resolve('@airy/maleo/lib/build/loaders/maleo-babel-loader'),\n        {\n          loader: 'ts-loader',\n          options: {\n            transpileOnly: true,\n          },\n        },\n      ],\n\n      if (isDev) {\n        config.plugins.push(new ForkTSCheckerWebpackPlugin());\n      }\n\n      return next();\n    })\n  },\n};\n```\n\n#### Customize Babel Config\n\nMaleo.js also let you have your own babel config. Just simply add `.babelrc` file at the root directory of your app.\n\nYou can include Maleo.js' babel preset in order to have latest JavaScript preset.\n\nHere's an example of `.babelrc` file:\n```json\n{\n  \"presets\": [\"@airy/maleo/babel\"],\n  \"plugins\": []\n}\n```\n\nThe `@airy/maleo/babel` preset includes everything you need to get your development started. The preset includes:\n- `@babel/preset-env`\n- `@babel/preset-react`\n- `@babel/plugin-proposal-class-properties`\n- `@babel/plugin-proposal-decorators`\n- `@babel/plugin-proposal-object-rest-spread`\n- `@babel/plugin-transform-runtime`\n- `react-loadable/babel`\n\n## CDN Support\n\nIf you are using a CDN, you can set up the `publicPath` setting and configure your CDN's origin to resolve to the domain that Maleo.js is hosted on.\n\n```js\n// maleo.config.js\n\nconst isProd = process.env.NODE_ENV === 'production';\n\nmodule.exports = {\n  assetPrefix: isProd && 'https://cdn.example.com';\n}\n```\n\n## FAQ\n\n== TO BE DETERMINED == \n\n## Plugins\n- [css-plugin](https://github.com/airyrooms/maleo-plugins/tree/master/packages/css-plugin)\n- [redux-plugin](https://github.com/airyrooms/maleo-plugins/tree/master/packages/redux-plugin)\n- [typescript-plugin](https://github.com/airyrooms/maleo-plugins/tree/master/packages/typescript-plugin)\n- [more](https://github.com/airyrooms/maleo-plugins)\n\n\n## Contributing\n[Please follow these steps to contribute to Maleo.js](https://github.com/airyrooms/maleo.js/blob/canary/CONTRIBUTING.md)\n\n[Please follow these steps to contribute to Maleo.js' plugins](https://github.com/airyrooms/maleo-plugins#contributing-guidelines)\n\n\n## Contributors\n\nThis project exists thanks to all the people who contribute. [Contribute](CONTRIBUTING.md).\n\nMany thanks to our **[contributors](https://github.com/airyrooms/maleo.js/graphs/contributors)**!\n\n\n## License\n\nMIT","readmeFilename":"README.md","bugs":{"url":"https://github.com/airyrooms/maleo.js/issues"},"homepage":"https://github.com/airyrooms/maleo.js#readme","_id":"@airy/maleo@0.2.1-canary.2","_nodeVersion":"11.14.0","_npmVersion":"lerna/3.13.1/node@v11.14.0+x64 (linux)","dist":{"integrity":"sha512-uR5k++IwWcSRSCnSX1U4U5XYxqaKubruxz8+ci2ptQDtYtxfToChvJoAngU4fZnDKQGD4sd6Zv5AqC286C6MYA==","shasum":"01fbc6d3520d66a2b6d9eb4613a5bf1b885ae061","tarball":"https://registry.npmjs.org/@airy/maleo/-/maleo-0.2.1-canary.2.tgz","fileCount":91,"unpackedSize":307954,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJcsER6CRA9TVsSAnZWagAArR0P/AtLZAoudh7k1HvKnDce\nZ9uKYMvvBLmNaTkxpjfkDqQDpuuMO3A/s/3DrdTRf7VoCCfWnjYdClbmRJAd\nBYhHGZnTGFdHS74jyGms2q2gzuLRrvFcztAFdyadALxvG8Hn9efckhZxRaxa\nt8+kM5kKpcZwDClA+6QzcpMjnUCRazXVVL199XDzpDMunnAT1Mz3lnZjLV79\nzFR+AzBaSG/DGQQSeyDrwT05GClva0bhMGWEQK2xwn/KyjLZD6Gcy/bPqREN\nyaz7BJM3Q1/cWYCSy8dJnj5ci+YzJAKTECVoy0JAn4yCokLGKIKomuzu7pIY\ndmp+yer1sv/y8Qra0YZ8Bc8zMktrXVCNVV/g9LmzIRQvPx+mACGj85swoMog\nt/nnvn9nCuc3aehQw6KUfbyHYj7s33euQ1059bYYNMxAOLx32ssRcF9tIuBf\nqe25WHq/+VtmxnI/zEsESH1yxpOb82rbJceki/79XYGDOhHoDmXC3ikjtzUf\naf8lLoqnzrc/yRfvaGHCF+eSseWsGSi46HzP40tBQd6yWQ7v0ljFIUUVsPhL\n02UhpGOnaVB67VrI34eHgYPOuecZDed5Ns2MJtrM68x6EtUTLWAlFGNsQkJ8\nNAcQWx3UmlYJeZiLpCzymkvORseJBSigS9/CYEpAqzaS7uk3jOt8xNwucUAF\ngb+c\r\n=tVcy\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDS3nWRa2TeYcE+Rzj06pVV2RKZBdlSCm2sNMzfe2h02gIhAKLnIOk20v+r1tQaFlvMlWam2xtUS3CiHb4LKo79QWjY"}]},"maintainers":[{"name":"airyrooms-engineering","email":"engineering@airyrooms.com"}],"_npmUser":{"name":"airyrooms-engineering","email":"engineering@airyrooms.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/maleo_0.2.1-canary.2_1555055737899_0.740344692339963"},"_hasShrinkwrap":false},"0.3.0":{"name":"@airy/maleo","version":"0.3.0","description":"Un-opinionated Universal Rendering Javascript Framework","repository":{"type":"git","url":"git+https://github.com/airyrooms/maleo.js.git","bugs":"https://github.com/airyrooms/maleo.js/issues"},"author":{"name":"Airy"},"license":"MIT","private":false,"sideEffects":false,"scripts":{"build":"rm -rf dist && gulp","watch":"gulp watch","prepublish":"yarn build","start":"node server.js"},"bin":{"maleo":"lib/bin/maleo.js"},"dependencies":{"@babel/core":"7.1.2","@babel/plugin-proposal-class-properties":"7.1.0","@babel/plugin-proposal-decorators":"7.1.2","@babel/plugin-proposal-object-rest-spread":"7.0.0","@babel/plugin-syntax-dynamic-import":"7.0.0","@babel/plugin-transform-runtime":"7.1.0","@babel/preset-env":"7.1.0","@babel/preset-react":"7.0.0","@babel/runtime":"7.1.2","@babel/runtime-corejs2":"7.2.0","babel-loader":"8.0.4","case-sensitive-paths-webpack-plugin":"2.1.2","compression":"1.7.4","es6-promise":"4.2.5","eventsource":"1.0.7","express":"4.16.4","figlet":"1.2.1","friendly-errors-webpack-plugin":"1.7.0","hard-source-webpack-plugin":"0.13.1","helmet":"3.14.0","isomorphic-fetch":"2.2.1","loader-utils":"1.1.0","node-notifier":"5.2.1","nodemon":"1.18.4","react-dom":"16.8.4","react-helmet":"5.2.0","react-loadable":"5.5.0","react-router-config":"5.0.0","react-router-dom":"5.0.0","rimraf":"2.6.3","source-map-support":"0.5.9","terser-webpack-plugin":"1.1.0","webpack":"4.29.0","webpack-bundle-analyzer":"3.0.3","webpack-cli":"3.1.2","webpack-dev-middleware":"3.4.0","webpack-hot-middleware":"2.24.3","webpackbar":"2.6.3"},"peerDependencies":{"react":"^16.8.4"},"devDependencies":{"@babel/preset-typescript":"^7.1.0","@types/webpack-bundle-analyzer":"^2.13.0","babel-plugin-module-resolver":"^3.1.1","gulp":"github:gulpjs/gulp#4.0","gulp-babel":"^8.0.0","gulp-clean":"^0.4.0","gulp-sourcemaps":"^2.6.4","gulp-uglify":"^3.0.1","gulp-watch":"^5.0.1","react":"^16.8.4"},"gitHead":"b684bb4b3d9290a531a63866e70cdbd82415684d","bugs":{"url":"https://github.com/airyrooms/maleo.js/issues"},"homepage":"https://github.com/airyrooms/maleo.js#readme","_id":"@airy/maleo@0.3.0","_nodeVersion":"10.11.0","_npmVersion":"lerna/3.13.1/node@v10.11.0+x64 (darwin)","dist":{"integrity":"sha512-GsOoAXXHBz8WTlptUsVQtktAOiQdzE27sRob2AKpcS4Zmezc5BNaNonugeQuYwMPPKOafcfTXeDlaEBKHcjMMw==","shasum":"36a1dadc722fc82836adf66b4158f59e63ebd86a","tarball":"https://registry.npmjs.org/@airy/maleo/-/maleo-0.3.0.tgz","fileCount":97,"unpackedSize":309407,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJc04hjCRA9TVsSAnZWagAA1U0P/jNe9+oIwwYDHUmJLdr0\nAm3qEGGVVPjjEcjdqwVRypiG/e30qlvndohv2tK8FWlFkm4btEfB86mis1zb\nfnwJUm27H2f+Gxan2OsSz4CAotBzK5bLtsBCY29pqJPIB2FuBuEd8o0QdlPR\nJFw1vamWEvk32fMh8c2g7UBxNkW8kIeClySFR2IBzgq078++Op2jsdCFjaqI\nTChAjnVvENpTXc1WBPG5nBnhzgsHggXLZ/vMFH8SXxzl8L6lk/17oimDF8TF\nAQ7EO8aDgKF1MBb4gG0kpxOSkm4Qkt59EvUIsC05oLCHzgdGSAcd8BduiMhq\ngKmCmH5FjMl2SpBerrNWrpQebCme+FvzAqG89kl7DMlo0vSP4/Gqkw3aGw8W\n9usXIXVAjmNQ1a4Cl0Kf9QSVAmFrb9Cyr6IODG22cJzo6BMV9b8xS6xyo73f\nnW1dKxXS8uNJZiY5ZpzBPN1ccroRx2mWMe3UfZWwlcilzsWbZfdWIwKdmczz\ncglS5IP0lChKIiOZExIGTQ+FehJgHlQ7OAMZ9tQe/eJAn5PAqrvsxqqFa3Wj\nzCaeyJM0rTAtXDNSOKdt6xYKv9Iq6n+P6Y6n3Cw2OWYhr6kYEG13cnACpptl\nLpEHpEgBOHiq/nVJeeVOcBngW74c5Vk8N3NIuR2XT+2aMEiMy0hIPDyWLjS6\nlXM5\r\n=4gnZ\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCVTGf/LZMuY23jXub312NqUHZQp7KgPPRqTSJIh3g8+wIgL6Jsz/e7D6kM2Hg52RCSQsFpTzj/qQWcWkmjdHjdeGI="}]},"maintainers":[{"name":"airyrooms-engineering","email":"engineering@airyrooms.com"}],"_npmUser":{"name":"airyrooms-engineering","email":"engineering@airyrooms.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/maleo_0.3.0_1557366882731_0.11922219853878424"},"_hasShrinkwrap":false},"0.3.1-canary.0":{"name":"@airy/maleo","version":"0.3.1-canary.0","description":"Un-opinionated Universal Rendering Javascript Framework","repository":{"type":"git","url":"git+https://github.com/airyrooms/maleo.js.git","bugs":"https://github.com/airyrooms/maleo.js/issues"},"author":{"name":"Airy"},"license":"MIT","private":false,"sideEffects":false,"scripts":{"build":"rm -rf dist && gulp","watch":"gulp watch","prepublish":"yarn build","start":"node server.js"},"bin":{"maleo":"lib/bin/maleo.js"},"dependencies":{"@babel/core":"7.1.2","@babel/plugin-proposal-class-properties":"7.1.0","@babel/plugin-proposal-decorators":"7.1.2","@babel/plugin-proposal-object-rest-spread":"7.0.0","@babel/plugin-syntax-dynamic-import":"7.0.0","@babel/plugin-transform-runtime":"7.1.0","@babel/preset-env":"7.1.0","@babel/preset-react":"7.0.0","@babel/runtime":"7.1.2","@babel/runtime-corejs2":"7.2.0","babel-loader":"8.0.4","case-sensitive-paths-webpack-plugin":"2.1.2","compression":"1.7.4","es6-promise":"4.2.5","eventsource":"1.0.7","express":"4.16.4","figlet":"1.2.1","friendly-errors-webpack-plugin":"1.7.0","hard-source-webpack-plugin":"0.13.1","helmet":"3.14.0","isomorphic-fetch":"2.2.1","loader-utils":"1.1.0","node-notifier":"5.2.1","nodemon":"1.18.4","react-dom":"16.8.4","react-helmet":"5.2.0","react-loadable":"5.5.0","react-router-config":"5.0.0","react-router-dom":"5.0.0","rimraf":"2.6.3","source-map-support":"0.5.9","terser-webpack-plugin":"1.1.0","webpack":"4.29.0","webpack-bundle-analyzer":"3.0.3","webpack-cli":"3.1.2","webpack-dev-middleware":"3.4.0","webpack-hot-middleware":"2.24.3","webpackbar":"2.6.3"},"peerDependencies":{"react":"^16.8.4"},"devDependencies":{"@babel/preset-typescript":"^7.1.0","@types/webpack-bundle-analyzer":"^2.13.0","babel-plugin-module-resolver":"^3.1.1","gulp":"github:gulpjs/gulp#4.0","gulp-babel":"^8.0.0","gulp-clean":"^0.4.0","gulp-sourcemaps":"^2.6.4","gulp-uglify":"^3.0.1","gulp-watch":"^5.0.1","react":"^16.8.4"},"gitHead":"99887987b12926b7bb0d099070381fa5c78e5b26","readme":"[![Build Status](https://travis-ci.org/airyrooms/maleo.js.svg?branch=master)](https://travis-ci.org/airyrooms/maleo.js)\n[![Coverage Status](https://coveralls.io/repos/github/airyrooms/maleo.js/badge.svg?branch=master)](https://coveralls.io/github/airyrooms/maleo.js?branch=master)\n[![npm version](https://badge.fury.io/js/%40airy%2Fmaleo.svg)](https://badge.fury.io/js/%40airy%2Fmaleo)\n[![Issues](http://img.shields.io/github/issues/airyrooms/maleo.js.svg)]( https://github.com/airyrooms/maleo.js/issues)\n[![MIT license](https://img.shields.io/badge/License-MIT-blue.svg)](https://github.com/airyrooms/maleo.js/blob/canary/LICENSE)\n[![Discord Chat](https://img.shields.io/discord/550498214789513218.svg)](https://discord.gg/9eArCQn)\n[![Conventional Commits](https://img.shields.io/badge/Conventional%20Commits-1.0.0-green.svg)](https://conventionalcommits.org)\n\n\n# Welcome to Maleo.js\n\nMaleo.js is an un-opinionated framework to enable Universal Rendering in JavaScript using React with no hassle.\n\nWe are here to solve the time consuming setups Universal Rendering Required.\n\n---\n\nReadme below is the documentation for the `canary` (prerelease) branch. To view the documentation for the latest stable Maleo.js version change branch to `master`\n\n---\n\n# Table of Contents\n- [Features](#features)\n- [Setup](#setup)\n- [Component Lifecycle](#component-lifecycle)\n- [Routing](#routing)\n- [Dynamic Import Component](#dynamic-import-component)\n  - [Tips](#tips)\n  - [Preloading](#preloading)\n- [Customizable Component](#customizable-component)\n  - [Custom Document](#custom-document)\n  - [Custom Wrap](#custom-wrap)\n- [Custom Configuration](#custom-configuration)\n  - [Customize Server](#customize-server)\n  - [Customize Webpack](#customize-webpack)\n  - [Customize Babel Config](#customize-babel-config)\n- [CDN Support](#cdn-support)\n- [Plugins](#plugins)\n- [FAQ](#faq)\n- [Contributing](#contributing)\n\n---\n\n## Features\n- Universal Rendering\n- Plugin based framework\n- Customizable\n\n## Setup\n\nInstall Maleo.js \n\n>*for now change `@airy/maleo` to `@airy/maleo@canary` until we publish the stable version of this package*\n\n\n**NPM**\n```bash\n$ npm install --save @airy/maleo react\n```\n**Yarn**\n```bash\n$ yarn add @airy/maleo react\n```\n\nAdd this script to your `package.json`\n```json\n{\n  \"scripts\": {\n    \"dev\": \"maleo dev\",\n    \"build\": \"export NODE_ENV=production && maleo build\",\n    \"start\": \"export NODE_ENV=production && node .maleo/server.js\"\n  }\n}\n```\n\nCreate a page Root component\n```jsx\n// ./src/Root.jsx\nimport React from 'react';\n\n// Export default is required for registering page\nexport default class RootComponent extends React.Component {\n  render() {\n    return (\n      <h1>Hello World!</h1>\n    )\n  }\n}\n```\n\nAnd lastly, create a routing file on your project root directory called `routes.json` and register your page component\n```json\n[\n  {\n    \"path\": \"/\",\n    \"page\": \"./src/Root\"\n  }\n]\n```\n\nAfter that you can now run `$ npm run dev` and go to `http://localhost:3000`.\n\nYou should now see your app running on your browser.\n\nBy now you should see \n- Automatic transpilation and bundling (with webpack and babel)\n- ~~Hot code reloading~~ [#17](https://github.com/airyrooms/maleo.js/issues/17)\n- Server rendering\n\nTo see how simple this is, check out the sample app!\n\n## Component Lifecycle \n\nMaleo.js added a new component lifecycle hook called `getInitialProps`, this function is called during Server Side Rendering (SSR).\n\nThis is useful especially for SEO purposes. \n\nExample for stateful component:\n```jsx\nimport React from 'react';\n\nexport default class extends React.Component {\n  static getInitialProps = async (ctx) => {\n    const { req } = ctx;\n\n    const userAgent = req ? req.headers['user-agent'] : navigator.userAgent;\n\n    // the return value will be passed as props for this component\n    return { userAgent };\n  }\n\n  render() {\n    return (\n      <div>\n        Hello World {this.props.userAgent}\n      </div>\n    );\n  }\n}\n```\n\nExample for stateless component:\n```jsx\nconst Component = (props) => (\n  <div>\n    Hello World {props.userAgent}\n  </div>\n);\n\nComponent.getInitialprops = async (ctx) => {\n  const { req } = ctx;\n\n  const userAgent = req ? req.headers['user-agent'] : navigator.userAgent;\n\n  return { userAgent };\n};\n\nexport default Component;\n```\n\n`getInitialProps` receives a context object with the following properties:\n- `req` - HTTP request object (server only)\n- `res` - HTTP response object (server only)\n- `...wrapProps` - Spreaded properties from custom Wrap\n- `...appProps` - Spreaded properties from custom App\n\n## Routing\n\nRouting is declared in a centralized route config.\nRegister all the route config in `routes.json` file.\n\nIf you put the `routes.json` files on root directory, Maleo will automatically register your route. Otherwise put path to your routes on [Maleo config](#custom-configuration).\n\nRoutes file has to export default the route configuration.\nThe route object **expected to have distinct key** to indicate the route.\n\n<table>\n  <tr>\n    <td>Key</td>\n    <td>Type</td>\n    <td>Description</td>\n  </tr>\n  <tr>\n    <td><code>path</code></td>\n    <td><code>String!</code></td>\n    <td>Routes path</td>\n  </tr>\n  <tr>\n    <td><code>page</code></td>\n    <td><code>String!</code></td>\n    <td>Path to React Component for this route path</td>\n  </tr>\n  <tr>\n    <td><code>exact</code></td>\n    <td><code>Boolean?</code> [<code>false</code>]</td>\n    <td>To make url has to match exactly the path in order to render the component. Give <code>false</code> value if the route is a wrapper route component</td>\n  </tr>\n  <tr>\n    <td><code>routes</code></td>\n    <td><code>RouteObject?</code></td>\n    <td>Nested route</td>\n  </tr>\n</table>\n\n\nFor example:\n```json\n[\n  {\n    \"page\": \"./src/MainApp\",\n    \"routes\": [\n      {\n        \"path\": \"/\",\n        \"page\": \"./src/Search\",\n        \"exact\": true\n      },\n      {\n        \"path\": \"/search\",\n        \"page\": \"./src/Search\",\n        \"routes\": [\n          {\n            \"path\": \"/search/hello\",\n            \"page\": \"./src/Detail\",\n            \"exact\": true\n          }\n        ]\n      },\n      {\n        \"path\": \"/detail\",\n        \"page\": \"./src/Detail\",\n        \"exact\": true\n      }\n    ]\n  }\n]\n```\n\n## Dynamic Import Component\nMaleo.js supports TC39 [dynamic import proposal](https://github.com/tc39/proposal-dynamic-import) for JavaScript.\n\nYou can think dynamic import as another way to split your code into manageable chunks. You can use our `Dynamic` function which utilizes [react loadable](https://github.com/jamiebuilds/react-loadable)\n\nFor Example\n```js\n// DynamicComponent.js\nimport Dynamic from '@airy/maleo/dynamic';\n\nexport default Dynamic({\n  loader: () => import( /* webpackChunkName:\"DynamicComponent\" */ './component/DynamicLoad'),\n})\n```\n\n### **Preloading**\n\nFor optimization purposes, you can also preload a component even before the component got rendered.\n\nFor example, if you want to load component when a button get pressed, you can start preloading the component when the user hovers the mouse over the button.\n\nThe component created by `Dynamic` exposes a [static `preload` method](https://github.com/jamiebuilds/react-loadable#loadablecomponentpreload).\n\n```jsx\nimport React from 'react';\nimport Dynamic from '@airy/maleo/dynamic';\n\nconst DynamicBar = Dynamic({\n  loader: () => import('./Bar'),\n  loading: LoadingCompoinent\n});\n\nclass MyComponent extends React.Component {\n  state = { showBar: false };\n\n  onClick = () => {\n    this.setState({ showBar: true });\n  };\n\n  onMouseOver = () => DynamicBar.preload();\n\n  render() {\n    return (\n      <div>\n        <button\n          onClick={this.onClick}\n          onMouseOver={this.onMouseOver}>\n          Show Bar\n        </button>\n        { this.state.showBar && <DynamicBar /> }\n      </div>\n    )\n  }\n}\n```\n\n## Customizable Component\n\n### Custom Document\n\nHighly inspired by what [Next.js](https://github.com/zeit/next.js) has done on their awesome template customization.\n\nMaleo.js also enable customization on `Document` as document's markup. So you don't need to include tags like `<html>`, `<head>`, `<body>`, etc. \n\nTo override the default behavior, you'll need to create a component that extends the `Document` React class provided by Maleo.\n\n```jsx\n// document.jsx\nimport React from 'react';\nimport { Document, Header, Main, Scripts } from '@airy/maleo/document';\n\nexport default class extends Document {\n  render() {\n    return (\n      <html>\n        <Header>\n          <title>Maleo JS</title>\n          <meta charset=\"utf-8\" />\n          <meta name=\"description\" content=\"Maleo.js is awesome!\" />\n\n          <style>\n            {` body { background-color: #fff } `}\n          </style>\n        </Header>\n\n        <body>\n          <Main />\n\n          <Scripts />\n\n          <ReduxScript />\n        </body>\n      </html>\n    );\n  }\n}\n```\n\n### Custom Wrap\n\nMaleo.js uses the `Wrap` component to initialize pages. `Wrap` contains React Router's Component. You can add HoC here to wrap the application and control the page initialization. Which allows you to do amazing things like:\n- Persisting layour between page changes\n- Keeping state when navigating pages\n- Custom error handling using `componentDidCatch`\n- Inject additional data into pages (like Redux provider, etc)\n\nTo override the default behavior, you'll need to create a component that extends the `Wrap` React class provided by Maleo.\n\n```jsx\n// wrap.jsx\nimport React from 'react';\nimport { Wrap } from '@airy/maleo/wrap';\n\n// Redux plugin for Maleo.js\n// Hoc that creates store and add Redux Provider\nimport { withRedux } from '@airy/maleo-redux-plugin';\n\n// Custom Wrapper that will be rendered for the whole Application\nimport Content from './component/Content';\nimport NavigationBar from './component/NavigationBar';\n\nimport { createStore } from './store';\n\n@withRedux(createStore)\nexport default class extends Wrap {\n  static getInitialProps = (ctx) => {\n    const { store } = ctx\n    // you receive store from context\n    // you can access or do something with the store here\n    console.log('Initialized Redux Store', store);\n    return {}\n  }\n\n  render() {\n    const { Container, containerProps, App, appProps } = this.props\n\n    return (\n      <Container {...containerProps}>\n        <NavigationBar />\n        <Content>\n          <App {...appProps}/>\n        </Content>\n      </Container>\n    )\n  }\n}\n\n```\nIf you put `document.jsx` and `wrap.jsx` on root directory (the same level with `package.json`), then Maleo will automatically register your custom Document and Wrap. Otherwise, you can add the path to your custom Document and Wrap on [Maleo config](#custom-configuration)\n\n---\n***We are also working on adding default and customizable `Error` component page***\n\n---\n\n## Custom Configuration\n\nFor more advanced configuration of Maleo.js, like `webpack` config, registering `plugins`, path to your routes, custom Document and Wrap, and adding `path alias`, you can create a `maleo.config.js` in the root of your project directory. (same directory with `package.json`)\n\n```js\n// maleo.config.js\n\nmodule.exports = {\n  /* config options here */\n}\n```\n\nHere are the API's for the configuration:\n\n<table>\n  <tr>\n    <td>Key</td>\n    <td>Type</td>\n    <td>Description</td>\n  </tr>\n  <tr>\n    <td><code>buildDir</code></td>\n    <td><code>String?</code> [<code>.maleo</code>]</td>\n    <td>Directory to put Maleo.js' build assets</td>\n  </tr>\n  <tr>\n    <td><code>cache</code></td>\n    <td><code>Boolean?</code> [<code>true</code>]</td>\n    <td>Enable webpack build caching</td>\n  </tr>\n  <tr>\n    <td><code>sourceMaps</code></td>\n    <td><code>Boolean?</code> [<code>true</code>]</td>\n    <td>Enable webpack to generate source maps</td>\n  </tr>\n  <tr>\n    <td><code>alias</code></td>\n    <td><code>Object?</code></td>\n    <td>A key value pair for aliasing path directory\n      <br>\n      <code>\n      { 'component': './src/component' }\n      </code>\n    </td>\n  </tr>\n  <tr>\n    <td><code>publicPath</code></td>\n    <td><code>String?</code> [<code>/_assets/</code>]</td>\n    <td>To customize webpack's <code>publicPath</code> \n    <br>Comes in handy if using CDN to put built assets</td>\n  </tr>\n  <tr>\n    <td><code>analyzeBundle</code></td>\n    <td><code>Boolean?</code> [<code>false</code>]</td>\n    <td>To enable webpack's bundle analyzer, for analyzing bundle sizes during bundle debugging should Maleo.js' build process got slow</td>\n  </tr>\n  <tr>\n    <td><code>webpack</code></td>\n    <td><code>Function?</code></td>\n    <td>To customize webpack configuration, more details <a href=\"#customize-webpack\">here</a></td>\n  </tr>\n  <tr>\n    <td><code>routes</code></td>\n    <td><code>string?</code> [<code>rootDir/routes.jsx</code>]</td>\n    <td>Path to your routes file</td>\n  </tr>\n  <tr>\n    <td><code>customDocument</code></td>\n    <td><code>string?</code> [<code>rootDir/document.jsx</code>]</td>\n    <td>Path to your custom document file</td>\n  </tr>\n  <tr>\n    <td><code>customWrap</code></td>\n    <td><code>string?</code> [<code>rootDir/wrap.jsx</code>]</td>\n    <td>Path to your custom wrap file</td>\n  </tr>\n  <tr>\n    <td><code>customApp</code></td>\n    <td><code>string?</code> [<code>rootDir/app.jsx</code>]</td>\n    <td>Path to your custom app file</td>\n  </tr>\n</table>\n\n#### Customize Server\n\nCreate a `server.js` file on root directory where your `package.json` lives.\nHere you can customize Maleo's server.\n```js\nimport { Server } from '@airy/maleo/server';\nimport path from 'path';\n\nimport routeConfig from './routes';\n\nconst PORT = process.env.PORT || 8080;\n\nconst maleoServer = Server.init({\n  port: PORT,\n});\n\nmaleoServer.run(() => {\n  console.log('Server running on port :', PORT);\n});\n```\n\nHere are the API's for the configuration:\n\n<table>\n  <tr>\n    <td>Key</td>\n    <td>Type</td>\n    <td>Description</td>\n  </tr>\n  <tr>\n    <td><code>port</code></td>\n    <td><code>Number?</code> [<code>3000</code>]</td>\n    <td>Port to run Maleo server</td>\n  </tr>\n  <tr>\n    <td><code>assetDir</code></td>\n    <td><code>String?</code> [<code>'<root>/.maleo/client'</code>]</td>\n    <td>Directory for all client related assets</td>\n  </tr>\n</table>\n\n#### Customize Webpack\n\nYou are able to extend Maleo.js' default webpack configuration by defining a function on `maleo.config.js`\n\n```js\n// maleo.config.js\n\nmodule.exports = {\n  webpack(config, context, next) {\n    // Perform customizations to webpack config\n    // Important: This call is required in order to let Maleo pass the config to webpack\n    return next(); \n  },\n};\n```\n\nWebpack function will receive three arguments:\n\n<table>\n  <tr>\n    <td>Argument</td>\n    <td>Details</td>\n  <tr>\n  <tr>\n    <td><code>config</code></td>\n    <td>This contains webpack configuration object that you can manipulate</td>\n  <tr>\n  <tr>\n    <td><code>context</code></td>\n    <td>This contains some keys that are useful for the build context\n      <br>\n      <table>\n        <tr>\n          <td><code>isDev</code></td>\n          <td><code>Boolean</code></td>\n          <td>Check if current build is for development</td>\n        </tr>\n        <tr>\n          <td><code>publicPath</code></td>\n          <td><code>String</code></td>\n          <td>Public Path of user defined or default's value</td>\n        </tr>\n        <tr>\n          <td><code>analyzeBundle</code></td>\n          <td><code>Boolean</code></td>\n          <td>Check if analyze bundle is enabled</td>\n        </tr>\n        <tr>\n          <td><code>buildDirectory</code></td>\n          <td><code>String</code></td>\n          <td>Build Directory of user defined or default's value</td>\n        </tr>\n        <tr>\n          <td><code>name</code></td>\n          <td><code>String</code></td>\n          <td>Build name <code>'server' || 'client'</code></td>\n        </tr>\n      </table>\n    </td>\n  <tr>\n  <tr>\n    <td><code>next</code></td>\n    <td>A callback required to be called to pass the custom configuration</td>\n  <tr>\n</table>\n\nExample of adding `ts-loader` through `maleo.config.js`:\n\n```js\n// maleo.config.js\n\n// Partly taken and modified from @airy/maleo-ts-plugin source\n// for simplicity purposes\n\nmodule.exports = {\n  webpack(config, context, next) {\n    const { isDev } = context\n\n    config.module.rules.push({\n      test: /\\.tsx?/,\n      exclude: /node_modules/,\n      use: [\n        require.resolve('@airy/maleo/lib/build/loaders/maleo-babel-loader'),\n        {\n          loader: 'ts-loader',\n          options: {\n            transpileOnly: true,\n          },\n        },\n      ],\n\n      if (isDev) {\n        config.plugins.push(new ForkTSCheckerWebpackPlugin());\n      }\n\n      return next();\n    })\n  },\n};\n```\n\n#### Customize Babel Config\n\nMaleo.js also let you have your own babel config. Just simply add `.babelrc` file at the root directory of your app.\n\nYou can include Maleo.js' babel preset in order to have latest JavaScript preset.\n\nHere's an example of `.babelrc` file:\n```json\n{\n  \"presets\": [\"@airy/maleo/babel\"],\n  \"plugins\": []\n}\n```\n\nThe `@airy/maleo/babel` preset includes everything you need to get your development started. The preset includes:\n- `@babel/preset-env`\n- `@babel/preset-react`\n- `@babel/plugin-proposal-class-properties`\n- `@babel/plugin-proposal-decorators`\n- `@babel/plugin-proposal-object-rest-spread`\n- `@babel/plugin-transform-runtime`\n- `react-loadable/babel`\n\n## CDN Support\n\nIf you are using a CDN, you can set up the `publicPath` setting and configure your CDN's origin to resolve to the domain that Maleo.js is hosted on.\n\n```js\n// maleo.config.js\n\nconst isProd = process.env.NODE_ENV === 'production';\n\nmodule.exports = {\n  assetPrefix: isProd && 'https://cdn.example.com';\n}\n```\n\n## FAQ\n\n== TO BE DETERMINED == \n\n## Plugins\n- [css-plugin](https://github.com/airyrooms/maleo-plugins/tree/master/packages/css-plugin)\n- [redux-plugin](https://github.com/airyrooms/maleo-plugins/tree/master/packages/redux-plugin)\n- [typescript-plugin](https://github.com/airyrooms/maleo-plugins/tree/master/packages/typescript-plugin)\n- [more](https://github.com/airyrooms/maleo-plugins)\n\n\n## Contributing\n[Please follow these steps to contribute to Maleo.js](https://github.com/airyrooms/maleo.js/blob/canary/CONTRIBUTING.md)\n\n[Please follow these steps to contribute to Maleo.js' plugins](https://github.com/airyrooms/maleo-plugins#contributing-guidelines)\n\n\n## Contributors\n\nThis project exists thanks to all the people who contribute. [Contribute](CONTRIBUTING.md).\n\nMany thanks to our **[contributors](https://github.com/airyrooms/maleo.js/graphs/contributors)**!\n\n\n## License\n\nMIT","readmeFilename":"README.md","bugs":{"url":"https://github.com/airyrooms/maleo.js/issues"},"homepage":"https://github.com/airyrooms/maleo.js#readme","_id":"@airy/maleo@0.3.1-canary.0","_nodeVersion":"10.15.3","_npmVersion":"lerna/3.13.1/node@v10.15.3+x64 (linux)","dist":{"integrity":"sha512-Gxpjh5mKN8MP2iWfInEaaw7RHofBdGOoHhYoUZcL8XHJSm08IhtcKGu32WSfR+1AUVDBfHwYMjCgE5UdSo+fzA==","shasum":"98616b7a885048f27e9c939ea048306881ce72c4","tarball":"https://registry.npmjs.org/@airy/maleo/-/maleo-0.3.1-canary.0.tgz","fileCount":91,"unpackedSize":308947,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJc04oBCRA9TVsSAnZWagAAqVsP/Rb+7PDYpeBJFqYD4Jpm\n6kCRpqMYrBKM6Ot/oekaBatSQ2B7D/WEmngvDCwcFv0XK0C1+wDGZgTO6I5/\n7du3QGF/LWrS1ujIzEDG6+fbBf7X9ntMyMaw/+li45d5gn7lQpxM8jd5i2tp\nw2vOM4LeBbQhv+NopSr/UwRvdWgebrEffkAQ7TjpqnHYq+b2+ujApAdHgb6a\n0klk+z+elEI4jsDLmkSsMbdVIomy4HMCfeKp+zfCuni81EBGIR50vbfrVqgx\nXPxE1H0wuZl3YedyS2k9QdG3vKOarBaX1tgRbupjdB9hcigBMcM2fQfUmKJG\n3V+DPfoy3PZz1Y/GFZRVBjzNhixlzF5QqBVJeQRhClJVe/8JNTfRy/q5eyOK\nk7g+DH0DA2J5o0dpB+OrZeuD/SvR3xZHQKD63utFDMjn+TgeDvCMOxCk39ar\nOD/JJBQw+6CZL7Z7MKTShH+cdPNsnwPeDtKheg+62V2XZhTeQND6cyh+kpbf\ntV07nat1kqIq9miSfrkWjIfFxP+Rzb4EBTzzoR6nOqsCdbetFVv2uziG38MF\n7f9Z+zAvUaT9yzx7DDKqYL6cyXpzpyfWEaIREl2aABOT4K4Acd3E6fbd1SB2\nZ0cV0VK+KLRJOjmaIeZMZLYkQSIERd+K4XVgeAGfKRNQQYwJ160aSMK8osZP\nGQe7\r\n=ug4x\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIAgaSUprwqtc1FeK3yERJ75JODRyDxO3Rpj+PHyAhD8PAiEAx06E+3oELOFRTzwtxwMWrNnRGyGIevFbO4HCcyjbN98="}]},"maintainers":[{"name":"airyrooms-engineering","email":"engineering@airyrooms.com"}],"_npmUser":{"name":"airyrooms-engineering","email":"engineering@airyrooms.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/maleo_0.3.1-canary.0_1557367297287_0.24323217514897166"},"_hasShrinkwrap":false},"0.3.1-canary.1":{"name":"@airy/maleo","version":"0.3.1-canary.1","description":"Un-opinionated Universal Rendering Javascript Framework","repository":{"type":"git","url":"git+https://github.com/airyrooms/maleo.js.git","bugs":"https://github.com/airyrooms/maleo.js/issues"},"author":{"name":"Airy"},"license":"MIT","private":false,"sideEffects":false,"scripts":{"build":"rm -rf dist && gulp","watch":"gulp watch","prepublish":"yarn build","start":"node server.js"},"bin":{"maleo":"lib/bin/maleo.js"},"dependencies":{"@babel/core":"7.1.2","@babel/plugin-proposal-class-properties":"7.1.0","@babel/plugin-proposal-decorators":"7.1.2","@babel/plugin-proposal-object-rest-spread":"7.0.0","@babel/plugin-syntax-dynamic-import":"7.0.0","@babel/plugin-transform-runtime":"7.1.0","@babel/preset-env":"7.1.0","@babel/preset-react":"7.0.0","@babel/runtime":"7.1.2","@babel/runtime-corejs2":"7.2.0","babel-loader":"8.0.4","case-sensitive-paths-webpack-plugin":"2.1.2","compression":"1.7.4","es6-promise":"4.2.5","eventsource":"1.0.7","express":"4.16.4","figlet":"1.2.1","friendly-errors-webpack-plugin":"1.7.0","hard-source-webpack-plugin":"0.13.1","helmet":"3.14.0","isomorphic-fetch":"2.2.1","loader-utils":"1.1.0","node-notifier":"5.2.1","nodemon":"1.18.4","react-dom":"16.8.4","react-helmet":"5.2.0","react-loadable":"5.5.0","react-router-config":"5.0.0","react-router-dom":"5.0.0","rimraf":"2.6.3","source-map-support":"0.5.9","terser-webpack-plugin":"1.1.0","webpack":"4.29.0","webpack-bundle-analyzer":"3.0.3","webpack-cli":"3.1.2","webpack-dev-middleware":"3.4.0","webpack-hot-middleware":"2.24.3","webpackbar":"2.6.3"},"peerDependencies":{"react":"^16.8.4"},"devDependencies":{"@babel/preset-typescript":"^7.1.0","@types/webpack-bundle-analyzer":"^2.13.0","babel-plugin-module-resolver":"^3.1.1","gulp":"github:gulpjs/gulp#4.0","gulp-babel":"^8.0.0","gulp-clean":"^0.4.0","gulp-sourcemaps":"^2.6.4","gulp-uglify":"^3.0.1","gulp-watch":"^5.0.1","react":"^16.8.4"},"gitHead":"5004c38d7d8b10f465009ac2bb6a57a56d655093","readme":"[![Build Status](https://travis-ci.org/airyrooms/maleo.js.svg?branch=master)](https://travis-ci.org/airyrooms/maleo.js)\n[![Coverage Status](https://coveralls.io/repos/github/airyrooms/maleo.js/badge.svg?branch=master)](https://coveralls.io/github/airyrooms/maleo.js?branch=master)\n[![npm version](https://badge.fury.io/js/%40airy%2Fmaleo.svg)](https://badge.fury.io/js/%40airy%2Fmaleo)\n[![Issues](http://img.shields.io/github/issues/airyrooms/maleo.js.svg)]( https://github.com/airyrooms/maleo.js/issues)\n[![MIT license](https://img.shields.io/badge/License-MIT-blue.svg)](https://github.com/airyrooms/maleo.js/blob/canary/LICENSE)\n[![Discord Chat](https://img.shields.io/discord/550498214789513218.svg)](https://discord.gg/9eArCQn)\n[![Conventional Commits](https://img.shields.io/badge/Conventional%20Commits-1.0.0-green.svg)](https://conventionalcommits.org)\n\n\n# Welcome to Maleo.js\n\nMaleo.js is an un-opinionated framework to enable Universal Rendering in JavaScript using React with no hassle.\n\nWe are here to solve the time consuming setups Universal Rendering Required.\n\n---\n\nReadme below is the documentation for the `canary` (prerelease) branch. To view the documentation for the latest stable Maleo.js version change branch to `master`\n\n---\n\n# Table of Contents\n- [Features](#features)\n- [Setup](#setup)\n- [Component Lifecycle](#component-lifecycle)\n- [Routing](#routing)\n- [Dynamic Import Component](#dynamic-import-component)\n  - [Tips](#tips)\n  - [Preloading](#preloading)\n- [Customizable Component](#customizable-component)\n  - [Custom Document](#custom-document)\n  - [Custom Wrap](#custom-wrap)\n- [Custom Configuration](#custom-configuration)\n  - [Customize Server](#customize-server)\n  - [Customize Webpack](#customize-webpack)\n  - [Customize Babel Config](#customize-babel-config)\n- [CDN Support](#cdn-support)\n- [Plugins](#plugins)\n- [FAQ](#faq)\n- [Contributing](#contributing)\n\n---\n\n## Features\n- Universal Rendering\n- Plugin based framework\n- Customizable\n\n## Setup\n\nInstall Maleo.js \n\n>*for now change `@airy/maleo` to `@airy/maleo@canary` until we publish the stable version of this package*\n\n\n**NPM**\n```bash\n$ npm install --save @airy/maleo react\n```\n**Yarn**\n```bash\n$ yarn add @airy/maleo react\n```\n\nAdd this script to your `package.json`\n```json\n{\n  \"scripts\": {\n    \"dev\": \"maleo dev\",\n    \"build\": \"export NODE_ENV=production && maleo build\",\n    \"start\": \"export NODE_ENV=production && maleo run\"\n  }\n}\n```\n\nCreate a page Root component\n```jsx\n// ./src/Root.jsx\nimport React from 'react';\n\n// Export default is required for registering page\nexport default class RootComponent extends React.Component {\n  render() {\n    return (\n      <h1>Hello World!</h1>\n    )\n  }\n}\n```\n\nAnd lastly, create a routing file on your project root directory called `routes.json` and register your page component\n```json\n[\n  {\n    \"path\": \"/\",\n    \"page\": \"./src/Root\"\n  }\n]\n```\n\nAfter that you can now run `$ npm run dev` and go to `http://localhost:3000`.\n\nYou should now see your app running on your browser.\n\nBy now you should see \n- Automatic transpilation and bundling (with webpack and babel)\n- ~~Hot code reloading~~ [#17](https://github.com/airyrooms/maleo.js/issues/17)\n- Server rendering\n\nTo see how simple this is, check out the sample app!\n\n## Component Lifecycle \n\nMaleo.js added a new component lifecycle hook called `getInitialProps`, this function is called during Server Side Rendering (SSR).\n\nThis is useful especially for SEO purposes. \n\nExample for stateful component:\n```jsx\nimport React from 'react';\n\nexport default class extends React.Component {\n  static getInitialProps = async (ctx) => {\n    const { req } = ctx;\n\n    const userAgent = req ? req.headers['user-agent'] : navigator.userAgent;\n\n    // the return value will be passed as props for this component\n    return { userAgent };\n  }\n\n  render() {\n    return (\n      <div>\n        Hello World {this.props.userAgent}\n      </div>\n    );\n  }\n}\n```\n\nExample for stateless component:\n```jsx\nconst Component = (props) => (\n  <div>\n    Hello World {props.userAgent}\n  </div>\n);\n\nComponent.getInitialprops = async (ctx) => {\n  const { req } = ctx;\n\n  const userAgent = req ? req.headers['user-agent'] : navigator.userAgent;\n\n  return { userAgent };\n};\n\nexport default Component;\n```\n\n`getInitialProps` receives a context object with the following properties:\n- `req` - HTTP request object (server only)\n- `res` - HTTP response object (server only)\n- `...wrapProps` - Spreaded properties from custom Wrap\n- `...appProps` - Spreaded properties from custom App\n\n## Routing\n\nRouting is declared in a centralized route config.\nRegister all the route config in `routes.json` file.\n\nIf you put the `routes.json` files on root directory, Maleo will automatically register your route. Otherwise put path to your routes on [Maleo config](#custom-configuration).\n\nRoutes file has to export default the route configuration.\nThe route object **expected to have distinct key** to indicate the route.\n\n<table>\n  <tr>\n    <td>Key</td>\n    <td>Type</td>\n    <td>Description</td>\n  </tr>\n  <tr>\n    <td><code>path</code></td>\n    <td><code>String!</code></td>\n    <td>Routes path</td>\n  </tr>\n  <tr>\n    <td><code>page</code></td>\n    <td><code>String!</code></td>\n    <td>Path to React Component for this route path</td>\n  </tr>\n  <tr>\n    <td><code>exact</code></td>\n    <td><code>Boolean?</code> [<code>false</code>]</td>\n    <td>To make url has to match exactly the path in order to render the component. Give <code>false</code> value if the route is a wrapper route component</td>\n  </tr>\n  <tr>\n    <td><code>routes</code></td>\n    <td><code>RouteObject?</code></td>\n    <td>Nested route</td>\n  </tr>\n</table>\n\n\nFor example:\n```json\n[\n  {\n    \"page\": \"./src/MainApp\",\n    \"routes\": [\n      {\n        \"path\": \"/\",\n        \"page\": \"./src/Search\",\n        \"exact\": true\n      },\n      {\n        \"path\": \"/search\",\n        \"page\": \"./src/Search\",\n        \"routes\": [\n          {\n            \"path\": \"/search/hello\",\n            \"page\": \"./src/Detail\",\n            \"exact\": true\n          }\n        ]\n      },\n      {\n        \"path\": \"/detail\",\n        \"page\": \"./src/Detail\",\n        \"exact\": true\n      }\n    ]\n  }\n]\n```\n\n## Dynamic Import Component\nMaleo.js supports TC39 [dynamic import proposal](https://github.com/tc39/proposal-dynamic-import) for JavaScript.\n\nYou can think dynamic import as another way to split your code into manageable chunks. You can use our `Dynamic` function which utilizes [react loadable](https://github.com/jamiebuilds/react-loadable)\n\nFor Example\n```js\n// DynamicComponent.js\nimport Dynamic from '@airy/maleo/dynamic';\n\nexport default Dynamic({\n  loader: () => import( /* webpackChunkName:\"DynamicComponent\" */ './component/DynamicLoad'),\n})\n```\n\n### **Preloading**\n\nFor optimization purposes, you can also preload a component even before the component got rendered.\n\nFor example, if you want to load component when a button get pressed, you can start preloading the component when the user hovers the mouse over the button.\n\nThe component created by `Dynamic` exposes a [static `preload` method](https://github.com/jamiebuilds/react-loadable#loadablecomponentpreload).\n\n```jsx\nimport React from 'react';\nimport Dynamic from '@airy/maleo/dynamic';\n\nconst DynamicBar = Dynamic({\n  loader: () => import('./Bar'),\n  loading: LoadingCompoinent\n});\n\nclass MyComponent extends React.Component {\n  state = { showBar: false };\n\n  onClick = () => {\n    this.setState({ showBar: true });\n  };\n\n  onMouseOver = () => DynamicBar.preload();\n\n  render() {\n    return (\n      <div>\n        <button\n          onClick={this.onClick}\n          onMouseOver={this.onMouseOver}>\n          Show Bar\n        </button>\n        { this.state.showBar && <DynamicBar /> }\n      </div>\n    )\n  }\n}\n```\n\n## Customizable Component\n\n### Custom Document\n\nHighly inspired by what [Next.js](https://github.com/zeit/next.js) has done on their awesome template customization.\n\nMaleo.js also enable customization on `Document` as document's markup. So you don't need to include tags like `<html>`, `<head>`, `<body>`, etc. \n\nTo override the default behavior, you'll need to create a component that extends the `Document` React class provided by Maleo.\n\n```jsx\n// document.jsx\nimport React from 'react';\nimport { Document, Header, Main, Scripts } from '@airy/maleo/document';\n\nexport default class extends Document {\n  render() {\n    return (\n      <html>\n        <Header>\n          <title>Maleo JS</title>\n          <meta charset=\"utf-8\" />\n          <meta name=\"description\" content=\"Maleo.js is awesome!\" />\n\n          <style>\n            {` body { background-color: #fff } `}\n          </style>\n        </Header>\n\n        <body>\n          <Main />\n\n          <Scripts />\n\n          <ReduxScript />\n        </body>\n      </html>\n    );\n  }\n}\n```\n\n### Custom Wrap\n\nMaleo.js uses the `Wrap` component to initialize pages. `Wrap` contains React Router's Component. You can add HoC here to wrap the application and control the page initialization. Which allows you to do amazing things like:\n- Persisting layour between page changes\n- Keeping state when navigating pages\n- Custom error handling using `componentDidCatch`\n- Inject additional data into pages (like Redux provider, etc)\n\nTo override the default behavior, you'll need to create a component that extends the `Wrap` React class provided by Maleo.\n\n```jsx\n// wrap.jsx\nimport React from 'react';\nimport { Wrap } from '@airy/maleo/wrap';\n\n// Redux plugin for Maleo.js\n// Hoc that creates store and add Redux Provider\nimport { withRedux } from '@airy/maleo-redux-plugin';\n\n// Custom Wrapper that will be rendered for the whole Application\nimport Content from './component/Content';\nimport NavigationBar from './component/NavigationBar';\n\nimport { createStore } from './store';\n\n@withRedux(createStore)\nexport default class extends Wrap {\n  static getInitialProps = (ctx) => {\n    const { store } = ctx\n    // you receive store from context\n    // you can access or do something with the store here\n    console.log('Initialized Redux Store', store);\n    return {}\n  }\n\n  render() {\n    const { Container, containerProps, App, appProps } = this.props\n\n    return (\n      <Container {...containerProps}>\n        <NavigationBar />\n        <Content>\n          <App {...appProps}/>\n        </Content>\n      </Container>\n    )\n  }\n}\n\n```\nIf you put `document.jsx` and `wrap.jsx` on root directory (the same level with `package.json`), then Maleo will automatically register your custom Document and Wrap. Otherwise, you can add the path to your custom Document and Wrap on [Maleo config](#custom-configuration)\n\n---\n***We are also working on adding default and customizable `Error` component page***\n\n---\n\n## Custom Configuration\n\nFor more advanced configuration of Maleo.js, like `webpack` config, registering `plugins`, path to your routes, custom Document and Wrap, and adding `path alias`, you can create a `maleo.config.js` in the root of your project directory. (same directory with `package.json`)\n\n```js\n// maleo.config.js\n\nmodule.exports = {\n  /* config options here */\n}\n```\n\nHere are the API's for the configuration:\n\n<table>\n  <tr>\n    <td>Key</td>\n    <td>Type</td>\n    <td>Description</td>\n  </tr>\n  <tr>\n    <td><code>buildDir</code></td>\n    <td><code>String?</code> [<code>.maleo</code>]</td>\n    <td>Directory to put Maleo.js' build assets</td>\n  </tr>\n  <tr>\n    <td><code>cache</code></td>\n    <td><code>Boolean?</code> [<code>true</code>]</td>\n    <td>Enable webpack build caching</td>\n  </tr>\n  <tr>\n    <td><code>isDev</code></td>\n    <td><code>Boolean?</code> [<code>process.env.NODE_ENV === 'development'</code>]</td>\n    <td>Enable development build configuration</td>\n  </tr>\n  <tr>\n    <td><code>sourceMaps</code></td>\n    <td><code>Boolean?</code> [<code>true</code>]</td>\n    <td>Enable webpack to generate source maps</td>\n  </tr>\n  <tr>\n    <td><code>alias</code></td>\n    <td><code>Object?</code></td>\n    <td>A key value pair for aliasing path directory\n      <br>\n      <code>\n      { 'component': './src/component' }\n      </code>\n    </td>\n  </tr>\n  <tr>\n    <td><code>publicPath</code></td>\n    <td><code>String?</code> [<code>/_assets/</code>]</td>\n    <td>To customize webpack's <code>publicPath</code> \n    <br>Comes in handy if using CDN to put built assets</td>\n  </tr>\n  <tr>\n    <td><code>analyzeBundle</code></td>\n    <td><code>Boolean?</code> [<code>false</code>]</td>\n    <td>To enable webpack's bundle analyzer, for analyzing bundle sizes during bundle debugging should Maleo.js' build process got slow</td>\n  </tr>\n  <tr>\n    <td><code>webpack</code></td>\n    <td><code>Function?</code></td>\n    <td>To customize webpack configuration, more details <a href=\"#customize-webpack\">here</a></td>\n  </tr>\n  <tr>\n    <td><code>routes</code></td>\n    <td><code>string?</code> [<code>rootDir/routes.jsx</code>]</td>\n    <td>Path to your routes file</td>\n  </tr>\n  <tr>\n    <td><code>customDocument</code></td>\n    <td><code>string?</code> [<code>rootDir/document.jsx</code>]</td>\n    <td>Path to your custom document file</td>\n  </tr>\n  <tr>\n    <td><code>customWrap</code></td>\n    <td><code>string?</code> [<code>rootDir/wrap.jsx</code>]</td>\n    <td>Path to your custom wrap file</td>\n  </tr>\n  <tr>\n    <td><code>customApp</code></td>\n    <td><code>string?</code> [<code>rootDir/app.jsx</code>]</td>\n    <td>Path to your custom app file</td>\n  </tr>\n</table>\n\n#### Customize Server\n\nCreate a `server.js` file on root directory where your `package.json` lives.\nHere you can customize Maleo's server.\n```js\nimport { Server } from '@airy/maleo/server';\nimport path from 'path';\n\nimport routeConfig from './routes';\n\nconst PORT = process.env.PORT || 8080;\n\nconst maleoServer = Server.init({\n  port: PORT,\n});\n\nmaleoServer.run(() => {\n  console.log('Server running on port :', PORT);\n});\n```\n\nHere are the API's for the configuration:\n\n<table>\n  <tr>\n    <td>Key</td>\n    <td>Type</td>\n    <td>Description</td>\n  </tr>\n  <tr>\n    <td><code>port</code></td>\n    <td><code>Number?</code> [<code>3000</code>]</td>\n    <td>Port to run Maleo server</td>\n  </tr>\n  <tr>\n    <td><code>assetDir</code></td>\n    <td><code>String?</code> [<code>'<root>/.maleo/client'</code>]</td>\n    <td>Directory for all client related assets</td>\n  </tr>\n</table>\n\n#### Customize Webpack\n\nYou are able to extend Maleo.js' default webpack configuration by defining a function on `maleo.config.js`\n\n```js\n// maleo.config.js\n\nmodule.exports = {\n  webpack(config, context, next) {\n    // Perform customizations to webpack config\n    // Important: This call is required in order to let Maleo pass the config to webpack\n    return next(); \n  },\n};\n```\n\nWebpack function will receive three arguments:\n\n<table>\n  <tr>\n    <td>Argument</td>\n    <td>Details</td>\n  <tr>\n  <tr>\n    <td><code>config</code></td>\n    <td>This contains webpack configuration object that you can manipulate</td>\n  <tr>\n  <tr>\n    <td><code>context</code></td>\n    <td>This contains some keys that are useful for the build context\n      <br>\n      <table>\n        <tr>\n          <td><code>isDev</code></td>\n          <td><code>Boolean</code></td>\n          <td>Check if current build is for development</td>\n        </tr>\n        <tr>\n          <td><code>publicPath</code></td>\n          <td><code>String</code></td>\n          <td>Public Path of user defined or default's value</td>\n        </tr>\n        <tr>\n          <td><code>analyzeBundle</code></td>\n          <td><code>Boolean</code></td>\n          <td>Check if analyze bundle is enabled</td>\n        </tr>\n        <tr>\n          <td><code>buildDirectory</code></td>\n          <td><code>String</code></td>\n          <td>Build Directory of user defined or default's value</td>\n        </tr>\n        <tr>\n          <td><code>name</code></td>\n          <td><code>String</code></td>\n          <td>Build name <code>'server' || 'client'</code></td>\n        </tr>\n      </table>\n    </td>\n  <tr>\n  <tr>\n    <td><code>next</code></td>\n    <td>A callback required to be called to pass the custom configuration</td>\n  <tr>\n</table>\n\nExample of adding `ts-loader` through `maleo.config.js`:\n\n```js\n// maleo.config.js\n\n// Partly taken and modified from @airy/maleo-ts-plugin source\n// for simplicity purposes\n\nmodule.exports = {\n  webpack(config, context, next) {\n    const { isDev } = context\n\n    config.module.rules.push({\n      test: /\\.tsx?/,\n      exclude: /node_modules/,\n      use: [\n        require.resolve('@airy/maleo/lib/build/loaders/maleo-babel-loader'),\n        {\n          loader: 'ts-loader',\n          options: {\n            transpileOnly: true,\n          },\n        },\n      ],\n\n      if (isDev) {\n        config.plugins.push(new ForkTSCheckerWebpackPlugin());\n      }\n\n      return next();\n    })\n  },\n};\n```\n\n#### Customize Babel Config\n\nMaleo.js also let you have your own babel config. Just simply add `.babelrc` file at the root directory of your app.\n\nYou can include Maleo.js' babel preset in order to have latest JavaScript preset.\n\nHere's an example of `.babelrc` file:\n```json\n{\n  \"presets\": [\"@airy/maleo/babel\"],\n  \"plugins\": []\n}\n```\n\nThe `@airy/maleo/babel` preset includes everything you need to get your development started. The preset includes:\n- `@babel/preset-env`\n- `@babel/preset-react`\n- `@babel/plugin-proposal-class-properties`\n- `@babel/plugin-proposal-decorators`\n- `@babel/plugin-proposal-object-rest-spread`\n- `@babel/plugin-transform-runtime`\n- `react-loadable/babel`\n\n## CDN Support\n\nIf you are using a CDN, you can set up the `publicPath` setting and configure your CDN's origin to resolve to the domain that Maleo.js is hosted on.\n\n```js\n// maleo.config.js\n\nconst isProd = process.env.NODE_ENV === 'production';\n\nmodule.exports = {\n  assetPrefix: isProd && 'https://cdn.example.com';\n}\n```\n\n## FAQ\n\n== TO BE DETERMINED == \n\n## Plugins\n- [css-plugin](https://github.com/airyrooms/maleo-plugins/tree/master/packages/css-plugin)\n- [redux-plugin](https://github.com/airyrooms/maleo-plugins/tree/master/packages/redux-plugin)\n- [typescript-plugin](https://github.com/airyrooms/maleo-plugins/tree/master/packages/typescript-plugin)\n- [more](https://github.com/airyrooms/maleo-plugins)\n\n\n## Contributing\n[Please follow these steps to contribute to Maleo.js](https://github.com/airyrooms/maleo.js/blob/canary/CONTRIBUTING.md)\n\n[Please follow these steps to contribute to Maleo.js' plugins](https://github.com/airyrooms/maleo-plugins#contributing-guidelines)\n\n\n## Contributors\n\nThis project exists thanks to all the people who contribute. [Contribute](CONTRIBUTING.md).\n\nMany thanks to our **[contributors](https://github.com/airyrooms/maleo.js/graphs/contributors)**!\n\n\n## License\n\nMIT","readmeFilename":"README.md","bugs":{"url":"https://github.com/airyrooms/maleo.js/issues"},"homepage":"https://github.com/airyrooms/maleo.js#readme","_id":"@airy/maleo@0.3.1-canary.1","_nodeVersion":"10.15.3","_npmVersion":"lerna/3.13.1/node@v10.15.3+x64 (linux)","dist":{"integrity":"sha512-PAxrsKftKvS3rwMgaZwhT7oXSEm0um8SbvooXoZUI/c5beL2qSBZop+OpqIoCQGD8uEvqdHSoU6I4RIjCKwEsw==","shasum":"3145cc0747186a3df5c58c007ae0294116830dad","tarball":"https://registry.npmjs.org/@airy/maleo/-/maleo-0.3.1-canary.1.tgz","fileCount":91,"unpackedSize":312348,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJc36PkCRA9TVsSAnZWagAAmUsP/3pUsssX1ELFgfVO5u0+\nacpwt7sF+/Ffkm9p2iU5TkTIAN4mPVrmwwDMRZqhS1jx1hMJ99dUtN3o9v4W\nf8hc+YsEuM/YANN0BU7ry8G6WJa2qtNs++2ruyuQish336ZlGmVS/CIYiqgW\n0jY9CQn9dnIKm5ayyIW9aSGbDF1sLdhTrHogoGb4N0A/J2Oj8iUbPMqmYhYA\nHUEq5sS8xWnDRf8eK02NnZOKQ4Sy/a9xAPwPqMIQLiu5UmKvvA1UJ0nTo8fA\n4nk3fpl+rwQ/sngpP74cWqklycFCwCCfz8j5LzNANPPoKWGlHar2LpWjH/PX\nV2mfSD2gc5Oo2pN3aF2DPXITWIGw/CQQjM1fTtLRT0tdWiZmADqsiTABt6gh\nabV9vygOx6rggJfWiaa4DND2b8k7DacNgke4kH5ko2FlCRxG+9wXADQksYWa\nF+D3Z9z5GcjEB8XdqiHvaGBS24BDjFwpBS3Cpfej9PEdVip6GgKvYmXEuhEl\nxyJdvGetY7JIh7nQjh8b0r2xXFURY7AWLvURYy7vfNbquu70Xu79DH9XBTfC\nk7o7So2MfclSO5XHKOzr04XUjSoEWGznHr1sPtUpDCciWHMTKdcUMMWHmfat\nJGoH4KxE81q19BJe9HemWyz9s6gn4xT6IYJDKXxfwPgcWPmRzoPcwK70VWXu\nGK/S\r\n=dPyz\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIEevDAeFXaTwnRiElAZS0BBIcIOoXcWpoeiSY+qDlpE/AiBotCSi4jWFKeAsXmG5L948tAt2fJDNtmzOqIohreERjA=="}]},"maintainers":[{"name":"airyrooms-engineering","email":"engineering@airyrooms.com"}],"_npmUser":{"name":"airyrooms-engineering","email":"engineering@airyrooms.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/maleo_0.3.1-canary.1_1558160356309_0.7539393574212605"},"_hasShrinkwrap":false},"0.3.1-canary.2":{"name":"@airy/maleo","version":"0.3.1-canary.2","description":"Un-opinionated Universal Rendering Javascript Framework","repository":{"type":"git","url":"git+https://github.com/airyrooms/maleo.js.git","bugs":"https://github.com/airyrooms/maleo.js/issues"},"author":{"name":"Airy"},"license":"MIT","private":false,"sideEffects":false,"scripts":{"build":"rm -rf dist && gulp","watch":"gulp watch","prepublish":"yarn build","start":"node server.js"},"bin":{"maleo":"lib/bin/maleo.js"},"dependencies":{"@babel/core":"7.1.2","@babel/plugin-proposal-class-properties":"7.1.0","@babel/plugin-proposal-decorators":"7.1.2","@babel/plugin-proposal-object-rest-spread":"7.0.0","@babel/plugin-syntax-dynamic-import":"7.0.0","@babel/plugin-transform-runtime":"7.1.0","@babel/preset-env":"7.1.0","@babel/preset-react":"7.0.0","@babel/runtime":"7.1.2","@babel/runtime-corejs2":"7.2.0","babel-loader":"8.0.4","case-sensitive-paths-webpack-plugin":"2.1.2","compression":"1.7.4","es6-promise":"4.2.5","eventsource":"1.0.7","express":"4.16.4","figlet":"1.2.1","friendly-errors-webpack-plugin":"1.7.0","hard-source-webpack-plugin":"0.13.1","helmet":"3.14.0","isomorphic-fetch":"2.2.1","loader-utils":"1.1.0","node-notifier":"5.2.1","nodemon":"1.18.4","react-dom":"16.8.4","react-helmet":"5.2.0","react-loadable":"5.5.0","react-router-config":"5.0.0","react-router-dom":"5.0.0","rimraf":"2.6.3","source-map-support":"0.5.9","terser-webpack-plugin":"1.1.0","webpack":"4.29.0","webpack-bundle-analyzer":"3.0.3","webpack-cli":"3.1.2","webpack-dev-middleware":"3.4.0","webpack-hot-middleware":"2.24.3","webpackbar":"2.6.3"},"peerDependencies":{"react":"^16.8.4"},"devDependencies":{"@babel/preset-typescript":"^7.1.0","@types/webpack-bundle-analyzer":"^2.13.0","babel-plugin-module-resolver":"^3.1.1","gulp":"github:gulpjs/gulp#4.0","gulp-babel":"^8.0.0","gulp-clean":"^0.4.0","gulp-sourcemaps":"^2.6.4","gulp-uglify":"^3.0.1","gulp-watch":"^5.0.1","react":"^16.8.4"},"gitHead":"cec38e4fa12833c922a5cc119d009bd654ede1ca","readme":"[![Build Status](https://travis-ci.org/airyrooms/maleo.js.svg?branch=master)](https://travis-ci.org/airyrooms/maleo.js)\n[![Coverage Status](https://coveralls.io/repos/github/airyrooms/maleo.js/badge.svg?branch=master)](https://coveralls.io/github/airyrooms/maleo.js?branch=master)\n[![npm version](https://badge.fury.io/js/%40airy%2Fmaleo.svg)](https://badge.fury.io/js/%40airy%2Fmaleo)\n[![Issues](http://img.shields.io/github/issues/airyrooms/maleo.js.svg)]( https://github.com/airyrooms/maleo.js/issues)\n[![MIT license](https://img.shields.io/badge/License-MIT-blue.svg)](https://github.com/airyrooms/maleo.js/blob/canary/LICENSE)\n[![Discord Chat](https://img.shields.io/discord/550498214789513218.svg)](https://discord.gg/9eArCQn)\n[![Conventional Commits](https://img.shields.io/badge/Conventional%20Commits-1.0.0-green.svg)](https://conventionalcommits.org)\n\n\n# Welcome to Maleo.js\n\nMaleo.js is an un-opinionated framework to enable Universal Rendering in JavaScript using React with no hassle.\n\nWe are here to solve the time consuming setups Universal Rendering Required.\n\n---\n\nReadme below is the documentation for the `canary` (prerelease) branch. To view the documentation for the latest stable Maleo.js version change branch to `master`\n\n---\n\n# Table of Contents\n- [Features](#features)\n- [Setup](#setup)\n- [Component Lifecycle](#component-lifecycle)\n- [Routing](#routing)\n- [Dynamic Import Component](#dynamic-import-component)\n  - [Tips](#tips)\n  - [Preloading](#preloading)\n- [Customizable Component](#customizable-component)\n  - [Custom Document](#custom-document)\n  - [Custom Wrap](#custom-wrap)\n- [Custom Configuration](#custom-configuration)\n  - [Customize Server](#customize-server)\n  - [Customize Webpack](#customize-webpack)\n  - [Customize Babel Config](#customize-babel-config)\n- [CDN Support](#cdn-support)\n- [Plugins](#plugins)\n- [FAQ](#faq)\n- [Contributing](#contributing)\n\n---\n\n## Features\n- Universal Rendering\n- Plugin based framework\n- Customizable\n\n## Setup\n\nInstall Maleo.js \n\n>*for now change `@airy/maleo` to `@airy/maleo@canary` until we publish the stable version of this package*\n\n\n**NPM**\n```bash\n$ npm install --save @airy/maleo react\n```\n**Yarn**\n```bash\n$ yarn add @airy/maleo react\n```\n\nAdd this script to your `package.json`\n```json\n{\n  \"scripts\": {\n    \"dev\": \"maleo dev\",\n    \"build\": \"export NODE_ENV=production && maleo build\",\n    \"start\": \"export NODE_ENV=production && maleo run\"\n  }\n}\n```\n\nCreate a page Root component\n```jsx\n// ./src/Root.jsx\nimport React from 'react';\n\n// Export default is required for registering page\nexport default class RootComponent extends React.Component {\n  render() {\n    return (\n      <h1>Hello World!</h1>\n    )\n  }\n}\n```\n\nAnd lastly, create a routing file on your project root directory called `routes.json` and register your page component\n```json\n[\n  {\n    \"path\": \"/\",\n    \"page\": \"./src/Root\"\n  }\n]\n```\n\nAfter that you can now run `$ npm run dev` and go to `http://localhost:3000`.\n\nYou should now see your app running on your browser.\n\nBy now you should see \n- Automatic transpilation and bundling (with webpack and babel)\n- ~~Hot code reloading~~ [#17](https://github.com/airyrooms/maleo.js/issues/17)\n- Server rendering\n\nTo see how simple this is, check out the sample app!\n\n## Component Lifecycle \n\nMaleo.js added a new component lifecycle hook called `getInitialProps`, this function is called during Server Side Rendering (SSR) and during route changes on Client Side Rendering (CSR).\n\nThis is useful especially for SEO purposes. \n\nExample for stateful component:\n```jsx\nimport React from 'react';\n\nexport default class extends React.Component {\n  static getInitialProps = async (ctx) => {\n    const { req } = ctx;\n\n    // check if getInitialProps is called on server or on client\n    const userAgent = req ? req.headers['user-agent'] : navigator.userAgent;\n\n    // the return value will be passed as props for this component\n    return { userAgent };\n  }\n\n  render() {\n    return (\n      <div>\n        Hello World {this.props.userAgent}\n      </div>\n    );\n  }\n}\n```\n\nExample for stateless component:\n```jsx\nconst Component = (props) => (\n  <div>\n    Hello World {props.userAgent}\n  </div>\n);\n\nComponent.getInitialprops = async (ctx) => {\n  const { req } = ctx;\n\n  const userAgent = req ? req.headers['user-agent'] : navigator.userAgent;\n\n  return { userAgent };\n};\n\nexport default Component;\n```\n\n`getInitialProps` receives a context object with the following properties:\n- `req` - HTTP request object (server only)\n- `res` - HTTP response object (server only)\n- `...wrapProps` - Spreaded properties from custom Wrap\n- `...appProps` - Spreaded properties from custom App\n\n## Routing\n\nRouting is declared in a centralized route config.\nRegister all the route config in `routes.json` file.\n\nIf you put the `routes.json` files on root directory, Maleo will automatically register your route. Otherwise put path to your routes on [Maleo config](#custom-configuration).\n\nRoutes file has to export default the route configuration.\nThe route object **expected to have distinct key** to indicate the route.\n\n<table>\n  <tr>\n    <td>Key</td>\n    <td>Type</td>\n    <td>Description</td>\n  </tr>\n  <tr>\n    <td><code>path</code></td>\n    <td><code>String!</code></td>\n    <td>Routes path</td>\n  </tr>\n  <tr>\n    <td><code>page</code></td>\n    <td><code>String!</code></td>\n    <td>Path to React Component for this route path</td>\n  </tr>\n  <tr>\n    <td><code>exact</code></td>\n    <td><code>Boolean?</code> [<code>false</code>]</td>\n    <td>To make url has to match exactly the path in order to render the component. Give <code>false</code> value if the route is a wrapper route component</td>\n  </tr>\n  <tr>\n    <td><code>routes</code></td>\n    <td><code>RouteObject?</code></td>\n    <td>Nested route</td>\n  </tr>\n</table>\n\n\nFor example:\n```json\n[\n  {\n    \"page\": \"./src/MainApp\",\n    \"routes\": [\n      {\n        \"path\": \"/\",\n        \"page\": \"./src/Search\",\n        \"exact\": true\n      },\n      {\n        \"path\": \"/search\",\n        \"page\": \"./src/Search\",\n        \"routes\": [\n          {\n            \"path\": \"/search/hello\",\n            \"page\": \"./src/Detail\",\n            \"exact\": true\n          }\n        ]\n      },\n      {\n        \"path\": \"/detail\",\n        \"page\": \"./src/Detail\",\n        \"exact\": true\n      }\n    ]\n  }\n]\n```\n\n## Dynamic Import Component\nMaleo.js supports TC39 [dynamic import proposal](https://github.com/tc39/proposal-dynamic-import) for JavaScript.\n\nYou can think dynamic import as another way to split your code into manageable chunks. You can use our `Dynamic` function which utilizes [react loadable](https://github.com/jamiebuilds/react-loadable)\n\nFor Example\n```js\n// DynamicComponent.js\nimport Dynamic from '@airy/maleo/dynamic';\n\nexport default Dynamic({\n  loader: () => import( /* webpackChunkName:\"DynamicComponent\" */ './component/DynamicLoad'),\n})\n```\n\n### **Preloading**\n\nFor optimization purposes, you can also preload a component even before the component got rendered.\n\nFor example, if you want to load component when a button get pressed, you can start preloading the component when the user hovers the mouse over the button.\n\nThe component created by `Dynamic` exposes a [static `preload` method](https://github.com/jamiebuilds/react-loadable#loadablecomponentpreload).\n\n```jsx\nimport React from 'react';\nimport Dynamic from '@airy/maleo/dynamic';\n\nconst DynamicBar = Dynamic({\n  loader: () => import('./Bar'),\n  loading: LoadingCompoinent\n});\n\nclass MyComponent extends React.Component {\n  state = { showBar: false };\n\n  onClick = () => {\n    this.setState({ showBar: true });\n  };\n\n  onMouseOver = () => DynamicBar.preload();\n\n  render() {\n    return (\n      <div>\n        <button\n          onClick={this.onClick}\n          onMouseOver={this.onMouseOver}>\n          Show Bar\n        </button>\n        { this.state.showBar && <DynamicBar /> }\n      </div>\n    )\n  }\n}\n```\n\n## Customizable Component\n\n### Custom Document\n\nHighly inspired by what [Next.js](https://github.com/zeit/next.js) has done on their awesome template customization.\n\nMaleo.js also enable customization on `Document` as document's markup. So you don't need to include tags like `<html>`, `<head>`, `<body>`, etc. \n\nTo override the default behavior, you'll need to create a component that extends the `Document` React class provided by Maleo.\n\n```jsx\n// document.jsx\nimport React from 'react';\nimport { Document, Header, Main, Scripts } from '@airy/maleo/document';\n\nexport default class extends Document {\n  render() {\n    return (\n      <html>\n        <Header>\n          <title>Maleo JS</title>\n          <meta charset=\"utf-8\" />\n          <meta name=\"description\" content=\"Maleo.js is awesome!\" />\n\n          <style>\n            {` body { background-color: #fff } `}\n          </style>\n        </Header>\n\n        <body>\n          <Main />\n\n          <Scripts />\n\n          <ReduxScript />\n        </body>\n      </html>\n    );\n  }\n}\n```\n\n### Custom Wrap\n\nMaleo.js uses the `Wrap` component to initialize pages. `Wrap` contains React Router's Component. You can add HoC here to wrap the application and control the page initialization. Which allows you to do amazing things like:\n- Persisting layour between page changes\n- Keeping state when navigating pages\n- Custom error handling using `componentDidCatch`\n- Inject additional data into pages (like Redux provider, etc)\n\nTo override the default behavior, you'll need to create a component that extends the `Wrap` React class provided by Maleo.\n\n```jsx\n// wrap.jsx\nimport React from 'react';\nimport { Wrap } from '@airy/maleo/wrap';\n\n// Redux plugin for Maleo.js\n// Hoc that creates store and add Redux Provider\nimport { withRedux } from '@airy/maleo-redux-plugin';\n\n// Custom Wrapper that will be rendered for the whole Application\nimport Content from './component/Content';\nimport NavigationBar from './component/NavigationBar';\n\nimport { createStore } from './store';\n\n@withRedux(createStore)\nexport default class extends Wrap {\n  static getInitialProps = (ctx) => {\n    const { store } = ctx\n    // you receive store from context\n    // you can access or do something with the store here\n    console.log('Initialized Redux Store', store);\n    return {}\n  }\n\n  render() {\n    const { Container, containerProps, App, appProps } = this.props\n\n    return (\n      <Container {...containerProps}>\n        <NavigationBar />\n        <Content>\n          <App {...appProps}/>\n        </Content>\n      </Container>\n    )\n  }\n}\n\n```\nIf you put `document.jsx` and `wrap.jsx` on root directory (the same level with `package.json`), then Maleo will automatically register your custom Document and Wrap. Otherwise, you can add the path to your custom Document and Wrap on [Maleo config](#custom-configuration)\n\n---\n***We are also working on adding default and customizable `Error` component page***\n\n---\n\n## Custom Configuration\n\nFor more advanced configuration of Maleo.js, like `webpack` config, registering `plugins`, path to your routes, custom Document and Wrap, and adding `path alias`, you can create a `maleo.config.js` in the root of your project directory. (same directory with `package.json`)\n\n```js\n// maleo.config.js\n\nmodule.exports = {\n  /* config options here */\n}\n```\n\nHere are the API's for the configuration:\n\n<table>\n  <tr>\n    <td>Key</td>\n    <td>Type</td>\n    <td>Description</td>\n  </tr>\n  <tr>\n    <td><code>buildDir</code></td>\n    <td><code>String?</code> [<code>.maleo</code>]</td>\n    <td>Directory to put Maleo.js' build assets</td>\n  </tr>\n  <tr>\n    <td><code>cache</code></td>\n    <td><code>Boolean?</code> [<code>true</code>]</td>\n    <td>Enable webpack build caching</td>\n  </tr>\n  <tr>\n    <td><code>isDev</code></td>\n    <td><code>Boolean?</code> [<code>process.env.NODE_ENV === 'development'</code>]</td>\n    <td>Enable development build configuration</td>\n  </tr>\n  <tr>\n    <td><code>sourceMaps</code></td>\n    <td><code>Boolean?</code> [<code>true</code>]</td>\n    <td>Enable webpack to generate source maps</td>\n  </tr>\n  <tr>\n    <td><code>alias</code></td>\n    <td><code>Object?</code></td>\n    <td>A key value pair for aliasing path directory\n      <br>\n      <code>\n      { 'component': './src/component' }\n      </code>\n    </td>\n  </tr>\n  <tr>\n    <td><code>publicPath</code></td>\n    <td><code>String?</code> [<code>/_assets/</code>]</td>\n    <td>To customize webpack's <code>publicPath</code> \n    <br>Comes in handy if using CDN to put built assets</td>\n  </tr>\n  <tr>\n    <td><code>analyzeBundle</code></td>\n    <td><code>Boolean?</code> [<code>false</code>]</td>\n    <td>To enable webpack's bundle analyzer, for analyzing bundle sizes during bundle debugging should Maleo.js' build process got slow</td>\n  </tr>\n  <tr>\n    <td><code>webpack</code></td>\n    <td><code>Function?</code></td>\n    <td>To customize webpack configuration, more details <a href=\"#customize-webpack\">here</a></td>\n  </tr>\n  <tr>\n    <td><code>routes</code></td>\n    <td><code>string?</code> [<code>rootDir/routes.jsx</code>]</td>\n    <td>Path to your routes file</td>\n  </tr>\n  <tr>\n    <td><code>customDocument</code></td>\n    <td><code>string?</code> [<code>rootDir/document.jsx</code>]</td>\n    <td>Path to your custom document file</td>\n  </tr>\n  <tr>\n    <td><code>customWrap</code></td>\n    <td><code>string?</code> [<code>rootDir/wrap.jsx</code>]</td>\n    <td>Path to your custom wrap file</td>\n  </tr>\n  <tr>\n    <td><code>customApp</code></td>\n    <td><code>string?</code> [<code>rootDir/app.jsx</code>]</td>\n    <td>Path to your custom app file</td>\n  </tr>\n</table>\n\n#### Customize Server\n\nCreate a `server.js` file on root directory where your `package.json` lives.\nHere you can customize Maleo's server.\n```js\nimport { Server } from '@airy/maleo/server';\nimport path from 'path';\n\nimport routeConfig from './routes';\n\nconst PORT = process.env.PORT || 8080;\n\nconst maleoServer = Server.init({\n  port: PORT,\n});\n\nmaleoServer.run(() => {\n  console.log('Server running on port :', PORT);\n});\n```\n\nHere are the API's for the configuration:\n\n<table>\n  <tr>\n    <td>Key</td>\n    <td>Type</td>\n    <td>Description</td>\n  </tr>\n  <tr>\n    <td><code>port</code></td>\n    <td><code>Number?</code> [<code>3000</code>]</td>\n    <td>Port to run Maleo server</td>\n  </tr>\n  <tr>\n    <td><code>assetDir</code></td>\n    <td><code>String?</code> [<code>'<root>/.maleo/client'</code>]</td>\n    <td>Directory for all client related assets</td>\n  </tr>\n</table>\n\n#### Customize Webpack\n\nYou are able to extend Maleo.js' default webpack configuration by defining a function on `maleo.config.js`\n\n```js\n// maleo.config.js\n\nmodule.exports = {\n  webpack(config, context, next) {\n    // Perform customizations to webpack config\n    // Important: This call is required in order to let Maleo pass the config to webpack\n    return next(); \n  },\n};\n```\n\nWebpack function will receive three arguments:\n\n<table>\n  <tr>\n    <td>Argument</td>\n    <td>Details</td>\n  <tr>\n  <tr>\n    <td><code>config</code></td>\n    <td>This contains webpack configuration object that you can manipulate</td>\n  <tr>\n  <tr>\n    <td><code>context</code></td>\n    <td>This contains some keys that are useful for the build context\n      <br>\n      <table>\n        <tr>\n          <td><code>isDev</code></td>\n          <td><code>Boolean</code></td>\n          <td>Check if current build is for development</td>\n        </tr>\n        <tr>\n          <td><code>publicPath</code></td>\n          <td><code>String</code></td>\n          <td>Public Path of user defined or default's value</td>\n        </tr>\n        <tr>\n          <td><code>analyzeBundle</code></td>\n          <td><code>Boolean</code></td>\n          <td>Check if analyze bundle is enabled</td>\n        </tr>\n        <tr>\n          <td><code>buildDirectory</code></td>\n          <td><code>String</code></td>\n          <td>Build Directory of user defined or default's value</td>\n        </tr>\n        <tr>\n          <td><code>name</code></td>\n          <td><code>String</code></td>\n          <td>Build name <code>'server' || 'client'</code></td>\n        </tr>\n      </table>\n    </td>\n  <tr>\n  <tr>\n    <td><code>next</code></td>\n    <td>A callback required to be called to pass the custom configuration</td>\n  <tr>\n</table>\n\nExample of adding `ts-loader` through `maleo.config.js`:\n\n```js\n// maleo.config.js\n\n// Partly taken and modified from @airy/maleo-ts-plugin source\n// for simplicity purposes\n\nmodule.exports = {\n  webpack(config, context, next) {\n    const { isDev } = context\n\n    config.module.rules.push({\n      test: /\\.tsx?/,\n      exclude: /node_modules/,\n      use: [\n        require.resolve('@airy/maleo/lib/build/loaders/maleo-babel-loader'),\n        {\n          loader: 'ts-loader',\n          options: {\n            transpileOnly: true,\n          },\n        },\n      ],\n\n      if (isDev) {\n        config.plugins.push(new ForkTSCheckerWebpackPlugin());\n      }\n\n      return next();\n    })\n  },\n};\n```\n\n#### Customize Babel Config\n\nMaleo.js also let you have your own babel config. Just simply add `.babelrc` file at the root directory of your app.\n\nYou can include Maleo.js' babel preset in order to have latest JavaScript preset.\n\nHere's an example of `.babelrc` file:\n```json\n{\n  \"presets\": [\"@airy/maleo/babel\"],\n  \"plugins\": []\n}\n```\n\nThe `@airy/maleo/babel` preset includes everything you need to get your development started. The preset includes:\n- `@babel/preset-env`\n- `@babel/preset-react`\n- `@babel/plugin-proposal-class-properties`\n- `@babel/plugin-proposal-decorators`\n- `@babel/plugin-proposal-object-rest-spread`\n- `@babel/plugin-transform-runtime`\n- `react-loadable/babel`\n\n## CDN Support\n\nIf you are using a CDN, you can set up the `publicPath` setting and configure your CDN's origin to resolve to the domain that Maleo.js is hosted on.\n\n```js\n// maleo.config.js\n\nconst isProd = process.env.NODE_ENV === 'production';\n\nmodule.exports = {\n  assetPrefix: isProd && 'https://cdn.example.com';\n}\n```\n\n## FAQ\n\n== TO BE DETERMINED == \n\n## Plugins\n- [css-plugin](https://github.com/airyrooms/maleo-plugins/tree/master/packages/css-plugin)\n- [redux-plugin](https://github.com/airyrooms/maleo-plugins/tree/master/packages/redux-plugin)\n- [typescript-plugin](https://github.com/airyrooms/maleo-plugins/tree/master/packages/typescript-plugin)\n- [more](https://github.com/airyrooms/maleo-plugins)\n\n\n## Contributing\n[Please follow these steps to contribute to Maleo.js](https://github.com/airyrooms/maleo.js/blob/canary/CONTRIBUTING.md)\n\n[Please follow these steps to contribute to Maleo.js' plugins](https://github.com/airyrooms/maleo-plugins#contributing-guidelines)\n\n\n## Contributors\n\nThis project exists thanks to all the people who contribute. [Contribute](CONTRIBUTING.md).\n\nMany thanks to our **[contributors](https://github.com/airyrooms/maleo.js/graphs/contributors)**!\n\n\n## License\n\nMIT","readmeFilename":"README.md","bugs":{"url":"https://github.com/airyrooms/maleo.js/issues"},"homepage":"https://github.com/airyrooms/maleo.js#readme","_id":"@airy/maleo@0.3.1-canary.2","_nodeVersion":"10.15.3","_npmVersion":"lerna/3.13.1/node@v10.15.3+x64 (linux)","dist":{"integrity":"sha512-+ZGSjoUhsKZ6aJnbkTeZbHYe/f3QyY8EhRcto09hYLB1gvgTiVxctJG92SVEp1qXDmBQou88gDMBy5wZOoQ54w==","shasum":"401bc12b5a5a6919af2ca0f73923087e40fdfaeb","tarball":"https://registry.npmjs.org/@airy/maleo/-/maleo-0.3.1-canary.2.tgz","fileCount":93,"unpackedSize":316782,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJc4iCvCRA9TVsSAnZWagAAJ7QP/152g8KeHnMAZokzsPmv\nia4DnDB3Zy9ESsiHl7MRNtjsLMF55mOgF4E2vusSBuxwdFRX1Bzva2jVQ07B\nZS7SdUwWseM8iw2R1jBcbfig9c0DL2l7pkmr8zfd9gJz0M56rSjIJtGMPAC/\nNPZKFeFbou8Yg0Nll0mH+7n4IFM50SeZPv/inLXReHbJ8lX+JysSZt+aAAc2\nXJVVkPWt9q0c8obWuaTAo27D+pFWkTQfQGomrARaZHAl4m/PF0lmx7tpuXsG\nGwb3nXN8B7QOls3rGEd9rGOyAYzVjdWKB3wvZMz+vCZp0VrQ/eJNfjLbP+SG\n62X7KhwvACqPa4nNpZbGYR2fCQahpnH2Zl+YW7wCZqQB/Vtr2Kb0bGvxIZn1\npdmvzpDQJ71R61lgBrHSwnapwaGU/FF8HrX7EhfM8a5eYC6rAcHSRzRZRUNC\nKweNb3U94HX2L/DtZec5KEleOwjrp6DsIFny0V15PLskWUwvCbYc+lbgCGKi\nVCK2VOlxN+lnOJXk4JqMlZnV1zIZFWzi+Xr9fymOqj3tnlytuNY4ga3K+s/Y\nuXcYJq8YRTXPZA3mCBaQ1Nl3OiorZc1oXJGwbh7ImCu6jE5IzW0sGsxIQAd8\nQtxYmYCO//ChA0AGvxoKtkZ9nCOzdbZN60LIPZA8oPnrc5eeUWI30bWxyHSZ\nDTGg\r\n=OX8T\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIGRLQLizmyg/mJVsaZ0pkWZXyyDt41tpD6NnEpa6VsuGAiEAiXDNsrgiRyneVtA6Ul9HN4xPFG5H+nSYYK+nR58Cz4s="}]},"maintainers":[{"name":"airyrooms-engineering","email":"engineering@airyrooms.com"}],"_npmUser":{"name":"airyrooms-engineering","email":"engineering@airyrooms.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/maleo_0.3.1-canary.2_1558323375135_0.9103478047640796"},"_hasShrinkwrap":false},"0.3.1-canary.3":{"name":"@airy/maleo","version":"0.3.1-canary.3","description":"Un-opinionated Universal Rendering Javascript Framework","repository":{"type":"git","url":"git+https://github.com/airyrooms/maleo.js.git","bugs":"https://github.com/airyrooms/maleo.js/issues"},"author":{"name":"Airy"},"license":"MIT","private":false,"sideEffects":false,"scripts":{"build":"rm -rf dist && gulp","watch":"gulp watch","prepublish":"yarn build","start":"node server.js"},"bin":{"maleo":"lib/bin/maleo.js"},"dependencies":{"@babel/core":"7.1.2","@babel/plugin-proposal-class-properties":"7.1.0","@babel/plugin-proposal-decorators":"7.1.2","@babel/plugin-proposal-object-rest-spread":"7.0.0","@babel/plugin-syntax-dynamic-import":"7.0.0","@babel/plugin-transform-runtime":"7.1.0","@babel/preset-env":"7.1.0","@babel/preset-react":"7.0.0","@babel/runtime":"7.1.2","@babel/runtime-corejs2":"7.2.0","babel-loader":"8.0.4","case-sensitive-paths-webpack-plugin":"2.1.2","compression":"1.7.4","es6-promise":"4.2.5","eventsource":"1.0.7","express":"4.16.4","figlet":"1.2.1","friendly-errors-webpack-plugin":"1.7.0","hard-source-webpack-plugin":"0.13.1","helmet":"3.14.0","hoist-non-react-statics":"^3.3.0","isomorphic-fetch":"2.2.1","loader-utils":"1.1.0","node-notifier":"5.2.1","nodemon":"1.18.4","react-dom":"16.8.4","react-helmet":"5.2.0","react-loadable":"5.5.0","react-router-config":"5.0.0","react-router-dom":"5.0.0","rimraf":"2.6.3","source-map-support":"0.5.9","terser-webpack-plugin":"1.1.0","webpack":"4.29.0","webpack-bundle-analyzer":"3.0.3","webpack-cli":"3.1.2","webpack-dev-middleware":"3.4.0","webpack-hot-middleware":"2.24.3","webpackbar":"2.6.3"},"peerDependencies":{"react":"^16.8.4"},"devDependencies":{"@babel/preset-typescript":"^7.1.0","@types/webpack-bundle-analyzer":"^2.13.0","babel-plugin-module-resolver":"^3.1.1","gulp":"github:gulpjs/gulp#4.0","gulp-babel":"^8.0.0","gulp-clean":"^0.4.0","gulp-sourcemaps":"^2.6.4","gulp-uglify":"^3.0.1","gulp-watch":"^5.0.1","react":"^16.8.4"},"gitHead":"50d61ab161b9364569b891b3f5158bf3f3941262","readme":"[![Build Status](https://travis-ci.org/airyrooms/maleo.js.svg?branch=master)](https://travis-ci.org/airyrooms/maleo.js)\n[![Coverage Status](https://coveralls.io/repos/github/airyrooms/maleo.js/badge.svg?branch=master)](https://coveralls.io/github/airyrooms/maleo.js?branch=master)\n[![npm version](https://badge.fury.io/js/%40airy%2Fmaleo.svg)](https://badge.fury.io/js/%40airy%2Fmaleo)\n[![Issues](http://img.shields.io/github/issues/airyrooms/maleo.js.svg)]( https://github.com/airyrooms/maleo.js/issues)\n[![MIT license](https://img.shields.io/badge/License-MIT-blue.svg)](https://github.com/airyrooms/maleo.js/blob/canary/LICENSE)\n[![Discord Chat](https://img.shields.io/discord/550498214789513218.svg)](https://discord.gg/9eArCQn)\n[![Conventional Commits](https://img.shields.io/badge/Conventional%20Commits-1.0.0-green.svg)](https://conventionalcommits.org)\n\n\n# Welcome to Maleo.js\n\nMaleo.js is an un-opinionated framework to enable Universal Rendering in JavaScript using React with no hassle.\n\nWe are here to solve the time consuming setups Universal Rendering Required.\n\n---\n\nReadme below is the documentation for the `canary` (prerelease) branch. To view the documentation for the latest stable Maleo.js version change branch to `master`\n\n---\n\n# Table of Contents\n- [Features](#features)\n- [Setup](#setup)\n- [Component Lifecycle](#component-lifecycle)\n- [Routing](#routing)\n- [Dynamic Import Component](#dynamic-import-component)\n  - [Tips](#tips)\n  - [Preloading](#preloading)\n- [Customizable Component](#customizable-component)\n  - [Custom Document](#custom-document)\n  - [Custom Wrap](#custom-wrap)\n- [Custom Configuration](#custom-configuration)\n  - [Customize Server](#customize-server)\n  - [Customize Webpack](#customize-webpack)\n  - [Customize Babel Config](#customize-babel-config)\n- [CDN Support](#cdn-support)\n- [Plugins](#plugins)\n- [FAQ](#faq)\n- [Contributing](#contributing)\n\n---\n\n## Features\n- Universal Rendering\n- Plugin based framework\n- Customizable\n\n## Setup\n\nInstall Maleo.js \n\n>*for now change `@airy/maleo` to `@airy/maleo@canary` until we publish the stable version of this package*\n\n\n**NPM**\n```bash\n$ npm install --save @airy/maleo react\n```\n**Yarn**\n```bash\n$ yarn add @airy/maleo react\n```\n\nAdd this script to your `package.json`\n```json\n{\n  \"scripts\": {\n    \"dev\": \"maleo dev\",\n    \"build\": \"export NODE_ENV=production && maleo build\",\n    \"start\": \"export NODE_ENV=production && maleo run\"\n  }\n}\n```\n\nCreate a page Root component\n```jsx\n// ./src/Root.jsx\nimport React from 'react';\n\n// Export default is required for registering page\nexport default class RootComponent extends React.Component {\n  render() {\n    return (\n      <h1>Hello World!</h1>\n    )\n  }\n}\n```\n\nAnd lastly, create a routing file on your project root directory called `routes.json` and register your page component\n```json\n[\n  {\n    \"path\": \"/\",\n    \"page\": \"./src/Root\"\n  }\n]\n```\n\nAfter that you can now run `$ npm run dev` and go to `http://localhost:3000`.\n\nYou should now see your app running on your browser.\n\nBy now you should see \n- Automatic transpilation and bundling (with webpack and babel)\n- ~~Hot code reloading~~ [#17](https://github.com/airyrooms/maleo.js/issues/17)\n- Server rendering\n\nTo see how simple this is, check out the sample app!\n\n## Component Lifecycle \n\nMaleo.js added a new component lifecycle hook called `getInitialProps`, this function is called during Server Side Rendering (SSR) and during route changes on Client Side Rendering (CSR).\n\nThis is useful especially for SEO purposes. \n\nExample for stateful component:\n```jsx\nimport React from 'react';\n\nexport default class extends React.Component {\n  static getInitialProps = async (ctx) => {\n    const { req } = ctx;\n\n    // check if getInitialProps is called on server or on client\n    const userAgent = req ? req.headers['user-agent'] : navigator.userAgent;\n\n    // the return value will be passed as props for this component\n    return { userAgent };\n  }\n\n  render() {\n    return (\n      <div>\n        Hello World {this.props.userAgent}\n      </div>\n    );\n  }\n}\n```\n\nExample for stateless component:\n```jsx\nconst Component = (props) => (\n  <div>\n    Hello World {props.userAgent}\n  </div>\n);\n\nComponent.getInitialprops = async (ctx) => {\n  const { req } = ctx;\n\n  const userAgent = req ? req.headers['user-agent'] : navigator.userAgent;\n\n  return { userAgent };\n};\n\nexport default Component;\n```\n\n`getInitialProps` receives a context object with the following properties:\n- `req` - HTTP request object (server only)\n- `res` - HTTP response object (server only)\n- `...wrapProps` - Spreaded properties from custom Wrap\n- `...appProps` - Spreaded properties from custom App\n\n## Routing\n\nRouting is declared in a centralized route config.\nRegister all the route config in `routes.json` file.\n\nIf you put the `routes.json` files on root directory, Maleo will automatically register your route. Otherwise put path to your routes on [Maleo config](#custom-configuration).\n\nRoutes file has to export default the route configuration.\nThe route object **expected to have distinct key** to indicate the route.\n\n<table>\n  <tr>\n    <td>Key</td>\n    <td>Type</td>\n    <td>Description</td>\n  </tr>\n  <tr>\n    <td><code>path</code></td>\n    <td><code>String!</code></td>\n    <td>Routes path</td>\n  </tr>\n  <tr>\n    <td><code>page</code></td>\n    <td><code>String!</code></td>\n    <td>Path to React Component for this route path</td>\n  </tr>\n  <tr>\n    <td><code>exact</code></td>\n    <td><code>Boolean?</code> [<code>false</code>]</td>\n    <td>To make url has to match exactly the path in order to render the component. Give <code>false</code> value if the route is a wrapper route component</td>\n  </tr>\n  <tr>\n    <td><code>routes</code></td>\n    <td><code>RouteObject?</code></td>\n    <td>Nested route</td>\n  </tr>\n</table>\n\n\nFor example:\n```json\n[\n  {\n    \"page\": \"./src/MainApp\",\n    \"routes\": [\n      {\n        \"path\": \"/\",\n        \"page\": \"./src/Search\",\n        \"exact\": true\n      },\n      {\n        \"path\": \"/search\",\n        \"page\": \"./src/Search\",\n        \"routes\": [\n          {\n            \"path\": \"/search/hello\",\n            \"page\": \"./src/Detail\",\n            \"exact\": true\n          }\n        ]\n      },\n      {\n        \"path\": \"/detail\",\n        \"page\": \"./src/Detail\",\n        \"exact\": true\n      }\n    ]\n  }\n]\n```\n\n## Dynamic Import Component\nMaleo.js supports TC39 [dynamic import proposal](https://github.com/tc39/proposal-dynamic-import) for JavaScript.\n\nYou can think dynamic import as another way to split your code into manageable chunks. You can use our `Dynamic` function which utilizes [react loadable](https://github.com/jamiebuilds/react-loadable)\n\nFor Example\n```js\n// DynamicComponent.js\nimport Dynamic from '@airy/maleo/dynamic';\n\nexport default Dynamic({\n  loader: () => import( /* webpackChunkName:\"DynamicComponent\" */ './component/DynamicLoad'),\n})\n```\n\n### **Preloading**\n\nFor optimization purposes, you can also preload a component even before the component got rendered.\n\nFor example, if you want to load component when a button get pressed, you can start preloading the component when the user hovers the mouse over the button.\n\nThe component created by `Dynamic` exposes a [static `preload` method](https://github.com/jamiebuilds/react-loadable#loadablecomponentpreload).\n\n```jsx\nimport React from 'react';\nimport Dynamic from '@airy/maleo/dynamic';\n\nconst DynamicBar = Dynamic({\n  loader: () => import('./Bar'),\n  loading: LoadingCompoinent\n});\n\nclass MyComponent extends React.Component {\n  state = { showBar: false };\n\n  onClick = () => {\n    this.setState({ showBar: true });\n  };\n\n  onMouseOver = () => DynamicBar.preload();\n\n  render() {\n    return (\n      <div>\n        <button\n          onClick={this.onClick}\n          onMouseOver={this.onMouseOver}>\n          Show Bar\n        </button>\n        { this.state.showBar && <DynamicBar /> }\n      </div>\n    )\n  }\n}\n```\n\n## Customizable Component\n\n### Custom Document\n\nHighly inspired by what [Next.js](https://github.com/zeit/next.js) has done on their awesome template customization.\n\nMaleo.js also enable customization on `Document` as document's markup. So you don't need to include tags like `<html>`, `<head>`, `<body>`, etc. \n\nTo override the default behavior, you'll need to create a component that extends the `Document` React class provided by Maleo.\n\n```jsx\n// document.jsx\nimport React from 'react';\nimport { Document, Header, Main, Scripts } from '@airy/maleo/document';\n\nexport default class extends Document {\n  render() {\n    return (\n      <html>\n        <Header>\n          <title>Maleo JS</title>\n          <meta charset=\"utf-8\" />\n          <meta name=\"description\" content=\"Maleo.js is awesome!\" />\n\n          <style>\n            {` body { background-color: #fff } `}\n          </style>\n        </Header>\n\n        <body>\n          <Main />\n\n          <Scripts />\n\n          <ReduxScript />\n        </body>\n      </html>\n    );\n  }\n}\n```\n\n### Custom Wrap\n\nMaleo.js uses the `Wrap` component to initialize pages. `Wrap` contains React Router's Component. You can add HoC here to wrap the application and control the page initialization. Which allows you to do amazing things like:\n- Persisting layour between page changes\n- Keeping state when navigating pages\n- Custom error handling using `componentDidCatch`\n- Inject additional data into pages (like Redux provider, etc)\n\nTo override the default behavior, you'll need to create a component that extends the `Wrap` React class provided by Maleo.\n\n```jsx\n// wrap.jsx\nimport React from 'react';\nimport { Wrap } from '@airy/maleo/wrap';\n\n// Redux plugin for Maleo.js\n// Hoc that creates store and add Redux Provider\nimport { withRedux } from '@airy/maleo-redux-plugin';\n\n// Custom Wrapper that will be rendered for the whole Application\nimport Content from './component/Content';\nimport NavigationBar from './component/NavigationBar';\n\nimport { createStore } from './store';\n\n@withRedux(createStore)\nexport default class extends Wrap {\n  static getInitialProps = (ctx) => {\n    const { store } = ctx\n    // you receive store from context\n    // you can access or do something with the store here\n    console.log('Initialized Redux Store', store);\n    return {}\n  }\n\n  render() {\n    const { Container, containerProps, App, appProps } = this.props\n\n    return (\n      <Container {...containerProps}>\n        <NavigationBar />\n        <Content>\n          <App {...appProps}/>\n        </Content>\n      </Container>\n    )\n  }\n}\n\n```\nIf you put `document.jsx` and `wrap.jsx` on root directory (the same level with `package.json`), then Maleo will automatically register your custom Document and Wrap. Otherwise, you can add the path to your custom Document and Wrap on [Maleo config](#custom-configuration)\n\n---\n***We are also working on adding default and customizable `Error` component page***\n\n---\n\n## Custom Configuration\n\nFor more advanced configuration of Maleo.js, like `webpack` config, registering `plugins`, path to your routes, custom Document and Wrap, and adding `path alias`, you can create a `maleo.config.js` in the root of your project directory. (same directory with `package.json`)\n\n```js\n// maleo.config.js\n\nmodule.exports = {\n  /* config options here */\n}\n```\n\nHere are the API's for the configuration:\n\n<table>\n  <tr>\n    <td>Key</td>\n    <td>Type</td>\n    <td>Description</td>\n  </tr>\n  <tr>\n    <td><code>buildDir</code></td>\n    <td><code>String?</code> [<code>.maleo</code>]</td>\n    <td>Directory to put Maleo.js' build assets</td>\n  </tr>\n  <tr>\n    <td><code>cache</code></td>\n    <td><code>Boolean?</code> [<code>true</code>]</td>\n    <td>Enable webpack build caching</td>\n  </tr>\n  <tr>\n    <td><code>isDev</code></td>\n    <td><code>Boolean?</code> [<code>process.env.NODE_ENV === 'development'</code>]</td>\n    <td>Enable development build configuration</td>\n  </tr>\n  <tr>\n    <td><code>sourceMaps</code></td>\n    <td><code>Boolean?</code> [<code>true</code>]</td>\n    <td>Enable webpack to generate source maps</td>\n  </tr>\n  <tr>\n    <td><code>alias</code></td>\n    <td><code>Object?</code></td>\n    <td>A key value pair for aliasing path directory\n      <br>\n      <code>\n      { 'component': './src/component' }\n      </code>\n    </td>\n  </tr>\n  <tr>\n    <td><code>publicPath</code></td>\n    <td><code>String?</code> [<code>/_assets/</code>]</td>\n    <td>To customize webpack's <code>publicPath</code> \n    <br>Comes in handy if using CDN to put built assets</td>\n  </tr>\n  <tr>\n    <td><code>analyzeBundle</code></td>\n    <td><code>Boolean?</code> [<code>false</code>]</td>\n    <td>To enable webpack's bundle analyzer, for analyzing bundle sizes during bundle debugging should Maleo.js' build process got slow</td>\n  </tr>\n  <tr>\n    <td><code>webpack</code></td>\n    <td><code>Function?</code></td>\n    <td>To customize webpack configuration, more details <a href=\"#customize-webpack\">here</a></td>\n  </tr>\n  <tr>\n    <td><code>routes</code></td>\n    <td><code>string?</code> [<code>rootDir/routes.jsx</code>]</td>\n    <td>Path to your routes file</td>\n  </tr>\n  <tr>\n    <td><code>customDocument</code></td>\n    <td><code>string?</code> [<code>rootDir/document.jsx</code>]</td>\n    <td>Path to your custom document file</td>\n  </tr>\n  <tr>\n    <td><code>customWrap</code></td>\n    <td><code>string?</code> [<code>rootDir/wrap.jsx</code>]</td>\n    <td>Path to your custom wrap file</td>\n  </tr>\n  <tr>\n    <td><code>customApp</code></td>\n    <td><code>string?</code> [<code>rootDir/app.jsx</code>]</td>\n    <td>Path to your custom app file</td>\n  </tr>\n</table>\n\n#### Customize Server\n\nCreate a `server.js` file on root directory where your `package.json` lives.\nHere you can customize Maleo's server.\n```js\nimport { Server } from '@airy/maleo/server';\nimport path from 'path';\n\nimport routeConfig from './routes';\n\nconst PORT = process.env.PORT || 8080;\n\nconst maleoServer = Server.init({\n  port: PORT,\n});\n\nmaleoServer.run(() => {\n  console.log('Server running on port :', PORT);\n});\n```\n\nHere are the API's for the configuration:\n\n<table>\n  <tr>\n    <td>Key</td>\n    <td>Type</td>\n    <td>Description</td>\n  </tr>\n  <tr>\n    <td><code>port</code></td>\n    <td><code>Number?</code> [<code>3000</code>]</td>\n    <td>Port to run Maleo server</td>\n  </tr>\n  <tr>\n    <td><code>assetDir</code></td>\n    <td><code>String?</code> [<code>'<root>/.maleo/client'</code>]</td>\n    <td>Directory for all client related assets</td>\n  </tr>\n</table>\n\n#### Customize Webpack\n\nYou are able to extend Maleo.js' default webpack configuration by defining a function on `maleo.config.js`\n\n```js\n// maleo.config.js\n\nmodule.exports = {\n  webpack(config, context, next) {\n    // Perform customizations to webpack config\n    // Important: This call is required in order to let Maleo pass the config to webpack\n    return next(); \n  },\n};\n```\n\nWebpack function will receive three arguments:\n\n<table>\n  <tr>\n    <td>Argument</td>\n    <td>Details</td>\n  <tr>\n  <tr>\n    <td><code>config</code></td>\n    <td>This contains webpack configuration object that you can manipulate</td>\n  <tr>\n  <tr>\n    <td><code>context</code></td>\n    <td>This contains some keys that are useful for the build context\n      <br>\n      <table>\n        <tr>\n          <td><code>isDev</code></td>\n          <td><code>Boolean</code></td>\n          <td>Check if current build is for development</td>\n        </tr>\n        <tr>\n          <td><code>publicPath</code></td>\n          <td><code>String</code></td>\n          <td>Public Path of user defined or default's value</td>\n        </tr>\n        <tr>\n          <td><code>analyzeBundle</code></td>\n          <td><code>Boolean</code></td>\n          <td>Check if analyze bundle is enabled</td>\n        </tr>\n        <tr>\n          <td><code>buildDirectory</code></td>\n          <td><code>String</code></td>\n          <td>Build Directory of user defined or default's value</td>\n        </tr>\n        <tr>\n          <td><code>name</code></td>\n          <td><code>String</code></td>\n          <td>Build name <code>'server' || 'client'</code></td>\n        </tr>\n      </table>\n    </td>\n  <tr>\n  <tr>\n    <td><code>next</code></td>\n    <td>A callback required to be called to pass the custom configuration</td>\n  <tr>\n</table>\n\nExample of adding `ts-loader` through `maleo.config.js`:\n\n```js\n// maleo.config.js\n\n// Partly taken and modified from @airy/maleo-ts-plugin source\n// for simplicity purposes\n\nmodule.exports = {\n  webpack(config, context, next) {\n    const { isDev } = context\n\n    config.module.rules.push({\n      test: /\\.tsx?/,\n      exclude: /node_modules/,\n      use: [\n        require.resolve('@airy/maleo/lib/build/loaders/maleo-babel-loader'),\n        {\n          loader: 'ts-loader',\n          options: {\n            transpileOnly: true,\n          },\n        },\n      ],\n\n      if (isDev) {\n        config.plugins.push(new ForkTSCheckerWebpackPlugin());\n      }\n\n      return next();\n    })\n  },\n};\n```\n\n#### Customize Babel Config\n\nMaleo.js also let you have your own babel config. Just simply add `.babelrc` file at the root directory of your app.\n\nYou can include Maleo.js' babel preset in order to have latest JavaScript preset.\n\nHere's an example of `.babelrc` file:\n```json\n{\n  \"presets\": [\"@airy/maleo/babel\"],\n  \"plugins\": []\n}\n```\n\nThe `@airy/maleo/babel` preset includes everything you need to get your development started. The preset includes:\n- `@babel/preset-env`\n- `@babel/preset-react`\n- `@babel/plugin-proposal-class-properties`\n- `@babel/plugin-proposal-decorators`\n- `@babel/plugin-proposal-object-rest-spread`\n- `@babel/plugin-transform-runtime`\n- `react-loadable/babel`\n\n## CDN Support\n\nIf you are using a CDN, you can set up the `publicPath` setting and configure your CDN's origin to resolve to the domain that Maleo.js is hosted on.\n\n```js\n// maleo.config.js\n\nconst isProd = process.env.NODE_ENV === 'production';\n\nmodule.exports = {\n  assetPrefix: isProd && 'https://cdn.example.com';\n}\n```\n\n## FAQ\n\n== TO BE DETERMINED == \n\n## Plugins\n- [css-plugin](https://github.com/airyrooms/maleo-plugins/tree/master/packages/css-plugin)\n- [redux-plugin](https://github.com/airyrooms/maleo-plugins/tree/master/packages/redux-plugin)\n- [typescript-plugin](https://github.com/airyrooms/maleo-plugins/tree/master/packages/typescript-plugin)\n- [more](https://github.com/airyrooms/maleo-plugins)\n\n\n## Contributing\n[Please follow these steps to contribute to Maleo.js](https://github.com/airyrooms/maleo.js/blob/canary/CONTRIBUTING.md)\n\n[Please follow these steps to contribute to Maleo.js' plugins](https://github.com/airyrooms/maleo-plugins#contributing-guidelines)\n\n\n## Contributors\n\nThis project exists thanks to all the people who contribute. [Contribute](CONTRIBUTING.md).\n\nMany thanks to our **[contributors](https://github.com/airyrooms/maleo.js/graphs/contributors)**!\n\n\n## License\n\nMIT","readmeFilename":"README.md","bugs":{"url":"https://github.com/airyrooms/maleo.js/issues"},"homepage":"https://github.com/airyrooms/maleo.js#readme","_id":"@airy/maleo@0.3.1-canary.3","_nodeVersion":"11.15.0","_npmVersion":"lerna/3.13.1/node@v11.15.0+x64 (linux)","dist":{"integrity":"sha512-CYF/MXmko8pQ4TyFNbtB84vRlGPUeA5flvtp4pw9C9SPOVikw7K7KAHZVQZZm45yfSQTtkVW4P83ajirV0MdTA==","shasum":"a168d29c1c63483fb47e4259faa615322e82dc54","tarball":"https://registry.npmjs.org/@airy/maleo/-/maleo-0.3.1-canary.3.tgz","fileCount":95,"unpackedSize":326148,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJc44BZCRA9TVsSAnZWagAAEa4P/RknlId4DlgAFY4dbscF\nD6SkDj87dCzhFfhvKmRVWp+tQROP5CnmpZ1/T20Nfuq/bBb7balGCNvdrF2B\nLF2g7nivhHscZWdhtGNFMutUjyCq69Bo8hBxEIquQyhsXnu3GYD7Ne0ru/l6\nZQZvBPwABarwWenKx//ebHJy3oUsIZBiWaMQn09YQbwvEs1cR/AWd9iRR3tX\n5Xv0W+oiF2DAnFXNJGOtL+7HlouECiEJ6JYsI5LiIg756NiVY544J4LHmwY4\ncpCr+AGyzZ1UnoS5R4oHXnkxaApNYyj4CSUczxWQsow5l4ay02M3iCr+B3/X\nth2rdSj4bQJkj/U01/AtrAbLOjiaIAIjAfBmFx+X6z8NK6nkC4iUCgMhNWG+\nEOF3R5bWDNYFKW+i9JBMD9vtV00V/R7lJ6lcCShOvD5RhHNCJJ3GWQa6W7f+\n9hl7FL7TnZfAdikxrN8FK77QMGZMRIuNQu8Qa32axW1XM5ggGzs7p7jFTuvN\no5fG9a4fobmFQ0Vq8WCpksZ5CGsKkWJqS5AIGzE4gD8VmUE8ClyUo+ng8swY\n5IQU0XJkTr6s2jxayMBT5SN9iOTZdvPxefHrd3bFA6vPFq1aZtV50ixUD818\nRUYomQf56v/ZM9awUEe/B1u9tmODxzGT/QepxGRrsNauF51N1fT5T6g7gYz4\n2NJv\r\n=sDHn\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIB+NxCPyF6PUosc8TWrX4sq2mQvVHRefYa1PIdaCKLy3AiApraZ8ppLio3CM2hVGXLJNHINJQRktfKYon6amVX76Tg=="}]},"maintainers":[{"name":"airyrooms-engineering","email":"engineering@airyrooms.com"}],"_npmUser":{"name":"airyrooms-engineering","email":"engineering@airyrooms.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/maleo_0.3.1-canary.3_1558413400600_0.7690416588916962"},"_hasShrinkwrap":false},"0.3.1-canary.4":{"name":"@airy/maleo","version":"0.3.1-canary.4","description":"Un-opinionated Universal Rendering Javascript Framework","repository":{"type":"git","url":"git+https://github.com/airyrooms/maleo.js.git","bugs":"https://github.com/airyrooms/maleo.js/issues"},"author":{"name":"Airy"},"license":"MIT","private":false,"sideEffects":false,"scripts":{"build":"rm -rf dist && gulp","watch":"gulp watch","prepublish":"yarn build","start":"node server.js"},"bin":{"maleo":"lib/bin/maleo.js"},"dependencies":{"@babel/core":"7.1.2","@babel/plugin-proposal-class-properties":"7.1.0","@babel/plugin-proposal-decorators":"7.1.2","@babel/plugin-proposal-object-rest-spread":"7.0.0","@babel/plugin-syntax-dynamic-import":"7.0.0","@babel/plugin-transform-runtime":"7.1.0","@babel/preset-env":"7.1.0","@babel/preset-react":"7.0.0","@babel/runtime":"7.1.2","@babel/runtime-corejs2":"7.2.0","babel-loader":"8.0.4","case-sensitive-paths-webpack-plugin":"2.1.2","compression":"1.7.4","es6-promise":"4.2.5","eventsource":"1.0.7","express":"4.16.4","figlet":"1.2.1","friendly-errors-webpack-plugin":"1.7.0","hard-source-webpack-plugin":"0.13.1","helmet":"3.14.0","hoist-non-react-statics":"^3.3.0","isomorphic-fetch":"2.2.1","loader-utils":"1.1.0","node-notifier":"5.2.1","nodemon":"1.18.4","react-dom":"16.8.4","react-helmet":"5.2.0","react-loadable":"5.5.0","react-router-config":"5.0.0","react-router-dom":"5.0.0","rimraf":"2.6.3","source-map-support":"0.5.9","terser-webpack-plugin":"1.1.0","webpack":"4.29.0","webpack-bundle-analyzer":"3.0.3","webpack-cli":"3.1.2","webpack-dev-middleware":"3.4.0","webpack-hot-middleware":"2.24.3","webpackbar":"2.6.3"},"peerDependencies":{"react":"^16.8.4"},"devDependencies":{"@babel/preset-typescript":"^7.1.0","@types/webpack-bundle-analyzer":"^2.13.0","babel-plugin-module-resolver":"^3.1.1","gulp":"github:gulpjs/gulp#4.0","gulp-babel":"^8.0.0","gulp-clean":"^0.4.0","gulp-sourcemaps":"^2.6.4","gulp-uglify":"^3.0.1","gulp-watch":"^5.0.1","react":"^16.8.4"},"gitHead":"ea542702f3c9de045ace508b703876f238223682","readme":"[![Build Status](https://travis-ci.org/airyrooms/maleo.js.svg?branch=master)](https://travis-ci.org/airyrooms/maleo.js)\n[![Coverage Status](https://coveralls.io/repos/github/airyrooms/maleo.js/badge.svg?branch=master)](https://coveralls.io/github/airyrooms/maleo.js?branch=master)\n[![npm version](https://badge.fury.io/js/%40airy%2Fmaleo.svg)](https://badge.fury.io/js/%40airy%2Fmaleo)\n[![Issues](http://img.shields.io/github/issues/airyrooms/maleo.js.svg)]( https://github.com/airyrooms/maleo.js/issues)\n[![MIT license](https://img.shields.io/badge/License-MIT-blue.svg)](https://github.com/airyrooms/maleo.js/blob/canary/LICENSE)\n[![Discord Chat](https://img.shields.io/discord/550498214789513218.svg)](https://discord.gg/9eArCQn)\n[![Conventional Commits](https://img.shields.io/badge/Conventional%20Commits-1.0.0-green.svg)](https://conventionalcommits.org)\n\n\n# Welcome to Maleo.js\n\nMaleo.js is an un-opinionated framework to enable Universal Rendering in JavaScript using React with no hassle.\n\nWe are here to solve the time consuming setups Universal Rendering Required.\n\n---\n\nReadme below is the documentation for the `canary` (prerelease) branch. To view the documentation for the latest stable Maleo.js version change branch to `master`\n\n---\n\n# Table of Contents\n- [Features](#features)\n- [Setup](#setup)\n- [Component Lifecycle](#component-lifecycle)\n- [Routing](#routing)\n- [Dynamic Import Component](#dynamic-import-component)\n  - [Tips](#tips)\n  - [Preloading](#preloading)\n- [Customizable Component](#customizable-component)\n  - [Custom Document](#custom-document)\n  - [Custom Wrap](#custom-wrap)\n- [Custom Configuration](#custom-configuration)\n  - [Customize Server](#customize-server)\n  - [Customize Webpack](#customize-webpack)\n  - [Customize Babel Config](#customize-babel-config)\n- [CDN Support](#cdn-support)\n- [Plugins](#plugins)\n- [FAQ](#faq)\n- [Contributing](#contributing)\n\n---\n\n## Features\n- Universal Rendering\n- Plugin based framework\n- Customizable\n\n## Setup\n\nInstall Maleo.js \n\n>*for now change `@airy/maleo` to `@airy/maleo@canary` until we publish the stable version of this package*\n\n\n**NPM**\n```bash\n$ npm install --save @airy/maleo react\n```\n**Yarn**\n```bash\n$ yarn add @airy/maleo react\n```\n\nAdd this script to your `package.json`\n```json\n{\n  \"scripts\": {\n    \"dev\": \"maleo dev\",\n    \"build\": \"export NODE_ENV=production && maleo build\",\n    \"start\": \"export NODE_ENV=production && maleo run\"\n  }\n}\n```\n\nCreate a page Root component\n```jsx\n// ./src/Root.jsx\nimport React from 'react';\n\n// Export default is required for registering page\nexport default class RootComponent extends React.Component {\n  render() {\n    return (\n      <h1>Hello World!</h1>\n    )\n  }\n}\n```\n\nAnd lastly, create a routing file on your project root directory called `routes.json` and register your page component\n```json\n[\n  {\n    \"path\": \"/\",\n    \"page\": \"./src/Root\"\n  }\n]\n```\n\nAfter that you can now run `$ npm run dev` and go to `http://localhost:3000`.\n\nYou should now see your app running on your browser.\n\nBy now you should see \n- Automatic transpilation and bundling (with webpack and babel)\n- ~~Hot code reloading~~ [#17](https://github.com/airyrooms/maleo.js/issues/17)\n- Server rendering\n\nTo see how simple this is, check out the sample app!\n\n## Component Lifecycle \n\nMaleo.js added a new component lifecycle hook called `getInitialProps`, this function is called during Server Side Rendering (SSR) and during route changes on Client Side Rendering (CSR).\n\nThis is useful especially for SEO purposes. \n\nExample for stateful component:\n```jsx\nimport React from 'react';\n\nexport default class extends React.Component {\n  static getInitialProps = async (ctx) => {\n    const { req } = ctx;\n\n    // check if getInitialProps is called on server or on client\n    const userAgent = req ? req.headers['user-agent'] : navigator.userAgent;\n\n    // the return value will be passed as props for this component\n    return { userAgent };\n  }\n\n  render() {\n    return (\n      <div>\n        Hello World {this.props.userAgent}\n      </div>\n    );\n  }\n}\n```\n\nExample for stateless component:\n```jsx\nconst Component = (props) => (\n  <div>\n    Hello World {props.userAgent}\n  </div>\n);\n\nComponent.getInitialprops = async (ctx) => {\n  const { req } = ctx;\n\n  const userAgent = req ? req.headers['user-agent'] : navigator.userAgent;\n\n  return { userAgent };\n};\n\nexport default Component;\n```\n\n`getInitialProps` receives a context object with the following properties:\n- `req` - HTTP request object (server only)\n- `res` - HTTP response object (server only)\n- `...wrapProps` - Spreaded properties from custom Wrap\n- `...appProps` - Spreaded properties from custom App\n\n## Routing\n\nRouting is declared in a centralized route config.\nRegister all the route config in `routes.json` file.\n\nIf you put the `routes.json` files on root directory, Maleo will automatically register your route. Otherwise put path to your routes on [Maleo config](#custom-configuration).\n\nRoutes file has to export default the route configuration.\nThe route object **expected to have distinct key** to indicate the route.\n\n<table>\n  <tr>\n    <td>Key</td>\n    <td>Type</td>\n    <td>Description</td>\n  </tr>\n  <tr>\n    <td><code>path</code></td>\n    <td><code>String!</code></td>\n    <td>Routes path</td>\n  </tr>\n  <tr>\n    <td><code>page</code></td>\n    <td><code>String!</code></td>\n    <td>Path to React Component for this route path</td>\n  </tr>\n  <tr>\n    <td><code>exact</code></td>\n    <td><code>Boolean?</code> [<code>false</code>]</td>\n    <td>To make url has to match exactly the path in order to render the component. Give <code>false</code> value if the route is a wrapper route component</td>\n  </tr>\n  <tr>\n    <td><code>routes</code></td>\n    <td><code>RouteObject?</code></td>\n    <td>Nested route</td>\n  </tr>\n</table>\n\n\nFor example:\n```json\n[\n  {\n    \"page\": \"./src/MainApp\",\n    \"routes\": [\n      {\n        \"path\": \"/\",\n        \"page\": \"./src/Search\",\n        \"exact\": true\n      },\n      {\n        \"path\": \"/search\",\n        \"page\": \"./src/Search\",\n        \"routes\": [\n          {\n            \"path\": \"/search/hello\",\n            \"page\": \"./src/Detail\",\n            \"exact\": true\n          }\n        ]\n      },\n      {\n        \"path\": \"/detail\",\n        \"page\": \"./src/Detail\",\n        \"exact\": true\n      }\n    ]\n  }\n]\n```\n\n## Dynamic Import Component\nMaleo.js supports TC39 [dynamic import proposal](https://github.com/tc39/proposal-dynamic-import) for JavaScript.\n\nYou can think dynamic import as another way to split your code into manageable chunks. You can use our `Dynamic` function which utilizes [react loadable](https://github.com/jamiebuilds/react-loadable)\n\nFor Example\n```js\n// DynamicComponent.js\nimport Dynamic from '@airy/maleo/dynamic';\n\nexport default Dynamic({\n  loader: () => import( /* webpackChunkName:\"DynamicComponent\" */ './component/DynamicLoad'),\n})\n```\n\n### **Preloading**\n\nFor optimization purposes, you can also preload a component even before the component got rendered.\n\nFor example, if you want to load component when a button get pressed, you can start preloading the component when the user hovers the mouse over the button.\n\nThe component created by `Dynamic` exposes a [static `preload` method](https://github.com/jamiebuilds/react-loadable#loadablecomponentpreload).\n\n```jsx\nimport React from 'react';\nimport Dynamic from '@airy/maleo/dynamic';\n\nconst DynamicBar = Dynamic({\n  loader: () => import('./Bar'),\n  loading: LoadingCompoinent\n});\n\nclass MyComponent extends React.Component {\n  state = { showBar: false };\n\n  onClick = () => {\n    this.setState({ showBar: true });\n  };\n\n  onMouseOver = () => DynamicBar.preload();\n\n  render() {\n    return (\n      <div>\n        <button\n          onClick={this.onClick}\n          onMouseOver={this.onMouseOver}>\n          Show Bar\n        </button>\n        { this.state.showBar && <DynamicBar /> }\n      </div>\n    )\n  }\n}\n```\n\n## Customizable Component\n\n### Custom Document\n\nHighly inspired by what [Next.js](https://github.com/zeit/next.js) has done on their awesome template customization.\n\nMaleo.js also enable customization on `Document` as document's markup. So you don't need to include tags like `<html>`, `<head>`, `<body>`, etc. \n\nTo override the default behavior, you'll need to create a component that extends the `Document` React class provided by Maleo.\n\n```jsx\n// document.jsx\nimport React from 'react';\nimport { Document, Header, Main, Scripts } from '@airy/maleo/document';\n\nexport default class extends Document {\n  render() {\n    return (\n      <html>\n        <Header>\n          <title>Maleo JS</title>\n          <meta charset=\"utf-8\" />\n          <meta name=\"description\" content=\"Maleo.js is awesome!\" />\n\n          <style>\n            {` body { background-color: #fff } `}\n          </style>\n        </Header>\n\n        <body>\n          <Main />\n\n          <Scripts />\n\n          <ReduxScript />\n        </body>\n      </html>\n    );\n  }\n}\n```\n\n### Custom Wrap\n\nMaleo.js uses the `Wrap` component to initialize pages. `Wrap` contains React Router's Component. You can add HoC here to wrap the application and control the page initialization. Which allows you to do amazing things like:\n- Persisting layour between page changes\n- Keeping state when navigating pages\n- Custom error handling using `componentDidCatch`\n- Inject additional data into pages (like Redux provider, etc)\n\nTo override the default behavior, you'll need to create a component that extends the `Wrap` React class provided by Maleo.\n\n```jsx\n// wrap.jsx\nimport React from 'react';\nimport { Wrap } from '@airy/maleo/wrap';\n\n// Redux plugin for Maleo.js\n// Hoc that creates store and add Redux Provider\nimport { withRedux } from '@airy/maleo-redux-plugin';\n\n// Custom Wrapper that will be rendered for the whole Application\nimport Content from './component/Content';\nimport NavigationBar from './component/NavigationBar';\n\nimport { createStore } from './store';\n\n@withRedux(createStore)\nexport default class extends Wrap {\n  static getInitialProps = (ctx) => {\n    const { store } = ctx\n    // you receive store from context\n    // you can access or do something with the store here\n    console.log('Initialized Redux Store', store);\n    return {}\n  }\n\n  render() {\n    const { Container, containerProps, App, appProps } = this.props\n\n    return (\n      <Container {...containerProps}>\n        <NavigationBar />\n        <Content>\n          <App {...appProps}/>\n        </Content>\n      </Container>\n    )\n  }\n}\n\n```\nIf you put `document.jsx` and `wrap.jsx` on root directory (the same level with `package.json`), then Maleo will automatically register your custom Document and Wrap. Otherwise, you can add the path to your custom Document and Wrap on [Maleo config](#custom-configuration)\n\n---\n***We are also working on adding default and customizable `Error` component page***\n\n---\n\n## Custom Configuration\n\nFor more advanced configuration of Maleo.js, like `webpack` config, registering `plugins`, path to your routes, custom Document and Wrap, and adding `path alias`, you can create a `maleo.config.js` in the root of your project directory. (same directory with `package.json`)\n\n```js\n// maleo.config.js\n\nmodule.exports = {\n  /* config options here */\n}\n```\n\nHere are the API's for the configuration:\n\n<table>\n  <tr>\n    <td>Key</td>\n    <td>Type</td>\n    <td>Description</td>\n  </tr>\n  <tr>\n    <td><code>buildDir</code></td>\n    <td><code>String?</code> [<code>.maleo</code>]</td>\n    <td>Directory to put Maleo.js' build assets</td>\n  </tr>\n  <tr>\n    <td><code>cache</code></td>\n    <td><code>Boolean?</code> [<code>true</code>]</td>\n    <td>Enable webpack build caching</td>\n  </tr>\n  <tr>\n    <td><code>isDev</code></td>\n    <td><code>Boolean?</code> [<code>process.env.NODE_ENV === 'development'</code>]</td>\n    <td>Enable development build configuration</td>\n  </tr>\n  <tr>\n    <td><code>sourceMaps</code></td>\n    <td><code>Boolean?</code> [<code>true</code>]</td>\n    <td>Enable webpack to generate source maps</td>\n  </tr>\n  <tr>\n    <td><code>alias</code></td>\n    <td><code>Object?</code></td>\n    <td>A key value pair for aliasing path directory\n      <br>\n      <code>\n      { 'component': './src/component' }\n      </code>\n    </td>\n  </tr>\n  <tr>\n    <td><code>publicPath</code></td>\n    <td><code>String?</code> [<code>/_assets/</code>]</td>\n    <td>To customize webpack's <code>publicPath</code> \n    <br>Comes in handy if using CDN to put built assets</td>\n  </tr>\n  <tr>\n    <td><code>analyzeBundle</code></td>\n    <td><code>Boolean?</code> [<code>false</code>]</td>\n    <td>To enable webpack's bundle analyzer, for analyzing bundle sizes during bundle debugging should Maleo.js' build process got slow</td>\n  </tr>\n  <tr>\n    <td><code>webpack</code></td>\n    <td><code>Function?</code></td>\n    <td>To customize webpack configuration, more details <a href=\"#customize-webpack\">here</a></td>\n  </tr>\n  <tr>\n    <td><code>routes</code></td>\n    <td><code>string?</code> [<code>rootDir/routes.jsx</code>]</td>\n    <td>Path to your routes file</td>\n  </tr>\n  <tr>\n    <td><code>customDocument</code></td>\n    <td><code>string?</code> [<code>rootDir/document.jsx</code>]</td>\n    <td>Path to your custom document file</td>\n  </tr>\n  <tr>\n    <td><code>customWrap</code></td>\n    <td><code>string?</code> [<code>rootDir/wrap.jsx</code>]</td>\n    <td>Path to your custom wrap file</td>\n  </tr>\n  <tr>\n    <td><code>customApp</code></td>\n    <td><code>string?</code> [<code>rootDir/app.jsx</code>]</td>\n    <td>Path to your custom app file</td>\n  </tr>\n</table>\n\n#### Customize Server\n\nCreate a `server.js` file on root directory where your `package.json` lives.\nHere you can customize Maleo's server.\n```js\nimport { Server } from '@airy/maleo/server';\nimport path from 'path';\n\nimport routeConfig from './routes';\n\nconst PORT = process.env.PORT || 8080;\n\nconst maleoServer = Server.init({\n  port: PORT,\n});\n\nmaleoServer.run(() => {\n  console.log('Server running on port :', PORT);\n});\n```\n\nHere are the API's for the configuration:\n\n<table>\n  <tr>\n    <td>Key</td>\n    <td>Type</td>\n    <td>Description</td>\n  </tr>\n  <tr>\n    <td><code>port</code></td>\n    <td><code>Number?</code> [<code>3000</code>]</td>\n    <td>Port to run Maleo server</td>\n  </tr>\n  <tr>\n    <td><code>assetDir</code></td>\n    <td><code>String?</code> [<code>'<root>/.maleo/client'</code>]</td>\n    <td>Directory for all client related assets</td>\n  </tr>\n</table>\n\n#### Customize Webpack\n\nYou are able to extend Maleo.js' default webpack configuration by defining a function on `maleo.config.js`\n\n```js\n// maleo.config.js\n\nmodule.exports = {\n  webpack(config, context, next) {\n    // Perform customizations to webpack config\n    // Important: This call is required in order to let Maleo pass the config to webpack\n    return next(); \n  },\n};\n```\n\nWebpack function will receive three arguments:\n\n<table>\n  <tr>\n    <td>Argument</td>\n    <td>Details</td>\n  <tr>\n  <tr>\n    <td><code>config</code></td>\n    <td>This contains webpack configuration object that you can manipulate</td>\n  <tr>\n  <tr>\n    <td><code>context</code></td>\n    <td>This contains some keys that are useful for the build context\n      <br>\n      <table>\n        <tr>\n          <td><code>isDev</code></td>\n          <td><code>Boolean</code></td>\n          <td>Check if current build is for development</td>\n        </tr>\n        <tr>\n          <td><code>publicPath</code></td>\n          <td><code>String</code></td>\n          <td>Public Path of user defined or default's value</td>\n        </tr>\n        <tr>\n          <td><code>analyzeBundle</code></td>\n          <td><code>Boolean</code></td>\n          <td>Check if analyze bundle is enabled</td>\n        </tr>\n        <tr>\n          <td><code>buildDirectory</code></td>\n          <td><code>String</code></td>\n          <td>Build Directory of user defined or default's value</td>\n        </tr>\n        <tr>\n          <td><code>name</code></td>\n          <td><code>String</code></td>\n          <td>Build name <code>'server' || 'client'</code></td>\n        </tr>\n      </table>\n    </td>\n  <tr>\n  <tr>\n    <td><code>next</code></td>\n    <td>A callback required to be called to pass the custom configuration</td>\n  <tr>\n</table>\n\nExample of adding `ts-loader` through `maleo.config.js`:\n\n```js\n// maleo.config.js\n\n// Partly taken and modified from @airy/maleo-ts-plugin source\n// for simplicity purposes\n\nmodule.exports = {\n  webpack(config, context, next) {\n    const { isDev } = context\n\n    config.module.rules.push({\n      test: /\\.tsx?/,\n      exclude: /node_modules/,\n      use: [\n        require.resolve('@airy/maleo/lib/build/loaders/maleo-babel-loader'),\n        {\n          loader: 'ts-loader',\n          options: {\n            transpileOnly: true,\n          },\n        },\n      ],\n\n      if (isDev) {\n        config.plugins.push(new ForkTSCheckerWebpackPlugin());\n      }\n\n      return next();\n    })\n  },\n};\n```\n\n#### Customize Babel Config\n\nMaleo.js also let you have your own babel config. Just simply add `.babelrc` file at the root directory of your app.\n\nYou can include Maleo.js' babel preset in order to have latest JavaScript preset.\n\nHere's an example of `.babelrc` file:\n```json\n{\n  \"presets\": [\"@airy/maleo/babel\"],\n  \"plugins\": []\n}\n```\n\nThe `@airy/maleo/babel` preset includes everything you need to get your development started. The preset includes:\n- `@babel/preset-env`\n- `@babel/preset-react`\n- `@babel/plugin-proposal-class-properties`\n- `@babel/plugin-proposal-decorators`\n- `@babel/plugin-proposal-object-rest-spread`\n- `@babel/plugin-transform-runtime`\n- `react-loadable/babel`\n\n## CDN Support\n\nIf you are using a CDN, you can set up the `publicPath` setting and configure your CDN's origin to resolve to the domain that Maleo.js is hosted on.\n\n```js\n// maleo.config.js\n\nconst isProd = process.env.NODE_ENV === 'production';\n\nmodule.exports = {\n  assetPrefix: isProd && 'https://cdn.example.com';\n}\n```\n\n## FAQ\n\n== TO BE DETERMINED == \n\n## Plugins\n- [css-plugin](https://github.com/airyrooms/maleo-plugins/tree/master/packages/css-plugin)\n- [redux-plugin](https://github.com/airyrooms/maleo-plugins/tree/master/packages/redux-plugin)\n- [typescript-plugin](https://github.com/airyrooms/maleo-plugins/tree/master/packages/typescript-plugin)\n- [more](https://github.com/airyrooms/maleo-plugins)\n\n\n## Contributing\n[Please follow these steps to contribute to Maleo.js](https://github.com/airyrooms/maleo.js/blob/canary/CONTRIBUTING.md)\n\n[Please follow these steps to contribute to Maleo.js' plugins](https://github.com/airyrooms/maleo-plugins#contributing-guidelines)\n\n\n## Contributors\n\nThis project exists thanks to all the people who contribute. [Contribute](CONTRIBUTING.md).\n\nMany thanks to our **[contributors](https://github.com/airyrooms/maleo.js/graphs/contributors)**!\n\n\n## License\n\nMIT","readmeFilename":"README.md","bugs":{"url":"https://github.com/airyrooms/maleo.js/issues"},"homepage":"https://github.com/airyrooms/maleo.js#readme","_id":"@airy/maleo@0.3.1-canary.4","_nodeVersion":"11.15.0","_npmVersion":"lerna/3.13.1/node@v11.15.0+x64 (linux)","dist":{"integrity":"sha512-APJyKjdWE/FQOpD+gySVBHZ7nuRZUcN1Fxpg6V30OjXnp7yvlJ/NeiAqNr+yyMBUbjwbJDFzj/g3+83GpUuylQ==","shasum":"d615e59ea54705956a4b864677d6b273f54e17e8","tarball":"https://registry.npmjs.org/@airy/maleo/-/maleo-0.3.1-canary.4.tgz","fileCount":95,"unpackedSize":324013,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJc47icCRA9TVsSAnZWagAAL18QAIWQMnRnliBmttcCHCzY\nZgOe+spkOSTrD6VjlcoLRaHxgFpVVjqGIUQSbC/FPv6gzYm9Y9dhNkxq5iq/\njcMfcd8zI2UztBR478tQh6KyiYebng6VmSSPUtqecQQom5vi7Los/t4Gg2II\njmG+5fE1vuuf8oZUfCo9S/+pJ45mrQTuTxgUwVMYbK9o4hQMBfgrW53MoB+B\n0Dxb/J1n5KnQWwNSMdsivQNxhXONzhIQrFnqUrGN3J9uO2bAkN8APn1OOlAC\n380vgbuZEUNbydJdadmw2akUIr659XGVd2bEtBv2P5VLfcPkb+A9Hudf4bj8\nOHYDQ56EwIrmcvPBgoh0MorXdnj1ZcSVEq43gj5K1YCaxDGnWaAp9V1mYdai\nLI4XHUj4yEzXO4lFecvlOzy6X71b4LTHYe277ilPTj7xMl/nJrLVrmol1sxW\nenYP5dYSjfCrXn3sKWCk9/xwp2v2fXv91OF/RkVGV8FVBg/EiPTmW1UAUBE+\nFgf45RhLzQdGvbnrRmQ0yoO0lgx2mJIxVjNho6OtbVPwA7/jMuUIj/3UvYqz\n+MW6BL6orMHRJHpGZMwsGWz+QQqwMKhbllotJmj6KVZ3QEXRAVKTAx7bikXe\nKCrSJ86ipZC/5Ak8DGe4x3lf0h4UBCj7bEw10cAz6QsyeB5A27+7dGvYQuNg\nxQmZ\r\n=ZjR+\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIAYI+GBuEQaUxwcutVVpa59gdBhiBUbN9EHzYHi1HnDPAiBhnnetS/uNMBz3gZU6wRd34q+j1WF0RnHbMKJQ3eohAw=="}]},"maintainers":[{"name":"airyrooms-engineering","email":"engineering@airyrooms.com"}],"_npmUser":{"name":"airyrooms-engineering","email":"engineering@airyrooms.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/maleo_0.3.1-canary.4_1558427803604_0.4188922525143004"},"_hasShrinkwrap":false},"0.3.1-canary.5":{"name":"@airy/maleo","version":"0.3.1-canary.5","description":"Un-opinionated Universal Rendering Javascript Framework","repository":{"type":"git","url":"git+https://github.com/airyrooms/maleo.js.git","bugs":"https://github.com/airyrooms/maleo.js/issues"},"author":{"name":"Airy"},"license":"MIT","private":false,"sideEffects":false,"scripts":{"build":"rm -rf dist && gulp","watch":"gulp watch","prepublish":"yarn build","start":"node server.js"},"bin":{"maleo":"lib/bin/maleo.js"},"dependencies":{"@babel/core":"7.1.2","@babel/plugin-proposal-class-properties":"7.1.0","@babel/plugin-proposal-decorators":"7.1.2","@babel/plugin-proposal-object-rest-spread":"7.0.0","@babel/plugin-syntax-dynamic-import":"7.0.0","@babel/plugin-transform-runtime":"7.1.0","@babel/preset-env":"7.1.0","@babel/preset-react":"7.0.0","@babel/runtime":"7.1.2","@babel/runtime-corejs2":"7.2.0","babel-loader":"8.0.4","case-sensitive-paths-webpack-plugin":"2.1.2","compression":"1.7.4","es6-promise":"4.2.5","eventsource":"1.0.7","express":"4.16.4","figlet":"1.2.1","friendly-errors-webpack-plugin":"1.7.0","hard-source-webpack-plugin":"0.13.1","helmet":"3.14.0","hoist-non-react-statics":"^3.3.0","isomorphic-fetch":"2.2.1","loader-utils":"1.1.0","node-notifier":"5.2.1","nodemon":"1.18.4","react-dom":"16.8.4","react-helmet":"5.2.0","react-loadable":"5.5.0","react-router-config":"5.0.0","react-router-dom":"5.0.0","rimraf":"2.6.3","source-map-support":"0.5.9","terser-webpack-plugin":"1.1.0","webpack":"4.29.0","webpack-bundle-analyzer":"3.0.3","webpack-cli":"3.1.2","webpack-dev-middleware":"3.4.0","webpack-hot-middleware":"2.24.3","webpackbar":"2.6.3"},"peerDependencies":{"react":"^16.8.4"},"devDependencies":{"@babel/preset-typescript":"^7.1.0","@types/webpack-bundle-analyzer":"^2.13.0","babel-plugin-module-resolver":"^3.1.1","gulp":"github:gulpjs/gulp#4.0","gulp-babel":"^8.0.0","gulp-clean":"^0.4.0","gulp-sourcemaps":"^2.6.4","gulp-uglify":"^3.0.1","gulp-watch":"^5.0.1","react":"^16.8.4"},"gitHead":"988983932c464106bbd0ff359342c26073fea6ee","readme":"[![Build Status](https://travis-ci.org/airyrooms/maleo.js.svg?branch=master)](https://travis-ci.org/airyrooms/maleo.js)\n[![Coverage Status](https://coveralls.io/repos/github/airyrooms/maleo.js/badge.svg?branch=master)](https://coveralls.io/github/airyrooms/maleo.js?branch=master)\n[![npm version](https://badge.fury.io/js/%40airy%2Fmaleo.svg)](https://badge.fury.io/js/%40airy%2Fmaleo)\n[![Issues](http://img.shields.io/github/issues/airyrooms/maleo.js.svg)]( https://github.com/airyrooms/maleo.js/issues)\n[![MIT license](https://img.shields.io/badge/License-MIT-blue.svg)](https://github.com/airyrooms/maleo.js/blob/canary/LICENSE)\n[![Discord Chat](https://img.shields.io/discord/550498214789513218.svg)](https://discord.gg/9eArCQn)\n[![Conventional Commits](https://img.shields.io/badge/Conventional%20Commits-1.0.0-green.svg)](https://conventionalcommits.org)\n\n\n# Welcome to Maleo.js\n\nMaleo.js is an un-opinionated framework to enable Universal Rendering in JavaScript using React with no hassle.\n\nWe are here to solve the time consuming setups Universal Rendering Required.\n\n---\n\nReadme below is the documentation for the `canary` (prerelease) branch. To view the documentation for the latest stable Maleo.js version change branch to `master`\n\n---\n\n# Table of Contents\n- [Features](#features)\n- [Setup](#setup)\n- [Component Lifecycle](#component-lifecycle)\n- [Routing](#routing)\n- [Dynamic Import Component](#dynamic-import-component)\n  - [Tips](#tips)\n  - [Preloading](#preloading)\n- [Customizable Component](#customizable-component)\n  - [Custom Document](#custom-document)\n  - [Custom Wrap](#custom-wrap)\n- [Custom Configuration](#custom-configuration)\n  - [Customize Server](#customize-server)\n  - [Customize Webpack](#customize-webpack)\n  - [Customize Babel Config](#customize-babel-config)\n- [CDN Support](#cdn-support)\n- [Plugins](#plugins)\n- [FAQ](#faq)\n- [Contributing](#contributing)\n\n---\n\n## Features\n- Universal Rendering\n- Plugin based framework\n- Customizable\n\n## Setup\n\nInstall Maleo.js \n\n>*for now change `@airy/maleo` to `@airy/maleo@canary` until we publish the stable version of this package*\n\n\n**NPM**\n```bash\n$ npm install --save @airy/maleo react\n```\n**Yarn**\n```bash\n$ yarn add @airy/maleo react\n```\n\nAdd this script to your `package.json`\n```json\n{\n  \"scripts\": {\n    \"dev\": \"maleo dev\",\n    \"build\": \"export NODE_ENV=production && maleo build\",\n    \"start\": \"export NODE_ENV=production && maleo run\"\n  }\n}\n```\n\nCreate a page Root component\n```jsx\n// ./src/Root.jsx\nimport React from 'react';\n\n// Export default is required for registering page\nexport default class RootComponent extends React.Component {\n  render() {\n    return (\n      <h1>Hello World!</h1>\n    )\n  }\n}\n```\n\nAnd lastly, create a routing file on your project root directory called `routes.json` and register your page component\n```json\n[\n  {\n    \"path\": \"/\",\n    \"page\": \"./src/Root\"\n  }\n]\n```\n\nAfter that you can now run `$ npm run dev` and go to `http://localhost:3000`.\n\nYou should now see your app running on your browser.\n\nBy now you should see \n- Automatic transpilation and bundling (with webpack and babel)\n- ~~Hot code reloading~~ [#17](https://github.com/airyrooms/maleo.js/issues/17)\n- Server rendering\n\nTo see how simple this is, check out the sample app!\n\n## Component Lifecycle \n\nMaleo.js added a new component lifecycle hook called `getInitialProps`, this function is called during Server Side Rendering (SSR) and during route changes on Client Side Rendering (CSR).\n\nThis is useful especially for SEO purposes. \n\nExample for stateful component:\n```jsx\nimport React from 'react';\n\nexport default class extends React.Component {\n  static getInitialProps = async (ctx) => {\n    const { req } = ctx;\n\n    // check if getInitialProps is called on server or on client\n    const userAgent = req ? req.headers['user-agent'] : navigator.userAgent;\n\n    // the return value will be passed as props for this component\n    return { userAgent };\n  }\n\n  render() {\n    return (\n      <div>\n        Hello World {this.props.userAgent}\n      </div>\n    );\n  }\n}\n```\n\nExample for stateless component:\n```jsx\nconst Component = (props) => (\n  <div>\n    Hello World {props.userAgent}\n  </div>\n);\n\nComponent.getInitialprops = async (ctx) => {\n  const { req } = ctx;\n\n  const userAgent = req ? req.headers['user-agent'] : navigator.userAgent;\n\n  return { userAgent };\n};\n\nexport default Component;\n```\n\n`getInitialProps` receives a context object with the following properties:\n- `req` - HTTP request object (server only)\n- `res` - HTTP response object (server only)\n- `...wrapProps` - Spreaded properties from custom Wrap\n- `...appProps` - Spreaded properties from custom App\n\n## Routing\n\nRouting is declared in a centralized route config.\nRegister all the route config in `routes.json` file.\n\nIf you put the `routes.json` files on root directory, Maleo will automatically register your route. Otherwise put path to your routes on [Maleo config](#custom-configuration).\n\nRoutes file has to export default the route configuration.\nThe route object **expected to have distinct key** to indicate the route.\n\n<table>\n  <tr>\n    <td>Key</td>\n    <td>Type</td>\n    <td>Description</td>\n  </tr>\n  <tr>\n    <td><code>path</code></td>\n    <td><code>String!</code></td>\n    <td>Routes path</td>\n  </tr>\n  <tr>\n    <td><code>page</code></td>\n    <td><code>String!</code></td>\n    <td>Path to React Component for this route path</td>\n  </tr>\n  <tr>\n    <td><code>exact</code></td>\n    <td><code>Boolean?</code> [<code>false</code>]</td>\n    <td>To make url has to match exactly the path in order to render the component. Give <code>false</code> value if the route is a wrapper route component</td>\n  </tr>\n  <tr>\n    <td><code>routes</code></td>\n    <td><code>RouteObject?</code></td>\n    <td>Nested route</td>\n  </tr>\n</table>\n\n\nFor example:\n```json\n[\n  {\n    \"page\": \"./src/MainApp\",\n    \"routes\": [\n      {\n        \"path\": \"/\",\n        \"page\": \"./src/Search\",\n        \"exact\": true\n      },\n      {\n        \"path\": \"/search\",\n        \"page\": \"./src/Search\",\n        \"routes\": [\n          {\n            \"path\": \"/search/hello\",\n            \"page\": \"./src/Detail\",\n            \"exact\": true\n          }\n        ]\n      },\n      {\n        \"path\": \"/detail\",\n        \"page\": \"./src/Detail\",\n        \"exact\": true\n      }\n    ]\n  }\n]\n```\n\n## Dynamic Import Component\nMaleo.js supports TC39 [dynamic import proposal](https://github.com/tc39/proposal-dynamic-import) for JavaScript.\n\nYou can think dynamic import as another way to split your code into manageable chunks. You can use our `Dynamic` function which utilizes [react loadable](https://github.com/jamiebuilds/react-loadable)\n\nFor Example\n```js\n// DynamicComponent.js\nimport Dynamic from '@airy/maleo/dynamic';\n\nexport default Dynamic({\n  loader: () => import( /* webpackChunkName:\"DynamicComponent\" */ './component/DynamicLoad'),\n})\n```\n\n### **Preloading**\n\nFor optimization purposes, you can also preload a component even before the component got rendered.\n\nFor example, if you want to load component when a button get pressed, you can start preloading the component when the user hovers the mouse over the button.\n\nThe component created by `Dynamic` exposes a [static `preload` method](https://github.com/jamiebuilds/react-loadable#loadablecomponentpreload).\n\n```jsx\nimport React from 'react';\nimport Dynamic from '@airy/maleo/dynamic';\n\nconst DynamicBar = Dynamic({\n  loader: () => import('./Bar'),\n  loading: LoadingCompoinent\n});\n\nclass MyComponent extends React.Component {\n  state = { showBar: false };\n\n  onClick = () => {\n    this.setState({ showBar: true });\n  };\n\n  onMouseOver = () => DynamicBar.preload();\n\n  render() {\n    return (\n      <div>\n        <button\n          onClick={this.onClick}\n          onMouseOver={this.onMouseOver}>\n          Show Bar\n        </button>\n        { this.state.showBar && <DynamicBar /> }\n      </div>\n    )\n  }\n}\n```\n\n## Customizable Component\n\n### Custom Document\n\nHighly inspired by what [Next.js](https://github.com/zeit/next.js) has done on their awesome template customization.\n\nMaleo.js also enable customization on `Document` as document's markup. So you don't need to include tags like `<html>`, `<head>`, `<body>`, etc. \n\nTo override the default behavior, you'll need to create a component that extends the `Document` React class provided by Maleo.\n\n```jsx\n// document.jsx\nimport React from 'react';\nimport { Document, Header, Main, Scripts } from '@airy/maleo/document';\n\nexport default class extends Document {\n  render() {\n    return (\n      <html>\n        <Header>\n          <title>Maleo JS</title>\n          <meta charset=\"utf-8\" />\n          <meta name=\"description\" content=\"Maleo.js is awesome!\" />\n\n          <style>\n            {` body { background-color: #fff } `}\n          </style>\n        </Header>\n\n        <body>\n          <Main />\n\n          <Scripts />\n\n          <ReduxScript />\n        </body>\n      </html>\n    );\n  }\n}\n```\n\n### Custom Wrap\n\nMaleo.js uses the `Wrap` component to initialize pages. `Wrap` contains React Router's Component. You can add HoC here to wrap the application and control the page initialization. Which allows you to do amazing things like:\n- Persisting layour between page changes\n- Keeping state when navigating pages\n- Custom error handling using `componentDidCatch`\n- Inject additional data into pages (like Redux provider, etc)\n\nTo override the default behavior, you'll need to create a component that extends the `Wrap` React class provided by Maleo.\n\n```jsx\n// wrap.jsx\nimport React from 'react';\nimport { Wrap } from '@airy/maleo/wrap';\n\n// Redux plugin for Maleo.js\n// Hoc that creates store and add Redux Provider\nimport { withRedux } from '@airy/maleo-redux-plugin';\n\n// Custom Wrapper that will be rendered for the whole Application\nimport Content from './component/Content';\nimport NavigationBar from './component/NavigationBar';\n\nimport { createStore } from './store';\n\n@withRedux(createStore)\nexport default class extends Wrap {\n  static getInitialProps = (ctx) => {\n    const { store } = ctx\n    // you receive store from context\n    // you can access or do something with the store here\n    console.log('Initialized Redux Store', store);\n    return {}\n  }\n\n  render() {\n    const { Container, containerProps, App, appProps } = this.props\n\n    return (\n      <Container {...containerProps}>\n        <NavigationBar />\n        <Content>\n          <App {...appProps}/>\n        </Content>\n      </Container>\n    )\n  }\n}\n\n```\nIf you put `document.jsx` and `wrap.jsx` on root directory (the same level with `package.json`), then Maleo will automatically register your custom Document and Wrap. Otherwise, you can add the path to your custom Document and Wrap on [Maleo config](#custom-configuration)\n\n---\n***We are also working on adding default and customizable `Error` component page***\n\n---\n\n## Custom Configuration\n\nFor more advanced configuration of Maleo.js, like `webpack` config, registering `plugins`, path to your routes, custom Document and Wrap, and adding `path alias`, you can create a `maleo.config.js` in the root of your project directory. (same directory with `package.json`)\n\n```js\n// maleo.config.js\n\nmodule.exports = {\n  /* config options here */\n}\n```\n\nHere are the API's for the configuration:\n\n<table>\n  <tr>\n    <td>Key</td>\n    <td>Type</td>\n    <td>Description</td>\n  </tr>\n  <tr>\n    <td><code>buildDir</code></td>\n    <td><code>String?</code> [<code>.maleo</code>]</td>\n    <td>Directory to put Maleo.js' build assets</td>\n  </tr>\n  <tr>\n    <td><code>cache</code></td>\n    <td><code>Boolean?</code> [<code>true</code>]</td>\n    <td>Enable webpack build caching</td>\n  </tr>\n  <tr>\n    <td><code>isDev</code></td>\n    <td><code>Boolean?</code> [<code>process.env.NODE_ENV === 'development'</code>]</td>\n    <td>Enable development build configuration</td>\n  </tr>\n  <tr>\n    <td><code>sourceMaps</code></td>\n    <td><code>Boolean?</code> [<code>true</code>]</td>\n    <td>Enable webpack to generate source maps</td>\n  </tr>\n  <tr>\n    <td><code>alias</code></td>\n    <td><code>Object?</code></td>\n    <td>A key value pair for aliasing path directory\n      <br>\n      <code>\n      { 'component': './src/component' }\n      </code>\n    </td>\n  </tr>\n  <tr>\n    <td><code>publicPath</code></td>\n    <td><code>String?</code> [<code>/_assets/</code>]</td>\n    <td>To customize webpack's <code>publicPath</code> \n    <br>Comes in handy if using CDN to put built assets</td>\n  </tr>\n  <tr>\n    <td><code>analyzeBundle</code></td>\n    <td><code>Boolean?</code> [<code>false</code>]</td>\n    <td>To enable webpack's bundle analyzer, for analyzing bundle sizes during bundle debugging should Maleo.js' build process got slow</td>\n  </tr>\n  <tr>\n    <td><code>webpack</code></td>\n    <td><code>Function?</code></td>\n    <td>To customize webpack configuration, more details <a href=\"#customize-webpack\">here</a></td>\n  </tr>\n  <tr>\n    <td><code>routes</code></td>\n    <td><code>string?</code> [<code>rootDir/routes.jsx</code>]</td>\n    <td>Path to your routes file</td>\n  </tr>\n  <tr>\n    <td><code>customDocument</code></td>\n    <td><code>string?</code> [<code>rootDir/document.jsx</code>]</td>\n    <td>Path to your custom document file</td>\n  </tr>\n  <tr>\n    <td><code>customWrap</code></td>\n    <td><code>string?</code> [<code>rootDir/wrap.jsx</code>]</td>\n    <td>Path to your custom wrap file</td>\n  </tr>\n  <tr>\n    <td><code>customApp</code></td>\n    <td><code>string?</code> [<code>rootDir/app.jsx</code>]</td>\n    <td>Path to your custom app file</td>\n  </tr>\n</table>\n\n#### Customize Server\n\nCreate a `server.js` file on root directory where your `package.json` lives.\nHere you can customize Maleo's server.\n```js\nimport { Server } from '@airy/maleo/server';\nimport path from 'path';\n\nimport routeConfig from './routes';\n\nconst PORT = process.env.PORT || 8080;\n\nconst maleoServer = Server.init({\n  port: PORT,\n});\n\nmaleoServer.run(() => {\n  console.log('Server running on port :', PORT);\n});\n```\n\nHere are the API's for the configuration:\n\n<table>\n  <tr>\n    <td>Key</td>\n    <td>Type</td>\n    <td>Description</td>\n  </tr>\n  <tr>\n    <td><code>port</code></td>\n    <td><code>Number?</code> [<code>3000</code>]</td>\n    <td>Port to run Maleo server</td>\n  </tr>\n  <tr>\n    <td><code>assetDir</code></td>\n    <td><code>String?</code> [<code>'<root>/.maleo/client'</code>]</td>\n    <td>Directory for all client related assets</td>\n  </tr>\n</table>\n\n#### Customize Webpack\n\nYou are able to extend Maleo.js' default webpack configuration by defining a function on `maleo.config.js`\n\n```js\n// maleo.config.js\n\nmodule.exports = {\n  webpack(config, context, next) {\n    // Perform customizations to webpack config\n    // Important: This call is required in order to let Maleo pass the config to webpack\n    return next(); \n  },\n};\n```\n\nWebpack function will receive three arguments:\n\n<table>\n  <tr>\n    <td>Argument</td>\n    <td>Details</td>\n  <tr>\n  <tr>\n    <td><code>config</code></td>\n    <td>This contains webpack configuration object that you can manipulate</td>\n  <tr>\n  <tr>\n    <td><code>context</code></td>\n    <td>This contains some keys that are useful for the build context\n      <br>\n      <table>\n        <tr>\n          <td><code>isDev</code></td>\n          <td><code>Boolean</code></td>\n          <td>Check if current build is for development</td>\n        </tr>\n        <tr>\n          <td><code>publicPath</code></td>\n          <td><code>String</code></td>\n          <td>Public Path of user defined or default's value</td>\n        </tr>\n        <tr>\n          <td><code>analyzeBundle</code></td>\n          <td><code>Boolean</code></td>\n          <td>Check if analyze bundle is enabled</td>\n        </tr>\n        <tr>\n          <td><code>buildDirectory</code></td>\n          <td><code>String</code></td>\n          <td>Build Directory of user defined or default's value</td>\n        </tr>\n        <tr>\n          <td><code>name</code></td>\n          <td><code>String</code></td>\n          <td>Build name <code>'server' || 'client'</code></td>\n        </tr>\n      </table>\n    </td>\n  <tr>\n  <tr>\n    <td><code>next</code></td>\n    <td>A callback required to be called to pass the custom configuration</td>\n  <tr>\n</table>\n\nExample of adding `ts-loader` through `maleo.config.js`:\n\n```js\n// maleo.config.js\n\n// Partly taken and modified from @airy/maleo-ts-plugin source\n// for simplicity purposes\n\nmodule.exports = {\n  webpack(config, context, next) {\n    const { isDev } = context\n\n    config.module.rules.push({\n      test: /\\.tsx?/,\n      exclude: /node_modules/,\n      use: [\n        require.resolve('@airy/maleo/lib/build/loaders/maleo-babel-loader'),\n        {\n          loader: 'ts-loader',\n          options: {\n            transpileOnly: true,\n          },\n        },\n      ],\n\n      if (isDev) {\n        config.plugins.push(new ForkTSCheckerWebpackPlugin());\n      }\n\n      return next();\n    })\n  },\n};\n```\n\n#### Customize Babel Config\n\nMaleo.js also let you have your own babel config. Just simply add `.babelrc` file at the root directory of your app.\n\nYou can include Maleo.js' babel preset in order to have latest JavaScript preset.\n\nHere's an example of `.babelrc` file:\n```json\n{\n  \"presets\": [\"@airy/maleo/babel\"],\n  \"plugins\": []\n}\n```\n\nThe `@airy/maleo/babel` preset includes everything you need to get your development started. The preset includes:\n- `@babel/preset-env`\n- `@babel/preset-react`\n- `@babel/plugin-proposal-class-properties`\n- `@babel/plugin-proposal-decorators`\n- `@babel/plugin-proposal-object-rest-spread`\n- `@babel/plugin-transform-runtime`\n- `react-loadable/babel`\n\n## CDN Support\n\nIf you are using a CDN, you can set up the `publicPath` setting and configure your CDN's origin to resolve to the domain that Maleo.js is hosted on.\n\n```js\n// maleo.config.js\n\nconst isProd = process.env.NODE_ENV === 'production';\n\nmodule.exports = {\n  assetPrefix: isProd && 'https://cdn.example.com';\n}\n```\n\n## FAQ\n\n== TO BE DETERMINED == \n\n## Plugins\n- [css-plugin](https://github.com/airyrooms/maleo-plugins/tree/master/packages/css-plugin)\n- [redux-plugin](https://github.com/airyrooms/maleo-plugins/tree/master/packages/redux-plugin)\n- [typescript-plugin](https://github.com/airyrooms/maleo-plugins/tree/master/packages/typescript-plugin)\n- [more](https://github.com/airyrooms/maleo-plugins)\n\n\n## Contributing\n[Please follow these steps to contribute to Maleo.js](https://github.com/airyrooms/maleo.js/blob/canary/CONTRIBUTING.md)\n\n[Please follow these steps to contribute to Maleo.js' plugins](https://github.com/airyrooms/maleo-plugins#contributing-guidelines)\n\n\n## Contributors\n\nThis project exists thanks to all the people who contribute. [Contribute](CONTRIBUTING.md).\n\nMany thanks to our **[contributors](https://github.com/airyrooms/maleo.js/graphs/contributors)**!\n\n\n## License\n\nMIT","readmeFilename":"README.md","bugs":{"url":"https://github.com/airyrooms/maleo.js/issues"},"homepage":"https://github.com/airyrooms/maleo.js#readme","_id":"@airy/maleo@0.3.1-canary.5","_nodeVersion":"10.15.3","_npmVersion":"lerna/3.13.1/node@v10.15.3+x64 (linux)","dist":{"integrity":"sha512-bfFe2t6uJRKJit5IcQXW/Uc5ddedWSVlWZoXHX1hgUkPmNiP1ib5Nh7nxSEyimJj6nQCRwPUTdkYltcKNWWz+w==","shasum":"e27fb5facd65b1a6fddede151c2f72f6ca6a548c","tarball":"https://registry.npmjs.org/@airy/maleo/-/maleo-0.3.1-canary.5.tgz","fileCount":95,"unpackedSize":324383,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJc49EpCRA9TVsSAnZWagAAw9UP/0sEtzX+pdLPKU+HOWIR\nohaCFK5o2oUkUwA5WHHE3PQrjQY5vZX1SoJP3Y0lwqiklUXtapglP3Xmo5gX\nuySmCdq9j7cN72lqrvhDn6t76qztsghwEN+qd9eBipUEJTb9pyXm9KtgGzt3\nb2XgxkiFg8DJioSC6stawoerCQk3ntjjwHdjSBF1yxNPsAeq5UI4fL/GdQSt\nLEkjDmgo5X9bkeFdfEgaSo8A9EkA9uoFbGX9vwnUYSwP55OS6G844fA1O0b0\nFP3HlD0ArSuf3G9a7/55UGB4rZfqn2SAI3okHPaxYTYFnuRTPV+kq85NbHuE\nWHd8EU/sx0XV5fMiu6rlNfg8eGNkrllhwtq7UZLkPnzfMvSmvqqVq8tyd0re\njqBQ9gDuBB5c6cAnOJAL617/uRPxEaVzJhX52ytjZzxgwn7PARfX/5GX3IuD\nFbmgeie3OUXzxkI+lweoh2kt2RkJJoQTmJtPDvRjqN3w98p9l+sq2obsWr6O\nMquOjRBD/b1e7QgfPMtaMnhJr7S92mj9z90Ojk4URXnBhbFJ9l1Ncg2cLhJj\nGY1757wwmz0Id8eXSUV180g0PIku8cKIg423jAIIl30GmrCN+QH7D4ZSTXSW\n8Mmazn9ABhtdoinyx3KU2r+cFkJkLF7tttVu2QbEEbhrVh07og5u9OAOyxp9\n6rwy\r\n=DBzd\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDGq7E1r73TsQIw597dV3KuZVEVVGGZaRqv5WQoy/jnOwIgbWx9VC3PPNUi3VK2p81qFgVc8rwrCBbq+Ca7lDM4kxw="}]},"maintainers":[{"name":"airyrooms-engineering","email":"engineering@airyrooms.com"}],"_npmUser":{"name":"airyrooms-engineering","email":"engineering@airyrooms.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/maleo_0.3.1-canary.5_1558434089269_0.15097870948891057"},"_hasShrinkwrap":false},"0.3.1-canary.6":{"name":"@airy/maleo","version":"0.3.1-canary.6","description":"Un-opinionated Universal Rendering Javascript Framework","repository":{"type":"git","url":"git+https://github.com/airyrooms/maleo.js.git","bugs":"https://github.com/airyrooms/maleo.js/issues"},"author":{"name":"Airy"},"license":"MIT","private":false,"sideEffects":false,"scripts":{"build":"rm -rf dist && gulp","watch":"gulp watch","prepublish":"yarn build","start":"node server.js"},"bin":{"maleo":"lib/bin/maleo.js"},"dependencies":{"@babel/core":"7.1.2","@babel/plugin-proposal-class-properties":"7.1.0","@babel/plugin-proposal-decorators":"7.1.2","@babel/plugin-proposal-object-rest-spread":"7.0.0","@babel/plugin-syntax-dynamic-import":"7.0.0","@babel/plugin-transform-runtime":"7.1.0","@babel/preset-env":"7.1.0","@babel/preset-react":"7.0.0","@babel/runtime":"7.1.2","@babel/runtime-corejs2":"7.2.0","babel-loader":"8.0.4","case-sensitive-paths-webpack-plugin":"2.1.2","compression":"1.7.4","es6-promise":"4.2.5","eventsource":"1.0.7","express":"4.16.4","figlet":"1.2.1","friendly-errors-webpack-plugin":"1.7.0","hard-source-webpack-plugin":"0.13.1","helmet":"3.14.0","hoist-non-react-statics":"^3.3.0","isomorphic-fetch":"2.2.1","loader-utils":"1.1.0","node-notifier":"5.2.1","nodemon":"1.18.4","react-dom":"16.8.4","react-helmet":"5.2.0","react-loadable":"5.5.0","react-router-config":"5.0.0","react-router-dom":"5.0.0","rimraf":"2.6.3","source-map-support":"0.5.9","terser-webpack-plugin":"1.1.0","webpack":"4.29.0","webpack-bundle-analyzer":"3.0.3","webpack-cli":"3.1.2","webpack-dev-middleware":"3.4.0","webpack-hot-middleware":"2.24.3","webpackbar":"2.6.3"},"peerDependencies":{"react":"^16.8.4"},"devDependencies":{"@babel/preset-typescript":"^7.1.0","@types/webpack-bundle-analyzer":"^2.13.0","babel-plugin-module-resolver":"^3.1.1","gulp":"github:gulpjs/gulp#4.0","gulp-babel":"^8.0.0","gulp-clean":"^0.4.0","gulp-sourcemaps":"^2.6.4","gulp-uglify":"^3.0.1","gulp-watch":"^5.0.1","react":"^16.8.4"},"gitHead":"23002a29330dc96616d2829b32aade64d92a2bf6","readme":"[![Build Status](https://travis-ci.org/airyrooms/maleo.js.svg?branch=master)](https://travis-ci.org/airyrooms/maleo.js)\n[![Coverage Status](https://coveralls.io/repos/github/airyrooms/maleo.js/badge.svg?branch=master)](https://coveralls.io/github/airyrooms/maleo.js?branch=master)\n[![npm version](https://badge.fury.io/js/%40airy%2Fmaleo.svg)](https://badge.fury.io/js/%40airy%2Fmaleo)\n[![Issues](http://img.shields.io/github/issues/airyrooms/maleo.js.svg)]( https://github.com/airyrooms/maleo.js/issues)\n[![MIT license](https://img.shields.io/badge/License-MIT-blue.svg)](https://github.com/airyrooms/maleo.js/blob/canary/LICENSE)\n[![Discord Chat](https://img.shields.io/discord/550498214789513218.svg)](https://discord.gg/9eArCQn)\n[![Conventional Commits](https://img.shields.io/badge/Conventional%20Commits-1.0.0-green.svg)](https://conventionalcommits.org)\n\n\n# Welcome to Maleo.js\n\nMaleo.js is an un-opinionated framework to enable Universal Rendering in JavaScript using React with no hassle.\n\nWe are here to solve the time consuming setups Universal Rendering Required.\n\n---\n\nReadme below is the documentation for the `canary` (prerelease) branch. To view the documentation for the latest stable Maleo.js version change branch to `master`\n\n---\n\n# Table of Contents\n- [Features](#features)\n- [Setup](#setup)\n- [Component Lifecycle](#component-lifecycle)\n- [Routing](#routing)\n- [Dynamic Import Component](#dynamic-import-component)\n  - [Tips](#tips)\n  - [Preloading](#preloading)\n- [Customizable Component](#customizable-component)\n  - [Custom Document](#custom-document)\n  - [Custom Wrap](#custom-wrap)\n- [Custom Configuration](#custom-configuration)\n  - [Customize Server](#customize-server)\n  - [Customize Webpack](#customize-webpack)\n  - [Customize Babel Config](#customize-babel-config)\n- [Core Utilities Functions](#core-utilities-functions)\n- [CDN Support](#cdn-support)\n- [Plugins](#plugins)\n- [FAQ](#faq)\n- [Contributing](#contributing)\n\n---\n\n## Features\n- Universal Rendering\n- Plugin based framework\n- Customizable\n\n## Setup\n\nInstall Maleo.js \n\n>*for now change `@airy/maleo` to `@airy/maleo@canary` until we publish the stable version of this package*\n\n\n**NPM**\n```bash\n$ npm install --save @airy/maleo react\n```\n**Yarn**\n```bash\n$ yarn add @airy/maleo react\n```\n\nAdd this script to your `package.json`\n```json\n{\n  \"scripts\": {\n    \"dev\": \"maleo dev\",\n    \"build\": \"export NODE_ENV=production && maleo build\",\n    \"start\": \"export NODE_ENV=production && maleo run\"\n  }\n}\n```\n\nCreate a page Root component\n```jsx\n// ./src/Root.jsx\nimport React from 'react';\n\n// Export default is required for registering page\nexport default class RootComponent extends React.Component {\n  render() {\n    return (\n      <h1>Hello World!</h1>\n    )\n  }\n}\n```\n\nAnd lastly, create a routing file on your project root directory called `routes.json` and register your page component\n```json\n[\n  {\n    \"path\": \"/\",\n    \"page\": \"./src/Root\"\n  }\n]\n```\n\nAfter that you can now run `$ npm run dev` and go to `http://localhost:3000`.\n\nYou should now see your app running on your browser.\n\nBy now you should see \n- Automatic transpilation and bundling (with webpack and babel)\n- ~~Hot code reloading~~ [#17](https://github.com/airyrooms/maleo.js/issues/17)\n- Server rendering\n\nTo see how simple this is, check out the sample app!\n\n## Component Lifecycle \n\nMaleo.js added a new component lifecycle hook called `getInitialProps`, this function is called during Server Side Rendering (SSR) and during route changes on Client Side Rendering (CSR).\n\nThis is useful especially for SEO purposes. \n\nExample for stateful component:\n```jsx\nimport React from 'react';\n\nexport default class extends React.Component {\n  static getInitialProps = async (ctx) => {\n    const { req } = ctx;\n\n    // check if getInitialProps is called on server or on client\n    const userAgent = req ? req.headers['user-agent'] : navigator.userAgent;\n\n    // the return value will be passed as props for this component\n    return { userAgent };\n  }\n\n  render() {\n    return (\n      <div>\n        Hello World {this.props.userAgent}\n      </div>\n    );\n  }\n}\n```\n\nExample for stateless component:\n```jsx\nconst Component = (props) => (\n  <div>\n    Hello World {props.userAgent}\n  </div>\n);\n\nComponent.getInitialprops = async (ctx) => {\n  const { req } = ctx;\n\n  const userAgent = req ? req.headers['user-agent'] : navigator.userAgent;\n\n  return { userAgent };\n};\n\nexport default Component;\n```\n\n`getInitialProps` receives a context object with the following properties:\n- `req` - HTTP request object (server only)\n- `res` - HTTP response object (server only)\n- `...wrapProps` - Spreaded properties from custom Wrap\n- `...appProps` - Spreaded properties from custom App\n\n## Routing\n\nRouting is declared in a centralized route config.\nRegister all the route config in `routes.json` file.\n\nIf you put the `routes.json` files on root directory, Maleo will automatically register your route. Otherwise put path to your routes on [Maleo config](#custom-configuration).\n\nRoutes file has to export default the route configuration.\nThe route object **expected to have distinct key** to indicate the route.\n\n<table>\n  <tr>\n    <td>Key</td>\n    <td>Type</td>\n    <td>Description</td>\n  </tr>\n  <tr>\n    <td><code>path</code></td>\n    <td><code>String!</code></td>\n    <td>Routes path</td>\n  </tr>\n  <tr>\n    <td><code>page</code></td>\n    <td><code>String!</code></td>\n    <td>Path to React Component for this route path</td>\n  </tr>\n  <tr>\n    <td><code>exact</code></td>\n    <td><code>Boolean?</code> [<code>false</code>]</td>\n    <td>To make url has to match exactly the path in order to render the component. Give <code>false</code> value if the route is a wrapper route component</td>\n  </tr>\n  <tr>\n    <td><code>routes</code></td>\n    <td><code>RouteObject?</code></td>\n    <td>Nested route</td>\n  </tr>\n</table>\n\n\nFor example:\n```json\n[\n  {\n    \"page\": \"./src/MainApp\",\n    \"routes\": [\n      {\n        \"path\": \"/\",\n        \"page\": \"./src/Search\",\n        \"exact\": true\n      },\n      {\n        \"path\": \"/search\",\n        \"page\": \"./src/Search\",\n        \"routes\": [\n          {\n            \"path\": \"/search/hello\",\n            \"page\": \"./src/Detail\",\n            \"exact\": true\n          }\n        ]\n      },\n      {\n        \"path\": \"/detail\",\n        \"page\": \"./src/Detail\",\n        \"exact\": true\n      }\n    ]\n  }\n]\n```\n\n## Dynamic Import Component\nMaleo.js supports TC39 [dynamic import proposal](https://github.com/tc39/proposal-dynamic-import) for JavaScript.\n\nYou can think dynamic import as another way to split your code into manageable chunks. You can use our `Dynamic` function which utilizes [react loadable](https://github.com/jamiebuilds/react-loadable)\n\nFor Example\n```js\n// DynamicComponent.js\nimport Dynamic from '@airy/maleo/dynamic';\n\nexport default Dynamic({\n  loader: () => import( /* webpackChunkName:\"DynamicComponent\" */ './component/DynamicLoad'),\n})\n```\n\n### **Preloading**\n\nFor optimization purposes, you can also preload a component even before the component got rendered.\n\nFor example, if you want to load component when a button get pressed, you can start preloading the component when the user hovers the mouse over the button.\n\nThe component created by `Dynamic` exposes a [static `preload` method](https://github.com/jamiebuilds/react-loadable#loadablecomponentpreload).\n\n```jsx\nimport React from 'react';\nimport Dynamic from '@airy/maleo/dynamic';\n\nconst DynamicBar = Dynamic({\n  loader: () => import('./Bar'),\n  loading: LoadingCompoinent\n});\n\nclass MyComponent extends React.Component {\n  state = { showBar: false };\n\n  onClick = () => {\n    this.setState({ showBar: true });\n  };\n\n  onMouseOver = () => DynamicBar.preload();\n\n  render() {\n    return (\n      <div>\n        <button\n          onClick={this.onClick}\n          onMouseOver={this.onMouseOver}>\n          Show Bar\n        </button>\n        { this.state.showBar && <DynamicBar /> }\n      </div>\n    )\n  }\n}\n```\n\n## Customizable Component\n\n### Custom Document\n\nHighly inspired by what [Next.js](https://github.com/zeit/next.js) has done on their awesome template customization.\n\nMaleo.js also enable customization on `Document` as document's markup. So you don't need to include tags like `<html>`, `<head>`, `<body>`, etc. \n\nTo override the default behavior, you'll need to create a component that extends the `Document` React class provided by Maleo.\n\n```jsx\n// document.jsx\nimport React from 'react';\nimport { Document, Header, Main, Scripts } from '@airy/maleo/document';\n\nexport default class extends Document {\n  render() {\n    return (\n      <html>\n        <Header>\n          <title>Maleo JS</title>\n          <meta charset=\"utf-8\" />\n          <meta name=\"description\" content=\"Maleo.js is awesome!\" />\n\n          <style>\n            {` body { background-color: #fff } `}\n          </style>\n        </Header>\n\n        <body>\n          <Main />\n\n          <Scripts />\n\n          <ReduxScript />\n        </body>\n      </html>\n    );\n  }\n}\n```\n\n### Custom Wrap\n\nMaleo.js uses the `Wrap` component to initialize pages. `Wrap` contains React Router's Component. You can add HoC here to wrap the application and control the page initialization. Which allows you to do amazing things like:\n- Persisting layour between page changes\n- Keeping state when navigating pages\n- Custom error handling using `componentDidCatch`\n- Inject additional data into pages (like Redux provider, etc)\n\nTo override the default behavior, you'll need to create a component that extends the `Wrap` React class provided by Maleo.\n\n```jsx\n// wrap.jsx\nimport React from 'react';\nimport { Wrap } from '@airy/maleo/wrap';\n\n// Redux plugin for Maleo.js\n// Hoc that creates store and add Redux Provider\nimport { withRedux } from '@airy/maleo-redux-plugin';\n\n// Custom Wrapper that will be rendered for the whole Application\nimport Content from './component/Content';\nimport NavigationBar from './component/NavigationBar';\n\nimport { createStore } from './store';\n\n@withRedux(createStore)\nexport default class extends Wrap {\n  static getInitialProps = (ctx) => {\n    const { store } = ctx\n    // you receive store from context\n    // you can access or do something with the store here\n    console.log('Initialized Redux Store', store);\n    return {}\n  }\n\n  render() {\n    const { Container, containerProps, App, appProps } = this.props\n\n    return (\n      <Container {...containerProps}>\n        <NavigationBar />\n        <Content>\n          <App {...appProps}/>\n        </Content>\n      </Container>\n    )\n  }\n}\n\n```\nIf you put `document.jsx` and `wrap.jsx` on root directory (the same level with `package.json`), then Maleo will automatically register your custom Document and Wrap. Otherwise, you can add the path to your custom Document and Wrap on [Maleo config](#custom-configuration)\n\n---\n***We are also working on adding default and customizable `Error` component page***\n\n---\n\n## Custom Configuration\n\nFor more advanced configuration of Maleo.js, like `webpack` config, registering `plugins`, path to your routes, custom Document and Wrap, and adding `path alias`, you can create a `maleo.config.js` in the root of your project directory. (same directory with `package.json`)\n\n```js\n// maleo.config.js\n\nmodule.exports = {\n  /* config options here */\n}\n```\n\nHere are the API's for the configuration:\n\n<table>\n  <tr>\n    <td>Key</td>\n    <td>Type</td>\n    <td>Description</td>\n  </tr>\n  <tr>\n    <td><code>buildDir</code></td>\n    <td><code>String?</code> [<code>.maleo</code>]</td>\n    <td>Directory to put Maleo.js' build assets</td>\n  </tr>\n  <tr>\n    <td><code>cache</code></td>\n    <td><code>Boolean?</code> [<code>true</code>]</td>\n    <td>Enable webpack build caching</td>\n  </tr>\n  <tr>\n    <td><code>isDev</code></td>\n    <td><code>Boolean?</code> [<code>process.env.NODE_ENV === 'development'</code>]</td>\n    <td>Enable development build configuration</td>\n  </tr>\n  <tr>\n    <td><code>sourceMaps</code></td>\n    <td><code>Boolean?</code> [<code>true</code>]</td>\n    <td>Enable webpack to generate source maps</td>\n  </tr>\n  <tr>\n    <td><code>alias</code></td>\n    <td><code>Object?</code></td>\n    <td>A key value pair for aliasing path directory\n      <br>\n      <code>\n      { 'component': './src/component' }\n      </code>\n    </td>\n  </tr>\n  <tr>\n    <td><code>publicPath</code></td>\n    <td><code>String?</code> [<code>/_assets/</code>]</td>\n    <td>To customize webpack's <code>publicPath</code> \n    <br>Comes in handy if using CDN to put built assets</td>\n  </tr>\n  <tr>\n    <td><code>analyzeBundle</code></td>\n    <td><code>Boolean?</code> [<code>false</code>]</td>\n    <td>To enable webpack's bundle analyzer, for analyzing bundle sizes during bundle debugging should Maleo.js' build process got slow</td>\n  </tr>\n  <tr>\n    <td><code>webpack</code></td>\n    <td><code>Function?</code></td>\n    <td>To customize webpack configuration, more details <a href=\"#customize-webpack\">here</a></td>\n  </tr>\n  <tr>\n    <td><code>routes</code></td>\n    <td><code>string?</code> [<code>rootDir/routes.jsx</code>]</td>\n    <td>Path to your routes file</td>\n  </tr>\n  <tr>\n    <td><code>customDocument</code></td>\n    <td><code>string?</code> [<code>rootDir/document.jsx</code>]</td>\n    <td>Path to your custom document file</td>\n  </tr>\n  <tr>\n    <td><code>customWrap</code></td>\n    <td><code>string?</code> [<code>rootDir/wrap.jsx</code>]</td>\n    <td>Path to your custom wrap file</td>\n  </tr>\n  <tr>\n    <td><code>customApp</code></td>\n    <td><code>string?</code> [<code>rootDir/app.jsx</code>]</td>\n    <td>Path to your custom app file</td>\n  </tr>\n</table>\n\n#### Customize Server\n\nCreate a `server.js` file on root directory where your `package.json` lives.\nHere you can customize Maleo's server.\n```js\nimport { Server } from '@airy/maleo/server';\nimport path from 'path';\n\nimport routeConfig from './routes';\n\nconst PORT = process.env.PORT || 8080;\n\nconst maleoServer = Server.init({\n  port: PORT,\n});\n\nmaleoServer.run(() => {\n  console.log('Server running on port :', PORT);\n});\n```\n\nHere are the API's for the configuration:\n\n<table>\n  <tr>\n    <td>Key</td>\n    <td>Type</td>\n    <td>Description</td>\n  </tr>\n  <tr>\n    <td><code>port</code></td>\n    <td><code>Number?</code> [<code>3000</code>]</td>\n    <td>Port to run Maleo server</td>\n  </tr>\n  <tr>\n    <td><code>assetDir</code></td>\n    <td><code>String?</code> [<code>'<root>/.maleo/client'</code>]</td>\n    <td>Directory for all client related assets</td>\n  </tr>\n</table>\n\n#### Customize Webpack\n\nYou are able to extend Maleo.js' default webpack configuration by defining a function on `maleo.config.js`\n\n```js\n// maleo.config.js\n\nmodule.exports = {\n  webpack(config, context, next) {\n    // Perform customizations to webpack config\n    // Important: This call is required in order to let Maleo pass the config to webpack\n    return next(); \n  },\n};\n```\n\nWebpack function will receive three arguments:\n\n<table>\n  <tr>\n    <td>Argument</td>\n    <td>Details</td>\n  <tr>\n  <tr>\n    <td><code>config</code></td>\n    <td>This contains webpack configuration object that you can manipulate</td>\n  <tr>\n  <tr>\n    <td><code>context</code></td>\n    <td>This contains some keys that are useful for the build context\n      <br>\n      <table>\n        <tr>\n          <td><code>isDev</code></td>\n          <td><code>Boolean</code></td>\n          <td>Check if current build is for development</td>\n        </tr>\n        <tr>\n          <td><code>publicPath</code></td>\n          <td><code>String</code></td>\n          <td>Public Path of user defined or default's value</td>\n        </tr>\n        <tr>\n          <td><code>analyzeBundle</code></td>\n          <td><code>Boolean</code></td>\n          <td>Check if analyze bundle is enabled</td>\n        </tr>\n        <tr>\n          <td><code>buildDirectory</code></td>\n          <td><code>String</code></td>\n          <td>Build Directory of user defined or default's value</td>\n        </tr>\n        <tr>\n          <td><code>name</code></td>\n          <td><code>String</code></td>\n          <td>Build name <code>'server' || 'client'</code></td>\n        </tr>\n      </table>\n    </td>\n  <tr>\n  <tr>\n    <td><code>next</code></td>\n    <td>A callback required to be called to pass the custom configuration</td>\n  <tr>\n</table>\n\nExample of adding `ts-loader` through `maleo.config.js`:\n\n```js\n// maleo.config.js\n\n// Partly taken and modified from @airy/maleo-ts-plugin source\n// for simplicity purposes\n\nmodule.exports = {\n  webpack(config, context, next) {\n    const { isDev } = context\n\n    config.module.rules.push({\n      test: /\\.tsx?/,\n      exclude: /node_modules/,\n      use: [\n        require.resolve('@airy/maleo/lib/build/loaders/maleo-babel-loader'),\n        {\n          loader: 'ts-loader',\n          options: {\n            transpileOnly: true,\n          },\n        },\n      ],\n\n      if (isDev) {\n        config.plugins.push(new ForkTSCheckerWebpackPlugin());\n      }\n\n      return next();\n    })\n  },\n};\n```\n\n#### Customize Babel Config\n\nMaleo.js also let you have your own babel config. Just simply add `.babelrc` file at the root directory of your app.\n\nYou can include Maleo.js' babel preset in order to have latest JavaScript preset.\n\nHere's an example of `.babelrc` file:\n```json\n{\n  \"presets\": [\"@airy/maleo/babel\"],\n  \"plugins\": []\n}\n```\n\nThe `@airy/maleo/babel` preset includes everything you need to get your development started. The preset includes:\n- `@babel/preset-env`\n- `@babel/preset-react`\n- `@babel/plugin-proposal-class-properties`\n- `@babel/plugin-proposal-decorators`\n- `@babel/plugin-proposal-object-rest-spread`\n- `@babel/plugin-transform-runtime`\n- `react-loadable/babel`\n\n## Core Utilities Functions\n\n#### Route Matching\n\nTo use you can import the matching routes function like this:\n```js\nimport { getMatchedRoutes } from '@airy/maleo/utils';\n```\n\nThe function will return all matched routes and the route object from `routes.json`\n\n## CDN Support\n\nIf you are using a CDN, you can set up the `publicPath` setting and configure your CDN's origin to resolve to the domain that Maleo.js is hosted on.\n\n```js\n// maleo.config.js\n\nconst isProd = process.env.NODE_ENV === 'production';\n\nmodule.exports = {\n  assetPrefix: isProd && 'https://cdn.example.com';\n}\n```\n\n## FAQ\n\n== TO BE DETERMINED == \n\n## Plugins\n- [css-plugin](https://github.com/airyrooms/maleo-plugins/tree/master/packages/css-plugin)\n- [redux-plugin](https://github.com/airyrooms/maleo-plugins/tree/master/packages/redux-plugin)\n- [typescript-plugin](https://github.com/airyrooms/maleo-plugins/tree/master/packages/typescript-plugin)\n- [more](https://github.com/airyrooms/maleo-plugins)\n\n\n## Contributing\n[Please follow these steps to contribute to Maleo.js](https://github.com/airyrooms/maleo.js/blob/canary/CONTRIBUTING.md)\n\n[Please follow these steps to contribute to Maleo.js' plugins](https://github.com/airyrooms/maleo-plugins#contributing-guidelines)\n\n\n## Contributors\n\nThis project exists thanks to all the people who contribute. [Contribute](CONTRIBUTING.md).\n\nMany thanks to our **[contributors](https://github.com/airyrooms/maleo.js/graphs/contributors)**!\n\n\n## License\n\nMIT","readmeFilename":"README.md","bugs":{"url":"https://github.com/airyrooms/maleo.js/issues"},"homepage":"https://github.com/airyrooms/maleo.js#readme","_id":"@airy/maleo@0.3.1-canary.6","_nodeVersion":"10.15.3","_npmVersion":"lerna/3.13.1/node@v10.15.3+x64 (linux)","dist":{"integrity":"sha512-gjcPpqGOxZlkBfuxNOaSE2lxxi55733pguT8qSFLDLyVjyHO8vJ/jSqdzjXoUiC6ooYft+k60QZMTRtyeetoag==","shasum":"d7e3daf429ddbce6ffd38c3a2d2565bca7d01303","tarball":"https://registry.npmjs.org/@airy/maleo/-/maleo-0.3.1-canary.6.tgz","fileCount":95,"unpackedSize":325622,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJc7S4oCRA9TVsSAnZWagAAanwP/R7C1Sd1AFrhVzeDqIiv\njxE3BfM/3isSrbI1uGSUq9oOrQrYbMGpiD/irMwnz4/ATXn0LEPFxjB26haM\nqKMN300be+tIq/Kl0+AeBtnjafQylE/8AxzLnYCI9VirYdB0BgbKkrINDJAt\nuhL7Ny3bVQNiaJkcfBPSWtxJ5BNqc/Ivw02Dj8yfjzzW6X3M4UfBXwsVWwFD\nObKdvLVbY6X4cjK8i+MVcHbnx5PZGEaTY8rhrOt1Kh1jtHIBX7ny5gY0EQz5\nE2F4e2FyprmhdPWqX6D5Q7VSn9m0seoVOBqrjwIyKxH5v00A+0b58z2srv8l\nHgfYJEYLkaeYNdtWWSlEhz9oeBIrCMQOy8sj9CYe2qk8TA3YEogSBKtcGLYu\nak7uhgIi9PQ+HZwQhIbE5+Y26tESW4VaZYeiOYGw4hpVMJdwjSHuqkf86dkp\nQBdXDmCbD/U2OB8ccy/bqgeZb5j2s+TlnUpm2oDYkZBMzKkleGoVWrhlPvcR\n9W67Aw1M6r3tJFX8JvSXY3eo/hKJIoCK32NtimqC9cJsnhDehVyJyko7V8yj\nHkfW+o4pzc5s8ryGH8GpHPWR3AJMEiYJehmOHum0aYXx8QNW3DAOhpvS4QpF\nuK+zW6Hw8FtsaFyDrOv8t8O35M5w+sNHRoKKNAaHXc1fp+zVIRuEVUN5DAkf\nOpMW\r\n=YwJN\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIHqe08lzuFOT7sR5QjLx753ET7JLPXbWgAyGcKJjyGsNAiBMqVNWVg6qwZqnaoID9270RcdvimTH+DYBgq+dB1owXQ=="}]},"maintainers":[{"name":"airyrooms-engineering","email":"engineering@airyrooms.com"}],"_npmUser":{"name":"airyrooms-engineering","email":"engineering@airyrooms.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/maleo_0.3.1-canary.6_1559047720078_0.957657910016906"},"_hasShrinkwrap":false},"0.3.1-canary.7":{"name":"@airy/maleo","version":"0.3.1-canary.7","description":"Un-opinionated Universal Rendering Javascript Framework","repository":{"type":"git","url":"git+https://github.com/airyrooms/maleo.js.git","bugs":"https://github.com/airyrooms/maleo.js/issues"},"author":{"name":"Airy"},"license":"MIT","private":false,"sideEffects":false,"scripts":{"build":"rm -rf dist && gulp","watch":"gulp watch","prepublish":"yarn build","start":"node server.js"},"bin":{"maleo":"lib/bin/maleo.js"},"dependencies":{"@babel/core":"7.1.2","@babel/plugin-proposal-class-properties":"7.1.0","@babel/plugin-proposal-decorators":"7.1.2","@babel/plugin-proposal-object-rest-spread":"7.0.0","@babel/plugin-syntax-dynamic-import":"7.0.0","@babel/plugin-transform-runtime":"7.1.0","@babel/preset-env":"7.1.0","@babel/preset-react":"7.0.0","@babel/runtime":"7.1.2","@babel/runtime-corejs2":"7.2.0","babel-loader":"8.0.4","case-sensitive-paths-webpack-plugin":"2.1.2","compression":"1.7.4","es6-promise":"4.2.5","eventsource":"1.0.7","express":"4.16.4","figlet":"1.2.1","friendly-errors-webpack-plugin":"1.7.0","hard-source-webpack-plugin":"0.13.1","helmet":"3.14.0","hoist-non-react-statics":"^3.3.0","isomorphic-fetch":"2.2.1","loader-utils":"1.1.0","node-notifier":"5.2.1","nodemon":"1.18.4","react-dom":"16.8.4","react-helmet":"5.2.0","react-loadable":"5.5.0","react-router-config":"5.0.0","react-router-dom":"5.0.0","rimraf":"2.6.3","source-map-support":"0.5.9","terser-webpack-plugin":"1.1.0","webpack":"4.29.0","webpack-bundle-analyzer":"3.0.3","webpack-cli":"3.1.2","webpack-dev-middleware":"3.4.0","webpack-hot-middleware":"2.24.3","webpackbar":"2.6.3"},"peerDependencies":{"react":"^16.8.4"},"devDependencies":{"@babel/preset-typescript":"^7.1.0","@types/webpack-bundle-analyzer":"^2.13.0","babel-plugin-module-resolver":"^3.1.1","gulp":"github:gulpjs/gulp#4.0","gulp-babel":"^8.0.0","gulp-clean":"^0.4.0","gulp-sourcemaps":"^2.6.4","gulp-uglify":"^3.0.1","gulp-watch":"^5.0.1","react":"^16.8.4"},"gitHead":"15f8f3aef64a0613d2fe42b5dcb4b1552e21fd5d","readme":"[![Build Status](https://travis-ci.org/airyrooms/maleo.js.svg?branch=master)](https://travis-ci.org/airyrooms/maleo.js)\n[![Coverage Status](https://coveralls.io/repos/github/airyrooms/maleo.js/badge.svg?branch=master)](https://coveralls.io/github/airyrooms/maleo.js?branch=master)\n[![npm version](https://badge.fury.io/js/%40airy%2Fmaleo.svg)](https://badge.fury.io/js/%40airy%2Fmaleo)\n[![Issues](http://img.shields.io/github/issues/airyrooms/maleo.js.svg)]( https://github.com/airyrooms/maleo.js/issues)\n[![MIT license](https://img.shields.io/badge/License-MIT-blue.svg)](https://github.com/airyrooms/maleo.js/blob/canary/LICENSE)\n[![Discord Chat](https://img.shields.io/discord/550498214789513218.svg)](https://discord.gg/9eArCQn)\n[![Conventional Commits](https://img.shields.io/badge/Conventional%20Commits-1.0.0-green.svg)](https://conventionalcommits.org)\n\n\n# Welcome to Maleo.js\n\nMaleo.js is an un-opinionated framework to enable Universal Rendering in JavaScript using React with no hassle.\n\nWe are here to solve the time consuming setups Universal Rendering Required.\n\n---\n\nReadme below is the documentation for the `canary` (prerelease) branch. To view the documentation for the latest stable Maleo.js version change branch to `master`\n\n---\n\n# Table of Contents\n- [Features](#features)\n- [Setup](#setup)\n- [Component Lifecycle](#component-lifecycle)\n- [Routing](#routing)\n- [Dynamic Import Component](#dynamic-import-component)\n  - [Tips](#tips)\n  - [Preloading](#preloading)\n- [Customizable Component](#customizable-component)\n  - [Custom Document](#custom-document)\n  - [Custom Wrap](#custom-wrap)\n- [Custom Configuration](#custom-configuration)\n  - [Customize Server](#customize-server)\n  - [Customize Webpack](#customize-webpack)\n  - [Customize Babel Config](#customize-babel-config)\n- [Core Utilities Functions](#core-utilities-functions)\n- [CDN Support](#cdn-support)\n- [Plugins](#plugins)\n- [FAQ](#faq)\n- [Contributing](#contributing)\n\n---\n\n## Features\n- Universal Rendering\n- Plugin based framework\n- Customizable\n\n## Setup\n\nInstall Maleo.js \n\n>*for now change `@airy/maleo` to `@airy/maleo@canary` until we publish the stable version of this package*\n\n\n**NPM**\n```bash\n$ npm install --save @airy/maleo react\n```\n**Yarn**\n```bash\n$ yarn add @airy/maleo react\n```\n\nAdd this script to your `package.json`\n```json\n{\n  \"scripts\": {\n    \"dev\": \"maleo dev\",\n    \"build\": \"export NODE_ENV=production && maleo build\",\n    \"start\": \"export NODE_ENV=production && maleo run\"\n  }\n}\n```\n\nCreate a page Root component\n```jsx\n// ./src/Root.jsx\nimport React from 'react';\n\n// Export default is required for registering page\nexport default class RootComponent extends React.Component {\n  render() {\n    return (\n      <h1>Hello World!</h1>\n    )\n  }\n}\n```\n\nAnd lastly, create a routing file on your project root directory called `routes.json` and register your page component\n```json\n[\n  {\n    \"path\": \"/\",\n    \"page\": \"./src/Root\"\n  }\n]\n```\n\nAfter that you can now run `$ npm run dev` and go to `http://localhost:3000`.\n\nYou should now see your app running on your browser.\n\nBy now you should see \n- Automatic transpilation and bundling (with webpack and babel)\n- ~~Hot code reloading~~ [#17](https://github.com/airyrooms/maleo.js/issues/17)\n- Server rendering\n\nTo see how simple this is, check out the sample app!\n\n## Component Lifecycle \n\nMaleo.js added a new component lifecycle hook called `getInitialProps`, this function is called during Server Side Rendering (SSR) and during route changes on Client Side Rendering (CSR).\n\nThis is useful especially for SEO purposes. \n\nExample for stateful component:\n```jsx\nimport React from 'react';\n\nexport default class extends React.Component {\n  static getInitialProps = async (ctx) => {\n    const { req } = ctx;\n\n    // check if getInitialProps is called on server or on client\n    const userAgent = req ? req.headers['user-agent'] : navigator.userAgent;\n\n    // the return value will be passed as props for this component\n    return { userAgent };\n  }\n\n  render() {\n    return (\n      <div>\n        Hello World {this.props.userAgent}\n      </div>\n    );\n  }\n}\n```\n\nExample for stateless component:\n```jsx\nconst Component = (props) => (\n  <div>\n    Hello World {props.userAgent}\n  </div>\n);\n\nComponent.getInitialprops = async (ctx) => {\n  const { req } = ctx;\n\n  const userAgent = req ? req.headers['user-agent'] : navigator.userAgent;\n\n  return { userAgent };\n};\n\nexport default Component;\n```\n\n`getInitialProps` receives a context object with the following properties:\n- `req` - HTTP request object (server only)\n- `res` - HTTP response object (server only)\n- `...wrapProps` - Spreaded properties from custom Wrap\n- `...appProps` - Spreaded properties from custom App\n\n## Routing\n\nRouting is declared in a centralized route config.\nRegister all the route config in `routes.json` file.\n\nIf you put the `routes.json` files on root directory, Maleo will automatically register your route. Otherwise put path to your routes on [Maleo config](#custom-configuration).\n\nRoutes file has to export default the route configuration.\nThe route object **expected to have distinct key** to indicate the route.\n\n<table>\n  <tr>\n    <td>Key</td>\n    <td>Type</td>\n    <td>Description</td>\n  </tr>\n  <tr>\n    <td><code>path</code></td>\n    <td><code>String!</code></td>\n    <td>Routes path</td>\n  </tr>\n  <tr>\n    <td><code>page</code></td>\n    <td><code>String!</code></td>\n    <td>Path to React Component for this route path</td>\n  </tr>\n  <tr>\n    <td><code>exact</code></td>\n    <td><code>Boolean?</code> [<code>false</code>]</td>\n    <td>To make url has to match exactly the path in order to render the component. Give <code>false</code> value if the route is a wrapper route component</td>\n  </tr>\n  <tr>\n    <td><code>routes</code></td>\n    <td><code>RouteObject?</code></td>\n    <td>Nested route</td>\n  </tr>\n</table>\n\n\nFor example:\n```json\n[\n  {\n    \"page\": \"./src/MainApp\",\n    \"routes\": [\n      {\n        \"path\": \"/\",\n        \"page\": \"./src/Search\",\n        \"exact\": true\n      },\n      {\n        \"path\": \"/search\",\n        \"page\": \"./src/Search\",\n        \"routes\": [\n          {\n            \"path\": \"/search/hello\",\n            \"page\": \"./src/Detail\",\n            \"exact\": true\n          }\n        ]\n      },\n      {\n        \"path\": \"/detail\",\n        \"page\": \"./src/Detail\",\n        \"exact\": true\n      }\n    ]\n  }\n]\n```\n\n## Dynamic Import Component\nMaleo.js supports TC39 [dynamic import proposal](https://github.com/tc39/proposal-dynamic-import) for JavaScript.\n\nYou can think dynamic import as another way to split your code into manageable chunks. You can use our `Dynamic` function which utilizes [react loadable](https://github.com/jamiebuilds/react-loadable)\n\nFor Example\n```js\n// DynamicComponent.js\nimport Dynamic from '@airy/maleo/dynamic';\n\nexport default Dynamic({\n  loader: () => import( /* webpackChunkName:\"DynamicComponent\" */ './component/DynamicLoad'),\n})\n```\n\n### **Preloading**\n\nFor optimization purposes, you can also preload a component even before the component got rendered.\n\nFor example, if you want to load component when a button get pressed, you can start preloading the component when the user hovers the mouse over the button.\n\nThe component created by `Dynamic` exposes a [static `preload` method](https://github.com/jamiebuilds/react-loadable#loadablecomponentpreload).\n\n```jsx\nimport React from 'react';\nimport Dynamic from '@airy/maleo/dynamic';\n\nconst DynamicBar = Dynamic({\n  loader: () => import('./Bar'),\n  loading: LoadingCompoinent\n});\n\nclass MyComponent extends React.Component {\n  state = { showBar: false };\n\n  onClick = () => {\n    this.setState({ showBar: true });\n  };\n\n  onMouseOver = () => DynamicBar.preload();\n\n  render() {\n    return (\n      <div>\n        <button\n          onClick={this.onClick}\n          onMouseOver={this.onMouseOver}>\n          Show Bar\n        </button>\n        { this.state.showBar && <DynamicBar /> }\n      </div>\n    )\n  }\n}\n```\n\n## Customizable Component\n\n### Custom Document\n\nHighly inspired by what [Next.js](https://github.com/zeit/next.js) has done on their awesome template customization.\n\nMaleo.js also enable customization on `Document` as document's markup. So you don't need to include tags like `<html>`, `<head>`, `<body>`, etc. \n\nTo override the default behavior, you'll need to create a component that extends the `Document` React class provided by Maleo.\n\n```jsx\n// document.jsx\nimport React from 'react';\nimport { Document, Header, Main, Scripts } from '@airy/maleo/document';\n\nexport default class extends Document {\n  render() {\n    return (\n      <html>\n        <Header>\n          <title>Maleo JS</title>\n          <meta charset=\"utf-8\" />\n          <meta name=\"description\" content=\"Maleo.js is awesome!\" />\n\n          <style>\n            {` body { background-color: #fff } `}\n          </style>\n        </Header>\n\n        <body>\n          <Main />\n\n          <Scripts />\n\n          <ReduxScript />\n        </body>\n      </html>\n    );\n  }\n}\n```\n\n### Custom Wrap\n\nMaleo.js uses the `Wrap` component to initialize pages. `Wrap` contains React Router's Component. You can add HoC here to wrap the application and control the page initialization. Which allows you to do amazing things like:\n- Persisting layour between page changes\n- Keeping state when navigating pages\n- Custom error handling using `componentDidCatch`\n- Inject additional data into pages (like Redux provider, etc)\n\nTo override the default behavior, you'll need to create a component that extends the `Wrap` React class provided by Maleo.\n\n```jsx\n// wrap.jsx\nimport React from 'react';\nimport { Wrap } from '@airy/maleo/wrap';\n\n// Redux plugin for Maleo.js\n// Hoc that creates store and add Redux Provider\nimport { withRedux } from '@airy/maleo-redux-plugin';\n\n// Custom Wrapper that will be rendered for the whole Application\nimport Content from './component/Content';\nimport NavigationBar from './component/NavigationBar';\n\nimport { createStore } from './store';\n\n@withRedux(createStore)\nexport default class extends Wrap {\n  static getInitialProps = (ctx) => {\n    const { store } = ctx\n    // you receive store from context\n    // you can access or do something with the store here\n    console.log('Initialized Redux Store', store);\n    return {}\n  }\n\n  render() {\n    const { Container, containerProps, App, appProps } = this.props\n\n    return (\n      <Container {...containerProps}>\n        <NavigationBar />\n        <Content>\n          <App {...appProps}/>\n        </Content>\n      </Container>\n    )\n  }\n}\n\n```\nIf you put `document.jsx` and `wrap.jsx` on root directory (the same level with `package.json`), then Maleo will automatically register your custom Document and Wrap. Otherwise, you can add the path to your custom Document and Wrap on [Maleo config](#custom-configuration)\n\n---\n***We are also working on adding default and customizable `Error` component page***\n\n---\n\n## Custom Configuration\n\nFor more advanced configuration of Maleo.js, like `webpack` config, registering `plugins`, path to your routes, custom Document and Wrap, and adding `path alias`, you can create a `maleo.config.js` in the root of your project directory. (same directory with `package.json`)\n\n```js\n// maleo.config.js\n\nmodule.exports = {\n  /* config options here */\n}\n```\n\nHere are the API's for the configuration:\n\n<table>\n  <tr>\n    <td>Key</td>\n    <td>Type</td>\n    <td>Description</td>\n  </tr>\n  <tr>\n    <td><code>buildDir</code></td>\n    <td><code>String?</code> [<code>.maleo</code>]</td>\n    <td>Directory to put Maleo.js' build assets</td>\n  </tr>\n  <tr>\n    <td><code>cache</code></td>\n    <td><code>Boolean?</code> [<code>true</code>]</td>\n    <td>Enable webpack build caching</td>\n  </tr>\n  <tr>\n    <td><code>isDev</code></td>\n    <td><code>Boolean?</code> [<code>process.env.NODE_ENV === 'development'</code>]</td>\n    <td>Enable development build configuration</td>\n  </tr>\n  <tr>\n    <td><code>sourceMaps</code></td>\n    <td><code>Boolean?</code> [<code>true</code>]</td>\n    <td>Enable webpack to generate source maps</td>\n  </tr>\n  <tr>\n    <td><code>alias</code></td>\n    <td><code>Object?</code></td>\n    <td>A key value pair for aliasing path directory\n      <br>\n      <code>\n      { 'component': './src/component' }\n      </code>\n    </td>\n  </tr>\n  <tr>\n    <td><code>publicPath</code></td>\n    <td><code>String?</code> [<code>/_assets/</code>]</td>\n    <td>To customize webpack's <code>publicPath</code> \n    <br>Comes in handy if using CDN to put built assets</td>\n  </tr>\n  <tr>\n    <td><code>analyzeBundle</code></td>\n    <td><code>Boolean?</code> [<code>false</code>]</td>\n    <td>To enable webpack's bundle analyzer, for analyzing bundle sizes during bundle debugging should Maleo.js' build process got slow</td>\n  </tr>\n  <tr>\n    <td><code>webpack</code></td>\n    <td><code>Function?</code></td>\n    <td>To customize webpack configuration, more details <a href=\"#customize-webpack\">here</a></td>\n  </tr>\n  <tr>\n    <td><code>routes</code></td>\n    <td><code>string?</code> [<code>rootDir/routes.jsx</code>]</td>\n    <td>Path to your routes file</td>\n  </tr>\n  <tr>\n    <td><code>customDocument</code></td>\n    <td><code>string?</code> [<code>rootDir/document.jsx</code>]</td>\n    <td>Path to your custom document file</td>\n  </tr>\n  <tr>\n    <td><code>customWrap</code></td>\n    <td><code>string?</code> [<code>rootDir/wrap.jsx</code>]</td>\n    <td>Path to your custom wrap file</td>\n  </tr>\n  <tr>\n    <td><code>customApp</code></td>\n    <td><code>string?</code> [<code>rootDir/app.jsx</code>]</td>\n    <td>Path to your custom app file</td>\n  </tr>\n</table>\n\n#### Customize Server\n\nCreate a `server.js` file on root directory where your `package.json` lives.\nHere you can customize Maleo's server.\n```js\nimport { Server } from '@airy/maleo/server';\nimport path from 'path';\n\nimport routeConfig from './routes';\n\nconst PORT = process.env.PORT || 8080;\n\nconst maleoServer = Server.init({\n  port: PORT,\n});\n\nmaleoServer.run(() => {\n  console.log('Server running on port :', PORT);\n});\n```\n\nHere are the API's for the configuration:\n\n<table>\n  <tr>\n    <td>Key</td>\n    <td>Type</td>\n    <td>Description</td>\n  </tr>\n  <tr>\n    <td><code>port</code></td>\n    <td><code>Number?</code> [<code>3000</code>]</td>\n    <td>Port to run Maleo server</td>\n  </tr>\n  <tr>\n    <td><code>assetDir</code></td>\n    <td><code>String?</code> [<code>'<root>/.maleo/client'</code>]</td>\n    <td>Directory for all client related assets</td>\n  </tr>\n</table>\n\n#### Customize Webpack\n\nYou are able to extend Maleo.js' default webpack configuration by defining a function on `maleo.config.js`\n\n```js\n// maleo.config.js\n\nmodule.exports = {\n  webpack(config, context, next) {\n    // Perform customizations to webpack config\n    // Important: This call is required in order to let Maleo pass the config to webpack\n    return next(); \n  },\n};\n```\n\nWebpack function will receive three arguments:\n\n<table>\n  <tr>\n    <td>Argument</td>\n    <td>Details</td>\n  <tr>\n  <tr>\n    <td><code>config</code></td>\n    <td>This contains webpack configuration object that you can manipulate</td>\n  <tr>\n  <tr>\n    <td><code>context</code></td>\n    <td>This contains some keys that are useful for the build context\n      <br>\n      <table>\n        <tr>\n          <td><code>isDev</code></td>\n          <td><code>Boolean</code></td>\n          <td>Check if current build is for development</td>\n        </tr>\n        <tr>\n          <td><code>publicPath</code></td>\n          <td><code>String</code></td>\n          <td>Public Path of user defined or default's value</td>\n        </tr>\n        <tr>\n          <td><code>analyzeBundle</code></td>\n          <td><code>Boolean</code></td>\n          <td>Check if analyze bundle is enabled</td>\n        </tr>\n        <tr>\n          <td><code>buildDirectory</code></td>\n          <td><code>String</code></td>\n          <td>Build Directory of user defined or default's value</td>\n        </tr>\n        <tr>\n          <td><code>name</code></td>\n          <td><code>String</code></td>\n          <td>Build name <code>'server' || 'client'</code></td>\n        </tr>\n      </table>\n    </td>\n  <tr>\n  <tr>\n    <td><code>next</code></td>\n    <td>A callback required to be called to pass the custom configuration</td>\n  <tr>\n</table>\n\nExample of adding `ts-loader` through `maleo.config.js`:\n\n```js\n// maleo.config.js\n\n// Partly taken and modified from @airy/maleo-ts-plugin source\n// for simplicity purposes\n\nmodule.exports = {\n  webpack(config, context, next) {\n    const { isDev } = context\n\n    config.module.rules.push({\n      test: /\\.tsx?/,\n      exclude: /node_modules/,\n      use: [\n        require.resolve('@airy/maleo/lib/build/loaders/maleo-babel-loader'),\n        {\n          loader: 'ts-loader',\n          options: {\n            transpileOnly: true,\n          },\n        },\n      ],\n\n      if (isDev) {\n        config.plugins.push(new ForkTSCheckerWebpackPlugin());\n      }\n\n      return next();\n    })\n  },\n};\n```\n\n#### Customize Babel Config\n\nMaleo.js also let you have your own babel config. Just simply add `.babelrc` file at the root directory of your app.\n\nYou can include Maleo.js' babel preset in order to have latest JavaScript preset.\n\nHere's an example of `.babelrc` file:\n```json\n{\n  \"presets\": [\"@airy/maleo/babel\"],\n  \"plugins\": []\n}\n```\n\nThe `@airy/maleo/babel` preset includes everything you need to get your development started. The preset includes:\n- `@babel/preset-env`\n- `@babel/preset-react`\n- `@babel/plugin-proposal-class-properties`\n- `@babel/plugin-proposal-decorators`\n- `@babel/plugin-proposal-object-rest-spread`\n- `@babel/plugin-transform-runtime`\n- `react-loadable/babel`\n\n## Core Utilities Functions\n\n#### Route Matching\n\nTo use you can import the matching routes function like this:\n```js\nimport { getMatchedRoutes } from '@airy/maleo/utils';\n```\n\nThe function will return all matched routes and the route object from `routes.json`\n\n## CDN Support\n\nIf you are using a CDN, you can set up the `publicPath` setting and configure your CDN's origin to resolve to the domain that Maleo.js is hosted on.\n\n```js\n// maleo.config.js\n\nconst isProd = process.env.NODE_ENV === 'production';\n\nmodule.exports = {\n  assetPrefix: isProd && 'https://cdn.example.com';\n}\n```\n\n## FAQ\n\n== TO BE DETERMINED == \n\n## Plugins\n- [css-plugin](https://github.com/airyrooms/maleo-plugins/tree/master/packages/css-plugin)\n- [redux-plugin](https://github.com/airyrooms/maleo-plugins/tree/master/packages/redux-plugin)\n- [typescript-plugin](https://github.com/airyrooms/maleo-plugins/tree/master/packages/typescript-plugin)\n- [more](https://github.com/airyrooms/maleo-plugins)\n\n\n## Contributing\n[Please follow these steps to contribute to Maleo.js](https://github.com/airyrooms/maleo.js/blob/canary/CONTRIBUTING.md)\n\n[Please follow these steps to contribute to Maleo.js' plugins](https://github.com/airyrooms/maleo-plugins#contributing-guidelines)\n\n\n## Contributors\n\nThis project exists thanks to all the people who contribute. [Contribute](CONTRIBUTING.md).\n\nMany thanks to our **[contributors](https://github.com/airyrooms/maleo.js/graphs/contributors)**!\n\n\n## License\n\nMIT","readmeFilename":"README.md","bugs":{"url":"https://github.com/airyrooms/maleo.js/issues"},"homepage":"https://github.com/airyrooms/maleo.js#readme","_id":"@airy/maleo@0.3.1-canary.7","_nodeVersion":"10.16.0","_npmVersion":"lerna/3.13.1/node@v10.16.0+x64 (linux)","dist":{"integrity":"sha512-Xw5FehFdh2gdzfDMNK7KTBDq68RbZCA+0wyOXUCE6kkKnZFIhoCUV5snd91/8idchfPrHsTM8FTCH/M9moO5aQ==","shasum":"c45b2be1ebd054cf829066c678e73aa151f9f981","tarball":"https://registry.npmjs.org/@airy/maleo/-/maleo-0.3.1-canary.7.tgz","fileCount":96,"unpackedSize":325997,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJc/hN7CRA9TVsSAnZWagAA2H8P/RCm5ZU4knsPPhxLPysI\n0HmZbzbu6bEO2HqHa6jYZzgKnSIdK4N/uIM+oNOzylU/X/e/b51YL+YS3asp\n+7l1SURkkUqAoo7/iai46D7zMCmj4E6t/ReC7xzu8csJAFx7sHoVaMCwPcQ+\nqxHX9TwcmLsxMCrnJ3YcBwnXNX7BSuVwoHFk4MiFKaDWv+ExAsXKBKSSgdlj\nyc8n/8TAugLgThQm/U+X3jE9ClLDQvIeIuosHNfnZ8BqKjPUuItrMV5LDmAy\nz05h+GT7ocPwGfP7Yvwaayc6l9M038NC9guAa6eHAbUK1+3VW1NzK7nl6dvH\nhioiT63EZDFPzkvNXgB/REoMbZuug/Tj2Rof6g9fzsBVJqg1eOxSMmW6eG0v\nbg7DDV8gIo/AjVvyQTiBWwRc38gQJ3Y8kbhg7j8P00OOSZZRqvA9y2Xqwpj3\nuU0FDXipJfcRVoEhWun8FyhIQARBtEwnJx/5+jYZvu3WdqU2JUQXI8GxXPA3\ngbKIJ1CH8e/e+j5IEYKHhg2/RQgYyTHvIgVBlgefeQ7yMIIoFmpIe0CsN1Oz\n+XKF/F87hueFbG+jXfhy5W/wl5wUcBe/0tsNAk9XjlF3CRj7+zOeXcZSvuPx\nphRbdkBDASkDey1ygMiICd5UuHXNJfYJ097RiVlCw7mqjlMYF/PUh4193S8R\n472H\r\n=ZC4I\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCfd61/FjR7pmt9MXMb9oVNEO5jcVM55iR64k1vzVJzDAIgMSVzWo8QN5cfPeUxg05IVc9tz3n13eeCTJ2EwwgNXxo="}]},"maintainers":[{"name":"airyrooms-engineering","email":"engineering@airyrooms.com"}],"_npmUser":{"name":"airyrooms-engineering","email":"engineering@airyrooms.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/maleo_0.3.1-canary.7_1560155003129_0.22502592643499697"},"_hasShrinkwrap":false},"0.3.1-canary.8":{"name":"@airy/maleo","version":"0.3.1-canary.8","description":"Un-opinionated Universal Rendering Javascript Framework","repository":{"type":"git","url":"git+https://github.com/airyrooms/maleo.js.git","bugs":"https://github.com/airyrooms/maleo.js/issues"},"author":{"name":"Airy"},"license":"MIT","private":false,"sideEffects":false,"scripts":{"build":"rm -rf dist && gulp","watch":"gulp watch","prepublish":"yarn build","start":"node server.js"},"bin":{"maleo":"lib/bin/maleo.js"},"dependencies":{"@babel/core":"7.1.2","@babel/plugin-proposal-class-properties":"7.1.0","@babel/plugin-proposal-decorators":"7.1.2","@babel/plugin-proposal-object-rest-spread":"7.0.0","@babel/plugin-syntax-dynamic-import":"7.0.0","@babel/plugin-transform-runtime":"7.1.0","@babel/preset-env":"7.1.0","@babel/preset-react":"7.0.0","@babel/runtime":"7.1.2","@babel/runtime-corejs2":"7.2.0","babel-loader":"8.0.4","case-sensitive-paths-webpack-plugin":"2.1.2","compression":"1.7.4","es6-promise":"4.2.5","eventsource":"1.0.7","express":"4.16.4","figlet":"1.2.1","friendly-errors-webpack-plugin":"1.7.0","hard-source-webpack-plugin":"0.13.1","helmet":"3.14.0","hoist-non-react-statics":"^3.3.0","isomorphic-fetch":"2.2.1","loader-utils":"1.1.0","node-notifier":"5.2.1","nodemon":"1.18.4","react-dom":"16.8.4","react-helmet":"5.2.0","react-loadable":"5.5.0","react-router-config":"5.0.0","react-router-dom":"5.0.0","rimraf":"2.6.3","source-map-support":"0.5.9","terser-webpack-plugin":"1.1.0","webpack":"4.29.0","webpack-bundle-analyzer":"3.0.3","webpack-cli":"3.1.2","webpack-dev-middleware":"3.4.0","webpack-hot-middleware":"2.24.3","webpackbar":"2.6.3"},"peerDependencies":{"react":"^16.8.4"},"devDependencies":{"@babel/preset-typescript":"^7.1.0","@types/webpack-bundle-analyzer":"^2.13.0","babel-plugin-module-resolver":"^3.1.1","gulp":"github:gulpjs/gulp#4.0","gulp-babel":"^8.0.0","gulp-clean":"^0.4.0","gulp-sourcemaps":"^2.6.4","gulp-uglify":"^3.0.1","gulp-watch":"^5.0.1","react":"^16.8.4"},"gitHead":"6c506052919f79704083c468858aaa9d613f0e34","readme":"[![Build Status](https://travis-ci.org/airyrooms/maleo.js.svg?branch=master)](https://travis-ci.org/airyrooms/maleo.js)\n[![Coverage Status](https://coveralls.io/repos/github/airyrooms/maleo.js/badge.svg?branch=master)](https://coveralls.io/github/airyrooms/maleo.js?branch=master)\n[![npm version](https://badge.fury.io/js/%40airy%2Fmaleo.svg)](https://badge.fury.io/js/%40airy%2Fmaleo)\n[![Issues](http://img.shields.io/github/issues/airyrooms/maleo.js.svg)]( https://github.com/airyrooms/maleo.js/issues)\n[![MIT license](https://img.shields.io/badge/License-MIT-blue.svg)](https://github.com/airyrooms/maleo.js/blob/canary/LICENSE)\n[![Discord Chat](https://img.shields.io/discord/550498214789513218.svg)](https://discord.gg/9eArCQn)\n[![Conventional Commits](https://img.shields.io/badge/Conventional%20Commits-1.0.0-green.svg)](https://conventionalcommits.org)\n\n\n# Welcome to Maleo.js\n\nMaleo.js is an un-opinionated framework to enable Universal Rendering in JavaScript using React with no hassle.\n\nWe are here to solve the time consuming setups Universal Rendering Required.\n\n---\n\nReadme below is the documentation for the `canary` (prerelease) branch. To view the documentation for the latest stable Maleo.js version change branch to `master`\n\n---\n\n# Table of Contents\n- [Features](#features)\n- [Setup](#setup)\n- [Component Lifecycle](#component-lifecycle)\n- [Routing](#routing)\n- [Dynamic Import Component](#dynamic-import-component)\n  - [Tips](#tips)\n  - [Preloading](#preloading)\n- [Customizable Component](#customizable-component)\n  - [Custom Document](#custom-document)\n  - [Custom Wrap](#custom-wrap)\n- [Custom Configuration](#custom-configuration)\n  - [Customize Server](#customize-server)\n  - [Customize Webpack](#customize-webpack)\n  - [Customize Babel Config](#customize-babel-config)\n- [Core Utilities Functions](#core-utilities-functions)\n- [CDN Support](#cdn-support)\n- [Plugins](#plugins)\n- [FAQ](#faq)\n- [Contributing](#contributing)\n\n---\n\n## Features\n- Universal Rendering\n- Plugin based framework\n- Customizable\n\n## Setup\n\nInstall Maleo.js \n\n>*for now change `@airy/maleo` to `@airy/maleo@canary` until we publish the stable version of this package*\n\n\n**NPM**\n```bash\n$ npm install --save @airy/maleo react\n```\n**Yarn**\n```bash\n$ yarn add @airy/maleo react\n```\n\nAdd this script to your `package.json`\n```json\n{\n  \"scripts\": {\n    \"dev\": \"maleo dev\",\n    \"build\": \"export NODE_ENV=production && maleo build\",\n    \"start\": \"export NODE_ENV=production && maleo run\"\n  }\n}\n```\n\nCreate a page Root component\n```jsx\n// ./src/Root.jsx\nimport React from 'react';\n\n// Export default is required for registering page\nexport default class RootComponent extends React.Component {\n  render() {\n    return (\n      <h1>Hello World!</h1>\n    )\n  }\n}\n```\n\nAnd lastly, create a routing file on your project root directory called `routes.json` and register your page component\n```json\n[\n  {\n    \"path\": \"/\",\n    \"page\": \"./src/Root\"\n  }\n]\n```\n\nAfter that you can now run `$ npm run dev` and go to `http://localhost:3000`.\n\nYou should now see your app running on your browser.\n\nBy now you should see \n- Automatic transpilation and bundling (with webpack and babel)\n- ~~Hot code reloading~~ [#17](https://github.com/airyrooms/maleo.js/issues/17)\n- Server rendering\n\nTo see how simple this is, check out the sample app!\n\n## Component Lifecycle \n\nMaleo.js added a new component lifecycle hook called `getInitialProps`, this function is called during Server Side Rendering (SSR) and during route changes on Client Side Rendering (CSR).\n\nThis is useful especially for SEO purposes. \n\nExample for stateful component:\n```jsx\nimport React from 'react';\n\nexport default class extends React.Component {\n  static getInitialProps = async (ctx) => {\n    const { req } = ctx;\n\n    // check if getInitialProps is called on server or on client\n    const userAgent = req ? req.headers['user-agent'] : navigator.userAgent;\n\n    // the return value will be passed as props for this component\n    return { userAgent };\n  }\n\n  render() {\n    return (\n      <div>\n        Hello World {this.props.userAgent}\n      </div>\n    );\n  }\n}\n```\n\nExample for stateless component:\n```jsx\nconst Component = (props) => (\n  <div>\n    Hello World {props.userAgent}\n  </div>\n);\n\nComponent.getInitialprops = async (ctx) => {\n  const { req } = ctx;\n\n  const userAgent = req ? req.headers['user-agent'] : navigator.userAgent;\n\n  return { userAgent };\n};\n\nexport default Component;\n```\n\n`getInitialProps` receives a context object with the following properties:\n- `req` - HTTP request object (server only)\n- `res` - HTTP response object (server only)\n- `...wrapProps` - Spreaded properties from custom Wrap\n- `...appProps` - Spreaded properties from custom App\n\n## Routing\n\nRouting is declared in a centralized route config.\nRegister all the route config in `routes.json` file.\n\nIf you put the `routes.json` files on root directory, Maleo will automatically register your route. Otherwise put path to your routes on [Maleo config](#custom-configuration).\n\nRoutes file has to export default the route configuration.\nThe route object **expected to have distinct key** to indicate the route.\n\n<table>\n  <tr>\n    <td>Key</td>\n    <td>Type</td>\n    <td>Description</td>\n  </tr>\n  <tr>\n    <td><code>path</code></td>\n    <td><code>String!</code></td>\n    <td>Routes path</td>\n  </tr>\n  <tr>\n    <td><code>page</code></td>\n    <td><code>String!</code></td>\n    <td>Path to React Component for this route path</td>\n  </tr>\n  <tr>\n    <td><code>exact</code></td>\n    <td><code>Boolean?</code> [<code>false</code>]</td>\n    <td>To make url has to match exactly the path in order to render the component. Give <code>false</code> value if the route is a wrapper route component</td>\n  </tr>\n  <tr>\n    <td><code>routes</code></td>\n    <td><code>RouteObject?</code></td>\n    <td>Nested route</td>\n  </tr>\n</table>\n\n\nFor example:\n```json\n[\n  {\n    \"page\": \"./src/MainApp\",\n    \"routes\": [\n      {\n        \"path\": \"/\",\n        \"page\": \"./src/Search\",\n        \"exact\": true\n      },\n      {\n        \"path\": \"/search\",\n        \"page\": \"./src/Search\",\n        \"routes\": [\n          {\n            \"path\": \"/search/hello\",\n            \"page\": \"./src/Detail\",\n            \"exact\": true\n          }\n        ]\n      },\n      {\n        \"path\": \"/detail\",\n        \"page\": \"./src/Detail\",\n        \"exact\": true\n      }\n    ]\n  }\n]\n```\n\n## Dynamic Import Component\nMaleo.js supports TC39 [dynamic import proposal](https://github.com/tc39/proposal-dynamic-import) for JavaScript.\n\nYou can think dynamic import as another way to split your code into manageable chunks. You can use our `Dynamic` function which utilizes [react loadable](https://github.com/jamiebuilds/react-loadable)\n\nFor Example\n```js\n// DynamicComponent.js\nimport Dynamic from '@airy/maleo/dynamic';\n\nexport default Dynamic({\n  loader: () => import( /* webpackChunkName:\"DynamicComponent\" */ './component/DynamicLoad'),\n})\n```\n\n### **Preloading**\n\nFor optimization purposes, you can also preload a component even before the component got rendered.\n\nFor example, if you want to load component when a button get pressed, you can start preloading the component when the user hovers the mouse over the button.\n\nThe component created by `Dynamic` exposes a [static `preload` method](https://github.com/jamiebuilds/react-loadable#loadablecomponentpreload).\n\n```jsx\nimport React from 'react';\nimport Dynamic from '@airy/maleo/dynamic';\n\nconst DynamicBar = Dynamic({\n  loader: () => import('./Bar'),\n  loading: LoadingCompoinent\n});\n\nclass MyComponent extends React.Component {\n  state = { showBar: false };\n\n  onClick = () => {\n    this.setState({ showBar: true });\n  };\n\n  onMouseOver = () => DynamicBar.preload();\n\n  render() {\n    return (\n      <div>\n        <button\n          onClick={this.onClick}\n          onMouseOver={this.onMouseOver}>\n          Show Bar\n        </button>\n        { this.state.showBar && <DynamicBar /> }\n      </div>\n    )\n  }\n}\n```\n\n## Customizable Component\n\n### Custom Document\n\nHighly inspired by what [Next.js](https://github.com/zeit/next.js) has done on their awesome template customization.\n\nMaleo.js also enable customization on `Document` as document's markup. So you don't need to include tags like `<html>`, `<head>`, `<body>`, etc. \n\nTo override the default behavior, you'll need to create a component that extends the `Document` React class provided by Maleo.\n\n```jsx\n// document.jsx\nimport React from 'react';\nimport { Document, Header, Main, Scripts } from '@airy/maleo/document';\n\nexport default class extends Document {\n  render() {\n    return (\n      <html>\n        <Header>\n          <title>Maleo JS</title>\n          <meta charset=\"utf-8\" />\n          <meta name=\"description\" content=\"Maleo.js is awesome!\" />\n\n          <style>\n            {` body { background-color: #fff } `}\n          </style>\n        </Header>\n\n        <body>\n          <Main />\n\n          <Scripts />\n\n          <ReduxScript />\n        </body>\n      </html>\n    );\n  }\n}\n```\n\n### Custom Wrap\n\nMaleo.js uses the `Wrap` component to initialize pages. `Wrap` contains React Router's Component. You can add HoC here to wrap the application and control the page initialization. Which allows you to do amazing things like:\n- Persisting layour between page changes\n- Keeping state when navigating pages\n- Custom error handling using `componentDidCatch`\n- Inject additional data into pages (like Redux provider, etc)\n\nTo override the default behavior, you'll need to create a component that extends the `Wrap` React class provided by Maleo.\n\n```jsx\n// wrap.jsx\nimport React from 'react';\nimport { Wrap } from '@airy/maleo/wrap';\n\n// Redux plugin for Maleo.js\n// Hoc that creates store and add Redux Provider\nimport { withRedux } from '@airy/maleo-redux-plugin';\n\n// Custom Wrapper that will be rendered for the whole Application\nimport Content from './component/Content';\nimport NavigationBar from './component/NavigationBar';\n\nimport { createStore } from './store';\n\n@withRedux(createStore)\nexport default class extends Wrap {\n  static getInitialProps = (ctx) => {\n    const { store } = ctx\n    // you receive store from context\n    // you can access or do something with the store here\n    console.log('Initialized Redux Store', store);\n    return {}\n  }\n\n  render() {\n    const { Container, containerProps, App, appProps } = this.props\n\n    return (\n      <Container {...containerProps}>\n        <NavigationBar />\n        <Content>\n          <App {...appProps}/>\n        </Content>\n      </Container>\n    )\n  }\n}\n\n```\nIf you put `document.jsx` and `wrap.jsx` on root directory (the same level with `package.json`), then Maleo will automatically register your custom Document and Wrap. Otherwise, you can add the path to your custom Document and Wrap on [Maleo config](#custom-configuration)\n\n---\n***We are also working on adding default and customizable `Error` component page***\n\n---\n\n## Custom Configuration\n\nFor more advanced configuration of Maleo.js, like `webpack` config, registering `plugins`, path to your routes, custom Document and Wrap, and adding `path alias`, you can create a `maleo.config.js` in the root of your project directory. (same directory with `package.json`)\n\n```js\n// maleo.config.js\n\nmodule.exports = {\n  /* config options here */\n}\n```\n\nHere are the API's for the configuration:\n\n<table>\n  <tr>\n    <td>Key</td>\n    <td>Type</td>\n    <td>Description</td>\n  </tr>\n  <tr>\n    <td><code>buildDir</code></td>\n    <td><code>String?</code> [<code>.maleo</code>]</td>\n    <td>Directory to put Maleo.js' build assets</td>\n  </tr>\n  <tr>\n    <td><code>cache</code></td>\n    <td><code>Boolean?</code> [<code>true</code>]</td>\n    <td>Enable webpack build caching</td>\n  </tr>\n  <tr>\n    <td><code>isDev</code></td>\n    <td><code>Boolean?</code> [<code>process.env.NODE_ENV === 'development'</code>]</td>\n    <td>Enable development build configuration</td>\n  </tr>\n  <tr>\n    <td><code>sourceMaps</code></td>\n    <td><code>Boolean?</code> [<code>true</code>]</td>\n    <td>Enable webpack to generate source maps</td>\n  </tr>\n  <tr>\n    <td><code>alias</code></td>\n    <td><code>Object?</code></td>\n    <td>A key value pair for aliasing path directory\n      <br>\n      <code>\n      { 'component': './src/component' }\n      </code>\n    </td>\n  </tr>\n  <tr>\n    <td><code>publicPath</code></td>\n    <td><code>String?</code> [<code>/_assets/</code>]</td>\n    <td>To customize webpack's <code>publicPath</code> \n    <br>Comes in handy if using CDN to put built assets</td>\n  </tr>\n  <tr>\n    <td><code>analyzeBundle</code></td>\n    <td><code>Boolean?</code> [<code>false</code>]</td>\n    <td>To enable webpack's bundle analyzer, for analyzing bundle sizes during bundle debugging should Maleo.js' build process got slow</td>\n  </tr>\n  <tr>\n    <td><code>webpack</code></td>\n    <td><code>Function?</code></td>\n    <td>To customize webpack configuration, more details <a href=\"#customize-webpack\">here</a></td>\n  </tr>\n  <tr>\n    <td><code>routes</code></td>\n    <td><code>string?</code> [<code>rootDir/routes.jsx</code>]</td>\n    <td>Path to your routes file</td>\n  </tr>\n  <tr>\n    <td><code>customDocument</code></td>\n    <td><code>string?</code> [<code>rootDir/document.jsx</code>]</td>\n    <td>Path to your custom document file</td>\n  </tr>\n  <tr>\n    <td><code>customWrap</code></td>\n    <td><code>string?</code> [<code>rootDir/wrap.jsx</code>]</td>\n    <td>Path to your custom wrap file</td>\n  </tr>\n  <tr>\n    <td><code>customApp</code></td>\n    <td><code>string?</code> [<code>rootDir/app.jsx</code>]</td>\n    <td>Path to your custom app file</td>\n  </tr>\n</table>\n\n#### Customize Server\n\nCreate a `server.js` file on root directory where your `package.json` lives.\nHere you can customize Maleo's server.\n```js\nimport { Server } from '@airy/maleo/server';\nimport path from 'path';\n\nimport routeConfig from './routes';\n\nconst PORT = process.env.PORT || 8080;\n\nconst maleoServer = Server.init({\n  port: PORT,\n});\n\nmaleoServer.run(() => {\n  console.log('Server running on port :', PORT);\n});\n```\n\nHere are the API's for the configuration:\n\n<table>\n  <tr>\n    <td>Key</td>\n    <td>Type</td>\n    <td>Description</td>\n  </tr>\n  <tr>\n    <td><code>port</code></td>\n    <td><code>Number?</code> [<code>3000</code>]</td>\n    <td>Port to run Maleo server</td>\n  </tr>\n  <tr>\n    <td><code>assetDir</code></td>\n    <td><code>String?</code> [<code>'<root>/.maleo/client'</code>]</td>\n    <td>Directory for all client related assets</td>\n  </tr>\n</table>\n\n#### Customize Webpack\n\nYou are able to extend Maleo.js' default webpack configuration by defining a function on `maleo.config.js`\n\n```js\n// maleo.config.js\n\nmodule.exports = {\n  webpack(config, context, next) {\n    // Perform customizations to webpack config\n    // Important: This call is required in order to let Maleo pass the config to webpack\n    return next(); \n  },\n};\n```\n\nWebpack function will receive three arguments:\n\n<table>\n  <tr>\n    <td>Argument</td>\n    <td>Details</td>\n  <tr>\n  <tr>\n    <td><code>config</code></td>\n    <td>This contains webpack configuration object that you can manipulate</td>\n  <tr>\n  <tr>\n    <td><code>context</code></td>\n    <td>This contains some keys that are useful for the build context\n      <br>\n      <table>\n        <tr>\n          <td><code>isDev</code></td>\n          <td><code>Boolean</code></td>\n          <td>Check if current build is for development</td>\n        </tr>\n        <tr>\n          <td><code>publicPath</code></td>\n          <td><code>String</code></td>\n          <td>Public Path of user defined or default's value</td>\n        </tr>\n        <tr>\n          <td><code>analyzeBundle</code></td>\n          <td><code>Boolean</code></td>\n          <td>Check if analyze bundle is enabled</td>\n        </tr>\n        <tr>\n          <td><code>buildDirectory</code></td>\n          <td><code>String</code></td>\n          <td>Build Directory of user defined or default's value</td>\n        </tr>\n        <tr>\n          <td><code>name</code></td>\n          <td><code>String</code></td>\n          <td>Build name <code>'server' || 'client'</code></td>\n        </tr>\n      </table>\n    </td>\n  <tr>\n  <tr>\n    <td><code>next</code></td>\n    <td>A callback required to be called to pass the custom configuration</td>\n  <tr>\n</table>\n\nExample of adding `ts-loader` through `maleo.config.js`:\n\n```js\n// maleo.config.js\n\n// Partly taken and modified from @airy/maleo-ts-plugin source\n// for simplicity purposes\n\nmodule.exports = {\n  webpack(config, context, next) {\n    const { isDev } = context\n\n    config.module.rules.push({\n      test: /\\.tsx?/,\n      exclude: /node_modules/,\n      use: [\n        require.resolve('@airy/maleo/lib/build/loaders/maleo-babel-loader'),\n        {\n          loader: 'ts-loader',\n          options: {\n            transpileOnly: true,\n          },\n        },\n      ],\n\n      if (isDev) {\n        config.plugins.push(new ForkTSCheckerWebpackPlugin());\n      }\n\n      return next();\n    })\n  },\n};\n```\n\n#### Customize Babel Config\n\nMaleo.js also let you have your own babel config. Just simply add `.babelrc` file at the root directory of your app.\n\nYou can include Maleo.js' babel preset in order to have latest JavaScript preset.\n\nHere's an example of `.babelrc` file:\n```json\n{\n  \"presets\": [\"@airy/maleo/babel\"],\n  \"plugins\": []\n}\n```\n\nThe `@airy/maleo/babel` preset includes everything you need to get your development started. The preset includes:\n- `@babel/preset-env`\n- `@babel/preset-react`\n- `@babel/plugin-proposal-class-properties`\n- `@babel/plugin-proposal-decorators`\n- `@babel/plugin-proposal-object-rest-spread`\n- `@babel/plugin-transform-runtime`\n- `react-loadable/babel`\n\n## Core Utilities Functions\n\n#### Route Matching\n\nTo use you can import the matching routes function like this:\n```js\nimport { getMatchedRoutes } from '@airy/maleo/utils';\n```\n\nThe function will return all matched routes and the route object from `routes.json`\n\n## CDN Support\n\nIf you are using a CDN, you can set up the `publicPath` setting and configure your CDN's origin to resolve to the domain that Maleo.js is hosted on.\n\n```js\n// maleo.config.js\n\nconst isProd = process.env.NODE_ENV === 'production';\n\nmodule.exports = {\n  assetPrefix: isProd && 'https://cdn.example.com';\n}\n```\n\n## FAQ\n\n== TO BE DETERMINED == \n\n## Plugins\n- [css-plugin](https://github.com/airyrooms/maleo-plugins/tree/master/packages/css-plugin)\n- [redux-plugin](https://github.com/airyrooms/maleo-plugins/tree/master/packages/redux-plugin)\n- [typescript-plugin](https://github.com/airyrooms/maleo-plugins/tree/master/packages/typescript-plugin)\n- [more](https://github.com/airyrooms/maleo-plugins)\n\n\n## Contributing\n[Please follow these steps to contribute to Maleo.js](https://github.com/airyrooms/maleo.js/blob/canary/CONTRIBUTING.md)\n\n[Please follow these steps to contribute to Maleo.js' plugins](https://github.com/airyrooms/maleo-plugins#contributing-guidelines)\n\n\n## Contributors\n\nThis project exists thanks to all the people who contribute. [Contribute](CONTRIBUTING.md).\n\nMany thanks to our **[contributors](https://github.com/airyrooms/maleo.js/graphs/contributors)**!\n\n\n## License\n\nMIT","readmeFilename":"README.md","bugs":{"url":"https://github.com/airyrooms/maleo.js/issues"},"homepage":"https://github.com/airyrooms/maleo.js#readme","_id":"@airy/maleo@0.3.1-canary.8","_nodeVersion":"11.15.0","_npmVersion":"lerna/3.13.1/node@v11.15.0+x64 (linux)","dist":{"integrity":"sha512-EuhTJSn8Fg7hCciYHxVhgbFxEZIA8oyrxzvAvnka3+gkyKnp8+UtXyzRK1by/phpBdwYdTVcOCVPzHWaXf5V1Q==","shasum":"e06b8f53b6cf084f9630853bf760e81c988266b3","tarball":"https://registry.npmjs.org/@airy/maleo/-/maleo-0.3.1-canary.8.tgz","fileCount":96,"unpackedSize":326644,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJc/3WLCRA9TVsSAnZWagAAWegP/iq0AaXTvp8EWt8uDsdm\ndtRoTlJ9GwFgc3BEOUgi60WAuf9LSAYdCXyvn+Drm5lVToHecdBZWrR00dJp\nVpGIZXTWnwcF3Sybr91PfA92rPBfwzfHkf70WbyI39dhLyObc0GcCtb28+oL\n41Vs+/jmHxOdQxfzMO85l4Jbc3udNPGtkcAS+ajWUbobEdKSTamvQb/EAA3L\nZyKI23RVcRUXNYNzfRg4J7SqRzEvoyTg1trTl7zEOcSdApZBA94JWkc5m8pr\nei70w6vRTY07oWltjaR5mkTsaY74S9teiMET23aSaZzSghpmapQKUNK+TcnF\npbBg3BV1J9moc9w9MIZI3Mv1Bw8Qk3j9ixf6B+BlMfgUN34ylO0U18mXPbLb\n+fYPsqkOm/+CAea5VYgb43PqSZQkgeOMzKMws9dMCHz2ON643tL7flN3GK0D\n5xFYCSbLQXJYcN6DMXURlfCM7+AH3u7cjXrbkSklV8UWkuHaDAu5Zw0MCCxl\nPD615zwRXR8HnhoSC386+4nbV/9KK0qykXc0gB74TbSJ1FfOu78L3FSVc2MZ\nWQU7r7in7IqAzMrfW1rDpGRFH//2i251FWis6NhC4GNWIEehagU2lFad7bKI\njTJpVMb/24T4/e18yqn01ATpMjpFUvbXUWpgB67ubb7wzUgiFi8JDep1RuhC\nkKji\r\n=UbOW\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCSWiL5CbbVHS/Ho6Gx7+/GdySDduYTbcpm3jiWWV+CMQIhANisqrf/FOCVXHOeVvuJ+So+hOXChOdPR2gZNHhup1B5"}]},"maintainers":[{"name":"airyrooms-engineering","email":"engineering@airyrooms.com"}],"_npmUser":{"name":"airyrooms-engineering","email":"engineering@airyrooms.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/maleo_0.3.1-canary.8_1560245643073_0.5140989783625007"},"_hasShrinkwrap":false},"0.3.1-canary.9":{"name":"@airy/maleo","version":"0.3.1-canary.9","description":"Un-opinionated Universal Rendering Javascript Framework","repository":{"type":"git","url":"git+https://github.com/airyrooms/maleo.js.git","bugs":"https://github.com/airyrooms/maleo.js/issues"},"author":{"name":"Airy"},"license":"MIT","private":false,"sideEffects":false,"scripts":{"build":"rm -rf dist && gulp","watch":"gulp watch","prepublish":"yarn build","start":"node server.js"},"bin":{"maleo":"lib/bin/maleo.js"},"dependencies":{"@babel/core":"7.1.2","@babel/plugin-proposal-class-properties":"7.1.0","@babel/plugin-proposal-decorators":"7.1.2","@babel/plugin-proposal-object-rest-spread":"7.0.0","@babel/plugin-syntax-dynamic-import":"7.0.0","@babel/plugin-transform-runtime":"7.1.0","@babel/preset-env":"7.1.0","@babel/preset-react":"7.0.0","@babel/runtime":"7.1.2","@babel/runtime-corejs2":"7.2.0","babel-loader":"8.0.4","case-sensitive-paths-webpack-plugin":"2.1.2","compression":"1.7.4","es6-promise":"4.2.5","eventsource":"1.0.7","express":"4.16.4","figlet":"1.2.1","friendly-errors-webpack-plugin":"1.7.0","hard-source-webpack-plugin":"0.13.1","helmet":"3.14.0","hoist-non-react-statics":"^3.3.0","isomorphic-fetch":"2.2.1","loader-utils":"1.1.0","node-notifier":"5.2.1","nodemon":"1.18.4","react-dom":"16.8.4","react-helmet":"5.2.0","react-loadable":"5.5.0","react-router-config":"5.0.0","react-router-dom":"5.0.0","rimraf":"2.6.3","source-map-support":"0.5.9","terser-webpack-plugin":"1.1.0","webpack":"4.29.0","webpack-bundle-analyzer":"3.0.3","webpack-cli":"3.1.2","webpack-dev-middleware":"3.4.0","webpack-hot-middleware":"2.24.3","webpackbar":"2.6.3"},"peerDependencies":{"react":"^16.8.4"},"devDependencies":{"@babel/preset-typescript":"^7.1.0","@types/webpack-bundle-analyzer":"^2.13.0","babel-plugin-module-resolver":"^3.1.1","gulp":"github:gulpjs/gulp#4.0","gulp-babel":"^8.0.0","gulp-clean":"^0.4.0","gulp-sourcemaps":"^2.6.4","gulp-uglify":"^3.0.1","gulp-watch":"^5.0.1","react":"^16.8.4"},"gitHead":"5b82fef637c55e91b03ff7c5ab585239f3574560","readme":"[![Build Status](https://travis-ci.org/airyrooms/maleo.js.svg?branch=master)](https://travis-ci.org/airyrooms/maleo.js)\n[![Coverage Status](https://coveralls.io/repos/github/airyrooms/maleo.js/badge.svg?branch=master)](https://coveralls.io/github/airyrooms/maleo.js?branch=master)\n[![npm version](https://badge.fury.io/js/%40airy%2Fmaleo.svg)](https://badge.fury.io/js/%40airy%2Fmaleo)\n[![Issues](http://img.shields.io/github/issues/airyrooms/maleo.js.svg)]( https://github.com/airyrooms/maleo.js/issues)\n[![MIT license](https://img.shields.io/badge/License-MIT-blue.svg)](https://github.com/airyrooms/maleo.js/blob/canary/LICENSE)\n[![Discord Chat](https://img.shields.io/discord/550498214789513218.svg)](https://discord.gg/9eArCQn)\n[![Conventional Commits](https://img.shields.io/badge/Conventional%20Commits-1.0.0-green.svg)](https://conventionalcommits.org)\n\n\n# Welcome to Maleo.js\n\nMaleo.js is an un-opinionated framework to enable Universal Rendering in JavaScript using React with no hassle.\n\nWe are here to solve the time consuming setups Universal Rendering Required.\n\n---\n\nReadme below is the documentation for the `canary` (prerelease) branch. To view the documentation for the latest stable Maleo.js version change branch to `master`\n\n---\n\n# Table of Contents\n- [Features](#features)\n- [Setup](#setup)\n- [Component Lifecycle](#component-lifecycle)\n- [Routing](#routing)\n- [Dynamic Import Component](#dynamic-import-component)\n  - [Tips](#tips)\n  - [Preloading](#preloading)\n- [Customizable Component](#customizable-component)\n  - [Custom Document](#custom-document)\n  - [Custom Wrap](#custom-wrap)\n- [Custom Configuration](#custom-configuration)\n  - [Customize Server](#customize-server)\n  - [Customize Webpack](#customize-webpack)\n  - [Customize Babel Config](#customize-babel-config)\n- [Core Utilities Functions](#core-utilities-functions)\n- [CDN Support](#cdn-support)\n- [Plugins](#plugins)\n- [FAQ](#faq)\n- [Contributing](#contributing)\n\n---\n\n## Features\n- Universal Rendering\n- Plugin based framework\n- Customizable\n\n## Setup\n\nInstall Maleo.js \n\n>*for now change `@airy/maleo` to `@airy/maleo@canary` until we publish the stable version of this package*\n\n\n**NPM**\n```bash\n$ npm install --save @airy/maleo react\n```\n**Yarn**\n```bash\n$ yarn add @airy/maleo react\n```\n\nAdd this script to your `package.json`\n```json\n{\n  \"scripts\": {\n    \"dev\": \"maleo dev\",\n    \"build\": \"export NODE_ENV=production && maleo build\",\n    \"start\": \"export NODE_ENV=production && maleo run\"\n  }\n}\n```\n\nCreate a page Root component\n```jsx\n// ./src/Root.jsx\nimport React from 'react';\n\n// Export default is required for registering page\nexport default class RootComponent extends React.Component {\n  render() {\n    return (\n      <h1>Hello World!</h1>\n    )\n  }\n}\n```\n\nAnd lastly, create a routing file on your project root directory called `routes.json` and register your page component\n```json\n[\n  {\n    \"path\": \"/\",\n    \"page\": \"./src/Root\"\n  }\n]\n```\n\nAfter that you can now run `$ npm run dev` and go to `http://localhost:3000`.\n\nYou should now see your app running on your browser.\n\nBy now you should see \n- Automatic transpilation and bundling (with webpack and babel)\n- ~~Hot code reloading~~ [#17](https://github.com/airyrooms/maleo.js/issues/17)\n- Server rendering\n\nTo see how simple this is, check out the sample app!\n\n## Component Lifecycle \n\nMaleo.js added a new component lifecycle hook called `getInitialProps`, this function is called during Server Side Rendering (SSR) and during route changes on Client Side Rendering (CSR).\n\nThis is useful especially for SEO purposes. \n\nExample for stateful component:\n```jsx\nimport React from 'react';\n\nexport default class extends React.Component {\n  static getInitialProps = async (ctx) => {\n    const { req } = ctx;\n\n    // check if getInitialProps is called on server or on client\n    const userAgent = req ? req.headers['user-agent'] : navigator.userAgent;\n\n    // the return value will be passed as props for this component\n    return { userAgent };\n  }\n\n  render() {\n    return (\n      <div>\n        Hello World {this.props.userAgent}\n      </div>\n    );\n  }\n}\n```\n\nExample for stateless component:\n```jsx\nconst Component = (props) => (\n  <div>\n    Hello World {props.userAgent}\n  </div>\n);\n\nComponent.getInitialprops = async (ctx) => {\n  const { req } = ctx;\n\n  const userAgent = req ? req.headers['user-agent'] : navigator.userAgent;\n\n  return { userAgent };\n};\n\nexport default Component;\n```\n\n`getInitialProps` receives a context object with the following properties:\n- `req` - HTTP request object (server only)\n- `res` - HTTP response object (server only)\n- `...wrapProps` - Spreaded properties from custom Wrap\n- `...appProps` - Spreaded properties from custom App\n\n## Routing\n\nRouting is declared in a centralized route config.\nRegister all the route config in `routes.json` file.\n\nIf you put the `routes.json` files on root directory, Maleo will automatically register your route. Otherwise put path to your routes on [Maleo config](#custom-configuration).\n\nRoutes file has to export default the route configuration.\nThe route object **expected to have distinct key** to indicate the route.\n\n<table>\n  <tr>\n    <td>Key</td>\n    <td>Type</td>\n    <td>Description</td>\n  </tr>\n  <tr>\n    <td><code>path</code></td>\n    <td><code>String!</code></td>\n    <td>Routes path</td>\n  </tr>\n  <tr>\n    <td><code>page</code></td>\n    <td><code>String!</code></td>\n    <td>Path to React Component for this route path</td>\n  </tr>\n  <tr>\n    <td><code>exact</code></td>\n    <td><code>Boolean?</code> [<code>false</code>]</td>\n    <td>To make url has to match exactly the path in order to render the component. Give <code>false</code> value if the route is a wrapper route component</td>\n  </tr>\n  <tr>\n    <td><code>routes</code></td>\n    <td><code>RouteObject?</code></td>\n    <td>Nested route</td>\n  </tr>\n</table>\n\n\nFor example:\n```json\n[\n  {\n    \"page\": \"./src/MainApp\",\n    \"routes\": [\n      {\n        \"path\": \"/\",\n        \"page\": \"./src/Search\",\n        \"exact\": true\n      },\n      {\n        \"path\": \"/search\",\n        \"page\": \"./src/Search\",\n        \"routes\": [\n          {\n            \"path\": \"/search/hello\",\n            \"page\": \"./src/Detail\",\n            \"exact\": true\n          }\n        ]\n      },\n      {\n        \"path\": \"/detail\",\n        \"page\": \"./src/Detail\",\n        \"exact\": true\n      }\n    ]\n  }\n]\n```\n\n## Dynamic Import Component\nMaleo.js supports TC39 [dynamic import proposal](https://github.com/tc39/proposal-dynamic-import) for JavaScript.\n\nYou can think dynamic import as another way to split your code into manageable chunks. You can use our `Dynamic` function which utilizes [react loadable](https://github.com/jamiebuilds/react-loadable)\n\nFor Example\n```js\n// DynamicComponent.js\nimport Dynamic from '@airy/maleo/dynamic';\n\nexport default Dynamic({\n  loader: () => import( /* webpackChunkName:\"DynamicComponent\" */ './component/DynamicLoad'),\n})\n```\n\n### **Preloading**\n\nFor optimization purposes, you can also preload a component even before the component got rendered.\n\nFor example, if you want to load component when a button get pressed, you can start preloading the component when the user hovers the mouse over the button.\n\nThe component created by `Dynamic` exposes a [static `preload` method](https://github.com/jamiebuilds/react-loadable#loadablecomponentpreload).\n\n```jsx\nimport React from 'react';\nimport Dynamic from '@airy/maleo/dynamic';\n\nconst DynamicBar = Dynamic({\n  loader: () => import('./Bar'),\n  loading: LoadingCompoinent\n});\n\nclass MyComponent extends React.Component {\n  state = { showBar: false };\n\n  onClick = () => {\n    this.setState({ showBar: true });\n  };\n\n  onMouseOver = () => DynamicBar.preload();\n\n  render() {\n    return (\n      <div>\n        <button\n          onClick={this.onClick}\n          onMouseOver={this.onMouseOver}>\n          Show Bar\n        </button>\n        { this.state.showBar && <DynamicBar /> }\n      </div>\n    )\n  }\n}\n```\n\n## Customizable Component\n\n### Custom Document\n\nHighly inspired by what [Next.js](https://github.com/zeit/next.js) has done on their awesome template customization.\n\nMaleo.js also enable customization on `Document` as document's markup. So you don't need to include tags like `<html>`, `<head>`, `<body>`, etc. \n\nTo override the default behavior, you'll need to create a component that extends the `Document` React class provided by Maleo.\n\n```jsx\n// document.jsx\nimport React from 'react';\nimport { Document, Header, Main, Scripts } from '@airy/maleo/document';\n\nexport default class extends Document {\n  render() {\n    return (\n      <html>\n        <Header>\n          <title>Maleo JS</title>\n          <meta charset=\"utf-8\" />\n          <meta name=\"description\" content=\"Maleo.js is awesome!\" />\n\n          <style>\n            {` body { background-color: #fff } `}\n          </style>\n        </Header>\n\n        <body>\n          <Main />\n\n          <Scripts />\n\n          <ReduxScript />\n        </body>\n      </html>\n    );\n  }\n}\n```\n\n### Custom Wrap\n\nMaleo.js uses the `Wrap` component to initialize pages. `Wrap` contains React Router's Component. You can add HoC here to wrap the application and control the page initialization. Which allows you to do amazing things like:\n- Persisting layour between page changes\n- Keeping state when navigating pages\n- Custom error handling using `componentDidCatch`\n- Inject additional data into pages (like Redux provider, etc)\n\nTo override the default behavior, you'll need to create a component that extends the `Wrap` React class provided by Maleo.\n\n```jsx\n// wrap.jsx\nimport React from 'react';\nimport { Wrap } from '@airy/maleo/wrap';\n\n// Redux plugin for Maleo.js\n// Hoc that creates store and add Redux Provider\nimport { withRedux } from '@airy/maleo-redux-plugin';\n\n// Custom Wrapper that will be rendered for the whole Application\nimport Content from './component/Content';\nimport NavigationBar from './component/NavigationBar';\n\nimport { createStore } from './store';\n\n@withRedux(createStore)\nexport default class extends Wrap {\n  static getInitialProps = (ctx) => {\n    const { store } = ctx\n    // you receive store from context\n    // you can access or do something with the store here\n    console.log('Initialized Redux Store', store);\n    return {}\n  }\n\n  render() {\n    const { Container, containerProps, App, appProps } = this.props\n\n    return (\n      <Container {...containerProps}>\n        <NavigationBar />\n        <Content>\n          <App {...appProps}/>\n        </Content>\n      </Container>\n    )\n  }\n}\n\n```\nIf you put `document.jsx` and `wrap.jsx` on root directory (the same level with `package.json`), then Maleo will automatically register your custom Document and Wrap. Otherwise, you can add the path to your custom Document and Wrap on [Maleo config](#custom-configuration)\n\n---\n***We are also working on adding default and customizable `Error` component page***\n\n---\n\n## Custom Configuration\n\nFor more advanced configuration of Maleo.js, like `webpack` config, registering `plugins`, path to your routes, custom Document and Wrap, and adding `path alias`, you can create a `maleo.config.js` in the root of your project directory. (same directory with `package.json`)\n\n```js\n// maleo.config.js\n\nmodule.exports = {\n  /* config options here */\n}\n```\n\nHere are the API's for the configuration:\n\n<table>\n  <tr>\n    <td>Key</td>\n    <td>Type</td>\n    <td>Description</td>\n  </tr>\n  <tr>\n    <td><code>buildDir</code></td>\n    <td><code>String?</code> [<code>.maleo</code>]</td>\n    <td>Directory to put Maleo.js' build assets</td>\n  </tr>\n  <tr>\n    <td><code>cache</code></td>\n    <td><code>Boolean?</code> [<code>true</code>]</td>\n    <td>Enable webpack build caching</td>\n  </tr>\n  <tr>\n    <td><code>isDev</code></td>\n    <td><code>Boolean?</code> [<code>process.env.NODE_ENV === 'development'</code>]</td>\n    <td>Enable development build configuration</td>\n  </tr>\n  <tr>\n    <td><code>sourceMaps</code></td>\n    <td><code>Boolean?</code> [<code>true</code>]</td>\n    <td>Enable webpack to generate source maps</td>\n  </tr>\n  <tr>\n    <td><code>alias</code></td>\n    <td><code>Object?</code></td>\n    <td>A key value pair for aliasing path directory\n      <br>\n      <code>\n      { 'component': './src/component' }\n      </code>\n    </td>\n  </tr>\n  <tr>\n    <td><code>publicPath</code></td>\n    <td><code>String?</code> [<code>/_assets/</code>]</td>\n    <td>To customize webpack's <code>publicPath</code> \n    <br>Comes in handy if using CDN to put built assets</td>\n  </tr>\n  <tr>\n    <td><code>analyzeBundle</code></td>\n    <td><code>Boolean?</code> [<code>false</code>]</td>\n    <td>To enable webpack's bundle analyzer, for analyzing bundle sizes during bundle debugging should Maleo.js' build process got slow</td>\n  </tr>\n  <tr>\n    <td><code>webpack</code></td>\n    <td><code>Function?</code></td>\n    <td>To customize webpack configuration, more details <a href=\"#customize-webpack\">here</a></td>\n  </tr>\n  <tr>\n    <td><code>routes</code></td>\n    <td><code>string?</code> [<code>rootDir/routes.jsx</code>]</td>\n    <td>Path to your routes file</td>\n  </tr>\n  <tr>\n    <td><code>customDocument</code></td>\n    <td><code>string?</code> [<code>rootDir/document.jsx</code>]</td>\n    <td>Path to your custom document file</td>\n  </tr>\n  <tr>\n    <td><code>customWrap</code></td>\n    <td><code>string?</code> [<code>rootDir/wrap.jsx</code>]</td>\n    <td>Path to your custom wrap file</td>\n  </tr>\n  <tr>\n    <td><code>customApp</code></td>\n    <td><code>string?</code> [<code>rootDir/app.jsx</code>]</td>\n    <td>Path to your custom app file</td>\n  </tr>\n</table>\n\n#### Customize Server\n\nCreate a `server.js` file on root directory where your `package.json` lives.\nHere you can customize Maleo's server.\n```js\nimport { Server } from '@airy/maleo/server';\nimport path from 'path';\n\nimport routeConfig from './routes';\n\nconst PORT = process.env.PORT || 8080;\n\nconst maleoServer = Server.init({\n  port: PORT,\n});\n\nmaleoServer.run(() => {\n  console.log('Server running on port :', PORT);\n});\n```\n\nHere are the API's for the configuration:\n\n<table>\n  <tr>\n    <td>Key</td>\n    <td>Type</td>\n    <td>Description</td>\n  </tr>\n  <tr>\n    <td><code>port</code></td>\n    <td><code>Number?</code> [<code>3000</code>]</td>\n    <td>Port to run Maleo server</td>\n  </tr>\n  <tr>\n    <td><code>assetDir</code></td>\n    <td><code>String?</code> [<code>'<root>/.maleo/client'</code>]</td>\n    <td>Directory for all client related assets</td>\n  </tr>\n</table>\n\n#### Customize Webpack\n\nYou are able to extend Maleo.js' default webpack configuration by defining a function on `maleo.config.js`\n\n```js\n// maleo.config.js\n\nmodule.exports = {\n  webpack(config, context, next) {\n    // Perform customizations to webpack config\n    // Important: This call is required in order to let Maleo pass the config to webpack\n    return next(); \n  },\n};\n```\n\nWebpack function will receive three arguments:\n\n<table>\n  <tr>\n    <td>Argument</td>\n    <td>Details</td>\n  <tr>\n  <tr>\n    <td><code>config</code></td>\n    <td>This contains webpack configuration object that you can manipulate</td>\n  <tr>\n  <tr>\n    <td><code>context</code></td>\n    <td>This contains some keys that are useful for the build context\n      <br>\n      <table>\n        <tr>\n          <td><code>isDev</code></td>\n          <td><code>Boolean</code></td>\n          <td>Check if current build is for development</td>\n        </tr>\n        <tr>\n          <td><code>publicPath</code></td>\n          <td><code>String</code></td>\n          <td>Public Path of user defined or default's value</td>\n        </tr>\n        <tr>\n          <td><code>analyzeBundle</code></td>\n          <td><code>Boolean</code></td>\n          <td>Check if analyze bundle is enabled</td>\n        </tr>\n        <tr>\n          <td><code>buildDirectory</code></td>\n          <td><code>String</code></td>\n          <td>Build Directory of user defined or default's value</td>\n        </tr>\n        <tr>\n          <td><code>name</code></td>\n          <td><code>String</code></td>\n          <td>Build name <code>'server' || 'client'</code></td>\n        </tr>\n      </table>\n    </td>\n  <tr>\n  <tr>\n    <td><code>next</code></td>\n    <td>A callback required to be called to pass the custom configuration</td>\n  <tr>\n</table>\n\nExample of adding `ts-loader` through `maleo.config.js`:\n\n```js\n// maleo.config.js\n\n// Partly taken and modified from @airy/maleo-ts-plugin source\n// for simplicity purposes\n\nmodule.exports = {\n  webpack(config, context, next) {\n    const { isDev } = context\n\n    config.module.rules.push({\n      test: /\\.tsx?/,\n      exclude: /node_modules/,\n      use: [\n        require.resolve('@airy/maleo/lib/build/loaders/maleo-babel-loader'),\n        {\n          loader: 'ts-loader',\n          options: {\n            transpileOnly: true,\n          },\n        },\n      ],\n\n      if (isDev) {\n        config.plugins.push(new ForkTSCheckerWebpackPlugin());\n      }\n\n      return next();\n    })\n  },\n};\n```\n\n#### Customize Babel Config\n\nMaleo.js also let you have your own babel config. Just simply add `.babelrc` file at the root directory of your app.\n\nYou can include Maleo.js' babel preset in order to have latest JavaScript preset.\n\nHere's an example of `.babelrc` file:\n```json\n{\n  \"presets\": [\"@airy/maleo/babel\"],\n  \"plugins\": []\n}\n```\n\nThe `@airy/maleo/babel` preset includes everything you need to get your development started. The preset includes:\n- `@babel/preset-env`\n- `@babel/preset-react`\n- `@babel/plugin-proposal-class-properties`\n- `@babel/plugin-proposal-decorators`\n- `@babel/plugin-proposal-object-rest-spread`\n- `@babel/plugin-transform-runtime`\n- `react-loadable/babel`\n\n## Core Utilities Functions\n\n#### Route Matching\n\nTo use you can import the matching routes function like this:\n```js\nimport { getMatchedRoutes } from '@airy/maleo/utils';\n```\n\nThe function will return all matched routes and the route object from `routes.json`\n\n## CDN Support\n\nIf you are using a CDN, you can set up the `publicPath` setting and configure your CDN's origin to resolve to the domain that Maleo.js is hosted on.\n\n```js\n// maleo.config.js\n\nconst isProd = process.env.NODE_ENV === 'production';\n\nmodule.exports = {\n  assetPrefix: isProd && 'https://cdn.example.com';\n}\n```\n\n## FAQ\n\n== TO BE DETERMINED == \n\n## Plugins\n- [css-plugin](https://github.com/airyrooms/maleo-plugins/tree/master/packages/css-plugin)\n- [redux-plugin](https://github.com/airyrooms/maleo-plugins/tree/master/packages/redux-plugin)\n- [typescript-plugin](https://github.com/airyrooms/maleo-plugins/tree/master/packages/typescript-plugin)\n- [more](https://github.com/airyrooms/maleo-plugins)\n\n\n## Contributing\n[Please follow these steps to contribute to Maleo.js](https://github.com/airyrooms/maleo.js/blob/canary/CONTRIBUTING.md)\n\n[Please follow these steps to contribute to Maleo.js' plugins](https://github.com/airyrooms/maleo-plugins#contributing-guidelines)\n\n\n## Contributors\n\nThis project exists thanks to all the people who contribute. [Contribute](CONTRIBUTING.md).\n\nMany thanks to our **[contributors](https://github.com/airyrooms/maleo.js/graphs/contributors)**!\n\n\n## License\n\nMIT","readmeFilename":"README.md","bugs":{"url":"https://github.com/airyrooms/maleo.js/issues"},"homepage":"https://github.com/airyrooms/maleo.js#readme","_id":"@airy/maleo@0.3.1-canary.9","_nodeVersion":"10.11.0","_npmVersion":"lerna/3.13.1/node@v10.11.0+x64 (darwin)","dist":{"integrity":"sha512-YvOOY3qcSgr6W/ZWqeO67lfe3Tuv7fWpOb+hV/QTztNFatYa6mR754sijvJKIVGo4Z2PPKGrzq19G826G/73uQ==","shasum":"be3796e760535491ae19b0d592cfd0caf4750ed8","tarball":"https://registry.npmjs.org/@airy/maleo/-/maleo-0.3.1-canary.9.tgz","fileCount":118,"unpackedSize":361662,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJc/3iGCRA9TVsSAnZWagAAAN8P/0CbWYQ8qTrRoVZy85SS\n1PWjDi7EaEdyJ427XNF53POxRB/rhFkQBB+XcJYNJKAUGf9TVndzi9wqxxSP\nbXCWtcyJKqyAb3THF0npaeoNI9B5Qn+fF5mWiS9ODrqREs+7gs3AObYsuaXS\nLpeyPU9sCCGkmzQD70LHcLNZbQarTegWmY+ozNp/LcDOFiVN2nDZY5/ZZqz0\naEFvw5mXkO9KjyUW7RbmNj2R7zyppsP1xmqdcVbs6D+J/iJIBDNBlFOxaHCV\nbbHrorewV5E6SEPFKvOWAvNA+BeFEZrQPcGDYIfjQiqj+CrjLtojKJwSRTgi\nLkjehVzUIcoidli8ZwUJzJD3IuwSgnVBFbaeYxrqSIw5JWRBpac1+9QZGwVR\nb4hkOG/N+8eHuZKJSnocbCeubCdYlrFpzkAShQXScStS3I1qAGXprupAuABK\nqp6OPlkXvPagRnH6cJZdXt8s5QWnj7A97kum9jzdfY8buvszhFwmV6kITa03\nUSc+NaEQkMr2HZJ+J3kfoNsq+hxJ83OYwPkgHaW89Tzf0+4/91gH6MZq39vr\n/kzLn6C0VFoU9CU9QSMYmonw3l/dYQ+q6jNPcd+ZqSWWDH6WjaTy8AvRr+Tp\nj7rGnkiQqyIQQRYckmNFlI4V0W/LMk6M8UzQEeZMnSJwRzz7HLTEjU2EOZY0\nNk2m\r\n=ua6p\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIDz9Jh0yuurvFPm0j0oZECUoegqlEeXWrKRjZUV/jjohAiBVnPzk7Hmw/dScSj6vkyS8J0edlcHvEvVPnhqNp9zjvQ=="}]},"maintainers":[{"name":"airyrooms-engineering","email":"engineering@airyrooms.com"}],"_npmUser":{"name":"airyrooms-engineering","email":"engineering@airyrooms.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/maleo_0.3.1-canary.9_1560246405398_0.3254695971643933"},"_hasShrinkwrap":false},"0.3.1-canary.10":{"name":"@airy/maleo","version":"0.3.1-canary.10","description":"Un-opinionated Universal Rendering Javascript Framework","repository":{"type":"git","url":"git+https://github.com/airyrooms/maleo.js.git","bugs":"https://github.com/airyrooms/maleo.js/issues"},"author":{"name":"Airy"},"license":"MIT","private":false,"sideEffects":false,"scripts":{"build":"rm -rf dist && gulp","watch":"gulp watch","prepublish":"yarn build","start":"node server.js"},"bin":{"maleo":"lib/bin/maleo.js"},"dependencies":{"@babel/core":"7.1.2","@babel/plugin-proposal-class-properties":"7.1.0","@babel/plugin-proposal-decorators":"7.1.2","@babel/plugin-proposal-object-rest-spread":"7.0.0","@babel/plugin-syntax-dynamic-import":"7.0.0","@babel/plugin-transform-runtime":"7.1.0","@babel/preset-env":"7.1.0","@babel/preset-react":"7.0.0","@babel/runtime":"7.1.2","@babel/runtime-corejs2":"7.2.0","babel-loader":"8.0.4","case-sensitive-paths-webpack-plugin":"2.1.2","compression":"1.7.4","es6-promise":"4.2.5","eventsource":"1.0.7","express":"4.16.4","figlet":"1.2.1","friendly-errors-webpack-plugin":"1.7.0","hard-source-webpack-plugin":"0.13.1","helmet":"3.14.0","hoist-non-react-statics":"^3.3.0","isomorphic-fetch":"2.2.1","loader-utils":"1.1.0","node-notifier":"5.2.1","nodemon":"1.18.4","react-dom":"16.8.4","react-helmet":"5.2.0","react-loadable":"5.5.0","react-router-config":"5.0.0","react-router-dom":"5.0.0","rimraf":"2.6.3","source-map-support":"0.5.9","terser-webpack-plugin":"1.1.0","webpack":"4.29.0","webpack-bundle-analyzer":"3.0.3","webpack-cli":"3.1.2","webpack-dev-middleware":"3.4.0","webpack-hot-middleware":"2.24.3","webpackbar":"2.6.3"},"peerDependencies":{"react":"^16.8.4"},"devDependencies":{"@babel/preset-typescript":"^7.1.0","@types/webpack-bundle-analyzer":"^2.13.0","babel-plugin-module-resolver":"^3.1.1","gulp":"github:gulpjs/gulp#4.0","gulp-babel":"^8.0.0","gulp-clean":"^0.4.0","gulp-sourcemaps":"^2.6.4","gulp-uglify":"^3.0.1","gulp-watch":"^5.0.1","react":"^16.8.4"},"gitHead":"a8d1185746d8e607b2788b90c80e0fa9e1dba789","readme":"[![Build Status](https://travis-ci.org/airyrooms/maleo.js.svg?branch=master)](https://travis-ci.org/airyrooms/maleo.js)\n[![Coverage Status](https://coveralls.io/repos/github/airyrooms/maleo.js/badge.svg?branch=master)](https://coveralls.io/github/airyrooms/maleo.js?branch=master)\n[![npm version](https://badge.fury.io/js/%40airy%2Fmaleo.svg)](https://badge.fury.io/js/%40airy%2Fmaleo)\n[![Issues](http://img.shields.io/github/issues/airyrooms/maleo.js.svg)]( https://github.com/airyrooms/maleo.js/issues)\n[![MIT license](https://img.shields.io/badge/License-MIT-blue.svg)](https://github.com/airyrooms/maleo.js/blob/canary/LICENSE)\n[![Discord Chat](https://img.shields.io/discord/550498214789513218.svg)](https://discord.gg/9eArCQn)\n[![Conventional Commits](https://img.shields.io/badge/Conventional%20Commits-1.0.0-green.svg)](https://conventionalcommits.org)\n\n\n# Welcome to Maleo.js\n\nMaleo.js is an un-opinionated framework to enable Universal Rendering in JavaScript using React with no hassle.\n\nWe are here to solve the time consuming setups Universal Rendering Required.\n\n---\n\nReadme below is the documentation for the `canary` (prerelease) branch. To view the documentation for the latest stable Maleo.js version change branch to `master`\n\n---\n\n# Table of Contents\n- [Features](#features)\n- [Setup](#setup)\n- [Component Lifecycle](#component-lifecycle)\n- [Routing](#routing)\n- [Dynamic Import Component](#dynamic-import-component)\n  - [Tips](#tips)\n  - [Preloading](#preloading)\n- [Customizable Component](#customizable-component)\n  - [Custom Document](#custom-document)\n  - [Custom Wrap](#custom-wrap)\n- [Custom Configuration](#custom-configuration)\n  - [Customize Server](#customize-server)\n  - [Customize Webpack](#customize-webpack)\n  - [Customize Babel Config](#customize-babel-config)\n- [Core Utilities Functions](#core-utilities-functions)\n- [CDN Support](#cdn-support)\n- [Plugins](#plugins)\n- [FAQ](#faq)\n- [Contributing](#contributing)\n\n---\n\n## Features\n- Universal Rendering\n- Plugin based framework\n- Customizable\n\n## Setup\n\nInstall Maleo.js \n\n>*for now change `@airy/maleo` to `@airy/maleo@canary` until we publish the stable version of this package*\n\n\n**NPM**\n```bash\n$ npm install --save @airy/maleo react\n```\n**Yarn**\n```bash\n$ yarn add @airy/maleo react\n```\n\nAdd this script to your `package.json`\n```json\n{\n  \"scripts\": {\n    \"dev\": \"maleo dev\",\n    \"build\": \"export NODE_ENV=production && maleo build\",\n    \"start\": \"export NODE_ENV=production && maleo run\"\n  }\n}\n```\n\nCreate a page Root component\n```jsx\n// ./src/Root.jsx\nimport React from 'react';\n\n// Export default is required for registering page\nexport default class RootComponent extends React.Component {\n  render() {\n    return (\n      <h1>Hello World!</h1>\n    )\n  }\n}\n```\n\nAnd lastly, create a routing file on your project root directory called `routes.json` and register your page component\n```json\n[\n  {\n    \"path\": \"/\",\n    \"page\": \"./src/Root\"\n  }\n]\n```\n\nAfter that you can now run `$ npm run dev` and go to `http://localhost:3000`.\n\nYou should now see your app running on your browser.\n\nBy now you should see \n- Automatic transpilation and bundling (with webpack and babel)\n- ~~Hot code reloading~~ [#17](https://github.com/airyrooms/maleo.js/issues/17)\n- Server rendering\n\nTo see how simple this is, check out the sample app!\n\n## Component Lifecycle \n\nMaleo.js added a new component lifecycle hook called `getInitialProps`, this function is called during Server Side Rendering (SSR) and during route changes on Client Side Rendering (CSR).\n\nThis is useful especially for SEO purposes. \n\nExample for stateful component:\n```jsx\nimport React from 'react';\n\nexport default class extends React.Component {\n  static getInitialProps = async (ctx) => {\n    const { req } = ctx;\n\n    // check if getInitialProps is called on server or on client\n    const userAgent = req ? req.headers['user-agent'] : navigator.userAgent;\n\n    // the return value will be passed as props for this component\n    return { userAgent };\n  }\n\n  render() {\n    return (\n      <div>\n        Hello World {this.props.userAgent}\n      </div>\n    );\n  }\n}\n```\n\nExample for stateless component:\n```jsx\nconst Component = (props) => (\n  <div>\n    Hello World {props.userAgent}\n  </div>\n);\n\nComponent.getInitialprops = async (ctx) => {\n  const { req } = ctx;\n\n  const userAgent = req ? req.headers['user-agent'] : navigator.userAgent;\n\n  return { userAgent };\n};\n\nexport default Component;\n```\n\n`getInitialProps` receives a context object with the following properties:\n- `req` - HTTP request object (server only)\n- `res` - HTTP response object (server only)\n- `...wrapProps` - Spreaded properties from custom Wrap\n- `...appProps` - Spreaded properties from custom App\n\n## Routing\n\nRouting is declared in a centralized route config.\nRegister all the route config in `routes.json` file.\n\nIf you put the `routes.json` files on root directory, Maleo will automatically register your route. Otherwise put path to your routes on [Maleo config](#custom-configuration).\n\nRoutes file has to export default the route configuration.\nThe route object **expected to have distinct key** to indicate the route.\n\n<table>\n  <tr>\n    <td>Key</td>\n    <td>Type</td>\n    <td>Description</td>\n  </tr>\n  <tr>\n    <td><code>path</code></td>\n    <td><code>String!</code></td>\n    <td>Routes path</td>\n  </tr>\n  <tr>\n    <td><code>page</code></td>\n    <td><code>String!</code></td>\n    <td>Path to React Component for this route path</td>\n  </tr>\n  <tr>\n    <td><code>exact</code></td>\n    <td><code>Boolean?</code> [<code>false</code>]</td>\n    <td>To make url has to match exactly the path in order to render the component. Give <code>false</code> value if the route is a wrapper route component</td>\n  </tr>\n  <tr>\n    <td><code>routes</code></td>\n    <td><code>RouteObject?</code></td>\n    <td>Nested route</td>\n  </tr>\n</table>\n\n\nFor example:\n```json\n[\n  {\n    \"page\": \"./src/MainApp\",\n    \"routes\": [\n      {\n        \"path\": \"/\",\n        \"page\": \"./src/Search\",\n        \"exact\": true\n      },\n      {\n        \"path\": \"/search\",\n        \"page\": \"./src/Search\",\n        \"routes\": [\n          {\n            \"path\": \"/search/hello\",\n            \"page\": \"./src/Detail\",\n            \"exact\": true\n          }\n        ]\n      },\n      {\n        \"path\": \"/detail\",\n        \"page\": \"./src/Detail\",\n        \"exact\": true\n      }\n    ]\n  }\n]\n```\n\n## Dynamic Import Component\nMaleo.js supports TC39 [dynamic import proposal](https://github.com/tc39/proposal-dynamic-import) for JavaScript.\n\nYou can think dynamic import as another way to split your code into manageable chunks. You can use our `Dynamic` function which utilizes [react loadable](https://github.com/jamiebuilds/react-loadable)\n\nFor Example\n```js\n// DynamicComponent.js\nimport Dynamic from '@airy/maleo/dynamic';\n\nexport default Dynamic({\n  loader: () => import( /* webpackChunkName:\"DynamicComponent\" */ './component/DynamicLoad'),\n})\n```\n\n### **Preloading**\n\nFor optimization purposes, you can also preload a component even before the component got rendered.\n\nFor example, if you want to load component when a button get pressed, you can start preloading the component when the user hovers the mouse over the button.\n\nThe component created by `Dynamic` exposes a [static `preload` method](https://github.com/jamiebuilds/react-loadable#loadablecomponentpreload).\n\n```jsx\nimport React from 'react';\nimport Dynamic from '@airy/maleo/dynamic';\n\nconst DynamicBar = Dynamic({\n  loader: () => import('./Bar'),\n  loading: LoadingCompoinent\n});\n\nclass MyComponent extends React.Component {\n  state = { showBar: false };\n\n  onClick = () => {\n    this.setState({ showBar: true });\n  };\n\n  onMouseOver = () => DynamicBar.preload();\n\n  render() {\n    return (\n      <div>\n        <button\n          onClick={this.onClick}\n          onMouseOver={this.onMouseOver}>\n          Show Bar\n        </button>\n        { this.state.showBar && <DynamicBar /> }\n      </div>\n    )\n  }\n}\n```\n\n## Customizable Component\n\n### Custom Document\n\nHighly inspired by what [Next.js](https://github.com/zeit/next.js) has done on their awesome template customization.\n\nMaleo.js also enable customization on `Document` as document's markup. So you don't need to include tags like `<html>`, `<head>`, `<body>`, etc. \n\nTo override the default behavior, you'll need to create a component that extends the `Document` React class provided by Maleo.\n\n```jsx\n// document.jsx\nimport React from 'react';\nimport { Document, Header, Main, Scripts } from '@airy/maleo/document';\n\nexport default class extends Document {\n  render() {\n    return (\n      <html>\n        <Header>\n          <title>Maleo JS</title>\n          <meta charset=\"utf-8\" />\n          <meta name=\"description\" content=\"Maleo.js is awesome!\" />\n\n          <style>\n            {` body { background-color: #fff } `}\n          </style>\n        </Header>\n\n        <body>\n          <Main />\n\n          <Scripts />\n\n          <ReduxScript />\n        </body>\n      </html>\n    );\n  }\n}\n```\n\n### Custom Wrap\n\nMaleo.js uses the `Wrap` component to initialize pages. `Wrap` contains React Router's Component. You can add HoC here to wrap the application and control the page initialization. Which allows you to do amazing things like:\n- Persisting layour between page changes\n- Keeping state when navigating pages\n- Custom error handling using `componentDidCatch`\n- Inject additional data into pages (like Redux provider, etc)\n\nTo override the default behavior, you'll need to create a component that extends the `Wrap` React class provided by Maleo.\n\n```jsx\n// wrap.jsx\nimport React from 'react';\nimport { Wrap } from '@airy/maleo/wrap';\n\n// Redux plugin for Maleo.js\n// Hoc that creates store and add Redux Provider\nimport { withRedux } from '@airy/maleo-redux-plugin';\n\n// Custom Wrapper that will be rendered for the whole Application\nimport Content from './component/Content';\nimport NavigationBar from './component/NavigationBar';\n\nimport { createStore } from './store';\n\n@withRedux(createStore)\nexport default class extends Wrap {\n  static getInitialProps = (ctx) => {\n    const { store } = ctx\n    // you receive store from context\n    // you can access or do something with the store here\n    console.log('Initialized Redux Store', store);\n    return {}\n  }\n\n  render() {\n    const { Container, containerProps, App, appProps } = this.props\n\n    return (\n      <Container {...containerProps}>\n        <NavigationBar />\n        <Content>\n          <App {...appProps}/>\n        </Content>\n      </Container>\n    )\n  }\n}\n\n```\nIf you put `document.jsx` and `wrap.jsx` on root directory (the same level with `package.json`), then Maleo will automatically register your custom Document and Wrap. Otherwise, you can add the path to your custom Document and Wrap on [Maleo config](#custom-configuration)\n\n---\n***We are also working on adding default and customizable `Error` component page***\n\n---\n\n## Custom Configuration\n\nFor more advanced configuration of Maleo.js, like `webpack` config, registering `plugins`, path to your routes, custom Document and Wrap, and adding `path alias`, you can create a `maleo.config.js` in the root of your project directory. (same directory with `package.json`)\n\n```js\n// maleo.config.js\n\nmodule.exports = {\n  /* config options here */\n}\n```\n\nHere are the API's for the configuration:\n\n<table>\n  <tr>\n    <td>Key</td>\n    <td>Type</td>\n    <td>Description</td>\n  </tr>\n  <tr>\n    <td><code>buildDir</code></td>\n    <td><code>String?</code> [<code>.maleo</code>]</td>\n    <td>Directory to put Maleo.js' build assets</td>\n  </tr>\n  <tr>\n    <td><code>cache</code></td>\n    <td><code>Boolean?</code> [<code>true</code>]</td>\n    <td>Enable webpack build caching</td>\n  </tr>\n  <tr>\n    <td><code>isDev</code></td>\n    <td><code>Boolean?</code> [<code>process.env.NODE_ENV === 'development'</code>]</td>\n    <td>Enable development build configuration</td>\n  </tr>\n  <tr>\n    <td><code>sourceMaps</code></td>\n    <td><code>Boolean?</code> [<code>true</code>]</td>\n    <td>Enable webpack to generate source maps</td>\n  </tr>\n  <tr>\n    <td><code>alias</code></td>\n    <td><code>Object?</code></td>\n    <td>A key value pair for aliasing path directory\n      <br>\n      <code>\n      { 'component': './src/component' }\n      </code>\n    </td>\n  </tr>\n  <tr>\n    <td><code>publicPath</code></td>\n    <td><code>String?</code> [<code>/_assets/</code>]</td>\n    <td>To customize webpack's <code>publicPath</code> \n    <br>Comes in handy if using CDN to put built assets</td>\n  </tr>\n  <tr>\n    <td><code>analyzeBundle</code></td>\n    <td><code>Boolean?</code> [<code>false</code>]</td>\n    <td>To enable webpack's bundle analyzer, for analyzing bundle sizes during bundle debugging should Maleo.js' build process got slow</td>\n  </tr>\n  <tr>\n    <td><code>webpack</code></td>\n    <td><code>Function?</code></td>\n    <td>To customize webpack configuration, more details <a href=\"#customize-webpack\">here</a></td>\n  </tr>\n  <tr>\n    <td><code>routes</code></td>\n    <td><code>string?</code> [<code>rootDir/routes.jsx</code>]</td>\n    <td>Path to your routes file</td>\n  </tr>\n  <tr>\n    <td><code>customDocument</code></td>\n    <td><code>string?</code> [<code>rootDir/document.jsx</code>]</td>\n    <td>Path to your custom document file</td>\n  </tr>\n  <tr>\n    <td><code>customWrap</code></td>\n    <td><code>string?</code> [<code>rootDir/wrap.jsx</code>]</td>\n    <td>Path to your custom wrap file</td>\n  </tr>\n  <tr>\n    <td><code>customApp</code></td>\n    <td><code>string?</code> [<code>rootDir/app.jsx</code>]</td>\n    <td>Path to your custom app file</td>\n  </tr>\n</table>\n\n#### Customize Server\n\nCreate a `server.js` file on root directory where your `package.json` lives.\nHere you can customize Maleo's server.\n```js\nimport { Server } from '@airy/maleo/server';\nimport path from 'path';\n\nimport routeConfig from './routes';\n\nconst PORT = process.env.PORT || 8080;\n\nconst maleoServer = Server.init({\n  port: PORT,\n});\n\nmaleoServer.run(() => {\n  console.log('Server running on port :', PORT);\n});\n```\n\nHere are the API's for the configuration:\n\n<table>\n  <tr>\n    <td>Key</td>\n    <td>Type</td>\n    <td>Description</td>\n  </tr>\n  <tr>\n    <td><code>port</code></td>\n    <td><code>Number?</code> [<code>3000</code>]</td>\n    <td>Port to run Maleo server</td>\n  </tr>\n  <tr>\n    <td><code>assetDir</code></td>\n    <td><code>String?</code> [<code>'<root>/.maleo/client'</code>]</td>\n    <td>Directory for all client related assets</td>\n  </tr>\n</table>\n\n#### Customize Webpack\n\nYou are able to extend Maleo.js' default webpack configuration by defining a function on `maleo.config.js`\n\n```js\n// maleo.config.js\n\nmodule.exports = {\n  webpack(config, context, next) {\n    // Perform customizations to webpack config\n    // Important: This call is required in order to let Maleo pass the config to webpack\n    return next(); \n  },\n};\n```\n\nWebpack function will receive three arguments:\n\n<table>\n  <tr>\n    <td>Argument</td>\n    <td>Details</td>\n  <tr>\n  <tr>\n    <td><code>config</code></td>\n    <td>This contains webpack configuration object that you can manipulate</td>\n  <tr>\n  <tr>\n    <td><code>context</code></td>\n    <td>This contains some keys that are useful for the build context\n      <br>\n      <table>\n        <tr>\n          <td><code>isDev</code></td>\n          <td><code>Boolean</code></td>\n          <td>Check if current build is for development</td>\n        </tr>\n        <tr>\n          <td><code>publicPath</code></td>\n          <td><code>String</code></td>\n          <td>Public Path of user defined or default's value</td>\n        </tr>\n        <tr>\n          <td><code>analyzeBundle</code></td>\n          <td><code>Boolean</code></td>\n          <td>Check if analyze bundle is enabled</td>\n        </tr>\n        <tr>\n          <td><code>buildDirectory</code></td>\n          <td><code>String</code></td>\n          <td>Build Directory of user defined or default's value</td>\n        </tr>\n        <tr>\n          <td><code>name</code></td>\n          <td><code>String</code></td>\n          <td>Build name <code>'server' || 'client'</code></td>\n        </tr>\n      </table>\n    </td>\n  <tr>\n  <tr>\n    <td><code>next</code></td>\n    <td>A callback required to be called to pass the custom configuration</td>\n  <tr>\n</table>\n\nExample of adding `ts-loader` through `maleo.config.js`:\n\n```js\n// maleo.config.js\n\n// Partly taken and modified from @airy/maleo-ts-plugin source\n// for simplicity purposes\n\nmodule.exports = {\n  webpack(config, context, next) {\n    const { isDev } = context\n\n    config.module.rules.push({\n      test: /\\.tsx?/,\n      exclude: /node_modules/,\n      use: [\n        require.resolve('@airy/maleo/lib/build/loaders/maleo-babel-loader'),\n        {\n          loader: 'ts-loader',\n          options: {\n            transpileOnly: true,\n          },\n        },\n      ],\n\n      if (isDev) {\n        config.plugins.push(new ForkTSCheckerWebpackPlugin());\n      }\n\n      return next();\n    })\n  },\n};\n```\n\n#### Customize Babel Config\n\nMaleo.js also let you have your own babel config. Just simply add `.babelrc` file at the root directory of your app.\n\nYou can include Maleo.js' babel preset in order to have latest JavaScript preset.\n\nHere's an example of `.babelrc` file:\n```json\n{\n  \"presets\": [\"@airy/maleo/babel\"],\n  \"plugins\": []\n}\n```\n\nThe `@airy/maleo/babel` preset includes everything you need to get your development started. The preset includes:\n- `@babel/preset-env`\n- `@babel/preset-react`\n- `@babel/plugin-proposal-class-properties`\n- `@babel/plugin-proposal-decorators`\n- `@babel/plugin-proposal-object-rest-spread`\n- `@babel/plugin-transform-runtime`\n- `react-loadable/babel`\n\n## Core Utilities Functions\n\n#### Route Matching\n\nTo use you can import the matching routes function like this:\n```js\nimport { getMatchedRoutes } from '@airy/maleo/utils';\n```\n\nThe function will return all matched routes and the route object from `routes.json`\n\n## CDN Support\n\nIf you are using a CDN, you can set up the `publicPath` setting and configure your CDN's origin to resolve to the domain that Maleo.js is hosted on.\n\n```js\n// maleo.config.js\n\nconst isProd = process.env.NODE_ENV === 'production';\n\nmodule.exports = {\n  assetPrefix: isProd && 'https://cdn.example.com';\n}\n```\n\n## FAQ\n\n== TO BE DETERMINED == \n\n## Plugins\n- [css-plugin](https://github.com/airyrooms/maleo-plugins/tree/master/packages/css-plugin)\n- [redux-plugin](https://github.com/airyrooms/maleo-plugins/tree/master/packages/redux-plugin)\n- [typescript-plugin](https://github.com/airyrooms/maleo-plugins/tree/master/packages/typescript-plugin)\n- [more](https://github.com/airyrooms/maleo-plugins)\n\n\n## Contributing\n[Please follow these steps to contribute to Maleo.js](https://github.com/airyrooms/maleo.js/blob/canary/CONTRIBUTING.md)\n\n[Please follow these steps to contribute to Maleo.js' plugins](https://github.com/airyrooms/maleo-plugins#contributing-guidelines)\n\n\n## Contributors\n\nThis project exists thanks to all the people who contribute. [Contribute](CONTRIBUTING.md).\n\nMany thanks to our **[contributors](https://github.com/airyrooms/maleo.js/graphs/contributors)**!\n\n\n## License\n\nMIT","readmeFilename":"README.md","bugs":{"url":"https://github.com/airyrooms/maleo.js/issues"},"homepage":"https://github.com/airyrooms/maleo.js#readme","_id":"@airy/maleo@0.3.1-canary.10","_nodeVersion":"11.15.0","_npmVersion":"lerna/3.13.1/node@v11.15.0+x64 (linux)","dist":{"integrity":"sha512-f1DXs3zjgUrkFM70YItjgJ+Ip09wIuT4Q57TQRxm3Qh2HhWhhl9vubHi8kdVfdP2ei1/w0YJWc1dsv/G+i3ffw==","shasum":"c1bbf130c1794aad77168013667368679d497e56","tarball":"https://registry.npmjs.org/@airy/maleo/-/maleo-0.3.1-canary.10.tgz","fileCount":96,"unpackedSize":327181,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJc/5a+CRA9TVsSAnZWagAAlr4P/07NLsfgYaERk6lthQfr\n2K3+NPlrO4ZBGhOtUQkovP95M/HKmSJMF4fRLnqxlrtHA2k3xAD+c211Tg7p\nttPGvceYwDEiY2mHGu+XiTK8pre64TqCkCTHc8LLSojiJnx4TlrTxAJo4s/q\nRMKT8sShh9+uDGK9oG65fo8knoTqQbgYiXeDUpTENbhOWZVSBz8Ft6wSrr4N\nGpTVQy1J/vuaqlk1MTcSJ1GGYk6K+UvTVJevqnTDnJHyGdfquNx8CC5Axr7m\nR2nB1FNUIUvJ0fQVZ2FftWDYiKZblVgTyTEiJo7NRCd8N68NYnpWqIrAPXJB\n6GOt20cfzbxaHLmiIVS3vEZu8qxQxAXP18JcEDEANocTmahtZVC964rN3/aI\nUqKG2+b1392YUdwX7Gyc1vRM69N8R5wkCdayinuMkRIdYpXenVLf37QXWGTh\nTSNbHIgjhlWhEWXr3SPCL9LhIHKk5R7BrUm6F46jYdxrZBeFCr7mP3qlXNtR\nACc++uz5epPZK5YPov2TuCNIBtTNZ4mgbTfpgOg4Q6wRmEUJOkXpDCVtlxht\nbS902aicRJC8Jqno13dQqoWPZPra1zCTdJF/6UjeaXxUvC2UUfDOVaSH5OYG\nJBoCSWQ4oto18Tl1aECrQ8U18ncquzwSarwi2hdfqS6G07ayBwzp/uwHDtgm\n4nWA\r\n=yK48\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCHDeR1dRz8nf4E671GKDYaKTZpPGd5dfwYKakJ6TfI7AIgWrinQ5NPVYWgwm8c6B0qb/TYnDmSSfiG1g9ADbCNEJ0="}]},"maintainers":[{"name":"airyrooms-engineering","email":"engineering@airyrooms.com"}],"_npmUser":{"name":"airyrooms-engineering","email":"engineering@airyrooms.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/maleo_0.3.1-canary.10_1560254142303_0.6042628279763886"},"_hasShrinkwrap":false},"0.3.1-canary.11":{"name":"@airy/maleo","version":"0.3.1-canary.11","description":"Un-opinionated Universal Rendering Javascript Framework","repository":{"type":"git","url":"git+https://github.com/airyrooms/maleo.js.git","bugs":"https://github.com/airyrooms/maleo.js/issues"},"author":{"name":"Airy"},"license":"MIT","private":false,"sideEffects":false,"scripts":{"build":"rm -rf dist && gulp","watch":"gulp watch","prepublish":"yarn build","start":"node server.js"},"bin":{"maleo":"lib/bin/maleo.js"},"dependencies":{"@babel/core":"7.1.2","@babel/plugin-proposal-class-properties":"7.1.0","@babel/plugin-proposal-decorators":"7.1.2","@babel/plugin-proposal-object-rest-spread":"7.0.0","@babel/plugin-syntax-dynamic-import":"7.0.0","@babel/plugin-transform-runtime":"7.1.0","@babel/preset-env":"7.1.0","@babel/preset-react":"7.0.0","@babel/runtime":"7.1.2","@babel/runtime-corejs2":"7.2.0","babel-loader":"8.0.4","case-sensitive-paths-webpack-plugin":"2.1.2","compression":"1.7.4","es6-promise":"4.2.5","eventsource":"1.0.7","express":"4.16.4","figlet":"1.2.1","friendly-errors-webpack-plugin":"1.7.0","hard-source-webpack-plugin":"0.13.1","helmet":"3.14.0","hoist-non-react-statics":"^3.3.0","isomorphic-fetch":"2.2.1","loader-utils":"1.1.0","node-notifier":"5.2.1","nodemon":"1.18.4","react-dom":"16.8.4","react-helmet":"5.2.0","react-loadable":"5.5.0","react-router-config":"5.0.0","react-router-dom":"5.0.0","rimraf":"2.6.3","source-map-support":"0.5.9","terser-webpack-plugin":"1.1.0","webpack":"4.29.0","webpack-bundle-analyzer":"3.0.3","webpack-cli":"3.1.2","webpack-dev-middleware":"3.4.0","webpack-hot-middleware":"2.24.3","webpackbar":"2.6.3"},"peerDependencies":{"react":"^16.8.4"},"devDependencies":{"@babel/preset-typescript":"^7.1.0","@types/webpack-bundle-analyzer":"^2.13.0","babel-plugin-module-resolver":"^3.1.1","gulp":"github:gulpjs/gulp#4.0","gulp-babel":"^8.0.0","gulp-clean":"^0.4.0","gulp-sourcemaps":"^2.6.4","gulp-uglify":"^3.0.1","gulp-watch":"^5.0.1","react":"^16.8.4"},"gitHead":"d6ed71eef75e44c86b481aff14ac33e9ca17e1f9","readme":"[![Build Status](https://travis-ci.org/airyrooms/maleo.js.svg?branch=master)](https://travis-ci.org/airyrooms/maleo.js)\n[![Coverage Status](https://coveralls.io/repos/github/airyrooms/maleo.js/badge.svg?branch=master)](https://coveralls.io/github/airyrooms/maleo.js?branch=master)\n[![npm version](https://badge.fury.io/js/%40airy%2Fmaleo.svg)](https://badge.fury.io/js/%40airy%2Fmaleo)\n[![Issues](http://img.shields.io/github/issues/airyrooms/maleo.js.svg)]( https://github.com/airyrooms/maleo.js/issues)\n[![MIT license](https://img.shields.io/badge/License-MIT-blue.svg)](https://github.com/airyrooms/maleo.js/blob/canary/LICENSE)\n[![Discord Chat](https://img.shields.io/discord/550498214789513218.svg)](https://discord.gg/9eArCQn)\n[![Conventional Commits](https://img.shields.io/badge/Conventional%20Commits-1.0.0-green.svg)](https://conventionalcommits.org)\n\n\n# Welcome to Maleo.js\n\nMaleo.js is an un-opinionated framework to enable Universal Rendering in JavaScript using React with no hassle.\n\nWe are here to solve the time consuming setups Universal Rendering Required.\n\n---\n\nReadme below is the documentation for the `canary` (prerelease) branch. To view the documentation for the latest stable Maleo.js version change branch to `master`\n\n---\n\n# Table of Contents\n- [Features](#features)\n- [Setup](#setup)\n- [Component Lifecycle](#component-lifecycle)\n- [Routing](#routing)\n- [Dynamic Import Component](#dynamic-import-component)\n  - [Tips](#tips)\n  - [Preloading](#preloading)\n- [Customizable Component](#customizable-component)\n  - [Custom Document](#custom-document)\n  - [Custom Wrap](#custom-wrap)\n- [Custom Configuration](#custom-configuration)\n  - [Customize Server](#customize-server)\n  - [Customize Webpack](#customize-webpack)\n  - [Customize Babel Config](#customize-babel-config)\n- [Core Utilities Functions](#core-utilities-functions)\n- [CDN Support](#cdn-support)\n- [Plugins](#plugins)\n- [FAQ](#faq)\n- [Contributing](#contributing)\n\n---\n\n## Features\n- Universal Rendering\n- Plugin based framework\n- Customizable\n\n## Setup\n\nInstall Maleo.js \n\n>*for now change `@airy/maleo` to `@airy/maleo@canary` until we publish the stable version of this package*\n\n\n**NPM**\n```bash\n$ npm install --save @airy/maleo react\n```\n**Yarn**\n```bash\n$ yarn add @airy/maleo react\n```\n\nAdd this script to your `package.json`\n```json\n{\n  \"scripts\": {\n    \"dev\": \"maleo dev\",\n    \"build\": \"export NODE_ENV=production && maleo build\",\n    \"start\": \"export NODE_ENV=production && maleo run\"\n  }\n}\n```\n\nCreate a page Root component\n```jsx\n// ./src/Root.jsx\nimport React from 'react';\n\n// Export default is required for registering page\nexport default class RootComponent extends React.Component {\n  render() {\n    return (\n      <h1>Hello World!</h1>\n    )\n  }\n}\n```\n\nAnd lastly, create a routing file on your project root directory called `routes.json` and register your page component\n```json\n[\n  {\n    \"path\": \"/\",\n    \"page\": \"./src/Root\"\n  }\n]\n```\n\nAfter that you can now run `$ npm run dev` and go to `http://localhost:3000`.\n\nYou should now see your app running on your browser.\n\nBy now you should see \n- Automatic transpilation and bundling (with webpack and babel)\n- ~~Hot code reloading~~ [#17](https://github.com/airyrooms/maleo.js/issues/17)\n- Server rendering\n\nTo see how simple this is, check out the sample app!\n\n## Component Lifecycle \n\nMaleo.js added a new component lifecycle hook called `getInitialProps`, this function is called during Server Side Rendering (SSR) and during route changes on Client Side Rendering (CSR).\n\nThis is useful especially for SEO purposes. \n\nExample for stateful component:\n```jsx\nimport React from 'react';\n\nexport default class extends React.Component {\n  static getInitialProps = async (ctx) => {\n    const { req } = ctx;\n\n    // check if getInitialProps is called on server or on client\n    const userAgent = req ? req.headers['user-agent'] : navigator.userAgent;\n\n    // the return value will be passed as props for this component\n    return { userAgent };\n  }\n\n  render() {\n    return (\n      <div>\n        Hello World {this.props.userAgent}\n      </div>\n    );\n  }\n}\n```\n\nExample for stateless component:\n```jsx\nconst Component = (props) => (\n  <div>\n    Hello World {props.userAgent}\n  </div>\n);\n\nComponent.getInitialprops = async (ctx) => {\n  const { req } = ctx;\n\n  const userAgent = req ? req.headers['user-agent'] : navigator.userAgent;\n\n  return { userAgent };\n};\n\nexport default Component;\n```\n\n`getInitialProps` receives a context object with the following properties:\n- `req` - HTTP request object (server only)\n- `res` - HTTP response object (server only)\n- `...wrapProps` - Spreaded properties from custom Wrap\n- `...appProps` - Spreaded properties from custom App\n\n## Routing\n\nRouting is declared in a centralized route config.\nRegister all the route config in `routes.json` file.\n\nIf you put the `routes.json` files on root directory, Maleo will automatically register your route. Otherwise put path to your routes on [Maleo config](#custom-configuration).\n\nRoutes file has to export default the route configuration.\nThe route object **expected to have distinct key** to indicate the route.\n\n<table>\n  <tr>\n    <td>Key</td>\n    <td>Type</td>\n    <td>Description</td>\n  </tr>\n  <tr>\n    <td><code>path</code></td>\n    <td><code>String!</code></td>\n    <td>Routes path</td>\n  </tr>\n  <tr>\n    <td><code>page</code></td>\n    <td><code>String!</code></td>\n    <td>Path to React Component for this route path</td>\n  </tr>\n  <tr>\n    <td><code>exact</code></td>\n    <td><code>Boolean?</code> [<code>false</code>]</td>\n    <td>To make url has to match exactly the path in order to render the component. Give <code>false</code> value if the route is a wrapper route component</td>\n  </tr>\n  <tr>\n    <td><code>routes</code></td>\n    <td><code>RouteObject?</code></td>\n    <td>Nested route</td>\n  </tr>\n</table>\n\n\nFor example:\n```json\n[\n  {\n    \"page\": \"./src/MainApp\",\n    \"routes\": [\n      {\n        \"path\": \"/\",\n        \"page\": \"./src/Search\",\n        \"exact\": true\n      },\n      {\n        \"path\": \"/search\",\n        \"page\": \"./src/Search\",\n        \"routes\": [\n          {\n            \"path\": \"/search/hello\",\n            \"page\": \"./src/Detail\",\n            \"exact\": true\n          }\n        ]\n      },\n      {\n        \"path\": \"/detail\",\n        \"page\": \"./src/Detail\",\n        \"exact\": true\n      }\n    ]\n  }\n]\n```\n\n## Dynamic Import Component\nMaleo.js supports TC39 [dynamic import proposal](https://github.com/tc39/proposal-dynamic-import) for JavaScript.\n\nYou can think dynamic import as another way to split your code into manageable chunks. You can use our `Dynamic` function which utilizes [react loadable](https://github.com/jamiebuilds/react-loadable)\n\nFor Example\n```js\n// DynamicComponent.js\nimport Dynamic from '@airy/maleo/dynamic';\n\nexport default Dynamic({\n  loader: () => import( /* webpackChunkName:\"DynamicComponent\" */ './component/DynamicLoad'),\n})\n```\n\n### **Preloading**\n\nFor optimization purposes, you can also preload a component even before the component got rendered.\n\nFor example, if you want to load component when a button get pressed, you can start preloading the component when the user hovers the mouse over the button.\n\nThe component created by `Dynamic` exposes a [static `preload` method](https://github.com/jamiebuilds/react-loadable#loadablecomponentpreload).\n\n```jsx\nimport React from 'react';\nimport Dynamic from '@airy/maleo/dynamic';\n\nconst DynamicBar = Dynamic({\n  loader: () => import('./Bar'),\n  loading: LoadingCompoinent\n});\n\nclass MyComponent extends React.Component {\n  state = { showBar: false };\n\n  onClick = () => {\n    this.setState({ showBar: true });\n  };\n\n  onMouseOver = () => DynamicBar.preload();\n\n  render() {\n    return (\n      <div>\n        <button\n          onClick={this.onClick}\n          onMouseOver={this.onMouseOver}>\n          Show Bar\n        </button>\n        { this.state.showBar && <DynamicBar /> }\n      </div>\n    )\n  }\n}\n```\n\n## Customizable Component\n\n### Custom Document\n\nHighly inspired by what [Next.js](https://github.com/zeit/next.js) has done on their awesome template customization.\n\nMaleo.js also enable customization on `Document` as document's markup. So you don't need to include tags like `<html>`, `<head>`, `<body>`, etc. \n\nTo override the default behavior, you'll need to create a component that extends the `Document` React class provided by Maleo.\n\n```jsx\n// document.jsx\nimport React from 'react';\nimport { Document, Header, Main, Scripts } from '@airy/maleo/document';\n\nexport default class extends Document {\n  render() {\n    return (\n      <html>\n        <Header>\n          <title>Maleo JS</title>\n          <meta charset=\"utf-8\" />\n          <meta name=\"description\" content=\"Maleo.js is awesome!\" />\n\n          <style>\n            {` body { background-color: #fff } `}\n          </style>\n        </Header>\n\n        <body>\n          <Main />\n\n          <Scripts />\n\n          <ReduxScript />\n        </body>\n      </html>\n    );\n  }\n}\n```\n\n### Custom Wrap\n\nMaleo.js uses the `Wrap` component to initialize pages. `Wrap` contains React Router's Component. You can add HoC here to wrap the application and control the page initialization. Which allows you to do amazing things like:\n- Persisting layour between page changes\n- Keeping state when navigating pages\n- Custom error handling using `componentDidCatch`\n- Inject additional data into pages (like Redux provider, etc)\n\nTo override the default behavior, you'll need to create a component that extends the `Wrap` React class provided by Maleo.\n\n```jsx\n// wrap.jsx\nimport React from 'react';\nimport { Wrap } from '@airy/maleo/wrap';\n\n// Redux plugin for Maleo.js\n// Hoc that creates store and add Redux Provider\nimport { withRedux } from '@airy/maleo-redux-plugin';\n\n// Custom Wrapper that will be rendered for the whole Application\nimport Content from './component/Content';\nimport NavigationBar from './component/NavigationBar';\n\nimport { createStore } from './store';\n\n@withRedux(createStore)\nexport default class extends Wrap {\n  static getInitialProps = (ctx) => {\n    const { store } = ctx\n    // you receive store from context\n    // you can access or do something with the store here\n    console.log('Initialized Redux Store', store);\n    return {}\n  }\n\n  render() {\n    const { Container, containerProps, App, appProps } = this.props\n\n    return (\n      <Container {...containerProps}>\n        <NavigationBar />\n        <Content>\n          <App {...appProps}/>\n        </Content>\n      </Container>\n    )\n  }\n}\n\n```\nIf you put `document.jsx` and `wrap.jsx` on root directory (the same level with `package.json`), then Maleo will automatically register your custom Document and Wrap. Otherwise, you can add the path to your custom Document and Wrap on [Maleo config](#custom-configuration)\n\n---\n***We are also working on adding default and customizable `Error` component page***\n\n---\n\n## Custom Configuration\n\nFor more advanced configuration of Maleo.js, like `webpack` config, registering `plugins`, path to your routes, custom Document and Wrap, and adding `path alias`, you can create a `maleo.config.js` in the root of your project directory. (same directory with `package.json`)\n\n```js\n// maleo.config.js\n\nmodule.exports = {\n  /* config options here */\n}\n```\n\nHere are the API's for the configuration:\n\n<table>\n  <tr>\n    <td>Key</td>\n    <td>Type</td>\n    <td>Description</td>\n  </tr>\n  <tr>\n    <td><code>buildDir</code></td>\n    <td><code>String?</code> [<code>.maleo</code>]</td>\n    <td>Directory to put Maleo.js' build assets</td>\n  </tr>\n  <tr>\n    <td><code>cache</code></td>\n    <td><code>Boolean?</code> [<code>true</code>]</td>\n    <td>Enable webpack build caching</td>\n  </tr>\n  <tr>\n    <td><code>isDev</code></td>\n    <td><code>Boolean?</code> [<code>process.env.NODE_ENV === 'development'</code>]</td>\n    <td>Enable development build configuration</td>\n  </tr>\n  <tr>\n    <td><code>sourceMaps</code></td>\n    <td><code>Boolean?</code> [<code>true</code>]</td>\n    <td>Enable webpack to generate source maps</td>\n  </tr>\n  <tr>\n    <td><code>alias</code></td>\n    <td><code>Object?</code></td>\n    <td>A key value pair for aliasing path directory\n      <br>\n      <code>\n      { 'component': './src/component' }\n      </code>\n    </td>\n  </tr>\n  <tr>\n    <td><code>publicPath</code></td>\n    <td><code>String?</code> [<code>/_assets/</code>]</td>\n    <td>To customize webpack's <code>publicPath</code> \n    <br>Comes in handy if using CDN to put built assets</td>\n  </tr>\n  <tr>\n    <td><code>analyzeBundle</code></td>\n    <td><code>Boolean?</code> [<code>false</code>]</td>\n    <td>To enable webpack's bundle analyzer, for analyzing bundle sizes during bundle debugging should Maleo.js' build process got slow</td>\n  </tr>\n  <tr>\n    <td><code>webpack</code></td>\n    <td><code>Function?</code></td>\n    <td>To customize webpack configuration, more details <a href=\"#customize-webpack\">here</a></td>\n  </tr>\n  <tr>\n    <td><code>routes</code></td>\n    <td><code>string?</code> [<code>rootDir/routes.jsx</code>]</td>\n    <td>Path to your routes file</td>\n  </tr>\n  <tr>\n    <td><code>customDocument</code></td>\n    <td><code>string?</code> [<code>rootDir/document.jsx</code>]</td>\n    <td>Path to your custom document file</td>\n  </tr>\n  <tr>\n    <td><code>customWrap</code></td>\n    <td><code>string?</code> [<code>rootDir/wrap.jsx</code>]</td>\n    <td>Path to your custom wrap file</td>\n  </tr>\n  <tr>\n    <td><code>customApp</code></td>\n    <td><code>string?</code> [<code>rootDir/app.jsx</code>]</td>\n    <td>Path to your custom app file</td>\n  </tr>\n</table>\n\n#### Customize Server\n\nCreate a `server.js` file on root directory where your `package.json` lives.\nHere you can customize Maleo's server.\n```js\nimport { Server } from '@airy/maleo/server';\nimport path from 'path';\n\nimport routeConfig from './routes';\n\nconst PORT = process.env.PORT || 8080;\n\nconst maleoServer = Server.init({\n  port: PORT,\n});\n\nmaleoServer.run(() => {\n  console.log('Server running on port :', PORT);\n});\n```\n\nHere are the API's for the configuration:\n\n<table>\n  <tr>\n    <td>Key</td>\n    <td>Type</td>\n    <td>Description</td>\n  </tr>\n  <tr>\n    <td><code>port</code></td>\n    <td><code>Number?</code> [<code>3000</code>]</td>\n    <td>Port to run Maleo server</td>\n  </tr>\n  <tr>\n    <td><code>assetDir</code></td>\n    <td><code>String?</code> [<code>'<root>/.maleo/client'</code>]</td>\n    <td>Directory for all client related assets</td>\n  </tr>\n</table>\n\n#### Customize Webpack\n\nYou are able to extend Maleo.js' default webpack configuration by defining a function on `maleo.config.js`\n\n```js\n// maleo.config.js\n\nmodule.exports = {\n  webpack(config, context, next) {\n    // Perform customizations to webpack config\n    // Important: This call is required in order to let Maleo pass the config to webpack\n    return next(); \n  },\n};\n```\n\nWebpack function will receive three arguments:\n\n<table>\n  <tr>\n    <td>Argument</td>\n    <td>Details</td>\n  <tr>\n  <tr>\n    <td><code>config</code></td>\n    <td>This contains webpack configuration object that you can manipulate</td>\n  <tr>\n  <tr>\n    <td><code>context</code></td>\n    <td>This contains some keys that are useful for the build context\n      <br>\n      <table>\n        <tr>\n          <td><code>isDev</code></td>\n          <td><code>Boolean</code></td>\n          <td>Check if current build is for development</td>\n        </tr>\n        <tr>\n          <td><code>publicPath</code></td>\n          <td><code>String</code></td>\n          <td>Public Path of user defined or default's value</td>\n        </tr>\n        <tr>\n          <td><code>analyzeBundle</code></td>\n          <td><code>Boolean</code></td>\n          <td>Check if analyze bundle is enabled</td>\n        </tr>\n        <tr>\n          <td><code>buildDirectory</code></td>\n          <td><code>String</code></td>\n          <td>Build Directory of user defined or default's value</td>\n        </tr>\n        <tr>\n          <td><code>name</code></td>\n          <td><code>String</code></td>\n          <td>Build name <code>'server' || 'client'</code></td>\n        </tr>\n      </table>\n    </td>\n  <tr>\n  <tr>\n    <td><code>next</code></td>\n    <td>A callback required to be called to pass the custom configuration</td>\n  <tr>\n</table>\n\nExample of adding `ts-loader` through `maleo.config.js`:\n\n```js\n// maleo.config.js\n\n// Partly taken and modified from @airy/maleo-ts-plugin source\n// for simplicity purposes\n\nmodule.exports = {\n  webpack(config, context, next) {\n    const { isDev } = context\n\n    config.module.rules.push({\n      test: /\\.tsx?/,\n      exclude: /node_modules/,\n      use: [\n        require.resolve('@airy/maleo/lib/build/loaders/maleo-babel-loader'),\n        {\n          loader: 'ts-loader',\n          options: {\n            transpileOnly: true,\n          },\n        },\n      ],\n\n      if (isDev) {\n        config.plugins.push(new ForkTSCheckerWebpackPlugin());\n      }\n\n      return next();\n    })\n  },\n};\n```\n\n#### Customize Babel Config\n\nMaleo.js also let you have your own babel config. Just simply add `.babelrc` file at the root directory of your app.\n\nYou can include Maleo.js' babel preset in order to have latest JavaScript preset.\n\nHere's an example of `.babelrc` file:\n```json\n{\n  \"presets\": [\"@airy/maleo/babel\"],\n  \"plugins\": []\n}\n```\n\nThe `@airy/maleo/babel` preset includes everything you need to get your development started. The preset includes:\n- `@babel/preset-env`\n- `@babel/preset-react`\n- `@babel/plugin-proposal-class-properties`\n- `@babel/plugin-proposal-decorators`\n- `@babel/plugin-proposal-object-rest-spread`\n- `@babel/plugin-transform-runtime`\n- `react-loadable/babel`\n\n## Core Utilities Functions\n\n#### Route Matching\n\nTo use you can import the matching routes function like this:\n```js\nimport { getMatchedRoutes } from '@airy/maleo/utils';\n```\n\nThe function will return all matched routes and the route object from `routes.json`\n\n## CDN Support\n\nIf you are using a CDN, you can set up the `publicPath` setting and configure your CDN's origin to resolve to the domain that Maleo.js is hosted on.\n\n```js\n// maleo.config.js\n\nconst isProd = process.env.NODE_ENV === 'production';\n\nmodule.exports = {\n  assetPrefix: isProd && 'https://cdn.example.com';\n}\n```\n\n## FAQ\n\n== TO BE DETERMINED == \n\n## Plugins\n- [css-plugin](https://github.com/airyrooms/maleo-plugins/tree/master/packages/css-plugin)\n- [redux-plugin](https://github.com/airyrooms/maleo-plugins/tree/master/packages/redux-plugin)\n- [typescript-plugin](https://github.com/airyrooms/maleo-plugins/tree/master/packages/typescript-plugin)\n- [more](https://github.com/airyrooms/maleo-plugins)\n\n\n## Contributing\n[Please follow these steps to contribute to Maleo.js](https://github.com/airyrooms/maleo.js/blob/canary/CONTRIBUTING.md)\n\n[Please follow these steps to contribute to Maleo.js' plugins](https://github.com/airyrooms/maleo-plugins#contributing-guidelines)\n\n\n## Contributors\n\nThis project exists thanks to all the people who contribute. [Contribute](CONTRIBUTING.md).\n\nMany thanks to our **[contributors](https://github.com/airyrooms/maleo.js/graphs/contributors)**!\n\n\n## License\n\nMIT","readmeFilename":"README.md","bugs":{"url":"https://github.com/airyrooms/maleo.js/issues"},"homepage":"https://github.com/airyrooms/maleo.js#readme","_id":"@airy/maleo@0.3.1-canary.11","_nodeVersion":"11.15.0","_npmVersion":"lerna/3.13.1/node@v11.15.0+x64 (linux)","dist":{"integrity":"sha512-/qMK5pGeYdntvHatk8PBDhROyWTGH3yNo07ksXxqyO3RhoRj2xSwR6koQY48C+jUgdNMju7BQC/wzl0oGAexfA==","shasum":"23cf5c6d952d4c624eb7c22d9b56c820635c0f53","tarball":"https://registry.npmjs.org/@airy/maleo/-/maleo-0.3.1-canary.11.tgz","fileCount":96,"unpackedSize":327404,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJdAJ7dCRA9TVsSAnZWagAAkcAP/32LMnmtD14P02d/x79t\n2vOZpKGQI+oDxMCwBLbYb90CvYTRzGshfkkt0cUIYiYNrhwGpJs5psOubg4Y\nb0Ifx/ReQ/tr5Ov12DXnDH3t7PIPo0NctQCBiMRgTK8Sq/dk+GYSSy8op49k\nBalZ9Z60Y3kuvKjpiPF9CUbQr81DijtysER2t2b/rwCOlX5azMs2d0p3mM+4\nr9E8JqeW6roCZzZQ2dFxJKztPxNt5ohW3Q3GNigDqUJ1LsKgUTcUp+5kSeF6\nfol+l3jbq8PzptYXe5holnKSdFBvc/kj6Y02eQCWx3WUog0XhwRVdLAKWL3i\nbf6/1y5du2M1OkUPtpq/Qd9k+HKkIS6/GzPCjeV8cYLsZkzlVyB2i/ZIVme+\nL6XkiPidX63mzkz/I6DA1yma5n2ZjnONlkB8+qxaKuRWAIGGqwOV1nRl6sut\nHXnOlMTePjKMv8bJdkMNfiZdhqRVQ2ocwosMZrpRiMuj4U+3n6OlPueAezpt\nyu32PJZ/WzWqKsBsy6xVw6hNm6XIs7XsWa8UYNFqBS0OHnvvz9ygHsqQ87CR\nSNbe9jyJ0RYhvH+jh7GBRF/dtU/4n+8frQ4GKHponQY4Kwcw9INw4vutwBIe\nm1FMF46ylTiqlPo0Gt/n1qlx0iXV4mYUjoJUJoLWe5shsU8aNHWo32OX/V7a\nt6zm\r\n=v2lf\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIH9IONtGgPXvseLFCKf1wpGwMfk/UCTnt5GySg5MpL8yAiBKGDro6+/Io8rQyXfeZAnhhxaxBy5w5WiRh7IfASJadQ=="}]},"maintainers":[{"name":"airyrooms-engineering","email":"engineering@airyrooms.com"}],"_npmUser":{"name":"airyrooms-engineering","email":"engineering@airyrooms.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/maleo_0.3.1-canary.11_1560321756663_0.4659533957808195"},"_hasShrinkwrap":false},"0.3.1-canary.12":{"name":"@airy/maleo","version":"0.3.1-canary.12","description":"Un-opinionated Universal Rendering Javascript Framework","repository":{"type":"git","url":"git+https://github.com/airyrooms/maleo.js.git","bugs":"https://github.com/airyrooms/maleo.js/issues"},"author":{"name":"Airy"},"license":"MIT","private":false,"sideEffects":false,"scripts":{"build":"rm -rf dist && gulp","watch":"gulp watch","prepublish":"yarn build","start":"node server.js"},"bin":{"maleo":"lib/bin/maleo.js"},"dependencies":{"@babel/core":"7.1.2","@babel/plugin-proposal-class-properties":"7.1.0","@babel/plugin-proposal-decorators":"7.1.2","@babel/plugin-proposal-object-rest-spread":"7.0.0","@babel/plugin-syntax-dynamic-import":"7.0.0","@babel/plugin-transform-runtime":"7.1.0","@babel/preset-env":"7.1.0","@babel/preset-react":"7.0.0","@babel/runtime":"7.1.2","@babel/runtime-corejs2":"7.2.0","babel-loader":"8.0.4","case-sensitive-paths-webpack-plugin":"2.1.2","compression":"1.7.4","es6-promise":"4.2.5","eventsource":"1.0.7","express":"4.16.4","figlet":"1.2.1","friendly-errors-webpack-plugin":"1.7.0","hard-source-webpack-plugin":"0.13.1","helmet":"3.14.0","hoist-non-react-statics":"^3.3.0","isomorphic-fetch":"2.2.1","loader-utils":"1.1.0","node-notifier":"5.2.1","nodemon":"1.18.4","react-dom":"16.8.4","react-helmet":"5.2.0","react-loadable":"5.5.0","react-router-config":"5.0.0","react-router-dom":"5.0.0","rimraf":"2.6.3","source-map-support":"0.5.9","terser-webpack-plugin":"1.1.0","webpack":"4.29.0","webpack-bundle-analyzer":"3.0.3","webpack-cli":"3.1.2","webpack-dev-middleware":"3.4.0","webpack-hot-middleware":"2.24.3","webpackbar":"2.6.3"},"peerDependencies":{"react":"^16.8.4"},"devDependencies":{"@babel/preset-typescript":"^7.1.0","@types/webpack-bundle-analyzer":"^2.13.0","babel-plugin-module-resolver":"^3.1.1","gulp":"github:gulpjs/gulp#4.0","gulp-babel":"^8.0.0","gulp-clean":"^0.4.0","gulp-sourcemaps":"^2.6.4","gulp-uglify":"^3.0.1","gulp-watch":"^5.0.1","react":"^16.8.4"},"gitHead":"55203e2f06653601383fd46bea88ed9d372124aa","readme":"[![Build Status](https://travis-ci.org/airyrooms/maleo.js.svg?branch=master)](https://travis-ci.org/airyrooms/maleo.js)\n[![Coverage Status](https://coveralls.io/repos/github/airyrooms/maleo.js/badge.svg?branch=master)](https://coveralls.io/github/airyrooms/maleo.js?branch=master)\n[![npm version](https://badge.fury.io/js/%40airy%2Fmaleo.svg)](https://badge.fury.io/js/%40airy%2Fmaleo)\n[![Issues](http://img.shields.io/github/issues/airyrooms/maleo.js.svg)]( https://github.com/airyrooms/maleo.js/issues)\n[![MIT license](https://img.shields.io/badge/License-MIT-blue.svg)](https://github.com/airyrooms/maleo.js/blob/canary/LICENSE)\n[![Discord Chat](https://img.shields.io/discord/550498214789513218.svg)](https://discord.gg/9eArCQn)\n[![Conventional Commits](https://img.shields.io/badge/Conventional%20Commits-1.0.0-green.svg)](https://conventionalcommits.org)\n\n\n# Welcome to Maleo.js\n\nMaleo.js is an un-opinionated framework to enable Universal Rendering in JavaScript using React with no hassle.\n\nWe are here to solve the time consuming setups Universal Rendering Required.\n\n---\n\nReadme below is the documentation for the `canary` (prerelease) branch. To view the documentation for the latest stable Maleo.js version change branch to `master`\n\n---\n\n# Table of Contents\n- [Features](#features)\n- [Setup](#setup)\n- [Component Lifecycle](#component-lifecycle)\n- [Routing](#routing)\n- [Dynamic Import Component](#dynamic-import-component)\n  - [Tips](#tips)\n  - [Preloading](#preloading)\n- [Customizable Component](#customizable-component)\n  - [Custom Document](#custom-document)\n  - [Custom Wrap](#custom-wrap)\n- [Custom Configuration](#custom-configuration)\n  - [Customize Server](#customize-server)\n  - [Customize Webpack](#customize-webpack)\n  - [Customize Babel Config](#customize-babel-config)\n- [Core Utilities Functions](#core-utilities-functions)\n- [CDN Support](#cdn-support)\n- [Plugins](#plugins)\n- [FAQ](#faq)\n- [Contributing](#contributing)\n\n---\n\n## Features\n- Universal Rendering\n- Plugin based framework\n- Customizable\n\n## Setup\n\nInstall Maleo.js \n\n>*for now change `@airy/maleo` to `@airy/maleo@canary` until we publish the stable version of this package*\n\n\n**NPM**\n```bash\n$ npm install --save @airy/maleo react\n```\n**Yarn**\n```bash\n$ yarn add @airy/maleo react\n```\n\nAdd this script to your `package.json`\n```json\n{\n  \"scripts\": {\n    \"dev\": \"maleo dev\",\n    \"build\": \"export NODE_ENV=production && maleo build\",\n    \"start\": \"export NODE_ENV=production && maleo run\"\n  }\n}\n```\n\nCreate a page Root component\n```jsx\n// ./src/Root.jsx\nimport React from 'react';\n\n// Export default is required for registering page\nexport default class RootComponent extends React.Component {\n  render() {\n    return (\n      <h1>Hello World!</h1>\n    )\n  }\n}\n```\n\nAnd lastly, create a routing file on your project root directory called `routes.json` and register your page component\n```json\n[\n  {\n    \"path\": \"/\",\n    \"page\": \"./src/Root\"\n  }\n]\n```\n\nAfter that you can now run `$ npm run dev` and go to `http://localhost:3000`.\n\nYou should now see your app running on your browser.\n\nBy now you should see \n- Automatic transpilation and bundling (with webpack and babel)\n- ~~Hot code reloading~~ [#17](https://github.com/airyrooms/maleo.js/issues/17)\n- Server rendering\n\nTo see how simple this is, check out the sample app!\n\n## Component Lifecycle \n\nMaleo.js added a new component lifecycle hook called `getInitialProps`, this function is called during Server Side Rendering (SSR) and during route changes on Client Side Rendering (CSR).\n\nThis is useful especially for SEO purposes. \n\nExample for stateful component:\n```jsx\nimport React from 'react';\n\nexport default class extends React.Component {\n  static getInitialProps = async (ctx) => {\n    const { req } = ctx;\n\n    // check if getInitialProps is called on server or on client\n    const userAgent = req ? req.headers['user-agent'] : navigator.userAgent;\n\n    // the return value will be passed as props for this component\n    return { userAgent };\n  }\n\n  render() {\n    return (\n      <div>\n        Hello World {this.props.userAgent}\n      </div>\n    );\n  }\n}\n```\n\nExample for stateless component:\n```jsx\nconst Component = (props) => (\n  <div>\n    Hello World {props.userAgent}\n  </div>\n);\n\nComponent.getInitialprops = async (ctx) => {\n  const { req } = ctx;\n\n  const userAgent = req ? req.headers['user-agent'] : navigator.userAgent;\n\n  return { userAgent };\n};\n\nexport default Component;\n```\n\n`getInitialProps` receives a context object with the following properties:\n- `req` - HTTP request object (server only)\n- `res` - HTTP response object (server only)\n- `...wrapProps` - Spreaded properties from custom Wrap\n- `...appProps` - Spreaded properties from custom App\n\n## Routing\n\nRouting is declared in a centralized route config.\nRegister all the route config in `routes.json` file.\n\nIf you put the `routes.json` files on root directory, Maleo will automatically register your route. Otherwise put path to your routes on [Maleo config](#custom-configuration).\n\nRoutes file has to export default the route configuration.\nThe route object **expected to have distinct key** to indicate the route.\n\n<table>\n  <tr>\n    <td>Key</td>\n    <td>Type</td>\n    <td>Description</td>\n  </tr>\n  <tr>\n    <td><code>path</code></td>\n    <td><code>String!</code></td>\n    <td>Routes path</td>\n  </tr>\n  <tr>\n    <td><code>page</code></td>\n    <td><code>String!</code></td>\n    <td>Path to React Component for this route path</td>\n  </tr>\n  <tr>\n    <td><code>exact</code></td>\n    <td><code>Boolean?</code> [<code>false</code>]</td>\n    <td>To make url has to match exactly the path in order to render the component. Give <code>false</code> value if the route is a wrapper route component</td>\n  </tr>\n  <tr>\n    <td><code>routes</code></td>\n    <td><code>RouteObject?</code></td>\n    <td>Nested route</td>\n  </tr>\n</table>\n\n\nFor example:\n```json\n[\n  {\n    \"page\": \"./src/MainApp\",\n    \"routes\": [\n      {\n        \"path\": \"/\",\n        \"page\": \"./src/Search\",\n        \"exact\": true\n      },\n      {\n        \"path\": \"/search\",\n        \"page\": \"./src/Search\",\n        \"routes\": [\n          {\n            \"path\": \"/search/hello\",\n            \"page\": \"./src/Detail\",\n            \"exact\": true\n          }\n        ]\n      },\n      {\n        \"path\": \"/detail\",\n        \"page\": \"./src/Detail\",\n        \"exact\": true\n      }\n    ]\n  }\n]\n```\n\n## Dynamic Import Component\nMaleo.js supports TC39 [dynamic import proposal](https://github.com/tc39/proposal-dynamic-import) for JavaScript.\n\nYou can think dynamic import as another way to split your code into manageable chunks. You can use our `Dynamic` function which utilizes [react loadable](https://github.com/jamiebuilds/react-loadable)\n\nFor Example\n```js\n// DynamicComponent.js\nimport Dynamic from '@airy/maleo/dynamic';\n\nexport default Dynamic({\n  loader: () => import( /* webpackChunkName:\"DynamicComponent\" */ './component/DynamicLoad'),\n})\n```\n\n### **Preloading**\n\nFor optimization purposes, you can also preload a component even before the component got rendered.\n\nFor example, if you want to load component when a button get pressed, you can start preloading the component when the user hovers the mouse over the button.\n\nThe component created by `Dynamic` exposes a [static `preload` method](https://github.com/jamiebuilds/react-loadable#loadablecomponentpreload).\n\n```jsx\nimport React from 'react';\nimport Dynamic from '@airy/maleo/dynamic';\n\nconst DynamicBar = Dynamic({\n  loader: () => import('./Bar'),\n  loading: LoadingCompoinent\n});\n\nclass MyComponent extends React.Component {\n  state = { showBar: false };\n\n  onClick = () => {\n    this.setState({ showBar: true });\n  };\n\n  onMouseOver = () => DynamicBar.preload();\n\n  render() {\n    return (\n      <div>\n        <button\n          onClick={this.onClick}\n          onMouseOver={this.onMouseOver}>\n          Show Bar\n        </button>\n        { this.state.showBar && <DynamicBar /> }\n      </div>\n    )\n  }\n}\n```\n\n## Customizable Component\n\n### Custom Document\n\nHighly inspired by what [Next.js](https://github.com/zeit/next.js) has done on their awesome template customization.\n\nMaleo.js also enable customization on `Document` as document's markup. So you don't need to include tags like `<html>`, `<head>`, `<body>`, etc. \n\nTo override the default behavior, you'll need to create a component that extends the `Document` React class provided by Maleo.\n\n```jsx\n// document.jsx\nimport React from 'react';\nimport { Document, Header, Main, Scripts } from '@airy/maleo/document';\n\nexport default class extends Document {\n  render() {\n    return (\n      <html>\n        <Header>\n          <title>Maleo JS</title>\n          <meta charset=\"utf-8\" />\n          <meta name=\"description\" content=\"Maleo.js is awesome!\" />\n\n          <style>\n            {` body { background-color: #fff } `}\n          </style>\n        </Header>\n\n        <body>\n          <Main />\n\n          <Scripts />\n\n          <ReduxScript />\n        </body>\n      </html>\n    );\n  }\n}\n```\n\n### Custom Wrap\n\nMaleo.js uses the `Wrap` component to initialize pages. `Wrap` contains React Router's Component. You can add HoC here to wrap the application and control the page initialization. Which allows you to do amazing things like:\n- Persisting layour between page changes\n- Keeping state when navigating pages\n- Custom error handling using `componentDidCatch`\n- Inject additional data into pages (like Redux provider, etc)\n\nTo override the default behavior, you'll need to create a component that extends the `Wrap` React class provided by Maleo.\n\n```jsx\n// wrap.jsx\nimport React from 'react';\nimport { Wrap } from '@airy/maleo/wrap';\n\n// Redux plugin for Maleo.js\n// Hoc that creates store and add Redux Provider\nimport { withRedux } from '@airy/maleo-redux-plugin';\n\n// Custom Wrapper that will be rendered for the whole Application\nimport Content from './component/Content';\nimport NavigationBar from './component/NavigationBar';\n\nimport { createStore } from './store';\n\n@withRedux(createStore)\nexport default class extends Wrap {\n  static getInitialProps = (ctx) => {\n    const { store } = ctx\n    // you receive store from context\n    // you can access or do something with the store here\n    console.log('Initialized Redux Store', store);\n    return {}\n  }\n\n  render() {\n    const { Container, containerProps, App, appProps } = this.props\n\n    return (\n      <Container {...containerProps}>\n        <NavigationBar />\n        <Content>\n          <App {...appProps}/>\n        </Content>\n      </Container>\n    )\n  }\n}\n\n```\nIf you put `document.jsx` and `wrap.jsx` on root directory (the same level with `package.json`), then Maleo will automatically register your custom Document and Wrap. Otherwise, you can add the path to your custom Document and Wrap on [Maleo config](#custom-configuration)\n\n---\n***We are also working on adding default and customizable `Error` component page***\n\n---\n\n## Custom Configuration\n\nFor more advanced configuration of Maleo.js, like `webpack` config, registering `plugins`, path to your routes, custom Document and Wrap, and adding `path alias`, you can create a `maleo.config.js` in the root of your project directory. (same directory with `package.json`)\n\n```js\n// maleo.config.js\n\nmodule.exports = {\n  /* config options here */\n}\n```\n\nHere are the API's for the configuration:\n\n<table>\n  <tr>\n    <td>Key</td>\n    <td>Type</td>\n    <td>Description</td>\n  </tr>\n  <tr>\n    <td><code>buildDir</code></td>\n    <td><code>String?</code> [<code>.maleo</code>]</td>\n    <td>Directory to put Maleo.js' build assets</td>\n  </tr>\n  <tr>\n    <td><code>cache</code></td>\n    <td><code>Boolean?</code> [<code>true</code>]</td>\n    <td>Enable webpack build caching</td>\n  </tr>\n  <tr>\n    <td><code>isDev</code></td>\n    <td><code>Boolean?</code> [<code>process.env.NODE_ENV === 'development'</code>]</td>\n    <td>Enable development build configuration</td>\n  </tr>\n  <tr>\n    <td><code>sourceMaps</code></td>\n    <td><code>Boolean?</code> [<code>true</code>]</td>\n    <td>Enable webpack to generate source maps</td>\n  </tr>\n  <tr>\n    <td><code>alias</code></td>\n    <td><code>Object?</code></td>\n    <td>A key value pair for aliasing path directory\n      <br>\n      <code>\n      { 'component': './src/component' }\n      </code>\n    </td>\n  </tr>\n  <tr>\n    <td><code>publicPath</code></td>\n    <td><code>String?</code> [<code>/_assets/</code>]</td>\n    <td>To customize webpack's <code>publicPath</code> \n    <br>Comes in handy if using CDN to put built assets</td>\n  </tr>\n  <tr>\n    <td><code>analyzeBundle</code></td>\n    <td><code>Boolean?</code> [<code>false</code>]</td>\n    <td>To enable webpack's bundle analyzer, for analyzing bundle sizes during bundle debugging should Maleo.js' build process got slow</td>\n  </tr>\n  <tr>\n    <td><code>webpack</code></td>\n    <td><code>Function?</code></td>\n    <td>To customize webpack configuration, more details <a href=\"#customize-webpack\">here</a></td>\n  </tr>\n  <tr>\n    <td><code>routes</code></td>\n    <td><code>string?</code> [<code>rootDir/routes.jsx</code>]</td>\n    <td>Path to your routes file</td>\n  </tr>\n  <tr>\n    <td><code>customDocument</code></td>\n    <td><code>string?</code> [<code>rootDir/document.jsx</code>]</td>\n    <td>Path to your custom document file</td>\n  </tr>\n  <tr>\n    <td><code>customWrap</code></td>\n    <td><code>string?</code> [<code>rootDir/wrap.jsx</code>]</td>\n    <td>Path to your custom wrap file</td>\n  </tr>\n  <tr>\n    <td><code>customApp</code></td>\n    <td><code>string?</code> [<code>rootDir/app.jsx</code>]</td>\n    <td>Path to your custom app file</td>\n  </tr>\n</table>\n\n#### Customize Server\n\nCreate a `server.js` file on root directory where your `package.json` lives.\nHere you can customize Maleo's server.\n```js\nimport { Server } from '@airy/maleo/server';\nimport path from 'path';\n\nimport routeConfig from './routes';\n\nconst PORT = process.env.PORT || 8080;\n\nconst maleoServer = Server.init({\n  port: PORT,\n});\n\nmaleoServer.run(() => {\n  console.log('Server running on port :', PORT);\n});\n```\n\nHere are the API's for the configuration:\n\n<table>\n  <tr>\n    <td>Key</td>\n    <td>Type</td>\n    <td>Description</td>\n  </tr>\n  <tr>\n    <td><code>port</code></td>\n    <td><code>Number?</code> [<code>3000</code>]</td>\n    <td>Port to run Maleo server</td>\n  </tr>\n  <tr>\n    <td><code>assetDir</code></td>\n    <td><code>String?</code> [<code>'<root>/.maleo/client'</code>]</td>\n    <td>Directory for all client related assets</td>\n  </tr>\n</table>\n\n#### Customize Webpack\n\nYou are able to extend Maleo.js' default webpack configuration by defining a function on `maleo.config.js`\n\n```js\n// maleo.config.js\n\nmodule.exports = {\n  webpack(config, context, next) {\n    // Perform customizations to webpack config\n    // Important: This call is required in order to let Maleo pass the config to webpack\n    return next(); \n  },\n};\n```\n\nWebpack function will receive three arguments:\n\n<table>\n  <tr>\n    <td>Argument</td>\n    <td>Details</td>\n  <tr>\n  <tr>\n    <td><code>config</code></td>\n    <td>This contains webpack configuration object that you can manipulate</td>\n  <tr>\n  <tr>\n    <td><code>context</code></td>\n    <td>This contains some keys that are useful for the build context\n      <br>\n      <table>\n        <tr>\n          <td><code>isDev</code></td>\n          <td><code>Boolean</code></td>\n          <td>Check if current build is for development</td>\n        </tr>\n        <tr>\n          <td><code>publicPath</code></td>\n          <td><code>String</code></td>\n          <td>Public Path of user defined or default's value</td>\n        </tr>\n        <tr>\n          <td><code>analyzeBundle</code></td>\n          <td><code>Boolean</code></td>\n          <td>Check if analyze bundle is enabled</td>\n        </tr>\n        <tr>\n          <td><code>buildDirectory</code></td>\n          <td><code>String</code></td>\n          <td>Build Directory of user defined or default's value</td>\n        </tr>\n        <tr>\n          <td><code>name</code></td>\n          <td><code>String</code></td>\n          <td>Build name <code>'server' || 'client'</code></td>\n        </tr>\n      </table>\n    </td>\n  <tr>\n  <tr>\n    <td><code>next</code></td>\n    <td>A callback required to be called to pass the custom configuration</td>\n  <tr>\n</table>\n\nExample of adding `ts-loader` through `maleo.config.js`:\n\n```js\n// maleo.config.js\n\n// Partly taken and modified from @airy/maleo-ts-plugin source\n// for simplicity purposes\n\nmodule.exports = {\n  webpack(config, context, next) {\n    const { isDev } = context\n\n    config.module.rules.push({\n      test: /\\.tsx?/,\n      exclude: /node_modules/,\n      use: [\n        require.resolve('@airy/maleo/lib/build/loaders/maleo-babel-loader'),\n        {\n          loader: 'ts-loader',\n          options: {\n            transpileOnly: true,\n          },\n        },\n      ],\n\n      if (isDev) {\n        config.plugins.push(new ForkTSCheckerWebpackPlugin());\n      }\n\n      return next();\n    })\n  },\n};\n```\n\n#### Customize Babel Config\n\nMaleo.js also let you have your own babel config. Just simply add `.babelrc` file at the root directory of your app.\n\nYou can include Maleo.js' babel preset in order to have latest JavaScript preset.\n\nHere's an example of `.babelrc` file:\n```json\n{\n  \"presets\": [\"@airy/maleo/babel\"],\n  \"plugins\": []\n}\n```\n\nThe `@airy/maleo/babel` preset includes everything you need to get your development started. The preset includes:\n- `@babel/preset-env`\n- `@babel/preset-react`\n- `@babel/plugin-proposal-class-properties`\n- `@babel/plugin-proposal-decorators`\n- `@babel/plugin-proposal-object-rest-spread`\n- `@babel/plugin-transform-runtime`\n- `react-loadable/babel`\n\n## Core Utilities Functions\n\n#### Route Matching\n\nTo use you can import the matching routes function like this:\n```js\nimport { getMatchedRoutes } from '@airy/maleo/utils';\n```\n\nThe function will return all matched routes and the route object from `routes.json`\n\n## CDN Support\n\nIf you are using a CDN, you can set up the `publicPath` setting and configure your CDN's origin to resolve to the domain that Maleo.js is hosted on.\n\n```js\n// maleo.config.js\n\nconst isProd = process.env.NODE_ENV === 'production';\n\nmodule.exports = {\n  assetPrefix: isProd && 'https://cdn.example.com';\n}\n```\n\n## FAQ\n\n== TO BE DETERMINED == \n\n## Plugins\n- [css-plugin](https://github.com/airyrooms/maleo-plugins/tree/master/packages/css-plugin)\n- [redux-plugin](https://github.com/airyrooms/maleo-plugins/tree/master/packages/redux-plugin)\n- [typescript-plugin](https://github.com/airyrooms/maleo-plugins/tree/master/packages/typescript-plugin)\n- [more](https://github.com/airyrooms/maleo-plugins)\n\n\n## Contributing\n[Please follow these steps to contribute to Maleo.js](https://github.com/airyrooms/maleo.js/blob/canary/CONTRIBUTING.md)\n\n[Please follow these steps to contribute to Maleo.js' plugins](https://github.com/airyrooms/maleo-plugins#contributing-guidelines)\n\n\n## Contributors\n\nThis project exists thanks to all the people who contribute. [Contribute](CONTRIBUTING.md).\n\nMany thanks to our **[contributors](https://github.com/airyrooms/maleo.js/graphs/contributors)**!\n\n\n## License\n\nMIT","readmeFilename":"README.md","bugs":{"url":"https://github.com/airyrooms/maleo.js/issues"},"homepage":"https://github.com/airyrooms/maleo.js#readme","_id":"@airy/maleo@0.3.1-canary.12","_nodeVersion":"10.16.0","_npmVersion":"lerna/3.13.1/node@v10.16.0+x64 (linux)","dist":{"integrity":"sha512-IFk8j870nmS0VVFG6p+A+FiSUjAEoZza3qJ4qJmqsdnRT4gLlsyPbdfumNC2rQCbFvrGQ3oharRZLrTZe5ObbQ==","shasum":"0a47748ba4308729a322eac16d9181d7e55f73a6","tarball":"https://registry.npmjs.org/@airy/maleo/-/maleo-0.3.1-canary.12.tgz","fileCount":96,"unpackedSize":328518,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJdA4C0CRA9TVsSAnZWagAAZCAP/Au0tlcJ8Jmf7VguMiLp\nd80Wtqtcvyv9A7q7n+UjpPU6ui2lXWGIzi7OKPl0Wwu5NpyDMTgKsw2dVV6O\n8q0njnKHa+fDSJnU8PA7THSt5MmmVQ2x0yZ5Ej/fkmoDA9BeA3ddl6mOldpT\nh4dH1twn+BViRg2Nm+M2hN0JTOj6o1nNYvjoiqcbxNhdgWln3iFpP85gtjLy\n8gf/mJRiCF9TDD1k8Vv2L0hGOjg6ntAX7F4uP9vewLGhFD+8yGi9FhJhWHP9\nh9m5OkWdQm66aUHG3fF9iuVCCU85io4uRltbimP02VSOMfdZWASV1h3YDGbc\nXu8tvOR32UHZ4dkNBezZ4uvDXp7FTyWWIOozveUz224bV7IZI+z4+hqwtYWP\nOxIO7hXh48ncQBnpek8/7rl+7gGlEI1cmGKoEb889ywVX3OX6VT0BdqAUZng\n8sPk2+6yfaiwCk34UifVY/dVgsLxgvUjqt6N1qwANTo8XNIZg2X6SqRs1igq\n6nSOn8UznRXLt1KhjMkrw141U2XhPsloKZeh4LLMBPw8xrXqUmTQw0KsaIyO\nOkdGqs4OEYstukNj1VtDn2Ej9A7LBVW2I8l7KIpCYIo85hDNeSOdzfQAKEEp\nBKgGJklyvr7/NvWF8BbP/nIqBxuNIXPoA66vAixNOSpv3vqKuNUjMBU3r2DA\nGrF8\r\n=J1ta\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIBAApPb6swgsxFO9oVe3oM/B+r4zFF3mEDAH4FSNKvNHAiB+/45iyDGhDKVbQBIYCZZmmc2x8XQCR46Was2wEPjPxA=="}]},"maintainers":[{"name":"airyrooms-engineering","email":"engineering@airyrooms.com"}],"_npmUser":{"name":"airyrooms-engineering","email":"engineering@airyrooms.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/maleo_0.3.1-canary.12_1560510644001_0.6746280749740774"},"_hasShrinkwrap":false},"0.3.1-canary.13":{"name":"@airy/maleo","version":"0.3.1-canary.13","description":"Un-opinionated Universal Rendering Javascript Framework","repository":{"type":"git","url":"git+https://github.com/airyrooms/maleo.js.git","bugs":"https://github.com/airyrooms/maleo.js/issues"},"author":{"name":"Airy"},"license":"MIT","private":false,"sideEffects":false,"scripts":{"build":"rm -rf dist && gulp","watch":"gulp watch","prepublish":"yarn build","start":"node server.js"},"bin":{"maleo":"lib/bin/maleo.js"},"dependencies":{"@babel/core":"7.1.2","@babel/plugin-proposal-class-properties":"7.1.0","@babel/plugin-proposal-decorators":"7.1.2","@babel/plugin-proposal-object-rest-spread":"7.0.0","@babel/plugin-syntax-dynamic-import":"7.0.0","@babel/plugin-transform-runtime":"7.1.0","@babel/preset-env":"7.1.0","@babel/preset-react":"7.0.0","@babel/runtime":"7.1.2","@babel/runtime-corejs2":"7.2.0","babel-loader":"8.0.4","case-sensitive-paths-webpack-plugin":"2.1.2","compression":"1.7.4","es6-promise":"4.2.5","eventsource":"1.0.7","express":"4.16.4","figlet":"1.2.1","friendly-errors-webpack-plugin":"1.7.0","hard-source-webpack-plugin":"0.13.1","helmet":"3.14.0","hoist-non-react-statics":"^3.3.0","isomorphic-fetch":"2.2.1","loader-utils":"1.1.0","node-notifier":"5.2.1","nodemon":"1.18.4","react-dom":"16.8.4","react-helmet":"5.2.0","react-loadable":"5.5.0","react-router-config":"5.0.0","react-router-dom":"5.0.0","rimraf":"2.6.3","source-map-support":"0.5.9","terser-webpack-plugin":"1.1.0","webpack":"4.29.0","webpack-bundle-analyzer":"3.0.3","webpack-cli":"3.1.2","webpack-dev-middleware":"3.4.0","webpack-hot-middleware":"2.24.3","webpackbar":"2.6.3"},"peerDependencies":{"react":"^16.8.4"},"devDependencies":{"@babel/preset-typescript":"^7.1.0","@types/webpack-bundle-analyzer":"^2.13.0","babel-plugin-module-resolver":"^3.1.1","gulp":"github:gulpjs/gulp#4.0","gulp-babel":"^8.0.0","gulp-clean":"^0.4.0","gulp-sourcemaps":"^2.6.4","gulp-uglify":"^3.0.1","gulp-watch":"^5.0.1","react":"^16.8.4"},"gitHead":"47a4a2261f9caddc9f52d1e73dbc2e47f650c20a","readme":"[![Build Status](https://travis-ci.org/airyrooms/maleo.js.svg?branch=master)](https://travis-ci.org/airyrooms/maleo.js)\n[![Coverage Status](https://coveralls.io/repos/github/airyrooms/maleo.js/badge.svg?branch=master)](https://coveralls.io/github/airyrooms/maleo.js?branch=master)\n[![npm version](https://badge.fury.io/js/%40airy%2Fmaleo.svg)](https://badge.fury.io/js/%40airy%2Fmaleo)\n[![Issues](http://img.shields.io/github/issues/airyrooms/maleo.js.svg)]( https://github.com/airyrooms/maleo.js/issues)\n[![MIT license](https://img.shields.io/badge/License-MIT-blue.svg)](https://github.com/airyrooms/maleo.js/blob/canary/LICENSE)\n[![Discord Chat](https://img.shields.io/discord/550498214789513218.svg)](https://discord.gg/9eArCQn)\n[![Conventional Commits](https://img.shields.io/badge/Conventional%20Commits-1.0.0-green.svg)](https://conventionalcommits.org)\n\n\n# Welcome to Maleo.js\n\nMaleo.js is an un-opinionated framework to enable Universal Rendering in JavaScript using React with no hassle.\n\nWe are here to solve the time consuming setups Universal Rendering Required.\n\n---\n\nReadme below is the documentation for the `canary` (prerelease) branch. To view the documentation for the latest stable Maleo.js version change branch to `master`\n\n---\n\n# Table of Contents\n- [Features](#features)\n- [Setup](#setup)\n- [Component Lifecycle](#component-lifecycle)\n- [Routing](#routing)\n- [Dynamic Import Component](#dynamic-import-component)\n  - [Tips](#tips)\n  - [Preloading](#preloading)\n- [Customizable Component](#customizable-component)\n  - [Custom Document](#custom-document)\n  - [Custom Wrap](#custom-wrap)\n- [Custom Configuration](#custom-configuration)\n  - [Customize Server](#customize-server)\n  - [Customize Webpack](#customize-webpack)\n  - [Customize Babel Config](#customize-babel-config)\n- [Core Utilities Functions](#core-utilities-functions)\n- [CDN Support](#cdn-support)\n- [Plugins](#plugins)\n- [FAQ](#faq)\n- [Contributing](#contributing)\n\n---\n\n## Features\n- Universal Rendering\n- Plugin based framework\n- Customizable\n\n## Setup\n\nInstall Maleo.js \n\n>*for now change `@airy/maleo` to `@airy/maleo@canary` until we publish the stable version of this package*\n\n\n**NPM**\n```bash\n$ npm install --save @airy/maleo react\n```\n**Yarn**\n```bash\n$ yarn add @airy/maleo react\n```\n\nAdd this script to your `package.json`\n```json\n{\n  \"scripts\": {\n    \"dev\": \"maleo dev\",\n    \"build\": \"export NODE_ENV=production && maleo build\",\n    \"start\": \"export NODE_ENV=production && maleo run\"\n  }\n}\n```\n\nCreate a page Root component\n```jsx\n// ./src/Root.jsx\nimport React from 'react';\n\n// Export default is required for registering page\nexport default class RootComponent extends React.Component {\n  render() {\n    return (\n      <h1>Hello World!</h1>\n    )\n  }\n}\n```\n\nAnd lastly, create a routing file on your project root directory called `routes.json` and register your page component\n```json\n[\n  {\n    \"path\": \"/\",\n    \"page\": \"./src/Root\"\n  }\n]\n```\n\nAfter that you can now run `$ npm run dev` and go to `http://localhost:3000`.\n\nYou should now see your app running on your browser.\n\nBy now you should see \n- Automatic transpilation and bundling (with webpack and babel)\n- ~~Hot code reloading~~ [#17](https://github.com/airyrooms/maleo.js/issues/17)\n- Server rendering\n\nTo see how simple this is, check out the sample app!\n\n## Component Lifecycle \n\nMaleo.js added a new component lifecycle hook called `getInitialProps`, this function is called during Server Side Rendering (SSR) and during route changes on Client Side Rendering (CSR).\n\nThis is useful especially for SEO purposes. \n\nExample for stateful component:\n```jsx\nimport React from 'react';\n\nexport default class extends React.Component {\n  static getInitialProps = async (ctx) => {\n    const { req } = ctx;\n\n    // check if getInitialProps is called on server or on client\n    const userAgent = req ? req.headers['user-agent'] : navigator.userAgent;\n\n    // the return value will be passed as props for this component\n    return { userAgent };\n  }\n\n  render() {\n    return (\n      <div>\n        Hello World {this.props.userAgent}\n      </div>\n    );\n  }\n}\n```\n\nExample for stateless component:\n```jsx\nconst Component = (props) => (\n  <div>\n    Hello World {props.userAgent}\n  </div>\n);\n\nComponent.getInitialprops = async (ctx) => {\n  const { req } = ctx;\n\n  const userAgent = req ? req.headers['user-agent'] : navigator.userAgent;\n\n  return { userAgent };\n};\n\nexport default Component;\n```\n\n`getInitialProps` receives a context object with the following properties:\n- `req` - HTTP request object (server only)\n- `res` - HTTP response object (server only)\n- `...wrapProps` - Spreaded properties from custom Wrap\n- `...appProps` - Spreaded properties from custom App\n\n## Routing\n\nRouting is declared in a centralized route config.\nRegister all the route config in `routes.json` file.\n\nIf you put the `routes.json` files on root directory, Maleo will automatically register your route. Otherwise put path to your routes on [Maleo config](#custom-configuration).\n\nRoutes file has to export default the route configuration.\nThe route object **expected to have distinct key** to indicate the route.\n\n<table>\n  <tr>\n    <td>Key</td>\n    <td>Type</td>\n    <td>Description</td>\n  </tr>\n  <tr>\n    <td><code>path</code></td>\n    <td><code>String!</code></td>\n    <td>Routes path</td>\n  </tr>\n  <tr>\n    <td><code>page</code></td>\n    <td><code>String!</code></td>\n    <td>Path to React Component for this route path</td>\n  </tr>\n  <tr>\n    <td><code>exact</code></td>\n    <td><code>Boolean?</code> [<code>false</code>]</td>\n    <td>To make url has to match exactly the path in order to render the component. Give <code>false</code> value if the route is a wrapper route component</td>\n  </tr>\n  <tr>\n    <td><code>routes</code></td>\n    <td><code>RouteObject?</code></td>\n    <td>Nested route</td>\n  </tr>\n</table>\n\n\nFor example:\n```json\n[\n  {\n    \"page\": \"./src/MainApp\",\n    \"routes\": [\n      {\n        \"path\": \"/\",\n        \"page\": \"./src/Search\",\n        \"exact\": true\n      },\n      {\n        \"path\": \"/search\",\n        \"page\": \"./src/Search\",\n        \"routes\": [\n          {\n            \"path\": \"/search/hello\",\n            \"page\": \"./src/Detail\",\n            \"exact\": true\n          }\n        ]\n      },\n      {\n        \"path\": \"/detail\",\n        \"page\": \"./src/Detail\",\n        \"exact\": true\n      }\n    ]\n  }\n]\n```\n\n## Dynamic Import Component\nMaleo.js supports TC39 [dynamic import proposal](https://github.com/tc39/proposal-dynamic-import) for JavaScript.\n\nYou can think dynamic import as another way to split your code into manageable chunks. You can use our `Dynamic` function which utilizes [react loadable](https://github.com/jamiebuilds/react-loadable)\n\nFor Example\n```js\n// DynamicComponent.js\nimport Dynamic from '@airy/maleo/dynamic';\n\nexport default Dynamic({\n  loader: () => import( /* webpackChunkName:\"DynamicComponent\" */ './component/DynamicLoad'),\n})\n```\n\n### **Preloading**\n\nFor optimization purposes, you can also preload a component even before the component got rendered.\n\nFor example, if you want to load component when a button get pressed, you can start preloading the component when the user hovers the mouse over the button.\n\nThe component created by `Dynamic` exposes a [static `preload` method](https://github.com/jamiebuilds/react-loadable#loadablecomponentpreload).\n\n```jsx\nimport React from 'react';\nimport Dynamic from '@airy/maleo/dynamic';\n\nconst DynamicBar = Dynamic({\n  loader: () => import('./Bar'),\n  loading: LoadingCompoinent\n});\n\nclass MyComponent extends React.Component {\n  state = { showBar: false };\n\n  onClick = () => {\n    this.setState({ showBar: true });\n  };\n\n  onMouseOver = () => DynamicBar.preload();\n\n  render() {\n    return (\n      <div>\n        <button\n          onClick={this.onClick}\n          onMouseOver={this.onMouseOver}>\n          Show Bar\n        </button>\n        { this.state.showBar && <DynamicBar /> }\n      </div>\n    )\n  }\n}\n```\n\n## Customizable Component\n\n### Custom Document\n\nHighly inspired by what [Next.js](https://github.com/zeit/next.js) has done on their awesome template customization.\n\nMaleo.js also enable customization on `Document` as document's markup. So you don't need to include tags like `<html>`, `<head>`, `<body>`, etc. \n\nTo override the default behavior, you'll need to create a component that extends the `Document` React class provided by Maleo.\n\n```jsx\n// document.jsx\nimport React from 'react';\nimport { Document, Header, Main, Scripts } from '@airy/maleo/document';\n\nexport default class extends Document {\n  render() {\n    return (\n      <html>\n        <Header>\n          <title>Maleo JS</title>\n          <meta charset=\"utf-8\" />\n          <meta name=\"description\" content=\"Maleo.js is awesome!\" />\n\n          <style>\n            {` body { background-color: #fff } `}\n          </style>\n        </Header>\n\n        <body>\n          <Main />\n\n          <Scripts />\n\n          <ReduxScript />\n        </body>\n      </html>\n    );\n  }\n}\n```\n\n### Custom Wrap\n\nMaleo.js uses the `Wrap` component to initialize pages. `Wrap` contains React Router's Component. You can add HoC here to wrap the application and control the page initialization. Which allows you to do amazing things like:\n- Persisting layour between page changes\n- Keeping state when navigating pages\n- Custom error handling using `componentDidCatch`\n- Inject additional data into pages (like Redux provider, etc)\n\nTo override the default behavior, you'll need to create a component that extends the `Wrap` React class provided by Maleo.\n\n```jsx\n// wrap.jsx\nimport React from 'react';\nimport { Wrap } from '@airy/maleo/wrap';\n\n// Redux plugin for Maleo.js\n// Hoc that creates store and add Redux Provider\nimport { withRedux } from '@airy/maleo-redux-plugin';\n\n// Custom Wrapper that will be rendered for the whole Application\nimport Content from './component/Content';\nimport NavigationBar from './component/NavigationBar';\n\nimport { createStore } from './store';\n\n@withRedux(createStore)\nexport default class extends Wrap {\n  static getInitialProps = (ctx) => {\n    const { store } = ctx\n    // you receive store from context\n    // you can access or do something with the store here\n    console.log('Initialized Redux Store', store);\n    return {}\n  }\n\n  render() {\n    const { Container, containerProps, App, appProps } = this.props\n\n    return (\n      <Container {...containerProps}>\n        <NavigationBar />\n        <Content>\n          <App {...appProps}/>\n        </Content>\n      </Container>\n    )\n  }\n}\n\n```\nIf you put `document.jsx` and `wrap.jsx` on root directory (the same level with `package.json`), then Maleo will automatically register your custom Document and Wrap. Otherwise, you can add the path to your custom Document and Wrap on [Maleo config](#custom-configuration)\n\n---\n***We are also working on adding default and customizable `Error` component page***\n\n---\n\n## Custom Configuration\n\nFor more advanced configuration of Maleo.js, like `webpack` config, registering `plugins`, path to your routes, custom Document and Wrap, and adding `path alias`, you can create a `maleo.config.js` in the root of your project directory. (same directory with `package.json`)\n\n```js\n// maleo.config.js\n\nmodule.exports = {\n  /* config options here */\n}\n```\n\nHere are the API's for the configuration:\n\n<table>\n  <tr>\n    <td>Key</td>\n    <td>Type</td>\n    <td>Description</td>\n  </tr>\n  <tr>\n    <td><code>buildDir</code></td>\n    <td><code>String?</code> [<code>.maleo</code>]</td>\n    <td>Directory to put Maleo.js' build assets</td>\n  </tr>\n  <tr>\n    <td><code>cache</code></td>\n    <td><code>Boolean?</code> [<code>true</code>]</td>\n    <td>Enable webpack build caching</td>\n  </tr>\n  <tr>\n    <td><code>isDev</code></td>\n    <td><code>Boolean?</code> [<code>process.env.NODE_ENV === 'development'</code>]</td>\n    <td>Enable development build configuration</td>\n  </tr>\n  <tr>\n    <td><code>sourceMaps</code></td>\n    <td><code>Boolean?</code> [<code>true</code>]</td>\n    <td>Enable webpack to generate source maps</td>\n  </tr>\n  <tr>\n    <td><code>alias</code></td>\n    <td><code>Object?</code></td>\n    <td>A key value pair for aliasing path directory\n      <br>\n      <code>\n      { 'component': './src/component' }\n      </code>\n    </td>\n  </tr>\n  <tr>\n    <td><code>publicPath</code></td>\n    <td><code>String?</code> [<code>/_assets/</code>]</td>\n    <td>To customize webpack's <code>publicPath</code> \n    <br>Comes in handy if using CDN to put built assets</td>\n  </tr>\n  <tr>\n    <td><code>analyzeBundle</code></td>\n    <td><code>Boolean?</code> [<code>false</code>]</td>\n    <td>To enable webpack's bundle analyzer, for analyzing bundle sizes during bundle debugging should Maleo.js' build process got slow</td>\n  </tr>\n  <tr>\n    <td><code>webpack</code></td>\n    <td><code>Function?</code></td>\n    <td>To customize webpack configuration, more details <a href=\"#customize-webpack\">here</a></td>\n  </tr>\n  <tr>\n    <td><code>routes</code></td>\n    <td><code>string?</code> [<code>rootDir/routes.jsx</code>]</td>\n    <td>Path to your routes file</td>\n  </tr>\n  <tr>\n    <td><code>customDocument</code></td>\n    <td><code>string?</code> [<code>rootDir/document.jsx</code>]</td>\n    <td>Path to your custom document file</td>\n  </tr>\n  <tr>\n    <td><code>customWrap</code></td>\n    <td><code>string?</code> [<code>rootDir/wrap.jsx</code>]</td>\n    <td>Path to your custom wrap file</td>\n  </tr>\n  <tr>\n    <td><code>customApp</code></td>\n    <td><code>string?</code> [<code>rootDir/app.jsx</code>]</td>\n    <td>Path to your custom app file</td>\n  </tr>\n</table>\n\n#### Customize Server\n\nCreate a `server.js` file on root directory where your `package.json` lives.\nHere you can customize Maleo's server.\n```js\nimport { Server } from '@airy/maleo/server';\nimport path from 'path';\n\nimport routeConfig from './routes';\n\nconst PORT = process.env.PORT || 8080;\n\nconst maleoServer = Server.init({\n  port: PORT,\n});\n\nmaleoServer.run(() => {\n  console.log('Server running on port :', PORT);\n});\n```\n\nHere are the API's for the configuration:\n\n<table>\n  <tr>\n    <td>Key</td>\n    <td>Type</td>\n    <td>Description</td>\n  </tr>\n  <tr>\n    <td><code>port</code></td>\n    <td><code>Number?</code> [<code>3000</code>]</td>\n    <td>Port to run Maleo server</td>\n  </tr>\n  <tr>\n    <td><code>assetDir</code></td>\n    <td><code>String?</code> [<code>'<root>/.maleo/client'</code>]</td>\n    <td>Directory for all client related assets</td>\n  </tr>\n</table>\n\n#### Customize Webpack\n\nYou are able to extend Maleo.js' default webpack configuration by defining a function on `maleo.config.js`\n\n```js\n// maleo.config.js\n\nmodule.exports = {\n  webpack(config, context, next) {\n    // Perform customizations to webpack config\n    // Important: This call is required in order to let Maleo pass the config to webpack\n    return next(); \n  },\n};\n```\n\nWebpack function will receive three arguments:\n\n<table>\n  <tr>\n    <td>Argument</td>\n    <td>Details</td>\n  <tr>\n  <tr>\n    <td><code>config</code></td>\n    <td>This contains webpack configuration object that you can manipulate</td>\n  <tr>\n  <tr>\n    <td><code>context</code></td>\n    <td>This contains some keys that are useful for the build context\n      <br>\n      <table>\n        <tr>\n          <td><code>isDev</code></td>\n          <td><code>Boolean</code></td>\n          <td>Check if current build is for development</td>\n        </tr>\n        <tr>\n          <td><code>publicPath</code></td>\n          <td><code>String</code></td>\n          <td>Public Path of user defined or default's value</td>\n        </tr>\n        <tr>\n          <td><code>analyzeBundle</code></td>\n          <td><code>Boolean</code></td>\n          <td>Check if analyze bundle is enabled</td>\n        </tr>\n        <tr>\n          <td><code>buildDirectory</code></td>\n          <td><code>String</code></td>\n          <td>Build Directory of user defined or default's value</td>\n        </tr>\n        <tr>\n          <td><code>name</code></td>\n          <td><code>String</code></td>\n          <td>Build name <code>'server' || 'client'</code></td>\n        </tr>\n      </table>\n    </td>\n  <tr>\n  <tr>\n    <td><code>next</code></td>\n    <td>A callback required to be called to pass the custom configuration</td>\n  <tr>\n</table>\n\nExample of adding `ts-loader` through `maleo.config.js`:\n\n```js\n// maleo.config.js\n\n// Partly taken and modified from @airy/maleo-ts-plugin source\n// for simplicity purposes\n\nmodule.exports = {\n  webpack(config, context, next) {\n    const { isDev } = context\n\n    config.module.rules.push({\n      test: /\\.tsx?/,\n      exclude: /node_modules/,\n      use: [\n        require.resolve('@airy/maleo/lib/build/loaders/maleo-babel-loader'),\n        {\n          loader: 'ts-loader',\n          options: {\n            transpileOnly: true,\n          },\n        },\n      ],\n\n      if (isDev) {\n        config.plugins.push(new ForkTSCheckerWebpackPlugin());\n      }\n\n      return next();\n    })\n  },\n};\n```\n\n#### Customize Babel Config\n\nMaleo.js also let you have your own babel config. Just simply add `.babelrc` file at the root directory of your app.\n\nYou can include Maleo.js' babel preset in order to have latest JavaScript preset.\n\nHere's an example of `.babelrc` file:\n```json\n{\n  \"presets\": [\"@airy/maleo/babel\"],\n  \"plugins\": []\n}\n```\n\nThe `@airy/maleo/babel` preset includes everything you need to get your development started. The preset includes:\n- `@babel/preset-env`\n- `@babel/preset-react`\n- `@babel/plugin-proposal-class-properties`\n- `@babel/plugin-proposal-decorators`\n- `@babel/plugin-proposal-object-rest-spread`\n- `@babel/plugin-transform-runtime`\n- `react-loadable/babel`\n\n## Core Utilities Functions\n\n#### Route Matching\n\nTo use you can import the matching routes function like this:\n```js\nimport { getMatchedRoutes } from '@airy/maleo/utils';\n```\n\nThe function will return all matched routes and the route object from `routes.json`\n\n## CDN Support\n\nIf you are using a CDN, you can set up the `publicPath` setting and configure your CDN's origin to resolve to the domain that Maleo.js is hosted on.\n\n```js\n// maleo.config.js\n\nconst isProd = process.env.NODE_ENV === 'production';\n\nmodule.exports = {\n  assetPrefix: isProd && 'https://cdn.example.com';\n}\n```\n\n## FAQ\n\n== TO BE DETERMINED == \n\n## Plugins\n- [css-plugin](https://github.com/airyrooms/maleo-plugins/tree/master/packages/css-plugin)\n- [redux-plugin](https://github.com/airyrooms/maleo-plugins/tree/master/packages/redux-plugin)\n- [typescript-plugin](https://github.com/airyrooms/maleo-plugins/tree/master/packages/typescript-plugin)\n- [more](https://github.com/airyrooms/maleo-plugins)\n\n\n## Contributing\n[Please follow these steps to contribute to Maleo.js](https://github.com/airyrooms/maleo.js/blob/canary/CONTRIBUTING.md)\n\n[Please follow these steps to contribute to Maleo.js' plugins](https://github.com/airyrooms/maleo-plugins#contributing-guidelines)\n\n\n## Contributors\n\nThis project exists thanks to all the people who contribute. [Contribute](CONTRIBUTING.md).\n\nMany thanks to our **[contributors](https://github.com/airyrooms/maleo.js/graphs/contributors)**!\n\n\n## License\n\nMIT","readmeFilename":"README.md","bugs":{"url":"https://github.com/airyrooms/maleo.js/issues"},"homepage":"https://github.com/airyrooms/maleo.js#readme","_id":"@airy/maleo@0.3.1-canary.13","_nodeVersion":"10.16.0","_npmVersion":"lerna/3.13.1/node@v10.16.0+x64 (linux)","dist":{"integrity":"sha512-nf4cii/RNCLCzNbFNHql0UHM+8V5/3G1BE4+6kfONJiDMQNylL8MQXTAkrFf18H9/LzGTrspt3HCGn4sU2SJbA==","shasum":"f6f6e9de1a7483c3c28c564080d0eb75bd043e6e","tarball":"https://registry.npmjs.org/@airy/maleo/-/maleo-0.3.1-canary.13.tgz","fileCount":96,"unpackedSize":329550,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJdC2MjCRA9TVsSAnZWagAAQhIP/1vehlxd8OQhY/jNDKEC\nUR1IrljbL9/0oQ3XPdFfH8jbR5wqLiqPma8xMugZAehpWjRJ2UTvwDEQI66h\njrbF0TAHEuqvK/CMpZ2nSqOYFhQOR4LFn8cTTXcf8D2DcQeKnhhqhJIpygFU\nuuU554kDA9cypELkgTHzVH6zh7PPAjVsYWSnrBsV9z/n23vy4oOJk9oYSkji\ntlSTIIeyJfZ1Q1G/ZJUhHxreSIJ2zUIFy1vbfm6WZTpiepPIE1aBULDyd0zf\ntB/iPdd2SI4l+/enmxz5i1tWIs+/5yLm/W5rs5Pmw6UpFMEGR9ijqXb8W6mM\nfvINwgJM/txTuhLx4+kTQfp3RChrzwDY58hgloLwoKsKGhR59u4f0nu3tn1b\nm7vd+a+ue3BUoJZYE75lpkHXWbwrfCPURUZ1nzxbT7CbYWWcZmhDJkcyKeJY\nigIfNwf/1HerLB1TX9Tn0i6RYiAapcAnEm9QVIbJhw94s2/BfFlhVZK7s+W8\nxenGlrkhQTqosR/Yh3wVs0zqozciHecaIHkKzP64zgLiFU8eG2Tag3pBozwH\nBIDBZHA9bEkpoRx2XwRHU/1MpBow/kDWcO3KTgvkufa8cZ8kT1m00qprUFTr\nLkVQyC5Y2IKEgBibDCRQpjrRlA3L4YZ6eaYC1U2CAaWeg4uN05NUIeiza2OF\nEfLB\r\n=u3QP\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDBenx/Ilrqy+kN1nuB4uGxKgZE2RF+Qc+WhJjmH2RATAIgISabgKf+H2HL3Qh9cHPjVUpHnYHJxg5hnfpMuHwKrvc="}]},"maintainers":[{"name":"airyrooms-engineering","email":"engineering@airyrooms.com"}],"_npmUser":{"name":"airyrooms-engineering","email":"engineering@airyrooms.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/maleo_0.3.1-canary.13_1561027362785_0.8773806470111054"},"_hasShrinkwrap":false},"0.3.1-canary.14":{"name":"@airy/maleo","version":"0.3.1-canary.14","description":"Un-opinionated Universal Rendering Javascript Framework","repository":{"type":"git","url":"git+https://github.com/airyrooms/maleo.js.git","bugs":"https://github.com/airyrooms/maleo.js/issues"},"author":{"name":"Airy"},"license":"MIT","private":false,"sideEffects":false,"scripts":{"build":"rm -rf dist && gulp","watch":"gulp watch","prepublish":"yarn build","start":"node server.js"},"bin":{"maleo":"lib/bin/maleo.js"},"dependencies":{"@babel/core":"7.1.2","@babel/plugin-proposal-class-properties":"7.1.0","@babel/plugin-proposal-decorators":"7.1.2","@babel/plugin-proposal-object-rest-spread":"7.0.0","@babel/plugin-syntax-dynamic-import":"7.0.0","@babel/plugin-transform-runtime":"7.1.0","@babel/preset-env":"7.1.0","@babel/preset-react":"7.0.0","@babel/runtime":"7.1.2","@babel/runtime-corejs2":"7.2.0","babel-loader":"8.0.4","case-sensitive-paths-webpack-plugin":"2.1.2","compression":"1.7.4","es6-promise":"4.2.5","eventsource":"1.0.7","express":"4.16.4","figlet":"1.2.1","friendly-errors-webpack-plugin":"1.7.0","hard-source-webpack-plugin":"0.13.1","helmet":"3.14.0","hoist-non-react-statics":"^3.3.0","isomorphic-fetch":"2.2.1","loader-utils":"1.1.0","node-notifier":"5.2.1","nodemon":"1.18.4","react-dom":"16.8.4","react-helmet":"5.2.0","react-loadable":"5.5.0","react-router-config":"5.0.0","react-router-dom":"5.0.0","rimraf":"2.6.3","source-map-support":"0.5.9","terser-webpack-plugin":"1.1.0","webpack":"4.29.0","webpack-bundle-analyzer":"3.0.3","webpack-cli":"3.1.2","webpack-dev-middleware":"3.4.0","webpack-hot-middleware":"2.24.3","webpackbar":"2.6.3"},"peerDependencies":{"react":"^16.8.4"},"devDependencies":{"@babel/preset-typescript":"^7.1.0","@types/webpack-bundle-analyzer":"^2.13.0","babel-plugin-module-resolver":"^3.1.1","gulp":"github:gulpjs/gulp#4.0","gulp-babel":"^8.0.0","gulp-clean":"^0.4.0","gulp-sourcemaps":"^2.6.4","gulp-uglify":"^3.0.1","gulp-watch":"^5.0.1","react":"^16.8.4"},"gitHead":"34674cfa627678ddb25e784e2e80cb91b52ff748","readme":"[![Build Status](https://travis-ci.org/airyrooms/maleo.js.svg?branch=master)](https://travis-ci.org/airyrooms/maleo.js)\n[![Coverage Status](https://coveralls.io/repos/github/airyrooms/maleo.js/badge.svg?branch=master)](https://coveralls.io/github/airyrooms/maleo.js?branch=master)\n[![npm version](https://badge.fury.io/js/%40airy%2Fmaleo.svg)](https://badge.fury.io/js/%40airy%2Fmaleo)\n[![Issues](http://img.shields.io/github/issues/airyrooms/maleo.js.svg)]( https://github.com/airyrooms/maleo.js/issues)\n[![MIT license](https://img.shields.io/badge/License-MIT-blue.svg)](https://github.com/airyrooms/maleo.js/blob/canary/LICENSE)\n[![Discord Chat](https://img.shields.io/discord/550498214789513218.svg)](https://discord.gg/9eArCQn)\n[![Conventional Commits](https://img.shields.io/badge/Conventional%20Commits-1.0.0-green.svg)](https://conventionalcommits.org)\n\n\n# Welcome to Maleo.js\n\nMaleo.js is an un-opinionated framework to enable Universal Rendering in JavaScript using React with no hassle.\n\nWe are here to solve the time consuming setups Universal Rendering Required.\n\n---\n\nReadme below is the documentation for the `canary` (prerelease) branch. To view the documentation for the latest stable Maleo.js version change branch to `master`\n\n---\n\n# Table of Contents\n- [Features](#features)\n- [Setup](#setup)\n- [Component Lifecycle](#component-lifecycle)\n- [Routing](#routing)\n- [Dynamic Import Component](#dynamic-import-component)\n  - [Tips](#tips)\n  - [Preloading](#preloading)\n- [Customizable Component](#customizable-component)\n  - [Custom Document](#custom-document)\n  - [Custom Wrap](#custom-wrap)\n- [Custom Configuration](#custom-configuration)\n  - [Customize Server](#customize-server)\n  - [Customize Webpack](#customize-webpack)\n  - [Customize Babel Config](#customize-babel-config)\n- [Core Utilities Functions](#core-utilities-functions)\n- [CDN Support](#cdn-support)\n- [Plugins](#plugins)\n- [FAQ](#faq)\n- [Contributing](#contributing)\n\n---\n\n## Features\n- Universal Rendering\n- Plugin based framework\n- Customizable\n\n## Setup\n\nInstall Maleo.js \n\n>*for now change `@airy/maleo` to `@airy/maleo@canary` until we publish the stable version of this package*\n\n\n**NPM**\n```bash\n$ npm install --save @airy/maleo react\n```\n**Yarn**\n```bash\n$ yarn add @airy/maleo react\n```\n\nAdd this script to your `package.json`\n```json\n{\n  \"scripts\": {\n    \"dev\": \"maleo dev\",\n    \"build\": \"export NODE_ENV=production && maleo build\",\n    \"start\": \"export NODE_ENV=production && maleo run\"\n  }\n}\n```\n\nCreate a page Root component\n```jsx\n// ./src/Root.jsx\nimport React from 'react';\n\n// Export default is required for registering page\nexport default class RootComponent extends React.Component {\n  render() {\n    return (\n      <h1>Hello World!</h1>\n    )\n  }\n}\n```\n\nAnd lastly, create a routing file on your project root directory called `routes.json` and register your page component\n```json\n[\n  {\n    \"path\": \"/\",\n    \"page\": \"./src/Root\"\n  }\n]\n```\n\nAfter that you can now run `$ npm run dev` and go to `http://localhost:3000`.\n\nYou should now see your app running on your browser.\n\nBy now you should see \n- Automatic transpilation and bundling (with webpack and babel)\n- ~~Hot code reloading~~ [#17](https://github.com/airyrooms/maleo.js/issues/17)\n- Server rendering\n\nTo see how simple this is, check out the sample app!\n\n## Component Lifecycle \n\nMaleo.js added a new component lifecycle hook called `getInitialProps`, this function is called during Server Side Rendering (SSR) and during route changes on Client Side Rendering (CSR).\n\nThis is useful especially for SEO purposes. \n\nExample for stateful component:\n```jsx\nimport React from 'react';\n\nexport default class extends React.Component {\n  static getInitialProps = async (ctx) => {\n    const { req } = ctx;\n\n    // check if getInitialProps is called on server or on client\n    const userAgent = req ? req.headers['user-agent'] : navigator.userAgent;\n\n    // the return value will be passed as props for this component\n    return { userAgent };\n  }\n\n  render() {\n    return (\n      <div>\n        Hello World {this.props.userAgent}\n      </div>\n    );\n  }\n}\n```\n\nExample for stateless component:\n```jsx\nconst Component = (props) => (\n  <div>\n    Hello World {props.userAgent}\n  </div>\n);\n\nComponent.getInitialprops = async (ctx) => {\n  const { req } = ctx;\n\n  const userAgent = req ? req.headers['user-agent'] : navigator.userAgent;\n\n  return { userAgent };\n};\n\nexport default Component;\n```\n\n`getInitialProps` receives a context object with the following properties:\n- `req` - HTTP request object (server only)\n- `res` - HTTP response object (server only)\n- `...wrapProps` - Spreaded properties from custom Wrap\n- `...appProps` - Spreaded properties from custom App\n\n## Routing\n\nRouting is declared in a centralized route config.\nRegister all the route config in `routes.json` file.\n\nIf you put the `routes.json` files on root directory, Maleo will automatically register your route. Otherwise put path to your routes on [Maleo config](#custom-configuration).\n\nRoutes file has to export default the route configuration.\nThe route object **expected to have distinct key** to indicate the route.\n\n<table>\n  <tr>\n    <td>Key</td>\n    <td>Type</td>\n    <td>Description</td>\n  </tr>\n  <tr>\n    <td><code>path</code></td>\n    <td><code>String!</code></td>\n    <td>Routes path</td>\n  </tr>\n  <tr>\n    <td><code>page</code></td>\n    <td><code>String!</code></td>\n    <td>Path to React Component for this route path</td>\n  </tr>\n  <tr>\n    <td><code>exact</code></td>\n    <td><code>Boolean?</code> [<code>false</code>]</td>\n    <td>To make url has to match exactly the path in order to render the component. Give <code>false</code> value if the route is a wrapper route component</td>\n  </tr>\n  <tr>\n    <td><code>routes</code></td>\n    <td><code>RouteObject?</code></td>\n    <td>Nested route</td>\n  </tr>\n</table>\n\n\nFor example:\n```json\n[\n  {\n    \"page\": \"./src/MainApp\",\n    \"routes\": [\n      {\n        \"path\": \"/\",\n        \"page\": \"./src/Search\",\n        \"exact\": true\n      },\n      {\n        \"path\": \"/search\",\n        \"page\": \"./src/Search\",\n        \"routes\": [\n          {\n            \"path\": \"/search/hello\",\n            \"page\": \"./src/Detail\",\n            \"exact\": true\n          }\n        ]\n      },\n      {\n        \"path\": \"/detail\",\n        \"page\": \"./src/Detail\",\n        \"exact\": true\n      }\n    ]\n  }\n]\n```\n\n## Dynamic Import Component\nMaleo.js supports TC39 [dynamic import proposal](https://github.com/tc39/proposal-dynamic-import) for JavaScript.\n\nYou can think dynamic import as another way to split your code into manageable chunks. You can use our `Dynamic` function which utilizes [react loadable](https://github.com/jamiebuilds/react-loadable)\n\nFor Example\n```js\n// DynamicComponent.js\nimport Dynamic from '@airy/maleo/dynamic';\n\nexport default Dynamic({\n  loader: () => import( /* webpackChunkName:\"DynamicComponent\" */ './component/DynamicLoad'),\n})\n```\n\n### **Preloading**\n\nFor optimization purposes, you can also preload a component even before the component got rendered.\n\nFor example, if you want to load component when a button get pressed, you can start preloading the component when the user hovers the mouse over the button.\n\nThe component created by `Dynamic` exposes a [static `preload` method](https://github.com/jamiebuilds/react-loadable#loadablecomponentpreload).\n\n```jsx\nimport React from 'react';\nimport Dynamic from '@airy/maleo/dynamic';\n\nconst DynamicBar = Dynamic({\n  loader: () => import('./Bar'),\n  loading: LoadingCompoinent\n});\n\nclass MyComponent extends React.Component {\n  state = { showBar: false };\n\n  onClick = () => {\n    this.setState({ showBar: true });\n  };\n\n  onMouseOver = () => DynamicBar.preload();\n\n  render() {\n    return (\n      <div>\n        <button\n          onClick={this.onClick}\n          onMouseOver={this.onMouseOver}>\n          Show Bar\n        </button>\n        { this.state.showBar && <DynamicBar /> }\n      </div>\n    )\n  }\n}\n```\n\n## Customizable Component\n\n### Custom Document\n\nHighly inspired by what [Next.js](https://github.com/zeit/next.js) has done on their awesome template customization.\n\nMaleo.js also enable customization on `Document` as document's markup. So you don't need to include tags like `<html>`, `<head>`, `<body>`, etc. \n\nTo override the default behavior, you'll need to create a component that extends the `Document` React class provided by Maleo.\n\n```jsx\n// document.jsx\nimport React from 'react';\nimport { Document, Header, Main, Scripts } from '@airy/maleo/document';\n\nexport default class extends Document {\n  render() {\n    return (\n      <html>\n        <Header>\n          <title>Maleo JS</title>\n          <meta charset=\"utf-8\" />\n          <meta name=\"description\" content=\"Maleo.js is awesome!\" />\n\n          <style>\n            {` body { background-color: #fff } `}\n          </style>\n        </Header>\n\n        <body>\n          <Main />\n\n          <Scripts />\n\n          <ReduxScript />\n        </body>\n      </html>\n    );\n  }\n}\n```\n\n### Custom Wrap\n\nMaleo.js uses the `Wrap` component to initialize pages. `Wrap` contains React Router's Component. You can add HoC here to wrap the application and control the page initialization. Which allows you to do amazing things like:\n- Persisting layour between page changes\n- Keeping state when navigating pages\n- Custom error handling using `componentDidCatch`\n- Inject additional data into pages (like Redux provider, etc)\n\nTo override the default behavior, you'll need to create a component that extends the `Wrap` React class provided by Maleo.\n\n```jsx\n// wrap.jsx\nimport React from 'react';\nimport { Wrap } from '@airy/maleo/wrap';\n\n// Redux plugin for Maleo.js\n// Hoc that creates store and add Redux Provider\nimport { withRedux } from '@airy/maleo-redux-plugin';\n\n// Custom Wrapper that will be rendered for the whole Application\nimport Content from './component/Content';\nimport NavigationBar from './component/NavigationBar';\n\nimport { createStore } from './store';\n\n@withRedux(createStore)\nexport default class extends Wrap {\n  static getInitialProps = (ctx) => {\n    const { store } = ctx\n    // you receive store from context\n    // you can access or do something with the store here\n    console.log('Initialized Redux Store', store);\n    return {}\n  }\n\n  render() {\n    const { Container, containerProps, App, appProps } = this.props\n\n    return (\n      <Container {...containerProps}>\n        <NavigationBar />\n        <Content>\n          <App {...appProps}/>\n        </Content>\n      </Container>\n    )\n  }\n}\n\n```\nIf you put `document.jsx` and `wrap.jsx` on root directory (the same level with `package.json`), then Maleo will automatically register your custom Document and Wrap. Otherwise, you can add the path to your custom Document and Wrap on [Maleo config](#custom-configuration)\n\n---\n***We are also working on adding default and customizable `Error` component page***\n\n---\n\n## Custom Configuration\n\nFor more advanced configuration of Maleo.js, like `webpack` config, registering `plugins`, path to your routes, custom Document and Wrap, and adding `path alias`, you can create a `maleo.config.js` in the root of your project directory. (same directory with `package.json`)\n\n```js\n// maleo.config.js\n\nmodule.exports = {\n  /* config options here */\n}\n```\n\nHere are the API's for the configuration:\n\n<table>\n  <tr>\n    <td>Key</td>\n    <td>Type</td>\n    <td>Description</td>\n  </tr>\n  <tr>\n    <td><code>buildDir</code></td>\n    <td><code>String?</code> [<code>.maleo</code>]</td>\n    <td>Directory to put Maleo.js' build assets</td>\n  </tr>\n  <tr>\n    <td><code>cache</code></td>\n    <td><code>Boolean?</code> [<code>true</code>]</td>\n    <td>Enable webpack build caching</td>\n  </tr>\n  <tr>\n    <td><code>isDev</code></td>\n    <td><code>Boolean?</code> [<code>process.env.NODE_ENV === 'development'</code>]</td>\n    <td>Enable development build configuration</td>\n  </tr>\n  <tr>\n    <td><code>sourceMaps</code></td>\n    <td><code>Boolean?</code> [<code>true</code>]</td>\n    <td>Enable webpack to generate source maps</td>\n  </tr>\n  <tr>\n    <td><code>alias</code></td>\n    <td><code>Object?</code></td>\n    <td>A key value pair for aliasing path directory\n      <br>\n      <code>\n      { 'component': './src/component' }\n      </code>\n    </td>\n  </tr>\n  <tr>\n    <td><code>publicPath</code></td>\n    <td><code>String?</code> [<code>/_assets/</code>]</td>\n    <td>To customize webpack's <code>publicPath</code> \n    <br>Comes in handy if using CDN to put built assets</td>\n  </tr>\n  <tr>\n    <td><code>analyzeBundle</code></td>\n    <td><code>Boolean?</code> [<code>false</code>]</td>\n    <td>To enable webpack's bundle analyzer, for analyzing bundle sizes during bundle debugging should Maleo.js' build process got slow</td>\n  </tr>\n  <tr>\n    <td><code>webpack</code></td>\n    <td><code>Function?</code></td>\n    <td>To customize webpack configuration, more details <a href=\"#customize-webpack\">here</a></td>\n  </tr>\n  <tr>\n    <td><code>routes</code></td>\n    <td><code>string?</code> [<code>rootDir/routes.jsx</code>]</td>\n    <td>Path to your routes file</td>\n  </tr>\n  <tr>\n    <td><code>customDocument</code></td>\n    <td><code>string?</code> [<code>rootDir/document.jsx</code>]</td>\n    <td>Path to your custom document file</td>\n  </tr>\n  <tr>\n    <td><code>customWrap</code></td>\n    <td><code>string?</code> [<code>rootDir/wrap.jsx</code>]</td>\n    <td>Path to your custom wrap file</td>\n  </tr>\n  <tr>\n    <td><code>customApp</code></td>\n    <td><code>string?</code> [<code>rootDir/app.jsx</code>]</td>\n    <td>Path to your custom app file</td>\n  </tr>\n</table>\n\n#### Customize Server\n\nCreate a `server.js` file on root directory where your `package.json` lives.\nHere you can customize Maleo's server.\n```js\nimport { Server } from '@airy/maleo/server';\nimport path from 'path';\n\nimport routeConfig from './routes';\n\nconst PORT = process.env.PORT || 8080;\n\nconst maleoServer = Server.init({\n  port: PORT,\n});\n\nmaleoServer.run(() => {\n  console.log('Server running on port :', PORT);\n});\n```\n\nHere are the API's for the configuration:\n\n<table>\n  <tr>\n    <td>Key</td>\n    <td>Type</td>\n    <td>Description</td>\n  </tr>\n  <tr>\n    <td><code>port</code></td>\n    <td><code>Number?</code> [<code>3000</code>]</td>\n    <td>Port to run Maleo server</td>\n  </tr>\n  <tr>\n    <td><code>assetDir</code></td>\n    <td><code>String?</code> [<code>'<root>/.maleo/client'</code>]</td>\n    <td>Directory for all client related assets</td>\n  </tr>\n</table>\n\n#### Customize Webpack\n\nYou are able to extend Maleo.js' default webpack configuration by defining a function on `maleo.config.js`\n\n```js\n// maleo.config.js\n\nmodule.exports = {\n  webpack(config, context, next) {\n    // Perform customizations to webpack config\n    // Important: This call is required in order to let Maleo pass the config to webpack\n    return next(); \n  },\n};\n```\n\nWebpack function will receive three arguments:\n\n<table>\n  <tr>\n    <td>Argument</td>\n    <td>Details</td>\n  <tr>\n  <tr>\n    <td><code>config</code></td>\n    <td>This contains webpack configuration object that you can manipulate</td>\n  <tr>\n  <tr>\n    <td><code>context</code></td>\n    <td>This contains some keys that are useful for the build context\n      <br>\n      <table>\n        <tr>\n          <td><code>isDev</code></td>\n          <td><code>Boolean</code></td>\n          <td>Check if current build is for development</td>\n        </tr>\n        <tr>\n          <td><code>publicPath</code></td>\n          <td><code>String</code></td>\n          <td>Public Path of user defined or default's value</td>\n        </tr>\n        <tr>\n          <td><code>analyzeBundle</code></td>\n          <td><code>Boolean</code></td>\n          <td>Check if analyze bundle is enabled</td>\n        </tr>\n        <tr>\n          <td><code>buildDirectory</code></td>\n          <td><code>String</code></td>\n          <td>Build Directory of user defined or default's value</td>\n        </tr>\n        <tr>\n          <td><code>name</code></td>\n          <td><code>String</code></td>\n          <td>Build name <code>'server' || 'client'</code></td>\n        </tr>\n      </table>\n    </td>\n  <tr>\n  <tr>\n    <td><code>next</code></td>\n    <td>A callback required to be called to pass the custom configuration</td>\n  <tr>\n</table>\n\nExample of adding `ts-loader` through `maleo.config.js`:\n\n```js\n// maleo.config.js\n\n// Partly taken and modified from @airy/maleo-ts-plugin source\n// for simplicity purposes\n\nmodule.exports = {\n  webpack(config, context, next) {\n    const { isDev } = context\n\n    config.module.rules.push({\n      test: /\\.tsx?/,\n      exclude: /node_modules/,\n      use: [\n        require.resolve('@airy/maleo/lib/build/loaders/maleo-babel-loader'),\n        {\n          loader: 'ts-loader',\n          options: {\n            transpileOnly: true,\n          },\n        },\n      ],\n\n      if (isDev) {\n        config.plugins.push(new ForkTSCheckerWebpackPlugin());\n      }\n\n      return next();\n    })\n  },\n};\n```\n\n#### Customize Babel Config\n\nMaleo.js also let you have your own babel config. Just simply add `.babelrc` file at the root directory of your app.\n\nYou can include Maleo.js' babel preset in order to have latest JavaScript preset.\n\nHere's an example of `.babelrc` file:\n```json\n{\n  \"presets\": [\"@airy/maleo/babel\"],\n  \"plugins\": []\n}\n```\n\nThe `@airy/maleo/babel` preset includes everything you need to get your development started. The preset includes:\n- `@babel/preset-env`\n- `@babel/preset-react`\n- `@babel/plugin-proposal-class-properties`\n- `@babel/plugin-proposal-decorators`\n- `@babel/plugin-proposal-object-rest-spread`\n- `@babel/plugin-transform-runtime`\n- `react-loadable/babel`\n\n## Core Utilities Functions\n\n#### Route Matching\n\nTo use you can import the matching routes function like this:\n```js\nimport { getMatchedRoutes } from '@airy/maleo/utils';\n```\n\nThe function will return all matched routes and the route object from `routes.json`\n\n## CDN Support\n\nIf you are using a CDN, you can set up the `publicPath` setting and configure your CDN's origin to resolve to the domain that Maleo.js is hosted on.\n\n```js\n// maleo.config.js\n\nconst isProd = process.env.NODE_ENV === 'production';\n\nmodule.exports = {\n  assetPrefix: isProd && 'https://cdn.example.com';\n}\n```\n\n## FAQ\n\n== TO BE DETERMINED == \n\n## Plugins\n- [css-plugin](https://github.com/airyrooms/maleo-plugins/tree/master/packages/css-plugin)\n- [redux-plugin](https://github.com/airyrooms/maleo-plugins/tree/master/packages/redux-plugin)\n- [typescript-plugin](https://github.com/airyrooms/maleo-plugins/tree/master/packages/typescript-plugin)\n- [more](https://github.com/airyrooms/maleo-plugins)\n\n\n## Contributing\n[Please follow these steps to contribute to Maleo.js](https://github.com/airyrooms/maleo.js/blob/canary/CONTRIBUTING.md)\n\n[Please follow these steps to contribute to Maleo.js' plugins](https://github.com/airyrooms/maleo-plugins#contributing-guidelines)\n\n\n## Contributors\n\nThis project exists thanks to all the people who contribute. [Contribute](CONTRIBUTING.md).\n\nMany thanks to our **[contributors](https://github.com/airyrooms/maleo.js/graphs/contributors)**!\n\n\n## License\n\nMIT","readmeFilename":"README.md","bugs":{"url":"https://github.com/airyrooms/maleo.js/issues"},"homepage":"https://github.com/airyrooms/maleo.js#readme","_id":"@airy/maleo@0.3.1-canary.14","_nodeVersion":"11.15.0","_npmVersion":"lerna/3.13.1/node@v11.15.0+x64 (linux)","dist":{"integrity":"sha512-Y1ISeXXmTQgrOtDwkVKRYRSapiaRjDjH88GrD266QP1M98Uh+CeXfCWqeyPSE0E7SLtmOWjp961b8Xkbba+rcw==","shasum":"a83d8b42dc6383cc15d96b9d73669cd74158b709","tarball":"https://registry.npmjs.org/@airy/maleo/-/maleo-0.3.1-canary.14.tgz","fileCount":96,"unpackedSize":330228,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJdEKcCCRA9TVsSAnZWagAA4V8P/RTeEpHdqrtXj9ljntRH\ni18L6GAGeDcvI4QF/Yws55Ibm6hIf0xtV86a0HRqZ452oG98e47sli1vMJhK\nRpvZuMl6eRr0VvzQ7z18RpddVSjf1KZHDmftmLrOGy4YjpDxXbXa7YjG4rn7\nJcBQdQ0beNp7N5bjhdzkffNXCybJjI5aWuk57dL04FRVSbFgYbB11sXFWyYK\npq1c721+IhfdGTVwOm0RrxfIecXtfz4E4dyjzfw5rYj2pQ540+SC6E1d+bb4\nqvIE2czAOrP06AM+dVazzdU4miL+90DhhxqeQpNn06q/IL4VQVuDb2I0kr+W\n3vI7U/AArhLItanil9cvQp+515RVVzEfU0dsCJfDQk+/pVCzlVO3+zAa8/nO\nYP0kjunpvRoMY1jIPKewdoO0ewYdlVXzVBlavv75VOg4dzUegxi12g6d28V9\nCB9c23wKdTcokqzu4FU1VAv4KuA3DAXH0FGaqPwQ+WmdIY6FEuUqk+xiA3+j\nXJyWGl0QLJ3hd8cLtXffHek0QtmqHxSci2201dJSCAg1sUopVY9ve8JIYlf+\n/33OBNE/JIlDUoj67CWFC65Is/guyq9lXDkuHm0Y7/ILnxRxmDf/U+DTkQSQ\naAhtQUN6Et9+KanQRZIKAJteFV6oqTjT7l5hIxPmY7u/hKaJjy2PrK0zf5EI\nFxIX\r\n=4Z7W\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCKOVljpv+UQEtUHakR1pknPPK2FWHngtpSwnUc+B17QQIgPJ6eEeVGEu8Xb8NcScT3JHyIxEP4AONmacpF7oiGcJ4="}]},"maintainers":[{"name":"airyrooms-engineering","email":"engineering@airyrooms.com"}],"_npmUser":{"name":"airyrooms-engineering","email":"engineering@airyrooms.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/maleo_0.3.1-canary.14_1561372417523_0.07607366100283897"},"_hasShrinkwrap":false},"0.3.1-canary.15":{"name":"@airy/maleo","version":"0.3.1-canary.15","description":"Un-opinionated Universal Rendering Javascript Framework","repository":{"type":"git","url":"git+https://github.com/airyrooms/maleo.js.git","bugs":"https://github.com/airyrooms/maleo.js/issues"},"author":{"name":"Airy"},"license":"MIT","private":false,"sideEffects":false,"scripts":{"build":"rm -rf dist && gulp","watch":"gulp watch","prepublish":"yarn build","start":"node server.js"},"bin":{"maleo":"lib/bin/maleo.js"},"dependencies":{"@babel/core":"7.1.2","@babel/plugin-proposal-class-properties":"7.1.0","@babel/plugin-proposal-decorators":"7.1.2","@babel/plugin-proposal-object-rest-spread":"7.0.0","@babel/plugin-syntax-dynamic-import":"7.0.0","@babel/plugin-transform-runtime":"7.1.0","@babel/preset-env":"7.1.0","@babel/preset-react":"7.0.0","@babel/runtime":"7.1.2","@babel/runtime-corejs2":"7.2.0","babel-loader":"8.0.4","case-sensitive-paths-webpack-plugin":"2.1.2","compression":"1.7.4","es6-promise":"4.2.5","eventsource":"1.0.7","express":"4.16.4","figlet":"1.2.1","friendly-errors-webpack-plugin":"1.7.0","hard-source-webpack-plugin":"0.13.1","helmet":"3.14.0","hoist-non-react-statics":"^3.3.0","isomorphic-fetch":"2.2.1","loader-utils":"1.1.0","node-notifier":"5.2.1","nodemon":"1.18.4","react-dom":"16.8.4","react-helmet":"5.2.0","react-loadable":"5.5.0","react-router-config":"5.0.0","react-router-dom":"5.0.0","rimraf":"2.6.3","source-map-support":"0.5.9","terser-webpack-plugin":"1.1.0","webpack":"4.29.0","webpack-bundle-analyzer":"3.0.3","webpack-cli":"3.1.2","webpack-dev-middleware":"3.4.0","webpack-hot-middleware":"2.24.3","webpackbar":"2.6.3"},"peerDependencies":{"react":"^16.8.4"},"devDependencies":{"@babel/preset-typescript":"^7.1.0","@types/webpack-bundle-analyzer":"^2.13.0","babel-plugin-module-resolver":"^3.1.1","gulp":"github:gulpjs/gulp#4.0","gulp-babel":"^8.0.0","gulp-clean":"^0.4.0","gulp-sourcemaps":"^2.6.4","gulp-uglify":"^3.0.1","gulp-watch":"^5.0.1","react":"^16.8.4"},"gitHead":"51f089798a793aacc7c84e3bd199725d60a0b54c","readme":"[![Build Status](https://travis-ci.org/airyrooms/maleo.js.svg?branch=master)](https://travis-ci.org/airyrooms/maleo.js)\n[![Coverage Status](https://coveralls.io/repos/github/airyrooms/maleo.js/badge.svg?branch=master)](https://coveralls.io/github/airyrooms/maleo.js?branch=master)\n[![npm version](https://badge.fury.io/js/%40airy%2Fmaleo.svg)](https://badge.fury.io/js/%40airy%2Fmaleo)\n[![Issues](http://img.shields.io/github/issues/airyrooms/maleo.js.svg)]( https://github.com/airyrooms/maleo.js/issues)\n[![MIT license](https://img.shields.io/badge/License-MIT-blue.svg)](https://github.com/airyrooms/maleo.js/blob/canary/LICENSE)\n[![Discord Chat](https://img.shields.io/discord/550498214789513218.svg)](https://discord.gg/9eArCQn)\n[![Conventional Commits](https://img.shields.io/badge/Conventional%20Commits-1.0.0-green.svg)](https://conventionalcommits.org)\n\n\n# Welcome to Maleo.js\n\nMaleo.js is an un-opinionated framework to enable Universal Rendering in JavaScript using React with no hassle.\n\nWe are here to solve the time consuming setups Universal Rendering Required.\n\n---\n\nReadme below is the documentation for the `canary` (prerelease) branch. To view the documentation for the latest stable Maleo.js version change branch to `master`\n\n---\n\n# Table of Contents\n- [Features](#features)\n- [Setup](#setup)\n- [Component Lifecycle](#component-lifecycle)\n- [Routing](#routing)\n- [Dynamic Import Component](#dynamic-import-component)\n  - [Tips](#tips)\n  - [Preloading](#preloading)\n- [Customizable Component](#customizable-component)\n  - [Custom Document](#custom-document)\n  - [Custom Wrap](#custom-wrap)\n- [Custom Configuration](#custom-configuration)\n  - [Customize Server](#customize-server)\n  - [Customize Webpack](#customize-webpack)\n  - [Customize Babel Config](#customize-babel-config)\n- [Core Utilities Functions](#core-utilities-functions)\n- [CDN Support](#cdn-support)\n- [Plugins](#plugins)\n- [FAQ](#faq)\n- [Contributing](#contributing)\n\n---\n\n## Features\n- Universal Rendering\n- Plugin based framework\n- Customizable\n\n## Setup\n\nInstall Maleo.js \n\n>*for now change `@airy/maleo` to `@airy/maleo@canary` until we publish the stable version of this package*\n\n\n**NPM**\n```bash\n$ npm install --save @airy/maleo react\n```\n**Yarn**\n```bash\n$ yarn add @airy/maleo react\n```\n\nAdd this script to your `package.json`\n```json\n{\n  \"scripts\": {\n    \"dev\": \"maleo dev\",\n    \"build\": \"export NODE_ENV=production && maleo build\",\n    \"start\": \"export NODE_ENV=production && maleo run\"\n  }\n}\n```\n\nCreate a page Root component\n```jsx\n// ./src/Root.jsx\nimport React from 'react';\n\n// Export default is required for registering page\nexport default class RootComponent extends React.Component {\n  render() {\n    return (\n      <h1>Hello World!</h1>\n    )\n  }\n}\n```\n\nAnd lastly, create a routing file on your project root directory called `routes.json` and register your page component\n```json\n[\n  {\n    \"path\": \"/\",\n    \"page\": \"./src/Root\"\n  }\n]\n```\n\nAfter that you can now run `$ npm run dev` and go to `http://localhost:3000`.\n\nYou should now see your app running on your browser.\n\nBy now you should see \n- Automatic transpilation and bundling (with webpack and babel)\n- ~~Hot code reloading~~ [#17](https://github.com/airyrooms/maleo.js/issues/17)\n- Server rendering\n\nTo see how simple this is, check out the sample app!\n\n## Component Lifecycle \n\nMaleo.js added a new component lifecycle hook called `getInitialProps`, this function is called during Server Side Rendering (SSR) and during route changes on Client Side Rendering (CSR).\n\nThis is useful especially for SEO purposes. \n\nExample for stateful component:\n```jsx\nimport React from 'react';\n\nexport default class extends React.Component {\n  static getInitialProps = async (ctx) => {\n    const { req } = ctx;\n\n    // check if getInitialProps is called on server or on client\n    const userAgent = req ? req.headers['user-agent'] : navigator.userAgent;\n\n    // the return value will be passed as props for this component\n    return { userAgent };\n  }\n\n  render() {\n    return (\n      <div>\n        Hello World {this.props.userAgent}\n      </div>\n    );\n  }\n}\n```\n\nExample for stateless component:\n```jsx\nconst Component = (props) => (\n  <div>\n    Hello World {props.userAgent}\n  </div>\n);\n\nComponent.getInitialprops = async (ctx) => {\n  const { req } = ctx;\n\n  const userAgent = req ? req.headers['user-agent'] : navigator.userAgent;\n\n  return { userAgent };\n};\n\nexport default Component;\n```\n\n`getInitialProps` receives a context object with the following properties:\n- `req` - HTTP request object (server only)\n- `res` - HTTP response object (server only)\n- `...wrapProps` - Spreaded properties from custom Wrap\n- `...appProps` - Spreaded properties from custom App\n\n## Routing\n\nRouting is declared in a centralized route config.\nRegister all the route config in `routes.json` file.\n\nIf you put the `routes.json` files on root directory, Maleo will automatically register your route. Otherwise put path to your routes on [Maleo config](#custom-configuration).\n\nRoutes file has to export default the route configuration.\nThe route object **expected to have distinct key** to indicate the route.\n\n<table>\n  <tr>\n    <td>Key</td>\n    <td>Type</td>\n    <td>Description</td>\n  </tr>\n  <tr>\n    <td><code>path</code></td>\n    <td><code>String!</code></td>\n    <td>Routes path</td>\n  </tr>\n  <tr>\n    <td><code>page</code></td>\n    <td><code>String!</code></td>\n    <td>Path to React Component for this route path</td>\n  </tr>\n  <tr>\n    <td><code>exact</code></td>\n    <td><code>Boolean?</code> [<code>false</code>]</td>\n    <td>To make url has to match exactly the path in order to render the component. Give <code>false</code> value if the route is a wrapper route component</td>\n  </tr>\n  <tr>\n    <td><code>routes</code></td>\n    <td><code>RouteObject?</code></td>\n    <td>Nested route</td>\n  </tr>\n</table>\n\n\nFor example:\n```json\n[\n  {\n    \"page\": \"./src/MainApp\",\n    \"routes\": [\n      {\n        \"path\": \"/\",\n        \"page\": \"./src/Search\",\n        \"exact\": true\n      },\n      {\n        \"path\": \"/search\",\n        \"page\": \"./src/Search\",\n        \"routes\": [\n          {\n            \"path\": \"/search/hello\",\n            \"page\": \"./src/Detail\",\n            \"exact\": true\n          }\n        ]\n      },\n      {\n        \"path\": \"/detail\",\n        \"page\": \"./src/Detail\",\n        \"exact\": true\n      }\n    ]\n  }\n]\n```\n\n## Dynamic Import Component\nMaleo.js supports TC39 [dynamic import proposal](https://github.com/tc39/proposal-dynamic-import) for JavaScript.\n\nYou can think dynamic import as another way to split your code into manageable chunks. You can use our `Dynamic` function which utilizes [react loadable](https://github.com/jamiebuilds/react-loadable)\n\nFor Example\n```js\n// DynamicComponent.js\nimport Dynamic from '@airy/maleo/dynamic';\n\nexport default Dynamic({\n  loader: () => import( /* webpackChunkName:\"DynamicComponent\" */ './component/DynamicLoad'),\n})\n```\n\n### **Preloading**\n\nFor optimization purposes, you can also preload a component even before the component got rendered.\n\nFor example, if you want to load component when a button get pressed, you can start preloading the component when the user hovers the mouse over the button.\n\nThe component created by `Dynamic` exposes a [static `preload` method](https://github.com/jamiebuilds/react-loadable#loadablecomponentpreload).\n\n```jsx\nimport React from 'react';\nimport Dynamic from '@airy/maleo/dynamic';\n\nconst DynamicBar = Dynamic({\n  loader: () => import('./Bar'),\n  loading: LoadingCompoinent\n});\n\nclass MyComponent extends React.Component {\n  state = { showBar: false };\n\n  onClick = () => {\n    this.setState({ showBar: true });\n  };\n\n  onMouseOver = () => DynamicBar.preload();\n\n  render() {\n    return (\n      <div>\n        <button\n          onClick={this.onClick}\n          onMouseOver={this.onMouseOver}>\n          Show Bar\n        </button>\n        { this.state.showBar && <DynamicBar /> }\n      </div>\n    )\n  }\n}\n```\n\n## Customizable Component\n\n### Custom Document\n\nHighly inspired by what [Next.js](https://github.com/zeit/next.js) has done on their awesome template customization.\n\nMaleo.js also enable customization on `Document` as document's markup. So you don't need to include tags like `<html>`, `<head>`, `<body>`, etc. \n\nTo override the default behavior, you'll need to create a component that extends the `Document` React class provided by Maleo.\n\n```jsx\n// document.jsx\nimport React from 'react';\nimport { Document, Header, Main, Scripts } from '@airy/maleo/document';\n\nexport default class extends Document {\n  render() {\n    return (\n      <html>\n        <Header>\n          <title>Maleo JS</title>\n          <meta charset=\"utf-8\" />\n          <meta name=\"description\" content=\"Maleo.js is awesome!\" />\n\n          <style>\n            {` body { background-color: #fff } `}\n          </style>\n        </Header>\n\n        <body>\n          <Main />\n\n          <Scripts />\n\n          <ReduxScript />\n        </body>\n      </html>\n    );\n  }\n}\n```\n\n### Custom Wrap\n\nMaleo.js uses the `Wrap` component to initialize pages. `Wrap` contains React Router's Component. You can add HoC here to wrap the application and control the page initialization. Which allows you to do amazing things like:\n- Persisting layour between page changes\n- Keeping state when navigating pages\n- Custom error handling using `componentDidCatch`\n- Inject additional data into pages (like Redux provider, etc)\n\nTo override the default behavior, you'll need to create a component that extends the `Wrap` React class provided by Maleo.\n\n```jsx\n// wrap.jsx\nimport React from 'react';\nimport { Wrap } from '@airy/maleo/wrap';\n\n// Redux plugin for Maleo.js\n// Hoc that creates store and add Redux Provider\nimport { withRedux } from '@airy/maleo-redux-plugin';\n\n// Custom Wrapper that will be rendered for the whole Application\nimport Content from './component/Content';\nimport NavigationBar from './component/NavigationBar';\n\nimport { createStore } from './store';\n\n@withRedux(createStore)\nexport default class extends Wrap {\n  static getInitialProps = (ctx) => {\n    const { store } = ctx\n    // you receive store from context\n    // you can access or do something with the store here\n    console.log('Initialized Redux Store', store);\n    return {}\n  }\n\n  render() {\n    const { Container, containerProps, App, appProps } = this.props\n\n    return (\n      <Container {...containerProps}>\n        <NavigationBar />\n        <Content>\n          <App {...appProps}/>\n        </Content>\n      </Container>\n    )\n  }\n}\n\n```\nIf you put `document.jsx` and `wrap.jsx` on root directory (the same level with `package.json`), then Maleo will automatically register your custom Document and Wrap. Otherwise, you can add the path to your custom Document and Wrap on [Maleo config](#custom-configuration)\n\n---\n***We are also working on adding default and customizable `Error` component page***\n\n---\n\n## Custom Configuration\n\nFor more advanced configuration of Maleo.js, like `webpack` config, registering `plugins`, path to your routes, custom Document and Wrap, and adding `path alias`, you can create a `maleo.config.js` in the root of your project directory. (same directory with `package.json`)\n\n```js\n// maleo.config.js\n\nmodule.exports = {\n  /* config options here */\n}\n```\n\nHere are the API's for the configuration:\n\n<table>\n  <tr>\n    <td>Key</td>\n    <td>Type</td>\n    <td>Description</td>\n  </tr>\n  <tr>\n    <td><code>buildDir</code></td>\n    <td><code>String?</code> [<code>.maleo</code>]</td>\n    <td>Directory to put Maleo.js' build assets</td>\n  </tr>\n  <tr>\n    <td><code>cache</code></td>\n    <td><code>Boolean?</code> [<code>true</code>]</td>\n    <td>Enable webpack build caching</td>\n  </tr>\n  <tr>\n    <td><code>isDev</code></td>\n    <td><code>Boolean?</code> [<code>process.env.NODE_ENV === 'development'</code>]</td>\n    <td>Enable development build configuration</td>\n  </tr>\n  <tr>\n    <td><code>sourceMaps</code></td>\n    <td><code>Boolean?</code> [<code>true</code>]</td>\n    <td>Enable webpack to generate source maps</td>\n  </tr>\n  <tr>\n    <td><code>alias</code></td>\n    <td><code>Object?</code></td>\n    <td>A key value pair for aliasing path directory\n      <br>\n      <code>\n      { 'component': './src/component' }\n      </code>\n    </td>\n  </tr>\n  <tr>\n    <td><code>publicPath</code></td>\n    <td><code>String?</code> [<code>/_assets/</code>]</td>\n    <td>To customize webpack's <code>publicPath</code> \n    <br>Comes in handy if using CDN to put built assets</td>\n  </tr>\n  <tr>\n    <td><code>analyzeBundle</code></td>\n    <td><code>Boolean?</code> [<code>false</code>]</td>\n    <td>To enable webpack's bundle analyzer, for analyzing bundle sizes during bundle debugging should Maleo.js' build process got slow</td>\n  </tr>\n  <tr>\n    <td><code>webpack</code></td>\n    <td><code>Function?</code></td>\n    <td>To customize webpack configuration, more details <a href=\"#customize-webpack\">here</a></td>\n  </tr>\n  <tr>\n    <td><code>routes</code></td>\n    <td><code>string?</code> [<code>rootDir/routes.jsx</code>]</td>\n    <td>Path to your routes file</td>\n  </tr>\n  <tr>\n    <td><code>customDocument</code></td>\n    <td><code>string?</code> [<code>rootDir/document.jsx</code>]</td>\n    <td>Path to your custom document file</td>\n  </tr>\n  <tr>\n    <td><code>customWrap</code></td>\n    <td><code>string?</code> [<code>rootDir/wrap.jsx</code>]</td>\n    <td>Path to your custom wrap file</td>\n  </tr>\n  <tr>\n    <td><code>customApp</code></td>\n    <td><code>string?</code> [<code>rootDir/app.jsx</code>]</td>\n    <td>Path to your custom app file</td>\n  </tr>\n</table>\n\n#### Customize Server\n\nCreate a `server.js` file on root directory where your `package.json` lives.\nHere you can customize Maleo's server.\n```js\nimport { Server } from '@airy/maleo/server';\nimport path from 'path';\n\nimport routeConfig from './routes';\n\nconst PORT = process.env.PORT || 8080;\n\nconst maleoServer = Server.init({\n  port: PORT,\n});\n\nmaleoServer.run(() => {\n  console.log('Server running on port :', PORT);\n});\n```\n\nHere are the API's for the configuration:\n\n<table>\n  <tr>\n    <td>Key</td>\n    <td>Type</td>\n    <td>Description</td>\n  </tr>\n  <tr>\n    <td><code>port</code></td>\n    <td><code>Number?</code> [<code>3000</code>]</td>\n    <td>Port to run Maleo server</td>\n  </tr>\n  <tr>\n    <td><code>assetDir</code></td>\n    <td><code>String?</code> [<code>'<root>/.maleo/client'</code>]</td>\n    <td>Directory for all client related assets</td>\n  </tr>\n</table>\n\n#### Customize Webpack\n\nYou are able to extend Maleo.js' default webpack configuration by defining a function on `maleo.config.js`\n\n```js\n// maleo.config.js\n\nmodule.exports = {\n  webpack(config, context, next) {\n    // Perform customizations to webpack config\n    // Important: This call is required in order to let Maleo pass the config to webpack\n    return next(); \n  },\n};\n```\n\nWebpack function will receive three arguments:\n\n<table>\n  <tr>\n    <td>Argument</td>\n    <td>Details</td>\n  <tr>\n  <tr>\n    <td><code>config</code></td>\n    <td>This contains webpack configuration object that you can manipulate</td>\n  <tr>\n  <tr>\n    <td><code>context</code></td>\n    <td>This contains some keys that are useful for the build context\n      <br>\n      <table>\n        <tr>\n          <td><code>isDev</code></td>\n          <td><code>Boolean</code></td>\n          <td>Check if current build is for development</td>\n        </tr>\n        <tr>\n          <td><code>publicPath</code></td>\n          <td><code>String</code></td>\n          <td>Public Path of user defined or default's value</td>\n        </tr>\n        <tr>\n          <td><code>analyzeBundle</code></td>\n          <td><code>Boolean</code></td>\n          <td>Check if analyze bundle is enabled</td>\n        </tr>\n        <tr>\n          <td><code>buildDirectory</code></td>\n          <td><code>String</code></td>\n          <td>Build Directory of user defined or default's value</td>\n        </tr>\n        <tr>\n          <td><code>name</code></td>\n          <td><code>String</code></td>\n          <td>Build name <code>'server' || 'client'</code></td>\n        </tr>\n      </table>\n    </td>\n  <tr>\n  <tr>\n    <td><code>next</code></td>\n    <td>A callback required to be called to pass the custom configuration</td>\n  <tr>\n</table>\n\nExample of adding `ts-loader` through `maleo.config.js`:\n\n```js\n// maleo.config.js\n\n// Partly taken and modified from @airy/maleo-ts-plugin source\n// for simplicity purposes\n\nmodule.exports = {\n  webpack(config, context, next) {\n    const { isDev } = context\n\n    config.module.rules.push({\n      test: /\\.tsx?/,\n      exclude: /node_modules/,\n      use: [\n        require.resolve('@airy/maleo/lib/build/loaders/maleo-babel-loader'),\n        {\n          loader: 'ts-loader',\n          options: {\n            transpileOnly: true,\n          },\n        },\n      ],\n\n      if (isDev) {\n        config.plugins.push(new ForkTSCheckerWebpackPlugin());\n      }\n\n      return next();\n    })\n  },\n};\n```\n\n#### Customize Babel Config\n\nMaleo.js also let you have your own babel config. Just simply add `.babelrc` file at the root directory of your app.\n\nYou can include Maleo.js' babel preset in order to have latest JavaScript preset.\n\nHere's an example of `.babelrc` file:\n```json\n{\n  \"presets\": [\"@airy/maleo/babel\"],\n  \"plugins\": []\n}\n```\n\nThe `@airy/maleo/babel` preset includes everything you need to get your development started. The preset includes:\n- `@babel/preset-env`\n- `@babel/preset-react`\n- `@babel/plugin-proposal-class-properties`\n- `@babel/plugin-proposal-decorators`\n- `@babel/plugin-proposal-object-rest-spread`\n- `@babel/plugin-transform-runtime`\n- `react-loadable/babel`\n\n## Core Utilities Functions\n\n#### Route Matching\n\nTo use you can import the matching routes function like this:\n```js\nimport { getMatchedRoutes } from '@airy/maleo/utils';\n```\n\nThe function will return all matched routes and the route object from `routes.json`\n\n## CDN Support\n\nIf you are using a CDN, you can set up the `publicPath` setting and configure your CDN's origin to resolve to the domain that Maleo.js is hosted on.\n\n```js\n// maleo.config.js\n\nconst isProd = process.env.NODE_ENV === 'production';\n\nmodule.exports = {\n  assetPrefix: isProd && 'https://cdn.example.com';\n}\n```\n\n## FAQ\n\n== TO BE DETERMINED == \n\n## Plugins\n- [css-plugin](https://github.com/airyrooms/maleo-plugins/tree/master/packages/css-plugin)\n- [redux-plugin](https://github.com/airyrooms/maleo-plugins/tree/master/packages/redux-plugin)\n- [typescript-plugin](https://github.com/airyrooms/maleo-plugins/tree/master/packages/typescript-plugin)\n- [more](https://github.com/airyrooms/maleo-plugins)\n\n\n## Contributing\n[Please follow these steps to contribute to Maleo.js](https://github.com/airyrooms/maleo.js/blob/canary/CONTRIBUTING.md)\n\n[Please follow these steps to contribute to Maleo.js' plugins](https://github.com/airyrooms/maleo-plugins#contributing-guidelines)\n\n\n## Contributors\n\nThis project exists thanks to all the people who contribute. [Contribute](CONTRIBUTING.md).\n\nMany thanks to our **[contributors](https://github.com/airyrooms/maleo.js/graphs/contributors)**!\n\n\n## License\n\nMIT","readmeFilename":"README.md","bugs":{"url":"https://github.com/airyrooms/maleo.js/issues"},"homepage":"https://github.com/airyrooms/maleo.js#readme","_id":"@airy/maleo@0.3.1-canary.15","_nodeVersion":"10.16.0","_npmVersion":"lerna/3.13.1/node@v10.16.0+x64 (linux)","dist":{"integrity":"sha512-Ruo7n/fMgbyna3G0JA3PY/QufMBn1gJPxG/Q1Hs0IScL3rOna53lHKSlx42P2A1P8ac0dsaO/wDoxbzVtTTvkQ==","shasum":"0dc6c682a409ec6fdffedefea643d357ff379bbd","tarball":"https://registry.npmjs.org/@airy/maleo/-/maleo-0.3.1-canary.15.tgz","fileCount":96,"unpackedSize":333435,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJdEcPbCRA9TVsSAnZWagAAofIQAKS75jMpM13Hgg31juXW\n9sxZnTHNm0FDaMbgXwxA8a4HNDkYCwFuyuJlHe/Qyaupi+CmghgRs6A1Kg4y\nf6kMbe4W5TRuntDzOnAJI36iAnNoR69gA9fDnsqLFryB6VvPGWWaoJeSbD4p\nLLLNY3uNSHCGM3m2uX2Mf5PE/uM2RnsJCeBS3Y4Jxm9DDUvJasKYeG4Jh5wX\nqtNCj31yWPt9ra9nI5C1XVWMfOmiEEIUg5SfW4dG2oVkWGJCGDWXdwkTNoFO\nZirEThVSf84xaXf8Ua5kxvqlRM4e1K3mngyR51hIoIWisXh5SUoISIQSkBa5\n8r1fqoqOz39NfawVS/YalYVr9+JgQ8xxZjBWaM25iMwvMaTaqIjatV1tvjYM\noGOKOpMPIGABdRbPnEAI4MSlBWmnEBpNb5ldqr+Z95MIOg8RxT4ke97HvyEy\ncGWxgHt0B7WJ4JOWDNpH5m3fXa/f70Awp8yYCfe1yVeFDu8HG5AHO5/qAy9n\nuz1yy9knKSHnTfzMILxAQyspTe+3/V+WoG2eufU5+CEHJL0xI/tvDkE6fbdF\njqICc1WSyTGOsMX0JOGWcLu5XFqJgM1Qr3kxE6iMqr6Y4e1yyBedAmWFihRO\nZcw9tO/rKzqhEaaHguZhLvU6DLb6ETPkWeT0yNUvz+fQ00hRtptXIVRlW8Kq\nMXun\r\n=LRPn\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIFUSg5xah6JD7hlGq92PfbS+o8XAJLMy/D5n45QL4smMAiBgfK1w55HX5YtGhQx8XSHTayGR/e7BhkggUrACoKqdIg=="}]},"maintainers":[{"name":"airyrooms-engineering","email":"engineering@airyrooms.com"}],"_npmUser":{"name":"airyrooms-engineering","email":"engineering@airyrooms.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/maleo_0.3.1-canary.15_1561445338548_0.38113722051293775"},"_hasShrinkwrap":false},"0.3.1-canary.16":{"name":"@airy/maleo","version":"0.3.1-canary.16","description":"Un-opinionated Universal Rendering Javascript Framework","repository":{"type":"git","url":"git+https://github.com/airyrooms/maleo.js.git","bugs":"https://github.com/airyrooms/maleo.js/issues"},"author":{"name":"Airy"},"license":"MIT","private":false,"sideEffects":false,"scripts":{"build":"rm -rf dist && gulp","watch":"gulp watch","prepublish":"yarn build","start":"node server.js"},"bin":{"maleo":"lib/bin/maleo.js"},"dependencies":{"@babel/core":"7.1.2","@babel/plugin-proposal-class-properties":"7.1.0","@babel/plugin-proposal-decorators":"7.1.2","@babel/plugin-proposal-object-rest-spread":"7.0.0","@babel/plugin-syntax-dynamic-import":"7.0.0","@babel/plugin-transform-runtime":"7.1.0","@babel/preset-env":"7.1.0","@babel/preset-react":"7.0.0","@babel/runtime":"7.1.2","@babel/runtime-corejs2":"7.2.0","babel-loader":"8.0.4","case-sensitive-paths-webpack-plugin":"2.1.2","compression":"1.7.4","es6-promise":"4.2.5","eventsource":"1.0.7","express":"4.16.4","figlet":"1.2.1","friendly-errors-webpack-plugin":"1.7.0","hard-source-webpack-plugin":"0.13.1","helmet":"3.14.0","hoist-non-react-statics":"^3.3.0","isomorphic-fetch":"2.2.1","loader-utils":"1.1.0","node-notifier":"5.2.1","nodemon":"1.18.4","react-dom":"16.8.4","react-helmet":"5.2.0","react-loadable":"5.5.0","react-router-config":"5.0.0","react-router-dom":"5.0.0","rimraf":"2.6.3","source-map-support":"0.5.9","terser-webpack-plugin":"1.1.0","webpack":"4.29.0","webpack-bundle-analyzer":"3.0.3","webpack-cli":"3.1.2","webpack-dev-middleware":"3.4.0","webpack-hot-middleware":"2.24.3","webpackbar":"2.6.3"},"peerDependencies":{"react":"^16.8.4"},"devDependencies":{"@babel/preset-typescript":"^7.1.0","@types/webpack-bundle-analyzer":"^2.13.0","babel-plugin-module-resolver":"^3.1.1","gulp":"github:gulpjs/gulp#4.0","gulp-babel":"^8.0.0","gulp-clean":"^0.4.0","gulp-sourcemaps":"^2.6.4","gulp-uglify":"^3.0.1","gulp-watch":"^5.0.1","react":"^16.8.4"},"gitHead":"be7eb093031b2df649c7a3e8474addbf63dbd713","readme":"[![Build Status](https://travis-ci.org/airyrooms/maleo.js.svg?branch=master)](https://travis-ci.org/airyrooms/maleo.js)\n[![Coverage Status](https://coveralls.io/repos/github/airyrooms/maleo.js/badge.svg?branch=master)](https://coveralls.io/github/airyrooms/maleo.js?branch=master)\n[![npm version](https://badge.fury.io/js/%40airy%2Fmaleo.svg)](https://badge.fury.io/js/%40airy%2Fmaleo)\n[![Issues](http://img.shields.io/github/issues/airyrooms/maleo.js.svg)]( https://github.com/airyrooms/maleo.js/issues)\n[![MIT license](https://img.shields.io/badge/License-MIT-blue.svg)](https://github.com/airyrooms/maleo.js/blob/canary/LICENSE)\n[![Discord Chat](https://img.shields.io/discord/550498214789513218.svg)](https://discord.gg/9eArCQn)\n[![Conventional Commits](https://img.shields.io/badge/Conventional%20Commits-1.0.0-green.svg)](https://conventionalcommits.org)\n\n\n# Welcome to Maleo.js\n\nMaleo.js is an un-opinionated framework to enable Universal Rendering in JavaScript using React with no hassle.\n\nWe are here to solve the time consuming setups Universal Rendering Required.\n\n---\n\nReadme below is the documentation for the `canary` (prerelease) branch. To view the documentation for the latest stable Maleo.js version change branch to `master`\n\n---\n\n# Table of Contents\n- [Features](#features)\n- [Setup](#setup)\n- [Component Lifecycle](#component-lifecycle)\n- [Routing](#routing)\n- [Dynamic Import Component](#dynamic-import-component)\n  - [Tips](#tips)\n  - [Preloading](#preloading)\n- [Customizable Component](#customizable-component)\n  - [Custom Document](#custom-document)\n  - [Custom Wrap](#custom-wrap)\n- [Custom Configuration](#custom-configuration)\n  - [Customize Server](#customize-server)\n  - [Customize Webpack](#customize-webpack)\n  - [Customize Babel Config](#customize-babel-config)\n- [Core Utilities Functions](#core-utilities-functions)\n- [CDN Support](#cdn-support)\n- [Plugins](#plugins)\n- [FAQ](#faq)\n- [Contributing](#contributing)\n\n---\n\n## Features\n- Universal Rendering\n- Plugin based framework\n- Customizable\n\n## Setup\n\nInstall Maleo.js \n\n>*for now change `@airy/maleo` to `@airy/maleo@canary` until we publish the stable version of this package*\n\n\n**NPM**\n```bash\n$ npm install --save @airy/maleo react\n```\n**Yarn**\n```bash\n$ yarn add @airy/maleo react\n```\n\nAdd this script to your `package.json`\n```json\n{\n  \"scripts\": {\n    \"dev\": \"maleo dev\",\n    \"build\": \"export NODE_ENV=production && maleo build\",\n    \"start\": \"export NODE_ENV=production && maleo run\"\n  }\n}\n```\n\nCreate a page Root component\n```jsx\n// ./src/Root.jsx\nimport React from 'react';\n\n// Export default is required for registering page\nexport default class RootComponent extends React.Component {\n  render() {\n    return (\n      <h1>Hello World!</h1>\n    )\n  }\n}\n```\n\nAnd lastly, create a routing file on your project root directory called `routes.json` and register your page component\n```json\n[\n  {\n    \"path\": \"/\",\n    \"page\": \"./src/Root\"\n  }\n]\n```\n\nAfter that you can now run `$ npm run dev` and go to `http://localhost:3000`.\n\nYou should now see your app running on your browser.\n\nBy now you should see \n- Automatic transpilation and bundling (with webpack and babel)\n- ~~Hot code reloading~~ [#17](https://github.com/airyrooms/maleo.js/issues/17)\n- Server rendering\n\nTo see how simple this is, check out the sample app!\n\n## Component Lifecycle \n\nMaleo.js added a new component lifecycle hook called `getInitialProps`, this function is called during Server Side Rendering (SSR) and during route changes on Client Side Rendering (CSR).\n\nThis is useful especially for SEO purposes. \n\nExample for stateful component:\n```jsx\nimport React from 'react';\n\nexport default class extends React.Component {\n  static getInitialProps = async (ctx) => {\n    const { req } = ctx;\n\n    // check if getInitialProps is called on server or on client\n    const userAgent = req ? req.headers['user-agent'] : navigator.userAgent;\n\n    // the return value will be passed as props for this component\n    return { userAgent };\n  }\n\n  render() {\n    return (\n      <div>\n        Hello World {this.props.userAgent}\n      </div>\n    );\n  }\n}\n```\n\nExample for stateless component:\n```jsx\nconst Component = (props) => (\n  <div>\n    Hello World {props.userAgent}\n  </div>\n);\n\nComponent.getInitialprops = async (ctx) => {\n  const { req } = ctx;\n\n  const userAgent = req ? req.headers['user-agent'] : navigator.userAgent;\n\n  return { userAgent };\n};\n\nexport default Component;\n```\n\n`getInitialProps` receives a context object with the following properties:\n- `req` - HTTP request object (server only)\n- `res` - HTTP response object (server only)\n- `...wrapProps` - Spreaded properties from custom Wrap\n- `...appProps` - Spreaded properties from custom App\n\n## Routing\n\nRouting is declared in a centralized route config.\nRegister all the route config in `routes.json` file.\n\nIf you put the `routes.json` files on root directory, Maleo will automatically register your route. Otherwise put path to your routes on [Maleo config](#custom-configuration).\n\nRoutes file has to export default the route configuration.\nThe route object **expected to have distinct key** to indicate the route.\n\n<table>\n  <tr>\n    <td>Key</td>\n    <td>Type</td>\n    <td>Description</td>\n  </tr>\n  <tr>\n    <td><code>path</code></td>\n    <td><code>String!</code></td>\n    <td>Routes path</td>\n  </tr>\n  <tr>\n    <td><code>page</code></td>\n    <td><code>String!</code></td>\n    <td>Path to React Component for this route path</td>\n  </tr>\n  <tr>\n    <td><code>exact</code></td>\n    <td><code>Boolean?</code> [<code>false</code>]</td>\n    <td>To make url has to match exactly the path in order to render the component. Give <code>false</code> value if the route is a wrapper route component</td>\n  </tr>\n  <tr>\n    <td><code>routes</code></td>\n    <td><code>RouteObject?</code></td>\n    <td>Nested route</td>\n  </tr>\n</table>\n\n\nFor example:\n```json\n[\n  {\n    \"page\": \"./src/MainApp\",\n    \"routes\": [\n      {\n        \"path\": \"/\",\n        \"page\": \"./src/Search\",\n        \"exact\": true\n      },\n      {\n        \"path\": \"/search\",\n        \"page\": \"./src/Search\",\n        \"routes\": [\n          {\n            \"path\": \"/search/hello\",\n            \"page\": \"./src/Detail\",\n            \"exact\": true\n          }\n        ]\n      },\n      {\n        \"path\": \"/detail\",\n        \"page\": \"./src/Detail\",\n        \"exact\": true\n      }\n    ]\n  }\n]\n```\n\n## Dynamic Import Component\nMaleo.js supports TC39 [dynamic import proposal](https://github.com/tc39/proposal-dynamic-import) for JavaScript.\n\nYou can think dynamic import as another way to split your code into manageable chunks. You can use our `Dynamic` function which utilizes [react loadable](https://github.com/jamiebuilds/react-loadable)\n\nFor Example\n```js\n// DynamicComponent.js\nimport Dynamic from '@airy/maleo/dynamic';\n\nexport default Dynamic({\n  loader: () => import( /* webpackChunkName:\"DynamicComponent\" */ './component/DynamicLoad'),\n})\n```\n\n### **Preloading**\n\nFor optimization purposes, you can also preload a component even before the component got rendered.\n\nFor example, if you want to load component when a button get pressed, you can start preloading the component when the user hovers the mouse over the button.\n\nThe component created by `Dynamic` exposes a [static `preload` method](https://github.com/jamiebuilds/react-loadable#loadablecomponentpreload).\n\n```jsx\nimport React from 'react';\nimport Dynamic from '@airy/maleo/dynamic';\n\nconst DynamicBar = Dynamic({\n  loader: () => import('./Bar'),\n  loading: LoadingCompoinent\n});\n\nclass MyComponent extends React.Component {\n  state = { showBar: false };\n\n  onClick = () => {\n    this.setState({ showBar: true });\n  };\n\n  onMouseOver = () => DynamicBar.preload();\n\n  render() {\n    return (\n      <div>\n        <button\n          onClick={this.onClick}\n          onMouseOver={this.onMouseOver}>\n          Show Bar\n        </button>\n        { this.state.showBar && <DynamicBar /> }\n      </div>\n    )\n  }\n}\n```\n\n## Customizable Component\n\n### Custom Document\n\nHighly inspired by what [Next.js](https://github.com/zeit/next.js) has done on their awesome template customization.\n\nMaleo.js also enable customization on `Document` as document's markup. So you don't need to include tags like `<html>`, `<head>`, `<body>`, etc. \n\nTo override the default behavior, you'll need to create a component that extends the `Document` React class provided by Maleo.\n\n```jsx\n// document.jsx\nimport React from 'react';\nimport { Document, Header, Main, Scripts } from '@airy/maleo/document';\n\nexport default class extends Document {\n  render() {\n    return (\n      <html>\n        <Header>\n          <title>Maleo JS</title>\n          <meta charset=\"utf-8\" />\n          <meta name=\"description\" content=\"Maleo.js is awesome!\" />\n\n          <style>\n            {` body { background-color: #fff } `}\n          </style>\n        </Header>\n\n        <body>\n          <Main />\n\n          <Scripts />\n\n          <ReduxScript />\n        </body>\n      </html>\n    );\n  }\n}\n```\n\n### Custom Wrap\n\nMaleo.js uses the `Wrap` component to initialize pages. `Wrap` contains React Router's Component. You can add HoC here to wrap the application and control the page initialization. Which allows you to do amazing things like:\n- Persisting layour between page changes\n- Keeping state when navigating pages\n- Custom error handling using `componentDidCatch`\n- Inject additional data into pages (like Redux provider, etc)\n\nTo override the default behavior, you'll need to create a component that extends the `Wrap` React class provided by Maleo.\n\n```jsx\n// wrap.jsx\nimport React from 'react';\nimport { Wrap } from '@airy/maleo/wrap';\n\n// Redux plugin for Maleo.js\n// Hoc that creates store and add Redux Provider\nimport { withRedux } from '@airy/maleo-redux-plugin';\n\n// Custom Wrapper that will be rendered for the whole Application\nimport Content from './component/Content';\nimport NavigationBar from './component/NavigationBar';\n\nimport { createStore } from './store';\n\n@withRedux(createStore)\nexport default class extends Wrap {\n  static getInitialProps = (ctx) => {\n    const { store } = ctx\n    // you receive store from context\n    // you can access or do something with the store here\n    console.log('Initialized Redux Store', store);\n    return {}\n  }\n\n  render() {\n    const { Container, containerProps, App, appProps } = this.props\n\n    return (\n      <Container {...containerProps}>\n        <NavigationBar />\n        <Content>\n          <App {...appProps}/>\n        </Content>\n      </Container>\n    )\n  }\n}\n\n```\nIf you put `document.jsx` and `wrap.jsx` on root directory (the same level with `package.json`), then Maleo will automatically register your custom Document and Wrap. Otherwise, you can add the path to your custom Document and Wrap on [Maleo config](#custom-configuration)\n\n---\n***We are also working on adding default and customizable `Error` component page***\n\n---\n\n## Custom Configuration\n\nFor more advanced configuration of Maleo.js, like `webpack` config, registering `plugins`, path to your routes, custom Document and Wrap, and adding `path alias`, you can create a `maleo.config.js` in the root of your project directory. (same directory with `package.json`)\n\n```js\n// maleo.config.js\n\nmodule.exports = {\n  /* config options here */\n}\n```\n\nHere are the API's for the configuration:\n\n<table>\n  <tr>\n    <td>Key</td>\n    <td>Type</td>\n    <td>Description</td>\n  </tr>\n  <tr>\n    <td><code>favicon</code></td>\n    <td><code>String</code> [<code>{project-dir}/favicon.ico</code>]</td>\n    <td>Path to favicon file</td>\n  </tr>\n  <tr>\n    <td><code>buildDir</code></td>\n    <td><code>String?</code> [<code>.maleo</code>]</td>\n    <td>Directory to put Maleo.js' build assets</td>\n  </tr>\n  <tr>\n    <td><code>cache</code></td>\n    <td><code>Boolean?</code> [<code>true</code>]</td>\n    <td>Enable webpack build caching</td>\n  </tr>\n  <tr>\n    <td><code>isDev</code></td>\n    <td><code>Boolean?</code> [<code>process.env.NODE_ENV === 'development'</code>]</td>\n    <td>Enable development build configuration</td>\n  </tr>\n  <tr>\n    <td><code>sourceMaps</code></td>\n    <td><code>Boolean?</code> [<code>true</code>]</td>\n    <td>Enable webpack to generate source maps</td>\n  </tr>\n  <tr>\n    <td><code>alias</code></td>\n    <td><code>Object?</code></td>\n    <td>A key value pair for aliasing path directory\n      <br>\n      <code>\n      { 'component': './src/component' }\n      </code>\n    </td>\n  </tr>\n  <tr>\n    <td><code>publicPath</code></td>\n    <td><code>String?</code> [<code>/_assets/</code>]</td>\n    <td>To customize webpack's <code>publicPath</code> \n    <br>Comes in handy if using CDN to put built assets</td>\n  </tr>\n  <tr>\n    <td><code>analyzeBundle</code></td>\n    <td><code>Boolean?</code> [<code>false</code>]</td>\n    <td>To enable webpack's bundle analyzer, for analyzing bundle sizes during bundle debugging should Maleo.js' build process got slow</td>\n  </tr>\n  <tr>\n    <td><code>webpack</code></td>\n    <td><code>Function?</code></td>\n    <td>To customize webpack configuration, more details <a href=\"#customize-webpack\">here</a></td>\n  </tr>\n  <tr>\n    <td><code>routes</code></td>\n    <td><code>string?</code> [<code>rootDir/routes.jsx</code>]</td>\n    <td>Path to your routes file</td>\n  </tr>\n  <tr>\n    <td><code>customDocument</code></td>\n    <td><code>string?</code> [<code>rootDir/document.jsx</code>]</td>\n    <td>Path to your custom document file</td>\n  </tr>\n  <tr>\n    <td><code>customWrap</code></td>\n    <td><code>string?</code> [<code>rootDir/wrap.jsx</code>]</td>\n    <td>Path to your custom wrap file</td>\n  </tr>\n  <tr>\n    <td><code>customApp</code></td>\n    <td><code>string?</code> [<code>rootDir/app.jsx</code>]</td>\n    <td>Path to your custom app file</td>\n  </tr>\n</table>\n\n#### Customize Server\n\nCreate a `server.js` file on root directory where your `package.json` lives.\nHere you can customize Maleo's server.\n```js\nimport { Server } from '@airy/maleo/server';\n\nconst PORT = process.env.PORT || 3000;\n\nconst maleoServer = Server.init({\n  port: PORT,\n  runHandler: () => {\n    console.log('Server running on port :', PORT);\n  }\n});\n\nmaleoServer.run();\n```\n\nHere are the API's for the configuration:\n\n<table>\n  <tr>\n    <td>Key</td>\n    <td>Type</td>\n    <td>Description</td>\n  </tr>\n  <tr>\n    <td><code>port</code></td>\n    <td><code>Number?</code> [<code>3000</code>]</td>\n    <td>Port to run Maleo server</td>\n  </tr>\n  <tr>\n    <td><code>assetDir</code></td>\n    <td><code>String?</code> [<code>'<root>/.maleo/client'</code>]</td>\n    <td>Directory for all client related assets</td>\n  </tr>\n  <tr>\n    <td><code>runHandler</code></td>\n    <td><code>Function?</code></td>\n    <td>Function called when maleo server starter</td>\n  </tr>\n  <tr>\n    <td><code>csp</code></td>\n    <td><code>cspConfig?</code> [<code>{\n      directives: {\n        defaultSrc: [`'self'`],\n        styleSrc: [`'self'`],\n      },\n    }</code>]</td>\n    <td>Config for <a href=\"https://developers.google.com/web/fundamentals/security/csp/\">Content Security Policy</a>, using <a href=\"https://helmetjs.github.io/docs/csp/\">Helmet-CSP</a></td>\n  </tr>\n</table>\n\n#### Customize Webpack\n\nYou are able to extend Maleo.js' default webpack configuration by defining a function on `maleo.config.js`\n\n```js\n// maleo.config.js\n\nmodule.exports = {\n  webpack(config, context, next) {\n    // Perform customizations to webpack config\n    // Important: This call is required in order to let Maleo pass the config to webpack\n    return next(); \n  },\n};\n```\n\nWebpack function will receive three arguments:\n\n<table>\n  <tr>\n    <td>Argument</td>\n    <td>Details</td>\n  <tr>\n  <tr>\n    <td><code>config</code></td>\n    <td>This contains webpack configuration object that you can manipulate</td>\n  <tr>\n  <tr>\n    <td><code>context</code></td>\n    <td>This contains some keys that are useful for the build context\n      <br>\n      <table>\n        <tr>\n          <td><code>isDev</code></td>\n          <td><code>Boolean</code></td>\n          <td>Check if current build is for development</td>\n        </tr>\n        <tr>\n          <td><code>publicPath</code></td>\n          <td><code>String</code></td>\n          <td>Public Path of user defined or default's value</td>\n        </tr>\n        <tr>\n          <td><code>analyzeBundle</code></td>\n          <td><code>Boolean</code></td>\n          <td>Check if analyze bundle is enabled</td>\n        </tr>\n        <tr>\n          <td><code>buildDirectory</code></td>\n          <td><code>String</code></td>\n          <td>Build Directory of user defined or default's value</td>\n        </tr>\n        <tr>\n          <td><code>name</code></td>\n          <td><code>String</code></td>\n          <td>Build name <code>'server' || 'client'</code></td>\n        </tr>\n      </table>\n    </td>\n  <tr>\n  <tr>\n    <td><code>next</code></td>\n    <td>A callback required to be called to pass the custom configuration</td>\n  <tr>\n</table>\n\nExample of adding `ts-loader` through `maleo.config.js`:\n\n```js\n// maleo.config.js\n\n// Partly taken and modified from @airy/maleo-ts-plugin source\n// for simplicity purposes\n\nmodule.exports = {\n  webpack(config, context, next) {\n    const { isDev } = context\n\n    config.module.rules.push({\n      test: /\\.tsx?/,\n      exclude: /node_modules/,\n      use: [\n        require.resolve('@airy/maleo/lib/build/loaders/maleo-babel-loader'),\n        {\n          loader: 'ts-loader',\n          options: {\n            transpileOnly: true,\n          },\n        },\n      ],\n\n      if (isDev) {\n        config.plugins.push(new ForkTSCheckerWebpackPlugin());\n      }\n\n      return next();\n    })\n  },\n};\n```\n\n#### Customize Babel Config\n\nMaleo.js also let you have your own babel config. Just simply add `.babelrc` file at the root directory of your app.\n\nYou can include Maleo.js' babel preset in order to have latest JavaScript preset.\n\nHere's an example of `.babelrc` file:\n```json\n{\n  \"presets\": [\"@airy/maleo/babel\"],\n  \"plugins\": []\n}\n```\n\nThe `@airy/maleo/babel` preset includes everything you need to get your development started. The preset includes:\n- `@babel/preset-env`\n- `@babel/preset-react`\n- `@babel/plugin-proposal-class-properties`\n- `@babel/plugin-proposal-decorators`\n- `@babel/plugin-proposal-object-rest-spread`\n- `@babel/plugin-transform-runtime`\n- `react-loadable/babel`\n\n## Core Utilities Functions\n\n#### Route Matching\n\nTo use you can import the matching routes function like this:\n```js\nimport { getMatchedRoutes } from '@airy/maleo/utils';\n```\n\nThe function will return all matched routes and the route object from `routes.json`\n\n## CDN Support\n\nIf you are using a CDN, you can set up the `publicPath` setting and configure your CDN's origin to resolve to the domain that Maleo.js is hosted on.\n\n```js\n// maleo.config.js\n\nconst isProd = process.env.NODE_ENV === 'production';\n\nmodule.exports = {\n  assetPrefix: isProd && 'https://cdn.example.com';\n}\n```\n\n## FAQ\n\n== TO BE DETERMINED == \n\n## Plugins\n- [css-plugin](https://github.com/airyrooms/maleo-plugins/tree/master/packages/css-plugin)\n- [redux-plugin](https://github.com/airyrooms/maleo-plugins/tree/master/packages/redux-plugin)\n- [typescript-plugin](https://github.com/airyrooms/maleo-plugins/tree/master/packages/typescript-plugin)\n- [more](https://github.com/airyrooms/maleo-plugins)\n\n\n## Contributing\n[Please follow these steps to contribute to Maleo.js](https://github.com/airyrooms/maleo.js/blob/canary/CONTRIBUTING.md)\n\n[Please follow these steps to contribute to Maleo.js' plugins](https://github.com/airyrooms/maleo-plugins#contributing-guidelines)\n\n\n## Contributors\n\nThis project exists thanks to all the people who contribute. [Contribute](CONTRIBUTING.md).\n\nMany thanks to our **[contributors](https://github.com/airyrooms/maleo.js/graphs/contributors)**!\n\n\n## License\n\nMIT","readmeFilename":"README.md","bugs":{"url":"https://github.com/airyrooms/maleo.js/issues"},"homepage":"https://github.com/airyrooms/maleo.js#readme","_id":"@airy/maleo@0.3.1-canary.16","_nodeVersion":"10.16.0","_npmVersion":"lerna/3.13.1/node@v10.16.0+x64 (linux)","dist":{"integrity":"sha512-Y5my20aiqOKa+Oc8wtB7umO2CRX8nyVWFDLXrdvfhNB2mzn+qyLaXhlnu1iNjsh3If4vdfAcKFiaUaQIulgEyw==","shasum":"f4f06b04e9eeed6037668d0d6328fdcacd8a55c4","tarball":"https://registry.npmjs.org/@airy/maleo/-/maleo-0.3.1-canary.16.tgz","fileCount":96,"unpackedSize":334553,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJdJqXSCRA9TVsSAnZWagAAny4P/jD7BpZ8UguOjE7w3vDZ\nNmO/USeS8KGK37cCq50cpqsHcQDddCmVNoyYttcKPEeG1uJ63pYL7OSA3AVz\nBjvdFwnwzu6owU+iPixBTCSMKc4W3gp6oe5dHDQ0oZvpg6AJ7kykrDjsNxym\n7WBcLWRVrgpp4H4hYR+cGrvpI2x6Gwouz5SpQVO/WW7bw9K0dVroWaXn+osd\nqX9oGoLqSiIDMc9oGMT+ZD0UwEbg3f0DkCDoQ+uxvTJHYa8QMKHIUaT7LJA+\nIMXGGEabVyeqoeFmV6CdTZdj2ZSQsgya9k3HaUNs2QRceoed3bjtJyoZ21Tg\n4dt8/vrMvM9o1tC3TFA4usn3uSQo5sHBdfe7efio+cPWW1v+If1jOXLsN4lt\ni6q9B//325hB4jWJ8hByvc84wMdS7rxfGpWHMzTB/trP0OBmrsSMNyirAKoC\nqDkvtP0C9j9CTZTsSUVDG3E7Em0/9taRspwXEICSojyXvvILDcovEyKR6OlL\nkt0BBh6IJxzhdI0dQ+DSpYZcqH6mRO2P4hoRdo3B1zwmbHWbAcRg1PwMGn8q\nyxwC4jMiN2I6ez0Fe6BQQ4HmSNHIRMvhOTJZYAIK4RlAclrAdVAZHJ1V22Eb\nSQJviyG0SviDxXBOxkq0fZTN1UZfWF1F0CwDvNoQ6mSV5UcImE+abrsTgg0u\nk/jB\r\n=w8IT\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDybKi0UXiMDX2nSR/YG7dmghCbFFubThbfzmasvq9qjwIhAKj6il3Rv1lEZWk/XPDTjI5rnF4lFlpK6CS/eWOJzbx0"}]},"maintainers":[{"name":"airyrooms-engineering","email":"engineering@airyrooms.com"}],"_npmUser":{"name":"airyrooms-engineering","email":"engineering@airyrooms.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/maleo_0.3.1-canary.16_1562813906054_0.17096755057318402"},"_hasShrinkwrap":false},"0.3.1-canary.17":{"name":"@airy/maleo","version":"0.3.1-canary.17","description":"Un-opinionated Universal Rendering Javascript Framework","repository":{"type":"git","url":"git+https://github.com/airyrooms/maleo.js.git","bugs":"https://github.com/airyrooms/maleo.js/issues"},"author":{"name":"Airy"},"license":"MIT","private":false,"sideEffects":false,"scripts":{"build":"rm -rf dist && gulp","watch":"gulp watch","prepublish":"yarn build","start":"node server.js"},"bin":{"maleo":"lib/bin/maleo.js"},"dependencies":{"@babel/core":"7.1.2","@babel/plugin-proposal-class-properties":"7.1.0","@babel/plugin-proposal-decorators":"7.1.2","@babel/plugin-proposal-object-rest-spread":"7.0.0","@babel/plugin-syntax-dynamic-import":"7.0.0","@babel/plugin-transform-runtime":"7.1.0","@babel/preset-env":"7.1.0","@babel/preset-react":"7.0.0","@babel/runtime":"7.1.2","@babel/runtime-corejs2":"7.2.0","babel-loader":"8.0.4","case-sensitive-paths-webpack-plugin":"2.1.2","compression":"1.7.4","es6-promise":"4.2.5","eventsource":"1.0.7","express":"4.16.4","figlet":"1.2.1","friendly-errors-webpack-plugin":"1.7.0","hard-source-webpack-plugin":"0.13.1","helmet":"3.14.0","hoist-non-react-statics":"^3.3.0","isomorphic-fetch":"2.2.1","loader-utils":"1.1.0","node-notifier":"5.2.1","nodemon":"1.18.4","react-dom":"16.8.4","react-helmet":"5.2.0","react-loadable":"5.5.0","react-router-config":"5.0.0","react-router-dom":"5.0.0","rimraf":"2.6.3","source-map-support":"0.5.9","terser-webpack-plugin":"1.1.0","webpack":"4.29.0","webpack-bundle-analyzer":"3.0.3","webpack-cli":"3.1.2","webpack-dev-middleware":"3.4.0","webpack-hot-middleware":"2.24.3","webpackbar":"2.6.3"},"peerDependencies":{"react":"^16.8.4"},"devDependencies":{"@babel/preset-typescript":"^7.1.0","@types/webpack-bundle-analyzer":"^2.13.0","babel-plugin-module-resolver":"^3.1.1","gulp":"github:gulpjs/gulp#4.0","gulp-babel":"^8.0.0","gulp-clean":"^0.4.0","gulp-sourcemaps":"^2.6.4","gulp-uglify":"^3.0.1","gulp-watch":"^5.0.1","react":"^16.8.4"},"gitHead":"8c603bc30d7aed73a0abb934590ad03f313db53d","readme":"[![Build Status](https://travis-ci.org/airyrooms/maleo.js.svg?branch=master)](https://travis-ci.org/airyrooms/maleo.js)\n[![Coverage Status](https://coveralls.io/repos/github/airyrooms/maleo.js/badge.svg?branch=master)](https://coveralls.io/github/airyrooms/maleo.js?branch=master)\n[![npm version](https://badge.fury.io/js/%40airy%2Fmaleo.svg)](https://badge.fury.io/js/%40airy%2Fmaleo)\n[![Issues](http://img.shields.io/github/issues/airyrooms/maleo.js.svg)]( https://github.com/airyrooms/maleo.js/issues)\n[![MIT license](https://img.shields.io/badge/License-MIT-blue.svg)](https://github.com/airyrooms/maleo.js/blob/canary/LICENSE)\n[![Discord Chat](https://img.shields.io/discord/550498214789513218.svg)](https://discord.gg/9eArCQn)\n[![Conventional Commits](https://img.shields.io/badge/Conventional%20Commits-1.0.0-green.svg)](https://conventionalcommits.org)\n\n\n# Welcome to Maleo.js\n\nMaleo.js is an un-opinionated framework to enable Universal Rendering in JavaScript using React with no hassle.\n\nWe are here to solve the time consuming setups Universal Rendering Required.\n\n---\n\nReadme below is the documentation for the `canary` (prerelease) branch. To view the documentation for the latest stable Maleo.js version change branch to `master`\n\n---\n\n# Table of Contents\n- [Features](#features)\n- [Setup](#setup)\n- [Component Lifecycle](#component-lifecycle)\n- [Routing](#routing)\n- [Dynamic Import Component](#dynamic-import-component)\n  - [Tips](#tips)\n  - [Preloading](#preloading)\n- [Customizable Component](#customizable-component)\n  - [Custom Document](#custom-document)\n  - [Custom Wrap](#custom-wrap)\n- [Custom Configuration](#custom-configuration)\n  - [Customize Server](#customize-server)\n  - [Customize Webpack](#customize-webpack)\n  - [Customize Babel Config](#customize-babel-config)\n- [Core Utilities Functions](#core-utilities-functions)\n- [CDN Support](#cdn-support)\n- [Plugins](#plugins)\n- [FAQ](#faq)\n- [Contributing](#contributing)\n\n---\n\n## Features\n- Universal Rendering\n- Plugin based framework\n- Customizable\n\n## Setup\n\nInstall Maleo.js \n\n>*for now change `@airy/maleo` to `@airy/maleo@canary` until we publish the stable version of this package*\n\n\n**NPM**\n```bash\n$ npm install --save @airy/maleo react\n```\n**Yarn**\n```bash\n$ yarn add @airy/maleo react\n```\n\nAdd this script to your `package.json`\n```json\n{\n  \"scripts\": {\n    \"dev\": \"maleo dev\",\n    \"build\": \"export NODE_ENV=production && maleo build\",\n    \"start\": \"export NODE_ENV=production && maleo run\"\n  }\n}\n```\n\nCreate a page Root component\n```jsx\n// ./src/Root.jsx\nimport React from 'react';\n\n// Export default is required for registering page\nexport default class RootComponent extends React.Component {\n  render() {\n    return (\n      <h1>Hello World!</h1>\n    )\n  }\n}\n```\n\nAnd lastly, create a routing file on your project root directory called `routes.json` and register your page component\n```json\n[\n  {\n    \"path\": \"/\",\n    \"page\": \"./src/Root\"\n  }\n]\n```\n\nAfter that you can now run `$ npm run dev` and go to `http://localhost:3000`.\n\nYou should now see your app running on your browser.\n\nBy now you should see \n- Automatic transpilation and bundling (with webpack and babel)\n- ~~Hot code reloading~~ [#17](https://github.com/airyrooms/maleo.js/issues/17)\n- Server rendering\n\nTo see how simple this is, check out the sample app!\n\n## Component Lifecycle \n\nMaleo.js added a new component lifecycle hook called `getInitialProps`, this function is called during Server Side Rendering (SSR) and during route changes on Client Side Rendering (CSR).\n\nThis is useful especially for SEO purposes. \n\nExample for stateful component:\n```jsx\nimport React from 'react';\n\nexport default class extends React.Component {\n  static getInitialProps = async (ctx) => {\n    const { req } = ctx;\n\n    // check if getInitialProps is called on server or on client\n    const userAgent = req ? req.headers['user-agent'] : navigator.userAgent;\n\n    // the return value will be passed as props for this component\n    return { userAgent };\n  }\n\n  render() {\n    return (\n      <div>\n        Hello World {this.props.userAgent}\n      </div>\n    );\n  }\n}\n```\n\nExample for stateless component:\n```jsx\nconst Component = (props) => (\n  <div>\n    Hello World {props.userAgent}\n  </div>\n);\n\nComponent.getInitialprops = async (ctx) => {\n  const { req } = ctx;\n\n  const userAgent = req ? req.headers['user-agent'] : navigator.userAgent;\n\n  return { userAgent };\n};\n\nexport default Component;\n```\n\n`getInitialProps` receives a context object with the following properties:\n- `req` - HTTP request object (server only)\n- `res` - HTTP response object (server only)\n- `...wrapProps` - Spreaded properties from custom Wrap\n- `...appProps` - Spreaded properties from custom App\n\n## Routing\n\nRouting is declared in a centralized route config.\nRegister all the route config in `routes.json` file.\n\nIf you put the `routes.json` files on root directory, Maleo will automatically register your route. Otherwise put path to your routes on [Maleo config](#custom-configuration).\n\nRoutes file has to export default the route configuration.\nThe route object **expected to have distinct key** to indicate the route.\n\n<table>\n  <tr>\n    <td>Key</td>\n    <td>Type</td>\n    <td>Description</td>\n  </tr>\n  <tr>\n    <td><code>path</code></td>\n    <td><code>String!</code></td>\n    <td>Routes path</td>\n  </tr>\n  <tr>\n    <td><code>page</code></td>\n    <td><code>String!</code></td>\n    <td>Path to React Component for this route path</td>\n  </tr>\n  <tr>\n    <td><code>exact</code></td>\n    <td><code>Boolean?</code> [<code>false</code>]</td>\n    <td>To make url has to match exactly the path in order to render the component. Give <code>false</code> value if the route is a wrapper route component</td>\n  </tr>\n  <tr>\n    <td><code>routes</code></td>\n    <td><code>RouteObject?</code></td>\n    <td>Nested route</td>\n  </tr>\n</table>\n\n\nFor example:\n```json\n[\n  {\n    \"page\": \"./src/MainApp\",\n    \"routes\": [\n      {\n        \"path\": \"/\",\n        \"page\": \"./src/Search\",\n        \"exact\": true\n      },\n      {\n        \"path\": \"/search\",\n        \"page\": \"./src/Search\",\n        \"routes\": [\n          {\n            \"path\": \"/search/hello\",\n            \"page\": \"./src/Detail\",\n            \"exact\": true\n          }\n        ]\n      },\n      {\n        \"path\": \"/detail\",\n        \"page\": \"./src/Detail\",\n        \"exact\": true\n      }\n    ]\n  }\n]\n```\n\n## Dynamic Import Component\nMaleo.js supports TC39 [dynamic import proposal](https://github.com/tc39/proposal-dynamic-import) for JavaScript.\n\nYou can think dynamic import as another way to split your code into manageable chunks. You can use our `Dynamic` function which utilizes [react loadable](https://github.com/jamiebuilds/react-loadable)\n\nFor Example\n```js\n// DynamicComponent.js\nimport Dynamic from '@airy/maleo/dynamic';\n\nexport default Dynamic({\n  loader: () => import( /* webpackChunkName:\"DynamicComponent\" */ './component/DynamicLoad'),\n})\n```\n\n### **Preloading**\n\nFor optimization purposes, you can also preload a component even before the component got rendered.\n\nFor example, if you want to load component when a button get pressed, you can start preloading the component when the user hovers the mouse over the button.\n\nThe component created by `Dynamic` exposes a [static `preload` method](https://github.com/jamiebuilds/react-loadable#loadablecomponentpreload).\n\n```jsx\nimport React from 'react';\nimport Dynamic from '@airy/maleo/dynamic';\n\nconst DynamicBar = Dynamic({\n  loader: () => import('./Bar'),\n  loading: LoadingCompoinent\n});\n\nclass MyComponent extends React.Component {\n  state = { showBar: false };\n\n  onClick = () => {\n    this.setState({ showBar: true });\n  };\n\n  onMouseOver = () => DynamicBar.preload();\n\n  render() {\n    return (\n      <div>\n        <button\n          onClick={this.onClick}\n          onMouseOver={this.onMouseOver}>\n          Show Bar\n        </button>\n        { this.state.showBar && <DynamicBar /> }\n      </div>\n    )\n  }\n}\n```\n\n## Customizable Component\n\n### Custom Document\n\nHighly inspired by what [Next.js](https://github.com/zeit/next.js) has done on their awesome template customization.\n\nMaleo.js also enable customization on `Document` as document's markup. So you don't need to include tags like `<html>`, `<head>`, `<body>`, etc. \n\nTo override the default behavior, you'll need to create a component that extends the `Document` React class provided by Maleo.\n\n```jsx\n// document.jsx\nimport React from 'react';\nimport { Document, Header, Main, Scripts } from '@airy/maleo/document';\n\nexport default class extends Document {\n  render() {\n    return (\n      <html>\n        <Header>\n          <title>Maleo JS</title>\n          <meta charset=\"utf-8\" />\n          <meta name=\"description\" content=\"Maleo.js is awesome!\" />\n\n          <style>\n            {` body { background-color: #fff } `}\n          </style>\n        </Header>\n\n        <body>\n          <Main />\n\n          <Scripts />\n\n          <ReduxScript />\n        </body>\n      </html>\n    );\n  }\n}\n```\n\n### Custom Wrap\n\nMaleo.js uses the `Wrap` component to initialize pages. `Wrap` contains React Router's Component. You can add HoC here to wrap the application and control the page initialization. Which allows you to do amazing things like:\n- Persisting layour between page changes\n- Keeping state when navigating pages\n- Custom error handling using `componentDidCatch`\n- Inject additional data into pages (like Redux provider, etc)\n\nTo override the default behavior, you'll need to create a component that extends the `Wrap` React class provided by Maleo.\n\n```jsx\n// wrap.jsx\nimport React from 'react';\nimport { Wrap } from '@airy/maleo/wrap';\n\n// Redux plugin for Maleo.js\n// Hoc that creates store and add Redux Provider\nimport { withRedux } from '@airy/maleo-redux-plugin';\n\n// Custom Wrapper that will be rendered for the whole Application\nimport Content from './component/Content';\nimport NavigationBar from './component/NavigationBar';\n\nimport { createStore } from './store';\n\n@withRedux(createStore)\nexport default class extends Wrap {\n  static getInitialProps = (ctx) => {\n    const { store } = ctx\n    // you receive store from context\n    // you can access or do something with the store here\n    console.log('Initialized Redux Store', store);\n    return {}\n  }\n\n  render() {\n    const { Container, containerProps, App, appProps } = this.props\n\n    return (\n      <Container {...containerProps}>\n        <NavigationBar />\n        <Content>\n          <App {...appProps}/>\n        </Content>\n      </Container>\n    )\n  }\n}\n\n```\nIf you put `document.jsx` and `wrap.jsx` on root directory (the same level with `package.json`), then Maleo will automatically register your custom Document and Wrap. Otherwise, you can add the path to your custom Document and Wrap on [Maleo config](#custom-configuration)\n\n---\n***We are also working on adding default and customizable `Error` component page***\n\n---\n\n## Custom Configuration\n\nFor more advanced configuration of Maleo.js, like `webpack` config, registering `plugins`, path to your routes, custom Document and Wrap, and adding `path alias`, you can create a `maleo.config.js` in the root of your project directory. (same directory with `package.json`)\n\n```js\n// maleo.config.js\n\nmodule.exports = {\n  /* config options here */\n}\n```\n\nHere are the API's for the configuration:\n\n<table>\n  <tr>\n    <td>Key</td>\n    <td>Type</td>\n    <td>Description</td>\n  </tr>\n  <tr>\n    <td><code>favicon</code></td>\n    <td><code>String</code> [<code>{project-dir}/favicon.ico</code>]</td>\n    <td>Path to favicon file</td>\n  </tr>\n  <tr>\n    <td><code>buildDir</code></td>\n    <td><code>String?</code> [<code>.maleo</code>]</td>\n    <td>Directory to put Maleo.js' build assets</td>\n  </tr>\n  <tr>\n    <td><code>cache</code></td>\n    <td><code>Boolean?</code> [<code>true</code>]</td>\n    <td>Enable webpack build caching</td>\n  </tr>\n  <tr>\n    <td><code>isDev</code></td>\n    <td><code>Boolean?</code> [<code>process.env.NODE_ENV === 'development'</code>]</td>\n    <td>Enable development build configuration</td>\n  </tr>\n  <tr>\n    <td><code>sourceMaps</code></td>\n    <td><code>Boolean?</code> [<code>true</code>]</td>\n    <td>Enable webpack to generate source maps</td>\n  </tr>\n  <tr>\n    <td><code>alias</code></td>\n    <td><code>Object?</code></td>\n    <td>A key value pair for aliasing path directory\n      <br>\n      <code>\n      { 'component': './src/component' }\n      </code>\n    </td>\n  </tr>\n  <tr>\n    <td><code>publicPath</code></td>\n    <td><code>String?</code> [<code>/_assets/</code>]</td>\n    <td>To customize webpack's <code>publicPath</code> \n    <br>Comes in handy if using CDN to put built assets</td>\n  </tr>\n  <tr>\n    <td><code>analyzeBundle</code></td>\n    <td><code>Boolean?</code> [<code>false</code>]</td>\n    <td>To enable webpack's bundle analyzer, for analyzing bundle sizes during bundle debugging should Maleo.js' build process got slow</td>\n  </tr>\n  <tr>\n    <td><code>webpack</code></td>\n    <td><code>Function?</code></td>\n    <td>To customize webpack configuration, more details <a href=\"#customize-webpack\">here</a></td>\n  </tr>\n  <tr>\n    <td><code>routes</code></td>\n    <td><code>string?</code> [<code>rootDir/routes.jsx</code>]</td>\n    <td>Path to your routes file</td>\n  </tr>\n  <tr>\n    <td><code>customDocument</code></td>\n    <td><code>string?</code> [<code>rootDir/document.jsx</code>]</td>\n    <td>Path to your custom document file</td>\n  </tr>\n  <tr>\n    <td><code>customWrap</code></td>\n    <td><code>string?</code> [<code>rootDir/wrap.jsx</code>]</td>\n    <td>Path to your custom wrap file</td>\n  </tr>\n  <tr>\n    <td><code>customApp</code></td>\n    <td><code>string?</code> [<code>rootDir/app.jsx</code>]</td>\n    <td>Path to your custom app file</td>\n  </tr>\n</table>\n\n#### Customize Server\n\nCreate a `server.js` file on root directory where your `package.json` lives.\nHere you can customize Maleo's server.\n```js\nimport { Server } from '@airy/maleo/server';\n\nconst PORT = process.env.PORT || 3000;\n\nconst maleoServer = Server.init({\n  port: PORT,\n  runHandler: () => {\n    console.log('Server running on port :', PORT);\n  }\n});\n\nmaleoServer.run();\n```\n\nHere are the API's for the configuration:\n\n<table>\n  <tr>\n    <td>Key</td>\n    <td>Type</td>\n    <td>Description</td>\n  </tr>\n  <tr>\n    <td><code>port</code></td>\n    <td><code>Number?</code> [<code>3000</code>]</td>\n    <td>Port to run Maleo server</td>\n  </tr>\n  <tr>\n    <td><code>assetDir</code></td>\n    <td><code>String?</code> [<code>'<root>/.maleo/client'</code>]</td>\n    <td>Directory for all client related assets</td>\n  </tr>\n  <tr>\n    <td><code>runHandler</code></td>\n    <td><code>Function?</code></td>\n    <td>Function called when maleo server starter</td>\n  </tr>\n  <tr>\n    <td><code>csp</code></td>\n    <td><code>cspConfig?</code> [<code>{\n      directives: {\n        defaultSrc: [`'self'`],\n        styleSrc: [`'self'`],\n      },\n    }</code>]</td>\n    <td>Config for <a href=\"https://developers.google.com/web/fundamentals/security/csp/\">Content Security Policy</a>, using <a href=\"https://helmetjs.github.io/docs/csp/\">Helmet-CSP</a></td>\n  </tr>\n</table>\n\n#### Customize Webpack\n\nYou are able to extend Maleo.js' default webpack configuration by defining a function on `maleo.config.js`\n\n```js\n// maleo.config.js\n\nmodule.exports = {\n  webpack(config, context, next) {\n    // Perform customizations to webpack config\n    // Important: This call is required in order to let Maleo pass the config to webpack\n    return next(); \n  },\n};\n```\n\nWebpack function will receive three arguments:\n\n<table>\n  <tr>\n    <td>Argument</td>\n    <td>Details</td>\n  <tr>\n  <tr>\n    <td><code>config</code></td>\n    <td>This contains webpack configuration object that you can manipulate</td>\n  <tr>\n  <tr>\n    <td><code>context</code></td>\n    <td>This contains some keys that are useful for the build context\n      <br>\n      <table>\n        <tr>\n          <td><code>isDev</code></td>\n          <td><code>Boolean</code></td>\n          <td>Check if current build is for development</td>\n        </tr>\n        <tr>\n          <td><code>publicPath</code></td>\n          <td><code>String</code></td>\n          <td>Public Path of user defined or default's value</td>\n        </tr>\n        <tr>\n          <td><code>analyzeBundle</code></td>\n          <td><code>Boolean</code></td>\n          <td>Check if analyze bundle is enabled</td>\n        </tr>\n        <tr>\n          <td><code>buildDirectory</code></td>\n          <td><code>String</code></td>\n          <td>Build Directory of user defined or default's value</td>\n        </tr>\n        <tr>\n          <td><code>name</code></td>\n          <td><code>String</code></td>\n          <td>Build name <code>'server' || 'client'</code></td>\n        </tr>\n      </table>\n    </td>\n  <tr>\n  <tr>\n    <td><code>next</code></td>\n    <td>A callback required to be called to pass the custom configuration</td>\n  <tr>\n</table>\n\nExample of adding `ts-loader` through `maleo.config.js`:\n\n```js\n// maleo.config.js\n\n// Partly taken and modified from @airy/maleo-ts-plugin source\n// for simplicity purposes\n\nmodule.exports = {\n  webpack(config, context, next) {\n    const { isDev } = context\n\n    config.module.rules.push({\n      test: /\\.tsx?/,\n      exclude: /node_modules/,\n      use: [\n        require.resolve('@airy/maleo/lib/build/loaders/maleo-babel-loader'),\n        {\n          loader: 'ts-loader',\n          options: {\n            transpileOnly: true,\n          },\n        },\n      ],\n\n      if (isDev) {\n        config.plugins.push(new ForkTSCheckerWebpackPlugin());\n      }\n\n      return next();\n    })\n  },\n};\n```\n\n#### Customize Babel Config\n\nMaleo.js also let you have your own babel config. Just simply add `.babelrc` file at the root directory of your app.\n\nYou can include Maleo.js' babel preset in order to have latest JavaScript preset.\n\nHere's an example of `.babelrc` file:\n```json\n{\n  \"presets\": [\"@airy/maleo/babel\"],\n  \"plugins\": []\n}\n```\n\nThe `@airy/maleo/babel` preset includes everything you need to get your development started. The preset includes:\n- `@babel/preset-env`\n- `@babel/preset-react`\n- `@babel/plugin-proposal-class-properties`\n- `@babel/plugin-proposal-decorators`\n- `@babel/plugin-proposal-object-rest-spread`\n- `@babel/plugin-transform-runtime`\n- `react-loadable/babel`\n\n## Core Utilities Functions\n\n#### Route Matching\n\nTo use you can import the matching routes function like this:\n```js\nimport { getMatchedRoutes } from '@airy/maleo/utils';\n```\n\nThe function will return all matched routes and the route object from `routes.json`\n\n## CDN Support\n\nIf you are using a CDN, you can set up the `publicPath` setting and configure your CDN's origin to resolve to the domain that Maleo.js is hosted on.\n\n```js\n// maleo.config.js\n\nconst isProd = process.env.NODE_ENV === 'production';\n\nmodule.exports = {\n  assetPrefix: isProd && 'https://cdn.example.com';\n}\n```\n\n## FAQ\n\n== TO BE DETERMINED == \n\n## Plugins\n- [css-plugin](https://github.com/airyrooms/maleo-plugins/tree/master/packages/css-plugin)\n- [redux-plugin](https://github.com/airyrooms/maleo-plugins/tree/master/packages/redux-plugin)\n- [typescript-plugin](https://github.com/airyrooms/maleo-plugins/tree/master/packages/typescript-plugin)\n- [more](https://github.com/airyrooms/maleo-plugins)\n\n\n## Contributing\n[Please follow these steps to contribute to Maleo.js](https://github.com/airyrooms/maleo.js/blob/canary/CONTRIBUTING.md)\n\n[Please follow these steps to contribute to Maleo.js' plugins](https://github.com/airyrooms/maleo-plugins#contributing-guidelines)\n\n\n## Contributors\n\nThis project exists thanks to all the people who contribute. [Contribute](CONTRIBUTING.md).\n\nMany thanks to our **[contributors](https://github.com/airyrooms/maleo.js/graphs/contributors)**!\n\n\n## License\n\nMIT","readmeFilename":"README.md","bugs":{"url":"https://github.com/airyrooms/maleo.js/issues"},"homepage":"https://github.com/airyrooms/maleo.js#readme","_id":"@airy/maleo@0.3.1-canary.17","_nodeVersion":"10.16.0","_npmVersion":"lerna/3.13.1/node@v10.16.0+x64 (linux)","dist":{"integrity":"sha512-yNNGmN+ZhimPQp/k7IQ4/OIqwKOxC1GN9wU8sezhWIsBLT5jRTiTNUpVnNxEd82TdFDkT9/vQgyaNaDTb8f8WA==","shasum":"84f93ba4099dfc6befcd4b6fe3afbab4b68b1500","tarball":"https://registry.npmjs.org/@airy/maleo/-/maleo-0.3.1-canary.17.tgz","fileCount":96,"unpackedSize":335105,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJdJrDQCRA9TVsSAnZWagAAqVcP/RON6MGxLOl8a66wdmn+\nZUusdn41OaAvtBZBnOKkfDPWCXH92d8FlhlHz9kprCJufJLXfvvCbcgN0y4w\n6lTiin5qAxCUBRWLiFLjAgLMbmmpBLvYq3FD+Xosp3g4ol5Amh2IEf9hhMg3\nXAsYUJyE6qEDUhCjFJfK7n0BxGhPFNuiQkJYigmokfXL+bmvo/j8U7h1CbVm\n7AokrDf4wvSh/WjGR8kWyqoID/9q4v4zPnjMz/OVYtA6zimBHMnF+m8QNfx1\na9XzvMdEU3MXDU3jQ8t5Kjk7vA0x5weLoD6wwfHUvmFreuIlOIF85hAIh695\nb63W4TQfcyAwUD3bN3f4LmmVq6k3hsS+vs7GSESsSYvbl8pRpJEsCxg7utDr\nHrZVl3qSpUKHJjz9/Hw8Hu3wkmdrrkj6DIhxUr6W5XVQLjRihj7ACLNl9kmP\nZ7yk39dSVNOmaJ4CL2+NjQxXvpUWG79iA2XlfpIiSi5NiX119ARissEYG1nL\nz4ar0QCXJ+KMb3ov/uv//Z/3P/eG/tW1DNWPsWvcEQDO2uD2jdITaa1ZztBK\nluQ7NAy2Zn6rdDk45+z9juNKfsFsbtHkWrTEc4BTFHI1VEVkl4m3Ew3Kl407\n+WQJm2HKDKDOM9gQJ2Nm1aqN1coZ/1SsXiOaUoJYzQxkKJJZI4gvZLZmboFI\nAgtE\r\n=jERF\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIC8vYordLuzacslgjcxmlMXzZ8EmppNaZfFgo3Va3ti/AiBBLd4Ic+2BfwliCsNek/jr3FL+5pG8MpHS2Exym8ADtQ=="}]},"maintainers":[{"name":"airyrooms-engineering","email":"engineering@airyrooms.com"}],"_npmUser":{"name":"airyrooms-engineering","email":"engineering@airyrooms.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/maleo_0.3.1-canary.17_1562816719887_0.6374332114031995"},"_hasShrinkwrap":false},"0.3.1-canary.18":{"name":"@airy/maleo","version":"0.3.1-canary.18","description":"Un-opinionated Universal Rendering Javascript Framework","repository":{"type":"git","url":"git+https://github.com/airyrooms/maleo.js.git","bugs":"https://github.com/airyrooms/maleo.js/issues"},"author":{"name":"Airy"},"license":"MIT","private":false,"sideEffects":false,"scripts":{"build":"rm -rf dist && gulp","watch":"gulp watch","prepublish":"yarn build","start":"node server.js"},"bin":{"maleo":"lib/bin/maleo.js"},"dependencies":{"@babel/core":"7.1.2","@babel/plugin-proposal-class-properties":"7.1.0","@babel/plugin-proposal-decorators":"7.1.2","@babel/plugin-proposal-object-rest-spread":"7.0.0","@babel/plugin-syntax-dynamic-import":"7.0.0","@babel/plugin-transform-runtime":"7.1.0","@babel/preset-env":"7.1.0","@babel/preset-react":"7.0.0","@babel/runtime":"7.1.2","@babel/runtime-corejs2":"7.2.0","babel-loader":"8.0.4","case-sensitive-paths-webpack-plugin":"2.1.2","compression":"1.7.4","es6-promise":"4.2.5","eventsource":"1.0.7","express":"4.16.4","figlet":"1.2.1","friendly-errors-webpack-plugin":"1.7.0","hard-source-webpack-plugin":"0.13.1","helmet":"3.14.0","hoist-non-react-statics":"^3.3.0","isomorphic-fetch":"2.2.1","loader-utils":"1.1.0","node-notifier":"5.2.1","nodemon":"1.18.4","react-dom":"16.8.4","react-helmet":"5.2.0","react-loadable":"5.5.0","react-router-config":"5.0.0","react-router-dom":"5.0.0","rimraf":"2.6.3","source-map-support":"0.5.9","terser-webpack-plugin":"1.1.0","webpack":"4.29.0","webpack-bundle-analyzer":"3.0.3","webpack-cli":"3.1.2","webpack-dev-middleware":"3.4.0","webpack-hot-middleware":"2.24.3","webpackbar":"2.6.3"},"peerDependencies":{"react":"^16.8.4"},"devDependencies":{"@babel/preset-typescript":"^7.1.0","@types/webpack-bundle-analyzer":"^2.13.0","babel-plugin-module-resolver":"^3.1.1","gulp":"github:gulpjs/gulp#4.0","gulp-babel":"^8.0.0","gulp-clean":"^0.4.0","gulp-sourcemaps":"^2.6.4","gulp-uglify":"^3.0.1","gulp-watch":"^5.0.1","react":"^16.8.4"},"gitHead":"24aa007acf8a656f1a2863153f62535f8cf48e8b","readme":"[![Build Status](https://travis-ci.org/airyrooms/maleo.js.svg?branch=master)](https://travis-ci.org/airyrooms/maleo.js)\n[![Coverage Status](https://coveralls.io/repos/github/airyrooms/maleo.js/badge.svg?branch=master)](https://coveralls.io/github/airyrooms/maleo.js?branch=master)\n[![npm version](https://badge.fury.io/js/%40airy%2Fmaleo.svg)](https://badge.fury.io/js/%40airy%2Fmaleo)\n[![Issues](http://img.shields.io/github/issues/airyrooms/maleo.js.svg)]( https://github.com/airyrooms/maleo.js/issues)\n[![MIT license](https://img.shields.io/badge/License-MIT-blue.svg)](https://github.com/airyrooms/maleo.js/blob/canary/LICENSE)\n[![Discord Chat](https://img.shields.io/discord/550498214789513218.svg)](https://discord.gg/9eArCQn)\n[![Conventional Commits](https://img.shields.io/badge/Conventional%20Commits-1.0.0-green.svg)](https://conventionalcommits.org)\n\n\n# Welcome to Maleo.js\n\nMaleo.js is an un-opinionated framework to enable Universal Rendering in JavaScript using React with no hassle.\n\nWe are here to solve the time consuming setups Universal Rendering Required.\n\n---\n\nReadme below is the documentation for the `canary` (prerelease) branch. To view the documentation for the latest stable Maleo.js version change branch to `master`\n\n---\n\n# Table of Contents\n- [Features](#features)\n- [Setup](#setup)\n- [Component Lifecycle](#component-lifecycle)\n- [Routing](#routing)\n- [Dynamic Import Component](#dynamic-import-component)\n  - [Tips](#tips)\n  - [Preloading](#preloading)\n- [Customizable Component](#customizable-component)\n  - [Custom Document](#custom-document)\n  - [Custom Wrap](#custom-wrap)\n- [Custom Configuration](#custom-configuration)\n  - [Customize Server](#customize-server)\n  - [Customize Webpack](#customize-webpack)\n  - [Customize Babel Config](#customize-babel-config)\n- [Core Utilities Functions](#core-utilities-functions)\n- [CDN Support](#cdn-support)\n- [Plugins](#plugins)\n- [FAQ](#faq)\n- [Contributing](#contributing)\n\n---\n\n## Features\n- Universal Rendering\n- Plugin based framework\n- Customizable\n\n## Setup\n\nInstall Maleo.js \n\n>*for now change `@airy/maleo` to `@airy/maleo@canary` until we publish the stable version of this package*\n\n\n**NPM**\n```bash\n$ npm install --save @airy/maleo react\n```\n**Yarn**\n```bash\n$ yarn add @airy/maleo react\n```\n\nAdd this script to your `package.json`\n```json\n{\n  \"scripts\": {\n    \"dev\": \"maleo dev\",\n    \"build\": \"export NODE_ENV=production && maleo build\",\n    \"start\": \"export NODE_ENV=production && maleo run\"\n  }\n}\n```\n\nCreate a page Root component\n```jsx\n// ./src/Root.jsx\nimport React from 'react';\n\n// Export default is required for registering page\nexport default class RootComponent extends React.Component {\n  render() {\n    return (\n      <h1>Hello World!</h1>\n    )\n  }\n}\n```\n\nAnd lastly, create a routing file on your project root directory called `routes.json` and register your page component\n```json\n[\n  {\n    \"path\": \"/\",\n    \"page\": \"./src/Root\"\n  }\n]\n```\n\nAfter that you can now run `$ npm run dev` and go to `http://localhost:3000`.\n\nYou should now see your app running on your browser.\n\nBy now you should see \n- Automatic transpilation and bundling (with webpack and babel)\n- ~~Hot code reloading~~ [#17](https://github.com/airyrooms/maleo.js/issues/17)\n- Server rendering\n\nTo see how simple this is, check out the sample app!\n\n## Component Lifecycle \n\nMaleo.js added a new component lifecycle hook called `getInitialProps`, this function is called during Server Side Rendering (SSR) and during route changes on Client Side Rendering (CSR).\n\nThis is useful especially for SEO purposes. \n\nExample for stateful component:\n```jsx\nimport React from 'react';\n\nexport default class extends React.Component {\n  static getInitialProps = async (ctx) => {\n    const { req } = ctx;\n\n    // check if getInitialProps is called on server or on client\n    const userAgent = req ? req.headers['user-agent'] : navigator.userAgent;\n\n    // the return value will be passed as props for this component\n    return { userAgent };\n  }\n\n  render() {\n    return (\n      <div>\n        Hello World {this.props.userAgent}\n      </div>\n    );\n  }\n}\n```\n\nExample for stateless component:\n```jsx\nconst Component = (props) => (\n  <div>\n    Hello World {props.userAgent}\n  </div>\n);\n\nComponent.getInitialprops = async (ctx) => {\n  const { req } = ctx;\n\n  const userAgent = req ? req.headers['user-agent'] : navigator.userAgent;\n\n  return { userAgent };\n};\n\nexport default Component;\n```\n\n`getInitialProps` receives a context object with the following properties:\n- `req` - HTTP request object (server only)\n- `res` - HTTP response object (server only)\n- `...wrapProps` - Spreaded properties from custom Wrap\n- `...appProps` - Spreaded properties from custom App\n\n## Routing\n\nRouting is declared in a centralized route config.\nRegister all the route config in `routes.json` file.\n\nIf you put the `routes.json` files on root directory, Maleo will automatically register your route. Otherwise put path to your routes on [Maleo config](#custom-configuration).\n\nRoutes file has to export default the route configuration.\nThe route object **expected to have distinct key** to indicate the route.\n\n<table>\n  <tr>\n    <td>Key</td>\n    <td>Type</td>\n    <td>Description</td>\n  </tr>\n  <tr>\n    <td><code>path</code></td>\n    <td><code>String!</code></td>\n    <td>Routes path</td>\n  </tr>\n  <tr>\n    <td><code>page</code></td>\n    <td><code>String!</code></td>\n    <td>Path to React Component for this route path</td>\n  </tr>\n  <tr>\n    <td><code>exact</code></td>\n    <td><code>Boolean?</code> [<code>false</code>]</td>\n    <td>To make url has to match exactly the path in order to render the component. Give <code>false</code> value if the route is a wrapper route component</td>\n  </tr>\n  <tr>\n    <td><code>routes</code></td>\n    <td><code>RouteObject?</code></td>\n    <td>Nested route</td>\n  </tr>\n</table>\n\n\nFor example:\n```json\n[\n  {\n    \"page\": \"./src/MainApp\",\n    \"routes\": [\n      {\n        \"path\": \"/\",\n        \"page\": \"./src/Search\",\n        \"exact\": true\n      },\n      {\n        \"path\": \"/search\",\n        \"page\": \"./src/Search\",\n        \"routes\": [\n          {\n            \"path\": \"/search/hello\",\n            \"page\": \"./src/Detail\",\n            \"exact\": true\n          }\n        ]\n      },\n      {\n        \"path\": \"/detail\",\n        \"page\": \"./src/Detail\",\n        \"exact\": true\n      }\n    ]\n  }\n]\n```\n\n## Dynamic Import Component\nMaleo.js supports TC39 [dynamic import proposal](https://github.com/tc39/proposal-dynamic-import) for JavaScript.\n\nYou can think dynamic import as another way to split your code into manageable chunks. You can use our `Dynamic` function which utilizes [react loadable](https://github.com/jamiebuilds/react-loadable)\n\nFor Example\n```js\n// DynamicComponent.js\nimport Dynamic from '@airy/maleo/dynamic';\n\nexport default Dynamic({\n  loader: () => import( /* webpackChunkName:\"DynamicComponent\" */ './component/DynamicLoad'),\n})\n```\n\n### **Preloading**\n\nFor optimization purposes, you can also preload a component even before the component got rendered.\n\nFor example, if you want to load component when a button get pressed, you can start preloading the component when the user hovers the mouse over the button.\n\nThe component created by `Dynamic` exposes a [static `preload` method](https://github.com/jamiebuilds/react-loadable#loadablecomponentpreload).\n\n```jsx\nimport React from 'react';\nimport Dynamic from '@airy/maleo/dynamic';\n\nconst DynamicBar = Dynamic({\n  loader: () => import('./Bar'),\n  loading: LoadingCompoinent\n});\n\nclass MyComponent extends React.Component {\n  state = { showBar: false };\n\n  onClick = () => {\n    this.setState({ showBar: true });\n  };\n\n  onMouseOver = () => DynamicBar.preload();\n\n  render() {\n    return (\n      <div>\n        <button\n          onClick={this.onClick}\n          onMouseOver={this.onMouseOver}>\n          Show Bar\n        </button>\n        { this.state.showBar && <DynamicBar /> }\n      </div>\n    )\n  }\n}\n```\n\n## Customizable Component\n\n### Custom Document\n\nHighly inspired by what [Next.js](https://github.com/zeit/next.js) has done on their awesome template customization.\n\nMaleo.js also enable customization on `Document` as document's markup. So you don't need to include tags like `<html>`, `<head>`, `<body>`, etc. \n\nTo override the default behavior, you'll need to create a component that extends the `Document` React class provided by Maleo.\n\n```jsx\n// document.jsx\nimport React from 'react';\nimport { Document, Header, Main, Scripts } from '@airy/maleo/document';\n\nexport default class extends Document {\n  render() {\n    return (\n      <html>\n        <Header>\n          <title>Maleo JS</title>\n          <meta charset=\"utf-8\" />\n          <meta name=\"description\" content=\"Maleo.js is awesome!\" />\n\n          <style>\n            {` body { background-color: #fff } `}\n          </style>\n        </Header>\n\n        <body>\n          <Main />\n\n          <Scripts />\n\n          <ReduxScript />\n        </body>\n      </html>\n    );\n  }\n}\n```\n\n### Custom Wrap\n\nMaleo.js uses the `Wrap` component to initialize pages. `Wrap` contains React Router's Component. You can add HoC here to wrap the application and control the page initialization. Which allows you to do amazing things like:\n- Persisting layour between page changes\n- Keeping state when navigating pages\n- Custom error handling using `componentDidCatch`\n- Inject additional data into pages (like Redux provider, etc)\n\nTo override the default behavior, you'll need to create a component that extends the `Wrap` React class provided by Maleo.\n\n```jsx\n// wrap.jsx\nimport React from 'react';\nimport { Wrap } from '@airy/maleo/wrap';\n\n// Redux plugin for Maleo.js\n// Hoc that creates store and add Redux Provider\nimport { withRedux } from '@airy/maleo-redux-plugin';\n\n// Custom Wrapper that will be rendered for the whole Application\nimport Content from './component/Content';\nimport NavigationBar from './component/NavigationBar';\n\nimport { createStore } from './store';\n\n@withRedux(createStore)\nexport default class extends Wrap {\n  static getInitialProps = (ctx) => {\n    const { store } = ctx\n    // you receive store from context\n    // you can access or do something with the store here\n    console.log('Initialized Redux Store', store);\n    return {}\n  }\n\n  render() {\n    const { Container, containerProps, App, appProps } = this.props\n\n    return (\n      <Container {...containerProps}>\n        <NavigationBar />\n        <Content>\n          <App {...appProps}/>\n        </Content>\n      </Container>\n    )\n  }\n}\n\n```\nIf you put `document.jsx` and `wrap.jsx` on root directory (the same level with `package.json`), then Maleo will automatically register your custom Document and Wrap. Otherwise, you can add the path to your custom Document and Wrap on [Maleo config](#custom-configuration)\n\n---\n***We are also working on adding default and customizable `Error` component page***\n\n---\n\n## Custom Configuration\n\nFor more advanced configuration of Maleo.js, like `webpack` config, registering `plugins`, path to your routes, custom Document and Wrap, and adding `path alias`, you can create a `maleo.config.js` in the root of your project directory. (same directory with `package.json`)\n\n```js\n// maleo.config.js\n\nmodule.exports = {\n  /* config options here */\n}\n```\n\nHere are the API's for the configuration:\n\n<table>\n  <tr>\n    <td>Key</td>\n    <td>Type</td>\n    <td>Description</td>\n  </tr>\n  <tr>\n    <td><code>favicon</code></td>\n    <td><code>String</code> [<code>{project-dir}/favicon.ico</code>]</td>\n    <td>Path to favicon file</td>\n  </tr>\n  <tr>\n    <td><code>buildDir</code></td>\n    <td><code>String?</code> [<code>.maleo</code>]</td>\n    <td>Directory to put Maleo.js' build assets</td>\n  </tr>\n  <tr>\n    <td><code>cache</code></td>\n    <td><code>Boolean?</code> [<code>true</code>]</td>\n    <td>Enable webpack build caching</td>\n  </tr>\n  <tr>\n    <td><code>isDev</code></td>\n    <td><code>Boolean?</code> [<code>process.env.NODE_ENV === 'development'</code>]</td>\n    <td>Enable development build configuration</td>\n  </tr>\n  <tr>\n    <td><code>sourceMaps</code></td>\n    <td><code>Boolean?</code> [<code>true</code>]</td>\n    <td>Enable webpack to generate source maps</td>\n  </tr>\n  <tr>\n    <td><code>alias</code></td>\n    <td><code>Object?</code></td>\n    <td>A key value pair for aliasing path directory\n      <br>\n      <code>\n      { 'component': './src/component' }\n      </code>\n    </td>\n  </tr>\n  <tr>\n    <td><code>publicPath</code></td>\n    <td><code>String?</code> [<code>/_assets/</code>]</td>\n    <td>To customize webpack's <code>publicPath</code> \n    <br>Comes in handy if using CDN to put built assets</td>\n  </tr>\n  <tr>\n    <td><code>analyzeBundle</code></td>\n    <td><code>Boolean?</code> [<code>false</code>]</td>\n    <td>To enable webpack's bundle analyzer, for analyzing bundle sizes during bundle debugging should Maleo.js' build process got slow</td>\n  </tr>\n  <tr>\n    <td><code>webpack</code></td>\n    <td><code>Function?</code></td>\n    <td>To customize webpack configuration, more details <a href=\"#customize-webpack\">here</a></td>\n  </tr>\n  <tr>\n    <td><code>routes</code></td>\n    <td><code>string?</code> [<code>rootDir/routes.jsx</code>]</td>\n    <td>Path to your routes file</td>\n  </tr>\n  <tr>\n    <td><code>customDocument</code></td>\n    <td><code>string?</code> [<code>rootDir/document.jsx</code>]</td>\n    <td>Path to your custom document file</td>\n  </tr>\n  <tr>\n    <td><code>customWrap</code></td>\n    <td><code>string?</code> [<code>rootDir/wrap.jsx</code>]</td>\n    <td>Path to your custom wrap file</td>\n  </tr>\n  <tr>\n    <td><code>customApp</code></td>\n    <td><code>string?</code> [<code>rootDir/app.jsx</code>]</td>\n    <td>Path to your custom app file</td>\n  </tr>\n</table>\n\n#### Customize Server\n\nCreate a `server.js` file on root directory where your `package.json` lives.\nHere you can customize Maleo's server.\n```js\nimport { Server } from '@airy/maleo/server';\n\nconst PORT = process.env.PORT || 3000;\n\nconst maleoServer = Server.init({\n  port: PORT,\n  runHandler: () => {\n    console.log('Server running on port :', PORT);\n  }\n});\n\nmaleoServer.run();\n```\n\nHere are the API's for the configuration:\n\n<table>\n  <tr>\n    <td>Key</td>\n    <td>Type</td>\n    <td>Description</td>\n  </tr>\n  <tr>\n    <td><code>port</code></td>\n    <td><code>Number?</code> [<code>3000</code>]</td>\n    <td>Port to run Maleo server</td>\n  </tr>\n  <tr>\n    <td><code>assetDir</code></td>\n    <td><code>String?</code> [<code>'<root>/.maleo/client'</code>]</td>\n    <td>Directory for all client related assets</td>\n  </tr>\n  <tr>\n    <td><code>runHandler</code></td>\n    <td><code>Function?</code></td>\n    <td>Function called when maleo server starter</td>\n  </tr>\n  <tr>\n    <td><code>csp</code></td>\n    <td><code>cspConfig?</code> [<code>{\n      directives: {\n        defaultSrc: [`'self'`],\n        styleSrc: [`'self'`],\n      },\n    }</code>]</td>\n    <td>Config for <a href=\"https://developers.google.com/web/fundamentals/security/csp/\">Content Security Policy</a>, using <a href=\"https://helmetjs.github.io/docs/csp/\">Helmet-CSP</a></td>\n  </tr>\n</table>\n\n#### Customize Webpack\n\nYou are able to extend Maleo.js' default webpack configuration by defining a function on `maleo.config.js`\n\n```js\n// maleo.config.js\n\nmodule.exports = {\n  webpack(config, context, next) {\n    // Perform customizations to webpack config\n    // Important: This call is required in order to let Maleo pass the config to webpack\n    return next(); \n  },\n};\n```\n\nWebpack function will receive three arguments:\n\n<table>\n  <tr>\n    <td>Argument</td>\n    <td>Details</td>\n  <tr>\n  <tr>\n    <td><code>config</code></td>\n    <td>This contains webpack configuration object that you can manipulate</td>\n  <tr>\n  <tr>\n    <td><code>context</code></td>\n    <td>This contains some keys that are useful for the build context\n      <br>\n      <table>\n        <tr>\n          <td><code>isDev</code></td>\n          <td><code>Boolean</code></td>\n          <td>Check if current build is for development</td>\n        </tr>\n        <tr>\n          <td><code>publicPath</code></td>\n          <td><code>String</code></td>\n          <td>Public Path of user defined or default's value</td>\n        </tr>\n        <tr>\n          <td><code>analyzeBundle</code></td>\n          <td><code>Boolean</code></td>\n          <td>Check if analyze bundle is enabled</td>\n        </tr>\n        <tr>\n          <td><code>buildDirectory</code></td>\n          <td><code>String</code></td>\n          <td>Build Directory of user defined or default's value</td>\n        </tr>\n        <tr>\n          <td><code>name</code></td>\n          <td><code>String</code></td>\n          <td>Build name <code>'server' || 'client'</code></td>\n        </tr>\n      </table>\n    </td>\n  <tr>\n  <tr>\n    <td><code>next</code></td>\n    <td>A callback required to be called to pass the custom configuration</td>\n  <tr>\n</table>\n\nExample of adding `ts-loader` through `maleo.config.js`:\n\n```js\n// maleo.config.js\n\n// Partly taken and modified from @airy/maleo-ts-plugin source\n// for simplicity purposes\n\nmodule.exports = {\n  webpack(config, context, next) {\n    const { isDev } = context\n\n    config.module.rules.push({\n      test: /\\.tsx?/,\n      exclude: /node_modules/,\n      use: [\n        require.resolve('@airy/maleo/lib/build/loaders/maleo-babel-loader'),\n        {\n          loader: 'ts-loader',\n          options: {\n            transpileOnly: true,\n          },\n        },\n      ],\n\n      if (isDev) {\n        config.plugins.push(new ForkTSCheckerWebpackPlugin());\n      }\n\n      return next();\n    })\n  },\n};\n```\n\n#### Customize Babel Config\n\nMaleo.js also let you have your own babel config. Just simply add `.babelrc` file at the root directory of your app.\n\nYou can include Maleo.js' babel preset in order to have latest JavaScript preset.\n\nHere's an example of `.babelrc` file:\n```json\n{\n  \"presets\": [\"@airy/maleo/babel\"],\n  \"plugins\": []\n}\n```\n\nThe `@airy/maleo/babel` preset includes everything you need to get your development started. The preset includes:\n- `@babel/preset-env`\n- `@babel/preset-react`\n- `@babel/plugin-proposal-class-properties`\n- `@babel/plugin-proposal-decorators`\n- `@babel/plugin-proposal-object-rest-spread`\n- `@babel/plugin-transform-runtime`\n- `react-loadable/babel`\n\n## Core Utilities Functions\n\n#### Route Matching\n\nTo use you can import the matching routes function like this:\n```js\nimport { getMatchedRoutes } from '@airy/maleo/utils';\n```\n\nThe function will return all matched routes and the route object from `routes.json`\n\n## CDN Support\n\nIf you are using a CDN, you can set up the `publicPath` setting and configure your CDN's origin to resolve to the domain that Maleo.js is hosted on.\n\n```js\n// maleo.config.js\n\nconst isProd = process.env.NODE_ENV === 'production';\n\nmodule.exports = {\n  assetPrefix: isProd && 'https://cdn.example.com';\n}\n```\n\n## FAQ\n\n== TO BE DETERMINED == \n\n## Plugins\n- [css-plugin](https://github.com/airyrooms/maleo-plugins/tree/master/packages/css-plugin)\n- [redux-plugin](https://github.com/airyrooms/maleo-plugins/tree/master/packages/redux-plugin)\n- [typescript-plugin](https://github.com/airyrooms/maleo-plugins/tree/master/packages/typescript-plugin)\n- [more](https://github.com/airyrooms/maleo-plugins)\n\n\n## Contributing\n[Please follow these steps to contribute to Maleo.js](https://github.com/airyrooms/maleo.js/blob/canary/CONTRIBUTING.md)\n\n[Please follow these steps to contribute to Maleo.js' plugins](https://github.com/airyrooms/maleo-plugins#contributing-guidelines)\n\n\n## Contributors\n\nThis project exists thanks to all the people who contribute. [Contribute](CONTRIBUTING.md).\n\nMany thanks to our **[contributors](https://github.com/airyrooms/maleo.js/graphs/contributors)**!\n\n\n## License\n\nMIT","readmeFilename":"README.md","bugs":{"url":"https://github.com/airyrooms/maleo.js/issues"},"homepage":"https://github.com/airyrooms/maleo.js#readme","_id":"@airy/maleo@0.3.1-canary.18","_nodeVersion":"11.15.0","_npmVersion":"lerna/3.13.1/node@v11.15.0+x64 (linux)","dist":{"integrity":"sha512-V1ugZfx44NbBcELKFC00CFgVe7U2vbnx0Gv31f1ZMvMDb/4S+sVNCrR1WuXg3KSVibvlDKF5Fdib8lRvlKzynw==","shasum":"841b06b3a1d5770d570ad80ccd7072be9a8fe551","tarball":"https://registry.npmjs.org/@airy/maleo/-/maleo-0.3.1-canary.18.tgz","fileCount":96,"unpackedSize":335392,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJdK/UeCRA9TVsSAnZWagAAuEsP/1mt1C5p84RcM3MZwwWW\nduN+UAeznRAky03lMmCMd2vt5htxGbFvgocamE1XXzzfBb1v0fACSrd/9r19\nh/9CAdEpOluXbx5m4RkXSm4dTnbFlHClyZGBjQgc0bemWzwQxhQ8UuNqIoSA\nQDQj5JKYcsZFGYnJdP5zi++f9KVVZEUsFFNpo3Q1wzEaWdD2xW0kduTfS0mq\nqnbVwmiwqXbGeveN1gbnsbAV7CUl9gBaV+4iaPtDgVNBVLK1wajNDk0t0rnz\nJEn+ggOyDJELpNAf+PWFsW/2Ycllh1ggtAN7d4dazbO7fS3TGFnz0Orc2Ogu\nb2RE/sRqIydwV+90T8oMvdPUKAuktHQ68SysmgtwUBwMgOuf96EQzIOaUH4p\nb5Lw7y3vqRwJYllZHBTRiloF88uZ2PDxKMUQGCgi4Op3h8iuT0MuXUBRPOC1\nGb0mUgtT7Xj5KFXSs8OVv35yLOIZD8Pw3Xm2nEgbmVKdhZm7xwkbJ8hlGFS4\n+n32WnaOi4RHalYgwGKLsb7MATz6PabUP0st76ttV5N7dn5Lm3E8F2jmlmcZ\n2sOI2bCFet8JLQByC51subjKkueHhPheNNT0rsCtM1D+GtrFOA+cp7UteOH5\n8gJjfQZt+qWF1JIzXlweCj0WmiGfyq9kUAtVU/y53z+3OF/fH5sbXm07hhKI\ny3GN\r\n=h6K7\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQC1G+g/63UG8EBcaPK3aQ3Ac2TQNrz3LWD7G2XePhATaAIgd4cF+f1Ry4i8MnR6gDkGO6rskozl3IY2xpG9sFM8x/4="}]},"maintainers":[{"name":"airyrooms-engineering","email":"engineering@airyrooms.com"}],"_npmUser":{"name":"airyrooms-engineering","email":"engineering@airyrooms.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/maleo_0.3.1-canary.18_1563161885518_0.5374723434562234"},"_hasShrinkwrap":false},"0.3.1-canary.19":{"name":"@airy/maleo","version":"0.3.1-canary.19","description":"Un-opinionated Universal Rendering Javascript Framework","repository":{"type":"git","url":"git+https://github.com/airyrooms/maleo.js.git","bugs":"https://github.com/airyrooms/maleo.js/issues"},"author":{"name":"Airy"},"license":"MIT","private":false,"sideEffects":false,"scripts":{"build":"rm -rf dist && gulp","watch":"gulp watch","prepublish":"yarn build","start":"node server.js"},"bin":{"maleo":"lib/bin/maleo.js"},"dependencies":{"@babel/core":"7.1.2","@babel/plugin-proposal-class-properties":"7.1.0","@babel/plugin-proposal-decorators":"7.1.2","@babel/plugin-proposal-object-rest-spread":"7.0.0","@babel/plugin-syntax-dynamic-import":"7.0.0","@babel/plugin-transform-runtime":"7.1.0","@babel/preset-env":"7.1.0","@babel/preset-react":"7.0.0","@babel/runtime":"7.1.2","@babel/runtime-corejs2":"7.2.0","babel-loader":"8.0.4","case-sensitive-paths-webpack-plugin":"2.1.2","compression":"1.7.4","es6-promise":"4.2.5","eventsource":"1.0.7","express":"4.16.4","figlet":"1.2.1","flatted":"^2.0.1","friendly-errors-webpack-plugin":"1.7.0","hard-source-webpack-plugin":"0.13.1","helmet":"3.14.0","hoist-non-react-statics":"^3.3.0","isomorphic-fetch":"2.2.1","loader-utils":"1.1.0","node-notifier":"5.2.1","nodemon":"1.18.4","react-dom":"16.8.4","react-helmet":"5.2.0","react-loadable":"5.5.0","react-router-config":"5.0.0","react-router-dom":"5.0.0","rimraf":"2.6.3","source-map-support":"0.5.9","terser-webpack-plugin":"1.1.0","webpack":"4.29.0","webpack-bundle-analyzer":"3.0.3","webpack-cli":"3.1.2","webpack-dev-middleware":"3.4.0","webpack-hot-middleware":"2.24.3","webpackbar":"2.6.3"},"peerDependencies":{"react":"^16.8.4"},"devDependencies":{"@babel/preset-typescript":"^7.1.0","@types/webpack-bundle-analyzer":"^2.13.0","babel-plugin-module-resolver":"^3.1.1","gulp":"github:gulpjs/gulp#4.0","gulp-babel":"^8.0.0","gulp-clean":"^0.4.0","gulp-sourcemaps":"^2.6.4","gulp-uglify":"^3.0.1","gulp-watch":"^5.0.1","react":"^16.8.4"},"gitHead":"3c58bb82061d80cb114a8d63e3314577b24dd08b","readme":"[![Build Status](https://travis-ci.org/airyrooms/maleo.js.svg?branch=master)](https://travis-ci.org/airyrooms/maleo.js)\n[![Coverage Status](https://coveralls.io/repos/github/airyrooms/maleo.js/badge.svg?branch=master)](https://coveralls.io/github/airyrooms/maleo.js?branch=master)\n[![npm version](https://badge.fury.io/js/%40airy%2Fmaleo.svg)](https://badge.fury.io/js/%40airy%2Fmaleo)\n[![Issues](http://img.shields.io/github/issues/airyrooms/maleo.js.svg)]( https://github.com/airyrooms/maleo.js/issues)\n[![MIT license](https://img.shields.io/badge/License-MIT-blue.svg)](https://github.com/airyrooms/maleo.js/blob/canary/LICENSE)\n[![Discord Chat](https://img.shields.io/discord/550498214789513218.svg)](https://discord.gg/9eArCQn)\n[![Conventional Commits](https://img.shields.io/badge/Conventional%20Commits-1.0.0-green.svg)](https://conventionalcommits.org)\n\n\n# Welcome to Maleo.js\n\nMaleo.js is an un-opinionated framework to enable Universal Rendering in JavaScript using React with no hassle.\n\nWe are here to solve the time consuming setups Universal Rendering Required.\n\n---\n\nReadme below is the documentation for the `canary` (prerelease) branch. To view the documentation for the latest stable Maleo.js version change branch to `master`\n\n---\n\n# Table of Contents\n- [Features](#features)\n- [Setup](#setup)\n- [Component Lifecycle](#component-lifecycle)\n- [Routing](#routing)\n- [Dynamic Import Component](#dynamic-import-component)\n  - [Tips](#tips)\n  - [Preloading](#preloading)\n- [Customizable Component](#customizable-component)\n  - [Custom Document](#custom-document)\n  - [Custom Wrap](#custom-wrap)\n- [Custom Configuration](#custom-configuration)\n  - [Customize Server](#customize-server)\n  - [Customize Webpack](#customize-webpack)\n  - [Customize Babel Config](#customize-babel-config)\n- [Core Utilities Functions](#core-utilities-functions)\n- [CDN Support](#cdn-support)\n- [Plugins](#plugins)\n- [FAQ](#faq)\n- [Contributing](#contributing)\n\n---\n\n## Features\n- Universal Rendering\n- Plugin based framework\n- Customizable\n\n## Setup\n\nInstall Maleo.js \n\n>*for now change `@airy/maleo` to `@airy/maleo@canary` until we publish the stable version of this package*\n\n\n**NPM**\n```bash\n$ npm install --save @airy/maleo react\n```\n**Yarn**\n```bash\n$ yarn add @airy/maleo react\n```\n\nAdd this script to your `package.json`\n```json\n{\n  \"scripts\": {\n    \"dev\": \"maleo dev\",\n    \"build\": \"export NODE_ENV=production && maleo build\",\n    \"start\": \"export NODE_ENV=production && maleo run\"\n  }\n}\n```\n\nCreate a page Root component\n```jsx\n// ./src/Root.jsx\nimport React from 'react';\n\n// Export default is required for registering page\nexport default class RootComponent extends React.Component {\n  render() {\n    return (\n      <h1>Hello World!</h1>\n    )\n  }\n}\n```\n\nAnd lastly, create a routing file on your project root directory called `routes.json` and register your page component\n```json\n[\n  {\n    \"path\": \"/\",\n    \"page\": \"./src/Root\"\n  }\n]\n```\n\nAfter that you can now run `$ npm run dev` and go to `http://localhost:3000`.\n\nYou should now see your app running on your browser.\n\nBy now you should see \n- Automatic transpilation and bundling (with webpack and babel)\n- ~~Hot code reloading~~ [#17](https://github.com/airyrooms/maleo.js/issues/17)\n- Server rendering\n\nTo see how simple this is, check out the sample app!\n\n## Component Lifecycle \n\nMaleo.js added a new component lifecycle hook called `getInitialProps`, this function is called during Server Side Rendering (SSR) and during route changes on Client Side Rendering (CSR).\n\nThis is useful especially for SEO purposes. \n\nExample for stateful component:\n```jsx\nimport React from 'react';\n\nexport default class extends React.Component {\n  static getInitialProps = async (ctx) => {\n    const { req } = ctx;\n\n    // check if getInitialProps is called on server or on client\n    const userAgent = req ? req.headers['user-agent'] : navigator.userAgent;\n\n    // the return value will be passed as props for this component\n    return { userAgent };\n  }\n\n  render() {\n    return (\n      <div>\n        Hello World {this.props.userAgent}\n      </div>\n    );\n  }\n}\n```\n\nExample for stateless component:\n```jsx\nconst Component = (props) => (\n  <div>\n    Hello World {props.userAgent}\n  </div>\n);\n\nComponent.getInitialprops = async (ctx) => {\n  const { req } = ctx;\n\n  const userAgent = req ? req.headers['user-agent'] : navigator.userAgent;\n\n  return { userAgent };\n};\n\nexport default Component;\n```\n\n`getInitialProps` receives a context object with the following properties:\n- `req` - HTTP request object (server only)\n- `res` - HTTP response object (server only)\n- `...wrapProps` - Spreaded properties from custom Wrap\n- `...appProps` - Spreaded properties from custom App\n\n## Routing\n\nRouting is declared in a centralized route config.\nRegister all the route config in `routes.json` file.\n\nIf you put the `routes.json` files on root directory, Maleo will automatically register your route. Otherwise put path to your routes on [Maleo config](#custom-configuration).\n\nRoutes file has to export default the route configuration.\nThe route object **expected to have distinct key** to indicate the route.\n\n<table>\n  <tr>\n    <td>Key</td>\n    <td>Type</td>\n    <td>Description</td>\n  </tr>\n  <tr>\n    <td><code>path</code></td>\n    <td><code>String!</code></td>\n    <td>Routes path</td>\n  </tr>\n  <tr>\n    <td><code>page</code></td>\n    <td><code>String!</code></td>\n    <td>Path to React Component for this route path</td>\n  </tr>\n  <tr>\n    <td><code>exact</code></td>\n    <td><code>Boolean?</code> [<code>false</code>]</td>\n    <td>To make url has to match exactly the path in order to render the component. Give <code>false</code> value if the route is a wrapper route component</td>\n  </tr>\n  <tr>\n    <td><code>routes</code></td>\n    <td><code>RouteObject?</code></td>\n    <td>Nested route</td>\n  </tr>\n</table>\n\n\nFor example:\n```json\n[\n  {\n    \"page\": \"./src/MainApp\",\n    \"routes\": [\n      {\n        \"path\": \"/\",\n        \"page\": \"./src/Search\",\n        \"exact\": true\n      },\n      {\n        \"path\": \"/search\",\n        \"page\": \"./src/Search\",\n        \"routes\": [\n          {\n            \"path\": \"/search/hello\",\n            \"page\": \"./src/Detail\",\n            \"exact\": true\n          }\n        ]\n      },\n      {\n        \"path\": \"/detail\",\n        \"page\": \"./src/Detail\",\n        \"exact\": true\n      }\n    ]\n  }\n]\n```\n\n## Dynamic Import Component\nMaleo.js supports TC39 [dynamic import proposal](https://github.com/tc39/proposal-dynamic-import) for JavaScript.\n\nYou can think dynamic import as another way to split your code into manageable chunks. You can use our `Dynamic` function which utilizes [react loadable](https://github.com/jamiebuilds/react-loadable)\n\nFor Example\n```js\n// DynamicComponent.js\nimport Dynamic from '@airy/maleo/dynamic';\n\nexport default Dynamic({\n  loader: () => import( /* webpackChunkName:\"DynamicComponent\" */ './component/DynamicLoad'),\n})\n```\n\n### **Preloading**\n\nFor optimization purposes, you can also preload a component even before the component got rendered.\n\nFor example, if you want to load component when a button get pressed, you can start preloading the component when the user hovers the mouse over the button.\n\nThe component created by `Dynamic` exposes a [static `preload` method](https://github.com/jamiebuilds/react-loadable#loadablecomponentpreload).\n\n```jsx\nimport React from 'react';\nimport Dynamic from '@airy/maleo/dynamic';\n\nconst DynamicBar = Dynamic({\n  loader: () => import('./Bar'),\n  loading: LoadingCompoinent\n});\n\nclass MyComponent extends React.Component {\n  state = { showBar: false };\n\n  onClick = () => {\n    this.setState({ showBar: true });\n  };\n\n  onMouseOver = () => DynamicBar.preload();\n\n  render() {\n    return (\n      <div>\n        <button\n          onClick={this.onClick}\n          onMouseOver={this.onMouseOver}>\n          Show Bar\n        </button>\n        { this.state.showBar && <DynamicBar /> }\n      </div>\n    )\n  }\n}\n```\n\n## Customizable Component\n\n### Custom Document\n\nHighly inspired by what [Next.js](https://github.com/zeit/next.js) has done on their awesome template customization.\n\nMaleo.js also enable customization on `Document` as document's markup. So you don't need to include tags like `<html>`, `<head>`, `<body>`, etc. \n\nTo override the default behavior, you'll need to create a component that extends the `Document` React class provided by Maleo.\n\n```jsx\n// document.jsx\nimport React from 'react';\nimport { Document, Header, Main, Scripts } from '@airy/maleo/document';\n\nexport default class extends Document {\n  render() {\n    return (\n      <html>\n        <Header>\n          <title>Maleo JS</title>\n          <meta charset=\"utf-8\" />\n          <meta name=\"description\" content=\"Maleo.js is awesome!\" />\n\n          <style>\n            {` body { background-color: #fff } `}\n          </style>\n        </Header>\n\n        <body>\n          <Main />\n\n          <Scripts />\n\n          <ReduxScript />\n        </body>\n      </html>\n    );\n  }\n}\n```\n\n### Custom Wrap\n\nMaleo.js uses the `Wrap` component to initialize pages. `Wrap` contains React Router's Component. You can add HoC here to wrap the application and control the page initialization. Which allows you to do amazing things like:\n- Persisting layour between page changes\n- Keeping state when navigating pages\n- Custom error handling using `componentDidCatch`\n- Inject additional data into pages (like Redux provider, etc)\n\nTo override the default behavior, you'll need to create a component that extends the `Wrap` React class provided by Maleo.\n\n```jsx\n// wrap.jsx\nimport React from 'react';\nimport { Wrap } from '@airy/maleo/wrap';\n\n// Redux plugin for Maleo.js\n// Hoc that creates store and add Redux Provider\nimport { withRedux } from '@airy/maleo-redux-plugin';\n\n// Custom Wrapper that will be rendered for the whole Application\nimport Content from './component/Content';\nimport NavigationBar from './component/NavigationBar';\n\nimport { createStore } from './store';\n\n@withRedux(createStore)\nexport default class extends Wrap {\n  static getInitialProps = (ctx) => {\n    const { store } = ctx\n    // you receive store from context\n    // you can access or do something with the store here\n    console.log('Initialized Redux Store', store);\n    return {}\n  }\n\n  render() {\n    const { Container, containerProps, App, appProps } = this.props\n\n    return (\n      <Container {...containerProps}>\n        <NavigationBar />\n        <Content>\n          <App {...appProps}/>\n        </Content>\n      </Container>\n    )\n  }\n}\n\n```\nIf you put `document.jsx` and `wrap.jsx` on root directory (the same level with `package.json`), then Maleo will automatically register your custom Document and Wrap. Otherwise, you can add the path to your custom Document and Wrap on [Maleo config](#custom-configuration)\n\n---\n***We are also working on adding default and customizable `Error` component page***\n\n---\n\n## Custom Configuration\n\nFor more advanced configuration of Maleo.js, like `webpack` config, registering `plugins`, path to your routes, custom Document and Wrap, and adding `path alias`, you can create a `maleo.config.js` in the root of your project directory. (same directory with `package.json`)\n\n```js\n// maleo.config.js\n\nmodule.exports = {\n  /* config options here */\n}\n```\n\nHere are the API's for the configuration:\n\n<table>\n  <tr>\n    <td>Key</td>\n    <td>Type</td>\n    <td>Description</td>\n  </tr>\n  <tr>\n    <td><code>favicon</code></td>\n    <td><code>String</code> [<code>{project-dir}/favicon.ico</code>]</td>\n    <td>Path to favicon file</td>\n  </tr>\n  <tr>\n    <td><code>buildDir</code></td>\n    <td><code>String?</code> [<code>.maleo</code>]</td>\n    <td>Directory to put Maleo.js' build assets</td>\n  </tr>\n  <tr>\n    <td><code>cache</code></td>\n    <td><code>Boolean?</code> [<code>true</code>]</td>\n    <td>Enable webpack build caching</td>\n  </tr>\n  <tr>\n    <td><code>isDev</code></td>\n    <td><code>Boolean?</code> [<code>process.env.NODE_ENV === 'development'</code>]</td>\n    <td>Enable development build configuration</td>\n  </tr>\n  <tr>\n    <td><code>sourceMaps</code></td>\n    <td><code>Boolean?</code> [<code>true</code>]</td>\n    <td>Enable webpack to generate source maps</td>\n  </tr>\n  <tr>\n    <td><code>alias</code></td>\n    <td><code>Object?</code></td>\n    <td>A key value pair for aliasing path directory\n      <br>\n      <code>\n      { 'component': './src/component' }\n      </code>\n    </td>\n  </tr>\n  <tr>\n    <td><code>publicPath</code></td>\n    <td><code>String?</code> [<code>/_assets/</code>]</td>\n    <td>To customize webpack's <code>publicPath</code> \n    <br>Comes in handy if using CDN to put built assets</td>\n  </tr>\n  <tr>\n    <td><code>analyzeBundle</code></td>\n    <td><code>Boolean?</code> [<code>false</code>]</td>\n    <td>To enable webpack's bundle analyzer, for analyzing bundle sizes during bundle debugging should Maleo.js' build process got slow</td>\n  </tr>\n  <tr>\n    <td><code>webpack</code></td>\n    <td><code>Function?</code></td>\n    <td>To customize webpack configuration, more details <a href=\"#customize-webpack\">here</a></td>\n  </tr>\n  <tr>\n    <td><code>routes</code></td>\n    <td><code>string?</code> [<code>rootDir/routes.jsx</code>]</td>\n    <td>Path to your routes file</td>\n  </tr>\n  <tr>\n    <td><code>customDocument</code></td>\n    <td><code>string?</code> [<code>rootDir/document.jsx</code>]</td>\n    <td>Path to your custom document file</td>\n  </tr>\n  <tr>\n    <td><code>customWrap</code></td>\n    <td><code>string?</code> [<code>rootDir/wrap.jsx</code>]</td>\n    <td>Path to your custom wrap file</td>\n  </tr>\n  <tr>\n    <td><code>customApp</code></td>\n    <td><code>string?</code> [<code>rootDir/app.jsx</code>]</td>\n    <td>Path to your custom app file</td>\n  </tr>\n</table>\n\n#### Customize Server\n\nCreate a `server.js` file on root directory where your `package.json` lives.\nHere you can customize Maleo's server.\n```js\nimport { Server } from '@airy/maleo/server';\n\nconst PORT = process.env.PORT || 3000;\n\nconst maleoServer = Server.init({\n  port: PORT,\n  runHandler: () => {\n    console.log('Server running on port :', PORT);\n  }\n});\n\nmaleoServer.run();\n```\n\nHere are the API's for the configuration:\n\n<table>\n  <tr>\n    <td>Key</td>\n    <td>Type</td>\n    <td>Description</td>\n  </tr>\n  <tr>\n    <td><code>port</code></td>\n    <td><code>Number?</code> [<code>3000</code>]</td>\n    <td>Port to run Maleo server</td>\n  </tr>\n  <tr>\n    <td><code>assetDir</code></td>\n    <td><code>String?</code> [<code>'<root>/.maleo/client'</code>]</td>\n    <td>Directory for all client related assets</td>\n  </tr>\n  <tr>\n    <td><code>runHandler</code></td>\n    <td><code>Function?</code></td>\n    <td>Function called when maleo server starter</td>\n  </tr>\n  <tr>\n    <td><code>csp</code></td>\n    <td><code>cspConfig?</code> [<code>{\n      directives: {\n        defaultSrc: [`'self'`],\n        styleSrc: [`'self'`],\n      },\n    }</code>]</td>\n    <td>Config for <a href=\"https://developers.google.com/web/fundamentals/security/csp/\">Content Security Policy</a>, using <a href=\"https://helmetjs.github.io/docs/csp/\">Helmet-CSP</a></td>\n  </tr>\n</table>\n\n#### Customize Webpack\n\nYou are able to extend Maleo.js' default webpack configuration by defining a function on `maleo.config.js`\n\n```js\n// maleo.config.js\n\nmodule.exports = {\n  webpack(config, context, next) {\n    // Perform customizations to webpack config\n    // Important: This call is required in order to let Maleo pass the config to webpack\n    return next(); \n  },\n};\n```\n\nWebpack function will receive three arguments:\n\n<table>\n  <tr>\n    <td>Argument</td>\n    <td>Details</td>\n  <tr>\n  <tr>\n    <td><code>config</code></td>\n    <td>This contains webpack configuration object that you can manipulate</td>\n  <tr>\n  <tr>\n    <td><code>context</code></td>\n    <td>This contains some keys that are useful for the build context\n      <br>\n      <table>\n        <tr>\n          <td><code>isDev</code></td>\n          <td><code>Boolean</code></td>\n          <td>Check if current build is for development</td>\n        </tr>\n        <tr>\n          <td><code>publicPath</code></td>\n          <td><code>String</code></td>\n          <td>Public Path of user defined or default's value</td>\n        </tr>\n        <tr>\n          <td><code>analyzeBundle</code></td>\n          <td><code>Boolean</code></td>\n          <td>Check if analyze bundle is enabled</td>\n        </tr>\n        <tr>\n          <td><code>buildDirectory</code></td>\n          <td><code>String</code></td>\n          <td>Build Directory of user defined or default's value</td>\n        </tr>\n        <tr>\n          <td><code>name</code></td>\n          <td><code>String</code></td>\n          <td>Build name <code>'server' || 'client'</code></td>\n        </tr>\n      </table>\n    </td>\n  <tr>\n  <tr>\n    <td><code>next</code></td>\n    <td>A callback required to be called to pass the custom configuration</td>\n  <tr>\n</table>\n\nExample of adding `ts-loader` through `maleo.config.js`:\n\n```js\n// maleo.config.js\n\n// Partly taken and modified from @airy/maleo-ts-plugin source\n// for simplicity purposes\n\nmodule.exports = {\n  webpack(config, context, next) {\n    const { isDev } = context\n\n    config.module.rules.push({\n      test: /\\.tsx?/,\n      exclude: /node_modules/,\n      use: [\n        require.resolve('@airy/maleo/lib/build/loaders/maleo-babel-loader'),\n        {\n          loader: 'ts-loader',\n          options: {\n            transpileOnly: true,\n          },\n        },\n      ],\n\n      if (isDev) {\n        config.plugins.push(new ForkTSCheckerWebpackPlugin());\n      }\n\n      return next();\n    })\n  },\n};\n```\n\n#### Customize Babel Config\n\nMaleo.js also let you have your own babel config. Just simply add `.babelrc` file at the root directory of your app.\n\nYou can include Maleo.js' babel preset in order to have latest JavaScript preset.\n\nHere's an example of `.babelrc` file:\n```json\n{\n  \"presets\": [\"@airy/maleo/babel\"],\n  \"plugins\": []\n}\n```\n\nThe `@airy/maleo/babel` preset includes everything you need to get your development started. The preset includes:\n- `@babel/preset-env`\n- `@babel/preset-react`\n- `@babel/plugin-proposal-class-properties`\n- `@babel/plugin-proposal-decorators`\n- `@babel/plugin-proposal-object-rest-spread`\n- `@babel/plugin-transform-runtime`\n- `react-loadable/babel`\n\n## Core Utilities Functions\n\n#### Route Matching\n\nTo use you can import the matching routes function like this:\n```js\nimport { getMatchedRoutes } from '@airy/maleo/utils';\n```\n\nThe function will return all matched routes and the route object from `routes.json`\n\n## CDN Support\n\nIf you are using a CDN, you can set up the `publicPath` setting and configure your CDN's origin to resolve to the domain that Maleo.js is hosted on.\n\n```js\n// maleo.config.js\n\nconst isProd = process.env.NODE_ENV === 'production';\n\nmodule.exports = {\n  assetPrefix: isProd && 'https://cdn.example.com';\n}\n```\n\n## FAQ\n\n== TO BE DETERMINED == \n\n## Plugins\n- [css-plugin](https://github.com/airyrooms/maleo-plugins/tree/master/packages/css-plugin)\n- [redux-plugin](https://github.com/airyrooms/maleo-plugins/tree/master/packages/redux-plugin)\n- [typescript-plugin](https://github.com/airyrooms/maleo-plugins/tree/master/packages/typescript-plugin)\n- [more](https://github.com/airyrooms/maleo-plugins)\n\n\n## Contributing\n[Please follow these steps to contribute to Maleo.js](https://github.com/airyrooms/maleo.js/blob/canary/CONTRIBUTING.md)\n\n[Please follow these steps to contribute to Maleo.js' plugins](https://github.com/airyrooms/maleo-plugins#contributing-guidelines)\n\n\n## Contributors\n\nThis project exists thanks to all the people who contribute. [Contribute](CONTRIBUTING.md).\n\nMany thanks to our **[contributors](https://github.com/airyrooms/maleo.js/graphs/contributors)**!\n\n\n## License\n\nMIT","readmeFilename":"README.md","bugs":{"url":"https://github.com/airyrooms/maleo.js/issues"},"homepage":"https://github.com/airyrooms/maleo.js#readme","_id":"@airy/maleo@0.3.1-canary.19","_nodeVersion":"10.16.0","_npmVersion":"lerna/3.13.1/node@v10.16.0+x64 (linux)","dist":{"integrity":"sha512-IlqTE9Gin607FUltFyY5PbLJYs7a2nOOAEljxT+ah9wYFAn+WCW+/p6yChrtKbxXuLDIUiB3aGNqG5TgcFKP0g==","shasum":"d1600b40afe8916f2f21e2ea0230a1eb58d22faf","tarball":"https://registry.npmjs.org/@airy/maleo/-/maleo-0.3.1-canary.19.tgz","fileCount":96,"unpackedSize":335722,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJdK/tsCRA9TVsSAnZWagAAi60P/0edCGs1iv0ubJ+WXpAV\nMpgpMVEjzDhJW0hradiJkBE2lf4HjbKTVxJY84neMrcu9DKtW9OiNxr8maTx\nMc9wckaZv2unNDd4WCCcnAr6UI7Ck8Pvbdcla4TfSJlhviGZHi4MGgbWQdbp\nocKRcxeRTP56R889jPqwKPRgtxGVK3d9lMSTRQLL3vnHycjVA4cJ/FXbolcx\nFBRhm2SmTAhMH4g1wmHGVp8m+gl+WtQf+XjWUQBxc1pwI6RUYKn2wyvazgMJ\n22/ncoCSOJqe8xtuFN2i8jleVns0JHoSgi/X2Zp3f2kOT0u4wVoRRAmw+5No\nXV4iT2KMH6nzBYPCaflAQx1k09EaB5VFIeD6w8B67aCPTmfP9wLjr1YUYLpD\n/Bze4pD9rv2YXfHQ/aftIoDtOe2l7DT0y3mC4GpovNVhxjrpbqer35g5r9HI\nDX4zRjsc8CP3XXilcxsFU7XdOlG07dAERqox7VhuZF/75VsQKSOH3D5G/WNW\niyXsgkT0H8HukfmGxa1XYWB4oEejgfKuwWXTOiN0LOVspv1NfIcYV0JtmQSI\n+vCXMQYRKQFoz1HIprXojcb69lAyZBy9tUKXfuOUcMvxIs2eiBlarcathW5z\n8FpuMmy3G18fX8nX3Vm3Jz4Gx/+ScfOauuGDg0Ctvn3uK4f71rcusbShoNaK\nuNRk\r\n=AGnf\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIFTCGRCbZ3u+GRU1qmIXQdvAQZHGD/NRPSH11sY0rGTPAiBrI8RWiaxgoganwwV97VWFJr4+pX7Bu/qTlo68RFEFQA=="}]},"maintainers":[{"name":"airyrooms-engineering","email":"engineering@airyrooms.com"}],"_npmUser":{"name":"airyrooms-engineering","email":"engineering@airyrooms.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/maleo_0.3.1-canary.19_1563163499777_0.909675418217035"},"_hasShrinkwrap":false},"0.3.1-canary.20":{"name":"@airy/maleo","version":"0.3.1-canary.20","description":"Un-opinionated Universal Rendering Javascript Framework","repository":{"type":"git","url":"git+https://github.com/airyrooms/maleo.js.git","bugs":"https://github.com/airyrooms/maleo.js/issues"},"author":{"name":"Airy"},"license":"MIT","private":false,"sideEffects":false,"scripts":{"build":"rm -rf dist && gulp","watch":"gulp watch","prepublish":"yarn build","start":"node server.js"},"bin":{"maleo":"lib/bin/maleo.js"},"dependencies":{"@babel/core":"7.1.2","@babel/plugin-proposal-class-properties":"7.1.0","@babel/plugin-proposal-decorators":"7.1.2","@babel/plugin-proposal-object-rest-spread":"7.0.0","@babel/plugin-syntax-dynamic-import":"7.0.0","@babel/plugin-transform-runtime":"7.1.0","@babel/preset-env":"7.1.0","@babel/preset-react":"7.0.0","@babel/runtime":"7.1.2","@babel/runtime-corejs2":"7.2.0","babel-loader":"8.0.4","case-sensitive-paths-webpack-plugin":"2.1.2","compression":"1.7.4","es6-promise":"4.2.5","eventsource":"1.0.7","express":"4.16.4","figlet":"1.2.1","flatted":"^2.0.1","friendly-errors-webpack-plugin":"1.7.0","hard-source-webpack-plugin":"0.13.1","helmet":"3.14.0","hoist-non-react-statics":"^3.3.0","isomorphic-fetch":"2.2.1","loader-utils":"1.1.0","node-notifier":"5.2.1","nodemon":"1.18.4","react-dom":"16.8.4","react-helmet":"5.2.0","react-loadable":"5.5.0","react-router-config":"5.0.0","react-router-dom":"5.0.0","rimraf":"2.6.3","source-map-support":"0.5.9","terser-webpack-plugin":"1.1.0","webpack":"4.29.0","webpack-bundle-analyzer":"3.0.3","webpack-cli":"3.1.2","webpack-dev-middleware":"3.4.0","webpack-hot-middleware":"2.24.3","webpackbar":"2.6.3"},"peerDependencies":{"react":"^16.8.4"},"devDependencies":{"@babel/preset-typescript":"^7.1.0","@types/webpack-bundle-analyzer":"^2.13.0","babel-plugin-module-resolver":"^3.1.1","gulp":"github:gulpjs/gulp#4.0","gulp-babel":"^8.0.0","gulp-clean":"^0.4.0","gulp-sourcemaps":"^2.6.4","gulp-uglify":"^3.0.1","gulp-watch":"^5.0.1","react":"^16.8.4"},"gitHead":"dc377d65fcb30891b7a021b9bd248a909c65bc43","readme":"[![Build Status](https://travis-ci.org/airyrooms/maleo.js.svg?branch=master)](https://travis-ci.org/airyrooms/maleo.js)\n[![Coverage Status](https://coveralls.io/repos/github/airyrooms/maleo.js/badge.svg?branch=master)](https://coveralls.io/github/airyrooms/maleo.js?branch=master)\n[![npm version](https://badge.fury.io/js/%40airy%2Fmaleo.svg)](https://badge.fury.io/js/%40airy%2Fmaleo)\n[![Issues](http://img.shields.io/github/issues/airyrooms/maleo.js.svg)]( https://github.com/airyrooms/maleo.js/issues)\n[![MIT license](https://img.shields.io/badge/License-MIT-blue.svg)](https://github.com/airyrooms/maleo.js/blob/canary/LICENSE)\n[![Discord Chat](https://img.shields.io/discord/550498214789513218.svg)](https://discord.gg/9eArCQn)\n[![Conventional Commits](https://img.shields.io/badge/Conventional%20Commits-1.0.0-green.svg)](https://conventionalcommits.org)\n\n\n# Welcome to Maleo.js\n\nMaleo.js is an un-opinionated framework to enable Universal Rendering in JavaScript using React with no hassle.\n\nWe are here to solve the time consuming setups Universal Rendering Required.\n\n---\n\nReadme below is the documentation for the `canary` (prerelease) branch. To view the documentation for the latest stable Maleo.js version change branch to `master`\n\n---\n\n# Table of Contents\n- [Features](#features)\n- [Setup](#setup)\n- [Component Lifecycle](#component-lifecycle)\n- [Routing](#routing)\n- [Dynamic Import Component](#dynamic-import-component)\n  - [Tips](#tips)\n  - [Preloading](#preloading)\n- [Customizable Component](#customizable-component)\n  - [Custom Document](#custom-document)\n  - [Custom Wrap](#custom-wrap)\n- [Custom Configuration](#custom-configuration)\n  - [Customize Server](#customize-server)\n  - [Customize Webpack](#customize-webpack)\n  - [Customize Babel Config](#customize-babel-config)\n- [Core Utilities Functions](#core-utilities-functions)\n- [CDN Support](#cdn-support)\n- [Plugins](#plugins)\n- [FAQ](#faq)\n- [Contributing](#contributing)\n\n---\n\n## Features\n- Universal Rendering\n- Plugin based framework\n- Customizable\n\n## Setup\n\nInstall Maleo.js \n\n>*for now change `@airy/maleo` to `@airy/maleo@canary` until we publish the stable version of this package*\n\n\n**NPM**\n```bash\n$ npm install --save @airy/maleo react\n```\n**Yarn**\n```bash\n$ yarn add @airy/maleo react\n```\n\nAdd this script to your `package.json`\n```json\n{\n  \"scripts\": {\n    \"dev\": \"maleo dev\",\n    \"build\": \"export NODE_ENV=production && maleo build\",\n    \"start\": \"export NODE_ENV=production && maleo run\"\n  }\n}\n```\n\nCreate a page Root component\n```jsx\n// ./src/Root.jsx\nimport React from 'react';\n\n// Export default is required for registering page\nexport default class RootComponent extends React.Component {\n  render() {\n    return (\n      <h1>Hello World!</h1>\n    )\n  }\n}\n```\n\nAnd lastly, create a routing file on your project root directory called `routes.json` and register your page component\n```json\n[\n  {\n    \"path\": \"/\",\n    \"page\": \"./src/Root\"\n  }\n]\n```\n\nAfter that you can now run `$ npm run dev` and go to `http://localhost:3000`.\n\nYou should now see your app running on your browser.\n\nBy now you should see \n- Automatic transpilation and bundling (with webpack and babel)\n- ~~Hot code reloading~~ [#17](https://github.com/airyrooms/maleo.js/issues/17)\n- Server rendering\n\nTo see how simple this is, check out the sample app!\n\n## Component Lifecycle \n\nMaleo.js added a new component lifecycle hook called `getInitialProps`, this function is called during Server Side Rendering (SSR) and during route changes on Client Side Rendering (CSR).\n\nThis is useful especially for SEO purposes. \n\nExample for stateful component:\n```jsx\nimport React from 'react';\n\nexport default class extends React.Component {\n  static getInitialProps = async (ctx) => {\n    const { req } = ctx;\n\n    // check if getInitialProps is called on server or on client\n    const userAgent = req ? req.headers['user-agent'] : navigator.userAgent;\n\n    // the return value will be passed as props for this component\n    return { userAgent };\n  }\n\n  render() {\n    return (\n      <div>\n        Hello World {this.props.userAgent}\n      </div>\n    );\n  }\n}\n```\n\nExample for stateless component:\n```jsx\nconst Component = (props) => (\n  <div>\n    Hello World {props.userAgent}\n  </div>\n);\n\nComponent.getInitialprops = async (ctx) => {\n  const { req } = ctx;\n\n  const userAgent = req ? req.headers['user-agent'] : navigator.userAgent;\n\n  return { userAgent };\n};\n\nexport default Component;\n```\n\n`getInitialProps` receives a context object with the following properties:\n- `req` - HTTP request object (server only)\n- `res` - HTTP response object (server only)\n- `...wrapProps` - Spreaded properties from custom Wrap\n- `...appProps` - Spreaded properties from custom App\n\n## Routing\n\nRouting is declared in a centralized route config.\nRegister all the route config in `routes.json` file.\n\nIf you put the `routes.json` files on root directory, Maleo will automatically register your route. Otherwise put path to your routes on [Maleo config](#custom-configuration).\n\nRoutes file has to export default the route configuration.\nThe route object **expected to have distinct key** to indicate the route.\n\n<table>\n  <tr>\n    <td>Key</td>\n    <td>Type</td>\n    <td>Description</td>\n  </tr>\n  <tr>\n    <td><code>path</code></td>\n    <td><code>String!</code></td>\n    <td>Routes path</td>\n  </tr>\n  <tr>\n    <td><code>page</code></td>\n    <td><code>String!</code></td>\n    <td>Path to React Component for this route path</td>\n  </tr>\n  <tr>\n    <td><code>exact</code></td>\n    <td><code>Boolean?</code> [<code>false</code>]</td>\n    <td>To make url has to match exactly the path in order to render the component. Give <code>false</code> value if the route is a wrapper route component</td>\n  </tr>\n  <tr>\n    <td><code>routes</code></td>\n    <td><code>RouteObject?</code></td>\n    <td>Nested route</td>\n  </tr>\n</table>\n\n\nFor example:\n```json\n[\n  {\n    \"page\": \"./src/MainApp\",\n    \"routes\": [\n      {\n        \"path\": \"/\",\n        \"page\": \"./src/Search\",\n        \"exact\": true\n      },\n      {\n        \"path\": \"/search\",\n        \"page\": \"./src/Search\",\n        \"routes\": [\n          {\n            \"path\": \"/search/hello\",\n            \"page\": \"./src/Detail\",\n            \"exact\": true\n          }\n        ]\n      },\n      {\n        \"path\": \"/detail\",\n        \"page\": \"./src/Detail\",\n        \"exact\": true\n      }\n    ]\n  }\n]\n```\n\n## Dynamic Import Component\nMaleo.js supports TC39 [dynamic import proposal](https://github.com/tc39/proposal-dynamic-import) for JavaScript.\n\nYou can think dynamic import as another way to split your code into manageable chunks. You can use our `Dynamic` function which utilizes [react loadable](https://github.com/jamiebuilds/react-loadable)\n\nFor Example\n```js\n// DynamicComponent.js\nimport Dynamic from '@airy/maleo/dynamic';\n\nexport default Dynamic({\n  loader: () => import( /* webpackChunkName:\"DynamicComponent\" */ './component/DynamicLoad'),\n})\n```\n\n### **Preloading**\n\nFor optimization purposes, you can also preload a component even before the component got rendered.\n\nFor example, if you want to load component when a button get pressed, you can start preloading the component when the user hovers the mouse over the button.\n\nThe component created by `Dynamic` exposes a [static `preload` method](https://github.com/jamiebuilds/react-loadable#loadablecomponentpreload).\n\n```jsx\nimport React from 'react';\nimport Dynamic from '@airy/maleo/dynamic';\n\nconst DynamicBar = Dynamic({\n  loader: () => import('./Bar'),\n  loading: LoadingCompoinent\n});\n\nclass MyComponent extends React.Component {\n  state = { showBar: false };\n\n  onClick = () => {\n    this.setState({ showBar: true });\n  };\n\n  onMouseOver = () => DynamicBar.preload();\n\n  render() {\n    return (\n      <div>\n        <button\n          onClick={this.onClick}\n          onMouseOver={this.onMouseOver}>\n          Show Bar\n        </button>\n        { this.state.showBar && <DynamicBar /> }\n      </div>\n    )\n  }\n}\n```\n\n## Customizable Component\n\n### Custom Document\n\nHighly inspired by what [Next.js](https://github.com/zeit/next.js) has done on their awesome template customization.\n\nMaleo.js also enable customization on `Document` as document's markup. So you don't need to include tags like `<html>`, `<head>`, `<body>`, etc. \n\nTo override the default behavior, you'll need to create a component that extends the `Document` React class provided by Maleo.\n\n```jsx\n// document.jsx\nimport React from 'react';\nimport { Document, Header, Main, Scripts } from '@airy/maleo/document';\n\nexport default class extends Document {\n  render() {\n    return (\n      <html>\n        <Header>\n          <title>Maleo JS</title>\n          <meta charset=\"utf-8\" />\n          <meta name=\"description\" content=\"Maleo.js is awesome!\" />\n\n          <style>\n            {` body { background-color: #fff } `}\n          </style>\n        </Header>\n\n        <body>\n          <Main />\n\n          <Scripts />\n\n          <ReduxScript />\n        </body>\n      </html>\n    );\n  }\n}\n```\n\n### Custom Wrap\n\nMaleo.js uses the `Wrap` component to initialize pages. `Wrap` contains React Router's Component. You can add HoC here to wrap the application and control the page initialization. Which allows you to do amazing things like:\n- Persisting layour between page changes\n- Keeping state when navigating pages\n- Custom error handling using `componentDidCatch`\n- Inject additional data into pages (like Redux provider, etc)\n\nTo override the default behavior, you'll need to create a component that extends the `Wrap` React class provided by Maleo.\n\n```jsx\n// wrap.jsx\nimport React from 'react';\nimport { Wrap } from '@airy/maleo/wrap';\n\n// Redux plugin for Maleo.js\n// Hoc that creates store and add Redux Provider\nimport { withRedux } from '@airy/maleo-redux-plugin';\n\n// Custom Wrapper that will be rendered for the whole Application\nimport Content from './component/Content';\nimport NavigationBar from './component/NavigationBar';\n\nimport { createStore } from './store';\n\n@withRedux(createStore)\nexport default class extends Wrap {\n  static getInitialProps = (ctx) => {\n    const { store } = ctx\n    // you receive store from context\n    // you can access or do something with the store here\n    console.log('Initialized Redux Store', store);\n    return {}\n  }\n\n  render() {\n    const { Container, containerProps, App, appProps } = this.props\n\n    return (\n      <Container {...containerProps}>\n        <NavigationBar />\n        <Content>\n          <App {...appProps}/>\n        </Content>\n      </Container>\n    )\n  }\n}\n\n```\nIf you put `document.jsx` and `wrap.jsx` on root directory (the same level with `package.json`), then Maleo will automatically register your custom Document and Wrap. Otherwise, you can add the path to your custom Document and Wrap on [Maleo config](#custom-configuration)\n\n---\n***We are also working on adding default and customizable `Error` component page***\n\n---\n\n## Custom Configuration\n\nFor more advanced configuration of Maleo.js, like `webpack` config, registering `plugins`, path to your routes, custom Document and Wrap, and adding `path alias`, you can create a `maleo.config.js` in the root of your project directory. (same directory with `package.json`)\n\n```js\n// maleo.config.js\n\nmodule.exports = {\n  /* config options here */\n}\n```\n\nHere are the API's for the configuration:\n\n<table>\n  <tr>\n    <td>Key</td>\n    <td>Type</td>\n    <td>Description</td>\n  </tr>\n  <tr>\n    <td><code>favicon</code></td>\n    <td><code>String</code> [<code>{project-dir}/favicon.ico</code>]</td>\n    <td>Path to favicon file</td>\n  </tr>\n  <tr>\n    <td><code>buildDir</code></td>\n    <td><code>String?</code> [<code>.maleo</code>]</td>\n    <td>Directory to put Maleo.js' build assets</td>\n  </tr>\n  <tr>\n    <td><code>cache</code></td>\n    <td><code>Boolean?</code> [<code>true</code>]</td>\n    <td>Enable webpack build caching</td>\n  </tr>\n  <tr>\n    <td><code>isDev</code></td>\n    <td><code>Boolean?</code> [<code>process.env.NODE_ENV === 'development'</code>]</td>\n    <td>Enable development build configuration</td>\n  </tr>\n  <tr>\n    <td><code>sourceMaps</code></td>\n    <td><code>Boolean?</code> [<code>true</code>]</td>\n    <td>Enable webpack to generate source maps</td>\n  </tr>\n  <tr>\n    <td><code>alias</code></td>\n    <td><code>Object?</code></td>\n    <td>A key value pair for aliasing path directory\n      <br>\n      <code>\n      { 'component': './src/component' }\n      </code>\n    </td>\n  </tr>\n  <tr>\n    <td><code>publicPath</code></td>\n    <td><code>String?</code> [<code>/_assets/</code>]</td>\n    <td>To customize webpack's <code>publicPath</code> \n    <br>Comes in handy if using CDN to put built assets</td>\n  </tr>\n  <tr>\n    <td><code>analyzeBundle</code></td>\n    <td><code>Boolean?</code> [<code>false</code>]</td>\n    <td>To enable webpack's bundle analyzer, for analyzing bundle sizes during bundle debugging should Maleo.js' build process got slow</td>\n  </tr>\n  <tr>\n    <td><code>webpack</code></td>\n    <td><code>Function?</code></td>\n    <td>To customize webpack configuration, more details <a href=\"#customize-webpack\">here</a></td>\n  </tr>\n  <tr>\n    <td><code>routes</code></td>\n    <td><code>string?</code> [<code>rootDir/routes.jsx</code>]</td>\n    <td>Path to your routes file</td>\n  </tr>\n  <tr>\n    <td><code>customDocument</code></td>\n    <td><code>string?</code> [<code>rootDir/document.jsx</code>]</td>\n    <td>Path to your custom document file</td>\n  </tr>\n  <tr>\n    <td><code>customWrap</code></td>\n    <td><code>string?</code> [<code>rootDir/wrap.jsx</code>]</td>\n    <td>Path to your custom wrap file</td>\n  </tr>\n  <tr>\n    <td><code>customApp</code></td>\n    <td><code>string?</code> [<code>rootDir/app.jsx</code>]</td>\n    <td>Path to your custom app file</td>\n  </tr>\n</table>\n\n#### Customize Server\n\nCreate a `server.js` file on root directory where your `package.json` lives.\nHere you can customize Maleo's server.\n```js\nimport { Server } from '@airy/maleo/server';\n\nconst PORT = process.env.PORT || 3000;\n\nconst maleoServer = Server.init({\n  port: PORT,\n  runHandler: () => {\n    console.log('Server running on port :', PORT);\n  }\n});\n\nmaleoServer.run();\n```\n\nHere are the API's for the configuration:\n\n<table>\n  <tr>\n    <td>Key</td>\n    <td>Type</td>\n    <td>Description</td>\n  </tr>\n  <tr>\n    <td><code>port</code></td>\n    <td><code>Number?</code> [<code>3000</code>]</td>\n    <td>Port to run Maleo server</td>\n  </tr>\n  <tr>\n    <td><code>assetDir</code></td>\n    <td><code>String?</code> [<code>'<root>/.maleo/client'</code>]</td>\n    <td>Directory for all client related assets</td>\n  </tr>\n  <tr>\n    <td><code>runHandler</code></td>\n    <td><code>Function?</code></td>\n    <td>Function called when maleo server starter</td>\n  </tr>\n  <tr>\n    <td><code>csp</code></td>\n    <td><code>cspConfig?</code> [<code>{\n      directives: {\n        defaultSrc: [`'self'`],\n        styleSrc: [`'self'`],\n      },\n    }</code>]</td>\n    <td>Config for <a href=\"https://developers.google.com/web/fundamentals/security/csp/\">Content Security Policy</a>, using <a href=\"https://helmetjs.github.io/docs/csp/\">Helmet-CSP</a></td>\n  </tr>\n</table>\n\n#### Customize Webpack\n\nYou are able to extend Maleo.js' default webpack configuration by defining a function on `maleo.config.js`\n\n```js\n// maleo.config.js\n\nmodule.exports = {\n  webpack(config, context, next) {\n    // Perform customizations to webpack config\n    // Important: This call is required in order to let Maleo pass the config to webpack\n    return next(); \n  },\n};\n```\n\nWebpack function will receive three arguments:\n\n<table>\n  <tr>\n    <td>Argument</td>\n    <td>Details</td>\n  <tr>\n  <tr>\n    <td><code>config</code></td>\n    <td>This contains webpack configuration object that you can manipulate</td>\n  <tr>\n  <tr>\n    <td><code>context</code></td>\n    <td>This contains some keys that are useful for the build context\n      <br>\n      <table>\n        <tr>\n          <td><code>isDev</code></td>\n          <td><code>Boolean</code></td>\n          <td>Check if current build is for development</td>\n        </tr>\n        <tr>\n          <td><code>publicPath</code></td>\n          <td><code>String</code></td>\n          <td>Public Path of user defined or default's value</td>\n        </tr>\n        <tr>\n          <td><code>analyzeBundle</code></td>\n          <td><code>Boolean</code></td>\n          <td>Check if analyze bundle is enabled</td>\n        </tr>\n        <tr>\n          <td><code>buildDirectory</code></td>\n          <td><code>String</code></td>\n          <td>Build Directory of user defined or default's value</td>\n        </tr>\n        <tr>\n          <td><code>name</code></td>\n          <td><code>String</code></td>\n          <td>Build name <code>'server' || 'client'</code></td>\n        </tr>\n      </table>\n    </td>\n  <tr>\n  <tr>\n    <td><code>next</code></td>\n    <td>A callback required to be called to pass the custom configuration</td>\n  <tr>\n</table>\n\nExample of adding `ts-loader` through `maleo.config.js`:\n\n```js\n// maleo.config.js\n\n// Partly taken and modified from @airy/maleo-ts-plugin source\n// for simplicity purposes\n\nmodule.exports = {\n  webpack(config, context, next) {\n    const { isDev } = context\n\n    config.module.rules.push({\n      test: /\\.tsx?/,\n      exclude: /node_modules/,\n      use: [\n        require.resolve('@airy/maleo/lib/build/loaders/maleo-babel-loader'),\n        {\n          loader: 'ts-loader',\n          options: {\n            transpileOnly: true,\n          },\n        },\n      ],\n\n      if (isDev) {\n        config.plugins.push(new ForkTSCheckerWebpackPlugin());\n      }\n\n      return next();\n    })\n  },\n};\n```\n\n#### Customize Babel Config\n\nMaleo.js also let you have your own babel config. Just simply add `.babelrc` file at the root directory of your app.\n\nYou can include Maleo.js' babel preset in order to have latest JavaScript preset.\n\nHere's an example of `.babelrc` file:\n```json\n{\n  \"presets\": [\"@airy/maleo/babel\"],\n  \"plugins\": []\n}\n```\n\nThe `@airy/maleo/babel` preset includes everything you need to get your development started. The preset includes:\n- `@babel/preset-env`\n- `@babel/preset-react`\n- `@babel/plugin-proposal-class-properties`\n- `@babel/plugin-proposal-decorators`\n- `@babel/plugin-proposal-object-rest-spread`\n- `@babel/plugin-transform-runtime`\n- `react-loadable/babel`\n\n## Core Utilities Functions\n\n#### Route Matching\n\nTo use you can import the matching routes function like this:\n```js\nimport { getMatchedRoutes } from '@airy/maleo/utils';\n```\n\nThe function will return all matched routes and the route object from `routes.json`\n\n## CDN Support\n\nIf you are using a CDN, you can set up the `publicPath` setting and configure your CDN's origin to resolve to the domain that Maleo.js is hosted on.\n\n```js\n// maleo.config.js\n\nconst isProd = process.env.NODE_ENV === 'production';\n\nmodule.exports = {\n  assetPrefix: isProd && 'https://cdn.example.com';\n}\n```\n\n## FAQ\n\n== TO BE DETERMINED == \n\n## Plugins\n- [css-plugin](https://github.com/airyrooms/maleo-plugins/tree/master/packages/css-plugin)\n- [redux-plugin](https://github.com/airyrooms/maleo-plugins/tree/master/packages/redux-plugin)\n- [typescript-plugin](https://github.com/airyrooms/maleo-plugins/tree/master/packages/typescript-plugin)\n- [more](https://github.com/airyrooms/maleo-plugins)\n\n\n## Contributing\n[Please follow these steps to contribute to Maleo.js](https://github.com/airyrooms/maleo.js/blob/canary/CONTRIBUTING.md)\n\n[Please follow these steps to contribute to Maleo.js' plugins](https://github.com/airyrooms/maleo-plugins#contributing-guidelines)\n\n\n## Contributors\n\nThis project exists thanks to all the people who contribute. [Contribute](CONTRIBUTING.md).\n\nMany thanks to our **[contributors](https://github.com/airyrooms/maleo.js/graphs/contributors)**!\n\n\n## License\n\nMIT","readmeFilename":"README.md","bugs":{"url":"https://github.com/airyrooms/maleo.js/issues"},"homepage":"https://github.com/airyrooms/maleo.js#readme","_id":"@airy/maleo@0.3.1-canary.20","_nodeVersion":"11.15.0","_npmVersion":"lerna/3.13.1/node@v11.15.0+x64 (linux)","dist":{"integrity":"sha512-pPfOilI0C2gYfNWdtHNnNzmwV1sD0AQ+CsijbYNaf0uot0xj6c/0yG/5A96tdeZoZpnOf4Au9XIGD5scCDAQyw==","shasum":"ae13fef6a19f0396d0210ae7ca02b2a54c63540c","tarball":"https://registry.npmjs.org/@airy/maleo/-/maleo-0.3.1-canary.20.tgz","fileCount":96,"unpackedSize":336087,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJdLE2mCRA9TVsSAnZWagAAKQEP+weEs1XgBKAQ0B4wkfgA\nXDX2CkbBzQArSW2u6x24LdWH/y07mQvRW3veXpisyKcHQiJAlwT6YxDSUirw\nsc3bgAArUj1NIsvFBZbtBk0CC9jbNr3MkTV4iNNi2MhTqKTJKmFiT+55oLSE\nOPcyCLzk1VMlLogMiT1Kd2jnpEbblV8k1lAX93ZTHGfA6sVNGrVgmYw3XkMV\nfXfABOO2h/UOs9Nfa++/TsS2TebU9ZRYLS16bNNQbPjNFLqQLofp4iaqHJg8\n5ohnAYgi2lBOi8TfQ4FGX5jvKj4esw1nYiXojH/p4jT2tQWX4epb/TQDqdZ9\ncuVhnj2q13XFjTnhQGPKsEYEb+hB5SH5kKGdHb2Q0oo9SDU7AcBCr3SP96ZV\nhr9T3WlAHWzZrVoSIjjxksllumYKVWJxHLoL79Td0LFHbv4zJpeF50uRcg/H\nlQWxy6Rf7uvQKHpIiasKNEFLorPLGOPu81BPa8h4EAdtGm5or1uH6RhR8Hie\nFqn2Ndo+3x3PHIXcZDS5sAEOeL/+MEu9YN3Tz/NqCPTkkfCRNEHE5IlBUQFd\ncduUT5dwL5UOeWt9+d/aGhVGEz7xEhv0MdURhCiHFafgG4Ava+ES5kkIUWxJ\nZus+1iqSNHY4J5vnzGgwFoDnDwfP2lNzzurclgM5a5Lryr2s8QY5EF3hSbL1\nKlnw\r\n=65cm\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDdR4DVZE4ew/dzaE2kLKE9Gq42Hd5pwD0KAL33218FqAIhANV5q4GeeVmeAA6tA2lirquU6vm7R8JigZRUK1OUSouC"}]},"maintainers":[{"name":"airyrooms-engineering","email":"engineering@airyrooms.com"}],"_npmUser":{"name":"airyrooms-engineering","email":"engineering@airyrooms.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/maleo_0.3.1-canary.20_1563184549218_0.5030927769611386"},"_hasShrinkwrap":false},"0.3.1-canary.21":{"name":"@airy/maleo","version":"0.3.1-canary.21","description":"Un-opinionated Universal Rendering Javascript Framework","repository":{"type":"git","url":"git+https://github.com/airyrooms/maleo.js.git","bugs":"https://github.com/airyrooms/maleo.js/issues"},"author":{"name":"Airy"},"license":"MIT","private":false,"sideEffects":false,"scripts":{"build":"rm -rf dist && gulp","watch":"gulp watch","prepublish":"yarn build","start":"node server.js"},"bin":{"maleo":"lib/bin/maleo.js"},"dependencies":{"@babel/core":"7.1.2","@babel/plugin-proposal-class-properties":"7.1.0","@babel/plugin-proposal-decorators":"7.1.2","@babel/plugin-proposal-object-rest-spread":"7.0.0","@babel/plugin-syntax-dynamic-import":"7.0.0","@babel/plugin-transform-runtime":"7.1.0","@babel/preset-env":"7.1.0","@babel/preset-react":"7.0.0","@babel/runtime":"7.1.2","@babel/runtime-corejs2":"7.2.0","babel-loader":"8.0.4","case-sensitive-paths-webpack-plugin":"2.1.2","compression":"1.7.4","es6-promise":"4.2.5","eventsource":"1.0.7","express":"4.16.4","figlet":"1.2.1","flatted":"^2.0.1","friendly-errors-webpack-plugin":"1.7.0","hard-source-webpack-plugin":"0.13.1","helmet":"3.14.0","hoist-non-react-statics":"^3.3.0","isomorphic-fetch":"2.2.1","loader-utils":"1.1.0","node-notifier":"5.2.1","nodemon":"1.18.4","react-dom":"16.8.4","react-helmet":"5.2.0","react-loadable":"5.5.0","react-router-config":"5.0.0","react-router-dom":"5.0.0","rimraf":"2.6.3","source-map-support":"0.5.9","terser-webpack-plugin":"1.1.0","webpack":"4.29.0","webpack-bundle-analyzer":"3.0.3","webpack-cli":"3.1.2","webpack-dev-middleware":"3.4.0","webpack-hot-middleware":"2.24.3","webpackbar":"2.6.3"},"peerDependencies":{"react":"^16.8.4"},"devDependencies":{"@babel/preset-typescript":"^7.1.0","@types/webpack-bundle-analyzer":"^2.13.0","babel-plugin-module-resolver":"^3.1.1","gulp":"github:gulpjs/gulp#4.0","gulp-babel":"^8.0.0","gulp-clean":"^0.4.0","gulp-sourcemaps":"^2.6.4","gulp-uglify":"^3.0.1","gulp-watch":"^5.0.1","react":"^16.8.4"},"gitHead":"b9f5a7568fd7f60a226af9d3747bf37aee3797c0","readme":"[![Build Status](https://travis-ci.org/airyrooms/maleo.js.svg?branch=master)](https://travis-ci.org/airyrooms/maleo.js)\n[![Coverage Status](https://coveralls.io/repos/github/airyrooms/maleo.js/badge.svg?branch=master)](https://coveralls.io/github/airyrooms/maleo.js?branch=master)\n[![npm version](https://badge.fury.io/js/%40airy%2Fmaleo.svg)](https://badge.fury.io/js/%40airy%2Fmaleo)\n[![Issues](http://img.shields.io/github/issues/airyrooms/maleo.js.svg)]( https://github.com/airyrooms/maleo.js/issues)\n[![MIT license](https://img.shields.io/badge/License-MIT-blue.svg)](https://github.com/airyrooms/maleo.js/blob/canary/LICENSE)\n[![Discord Chat](https://img.shields.io/discord/550498214789513218.svg)](https://discord.gg/9eArCQn)\n[![Conventional Commits](https://img.shields.io/badge/Conventional%20Commits-1.0.0-green.svg)](https://conventionalcommits.org)\n\n\n# Welcome to Maleo.js\n\nMaleo.js is an un-opinionated framework to enable Universal Rendering in JavaScript using React with no hassle.\n\nWe are here to solve the time consuming setups Universal Rendering Required.\n\n---\n\nReadme below is the documentation for the `canary` (prerelease) branch. To view the documentation for the latest stable Maleo.js version change branch to `master`\n\n---\n\n# Table of Contents\n- [Features](#features)\n- [Setup](#setup)\n- [Component Lifecycle](#component-lifecycle)\n- [Routing](#routing)\n- [Dynamic Import Component](#dynamic-import-component)\n  - [Tips](#tips)\n  - [Preloading](#preloading)\n- [Customizable Component](#customizable-component)\n  - [Custom Document](#custom-document)\n  - [Custom Wrap](#custom-wrap)\n- [Custom Configuration](#custom-configuration)\n  - [Customize Server](#customize-server)\n  - [Customize Webpack](#customize-webpack)\n  - [Customize Babel Config](#customize-babel-config)\n- [Core Utilities Functions](#core-utilities-functions)\n- [CDN Support](#cdn-support)\n- [Plugins](#plugins)\n- [FAQ](#faq)\n- [Contributing](#contributing)\n\n---\n\n## Features\n- Universal Rendering\n- Plugin based framework\n- Customizable\n\n## Setup\n\nInstall Maleo.js \n\n>*for now change `@airy/maleo` to `@airy/maleo@canary` until we publish the stable version of this package*\n\n\n**NPM**\n```bash\n$ npm install --save @airy/maleo react\n```\n**Yarn**\n```bash\n$ yarn add @airy/maleo react\n```\n\nAdd this script to your `package.json`\n```json\n{\n  \"scripts\": {\n    \"dev\": \"maleo dev\",\n    \"build\": \"export NODE_ENV=production && maleo build\",\n    \"start\": \"export NODE_ENV=production && maleo run\"\n  }\n}\n```\n\nCreate a page Root component\n```jsx\n// ./src/Root.jsx\nimport React from 'react';\n\n// Export default is required for registering page\nexport default class RootComponent extends React.Component {\n  render() {\n    return (\n      <h1>Hello World!</h1>\n    )\n  }\n}\n```\n\nAnd lastly, create a routing file on your project root directory called `routes.json` and register your page component\n```json\n[\n  {\n    \"path\": \"/\",\n    \"page\": \"./src/Root\"\n  }\n]\n```\n\nAfter that you can now run `$ npm run dev` and go to `http://localhost:3000`.\n\nYou should now see your app running on your browser.\n\nBy now you should see \n- Automatic transpilation and bundling (with webpack and babel)\n- ~~Hot code reloading~~ [#17](https://github.com/airyrooms/maleo.js/issues/17)\n- Server rendering\n\nTo see how simple this is, check out the sample app!\n\n## Component Lifecycle \n\nMaleo.js added a new component lifecycle hook called `getInitialProps`, this function is called during Server Side Rendering (SSR) and during route changes on Client Side Rendering (CSR).\n\nThis is useful especially for SEO purposes. \n\nExample for stateful component:\n```jsx\nimport React from 'react';\n\nexport default class extends React.Component {\n  static getInitialProps = async (ctx) => {\n    const { req } = ctx;\n\n    // check if getInitialProps is called on server or on client\n    const userAgent = req ? req.headers['user-agent'] : navigator.userAgent;\n\n    // the return value will be passed as props for this component\n    return { userAgent };\n  }\n\n  render() {\n    return (\n      <div>\n        Hello World {this.props.userAgent}\n      </div>\n    );\n  }\n}\n```\n\nExample for stateless component:\n```jsx\nconst Component = (props) => (\n  <div>\n    Hello World {props.userAgent}\n  </div>\n);\n\nComponent.getInitialprops = async (ctx) => {\n  const { req } = ctx;\n\n  const userAgent = req ? req.headers['user-agent'] : navigator.userAgent;\n\n  return { userAgent };\n};\n\nexport default Component;\n```\n\n`getInitialProps` receives a context object with the following properties:\n- `req` - HTTP request object (server only)\n- `res` - HTTP response object (server only)\n- `...wrapProps` - Spreaded properties from custom Wrap\n- `...appProps` - Spreaded properties from custom App\n\n## Routing\n\nRouting is declared in a centralized route config.\nRegister all the route config in `routes.json` file.\n\nIf you put the `routes.json` files on root directory, Maleo will automatically register your route. Otherwise put path to your routes on [Maleo config](#custom-configuration).\n\nRoutes file has to export default the route configuration.\nThe route object **expected to have distinct key** to indicate the route.\n\n<table>\n  <tr>\n    <td>Key</td>\n    <td>Type</td>\n    <td>Description</td>\n  </tr>\n  <tr>\n    <td><code>path</code></td>\n    <td><code>String!</code></td>\n    <td>Routes path</td>\n  </tr>\n  <tr>\n    <td><code>page</code></td>\n    <td><code>String!</code></td>\n    <td>Path to React Component for this route path</td>\n  </tr>\n  <tr>\n    <td><code>exact</code></td>\n    <td><code>Boolean?</code> [<code>false</code>]</td>\n    <td>To make url has to match exactly the path in order to render the component. Give <code>false</code> value if the route is a wrapper route component</td>\n  </tr>\n  <tr>\n    <td><code>routes</code></td>\n    <td><code>RouteObject?</code></td>\n    <td>Nested route</td>\n  </tr>\n</table>\n\n\nFor example:\n```json\n[\n  {\n    \"page\": \"./src/MainApp\",\n    \"routes\": [\n      {\n        \"path\": \"/\",\n        \"page\": \"./src/Search\",\n        \"exact\": true\n      },\n      {\n        \"path\": \"/search\",\n        \"page\": \"./src/Search\",\n        \"routes\": [\n          {\n            \"path\": \"/search/hello\",\n            \"page\": \"./src/Detail\",\n            \"exact\": true\n          }\n        ]\n      },\n      {\n        \"path\": \"/detail\",\n        \"page\": \"./src/Detail\",\n        \"exact\": true\n      }\n    ]\n  }\n]\n```\n\n## Dynamic Import Component\nMaleo.js supports TC39 [dynamic import proposal](https://github.com/tc39/proposal-dynamic-import) for JavaScript.\n\nYou can think dynamic import as another way to split your code into manageable chunks. You can use our `Dynamic` function which utilizes [react loadable](https://github.com/jamiebuilds/react-loadable)\n\nFor Example\n```js\n// DynamicComponent.js\nimport Dynamic from '@airy/maleo/dynamic';\n\nexport default Dynamic({\n  loader: () => import( /* webpackChunkName:\"DynamicComponent\" */ './component/DynamicLoad'),\n})\n```\n\n### **Preloading**\n\nFor optimization purposes, you can also preload a component even before the component got rendered.\n\nFor example, if you want to load component when a button get pressed, you can start preloading the component when the user hovers the mouse over the button.\n\nThe component created by `Dynamic` exposes a [static `preload` method](https://github.com/jamiebuilds/react-loadable#loadablecomponentpreload).\n\n```jsx\nimport React from 'react';\nimport Dynamic from '@airy/maleo/dynamic';\n\nconst DynamicBar = Dynamic({\n  loader: () => import('./Bar'),\n  loading: LoadingCompoinent\n});\n\nclass MyComponent extends React.Component {\n  state = { showBar: false };\n\n  onClick = () => {\n    this.setState({ showBar: true });\n  };\n\n  onMouseOver = () => DynamicBar.preload();\n\n  render() {\n    return (\n      <div>\n        <button\n          onClick={this.onClick}\n          onMouseOver={this.onMouseOver}>\n          Show Bar\n        </button>\n        { this.state.showBar && <DynamicBar /> }\n      </div>\n    )\n  }\n}\n```\n\n## Customizable Component\n\n### Custom Document\n\nHighly inspired by what [Next.js](https://github.com/zeit/next.js) has done on their awesome template customization.\n\nMaleo.js also enable customization on `Document` as document's markup. So you don't need to include tags like `<html>`, `<head>`, `<body>`, etc. \n\nTo override the default behavior, you'll need to create a component that extends the `Document` React class provided by Maleo.\n\n```jsx\n// document.jsx\nimport React from 'react';\nimport { Document, Header, Main, Scripts } from '@airy/maleo/document';\n\nexport default class extends Document {\n  render() {\n    return (\n      <html>\n        <Header>\n          <title>Maleo JS</title>\n          <meta charset=\"utf-8\" />\n          <meta name=\"description\" content=\"Maleo.js is awesome!\" />\n\n          <style>\n            {` body { background-color: #fff } `}\n          </style>\n        </Header>\n\n        <body>\n          <Main />\n\n          <Scripts />\n\n          <ReduxScript />\n        </body>\n      </html>\n    );\n  }\n}\n```\n\n### Custom Wrap\n\nMaleo.js uses the `Wrap` component to initialize pages. `Wrap` contains React Router's Component. You can add HoC here to wrap the application and control the page initialization. Which allows you to do amazing things like:\n- Persisting layour between page changes\n- Keeping state when navigating pages\n- Custom error handling using `componentDidCatch`\n- Inject additional data into pages (like Redux provider, etc)\n\nTo override the default behavior, you'll need to create a component that extends the `Wrap` React class provided by Maleo.\n\n```jsx\n// wrap.jsx\nimport React from 'react';\nimport { Wrap } from '@airy/maleo/wrap';\n\n// Redux plugin for Maleo.js\n// Hoc that creates store and add Redux Provider\nimport { withRedux } from '@airy/maleo-redux-plugin';\n\n// Custom Wrapper that will be rendered for the whole Application\nimport Content from './component/Content';\nimport NavigationBar from './component/NavigationBar';\n\nimport { createStore } from './store';\n\n@withRedux(createStore)\nexport default class extends Wrap {\n  static getInitialProps = (ctx) => {\n    const { store } = ctx\n    // you receive store from context\n    // you can access or do something with the store here\n    console.log('Initialized Redux Store', store);\n    return {}\n  }\n\n  render() {\n    const { Container, containerProps, App, appProps } = this.props\n\n    return (\n      <Container {...containerProps}>\n        <NavigationBar />\n        <Content>\n          <App {...appProps}/>\n        </Content>\n      </Container>\n    )\n  }\n}\n\n```\nIf you put `document.jsx` and `wrap.jsx` on root directory (the same level with `package.json`), then Maleo will automatically register your custom Document and Wrap. Otherwise, you can add the path to your custom Document and Wrap on [Maleo config](#custom-configuration)\n\n---\n***We are also working on adding default and customizable `Error` component page***\n\n---\n\n## Custom Configuration\n\nFor more advanced configuration of Maleo.js, like `webpack` config, registering `plugins`, path to your routes, custom Document and Wrap, and adding `path alias`, you can create a `maleo.config.js` in the root of your project directory. (same directory with `package.json`)\n\n```js\n// maleo.config.js\n\nmodule.exports = {\n  /* config options here */\n}\n```\n\nHere are the API's for the configuration:\n\n<table>\n  <tr>\n    <td>Key</td>\n    <td>Type</td>\n    <td>Description</td>\n  </tr>\n  <tr>\n    <td><code>favicon</code></td>\n    <td><code>String</code> [<code>{project-dir}/favicon.ico</code>]</td>\n    <td>Path to favicon file</td>\n  </tr>\n  <tr>\n    <td><code>buildDir</code></td>\n    <td><code>String?</code> [<code>.maleo</code>]</td>\n    <td>Directory to put Maleo.js' build assets</td>\n  </tr>\n  <tr>\n    <td><code>cache</code></td>\n    <td><code>Boolean?</code> [<code>true</code>]</td>\n    <td>Enable webpack build caching</td>\n  </tr>\n  <tr>\n    <td><code>isDev</code></td>\n    <td><code>Boolean?</code> [<code>process.env.NODE_ENV === 'development'</code>]</td>\n    <td>Enable development build configuration</td>\n  </tr>\n  <tr>\n    <td><code>sourceMaps</code></td>\n    <td><code>Boolean?</code> [<code>true</code>]</td>\n    <td>Enable webpack to generate source maps</td>\n  </tr>\n  <tr>\n    <td><code>alias</code></td>\n    <td><code>Object?</code></td>\n    <td>A key value pair for aliasing path directory\n      <br>\n      <code>\n      { 'component': './src/component' }\n      </code>\n    </td>\n  </tr>\n  <tr>\n    <td><code>publicPath</code></td>\n    <td><code>String?</code> [<code>/_assets/</code>]</td>\n    <td>To customize webpack's <code>publicPath</code> \n    <br>Comes in handy if using CDN to put built assets</td>\n  </tr>\n  <tr>\n    <td><code>analyzeBundle</code></td>\n    <td><code>Boolean?</code> [<code>false</code>]</td>\n    <td>To enable webpack's bundle analyzer, for analyzing bundle sizes during bundle debugging should Maleo.js' build process got slow</td>\n  </tr>\n  <tr>\n    <td><code>webpack</code></td>\n    <td><code>Function?</code></td>\n    <td>To customize webpack configuration, more details <a href=\"#customize-webpack\">here</a></td>\n  </tr>\n  <tr>\n    <td><code>routes</code></td>\n    <td><code>string?</code> [<code>rootDir/routes.jsx</code>]</td>\n    <td>Path to your routes file</td>\n  </tr>\n  <tr>\n    <td><code>customDocument</code></td>\n    <td><code>string?</code> [<code>rootDir/document.jsx</code>]</td>\n    <td>Path to your custom document file</td>\n  </tr>\n  <tr>\n    <td><code>customWrap</code></td>\n    <td><code>string?</code> [<code>rootDir/wrap.jsx</code>]</td>\n    <td>Path to your custom wrap file</td>\n  </tr>\n  <tr>\n    <td><code>customApp</code></td>\n    <td><code>string?</code> [<code>rootDir/app.jsx</code>]</td>\n    <td>Path to your custom app file</td>\n  </tr>\n  <tr>\n    <td><code>csp</code></td>\n    <td><code>boolean | cspConfig</code> [<code>false</code>]</td>\n    <td>Config for <a href=\"https://developers.google.com/web/fundamentals/security/csp/\">Content Security Policy</a>, using <a href=\"https://helmetjs.github.io/docs/csp/\">Helmet-CSP</a>\n    <br/>\n    If set to true it will enable default config:\n    <br/>\n    <code>{\n      directives: {\n        defaultSrc: [`'self'`],\n        styleSrc: [`'self'`],\n      },\n    }</code>\n    </td>\n  </tr>\n</table>\n\n#### Customize Server\n\nCreate a `server.js` file on root directory where your `package.json` lives.\nHere you can customize Maleo's server.\n```js\nimport { Server } from '@airy/maleo/server';\n\nconst PORT = process.env.PORT || 3000;\n\nconst maleoServer = Server.init({\n  port: PORT,\n  runHandler: () => {\n    console.log('Server running on port :', PORT);\n  }\n});\n\nmaleoServer.run();\n```\n\nHere are the API's for the configuration:\n\n<table>\n  <tr>\n    <td>Key</td>\n    <td>Type</td>\n    <td>Description</td>\n  </tr>\n  <tr>\n    <td><code>port</code></td>\n    <td><code>Number?</code> [<code>3000</code>]</td>\n    <td>Port to run Maleo server</td>\n  </tr>\n  <tr>\n    <td><code>assetDir</code></td>\n    <td><code>String?</code> [<code>'<root>/.maleo/client'</code>]</td>\n    <td>Directory for all client related assets</td>\n  </tr>\n  <tr>\n    <td><code>runHandler</code></td>\n    <td><code>Function?</code></td>\n    <td>Function called when maleo server starter</td>\n  </tr>\n</table>\n\n#### Customize Webpack\n\nYou are able to extend Maleo.js' default webpack configuration by defining a function on `maleo.config.js`\n\n```js\n// maleo.config.js\n\nmodule.exports = {\n  webpack(config, context, next) {\n    // Perform customizations to webpack config\n    // Important: This call is required in order to let Maleo pass the config to webpack\n    return next(); \n  },\n};\n```\n\nWebpack function will receive three arguments:\n\n<table>\n  <tr>\n    <td>Argument</td>\n    <td>Details</td>\n  <tr>\n  <tr>\n    <td><code>config</code></td>\n    <td>This contains webpack configuration object that you can manipulate</td>\n  <tr>\n  <tr>\n    <td><code>context</code></td>\n    <td>This contains some keys that are useful for the build context\n      <br>\n      <table>\n        <tr>\n          <td><code>isDev</code></td>\n          <td><code>Boolean</code></td>\n          <td>Check if current build is for development</td>\n        </tr>\n        <tr>\n          <td><code>publicPath</code></td>\n          <td><code>String</code></td>\n          <td>Public Path of user defined or default's value</td>\n        </tr>\n        <tr>\n          <td><code>analyzeBundle</code></td>\n          <td><code>Boolean</code></td>\n          <td>Check if analyze bundle is enabled</td>\n        </tr>\n        <tr>\n          <td><code>buildDirectory</code></td>\n          <td><code>String</code></td>\n          <td>Build Directory of user defined or default's value</td>\n        </tr>\n        <tr>\n          <td><code>name</code></td>\n          <td><code>String</code></td>\n          <td>Build name <code>'server' || 'client'</code></td>\n        </tr>\n      </table>\n    </td>\n  <tr>\n  <tr>\n    <td><code>next</code></td>\n    <td>A callback required to be called to pass the custom configuration</td>\n  <tr>\n</table>\n\nExample of adding `ts-loader` through `maleo.config.js`:\n\n```js\n// maleo.config.js\n\n// Partly taken and modified from @airy/maleo-ts-plugin source\n// for simplicity purposes\n\nmodule.exports = {\n  webpack(config, context, next) {\n    const { isDev } = context\n\n    config.module.rules.push({\n      test: /\\.tsx?/,\n      exclude: /node_modules/,\n      use: [\n        require.resolve('@airy/maleo/lib/build/loaders/maleo-babel-loader'),\n        {\n          loader: 'ts-loader',\n          options: {\n            transpileOnly: true,\n          },\n        },\n      ],\n\n      if (isDev) {\n        config.plugins.push(new ForkTSCheckerWebpackPlugin());\n      }\n\n      return next();\n    })\n  },\n};\n```\n\n#### Customize Babel Config\n\nMaleo.js also let you have your own babel config. Just simply add `.babelrc` file at the root directory of your app.\n\nYou can include Maleo.js' babel preset in order to have latest JavaScript preset.\n\nHere's an example of `.babelrc` file:\n```json\n{\n  \"presets\": [\"@airy/maleo/babel\"],\n  \"plugins\": []\n}\n```\n\nThe `@airy/maleo/babel` preset includes everything you need to get your development started. The preset includes:\n- `@babel/preset-env`\n- `@babel/preset-react`\n- `@babel/plugin-proposal-class-properties`\n- `@babel/plugin-proposal-decorators`\n- `@babel/plugin-proposal-object-rest-spread`\n- `@babel/plugin-transform-runtime`\n- `react-loadable/babel`\n\n## Core Utilities Functions\n\n#### Route Matching\n\nTo use you can import the matching routes function like this:\n```js\nimport { getMatchedRoutes } from '@airy/maleo/utils';\n```\n\nThe function will return all matched routes and the route object from `routes.json`\n\n## CDN Support\n\nIf you are using a CDN, you can set up the `publicPath` setting and configure your CDN's origin to resolve to the domain that Maleo.js is hosted on.\n\n```js\n// maleo.config.js\n\nconst isProd = process.env.NODE_ENV === 'production';\n\nmodule.exports = {\n  assetPrefix: isProd && 'https://cdn.example.com';\n}\n```\n\n## FAQ\n\n== TO BE DETERMINED == \n\n## Plugins\n- [css-plugin](https://github.com/airyrooms/maleo-plugins/tree/master/packages/css-plugin)\n- [redux-plugin](https://github.com/airyrooms/maleo-plugins/tree/master/packages/redux-plugin)\n- [typescript-plugin](https://github.com/airyrooms/maleo-plugins/tree/master/packages/typescript-plugin)\n- [more](https://github.com/airyrooms/maleo-plugins)\n\n\n## Contributing\n[Please follow these steps to contribute to Maleo.js](https://github.com/airyrooms/maleo.js/blob/canary/CONTRIBUTING.md)\n\n[Please follow these steps to contribute to Maleo.js' plugins](https://github.com/airyrooms/maleo-plugins#contributing-guidelines)\n\n\n## Contributors\n\nThis project exists thanks to all the people who contribute. [Contribute](CONTRIBUTING.md).\n\nMany thanks to our **[contributors](https://github.com/airyrooms/maleo.js/graphs/contributors)**!\n\n\n## License\n\nMIT","readmeFilename":"README.md","bugs":{"url":"https://github.com/airyrooms/maleo.js/issues"},"homepage":"https://github.com/airyrooms/maleo.js#readme","_id":"@airy/maleo@0.3.1-canary.21","_nodeVersion":"10.16.0","_npmVersion":"lerna/3.13.1/node@v10.16.0+x64 (linux)","dist":{"integrity":"sha512-lolj86LXmnp2j3i/ddEgmt1jK9kFT9U7/Qb+3LnQLQazjjjOlKwABcJzhR8d7cQmEXIRM6gLukRCj3ZR38X/ww==","shasum":"ed12953a3ce87ccfd1cb661c5a964d9104023b07","tarball":"https://registry.npmjs.org/@airy/maleo/-/maleo-0.3.1-canary.21.tgz","fileCount":96,"unpackedSize":337235,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJdLFDtCRA9TVsSAnZWagAASOAP/iam0i34E6bGxqTxWlgR\nfU95FhNUVJ6WYNVsRVeL0G/Ac4mtPYKh+9DKWz9/mrk82bToyF4OSEt2+C3u\nfzEHT3EAGmSqHW7cSJ3uh85Zp9G4Oo3mcxMxx4ZFHyI6xV2xNodRyHnhSsQw\n13F03aaf6G/mtONJpq0x5EFlnVaxr0eKA6CkNMVDBwwshNdM7uVVIxpop7cH\nPZqe5hXWpC7DvanGuIoo+a6G4OWbgbU/QUOS1FfcPb4eXDS31C86FW8RfYMV\n7SOMej31nZXjK2t6mmS4phRAugWDcc67xlx4eVcgRSGZQrpfXwplvrcu78Wu\nddW4KvYdSqHS7FveUB4TGLenEazc+TEM4OroNkU6xzPKFS7zeVjJgeaN7v5A\nzrfLYoxSEGS+/l6VNxWDcw6l+uCTYfgH8fI7F6cqd7UIxvto2L4CKLoLxz/8\nJ8637kdgS6lx43s5p0bEX2bNKgZXbRjWrV4BHa8caFr5BU1tRPfkjy1xw49G\nzeyeAIQLt6VObTAtueUapdSV8dH+9q/uljZ+0arXnLNakCm5WTXp7q7pr6SG\n0MnAIIxVHNX8DftGxRN6gY3vXsC4gS4Svz/rsLQ8BK8MRRrE7wcw6KGDKvdP\nKSYf0ULtur18RjPXX1Gf5SehyahwWTFeftsfaqxrZA0x0V4yDcmuZ1sN35a4\nUaMT\r\n=8nPz\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCH29+FKM7N4uu+OxpHjIRQ5Jfv0uX5VqiAoGG6hVWQjAIhALDmf1UTyNfS79Y8FSlkVC7X/zYdJ7/5fPoqVRaCWZDc"}]},"maintainers":[{"name":"airyrooms-engineering","email":"engineering@airyrooms.com"}],"_npmUser":{"name":"airyrooms-engineering","email":"engineering@airyrooms.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/maleo_0.3.1-canary.21_1563185388895_0.287215092296347"},"_hasShrinkwrap":false},"0.3.1-canary.22":{"name":"@airy/maleo","version":"0.3.1-canary.22","description":"Un-opinionated Universal Rendering Javascript Framework","repository":{"type":"git","url":"git+https://github.com/airyrooms/maleo.js.git","bugs":"https://github.com/airyrooms/maleo.js/issues"},"author":{"name":"Airy"},"license":"MIT","private":false,"sideEffects":false,"scripts":{"build":"rm -rf dist && gulp","watch":"gulp watch","prepublish":"yarn build","start":"node server.js"},"bin":{"maleo":"lib/bin/maleo.js"},"dependencies":{"@babel/core":"7.1.2","@babel/plugin-proposal-class-properties":"7.1.0","@babel/plugin-proposal-decorators":"7.1.2","@babel/plugin-proposal-object-rest-spread":"7.0.0","@babel/plugin-syntax-dynamic-import":"7.0.0","@babel/plugin-transform-runtime":"7.1.0","@babel/preset-env":"7.1.0","@babel/preset-react":"7.0.0","@babel/runtime":"7.1.2","@babel/runtime-corejs2":"7.2.0","babel-loader":"8.0.4","case-sensitive-paths-webpack-plugin":"2.1.2","compression":"1.7.4","es6-promise":"4.2.5","eventsource":"1.0.7","express":"4.16.4","figlet":"1.2.1","flatted":"^2.0.1","friendly-errors-webpack-plugin":"1.7.0","hard-source-webpack-plugin":"0.13.1","helmet":"3.14.0","hoist-non-react-statics":"^3.3.0","isomorphic-fetch":"2.2.1","loader-utils":"1.1.0","node-notifier":"5.2.1","nodemon":"1.18.4","react-dom":"16.8.4","react-helmet":"5.2.0","react-loadable":"5.5.0","react-router-config":"5.0.0","react-router-dom":"5.0.0","rimraf":"2.6.3","source-map-support":"0.5.9","terser-webpack-plugin":"1.1.0","webpack":"4.29.0","webpack-bundle-analyzer":"3.0.3","webpack-cli":"3.1.2","webpack-dev-middleware":"3.4.0","webpack-hot-middleware":"2.24.3","webpackbar":"2.6.3"},"peerDependencies":{"react":"^16.8.4"},"devDependencies":{"@babel/preset-typescript":"^7.1.0","@types/webpack-bundle-analyzer":"^2.13.0","babel-plugin-module-resolver":"^3.1.1","gulp":"github:gulpjs/gulp#4.0","gulp-babel":"^8.0.0","gulp-clean":"^0.4.0","gulp-sourcemaps":"^2.6.4","gulp-uglify":"^3.0.1","gulp-watch":"^5.0.1","react":"^16.8.4"},"gitHead":"b6ba1d58a4a21f4db0b54b767c77854ab722be37","readme":"[![Build Status](https://travis-ci.org/airyrooms/maleo.js.svg?branch=master)](https://travis-ci.org/airyrooms/maleo.js)\n[![Coverage Status](https://coveralls.io/repos/github/airyrooms/maleo.js/badge.svg?branch=master)](https://coveralls.io/github/airyrooms/maleo.js?branch=master)\n[![npm version](https://badge.fury.io/js/%40airy%2Fmaleo.svg)](https://badge.fury.io/js/%40airy%2Fmaleo)\n[![Issues](http://img.shields.io/github/issues/airyrooms/maleo.js.svg)]( https://github.com/airyrooms/maleo.js/issues)\n[![MIT license](https://img.shields.io/badge/License-MIT-blue.svg)](https://github.com/airyrooms/maleo.js/blob/canary/LICENSE)\n[![Discord Chat](https://img.shields.io/discord/550498214789513218.svg)](https://discord.gg/9eArCQn)\n[![Conventional Commits](https://img.shields.io/badge/Conventional%20Commits-1.0.0-green.svg)](https://conventionalcommits.org)\n\n\n# Welcome to Maleo.js\n\nMaleo.js is an un-opinionated framework to enable Universal Rendering in JavaScript using React with no hassle.\n\nWe are here to solve the time consuming setups Universal Rendering Required.\n\n---\n\nReadme below is the documentation for the `canary` (prerelease) branch. To view the documentation for the latest stable Maleo.js version change branch to `master`\n\n---\n\n# Table of Contents\n- [Features](#features)\n- [Setup](#setup)\n- [Component Lifecycle](#component-lifecycle)\n- [Routing](#routing)\n- [Dynamic Import Component](#dynamic-import-component)\n  - [Tips](#tips)\n  - [Preloading](#preloading)\n- [Customizable Component](#customizable-component)\n  - [Custom Document](#custom-document)\n  - [Custom Wrap](#custom-wrap)\n- [Custom Configuration](#custom-configuration)\n  - [Customize Server](#customize-server)\n  - [Customize Webpack](#customize-webpack)\n  - [Customize Babel Config](#customize-babel-config)\n- [Core Utilities Functions](#core-utilities-functions)\n- [CDN Support](#cdn-support)\n- [Plugins](#plugins)\n- [FAQ](#faq)\n- [Contributing](#contributing)\n\n---\n\n## Features\n- Universal Rendering\n- Plugin based framework\n- Customizable\n\n## Setup\n\nInstall Maleo.js \n\n>*for now change `@airy/maleo` to `@airy/maleo@canary` until we publish the stable version of this package*\n\n\n**NPM**\n```bash\n$ npm install --save @airy/maleo react\n```\n**Yarn**\n```bash\n$ yarn add @airy/maleo react\n```\n\nAdd this script to your `package.json`\n```json\n{\n  \"scripts\": {\n    \"dev\": \"maleo dev\",\n    \"build\": \"export NODE_ENV=production && maleo build\",\n    \"start\": \"export NODE_ENV=production && maleo run\"\n  }\n}\n```\n\nCreate a page Root component\n```jsx\n// ./src/Root.jsx\nimport React from 'react';\n\n// Export default is required for registering page\nexport default class RootComponent extends React.Component {\n  render() {\n    return (\n      <h1>Hello World!</h1>\n    )\n  }\n}\n```\n\nAnd lastly, create a routing file on your project root directory called `routes.json` and register your page component\n```json\n[\n  {\n    \"path\": \"/\",\n    \"page\": \"./src/Root\"\n  }\n]\n```\n\nAfter that you can now run `$ npm run dev` and go to `http://localhost:3000`.\n\nYou should now see your app running on your browser.\n\nBy now you should see \n- Automatic transpilation and bundling (with webpack and babel)\n- ~~Hot code reloading~~ [#17](https://github.com/airyrooms/maleo.js/issues/17)\n- Server rendering\n\nTo see how simple this is, check out the sample app!\n\n## Component Lifecycle \n\nMaleo.js added a new component lifecycle hook called `getInitialProps`, this function is called during Server Side Rendering (SSR) and during route changes on Client Side Rendering (CSR).\n\nThis is useful especially for SEO purposes. \n\nExample for stateful component:\n```jsx\nimport React from 'react';\n\nexport default class extends React.Component {\n  static getInitialProps = async (ctx) => {\n    const { req } = ctx;\n\n    // check if getInitialProps is called on server or on client\n    const userAgent = req ? req.headers['user-agent'] : navigator.userAgent;\n\n    // the return value will be passed as props for this component\n    return { userAgent };\n  }\n\n  render() {\n    return (\n      <div>\n        Hello World {this.props.userAgent}\n      </div>\n    );\n  }\n}\n```\n\nExample for stateless component:\n```jsx\nconst Component = (props) => (\n  <div>\n    Hello World {props.userAgent}\n  </div>\n);\n\nComponent.getInitialprops = async (ctx) => {\n  const { req } = ctx;\n\n  const userAgent = req ? req.headers['user-agent'] : navigator.userAgent;\n\n  return { userAgent };\n};\n\nexport default Component;\n```\n\n`getInitialProps` receives a context object with the following properties:\n- `req` - HTTP request object (server only)\n- `res` - HTTP response object (server only)\n- `...wrapProps` - Spreaded properties from custom Wrap\n- `...appProps` - Spreaded properties from custom App\n\n## Routing\n\nRouting is declared in a centralized route config.\nRegister all the route config in `routes.json` file.\n\nIf you put the `routes.json` files on root directory, Maleo will automatically register your route. Otherwise put path to your routes on [Maleo config](#custom-configuration).\n\nRoutes file has to export default the route configuration.\nThe route object **expected to have distinct key** to indicate the route.\n\n<table>\n  <tr>\n    <td>Key</td>\n    <td>Type</td>\n    <td>Description</td>\n  </tr>\n  <tr>\n    <td><code>path</code></td>\n    <td><code>String!</code></td>\n    <td>Routes path</td>\n  </tr>\n  <tr>\n    <td><code>page</code></td>\n    <td><code>String!</code></td>\n    <td>Path to React Component for this route path</td>\n  </tr>\n  <tr>\n    <td><code>exact</code></td>\n    <td><code>Boolean?</code> [<code>false</code>]</td>\n    <td>To make url has to match exactly the path in order to render the component. Give <code>false</code> value if the route is a wrapper route component</td>\n  </tr>\n  <tr>\n    <td><code>routes</code></td>\n    <td><code>RouteObject?</code></td>\n    <td>Nested route</td>\n  </tr>\n</table>\n\n\nFor example:\n```json\n[\n  {\n    \"page\": \"./src/MainApp\",\n    \"routes\": [\n      {\n        \"path\": \"/\",\n        \"page\": \"./src/Search\",\n        \"exact\": true\n      },\n      {\n        \"path\": \"/search\",\n        \"page\": \"./src/Search\",\n        \"routes\": [\n          {\n            \"path\": \"/search/hello\",\n            \"page\": \"./src/Detail\",\n            \"exact\": true\n          }\n        ]\n      },\n      {\n        \"path\": \"/detail\",\n        \"page\": \"./src/Detail\",\n        \"exact\": true\n      }\n    ]\n  }\n]\n```\n\n## Dynamic Import Component\nMaleo.js supports TC39 [dynamic import proposal](https://github.com/tc39/proposal-dynamic-import) for JavaScript.\n\nYou can think dynamic import as another way to split your code into manageable chunks. You can use our `Dynamic` function which utilizes [react loadable](https://github.com/jamiebuilds/react-loadable)\n\nFor Example\n```js\n// DynamicComponent.js\nimport Dynamic from '@airy/maleo/dynamic';\n\nexport default Dynamic({\n  loader: () => import( /* webpackChunkName:\"DynamicComponent\" */ './component/DynamicLoad'),\n})\n```\n\n### **Preloading**\n\nFor optimization purposes, you can also preload a component even before the component got rendered.\n\nFor example, if you want to load component when a button get pressed, you can start preloading the component when the user hovers the mouse over the button.\n\nThe component created by `Dynamic` exposes a [static `preload` method](https://github.com/jamiebuilds/react-loadable#loadablecomponentpreload).\n\n```jsx\nimport React from 'react';\nimport Dynamic from '@airy/maleo/dynamic';\n\nconst DynamicBar = Dynamic({\n  loader: () => import('./Bar'),\n  loading: LoadingCompoinent\n});\n\nclass MyComponent extends React.Component {\n  state = { showBar: false };\n\n  onClick = () => {\n    this.setState({ showBar: true });\n  };\n\n  onMouseOver = () => DynamicBar.preload();\n\n  render() {\n    return (\n      <div>\n        <button\n          onClick={this.onClick}\n          onMouseOver={this.onMouseOver}>\n          Show Bar\n        </button>\n        { this.state.showBar && <DynamicBar /> }\n      </div>\n    )\n  }\n}\n```\n\n## Customizable Component\n\n### Custom Document\n\nHighly inspired by what [Next.js](https://github.com/zeit/next.js) has done on their awesome template customization.\n\nMaleo.js also enable customization on `Document` as document's markup. So you don't need to include tags like `<html>`, `<head>`, `<body>`, etc. \n\nTo override the default behavior, you'll need to create a component that extends the `Document` React class provided by Maleo.\n\n```jsx\n// document.jsx\nimport React from 'react';\nimport { Document, Header, Main, Scripts } from '@airy/maleo/document';\n\nexport default class extends Document {\n  render() {\n    return (\n      <html>\n        <Header>\n          <title>Maleo JS</title>\n          <meta charset=\"utf-8\" />\n          <meta name=\"description\" content=\"Maleo.js is awesome!\" />\n\n          <style>\n            {` body { background-color: #fff } `}\n          </style>\n        </Header>\n\n        <body>\n          <Main />\n\n          <Scripts />\n\n          <ReduxScript />\n        </body>\n      </html>\n    );\n  }\n}\n```\n\n### Custom Wrap\n\nMaleo.js uses the `Wrap` component to initialize pages. `Wrap` contains React Router's Component. You can add HoC here to wrap the application and control the page initialization. Which allows you to do amazing things like:\n- Persisting layour between page changes\n- Keeping state when navigating pages\n- Custom error handling using `componentDidCatch`\n- Inject additional data into pages (like Redux provider, etc)\n\nTo override the default behavior, you'll need to create a component that extends the `Wrap` React class provided by Maleo.\n\n```jsx\n// wrap.jsx\nimport React from 'react';\nimport { Wrap } from '@airy/maleo/wrap';\n\n// Redux plugin for Maleo.js\n// Hoc that creates store and add Redux Provider\nimport { withRedux } from '@airy/maleo-redux-plugin';\n\n// Custom Wrapper that will be rendered for the whole Application\nimport Content from './component/Content';\nimport NavigationBar from './component/NavigationBar';\n\nimport { createStore } from './store';\n\n@withRedux(createStore)\nexport default class extends Wrap {\n  static getInitialProps = (ctx) => {\n    const { store } = ctx\n    // you receive store from context\n    // you can access or do something with the store here\n    console.log('Initialized Redux Store', store);\n    return {}\n  }\n\n  render() {\n    const { Container, containerProps, App, appProps } = this.props\n\n    return (\n      <Container {...containerProps}>\n        <NavigationBar />\n        <Content>\n          <App {...appProps}/>\n        </Content>\n      </Container>\n    )\n  }\n}\n\n```\nIf you put `document.jsx` and `wrap.jsx` on root directory (the same level with `package.json`), then Maleo will automatically register your custom Document and Wrap. Otherwise, you can add the path to your custom Document and Wrap on [Maleo config](#custom-configuration)\n\n---\n***We are also working on adding default and customizable `Error` component page***\n\n---\n\n## Custom Configuration\n\nFor more advanced configuration of Maleo.js, like `webpack` config, registering `plugins`, path to your routes, custom Document and Wrap, and adding `path alias`, you can create a `maleo.config.js` in the root of your project directory. (same directory with `package.json`)\n\n```js\n// maleo.config.js\n\nmodule.exports = {\n  /* config options here */\n}\n```\n\nHere are the API's for the configuration:\n\n<table>\n  <tr>\n    <td>Key</td>\n    <td>Type</td>\n    <td>Description</td>\n  </tr>\n  <tr>\n    <td><code>favicon</code></td>\n    <td><code>String</code> [<code>{project-dir}/favicon.ico</code>]</td>\n    <td>Path to favicon file</td>\n  </tr>\n  <tr>\n    <td><code>buildDir</code></td>\n    <td><code>String?</code> [<code>.maleo</code>]</td>\n    <td>Directory to put Maleo.js' build assets</td>\n  </tr>\n  <tr>\n    <td><code>cache</code></td>\n    <td><code>Boolean?</code> [<code>true</code>]</td>\n    <td>Enable webpack build caching</td>\n  </tr>\n  <tr>\n    <td><code>isDev</code></td>\n    <td><code>Boolean?</code> [<code>process.env.NODE_ENV === 'development'</code>]</td>\n    <td>Enable development build configuration</td>\n  </tr>\n  <tr>\n    <td><code>sourceMaps</code></td>\n    <td><code>Boolean?</code> [<code>true</code>]</td>\n    <td>Enable webpack to generate source maps</td>\n  </tr>\n  <tr>\n    <td><code>alias</code></td>\n    <td><code>Object?</code></td>\n    <td>A key value pair for aliasing path directory\n      <br>\n      <code>\n      { 'component': './src/component' }\n      </code>\n    </td>\n  </tr>\n  <tr>\n    <td><code>publicPath</code></td>\n    <td><code>String?</code> [<code>/_assets/</code>]</td>\n    <td>To customize webpack's <code>publicPath</code> \n    <br>Comes in handy if using CDN to put built assets</td>\n  </tr>\n  <tr>\n    <td><code>analyzeBundle</code></td>\n    <td><code>Boolean?</code> [<code>false</code>]</td>\n    <td>To enable webpack's bundle analyzer, for analyzing bundle sizes during bundle debugging should Maleo.js' build process got slow</td>\n  </tr>\n  <tr>\n    <td><code>webpack</code></td>\n    <td><code>Function?</code></td>\n    <td>To customize webpack configuration, more details <a href=\"#customize-webpack\">here</a></td>\n  </tr>\n  <tr>\n    <td><code>routes</code></td>\n    <td><code>string?</code> [<code>rootDir/routes.jsx</code>]</td>\n    <td>Path to your routes file</td>\n  </tr>\n  <tr>\n    <td><code>customDocument</code></td>\n    <td><code>string?</code> [<code>rootDir/document.jsx</code>]</td>\n    <td>Path to your custom document file</td>\n  </tr>\n  <tr>\n    <td><code>customWrap</code></td>\n    <td><code>string?</code> [<code>rootDir/wrap.jsx</code>]</td>\n    <td>Path to your custom wrap file</td>\n  </tr>\n  <tr>\n    <td><code>customApp</code></td>\n    <td><code>string?</code> [<code>rootDir/app.jsx</code>]</td>\n    <td>Path to your custom app file</td>\n  </tr>\n  <tr>\n    <td><code>csp</code></td>\n    <td><code>boolean | cspConfig</code> [<code>false</code>]</td>\n    <td>Config for <a href=\"https://developers.google.com/web/fundamentals/security/csp/\">Content Security Policy</a>, using <a href=\"https://helmetjs.github.io/docs/csp/\">Helmet-CSP</a>\n    <br/>\n    If set to true it will enable default config:\n    <br/>\n    <code>{\n      directives: {\n        defaultSrc: [`'self'`],\n        styleSrc: [`'self'`],\n      },\n    }</code>\n    </td>\n  </tr>\n</table>\n\n#### Customize Server\n\nCreate a `server.js` file on root directory where your `package.json` lives.\nHere you can customize Maleo's server.\n```js\nimport { Server } from '@airy/maleo/server';\n\nconst PORT = process.env.PORT || 3000;\n\nconst maleoServer = Server.init({\n  port: PORT,\n  runHandler: () => {\n    console.log('Server running on port :', PORT);\n  }\n});\n\nmaleoServer.run();\n```\n\nHere are the API's for the configuration:\n\n<table>\n  <tr>\n    <td>Key</td>\n    <td>Type</td>\n    <td>Description</td>\n  </tr>\n  <tr>\n    <td><code>port</code></td>\n    <td><code>Number?</code> [<code>3000</code>]</td>\n    <td>Port to run Maleo server</td>\n  </tr>\n  <tr>\n    <td><code>assetDir</code></td>\n    <td><code>String?</code> [<code>'<root>/.maleo/client'</code>]</td>\n    <td>Directory for all client related assets</td>\n  </tr>\n  <tr>\n    <td><code>runHandler</code></td>\n    <td><code>Function?</code></td>\n    <td>Function called when maleo server starter</td>\n  </tr>\n</table>\n\n#### Customize Webpack\n\nYou are able to extend Maleo.js' default webpack configuration by defining a function on `maleo.config.js`\n\n```js\n// maleo.config.js\n\nmodule.exports = {\n  webpack(config, context, next) {\n    // Perform customizations to webpack config\n    // Important: This call is required in order to let Maleo pass the config to webpack\n    return next(); \n  },\n};\n```\n\nWebpack function will receive three arguments:\n\n<table>\n  <tr>\n    <td>Argument</td>\n    <td>Details</td>\n  <tr>\n  <tr>\n    <td><code>config</code></td>\n    <td>This contains webpack configuration object that you can manipulate</td>\n  <tr>\n  <tr>\n    <td><code>context</code></td>\n    <td>This contains some keys that are useful for the build context\n      <br>\n      <table>\n        <tr>\n          <td><code>isDev</code></td>\n          <td><code>Boolean</code></td>\n          <td>Check if current build is for development</td>\n        </tr>\n        <tr>\n          <td><code>publicPath</code></td>\n          <td><code>String</code></td>\n          <td>Public Path of user defined or default's value</td>\n        </tr>\n        <tr>\n          <td><code>analyzeBundle</code></td>\n          <td><code>Boolean</code></td>\n          <td>Check if analyze bundle is enabled</td>\n        </tr>\n        <tr>\n          <td><code>buildDirectory</code></td>\n          <td><code>String</code></td>\n          <td>Build Directory of user defined or default's value</td>\n        </tr>\n        <tr>\n          <td><code>name</code></td>\n          <td><code>String</code></td>\n          <td>Build name <code>'server' || 'client'</code></td>\n        </tr>\n      </table>\n    </td>\n  <tr>\n  <tr>\n    <td><code>next</code></td>\n    <td>A callback required to be called to pass the custom configuration</td>\n  <tr>\n</table>\n\nExample of adding `ts-loader` through `maleo.config.js`:\n\n```js\n// maleo.config.js\n\n// Partly taken and modified from @airy/maleo-ts-plugin source\n// for simplicity purposes\n\nmodule.exports = {\n  webpack(config, context, next) {\n    const { isDev } = context\n\n    config.module.rules.push({\n      test: /\\.tsx?/,\n      exclude: /node_modules/,\n      use: [\n        require.resolve('@airy/maleo/lib/build/loaders/maleo-babel-loader'),\n        {\n          loader: 'ts-loader',\n          options: {\n            transpileOnly: true,\n          },\n        },\n      ],\n\n      if (isDev) {\n        config.plugins.push(new ForkTSCheckerWebpackPlugin());\n      }\n\n      return next();\n    })\n  },\n};\n```\n\n#### Customize Babel Config\n\nMaleo.js also let you have your own babel config. Just simply add `.babelrc` file at the root directory of your app.\n\nYou can include Maleo.js' babel preset in order to have latest JavaScript preset.\n\nHere's an example of `.babelrc` file:\n```json\n{\n  \"presets\": [\"@airy/maleo/babel\"],\n  \"plugins\": []\n}\n```\n\nThe `@airy/maleo/babel` preset includes everything you need to get your development started. The preset includes:\n- `@babel/preset-env`\n- `@babel/preset-react`\n- `@babel/plugin-proposal-class-properties`\n- `@babel/plugin-proposal-decorators`\n- `@babel/plugin-proposal-object-rest-spread`\n- `@babel/plugin-transform-runtime`\n- `react-loadable/babel`\n\n## Core Utilities Functions\n\n#### Route Matching\n\nTo use you can import the matching routes function like this:\n```js\nimport { getMatchedRoutes } from '@airy/maleo/utils';\n```\n\nThe function will return all matched routes and the route object from `routes.json`\n\n## CDN Support\n\nIf you are using a CDN, you can set up the `publicPath` setting and configure your CDN's origin to resolve to the domain that Maleo.js is hosted on.\n\n```js\n// maleo.config.js\n\nconst isProd = process.env.NODE_ENV === 'production';\n\nmodule.exports = {\n  assetPrefix: isProd && 'https://cdn.example.com';\n}\n```\n\n## FAQ\n\n== TO BE DETERMINED == \n\n## Plugins\n- [css-plugin](https://github.com/airyrooms/maleo-plugins/tree/master/packages/css-plugin)\n- [redux-plugin](https://github.com/airyrooms/maleo-plugins/tree/master/packages/redux-plugin)\n- [typescript-plugin](https://github.com/airyrooms/maleo-plugins/tree/master/packages/typescript-plugin)\n- [more](https://github.com/airyrooms/maleo-plugins)\n\n\n## Contributing\n[Please follow these steps to contribute to Maleo.js](https://github.com/airyrooms/maleo.js/blob/canary/CONTRIBUTING.md)\n\n[Please follow these steps to contribute to Maleo.js' plugins](https://github.com/airyrooms/maleo-plugins#contributing-guidelines)\n\n\n## Contributors\n\nThis project exists thanks to all the people who contribute. [Contribute](CONTRIBUTING.md).\n\nMany thanks to our **[contributors](https://github.com/airyrooms/maleo.js/graphs/contributors)**!\n\n\n## License\n\nMIT","readmeFilename":"README.md","bugs":{"url":"https://github.com/airyrooms/maleo.js/issues"},"homepage":"https://github.com/airyrooms/maleo.js#readme","_id":"@airy/maleo@0.3.1-canary.22","_nodeVersion":"11.15.0","_npmVersion":"lerna/3.13.1/node@v11.15.0+x64 (linux)","dist":{"integrity":"sha512-b7nk8nRsMkzBlVblZh0RsrwLUR+oU9NYcLl0suYfVYSQequ1Kx7h7Pqk+kqjMWX5Ga8Ro7dD4sZoEctcmBeh4A==","shasum":"494057909d26790536d1a0b6a486c1fb73e5206c","tarball":"https://registry.npmjs.org/@airy/maleo/-/maleo-0.3.1-canary.22.tgz","fileCount":105,"unpackedSize":371158,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJdLr0wCRA9TVsSAnZWagAA6v0P/Aq1RzgqG1WTVNUtg5nS\neWwVYYyVuy1bt7HH3xOQZlRmp78fGJMfrSLMyqSwkF/Wn8Eu+mK5gy45BQiO\n5OgW0MDylyzJsvRauZPtdQEfF2tYCHhAHndbaOvzPJgKNppFhxTvND23ZHYe\nxqcBJpdL2yP5T/SveYcU1g9ZnA3qjR6EopkLkGB5jZnjv8+LVs1SYDLeiNzA\ny80Z1Bl9VoeDqvDdiJjtx5VLEJgW6qhR/7fJSmGdNVPx5A/cFbYRq+W4Tuw9\nFHoFjuCZl2B4aIcCckHi2U4KiwQiieR8hHaMexeM/AWRlZZ4QNlFHZdXBnoj\n++43jjwfrfEKX628HntFc0OGI1d5pVFuQwFJ3gtLvHFwla5mh0hbAbiFnNxz\nrl/mwK+6+639CQUJ9Blq2u5sm73+Ej/bcQDFFL76BPWPJWLA7DOYyXajTQ5b\nuBR02/K6W106DNZZMwkX1fekfd8peirHNgDSXgcYw6zrIqWxch8F+oYIpQG5\nFocrNO8YAeyPRy+qSLky6Yev/ZAfduKvA91GsJL0QRTPtHZTZO6G4vxdK9yE\nCUv4L1s2+TDQlNwmbrCWzWTRmQPkClqJeO4ApndbWcFWQSegidFiWFyawXbR\nVv12LJGViuZgVgU30yI0GUsxBPXGNKg3p3mn6k6eSui4T9LmRO6U0XHR0uYr\ndJv+\r\n=BfUZ\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIA2yoexi1pUK1Ap6gpCgi8Gt1Y5xfARjA9V4/RkxzsPNAiB/iTDrNgdZnjD4jXWecGnf5eCMf8VrOfeCGkL2QB4rrQ=="}]},"maintainers":[{"name":"airyrooms-engineering","email":"engineering@airyrooms.com"}],"_npmUser":{"name":"airyrooms-engineering","email":"engineering@airyrooms.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/maleo_0.3.1-canary.22_1563344175415_0.47662204494063354"},"_hasShrinkwrap":false},"0.3.1-canary.23":{"name":"@airy/maleo","version":"0.3.1-canary.23","description":"Un-opinionated Universal Rendering Javascript Framework","repository":{"type":"git","url":"git+https://github.com/airyrooms/maleo.js.git","bugs":"https://github.com/airyrooms/maleo.js/issues"},"author":{"name":"Airy"},"license":"MIT","private":false,"sideEffects":false,"scripts":{"build":"rm -rf dist && gulp","watch":"gulp watch","prepublish":"yarn build","start":"node server.js"},"bin":{"maleo":"lib/bin/maleo.js"},"dependencies":{"@babel/core":"7.1.2","@babel/plugin-proposal-class-properties":"7.1.0","@babel/plugin-proposal-decorators":"7.1.2","@babel/plugin-proposal-object-rest-spread":"7.0.0","@babel/plugin-syntax-dynamic-import":"7.0.0","@babel/plugin-transform-runtime":"7.1.0","@babel/preset-env":"7.1.0","@babel/preset-react":"7.0.0","@babel/runtime":"7.1.2","@babel/runtime-corejs2":"7.2.0","babel-loader":"8.0.4","case-sensitive-paths-webpack-plugin":"2.1.2","compression":"1.7.4","es6-promise":"4.2.5","eventsource":"1.0.7","express":"4.16.4","figlet":"1.2.1","flatted":"^2.0.1","friendly-errors-webpack-plugin":"1.7.0","hard-source-webpack-plugin":"0.13.1","helmet":"3.14.0","hoist-non-react-statics":"^3.3.0","isomorphic-fetch":"2.2.1","loader-utils":"1.1.0","node-notifier":"5.2.1","nodemon":"1.18.4","react-dom":"16.8.4","react-helmet":"5.2.0","react-loadable":"5.5.0","react-router-config":"5.0.0","react-router-dom":"5.0.0","rimraf":"2.6.3","source-map-support":"0.5.9","terser-webpack-plugin":"1.1.0","webpack":"4.29.0","webpack-bundle-analyzer":"3.0.3","webpack-cli":"3.1.2","webpack-dev-middleware":"3.4.0","webpack-hot-middleware":"2.24.3","webpackbar":"2.6.3"},"peerDependencies":{"react":"^16.8.4"},"devDependencies":{"@babel/preset-typescript":"^7.1.0","@types/webpack-bundle-analyzer":"^2.13.0","babel-plugin-module-resolver":"^3.1.1","gulp":"github:gulpjs/gulp#4.0","gulp-babel":"^8.0.0","gulp-clean":"^0.4.0","gulp-sourcemaps":"^2.6.4","gulp-uglify":"^3.0.1","gulp-watch":"^5.0.1","react":"^16.8.4"},"gitHead":"a3395ca32464d284739c5d528160e632b721e620","readme":"[![Build Status](https://travis-ci.org/airyrooms/maleo.js.svg?branch=master)](https://travis-ci.org/airyrooms/maleo.js)\n[![Coverage Status](https://coveralls.io/repos/github/airyrooms/maleo.js/badge.svg?branch=master)](https://coveralls.io/github/airyrooms/maleo.js?branch=master)\n[![npm version](https://badge.fury.io/js/%40airy%2Fmaleo.svg)](https://badge.fury.io/js/%40airy%2Fmaleo)\n[![Issues](http://img.shields.io/github/issues/airyrooms/maleo.js.svg)]( https://github.com/airyrooms/maleo.js/issues)\n[![MIT license](https://img.shields.io/badge/License-MIT-blue.svg)](https://github.com/airyrooms/maleo.js/blob/canary/LICENSE)\n[![Discord Chat](https://img.shields.io/discord/550498214789513218.svg)](https://discord.gg/9eArCQn)\n[![Conventional Commits](https://img.shields.io/badge/Conventional%20Commits-1.0.0-green.svg)](https://conventionalcommits.org)\n\n\n# Welcome to Maleo.js\n\nMaleo.js is an un-opinionated framework to enable Universal Rendering in JavaScript using React with no hassle.\n\nWe are here to solve the time consuming setups Universal Rendering Required.\n\n---\n\nReadme below is the documentation for the `canary` (prerelease) branch. To view the documentation for the latest stable Maleo.js version change branch to `master`\n\n---\n\n# Table of Contents\n- [Features](#features)\n- [Setup](#setup)\n- [Component Lifecycle](#component-lifecycle)\n- [Routing](#routing)\n- [Dynamic Import Component](#dynamic-import-component)\n  - [Tips](#tips)\n  - [Preloading](#preloading)\n- [Customizable Component](#customizable-component)\n  - [Custom Document](#custom-document)\n  - [Custom Wrap](#custom-wrap)\n- [Custom Configuration](#custom-configuration)\n  - [Customize Server](#customize-server)\n  - [Customize Webpack](#customize-webpack)\n  - [Customize Babel Config](#customize-babel-config)\n- [Core Utilities Functions](#core-utilities-functions)\n- [CDN Support](#cdn-support)\n- [Plugins](#plugins)\n- [FAQ](#faq)\n- [Contributing](#contributing)\n\n---\n\n## Features\n- Universal Rendering\n- Plugin based framework\n- Customizable\n\n## Setup\n\nInstall Maleo.js \n\n>*for now change `@airy/maleo` to `@airy/maleo@canary` until we publish the stable version of this package*\n\n\n**NPM**\n```bash\n$ npm install --save @airy/maleo react\n```\n**Yarn**\n```bash\n$ yarn add @airy/maleo react\n```\n\nAdd this script to your `package.json`\n```json\n{\n  \"scripts\": {\n    \"dev\": \"maleo dev\",\n    \"build\": \"export NODE_ENV=production && maleo build\",\n    \"start\": \"export NODE_ENV=production && maleo run\"\n  }\n}\n```\n\nCreate a page Root component\n```jsx\n// ./src/Root.jsx\nimport React from 'react';\n\n// Export default is required for registering page\nexport default class RootComponent extends React.Component {\n  render() {\n    return (\n      <h1>Hello World!</h1>\n    )\n  }\n}\n```\n\nAnd lastly, create a routing file on your project root directory called `routes.json` and register your page component\n```json\n[\n  {\n    \"path\": \"/\",\n    \"page\": \"./src/Root\"\n  }\n]\n```\n\nAfter that you can now run `$ npm run dev` and go to `http://localhost:3000`.\n\nYou should now see your app running on your browser.\n\nBy now you should see \n- Automatic transpilation and bundling (with webpack and babel)\n- ~~Hot code reloading~~ [#17](https://github.com/airyrooms/maleo.js/issues/17)\n- Server rendering\n\nTo see how simple this is, check out the sample app!\n\n## Component Lifecycle \n\nMaleo.js added a new component lifecycle hook called `getInitialProps`, this function is called during Server Side Rendering (SSR) and during route changes on Client Side Rendering (CSR).\n\nThis is useful especially for SEO purposes. \n\nExample for stateful component:\n```jsx\nimport React from 'react';\n\nexport default class extends React.Component {\n  static getInitialProps = async (ctx) => {\n    const { req } = ctx;\n\n    // check if getInitialProps is called on server or on client\n    const userAgent = req ? req.headers['user-agent'] : navigator.userAgent;\n\n    // the return value will be passed as props for this component\n    return { userAgent };\n  }\n\n  render() {\n    return (\n      <div>\n        Hello World {this.props.userAgent}\n      </div>\n    );\n  }\n}\n```\n\nExample for stateless component:\n```jsx\nconst Component = (props) => (\n  <div>\n    Hello World {props.userAgent}\n  </div>\n);\n\nComponent.getInitialprops = async (ctx) => {\n  const { req } = ctx;\n\n  const userAgent = req ? req.headers['user-agent'] : navigator.userAgent;\n\n  return { userAgent };\n};\n\nexport default Component;\n```\n\n`getInitialProps` receives a context object with the following properties:\n- `req` - HTTP request object (server only)\n- `res` - HTTP response object (server only)\n- `...wrapProps` - Spreaded properties from custom Wrap\n- `...appProps` - Spreaded properties from custom App\n\n## Routing\n\nRouting is declared in a centralized route config.\nRegister all the route config in `routes.json` file.\n\nIf you put the `routes.json` files on root directory, Maleo will automatically register your route. Otherwise put path to your routes on [Maleo config](#custom-configuration).\n\nRoutes file has to export default the route configuration.\nThe route object **expected to have distinct key** to indicate the route.\n\n<table>\n  <tr>\n    <td>Key</td>\n    <td>Type</td>\n    <td>Description</td>\n  </tr>\n  <tr>\n    <td><code>path</code></td>\n    <td><code>String!</code></td>\n    <td>Routes path</td>\n  </tr>\n  <tr>\n    <td><code>page</code></td>\n    <td><code>String!</code></td>\n    <td>Path to React Component for this route path</td>\n  </tr>\n  <tr>\n    <td><code>exact</code></td>\n    <td><code>Boolean?</code> [<code>false</code>]</td>\n    <td>To make url has to match exactly the path in order to render the component. Give <code>false</code> value if the route is a wrapper route component</td>\n  </tr>\n  <tr>\n    <td><code>routes</code></td>\n    <td><code>RouteObject?</code></td>\n    <td>Nested route</td>\n  </tr>\n</table>\n\n\nFor example:\n```json\n[\n  {\n    \"page\": \"./src/MainApp\",\n    \"routes\": [\n      {\n        \"path\": \"/\",\n        \"page\": \"./src/Search\",\n        \"exact\": true\n      },\n      {\n        \"path\": \"/search\",\n        \"page\": \"./src/Search\",\n        \"routes\": [\n          {\n            \"path\": \"/search/hello\",\n            \"page\": \"./src/Detail\",\n            \"exact\": true\n          }\n        ]\n      },\n      {\n        \"path\": \"/detail\",\n        \"page\": \"./src/Detail\",\n        \"exact\": true\n      }\n    ]\n  }\n]\n```\n\n## Dynamic Import Component\nMaleo.js supports TC39 [dynamic import proposal](https://github.com/tc39/proposal-dynamic-import) for JavaScript.\n\nYou can think dynamic import as another way to split your code into manageable chunks. You can use our `Dynamic` function which utilizes [react loadable](https://github.com/jamiebuilds/react-loadable)\n\nFor Example\n```js\n// DynamicComponent.js\nimport Dynamic from '@airy/maleo/dynamic';\n\nexport default Dynamic({\n  loader: () => import( /* webpackChunkName:\"DynamicComponent\" */ './component/DynamicLoad'),\n})\n```\n\n### **Preloading**\n\nFor optimization purposes, you can also preload a component even before the component got rendered.\n\nFor example, if you want to load component when a button get pressed, you can start preloading the component when the user hovers the mouse over the button.\n\nThe component created by `Dynamic` exposes a [static `preload` method](https://github.com/jamiebuilds/react-loadable#loadablecomponentpreload).\n\n```jsx\nimport React from 'react';\nimport Dynamic from '@airy/maleo/dynamic';\n\nconst DynamicBar = Dynamic({\n  loader: () => import('./Bar'),\n  loading: LoadingCompoinent\n});\n\nclass MyComponent extends React.Component {\n  state = { showBar: false };\n\n  onClick = () => {\n    this.setState({ showBar: true });\n  };\n\n  onMouseOver = () => DynamicBar.preload();\n\n  render() {\n    return (\n      <div>\n        <button\n          onClick={this.onClick}\n          onMouseOver={this.onMouseOver}>\n          Show Bar\n        </button>\n        { this.state.showBar && <DynamicBar /> }\n      </div>\n    )\n  }\n}\n```\n\n## Customizable Component\n\n### Custom Document\n\nHighly inspired by what [Next.js](https://github.com/zeit/next.js) has done on their awesome template customization.\n\nMaleo.js also enable customization on `Document` as document's markup. So you don't need to include tags like `<html>`, `<head>`, `<body>`, etc. \n\nTo override the default behavior, you'll need to create a component that extends the `Document` React class provided by Maleo.\n\n```jsx\n// document.jsx\nimport React from 'react';\nimport { Document, Header, Main, Scripts } from '@airy/maleo/document';\n\nexport default class extends Document {\n  render() {\n    return (\n      <html>\n        <Header>\n          <title>Maleo JS</title>\n          <meta charset=\"utf-8\" />\n          <meta name=\"description\" content=\"Maleo.js is awesome!\" />\n\n          <style>\n            {` body { background-color: #fff } `}\n          </style>\n        </Header>\n\n        <body>\n          <Main />\n\n          <Scripts />\n\n          <ReduxScript />\n        </body>\n      </html>\n    );\n  }\n}\n```\n\n### Custom Wrap\n\nMaleo.js uses the `Wrap` component to initialize pages. `Wrap` contains React Router's Component. You can add HoC here to wrap the application and control the page initialization. Which allows you to do amazing things like:\n- Persisting layour between page changes\n- Keeping state when navigating pages\n- Custom error handling using `componentDidCatch`\n- Inject additional data into pages (like Redux provider, etc)\n\nTo override the default behavior, you'll need to create a component that extends the `Wrap` React class provided by Maleo.\n\n```jsx\n// wrap.jsx\nimport React from 'react';\nimport { Wrap } from '@airy/maleo/wrap';\n\n// Redux plugin for Maleo.js\n// Hoc that creates store and add Redux Provider\nimport { withRedux } from '@airy/maleo-redux-plugin';\n\n// Custom Wrapper that will be rendered for the whole Application\nimport Content from './component/Content';\nimport NavigationBar from './component/NavigationBar';\n\nimport { createStore } from './store';\n\n@withRedux(createStore)\nexport default class extends Wrap {\n  static getInitialProps = (ctx) => {\n    const { store } = ctx\n    // you receive store from context\n    // you can access or do something with the store here\n    console.log('Initialized Redux Store', store);\n    return {}\n  }\n\n  render() {\n    const { Container, containerProps, App, appProps } = this.props\n\n    return (\n      <Container {...containerProps}>\n        <NavigationBar />\n        <Content>\n          <App {...appProps}/>\n        </Content>\n      </Container>\n    )\n  }\n}\n\n```\nIf you put `document.jsx` and `wrap.jsx` on root directory (the same level with `package.json`), then Maleo will automatically register your custom Document and Wrap. Otherwise, you can add the path to your custom Document and Wrap on [Maleo config](#custom-configuration)\n\n---\n***We are also working on adding default and customizable `Error` component page***\n\n---\n\n## Custom Configuration\n\nFor more advanced configuration of Maleo.js, like `webpack` config, registering `plugins`, path to your routes, custom Document and Wrap, and adding `path alias`, you can create a `maleo.config.js` in the root of your project directory. (same directory with `package.json`)\n\n```js\n// maleo.config.js\n\nmodule.exports = {\n  /* config options here */\n}\n```\n\nHere are the API's for the configuration:\n\n<table>\n  <tr>\n    <td>Key</td>\n    <td>Type</td>\n    <td>Description</td>\n  </tr>\n  <tr>\n    <td><code>favicon</code></td>\n    <td><code>String</code> [<code>{project-dir}/favicon.ico</code>]</td>\n    <td>Path to favicon file</td>\n  </tr>\n  <tr>\n    <td><code>buildDir</code></td>\n    <td><code>String?</code> [<code>.maleo</code>]</td>\n    <td>Directory to put Maleo.js' build assets</td>\n  </tr>\n  <tr>\n    <td><code>cache</code></td>\n    <td><code>Boolean?</code> [<code>true</code>]</td>\n    <td>Enable webpack build caching</td>\n  </tr>\n  <tr>\n    <td><code>isDev</code></td>\n    <td><code>Boolean?</code> [<code>process.env.NODE_ENV === 'development'</code>]</td>\n    <td>Enable development build configuration</td>\n  </tr>\n  <tr>\n    <td><code>sourceMaps</code></td>\n    <td><code>Boolean?</code> [<code>true</code>]</td>\n    <td>Enable webpack to generate source maps</td>\n  </tr>\n  <tr>\n    <td><code>alias</code></td>\n    <td><code>Object?</code></td>\n    <td>A key value pair for aliasing path directory\n      <br>\n      <code>\n      { 'component': './src/component' }\n      </code>\n    </td>\n  </tr>\n  <tr>\n    <td><code>publicPath</code></td>\n    <td><code>String?</code> [<code>/_assets/</code>]</td>\n    <td>To customize webpack's <code>publicPath</code> \n    <br>Comes in handy if using CDN to put built assets</td>\n  </tr>\n  <tr>\n    <td><code>analyzeBundle</code></td>\n    <td><code>Boolean?</code> [<code>false</code>]</td>\n    <td>To enable webpack's bundle analyzer, for analyzing bundle sizes during bundle debugging should Maleo.js' build process got slow</td>\n  </tr>\n  <tr>\n    <td><code>webpack</code></td>\n    <td><code>Function?</code></td>\n    <td>To customize webpack configuration, more details <a href=\"#customize-webpack\">here</a></td>\n  </tr>\n  <tr>\n    <td><code>routes</code></td>\n    <td><code>string?</code> [<code>rootDir/routes.jsx</code>]</td>\n    <td>Path to your routes file</td>\n  </tr>\n  <tr>\n    <td><code>customDocument</code></td>\n    <td><code>string?</code> [<code>rootDir/document.jsx</code>]</td>\n    <td>Path to your custom document file</td>\n  </tr>\n  <tr>\n    <td><code>customWrap</code></td>\n    <td><code>string?</code> [<code>rootDir/wrap.jsx</code>]</td>\n    <td>Path to your custom wrap file</td>\n  </tr>\n  <tr>\n    <td><code>customApp</code></td>\n    <td><code>string?</code> [<code>rootDir/app.jsx</code>]</td>\n    <td>Path to your custom app file</td>\n  </tr>\n  <tr>\n    <td><code>csp</code></td>\n    <td><code>boolean | cspConfig</code> [<code>false</code>]</td>\n    <td>Config for <a href=\"https://developers.google.com/web/fundamentals/security/csp/\">Content Security Policy</a>, using <a href=\"https://helmetjs.github.io/docs/csp/\">Helmet-CSP</a>\n    <br/>\n    If set to true it will enable default config:\n    <br/>\n    <code>{\n      directives: {\n        defaultSrc: [`'self'`],\n        styleSrc: [`'self'`],\n      },\n    }</code>\n    </td>\n  </tr>\n</table>\n\n#### Customize Server\n\nCreate a `server.js` file on root directory where your `package.json` lives.\nHere you can customize Maleo's server.\n```js\nimport { Server } from '@airy/maleo/server';\n\nconst PORT = process.env.PORT || 3000;\n\nconst maleoServer = Server.init({\n  port: PORT,\n  runHandler: () => {\n    console.log('Server running on port :', PORT);\n  }\n});\n\nmaleoServer.run();\n```\n\nHere are the API's for the configuration:\n\n<table>\n  <tr>\n    <td>Key</td>\n    <td>Type</td>\n    <td>Description</td>\n  </tr>\n  <tr>\n    <td><code>port</code></td>\n    <td><code>Number?</code> [<code>3000</code>]</td>\n    <td>Port to run Maleo server</td>\n  </tr>\n  <tr>\n    <td><code>assetDir</code></td>\n    <td><code>String?</code> [<code>'<root>/.maleo/client'</code>]</td>\n    <td>Directory for all client related assets</td>\n  </tr>\n  <tr>\n    <td><code>runHandler</code></td>\n    <td><code>Function?</code></td>\n    <td>Function called when maleo server starter</td>\n  </tr>\n</table>\n\n#### Customize Webpack\n\nYou are able to extend Maleo.js' default webpack configuration by defining a function on `maleo.config.js`\n\n```js\n// maleo.config.js\n\nmodule.exports = {\n  webpack(config, context, next) {\n    // Perform customizations to webpack config\n    // Important: This call is required in order to let Maleo pass the config to webpack\n    return next(); \n  },\n};\n```\n\nWebpack function will receive three arguments:\n\n<table>\n  <tr>\n    <td>Argument</td>\n    <td>Details</td>\n  <tr>\n  <tr>\n    <td><code>config</code></td>\n    <td>This contains webpack configuration object that you can manipulate</td>\n  <tr>\n  <tr>\n    <td><code>context</code></td>\n    <td>This contains some keys that are useful for the build context\n      <br>\n      <table>\n        <tr>\n          <td><code>isDev</code></td>\n          <td><code>Boolean</code></td>\n          <td>Check if current build is for development</td>\n        </tr>\n        <tr>\n          <td><code>publicPath</code></td>\n          <td><code>String</code></td>\n          <td>Public Path of user defined or default's value</td>\n        </tr>\n        <tr>\n          <td><code>analyzeBundle</code></td>\n          <td><code>Boolean</code></td>\n          <td>Check if analyze bundle is enabled</td>\n        </tr>\n        <tr>\n          <td><code>buildDirectory</code></td>\n          <td><code>String</code></td>\n          <td>Build Directory of user defined or default's value</td>\n        </tr>\n        <tr>\n          <td><code>name</code></td>\n          <td><code>String</code></td>\n          <td>Build name <code>'server' || 'client'</code></td>\n        </tr>\n      </table>\n    </td>\n  <tr>\n  <tr>\n    <td><code>next</code></td>\n    <td>A callback required to be called to pass the custom configuration</td>\n  <tr>\n</table>\n\nExample of adding `ts-loader` through `maleo.config.js`:\n\n```js\n// maleo.config.js\n\n// Partly taken and modified from @airy/maleo-ts-plugin source\n// for simplicity purposes\n\nmodule.exports = {\n  webpack(config, context, next) {\n    const { isDev } = context\n\n    config.module.rules.push({\n      test: /\\.tsx?/,\n      exclude: /node_modules/,\n      use: [\n        require.resolve('@airy/maleo/lib/build/loaders/maleo-babel-loader'),\n        {\n          loader: 'ts-loader',\n          options: {\n            transpileOnly: true,\n          },\n        },\n      ],\n\n      if (isDev) {\n        config.plugins.push(new ForkTSCheckerWebpackPlugin());\n      }\n\n      return next();\n    })\n  },\n};\n```\n\n#### Customize Babel Config\n\nMaleo.js also let you have your own babel config. Just simply add `.babelrc` file at the root directory of your app.\n\nYou can include Maleo.js' babel preset in order to have latest JavaScript preset.\n\nHere's an example of `.babelrc` file:\n```json\n{\n  \"presets\": [\"@airy/maleo/babel\"],\n  \"plugins\": []\n}\n```\n\nThe `@airy/maleo/babel` preset includes everything you need to get your development started. The preset includes:\n- `@babel/preset-env`\n- `@babel/preset-react`\n- `@babel/plugin-proposal-class-properties`\n- `@babel/plugin-proposal-decorators`\n- `@babel/plugin-proposal-object-rest-spread`\n- `@babel/plugin-transform-runtime`\n- `react-loadable/babel`\n\n## Core Utilities Functions\n\n#### Route Matching\n\nTo use you can import the matching routes function like this:\n```js\nimport { getMatchedRoutes } from '@airy/maleo/utils';\n```\n\nThe function will return all matched routes and the route object from `routes.json`\n\n## CDN Support\n\nIf you are using a CDN, you can set up the `publicPath` setting and configure your CDN's origin to resolve to the domain that Maleo.js is hosted on.\n\n```js\n// maleo.config.js\n\nconst isProd = process.env.NODE_ENV === 'production';\n\nmodule.exports = {\n  assetPrefix: isProd && 'https://cdn.example.com';\n}\n```\n\n## FAQ\n\n== TO BE DETERMINED == \n\n## Plugins\n- [css-plugin](https://github.com/airyrooms/maleo-plugins/tree/master/packages/css-plugin)\n- [redux-plugin](https://github.com/airyrooms/maleo-plugins/tree/master/packages/redux-plugin)\n- [typescript-plugin](https://github.com/airyrooms/maleo-plugins/tree/master/packages/typescript-plugin)\n- [more](https://github.com/airyrooms/maleo-plugins)\n\n\n## Contributing\n[Please follow these steps to contribute to Maleo.js](https://github.com/airyrooms/maleo.js/blob/canary/CONTRIBUTING.md)\n\n[Please follow these steps to contribute to Maleo.js' plugins](https://github.com/airyrooms/maleo-plugins#contributing-guidelines)\n\n\n## Contributors\n\nThis project exists thanks to all the people who contribute. [Contribute](CONTRIBUTING.md).\n\nMany thanks to our **[contributors](https://github.com/airyrooms/maleo.js/graphs/contributors)**!\n\n\n## License\n\nMIT","readmeFilename":"README.md","bugs":{"url":"https://github.com/airyrooms/maleo.js/issues"},"homepage":"https://github.com/airyrooms/maleo.js#readme","_id":"@airy/maleo@0.3.1-canary.23","_nodeVersion":"11.15.0","_npmVersion":"lerna/3.13.1/node@v11.15.0+x64 (linux)","dist":{"integrity":"sha512-JVfbyQ+e2DdnMcgxNq9AgOSTT4f8ysjmmUjGAHjzLsE/w7d23dK8Nyi5i+UoD956Av0Icb3xQ0QaWxMomNkyhQ==","shasum":"7d63213bd8cf2a0f5efad7380502c4f8b517fbf0","tarball":"https://registry.npmjs.org/@airy/maleo/-/maleo-0.3.1-canary.23.tgz","fileCount":105,"unpackedSize":371575,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJdN9xGCRA9TVsSAnZWagAAQdMP/0Dlzai9t1UgyNAI3I7u\nZUgHcG9uUSMseE1Y5WYC9X4+Bq/0B006jYPtUi3F722kidVBCq1Hu69HHLnG\nmcsXxezxhgmq2I1LvJ2mFk5l9ITfLvAKtcLAPGro9cMKetbMamRnM1//PASb\nN0pQUTqzs1oBY+bAMfAnHBV4Az8xnJGoaLaPFrk1pw1tIVQ+XXPQ9bdoaNoA\nUnMgCen9JGDCFJ9kpql+ORfxy3ioxjXrK6URxcE6E7r8lo2afWmpLhAWmXRU\nS1/vihV4V9rHiRqRFQpgJZH3U1Ved+jwkKAEiDdn8bW56RHWhcQ2fe8FAo7d\ntFcxBuoiwxPZdeNciJPkU35PGN/hlDgjMajzfPSB0GctjUkgqtSHQbeON5nE\nC2TwTrKorpWGxVUv9+7x+YdKoOCMLWCgy8ieABx94U7KhHkGk/T8AVC78pv2\nrjs5eD51ewGKiIyKE6MFCJXsJPIMk1bRraYRGKCedvT3cvqDCwi3DFALB3PS\n1n38/Ps0xREy239Qi5RXpLrhYr8JfBIB4xhCZK/CL21IXt4Eu/0X1yT7yEUO\nnDhpeWyS3oojOCGummT0JhYjTEWc2q/xylftIj713KS63mpo4nJvPNE8Re92\n7fLHCKR5A0p08inT4cAuVcUnaRdnyvAnapkR7oi5CC85LAM8fdNvoW7+MI3g\nephm\r\n=i10R\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIADm9bhkQGa7CUleRF+hL/+NWpA9dgeOEzcgpYpntOoOAiEAxHlFpua4BXv2XOgOXlLcTBg/tDmqRsINztRor9qZm0o="}]},"maintainers":[{"name":"airyrooms-engineering","email":"engineering@airyrooms.com"}],"_npmUser":{"name":"airyrooms-engineering","email":"engineering@airyrooms.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/maleo_0.3.1-canary.23_1563941957288_0.36045664248339615"},"_hasShrinkwrap":false},"0.3.1-canary.24":{"name":"@airy/maleo","version":"0.3.1-canary.24","description":"Un-opinionated Universal Rendering Javascript Framework","repository":{"type":"git","url":"git+https://github.com/airyrooms/maleo.js.git","bugs":"https://github.com/airyrooms/maleo.js/issues"},"author":{"name":"Airy"},"license":"MIT","private":false,"sideEffects":false,"scripts":{"build":"rm -rf dist && gulp","watch":"gulp watch","prepublish":"yarn build","start":"node server.js"},"bin":{"maleo":"lib/bin/maleo.js"},"dependencies":{"@babel/core":"7.1.2","@babel/plugin-proposal-class-properties":"7.1.0","@babel/plugin-proposal-decorators":"7.1.2","@babel/plugin-proposal-object-rest-spread":"7.0.0","@babel/plugin-syntax-dynamic-import":"7.0.0","@babel/plugin-transform-runtime":"7.1.0","@babel/preset-env":"7.1.0","@babel/preset-react":"7.0.0","@babel/runtime":"7.1.2","@babel/runtime-corejs2":"7.2.0","babel-loader":"8.0.4","case-sensitive-paths-webpack-plugin":"2.1.2","compression":"1.7.4","es6-promise":"4.2.5","eventsource":"1.0.7","express":"4.16.4","figlet":"1.2.1","flatted":"^2.0.1","friendly-errors-webpack-plugin":"1.7.0","hard-source-webpack-plugin":"0.13.1","helmet":"3.14.0","hoist-non-react-statics":"^3.3.0","isomorphic-fetch":"2.2.1","loader-utils":"1.1.0","node-notifier":"5.2.1","nodemon":"1.18.4","react-dom":"16.8.4","react-helmet":"5.2.0","react-loadable":"5.5.0","react-router-config":"5.0.0","react-router-dom":"5.0.0","rimraf":"2.6.3","source-map-support":"0.5.9","terser-webpack-plugin":"1.1.0","webpack":"4.29.0","webpack-bundle-analyzer":"3.0.3","webpack-cli":"3.1.2","webpack-dev-middleware":"3.4.0","webpack-hot-middleware":"2.24.3","webpackbar":"2.6.3"},"peerDependencies":{"react":"^16.8.4"},"devDependencies":{"@babel/preset-typescript":"^7.1.0","@types/webpack-bundle-analyzer":"^2.13.0","babel-plugin-module-resolver":"^3.1.1","gulp":"github:gulpjs/gulp#4.0","gulp-babel":"^8.0.0","gulp-clean":"^0.4.0","gulp-sourcemaps":"^2.6.4","gulp-uglify":"^3.0.1","gulp-watch":"^5.0.1","react":"^16.8.4"},"gitHead":"a227dbaac22851b28922917dc7408fb7d6dfdc17","readme":"[![Build Status](https://travis-ci.org/airyrooms/maleo.js.svg?branch=master)](https://travis-ci.org/airyrooms/maleo.js)\n[![Coverage Status](https://coveralls.io/repos/github/airyrooms/maleo.js/badge.svg?branch=master)](https://coveralls.io/github/airyrooms/maleo.js?branch=master)\n[![npm version](https://badge.fury.io/js/%40airy%2Fmaleo.svg)](https://badge.fury.io/js/%40airy%2Fmaleo)\n[![Issues](http://img.shields.io/github/issues/airyrooms/maleo.js.svg)]( https://github.com/airyrooms/maleo.js/issues)\n[![MIT license](https://img.shields.io/badge/License-MIT-blue.svg)](https://github.com/airyrooms/maleo.js/blob/canary/LICENSE)\n[![Discord Chat](https://img.shields.io/discord/550498214789513218.svg)](https://discord.gg/9eArCQn)\n[![Conventional Commits](https://img.shields.io/badge/Conventional%20Commits-1.0.0-green.svg)](https://conventionalcommits.org)\n\n\n# Welcome to Maleo.js\n\nMaleo.js is an un-opinionated framework to enable Universal Rendering in JavaScript using React with no hassle.\n\nWe are here to solve the time consuming setups Universal Rendering Required.\n\n---\n\nReadme below is the documentation for the `canary` (prerelease) branch. To view the documentation for the latest stable Maleo.js version change branch to `master`\n\n---\n\n# Table of Contents\n- [Features](#features)\n- [Setup](#setup)\n- [Component Lifecycle](#component-lifecycle)\n- [Routing](#routing)\n- [Dynamic Import Component](#dynamic-import-component)\n  - [Tips](#tips)\n  - [Preloading](#preloading)\n- [Customizable Component](#customizable-component)\n  - [Custom Document](#custom-document)\n  - [Custom Wrap](#custom-wrap)\n- [Custom Configuration](#custom-configuration)\n  - [Customize Server](#customize-server)\n  - [Customize Webpack](#customize-webpack)\n  - [Customize Babel Config](#customize-babel-config)\n- [Core Utilities Functions](#core-utilities-functions)\n- [CDN Support](#cdn-support)\n- [Plugins](#plugins)\n- [FAQ](#faq)\n- [Contributing](#contributing)\n\n---\n\n## Features\n- Universal Rendering\n- Plugin based framework\n- Customizable\n\n## Setup\n\nInstall Maleo.js \n\n>*for now change `@airy/maleo` to `@airy/maleo@canary` until we publish the stable version of this package*\n\n\n**NPM**\n```bash\n$ npm install --save @airy/maleo react\n```\n**Yarn**\n```bash\n$ yarn add @airy/maleo react\n```\n\nAdd this script to your `package.json`\n```json\n{\n  \"scripts\": {\n    \"dev\": \"maleo dev\",\n    \"build\": \"export NODE_ENV=production && maleo build\",\n    \"start\": \"export NODE_ENV=production && maleo run\"\n  }\n}\n```\n\nCreate a page Root component\n```jsx\n// ./src/Root.jsx\nimport React from 'react';\n\n// Export default is required for registering page\nexport default class RootComponent extends React.Component {\n  render() {\n    return (\n      <h1>Hello World!</h1>\n    )\n  }\n}\n```\n\nAnd lastly, create a routing file on your project root directory called `routes.json` and register your page component\n```json\n[\n  {\n    \"path\": \"/\",\n    \"page\": \"./src/Root\"\n  }\n]\n```\n\nAfter that you can now run `$ npm run dev` and go to `http://localhost:3000`.\n\nYou should now see your app running on your browser.\n\nBy now you should see \n- Automatic transpilation and bundling (with webpack and babel)\n- ~~Hot code reloading~~ [#17](https://github.com/airyrooms/maleo.js/issues/17)\n- Server rendering\n\nTo see how simple this is, check out the sample app!\n\n## Component Lifecycle \n\nMaleo.js added a new component lifecycle hook called `getInitialProps`, this function is called during Server Side Rendering (SSR) and during route changes on Client Side Rendering (CSR).\n\nThis is useful especially for SEO purposes. \n\nExample for stateful component:\n```jsx\nimport React from 'react';\n\nexport default class extends React.Component {\n  static getInitialProps = async (ctx) => {\n    const { req } = ctx;\n\n    // check if getInitialProps is called on server or on client\n    const userAgent = req ? req.headers['user-agent'] : navigator.userAgent;\n\n    // the return value will be passed as props for this component\n    return { userAgent };\n  }\n\n  render() {\n    return (\n      <div>\n        Hello World {this.props.userAgent}\n      </div>\n    );\n  }\n}\n```\n\nExample for stateless component:\n```jsx\nconst Component = (props) => (\n  <div>\n    Hello World {props.userAgent}\n  </div>\n);\n\nComponent.getInitialprops = async (ctx) => {\n  const { req } = ctx;\n\n  const userAgent = req ? req.headers['user-agent'] : navigator.userAgent;\n\n  return { userAgent };\n};\n\nexport default Component;\n```\n\n`getInitialProps` receives a context object with the following properties:\n- `req` - HTTP request object (server only)\n- `res` - HTTP response object (server only)\n- `...wrapProps` - Spreaded properties from custom Wrap\n- `...appProps` - Spreaded properties from custom App\n\n## Routing\n\nRouting is declared in a centralized route config.\nRegister all the route config in `routes.json` file.\n\nIf you put the `routes.json` files on root directory, Maleo will automatically register your route. Otherwise put path to your routes on [Maleo config](#custom-configuration).\n\nRoutes file has to export default the route configuration.\nThe route object **expected to have distinct key** to indicate the route.\n\n<table>\n  <tr>\n    <td>Key</td>\n    <td>Type</td>\n    <td>Description</td>\n  </tr>\n  <tr>\n    <td><code>path</code></td>\n    <td><code>String!</code></td>\n    <td>Routes path</td>\n  </tr>\n  <tr>\n    <td><code>page</code></td>\n    <td><code>String!</code></td>\n    <td>Path to React Component for this route path</td>\n  </tr>\n  <tr>\n    <td><code>exact</code></td>\n    <td><code>Boolean?</code> [<code>false</code>]</td>\n    <td>To make url has to match exactly the path in order to render the component. Give <code>false</code> value if the route is a wrapper route component</td>\n  </tr>\n  <tr>\n    <td><code>routes</code></td>\n    <td><code>RouteObject?</code></td>\n    <td>Nested route</td>\n  </tr>\n</table>\n\n\nFor example:\n```json\n[\n  {\n    \"page\": \"./src/MainApp\",\n    \"routes\": [\n      {\n        \"path\": \"/\",\n        \"page\": \"./src/Search\",\n        \"exact\": true\n      },\n      {\n        \"path\": \"/search\",\n        \"page\": \"./src/Search\",\n        \"routes\": [\n          {\n            \"path\": \"/search/hello\",\n            \"page\": \"./src/Detail\",\n            \"exact\": true\n          }\n        ]\n      },\n      {\n        \"path\": \"/detail\",\n        \"page\": \"./src/Detail\",\n        \"exact\": true\n      }\n    ]\n  }\n]\n```\n\n## Dynamic Import Component\nMaleo.js supports TC39 [dynamic import proposal](https://github.com/tc39/proposal-dynamic-import) for JavaScript.\n\nYou can think dynamic import as another way to split your code into manageable chunks. You can use our `Dynamic` function which utilizes [react loadable](https://github.com/jamiebuilds/react-loadable)\n\nFor Example\n```js\n// DynamicComponent.js\nimport Dynamic from '@airy/maleo/dynamic';\n\nexport default Dynamic({\n  loader: () => import( /* webpackChunkName:\"DynamicComponent\" */ './component/DynamicLoad'),\n})\n```\n\n### **Preloading**\n\nFor optimization purposes, you can also preload a component even before the component got rendered.\n\nFor example, if you want to load component when a button get pressed, you can start preloading the component when the user hovers the mouse over the button.\n\nThe component created by `Dynamic` exposes a [static `preload` method](https://github.com/jamiebuilds/react-loadable#loadablecomponentpreload).\n\n```jsx\nimport React from 'react';\nimport Dynamic from '@airy/maleo/dynamic';\n\nconst DynamicBar = Dynamic({\n  loader: () => import('./Bar'),\n  loading: LoadingCompoinent\n});\n\nclass MyComponent extends React.Component {\n  state = { showBar: false };\n\n  onClick = () => {\n    this.setState({ showBar: true });\n  };\n\n  onMouseOver = () => DynamicBar.preload();\n\n  render() {\n    return (\n      <div>\n        <button\n          onClick={this.onClick}\n          onMouseOver={this.onMouseOver}>\n          Show Bar\n        </button>\n        { this.state.showBar && <DynamicBar /> }\n      </div>\n    )\n  }\n}\n```\n\n## Customizable Component\n\n### Custom Document\n\nHighly inspired by what [Next.js](https://github.com/zeit/next.js) has done on their awesome template customization.\n\nMaleo.js also enable customization on `Document` as document's markup. So you don't need to include tags like `<html>`, `<head>`, `<body>`, etc. \n\nTo override the default behavior, you'll need to create a component that extends the `Document` React class provided by Maleo.\n\n```jsx\n// document.jsx\nimport React from 'react';\nimport { Document, Header, Main, Scripts } from '@airy/maleo/document';\n\nexport default class extends Document {\n  render() {\n    return (\n      <html>\n        <Header>\n          <title>Maleo JS</title>\n          <meta charset=\"utf-8\" />\n          <meta name=\"description\" content=\"Maleo.js is awesome!\" />\n\n          <style>\n            {` body { background-color: #fff } `}\n          </style>\n        </Header>\n\n        <body>\n          <Main />\n\n          <Scripts />\n\n          <ReduxScript />\n        </body>\n      </html>\n    );\n  }\n}\n```\n\n### Custom Wrap\n\nMaleo.js uses the `Wrap` component to initialize pages. `Wrap` contains React Router's Component. You can add HoC here to wrap the application and control the page initialization. Which allows you to do amazing things like:\n- Persisting layour between page changes\n- Keeping state when navigating pages\n- Custom error handling using `componentDidCatch`\n- Inject additional data into pages (like Redux provider, etc)\n\nTo override the default behavior, you'll need to create a component that extends the `Wrap` React class provided by Maleo.\n\n```jsx\n// wrap.jsx\nimport React from 'react';\nimport { Wrap } from '@airy/maleo/wrap';\n\n// Redux plugin for Maleo.js\n// Hoc that creates store and add Redux Provider\nimport { withRedux } from '@airy/maleo-redux-plugin';\n\n// Custom Wrapper that will be rendered for the whole Application\nimport Content from './component/Content';\nimport NavigationBar from './component/NavigationBar';\n\nimport { createStore } from './store';\n\n@withRedux(createStore)\nexport default class extends Wrap {\n  static getInitialProps = (ctx) => {\n    const { store } = ctx\n    // you receive store from context\n    // you can access or do something with the store here\n    console.log('Initialized Redux Store', store);\n    return {}\n  }\n\n  render() {\n    const { Container, containerProps, App, appProps } = this.props\n\n    return (\n      <Container {...containerProps}>\n        <NavigationBar />\n        <Content>\n          <App {...appProps}/>\n        </Content>\n      </Container>\n    )\n  }\n}\n\n```\nIf you put `document.jsx` and `wrap.jsx` on root directory (the same level with `package.json`), then Maleo will automatically register your custom Document and Wrap. Otherwise, you can add the path to your custom Document and Wrap on [Maleo config](#custom-configuration)\n\n---\n***We are also working on adding default and customizable `Error` component page***\n\n---\n\n## Custom Configuration\n\nFor more advanced configuration of Maleo.js, like `webpack` config, registering `plugins`, path to your routes, custom Document and Wrap, and adding `path alias`, you can create a `maleo.config.js` in the root of your project directory. (same directory with `package.json`)\n\n```js\n// maleo.config.js\n\nmodule.exports = {\n  /* config options here */\n}\n```\n\nHere are the API's for the configuration:\n\n<table>\n  <tr>\n    <td>Key</td>\n    <td>Type</td>\n    <td>Description</td>\n  </tr>\n  <tr>\n    <td><code>favicon</code></td>\n    <td><code>String</code> [<code>{project-dir}/favicon.ico</code>]</td>\n    <td>Path to favicon file</td>\n  </tr>\n  <tr>\n    <td><code>buildDir</code></td>\n    <td><code>String?</code> [<code>.maleo</code>]</td>\n    <td>Directory to put Maleo.js' build assets</td>\n  </tr>\n  <tr>\n    <td><code>cache</code></td>\n    <td><code>Boolean?</code> [<code>true</code>]</td>\n    <td>Enable webpack build caching</td>\n  </tr>\n  <tr>\n    <td><code>isDev</code></td>\n    <td><code>Boolean?</code> [<code>process.env.NODE_ENV === 'development'</code>]</td>\n    <td>Enable development build configuration</td>\n  </tr>\n  <tr>\n    <td><code>sourceMaps</code></td>\n    <td><code>Boolean?</code> [<code>true</code>]</td>\n    <td>Enable webpack to generate source maps</td>\n  </tr>\n  <tr>\n    <td><code>alias</code></td>\n    <td><code>Object?</code></td>\n    <td>A key value pair for aliasing path directory\n      <br>\n      <code>\n      { 'component': './src/component' }\n      </code>\n    </td>\n  </tr>\n  <tr>\n    <td><code>publicPath</code></td>\n    <td><code>String?</code> [<code>/_assets/</code>]</td>\n    <td>To customize webpack's <code>publicPath</code> \n    <br>Comes in handy if using CDN to put built assets</td>\n  </tr>\n  <tr>\n    <td><code>analyzeBundle</code></td>\n    <td><code>Boolean?</code> [<code>false</code>]</td>\n    <td>To enable webpack's bundle analyzer, for analyzing bundle sizes during bundle debugging should Maleo.js' build process got slow</td>\n  </tr>\n  <tr>\n    <td><code>webpack</code></td>\n    <td><code>Function?</code></td>\n    <td>To customize webpack configuration, more details <a href=\"#customize-webpack\">here</a></td>\n  </tr>\n  <tr>\n    <td><code>routes</code></td>\n    <td><code>string?</code> [<code>rootDir/routes.jsx</code>]</td>\n    <td>Path to your routes file</td>\n  </tr>\n  <tr>\n    <td><code>customDocument</code></td>\n    <td><code>string?</code> [<code>rootDir/document.jsx</code>]</td>\n    <td>Path to your custom document file</td>\n  </tr>\n  <tr>\n    <td><code>customWrap</code></td>\n    <td><code>string?</code> [<code>rootDir/wrap.jsx</code>]</td>\n    <td>Path to your custom wrap file</td>\n  </tr>\n  <tr>\n    <td><code>customApp</code></td>\n    <td><code>string?</code> [<code>rootDir/app.jsx</code>]</td>\n    <td>Path to your custom app file</td>\n  </tr>\n  <tr>\n    <td><code>csp</code></td>\n    <td><code>boolean | cspConfig</code> [<code>false</code>]</td>\n    <td>Config for <a href=\"https://developers.google.com/web/fundamentals/security/csp/\">Content Security Policy</a>, using <a href=\"https://helmetjs.github.io/docs/csp/\">Helmet-CSP</a>\n    <br/>\n    If set to true it will enable default config:\n    <br/>\n    <code>{\n      directives: {\n        defaultSrc: [`'self'`],\n        styleSrc: [`'self'`],\n      },\n    }</code>\n    </td>\n  </tr>\n</table>\n\n#### Customize Server\n\nCreate a `server.js` file on root directory where your `package.json` lives.\nHere you can customize Maleo's server.\n```js\nimport { Server } from '@airy/maleo/server';\n\nconst PORT = process.env.PORT || 3000;\n\nconst maleoServer = Server.init({\n  port: PORT,\n  runHandler: () => {\n    console.log('Server running on port :', PORT);\n  }\n});\n\nmaleoServer.run();\n```\n\nHere are the API's for the configuration:\n\n<table>\n  <tr>\n    <td>Key</td>\n    <td>Type</td>\n    <td>Description</td>\n  </tr>\n  <tr>\n    <td><code>port</code></td>\n    <td><code>Number?</code> [<code>3000</code>]</td>\n    <td>Port to run Maleo server</td>\n  </tr>\n  <tr>\n    <td><code>assetDir</code></td>\n    <td><code>String?</code> [<code>'<root>/.maleo/client'</code>]</td>\n    <td>Directory for all client related assets</td>\n  </tr>\n  <tr>\n    <td><code>runHandler</code></td>\n    <td><code>Function?</code></td>\n    <td>Function called when maleo server starter</td>\n  </tr>\n</table>\n\n#### Customize Webpack\n\nYou are able to extend Maleo.js' default webpack configuration by defining a function on `maleo.config.js`\n\n```js\n// maleo.config.js\n\nmodule.exports = {\n  webpack(config, context, next) {\n    // Perform customizations to webpack config\n    // Important: This call is required in order to let Maleo pass the config to webpack\n    return next(); \n  },\n};\n```\n\nWebpack function will receive three arguments:\n\n<table>\n  <tr>\n    <td>Argument</td>\n    <td>Details</td>\n  <tr>\n  <tr>\n    <td><code>config</code></td>\n    <td>This contains webpack configuration object that you can manipulate</td>\n  <tr>\n  <tr>\n    <td><code>context</code></td>\n    <td>This contains some keys that are useful for the build context\n      <br>\n      <table>\n        <tr>\n          <td><code>isDev</code></td>\n          <td><code>Boolean</code></td>\n          <td>Check if current build is for development</td>\n        </tr>\n        <tr>\n          <td><code>publicPath</code></td>\n          <td><code>String</code></td>\n          <td>Public Path of user defined or default's value</td>\n        </tr>\n        <tr>\n          <td><code>analyzeBundle</code></td>\n          <td><code>Boolean</code></td>\n          <td>Check if analyze bundle is enabled</td>\n        </tr>\n        <tr>\n          <td><code>buildDirectory</code></td>\n          <td><code>String</code></td>\n          <td>Build Directory of user defined or default's value</td>\n        </tr>\n        <tr>\n          <td><code>name</code></td>\n          <td><code>String</code></td>\n          <td>Build name <code>'server' || 'client'</code></td>\n        </tr>\n      </table>\n    </td>\n  <tr>\n  <tr>\n    <td><code>next</code></td>\n    <td>A callback required to be called to pass the custom configuration</td>\n  <tr>\n</table>\n\nExample of adding `ts-loader` through `maleo.config.js`:\n\n```js\n// maleo.config.js\n\n// Partly taken and modified from @airy/maleo-ts-plugin source\n// for simplicity purposes\n\nmodule.exports = {\n  webpack(config, context, next) {\n    const { isDev } = context\n\n    config.module.rules.push({\n      test: /\\.tsx?/,\n      exclude: /node_modules/,\n      use: [\n        require.resolve('@airy/maleo/lib/build/loaders/maleo-babel-loader'),\n        {\n          loader: 'ts-loader',\n          options: {\n            transpileOnly: true,\n          },\n        },\n      ],\n\n      if (isDev) {\n        config.plugins.push(new ForkTSCheckerWebpackPlugin());\n      }\n\n      return next();\n    })\n  },\n};\n```\n\n#### Customize Babel Config\n\nMaleo.js also let you have your own babel config. Just simply add `.babelrc` file at the root directory of your app.\n\nYou can include Maleo.js' babel preset in order to have latest JavaScript preset.\n\nHere's an example of `.babelrc` file:\n```json\n{\n  \"presets\": [\"@airy/maleo/babel\"],\n  \"plugins\": []\n}\n```\n\nThe `@airy/maleo/babel` preset includes everything you need to get your development started. The preset includes:\n- `@babel/preset-env`\n- `@babel/preset-react`\n- `@babel/plugin-proposal-class-properties`\n- `@babel/plugin-proposal-decorators`\n- `@babel/plugin-proposal-object-rest-spread`\n- `@babel/plugin-transform-runtime`\n- `react-loadable/babel`\n\n## Core Utilities Functions\n\n#### Route Matching\n\nTo use you can import the matching routes function like this:\n```js\nimport { getMatchedRoutes } from '@airy/maleo/utils';\n```\n\nThe function will return all matched routes and the route object from `routes.json`\n\n## CDN Support\n\nIf you are using a CDN, you can set up the `publicPath` setting and configure your CDN's origin to resolve to the domain that Maleo.js is hosted on.\n\n```js\n// maleo.config.js\n\nconst isProd = process.env.NODE_ENV === 'production';\n\nmodule.exports = {\n  assetPrefix: isProd && 'https://cdn.example.com';\n}\n```\n\n## FAQ\n\n== TO BE DETERMINED == \n\n## Plugins\n- [css-plugin](https://github.com/airyrooms/maleo-plugins/tree/master/packages/css-plugin)\n- [redux-plugin](https://github.com/airyrooms/maleo-plugins/tree/master/packages/redux-plugin)\n- [typescript-plugin](https://github.com/airyrooms/maleo-plugins/tree/master/packages/typescript-plugin)\n- [more](https://github.com/airyrooms/maleo-plugins)\n\n\n## Contributing\n[Please follow these steps to contribute to Maleo.js](https://github.com/airyrooms/maleo.js/blob/canary/CONTRIBUTING.md)\n\n[Please follow these steps to contribute to Maleo.js' plugins](https://github.com/airyrooms/maleo-plugins#contributing-guidelines)\n\n\n## Contributors\n\nThis project exists thanks to all the people who contribute. [Contribute](CONTRIBUTING.md).\n\nMany thanks to our **[contributors](https://github.com/airyrooms/maleo.js/graphs/contributors)**!\n\n\n## License\n\nMIT","readmeFilename":"README.md","bugs":{"url":"https://github.com/airyrooms/maleo.js/issues"},"homepage":"https://github.com/airyrooms/maleo.js#readme","_id":"@airy/maleo@0.3.1-canary.24","_nodeVersion":"11.15.0","_npmVersion":"lerna/3.13.1/node@v11.15.0+x64 (linux)","dist":{"integrity":"sha512-wjVLF3jP1yNVTgbvQoUL3aZQFAztNIlhtbNAqKQKuRaX97ien9hRhMP5BKCqUOrlguo63HEDrU0F5qpL2KZxNg==","shasum":"52f6b3123fd16b6f2b0c9204ac8d1ca60383500d","tarball":"https://registry.npmjs.org/@airy/maleo/-/maleo-0.3.1-canary.24.tgz","fileCount":105,"unpackedSize":372212,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJdOBBDCRA9TVsSAnZWagAAqVsP/1NwYutEOHe674yLN1rg\nFDx0HvTyvCbOj+juBsH0zZVcquFw1e7ddjHwA+tB65+/myC0CtYOS+LBmyB1\nwd8/VCacNYML0Hz6yH7eouQkbVCWHQKr0AZ8UkxY9blRH1opY5sCvRsi9B16\nNrCLRygzOJoJRZhW/I3CV/ngKeBkBHm/68k5JC81G+jTa5UHs99zOWEsquyX\n7xQE+424TbAZYVIyx/F+GTV2r5dLt3+03cTcxjs7xKGjkLBZ7cqyIrPTIhKk\nx4HSj+XmBvj8rnVbnBphw78+g00JZ3axgqcK44R0Ppr4Q4b7ML2Ou2QTAsdB\noKiXKbNnYh16cLhE58P6OYjMFcbbLgXxURP3W9+LfMDmAhIGDbKE+3Bwq8Dw\n1CcKp0oUx2JNg7xdWJW105LnhQSJxCSUDl7iz/RRD2AsiK1RrS82A0KXl4qN\n1pXMjzkNPazFivTSPpSe6aonebqllChfT2RC7r2afmNlL+yGBy5bXfFRYp3S\noDCb2w17dMl1f6CE2IllMF6LFl1qTUR2JCPr45W6byiJqhpwBJVmVk3IqUNe\n6DLkLEQ16wsrKRThc8xr0D5yD3kuiTw8O0FaXZPzGsF0lI/zlfEwXrlPcoVR\nhgSidOjTeBj0ahAUD7qxU5CDSiT+yorotRIdtwbg1zd/Z0RGaeay+4p1+Ss7\nCoJM\r\n=tTm/\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDfyq1rpDVg4LIaaktw7aGPZLXaDaQr18h+tFxPlvVNhAIhAO1hpdOUI2/QMbE6aEQxtgXufmjVKhTntDRxpoExbK4l"}]},"maintainers":[{"name":"airyrooms-engineering","email":"engineering@airyrooms.com"}],"_npmUser":{"name":"airyrooms-engineering","email":"engineering@airyrooms.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/maleo_0.3.1-canary.24_1563955266697_0.8495939938444894"},"_hasShrinkwrap":false},"0.3.1-canary.25":{"name":"@airy/maleo","version":"0.3.1-canary.25","description":"Un-opinionated Universal Rendering Javascript Framework","repository":{"type":"git","url":"git+https://github.com/airyrooms/maleo.js.git","bugs":"https://github.com/airyrooms/maleo.js/issues"},"author":{"name":"Airy"},"license":"MIT","private":false,"sideEffects":false,"scripts":{"build":"rm -rf dist && gulp","watch":"gulp watch","prepublish":"yarn build","start":"node server.js"},"bin":{"maleo":"lib/bin/maleo.js"},"dependencies":{"@babel/core":"7.1.2","@babel/plugin-proposal-class-properties":"7.1.0","@babel/plugin-proposal-decorators":"7.1.2","@babel/plugin-proposal-object-rest-spread":"7.0.0","@babel/plugin-syntax-dynamic-import":"7.0.0","@babel/plugin-transform-runtime":"7.1.0","@babel/preset-env":"7.1.0","@babel/preset-react":"7.0.0","@babel/runtime":"7.1.2","@babel/runtime-corejs2":"7.2.0","babel-loader":"8.0.4","case-sensitive-paths-webpack-plugin":"2.1.2","compression":"1.7.4","es6-promise":"4.2.5","eventsource":"1.0.7","express":"4.16.4","figlet":"1.2.1","flatted":"^2.0.1","friendly-errors-webpack-plugin":"1.7.0","hard-source-webpack-plugin":"0.13.1","helmet":"3.14.0","hoist-non-react-statics":"^3.3.0","isomorphic-fetch":"2.2.1","loader-utils":"1.1.0","node-notifier":"5.2.1","nodemon":"1.18.4","react-dom":"16.8.4","react-helmet":"5.2.0","react-loadable":"5.5.0","react-router-config":"5.0.0","react-router-dom":"5.0.0","rimraf":"2.6.3","source-map-support":"0.5.9","terser-webpack-plugin":"1.1.0","webpack":"4.29.0","webpack-bundle-analyzer":"3.0.3","webpack-cli":"3.1.2","webpack-dev-middleware":"3.4.0","webpack-hot-middleware":"2.24.3","webpackbar":"2.6.3"},"peerDependencies":{"react":"^16.8.4"},"devDependencies":{"@babel/preset-typescript":"^7.1.0","@types/webpack-bundle-analyzer":"^2.13.0","babel-plugin-module-resolver":"^3.1.1","gulp":"github:gulpjs/gulp#4.0","gulp-babel":"^8.0.0","gulp-clean":"^0.4.0","gulp-sourcemaps":"^2.6.4","gulp-uglify":"^3.0.1","gulp-watch":"^5.0.1","react":"^16.8.4"},"gitHead":"cd5ce2ff76471aa50ded38b6fdce1f06f7e3a105","readme":"[![Build Status](https://travis-ci.org/airyrooms/maleo.js.svg?branch=master)](https://travis-ci.org/airyrooms/maleo.js)\n[![Coverage Status](https://coveralls.io/repos/github/airyrooms/maleo.js/badge.svg?branch=master)](https://coveralls.io/github/airyrooms/maleo.js?branch=master)\n[![npm version](https://badge.fury.io/js/%40airy%2Fmaleo.svg)](https://badge.fury.io/js/%40airy%2Fmaleo)\n[![Issues](http://img.shields.io/github/issues/airyrooms/maleo.js.svg)]( https://github.com/airyrooms/maleo.js/issues)\n[![MIT license](https://img.shields.io/badge/License-MIT-blue.svg)](https://github.com/airyrooms/maleo.js/blob/canary/LICENSE)\n[![Discord Chat](https://img.shields.io/discord/550498214789513218.svg)](https://discord.gg/9eArCQn)\n[![Conventional Commits](https://img.shields.io/badge/Conventional%20Commits-1.0.0-green.svg)](https://conventionalcommits.org)\n\n\n# Welcome to Maleo.js\n\nMaleo.js is an un-opinionated framework to enable Universal Rendering in JavaScript using React with no hassle.\n\nWe are here to solve the time consuming setups Universal Rendering Required.\n\n---\n\nReadme below is the documentation for the `canary` (prerelease) branch. To view the documentation for the latest stable Maleo.js version change branch to `master`\n\n---\n\n# Table of Contents\n- [Features](#features)\n- [Setup](#setup)\n- [Component Lifecycle](#component-lifecycle)\n- [Routing](#routing)\n- [Dynamic Import Component](#dynamic-import-component)\n  - [Tips](#tips)\n  - [Preloading](#preloading)\n- [Customizable Component](#customizable-component)\n  - [Custom Document](#custom-document)\n  - [Custom Wrap](#custom-wrap)\n- [Custom Configuration](#custom-configuration)\n  - [Customize Server](#customize-server)\n  - [Customize Webpack](#customize-webpack)\n  - [Customize Babel Config](#customize-babel-config)\n- [Core Utilities Functions](#core-utilities-functions)\n- [CDN Support](#cdn-support)\n- [Plugins](#plugins)\n- [FAQ](#faq)\n- [Contributing](#contributing)\n\n---\n\n## Features\n- Universal Rendering\n- Plugin based framework\n- Customizable\n\n## Setup\n\nInstall Maleo.js \n\n>*for now change `@airy/maleo` to `@airy/maleo@canary` until we publish the stable version of this package*\n\n\n**NPM**\n```bash\n$ npm install --save @airy/maleo react\n```\n**Yarn**\n```bash\n$ yarn add @airy/maleo react\n```\n\nAdd this script to your `package.json`\n```json\n{\n  \"scripts\": {\n    \"dev\": \"maleo dev\",\n    \"build\": \"export NODE_ENV=production && maleo build\",\n    \"start\": \"export NODE_ENV=production && maleo run\"\n  }\n}\n```\n\nCreate a page Root component\n```jsx\n// ./src/Root.jsx\nimport React from 'react';\n\n// Export default is required for registering page\nexport default class RootComponent extends React.Component {\n  render() {\n    return (\n      <h1>Hello World!</h1>\n    )\n  }\n}\n```\n\nAnd lastly, create a routing file on your project root directory called `routes.json` and register your page component\n```json\n[\n  {\n    \"path\": \"/\",\n    \"page\": \"./src/Root\"\n  }\n]\n```\n\nAfter that you can now run `$ npm run dev` and go to `http://localhost:3000`.\n\nYou should now see your app running on your browser.\n\nBy now you should see \n- Automatic transpilation and bundling (with webpack and babel)\n- ~~Hot code reloading~~ [#17](https://github.com/airyrooms/maleo.js/issues/17)\n- Server rendering\n\nTo see how simple this is, check out the sample app!\n\n## Component Lifecycle \n\nMaleo.js added a new component lifecycle hook called `getInitialProps`, this function is called during Server Side Rendering (SSR) and during route changes on Client Side Rendering (CSR).\n\nThis is useful especially for SEO purposes. \n\nExample for stateful component:\n```jsx\nimport React from 'react';\n\nexport default class extends React.Component {\n  static getInitialProps = async (ctx) => {\n    const { req } = ctx;\n\n    // check if getInitialProps is called on server or on client\n    const userAgent = req ? req.headers['user-agent'] : navigator.userAgent;\n\n    // the return value will be passed as props for this component\n    return { userAgent };\n  }\n\n  render() {\n    return (\n      <div>\n        Hello World {this.props.userAgent}\n      </div>\n    );\n  }\n}\n```\n\nExample for stateless component:\n```jsx\nconst Component = (props) => (\n  <div>\n    Hello World {props.userAgent}\n  </div>\n);\n\nComponent.getInitialprops = async (ctx) => {\n  const { req } = ctx;\n\n  const userAgent = req ? req.headers['user-agent'] : navigator.userAgent;\n\n  return { userAgent };\n};\n\nexport default Component;\n```\n\n`getInitialProps` receives a context object with the following properties:\n- `req` - HTTP request object (server only)\n- `res` - HTTP response object (server only)\n- `...wrapProps` - Spreaded properties from custom Wrap\n- `...appProps` - Spreaded properties from custom App\n\n## Routing\n\nRouting is declared in a centralized route config.\nRegister all the route config in `routes.json` file.\n\nIf you put the `routes.json` files on root directory, Maleo will automatically register your route. Otherwise put path to your routes on [Maleo config](#custom-configuration).\n\nRoutes file has to export default the route configuration.\nThe route object **expected to have distinct key** to indicate the route.\n\n<table>\n  <tr>\n    <td>Key</td>\n    <td>Type</td>\n    <td>Description</td>\n  </tr>\n  <tr>\n    <td><code>path</code></td>\n    <td><code>String!</code></td>\n    <td>Routes path</td>\n  </tr>\n  <tr>\n    <td><code>page</code></td>\n    <td><code>String!</code></td>\n    <td>Path to React Component for this route path</td>\n  </tr>\n  <tr>\n    <td><code>exact</code></td>\n    <td><code>Boolean?</code> [<code>false</code>]</td>\n    <td>To make url has to match exactly the path in order to render the component. Give <code>false</code> value if the route is a wrapper route component</td>\n  </tr>\n  <tr>\n    <td><code>routes</code></td>\n    <td><code>RouteObject?</code></td>\n    <td>Nested route</td>\n  </tr>\n</table>\n\n\nFor example:\n```json\n[\n  {\n    \"page\": \"./src/MainApp\",\n    \"routes\": [\n      {\n        \"path\": \"/\",\n        \"page\": \"./src/Search\",\n        \"exact\": true\n      },\n      {\n        \"path\": \"/search\",\n        \"page\": \"./src/Search\",\n        \"routes\": [\n          {\n            \"path\": \"/search/hello\",\n            \"page\": \"./src/Detail\",\n            \"exact\": true\n          }\n        ]\n      },\n      {\n        \"path\": \"/detail\",\n        \"page\": \"./src/Detail\",\n        \"exact\": true\n      }\n    ]\n  }\n]\n```\n\n## Dynamic Import Component\nMaleo.js supports TC39 [dynamic import proposal](https://github.com/tc39/proposal-dynamic-import) for JavaScript.\n\nYou can think dynamic import as another way to split your code into manageable chunks. You can use our `Dynamic` function which utilizes [react loadable](https://github.com/jamiebuilds/react-loadable)\n\nFor Example\n```js\n// DynamicComponent.js\nimport Dynamic from '@airy/maleo/dynamic';\n\nexport default Dynamic({\n  loader: () => import( /* webpackChunkName:\"DynamicComponent\" */ './component/DynamicLoad'),\n})\n```\n\n### **Preloading**\n\nFor optimization purposes, you can also preload a component even before the component got rendered.\n\nFor example, if you want to load component when a button get pressed, you can start preloading the component when the user hovers the mouse over the button.\n\nThe component created by `Dynamic` exposes a [static `preload` method](https://github.com/jamiebuilds/react-loadable#loadablecomponentpreload).\n\n```jsx\nimport React from 'react';\nimport Dynamic from '@airy/maleo/dynamic';\n\nconst DynamicBar = Dynamic({\n  loader: () => import('./Bar'),\n  loading: LoadingCompoinent\n});\n\nclass MyComponent extends React.Component {\n  state = { showBar: false };\n\n  onClick = () => {\n    this.setState({ showBar: true });\n  };\n\n  onMouseOver = () => DynamicBar.preload();\n\n  render() {\n    return (\n      <div>\n        <button\n          onClick={this.onClick}\n          onMouseOver={this.onMouseOver}>\n          Show Bar\n        </button>\n        { this.state.showBar && <DynamicBar /> }\n      </div>\n    )\n  }\n}\n```\n\n## Customizable Component\n\n### Custom Document\n\nHighly inspired by what [Next.js](https://github.com/zeit/next.js) has done on their awesome template customization.\n\nMaleo.js also enable customization on `Document` as document's markup. So you don't need to include tags like `<html>`, `<head>`, `<body>`, etc. \n\nTo override the default behavior, you'll need to create a component that extends the `Document` React class provided by Maleo.\n\n```jsx\n// document.jsx\nimport React from 'react';\nimport { Document, Header, Main, Scripts } from '@airy/maleo/document';\n\nexport default class extends Document {\n  render() {\n    return (\n      <html>\n        <Header>\n          <title>Maleo JS</title>\n          <meta charset=\"utf-8\" />\n          <meta name=\"description\" content=\"Maleo.js is awesome!\" />\n\n          <style>\n            {` body { background-color: #fff } `}\n          </style>\n        </Header>\n\n        <body>\n          <Main />\n\n          <Scripts />\n\n          <ReduxScript />\n        </body>\n      </html>\n    );\n  }\n}\n```\n\n### Custom Wrap\n\nMaleo.js uses the `Wrap` component to initialize pages. `Wrap` contains React Router's Component. You can add HoC here to wrap the application and control the page initialization. Which allows you to do amazing things like:\n- Persisting layour between page changes\n- Keeping state when navigating pages\n- Custom error handling using `componentDidCatch`\n- Inject additional data into pages (like Redux provider, etc)\n\nTo override the default behavior, you'll need to create a component that extends the `Wrap` React class provided by Maleo.\n\n```jsx\n// wrap.jsx\nimport React from 'react';\nimport { Wrap } from '@airy/maleo/wrap';\n\n// Redux plugin for Maleo.js\n// Hoc that creates store and add Redux Provider\nimport { withRedux } from '@airy/maleo-redux-plugin';\n\n// Custom Wrapper that will be rendered for the whole Application\nimport Content from './component/Content';\nimport NavigationBar from './component/NavigationBar';\n\nimport { createStore } from './store';\n\n@withRedux(createStore)\nexport default class extends Wrap {\n  static getInitialProps = (ctx) => {\n    const { store } = ctx\n    // you receive store from context\n    // you can access or do something with the store here\n    console.log('Initialized Redux Store', store);\n    return {}\n  }\n\n  render() {\n    const { Container, containerProps, App, appProps } = this.props\n\n    return (\n      <Container {...containerProps}>\n        <NavigationBar />\n        <Content>\n          <App {...appProps}/>\n        </Content>\n      </Container>\n    )\n  }\n}\n\n```\nIf you put `document.jsx` and `wrap.jsx` on root directory (the same level with `package.json`), then Maleo will automatically register your custom Document and Wrap. Otherwise, you can add the path to your custom Document and Wrap on [Maleo config](#custom-configuration)\n\n---\n***We are also working on adding default and customizable `Error` component page***\n\n---\n\n## Custom Configuration\n\nFor more advanced configuration of Maleo.js, like `webpack` config, registering `plugins`, path to your routes, custom Document and Wrap, and adding `path alias`, you can create a `maleo.config.js` in the root of your project directory. (same directory with `package.json`)\n\n```js\n// maleo.config.js\n\nmodule.exports = {\n  /* config options here */\n}\n```\n\nHere are the API's for the configuration:\n\n<table>\n  <tr>\n    <td>Key</td>\n    <td>Type</td>\n    <td>Description</td>\n  </tr>\n  <tr>\n    <td><code>favicon</code></td>\n    <td><code>String</code> [<code>{project-dir}/favicon.ico</code>]</td>\n    <td>Path to favicon file</td>\n  </tr>\n  <tr>\n    <td><code>buildDir</code></td>\n    <td><code>String?</code> [<code>.maleo</code>]</td>\n    <td>Directory to put Maleo.js' build assets</td>\n  </tr>\n  <tr>\n    <td><code>cache</code></td>\n    <td><code>Boolean?</code> [<code>true</code>]</td>\n    <td>Enable webpack build caching</td>\n  </tr>\n  <tr>\n    <td><code>isDev</code></td>\n    <td><code>Boolean?</code> [<code>process.env.NODE_ENV === 'development'</code>]</td>\n    <td>Enable development build configuration</td>\n  </tr>\n  <tr>\n    <td><code>sourceMaps</code></td>\n    <td><code>Boolean?</code> [<code>true</code>]</td>\n    <td>Enable webpack to generate source maps</td>\n  </tr>\n  <tr>\n    <td><code>alias</code></td>\n    <td><code>Object?</code></td>\n    <td>A key value pair for aliasing path directory\n      <br>\n      <code>\n      { 'component': './src/component' }\n      </code>\n    </td>\n  </tr>\n  <tr>\n    <td><code>publicPath</code></td>\n    <td><code>String?</code> [<code>/_assets/</code>]</td>\n    <td>To customize webpack's <code>publicPath</code> \n    <br>Comes in handy if using CDN to put built assets</td>\n  </tr>\n  <tr>\n    <td><code>analyzeBundle</code></td>\n    <td><code>Boolean?</code> [<code>false</code>]</td>\n    <td>To enable webpack's bundle analyzer, for analyzing bundle sizes during bundle debugging should Maleo.js' build process got slow</td>\n  </tr>\n  <tr>\n    <td><code>webpack</code></td>\n    <td><code>Function?</code></td>\n    <td>To customize webpack configuration, more details <a href=\"#customize-webpack\">here</a></td>\n  </tr>\n  <tr>\n    <td><code>routes</code></td>\n    <td><code>string?</code> [<code>rootDir/routes.jsx</code>]</td>\n    <td>Path to your routes file</td>\n  </tr>\n  <tr>\n    <td><code>customDocument</code></td>\n    <td><code>string?</code> [<code>rootDir/document.jsx</code>]</td>\n    <td>Path to your custom document file</td>\n  </tr>\n  <tr>\n    <td><code>customWrap</code></td>\n    <td><code>string?</code> [<code>rootDir/wrap.jsx</code>]</td>\n    <td>Path to your custom wrap file</td>\n  </tr>\n  <tr>\n    <td><code>customApp</code></td>\n    <td><code>string?</code> [<code>rootDir/app.jsx</code>]</td>\n    <td>Path to your custom app file</td>\n  </tr>\n  <tr>\n    <td><code>csp</code></td>\n    <td><code>boolean | cspConfig</code> [<code>false</code>]</td>\n    <td>Config for <a href=\"https://developers.google.com/web/fundamentals/security/csp/\">Content Security Policy</a>, using <a href=\"https://helmetjs.github.io/docs/csp/\">Helmet-CSP</a>\n    <br/>\n    If set to true it will enable default config:\n    <br/>\n    <code>{\n      directives: {\n        defaultSrc: [`'self'`],\n        styleSrc: [`'self'`],\n      },\n    }</code>\n    </td>\n  </tr>\n</table>\n\n#### Customize Server\n\nCreate a `server.js` file on root directory where your `package.json` lives.\nHere you can customize Maleo's server.\n```js\nimport { Server } from '@airy/maleo/server';\n\nconst PORT = process.env.PORT || 3000;\n\nconst maleoServer = Server.init({\n  port: PORT,\n  runHandler: () => {\n    console.log('Server running on port :', PORT);\n  }\n});\n\nmaleoServer.run();\n```\n\nHere are the API's for the configuration:\n\n<table>\n  <tr>\n    <td>Key</td>\n    <td>Type</td>\n    <td>Description</td>\n  </tr>\n  <tr>\n    <td><code>port</code></td>\n    <td><code>Number?</code> [<code>3000</code>]</td>\n    <td>Port to run Maleo server</td>\n  </tr>\n  <tr>\n    <td><code>assetDir</code></td>\n    <td><code>String?</code> [<code>'<root>/.maleo/client'</code>]</td>\n    <td>Directory for all client related assets</td>\n  </tr>\n  <tr>\n    <td><code>runHandler</code></td>\n    <td><code>Function?</code></td>\n    <td>Function called when maleo server starter</td>\n  </tr>\n</table>\n\n#### Customize Webpack\n\nYou are able to extend Maleo.js' default webpack configuration by defining a function on `maleo.config.js`\n\n```js\n// maleo.config.js\n\nmodule.exports = {\n  webpack(config, context, next) {\n    // Perform customizations to webpack config\n    // Important: This call is required in order to let Maleo pass the config to webpack\n    return next(); \n  },\n};\n```\n\nWebpack function will receive three arguments:\n\n<table>\n  <tr>\n    <td>Argument</td>\n    <td>Details</td>\n  <tr>\n  <tr>\n    <td><code>config</code></td>\n    <td>This contains webpack configuration object that you can manipulate</td>\n  <tr>\n  <tr>\n    <td><code>context</code></td>\n    <td>This contains some keys that are useful for the build context\n      <br>\n      <table>\n        <tr>\n          <td><code>isDev</code></td>\n          <td><code>Boolean</code></td>\n          <td>Check if current build is for development</td>\n        </tr>\n        <tr>\n          <td><code>publicPath</code></td>\n          <td><code>String</code></td>\n          <td>Public Path of user defined or default's value</td>\n        </tr>\n        <tr>\n          <td><code>analyzeBundle</code></td>\n          <td><code>Boolean</code></td>\n          <td>Check if analyze bundle is enabled</td>\n        </tr>\n        <tr>\n          <td><code>buildDirectory</code></td>\n          <td><code>String</code></td>\n          <td>Build Directory of user defined or default's value</td>\n        </tr>\n        <tr>\n          <td><code>name</code></td>\n          <td><code>String</code></td>\n          <td>Build name <code>'server' || 'client'</code></td>\n        </tr>\n      </table>\n    </td>\n  <tr>\n  <tr>\n    <td><code>next</code></td>\n    <td>A callback required to be called to pass the custom configuration</td>\n  <tr>\n</table>\n\nExample of adding `ts-loader` through `maleo.config.js`:\n\n```js\n// maleo.config.js\n\n// Partly taken and modified from @airy/maleo-ts-plugin source\n// for simplicity purposes\n\nmodule.exports = {\n  webpack(config, context, next) {\n    const { isDev } = context\n\n    config.module.rules.push({\n      test: /\\.tsx?/,\n      exclude: /node_modules/,\n      use: [\n        require.resolve('@airy/maleo/lib/build/loaders/maleo-babel-loader'),\n        {\n          loader: 'ts-loader',\n          options: {\n            transpileOnly: true,\n          },\n        },\n      ],\n\n      if (isDev) {\n        config.plugins.push(new ForkTSCheckerWebpackPlugin());\n      }\n\n      return next();\n    })\n  },\n};\n```\n\n#### Customize Babel Config\n\nMaleo.js also let you have your own babel config. Just simply add `.babelrc` file at the root directory of your app.\n\nYou can include Maleo.js' babel preset in order to have latest JavaScript preset.\n\nHere's an example of `.babelrc` file:\n```json\n{\n  \"presets\": [\"@airy/maleo/babel\"],\n  \"plugins\": []\n}\n```\n\nThe `@airy/maleo/babel` preset includes everything you need to get your development started. The preset includes:\n- `@babel/preset-env`\n- `@babel/preset-react`\n- `@babel/plugin-proposal-class-properties`\n- `@babel/plugin-proposal-decorators`\n- `@babel/plugin-proposal-object-rest-spread`\n- `@babel/plugin-transform-runtime`\n- `react-loadable/babel`\n\n## Core Utilities Functions\n\n#### Route Matching\n\nTo use you can import the matching routes function like this:\n```js\nimport { getMatchedRoutes } from '@airy/maleo/utils';\n```\n\nThe function will return all matched routes and the route object from `routes.json`\n\n## CDN Support\n\nIf you are using a CDN, you can set up the `publicPath` setting and configure your CDN's origin to resolve to the domain that Maleo.js is hosted on.\n\n```js\n// maleo.config.js\n\nconst isProd = process.env.NODE_ENV === 'production';\n\nmodule.exports = {\n  assetPrefix: isProd && 'https://cdn.example.com';\n}\n```\n\n## FAQ\n\n== TO BE DETERMINED == \n\n## Plugins\n- [css-plugin](https://github.com/airyrooms/maleo-plugins/tree/master/packages/css-plugin)\n- [redux-plugin](https://github.com/airyrooms/maleo-plugins/tree/master/packages/redux-plugin)\n- [typescript-plugin](https://github.com/airyrooms/maleo-plugins/tree/master/packages/typescript-plugin)\n- [more](https://github.com/airyrooms/maleo-plugins)\n\n\n## Contributing\n[Please follow these steps to contribute to Maleo.js](https://github.com/airyrooms/maleo.js/blob/canary/CONTRIBUTING.md)\n\n[Please follow these steps to contribute to Maleo.js' plugins](https://github.com/airyrooms/maleo-plugins#contributing-guidelines)\n\n\n## Contributors\n\nThis project exists thanks to all the people who contribute. [Contribute](CONTRIBUTING.md).\n\nMany thanks to our **[contributors](https://github.com/airyrooms/maleo.js/graphs/contributors)**!\n\n\n## License\n\nMIT","readmeFilename":"README.md","bugs":{"url":"https://github.com/airyrooms/maleo.js/issues"},"homepage":"https://github.com/airyrooms/maleo.js#readme","_id":"@airy/maleo@0.3.1-canary.25","_nodeVersion":"10.16.0","_npmVersion":"lerna/3.13.1/node@v10.16.0+x64 (linux)","dist":{"integrity":"sha512-Nbd8E7nvnbwk+3i5ha0KBDnvfJ+dQuWh5lDOXZ9+tAhfWBiVPaBC5g32aj7WVItZEG54uPNfUJgXjEUTwBhphA==","shasum":"cf3b579c123fc01efda8fe6c432c854d45773001","tarball":"https://registry.npmjs.org/@airy/maleo/-/maleo-0.3.1-canary.25.tgz","fileCount":105,"unpackedSize":372490,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJdOmlRCRA9TVsSAnZWagAAghcP/A78kIdf74WhioIFL4PB\ns1h0smZs2CRdWzLQ1RKv0JzIDoHuwYAe2cRfR3jp3xnxCQtxlq05f6dbbsz0\n5t26pXTKIKYwUMKVnCHNEWCmwBPTaiE9FwTXZ6E4mN/y2G25hvMxQ2z4Wysl\nU36mt/U8tFjKc22AOBAL+qW7YFusUOPkzPkfQeHNnUFzCCNNUmQCpdNIUk6o\nTDwxZ81KyDy6QYPqy+ILwrzK3R6XxIkLdkxBPnfTn4UheLTZF93qOWV8qpnc\nS45vpj3m6RB7Vlqs6ME1q1AU8g1DjvxSffOzNof+vflYagLJ/qSrObl7cde3\n/GouiJK6tCWX2KuCLEMPv1Q8fIOkeN3NqJjsPrYqARnGpQB8BPHq5zaH+0O3\n5xL75ct5iZkdmDNlMFm74hyge39qygzabmPsLLEGyHqJg3ppN/hw3w7L1a3v\ns3dGMRAvvAymIkRyc3Cd1Nk89DaD7kQYJ2G9BW5E9Gr1xiP9PxpCWza3Uend\nAL2PUSDbe2ivf0i91cvAjCg5s+Ef12gztFbR9BC0mdV+lZa4qqwyQwWfu4mA\nF+vwViT7QSkHJLi20PrFgaWwjRAnv4b960x+ULC6KMtG/cCq91eSc60Y1FCa\nM0CRafK8A1TAYBCzpjMtX46rnS2fXTFtfWUsRDtJkeqmmRM1qGEl08do3Wup\npkWR\r\n=CIkU\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCICTUHJFbshcICQ2znHP5/eqiQDDz0GxVWFekMRRpseEjAiEAyLtvq1NAg3jaudDhEqM+sDEOQbVoXnwu88MHHQjbXks="}]},"maintainers":[{"name":"airyrooms-engineering","email":"engineering@airyrooms.com"}],"_npmUser":{"name":"airyrooms-engineering","email":"engineering@airyrooms.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/maleo_0.3.1-canary.25_1564109137050_0.9837053879642927"},"_hasShrinkwrap":false},"0.3.1-canary.26":{"name":"@airy/maleo","version":"0.3.1-canary.26","description":"Un-opinionated Universal Rendering Javascript Framework","repository":{"type":"git","url":"git+https://github.com/airyrooms/maleo.js.git","bugs":"https://github.com/airyrooms/maleo.js/issues"},"author":{"name":"Airy"},"license":"MIT","private":false,"sideEffects":false,"scripts":{"build":"rm -rf dist && gulp","watch":"gulp watch","prepublish":"yarn build","start":"node server.js"},"bin":{"maleo":"lib/bin/maleo.js"},"dependencies":{"@babel/core":"7.1.2","@babel/plugin-proposal-class-properties":"7.1.0","@babel/plugin-proposal-decorators":"7.1.2","@babel/plugin-proposal-object-rest-spread":"7.0.0","@babel/plugin-syntax-dynamic-import":"7.0.0","@babel/plugin-transform-runtime":"7.1.0","@babel/preset-env":"7.1.0","@babel/preset-react":"7.0.0","@babel/runtime":"7.1.2","@babel/runtime-corejs2":"7.2.0","babel-loader":"8.0.4","case-sensitive-paths-webpack-plugin":"2.1.2","compression":"1.7.4","es6-promise":"4.2.5","eventsource":"1.0.7","express":"4.16.4","figlet":"1.2.1","flatted":"^2.0.1","friendly-errors-webpack-plugin":"1.7.0","hard-source-webpack-plugin":"0.13.1","helmet":"3.14.0","hoist-non-react-statics":"^3.3.0","isomorphic-fetch":"2.2.1","loader-utils":"1.1.0","node-notifier":"5.2.1","nodemon":"1.18.4","react-dom":"16.8.4","react-helmet":"5.2.0","react-loadable":"5.5.0","react-router-config":"5.0.0","react-router-dom":"5.0.0","rimraf":"2.6.3","source-map-support":"0.5.9","terser-webpack-plugin":"1.1.0","webpack":"4.29.0","webpack-bundle-analyzer":"3.0.3","webpack-cli":"3.1.2","webpack-dev-middleware":"3.4.0","webpack-hot-middleware":"2.24.3","webpackbar":"2.6.3"},"peerDependencies":{"react":"^16.8.4"},"devDependencies":{"@babel/preset-typescript":"^7.1.0","@types/webpack-bundle-analyzer":"^2.13.0","babel-plugin-module-resolver":"^3.1.1","gulp":"github:gulpjs/gulp#4.0","gulp-babel":"^8.0.0","gulp-clean":"^0.4.0","gulp-sourcemaps":"^2.6.4","gulp-uglify":"^3.0.1","gulp-watch":"^5.0.1","react":"^16.8.4"},"gitHead":"f5174d597f5dc88278df7583058a7812d6157f1d","readme":"[![Build Status](https://travis-ci.org/airyrooms/maleo.js.svg?branch=master)](https://travis-ci.org/airyrooms/maleo.js)\n[![Coverage Status](https://coveralls.io/repos/github/airyrooms/maleo.js/badge.svg?branch=master)](https://coveralls.io/github/airyrooms/maleo.js?branch=master)\n[![npm version](https://badge.fury.io/js/%40airy%2Fmaleo.svg)](https://badge.fury.io/js/%40airy%2Fmaleo)\n[![Issues](http://img.shields.io/github/issues/airyrooms/maleo.js.svg)]( https://github.com/airyrooms/maleo.js/issues)\n[![MIT license](https://img.shields.io/badge/License-MIT-blue.svg)](https://github.com/airyrooms/maleo.js/blob/canary/LICENSE)\n[![Discord Chat](https://img.shields.io/discord/550498214789513218.svg)](https://discord.gg/9eArCQn)\n[![Conventional Commits](https://img.shields.io/badge/Conventional%20Commits-1.0.0-green.svg)](https://conventionalcommits.org)\n\n\n# Welcome to Maleo.js\n\nMaleo.js is an un-opinionated framework to enable Universal Rendering in JavaScript using React with no hassle.\n\nWe are here to solve the time consuming setups Universal Rendering Required.\n\n---\n\nReadme below is the documentation for the `canary` (prerelease) branch. To view the documentation for the latest stable Maleo.js version change branch to `master`\n\n---\n\n# Table of Contents\n- [Features](#features)\n- [Setup](#setup)\n- [Component Lifecycle](#component-lifecycle)\n- [Routing](#routing)\n- [Dynamic Import Component](#dynamic-import-component)\n  - [Tips](#tips)\n  - [Preloading](#preloading)\n- [Customizable Component](#customizable-component)\n  - [Custom Document](#custom-document)\n  - [Custom Wrap](#custom-wrap)\n- [Custom Configuration](#custom-configuration)\n  - [Customize Server](#customize-server)\n  - [Customize Webpack](#customize-webpack)\n  - [Customize Babel Config](#customize-babel-config)\n- [Core Utilities Functions](#core-utilities-functions)\n- [CDN Support](#cdn-support)\n- [Plugins](#plugins)\n- [FAQ](#faq)\n- [Contributing](#contributing)\n\n---\n\n## Features\n- Universal Rendering\n- Plugin based framework\n- Customizable\n\n## Setup\n\nInstall Maleo.js \n\n>*for now change `@airy/maleo` to `@airy/maleo@canary` until we publish the stable version of this package*\n\n\n**NPM**\n```bash\n$ npm install --save @airy/maleo react\n```\n**Yarn**\n```bash\n$ yarn add @airy/maleo react\n```\n\nAdd this script to your `package.json`\n```json\n{\n  \"scripts\": {\n    \"dev\": \"maleo dev\",\n    \"build\": \"export NODE_ENV=production && maleo build\",\n    \"start\": \"export NODE_ENV=production && maleo run\"\n  }\n}\n```\n\nCreate a page Root component\n```jsx\n// ./src/Root.jsx\nimport React from 'react';\n\n// Export default is required for registering page\nexport default class RootComponent extends React.Component {\n  render() {\n    return (\n      <h1>Hello World!</h1>\n    )\n  }\n}\n```\n\nAnd lastly, create a routing file on your project root directory called `routes.json` and register your page component\n```json\n[\n  {\n    \"path\": \"/\",\n    \"page\": \"./src/Root\"\n  }\n]\n```\n\nAfter that you can now run `$ npm run dev` and go to `http://localhost:3000`.\n\nYou should now see your app running on your browser.\n\nBy now you should see \n- Automatic transpilation and bundling (with webpack and babel)\n- ~~Hot code reloading~~ [#17](https://github.com/airyrooms/maleo.js/issues/17)\n- Server rendering\n\nTo see how simple this is, check out the sample app!\n\n## Component Lifecycle \n\nMaleo.js added a new component lifecycle hook called `getInitialProps`, this function is called during Server Side Rendering (SSR) and during route changes on Client Side Rendering (CSR).\n\nThis is useful especially for SEO purposes. \n\nExample for stateful component:\n```jsx\nimport React from 'react';\n\nexport default class extends React.Component {\n  static getInitialProps = async (ctx) => {\n    const { req } = ctx;\n\n    // check if getInitialProps is called on server or on client\n    const userAgent = req ? req.headers['user-agent'] : navigator.userAgent;\n\n    // the return value will be passed as props for this component\n    return { userAgent };\n  }\n\n  render() {\n    return (\n      <div>\n        Hello World {this.props.userAgent}\n      </div>\n    );\n  }\n}\n```\n\nExample for stateless component:\n```jsx\nconst Component = (props) => (\n  <div>\n    Hello World {props.userAgent}\n  </div>\n);\n\nComponent.getInitialprops = async (ctx) => {\n  const { req } = ctx;\n\n  const userAgent = req ? req.headers['user-agent'] : navigator.userAgent;\n\n  return { userAgent };\n};\n\nexport default Component;\n```\n\n`getInitialProps` receives a context object with the following properties:\n- `req` - HTTP request object (server only)\n- `res` - HTTP response object (server only)\n- `...wrapProps` - Spreaded properties from custom Wrap\n- `...appProps` - Spreaded properties from custom App\n- `matched` - Matched routes contains list of object, which is consists of `match` object and `route` object\n\n## Routing\n\nRouting is declared in a centralized route config.\nRegister all the route config in `routes.json` file.\n\nIf you put the `routes.json` files on root directory, Maleo will automatically register your route. Otherwise put path to your routes on [Maleo config](#custom-configuration).\n\nRoutes file has to export default the route configuration.\nThe route object **expected to have distinct key** to indicate the route.\n\n<table>\n  <tr>\n    <td>Key</td>\n    <td>Type</td>\n    <td>Description</td>\n  </tr>\n  <tr>\n    <td><code>path</code></td>\n    <td><code>String!</code></td>\n    <td>Routes path</td>\n  </tr>\n  <tr>\n    <td><code>page</code></td>\n    <td><code>String!</code></td>\n    <td>Path to React Component for this route path</td>\n  </tr>\n  <tr>\n    <td><code>exact</code></td>\n    <td><code>Boolean?</code> [<code>false</code>]</td>\n    <td>To make url has to match exactly the path in order to render the component. Give <code>false</code> value if the route is a wrapper route component</td>\n  </tr>\n  <tr>\n    <td><code>routes</code></td>\n    <td><code>RouteObject?</code></td>\n    <td>Nested route</td>\n  </tr>\n</table>\n\n\nFor example:\n```json\n[\n  {\n    \"page\": \"./src/MainApp\",\n    \"routes\": [\n      {\n        \"path\": \"/\",\n        \"page\": \"./src/Search\",\n        \"exact\": true\n      },\n      {\n        \"path\": \"/search\",\n        \"page\": \"./src/Search\",\n        \"routes\": [\n          {\n            \"path\": \"/search/hello\",\n            \"page\": \"./src/Detail\",\n            \"exact\": true\n          }\n        ]\n      },\n      {\n        \"path\": \"/detail\",\n        \"page\": \"./src/Detail\",\n        \"exact\": true\n      }\n    ]\n  }\n]\n```\n\n## Dynamic Import Component\nMaleo.js supports TC39 [dynamic import proposal](https://github.com/tc39/proposal-dynamic-import) for JavaScript.\n\nYou can think dynamic import as another way to split your code into manageable chunks. You can use our `Dynamic` function which utilizes [react loadable](https://github.com/jamiebuilds/react-loadable)\n\nFor Example\n```js\n// DynamicComponent.js\nimport Dynamic from '@airy/maleo/dynamic';\n\nexport default Dynamic({\n  loader: () => import( /* webpackChunkName:\"DynamicComponent\" */ './component/DynamicLoad'),\n})\n```\n\n### **Preloading**\n\nFor optimization purposes, you can also preload a component even before the component got rendered.\n\nFor example, if you want to load component when a button get pressed, you can start preloading the component when the user hovers the mouse over the button.\n\nThe component created by `Dynamic` exposes a [static `preload` method](https://github.com/jamiebuilds/react-loadable#loadablecomponentpreload).\n\n```jsx\nimport React from 'react';\nimport Dynamic from '@airy/maleo/dynamic';\n\nconst DynamicBar = Dynamic({\n  loader: () => import('./Bar'),\n  loading: LoadingCompoinent\n});\n\nclass MyComponent extends React.Component {\n  state = { showBar: false };\n\n  onClick = () => {\n    this.setState({ showBar: true });\n  };\n\n  onMouseOver = () => DynamicBar.preload();\n\n  render() {\n    return (\n      <div>\n        <button\n          onClick={this.onClick}\n          onMouseOver={this.onMouseOver}>\n          Show Bar\n        </button>\n        { this.state.showBar && <DynamicBar /> }\n      </div>\n    )\n  }\n}\n```\n\n## Customizable Component\n\n### Custom Document\n\nHighly inspired by what [Next.js](https://github.com/zeit/next.js) has done on their awesome template customization.\n\nMaleo.js also enable customization on `Document` as document's markup. So you don't need to include tags like `<html>`, `<head>`, `<body>`, etc. \n\nTo override the default behavior, you'll need to create a component that extends the `Document` React class provided by Maleo.\n\n```jsx\n// document.jsx\nimport React from 'react';\nimport { Document, Header, Main, Scripts } from '@airy/maleo/document';\n\nexport default class extends Document {\n  render() {\n    return (\n      <html>\n        <Header>\n          <title>Maleo JS</title>\n          <meta charset=\"utf-8\" />\n          <meta name=\"description\" content=\"Maleo.js is awesome!\" />\n\n          <style>\n            {` body { background-color: #fff } `}\n          </style>\n        </Header>\n\n        <body>\n          <Main />\n\n          <Scripts />\n\n          <ReduxScript />\n        </body>\n      </html>\n    );\n  }\n}\n```\n\n### Custom Wrap\n\nMaleo.js uses the `Wrap` component to initialize pages. `Wrap` contains React Router's Component. You can add HoC here to wrap the application and control the page initialization. Which allows you to do amazing things like:\n- Persisting layour between page changes\n- Keeping state when navigating pages\n- Custom error handling using `componentDidCatch`\n- Inject additional data into pages (like Redux provider, etc)\n\nTo override the default behavior, you'll need to create a component that extends the `Wrap` React class provided by Maleo.\n\n```jsx\n// wrap.jsx\nimport React from 'react';\nimport { Wrap } from '@airy/maleo/wrap';\n\n// Redux plugin for Maleo.js\n// Hoc that creates store and add Redux Provider\nimport { withRedux } from '@airy/maleo-redux-plugin';\n\n// Custom Wrapper that will be rendered for the whole Application\nimport Content from './component/Content';\nimport NavigationBar from './component/NavigationBar';\n\nimport { createStore } from './store';\n\n@withRedux(createStore)\nexport default class extends Wrap {\n  static getInitialProps = (ctx) => {\n    const { store } = ctx\n    // you receive store from context\n    // you can access or do something with the store here\n    console.log('Initialized Redux Store', store);\n    return {}\n  }\n\n  render() {\n    const { Container, containerProps, App, appProps } = this.props\n\n    return (\n      <Container {...containerProps}>\n        <NavigationBar />\n        <Content>\n          <App {...appProps}/>\n        </Content>\n      </Container>\n    )\n  }\n}\n\n```\nIf you put `document.jsx` and `wrap.jsx` on root directory (the same level with `package.json`), then Maleo will automatically register your custom Document and Wrap. Otherwise, you can add the path to your custom Document and Wrap on [Maleo config](#custom-configuration)\n\n---\n***We are also working on adding default and customizable `Error` component page***\n\n---\n\n## Custom Configuration\n\nFor more advanced configuration of Maleo.js, like `webpack` config, registering `plugins`, path to your routes, custom Document and Wrap, and adding `path alias`, you can create a `maleo.config.js` in the root of your project directory. (same directory with `package.json`)\n\n```js\n// maleo.config.js\n\nmodule.exports = {\n  /* config options here */\n}\n```\n\nHere are the API's for the configuration:\n\n<table>\n  <tr>\n    <td>Key</td>\n    <td>Type</td>\n    <td>Description</td>\n  </tr>\n  <tr>\n    <td><code>favicon</code></td>\n    <td><code>String</code> [<code>{project-dir}/favicon.ico</code>]</td>\n    <td>Path to favicon file</td>\n  </tr>\n  <tr>\n    <td><code>buildDir</code></td>\n    <td><code>String?</code> [<code>.maleo</code>]</td>\n    <td>Directory to put Maleo.js' build assets</td>\n  </tr>\n  <tr>\n    <td><code>cache</code></td>\n    <td><code>Boolean?</code> [<code>true</code>]</td>\n    <td>Enable webpack build caching</td>\n  </tr>\n  <tr>\n    <td><code>isDev</code></td>\n    <td><code>Boolean?</code> [<code>process.env.NODE_ENV === 'development'</code>]</td>\n    <td>Enable development build configuration</td>\n  </tr>\n  <tr>\n    <td><code>sourceMaps</code></td>\n    <td><code>Boolean?</code> [<code>true</code>]</td>\n    <td>Enable webpack to generate source maps</td>\n  </tr>\n  <tr>\n    <td><code>alias</code></td>\n    <td><code>Object?</code></td>\n    <td>A key value pair for aliasing path directory\n      <br>\n      <code>\n      { 'component': './src/component' }\n      </code>\n    </td>\n  </tr>\n  <tr>\n    <td><code>publicPath</code></td>\n    <td><code>String?</code> [<code>/_assets/</code>]</td>\n    <td>To customize webpack's <code>publicPath</code> \n    <br>Comes in handy if using CDN to put built assets</td>\n  </tr>\n  <tr>\n    <td><code>analyzeBundle</code></td>\n    <td><code>Boolean?</code> [<code>false</code>]</td>\n    <td>To enable webpack's bundle analyzer, for analyzing bundle sizes during bundle debugging should Maleo.js' build process got slow</td>\n  </tr>\n  <tr>\n    <td><code>webpack</code></td>\n    <td><code>Function?</code></td>\n    <td>To customize webpack configuration, more details <a href=\"#customize-webpack\">here</a></td>\n  </tr>\n  <tr>\n    <td><code>routes</code></td>\n    <td><code>string?</code> [<code>rootDir/routes.jsx</code>]</td>\n    <td>Path to your routes file</td>\n  </tr>\n  <tr>\n    <td><code>customDocument</code></td>\n    <td><code>string?</code> [<code>rootDir/document.jsx</code>]</td>\n    <td>Path to your custom document file</td>\n  </tr>\n  <tr>\n    <td><code>customWrap</code></td>\n    <td><code>string?</code> [<code>rootDir/wrap.jsx</code>]</td>\n    <td>Path to your custom wrap file</td>\n  </tr>\n  <tr>\n    <td><code>customApp</code></td>\n    <td><code>string?</code> [<code>rootDir/app.jsx</code>]</td>\n    <td>Path to your custom app file</td>\n  </tr>\n  <tr>\n    <td><code>csp</code></td>\n    <td><code>boolean | cspConfig</code> [<code>false</code>]</td>\n    <td>Config for <a href=\"https://developers.google.com/web/fundamentals/security/csp/\">Content Security Policy</a>, using <a href=\"https://helmetjs.github.io/docs/csp/\">Helmet-CSP</a>\n    <br/>\n    If set to true it will enable default config:\n    <br/>\n    <code>{\n      directives: {\n        defaultSrc: [`'self'`],\n        styleSrc: [`'self'`],\n      },\n    }</code>\n    </td>\n  </tr>\n</table>\n\n#### Customize Server\n\nCreate a `server.js` file on root directory where your `package.json` lives.\nHere you can customize Maleo's server.\n```js\nimport { Server } from '@airy/maleo/server';\n\nconst PORT = process.env.PORT || 3000;\n\nconst maleoServer = Server.init({\n  port: PORT,\n  runHandler: () => {\n    console.log('Server running on port :', PORT);\n  }\n});\n\nmaleoServer.run();\n```\n\nHere are the API's for the configuration:\n\n<table>\n  <tr>\n    <td>Key</td>\n    <td>Type</td>\n    <td>Description</td>\n  </tr>\n  <tr>\n    <td><code>port</code></td>\n    <td><code>Number?</code> [<code>3000</code>]</td>\n    <td>Port to run Maleo server</td>\n  </tr>\n  <tr>\n    <td><code>assetDir</code></td>\n    <td><code>String?</code> [<code>'<root>/.maleo/client'</code>]</td>\n    <td>Directory for all client related assets</td>\n  </tr>\n  <tr>\n    <td><code>runHandler</code></td>\n    <td><code>Function?</code></td>\n    <td>Function called when maleo server starter</td>\n  </tr>\n</table>\n\n#### Customize Webpack\n\nYou are able to extend Maleo.js' default webpack configuration by defining a function on `maleo.config.js`\n\n```js\n// maleo.config.js\n\nmodule.exports = {\n  webpack(config, context, next) {\n    // Perform customizations to webpack config\n    // Important: This call is required in order to let Maleo pass the config to webpack\n    return next(); \n  },\n};\n```\n\nWebpack function will receive three arguments:\n\n<table>\n  <tr>\n    <td>Argument</td>\n    <td>Details</td>\n  <tr>\n  <tr>\n    <td><code>config</code></td>\n    <td>This contains webpack configuration object that you can manipulate</td>\n  <tr>\n  <tr>\n    <td><code>context</code></td>\n    <td>This contains some keys that are useful for the build context\n      <br>\n      <table>\n        <tr>\n          <td><code>isDev</code></td>\n          <td><code>Boolean</code></td>\n          <td>Check if current build is for development</td>\n        </tr>\n        <tr>\n          <td><code>publicPath</code></td>\n          <td><code>String</code></td>\n          <td>Public Path of user defined or default's value</td>\n        </tr>\n        <tr>\n          <td><code>analyzeBundle</code></td>\n          <td><code>Boolean</code></td>\n          <td>Check if analyze bundle is enabled</td>\n        </tr>\n        <tr>\n          <td><code>buildDirectory</code></td>\n          <td><code>String</code></td>\n          <td>Build Directory of user defined or default's value</td>\n        </tr>\n        <tr>\n          <td><code>name</code></td>\n          <td><code>String</code></td>\n          <td>Build name <code>'server' || 'client'</code></td>\n        </tr>\n      </table>\n    </td>\n  <tr>\n  <tr>\n    <td><code>next</code></td>\n    <td>A callback required to be called to pass the custom configuration</td>\n  <tr>\n</table>\n\nExample of adding `ts-loader` through `maleo.config.js`:\n\n```js\n// maleo.config.js\n\n// Partly taken and modified from @airy/maleo-ts-plugin source\n// for simplicity purposes\n\nmodule.exports = {\n  webpack(config, context, next) {\n    const { isDev } = context\n\n    config.module.rules.push({\n      test: /\\.tsx?/,\n      exclude: /node_modules/,\n      use: [\n        require.resolve('@airy/maleo/lib/build/loaders/maleo-babel-loader'),\n        {\n          loader: 'ts-loader',\n          options: {\n            transpileOnly: true,\n          },\n        },\n      ],\n\n      if (isDev) {\n        config.plugins.push(new ForkTSCheckerWebpackPlugin());\n      }\n\n      return next();\n    })\n  },\n};\n```\n\n#### Customize Babel Config\n\nMaleo.js also let you have your own babel config. Just simply add `.babelrc` file at the root directory of your app.\n\nYou can include Maleo.js' babel preset in order to have latest JavaScript preset.\n\nHere's an example of `.babelrc` file:\n```json\n{\n  \"presets\": [\"@airy/maleo/babel\"],\n  \"plugins\": []\n}\n```\n\nThe `@airy/maleo/babel` preset includes everything you need to get your development started. The preset includes:\n- `@babel/preset-env`\n- `@babel/preset-react`\n- `@babel/plugin-proposal-class-properties`\n- `@babel/plugin-proposal-decorators`\n- `@babel/plugin-proposal-object-rest-spread`\n- `@babel/plugin-transform-runtime`\n- `react-loadable/babel`\n\n## Core Utilities Functions\n\n#### Route Matching\n\nTo use you can import the matching routes function like this:\n```js\nimport { getMatchedRoutes } from '@airy/maleo/utils';\n```\n\nThe function will return all matched routes and the route object from `routes.json`\n\n## CDN Support\n\nIf you are using a CDN, you can set up the `publicPath` setting and configure your CDN's origin to resolve to the domain that Maleo.js is hosted on.\n\n```js\n// maleo.config.js\n\nconst isProd = process.env.NODE_ENV === 'production';\n\nmodule.exports = {\n  assetPrefix: isProd && 'https://cdn.example.com';\n}\n```\n\n## FAQ\n\n== TO BE DETERMINED == \n\n## Plugins\n- [css-plugin](https://github.com/airyrooms/maleo-plugins/tree/master/packages/css-plugin)\n- [redux-plugin](https://github.com/airyrooms/maleo-plugins/tree/master/packages/redux-plugin)\n- [typescript-plugin](https://github.com/airyrooms/maleo-plugins/tree/master/packages/typescript-plugin)\n- [more](https://github.com/airyrooms/maleo-plugins)\n\n\n## Contributing\n[Please follow these steps to contribute to Maleo.js](https://github.com/airyrooms/maleo.js/blob/canary/CONTRIBUTING.md)\n\n[Please follow these steps to contribute to Maleo.js' plugins](https://github.com/airyrooms/maleo-plugins#contributing-guidelines)\n\n\n## Contributors\n\nThis project exists thanks to all the people who contribute. [Contribute](CONTRIBUTING.md).\n\nMany thanks to our **[contributors](https://github.com/airyrooms/maleo.js/graphs/contributors)**!\n\n\n## License\n\nMIT","readmeFilename":"README.md","bugs":{"url":"https://github.com/airyrooms/maleo.js/issues"},"homepage":"https://github.com/airyrooms/maleo.js#readme","_id":"@airy/maleo@0.3.1-canary.26","_nodeVersion":"10.16.0","_npmVersion":"lerna/3.13.1/node@v10.16.0+x64 (linux)","dist":{"integrity":"sha512-O4+Rre614SMtOmPe3Fb1fKNbIdgwmpZxcQyMXYQT+xNc0SmcvAUaoqI2jdY4aJYioySMU0voV9x7j9DmJVDr8A==","shasum":"5be1443191304c0d16125df95513b188c78faa9e","tarball":"https://registry.npmjs.org/@airy/maleo/-/maleo-0.3.1-canary.26.tgz","fileCount":107,"unpackedSize":377401,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJdP/OmCRA9TVsSAnZWagAAmt8P/jymb9SK81g9thhyiPx8\n/Tjz0HcWYS4sK9t1JsiuO8y06tDlYcw2ZL3DVgw6kL1AYkvjpSWe+6wqdX8b\nWO+hfm8OyU1CXSl9NRlOzefpKNMzN2EqVTkmJ15crE9/JKo+PA6fbX5PhZip\n8rxotGXWZo2gOMHp7C7ZWzccSXP5twz+pLsafSOQFoFgIjX8Y75kqJaP1k7G\n93QcA7HY0KbDGomRdB9B2ekJXgSIX5RY1hkommSBeLsfK9L5pOVQjG512KS1\ns/1rOuZXdPgFECk3x5UyjfVTiXI56egqYc4qLmG5kBUG69BRaDeh5gTg3i+2\nmWeQw+kqFZ6/HW7U6erRc6f77fMCj3Abl/6VtPpgyZOXAnjxs6Z41qRiR4WR\nZ5kKY++ZTqRUOqs43+8GtRVHFVn28emXMfRXDalVNHIHffyKd4jg/K47BYeH\ncGDqFNKo/GZHWySTK6NMB1xJlpS1Iktehcxgn/pGxQcXqNPmf3TzmmA4FDzb\nsy1NNlpUauqrv2qBq/MXrL2m9EB62Om/8iCHsetKbDnV0k04dA0MKRbXf/C+\n6mjIifYCJ8rXcbGRUtMJA6RSiVJFYwbZ6AEybuEPSnswCYYpetmjtKXkYdM/\n9A6ANGQzbo+XVF8vc7MPxe2/7f/BAFs+FXOkXgCYZBFWJ0Vwjcx8GrXxzsPo\nN72S\r\n=jdUk\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDbgYOm2NYd39TrGE9Rq/JdgN+tgaLEgwfnbRkZ2oKNWwIgS/sR8qD4UHnOLAItOIwKXiAKuFhc7PgaihRamHgysh8="}]},"maintainers":[{"name":"airyrooms-engineering","email":"engineering@airyrooms.com"}],"_npmUser":{"name":"airyrooms-engineering","email":"engineering@airyrooms.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/maleo_0.3.1-canary.26_1564472230363_0.44661394275019406"},"_hasShrinkwrap":false},"0.3.1-canary.27":{"name":"@airy/maleo","version":"0.3.1-canary.27","description":"Un-opinionated Universal Rendering Javascript Framework","repository":{"type":"git","url":"git+https://github.com/airyrooms/maleo.js.git","bugs":"https://github.com/airyrooms/maleo.js/issues"},"author":{"name":"Airy"},"license":"MIT","private":false,"sideEffects":false,"scripts":{"build":"rm -rf dist && gulp","watch":"gulp watch","prepublish":"yarn build","start":"node server.js"},"bin":{"maleo":"lib/bin/maleo.js"},"dependencies":{"@babel/core":"7.1.2","@babel/plugin-proposal-class-properties":"7.1.0","@babel/plugin-proposal-decorators":"7.1.2","@babel/plugin-proposal-object-rest-spread":"7.0.0","@babel/plugin-syntax-dynamic-import":"7.0.0","@babel/plugin-transform-runtime":"7.1.0","@babel/preset-env":"7.1.0","@babel/preset-react":"7.0.0","@babel/runtime":"7.1.2","@babel/runtime-corejs2":"7.2.0","babel-loader":"8.0.4","case-sensitive-paths-webpack-plugin":"2.1.2","compression":"1.7.4","es6-promise":"4.2.5","eventsource":"1.0.7","express":"4.16.4","figlet":"1.2.1","flatted":"^2.0.1","friendly-errors-webpack-plugin":"1.7.0","hard-source-webpack-plugin":"0.13.1","helmet":"3.14.0","hoist-non-react-statics":"^3.3.0","isomorphic-fetch":"2.2.1","loader-utils":"1.1.0","node-notifier":"5.2.1","nodemon":"1.18.4","react-dom":"16.8.4","react-helmet":"5.2.0","react-loadable":"5.5.0","react-router-config":"5.0.0","react-router-dom":"5.0.0","rimraf":"2.6.3","source-map-support":"0.5.9","terser-webpack-plugin":"1.1.0","webpack":"4.29.0","webpack-bundle-analyzer":"3.0.3","webpack-cli":"3.1.2","webpack-dev-middleware":"3.4.0","webpack-hot-middleware":"2.24.3","webpackbar":"2.6.3"},"peerDependencies":{"react":"^16.8.4"},"devDependencies":{"@babel/preset-typescript":"^7.1.0","@types/webpack-bundle-analyzer":"^2.13.0","babel-plugin-module-resolver":"^3.1.1","gulp":"github:gulpjs/gulp#4.0","gulp-babel":"^8.0.0","gulp-clean":"^0.4.0","gulp-sourcemaps":"^2.6.4","gulp-uglify":"^3.0.1","gulp-watch":"^5.0.1","react":"^16.8.4"},"gitHead":"51c5491c418c4e5874ce927746a46603dc0c3629","readme":"[![Build Status](https://travis-ci.org/airyrooms/maleo.js.svg?branch=master)](https://travis-ci.org/airyrooms/maleo.js)\n[![Coverage Status](https://coveralls.io/repos/github/airyrooms/maleo.js/badge.svg?branch=master)](https://coveralls.io/github/airyrooms/maleo.js?branch=master)\n[![npm version](https://badge.fury.io/js/%40airy%2Fmaleo.svg)](https://badge.fury.io/js/%40airy%2Fmaleo)\n[![Issues](http://img.shields.io/github/issues/airyrooms/maleo.js.svg)]( https://github.com/airyrooms/maleo.js/issues)\n[![MIT license](https://img.shields.io/badge/License-MIT-blue.svg)](https://github.com/airyrooms/maleo.js/blob/canary/LICENSE)\n[![Discord Chat](https://img.shields.io/discord/550498214789513218.svg)](https://discord.gg/9eArCQn)\n[![Conventional Commits](https://img.shields.io/badge/Conventional%20Commits-1.0.0-green.svg)](https://conventionalcommits.org)\n\n\n# Welcome to Maleo.js\n\nMaleo.js is an un-opinionated framework to enable Universal Rendering in JavaScript using React with no hassle.\n\nWe are here to solve the time consuming setups Universal Rendering Required.\n\n---\n\nReadme below is the documentation for the `canary` (prerelease) branch. To view the documentation for the latest stable Maleo.js version change branch to `master`\n\n---\n\n# Table of Contents\n- [Features](#features)\n- [Setup](#setup)\n- [Component Lifecycle](#component-lifecycle)\n- [Routing](#routing)\n- [Dynamic Import Component](#dynamic-import-component)\n  - [Tips](#tips)\n  - [Preloading](#preloading)\n- [Customizable Component](#customizable-component)\n  - [Custom Document](#custom-document)\n  - [Custom Wrap](#custom-wrap)\n- [Custom Configuration](#custom-configuration)\n  - [Customize Server](#customize-server)\n  - [Customize Webpack](#customize-webpack)\n  - [Customize Babel Config](#customize-babel-config)\n- [Core Utilities Functions](#core-utilities-functions)\n- [CDN Support](#cdn-support)\n- [Plugins](#plugins)\n- [FAQ](#faq)\n- [Contributing](#contributing)\n\n---\n\n## Features\n- Universal Rendering\n- Plugin based framework\n- Customizable\n\n## Setup\n\nInstall Maleo.js \n\n>*for now change `@airy/maleo` to `@airy/maleo@canary` until we publish the stable version of this package*\n\n\n**NPM**\n```bash\n$ npm install --save @airy/maleo react\n```\n**Yarn**\n```bash\n$ yarn add @airy/maleo react\n```\n\nAdd this script to your `package.json`\n```json\n{\n  \"scripts\": {\n    \"dev\": \"maleo dev\",\n    \"build\": \"export NODE_ENV=production && maleo build\",\n    \"start\": \"export NODE_ENV=production && maleo run\"\n  }\n}\n```\n\nCreate a page Root component\n```jsx\n// ./src/Root.jsx\nimport React from 'react';\n\n// Export default is required for registering page\nexport default class RootComponent extends React.Component {\n  render() {\n    return (\n      <h1>Hello World!</h1>\n    )\n  }\n}\n```\n\nAnd lastly, create a routing file on your project root directory called `routes.json` and register your page component\n```json\n[\n  {\n    \"path\": \"/\",\n    \"page\": \"./src/Root\"\n  }\n]\n```\n\nAfter that you can now run `$ npm run dev` and go to `http://localhost:3000`.\n\nYou should now see your app running on your browser.\n\nBy now you should see \n- Automatic transpilation and bundling (with webpack and babel)\n- ~~Hot code reloading~~ [#17](https://github.com/airyrooms/maleo.js/issues/17)\n- Server rendering\n\nTo see how simple this is, check out the sample app!\n\n## Component Lifecycle \n\nMaleo.js added a new component lifecycle hook called `getInitialProps`, this function is called during Server Side Rendering (SSR) and during route changes on Client Side Rendering (CSR).\n\nThis is useful especially for SEO purposes. \n\nExample for stateful component:\n```jsx\nimport React from 'react';\n\nexport default class extends React.Component {\n  static getInitialProps = async (ctx) => {\n    const { req } = ctx;\n\n    // check if getInitialProps is called on server or on client\n    const userAgent = req ? req.headers['user-agent'] : navigator.userAgent;\n\n    // the return value will be passed as props for this component\n    return { userAgent };\n  }\n\n  render() {\n    return (\n      <div>\n        Hello World {this.props.userAgent}\n      </div>\n    );\n  }\n}\n```\n\nExample for stateless component:\n```jsx\nconst Component = (props) => (\n  <div>\n    Hello World {props.userAgent}\n  </div>\n);\n\nComponent.getInitialprops = async (ctx) => {\n  const { req } = ctx;\n\n  const userAgent = req ? req.headers['user-agent'] : navigator.userAgent;\n\n  return { userAgent };\n};\n\nexport default Component;\n```\n\n`getInitialProps` receives a context object with the following properties:\n- `req` - HTTP request object (server only)\n- `res` - HTTP response object (server only)\n- `...wrapProps` - Spreaded properties from custom Wrap\n- `...appProps` - Spreaded properties from custom App\n- `matched` - Matched routes contains list of object, which is consists of `match` object and `route` object\n\n## Routing\n\nRouting is declared in a centralized route config.\nRegister all the route config in `routes.json` file.\n\nIf you put the `routes.json` files on root directory, Maleo will automatically register your route. Otherwise put path to your routes on [Maleo config](#custom-configuration).\n\nRoutes file has to export default the route configuration.\nThe route object **expected to have distinct key** to indicate the route.\n\n<table>\n  <tr>\n    <td>Key</td>\n    <td>Type</td>\n    <td>Description</td>\n  </tr>\n  <tr>\n    <td><code>path</code></td>\n    <td><code>String!</code></td>\n    <td>Routes path</td>\n  </tr>\n  <tr>\n    <td><code>page</code></td>\n    <td><code>String!</code></td>\n    <td>Path to React Component for this route path</td>\n  </tr>\n  <tr>\n    <td><code>exact</code></td>\n    <td><code>Boolean?</code> [<code>false</code>]</td>\n    <td>To make url has to match exactly the path in order to render the component. Give <code>false</code> value if the route is a wrapper route component</td>\n  </tr>\n  <tr>\n    <td><code>routes</code></td>\n    <td><code>RouteObject?</code></td>\n    <td>Nested route</td>\n  </tr>\n</table>\n\n\nFor example:\n```json\n[\n  {\n    \"page\": \"./src/MainApp\",\n    \"routes\": [\n      {\n        \"path\": \"/\",\n        \"page\": \"./src/Search\",\n        \"exact\": true\n      },\n      {\n        \"path\": \"/search\",\n        \"page\": \"./src/Search\",\n        \"routes\": [\n          {\n            \"path\": \"/search/hello\",\n            \"page\": \"./src/Detail\",\n            \"exact\": true\n          }\n        ]\n      },\n      {\n        \"path\": \"/detail\",\n        \"page\": \"./src/Detail\",\n        \"exact\": true\n      }\n    ]\n  }\n]\n```\n\n## Dynamic Import Component\nMaleo.js supports TC39 [dynamic import proposal](https://github.com/tc39/proposal-dynamic-import) for JavaScript.\n\nYou can think dynamic import as another way to split your code into manageable chunks. You can use our `Dynamic` function which utilizes [react loadable](https://github.com/jamiebuilds/react-loadable)\n\nFor Example\n```js\n// DynamicComponent.js\nimport Dynamic from '@airy/maleo/dynamic';\n\nexport default Dynamic({\n  loader: () => import( /* webpackChunkName:\"DynamicComponent\" */ './component/DynamicLoad'),\n})\n```\n\n### **Preloading**\n\nFor optimization purposes, you can also preload a component even before the component got rendered.\n\nFor example, if you want to load component when a button get pressed, you can start preloading the component when the user hovers the mouse over the button.\n\nThe component created by `Dynamic` exposes a [static `preload` method](https://github.com/jamiebuilds/react-loadable#loadablecomponentpreload).\n\n```jsx\nimport React from 'react';\nimport Dynamic from '@airy/maleo/dynamic';\n\nconst DynamicBar = Dynamic({\n  loader: () => import('./Bar'),\n  loading: LoadingCompoinent\n});\n\nclass MyComponent extends React.Component {\n  state = { showBar: false };\n\n  onClick = () => {\n    this.setState({ showBar: true });\n  };\n\n  onMouseOver = () => DynamicBar.preload();\n\n  render() {\n    return (\n      <div>\n        <button\n          onClick={this.onClick}\n          onMouseOver={this.onMouseOver}>\n          Show Bar\n        </button>\n        { this.state.showBar && <DynamicBar /> }\n      </div>\n    )\n  }\n}\n```\n\n## Customizable Component\n\n### Custom Document\n\nHighly inspired by what [Next.js](https://github.com/zeit/next.js) has done on their awesome template customization.\n\nMaleo.js also enable customization on `Document` as document's markup. So you don't need to include tags like `<html>`, `<head>`, `<body>`, etc. \n\nTo override the default behavior, you'll need to create a component that extends the `Document` React class provided by Maleo.\n\n```jsx\n// document.jsx\nimport React from 'react';\nimport { Document, Header, Main, Scripts } from '@airy/maleo/document';\n\nexport default class extends Document {\n  render() {\n    return (\n      <html>\n        <Header>\n          <title>Maleo JS</title>\n          <meta charset=\"utf-8\" />\n          <meta name=\"description\" content=\"Maleo.js is awesome!\" />\n\n          <style>\n            {` body { background-color: #fff } `}\n          </style>\n        </Header>\n\n        <body>\n          <Main />\n\n          <Scripts />\n\n          <ReduxScript />\n        </body>\n      </html>\n    );\n  }\n}\n```\n\n### Custom Wrap\n\nMaleo.js uses the `Wrap` component to initialize pages. `Wrap` contains React Router's Component. You can add HoC here to wrap the application and control the page initialization. Which allows you to do amazing things like:\n- Persisting layour between page changes\n- Keeping state when navigating pages\n- Custom error handling using `componentDidCatch`\n- Inject additional data into pages (like Redux provider, etc)\n\nTo override the default behavior, you'll need to create a component that extends the `Wrap` React class provided by Maleo.\n\n```jsx\n// wrap.jsx\nimport React from 'react';\nimport { Wrap } from '@airy/maleo/wrap';\n\n// Redux plugin for Maleo.js\n// Hoc that creates store and add Redux Provider\nimport { withRedux } from '@airy/maleo-redux-plugin';\n\n// Custom Wrapper that will be rendered for the whole Application\nimport Content from './component/Content';\nimport NavigationBar from './component/NavigationBar';\n\nimport { createStore } from './store';\n\n@withRedux(createStore)\nexport default class extends Wrap {\n  static getInitialProps = (ctx) => {\n    const { store } = ctx\n    // you receive store from context\n    // you can access or do something with the store here\n    console.log('Initialized Redux Store', store);\n    return {}\n  }\n\n  render() {\n    const { Container, containerProps, App, appProps } = this.props\n\n    return (\n      <Container {...containerProps}>\n        <NavigationBar />\n        <Content>\n          <App {...appProps}/>\n        </Content>\n      </Container>\n    )\n  }\n}\n\n```\nIf you put `document.jsx` and `wrap.jsx` on root directory (the same level with `package.json`), then Maleo will automatically register your custom Document and Wrap. Otherwise, you can add the path to your custom Document and Wrap on [Maleo config](#custom-configuration)\n\n---\n***We are also working on adding default and customizable `Error` component page***\n\n---\n\n## Custom Configuration\n\nFor more advanced configuration of Maleo.js, like `webpack` config, registering `plugins`, path to your routes, custom Document and Wrap, and adding `path alias`, you can create a `maleo.config.js` in the root of your project directory. (same directory with `package.json`)\n\n```js\n// maleo.config.js\n\nmodule.exports = {\n  /* config options here */\n}\n```\n\nHere are the API's for the configuration:\n\n<table>\n  <tr>\n    <td>Key</td>\n    <td>Type</td>\n    <td>Description</td>\n  </tr>\n  <tr>\n    <td><code>favicon</code></td>\n    <td><code>String</code> [<code>{project-dir}/favicon.ico</code>]</td>\n    <td>Path to favicon file</td>\n  </tr>\n  <tr>\n    <td><code>buildDir</code></td>\n    <td><code>String?</code> [<code>.maleo</code>]</td>\n    <td>Directory to put Maleo.js' build assets</td>\n  </tr>\n  <tr>\n    <td><code>cache</code></td>\n    <td><code>Boolean?</code> [<code>true</code>]</td>\n    <td>Enable webpack build caching</td>\n  </tr>\n  <tr>\n    <td><code>isDev</code></td>\n    <td><code>Boolean?</code> [<code>process.env.NODE_ENV === 'development'</code>]</td>\n    <td>Enable development build configuration</td>\n  </tr>\n  <tr>\n    <td><code>sourceMaps</code></td>\n    <td><code>Boolean?</code> [<code>true</code>]</td>\n    <td>Enable webpack to generate source maps</td>\n  </tr>\n  <tr>\n    <td><code>alias</code></td>\n    <td><code>Object?</code></td>\n    <td>A key value pair for aliasing path directory\n      <br>\n      <code>\n      { 'component': './src/component' }\n      </code>\n    </td>\n  </tr>\n  <tr>\n    <td><code>publicPath</code></td>\n    <td><code>String?</code> [<code>/_assets/</code>]</td>\n    <td>To customize webpack's <code>publicPath</code> \n    <br>Comes in handy if using CDN to put built assets</td>\n  </tr>\n  <tr>\n    <td><code>analyzeBundle</code></td>\n    <td><code>Boolean?</code> [<code>false</code>]</td>\n    <td>To enable webpack's bundle analyzer, for analyzing bundle sizes during bundle debugging should Maleo.js' build process got slow</td>\n  </tr>\n  <tr>\n    <td><code>webpack</code></td>\n    <td><code>Function?</code></td>\n    <td>To customize webpack configuration, more details <a href=\"#customize-webpack\">here</a></td>\n  </tr>\n  <tr>\n    <td><code>routes</code></td>\n    <td><code>string?</code> [<code>rootDir/routes.jsx</code>]</td>\n    <td>Path to your routes file</td>\n  </tr>\n  <tr>\n    <td><code>customDocument</code></td>\n    <td><code>string?</code> [<code>rootDir/document.jsx</code>]</td>\n    <td>Path to your custom document file</td>\n  </tr>\n  <tr>\n    <td><code>customWrap</code></td>\n    <td><code>string?</code> [<code>rootDir/wrap.jsx</code>]</td>\n    <td>Path to your custom wrap file</td>\n  </tr>\n  <tr>\n    <td><code>customApp</code></td>\n    <td><code>string?</code> [<code>rootDir/app.jsx</code>]</td>\n    <td>Path to your custom app file</td>\n  </tr>\n  <tr>\n    <td><code>csp</code></td>\n    <td><code>boolean | cspConfig</code> [<code>false</code>]</td>\n    <td>Config for <a href=\"https://developers.google.com/web/fundamentals/security/csp/\">Content Security Policy</a>, using <a href=\"https://helmetjs.github.io/docs/csp/\">Helmet-CSP</a>\n    <br/>\n    If set to true it will enable default config:\n    <br/>\n    <code>{\n      directives: {\n        defaultSrc: [`'self'`],\n        styleSrc: [`'self'`],\n      },\n    }</code>\n    </td>\n  </tr>\n</table>\n\n#### Customize Server\n\nCreate a `server.js` file on root directory where your `package.json` lives.\nHere you can customize Maleo's server.\n```js\nimport { Server } from '@airy/maleo/server';\n\nconst PORT = process.env.PORT || 3000;\n\nconst maleoServer = Server.init({\n  port: PORT,\n  runHandler: () => {\n    console.log('Server running on port :', PORT);\n  }\n});\n\nmaleoServer.run();\n```\n\nHere are the API's for the configuration:\n\n<table>\n  <tr>\n    <td>Key</td>\n    <td>Type</td>\n    <td>Description</td>\n  </tr>\n  <tr>\n    <td><code>port</code></td>\n    <td><code>Number?</code> [<code>3000</code>]</td>\n    <td>Port to run Maleo server</td>\n  </tr>\n  <tr>\n    <td><code>assetDir</code></td>\n    <td><code>String?</code> [<code>'<root>/.maleo/client'</code>]</td>\n    <td>Directory for all client related assets</td>\n  </tr>\n  <tr>\n    <td><code>runHandler</code></td>\n    <td><code>Function?</code></td>\n    <td>Function called when maleo server starter</td>\n  </tr>\n</table>\n\n#### Customize Webpack\n\nYou are able to extend Maleo.js' default webpack configuration by defining a function on `maleo.config.js`\n\n```js\n// maleo.config.js\n\nmodule.exports = {\n  webpack(config, context, next) {\n    // Perform customizations to webpack config\n    // Important: This call is required in order to let Maleo pass the config to webpack\n    return next(); \n  },\n};\n```\n\nWebpack function will receive three arguments:\n\n<table>\n  <tr>\n    <td>Argument</td>\n    <td>Details</td>\n  <tr>\n  <tr>\n    <td><code>config</code></td>\n    <td>This contains webpack configuration object that you can manipulate</td>\n  <tr>\n  <tr>\n    <td><code>context</code></td>\n    <td>This contains some keys that are useful for the build context\n      <br>\n      <table>\n        <tr>\n          <td><code>isDev</code></td>\n          <td><code>Boolean</code></td>\n          <td>Check if current build is for development</td>\n        </tr>\n        <tr>\n          <td><code>publicPath</code></td>\n          <td><code>String</code></td>\n          <td>Public Path of user defined or default's value</td>\n        </tr>\n        <tr>\n          <td><code>analyzeBundle</code></td>\n          <td><code>Boolean</code></td>\n          <td>Check if analyze bundle is enabled</td>\n        </tr>\n        <tr>\n          <td><code>buildDirectory</code></td>\n          <td><code>String</code></td>\n          <td>Build Directory of user defined or default's value</td>\n        </tr>\n        <tr>\n          <td><code>name</code></td>\n          <td><code>String</code></td>\n          <td>Build name <code>'server' || 'client'</code></td>\n        </tr>\n      </table>\n    </td>\n  <tr>\n  <tr>\n    <td><code>next</code></td>\n    <td>A callback required to be called to pass the custom configuration</td>\n  <tr>\n</table>\n\nExample of adding `ts-loader` through `maleo.config.js`:\n\n```js\n// maleo.config.js\n\n// Partly taken and modified from @airy/maleo-ts-plugin source\n// for simplicity purposes\n\nmodule.exports = {\n  webpack(config, context, next) {\n    const { isDev } = context\n\n    config.module.rules.push({\n      test: /\\.tsx?/,\n      exclude: /node_modules/,\n      use: [\n        require.resolve('@airy/maleo/lib/build/loaders/maleo-babel-loader'),\n        {\n          loader: 'ts-loader',\n          options: {\n            transpileOnly: true,\n          },\n        },\n      ],\n\n      if (isDev) {\n        config.plugins.push(new ForkTSCheckerWebpackPlugin());\n      }\n\n      return next();\n    })\n  },\n};\n```\n\n#### Customize Babel Config\n\nMaleo.js also let you have your own babel config. Just simply add `.babelrc` file at the root directory of your app.\n\nYou can include Maleo.js' babel preset in order to have latest JavaScript preset.\n\nHere's an example of `.babelrc` file:\n```json\n{\n  \"presets\": [\"@airy/maleo/babel\"],\n  \"plugins\": []\n}\n```\n\nThe `@airy/maleo/babel` preset includes everything you need to get your development started. The preset includes:\n- `@babel/preset-env`\n- `@babel/preset-react`\n- `@babel/plugin-proposal-class-properties`\n- `@babel/plugin-proposal-decorators`\n- `@babel/plugin-proposal-object-rest-spread`\n- `@babel/plugin-transform-runtime`\n- `react-loadable/babel`\n\n## Core Utilities Functions\n\n#### Route Matching\n\nTo use you can import the matching routes function like this:\n```js\nimport { getMatchedRoutes } from '@airy/maleo/utils';\n```\n\nThe function will return all matched routes and the route object from `routes.json`\n\n## CDN Support\n\nIf you are using a CDN, you can set up the `publicPath` setting and configure your CDN's origin to resolve to the domain that Maleo.js is hosted on.\n\n```js\n// maleo.config.js\n\nconst isProd = process.env.NODE_ENV === 'production';\n\nmodule.exports = {\n  assetPrefix: isProd && 'https://cdn.example.com';\n}\n```\n\n## FAQ\n\n== TO BE DETERMINED == \n\n## Plugins\n- [css-plugin](https://github.com/airyrooms/maleo-plugins/tree/master/packages/css-plugin)\n- [redux-plugin](https://github.com/airyrooms/maleo-plugins/tree/master/packages/redux-plugin)\n- [typescript-plugin](https://github.com/airyrooms/maleo-plugins/tree/master/packages/typescript-plugin)\n- [more](https://github.com/airyrooms/maleo-plugins)\n\n\n## Contributing\n[Please follow these steps to contribute to Maleo.js](https://github.com/airyrooms/maleo.js/blob/canary/CONTRIBUTING.md)\n\n[Please follow these steps to contribute to Maleo.js' plugins](https://github.com/airyrooms/maleo-plugins#contributing-guidelines)\n\n\n## Contributors\n\nThis project exists thanks to all the people who contribute. [Contribute](CONTRIBUTING.md).\n\nMany thanks to our **[contributors](https://github.com/airyrooms/maleo.js/graphs/contributors)**!\n\n\n## License\n\nMIT","readmeFilename":"README.md","bugs":{"url":"https://github.com/airyrooms/maleo.js/issues"},"homepage":"https://github.com/airyrooms/maleo.js#readme","_id":"@airy/maleo@0.3.1-canary.27","_nodeVersion":"10.16.0","_npmVersion":"lerna/3.13.1/node@v10.16.0+x64 (linux)","dist":{"integrity":"sha512-q/NjQkFSAulCrV9BThNb7zUfNQ4OGj/zw8VeQSACfu6B54hlHq5FR9lWpx4DHvgF+g5Q/ite577qeWEkDwnYAg==","shasum":"85e71a737f557b1a8d3d39937c9596e6c2a7e4d1","tarball":"https://registry.npmjs.org/@airy/maleo/-/maleo-0.3.1-canary.27.tgz","fileCount":107,"unpackedSize":377698,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJdP/bFCRA9TVsSAnZWagAA3tUP/2eX0M7E1wxDCMKrL2SH\n2lvmhaHXSfJioEqAO7gPyTXD73QTAE0MGFKHEZXFtCvxLjNxtnsXkOWH+VUZ\ntgg6rS/553cFI3rCGm9QJ3bjFnQ6DCxQK5Lyfq/Yi7iTcE4gj2LYQ2ivn3Jr\n6meuQ2V+IyZskAn9Dpl8uxJASJRlMsdB4qQo8bdCfnUlHDcQbHaH8N+PxZvq\nlB7yrmOrK4iCXfb1In3X/SXhEKFAExSzh1gOwCsMNAkBua58yWns8JhyfErJ\nSzb7EFrxBaTQyf4H2gzZCHj4nrmzrCzj5vrLGs2uUaxbYhJT8WlC4wnfPBuc\nRZFrFcHYWafNyMFE6G/VpNDHU4vuRiYTDjeJuehFb4d77lcmmWky9AE17qwE\nhsQJBa0/3fiuKc1Atey9t+M5pLDN/pfqnEavdCsy8oOXsC7FEqfNP6NEnZ60\n+pJzLMzIvr/vXKosM4xA7eW4b/yMlVPIz1Ckgj7TOJ/nEEiJjx3E7zuBWfqr\nWeYjfDBuxVQvJjbw5xrBlamPnd92oGxVqhBmqJFt7SmIbYszFV9mej92EFKo\n+Xs85YDTGf8baXn7VClhlo2i41PkZWqVWnKV6hCsGXpI9Lq1bRFvJO9h1KM5\ntVNwxuBhsrgtgmULza1hCp/kba1SVRmlQ3CS1+sHqr4WJpRYB4Ij8/pJSH0Y\nptQN\r\n=3oAi\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCUlrNa7C8tBm8WzaYFqWkouGmNEJKd7KnqgxVhYntDtwIgDX4DfPptD5kpnVQ50nqqEs4W+Ej/5GbnJA3P2UeW5pY="}]},"maintainers":[{"name":"airyrooms-engineering","email":"engineering@airyrooms.com"}],"_npmUser":{"name":"airyrooms-engineering","email":"engineering@airyrooms.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/maleo_0.3.1-canary.27_1564473028510_0.6100104759743361"},"_hasShrinkwrap":false},"0.3.1-canary.28":{"name":"@airy/maleo","version":"0.3.1-canary.28","description":"Un-opinionated Universal Rendering Javascript Framework","repository":{"type":"git","url":"git+https://github.com/airyrooms/maleo.js.git","bugs":"https://github.com/airyrooms/maleo.js/issues"},"author":{"name":"Airy"},"license":"MIT","private":false,"sideEffects":false,"scripts":{"build":"rm -rf dist && gulp","watch":"gulp watch","prepublish":"yarn build","start":"node server.js"},"bin":{"maleo":"lib/bin/maleo.js"},"dependencies":{"@babel/core":"7.1.2","@babel/plugin-proposal-class-properties":"7.1.0","@babel/plugin-proposal-decorators":"7.1.2","@babel/plugin-proposal-object-rest-spread":"7.0.0","@babel/plugin-syntax-dynamic-import":"7.0.0","@babel/plugin-transform-runtime":"7.1.0","@babel/preset-env":"7.1.0","@babel/preset-react":"7.0.0","@babel/runtime":"7.1.2","@babel/runtime-corejs2":"7.2.0","babel-loader":"8.0.4","case-sensitive-paths-webpack-plugin":"2.1.2","compression":"1.7.4","es6-promise":"4.2.5","eventsource":"1.0.7","express":"4.16.4","figlet":"1.2.1","flatted":"^2.0.1","friendly-errors-webpack-plugin":"1.7.0","hard-source-webpack-plugin":"0.13.1","helmet":"3.14.0","hoist-non-react-statics":"^3.3.0","isomorphic-fetch":"2.2.1","loader-utils":"1.1.0","node-notifier":"5.2.1","nodemon":"1.18.4","react-dom":"16.8.4","react-helmet":"5.2.0","react-loadable":"5.5.0","react-router-config":"5.0.0","react-router-dom":"5.0.0","rimraf":"2.6.3","source-map-support":"0.5.9","terser-webpack-plugin":"1.1.0","webpack":"4.29.0","webpack-bundle-analyzer":"3.0.3","webpack-cli":"3.1.2","webpack-dev-middleware":"3.4.0","webpack-hot-middleware":"2.24.3","webpackbar":"2.6.3"},"peerDependencies":{"react":"^16.8.4"},"devDependencies":{"@babel/preset-typescript":"^7.1.0","@types/webpack-bundle-analyzer":"^2.13.0","babel-plugin-module-resolver":"^3.1.1","gulp":"github:gulpjs/gulp#4.0","gulp-babel":"^8.0.0","gulp-clean":"^0.4.0","gulp-sourcemaps":"^2.6.4","gulp-uglify":"^3.0.1","gulp-watch":"^5.0.1","react":"^16.8.4"},"gitHead":"178b4a0a4c42a600c1e4a23cd3b600c890f862f4","readme":"[![Build Status](https://travis-ci.org/airyrooms/maleo.js.svg?branch=master)](https://travis-ci.org/airyrooms/maleo.js)\n[![Coverage Status](https://coveralls.io/repos/github/airyrooms/maleo.js/badge.svg?branch=master)](https://coveralls.io/github/airyrooms/maleo.js?branch=master)\n[![npm version](https://badge.fury.io/js/%40airy%2Fmaleo.svg)](https://badge.fury.io/js/%40airy%2Fmaleo)\n[![Issues](http://img.shields.io/github/issues/airyrooms/maleo.js.svg)]( https://github.com/airyrooms/maleo.js/issues)\n[![MIT license](https://img.shields.io/badge/License-MIT-blue.svg)](https://github.com/airyrooms/maleo.js/blob/canary/LICENSE)\n[![Discord Chat](https://img.shields.io/discord/550498214789513218.svg)](https://discord.gg/9eArCQn)\n[![Conventional Commits](https://img.shields.io/badge/Conventional%20Commits-1.0.0-green.svg)](https://conventionalcommits.org)\n\n\n# Welcome to Maleo.js\n\nMaleo.js is an un-opinionated framework to enable Universal Rendering in JavaScript using React with no hassle.\n\nWe are here to solve the time consuming setups Universal Rendering Required.\n\n---\n\nReadme below is the documentation for the `canary` (prerelease) branch. To view the documentation for the latest stable Maleo.js version change branch to `master`\n\n---\n\n# Table of Contents\n- [Features](#features)\n- [Setup](#setup)\n- [Component Lifecycle](#component-lifecycle)\n- [Routing](#routing)\n- [Dynamic Import Component](#dynamic-import-component)\n  - [Tips](#tips)\n  - [Preloading](#preloading)\n- [Customizable Component](#customizable-component)\n  - [Custom Document](#custom-document)\n  - [Custom Wrap](#custom-wrap)\n- [Custom Configuration](#custom-configuration)\n  - [Customize Server](#customize-server)\n  - [Customize Webpack](#customize-webpack)\n  - [Customize Babel Config](#customize-babel-config)\n- [Core Utilities Functions](#core-utilities-functions)\n- [CDN Support](#cdn-support)\n- [Plugins](#plugins)\n- [FAQ](#faq)\n- [Contributing](#contributing)\n\n---\n\n## Features\n- Universal Rendering\n- Plugin based framework\n- Customizable\n\n## Setup\n\nInstall Maleo.js \n\n>*for now change `@airy/maleo` to `@airy/maleo@canary` until we publish the stable version of this package*\n\n\n**NPM**\n```bash\n$ npm install --save @airy/maleo react\n```\n**Yarn**\n```bash\n$ yarn add @airy/maleo react\n```\n\nAdd this script to your `package.json`\n```json\n{\n  \"scripts\": {\n    \"dev\": \"maleo dev\",\n    \"build\": \"export NODE_ENV=production && maleo build\",\n    \"start\": \"export NODE_ENV=production && maleo run\"\n  }\n}\n```\n\nCreate a page Root component\n```jsx\n// ./src/Root.jsx\nimport React from 'react';\n\n// Export default is required for registering page\nexport default class RootComponent extends React.Component {\n  render() {\n    return (\n      <h1>Hello World!</h1>\n    )\n  }\n}\n```\n\nAnd lastly, create a routing file on your project root directory called `routes.json` and register your page component\n```json\n[\n  {\n    \"path\": \"/\",\n    \"page\": \"./src/Root\"\n  }\n]\n```\n\nAfter that you can now run `$ npm run dev` and go to `http://localhost:3000`.\n\nYou should now see your app running on your browser.\n\nBy now you should see \n- Automatic transpilation and bundling (with webpack and babel)\n- ~~Hot code reloading~~ [#17](https://github.com/airyrooms/maleo.js/issues/17)\n- Server rendering\n\nTo see how simple this is, check out the sample app!\n\n## Component Lifecycle \n\nMaleo.js added a new component lifecycle hook called `getInitialProps`, this function is called during Server Side Rendering (SSR) and during route changes on Client Side Rendering (CSR).\n\nThis is useful especially for SEO purposes. \n\nExample for stateful component:\n```jsx\nimport React from 'react';\n\nexport default class extends React.Component {\n  static getInitialProps = async (ctx) => {\n    const { req } = ctx;\n\n    // check if getInitialProps is called on server or on client\n    const userAgent = req ? req.headers['user-agent'] : navigator.userAgent;\n\n    // the return value will be passed as props for this component\n    return { userAgent };\n  }\n\n  render() {\n    return (\n      <div>\n        Hello World {this.props.userAgent}\n      </div>\n    );\n  }\n}\n```\n\nExample for stateless component:\n```jsx\nconst Component = (props) => (\n  <div>\n    Hello World {props.userAgent}\n  </div>\n);\n\nComponent.getInitialprops = async (ctx) => {\n  const { req } = ctx;\n\n  const userAgent = req ? req.headers['user-agent'] : navigator.userAgent;\n\n  return { userAgent };\n};\n\nexport default Component;\n```\n\n`getInitialProps` receives a context object with the following properties:\n- `req` - HTTP request object (server only)\n- `res` - HTTP response object (server only)\n- `...wrapProps` - Spreaded properties from custom Wrap\n- `...appProps` - Spreaded properties from custom App\n- `matched` - Matched routes contains list of object, which is consists of `match` object and `route` object\n- `route` - Component's route attributes on `routes.json` file\n\n## Routing\n\nRouting is declared in a centralized route config.\nRegister all the route config in `routes.json` file.\n\nIf you put the `routes.json` files on root directory, Maleo will automatically register your route. Otherwise put path to your routes on [Maleo config](#custom-configuration).\n\nRoutes file has to export default the route configuration.\nThe route object **expected to have distinct key** to indicate the route.\n\n<table>\n  <tr>\n    <td>Key</td>\n    <td>Type</td>\n    <td>Description</td>\n  </tr>\n  <tr>\n    <td><code>path</code></td>\n    <td><code>String!</code></td>\n    <td>Routes path</td>\n  </tr>\n  <tr>\n    <td><code>page</code></td>\n    <td><code>String!</code></td>\n    <td>Path to React Component for this route path</td>\n  </tr>\n  <tr>\n    <td><code>exact</code></td>\n    <td><code>Boolean?</code> [<code>false</code>]</td>\n    <td>To make url has to match exactly the path in order to render the component. Give <code>false</code> value if the route is a wrapper route component</td>\n  </tr>\n  <tr>\n    <td><code>routes</code></td>\n    <td><code>RouteObject?</code></td>\n    <td>Nested route</td>\n  </tr>\n</table>\n\n\nFor example:\n```json\n[\n  {\n    \"page\": \"./src/MainApp\",\n    \"routes\": [\n      {\n        \"path\": \"/\",\n        \"page\": \"./src/Search\",\n        \"exact\": true\n      },\n      {\n        \"path\": \"/search\",\n        \"page\": \"./src/Search\",\n        \"routes\": [\n          {\n            \"path\": \"/search/hello\",\n            \"page\": \"./src/Detail\",\n            \"exact\": true\n          }\n        ]\n      },\n      {\n        \"path\": \"/detail\",\n        \"page\": \"./src/Detail\",\n        \"exact\": true\n      }\n    ]\n  }\n]\n```\n\n## Dynamic Import Component\nMaleo.js supports TC39 [dynamic import proposal](https://github.com/tc39/proposal-dynamic-import) for JavaScript.\n\nYou can think dynamic import as another way to split your code into manageable chunks. You can use our `Dynamic` function which utilizes [react loadable](https://github.com/jamiebuilds/react-loadable)\n\nFor Example\n```js\n// DynamicComponent.js\nimport Dynamic from '@airy/maleo/dynamic';\n\nexport default Dynamic({\n  loader: () => import( /* webpackChunkName:\"DynamicComponent\" */ './component/DynamicLoad'),\n})\n```\n\n### **Preloading**\n\nFor optimization purposes, you can also preload a component even before the component got rendered.\n\nFor example, if you want to load component when a button get pressed, you can start preloading the component when the user hovers the mouse over the button.\n\nThe component created by `Dynamic` exposes a [static `preload` method](https://github.com/jamiebuilds/react-loadable#loadablecomponentpreload).\n\n```jsx\nimport React from 'react';\nimport Dynamic from '@airy/maleo/dynamic';\n\nconst DynamicBar = Dynamic({\n  loader: () => import('./Bar'),\n  loading: LoadingCompoinent\n});\n\nclass MyComponent extends React.Component {\n  state = { showBar: false };\n\n  onClick = () => {\n    this.setState({ showBar: true });\n  };\n\n  onMouseOver = () => DynamicBar.preload();\n\n  render() {\n    return (\n      <div>\n        <button\n          onClick={this.onClick}\n          onMouseOver={this.onMouseOver}>\n          Show Bar\n        </button>\n        { this.state.showBar && <DynamicBar /> }\n      </div>\n    )\n  }\n}\n```\n\n## Customizable Component\n\n### Custom Document\n\nHighly inspired by what [Next.js](https://github.com/zeit/next.js) has done on their awesome template customization.\n\nMaleo.js also enable customization on `Document` as document's markup. So you don't need to include tags like `<html>`, `<head>`, `<body>`, etc. \n\nTo override the default behavior, you'll need to create a component that extends the `Document` React class provided by Maleo.\n\n```jsx\n// document.jsx\nimport React from 'react';\nimport { Document, Header, Main, Scripts } from '@airy/maleo/document';\n\nexport default class extends Document {\n  render() {\n    return (\n      <html>\n        <Header>\n          <title>Maleo JS</title>\n          <meta charset=\"utf-8\" />\n          <meta name=\"description\" content=\"Maleo.js is awesome!\" />\n\n          <style>\n            {` body { background-color: #fff } `}\n          </style>\n        </Header>\n\n        <body>\n          <Main />\n\n          <Scripts />\n\n          <ReduxScript />\n        </body>\n      </html>\n    );\n  }\n}\n```\n\n### Custom Wrap\n\nMaleo.js uses the `Wrap` component to initialize pages. `Wrap` contains React Router's Component. You can add HoC here to wrap the application and control the page initialization. Which allows you to do amazing things like:\n- Persisting layour between page changes\n- Keeping state when navigating pages\n- Custom error handling using `componentDidCatch`\n- Inject additional data into pages (like Redux provider, etc)\n\nTo override the default behavior, you'll need to create a component that extends the `Wrap` React class provided by Maleo.\n\n```jsx\n// wrap.jsx\nimport React from 'react';\nimport { Wrap } from '@airy/maleo/wrap';\n\n// Redux plugin for Maleo.js\n// Hoc that creates store and add Redux Provider\nimport { withRedux } from '@airy/maleo-redux-plugin';\n\n// Custom Wrapper that will be rendered for the whole Application\nimport Content from './component/Content';\nimport NavigationBar from './component/NavigationBar';\n\nimport { createStore } from './store';\n\n@withRedux(createStore)\nexport default class extends Wrap {\n  static getInitialProps = (ctx) => {\n    const { store } = ctx\n    // you receive store from context\n    // you can access or do something with the store here\n    console.log('Initialized Redux Store', store);\n    return {}\n  }\n\n  render() {\n    const { Container, containerProps, App, appProps } = this.props\n\n    return (\n      <Container {...containerProps}>\n        <NavigationBar />\n        <Content>\n          <App {...appProps}/>\n        </Content>\n      </Container>\n    )\n  }\n}\n\n```\nIf you put `document.jsx` and `wrap.jsx` on root directory (the same level with `package.json`), then Maleo will automatically register your custom Document and Wrap. Otherwise, you can add the path to your custom Document and Wrap on [Maleo config](#custom-configuration)\n\n---\n***We are also working on adding default and customizable `Error` component page***\n\n---\n\n## Custom Configuration\n\nFor more advanced configuration of Maleo.js, like `webpack` config, registering `plugins`, path to your routes, custom Document and Wrap, and adding `path alias`, you can create a `maleo.config.js` in the root of your project directory. (same directory with `package.json`)\n\n```js\n// maleo.config.js\n\nmodule.exports = {\n  /* config options here */\n}\n```\n\nHere are the API's for the configuration:\n\n<table>\n  <tr>\n    <td>Key</td>\n    <td>Type</td>\n    <td>Description</td>\n  </tr>\n  <tr>\n    <td><code>favicon</code></td>\n    <td><code>String</code> [<code>{project-dir}/favicon.ico</code>]</td>\n    <td>Path to favicon file</td>\n  </tr>\n  <tr>\n    <td><code>buildDir</code></td>\n    <td><code>String?</code> [<code>.maleo</code>]</td>\n    <td>Directory to put Maleo.js' build assets</td>\n  </tr>\n  <tr>\n    <td><code>cache</code></td>\n    <td><code>Boolean?</code> [<code>true</code>]</td>\n    <td>Enable webpack build caching</td>\n  </tr>\n  <tr>\n    <td><code>isDev</code></td>\n    <td><code>Boolean?</code> [<code>process.env.NODE_ENV === 'development'</code>]</td>\n    <td>Enable development build configuration</td>\n  </tr>\n  <tr>\n    <td><code>sourceMaps</code></td>\n    <td><code>Boolean?</code> [<code>true</code>]</td>\n    <td>Enable webpack to generate source maps</td>\n  </tr>\n  <tr>\n    <td><code>alias</code></td>\n    <td><code>Object?</code></td>\n    <td>A key value pair for aliasing path directory\n      <br>\n      <code>\n      { 'component': './src/component' }\n      </code>\n    </td>\n  </tr>\n  <tr>\n    <td><code>publicPath</code></td>\n    <td><code>String?</code> [<code>/_assets/</code>]</td>\n    <td>To customize webpack's <code>publicPath</code> \n    <br>Comes in handy if using CDN to put built assets</td>\n  </tr>\n  <tr>\n    <td><code>analyzeBundle</code></td>\n    <td><code>Boolean?</code> [<code>false</code>]</td>\n    <td>To enable webpack's bundle analyzer, for analyzing bundle sizes during bundle debugging should Maleo.js' build process got slow</td>\n  </tr>\n  <tr>\n    <td><code>webpack</code></td>\n    <td><code>Function?</code></td>\n    <td>To customize webpack configuration, more details <a href=\"#customize-webpack\">here</a></td>\n  </tr>\n  <tr>\n    <td><code>routes</code></td>\n    <td><code>string?</code> [<code>rootDir/routes.jsx</code>]</td>\n    <td>Path to your routes file</td>\n  </tr>\n  <tr>\n    <td><code>customDocument</code></td>\n    <td><code>string?</code> [<code>rootDir/document.jsx</code>]</td>\n    <td>Path to your custom document file</td>\n  </tr>\n  <tr>\n    <td><code>customWrap</code></td>\n    <td><code>string?</code> [<code>rootDir/wrap.jsx</code>]</td>\n    <td>Path to your custom wrap file</td>\n  </tr>\n  <tr>\n    <td><code>customApp</code></td>\n    <td><code>string?</code> [<code>rootDir/app.jsx</code>]</td>\n    <td>Path to your custom app file</td>\n  </tr>\n  <tr>\n    <td><code>csp</code></td>\n    <td><code>boolean | cspConfig</code> [<code>false</code>]</td>\n    <td>Config for <a href=\"https://developers.google.com/web/fundamentals/security/csp/\">Content Security Policy</a>, using <a href=\"https://helmetjs.github.io/docs/csp/\">Helmet-CSP</a>\n    <br/>\n    If set to true it will enable default config:\n    <br/>\n    <code>{\n      directives: {\n        defaultSrc: [`'self'`],\n        styleSrc: [`'self'`],\n      },\n    }</code>\n    </td>\n  </tr>\n</table>\n\n#### Customize Server\n\nCreate a `server.js` file on root directory where your `package.json` lives.\nHere you can customize Maleo's server.\n```js\nimport { Server } from '@airy/maleo/server';\n\nconst PORT = process.env.PORT || 3000;\n\nconst maleoServer = Server.init({\n  port: PORT,\n  runHandler: () => {\n    console.log('Server running on port :', PORT);\n  }\n});\n\nmaleoServer.run();\n```\n\nHere are the API's for the configuration:\n\n<table>\n  <tr>\n    <td>Key</td>\n    <td>Type</td>\n    <td>Description</td>\n  </tr>\n  <tr>\n    <td><code>port</code></td>\n    <td><code>Number?</code> [<code>3000</code>]</td>\n    <td>Port to run Maleo server</td>\n  </tr>\n  <tr>\n    <td><code>assetDir</code></td>\n    <td><code>String?</code> [<code>'<root>/.maleo/client'</code>]</td>\n    <td>Directory for all client related assets</td>\n  </tr>\n  <tr>\n    <td><code>runHandler</code></td>\n    <td><code>Function?</code></td>\n    <td>Function called when maleo server starter</td>\n  </tr>\n</table>\n\n#### Customize Webpack\n\nYou are able to extend Maleo.js' default webpack configuration by defining a function on `maleo.config.js`\n\n```js\n// maleo.config.js\n\nmodule.exports = {\n  webpack(config, context, next) {\n    // Perform customizations to webpack config\n    // Important: This call is required in order to let Maleo pass the config to webpack\n    return next(); \n  },\n};\n```\n\nWebpack function will receive three arguments:\n\n<table>\n  <tr>\n    <td>Argument</td>\n    <td>Details</td>\n  <tr>\n  <tr>\n    <td><code>config</code></td>\n    <td>This contains webpack configuration object that you can manipulate</td>\n  <tr>\n  <tr>\n    <td><code>context</code></td>\n    <td>This contains some keys that are useful for the build context\n      <br>\n      <table>\n        <tr>\n          <td><code>isDev</code></td>\n          <td><code>Boolean</code></td>\n          <td>Check if current build is for development</td>\n        </tr>\n        <tr>\n          <td><code>publicPath</code></td>\n          <td><code>String</code></td>\n          <td>Public Path of user defined or default's value</td>\n        </tr>\n        <tr>\n          <td><code>analyzeBundle</code></td>\n          <td><code>Boolean</code></td>\n          <td>Check if analyze bundle is enabled</td>\n        </tr>\n        <tr>\n          <td><code>buildDirectory</code></td>\n          <td><code>String</code></td>\n          <td>Build Directory of user defined or default's value</td>\n        </tr>\n        <tr>\n          <td><code>name</code></td>\n          <td><code>String</code></td>\n          <td>Build name <code>'server' || 'client'</code></td>\n        </tr>\n      </table>\n    </td>\n  <tr>\n  <tr>\n    <td><code>next</code></td>\n    <td>A callback required to be called to pass the custom configuration</td>\n  <tr>\n</table>\n\nExample of adding `ts-loader` through `maleo.config.js`:\n\n```js\n// maleo.config.js\n\n// Partly taken and modified from @airy/maleo-ts-plugin source\n// for simplicity purposes\n\nmodule.exports = {\n  webpack(config, context, next) {\n    const { isDev } = context\n\n    config.module.rules.push({\n      test: /\\.tsx?/,\n      exclude: /node_modules/,\n      use: [\n        require.resolve('@airy/maleo/lib/build/loaders/maleo-babel-loader'),\n        {\n          loader: 'ts-loader',\n          options: {\n            transpileOnly: true,\n          },\n        },\n      ],\n\n      if (isDev) {\n        config.plugins.push(new ForkTSCheckerWebpackPlugin());\n      }\n\n      return next();\n    })\n  },\n};\n```\n\n#### Customize Babel Config\n\nMaleo.js also let you have your own babel config. Just simply add `.babelrc` file at the root directory of your app.\n\nYou can include Maleo.js' babel preset in order to have latest JavaScript preset.\n\nHere's an example of `.babelrc` file:\n```json\n{\n  \"presets\": [\"@airy/maleo/babel\"],\n  \"plugins\": []\n}\n```\n\nThe `@airy/maleo/babel` preset includes everything you need to get your development started. The preset includes:\n- `@babel/preset-env`\n- `@babel/preset-react`\n- `@babel/plugin-proposal-class-properties`\n- `@babel/plugin-proposal-decorators`\n- `@babel/plugin-proposal-object-rest-spread`\n- `@babel/plugin-transform-runtime`\n- `react-loadable/babel`\n\n## Core Utilities Functions\n\n#### Route Matching\n\nTo use you can import the matching routes function like this:\n```js\nimport { getMatchedRoutes } from '@airy/maleo/utils';\n```\n\nThe function will return all matched routes and the route object from `routes.json`\n\n## CDN Support\n\nIf you are using a CDN, you can set up the `publicPath` setting and configure your CDN's origin to resolve to the domain that Maleo.js is hosted on.\n\n```js\n// maleo.config.js\n\nconst isProd = process.env.NODE_ENV === 'production';\n\nmodule.exports = {\n  assetPrefix: isProd && 'https://cdn.example.com';\n}\n```\n\n## FAQ\n\n== TO BE DETERMINED == \n\n## Plugins\n- [css-plugin](https://github.com/airyrooms/maleo-plugins/tree/master/packages/css-plugin)\n- [redux-plugin](https://github.com/airyrooms/maleo-plugins/tree/master/packages/redux-plugin)\n- [typescript-plugin](https://github.com/airyrooms/maleo-plugins/tree/master/packages/typescript-plugin)\n- [more](https://github.com/airyrooms/maleo-plugins)\n\n\n## Contributing\n[Please follow these steps to contribute to Maleo.js](https://github.com/airyrooms/maleo.js/blob/canary/CONTRIBUTING.md)\n\n[Please follow these steps to contribute to Maleo.js' plugins](https://github.com/airyrooms/maleo-plugins#contributing-guidelines)\n\n\n## Contributors\n\nThis project exists thanks to all the people who contribute. [Contribute](CONTRIBUTING.md).\n\nMany thanks to our **[contributors](https://github.com/airyrooms/maleo.js/graphs/contributors)**!\n\n\n## License\n\nMIT","readmeFilename":"README.md","bugs":{"url":"https://github.com/airyrooms/maleo.js/issues"},"homepage":"https://github.com/airyrooms/maleo.js#readme","_id":"@airy/maleo@0.3.1-canary.28","_nodeVersion":"10.16.0","_npmVersion":"lerna/3.13.1/node@v10.16.0+x64 (linux)","dist":{"integrity":"sha512-+hrsDstAW3YYhWaynmdGglTFaFWtdj2Tp4P/1Hh5IhsBwER1uARGi69jKgnHpyRpayFtSz3kQgSaxCWIVX6huw==","shasum":"4378d4d3dfd614df04c7f34c458ec913d11daf15","tarball":"https://registry.npmjs.org/@airy/maleo/-/maleo-0.3.1-canary.28.tgz","fileCount":107,"unpackedSize":378116,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJdQUPUCRA9TVsSAnZWagAAMUQP/R3/JJcKJLmMl+paweg4\ntnB+jBunr5MsHXIJW2TeOpjiLIioD37dqyJQiwaXmhmMopA5OhlYwLbWV5N5\niGAyCLwoiJdxOk+btw4+zd2nNPrPwx2t5ashGZZI5vtQvQ5Gds3BS9T0lgta\n2gHf0ZUtxzpb9C4IwmKYwWtHarr++taKcztZwK87Ze9KEfWZfgg6PP5NVI3o\nb10pfDmasX+PApkz1O1vsfFV3jo5pvyvHZXRExn2BdDr5ZE0mpI9jIIuDzT8\n8xS6DdWPB+ZcjwRgrbGj+caaI1Fyt6H2RZU4wKe3aVVCCF2Ix/lvoxzrwZ+M\n3+ohlg9SaR3/H70k5HrTzREOiQgCMZPw7C+E6IFPhVLu/fS3BYl3zo30ObPP\nTG5aE9j5wznkww/3JGLRMxnyvdpdctoenY5g6ea7mgtzdwKgoE9vzkRZ9/9f\nSeQ4WvNEYKOCnyuFXE1tReyDqfV0BBh3cd7h/3ejROlGsJtq2uNT4NlEXVHh\njZiIvnWoCjPhmMK1lCiilzMcUD+MPJ6yDKC2w8fF/TdQ24cQh2V+5bgCBFWk\nslRmc5k+8ep/WA9xxzdxgp0pd5QFpGLquy6WCkUeByWku2zMlHVJelkSINkt\nGDczXqmRiXqMoNCYDXT57y5rb0ph0ZrJBwXknqlWBFTSN2WzEk/gRCNw9rXB\nirCi\r\n=hf5r\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIG2bcuBBfN/lDPgc5iqOt5Q9wwygZ9tPAZZ4Ea753gvLAiEAtV8SvbDW3xzABZsJnR1UetXJS3kYtmsAtjt+LbSDHaw="}]},"maintainers":[{"name":"airyrooms-engineering","email":"engineering@airyrooms.com"}],"_npmUser":{"name":"airyrooms-engineering","email":"engineering@airyrooms.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/maleo_0.3.1-canary.28_1564558292251_0.4309773115802593"},"_hasShrinkwrap":false},"0.3.1-canary.29":{"name":"@airy/maleo","version":"0.3.1-canary.29","description":"Un-opinionated Universal Rendering Javascript Framework","repository":{"type":"git","url":"git+https://github.com/airyrooms/maleo.js.git","bugs":"https://github.com/airyrooms/maleo.js/issues"},"author":{"name":"Airy"},"license":"MIT","private":false,"sideEffects":false,"scripts":{"build":"rm -rf dist && gulp","watch":"gulp watch","prepublish":"yarn build","start":"node server.js"},"bin":{"maleo":"lib/bin/maleo.js"},"dependencies":{"@babel/core":"7.1.2","@babel/plugin-proposal-class-properties":"7.1.0","@babel/plugin-proposal-decorators":"7.1.2","@babel/plugin-proposal-object-rest-spread":"7.0.0","@babel/plugin-syntax-dynamic-import":"7.0.0","@babel/plugin-transform-runtime":"7.1.0","@babel/preset-env":"7.1.0","@babel/preset-react":"7.0.0","@babel/runtime":"7.1.2","@babel/runtime-corejs2":"7.2.0","babel-loader":"8.0.4","babel-plugin-transform-react-remove-prop-types":"^0.4.24","case-sensitive-paths-webpack-plugin":"2.1.2","compression":"1.7.4","es6-promise":"4.2.5","eventsource":"1.0.7","express":"4.16.4","figlet":"1.2.1","flatted":"^2.0.1","friendly-errors-webpack-plugin":"1.7.0","hard-source-webpack-plugin":"0.13.1","helmet":"3.14.0","hoist-non-react-statics":"^3.3.0","isomorphic-fetch":"2.2.1","loader-utils":"1.1.0","node-notifier":"5.2.1","nodemon":"1.18.4","react-dom":"16.8.4","react-helmet":"5.2.0","react-loadable":"5.5.0","react-router-config":"5.0.0","react-router-dom":"5.0.0","rimraf":"2.6.3","source-map-support":"0.5.9","terser-webpack-plugin":"1.1.0","webpack":"4.29.0","webpack-bundle-analyzer":"3.0.3","webpack-cli":"3.1.2","webpack-dev-middleware":"3.4.0","webpack-hot-middleware":"2.24.3","webpackbar":"2.6.3"},"peerDependencies":{"react":"^16.8.4"},"devDependencies":{"@babel/preset-typescript":"^7.1.0","@types/webpack-bundle-analyzer":"^2.13.0","babel-plugin-module-resolver":"^3.1.1","gulp":"github:gulpjs/gulp#4.0","gulp-babel":"^8.0.0","gulp-clean":"^0.4.0","gulp-sourcemaps":"^2.6.4","gulp-uglify":"^3.0.1","gulp-watch":"^5.0.1","react":"^16.8.4"},"gitHead":"cbc1afb7f9cafe77778fe487007dab9f783e1c08","readme":"[![Build Status](https://travis-ci.org/airyrooms/maleo.js.svg?branch=master)](https://travis-ci.org/airyrooms/maleo.js)\n[![Coverage Status](https://coveralls.io/repos/github/airyrooms/maleo.js/badge.svg?branch=master)](https://coveralls.io/github/airyrooms/maleo.js?branch=master)\n[![npm version](https://badge.fury.io/js/%40airy%2Fmaleo.svg)](https://badge.fury.io/js/%40airy%2Fmaleo)\n[![Issues](http://img.shields.io/github/issues/airyrooms/maleo.js.svg)]( https://github.com/airyrooms/maleo.js/issues)\n[![MIT license](https://img.shields.io/badge/License-MIT-blue.svg)](https://github.com/airyrooms/maleo.js/blob/canary/LICENSE)\n[![Discord Chat](https://img.shields.io/discord/550498214789513218.svg)](https://discord.gg/9eArCQn)\n[![Conventional Commits](https://img.shields.io/badge/Conventional%20Commits-1.0.0-green.svg)](https://conventionalcommits.org)\n\n\n# Welcome to Maleo.js\n\nMaleo.js is an un-opinionated framework to enable Universal Rendering in JavaScript using React with no hassle.\n\nWe are here to solve the time consuming setups Universal Rendering Required.\n\n---\n\nReadme below is the documentation for the `canary` (prerelease) branch. To view the documentation for the latest stable Maleo.js version change branch to `master`\n\n---\n\n# Table of Contents\n- [Features](#features)\n- [Setup](#setup)\n- [Component Lifecycle](#component-lifecycle)\n- [Routing](#routing)\n- [Dynamic Import Component](#dynamic-import-component)\n  - [Tips](#tips)\n  - [Preloading](#preloading)\n- [Customizable Component](#customizable-component)\n  - [Custom Document](#custom-document)\n  - [Custom Wrap](#custom-wrap)\n- [Custom Configuration](#custom-configuration)\n  - [Customize Server](#customize-server)\n  - [Customize Webpack](#customize-webpack)\n  - [Customize Babel Config](#customize-babel-config)\n- [Core Utilities Functions](#core-utilities-functions)\n- [CDN Support](#cdn-support)\n- [Plugins](#plugins)\n- [FAQ](#faq)\n- [Contributing](#contributing)\n\n---\n\n## Features\n- Universal Rendering\n- Plugin based framework\n- Customizable\n\n## Setup\n\nInstall Maleo.js \n\n>*for now change `@airy/maleo` to `@airy/maleo@canary` until we publish the stable version of this package*\n\n\n**NPM**\n```bash\n$ npm install --save @airy/maleo react\n```\n**Yarn**\n```bash\n$ yarn add @airy/maleo react\n```\n\nAdd this script to your `package.json`\n```json\n{\n  \"scripts\": {\n    \"dev\": \"maleo dev\",\n    \"build\": \"export NODE_ENV=production && maleo build\",\n    \"start\": \"export NODE_ENV=production && maleo run\"\n  }\n}\n```\n\nCreate a page Root component\n```jsx\n// ./src/Root.jsx\nimport React from 'react';\n\n// Export default is required for registering page\nexport default class RootComponent extends React.Component {\n  render() {\n    return (\n      <h1>Hello World!</h1>\n    )\n  }\n}\n```\n\nAnd lastly, create a routing file on your project root directory called `routes.json` and register your page component\n```json\n[\n  {\n    \"path\": \"/\",\n    \"page\": \"./src/Root\"\n  }\n]\n```\n\nAfter that you can now run `$ npm run dev` and go to `http://localhost:3000`.\n\nYou should now see your app running on your browser.\n\nBy now you should see \n- Automatic transpilation and bundling (with webpack and babel)\n- ~~Hot code reloading~~ [#17](https://github.com/airyrooms/maleo.js/issues/17)\n- Server rendering\n\nTo see how simple this is, check out the sample app!\n\n## Component Lifecycle \n\nMaleo.js added a new component lifecycle hook called `getInitialProps`, this function is called during Server Side Rendering (SSR) and during route changes on Client Side Rendering (CSR).\n\nThis is useful especially for SEO purposes. \n\nExample for stateful component:\n```jsx\nimport React from 'react';\n\nexport default class extends React.Component {\n  static getInitialProps = async (ctx) => {\n    const { req } = ctx;\n\n    // check if getInitialProps is called on server or on client\n    const userAgent = req ? req.headers['user-agent'] : navigator.userAgent;\n\n    // the return value will be passed as props for this component\n    return { userAgent };\n  }\n\n  render() {\n    return (\n      <div>\n        Hello World {this.props.userAgent}\n      </div>\n    );\n  }\n}\n```\n\nExample for stateless component:\n```jsx\nconst Component = (props) => (\n  <div>\n    Hello World {props.userAgent}\n  </div>\n);\n\nComponent.getInitialprops = async (ctx) => {\n  const { req } = ctx;\n\n  const userAgent = req ? req.headers['user-agent'] : navigator.userAgent;\n\n  return { userAgent };\n};\n\nexport default Component;\n```\n\n`getInitialProps` receives a context object with the following properties:\n- `req` - HTTP request object (server only)\n- `res` - HTTP response object (server only)\n- `...wrapProps` - Spreaded properties from custom Wrap\n- `...appProps` - Spreaded properties from custom App\n- `matched` - Matched routes contains list of object, which is consists of `match` object and `route` object\n- `route` - Component's route attributes on `routes.json` file\n\n## Routing\n\nRouting is declared in a centralized route config.\nRegister all the route config in `routes.json` file.\n\nIf you put the `routes.json` files on root directory, Maleo will automatically register your route. Otherwise put path to your routes on [Maleo config](#custom-configuration).\n\nRoutes file has to export default the route configuration.\nThe route object **expected to have distinct key** to indicate the route.\n\n<table>\n  <tr>\n    <td>Key</td>\n    <td>Type</td>\n    <td>Description</td>\n  </tr>\n  <tr>\n    <td><code>path</code></td>\n    <td><code>String!</code></td>\n    <td>Routes path</td>\n  </tr>\n  <tr>\n    <td><code>page</code></td>\n    <td><code>String!</code></td>\n    <td>Path to React Component for this route path</td>\n  </tr>\n  <tr>\n    <td><code>exact</code></td>\n    <td><code>Boolean?</code> [<code>false</code>]</td>\n    <td>To make url has to match exactly the path in order to render the component. Give <code>false</code> value if the route is a wrapper route component</td>\n  </tr>\n  <tr>\n    <td><code>routes</code></td>\n    <td><code>RouteObject?</code></td>\n    <td>Nested route</td>\n  </tr>\n</table>\n\n\nFor example:\n```json\n[\n  {\n    \"page\": \"./src/MainApp\",\n    \"routes\": [\n      {\n        \"path\": \"/\",\n        \"page\": \"./src/Search\",\n        \"exact\": true\n      },\n      {\n        \"path\": \"/search\",\n        \"page\": \"./src/Search\",\n        \"routes\": [\n          {\n            \"path\": \"/search/hello\",\n            \"page\": \"./src/Detail\",\n            \"exact\": true\n          }\n        ]\n      },\n      {\n        \"path\": \"/detail\",\n        \"page\": \"./src/Detail\",\n        \"exact\": true\n      }\n    ]\n  }\n]\n```\n\n## Dynamic Import Component\nMaleo.js supports TC39 [dynamic import proposal](https://github.com/tc39/proposal-dynamic-import) for JavaScript.\n\nYou can think dynamic import as another way to split your code into manageable chunks. You can use our `Dynamic` function which utilizes [react loadable](https://github.com/jamiebuilds/react-loadable)\n\nFor Example\n```js\n// DynamicComponent.js\nimport Dynamic from '@airy/maleo/dynamic';\n\nexport default Dynamic({\n  loader: () => import( /* webpackChunkName:\"DynamicComponent\" */ './component/DynamicLoad'),\n})\n```\n\n### **Preloading**\n\nFor optimization purposes, you can also preload a component even before the component got rendered.\n\nFor example, if you want to load component when a button get pressed, you can start preloading the component when the user hovers the mouse over the button.\n\nThe component created by `Dynamic` exposes a [static `preload` method](https://github.com/jamiebuilds/react-loadable#loadablecomponentpreload).\n\n```jsx\nimport React from 'react';\nimport Dynamic from '@airy/maleo/dynamic';\n\nconst DynamicBar = Dynamic({\n  loader: () => import('./Bar'),\n  loading: LoadingCompoinent\n});\n\nclass MyComponent extends React.Component {\n  state = { showBar: false };\n\n  onClick = () => {\n    this.setState({ showBar: true });\n  };\n\n  onMouseOver = () => DynamicBar.preload();\n\n  render() {\n    return (\n      <div>\n        <button\n          onClick={this.onClick}\n          onMouseOver={this.onMouseOver}>\n          Show Bar\n        </button>\n        { this.state.showBar && <DynamicBar /> }\n      </div>\n    )\n  }\n}\n```\n\n## Customizable Component\n\n### Custom Document\n\nHighly inspired by what [Next.js](https://github.com/zeit/next.js) has done on their awesome template customization.\n\nMaleo.js also enable customization on `Document` as document's markup. So you don't need to include tags like `<html>`, `<head>`, `<body>`, etc. \n\nTo override the default behavior, you'll need to create a component that extends the `Document` React class provided by Maleo.\n\n```jsx\n// document.jsx\nimport React from 'react';\nimport { Document, Header, Main, Scripts } from '@airy/maleo/document';\n\nexport default class extends Document {\n  render() {\n    return (\n      <html>\n        <Header>\n          <title>Maleo JS</title>\n          <meta charset=\"utf-8\" />\n          <meta name=\"description\" content=\"Maleo.js is awesome!\" />\n\n          <style>\n            {` body { background-color: #fff } `}\n          </style>\n        </Header>\n\n        <body>\n          <Main />\n\n          <Scripts />\n\n          <ReduxScript />\n        </body>\n      </html>\n    );\n  }\n}\n```\n\n### Custom Wrap\n\nMaleo.js uses the `Wrap` component to initialize pages. `Wrap` contains React Router's Component. You can add HoC here to wrap the application and control the page initialization. Which allows you to do amazing things like:\n- Persisting layour between page changes\n- Keeping state when navigating pages\n- Custom error handling using `componentDidCatch`\n- Inject additional data into pages (like Redux provider, etc)\n\nTo override the default behavior, you'll need to create a component that extends the `Wrap` React class provided by Maleo.\n\n```jsx\n// wrap.jsx\nimport React from 'react';\nimport { Wrap } from '@airy/maleo/wrap';\n\n// Redux plugin for Maleo.js\n// Hoc that creates store and add Redux Provider\nimport { withRedux } from '@airy/maleo-redux-plugin';\n\n// Custom Wrapper that will be rendered for the whole Application\nimport Content from './component/Content';\nimport NavigationBar from './component/NavigationBar';\n\nimport { createStore } from './store';\n\n@withRedux(createStore)\nexport default class extends Wrap {\n  static getInitialProps = (ctx) => {\n    const { store } = ctx\n    // you receive store from context\n    // you can access or do something with the store here\n    console.log('Initialized Redux Store', store);\n    return {}\n  }\n\n  render() {\n    const { Container, containerProps, App, appProps } = this.props\n\n    return (\n      <Container {...containerProps}>\n        <NavigationBar />\n        <Content>\n          <App {...appProps}/>\n        </Content>\n      </Container>\n    )\n  }\n}\n\n```\nIf you put `document.jsx` and `wrap.jsx` on root directory (the same level with `package.json`), then Maleo will automatically register your custom Document and Wrap. Otherwise, you can add the path to your custom Document and Wrap on [Maleo config](#custom-configuration)\n\n---\n***We are also working on adding default and customizable `Error` component page***\n\n---\n\n## Custom Configuration\n\nFor more advanced configuration of Maleo.js, like `webpack` config, registering `plugins`, path to your routes, custom Document and Wrap, and adding `path alias`, you can create a `maleo.config.js` in the root of your project directory. (same directory with `package.json`)\n\n```js\n// maleo.config.js\n\nmodule.exports = {\n  /* config options here */\n}\n```\n\nHere are the API's for the configuration:\n\n<table>\n  <tr>\n    <td>Key</td>\n    <td>Type</td>\n    <td>Description</td>\n  </tr>\n  <tr>\n    <td><code>favicon</code></td>\n    <td><code>String</code> [<code>{project-dir}/favicon.ico</code>]</td>\n    <td>Path to favicon file</td>\n  </tr>\n  <tr>\n    <td><code>buildDir</code></td>\n    <td><code>String?</code> [<code>.maleo</code>]</td>\n    <td>Directory to put Maleo.js' build assets</td>\n  </tr>\n  <tr>\n    <td><code>cache</code></td>\n    <td><code>Boolean?</code> [<code>true</code>]</td>\n    <td>Enable webpack build caching</td>\n  </tr>\n  <tr>\n    <td><code>isDev</code></td>\n    <td><code>Boolean?</code> [<code>process.env.NODE_ENV === 'development'</code>]</td>\n    <td>Enable development build configuration</td>\n  </tr>\n  <tr>\n    <td><code>sourceMaps</code></td>\n    <td><code>Boolean?</code> [<code>true</code>]</td>\n    <td>Enable webpack to generate source maps</td>\n  </tr>\n  <tr>\n    <td><code>alias</code></td>\n    <td><code>Object?</code></td>\n    <td>A key value pair for aliasing path directory\n      <br>\n      <code>\n      { 'component': './src/component' }\n      </code>\n    </td>\n  </tr>\n  <tr>\n    <td><code>publicPath</code></td>\n    <td><code>String?</code> [<code>/_assets/</code>]</td>\n    <td>To customize webpack's <code>publicPath</code> \n    <br>Comes in handy if using CDN to put built assets</td>\n  </tr>\n  <tr>\n    <td><code>analyzeBundle</code></td>\n    <td><code>Boolean?</code> [<code>false</code>]</td>\n    <td>To enable webpack's bundle analyzer, for analyzing bundle sizes during bundle debugging should Maleo.js' build process got slow</td>\n  </tr>\n  <tr>\n    <td><code>webpack</code></td>\n    <td><code>Function?</code></td>\n    <td>To customize webpack configuration, more details <a href=\"#customize-webpack\">here</a></td>\n  </tr>\n  <tr>\n    <td><code>routes</code></td>\n    <td><code>string?</code> [<code>rootDir/routes.jsx</code>]</td>\n    <td>Path to your routes file</td>\n  </tr>\n  <tr>\n    <td><code>customDocument</code></td>\n    <td><code>string?</code> [<code>rootDir/document.jsx</code>]</td>\n    <td>Path to your custom document file</td>\n  </tr>\n  <tr>\n    <td><code>customWrap</code></td>\n    <td><code>string?</code> [<code>rootDir/wrap.jsx</code>]</td>\n    <td>Path to your custom wrap file</td>\n  </tr>\n  <tr>\n    <td><code>customApp</code></td>\n    <td><code>string?</code> [<code>rootDir/app.jsx</code>]</td>\n    <td>Path to your custom app file</td>\n  </tr>\n  <tr>\n    <td><code>csp</code></td>\n    <td><code>boolean | cspConfig</code> [<code>false</code>]</td>\n    <td>Config for <a href=\"https://developers.google.com/web/fundamentals/security/csp/\">Content Security Policy</a>, using <a href=\"https://helmetjs.github.io/docs/csp/\">Helmet-CSP</a>\n    <br/>\n    If set to true it will enable default config:\n    <br/>\n    <code>{\n      directives: {\n        defaultSrc: [`'self'`],\n        styleSrc: [`'self'`],\n      },\n    }</code>\n    </td>\n  </tr>\n</table>\n\n#### Customize Server\n\nCreate a `server.js` file on root directory where your `package.json` lives.\nHere you can customize Maleo's server.\n```js\nimport { Server } from '@airy/maleo/server';\n\nconst PORT = process.env.PORT || 3000;\n\nconst maleoServer = Server.init({\n  port: PORT,\n  runHandler: () => {\n    console.log('Server running on port :', PORT);\n  }\n});\n\nmaleoServer.run();\n```\n\nHere are the API's for the configuration:\n\n<table>\n  <tr>\n    <td>Key</td>\n    <td>Type</td>\n    <td>Description</td>\n  </tr>\n  <tr>\n    <td><code>port</code></td>\n    <td><code>Number?</code> [<code>3000</code>]</td>\n    <td>Port to run Maleo server</td>\n  </tr>\n  <tr>\n    <td><code>assetDir</code></td>\n    <td><code>String?</code> [<code>'<root>/.maleo/client'</code>]</td>\n    <td>Directory for all client related assets</td>\n  </tr>\n  <tr>\n    <td><code>runHandler</code></td>\n    <td><code>Function?</code></td>\n    <td>Function called when maleo server starter</td>\n  </tr>\n</table>\n\n#### Customize Webpack\n\nYou are able to extend Maleo.js' default webpack configuration by defining a function on `maleo.config.js`\n\n```js\n// maleo.config.js\n\nmodule.exports = {\n  webpack(config, context, next) {\n    // Perform customizations to webpack config\n    // Important: This call is required in order to let Maleo pass the config to webpack\n    return next(); \n  },\n};\n```\n\nWebpack function will receive three arguments:\n\n<table>\n  <tr>\n    <td>Argument</td>\n    <td>Details</td>\n  <tr>\n  <tr>\n    <td><code>config</code></td>\n    <td>This contains webpack configuration object that you can manipulate</td>\n  <tr>\n  <tr>\n    <td><code>context</code></td>\n    <td>This contains some keys that are useful for the build context\n      <br>\n      <table>\n        <tr>\n          <td><code>isDev</code></td>\n          <td><code>Boolean</code></td>\n          <td>Check if current build is for development</td>\n        </tr>\n        <tr>\n          <td><code>publicPath</code></td>\n          <td><code>String</code></td>\n          <td>Public Path of user defined or default's value</td>\n        </tr>\n        <tr>\n          <td><code>analyzeBundle</code></td>\n          <td><code>Boolean</code></td>\n          <td>Check if analyze bundle is enabled</td>\n        </tr>\n        <tr>\n          <td><code>buildDirectory</code></td>\n          <td><code>String</code></td>\n          <td>Build Directory of user defined or default's value</td>\n        </tr>\n        <tr>\n          <td><code>name</code></td>\n          <td><code>String</code></td>\n          <td>Build name <code>'server' || 'client'</code></td>\n        </tr>\n      </table>\n    </td>\n  <tr>\n  <tr>\n    <td><code>next</code></td>\n    <td>A callback required to be called to pass the custom configuration</td>\n  <tr>\n</table>\n\nExample of adding `ts-loader` through `maleo.config.js`:\n\n```js\n// maleo.config.js\n\n// Partly taken and modified from @airy/maleo-ts-plugin source\n// for simplicity purposes\n\nmodule.exports = {\n  webpack(config, context, next) {\n    const { isDev } = context\n\n    config.module.rules.push({\n      test: /\\.tsx?/,\n      exclude: /node_modules/,\n      use: [\n        require.resolve('@airy/maleo/lib/build/loaders/maleo-babel-loader'),\n        {\n          loader: 'ts-loader',\n          options: {\n            transpileOnly: true,\n          },\n        },\n      ],\n\n      if (isDev) {\n        config.plugins.push(new ForkTSCheckerWebpackPlugin());\n      }\n\n      return next();\n    })\n  },\n};\n```\n\n#### Customize Babel Config\n\nMaleo.js also let you have your own babel config. Just simply add `.babelrc` file at the root directory of your app.\n\nYou can include Maleo.js' babel preset in order to have latest JavaScript preset.\n\nHere's an example of `.babelrc` file:\n```json\n{\n  \"presets\": [\"@airy/maleo/babel\"],\n  \"plugins\": []\n}\n```\n\nThe `@airy/maleo/babel` preset includes everything you need to get your development started. The preset includes:\n- `@babel/preset-env`\n- `@babel/preset-react`\n- `@babel/plugin-proposal-class-properties`\n- `@babel/plugin-proposal-decorators`\n- `@babel/plugin-proposal-object-rest-spread`\n- `@babel/plugin-transform-runtime`\n- `react-loadable/babel`\n\n## Core Utilities Functions\n\n#### Route Matching\n\nTo use you can import the matching routes function like this:\n```js\nimport { getMatchedRoutes } from '@airy/maleo/utils';\n```\n\nThe function will return all matched routes and the route object from `routes.json`\n\n## CDN Support\n\nIf you are using a CDN, you can set up the `publicPath` setting and configure your CDN's origin to resolve to the domain that Maleo.js is hosted on.\n\n```js\n// maleo.config.js\n\nconst isProd = process.env.NODE_ENV === 'production';\n\nmodule.exports = {\n  assetPrefix: isProd && 'https://cdn.example.com';\n}\n```\n\n## FAQ\n\n== TO BE DETERMINED == \n\n## Plugins\n- [css-plugin](https://github.com/airyrooms/maleo-plugins/tree/master/packages/css-plugin)\n- [redux-plugin](https://github.com/airyrooms/maleo-plugins/tree/master/packages/redux-plugin)\n- [typescript-plugin](https://github.com/airyrooms/maleo-plugins/tree/master/packages/typescript-plugin)\n- [more](https://github.com/airyrooms/maleo-plugins)\n\n\n## Contributing\n[Please follow these steps to contribute to Maleo.js](https://github.com/airyrooms/maleo.js/blob/canary/CONTRIBUTING.md)\n\n[Please follow these steps to contribute to Maleo.js' plugins](https://github.com/airyrooms/maleo-plugins#contributing-guidelines)\n\n\n## Contributors\n\nThis project exists thanks to all the people who contribute. [Contribute](CONTRIBUTING.md).\n\nMany thanks to our **[contributors](https://github.com/airyrooms/maleo.js/graphs/contributors)**!\n\n\n## License\n\nMIT","readmeFilename":"README.md","bugs":{"url":"https://github.com/airyrooms/maleo.js/issues"},"homepage":"https://github.com/airyrooms/maleo.js#readme","_id":"@airy/maleo@0.3.1-canary.29","_nodeVersion":"11.15.0","_npmVersion":"lerna/3.13.1/node@v11.15.0+x64 (linux)","dist":{"integrity":"sha512-l+GUNPtgCjN8lh4NnWCMngBhPkseRaap79tNArDUpx1GIBq5VacrYb3xlgJgziAf5XgG6UoND+XD81iv6aEk1Q==","shasum":"6ea6601279ba134fb476f2513712fa043fac1708","tarball":"https://registry.npmjs.org/@airy/maleo/-/maleo-0.3.1-canary.29.tgz","fileCount":107,"unpackedSize":378680,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJdQVYQCRA9TVsSAnZWagAAYtAP/AtiAXlFxBbdIjm9hSVw\nCiF2UbCy8S4++/Eqb6MlOHXGwzR56V3Kct1TRvitv5tlHdkMePiElrDkHl78\nNTc66QKvya6h8TSRt94UtedivmMZc48xfyw7h0XcWe8Yq5EXW473ZXdxA7O9\n+K0CrwpLS13jgrXm9aafSOwqnNMO05sNH06QQk7aLvo93a5vAibzk5Kvvbub\ny0C5nV2zZF1blwzf3q+BrqCt2F+nbxTxwf3gYhyx5OLOTpWRAwKn2vnr/SKp\naXWWLX1SrYfnqfhowmoTnwyM7uTnQhTCC79TNmkf61UupvTPqpf7urMfJ1Js\ns3iE6X2Ahh3MR9/iQzu68a15YogRZnZYrVnwlzF0zZKAlR/LdHFsPsQc4RmE\nJJr/LUa9+UDoG+J/ks0BgErq6/MSLtnbGG+wUiaUQMyTTmpDy5rcdO98lHMD\nIWiQ3WevalvmdESimXYIlzK0XgHhaLv/SFl57Xgqz32CWpMChoZbKi+H2iun\nFeBwyQTRMVCwcFT+VSV0WZ8s41Jo9yLlrbE2fTCA+6A8RlN47xm3e9A45mpi\nMzgfJ4CGwI9gbdJGhF6Ku3OM2noJiIn4jAWpn4ijzXxSCo9LOieDLGvoXmd9\nfxjdvSOGipgf4pm5dtKk+eF8mnnN+8N6cWSXXCk1LdzpnDRayXRJ3iNElq0L\noh/n\r\n=Ath1\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDghAbjJpPIKVH0jiE2nqJ7jOwF2j7Ps6U4wq2Vw50btQIgSPyjj+GdI2g/TNiO/1wV1qnc0cT7e99giPatVSeWKGo="}]},"maintainers":[{"name":"airyrooms-engineering","email":"engineering@airyrooms.com"}],"_npmUser":{"name":"airyrooms-engineering","email":"engineering@airyrooms.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/maleo_0.3.1-canary.29_1564562960078_0.15750090755925572"},"_hasShrinkwrap":false},"0.3.1-canary.30":{"name":"@airy/maleo","version":"0.3.1-canary.30","description":"Un-opinionated Universal Rendering Javascript Framework","repository":{"type":"git","url":"git+https://github.com/airyrooms/maleo.js.git","bugs":"https://github.com/airyrooms/maleo.js/issues"},"author":{"name":"Airy"},"license":"MIT","private":false,"sideEffects":false,"scripts":{"build":"rm -rf dist && gulp","watch":"gulp watch","prepublish":"yarn build","start":"node server.js"},"bin":{"maleo":"lib/bin/maleo.js"},"dependencies":{"@babel/core":"7.1.2","@babel/plugin-proposal-class-properties":"7.1.0","@babel/plugin-proposal-decorators":"7.1.2","@babel/plugin-proposal-object-rest-spread":"7.0.0","@babel/plugin-syntax-dynamic-import":"7.0.0","@babel/plugin-transform-runtime":"7.1.0","@babel/preset-env":"7.1.0","@babel/preset-react":"7.0.0","@babel/runtime":"7.1.2","@babel/runtime-corejs2":"7.2.0","babel-loader":"8.0.4","babel-plugin-transform-react-remove-prop-types":"^0.4.24","case-sensitive-paths-webpack-plugin":"2.1.2","compression":"1.7.4","es6-promise":"4.2.5","eventsource":"1.0.7","express":"4.16.4","figlet":"1.2.1","flatted":"^2.0.1","friendly-errors-webpack-plugin":"1.7.0","hard-source-webpack-plugin":"0.13.1","helmet":"3.14.0","hoist-non-react-statics":"^3.3.0","isomorphic-fetch":"2.2.1","loader-utils":"1.1.0","node-notifier":"5.2.1","nodemon":"1.18.4","react-dom":"16.8.4","react-helmet":"5.2.0","react-loadable":"5.5.0","react-router-config":"5.0.0","react-router-dom":"5.0.0","rimraf":"2.6.3","source-map-support":"0.5.9","terser-webpack-plugin":"1.1.0","webpack":"4.29.0","webpack-bundle-analyzer":"3.0.3","webpack-cli":"3.1.2","webpack-dev-middleware":"3.4.0","webpack-hot-middleware":"2.24.3","webpackbar":"2.6.3"},"peerDependencies":{"react":"^16.8.4"},"devDependencies":{"@babel/preset-typescript":"^7.1.0","@types/webpack-bundle-analyzer":"^2.13.0","babel-plugin-module-resolver":"^3.1.1","gulp":"github:gulpjs/gulp#4.0","gulp-babel":"^8.0.0","gulp-clean":"^0.4.0","gulp-sourcemaps":"^2.6.4","gulp-uglify":"^3.0.1","gulp-watch":"^5.0.1","react":"^16.8.4"},"gitHead":"b018f9b273b31d03cc6bc664fe1b7feb16f94a1a","readme":"[![Build Status](https://travis-ci.org/airyrooms/maleo.js.svg?branch=master)](https://travis-ci.org/airyrooms/maleo.js)\n[![Coverage Status](https://coveralls.io/repos/github/airyrooms/maleo.js/badge.svg?branch=master)](https://coveralls.io/github/airyrooms/maleo.js?branch=master)\n[![npm version](https://badge.fury.io/js/%40airy%2Fmaleo.svg)](https://badge.fury.io/js/%40airy%2Fmaleo)\n[![Issues](http://img.shields.io/github/issues/airyrooms/maleo.js.svg)]( https://github.com/airyrooms/maleo.js/issues)\n[![MIT license](https://img.shields.io/badge/License-MIT-blue.svg)](https://github.com/airyrooms/maleo.js/blob/canary/LICENSE)\n[![Discord Chat](https://img.shields.io/discord/550498214789513218.svg)](https://discord.gg/9eArCQn)\n[![Conventional Commits](https://img.shields.io/badge/Conventional%20Commits-1.0.0-green.svg)](https://conventionalcommits.org)\n\n\n# Welcome to Maleo.js\n\nMaleo.js is an un-opinionated framework to enable Universal Rendering in JavaScript using React with no hassle.\n\nWe are here to solve the time consuming setups Universal Rendering Required.\n\n---\n\nReadme below is the documentation for the `canary` (prerelease) branch. To view the documentation for the latest stable Maleo.js version change branch to `master`\n\n---\n\n# Table of Contents\n- [Features](#features)\n- [Setup](#setup)\n- [Component Lifecycle](#component-lifecycle)\n- [Routing](#routing)\n- [Dynamic Import Component](#dynamic-import-component)\n  - [Tips](#tips)\n  - [Preloading](#preloading)\n- [Customizable Component](#customizable-component)\n  - [Custom Document](#custom-document)\n  - [Custom Wrap](#custom-wrap)\n- [Custom Configuration](#custom-configuration)\n  - [Customize Server](#customize-server)\n  - [Customize Webpack](#customize-webpack)\n  - [Customize Babel Config](#customize-babel-config)\n- [Core Utilities Functions](#core-utilities-functions)\n- [CDN Support](#cdn-support)\n- [Plugins](#plugins)\n- [FAQ](#faq)\n- [Contributing](#contributing)\n\n---\n\n## Features\n- Universal Rendering\n- Plugin based framework\n- Customizable\n\n## Setup\n\nInstall Maleo.js \n\n>*for now change `@airy/maleo` to `@airy/maleo@canary` until we publish the stable version of this package*\n\n\n**NPM**\n```bash\n$ npm install --save @airy/maleo react\n```\n**Yarn**\n```bash\n$ yarn add @airy/maleo react\n```\n\nAdd this script to your `package.json`\n```json\n{\n  \"scripts\": {\n    \"dev\": \"maleo dev\",\n    \"build\": \"export NODE_ENV=production && maleo build\",\n    \"start\": \"export NODE_ENV=production && maleo run\"\n  }\n}\n```\n\nCreate a page Root component\n```jsx\n// ./src/Root.jsx\nimport React from 'react';\n\n// Export default is required for registering page\nexport default class RootComponent extends React.Component {\n  render() {\n    return (\n      <h1>Hello World!</h1>\n    )\n  }\n}\n```\n\nAnd lastly, create a routing file on your project root directory called `routes.json` and register your page component\n```json\n[\n  {\n    \"path\": \"/\",\n    \"page\": \"./src/Root\"\n  }\n]\n```\n\nAfter that you can now run `$ npm run dev` and go to `http://localhost:3000`.\n\nYou should now see your app running on your browser.\n\nBy now you should see \n- Automatic transpilation and bundling (with webpack and babel)\n- ~~Hot code reloading~~ [#17](https://github.com/airyrooms/maleo.js/issues/17)\n- Server rendering\n\nTo see how simple this is, check out the sample app!\n\n## Component Lifecycle \n\nMaleo.js added a new component lifecycle hook called `getInitialProps`, this function is called during Server Side Rendering (SSR) and during route changes on Client Side Rendering (CSR).\n\nThis is useful especially for SEO purposes. \n\nExample for stateful component:\n```jsx\nimport React from 'react';\n\nexport default class extends React.Component {\n  static getInitialProps = async (ctx) => {\n    const { req } = ctx;\n\n    // check if getInitialProps is called on server or on client\n    const userAgent = req ? req.headers['user-agent'] : navigator.userAgent;\n\n    // the return value will be passed as props for this component\n    return { userAgent };\n  }\n\n  render() {\n    return (\n      <div>\n        Hello World {this.props.userAgent}\n      </div>\n    );\n  }\n}\n```\n\nExample for stateless component:\n```jsx\nconst Component = (props) => (\n  <div>\n    Hello World {props.userAgent}\n  </div>\n);\n\nComponent.getInitialprops = async (ctx) => {\n  const { req } = ctx;\n\n  const userAgent = req ? req.headers['user-agent'] : navigator.userAgent;\n\n  return { userAgent };\n};\n\nexport default Component;\n```\n\n`getInitialProps` receives a context object with the following properties:\n- `req` - HTTP request object (server only)\n- `res` - HTTP response object (server only)\n- `...wrapProps` - Spreaded properties from custom Wrap\n- `...appProps` - Spreaded properties from custom App\n- `matched` - Matched routes contains list of object, which is consists of `match` object and `route` object\n- `route` - Component's route attributes on `routes.json` file\n\n## Routing\n\nRouting is declared in a centralized route config.\nRegister all the route config in `routes.json` file.\n\nIf you put the `routes.json` files on root directory, Maleo will automatically register your route. Otherwise put path to your routes on [Maleo config](#custom-configuration).\n\nRoutes file has to export default the route configuration.\nThe route object **expected to have distinct key** to indicate the route.\n\n<table>\n  <tr>\n    <td>Key</td>\n    <td>Type</td>\n    <td>Description</td>\n  </tr>\n  <tr>\n    <td><code>path</code></td>\n    <td><code>String!</code></td>\n    <td>Routes path</td>\n  </tr>\n  <tr>\n    <td><code>page</code></td>\n    <td><code>String!</code></td>\n    <td>Path to React Component for this route path</td>\n  </tr>\n  <tr>\n    <td><code>exact</code></td>\n    <td><code>Boolean?</code> [<code>false</code>]</td>\n    <td>To make url has to match exactly the path in order to render the component. Give <code>false</code> value if the route is a wrapper route component</td>\n  </tr>\n  <tr>\n    <td><code>routes</code></td>\n    <td><code>RouteObject?</code></td>\n    <td>Nested route</td>\n  </tr>\n</table>\n\n\nFor example:\n```json\n[\n  {\n    \"page\": \"./src/MainApp\",\n    \"routes\": [\n      {\n        \"path\": \"/\",\n        \"page\": \"./src/Search\",\n        \"exact\": true\n      },\n      {\n        \"path\": \"/search\",\n        \"page\": \"./src/Search\",\n        \"routes\": [\n          {\n            \"path\": \"/search/hello\",\n            \"page\": \"./src/Detail\",\n            \"exact\": true\n          }\n        ]\n      },\n      {\n        \"path\": \"/detail\",\n        \"page\": \"./src/Detail\",\n        \"exact\": true\n      }\n    ]\n  }\n]\n```\n\n## Dynamic Import Component\nMaleo.js supports TC39 [dynamic import proposal](https://github.com/tc39/proposal-dynamic-import) for JavaScript.\n\nYou can think dynamic import as another way to split your code into manageable chunks. You can use our `Dynamic` function which utilizes [react loadable](https://github.com/jamiebuilds/react-loadable)\n\nFor Example\n```js\n// DynamicComponent.js\nimport Dynamic from '@airy/maleo/dynamic';\n\nexport default Dynamic({\n  loader: () => import( /* webpackChunkName:\"DynamicComponent\" */ './component/DynamicLoad'),\n})\n```\n\n### **Preloading**\n\nFor optimization purposes, you can also preload a component even before the component got rendered.\n\nFor example, if you want to load component when a button get pressed, you can start preloading the component when the user hovers the mouse over the button.\n\nThe component created by `Dynamic` exposes a [static `preload` method](https://github.com/jamiebuilds/react-loadable#loadablecomponentpreload).\n\n```jsx\nimport React from 'react';\nimport Dynamic from '@airy/maleo/dynamic';\n\nconst DynamicBar = Dynamic({\n  loader: () => import('./Bar'),\n  loading: LoadingCompoinent\n});\n\nclass MyComponent extends React.Component {\n  state = { showBar: false };\n\n  onClick = () => {\n    this.setState({ showBar: true });\n  };\n\n  onMouseOver = () => DynamicBar.preload();\n\n  render() {\n    return (\n      <div>\n        <button\n          onClick={this.onClick}\n          onMouseOver={this.onMouseOver}>\n          Show Bar\n        </button>\n        { this.state.showBar && <DynamicBar /> }\n      </div>\n    )\n  }\n}\n```\n\n## Customizable Component\n\n### Custom Document\n\nHighly inspired by what [Next.js](https://github.com/zeit/next.js) has done on their awesome template customization.\n\nMaleo.js also enable customization on `Document` as document's markup. So you don't need to include tags like `<html>`, `<head>`, `<body>`, etc. \n\nTo override the default behavior, you'll need to create a component that extends the `Document` React class provided by Maleo.\n\n```jsx\n// document.jsx\nimport React from 'react';\nimport { Document, Header, Main, Scripts } from '@airy/maleo/document';\n\nexport default class extends Document {\n  render() {\n    return (\n      <html>\n        <Header>\n          <title>Maleo JS</title>\n          <meta charset=\"utf-8\" />\n          <meta name=\"description\" content=\"Maleo.js is awesome!\" />\n\n          <style>\n            {` body { background-color: #fff } `}\n          </style>\n        </Header>\n\n        <body>\n          <Main />\n\n          <Scripts />\n\n          <ReduxScript />\n        </body>\n      </html>\n    );\n  }\n}\n```\n\n### Custom Wrap\n\nMaleo.js uses the `Wrap` component to initialize pages. `Wrap` contains React Router's Component. You can add HoC here to wrap the application and control the page initialization. Which allows you to do amazing things like:\n- Persisting layour between page changes\n- Keeping state when navigating pages\n- Custom error handling using `componentDidCatch`\n- Inject additional data into pages (like Redux provider, etc)\n\nTo override the default behavior, you'll need to create a component that extends the `Wrap` React class provided by Maleo.\n\n```jsx\n// wrap.jsx\nimport React from 'react';\nimport { Wrap } from '@airy/maleo/wrap';\n\n// Redux plugin for Maleo.js\n// Hoc that creates store and add Redux Provider\nimport { withRedux } from '@airy/maleo-redux-plugin';\n\n// Custom Wrapper that will be rendered for the whole Application\nimport Content from './component/Content';\nimport NavigationBar from './component/NavigationBar';\n\nimport { createStore } from './store';\n\n@withRedux(createStore)\nexport default class extends Wrap {\n  static getInitialProps = (ctx) => {\n    const { store } = ctx\n    // you receive store from context\n    // you can access or do something with the store here\n    console.log('Initialized Redux Store', store);\n    return {}\n  }\n\n  render() {\n    const { Container, containerProps, App, appProps } = this.props\n\n    return (\n      <Container {...containerProps}>\n        <NavigationBar />\n        <Content>\n          <App {...appProps}/>\n        </Content>\n      </Container>\n    )\n  }\n}\n\n```\nIf you put `document.jsx` and `wrap.jsx` on root directory (the same level with `package.json`), then Maleo will automatically register your custom Document and Wrap. Otherwise, you can add the path to your custom Document and Wrap on [Maleo config](#custom-configuration)\n\n---\n***We are also working on adding default and customizable `Error` component page***\n\n---\n\n## Custom Configuration\n\nFor more advanced configuration of Maleo.js, like `webpack` config, registering `plugins`, path to your routes, custom Document and Wrap, and adding `path alias`, you can create a `maleo.config.js` in the root of your project directory. (same directory with `package.json`)\n\n```js\n// maleo.config.js\n\nmodule.exports = {\n  /* config options here */\n}\n```\n\nHere are the API's for the configuration:\n\n<table>\n  <tr>\n    <td>Key</td>\n    <td>Type</td>\n    <td>Description</td>\n  </tr>\n  <tr>\n    <td><code>favicon</code></td>\n    <td><code>String</code> [<code>{project-dir}/favicon.ico</code>]</td>\n    <td>Path to favicon file</td>\n  </tr>\n  <tr>\n    <td><code>buildDir</code></td>\n    <td><code>String?</code> [<code>.maleo</code>]</td>\n    <td>Directory to put Maleo.js' build assets</td>\n  </tr>\n  <tr>\n    <td><code>cache</code></td>\n    <td><code>Boolean?</code> [<code>true</code>]</td>\n    <td>Enable webpack build caching</td>\n  </tr>\n  <tr>\n    <td><code>isDev</code></td>\n    <td><code>Boolean?</code> [<code>process.env.NODE_ENV === 'development'</code>]</td>\n    <td>Enable development build configuration</td>\n  </tr>\n  <tr>\n    <td><code>sourceMaps</code></td>\n    <td><code>Boolean?</code> [<code>true</code>]</td>\n    <td>Enable webpack to generate source maps</td>\n  </tr>\n  <tr>\n    <td><code>alias</code></td>\n    <td><code>Object?</code></td>\n    <td>A key value pair for aliasing path directory\n      <br>\n      <code>\n      { 'component': './src/component' }\n      </code>\n    </td>\n  </tr>\n  <tr>\n    <td><code>publicPath</code></td>\n    <td><code>String?</code> [<code>/_assets/</code>]</td>\n    <td>To customize webpack's <code>publicPath</code> \n    <br>Comes in handy if using CDN to put built assets</td>\n  </tr>\n  <tr>\n    <td><code>analyzeBundle</code></td>\n    <td><code>Boolean?</code> [<code>false</code>]</td>\n    <td>To enable webpack's bundle analyzer, for analyzing bundle sizes during bundle debugging should Maleo.js' build process got slow</td>\n  </tr>\n  <tr>\n    <td><code>webpack</code></td>\n    <td><code>Function?</code></td>\n    <td>To customize webpack configuration, more details <a href=\"#customize-webpack\">here</a></td>\n  </tr>\n  <tr>\n    <td><code>routes</code></td>\n    <td><code>string?</code> [<code>rootDir/routes.jsx</code>]</td>\n    <td>Path to your routes file</td>\n  </tr>\n  <tr>\n    <td><code>customDocument</code></td>\n    <td><code>string?</code> [<code>rootDir/document.jsx</code>]</td>\n    <td>Path to your custom document file</td>\n  </tr>\n  <tr>\n    <td><code>customWrap</code></td>\n    <td><code>string?</code> [<code>rootDir/wrap.jsx</code>]</td>\n    <td>Path to your custom wrap file</td>\n  </tr>\n  <tr>\n    <td><code>customApp</code></td>\n    <td><code>string?</code> [<code>rootDir/app.jsx</code>]</td>\n    <td>Path to your custom app file</td>\n  </tr>\n  <tr>\n    <td><code>csp</code></td>\n    <td><code>boolean | cspConfig</code> [<code>false</code>]</td>\n    <td>Config for <a href=\"https://developers.google.com/web/fundamentals/security/csp/\">Content Security Policy</a>, using <a href=\"https://helmetjs.github.io/docs/csp/\">Helmet-CSP</a>\n    <br/>\n    If set to true it will enable default config:\n    <br/>\n    <code>{\n      directives: {\n        defaultSrc: [`'self'`],\n        styleSrc: [`'self'`],\n      },\n    }</code>\n    </td>\n  </tr>\n</table>\n\n#### Customize Server\n\nCreate a `server.js` file on root directory where your `package.json` lives.\nHere you can customize Maleo's server.\n```js\nimport { Server } from '@airy/maleo/server';\n\nconst PORT = process.env.PORT || 3000;\n\nconst maleoServer = Server.init({\n  port: PORT,\n  runHandler: () => {\n    console.log('Server running on port :', PORT);\n  }\n});\n\nmaleoServer.run();\n```\n\nHere are the API's for the configuration:\n\n<table>\n  <tr>\n    <td>Key</td>\n    <td>Type</td>\n    <td>Description</td>\n  </tr>\n  <tr>\n    <td><code>port</code></td>\n    <td><code>Number?</code> [<code>3000</code>]</td>\n    <td>Port to run Maleo server</td>\n  </tr>\n  <tr>\n    <td><code>assetDir</code></td>\n    <td><code>String?</code> [<code>'<root>/.maleo/client'</code>]</td>\n    <td>Directory for all client related assets</td>\n  </tr>\n  <tr>\n    <td><code>runHandler</code></td>\n    <td><code>Function?</code></td>\n    <td>Function called when maleo server starter</td>\n  </tr>\n</table>\n\n#### Customize Webpack\n\nYou are able to extend Maleo.js' default webpack configuration by defining a function on `maleo.config.js`\n\n```js\n// maleo.config.js\n\nmodule.exports = {\n  webpack(config, context, next) {\n    // Perform customizations to webpack config\n    // Important: This call is required in order to let Maleo pass the config to webpack\n    return next(); \n  },\n};\n```\n\nWebpack function will receive three arguments:\n\n<table>\n  <tr>\n    <td>Argument</td>\n    <td>Details</td>\n  <tr>\n  <tr>\n    <td><code>config</code></td>\n    <td>This contains webpack configuration object that you can manipulate</td>\n  <tr>\n  <tr>\n    <td><code>context</code></td>\n    <td>This contains some keys that are useful for the build context\n      <br>\n      <table>\n        <tr>\n          <td><code>isDev</code></td>\n          <td><code>Boolean</code></td>\n          <td>Check if current build is for development</td>\n        </tr>\n        <tr>\n          <td><code>publicPath</code></td>\n          <td><code>String</code></td>\n          <td>Public Path of user defined or default's value</td>\n        </tr>\n        <tr>\n          <td><code>analyzeBundle</code></td>\n          <td><code>Boolean</code></td>\n          <td>Check if analyze bundle is enabled</td>\n        </tr>\n        <tr>\n          <td><code>buildDirectory</code></td>\n          <td><code>String</code></td>\n          <td>Build Directory of user defined or default's value</td>\n        </tr>\n        <tr>\n          <td><code>name</code></td>\n          <td><code>String</code></td>\n          <td>Build name <code>'server' || 'client'</code></td>\n        </tr>\n      </table>\n    </td>\n  <tr>\n  <tr>\n    <td><code>next</code></td>\n    <td>A callback required to be called to pass the custom configuration</td>\n  <tr>\n</table>\n\nExample of adding `ts-loader` through `maleo.config.js`:\n\n```js\n// maleo.config.js\n\n// Partly taken and modified from @airy/maleo-ts-plugin source\n// for simplicity purposes\n\nmodule.exports = {\n  webpack(config, context, next) {\n    const { isDev } = context\n\n    config.module.rules.push({\n      test: /\\.tsx?/,\n      exclude: /node_modules/,\n      use: [\n        require.resolve('@airy/maleo/lib/build/loaders/maleo-babel-loader'),\n        {\n          loader: 'ts-loader',\n          options: {\n            transpileOnly: true,\n          },\n        },\n      ],\n\n      if (isDev) {\n        config.plugins.push(new ForkTSCheckerWebpackPlugin());\n      }\n\n      return next();\n    })\n  },\n};\n```\n\n#### Customize Babel Config\n\nMaleo.js also let you have your own babel config. Just simply add `.babelrc` file at the root directory of your app.\n\nYou can include Maleo.js' babel preset in order to have latest JavaScript preset.\n\nHere's an example of `.babelrc` file:\n```json\n{\n  \"presets\": [\"@airy/maleo/babel\"],\n  \"plugins\": []\n}\n```\n\nThe `@airy/maleo/babel` preset includes everything you need to get your development started. The preset includes:\n- `@babel/preset-env`\n- `@babel/preset-react`\n- `@babel/plugin-proposal-class-properties`\n- `@babel/plugin-proposal-decorators`\n- `@babel/plugin-proposal-object-rest-spread`\n- `@babel/plugin-transform-runtime`\n- `react-loadable/babel`\n\n## Core Utilities Functions\n\n#### Route Matching\n\nTo use you can import the matching routes function like this:\n```js\nimport { getMatchedRoutes } from '@airy/maleo/utils';\n```\n\nThe function will return all matched routes and the route object from `routes.json`\n\n## CDN Support\n\nIf you are using a CDN, you can set up the `publicPath` setting and configure your CDN's origin to resolve to the domain that Maleo.js is hosted on.\n\n```js\n// maleo.config.js\n\nconst isProd = process.env.NODE_ENV === 'production';\n\nmodule.exports = {\n  assetPrefix: isProd && 'https://cdn.example.com';\n}\n```\n\n## FAQ\n\n== TO BE DETERMINED == \n\n## Plugins\n- [css-plugin](https://github.com/airyrooms/maleo-plugins/tree/master/packages/css-plugin)\n- [redux-plugin](https://github.com/airyrooms/maleo-plugins/tree/master/packages/redux-plugin)\n- [typescript-plugin](https://github.com/airyrooms/maleo-plugins/tree/master/packages/typescript-plugin)\n- [more](https://github.com/airyrooms/maleo-plugins)\n\n\n## Contributing\n[Please follow these steps to contribute to Maleo.js](https://github.com/airyrooms/maleo.js/blob/canary/CONTRIBUTING.md)\n\n[Please follow these steps to contribute to Maleo.js' plugins](https://github.com/airyrooms/maleo-plugins#contributing-guidelines)\n\n\n## Contributors\n\nThis project exists thanks to all the people who contribute. [Contribute](CONTRIBUTING.md).\n\nMany thanks to our **[contributors](https://github.com/airyrooms/maleo.js/graphs/contributors)**!\n\n\n## License\n\nMIT","readmeFilename":"README.md","bugs":{"url":"https://github.com/airyrooms/maleo.js/issues"},"homepage":"https://github.com/airyrooms/maleo.js#readme","_id":"@airy/maleo@0.3.1-canary.30","_nodeVersion":"11.15.0","_npmVersion":"lerna/3.13.1/node@v11.15.0+x64 (linux)","dist":{"integrity":"sha512-/i+YyyxohUxL0GM6OSsuXGRel3DnD9c3pOaniE8lNOIurf4OBpU9/3i6E3JwfyELqlXo9atT+qwJW58u5IxprA==","shasum":"ec6c4c03f7758737c0eab68754b5d3c0d334139e","tarball":"https://registry.npmjs.org/@airy/maleo/-/maleo-0.3.1-canary.30.tgz","fileCount":107,"unpackedSize":378899,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJdSQKsCRA9TVsSAnZWagAAfOsP/3UtJCnFZgpa1PRwhzfZ\nBzC2MzF3mHc3jq3N/qyDAmqeh1ff6G3WrOCfQvI6slCxfTAP6Gu1+U1yfvuE\nLs/uzW1A+XH/OZwq0xNrXxqCUb0nhGhJCsMrv3ZLJZubtw9umcXVuuKbbNxE\nhMSY9y8V3JQKKMaM5bKP3YTZ3UzfDSxSvLgy0k1iiO1S+NeT5Sp/dQh3UovY\nEpvqKroEvkL+a1yUJovxQO9C5mGhjyJxNHIjfFZ7HLv2aqpuZL+dEgJfMPML\n/a9rRjbr35D3v2rhCRRxDePA34HLZt0FmqFaRO6LwhBy2OIaE20xU+TmN4eo\n8wKypWg2Ld87jU2xacy5DwIbNbh9NUvIcpveWvZ8W5kyzKMi4JXwjISXLyNm\n5m20ppFw+atOrayvAhWcNwOhwW775gv/48Mz8wMEZK7Itp7gA2I3ZxEZ8UJ5\nwx4oIuJgk/aG1DfmwxoC4ZRed/xXz1e/IVqRlTrMJqO4/Sk+4hCFQvNezNjH\nF+OJyQ9vKrb0CMYu7ZDOr15J9ebI9e/oBPDNl71bSk4D0ed3/8l5OU7z6PSj\ndFlbZUJkj90ETSYYWCcyEjRkt1EcODLHkMsTdIX9SWnhYLBQLhhefZoKejVY\ndsVT++fsIg/ChKECDcBXIjaqTw3JBh3+L+C8xfe1JMZdFjEPi/+huekLXTin\nJxBA\r\n=FcGK\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCuKybpg69eokDG0el6M/WvoZxmQr/C1xt/wyu0MQaMEAIhAIKsmFXKcM0h2V8AldzbJ+e693lxp7ELz2H976FJIht9"}]},"maintainers":[{"name":"airyrooms-engineering","email":"engineering@airyrooms.com"}],"_npmUser":{"name":"airyrooms-engineering","email":"engineering@airyrooms.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/maleo_0.3.1-canary.30_1565065900162_0.6305034516997181"},"_hasShrinkwrap":false},"0.3.1-canary.31":{"name":"@airy/maleo","version":"0.3.1-canary.31","description":"Un-opinionated Universal Rendering Javascript Framework","repository":{"type":"git","url":"git+https://github.com/airyrooms/maleo.js.git","bugs":"https://github.com/airyrooms/maleo.js/issues"},"author":{"name":"Airy"},"license":"MIT","private":false,"sideEffects":false,"scripts":{"build":"rm -rf dist && gulp","watch":"gulp watch","prepublish":"yarn build","start":"node server.js"},"bin":{"maleo":"lib/bin/maleo.js"},"dependencies":{"@babel/core":"7.1.2","@babel/plugin-proposal-class-properties":"7.1.0","@babel/plugin-proposal-decorators":"7.1.2","@babel/plugin-proposal-object-rest-spread":"7.0.0","@babel/plugin-syntax-dynamic-import":"7.0.0","@babel/plugin-transform-runtime":"7.1.0","@babel/preset-env":"7.1.0","@babel/preset-react":"7.0.0","@babel/runtime":"7.1.2","@babel/runtime-corejs2":"7.2.0","babel-loader":"8.0.4","babel-plugin-transform-react-remove-prop-types":"^0.4.24","case-sensitive-paths-webpack-plugin":"2.1.2","compression":"1.7.4","es6-promise":"4.2.5","eventsource":"1.0.7","express":"4.16.4","figlet":"1.2.1","flatted":"^2.0.1","friendly-errors-webpack-plugin":"1.7.0","hard-source-webpack-plugin":"0.13.1","helmet":"3.14.0","hoist-non-react-statics":"^3.3.0","isomorphic-fetch":"2.2.1","loader-utils":"1.1.0","node-notifier":"5.2.1","nodemon":"1.18.4","react-dom":"16.8.4","react-helmet":"5.2.0","react-loadable":"5.5.0","react-router-config":"5.0.0","react-router-dom":"5.0.0","rimraf":"2.6.3","source-map-support":"0.5.9","terser-webpack-plugin":"1.1.0","webpack":"4.29.0","webpack-bundle-analyzer":"3.0.3","webpack-cli":"3.1.2","webpack-dev-middleware":"3.4.0","webpack-hot-middleware":"2.24.3","webpackbar":"2.6.3"},"peerDependencies":{"react":"^16.8.4"},"devDependencies":{"@babel/preset-typescript":"^7.1.0","@types/webpack-bundle-analyzer":"^2.13.0","babel-plugin-module-resolver":"^3.1.1","gulp":"github:gulpjs/gulp#4.0","gulp-babel":"^8.0.0","gulp-clean":"^0.4.0","gulp-sourcemaps":"^2.6.4","gulp-uglify":"^3.0.1","gulp-watch":"^5.0.1","react":"^16.8.4"},"gitHead":"f6c28eb2f1b9976c3016850d2878f0b5475b24ac","readme":"[![Build Status](https://travis-ci.org/airyrooms/maleo.js.svg?branch=master)](https://travis-ci.org/airyrooms/maleo.js)\n[![Coverage Status](https://coveralls.io/repos/github/airyrooms/maleo.js/badge.svg?branch=master)](https://coveralls.io/github/airyrooms/maleo.js?branch=master)\n[![npm version](https://badge.fury.io/js/%40airy%2Fmaleo.svg)](https://badge.fury.io/js/%40airy%2Fmaleo)\n[![Issues](http://img.shields.io/github/issues/airyrooms/maleo.js.svg)]( https://github.com/airyrooms/maleo.js/issues)\n[![MIT license](https://img.shields.io/badge/License-MIT-blue.svg)](https://github.com/airyrooms/maleo.js/blob/canary/LICENSE)\n[![Discord Chat](https://img.shields.io/discord/550498214789513218.svg)](https://discord.gg/9eArCQn)\n[![Conventional Commits](https://img.shields.io/badge/Conventional%20Commits-1.0.0-green.svg)](https://conventionalcommits.org)\n\n\n# Welcome to Maleo.js\n\nMaleo.js is an un-opinionated framework to enable Universal Rendering in JavaScript using React with no hassle.\n\nWe are here to solve the time consuming setups Universal Rendering Required.\n\n---\n\nReadme below is the documentation for the `canary` (prerelease) branch. To view the documentation for the latest stable Maleo.js version change branch to `master`\n\n---\n\n# Table of Contents\n- [Features](#features)\n- [Setup](#setup)\n- [Component Lifecycle](#component-lifecycle)\n- [Routing](#routing)\n- [Dynamic Import Component](#dynamic-import-component)\n  - [Tips](#tips)\n  - [Preloading](#preloading)\n- [Customizable Component](#customizable-component)\n  - [Custom Document](#custom-document)\n  - [Custom Wrap](#custom-wrap)\n- [Custom Configuration](#custom-configuration)\n  - [Customize Server](#customize-server)\n  - [Customize Webpack](#customize-webpack)\n  - [Customize Babel Config](#customize-babel-config)\n- [Core Utilities Functions](#core-utilities-functions)\n- [CDN Support](#cdn-support)\n- [Plugins](#plugins)\n- [FAQ](#faq)\n- [Contributing](#contributing)\n\n---\n\n## Features\n- Universal Rendering\n- Plugin based framework\n- Customizable\n\n## Setup\n\nInstall Maleo.js \n\n>*for now change `@airy/maleo` to `@airy/maleo@canary` until we publish the stable version of this package*\n\n\n**NPM**\n```bash\n$ npm install --save @airy/maleo react\n```\n**Yarn**\n```bash\n$ yarn add @airy/maleo react\n```\n\nAdd this script to your `package.json`\n```json\n{\n  \"scripts\": {\n    \"dev\": \"maleo dev\",\n    \"build\": \"export NODE_ENV=production && maleo build\",\n    \"start\": \"export NODE_ENV=production && maleo run\"\n  }\n}\n```\n\nCreate a page Root component\n```jsx\n// ./src/Root.jsx\nimport React from 'react';\n\n// Export default is required for registering page\nexport default class RootComponent extends React.Component {\n  render() {\n    return (\n      <h1>Hello World!</h1>\n    )\n  }\n}\n```\n\nAnd lastly, create a routing file on your project root directory called `routes.json` and register your page component\n```json\n[\n  {\n    \"path\": \"/\",\n    \"page\": \"./src/Root\"\n  }\n]\n```\n\nAfter that you can now run `$ npm run dev` and go to `http://localhost:3000`.\n\nYou should now see your app running on your browser.\n\nBy now you should see \n- Automatic transpilation and bundling (with webpack and babel)\n- ~~Hot code reloading~~ [#17](https://github.com/airyrooms/maleo.js/issues/17)\n- Server rendering\n\nTo see how simple this is, check out the sample app!\n\n## Component Lifecycle \n\nMaleo.js added a new component lifecycle hook called `getInitialProps`, this function is called during Server Side Rendering (SSR) and during route changes on Client Side Rendering (CSR).\n\nThis is useful especially for SEO purposes. \n\nExample for stateful component:\n```jsx\nimport React from 'react';\n\nexport default class extends React.Component {\n  static getInitialProps = async (ctx) => {\n    const { req } = ctx;\n\n    // check if getInitialProps is called on server or on client\n    const userAgent = req ? req.headers['user-agent'] : navigator.userAgent;\n\n    // the return value will be passed as props for this component\n    return { userAgent };\n  }\n\n  render() {\n    return (\n      <div>\n        Hello World {this.props.userAgent}\n      </div>\n    );\n  }\n}\n```\n\nExample for stateless component:\n```jsx\nconst Component = (props) => (\n  <div>\n    Hello World {props.userAgent}\n  </div>\n);\n\nComponent.getInitialprops = async (ctx) => {\n  const { req } = ctx;\n\n  const userAgent = req ? req.headers['user-agent'] : navigator.userAgent;\n\n  return { userAgent };\n};\n\nexport default Component;\n```\n\n`getInitialProps` receives a context object with the following properties:\n- `req` - HTTP request object (server only)\n- `res` - HTTP response object (server only)\n- `...wrapProps` - Spreaded properties from custom Wrap\n- `...appProps` - Spreaded properties from custom App\n- `matched` - Matched routes contains list of object, which is consists of `match` object and `route` object\n- `route` - Component's route attributes on `routes.json` file\n\n## Routing\n\nRouting is declared in a centralized route config.\nRegister all the route config in `routes.json` file.\n\nIf you put the `routes.json` files on root directory, Maleo will automatically register your route. Otherwise put path to your routes on [Maleo config](#custom-configuration).\n\nRoutes file has to export default the route configuration.\nThe route object **expected to have distinct key** to indicate the route.\n\n<table>\n  <tr>\n    <td>Key</td>\n    <td>Type</td>\n    <td>Description</td>\n  </tr>\n  <tr>\n    <td><code>path</code></td>\n    <td><code>String!</code></td>\n    <td>Routes path</td>\n  </tr>\n  <tr>\n    <td><code>page</code></td>\n    <td><code>String!</code></td>\n    <td>Path to React Component for this route path</td>\n  </tr>\n  <tr>\n    <td><code>exact</code></td>\n    <td><code>Boolean?</code> [<code>false</code>]</td>\n    <td>To make url has to match exactly the path in order to render the component. Give <code>false</code> value if the route is a wrapper route component</td>\n  </tr>\n  <tr>\n    <td><code>routes</code></td>\n    <td><code>RouteObject?</code></td>\n    <td>Nested route</td>\n  </tr>\n</table>\n\n\nFor example:\n```json\n[\n  {\n    \"page\": \"./src/MainApp\",\n    \"routes\": [\n      {\n        \"path\": \"/\",\n        \"page\": \"./src/Search\",\n        \"exact\": true\n      },\n      {\n        \"path\": \"/search\",\n        \"page\": \"./src/Search\",\n        \"routes\": [\n          {\n            \"path\": \"/search/hello\",\n            \"page\": \"./src/Detail\",\n            \"exact\": true\n          }\n        ]\n      },\n      {\n        \"path\": \"/detail\",\n        \"page\": \"./src/Detail\",\n        \"exact\": true\n      }\n    ]\n  }\n]\n```\n\n## Dynamic Import Component\nMaleo.js supports TC39 [dynamic import proposal](https://github.com/tc39/proposal-dynamic-import) for JavaScript.\n\nYou can think dynamic import as another way to split your code into manageable chunks. You can use our `Dynamic` function which utilizes [react loadable](https://github.com/jamiebuilds/react-loadable)\n\nFor Example\n```js\n// DynamicComponent.js\nimport Dynamic from '@airy/maleo/dynamic';\n\nexport default Dynamic({\n  loader: () => import( /* webpackChunkName:\"DynamicComponent\" */ './component/DynamicLoad'),\n})\n```\n\n### **Preloading**\n\nFor optimization purposes, you can also preload a component even before the component got rendered.\n\nFor example, if you want to load component when a button get pressed, you can start preloading the component when the user hovers the mouse over the button.\n\nThe component created by `Dynamic` exposes a [static `preload` method](https://github.com/jamiebuilds/react-loadable#loadablecomponentpreload).\n\n```jsx\nimport React from 'react';\nimport Dynamic from '@airy/maleo/dynamic';\n\nconst DynamicBar = Dynamic({\n  loader: () => import('./Bar'),\n  loading: LoadingCompoinent\n});\n\nclass MyComponent extends React.Component {\n  state = { showBar: false };\n\n  onClick = () => {\n    this.setState({ showBar: true });\n  };\n\n  onMouseOver = () => DynamicBar.preload();\n\n  render() {\n    return (\n      <div>\n        <button\n          onClick={this.onClick}\n          onMouseOver={this.onMouseOver}>\n          Show Bar\n        </button>\n        { this.state.showBar && <DynamicBar /> }\n      </div>\n    )\n  }\n}\n```\n\n## Customizable Component\n\n### Custom Document\n\nHighly inspired by what [Next.js](https://github.com/zeit/next.js) has done on their awesome template customization.\n\nMaleo.js also enable customization on `Document` as document's markup. So you don't need to include tags like `<html>`, `<head>`, `<body>`, etc. \n\nTo override the default behavior, you'll need to create a component that extends the `Document` React class provided by Maleo.\n\n```jsx\n// document.jsx\nimport React from 'react';\nimport { Document, Header, Main, Scripts } from '@airy/maleo/document';\n\nexport default class extends Document {\n  render() {\n    return (\n      <html>\n        <Header>\n          <title>Maleo JS</title>\n          <meta charset=\"utf-8\" />\n          <meta name=\"description\" content=\"Maleo.js is awesome!\" />\n\n          <style>\n            {` body { background-color: #fff } `}\n          </style>\n        </Header>\n\n        <body>\n          <Main />\n\n          <Scripts />\n\n          <ReduxScript />\n        </body>\n      </html>\n    );\n  }\n}\n```\n\n### Custom Wrap\n\nMaleo.js uses the `Wrap` component to initialize pages. `Wrap` contains React Router's Component. You can add HoC here to wrap the application and control the page initialization. Which allows you to do amazing things like:\n- Persisting layour between page changes\n- Keeping state when navigating pages\n- Custom error handling using `componentDidCatch`\n- Inject additional data into pages (like Redux provider, etc)\n\nTo override the default behavior, you'll need to create a component that extends the `Wrap` React class provided by Maleo.\n\n```jsx\n// wrap.jsx\nimport React from 'react';\nimport { Wrap } from '@airy/maleo/wrap';\n\n// Redux plugin for Maleo.js\n// Hoc that creates store and add Redux Provider\nimport { withRedux } from '@airy/maleo-redux-plugin';\n\n// Custom Wrapper that will be rendered for the whole Application\nimport Content from './component/Content';\nimport NavigationBar from './component/NavigationBar';\n\nimport { createStore } from './store';\n\n@withRedux(createStore)\nexport default class extends Wrap {\n  static getInitialProps = (ctx) => {\n    const { store } = ctx\n    // you receive store from context\n    // you can access or do something with the store here\n    console.log('Initialized Redux Store', store);\n    return {}\n  }\n\n  render() {\n    const { Container, containerProps, App, appProps } = this.props\n\n    return (\n      <Container {...containerProps}>\n        <NavigationBar />\n        <Content>\n          <App {...appProps}/>\n        </Content>\n      </Container>\n    )\n  }\n}\n\n```\nIf you put `document.jsx` and `wrap.jsx` on root directory (the same level with `package.json`), then Maleo will automatically register your custom Document and Wrap. Otherwise, you can add the path to your custom Document and Wrap on [Maleo config](#custom-configuration)\n\n---\n***We are also working on adding default and customizable `Error` component page***\n\n---\n\n## Custom Configuration\n\nFor more advanced configuration of Maleo.js, like `webpack` config, registering `plugins`, path to your routes, custom Document and Wrap, and adding `path alias`, you can create a `maleo.config.js` in the root of your project directory. (same directory with `package.json`)\n\n```js\n// maleo.config.js\n\nmodule.exports = {\n  /* config options here */\n}\n```\n\nHere are the API's for the configuration:\n\n<table>\n  <tr>\n    <td>Key</td>\n    <td>Type</td>\n    <td>Description</td>\n  </tr>\n  <tr>\n    <td><code>favicon</code></td>\n    <td><code>String</code> [<code>{project-dir}/favicon.ico</code>]</td>\n    <td>Path to favicon file</td>\n  </tr>\n  <tr>\n    <td><code>buildDir</code></td>\n    <td><code>String?</code> [<code>.maleo</code>]</td>\n    <td>Directory to put Maleo.js' build assets</td>\n  </tr>\n  <tr>\n    <td><code>cache</code></td>\n    <td><code>Boolean?</code> [<code>true</code>]</td>\n    <td>Enable webpack build caching</td>\n  </tr>\n  <tr>\n    <td><code>isDev</code></td>\n    <td><code>Boolean?</code> [<code>process.env.NODE_ENV === 'development'</code>]</td>\n    <td>Enable development build configuration</td>\n  </tr>\n  <tr>\n    <td><code>sourceMaps</code></td>\n    <td><code>Boolean?</code> [<code>true</code>]</td>\n    <td>Enable webpack to generate source maps</td>\n  </tr>\n  <tr>\n    <td><code>alias</code></td>\n    <td><code>Object?</code></td>\n    <td>A key value pair for aliasing path directory\n      <br>\n      <code>\n      { 'component': './src/component' }\n      </code>\n    </td>\n  </tr>\n  <tr>\n    <td><code>publicPath</code></td>\n    <td><code>String?</code> [<code>/_assets/</code>]</td>\n    <td>To customize webpack's <code>publicPath</code> \n    <br>Comes in handy if using CDN to put built assets</td>\n  </tr>\n  <tr>\n    <td><code>analyzeBundle</code></td>\n    <td><code>Boolean?</code> [<code>false</code>]</td>\n    <td>To enable webpack's bundle analyzer, for analyzing bundle sizes during bundle debugging should Maleo.js' build process got slow</td>\n  </tr>\n  <tr>\n    <td><code>webpack</code></td>\n    <td><code>Function?</code></td>\n    <td>To customize webpack configuration, more details <a href=\"#customize-webpack\">here</a></td>\n  </tr>\n  <tr>\n    <td><code>routes</code></td>\n    <td><code>string?</code> [<code>rootDir/routes.jsx</code>]</td>\n    <td>Path to your routes file</td>\n  </tr>\n  <tr>\n    <td><code>customDocument</code></td>\n    <td><code>string?</code> [<code>rootDir/document.jsx</code>]</td>\n    <td>Path to your custom document file</td>\n  </tr>\n  <tr>\n    <td><code>customWrap</code></td>\n    <td><code>string?</code> [<code>rootDir/wrap.jsx</code>]</td>\n    <td>Path to your custom wrap file</td>\n  </tr>\n  <tr>\n    <td><code>customApp</code></td>\n    <td><code>string?</code> [<code>rootDir/app.jsx</code>]</td>\n    <td>Path to your custom app file</td>\n  </tr>\n  <tr>\n    <td><code>csp</code></td>\n    <td><code>boolean | cspConfig</code> [<code>false</code>]</td>\n    <td>Config for <a href=\"https://developers.google.com/web/fundamentals/security/csp/\">Content Security Policy</a>, using <a href=\"https://helmetjs.github.io/docs/csp/\">Helmet-CSP</a>\n    <br/>\n    If set to true it will enable default config:\n    <br/>\n    <code>{\n      directives: {\n        defaultSrc: [`'self'`],\n        styleSrc: [`'self'`],\n      },\n    }</code>\n    </td>\n  </tr>\n</table>\n\n#### Customize Server\n\nCreate a `server.js` file on root directory where your `package.json` lives.\nHere you can customize Maleo's server.\n```js\nimport { Server } from '@airy/maleo/server';\n\nconst PORT = process.env.PORT || 3000;\n\nconst maleoServer = Server.init({\n  port: PORT,\n  runHandler: () => {\n    console.log('Server running on port :', PORT);\n  }\n});\n\nmaleoServer.run();\n```\n\nHere are the API's for the configuration:\n\n<table>\n  <tr>\n    <td>Key</td>\n    <td>Type</td>\n    <td>Description</td>\n  </tr>\n  <tr>\n    <td><code>port</code></td>\n    <td><code>Number?</code> [<code>3000</code>]</td>\n    <td>Port to run Maleo server</td>\n  </tr>\n  <tr>\n    <td><code>assetDir</code></td>\n    <td><code>String?</code> [<code>'<root>/.maleo/client'</code>]</td>\n    <td>Directory for all client related assets</td>\n  </tr>\n  <tr>\n    <td><code>runHandler</code></td>\n    <td><code>Function?</code></td>\n    <td>Function called when maleo server starter</td>\n  </tr>\n</table>\n\n#### Customize Webpack\n\nYou are able to extend Maleo.js' default webpack configuration by defining a function on `maleo.config.js`\n\n```js\n// maleo.config.js\n\nmodule.exports = {\n  webpack(config, context, next) {\n    // Perform customizations to webpack config\n    // Important: This call is required in order to let Maleo pass the config to webpack\n    return next(); \n  },\n};\n```\n\nWebpack function will receive three arguments:\n\n<table>\n  <tr>\n    <td>Argument</td>\n    <td>Details</td>\n  <tr>\n  <tr>\n    <td><code>config</code></td>\n    <td>This contains webpack configuration object that you can manipulate</td>\n  <tr>\n  <tr>\n    <td><code>context</code></td>\n    <td>This contains some keys that are useful for the build context\n      <br>\n      <table>\n        <tr>\n          <td><code>isDev</code></td>\n          <td><code>Boolean</code></td>\n          <td>Check if current build is for development</td>\n        </tr>\n        <tr>\n          <td><code>publicPath</code></td>\n          <td><code>String</code></td>\n          <td>Public Path of user defined or default's value</td>\n        </tr>\n        <tr>\n          <td><code>analyzeBundle</code></td>\n          <td><code>Boolean</code></td>\n          <td>Check if analyze bundle is enabled</td>\n        </tr>\n        <tr>\n          <td><code>buildDirectory</code></td>\n          <td><code>String</code></td>\n          <td>Build Directory of user defined or default's value</td>\n        </tr>\n        <tr>\n          <td><code>name</code></td>\n          <td><code>String</code></td>\n          <td>Build name <code>'server' || 'client'</code></td>\n        </tr>\n      </table>\n    </td>\n  <tr>\n  <tr>\n    <td><code>next</code></td>\n    <td>A callback required to be called to pass the custom configuration</td>\n  <tr>\n</table>\n\nExample of adding `ts-loader` through `maleo.config.js`:\n\n```js\n// maleo.config.js\n\n// Partly taken and modified from @airy/maleo-ts-plugin source\n// for simplicity purposes\n\nmodule.exports = {\n  webpack(config, context, next) {\n    const { isDev } = context\n\n    config.module.rules.push({\n      test: /\\.tsx?/,\n      exclude: /node_modules/,\n      use: [\n        require.resolve('@airy/maleo/lib/build/loaders/maleo-babel-loader'),\n        {\n          loader: 'ts-loader',\n          options: {\n            transpileOnly: true,\n          },\n        },\n      ],\n\n      if (isDev) {\n        config.plugins.push(new ForkTSCheckerWebpackPlugin());\n      }\n\n      return next();\n    })\n  },\n};\n```\n\n#### Customize Babel Config\n\nMaleo.js also let you have your own babel config. Just simply add `.babelrc` file at the root directory of your app.\n\nYou can include Maleo.js' babel preset in order to have latest JavaScript preset.\n\nHere's an example of `.babelrc` file:\n```json\n{\n  \"presets\": [\"@airy/maleo/babel\"],\n  \"plugins\": []\n}\n```\n\nThe `@airy/maleo/babel` preset includes everything you need to get your development started. The preset includes:\n- `@babel/preset-env`\n- `@babel/preset-react`\n- `@babel/plugin-proposal-class-properties`\n- `@babel/plugin-proposal-decorators`\n- `@babel/plugin-proposal-object-rest-spread`\n- `@babel/plugin-transform-runtime`\n- `react-loadable/babel`\n\n## Core Utilities Functions\n\n#### Route Matching\n\nTo use you can import the matching routes function like this:\n```js\nimport { getMatchedRoutes } from '@airy/maleo/utils';\n```\n\nThe function will return all matched routes and the route object from `routes.json`\n\n## CDN Support\n\nIf you are using a CDN, you can set up the `publicPath` setting and configure your CDN's origin to resolve to the domain that Maleo.js is hosted on.\n\n```js\n// maleo.config.js\n\nconst isProd = process.env.NODE_ENV === 'production';\n\nmodule.exports = {\n  assetPrefix: isProd && 'https://cdn.example.com';\n}\n```\n\n## FAQ\n\n== TO BE DETERMINED == \n\n## Plugins\n- [css-plugin](https://github.com/airyrooms/maleo-plugins/tree/master/packages/css-plugin)\n- [redux-plugin](https://github.com/airyrooms/maleo-plugins/tree/master/packages/redux-plugin)\n- [typescript-plugin](https://github.com/airyrooms/maleo-plugins/tree/master/packages/typescript-plugin)\n- [more](https://github.com/airyrooms/maleo-plugins)\n\n\n## Contributing\n[Please follow these steps to contribute to Maleo.js](https://github.com/airyrooms/maleo.js/blob/canary/CONTRIBUTING.md)\n\n[Please follow these steps to contribute to Maleo.js' plugins](https://github.com/airyrooms/maleo-plugins#contributing-guidelines)\n\n\n## Contributors\n\nThis project exists thanks to all the people who contribute. [Contribute](CONTRIBUTING.md).\n\nMany thanks to our **[contributors](https://github.com/airyrooms/maleo.js/graphs/contributors)**!\n\n\n## License\n\nMIT","readmeFilename":"README.md","bugs":{"url":"https://github.com/airyrooms/maleo.js/issues"},"homepage":"https://github.com/airyrooms/maleo.js#readme","_id":"@airy/maleo@0.3.1-canary.31","_nodeVersion":"10.16.1","_npmVersion":"lerna/3.13.1/node@v10.16.1+x64 (linux)","dist":{"integrity":"sha512-0EGNvb6fSBJBHAEFKL5z8QyVhlWlDDuMWGisi/gXOt56K3x1qqTiW/t9tdr499S2N6h5Ocep3douBvPkdj3slg==","shasum":"cd913bd6b61784410190c3ebf6e41b9699624d8e","tarball":"https://registry.npmjs.org/@airy/maleo/-/maleo-0.3.1-canary.31.tgz","fileCount":107,"unpackedSize":379436,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJdSTKTCRA9TVsSAnZWagAAP0oP+wQmRZF7ss/v7UEX4tIi\nxaTTEcwkoyoSOnBFpIrD/1dD6v0hGbvs7uoMkrsVYAkmR/fD/Q9Q9ior/rBm\n9noJFrxtfAzCGp4WClXh7CtJbYaX9OVnyg59a0xT09DwfLK8p9yT2NXHvaed\nK8TJZbouJWqm+pzeCcfQx2XDzRMCxB9Pl9eKdbno69OEBBjYUCV0cbf5k6/W\nmUJiA1uzkOqwj50x/JFpFq9Zcw63FbCpt92lnK2ni2ABBqSCO3YB3B75JcO5\n6itRG4yT7ibkx/QYRNsrPBDJog21kjGXxDu/VsP3ji5kQg0wwh1Nf/V5ubM0\niLRz6tI7nI4uHU/1gFoA7DZONDpxobPkSoPAV00/KISV91vOp32zjMojx1qP\nTBKaBh4vTNNRXUH0HcOuPs26iorD4RgNnHFQZwJmT7bY14LV5q8FrwAOWyEE\nF6zESBNEN4MB3cnlIFxyh/qKyRWJMLEfRisJQntaHCCQ1/E9F64xDUTFh0W1\nUL8faWMJw3OZm/vW2qErSCeCFdvaLzFqTbEVnrJtuJo9gHwhEKv6QySv7UW3\nTHsA/5KgaH/+iSOM69C09e3p5ogKuSa/PBvgGxBZX8D0Fv/wRu9WY2Q+C5RR\nlANr1Yv8Ncjt/pnLSs7+BI47KqSJID74IBAvdI4DLYUBvsdbisca3hYRZ4iC\nJQIF\r\n=/WoS\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCICXMtbXcNkddXSQsJvM1/VfHDgZaZqg5njsmLIlZWZFzAiA4COKOlUYZU/s4IqF++v2J2piQpCoWXNQZI28h84HMSg=="}]},"maintainers":[{"name":"airyrooms-engineering","email":"engineering@airyrooms.com"}],"_npmUser":{"name":"airyrooms-engineering","email":"engineering@airyrooms.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/maleo_0.3.1-canary.31_1565078162499_0.5594985803848411"},"_hasShrinkwrap":false},"0.3.1-canary.32":{"name":"@airy/maleo","version":"0.3.1-canary.32","description":"Un-opinionated Universal Rendering Javascript Framework","repository":{"type":"git","url":"git+https://github.com/airyrooms/maleo.js.git","bugs":"https://github.com/airyrooms/maleo.js/issues"},"author":{"name":"Airy"},"license":"MIT","private":false,"sideEffects":false,"scripts":{"build":"rm -rf dist && gulp","watch":"gulp watch","prepublish":"yarn build","start":"node server.js"},"bin":{"maleo":"lib/bin/maleo.js"},"dependencies":{"@babel/core":"7.1.2","@babel/plugin-proposal-class-properties":"7.1.0","@babel/plugin-proposal-decorators":"7.1.2","@babel/plugin-proposal-object-rest-spread":"7.0.0","@babel/plugin-syntax-dynamic-import":"7.0.0","@babel/plugin-transform-runtime":"7.1.0","@babel/preset-env":"7.1.0","@babel/preset-react":"7.0.0","@babel/runtime":"7.1.2","@babel/runtime-corejs2":"7.2.0","babel-loader":"8.0.4","babel-plugin-transform-react-remove-prop-types":"^0.4.24","case-sensitive-paths-webpack-plugin":"2.1.2","compression":"1.7.4","es6-promise":"4.2.5","eventsource":"1.0.7","express":"4.16.4","figlet":"1.2.1","flatted":"^2.0.1","friendly-errors-webpack-plugin":"1.7.0","hard-source-webpack-plugin":"0.13.1","helmet":"3.14.0","hoist-non-react-statics":"^3.3.0","isomorphic-fetch":"2.2.1","loader-utils":"1.1.0","node-notifier":"5.2.1","nodemon":"1.18.4","react-dom":"16.8.4","react-helmet":"5.2.0","react-loadable":"5.5.0","react-router-config":"5.0.0","react-router-dom":"5.0.0","rimraf":"2.6.3","source-map-support":"0.5.9","terser-webpack-plugin":"1.1.0","webpack":"4.29.0","webpack-bundle-analyzer":"3.0.3","webpack-cli":"3.1.2","webpack-dev-middleware":"3.4.0","webpack-hot-middleware":"2.24.3","webpackbar":"2.6.3"},"peerDependencies":{"react":"^16.8.4"},"devDependencies":{"@babel/preset-typescript":"^7.1.0","@types/webpack-bundle-analyzer":"^2.13.0","babel-plugin-module-resolver":"^3.1.1","gulp":"github:gulpjs/gulp#4.0","gulp-babel":"^8.0.0","gulp-clean":"^0.4.0","gulp-sourcemaps":"^2.6.4","gulp-uglify":"^3.0.1","gulp-watch":"^5.0.1","react":"^16.8.4"},"gitHead":"84acc4b0ee6052c677e988233aa90d713576ef2d","readme":"[![Build Status](https://travis-ci.org/airyrooms/maleo.js.svg?branch=master)](https://travis-ci.org/airyrooms/maleo.js)\n[![Coverage Status](https://coveralls.io/repos/github/airyrooms/maleo.js/badge.svg?branch=master)](https://coveralls.io/github/airyrooms/maleo.js?branch=master)\n[![npm version](https://badge.fury.io/js/%40airy%2Fmaleo.svg)](https://badge.fury.io/js/%40airy%2Fmaleo)\n[![Issues](http://img.shields.io/github/issues/airyrooms/maleo.js.svg)]( https://github.com/airyrooms/maleo.js/issues)\n[![MIT license](https://img.shields.io/badge/License-MIT-blue.svg)](https://github.com/airyrooms/maleo.js/blob/canary/LICENSE)\n[![Discord Chat](https://img.shields.io/discord/550498214789513218.svg)](https://discord.gg/9eArCQn)\n[![Conventional Commits](https://img.shields.io/badge/Conventional%20Commits-1.0.0-green.svg)](https://conventionalcommits.org)\n\n\n# Welcome to Maleo.js\n\nMaleo.js is an un-opinionated framework to enable Universal Rendering in JavaScript using React with no hassle.\n\nWe are here to solve the time consuming setups Universal Rendering Required.\n\n---\n\nReadme below is the documentation for the `canary` (prerelease) branch. To view the documentation for the latest stable Maleo.js version change branch to `master`\n\n---\n\n# Table of Contents\n- [Features](#features)\n- [Setup](#setup)\n- [Component Lifecycle](#component-lifecycle)\n- [Routing](#routing)\n- [Dynamic Import Component](#dynamic-import-component)\n  - [Tips](#tips)\n  - [Preloading](#preloading)\n- [Customizable Component](#customizable-component)\n  - [Custom Document](#custom-document)\n  - [Custom Wrap](#custom-wrap)\n- [Custom Configuration](#custom-configuration)\n  - [Customize Server](#customize-server)\n  - [Customize Webpack](#customize-webpack)\n  - [Customize Babel Config](#customize-babel-config)\n- [Core Utilities Functions](#core-utilities-functions)\n- [CDN Support](#cdn-support)\n- [Plugins](#plugins)\n- [FAQ](#faq)\n- [Contributing](#contributing)\n\n---\n\n## Features\n- Universal Rendering\n- Plugin based framework\n- Customizable\n\n## Setup\n\nInstall Maleo.js \n\n>*for now change `@airy/maleo` to `@airy/maleo@canary` until we publish the stable version of this package*\n\n\n**NPM**\n```bash\n$ npm install --save @airy/maleo react\n```\n**Yarn**\n```bash\n$ yarn add @airy/maleo react\n```\n\nAdd this script to your `package.json`\n```json\n{\n  \"scripts\": {\n    \"dev\": \"maleo dev\",\n    \"build\": \"export NODE_ENV=production && maleo build\",\n    \"start\": \"export NODE_ENV=production && maleo run\"\n  }\n}\n```\n\nCreate a page Root component\n```jsx\n// ./src/Root.jsx\nimport React from 'react';\n\n// Export default is required for registering page\nexport default class RootComponent extends React.Component {\n  render() {\n    return (\n      <h1>Hello World!</h1>\n    )\n  }\n}\n```\n\nAnd lastly, create a routing file on your project root directory called `routes.json` and register your page component\n```json\n[\n  {\n    \"path\": \"/\",\n    \"page\": \"./src/Root\"\n  }\n]\n```\n\nAfter that you can now run `$ npm run dev` and go to `http://localhost:3000`.\n\nYou should now see your app running on your browser.\n\nBy now you should see \n- Automatic transpilation and bundling (with webpack and babel)\n- ~~Hot code reloading~~ [#17](https://github.com/airyrooms/maleo.js/issues/17)\n- Server rendering\n\nTo see how simple this is, check out the sample app!\n\n## Component Lifecycle \n\nMaleo.js added a new component lifecycle hook called `getInitialProps`, this function is called during Server Side Rendering (SSR) and during route changes on Client Side Rendering (CSR).\n\nThis is useful especially for SEO purposes. \n\nExample for stateful component:\n```jsx\nimport React from 'react';\n\nexport default class extends React.Component {\n  static getInitialProps = async (ctx) => {\n    const { req } = ctx;\n\n    // check if getInitialProps is called on server or on client\n    const userAgent = req ? req.headers['user-agent'] : navigator.userAgent;\n\n    // the return value will be passed as props for this component\n    return { userAgent };\n  }\n\n  render() {\n    return (\n      <div>\n        Hello World {this.props.userAgent}\n      </div>\n    );\n  }\n}\n```\n\nExample for stateless component:\n```jsx\nconst Component = (props) => (\n  <div>\n    Hello World {props.userAgent}\n  </div>\n);\n\nComponent.getInitialprops = async (ctx) => {\n  const { req } = ctx;\n\n  const userAgent = req ? req.headers['user-agent'] : navigator.userAgent;\n\n  return { userAgent };\n};\n\nexport default Component;\n```\n\n`getInitialProps` receives a context object with the following properties:\n- `req` - HTTP request object (server only)\n- `res` - HTTP response object (server only)\n- `...wrapProps` - Spreaded properties from custom Wrap\n- `...appProps` - Spreaded properties from custom App\n- `matched` - Matched routes contains list of object, which is consists of `match` object and `route` object\n- `route` - Component's route attributes on `routes.json` file\n\n## Routing\n\nRouting is declared in a centralized route config.\nRegister all the route config in `routes.json` file.\n\nIf you put the `routes.json` files on root directory, Maleo will automatically register your route. Otherwise put path to your routes on [Maleo config](#custom-configuration).\n\nRoutes file has to export default the route configuration.\nThe route object **expected to have distinct key** to indicate the route.\n\n<table>\n  <tr>\n    <td>Key</td>\n    <td>Type</td>\n    <td>Description</td>\n  </tr>\n  <tr>\n    <td><code>path</code></td>\n    <td><code>String!</code></td>\n    <td>Routes path</td>\n  </tr>\n  <tr>\n    <td><code>page</code></td>\n    <td><code>String!</code></td>\n    <td>Path to React Component for this route path</td>\n  </tr>\n  <tr>\n    <td><code>exact</code></td>\n    <td><code>Boolean?</code> [<code>false</code>]</td>\n    <td>To make url has to match exactly the path in order to render the component. Give <code>false</code> value if the route is a wrapper route component</td>\n  </tr>\n  <tr>\n    <td><code>routes</code></td>\n    <td><code>RouteObject?</code></td>\n    <td>Nested route</td>\n  </tr>\n</table>\n\n\nFor example:\n```json\n[\n  {\n    \"page\": \"./src/MainApp\",\n    \"routes\": [\n      {\n        \"path\": \"/\",\n        \"page\": \"./src/Search\",\n        \"exact\": true\n      },\n      {\n        \"path\": \"/search\",\n        \"page\": \"./src/Search\",\n        \"routes\": [\n          {\n            \"path\": \"/search/hello\",\n            \"page\": \"./src/Detail\",\n            \"exact\": true\n          }\n        ]\n      },\n      {\n        \"path\": \"/detail\",\n        \"page\": \"./src/Detail\",\n        \"exact\": true\n      }\n    ]\n  }\n]\n```\n\n## Dynamic Import Component\nMaleo.js supports TC39 [dynamic import proposal](https://github.com/tc39/proposal-dynamic-import) for JavaScript.\n\nYou can think dynamic import as another way to split your code into manageable chunks. You can use our `Dynamic` function which utilizes [react loadable](https://github.com/jamiebuilds/react-loadable)\n\nFor Example\n```js\n// DynamicComponent.js\nimport Dynamic from '@airy/maleo/dynamic';\n\nexport default Dynamic({\n  loader: () => import( /* webpackChunkName:\"DynamicComponent\" */ './component/DynamicLoad'),\n})\n```\n\n### **Preloading**\n\nFor optimization purposes, you can also preload a component even before the component got rendered.\n\nFor example, if you want to load component when a button get pressed, you can start preloading the component when the user hovers the mouse over the button.\n\nThe component created by `Dynamic` exposes a [static `preload` method](https://github.com/jamiebuilds/react-loadable#loadablecomponentpreload).\n\n```jsx\nimport React from 'react';\nimport Dynamic from '@airy/maleo/dynamic';\n\nconst DynamicBar = Dynamic({\n  loader: () => import('./Bar'),\n  loading: LoadingCompoinent\n});\n\nclass MyComponent extends React.Component {\n  state = { showBar: false };\n\n  onClick = () => {\n    this.setState({ showBar: true });\n  };\n\n  onMouseOver = () => DynamicBar.preload();\n\n  render() {\n    return (\n      <div>\n        <button\n          onClick={this.onClick}\n          onMouseOver={this.onMouseOver}>\n          Show Bar\n        </button>\n        { this.state.showBar && <DynamicBar /> }\n      </div>\n    )\n  }\n}\n```\n\n## Customizable Component\n\n### Custom Document\n\nHighly inspired by what [Next.js](https://github.com/zeit/next.js) has done on their awesome template customization.\n\nMaleo.js also enable customization on `Document` as document's markup. So you don't need to include tags like `<html>`, `<head>`, `<body>`, etc. \n\nTo override the default behavior, you'll need to create a component that extends the `Document` React class provided by Maleo.\n\n```jsx\n// document.jsx\nimport React from 'react';\nimport { Document, Header, Main, Scripts } from '@airy/maleo/document';\n\nexport default class extends Document {\n  render() {\n    return (\n      <html>\n        <Header>\n          <title>Maleo JS</title>\n          <meta charset=\"utf-8\" />\n          <meta name=\"description\" content=\"Maleo.js is awesome!\" />\n\n          <style>\n            {` body { background-color: #fff } `}\n          </style>\n        </Header>\n\n        <body>\n          <Main />\n\n          <Scripts />\n\n          <ReduxScript />\n        </body>\n      </html>\n    );\n  }\n}\n```\n\n### Custom Wrap\n\nMaleo.js uses the `Wrap` component to initialize pages. `Wrap` contains React Router's Component. You can add HoC here to wrap the application and control the page initialization. Which allows you to do amazing things like:\n- Persisting layour between page changes\n- Keeping state when navigating pages\n- Custom error handling using `componentDidCatch`\n- Inject additional data into pages (like Redux provider, etc)\n\nTo override the default behavior, you'll need to create a component that extends the `Wrap` React class provided by Maleo.\n\n```jsx\n// wrap.jsx\nimport React from 'react';\nimport { Wrap } from '@airy/maleo/wrap';\n\n// Redux plugin for Maleo.js\n// Hoc that creates store and add Redux Provider\nimport { withRedux } from '@airy/maleo-redux-plugin';\n\n// Custom Wrapper that will be rendered for the whole Application\nimport Content from './component/Content';\nimport NavigationBar from './component/NavigationBar';\n\nimport { createStore } from './store';\n\n@withRedux(createStore)\nexport default class extends Wrap {\n  static getInitialProps = (ctx) => {\n    const { store } = ctx\n    // you receive store from context\n    // you can access or do something with the store here\n    console.log('Initialized Redux Store', store);\n    return {}\n  }\n\n  render() {\n    const { Container, containerProps, App, appProps } = this.props\n\n    return (\n      <Container {...containerProps}>\n        <NavigationBar />\n        <Content>\n          <App {...appProps}/>\n        </Content>\n      </Container>\n    )\n  }\n}\n\n```\nIf you put `document.jsx` and `wrap.jsx` on root directory (the same level with `package.json`), then Maleo will automatically register your custom Document and Wrap. Otherwise, you can add the path to your custom Document and Wrap on [Maleo config](#custom-configuration)\n\n---\n***We are also working on adding default and customizable `Error` component page***\n\n---\n\n## Custom Configuration\n\nFor more advanced configuration of Maleo.js, like `webpack` config, registering `plugins`, path to your routes, custom Document and Wrap, and adding `path alias`, you can create a `maleo.config.js` in the root of your project directory. (same directory with `package.json`)\n\n```js\n// maleo.config.js\n\nmodule.exports = {\n  /* config options here */\n}\n```\n\nHere are the API's for the configuration:\n\n<table>\n  <tr>\n    <td>Key</td>\n    <td>Type</td>\n    <td>Description</td>\n  </tr>\n  <tr>\n    <td><code>favicon</code></td>\n    <td><code>String</code> [<code>{project-dir}/favicon.ico</code>]</td>\n    <td>Path to favicon file</td>\n  </tr>\n  <tr>\n    <td><code>buildDir</code></td>\n    <td><code>String?</code> [<code>.maleo</code>]</td>\n    <td>Directory to put Maleo.js' build assets</td>\n  </tr>\n  <tr>\n    <td><code>cache</code></td>\n    <td><code>Boolean?</code> [<code>true</code>]</td>\n    <td>Enable webpack build caching</td>\n  </tr>\n  <tr>\n    <td><code>isDev</code></td>\n    <td><code>Boolean?</code> [<code>process.env.NODE_ENV === 'development'</code>]</td>\n    <td>Enable development build configuration</td>\n  </tr>\n  <tr>\n    <td><code>sourceMaps</code></td>\n    <td><code>Boolean?</code> [<code>true</code>]</td>\n    <td>Enable webpack to generate source maps</td>\n  </tr>\n  <tr>\n    <td><code>alias</code></td>\n    <td><code>Object?</code></td>\n    <td>A key value pair for aliasing path directory\n      <br>\n      <code>\n      { 'component': './src/component' }\n      </code>\n    </td>\n  </tr>\n  <tr>\n    <td><code>publicPath</code></td>\n    <td><code>String?</code> [<code>/_assets/</code>]</td>\n    <td>To customize webpack's <code>publicPath</code> \n    <br>Comes in handy if using CDN to put built assets</td>\n  </tr>\n  <tr>\n    <td><code>analyzeBundle</code></td>\n    <td><code>Boolean?</code> [<code>false</code>]</td>\n    <td>To enable webpack's bundle analyzer, for analyzing bundle sizes during bundle debugging should Maleo.js' build process got slow</td>\n  </tr>\n  <tr>\n    <td><code>webpack</code></td>\n    <td><code>Function?</code></td>\n    <td>To customize webpack configuration, more details <a href=\"#customize-webpack\">here</a></td>\n  </tr>\n  <tr>\n    <td><code>routes</code></td>\n    <td><code>string?</code> [<code>rootDir/routes.jsx</code>]</td>\n    <td>Path to your routes file</td>\n  </tr>\n  <tr>\n    <td><code>customDocument</code></td>\n    <td><code>string?</code> [<code>rootDir/document.jsx</code>]</td>\n    <td>Path to your custom document file</td>\n  </tr>\n  <tr>\n    <td><code>customWrap</code></td>\n    <td><code>string?</code> [<code>rootDir/wrap.jsx</code>]</td>\n    <td>Path to your custom wrap file</td>\n  </tr>\n  <tr>\n    <td><code>customApp</code></td>\n    <td><code>string?</code> [<code>rootDir/app.jsx</code>]</td>\n    <td>Path to your custom app file</td>\n  </tr>\n  <tr>\n    <td><code>csp</code></td>\n    <td><code>boolean | cspConfig</code> [<code>false</code>]</td>\n    <td>Config for <a href=\"https://developers.google.com/web/fundamentals/security/csp/\">Content Security Policy</a>, using <a href=\"https://helmetjs.github.io/docs/csp/\">Helmet-CSP</a>\n    <br/>\n    If set to true it will enable default config:\n    <br/>\n    <code>{\n      directives: {\n        defaultSrc: [`'self'`],\n        styleSrc: [`'self'`],\n      },\n    }</code>\n    </td>\n  </tr>\n</table>\n\n#### Customize Server\n\nCreate a `server.js` file on root directory where your `package.json` lives.\nHere you can customize Maleo's server.\n```js\nimport { Server } from '@airy/maleo/server';\n\nconst PORT = process.env.PORT || 3000;\n\nconst maleoServer = Server.init({\n  port: PORT,\n  runHandler: () => {\n    console.log('Server running on port :', PORT);\n  }\n});\n\nmaleoServer.run();\n```\n\nHere are the API's for the configuration:\n\n<table>\n  <tr>\n    <td>Key</td>\n    <td>Type</td>\n    <td>Description</td>\n  </tr>\n  <tr>\n    <td><code>port</code></td>\n    <td><code>Number?</code> [<code>3000</code>]</td>\n    <td>Port to run Maleo server</td>\n  </tr>\n  <tr>\n    <td><code>assetDir</code></td>\n    <td><code>String?</code> [<code>'<root>/.maleo/client'</code>]</td>\n    <td>Directory for all client related assets</td>\n  </tr>\n  <tr>\n    <td><code>runHandler</code></td>\n    <td><code>Function?</code></td>\n    <td>Function called when maleo server starter</td>\n  </tr>\n</table>\n\n#### Customize Webpack\n\nYou are able to extend Maleo.js' default webpack configuration by defining a function on `maleo.config.js`\n\n```js\n// maleo.config.js\n\nmodule.exports = {\n  webpack(config, context, next) {\n    // Perform customizations to webpack config\n    // Important: This call is required in order to let Maleo pass the config to webpack\n    return next(); \n  },\n};\n```\n\nWebpack function will receive three arguments:\n\n<table>\n  <tr>\n    <td>Argument</td>\n    <td>Details</td>\n  <tr>\n  <tr>\n    <td><code>config</code></td>\n    <td>This contains webpack configuration object that you can manipulate</td>\n  <tr>\n  <tr>\n    <td><code>context</code></td>\n    <td>This contains some keys that are useful for the build context\n      <br>\n      <table>\n        <tr>\n          <td><code>isDev</code></td>\n          <td><code>Boolean</code></td>\n          <td>Check if current build is for development</td>\n        </tr>\n        <tr>\n          <td><code>publicPath</code></td>\n          <td><code>String</code></td>\n          <td>Public Path of user defined or default's value</td>\n        </tr>\n        <tr>\n          <td><code>analyzeBundle</code></td>\n          <td><code>Boolean</code></td>\n          <td>Check if analyze bundle is enabled</td>\n        </tr>\n        <tr>\n          <td><code>buildDirectory</code></td>\n          <td><code>String</code></td>\n          <td>Build Directory of user defined or default's value</td>\n        </tr>\n        <tr>\n          <td><code>name</code></td>\n          <td><code>String</code></td>\n          <td>Build name <code>'server' || 'client'</code></td>\n        </tr>\n      </table>\n    </td>\n  <tr>\n  <tr>\n    <td><code>next</code></td>\n    <td>A callback required to be called to pass the custom configuration</td>\n  <tr>\n</table>\n\nExample of adding `ts-loader` through `maleo.config.js`:\n\n```js\n// maleo.config.js\n\n// Partly taken and modified from @airy/maleo-ts-plugin source\n// for simplicity purposes\n\nmodule.exports = {\n  webpack(config, context, next) {\n    const { isDev } = context\n\n    config.module.rules.push({\n      test: /\\.tsx?/,\n      exclude: /node_modules/,\n      use: [\n        require.resolve('@airy/maleo/lib/build/loaders/maleo-babel-loader'),\n        {\n          loader: 'ts-loader',\n          options: {\n            transpileOnly: true,\n          },\n        },\n      ],\n\n      if (isDev) {\n        config.plugins.push(new ForkTSCheckerWebpackPlugin());\n      }\n\n      return next();\n    })\n  },\n};\n```\n\n#### Customize Babel Config\n\nMaleo.js also let you have your own babel config. Just simply add `.babelrc` file at the root directory of your app.\n\nYou can include Maleo.js' babel preset in order to have latest JavaScript preset.\n\nHere's an example of `.babelrc` file:\n```json\n{\n  \"presets\": [\"@airy/maleo/babel\"],\n  \"plugins\": []\n}\n```\n\nThe `@airy/maleo/babel` preset includes everything you need to get your development started. The preset includes:\n- `@babel/preset-env`\n- `@babel/preset-react`\n- `@babel/plugin-proposal-class-properties`\n- `@babel/plugin-proposal-decorators`\n- `@babel/plugin-proposal-object-rest-spread`\n- `@babel/plugin-transform-runtime`\n- `react-loadable/babel`\n\n## Core Utilities Functions\n\n#### Route Matching\n\nTo use you can import the matching routes function like this:\n```js\nimport { getMatchedRoutes } from '@airy/maleo/utils';\n```\n\nThe function will return all matched routes and the route object from `routes.json`\n\n## CDN Support\n\nIf you are using a CDN, you can set up the `publicPath` setting and configure your CDN's origin to resolve to the domain that Maleo.js is hosted on.\n\n```js\n// maleo.config.js\n\nconst isProd = process.env.NODE_ENV === 'production';\n\nmodule.exports = {\n  assetPrefix: isProd && 'https://cdn.example.com';\n}\n```\n\n## FAQ\n\n== TO BE DETERMINED == \n\n## Plugins\n- [css-plugin](https://github.com/airyrooms/maleo-plugins/tree/master/packages/css-plugin)\n- [redux-plugin](https://github.com/airyrooms/maleo-plugins/tree/master/packages/redux-plugin)\n- [typescript-plugin](https://github.com/airyrooms/maleo-plugins/tree/master/packages/typescript-plugin)\n- [more](https://github.com/airyrooms/maleo-plugins)\n\n\n## Contributing\n[Please follow these steps to contribute to Maleo.js](https://github.com/airyrooms/maleo.js/blob/canary/CONTRIBUTING.md)\n\n[Please follow these steps to contribute to Maleo.js' plugins](https://github.com/airyrooms/maleo-plugins#contributing-guidelines)\n\n\n## Contributors\n\nThis project exists thanks to all the people who contribute. [Contribute](CONTRIBUTING.md).\n\nMany thanks to our **[contributors](https://github.com/airyrooms/maleo.js/graphs/contributors)**!\n\n\n## License\n\nMIT","readmeFilename":"README.md","bugs":{"url":"https://github.com/airyrooms/maleo.js/issues"},"homepage":"https://github.com/airyrooms/maleo.js#readme","_id":"@airy/maleo@0.3.1-canary.32","_nodeVersion":"11.15.0","_npmVersion":"lerna/3.13.1/node@v11.15.0+x64 (linux)","dist":{"integrity":"sha512-nnqGUdbZaxZqkM463Yyn0m6HUTnPGrfhTLhe3NsQwrOEvXkB8n3Cm8MRdgG7R2esi4PGO/XtSlrrOnUrVwM6dw==","shasum":"11a4a201eacc94405a824f03fc92dda73e8f4216","tarball":"https://registry.npmjs.org/@airy/maleo/-/maleo-0.3.1-canary.32.tgz","fileCount":107,"unpackedSize":379784,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJde0RRCRA9TVsSAnZWagAA+OgP/iOOalFRTSW1rzoyD5Qy\nBVqLEZnWVmtA37BsOmyq8ohuZmSE/XrjPw0HdJYXGPgnm/zzG8SPWFrABeBV\nYf7eirDbKVTWlmX9dEJk+QmfOSytSGK100+ozUcgiA6+wGCg5/7Ln/2K1s+0\nyZMHd0n/wdRtoUCSQ1uKtE7smcmcYp3mTzM9NNJ03pq0EkXllbOYUlGwK6q7\nKiJvU7bZHO2BnLTpzSIMbAxTHIybgTHfkqMm+brsBtB8hu56QovsXG5vI8S4\nCOv2S7kwcna8bnlIFJTAFwoeAhj9PYY0rdSAuKAZllwBz10f6z+1EqNw6TV4\npFWf2ku/z4zAzIJr5rH6cm1jwEoYVDW/ULVPpi6jzFJpEWW6yD3eF0yBVp85\nqaN8GSqUfsHT/swYNE3i7AO0QNZFC1+OGZbtCEFeqhh2R5C/cgpkw0y7e6Hs\nxaMyCeS0uttevyvrIK32erpAhRtE5ZFd64lHpxBF6t7SCoP/9S5CA7LRE3/z\nijIv88Od6W6n6yJgKsFBrmDVkSzl1t6oVhKU66gQJD+j+rR/5VzDb6+6TrzT\nepY3Kj6BzZZt/mOW+HvqZWBZKUe3eoTEeLuIfGO9pwICfS4A2qPX1Ccsc3qd\nf+0lDL04yefiXEGWu+KzSmL95hHGrQtOXUphhPAHnjUUoary1TPr6LCt++Jp\nJbER\r\n=450f\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIEuprqznuLl5nD8E5hTxmmo9pwXTbVEEBQFda3YJ5uDHAiEAv/46wjwndo1k4t96YGlLz9is4iVeJsmkUOtwYysds08="}]},"maintainers":[{"name":"airyrooms-engineering","email":"engineering@airyrooms.com"}],"_npmUser":{"name":"airyrooms-engineering","email":"engineering@airyrooms.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/maleo_0.3.1-canary.32_1568359505269_0.6419308590265929"},"_hasShrinkwrap":false},"0.3.1-canary.33":{"name":"@airy/maleo","version":"0.3.1-canary.33","description":"Un-opinionated Universal Rendering Javascript Framework","repository":{"type":"git","url":"git+https://github.com/airyrooms/maleo.js.git","bugs":"https://github.com/airyrooms/maleo.js/issues"},"author":{"name":"Airy"},"license":"MIT","private":false,"sideEffects":false,"scripts":{"build":"rm -rf dist && gulp","watch":"gulp watch","prepublish":"yarn build","start":"node server.js"},"bin":{"maleo":"lib/bin/maleo.js"},"dependencies":{"@babel/core":"7.6.2","@babel/plugin-proposal-class-properties":"7.5.5","@babel/plugin-proposal-decorators":"7.6.0","@babel/plugin-proposal-object-rest-spread":"7.6.2","@babel/plugin-syntax-dynamic-import":"7.2.0","@babel/plugin-transform-runtime":"7.6.2","@babel/preset-env":"7.6.2","@babel/preset-react":"7.0.0","@babel/runtime":"7.6.2","@babel/runtime-corejs2":"7.6.2","babel-loader":"8.0.4","babel-plugin-transform-react-remove-prop-types":"^0.4.24","case-sensitive-paths-webpack-plugin":"2.2.0","compression":"1.7.4","es6-promise":"4.2.5","eventsource":"1.0.7","express":"4.16.4","figlet":"1.2.1","flatted":"^2.0.1","friendly-errors-webpack-plugin":"1.7.0","hard-source-webpack-plugin":"0.13.1","helmet":"3.14.0","hoist-non-react-statics":"^3.3.0","isomorphic-fetch":"2.2.1","loader-utils":"1.1.0","node-notifier":"5.2.1","nodemon":"1.18.4","react-dom":"16.8.4","react-helmet":"5.2.0","react-loadable":"5.5.0","react-router-config":"5.0.0","react-router-dom":"5.0.0","rimraf":"2.6.3","source-map-support":"0.5.9","terser-webpack-plugin":"1.1.0","webpack":"4.29.0","webpack-bundle-analyzer":"3.0.3","webpack-cli":"3.1.2","webpack-dev-middleware":"3.4.0","webpack-hot-middleware":"2.24.3","webpackbar":"2.6.3"},"peerDependencies":{"react":"^16.8.4"},"devDependencies":{"@babel/preset-typescript":"^7.1.0","@types/webpack-bundle-analyzer":"^2.13.0","babel-plugin-module-resolver":"^3.1.1","gulp":"github:gulpjs/gulp#4.0","gulp-babel":"^8.0.0","gulp-clean":"^0.4.0","gulp-sourcemaps":"^2.6.4","gulp-uglify":"^3.0.1","gulp-watch":"^5.0.1","react":"^16.8.4"},"gitHead":"fba81080d162ee18e3d11a588adba85af10def9c","readme":"[![Build Status](https://travis-ci.org/airyrooms/maleo.js.svg?branch=master)](https://travis-ci.org/airyrooms/maleo.js)\n[![Coverage Status](https://coveralls.io/repos/github/airyrooms/maleo.js/badge.svg?branch=master)](https://coveralls.io/github/airyrooms/maleo.js?branch=master)\n[![npm version](https://badge.fury.io/js/%40airy%2Fmaleo.svg)](https://badge.fury.io/js/%40airy%2Fmaleo)\n[![Issues](http://img.shields.io/github/issues/airyrooms/maleo.js.svg)]( https://github.com/airyrooms/maleo.js/issues)\n[![MIT license](https://img.shields.io/badge/License-MIT-blue.svg)](https://github.com/airyrooms/maleo.js/blob/canary/LICENSE)\n[![Discord Chat](https://img.shields.io/discord/550498214789513218.svg)](https://discord.gg/9eArCQn)\n[![Conventional Commits](https://img.shields.io/badge/Conventional%20Commits-1.0.0-green.svg)](https://conventionalcommits.org)\n\n\n# Welcome to Maleo.js\n\nMaleo.js is an un-opinionated framework to enable Universal Rendering in JavaScript using React with no hassle.\n\nWe are here to solve the time consuming setups Universal Rendering Required.\n\n---\n\nReadme below is the documentation for the `canary` (prerelease) branch. To view the documentation for the latest stable Maleo.js version change branch to `master`\n\n---\n\n# Table of Contents\n- [Features](#features)\n- [Setup](#setup)\n- [Component Lifecycle](#component-lifecycle)\n- [Routing](#routing)\n- [Dynamic Import Component](#dynamic-import-component)\n  - [Tips](#tips)\n  - [Preloading](#preloading)\n- [Customizable Component](#customizable-component)\n  - [Custom Document](#custom-document)\n  - [Custom Wrap](#custom-wrap)\n- [Custom Configuration](#custom-configuration)\n  - [Customize Server](#customize-server)\n  - [Customize Webpack](#customize-webpack)\n  - [Customize Babel Config](#customize-babel-config)\n- [Core Utilities Functions](#core-utilities-functions)\n- [CDN Support](#cdn-support)\n- [Plugins](#plugins)\n- [FAQ](#faq)\n- [Contributing](#contributing)\n\n---\n\n## Features\n- Universal Rendering\n- Plugin based framework\n- Customizable\n\n## Setup\n\nInstall Maleo.js \n\n>*for now change `@airy/maleo` to `@airy/maleo@canary` until we publish the stable version of this package*\n\n\n**NPM**\n```bash\n$ npm install --save @airy/maleo react\n```\n**Yarn**\n```bash\n$ yarn add @airy/maleo react\n```\n\nAdd this script to your `package.json`\n```json\n{\n  \"scripts\": {\n    \"dev\": \"maleo dev\",\n    \"build\": \"export NODE_ENV=production && maleo build\",\n    \"start\": \"export NODE_ENV=production && maleo run\"\n  }\n}\n```\n\nCreate a page Root component\n```jsx\n// ./src/Root.jsx\nimport React from 'react';\n\n// Export default is required for registering page\nexport default class RootComponent extends React.Component {\n  render() {\n    return (\n      <h1>Hello World!</h1>\n    )\n  }\n}\n```\n\nAnd lastly, create a routing file on your project root directory called `routes.json` and register your page component\n```json\n[\n  {\n    \"path\": \"/\",\n    \"page\": \"./src/Root\"\n  }\n]\n```\n\nAfter that you can now run `$ npm run dev` and go to `http://localhost:3000`.\n\nYou should now see your app running on your browser.\n\nBy now you should see \n- Automatic transpilation and bundling (with webpack and babel)\n- ~~Hot code reloading~~ [#17](https://github.com/airyrooms/maleo.js/issues/17)\n- Server rendering\n\nTo see how simple this is, check out the sample app!\n\n## Component Lifecycle \n\nMaleo.js added a new component lifecycle hook called `getInitialProps`, this function is called during Server Side Rendering (SSR) and during route changes on Client Side Rendering (CSR).\n\nThis is useful especially for SEO purposes. \n\nExample for stateful component:\n```jsx\nimport React from 'react';\n\nexport default class extends React.Component {\n  static getInitialProps = async (ctx) => {\n    const { req } = ctx;\n\n    // check if getInitialProps is called on server or on client\n    const userAgent = req ? req.headers['user-agent'] : navigator.userAgent;\n\n    // the return value will be passed as props for this component\n    return { userAgent };\n  }\n\n  render() {\n    return (\n      <div>\n        Hello World {this.props.userAgent}\n      </div>\n    );\n  }\n}\n```\n\nExample for stateless component:\n```jsx\nconst Component = (props) => (\n  <div>\n    Hello World {props.userAgent}\n  </div>\n);\n\nComponent.getInitialprops = async (ctx) => {\n  const { req } = ctx;\n\n  const userAgent = req ? req.headers['user-agent'] : navigator.userAgent;\n\n  return { userAgent };\n};\n\nexport default Component;\n```\n\n`getInitialProps` receives a context object with the following properties:\n- `req` - HTTP request object (server only)\n- `res` - HTTP response object (server only)\n- `...wrapProps` - Spreaded properties from custom Wrap\n- `...appProps` - Spreaded properties from custom App\n- `matched` - Matched routes contains list of object, which is consists of `match` object and `route` object\n- `route` - Component's route attributes on `routes.json` file\n\n## Routing\n\nRouting is declared in a centralized route config.\nRegister all the route config in `routes.json` file.\n\nIf you put the `routes.json` files on root directory, Maleo will automatically register your route. Otherwise put path to your routes on [Maleo config](#custom-configuration).\n\nRoutes file has to export default the route configuration.\nThe route object **expected to have distinct key** to indicate the route.\n\n<table>\n  <tr>\n    <td>Key</td>\n    <td>Type</td>\n    <td>Description</td>\n  </tr>\n  <tr>\n    <td><code>path</code></td>\n    <td><code>String!</code></td>\n    <td>Routes path</td>\n  </tr>\n  <tr>\n    <td><code>page</code></td>\n    <td><code>String!</code></td>\n    <td>Path to React Component for this route path</td>\n  </tr>\n  <tr>\n    <td><code>exact</code></td>\n    <td><code>Boolean?</code> [<code>false</code>]</td>\n    <td>To make url has to match exactly the path in order to render the component. Give <code>false</code> value if the route is a wrapper route component</td>\n  </tr>\n  <tr>\n    <td><code>routes</code></td>\n    <td><code>RouteObject?</code></td>\n    <td>Nested route</td>\n  </tr>\n</table>\n\n\nFor example:\n```json\n[\n  {\n    \"page\": \"./src/MainApp\",\n    \"routes\": [\n      {\n        \"path\": \"/\",\n        \"page\": \"./src/Search\",\n        \"exact\": true\n      },\n      {\n        \"path\": \"/search\",\n        \"page\": \"./src/Search\",\n        \"routes\": [\n          {\n            \"path\": \"/search/hello\",\n            \"page\": \"./src/Detail\",\n            \"exact\": true\n          }\n        ]\n      },\n      {\n        \"path\": \"/detail\",\n        \"page\": \"./src/Detail\",\n        \"exact\": true\n      }\n    ]\n  }\n]\n```\n\n## Dynamic Import Component\nMaleo.js supports TC39 [dynamic import proposal](https://github.com/tc39/proposal-dynamic-import) for JavaScript.\n\nYou can think dynamic import as another way to split your code into manageable chunks. You can use our `Dynamic` function which utilizes [react loadable](https://github.com/jamiebuilds/react-loadable)\n\nFor Example\n```js\n// DynamicComponent.js\nimport Dynamic from '@airy/maleo/dynamic';\n\nexport default Dynamic({\n  loader: () => import( /* webpackChunkName:\"DynamicComponent\" */ './component/DynamicLoad'),\n})\n```\n\n### **Preloading**\n\nFor optimization purposes, you can also preload a component even before the component got rendered.\n\nFor example, if you want to load component when a button get pressed, you can start preloading the component when the user hovers the mouse over the button.\n\nThe component created by `Dynamic` exposes a [static `preload` method](https://github.com/jamiebuilds/react-loadable#loadablecomponentpreload).\n\n```jsx\nimport React from 'react';\nimport Dynamic from '@airy/maleo/dynamic';\n\nconst DynamicBar = Dynamic({\n  loader: () => import('./Bar'),\n  loading: LoadingCompoinent\n});\n\nclass MyComponent extends React.Component {\n  state = { showBar: false };\n\n  onClick = () => {\n    this.setState({ showBar: true });\n  };\n\n  onMouseOver = () => DynamicBar.preload();\n\n  render() {\n    return (\n      <div>\n        <button\n          onClick={this.onClick}\n          onMouseOver={this.onMouseOver}>\n          Show Bar\n        </button>\n        { this.state.showBar && <DynamicBar /> }\n      </div>\n    )\n  }\n}\n```\n\n## Customizable Component\n\n### Custom Document\n\nHighly inspired by what [Next.js](https://github.com/zeit/next.js) has done on their awesome template customization.\n\nMaleo.js also enable customization on `Document` as document's markup. So you don't need to include tags like `<html>`, `<head>`, `<body>`, etc. \n\nTo override the default behavior, you'll need to create a component that extends the `Document` React class provided by Maleo.\n\n```jsx\n// document.jsx\nimport React from 'react';\nimport { Document, Header, Main, Scripts } from '@airy/maleo/document';\n\nexport default class extends Document {\n  render() {\n    return (\n      <html>\n        <Header>\n          <title>Maleo JS</title>\n          <meta charset=\"utf-8\" />\n          <meta name=\"description\" content=\"Maleo.js is awesome!\" />\n\n          <style>\n            {` body { background-color: #fff } `}\n          </style>\n        </Header>\n\n        <body>\n          <Main />\n\n          <Scripts />\n\n          <ReduxScript />\n        </body>\n      </html>\n    );\n  }\n}\n```\n\n### Custom Wrap\n\nMaleo.js uses the `Wrap` component to initialize pages. `Wrap` contains React Router's Component. You can add HoC here to wrap the application and control the page initialization. Which allows you to do amazing things like:\n- Persisting layour between page changes\n- Keeping state when navigating pages\n- Custom error handling using `componentDidCatch`\n- Inject additional data into pages (like Redux provider, etc)\n\nTo override the default behavior, you'll need to create a component that extends the `Wrap` React class provided by Maleo.\n\n```jsx\n// wrap.jsx\nimport React from 'react';\nimport { Wrap } from '@airy/maleo/wrap';\n\n// Redux plugin for Maleo.js\n// Hoc that creates store and add Redux Provider\nimport { withRedux } from '@airy/maleo-redux-plugin';\n\n// Custom Wrapper that will be rendered for the whole Application\nimport Content from './component/Content';\nimport NavigationBar from './component/NavigationBar';\n\nimport { createStore } from './store';\n\n@withRedux(createStore)\nexport default class extends Wrap {\n  static getInitialProps = (ctx) => {\n    const { store } = ctx\n    // you receive store from context\n    // you can access or do something with the store here\n    console.log('Initialized Redux Store', store);\n    return {}\n  }\n\n  render() {\n    const { Container, containerProps, App, appProps } = this.props\n\n    return (\n      <Container {...containerProps}>\n        <NavigationBar />\n        <Content>\n          <App {...appProps}/>\n        </Content>\n      </Container>\n    )\n  }\n}\n\n```\nIf you put `document.jsx` and `wrap.jsx` on root directory (the same level with `package.json`), then Maleo will automatically register your custom Document and Wrap. Otherwise, you can add the path to your custom Document and Wrap on [Maleo config](#custom-configuration)\n\n---\n***We are also working on adding default and customizable `Error` component page***\n\n---\n\n## Custom Configuration\n\nFor more advanced configuration of Maleo.js, like `webpack` config, registering `plugins`, path to your routes, custom Document and Wrap, and adding `path alias`, you can create a `maleo.config.js` in the root of your project directory. (same directory with `package.json`)\n\n```js\n// maleo.config.js\n\nmodule.exports = {\n  /* config options here */\n}\n```\n\nHere are the API's for the configuration:\n\n<table>\n  <tr>\n    <td>Key</td>\n    <td>Type</td>\n    <td>Description</td>\n  </tr>\n  <tr>\n    <td><code>favicon</code></td>\n    <td><code>String</code> [<code>{project-dir}/favicon.ico</code>]</td>\n    <td>Path to favicon file</td>\n  </tr>\n  <tr>\n    <td><code>buildDir</code></td>\n    <td><code>String?</code> [<code>.maleo</code>]</td>\n    <td>Directory to put Maleo.js' build assets</td>\n  </tr>\n  <tr>\n    <td><code>cache</code></td>\n    <td><code>Boolean?</code> [<code>true</code>]</td>\n    <td>Enable webpack build caching</td>\n  </tr>\n  <tr>\n    <td><code>isDev</code></td>\n    <td><code>Boolean?</code> [<code>process.env.NODE_ENV === 'development'</code>]</td>\n    <td>Enable development build configuration</td>\n  </tr>\n  <tr>\n    <td><code>sourceMaps</code></td>\n    <td><code>Boolean?</code> [<code>true</code>]</td>\n    <td>Enable webpack to generate source maps</td>\n  </tr>\n  <tr>\n    <td><code>alias</code></td>\n    <td><code>Object?</code></td>\n    <td>A key value pair for aliasing path directory\n      <br>\n      <code>\n      { 'component': './src/component' }\n      </code>\n    </td>\n  </tr>\n  <tr>\n    <td><code>publicPath</code></td>\n    <td><code>String?</code> [<code>/_assets/</code>]</td>\n    <td>To customize webpack's <code>publicPath</code> \n    <br>Comes in handy if using CDN to put built assets</td>\n  </tr>\n  <tr>\n    <td><code>analyzeBundle</code></td>\n    <td><code>Boolean?</code> [<code>false</code>]</td>\n    <td>To enable webpack's bundle analyzer, for analyzing bundle sizes during bundle debugging should Maleo.js' build process got slow</td>\n  </tr>\n  <tr>\n    <td><code>webpack</code></td>\n    <td><code>Function?</code></td>\n    <td>To customize webpack configuration, more details <a href=\"#customize-webpack\">here</a></td>\n  </tr>\n  <tr>\n    <td><code>routes</code></td>\n    <td><code>string?</code> [<code>rootDir/routes.jsx</code>]</td>\n    <td>Path to your routes file</td>\n  </tr>\n  <tr>\n    <td><code>customDocument</code></td>\n    <td><code>string?</code> [<code>rootDir/document.jsx</code>]</td>\n    <td>Path to your custom document file</td>\n  </tr>\n  <tr>\n    <td><code>customWrap</code></td>\n    <td><code>string?</code> [<code>rootDir/wrap.jsx</code>]</td>\n    <td>Path to your custom wrap file</td>\n  </tr>\n  <tr>\n    <td><code>customApp</code></td>\n    <td><code>string?</code> [<code>rootDir/app.jsx</code>]</td>\n    <td>Path to your custom app file</td>\n  </tr>\n  <tr>\n    <td><code>csp</code></td>\n    <td><code>boolean | cspConfig</code> [<code>false</code>]</td>\n    <td>Config for <a href=\"https://developers.google.com/web/fundamentals/security/csp/\">Content Security Policy</a>, using <a href=\"https://helmetjs.github.io/docs/csp/\">Helmet-CSP</a>\n    <br/>\n    If set to true it will enable default config:\n    <br/>\n    <code>{\n      directives: {\n        defaultSrc: [`'self'`],\n        styleSrc: [`'self'`],\n      },\n    }</code>\n    </td>\n  </tr>\n</table>\n\n#### Customize Server\n\nCreate a `server.js` file on root directory where your `package.json` lives.\nHere you can customize Maleo's server.\n```js\nimport { Server } from '@airy/maleo/server';\n\nconst PORT = process.env.PORT || 3000;\n\nconst maleoServer = Server.init({\n  port: PORT,\n  runHandler: () => {\n    console.log('Server running on port :', PORT);\n  }\n});\n\nmaleoServer.run();\n```\n\nHere are the API's for the configuration:\n\n<table>\n  <tr>\n    <td>Key</td>\n    <td>Type</td>\n    <td>Description</td>\n  </tr>\n  <tr>\n    <td><code>port</code></td>\n    <td><code>Number?</code> [<code>3000</code>]</td>\n    <td>Port to run Maleo server</td>\n  </tr>\n  <tr>\n    <td><code>assetDir</code></td>\n    <td><code>String?</code> [<code>'<root>/.maleo/client'</code>]</td>\n    <td>Directory for all client related assets</td>\n  </tr>\n  <tr>\n    <td><code>runHandler</code></td>\n    <td><code>Function?</code></td>\n    <td>Function called when maleo server starter</td>\n  </tr>\n</table>\n\n#### Customize Webpack\n\nYou are able to extend Maleo.js' default webpack configuration by defining a function on `maleo.config.js`\n\n```js\n// maleo.config.js\n\nmodule.exports = {\n  webpack(config, context, next) {\n    // Perform customizations to webpack config\n    // Important: This call is required in order to let Maleo pass the config to webpack\n    return next(); \n  },\n};\n```\n\nWebpack function will receive three arguments:\n\n<table>\n  <tr>\n    <td>Argument</td>\n    <td>Details</td>\n  <tr>\n  <tr>\n    <td><code>config</code></td>\n    <td>This contains webpack configuration object that you can manipulate</td>\n  <tr>\n  <tr>\n    <td><code>context</code></td>\n    <td>This contains some keys that are useful for the build context\n      <br>\n      <table>\n        <tr>\n          <td><code>isDev</code></td>\n          <td><code>Boolean</code></td>\n          <td>Check if current build is for development</td>\n        </tr>\n        <tr>\n          <td><code>publicPath</code></td>\n          <td><code>String</code></td>\n          <td>Public Path of user defined or default's value</td>\n        </tr>\n        <tr>\n          <td><code>analyzeBundle</code></td>\n          <td><code>Boolean</code></td>\n          <td>Check if analyze bundle is enabled</td>\n        </tr>\n        <tr>\n          <td><code>buildDirectory</code></td>\n          <td><code>String</code></td>\n          <td>Build Directory of user defined or default's value</td>\n        </tr>\n        <tr>\n          <td><code>name</code></td>\n          <td><code>String</code></td>\n          <td>Build name <code>'server' || 'client'</code></td>\n        </tr>\n      </table>\n    </td>\n  <tr>\n  <tr>\n    <td><code>next</code></td>\n    <td>A callback required to be called to pass the custom configuration</td>\n  <tr>\n</table>\n\nExample of adding `ts-loader` through `maleo.config.js`:\n\n```js\n// maleo.config.js\n\n// Partly taken and modified from @airy/maleo-ts-plugin source\n// for simplicity purposes\n\nmodule.exports = {\n  webpack(config, context, next) {\n    const { isDev } = context\n\n    config.module.rules.push({\n      test: /\\.tsx?/,\n      exclude: /node_modules/,\n      use: [\n        require.resolve('@airy/maleo/lib/build/loaders/maleo-babel-loader'),\n        {\n          loader: 'ts-loader',\n          options: {\n            transpileOnly: true,\n          },\n        },\n      ],\n\n      if (isDev) {\n        config.plugins.push(new ForkTSCheckerWebpackPlugin());\n      }\n\n      return next();\n    })\n  },\n};\n```\n\n#### Customize Babel Config\n\nMaleo.js also let you have your own babel config. Just simply add `.babelrc` file at the root directory of your app.\n\nYou can include Maleo.js' babel preset in order to have latest JavaScript preset.\n\nHere's an example of `.babelrc` file:\n```json\n{\n  \"presets\": [\"@airy/maleo/babel\"],\n  \"plugins\": []\n}\n```\n\nThe `@airy/maleo/babel` preset includes everything you need to get your development started. The preset includes:\n- `@babel/preset-env`\n- `@babel/preset-react`\n- `@babel/plugin-proposal-class-properties`\n- `@babel/plugin-proposal-decorators`\n- `@babel/plugin-proposal-object-rest-spread`\n- `@babel/plugin-transform-runtime`\n- `react-loadable/babel`\n\n## Core Utilities Functions\n\n#### Route Matching\n\nTo use you can import the matching routes function like this:\n```js\nimport { getMatchedRoutes } from '@airy/maleo/utils';\n```\n\nThe function will return all matched routes and the route object from `routes.json`\n\n## CDN Support\n\nIf you are using a CDN, you can set up the `publicPath` setting and configure your CDN's origin to resolve to the domain that Maleo.js is hosted on.\n\n```js\n// maleo.config.js\n\nconst isProd = process.env.NODE_ENV === 'production';\n\nmodule.exports = {\n  assetPrefix: isProd && 'https://cdn.example.com';\n}\n```\n\n## FAQ\n\n== TO BE DETERMINED == \n\n## Plugins\n- [css-plugin](https://github.com/airyrooms/maleo-plugins/tree/master/packages/css-plugin)\n- [redux-plugin](https://github.com/airyrooms/maleo-plugins/tree/master/packages/redux-plugin)\n- [typescript-plugin](https://github.com/airyrooms/maleo-plugins/tree/master/packages/typescript-plugin)\n- [more](https://github.com/airyrooms/maleo-plugins)\n\n\n## Contributing\n[Please follow these steps to contribute to Maleo.js](https://github.com/airyrooms/maleo.js/blob/canary/CONTRIBUTING.md)\n\n[Please follow these steps to contribute to Maleo.js' plugins](https://github.com/airyrooms/maleo-plugins#contributing-guidelines)\n\n\n## Contributors\n\nThis project exists thanks to all the people who contribute. [Contribute](CONTRIBUTING.md).\n\nMany thanks to our **[contributors](https://github.com/airyrooms/maleo.js/graphs/contributors)**!\n\n\n## License\n\nMIT","readmeFilename":"README.md","bugs":{"url":"https://github.com/airyrooms/maleo.js/issues"},"homepage":"https://github.com/airyrooms/maleo.js#readme","_id":"@airy/maleo@0.3.1-canary.33","_nodeVersion":"10.16.3","_npmVersion":"lerna/3.13.1/node@v10.16.3+x64 (linux)","dist":{"integrity":"sha512-orMnfCV+rR6ghDSS7UxgcTdqQYlcrlBvZ+IqM9ye8CABlyGzq3+Xv1JcfUeHsDigm6vEMeyYl+vzW67hsvk7BA==","shasum":"c79ca80971c3b58f42bad3527aa8a5183a165802","tarball":"https://registry.npmjs.org/@airy/maleo/-/maleo-0.3.1-canary.33.tgz","fileCount":107,"unpackedSize":400236,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJdlXTXCRA9TVsSAnZWagAAX2QP/33LF9/jXMK+EcEEw8vn\nA+aYu2axO90wN7eTcB/c3FDEXE3KVK8RicbAS2RTlWLTzhLf9n5IODpXLqN6\nAqZgo72r9U3qWDuHDXWJpPzMG8I/BuzvSSJtZu1oTwjWUlKtDFKbF5jWa/bm\nSSZ9LEIUzHlh+b5ZEZv87t7QicTeOPd380STy2UmoF/hzhcmtcH1I0yq20c1\nGPAR8dBsV2uPT7HNvRck979njlohvIxxjhbxyvA8vVuvFsb2MtKgMo5KiMd0\nDCZRu39pkruVzY+3SXk2W5R4xd6935r+ShPXe03VcC4ZPTmkEtf/ur4097b0\nvBOr3/dnpnMJVexN7dhGscfVdiGaqngKO+2QfGjPfjT0iy7Sp80eNm23ueGq\nicZILeRCJHIo4UO3xuKgVtV+jqaw8Pe1nOWTmaNYfxecp/1t+E737aMVDIcG\ngA9OeOqKW8mD82kc3tYfQQh8qmmDnBug5xtXaOnDUGp5flNwQwUDR+5egOEt\nmNexHfEIBhpwBFIjtk9+l+F4VpP8Zd1FpCsB64M8PbaYQ08DRV2YjJ9pcn52\nzD/ne/fmGwmFySJ+yOn8NsVzq/3bH3P72N1egS/nxQ2ZKJFmmml/7qlUdgov\n6v/8ndLIPUT6tzGqBbHVmaaAgC/q0SgIZNTVeoVvda1JrgnY2YIdjywMMaPJ\nbalZ\r\n=X4ba\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIGDoNFNfYQPnz4m2oY8emz7FNlTvheVUQ3Dt+huJq5JNAiARplDazUKyUs8b70wZlxCK8IBOk1HKV95YE7v5CPRUcw=="}]},"maintainers":[{"name":"airyrooms-engineering","email":"engineering@airyrooms.com"}],"_npmUser":{"name":"airyrooms-engineering","email":"engineering@airyrooms.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/maleo_0.3.1-canary.33_1570075862681_0.7457616418577464"},"_hasShrinkwrap":false},"0.3.1-canary.34":{"name":"@airy/maleo","version":"0.3.1-canary.34","description":"Un-opinionated Universal Rendering Javascript Framework","repository":{"type":"git","url":"git+https://github.com/airyrooms/maleo.js.git","bugs":"https://github.com/airyrooms/maleo.js/issues"},"author":{"name":"Airy"},"license":"MIT","private":false,"sideEffects":false,"scripts":{"build":"rm -rf dist && gulp","watch":"gulp watch","prepublish":"yarn build","start":"node server.js"},"bin":{"maleo":"lib/bin/maleo.js"},"dependencies":{"@babel/core":"7.6.2","@babel/plugin-proposal-class-properties":"7.5.5","@babel/plugin-proposal-decorators":"7.6.0","@babel/plugin-proposal-object-rest-spread":"7.6.2","@babel/plugin-syntax-dynamic-import":"7.2.0","@babel/plugin-transform-runtime":"7.6.2","@babel/preset-env":"7.6.2","@babel/preset-react":"7.0.0","@babel/runtime":"7.6.2","@babel/runtime-corejs2":"7.6.2","babel-loader":"8.0.4","babel-plugin-transform-react-remove-prop-types":"^0.4.24","case-sensitive-paths-webpack-plugin":"2.2.0","compression":"1.7.4","es6-promise":"4.2.5","eventsource":"1.0.7","express":"4.16.4","figlet":"1.2.1","flatted":"^2.0.1","friendly-errors-webpack-plugin":"1.7.0","hard-source-webpack-plugin":"0.13.1","helmet":"3.14.0","hoist-non-react-statics":"^3.3.0","isomorphic-fetch":"2.2.1","loader-utils":"1.1.0","node-notifier":"5.2.1","nodemon":"1.18.4","react-dom":"16.8.4","react-helmet":"5.2.0","react-loadable":"5.5.0","react-router-config":"5.0.0","react-router-dom":"5.0.0","rimraf":"2.6.3","source-map-support":"0.5.9","terser-webpack-plugin":"1.1.0","webpack":"4.29.0","webpack-bundle-analyzer":"3.0.3","webpack-cli":"3.1.2","webpack-dev-middleware":"3.4.0","webpack-hot-middleware":"2.24.3","webpackbar":"2.6.3"},"peerDependencies":{"react":"^16.8.4"},"devDependencies":{"@babel/preset-typescript":"^7.1.0","@types/webpack-bundle-analyzer":"^2.13.0","babel-plugin-module-resolver":"^3.1.1","gulp":"github:gulpjs/gulp#4.0","gulp-babel":"^8.0.0","gulp-clean":"^0.4.0","gulp-sourcemaps":"^2.6.4","gulp-uglify":"^3.0.1","gulp-watch":"^5.0.1","react":"^16.8.4"},"gitHead":"6067c57f9cdd4f51a74733b9edf55e6651c7f3ee","readme":"[![Build Status](https://travis-ci.org/airyrooms/maleo.js.svg?branch=master)](https://travis-ci.org/airyrooms/maleo.js)\n[![Coverage Status](https://coveralls.io/repos/github/airyrooms/maleo.js/badge.svg?branch=master)](https://coveralls.io/github/airyrooms/maleo.js?branch=master)\n[![npm version](https://badge.fury.io/js/%40airy%2Fmaleo.svg)](https://badge.fury.io/js/%40airy%2Fmaleo)\n[![Issues](http://img.shields.io/github/issues/airyrooms/maleo.js.svg)]( https://github.com/airyrooms/maleo.js/issues)\n[![MIT license](https://img.shields.io/badge/License-MIT-blue.svg)](https://github.com/airyrooms/maleo.js/blob/canary/LICENSE)\n[![Discord Chat](https://img.shields.io/discord/550498214789513218.svg)](https://discord.gg/9eArCQn)\n[![Conventional Commits](https://img.shields.io/badge/Conventional%20Commits-1.0.0-green.svg)](https://conventionalcommits.org)\n\n\n# Welcome to Maleo.js\n\nMaleo.js is an un-opinionated framework to enable Universal Rendering in JavaScript using React with no hassle.\n\nWe are here to solve the time consuming setups Universal Rendering Required.\n\n---\n\nReadme below is the documentation for the `canary` (prerelease) branch. To view the documentation for the latest stable Maleo.js version change branch to `master`\n\n---\n\n# Table of Contents\n- [Features](#features)\n- [Setup](#setup)\n- [Component Lifecycle](#component-lifecycle)\n- [Routing](#routing)\n- [Dynamic Import Component](#dynamic-import-component)\n  - [Tips](#tips)\n  - [Preloading](#preloading)\n- [Customizable Component](#customizable-component)\n  - [Custom Document](#custom-document)\n  - [Custom Wrap](#custom-wrap)\n- [Custom Configuration](#custom-configuration)\n  - [Customize Server](#customize-server)\n  - [Customize Webpack](#customize-webpack)\n  - [Customize Babel Config](#customize-babel-config)\n- [Core Utilities Functions](#core-utilities-functions)\n- [CDN Support](#cdn-support)\n- [Plugins](#plugins)\n- [FAQ](#faq)\n- [Contributing](#contributing)\n\n---\n\n## Features\n- Universal Rendering\n- Plugin based framework\n- Customizable\n\n## Setup\n\nInstall Maleo.js \n\n>*for now change `@airy/maleo` to `@airy/maleo@canary` until we publish the stable version of this package*\n\n\n**NPM**\n```bash\n$ npm install --save @airy/maleo react\n```\n**Yarn**\n```bash\n$ yarn add @airy/maleo react\n```\n\nAdd this script to your `package.json`\n```json\n{\n  \"scripts\": {\n    \"dev\": \"maleo dev\",\n    \"build\": \"export NODE_ENV=production && maleo build\",\n    \"start\": \"export NODE_ENV=production && maleo run\"\n  }\n}\n```\n\nCreate a page Root component\n```jsx\n// ./src/Root.jsx\nimport React from 'react';\n\n// Export default is required for registering page\nexport default class RootComponent extends React.Component {\n  render() {\n    return (\n      <h1>Hello World!</h1>\n    )\n  }\n}\n```\n\nAnd lastly, create a routing file on your project root directory called `routes.json` and register your page component\n```json\n[\n  {\n    \"path\": \"/\",\n    \"page\": \"./src/Root\"\n  }\n]\n```\n\nAfter that you can now run `$ npm run dev` and go to `http://localhost:3000`.\n\nYou should now see your app running on your browser.\n\nBy now you should see \n- Automatic transpilation and bundling (with webpack and babel)\n- ~~Hot code reloading~~ [#17](https://github.com/airyrooms/maleo.js/issues/17)\n- Server rendering\n\nTo see how simple this is, check out the sample app!\n\n## Component Lifecycle \n\nMaleo.js added a new component lifecycle hook called `getInitialProps`, this function is called during Server Side Rendering (SSR) and during route changes on Client Side Rendering (CSR).\n\nThis is useful especially for SEO purposes. \n\nExample for stateful component:\n```jsx\nimport React from 'react';\n\nexport default class extends React.Component {\n  static getInitialProps = async (ctx) => {\n    const { req } = ctx;\n\n    // check if getInitialProps is called on server or on client\n    const userAgent = req ? req.headers['user-agent'] : navigator.userAgent;\n\n    // the return value will be passed as props for this component\n    return { userAgent };\n  }\n\n  render() {\n    return (\n      <div>\n        Hello World {this.props.userAgent}\n      </div>\n    );\n  }\n}\n```\n\nExample for stateless component:\n```jsx\nconst Component = (props) => (\n  <div>\n    Hello World {props.userAgent}\n  </div>\n);\n\nComponent.getInitialprops = async (ctx) => {\n  const { req } = ctx;\n\n  const userAgent = req ? req.headers['user-agent'] : navigator.userAgent;\n\n  return { userAgent };\n};\n\nexport default Component;\n```\n\n`getInitialProps` receives a context object with the following properties:\n- `req` - HTTP request object (server only)\n- `res` - HTTP response object (server only)\n- `...wrapProps` - Spreaded properties from custom Wrap\n- `...appProps` - Spreaded properties from custom App\n- `matched` - Matched routes contains list of object, which is consists of `match` object and `route` object\n- `route` - Component's route attributes on `routes.json` file\n\n## Routing\n\nRouting is declared in a centralized route config.\nRegister all the route config in `routes.json` file.\n\nIf you put the `routes.json` files on root directory, Maleo will automatically register your route. Otherwise put path to your routes on [Maleo config](#custom-configuration).\n\nRoutes file has to export default the route configuration.\nThe route object **expected to have distinct key** to indicate the route.\n\n<table>\n  <tr>\n    <td>Key</td>\n    <td>Type</td>\n    <td>Description</td>\n  </tr>\n  <tr>\n    <td><code>path</code></td>\n    <td><code>String!</code></td>\n    <td>Routes path</td>\n  </tr>\n  <tr>\n    <td><code>page</code></td>\n    <td><code>String!</code></td>\n    <td>Path to React Component for this route path</td>\n  </tr>\n  <tr>\n    <td><code>exact</code></td>\n    <td><code>Boolean?</code> [<code>false</code>]</td>\n    <td>To make url has to match exactly the path in order to render the component. Give <code>false</code> value if the route is a wrapper route component</td>\n  </tr>\n  <tr>\n    <td><code>routes</code></td>\n    <td><code>RouteObject?</code></td>\n    <td>Nested route</td>\n  </tr>\n</table>\n\n\nFor example:\n```json\n[\n  {\n    \"page\": \"./src/MainApp\",\n    \"routes\": [\n      {\n        \"path\": \"/\",\n        \"page\": \"./src/Search\",\n        \"exact\": true\n      },\n      {\n        \"path\": \"/search\",\n        \"page\": \"./src/Search\",\n        \"routes\": [\n          {\n            \"path\": \"/search/hello\",\n            \"page\": \"./src/Detail\",\n            \"exact\": true\n          }\n        ]\n      },\n      {\n        \"path\": \"/detail\",\n        \"page\": \"./src/Detail\",\n        \"exact\": true\n      }\n    ]\n  }\n]\n```\n\n## Dynamic Import Component\nMaleo.js supports TC39 [dynamic import proposal](https://github.com/tc39/proposal-dynamic-import) for JavaScript.\n\nYou can think dynamic import as another way to split your code into manageable chunks. You can use our `Dynamic` function which utilizes [react loadable](https://github.com/jamiebuilds/react-loadable)\n\nFor Example\n```js\n// DynamicComponent.js\nimport Dynamic from '@airy/maleo/dynamic';\n\nexport default Dynamic({\n  loader: () => import( /* webpackChunkName:\"DynamicComponent\" */ './component/DynamicLoad'),\n})\n```\n\n### **Preloading**\n\nFor optimization purposes, you can also preload a component even before the component got rendered.\n\nFor example, if you want to load component when a button get pressed, you can start preloading the component when the user hovers the mouse over the button.\n\nThe component created by `Dynamic` exposes a [static `preload` method](https://github.com/jamiebuilds/react-loadable#loadablecomponentpreload).\n\n```jsx\nimport React from 'react';\nimport Dynamic from '@airy/maleo/dynamic';\n\nconst DynamicBar = Dynamic({\n  loader: () => import('./Bar'),\n  loading: LoadingCompoinent\n});\n\nclass MyComponent extends React.Component {\n  state = { showBar: false };\n\n  onClick = () => {\n    this.setState({ showBar: true });\n  };\n\n  onMouseOver = () => DynamicBar.preload();\n\n  render() {\n    return (\n      <div>\n        <button\n          onClick={this.onClick}\n          onMouseOver={this.onMouseOver}>\n          Show Bar\n        </button>\n        { this.state.showBar && <DynamicBar /> }\n      </div>\n    )\n  }\n}\n```\n\n## Customizable Component\n\n### Custom Document\n\nHighly inspired by what [Next.js](https://github.com/zeit/next.js) has done on their awesome template customization.\n\nMaleo.js also enable customization on `Document` as document's markup. So you don't need to include tags like `<html>`, `<head>`, `<body>`, etc. \n\nTo override the default behavior, you'll need to create a component that extends the `Document` React class provided by Maleo.\n\n```jsx\n// document.jsx\nimport React from 'react';\nimport { Document, Header, Main, Scripts } from '@airy/maleo/document';\n\nexport default class extends Document {\n  render() {\n    return (\n      <html>\n        <Header>\n          <title>Maleo JS</title>\n          <meta charset=\"utf-8\" />\n          <meta name=\"description\" content=\"Maleo.js is awesome!\" />\n\n          <style>\n            {` body { background-color: #fff } `}\n          </style>\n        </Header>\n\n        <body>\n          <Main />\n\n          <Scripts />\n\n          <ReduxScript />\n        </body>\n      </html>\n    );\n  }\n}\n```\n\n### Custom Wrap\n\nMaleo.js uses the `Wrap` component to initialize pages. `Wrap` contains React Router's Component. You can add HoC here to wrap the application and control the page initialization. Which allows you to do amazing things like:\n- Persisting layour between page changes\n- Keeping state when navigating pages\n- Custom error handling using `componentDidCatch`\n- Inject additional data into pages (like Redux provider, etc)\n\nTo override the default behavior, you'll need to create a component that extends the `Wrap` React class provided by Maleo.\n\n```jsx\n// wrap.jsx\nimport React from 'react';\nimport { Wrap } from '@airy/maleo/wrap';\n\n// Redux plugin for Maleo.js\n// Hoc that creates store and add Redux Provider\nimport { withRedux } from '@airy/maleo-redux-plugin';\n\n// Custom Wrapper that will be rendered for the whole Application\nimport Content from './component/Content';\nimport NavigationBar from './component/NavigationBar';\n\nimport { createStore } from './store';\n\n@withRedux(createStore)\nexport default class extends Wrap {\n  static getInitialProps = (ctx) => {\n    const { store } = ctx\n    // you receive store from context\n    // you can access or do something with the store here\n    console.log('Initialized Redux Store', store);\n    return {}\n  }\n\n  render() {\n    const { Container, containerProps, App, appProps } = this.props\n\n    return (\n      <Container {...containerProps}>\n        <NavigationBar />\n        <Content>\n          <App {...appProps}/>\n        </Content>\n      </Container>\n    )\n  }\n}\n\n```\nIf you put `document.jsx` and `wrap.jsx` on root directory (the same level with `package.json`), then Maleo will automatically register your custom Document and Wrap. Otherwise, you can add the path to your custom Document and Wrap on [Maleo config](#custom-configuration)\n\n---\n***We are also working on adding default and customizable `Error` component page***\n\n---\n\n## Custom Configuration\n\nFor more advanced configuration of Maleo.js, like `webpack` config, registering `plugins`, path to your routes, custom Document and Wrap, and adding `path alias`, you can create a `maleo.config.js` in the root of your project directory. (same directory with `package.json`)\n\n```js\n// maleo.config.js\n\nmodule.exports = {\n  /* config options here */\n}\n```\n\nHere are the API's for the configuration:\n\n<table>\n  <tr>\n    <td>Key</td>\n    <td>Type</td>\n    <td>Description</td>\n  </tr>\n  <tr>\n    <td><code>favicon</code></td>\n    <td><code>String</code> [<code>{project-dir}/favicon.ico</code>]</td>\n    <td>Path to favicon file</td>\n  </tr>\n  <tr>\n    <td><code>buildDir</code></td>\n    <td><code>String?</code> [<code>.maleo</code>]</td>\n    <td>Directory to put Maleo.js' build assets</td>\n  </tr>\n  <tr>\n    <td><code>cache</code></td>\n    <td><code>Boolean?</code> [<code>true</code>]</td>\n    <td>Enable webpack build caching</td>\n  </tr>\n  <tr>\n    <td><code>isDev</code></td>\n    <td><code>Boolean?</code> [<code>process.env.NODE_ENV === 'development'</code>]</td>\n    <td>Enable development build configuration</td>\n  </tr>\n  <tr>\n    <td><code>sourceMaps</code></td>\n    <td><code>Boolean?</code> [<code>true</code>]</td>\n    <td>Enable webpack to generate source maps</td>\n  </tr>\n  <tr>\n    <td><code>alias</code></td>\n    <td><code>Object?</code></td>\n    <td>A key value pair for aliasing path directory\n      <br>\n      <code>\n      { 'component': './src/component' }\n      </code>\n    </td>\n  </tr>\n  <tr>\n    <td><code>publicPath</code></td>\n    <td><code>String?</code> [<code>/_assets/</code>]</td>\n    <td>To customize webpack's <code>publicPath</code> \n    <br>Comes in handy if using CDN to put built assets</td>\n  </tr>\n  <tr>\n    <td><code>analyzeBundle</code></td>\n    <td><code>Boolean?</code> [<code>false</code>]</td>\n    <td>To enable webpack's bundle analyzer, for analyzing bundle sizes during bundle debugging should Maleo.js' build process got slow</td>\n  </tr>\n  <tr>\n    <td><code>webpack</code></td>\n    <td><code>Function?</code></td>\n    <td>To customize webpack configuration, more details <a href=\"#customize-webpack\">here</a></td>\n  </tr>\n  <tr>\n    <td><code>routes</code></td>\n    <td><code>string?</code> [<code>rootDir/routes.jsx</code>]</td>\n    <td>Path to your routes file</td>\n  </tr>\n  <tr>\n    <td><code>customDocument</code></td>\n    <td><code>string?</code> [<code>rootDir/document.jsx</code>]</td>\n    <td>Path to your custom document file</td>\n  </tr>\n  <tr>\n    <td><code>customWrap</code></td>\n    <td><code>string?</code> [<code>rootDir/wrap.jsx</code>]</td>\n    <td>Path to your custom wrap file</td>\n  </tr>\n  <tr>\n    <td><code>customApp</code></td>\n    <td><code>string?</code> [<code>rootDir/app.jsx</code>]</td>\n    <td>Path to your custom app file</td>\n  </tr>\n  <tr>\n    <td><code>csp</code></td>\n    <td><code>boolean | cspConfig</code> [<code>false</code>]</td>\n    <td>Config for <a href=\"https://developers.google.com/web/fundamentals/security/csp/\">Content Security Policy</a>, using <a href=\"https://helmetjs.github.io/docs/csp/\">Helmet-CSP</a>\n    <br/>\n    If set to true it will enable default config:\n    <br/>\n    <code>{\n      directives: {\n        defaultSrc: [`'self'`],\n        styleSrc: [`'self'`],\n      },\n    }</code>\n    </td>\n  </tr>\n</table>\n\n#### Customize Server\n\nCreate a `server.js` file on root directory where your `package.json` lives.\nHere you can customize Maleo's server.\n```js\nimport { Server } from '@airy/maleo/server';\n\nconst PORT = process.env.PORT || 3000;\n\nconst maleoServer = Server.init({\n  port: PORT,\n  runHandler: () => {\n    console.log('Server running on port :', PORT);\n  }\n});\n\nmaleoServer.run();\n```\n\nHere are the API's for the configuration:\n\n<table>\n  <tr>\n    <td>Key</td>\n    <td>Type</td>\n    <td>Description</td>\n  </tr>\n  <tr>\n    <td><code>port</code></td>\n    <td><code>Number?</code> [<code>3000</code>]</td>\n    <td>Port to run Maleo server</td>\n  </tr>\n  <tr>\n    <td><code>assetDir</code></td>\n    <td><code>String?</code> [<code>'<root>/.maleo/client'</code>]</td>\n    <td>Directory for all client related assets</td>\n  </tr>\n  <tr>\n    <td><code>runHandler</code></td>\n    <td><code>Function?</code></td>\n    <td>Function called when maleo server starter</td>\n  </tr>\n</table>\n\n#### Customize Webpack\n\nYou are able to extend Maleo.js' default webpack configuration by defining a function on `maleo.config.js`\n\n```js\n// maleo.config.js\n\nmodule.exports = {\n  webpack(config, context, next) {\n    // Perform customizations to webpack config\n    // Important: This call is required in order to let Maleo pass the config to webpack\n    return next(); \n  },\n};\n```\n\nWebpack function will receive three arguments:\n\n<table>\n  <tr>\n    <td>Argument</td>\n    <td>Details</td>\n  <tr>\n  <tr>\n    <td><code>config</code></td>\n    <td>This contains webpack configuration object that you can manipulate</td>\n  <tr>\n  <tr>\n    <td><code>context</code></td>\n    <td>This contains some keys that are useful for the build context\n      <br>\n      <table>\n        <tr>\n          <td><code>isDev</code></td>\n          <td><code>Boolean</code></td>\n          <td>Check if current build is for development</td>\n        </tr>\n        <tr>\n          <td><code>publicPath</code></td>\n          <td><code>String</code></td>\n          <td>Public Path of user defined or default's value</td>\n        </tr>\n        <tr>\n          <td><code>analyzeBundle</code></td>\n          <td><code>Boolean</code></td>\n          <td>Check if analyze bundle is enabled</td>\n        </tr>\n        <tr>\n          <td><code>buildDirectory</code></td>\n          <td><code>String</code></td>\n          <td>Build Directory of user defined or default's value</td>\n        </tr>\n        <tr>\n          <td><code>name</code></td>\n          <td><code>String</code></td>\n          <td>Build name <code>'server' || 'client'</code></td>\n        </tr>\n      </table>\n    </td>\n  <tr>\n  <tr>\n    <td><code>next</code></td>\n    <td>A callback required to be called to pass the custom configuration</td>\n  <tr>\n</table>\n\nExample of adding `ts-loader` through `maleo.config.js`:\n\n```js\n// maleo.config.js\n\n// Partly taken and modified from @airy/maleo-ts-plugin source\n// for simplicity purposes\n\nmodule.exports = {\n  webpack(config, context, next) {\n    const { isDev } = context\n\n    config.module.rules.push({\n      test: /\\.tsx?/,\n      exclude: /node_modules/,\n      use: [\n        require.resolve('@airy/maleo/lib/build/loaders/maleo-babel-loader'),\n        {\n          loader: 'ts-loader',\n          options: {\n            transpileOnly: true,\n          },\n        },\n      ],\n\n      if (isDev) {\n        config.plugins.push(new ForkTSCheckerWebpackPlugin());\n      }\n\n      return next();\n    })\n  },\n};\n```\n\n#### Customize Babel Config\n\nMaleo.js also let you have your own babel config. Just simply add `.babelrc` file at the root directory of your app.\n\nYou can include Maleo.js' babel preset in order to have latest JavaScript preset.\n\nHere's an example of `.babelrc` file:\n```json\n{\n  \"presets\": [\"@airy/maleo/babel\"],\n  \"plugins\": []\n}\n```\n\nThe `@airy/maleo/babel` preset includes everything you need to get your development started. The preset includes:\n- `@babel/preset-env`\n- `@babel/preset-react`\n- `@babel/plugin-proposal-class-properties`\n- `@babel/plugin-proposal-decorators`\n- `@babel/plugin-proposal-object-rest-spread`\n- `@babel/plugin-transform-runtime`\n- `react-loadable/babel`\n\n## Core Utilities Functions\n\n#### Route Matching\n\nTo use you can import the matching routes function like this:\n```js\nimport { getMatchedRoutes } from '@airy/maleo/utils';\n```\n\nThe function will return all matched routes and the route object from `routes.json`\n\n## CDN Support\n\nIf you are using a CDN, you can set up the `publicPath` setting and configure your CDN's origin to resolve to the domain that Maleo.js is hosted on.\n\n```js\n// maleo.config.js\n\nconst isProd = process.env.NODE_ENV === 'production';\n\nmodule.exports = {\n  assetPrefix: isProd && 'https://cdn.example.com';\n}\n```\n\n## FAQ\n\n== TO BE DETERMINED == \n\n## Plugins\n- [css-plugin](https://github.com/airyrooms/maleo-plugins/tree/master/packages/css-plugin)\n- [redux-plugin](https://github.com/airyrooms/maleo-plugins/tree/master/packages/redux-plugin)\n- [typescript-plugin](https://github.com/airyrooms/maleo-plugins/tree/master/packages/typescript-plugin)\n- [more](https://github.com/airyrooms/maleo-plugins)\n\n\n## Contributing\n[Please follow these steps to contribute to Maleo.js](https://github.com/airyrooms/maleo.js/blob/canary/CONTRIBUTING.md)\n\n[Please follow these steps to contribute to Maleo.js' plugins](https://github.com/airyrooms/maleo-plugins#contributing-guidelines)\n\n\n## Contributors\n\nThis project exists thanks to all the people who contribute. [Contribute](CONTRIBUTING.md).\n\nMany thanks to our **[contributors](https://github.com/airyrooms/maleo.js/graphs/contributors)**!\n\n\n## License\n\nMIT","readmeFilename":"README.md","bugs":{"url":"https://github.com/airyrooms/maleo.js/issues"},"homepage":"https://github.com/airyrooms/maleo.js#readme","_id":"@airy/maleo@0.3.1-canary.34","_nodeVersion":"11.15.0","_npmVersion":"lerna/3.13.1/node@v11.15.0+x64 (linux)","dist":{"integrity":"sha512-jrXYrzzxSbsX92uIz+0c6Z3JnkVCxqHbX51EoMRmXWFid2pnDdQOSezBrBeuv2kkENKw+j738pcY68iMHpP8fQ==","shasum":"64018ccc0497034279486dff0bb5f83fe2caa01a","tarball":"https://registry.npmjs.org/@airy/maleo/-/maleo-0.3.1-canary.34.tgz","fileCount":107,"unpackedSize":400737,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJdlYXzCRA9TVsSAnZWagAACJcQAJSvlo3AJbUxIeavD5y6\n/pxOAFhc5+kScqNH2ilSpW+IVolO0ipzP11xryDXrV+wJ6onJUuIIQ7wxzih\nVDMbfDHS9b39B3pYhrafQwnYhVNlFflHI49rzj130zOgxs/ODNpYzlKvVHWT\nVlU/JyIMCOXTZJSaddabp6T5qCICvbrht8PqYIL2g/GIL95eH4MHJYUoV8N4\nIGA9zzyMi6RKXzldG9jXGQI0Y1cObHmi8Fn3FIEewqAXoa4w59V9mZ8uNstQ\nnaX0fZTAgXv4cNMLcDSjrmXpo/SWTvLjOFlObYzL5g/FHpPYvr2hri6ib+nj\n5YmZ+NUbXKhdldWn24Jpjo1EPsyqDgipEqZ85eEsavn+ouAuVRcsOUio95iz\ns28LcIWw1LJC78vRNA+Mbr3NV0QBDijVY4K7Nxqv2/jNtKhlIVkfxJ45CGPe\neLXApsfAC9YlZIpAha/mReTXMmx3/bHjxA2LFYT05jecm+p0HEZRYmhW0/rm\n8dKS/S5y8UW/CWGVqlvUJJ9+rlYkVZe284UXX9/p7hI3tudevAF5a81Fnbtt\nwp17I0pHVra/qT4r/B/2PxnrgpBXwFnFQqmRG1W90Pxbx5Zk5IAqZwNdgBbW\nRhFJ0HO7seFn+XKiE+YCQiQpLWLwLmEbKk2TBl3hJh4uXXdVRTWGnhGZchw/\nN+VB\r\n=iIDj\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDMstbvvY/RwDiqxgN8pfl6u3pJi5CxwtWI+LZK3JTkwwIhANwnok0IGv+ScJ51a62pZy0FI3EBSmeHTI24CU+lOaXY"}]},"maintainers":[{"name":"airyrooms-engineering","email":"engineering@airyrooms.com"}],"_npmUser":{"name":"airyrooms-engineering","email":"engineering@airyrooms.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/maleo_0.3.1-canary.34_1570080242842_0.6284277557802649"},"_hasShrinkwrap":false},"0.3.1-canary.35":{"name":"@airy/maleo","version":"0.3.1-canary.35","description":"Un-opinionated Universal Rendering Javascript Framework","repository":{"type":"git","url":"git+https://github.com/airyrooms/maleo.js.git","bugs":"https://github.com/airyrooms/maleo.js/issues"},"author":{"name":"Airy"},"license":"MIT","private":false,"sideEffects":false,"scripts":{"build":"rm -rf dist && gulp","watch":"gulp watch","prepublish":"yarn build","start":"node server.js"},"bin":{"maleo":"lib/bin/maleo.js"},"dependencies":{"@babel/core":"7.6.2","@babel/plugin-proposal-class-properties":"7.5.5","@babel/plugin-proposal-decorators":"7.6.0","@babel/plugin-proposal-object-rest-spread":"7.6.2","@babel/plugin-syntax-dynamic-import":"7.2.0","@babel/plugin-transform-runtime":"7.6.2","@babel/preset-env":"7.6.2","@babel/preset-react":"7.0.0","@babel/runtime":"7.6.2","@babel/runtime-corejs2":"7.6.2","babel-loader":"8.0.4","babel-plugin-transform-react-remove-prop-types":"^0.4.24","case-sensitive-paths-webpack-plugin":"2.2.0","compression":"1.7.4","es6-promise":"4.2.5","eventsource":"1.0.7","express":"4.16.4","figlet":"1.2.1","flatted":"^2.0.1","friendly-errors-webpack-plugin":"1.7.0","hard-source-webpack-plugin":"0.13.1","helmet":"3.14.0","hoist-non-react-statics":"^3.3.0","isomorphic-fetch":"2.2.1","loader-utils":"1.1.0","node-notifier":"5.2.1","nodemon":"1.18.4","react-dom":"16.8.4","react-helmet":"5.2.0","react-loadable":"5.5.0","react-router-config":"5.0.0","react-router-dom":"5.0.0","rimraf":"2.6.3","source-map-support":"0.5.9","terser-webpack-plugin":"1.1.0","webpack":"4.29.0","webpack-bundle-analyzer":"3.0.3","webpack-cli":"3.1.2","webpack-dev-middleware":"3.4.0","webpack-hot-middleware":"2.24.3","webpackbar":"2.6.3"},"peerDependencies":{"react":"^16.8.4"},"devDependencies":{"@babel/preset-typescript":"^7.1.0","@types/webpack-bundle-analyzer":"^2.13.0","babel-plugin-module-resolver":"^3.1.1","gulp":"github:gulpjs/gulp#4.0","gulp-babel":"^8.0.0","gulp-clean":"^0.4.0","gulp-sourcemaps":"^2.6.4","gulp-uglify":"^3.0.1","gulp-watch":"^5.0.1","react":"^16.8.4"},"gitHead":"d8073cf99cc32deb96114ea070d1d53586aee9a9","readme":"[![Build Status](https://travis-ci.org/airyrooms/maleo.js.svg?branch=master)](https://travis-ci.org/airyrooms/maleo.js)\n[![Coverage Status](https://coveralls.io/repos/github/airyrooms/maleo.js/badge.svg?branch=master)](https://coveralls.io/github/airyrooms/maleo.js?branch=master)\n[![npm version](https://badge.fury.io/js/%40airy%2Fmaleo.svg)](https://badge.fury.io/js/%40airy%2Fmaleo)\n[![Issues](http://img.shields.io/github/issues/airyrooms/maleo.js.svg)]( https://github.com/airyrooms/maleo.js/issues)\n[![MIT license](https://img.shields.io/badge/License-MIT-blue.svg)](https://github.com/airyrooms/maleo.js/blob/canary/LICENSE)\n[![Discord Chat](https://img.shields.io/discord/550498214789513218.svg)](https://discord.gg/9eArCQn)\n[![Conventional Commits](https://img.shields.io/badge/Conventional%20Commits-1.0.0-green.svg)](https://conventionalcommits.org)\n\n\n# Welcome to Maleo.js\n\nMaleo.js is an un-opinionated framework to enable Universal Rendering in JavaScript using React with no hassle.\n\nWe are here to solve the time consuming setups Universal Rendering Required.\n\n---\n\nReadme below is the documentation for the `canary` (prerelease) branch. To view the documentation for the latest stable Maleo.js version change branch to `master`\n\n---\n\n# Table of Contents\n- [Features](#features)\n- [Setup](#setup)\n- [Component Lifecycle](#component-lifecycle)\n- [Routing](#routing)\n- [Dynamic Import Component](#dynamic-import-component)\n  - [Tips](#tips)\n  - [Preloading](#preloading)\n- [Customizable Component](#customizable-component)\n  - [Custom Document](#custom-document)\n  - [Custom Wrap](#custom-wrap)\n- [Custom Configuration](#custom-configuration)\n  - [Customize Server](#customize-server)\n  - [Customize Webpack](#customize-webpack)\n  - [Customize Babel Config](#customize-babel-config)\n- [Core Utilities Functions](#core-utilities-functions)\n- [CDN Support](#cdn-support)\n- [Plugins](#plugins)\n- [FAQ](#faq)\n- [Contributing](#contributing)\n\n---\n\n## Features\n- Universal Rendering\n- Plugin based framework\n- Customizable\n\n## Setup\n\nInstall Maleo.js \n\n>*for now change `@airy/maleo` to `@airy/maleo@canary` until we publish the stable version of this package*\n\n\n**NPM**\n```bash\n$ npm install --save @airy/maleo react\n```\n**Yarn**\n```bash\n$ yarn add @airy/maleo react\n```\n\nAdd this script to your `package.json`\n```json\n{\n  \"scripts\": {\n    \"dev\": \"maleo dev\",\n    \"build\": \"export NODE_ENV=production && maleo build\",\n    \"start\": \"export NODE_ENV=production && maleo run\"\n  }\n}\n```\n\nCreate a page Root component\n```jsx\n// ./src/Root.jsx\nimport React from 'react';\n\n// Export default is required for registering page\nexport default class RootComponent extends React.Component {\n  render() {\n    return (\n      <h1>Hello World!</h1>\n    )\n  }\n}\n```\n\nAnd lastly, create a routing file on your project root directory called `routes.json` and register your page component\n```json\n[\n  {\n    \"path\": \"/\",\n    \"page\": \"./src/Root\"\n  }\n]\n```\n\nAfter that you can now run `$ npm run dev` and go to `http://localhost:3000`.\n\nYou should now see your app running on your browser.\n\nBy now you should see \n- Automatic transpilation and bundling (with webpack and babel)\n- ~~Hot code reloading~~ [#17](https://github.com/airyrooms/maleo.js/issues/17)\n- Server rendering\n\nTo see how simple this is, check out the sample app!\n\n## Component Lifecycle \n\nMaleo.js added a new component lifecycle hook called `getInitialProps`, this function is called during Server Side Rendering (SSR) and during route changes on Client Side Rendering (CSR).\n\nThis is useful especially for SEO purposes. \n\nExample for stateful component:\n```jsx\nimport React from 'react';\n\nexport default class extends React.Component {\n  static getInitialProps = async (ctx) => {\n    const { req } = ctx;\n\n    // check if getInitialProps is called on server or on client\n    const userAgent = req ? req.headers['user-agent'] : navigator.userAgent;\n\n    // the return value will be passed as props for this component\n    return { userAgent };\n  }\n\n  render() {\n    return (\n      <div>\n        Hello World {this.props.userAgent}\n      </div>\n    );\n  }\n}\n```\n\nExample for stateless component:\n```jsx\nconst Component = (props) => (\n  <div>\n    Hello World {props.userAgent}\n  </div>\n);\n\nComponent.getInitialprops = async (ctx) => {\n  const { req } = ctx;\n\n  const userAgent = req ? req.headers['user-agent'] : navigator.userAgent;\n\n  return { userAgent };\n};\n\nexport default Component;\n```\n\n`getInitialProps` receives a context object with the following properties:\n- `req` - HTTP request object (server only)\n- `res` - HTTP response object (server only)\n- `...wrapProps` - Spreaded properties from custom Wrap\n- `...appProps` - Spreaded properties from custom App\n- `matched` - Matched routes contains list of object, which is consists of `match` object and `route` object\n- `route` - Component's route attributes on `routes.json` file\n\n## Routing\n\nRouting is declared in a centralized route config.\nRegister all the route config in `routes.json` file.\n\nIf you put the `routes.json` files on root directory, Maleo will automatically register your route. Otherwise put path to your routes on [Maleo config](#custom-configuration).\n\nRoutes file has to export default the route configuration.\nThe route object **expected to have distinct key** to indicate the route.\n\n<table>\n  <tr>\n    <td>Key</td>\n    <td>Type</td>\n    <td>Description</td>\n  </tr>\n  <tr>\n    <td><code>path</code></td>\n    <td><code>String!</code></td>\n    <td>Routes path</td>\n  </tr>\n  <tr>\n    <td><code>page</code></td>\n    <td><code>String!</code></td>\n    <td>Path to React Component for this route path</td>\n  </tr>\n  <tr>\n    <td><code>exact</code></td>\n    <td><code>Boolean?</code> [<code>false</code>]</td>\n    <td>To make url has to match exactly the path in order to render the component. Give <code>false</code> value if the route is a wrapper route component</td>\n  </tr>\n  <tr>\n    <td><code>routes</code></td>\n    <td><code>RouteObject?</code></td>\n    <td>Nested route</td>\n  </tr>\n</table>\n\n\nFor example:\n```json\n[\n  {\n    \"page\": \"./src/MainApp\",\n    \"routes\": [\n      {\n        \"path\": \"/\",\n        \"page\": \"./src/Search\",\n        \"exact\": true\n      },\n      {\n        \"path\": \"/search\",\n        \"page\": \"./src/Search\",\n        \"routes\": [\n          {\n            \"path\": \"/search/hello\",\n            \"page\": \"./src/Detail\",\n            \"exact\": true\n          }\n        ]\n      },\n      {\n        \"path\": \"/detail\",\n        \"page\": \"./src/Detail\",\n        \"exact\": true\n      }\n    ]\n  }\n]\n```\n\n## Dynamic Import Component\nMaleo.js supports TC39 [dynamic import proposal](https://github.com/tc39/proposal-dynamic-import) for JavaScript.\n\nYou can think dynamic import as another way to split your code into manageable chunks. You can use our `Dynamic` function which utilizes [react loadable](https://github.com/jamiebuilds/react-loadable)\n\nFor Example\n```js\n// DynamicComponent.js\nimport Dynamic from '@airy/maleo/dynamic';\n\nexport default Dynamic({\n  loader: () => import( /* webpackChunkName:\"DynamicComponent\" */ './component/DynamicLoad'),\n})\n```\n\n### **Preloading**\n\nFor optimization purposes, you can also preload a component even before the component got rendered.\n\nFor example, if you want to load component when a button get pressed, you can start preloading the component when the user hovers the mouse over the button.\n\nThe component created by `Dynamic` exposes a [static `preload` method](https://github.com/jamiebuilds/react-loadable#loadablecomponentpreload).\n\n```jsx\nimport React from 'react';\nimport Dynamic from '@airy/maleo/dynamic';\n\nconst DynamicBar = Dynamic({\n  loader: () => import('./Bar'),\n  loading: LoadingCompoinent\n});\n\nclass MyComponent extends React.Component {\n  state = { showBar: false };\n\n  onClick = () => {\n    this.setState({ showBar: true });\n  };\n\n  onMouseOver = () => DynamicBar.preload();\n\n  render() {\n    return (\n      <div>\n        <button\n          onClick={this.onClick}\n          onMouseOver={this.onMouseOver}>\n          Show Bar\n        </button>\n        { this.state.showBar && <DynamicBar /> }\n      </div>\n    )\n  }\n}\n```\n\n## Customizable Component\n\n### Custom Document\n\nHighly inspired by what [Next.js](https://github.com/zeit/next.js) has done on their awesome template customization.\n\nMaleo.js also enable customization on `Document` as document's markup. So you don't need to include tags like `<html>`, `<head>`, `<body>`, etc. \n\nTo override the default behavior, you'll need to create a component that extends the `Document` React class provided by Maleo.\n\n```jsx\n// document.jsx\nimport React from 'react';\nimport { Document, Header, Main, Scripts } from '@airy/maleo/document';\n\nexport default class extends Document {\n  render() {\n    return (\n      <html>\n        <Header>\n          <title>Maleo JS</title>\n          <meta charset=\"utf-8\" />\n          <meta name=\"description\" content=\"Maleo.js is awesome!\" />\n\n          <style>\n            {` body { background-color: #fff } `}\n          </style>\n        </Header>\n\n        <body>\n          <Main />\n\n          <Scripts />\n\n          <ReduxScript />\n        </body>\n      </html>\n    );\n  }\n}\n```\n\n### Custom Wrap\n\nMaleo.js uses the `Wrap` component to initialize pages. `Wrap` contains React Router's Component. You can add HoC here to wrap the application and control the page initialization. Which allows you to do amazing things like:\n- Persisting layour between page changes\n- Keeping state when navigating pages\n- Custom error handling using `componentDidCatch`\n- Inject additional data into pages (like Redux provider, etc)\n\nTo override the default behavior, you'll need to create a component that extends the `Wrap` React class provided by Maleo.\n\n```jsx\n// wrap.jsx\nimport React from 'react';\nimport { Wrap } from '@airy/maleo/wrap';\n\n// Redux plugin for Maleo.js\n// Hoc that creates store and add Redux Provider\nimport { withRedux } from '@airy/maleo-redux-plugin';\n\n// Custom Wrapper that will be rendered for the whole Application\nimport Content from './component/Content';\nimport NavigationBar from './component/NavigationBar';\n\nimport { createStore } from './store';\n\n@withRedux(createStore)\nexport default class extends Wrap {\n  static getInitialProps = (ctx) => {\n    const { store } = ctx\n    // you receive store from context\n    // you can access or do something with the store here\n    console.log('Initialized Redux Store', store);\n    return {}\n  }\n\n  render() {\n    const { Container, containerProps, App, appProps } = this.props\n\n    return (\n      <Container {...containerProps}>\n        <NavigationBar />\n        <Content>\n          <App {...appProps}/>\n        </Content>\n      </Container>\n    )\n  }\n}\n\n```\nIf you put `document.jsx` and `wrap.jsx` on root directory (the same level with `package.json`), then Maleo will automatically register your custom Document and Wrap. Otherwise, you can add the path to your custom Document and Wrap on [Maleo config](#custom-configuration)\n\n---\n***We are also working on adding default and customizable `Error` component page***\n\n---\n\n## Custom Configuration\n\nFor more advanced configuration of Maleo.js, like `webpack` config, registering `plugins`, path to your routes, custom Document and Wrap, and adding `path alias`, you can create a `maleo.config.js` in the root of your project directory. (same directory with `package.json`)\n\n```js\n// maleo.config.js\n\nmodule.exports = {\n  /* config options here */\n}\n```\n\nHere are the API's for the configuration:\n\n<table>\n  <tr>\n    <td>Key</td>\n    <td>Type</td>\n    <td>Description</td>\n  </tr>\n  <tr>\n    <td><code>favicon</code></td>\n    <td><code>String</code> [<code>{project-dir}/favicon.ico</code>]</td>\n    <td>Path to favicon file</td>\n  </tr>\n  <tr>\n    <td><code>buildDir</code></td>\n    <td><code>String?</code> [<code>.maleo</code>]</td>\n    <td>Directory to put Maleo.js' build assets</td>\n  </tr>\n  <tr>\n    <td><code>cache</code></td>\n    <td><code>Boolean?</code> [<code>true</code>]</td>\n    <td>Enable webpack build caching</td>\n  </tr>\n  <tr>\n    <td><code>isDev</code></td>\n    <td><code>Boolean?</code> [<code>process.env.NODE_ENV === 'development'</code>]</td>\n    <td>Enable development build configuration</td>\n  </tr>\n  <tr>\n    <td><code>sourceMaps</code></td>\n    <td><code>Boolean?</code> [<code>true</code>]</td>\n    <td>Enable webpack to generate source maps</td>\n  </tr>\n  <tr>\n    <td><code>alias</code></td>\n    <td><code>Object?</code></td>\n    <td>A key value pair for aliasing path directory\n      <br>\n      <code>\n      { 'component': './src/component' }\n      </code>\n    </td>\n  </tr>\n  <tr>\n    <td><code>publicPath</code></td>\n    <td><code>String?</code> [<code>/_assets/</code>]</td>\n    <td>To customize webpack's <code>publicPath</code> \n    <br>Comes in handy if using CDN to put built assets</td>\n  </tr>\n  <tr>\n    <td><code>analyzeBundle</code></td>\n    <td><code>Boolean?</code> [<code>false</code>]</td>\n    <td>To enable webpack's bundle analyzer, for analyzing bundle sizes during bundle debugging should Maleo.js' build process got slow</td>\n  </tr>\n  <tr>\n    <td><code>webpack</code></td>\n    <td><code>Function?</code></td>\n    <td>To customize webpack configuration, more details <a href=\"#customize-webpack\">here</a></td>\n  </tr>\n  <tr>\n    <td><code>routes</code></td>\n    <td><code>string?</code> [<code>rootDir/routes.jsx</code>]</td>\n    <td>Path to your routes file</td>\n  </tr>\n  <tr>\n    <td><code>customDocument</code></td>\n    <td><code>string?</code> [<code>rootDir/document.jsx</code>]</td>\n    <td>Path to your custom document file</td>\n  </tr>\n  <tr>\n    <td><code>customWrap</code></td>\n    <td><code>string?</code> [<code>rootDir/wrap.jsx</code>]</td>\n    <td>Path to your custom wrap file</td>\n  </tr>\n  <tr>\n    <td><code>customApp</code></td>\n    <td><code>string?</code> [<code>rootDir/app.jsx</code>]</td>\n    <td>Path to your custom app file</td>\n  </tr>\n  <tr>\n    <td><code>csp</code></td>\n    <td><code>boolean | cspConfig</code> [<code>false</code>]</td>\n    <td>Config for <a href=\"https://developers.google.com/web/fundamentals/security/csp/\">Content Security Policy</a>, using <a href=\"https://helmetjs.github.io/docs/csp/\">Helmet-CSP</a>\n    <br/>\n    If set to true it will enable default config:\n    <br/>\n    <code>{\n      directives: {\n        defaultSrc: [`'self'`],\n        styleSrc: [`'self'`],\n      },\n    }</code>\n    </td>\n  </tr>\n  <tr>\n    <td><code>gzip</code></td>\n    <td><code>boolean?</code> [<code>true</code>]</td>\n    <td>Enable GZIP Compression</td>\n  </tr>\n</table>\n\n#### Customize Server\n\nCreate a `server.js` file on root directory where your `package.json` lives.\nHere you can customize Maleo's server.\n```js\nimport { Server } from '@airy/maleo/server';\n\nconst PORT = process.env.PORT || 3000;\n\nconst maleoServer = Server.init({\n  port: PORT,\n  runHandler: () => {\n    console.log('Server running on port :', PORT);\n  }\n});\n\nmaleoServer.run();\n```\n\nHere are the API's for the configuration:\n\n<table>\n  <tr>\n    <td>Key</td>\n    <td>Type</td>\n    <td>Description</td>\n  </tr>\n  <tr>\n    <td><code>port</code></td>\n    <td><code>Number?</code> [<code>3000</code>]</td>\n    <td>Port to run Maleo server</td>\n  </tr>\n  <tr>\n    <td><code>assetDir</code></td>\n    <td><code>String?</code> [<code>'<root>/.maleo/client'</code>]</td>\n    <td>Directory for all client related assets</td>\n  </tr>\n  <tr>\n    <td><code>runHandler</code></td>\n    <td><code>Function?</code></td>\n    <td>Function called when maleo server starter</td>\n  </tr>\n</table>\n\n#### Customize Webpack\n\nYou are able to extend Maleo.js' default webpack configuration by defining a function on `maleo.config.js`\n\n```js\n// maleo.config.js\n\nmodule.exports = {\n  webpack(config, context, next) {\n    // Perform customizations to webpack config\n    // Important: This call is required in order to let Maleo pass the config to webpack\n    return next(); \n  },\n};\n```\n\nWebpack function will receive three arguments:\n\n<table>\n  <tr>\n    <td>Argument</td>\n    <td>Details</td>\n  <tr>\n  <tr>\n    <td><code>config</code></td>\n    <td>This contains webpack configuration object that you can manipulate</td>\n  <tr>\n  <tr>\n    <td><code>context</code></td>\n    <td>This contains some keys that are useful for the build context\n      <br>\n      <table>\n        <tr>\n          <td><code>isDev</code></td>\n          <td><code>Boolean</code></td>\n          <td>Check if current build is for development</td>\n        </tr>\n        <tr>\n          <td><code>publicPath</code></td>\n          <td><code>String</code></td>\n          <td>Public Path of user defined or default's value</td>\n        </tr>\n        <tr>\n          <td><code>analyzeBundle</code></td>\n          <td><code>Boolean</code></td>\n          <td>Check if analyze bundle is enabled</td>\n        </tr>\n        <tr>\n          <td><code>buildDirectory</code></td>\n          <td><code>String</code></td>\n          <td>Build Directory of user defined or default's value</td>\n        </tr>\n        <tr>\n          <td><code>name</code></td>\n          <td><code>String</code></td>\n          <td>Build name <code>'server' || 'client'</code></td>\n        </tr>\n      </table>\n    </td>\n  <tr>\n  <tr>\n    <td><code>next</code></td>\n    <td>A callback required to be called to pass the custom configuration</td>\n  <tr>\n</table>\n\nExample of adding `ts-loader` through `maleo.config.js`:\n\n```js\n// maleo.config.js\n\n// Partly taken and modified from @airy/maleo-ts-plugin source\n// for simplicity purposes\n\nmodule.exports = {\n  webpack(config, context, next) {\n    const { isDev } = context\n\n    config.module.rules.push({\n      test: /\\.tsx?/,\n      exclude: /node_modules/,\n      use: [\n        require.resolve('@airy/maleo/lib/build/loaders/maleo-babel-loader'),\n        {\n          loader: 'ts-loader',\n          options: {\n            transpileOnly: true,\n          },\n        },\n      ],\n\n      if (isDev) {\n        config.plugins.push(new ForkTSCheckerWebpackPlugin());\n      }\n\n      return next();\n    })\n  },\n};\n```\n\n#### Customize Babel Config\n\nMaleo.js also let you have your own babel config. Just simply add `.babelrc` file at the root directory of your app.\n\nYou can include Maleo.js' babel preset in order to have latest JavaScript preset.\n\nHere's an example of `.babelrc` file:\n```json\n{\n  \"presets\": [\"@airy/maleo/babel\"],\n  \"plugins\": []\n}\n```\n\nThe `@airy/maleo/babel` preset includes everything you need to get your development started. The preset includes:\n- `@babel/preset-env`\n- `@babel/preset-react`\n- `@babel/plugin-proposal-class-properties`\n- `@babel/plugin-proposal-decorators`\n- `@babel/plugin-proposal-object-rest-spread`\n- `@babel/plugin-transform-runtime`\n- `react-loadable/babel`\n\n## Core Utilities Functions\n\n#### Route Matching\n\nTo use you can import the matching routes function like this:\n```js\nimport { getMatchedRoutes } from '@airy/maleo/utils';\n```\n\nThe function will return all matched routes and the route object from `routes.json`\n\n## CDN Support\n\nIf you are using a CDN, you can set up the `publicPath` setting and configure your CDN's origin to resolve to the domain that Maleo.js is hosted on.\n\n```js\n// maleo.config.js\n\nconst isProd = process.env.NODE_ENV === 'production';\n\nmodule.exports = {\n  assetPrefix: isProd && 'https://cdn.example.com';\n}\n```\n\n## FAQ\n\n== TO BE DETERMINED == \n\n## Plugins\n- [css-plugin](https://github.com/airyrooms/maleo-plugins/tree/master/packages/css-plugin)\n- [redux-plugin](https://github.com/airyrooms/maleo-plugins/tree/master/packages/redux-plugin)\n- [typescript-plugin](https://github.com/airyrooms/maleo-plugins/tree/master/packages/typescript-plugin)\n- [more](https://github.com/airyrooms/maleo-plugins)\n\n\n## Contributing\n[Please follow these steps to contribute to Maleo.js](https://github.com/airyrooms/maleo.js/blob/canary/CONTRIBUTING.md)\n\n[Please follow these steps to contribute to Maleo.js' plugins](https://github.com/airyrooms/maleo-plugins#contributing-guidelines)\n\n\n## Contributors\n\nThis project exists thanks to all the people who contribute. [Contribute](CONTRIBUTING.md).\n\nMany thanks to our **[contributors](https://github.com/airyrooms/maleo.js/graphs/contributors)**!\n\n\n## License\n\nMIT","readmeFilename":"README.md","bugs":{"url":"https://github.com/airyrooms/maleo.js/issues"},"homepage":"https://github.com/airyrooms/maleo.js#readme","_id":"@airy/maleo@0.3.1-canary.35","_nodeVersion":"11.15.0","_npmVersion":"lerna/3.13.1/node@v11.15.0+x64 (linux)","dist":{"integrity":"sha512-VpiY3p7tEwATVcJc6eF6mRgWNb531pEd8S3JPaJ/1ya9eXTNjjATTF4+IhfNIzRA9sJBXRowUkx3c0slHx/QyQ==","shasum":"92740db7a741832215e587c64e9aaacd1cc67410","tarball":"https://registry.npmjs.org/@airy/maleo/-/maleo-0.3.1-canary.35.tgz","fileCount":107,"unpackedSize":401471,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJdlYcmCRA9TVsSAnZWagAA5PwP/2o6hZRjQc7/DuycShXn\nmPRcrhtWRpNa2nlR5G80YTZtqkaN+xjNj/uNjNFNDPnhJ9IMpB1aYzzWDle8\nB37WZ5KV4Vy9x92FFKNn3jXFxEHHqDqanRwlJnGueYVXVj6tqNF/qZGAFGyS\nFFcd2WxuIfEgvXThDh7kcOFqral0Tta5SopPb3viZzlA8o6sK9CA+aene5bB\nLbFhj8Hhy3tauSfraE2+s3P7rRXulsTRnMfAFjKLQvIclS85t4PgTwhysvgD\nKFHJ50RSIFJY8tDoeRyZBF3mN6kf+eGX5XydGNA3z0BCD++1cS/h5yCYfVWh\nQLkpRCyordP9saFXfoIc18/SXLSQqmA0Ik7pXCZq6EZ5XC1rP8CbPVdH5LA1\nbhBn3DIBvEyHb3e/OYcNDbRa87KC8FHbP8MhMZJcntyloKbZ+l0sHTIhZVKb\niAe0agjSPiOqC7zl7W68NNMV22nAFHefd0W1wNi9/5Sl+3Fkbwt19ueyUIrH\neSYRbo39SoJ+9Wg59lmjU+oMLGQrLBpTN75Ifkxhjnfx5tNfBigVOGMzzNFA\nb625xS1pyKlrK79LrOqNPwU5KU5MyLhk7/RI8zVylF70gTS3gB86TAx6ynsG\nXZWdaHGooydZK/FXNJHVgfKqOKwhG5GPAowMtEhp8ZOLtSsL4QeZLDb1SaZ5\nGQwU\r\n=Ni1E\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIF4maUBFaTdJm7ZQlSoCkbwY0rT6S3moV3WRe14IALksAiBQNLqYCu7taRyEjbAwdghf7g9iNU9iasqYpRh7AZ4OHA=="}]},"maintainers":[{"name":"airyrooms-engineering","email":"engineering@airyrooms.com"}],"_npmUser":{"name":"airyrooms-engineering","email":"engineering@airyrooms.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/maleo_0.3.1-canary.35_1570080549903_0.8010210994157585"},"_hasShrinkwrap":false},"0.3.1-canary.36":{"name":"@airy/maleo","version":"0.3.1-canary.36","description":"Un-opinionated Universal Rendering Javascript Framework","repository":{"type":"git","url":"git+https://github.com/airyrooms/maleo.js.git","bugs":"https://github.com/airyrooms/maleo.js/issues"},"author":{"name":"Airy"},"license":"MIT","private":false,"sideEffects":false,"scripts":{"build":"rm -rf dist && gulp","watch":"gulp watch","prepublish":"yarn build","start":"node server.js"},"bin":{"maleo":"lib/bin/maleo.js"},"dependencies":{"@babel/core":"7.6.2","@babel/plugin-proposal-class-properties":"7.5.5","@babel/plugin-proposal-decorators":"7.6.0","@babel/plugin-proposal-object-rest-spread":"7.6.2","@babel/plugin-syntax-dynamic-import":"7.2.0","@babel/plugin-transform-runtime":"7.6.2","@babel/preset-env":"7.6.2","@babel/preset-react":"7.0.0","@babel/runtime":"7.6.2","@babel/runtime-corejs2":"7.6.2","babel-loader":"8.0.4","babel-plugin-transform-react-remove-prop-types":"^0.4.24","case-sensitive-paths-webpack-plugin":"2.2.0","compression":"1.7.4","es6-promise":"4.2.5","eventsource":"1.0.7","express":"4.16.4","figlet":"1.2.1","flatted":"^2.0.1","friendly-errors-webpack-plugin":"1.7.0","hard-source-webpack-plugin":"0.13.1","helmet":"3.14.0","hoist-non-react-statics":"^3.3.0","isomorphic-fetch":"2.2.1","loader-utils":"1.1.0","node-notifier":"5.2.1","nodemon":"1.18.4","react-dom":"16.8.4","react-helmet":"5.2.0","react-loadable":"5.5.0","react-router-config":"5.0.0","react-router-dom":"5.0.0","rimraf":"2.6.3","source-map-support":"0.5.9","terser-webpack-plugin":"1.1.0","webpack":"4.29.0","webpack-bundle-analyzer":"3.0.3","webpack-cli":"3.1.2","webpack-dev-middleware":"3.4.0","webpack-hot-middleware":"2.24.3","webpackbar":"2.6.3"},"peerDependencies":{"react":"^16.8.4"},"devDependencies":{"@babel/preset-typescript":"^7.1.0","@types/webpack-bundle-analyzer":"^2.13.0","babel-plugin-module-resolver":"^3.1.1","gulp":"github:gulpjs/gulp#4.0","gulp-babel":"^8.0.0","gulp-clean":"^0.4.0","gulp-sourcemaps":"^2.6.4","gulp-uglify":"^3.0.1","gulp-watch":"^5.0.1","react":"^16.8.4"},"gitHead":"34c9f4fe5dcc592b10b4d2b9aa319a10d738d20d","readme":"[![Build Status](https://travis-ci.org/airyrooms/maleo.js.svg?branch=master)](https://travis-ci.org/airyrooms/maleo.js)\n[![Coverage Status](https://coveralls.io/repos/github/airyrooms/maleo.js/badge.svg?branch=master)](https://coveralls.io/github/airyrooms/maleo.js?branch=master)\n[![npm version](https://badge.fury.io/js/%40airy%2Fmaleo.svg)](https://badge.fury.io/js/%40airy%2Fmaleo)\n[![Issues](http://img.shields.io/github/issues/airyrooms/maleo.js.svg)]( https://github.com/airyrooms/maleo.js/issues)\n[![MIT license](https://img.shields.io/badge/License-MIT-blue.svg)](https://github.com/airyrooms/maleo.js/blob/canary/LICENSE)\n[![Discord Chat](https://img.shields.io/discord/550498214789513218.svg)](https://discord.gg/9eArCQn)\n[![Conventional Commits](https://img.shields.io/badge/Conventional%20Commits-1.0.0-green.svg)](https://conventionalcommits.org)\n\n\n# Welcome to Maleo.js\n\nMaleo.js is an un-opinionated framework to enable Universal Rendering in JavaScript using React with no hassle.\n\nWe are here to solve the time consuming setups Universal Rendering Required.\n\n---\n\nReadme below is the documentation for the `canary` (prerelease) branch. To view the documentation for the latest stable Maleo.js version change branch to `master`\n\n---\n\n# Table of Contents\n- [Features](#features)\n- [Setup](#setup)\n- [Component Lifecycle](#component-lifecycle)\n- [Routing](#routing)\n- [Dynamic Import Component](#dynamic-import-component)\n  - [Tips](#tips)\n  - [Preloading](#preloading)\n- [Customizable Component](#customizable-component)\n  - [Custom Document](#custom-document)\n  - [Custom Wrap](#custom-wrap)\n- [Custom Configuration](#custom-configuration)\n  - [Customize Server](#customize-server)\n  - [Customize Webpack](#customize-webpack)\n  - [Customize Babel Config](#customize-babel-config)\n- [Core Utilities Functions](#core-utilities-functions)\n- [CDN Support](#cdn-support)\n- [Plugins](#plugins)\n- [FAQ](#faq)\n- [Contributing](#contributing)\n\n---\n\n## Features\n- Universal Rendering\n- Plugin based framework\n- Customizable\n\n## Setup\n\nInstall Maleo.js \n\n>*for now change `@airy/maleo` to `@airy/maleo@canary` until we publish the stable version of this package*\n\n\n**NPM**\n```bash\n$ npm install --save @airy/maleo react\n```\n**Yarn**\n```bash\n$ yarn add @airy/maleo react\n```\n\nAdd this script to your `package.json`\n```json\n{\n  \"scripts\": {\n    \"dev\": \"maleo dev\",\n    \"build\": \"export NODE_ENV=production && maleo build\",\n    \"start\": \"export NODE_ENV=production && maleo run\"\n  }\n}\n```\n\nCreate a page Root component\n```jsx\n// ./src/Root.jsx\nimport React from 'react';\n\n// Export default is required for registering page\nexport default class RootComponent extends React.Component {\n  render() {\n    return (\n      <h1>Hello World!</h1>\n    )\n  }\n}\n```\n\nAnd lastly, create a routing file on your project root directory called `routes.json` and register your page component\n```json\n[\n  {\n    \"path\": \"/\",\n    \"page\": \"./src/Root\"\n  }\n]\n```\n\nAfter that you can now run `$ npm run dev` and go to `http://localhost:3000`.\n\nYou should now see your app running on your browser.\n\nBy now you should see \n- Automatic transpilation and bundling (with webpack and babel)\n- ~~Hot code reloading~~ [#17](https://github.com/airyrooms/maleo.js/issues/17)\n- Server rendering\n\nTo see how simple this is, check out the sample app!\n\n## Component Lifecycle \n\nMaleo.js added a new component lifecycle hook called `getInitialProps`, this function is called during Server Side Rendering (SSR) and during route changes on Client Side Rendering (CSR).\n\nThis is useful especially for SEO purposes. \n\nExample for stateful component:\n```jsx\nimport React from 'react';\n\nexport default class extends React.Component {\n  static getInitialProps = async (ctx) => {\n    const { req } = ctx;\n\n    // check if getInitialProps is called on server or on client\n    const userAgent = req ? req.headers['user-agent'] : navigator.userAgent;\n\n    // the return value will be passed as props for this component\n    return { userAgent };\n  }\n\n  render() {\n    return (\n      <div>\n        Hello World {this.props.userAgent}\n      </div>\n    );\n  }\n}\n```\n\nExample for stateless component:\n```jsx\nconst Component = (props) => (\n  <div>\n    Hello World {props.userAgent}\n  </div>\n);\n\nComponent.getInitialprops = async (ctx) => {\n  const { req } = ctx;\n\n  const userAgent = req ? req.headers['user-agent'] : navigator.userAgent;\n\n  return { userAgent };\n};\n\nexport default Component;\n```\n\n`getInitialProps` receives a context object with the following properties:\n- `req` - HTTP request object (server only)\n- `res` - HTTP response object (server only)\n- `...wrapProps` - Spreaded properties from custom Wrap\n- `...appProps` - Spreaded properties from custom App\n- `matched` - Matched routes contains list of object, which is consists of `match` object and `route` object\n- `route` - Component's route attributes on `routes.json` file\n\n## Routing\n\nRouting is declared in a centralized route config.\nRegister all the route config in `routes.json` file.\n\nIf you put the `routes.json` files on root directory, Maleo will automatically register your route. Otherwise put path to your routes on [Maleo config](#custom-configuration).\n\nRoutes file has to export default the route configuration.\nThe route object **expected to have distinct key** to indicate the route.\n\n<table>\n  <tr>\n    <td>Key</td>\n    <td>Type</td>\n    <td>Description</td>\n  </tr>\n  <tr>\n    <td><code>path</code></td>\n    <td><code>String!</code></td>\n    <td>Routes path</td>\n  </tr>\n  <tr>\n    <td><code>page</code></td>\n    <td><code>String!</code></td>\n    <td>Path to React Component for this route path</td>\n  </tr>\n  <tr>\n    <td><code>exact</code></td>\n    <td><code>Boolean?</code> [<code>false</code>]</td>\n    <td>To make url has to match exactly the path in order to render the component. Give <code>false</code> value if the route is a wrapper route component</td>\n  </tr>\n  <tr>\n    <td><code>routes</code></td>\n    <td><code>RouteObject?</code></td>\n    <td>Nested route</td>\n  </tr>\n</table>\n\n\nFor example:\n```json\n[\n  {\n    \"page\": \"./src/MainApp\",\n    \"routes\": [\n      {\n        \"path\": \"/\",\n        \"page\": \"./src/Search\",\n        \"exact\": true\n      },\n      {\n        \"path\": \"/search\",\n        \"page\": \"./src/Search\",\n        \"routes\": [\n          {\n            \"path\": \"/search/hello\",\n            \"page\": \"./src/Detail\",\n            \"exact\": true\n          }\n        ]\n      },\n      {\n        \"path\": \"/detail\",\n        \"page\": \"./src/Detail\",\n        \"exact\": true\n      }\n    ]\n  }\n]\n```\n\n## Dynamic Import Component\nMaleo.js supports TC39 [dynamic import proposal](https://github.com/tc39/proposal-dynamic-import) for JavaScript.\n\nYou can think dynamic import as another way to split your code into manageable chunks. You can use our `Dynamic` function which utilizes [react loadable](https://github.com/jamiebuilds/react-loadable)\n\nFor Example\n```js\n// DynamicComponent.js\nimport Dynamic from '@airy/maleo/dynamic';\n\nexport default Dynamic({\n  loader: () => import( /* webpackChunkName:\"DynamicComponent\" */ './component/DynamicLoad'),\n})\n```\n\n### **Preloading**\n\nFor optimization purposes, you can also preload a component even before the component got rendered.\n\nFor example, if you want to load component when a button get pressed, you can start preloading the component when the user hovers the mouse over the button.\n\nThe component created by `Dynamic` exposes a [static `preload` method](https://github.com/jamiebuilds/react-loadable#loadablecomponentpreload).\n\n```jsx\nimport React from 'react';\nimport Dynamic from '@airy/maleo/dynamic';\n\nconst DynamicBar = Dynamic({\n  loader: () => import('./Bar'),\n  loading: LoadingCompoinent\n});\n\nclass MyComponent extends React.Component {\n  state = { showBar: false };\n\n  onClick = () => {\n    this.setState({ showBar: true });\n  };\n\n  onMouseOver = () => DynamicBar.preload();\n\n  render() {\n    return (\n      <div>\n        <button\n          onClick={this.onClick}\n          onMouseOver={this.onMouseOver}>\n          Show Bar\n        </button>\n        { this.state.showBar && <DynamicBar /> }\n      </div>\n    )\n  }\n}\n```\n\n## Customizable Component\n\n### Custom Document\n\nHighly inspired by what [Next.js](https://github.com/zeit/next.js) has done on their awesome template customization.\n\nMaleo.js also enable customization on `Document` as document's markup. So you don't need to include tags like `<html>`, `<head>`, `<body>`, etc. \n\nTo override the default behavior, you'll need to create a component that extends the `Document` React class provided by Maleo.\n\n```jsx\n// document.jsx\nimport React from 'react';\nimport { Document, Header, Main, Scripts } from '@airy/maleo/document';\n\nexport default class extends Document {\n  render() {\n    return (\n      <html>\n        <Header>\n          <title>Maleo JS</title>\n          <meta charset=\"utf-8\" />\n          <meta name=\"description\" content=\"Maleo.js is awesome!\" />\n\n          <style>\n            {` body { background-color: #fff } `}\n          </style>\n        </Header>\n\n        <body>\n          <Main />\n\n          <Scripts />\n\n          <ReduxScript />\n        </body>\n      </html>\n    );\n  }\n}\n```\n\n### Custom Wrap\n\nMaleo.js uses the `Wrap` component to initialize pages. `Wrap` contains React Router's Component. You can add HoC here to wrap the application and control the page initialization. Which allows you to do amazing things like:\n- Persisting layour between page changes\n- Keeping state when navigating pages\n- Custom error handling using `componentDidCatch`\n- Inject additional data into pages (like Redux provider, etc)\n\nTo override the default behavior, you'll need to create a component that extends the `Wrap` React class provided by Maleo.\n\n```jsx\n// wrap.jsx\nimport React from 'react';\nimport { Wrap } from '@airy/maleo/wrap';\n\n// Redux plugin for Maleo.js\n// Hoc that creates store and add Redux Provider\nimport { withRedux } from '@airy/maleo-redux-plugin';\n\n// Custom Wrapper that will be rendered for the whole Application\nimport Content from './component/Content';\nimport NavigationBar from './component/NavigationBar';\n\nimport { createStore } from './store';\n\n@withRedux(createStore)\nexport default class extends Wrap {\n  static getInitialProps = (ctx) => {\n    const { store } = ctx\n    // you receive store from context\n    // you can access or do something with the store here\n    console.log('Initialized Redux Store', store);\n    return {}\n  }\n\n  render() {\n    const { Container, containerProps, App, appProps } = this.props\n\n    return (\n      <Container {...containerProps}>\n        <NavigationBar />\n        <Content>\n          <App {...appProps}/>\n        </Content>\n      </Container>\n    )\n  }\n}\n\n```\nIf you put `document.jsx` and `wrap.jsx` on root directory (the same level with `package.json`), then Maleo will automatically register your custom Document and Wrap. Otherwise, you can add the path to your custom Document and Wrap on [Maleo config](#custom-configuration)\n\n---\n***We are also working on adding default and customizable `Error` component page***\n\n---\n\n## Custom Configuration\n\nFor more advanced configuration of Maleo.js, like `webpack` config, registering `plugins`, path to your routes, custom Document and Wrap, and adding `path alias`, you can create a `maleo.config.js` in the root of your project directory. (same directory with `package.json`)\n\n```js\n// maleo.config.js\n\nmodule.exports = {\n  /* config options here */\n}\n```\n\nHere are the API's for the configuration:\n\n<table>\n  <tr>\n    <td>Key</td>\n    <td>Type</td>\n    <td>Description</td>\n  </tr>\n  <tr>\n    <td><code>favicon</code></td>\n    <td><code>String</code> [<code>{project-dir}/favicon.ico</code>]</td>\n    <td>Path to favicon file</td>\n  </tr>\n  <tr>\n    <td><code>buildDir</code></td>\n    <td><code>String?</code> [<code>.maleo</code>]</td>\n    <td>Directory to put Maleo.js' build assets</td>\n  </tr>\n  <tr>\n    <td><code>cache</code></td>\n    <td><code>Boolean?</code> [<code>true</code>]</td>\n    <td>Enable webpack build caching</td>\n  </tr>\n  <tr>\n    <td><code>isDev</code></td>\n    <td><code>Boolean?</code> [<code>process.env.NODE_ENV === 'development'</code>]</td>\n    <td>Enable development build configuration</td>\n  </tr>\n  <tr>\n    <td><code>sourceMaps</code></td>\n    <td><code>Boolean?</code> [<code>true</code>]</td>\n    <td>Enable webpack to generate source maps</td>\n  </tr>\n  <tr>\n    <td><code>alias</code></td>\n    <td><code>Object?</code></td>\n    <td>A key value pair for aliasing path directory\n      <br>\n      <code>\n      { 'component': './src/component' }\n      </code>\n    </td>\n  </tr>\n  <tr>\n    <td><code>publicPath</code></td>\n    <td><code>String?</code> [<code>/_assets/</code>]</td>\n    <td>To customize webpack's <code>publicPath</code> \n    <br>Comes in handy if using CDN to put built assets</td>\n  </tr>\n  <tr>\n    <td><code>analyzeBundle</code></td>\n    <td><code>Boolean?</code> [<code>false</code>]</td>\n    <td>To enable webpack's bundle analyzer, for analyzing bundle sizes during bundle debugging should Maleo.js' build process got slow</td>\n  </tr>\n  <tr>\n    <td><code>webpack</code></td>\n    <td><code>Function?</code></td>\n    <td>To customize webpack configuration, more details <a href=\"#customize-webpack\">here</a></td>\n  </tr>\n  <tr>\n    <td><code>routes</code></td>\n    <td><code>string?</code> [<code>rootDir/routes.jsx</code>]</td>\n    <td>Path to your routes file</td>\n  </tr>\n  <tr>\n    <td><code>customDocument</code></td>\n    <td><code>string?</code> [<code>rootDir/document.jsx</code>]</td>\n    <td>Path to your custom document file</td>\n  </tr>\n  <tr>\n    <td><code>customWrap</code></td>\n    <td><code>string?</code> [<code>rootDir/wrap.jsx</code>]</td>\n    <td>Path to your custom wrap file</td>\n  </tr>\n  <tr>\n    <td><code>customApp</code></td>\n    <td><code>string?</code> [<code>rootDir/app.jsx</code>]</td>\n    <td>Path to your custom app file</td>\n  </tr>\n  <tr>\n    <td><code>csp</code></td>\n    <td><code>boolean | cspConfig</code> [<code>false</code>]</td>\n    <td>Config for <a href=\"https://developers.google.com/web/fundamentals/security/csp/\">Content Security Policy</a>, using <a href=\"https://helmetjs.github.io/docs/csp/\">Helmet-CSP</a>\n    <br/>\n    If set to true it will enable default config:\n    <br/>\n    <code>{\n      directives: {\n        defaultSrc: [`'self'`],\n        styleSrc: [`'self'`],\n      },\n    }</code>\n    </td>\n  </tr>\n  <tr>\n    <td><code>gzip</code></td>\n    <td><code>boolean?</code> [<code>true</code>]</td>\n    <td>Enable GZIP Compression</td>\n  </tr>\n</table>\n\n#### Customize Server\n\nCreate a `server.js` file on root directory where your `package.json` lives.\nHere you can customize Maleo's server.\n```js\nimport { Server } from '@airy/maleo/server';\n\nconst PORT = process.env.PORT || 3000;\n\nconst maleoServer = Server.init({\n  port: PORT,\n  runHandler: () => {\n    console.log('Server running on port :', PORT);\n  }\n});\n\nmaleoServer.run();\n```\n\nHere are the API's for the configuration:\n\n<table>\n  <tr>\n    <td>Key</td>\n    <td>Type</td>\n    <td>Description</td>\n  </tr>\n  <tr>\n    <td><code>port</code></td>\n    <td><code>Number?</code> [<code>3000</code>]</td>\n    <td>Port to run Maleo server</td>\n  </tr>\n  <tr>\n    <td><code>assetDir</code></td>\n    <td><code>String?</code> [<code>'<root>/.maleo/client'</code>]</td>\n    <td>Directory for all client related assets</td>\n  </tr>\n  <tr>\n    <td><code>runHandler</code></td>\n    <td><code>Function?</code></td>\n    <td>Function called when maleo server starter</td>\n  </tr>\n</table>\n\n#### Customize Webpack\n\nYou are able to extend Maleo.js' default webpack configuration by defining a function on `maleo.config.js`\n\n```js\n// maleo.config.js\n\nmodule.exports = {\n  webpack(config, context, next) {\n    // Perform customizations to webpack config\n    // Important: This call is required in order to let Maleo pass the config to webpack\n    return next(); \n  },\n};\n```\n\nWebpack function will receive three arguments:\n\n<table>\n  <tr>\n    <td>Argument</td>\n    <td>Details</td>\n  <tr>\n  <tr>\n    <td><code>config</code></td>\n    <td>This contains webpack configuration object that you can manipulate</td>\n  <tr>\n  <tr>\n    <td><code>context</code></td>\n    <td>This contains some keys that are useful for the build context\n      <br>\n      <table>\n        <tr>\n          <td><code>isDev</code></td>\n          <td><code>Boolean</code></td>\n          <td>Check if current build is for development</td>\n        </tr>\n        <tr>\n          <td><code>publicPath</code></td>\n          <td><code>String</code></td>\n          <td>Public Path of user defined or default's value</td>\n        </tr>\n        <tr>\n          <td><code>analyzeBundle</code></td>\n          <td><code>Boolean</code></td>\n          <td>Check if analyze bundle is enabled</td>\n        </tr>\n        <tr>\n          <td><code>buildDirectory</code></td>\n          <td><code>String</code></td>\n          <td>Build Directory of user defined or default's value</td>\n        </tr>\n        <tr>\n          <td><code>name</code></td>\n          <td><code>String</code></td>\n          <td>Build name <code>'server' || 'client'</code></td>\n        </tr>\n      </table>\n    </td>\n  <tr>\n  <tr>\n    <td><code>next</code></td>\n    <td>A callback required to be called to pass the custom configuration</td>\n  <tr>\n</table>\n\nExample of adding `ts-loader` through `maleo.config.js`:\n\n```js\n// maleo.config.js\n\n// Partly taken and modified from @airy/maleo-ts-plugin source\n// for simplicity purposes\n\nmodule.exports = {\n  webpack(config, context, next) {\n    const { isDev } = context\n\n    config.module.rules.push({\n      test: /\\.tsx?/,\n      exclude: /node_modules/,\n      use: [\n        require.resolve('@airy/maleo/lib/build/loaders/maleo-babel-loader'),\n        {\n          loader: 'ts-loader',\n          options: {\n            transpileOnly: true,\n          },\n        },\n      ],\n\n      if (isDev) {\n        config.plugins.push(new ForkTSCheckerWebpackPlugin());\n      }\n\n      return next();\n    })\n  },\n};\n```\n\n#### Customize Babel Config\n\nMaleo.js also let you have your own babel config. Just simply add `.babelrc` file at the root directory of your app.\n\nYou can include Maleo.js' babel preset in order to have latest JavaScript preset.\n\nHere's an example of `.babelrc` file:\n```json\n{\n  \"presets\": [\"@airy/maleo/babel\"],\n  \"plugins\": []\n}\n```\n\nThe `@airy/maleo/babel` preset includes everything you need to get your development started. The preset includes:\n- `@babel/preset-env`\n- `@babel/preset-react`\n- `@babel/plugin-proposal-class-properties`\n- `@babel/plugin-proposal-decorators`\n- `@babel/plugin-proposal-object-rest-spread`\n- `@babel/plugin-transform-runtime`\n- `react-loadable/babel`\n\n## Core Utilities Functions\n\n#### Route Matching\n\nTo use you can import the matching routes function like this:\n```js\nimport { getMatchedRoutes } from '@airy/maleo/utils';\n```\n\nThe function will return all matched routes and the route object from `routes.json`\n\n## CDN Support\n\nIf you are using a CDN, you can set up the `publicPath` setting and configure your CDN's origin to resolve to the domain that Maleo.js is hosted on.\n\n```js\n// maleo.config.js\n\nconst isProd = process.env.NODE_ENV === 'production';\n\nmodule.exports = {\n  assetPrefix: isProd && 'https://cdn.example.com';\n}\n```\n\n## FAQ\n\n== TO BE DETERMINED == \n\n## Plugins\n- [css-plugin](https://github.com/airyrooms/maleo-plugins/tree/master/packages/css-plugin)\n- [redux-plugin](https://github.com/airyrooms/maleo-plugins/tree/master/packages/redux-plugin)\n- [typescript-plugin](https://github.com/airyrooms/maleo-plugins/tree/master/packages/typescript-plugin)\n- [more](https://github.com/airyrooms/maleo-plugins)\n\n\n## Contributing\n[Please follow these steps to contribute to Maleo.js](https://github.com/airyrooms/maleo.js/blob/canary/CONTRIBUTING.md)\n\n[Please follow these steps to contribute to Maleo.js' plugins](https://github.com/airyrooms/maleo-plugins#contributing-guidelines)\n\n\n## Contributors\n\nThis project exists thanks to all the people who contribute. [Contribute](CONTRIBUTING.md).\n\nMany thanks to our **[contributors](https://github.com/airyrooms/maleo.js/graphs/contributors)**!\n\n\n## License\n\nMIT","readmeFilename":"README.md","bugs":{"url":"https://github.com/airyrooms/maleo.js/issues"},"homepage":"https://github.com/airyrooms/maleo.js#readme","_id":"@airy/maleo@0.3.1-canary.36","_nodeVersion":"10.17.0","_npmVersion":"lerna/3.13.1/node@v10.17.0+x64 (linux)","dist":{"integrity":"sha512-iaWKlutc5AjKyV9zPJGFo3SSavzhihO1pTWLQKhv5Kzkwp0+PbT8h16SMdW5cZV6+07pP+TNYLkj/TMvKsRnqQ==","shasum":"0485adeef08e723e969304518abcdbc8d107b12a","tarball":"https://registry.npmjs.org/@airy/maleo/-/maleo-0.3.1-canary.36.tgz","fileCount":107,"unpackedSize":401959,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJdu76QCRA9TVsSAnZWagAAQWQQAKUQXgTT29mPFoG0J7nR\nou/4KyKpMMzDl9tFN1IM3tcyh9xP/6Ni6yLD04WoNVAU4X8ZF+QnhiIhoUCN\ng4phGo4fe6VIvrWwhobdII3BGhIo23T6WfdxsMknYfhwr7pcB+MdlRNykCbs\nQnxe49FGBsZncYDIMOFM0O3+zdazHHRkbM4B0Q2J+/z4vOgvlw4S8Vlm1uTW\ns7BC1TMLLk5pd8CmJ/AbBiLUiXO+J58jfmX1bQPJkeFj0cmFJlf7zCUqCnk7\ne2okhKbo/LcwqTSLSwi3EYcu4/yMKjiavksksKkYOYbMjk6dVMnSkg3/ITnZ\nDDT3NQt7mg4WefqReK997/kRANrfmFBbkUhtH7BYOhOiEfMAvAphmu8aBhos\nGXyCHKRu3e9lIz85RdAkBZQgmn2Kxf22hHG92QXOpP5gz7f8fx/zPUe74MaB\n9dKoe1RxgqIrkp2gQORApNFJlox3uUuKBOlmESeh4Wo1KFJOeutQa/LpgB80\nanhiPXE9tEBcATmU8xTLPBE17i1+RYTAP+Z78QH4bpq4igSE1C2ZBq1STr47\nF7Si61q/Jk/zV1mKs2IuSL/Nv/L5REJHaF9cadFuXesRVY/App94KIDC65ek\nR9btqTervqamROFJeCl/d5wqPneHbPaXYFKXUO71wJnY8HzXI0FJgDyanuNd\ncIg8\r\n=mAXe\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIGcQDYAHgnl/zNPXkzU6xigwbwa9xFwOuhzwpYIjU1MqAiBcqqtXG9K+Qt/FM3RXb+wVxjYMWTWr+wLhmZxYPl1Thg=="}]},"maintainers":[{"name":"airyrooms-engineering","email":"engineering@airyrooms.com"}],"_npmUser":{"name":"airyrooms-engineering","email":"engineering@airyrooms.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/maleo_0.3.1-canary.36_1572585104291_0.48166022068723424"},"_hasShrinkwrap":false}},"time":{"created":"2019-03-13T06:44:58.355Z","0.0.1-canary.51":"2019-03-13T06:44:58.624Z","modified":"2022-04-04T12:23:47.993Z","0.0.1-canary.49":"2019-03-13T06:57:01.045Z","0.0.3-canary.49":"2019-03-13T08:04:05.664Z","0.0.4-canary.49":"2019-03-13T08:25:37.196Z","0.0.5-canary.49":"2019-03-13T11:25:45.164Z","0.0.6-canary.49":"2019-03-18T03:09:21.567Z","0.0.6-canary.0":"2019-03-18T04:27:50.190Z","0.0.8-canary.0":"2019-03-18T09:20:07.513Z","0.0.8-canary.2":"2019-03-18T09:28:21.099Z","0.0.8-canary.3":"2019-03-19T04:13:20.254Z","1.0.0":"2019-03-19T08:10:59.732Z","0.0.8":"2019-03-20T03:51:55.269Z","0.0.9-canary.0":"2019-03-20T03:59:54.614Z","0.0.9-canary.1":"2019-03-20T07:17:47.778Z","0.0.9":"2019-03-20T07:36:23.709Z","0.0.10-canary.0":"2019-03-20T07:45:42.102Z","0.0.10":"2019-03-22T08:42:59.105Z","0.0.11":"2019-03-22T14:20:35.539Z","0.0.12-canary.0":"2019-03-25T02:17:48.711Z","0.0.12-canary.1":"2019-03-25T07:22:04.522Z","0.0.12":"2019-03-25T07:59:36.511Z","0.0.13-canary.0":"2019-03-25T08:09:53.403Z","0.0.13":"2019-03-25T09:17:13.913Z","0.0.14":"2019-03-26T06:28:58.268Z","0.0.15-canary.0":"2019-03-26T09:37:46.471Z","0.0.15":"2019-03-27T07:39:17.427Z","0.1.0":"2019-03-28T05:47:11.290Z","0.1.1-canary.0":"2019-03-28T06:29:41.700Z","0.1.1-canary.1":"2019-03-29T03:24:33.507Z","0.1.1-canary.2":"2019-04-02T03:41:06.181Z","0.1.1-canary.3":"2019-04-02T06:59:55.845Z","0.1.1":"2019-04-04T02:43:15.156Z","0.1.2-canary.0":"2019-04-04T02:47:46.141Z","0.2.0":"2019-04-06T08:24:39.786Z","0.2.1-canary.0":"2019-04-06T08:31:23.592Z","0.2.1-canary.1":"2019-04-09T12:31:38.402Z","0.2.1":"2019-04-09T12:36:38.057Z","0.2.1-canary.2":"2019-04-12T07:55:38.049Z","0.3.0":"2019-05-09T01:54:42.927Z","0.3.1-canary.0":"2019-05-09T02:01:37.404Z","0.3.1-canary.1":"2019-05-18T06:19:16.473Z","0.3.1-canary.2":"2019-05-20T03:36:15.308Z","0.3.1-canary.3":"2019-05-21T04:36:40.732Z","0.3.1-canary.4":"2019-05-21T08:36:43.782Z","0.3.1-canary.5":"2019-05-21T10:21:29.407Z","0.3.1-canary.6":"2019-05-28T12:48:40.330Z","0.3.1-canary.7":"2019-06-10T08:23:23.318Z","0.3.1-canary.8":"2019-06-11T09:34:03.221Z","0.3.1-canary.9":"2019-06-11T09:46:45.663Z","0.3.1-canary.10":"2019-06-11T11:55:42.490Z","0.3.1-canary.11":"2019-06-12T06:42:36.827Z","0.3.1-canary.12":"2019-06-14T11:10:44.183Z","0.3.1-canary.13":"2019-06-20T10:42:42.888Z","0.3.1-canary.14":"2019-06-24T10:33:37.637Z","0.3.1-canary.15":"2019-06-25T06:48:58.685Z","0.3.1-canary.16":"2019-07-11T02:58:26.250Z","0.3.1-canary.17":"2019-07-11T03:45:20.027Z","0.3.1-canary.18":"2019-07-15T03:38:05.665Z","0.3.1-canary.19":"2019-07-15T04:04:59.998Z","0.3.1-canary.20":"2019-07-15T09:55:49.406Z","0.3.1-canary.21":"2019-07-15T10:09:49.036Z","0.3.1-canary.22":"2019-07-17T06:16:15.539Z","0.3.1-canary.23":"2019-07-24T04:19:17.442Z","0.3.1-canary.24":"2019-07-24T08:01:06.877Z","0.3.1-canary.25":"2019-07-26T02:45:37.273Z","0.3.1-canary.26":"2019-07-30T07:37:10.556Z","0.3.1-canary.27":"2019-07-30T07:50:28.628Z","0.3.1-canary.28":"2019-07-31T07:31:32.379Z","0.3.1-canary.29":"2019-07-31T08:49:20.284Z","0.3.1-canary.30":"2019-08-06T04:31:40.295Z","0.3.1-canary.31":"2019-08-06T07:56:02.639Z","0.3.1-canary.32":"2019-09-13T07:25:05.384Z","0.3.1-canary.33":"2019-10-03T04:11:02.824Z","0.3.1-canary.34":"2019-10-03T05:24:02.954Z","0.3.1-canary.35":"2019-10-03T05:29:10.047Z","0.3.1-canary.36":"2019-11-01T05:11:44.444Z"},"maintainers":[{"name":"airyrooms-engineering","email":"engineering@airyrooms.com"}],"description":"Un-opinionated Universal Rendering Javascript Framework","repository":{"type":"git","url":"git+https://github.com/airyrooms/maleo.js.git","bugs":"https://github.com/airyrooms/maleo.js/issues"},"author":{"name":"Airy"},"license":"MIT","readme":"","readmeFilename":"","homepage":"https://github.com/airyrooms/maleo.js#readme","bugs":{"url":"https://github.com/airyrooms/maleo.js/issues"}}