sea: allow using inspector command line flags with SEA · nodejs/node@bf26b47

1+

'use strict';

2+3+

// This tests the creation of a single executable application that can be

4+

// debugged using the inspector protocol with NODE_OPTIONS=--inspect-brk=0

5+6+

require('../common');

7+

const assert = require('assert');

8+

const { writeFileSync, existsSync } = require('fs');

9+

const { spawn } = require('child_process');

10+

const tmpdir = require('../common/tmpdir');

11+

const { spawnSyncAndExitWithoutError } = require('../common/child_process');

12+13+

const {

14+

generateSEA,

15+

skipIfSingleExecutableIsNotSupported,

16+

} = require('../common/sea');

17+18+

skipIfSingleExecutableIsNotSupported();

19+20+

const configFile = tmpdir.resolve('sea-config.json');

21+

const seaPrepBlob = tmpdir.resolve('sea-prep.blob');

22+

const outputFile = tmpdir.resolve(process.platform === 'win32' ? 'sea.exe' : 'sea');

23+24+

tmpdir.refresh();

25+26+

// Create a simple hello world script

27+

writeFileSync(tmpdir.resolve('hello.js'), `console.log('Hello, world!');`, 'utf-8');

28+29+

// Create SEA configuration

30+

writeFileSync(configFile, `

31+

{

32+

"main": "hello.js",

33+

"output": "sea-prep.blob"

34+

}

35+

`);

36+37+

// Generate the SEA prep blob

38+

spawnSyncAndExitWithoutError(

39+

process.execPath,

40+

['--experimental-sea-config', 'sea-config.json'],

41+

{ cwd: tmpdir.path }

42+

);

43+44+

assert(existsSync(seaPrepBlob));

45+46+

// Generate the SEA executable

47+

generateSEA(outputFile, process.execPath, seaPrepBlob);

48+49+

// Spawn the SEA with inspect option

50+

const seaProcess = spawn(outputFile, [], {

51+

env: {

52+

...process.env,

53+

NODE_OPTIONS: '--inspect-brk=0',

54+

},

55+

});

56+57+

let debuggerUrl = null;

58+

let seaStderr = '';

59+60+

seaProcess.stderr.setEncoding('utf8');

61+

seaProcess.stdout.setEncoding('utf8');

62+63+

seaProcess.stdout.on('data', (data) => {

64+

console.log(`[SEA][STDOUT] ${data}`);

65+

});

66+67+

seaProcess.stderr.on('data', (data) => {

68+

console.log(`[SEA][STDERR] ${data}`);

69+

seaStderr += data;

70+71+

// Parse the debugger listening message

72+

const match = seaStderr.match(/Debugger listening on ws:\/\/([\d.]+):(\d+)\//);

73+

if (match && !debuggerUrl) {

74+

const host = match[1];

75+

const port = match[2];

76+

debuggerUrl = `${host}:${port}`;

77+78+

console.log(`Running ${process.execPath} inspect ${debuggerUrl}`);

79+

// Once we have the debugger URL, spawn the inspector CLI

80+

const inspectorProcess = spawn(process.execPath, ['inspect', debuggerUrl], {

81+

stdio: ['pipe', 'pipe', 'pipe'],

82+

});

83+84+

let inspectorStdout = '';

85+

inspectorProcess.stdout.setEncoding('utf8');

86+

inspectorProcess.stderr.setEncoding('utf8');

87+88+

inspectorProcess.stdout.on('data', (data) => {

89+

console.log(`[INSPECT][STDOUT] ${data}`);

90+

inspectorStdout += data;

91+92+

// Check if we successfully connected

93+

const matches = [...inspectorStdout.matchAll(/debug> /g)];

94+

if (inspectorStdout.includes(`connecting to ${host}:${port} ... ok`) &&

95+

matches.length >= 2) {

96+

// We are at the second prompt, which means we can send commands to terminate both now.

97+

console.log('Sending .exit command to inspector...');

98+

inspectorProcess.stdin.write('.exit\n');

99+

}

100+

});

101+102+

inspectorProcess.stderr.on('data', (data) => {

103+

console.log(`[INSPECT][STDERR] ${data}`);

104+

});

105+106+

inspectorProcess.on('close', (code) => {

107+

assert.strictEqual(code, 0, `Inspector process exited with code ${code}.`);

108+

});

109+110+

inspectorProcess.on('error', (err) => {

111+

throw err;

112+

});

113+

}

114+

});

115+116+

seaProcess.on('close', (code) => {

117+

assert.strictEqual(code, 0, `SEA process exited with code ${code}.`);

118+

});

119+120+

seaProcess.on('error', (err) => {

121+

throw err;

122+

});