Skip to content

Entry Points

mattdesl edited this page Nov 2, 2014 · 3 revisions

Occasionally you will find it useful to provide the user with multiple entry points, or export a higher-order function for advanced uses of the library. These are particularly useful for front-end code, where bundle size and compatibility with other frameworks may be a concern.

"Grab Bag"

The "grab bag" approach is useful to split many small functions into their own files, where separate modules would be too cumbersome. Often the main entry point exports all the functions, for convenience, but module code is encouraged to use the specific functions.

//require a specific function
var ortho = require('gl-mat4/ortho')

//require all functions, e.g. for convenience in app code
var mat4 = require('gl-mat4')

Naming conventions follow Node standards for files (dash-case) and JS standards for exported variables (camelCase).

Examples:

  • gl-mat4 - splitting @toji's gl-matrix library into better browserfiable pieces
  • eases - Robert Penner's easing equations, modularized
  • glsl-easings - same as above, but for glslify
  • vectors - generates new functions for optimized N-vector math

Vendor Lock-In

Sometimes your module might lead to "vendor lock-in" by depending on a complex framework, like poly2tri (~20kb minified). In this case it might be worth including an alternative entry point, so the user is free of vendor lock-in and can optimize their bundle size. For example, fontpath-gl requires poly2tri, but exposes a second entry point to override the triangulator (e.g. with Tess2 instead).

The main entry point:

//will bundle poly2tri for convenience
var TextRenderer = require('fontpath-gl')

Alternative entry that allows the user to hook up a custom triangulator:

//base class without any poly2tri dependencies
var Base = require('fontpath-gl/base')

Large Frameworks

Building plugins and utilities for ThreeJS, Pixi, etc is a little tricky on npm. These are frameworks not designed with modularity, versioning, or the Unix Philosophy in mind. So, for example, if you build lots of small modules that depend on three, you may run into a situation down the line where multiple versions of the entire ThreeJS framework are included in your final bundle.

In these cases your entry point could just take in the global object (THREE, PIXI, etc), so the user has control over how it is brought into their application. So, your module might look like this:

module.exports = function(THREE) {
    return function computeCentroid(geometry) {
        var centroid = new THREE.Vector3()
        for (var i = 0, l = geometry.vertices.length; i < l; i++) {
            centroid.add(geometry.vertices[i])
        }
        centroid.divideScalar(geometry.vertices.length)
        return centroid
    }
}

And the user would need to specify THREE explicitly:

var computeCentroid = require('three-compute-centroid')(THREE)
computeCentroid(geometry)

It's also a good idea to document exactly which version(s) of the framework your module was tested against, since many of these frameworks have not adopted semantic versioning.

Examples:

Clone this wiki locally