Fittings for BigPipe which allows you to use the BigPipe as a third party/external content provider.
npm install externalExternal is a dual purpose library. It ships with a client-side framework renders
third party or external pages in the most optimal way as possible. This is done
using various of techniques:
- The payload is downloaded using a fully async streaming XHR request. This way
we can continuously update and render our placeholder while data flows and
therefor reducing the time to render.
- All assets of the page are loaded async, this includes the CSS.
- The received client code is wrapped before execution so client code can re-use
our dependencies while keeping a sandboxed approach.
- While the client was specifically written for the [BigPipe] framework it
should work against any back-end as long as it returns the same data
structure.
- Templates are rendered using React so it's easy to compose and update.
But we also ship with a server-side framework implementation for [BigPipe] which
makes it possible to serve the client and automatically format all the output in
the expected HTML structure.
- Installation
- Building
- Serving
- Listening
- Extern
- BigPipe
- Wire Format
- License
The client-side component is composed from various of tiny modules and can be
build using [Browserify]. It can be build-in to other browserify components by
simply requiring the external module in your client-code.
The server side part of this framework can be installed through npm:
```
npm install external
In addition to providing a browserify-able client-side script there is also a
compiled version of this code which lives in the dist folder calledextern.js. This pre-compiled library exposes it self using the Extern globalrequire
and therefor does not introduced statement as globals. In all the codeExtern
examples in documentation we assume that you have an global. If you usedist
the build you can skip the following example:
`js`
var Extern = require('extern');
If you want to generate new stand alone bundles of the Extern library you can
run our prepublish and dev scripts using the npm run command. ThesedevDependencies
commands do assume that you've installed the of this project.dist/extern.min.js
To generate a new production build, run:
``
npm run prepublish
As this is a prepublish script, it means that every release to npm will havedist/extern.js
the included. So if browserify isn't your think, you can justextern/dist/extern.min.js
include the instead.
To generate an un-minified build for development purposes you can run:
``
npm run dev
This will generate a new dist/extern.dev.js file.
Now that you know how to install it and what type of bundles there are you can
decide how to serve the library. When this module is used as plugin in [BigPipe]
it will automatically serve the browserify and plugin combined bundle from:
``
http(s)://domain.com/extern.js
We also mount our dist folder on the server so the static assets in this
folder can also be served:
``
http(s)://domain.com/extern.min.js
Now that you've picked your build, and know how the files are served you can
simply put the script tag in your page and your ready to display external
pages/apps.
`html`
The easiest way to have Extern load your remote pages is by using theExtern.listen method in combination with the rel="extern" attributes on elements:
`html`
Remote
The Extern.listen method will gather all elements and search for a relextern
that is set to and uses the set href of the element as URL that needs
to be remotely loaded.
`js`
Extern.listen(document.body, {});
The following options are supported:
- timeout Timeout for dependency loading. If assets take longer we should
render and error template instead. The timeout is in milliseconds.
- document Reference to the document global can be useful if assets needclassName
to be loaded in iframes instead of the global document.
- If a link has this className we will automatically load it inextern-loads
the placeholder. This className will also automatically be add and removed
once the link is clicked. Defaults to .
`js`
var extern = new Extern('http://my.example.com/page', document.body, {
timeout: 10000
});
The returned extern instance is actually an EventEmitter3 instance so you
can listen to the various of events that we're emitting:
- error Emitted when something went so horribly wrong that we decided to
show the error template. This event receives the actual error as argument.name
- done The streaming XHR is finished with loading.
- name:render Called when a fragment is about to render in to the
placeholder. The part in the event should be name of the fragment you
want to listen for.
- name:loaded All the assets are loaded for the given placeholder name.
The client code for each fragments are loaded through an XHR connection. This
way we can safely executed third party code by wrapping the execution in a
try/catch statement. But not only does this allow us to wrap code, it also
allows us to introduce variables in the function. The following variables are
introduced as "globals":
- React, This is the react/addons reference.require
- , Reference to our require statement so you can re-use all the
bundled things.
The following properties and methods are exposed on the Extern instance.
#### Extern.listen
Exposed on the constructor
Scan the current document for all elements and attach clickplaceholder
listeners to it so we can automatically update the supplied placeholder with the
contents of the set URL. This method accepts one argument and that is the DOM element where all pages should loaded in
`js`
Extern.listen(document.body);
#### Extern.merge
Exposed on the constructor
Merge the object of the second argument in to the first argument. It returns the
fully merged first argument.
`js`
var x = Extern.merge({ foo: 'foo' }, { bar: 'bar' });
#### Extern.requests
Exposed on the constructor
A reference to the requests module that we're using for our XHR requests.
`js`
var requests = Extern.requests.
See unshiftio/requests for more
information.
This library ships with a custom [Fittings] framework implementation for
[BigPipe] which allows us to control how everything is processed inside of
[BigPipe]. Adding it to your BigPipe instance is just as simple as passing a
custom framework option while creating a new instance:
`js
'use strict';
var BigPipe = require('bigpipe')
, Extern = require('external');
var app = BigPipe.createServer({
framework: Extern,
port: 8080
});
`
But the framework can also be set _after_ the construction using the framework
method:
`js`
app.framework(Extern);
**Please do note that the current Fittings implentation is in the BigPipe master
branch but will out in the release that follows 0.9**
Once the fittings are installed on the application, it will start spitting out
responses based on the specified Wire Format below. The
processing instructions can be found in the instructions folder
in the root of this repository. But before fiddling with these files I would
suggest giving the [README.md][Fittings] of Fittings a read so you know how the
data formatting works.
In order to have the broadest support within this framework we came up with a
dedicated wire-format in order to have the server-side and client-side
components interact with each other. While this wire-format is mostly catered to
the needs of an application that is build using the [BigPipe] framework it
should be relatively easy to produce exactly the same output in different
frameworks and programming languages. This wire format is also required in order
to make streaming data as simple as possible as we can trigger buffer flushes
based on this.
The format that we're using is \u1337 separated JSON. Every time we\u1337
encounter the character on the client-side we assume it's the end of
chunk that requires processing. The JSON payload that is send should contain the
following properties:
- _id A unique id for the payload that is flushed.
- name Name of the payload that is flushed. This is used to track
potential child->parent references throughout the flushed payload.
- details An object that contains:
- js Array with path names for the JavaScript files that need to be
loaded on the page. We will automatically prepend the server address to
these assets.
- css Array with path names for the CSS files that need to be
loaded on the page. We will automatically prepend the server address to
these assets.
- state Additional state that will be spread on the component when we
render it.
- template An initial HTML template that should be rendered in the given
placeholder.
In order to be able to load CSS assets fully async in every browser we need to
know when the styles are applied. This is done by Extern client by adding a DOM
element to the page that has an id attribute which contains _ and the filename of#_yourfilename
the asset that is being downloaded (). We therefor requireheight
that the CSS file contains CSS selector and sets the property to42px. This allows us to poll the element for height changes to know when the1aFafa801jz09.css
CSS is fully loaded. So if we have a file called it should
have the following selector in the source:
`css``
#_1aFafa801jz09 { height: 42px }
This project has been released under the MIT license, see [LICENSE].
[Fittings]: https://github.com/bigpipe/fittings
[Browserify]: http://github.com/substack/node-browserify
[BigPipe]: https://github.com/bigpipe/bigpipe