diff --git a/test/dev.ts b/test/dev.ts index 550fa96ee..e682988d7 100644 --- a/test/dev.ts +++ b/test/dev.ts @@ -9,6 +9,7 @@ import { loadEnv } from 'payload/node' import { getNextRootDir } from './helpers/getNextRootDir.js' import { runInit } from './runInit.js' +import { safelyRunScriptFunction } from './safelyRunScript.js' import { createTestHooks } from './testHooks.js' const prod = process.argv.includes('--prod') @@ -43,7 +44,7 @@ await beforeTest() const { rootDir, adminRoute } = getNextRootDir(testSuiteArg) -await runInit(testSuiteArg, true) +await safelyRunScriptFunction(runInit, 4000, testSuiteArg) // Open the admin if the -o flag is passed if (args.o) { diff --git a/test/safelyRunScript.ts b/test/safelyRunScript.ts new file mode 100644 index 000000000..94c579fab --- /dev/null +++ b/test/safelyRunScript.ts @@ -0,0 +1,50 @@ +import { spawn } from 'child_process' +import path from 'path' + +/** + * Sometimes, running certain functions in certain scripts from the command line will cause the script to be terminated + * with a "Detected unsettled top-level await" error. This often happens if that function imports the payload config. + * It seems to be a bug in Node.js and I do not know how to properly fix it. As a workaround, this script automatically re-runs + * the script if said function is not resolved within a certain time frame, and prevents the "Detected unsettled top-level await" error. + */ +export async function safelyRunScriptFunction( + functionToRun: any, + timeout: number = 4000, + ...args: any[] +): Promise { + return new Promise((resolve, reject) => { + const timeoutId = setTimeout(() => { + restartProcess(`runInit timed out after ${timeout / 1000} seconds`) + }, timeout) + + functionToRun(...args) + .then(() => { + clearTimeout(timeoutId) + resolve() + }) + .catch((error) => { + clearTimeout(timeoutId) + restartProcess(`runInit failed: ${error.message}`) + }) + }) +} + +function restartProcess(reason: string): never { + console.warn(`Restarting process: ${reason}`) + + // Get the path to the current script + const scriptPath = process.argv[1] + const absoluteScriptPath = path.resolve(scriptPath) + + // Spawn a new process + const child = spawn('tsx', [absoluteScriptPath, ...process.argv.slice(2)], { + stdio: 'inherit', + detached: true, + }) + + // Unref the child process so the parent can exit + child.unref() + + // Exit the current process + process.exit(0) +}