├── .gitignore
├── LICENSE
├── README.md
├── eval.d.ts
├── eval.js
├── package.json
└── test.js
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules/
2 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Copyright (c) 2012 Pierre Curto
2 |
3 | Permission is hereby granted, free of charge, to any person obtaining a copy
4 | of this software and associated documentation files (the "Software"), to deal
5 | in the Software without restriction, including without limitation the rights
6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7 | copies of the Software, and to permit persons to whom the Software is
8 | furnished to do so, subject to the following conditions:
9 |
10 | The above copyright notice and this permission notice shall be included in
11 | all copies or substantial portions of the Software.
12 |
13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19 | THE SOFTWARE.
20 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Eval - require() for module content!
2 |
3 | ## Overview
4 |
5 | This module is a simple way to evaluate a module content in the same way as require() but without loading it from a file. Effectively, it mimicks the javascript evil `eval` function but leverages Node's VM module instead.
6 |
7 |
8 | ## Benefits
9 |
10 | Why would you be using the `eval` module over the native`require`? Most of the time `require` is fine but in some situations, I have found myself wishing for the following:
11 |
12 | * Ability to supply a context to a module
13 | * Ability to load the module file(s) from non node standard places
14 |
15 | Or simply to leverage JavaScript's `eval` but with sandboxing.
16 |
17 |
18 | ## Download
19 |
20 | It is published on node package manager (npm). To install, do:
21 |
22 | npm install eval
23 |
24 |
25 | ## Usage
26 |
27 | ```` javascript
28 | var _eval = require('eval')
29 | var res = _eval(content /*, filename, scope, includeGlobals */)
30 | ````
31 |
32 | The following options are available:
33 |
34 | * `content` (__String__): the content to be evaluated
35 | * `filename` (__String__): optional dummy name to be given (used in stacktraces)
36 | * `scope` (__Object__): scope properties are provided as variables to the content
37 | * `includeGlobals` (__Boolean__): allow/disallow global variables (and require) to be supplied to the content (default=false)
38 |
39 |
40 | ## Examples
41 |
42 | ```` javascript
43 | var _eval = require('eval')
44 | var res = _eval('var x = 123; exports.x = x')
45 | // => res === { x: 123 }
46 |
47 | res = _eval('module.exports = function () { return 123 }')
48 | // => res() === 123
49 |
50 | res = _eval('module.exports = require("events")', true)
51 | // => res === require('events')
52 |
53 | res = _eval('exports.x = process', true)
54 | // => res.x === process
55 | ````
56 |
57 |
58 | ## License
59 |
60 | [Here](https://github.com/pierrec/node-eval/tree/master/LICENSE)
61 |
--------------------------------------------------------------------------------
/eval.d.ts:
--------------------------------------------------------------------------------
1 | ///
2 |
3 | import { Script } from "vm";
4 |
5 | /**
6 | * A simple way to evaluate a module content in the same way as require() but
7 | * without loading it from a file. Effectively, it mimicks the javascript evil
8 | * `eval` function but leverages Node's VM module instead.
9 | */
10 | declare const nodeEval: {
11 | (
12 | /** The content to be evaluated. */
13 | content: string | Buffer | Script,
14 | ): unknown;
15 | (
16 | /** The content to be evaluated. */
17 | content: string | Buffer | Script,
18 | /**
19 | * Optional flag to allow/disallow global variables (and require) to be
20 | * supplied to the content (default=false).
21 | */
22 | includeGlobals?: boolean,
23 | ): unknown;
24 | (
25 | /** The content to be evaluated. */
26 | content: string | Buffer | Script,
27 | /** Optional scope properties are provided as variables to the content. */
28 | scope?: Record,
29 | /**
30 | * Optional flag to allow/disallow global variables (and require) to be
31 | * supplied to the content (default=false).
32 | */
33 | includeGlobals?: boolean,
34 | ): unknown;
35 | (
36 | /** The content to be evaluated. */
37 | content: string | Buffer | Script,
38 | /** Optional dummy name to be given (used in stacktraces). */
39 | filename?: string,
40 | /** Optional scope properties are provided as variables to the content. */
41 | scope?: Record,
42 | /**
43 | * Optional flag to allow/disallow global variables (and require) to be
44 | * supplied to the content (default=false).
45 | */
46 | includeGlobals?: boolean,
47 | ): unknown;
48 | };
49 |
50 | export = nodeEval;
51 |
--------------------------------------------------------------------------------
/eval.js:
--------------------------------------------------------------------------------
1 | var vm = require('vm')
2 | var isBuffer = Buffer.isBuffer
3 |
4 | var requireLike = require('require-like')
5 |
6 | function merge (a, b) {
7 | if (!a || !b) return a
8 | var keys = Object.keys(b)
9 | for (var k, i = 0, n = keys.length; i < n; i++) {
10 | k = keys[i]
11 | a[k] = b[k]
12 | }
13 | return a
14 | }
15 |
16 | var vmGlobals = new vm.Script('Object.getOwnPropertyNames(globalThis)')
17 | .runInNewContext()
18 |
19 | // Return the exports/module.exports variable set in the content
20 | // content (String|VmScript): required
21 | module.exports = function (content, filename, scope, includeGlobals) {
22 |
23 | if (typeof filename !== 'string') {
24 | if (typeof filename === 'object') {
25 | includeGlobals = scope
26 | scope = filename
27 | filename = ''
28 | } else if (typeof filename === 'boolean') {
29 | includeGlobals = filename
30 | scope = {}
31 | filename = ''
32 | }
33 | }
34 |
35 | // Expose standard Node globals
36 | var sandbox = {}
37 | var exports = {}
38 | var _filename = filename || module.parent.filename;
39 |
40 | if (includeGlobals) {
41 | // Merge enumerable variables on `global`
42 | merge(sandbox, global)
43 | // Merge all non-enumerable variables on `global`, including console (v10+),
44 | // process (v12+), URL, etc. We first filter out anything that's already in
45 | // the VM scope (i.e. those in the ES standard) so we don't have two copies
46 | Object.getOwnPropertyNames(global).forEach((name) => {
47 | if (!vmGlobals.includes(name)) {
48 | sandbox[name] = global[name]
49 | }
50 | })
51 | // `console` exists in VM scope, but we want to pipe the output to the
52 | // process'
53 | sandbox.console = console
54 | sandbox.require = requireLike(_filename)
55 | }
56 |
57 | if (typeof scope === 'object') {
58 | merge(sandbox, scope)
59 | }
60 |
61 | sandbox.exports = exports
62 | sandbox.module = {
63 | exports: exports,
64 | filename: _filename,
65 | id: _filename,
66 | parent: module.parent,
67 | require: sandbox.require || requireLike(_filename)
68 | }
69 | sandbox.global = sandbox
70 |
71 | var options = {
72 | filename: filename,
73 | displayErrors: false
74 | }
75 |
76 | if (isBuffer(content)) {
77 | content = content.toString()
78 | }
79 |
80 | // Evalutate the content with the given scope
81 | if (typeof content === 'string') {
82 | var stringScript = content.replace(/^\#\!.*/, '')
83 | var script = new vm.Script(stringScript, options)
84 | script.runInNewContext(sandbox, options)
85 | } else {
86 | content.runInNewContext(sandbox, options)
87 | }
88 |
89 | return sandbox.module.exports
90 | }
91 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "author": "Pierre Curto"
3 | , "name": "eval"
4 | , "description": "Evaluate node require() module content directly"
5 | , "keywords": ["require","eval","vm","module"]
6 | , "version": "0.1.8"
7 | , "homepage": "http://github.com/pierrec/node-eval"
8 | , "repository": {
9 | "type": "git"
10 | , "url": "git://github.com/pierrec/node-eval.git"
11 | }
12 | , "main": "eval.js"
13 | , "types": "eval.d.ts"
14 | , "bugs": { "url" : "http://github.com/pierrec/node-eval/issues" }
15 | , "license": "MIT"
16 | , "engines": {
17 | "node": ">= 0.8"
18 | }
19 | , "dependencies": {
20 | "@types/node": "*"
21 | , "require-like": ">= 0.1.1"
22 | }
23 | , "devDependencies": {
24 | }
25 | , "scripts": {
26 | "test": "node test.js"
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/test.js:
--------------------------------------------------------------------------------
1 | var assert = require('assert')
2 |
3 | var _eval = require('./eval')
4 | var res
5 |
6 | res = _eval('var x = 123; exports.x = x')
7 | assert.deepEqual( res, { x: 123 } )
8 |
9 | res = _eval('module.exports = function () { return 123 }')
10 | assert.deepEqual( res(), 123 )
11 |
12 | res = _eval('module.exports = require("events")', true)
13 | assert.deepEqual( res, require('events') )
14 |
15 | res = _eval('exports.x = process', true)
16 | assert.deepEqual( res.x, process )
17 |
18 | // Lock down scope by default
19 | global.add = function (a, b) { return a + b }
20 | res = _eval('exports.add = function (x, y) { return add(x, y) }')
21 | assert.throws(function () {
22 | res.add(5, 6)
23 | })
24 |
25 | // Do not expose require by default
26 | assert.throws(function () {
27 | _eval('require("fs")')
28 | })
29 |
30 | // Verify that the console is available when globals are passed
31 | res = _eval('exports.x = console', true)
32 | assert.deepEqual(res.x, console)
33 |
34 | console.log('All tests passed')
35 |
--------------------------------------------------------------------------------