Code Monkey home page Code Monkey logo

ast-query's Introduction

AST Query

npm tests dependencies

This project is a tentative to create a simple JavaScript AST modification library.

If you've ever worked with AST trying to edit source code, you'll know it is a bad time. AST syntax is terse and forces you to loop a tree and use conditional structure a lot. AST Query hide these complexities behind a declarative façade.

Making the simplicity choice means AST Query won't try to cover the full AST API. Rather we strive to answer commons needs.

Getting Started

Install: npm install --save ast-query

First, you need to pass a program code into AST query:

var program = require("ast-query");
var tree = program("var a = 'foo'");

This function returns a wrapped AST tree you can query and modify.

Once you've modified the AST, get the source code back by calling the toString method on the tree.

// ...
tree.var("a").value("'bar'");

console.log( tree.toString() );
// LOG: var a = 'bar';

Remember that you are editing source code. This mean you provide raw source code strings. This mean you need to double wrap strings (e.g.: "'foo'"). If that's not done, AST-query assume you're referencing a variable called foo.

API

Program

var tree = program( sourceCode, escodegenOptions, acornOptions )

  • sourceCode (String) - The source code to edit.
  • escodegenOptions (Object) optional - escodegen option object
  • acornOptions (Object) optional - acorn option object

Returns an AST tree you can then query as explained below:

tree.var( name )

  • name (String) - The variable name

Find and returns a Variable node.

Given this code

var bar = 23;

You'd call tree.var('bar') to get the Variable node.

tree.callExpression( name )

  • name (String) - The name of the function or method being called.

Find a function or method call and return a CallExpression node

Given this code

grunt.initConfig({});

You'd call tree.callExpression('grunt.initConfig') to get the CallExpression node.

tree.assignment( assignedTo )

  • assignedTo (String) - The name (name or object) a value is assigned to

Find and return an AssignmentExpression node.

You'd call tree.assignment('module.exports') to query the code below:

module.exports = function () {
  // code
};

tree.body

Property representing the program body in a Body node.

tree.verbatim( body )

  • body (String) - The source code to inline verbatim

Adds body and return a token assigment.

tree.body.append('var a = 1;' + tree.verbatim('ANYTHING'));

Variable node

.value( value )

  • value (String) optionnal - A string containing the new variable value.

It returns the current or new value wrapped in AST query interface.

.rename( name )

  • name (String) - Change the variable name

CallExpression node

.filter( iterator )

  • iterator (Function) - Function receiving each node as arguments and returning true to keep the current node in the returned set.

Return a new CallExpression nodes collection with nodes passing the iterator test.

.arguments

A property pointing to an ArrayExpression node referencing the called function arguments.

AssignmentExpression node

.value( value )

Replace the assignment value with a new value or return the current value wrapped in an AST query interface.

Literal node

A Literal node represent a raw JavaScript value as a String, a Number or a Boolean.

.value( value )

Get or update the value.

FunctionExpression node

Node representing a function declaration (e.g. function () {}).

.body

Property pointing to a Body node representing the function expression body.

ObjectExpression node

.key( name )

  • name (String) - Key name Get a key value object or create a blank placeholder

value( value )

Replace current node with a new value. Returns the new value wrapped.

ArrayExpression node

.push( value )

  • value (String) - value to push in the array

.unshift( value )

  • value (String) - value to unshift in the array

.at( index )

  • index (Number) - Index of the value to fetch

Returns a value wrapped in an AST query interface.

value( value )

Replace current node with a new value. Returns the new value wrapped.

Body node

.prepend( code )

Preprend the given code lines in the body. If a "use strict"; statement is present, it always stay first.

.append( code )

Append the given code lines in the body.

Contributing

Style Guide: Please base yourself on Idiomatic.js style guide with two space indent Unit test: Unit test are wrote in Mocha. Please add a unit test for every new feature or bug fix. npm test to run the test suite. Documentation: Add documentation for every API change. Feel free to send corrections or better docs! Pull Requests: Send fixes PR on the master branch. Any new features should be send on the wipbranch.

License

Copyright (c) 2013 Simon Boudrias (twitter: @vaxilart) Licensed under the MIT license.

ast-query's People

Contributors

azu avatar bezoerb avatar bryanforbes avatar clmath avatar greenkeeper[bot] avatar jmgunn87 avatar johngully avatar mottykohn avatar nomiad avatar sap9433 avatar sboudrias avatar sindresorhus avatar twotonebytes avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

ast-query's Issues

Dependencies don't fully support ES2015 classes

