From 8a8f357d1bbce21574dcc1674954f0640cb2b3db Mon Sep 17 00:00:00 2001 From: Yury Semikhatsky Date: Thu, 18 Jun 2026 14:41:04 -0700 Subject: [PATCH 1/4] feat(driver): bundle playwright-core in driver, keep only Node.js in driver-bundle Move the platform-independent playwright-core package and the DriverJar loader from driver-bundle into the always-present driver module. driver-bundle now contains only the Node.js binaries, one per platform. DriverJar assembles the driver directory from two classpath roots: the shared package (driver module) and the host platform's Node.js (driver-bundle), the latter skipped when a preinstalled Node.js is configured. This lets a user with a preinstalled Node.js (PLAYWRIGHT_NODEJS_PATH) exclude the driver-bundle dependency entirely. --- CONTRIBUTING.md | 2 +- driver-bundle/pom.xml | 23 ++------ driver/pom.xml | 18 +++++- .../playwright/impl/driver/Driver.java | 5 +- .../playwright/impl/driver/jar/DriverJar.java | 29 +++++++--- driver/src/main/resources/.gitignore | 2 + .../impl/driver/jar/TestInstall.java | 0 scripts/download_driver.sh | 56 ++++++++++--------- 8 files changed, 80 insertions(+), 55 deletions(-) rename {driver-bundle => driver}/src/main/java/com/microsoft/playwright/impl/driver/jar/DriverJar.java (85%) create mode 100644 driver/src/main/resources/.gitignore rename {driver-bundle => playwright}/src/test/java/com/microsoft/playwright/impl/driver/jar/TestInstall.java (100%) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index cd67302b2..67038419a 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -20,7 +20,7 @@ git clone https://github.com/microsoft/playwright-java cd playwright-java ``` -2. Run the following script to download and assemble the Playwright driver for all platforms into `driver-bundle/src/main/resources/driver/` directory (browser binaries for Chromium, Firefox and WebKit will be automatically downloaded later on first Playwright run). +2. Run the following script to download and assemble the Playwright driver. The platform-independent `playwright-core` package is assembled once into `driver/src/main/resources/driver/package/`, and the Node.js binary for each platform into `driver-bundle/src/main/resources/driver//` (browser binaries for Chromium, Firefox and WebKit will be automatically downloaded later on first Playwright run). ```bash scripts/download_driver.sh diff --git a/driver-bundle/pom.xml b/driver-bundle/pom.xml index df02c5c16..98ec27a97 100644 --- a/driver-bundle/pom.xml +++ b/driver-bundle/pom.xml @@ -10,28 +10,17 @@ driver-bundle - Playwright - Drivers For All Platforms + Playwright - Node.js For All Platforms - This module includes Playwright driver and related utilities for all supported platforms. - It is intended to be used on the systems where Playwright driver is not preinstalled. + This module bundles the Node.js binaries needed to run the Playwright driver on every supported + platform. The platform-independent driver code lives in the driver module. It is intended for + systems where Node.js is not preinstalled; when Node.js is available on the host (see the + PLAYWRIGHT_NODEJS_PATH environment variable) this dependency can be excluded. - - - com.microsoft.playwright - driver - ${project.version} - compile - - - org.junit.jupiter - junit-jupiter-engine - - - - org.apache.maven.plugins diff --git a/driver/pom.xml b/driver/pom.xml index 1f1e7df9d..f32abceb6 100644 --- a/driver/pom.xml +++ b/driver/pom.xml @@ -12,7 +12,9 @@ driver Playwright - Driver - This module provides API for discovery and launching of Playwright driver. + This module provides the API for discovery and launching of the Playwright driver. It also + bundles the platform-independent driver code (the playwright-core package); the matching + Node.js binary is provided by the driver-bundle module or by a preinstalled Node.js. @@ -21,4 +23,18 @@ junit-jupiter-engine + + + + + + org.apache.maven.plugins + maven-source-plugin + + true + + + + diff --git a/driver/src/main/java/com/microsoft/playwright/impl/driver/Driver.java b/driver/src/main/java/com/microsoft/playwright/impl/driver/Driver.java index 092d290ab..9b71b2cbc 100644 --- a/driver/src/main/java/com/microsoft/playwright/impl/driver/Driver.java +++ b/driver/src/main/java/com/microsoft/playwright/impl/driver/Driver.java @@ -25,8 +25,9 @@ /** * This class provides access to playwright-cli. It can be either preinstalled - * in the host system and its path is passed as a system property or it can be - * loaded from the driver-bundle module if that module is in the classpath. + * in the host system and its path is passed as a system property, or it can be + * loaded from the classpath: the platform-independent driver code ships in the + * driver module and the Node.js binary in the optional driver-bundle module. */ public abstract class Driver { protected final Map env = new LinkedHashMap<>(System.getenv()); diff --git a/driver-bundle/src/main/java/com/microsoft/playwright/impl/driver/jar/DriverJar.java b/driver/src/main/java/com/microsoft/playwright/impl/driver/jar/DriverJar.java similarity index 85% rename from driver-bundle/src/main/java/com/microsoft/playwright/impl/driver/jar/DriverJar.java rename to driver/src/main/java/com/microsoft/playwright/impl/driver/jar/DriverJar.java index 654ff6732..b7e7da6a1 100644 --- a/driver-bundle/src/main/java/com/microsoft/playwright/impl/driver/jar/DriverJar.java +++ b/driver/src/main/java/com/microsoft/playwright/impl/driver/jar/DriverJar.java @@ -119,7 +119,26 @@ public static URI getDriverResourceURI() throws URISyntaxException { } void extractDriverToTempDir() throws URISyntaxException, IOException { - URI originalUri = getDriverResourceURI(); + // The platform-independent driver code (the playwright-core package) ships in the driver + // module and is always on the classpath. + extractResourceToDir("driver/package", driverTempDir.resolve("package")); + // The Node.js binary is only needed when it is not preinstalled on the host. It ships in the + // optional driver-bundle module, one binary per platform; a user with a preinstalled Node.js + // can exclude that dependency entirely. + if (preinstalledNodePath == null) { + String platformResource = "driver/" + platformDir(); + if (DriverJar.class.getClassLoader().getResource(platformResource) == null) { + throw new RuntimeException("Failed to find the bundled Node.js for platform '" + platformDir() + + "'. Add the com.microsoft.playwright:driver-bundle dependency, or set the " + + PLAYWRIGHT_NODEJS_PATH + " environment variable (or the playwright.nodejs.path system " + + "property) to point at a preinstalled Node.js."); + } + extractResourceToDir(platformResource, driverTempDir); + } + } + + private void extractResourceToDir(String resourcePath, Path destDir) throws URISyntaxException, IOException { + URI originalUri = DriverJar.class.getClassLoader().getResource(resourcePath).toURI(); URI uri = maybeExtractNestedJar(originalUri); // Create zip filesystem if loading from jar. @@ -131,14 +150,8 @@ void extractDriverToTempDir() throws URISyntaxException, IOException { // See https://github.com/microsoft/playwright-java/issues/306 Path srcRootDefaultFs = Paths.get(srcRoot.toString()); Files.walk(srcRoot).forEach(fromPath -> { - if (preinstalledNodePath != null) { - String fileName = fromPath.getFileName().toString(); - if ("node.exe".equals(fileName) || "node".equals(fileName)) { - return; - } - } Path relative = srcRootDefaultFs.relativize(Paths.get(fromPath.toString())); - Path toPath = driverTempDir.resolve(relative.toString()); + Path toPath = destDir.resolve(relative.toString()); try { if (Files.isDirectory(fromPath)) { Files.createDirectories(toPath); diff --git a/driver/src/main/resources/.gitignore b/driver/src/main/resources/.gitignore new file mode 100644 index 000000000..045408616 --- /dev/null +++ b/driver/src/main/resources/.gitignore @@ -0,0 +1,2 @@ +driver/ +local-driver/ diff --git a/driver-bundle/src/test/java/com/microsoft/playwright/impl/driver/jar/TestInstall.java b/playwright/src/test/java/com/microsoft/playwright/impl/driver/jar/TestInstall.java similarity index 100% rename from driver-bundle/src/test/java/com/microsoft/playwright/impl/driver/jar/TestInstall.java rename to playwright/src/test/java/com/microsoft/playwright/impl/driver/jar/TestInstall.java diff --git a/scripts/download_driver.sh b/scripts/download_driver.sh index dba7f75d2..29ba05b6c 100755 --- a/scripts/download_driver.sh +++ b/scripts/download_driver.sh @@ -9,9 +9,11 @@ cd "$(dirname $0)" if [[ ($1 == '-h') || ($1 == '--help') ]]; then echo "" echo "This script downloads and assembles the Playwright driver for all platforms." - echo "Each driver is assembled from the 'playwright-core' npm package and the matching" - echo "Node.js binary from https://nodejs.org, the same way the upstream Playwright build" - echo "does it. The result is put under 'driver-bundle/src/main/resources/driver'." + echo "The platform-independent 'playwright-core' npm package is assembled once into the driver" + echo "module ('driver/src/main/resources/driver/package'), and the matching Node.js binary from" + echo "https://nodejs.org for each platform goes into the driver-bundle module" + echo "('driver-bundle/src/main/resources/driver/'), the same way the upstream" + echo "Playwright build does it." echo "" echo "Usage: scripts/download_driver.sh [option]" echo "" @@ -56,20 +58,25 @@ echo "Driver version: $DRIVER_VERSION" echo "Upstream commit: $GIT_HEAD" echo "Node.js version: $NODE_VERSION" -cd ../driver-bundle/src/main/resources +# The platform-independent driver code (playwright-core) is assembled once into the driver module; +# the Node.js binary for each platform is assembled into the driver-bundle module. See issue #1196. +ROOT="$(cd .. && pwd)" +CORE_DEST="$ROOT/driver/src/main/resources/driver" +NODE_DEST="$ROOT/driver-bundle/src/main/resources/driver" -if [[ -d 'driver' ]]; then - echo "Deleting existing drivers from $(pwd)" - rm -rf driver -fi - -mkdir -p driver -cd driver +TMP_DIR="$(mktemp -d)" +trap 'rm -rf "$TMP_DIR"' EXIT -# Download the platform-independent driver package (playwright-core) once. -CORE_TGZ="$(pwd)/playwright-core-$DRIVER_VERSION.tgz" +# 1. playwright-core package -> driver module (once, shared by every platform). +echo "Assembling playwright-core package to $CORE_DEST/package" +rm -rf "$CORE_DEST/package" +mkdir -p "$CORE_DEST" +CORE_TGZ="$TMP_DIR/playwright-core-$DRIVER_VERSION.tgz" download "https://registry.npmjs.org/playwright-core/-/playwright-core-$DRIVER_VERSION.tgz" "$CORE_TGZ" +# The npm tarball has a top-level package/ directory, so this creates $CORE_DEST/package. +tar -xzf "$CORE_TGZ" -C "$CORE_DEST" +# 2. Node.js binary for each platform -> driver-bundle module. # :: for ENTRY in \ "mac:darwin-x64:tar.gz" \ @@ -79,28 +86,25 @@ for ENTRY in \ "win32_x64:win-x64:zip" do IFS=':' read -r PLATFORM NODE_SUFFIX ARCHIVE <<< "$ENTRY" - echo "Assembling driver for $PLATFORM to $(pwd)/$PLATFORM" - mkdir "$PLATFORM" + DEST="$NODE_DEST/$PLATFORM" + echo "Assembling Node.js for $PLATFORM to $DEST" + rm -rf "$DEST" + mkdir -p "$DEST" - # 1. playwright-core package contents -> $PLATFORM/package - tar -xzf "$CORE_TGZ" -C "$PLATFORM" - - # 2. Node.js binary and its license from the official Node.js distribution. + # Node.js binary and its license from the official Node.js distribution. NODE_DIR="node-v$NODE_VERSION-$NODE_SUFFIX" - NODE_ARCHIVE="$NODE_DIR.$ARCHIVE" + NODE_ARCHIVE="$TMP_DIR/$NODE_DIR.$ARCHIVE" download "https://nodejs.org/dist/v$NODE_VERSION/$NODE_DIR.$ARCHIVE" "$NODE_ARCHIVE" if [[ $ARCHIVE == "zip" ]]; then - unzip -joq "$NODE_ARCHIVE" "$NODE_DIR/node.exe" -d "$PLATFORM" - unzip -joq "$NODE_ARCHIVE" "$NODE_DIR/LICENSE" -d "$PLATFORM" + unzip -joq "$NODE_ARCHIVE" "$NODE_DIR/node.exe" -d "$DEST" + unzip -joq "$NODE_ARCHIVE" "$NODE_DIR/LICENSE" -d "$DEST" else - tar -xzf "$NODE_ARCHIVE" -C "$PLATFORM" --strip-components=2 "$NODE_DIR/bin/node" - tar -xzf "$NODE_ARCHIVE" -C "$PLATFORM" --strip-components=1 "$NODE_DIR/LICENSE" + tar -xzf "$NODE_ARCHIVE" -C "$DEST" --strip-components=2 "$NODE_DIR/bin/node" + tar -xzf "$NODE_ARCHIVE" -C "$DEST" --strip-components=1 "$NODE_DIR/LICENSE" fi rm -f "$NODE_ARCHIVE" done -rm -f "$CORE_TGZ" - echo "" echo "All drivers have been successfully assembled." echo "" From 37d00f01e0da1e16e6fbaa948be7d6c675d39c58 Mon Sep 17 00:00:00 2001 From: Yury Semikhatsky Date: Thu, 18 Jun 2026 14:50:39 -0700 Subject: [PATCH 2/4] chore(driver): drop redundant comments and tighten module descriptions --- driver-bundle/pom.xml | 6 ++---- driver/pom.xml | 5 ++--- .../com/microsoft/playwright/impl/driver/jar/DriverJar.java | 5 ----- 3 files changed, 4 insertions(+), 12 deletions(-) diff --git a/driver-bundle/pom.xml b/driver-bundle/pom.xml index 98ec27a97..2aa0b02e2 100644 --- a/driver-bundle/pom.xml +++ b/driver-bundle/pom.xml @@ -12,10 +12,8 @@ driver-bundle Playwright - Node.js For All Platforms - This module bundles the Node.js binaries needed to run the Playwright driver on every supported - platform. The platform-independent driver code lives in the driver module. It is intended for - systems where Node.js is not preinstalled; when Node.js is available on the host (see the - PLAYWRIGHT_NODEJS_PATH environment variable) this dependency can be excluded. + Node.js binaries for the Playwright driver on every supported platform. Can be excluded when + Node.js is preinstalled on the host (see PLAYWRIGHT_NODEJS_PATH). diff --git a/driver/pom.xml b/driver/pom.xml index f32abceb6..fc307e9fc 100644 --- a/driver/pom.xml +++ b/driver/pom.xml @@ -12,9 +12,8 @@ driver Playwright - Driver - This module provides the API for discovery and launching of the Playwright driver. It also - bundles the platform-independent driver code (the playwright-core package); the matching - Node.js binary is provided by the driver-bundle module or by a preinstalled Node.js. + API for launching the Playwright driver. Bundles the platform-independent playwright-core + package; the Node.js binary comes from the driver-bundle module or a preinstalled Node.js. diff --git a/driver/src/main/java/com/microsoft/playwright/impl/driver/jar/DriverJar.java b/driver/src/main/java/com/microsoft/playwright/impl/driver/jar/DriverJar.java index b7e7da6a1..060bd9c2d 100644 --- a/driver/src/main/java/com/microsoft/playwright/impl/driver/jar/DriverJar.java +++ b/driver/src/main/java/com/microsoft/playwright/impl/driver/jar/DriverJar.java @@ -119,12 +119,7 @@ public static URI getDriverResourceURI() throws URISyntaxException { } void extractDriverToTempDir() throws URISyntaxException, IOException { - // The platform-independent driver code (the playwright-core package) ships in the driver - // module and is always on the classpath. extractResourceToDir("driver/package", driverTempDir.resolve("package")); - // The Node.js binary is only needed when it is not preinstalled on the host. It ships in the - // optional driver-bundle module, one binary per platform; a user with a preinstalled Node.js - // can exclude that dependency entirely. if (preinstalledNodePath == null) { String platformResource = "driver/" + platformDir(); if (DriverJar.class.getClassLoader().getResource(platformResource) == null) { From 995c275c04fa91b553302a92f5f1ff26c589e307 Mon Sep 17 00:00:00 2001 From: Yury Semikhatsky Date: Thu, 18 Jun 2026 14:53:09 -0700 Subject: [PATCH 3/4] chore(driver): remove playwright-core tarball right after extracting it --- scripts/download_driver.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/scripts/download_driver.sh b/scripts/download_driver.sh index 29ba05b6c..c72dad687 100755 --- a/scripts/download_driver.sh +++ b/scripts/download_driver.sh @@ -75,6 +75,7 @@ CORE_TGZ="$TMP_DIR/playwright-core-$DRIVER_VERSION.tgz" download "https://registry.npmjs.org/playwright-core/-/playwright-core-$DRIVER_VERSION.tgz" "$CORE_TGZ" # The npm tarball has a top-level package/ directory, so this creates $CORE_DEST/package. tar -xzf "$CORE_TGZ" -C "$CORE_DEST" +rm -f "$CORE_TGZ" # 2. Node.js binary for each platform -> driver-bundle module. # :: From 5e2d2f8516779214d8e57c38029dbae59ad01f2d Mon Sep 17 00:00:00 2001 From: Yury Semikhatsky Date: Fri, 19 Jun 2026 09:28:39 -0700 Subject: [PATCH 4/4] fix(driver): drop stale driver-bundle test copy from local-installation script TestInstall moved from driver-bundle/src/test to playwright/src/test, which the script already copies. The obsolete driver-bundle/src/test copy broke the Docker test job (the now-empty directory is absent on a fresh checkout). --- tools/test-local-installation/create_project_and_run_tests.sh | 1 - 1 file changed, 1 deletion(-) diff --git a/tools/test-local-installation/create_project_and_run_tests.sh b/tools/test-local-installation/create_project_and_run_tests.sh index 99b2570bd..12864b84c 100755 --- a/tools/test-local-installation/create_project_and_run_tests.sh +++ b/tools/test-local-installation/create_project_and_run_tests.sh @@ -10,7 +10,6 @@ cd "$(dirname $0)" PROJECT_DIR=$(mktemp -d) echo "Creating project in $PROJECT_DIR" cp -R . $PROJECT_DIR -cp -R ../../driver-bundle/src/test/ $PROJECT_DIR/src/ cp -R ../../playwright/src/test/ $PROJECT_DIR/src/ cd $PROJECT_DIR