Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[WIP] feat(core): add -noPolyfill option to grunt build #1968

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 10 additions & 2 deletions Gruntfile.js
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ module.exports = function(grunt) {
{
expand: true,
cwd: 'lib/core',
src: ['**/*.js', '!imports/index.js'],
src: ['**/*.js', '!imports/index.js', '!polyfills/index.js'],
dest: 'tmp/core'
}
]
Expand Down Expand Up @@ -350,6 +350,10 @@ module.exports = function(grunt) {
cmd: 'node',
args: ['./build/imports-generator']
},
npm_run_polyfills: {
cmd: 'node',
args: ['./build/polyfills-generator']
},
npm_run_testHeadless: {
cmd: 'npm',
args: ['run', 'test:headless']
Expand All @@ -363,7 +367,11 @@ module.exports = function(grunt) {
'concat:commons',
'add-locale'
]);
grunt.registerTask('pre-build', ['clean', 'run:npm_run_imports']);
const preBuildTasks = ['clean', 'run:npm_run_imports'];
if (!grunt.option('noPolyfills')) {
preBuildTasks.push('run:npm_run_polyfills');
}
grunt.registerTask('pre-build', preBuildTasks);
grunt.registerTask('build', [
'pre-build',
'validate',
Expand Down
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,10 @@ Read the [documentation on contributing](CONTRIBUTING.md)

[List of projects using axe-core](doc/projects.md)

## Version Without Polyfills

To build a version of axe without polyfills, run `grunt --noPolyfills`.

## Acknowledgements

Thanks to Dulin Marat for his [css-selector-parser](https://www.npmjs.com/package/css-selector-parser) implementation which is included for shadow DOM support.
Expand Down
39 changes: 39 additions & 0 deletions build/polyfills-generator.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
const path = require('path');
const browserify = require('browserify');
const derequire = require('derequire');
const createFile = require('./shared/create-file');

const inputFile = path.join(
__dirname,
'..',
'lib',
'core',
'polyfills',
'index.js'
);
const outputFile = path.join(
__dirname,
'..',
'tmp',
'core',
'polyfills',
'index.js'
);

async function run() {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a clone of imports-generator - https://github.com/dequelabs/axe-core/blob/develop/build/imports-generator.js

I see no reason it has to be though. You should make this a common function, that when given a source and destination and the namespace to mount, it creates these files.

Why have two files with the same code one for creating axe.imports and one for creating axe.polyfills?

browserify(inputFile).bundle(async (err, result) => {
if (err) {
throw new Error('Cannot browserify polyfills', err);
}
try {
// Replace `require` calls with `_dereq_` in order not to confuse Cypress.js
result = derequire(result);
await createFile(outputFile, result);
} catch (error) {
throw new Error('Cannot write browserify generated polyfills', error);
}
});
}

// exec
run();
30 changes: 0 additions & 30 deletions lib/core/imports/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,36 +6,6 @@
* See - './build/imports-generator'
*/

/**
* Polyfill `Promise`
* Reference: https://www.npmjs.com/package/es6-promise
*/
if (!('Promise' in window)) {
require('es6-promise').polyfill();
}

/**
* Polyfill required TypedArray and functions
* Reference https://github.com/zloirock/core-js/
*/
if (!('Uint32Array' in window)) {
require('core-js/features/typed-array/uint32-array');
}
if (window.Uint32Array) {
if (!('some' in window.Uint32Array.prototype)) {
require('core-js/features/typed-array/some');
}
if (!('reduce' in window.Uint32Array.prototype)) {
require('core-js/features/typed-array/reduce');
}
}

/**
* Polyfill `WeakMap`
* Reference: https://github.com/polygonplanet/weakmap-polyfill
*/
require('weakmap-polyfill');

/**
* Namespace `axe.imports` which holds required external dependencies
*
Expand Down
262 changes: 262 additions & 0 deletions lib/core/polyfills/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,262 @@
/**
* Note:
* This file is run via browserify to pull in the required dependencies.
* See - './build/polyfills-generator'
*/

/**
* Polyfill `Promise`
* Reference: https://www.npmjs.com/package/es6-promise
*/
if (!('Promise' in window)) {
require('es6-promise').polyfill();
}

/**
* Polyfill required TypedArray and functions
* Reference https://github.com/zloirock/core-js/
*/
if (!('Uint32Array' in window)) {
require('core-js/features/typed-array/uint32-array');
}
if (window.Uint32Array) {
if (!('some' in window.Uint32Array.prototype)) {
require('core-js/features/typed-array/some');
}
if (!('reduce' in window.Uint32Array.prototype)) {
require('core-js/features/typed-array/reduce');
}
}

/**
* Polyfill `WeakMap`
* Reference: https://github.com/polygonplanet/weakmap-polyfill
*/
require('weakmap-polyfill');

/* eslint-disable */
/*
These pollyfills came directly from the ES Specification it's self
Contained within:
- Object.assign
- Array.prototype.find
*/
if (typeof Object.assign !== 'function') {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

another thought is, given we have core-js already above for reduce, can we remove the copy pasted polyfills below and swap them out to features from core-js

Eg: core-js(-pure)/es|stable|features/object/assign

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree here, but don't think that needs to change for this PR to land

(function() {
Object.assign = function(target) {
'use strict';
if (target === undefined || target === null) {
throw new TypeError('Cannot convert undefined or null to object');
}

var output = Object(target);
for (var index = 1; index < arguments.length; index++) {
var source = arguments[index];
if (source !== undefined && source !== null) {
for (var nextKey in source) {
if (source.hasOwnProperty(nextKey)) {
output[nextKey] = source[nextKey];
}
}
}
}
return output;
};
})();
}

if (!Array.prototype.find) {
Object.defineProperty(Array.prototype, 'find', {
value: function(predicate) {
if (this === null) {
throw new TypeError('Array.prototype.find called on null or undefined');
}
if (typeof predicate !== 'function') {
throw new TypeError('predicate must be a function');
}
var list = Object(this);
var length = list.length >>> 0;
var thisArg = arguments[1];
var value;

for (var i = 0; i < length; i++) {
value = list[i];
if (predicate.call(thisArg, value, i, list)) {
return value;
}
}
return undefined;
}
});
}

if (!Array.prototype.includes) {
Object.defineProperty(Array.prototype, 'includes', {
value: function(searchElement) {
'use strict';
var O = Object(this);
var len = parseInt(O.length, 10) || 0;
if (len === 0) {
return false;
}
var n = parseInt(arguments[1], 10) || 0;
var k;
if (n >= 0) {
k = n;
} else {
k = len + n;
if (k < 0) {
k = 0;
}
}
var currentElement;
while (k < len) {
currentElement = O[k];
if (
searchElement === currentElement ||
(searchElement !== searchElement && currentElement !== currentElement)
) {
// NaN !== NaN
return true;
}
k++;
}
return false;
}
});
}

// Production steps of ECMA-262, Edition 5, 15.4.4.17
// Reference: http://es5.github.io/#x15.4.4.17
if (!Array.prototype.some) {
Object.defineProperty(Array.prototype, 'some', {
value: function(fun) {
'use strict';

if (this == null) {
throw new TypeError('Array.prototype.some called on null or undefined');
}

if (typeof fun !== 'function') {
throw new TypeError();
}

var t = Object(this);
var len = t.length >>> 0;

var thisArg = arguments.length >= 2 ? arguments[1] : void 0;
for (var i = 0; i < len; i++) {
if (i in t && fun.call(thisArg, t[i], i, t)) {
return true;
}
}

return false;
}
});
}

if (!Array.from) {
Object.defineProperty(Array, 'from', {
value: (function() {
var toStr = Object.prototype.toString;
var isCallable = function(fn) {
return (
typeof fn === 'function' || toStr.call(fn) === '[object Function]'
);
};
var toInteger = function(value) {
var number = Number(value);
if (isNaN(number)) {
return 0;
}
if (number === 0 || !isFinite(number)) {
return number;
}
return (number > 0 ? 1 : -1) * Math.floor(Math.abs(number));
};
var maxSafeInteger = Math.pow(2, 53) - 1;
var toLength = function(value) {
var len = toInteger(value);
return Math.min(Math.max(len, 0), maxSafeInteger);
};

// The length property of the from method is 1.
return function from(arrayLike /*, mapFn, thisArg */) {
// 1. Let C be the this value.
var C = this;

// 2. Let items be ToObject(arrayLike).
var items = Object(arrayLike);

// 3. ReturnIfAbrupt(items).
if (arrayLike == null) {
throw new TypeError(
'Array.from requires an array-like object - not null or undefined'
);
}

// 4. If mapfn is undefined, then let mapping be false.
var mapFn = arguments.length > 1 ? arguments[1] : void undefined;
var T;
if (typeof mapFn !== 'undefined') {
// 5. else
// 5. a If IsCallable(mapfn) is false, throw a TypeError exception.
if (!isCallable(mapFn)) {
throw new TypeError(
'Array.from: when provided, the second argument must be a function'
);
}

// 5. b. If thisArg was supplied, let T be thisArg; else let T be undefined.
if (arguments.length > 2) {
T = arguments[2];
}
}

// 10. Let lenValue be Get(items, "length").
// 11. Let len be ToLength(lenValue).
var len = toLength(items.length);

// 13. If IsConstructor(C) is true, then
// 13. a. Let A be the result of calling the [[Construct]] internal method of C with an argument list containing the single item len.
// 14. a. Else, Let A be ArrayCreate(len).
var A = isCallable(C) ? Object(new C(len)) : new Array(len);

// 16. Let k be 0.
var k = 0;
// 17. Repeat, while k < len… (also steps a - h)
var kValue;
while (k < len) {
kValue = items[k];
if (mapFn) {
A[k] =
typeof T === 'undefined'
? mapFn(kValue, k)
: mapFn.call(T, kValue, k);
} else {
A[k] = kValue;
}
k += 1;
}
// 18. Let putStatus be Put(A, "length", len, true).
A.length = len;
// 20. Return A.
return A;
};
})()
});
}

if (!String.prototype.includes) {
String.prototype.includes = function(search, start) {
if (typeof start !== 'number') {
start = 0;
}
if (start + search.length > this.length) {
return false;
} else {
return this.indexOf(search, start) !== -1;
}
};
}
Loading