diff --git a/src/common/BrowserConnector.ts b/src/common/BrowserConnector.ts
index b3f2b3ba846..d9ede04f2ec 100644
--- a/src/common/BrowserConnector.ts
+++ b/src/common/BrowserConnector.ts
@@ -14,18 +14,18 @@
* limitations under the License.
*/
-import { ConnectionTransport } from './ConnectionTransport.js';
+import { debugError } from '../common/helper.js';
+import { isNode } from '../environment.js';
+import { assert } from './assert.js';
import {
Browser,
- TargetFilterCallback,
IsPageTargetCallback,
+ TargetFilterCallback,
} from './Browser.js';
-import { assert } from './assert.js';
-import { debugError } from '../common/helper.js';
import { Connection } from './Connection.js';
-import { Viewport } from './PuppeteerViewport.js';
-import { isNode } from '../environment.js';
+import { ConnectionTransport } from './ConnectionTransport.js';
import { getFetch } from './fetch.js';
+import { Viewport } from './PuppeteerViewport.js';
/**
* Generic browser options that can be passed when launching any browser or when
diff --git a/src/common/DOMWorld.ts b/src/common/DOMWorld.ts
index 51de8de2c5e..5558f9d8205 100644
--- a/src/common/DOMWorld.ts
+++ b/src/common/DOMWorld.ts
@@ -14,30 +14,29 @@
* limitations under the License.
*/
+import { Protocol } from 'devtools-protocol';
import { assert } from './assert.js';
-import { helper, debugError } from './helper.js';
+import { CDPSession } from './Connection.js';
+import { TimeoutError } from './Errors.js';
+import {
+ EvaluateFn,
+ EvaluateFnReturnType,
+ EvaluateHandleFn,
+ SerializableOrJSHandle,
+ UnwrapPromiseLike,
+ WrapElementHandle,
+} from './EvalTypes.js';
+import { ExecutionContext } from './ExecutionContext.js';
+import { Frame, FrameManager } from './FrameManager.js';
+import { debugError, helper } from './helper.js';
+import { MouseButton } from './Input.js';
+import { ElementHandle, JSHandle } from './JSHandle.js';
import {
LifecycleWatcher,
PuppeteerLifeCycleEvent,
} from './LifecycleWatcher.js';
-import { TimeoutError } from './Errors.js';
-import { JSHandle, ElementHandle } from './JSHandle.js';
-import { ExecutionContext } from './ExecutionContext.js';
-import { TimeoutSettings } from './TimeoutSettings.js';
-import { MouseButton } from './Input.js';
-import { FrameManager, Frame } from './FrameManager.js';
import { getQueryHandlerAndSelector } from './QueryHandler.js';
-import {
- SerializableOrJSHandle,
- EvaluateHandleFn,
- WrapElementHandle,
- EvaluateFn,
- EvaluateFnReturnType,
- UnwrapPromiseLike,
-} from './EvalTypes.js';
-import { isNode } from '../environment.js';
-import { Protocol } from 'devtools-protocol';
-import { CDPSession } from './Connection.js';
+import { TimeoutSettings } from './TimeoutSettings.js';
// predicateQueryHandler and checkWaitForOptions are declared here so that
// TypeScript knows about them when used in the predicate function below.
@@ -330,13 +329,18 @@ export class DOMWorld {
}
if (path !== null) {
- if (!isNode) {
- throw new Error(
- 'Cannot pass a filepath to addScriptTag in the browser environment.'
- );
+ let fs;
+ try {
+ fs = (await import('fs')).promises;
+ } catch (error) {
+ if (error instanceof TypeError) {
+ throw new Error(
+ 'Can only pass a filepath to addScriptTag in a Node-like environment.'
+ );
+ }
+ throw error;
}
- const fs = await import('fs');
- let contents = await fs.promises.readFile(path, 'utf8');
+ let contents = await fs.readFile(path, 'utf8');
contents += '//# sourceURL=' + path.replace(/\n/g, '');
const context = await this.executionContext();
const handle = await context.evaluateHandle(
@@ -437,13 +441,19 @@ export class DOMWorld {
}
if (path !== null) {
- if (!isNode) {
- throw new Error(
- 'Cannot pass a filepath to addStyleTag in the browser environment.'
- );
+ let fs: typeof import('fs').promises;
+ try {
+ fs = (await import('fs')).promises;
+ } catch (error) {
+ if (error instanceof TypeError) {
+ throw new Error(
+ 'Cannot pass a filepath to addStyleTag in the browser environment.'
+ );
+ }
+ throw error;
}
- const fs = await import('fs');
- let contents = await fs.promises.readFile(path, 'utf8');
+
+ let contents = await fs.readFile(path, 'utf8');
contents += '/*# sourceURL=' + path.replace(/\n/g, '') + '*/';
const context = await this.executionContext();
const handle = await context.evaluateHandle(addStyleContent, contents);
diff --git a/src/common/JSHandle.ts b/src/common/JSHandle.ts
index 1e143c4a7cb..d28532e9f7c 100644
--- a/src/common/JSHandle.ts
+++ b/src/common/JSHandle.ts
@@ -14,25 +14,24 @@
* limitations under the License.
*/
-import { assert } from './assert.js';
-import { helper, debugError } from './helper.js';
-import { ExecutionContext } from './ExecutionContext.js';
-import { Page, ScreenshotOptions } from './Page.js';
-import { CDPSession } from './Connection.js';
-import { KeyInput } from './USKeyboardLayout.js';
-import { FrameManager, Frame } from './FrameManager.js';
-import { getQueryHandlerAndSelector } from './QueryHandler.js';
import { Protocol } from 'devtools-protocol';
+import { assert } from './assert.js';
+import { CDPSession } from './Connection.js';
import {
EvaluateFn,
- SerializableOrJSHandle,
EvaluateFnReturnType,
EvaluateHandleFn,
- WrapElementHandle,
+ SerializableOrJSHandle,
UnwrapPromiseLike,
+ WrapElementHandle,
} from './EvalTypes.js';
-import { isNode } from '../environment.js';
+import { ExecutionContext } from './ExecutionContext.js';
+import { Frame, FrameManager } from './FrameManager.js';
+import { debugError, helper } from './helper.js';
import { MouseButton } from './Input.js';
+import { Page, ScreenshotOptions } from './Page.js';
+import { getQueryHandlerAndSelector } from './QueryHandler.js';
+import { KeyInput } from './USKeyboardLayout.js';
/**
* @public
@@ -822,17 +821,18 @@ export class ElementHandle<
'Multiple file uploads only work with '
);
- if (!isNode) {
- throw new Error(
- `JSHandle#uploadFile can only be used in Node environments.`
- );
- }
- /*
- This import is only needed for `uploadFile`, so keep it scoped here to
- avoid paying the cost unnecessarily.
- */
- const path = await import('path');
// Locate all files and confirm that they exist.
+ let path: typeof import('path');
+ try {
+ path = await import('path');
+ } catch (error) {
+ if (error instanceof TypeError) {
+ throw new Error(
+ `JSHandle#uploadFile can only be used in Node-like environments.`
+ );
+ }
+ throw error;
+ }
const files = filePaths.map((filePath) => {
if (path.isAbsolute(filePath)) {
return filePath;
diff --git a/src/common/Page.ts b/src/common/Page.ts
index c47066a5d9c..c01c6beaf01 100644
--- a/src/common/Page.ts
+++ b/src/common/Page.ts
@@ -16,7 +16,6 @@
import { Protocol } from 'devtools-protocol';
import type { Readable } from 'stream';
-import { isNode } from '../environment.js';
import { Accessibility } from './Accessibility.js';
import { assert, assertNever } from './assert.js';
import { Browser, BrowserContext } from './Browser.js';
@@ -2825,13 +2824,17 @@ export class Page extends EventEmitter {
: Buffer.from(result.data, 'base64');
if (options.path) {
- if (!isNode) {
- throw new Error(
- 'Screenshots can only be written to a file path in a Node environment.'
- );
+ try {
+ const fs = (await import('fs')).promises;
+ await fs.writeFile(options.path, buffer);
+ } catch (error) {
+ if (error instanceof TypeError) {
+ throw new Error(
+ 'Screenshots can only be written to a file path in a Node-like environment.'
+ );
+ }
+ throw error;
}
- const fs = (await import('fs')).promises;
- await fs.writeFile(options.path, buffer);
}
return buffer;
diff --git a/src/common/fetch.ts b/src/common/fetch.ts
index 8f41553b04a..c0ca7e3baf0 100644
--- a/src/common/fetch.ts
+++ b/src/common/fetch.ts
@@ -14,9 +14,7 @@
* limitations under the License.
*/
-import { isNode } from '../environment.js';
-
/* Use the global version if we're in the browser, else load the node-fetch module. */
export const getFetch = async (): Promise => {
- return isNode ? (await import('cross-fetch')).fetch : globalThis.fetch;
+ return globalThis.fetch || (await import('cross-fetch')).fetch;
};
diff --git a/src/common/helper.ts b/src/common/helper.ts
index bc57e582c87..08a57483b36 100644
--- a/src/common/helper.ts
+++ b/src/common/helper.ts
@@ -14,15 +14,14 @@
* limitations under the License.
*/
-import type { Readable } from 'stream';
-
-import { TimeoutError } from './Errors.js';
-import { debug } from './Debug.js';
-import { CDPSession } from './Connection.js';
import { Protocol } from 'devtools-protocol';
-import { CommonEventEmitter } from './EventEmitter.js';
-import { assert } from './assert.js';
+import type { Readable } from 'stream';
import { isNode } from '../environment.js';
+import { assert } from './assert.js';
+import { CDPSession } from './Connection.js';
+import { debug } from './Debug.js';
+import { TimeoutError } from './Errors.js';
+import { CommonEventEmitter } from './EventEmitter.js';
export const debugError = debug('puppeteer:error');
@@ -318,31 +317,34 @@ async function getReadableAsBuffer(
readable: Readable,
path?: string
): Promise {
- if (!isNode && path) {
- throw new Error('Cannot write to a path outside of Node.js environment.');
- }
-
- const fs = isNode ? (await import('fs')).promises : null;
-
- let fileHandle: import('fs').promises.FileHandle | undefined;
-
- if (path && fs) {
- fileHandle = await fs.open(path, 'w');
- }
const buffers = [];
- for await (const chunk of readable) {
- buffers.push(chunk);
- if (fileHandle && fs) {
- await fs.writeFile(fileHandle, chunk);
+ if (path) {
+ let fs: typeof import('fs').promises;
+ try {
+ fs = (await import('fs')).promises;
+ } catch (error) {
+ if (error instanceof TypeError) {
+ throw new Error(
+ 'Cannot write to a path outside of a Node-like environment.'
+ );
+ }
+ throw error;
+ }
+ const fileHandle = await fs.open(path, 'w+');
+ for await (const chunk of readable) {
+ buffers.push(chunk);
+ await fileHandle.writeFile(chunk);
+ }
+ await fileHandle.close();
+ } else {
+ for await (const chunk of readable) {
+ buffers.push(chunk);
}
}
-
- if (path && fileHandle) await fileHandle.close();
- let resultBuffer = null;
try {
- resultBuffer = Buffer.concat(buffers);
- } finally {
- return resultBuffer;
+ return Buffer.concat(buffers);
+ } catch (error) {
+ return null;
}
}
@@ -350,8 +352,8 @@ async function getReadableFromProtocolStream(
client: CDPSession,
handle: string
): Promise {
- // TODO:
- // This restriction can be lifted once https://github.com/nodejs/node/pull/39062 has landed
+ // TODO: Once Node 18 becomes the lowest supported version, we can migrate to
+ // ReadableStream.
if (!isNode) {
throw new Error('Cannot create a stream outside of Node.js environment.');
}
diff --git a/src/node-puppeteer-core.ts b/src/node-puppeteer-core.ts
index 976dfd5715c..23269bbc318 100644
--- a/src/node-puppeteer-core.ts
+++ b/src/node-puppeteer-core.ts
@@ -14,8 +14,8 @@
* limitations under the License.
*/
-import { initializePuppeteerNode } from './initialize-node.js';
import { isNode } from './environment.js';
+import { initializePuppeteerNode } from './initialize-node.js';
if (!isNode) {
throw new Error('Cannot run puppeteer-core outside of Node.js');
diff --git a/src/node.ts b/src/node.ts
index 714f8c2b948..b996436b9d5 100644
--- a/src/node.ts
+++ b/src/node.ts
@@ -14,10 +14,12 @@
* limitations under the License.
*/
-import { initializePuppeteerNode } from './initialize-node.js';
import { isNode } from './environment.js';
+import { initializePuppeteerNode } from './initialize-node.js';
if (!isNode) {
throw new Error('Trying to run Puppeteer-Node in a web environment.');
}
-export default initializePuppeteerNode('puppeteer');
+
+const puppeteer = initializePuppeteerNode('puppeteer');
+export default puppeteer;
diff --git a/src/web.ts b/src/web.ts
index a48a5cf14d6..777f9ae0c3a 100644
--- a/src/web.ts
+++ b/src/web.ts
@@ -14,11 +14,12 @@
* limitations under the License.
*/
-import { initializePuppeteerWeb } from './initialize-web.js';
import { isNode } from './environment.js';
+import { initializePuppeteerWeb } from './initialize-web.js';
if (isNode) {
throw new Error('Trying to run Puppeteer-Web in a Node environment');
}
-export default initializePuppeteerWeb('puppeteer');
+const puppeteer = initializePuppeteerWeb('puppeteer');
+export default puppeteer;