Documentation Index
Fetch the complete documentation index at: https://mintlify.com/botnadzor/extension/llms.txt
Use this file to discover all available pages before exploring further.
The Botnadzor Extension follows specific coding conventions to maintain consistency and quality across the codebase. This guide covers the key conventions you should follow when contributing.
React Compiler
Do NOT use manual memoization.
The project uses the React Compiler which automatically handles memoization. You should never use:
useMemo
useCallback
React.memo
The compiler optimizes component re-renders automatically, making these manual optimizations unnecessary and potentially harmful.
// ❌ Bad - Manual memoization
const memoizedValue = useMemo(() => computeExpensiveValue(a, b), [a, b]);
const memoizedCallback = useCallback(() => { doSomething(a, b); }, [a, b]);
const MemoizedComponent = React.memo(MyComponent);
// ✅ Good - Let React Compiler handle it
const value = computeExpensiveValue(a, b);
const handleClick = () => { doSomething(a, b); };
const MyComponent = () => { /* ... */ };
Tailwind CSS Prefix
Content Script Insertions
All Tailwind classes in content script insertions MUST use the bn: prefix.
This prevents style conflicts with VK’s own CSS. Insertions are located in src/entrypoints/content/insertion-variants/.
// ❌ Bad - No prefix in content script
<div className="flex items-center gap-2">
<span className="text-sm font-bold">Bot</span>
</div>
// ✅ Good - bn: prefix in content script
<div className="bn:flex bn:items-center bn:gap-2">
<span className="bn:text-sm bn:font-bold">Bot</span>
</div>
Popup and Isolated UIs
Unprefixed classes are used in popup and other isolated contexts where there’s no risk of CSS conflicts with the host page.
// ✅ Good - No prefix needed in popup
<div className="flex items-center gap-2">
<span className="text-sm font-bold">Settings</span>
</div>
Icons
Content Script Insertions
Import from lucide-static, NOT lucide-react.
Content script insertions should use static SVG icons to avoid React component overhead in the host page:
// ❌ Bad - React components in content script
import { Bot } from 'lucide-react';
<Bot className="bn:size-4" />
// ✅ Good - Static SVG in content script
import BotIcon from 'lucide-static/icons/bot.svg?raw';
<span
className="bn:size-4"
dangerouslySetInnerHTML={{ __html: BotIcon }}
/>
Popup and Isolated UIs
Use lucide-react in popup and other isolated contexts:
// ✅ Good - React components in popup
import { Settings } from 'lucide-react';
<Settings className="size-4" />
Class Utilities
Use the cn() and cnt() helpers from @/shared/tailwindcss-helpers for composing class names:
cn() - Class Names with Merge
Merges Tailwind classes intelligently, handling conflicts:
import { cn } from '@/shared/tailwindcss-helpers';
// Handles conflicting classes correctly
<div className={cn(
'bn:px-4 bn:py-2',
isActive && 'bn:px-6', // Overrides bn:px-4 when active
'bn:text-sm'
)} />
cnt() - Class Names Template
Utility for building conditional class strings:
import { cnt } from '@/shared/tailwindcss-helpers';
<div className={cnt(
isError && 'bn:text-red-500',
isWarning && 'bn:text-yellow-500',
isSuccess && 'bn:text-green-500'
)} />
Always use formatting helpers from @/shared/formatting for consistent internationalization:
import { formatInt, formatDate, formatDateTime } from '@/shared/formatting';
// ❌ Bad - Direct number/date formatting
<span>{count.toLocaleString()}</span>
<span>{date.toLocaleDateString()}</span>
// ✅ Good - Use formatting helpers
<span>{formatInt(count)}</span>
<span>{formatDate(date)}</span>
<span>{formatDateTime(date)}</span>
For complex messages with plurals and interpolation, use ICU message format:
import { IntlMessageFormat } from 'intl-messageformat';
const message = new IntlMessageFormat(
'{count, plural, one {# bot} other {# bots}}',
'ru'
);
const formatted = message.format({ count: 5 }); // "5 bots"
Logging
Use hierarchical logging categories via @/shared/logging:
import { getLogger } from '@/shared/logging';
const logger = getLogger('content-script:insertions');
logger.debug('Insertion started', { variant: 'bot-badge' });
logger.info('User action detected', { action: 'report' });
logger.warn('API rate limit approaching', { remaining: 10 });
logger.error('Failed to fetch data', { error });
Logger categories should be hierarchical and descriptive:
background:service:api
content-script:insertions:bot-badge
popup:settings
Error Handling
Prefer result objects over throwing errors for expected error cases:
// ❌ Bad - Throwing for expected errors
function fetchUserData(userId: string) {
if (!userId) {
throw new Error('User ID is required');
}
// ...
}
// ✅ Good - Return result object
function fetchUserData(userId: string):
| { success: true; data: User }
| { success: false; error: string }
{
if (!userId) {
return { success: false, error: 'User ID is required' };
}
// ...
return { success: true, data: user };
}
This pattern makes error handling explicit and type-safe:
const result = fetchUserData(userId);
if (!result.success) {
logger.error('Failed to fetch user', { error: result.error });
return;
}
// TypeScript knows result.data exists here
console.log(result.data.name);
Zod Schema Validation
Import from zod/mini
Always import from zod/mini for smaller bundle size:
// ❌ Bad
import { z } from 'zod';
// ✅ Good
import { z } from 'zod/mini';
Use z.exactOptional()
Use z.exactOptional() instead of z.optional() for strict schema validation:
// ❌ Bad - Allows undefined implicitly
const schema = z.object({
name: z.string(),
age: z.number().optional(),
});
// ✅ Good - Explicit about optional fields
const schema = z.object({
name: z.string(),
age: z.exactOptional(z.number()),
});
This ensures that optional fields are explicitly marked and validated correctly, preventing subtle bugs with missing vs undefined values.
File Structure
The project follows a recursive tree approach for organizing files. Related functionality is grouped together in directories:
src/
├── entrypoints/
│ ├── background/
│ │ └── @services/ # Background services
│ ├── content/
│ │ └── insertion-variants/ # DOM modification configs
│ └── popup/
├── shared/
│ ├── @model/ # Data models
│ ├── formatting/ # Formatting utilities
│ ├── logging/ # Logging utilities
│ └── tailwindcss-helpers/ # CSS utilities
For more details on the file structure conventions, see AGENTS.md.
Additional Resources