Skip to content

Instantly share code, notes, and snippets.

@jfrancos
Last active January 31, 2022 17:06
Show Gist options
  • Save jfrancos/d336f55beb8bf18d9e1cb59bca6d4ce2 to your computer and use it in GitHub Desktop.
Save jfrancos/d336f55beb8bf18d9e1cb59bca6d4ce2 to your computer and use it in GitHub Desktop.
Continuous lighthouse score updates for your vite project
#!/usr/bin/env npx ts-node --skip-project --transpile-only
import lighthouse from "lighthouse";
import { launch } from "chrome-launcher";
import { pick } from "lodash";
import { preview, build } from "vite";
import { mkdtemp, rm } from "fs/promises";
import { tmpdir } from "os";
import { join } from "path";
import { AddressInfo } from "net";
import { say } from "cfonts";
import { watch } from "chokidar";
type PreviewAndChromePorts = {
previewPort: number;
chromePort: number;
};
type FunctionNeedingPreviewAndChromePorts = (
lambdaOptions: PreviewAndChromePorts
) => void;
type FunctionNeedingTempDir = (tempDir: string) => void;
const audits = [
"first-contentful-paint",
"interactive",
"speed-index",
"total-blocking-time",
"largest-contentful-paint",
"cumulative-layout-shift",
];
const withTempDir = async (lambda: FunctionNeedingTempDir) => {
const tempDir = await mkdtemp(join(tmpdir(), "vite-lighthouse"));
await lambda(tempDir);
await rm(tempDir, { recursive: true });
};
const runLighthouse = async ({
previewPort,
chromePort,
}: PreviewAndChromePorts) => {
const options = { onlyCategories: ["performance"], port: chromePort };
const runnerResult = await lighthouse(
`http://localhost:${previewPort}`,
options
);
const performanceString = Object.values(pick(runnerResult.lhr.audits, audits))
.map((item) => `${item.title}: ${item.displayValue}`)
.join("\n");
console.log(performanceString);
const { score } = runnerResult.lhr.categories.performance;
printScore(score);
};
const printScore = (score: number) => {
let color = "red";
const roundedScore = Math.round(score * 1000) / 10;
if (roundedScore >= 90) color = "#FFA500";
if (roundedScore >= 99) color = "yellow";
if (roundedScore === 100) color = "green";
say(roundedScore + "%", { colors: [color, "#333"], align: "center" });
};
const withPreviewAndChromePorts = async (
lambda: FunctionNeedingPreviewAndChromePorts
) => {
await withTempDir(async (outDir) => {
const [_, { httpServer }, headlessChrome] = await Promise.all([
build({ build: { outDir }, logLevel: "error" }),
preview({ build: { outDir } }),
launch({chromeFlags: ['--headless']}),
]);
const { port: previewPort } = httpServer.address() as AddressInfo;
const { port: chromePort } = headlessChrome;
await lambda({ previewPort, chromePort });
await Promise.all([headlessChrome.kill(), httpServer.close()]);
});
};
withPreviewAndChromePorts(runLighthouse);
watch("src", { ignoreInitial: true }).on("all", async (event) => {
console.log("Updating ...")
await withPreviewAndChromePorts(runLighthouse);
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment