From 74f0d5de6352ae043b3a0bb17f7e705e3fc9993e Mon Sep 17 00:00:00 2001 From: Julien Ripouteau Date: Sat, 13 Dec 2025 21:00:45 +0100 Subject: [PATCH 1/6] fix: move watcher to assembler and send IPC messages for file changes --- package.json | 2 +- src/dev_server.ts | 132 ++++++++++++++++++++++++--------------- tests/dev_server.spec.ts | 67 ++++++++++++++++++++ 3 files changed, 150 insertions(+), 51 deletions(-) diff --git a/package.json b/package.json index 7f3d35a9..e423bc25 100644 --- a/package.json +++ b/package.json @@ -53,7 +53,7 @@ "cross-env": "^10.1.0", "del-cli": "^7.0.0", "eslint": "^9.37.0", - "hot-hook": "^0.4.1-next.0", + "hot-hook": "^0.4.1-next.2", "p-event": "^7.0.0", "prettier": "^3.6.2", "release-it": "^19.0.5", diff --git a/src/dev_server.ts b/src/dev_server.ts index 92b6bd4c..3e0e5eb6 100644 --- a/src/dev_server.ts +++ b/src/dev_server.ts @@ -119,6 +119,11 @@ export class DevServer { */ #httpServer?: ResultPromise + /** + * Flag to track if the HTTP server child process is alive + */ + #isHttpServerAlive = false + /** * Keyboard shortcuts manager instance */ @@ -348,6 +353,64 @@ export class DevServer { } } + /** + * Creates our file system watcher + */ + #createWatcher(options?: { poll?: boolean }) { + const watcher = watch({ + usePolling: options?.poll ?? false, + cwd: this.cwdPath, + ignoreInitial: true, + ignored: (file, stats) => { + if (!stats) return false + if (file.includes('inertia') && !file.includes('node_modules')) return false + if (stats.isFile()) return !this.#fileSystem.shouldWatchFile(file) + + return !this.#fileSystem.shouldWatchDirectory(file) + }, + }) + + watcher.on('error', (error: any) => { + this.ui.logger.warning('file system watcher failure') + this.ui.logger.fatal(error as any) + + this.#onError?.(error) + this.#watcher?.close() + }) + + watcher.on('ready', () => { + this.ui.logger.info('watching file system for changes...') + }) + + return watcher + } + + /** + * Handles file change events in HMR mode by forwarding to hot-hook + * or restarting the server if dead. + */ + #handleHmrWatcherEvent(options: { + filePath: string + action: 'add' | 'change' | 'unlink' + displayLabel: 'add' | 'update' | 'delete' + }) { + const relativePath = string.toUnixSlash(options.filePath) + + if (this.#isHttpServerAlive === false) { + this.#clearScreen() + this.ui.logger.log(`${this.ui.colors.green(options.displayLabel)} ${relativePath}`) + this.#restartHTTPServer() + return + } + + const absolutePath = join(this.cwdPath, relativePath) + this.#httpServer?.send({ + type: 'hot-hook:file-changed', + path: absolutePath, + action: options.action, + }) + } + /** * Handles file change events and triggers appropriate server actions * @@ -627,7 +690,7 @@ export class DevServer { await this.#hooks.runner('devServerStarting').run(this) debug('starting http server using "%s" file, options %O', this.scriptFile, this.options) - return new Promise(async (resolve) => { + return new Promise((resolve) => { /** * Creating child process */ @@ -638,6 +701,7 @@ export class DevServer { reject: true, scriptArgs: this.options.scriptArgs, }) + this.#isHttpServerAlive = true this.#httpServer.on('message', async (message) => { if (this.#isAdonisJSReadyMessage(message)) { @@ -684,6 +748,7 @@ export class DevServer { this.#httpServer .then((result) => { + this.#isHttpServerAlive = false if (!this.#watcher) { this.#onClose?.(result.exitCode!) } else { @@ -691,6 +756,7 @@ export class DevServer { } }) .catch((error) => { + this.#isHttpServerAlive = false if (!this.#watcher) { this.#onError?.(error) } else { @@ -783,19 +849,25 @@ export class DevServer { this.options.nodeArgs.push('--import=hot-hook/register') this.options.env = { ...this.options.env, - HOT_HOOK_INCLUDE: this.#fileSystem.includes.join(','), - HOT_HOOK_IGNORE: this.#fileSystem.excludes - .filter((exclude) => !exclude.includes('inertia')) - .join(','), - HOT_HOOK_RESTART: (this.options.metaFiles ?? []) - .filter(({ reloadServer }) => !!reloadServer) - .map(({ pattern }) => pattern) - .join(','), + HOT_HOOK_WATCH: 'false', } } this.ui.logger.info('starting HTTP server...') await this.#startHTTPServer(this.#stickyPort) + + if (this.#mode !== 'hmr') return + + this.#watcher = this.#createWatcher() + this.#watcher.on('add', (filePath) => { + this.#handleHmrWatcherEvent({ filePath, action: 'add', displayLabel: 'add' }) + }) + this.#watcher.on('change', (filePath) => { + this.#handleHmrWatcherEvent({ filePath, action: 'change', displayLabel: 'update' }) + }) + this.#watcher.on('unlink', (filePath) => { + this.#handleHmrWatcherEvent({ filePath, action: 'unlink', displayLabel: 'delete' }) + }) } /** @@ -822,47 +894,7 @@ export class DevServer { this.ui.logger.info('starting HTTP server...') await this.#startHTTPServer(this.#stickyPort) - /** - * Create watcher - */ - this.#watcher = watch({ - usePolling: options?.poll ?? false, - cwd: this.cwdPath, - ignoreInitial: true, - ignored: (file, stats) => { - if (!stats) { - return false - } - - if (file.includes('inertia') && !file.includes('node_modules')) { - return false - } - - if (stats.isFile()) { - return !this.#fileSystem.shouldWatchFile(file) - } - return !this.#fileSystem.shouldWatchDirectory(file) - }, - }) - - /** - * Notify the watcher is ready - */ - this.#watcher.on('ready', () => { - this.ui.logger.info('watching file system for changes...') - }) - - /** - * Cleanup when watcher dies - */ - this.#watcher.on('error', (error: any) => { - this.ui.logger.warning('file system watcher failure') - this.ui.logger.fatal(error as any) - - this.#onError?.(error) - this.#watcher?.close() - }) - + this.#watcher = this.#createWatcher({ poll: options?.poll }) this.#watcher.on('add', (filePath) => { const relativePath = string.toUnixSlash(filePath) const absolutePath = join(this.cwdPath, relativePath) diff --git a/tests/dev_server.spec.ts b/tests/dev_server.spec.ts index accd6f1f..ac3535c9 100644 --- a/tests/dev_server.spec.ts +++ b/tests/dev_server.spec.ts @@ -539,6 +539,73 @@ test.group('DevServer', () => { assert.lengthOf(indexGenerationLogs, 3) }).timeout(10 * 1000) + test('restart server on file change when child process has crashed in hmr mode', async ({ + fs, + assert, + cleanup, + }) => { + /** + * Create a server that crashes immediately after sending ready message + */ + await fs.createJson('tsconfig.json', { include: ['**/*'], exclude: [] }) + await fs.create( + 'bin/server.ts', + ` + process.send({ isAdonisJS: true, environment: 'web', port: process.env.PORT, host: 'localhost' }) + setTimeout(() => { throw new Error('crash') }, 100) + ` + ) + await fs.create('app/controllers/home_controller.ts', 'export default class {}') + await fs.create('.env', 'PORT=3350') + + const devServer = new DevServer(fs.baseUrl, { + hmr: true, + nodeArgs: [], + scriptArgs: [], + clearScreen: false, + }) + + devServer.ui = cliui() + devServer.ui.switchMode('raw') + + await devServer.start() + cleanup(() => devServer.close()) + + /** + * Wait for the server to crash + */ + await sleep(500) + + /** + * Modify a file, should trigger a restart since the child is dead + */ + await fs.create('app/controllers/home_controller.ts', 'export default class { foo() {} }') + + await sleep(1000) + + const logMessages = devServer.ui.logger.getLogs().map(({ message }) => message) + console.log(logMessages) + + assert.snapshot(logMessages).matchInline(` + [ + "[ blue(info) ] starting server in hmr mode...", + "[ blue(info) ] loading hooks...", + "[ blue(info) ] generating indexes...", + "[ blue(info) ] starting HTTP server...", + "Server address: cyan(http://localhost:3350) + Mode: cyan(hmr) + Press dim(h) to show help", + "[ blue(info) ] watching file system for changes...", + "[ blue(info) ] Underlying HTTP server died. Still watching for changes", + "green(update) app/controllers/home_controller.ts", + "Server address: cyan(http://localhost:3350) + Mode: cyan(hmr) + Press dim(h) to show help", + "[ blue(info) ] Underlying HTTP server died. Still watching for changes", + ] + `) + }).timeout(10 * 1000) + test('define hooks as inline functions', async ({ fs, assert, cleanup }) => { let hooksStack: string[] = [] From 3546f045b9295da9a722f092da7147387b8c29a3 Mon Sep 17 00:00:00 2001 From: Julien Ripouteau Date: Sat, 13 Dec 2025 21:24:17 +0100 Subject: [PATCH 2/6] test: attempt to fix flaky tests --- tests/dev_server.spec.ts | 49 +++++++++++++++++++--------------------- tests/helpers.ts | 29 ++++++++++++++++++++++++ 2 files changed, 52 insertions(+), 26 deletions(-) diff --git a/tests/dev_server.spec.ts b/tests/dev_server.spec.ts index ac3535c9..d902eefa 100644 --- a/tests/dev_server.spec.ts +++ b/tests/dev_server.spec.ts @@ -14,7 +14,7 @@ import { setTimeout as sleep } from 'node:timers/promises' import { RuntimeException } from '@poppinss/utils/exception' import { DevServer } from '../index.ts' -import { normalizePathForWindows, setupFakeAdonisproject } from './helpers.ts' +import { normalizePathForWindows, setupFakeAdonisproject, waitForLogsCount } from './helpers.ts' test.group('DevServer', () => { test('start in static mode and execute hooks', async ({ fs, assert, cleanup }) => { @@ -443,33 +443,31 @@ test.group('DevServer', () => { await devServer.start() cleanup(() => devServer.close()) + const indexFilter = ({ message }: { message: string }) => + message.includes('.adonisjs/server/controllers.ts') + assert.snapshot(await fs.contents('.adonisjs/server/controllers.ts')).matchInline(` "export const controllers = {} " `) - await sleep(1000) + await waitForLogsCount({ devServer, filter: indexFilter, count: 1 }) + await fs.create('app/controllers/users_controller.ts', 'foo') + await waitForLogsCount({ devServer, filter: indexFilter, count: 2 }) - await sleep(1000) await fs.create('app/controllers/posts_controller.ts', 'bar') + await waitForLogsCount({ devServer, filter: indexFilter, count: 3 }) - await sleep(1000) await fs.create('app/models/user.ts', 'bar') - - await sleep(1000) await fs.create('app/models/post.ts', 'bar') - await sleep(1000) - const logs = devServer.ui.logger.getLogs() - - console.log(logs) + await sleep(500) - const indexGenerationLogs = logs.filter(({ message }) => - message.includes('.adonisjs/server/controllers.ts') - ) + const logs = devServer.ui.logger.getLogs() + const indexGenerationLogs = logs.filter(indexFilter) assert.lengthOf(indexGenerationLogs, 3) - }).timeout(10 * 1000) + }).timeout(15 * 1000) test('regenerate index file on related file changes in watch mode', async ({ fs, @@ -512,32 +510,31 @@ test.group('DevServer', () => { await devServer.startAndWatch() cleanup(() => devServer.close()) + const indexFilter = ({ message }: { message: string }) => + message.includes('.adonisjs/server/controllers.ts') + assert.snapshot(await fs.contents('.adonisjs/server/controllers.ts')).matchInline(` "export const controllers = {} " `) - await sleep(1000) + await waitForLogsCount({ devServer, filter: indexFilter, count: 1 }) + await fs.create('app/controllers/users_controller.ts', 'foo') + await waitForLogsCount({ devServer, filter: indexFilter, count: 2 }) - await sleep(1000) await fs.create('app/controllers/posts_controller.ts', 'bar') + await waitForLogsCount({ devServer, filter: indexFilter, count: 3 }) - await sleep(1000) await fs.create('app/models/user.ts', 'bar') - - await sleep(1000) await fs.create('app/models/post.ts', 'bar') - await sleep(1000) - const logs = devServer.ui.logger.getLogs() + await sleep(500) - console.log(logs) - const indexGenerationLogs = logs.filter(({ message }) => - message.includes('.adonisjs/server/controllers.ts') - ) + const logs = devServer.ui.logger.getLogs() + const indexGenerationLogs = logs.filter(indexFilter) assert.lengthOf(indexGenerationLogs, 3) - }).timeout(10 * 1000) + }).timeout(15 * 1000) test('restart server on file change when child process has crashed in hmr mode', async ({ fs, diff --git a/tests/helpers.ts b/tests/helpers.ts index 00c98309..be3223c3 100644 --- a/tests/helpers.ts +++ b/tests/helpers.ts @@ -9,9 +9,12 @@ import { sep } from 'node:path' import { platform } from 'node:os' +import { type DevServer } from '../src/dev_server.ts' + import { test } from '@japa/runner' import { readFile } from 'node:fs/promises' import { type FileSystem } from '@japa/file-system' +import { setTimeout as sleep } from 'node:timers/promises' export async function setupFakeAdonisproject(fs: FileSystem) { await Promise.all([ @@ -60,3 +63,29 @@ export function normalizePathForWindows(filePath: string) { } return filePath } + +/** + * Wait until the logger has the expected number of logs matching the filter. + */ +export async function waitForLogsCount(options: { + devServer: DevServer + filter: (log: { message: string }) => boolean + count: number + timeout?: number +}): Promise { + const { devServer, filter, count, timeout = 5000 } = options + const start = Date.now() + + while (Date.now() - start < timeout) { + const logs = devServer.ui.logger.getLogs() + const matchingLogs = logs.filter(filter) + if (matchingLogs.length >= count) return + await sleep(50) + } + + const logs = devServer.ui.logger.getLogs() + const matchingLogs = logs.filter(filter) + throw new Error( + `Timeout waiting for ${count} logs (got ${matchingLogs.length}). Logs: ${JSON.stringify(logs)}` + ) +} From a4f8dd56c6658557b996d002a0d2903d8325f785 Mon Sep 17 00:00:00 2001 From: Julien Ripouteau Date: Sat, 13 Dec 2025 21:39:04 +0100 Subject: [PATCH 3/6] fix --- tests/dev_server.spec.ts | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/tests/dev_server.spec.ts b/tests/dev_server.spec.ts index d902eefa..9db45993 100644 --- a/tests/dev_server.spec.ts +++ b/tests/dev_server.spec.ts @@ -445,13 +445,15 @@ test.group('DevServer', () => { const indexFilter = ({ message }: { message: string }) => message.includes('.adonisjs/server/controllers.ts') + const watcherReadyFilter = ({ message }: { message: string }) => + message.includes('watching file system for changes') assert.snapshot(await fs.contents('.adonisjs/server/controllers.ts')).matchInline(` "export const controllers = {} " `) - await waitForLogsCount({ devServer, filter: indexFilter, count: 1 }) + await waitForLogsCount({ devServer, filter: watcherReadyFilter, count: 1 }) await fs.create('app/controllers/users_controller.ts', 'foo') await waitForLogsCount({ devServer, filter: indexFilter, count: 2 }) @@ -512,13 +514,15 @@ test.group('DevServer', () => { const indexFilter = ({ message }: { message: string }) => message.includes('.adonisjs/server/controllers.ts') + const watcherReadyFilter = ({ message }: { message: string }) => + message.includes('watching file system for changes') assert.snapshot(await fs.contents('.adonisjs/server/controllers.ts')).matchInline(` "export const controllers = {} " `) - await waitForLogsCount({ devServer, filter: indexFilter, count: 1 }) + await waitForLogsCount({ devServer, filter: watcherReadyFilter, count: 1 }) await fs.create('app/controllers/users_controller.ts', 'foo') await waitForLogsCount({ devServer, filter: indexFilter, count: 2 }) From 5a39d69bc543790b517a420547a283dd30e31c72 Mon Sep 17 00:00:00 2001 From: Julien Ripouteau Date: Sat, 13 Dec 2025 22:01:02 +0100 Subject: [PATCH 4/6] fix --- src/dev_server.ts | 43 +++++++++++++++++++------------------------ 1 file changed, 19 insertions(+), 24 deletions(-) diff --git a/src/dev_server.ts b/src/dev_server.ts index 3e0e5eb6..8b4c5b1e 100644 --- a/src/dev_server.ts +++ b/src/dev_server.ts @@ -55,15 +55,6 @@ import { * await devServer.start(ts) */ export class DevServer { - /** - * Pre-allocated info object for hot-hook change events to avoid repeated object creation - */ - static readonly #HOT_HOOK_CHANGE_INFO = { - source: 'hot-hook' as const, - fullReload: false, - hotReloaded: false, - } - /** * Pre-allocated info object for hot-hook full reload events */ @@ -395,6 +386,7 @@ export class DevServer { displayLabel: 'add' | 'update' | 'delete' }) { const relativePath = string.toUnixSlash(options.filePath) + const absolutePath = join(this.cwdPath, relativePath) if (this.#isHttpServerAlive === false) { this.#clearScreen() @@ -403,7 +395,23 @@ export class DevServer { return } - const absolutePath = join(this.cwdPath, relativePath) + /** + * For add/unlink, we call the hooks directly since hot-hook ignores files + * not in its dependency tree. This ensures index files are regenerated + * for new/removed files. + */ + if (options.action === 'add') { + this.#hooks.runner('fileAdded').run(relativePath, absolutePath, this) + } else if (options.action === 'unlink') { + this.#hooks.runner('fileRemoved').run(relativePath, absolutePath, this) + } + + /** + * Forward all events to hot-hook so it can: + * - Update its dependency tree (for unlink) + * - Handle HMR for change events on imported files + * - Then we wait for hot-hook to notify us back via IPC message + */ this.#httpServer?.send({ type: 'hot-hook:file-changed', path: absolutePath, @@ -715,20 +723,7 @@ export class DevServer { } else if (this.#mode === 'hmr' && this.#isHotHookMessage(message)) { debug('received hot-hook message %O', message) - if (message.type === 'hot-hook:file-changed') { - const absolutePath = message.path ? string.toUnixSlash(message.path) : '' - const relativePath = relative(this.cwdPath, absolutePath) - - if (message.action === 'add') { - this.#hooks.runner('fileAdded').run(relativePath, absolutePath, this) - } else if (message.action === 'change') { - this.#hooks - .runner('fileChanged') - .run(relativePath, absolutePath, DevServer.#HOT_HOOK_CHANGE_INFO, this) - } else if (message.action === 'unlink') { - this.#hooks.runner('fileRemoved').run(relativePath, absolutePath, this) - } - } else if (message.type === 'hot-hook:full-reload') { + if (message.type === 'hot-hook:full-reload') { const absolutePath = message.path ? string.toUnixSlash(message.path) : '' const relativePath = relative(this.cwdPath, absolutePath) From 09993ccf8540c2867f94bc43df36a4a96acc4a88 Mon Sep 17 00:00:00 2001 From: Julien Ripouteau Date: Sat, 13 Dec 2025 22:04:00 +0100 Subject: [PATCH 5/6] Revert "fix" This reverts commit a4f8dd56c6658557b996d002a0d2903d8325f785. --- tests/dev_server.spec.ts | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/tests/dev_server.spec.ts b/tests/dev_server.spec.ts index 9db45993..d902eefa 100644 --- a/tests/dev_server.spec.ts +++ b/tests/dev_server.spec.ts @@ -445,15 +445,13 @@ test.group('DevServer', () => { const indexFilter = ({ message }: { message: string }) => message.includes('.adonisjs/server/controllers.ts') - const watcherReadyFilter = ({ message }: { message: string }) => - message.includes('watching file system for changes') assert.snapshot(await fs.contents('.adonisjs/server/controllers.ts')).matchInline(` "export const controllers = {} " `) - await waitForLogsCount({ devServer, filter: watcherReadyFilter, count: 1 }) + await waitForLogsCount({ devServer, filter: indexFilter, count: 1 }) await fs.create('app/controllers/users_controller.ts', 'foo') await waitForLogsCount({ devServer, filter: indexFilter, count: 2 }) @@ -514,15 +512,13 @@ test.group('DevServer', () => { const indexFilter = ({ message }: { message: string }) => message.includes('.adonisjs/server/controllers.ts') - const watcherReadyFilter = ({ message }: { message: string }) => - message.includes('watching file system for changes') assert.snapshot(await fs.contents('.adonisjs/server/controllers.ts')).matchInline(` "export const controllers = {} " `) - await waitForLogsCount({ devServer, filter: watcherReadyFilter, count: 1 }) + await waitForLogsCount({ devServer, filter: indexFilter, count: 1 }) await fs.create('app/controllers/users_controller.ts', 'foo') await waitForLogsCount({ devServer, filter: indexFilter, count: 2 }) From 9906261f2f1aedad4dfb751b9ebd70e3c7bc7b8d Mon Sep 17 00:00:00 2001 From: Julien Ripouteau Date: Sat, 13 Dec 2025 22:04:01 +0100 Subject: [PATCH 6/6] Revert "test: attempt to fix flaky tests" This reverts commit 3546f045b9295da9a722f092da7147387b8c29a3. --- tests/dev_server.spec.ts | 49 +++++++++++++++++++++------------------- tests/helpers.ts | 29 ------------------------ 2 files changed, 26 insertions(+), 52 deletions(-) diff --git a/tests/dev_server.spec.ts b/tests/dev_server.spec.ts index d902eefa..ac3535c9 100644 --- a/tests/dev_server.spec.ts +++ b/tests/dev_server.spec.ts @@ -14,7 +14,7 @@ import { setTimeout as sleep } from 'node:timers/promises' import { RuntimeException } from '@poppinss/utils/exception' import { DevServer } from '../index.ts' -import { normalizePathForWindows, setupFakeAdonisproject, waitForLogsCount } from './helpers.ts' +import { normalizePathForWindows, setupFakeAdonisproject } from './helpers.ts' test.group('DevServer', () => { test('start in static mode and execute hooks', async ({ fs, assert, cleanup }) => { @@ -443,31 +443,33 @@ test.group('DevServer', () => { await devServer.start() cleanup(() => devServer.close()) - const indexFilter = ({ message }: { message: string }) => - message.includes('.adonisjs/server/controllers.ts') - assert.snapshot(await fs.contents('.adonisjs/server/controllers.ts')).matchInline(` "export const controllers = {} " `) - await waitForLogsCount({ devServer, filter: indexFilter, count: 1 }) - + await sleep(1000) await fs.create('app/controllers/users_controller.ts', 'foo') - await waitForLogsCount({ devServer, filter: indexFilter, count: 2 }) + await sleep(1000) await fs.create('app/controllers/posts_controller.ts', 'bar') - await waitForLogsCount({ devServer, filter: indexFilter, count: 3 }) + await sleep(1000) await fs.create('app/models/user.ts', 'bar') - await fs.create('app/models/post.ts', 'bar') - await sleep(500) + await sleep(1000) + await fs.create('app/models/post.ts', 'bar') + await sleep(1000) const logs = devServer.ui.logger.getLogs() - const indexGenerationLogs = logs.filter(indexFilter) + + console.log(logs) + + const indexGenerationLogs = logs.filter(({ message }) => + message.includes('.adonisjs/server/controllers.ts') + ) assert.lengthOf(indexGenerationLogs, 3) - }).timeout(15 * 1000) + }).timeout(10 * 1000) test('regenerate index file on related file changes in watch mode', async ({ fs, @@ -510,31 +512,32 @@ test.group('DevServer', () => { await devServer.startAndWatch() cleanup(() => devServer.close()) - const indexFilter = ({ message }: { message: string }) => - message.includes('.adonisjs/server/controllers.ts') - assert.snapshot(await fs.contents('.adonisjs/server/controllers.ts')).matchInline(` "export const controllers = {} " `) - await waitForLogsCount({ devServer, filter: indexFilter, count: 1 }) - + await sleep(1000) await fs.create('app/controllers/users_controller.ts', 'foo') - await waitForLogsCount({ devServer, filter: indexFilter, count: 2 }) + await sleep(1000) await fs.create('app/controllers/posts_controller.ts', 'bar') - await waitForLogsCount({ devServer, filter: indexFilter, count: 3 }) + await sleep(1000) await fs.create('app/models/user.ts', 'bar') - await fs.create('app/models/post.ts', 'bar') - await sleep(500) + await sleep(1000) + await fs.create('app/models/post.ts', 'bar') + await sleep(1000) const logs = devServer.ui.logger.getLogs() - const indexGenerationLogs = logs.filter(indexFilter) + + console.log(logs) + const indexGenerationLogs = logs.filter(({ message }) => + message.includes('.adonisjs/server/controllers.ts') + ) assert.lengthOf(indexGenerationLogs, 3) - }).timeout(15 * 1000) + }).timeout(10 * 1000) test('restart server on file change when child process has crashed in hmr mode', async ({ fs, diff --git a/tests/helpers.ts b/tests/helpers.ts index be3223c3..00c98309 100644 --- a/tests/helpers.ts +++ b/tests/helpers.ts @@ -9,12 +9,9 @@ import { sep } from 'node:path' import { platform } from 'node:os' -import { type DevServer } from '../src/dev_server.ts' - import { test } from '@japa/runner' import { readFile } from 'node:fs/promises' import { type FileSystem } from '@japa/file-system' -import { setTimeout as sleep } from 'node:timers/promises' export async function setupFakeAdonisproject(fs: FileSystem) { await Promise.all([ @@ -63,29 +60,3 @@ export function normalizePathForWindows(filePath: string) { } return filePath } - -/** - * Wait until the logger has the expected number of logs matching the filter. - */ -export async function waitForLogsCount(options: { - devServer: DevServer - filter: (log: { message: string }) => boolean - count: number - timeout?: number -}): Promise { - const { devServer, filter, count, timeout = 5000 } = options - const start = Date.now() - - while (Date.now() - start < timeout) { - const logs = devServer.ui.logger.getLogs() - const matchingLogs = logs.filter(filter) - if (matchingLogs.length >= count) return - await sleep(50) - } - - const logs = devServer.ui.logger.getLogs() - const matchingLogs = logs.filter(filter) - throw new Error( - `Timeout waiting for ${count} logs (got ${matchingLogs.length}). Logs: ${JSON.stringify(logs)}` - ) -}