@caltho/site-kit 0.2.5 → 0.2.6

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
@@ -58,7 +58,7 @@ the kit only checks it's a URL.
58
58
  | **Content** (`@caltho/site-kit/content`) | `defineSite` and the pieces to build blocks from (`text`, `md`, `image`, `link`, `href`, `business`, …); the checks, including that every link lands on a page and every `#part` exists; the JSON Schema. |
59
59
  | **prepare** | Loads the content from CalOS (`CALOS_CONTENT_URL`, `CALOS_SITE_TOKEN`; the site's own while nothing's published there) or `content/site.json`, sends CalOS the schema, checks it, writes the schema, and makes every picture the content names as WebP at the widths screens need plus a 1200×630 share crop, named by content so they cache for a year. |
60
60
  | **finish** | After the framework's build: a content security policy in every page (each inline script named by hash), `_headers` (security headers, long caching, `noindex` on preview addresses), and `_redirects` from CalOS (`CALOS_REDIRECTS_URL`; a build that can't reach CalOS fails rather than lose them). |
61
- | **check** | Every page in a real browser through axe (WCAG 2.0–2.2 A and AA, best practice) on a computer and a phone; fails on any violation, on anything the policy blocks, and on anything that doesn't load. |
61
+ | **check** | Every page in a real browser on a computer and a phone: fails on anything the policy blocks and anything that doesn't load. What axe finds (WCAG 2.0–2.2 A and AA, best practice) is said with where, for the site to weigh (a colour the business chose over the contrast rule is their choice); `--strict` fails on it too, `--allow=rule,rule` leaves named rules out. |
62
62
  | **Search** | Titles that fit, canonical addresses, the business as schema.org data (hours, areas, address), FAQ and breadcrumb data, robots.txt, the sitemap. |
63
63
  | **Policies** | Privacy, accessibility and terms, written from the business's facts and what the site does. Optional: a site calls `policy()` where it wants them. |
