Як ловити типові поломки коду з v0 і Bolt
Автор на note.com описав конвеєр перевірок для коду з v0, Bolt і Lovable. Він ловить три збої. Перший: TSX, обрізаний на ліміті токенів (`Unexpected EOF`). Другий: `useEffect`, що оновлює стан зі свого ж масиву залежностей (`Too many re-renders`). Третій: вгадані типи API, наприклад `userId` замість `user_id`, звідки беруться 422 і `TypeError`.
Python-скрипт рахує дужки й шукає `export`, а також шукає сетер стану в `useEffect`. `tsc` звіряє код з `api.d.ts`, згенерованим з OpenAPI-схеми. Знайдені помилки стають промптом на перегенерацію. Синтаксис скрипт перевіряє лише підрахунком `{}`, `()` і `[]`, тому дужки всередині рядків дадуть хибні спрацювання.
Системний промпт, який зменшує поломки ще до перевірки
Окрім скрипта, автор дає правила для поля System Prompt / Custom Instructions у v0, Bolt чи Lovable. Кожне правило спрямоване на одну з трьох поломок:
- Обрізаний TSX: один файл має бути не довшим за 200 рядків, а складні UI, форми й діалоги винесено в окремі файли (наприклад,
./components/ui/). Скорочення на кшталт// ... existing code ...заборонені, а в кінці файлу обов'язковийexport default ComponentName;. - Нескінченний рендер: стан, який оновлюється в
useEffect, не можна класти в його масив залежностей. Похідні дані рахуються черезuseMemoабо прямо у функції, а не зберігаються в state. - Вгадані типи:
anyзаборонено, типи API імпортуються з@/types/api.ts, а відповіді відповідають інтерфейсуApiResponse<T>з полямиsuccess,dataі необов'язковимerror.
Ліміт рядків допомагає й із першою проблемою: за словами автора, обрізання частіше трапляється тоді, коли модель пхає UI, стан і логіку в один файл.
Синхронізація типів покроково
Типовий симптом: POST /api/v1/users повертає 422, а в тілі відповіді є "loc": ["body", "user_id"] і "field required". Інша форма тієї ж проблеми: обов'язкове поле позначене як user?, і UI падає на undefined. Потрібен бекенд з OpenAPI 3.0+ (FastAPI, DRF, NestJS).
- Забрати схему:
curl http://localhost:8000/openapi.json -o openapi.json - Згенерувати типи:
npx openapi-typescript openapi.json -o src/types/api.ts - Вставити вміст
src/types/api.tsу чат і попросити переписати код під ці типи. - Прогнати
tsc, щоб він звірив згенерований код із цим файлом.
У статті файл типів називається по-різному: api.d.ts у схемі конвеєра і src/types/api.ts у кроках. Оберіть один шлях і пропишіть той самий у системному промпті.
Pre-commit хук
Збережіть скрипт у .git/hooks/pre-commit і виконайте chmod +x .git/hooks/pre-commit. Хук бере staged-файли .ts/.tsx через git diff --cached --name-only --diff-filter=ACM, проганяє кожен через python3 vibe_guardrail.py і відхиляє коміт, якщо скрипт повернув ненульовий код. Брак export вважається лише попередженням і коміт не блокує.
Хук викликає скрипт за відносним шляхом, тому vibe_guardrail.py має лежати в корені репозиторію. Перевіряється файл на диску, а не версія в індексі: якщо частина змін не додана через git add, результат стосуватиметься робочої копії.
Де скрипт помиляється і що перевірити
Лог у статті показує прапорці --openapi, --auto-fix і рядок про автоматичне виправлення тегів. В опублікованому vibe_guardrail.py цього немає: скрипт приймає один файл і лише звітує.
- Обрізання між компонентами зі збалансованими дужками не дає помилки. Якщо при цьому зник
export, буде лише попередження. - Детектор циклу — це регулярний вираз під
useEffect(() => {. Він пропускаєasync-колбеки таfunction, а на вкладених блоках може обрізати тіло ефекту. - Сетер і залежність зіставляються лише за назвою (
setData↔data). Залежністьdata.lengthскрипт не впізнає. - Ефекти з порожнім масивом залежностей або без масиву пропускаються. При цьому
setStateв ефекті без масиву теж може зациклити рендер. - Перш ніж довіряти хуку, закомітьте навмисно обрізаний файл і ефект із
setDataу[data]та переконайтеся, що коміт відхилено.