From b9d0514b4850eca2670ebfd5c3aa57d6171c7ca9 Mon Sep 17 00:00:00 2001 From: Bill Glesias Date: Thu, 17 Jul 2025 13:43:23 -0400 Subject: [PATCH 1/6] convert run_require_async_child to TypeScript --- .../lib/plugins/child/require_async_child.js | 2 +- ...nc_child.js => run_require_async_child.ts} | 80 ++++++++++++------- .../child/run_require_async_child_spec.js | 2 +- 3 files changed, 52 insertions(+), 32 deletions(-) rename packages/server/lib/plugins/child/{run_require_async_child.js => run_require_async_child.ts} (71%) diff --git a/packages/server/lib/plugins/child/require_async_child.js b/packages/server/lib/plugins/child/require_async_child.js index 7e15208fc522..d89b6500fc7d 100644 --- a/packages/server/lib/plugins/child/require_async_child.js +++ b/packages/server/lib/plugins/child/require_async_child.js @@ -23,7 +23,7 @@ process.on('disconnect', () => { require('graceful-fs').gracefulify(require('fs')) const util = require('../util') const ipc = util.wrapIpc(process) -const run = require('./run_require_async_child') +const run = require('./run_require_async_child').default exporter.attachIPC(ipc) diff --git a/packages/server/lib/plugins/child/run_require_async_child.js b/packages/server/lib/plugins/child/run_require_async_child.ts similarity index 71% rename from packages/server/lib/plugins/child/run_require_async_child.js rename to packages/server/lib/plugins/child/run_require_async_child.ts index 1f2bdfb28899..a970f19c227b 100644 --- a/packages/server/lib/plugins/child/run_require_async_child.js +++ b/packages/server/lib/plugins/child/run_require_async_child.ts @@ -1,11 +1,19 @@ -require('graceful-fs').gracefulify(require('fs')) -const debugLib = require('debug') -const { pathToFileURL } = require('url') -const util = require('../util') -const { RunPlugins } = require('./run_plugins') +import gracefulFs from 'graceful-fs' +import fs from 'fs' +import debugLib from 'debug' +import { pathToFileURL } from 'url' +import * as util from '../util' +import { RunPlugins } from './run_plugins' + +gracefulFs.gracefulify(fs) const debug = debugLib(`cypress:lifecycle:child:run_require_async_child:${process.pid}`) +interface IPC { + send: (event: string, data?: any) => void + on: (event: string, callback: (...args: any[]) => void) => void +} + /** * Executes and returns the passed `file` (usually `configFile`) file in the ipc `loadConfig` event * @param {*} ipc Inter Process Communication protocol @@ -13,7 +21,7 @@ const debug = debugLib(`cypress:lifecycle:child:run_require_async_child:${proces * @param {*} projectRoot the root of the typescript project (useful mainly for tsnode) * @returns */ -function run (ipc, file, projectRoot) { +function run (ipc: IPC, file: string, projectRoot: string) { debug('configFile:', file) debug('projectRoot:', projectRoot) if (!projectRoot) { @@ -27,7 +35,7 @@ function run (ipc, file, projectRoot) { return false }) - process.on('unhandledRejection', (event) => { + process.on('unhandledRejection', (event: any) => { let err = event debug('unhandled rejection:', event) @@ -43,10 +51,12 @@ function run (ipc, file, projectRoot) { return false }) - const isValidSetupNodeEvents = (config, testingType) => { + const isValidSetupNodeEvents = async (config: any, testingType: string) => { if (config[testingType] && config[testingType].setupNodeEvents && typeof config[testingType].setupNodeEvents !== 'function') { + const errors = await import('@packages/errors') + ipc.send('setupTestingType:error', util.serializeError( - require('@packages/errors').getError('SETUP_NODE_EVENTS_IS_NOT_FUNCTION', file, testingType, config[testingType].setupNodeEvents), + errors.getError('SETUP_NODE_EVENTS_IS_NOT_FUNCTION', file, testingType, config[testingType].setupNodeEvents), )) return false @@ -55,7 +65,7 @@ function run (ipc, file, projectRoot) { return true } - const getValidDevServer = (config) => { + const getValidDevServer = async (config: any) => { const { devServer } = config if (devServer && typeof devServer === 'function') { @@ -64,17 +74,23 @@ function run (ipc, file, projectRoot) { if (devServer && typeof devServer === 'object') { if (devServer.bundler === 'webpack') { - return { devServer: require('@cypress/webpack-dev-server').devServer, objApi: true } + const { devServer: webpackDevServer } = await import('@cypress/webpack-dev-server') + + return { devServer: webpackDevServer, objApi: true } } if (devServer.bundler === 'vite') { // tsx magic allows this import to work even though its a ESM module and we are in a CJS context - return { devServer: require('@cypress/vite-dev-server').devServer, objApi: true } + const { devServer: viteDevServer } = await import('@cypress/vite-dev-server') + + return { devServer: viteDevServer, objApi: true } } } + const errors = await import('@packages/errors') + ipc.send('setupTestingType:error', util.serializeError( - require('@packages/errors').getError('CONFIG_FILE_DEV_SERVER_IS_NOT_VALID', file, config), + errors.getError('CONFIG_FILE_DEV_SERVER_IS_NOT_VALID', file, config), )) return false @@ -82,12 +98,12 @@ function run (ipc, file, projectRoot) { // Config file loading of modules is tested within // system-tests/projects/config-cjs-and-esm/* - const loadFile = async (file) => { + const loadFile = async (file: string) => { try { debug('Loading file %s', file) return require(file) - } catch (err) { + } catch (err: any) { if (!err.stack.includes('[ERR_REQUIRE_ESM]') && !err.stack.includes('SyntaxError: Cannot use import statement outside a module')) { throw err } @@ -105,7 +121,7 @@ function run (ipc, file, projectRoot) { return await import(fileURL) } catch (err) { - debug('error loading file via native Node.js module loader %s', err.message) + debug('error loading file via native Node.js module loader %s', (err as Error).message) throw err } } @@ -118,7 +134,7 @@ function run (ipc, file, projectRoot) { debug('loaded config file', file) const result = configFileExport.default || configFileExport - const replacer = (_key, val) => { + const replacer = (_key: string, val: any) => { return typeof val === 'function' ? `[Function ${val.name}]` : val } @@ -126,7 +142,7 @@ function run (ipc, file, projectRoot) { let hasSetup = false - ipc.on('setupTestingType', (testingType, options) => { + ipc.on('setupTestingType', async (testingType: string, options: any) => { if (hasSetup) { throw new Error('Already Setup') } @@ -137,12 +153,12 @@ function run (ipc, file, projectRoot) { const runPlugins = new RunPlugins(ipc, projectRoot, file) - if (!isValidSetupNodeEvents(result, testingType)) { + if (!(await isValidSetupNodeEvents(result, testingType))) { return } if (testingType === 'component') { - const devServerInfo = getValidDevServer(result.component || {}) + const devServerInfo = await getValidDevServer(result.component || {}) if (!devServerInfo) { return @@ -150,16 +166,18 @@ function run (ipc, file, projectRoot) { const { devServer, objApi } = devServerInfo - runPlugins.runSetupNodeEvents(options, (on, config) => { - const setupNodeEvents = result.component && result.component.setupNodeEvents || ((on, config) => {}) + runPlugins.runSetupNodeEvents(options, (on: any, config: any) => { + const setupNodeEvents = result.component && result.component.setupNodeEvents || ((on: any, config: any) => {}) + + const onConfigNotFound = async (devServer: any, root: string, searchedFor: any) => { + const errors = await import('@packages/errors') - const onConfigNotFound = (devServer, root, searchedFor) => { ipc.send('setupTestingType:error', util.serializeError( - require('@packages/errors').getError('DEV_SERVER_CONFIG_FILE_NOT_FOUND', devServer, root, searchedFor), + errors.getError('DEV_SERVER_CONFIG_FILE_NOT_FOUND', devServer, root, searchedFor), )) } - on('dev-server:start', (devServerOpts) => { + on('dev-server:start', (devServerOpts: any) => { if (objApi) { const { specs, devServerEvents } = devServerOpts @@ -180,7 +198,7 @@ function run (ipc, file, projectRoot) { return setupNodeEvents(on, config) }) } else if (testingType === 'e2e') { - const setupNodeEvents = result.e2e && result.e2e.setupNodeEvents || ((on, config) => {}) + const setupNodeEvents = result.e2e && result.e2e.setupNodeEvents || ((on: any, config: any) => {}) runPlugins.runSetupNodeEvents(options, setupNodeEvents) } else { @@ -192,7 +210,7 @@ function run (ipc, file, projectRoot) { }) debug('loaded config from %s %o', file, result) - } catch (err) { + } catch (err: any) { // With tsx, errors now come in as TransformErrors instead of TSErrors (as they also include JavaScript errors). if (err.name === 'TransformError' || err.stack.includes('TransformError')) { const { compilerErrorLocation, originalMessage, message } = util.buildErrorLocationFromTransformError(err, projectRoot) @@ -203,15 +221,17 @@ function run (ipc, file, projectRoot) { } else if (Array.isArray(err.errors)) { // The stack trace of the esbuild error, do not give to much information related with the user error, // we have the errors array which includes the users file and information related with the error - const firstError = err.errors.filter((e) => Boolean(e.location))[0] + const firstError = err.errors.filter((e: any) => Boolean(e.location))[0] if (firstError && firstError.location.file) { err.compilerErrorLocation = { filePath: firstError.location.file, line: Number(firstError.location.line), column: Number(firstError.location.column) } } } + const errors = await import('@packages/errors') + ipc.send('loadConfig:error', util.serializeError( - require('@packages/errors').getError('CONFIG_FILE_REQUIRE_ERROR', file, err), + errors.getError('CONFIG_FILE_REQUIRE_ERROR', file, err), )) } }) @@ -219,4 +239,4 @@ function run (ipc, file, projectRoot) { ipc.send('ready') } -module.exports = run +export default run diff --git a/packages/server/test/unit/plugins/child/run_require_async_child_spec.js b/packages/server/test/unit/plugins/child/run_require_async_child_spec.js index 9cfebc3a2750..a14a010f5ee3 100644 --- a/packages/server/test/unit/plugins/child/run_require_async_child_spec.js +++ b/packages/server/test/unit/plugins/child/run_require_async_child_spec.js @@ -1,6 +1,6 @@ require('../../../spec_helper') -const runRequireAsyncChild = require('../../../../lib/plugins/child/run_require_async_child') +const runRequireAsyncChild = require('../../../../lib/plugins/child/run_require_async_child').default describe('lib/plugins/child/run_require_async_child', () => { beforeEach(function () { From 8dfcb32c53bc4b4a281dd5bac57a3b878491bbde Mon Sep 17 00:00:00 2001 From: Bill Glesias Date: Thu, 17 Jul 2025 16:41:46 -0400 Subject: [PATCH 2/6] convert run_require_async_child tests to TypeScript --- ...ild_spec.js => run_require_async_child_spec.ts} | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) rename packages/server/test/unit/plugins/child/{run_require_async_child_spec.js => run_require_async_child_spec.ts} (76%) diff --git a/packages/server/test/unit/plugins/child/run_require_async_child_spec.js b/packages/server/test/unit/plugins/child/run_require_async_child_spec.ts similarity index 76% rename from packages/server/test/unit/plugins/child/run_require_async_child_spec.js rename to packages/server/test/unit/plugins/child/run_require_async_child_spec.ts index a14a010f5ee3..a9cea8b5c227 100644 --- a/packages/server/test/unit/plugins/child/run_require_async_child_spec.js +++ b/packages/server/test/unit/plugins/child/run_require_async_child_spec.ts @@ -1,6 +1,16 @@ -require('../../../spec_helper') +/* eslint-disable @typescript-eslint/no-this-alias, @typescript-eslint/unbound-method, mocha/no-synchronous-tests */ +import '../../../spec_helper' +import runRequireAsyncChild from '../../../../lib/plugins/child/run_require_async_child' -const runRequireAsyncChild = require('../../../../lib/plugins/child/run_require_async_child').default +// Global declarations for test environment +declare const describe: any +declare const beforeEach: any +declare const afterEach: any +declare const it: any +declare const expect: any +declare const sinon: any +declare const mockery: any +declare const process: any describe('lib/plugins/child/run_require_async_child', () => { beforeEach(function () { From 0aa1316e14dfc302fe04c221ae815acdddd12e68 Mon Sep 17 00:00:00 2001 From: Bill Glesias Date: Thu, 17 Jul 2025 16:45:19 -0400 Subject: [PATCH 3/6] convert run_async_child to TypeScript --- ...ire_async_child.js => require_async_child.ts} | 16 ++++++++++------ .../plugins/child/require_async_child_spec.js | 4 +++- .../test/unit/plugins/child/run_child_fixture.js | 2 +- 3 files changed, 14 insertions(+), 8 deletions(-) rename packages/server/lib/plugins/child/{require_async_child.js => require_async_child.ts} (60%) diff --git a/packages/server/lib/plugins/child/require_async_child.js b/packages/server/lib/plugins/child/require_async_child.ts similarity index 60% rename from packages/server/lib/plugins/child/require_async_child.js rename to packages/server/lib/plugins/child/require_async_child.ts index d89b6500fc7d..3e07ced9b61e 100644 --- a/packages/server/lib/plugins/child/require_async_child.js +++ b/packages/server/lib/plugins/child/require_async_child.ts @@ -1,8 +1,14 @@ process.title = 'Cypress: Config Manager' -const { telemetry, OTLPTraceExporterIpc, decodeTelemetryContext } = require('@packages/telemetry') +import { telemetry, OTLPTraceExporterIpc, decodeTelemetryContext } from '@packages/telemetry' +import minimist from 'minimist' +import { suppress } from '../../util/suppress_warnings' +import gracefulFs from 'graceful-fs' +import fs from 'fs' +import * as util from '../util' +import run from './run_require_async_child' -const { file, projectRoot, telemetryCtx } = require('minimist')(process.argv.slice(2)) +const { file, projectRoot, telemetryCtx } = minimist(process.argv.slice(2)) const { context, version } = decodeTelemetryContext(telemetryCtx) @@ -14,16 +20,14 @@ if (version && context) { const span = telemetry.startSpan({ name: 'child:process', active: true }) -require('../../util/suppress_warnings').suppress() +suppress() process.on('disconnect', () => { process.exit() }) -require('graceful-fs').gracefulify(require('fs')) -const util = require('../util') +gracefulFs.gracefulify(fs) const ipc = util.wrapIpc(process) -const run = require('./run_require_async_child').default exporter.attachIPC(ipc) diff --git a/packages/server/test/unit/plugins/child/require_async_child_spec.js b/packages/server/test/unit/plugins/child/require_async_child_spec.js index c50e2894ca10..d3a26aa2637c 100644 --- a/packages/server/test/unit/plugins/child/require_async_child_spec.js +++ b/packages/server/test/unit/plugins/child/require_async_child_spec.js @@ -5,7 +5,9 @@ const PROJECT_ROOT = path.join(path.dirname(require.resolve('@tooling/system-tes describe('require_async_child', () => { it('disconnects if the parent ipc is closed', (done) => { - const child = childProcess.fork(path.join(__dirname, 'run_child_fixture')) + const child = childProcess.fork(path.join(__dirname, 'run_child_fixture'), { + execArgv: ['--import', 'tsx'], + }) let childPid diff --git a/packages/server/test/unit/plugins/child/run_child_fixture.js b/packages/server/test/unit/plugins/child/run_child_fixture.js index 915a67c00925..ecaa1012cb1c 100644 --- a/packages/server/test/unit/plugins/child/run_child_fixture.js +++ b/packages/server/test/unit/plugins/child/run_child_fixture.js @@ -1,7 +1,7 @@ const childProcess = require('child_process') const path = require('path') -const REQUIRE_ASYNC_CHILD_PATH = require.resolve('@packages/server/lib/plugins/child/require_async_child') +const REQUIRE_ASYNC_CHILD_PATH = require.resolve('@packages/server/lib/plugins/child/require_async_child.ts') let proc From c23239bef8cc3f7a4d9247b4c8035b240b440828 Mon Sep 17 00:00:00 2001 From: Bill Glesias Date: Thu, 17 Jul 2025 14:27:44 -0400 Subject: [PATCH 4/6] convert run_async_child tests to typescript --- ..._child_spec.js => require_async_child_spec.ts} | 6 +++--- ...{run_child_fixture.js => run_child_fixture.ts} | 15 ++++++++------- 2 files changed, 11 insertions(+), 10 deletions(-) rename packages/server/test/unit/plugins/child/{require_async_child_spec.js => require_async_child_spec.ts} (93%) rename packages/server/test/unit/plugins/child/{run_child_fixture.js => run_child_fixture.ts} (60%) diff --git a/packages/server/test/unit/plugins/child/require_async_child_spec.js b/packages/server/test/unit/plugins/child/require_async_child_spec.ts similarity index 93% rename from packages/server/test/unit/plugins/child/require_async_child_spec.js rename to packages/server/test/unit/plugins/child/require_async_child_spec.ts index d3a26aa2637c..4a17794d36a5 100644 --- a/packages/server/test/unit/plugins/child/require_async_child_spec.js +++ b/packages/server/test/unit/plugins/child/require_async_child_spec.ts @@ -1,5 +1,5 @@ -const childProcess = require('child_process') -const path = require('path') +import childProcess from 'child_process' +import path from 'path' const PROJECT_ROOT = path.join(path.dirname(require.resolve('@tooling/system-tests/package.json')), 'projects/kill-child-process') @@ -11,7 +11,7 @@ describe('require_async_child', () => { let childPid - child.on('message', (msg) => { + child.on('message', (msg: any) => { if (msg.childPid) { childPid = msg.childPid child.send({ msg: 'toChild', data: { event: 'loadConfig', args: [] } }) diff --git a/packages/server/test/unit/plugins/child/run_child_fixture.js b/packages/server/test/unit/plugins/child/run_child_fixture.ts similarity index 60% rename from packages/server/test/unit/plugins/child/run_child_fixture.js rename to packages/server/test/unit/plugins/child/run_child_fixture.ts index ecaa1012cb1c..ec80f8525ecd 100644 --- a/packages/server/test/unit/plugins/child/run_child_fixture.js +++ b/packages/server/test/unit/plugins/child/run_child_fixture.ts @@ -1,18 +1,19 @@ -const childProcess = require('child_process') -const path = require('path') +import childProcess from 'child_process' +import type { ChildProcess } from 'child_process' +import path from 'path' const REQUIRE_ASYNC_CHILD_PATH = require.resolve('@packages/server/lib/plugins/child/require_async_child.ts') -let proc +let proc: ChildProcess -process.on('message', (msg) => { +process.on('message', (msg: any) => { if (msg.msg === 'spawn') { proc = childProcess.fork(REQUIRE_ASYNC_CHILD_PATH, ['--projectRoot', msg.data.projectRoot, '--file', path.join(msg.data.projectRoot, 'cypress.config.js')]) - proc.on('message', (msg) => { - process.send({ childMessage: msg }) + proc.on('message', (msg: any) => { + process.send!({ childMessage: msg }) }) - process.send({ childPid: proc.pid }) + process.send!({ childPid: proc.pid }) } if (msg.msg === 'toChild') { From dc7575c4131911c2ba6d01dd0e17c5057dd382d6 Mon Sep 17 00:00:00 2001 From: Bill Glesias Date: Thu, 17 Jul 2025 17:26:07 -0400 Subject: [PATCH 5/6] update projectConfigIpc to find right entry point --- packages/data-context/src/data/ProjectConfigIpc.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/data-context/src/data/ProjectConfigIpc.ts b/packages/data-context/src/data/ProjectConfigIpc.ts index 417213ed38ab..b1d07bc11d17 100644 --- a/packages/data-context/src/data/ProjectConfigIpc.ts +++ b/packages/data-context/src/data/ProjectConfigIpc.ts @@ -18,7 +18,7 @@ const pkg = require('@packages/root') const debug = debugLib(`cypress:lifecycle:ProjectConfigIpc`) const debugVerbose = debugLib(`cypress-verbose:lifecycle:ProjectConfigIpc`) -const CHILD_PROCESS_FILE_PATH = require.resolve('@packages/server/lib/plugins/child/require_async_child') +const CHILD_PROCESS_FILE_PATH = require.resolve('@packages/server/lib/plugins/child/require_async_child.ts') // NOTE: need the file:// prefix to avoid https://nodejs.org/api/errors.html#err_unsupported_esm_url_scheme on windows const tsx = os.platform() === 'win32' ? `file://${toPosix(require.resolve('tsx'))}` : toPosix(require.resolve('tsx')) From cb1b55640d02a9455a0c635f176b1300ebe777ae Mon Sep 17 00:00:00 2001 From: Bill Glesias Date: Thu, 17 Jul 2025 18:28:39 -0400 Subject: [PATCH 6/6] test --- packages/data-context/src/data/ProjectConfigIpc.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/data-context/src/data/ProjectConfigIpc.ts b/packages/data-context/src/data/ProjectConfigIpc.ts index b1d07bc11d17..0a29596f16e7 100644 --- a/packages/data-context/src/data/ProjectConfigIpc.ts +++ b/packages/data-context/src/data/ProjectConfigIpc.ts @@ -18,7 +18,7 @@ const pkg = require('@packages/root') const debug = debugLib(`cypress:lifecycle:ProjectConfigIpc`) const debugVerbose = debugLib(`cypress-verbose:lifecycle:ProjectConfigIpc`) -const CHILD_PROCESS_FILE_PATH = require.resolve('@packages/server/lib/plugins/child/require_async_child.ts') +const CHILD_PROCESS_FILE_PATH = process.env.CYPRESS_INTERNAL_ENV === 'production' ? require.resolve('@packages/server/lib/plugins/child/require_async_child.js') : require.resolve('@packages/server/lib/plugins/child/require_async_child.ts') // NOTE: need the file:// prefix to avoid https://nodejs.org/api/errors.html#err_unsupported_esm_url_scheme on windows const tsx = os.platform() === 'win32' ? `file://${toPosix(require.resolve('tsx'))}` : toPosix(require.resolve('tsx'))