Socket
Socket
Sign inDemoInstall

amdextract

Package Overview
Dependencies
2
Maintainers
1
Versions
31
Alerts
File Explorer

Advanced tools

Install Socket

Detect and block malicious and high-risk dependencies

Install

Comparing version 2.0.3 to 2.1.1

328

index.js

@@ -11,23 +11,96 @@ /*

var esprima = require('esprima');
var esprima = require('esprima'),
estraverse = require('estraverse');
var toString = Object.prototype.toString,
ArrayProto = Array.prototype;
var defineRegExp = /(?:\/[\*\/]\s*exceptsPaths\s*\:\s*([^]+?)\s*(?:(?:\*\/)|(?:[\r\n]+)))?\s*define\s*\(\s*(?:['"](.*)['"]\s*,\s*)?(?:\[\s*([^]*?)\s*\]\s*,)?\s*function\s*\(\s*([^]*?)\s*\)\s*\{/gm;
var commentRegExp = /(?:([^\\])(\/\*[^]*?\*\/))|(?:([^\\])(\/\/.*?)$)/gm;
var commaRegExp = /(\s*),(\s*)/;
var toString = Object.prototype.toString;
function traverse(object, visitor) {
var key, child;
var key, child,
result = visitor(object);
if (visitor(object)) {
return true;
if (result || result === false) {
return result;
}
if (object.type === 'FunctionExpression' || object.type === 'FunctionDeclaration') {
var params = object.params;
for (key in object) {
if (object.hasOwnProperty(key)) {
child = object[key];
if (typeof child === 'object' && child !== null) {
child.key = key;
if (result = traverse(child, visitor)) {
return result;
}
}
}
}
if (traverse(object.body, function(obj) {
if (visitor(obj)) {
return false;
}
function getModules(parsedCode) {
var modules = [],
comments = parsedCode.comments,
tokens = parsedCode.tokens;
traverse(parsedCode, function(object) {
if (object.type === 'ExpressionStatement') {
var expression = object.expression;
if (expression.type === 'CallExpression' && expression.callee &&
expression.callee.type === 'Identifier' && expression.callee.name === 'define') {
expression.callee = estraverse.attachComments(expression.callee, comments, tokens);
var module = {},
leadingComments = expression.callee.leadingComments,
exceptsPaths = [],
id, paths, pathsIndex, callback, callbackIndex;
if (leadingComments) {
leadingComments.forEach(function(leadingComment) {
var matches = /^\s*exceptsPaths\s*:\s*(\w+(?:\s*,\s*\w+)*)\s*$/m.exec(leadingComment.value);
if (matches) {
Array.prototype.push.apply(exceptsPaths, matches[1].split(/\s*,\s*/));
}
});
}
module.exceptsPaths = exceptsPaths;
id = expression.arguments[0];
if (id && id.type === 'Literal') {
module.id = id;
}
pathsIndex = module.id ? 1 : 0;
paths = expression.arguments[pathsIndex];
if (paths && paths.type === 'ArrayExpression') {
module.paths = paths.elements;
}
callbackIndex = pathsIndex + 1;
callback = expression.arguments[callbackIndex];
if (callback && callback.type === 'FunctionExpression') {
module.dependencies = callback.params;
module.body = callback.body;
}
modules.push(module);
}
}
});
return modules;
}
function findUseage(variable, parsedCode) {
return traverse(parsedCode, function(object) {
if (object.type === 'FunctionExpression' || object.type === 'FunctionDeclaration') {
var params = object.params, obj;
if (obj = findUseage(variable, object.body)) {
for (var i = 0, length = params.length; i < length; i++) {

@@ -41,61 +114,57 @@ var param = params[i];

if (i === length) {
return true;
return obj;
}
}
})) {
return true;
// Do not traverse function body.
return false;
} else if (object.type === 'Identifier' && object.name === variable &&
object.key !== 'property' && object.key !== 'id') {
return object;
}
} else {
for (key in object) {
if (object.hasOwnProperty(key)) {
child = object[key];
if (typeof child === 'object' && child !== null) {
if (key !== 'property' && key !== 'id' && key !== 'params') {
if (traverse(child, visitor)) {
return true;
};
}
}
}
}
}
return false;
});
}
function getModuleBody(text) {
for (var i = 0, counter = 0, len = text.length; i < len; ++i) {
if (text[i] === '{') {
++counter;
} else if (text[i] === '}') {
--counter;
function extendRange(range, source) {
var regEx = /[\s,]/,
start = range[0] - 1,
end = range[1],
commaVisited = false;
for (var char = source[start]; regEx.test(char); char = source[--start]) {
if (char === ',') {
commaVisited = true;
}
if (!counter) {
break;
}
}
return text.substring(1, i);
}
function removeComments(text) {
var comments = [];
if (text) {
text = text.replace(commentRegExp, function (match, chr1, comment1, chr2, comment2) {
var chr = chr1 || chr2;
var comment = comment1 || comment2;
comments.push(comment);
return chr;
});
if (!commaVisited) {
for (var char = source[end]; regEx.test(char); char = source[++end])
;
}
return { source: text, comments: comments };
return [start + 1, end];
}
function findUseage(variable, text) {
var parsedCode = esprima.parse('function wrapper(){' + text + '}').body[0].body;
function optimizeContent(content, rangesToRemove) {
var output = '',
start = 0;
return traverse(parsedCode, function(object) {
if (object.type === 'Identifier' && object.name === variable) {
return true;
rangesToRemove.forEach(function(range) {
range = extendRange(range, content);
if (range[0] > start) {
var tmp = content.substring(start, range[0]);
if (/[[(]/.test(output[output.length - 1])) {
tmp = tmp.replace(/^[,\s]*/, '');
}
output += tmp;
}
start = range[1];
});
output += content.substring(start);
return output;
}

@@ -111,8 +180,8 @@

function isException(exceptions, dependency) {
function isException(exceptions, item) {
return exceptions.some(function (exception) {
if (isString(exception)) {
return exception === dependency;
return exception === item;
} else if (isRegExp(exception)) {
return exception.test(dependency);
return exception.test(item);
}

@@ -122,19 +191,2 @@ });

function splitByComma(str) {
var result = [],
parts = str.split(commaRegExp),
tokensLength = (parts.length + 2) / 3;
for (var i = 0; i < tokensLength; i++) {
var index = 3 * i;
result.push({
before: parts[index - 1],
token: parts[index],
after: parts[index + 1]
});
}
return result;
}
module.exports.parse = function (content, options) {

@@ -145,97 +197,45 @@ options = options || {};

var results = [];
var parsedCode = esprima.parse(content, { range: true, comment: true, tokens: true }),
modules = getModules(parsedCode),
result = {},
ranges;
var output = content.replace(defineRegExp, function (match, exceptsPathsStr, moduleId, pathsStr, dependenciesStr, offset) {
var
// Unprocessed
text = content.substr(offset + match.length - 1),
result.results = modules.map(function(module) {
var moduleId = module.id,
paths = module.paths || [],
dependencies = module.dependencies || [],
unusedPaths,
unusedDependencies,
excepts = options.excepts,
exceptsPaths = module.exceptsPaths.concat(options.exceptsPaths);
// Module body without comments
source,
unusedDependencies = dependencies.filter(function(dependency, index) {
return !isException(excepts, dependency.name) &&
(index >= paths.length || !isException(exceptsPaths, paths[index].value)) &&
!findUseage(dependency.name, module.body);
});
paths, dependencies,
commentlessPathsStr, commentlessDependenciesStr,
unusedDependencies = [],
unusedPaths = [],
exceptsPaths = options.exceptsPaths,
excepts = options.excepts;
unusedPaths = unusedDependencies.map(function(dependency) {
return paths[dependencies.indexOf(dependency)];
}).concat(paths.slice(dependencies.length)).filter(function(path) {
return path && !isException(exceptsPaths, path.value);
});
if (exceptsPathsStr) {
exceptsPaths = options.exceptsPaths.concat(splitByComma(exceptsPathsStr).map(function (p) { return p.token; }));
}
commentlessPathsStr = removeComments(pathsStr).source;
commentlessDependenciesStr = removeComments(dependenciesStr).source;
paths = commentlessPathsStr ? splitByComma(commentlessPathsStr).map(function (p) {
return {
path: p.token.substr(1, p.token.length - 2),
quote: p.token[0],
before: p.before,
after: p.after
};
}) : [];
dependencies = commentlessDependenciesStr ? splitByComma(commentlessDependenciesStr) : [];
if (text) {
var rcResult = removeComments(text);
if (rcResult) {
source = getModuleBody(rcResult.source);
unusedDependencies = dependencies.filter(function (dependency) {
var index = dependencies.indexOf(dependency);
return !isException(excepts, dependency.token) &&
(index >= paths.length || !isException(exceptsPaths, paths[index].path)) &&
!findUseage(dependency.token, source);
});
unusedPaths = unusedDependencies.map(function (dependency) {
var index = dependencies.indexOf(dependency);
return index < paths.length ? paths[index] : void 0;
}).concat(paths.slice(dependencies.length)).filter(function(p) {
return p && !isException(exceptsPaths, p.path);
});
results.push({
moduleId: moduleId,
paths: paths.map(function (p) { return p.path; }),
unusedPaths: unusedPaths.map(function (p) { return p.path; }),
dependencies: dependencies.map(function (d) { return d.token; }),
unusedDependencies: unusedDependencies.map(function (d) { return d.token; })
});
}
}
if (options.removeUnusedDependencies) {
var usedDependencies = dependencies.filter(function (dependency) {
return unusedDependencies.indexOf(dependency) < 0;
});
var usedPaths = paths.filter(function (dependency) {
return unusedPaths.indexOf(dependency) < 0;
});
match = match.replace(pathsStr, usedPaths.map(function (p, index, array) {
var before = (index === 0 || !p.before) ? '' : p.before;
var after = (index === array.length || !p.after) ? '' : p.after;
return before + p.quote + p.path + p.quote + after;
}).join(','))
.replace(dependenciesStr, usedDependencies.map(function (d, index, array) {
var before = (index === 0 || !d.before) ? '' : d.before;
var after = (index === array.length || !d.after) ? '' : d.after;
return before + d.token + after;
}).join(','));
ranges = [];
ArrayProto.push.apply(ranges, unusedPaths.map(function(path) { return path.range; }));
ArrayProto.push.apply(ranges, unusedDependencies.map(function(dependency) { return dependency.range; }));
}
return match;
return {
moduleId: moduleId ? moduleId.value : void 0,
paths: paths.map(function(path) { return path.value; }),
dependencies: dependencies.map(function(dep) { return dep.name; }),
unusedPaths: unusedPaths.map(function(path) { return path.value; }),
unusedDependencies: unusedDependencies.map(function(dep) { return dep.name; })
};
});
var result = {
results: results
};
if (options.removeUnusedDependencies) {
result.optimizedContent = output;
result.optimizedContent = optimizeContent(content, ranges);
}

@@ -242,0 +242,0 @@

{
"name": "amdextract",
"version": "2.0.3",
"description": "Extracts AMD modules, their parts and an optimized output without unused dependencies. (Uses AST to find out unused dependencies)",
"version": "2.1.1",
"description": "Use AST to extract AMD modules, their parts and an optimized output without unused dependencies.",
"main": "index.js",

@@ -40,4 +40,5 @@ "scripts": {

"dependencies": {
"esprima": "~1.2.2"
"esprima": "~1.2.2",
"estraverse": "~1.5.1"
}
}
# amdextract [![Build Status](https://travis-ci.org/mehdishojaei/amdextract.png)](https://travis-ci.org/mehdishojaei/amdextract)
Extracts AMD modules, their parts and an optimized output without unused dependencies.
(Uses AST to find out unused dependencies)
Use AST to extract AMD modules, their parts and an optimized output without unused dependencies.

@@ -121,2 +120,3 @@ ## example

## Release History
* 2014-08-16   v2.1.0   Entierly use AST.
* 2014-07-21   v2.0.3   Fix an issue related to comment detection.

@@ -123,0 +123,0 @@ * 2014-07-21   v2.0.2   Fix an issue related to RegExp literals.

@@ -5,3 +5,3 @@ // General test for all cases.

define('name', ["p2"
, 'p3', "t5" , 'm6'
, 'p3', 'p4', "t1" , 'm1'

@@ -12,14 +12,28 @@ ,

'm7'], function (b
, c) {
'm2'], function (b
, c, d) {
/**
* a.fetch() must not be encountered as code.
*/
b.fetch();
// c.fetch() must not be encountered as code.
function test(a) {
b.a();
function test1(a) {
return a;
}
function test2() {
function test3(a) {
return a;
}
}
function test4() {
function test5() {
return d;
}
}
var result = (function(a) {

@@ -29,3 +43,9 @@ return a;

var result = (function(a) {
return (function(a) {
return a;
})(a);
})(b);
var regEx = /\/download\//i;
});

@@ -6,4 +6,4 @@ // General test for all cases.

"p2"
, 'p3',
"p4", "t5" , 'm6'
, 'p3', 'p4',
"p5", "t1" , 'm1'

@@ -14,14 +14,28 @@ ,

'm7'], function (a , b
, c) {
'm2'], function (a , b
, c, d) {
/**
* a.fetch() must not be encountered as code.
*/
b.fetch();
// c.fetch() must not be encountered as code.
function test(a) {
b.a();
function test1(a) {
return a;
}
function test2() {
function test3(a) {
return a;
}
}
function test4() {
function test5() {
return d;
}
}
var result = (function(a) {

@@ -31,3 +45,9 @@ return a;

var result = (function(a) {
return (function(a) {
return a;
})(a);
})(b);
var regEx = /\/download\//i;
});

@@ -94,2 +94,18 @@ var amdextract = require('..');

describe('all paths are unused except three', function() {
var output = amdextract.parse(
"define('name', ['p1', 'p2', 'p3', 'p4', 'p5', 'p6', 'p7', 'p8'], function(a, b, c, d, e, f, g, h) { return d.concat(g); })",
{ removeUnusedDependencies: true }
);
var result = output.results[0];
it('.moduleId', function() { should(result.moduleId).equal('name'); });
it('.paths', function() { result.paths.should.be.eql(['p1', 'p2', 'p3', 'p4', 'p5', 'p6', 'p7', 'p8']); });
it('.dependencies', function() { result.dependencies.should.be.eql(['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h']); });
it('.unusedPaths', function() { result.unusedPaths.should.be.eql(['p1', 'p2', 'p3', 'p5', 'p6', 'p8']); });
it('.unusedDependencies', function() { result.unusedDependencies.should.be.eql(['a', 'b', 'c', 'e', 'f', 'h']); });
it('.optimizedContent', function() { should(output.optimizedContent).be.equal(
"define('name', ['p4', 'p7'], function(d, g) { return d.concat(g); })"
); });
});
describe('number of paths is grater than number of variables', function() {

@@ -144,9 +160,9 @@ var output = amdextract.parse(

describe('general test', function() {
var output = parse('sample', { removeUnusedDependencies: true, exceptsPaths: ['t5', /^m/] });
var output = parse('sample', { removeUnusedDependencies: true, exceptsPaths: ['t1', /^m/] });
var result = output.results[0];
var optimizedContent = read('sample-optimized');
it('.moduleId', function() { should(result.moduleId).equal('name'); });
it('.paths', function() { result.paths.should.be.eql(['p1', 'p2', 'p3', 'p4', 't5', 'm6', 'm7']); });
it('.dependencies', function() { result.dependencies.should.be.eql(['a', 'b', 'c']); });
it('.unusedPaths', function() { result.unusedPaths.should.be.eql(['p1', 'p4']); });
it('.paths', function() { result.paths.should.be.eql(['p1', 'p2', 'p3', 'p4', 'p5', 't1', 'm1', 'm2']); });
it('.dependencies', function() { result.dependencies.should.be.eql(['a', 'b', 'c', 'd']); });
it('.unusedPaths', function() { result.unusedPaths.should.be.eql(['p1', 'p5']); });
it('.unusedDependencies', function() { result.unusedDependencies.should.be.eql(['a']); });

@@ -153,0 +169,0 @@ it('.optimizedContent', function() { should(output.optimizedContent).be.equal(optimizedContent); });

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc