tessellate icon indicating copy to clipboard operation
tessellate copied to clipboard

Plugins for bundle loading and properties loading

Open mfellner opened this issue 7 years ago • 0 comments

What

Add a plugin system for fetching external data (bundles and properties) and move all such logic into plugin packages.

Why

Depending on the deployment context, custom logic is necessary to fetch JavaScript bundles and external properties.

Right now, tessellate-fragment includes code specific to Zalando's internal setup (x-zalando-request-host) and there is no good way to implement generic enough solutions for bundle and property fetching that can be configured for each use case:

  • based on request url
  • based on custom query parameters
  • based on custom HTTP headers
  • based on calling external APIs
  • based on static configuration

Proposal

Implement a plugin system (similar to babel plugins) that allows adding plugins for loading bundles and loading properties. The plugins are configured by name in two separate lists and are dynamically required by tessellate-fragment. They implement a standardised API.

Configuration

A plugin package name should be prefixed by tessellate-fragment-. The configuration should define a list of plugin names without the prefix. Plugins are going to be applied in the order of their configuration.

API

Given the following sequence for a request to tessellate-fragment:

           +-----------------+
request -> | request-handler | -> parsed request ->
           +-----------------+
   +----------------+
-> | bundle-plugins | -> bundle
   +----------------+                     +----------+
                       {bundle, props} -> | renderer | -> response
   +---------------+                      +----------+
-> | props-plugins | -> props
   +---------------+
  1. parse the incoming request
  2. asynchronously call plugins with the parsed request and any options as "context"
  3. collect the bundle and properties from the context
  4. render the response

The API for bundle and property plugins should be the same and use a composable "middleware" style:

export default function(context: Context): Promise<Context>
type Context = {| request: Request; bundle: Bundle; props: object; |};
type Bundle  = {| js: Array<string>; css: Array<string>; |};

Multiple plugins are going to be composed in order or their declaration in the configuration:

const { bundle, props } = await pluginA(await pluginB(await pluginC(context)));

However if plugin functions have an attribute async: true (e.g. pluginA.async) they are called asynchronously and their results are merged:

const results = await Promise.all([ pluginA(context), pluginB(await pluginC(context)) ]);
const { bundle, props } = merge(...results);

mfellner avatar Mar 30 '17 09:03 mfellner