laxy-verify 0.1.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/README.md +163 -0
- package/action.yml +97 -0
- package/dist/build-runner.d.ts +22 -0
- package/dist/build-runner.js +156 -0
- package/dist/build-runner.js.map +1 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +102 -0
- package/dist/cli.js.map +1 -0
- package/dist/config.d.ts +16 -0
- package/dist/config.js +55 -0
- package/dist/config.js.map +1 -0
- package/dist/dev-server.d.ts +8 -0
- package/dist/dev-server.js +81 -0
- package/dist/dev-server.js.map +1 -0
- package/dist/lighthouse-runner.d.ts +10 -0
- package/dist/lighthouse-runner.js +99 -0
- package/dist/lighthouse-runner.js.map +1 -0
- package/dist/project-runtime.d.ts +32 -0
- package/dist/project-runtime.js +99 -0
- package/dist/project-runtime.js.map +1 -0
- package/dist/reporter.d.ts +21 -0
- package/dist/reporter.js +100 -0
- package/dist/reporter.js.map +1 -0
- package/dist/verification.d.ts +42 -0
- package/dist/verification.js +105 -0
- package/dist/verification.js.map +1 -0
- package/package.json +44 -0
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import { spawn } from "child_process";
|
|
2
|
+
import http from "http";
|
|
3
|
+
import { readFileSync, readdirSync } from "fs";
|
|
4
|
+
import { join } from "path";
|
|
5
|
+
import { detectRuntimeFromFiles, getDevCommandCandidates, parseProjectPackageJson, } from "./project-runtime.js";
|
|
6
|
+
function waitForPort(port, timeoutMs = 30000) {
|
|
7
|
+
const start = Date.now();
|
|
8
|
+
return new Promise((resolve, reject) => {
|
|
9
|
+
const check = () => {
|
|
10
|
+
const req = http.get(`http://localhost:${port}`, (res) => {
|
|
11
|
+
res.resume();
|
|
12
|
+
resolve(res.statusCode ?? 0);
|
|
13
|
+
});
|
|
14
|
+
req.on("error", () => {
|
|
15
|
+
if (Date.now() - start > timeoutMs) {
|
|
16
|
+
reject(new Error(`Dev server did not respond on port ${port} within ${timeoutMs / 1000}s`));
|
|
17
|
+
}
|
|
18
|
+
else {
|
|
19
|
+
setTimeout(check, 1000);
|
|
20
|
+
}
|
|
21
|
+
});
|
|
22
|
+
req.setTimeout(3000, () => {
|
|
23
|
+
req.destroy();
|
|
24
|
+
if (Date.now() - start > timeoutMs) {
|
|
25
|
+
reject(new Error(`Dev server timed out on port ${port}`));
|
|
26
|
+
}
|
|
27
|
+
else {
|
|
28
|
+
setTimeout(check, 1000);
|
|
29
|
+
}
|
|
30
|
+
});
|
|
31
|
+
};
|
|
32
|
+
check();
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
export async function startDevServer(projectPath, port) {
|
|
36
|
+
const pkgContent = readFileSync(join(projectPath, "package.json"), "utf-8");
|
|
37
|
+
const packageJson = parseProjectPackageJson(pkgContent);
|
|
38
|
+
const candidateFiles = readdirSync(projectPath).filter((f) => !f.startsWith("."));
|
|
39
|
+
const runtime = detectRuntimeFromFiles(candidateFiles, packageJson);
|
|
40
|
+
const npm = process.platform === "win32" ? "npm.cmd" : "npm";
|
|
41
|
+
const npx = process.platform === "win32" ? "npx.cmd" : "npx";
|
|
42
|
+
const devCommands = getDevCommandCandidates(runtime, { port, npmCommand: npm, npxCommand: npx });
|
|
43
|
+
if (devCommands.length === 0) {
|
|
44
|
+
throw new Error("No dev server command found");
|
|
45
|
+
}
|
|
46
|
+
const command = devCommands[0];
|
|
47
|
+
const cmd = process.platform === "win32" ? `chcp 65001 > nul && ${command.cmd}` : command.cmd;
|
|
48
|
+
const child = spawn(cmd, command.args, {
|
|
49
|
+
cwd: projectPath,
|
|
50
|
+
shell: true,
|
|
51
|
+
env: { ...process.env, PORT: String(port) },
|
|
52
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
53
|
+
});
|
|
54
|
+
// Drain stdout/stderr to prevent buffer blocking
|
|
55
|
+
child.stdout?.resume();
|
|
56
|
+
child.stderr?.resume();
|
|
57
|
+
const statusCode = await waitForPort(port);
|
|
58
|
+
if (statusCode !== 200 && statusCode !== 304) {
|
|
59
|
+
child.kill();
|
|
60
|
+
throw new Error(`Dev server returned HTTP ${statusCode}. Check environment variables.`);
|
|
61
|
+
}
|
|
62
|
+
return {
|
|
63
|
+
process: child,
|
|
64
|
+
port,
|
|
65
|
+
url: `http://localhost:${port}`,
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
export function stopDevServer(handle) {
|
|
69
|
+
try {
|
|
70
|
+
if (process.platform === "win32") {
|
|
71
|
+
spawn("taskkill", ["/pid", String(handle.process.pid), "/f", "/t"], { shell: true });
|
|
72
|
+
}
|
|
73
|
+
else {
|
|
74
|
+
handle.process.kill("SIGTERM");
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
catch {
|
|
78
|
+
// already dead
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
//# sourceMappingURL=dev-server.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dev-server.js","sourceRoot":"","sources":["../src/dev-server.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { LighthouseScores } from "./verification.js";
|
|
2
|
+
export interface LighthouseResult {
|
|
3
|
+
scores: LighthouseScores | null;
|
|
4
|
+
error?: string;
|
|
5
|
+
}
|
|
6
|
+
export declare function runLighthouse(projectPath: string, options?: {
|
|
7
|
+
port?: number;
|
|
8
|
+
runs?: number;
|
|
9
|
+
ciMode?: boolean;
|
|
10
|
+
}): Promise<LighthouseResult>;
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import { execFileSync } from "child_process";
|
|
2
|
+
import { existsSync, readFileSync } from "fs";
|
|
3
|
+
import { join } from "path";
|
|
4
|
+
import { startDevServer, stopDevServer } from "./dev-server.js";
|
|
5
|
+
function findLhciManifest(tmpDir) {
|
|
6
|
+
// lhci stores results in .lighthouseci/ directory
|
|
7
|
+
const lhciDir = join(tmpDir, ".lighthouseci");
|
|
8
|
+
if (!existsSync(lhciDir))
|
|
9
|
+
return null;
|
|
10
|
+
const manifest = join(lhciDir, "manifest.json");
|
|
11
|
+
if (existsSync(manifest))
|
|
12
|
+
return manifest;
|
|
13
|
+
return null;
|
|
14
|
+
}
|
|
15
|
+
function parseLhciResults(projectPath) {
|
|
16
|
+
const manifest = findLhciManifest(projectPath);
|
|
17
|
+
if (!manifest)
|
|
18
|
+
return null;
|
|
19
|
+
try {
|
|
20
|
+
const entries = JSON.parse(readFileSync(manifest, "utf-8"));
|
|
21
|
+
if (!Array.isArray(entries) || entries.length === 0)
|
|
22
|
+
return null;
|
|
23
|
+
// Use the median run (middle entry) or last entry
|
|
24
|
+
const entry = entries[Math.floor(entries.length / 2)];
|
|
25
|
+
const reportPath = join(projectPath, ".lighthouseci", entry.jsonPath || "");
|
|
26
|
+
if (!existsSync(reportPath))
|
|
27
|
+
return null;
|
|
28
|
+
const report = JSON.parse(readFileSync(reportPath, "utf-8"));
|
|
29
|
+
const cat = report.categories;
|
|
30
|
+
if (!cat)
|
|
31
|
+
return null;
|
|
32
|
+
const score = (key) => Math.round((cat[key]?.score ?? 0) * 100);
|
|
33
|
+
return {
|
|
34
|
+
performance: score("performance"),
|
|
35
|
+
accessibility: score("accessibility"),
|
|
36
|
+
bestPractices: score("best-practices"),
|
|
37
|
+
seo: score("seo"),
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
catch {
|
|
41
|
+
return null;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
export async function runLighthouse(projectPath, options = {}) {
|
|
45
|
+
const port = options.port ?? 3000;
|
|
46
|
+
const runs = options.runs ?? (options.ciMode ? 3 : 1);
|
|
47
|
+
let serverHandle = null;
|
|
48
|
+
try {
|
|
49
|
+
// Start dev server
|
|
50
|
+
serverHandle = await startDevServer(projectPath, port);
|
|
51
|
+
}
|
|
52
|
+
catch (err) {
|
|
53
|
+
const msg = err instanceof Error ? err.message : "Dev server failed to start";
|
|
54
|
+
return { scores: null, error: msg };
|
|
55
|
+
}
|
|
56
|
+
try {
|
|
57
|
+
const url = `http://localhost:${port}`;
|
|
58
|
+
// Run lhci collect
|
|
59
|
+
const npx = process.platform === "win32" ? "npx.cmd" : "npx";
|
|
60
|
+
const lhciArgs = [
|
|
61
|
+
"-y", "@lhci/cli", "collect",
|
|
62
|
+
`--url=${url}`,
|
|
63
|
+
`--numberOfRuns=${runs}`,
|
|
64
|
+
"--settings.formFactor=desktop",
|
|
65
|
+
"--settings.screenEmulation.disabled=true",
|
|
66
|
+
"--settings.throttlingMethod=provided",
|
|
67
|
+
"--settings.onlyCategories=performance,accessibility,best-practices,seo",
|
|
68
|
+
];
|
|
69
|
+
if (process.env.CI) {
|
|
70
|
+
lhciArgs.push("--settings.chromeFlags=--no-sandbox --disable-gpu --headless");
|
|
71
|
+
}
|
|
72
|
+
try {
|
|
73
|
+
execFileSync(npx, lhciArgs, {
|
|
74
|
+
cwd: projectPath,
|
|
75
|
+
timeout: 120000,
|
|
76
|
+
stdio: "pipe",
|
|
77
|
+
env: { ...process.env, LHCI_BUILD_CONTEXT__CURRENT_HASH: "cli" },
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
catch (err) {
|
|
81
|
+
// lhci collect may exit non-zero even on success, check for results
|
|
82
|
+
const scores = parseLhciResults(projectPath);
|
|
83
|
+
if (scores)
|
|
84
|
+
return { scores };
|
|
85
|
+
const msg = err instanceof Error ? err.message : "Lighthouse execution failed";
|
|
86
|
+
return { scores: null, error: `Lighthouse failed: ${msg.slice(0, 200)}` };
|
|
87
|
+
}
|
|
88
|
+
const scores = parseLhciResults(projectPath);
|
|
89
|
+
if (!scores) {
|
|
90
|
+
return { scores: null, error: "Lighthouse completed but produced no parseable results" };
|
|
91
|
+
}
|
|
92
|
+
return { scores };
|
|
93
|
+
}
|
|
94
|
+
finally {
|
|
95
|
+
if (serverHandle)
|
|
96
|
+
stopDevServer(serverHandle);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
//# sourceMappingURL=lighthouse-runner.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"lighthouse-runner.js","sourceRoot":"","sources":["../src/lighthouse-runner.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
export interface ProjectPackageJson {
|
|
2
|
+
scripts?: Record<string, string>;
|
|
3
|
+
dependencies?: Record<string, string>;
|
|
4
|
+
devDependencies?: Record<string, string>;
|
|
5
|
+
[key: string]: unknown;
|
|
6
|
+
}
|
|
7
|
+
export interface RuntimeDetection {
|
|
8
|
+
packageJson: ProjectPackageJson | null;
|
|
9
|
+
hasPackageJson: boolean;
|
|
10
|
+
hasNext: boolean;
|
|
11
|
+
hasVite: boolean;
|
|
12
|
+
hasReactScripts: boolean;
|
|
13
|
+
hasIndexHtml: boolean;
|
|
14
|
+
hasNextConfig: boolean;
|
|
15
|
+
hasViteConfig: boolean;
|
|
16
|
+
}
|
|
17
|
+
export interface CommandSpec {
|
|
18
|
+
label: string;
|
|
19
|
+
cmd: string;
|
|
20
|
+
args: string[];
|
|
21
|
+
}
|
|
22
|
+
export declare function parseProjectPackageJson(content: string | null | undefined): ProjectPackageJson | null;
|
|
23
|
+
export declare function detectRuntimeFromFiles(fileNamesInput: Iterable<string>, packageJson: ProjectPackageJson | null): RuntimeDetection;
|
|
24
|
+
export declare function getBuildCommandCandidates(runtime: RuntimeDetection, options: {
|
|
25
|
+
npmCommand: string;
|
|
26
|
+
npxCommand: string;
|
|
27
|
+
}): CommandSpec[];
|
|
28
|
+
export declare function getDevCommandCandidates(runtime: RuntimeDetection, options: {
|
|
29
|
+
port: number;
|
|
30
|
+
npmCommand: string;
|
|
31
|
+
npxCommand: string;
|
|
32
|
+
}): CommandSpec[];
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
function hasFile(fileNames, matcher) {
|
|
2
|
+
if (typeof matcher === "string") {
|
|
3
|
+
return fileNames.some((f) => f === matcher || f.endsWith(`/${matcher}`));
|
|
4
|
+
}
|
|
5
|
+
return fileNames.some((f) => matcher.test(f));
|
|
6
|
+
}
|
|
7
|
+
function getAllDependencies(pkg) {
|
|
8
|
+
return { ...(pkg?.dependencies || {}), ...(pkg?.devDependencies || {}) };
|
|
9
|
+
}
|
|
10
|
+
function hasScript(script) {
|
|
11
|
+
return typeof script === "string" && script.trim().length > 0;
|
|
12
|
+
}
|
|
13
|
+
function addCommand(commands, seen, label, cmd, args) {
|
|
14
|
+
const key = [cmd, ...args].join("\0");
|
|
15
|
+
if (seen.has(key))
|
|
16
|
+
return;
|
|
17
|
+
seen.add(key);
|
|
18
|
+
commands.push({ label, cmd, args });
|
|
19
|
+
}
|
|
20
|
+
export function parseProjectPackageJson(content) {
|
|
21
|
+
if (!content)
|
|
22
|
+
return null;
|
|
23
|
+
try {
|
|
24
|
+
const parsed = JSON.parse(content);
|
|
25
|
+
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed))
|
|
26
|
+
return null;
|
|
27
|
+
return parsed;
|
|
28
|
+
}
|
|
29
|
+
catch {
|
|
30
|
+
return null;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
export function detectRuntimeFromFiles(fileNamesInput, packageJson) {
|
|
34
|
+
const fileNames = Array.from(fileNamesInput);
|
|
35
|
+
const allDeps = getAllDependencies(packageJson);
|
|
36
|
+
const hasNextConfig = hasFile(fileNames, /(^|\/)next\.config\.(js|mjs|ts|cjs)$/);
|
|
37
|
+
const hasViteConfig = hasFile(fileNames, /(^|\/)vite\.config\.(js|mjs|ts|cjs)$/);
|
|
38
|
+
return {
|
|
39
|
+
packageJson,
|
|
40
|
+
hasPackageJson: hasFile(fileNames, "package.json") || packageJson !== null,
|
|
41
|
+
hasNextConfig,
|
|
42
|
+
hasViteConfig,
|
|
43
|
+
hasIndexHtml: hasFile(fileNames, "index.html"),
|
|
44
|
+
hasNext: hasNextConfig || Boolean(allDeps.next),
|
|
45
|
+
hasVite: hasViteConfig || Boolean(allDeps.vite),
|
|
46
|
+
hasReactScripts: Boolean(allDeps["react-scripts"]),
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
export function getBuildCommandCandidates(runtime, options) {
|
|
50
|
+
const commands = [];
|
|
51
|
+
const seen = new Set();
|
|
52
|
+
const scripts = runtime.packageJson?.scripts || {};
|
|
53
|
+
if (hasScript(scripts.build)) {
|
|
54
|
+
addCommand(commands, seen, "npm run build", options.npmCommand, ["run", "build"]);
|
|
55
|
+
}
|
|
56
|
+
if (runtime.hasNext) {
|
|
57
|
+
addCommand(commands, seen, "npx next build", options.npxCommand, ["-y", "next", "build"]);
|
|
58
|
+
}
|
|
59
|
+
if (runtime.hasVite) {
|
|
60
|
+
addCommand(commands, seen, "npx vite build", options.npxCommand, ["-y", "vite", "build"]);
|
|
61
|
+
}
|
|
62
|
+
if (runtime.hasReactScripts) {
|
|
63
|
+
addCommand(commands, seen, "npx react-scripts build", options.npxCommand, ["-y", "react-scripts", "build"]);
|
|
64
|
+
}
|
|
65
|
+
return commands;
|
|
66
|
+
}
|
|
67
|
+
export function getDevCommandCandidates(runtime, options) {
|
|
68
|
+
const commands = [];
|
|
69
|
+
const seen = new Set();
|
|
70
|
+
const scripts = runtime.packageJson?.scripts || {};
|
|
71
|
+
const port = String(options.port);
|
|
72
|
+
const devScript = scripts.dev;
|
|
73
|
+
const startScript = scripts.start;
|
|
74
|
+
if (hasScript(devScript)) {
|
|
75
|
+
if (runtime.hasNext || /\bnext\b/i.test(devScript)) {
|
|
76
|
+
addCommand(commands, seen, "npm run dev", options.npmCommand, ["run", "dev", "--", "-H", "0.0.0.0", "-p", port]);
|
|
77
|
+
}
|
|
78
|
+
else if (runtime.hasVite || /\bvite\b/i.test(devScript)) {
|
|
79
|
+
addCommand(commands, seen, "npm run dev", options.npmCommand, ["run", "dev", "--", "--host", "0.0.0.0", "--port", port]);
|
|
80
|
+
}
|
|
81
|
+
else {
|
|
82
|
+
addCommand(commands, seen, "npm run dev", options.npmCommand, ["run", "dev"]);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
if (!hasScript(devScript) && hasScript(startScript)) {
|
|
86
|
+
addCommand(commands, seen, "npm run start", options.npmCommand, ["run", "start"]);
|
|
87
|
+
}
|
|
88
|
+
if (runtime.hasNext) {
|
|
89
|
+
addCommand(commands, seen, "npx next dev", options.npxCommand, ["-y", "next", "dev", "-H", "0.0.0.0", "-p", port]);
|
|
90
|
+
}
|
|
91
|
+
if (runtime.hasVite) {
|
|
92
|
+
addCommand(commands, seen, "npx vite", options.npxCommand, ["-y", "vite", "--host", "0.0.0.0", "--port", port]);
|
|
93
|
+
}
|
|
94
|
+
if (runtime.hasIndexHtml) {
|
|
95
|
+
addCommand(commands, seen, "npx serve", options.npxCommand, ["-y", "serve", "-l", port, "--no-clipboard"]);
|
|
96
|
+
}
|
|
97
|
+
return commands;
|
|
98
|
+
}
|
|
99
|
+
//# sourceMappingURL=project-runtime.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"project-runtime.js","sourceRoot":"","sources":["../src/project-runtime.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { VerificationGrade, LighthouseScores } from "./verification.js";
|
|
2
|
+
import type { LaxyConfig } from "./config.js";
|
|
3
|
+
import { getImprovementRecommendations } from "./verification.js";
|
|
4
|
+
export interface VerifyReport {
|
|
5
|
+
grade: VerificationGrade;
|
|
6
|
+
build: {
|
|
7
|
+
success: boolean;
|
|
8
|
+
errors: string[];
|
|
9
|
+
duration: number;
|
|
10
|
+
};
|
|
11
|
+
lighthouse: {
|
|
12
|
+
scores: LighthouseScores | null;
|
|
13
|
+
error?: string;
|
|
14
|
+
};
|
|
15
|
+
thresholds: LaxyConfig["thresholds"];
|
|
16
|
+
recommendations: ReturnType<typeof getImprovementRecommendations>;
|
|
17
|
+
}
|
|
18
|
+
export declare function formatConsole(report: VerifyReport): string;
|
|
19
|
+
export declare function formatJson(report: VerifyReport): string;
|
|
20
|
+
export declare function formatMarkdown(report: VerifyReport): string;
|
|
21
|
+
export declare function formatReport(report: VerifyReport, format: "console" | "json" | "md"): string;
|
package/dist/reporter.js
ADDED
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
const GRADE_LABELS = {
|
|
2
|
+
gold: { emoji: "\u{1F3C6}", label: "Gold" },
|
|
3
|
+
silver: { emoji: "\u2705", label: "Silver" },
|
|
4
|
+
bronze: { emoji: "\u{1F528}", label: "Bronze" },
|
|
5
|
+
unverified: { emoji: "\u26A0\uFE0F", label: "Unverified" },
|
|
6
|
+
};
|
|
7
|
+
export function formatConsole(report) {
|
|
8
|
+
const g = GRADE_LABELS[report.grade];
|
|
9
|
+
const lines = [];
|
|
10
|
+
lines.push("");
|
|
11
|
+
lines.push(` Laxy Verify — ${g.emoji} ${g.label}`);
|
|
12
|
+
lines.push(" " + "=".repeat(40));
|
|
13
|
+
lines.push("");
|
|
14
|
+
// Build
|
|
15
|
+
lines.push(` Build: ${report.build.success ? "PASS" : "FAIL"} (${report.build.duration}ms)`);
|
|
16
|
+
// Lighthouse
|
|
17
|
+
if (report.lighthouse.scores) {
|
|
18
|
+
const s = report.lighthouse.scores;
|
|
19
|
+
const t = report.thresholds;
|
|
20
|
+
const check = (score, threshold) => score >= threshold ? "PASS" : "FAIL";
|
|
21
|
+
lines.push(` Performance: ${s.performance} (threshold ${t.performance}) ${check(s.performance, t.performance)}`);
|
|
22
|
+
lines.push(` Accessibility: ${s.accessibility} (threshold ${t.accessibility}) ${check(s.accessibility, t.accessibility)}`);
|
|
23
|
+
lines.push(` SEO: ${s.seo} (threshold ${t.seo}) ${check(s.seo, t.seo)}`);
|
|
24
|
+
lines.push(` Best Practices: ${s.bestPractices} (threshold ${t.bestPractices}) ${check(s.bestPractices, t.bestPractices)}`);
|
|
25
|
+
}
|
|
26
|
+
else if (report.lighthouse.error) {
|
|
27
|
+
lines.push(` Lighthouse: SKIPPED — ${report.lighthouse.error}`);
|
|
28
|
+
}
|
|
29
|
+
lines.push("");
|
|
30
|
+
// Errors
|
|
31
|
+
if (report.build.errors.length > 0) {
|
|
32
|
+
lines.push(" Errors:");
|
|
33
|
+
for (const err of report.build.errors.slice(0, 5)) {
|
|
34
|
+
lines.push(` - ${err}`);
|
|
35
|
+
}
|
|
36
|
+
lines.push("");
|
|
37
|
+
}
|
|
38
|
+
// Recommendations
|
|
39
|
+
if (report.recommendations.length > 0) {
|
|
40
|
+
lines.push(" Recommendations:");
|
|
41
|
+
for (const rec of report.recommendations.slice(0, 3)) {
|
|
42
|
+
lines.push(` [${rec.priority}] ${rec.title}`);
|
|
43
|
+
lines.push(` ${rec.action}`);
|
|
44
|
+
}
|
|
45
|
+
lines.push("");
|
|
46
|
+
}
|
|
47
|
+
return lines.join("\n");
|
|
48
|
+
}
|
|
49
|
+
export function formatJson(report) {
|
|
50
|
+
return JSON.stringify(report, null, 2);
|
|
51
|
+
}
|
|
52
|
+
export function formatMarkdown(report) {
|
|
53
|
+
const g = GRADE_LABELS[report.grade];
|
|
54
|
+
const lines = [];
|
|
55
|
+
lines.push(`<!-- laxy-verify -->`);
|
|
56
|
+
lines.push(`## Laxy Verify — ${g.label} ${g.emoji}`);
|
|
57
|
+
lines.push("");
|
|
58
|
+
lines.push("| Category | Score | Threshold | Status |");
|
|
59
|
+
lines.push("|----------|-------|-----------|--------|");
|
|
60
|
+
lines.push(`| Build | ${report.build.success ? "PASS" : "FAIL"} | — | ${report.build.success ? "\u2705" : "\u274C"} |`);
|
|
61
|
+
if (report.lighthouse.scores) {
|
|
62
|
+
const s = report.lighthouse.scores;
|
|
63
|
+
const t = report.thresholds;
|
|
64
|
+
const row = (label, score, threshold) => {
|
|
65
|
+
const pass = score >= threshold;
|
|
66
|
+
return `| ${label} | ${score} | ${threshold} | ${pass ? "\u2705" : "\u274C"} |`;
|
|
67
|
+
};
|
|
68
|
+
lines.push(row("Performance", s.performance, t.performance));
|
|
69
|
+
lines.push(row("Accessibility", s.accessibility, t.accessibility));
|
|
70
|
+
lines.push(row("SEO", s.seo, t.seo));
|
|
71
|
+
lines.push(row("Best Practices", s.bestPractices, t.bestPractices));
|
|
72
|
+
}
|
|
73
|
+
else if (report.lighthouse.error) {
|
|
74
|
+
lines.push(`| Lighthouse | SKIPPED | — | \u26A0\uFE0F |`);
|
|
75
|
+
}
|
|
76
|
+
lines.push("");
|
|
77
|
+
if (report.recommendations.length > 0) {
|
|
78
|
+
lines.push("<details><summary>Recommendations</summary>");
|
|
79
|
+
lines.push("");
|
|
80
|
+
for (const rec of report.recommendations) {
|
|
81
|
+
lines.push(`- **[${rec.priority}] ${rec.title}**: ${rec.action}`);
|
|
82
|
+
}
|
|
83
|
+
lines.push("");
|
|
84
|
+
lines.push("</details>");
|
|
85
|
+
lines.push("");
|
|
86
|
+
}
|
|
87
|
+
if (report.grade !== "gold") {
|
|
88
|
+
lines.push("> Want Gold? Add E2E tests with [Laxy](https://github.com/psungmin24/Laxy)");
|
|
89
|
+
lines.push("");
|
|
90
|
+
}
|
|
91
|
+
return lines.join("\n");
|
|
92
|
+
}
|
|
93
|
+
export function formatReport(report, format) {
|
|
94
|
+
switch (format) {
|
|
95
|
+
case "json": return formatJson(report);
|
|
96
|
+
case "md": return formatMarkdown(report);
|
|
97
|
+
default: return formatConsole(report);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
//# sourceMappingURL=reporter.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"reporter.js","sourceRoot":"","sources":["../src/reporter.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
export type VerificationGrade = "gold" | "silver" | "bronze" | "unverified";
|
|
2
|
+
export interface LighthouseScores {
|
|
3
|
+
performance: number;
|
|
4
|
+
accessibility: number;
|
|
5
|
+
bestPractices: number;
|
|
6
|
+
seo: number;
|
|
7
|
+
}
|
|
8
|
+
export interface VerificationInput {
|
|
9
|
+
buildSuccess?: boolean;
|
|
10
|
+
e2ePassed?: number;
|
|
11
|
+
e2eTotal?: number;
|
|
12
|
+
lighthouseScores?: LighthouseScores;
|
|
13
|
+
}
|
|
14
|
+
export declare const LH_THRESHOLDS: {
|
|
15
|
+
performance: number;
|
|
16
|
+
accessibility: number;
|
|
17
|
+
seo: number;
|
|
18
|
+
bestPractices: number;
|
|
19
|
+
};
|
|
20
|
+
export declare const LH_CI_THRESHOLDS: {
|
|
21
|
+
performance: number;
|
|
22
|
+
accessibility: number;
|
|
23
|
+
seo: number;
|
|
24
|
+
bestPractices: number;
|
|
25
|
+
};
|
|
26
|
+
export declare function getLighthousePass(lh: LighthouseScores | undefined, thresholds?: {
|
|
27
|
+
performance: number;
|
|
28
|
+
accessibility: number;
|
|
29
|
+
seo: number;
|
|
30
|
+
bestPractices: number;
|
|
31
|
+
}): boolean;
|
|
32
|
+
export declare function getVerificationGrade(input: VerificationInput): VerificationGrade;
|
|
33
|
+
export interface ImprovementRule {
|
|
34
|
+
category: "build" | "performance" | "accessibility" | "seo" | "bestPractices" | "e2e";
|
|
35
|
+
priority: "critical" | "high" | "medium";
|
|
36
|
+
title: string;
|
|
37
|
+
description: string;
|
|
38
|
+
action: string;
|
|
39
|
+
}
|
|
40
|
+
export declare function getImprovementRecommendations(input: VerificationInput & {
|
|
41
|
+
buildErrors?: string[];
|
|
42
|
+
}): ImprovementRule[];
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
export const LH_THRESHOLDS = {
|
|
2
|
+
performance: 70,
|
|
3
|
+
accessibility: 85,
|
|
4
|
+
seo: 80,
|
|
5
|
+
bestPractices: 80,
|
|
6
|
+
};
|
|
7
|
+
export const LH_CI_THRESHOLDS = {
|
|
8
|
+
performance: 60, // CI mode: 10 points relaxed
|
|
9
|
+
accessibility: 85,
|
|
10
|
+
seo: 80,
|
|
11
|
+
bestPractices: 80,
|
|
12
|
+
};
|
|
13
|
+
export function getLighthousePass(lh, thresholds = LH_THRESHOLDS) {
|
|
14
|
+
if (!lh)
|
|
15
|
+
return false;
|
|
16
|
+
return (lh.performance >= thresholds.performance &&
|
|
17
|
+
lh.accessibility >= thresholds.accessibility &&
|
|
18
|
+
lh.seo >= thresholds.seo &&
|
|
19
|
+
lh.bestPractices >= thresholds.bestPractices);
|
|
20
|
+
}
|
|
21
|
+
export function getVerificationGrade(input) {
|
|
22
|
+
const buildOk = input.buildSuccess === true;
|
|
23
|
+
const e2eOk = typeof input.e2ePassed === "number" &&
|
|
24
|
+
typeof input.e2eTotal === "number" &&
|
|
25
|
+
input.e2eTotal > 0 &&
|
|
26
|
+
input.e2ePassed === input.e2eTotal;
|
|
27
|
+
const lhOk = getLighthousePass(input.lighthouseScores);
|
|
28
|
+
if (buildOk && e2eOk && lhOk)
|
|
29
|
+
return "gold";
|
|
30
|
+
if (buildOk && e2eOk)
|
|
31
|
+
return "silver";
|
|
32
|
+
if (buildOk && lhOk)
|
|
33
|
+
return "silver"; // CLI path: no E2E, build+LH = Silver
|
|
34
|
+
if (buildOk)
|
|
35
|
+
return "bronze";
|
|
36
|
+
return "unverified";
|
|
37
|
+
}
|
|
38
|
+
export function getImprovementRecommendations(input) {
|
|
39
|
+
const rules = [];
|
|
40
|
+
if (input.buildSuccess === false) {
|
|
41
|
+
const errors = input.buildErrors ?? [];
|
|
42
|
+
if (errors.some((e) => /TS\d+|type/i.test(e))) {
|
|
43
|
+
rules.push({
|
|
44
|
+
category: "build",
|
|
45
|
+
priority: "critical",
|
|
46
|
+
title: "TypeScript type error",
|
|
47
|
+
description: "TypeScript compilation errors are blocking the production build.",
|
|
48
|
+
action: "Check the file:line location in error messages and fix type mismatches.",
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
if (errors.some((e) => /Module not found|Cannot find module|Failed to resolve/i.test(e))) {
|
|
52
|
+
rules.push({
|
|
53
|
+
category: "build",
|
|
54
|
+
priority: "critical",
|
|
55
|
+
title: "Module resolution failure",
|
|
56
|
+
description: "An imported module or file cannot be found.",
|
|
57
|
+
action: "Check import paths and ensure required packages are in package.json.",
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
if (errors.some((e) => /SyntaxError|Unexpected token/i.test(e))) {
|
|
61
|
+
rules.push({
|
|
62
|
+
category: "build",
|
|
63
|
+
priority: "critical",
|
|
64
|
+
title: "Syntax error",
|
|
65
|
+
description: "JavaScript or TypeScript parsing error detected.",
|
|
66
|
+
action: "Check brackets, commas, string terminators, and JSX syntax.",
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
if (rules.filter((r) => r.category === "build").length === 0) {
|
|
70
|
+
rules.push({
|
|
71
|
+
category: "build",
|
|
72
|
+
priority: "critical",
|
|
73
|
+
title: "Build failed",
|
|
74
|
+
description: "The production build failed.",
|
|
75
|
+
action: "Check the first real error in build output and fix it.",
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
const lh = input.lighthouseScores;
|
|
80
|
+
if (lh) {
|
|
81
|
+
const checks = [
|
|
82
|
+
{ key: "performance", category: "performance", label: "Performance", tip: "Optimize images, remove unused JS, apply code splitting." },
|
|
83
|
+
{ key: "accessibility", category: "accessibility", label: "Accessibility", tip: "Add alt text, aria-labels, ensure sufficient color contrast." },
|
|
84
|
+
{ key: "seo", category: "seo", label: "SEO", tip: "Add title, meta description, robots.txt, sitemap.xml." },
|
|
85
|
+
{ key: "bestPractices", category: "bestPractices", label: "Best Practices", tip: "Fix console errors, update libraries, check resource references." },
|
|
86
|
+
];
|
|
87
|
+
for (const c of checks) {
|
|
88
|
+
const score = lh[c.key];
|
|
89
|
+
const threshold = LH_THRESHOLDS[c.key];
|
|
90
|
+
if (score < threshold) {
|
|
91
|
+
const gap = threshold - score;
|
|
92
|
+
rules.push({
|
|
93
|
+
category: c.category,
|
|
94
|
+
priority: gap >= 20 ? "high" : "medium",
|
|
95
|
+
title: `${c.label} below threshold (${score} / target ${threshold})`,
|
|
96
|
+
description: `${c.label} score does not meet the required threshold.`,
|
|
97
|
+
action: c.tip,
|
|
98
|
+
});
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
const priorityOrder = { critical: 0, high: 1, medium: 2 };
|
|
103
|
+
return rules.sort((a, b) => priorityOrder[a.priority] - priorityOrder[b.priority]);
|
|
104
|
+
}
|
|
105
|
+
//# sourceMappingURL=verification.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"verification.js","sourceRoot":"","sources":["../src/verification.ts"],"names":[],"mappings":"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"}
|