require-is

Conditional loader plugin for RequireJS with code branch build support


License
MIT
Install
bower install require-is

Documentation

Require-is

No longer compatible with the r.js optimizer due to recent changes.

A conditional loading plugin for Require-JS that works with optimizer builds and build layers.

Very similar to the has module in Dojo, except fully compatible with the Require-JS optimizer.

In this way, conditional code branches can be managed at a fine level by the Require-JS optimizer build process.

Basic usage:

define(['is!mobile?mobile-component:desktop-component'], function(component) {
  //...
});

Installation

Download require-is into its own folder either manually or using volo:

volo add guybedford/require-is

Add the following map configuration for ease of use:

  map: {
    '*': {
      'is': 'require-is/is'
    }
  }

Syntax

Where conditional loads are needed, use the RequireJS plugin syntax:

is![conditionId]?[moduleId]:[moduleId]

The negation form is also accepted:

is!~[conditionId]?[moduleId]

Setting Conditions

There are two ways to set conditions.

1. Using the RequireJS configuration:

requirejs.config({
  config: {
    'require-is/is': {
      mobile: true
    }
  }
});

Any number of conditions can be set to true or false in this way.

2. With a detection script:

When the conditionId is not found in the configuration, Require-IS loads the RequireJS module with moduleId, conditionId.

This module should return true or false based on the feature condition.

Example:

To create our mobile condition, we would populate the file mobile.js in the base RequireJS folder:

mobile.js:

define(function() {
  return navigator.userAgent.match(/iPhone/);
});
'browser' condition

By default, require-is defines the browser condition automatically, and will automatically exclude any scripts loaded on the ~browser condition from the build. This allows for easy management of code branches sharing client and server code allowing for a single codebase between client and server.

Multiple conditions:

Multiple conditions can be handled in two ways:

1) Nested 'is' calls:
  define(['is!ios?ios-code:is!android?android-code'], function(mobileCode) {
  });

This can become difficult to read, and there is no bracket notation supported for operation ordering. Calls are simply run from left to right.

2) Multiple 'is' calls:
  define(['is!ios?ios-code', 'is!android?android-code', 'is!blackberry?blackberry-code'], function(ios, android, blackberry) {
    var mobileCode = ios || android || blackberry;
  });

When no alternative load is give, is returns a null module, so that the above conditional checks will return the only defined module in the group.

Polyfills

Require-IS provides a nice natural polyfill system.

A feature detection module can be run, and the required polyfill only downloaded if the feature detection fails.

This allows modules to be created that are minimal in builds, excluding the polyfill module but only including the feature detection module.

Implementations for some simple polyfills are provided below:

Polyfill Implementations

  • selector Performs native querySelector testing, downloading sizzle dynamically only if necessary. Otherwise it returns the native selector. Also compatible with jQuery. Allows modules to only be dependent on a small selector feature detection.
  • json Checks for JSON.parse support, and if not provided downloads json2 by Douglas Crockford.

Optimizer Configuration

When running a build, Require-IS will by default inline the condition detection module, as well as all possible module variations loaded with Require-IS.

To get fine-grained control over this build process use the isExclude array option.

Condition exclusions: isExclude

You may wish to entirely exclude a specific condition case. For example, to exclude all the mobile scripts from the build, and then have them only loaded dynamically if needed.

This can be done with the build paramter - isExclude.

isExclude is an array of conditionIds to exclude from the build layer. It can be applied for a single build layer or for the entire build config.

Example:

Require-JS build config:

{
  modules: [
  {
    name: 'app',
    isExclude: ['mobile']
  }
  ]
}

This will build app with all is!mobile?moduleId moduleIds excluded from the build layer.

To exclude the negation of mobile (from is!~mobile?moduleId OR is!mobile?...:moduleId), simply add ~mobile to the isExclude array.

Layered Loading

This is all good and well, but if the mobile detection activiates, and we have many different mobile scripts, then we will end up with many separate dynamic requests to mobile modules, which goes against the point of having a build.

To ensure that the mobile scripts are loaded from the correct layer, create a paths configuration in the production configuration to point all the mobile moduleIds to the mobile layer moduleId.

Typically this is a standard post-processing operation done for all builds to allow optimal layer loading.

Example:

{
  modules: [
  {
    name: 'app',
    isExclude: ['mobile'],
  },
  {
    //define the 'app-mobile' layer to contain all the mobile scripts from 'app'
    name: 'app-mobile',
    create: true,
    include: ['app'], //fires the default inclusion of app, which includes all conditions (and mobile)
    exclude: ['app'] //exclude uses the version of app as above, with the exclusions made, hence the difference between include and exclude is purely the mobile scripts!
  }
  ]
}

With production config:

{
  paths: {
    'mobile-dependency-1': 'app-mobile',
    'mobile-dependency-2': 'app-mobile'
  }
}

This example is included in the example folder of the project.

In this way, flexible code branches can be managed.

  1. Default build exclusion

    For polyfill modules, typically one wants the modules excluded from the build by default, without having to add the isExclude property.

    For this purpose, a require can be made of the form:

      define(['is!~feature-test?[polyfill]'], function(feature) {
        feature  = feature || nativeSupport();
    
        // use feature
      });
    

    The [moduleId] form of loading implies that the polyfill is by default excluded from the build.