Skip to content

Commit 0fa7953

Browse files
committed
Merge pull request #106 from brettlangdon/ast
allow saving and loading in ast format
2 parents a18921c + 5607fdc commit 0fa7953

File tree

5 files changed

+77
-2
lines changed

5 files changed

+77
-2
lines changed

README.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ Usage
1919
```
2020
$ jsfmt --help
2121
Usage:
22-
jsfmt [--no-format] [--diff|--list|--write] [--validate] [--rewrite PATTERN|--search PATTERN] [--json] [<file>...]
22+
jsfmt [--no-format] [--save-ast] [--diff|--list|--write] [--validate] [--rewrite PATTERN|--search PATTERN] [--json|--ast] [<file>...]
2323
jsfmt (--version | --help)
2424
2525
Options:
@@ -31,6 +31,8 @@ Options:
3131
--no-format Do not format the input file(s)
3232
-w --write Overwrite the original file with jsfmt output
3333
-j --json Tell jsfmt that the file being parsed is json
34+
-a --ast Tell jsfmt that the file being parsed is in JSON AST
35+
--save-ast Output the resulting js in JSON AST format
3436
-r=PATTERN --rewrite PATTERN Rewrite rule (e.g., 'a.slice(b, len(a) -> a.slice(b)')
3537
-s=PATTERN --search PATTERN Search rule (e.g., 'a.slice')
3638
```

lib/ast.js

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
var escodegen = require('escodegen');
2+
var esprima = require('esprima');
3+
4+
5+
module.exports.parseAST = function(ast){
6+
var js = escodegen.generate(ast, {
7+
comment: true,
8+
format: {
9+
quotes: 'double'
10+
}
11+
});
12+
return js;
13+
};
14+
15+
module.exports.generateAST = function(js){
16+
var ast = esprima.parse(js, {
17+
raw: true,
18+
tokens: true,
19+
range: true,
20+
comment: true
21+
});
22+
ast = escodegen.attachComments(ast, ast.comments, ast.tokens);
23+
return ast;
24+
};

lib/index.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
var rewritePath = './rewrite.js';
22
var format = require('./format.js');
33
var validate = require('./validate.js');
4+
var ast = require('./ast.js');
45

56
exports.rewrite = require(rewritePath).rewrite;
67
exports.search = require(rewritePath).search;
@@ -9,3 +10,5 @@ exports.formatJSON = format.formatJSON;
910
exports.validate = validate.validate;
1011
exports.validateJSON = validate.validateJSON;
1112
exports.getConfig = require('./config.js').getConfig;
13+
exports.parseAST = ast.parseAST;
14+
exports.generateAST = ast.generateAST;

lib/run.js

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ tmp.setGracefulCleanup();
1515

