diff --git a/README.md b/README.md index 7b937e7..ab48da7 100644 --- a/README.md +++ b/README.md @@ -19,7 +19,7 @@ Usage ``` $ jsfmt --help Usage: - jsfmt [--no-format] [--diff|--list|--write] [--validate] [--rewrite PATTERN|--search PATTERN] [--json] [...] + jsfmt [--no-format] [--save-ast] [--diff|--list|--write] [--validate] [--rewrite PATTERN|--search PATTERN] [--json|--ast] [...] jsfmt (--version | --help) Options: @@ -31,6 +31,8 @@ Options: --no-format Do not format the input file(s) -w --write Overwrite the original file with jsfmt output -j --json Tell jsfmt that the file being parsed is json + -a --ast Tell jsfmt that the file being parsed is in JSON AST + --save-ast Output the resulting js in JSON AST format -r=PATTERN --rewrite PATTERN Rewrite rule (e.g., 'a.slice(b, len(a) -> a.slice(b)') -s=PATTERN --search PATTERN Search rule (e.g., 'a.slice') ``` diff --git a/lib/ast.js b/lib/ast.js new file mode 100644 index 0000000..0a51bf9 --- /dev/null +++ b/lib/ast.js @@ -0,0 +1,24 @@ +var escodegen = require('escodegen'); +var esprima = require('esprima'); + + +module.exports.parseAST = function(ast){ + var js = escodegen.generate(ast, { + comment: true, + format: { + quotes: 'double' + } + }); + return js; +}; + +module.exports.generateAST = function(js){ + var ast = esprima.parse(js, { + raw: true, + tokens: true, + range: true, + comment: true + }); + ast = escodegen.attachComments(ast, ast.comments, ast.tokens); + return ast; +}; diff --git a/lib/index.js b/lib/index.js index 1014d6d..2aecc54 100644 --- a/lib/index.js +++ b/lib/index.js @@ -1,6 +1,7 @@ var rewritePath = './rewrite.js'; var format = require('./format.js'); var validate = require('./validate.js'); +var ast = require('./ast.js'); exports.rewrite = require(rewritePath).rewrite; exports.search = require(rewritePath).search; @@ -9,3 +10,5 @@ exports.formatJSON = format.formatJSON; exports.validate = validate.validate; exports.validateJSON = validate.validateJSON; exports.getConfig = require('./config.js').getConfig; +exports.parseAST = ast.parseAST; +exports.generateAST = ast.generateAST; diff --git a/lib/run.js b/lib/run.js index e73d18b..ec84fc9 100644 --- a/lib/run.js +++ b/lib/run.js @@ -15,7 +15,7 @@ tmp.setGracefulCleanup(); var doc = [ 'Usage:', - ' jsfmt [--no-format] [--diff|--list|--write] [--validate] [--rewrite PATTERN|--search PATTERN] [--json] [...]', + ' jsfmt [--no-format] [--save-ast] [--diff|--list|--write] [--validate] [--rewrite PATTERN|--search PATTERN] [--json|--ast] [...]', ' jsfmt (--version | --help)', '', 'Options:', @@ -27,6 +27,8 @@ var doc = [ ' --no-format Do not format the input file(s)', ' -w --write Overwrite the original file with jsfmt output', ' -j --json Tell jsfmt that the file being parsed is json', + ' -a --ast Tell jsfmt that the file being parsed is in JSON AST', + ' --save-ast Output the resulting js in JSON AST format', ' -r=PATTERN --rewrite PATTERN Rewrite rule (e.g., \'a.slice(b, len(a) -> a.slice(b)\')', ' -s=PATTERN --search PATTERN Search rule (e.g., \'a.slice\')', ].join("\r\n"); @@ -90,6 +92,15 @@ function handleJavascript(fullPath, original) { var js = original; var relativePath = path.relative(process.cwd(), fullPath); + if (argv['--ast']) { + try { + js = jsfmt.parseAST(JSON.parse(js)); + } catch(err) { + console.error(relativePath, err.message); + return; + } + } + if (argv['--search']) { try { jsfmt.search(js, argv['--search']).forEach(function(match) { @@ -157,6 +168,13 @@ function handleJavascript(fullPath, original) { // Overwrite original file fs.writeFileSync(fullPath, js); } else { + if (argv['--save-ast']) { + var ast = jsfmt.generateAST(js); + js = JSON.stringify(ast); + if (!argv['--no-format']) { + js = jsfmt.formatJSON(js); + } + } // Print to stdout console.log(js); } diff --git a/tests/ast.js b/tests/ast.js new file mode 100644 index 0000000..2affccc --- /dev/null +++ b/tests/ast.js @@ -0,0 +1,28 @@ +/* jshint node:true */ +/* global describe,it */ +'use strict'; +var should = require('should'); + +var libPath = process.env.JSFMT_COV ? 'lib-cov' : 'lib'; +var jsfmt = require('../' + libPath + '/index'); + +describe('jsfmt.parseAST', function() { + it('should test basic ast json parsing', function() { + 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]}]}'; + ast = JSON.parse(ast); + + var js = jsfmt.parseAST(ast); + js.should.eql('var a = 50;\nvar b = 100;'); + }); +}); + +describe('jsfmt.generateAST', function() { + it('should test basic ast generation', function() { + var js = 'var a = 50;\nvar b = 100;'; + var ast = jsfmt.generateAST(js); + + 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]}]}'; + + JSON.stringify(ast).should.eql(astExpected); + }); +});