{{>header}} {{>nav this}}

Authoring polyfills

Polyfills are located in the /polyfills directory and organised by feature name.

Naming

Polyfill directories should be named after their main JavaScript global, and nested subdirectories should be used to descend through objects, e.g. /polyfills/Array/prototype/forEach should be used to hold the polyfill for Array.prototype.forEach.

Where a polyfill exposes multiple globals, consider splitting it if possible, but if that's not practical, name the polyfill after the most well known of the globals.

If a feature has no JavaScript API, e.g. support for CSS styling of HTML5 elements, it should be given a descriptive name prefixed with a tilde (~), ie. ~html5-elements.

Files

Each polyfill directory (and subdirectory) is structured like this:

Configuration

The config.json file may contain any of the following keys:

Example:

{
    "browsers": {
        "ie": "6 - 9",
        "firefox": "<=20",
        "opera": "11 || 14",
        "safari": "<=4",
        "ios_saf" "<=6",
        "samsung_mob": "*"
    },
    "aliases": [
        "modernizr:es5array"
    ],
    "dependencies": [
        "Object.defineProperties",
        "Object.create"
    ],
    "license": "MIT"
}

A request from IE7 would receive this polyfill, since it is targeted to IE 6-9. It may also receive polyfills for Object.defineProperties and Object.create, since those are dependencies of the polyfill in this example, if those polyfills also apply in IE7.

Authoring guidelines

We are always glad to accept contributions of original polyfills as well as copies of third party polyfills maintained elsewhere. It's important to distinguish between these, because:

In addition, whether original or third party, if the polyfill targeting is open ended (ie it targets current versions of browsers), it must only make changes that create conformance with a published specification. If it is targeted only at older versions of browsers, it may be OK to exhibit any behaviour that is likely to satisfy the needs of applications that depend on the feature. For example, our devicePixelRatio polyfill in very old browsers simply returns 1 without actually measuring anything.

Binding to global scope

Polyfill bundles produced by the service are wrapped in the following IIFE:

(function(undefined) {
	// Polyfills go here
).call('object' === typeof window && window || 'object' === typeof self && self || 'object' === typeof global && global || {});

This provides a safe this variable to which polyfills may attach properties that they want to add to global scope. Prefer this to attaching to `window` or `self`, which may not always be what you think they are.

Feature detects

Since polyfills are loaded in an environment with a this value guaranteed to be an object, detects should lean heavily on the in operator and build from this, e.g.:

'navigator' in this && 'geolocation' in this.navigator

Objects defined as part of ECMAScript and present in all browsers above our baseline may be assumed to be present, so this is fine:

'imul' in Math

Update tasks for 3rd party polyfills

If a polyfill comes from a third party source, it's a good idea to write an update task that can be run to import the latest version of the polyfill code from its master repo. An update task is called update.task.js and lives alongside the polyfill.js and config.json files. It must export a function that accepts the grunt object, and currently must complete its work synchronously. Example:

'use strict';
var path = require('path');
module.exports = function(grunt) {
	// ... download/import latest source here
	// ... do any required source modification
	var newSource = 'test';
	grunt.file.write(path.join(__dirname, 'polyfill.js'), newSource);
};

Often changes are required (or desirable) to support the format of polyfills needed by the polyfill service, for example:

Browsers

The short names should be used in the config.json to configure the browser support using the browsers key.

Short nameUser Agent NameBaseline
ieInternet Explorer / Edge / Edge Mobile{{baselines.ie}}
ie_mobInternet Explorer Mobile{{baselines.ie_mob}}
chromeChrome{{baselines.chrome}}
ios_chrChrome on iOS{{baselines.ios_chr}}
safariSafari{{baselines.safari}}
ios_safSafari on iOS{{baselines.ios_saf}}
firefoxFirefox{{baselines.firefox}}
firefox_mobFirefox on Android{{baselines.firefox_mob}}
androidAndroid Browser{{baselines.android}}
operaOpera{{baselines.opera}}
op_mobOpera Mobile{{baselines.op_mob}}
op_miniOpera Mini{{baselines.op_mini}}
samsung_mobSamsung Internet{{baselines.samsung_mob}}
bbBlackberry{{baselines.bb}}

Browser baselines

The polyfill service does not attempt to serve polyfills to very old browsers. We maintain a movable baseline of browser support, which is shown in the table above and configured in the UA module. If a request for a polyfill bundle comes from a UA that is below the baseline or unknown, the response will be something like this:

 * UA detected: ie/5.5.0 (unknown/unsupported; using policy `unknown=ignore`)
 * Features requested: Promise
 *
 * Version range for polyfill support in this family is: >=6

In practice this means that in some cases, where polyfills have configurations targeting all versions of a browser (e.g. "ie": "*"), the polyfill will actually only be available in versions of that browser above the baseline (unless overridden using the `unknown` query string argument).

This feature is intended to allow simpler testing and maintenance of the service, so that the general baseline can be moved forward without having to update every polyfill config individually.

{{>footer}}