@piercebarney/whs-eleventy 2026.9.28 → 2026.9.30
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/lib/compliance.js +24 -2
- package/package.json +1 -1
- package/standard/CHANGELOG.md +39 -0
- package/standard/core.md +2 -2
package/lib/compliance.js
CHANGED
|
@@ -109,6 +109,28 @@ const fromDoctor = (id, extra = "", { strict = false, missingIsFail = false } =
|
|
|
109
109
|
// ---- per-chapter checks ------------------------------------------------
|
|
110
110
|
// Each returns { status, note }. Keep them mechanical; hand judgement to MANUAL.
|
|
111
111
|
|
|
112
|
+
// Used by `privacy` below: the third-party list must render from
|
|
113
|
+
// _data/thirdparties.js — either a Nunjucks `for...in` loop or a JS
|
|
114
|
+
// `.map()`/`.forEach()` iteration.
|
|
115
|
+
const THIRD_PARTY_LOOP = /for\s+t\s+in\s+thirdparties|thirdparties\s*\.\s*(?:map|forEach)\s*\(/;
|
|
116
|
+
|
|
117
|
+
function hasThirdPartyLoop() {
|
|
118
|
+
if (grepSrc(THIRD_PARTY_LOOP).length) return true;
|
|
119
|
+
// A privacy page can be a root-level template (a flat-layout adopted
|
|
120
|
+
// project's own convention, outside grepSrc's src/static/scripts roots
|
|
121
|
+
// entirely). Check top-level files at the project root too — never
|
|
122
|
+
// recursive, so node_modules/.git/etc. are never walked (readdirSync with
|
|
123
|
+
// no recursion, filtered to isFile()).
|
|
124
|
+
try {
|
|
125
|
+
return fs
|
|
126
|
+
.readdirSync(ROOT, { withFileTypes: true })
|
|
127
|
+
.filter((e) => e.isFile() && /\.(js|njk)$/.test(e.name))
|
|
128
|
+
.some((e) => THIRD_PARTY_LOOP.test(fs.readFileSync(path.join(ROOT, e.name), "utf8")));
|
|
129
|
+
} catch {
|
|
130
|
+
return false;
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
|
|
112
134
|
const CHECKS = {
|
|
113
135
|
"repo-hygiene": () => {
|
|
114
136
|
const docs = ["README.md", "CLAUDE.md"].filter((f) => has(f));
|
|
@@ -445,14 +467,14 @@ const CHECKS = {
|
|
|
445
467
|
// The privacy page may be a standalone src/privacy.njk or a pages.json
|
|
446
468
|
// entry rendered by a src/pages/ shell — check the built output, with a
|
|
447
469
|
// source fallback for when _site isn't built yet.
|
|
448
|
-
const built = has("_site/privacy/index.html");
|
|
470
|
+
const built = has("_site/privacy/index.html") || has("_site/privacy.html");
|
|
449
471
|
const source =
|
|
450
472
|
has("src/privacy.njk") ||
|
|
451
473
|
/["']slug["']\s*:\s*["']privacy["']/.test(read("src/content/pages.json") || "");
|
|
452
474
|
if (!built && !source) return { status: FAIL, note: "no privacy page" };
|
|
453
475
|
// The third-party disclosure must be generated from _data/thirdparties.js,
|
|
454
476
|
// never hand-maintained — the loop lives in the privacy shell or an include.
|
|
455
|
-
if (
|
|
477
|
+
if (!hasThirdPartyLoop())
|
|
456
478
|
return {
|
|
457
479
|
status: FAIL,
|
|
458
480
|
note: "privacy third-party list not rendered from _data/thirdparties.js",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@piercebarney/whs-eleventy",
|
|
3
|
-
"version": "2026.9.
|
|
3
|
+
"version": "2026.9.30",
|
|
4
4
|
"description": "The web house style's Eleventy + Netlify tooling — the compliance sweep, the infra doctor, the link/CSP integrity check, and the a11y scan, shared by every project on the stack.",
|
|
5
5
|
"bin": {
|
|
6
6
|
"whs": "cli.js"
|
package/standard/CHANGELOG.md
CHANGED
|
@@ -21,6 +21,45 @@ enforced, new *opt-in* chapters/capabilities, tooling-only changes — is
|
|
|
21
21
|
|
|
22
22
|
---
|
|
23
23
|
|
|
24
|
+
## 2026-09-30 [non-breaking]
|
|
25
|
+
|
|
26
|
+
### packages/whs-eleventy
|
|
27
|
+
|
|
28
|
+
`privacy` in `lib/compliance.js`'s third-party-loop gate
|
|
29
|
+
(`grepSrc(/for\s+t\s+in\s+thirdparties/)`) only recognized Nunjucks
|
|
30
|
+
`for...in` syntax and only scanned `src/`, `static/`, `scripts/` — so a
|
|
31
|
+
root-level, JS-class (`.11ty.js`) privacy page rendering its disclosure list
|
|
32
|
+
via `.map()`/`.forEach()` could pass only by coincidence, via an unrelated
|
|
33
|
+
file elsewhere in the scanned roots happening to contain a matching Nunjucks
|
|
34
|
+
loop. Now also recognizes `.map()`/`.forEach()` iteration, and falls back to
|
|
35
|
+
a non-recursive scan of top-level project-root files when the shared scan
|
|
36
|
+
finds nothing. This resolves the concrete reported case — the check now
|
|
37
|
+
reaches and validates the actual root-level privacy page's own rendering
|
|
38
|
+
instead of passing via a coincidental match — without claiming full
|
|
39
|
+
precision: Eleventy supports too many valid page-rendering shapes for the
|
|
40
|
+
check to reliably identify "the one file that renders this page" in general.
|
|
41
|
+
Local to `privacy`; `grepSrc`'s shared defaults and its other six call sites
|
|
42
|
+
are untouched. Resolves
|
|
43
|
+
`feedback/2026-09-23-privacy-thirdparty-check-scans-wrong-file.md`.
|
|
44
|
+
|
|
45
|
+
---
|
|
46
|
+
|
|
47
|
+
## 2026-09-29 [non-breaking]
|
|
48
|
+
|
|
49
|
+
### packages/whs-eleventy
|
|
50
|
+
|
|
51
|
+
`privacy` in `lib/compliance.js`'s built-output signal only matched a
|
|
52
|
+
directory-style permalink (`_site/privacy/index.html`), so a project with a
|
|
53
|
+
flat `privacy.html` permalink failed the chapter despite having a real,
|
|
54
|
+
working privacy page — the same directory-assumption class as the earlier
|
|
55
|
+
`check-links.js` relative-resolution fix. Now also accepts `_site/privacy.html`,
|
|
56
|
+
mirroring how the adjacent source-level signal in the same chapter already
|
|
57
|
+
carries multiple fallbacks for the same question. No `feedback/` file this
|
|
58
|
+
time: found and fixed while reviewing a real project's migration report, not
|
|
59
|
+
filed by an adopting session. Fixed in `41881c2`.
|
|
60
|
+
|
|
61
|
+
---
|
|
62
|
+
|
|
24
63
|
## 2026-09-28 [non-breaking]
|
|
25
64
|
|
|
26
65
|
### packages/whs-eleventy
|
package/standard/core.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Web project house style — CORE (stack-agnostic)
|
|
2
2
|
|
|
3
|
-
**Version:** 2026-09-
|
|
3
|
+
**Version:** 2026-09-30 · **Status:** active
|
|
4
4
|
|
|
5
5
|
This is the stack-agnostic contract every web project follows, regardless of
|
|
6
6
|
framework, host, or CSS system. It says **what** must be true, with concrete
|
|
@@ -1467,7 +1467,7 @@ governs it and follow that chapter — the slugs are the index (a `<script>` →
|
|
|
1467
1467
|
- production-url: https://example.com
|
|
1468
1468
|
- content-type: tool # tool | article (article => feeds)
|
|
1469
1469
|
- publishing-rate: ~5 pages/week
|
|
1470
|
-
- standard-version: 2026-09-
|
|
1470
|
+
- standard-version: 2026-09-30 # recommended — enables standard-version drift tracking (#compliance)
|
|
1471
1471
|
```
|
|
1472
1472
|
|
|
1473
1473
|
If the section is absent, the assistant's first action is to run the flow above
|