Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .puppeteerrc.cjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
module.exports = {
// We use the system Chrome instead
skipDownload: true,
};
14 changes: 7 additions & 7 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,14 +23,14 @@
"author": "netlify-labs",
"license": "MIT",
"dependencies": {
"chalk": "^4.1.0",
"chalk": "^5.6.2",
"chrome-launcher": "^0.15.0",
"compression": "^1.7.4",
"dotenv": "^16.0.0",
"express": "^4.17.1",
"html-minifier-terser": "^7.2.0",
"lighthouse": "^9.6.3",
"puppeteer": "^24.8.2"
"puppeteer": "^24.29.1"
},
"engines": {
"node": ">=18.14.0"
Expand All @@ -47,14 +47,14 @@
"@commitlint/cli": "^17.0.0",
"@commitlint/config-conventional": "^17.0.0",
"cypress": "^13.17.0",
"eslint": "^8.32.0",
"eslint-plugin-cypress": "^2.11.2",
"eslint-plugin-import": "^2.27.4",
"eslint": "^8.57.1",
"eslint-plugin-cypress": "^2.15.2",
"eslint-plugin-import": "^2.32.0",
"husky": "^8.0.1",
"jest": "^29.0.0",
"netlify-plugin-cypress": "^2.2.1",
"prettier": "^2.0.0",
"strip-ansi": "^7.0.1"
"prettier": "3.6.2",
"strip-ansi": "^7.1.2"
},
"husky": {
"hooks": {
Expand Down
19 changes: 16 additions & 3 deletions src/index.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { join } from 'path';
import { existsSync } from 'fs';
import { homedir } from 'os';
import { join } from 'path';

import * as dotenv from 'dotenv';

Expand All @@ -13,14 +14,26 @@ const puppeteerCacheDir = join(homedir(), '.cache', 'puppeteer');
const restorePuppeteerCache = async ({ utils } = {}) => {
console.log('Restoring Lighthouse cache...');
// Puppeteer relies on a global cache since v19.x, which otherwise would not be persisted in Netlify builds
// Note: We use system Chrome now (skipDownload: true), so this cache may be empty
await utils?.cache.restore(puppeteerCacheDir);
console.log('Lighthouse cache restored');
};

const persistPuppeteerCache = async ({ utils } = {}) => {
console.log('Persisting Lighthouse cache...');
await utils?.cache.save(puppeteerCacheDir);
console.log('Lighthouse cache persisted');
// Only cache if the directory exists and is not empty
// Since we use system Chrome (skipDownload: true), this may not exist
if (existsSync(puppeteerCacheDir)) {
try {
await utils?.cache.save(puppeteerCacheDir);
console.log('Lighthouse cache persisted');
} catch (error) {
// Non-critical error - cache persistence failed but build can continue
console.log('Note: Lighthouse cache persistence skipped');
}
} else {
console.log('Note: Lighthouse cache not found (using system Chrome)');
}
};

export default function lighthousePlugin(inputs) {
Expand Down
54 changes: 45 additions & 9 deletions src/run-lighthouse.js
Original file line number Diff line number Diff line change
@@ -1,16 +1,52 @@
import puppeteer from 'puppeteer';
import { execSync } from 'child_process';

import chromeLauncher from 'chrome-launcher';
import lighthouse from 'lighthouse';
import log from 'lighthouse-logger';
import chromeLauncher from 'chrome-launcher';
import puppeteer from 'puppeteer';

export const getBrowserPath = async () => {
const browser = await puppeteer.launch({
headless: 'new',
args: ['--no-sandbox', '--disable-gpu', '--disable-dev-shm-usage'],
});
const path = browser.process().spawnfile;
await browser.close();
return path;
// First, try to find Chrome using chromeLauncher
// This works well on CI environments like Netlify
try {
const installations = chromeLauncher.getChromePath();
if (installations) {
return installations;
}
} catch (error) {
// chromeLauncher.getChromePath() failed, continue to next method
}

// Try to find Chrome using common paths
try {
const chromePath = execSync(
'which google-chrome || which chromium-browser || which chromium',
{
encoding: 'utf8',
stdio: ['pipe', 'pipe', 'ignore'],
},
).trim();
if (chromePath) {
return chromePath;
}
} catch (error) {
// which command failed, continue to next method
}

// Fall back to using puppeteer's bundled Chrome (if available)
try {
const browser = await puppeteer.launch({

Check warning on line 38 in src/run-lighthouse.js

View workflow job for this annotation

GitHub Actions / test (21.x)

Caution: `puppeteer` also has a named export `launch`. Check if you meant to write `import {launch} from 'puppeteer'` instead

Check warning on line 38 in src/run-lighthouse.js

View workflow job for this annotation

GitHub Actions / test (18.x)

Caution: `puppeteer` also has a named export `launch`. Check if you meant to write `import {launch} from 'puppeteer'` instead

Check warning on line 38 in src/run-lighthouse.js

View workflow job for this annotation

GitHub Actions / test (20.x)

Caution: `puppeteer` also has a named export `launch`. Check if you meant to write `import {launch} from 'puppeteer'` instead
headless: 'new',
args: ['--no-sandbox', '--disable-gpu', '--disable-dev-shm-usage'],
});
const path = browser.process().spawnfile;
await browser.close();
return path;
} catch (error) {
throw new Error(
'Could not find Chrome. Please ensure Chrome is installed on the system.',
);
}
};

export const runLighthouse = async (browserPath, url, settings) => {
Expand Down
Loading
Loading