Skip to content

Commit

Permalink
chore: ability to add external libs (axios)
Browse files Browse the repository at this point in the history
chore: ability to add external libs (axios)
  • Loading branch information
jeeyyy committed Jul 12, 2018
1 parent 4117331 commit 0957dab
Show file tree
Hide file tree
Showing 9 changed files with 134 additions and 9 deletions.
3 changes: 3 additions & 0 deletions .eslintignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
build/tasks/generate-imports.js
!lib/core/imports/index.js
lib/core/imports/*.js
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ typings/axe-core/axe-core-tests.js
doc/rule-descriptions.*.md
package-lock.json
.DS_Store
!lib/core/imports/index.js
lib/core/imports/*.js

# running circleci locally to verify build, ignoring relevant files
# if circle and docker is configured locally (copy circle.yml to .circleci/config.yml) - run -> circleci build
Expand Down
7 changes: 7 additions & 0 deletions Gruntfile.js
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,12 @@ module.exports = function(grunt) {
dest: 'tmp/commons.js'
}
},
'generate-imports': {
// list of external dependencies, which needs to be added to axe.imports object
data: {
axios: './node_modules/axios/dist/axios.js'
}
},
'aria-supported': {
data: {
entry: 'lib/commons/aria/index.js',
Expand Down Expand Up @@ -368,6 +374,7 @@ module.exports = function(grunt) {

grunt.registerTask('build', [
'clean',
'generate-imports',
'eslint',
'validate',
'concat:commons',
Expand Down
84 changes: 84 additions & 0 deletions build/tasks/generate-imports.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
/*eslint-env node */
const UglifyJS = require('uglify-js');

module.exports = grunt => {
grunt.registerMultiTask(
'generate-imports',
'Task for generating an axe.imports module with external dependencies.',
function() {
// Convenience method that utilises uglifyjs tree-transformer to unwrap umd module resolver
const removeUMD = new UglifyJS.TreeTransformer(node => {
if (node.body[0].body.args.length <= 0) {
throw new Error('Not a UMD wrapper as arguments are missing.');
}

// the last (or only) argument in umd resolver is the factory to be mounted
const umdFactory =
node.body[0].body.args[node.body[0].body.args.length - 1];
const funcCall = new UglifyJS.AST_Call({
expression: umdFactory,
args: umdFactory.argnames // pass arguments into self invoking func
});
const statement = new UglifyJS.AST_SimpleStatement({
body: funcCall,
start: {
comments_before: node.start.comments_before // bring over comments
}
});
return new UglifyJS.AST_Toplevel({ body: [statement] });
});

// Convenience method that uses uglifyjs to parse a given code and run a transformer
const unwrappedCode = (sourceCode, cb) => {
try {
const unWrappedCode = UglifyJS.parse(sourceCode)
.transform(removeUMD)
.print_to_string({ comments: true });
cb(null, unWrappedCode);
} catch (e) {
cb(e, null);
}
};

const hasUmdWrapper = sourceCode => {
return (
/typeof exports/.test(sourceCode) &&
/typeof define/.test(sourceCode) &&
/typeof module/.test(sourceCode)
);
};

const writeLibrary = (libName, factory) => {
const lib = `axe.imports["${libName}"] = ${factory}`;
const location = `./lib/core/imports/${libName}.js`;
grunt.file.write(location, lib);
};

/**
* Process a given library to unwrapped UMD module if exists, and return the factory
* @param {string} libName name of the library
* @param {string} sourceUrl path to the distributable of the library
*/
const processImport = (libName, sourceUrl) => {
const sourceCode = grunt.file.read(sourceUrl);
if (hasUmdWrapper(sourceCode)) {
unwrappedCode(sourceCode, (err, factory) => {
if (err) {
// running uglifyjs transform in a try block, this is to catch any errors from the transform.
throw new Error(err);
}
writeLibrary(libName, factory);
});
} else {
// assumption is that the library returns an IIFE
writeLibrary(libName, sourceCode);
}
};

// Iterate through each library to import and process the code
Object.keys(this.data).forEach(key => {
processImport(key, this.data[key]);
});
}
);
};
7 changes: 7 additions & 0 deletions lib/core/imports/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
/**
* Namespace for imports which holds globals of external dependencies.
* @namespace imports
* @memberof axe
*/

axe.imports = {};
4 changes: 3 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@
"devDependencies": {
"angular-precommit": "^1.0.3",
"aria-query": "^3.0.0",
"axios": "^0.18.0",
"babel-core": "^6.26.0",
"babel-plugin-transform-object-rest-spread": "^6.6.5",
"babel-polyfill": "^6.7.4",
Expand Down Expand Up @@ -104,7 +105,8 @@
"revalidator": "~0.3.1",
"selenium-webdriver": "~3.6.0",
"sri-toolbox": "^0.2.0",
"standard-version": "^4.2.0"
"standard-version": "^4.2.0",
"uglify-js": "^3.4.4"
},
"dependencies": {},
"lint-staged": {
Expand Down
12 changes: 6 additions & 6 deletions test/integration/full/umd/umd-define.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,18 @@
describe('UMD define', function() {
'use strict';

it('calls define and passes it axe', function() {
assert.equal(defineCalls.length, 1);
it('should have atleast one umd global', function() {
assert.isAtLeast(defineCalls.length, 1);
});

var call = defineCalls[0];
it('calls define and passes it axe', function() {
var call = defineCalls[defineCalls.length - 1];
assert.isFunction(call[2]);
assert.strictEqual(call[2](), axe);
});

it('defines module name as axe-core', function() {
assert.equal(defineCalls.length, 1);

var call = defineCalls[0];
var call = defineCalls[defineCalls.length - 1];
assert.equal(call[0], 'axe-core');
});
});
4 changes: 4 additions & 0 deletions test/integration/full/umd/umd-module-exports.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,8 @@ describe('UMD module.export', function() {
it('registers axe to module.exports', function() {
assert.strictEqual(module.exports, axe);
});

it('should ensure axe source includes axios', function() {
assert.isTrue(axe.source.includes(axe.imports.axios.toString()));
});
});
20 changes: 18 additions & 2 deletions test/integration/full/umd/umd-window.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,23 @@
describe('UMD window', function() {
'use strict';

it('exposes axe as a property of window', function() {
assert.strictEqual(window.axe, axe);
it('should expose axe as a property of window', function() {
assert.property(window, 'axe');
});

it('should ensure axe has prototype chained keys', function() {
assert.hasAnyKeys(axe, ['utils', 'commons', 'core']);
});

it('should expose not expose axios as a property of window', function() {
assert.notProperty(window, 'axios');
});

it('should ensure axios is a mounted to axe.imports', function() {
assert.hasAnyKeys(axe.imports, ['axios']);
});

it('should ensure axios has prototype chained keys', function() {
assert.hasAnyKeys(axe.imports.axios, ['get', 'request', 'options', 'post']);
});
});

0 comments on commit 0957dab

Please sign in to comment.