diff --git a/doc/iojs.1 b/doc/iojs.1 index 9ca539be366d1e..57e8c2ed91733d 100644 --- a/doc/iojs.1 +++ b/doc/iojs.1 @@ -52,6 +52,8 @@ and servers. -i, --interactive always enter the REPL even if stdin does not appear to be a terminal + -r, --require module to preload at startup + --no-deprecation silence deprecation warnings --trace-deprecation show stack traces on deprecations diff --git a/src/node.cc b/src/node.cc index 394876287903d4..42b690f5d41b72 100644 --- a/src/node.cc +++ b/src/node.cc @@ -114,6 +114,8 @@ static bool trace_deprecation = false; static bool throw_deprecation = false; static bool abort_on_uncaught_exception = false; static const char* eval_string = nullptr; +static unsigned int preload_module_count = 0; +static const char** preload_modules = nullptr; static bool use_debug_agent = false; static bool debug_wait_connect = false; static int debug_port = 5858; @@ -2766,6 +2768,19 @@ void SetupProcessObject(Environment* env, READONLY_PROPERTY(process, "_forceRepl", True(env->isolate())); } + if (preload_module_count) { + CHECK(preload_modules); + Local array = Array::New(env->isolate()); + for (unsigned int i = 0; i < preload_module_count; ++i) { + Local module = String::NewFromUtf8(env->isolate(), + preload_modules[i]); + array->Set(i, module); + } + READONLY_PROPERTY(process, + "_preload_modules", + array); + } + // --no-deprecation if (no_deprecation) { READONLY_PROPERTY(process, "noDeprecation", True(env->isolate())); @@ -2990,6 +3005,7 @@ static void PrintHelp() { " -p, --print evaluate script and print result\n" " -i, --interactive always enter the REPL even if stdin\n" " does not appear to be a terminal\n" + " -r, --require module to preload (option can be repeated)\n" " --no-deprecation silence deprecation warnings\n" " --throw-deprecation throw an exception anytime a deprecated " "function is used\n" @@ -3045,11 +3061,13 @@ static void ParseArgs(int* argc, const char** new_exec_argv = new const char*[nargs]; const char** new_v8_argv = new const char*[nargs]; const char** new_argv = new const char*[nargs]; + const char** local_preload_modules = new const char*[nargs]; for (unsigned int i = 0; i < nargs; ++i) { new_exec_argv[i] = nullptr; new_v8_argv[i] = nullptr; new_argv[i] = nullptr; + local_preload_modules[i] = nullptr; } // exec_argv starts with the first option, the other two start with argv[0]. @@ -3098,6 +3116,15 @@ static void ParseArgs(int* argc, eval_string += 1; } } + } else if (strcmp(arg, "--require") == 0 || + strcmp(arg, "-r") == 0) { + const char* module = argv[index + 1]; + if (module == nullptr) { + fprintf(stderr, "%s: %s requires an argument\n", argv[0], arg); + exit(9); + } + args_consumed += 1; + local_preload_modules[preload_module_count++] = module; } else if (strcmp(arg, "--interactive") == 0 || strcmp(arg, "-i") == 0) { force_repl = true; } else if (strcmp(arg, "--no-deprecation") == 0) { @@ -3144,6 +3171,16 @@ static void ParseArgs(int* argc, memcpy(argv, new_argv, new_argc * sizeof(*argv)); delete[] new_argv; *argc = static_cast(new_argc); + + // Copy the preload_modules from the local array to an appropriately sized + // global array. + if (preload_module_count > 0) { + CHECK(!preload_modules); + preload_modules = new const char*[preload_module_count]; + memcpy(preload_modules, local_preload_modules, + preload_module_count * sizeof(*preload_modules)); + } + delete[] local_preload_modules; } diff --git a/src/node.js b/src/node.js index 93660867f30b37..7215f747190a3c 100644 --- a/src/node.js +++ b/src/node.js @@ -68,84 +68,96 @@ var d = NativeModule.require('_debug_agent'); d.start(); - } else if (process._eval != null) { - // User passed '-e' or '--eval' arguments to Node. - evalScript('[eval]'); - } else if (process.argv[1]) { - // make process.argv[1] into a full path - var path = NativeModule.require('path'); - process.argv[1] = path.resolve(process.argv[1]); - - // If this is a worker in cluster mode, start up the communication - // channel. - if (process.env.NODE_UNIQUE_ID) { - var cluster = NativeModule.require('cluster'); - cluster._setupWorker(); - - // Make sure it's not accidentally inherited by child processes. - delete process.env.NODE_UNIQUE_ID; + } else { + // There is user code to be run + + // Load any preload modules + if (process._preload_modules) { + var Module = NativeModule.require('module'); + process._preload_modules.forEach(function(module) { + Module._load(module); + }); } - var Module = NativeModule.require('module'); + if (process._eval != null) { + // User passed '-e' or '--eval' arguments to Node. + evalScript('[eval]'); + } else if (process.argv[1]) { + // make process.argv[1] into a full path + var path = NativeModule.require('path'); + process.argv[1] = path.resolve(process.argv[1]); + + // If this is a worker in cluster mode, start up the communication + // channel. + if (process.env.NODE_UNIQUE_ID) { + var cluster = NativeModule.require('cluster'); + cluster._setupWorker(); + + // Make sure it's not accidentally inherited by child processes. + delete process.env.NODE_UNIQUE_ID; + } - if (global.v8debug && - process.execArgv.some(function(arg) { - return arg.match(/^--debug-brk(=[0-9]*)?$/); - })) { + var Module = NativeModule.require('module'); - // XXX Fix this terrible hack! - // - // Give the client program a few ticks to connect. - // Otherwise, there's a race condition where `node debug foo.js` - // will not be able to connect in time to catch the first - // breakpoint message on line 1. - // - // A better fix would be to somehow get a message from the - // global.v8debug object about a connection, and runMain when - // that occurs. --isaacs + if (global.v8debug && + process.execArgv.some(function(arg) { + return arg.match(/^--debug-brk(=[0-9]*)?$/); + })) { - var debugTimeout = +process.env.NODE_DEBUG_TIMEOUT || 50; - setTimeout(Module.runMain, debugTimeout); + // XXX Fix this terrible hack! + // + // Give the client program a few ticks to connect. + // Otherwise, there's a race condition where `node debug foo.js` + // will not be able to connect in time to catch the first + // breakpoint message on line 1. + // + // A better fix would be to somehow get a message from the + // global.v8debug object about a connection, and runMain when + // that occurs. --isaacs - } else { - // Main entry point into most programs: - Module.runMain(); - } + var debugTimeout = +process.env.NODE_DEBUG_TIMEOUT || 50; + setTimeout(Module.runMain, debugTimeout); - } else { - var Module = NativeModule.require('module'); - - // If -i or --interactive were passed, or stdin is a TTY. - if (process._forceRepl || NativeModule.require('tty').isatty(0)) { - // REPL - var opts = { - useGlobal: true, - ignoreUndefined: false - }; - if (parseInt(process.env['NODE_NO_READLINE'], 10)) { - opts.terminal = false; - } - if (parseInt(process.env['NODE_DISABLE_COLORS'], 10)) { - opts.useColors = false; + } else { + // Main entry point into most programs: + Module.runMain(); } - var repl = Module.requireRepl().start(opts); - repl.on('exit', function() { - process.exit(); - }); } else { - // Read all of stdin - execute it. - process.stdin.setEncoding('utf8'); + var Module = NativeModule.require('module'); + + // If -i or --interactive were passed, or stdin is a TTY. + if (process._forceRepl || NativeModule.require('tty').isatty(0)) { + // REPL + var opts = { + useGlobal: true, + ignoreUndefined: false + }; + if (parseInt(process.env['NODE_NO_READLINE'], 10)) { + opts.terminal = false; + } + if (parseInt(process.env['NODE_DISABLE_COLORS'], 10)) { + opts.useColors = false; + } + var repl = Module.requireRepl().start(opts); + repl.on('exit', function() { + process.exit(); + }); - var code = ''; - process.stdin.on('data', function(d) { - code += d; - }); + } else { + // Read all of stdin - execute it. + process.stdin.setEncoding('utf8'); - process.stdin.on('end', function() { - process._eval = code; - evalScript('[stdin]'); - }); + var code = ''; + process.stdin.on('data', function(d) { + code += d; + }); + + process.stdin.on('end', function() { + process._eval = code; + evalScript('[stdin]'); + }); + } } } } diff --git a/test/fixtures/printA.js b/test/fixtures/printA.js new file mode 100644 index 00000000000000..30a56425de829c --- /dev/null +++ b/test/fixtures/printA.js @@ -0,0 +1 @@ +console.log('A') diff --git a/test/fixtures/printB.js b/test/fixtures/printB.js new file mode 100644 index 00000000000000..241b6bdcf6f4d2 --- /dev/null +++ b/test/fixtures/printB.js @@ -0,0 +1 @@ +console.log('B') diff --git a/test/fixtures/printC.js b/test/fixtures/printC.js new file mode 100644 index 00000000000000..9a203c981ea11d --- /dev/null +++ b/test/fixtures/printC.js @@ -0,0 +1 @@ +console.log('C') diff --git a/test/parallel/test-preload.js b/test/parallel/test-preload.js new file mode 100644 index 00000000000000..aef778b5873073 --- /dev/null +++ b/test/parallel/test-preload.js @@ -0,0 +1,74 @@ +var common = require('../common'), + assert = require('assert'), + path = require('path'), + child_process = require('child_process'); + +var nodeBinary = process.argv[0]; + +var preloadOption = function(preloads) { + var option = ''; + preloads.forEach(function(preload, index) { + // TODO: randomly pick -r or --require + option += '-r ' + preload + ' '; + }); + return option; +} + +var fixture = function(name) { + return path.join(__dirname, '../fixtures/' + name); +} + +var fixtureA = fixture('printA.js'); +var fixtureB = fixture('printB.js'); +var fixtureC = fixture('printC.js') +var fixtureThrows = fixture('throws_error4.js'); + +// test preloading a single module works +child_process.exec(nodeBinary + ' ' + + preloadOption([fixtureA]) + ' ' + + fixtureB, + function(err, stdout, stderr) { + if (err) throw err; + assert.equal(stdout, 'A\nB\n'); + }); + +// test preloading multiple modules works +child_process.exec(nodeBinary + ' ' + + preloadOption([fixtureA, fixtureB]) + ' ' + + fixtureC, + function(err, stdout, stderr) { + if (err) throw err; + assert.equal(stdout, 'A\nB\nC\n'); + }); + +// test that preloading a throwing module aborts +child_process.exec(nodeBinary + ' ' + + preloadOption([fixtureA, fixtureThrows]) + ' ' + + fixtureB, + function(err, stdout, stderr) { + if (err) { + assert.equal(stdout, 'A\n'); + } else { + throw new Error('Preload should have failed'); + } + }); + +// test that preload can be used with --eval +child_process.exec(nodeBinary + ' ' + + preloadOption([fixtureA]) + + '-e "console.log(\'hello\');"', + function(err, stdout, stderr) { + if (err) throw err; + assert.equal(stdout, 'A\nhello\n'); + }); + +// test that preload placement at other points in the cmdline +// also test that duplicated preload only gets loaded once +child_process.exec(nodeBinary + ' ' + + preloadOption([fixtureA]) + + '-e "console.log(\'hello\');" ' + + preloadOption([fixtureA, fixtureB]), + function(err, stdout, stderr) { + if (err) throw err; + assert.equal(stdout, 'A\nB\nhello\n'); + });