I’m working on an Electron-Vue3 application that uses Playwright for web scraping. Everything works fine during development, but after I build the installer with electron-builder, the browser stops functioning.
The only solution I’ve found is to explicitly define the browser’s executablePath pointing to a local directory. However, when I try to make the path relative to the app (by including the browser within the app itself), it fails to work after installation.
What I’m looking for:
Is there a way to include the Chrome browser inside the installer/compiled app (like within the /resources directory) and configure Playwright to use that browser from within the installed app?
Addiocional Context:
package.json
{
"name": "playpix-brayo",
"version": "1.0.0",
"description": "playpix",
"main": "main/main.js",
"scripts": {
"dev": "node scripts/dev-server.js",
"build": "node scripts/build.js && electron-builder --config.extraMetadata.main=main/main.js --config.extraMetadata.buildDependencies.node-abi=108",
"build:win": "node scripts/build.js && electron-builder --win --config.extraMetadata.main=main/main.js --config.extraMetadata.buildDependencies.node-abi=108",
"build:mac": "node scripts/build.js && electron-builder --mac",
"build:linux": "node scripts/build.js && electron-builder --linux"
},
"repository": "https://github.com/deluze/electron-vue-template",
"author": {
"name": "Brayozin",
"url": "https://github.com/Brayozin"
},
"devDependencies": {
"@playwright/test": "1.47.1",
"@types/node": "22.5.0",
"@vitejs/plugin-vue": "^4.4.1",
"autoprefixer": "^10.4.20",
"chalk": "^4.1.2",
"chokidar": "^3.5.3",
"daisyui": "^4.12.10",
"electron": "^27.1.0",
"electron-builder": "^24.2.1",
"postcss": "^8.4.45",
"postcss-loader": "^8.1.1",
"tailwindcss": "^3.4.11",
"typescript": "^5.2.2",
"vite": "^4.5.0"
},
"dependencies": {
"@fortawesome/fontawesome-svg-core": "^6.6.0",
"@fortawesome/free-brands-svg-icons": "^6.6.0",
"@fortawesome/free-regular-svg-icons": "^6.6.0",
"@fortawesome/free-solid-svg-icons": "^6.6.0",
"@fortawesome/vue-fontawesome": "^3.0.8",
"axios": "^1.7.7",
"daisyui": "^4.12.10",
"playwright": "^1.47.1",
"playwright-chromium": "^1.47.1",
"sqlite3": "5.1.6",
"vite": "^2.9.18",
"vue": "3.3.8",
"vue-router": "^4.0.13",
"vuex": "^4.1.0"
}
}
build.js
const Path = require('path');
const Chalk = require('chalk');
const FileSystem = require('fs');
const Vite = require('vite');
const compileTs = require('./private/tsc');
function buildRenderer() {
return Vite.build({
configFile: Path.join(__dirname, '..', 'vite.config.js'),
base: './',
mode: 'production'
});
}
console.log(Chalk.blueBright('Transpiling renderer & main...'));
console.log(Chalk.blueBright('PATH: ', Path.join(__dirname, '..', 'build')));
function buildMain() {
const mainPath = Path.join(__dirname, '..', 'src', 'main');
return compileTs(mainPath);
}
function copyScrapperBrowser() {
const srcPath = Path.join(
__dirname,
"..",
"src",
"main",
"scrapper",
"browser"
);
const destPath = Path.join(
__dirname,
"..",
"dist",
"scrapper",
"browser"
);
FileSystem.mkdirSync(destPath, { recursive: true });
FileSystem.cpSync(srcPath, destPath, { recursive: true });
}
function copyMainJs() {
const srcPath = Path.join(__dirname, "..", "src", "main", "main.js");
const destPath = Path.join(__dirname, "..", "dist", "main", "main.js");
FileSystem.mkdirSync(Path.dirname(destPath), { recursive: true });
FileSystem.copyFileSync(srcPath, destPath);
}
FileSystem.rmSync(Path.join(__dirname, "..", "build"), {
recursive: true,
force: true,
});
console.log(Chalk.blueBright("Transpiling renderer & main..."));
console.log(Chalk.blueBright("PATH: ", Path.join(__dirname, "..", "build")));
FileSystem.rmSync(Path.join(__dirname, '..', 'build'), {
recursive: true,
force: true,
})
console.log(Chalk.blueBright('Transpiling renderer & main...'));
Promise.allSettled([
buildRenderer(),
buildMain(),
]).then(() => {
copyScrapperBrowser();
// copyMainJs();
console.log(Chalk.greenBright('Renderer & main successfully transpiled! (ready to be built with electron-builder)'));
});
ConfigBrowser src/main/scrapper/configBrowser.js
import { chromium } from "playwright";
import { login } from "./login.js";
import fs from "fs";
import path from "path";
const { app } = require("electron");
export async function configBrowser() {
const contextFilePath = "browserContext.json";
let context;
let page;
const executablePath = path.join(
appPath,
"scrapper",
"browser",
"chrome",
"chrome"
);
let actualDir = __dirname;
console.log("executablePath====================================");
console.log(executablePath);
console.log("====================================executablePath");
const browser = await chromium.launch({
headless: false,
devtools: true,
args: [
"--no-sandbox",
"--no-devtools",
"--disable-setuid-sandbox",
"--mute-audio",
],
executablePath: executablePath,
// executablePath: "./browser/chrome/chrome", // src/main/scrapper/browser/chrome/chrome
timeout: 60000, // Increase timeout to 60 seconds
});
if (fs.existsSync(contextFilePath)) {
console.log("oi1");
const contextData = fs.readFileSync(contextFilePath, "utf8");
context = await browser.newContext({
storageState: JSON.parse(contextData),
});
...