packages-pagelet
Version:
Pagelet for rendering a npm package
275 lines (214 loc) • 8.81 kB
Markdown
# packages-pagelet
The `packages-pagelet` is a small re-usable component that you could use to
display information about any npm package. Private, public it doesn't matter as
long as it's in a npm registry.
The `packages-pagelet` is build upon the [Pagelet] interface from the [BigPipe]
framework which allows you to compose pages from small fragments or components
which is you can re-use and remix in any way you want allowing for a fully
modular and high performance front-end development experience.
## Installation
This module is distributed through the public npm registry and can therefor be
installed using:
```
npm install --save packages-pagelet
```
The `--save` flag automatically tells `npm` to add this dependency and it's
installed version in to your `package.json`. If you do not have the BigPipe
framework installed, run:
```
npm install --save bigpipe packages-pagelet
```
To also install and save BigPipe in one go.
## Configuration
The `packages-pagelet` was designed with flexibility and developer freedom in
mind that why there are a lot of (important) options that can be changed and
fine tuned. So take a few minutes to read through all options to ensure you get
the most out of this module. In order to configure this module you need to
`extend` the module and override the desired properties. The extending of the
module is done using `extend` method that is exposed on the module. The `extend`
method follows the same schematics as in the `backbone` library:
```js
'use strict';
var Packages = require('packages-pagelet');
//
// configure the module by extending it with new properties.
//
module.exports = Packages.extend({
registry: 'https://registry.nodejitsu.com/'
});
```
The following options (keys) can be configured:
### registry
The `registry` property allows two different values, it can either be a string
with a trailing slash which points to the location of The npm Registry you want
to use (which is useful for a private registry) or it can be set to a
pre-configured `npm-registry` instance which would be preferred. The reason for
this is that the `npm-registry` client uses the `githulk` module to make API
requests to GitHub repositories to retrieve addition data about packages. As the
API of GitHub is rate limited it's vital that you use authenticated API requests
to GitHub where ever possible. See the [npm-registry] for more detailed
information about the options that can be configured.
```js
Packages.extend({
registry: 'https://registry.nodejitsu.com/'
});
```
Or
```
var Registry = require('npm-registry');
Packages.extend({
registry: new Registry({
registry: 'https://registry.nodejitsu.com/'
});
});
```
### cache
This might be one of the most important properties that is configurable on this
module. The cache allows you to cache the result of the all gathered data per
module. We support 2 different cache patterns, an **async** cache and an
**sync** cache. The sync cache would be ideal during development as you can just
use a simple LRU cache for this. In a production environment it might be worth to
go with an async, distributed cache. Once you want to scale out to multiple node
processes you still want to share this cache.
The supplied cache only needs 2 different methods:
- `get(key)`: A method that returns a JSON object or null for a given key.
- `set(key, obj)`: A method that stores a JSON object for a given key.
When you are using an async cache each method will receive a callback as last
argument in addition to that, the cache will be set using fire and forget as we
will not wait until the supplied callback is answered before we rendering the
content.
```js
var store = {};
Packages.extend({
cache: {
get: function (key) {
return store['.prefix@'+ key];
},
set: function (key, value) {
return store['.prefix@'+ key] = value;
}
}
});
```
Please be aware that this cache is not automatically invalidated. So it might
make sense to use either a [memcached] or [redis] cache with an expire value
for this.
### githulk
The `packages-pagelet` uses a lot of GitHub API end points to get the data
retrieval as accurate and complete as possible and to render README's correctly.
As the GitHub API is rate limited to 60 calls per hour for unauthorized and 5000
calls for authorized connections it's really important to use an authorized
`githulk` instance within your pagelet. The [GitHulk] module has 2 ways of
creating an authorized connection, either by supplying it with an `token` option
which is your OAuth token or by supplying the user name and password for your
account so it can use basic auth. For small and private pages it might enough to
have 5000 API calls per hour, but if you want to host the packages page in the
public you would probably need as much as possible. That's why the `githulk`
implements a request cache so it can do conditional requests to the GitHub API
based on the returned `Etag` headers from the API.
The caching is implemented in [mana] which is a framework for writing high
available API clients. The cache API that it requires follows the same
schematics as the cache that you can implement for this pagelet as it supports
both an **sync** and **async** interface for retrieving and storing cache.
You don't need to manually invalidate this cache as it's automatically overridden
when the Etag is no longer accepted by the GitHub API.
```js
var GitHulk = require('githulk')
var hulk = new GitHulk({
token: 'my secret oh.. auth token',
cache: {
get: function (key) {},
set: function (key, value) {}
}
});
```
The `githulk` instance that you've created can also be passed in to your custom
`npm-registry` client so it can also re-use the same instance and credentials
for when it does a lookup.
### key
When we cache the data for the pagelet we automatically run the name of the
module through the `key` method which prefixes the key so it can be used as
cache key. We are currently prefixing the key with the **major** version of this
module. So when we make a backwards incompatible change to the data structure
your page wouldn't die because it had an incorrect data structure.
But if you want absolute control over the process, you can just create your own
key prefixer:
```js
Packages.extend({
key: function key(name, version) {
return 'foo-bar-prefixed-key:'+ name +':'+ version;
}
});
```
### expire
This sets the expiration values for the cache. There are 2 properties can be
configured on this object, the `latest` which expires the latest version number
for a given package and `data` which expires the resolved data for the resolved
latest version number. The values should be set in `miliseconds`.
```js
Packages.extend({
expire: {
latest: 1000,
data: 1000 * 1000 * 1000
}
});
```
So a fully configured and customized `packages-pagelet` should look something
like this:
```js
'use strict';
var GitHulk = require('githulk')
, Registry = require('npm-registry')
, Packages = require('packages-pagelet')
, DistributedJSONCache = require('no-existing-module-implement-it-yourself');
var githulk = new GitHulk({
cache: new DistributedJSONCache(),
token: 'your oauth token'
})
, registry = new Registry({
githulk: githulk,
registry: 'https://registry.nodejitsu.com/'
});
module.exports = Packages.extend({
cache: new DistributedJSONCache(),
registry: registry,
githulk: githulk
});
```
## Pre-population
Correctly resolving a single package requires a lot of HTTP requests and
computation. This pagelet does support caching, but this is of course only useful
if you start of with a completely cached result set as packages are only cached
when they are accessed for the first time (depending on how you implemented your
own cache of course).
To make the population of cache a bit easier we've exposed our internal resolve
method so it can be used out side of the pagelet as well.
```js
var resolve = require('packages-pagelet').resolve;
[
/* assume a list of modules you want to pre-cache */
].forEach(function (name) {
resolve(name, function (err, data) {
// Cache the data.
});
});
```
The resolve method accepts 3 arguments:
- `name`: The name of the module it needs to look up.
- `options`: Optional object which contains a reference to the [registry] and
[githulk] options.
- `callback`: The completion callback which follows an error first callback
pattern.
[BigPipe]: http://bigpipe.io
[Pagelet]: https://github.com/bigpipe/pagelet
[registry]: #registry
[githulk]: #githulk
[GitHulk]: https://github.com/3rd-Eden/githulk
[mana]: https://github.com/3rd-Eden/mana
[memcached]: https://github.com/3rd-Eden/node-memcached
[redis]: https://github.com/mranney/node_redis