@tideworthy/tidysites 0.1.11 → 0.1.12

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 CHANGED
@@ -24,6 +24,7 @@ nothing else; it lives in `.tidysites/key`, which is kept out of git.
24
24
  | `tidysites push [--prune] [--force] [--dry] [--only pages]` | validate, then write the created and updated entries back (with `--prune`, delete the entries whose files are gone), then pull |
25
25
  | `tidysites shot <path or url> [--mobile] [--tablet] [--desktop] [--wide] [--all] [--first-screen] [--live]` | a screenshot of a page of the preview site, the live site, or any address, saved under `.tidysites/shots/`; none of the width flags means desktop and mobile; a port compares all four. A page taller than 2400px comes in 1600px bands (`<name>-band-01.jpg`, …), top to bottom, that an image reader can take in one at a time. `--menu` opens every navigation menu (burgers, `<details>`, hover dropdowns) before the capture; `--open a,b` clicks those selectors and `--hover a,b` shows the submenus under them, for a popup, a tab or one dropdown |
26
26
  | `tidysites measure <path or url> [--mobile] [--tablet] [--desktop] [--wide] [--all] [--full]` | the page as the browser lays it out: every heading, paragraph, button, picture and section box with its computed type (size, line height, weight, family, colour) and box (position, size, padding, margin, background), saved as JSON under `.tidysites/measures/` and printed. Measure the old page and the new one at the same width and style to the numbers. The same `--menu`, `--open` and `--hover` as `shot`, so a dropdown's open type is measured |
27
+ | `tidysites speed <path or url> [--mobile\|--desktop] [--live] [--against <url>]` | PageSpeed Insights (Google's Lighthouse) for a page of the preview or live site, or any address: scores, lab and field metrics, the largest-paint element and the opportunities with the files they name, saved under `.tidysites/speed/`. `--against <url>` tests the old page in the same call for a side-by-side. Run it on the live site before calling a build done; what the content cannot fix goes in the report |
27
28
  | `tidysites redirect [add <old path> <new path> \| rm <old path>]` | the website's redirects: an old address that must keep working after a port goes here; they ship with the next publish |
28
29
  | `tidysites pages` | every address the website renders |
29
30
  | `tidysites page <path> [--html]` | the rendered page, section by section, with the key each section carries |
package/bin/tidysites.mjs CHANGED
@@ -9,6 +9,7 @@
9
9
  * push [--prune] [--force] [--dry] write the changes back, then pull
10
10
  * shot <path|url> [--mobile] [--tablet] [--desktop] [--wide] [--all] [--first-screen] [--menu] [--open sel,..] [--hover sel,..] screenshot to .tidysites/shots (none = desktop and mobile); a tall page comes in 1600px bands; --menu opens every navigation menu first
11
11
  * measure <path|url> [--mobile] [--tablet] [--desktop] [--wide] [--all] [--full] [--menu] [--open sel,..] [--hover sel,..] the computed type and boxes of a page: the numbers to style to and diff
12
+ * speed <path|url> [--mobile|--desktop] [--live] [--against <url>] PageSpeed Insights for a page, and the old page beside it
12
13
  * redirect [add <old> <new> | rm <old>] the website's redirects (an old address that must keep working goes here)
13
14
  * pages every address the website renders
14
15
  * page <path> [--html] the rendered page, section by section
@@ -434,6 +435,39 @@ async function measure(ctx) {
434
435
  }
435
436
  }
436
437
 
