diff --git a/eslint.config.mjs b/eslint.config.mjs index 0f1dfe15522ee..0bd0d7837dbb6 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -296,6 +296,11 @@ const reactFiles = [ `packages/web/src/**/*.tsx`, ]; +const noProcessExitSyntax = { + selector: "CallExpression[callee.name='processExit']", + message: "Please use gracefullyProcessExitDoNotHang function to exit the process.", +}; + function reactPackageSection(packageName) { return { files: [ @@ -348,6 +353,10 @@ export default [ { object: "process", property: "stdout" }, { object: "process", property: "stderr" }, ], + "no-restricted-syntax": [ + "error", + noProcessExitSyntax, + ], }, }, { @@ -424,6 +433,7 @@ export default [ ], "no-restricted-syntax": [ "error", + noProcessExitSyntax, { selector: "TSAsExpression > TSAnyKeyword", message: "Avoid 'as any' — risk of accidentally casting to client interfaces. Use a precise type or add an eslint-disable with justification.", diff --git a/packages/playwright-chromium/cli.js b/packages/playwright-chromium/cli.js index 1ab7f499d77e6..c50f2643facfb 100755 --- a/packages/playwright-chromium/cli.js +++ b/packages/playwright-chromium/cli.js @@ -15,6 +15,7 @@ * limitations under the License. */ +require('playwright-core/lib/bootstrap'); const { program } = require('playwright-core/lib/utilsBundle'); const { libCli } = require('playwright-core/lib/coreBundle'); libCli.decorateProgram(program); diff --git a/packages/playwright-core/cli.js b/packages/playwright-core/cli.js index f275a03346a89..7dbfc530629d9 100755 --- a/packages/playwright-core/cli.js +++ b/packages/playwright-core/cli.js @@ -14,6 +14,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +require('./lib/bootstrap'); const { libCli, libCliTestStub } = require('./lib/coreBundle'); const { program } = require('./lib/utilsBundle'); libCli.decorateProgram(program); diff --git a/packages/playwright-core/src/DEPS.list b/packages/playwright-core/src/DEPS.list index 264d291549e24..9f5059ca1eaf8 100644 --- a/packages/playwright-core/src/DEPS.list +++ b/packages/playwright-core/src/DEPS.list @@ -49,6 +49,9 @@ node_modules/stoppable node_modules/ws node_modules/yaml +[bootstrap.ts] +"strict" + [package.ts] "strict" diff --git a/packages/playwright-core/src/bootstrap.ts b/packages/playwright-core/src/bootstrap.ts index b981e3c1a74b8..c3ff509cb887b 100644 --- a/packages/playwright-core/src/bootstrap.ts +++ b/packages/playwright-core/src/bootstrap.ts @@ -31,6 +31,47 @@ if (major < minimumMajorNodeVersion) { process.exit(1); } +let stopProfiling = (callback: () => void) => callback(); + +if (process.env.PW_PROFILE_DIR) { + const fs: typeof import('fs') = require('fs'); + const path: typeof import('path') = require('path'); + const { Session }: typeof import('inspector') = require('inspector'); + + const profileDir = path.resolve(process.env.PW_PROFILE_DIR); + const script = process.argv[1] ? path.basename(process.argv[1], path.extname(process.argv[1])) : 'node'; + const command = /^[a-z][\w-]*$/i.test(process.argv[2] ?? '') ? process.argv[2] : undefined; + const profileName = [script, command, process.pid].filter(Boolean).join('-'); + + const session = new Session(); + session.connect(); + session.post('Profiler.enable'); + session.post('Profiler.start'); + + stopProfiling = callback => { + stopProfiling = callback => callback(); + session.post('Profiler.stop', (error, result) => { + try { + if (!error) { + fs.mkdirSync(profileDir, { recursive: true }); + fs.writeFileSync(path.join(profileDir, profileName + '.cpuprofile'), JSON.stringify(result.profile)); + } + } finally { + callback(); + } + }); + }; + process.on('beforeExit', () => stopProfiling(() => {})); +} + +export function processExit(code?: number): never { + stopProfiling(() => { + // eslint-disable-next-line no-restricted-properties + process.exit(code); + }); + return undefined as never; +} + if (process.env.PW_INSTRUMENT_MODULES) { const Module = require('module'); const originalLoad = Module._load; diff --git a/packages/playwright-core/src/cli/DEPS.list b/packages/playwright-core/src/cli/DEPS.list index f71bab90aa05f..9f6a1f2cc3114 100644 --- a/packages/playwright-core/src/cli/DEPS.list +++ b/packages/playwright-core/src/cli/DEPS.list @@ -10,7 +10,6 @@ ../tools/cli-client/program.ts ../tools/mcp/program.ts ../tools/trace/traceCli.ts -../bootstrap.ts node_modules/commander node_modules/debug node_modules/dotenv diff --git a/packages/playwright-core/src/cli/program.ts b/packages/playwright-core/src/cli/program.ts index cec10813fc804..7b4d685411982 100644 --- a/packages/playwright-core/src/cli/program.ts +++ b/packages/playwright-core/src/cli/program.ts @@ -16,7 +16,6 @@ /* eslint-disable no-console */ -import '../bootstrap'; import { program } from 'commander'; import { gracefullyProcessExitDoNotHang } from '@utils/processLauncher'; import { getPackageManagerExecCommand } from '@utils/env'; diff --git a/packages/playwright-core/src/remote/playwrightConnection.ts b/packages/playwright-core/src/remote/playwrightConnection.ts index f03f7a1d8b3e9..e978b85ce033b 100644 --- a/packages/playwright-core/src/remote/playwrightConnection.ts +++ b/packages/playwright-core/src/remote/playwrightConnection.ts @@ -14,7 +14,6 @@ * limitations under the License. */ -import { startProfiling, stopProfiling } from '@utils/profiler'; import { debugLogger } from '@utils/debugLogger'; import { monotonicTime } from '@isomorphic/time'; import { Semaphore } from '@isomorphic/semaphore'; @@ -39,13 +38,11 @@ export class PlaywrightConnection { private _id: string; private _onDisconnectPromise: Promise | undefined; private _root: DispatcherScope; - private _profileName: string; constructor(semaphore: Semaphore, transport: ServerTransport, controller: boolean, playwright: Playwright, initialize: () => Promise, id: string) { this._transport = transport; this._semaphore = semaphore; this._id = id; - this._profileName = new Date().toISOString(); const lock = this._semaphore.acquire(); @@ -89,8 +86,6 @@ export class PlaywrightConnection { } this._root = new RootDispatcher(this._dispatcherConnection, async (scope, params) => { - await startProfiling(); - const options = await initialize(); if (options.preLaunchedBrowser) { const browser = options.preLaunchedBrowser; @@ -130,7 +125,6 @@ export class PlaywrightConnection { debugLogger.log('server', `[${this._id}] starting cleanup`); for (const cleanup of this._cleanups) await cleanup().catch(() => {}); - await stopProfiling(this._profileName); this._semaphore.release(); debugLogger.log('server', `[${this._id}] finished cleanup`); } diff --git a/packages/playwright-core/src/server/registry/DEPS.list b/packages/playwright-core/src/server/registry/DEPS.list index a6aa9a2c65cd0..f66d3c07b9205 100644 --- a/packages/playwright-core/src/server/registry/DEPS.list +++ b/packages/playwright-core/src/server/registry/DEPS.list @@ -1,6 +1,7 @@ [*] @utils/** @isomorphic/** +../../bootstrap.ts ../../package.ts ../utils.ts ../userAgent.ts diff --git a/packages/playwright-core/src/server/registry/oopDownloadBrowserMain.ts b/packages/playwright-core/src/server/registry/oopDownloadBrowserMain.ts index 06acef879795d..e7a302b33820a 100644 --- a/packages/playwright-core/src/server/registry/oopDownloadBrowserMain.ts +++ b/packages/playwright-core/src/server/registry/oopDownloadBrowserMain.ts @@ -22,6 +22,7 @@ import { getAsBooleanFromENV } from '@utils/env'; import { httpRequest } from '@utils/network'; import { removeFolders } from '@utils/fileUtils'; import { extractZip } from '@utils/third_party/extractZip'; +import { processExit } from '../../bootstrap'; export type DownloadParams = { title: string; @@ -133,17 +134,17 @@ export function runOopDownloadBrowserMain() { if (method === 'download') { try { await main(params); - // eslint-disable-next-line no-restricted-properties - process.exit(0); + // eslint-disable-next-line no-restricted-syntax + processExit(0); } catch (e) { // eslint-disable-next-line no-console console.error(e); - // eslint-disable-next-line no-restricted-properties - process.exit(1); + // eslint-disable-next-line no-restricted-syntax + processExit(1); } } }); - // eslint-disable-next-line no-restricted-properties - process.on('disconnect', () => { process.exit(0); }); + // eslint-disable-next-line no-restricted-syntax + process.on('disconnect', () => { processExit(0); }); } diff --git a/packages/playwright-core/src/tools/cli-client/DEPS.list b/packages/playwright-core/src/tools/cli-client/DEPS.list index 07864bbb60b55..7010d5ff3550c 100644 --- a/packages/playwright-core/src/tools/cli-client/DEPS.list +++ b/packages/playwright-core/src/tools/cli-client/DEPS.list @@ -1,5 +1,11 @@ +[cli.ts] +"strict" +../../bootstrap.ts +./program.ts + [program.ts] "strict" +../../bootstrap.ts ../../package.ts ../../serverRegistry.ts ./channelSessions.ts @@ -10,6 +16,7 @@ [output.ts] "strict" +../../bootstrap.ts ../utils/extension.ts ./channelSessions.ts diff --git a/packages/playwright-core/src/tools/cli-client/cli.ts b/packages/playwright-core/src/tools/cli-client/cli.ts index 6db3e40ee050b..cda0cc4cfe152 100644 --- a/packages/playwright-core/src/tools/cli-client/cli.ts +++ b/packages/playwright-core/src/tools/cli-client/cli.ts @@ -14,11 +14,12 @@ * limitations under the License. */ +import { processExit } from '../../bootstrap'; import { program } from './program'; program().catch(e => { /* eslint-disable no-console */ console.error(e.message); - /* eslint-disable no-restricted-properties */ - process.exit(1); + // eslint-disable-next-line no-restricted-syntax + processExit(1); }); diff --git a/packages/playwright-core/src/tools/cli-client/output.ts b/packages/playwright-core/src/tools/cli-client/output.ts index afcac0e728239..f481fcd776a69 100644 --- a/packages/playwright-core/src/tools/cli-client/output.ts +++ b/packages/playwright-core/src/tools/cli-client/output.ts @@ -15,11 +15,11 @@ */ /* eslint-disable no-console */ -/* eslint-disable no-restricted-properties */ import path from 'path'; import { playwrightExtensionInstallUrl } from '../utils/extension'; +import { processExit } from '../../bootstrap'; import type { ChannelSession } from './channelSessions'; import type { BrowserDescriptor } from '../../serverRegistry'; @@ -88,48 +88,56 @@ export class TextOutput implements Output { errorUnknownCommand(name: string | undefined, globalHelp: string): never { console.error(`Unknown command: ${name}\n`); console.log(globalHelp); - return process.exit(1); + // eslint-disable-next-line no-restricted-syntax + return processExit(1); } errorUnknownOption(opts: string[], commandHelp: string): never { console.error(`Unknown option${opts.length > 1 ? 's' : ''}: ${opts.map(f => `--${f}`).join(', ')}`); console.log(''); console.log(commandHelp); - return process.exit(1); + // eslint-disable-next-line no-restricted-syntax + return processExit(1); } errorTooManyArguments(expected: number, received: number, commandHelp: string): never { console.error(`error: too many arguments: expected ${expected}, received ${received}`); console.log(''); console.log(commandHelp); - return process.exit(1); + // eslint-disable-next-line no-restricted-syntax + return processExit(1); } errorAttachConflict(): never { console.error(`Error: only one of [name], --cdp, --endpoint, or --extension can be specified`); - return process.exit(1); + // eslint-disable-next-line no-restricted-syntax + return processExit(1); } errorDetachNotAttached(session: string): never { console.error(`Error: session '${session}' was not attached; use \`playwright-cli${session !== 'default' ? ` -s=${session}` : ''} close\` to stop it.`); - return process.exit(1); + // eslint-disable-next-line no-restricted-syntax + return processExit(1); } errorBrowserNotOpenForTool(session: string): never { console.log(`The browser '${session}' is not open, please run open first`); console.log(''); console.log(` playwright-cli${session !== 'default' ? ` -s=${session}` : ''} open [params]`); - return process.exit(1); + // eslint-disable-next-line no-restricted-syntax + return processExit(1); } errorAttachNoTarget(): never { console.error(`Error: no target specified for attach command; use one of [name], --cdp, --endpoint, or --extension to specify the target to attach to.`); - return process.exit(1); + // eslint-disable-next-line no-restricted-syntax + return processExit(1); } errorInstallGlobalRequiresSkills(): never { console.error(`Error: --global requires --skills`); - return process.exit(1); + // eslint-disable-next-line no-restricted-syntax + return processExit(1); } list({ all, browsers, servers, channelSessions }: ListData): void { @@ -280,42 +288,50 @@ export class JsonOutput implements Output { errorUnknownCommand(name: string | undefined, _globalHelp: string): never { this._emit({ isError: true, error: `Unknown command: ${name}` }); - return process.exit(1); + // eslint-disable-next-line no-restricted-syntax + return processExit(1); } errorUnknownOption(opts: string[], _commandHelp: string): never { this._emit({ isError: true, error: `Unknown option${opts.length > 1 ? 's' : ''}: ${opts.map(f => `--${f}`).join(', ')}` }); - return process.exit(1); + // eslint-disable-next-line no-restricted-syntax + return processExit(1); } errorTooManyArguments(expected: number, received: number, _commandHelp: string): never { this._emit({ isError: true, error: `error: too many arguments: expected ${expected}, received ${received}` }); - return process.exit(1); + // eslint-disable-next-line no-restricted-syntax + return processExit(1); } errorAttachConflict(): never { this._emit({ isError: true, error: `only one of [name], --cdp, --endpoint, or --extension can be specified` }); - return process.exit(1); + // eslint-disable-next-line no-restricted-syntax + return processExit(1); } errorDetachNotAttached(session: string): never { this._emit({ isError: true, error: `session '${session}' was not attached; use close to stop it.` }); - return process.exit(1); + // eslint-disable-next-line no-restricted-syntax + return processExit(1); } errorBrowserNotOpenForTool(session: string): never { this._emit({ isError: true, error: `The browser '${session}' is not open, please run open first` }); - return process.exit(1); + // eslint-disable-next-line no-restricted-syntax + return processExit(1); } errorAttachNoTarget(): never { this._emit({ isError: true, error: `no target specified for attach command; use one of [name], --cdp, --endpoint, or --extension to specify the target to attach to.` }); - return process.exit(1); + // eslint-disable-next-line no-restricted-syntax + return processExit(1); } errorInstallGlobalRequiresSkills(): never { this._emit({ isError: true, error: `--global requires --skills` }); - return process.exit(1); + // eslint-disable-next-line no-restricted-syntax + return processExit(1); } list({ all, browsers, servers, channelSessions }: ListData): void { diff --git a/packages/playwright-core/src/tools/cli-client/program.ts b/packages/playwright-core/src/tools/cli-client/program.ts index 3279731070754..246a21a7a2d7d 100644 --- a/packages/playwright-core/src/tools/cli-client/program.ts +++ b/packages/playwright-core/src/tools/cli-client/program.ts @@ -14,8 +14,6 @@ * limitations under the License. */ -/* eslint-disable no-restricted-properties */ - import { execSync, spawn } from 'child_process'; import crypto from 'crypto'; @@ -29,6 +27,7 @@ import { Session } from './session'; import { libPath } from '../../package'; import { serverRegistry } from '../../serverRegistry'; import { minimist } from './minimist'; +import { processExit } from '../../bootstrap'; import type { ListData, ListedBrowser, Output } from './output'; import type { ClientInfo, SessionFile } from './registry'; @@ -97,7 +96,8 @@ export async function program(options?: { embedderVersion?: string}) { if (args.version || args.v) { output.version(options?.embedderVersion ?? clientInfo.version); - process.exit(0); + // eslint-disable-next-line no-restricted-syntax + processExit(0); } const command = commandName && help.commands[commandName]; @@ -111,7 +111,8 @@ export async function program(options?: { embedderVersion?: string}) { lines.push(help.global); output.help(lines.join('\n\n')); } - process.exit(0); + // eslint-disable-next-line no-restricted-syntax + processExit(0); } if (!command) diff --git a/packages/playwright-core/src/tools/cli-daemon/DEPS.list b/packages/playwright-core/src/tools/cli-daemon/DEPS.list index af8874ca8887f..ed4ad436c21d1 100644 --- a/packages/playwright-core/src/tools/cli-daemon/DEPS.list +++ b/packages/playwright-core/src/tools/cli-daemon/DEPS.list @@ -1,4 +1,5 @@ [*] +../../bootstrap.ts ../../package.ts ../utils/socketConnection.ts ../utils/installSkills.ts diff --git a/packages/playwright-core/src/tools/cli-daemon/program.ts b/packages/playwright-core/src/tools/cli-daemon/program.ts index e96685479c525..1e2b39af7befd 100644 --- a/packages/playwright-core/src/tools/cli-daemon/program.ts +++ b/packages/playwright-core/src/tools/cli-daemon/program.ts @@ -29,6 +29,7 @@ import * as configUtils from '../mcp/config'; import { createClientInfo } from '../cli-client/registry'; import { installSkills } from '../utils/installSkills'; import { registry as browserRegistry } from '../../server/registry/index'; +import { processExit } from '../../bootstrap'; import type { Command } from 'commander'; export function decorateProgram(program: Command) { @@ -102,8 +103,8 @@ export async function initWorkspace(initSkills: string | undefined, initSkillsGl await installSkills(['playwright-cli'], target, { global: globalSkills }); } catch (error) { console.error('❌', error instanceof Error ? error.message : error); - // eslint-disable-next-line no-restricted-properties - process.exit(1); + // eslint-disable-next-line no-restricted-syntax + processExit(1); } } diff --git a/packages/playwright-core/src/tools/mcp/watchdog.ts b/packages/playwright-core/src/tools/mcp/watchdog.ts index 0f947ee7ca8bf..e2ee1afad26d5 100644 --- a/packages/playwright-core/src/tools/mcp/watchdog.ts +++ b/packages/playwright-core/src/tools/mcp/watchdog.ts @@ -16,6 +16,7 @@ import { gracefullyCloseAll, gracefullyCloseSet } from '@utils/processLauncher'; import { testDebug } from './log'; +import { processExit } from '../../bootstrap'; export function setupExitWatchdog() { let isExiting = false; @@ -23,12 +24,12 @@ export function setupExitWatchdog() { if (isExiting) return; isExiting = true; - // eslint-disable-next-line no-restricted-properties - setTimeout(() => process.exit(0), 15000); + // eslint-disable-next-line no-restricted-syntax + setTimeout(() => processExit(0), 15000); testDebug('gracefully closing ' + gracefullyCloseSet.size); await gracefullyCloseAll(); - // eslint-disable-next-line no-restricted-properties - process.exit(0); + // eslint-disable-next-line no-restricted-syntax + processExit(0); }; process.stdin.on('close', () => handleExit('close')); diff --git a/packages/playwright-firefox/cli.js b/packages/playwright-firefox/cli.js index 1ab7f499d77e6..c50f2643facfb 100755 --- a/packages/playwright-firefox/cli.js +++ b/packages/playwright-firefox/cli.js @@ -15,6 +15,7 @@ * limitations under the License. */ +require('playwright-core/lib/bootstrap'); const { program } = require('playwright-core/lib/utilsBundle'); const { libCli } = require('playwright-core/lib/coreBundle'); libCli.decorateProgram(program); diff --git a/packages/playwright-webkit/cli.js b/packages/playwright-webkit/cli.js index 1ab7f499d77e6..c50f2643facfb 100755 --- a/packages/playwright-webkit/cli.js +++ b/packages/playwright-webkit/cli.js @@ -15,6 +15,7 @@ * limitations under the License. */ +require('playwright-core/lib/bootstrap'); const { program } = require('playwright-core/lib/utilsBundle'); const { libCli } = require('playwright-core/lib/coreBundle'); libCli.decorateProgram(program); diff --git a/packages/playwright/src/cli/testActions.ts b/packages/playwright/src/cli/testActions.ts index c03abcff8064f..b6fd25facb05c 100644 --- a/packages/playwright/src/cli/testActions.ts +++ b/packages/playwright/src/cli/testActions.ts @@ -18,7 +18,6 @@ import fs from 'fs'; import path from 'path'; import { gracefullyProcessExitDoNotHang } from '@utils/processLauncher'; -import { startProfiling, stopProfiling } from '@utils/profiler'; import { builtInReporters, configLoader, ipc } from '../common'; import { base, projectUtils, testServer, watchMode, testRunner, runnerReporters } from '../runner'; @@ -26,7 +25,6 @@ import type { ReporterDescription } from '../../types/test'; import type { TestRunOptions } from '../runner/tasks'; export async function runTests(args: string[], opts: { [key: string]: any }) { - await startProfiling(); const cliOverrides = overridesFromOptions(opts); cliOverrides.argv = process.argv; @@ -62,7 +60,6 @@ export async function runTests(args: string[], opts: { [key: string]: any }) { project: opts.project || undefined, reporter: Array.isArray(opts.reporter) ? opts.reporter : opts.reporter ? [opts.reporter] : undefined, }); - await stopProfiling('runner'); const exitCode = status === 'interrupted' ? 130 : (status === 'passed' ? 0 : 1); gracefullyProcessExitDoNotHang(exitCode); return; @@ -80,14 +77,12 @@ export async function runTests(args: string[], opts: { [key: string]: any }) { grep: opts.grep } ); - await stopProfiling('runner'); const exitCode = status === 'interrupted' ? 130 : (status === 'passed' ? 0 : 1); gracefullyProcessExitDoNotHang(exitCode); return; } const status = await testRunner.runAllTestsWithConfig(config, options); - await stopProfiling('runner'); const exitCode = status === 'interrupted' ? 130 : (status === 'passed' ? 0 : 1); gracefullyProcessExitDoNotHang(exitCode); } diff --git a/packages/playwright/src/common/ipc.ts b/packages/playwright/src/common/ipc.ts index 872e06b30eabc..512a919b5d285 100644 --- a/packages/playwright/src/common/ipc.ts +++ b/packages/playwright/src/common/ipc.ts @@ -58,7 +58,6 @@ export type SerializedConfig = { export type ProcessInitParams = { timeOrigin: number; - processName: string; }; export type WorkerInitParams = { diff --git a/packages/playwright/src/common/process.ts b/packages/playwright/src/common/process.ts index 1d65cf1146d0b..8e6b54b930404 100644 --- a/packages/playwright/src/common/process.ts +++ b/packages/playwright/src/common/process.ts @@ -14,11 +14,10 @@ * limitations under the License. */ -import 'playwright-core/lib/bootstrap'; +import { processExit } from 'playwright-core/lib/bootstrap'; import { ManualPromise } from '@isomorphic/manualPromise'; import { setTimeOrigin } from '@isomorphic/time'; -import { startProfiling, stopProfiling } from '@utils/profiler'; import { setBoxedStackPrefixes } from '@utils/stackTrace'; import { packageRoot } from '../package'; @@ -61,7 +60,6 @@ let gracefullyCloseCalled = false; let forceExitInitiated = false; let processRunner: ProcessRunner | undefined; -let processName: string | undefined; const startingEnv = { ...process.env }; setBoxedStackPrefixes([packageRoot]); @@ -75,10 +73,8 @@ export function startProcessRunner(create: (params: any) => ProcessRunner) { process.on('message', async (message: any) => { if (message.method === '__init__') { const { processParams, runnerParams } = message.params as { processParams: ProcessInitParams, runnerParams: any }; - void startProfiling(); setTimeOrigin(processParams.timeOrigin); processRunner = create(runnerParams); - processName = processParams.processName; return; } if (message.method === '__stop__') { @@ -111,8 +107,8 @@ async function gracefullyCloseAndExit(forceExit: boolean) { if (forceExit && !forceExitInitiated) { forceExitInitiated = true; // Force exit after 30 seconds. - // eslint-disable-next-line no-restricted-properties - setTimeout(() => process.exit(0), kForceExitTimeout); + // eslint-disable-next-line no-restricted-syntax + setTimeout(() => processExit(0), kForceExitTimeout); } if (!gracefullyCloseCalled) { gracefullyCloseCalled = true; @@ -123,10 +119,8 @@ async function gracefullyCloseAndExit(forceExit: boolean) { // Meanwhile, try to gracefully shutdown. await processRunner?.gracefullyClose().catch(() => {}); clearInterval(heartbeat); - if (processName) - await stopProfiling(processName).catch(() => {}); - // eslint-disable-next-line no-restricted-properties - process.exit(0); + // eslint-disable-next-line no-restricted-syntax + processExit(0); } } diff --git a/packages/playwright/src/runner/processHost.ts b/packages/playwright/src/runner/processHost.ts index ea3b589aab857..38a3d367307e6 100644 --- a/packages/playwright/src/runner/processHost.ts +++ b/packages/playwright/src/runner/processHost.ts @@ -128,7 +128,6 @@ export class ProcessHost extends EventEmitter { return error; const processParams: ipc.ProcessInitParams = { - processName: this._processName, timeOrigin: timeOrigin(), }; diff --git a/packages/utils/DEPS.list b/packages/utils/DEPS.list index db4f4ba44dcb6..86d85869ee977 100644 --- a/packages/utils/DEPS.list +++ b/packages/utils/DEPS.list @@ -13,6 +13,9 @@ node_modules/ws node_modules/yauzl node_modules/yazl +[processLauncher.ts] +../playwright-core/src/bootstrap.ts + [comparators.ts] ./image_tools ./third_party/pixelmatch diff --git a/packages/utils/index.ts b/packages/utils/index.ts index 5231fef6e520c..8c06239ed5149 100644 --- a/packages/utils/index.ts +++ b/packages/utils/index.ts @@ -28,7 +28,6 @@ export * from './hostPlatform'; export * from './httpServer'; export * from './network'; export * from './processLauncher'; -export * from './profiler'; export * from './serializedFS'; export * from './socksProxy'; export * from './stackTrace'; diff --git a/packages/utils/processLauncher.ts b/packages/utils/processLauncher.ts index 662ac482d6c66..14a982d66e4d4 100644 --- a/packages/utils/processLauncher.ts +++ b/packages/utils/processLauncher.ts @@ -21,6 +21,7 @@ import * as readline from 'readline'; import { removeFolders } from './fileUtils'; import { isUnderTest } from './debug'; +import { processExit } from '../playwright-core/src/bootstrap'; import type { SpawnOptions } from 'child_process'; @@ -60,8 +61,8 @@ export async function gracefullyCloseAll() { export function gracefullyProcessExitDoNotHang(code: number, onExit?: () => Promise) { // Force exit after 30 seconds. const beforeExit = onExit ? () => onExit().catch(() => {}) : () => Promise.resolve(); - // eslint-disable-next-line no-restricted-properties - const callback = () => beforeExit().then(() => process.exit(code)); + // eslint-disable-next-line no-restricted-syntax + const callback = () => beforeExit().then(() => processExit(code)); setTimeout(callback, 30000); // Meanwhile, try to gracefully close all browsers. @@ -78,11 +79,11 @@ function sigintHandler() { const exitWithCode130 = () => { // Give tests a chance to see that launched process did exit and dispatch any async calls. if (isUnderTest()) { - // eslint-disable-next-line no-restricted-properties - setTimeout(() => process.exit(130), 1000); + // eslint-disable-next-line no-restricted-syntax + setTimeout(() => processExit(130), 1000); } else { - // eslint-disable-next-line no-restricted-properties - process.exit(130); + // eslint-disable-next-line no-restricted-syntax + processExit(130); } }; diff --git a/packages/utils/profiler.ts b/packages/utils/profiler.ts deleted file mode 100644 index e414f0ff4a960..0000000000000 --- a/packages/utils/profiler.ts +++ /dev/null @@ -1,48 +0,0 @@ -/** - * Copyright Microsoft Corporation. All rights reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import fs from 'fs'; -import path from 'path'; - -const profileDir = process.env.PWTEST_PROFILE_DIR || ''; - -let session: import('inspector').Session; - -export async function startProfiling() { - if (!profileDir) - return; - - session = new (require('inspector').Session)(); - session.connect(); - await new Promise(f => { - session.post('Profiler.enable', () => { - session.post('Profiler.start', f); - }); - }); -} - -export async function stopProfiling(profileName: string) { - if (!profileDir) - return; - - await new Promise(f => session.post('Profiler.stop', (err, { profile }) => { - if (!err) { - fs.mkdirSync(profileDir, { recursive: true }); - fs.writeFileSync(path.join(profileDir, profileName + '.json'), JSON.stringify(profile)); - } - f(); - })); -} diff --git a/utils/build/build.js b/utils/build/build.js index 68de8fbcf6fae..2a8d24c64f2d8 100644 --- a/utils/build/build.js +++ b/utils/build/build.js @@ -710,6 +710,10 @@ steps.push(new EsbuildStep({ name: 'externalize-utilsBundle', setup: build => build.onResolve({ filter: /utilsBundle/ }, () => ({ path: './utilsBundle', external: true })), + }, { + name: 'externalize-bootstrap', + setup: build => build.onResolve({ filter: /\/bootstrap$/ }, + args => path.resolve(args.resolveDir, args.path) === filePath('packages/playwright-core/src/bootstrap') ? { path: './bootstrap', external: true } : undefined), }, dynamicImportToRequirePlugin], }, [playwrightCoreSrc, ...commonUtilsSrc, filePath('packages/injected')]));