chore: do not use process in client (#34816)

This commit is contained in:
Pavel Feldman 2025-02-15 19:49:30 -08:00 committed by GitHub
parent 3606a434fe
commit f70f92d5cd
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
17 changed files with 72 additions and 33 deletions

View File

@ -177,7 +177,7 @@ const noBooleanCompareRules = {
'@typescript-eslint/no-unnecessary-boolean-literal-compare': 2,
};
const noRestrictedGlobalsRules = {
const noWebGlobalsRules = {
'no-restricted-globals': [
'error',
{ 'name': 'window' },
@ -186,6 +186,13 @@ const noRestrictedGlobalsRules = {
],
};
const noNodeGlobalsRules = {
'no-restricted-globals': [
'error',
{ 'name': 'process' },
],
};
const importOrderRules = {
'import/order': [2, {
'groups': ['builtin', 'external', 'internal', ['parent', 'sibling'], 'index', 'type'],
@ -249,7 +256,19 @@ export default [{
files: ['packages/playwright-core/src/server/injected/**/*.ts'],
languageOptions: languageOptionsWithTsConfig,
rules: {
...noRestrictedGlobalsRules,
...noWebGlobalsRules,
...noFloatingPromisesRules,
...noBooleanCompareRules,
}
}, {
files: [
'packages/playwright-core/src/client/**/*.ts',
'packages/playwright-core/src/protocol/**/*.ts',
'packages/playwright-core/src/utils/**/*.ts',
],
languageOptions: languageOptionsWithTsConfig,
rules: {
...noNodeGlobalsRules,
...noFloatingPromisesRules,
...noBooleanCompareRules,
}

View File

@ -23,9 +23,9 @@ import * as path from 'path';
import * as playwright from '../..';
import { launchBrowserServer, printApiJson, runDriver, runServer } from './driver';
import { registry, writeDockerVersion } from '../server';
import { gracefullyProcessExitDoNotHang } from '../utils';
import { gracefullyProcessExitDoNotHang, isLikelyNpxGlobal } from '../utils';
import { runTraceInBrowser, runTraceViewerApp } from '../server/trace/viewer/traceViewer';
import { assert, getPackageManagerExecCommand, isLikelyNpxGlobal } from '../utils';
import { assert, getPackageManagerExecCommand } from '../utils';
import { wrapInASCIIBox } from '../server/utils/ascii';
import { dotenv, program } from '../utilsBundle';

View File

@ -51,7 +51,9 @@ export class Android extends ChannelOwner<channels.AndroidChannel> implements ap
setDefaultTimeout(timeout: number) {
this._timeoutSettings.setDefaultTimeout(timeout);
this._channel.setDefaultTimeoutNoReply({ timeout });
this._wrapApiCall(async () => {
await this._channel.setDefaultTimeoutNoReply({ timeout });
}, true).catch(() => {});
}
async devices(options: { port?: number } = {}): Promise<AndroidDevice[]> {
@ -133,7 +135,9 @@ export class AndroidDevice extends ChannelOwner<channels.AndroidDeviceChannel> i
setDefaultTimeout(timeout: number) {
this._timeoutSettings.setDefaultTimeout(timeout);
this._channel.setDefaultTimeoutNoReply({ timeout });
this._wrapApiCall(async () => {
await this._channel.setDefaultTimeoutNoReply({ timeout });
}, true).catch(() => {});
}
serial(): string {

View File

@ -246,15 +246,15 @@ export class BrowserContext extends ChannelOwner<channels.BrowserContextChannel>
setDefaultNavigationTimeout(timeout: number | undefined) {
this._timeoutSettings.setDefaultNavigationTimeout(timeout);
this._wrapApiCall(async () => {
this._channel.setDefaultNavigationTimeoutNoReply({ timeout }).catch(() => {});
}, true);
await this._channel.setDefaultNavigationTimeoutNoReply({ timeout });
}, true).catch(() => {});
}
setDefaultTimeout(timeout: number | undefined) {
this._timeoutSettings.setDefaultTimeout(timeout);
this._wrapApiCall(async () => {
this._channel.setDefaultTimeoutNoReply({ timeout }).catch(() => {});
}, true);
await this._channel.setDefaultTimeoutNoReply({ timeout });
}, true).catch(() => {});
}
browser(): Browser | null {
@ -559,7 +559,7 @@ export async function prepareBrowserContextParams(platform: Platform, options: B
};
}
if (contextParams.recordVideo && contextParams.recordVideo.dir)
contextParams.recordVideo.dir = platform.path().resolve(process.cwd(), contextParams.recordVideo.dir);
contextParams.recordVideo.dir = platform.path().resolve(contextParams.recordVideo.dir);
return contextParams;
}

View File

@ -199,7 +199,7 @@ export abstract class ChannelOwner<T extends channels.Channel = channels.Channel
}
return result;
} catch (e) {
const innerError = ((process.env.PWDEBUGIMPL || this._platform.isUnderTest()) && e.stack) ? '\n<inner error>\n' + e.stack : '';
const innerError = ((this._platform.showInternalStackFrames() || this._platform.isUnderTest()) && e.stack) ? '\n<inner error>\n' + e.stack : '';
if (apiZone.apiName && !apiZone.apiName.includes('<anonymous>'))
e.message = apiZone.apiName + ': ' + e.message;
const stackFrames = '\n' + stringifyStackFrames(stackTrace.frames).join('\n') + innerError;

View File

@ -28,7 +28,7 @@ export function captureLibraryStackTrace(platform: Platform): { frames: StackFra
isPlaywrightLibrary: boolean;
};
let parsedFrames = stack.map(line => {
const frame = parseStackFrame(line, platform.pathSeparator);
const frame = parseStackFrame(line, platform.pathSeparator, platform.showInternalStackFrames());
if (!frame || !frame.file)
return null;
const isPlaywrightLibrary = !!platform.coreDir && frame.file.startsWith(platform.coreDir);
@ -62,10 +62,8 @@ export function captureLibraryStackTrace(platform: Platform): { frames: StackFra
}
// This is for the inspector so that it did not include the test runner stack frames.
const filterPrefixes = platform.coreDir ? [platform.coreDir, ...platform.boxedStackPrefixes()] : platform.boxedStackPrefixes();
const filterPrefixes = platform.boxedStackPrefixes();
parsedFrames = parsedFrames.filter(f => {
if (process.env.PWDEBUGIMPL)
return true;
if (filterPrefixes.some(prefix => f.frame.file.startsWith(prefix)))
return false;
return true;

View File

@ -54,7 +54,7 @@ export class Electron extends ChannelOwner<channels.ElectronChannel> implements
async launch(options: ElectronOptions = {}): Promise<ElectronApplication> {
const params: channels.ElectronLaunchParams = {
...await prepareBrowserContextParams(this._platform, options),
env: envObjectToArray(options.env ? options.env : process.env),
env: envObjectToArray(options.env ? options.env : this._platform.env),
tracesDir: options.tracesDir,
};
const app = ElectronApplication.from((await this._channel.launch(params)).electronApplication);

View File

@ -277,15 +277,15 @@ export class Page extends ChannelOwner<channels.PageChannel> implements api.Page
setDefaultNavigationTimeout(timeout: number) {
this._timeoutSettings.setDefaultNavigationTimeout(timeout);
this._wrapApiCall(async () => {
this._channel.setDefaultNavigationTimeoutNoReply({ timeout }).catch(() => {});
}, true);
await this._channel.setDefaultNavigationTimeoutNoReply({ timeout });
}, true).catch(() => {});
}
setDefaultTimeout(timeout: number) {
this._timeoutSettings.setDefaultTimeout(timeout);
this._wrapApiCall(async () => {
this._channel.setDefaultTimeoutNoReply({ timeout }).catch(() => {});
}, true);
await this._channel.setDefaultTimeoutNoReply({ timeout });
}, true).catch(() => {});
}
private _forceVideo(): Video {

View File

@ -45,6 +45,7 @@ export type Platform = {
coreDir?: string;
createGuid: () => string;
defaultMaxListeners: () => number;
env: Record<string, string | undefined>;
fs: () => typeof fs;
inspectCustom: symbol | undefined;
isDebugMode: () => boolean;
@ -54,6 +55,7 @@ export type Platform = {
log: (name: 'api' | 'channel', message: string | Error | object) => void;
path: () => typeof path;
pathSeparator: string;
showInternalStackFrames: () => boolean,
streamFile: (path: string, writable: Writable) => Promise<void>,
streamReadable: (channel: channels.StreamChannel) => Readable,
streamWritable: (channel: channels.WritableStreamChannel) => Writable,
@ -77,6 +79,8 @@ export const emptyPlatform: Platform = {
defaultMaxListeners: () => 10,
env: {},
fs: () => {
throw new Error('Not implemented');
},
@ -101,6 +105,8 @@ export const emptyPlatform: Platform = {
pathSeparator: '/',
showInternalStackFrames: () => false,
streamFile(path: string, writable: Writable): Promise<void> {
throw new Error('Streams are not available');
},

View File

@ -96,8 +96,8 @@ export class Waiter {
log(s: string) {
this._logs.push(s);
this._channelOwner._wrapApiCall(async () => {
await this._channelOwner._channel.waitForEventInfo({ info: { waitId: this._waitId, phase: 'log', message: s } }).catch(() => {});
}, true);
await this._channelOwner._channel.waitForEventInfo({ info: { waitId: this._waitId, phase: 'log', message: s } });
}, true).catch(() => {});
}
private _rejectOn(promise: Promise<any>, dispose?: () => void) {

View File

@ -39,7 +39,7 @@ export async function connectOverWebSocket(parentConnection: Connection, params:
connection!.dispatch(message);
} catch (e) {
closeError = String(e);
transport.close();
transport.close().catch(() => {});
}
});
return connection;
@ -70,7 +70,7 @@ class JsonPipeTransport implements Transport {
}
async send(message: object) {
this._owner._wrapApiCall(async () => {
await this._owner._wrapApiCall(async () => {
await this._pipe!.send({ message });
}, /* isInternal */ true);
}

View File

@ -47,3 +47,7 @@ export function getPackageManagerExecCommand() {
return 'pnpm exec';
return 'npx';
}
export function isLikelyNpxGlobal() {
return process.argv.length >= 2 && process.argv[1].includes('_npx');
}

View File

@ -61,10 +61,16 @@ export function setBoxedStackPrefixes(prefixes: string[]) {
boxedStackPrefixes = prefixes;
}
const coreDir = path.dirname(require.resolve('../../../package.json'));
export const nodePlatform: Platform = {
name: 'node',
boxedStackPrefixes: () => boxedStackPrefixes,
boxedStackPrefixes: () => {
if (process.env.PWDEBUGIMPL)
return [];
return [coreDir, ...boxedStackPrefixes];
},
calculateSha1: (text: string) => {
const sha1 = crypto.createHash('sha1');
@ -74,13 +80,15 @@ export const nodePlatform: Platform = {
colors,
coreDir: path.dirname(require.resolve('../../../package.json')),
coreDir,
createGuid: () => crypto.randomBytes(16).toString('hex'),
defaultMaxListeners: () => EventEmitter.defaultMaxListeners,
fs: () => fs,
env: process.env,
inspectCustom: util.inspect.custom,
isDebugMode: () => !!debugMode(),
@ -101,6 +109,8 @@ export const nodePlatform: Platform = {
pathSeparator: path.sep,
showInternalStackFrames: () => !!process.env.PWDEBUGIMPL,
async streamFile(path: string, stream: Writable): Promise<void> {
await pipelineAsync(fs.createReadStream(path), stream);
},

View File

@ -27,5 +27,3 @@ export function isObject(obj: any): obj is NonNullable<object> {
export function isError(obj: any): obj is Error {
return obj instanceof Error || (obj && Object.getPrototypeOf(obj)?.name === 'Error');
}
export const isLikelyNpxGlobal = () => process.argv.length >= 2 && process.argv[1].includes('_npx');

View File

@ -37,7 +37,7 @@ export function captureRawStack(): RawStack {
return stack.split('\n');
}
export function parseStackFrame(text: string, pathSeparator: string): StackFrame | null {
export function parseStackFrame(text: string, pathSeparator: string, showInternalStackFrames: boolean): StackFrame | null {
const match = text && text.match(re);
if (!match)
return null;
@ -46,7 +46,7 @@ export function parseStackFrame(text: string, pathSeparator: string): StackFrame
let file = match[7];
if (!file)
return null;
if (!process.env.PWDEBUGIMPL && (file.startsWith('internal') || file.startsWith('node:')))
if (!showInternalStackFrames && (file.startsWith('internal') || file.startsWith('node:')))
return null;
const line = match[8];

View File

@ -522,7 +522,7 @@ export function prepareErrorStack(stack: string): {
const stackLines = lines.slice(firstStackLine);
let location: Location | undefined;
for (const line of stackLines) {
const frame = parseStackFrame(line, path.sep);
const frame = parseStackFrame(line, path.sep, !!process.env.PWDEBUGIMPL);
if (!frame || !frame.file)
continue;
if (belongsToNodeModules(frame.file))

View File

@ -55,7 +55,7 @@ export function filterStackFile(file: string) {
export function filteredStackTrace(rawStack: RawStack): StackFrame[] {
const frames: StackFrame[] = [];
for (const line of rawStack) {
const frame = parseStackFrame(line, path.sep);
const frame = parseStackFrame(line, path.sep, !!process.env.PWDEBUGIMPL);
if (!frame || !frame.file)
continue;
if (!filterStackFile(frame.file))