pagespeed-quest 0.6.0 → 0.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/build/adhoc.js +2 -2
- package/build/dependency.js +3 -2
- package/build/index.d.ts +1 -0
- package/build/index.js +2 -1
- package/build/lighthouse-digest.d.ts +12 -0
- package/build/lighthouse-digest.js +69 -0
- package/build/lighthouse-summary.d.ts +32 -0
- package/build/lighthouse-summary.js +80 -0
- package/build/lighthouse.d.ts +2 -1
- package/build/lighthouse.js +31 -1
- package/build/loadshow.d.ts +4 -2
- package/build/loadshow.js +31 -23
- package/package.json +6 -3
- package/scripts/postinstall.js +99 -0
package/build/adhoc.js
CHANGED
|
@@ -27,5 +27,5 @@ export async function playback() {
|
|
|
27
27
|
await execLighthouse({ url: proxy.entryUrl, proxyPort: proxy.port, headless: false, timeout: 60000 }, dependency);
|
|
28
28
|
});
|
|
29
29
|
}
|
|
30
|
-
|
|
31
|
-
//# sourceMappingURL=data:application/json;base64,
|
|
30
|
+
recording().then(playback);
|
|
31
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiYWRob2MuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi9zcmMvYWRob2MudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxHQUFHLE1BQU0sYUFBYSxDQUFBO0FBRTdCLE9BQU8sRUFBRSxVQUFVLEVBQUUsTUFBTSxpQkFBaUIsQ0FBQTtBQUM1QyxPQUFPLEVBQUUsbUJBQW1CLEVBQUUsTUFBTSxnQkFBZ0IsQ0FBQTtBQUNwRCxPQUFPLEVBQUUsY0FBYyxFQUFFLE1BQU0saUJBQWlCLENBQUE7QUFDaEQsT0FBTyxFQUFFLGlCQUFpQixFQUFFLE1BQU0sZUFBZSxDQUFBO0FBQ2pELE9BQU8sRUFBRSxrQkFBa0IsRUFBRSxNQUFNLGdCQUFnQixDQUFBO0FBRW5ELE1BQU0sVUFBVSxHQUFHLElBQUksVUFBVSxFQUFFLENBQUE7QUFFbkMsTUFBTSxDQUFDLEtBQUssVUFBVSxTQUFTO0lBQzdCLE1BQU0sU0FBUyxHQUFHLCtCQUErQixDQUFBO0lBQ2pELGlEQUFpRDtJQUVqRCxNQUFNLEdBQUcsQ0FBQyxLQUFLLENBQUMsT0FBTyxFQUFFLEVBQUUsU0FBUyxFQUFFLElBQUksRUFBRSxDQUFDLENBQUE7SUFDN0MsTUFBTSxtQkFBbUIsR0FBRyxJQUFJLG1CQUFtQixDQUFDLE9BQU8sRUFBRSxVQUFVLENBQUMsQ0FBQTtJQUN4RSxNQUFNLGtCQUFrQixDQUN0QjtRQUNFLG1CQUFtQjtLQUNwQixFQUNELFVBQVUsRUFDVixLQUFLLEVBQUUsS0FBSyxFQUFFLEVBQUU7UUFDZCxLQUFLLENBQUMsUUFBUSxHQUFHLFNBQVMsQ0FBQTtRQUMxQixNQUFNLGNBQWMsQ0FBQyxFQUFFLEdBQUcsRUFBRSxTQUFTLEVBQUUsU0FBUyxFQUFFLEtBQUssQ0FBQyxJQUFJLEVBQUUsUUFBUSxFQUFFLEtBQUssRUFBRSxPQUFPLEVBQUUsS0FBSyxFQUFFLEVBQUUsVUFBVSxDQUFDLENBQUE7SUFDOUcsQ0FBQyxDQUNGLENBQUE7QUFDSCxDQUFDO0FBRUQsTUFBTSxDQUFDLEtBQUssVUFBVSxRQUFRO0lBQzVCLE1BQU0sbUJBQW1CLEdBQUcsSUFBSSxtQkFBbUIsQ0FBQyxPQUFPLEVBQUUsVUFBVSxDQUFDLENBQUE7SUFDeEUsTUFBTSxpQkFBaUIsQ0FDckI7UUFDRSxtQkFBbUI7S0FDcEIsRUFDRCxVQUFVLEVBQ1YsS0FBSyxFQUFFLEtBQUssRUFBRSxFQUFFO1FBQ2QsSUFBSSxDQUFDLEtBQUssQ0FBQyxRQUFRO1lBQUUsTUFBTSxJQUFJLEtBQUssQ0FBQyx5QkFBeUIsQ0FBQyxDQUFBO1FBQy9ELE1BQU0sY0FBYyxDQUFDLEVBQUUsR0FBRyxFQUFFLEtBQUssQ0FBQyxRQUFRLEVBQUUsU0FBUyxFQUFFLEtBQUssQ0FBQyxJQUFJLEVBQUUsUUFBUSxFQUFFLEtBQUssRUFBRSxPQUFPLEVBQUUsS0FBSyxFQUFFLEVBQUUsVUFBVSxDQUFDLENBQUE7SUFDbkgsQ0FBQyxDQUNGLENBQUE7QUFDSCxDQUFDO0FBRUQsU0FBUyxFQUFFLENBQUMsSUFBSSxDQUFDLFFBQVEsQ0FBQyxDQUFBIn0=
|
package/build/dependency.js
CHANGED
|
@@ -24,8 +24,9 @@ export class Dependency {
|
|
|
24
24
|
await execa(lighthousePath, args, { stdout: 'inherit', stderr: 'inherit' });
|
|
25
25
|
}
|
|
26
26
|
async executeLoadshow(args) {
|
|
27
|
-
const
|
|
27
|
+
const binaryName = process.platform === 'win32' ? 'loadshow.exe' : 'loadshow';
|
|
28
|
+
const loadshowPath = process.env.LOADSHOW_PATH || `./bin/${binaryName}`;
|
|
28
29
|
await execa(loadshowPath, args, { stdout: 'inherit', stderr: 'inherit' });
|
|
29
30
|
}
|
|
30
31
|
}
|
|
31
|
-
//# sourceMappingURL=data:application/json;base64,
|
|
32
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZGVwZW5kZW5jeS5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uL3NyYy9kZXBlbmRlbmN5LnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sR0FBRyxNQUFNLGFBQWEsQ0FBQTtBQUU3QixPQUFPLEVBQUUsS0FBSyxFQUFFLE1BQU0sT0FBTyxDQUFBO0FBQzdCLE9BQU8sSUFBSSxNQUFNLE1BQU0sQ0FBQTtBQUl2QixNQUFNLE9BQU8sVUFBVTtJQUNyQixNQUFNLENBQWM7SUFFcEI7UUFDRSxJQUFJLENBQUMsTUFBTSxHQUFHLElBQUksQ0FBQztZQUNqQixLQUFLLEVBQUUsT0FBTyxDQUFDLEdBQUcsQ0FBQyxTQUFTLElBQUksTUFBTTtZQUN0QyxTQUFTLEVBQUU7Z0JBQ1QsTUFBTSxFQUFFLGFBQWE7Z0JBQ3JCLE9BQU8sRUFBRTtvQkFDUCxRQUFRLEVBQUUsSUFBSTtvQkFDZCxNQUFNLEVBQUUsY0FBYztvQkFDdEIsVUFBVSxFQUFFLENBQUMsQ0FBQyxFQUFFLEVBQUUsR0FBRyxFQUFFLE9BQU8sRUFBRSxJQUFJLENBQUMsQ0FBQyxRQUFRLENBQUMsT0FBTyxDQUFDLEdBQUcsQ0FBQyxXQUFXLEVBQUUsV0FBVyxFQUFFLENBQUM7aUJBQ3ZGO2FBQ0Y7U0FDRixDQUFDLENBQUE7SUFDSixDQUFDO0lBRUQsS0FBSyxDQUFDLE1BQU0sQ0FBQyxPQUFlO1FBQzFCLE1BQU0sR0FBRyxDQUFDLEtBQUssQ0FBQyxPQUFPLEVBQUUsRUFBRSxTQUFTLEVBQUUsSUFBSSxFQUFFLENBQUMsQ0FBQTtJQUMvQyxDQUFDO0lBRUQsS0FBSyxDQUFDLGlCQUFpQixDQUFDLElBQWM7UUFDcEMsTUFBTSxjQUFjLEdBQUcsT0FBTyxDQUFDLEdBQUcsQ0FBQyxlQUFlLElBQUksZ0NBQWdDLENBQUE7UUFDdEYsTUFBTSxLQUFLLENBQUMsY0FBYyxFQUFFLElBQUksRUFBRSxFQUFFLE1BQU0sRUFBRSxTQUFTLEVBQUUsTUFBTSxFQUFFLFNBQVMsRUFBRSxDQUFDLENBQUE7SUFDN0UsQ0FBQztJQUVELEtBQUssQ0FBQyxlQUFlLENBQUMsSUFBYztRQUNsQyxNQUFNLFVBQVUsR0FBRyxPQUFPLENBQUMsUUFBUSxLQUFLLE9BQU8sQ0FBQyxDQUFDLENBQUMsY0FBYyxDQUFDLENBQUMsQ0FBQyxVQUFVLENBQUE7UUFDN0UsTUFBTSxZQUFZLEdBQUcsT0FBTyxDQUFDLEdBQUcsQ0FBQyxhQUFhLElBQUksU0FBUyxVQUFVLEVBQUUsQ0FBQTtRQUN2RSxNQUFNLEtBQUssQ0FBQyxZQUFZLEVBQUUsSUFBSSxFQUFFLEVBQUUsTUFBTSxFQUFFLFNBQVMsRUFBRSxNQUFNLEVBQUUsU0FBUyxFQUFFLENBQUMsQ0FBQTtJQUMzRSxDQUFDO0NBQ0YifQ==
|
package/build/index.d.ts
CHANGED
package/build/index.js
CHANGED
|
@@ -7,5 +7,6 @@ export * from './playback.js';
|
|
|
7
7
|
export * from './recording.js';
|
|
8
8
|
export * from './types.js';
|
|
9
9
|
export * from './lighthouse.js';
|
|
10
|
+
export * from './lighthouse-digest.js';
|
|
10
11
|
export * from './loadshow.js';
|
|
11
|
-
//# sourceMappingURL=data:application/json;base64,
|
|
12
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi9zcmMvaW5kZXgudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsY0FBYyxpQkFBaUIsQ0FBQTtBQUMvQixjQUFjLGVBQWUsQ0FBQTtBQUM3QixjQUFjLGlCQUFpQixDQUFBO0FBQy9CLGNBQWMsV0FBVyxDQUFBO0FBQ3pCLGNBQWMsZ0JBQWdCLENBQUE7QUFDOUIsY0FBYyxlQUFlLENBQUE7QUFDN0IsY0FBYyxnQkFBZ0IsQ0FBQTtBQUM5QixjQUFjLFlBQVksQ0FBQTtBQUMxQixjQUFjLGlCQUFpQixDQUFBO0FBQy9CLGNBQWMsd0JBQXdCLENBQUE7QUFDdEMsY0FBYyxlQUFlLENBQUEifQ==
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { DependencyInterface } from './types.js';
|
|
2
|
+
export interface CaptureLighthouseDigestInput {
|
|
3
|
+
htmlPath: string;
|
|
4
|
+
outputPath: string;
|
|
5
|
+
}
|
|
6
|
+
/**
|
|
7
|
+
* Captures a digest screenshot of the Lighthouse report.
|
|
8
|
+
* This function captures two specific sections:
|
|
9
|
+
* 1. The final screenshot header (lh-category-header__finalscreenshot)
|
|
10
|
+
* 2. The metrics audit group (lh-audit-group--metrics)
|
|
11
|
+
*/
|
|
12
|
+
export declare function captureLighthouseDigest(opts: CaptureLighthouseDigestInput, dependency: Pick<DependencyInterface, 'logger' | 'mkdirp'>): Promise<void>;
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import Path from 'path';
|
|
2
|
+
import puppeteer from 'puppeteer';
|
|
3
|
+
/**
|
|
4
|
+
* Captures a digest screenshot of the Lighthouse report.
|
|
5
|
+
* This function captures two specific sections:
|
|
6
|
+
* 1. The final screenshot header (lh-category-header__finalscreenshot)
|
|
7
|
+
* 2. The metrics audit group (lh-audit-group--metrics)
|
|
8
|
+
*/
|
|
9
|
+
export async function captureLighthouseDigest(opts, dependency) {
|
|
10
|
+
const browser = await puppeteer.launch({
|
|
11
|
+
headless: true,
|
|
12
|
+
args: ['--no-sandbox', '--disable-setuid-sandbox'],
|
|
13
|
+
});
|
|
14
|
+
try {
|
|
15
|
+
const page = await browser.newPage();
|
|
16
|
+
// Load the HTML file
|
|
17
|
+
const fileUrl = `file://${Path.resolve(opts.htmlPath)}`;
|
|
18
|
+
await page.goto(fileUrl, { waitUntil: 'networkidle0' });
|
|
19
|
+
// Check if the required elements exist
|
|
20
|
+
const elementsExist = await page.evaluate(() => {
|
|
21
|
+
const finalScreenshot = document.querySelector('.lh-category-header__finalscreenshot');
|
|
22
|
+
const metrics = document.querySelector('.lh-audit-group--metrics');
|
|
23
|
+
return finalScreenshot !== null && metrics !== null;
|
|
24
|
+
});
|
|
25
|
+
if (!elementsExist) {
|
|
26
|
+
throw new Error('Required elements not found in the Lighthouse report');
|
|
27
|
+
}
|
|
28
|
+
// Get the bounding boxes of both elements
|
|
29
|
+
const finalScreenshotElement = await page.$('.lh-category-header__finalscreenshot');
|
|
30
|
+
const metricsElement = await page.$('.lh-audit-group--metrics');
|
|
31
|
+
if (!finalScreenshotElement || !metricsElement) {
|
|
32
|
+
throw new Error('Required elements not found in the Lighthouse report');
|
|
33
|
+
}
|
|
34
|
+
const finalScreenshotBox = await finalScreenshotElement.boundingBox();
|
|
35
|
+
const metricsBox = await metricsElement.boundingBox();
|
|
36
|
+
if (!finalScreenshotBox || !metricsBox) {
|
|
37
|
+
throw new Error('Could not get bounding box of required elements');
|
|
38
|
+
}
|
|
39
|
+
// Calculate the bounding box that encompasses both elements
|
|
40
|
+
const minX = Math.min(finalScreenshotBox.x, metricsBox.x);
|
|
41
|
+
const minY = Math.min(finalScreenshotBox.y, metricsBox.y);
|
|
42
|
+
const maxX = Math.max(finalScreenshotBox.x + finalScreenshotBox.width, metricsBox.x + metricsBox.width);
|
|
43
|
+
const maxY = Math.max(finalScreenshotBox.y + finalScreenshotBox.height, metricsBox.y + metricsBox.height);
|
|
44
|
+
const boundingBox = {
|
|
45
|
+
x: minX,
|
|
46
|
+
y: minY,
|
|
47
|
+
width: maxX - minX,
|
|
48
|
+
height: maxY - minY,
|
|
49
|
+
};
|
|
50
|
+
// Ensure output directory exists
|
|
51
|
+
const outputDir = Path.dirname(opts.outputPath);
|
|
52
|
+
await dependency.mkdirp(outputDir);
|
|
53
|
+
// Capture the screenshot
|
|
54
|
+
await page.screenshot({
|
|
55
|
+
path: opts.outputPath,
|
|
56
|
+
clip: {
|
|
57
|
+
x: boundingBox.x,
|
|
58
|
+
y: boundingBox.y,
|
|
59
|
+
width: boundingBox.width,
|
|
60
|
+
height: boundingBox.height,
|
|
61
|
+
},
|
|
62
|
+
});
|
|
63
|
+
dependency.logger?.info(`Lighthouse digest screenshot saved to ${opts.outputPath}`);
|
|
64
|
+
}
|
|
65
|
+
finally {
|
|
66
|
+
await browser.close();
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibGlnaHRob3VzZS1kaWdlc3QuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi9zcmMvbGlnaHRob3VzZS1kaWdlc3QudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxJQUFJLE1BQU0sTUFBTSxDQUFBO0FBRXZCLE9BQU8sU0FBUyxNQUFNLFdBQVcsQ0FBQTtBQVNqQzs7Ozs7R0FLRztBQUNILE1BQU0sQ0FBQyxLQUFLLFVBQVUsdUJBQXVCLENBQzNDLElBQWtDLEVBQ2xDLFVBQTBEO0lBRTFELE1BQU0sT0FBTyxHQUFHLE1BQU0sU0FBUyxDQUFDLE1BQU0sQ0FBQztRQUNyQyxRQUFRLEVBQUUsSUFBSTtRQUNkLElBQUksRUFBRSxDQUFDLGNBQWMsRUFBRSwwQkFBMEIsQ0FBQztLQUNuRCxDQUFDLENBQUE7SUFFRixJQUFJO1FBQ0YsTUFBTSxJQUFJLEdBQUcsTUFBTSxPQUFPLENBQUMsT0FBTyxFQUFFLENBQUE7UUFFcEMscUJBQXFCO1FBQ3JCLE1BQU0sT0FBTyxHQUFHLFVBQVUsSUFBSSxDQUFDLE9BQU8sQ0FBQyxJQUFJLENBQUMsUUFBUSxDQUFDLEVBQUUsQ0FBQTtRQUN2RCxNQUFNLElBQUksQ0FBQyxJQUFJLENBQUMsT0FBTyxFQUFFLEVBQUUsU0FBUyxFQUFFLGNBQWMsRUFBRSxDQUFDLENBQUE7UUFFdkQsdUNBQXVDO1FBQ3ZDLE1BQU0sYUFBYSxHQUFHLE1BQU0sSUFBSSxDQUFDLFFBQVEsQ0FBQyxHQUFHLEVBQUU7WUFDN0MsTUFBTSxlQUFlLEdBQUcsUUFBUSxDQUFDLGFBQWEsQ0FBQyxzQ0FBc0MsQ0FBQyxDQUFBO1lBQ3RGLE1BQU0sT0FBTyxHQUFHLFFBQVEsQ0FBQyxhQUFhLENBQUMsMEJBQTBCLENBQUMsQ0FBQTtZQUNsRSxPQUFPLGVBQWUsS0FBSyxJQUFJLElBQUksT0FBTyxLQUFLLElBQUksQ0FBQTtRQUNyRCxDQUFDLENBQUMsQ0FBQTtRQUVGLElBQUksQ0FBQyxhQUFhLEVBQUU7WUFDbEIsTUFBTSxJ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
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { DependencyInterface } from './types.js';
|
|
2
|
+
/**
|
|
3
|
+
* Lighthouse audit result structure
|
|
4
|
+
*/
|
|
5
|
+
export interface LighthouseAudit {
|
|
6
|
+
id: string;
|
|
7
|
+
title: string;
|
|
8
|
+
score: number | null;
|
|
9
|
+
numericValue?: number;
|
|
10
|
+
displayValue?: string;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Lighthouse JSON report structure (partial)
|
|
14
|
+
*/
|
|
15
|
+
export interface LighthouseReport {
|
|
16
|
+
categories: {
|
|
17
|
+
performance: {
|
|
18
|
+
id: string;
|
|
19
|
+
title: string;
|
|
20
|
+
score: number | null;
|
|
21
|
+
};
|
|
22
|
+
};
|
|
23
|
+
audits: Record<string, LighthouseAudit>;
|
|
24
|
+
}
|
|
25
|
+
export interface GenerateLighthouseSummaryInput {
|
|
26
|
+
jsonPath: string;
|
|
27
|
+
outputPath: string;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Generates a Markdown summary from a Lighthouse JSON report
|
|
31
|
+
*/
|
|
32
|
+
export declare function generateLighthouseSummary(opts: GenerateLighthouseSummaryInput, dependency: Pick<DependencyInterface, 'logger' | 'mkdirp'>): Promise<void>;
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import fs from 'fs/promises';
|
|
2
|
+
import Path from 'path';
|
|
3
|
+
/**
|
|
4
|
+
* Performance metrics to extract from Lighthouse report
|
|
5
|
+
* Weights are based on Lighthouse v10+ scoring
|
|
6
|
+
*/
|
|
7
|
+
const PERFORMANCE_METRICS = [
|
|
8
|
+
{ id: 'first-contentful-paint', abbr: 'FCP', unit: 'ms', weight: 0.1 },
|
|
9
|
+
{ id: 'largest-contentful-paint', abbr: 'LCP', unit: 'ms', weight: 0.25 },
|
|
10
|
+
{ id: 'total-blocking-time', abbr: 'TBT', unit: 'ms', weight: 0.3 },
|
|
11
|
+
{ id: 'cumulative-layout-shift', abbr: 'CLS', unit: '', weight: 0.25 },
|
|
12
|
+
{ id: 'speed-index', abbr: 'SI', unit: 'ms', weight: 0.1 },
|
|
13
|
+
];
|
|
14
|
+
/**
|
|
15
|
+
* Formats a score as a percentage integer
|
|
16
|
+
*/
|
|
17
|
+
function formatScore(score) {
|
|
18
|
+
if (score === null)
|
|
19
|
+
return 'N/A';
|
|
20
|
+
return `${Math.round(score * 100)}`;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Formats a numeric value in milliseconds (rounded to integer)
|
|
24
|
+
* For CLS, returns the raw value with 3 decimal places
|
|
25
|
+
*/
|
|
26
|
+
function formatValue(numericValue, unit) {
|
|
27
|
+
if (numericValue === undefined)
|
|
28
|
+
return 'N/A';
|
|
29
|
+
if (unit === '') {
|
|
30
|
+
// CLS - no unit, show decimal value
|
|
31
|
+
return numericValue.toFixed(3);
|
|
32
|
+
}
|
|
33
|
+
// Time-based metrics - round to integer ms
|
|
34
|
+
return `${Math.round(numericValue)} ${unit}`;
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Generates a Markdown summary from a Lighthouse JSON report
|
|
38
|
+
*/
|
|
39
|
+
export async function generateLighthouseSummary(opts, dependency) {
|
|
40
|
+
// Read and parse the Lighthouse JSON report
|
|
41
|
+
const jsonContent = await fs.readFile(opts.jsonPath, 'utf-8');
|
|
42
|
+
const report = JSON.parse(jsonContent);
|
|
43
|
+
// Extract overall performance score
|
|
44
|
+
const overallScore = report.categories?.performance?.score;
|
|
45
|
+
// Build the Markdown content
|
|
46
|
+
const lines = [];
|
|
47
|
+
lines.push('# Lighthouse Performance Summary');
|
|
48
|
+
lines.push('');
|
|
49
|
+
// Section 1: Metric Values
|
|
50
|
+
lines.push('## Metrics');
|
|
51
|
+
lines.push('');
|
|
52
|
+
for (const metric of PERFORMANCE_METRICS) {
|
|
53
|
+
const audit = report.audits[metric.id];
|
|
54
|
+
if (audit) {
|
|
55
|
+
const value = formatValue(audit.numericValue, metric.unit);
|
|
56
|
+
lines.push(`- ${metric.abbr} ${value}`);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
lines.push('');
|
|
60
|
+
// Section 2: Scores
|
|
61
|
+
lines.push('## Scores');
|
|
62
|
+
lines.push('');
|
|
63
|
+
lines.push(`- Overall ${formatScore(overallScore)}`);
|
|
64
|
+
for (const metric of PERFORMANCE_METRICS) {
|
|
65
|
+
const audit = report.audits[metric.id];
|
|
66
|
+
if (audit) {
|
|
67
|
+
const score = formatScore(audit.score);
|
|
68
|
+
const weight = `x${metric.weight.toFixed(2)}`;
|
|
69
|
+
lines.push(`- ${metric.abbr} ${score} ${weight}`);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
lines.push('');
|
|
73
|
+
// Ensure output directory exists
|
|
74
|
+
const outputDir = Path.dirname(opts.outputPath);
|
|
75
|
+
await dependency.mkdirp(outputDir);
|
|
76
|
+
// Write the summary file
|
|
77
|
+
await fs.writeFile(opts.outputPath, lines.join('\n'), 'utf-8');
|
|
78
|
+
dependency.logger?.info(`Lighthouse summary saved to ${opts.outputPath}`);
|
|
79
|
+
}
|
|
80
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibGlnaHRob3VzZS1zdW1tYXJ5LmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vc3JjL2xpZ2h0aG91c2Utc3VtbWFyeS50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEVBQUUsTUFBTSxhQUFhLENBQUE7QUFDNUIsT0FBTyxJQUFJLE1BQU0sTUFBTSxDQUFBO0FBNkJ2Qjs7O0dBR0c7QUFDSCxNQUFNLG1CQUFtQixHQUFHO0lBQzFCLEVBQUUsRUFBRSxFQUFFLHdCQUF3QixFQUFFLElBQUksRUFBRSxLQUFLLEVBQUUsSUFBSSxFQUFFLElBQUksRUFBRSxNQUFNLEVBQUUsR0FBRyxFQUFFO0lBQ3RFLEVBQUUsRUFBRSxFQUFFLDBCQUEwQixFQUFFLElBQUksRUFBRSxLQUFLLEVBQUUsSUFBSSxFQUFFLElBQUksRUFBRSxNQUFNLEVBQUUsSUFBSSxFQUFFO0lBQ3pFLEVBQUUsRUFBRSxFQUFFLHFCQUFxQixFQUFFLElBQUksRUFBRSxLQUFLLEVBQUUsSUFBSSxFQUFFLElBQUksRUFBRSxNQUFNLEVBQUUsR0FBRyxFQUFFO0lBQ25FLEVBQUUsRUFBRSxFQUFFLHlCQUF5QixFQUFFLElBQUksRUFBRSxLQUFLLEVBQUUsSUFBSSxFQUFFLEVBQUUsRUFBRSxNQUFNLEVBQUUsSUFBSSxFQUFFO0lBQ3RFLEVBQUUsRUFBRSxFQUFFLGFBQWEsRUFBRSxJQUFJLEVBQUUsSUFBSSxFQUFFLElBQUksRUFBRSxJQUFJLEVBQUUsTUFBTSxFQUFFLEdBQUcsRUFBRTtDQUMzRCxDQUFBO0FBT0Q7O0dBRUc7QUFDSCxTQUFTLFdBQVcsQ0FBQyxLQUFvQjtJQUN2QyxJQUFJLEtBQUssS0FBSyxJQUFJO1FBQUUsT0FBTyxLQUFLLENBQUE7SUFDaEMsT0FBTyxHQUFHLElBQUksQ0FBQyxLQUFLLENBQUMsS0FBSyxHQUFHLEdBQUcsQ0FBQyxFQUFFLENBQUE7QUFDckMsQ0FBQztBQUVEOzs7R0FHRztBQUNILFNBQVMsV0FBVyxDQUFDLFlBQWdDLEVBQUUs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
|
package/build/lighthouse.d.ts
CHANGED
|
@@ -7,5 +7,6 @@ export interface ExecLighthouseInput {
|
|
|
7
7
|
artifactsDir?: string;
|
|
8
8
|
headless: boolean;
|
|
9
9
|
timeout: number;
|
|
10
|
+
captureScoreAndMetrics?: boolean;
|
|
10
11
|
}
|
|
11
|
-
export declare function execLighthouse(opts: ExecLighthouseInput, dependency: Pick<DependencyInterface, 'mkdirp' | 'executeLighthouse'>): Promise<void>;
|
|
12
|
+
export declare function execLighthouse(opts: ExecLighthouseInput, dependency: Pick<DependencyInterface, 'mkdirp' | 'executeLighthouse' | 'logger'>): Promise<void>;
|
package/build/lighthouse.js
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import Path from 'path';
|
|
2
|
+
import { captureLighthouseDigest } from './lighthouse-digest.js';
|
|
3
|
+
import { generateLighthouseSummary } from './lighthouse-summary.js';
|
|
2
4
|
export async function execLighthouse(opts, dependency) {
|
|
3
5
|
const artifactsDir = opts.artifactsDir || './artifacts';
|
|
4
6
|
await dependency.mkdirp(artifactsDir);
|
|
@@ -28,5 +30,33 @@ export async function execLighthouse(opts, dependency) {
|
|
|
28
30
|
if (opts.view)
|
|
29
31
|
args.push('--view');
|
|
30
32
|
await dependency.executeLighthouse(args);
|
|
33
|
+
// Capture score and metrics screenshot if requested
|
|
34
|
+
if (opts.captureScoreAndMetrics !== false) {
|
|
35
|
+
const htmlPath = `${outputPath}.report.html`;
|
|
36
|
+
const digestPath = Path.join(artifactsDir, 'lighthouse.digest.png');
|
|
37
|
+
try {
|
|
38
|
+
await captureLighthouseDigest({
|
|
39
|
+
htmlPath,
|
|
40
|
+
outputPath: digestPath,
|
|
41
|
+
}, dependency);
|
|
42
|
+
}
|
|
43
|
+
catch (error) {
|
|
44
|
+
dependency.logger?.warn(`Failed to capture Lighthouse score and metrics: ${error}`);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
// Generate summary markdown
|
|
48
|
+
if (opts.artifactsDir) {
|
|
49
|
+
const jsonPath = `${outputPath}.report.json`;
|
|
50
|
+
const summaryPath = Path.join(artifactsDir, 'summary.md');
|
|
51
|
+
try {
|
|
52
|
+
await generateLighthouseSummary({
|
|
53
|
+
jsonPath,
|
|
54
|
+
outputPath: summaryPath,
|
|
55
|
+
}, dependency);
|
|
56
|
+
}
|
|
57
|
+
catch (error) {
|
|
58
|
+
dependency.logger?.warn(`Failed to generate Lighthouse summary: ${error}`);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
31
61
|
}
|
|
32
|
-
//# sourceMappingURL=data:application/json;base64,
|
|
62
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibGlnaHRob3VzZS5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uL3NyYy9saWdodGhvdXNlLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sSUFBSSxNQUFNLE1BQU0sQ0FBQTtBQUV2QixPQUFPLEVBQUUsdUJBQXVCLEVBQUUsTUFBTSx3QkFBd0IsQ0FBQTtBQUNoRSxPQUFPLEVBQUUseUJBQXlCLEVBQUUsTUFBTSx5QkFBeUIsQ0FBQTtBQWNuRSxNQUFNLENBQUMsS0FBSyxVQUFVLGNBQWMsQ0FDbEMsSUFBeUIsRUFDekIsVUFBZ0Y7SUFFaEYsTUFBTSxZQUFZLEdBQUcsSUFBSSxDQUFDLFlBQVksSUFBSSxhQUFhLENBQUE7SUFDdkQsTUFBTSxVQUFVLENBQUMsTUFBTSxDQUFDLFlBQVksQ0FBQyxDQUFBO0lBRXJDLE1BQU0sVUFBVSxHQUFHLElBQUksQ0FBQyxVQUFVLElBQUksUUFBUSxDQUFBO0lBQzlDLE1BQU0sVUFBVSxHQUFHLElBQUksQ0FBQyxJQUFJLENBQUMsWUFBWSxFQUFFLFlBQVksQ0FBQyxDQUFBO0lBQ3hELE1BQU0sSUFBSSxHQUFhO1FBQ3JCLElBQUksQ0FBQyxHQUFHO1FBQ1IsZUFBZTtRQUNmLG9CQUFvQjtRQUNwQixpQkFBaUIsVUFBVSxFQUFFO1FBQzdCLCtCQUErQjtRQUMvQixpQkFBaUIsVUFBVSxFQUFFO1FBQzdCLDRDQUE0QztRQUM1Qyw0QkFBNEIsVUFBVSxLQUFLLFFBQVEsQ0FBQyxDQUFDLENBQUMsTUFBTSxDQUFDLENBQUMsQ0FBQyxPQUFPLEVBQUU7UUFDeEUsNkRBQTZEO1FBQzdELHNCQUFzQjtRQUN0QiwrQkFBK0I7UUFDL0IsdUNBQXVDO1FBQ3ZDLHFDQUFxQztRQUNyQyxzQ0FBc0M7S0FDdkMsQ0FBQTtJQUVELElBQUksQ0FBQyxJQUFJLENBQUMsdUJBQXVCLElBQUksQ0FBQyxPQUFPLEVBQUUsQ0FBQyxDQUFBO0lBRWhELE1BQU0sV0FBVyxHQUFhLENBQUMsNkJBQTZCLEVBQUUsbUNBQW1DLElBQUksQ0FBQyxTQUFTLEVBQUUsQ0FBQyxDQUFBO0lBQ2xILElBQUksSUFBSSxDQUFDLFFBQVE7UUFBRSxXQUFXLENBQUMsSUFBSSxDQUFDLFlBQVksQ0FBQyxDQUFBO0lBQ2pELElBQUksQ0FBQyxJQUFJLENBQUMsbUJBQW1CLFdBQVcsQ0FBQyxJQUFJLENBQUMsR0FBRyxDQUFDLEdBQUcsQ0FBQyxDQUFBO0lBRXRELElBQUksSUFBSSxDQUFDLElBQUk7UUFBRSxJQUFJLENBQUMsSUFBSSxDQUFDLFFBQVEsQ0FBQyxDQUFBO0lBRWxDLE1BQU0sVUFBVSxDQUFDLGlCQUFpQixDQUFDLElBQUksQ0FBQyxDQUFBO0lBRXhDLG9EQUFvRDtJQUNwRCxJQUFJLElBQUksQ0FBQyxzQkFBc0IsS0FBSyxLQUFLLEVBQUU7UUFDekMsTUFBTSxRQUFRLEdBQUcsR0FBRyxVQUFVLGNBQWMsQ0FBQTtRQUM1QyxNQUFNLFVBQVUsR0FBRyxJQUFJLENBQUMsSUFBSSxDQUFDLFlBQVksRUFBRSx1QkFBdUIsQ0FBQyxDQUFBO1FBRW5FLElBQUk7WUFDRixNQUFNLHVCQUF1QixDQUMzQjtnQkFDRSxRQUFRO2dCQUNSLFVBQVUsRUFBRSxVQUFVO2FBQ3ZCLEVBQ0QsVUFBVSxDQUNYLENBQUE7U0FDRjtRQUFDLE9BQU8sS0FBSyxFQUFFO1lBQ2QsVUFBVSxDQUFDLE1BQU0sRUFBRSxJQUFJLENBQUMsbURBQW1ELEtBQUssRUFBRSxDQUFDLENBQUE7U0FDcEY7S0FDRjtJQUVELDRCQUE0QjtJQUM1QixJQUFJLElBQUksQ0FBQyxZQUFZLEVBQUU7UUFDckIsTUFBTSxRQUFRLEdBQUcsR0FBRyxVQUFVLGNBQWMsQ0FBQTtRQUM1QyxNQUFNLFdBQVcsR0FBRyxJQUFJLENBQUMsSUFBSSxDQUFDLFlBQVksRUFBRSxZQUFZLENBQUMsQ0FBQTtRQUV6RCxJQUFJO1lBQ0YsTUFBTSx5QkFBeUIsQ0FDN0I7Z0JBQ0UsUUFBUTtnQkFDUixVQUFVLEVBQUUsV0FBVzthQUN4QixFQUNELFVBQVUsQ0FDWCxDQUFBO1NBQ0Y7UUFBQyxPQUFPLEtBQUssRUFBRTtZQUNkLFVBQVUsQ0FBQyxNQUFNLEVBQUUsSUFBSSxDQUFDLDBDQUEwQyxLQUFLLEVBQUUsQ0FBQyxDQUFBO1NBQzNFO0tBQ0Y7QUFDSCxDQUFDIn0=
|
package/build/loadshow.d.ts
CHANGED
|
@@ -8,12 +8,14 @@ export interface ExecLoadshowInput {
|
|
|
8
8
|
credit?: string;
|
|
9
9
|
}
|
|
10
10
|
export interface ExecLoadshowSpec {
|
|
11
|
+
preset?: 'desktop' | 'mobile';
|
|
11
12
|
viewportWidth?: number;
|
|
12
13
|
columns?: number;
|
|
13
14
|
cpuThrottling?: number;
|
|
14
|
-
|
|
15
|
-
userAgent?: string;
|
|
15
|
+
timeoutSec?: number;
|
|
16
16
|
proxyPort?: number;
|
|
17
17
|
credit?: string;
|
|
18
|
+
debugDir?: string;
|
|
19
|
+
outputSummary?: string;
|
|
18
20
|
}
|
|
19
21
|
export declare function execLoadshow(input: ExecLoadshowInput, dependency: Pick<DependencyInterface, 'mkdirp' | 'executeLoadshow'>): Promise<void>;
|
package/build/loadshow.js
CHANGED
|
@@ -1,54 +1,62 @@
|
|
|
1
1
|
import Path from 'path';
|
|
2
|
-
import { defaultConfig, desktopConfig } from 'lighthouse';
|
|
3
2
|
function execSpecToCommandArgs(spec) {
|
|
4
3
|
const args = [];
|
|
4
|
+
// preset
|
|
5
|
+
if (spec.preset !== undefined)
|
|
6
|
+
args.push('--preset', spec.preset);
|
|
5
7
|
// layout
|
|
6
8
|
if (spec.columns !== undefined)
|
|
7
|
-
args.push('
|
|
8
|
-
//
|
|
9
|
+
args.push('--columns', String(spec.columns));
|
|
10
|
+
// browser settings
|
|
9
11
|
if (spec.viewportWidth !== undefined)
|
|
10
|
-
args.push('-
|
|
12
|
+
args.push('--viewport-width', String(spec.viewportWidth));
|
|
11
13
|
if (spec.cpuThrottling !== undefined)
|
|
12
|
-
args.push('-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
// recording.puppeteer
|
|
18
|
-
const chromeArgs = ['--ignore-certificate-errors'];
|
|
14
|
+
args.push('--cpu-throttling', String(spec.cpuThrottling));
|
|
15
|
+
// timeout
|
|
16
|
+
if (spec.timeoutSec !== undefined)
|
|
17
|
+
args.push('--timeout-sec', String(spec.timeoutSec));
|
|
18
|
+
// proxy settings
|
|
19
19
|
if (spec.proxyPort !== undefined) {
|
|
20
|
-
|
|
20
|
+
args.push('--proxy-server', `http://localhost:${spec.proxyPort}`);
|
|
21
|
+
args.push('--ignore-https-errors');
|
|
21
22
|
}
|
|
22
|
-
args.push('-u', 'recording.puppeteer.args=' + chromeArgs.join(','));
|
|
23
23
|
// credit
|
|
24
24
|
if (spec.credit)
|
|
25
|
-
args.push('
|
|
25
|
+
args.push('--credit', spec.credit);
|
|
26
|
+
// debug directory
|
|
27
|
+
if (spec.debugDir)
|
|
28
|
+
args.push('--debug-dir', spec.debugDir);
|
|
29
|
+
// output summary
|
|
30
|
+
if (spec.outputSummary)
|
|
31
|
+
args.push('--output-summary', spec.outputSummary);
|
|
26
32
|
return args;
|
|
27
33
|
}
|
|
28
34
|
export async function execLoadshow(input, dependency) {
|
|
29
35
|
const artifactsDir = input.artifactsDir || './artifacts';
|
|
30
36
|
const loadshowDir = Path.join(artifactsDir, 'loadshow');
|
|
31
37
|
const outputPath = Path.join(artifactsDir, 'loadshow.mp4');
|
|
38
|
+
const summaryPath = Path.join(loadshowDir, 'summary.md');
|
|
32
39
|
await dependency.mkdirp(loadshowDir);
|
|
33
40
|
// By form factor
|
|
34
|
-
const
|
|
41
|
+
const preset = input.deviceType === 'desktop' ? 'desktop' : 'mobile';
|
|
35
42
|
const customByDevice = input.deviceType === 'desktop' ? { columns: 2 } : { columns: 3 };
|
|
43
|
+
// Convert timeout from milliseconds to seconds
|
|
44
|
+
const timeoutSec = Math.ceil(input.timeout / 1000);
|
|
36
45
|
// Basic spec
|
|
37
|
-
const userAgent = lighthouseByDevice.settings?.emulatedUserAgent;
|
|
38
46
|
const spec = {
|
|
47
|
+
preset,
|
|
39
48
|
proxyPort: input.proxyPort,
|
|
40
49
|
columns: customByDevice.columns,
|
|
41
|
-
|
|
42
|
-
cpuThrottling: lighthouseByDevice.settings?.throttling?.cpuSlowdownMultiplier,
|
|
43
|
-
userAgent: typeof userAgent === 'string' ? userAgent : undefined,
|
|
44
|
-
timeoutMs: input.timeout,
|
|
50
|
+
timeoutSec,
|
|
45
51
|
credit: input.credit,
|
|
52
|
+
debugDir: loadshowDir,
|
|
53
|
+
outputSummary: summaryPath,
|
|
46
54
|
};
|
|
47
55
|
const args = [];
|
|
48
56
|
args.push('record');
|
|
49
|
-
args.push('-a', loadshowDir);
|
|
50
57
|
args.push(...execSpecToCommandArgs(spec));
|
|
51
|
-
args.push(
|
|
58
|
+
args.push('--output', outputPath);
|
|
59
|
+
args.push(input.url);
|
|
52
60
|
await dependency.executeLoadshow(args);
|
|
53
61
|
}
|
|
54
|
-
//# sourceMappingURL=data:application/json;base64,
|
|
62
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibG9hZHNob3cuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi9zcmMvbG9hZHNob3cudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxJQUFJLE1BQU0sTUFBTSxDQUFBO0FBeUJ2QixTQUFTLHFCQUFxQixDQUFDLElBQXNCO0lBQ25ELE1BQU0sSUFBSSxHQUFhLEVBQUUsQ0FBQTtJQUV6QixTQUFTO0lBQ1QsSUFBSSxJQUFJLENBQUMsTUFBTSxLQUFLLFNBQVM7UUFBRSxJQUFJLENBQUMsSUFBSSxDQUFDLFVBQVUsRUFBRSxJQUFJLENBQUMsTUFBTSxDQUFDLENBQUE7SUFFakUsU0FBUztJQUNULElBQUksSUFBSSxDQUFDLE9BQU8sS0FBSyxTQUFTO1FBQUUsSUFBSSxDQUFDLElBQUksQ0FBQyxXQUFXLEVBQUUsTUFBTSxDQUFDLElBQUksQ0FBQyxPQUFPLENBQUMsQ0FBQyxDQUFBO0lBRTVFLG1CQUFtQjtJQUNuQixJQUFJLElBQUksQ0FBQyxhQUFhLEtBQUssU0FBUztRQUFFLElBQUksQ0FBQyxJQUFJLENBQUMsa0JBQWtCLEVBQUUsTUFBTSxDQUFDLElBQUksQ0FBQyxhQUFhLENBQUMsQ0FBQyxDQUFBO0lBQy9GLElBQUksSUFBSSxDQUFDLGFBQWEsS0FBSyxTQUFTO1FBQUUsSUFBSSxDQUFDLElBQUksQ0FBQyxrQkFBa0IsRUFBRSxNQUFNLENBQUMsSUFBSSxDQUFDLGFBQWEsQ0FBQyxDQUFDLENBQUE7SUFFL0YsVUFBVTtJQUNWLElBQUksSUFBSSxDQUFDLFVBQVUsS0FBSyxTQUFTO1FBQUUsSUFBSSxDQUFDLElBQUksQ0FBQyxlQUFlLEVBQUUsTUFBTSxDQUFDLElBQUksQ0FBQyxVQUFVLENBQUMsQ0FBQyxDQUFBO0lBRXRGLGlCQUFpQjtJQUNqQixJQUFJLElBQUksQ0FBQyxTQUFTLEtBQUssU0FBUyxFQUFFO1FBQ2hDLElBQUksQ0FBQyxJQUFJLENBQUMsZ0JBQWdCLEVBQUUsb0JBQW9CLElBQUksQ0FBQyxTQUFTLEVB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
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pagespeed-quest",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.7.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "A framework for efficient web front-end speed improvement",
|
|
6
6
|
"main": "build/index.js",
|
|
@@ -46,7 +46,8 @@
|
|
|
46
46
|
"prepare-release": "run-s reset-hard test cov:check doc:html version doc:publish",
|
|
47
47
|
"adhoc": "yarn build && node build/adhoc.js",
|
|
48
48
|
"command": "yarn build && node build/command.js",
|
|
49
|
-
"prepublishOnly": "yarn build"
|
|
49
|
+
"prepublishOnly": "yarn build",
|
|
50
|
+
"postinstall": "node scripts/postinstall.js"
|
|
50
51
|
},
|
|
51
52
|
"engines": {
|
|
52
53
|
"node": ">=18"
|
|
@@ -57,11 +58,12 @@
|
|
|
57
58
|
"iconv-lite": "^0.6.3",
|
|
58
59
|
"jschardet": "^3.0.0",
|
|
59
60
|
"lighthouse": "^12.2.1",
|
|
60
|
-
"
|
|
61
|
+
"node-html-to-image": "^5.0.0",
|
|
61
62
|
"node-watch": "^0.7.4",
|
|
62
63
|
"pino": "^9.4.0",
|
|
63
64
|
"pino-pretty": "^11.2.2",
|
|
64
65
|
"prettier": "^2.1.1",
|
|
66
|
+
"puppeteer": "^24.31.0",
|
|
65
67
|
"rust-http-playback-proxy": "0.4.2",
|
|
66
68
|
"tmp-promise": "^3.0.3"
|
|
67
69
|
},
|
|
@@ -91,6 +93,7 @@
|
|
|
91
93
|
},
|
|
92
94
|
"files": [
|
|
93
95
|
"build",
|
|
96
|
+
"scripts",
|
|
94
97
|
"!**/*.spec.*",
|
|
95
98
|
"!**/*.json",
|
|
96
99
|
"CHANGELOG.md",
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import { createWriteStream, mkdirSync, chmodSync, existsSync, unlinkSync } from 'fs'
|
|
4
|
+
import { pipeline } from 'stream/promises'
|
|
5
|
+
import { createGunzip } from 'zlib'
|
|
6
|
+
import { extract } from 'tar'
|
|
7
|
+
import { fileURLToPath } from 'url'
|
|
8
|
+
import { dirname, join } from 'path'
|
|
9
|
+
|
|
10
|
+
const __filename = fileURLToPath(import.meta.url)
|
|
11
|
+
const __dirname = dirname(__filename)
|
|
12
|
+
|
|
13
|
+
const LOADSHOW_VERSION = 'v1.3.0'
|
|
14
|
+
const REPO = 'ideamans/go-loadshow'
|
|
15
|
+
|
|
16
|
+
function getPlatformInfo() {
|
|
17
|
+
const platform = process.platform
|
|
18
|
+
const arch = process.arch
|
|
19
|
+
|
|
20
|
+
if (platform === 'darwin' && arch === 'arm64') {
|
|
21
|
+
return { os: 'darwin', arch: 'arm64', ext: 'tar.gz' }
|
|
22
|
+
} else if (platform === 'darwin' && arch === 'x64') {
|
|
23
|
+
// macOS x64 is not available, fall back to arm64 (Rosetta 2)
|
|
24
|
+
return { os: 'darwin', arch: 'arm64', ext: 'tar.gz' }
|
|
25
|
+
} else if (platform === 'linux' && arch === 'x64') {
|
|
26
|
+
return { os: 'linux', arch: 'amd64', ext: 'tar.gz' }
|
|
27
|
+
} else if (platform === 'linux' && arch === 'arm64') {
|
|
28
|
+
return { os: 'linux', arch: 'arm64', ext: 'tar.gz' }
|
|
29
|
+
} else if (platform === 'win32' && arch === 'x64') {
|
|
30
|
+
return { os: 'windows', arch: 'amd64', ext: 'zip' }
|
|
31
|
+
} else {
|
|
32
|
+
throw new Error(`Unsupported platform: ${platform} ${arch}`)
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
async function downloadFile(url, destPath) {
|
|
37
|
+
const response = await fetch(url, { redirect: 'follow' })
|
|
38
|
+
if (!response.ok) {
|
|
39
|
+
throw new Error(`Failed to download: ${response.status} ${response.statusText}`)
|
|
40
|
+
}
|
|
41
|
+
const fileStream = createWriteStream(destPath)
|
|
42
|
+
await pipeline(response.body, fileStream)
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
async function extractTarGz(archivePath, destDir) {
|
|
46
|
+
const { execSync } = await import('child_process')
|
|
47
|
+
execSync(`tar -xzf "${archivePath}" -C "${destDir}"`, { stdio: 'inherit' })
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
async function extractZip(archivePath, destDir) {
|
|
51
|
+
const { execSync } = await import('child_process')
|
|
52
|
+
execSync(`unzip -o "${archivePath}" -d "${destDir}"`, { stdio: 'inherit' })
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
async function main() {
|
|
56
|
+
const binDir = join(__dirname, '..', 'bin')
|
|
57
|
+
const platformInfo = getPlatformInfo()
|
|
58
|
+
|
|
59
|
+
const assetName = `loadshow_${LOADSHOW_VERSION}_${platformInfo.os}_${platformInfo.arch}.${platformInfo.ext}`
|
|
60
|
+
const downloadUrl = `https://github.com/${REPO}/releases/download/${LOADSHOW_VERSION}/${assetName}`
|
|
61
|
+
|
|
62
|
+
console.log(`Downloading go-loadshow ${LOADSHOW_VERSION} for ${platformInfo.os}/${platformInfo.arch}...`)
|
|
63
|
+
console.log(`URL: ${downloadUrl}`)
|
|
64
|
+
|
|
65
|
+
// Create bin directory if not exists
|
|
66
|
+
if (!existsSync(binDir)) {
|
|
67
|
+
mkdirSync(binDir, { recursive: true })
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
const archivePath = join(binDir, assetName)
|
|
71
|
+
const binaryName = platformInfo.os === 'windows' ? 'loadshow.exe' : 'loadshow'
|
|
72
|
+
const binaryPath = join(binDir, binaryName)
|
|
73
|
+
|
|
74
|
+
// Download archive
|
|
75
|
+
await downloadFile(downloadUrl, archivePath)
|
|
76
|
+
console.log('Download complete. Extracting...')
|
|
77
|
+
|
|
78
|
+
// Extract archive
|
|
79
|
+
if (platformInfo.ext === 'tar.gz') {
|
|
80
|
+
await extractTarGz(archivePath, binDir)
|
|
81
|
+
} else {
|
|
82
|
+
await extractZip(archivePath, binDir)
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// Make binary executable (Unix only)
|
|
86
|
+
if (platformInfo.os !== 'windows') {
|
|
87
|
+
chmodSync(binaryPath, 0o755)
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// Clean up archive
|
|
91
|
+
unlinkSync(archivePath)
|
|
92
|
+
|
|
93
|
+
console.log(`go-loadshow installed successfully at ${binaryPath}`)
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
main().catch((err) => {
|
|
97
|
+
console.error('Failed to install go-loadshow:', err.message)
|
|
98
|
+
process.exit(1)
|
|
99
|
+
})
|