We have some code that uses ast-query, and it has started choking on some newer ES2015 features. The current error we get is this:
Error: Unknown node type Super.
at Controller.traverse (my-project/node_modules/estraverse/estraverse.js:518:31)
at traverse (my-project/node_modules/estraverse/estraverse.js:710:27)
at Object.attachComments (my-project/node_modules/estraverse/estraverse.js:799:9)
at new Tree (my-project/node_modules/ast-query/lib/tree.js:33:25)
at module.exports (my-project/node_modules/ast-query/lib/tree.js:115:10)
....

After digging in, it appears that escodegen-wallaby has an outdated dependency on estraverse (1.9.x), which causes it to fail to parse certain newer code. estraverse has been updated with the fixes (4.x), but escodegen-wallaby has not been updated to use it. There is already an open issue on the upstream escodegen repo to update the estraverse dependency.

Just FYI. Feel free to close since the issue is really with escodegen-wallaby.

An in-range update of escodegen-wallaby is breaking the build 🚨

Version 1.6.13 of escodegen-wallaby was just published.

Branch Build failing 🚨
Dependency escodegen-wallaby
Current Version 1.6.12
Type dependency

This version is covered by your current version range and after updating it in your project the build failed.

escodegen-wallaby is a direct dependency of this project, and it is very likely causing it to break. If other packages depend on yours, this update is probably also breaking those in turn.

Status Details
  • continuous-integration/travis-ci/push The Travis CI build failed Details

Commits

The new version differs by 2 commits.

See the full diff

FAQ and help

There is a collection of frequently asked questions. If those don’t help, you can always ask the humans behind Greenkeeper.


Your Greenkeeper Bot 🌴

An in-range update of acorn-jsx is breaking the build 🚨

Version 4.1.0 of acorn-jsx was just published.

Branch Build failing 🚨
Dependency acorn-jsx
Current Version 4.0.1
Type dependency

This version is covered by your current version range and after updating it in your project the build failed.

acorn-jsx is a direct dependency of this project, and it is very likely causing it to break. If other packages depend on yours, this update is probably also breaking those in turn.

Status Details
  • continuous-integration/travis-ci/push The Travis CI build failed Details

Release Notes Release 4.1.0

Added support for JSX fragments (by @ArtemGovorov)

Commits

The new version differs by 2 commits.

See the full diff

FAQ and help

There is a collection of frequently asked questions. If those don’t help, you can always ask the humans behind Greenkeeper.


Your Greenkeeper Bot 🌴

Parsing `export default`

Hi there,

I'm trying to parse an export default statement in order to append something to an array. Ideally I'd like the following to work:

program = require('ast-query');
tree = program('export default [];', {}, { sourceType: 'module' });
console.log(tree.push('item').toString());

However I get the following when trying to parse the program:

screenshot 2016-03-06 09 49 30

Update object val that points to a variable

When trying to update a config block that was a variable reference, an error was being thrown:

var conf = {
  abc: bar
};

/Users/eddie/Dropbox/Sites/OSS/yeoman/generator/node_modules/gruntfile-editor/index.js:33
    .key(name).value(config);
               ^
TypeError: Object #<Object> has no method 'value'
    at GruntfileEditor.insertConfig (/Users/eddie/Dropbox/Sites/OSS/yeoman/generator/node_modules/gruntfile-editor/index.js:33:16)
    at module.exports (/Users/eddie/Dropbox/Sites/OSS/yeoman/generator-angular/helpers/gruntfile.js:16:18)
    at module.exports.yeoman.generators.Base.extend.writing.build (/Users/eddie/Dropbox/Sites/OSS/yeoman/generator-angular/generators/app/index.js:255:17)
    at /Users/eddie/Dropbox/Sites/OSS/yeoman/generator/lib/base.js:387:14
    at processImmediate [as _immediateCallback] (timers.js:336:15)

Question: Adding an element to a shorthand object

I am trying to edit a file that has a block like this

const reducers = combineReducers({
    app,
    home
});

I have been able to add a new key to it using this command:

tree.callExpression('combineReducers').arguments.at(0).key('test').value('test')

This ends up creating code like this:

const reducers = combineReducers({
    app,
    home,
    test: test
});

How can I get it to result in this??:

const reducers = combineReducers({
    app,
    home,
    test
});

Rewrite

The current code isn't great. It abstract recursing over a tree and making modifications based on strings.

This make for some pretty complicated code logic, and managing the white space/commas is not ideal (currently RegExp based).

From this experience, I plan to rewrite this module completely.

