also looking at this
Promote main to production: signed-out chrome and the subscriber tables
#17
54 files
+4252
-1353
Review threads live on the whole diff, not on one commit, so none are shown here - a thread's line means something in the branch's final form, and painting it into an intermediate step would put it on code it is not about.
| @@ -1,4 +1,4 @@ | ||
| 1 | import { readdirSync, readFileSync, statSync } from 'node:fs' | |
| 1 | import { existsSync, readdirSync, readFileSync, statSync } from 'node:fs' | |
| 2 | 2 | import { join, relative, resolve } from 'node:path' |
| 3 | 3 | |
| 4 | 4 | const root = process.cwd() |
| @@ -35,7 +35,21 @@ function report(file: string, source: string, index: number, message: string): v | ||
| 35 | 35 | |
| 36 | 36 | const files = filesIn(resources) |
| 37 | 37 | const markupFiles = files.filter(file => /\.(?:stx|html)$/.test(file)) |
| 38 | const combined = files.map(file => readFileSync(file, 'utf8')).join('\n') | |
| 38 | /* | |
| 39 | * The two app-wide checks at the bottom read resources/ PLUS the CSS config. | |
| 40 | * Resources alone stopped being enough once page <style> blocks moved into | |
| 41 | * config/crosswind.ts under stx-standards rule 3: the focus and breakpoint | |
| 42 | * treatments are still declared, in the one <style data-crosswind> tag the | |
| 43 | * router preserves, and a resources-only corpus cannot see them. statushq hit | |
| 44 | * exactly that -- CI red on main and deploy-prod skipped for two commits. | |
| 45 | * Missing the config throws rather than reading empty, because a check with | |
| 46 | * nothing to search always passes. | |
| 47 | */ | |
| 48 | const cssConfig = resolve(root, 'config/crosswind.ts') | |
| 49 | if (!existsSync(cssConfig)) | |
| 50 | throw new Error(`validate-ui: expected the CSS config at ${relative(root, cssConfig)}`) | |
| 51 | ||
| 52 | const combined = [...files, cssConfig].map(file => readFileSync(file, 'utf8')).join('\n') | |
| 39 | 53 | |
| 40 | 54 | for (const file of markupFiles) { |
| 41 | 55 | const original = readFileSync(file, 'utf8') |
| @@ -80,7 +94,13 @@ for (const file of markupFiles) { | ||
| 80 | 94 | |
| 81 | 95 | if (!/(?:focus-visible|:focus|focus:|focus-within)/.test(combined)) |
| 82 | 96 | issues.add('resources: no visible keyboard focus treatment found') |
| 83 | if (!/(?:@media|\b(?:sm|md|lg|xl):)/.test(combined)) | |
| 97 | /* | |
| 98 | * The utility half needs a class-token boundary, not \b: \b sits between the | |
| 99 | * hyphen and the s in `--radius-sm:`, so a single custom property name | |
| 100 | * satisfied this check on its own and it could not fail. Found in statushq, | |
| 101 | * where every @media block could be removed and the check still passed. | |
| 102 | */ | |
| 103 | if (!/(?:@media|(?:^|[\s"'`:])(?:sm|md|lg|xl):)/m.test(combined)) | |
| 84 | 104 | issues.add('resources: no responsive breakpoint treatment found') |
| 85 | 105 | |
| 86 | 106 | console.log(`Validated ${markupFiles.length} UI templates across desktop, tablet, and mobile contracts.`) |