Jsgrep is program that searches for a particular JavaScript pattern using the abstract syntax tree (AST) of the program. This enables matching expressions based on their JavaScript meaning, rather than based on simple strings.
Find calls to window.setTimeout with a 0 timeout
$ jsgrep 'setTimeout(A, 0)' tests/*.js
tests/jquery.js: setTimeout( function() {
tests/jquery.js: setTimeout( clearFxNow, 0 );
Jsgrep uses metavariables as wildcards. Metavariables match any valid JavaScript chunk, so in this case, the first match was an entire inline function. In both cases, the second parameter to setTimeout was a literal value of 0.
Find value defaulting
$ jsgrep "A = A || B;" tests/*.js
tests/jquery.js: args = args || [];
tests/jquery.js: dataType = dataType || options.dataTypes[ 0 ];
When the pattern references the same metavariable multiple times, jsgrep ensures that the value of the metavariable is the same throughout the match.
Find the names of all invoked events
$ ./jsgrep -p B "A.fire(B, ...)" tests/*.js
tests/connect.js: 'auth.logout'
tests/connect.js: 'auth.login'
The ...
operator matches 0 or more expressions in function calls and array
initializers. This example also uses the -p
flag to print only a particular
matched variable.
Find classes that have a 'path' property
$ jsgrep -p C "JX.install(C, { properties: { path: X } })" tests/*.js
tests/javelin.js: 'Event'
tests/javelin.js: 'URI'
Jsgrep allows you to search object initializations partially, which enables easily drilling into the structure of JavaScript classes.
Jsgrep allows you to identify source code that resembles a certain pattern. Jspatch is a tool to take that information and programmatically modify the original source code.
Replace setTimeout with window.setTimeout
$ jspatch -e 's/setTimeout( A, B )/window.setTimeout( A, B )/' tests/jquery.js
--- tests/jquery.js 2012-01-10 14:51:36.000000000 -0800
+++ /tmp/jspatch.jquery.js 2012-01-11 17:13:56.000000000 -0800
@@ -420,7 +420,7 @@
if ( (wait === true && !--jQuery.readyWait) || (wait !== true && !jQuery.isReady) ) {
// Make sure body exists, at least, in case IE gets a little overzealous (ticket #5443).
if ( !document.body ) {
- return setTimeout( jQuery.ready, 1 );
+ return window.setTimeout( jQuery.ready, 1 );
}
// Remember that the DOM is ready
@@ -1987,7 +1987,7 @@
( src === "mark" || !jQuery._data(elem, markDataKey) ) ) {
// Give room for hard-coded callbacks to fire first
// and eventually mark/queue something else on the element
- setTimeout( function() {
+ window.setTimeout( function() {
if ( !jQuery._data( elem, queueDataKey ) &&
!jQuery._data( elem, markDataKey ) ) {
jQuery.removeData( elem, deferDataKey, true );
Sed mode allows you to quickly perform simple substitutions on the source code.
Refactor properties/members into just properties
$ cat tests/transform.spatch
JX.install(N, {
...,
- properties : {
+ members : {
...
+ ,
- },
- members : {
...
},
...
})
$ jspatch -f tests/transform.spatch tests/javelin.js
--- tests/javelin.js 2012-01-08 10:55:04.000000000 -0800
+++ /tmp/jspatch.javelin.js 2012-01-12 03:06:45.000000000 -0800
@@ -3615,16 +3615,14 @@
}
},
- properties : {
+ members : {
protocol: undefined,
port: undefined,
path: undefined,
queryParams: undefined,
fragment: undefined,
- querySerializer: undefined
- },
-
- members : {
+ querySerializer: undefined,
+
_domain: undefined,
/**
This complex spatch file leverages the fact that properties and members are always adjacent to one another in the source. This could be verified using jsgrep to ensure that no cases were missed.
- Consider a metavar that matches but doesn't save, to prevent ambiguities.
- Rewrite forEachNode to be iterative rather than recursive (to fix ...)
- Consider support for --where/--eval and/or not-patterns
- Support for most statements in patterns
- Formally testing the pattern matchers
jspatch:
- Testing
Jsgrep was written by Ryan Patterson at Facebook. Inspiration for this project draws heavily from pfff, a source code analyzer written at Facebook.