64
64
  | **Helpers for components** (`@caltho/site-kit/lib`) | The pieces that touch no files: `md` (Markdown that can't run HTML), `plain`, `policy`, the search data, `imagesIn`, `clock`, `dayRange`, `telHref`. |
@@ -1,4 +1,5 @@
1
1
  export declare function check(dir: string, o?: {
2
- allow?: string[];
2
+ allow?: string[]; /** What axe finds fails the check too. */
3
+ strict?: boolean;
3
4
  log?: (line: string) => void;
4
5
  }): Promise<boolean>;
package/dist/cli/check.js CHANGED
@@ -1,7 +1,9 @@
1
1
  // Checks a built site in a real browser before it's published: every page in its sitemap and the
2
- // 404 page, on a computer screen and a phone, with axe (WCAG 2.2 AA and best practice), and with
3
- // nothing blocked by the site's own content security policy or failing to load. Says what's wrong
4
- // and where; true when nothing is.
2
+ // 404 page, on a computer screen and a phone, with nothing blocked by the site's own content
3
+ // security policy or failing to load, and with axe (WCAG 2.2 AA and best practice). Something
4
+ // broken fails the check. What axe finds is said, with where, but is the site's to weigh (a colour
5
+ // the business chose over the contrast rule is their choice): it fails the check only with
6
+ // `strict`. True when nothing is broken (and, strict, nothing is found).
5
7
  var __rewriteRelativeImportExtension = (this && this.__rewriteRelativeImportExtension) || function (path, preserveJsx) {
6
8
  if (typeof path === "string" && /^\.\.?\//.test(path)) {
7
9
  return path.replace(/\.(tsx)$|((?:\.d)?)((?:\.[^./]+?)?)\.([cm]?)ts$/i, function (m, tsx, d, ext, cm) {
@@ -28,6 +30,7 @@ export async function check(dir, o = {}) {
28
30
  paths.push("/404.html");
29
31
  const server = await serve(dir, port);
30
32
  const problems = [];
33
+ const found = [];
31
34
  const browser = await chromium.launch(process.env.CHROMIUM_PATH ? { executablePath: process.env.CHROMIUM_PATH } : {}).catch((err) => {
32
35
  server.close();
33
36
  throw err;
@@ -58,7 +61,7 @@ export async function check(dir, o = {}) {
58
61
  window.axe.run(document, { runOnly: { type: "tag", values: ["wcag2a", "wcag2aa", "wcag21a", "wcag21aa", "wcag22aa", "best-practice"] } })));
59
62
  for (const v of result.violations.filter((x) => !allow.has(x.id))) {
60
63
  const targets = v.nodes.slice(0, 3).map((n) => n.target.join(" "));
61
- problems.push(`${path} (${screen}): ${v.id}: ${v.help} [${v.impact}] at ${targets.join(", ")}${v.nodes.length > 3 ? ` and ${v.nodes.length - 3} more` : ""}`);
64
+ found.push(`${path} (${screen}): ${v.id}: ${v.help} [${v.impact}] at ${targets.join(", ")}${v.nodes.length > 3 ? ` and ${v.nodes.length - 3} more` : ""}`);
62
65
  }
63
66
  }
64
67
  await context.close();
@@ -68,10 +71,14 @@ export async function check(dir, o = {}) {
68
71
  await browser.close();
69
72
  server.close();
70
73
  }
71
- if (problems.length) {
72
- log(`✗ ${dir}: ${problems.length} problem${problems.length === 1 ? "" : "s"}\n - ${[...new Set(problems)].join("\n - ")}`);
73
- return false;
74
- }
75
- log(`✓ ${dir}: ${paths.length} pages, on a computer and a phone: no accessibility problems axe can find${allow.size ? ` (except the ones allowed: ${[...allow].join(", ")})` : ""}, nothing blocked or broken`);
76
- return true;
74
+ const list = (lines) => `\n - ${[...new Set(lines)].join("\n - ")}`;
75
+ if (problems.length)
76
+ log(`✗ ${dir}: ${problems.length} problem${problems.length === 1 ? "" : "s"}${list(problems)}`);
77
+ else
78
+ log(`✓ ${dir}: ${paths.length} pages, on a computer and a phone: nothing blocked or broken`);
79
+ if (found.length)
80
+ log(`${o.strict ? "✗" : "•"} ${dir}: accessibility, ${found.length} thing${found.length === 1 ? "" : "s"} to look at${o.strict ? "" : " (the site's to weigh; --strict fails on them)"}${list(found)}`);
81
+ else
82
+ log(`✓ ${dir}: no accessibility problems axe can find${allow.size ? ` (except the ones allowed: ${[...allow].join(", ")})` : ""}`);
83
+ return !problems.length && (!o.strict || !found.length);
77
84
  }
package/dist/cli/index.js CHANGED
@@ -2,7 +2,7 @@
2
2
  // site-kit prepare content checked, schema written, images ready for the build (.site/, public/)
3
3
  // site-kit finish [out] after the build: the content security policy, _headers, _redirects
4
4
  // site-kit serve [out] [port] the build served as Cloudflare Pages will
5
- // site-kit check [out] [--allow=rule,rule] every page in a real browser: axe, blocked or broken loads
5
+ // site-kit check [out] [--allow=rule,rule] [--strict] every page in a real browser: blocked or broken loads fail; what axe finds is said (fails only with --strict)
6
6
  import { resolve } from "node:path";
7
7
  import { finish } from "../finish.js";
8
8
  import { prepare } from "../prepare.js";
@@ -23,7 +23,7 @@ try {
23
23
  const { check } = await import("./check.js");
24
24
  const allow = args.filter((a) => a.startsWith("--allow=")).flatMap((a) => a.slice(8).split(","));
25
25
  const dir = args.find((a) => !a.startsWith("--")) ?? "out";
26
- process.exitCode = (await check(resolve(dir), { allow })) ? 0 : 1;
26
+ process.exitCode = (await check(resolve(dir), { allow, strict: args.includes("--strict") })) ? 0 : 1;
27
27
  }
28
28
  else {
29
29
  console.error("Usage: site-kit prepare | finish [out] | serve [out] [port] | check [out]");
@@ -149,7 +149,7 @@ export function defineSite(blocks, o = {}) {
149
149
  lang: z.string().default("en-US").meta({ title: "Language" }),
150
150
  business,
151
151
  /** Where the contact forms post to, if the site has any (a CalOS form's address, or another service's). */
152
- form: z.url({ protocol: /^https?$/ }).meta({ title: "Where its forms send" }).optional(),
152
+ form: z.url({ protocol: /^https?$/ }).meta({ title: "Where its forms send", "x-ui": "form" }).optional(),
153
153
  /** The header's links in order, and its button: edited as one list in CalOS. Left out, the pages' menu labels are used. */
154
154
  menu: z
155
155
  .object({
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@caltho/site-kit",
3
- "version": "0.2.5",
3
+ "version": "0.2.6",
4
4
  "description": "What every Caltho site is built with: the contract with CalOS (content, and how a site declares what is editable), images, search and security, and the checks",
5
5
  "license": "MIT",
6
6
  "type": "module",