diff --git a/lib/helpers.js b/lib/helpers.js index 9469ae9..0c89483 100644 --- a/lib/helpers.js +++ b/lib/helpers.js @@ -28,6 +28,8 @@ helpers.removeExtraComma = function( node ) { .replace(/(var\s*)(,\s?)*/gi, "$1") // comma following a `var` .replace(/(,\s?)(\s*\})/gi, "$2") // comma ending an object literal .replace(/(\{\s*)(,\s?)/, "$1") // comma starting an object literal + .replace(/(\[\s*)(,\s?)/, "$1") // comma starting an array literal + .replace(/(,\s?)(\s*\])/gi, "$2") // comma ending an array literal .replace(/(,)\s?;/gi, ";"); // ending comma node.update( src ); }; diff --git a/lib/tree.js b/lib/tree.js index 751e671..0f2ff31 100644 --- a/lib/tree.js +++ b/lib/tree.js @@ -58,4 +58,12 @@ Tree.prototype.object = function() { return token; }; - +/** + * Add an array token to the queue + * @return {Var token} Selected variable token + */ +Tree.prototype.array = function() { + var token = new types.Array(); + this.tokens.push( token ); + return token; +}; diff --git a/lib/types/array.js b/lib/types/array.js new file mode 100644 index 0000000..b7670a1 --- /dev/null +++ b/lib/types/array.js @@ -0,0 +1,92 @@ +var helpers = require("../helpers"); +var Property = require("./_property"); + +/** + * Create an array token + * @constructor + */ +function ArrayObj() { + this.check = function() { return true; }; + this.action = function() {}; +} + +module.exports = ArrayObj; + +/** + * Standard run function used when tokens are applied. + * @param {AST node} node + * @return {null} + */ +ArrayObj.prototype.use = function( node ) { + if ( node.type === "ArrayExpression" && this.check(node) ) { + this.action( node ); + } +}; + +/** + * Scope to select only object assigned to a variable + * @param {Stirng} name variable name + * @return {types.Array} + */ +ArrayObj.prototype.assignedTo = function( name ) { + this.check = function( node ) { + return (node.parent.type === "VariableDeclarator" && node.parent.id.name === name); + }; + return this; +}; + +/** + * Scope to select only object passed to a function or a method + * TODO: Selecting method only work for two level deeps - should be better + * @param {String} name variable name + * @return {types.Array} + */ +ArrayObj.prototype.passedTo = function( name ) { + this.check = function( node ) { + var top = node.parent; + if ( top.type !== "CallExpression" ) return false; + if ( top.callee.type === "Identifier" && top.callee.name === name ) return true; + if ( top.callee.type === "MemberExpression" && + name === top.callee.object.name + "." + top.callee.property.name ) return true; + return false; + }; + return this; +}; + +/** + * push an element at the end of the array + * @param {String} item - Item to push source + * @return {types.Array} + */ +ArrayObj.prototype.push = function( item ) { + this.action = function( node ) { + var src = node.source().replace(/\]$/, "") + ", " + item + "]"; + node.update( src ); + helpers.removeExtraComma(node); + }; +}; + +/** + * unshift an element at the beginning of the array + * @param {String} item - Item to unshift source + * @return {types.Array} + */ +ArrayObj.prototype.unshift = function( item ) { + this.action = function( node ) { + var src = "[" + item + ", " + node.source().replace(/^\[/, ""); + node.update( src ); + helpers.removeExtraComma(node); + }; +}; + +/** + * unshift an element at the beginning of the array + * @param {String} item - Item to unshift source + * @return {types.Array} + */ +ArrayObj.prototype.pop = function( item ) { + this.action = function( node ) { + node.elements.pop(); + node.update(node.source()); + }; +}; diff --git a/lib/types/index.js b/lib/types/index.js index ec77a14..00b8867 100644 --- a/lib/types/index.js +++ b/lib/types/index.js @@ -1,2 +1,3 @@ module.exports.Var = require("./var"); module.exports.Object = require("./object"); +module.exports.Array = require("./array"); diff --git a/test/specs/types/array.js b/test/specs/types/array.js new file mode 100644 index 0000000..c896326 --- /dev/null +++ b/test/specs/types/array.js @@ -0,0 +1,64 @@ +var expect = require("chai").expect; +var Tree = require("../../../lib/tree"); + +describe("array modification API", function() { + it("should select array assigned to a variable", function() { + var src = "var foo = ['foo'];"; + var tree = new Tree(src); + tree.array().assignedTo("foo"); + tree.action = function( node ) { + expect(node.source()).to.equal("['foo']"); + }; + tree.toString(); + }); + + it("should select array passed to a function", function() { + var src = "foo(['doe']);"; + var tree = new Tree(src); + tree.array().passedTo("foo"); + tree.action = function( node ) { + expect(node.source()).to.equal("['doe']"); + }; + tree.toString(); + }); + + it("should select array passed to a method", function() { + var src = "grunt.init(['john']);"; + var tree = new Tree(src); + tree.array().passedTo("grunt.init"); + tree.action = function( node ) { + expect(node.source()).to.equal("['john']"); + }; + tree.toString(); + }); + + it("should push an item at the end of an array", function() { + var tree = new Tree("var a = ['bar'];"); + tree.array().push("'foo'"); + expect(tree.toString()).to.equal("var a = ['bar', 'foo'];"); + }); + + it("should push an item in an empty array", function() { + var tree = new Tree("var a = [];"); + tree.array().push("'foo'"); + expect(tree.toString()).to.equal("var a = ['foo'];"); + }); + + it("should unshift an item at the beginning of an array", function() { + var tree = new Tree("var a = ['bar'];"); + tree.array().unshift("'foo'"); + expect(tree.toString()).to.equal("var a = ['foo', 'bar'];"); + }); + + it("should unshift an item at the beginning of an empty array", function() { + var tree = new Tree("var a = [];"); + tree.array().unshift("'foo'"); + expect(tree.toString()).to.equal("var a = ['foo'];"); + }); + + it("should pop an element the end of an array", function() { + var tree = new Tree("var a = ['bar', 'foo'];"); + tree.array().pop(); + expect(tree.toString()).to.equal("var a = ['bar'];"); + }); +});