@kamilmac/puppeteer

Library for managing micro-frontends.

Stats

stars 🌟issues ⚠️updated 🛠created 🐣size 🏋️‍♀️
20Nov 22, 2017Nov 13, 2017Minified + gzip package size for @kamilmac/puppeteer in KB

Readme

Puppeteer

Library for managing micro-frontends.

How it works ?

Puppeteer is based on simple pub/sub pattern implementation.

By passing config object and running generateAppEvents Puppeteer creates instance of event bus with default actions responsible for dynamic mounting/unmounting of children applications.

One can also use inititateAppRouter when simple routing is needed. In this case Puppeteer observes and appends hash dynamicaly.

On top of that simple Store functionality is provided which might be usefull for keeping global app state like auth info etc.

Puppeteer uses default loader for dynamic loading of external scripts. It is possible to use custom loader by passing it as 'loader' property in config object. Just make sure it is a funtion that takes file path as argument and returns a promise.

Config

  • dev: true|false(default) flag (logs out events)
  • loader: custom loader function (consumes file string and returns a promise)
  • apps: object describing children apps. App object key is a base for route & action name.
    • appName: this object key will resolve to '#/appname' route and APPNAME prefix for events
      • bundleLocation: path to external app bundles. List is loaded sequentially.
      • domHook: document_id where app should be attached
      • mountFuncName: name of the mountung function exisiting on the window object in children app. Returns unmount function.

Installation & Simple Usage

NPM

npm i --save @kamilmac/puppeteer

YARN

yarn add @kamilmac/puppeteer

Main app - index.js

import Puppeteer from '@kamilmac/puppeteer'

const config = {
  // loader: YOUR_OPTIONAL_LOADER
  dev: true,
  apps: {
    app1: {
      bundleLocation: ['app1.js'],
      domHook: 'app1',
      mountFuncName: 'mountApp1'
    },
    app2: {
      bundleLocation: ['app2.js'],
      domHook: 'app2',
      mountFuncName: 'mountApp2'
    },
  },
}

// optional store
let store = {
  greeting: 'hello!',
}

const puppeteer = Puppeteer(config)
  .generateAppEvents() // generates actions ie. 'APP1:ACTION', 'APP2:ACTION'
  .initiateAppRouter() // (optional) attaches router and creates routes for `#/(app1|app2)`
  .attachStore(store) // (optional)

// Runs App1 with payload object
// If bundle is not loaded, Puppeteer mounts it dynamically.
// When done, mountFunc is run with (puppeteer, domHook) arguments.
// Look below for mountFunc example
puppeteer.publish('APP1:ACTION', { data })

setTimeout(() => {
  // Unmounts App1 and runs App2
  puppeteer.publish('APP2:ACTION', { data })
}, 3000)


puppeteer.store('GET', 'greeting').then(value => {
  console.log(value) // prints 'hello!'
})
// Write to store
puppeteer.store('SET', { token: 'token' })
// Read from store
puppeteer.store('GET', 'token').then(value => {
  console.log(value) // prints 'token'
})
// Subscribes to change in store
puppeteer.store('CHANGE', data => {
  console.log('Modified data', data)
})

Children app - app1.js (React example)


// Puppeteer sets puppeteerActive flag to true when initialised.
// Simple check like below allows to run the app in isolation.
if (!window.puppeteerActive) {
  ReactDOM.render(
    <App/>,
    document.getElementById('root')
  )
} else {
  // Mount function for App1. This is the glue between App1 and Puppeteer
  window.mountApp1 = (puppeteer, domHook) => {
    ReactDOM.render(
      <App puppeteer={puppeteer}/>,
      document.getElementById(domHook)
    )
    return () => {
      ReactDOM.unmountComponentAtNode(document.getElementById(domHook))
    }
  }
}

class App extends React.component {
  componentDidMount() {
    this.unsub = this.props.puppeteer.subscribe('APP1:ACTION', payload => {
      // DO YOUR THING
    })
  }

  componentWillUnmount() {
    // unsubscribe when component unmounts
    this.unsub()
  }

  runApp2(payload) {
    // Opens App2 and unmounts App1
    this.props.puppeteer.publish('APP2:ACTION', payload)
  }

  render() {
    return <h1>APP1</h1>
  }
}

API

Functions

Puppeteer(config)Object

Consumes Config object and creates Puppeteer instance

