https://github.com/thoughtspile/scoped-function
ScopedFunction = new Function + scope
https://github.com/thoughtspile/scoped-function
compiler dsl function javascript metaprogramming utility
Last synced: about 1 year ago
JSON representation
ScopedFunction = new Function + scope
- Host: GitHub
- URL: https://github.com/thoughtspile/scoped-function
- Owner: thoughtspile
- License: mit
- Created: 2018-06-12T11:06:09.000Z (about 8 years ago)
- Default Branch: master
- Last Pushed: 2018-06-12T11:46:24.000Z (about 8 years ago)
- Last Synced: 2025-03-27T03:41:30.359Z (about 1 year ago)
- Topics: compiler, dsl, function, javascript, metaprogramming, utility
- Language: JavaScript
- Size: 10.7 KB
- Stars: 9
- Watchers: 2
- Forks: 1
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- License: LICENSE
Awesome Lists containing this project
README
# ScopedFunction = new Function + scope
`ScopedFunction` allows you to inject scope object into `Function` constructor.
The properties of the scope object can be accesed in the function body as if they
were closure variables: `ScopedFunction('return s;', { s: 'hello' }) -> 'hello'`.
Build paper-thin DSLs over JavaScript syntax — let your users write math in
standard syntax, `exp(10 * cos(x))`, without prepending the nasty `Math`
builtin, or pass libraries to in-browser JS playgrounds.
There's no runtime performance penalty for functions compiled using `ScopedFunction`.
This library is a great foundation for safer and faster `eval` or `with`, the
infamous optimization busters. `ScopedFunction` is tiny: <40 SLOC, or around
500 bytes minified.
## Usage
```js
// Use whichever you like
const ScopedFunction = require('ScopedFunction');
import ScopedFunction from 'scoped-function';
// You can also drop lib/scoped-function.js into your HTML if you feel like it
// Build your smallest DSL ever:
const trig = ScopedFunction('x', 'return sin(pi * x) + cos(pi * x)', {
sin: Math.sin,
cos: Math.cos,
pi: Math.PI
});
const v = trig(1); // = -1
// Add slight rewriting:
const compileMath = e => ScopedFunction('x', `return (${e});`, Math);
compileMath('atan(exp(x) - 1)')(0); // = 0
// Inject libraries into user code
// Say you're building a JS course with live problems:
const userSolution = `
function ageMode(data) {
if (_.isEmpty(data)) return undefined;
return _.maxBy(
_.values(_.groupBy(data, 'age')),
v => v.length
)[0].age;
}
`;
const _ = require('lodash');
// Note return + call: we can't parse the user-supplied function, but still inject the "_"
const userFn = ScopedFunction(`return (${userSolution});`, { _ })();
const isValid = userFn([{ age: 10 }, { age: 20 }, { age: 10 }]) === 10;
// You can also use ScopedFunction as a constructor:
const f = new ScopedFunction('x', 'return _.min(x)', { _ });
```
## Standards Compliance and Usage Notes
`ScopedFunction` shallow-clones the scope object — you can't add, remove, or change
the variables afterwards. However, the objects (as in `{ hub: {} }`) are shared
by reference. Use this to communicate between the functions, or opt out with
a `_.cloneDeep(...)`.
The functions produced mimic the ones that come from `Function(...)`:
- respond to `typeof` / `instanceof`
- have proper `call` / `bind` / `apply`
- accessing `arguments` still works
- name is set to `anonymous` as per the spec.
If you find a deviation from the [spec](https://www.ecma-international.org/ecma-262/6.0/#sec-function-constructor), drop an issue.
Both `Function` and `ScopedFunction` accept non-simple ES6 formal
parameters: `...rest`, `{ destructuring }`, and `default = true`. Use these in
the browser, but remember that your code is compiled as-is, not transpiled,
and would break in browsers with poor ES6 support, like Safari.
Also note that this library does not give you a secure sanbox for untrusted code
— use [vm2](https://github.com/patriksimek/vm2) for that.
## Installation
```sh
$ npm i --save scoped-function
$ yarn add scoped-function
```
Made by [Vladimir Klepov](https://github.com/thoughtspile).
## License
[MIT](LICENSE)