gkz / grasp Goto Github PK
View Code? Open in Web Editor NEWJavaScript structural search, replace, and refactor
Home Page: http://graspjs.com
License: MIT License
JavaScript structural search, replace, and refactor
Home Page: http://graspjs.com
License: MIT License
Using the demo on:
http://graspjs.com/
/$ grasp if a.js
2: if (x && f(x)) { return [1, 2; }]
...
/$ cat a.js
function g(x) {
if (x && f(x)) { return [1, 2]; }
...
/$
Grasp can't open files specified with absolute paths.
A possible fix is to use path.resolve
instead of path.join
in https://github.com/gkz/grasp/blob/master/src/index.ls#L273 (this however makes all paths absolute in the output).
Example file:
Test = function () {};
function buildDialog() {
var tt = "11";
}
And then running:
grasp -d 'func[id=#buildDialog]' test2.js
Returns:
options:
{ debug: true,
extensions: [ 'js' ],
parser: [ 'acorn', { locations: true } ],
lineNumber: true,
color: true,
_: [ 'func[id=#buildDialog]', 'test2.js' ] }
parse-selector: 2ms
parsed-selector:
{"type":"compound","selectors":[{"type":"matches","selectors":[{"type":"identifier","value":"FunctionDeclaration"},{"type":"identifier","value":"FunctionExpression"}]},{"type":"attribute","name":"id","operator":"=","valType":"complex","value":{"type":"compound","selectors":[{"type":"identifier","value":"Identifier"},{"type":"attribute","name":"name","operator":"=","valType":"primitive","value":{"type":"literal","value":"buildDialog"}}]}}]}
parse-input:test2.js: 2ms
Cannot read property 'type' of null
everything: 11ms
The problem is in definition of Test variable, if I define first function with a name, then it works. For example:
Test = function a() {};
function buildDialog() {
var tt = "11";
}
Great tool! Is this a problem of acorn parser or grasp itself?
Thanks for any info ...
There’s a case with strings declared in different quotes:
grasp.replace('equery', 'require("a")', 'require("b")', "var x = require('a')" );
The replacement won’t trigger because the required module a
is in single quotes instead of double ones.
It it quite easy to workaround, just running the replacement second time with changed quotes style.
But it might be painful when a source code includes mixed quotes, in that case it is supposed to grasp all possible combinations of wrapped strings, which might be quite tedious.
Is there a reason it is done so, or would it be more sensible to tolerate string quotes?
No matter where I place or how I write the extensions flag, it gives me this error:
"Can't set argument 'x' when not last flag in a group of short flags."
My structure has the template files in directories inside the controller directory. Parse errors on the .ejs files make the listing impossible to read.
In the big picture, finding a way to parse the template fils is going to be necessary for this to be fully useful, imho.
Thanks for the cool tool. It's really an interesting thing to do.
Hi,
I am using requirejs
for my project and I would like to know if grasp
would be suitable to do the reordering of the arguments according to a specified order.
Background:
requirejs
's define
works like this:
define([depA, depB, depC], function (a,b,c) {
});
I want to reorder the arguments according to a function of my choosing, giving priorities: jQuery, underscore etc.. first, then my Backbone views, then my Backbone models and then all the other modules. My views and models are respectfully in views/
and models/
so I can match the path of the module to know its priority.
I guess the priority giving function would look something like:
var priorities = [
'jquery',
'underscore',
'views/',
'models/',
];
var enumPriorities = _.map(priorities, function (v, k) { return [k,v]; });
function priority (path) {
return _.find(enumPriorities, function (o) {
return o[1].match(path);
})[0];
}
So the input would be
define(['models/hello', 'jquery', 'views/toto', 'underscore'], function (hello, $, toto, _) {
});
and the output
define(['jquery', 'underscore', 'views/toto', 'models/hello'], function ($, _, toto, hello) {
});
It seems that grasp cannot handle complex cases like this where the reordering is dynamic. Or can it ?
If Grasp cannot do it, what tool would you recommend ?
Thanks !
grasp -e '.set(,"title",)' -- UI
grasp -e ".set(,'title',)" -- UI
do I need to write this ? to is there some better way to specify literal in e query not sensitive to quoting style ?
e.g.
>> test.js:
define(["one", "two", "old"], callback);
define(["old"], callback);
define(["old", "three", "four"], callback);
What I tried
grasp -e 'define([_$before,"old",_$after], $callback)' test.js -R "define([{{before | join ','}},'new', {{after | join ','}}], callback)"
Output
define(["one","two",'new', ], callback);
define([,'new', ], callback);
define([,'new', "three","four"], callback);
What I would like to achieve
define(["one","two",'new' ], callback);
define(['new' ], callback);
define(['new', "three","four"], callback);
thanx a lot.
I am having trouble grasping inside of a function expression passed as an argument to another function.
If I run grasp 'func[id=#fn]' test.js
on the following:
function outerFn() {
}
function fn() {
console.log('this works');
}
outerFn(function (foo) {
function fn() {
console.log('this does not work');
}
});
I get the following output:
$ grasp 'func[id=#fn]' test.js
Cannot read property 'type' of null
If I comment out the call to outerFn
, I get the following output:
$ grasp 'func[id=#fn]' test.js
4-6:(multiline):
function fn() {
console.log('this works');
}
I wan to transform the following:
a.fn(b, c);
a.fn(b);
a.fn();
to
fn2(a, b, c);
fn2(a, b);
fn2(a);
In order to do so, I attempted the following grasp command:
grasp "call! > member[prop.name=fn]
" -R "fn2({{.args | prepend .callee.obj | join ', '}})"
however, this results in:
fn2(.callee.obj, b, c);
fn2(.callee.obj, b);
fn2(.callee.obj);
i.e. it used .callee.obj
as a string literal when I intended a named submatch. Can there be a way to provide named submatches as arguments to filters?
hello, i have a object,like this:
var user = {
hobby:['sing', 'tour']
}
var outerArray=['sing', 'game'];
how can i get the 'sing' in the user's hobby property array? not the outerArray's
A lot of tools exist today for working with AST, and it's important to them to be able to communicate with each other by just passing AST as input, transforming it inside and outputting AST object as well.
Grasp seems to use Mozilla AST internally, and it's possible to pass AST in using identity function (function (input) { return input }
) in parser
option, but it lacks ability to get resulting AST after replacement.
Such feature should be easy to add (I'm just not familiar with LiveScript syntax, so it may take more time to figure out how to do that) and it will allow much better interoperability with other developer tools.
Hi George,
I have something like: grasp -e ".replace(,,)"
and I need to specify, that I don't want to match the first variable == "firstVar"
so the following line of code is ignored:
firstVar.replace(smthg, smthg2, smthg3);
Is there any way to achieve this condition, maybe with squery?
i have a method call like below:
sayHello("from", {
title: "abc",
message: "aha"
});
how to change the second argument's property name "message" to "info"(only when the method's name is "sayHello" and the first argument is "from")?
by the way, i use grasp in nodejs and squery
thank you very much!
ref #24
It would sometimes be nice to be able access primitive values - eg. get the value of a string, rather than getting it with surrounding quotes.
for concocting queries, or debugging, it would be nice to have some sort of option to just display the ast from some code, printed as JSON or something better perhaps
My use case is the following:
Example:
alert(foo() + bar());
function foo() {/*...*/}
function bar() {/*...*/}
⬇︎
Global.foo = foo;
Global.bar = bar;
alert(foo() + bar());
function foo() {/*...*/}
function bar() {/*...*/}
Right now I chain two rules to achieve it:
grasp.replace('squery', ':root[body]', 'Global.funcdecl$.{{func-dec.id | join "\nGlobal.funcdecl$."}}\n{{}}');
grasp.replace('equery', 'Global.funcdecl$.$funcName', 'Global.{{funcName}} = {{funcName}};');
Applying the second rule is rather expensive. Is there a way to repeat the id in the join expression of the first replacement?
Hi,
I read http://graspjs.com/blog/2014/01/07/refactoring-javascript-with-grasp/ -- so maybe I need to read more documentation but...
My end goal is to turn last()
calls like this:
lib/html5/parser.js:2395: if (tree.open_elements.last() != node) {
lib/html5/parser.js:2407: if (tree.open_elements.last().tagName.toLowerCase() != name)
lib/html5/parser.js:2431: if (tree.open_elements.last().tagName.toLowerCase() != name)
lib/html5/parser.js:2464: if (afeElement != tree.open_elements.last()) {
lib/html5/parser.js:2556: tree.activeFormattingElements.push(tree.open_elements.last());
lib/html5/parser.js:2576: {name: tree.open_elements.last().tagName.toLowerCase()});
lib/html5/parser.js:2650: var currentNode = this.tree.open_elements.last() || null;
Into something like this:
lib/html5/parser.js:2395: if (last(tree.open_elements) != node) {
lib/html5/parser.js:2407: if (last(tree.open_elements).tagName.toLowerCase() != name)
lib/html5/parser.js:2431: if (last(tree.open_elements).tagName.toLowerCase() != name)
lib/html5/parser.js:2464: if (afeElement != last(tree.open_elements)) {
lib/html5/parser.js:2556: tree.activeFormattingElements.push(last(tree.open_elements));
lib/html5/parser.js:2576: {name: last(tree.open_elements).tagName.toLowerCase()});
lib/html5/parser.js:2650: var currentNode = last(this.tree.open_elements) || null;
I can detect the last
identifier like so:
grasp -e 'last' lib/html5/parser.js
However -- when I try to detect it with parens I get no results: grasp -e 'last()' lib/html5/parser.js
I also tried adding wildcards -- but just get an error:
islams-mbp:html5 islam$ grasp -e '__last()__' lib/html5/parser.js
/Users/islam/.nvm/v0.10.24/lib/node_modules/grasp/node_modules/grasp-equery/lib/parse.js:42
throw new Error("Error processing selector '" + selector + "'.");
^
Error: Error processing selector '__last()__'.
at Object.parse (/Users/islam/.nvm/v0.10.24/lib/node_modules/grasp/node_modules/grasp-equery/lib/parse.js:42:13)
at run (/Users/islam/.nvm/v0.10.24/lib/node_modules/grasp/lib/index.js:174:34)
at Object.<anonymous> (/Users/islam/.nvm/v0.10.24/lib/node_modules/grasp/bin/grasp:4:1)
at Module._compile (module.js:456:26)
at Object.Module._extensions..js (module.js:474:10)
at Module.load (module.js:356:32)
at Function.Module._load (module.js:312:12)
at Function.Module.runMain (module.js:497:10)
at startup (node.js:119:16)
at node.js:902:3
Am I misunderstanding how to use grasp? What am I doing wrong? Or is it something to be debugged?
Hi again, I'm stuck in the next step of my task.
I need to extract all the it
s in a test suite that are direct children to a certain describe
.
For example, if I have the following code
describe('outer', function() {
it('first', function() {
// ...
});
describe('inner to be ignored', function() {
it('again ignore', function() {});
});
it('second', function() {
// ...
});
});
I would like to get back the two inner it
s (named "first" and "second") thus ignoring any inner describe and its it
children.
This is what I got so far:
grasp -e "describe(nameHere, _$)" file
in this case it's easy to target the correct describe, but I get everything inside, I could not find a way to negate non direct children.
I've also tried using what you suggested in the other question, which is
grasp 'call[callee=#describe] call[callee=#it]'
in this case I get all children (when I do grasp 'call[callee=#describe] > call[callee=#it]'
I get nothing, I can't figure out why), but I have some problems in understanding how to specify the name of the first parameter in the describe.
Thank you in advance ffor any hint!
grasp.replace('equery', '1', '', '1;'); //⇒ []
Any attempt to replace anything with an empty string returns an empty array. Seems that replace
is unable to remove matched code.
A simple workaround is to pass an empty statement:
grasp.replace('equery', '1', ';', '1;'); //⇒ [';;']
But it is not expected behaviour of replace
, is it?
Hi, if i have a code like:
var a = 0;
var b = 1;
var c = 2;
the squery 'dec' returns an array of 3 declarations, but the query
'dec:last' returns an empty array. The same for every selector (:first, :nth, ecc..).
Thanks.
In light of #12, #13 perhaps a way to explicitly declare additional syntax would be favorable - this could take a few different forms
Realize that these are ES6 features and this not in any browser (without being behind flags) at present, however
Great tool btw, loving the power.
Say I have this code:
var a = {};
var n = "hello";
a.n = 5;
a[n] = 6;
I want to rename n
to m
but a.n
should obviously stay the same as that is a completely different n
.
member #n
seems to match both a[n]
and a.n
Thank you in advance,
Vadim
I have this file:
var t1 = "";
When I search for t1 (grasp "#t1" file.js) it doesn't return any results.
When I change t1 to t in the file and then search for t, I receive the correct result.
I'm on Windows 7 with node v0.10.26 and grasp v0.2.1.
When replacing a variable used three time or more on one line, the third and subsequent replacements get a bit confused.
file.js
a(searchVal);
a(searchVal, searchVal);
a(searchVal, searchVal, searchVal);
a(searchVal, searchVal, searchVal, searchVal);
a(searchVal, searchVal, searchVal, searchVal, searchVal);
a(searchVal, searchVal, searchVal, searchVal, searchVal, searchVal);
a(searchVal, searchVal, searchVal, searchVal, searchVal, searchVal, searchVal);
a(searchVal,
searchVal,
searchVal,
searchVal,
searchVal,
searchVal,
searchVal);
grasp -i '#searchVal' -R n file.js
results in:
a(n);
a(n, n);
a(n, n, searchVan
a(n, n, searchVanVal);n
a(n, n, searchVanVal, searcnn
a(n, n, searchVanVal, searcnrchVal);nn
a(n, n, searchVanVal, searcnrchVal, sennn
a(n,
n,
n,
n,
n,
n,
n);
How much trouble would it be to support an interface like the following?
var ast = require("esprima").parse(code);
ast = require("grasp").replace("{{.r}}+{{.l}}", ast);
process.stdout.write(require("escodegen").generate(ast));
Or, even better:
var transform = require("grasp").compileReplacer("{{.r}}+{{.l}}");
var ast = transform(require("esprima").parse(code));
process.stdout.write(require("escodegen").generate(ast));
This kind of interface would allow multiple transformations to be chained together easily, and would also leave the complexity of reprinting to an external library like https://github.com/Constellation/escodegen or https://github.com/benjamn/recast.
If grasp already supports use cases like this, please feel free to point me there (and close this issue).
Perhaps that would be an entirely different project.
I'm trying to execute this on the Windows PowerShell:
grasp -e 'dojo.publish($a,[$b])' -R 'topic.publish({{a}},{{b}})' test.js
But I'm getting this return value:
-R was unexpected at this time.
This command runs fine on my linux terminal.
Is there any additional escaping or are there syntax adjustments for the windows CLI?
Why would one ever practically need structural search or search-and-replace of JavaScript? When would textual search (with grep, etc.) for something you actually need in your work be insufficiently powerful or harder than using grasp?
I can think of two use-cases for grasp, but they don’t seem very compelling, so I must be missing some.
&&
s and if
s inside its implementation.myLib.find({query: "abc"})
to myLib.search("abc")
.The three toy examples on the home page show the syntax and capabilities, but don’t sound like things anyone would want to actually do.
I think the answer to this should be in the FAQ, or maybe even on the home page.
One possible way of implementing could be to allow user to specify a file which would export the filters.
If the replacement feature is used and the replacement is surrounded by qoutes (single or double), those quotes are not used as part of the replacement, but are ignored.
Take the following is the input file:
var foo = {
a: "value A",
"b": "value B",
c: "value C"
}
grasp is supposed to be used to ensure every key
in this code example is encapsulated in double quotes. The following command is therefore issued:
$ grasp 'prop[key!=String] > @key' example --replace '"{{}}"'
Problem the replacement is identical to the input, as the surrounding doubles quotes are ignored.
Doubling the quotes issues the correct result:
$ grasp 'prop[key!=String] > @key' example --replace '""{{}}""'
const is coming with es6, and is already in use in some environments:
Hi George,
I was wondering if it's possible to match code blocks with multiple occurrences of return statement with grasp. I'm afraid, that there is no way to match a condition like this, but please correct me if I'm wrong.
Thanks.
item.label = item.label ? item.label : "";
I would like to find this and refactor to:
in general:
X = X ? X: __
thanx
if i want to replace many variables in a js file,i can write many command like below:
grasp '#a' -R aaa -i a.js
grasp '#b' -R bbb -i a.js
grasp '#c' -R ccc -i a.js
grasp '#d' -R ddd -i a.js
...
...
a.js is very big(>=1MB),so it's very slow,is there a faster way?
Given this example:
foo('baz', 'bar')
bar('bar')
baz(x)
I want to match things like:
Experimented with call[arguments]
and putting different things after arguments
without success. I guess the answer should be at complex attributes but I couldn't figure it out.
Using grasp 0.2.1.
Awesome tool btw, I have high hopes for using it in complicated refactorings.
Hi everybody,
I just stumbled across grasp and I find the idea pretty amazing and powerfull. My first use in real life would be to do a function rename. So image I have a file that looks like this:
a('a', ['b','c'], function(b,c) {
if(b && c) {
connsole.log('hello world');
}
});
This is a call to function a that has several arguments 'a', ['b','c']...
Now I want to rename the function call from a to d. So I tried:
grasp -s "call[callee=#a]" --replace "d({{.args}})" file.js
The output is:
d('a');
What I wanted is:
d('a', ['b','c'], function(b,c) {
if(b && c) {
console.log('hello world');
}
});
Of course the number of arguments can vary, so a solution with a fixed number of parameters is not viable.
Any chances that I can get it to work?
Any help appreciated :)
Best,
fanderlf
Hi, first of all thank you for this project, it will help me in so many aspects!
I looked at the docs and saw that we can use "parent > child" to select all direct children of parent.
I do not understand the proper syntax that I have to use though.
I am testing it on a sample file like this:
describe('sum', function() {
it('sums', function() {
assert.equal(sum(41, 1), 42);
});
});
running this command grasp '#describe > #it' sum.js
.
Running grasp '#describe' sum.js
and grasp '#it' sum.js
on their own gives the results as expected (line 1 and 2).
What am I doing wrong?
Meanwhile I will go through the code to see if I can figure that out.
Thank you for you time.
with d.js
as:
console.log(a + b + c);
and
$ grasp bi --replace '{{.r}}-{{.l}}' d.js
console.lob-a + b);
Another example:
$ cat c.js
f(x < y, x == z - w);
$ grasp bi --replace '{{.r}}+{{.l}}' c.js
f(y+x, z - w+z
Hi, I'm trying to add a prefix to my functions and I would like to know why the first form works as expected, but not the second:
First form:
grasp '#myidentifier' -R 'PREFIX{{}}' file.js
Second form:
grasp '#myidentifier' -R 'PREFIX{{.name}}' file.js
The documents (http://graspjs.com/docs/syntax-js/) say that the ident node has an attribute called name. Am I doing something wrong?
// this is what I have
grasp -s "call[callee.prop=(#forEach,#map,#filter,#reduce)]" -r
now I want to limit the result to calls that have only one argument)
is it possible with -s syntax ?
I'm trying to replace all strings so that they have a consistent quote style.
Things I've tried:
grasp 'str' --replace "{{ str }}" path/to/script.js
grasp 'str' --replace "{{ str | wrap \' }}" path/to/script.js
grasp 'str' --replace "{{ .value | wrap \' }}" path/to/script.js
grasp 'str' --replace "{{ {{}} | slice 1 -1 | wrap \' }}" path/to/script.js
grasp 'str' --replace "{{ .value | slice 1 -1 | wrap \' }}" path/to/script.js
And probably some more. I cannot figure this out. How can I match and filter a string's value? What's the best way to do this?
From this ticket, #22 , we can use a function to replace.
I try this and it doesn't work.
var code = grasp.replace('equery' , 'require($str)' , function( $str ){
return path.join( __dirname , $str ) + '.js'
}, '... require("./a/b") ...')
I have this error.
Option 'replace': Invalid type for 'function ( $str ){
return path.join( __dirname , $str ) + '.js'
}' - expected type 'replacement::String'.
How can i use this options?
// trying to get only top level calls to AMD define and require in all files
// but with no success:
// my trials (errors) so fat
grasp -s "program.body>call.callee:matches(#define,#require)" -r .
grasp -s ":root.body>call.callee:matches(#define,#require)" -r .
please help, I'm struggling with docs
Hi,
complex selector seems not working.
var a = 0;
var b = 1;
var c = 2;
The squery 'dec' returns 3 declarations, but 'dec ~ dec' returns an empty array.
'dec ~ *', 'dec + ' and any other i tried seems not working.
I need to build a CPS transformer for async programming and i need to query all the statements that comes after a certain function call in order to nest them inside a callback.
for example:
result = async_func(arg1, arg2, stop);
console.log(result);
must became:
async_func(arg1, arg2, function(err, result){
if (err) {return throw err; }
console.log(result);
});
the query for the assignment is:
'assign! call.arguments:last[name=stop] '
but from that how can i include all the sibling code?
'assign! call.arguments:last[name=stop] ~ *!'
returns an empty array.
The replacing code i'm writing is:
return {{assign.right.callee}}({{assign.right.arguments | initial | join "," }}, function (err, {{assign.left}}){
if(err){return throw err;}
// sibling code here.
});
I'm a newbye of grasp (it seems great!!!) but i'm not sure if it is the right tool for that, what do you think about it? Refactoring is the right technique to produce this transformation or maybe i have to work manually on AST?
Thanks.
Any plans to create a ST (preferably 3) plugin for this? I'd do it myself but I don't know Python at all, let alone how to write a plugin for sublime :)
Specifically, I have many 100s of files in 30 or so folders, each with dependencies via require() on each other, and I need to move a whole bunch of folders around.
Is this possible, perhaps by some kind of grasp move src trg ?
Thanks,
Dave
like this:
var code = grasp.replace('equery' , 'require($str)' , function( $str ){
return path.join( __dirname , $str ) + '.js'
}, '... require("./a/b") ...')
//code ==> require('/User/test/a/b.js')
tks!
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.