438
+ /** PageSpeed Insights for a page of the website (or any address), and the old page beside it with --against. */
439
+ async function speed(ctx) {
440
+ const target = positional[0] ?? '/';
441
+ const isUrl = /^https?:\/\//.test(target);
442
+ const strategy = flags.mobile && !flags.desktop ? 'mobile' : flags.desktop && !flags.mobile ? 'desktop' : 'both';
443
+ const args = { site_id: ctx.cfg.site, strategy, ...(isUrl ? { url: target } : { path: target, site: flags.live ? 'live' : 'preview' }), ...(flags.against ? { compare_url: String(flags.against) } : {}) };
444
+ say(`Testing${flags.against ? ' both pages' : ''} with PageSpeed Insights (a minute or two)...`);
445
+ const { json } = await tool(ctx.cfg, 'speed_website', args);
446
+ if (!json?.page) fail('No result came back.');
447
+ const outDir = join(ctx.dir, META, 'speed');
448
+ mkdirSync(outDir, { recursive: true });
449
+ const stamp = new Date().toISOString().replace(/[-:.]/g, '').slice(4, 18);
450
+ const base = (isUrl ? new URL(target).hostname + new URL(target).pathname : target).replace(/[^a-z0-9]+/gi, '-').replace(/^-|-$/g, '') || 'home';
451
+ const file = join(outDir, `${base}-${stamp}.json`);
452
+ writeFileSync(file, JSON.stringify(json, null, '\t'));
453
+ const n = (v, unit = '') => (v === null || v === undefined ? '—' : `${v}${unit}`);
454
+ const line = (r) => r.error ? ` ${r.strategy.padEnd(8)} ${r.error}` : ` ${r.strategy.padEnd(8)} perf ${n(r.scores.performance)} a11y ${n(r.scores.accessibility)} best ${n(r.scores.best_practices)} seo ${n(r.scores.seo)} LCP ${n(r.lab.lcp_ms, 'ms')} FCP ${n(r.lab.fcp_ms, 'ms')} TBT ${n(r.lab.tbt_ms, 'ms')} CLS ${n(r.lab.cls)} ${r.lab.total_bytes ? Math.round(r.lab.total_bytes / 1024) + ' KB' : '—'} in ${n(r.lab.requests)} requests${r.field ? ` field: LCP ${n(r.field.lcp_ms, 'ms')} INP ${n(r.field.inp_ms, 'ms')} CLS ${n(r.field.cls)} (${r.field.overall})` : ''}`;
455
+ say(`\n${json.target}`);
456
+ for (const r of json.page) say(line(r));
457
+ if (json.compare) { say(`\n${json.compare_url} (the page it is compared with)`); for (const r of json.compare) say(line(r)); }
458
+ for (const r of json.page) {
459
+ if (r.error) continue;
460
+ say(`\n ${r.strategy}: what to change`);
461
+ if (r.lcp_element) say(` largest paint: ${r.lcp_element.replace(/\s+/g, ' ').slice(0, 160)}`);
462
+ for (const o of r.opportunities.slice(0, 8)) {
463
+ say(` • ${o.title}${o.savings_ms ? ` (${o.savings_ms} ms)` : ''}${o.savings_kb ? ` (${o.savings_kb} KB)` : ''}`);
464
+ for (const it of o.items.slice(0, 4)) say(` ${it}`);
465
+ }
466
+ for (const d of r.diagnostics.slice(0, 8)) say(` · ${d}`);
467
+ }
468
+ say(`\nSaved to ${relative(process.cwd(), file)}. Full reports: ${json.page.filter((r) => !r.error).map((r) => r.report_url).join(' ')}`);
469
+ }
470
+
437
471
  /** The website's redirects: list them, add one, remove one. They ship with the next publish. */
438
472
  async function redirect(ctx) {
439
473
  const sub = positional[0];
@@ -642,6 +676,8 @@ async function main() {
642
676
  }
643
677
  case 'measure':
644
678
  return measure(config());
679
+ case 'speed':
680
+ return speed(config());
645
681
  case 'redirect':
646
682
  case 'redirects':
647
683
  return redirect(config());
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tideworthy/tidysites",
3
- "version": "0.1.11",
3
+ "version": "0.1.12",
4
4
  "description": "Pull a Tidysites website to files, edit, validate against its content model, push it back, screenshot the result.",
5
5
  "type": "module",
6
6
  "bin": {