◐ Shell
clean mode source ↗

watch: track worker entry files in watch mode · nodejs/node@0e2adb3

1+

import * as common from '../common/index.mjs';

2+

import tmpdir from '../common/tmpdir.js';

3+

import assert from 'node:assert';

4+

import path from 'node:path';

5+

import { execPath } from 'node:process';

6+

import { describe, it } from 'node:test';

7+

import { spawn } from 'node:child_process';

8+

import { writeFileSync, readFileSync } from 'node:fs';

9+

import { inspect } from 'node:util';

10+

import { pathToFileURL } from 'node:url';

11+

import { createInterface } from 'node:readline';

12+13+

if (common.isIBMi)

14+

common.skip('IBMi does not support `fs.watch()`');

15+16+

function restart(file, content = readFileSync(file)) {

17+

writeFileSync(file, content);

18+

const timer = setInterval(() => writeFileSync(file, content), common.platformTimeout(250));

19+

return () => clearInterval(timer);

20+

}

21+22+

let tmpFiles = 0;

23+

function createTmpFile(content = 'console.log(\'running\');', ext = '.js', basename = tmpdir.path) {

24+

const file = path.join(basename, `${tmpFiles++}${ext}`);

25+

writeFileSync(file, content);

26+

return file;

27+

}

28+29+

async function runWriteSucceed({

30+

file,

31+

watchedFile,

32+

watchFlag = '--watch',

33+

args = [file],

34+

completed = 'Completed running',

35+

restarts = 2,

36+

options = {},

37+

shouldFail = false,

38+

}) {

39+

args.unshift('--no-warnings');

40+

if (watchFlag !== null) args.unshift(watchFlag);

41+42+

const child = spawn(execPath, args, { encoding: 'utf8', stdio: 'pipe', ...options });

43+44+

let completes = 0;

45+

let cancelRestarts = () => {};

46+

let stderr = '';

47+

const stdout = [];

48+49+

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

50+

stderr += data;

51+

});

52+53+

try {

54+

for await (const data of createInterface({ input: child.stdout })) {

55+

if (!data.startsWith('Waiting for graceful termination') &&

56+

!data.startsWith('Gracefully restarted')) {

57+

stdout.push(data);

58+

}

59+60+

if (data.startsWith(completed)) {

61+

completes++;

62+63+

if (completes === restarts) break;

64+65+

if (completes === 1) {

66+

cancelRestarts = restart(watchedFile);

67+

}

68+

}

69+70+

if (!shouldFail && data.startsWith('Failed running')) break;

71+

}

72+

} finally {

73+

child.kill();

74+

cancelRestarts();

75+

}

76+77+

return { stdout, stderr, pid: child.pid };

78+

}

79+80+

tmpdir.refresh();

81+

const dir = tmpdir.path;

82+83+

