Parse a function into an object using espree, acorn or babylon parsers. Extensible through Smart Plugins
npm install @ericmorand/parse-function
> Parse a function into an object using espree, acorn or babylon parsers. Extensible through Smart Plugins
You might also be interested in hela.
[![Code Style Standard][standard-img]][standard-url]
[![Linux Build][travis-img]][travis-url]
[![Code Coverage][codecov-img]][codecov-url]
[![Dependencies Status][dependencies-img]][dependencies-url]
[![Renovate App Status][renovate-img]][renovate-url]
If you have any _how-to_ kind of questions, please read Code of Conduct and join the chat room or [open an issue][open-issue-url].
You may also read the Contributing Guide. There, beside _"How to contribute?"_, we describe everything _stated_ by the badges.
[![tunnckoCore support][gitterchat-img]][gitterchat-url]
[![Code Format Prettier][prettier-img]][prettier-url]
[![node security status][nodesecurity-img]][nodesecurity-url]
[![conventional Commits][ccommits-img]][ccommits-url]
[![semantic release][semantic-release-img]][semantic-release-url]
[![Node Version Required][nodeversion-img]][nodeversion-url]
- Always up-to-date: auto-publish new version when new version of dependency is out, Renovate
- Standard: using StandardJS, Prettier, SemVer, Semantic Release and conventional commits
- Smart Plugins: for extending the core API or the end Result, see .use method and Plugins Architecture
- Extensible: using plugins for working directly on AST nodes, see the Plugins Architecture
- ES2017 Ready: by using .parseExpression method of the [babylon][] v7.x parser
- Customization: allows switching the parser, through options.parse
- Support for: arrow functions, default parameters, generators and async/await functions
- Stable: battle-tested in production and against all parsers - [espree][], [acorn][], [babylon][]
- Tested: with 370+ tests for _200%_ coverage
_(TOC generated by verb using markdown-toc)_
This project requires [Node.js][nodeversion-url] v6 and above. Use yarn v1 / npm v5 or above to install it.
```
$ yarn add parse-function
There's no breaking changes between the v2.x version. The only breaking is v2.1 which also is not
working properly, so no use it.
Use v2.0.x
When you don't need support for arrow functions and es6 default params. This version
uses a RegExp expression to work.
Use v2.2.x
Only when you need a _basic_ support for es6 features like arrow functions. This version
uses a RegExp expression to work.
Use v2.3.x
When you want _full*_ support for arrow functions and es6 default params. Where this "full",acorn.parse
means "almost full", because it has bugs. This version also uses () real parser
to do the parsing.
Use v3.x
When you want to use different parser instead of the default babylon.parse, by passing customoptions.parse
parse function to the option. From this version we require node >= 4.
Use v4.x
When you want full customization and most stable support for old and modern features. This version
uses babylon.parseExpression for parsing and provides a Plugins API.
See the Features section for more info.
Use v5.x
It is basically the same as v4, but requires Node 6 & npm 5. Another is boilerplate stuff.
> _see: issue #3 and test/index.js#L229-L235_
It may throw in one specific case, otherwise it won't throw, so you should
relay on the result.isValid for sure.
> _see: test/index.js#L319-L324 and Result section_
If you pass a function which is named _"anonymous"_ the result.name will be 'anonymous', result.isAnonymous
but the will be false and result.isNamed will be true, because
in fact it's a named function.
> _see: test/index.js#L326-L331 and Result section_
Only if you pass really an anonymous function you will get result.name equal to null, result.isAnonymous equal to true and result.isNamed equal to false.
> _see: the .use method, test/index.js#L305-L317 and test/index.js#L396-L414_
A more human description of the plugin mechanism. Plugins are synchronous - no support
and no need for async plugins here, but notice that you can do that manually, because
that exact architecture.
The first function that is passed to the .use method is used for extending the core API,
for example adding a new method to the app instance. That function is immediately invoked.
`js
const parseFunction = require('parse-function')
const app = parseFunction()
app.use((self) => {
// self is same as app
console.log(self.use)
console.log(self.parse)
console.log(self.define)
self.define(self, 'foo', (bar) => bar + 1)
})
console.log(app.foo(2)) // => 3
`
On the other side, if you want to access the AST of the parser, you should return a function
from that plugin, which function is passed with (node, result) signature.
This function is lazy plugin, it is called only when the .parse method is called.
`js
const parseFunction = require('parse-function')
const app = parseFunction()
app.use((self) => {
console.log('immediately called')
return (node, result) => {
console.log('called only when .parse is invoked')
console.log(node)
console.log(result)
}
})
`
Where 1) the node argument is an object - actual and real AST Node coming from the parser result
and 2) the is an object too - the end Result, on which
you can add more properties if you want.
object which is passed directly to the desired parser and returns an object with .use and .parse methods. The default parse which is used is [babylon][]'s .parseExpression method from v7.Params
*
opts {Object}: optional, merged with options passed to .parse method
* returns {Object} app: object with .use and .parse methods Example
`js
const parseFunction = require('parse-function')const app = parseFunction({
ecmaVersion: 2017
})
const fixtureFn = (a, b, c) => {
a = b + c
return a + 2
}
const result = app.parse(fixtureFn)
console.log(result)
// see more
console.log(result.name) // => null
console.log(result.isNamed) // => false
console.log(result.isArrow) // => true
console.log(result.isAnonymous) // => true
// array of names of the arguments
console.log(result.args) // => ['a', 'b', 'c']
// comma-separated names of the arguments
console.log(result.params) // => 'a, b, c'
`$3
> Parse a given code and returns a result object with useful properties - such as name, body and args. By default it uses Babylon parser, but you can switch it by passing options.parse - for example options.parse: acorn.parse. In the below example will show how to use acorn parser, instead of the default one.Params
*
code {Function|String}: any kind of function or string to be parsed
* options {Object}: directly passed to the parser - babylon, acorn, espree
* options.parse {Function}: by default babylon.parseExpression, all options are passed as second argument to that provided function
* returns {Object} result: see result section for more info Example
`js
const acorn = require('acorn')
const parseFn = require('parse-function')
const app = parseFn()const fn = function foo (bar, baz) { return bar * baz }
const result = app.parse(fn, {
parse: acorn.parse,
ecmaVersion: 2017
})
console.log(result.name) // => 'foo'
console.log(result.args) // => ['bar', 'baz']
console.log(result.body) // => ' return bar * baz '
console.log(result.isNamed) // => true
console.log(result.isArrow) // => false
console.log(result.isAnonymous) // => false
console.log(result.isGenerator) // => false
`$3
> Add a plugin fn function for extending the API or working on the AST nodes. The fn is immediately invoked and passed with app argument which is instance of parseFunction() call. That fn may return another function that accepts (node, result) signature, where node is an AST node and result is an object which will be returned result from the .parse method. This retuned function is called on each node only when .parse method is called._See Plugins Architecture section._
Params
*
fn {Function}: plugin to be called
* returns {Object} app: instance for chaining Example
`js
// plugin extending the app
app.use((app) => {
app.define(app, 'hello', (place) => Hello ${place}!)
})const hi = app.hello('World')
console.log(hi) // => 'Hello World!'
// or plugin that works on AST nodes
app.use((app) => (node, result) => {
if (node.type === 'ArrowFunctionExpression') {
result.thatIsArrow = true
}
return result
})
const result = app.parse((a, b) => (a + b + 123))
console.log(result.name) // => null
console.log(result.isArrow) // => true
console.log(result.thatIsArrow) // => true
const result = app.parse(function foo () { return 123 })
console.log(result.name) // => 'foo'
console.log(result.isArrow) // => false
console.log(result.thatIsArrow) // => undefined
`$3
> Define a non-enumerable property on an object. Just a convenience mirror of the [define-property][] library, so check out its docs. Useful to be used in plugins.Params
*
obj {Object}: the object on which to define the property
* prop {String}: the name of the property to be defined or modified
* val {Any}: the descriptor for the property being defined or modified
* returns {Object} obj: the passed object, but modified Example
`js
const parseFunction = require('parse-function')
const app = parseFunction()// use it like
define-property lib
const obj = {}
app.define(obj, 'hi', 'world')
console.log(obj) // => { hi: 'world' }// or define a custom plugin that adds
.foo property
// to the end result, returned from app.parse
app.use((app) => {
return (node, result) => {
// this function is called
// only when .parse is called app.define(result, 'foo', 123)
return result
}
})
// fixture function to be parsed
const asyncFn = async (qux) => {
const bar = await Promise.resolve(qux)
return bar
}
const result = app.parse(asyncFn)
console.log(result.name) // => null
console.log(result.foo) // => 123
console.log(result.args) // => ['qux']
console.log(result.isAsync) // => true
console.log(result.isArrow) // => true
console.log(result.isNamed) // => false
console.log(result.isAnonymous) // => true
`$3
> In the result object you have name, args, params, body and few hidden properties
that can be useful to determine what the function is - arrow, regular, async/await or generator.*
name {String|null}: name of the passed function or null if anonymous
* args {Array}: arguments of the function
* params {String}: comma-separated list representing the args
* defaults {Object}: key/value pairs, useful when use ES2015 default arguments
* body {String}: actual body of the function, respects trailing newlines and whitespaces
* isValid {Boolean}: is the given value valid or not, that's because it never throws!
* isAsync {Boolean}: true if function is ES2015 async/await function
* isArrow {Boolean}: true if the function is arrow function
* isNamed {Boolean}: true if function has name, or false if is anonymous
* isGenerator {Boolean}: true if the function is ES2015 generator function
* isAnonymous {Boolean}: true` if the function don't have name*
_This file was generated by verb-generate-readme, v0.6.0, on February 14, 2018._
Project scaffolded and managed with [hela][].
[acorn]: https://github.com/acornjs/acorn
[babylon]: https://babeljs.io/
[charlike-cli]: https://github.com/tunnckoCore/charlike-cli
[charlike]: https://github.com/tunnckoCore/charlike
[define-property]: https://github.com/jonschlinkert/define-property
[espree]: https://github.com/eslint/espree
[execa]: https://github.com/sindresorhus/execa
[function-arguments]: https://github.com/tunnckocore/function-arguments
[npmv-url]: https://www.npmjs.com/package/parse-function
[npmv-img]: https://img.shields.io/npm/v/parse-function.svg
[open-issue-url]: https://github.com/tunnckoCore/parse-function/issues/new
[github-release-url]: https://github.com/tunnckoCore/parse-function/releases/latest
[github-release-img]: https://img.shields.io/github/release/tunnckoCore/parse-function.svg
[license-url]: https://github.com/tunnckoCore/parse-function/blob/master/LICENSE
[license-img]: https://img.shields.io/badge/license-MIT-blue.svg
[downloads-weekly-url]: https://www.npmjs.com/package/parse-function
[downloads-weekly-img]: https://img.shields.io/npm/dw/parse-function.svg
[downloads-total-url]: https://www.npmjs.com/package/parse-function
[downloads-total-img]: https://img.shields.io/npm/dt/parse-function.svg
[codeclimate-url]: https://codeclimate.com/github/tunnckoCore/parse-function
[codeclimate-img]: https://img.shields.io/codeclimate/github/tunnckoCore/parse-function.svg
[standard-url]: https://github.com/standard/standard
[standard-img]: https://img.shields.io/badge/code_style-standard-brightgreen.svg
[travis-url]: https://travis-ci.org/tunnckoCore/parse-function
[travis-img]: https://img.shields.io/travis/tunnckoCore/parse-function/master.svg?label=linux
[codecov-url]: https://codecov.io/gh/tunnckoCore/parse-function
[codecov-img]: https://img.shields.io/codecov/c/github/tunnckoCore/parse-function/master.svg
[dependencies-url]: https://david-dm.org/tunnckoCore/parse-function
[dependencies-img]: https://img.shields.io/david/tunnckoCore/parse-function.svg
[renovate-url]: https://renovateapp.com
[renovate-img]: https://img.shields.io/badge/renovate-enabled-brightgreen.svg
[gitterchat-url]: https://gitter.im/tunnckoCore/support
[gitterchat-img]: https://img.shields.io/gitter/room/tunnckoCore/support.svg
[prettier-url]: https://github.com/prettier/prettier
[prettier-img]: https://img.shields.io/badge/styled_with-prettier-f952a5.svg
[nodesecurity-url]: https://nodesecurity.io/orgs/tunnckocore/projects/42a5e14a-70da-49ee-86e7-d1f39ed08603
[nodesecurity-img]: https://nodesecurity.io/orgs/tunnckocore/projects/42a5e14a-70da-49ee-86e7-d1f39ed08603/badge
[semantic-release-url]: https://github.com/apps/new-release
[semantic-release-img]: https://img.shields.io/badge/%20%20%F0%9F%93%A6%F0%9F%9A%80-new--release-e10079.svg
[ccommits-url]: https://conventionalcommits.org/
[ccommits-img]: https://img.shields.io/badge/conventional_commits-1.0.0-yellow.svg
[nodeversion-url]: https://nodejs.org/en/download
[nodeversion-img]: https://img.shields.io/node/v/parse-function.svg
[hela]: https://github.com/tunnckoCore/hela