mirror of
https://github.com/puppeteer/puppeteer
synced 2024-06-14 14:02:48 +00:00
refactor!: use ReadableStreams (#11805)
This commit is contained in:
parent
514e2d5241
commit
84d9a94d62
@ -10,7 +10,9 @@ Generates a PDF of the page with the `print` CSS media type.
|
|||||||
|
|
||||||
```typescript
|
```typescript
|
||||||
class Page {
|
class Page {
|
||||||
abstract createPDFStream(options?: PDFOptions): Promise<Readable>;
|
abstract createPDFStream(
|
||||||
|
options?: PDFOptions
|
||||||
|
): Promise<ReadableStream<Uint8Array>>;
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -22,7 +24,7 @@ class Page {
|
|||||||
|
|
||||||
**Returns:**
|
**Returns:**
|
||||||
|
|
||||||
Promise<Readable>
|
Promise<ReadableStream<Uint8Array>>
|
||||||
|
|
||||||
## Remarks
|
## Remarks
|
||||||
|
|
||||||
|
@ -4,8 +4,6 @@
|
|||||||
* SPDX-License-Identifier: Apache-2.0
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import type {Readable} from 'stream';
|
|
||||||
|
|
||||||
import type {Protocol} from 'devtools-protocol';
|
import type {Protocol} from 'devtools-protocol';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
@ -2574,7 +2572,9 @@ export abstract class Page extends EventEmitter<PageEvents> {
|
|||||||
* {@link https://developer.mozilla.org/en-US/docs/Web/CSS/-webkit-print-color-adjust | `-webkit-print-color-adjust`}
|
* {@link https://developer.mozilla.org/en-US/docs/Web/CSS/-webkit-print-color-adjust | `-webkit-print-color-adjust`}
|
||||||
* property to force rendering of exact colors.
|
* property to force rendering of exact colors.
|
||||||
*/
|
*/
|
||||||
abstract createPDFStream(options?: PDFOptions): Promise<Readable>;
|
abstract createPDFStream(
|
||||||
|
options?: PDFOptions
|
||||||
|
): Promise<ReadableStream<Uint8Array>>;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* {@inheritDoc Page.createPDFStream}
|
* {@inheritDoc Page.createPDFStream}
|
||||||
|
@ -4,8 +4,6 @@
|
|||||||
* SPDX-License-Identifier: Apache-2.0
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import type {Readable} from 'stream';
|
|
||||||
|
|
||||||
import * as Bidi from 'chromium-bidi/lib/cjs/protocol/protocol.js';
|
import * as Bidi from 'chromium-bidi/lib/cjs/protocol/protocol.js';
|
||||||
import type Protocol from 'devtools-protocol';
|
import type Protocol from 'devtools-protocol';
|
||||||
|
|
||||||
@ -638,19 +636,15 @@ export class BidiPage extends Page {
|
|||||||
|
|
||||||
override async createPDFStream(
|
override async createPDFStream(
|
||||||
options?: PDFOptions | undefined
|
options?: PDFOptions | undefined
|
||||||
): Promise<Readable> {
|
): Promise<ReadableStream<Uint8Array>> {
|
||||||
const buffer = await this.pdf(options);
|
const buffer = await this.pdf(options);
|
||||||
try {
|
|
||||||
const {Readable} = await import('stream');
|
return new ReadableStream({
|
||||||
return Readable.from(buffer);
|
start(controller) {
|
||||||
} catch (error) {
|
controller.enqueue(buffer);
|
||||||
if (error instanceof TypeError) {
|
controller.close();
|
||||||
throw new Error(
|
},
|
||||||
'Can only pass a file path in a Node-like environment.'
|
});
|
||||||
);
|
|
||||||
}
|
|
||||||
throw error;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override async _screenshot(
|
override async _screenshot(
|
||||||
|
@ -4,8 +4,6 @@
|
|||||||
* SPDX-License-Identifier: Apache-2.0
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import type {Readable} from 'stream';
|
|
||||||
|
|
||||||
import type {Protocol} from 'devtools-protocol';
|
import type {Protocol} from 'devtools-protocol';
|
||||||
|
|
||||||
import {firstValueFrom, from, raceWith} from '../../third_party/rxjs/rxjs.js';
|
import {firstValueFrom, from, raceWith} from '../../third_party/rxjs/rxjs.js';
|
||||||
@ -1105,7 +1103,9 @@ export class CdpPage extends Page {
|
|||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
|
|
||||||
override async createPDFStream(options: PDFOptions = {}): Promise<Readable> {
|
override async createPDFStream(
|
||||||
|
options: PDFOptions = {}
|
||||||
|
): Promise<ReadableStream<Uint8Array>> {
|
||||||
const {timeout: ms = this._timeoutSettings.timeout()} = options;
|
const {timeout: ms = this._timeoutSettings.timeout()} = options;
|
||||||
const {
|
const {
|
||||||
landscape,
|
landscape,
|
||||||
|
@ -5,13 +5,10 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import type FS from 'fs/promises';
|
import type FS from 'fs/promises';
|
||||||
import type {Readable} from 'stream';
|
|
||||||
|
|
||||||
import {map, NEVER, Observable, timer} from '../../third_party/rxjs/rxjs.js';
|
import {map, NEVER, Observable, timer} from '../../third_party/rxjs/rxjs.js';
|
||||||
import type {CDPSession} from '../api/CDPSession.js';
|
import type {CDPSession} from '../api/CDPSession.js';
|
||||||
import {isNode} from '../environment.js';
|
|
||||||
import {assert} from '../util/assert.js';
|
import {assert} from '../util/assert.js';
|
||||||
import {isErrorLike} from '../util/ErrorLike.js';
|
|
||||||
|
|
||||||
import {debug} from './Debug.js';
|
import {debug} from './Debug.js';
|
||||||
import {TimeoutError} from './Errors.js';
|
import {TimeoutError} from './Errors.js';
|
||||||
@ -209,69 +206,74 @@ export async function importFSPromises(): Promise<typeof FS> {
|
|||||||
* @internal
|
* @internal
|
||||||
*/
|
*/
|
||||||
export async function getReadableAsBuffer(
|
export async function getReadableAsBuffer(
|
||||||
readable: Readable,
|
readable: ReadableStream<Uint8Array>,
|
||||||
path?: string
|
path?: string
|
||||||
): Promise<Buffer | null> {
|
): Promise<Buffer | null> {
|
||||||
const buffers = [];
|
const buffers = [];
|
||||||
|
const reader = readable.getReader();
|
||||||
if (path) {
|
if (path) {
|
||||||
const fs = await importFSPromises();
|
const fs = await importFSPromises();
|
||||||
const fileHandle = await fs.open(path, 'w+');
|
const fileHandle = await fs.open(path, 'w+');
|
||||||
try {
|
try {
|
||||||
for await (const chunk of readable) {
|
while (true) {
|
||||||
buffers.push(chunk);
|
const {done, value} = await reader.read();
|
||||||
await fileHandle.writeFile(chunk);
|
if (done) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
buffers.push(value);
|
||||||
|
await fileHandle.writeFile(value);
|
||||||
}
|
}
|
||||||
} finally {
|
} finally {
|
||||||
await fileHandle.close();
|
await fileHandle.close();
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
for await (const chunk of readable) {
|
while (true) {
|
||||||
buffers.push(chunk);
|
const {done, value} = await reader.read();
|
||||||
|
if (done) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
buffers.push(value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
return Buffer.concat(buffers);
|
return Buffer.concat(buffers);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
debugError(error);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @internal
|
||||||
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @internal
|
* @internal
|
||||||
*/
|
*/
|
||||||
export async function getReadableFromProtocolStream(
|
export async function getReadableFromProtocolStream(
|
||||||
client: CDPSession,
|
client: CDPSession,
|
||||||
handle: string
|
handle: string
|
||||||
): Promise<Readable> {
|
): Promise<ReadableStream<Uint8Array>> {
|
||||||
// TODO: Once Node 18 becomes the lowest supported version, we can migrate to
|
return new ReadableStream({
|
||||||
// ReadableStream.
|
async pull(controller) {
|
||||||
if (!isNode) {
|
function getUnit8Array(data: string, isBase64: boolean): Uint8Array {
|
||||||
throw new Error('Cannot create a stream outside of Node.js environment.');
|
if (isBase64) {
|
||||||
}
|
return Uint8Array.from(atob(data), m => {
|
||||||
|
return m.codePointAt(0)!;
|
||||||
const {Readable} = await import('stream');
|
});
|
||||||
|
}
|
||||||
let eof = false;
|
const encoder = new TextEncoder();
|
||||||
return new Readable({
|
return encoder.encode(data);
|
||||||
async read(size: number) {
|
|
||||||
if (eof) {
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
const {data, base64Encoded, eof} = await client.send('IO.read', {
|
||||||
const response = await client.send('IO.read', {handle, size});
|
handle,
|
||||||
this.push(response.data, response.base64Encoded ? 'base64' : undefined);
|
});
|
||||||
if (response.eof) {
|
|
||||||
eof = true;
|
controller.enqueue(getUnit8Array(data, base64Encoded ?? false));
|
||||||
await client.send('IO.close', {handle});
|
if (eof) {
|
||||||
this.push(null);
|
await client.send('IO.close', {handle});
|
||||||
}
|
controller.close();
|
||||||
} catch (error) {
|
|
||||||
if (isErrorLike(error)) {
|
|
||||||
this.destroy(error);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
throw error;
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
@ -1964,9 +1964,15 @@ describe('Page', function () {
|
|||||||
|
|
||||||
const stream = await page.createPDFStream();
|
const stream = await page.createPDFStream();
|
||||||
let size = 0;
|
let size = 0;
|
||||||
for await (const chunk of stream) {
|
const reader = stream.getReader();
|
||||||
size += chunk.length;
|
while (true) {
|
||||||
|
const {done, value} = await reader.read();
|
||||||
|
if (done) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
size += value.length;
|
||||||
}
|
}
|
||||||
|
|
||||||
expect(size).toBeGreaterThan(0);
|
expect(size).toBeGreaterThan(0);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user