describe('watch mode', { concurrency: !process.env.TEST_PARALLEL, timeout: 60_000 }, () => {

84+

it('should watch changes to worker - cjs', async () => {

85+

const worker = path.join(dir, 'worker.js');

86+87+

writeFileSync(worker, `

88+

console.log('worker running');

89+

`);

90+91+

const file = createTmpFile(`

92+

const { Worker } = require('node:worker_threads');

93+

const w = new Worker(${JSON.stringify(worker)});

94+

`, '.js', dir);

95+96+

const { stderr, stdout } = await runWriteSucceed({

97+

file,

98+

watchedFile: worker,

99+

});

100+101+

assert.strictEqual(stderr, '');

102+

assert.deepStrictEqual(stdout, [

103+

'worker running',

104+

`Completed running ${inspect(file)}. Waiting for file changes before restarting...`,

105+

`Restarting ${inspect(file)}`,

106+

'worker running',

107+

`Completed running ${inspect(file)}. Waiting for file changes before restarting...`,

108+

]);

109+

});

110+111+

it('should watch changes to worker dependencies - cjs', async () => {

112+

const dep = path.join(dir, 'dep.js');

113+

const worker = path.join(dir, 'worker.js');

114+115+

writeFileSync(dep, `

116+

module.exports = 'dep v1';

117+

`);

118+119+

writeFileSync(worker, `

120+

const dep = require('./dep.js');

121+

console.log(dep);

122+

`);

123+124+

const file = createTmpFile(`

125+

const { Worker } = require('node:worker_threads');

126+

const w = new Worker(${JSON.stringify(worker)});

127+

`, '.js', dir);

128+129+

const { stderr, stdout } = await runWriteSucceed({

130+

file,

131+

watchedFile: dep,

132+

});

133+134+

assert.strictEqual(stderr, '');

135+

assert.deepStrictEqual(stdout, [

136+

'dep v1',

137+

`Completed running ${inspect(file)}. Waiting for file changes before restarting...`,

138+

`Restarting ${inspect(file)}`,

139+

'dep v1',

140+

`Completed running ${inspect(file)}. Waiting for file changes before restarting...`,

141+

]);

142+

});

143+144+

it('should watch changes to nested worker dependencies - cjs', async () => {

145+

const subDep = path.join(dir, 'sub-dep.js');

146+

const dep = path.join(dir, 'dep.js');

147+

const worker = path.join(dir, 'worker.js');

148+149+

writeFileSync(subDep, `

150+

module.exports = 'sub-dep v1';

151+

`);

152+153+

writeFileSync(dep, `

154+

const subDep = require('./sub-dep.js');

155+

console.log(subDep);

156+

module.exports = 'dep v1';

157+

`);

158+159+

writeFileSync(worker, `

160+

const dep = require('./dep.js');

161+

`);

162+163+

const file = createTmpFile(`

164+

const { Worker } = require('node:worker_threads');

165+

const w = new Worker(${JSON.stringify(worker)});

166+

`, '.js', dir);

167+168+

const { stderr, stdout } = await runWriteSucceed({

169+

file,

170+

watchedFile: subDep,

171+

});

172+173+

assert.strictEqual(stderr, '');

174+

assert.deepStrictEqual(stdout, [

175+

'sub-dep v1',

176+

`Completed running ${inspect(file)}. Waiting for file changes before restarting...`,

177+

`Restarting ${inspect(file)}`,

178+

'sub-dep v1',

179+

`Completed running ${inspect(file)}. Waiting for file changes before restarting...`,

180+

]);

181+

});

182+183+

it('should watch changes to worker - esm', async () => {

184+

const worker = path.join(dir, 'worker.mjs');

185+186+

writeFileSync(worker, `

187+

console.log('worker running');

188+

`);

189+190+

const file = createTmpFile(`

191+

import { Worker } from 'node:worker_threads';

192+

new Worker(new URL(${JSON.stringify(pathToFileURL(worker))}));

193+

`, '.mjs', dir);

194+195+

const { stderr, stdout } = await runWriteSucceed({

196+

file,

197+

watchedFile: worker,

198+

});

199+200+

assert.strictEqual(stderr, '');

201+

assert.deepStrictEqual(stdout, [

202+

'worker running',

203+

`Completed running ${inspect(file)}. Waiting for file changes before restarting...`,

204+

`Restarting ${inspect(file)}`,

205+

'worker running',

206+

`Completed running ${inspect(file)}. Waiting for file changes before restarting...`,

207+

]);

208+

});

209+210+

it('should watch changes to worker dependencies - esm', async () => {

211+

const dep = path.join(dir, 'dep.mjs');

212+

const worker = path.join(dir, 'worker.mjs');

213+214+

writeFileSync(dep, `

215+

export default 'dep v1';

216+

`);

217+218+

writeFileSync(worker, `

219+

import dep from ${JSON.stringify(pathToFileURL(dep))};

220+

console.log(dep);

221+

`);

222+223+

const file = createTmpFile(`

224+

import { Worker } from 'node:worker_threads';

225+

new Worker(new URL(${JSON.stringify(pathToFileURL(worker))}));

226+

`, '.mjs', dir);

227+228+

const { stderr, stdout } = await runWriteSucceed({

229+

file,

230+

watchedFile: dep,

231+

});

232+233+

assert.strictEqual(stderr, '');

234+

assert.deepStrictEqual(stdout, [

235+

'dep v1',

236+

`Completed running ${inspect(file)}. Waiting for file changes before restarting...`,

237+

`Restarting ${inspect(file)}`,

238+

'dep v1',

239+

`Completed running ${inspect(file)}. Waiting for file changes before restarting...`,

240+

]);

241+

});

242+243+

it('should watch changes to nested worker dependencies - esm', async () => {

244+

const subDep = path.join(dir, 'sub-dep.mjs');

245+

const dep = path.join(dir, 'dep.mjs');

246+

const worker = path.join(dir, 'worker.mjs');

247+248+

writeFileSync(subDep, `

249+

export default 'sub-dep v1';

250+

`);

251+252+

writeFileSync(dep, `

253+

import subDep from ${JSON.stringify(pathToFileURL(subDep))};

254+

console.log(subDep);

255+

export default 'dep v1';

256+

`);

257+258+

writeFileSync(worker, `

259+

import dep from ${JSON.stringify(pathToFileURL(dep))};

260+

`);

261+262+

const file = createTmpFile(`

263+

import { Worker } from 'node:worker_threads';

264+

new Worker(new URL(${JSON.stringify(pathToFileURL(worker))}));

265+

`, '.mjs', dir);

266+267+

const { stderr, stdout } = await runWriteSucceed({

268+

file,

269+

watchedFile: subDep,

270+

});

271+272+

assert.strictEqual(stderr, '');

273+

assert.deepStrictEqual(stdout, [

274+

'sub-dep v1',

275+

`Completed running ${inspect(file)}. Waiting for file changes before restarting...`,

276+

`Restarting ${inspect(file)}`,

277+

'sub-dep v1',

278+

`Completed running ${inspect(file)}. Waiting for file changes before restarting...`,

279+

]);

280+

});

281+

});