├── .gitignore ├── .jshintrc ├── test ├── cli │ ├── argv.js │ └── check.js ├── index.js └── cli.js ├── .travis.yml ├── package.json ├── LICENSE ├── History.md ├── index.js ├── README.md ├── bin └── gnode └── fallback.js /.gitignore: -------------------------------------------------------------------------------- 1 | /node_modules 2 | /?.js 3 | -------------------------------------------------------------------------------- /.jshintrc: -------------------------------------------------------------------------------- 1 | { 2 | "evil": true 3 | } 4 | -------------------------------------------------------------------------------- /test/cli/argv.js: -------------------------------------------------------------------------------- 1 | console.log('%j', process.argv); 2 | -------------------------------------------------------------------------------- /test/cli/check.js: -------------------------------------------------------------------------------- 1 | 2 | var assert = require('assert'); 3 | 4 | // assert up here to ensure that hoisting works as expected 5 | assert('gen' == gen.name); 6 | assert('GeneratorFunction' == gen.constructor.name); 7 | 8 | function *gen () {} 9 | 10 | var g = gen(); 11 | assert('function' == typeof g.next); 12 | assert('function' == typeof g.throw); 13 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | sudo: false 2 | 3 | language: node_js 4 | 5 | node_js: 6 | - "0.8" 7 | - "0.10" 8 | - "0.12" 9 | - "1" 10 | - "2" 11 | - "3" 12 | - "4" 13 | - "5" 14 | 15 | install: 16 | - PATH="`npm bin`:`npm bin -g`:$PATH" 17 | # Node 0.8 comes with a too obsolete npm 18 | - if [[ "`node --version`" =~ ^v0\.8\. ]]; then npm install -g npm@1.4.28 ; fi 19 | # Install dependencies and build 20 | - npm install 21 | 22 | script: 23 | # Output useful info for debugging 24 | - node --version 25 | - npm --version 26 | # Run tests 27 | - npm test 28 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "gnode", 3 | "version": "0.1.2", 4 | "description": "Run node with ES6 Generators, today!", 5 | "main": "index.js", 6 | "bin": "bin/gnode", 7 | "scripts": { 8 | "test": "node test" 9 | }, 10 | "repository": { 11 | "type": "git", 12 | "url": "git://github.com/TooTallNate/gnode.git" 13 | }, 14 | "keywords": [ 15 | "node", 16 | "generators", 17 | "es6", 18 | "facebook", 19 | "regenerator", 20 | "co" 21 | ], 22 | "author": "Nathan Rajlich (http://n8.io/)", 23 | "license": "MIT", 24 | "bugs": { 25 | "url": "https://github.com/TooTallNate/gnode/issues" 26 | }, 27 | "dependencies": { 28 | "regenerator": "~0.8.8" 29 | }, 30 | "devDependencies": { 31 | "co": "~2.1.0", 32 | "mocha": "~2.4.5", 33 | "semver": "~2.2.1", 34 | "suspend": "~0.3.0" 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /test/index.js: -------------------------------------------------------------------------------- 1 | 2 | /** 3 | * Module dependencies. 4 | */ 5 | 6 | var path = require('path'); 7 | var spawn = require('child_process').spawn; 8 | 9 | // node executable 10 | var node = process.execPath || process.argv[0]; 11 | var mocha = path.resolve(__dirname, '..', 'node_modules', 'mocha', 'bin', 'mocha'); 12 | var mochaOpts = [ '--reporter', 'spec' ]; 13 | 14 | run([ 15 | [ mocha, path.resolve(__dirname, 'cli.js') ] 16 | ]); 17 | 18 | function run (tests) { 19 | if (0 == tests.length) return; 20 | var argv = tests.shift(); 21 | if (argv.indexOf(mocha) != -1) { 22 | // running mocha, append "mochaOpts" 23 | argv.push.apply(argv, mochaOpts); 24 | } 25 | var opts = { 26 | customFds: [ 0, 1, 2 ], 27 | stdio: 'inherit' 28 | }; 29 | var child = spawn(node, argv, opts); 30 | child.on('exit', function (code) { 31 | if (0 == code) { 32 | run(tests); 33 | } else { 34 | process.exit(code); 35 | } 36 | }); 37 | } 38 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | (The MIT License) 2 | 3 | Copyright (c) 2013 Nathan Rajlich 4 | 5 | Permission is hereby granted, free of charge, to any person 6 | obtaining a copy of this software and associated documentation 7 | files (the "Software"), to deal in the Software without 8 | restriction, including without limitation the rights to use, 9 | copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the 11 | Software is furnished to do so, subject to the following 12 | conditions: 13 | 14 | The above copyright notice and this permission notice shall be 15 | included in all copies or substantial portions of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 18 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 19 | OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 20 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 21 | HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 22 | WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 23 | FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 24 | OTHER DEALINGS IN THE SOFTWARE. 25 | -------------------------------------------------------------------------------- /History.md: -------------------------------------------------------------------------------- 1 | 2 | 0.1.2 / 2016-02-08 3 | ================== 4 | 5 | * travis: test lots more node versions 6 | * README: use SVG for Travis-CI badge 7 | * test: fix `npm test` failing to run (#21, @etiktin) 8 | * test: fix typo in test case name 9 | 10 | 0.1.1 / 2015-01-14 11 | ================== 12 | 13 | * package: upgrade regenerator to v0.8.8 (#18, @popomore) 14 | * travis: use "strings" for version numbers 15 | 16 | 0.1.0 / 2014-09-12 17 | ================== 18 | 19 | * package: update "regenerator" to v0.6.3 (@benjamn, #13) 20 | 21 | 0.0.8 / 2014-04-28 22 | ================== 23 | 24 | * bin: fix repl erroring on valid code (@timoxley, #10) 25 | * bin: make gnode -p &/or gnode -e work properly (@timoxley, #2, #9) 26 | 27 | 0.0.7 / 2014-02-14 28 | ================== 29 | 30 | * package: allow any v0.x.x of "regenerator" 31 | 32 | 0.0.6 / 2014-01-06 33 | ================== 34 | 35 | * Never compile code that is already valid (#6, @ForbesLindesay) 36 | * package: update "regenerator" to v0.3.4 37 | 38 | 0.0.5 / 2013-12-19 39 | ================== 40 | 41 | * Upgrade regenerator dependency to v0.3.2 (@benjamn) 42 | * README++ 43 | * fallback: make `process.argv` only have 1 entry in the repl 44 | * index: don't add the global require .js hook in some cases 45 | * index: match the strip shebang logic from `lib/module.js` 46 | 47 | 0.0.4 / 2013-11-11 48 | ================== 49 | 50 | * gnode: fix entering the REPL with other flags are passed in as well 51 | * gnode: print out the args for debugging purposes with GNODE_PRINT_ARGS 52 | * gnode: proxy the exit code from the child node process 53 | * test: initial CLI tests 54 | * index: strip away the hashbang before feeding the script to esprima 55 | 56 | 0.0.3 / 2013-10-31 57 | ================== 58 | 59 | * gnode: avoid adding the --harmony_generators flag when not supported 60 | * fallback: set `process.execPath` and `process.argv[0]` to the gnode binary 61 | * fallback, index: include the regenerator runtime immediately 62 | 63 | 0.0.2 / 2013-10-30 64 | ================== 65 | 66 | * fallback: add proper REPL support 67 | * fallback: add stdin piping support 68 | 69 | 0.0.1 / 2013-10-30 70 | ================== 71 | 72 | * gitignore: ignore ?.js files 73 | * gnode: add support for flags on gnode 74 | * gnode: move the --harmony_generators flag out into a variable 75 | * gnode: fall back to process.argv[0] for the node process 76 | * README++ 77 | * add .gitignore 78 | * fallback: throw an error for REPL / stdin mode for now 79 | * gnode: only set GNODE_ENTRY_POINT if a 3rd argument was passed in 80 | * gnode: mark as executable 81 | * gnode: add comment 82 | * fallback: clean up the GNODE_ENTRY_POINT env variable 83 | * gnode: drop the "flag" stuff for now 84 | * initial commit 85 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | 2 | /** 3 | * Only apply the global .js require() hook if: 4 | * 5 | * 1) ES6 Generators are not already supported in the environment 6 | * 2) The require.extensions['.js'] hook is not already patched by gnode 7 | */ 8 | 9 | if (!hasNativeGenerators() && !isPatchedByGnode()) { 10 | 11 | /** 12 | * Module dependencies. 13 | */ 14 | 15 | var fs = require('fs'); 16 | var regenerator = require('regenerator'); 17 | var genFunExp = /\bfunction\s*\*/; 18 | 19 | /** 20 | * First include the regenerator runtime. It gets installed gloablly as 21 | * `regeneratorRuntime`, so we just need to make sure that global 22 | * function is available. 23 | */ 24 | 25 | regenerator.runtime(); 26 | 27 | /** 28 | * Entry point for node versions that don't have Generator support. 29 | * 30 | * This file replaces the default `.js` require.extensions implementation with 31 | * one that first compiles the JavaScript code via "facebook/regenerator". 32 | * 33 | * Once that is in place then it loads the original entry point .js file. 34 | */ 35 | 36 | require.extensions['.js'] = gnodeJsExtensionCompiler; 37 | } 38 | 39 | /** 40 | * ES6 Generators enabled `require.extensions['.js']` hook. 41 | * 42 | * @api public 43 | */ 44 | 45 | function gnodeJsExtensionCompiler (module, filename) { 46 | var content = fs.readFileSync(filename, 'utf8'); 47 | 48 | // remove the Byte Order Marker if present 49 | content = stripBOM(content); 50 | 51 | // strip away the shebang if present 52 | content = stripShebang(content); 53 | 54 | if (genFunExp.test(content) && !isValid(content)) { 55 | // compile JS via facebook/regenerator 56 | content = regenerator.compile(content, { 57 | includeRuntime: 'object' !== typeof regeneratorRuntime 58 | }).code; 59 | } 60 | 61 | module._compile(content, filename); 62 | } 63 | 64 | function isValid(content) { 65 | try { 66 | Function('', content); 67 | return true; 68 | } catch (ex) { 69 | return false; 70 | } 71 | } 72 | 73 | /** 74 | * Remove byte order marker. This catches EF BB BF (the UTF-8 BOM) 75 | * because the buffer-to-string conversion in `fs.readFileSync()` 76 | * translates it to FEFF, the UTF-16 BOM. 77 | * 78 | * Copied directly from joyent/node's lib/module.js 79 | * 80 | * @api private 81 | */ 82 | 83 | function stripBOM (content) { 84 | if (content.charCodeAt(0) === 0xFEFF) { 85 | content = content.slice(1); 86 | } 87 | return content; 88 | } 89 | 90 | /** 91 | * Strips away the "shebang" from the source file if present. 92 | * 93 | * Copied directly from joyent/node's lib/module.js 94 | * 95 | * @api private 96 | */ 97 | 98 | function stripShebang (content) { 99 | return content.replace(/^\#\!.*/, ''); 100 | } 101 | 102 | /** 103 | * Tests if the environment supports ES6 Generators. 104 | * 105 | * @api private 106 | */ 107 | 108 | function hasNativeGenerators () { 109 | var has = false; 110 | try { 111 | eval('(function*(){})'); 112 | has = true; 113 | } catch (e) { 114 | } 115 | return has; 116 | } 117 | 118 | /** 119 | * Check if require.extensions['.js'] is already patched by gnode 120 | * 121 | * @api private 122 | */ 123 | 124 | function isPatchedByGnode () { 125 | return 'gnodeJsExtensionCompiler' == require.extensions['.js'].name; 126 | } 127 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | gnode 2 | ===== 3 | ### Run node with ES6 Generators, today! 4 | [![Build Status](https://travis-ci.org/TooTallNate/gnode.svg?branch=master)](https://travis-ci.org/TooTallNate/gnode) 5 | 6 | `gnode` is a very light wrapper around your `node` executable that ensures 7 | [ES6 Generator][generators] support, even on versions of node that do not 8 | support ES6 Generators natively. 9 | 10 | You use it exactly like the regular `node` executable, except that you _do not_ 11 | need to pass the `--harmony-generators` flag. That is where the magic happens. 12 | 13 | With `gnode` you can use [`co`][co] or [`suspend`][suspend], or any other 14 | Generator-based flow control based module, today! 15 | 16 | 17 | How does this magic work? 18 | ------------------------- 19 | 20 | #### node < 0.11.3 21 | 22 | When V8 provides no native ES6 generators support, then `gnode` invokes a node 23 | instance with a patched `require.extensions['.js']` function, which transparently 24 | transpiles your ES6 code with Generators into ES5-compatible code. We can thank 25 | [`facebook/regenerator`][regenerator] for making this possible. 26 | 27 | Under the hood, this command: 28 | 29 | ``` bash 30 | $ gnode foo.js all the args 31 | ``` 32 | 33 | Turns into something like this: 34 | 35 | ``` bash 36 | $ GNODE_ENTRY_POINT=foo.js node fallback.js all the args 37 | ``` 38 | 39 | #### node >= 0.11.3 40 | 41 | When V8 supports ES6 generators natively, then `gnode` invokes a node instance 42 | with the `--harmony-generators` flag passed in transparently, so that the native 43 | generators are used, and no transpiling takes place. Everything else _just works_ 44 | as you would expect it to. 45 | 46 | Under the hood, this command: 47 | 48 | ``` bash 49 | $ gnode foo.js all the args 50 | ``` 51 | 52 | Turns into something like this: 53 | 54 | ``` bash 55 | $ node --harmony-generators foo.js all the args 56 | ``` 57 | 58 | 59 | Installation 60 | ------------ 61 | 62 | Install the `gnode` executable via npm: 63 | 64 | ``` bash 65 | $ npm install -g gnode 66 | ``` 67 | 68 | 69 | CLI Examples 70 | ------------ 71 | 72 | The `gnode` executable uses whatever version of node is installed in your `PATH`: 73 | 74 | Here's our example `t.js` file: 75 | 76 | ``` js 77 | var co = require('co'); 78 | 79 | function sleep (ms) { 80 | return function (fn) { 81 | setTimeout(fn, ms); 82 | }; 83 | } 84 | 85 | co(function* () { 86 | for (var i = 0; i < 5; i++) { 87 | console.log(i); 88 | yield sleep(1000); 89 | } 90 | })(); 91 | ``` 92 | 93 | This script with an ES6 Generator in it can be run using any version of node 94 | by using `gnode`: 95 | 96 | ``` bash 97 | ☮ ~ (master) ∴ n 0.8.26 98 | 99 | ☮ ~ (master) ∴ gnode -v 100 | v0.8.26 101 | 102 | ☮ ~ (master) ∴ gnode t.js 103 | 0 104 | 1 105 | 2 106 | 3 107 | 4 108 | 109 | ☮ ~ (master) ∴ n 0.10.21 110 | 111 | ☮ ~ (master) ∴ gnode -v 112 | v0.10.21 113 | 114 | ☮ ~ (master) ∴ gnode t.js 115 | 0 116 | 1 117 | 2 118 | 3 119 | 4 120 | 121 | ☮ ~ (master) ∴ n 0.11.8 122 | 123 | ☮ ~ (master) ∴ gnode -v 124 | v0.11.8 125 | 126 | ☮ ~ (master) ∴ gnode t.js 127 | 0 128 | 1 129 | 2 130 | 3 131 | 4 132 | ``` 133 | 134 | 135 | Programmatic API 136 | ---------------- 137 | 138 | You can also just `require('gnode')` in a script _without any generators_, and 139 | then `require()` any other .js file that has generators after that. 140 | 141 | ``` js 142 | require('gnode'); 143 | var gen = require('./someGenerator'); 144 | // etc… 145 | ``` 146 | 147 | [co]: https://github.com/visionmedia/co 148 | [suspend]: https://github.com/jmar777/suspend 149 | [generators]: http://wiki.ecmascript.org/doku.php?id=harmony:generators 150 | [regenerator]: https://github.com/facebook/regenerator 151 | -------------------------------------------------------------------------------- /bin/gnode: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | var spawn = require('child_process').spawn; 4 | var node = process.execPath || process.argv[0]; 5 | var HG = '--harmony_generators'; 6 | 7 | var regenerator = require('regenerator') 8 | 9 | // first check if the current node executable has "Generators". 10 | // if not, fall back to facebook/regenerator for Generators support. 11 | hasGenerators(function (err, has) { 12 | if (err) throw err; 13 | 14 | // build up the node(1) arguments for Generator support 15 | var argv = (process.execArgv || []).slice(0); 16 | 17 | // process the regular arguments. if any of them begin with a hyphen (-) then 18 | // it's a switch and should be added *before* the ENTRY_POINT in the argv... 19 | var argsStart = 2; 20 | for (var i = argsStart; i < process.argv.length; i++) { 21 | var arg = process.argv[i]; 22 | var isFlag = '-' == arg[0]; 23 | if (isFlag) { 24 | if (!/^--harmony[_-]generators$/.test(arg)) { 25 | argv.push(arg); 26 | } 27 | 28 | argsStart++; 29 | 30 | if (/^-[ep]$/.test(arg)) { 31 | // handle gnode -e|-p "console.log('script')" 32 | var code = process.argv[i + 1] 33 | if (!has) { 34 | code = regenerator.compile(code, { 35 | includeRuntime: true 36 | }).code; 37 | } 38 | argv.push(code) 39 | // skip arg, we just processed it 40 | i++ 41 | } 42 | } else { 43 | // must be the script file 44 | break; 45 | } 46 | } 47 | 48 | if (has && -1 == argv.indexOf(HG) && -1 == argv.indexOf('--harmony-generators')) { 49 | // if the node process natively supports Generators, 50 | // then add the --harmony_generators flag 51 | argv.push(HG); 52 | } 53 | 54 | if (!has) { 55 | // no Generator support, then we need to load fallback.js first 56 | argv.push(require('path').resolve(__dirname, '..', 'fallback.js')); 57 | 58 | // store a reference to the entry point, so that fallback.js can pick it up 59 | if (process.argv.length > argsStart) { 60 | process.env.GNODE_ENTRY_POINT = process.argv[argsStart]; 61 | } 62 | 63 | argsStart++; 64 | } 65 | 66 | // add the rest of the arguments specified by the user 67 | if (process.argv.length >= argsStart) { 68 | argv.push.apply(argv, process.argv.slice(argsStart)); 69 | } 70 | 71 | if (parseInt(process.env.GNODE_PRINT_ARGS, 10)) { 72 | // print out the arguments that will be passed in for debugging purposes 73 | console.error(argv); 74 | } 75 | 76 | var opts = { 77 | customFds: [ 0, 1, 2 ], 78 | stdio: 'inherit' 79 | }; 80 | 81 | // spawn a new node process with the necessary flags 82 | var child = spawn(node, argv, opts); 83 | child.on('exit', onexit); 84 | }); 85 | 86 | /** 87 | * Main node subprocess "exit" event handler. 88 | * Proxies the exit code up to this process. 89 | * 90 | * @api private 91 | */ 92 | 93 | function onexit(code, signal) { 94 | if (signal) { 95 | process.kill(process.pid, signal); 96 | } else if (0 !== code) { 97 | if (null == process.exitCode) { 98 | // old node, no `process.exitCode` support... 99 | process.exit(code); 100 | } else { 101 | // newer node, we can set `process.exitCode` 102 | // and let the process exit naturally 103 | process.exitCode = code; 104 | } 105 | } 106 | } 107 | 108 | /** 109 | * Async function that returns `true`/`false` for if Generators are 110 | * supported by V8. 111 | * 112 | * @api private 113 | */ 114 | 115 | function hasGenerators (fn) { 116 | var cp = spawn(node, [ '--v8-options' ]); 117 | var data = ''; 118 | 119 | function onerror (e) { 120 | fn(e); 121 | } 122 | function ondata (b) { 123 | data += b; 124 | } 125 | function onend () { 126 | cp.removeListener('error', onerror); 127 | var has = -1 != data.indexOf(HG); 128 | fn(null, has); 129 | } 130 | 131 | cp.on('error', onerror); 132 | cp.stdout.setEncoding('utf8'); 133 | cp.stdout.on('data', ondata); 134 | cp.stdout.on('end', onend); 135 | } 136 | -------------------------------------------------------------------------------- /fallback.js: -------------------------------------------------------------------------------- 1 | 2 | /** 3 | * A lot of this entry file is based off of node's src/node.js. 4 | */ 5 | 6 | var regenerator = require('regenerator'); 7 | 8 | // grab a reference to the entry point script, then clean up the env 9 | var entryPoint = process.env.GNODE_ENTRY_POINT; 10 | delete process.env.GNODE_ENTRY_POINT; 11 | 12 | // load the custom `.js` ES6 Generator compiler 13 | require('./index.js'); 14 | 15 | // overwrite process.execPath and process.argv[0] with the 16 | // absolute path to the gnode binary 17 | process.execPath = process.argv[0] = require('path').resolve(__dirname, 'bin', 'gnode'); 18 | 19 | // remove "fallback.js" from `process.argv` 20 | process.argv.splice(1, 1); 21 | 22 | if (process._eval != null) { 23 | // User passed '-e' or '--eval' arguments to Node. 24 | evalScript('[eval]'); 25 | } else if (entryPoint) { 26 | // replace `process.argv[1]` with the expected path value, 27 | // and re-run Module.runMain() 28 | process.argv.splice(1, 0, require('path').resolve(entryPoint)); 29 | require('module').runMain(); 30 | } else { 31 | // run the REPL, or run from stdin 32 | 33 | // If -i or --interactive were passed, or stdin is a TTY. 34 | if (process._forceRepl || require('tty').isatty(0)) { 35 | // REPL 36 | var opts = { 37 | 'eval': gnodeEval, 38 | useGlobal: true, 39 | ignoreUndefined: false 40 | }; 41 | if (parseInt(process.env.NODE_NO_READLINE, 10)) { 42 | opts.terminal = false; 43 | } 44 | if (parseInt(process.env.NODE_DISABLE_COLORS, 10)) { 45 | opts.useColors = false; 46 | } 47 | var repl = require('module').requireRepl().start(opts); 48 | repl.on('exit', function() { 49 | process.exit(); 50 | }); 51 | 52 | } else { 53 | // Read all of stdin - execute it. 54 | process.stdin.resume(); 55 | process.stdin.setEncoding('utf8'); 56 | 57 | var code = ''; 58 | process.stdin.on('data', function(d) { 59 | code += d; 60 | }); 61 | 62 | process.stdin.on('end', function() { 63 | process._eval = code; 64 | evalScript('[stdin]'); 65 | }); 66 | } 67 | } 68 | 69 | // custom eval() function for the REPL, that first compiles 70 | function gnodeEval (code, context, file, fn) { 71 | var err, result; 72 | try { 73 | // compile JS via facebook/regenerator 74 | code = regenerator.compile(code, { 75 | includeRuntime: 'object' !== typeof regeneratorRuntime 76 | }).code; 77 | } catch (e) { 78 | // Treat regenerator errors as syntax errors in repl. 79 | // A hack to have repl interpret certain js structures correctly. 80 | e.name = 'SyntaxError' 81 | err = e; 82 | } 83 | try { 84 | if (this.useGlobal) { 85 | result = vm.runInThisContext(code, file); 86 | } else { 87 | result = vm.runInContext(code, context, file); 88 | } 89 | } catch (e) { 90 | err = e; 91 | } 92 | 93 | fn(err, result); 94 | } 95 | 96 | // copied (almost) directly from joyent/node's src/node.js 97 | function evalScript (name) { 98 | var Module = require('module'); 99 | var path = require('path'); 100 | var cwd = process.cwd(); 101 | 102 | var module = new Module(name); 103 | module.filename = path.join(cwd, name); 104 | module.paths = Module._nodeModulePaths(cwd); 105 | var script = process._eval; 106 | 107 | // compile JS via facebook/regenerator 108 | script = regenerator.compile(script, { 109 | includeRuntime: 'object' !== typeof regeneratorRuntime 110 | }).code; 111 | 112 | if (!Module._contextLoad) { 113 | var body = script; 114 | script = 'global.__filename = ' + JSON.stringify(name) + ';\n' + 115 | 'global.exports = exports;\n' + 116 | 'global.module = module;\n' + 117 | 'global.__dirname = __dirname;\n' + 118 | 'global.require = require;\n' + 119 | 'return require("vm").runInThisContext(' + 120 | JSON.stringify(body) + ', ' + 121 | JSON.stringify(name) + ', true);\n'; 122 | } 123 | 124 | var result = module._compile(script, name + '-wrapper'); 125 | if (process._print_eval) console.log(result); 126 | } 127 | -------------------------------------------------------------------------------- /test/cli.js: -------------------------------------------------------------------------------- 1 | 2 | /** 3 | * This cli.js test file tests the `gnode` wrapper executable via 4 | * `child_process.spawn()`. Generator syntax is *NOT* enabled for these 5 | * test cases. 6 | */ 7 | 8 | var path = require('path'); 9 | var assert = require('assert'); 10 | var semver = require('semver'); 11 | var spawn = require('child_process').spawn; 12 | 13 | // node executable 14 | var node = process.execPath || process.argv[0]; 15 | var gnode = path.resolve(__dirname, '..', 'bin', 'gnode'); 16 | 17 | // chdir() to the "test" dir, so that relative test filenames work as expected 18 | process.chdir(path.resolve(__dirname, 'cli')); 19 | 20 | describe('command line interface', function () { 21 | 22 | this.slow(1000); 23 | this.timeout(2000); 24 | 25 | cli([ '-v' ], 'should output the version number', function (child, done) { 26 | buffer(child.stdout, function (err, data) { 27 | assert(semver.valid(data.trim())); 28 | done(); 29 | }); 30 | }); 31 | 32 | cli([ '--help' ], 'should output the "help" display', function (child, done) { 33 | buffer(child.stdout, function (err, data) { 34 | assert(/^Usage\: (node|iojs)/.test(data)); 35 | done(); 36 | }); 37 | }); 38 | 39 | cli([ 'check.js' ], 'should quit with a SUCCESS exit code', function (child, done) { 40 | child.on('exit', function (code) { 41 | assert(code == 0, 'gnode quit with exit code: ' + code); 42 | done(); 43 | }); 44 | }); 45 | 46 | cli([ 'nonexistant.js' ], 'should quit with a FAILURE exit code', function (child, done) { 47 | child.on('exit', function (code) { 48 | assert(code != 0, 'gnode quit with exit code: ' + code); 49 | done(); 50 | }); 51 | }); 52 | 53 | cli([ 'argv.js', '1', 'foo' ], 'should have a matching `process.argv`', function (child, done) { 54 | buffer(child.stdout, function (err, data) { 55 | if (err) return done(err); 56 | data = JSON.parse(data); 57 | assert('argv.js' == path.basename(data[1])); 58 | assert('1' == data[2]); 59 | assert('foo' == data[3]); 60 | done(); 61 | }); 62 | }); 63 | 64 | cli([ '--harmony_generators', 'check.js' ], 'should not output the "unrecognized flag" warning', function (child, done) { 65 | var async = 2; 66 | buffer(child.stderr, function (err, data) { 67 | if (err) return done(err); 68 | assert(!/unrecognized flag/.test(data), 'got stderr data: ' + JSON.stringify(data)); 69 | --async || done(); 70 | }); 71 | child.on('exit', function (code) { 72 | assert(code == 0, 'gnode quit with exit code: ' + code); 73 | --async || done(); 74 | }); 75 | }); 76 | 77 | cli([], 'should work properly over stdin', function (child, done) { 78 | child.stdin.end( 79 | 'var assert = require("assert");' + 80 | 'function *test () {};' + 81 | 'var t = test();' + 82 | 'assert("function" == typeof t.next);' + 83 | 'assert("function" == typeof t.throw);' 84 | ); 85 | child.on('exit', function (code) { 86 | assert(code == 0, 'gnode quit with exit code: ' + code); 87 | done(); 88 | }); 89 | }); 90 | 91 | if (!/^v0.8/.test(process.version)) cli(['-p', 'function *test () {yield 3}; test().next().value;'], 'should print result with -p', function (child, done) { 92 | var async = 2 93 | buffer(child.stdout, function (err, data) { 94 | if (err) return done(err); 95 | assert('3' == data.trim(), 'gnode printed ' + data); 96 | --async || done(); 97 | }); 98 | child.on('exit', function (code) { 99 | assert(code == 0, 'gnode quit with exit code: ' + code); 100 | --async || done(); 101 | }); 102 | }); 103 | 104 | cli(['-e', 'function *test () {yield 3}; console.log(test().next().value);'], 'should print result with -e', function (child, done) { 105 | var async = 2 106 | buffer(child.stdout, function (err, data) { 107 | if (err) return done(err); 108 | assert('3' == data.trim(), 'expected 3, got: ' + data); 109 | --async || done(); 110 | }); 111 | 112 | child.on('exit', function (code) { 113 | assert(code == 0, 'gnode quit with exit code: ' + code); 114 | --async || done(); 115 | }); 116 | }); 117 | 118 | cli(['--harmony_generators', '-e', 'function *test () {yield 3}; console.log(test().next().value);'], 'should print result with -e', function (child, done) { 119 | var async = 2 120 | buffer(child.stdout, function (err, data) { 121 | if (err) return done(err); 122 | assert('3' == data.trim(), 'gnode printed ' + data); 123 | --async || done(); 124 | }); 125 | child.on('exit', function (code) { 126 | assert(code == 0, 'gnode quit with exit code: ' + code); 127 | --async || done(); 128 | }); 129 | }); 130 | 131 | cli(['-e', 'console.log(JSON.stringify(process.argv))', 'a', 'b', 'c'], 'should pass additional arguments after -e', function (child, done) { 132 | var async = 2 133 | buffer(child.stdout, function (err, data) { 134 | if (err) return done(err); 135 | data = JSON.parse(data) 136 | assert.deepEqual(['a', 'b', 'c'], data.slice(2)) 137 | --async || done(); 138 | }); 139 | child.on('exit', function (code) { 140 | assert(code == 0, 'gnode quit with exit code: ' + code); 141 | --async || done(); 142 | }); 143 | }); 144 | }); 145 | 146 | 147 | function cli (argv, name, fn) { 148 | describe('gnode ' + argv.join(' '), function () { 149 | it(name, function (done) { 150 | var child = spawn(node, [ gnode ].concat(argv)); 151 | fn(child, done); 152 | }); 153 | }); 154 | } 155 | 156 | function buffer (stream, fn) { 157 | var buffers = ''; 158 | stream.setEncoding('utf8'); 159 | stream.on('data', ondata); 160 | stream.on('end', onend); 161 | stream.on('error', onerror); 162 | 163 | function ondata (b) { 164 | buffers += b; 165 | } 166 | function onend () { 167 | stream.removeListener('error', onerror); 168 | fn(null, buffers); 169 | } 170 | function onerror (err) { 171 | fn(err); 172 | } 173 | } 174 | --------------------------------------------------------------------------------