Skip to main content

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>
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 }}
/>
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'
)} />

Formatting Helpers

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>

ICU Message Format

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

Build docs developers (and LLMs) love