1
1
import {
2
- type BrowserContext ,
3
2
chromium ,
4
3
devices ,
5
4
type Page ,
6
5
type BrowserContextOptions ,
7
6
type Response ,
7
+ type Browser ,
8
8
} from "playwright" ;
9
9
import { PlaywrightBlocker } from "@cliqz/adblocker-playwright" ;
10
10
import { env } from "$env/dynamic/private" ;
11
+ import { logger } from "$lib/server/logger" ;
11
12
12
- // Singleton initialized by initPlaywrightService
13
- let playwrightService : Promise < { ctx : BrowserContext ; blocker : PlaywrightBlocker } > | undefined ;
14
-
15
- async function initPlaywrightService ( ) {
16
- if ( playwrightService ) return playwrightService ;
13
+ const blocker = await PlaywrightBlocker . fromPrebuiltAdsAndTracking ( fetch ) . then ( ( blker ) => {
14
+ const mostBlocked = blker . blockFonts ( ) . blockMedias ( ) . blockFrames ( ) . blockImages ( ) ;
15
+ if ( env . WEBSEARCH_JAVASCRIPT === "false" ) return mostBlocked . blockScripts ( ) ;
16
+ return mostBlocked ;
17
+ } ) ;
17
18
19
+ let browserSingleton : Promise < Browser > | undefined ;
20
+ async function getBrowser ( ) {
18
21
const browser = await chromium . launch ( { headless : true } ) ;
19
-
20
22
process . on ( "SIGINT" , ( ) => browser . close ( ) ) ;
23
+ browser . on ( "disconnected" , ( ) => {
24
+ logger . warn ( "Browser closed" ) ;
25
+ browserSingleton = undefined ;
26
+ } ) ;
27
+ return browser ;
28
+ }
29
+
30
+ async function getPlaywrightCtx ( ) {
31
+ if ( ! browserSingleton ) browserSingleton = getBrowser ( ) ;
32
+ const browser = await browserSingleton ;
21
33
22
34
const device = devices [ "Desktop Chrome" ] ;
23
35
const options : BrowserContextOptions = {
@@ -36,31 +48,26 @@ async function initPlaywrightService() {
36
48
timezoneId : "America/New_York" ,
37
49
locale : "en-US" ,
38
50
} ;
39
- const ctx = await browser . newContext ( options ) ;
40
- const blocker = await PlaywrightBlocker . fromPrebuiltAdsAndTracking ( fetch ) . then ( ( blker ) => {
41
- const mostBlocked = blker . blockFonts ( ) . blockMedias ( ) . blockFrames ( ) . blockImages ( ) ;
42
- if ( env . WEBSEARCH_JAVASCRIPT === "false" ) return mostBlocked . blockScripts ( ) ;
43
- return mostBlocked ;
44
- } ) ;
45
-
46
- // Clear the singleton when the context closes
47
- ctx . on ( "close" , ( ) => {
48
- playwrightService = undefined ;
49
- } ) ;
50
-
51
- return Object . freeze ( { ctx, blocker } ) ;
51
+ return browser . newContext ( options ) ;
52
52
}
53
53
54
- export async function loadPage ( url : string ) : Promise < { res ?: Response ; page : Page } > {
55
- if ( ! playwrightService ) playwrightService = initPlaywrightService ( ) ;
56
- const { ctx, blocker } = await playwrightService ;
54
+ export async function withPage < T > (
55
+ url : string ,
56
+ callback : ( page : Page , response ?: Response ) => Promise < T >
57
+ ) : Promise < T > {
58
+ const ctx = await getPlaywrightCtx ( ) ;
57
59
58
- const page = await ctx . newPage ( ) ;
59
- await blocker . enableBlockingInPage ( page ) ;
60
+ try {
61
+ const page = await ctx . newPage ( ) ;
62
+ await blocker . enableBlockingInPage ( page ) ;
60
63
61
- const res = await page . goto ( url , { waitUntil : "load" , timeout : 3500 } ) . catch ( ( ) => {
62
- console . warn ( `Failed to load page within 2s: ${ url } ` ) ;
63
- } ) ;
64
+ const res = await page . goto ( url , { waitUntil : "load" , timeout : 3500 } ) . catch ( ( ) => {
65
+ console . warn ( `Failed to load page within 2s: ${ url } ` ) ;
66
+ } ) ;
64
67
65
- return { res : res ?? undefined , page } ;
68
+ // await needed here so that we don't close the context before the callback is done
69
+ return await callback ( page , res ?? undefined ) ;
70
+ } finally {
71
+ ctx . close ( ) ;
72
+ }
66
73
}
0 commit comments