diff --git a/build/amdclean.min.js b/build/amdclean.min.js index 2a43dfe..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,s){return t({esprima:n,estraverse:r,escodegen:i,underscore:s},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,s=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(s)&&(r=s(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,s,a,o;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)&&(s=_.where(r.body.body,{type:"ReturnStatement"})[0],_.isObject(s)&&_.isObject(s.argument)&&"FunctionExpression"===s.argument.type&&(o=s.argument,_.isObject(o.body)&&_.isArray(o.body.body)&&(a=_.where(o.body.body,{type:"ReturnStatement"})[0],_.isObject(a.argument)&&_.isObject(a.argument.right)&&_.isObject(a.argument.right.property)&&a.argument.right.property.name&&(n={type:"MemberExpression",computed:!1,object:{type:"Identifier",name:"window"},property:{type:"Identifier",name:a.argument.right.property.name}}))))),n=n||e.moduleReturnValue}(),i=publicAPI.options,s=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 s},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}(),s=e.dependencyNames,a=publicAPI.options,o=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:s}}(),c=function(){return a.globalObject===!0&&a.globalObjectName?{type:"ExpressionStatement",expression:{type:"AssignmentExpression",operator:"=",left:{type:"MemberExpression",computed:!0,object:{type:"Identifier",name:a.globalObjectName},property:{type:"Literal",value:t,raw:""+t}},right:o}}:{type:"VariableDeclaration",declarations:[{type:"VariableDeclarator",id:{type:"Identifier",name:t},init:o}],kind:"var"}}();return c},convertToFunctionExpression:function(e){var t=e.isDefine,n=e.isRequire,r=!1,i=(e.node,e.moduleName),s=e.dependencies,a=s.length,o=publicAPI.options,c=function(){for(var e,t=[],n=-1;++n1||s&&"Identifier"===s.type)return o;o=s,r=!0,o.params&&(a=o.params.length)}}else o&&"FunctionExpression"===o.type&&o.body&&_.isArray(o.body.body)&&0===o.body.body.length&&(o={type:"Identifier",name:"undefined"},a=0);return o}(),l=function(){var e=[];return p&&p.body&&_.isArray(p.body.body)&&(e=_.where(p.body.body,{type:"ReturnStatement"}),e.length)?!0:!1}(),u=!1,d=function(){for(var e,t,n=[],r=-1,i=p.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 = 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); + } + + return memo; + }, []); + return normalizedSegments.join('/'); + }, + baseName = function(path) { + var segments = path.split('/'); + + segments.pop(); + return segments.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, isOptimized = false, node = obj.node, moduleName = obj.moduleName, + moduleId = obj.moduleId, dependencies = obj.dependencies, depLength = dependencies.length, options = publicAPI.options, @@ -577,7 +622,7 @@ iterator = -1, currentName; while(++iterator < depLength) { - currentName = dependencies[iterator]; + currentName = publicAPI.normalizeDependencyName(moduleId, dependencies[iterator]); if(options.globalObject === true && options.globalObjectName && currentName !== '{}') { deps.push({ 'type': 'MemberExpression', @@ -717,6 +762,7 @@ args, dependencies, moduleReturnValue, + moduleId, params, isDefine = publicAPI.isDefine(node), isRequire = publicAPI.isRequire(node), @@ -778,10 +824,12 @@ return depNames; }()); moduleReturnValue = isRequire ? args[1] : args[args.length - 1]; - moduleName = publicAPI.normalizeModuleName(node.expression['arguments'][0].value); + moduleId = node.expression['arguments'][0].value; + moduleName = publicAPI.normalizeModuleName(moduleId); params = { node: node, moduleName: moduleName, + moduleId: moduleId, dependencies: dependencies, moduleReturnValue: moduleReturnValue, isDefine: isDefine, @@ -968,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') { @@ -976,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 b04dfa2..1c2331d 100644 --- a/test/specs/convert.js +++ b/test/specs/convert.js @@ -37,6 +37,30 @@ describe('amdclean specs', function() { expect(cleanedCode).toBe(standardJavaScript); }); + 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);"; + + 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 } } }), @@ -119,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); }); @@ -337,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); }); @@ -355,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 } } }),