Skip to content

Commit 63b3e10

Browse files
committed
feat: playwright build extension
1 parent 983bb41 commit 63b3e10

File tree

2 files changed

+206
-1
lines changed

2 files changed

+206
-1
lines changed

packages/build/package.json

+16-1
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,8 @@
2929
"./extensions/prisma": "./src/extensions/prisma.ts",
3030
"./extensions/audioWaveform": "./src/extensions/audioWaveform.ts",
3131
"./extensions/typescript": "./src/extensions/typescript.ts",
32-
"./extensions/puppeteer": "./src/extensions/puppeteer.ts"
32+
"./extensions/puppeteer": "./src/extensions/puppeteer.ts",
33+
"./extensions/playwright": "./src/extensions/playwright.ts"
3334
},
3435
"sourceDialects": [
3536
"@triggerdotdev/source"
@@ -57,6 +58,9 @@
5758
],
5859
"extensions/puppeteer": [
5960
"dist/commonjs/extensions/puppeteer.d.ts"
61+
],
62+
"extensions/playwright": [
63+
"dist/commonjs/extensions/playwright.d.ts"
6064
]
6165
}
6266
},
@@ -173,6 +177,17 @@
173177
"types": "./dist/commonjs/extensions/puppeteer.d.ts",
174178
"default": "./dist/commonjs/extensions/puppeteer.js"
175179
}
180+
},
181+
"./extensions/playwright": {
182+
"import": {
183+
"@triggerdotdev/source": "./src/extensions/playwright.ts",
184+
"types": "./dist/esm/extensions/playwright.d.ts",
185+
"default": "./dist/esm/extensions/playwright.js"
186+
},
187+
"require": {
188+
"types": "./dist/commonjs/extensions/playwright.d.ts",
189+
"default": "./dist/commonjs/extensions/playwright.js"
190+
}
176191
}
177192
},
178193
"main": "./dist/commonjs/index.js",
+190
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,190 @@
1+
import type { BuildContext, BuildExtension } from "@trigger.dev/core/v3/build";
2+
3+
type PlaywrightBrowser = "chromium" | "firefox" | "webkit";
4+
5+
interface PlaywrightExtensionOptions {
6+
/**
7+
* Browsers to install. Select only needed browsers to optimize build time and size.
8+
* @default ["chromium"]
9+
*/
10+
browsers?: PlaywrightBrowser[];
11+
12+
/**
13+
* Whether to support non-headless mode.
14+
* @default true
15+
*/
16+
headless?: boolean;
17+
}
18+
19+
/**
20+
* Creates a Playwright extension for trigger.dev
21+
* @param options Configuration options
22+
*/
23+
export function playwright(options: PlaywrightExtensionOptions = {}) {
24+
return new PlaywrightExtension(options);
25+
}
26+
27+
class PlaywrightExtension implements BuildExtension {
28+
public readonly name = "PlaywrightExtension";
29+
private readonly options: Required<PlaywrightExtensionOptions>;
30+
31+
constructor({ browsers = ["chromium"], headless = true }: PlaywrightExtensionOptions = {}) {
32+
if (browsers && browsers.length === 0) {
33+
throw new Error("At least one browser must be specified");
34+
}
35+
this.options = { browsers, headless };
36+
}
37+
38+
onBuildComplete(context: BuildContext) {
39+
if (context.target === "dev") return;
40+
41+
context.logger.debug(
42+
`Adding ${this.name} to the build with browsers: ${this.options.browsers.join(", ")}`
43+
);
44+
45+
const instructions: string[] = [
46+
// Base dependencies
47+
`RUN apt-get update && apt-get install -y --no-install-recommends \
48+
curl \
49+
unzip \
50+
jq \
51+
grep \
52+
sed \
53+
npm \
54+
&& apt-get clean && rm -rf /var/lib/apt/lists/*`,
55+
56+
// Install Playwright globally
57+
`RUN npm install -g playwright`,
58+
];
59+
60+
// Browser-specific dependencies
61+
const chromiumDeps = [
62+
"libnspr4",
63+
"libatk1.0-0",
64+
"libatk-bridge2.0-0",
65+
"libatspi2.0-0",
66+
"libasound2",
67+
"libnss3",
68+
"libxcomposite1",
69+
"libxdamage1",
70+
"libxfixes3",
71+
"libxrandr2",
72+
"libgbm1",
73+
"libxkbcommon0",
74+
];
75+
76+
const firefoxDeps = [
77+
"libgtk-3.0",
78+
"libgtk-4-1",
79+
"libgtk-4-common",
80+
"libgtk-4-dev",
81+
"libgtk-4-doc",
82+
"libasound2",
83+
];
84+
85+
const webkitDeps = [
86+
"libenchant-2-2",
87+
"libgl1",
88+
"libgles2",
89+
"libgstreamer-gl1.0-0",
90+
"libgstreamer-plugins-base1.0-0",
91+
"libgstreamer-plugins-bad1.0-0",
92+
"libharfbuzz-icu0",
93+
"libhyphen0",
94+
"libicu72",
95+
"libjpeg-dev",
96+
"libopenjp2-7",
97+
"libopus0",
98+
"libpng-dev",
99+
"libsecret-1-0",
100+
"libvpx7",
101+
"libwebp7",
102+
"libwoff1",
103+
"libx11-6",
104+
"libxcomposite1",
105+
"libxdamage1",
106+
"libxrender1",
107+
"libxt6",
108+
"libgtk-4-1",
109+
"libgraphene-1.0-0",
110+
"libxslt1.1",
111+
"libevent-2.1-7",
112+
"libmanette-0.2-0",
113+
"libwebpdemux2",
114+
"libwebpmux3",
115+
"libatomic1",
116+
"libavif15",
117+
"libx264-dev",
118+
"flite",
119+
"libatk1.0-0",
120+
"libatk-bridge2.0-0",
121+
];
122+
123+
const deps = [];
124+
if (this.options.browsers.includes("chromium")) deps.push(...chromiumDeps);
125+
if (this.options.browsers.includes("firefox")) deps.push(...firefoxDeps);
126+
if (this.options.browsers.includes("webkit")) deps.push(...webkitDeps);
127+
128+
const uniqueDeps = [...new Set(deps)];
129+
130+
if (uniqueDeps.length > 0) {
131+
instructions.push(
132+
`RUN apt-get update && apt-get install -y --no-install-recommends ${uniqueDeps.join(" ")} \
133+
&& apt-get clean && rm -rf /var/lib/apt/lists/*`
134+
);
135+
}
136+
137+
// Setup Playwright browsers
138+
instructions.push(`RUN mkdir -p /ms-playwright`);
139+
instructions.push(`RUN npx playwright install --dry-run > /tmp/browser-info.txt`);
140+
141+
this.options.browsers.forEach((browser) => {
142+
const browserType = browser === "chromium" ? "chromium-headless-shell" : browser;
143+
144+
instructions.push(
145+
`RUN grep -A5 "browser: ${browserType}" /tmp/browser-info.txt > /tmp/${browser}-info.txt`,
146+
147+
`RUN INSTALL_DIR=$(grep "Install location:" /tmp/${browser}-info.txt | cut -d':' -f2- | xargs) && \
148+
DIR_NAME=$(basename "$INSTALL_DIR") && \
149+
MS_DIR="/ms-playwright/$DIR_NAME" && \
150+
mkdir -p "$MS_DIR"`,
151+
152+
`RUN DOWNLOAD_URL=$(grep "Download url:" /tmp/${browser}-info.txt | cut -d':' -f2- | xargs | sed "s/mac-arm64/linux/g" | sed "s/mac-15-arm64/ubuntu-20.04/g") && \
153+
echo "Downloading ${browser} from $DOWNLOAD_URL" && \
154+
curl -L -o /tmp/${browser}.zip "$DOWNLOAD_URL" && \
155+
unzip -q /tmp/${browser}.zip -d "/ms-playwright/$(basename $(grep "Install location:" /tmp/${browser}-info.txt | cut -d':' -f2- | xargs))" && \
156+
chmod -R +x "/ms-playwright/$(basename $(grep "Install location:" /tmp/${browser}-info.txt | cut -d':' -f2- | xargs))" && \
157+
rm /tmp/${browser}.zip`
158+
);
159+
});
160+
161+
// Environment variables
162+
const envVars: Record<string, string> = {
163+
PLAYWRIGHT_BROWSERS_PATH: "/ms-playwright",
164+
PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD: "1",
165+
PLAYWRIGHT_SKIP_BROWSER_VALIDATION: "1",
166+
};
167+
168+
if (!this.options.headless) {
169+
instructions.push(
170+
`RUN echo '#!/bin/sh' > /usr/local/bin/xvfb-exec`,
171+
`RUN echo 'Xvfb :99 -screen 0 1024x768x24 &' >> /usr/local/bin/xvfb-exec`,
172+
`RUN echo 'exec "$@"' >> /usr/local/bin/xvfb-exec`,
173+
`RUN chmod +x /usr/local/bin/xvfb-exec`
174+
);
175+
176+
envVars.DISPLAY = ":99";
177+
}
178+
179+
context.addLayer({
180+
id: "playwright",
181+
image: {
182+
instructions,
183+
},
184+
deploy: {
185+
env: envVars,
186+
override: true,
187+
},
188+
});
189+
}
190+
}

0 commit comments

Comments
 (0)