create-website-build-kit 0.1.2 → 0.1.3

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-website-build-kit",
3
- "version": "0.1.2",
3
+ "version": "0.1.3",
4
4
  "description": "Scaffold a production marketing site \u2014 Astro on Cloudflare Workers, with the gates, the migration playbook and the accessibility work already wired.",
5
5
  "keywords": [
6
6
  "astro",
@@ -930,3 +930,42 @@ Worth knowing before you go hunting: this got *more* visible when the run starte
930
930
  covering both colour schemes, because that doubles the number of Chrome sessions
931
931
  and so doubles the chances of hitting the race. The change that surfaced it was
932
932
  not the change that caused it.
933
+
934
+ ### A script throws `ReferenceError` for something nothing ever imported
935
+
936
+ **Symptom:** `npm run recon` runs the whole crawl, prints its sitemap and URL
937
+ sections, and then dies at the last one:
938
+
939
+ ```
940
+ const PRESERVE = PRESERVED;
941
+ ^
942
+ ReferenceError: PRESERVED is not defined
943
+ ```
944
+
945
+ The file imports two things and uses a third. It shipped in a published package
946
+ and a user hit it on a real migration, on Windows, on the first command the
947
+ documentation tells you to run.
948
+
949
+ **Why nothing caught it.** This is the important part, because the instinct is
950
+ that surely *something* would have:
951
+
952
+ | | |
953
+ | --- | --- |
954
+ | `node --check` | Parses. An undefined identifier is **valid syntax** |
955
+ | `astro check` | Types `.astro` and `.ts`. The scripts are standalone `.mjs` |
956
+ | CI | Runs the build. `recon` needs a live site, so CI never runs it |
957
+ | Smoke-running it | The throw is on line 302, reached only after the crawl — tested, and a `--help` load-check passes the broken file |
958
+
959
+ **Fix:** import it. The real fix is the gate — `npm run check:refs` cross-checks
960
+ every name `scripts/lib/*.mjs` exports against every script that uses one, and
961
+ fails when a use has no import.
962
+
963
+ **The first version of that gate was worse than none.** It flagged every
964
+ SCREAMING_CASE identifier that was never bound, and produced seven false
965
+ positives on a clean tree: `WCAG` and `CAA` in prose, `ERR_ABORTED` inside a
966
+ regex literal, `AND` in a comment. Stripping comments and strings with regexes
967
+ is a losing game without a parser. Narrowing it to names the libs actually
968
+ export removed the guesswork — prose never collides with a real export.
969
+
970
+ **A checker with false positives gets switched off, and then its silence means
971
+ "nobody looked" rather than "nothing wrong".**
@@ -89,6 +89,14 @@ const runners = config.defaults?.runners ?? ['htmlcs'];
89
89
  * was tested when half of it was not is the thing somebody hands to a lawyer.
90
90
  * See scripts/lib/schemes.mjs.
91
91
  */
92
+ /*
93
+ * ⚠ `shell: true` ON WINDOWS, and it is not optional there. `npx` is
94
+ * `npx.cmd`, and execFileSync does not resolve .cmd without a shell — it
95
+ * fails ENOENT, which reads as "npx is not installed" on a machine where it
96
+ * plainly is. Left off on POSIX, where a shell buys nothing and costs quoting.
97
+ */
98
+ const WIN = process.platform === 'win32';
99
+
92
100
  const tmp = mkdtempSync(join(tmpdir(), 'a11y-evidence-'));
93
101
 
94
102
  const runScheme = (scheme) => {
@@ -104,7 +112,9 @@ const runScheme = (scheme) => {
104
112
  try {
105
113
  /* pa11y-ci exits non-zero when it finds errors, and still prints the JSON.
106
114
  A non-zero exit here is a RESULT, not a failure to run. */
107
- return JSON.parse(execFileSync('npx', args, { encoding: 'utf8', maxBuffer: 64 * 1024 * 1024 }));
115
+ return JSON.parse(
116
+ execFileSync('npx', args, { encoding: 'utf8', maxBuffer: 64 * 1024 * 1024, shell: WIN }),
117
+ );
108
118
  } catch (error) {
109
119
  const out = error.stdout?.toString() ?? '';
110
120
  try {
@@ -28,6 +28,14 @@ const GREEN = '\x1b[32m';
28
28
  const DIM = '\x1b[2m';
29
29
  const BOLD = '\x1b[1m';
30
30
 
31
+ /*
32
+ * ⚠ `shell: true` ON WINDOWS, and it is not optional there. `npx` is
33
+ * `npx.cmd`, and execFileSync does not resolve .cmd without a shell — it
34
+ * fails ENOENT, which reads as "npx is not installed" on a machine where it
35
+ * plainly is. Left off on POSIX, where a shell buys nothing and costs quoting.
36
+ */
37
+ const WIN = process.platform === 'win32';
38
+
31
39
  const CONFIG = '.pa11yci.json';
32
40
  if (!existsSync(CONFIG)) {
33
41
  console.error(`${RED}✗${RESET} ${CONFIG} not found — run this from the site root.`);
@@ -58,7 +66,7 @@ for (const scheme of schemes) {
58
66
 
59
67
  console.log(`${BOLD}${scheme}${RESET}`);
60
68
  try {
61
- execFileSync('npx', ['pa11y-ci', '--config', file], { stdio: 'inherit' });
69
+ execFileSync('npx', ['pa11y-ci', '--config', file], { stdio: 'inherit', shell: WIN });
62
70
  console.log(` ${GREEN}✓${RESET} ${scheme} clean\n`);
63
71
  } catch {
64
72
  failed++;
@@ -64,6 +64,14 @@ function have(bin, args = ['--version']) {
64
64
  }
65
65
  }
66
66
 
67
+ /** The install line for the platform this is actually running on. */
68
+ const hint = (brew, winget, apt) =>
69
+ process.platform === 'win32'
70
+ ? `winget install ${winget}`
71
+ : process.platform === 'linux'
72
+ ? `sudo apt install ${apt}`
73
+ : `brew install ${brew}`;
74
+
67
75
  function preflight() {
68
76
  const missing = [];
69
77
 
@@ -83,9 +91,12 @@ function preflight() {
83
91
  }
84
92
 
85
93
  const tools = [
86
- ['magick', ['-version'], 'ImageMagick', 'brew install imagemagick'],
87
- ['rsvg-convert', ['--version'], 'rsvg-convert', 'brew install librsvg'],
88
- ['python3', ['--version'], 'Python 3', 'brew install python'],
94
+ /* Per-platform. "brew install" on Windows is not a hint, it is a dead
95
+ end and this preflight exists precisely so a missing tool names its
96
+ own fix. */
97
+ ['magick', ['-version'], 'ImageMagick', hint('imagemagick', 'ImageMagick.ImageMagick', 'imagemagick')],
98
+ ['rsvg-convert', ['--version'], 'rsvg-convert', hint('librsvg', 'GNOME.Librsvg', 'librsvg2-bin')],
99
+ ['python3', ['--version'], 'Python 3', hint('python', 'Python.Python.3.12', 'python3')],
89
100
  ];
90
101
  for (const [bin, args, label, install] of tools) {
91
102
  if (!have(bin, args)) {
@@ -28,6 +28,7 @@
28
28
  import { mkdirSync, writeFileSync } from 'node:fs';
29
29
 
30
30
  import { GONE_TAG } from './lib/inventory.mjs';
31
+ import { PRESERVED } from './lib/preserved.mjs';
31
32
 
32
33
  const RESET = '';
33
34
  const RED = '';
@@ -299,11 +300,9 @@ if (goneList.length) {
299
300
  /* ── 3. Paths other systems point at ──────────────────────────────────── */
300
301
  section('Preserved paths');
301
302
 
302
- const PRESERVE = PRESERVED;
303
-
304
303
  /* Manual redirects again: "serves a feed" and "301s to a feed" are different
305
304
  facts, and only the first means the path must be reproduced. */
306
- const preserved = await pool(PRESERVE, async ([path, why]) => {
305
+ const preserved = await pool(PRESERVED, async ([path, why]) => {
307
306
  const r = await req(`${ORIGIN}${path}`, { method: 'HEAD', redirect: 'manual' });
308
307
  return { path, why, status: r?.status ?? 0, location: r?.headers.get('location') ?? '' };
309
308
  });
@@ -1 +1,5 @@
1
- {}
1
+ {
2
+ "/": "2026-08-26",
3
+ "/accessibility/": "2026-08-26",
4
+ "/contact/": "2026-08-26"
5
+ }