UNPKG

parse-function

Version:

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

572 lines (418 loc) 23.9 kB
# parse-function [![npm version][npmv-img]][npmv-url] [![License][license-img]][license-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] [![DavidDM dependency status][dependencies-img]][dependencies-url] [![Renovate App Status][renovateapp-img]][renovateapp-url] [![Time Since Last Commit][last-commit-img]][last-commit-url] [![Make A Pull Request][prs-welcome-img]][prs-welcome-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) - [Result](#result) - [Contributing](#contributing) - [Guides and Community](#guides-and-community) - [Support the project](#support-the-project) - [OPEN Open Source](#open-open-source) - [Wonderful Contributors](#wonderful-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)** <!-- docks-start --> ## API _Generated using [jest-runner-docs](https://npmjs.com/package/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`. **Signature** ```ts function(opts = {}) ``` **Params** - `opts` - optional, merged with options passed to `.parse` method - `returns` - 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' ``` ### [.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. **Params** - `code` - any kind of function or string to be parsed - `options` - directly passed to the parser babylon, acorn, espree - `options.parse` - by default `babylon.parseExpression`, all `options` are passed as second argument - `returns` - result see [result section](#result) 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 ``` ### [.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. **Params** - `fn` - plugin to be called - `returns` - app instance for chaining _See [Plugins Architecture](#plugins-architecture) section._ **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 ``` ### [.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. **Params** - `obj` - the object on which to define the property - `prop` - the name of the property to be defined or modified - `val` - the descriptor for the property being defined or modified - `returns` - 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 ``` <!-- docks-end --> ## 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](#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?][patreon-url] :dollar: Check the **Partner**, **Sponsor** or **Omega-level** tiers! :tada: You can get your company logo, link & name on this file. It's also rendered on package 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: <!-- ### OPEN Open Source This project is following [OPEN Open Source](http://openopensource.org) model > Individuals making significant and valuable contributions are given commit-access to the project to contribute as they see fit. This project is built on collective efforts and it's not strongly guarded by its founders. There are a few basic ground-rules for its contributors 1. Any **significant modifications** must be subject to a pull request to get feedback from other contributors. 2. [Pull requests](https://github.com/tunnckoCoreLabs/contributing#opening-a-pull-request) to get feedback are _encouraged_ for any other trivial contributions, but are not required. 3. Contributors should attempt to adhere to the prevailing code-style and development workflow. --> ### Wonderful Contributors Thanks to the hard work of these wonderful people this project is alive! It follows the [all-contributors](https://github.com/kentcdodds/all-contributors) specification. Don't hesitate to add yourself to that list if you have made any contribution! ;) [See how, here](https://github.com/jfmengels/all-contributors-cli#usage). <!-- ALL-CONTRIBUTORS-LIST:START - Do not remove or modify this section --> <!-- prettier-ignore --> | [<img src="https://avatars3.githubusercontent.com/u/5038030?v=4" width="120px;"/><br /><sub><b>Charlike Mike Reagent</b></sub>](https://tunnckocore.com)<br />[💻](https://github.com/tunnckoCore/opensource/commits?author=tunnckoCore "Code") [📖](https://github.com/tunnckoCore/opensource/commits?author=tunnckoCore "Documentation") [💬](#question-tunnckoCore "Answering Questions") [👀](#review-tunnckoCore "Reviewed Pull Requests") [🔍](#fundingFinding-tunnckoCore "Funding Finding") | | :---: | <!-- ALL-CONTRIBUTORS-LIST:END --> Consider showing your [support](#support-the-project) to them. :sparkling_heart: **[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]. [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 [nodejs-img]: https://badgen.net/npm/node/parse-function <!-- [ghrelease-url]: https://github.com/tunnckoCore/opensource/releases/latest [ghrelease-img]: https://badgen.net/github/release/tunnckoCore/opensource?icon=github --> [license-url]: https://github.com/tunnckoCore/opensource/blob/master/packages/parse-function/LICENSE [license-img]: https://badgen.net/npm/license/parse-function <!-- Front line badges --> [codestyle-url]: https://github.com/airbnb/javascript [codestyle-img]: https://badgen.net/badge/code%20style/airbnb/ff5a5f?icon=airbnb [linuxbuild-url]: https://github.com/tunnckocore/opensource/actions [linuxbuild-img]: https://badgennet.charlike-oss.now.sh/github/checks/tunnckoCore/opensource/master?label=build&icon=github [codecoverage-url]: https://codecov.io/gh/tunnckoCore/opensource [codecoverage-img]: https://badgen.net/codecov/c/github/tunnckoCore/opensource?icon=codecov [dependencies-url]: https://david-dm.org/tunnckoCore/opensource [dependencies-img]: https://badgen.net/david/dep/tunnckoCore/opensource?label=deps [ccommits-url]: https://conventionalcommits.org/ [ccommits-img]: https://badgen.net/badge/conventional%20commits/v1.0.0/green [standard-release-url]: https://github.com/standard-release/standard-release [standard-release-img]: https://badgen.net/badge/semantically/released/05c5ff [community-img]: https://badgen.net/badge/join/community/7b16ff [community-url]: https://github.com/tunnckocorehq/community [last-commit-img]: https://badgen.net/github/last-commit/tunnckoCore/opensource/master [last-commit-url]: https://github.com/tunnckoCore/opensource/commits/master [downloads-weekly-img]: https://badgen.net/npm/dw/parse-function?icon=npm [downloads-monthly-img]: https://badgen.net/npm/dm/parse-function?icon=npm [downloads-total-img]: https://badgen.net/npm/dt/parse-function?icon=npm [renovateapp-url]: https://renovatebot.com [renovateapp-img]: https://badgen.net/badge/renovate/enabled/green [prs-welcome-img]: https://badgen.net/badge/PRs/welcome/green [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?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?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?icon=https://simpleicons.now.sh/bitcoin/fff [keybase-url]: https://keybase.io/tunnckoCore [keybase-img]: https://badgen.net/keybase/pgp/tunnckoCore [twitter-url]: https://twitter.com/tunnckoCore [twitter-img]: https://badgen.net/twitter/follow/tunnckoCore?icon=twitter&color=1da1f2 [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://github.com/tunnckoCore/opensource&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 [acorn]: https://github.com/acornjs/acorn [babylon]: https://babeljs.io/ [define-property]: https://github.com/jonschlinkert/define-property [espree]: https://github.com/eslint/espree