{ loader: OPTIONAL_LOADER_FUNCTION dev: DEV_FLAG apps: { // Uppercase object key is used as a base for action type and hash name app1: { bundleLocation: 'app1.js', // document element id where app will be attached domHook: 'app1', // Mounting function accesible of window object // should return unmount function mountFuncName: 'mountApp1' }, app2: { bundleLocation: 'app2.js', domHook: 'app2', mountFuncName: 'mountApp2' }, }, }

subscribe(topic, listener)function

Subscribes to TOPIC on event buss. Callback is executed with payload data each time 'publish' method is run.

publish(topic, payload)Promise

Publishes event with optional payload.

store(action, arg)Promise | function

Wrapper for store events

generateAppEvents()Object

Creates default actions/events for children apps in config object. It uses subscribe/publish methods. Route names are based on app keys in config object For each app TOPICs are created:

${APP}:ACTION, ${APP}:MOUNT, ${APP}:UNMOUNT

Apps should communicate by using ${APP}:ACTION topic. MOUNT/UNMOUNT actions are resolved automatically.

initiateAppRouter()Object

Subscribes to ROUTER:CUSTOM_HASH_CHANGE event which is run each time url location changes. It publishes ROUTER:NONE_EXISTING_ROUTE event when apps dont match url. It also uses ${APP}:ACTION events for dynamic app mounting. App route names are based on app keys in config object.

attachStore(Optional)Object

Creates simple key-value store which also uses publish/subscribe for communication. Instead of calling publish/subscribe, Puppeteer provides store wrapper for dealing with it.

getActiveApp()String

Returns name of current active app.

Puppeteer(config) ⇒ Object

Consumes Config object and creates Puppeteer instance

{ loader: OPTIONAL_LOADER_FUNCTION dev: DEV_FLAG apps: { // Uppercase object key is used as a base for action type and hash name app1: { bundleLocation: ['app1.js'], // document element id where app will be attached domHook: 'app1', // Mounting function accesible of window object // should return unmount function mountFuncName: 'mountApp1' }, app2: { bundleLocation: ['app2.js'], domHook: 'app2', mountFuncName: 'mountApp2' }, }, }

Kind: global function
Returns: Object - Puppeteer instance

Param Type
config Object

subscribe(topic, listener) ⇒ function

Subscribes to TOPIC on event buss. Callback is executed with payload data each time 'publish' method is run.

Kind: global function
Returns: function - Unsubscribe function.

Param Type Description
topic String Event to subscribe to.
listener function Callback function run when topic event is published.

publish(topic, payload) ⇒ Promise

Publishes event with optional payload.

Kind: global function
Returns: Promise - Resolves when all subscribed callbacks finished.

Param Type Description
topic String Event to publish.
payload Object optional payload object.

store(action, arg) ⇒ Promise | function

Wrapper for store events

Kind: global function
Returns: Promise | function - Returns Promise on GET action / Unsubscribe function on CHANGE

Param Type Description
action String Event to publish. (GET
arg String | Object | function Depending on action. (KEY_STRING

generateAppEvents() ⇒ Object

Creates default actions/events for children apps in config object. It uses subscribe/publish methods. Route names are based on app keys in config object For each app TOPICs are created:

${APP}:ACTION, ${APP}:MOUNT, ${APP}:UNMOUNT

Apps should communicate by using ${APP}:ACTION topic. MOUNT/UNMOUNT actions are resolved automatically.

Kind: global function
Returns: Object - returns this instance

initiateAppRouter() ⇒ Object

Subscribes to ROUTER:CUSTOM_HASH_CHANGE event which is run each time url location changes. It publishes ROUTER:NONE_EXISTING_ROUTE event when apps dont match url. It also uses ${APP}:ACTION events for dynamic app mounting. App route names are based on app keys in config object.

Kind: global function
Returns: Object - returns this instance

attachStore(Optional) ⇒ Object

Creates simple key-value store which also uses publish/subscribe for communication. Instead of calling publish/subscribe, Puppeteer provides store wrapper for dealing with it.

Kind: global function
Returns: Object - returns this instance.

Param Type Description
Optional Object initial store.

getActiveApp() ⇒ String

Returns name of current active app.

Kind: global function

If you find any bugs or have a feature request, please open an issue on github!

The npm package download data comes from npm's download counts api and package details come from npms.io.