Add lint, typecheck, and html-validate guardrails

Tooling:
- ESLint 9 flat config (eslint.config.mjs) with three scopes:
  - browser vanilla JS for js/ (no modules, window globals)
  - Node ESM for scripts/
  - typescript-eslint for tests/ and *.ts
- Stylelint with a deliberately minimal "bug-only" ruleset
  (block-no-empty, color-no-invalid-hex, function-no-unknown,
  property-no-unknown, …) — no nags about compact handwritten CSS
- html-validate against index.html (DOCTYPE, accessible names,
  non-redundant ARIA roles, valid landmark usage)
- TypeScript --noEmit strict on playwright.config.ts + tests/*.ts

npm scripts:
- lint        — parallel run of lint:js, lint:css, lint:html (~3s)
- lint:js{,:fix}, lint:css{,:fix}, lint:html
- typecheck   — tsc --noEmit
- check       — lint + typecheck + test in parallel (CI entry point)

Fixes uncovered by the new checks:
- js/app.js: drop unused `activeTimers`, drop unused `t` lookup in
  refreshTreeRow, switch `activeTags` let→const, replace empty
  `catch(_)` blocks with parameter-less catch + intent comment
- js/data.js: drop unused index arg in renderExperience
- index.html: uppercase DOCTYPE, drop redundant role="banner" /
  role="contentinfo" on <header>/<footer>, add aria-labels on icon-only
  Stop / Reset buttons, add role="group" to the tag bar so its
  aria-label is valid, add explicit type="button" everywhere
- tests/portfolio.spec.ts: landmark test now uses getByRole() rather
  than the explicit [role="banner"] attribute selector

Housekeeping:
- .gitignore picks up .eslintcache / .stylelintcache / *.tsbuildinfo
- README documents the lint + check toolchain

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Builder
2026-05-11 23:06:42 -04:00
co-authored by Cursor
parent b596b2d608
commit 6f897fafb9
12 changed files with 3447 additions and 29 deletions
+1 -1
View File
@@ -414,7 +414,7 @@ function renderAbout(){
function renderExperience(){
return `<div class="block">${
PORTFOLIO.experience.map((e,i)=>`
PORTFOLIO.experience.map((e)=>`
<h4>${_esc(e.company)}</h4>
<p style="font-family:var(--font-mono);font-size:11.5px;color:var(--text-3);margin:2px 0 8px;line-height:1.45">${_esc(e.role)} · ${_esc(e.when)} · ${_esc(e.where)}</p>
<ul>${e.bullets.map(b=>`<li>${_esc(b)}</li>`).join('')}</ul>