pattern-reviewer-vite
Skill0 starsChanged 5 months agoMITView on GitHub
Vite audit: stack choice (Vite for CSR, Next for SSR/SSG/SEO); `VITE_` prefix on every client-exposed `import.meta.env` read; `VITE_` is NOT a security boundary (secret-shaped `VITE_*` is HIGH); `loadEnv(..., '')` leaks server secrets; `.env.example` mirrors every `VITE_*`; type-check gap (no `tsc --noEmit`); prod sourcemap without Sentry; route-boundary lazy-load. Activate when the diff touches `vite.config.*`, `vitest.config.*`, or `import.meta.env` reads.
- Reads credentials
In this repository74 other files it gives its agents.
- CLAUDE.mdCLAUDE.md
- create-bug-issueSkill
- create-enhancement-issueSkill
- create-feature-issuesSkill
- dream-summary-memorySkill
- memory-conventionSkill
- operation-gitSkill
- pattern-architect-api-endpointSkill
- pattern-architect-data-modelSkill
- pattern-architect-deep-moduleSkill
- pattern-e2e-coding-standardSkill
- pattern-engineer-apiSkill
- pattern-engineer-backend-standardSkill
- pattern-engineer-coding-standardSkill
- pattern-engineer-containerSkill
- pattern-engineer-databaseSkill
- pattern-engineer-fastapiSkill
- pattern-engineer-frontend-standardSkill
- pattern-engineer-goSkill
- pattern-engineer-javaSkill
- pattern-engineer-kotlinSkill
- pattern-engineer-nodeSkill
- pattern-engineer-non-functionalSkill
- pattern-engineer-observabilitySkill
- pattern-engineer-pythonSkill
- pattern-engineer-rustSkill
- pattern-engineer-securitySkill
- pattern-engineer-ssrSkill
- pattern-engineer-swiftSkill
- pattern-engineer-typescriptSkill
- pattern-engineer-viteSkill
- pattern-exploratory-testingSkill
- pattern-reviewer-apiSkill
- pattern-reviewer-backend-standardSkill
- pattern-reviewer-coding-standardSkill
- pattern-reviewer-containerSkill
- pattern-reviewer-contractSkill
- pattern-reviewer-databaseSkill
- pattern-reviewer-fastapiSkill
- pattern-reviewer-frontend-standardSkill
- pattern-reviewer-goSkill
- pattern-reviewer-javaSkill
- pattern-reviewer-kotlinSkill
- pattern-reviewer-nodeSkill
- pattern-reviewer-non-functionalSkill
- pattern-reviewer-observabilitySkill
- pattern-reviewer-pythonSkill
- pattern-reviewer-rustSkill
- pattern-reviewer-securitySkill
- pattern-reviewer-ssrSkill
- pattern-reviewer-swiftSkill
- pattern-reviewer-test-coverageSkill
- pattern-reviewer-typescriptSkill
- pattern-test-coverageSkill
- principle-engineer-tddSkill
- scaffold-projectSkill
- workflow-architect-interviewSkill
- workflow-designer-sample-pagesSkill
- workflow-design-interviewSkill
- workflow-e2e-authorSkill
- The first 60 shown.
The file
We split this skill into the rules it gives an agent
5 rules. About 0 are common, 1 is distinctive, and 4 are very specific to this project.
≈0
Common · estimate
Many files like this have a rule of this kind.
≈1
Distinctive · estimate
This author's own choices. Worth a look.
4
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- Very specific: line 15
After loading this skill, also check $MAIN_ROOT/.claude/memory/patterns/pattern-reviewer-vite.md in the consuming project (resolve …
- Distinctive: line 26
Run these sweeps before walking the patterns — each feeds the matching check below:
- Very specific: line 65
Every import.meta.env.VITE_* the code reads has a placeholder row in .env.example.
- Very specific: line 110
Default target (modules) shipping to a project that explicitly supports legacy browsers (per project's browserslist / requirements) → flag.
- Very specific: line 165
Use the shape in templates/review-comment.md.
≈ “Common” is an estimate: at least 1 in 5 of the 350,495 skill files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.
Files
pattern-reviewer-vite
When to activate
- Reviewing a diff that touches
vite.config.*,vitest.config.*,import.meta.envreads, or Vite-served static assets. - A user says "review the Vite config / dev-server proxy / bundle setup".
Project memory overlay
After loading this skill, also check $MAIN_ROOT/.claude/memory/patterns/pattern-reviewer-vite.md in the consuming project (resolve MAIN_ROOT="$(dirname "$(git rev-parse --path-format=absolute --git-common-dir)")"). If present, load it as an additive overlay to the rules below; if absent, skip silently. See memory-convention for the full contract (additivity, severity floor, conflict surfacing).
Iron rules
- >80% confidence filter. Report only when you are >80% confident. Consolidate similar findings.
- Cite
path/to/file.ext:line. Quote the offending snippet in a BAD block; show the fix in a GOOD block. - Severity is load-bearing. CRITICAL / HIGH block the gate; MEDIUM / LOW are informational. Use the per-pattern severity assigned below.
- Never refer to a finding as
#N— GitHub auto-links those to issues. Use a non-numeric handle (quoted title,F1/F2,Finding 1).
Where to look
Run these sweeps before walking the patterns — each feeds the matching check below:
rg "import\.meta\.env\." src/ # every env read → prefix, secret-shape, .env.example row
rg "loadEnv\(" vite.config.* # prefix discipline on the third argument
rg "sourcemap" vite.config.* # prod source-map exposure
rg "vite preview" Dockerfile* *compose* # preview serving production
rg "server\.|host|port" vite.config.* # host binding (when a dev Dockerfile exists) + committed port overrides
rg "lazy\(" src/ && rg "Suspense" src/ # split points vs fallbacksAlso open: .env.example (diff against the VITE_* reads found above), the CI workflow + package.json scripts (look for tsc --noEmit / vite-plugin-checker — its absence is the type-check gap), and any index.ts barrel re-exports imported from hot-path modules.
Patterns to review
Stack choice (HIGH)
- Vite picked for an app that needs SSR / SSG / ISR / SEO / file-based routing / server components → flag and recommend Next.js.
- Next picked for a pure-CSR internal tool / dashboard / embedded widget → flag (overkill).
VITE_ prefix (HIGH)
import.meta.env.SOMETHINGwithout theVITE_prefix → flag: Vite won't inline the value, so the read returnsundefinedin production.- Same env var read in multiple components / hooks instead of through a single typed accessor module → MEDIUM (centralize).
// BAD — no prefix; undefined in prod
const apiUrl = import.meta.env.API_URL;
// GOOD — VITE_ prefix + centralized accessor
// src/env.ts
export const API_URL = import.meta.env.VITE_API_URL ?? "/api";
// somewhere.ts
import { API_URL } from "@/env";.env.example lockstep (MEDIUM)
- Every
import.meta.env.VITE_*the code reads has a placeholder row in.env.example. - Renamed / removed
VITE_*not reflected in.env.example→ flag. - Server-only secrets without the
VITE_prefix accidentally exposed via a client-side import → HIGH.
vite.config.ts scope (MEDIUM)
- Build
target,resolve.alias, dev-serverproxy,plugins,server.portoverrides → yes, here. - Route-specific logic / app behavior in
vite.config.ts→ flag (belongs in the app). - Dev-server
proxymissing for the backend prefix → flag (the SPA can't hit same-origin and cookies / CSRF break in dev).
Dev-server port (LOW)
server.portoverridden invite.config.ts(committed) for slice-isolation reasons → flag; that's a shell-env override (per engineer-pre-push hook rule), not a permanent config change.
Vitest setup (MEDIUM)
test.globals: trueinvitest.configwithout"vitest/globals"incompilerOptions.types→ flag.test.environment: 'jsdom'missing for component tests that use DOM matchers → flag.vitest.setup.tsmissing the@testing-library/jest-domimport that the matchers need → flag.
Code splitting (MEDIUM)
- A heavy route (
Settings, dashboard, editor) imported eagerly → flag; lazy vialazy(() => import(...)). lazywithout a meaningful<Suspense fallback={...}>→ flag (default is empty; users see a blank screen).- Critical-path landing route lazy-loaded → flag (pays cost on the path you most want fast).
// BAD — heavy route eager; lazy route with empty fallback
import { Settings } from "./pages/Settings";
const Editor = lazy(() => import("./pages/Editor"));
<Suspense><Editor /></Suspense>
// GOOD — heavy routes split, landing route eager, real fallback
import { Home } from "./pages/Home";
const Settings = lazy(() => import("./pages/Settings"));
<Suspense fallback={<PageSkeleton />}><Settings /></Suspense>Static assets (LOW)
- Image / font imported as a string URL instead of
import logoUrl from './logo.svg'→ flag (no hashing → cache-busting breaks). - Asset in
src/that needs a stable URL (used in<img src="/assets/...">) belongs inpublic/→ flag.
Build target (MEDIUM)
- Default
target(modules) shipping to a project that explicitly supports legacy browsers (per project's browserslist / requirements) → flag; bumptargetor add@vitejs/plugin-legacy.
vite preview in production (HIGH)
- Production Dockerfile / compose using
vite preview→ flag; servedist/via nginx / static host.
loadEnv prefix discipline (HIGH)
// BAD — empty prefix loads ALL env vars (server secrets included)
const env = loadEnv(mode, process.cwd(), '');
return { define: { __API_URL__: JSON.stringify(env.API_URL) } };
// GOOD — explicit prefix list
const env = loadEnv(mode, process.cwd(), ['VITE_']);loadEnv(mode, root, '') → HIGH security. A later define: {...} mistake can inline a server secret into the client bundle.
Production source maps (HIGH)
build.sourcemap: true(or'inline') in a production config without evidence of upload to an error tracker (Sentry / Bugsnag) → HIGH; ships the original source code publicly.- Acceptable:
'hidden'+ upload-and-delete pipeline.
// BAD — original sources served to every visitor
export default defineConfig({ build: { sourcemap: true } });
// GOOD — maps generated for the tracker, never referenced from the bundle
export default defineConfig({ build: { sourcemap: "hidden" } });
// CI: sentry-cli sourcemaps upload dist/ && rm dist/**/*.mapType-check gap (HIGH)
- TypeScript project,
vite buildstep, notsc --noEmitin CI AND novite-plugin-checkerinvite.config.ts→ HIGH. Type errors silently ship.
Containerized dev without server.host: true (HIGH)
- Project ships a dev Dockerfile / dev compose service but
vite.config.tsleavesserver.hostunset (defaults tolocalhost) → HIGH. The container binds 127.0.0.1; the host can't reach the dev server.
Barrel files in the hot path (MEDIUM)
index.tsfiles that re-export an entire directory's exports, imported by hot-path modules → MEDIUM. Each import loads every re-export; this is the #1 dev-server slowdown flagged by Vite docs.
Hand-rolled resolve.alias duplicating tsconfig.paths (LOW)
vite.config.tslistsresolve.aliasentries that already exist intsconfig.jsoncompilerOptions.paths→ LOW. Recommendvite-tsconfig-paths; eliminates the two-place edit.
VITE_ prefix as a security boundary (HIGH)
- A secret-shaped value (token, API key, signing secret, DB URL) stored in a
VITE_*env var and read fromimport.meta.env→ HIGH.VITE_*values are statically inlined into the shipped JS and extractable by anyone with a DevTools console; secrets must live server-side.
Constructing the finding
Use the shape in templates/review-comment.md.
Does it work?
Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.
No reviews yet
People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.
- It worked
- Partly
- It didn't
Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.
Your trail
Files you open will collect here, so you can find them again.
Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.
Now do this for your own.
Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.