The way I view it, AST-Query should work directly on the tree object by appending and searching nodes. This mean modifications will be instantaneous (no need to process the tree after each modifications) and will hopefully make the code simpler and more reliable.

A main goal is adding capacity to inject new nodes in the three. Currently it is easy to modify an existing node, but not injecting one.

Expose valueFactory and teach it to create properties

I wanted to do some manipulation and I couldn't find a way to insert a property into an ObjectExpression. I dug around and found the value factory (/lib/factory/value.js) that allows to easily create AST elements.

However I couldn't find a way to create an object property, is that supported somehow?

Would be nice to have access to the value factory and be able to create object properties through it.

EDIT: this seems to work:

/**
 * Create a property node from a value string
 * @param  {String} valKey key string
 * @param  {String} valStr value string
 * @return {Object}        Property node
 */
exports.createProp = function (valKey, valStr) {
  var tree = esprima.parse('var astValFactory = { ' + valKey + ': ' + valStr + '};',esprimaOptions);
  tree = escodegen.attachComments(tree, tree.comments, tree.tokens);
  return tree.body[0].declarations[0].init.properties[0];
};

Thanks!

An in-range update of grunt is breaking the build 🚨

Version 1.0.2 of grunt was just published.

Branch Build failing 🚨
Dependency grunt
Current Version 1.0.1
Type devDependency

This version is covered by your current version range and after updating it in your project the build failed.

grunt is a devDependency of this project. It might not break your production code or affect downstream projects, but probably breaks your build or test tools, which may prevent deploying or publishing.

Status Details
  • continuous-integration/travis-ci/push The Travis CI build failed Details

Commits

