// Copyright Joyent, Inc. and other Node contributors. // // Permission is hereby granted, free of charge, to any person obtaining a // copy of this software and associated documentation files (the // "Software"), to deal in the Software without restriction, including // without limitation the rights to use, copy, modify, merge, publish, // distribute, sublicense, and/or sell copies of the Software, and to permit // persons to whom the Software is furnished to do so, subject to the // following conditions: // // The above copyright notice and this permission notice shall be included // in all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN // NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, // DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE // USE OR OTHER DEALINGS IN THE SOFTWARE. 'use strict'; if (module !== require.main) { // Signal we've been loaded as a module. // The following console.log() is part of the test. console.log('Loaded as a module, exiting with status code 42.'); process.exit(42); } const common = require('../common'); const assert = require('assert'); const child = require('child_process'); const path = require('path'); const fixtures = require('../common/fixtures'); if (process.argv.length > 2) { console.log(process.argv.slice(2).join(' ')); process.exit(0); } // Assert that nothing is written to stdout. child.exec(...common.escapePOSIXShell`"${process.execPath}" --eval 42`, common.mustSucceed((stdout, stderr) => { assert.strictEqual(stdout, ''); assert.strictEqual(stderr, ''); })); // Assert that "42\n" is written to stderr. child.exec(...common.escapePOSIXShell`"${process.execPath}" --eval "console.error(42)"`, common.mustSucceed((stdout, stderr) => { assert.strictEqual(stdout, ''); assert.strictEqual(stderr, '42\n'); })); // Assert that the expected output is written to stdout. ['--print', '-p -e', '-pe', '-p'].forEach((s) => { const [cmd, opts] = common.escapePOSIXShell`"${process.execPath}" ${s}`; child.exec(`${cmd} 42`, opts, common.mustSucceed((stdout, stderr) => { assert.strictEqual(stdout, '42\n'); assert.strictEqual(stderr, ''); })); child.exec(`${cmd} '[]'`, opts, common.mustSucceed((stdout, stderr) => { assert.strictEqual(stdout, '[]\n'); assert.strictEqual(stderr, ''); })); }); // Assert that module loading works. { common.spawnPromisified(process.execPath, ['--eval', `require(${JSON.stringify(__filename)})`]) .then(common.mustCall(({ stdout, stderr, code }) => { assert.strictEqual(stderr, ''); assert.strictEqual( stdout, 'Loaded as a module, exiting with status code 42.\n'); assert.strictEqual(code, 42); })); } // Check that builtin modules are pre-defined. child.exec(...common.escapePOSIXShell`"${process.execPath}" --print "os.platform()"`, common.mustSucceed((stdout, stderr) => { assert.strictEqual(stderr, ''); assert.strictEqual(stdout.trim(), require('os').platform()); })); // Module path resolve bug regression test. const [cmd, opts] = common.escapePOSIXShell`"${process.execPath}" --eval "require('./test/parallel/test-cli-eval.js')"`; child.exec(cmd, { ...opts, cwd: path.resolve(__dirname, '../../') }, common.mustCall((err, stdout, stderr) => { assert.strictEqual(err.code, 42); assert.strictEqual( stdout, 'Loaded as a module, exiting with status code 42.\n'); assert.strictEqual(stderr, ''); })); // Missing argument should not crash. child.exec(...common.escapePOSIXShell`"${process.execPath}" -e`, common.mustCall((err, stdout, stderr) => { assert.strictEqual(err.code, 9); assert.strictEqual(stdout, ''); assert.strictEqual(stderr.trim(), `${process.execPath}: -e requires an argument`); })); // Empty program should do nothing. child.exec(...common.escapePOSIXShell`"${process.execPath}" -e ""`, common.mustSucceed((stdout, stderr) => { assert.strictEqual(stdout, ''); assert.strictEqual(stderr, ''); })); // "\\-42" should be interpreted as an escaped expression, not a switch. child.exec(...common.escapePOSIXShell`"${process.execPath}" -p "\\-42"`, common.mustSucceed((stdout, stderr) => { assert.strictEqual(stdout, '-42\n'); assert.strictEqual(stderr, ''); })); child.exec(...common.escapePOSIXShell`"${process.execPath}" --use-strict -p process.execArgv`, common.mustSucceed((stdout, stderr) => { assert.strictEqual( stdout, "[ '--use-strict', '-p', 'process.execArgv' ]\n" ); assert.strictEqual(stderr, ''); })); // Regression test for https://github.com/nodejs/node/issues/3574. { const emptyFile = fixtures.path('empty.js'); common.spawnPromisified(process.execPath, ['-e', `require("child_process").fork(${JSON.stringify(emptyFile)})`]) .then(common.mustCall(({ stdout, stderr, code }) => { assert.strictEqual(stdout, ''); assert.strictEqual(stderr, ''); assert.strictEqual(code, 0); })); // Make sure that monkey-patching process.execArgv doesn't cause child_process // to incorrectly munge execArgv. common.spawnPromisified(process.execPath, [ '-e', 'process.execArgv = [\'-e\', \'console.log(42)\', \'thirdArg\'];' + `require('child_process').fork(${JSON.stringify(emptyFile)})`, ]).then(common.mustCall(({ stdout, stderr, code }) => { assert.strictEqual(stdout, '42\n'); assert.strictEqual(stderr, ''); assert.strictEqual(code, 0); })); } // Regression test for https://github.com/nodejs/node/issues/8534. { const script = ` // console.log() can revive the event loop so we must be careful // to write from a 'beforeExit' event listener only once. process.once("beforeExit", () => console.log("beforeExit")); process.on("exit", () => console.log("exit")); console.log("start"); `; const options = { encoding: 'utf8' }; const proc = child.spawnSync(process.execPath, ['-e', script], options); assert.strictEqual(proc.stderr, ''); assert.strictEqual(proc.stdout, 'start\nbeforeExit\nexit\n'); } // Regression test for https://github.com/nodejs/node/issues/11948. { const script = ` process.on('message', (message) => { if (message === 'ping') process.send('pong'); if (message === 'exit') process.disconnect(); }); `; const proc = child.fork('-e', [script]); proc.on('exit', common.mustCall((exitCode, signalCode) => { assert.strictEqual(exitCode, 0); assert.strictEqual(signalCode, null); })); proc.on('message', (message) => { if (message === 'pong') proc.send('exit'); }); proc.send('ping'); } [ '-arg1', '-arg1 arg2 --arg3', '--', 'arg1 -- arg2', ].forEach(function(args) { // Ensure that arguments are successfully passed to eval. child.exec( ...common.escapePOSIXShell`"${process.execPath}" --eval "console.log(process.argv.slice(1).join(' '))" -- ${args}`, common.mustCall(function(err, stdout, stderr) { assert.strictEqual(stdout, `${args}\n`); assert.strictEqual(stderr, ''); assert.strictEqual(err, null); }) ); // Ensure that arguments are successfully passed to print. child.exec( ...common.escapePOSIXShell`"${process.execPath}" --print "process.argv.slice(1).join(' ')" -- ${args}`, common.mustCall(function(err, stdout, stderr) { assert.strictEqual(stdout, `${args}\n`); assert.strictEqual(stderr, ''); assert.strictEqual(err, null); }) ); // Ensure that arguments are successfully passed to a script. // The first argument after '--' should be interpreted as a script // filename. child.exec(...common.escapePOSIXShell`"${process.execPath}" -- "${__filename}" ${args}`, common.mustCall(function(err, stdout, stderr) { assert.strictEqual(stdout, `${args}\n`); assert.strictEqual(stderr, ''); assert.strictEqual(err, null); })); }); // ESModule eval tests // Assert that "42\n" is written to stdout on module eval. const execOptions = '--input-type module'; child.exec( ...common.escapePOSIXShell`"${process.execPath}" ${execOptions} --eval "console.log(42)"`, common.mustSucceed((stdout) => { assert.strictEqual(stdout, '42\n'); })); // Assert that "42\n" is written to stdout with print option. child.exec( ...common.escapePOSIXShell`"${process.execPath}" ${execOptions} --print --eval "42"`, common.mustCall((err, stdout, stderr) => { assert.ok(err); assert.strictEqual(stdout, ''); assert.ok(stderr.includes('--print cannot be used with ESM input')); })); // Assert that error is written to stderr on invalid input. child.exec( ...common.escapePOSIXShell`"${process.execPath}" ${execOptions} --eval "!!!!"`, common.mustCall((err, stdout, stderr) => { assert.ok(err); assert.strictEqual(stdout, ''); assert.ok(stderr.indexOf('SyntaxError: Unexpected end of input') > 0); })); // Assert that require is undefined in ESM support child.exec( ...common.escapePOSIXShell`"${process.execPath}" ${execOptions} --eval "console.log(typeof require);"`, common.mustSucceed((stdout) => { assert.strictEqual(stdout, 'undefined\n'); })); // Assert that import.meta is defined in ESM child.exec( ...common.escapePOSIXShell`"${process.execPath}" ${execOptions} --eval "console.log(typeof import.meta);"`, common.mustSucceed((stdout) => { assert.strictEqual(stdout, 'object\n'); })); { // Assert that packages can be imported cwd-relative with --eval const [cmd, opts] = common.escapePOSIXShell`"${process.execPath}" ${execOptions} --eval`; const options = { ...opts, cwd: path.join(__dirname, '../..') }; child.exec( `${cmd} "import './test/fixtures/es-modules/mjs-file.mjs'"`, options, common.mustSucceed((stdout) => { assert.strictEqual(stdout, '.mjs file\n'); })); // Assert that packages can be dynamic imported initial cwd-relative with --eval child.exec( cmd + ' "process.chdir(\'..\');' + 'import(\'./test/fixtures/es-modules/mjs-file.mjs\')"', options, common.mustSucceed((stdout) => { assert.strictEqual(stdout, '.mjs file\n'); })); child.exec( cmd + ' "process.chdir(\'..\');' + 'import(\'./test/fixtures/es-modules/mjs-file.mjs\')"', options, common.mustSucceed((stdout) => { assert.strictEqual(stdout, '.mjs file\n'); })); } if (common.hasCrypto) { // Assert that calls to crypto utils work without require. child.exec( ...common.escapePOSIXShell`"${process.execPath}" -e "console.log(crypto.randomBytes(16).toString('hex'))"`, common.mustSucceed((stdout) => { assert.match(stdout, /[0-9a-f]{32}/i); })); child.exec( ...common.escapePOSIXShell`"${process.execPath}" -p "crypto.randomBytes(16).toString('hex')"`, common.mustSucceed((stdout) => { assert.match(stdout, /[0-9a-f]{32}/i); })); } // Assert that overriding crypto works. child.exec( ...common.escapePOSIXShell`"${process.execPath}" -p "crypto=Symbol('test')"`, common.mustSucceed((stdout) => { assert.match(stdout, /Symbol\(test\)/i); })); child.exec( ...common.escapePOSIXShell`"${process.execPath}" -e "crypto = {};console.log('randomBytes', typeof crypto.randomBytes)"`, common.mustSucceed((stdout) => { assert.match(stdout, /randomBytes\sundefined/); })); // Assert that overriding crypto with a local variable works. child.exec( ...common.escapePOSIXShell`"${process.execPath}" -e "const crypto = {};console.log('randomBytes', typeof crypto.randomBytes)"`, common.mustSucceed((stdout) => { assert.match(stdout, /randomBytes\sundefined/); })); child.exec( ...common.escapePOSIXShell`"${process.execPath}" -e "let crypto = {};console.log('randomBytes', typeof crypto.randomBytes)"`, common.mustSucceed((stdout) => { assert.match(stdout, /randomBytes\sundefined/); })); child.exec( ...common.escapePOSIXShell`"${process.execPath}" -e "var crypto = {};console.log('randomBytes', typeof crypto.randomBytes)"`, common.mustSucceed((stdout) => { assert.match(stdout, /randomBytes\sundefined/); })); child.exec( ...common.escapePOSIXShell`"${process.execPath}" -p "const crypto = {randomBytes:1};typeof crypto.randomBytes"`, common.mustSucceed((stdout) => { assert.match(stdout, /^number/); })); child.exec( ...common.escapePOSIXShell`"${process.execPath}" -p "let crypto = {randomBytes:1};typeof crypto.randomBytes"`, common.mustSucceed((stdout) => { assert.match(stdout, /^number/); })); // Regression test for https://github.com/nodejs/node/issues/45336 child.execFile(process.execPath, ['-p', 'Object.defineProperty(global, "fs", { configurable: false });' + 'fs === require("node:fs")'], common.mustSucceed((stdout) => { assert.match(stdout, /^true/); }));