diff --git a/@types/.gitkeep b/@types/.gitkeep new file mode 100644 index 00000000..e69de29b diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md index 5fa3fa7f..286360ae 100644 --- a/CODE_OF_CONDUCT.md +++ b/CODE_OF_CONDUCT.md @@ -55,7 +55,7 @@ further defined and clarified by project maintainers. ## Enforcement Instances of abusive, harassing, or otherwise unacceptable behavior may be -reported by contacting the project team at `open.source.charlike@gmail.com` mail address. All +reported by contacting the project team at `hello@tunnckocore.com` mail address. All complaints will be reviewed and investigated and will result in a response that is deemed necessary and appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. diff --git a/package.json b/package.json index 3e30ffda..d9f0bf79 100644 --- a/package.json +++ b/package.json @@ -15,19 +15,20 @@ "clean:dist": "rm -rf configs/*/dist packages/*/dist @tunnckocore/*/dist", "clean:fresh": "lerna clean -y && rm -rf node_modules", "cleanup": "yarn clean:dist && yarn clean:fresh", + "cmt": "LOCAL_TESTING=0 CI=1 yarn start test -u && yarn testy && git status --porcelain", "cr": "node create-package.js", "patch:hela": "cp patches/hela-cli/* node_modules/@hela/cli/dist/build/cjs/", "patch:sade": "cp patches/sade/* node_modules/sade/lib/", "patch:verb": "cp patches/verb-repo-helpers/index.js node_modules/verb-repo-helpers/index.js", "postsetup": "yarn patch:verb && yarn patch:hela && yarn patch:sade", "postsetup:ci": "yarn patch:verb && yarn patch:hela && yarn patch:sade", - "pre-commit": "yarn docs && yarn fmt", + "pre-commit": "echo nothing", "refresh": "yarn cleanup && yarn setup", "release": "lerna version && lerna publish from-package", "setup": "yarn && yarn bootstrap", "setup:ci": "yarn --frozen-lockfile && yarn bootstrap", "start": "node node_modules/@hela/cli/dist/build/cjs/cli.js", - "test": "CI=1 yarn start test", + "test": "FORCE_COLOR=1 CI=1 LOCAL_TESTING=1 yarn start test", "testy": "node generate-coverage-info.js && yarn start docs && yarn start format" }, "dependencies": { @@ -70,8 +71,8 @@ }, "husky": { "hooks": { - "pre-commit": "echo 'yarn run pre-commit'", - "commit-msg": "echo 'commitlint -E HUSKY_GIT_PARAMS'" + "pre-commit": "yarn run pre-commit", + "commit-msg": "commitlint -E HUSKY_GIT_PARAMS" } }, "jest": { diff --git a/packages/parse-function/.verb.md b/packages/parse-function/.verb.md index bde4b12d..7bea87d0 100644 --- a/packages/parse-function/.verb.md +++ b/packages/parse-function/.verb.md @@ -67,47 +67,34 @@ Only if you pass really an anonymous function you will get `result.name` equal t > _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. +and no need for **async** plugins here. Plugins MAY or MAY NOT return Result object, so +don't worry if you miss to return something. ```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 +import { parseFunction } from 'parse-function'; + +function someFn(foo, bar) { + return foo + bar; +} + +const plugins = [ + (node, result) => { + return { ...result, zaz: 111 }; + }, + (node, result) => { + return { qux: result.zaz + 202 }; + }, +]; + +const res = parseFunction(someFn, { plugins }); +console.log(res); + +console.log(res.name); // => 'someFn' +console.log(res.zaz); // => 111 +console.log(res.qux); // => 313 ``` -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); - }; -}); -``` +If you want to access the AST of the parser, use the `node` parameter of the plugin. 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 @@ -121,215 +108,151 @@ you can add more properties if you want. _Generated using [jest-runner-docs](https://npmjs.com/package/jest-runner-docs)._ -### [parseFunction](./src/index.js#L52) +### [.parseFunction](./src/index.js#L84) -> 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`. +Parse a given `input` and returns a `Result` object +with useful properties - such as `name`, `body` and `args`. +By default it uses `@babel/parser` parser, but you can switch it by +passing `options.parse` or `options.parseExpression`, for example `options.parse: acorn.parse`. +In the below example will show how to use `acorn` parser, instead +of the default one. **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' +function(input, options) ``` -### [.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 +- `input` - 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 +- `options.parse` - by default `@babel/parser`'s `.parse` or `.parseExpression`, +- `options.parserOptions` - passed to the parser +- `options.plugins` - a plugin function like `function plugin(node: Node, result: Result): Result {}` +- `returns` - result object of the `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, +import { parse as acornParse } from 'acorn'; +import { parse as espreeParse } from 'espree'; +import { parseFunction } from 'parse-function'; + +// or in CommonJS +// const { parseFunction } = require('parse-function'); +// const parseFunction = require('parse-function').parseFunction; +// const fn = require('parse-function'); +// fn.parseFunction() + +function fooFn(bar, baz = 123) { + return bar + baz; +} + +const result1 = parseFunction(fooFn, { parse: acornParse }); +console.log(result1); + +const result2 = parseFunction(fooFn, { + parse: espreeParse, + parserOptions: { + ecmaVersion: 9, + sourceType: 'module', + ecmaFeatures: { jsx: true, globalReturn: true }, + }, }); -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 +console.log('parsed with espree', result2); +// => { +// name: 'fooFn', +// body: '\n return bar + baz;\n', +// args: [ 'bar', 'baz' ], +// params: 'bar, baz', +// defaults: { bar: undefined, baz: '123' }, +// value: '(function fooFn(bar, baz = 123) {\n return bar + baz;\n})', +// isValid: true, +// isArrow: false, +// isAsync: false, +// isNamed: true, +// isAnonymous: false, +// isGenerator: false, +// isExpression: false, +// bobby: 'bobby', +// barry: 'barry barry', +// hasDefaultParams: true +// } + +function basicPlugin(node, result) { + const bar = 'barry'; + const hasDefaultParams = + Object.values(result.defaults).filter(Boolean).length > 0; + + return { ...result, foo: 123, bar, hasDefaultParams }; +} + +const resultWithPlugins = parseFunction(fooFn, { plugins: basicPlugin }); +console.log(resultWithPlugins.name); // => 'fooFn' +console.log(resultWithPlugins.foo); // => 123 +console.log(resultWithPlugins.bar); // => 'barry' +console.log(resultWithPlugins.hasDefaultParams); // => true ``` -### [.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 +## Result - app.define(result, 'foo', 123); +> 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. - return result; - }; -}); +- `name` **{string|null}** - name of the passed function or `null` if anonymous +- `body` **{string}** - actual body of the function, respects trailing newlines and whitespaces +- `args` **{ResultArgs}** - an array of arguments of the function, `result.params` split by `,` +- `params` **{string}** - comma-separated list representing the `args` +- `defaults` **{ResultDefaultParams}** - key/value pairs, useful when use ES2015 default arguments +- `value` **{string}** - string value of what actually has been parsed +- `isValid` **{boolean}** - is the given value valid or not, that's because it never throws! +- `isArrow` **{boolean}** - `true` if the function is arrow function +- `isAsync` **{boolean}** - `true` if function is ES2015 async/await function +- `isNamed` **{boolean}** - `true` if function has name, or `false` if is anonymous +- `isAnonymous` **{boolean}** - `true` if the function don't have name +- `isGenerator` **{boolean}** - `true` if the function is ES2015 generator function +- `isExpression` **{boolean}** - `true` if the value parsed is an expression + +## Types -// fixture function to be parsed -const asyncFn = async (qux) => { - const bar = await Promise.resolve(qux); - return bar; +```ts +import { ParserOptions } from '@babel/parser'; +import { File } from '@babel/types'; + +type FnType = (...args: any) => any; + +export type Input = FnType | string; +export type Plugin = (node: any, result: Result) => Result | undefined; +export type Plugins = Plugin | Array; +export type ResultDefaultParams = { [key: string]: string | undefined }; +export type ResultArgs = string[]; + +export type Options = { + parse?(input: string, options?: ParserOptions): File; + parseExpression?(input: string, options?: ParserOptions): File; + parserOptions?: ParserOptions; + plugins?: Plugins; }; -const result = app.parse(asyncFn); - -console.log(result.name); // => null -console.log(result.foo); // => 123 -console.log(result.args); // => ['qux'] +export type Result = { + name: string | null; + body: string; + args: ResultArgs; + params: string; + defaults: ResultDefaultParams; + value: string; + isValid: boolean; + isArrow: boolean; + isAsync: boolean; + isNamed: boolean; + isAnonymous: boolean; + isGenerator: boolean; + isExpression: boolean; +}; -console.log(result.isAsync); // => true -console.log(result.isArrow); // => true -console.log(result.isNamed); // => false -console.log(result.isAnonymous); // => true +export function parseFunction(code: Input, options?: Options): Result; ``` - - - -## 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 diff --git a/packages/parse-function/README.md b/packages/parse-function/README.md index 68a60032..d42ce415 100644 --- a/packages/parse-function/README.md +++ b/packages/parse-function/README.md @@ -74,11 +74,9 @@ Project is [semantically](https://semver.org) versioned & automatically released - [Real anonymous function](#real-anonymous-function) - [Plugins Architecture](#plugins-architecture) - [API](#api) - - [parseFunction](#parsefunction) - - [.parse](#parse) - - [.use](#use) - - [.define](#define) + - [.parseFunction](#parsefunction) - [Result](#result) +- [Types](#types) - [Contributing](#contributing) - [Guides and Community](#guides-and-community) - [Support the project](#support-the-project) @@ -90,7 +88,7 @@ _(TOC generated by [verb](https://github.com/verbose/verb) using [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 +This project requires [**Node.js**](https://nodejs.org) **>=10.13** _(see [Support & Release Policy](https://github.com/tunnckoCoreLabs/support-release-policy))_. Install it using [**yarn**](https://yarnpkg.com) or [**npm**](https://npmjs.com).
_We highly recommend to use Yarn when you think to contribute to this project._ @@ -167,47 +165,34 @@ Only if you pass really an anonymous function you will get `result.name` equal t > _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. +and no need for **async** plugins here. Plugins MAY or MAY NOT return Result object, so +don't worry if you miss to return something. ```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 +import { parseFunction } from 'parse-function'; + +function someFn(foo, bar) { + return foo + bar; +} + +const plugins = [ + (node, result) => { + return { ...result, zaz: 111 }; + }, + (node, result) => { + return { qux: result.zaz + 202 }; + }, +]; + +const res = parseFunction(someFn, { plugins }); +console.log(res); + +console.log(res.name); // => 'someFn' +console.log(res.zaz); // => 111 +console.log(res.qux); // => 313 ``` -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); - }; -}); -``` +If you want to access the AST of the parser, use the `node` parameter of the plugin. 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 @@ -221,219 +206,155 @@ you can add more properties if you want. _Generated using [jest-runner-docs](https://npmjs.com/package/jest-runner-docs)._ -### [parseFunction](./src/index.js#L52) +### [.parseFunction](./src/index.js#L84) -> 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`. +Parse a given `input` and returns a `Result` object +with useful properties - such as `name`, `body` and `args`. +By default it uses `@babel/parser` parser, but you can switch it by +passing `options.parse` or `options.parseExpression`, for example `options.parse: acorn.parse`. +In the below example will show how to use `acorn` parser, instead +of the default one. **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' +function(input, options) ``` -### [.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 +- `input` - 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 +- `options.parse` - by default `@babel/parser`'s `.parse` or `.parseExpression`, +- `options.parserOptions` - passed to the parser +- `options.plugins` - a plugin function like `function plugin(node: Node, result: Result): Result {}` +- `returns` - result object of the `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, +import { parse as acornParse } from 'acorn'; +import { parse as espreeParse } from 'espree'; +import { parseFunction } from 'parse-function'; + +// or in CommonJS +// const { parseFunction } = require('parse-function'); +// const parseFunction = require('parse-function').parseFunction; +// const fn = require('parse-function'); +// fn.parseFunction() + +function fooFn(bar, baz = 123) { + return bar + baz; +} + +const result1 = parseFunction(fooFn, { parse: acornParse }); +console.log(result1); + +const result2 = parseFunction(fooFn, { + parse: espreeParse, + parserOptions: { + ecmaVersion: 9, + sourceType: 'module', + ecmaFeatures: { jsx: true, globalReturn: true }, + }, }); -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 +console.log('parsed with espree', result2); +// => { +// name: 'fooFn', +// body: '\n return bar + baz;\n', +// args: [ 'bar', 'baz' ], +// params: 'bar, baz', +// defaults: { bar: undefined, baz: '123' }, +// value: '(function fooFn(bar, baz = 123) {\n return bar + baz;\n})', +// isValid: true, +// isArrow: false, +// isAsync: false, +// isNamed: true, +// isAnonymous: false, +// isGenerator: false, +// isExpression: false, +// bobby: 'bobby', +// barry: 'barry barry', +// hasDefaultParams: true +// } + +function basicPlugin(node, result) { + const bar = 'barry'; + const hasDefaultParams = + Object.values(result.defaults).filter(Boolean).length > 0; + + return { ...result, foo: 123, bar, hasDefaultParams }; +} + +const resultWithPlugins = parseFunction(fooFn, { plugins: basicPlugin }); +console.log(resultWithPlugins.name); // => 'fooFn' +console.log(resultWithPlugins.foo); // => 123 +console.log(resultWithPlugins.bar); // => 'barry' +console.log(resultWithPlugins.hasDefaultParams); // => true ``` -### [.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 +## Result - app.define(result, 'foo', 123); +> 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. - return result; - }; -}); +- `name` **{string|null}** - name of the passed function or `null` if anonymous +- `body` **{string}** - actual body of the function, respects trailing newlines and whitespaces +- `args` **{ResultArgs}** - an array of arguments of the function, `result.params` split by `,` +- `params` **{string}** - comma-separated list representing the `args` +- `defaults` **{ResultDefaultParams}** - key/value pairs, useful when use ES2015 default arguments +- `value` **{string}** - string value of what actually has been parsed +- `isValid` **{boolean}** - is the given value valid or not, that's because it never throws! +- `isArrow` **{boolean}** - `true` if the function is arrow function +- `isAsync` **{boolean}** - `true` if function is ES2015 async/await function +- `isNamed` **{boolean}** - `true` if function has name, or `false` if is anonymous +- `isAnonymous` **{boolean}** - `true` if the function don't have name +- `isGenerator` **{boolean}** - `true` if the function is ES2015 generator function +- `isExpression` **{boolean}** - `true` if the value parsed is an expression + +## Types -// fixture function to be parsed -const asyncFn = async (qux) => { - const bar = await Promise.resolve(qux); - return bar; +```ts +import { ParserOptions } from '@babel/parser'; +import { File } from '@babel/types'; + +type FnType = (...args: any) => any; + +export type Input = FnType | string; +export type Plugin = (node: any, result: Result) => Result | undefined; +export type Plugins = Plugin | Array; +export type ResultDefaultParams = { [key: string]: string | undefined }; +export type ResultArgs = string[]; + +export type Options = { + parse?(input: string, options?: ParserOptions): File; + parseExpression?(input: string, options?: ParserOptions): File; + parserOptions?: ParserOptions; + plugins?: Plugins; }; -const result = app.parse(asyncFn); - -console.log(result.name); // => null -console.log(result.foo); // => 123 -console.log(result.args); // => ['qux'] +export type Result = { + name: string | null; + body: string; + args: ResultArgs; + params: string; + defaults: ResultDefaultParams; + value: string; + isValid: boolean; + isArrow: boolean; + isAsync: boolean; + isNamed: boolean; + isAnonymous: boolean; + isGenerator: boolean; + isExpression: boolean; +}; -console.log(result.isAsync); // => true -console.log(result.isArrow); // => true -console.log(result.isNamed); // => false -console.log(result.isAnonymous); // => true +export function parseFunction(code: Input, options?: Options): Result; ``` - - -## 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 @@ -497,7 +418,7 @@ Released under the [MPL-2.0 License][license-url]. [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/badge/node/>=8.11/green +[nodejs-img]: https://badgen.net/badge/node/>=10.13/green