diff --git a/build/amdclean.min.js b/build/amdclean.min.js index 0daa2e2..39e7987 100644 --- a/build/amdclean.min.js +++ b/build/amdclean.min.js @@ -1 +1 @@ -!function(e,t){"use strict";"function"==typeof define&&define.amd?(t.env="undefined"!=typeof exports?"node":"web",t.amd=!0,define(["esprima","estraverse","escodegen","underscore"],function(n,r,i,a){return t({esprima:n,estraverse:r,escodegen:i,underscore:a},e)})):"undefined"!=typeof exports?(t.env="node",module.exports=t(null,e)):(t.env="web",e.amdclean=t(null,e))}(this,function cleanamd(amdDependencies,scope){"use strict";amdDependencies=amdDependencies||{};var codeEnv=cleanamd.env,that=scope,esprima=function(){return cleanamd.amd&&amdDependencies.esprima&&amdDependencies.esprima.parse?amdDependencies.esprima:that&&that.esprima&&that.esprima.parse?that.esprima:"node"===codeEnv?require("esprima"):void 0}(),estraverse=function(){return cleanamd.amd&&amdDependencies.estraverse&&amdDependencies.estraverse.traverse?amdDependencies.estraverse:that&&that.estraverse&&that.estraverse.traverse?that.estraverse:"node"===codeEnv?require("estraverse"):void 0}(),escodegen=function(){return cleanamd.amd&&amdDependencies.escodegen&&amdDependencies.escodegen.generate?amdDependencies.escodegen:that&&that.escodegen&&that.escodegen.generate?that.escodegen:"node"===codeEnv?require("escodegen"):void 0}(),_=function(){return cleanamd.amd&&amdDependencies.underscore?amdDependencies.underscore:that&&that._?that._:"node"===codeEnv?require("lodash"):void 0}(),fs="node"===codeEnv?require("fs"):{},publicAPI={VERSION:"1.2.1",defaultOptions:{code:"",filePath:"",globalModules:[],globalObject:!1,globalObjectName:"amdclean",esprima:{comment:!0,loc:!0,range:!0,tokens:!0},escodegen:{comment:!0},commentCleanName:"amdclean",ignoreModules:[],removeModules:[],removeAllRequires:!1,removeUseStricts:!0,shimOverrides:{},rememberGlobalObject:!0,prefixMode:"standard",prefixTransform:function(e){return e},wrap:{start:"",end:""}},env:codeEnv,errorMsgs:{emptyCode:"There is no code to generate the AST with",emptyAst:function(e){return"An AST is not being passed to the "+e+"() method"},invalidObject:function(e){return"An object is not being passed as the first parameter to the "+e+"() method"},lodash:"There is not an _.isPlainObject() method. Make sure you have included lodash (https://github.com/lodash/lodash).",esprima:"There is not an esprima.parse() method. Make sure you have included esprima (https://github.com/ariya/esprima).",estraverse:"There is not an estraverse.replace() method. Make sure you have included estraverse (https://github.com/Constellation/estraverse).",escodegen:"There is not an escodegen.generate() method. Make sure you have included escodegen (https://github.com/Constellation/escodegen)."},dependencyBlacklist:{require:!0,exports:!0,module:!0},readFile:function(e){return"node"!==publicAPI.env?"":fs.readFileSync(e,"utf8")},isDefine:function(e){var t=e.expression||{},n=t.callee;return _.isObject(e)&&"ExpressionStatement"===e.type&&_.isObject(t)&&"CallExpression"===t.type&&"Identifier"===n.type&&"define"===n.name},isRequire:function(e){var t=e.expression||{},n=t.callee;return _.isObject(e)&&"ExpressionStatement"===e.type&&_.isObject(t)&&"CallExpression"===t.type&&"Identifier"===n.type&&"require"===n.name},isRequireExpression:function(e){return"CallExpression"===e.type&&e.callee&&"require"===e.callee.name},isObjectExpression:function(e){return e&&_.isPlainObject(e)&&"ObjectExpression"===e.type},isFunctionExpression:function(e){return e&&_.isPlainObject(e)&&"FunctionExpression"===e.type},isFunctionCallExpression:function(e){return e&&_.isPlainObject(e)&&"CallExpression"===e.type&&_.isPlainObject(e.callee)&&"FunctionExpression"===e.callee.type},isUseStrict:function(e){return e&&_.isPlainObject(e)&&"Literal"===e.type&&"use strict"===e.value},isAMDConditional:function(e){if(e&&"IfStatement"!==e.type||!_.isObject(e.test)||!_.isObject(e.test.left)||_.isNull(e.test.left.value))return!1;var t={left:{operator:"typeof",argument:{type:"Identifier",name:"define"}},right:{type:"Literal",value:"function"}};return _.where(e.test,t).length||_.where([e.test],t).length||_.where(e.test.left,t).length||_.where([e.test.left],t).length},arrayContains:function(e,t){return _.isArray(e)?-1!==e.indexOf(t):!1},convertToCamelCase:function(e,t){return t=t||"_",e.replace(new RegExp(t+"(.)","g"),function(e,t){return t.toUpperCase()})},prefixReservedWords:function(name){var reservedWord=!1;try{name.length&&eval("var "+name+" = 1;")}catch(e){reservedWord=!0}return reservedWord===!0?"_"+name:name},normalizeModuleName:function(e){var t,n,r,i=publicAPI.options.prefixMode,a=publicAPI.options.prefixTransform;return e=e||"","{}"===e?e:(t=publicAPI.prefixReservedWords(e.replace(/\./g,"").replace(/[^A-Za-z0-9_$]/g,"_").replace(/^_+/,"")),n="camelCase"===i?publicAPI.convertToCamelCase(t):t,_.isFunction(a)&&(r=a(n),_.isString(r)&&r.length)?r:n)},returnExpressionIdentifier:function(e){return{type:"ExpressionStatement",expression:{type:"Identifier",name:e}}},convertToObjectDeclaration:function(e,t){var n=(e.node,e.moduleName),r=function(){var n,r,i,a,o,s;return"functionCallExpression"===t&&(n=e.moduleReturnValue,r=n.callee,i=r.params,i&&i.length&&_.isArray(i)&&_.where(i,{name:"global"})&&_.isObject(r.body)&&_.isArray(r.body.body)&&(a=_.where(r.body.body,{type:"ReturnStatement"})[0],_.isObject(a)&&_.isObject(a.argument)&&"FunctionExpression"===a.argument.type&&(s=a.argument,_.isObject(s.body)&&_.isArray(s.body.body)&&(o=_.where(s.body.body,{type:"ReturnStatement"})[0],_.isObject(o.argument)&&_.isObject(o.argument.right)&&_.isObject(o.argument.right.property)&&o.argument.right.property.name&&(n={type:"MemberExpression",computed:!1,object:{type:"Identifier",name:"window"},property:{type:"Identifier",name:o.argument.right.property.name}}))))),n=n||e.moduleReturnValue}(),i=publicAPI.options,a=function(){return i.globalObject===!0&&i.globalObjectName?{type:"ExpressionStatement",expression:{type:"AssignmentExpression",operator:"=",left:{type:"MemberExpression",computed:!0,object:{type:"Identifier",name:i.globalObjectName},property:{type:"Literal",value:n,raw:""+n}},right:r}}:{type:"VariableDeclaration",declarations:[{type:"VariableDeclarator",id:{type:"Identifier",name:n},init:r}],kind:"var"}}();return a},convertToIIFE:function(e){var t=e.callbackFuncParams,n=e.callbackFunc,r=e.dependencyNames;return{type:"ExpressionStatement",expression:{type:"CallExpression",callee:{type:"FunctionExpression",id:null,params:t,defaults:[],body:n.body,rest:n.rest,generator:n.generator,expression:n.expression},arguments:r}}},convertToIIFEDeclaration:function(e){var t=e.moduleName,n=e.callbackFuncParams,r=e.isOptimized,i=function(){var t=e.callbackFunc;return"Identifier"===t.type&&"undefined"!==t.name&&(t={type:"FunctionExpression",id:null,params:[],defaults:[],body:{type:"BlockStatement",body:[{type:"ReturnStatement",argument:{type:"CallExpression",callee:{type:"Identifier",name:t.name},arguments:[]}}]},rest:null,generator:!1,expression:!1}),t}(),a=e.dependencyNames,o=publicAPI.options,s=function(){return"Literal"===i.type||"Identifier"===i.type&&"undefined"===i.name||r===!0?i:{type:"CallExpression",callee:{type:"FunctionExpression",id:{type:"Identifier",name:""},params:n,defaults:[],body:i.body,rest:i.rest,generator:i.generator,expression:i.expression},arguments:a}}(),c=function(){return o.globalObject===!0&&o.globalObjectName?{type:"ExpressionStatement",expression:{type:"AssignmentExpression",operator:"=",left:{type:"MemberExpression",computed:!0,object:{type:"Identifier",name:o.globalObjectName},property:{type:"Literal",value:t,raw:""+t}},right:s}}:{type:"VariableDeclaration",declarations:[{type:"VariableDeclarator",id:{type:"Identifier",name:t},init:s}],kind:"var"}}();return c},normalizeDepId:function(e,t){if(!e||!t)return t;var n,r,i;return n=function(e){var t,n=e.split("/");return t=_.reduce(n,function(e,t){switch(t){case".":break;case"..":e.pop();break;default:e.push(t)}return e},[]),t.join("/")},r=function(e){var t=e.split("/");return t.pop(),t.join("/")},i=function(e){var t=e.split("/");return 1===t.length?!1:"."===t[0]||".."===t[0]},i(t)?n([r(e),t].join("/")):t},convertToFunctionExpression:function(e){var t=e.isDefine,n=e.isRequire,r=!1,i=(e.node,e.moduleName),a=e.moduleId,o=e.dependencies,s=o.length,c=publicAPI.options,p=function(){for(var e,t=[],n=-1;++n1||a&&"Identifier"===a.type)return o;o=a,r=!0,o.params&&(s=o.params.length)}}else o&&"FunctionExpression"===o.type&&o.body&&_.isArray(o.body.body)&&0===o.body.body.length&&(o={type:"Identifier",name:"undefined"},s=0);return o}(),l=function(){var e=[];return u&&u.body&&_.isArray(u.body.body)&&(e=_.where(u.body.body,{type:"ReturnStatement"}),e.length)?!0:!1}(),d=!1,m=function(){for(var e,t,n=[],r=-1,i=u.params||[];++r1||a&&"Identifier"===a.type)return o;o=a,r=!0,o.params&&(s=o.params.length)}}else o&&"FunctionExpression"===o.type&&o.body&&_.isArray(o.body.body)&&0===o.body.body.length&&(o={type:"Identifier",name:"undefined"},s=0);return o}(),u=function(){var e=[];return l&&l.body&&_.isArray(l.body.body)&&(e=_.where(l.body.body,{type:"ReturnStatement"}),e.length)?!0:!1}(),d=!1,m=function(){for(var e,t,n=[],r=-1,i=l.params||[];++r true + isRelativeFilePath: function(path) { + var segments = path.split('/'); + + return segments.length !== -1 && (segments[0] === '.' || segments[0] === '..'); + }, + // normalizeDependencyName + // ----------------------- + // Returns a normalized dependency name that handles relative file paths + 'normalizeDependencyName': function(moduleId, dep) { + if(!moduleId || !dep || !publicAPI.isRelativeFilePath(dep)) { return dep; } - var normalizePath, - baseName, - isRelative; + var normalizePath = function(path) { + var segments = path.split('/'), + normalizedSegments; - normalizePath = function(path) { - var segments = path.split('/'), - normalizedSegments; - - normalizedSegments = _.reduce(segments, function(memo, segment) { - switch(segment) { - case '.': - break; - case '..': - memo.pop(); - break; - default: - memo.push(segment); - } + normalizedSegments = _.reduce(segments, function(memo, segment) { + switch(segment) { + case '.': + break; + case '..': + memo.pop(); + break; + default: + memo.push(segment); + } - return memo; - }, []); - return normalizedSegments.join('/'); - }; + return memo; + }, []); + return normalizedSegments.join('/'); + }, baseName = function(path) { var segments = path.split('/'); segments.pop(); return segments.join('/'); }; - isRelative = function(path) { - var segments = path.split('/'); - - if(segments.length === 1) { - return false; - } - return (segments[0] === '.' || segments[0] === '..'); - }; - if(!isRelative(dep)) { - return dep; - } - return normalizePath([baseName(moduleId), dep].join('/')); + return normalizePath([baseName(moduleId), dep].join('/')); }, // convertToFunctionExpression // --------------------------- // Returns either an IIFE or variable declaration. - // Internally calls either convertToIIFE() or convertToIIFEDeclaration(). + // Internally calls either convertToIIFE() or convertToIIFEDeclaration() 'convertToFunctionExpression': function(obj) { var isDefine = obj.isDefine, isRequire = obj.isRequire, @@ -625,7 +622,7 @@ iterator = -1, currentName; while(++iterator < depLength) { - currentName = publicAPI.normalizeDepId(moduleId, dependencies[iterator]); + currentName = publicAPI.normalizeDependencyName(moduleId, dependencies[iterator]); if(options.globalObject === true && options.globalObjectName && currentName !== '{}') { deps.push({ 'type': 'MemberExpression', @@ -1019,6 +1016,7 @@ if(ast && _.isArray(ast.body)) { estraverse.replace(ast, { enter: function(node, parent) { + var normalizedModuleName; if(node === undefined || node.type === 'EmptyStatement') { _.each(parent.body, function(currentNode, iterator) { if(currentNode === undefined || currentNode.type === 'EmptyStatement') { @@ -1027,10 +1025,27 @@ }); } else if(publicAPI.isRequireExpression(node)) { if(node['arguments'] && node['arguments'][0] && node['arguments'][0].value) { - return { - 'type': 'Identifier', - 'name': publicAPI.normalizeModuleName(node['arguments'][0].value) - }; + normalizedModuleName = publicAPI.normalizeModuleName(node['arguments'][0].value); + if(options.globalObject === true && (options.globalObjectName && _.isString(options.globalObjectName) && options.globalObjectName.length)) { + return { + 'type': 'MemberExpression', + 'computed': true, + 'object': { + 'type': 'Identifier', + 'name': options.globalObjectName + }, + 'property': { + 'type': 'Literal', + 'value': normalizedModuleName, + 'raw': normalizedModuleName + } + }; + } else { + return { + 'type': 'Identifier', + 'name': normalizedModuleName + }; + } } else { return node; } diff --git a/test/specs/convert.js b/test/specs/convert.js index 8b91b94..1c2331d 100644 --- a/test/specs/convert.js +++ b/test/specs/convert.js @@ -37,7 +37,7 @@ describe('amdclean specs', function() { expect(cleanedCode).toBe(standardJavaScript); }); - it('should correctly normalize relative file paths in deps', function() { + it('should correctly normalize relative file paths dependencies', function() { var AMDcode = "define('./modules/example', ['./example1', './example2', '../example3'], function(one, two, three) {var test = true;});", cleanedCode = amdclean.clean({ code: AMDcode, escodegen: { format: { compact: true } } }), standardJavaScript = "var modules_example=function (one,two,three){var test=true;}(modules_example1,modules_example2,example3);"; @@ -45,6 +45,22 @@ describe('amdclean specs', function() { expect(cleanedCode).toBe(standardJavaScript); }); + it('should correctly normalize relative file paths dependencies with the globalObject option', function() { + var AMDcode = "define('./modules/example', ['./example1', './example2', '../example3'], function(one, two, three) {var test = true;});", + cleanedCode = amdclean.clean({ globalObject: true, rememberGlobalObject: false, code: AMDcode, escodegen: { format: { compact: true } } }), + standardJavaScript = "var amdclean={};amdclean['modules_example']=function (one,two,three){var test=true;}(amdclean['modules_example1'],amdclean['modules_example2'],amdclean['example3']);"; + + expect(cleanedCode).toBe(standardJavaScript); + }); + + it('should correctly normalize multi-level relative file paths dependencies', function() { + var AMDcode = "define('./foo/prototype/subModule/myModule', ['example1','example2', '/anotherModule/example3', '../../example4','../anotherModule/example5'], function(one, two, three, four, five) { var test = true;});", + cleanedCode = amdclean.clean({ code: AMDcode, escodegen: { format: { compact: true } } }), + standardJavaScript = "var foo_prototype_subModule_myModule=function (one,two,three,four,five){var test=true;}(example1,example2,anotherModule_example3,foo_example4,foo_prototype_anotherModule_example5);"; + + expect(cleanedCode).toBe(standardJavaScript); + }); + it('should correctly normalize multi-level relative file paths', function() { var AMDcode = "define('./foo/prototype/commonMethodName.js', ['example1', 'example2'], function(one, two) { var test = true;});", cleanedCode = amdclean.clean({ code: AMDcode, escodegen: { format: { compact: true } } }), @@ -127,8 +143,8 @@ describe('amdclean specs', function() { it('should support storing modules inside of a global object', function() { var AMDcode = "define('foo', ['require', 'exports', './bar'], function(require, exports){exports.bar = require('./bar');});", - cleanedCode = amdclean.clean({ globalObject: true, globalObjectName: 'yeabuddy', code: AMDcode, escodegen: { format: { compact: true } } }), - standardJavaScript = "var yeabuddy={};yeabuddy['foo']=function (require,exports,bar){exports.bar=bar;return exports;}({},{},yeabuddy['bar']);"; + cleanedCode = amdclean.clean({ globalObject: true, rememberGlobalObject: false, globalObjectName: 'yeabuddy', code: AMDcode, escodegen: { format: { compact: true } } }), + standardJavaScript = "var yeabuddy={};yeabuddy['foo']=function (require,exports,bar){exports.bar=yeabuddy['bar'];return exports;}({},{},yeabuddy['bar']);"; expect(cleanedCode).toBe(standardJavaScript); }); @@ -345,8 +361,8 @@ describe('amdclean specs', function() { it('should convert object return values to a global object', function() { var AMDcode = "define('third', { exampleProp: 'This is an example' });", - cleanedCode = amdclean.clean({ globalObject: true, code: AMDcode, escodegen: { format: { compact: true } } }), - standardJavaScript = "amdclean['third']={exampleProp:'This is an example'};"; + cleanedCode = amdclean.clean({ globalObject: true, rememberGlobalObject: false, code: AMDcode, escodegen: { format: { compact: true } } }), + standardJavaScript = "var amdclean={};amdclean['third']={exampleProp:'This is an example'};"; expect(cleanedCode).toBe(standardJavaScript); }); @@ -363,6 +379,14 @@ describe('amdclean specs', function() { expect(cleanedCode).toBe(standardJavaScript); }); + it('should convert CommonJS require() calls correctly with the globalObject option', function() { + var AMDcode = "var example = require('anotherModule');", + cleanedCode = amdclean.clean({ code: AMDcode, globalObject: true, rememberGlobalObject: false, escodegen: { format: { compact: true } } }), + standardJavaScript = "var amdclean={};var example=amdclean['anotherModule'];"; + + expect(cleanedCode).toBe(standardJavaScript); + }); + it('should convert CommonJS require() calls with file paths', function() { var AMDcode = "var example = require('./anotherModule');", cleanedCode = amdclean.clean({ code: AMDcode, escodegen: { format: { compact: true } } }),