1616
var doc = [
1717
'Usage:',
18-
' jsfmt [--no-format] [--diff|--list|--write] [--validate] [--rewrite PATTERN|--search PATTERN] [--json] [<file>...]',
18+
' jsfmt [--no-format] [--save-ast] [--diff|--list|--write] [--validate] [--rewrite PATTERN|--search PATTERN] [--json|--ast] [<file>...]',
1919
' jsfmt (--version | --help)',
2020
'',
2121
'Options:',
@@ -27,6 +27,8 @@ var doc = [
2727
' --no-format Do not format the input file(s)',
2828
' -w --write Overwrite the original file with jsfmt output',
2929
' -j --json Tell jsfmt that the file being parsed is json',
30+
' -a --ast Tell jsfmt that the file being parsed is in JSON AST',
31+
' --save-ast Output the resulting js in JSON AST format',
3032
' -r=PATTERN --rewrite PATTERN Rewrite rule (e.g., \'a.slice(b, len(a) -> a.slice(b)\')',
3133
' -s=PATTERN --search PATTERN Search rule (e.g., \'a.slice\')',
3234
].join("\r\n");
@@ -90,6 +92,15 @@ function handleJavascript(fullPath, original) {
9092
var js = original;
9193
var relativePath = path.relative(process.cwd(), fullPath);
9294

95+
if (argv['--ast']) {
96+
try {
97+
js = jsfmt.parseAST(JSON.parse(js));
98+
} catch(err) {
99+
console.error(relativePath, err.message);
100+
return;
101+
}
102+
}
103+
93104
if (argv['--search']) {
94105
try {
95106
jsfmt.search(js, argv['--search']).forEach(function(match) {
@@ -157,6 +168,13 @@ function handleJavascript(fullPath, original) {
157168
// Overwrite original file
158169
fs.writeFileSync(fullPath, js);
159170
} else {
171+
if (argv['--save-ast']) {
172+
var ast = jsfmt.generateAST(js);
173+
js = JSON.stringify(ast);
174+
if (!argv['--no-format']) {
175+
js = jsfmt.formatJSON(js);
176+
}
177+
}
160178
// Print to stdout
161179
console.log(js);
162180
}

tests/ast.js

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
/* jshint node:true */
2+
/* global describe,it */
3+
'use strict';
4+
var should = require('should');
5+
6+
var libPath = process.env.JSFMT_COV ? 'lib-cov' : 'lib';
7+
var jsfmt = require('../' + libPath + '/index');
8+
9+
describe('jsfmt.parseAST', function() {
10+
it('should test basic ast json parsing', function() {
11+
var ast = '{"type":"Program","body":[{"type":"VariableDeclaration","declarations":[{"type":"VariableDeclarator","id":{"type":"Identifier","name":"a","range":[4,5]},"init":{"type":"Literal","value":50,"raw":"50","range":[8,10]},"range":[4,10]}],"kind":"var","range":[0,11]},{"type":"VariableDeclaration","declarations":[{"type":"VariableDeclarator","id":{"type":"Identifier","name":"b","range":[16,17]},"init":{"type":"Literal","value":100,"raw":"100","range":[20,23]},"range":[16,23]}],"kind":"var","range":[12,24]}],"range":[0,24],"comments":[],"tokens":[{"type":"Keyword","value":"var","range":[0,3]},{"type":"Identifier","value":"a","range":[4,5]},{"type":"Punctuator","value":"=","range":[6,7]},{"type":"Numeric","value":"50","range":[8,10]},{"type":"Punctuator","value":";","range":[10,11]},{"type":"Keyword","value":"var","range":[12,15]},{"type":"Identifier","value":"b","range":[16,17]},{"type":"Punctuator","value":"=","range":[18,19]},{"type":"Numeric","value":"100","range":[20,23]},{"type":"Punctuator","value":";","range":[23,24]}]}';
12+
ast = JSON.parse(ast);
13+
14+
var js = jsfmt.parseAST(ast);
15+
js.should.eql('var a = 50;\nvar b = 100;');
16+
});
17+
});
18+
19+
describe('jsfmt.generateAST', function() {
20+
it('should test basic ast generation', function() {
21+
var js = 'var a = 50;\nvar b = 100;';
22+
var ast = jsfmt.generateAST(js);
23+
24+
var astExpected = '{"type":"Program","body":[{"type":"VariableDeclaration","declarations":[{"type":"VariableDeclarator","id":{"type":"Identifier","name":"a","range":[4,5]},"init":{"type":"Literal","value":50,"raw":"50","range":[8,10]},"range":[4,10]}],"kind":"var","range":[0,11]},{"type":"VariableDeclaration","declarations":[{"type":"VariableDeclarator","id":{"type":"Identifier","name":"b","range":[16,17]},"init":{"type":"Literal","value":100,"raw":"100","range":[20,23]},"range":[16,23]}],"kind":"var","range":[12,24]}],"range":[0,24],"comments":[],"tokens":[{"type":"Keyword","value":"var","range":[0,3]},{"type":"Identifier","value":"a","range":[4,5]},{"type":"Punctuator","value":"=","range":[6,7]},{"type":"Numeric","value":"50","range":[8,10]},{"type":"Punctuator","value":";","range":[10,11]},{"type":"Keyword","value":"var","range":[12,15]},{"type":"Identifier","value":"b","range":[16,17]},{"type":"Punctuator","value":"=","range":[18,19]},{"type":"Numeric","value":"100","range":[20,23]},{"type":"Punctuator","value":";","range":[23,24]}]}';
25+
26+
JSON.stringify(ast).should.eql(astExpected);
27+
});
28+
});

0 commit comments

Comments
 (0)