Auto Generating Polyfills for JavaScript.
I’m sure we can safely say at the time of this writing and going forward that we as developers no longer care to maintain let alone write prefixes and polyfills in our code. The task is time consuming, messy and hard to maintain over time.
Currently we have tools like autoprefixer and -prefix-free that let us write CSS properties without the hassle of remembering what prefix we’re using for each property.
Today we’re going to take a look at a tool that lets authors use a similar approach used in CSS land, but with JavaScript Polyfills! Awesome right? Lets take a look at an Open Source tool called autopolyfiller by azproduction which is a bit like Autoprefixer, but for JavaScript Polyfills. It scans your code and applies only the required polyfills. It can help you to write cutting-edge JavaScript without thinking about ES shims and shivs by generating polyfills based on the polyfill database.
If all that sounds like gobbledygook still then think of it like this…
Autopolyfiller is a generated shim that mimics a future API providing fallback functionality to older browsers. This means our JS is polyfilled for things like Object.keys(window)
where an Object.keys
polyfill is required to run in any browser (including IE7).
If you’d like to dive deeper into nomenclature feel free to read Remi Sharp’s take on what is a polyfill.
Installation:
Grunt
npm install grunt-autopolyfiller --save-dev
Once the plugin has been installed you can load and register the task…
grunt.loadNpmTasks('grunt-autopolyfiller');
grunt.registerTask('jspoly', ['autopolyfiller']);
Now that we have autopolyfiller installed and saved to our package.json
, we can add the task to our Gruntfile.
autopolyfiller: {
latest_browsers_and_ie: {
options: {
browsers: ['last 2 version', 'ie 8', 'ie 9']
},
files: {
'js/your_polyfill_result.js': ['js/your_js_file.js']
}
}
},
Place snippet within your grunt.initConfig({})
wrapper.
If we run autopolyfiller on a single JS file that contains the following code…
if (window.matchMedia("(min-width: 400px)").matches) {
alert('the view port is at least 400 pixels wide');
} else {
alert('the view port is less than 400 pixels wide');
}
matchmedia.js
autopolyfiller will generate a matchmedia polyfill for us based on our browser support stated in our Gruntfile that looks like this…
if (typeof window.matchMedia === "undefined") {
// Window.prototype.matchMedia
(function () {
function evalQuery(window, query) {
return new Function('media', 'try{ return !!(%s) }catch(e){ return false }'
.replace('%s', query||'true')
.replace(/^onlys+/, '')
.replace(/(device)-([w.]+)/g, '$1.$2')
.replace(/([w.]+)s*:/g, 'media.$1 ===')
.replace(/min-([w.]+)s*===/g, '$1 >=')
.replace(/max-([w.]+)s*===/g, '$1 <=')
.replace(/all|screen/g, '1')
.replace(/print/g, '0')
.replace(/,/g, '||')
.replace(/and/g, '&&')
.replace(/dpi/g, '')
.replace(/(d+)(cm|em|in|mm|pc|pt|px|rem)/, function ($0, $1, $2) {
return $1 * (
$2 === 'cm' ? 0.3937 * 96 : (
$2 === 'em' || $2 === 'rem' ? 16 : (
$2 === 'in' ? 96 : (
$2 === 'mm' ? 0.3937 * 96 / 10 : (
$2 === 'pc' ? 12 * 96 / 72 : (
$2 === 'pt' ? 96 / 72 : 1
)
)
)
)
)
);
})
)({
width: window.innerWidth,
height: window.innerHeight,
orientation: window.orientation || 'landscape',
device: {
width: window.screen.width,
height: window.screen.height,
orientation: window.screen.orientation || window.orientation || 'landscape'
}
});
}
function MediaQueryList() {
this.matches = false;
this.media = 'invalid';
}
MediaQueryList.prototype.addListener = function addListener(listener) {
this.addListener.listeners.push(listener);
};
MediaQueryList.prototype.removeListener = function removeListener(listener) {
this.addListener.listeners.splice(this.addListener.listeners.indexof(listener), 1);
};
window.matchMedia = Window.prototype.matchMedia = function matchMedia(query) {
var
window = this,
list = new MediaQueryList();
if (0===arguments.length) {
throw new TypeError('Not enough arguments to window.matchMedia');
}
list.media = String(query);
list.matches = evalQuery(window, list.media);
list.addListener.listeners = [];
window.addEventListener('resize', function () {
var listeners = [].concat(list.addListener.listeners), matches = evalQuery(window, list.media);
if (matches != list.matches) {
list.matches = matches;
for (var index = 0, length = listeners.length; index < length; ++index) {
listeners[index].call(window, list);
}
}
});
return list;
};
})();
}
matchmedia-polyfill-result.js
If you fancy checking out the autopolyfiller code in more depth Mikhail Davydov has it hosted on GitHub along with a handy Example Page.
This result means we can now add a single and very small file for a matchmedia polyfill to our project without worrying about fallbacks. If you're a gulp user then you're covered as well with gulp-autopolyfiller.
Enjoy auto generating your JavaScript polyfills folks.