JSPM

  • Created
  • Published
  • Downloads 270548
  • Score
    100M100P100Q169806F
  • License MIT

Parse a function into an object that has its name, body, args and a few more useful properties.

Package Exports

  • parse-function

This package does not declare an exports field, so the exports above have been automatically detected and optimized by JSPM instead. If any package subpath is missing, it is recommended to post an issue to the original package (parse-function) to support the "exports" field. If that is not possible, create a JSPM override to customize the exports field for this package.

Readme

parse-function npm version npm version npm downloads monthly npm downloads total
Parse a function
Parse a function into an object

Parse a function into an object that has its name, body, args and a few more useful properties.

codeclimate codestyle linux build windows build codecov dependency status

You might also be interested in function-arguments library if you need more lightweight solution and need for just getting the names of the function arguments.

Features

  • 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 275+ tests for 200% coverage

Table of Contents

Install

Install with npm

$ npm install parse-function --save

or install using yarn

$ yarn add parse-function

Usage

For more use-cases see the tests

const parseFunction = require('parse-function')
const app = parseFunction()

console.log(app.use) // => function
console.log(app.parse) // => function
console.log(app.define) // => function

back to top

Which version to use?

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", means "almost full", because it has bugs. This version also uses (acorn.parse) real parser to do the parsing.

Use v3.x

When you want to use different parser instead of the default babylon.parse, by passing custom parse function to the options.parse 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.

back to top

Notes

Throws in one specific case

see: issue #3 and test.js#L210-L215

It may throw in one specific case, otherwise it won't throw, so you should relay on the result.isValid for sure.

Function named "anonymous"

see: test.js#L293-L298 and Result section

If you pass a function which is named "anonymous" the result.name will be 'anonymous', but the result.isAnonymous will be false and result.isNamed will be true, because in fact it's a named function.

Real anonymous function

see: test.js#L300-L305 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.

back to top

Plugins Architecture

see: the .use method and test.js#L354-L382

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.

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.

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 and 2) the result is an object too - the end Result, on which you can add more properties if you want.

back to top

API

parseFunction

Initializes with optional opts 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

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'

.parse

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

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

.use

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

// 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

.define

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

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

back to top

Result

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

back to top

  • acorn: ECMAScript parser | homepage
  • always-done: Handle completion and errors with elegance! Support for streams, callbacks, promises, child processes, async/await and sync functions. A drop-in replacement… more | homepage
  • babylon: A JavaScript parser | homepage
  • each-promise: Iterate over promises, promise-returning or async/await functions in series or parallel. Support settle (fail-fast), concurrency (limiting) and hooks system (start… more | homepage
  • espree: An Esprima-compatible JavaScript parser built on Acorn | homepage
  • minibase: Minimalist alternative for Base. Build complex APIs with small units called plugins. Works well with most of the already existing… more | homepage
  • parse-semver: Parse, normalize and validate given semver shorthand (e.g. gulp@v3.8.10) to object. | homepage
  • try-catch-core: Low-level package to handle completion and errors of sync or asynchronous functions, using once and dezalgo libs. Useful for and… more | homepage

Contributing

Pull requests and stars are always welcome. For bugs and feature requests, please create an issue.
Please read the contributing guidelines for advice on opening issues, pull requests, and coding standards.
If you need some help and can spent some cash, feel free to contact me at CodeMentor.io too.

In short: If you want to contribute to that project, please follow these things

  1. Please DO NOT edit README.md, CHANGELOG.md and .verb.md files. See "Building docs" section.
  2. Ensure anything is okey by installing the dependencies and run the tests. See "Running tests" section.
  3. Always use npm run commit to commit changes instead of git commit, because it is interactive and user-friendly. It uses commitizen behind the scenes, which follows Conventional Changelog idealogy.
  4. Do NOT bump the version in package.json. For that we use npm run release, which is standard-version and follows Conventional Changelog idealogy.

Thanks a lot! :)

Building docs

Documentation and that readme is generated using verb-generate-readme, which is a verb generator, so you need to install both of them and then run verb command like that

$ npm install verbose/verb#dev verb-generate-readme --global && verb

Please don't edit the README directly. Any changes to the readme must be made in .verb.md.

Running tests

Clone repository and run the following in that cloned directory

$ npm install && npm test

Author

Charlike Mike Reagent

The logo is Menorah Emoji from EmojiOne.com. Released under the CC BY 4.0 license.

License

Copyright © 2015, 2017, Charlike Mike Reagent. Released under the MIT License.


This file was generated by verb-generate-readme, v0.4.3, on March 09, 2017.
Project scaffolded using charlike cli.