pax_global_header00006660000000000000000000000064130666152040014515gustar00rootroot0000000000000052 comment=4ccca36056d1b51f4af7c0d0e69462a248857844 node-falafel-2.1.0/000077500000000000000000000000001306661520400140325ustar00rootroot00000000000000node-falafel-2.1.0/.gitignore000066400000000000000000000000331306661520400160160ustar00rootroot00000000000000node_modules bundle* *.log node-falafel-2.1.0/.travis.yml000066400000000000000000000000441306661520400161410ustar00rootroot00000000000000language: node_js node_js: - 0.12 node-falafel-2.1.0/example/000077500000000000000000000000001306661520400154655ustar00rootroot00000000000000node-falafel-2.1.0/example/array.js000066400000000000000000000005061306661520400171420ustar00rootroot00000000000000var falafel = require('../'); var src = '(' + function () { var xs = [ 1, 2, [ 3, 4 ] ]; var ys = [ 5, 6 ]; console.dir([ xs, ys ]); } + ')()'; var output = falafel(src, function (node) { if (node.type === 'ArrayExpression') { node.update('fn(' + node.source() + ')'); } }); console.log(output); node-falafel-2.1.0/example/keyword.js000066400000000000000000000006421306661520400175110ustar00rootroot00000000000000var falafel = require('../'); var src = 'console.log(beep "boop", "BOOP");'; function isKeyword (id) { if (id === 'beep') return true; } var output = falafel(src, { isKeyword: isKeyword }, function (node) { if (node.type === 'UnaryExpression' && node.operator === 'beep') { node.update( 'String(' + node.argument.source() + ').toUpperCase()' ); } }); console.log(output); node-falafel-2.1.0/example/prompt.js000066400000000000000000000023041306661520400173430ustar00rootroot00000000000000var falafel = require('../'); var vm = require('vm'); var termExps = [ 'Identifier', 'CallExpression', 'BinaryExpression', 'UpdateExpression', 'UnaryExpression' ].reduce(function (acc, key) { acc[key] = true; return acc }, {}); function terminated (node) { for (var p = node; p.parent; p = p.parent) { if (termExps[p.type]) return true; } return false; } var src = '{"a":[2,~9,prompt(":d")],"b":4,"c":prompt("beep"),"d":6}'; var offsets = []; var output = falafel('(' + src + ')', function (node) { var isLeaf = node.parent && !terminated(node.parent) && terminated(node) ; if (isLeaf) { var s = node.source(); var prompted = false; var res = vm.runInNewContext('(' + s + ')', { prompt : function (x) { setTimeout(function () { node.update(x.toUpperCase()); }, Math.random() * 50); prompted = true; } }); if (!prompted) { var s_ = JSON.stringify(res); node.update(s_); } } }); setTimeout(function () { console.log(src); console.log('---'); console.log(output); }, 200); node-falafel-2.1.0/index.js000066400000000000000000000042271306661520400155040ustar00rootroot00000000000000var parse = require('acorn').parse; var isArray = require('isarray'); var objectKeys = require('object-keys'); var forEach = require('foreach'); module.exports = function (src, opts, fn) { if (typeof opts === 'function') { fn = opts; opts = {}; } if (src && typeof src === 'object' && src.constructor.name === 'Buffer') { src = src.toString(); } else if (src && typeof src === 'object') { opts = src; src = opts.source; delete opts.source; } src = src === undefined ? opts.source : src; if (typeof src !== 'string') src = String(src); if (opts.parser) parse = opts.parser.parse; var ast = parse(src, opts); var result = { chunks : src.split(''), toString : function () { return result.chunks.join('') }, inspect : function () { return result.toString() } }; var index = 0; (function walk (node, parent) { insertHelpers(node, parent, result.chunks); forEach(objectKeys(node), function (key) { if (key === 'parent') return; var child = node[key]; if (isArray(child)) { forEach(child, function (c) { if (c && typeof c.type === 'string') { walk(c, node); } }); } else if (child && typeof child.type === 'string') { walk(child, node); } }); fn(node); })(ast, undefined); return result; }; function insertHelpers (node, parent, chunks) { node.parent = parent; node.source = function () { return chunks.slice(node.start, node.end).join(''); }; if (node.update && typeof node.update === 'object') { var prev = node.update; forEach(objectKeys(prev), function (key) { update[key] = prev[key]; }); node.update = update; } else { node.update = update; } function update (s) { chunks[node.start] = s; for (var i = node.start + 1; i < node.end; i++) { chunks[i] = ''; } } } node-falafel-2.1.0/package.json000066400000000000000000000023711306661520400163230ustar00rootroot00000000000000{ "name": "falafel", "description": "transform the ast on a recursive walk", "version": "2.1.0", "repository": { "type": "git", "url": "git://github.com/substack/node-falafel.git" }, "main": "index.js", "keywords": [ "ast", "burrito", "source", "syntax", "traversal", "tree" ], "directories": { "example": "example", "test": "test" }, "scripts": { "coverage": "covert test/*.js", "test": "node --harmony test/bin/run.js test/*.js" }, "dependencies": { "acorn": "^5.0.0", "foreach": "^2.0.5", "isarray": "0.0.1", "object-keys": "^1.0.6" }, "devDependencies": { "acorn-jsx": "^3.0.0", "covert": "^1.1.0", "glob": "^6.0.4", "tape": "^4.0.0" }, "engines": { "node": ">=0.4.0" }, "license": "MIT", "author": { "email": "mail@substack.net", "name": "James Halliday", "url": "http://substack.net" }, "testling": { "browsers": { "chrome": [ "20.0" ], "firefox": [ "10.0", "15.0" ], "iexplore": [ "6.0", "7.0", "8.0", "9.0" ], "opera": [ "12.0" ], "safari": [ "5.1" ] }, "files": "test/*.js" } } node-falafel-2.1.0/readme.markdown000066400000000000000000000056171306661520400170440ustar00rootroot00000000000000# falafel Transform the [ast](http://en.wikipedia.org/wiki/Abstract_syntax_tree) on a recursive walk. [![browser support](http://ci.testling.com/substack/node-falafel.png)](http://ci.testling.com/substack/node-falafel) [![build status](https://secure.travis-ci.org/substack/node-falafel.png)](http://travis-ci.org/substack/node-falafel) This modules uses [acorn](https://npmjs.org/package/acorn) to create an AST from source code. ![falafel döner](http://substack.net/images/falafel.png) # example ## array.js Put a function wrapper around all array literals. ``` js var falafel = require('falafel'); var src = '(' + function () { var xs = [ 1, 2, [ 3, 4 ] ]; var ys = [ 5, 6 ]; console.dir([ xs, ys ]); } + ')()'; var output = falafel(src, function (node) { if (node.type === 'ArrayExpression') { node.update('fn(' + node.source() + ')'); } }); console.log(output); ``` output: ``` (function () { var xs = fn([ 1, 2, fn([ 3, 4 ]) ]); var ys = fn([ 5, 6 ]); console.dir(fn([ xs, ys ])); })() ``` # methods ``` js var falafel = require('falafel') ``` ## falafel(src, opts={}, fn) Transform the string source `src` with the function `fn`, returning a string-like transformed output object. For every node in the ast, `fn(node)` fires. The recursive walk is a pre-traversal, so children get called before their parents. Performing a pre-traversal makes it easier to write nested transforms since transforming parents often requires transforming all its children first. The return value is string-like (it defines `.toString()` and `.inspect()`) so that you can call `node.update()` asynchronously after the function has returned and still capture the output. Instead of passing a `src` you can also use `opts.source`. All of the `opts` will be passed directly to [acorn](https://npmjs.org/package/acorn). ## custom parser You may pass in an instance of acorn to the opts as `opts.parser` to use that version instead of the version of acorn packaged with this library. ```js var acorn = require('acorn-jsx'); falafel(src, {parser: acorn, plugins: { jsx: true }}, function(node) { // this will parse jsx }); ``` # nodes Aside from the regular [esprima](http://esprima.org) data, you can also call some inserted methods on nodes. Aside from updating the current node, you can also reach into sub-nodes to call update functions on children from parent nodes. ## node.source() Return the source for the given node, including any modifications made to children nodes. ## node.update(s) Transform the source for the present node to the string `s`. Note that in `'ForStatement'` node types, there is an existing subnode called `update`. For those nodes all the properties are copied over onto the `node.update()` function. ## node.parent Reference to the parent element or `null` at the root element. # install With [npm](http://npmjs.org) do: ``` npm install falafel ``` # license MIT node-falafel-2.1.0/test/000077500000000000000000000000001306661520400150115ustar00rootroot00000000000000node-falafel-2.1.0/test/array.js000066400000000000000000000014311306661520400164640ustar00rootroot00000000000000var falafel = require('../'); var test = require('tape'); test('array', function (t) { t.plan(5); var src = '(' + function () { var xs = [ 1, 2, [ 3, 4 ] ]; var ys = [ 5, 6 ]; g([ xs, ys ]); } + ')()'; var output = falafel(src, function (node) { if (node.type === 'ArrayExpression') { node.update('fn(' + node.source() + ')'); } }); var arrays = [ [ 3, 4 ], [ 1, 2, [ 3, 4 ] ], [ 5, 6 ], [ [ 1, 2, [ 3, 4 ] ], [ 5, 6 ] ], ]; Function(['fn','g'], output)( function (xs) { t.same(arrays.shift(), xs); return xs; }, function (xs) { t.same(xs, [ [ 1, 2, [ 3, 4 ] ], [ 5, 6 ] ]); } ); }); node-falafel-2.1.0/test/async.js000066400000000000000000000020141306661520400164610ustar00rootroot00000000000000var falafel = require('../'); var test = require('tape'); test('array', function (t) { t.plan(5); var src = '(function () {' + 'var xs = [ 1, 2, [ 3, 4 ] ];' + 'var ys = [ 5, 6 ];' + 'g([ xs, ys ]);' + '})()'; var pending = 0; var output = falafel(src, function (node) { if (node.type === 'ArrayExpression') { pending ++; setTimeout(function () { node.update('fn(' + node.source() + ')'); if (--pending === 0) check(); }, 50 * pending * 2); } }); var arrays = [ [ 3, 4 ], [ 1, 2, [ 3, 4 ] ], [ 5, 6 ], [ [ 1, 2, [ 3, 4 ] ], [ 5, 6 ] ], ]; function check () { Function([ 'fn', 'g' ], output)( function (xs) { t.same(arrays.shift(), xs); return xs; }, function (xs) { t.same(xs, [ [ 1, 2, [ 3, 4 ] ], [ 5, 6 ] ]); } ); } }); node-falafel-2.1.0/test/bin/000077500000000000000000000000001306661520400155615ustar00rootroot00000000000000node-falafel-2.1.0/test/bin/run.js000077500000000000000000000003701306661520400167260ustar00rootroot00000000000000var path = require('path'); var glob = require('glob'); for (var i = 2; i < process.argv.length; i++) { glob(process.argv[i], function (er, files) { files.forEach(function (file) { require(path.resolve(process.cwd(), file)); }); }); } node-falafel-2.1.0/test/custom-parser.js000066400000000000000000000017171306661520400201610ustar00rootroot00000000000000var falafel = require('../'); var acorn = require('acorn-jsx'); var test = require('tape'); test('custom parser', function (t) { var src = '(function() { var f = {a: "b"}; var a =
; })()'; var nodeTypes = [ 'Identifier', 'Identifier', 'Literal', 'Property', 'ObjectExpression', 'VariableDeclarator', 'VariableDeclaration', 'Identifier', 'Identifier', 'JSXSpreadAttribute', 'JSXIdentifier', 'Literal', 'JSXAttribute', 'JSXIdentifier', 'JSXOpeningElement', 'JSXIdentifier', 'JSXClosingElement', 'JSXElement', 'VariableDeclarator', 'VariableDeclaration', 'BlockStatement', 'FunctionExpression', 'CallExpression', 'ExpressionStatement', 'Program' ]; t.plan(nodeTypes.length); var output = falafel(src, {parser: acorn, ecmaVersion: 6, plugins: { jsx: true }}, function(node) { t.equal(node.type, nodeTypes.shift()); }); }); node-falafel-2.1.0/test/es6.js000066400000000000000000000006611306661520400160470ustar00rootroot00000000000000var falafel = require('../'); var test = require('tape'); test('generators', function (t) { t.plan(1); var src = 'console.log((function * () { yield 3 })().next().value)'; var output = falafel(src, { ecmaVersion: 6 }, function (node) { if (node.type === 'Literal') { node.update('555'); } }); Function(['console'],output)({log:log}); function log (n) { t.equal(n, 555) } }); node-falafel-2.1.0/test/for.js000066400000000000000000000015061306661520400161370ustar00rootroot00000000000000var falafel = require('../'); var test = require('tape'); test('for loop', function (t) { t.plan(7); var src = '(function () {' + 'var sum = 0;' + 'for (var i = 0; i < 10; i++)' + 'sum += i;' + 'if (true)' + 'for (var i = 0; i < 10; i++)' + 'sum += i;' + 'return sum;' + '})()'; var output = falafel(src, function (node) { if (node.type === 'ForStatement') { t.equal(node.update.source(), 'i++'); t.equal(node.update.type, "UpdateExpression"); node.update.update('i+=2'); } if (node.type === 'UpdateExpression') { t.equal(node.source(), 'i++'); } }); var res = Function('return ' + output)(); t.equal(res, 2 + 4 + 6 + 8 + 2 + 4 + 6 + 8); }); node-falafel-2.1.0/test/inspect.js000066400000000000000000000015251306661520400170170ustar00rootroot00000000000000var falafel = require('../'); var test = require('tape'); test('inspect', function (t) { t.plan(6); var src = '(function () {' + 'var xs = [ 1, 2, [ 3, 4 ] ];' + 'var ys = [ 5, 6 ];' + 'g([ xs, ys ]);' + '})()'; var output = falafel(src, function (node) { if (node.type === 'ArrayExpression') { node.update('fn(' + node.source() + ')'); } }); t.equal(output.inspect(), output.toString()); var arrays = [ [ 3, 4 ], [ 1, 2, [ 3, 4 ] ], [ 5, 6 ], [ [ 1, 2, [ 3, 4 ] ], [ 5, 6 ] ], ]; Function(['fn','g'], output)( function (xs) { t.same(arrays.shift(), xs); return xs; }, function (xs) { t.same(xs, [ [ 1, 2, [ 3, 4 ] ], [ 5, 6 ] ]); } ); }); node-falafel-2.1.0/test/opts.js000066400000000000000000000060711306661520400163400ustar00rootroot00000000000000var falafel = require('../'); var test = require('tape'); test('first opts arg', function (t) { t.plan(5); var src = '(function () {' + 'var xs = [ 1, 2, [ 3, 4 ] ];' + 'var ys = [ 5, 6 ];' + 'g([ xs, ys ]);' + '})()'; var output = falafel({ source: src }, function (node) { if (node.type === 'ArrayExpression') { node.update('fn(' + node.source() + ')'); } }); var arrays = [ [ 3, 4 ], [ 1, 2, [ 3, 4 ] ], [ 5, 6 ], [ [ 1, 2, [ 3, 4 ] ], [ 5, 6 ] ], ]; Function(['fn','g'], output)( function (xs) { t.same(arrays.shift(), xs); return xs; }, function (xs) { t.same(xs, [ [ 1, 2, [ 3, 4 ] ], [ 5, 6 ] ]); } ); }); test('opts.source', function (t) { t.plan(5); var src = '(function () {' + 'var xs = [ 1, 2, [ 3, 4 ] ];' + 'var ys = [ 5, 6 ];' + 'g([ xs, ys ]);' + '})()'; var output = falafel(undefined, { source: src }, function (node) { if (node.type === 'ArrayExpression') { node.update('fn(' + node.source() + ')'); } }); var arrays = [ [ 3, 4 ], [ 1, 2, [ 3, 4 ] ], [ 5, 6 ], [ [ 1, 2, [ 3, 4 ] ], [ 5, 6 ] ], ]; Function(['fn','g'], output)( function (xs) { t.same(arrays.shift(), xs); return xs; }, function (xs) { t.same(xs, [ [ 1, 2, [ 3, 4 ] ], [ 5, 6 ] ]); } ); }); test('Buffer opts.source', function (t) { t.plan(5); var src = Buffer('(function () {' + 'var xs = [ 1, 2, [ 3, 4 ] ];' + 'var ys = [ 5, 6 ];' + 'g([ xs, ys ]);' + '})()'); var output = falafel({ source: src }, function (node) { if (node.type === 'ArrayExpression') { node.update('fn(' + node.source() + ')'); } }); var arrays = [ [ 3, 4 ], [ 1, 2, [ 3, 4 ] ], [ 5, 6 ], [ [ 1, 2, [ 3, 4 ] ], [ 5, 6 ] ], ]; Function(['fn','g'], output)( function (xs) { t.same(arrays.shift(), xs); return xs; }, function (xs) { t.same(xs, [ [ 1, 2, [ 3, 4 ] ], [ 5, 6 ] ]); } ); }); test('Buffer source', function (t) { t.plan(5); var src = Buffer('(function () {' + 'var xs = [ 1, 2, [ 3, 4 ] ];' + 'var ys = [ 5, 6 ];' + 'g([ xs, ys ]);' + '})()'); var output = falafel(src, function (node) { if (node.type === 'ArrayExpression') { node.update('fn(' + node.source() + ')'); } }); var arrays = [ [ 3, 4 ], [ 1, 2, [ 3, 4 ] ], [ 5, 6 ], [ [ 1, 2, [ 3, 4 ] ], [ 5, 6 ] ], ]; Function(['fn','g'], output)( function (xs) { t.same(arrays.shift(), xs); return xs; }, function (xs) { t.same(xs, [ [ 1, 2, [ 3, 4 ] ], [ 5, 6 ] ]); } ); }); node-falafel-2.1.0/test/parent.js000066400000000000000000000016161306661520400166440ustar00rootroot00000000000000var falafel = require('../'); var test = require('tape'); test('parent', function (t) { t.plan(5); var src = '(function () {' + 'var xs = [ 1, 2, 3 ];' + 'fn(ys);' + '})()'; var output = falafel(src, function (node) { if (node.type === 'ArrayExpression') { t.equal(node.parent.type, 'VariableDeclarator'); t.equal( ffBracket(node.parent.source()), 'xs = [ 1, 2, 3 ]' ); t.equal(node.parent.parent.type, 'VariableDeclaration'); t.equal( ffBracket(node.parent.parent.source()), 'var xs = [ 1, 2, 3 ];' ); node.parent.update('ys = 4;'); } }); Function(['fn'], output)(function (x) { t.equal(x, 4) }); }); function ffBracket (s) { return s.replace(/\[\s*/, '[ ').replace(/\s*\]/, ' ]'); }