sitelens 0.1.0 → 0.1.1

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.
Files changed (3) hide show
  1. package/README.md +1 -1
  2. package/dist/cli.js +44 -31
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -6,7 +6,7 @@ A website audit anyone can understand. One command checks performance, SEO, acce
6
6
  npx sitelens
7
7
  ```
8
8
 
9
- On its own, sitelens asks for the report language, the website address and the audit mode; pick with the arrow keys. To skip the questions, give the address and options straight away:
9
+ On its own, sitelens asks for the report language, the website address and the audit mode; pick with the arrow keys. When the report is ready, sitelens prints its full path and opens it when you press Enter. To skip the questions, give the address and options straight away:
10
10
 
11
11
  ```sh
12
12
  npx sitelens https://yourwebsite.com --lang en --out report.pdf
package/dist/cli.js CHANGED
@@ -607,7 +607,7 @@ var require_boolbase = __commonJS({
607
607
  import { spawn, spawnSync } from "node:child_process";
608
608
  import { writeFileSync } from "node:fs";
609
609
  import { createRequire } from "node:module";
610
- import { dirname, join as join2, relative, resolve } from "node:path";
610
+ import { dirname, join as join2, resolve } from "node:path";
611
611
 
612
612
  // ../collect-node/src/browser.ts
613
613
  import { existsSync } from "node:fs";
@@ -2364,10 +2364,10 @@ function compareDocumentPosition(nodeA, nodeB) {
2364
2364
  function uniqueSort(nodes) {
2365
2365
  nodes = nodes.filter((node, i, arr) => !arr.includes(node, i + 1));
2366
2366
  nodes.sort((a, b) => {
2367
- const relative2 = compareDocumentPosition(a, b);
2368
- if (relative2 & DocumentPosition.PRECEDING) {
2367
+ const relative = compareDocumentPosition(a, b);
2368
+ if (relative & DocumentPosition.PRECEDING) {
2369
2369
  return -1;
2370
- } else if (relative2 & DocumentPosition.FOLLOWING) {
2370
+ } else if (relative & DocumentPosition.FOLLOWING) {
2371
2371
  return 1;
2372
2372
  }
2373
2373
  return 0;
@@ -7200,16 +7200,16 @@ var thresholds = {
7200
7200
  cls: { good: 0.1, poor: 0.25 },
7201
7201
  // Lab data has no INP; Total Blocking Time stands in for it with Lighthouse's own bands
7202
7202
  tbt: { good: 200, poor: 600 },
7203
- // TODO(verify): web.dev calls TTFB above 0.8 s "needs improvement"; confirm before 1.0
7203
+ // web.dev calls TTFB above 0.8 s "needs improvement"
7204
7204
  ttfb: { poor: 800 },
7205
- // TODO(verify): page weight bands are a first guess
7205
+ // No standard exists for page weight; these bands are the owner's call (25.10.2026)
7206
7206
  pageWeight: { minor: 3e6, important: 5e6 },
7207
7207
  // Savings worth reporting, in bytes and milliseconds
7208
7208
  imageSavings: 1e5,
7209
7209
  renderBlockingMs: 300,
7210
7210
  title: { min: 30, max: 60 },
7211
7211
  description: { max: 160 },
7212
- // TODO(verify): days before certificate expiry worth a warning
7212
+ // ACME clients such as certbot renew 30 days before expiry, so under 14 days usually means renewal failed
7213
7213
  certificateWarningDays: 14,
7214
7214
  hstsMinMaxAge: 15552e3,
7215
7215
  thinTextLength: 300
@@ -12033,6 +12033,7 @@ Przyk\u0142ad:
12033
12033
  warnings: (count) => `${count} ${plural(count, ["element nie da\u0142 si\u0119 sprawdzi\u0107", "elementy nie da\u0142y si\u0119 sprawdzi\u0107", "element\xF3w nie da\u0142o si\u0119 sprawdzi\u0107"], "pl")}; szczeg\xF3\u0142y w za\u0142\u0105czniku raportu.`,
12034
12034
  failUnder: (score, limit) => `Wynik ${score} jest ni\u017Cszy ni\u017C wymagane ${limit} (--fail-under).`,
12035
12035
  optionLater: (flag) => `Opcja ${flag} b\u0119dzie dost\u0119pna w kolejnej wersji. Audyt dzia\u0142a bez niej.`,
12036
+ openKey: "Naci\u015Bnij Enter, \u017Ceby otworzy\u0107 raport (Esc ko\u0144czy).",
12036
12037
  wizard: {
12037
12038
  language: "J\u0119zyk raportu",
12038
12039
  address: "Adres strony",
@@ -12044,9 +12045,6 @@ Przyk\u0142ad:
12044
12045
  standard: { label: "Standardowy", hint: (pages) => `do ${pages} podstron` },
12045
12046
  thorough: { label: "Dok\u0142adny", hint: (pages) => `do ${pages} podstron, trwa d\u0142u\u017Cej` }
12046
12047
  },
12047
- open: "Otworzy\u0107 raport?",
12048
- yes: "Tak",
12049
- no: "Nie",
12050
12048
  openFailed: (path) => `Nie uda\u0142o si\u0119 otworzy\u0107 raportu. Plik jest tutaj: ${path}`
12051
12049
  },
12052
12050
  errors: {
@@ -12107,6 +12105,7 @@ Example:
12107
12105
  warnings: (count) => `${count} ${count === 1 ? "item" : "items"} could not be checked; details are in the report appendix.`,
12108
12106
  failUnder: (score, limit) => `Score ${score} is below the required ${limit} (--fail-under).`,
12109
12107
  optionLater: (flag) => `The ${flag} option arrives in a later version. The audit works without it.`,
12108
+ openKey: "Press Enter to open the report (Esc to quit).",
12110
12109
  wizard: {
12111
12110
  language: "Report language",
12112
12111
  address: "Website address",
@@ -12118,9 +12117,6 @@ Example:
12118
12117
  standard: { label: "Standard", hint: (pages) => `up to ${pages} pages` },
12119
12118
  thorough: { label: "Thorough", hint: (pages) => `up to ${pages} pages, takes longer` }
12120
12119
  },
12121
- open: "Open the report?",
12122
- yes: "Yes",
12123
- no: "No",
12124
12120
  openFailed: (path) => `Could not open the report. The file is here: ${path}`
12125
12121
  },
12126
12122
  errors: {
@@ -12225,9 +12221,19 @@ import { createInterface } from "node:readline/promises";
12225
12221
  import { stripVTControlCharacters } from "node:util";
12226
12222
 
12227
12223
  // src/ui.ts
12224
+ import { pathToFileURL } from "node:url";
12228
12225
  import { styleText } from "node:util";
12229
12226
  var paint = (style, text3, stream = process.stdout) => styleText(style, text3, { stream });
12230
12227
  var FRAMES = ["\u280B", "\u2819", "\u2839", "\u2838", "\u283C", "\u2834", "\u2826", "\u2827", "\u2807", "\u280F"];
12228
+ function linksWork(stream) {
12229
+ const env = process.env;
12230
+ if (!stream.isTTY || env.CI) return false;
12231
+ if (env.WT_SESSION || Number(env.VTE_VERSION) >= 5e3) return true;
12232
+ return ["vscode", "iTerm.app", "WezTerm", "ghostty"].includes(env.TERM_PROGRAM ?? "");
12233
+ }
12234
+ function fileLink(path, stream = process.stdout) {
12235
+ return linksWork(stream) ? `\x1B]8;;${pathToFileURL(path).href}\x1B\\${path}\x1B]8;;\x1B\\` : path;
12236
+ }
12231
12237
  function scoreStyle(score) {
12232
12238
  return score >= 90 ? "green" : score >= 50 ? "yellow" : "red";
12233
12239
  }
@@ -12343,6 +12349,23 @@ function select2(message, choices, { initial = 0, done = (choice) => `${message}
12343
12349
  draw();
12344
12350
  });
12345
12351
  }
12352
+ function pressEnter(message, { io = terminal } = {}) {
12353
+ const { input, output } = io;
12354
+ return new Promise((resolve2) => {
12355
+ const onKey = (_text, key = {}) => {
12356
+ input.off("keypress", onKey);
12357
+ input.setRawMode(false);
12358
+ input.pause();
12359
+ output.write("\n");
12360
+ resolve2(key.name === "return" || key.name === "enter");
12361
+ };
12362
+ output.write(`${paint("cyan", "\u203A", output)} ${message} `);
12363
+ emitKeypressEvents(input);
12364
+ input.setRawMode(true);
12365
+ input.resume();
12366
+ input.on("keypress", onKey);
12367
+ });
12368
+ }
12346
12369
  async function ask(message, check, { hint = "", io = terminal } = {}) {
12347
12370
  const { input, output } = io;
12348
12371
  const rl = createInterface({ input, output, terminal: true });
@@ -12371,7 +12394,7 @@ async function ask(message, check, { hint = "", io = terminal } = {}) {
12371
12394
  }
12372
12395
 
12373
12396
  // src/cli.ts
12374
- var VERSION = true ? "0.1.0" : "0.0.0-dev";
12397
+ var VERSION = true ? "0.1.1" : "0.0.0-dev";
12375
12398
  var NODE_NEEDED = "22.19.0";
12376
12399
  var MODES = [
12377
12400
  { mode: "quick", pages: 1 },
@@ -12470,19 +12493,7 @@ function openReport(path, m) {
12470
12493
  }
12471
12494
  async function offerToOpen(path, m) {
12472
12495
  console.log("");
12473
- try {
12474
- const open = await select2(
12475
- m.wizard.open,
12476
- [
12477
- { label: m.wizard.yes, value: true },
12478
- { label: m.wizard.no, value: false }
12479
- ],
12480
- { done: (choice) => `${m.wizard.open} ${choice.label}` }
12481
- );
12482
- if (open) openReport(path, m);
12483
- } catch (error) {
12484
- if (!(error instanceof PromptCancelled)) throw error;
12485
- }
12496
+ if (await pressEnter(m.openKey)) openReport(path, m);
12486
12497
  }
12487
12498
  async function audit(options, outPath, m) {
12488
12499
  const browserInfo = findBrowser();
@@ -12541,11 +12552,12 @@ async function audit(options, outPath, m) {
12541
12552
  }
12542
12553
  progress.stop();
12543
12554
  printScores(scores.categories, scores.overall, m);
12544
- const shown = relative(process.cwd(), outPath).startsWith("..") ? outPath : relative(process.cwd(), outPath);
12545
- console.log(`${paint("green", "\u2713")} ${paint("dim", `${m.saved}:`)} ${paint("cyan", shown)}`);
12555
+ console.log(`${paint("green", "\u2713")} ${m.saved}:
12556
+ ${paint("cyan", fileLink(outPath))}`);
12546
12557
  if (options.json)
12547
12558
  console.log(
12548
- `${paint("green", "\u2713")} ${paint("dim", `${m.savedJson}:`)} ${paint("cyan", shown.replace(/\.pdf$/i, "") + ".json")}`
12559
+ `${paint("green", "\u2713")} ${m.savedJson}:
12560
+ ${paint("cyan", fileLink(outPath.replace(/\.pdf$/i, "") + ".json"))}`
12549
12561
  );
12550
12562
  if (warnings.length) console.log(paint("dim", m.warnings(warnings.length)));
12551
12563
  if (options.failUnder !== void 0 && scores.overall !== null && scores.overall < options.failUnder) {
@@ -12611,7 +12623,8 @@ async function main(argv) {
12611
12623
  console.error(m.errors.unexpected(error instanceof Error ? error.message : String(error)));
12612
12624
  return 4 /* Environment */;
12613
12625
  }
12614
- if (interactive && exit === 0 /* Ok */) await offerToOpen(outPath, m);
12626
+ if ((exit === 0 /* Ok */ || exit === 1 /* BelowThreshold */) && canPrompt() && !process.env.CI)
12627
+ await offerToOpen(outPath, m);
12615
12628
  return exit;
12616
12629
  }
12617
12630
  process.exitCode = await main(process.argv.slice(2));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sitelens",
3
- "version": "0.1.0",
3
+ "version": "0.1.1",
4
4
  "description": "Website audit with a plain-language PDF report: performance, SEO, accessibility and security.",
5
5
  "keywords": [
6
6
  "website audit",