The new version differs by 27 commits.

  • ccc3163 v1.0.2
  • e7795dc Remove iojs from test matrix (#1622)
  • a6a133b Remove deprecation warning for coffeescript (#1621) r=@vladikoff
  • 06b377e https links to webchat.freenode.net and gruntjs.com (#1610)
  • 7c5242f Use node executable for local grunt bin for Windows support
  • f6cbb63 Oh right, Windows isnt bash
  • a9a20da Try and debug why appveyor is failing on npm
  • 48bba6c Move to the test script to avoid npm was unexpected at this time.
  • 6b97ac0 Try to fix appveyor script
  • 19003ba For appveyor, check node version to decide whether to install npm@3
  • 3fcf921 Install npm@3 if npm version is npm 1 or 2
  • 77fc157 Use the local version of grunt to run tests
  • 400601a Updating devDependencies
  • 6592ad1 Update travis/appveyor to node versions we support
  • b63aeec Dont manually install npm, use the version each node version corresponds with

There are 27 commits in total.

See the full diff

FAQ and help

There is a collection of frequently asked questions. If those don’t help, you can always ask the humans behind Greenkeeper.


Your Greenkeeper Bot 🌴

Expand API for tree manipulation

Maybe I'm missing something fundamental here, but it seems the API is very limited with regards to tree manipulation (or the documentation is lacking). E.g. I'd want to replace the arguments of a CallExpression, but the ArrayExpression node only provides methods to add nodes, not remove them. Obviously I can start hacking away and start doing stuff like methodCall.arguments.nodes[0].pop(), but that's verrrry yucky.

Am I missing the point of your lib, or is this planned, or did I just somehow miss this?

Problem with "use strict" and body.prepend()

Hi,

I found a bug while writing a yeoman generator. The sort function in body.prepend is not correct in all cases. You can see this jsfiddle that demonstrate the problem: http://jsfiddle.net/j5xynq0k/.

I can think of two options to correct this:

  1. Correcting the sort function
Body.prototype.prepend = function (code) {
  var values = esprima.parse(code).body;
  Array.prototype.unshift.apply(this.node, values);

  // Ensure "use strict" declaration is kept on top
  this.node.sort(function (prev, node) {
    if (node && node.expression && node.expression.type === 'Literal' && node.expression.value === 'use strict') {
      return 1;
    } else if (prev && prev.expression && prev.expression.type === 'Literal' && prev.expression.value === 'use strict') {
        return -1;
    } else {
        return 0;
    }
  });

  return this;
};
  1. Getting rid of the sort as "use strict" statement will always be first. This also allow to keep statement in order (whereas sort is not stable).
Body.prototype.prepend = function (code) {
  var values = esprima.parse(code).body;
  var insertionIndex = 0;
  var nodes = this.node;

  // Ensure "use strict" declaration is kept on top
  if (nodes[0] && nodes[0].expression && nodes[0].expression.type === 'Literal' && nodes[0].expression.value === 'use strict') {
    insertionIndex = 1;
  }

  values.forEach(function (value){
    nodes.splice(insertionIndex, 0, value);
  });

  return this;
};

I'd rather go for 2) as it keep statements order.

Thanks for your work on this very useful api !

More Generic selectors

I am trying to query the remove properties in this data object and then remove them.
Does AST-query allow more generic queries like (just an example)
module.exports = Object(\.+\g, Array(Object(remove)))

module.exports = {
  form0: [
    {
      remain: "remain val",
      remove: "remove val",
    },
    {
      remove: "remove val",
      remain: "remain val",
    },
  ],
  form1: [
    {
      remain2: "remain2 val",
      remain1: "remain1 val",
    }
  ],
}

Result once all remove keys are removed

module.exports = {
  form0: [
    {
      remain: "remain val",
    },
    {
      remain: "remain val",
    },
  ],
  form1: [{
    remain2: "remain2 val",
    remain1: "remain1 val",
  }],
}

esprima parse option support, primarily for modules

esprima.parse('import foo from "bar"');

The online parser demo at http://esprima.org/demo/parse.html also gives this error.

You need to specify that it is a module that is being parsed (vs a script):

esprima.parse('import foo from "bar"', { sourceType: 'module'} );

using ast-query, we are currently unable to pass options to parse.

will fork and make sure value flows to base esprima provider.

An in-range update of lodash is breaking the build 🚨

Version 4.17.5 of lodash was just published.

Branch Build failing 🚨
Dependency lodash
Current Version 4.17.4
Type dependency

This version is covered by your current version range and after updating it in your project the build failed.

lodash is a direct dependency of this project, and it is very likely causing it to break. If other packages depend on yours, this update is probably also breaking those in turn.

Status Details
  • continuous-integration/travis-ci/push The Travis CI build failed Details

FAQ and help

There is a collection of frequently asked questions. If those don’t help, you can always ask the humans behind Greenkeeper.


Your Greenkeeper Bot 🌴

Feature request: query by character position (range)

I'd love to tell someone what something is where their cursor is. Is it possible to query by "range"? For example, I could pass tree.atPosition(48) and it would return

Node {
       type: 'CallExpression',
       start: 47,
       end: 62,
       range: [Object],
       callee: [Object],
       arguments: [Object] }

ArrayExpression.value() method

A value() method for arrays would be nice, like ObjectExpression.value().
I'm working on a pull request now but might have a few questions.

Also, adding ArrayExpression tests.

Allow append/prepend API for function bodies / Expose nodes modules

Are we able to get append/prepend API for inserting calls in function bodies? Currently the append/prepend API only works for the tree body.

Example:

function test() {
    foo('test');
}

Fails: c.body.node[0].body.append('bar();').

So I looked for workarounds. I tried to manually push this new call into the body array via c.body.node[0].body.body.push('bar();'). But doing so fails because bar(); is a string and not an esprima object. A possible solution is to expose AST-query nodes modules so we can do the above. Or add documentation that the body array requires an esprima object.

I can code this change, just need vision.

Update to acorn 5

Any way to get this to use acorn 5 so the "range" property is included?

Version 10 of node.js has been released

Version 10 of Node.js (code name Dubnium) has been released! 🎊

To see what happens to your code in Node.js 10, Greenkeeper has created a branch with the following changes:

  • Added the new Node.js version to your .travis.yml

If you’re interested in upgrading this repo to Node.js 10, you can open a PR with these changes. Please note that this issue is just intended as a friendly reminder and the PR as a possible starting point for getting your code running on Node.js 10.

More information on this issue

Greenkeeper has checked the engines key in any package.json file, the .nvmrc file, and the .travis.yml file, if present.

  • engines was only updated if it defined a single version, not a range.
  • .nvmrc was updated to Node.js 10
  • .travis.yml was only changed if there was a root-level node_js that didn’t already include Node.js 10, such as node or lts/*. In this case, the new version was appended to the list. We didn’t touch job or matrix configurations because these tend to be quite specific and complex, and it’s difficult to infer what the intentions were.

For many simpler .travis.yml configurations, this PR should suffice as-is, but depending on what you’re doing it may require additional work or may not be applicable at all. We’re also aware that you may have good reasons to not update to Node.js 10, which is why this was sent as an issue and not a pull request. Feel free to delete it without comment, I’m a humble robot and won’t feel rejected 🤖


FAQ and help

There is a collection of frequently asked questions. If those don’t help, you can always ask the humans behind Greenkeeper.


Your Greenkeeper Bot 🌴

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.