tunnckoCore/gibon

View on GitHub
@packages/parse-function/README.md

Summary

Maintainability
Test Coverage
<p align="center">
  <img
    align="center"
    src="https://cdn.jsdelivr.net/emojione/assets/svg/1f54e.svg"
    width="256"
    height="256"
    alt="Parse a function"
  />
</p>

# parse-function [![npm version][npmv-img]][npmv-url] [![License][license-img]][license-url] [![Libera Manifesto][libera-manifesto-img]][libera-manifesto-url]

> Parse a function into an object using espree, acorn or babylon parsers.
> Extensible through Smart Plugins

Please consider following this project's author,
[Charlike Mike Reagent](https://github.com/tunnckoCore), and :star: the project
to show your :heart: and support.

<div id="readme"></div>

[![Code style][codestyle-img]][codestyle-url]
[![CircleCI linux build][linuxbuild-img]][linuxbuild-url]
[![CodeCov coverage status][codecoverage-img]][codecoverage-url]
[![Renovate App Status][renovateapp-img]][renovateapp-url]
[![Make A Pull Request][prs-welcome-img]][prs-welcome-url]
[![Time Since Last Commit][last-commit-img]][last-commit-url]

<!-- [![Semantically Released][standard-release-img]][standard-release-url] -->

If you have any _how-to_ kind of questions, please read the [Contributing
Guide][contributing-url] and [Code of Conduct][code_of_conduct-url] documents.
For bugs reports and feature requests, [please create an issue][open-issue-url]
or ping [@tunnckoCore](https://twitter.com/tunnckoCore) at Twitter.

[![Conventional Commits][ccommits-img]][ccommits-url]
[![Minimum Required Nodejs][nodejs-img]][npmv-url]
[![NPM Downloads Monthly][downloads-monthly-img]][npmv-url]
[![NPM Downloads Total][downloads-total-img]][npmv-url]
[![Share Love Tweet][twitter-share-img]][twitter-share-url]
[![Twitter][twitter-img]][twitter-url]

Project is [semantically](https://semver.org) versioned & automatically released
from [GitHub Actions](https://github.com/features/actions) with
[Lerna](https://github.com/lerna/lerna).

[![Become a Patron][patreon-img]][patreon-url]
[![Buy me a Kofi][kofi-img]][kofi-url]
[![PayPal Donation][paypal-img]][paypal-url]
[![Bitcoin Coinbase][bitcoin-img]][bitcoin-url]
[![Keybase PGP][keybase-img]][keybase-url]

| Topic                                                            |                                           Contact |
| :--------------------------------------------------------------- | ------------------------------------------------: |
| Any legal or licensing questions, like private or commerical use |           ![tunnckocore_legal][tunnckocore_legal] |
| For any critical problems and security reports                   |     ![tunnckocore_security][tunnckocore_security] |
| Consulting, professional support, personal or team training      | ![tunnckocore_consulting][tunnckocore_consulting] |
| For any questions about Open Source, partnerships and sponsoring | ![tunnckocore_opensource][tunnckocore_opensource] |

<!-- Logo when needed:

<p align="center">
  <a href="https://github.com/tunnckoCore/opensource">
    <img src="./media/logo.png" width="85%">
  </a>
</p>

-->

## Features

- **Always up-to-date:** auto-publish new version when new version of dependency
  is out, [Renovate](https://renovateapp.com)
- **Standard:** using StandardJS, Prettier, SemVer, Semantic Release and
  conventional commits
- **Smart Plugins:** for extending the core API or the end [Result](#result),
  see [.use](#use) method and [Plugins Architecture](#plugins-architecture)
- **Extensible:** using plugins for working directly on AST nodes, see the
  [Plugins Architecture](#plugins-architecture)
- **ES2020+ Ready:** by using `.parseExpression` method of the Babel `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][], [@babel/parser](https://npmjs.com/packages/@babel/parser)
- **Tested:** with [450+ tests](./test/index.js) for _200%_ coverage

## Table of Contents

- [Install](#install)
- [Which version to use?](#which-version-to-use)
- [Notes](#notes)
  - [Throws in one specific case](#throws-in-one-specific-case)
  - [Function named _"anonymous"_](#function-named-_anonymous_)
  - [Real anonymous function](#real-anonymous-function)
  - [Plugins Architecture](#plugins-architecture)
- [API](#api)
  - [parseFunction](#parsefunction)
  - [.parse](#parse)
  - [.use](#use)
  - [.define](#define)
- [Contributing](#contributing)
  - [Guides and Community](#guides-and-community)
  - [Support the project](#support-the-project)
- [Contributors](#contributors)
- [License](#license)

_(TOC generated by [verb](https://github.com/verbose/verb) using
[markdown-toc](https://github.com/jonschlinkert/markdown-toc))_

## Install

This project requires [**Node.js**](https://nodejs.org) **>=8.11** _(see
[Support & Release Policy](https://github.com/tunnckoCoreLabs/support-release-policy))_.
Install it using [**yarn**](https://yarnpkg.com) or
[**npm**](https://npmjs.com).<br> _We highly recommend to use Yarn when you
think to contribute to this project._

```bash
$ yarn add parse-function
```

## 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](#plugins-architecture). See the [Features](#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.

**[back to top](#readme)**

## Notes

### Throws in one specific case

> _see: [issue #3](https://github.com/tunnckoCore/parse-function/issues/3) and
> [test/index.js#L229-L235](https://github.com/tunnckoCore/parse-function/blob/master/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.

### Function named _"anonymous"_

> _see:
> [test/index.js#L319-L324](https://github.com/tunnckoCore/parse-function/blob/master/test/index.js#L319-L324)
> and [Result](#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/index.js#L326-L331](https://github.com/tunnckoCore/parse-function/blob/master/test/index.js#L326-L331)
> and [Result](#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](#readme)**

### Plugins Architecture

> _see: the [.use](#use) method,
> [test/index.js#L305-L317](https://github.com/tunnckoCore/parse-function/blob/master/test/index.js#L305-L317)
> and
> [test/index.js#L396-L414](https://github.com/tunnckoCore/parse-function/blob/master/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](#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](#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 and **2)** the `result` is an object too - the end
[Result](#result), on which you can add more properties if you want.

**[back to top](#readme)**

## API

<!-- docks-start -->

_Generated using [jest-runner-docs](https://ghub.now.sh/jest-runner-docs)._

### [parseFunction](./src/index.js#L52)

> 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`.

<span id="parsefunction-signature"></span>

#### Signature

```ts
function(opts = {})
```

<span id="parsefunction-params"></span>

#### Params

- `opts` **{object}** - optional, merged with options passed to `.parse` method
- `returns` **{object}** - app object with `.use` and `.parse` methods

<span id="parsefunction-examples"></span>

#### Examples

```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'
```

### [.parse](./src/index.js#L117)

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

<span id="parse-params"></span>

#### 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
- `returns` **{object}** - result see [result section](#result) for more info

<span id="parse-examples"></span>

#### Examples

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

### [.use](./src/index.js#L170)

> 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](#result) from the `.parse`
> method. This retuned function is called on each node only when `.parse` method
> is called.

<span id="use-params"></span>

#### Params

- `fn` **{Function}** - plugin to be called
- `returns` **{object}** - app instance for chaining

_See [Plugins Architecture](#plugins-architecture) section._

<span id="use-examples"></span>

#### Examples

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

### [.define](./src/index.js#L228)

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

<span id="define-params"></span>

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

<span id="define-examples"></span>

#### Examples

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

<!-- docks-end -->

**[back to top](#readme)**

## Contributing

### Guides and Community

Please read the [Contributing Guide][contributing-url] and [Code of
Conduct][code_of_conduct-url] documents for advices.

For bug reports and feature requests, please join our [community][community-url]
forum and open a thread there with prefixing the title of the thread with the
name of the project if there's no separate channel for it.

Consider reading the
[Support and Release Policy](https://github.com/tunnckoCoreLabs/support-release-policy)
guide if you are interested in what are the supported Node.js versions and how
we proceed. In short, we support latest two even-numbered Node.js release lines.

### Support the project

[Become a Partner or Sponsor?][kofi-url] :dollar: Check the **OpenSource**
Commision (tier). :tada: You can get your company logo, link & name on this
file. It's also rendered on package's page in [npmjs.com][npmv-url] and
[yarnpkg.com](https://yarnpkg.com/en/package/parse-function) sites too! :rocket:

Not financial support? Okey!
[Pull requests](https://github.com/tunnckoCoreLabs/contributing#opening-a-pull-request),
stars and all kind of
[contributions](https://opensource.guide/how-to-contribute/#what-it-means-to-contribute)
are always welcome. :sparkles:

## Contributors

This project follows the
[all-contributors](https://github.com/all-contributors/all-contributors)
specification. Contributions of any kind are welcome!

Thanks goes to these wonderful people
([emoji key](https://allcontributors.org/docs/en/emoji-key)), consider showing
your [support](#support-the-project) to them:

<!-- ALL-CONTRIBUTORS-LIST:START -->
<!-- prettier-ignore-start -->
<!-- markdownlint-disable -->
<table>
  <tr>
    <td align="center"><a href="https://tunnckoCore.com"><img src="https://avatars3.githubusercontent.com/u/5038030?v=4" width="100px;" alt=""/><br /><sub><b>Charlike Mike Reagent</b></sub></a><br /><a href="#infra-tunnckoCore" title="Infrastructure (Hosting, Build-Tools, etc)">🚇</a> <a href="https://github.com/node-formidable/node-formidable/commits?author=tunnckoCore" title="Code">💻</a> <a href="https://github.com/node-formidable/node-formidable/commits?author=tunnckoCore" title="Documentation">📖</a> <a href="#ideas-tunnckoCore" title="Ideas, Planning, & Feedback">🤔</a> <a href="#maintenance-tunnckoCore" title="Maintenance">🚧</a> <a href="https://github.com/node-formidable/node-formidable/commits?author=tunnckoCore" title="Tests">⚠️</a></td>
  </tr>
</table>

<!-- markdownlint-enable -->
<!-- prettier-ignore-end -->

<!-- ALL-CONTRIBUTORS-LIST:END -->

**[back to top](#readme)**

## License

Copyright (c) 2016-present, [Charlike Mike Reagent](https://tunnckocore.com)
`<opensource@tunnckocore.com>` & [contributors](#wonderful-contributors).<br>
Released under the [MPL-2.0 License][license-url].

<!-- badges -->

<!-- prettier-ignore-start -->

[contributing-url]: https://github.com/tunnckoCore/opensource/blob/master/CONTRIBUTING.md
[code_of_conduct-url]: https://github.com/tunnckoCore/opensource/blob/master/CODE_OF_CONDUCT.md

<!-- Heading badges -->

[npmv-url]: https://www.npmjs.com/package/parse-function
[npmv-img]: https://badgen.net/npm/v/parse-function?icon=npm&cache=300

[license-url]: https://github.com/tunnckoCore/opensource/blob/master/packages/parse-function/LICENSE
[license-img]: https://badgen.net/npm/license/parse-function?cache=300

[libera-manifesto-url]: https://liberamanifesto.com
[libera-manifesto-img]: https://badgen.net/badge/libera/manifesto/grey

<!-- Front line badges -->

[codecoverage-img]: https://badgen.net/badge/coverage/100%25/green?icon=codecov&cache=300

[codecoverage-url]: https://codecov.io/gh/tunnckoCore/opensource

[codestyle-url]: https://github.com/airbnb/javascript
[codestyle-img]: https://badgen.net/badge/code%20style/airbnb/ff5a5f?icon=airbnb&cache=300

[linuxbuild-url]: https://github.com/tunnckocore/opensource/actions
[linuxbuild-img]: https://badgen.net/github/checks/tunnckoCore/opensource/master?cache=300&label=build&icon=github

[ccommits-url]: https://conventionalcommits.org/
[ccommits-img]: https://badgen.net/badge/conventional%20commits/v1.0.0/green?cache=300

[standard-release-url]: https://github.com/standard-release/standard-release
[standard-release-img]: https://badgen.net/badge/semantically/released/05c5ff?cache=300

[community-img]: https://badgen.net/badge/join/community/7b16ff?cache=300
[community-url]: https://github.com/tunnckocorehq/community

[last-commit-img]: https://badgen.net/github/last-commit/tunnckoCore/opensource/master?cache=300
[last-commit-url]: https://github.com/tunnckoCore/opensource/commits/master

[nodejs-img]: https://badgen.net/badge/node/>=8.11/green?cache=300

[downloads-weekly-img]: https://badgen.net/npm/dw/parse-function?icon=npm&cache=300
[downloads-monthly-img]: https://badgen.net/npm/dm/parse-function?icon=npm&cache=300
[downloads-total-img]: https://badgen.net/npm/dt/parse-function?icon=npm&cache=300

[renovateapp-url]: https://renovatebot.com
[renovateapp-img]: https://badgen.net/badge/renovate/enabled/green?cache=300

[prs-welcome-img]: https://badgen.net/badge/PRs/welcome/green?cache=300
[prs-welcome-url]: http://makeapullrequest.com

<!-- TODO: update icon -->

[paypal-url]: https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=HYJJEZNSGAPGC&source=url
[paypal-img]: https://badgen.net/badge/PayPal/donate/003087?cache=300&icon=https://simpleicons.now.sh/paypal/fff

<!-- TODO: update icon -->

[kofi-url]: https://ko-fi.com/tunnckoCore
[kofi-img]: https://badgen.net/badge/Buy%20me/a%20coffee/29abe0c2?cache=300&icon=https://rawcdn.githack.com/tunnckoCore/badgen-icons/f8264c6414e0bec449dd86f2241d50a9b89a1203/icons/kofi.svg

<!-- TODO: update icon -->

[bitcoin-url]: https://www.blockchain.com/btc/payment_request?address=3QNHKun1K1SUui1b4Z3KEGPPsWC1TgtnqA&message=Open+Source+Software&amount_local=10&currency=USD
[bitcoin-img]: https://badgen.net/badge/Bitcoin%20tip/3QNHKun...b4Z3KEGPPsWC1TgtnqA/yellow?cache=300&icon=https://simpleicons.now.sh/bitcoin/fff
[keybase-url]: https://keybase.io/tunnckoCore
[keybase-img]: https://badgen.net/keybase/pgp/tunnckoCore?cache=300
[twitter-url]: https://twitter.com/tunnckoCore
[twitter-img]: https://badgen.net/twitter/follow/tunnckoCore?icon=twitter&color=1da1f2&cache=300
[patreon-url]: https://www.patreon.com/bePatron?u=5579781
[patreon-img]: https://badgen.net/badge/Become/a%20patron/F96854?icon=patreon

<!-- [patreon-img]: https://badgen.net/badge/Patreon/tunnckoCore/F96854?icon=patreon -->

[patreon-sponsor-img]: https://badgen.net/badge/become/a%20sponsor/F96854?icon=patreon
[twitter-share-url]: https://twitter.com/intent/tweet?text=https://ghub.now.sh/parse-function&via=tunnckoCore
[twitter-share-img]: https://badgen.net/badge/twitter/share/1da1f2?icon=twitter
[open-issue-url]: https://github.com/tunnckoCore/opensource/issues/new
[tunnckocore_legal]: https://badgen.net/https/liam-badge-daknys6gadky.runkit.sh/com/legal/tunnckocore?label&color=A56016&icon=https://svgshare.com/i/Dt6.svg
[tunnckocore_consulting]: https://badgen.net/https/liam-badge-daknys6gadky.runkit.sh/com/consulting/tunnckocore?label&color=07ba96&icon=https://svgshare.com/i/Dt6.svg
[tunnckocore_security]: https://badgen.net/https/liam-badge-daknys6gadky.runkit.sh/com/security/tunnckocore?label&color=ed1848&icon=https://svgshare.com/i/Dt6.svg
[tunnckocore_opensource]: https://badgen.net/https/liam-badge-daknys6gadky.runkit.sh/com/opensource/tunnckocore?label&color=ff7a2f&icon=https://svgshare.com/i/Dt6.svg
[tunnckocore_newsletter]: https://badgen.net/https/liam-badge-daknys6gadky.runkit.sh/com/newsletter/tunnckocore?label&color=5199FF&icon=https://svgshare.com/i/Dt6.svg

<!-- prettier-ignore-end -->

[acorn]: https://github.com/acornjs/acorn
[babylon]: https://babeljs.io/
[cacache]: https://github.com/npm/cacache
[collect-mentions]: https://github.com/olstenlarck/collect-mentions
[define-property]: https://github.com/jonschlinkert/define-property
[espree]: https://github.com/eslint/espree
[execa]: https://github.com/sindresorhus/execa
[fast-glob]: https://github.com/mrmlnc/fast-glob
[glob]: https://github.com/isaacs/node-glob
[globby]: https://github.com/sindresorhus/globby
[koa-convert]: https://github.com/gyson/koa-convert
[koa]: https://github.com/koajs/koa
[tiny-glob]: https://github.com/terkelg/tiny-glob