diff --git a/src/node_options.cc b/src/node_options.cc index 5cf7a9cbf58c7e..d74ed1ca5afe53 100644 --- a/src/node_options.cc +++ b/src/node_options.cc @@ -422,10 +422,6 @@ void Parse( // TODO(addaleax): Make that unnecessary. DebugOptionsParser::DebugOptionsParser() { -#ifndef DISABLE_SINGLE_EXECUTABLE_APPLICATION - if (sea::IsSingleExecutable()) return; -#endif - AddOption("--inspect-port", "set host:port for inspector", &DebugOptions::host_port, diff --git a/test/sequential/sequential.status b/test/sequential/sequential.status index 439fa2e3cd0f22..63414f94193595 100644 --- a/test/sequential/sequential.status +++ b/test/sequential/sequential.status @@ -61,6 +61,8 @@ test-single-executable-application-disable-experimental-sea-warning: SKIP test-single-executable-application-empty: SKIP test-single-executable-application-exec-argv: SKIP test-single-executable-application-exec-argv-empty: SKIP +test-single-executable-application-inspect-in-sea-flags: SKIP +test-single-executable-application-inspect: SKIP test-single-executable-application-snapshot: SKIP test-single-executable-application-snapshot-and-code-cache: SKIP test-single-executable-application-snapshot-worker: SKIP diff --git a/test/sequential/test-single-executable-application-inspect-in-sea-flags.js b/test/sequential/test-single-executable-application-inspect-in-sea-flags.js new file mode 100644 index 00000000000000..32a0ad34848e80 --- /dev/null +++ b/test/sequential/test-single-executable-application-inspect-in-sea-flags.js @@ -0,0 +1,69 @@ +'use strict'; + +// This tests that the debugger flag --inspect passed directly to a single executable +// application would not be consumed by Node.js but passed to the application +// instead. + +require('../common'); +const assert = require('assert'); +const { writeFileSync, existsSync } = require('fs'); +const { spawnSyncAndAssert } = require('../common/child_process'); +const tmpdir = require('../common/tmpdir'); +const { spawnSyncAndExitWithoutError } = require('../common/child_process'); + +const { + generateSEA, + skipIfSingleExecutableIsNotSupported, +} = require('../common/sea'); + +skipIfSingleExecutableIsNotSupported(); + +const configFile = tmpdir.resolve('sea-config.json'); +const seaPrepBlob = tmpdir.resolve('sea-prep.blob'); +const outputFile = tmpdir.resolve(process.platform === 'win32' ? 'sea.exe' : 'sea'); + +tmpdir.refresh(); + +writeFileSync(tmpdir.resolve('sea.js'), `console.log(process.argv);`, 'utf-8'); + +// Create SEA configuration +writeFileSync(configFile, ` +{ + "main": "sea.js", + "output": "sea-prep.blob" +} +`); + +// Generate the SEA prep blob +spawnSyncAndExitWithoutError( + process.execPath, + ['--experimental-sea-config', 'sea-config.json'], + { cwd: tmpdir.path } +); + +assert(existsSync(seaPrepBlob)); + +// Generate the SEA executable +generateSEA(outputFile, process.execPath, seaPrepBlob); + +// Spawn the SEA with inspect option +spawnSyncAndAssert( + outputFile, + ['--inspect=0'], + { + env: { + ...process.env, + }, + }, + { + stdout(data) { + assert.match(data, /--inspect=0/); + return true; + }, + stderr(data) { + assert.doesNotMatch(data, /Debugger listening/); + return true; + }, + trim: true, + } +); diff --git a/test/sequential/test-single-executable-application-inspect.js b/test/sequential/test-single-executable-application-inspect.js new file mode 100644 index 00000000000000..009621ddf78b4d --- /dev/null +++ b/test/sequential/test-single-executable-application-inspect.js @@ -0,0 +1,122 @@ +'use strict'; + +// This tests the creation of a single executable application that can be +// debugged using the inspector protocol with NODE_OPTIONS=--inspect-brk=0 + +require('../common'); +const assert = require('assert'); +const { writeFileSync, existsSync } = require('fs'); +const { spawn } = require('child_process'); +const tmpdir = require('../common/tmpdir'); +const { spawnSyncAndExitWithoutError } = require('../common/child_process'); + +const { + generateSEA, + skipIfSingleExecutableIsNotSupported, +} = require('../common/sea'); + +skipIfSingleExecutableIsNotSupported(); + +const configFile = tmpdir.resolve('sea-config.json'); +const seaPrepBlob = tmpdir.resolve('sea-prep.blob'); +const outputFile = tmpdir.resolve(process.platform === 'win32' ? 'sea.exe' : 'sea'); + +tmpdir.refresh(); + +// Create a simple hello world script +writeFileSync(tmpdir.resolve('hello.js'), `console.log('Hello, world!');`, 'utf-8'); + +// Create SEA configuration +writeFileSync(configFile, ` +{ + "main": "hello.js", + "output": "sea-prep.blob" +} +`); + +// Generate the SEA prep blob +spawnSyncAndExitWithoutError( + process.execPath, + ['--experimental-sea-config', 'sea-config.json'], + { cwd: tmpdir.path } +); + +assert(existsSync(seaPrepBlob)); + +// Generate the SEA executable +generateSEA(outputFile, process.execPath, seaPrepBlob); + +// Spawn the SEA with inspect option +const seaProcess = spawn(outputFile, [], { + env: { + ...process.env, + NODE_OPTIONS: '--inspect-brk=0', + }, +}); + +let debuggerUrl = null; +let seaStderr = ''; + +seaProcess.stderr.setEncoding('utf8'); +seaProcess.stdout.setEncoding('utf8'); + +seaProcess.stdout.on('data', (data) => { + console.log(`[SEA][STDOUT] ${data}`); +}); + +seaProcess.stderr.on('data', (data) => { + console.log(`[SEA][STDERR] ${data}`); + seaStderr += data; + + // Parse the debugger listening message + const match = seaStderr.match(/Debugger listening on ws:\/\/([\d.]+):(\d+)\//); + if (match && !debuggerUrl) { + const host = match[1]; + const port = match[2]; + debuggerUrl = `${host}:${port}`; + + console.log(`Running ${process.execPath} inspect ${debuggerUrl}`); + // Once we have the debugger URL, spawn the inspector CLI + const inspectorProcess = spawn(process.execPath, ['inspect', debuggerUrl], { + stdio: ['pipe', 'pipe', 'pipe'], + }); + + let inspectorStdout = ''; + inspectorProcess.stdout.setEncoding('utf8'); + inspectorProcess.stderr.setEncoding('utf8'); + + inspectorProcess.stdout.on('data', (data) => { + console.log(`[INSPECT][STDOUT] ${data}`); + inspectorStdout += data; + + // Check if we successfully connected + const matches = [...inspectorStdout.matchAll(/debug> /g)]; + if (inspectorStdout.includes(`connecting to ${host}:${port} ... ok`) && + matches.length >= 2) { + // We are at the second prompt, which means we can send commands to terminate both now. + console.log('Sending .exit command to inspector...'); + inspectorProcess.stdin.write('.exit\n'); + } + }); + + inspectorProcess.stderr.on('data', (data) => { + console.log(`[INSPECT][STDERR] ${data}`); + }); + + inspectorProcess.on('close', (code) => { + assert.strictEqual(code, 0, `Inspector process exited with code ${code}.`); + }); + + inspectorProcess.on('error', (err) => { + throw err; + }); + } +}); + +seaProcess.on('close', (code) => { + assert.strictEqual(code, 0, `SEA process exited with code ${code}.`); +}); + +seaProcess.on('error', (err) => { + throw err; +});