@hydration-audit/cli 0.2.4 → 0.2.5
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/dist/index.mjs +83 -1
- package/dist/index.mjs.map +1 -1
- package/package.json +3 -2
package/dist/index.mjs
CHANGED
|
@@ -2,7 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
// src/index.ts
|
|
4
4
|
import { Command } from "commander";
|
|
5
|
-
import { analyze, resolveConfig } from "@hydration-audit/core";
|
|
5
|
+
import { analyze, resolveConfig, loadConfig } from "@hydration-audit/core";
|
|
6
|
+
import { auditImages } from "@hydration-audit/image-auditor";
|
|
6
7
|
import fs from "fs";
|
|
7
8
|
import path from "path";
|
|
8
9
|
var program = new Command();
|
|
@@ -137,6 +138,87 @@ Run "hydration-audit analyze" first to generate the report.`
|
|
|
137
138
|
process.exit(1);
|
|
138
139
|
}
|
|
139
140
|
});
|
|
141
|
+
program.command("image-audit").description("Audit images and media assets in a built project").argument("[dir]", "Build output directory", ".").option("--json", "Output JSON report only").option("--config <path>", "Path to config file").action(async (dir, opts) => {
|
|
142
|
+
try {
|
|
143
|
+
const cwd = path.resolve(dir);
|
|
144
|
+
const loaded = await loadConfig(opts.config ? path.dirname(path.resolve(opts.config)) : cwd);
|
|
145
|
+
const rawConfig = loaded?.config || {};
|
|
146
|
+
const config = resolveConfig(rawConfig, cwd);
|
|
147
|
+
const outDir = config.outDir;
|
|
148
|
+
if (!fs.existsSync(outDir)) {
|
|
149
|
+
console.error(`Error: Build directory "${outDir}" not found. Please run your build first.`);
|
|
150
|
+
process.exit(1);
|
|
151
|
+
}
|
|
152
|
+
let projectName = "unknown";
|
|
153
|
+
try {
|
|
154
|
+
const pkgPath = path.join(config.rootDir, "package.json");
|
|
155
|
+
if (fs.existsSync(pkgPath)) {
|
|
156
|
+
const pkg = JSON.parse(fs.readFileSync(pkgPath, "utf-8"));
|
|
157
|
+
projectName = pkg.name || "unknown";
|
|
158
|
+
}
|
|
159
|
+
} catch {
|
|
160
|
+
}
|
|
161
|
+
if (!opts.json) {
|
|
162
|
+
console.log(`
|
|
163
|
+
Auditing images and media in "${outDir}"...
|
|
164
|
+
`);
|
|
165
|
+
}
|
|
166
|
+
const report = await auditImages(outDir, {
|
|
167
|
+
cdnUrls: rawConfig.cdnUrls || [],
|
|
168
|
+
maxSize: rawConfig.maxSize,
|
|
169
|
+
checkRemote: rawConfig.checkRemote
|
|
170
|
+
}, projectName);
|
|
171
|
+
const reportPath = path.resolve(cwd, ".image-audit-report.json");
|
|
172
|
+
fs.writeFileSync(reportPath, JSON.stringify(report, null, 2));
|
|
173
|
+
if (opts.json) {
|
|
174
|
+
console.log(JSON.stringify(report, null, 2));
|
|
175
|
+
return;
|
|
176
|
+
}
|
|
177
|
+
console.log(` Image & Media Audit Summary \u2014 ${report.projectName}`);
|
|
178
|
+
console.log(` ${"\u2500".repeat(50)}`);
|
|
179
|
+
console.log(` Total Pages: ${report.totals.totalPages}`);
|
|
180
|
+
console.log(` Total Images: ${report.totals.totalImages}`);
|
|
181
|
+
console.log(` Total Attachments: ${report.totals.totalAttachments}`);
|
|
182
|
+
console.log(` Total Issues: ${report.totals.totalIssues}`);
|
|
183
|
+
console.log(` Errors: ${report.totals.issuesBySeverity.error}`);
|
|
184
|
+
console.log(` Warnings: ${report.totals.issuesBySeverity.warning}`);
|
|
185
|
+
console.log(` Info: ${report.totals.issuesBySeverity.info}`);
|
|
186
|
+
console.log(` ${"\u2500".repeat(50)}`);
|
|
187
|
+
if (report.totals.totalIssues > 0) {
|
|
188
|
+
console.log(`
|
|
189
|
+
Issues breakdown:`);
|
|
190
|
+
for (const page of report.pages) {
|
|
191
|
+
const pageIssuesCount = page.images.reduce((sum, img) => sum + img.issues.length, 0) + page.attachments.reduce((sum, att) => sum + att.issues.length, 0);
|
|
192
|
+
if (pageIssuesCount === 0) continue;
|
|
193
|
+
console.log(`
|
|
194
|
+
Route: ${page.route} (${page.htmlFile})`);
|
|
195
|
+
for (const img of page.images) {
|
|
196
|
+
for (const issue of img.issues) {
|
|
197
|
+
const prefix = issue.severity === "error" ? "\u{1F534} [ERROR]" : issue.severity === "warning" ? "\u{1F7E1} [WARN]" : "\u{1F535} [INFO]";
|
|
198
|
+
console.log(` ${prefix} ${issue.message}`);
|
|
199
|
+
console.log(` Rec: ${issue.recommendation}`);
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
for (const att of page.attachments) {
|
|
203
|
+
for (const issue of att.issues) {
|
|
204
|
+
const prefix = issue.severity === "error" ? "\u{1F534} [ERROR]" : issue.severity === "warning" ? "\u{1F7E1} [WARN]" : "\u{1F535} [INFO]";
|
|
205
|
+
console.log(` ${prefix} ${issue.message}`);
|
|
206
|
+
console.log(` Rec: ${issue.recommendation}`);
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
} else {
|
|
211
|
+
console.log(`
|
|
212
|
+
\u2728 No media issues found! All assets healthy.`);
|
|
213
|
+
}
|
|
214
|
+
console.log(`
|
|
215
|
+
Report written to: ${reportPath}
|
|
216
|
+
`);
|
|
217
|
+
} catch (err) {
|
|
218
|
+
console.error(`Error: ${err.message}`);
|
|
219
|
+
process.exit(1);
|
|
220
|
+
}
|
|
221
|
+
});
|
|
140
222
|
program.command("history").description("Show hydration cost history over time").argument("[dir]", "Project directory", ".").option("--limit <n>", "Number of entries to show", "10").action(async (dir, opts) => {
|
|
141
223
|
const cwd = path.resolve(dir);
|
|
142
224
|
const { readHistory } = await import("@hydration-audit/core");
|
package/dist/index.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts"],"sourcesContent":["import { Command } from 'commander';\nimport { analyze, resolveConfig, loadConfig } from '@hydration-audit/core';\nimport type { UserConfig } from '@hydration-audit/core';\nimport fs from 'node:fs';\nimport path from 'node:path';\n\nconst program = new Command();\n\nprogram\n .name('hydration-audit')\n .description('Analyze JavaScript hydration costs in island-architecture frameworks')\n .version('0.1.0');\n\n// ─── analyze command ─────────────────────────────────────────────\n\nprogram\n .command('analyze')\n .description('Analyze hydration costs in a built project')\n .argument('[dir]', 'Build output directory', '.')\n .option('--json', 'Output JSON report only')\n .option('--ci', 'CI mode — exit with code 1 on errors')\n .option('--no-terminal', 'Suppress terminal output')\n .option('--verbose', 'Enable verbose logging')\n .option('--watch', 'Watch for changes and re-analyze')\n .option('--config <path>', 'Path to config file')\n .option('--budget <bytes>', 'Per-island gzip budget in bytes', parseInt)\n .action(async (dir: string, opts: any) => {\n try {\n const cwd = path.resolve(dir);\n\n const configOverrides: UserConfig = {};\n\n if (opts.json) {\n configOverrides.output = { terminal: false, json: '.hydration-audit-report.json', dashboard: false };\n }\n\n if (opts.noTerminal) {\n configOverrides.output = { ...configOverrides.output, terminal: false, json: '.hydration-audit-report.json', dashboard: false };\n }\n\n if (opts.budget) {\n configOverrides.thresholds = { islandBudget: opts.budget };\n }\n\n const report = await analyze({\n config: configOverrides,\n configPath: opts.config,\n cwd,\n verbose: opts.verbose,\n });\n\n // In JSON mode, print the report to stdout\n if (opts.json) {\n console.log(JSON.stringify(report, null, 2));\n }\n\n // In CI mode, exit with code 1 if there are errors\n if (opts.ci) {\n const config = resolveConfig(configOverrides, cwd);\n const failOn = config.ci.failOn;\n\n if (failOn === 'none') {\n process.exit(0);\n }\n\n const hasErrors = report.totals.issuesBySeverity.error > 0;\n const hasWarnings = report.totals.issuesBySeverity.warning > 0;\n\n if (failOn === 'error' && hasErrors) {\n // Emit GitHub Actions annotations if in CI\n if (process.env.CI) {\n emitGitHubAnnotations(report);\n }\n process.exit(1);\n }\n\n if (failOn === 'warning' && (hasErrors || hasWarnings)) {\n if (process.env.CI) {\n emitGitHubAnnotations(report);\n }\n process.exit(1);\n }\n }\n // Watch mode — re-analyze when build output changes\n if (opts.watch) {\n console.log('\\nWatching for changes in', cwd, '...\\n');\n const outDir = path.resolve(cwd, 'dist');\n let debounceTimer: ReturnType<typeof setTimeout> | null = null;\n\n fs.watch(outDir, { recursive: true }, (eventType, filename) => {\n if (!filename?.match(/\\.(js|mjs|html)$/)) return;\n if (debounceTimer) clearTimeout(debounceTimer);\n debounceTimer = setTimeout(async () => {\n console.log(`\\nChange detected: ${filename}. Re-analyzing...\\n`);\n try {\n await analyze({\n config: configOverrides,\n configPath: opts.config,\n cwd,\n verbose: opts.verbose,\n });\n } catch (e) {\n console.error(`Re-analysis failed: ${(e as Error).message}`);\n }\n }, 500);\n });\n\n // Keep process alive\n process.stdin.resume();\n }\n } catch (err) {\n console.error(`Error: ${(err as Error).message}`);\n process.exit(1);\n }\n });\n\n// ─── init command ────────────────────────────────────────────────\n\nprogram\n .command('init')\n .description('Create a hydration-audit config file')\n .action(async () => {\n const configPath = path.resolve('.hydration-audit.config.ts');\n\n if (fs.existsSync(configPath)) {\n console.log('Config file already exists:', configPath);\n return;\n }\n\n const template = `import { defineConfig } from '@hydration-audit/core';\n\nexport default defineConfig({\n framework: 'auto',\n thresholds: {\n islandBudget: 50_000,\n islandBudgetError: 100_000,\n pageBudget: 150_000,\n pageBudgetError: 300_000,\n totalBudget: 500_000,\n },\n rules: {\n 'oversized-island': 'error',\n 'eager-below-fold': 'warn',\n 'duplicate-framework': 'warn',\n 'budget-exceeded': 'error',\n },\n belowFold: ['Footer', 'Comments', 'Newsletter'],\n exclude: [],\n output: {\n json: '.hydration-audit-report.json',\n terminal: true,\n dashboard: false,\n },\n});\n`;\n\n fs.writeFileSync(configPath, template);\n console.log('Created config file:', configPath);\n });\n\n// ─── dashboard command ───────────────────────────────────────────\n\nprogram\n .command('dashboard')\n .description('Launch the hydration tax dashboard')\n .argument('[dir]', 'Project directory', '.')\n .option('--port <port>', 'Port number', '4173')\n .option('--report <path>', 'Path to report JSON file')\n .action(async (dir: string, opts: any) => {\n const cwd = path.resolve(dir);\n const reportPath = opts.report\n ? path.resolve(opts.report)\n : path.resolve(cwd, '.hydration-audit-report.json');\n\n if (!fs.existsSync(reportPath)) {\n console.error(\n `Report file not found: ${reportPath}\\n` +\n `Run \"hydration-audit analyze\" first to generate the report.`,\n );\n process.exit(1);\n }\n\n try {\n const { startDashboard } = await import('@hydration-audit/dashboard');\n const { url } = await startDashboard(reportPath, parseInt(opts.port));\n console.log(`Dashboard running at ${url}`);\n console.log('Press Ctrl+C to stop.\\n');\n\n // Keep process alive\n process.stdin.resume();\n } catch (err) {\n console.error(`Failed to start dashboard: ${(err as Error).message}`);\n console.error('Make sure @hydration-audit/dashboard is installed.');\n process.exit(1);\n }\n });\n\n// ─── history command ─────────────────────────────────────────────\n\nprogram\n .command('history')\n .description('Show hydration cost history over time')\n .argument('[dir]', 'Project directory', '.')\n .option('--limit <n>', 'Number of entries to show', '10')\n .action(async (dir: string, opts: any) => {\n const cwd = path.resolve(dir);\n const { readHistory } = await import('@hydration-audit/core');\n\n const history = readHistory(cwd);\n if (!history || history.entries.length === 0) {\n console.log('No history data found. Run \"hydration-audit analyze\" at least once.');\n return;\n }\n\n const limit = parseInt(opts.limit) || 10;\n const entries = history.entries.slice(0, limit);\n\n console.log(`\\n Hydration Tax History — ${history.projectName}\\n`);\n console.log(' Date Islands Gzip Size Issues Commit');\n console.log(' ' + '─'.repeat(70));\n\n for (const entry of entries) {\n const date = new Date(entry.timestamp).toLocaleDateString('en-US', {\n month: 'short', day: '2-digit', year: 'numeric', hour: '2-digit', minute: '2-digit',\n });\n const commit = entry.commitHash ? entry.commitHash.slice(0, 8) : '--------';\n const issues = (entry.issuesBySeverity.error ?? 0) + (entry.issuesBySeverity.warning ?? 0);\n console.log(\n ` ${date.padEnd(22)} ${String(entry.totalIslands).padStart(4)} ${formatBytes(entry.totalGzipSize).padStart(10)} ${String(issues).padStart(6)} ${commit}`,\n );\n }\n\n console.log();\n });\n\n// ─── lighthouse command ──────────────────────────────────────────\n\nprogram\n .command('lighthouse')\n .description('Run Lighthouse and correlate with hydration costs')\n .argument('<url>', 'URL to test')\n .option('--report <path>', 'Path to hydration-audit report JSON')\n .option('--runs <n>', 'Number of Lighthouse runs to average', '1')\n .option('--desktop', 'Use desktop emulation instead of mobile')\n .action(async (url: string, opts: any) => {\n const {\n runLighthouse,\n correlateLighthouseWithReport,\n formatLighthouseReport,\n } = await import('@hydration-audit/core');\n\n const reportPath = opts.report\n ? path.resolve(opts.report)\n : path.resolve('.hydration-audit-report.json');\n\n console.log(`\\n Running Lighthouse on ${url}...\\n`);\n\n try {\n const metrics = await runLighthouse({\n url,\n runs: parseInt(opts.runs) || 1,\n emulatedFormFactor: opts.desktop ? 'desktop' : 'mobile',\n });\n\n // Try to correlate with hydration report\n if (fs.existsSync(reportPath)) {\n const reportContent = fs.readFileSync(reportPath, 'utf-8');\n const report = JSON.parse(reportContent);\n const lhReport = correlateLighthouseWithReport(metrics, report, url);\n console.log(formatLighthouseReport(lhReport));\n } else {\n // Just print raw Lighthouse metrics\n console.log(` Performance Score: ${metrics.performanceScore}/100`);\n console.log(` FCP: ${metrics.fcp.toFixed(0)}ms LCP: ${metrics.lcp.toFixed(0)}ms`);\n console.log(` TBT: ${metrics.tbt.toFixed(0)}ms TTI: ${metrics.tti.toFixed(0)}ms`);\n console.log(` CLS: ${metrics.cls.toFixed(3)} SI: ${metrics.speedIndex.toFixed(0)}ms`);\n console.log(`\\n Note: No hydration-audit report found at ${reportPath}`);\n console.log(' Run \"hydration-audit analyze\" first for island correlation.\\n');\n }\n } catch (err) {\n console.error(`Lighthouse failed: ${(err as Error).message}`);\n process.exit(1);\n }\n });\n\n// ─── Parse and run ───────────────────────────────────────────────\n\nprogram.parse();\n\n// ─── Helpers ─────────────────────────────────────────────────────\n\nfunction emitGitHubAnnotations(report: any): void {\n for (const island of report.islands) {\n for (const issue of island.issues) {\n const level = issue.severity === 'error' ? 'error' : 'warning';\n const file = island.component.sourceFile;\n const line = island.component.sourceLine ?? 1;\n console.log(\n `::${level} file=${file},line=${line}::${issue.message}`,\n );\n }\n }\n\n // Write GitHub step summary\n if (process.env.GITHUB_STEP_SUMMARY) {\n const summary = [\n '## Hydration Tax Report',\n '',\n `| Metric | Value |`,\n `| --- | --- |`,\n `| Islands | ${report.totals.totalIslands} |`,\n `| Total JS (gzip) | ${formatBytes(report.totals.totalGzipSize)} |`,\n `| Errors | ${report.totals.issuesBySeverity.error} |`,\n `| Warnings | ${report.totals.issuesBySeverity.warning} |`,\n '',\n ].join('\\n');\n\n fs.appendFileSync(process.env.GITHUB_STEP_SUMMARY, summary);\n }\n}\n\nfunction formatBytes(bytes: number): string {\n if (bytes < 1024) return `${bytes} B`;\n const kb = bytes / 1024;\n if (kb < 1024) return `${kb.toFixed(1)} KB`;\n const mb = kb / 1024;\n return `${mb.toFixed(2)} MB`;\n}\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts"],"sourcesContent":["import { Command } from 'commander';\nimport { analyze, resolveConfig, loadConfig } from '@hydration-audit/core';\nimport type { UserConfig } from '@hydration-audit/core';\nimport { auditImages } from '@hydration-audit/image-auditor';\nimport fs from 'node:fs';\nimport path from 'node:path';\n\nconst program = new Command();\n\nprogram\n .name('hydration-audit')\n .description('Analyze JavaScript hydration costs in island-architecture frameworks')\n .version('0.1.0');\n\n// ─── analyze command ─────────────────────────────────────────────\n\nprogram\n .command('analyze')\n .description('Analyze hydration costs in a built project')\n .argument('[dir]', 'Build output directory', '.')\n .option('--json', 'Output JSON report only')\n .option('--ci', 'CI mode — exit with code 1 on errors')\n .option('--no-terminal', 'Suppress terminal output')\n .option('--verbose', 'Enable verbose logging')\n .option('--watch', 'Watch for changes and re-analyze')\n .option('--config <path>', 'Path to config file')\n .option('--budget <bytes>', 'Per-island gzip budget in bytes', parseInt)\n .action(async (dir: string, opts: any) => {\n try {\n const cwd = path.resolve(dir);\n\n const configOverrides: UserConfig = {};\n\n if (opts.json) {\n configOverrides.output = { terminal: false, json: '.hydration-audit-report.json', dashboard: false };\n }\n\n if (opts.noTerminal) {\n configOverrides.output = { ...configOverrides.output, terminal: false, json: '.hydration-audit-report.json', dashboard: false };\n }\n\n if (opts.budget) {\n configOverrides.thresholds = { islandBudget: opts.budget };\n }\n\n const report = await analyze({\n config: configOverrides,\n configPath: opts.config,\n cwd,\n verbose: opts.verbose,\n });\n\n // In JSON mode, print the report to stdout\n if (opts.json) {\n console.log(JSON.stringify(report, null, 2));\n }\n\n // In CI mode, exit with code 1 if there are errors\n if (opts.ci) {\n const config = resolveConfig(configOverrides, cwd);\n const failOn = config.ci.failOn;\n\n if (failOn === 'none') {\n process.exit(0);\n }\n\n const hasErrors = report.totals.issuesBySeverity.error > 0;\n const hasWarnings = report.totals.issuesBySeverity.warning > 0;\n\n if (failOn === 'error' && hasErrors) {\n // Emit GitHub Actions annotations if in CI\n if (process.env.CI) {\n emitGitHubAnnotations(report);\n }\n process.exit(1);\n }\n\n if (failOn === 'warning' && (hasErrors || hasWarnings)) {\n if (process.env.CI) {\n emitGitHubAnnotations(report);\n }\n process.exit(1);\n }\n }\n // Watch mode — re-analyze when build output changes\n if (opts.watch) {\n console.log('\\nWatching for changes in', cwd, '...\\n');\n const outDir = path.resolve(cwd, 'dist');\n let debounceTimer: ReturnType<typeof setTimeout> | null = null;\n\n fs.watch(outDir, { recursive: true }, (eventType, filename) => {\n if (!filename?.match(/\\.(js|mjs|html)$/)) return;\n if (debounceTimer) clearTimeout(debounceTimer);\n debounceTimer = setTimeout(async () => {\n console.log(`\\nChange detected: ${filename}. Re-analyzing...\\n`);\n try {\n await analyze({\n config: configOverrides,\n configPath: opts.config,\n cwd,\n verbose: opts.verbose,\n });\n } catch (e) {\n console.error(`Re-analysis failed: ${(e as Error).message}`);\n }\n }, 500);\n });\n\n // Keep process alive\n process.stdin.resume();\n }\n } catch (err) {\n console.error(`Error: ${(err as Error).message}`);\n process.exit(1);\n }\n });\n\n// ─── init command ────────────────────────────────────────────────\n\nprogram\n .command('init')\n .description('Create a hydration-audit config file')\n .action(async () => {\n const configPath = path.resolve('.hydration-audit.config.ts');\n\n if (fs.existsSync(configPath)) {\n console.log('Config file already exists:', configPath);\n return;\n }\n\n const template = `import { defineConfig } from '@hydration-audit/core';\n\nexport default defineConfig({\n framework: 'auto',\n thresholds: {\n islandBudget: 50_000,\n islandBudgetError: 100_000,\n pageBudget: 150_000,\n pageBudgetError: 300_000,\n totalBudget: 500_000,\n },\n rules: {\n 'oversized-island': 'error',\n 'eager-below-fold': 'warn',\n 'duplicate-framework': 'warn',\n 'budget-exceeded': 'error',\n },\n belowFold: ['Footer', 'Comments', 'Newsletter'],\n exclude: [],\n output: {\n json: '.hydration-audit-report.json',\n terminal: true,\n dashboard: false,\n },\n});\n`;\n\n fs.writeFileSync(configPath, template);\n console.log('Created config file:', configPath);\n });\n\n// ─── dashboard command ───────────────────────────────────────────\n\nprogram\n .command('dashboard')\n .description('Launch the hydration tax dashboard')\n .argument('[dir]', 'Project directory', '.')\n .option('--port <port>', 'Port number', '4173')\n .option('--report <path>', 'Path to report JSON file')\n .action(async (dir: string, opts: any) => {\n const cwd = path.resolve(dir);\n const reportPath = opts.report\n ? path.resolve(opts.report)\n : path.resolve(cwd, '.hydration-audit-report.json');\n\n if (!fs.existsSync(reportPath)) {\n console.error(\n `Report file not found: ${reportPath}\\n` +\n `Run \"hydration-audit analyze\" first to generate the report.`,\n );\n process.exit(1);\n }\n\n try {\n const { startDashboard } = await import('@hydration-audit/dashboard');\n const { url } = await startDashboard(reportPath, parseInt(opts.port));\n console.log(`Dashboard running at ${url}`);\n console.log('Press Ctrl+C to stop.\\n');\n\n // Keep process alive\n process.stdin.resume();\n } catch (err) {\n console.error(`Failed to start dashboard: ${(err as Error).message}`);\n console.error('Make sure @hydration-audit/dashboard is installed.');\n process.exit(1);\n }\n });\n\n// ─── image-audit command ─────────────────────────────────────────\n\nprogram\n .command('image-audit')\n .description('Audit images and media assets in a built project')\n .argument('[dir]', 'Build output directory', '.')\n .option('--json', 'Output JSON report only')\n .option('--config <path>', 'Path to config file')\n .action(async (dir: string, opts: any) => {\n try {\n const cwd = path.resolve(dir);\n const loaded = await loadConfig(opts.config ? path.dirname(path.resolve(opts.config)) : cwd);\n const rawConfig = loaded?.config || {};\n\n const config = resolveConfig(rawConfig, cwd);\n const outDir = config.outDir;\n\n if (!fs.existsSync(outDir)) {\n console.error(`Error: Build directory \"${outDir}\" not found. Please run your build first.`);\n process.exit(1);\n }\n\n let projectName = 'unknown';\n try {\n const pkgPath = path.join(config.rootDir, 'package.json');\n if (fs.existsSync(pkgPath)) {\n const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf-8'));\n projectName = pkg.name || 'unknown';\n }\n } catch {}\n\n if (!opts.json) {\n console.log(`\\n Auditing images and media in \"${outDir}\"...\\n`);\n }\n\n const report = await auditImages(outDir, {\n cdnUrls: (rawConfig as any).cdnUrls || [],\n maxSize: (rawConfig as any).maxSize,\n checkRemote: (rawConfig as any).checkRemote,\n }, projectName);\n\n const reportPath = path.resolve(cwd, '.image-audit-report.json');\n fs.writeFileSync(reportPath, JSON.stringify(report, null, 2));\n\n if (opts.json) {\n console.log(JSON.stringify(report, null, 2));\n return;\n }\n\n console.log(` Image & Media Audit Summary — ${report.projectName}`);\n console.log(` ${'─'.repeat(50)}`);\n console.log(` Total Pages: ${report.totals.totalPages}`);\n console.log(` Total Images: ${report.totals.totalImages}`);\n console.log(` Total Attachments: ${report.totals.totalAttachments}`);\n console.log(` Total Issues: ${report.totals.totalIssues}`);\n console.log(` Errors: ${report.totals.issuesBySeverity.error}`);\n console.log(` Warnings: ${report.totals.issuesBySeverity.warning}`);\n console.log(` Info: ${report.totals.issuesBySeverity.info}`);\n console.log(` ${'─'.repeat(50)}`);\n\n if (report.totals.totalIssues > 0) {\n console.log(`\\n Issues breakdown:`);\n for (const page of report.pages) {\n const pageIssuesCount = page.images.reduce((sum, img) => sum + img.issues.length, 0) +\n page.attachments.reduce((sum, att) => sum + att.issues.length, 0);\n if (pageIssuesCount === 0) continue;\n\n console.log(`\\n Route: ${page.route} (${page.htmlFile})`);\n \n for (const img of page.images) {\n for (const issue of img.issues) {\n const prefix = issue.severity === 'error' ? '🔴 [ERROR]' : issue.severity === 'warning' ? '🟡 [WARN]' : '🔵 [INFO]';\n console.log(` ${prefix} ${issue.message}`);\n console.log(` Rec: ${issue.recommendation}`);\n }\n }\n for (const att of page.attachments) {\n for (const issue of att.issues) {\n const prefix = issue.severity === 'error' ? '🔴 [ERROR]' : issue.severity === 'warning' ? '🟡 [WARN]' : '🔵 [INFO]';\n console.log(` ${prefix} ${issue.message}`);\n console.log(` Rec: ${issue.recommendation}`);\n }\n }\n }\n } else {\n console.log(`\\n ✨ No media issues found! All assets healthy.`);\n }\n console.log(`\\n Report written to: ${reportPath}\\n`);\n } catch (err) {\n console.error(`Error: ${(err as Error).message}`);\n process.exit(1);\n }\n });\n\n// ─── history command ─────────────────────────────────────────────\n\nprogram\n .command('history')\n .description('Show hydration cost history over time')\n .argument('[dir]', 'Project directory', '.')\n .option('--limit <n>', 'Number of entries to show', '10')\n .action(async (dir: string, opts: any) => {\n const cwd = path.resolve(dir);\n const { readHistory } = await import('@hydration-audit/core');\n\n const history = readHistory(cwd);\n if (!history || history.entries.length === 0) {\n console.log('No history data found. Run \"hydration-audit analyze\" at least once.');\n return;\n }\n\n const limit = parseInt(opts.limit) || 10;\n const entries = history.entries.slice(0, limit);\n\n console.log(`\\n Hydration Tax History — ${history.projectName}\\n`);\n console.log(' Date Islands Gzip Size Issues Commit');\n console.log(' ' + '─'.repeat(70));\n\n for (const entry of entries) {\n const date = new Date(entry.timestamp).toLocaleDateString('en-US', {\n month: 'short', day: '2-digit', year: 'numeric', hour: '2-digit', minute: '2-digit',\n });\n const commit = entry.commitHash ? entry.commitHash.slice(0, 8) : '--------';\n const issues = (entry.issuesBySeverity.error ?? 0) + (entry.issuesBySeverity.warning ?? 0);\n console.log(\n ` ${date.padEnd(22)} ${String(entry.totalIslands).padStart(4)} ${formatBytes(entry.totalGzipSize).padStart(10)} ${String(issues).padStart(6)} ${commit}`,\n );\n }\n\n console.log();\n });\n\n// ─── lighthouse command ──────────────────────────────────────────\n\nprogram\n .command('lighthouse')\n .description('Run Lighthouse and correlate with hydration costs')\n .argument('<url>', 'URL to test')\n .option('--report <path>', 'Path to hydration-audit report JSON')\n .option('--runs <n>', 'Number of Lighthouse runs to average', '1')\n .option('--desktop', 'Use desktop emulation instead of mobile')\n .action(async (url: string, opts: any) => {\n const {\n runLighthouse,\n correlateLighthouseWithReport,\n formatLighthouseReport,\n } = await import('@hydration-audit/core');\n\n const reportPath = opts.report\n ? path.resolve(opts.report)\n : path.resolve('.hydration-audit-report.json');\n\n console.log(`\\n Running Lighthouse on ${url}...\\n`);\n\n try {\n const metrics = await runLighthouse({\n url,\n runs: parseInt(opts.runs) || 1,\n emulatedFormFactor: opts.desktop ? 'desktop' : 'mobile',\n });\n\n // Try to correlate with hydration report\n if (fs.existsSync(reportPath)) {\n const reportContent = fs.readFileSync(reportPath, 'utf-8');\n const report = JSON.parse(reportContent);\n const lhReport = correlateLighthouseWithReport(metrics, report, url);\n console.log(formatLighthouseReport(lhReport));\n } else {\n // Just print raw Lighthouse metrics\n console.log(` Performance Score: ${metrics.performanceScore}/100`);\n console.log(` FCP: ${metrics.fcp.toFixed(0)}ms LCP: ${metrics.lcp.toFixed(0)}ms`);\n console.log(` TBT: ${metrics.tbt.toFixed(0)}ms TTI: ${metrics.tti.toFixed(0)}ms`);\n console.log(` CLS: ${metrics.cls.toFixed(3)} SI: ${metrics.speedIndex.toFixed(0)}ms`);\n console.log(`\\n Note: No hydration-audit report found at ${reportPath}`);\n console.log(' Run \"hydration-audit analyze\" first for island correlation.\\n');\n }\n } catch (err) {\n console.error(`Lighthouse failed: ${(err as Error).message}`);\n process.exit(1);\n }\n });\n\n// ─── Parse and run ───────────────────────────────────────────────\n\nprogram.parse();\n\n// ─── Helpers ─────────────────────────────────────────────────────\n\nfunction emitGitHubAnnotations(report: any): void {\n for (const island of report.islands) {\n for (const issue of island.issues) {\n const level = issue.severity === 'error' ? 'error' : 'warning';\n const file = island.component.sourceFile;\n const line = island.component.sourceLine ?? 1;\n console.log(\n `::${level} file=${file},line=${line}::${issue.message}`,\n );\n }\n }\n\n // Write GitHub step summary\n if (process.env.GITHUB_STEP_SUMMARY) {\n const summary = [\n '## Hydration Tax Report',\n '',\n `| Metric | Value |`,\n `| --- | --- |`,\n `| Islands | ${report.totals.totalIslands} |`,\n `| Total JS (gzip) | ${formatBytes(report.totals.totalGzipSize)} |`,\n `| Errors | ${report.totals.issuesBySeverity.error} |`,\n `| Warnings | ${report.totals.issuesBySeverity.warning} |`,\n '',\n ].join('\\n');\n\n fs.appendFileSync(process.env.GITHUB_STEP_SUMMARY, summary);\n }\n}\n\nfunction formatBytes(bytes: number): string {\n if (bytes < 1024) return `${bytes} B`;\n const kb = bytes / 1024;\n if (kb < 1024) return `${kb.toFixed(1)} KB`;\n const mb = kb / 1024;\n return `${mb.toFixed(2)} MB`;\n}\n"],"mappings":"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","names":[]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hydration-audit/cli",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.5",
|
|
4
4
|
"description": "CLI for analyzing JavaScript hydration costs",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -15,8 +15,9 @@
|
|
|
15
15
|
},
|
|
16
16
|
"dependencies": {
|
|
17
17
|
"commander": "^12.1.0",
|
|
18
|
+
"@hydration-audit/dashboard": "0.2.5",
|
|
18
19
|
"@hydration-audit/core": "0.2.4",
|
|
19
|
-
"@hydration-audit/
|
|
20
|
+
"@hydration-audit/image-auditor": "0.3.0"
|
|
20
21
|
},
|
|
21
22
|
"devDependencies": {
|
|
22
23
|
"@types/node": "^22.0.0",
|