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