Maintaining code quality and standards in StickerNest. Use when refactoring code, checking file sizes, splitting large files, reviewing code structure, or when files exceed length limits. Covers file length limits, refactoring patterns, code organization, and the "check existing code first" principle.
This skill defines StickerNest's code quality standards, including file length limits, refactoring triggers, and the critical "check existing code first" principle.
BEFORE writing any new code, ALWAYS:
Search for existing implementations
# Search for similar functionality
grep -r "similar keyword" src/
Check for existing utilities
src/utils/ - Utility functionssrc/hooks/ - Custom hookssrc/components/ - Reusable componentsFollow existing patterns
Ask yourself:
Why? Duplicate code leads to inconsistencies, bugs, and maintenance burden. StickerNest has many utilities that solve common problems.
| File Type | Ideal | Warning | Refactor Required |
|---|---|---|---|
| Component (.tsx) | < 300 | 300-500 | > 500 |
| Store (.ts) | < 400 | 400-600 | > 600 |
| Utility (.ts) | < 200 | 200-300 | > 300 |
| Hook (.ts) | < 150 | 150-250 | > 250 |
| Types (.ts) | < 200 | 200-400 | > 400 |
| Test (.test.ts) | < 500 | 500-800 | > 800 |
Based on codebase analysis, these files exceed limits:
1924 lines - src/runtime/WidgetSandboxHost.ts ⚠️ CRITICAL
1808 lines - src/components/.../StyleGalleryPanel.tsx ⚠️ CRITICAL
1586 lines - src/runtime/WidgetAPI.ts ⚠️ CRITICAL
1548 lines - src/widgets/.../LiveChatWidget.ts ⚠️ CRITICAL
1471 lines - src/services/enhancedAIGenerator.ts ⚠️ CRITICAL
1403 lines - src/state/useCanvasStore.ts ⚠️ CRITICAL
// 🚩 Too many imports (> 15)
import { a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p } from '...';
// → Split into focused modules
// 🚩 God component (does too much)
function MyComponent() {
// 500+ lines of mixed concerns
}
// → Split into container + presentational components
// 🚩 Prop drilling (> 3 levels)
<A data={data}><B data={data}><C data={data}><D data={data} /></C></B></A>
// → Use context or Zustand store
// 🚩 Complex conditionals
if (a && (b || c) && (!d || (e && f))) { ... }
// → Extract to named boolean or function
// BEFORE: Monolithic component
function BigComponent() {
return (
<div>
{/* 50 lines of header */}
{/* 100 lines of content */}
{/* 50 lines of footer */}
</div>
);
}
// AFTER: Composed components
function BigComponent() {
return (
<div>
<Header />
<Content />
<Footer />
</div>
);
}
// Each in its own file if > 100 lines
// src/components/BigComponent/
// index.tsx
// Header.tsx
// Content.tsx
// Footer.tsx
// BEFORE: Logic in component
function MyComponent() {
const [data, setData] = useState(null);
const [loading, setLoading] = useState(false);
const [error, setError] = useState(null);
useEffect(() => {
setLoading(true);
fetchData()
.then(setData)
.catch(setError)
.finally(() => setLoading(false));
}, []);
// ... 200 more lines
}
// AFTER: Custom hook
function useDataFetch() {
const [data, setData] = useState(null);
const [loading, setLoading] = useState(false);
const [error, setError] = useState(null);
useEffect(() => {
setLoading(true);
fetchData()
.then(setData)
.catch(setError)
.finally(() => setLoading(false));
}, []);
return { data, loading, error };
}
function MyComponent() {
const { data, loading, error } = useDataFetch();
// ... cleaner component
}
// BEFORE: Massive store
// useCanvasStore.ts - 1400 lines
// AFTER: Sliced stores
// useCanvasStore.ts - Main canvas state
// useSelectionStore.ts - Selection logic
// useViewportStore.ts - Viewport/zoom/pan
// useHistoryStore.ts - Undo/redo
// useGridStore.ts - Grid/snap settings
// Or use Zustand slices pattern:
const createSelectionSlice = (set, get) => ({
selectedIds: new Set(),
select: (id) => set({ selectedIds: new Set([id]) }),
// ...
});
// BEFORE: Inline logic
function Component() {
const formatted = value
.replace(/[^a-z]/gi, '')
.toLowerCase()
.substring(0, 50);
}
// AFTER: Utility function
// src/utils/formatters.ts
export function sanitizeInput(value: string, maxLength = 50): string {
return value
.replace(/[^a-z]/gi, '')
.toLowerCase()
.substring(0, maxLength);
}
// Component
import { sanitizeInput } from '@/utils/formatters';
const formatted = sanitizeInput(value);
src/components/MyFeature/
├── index.tsx # Main export
├── MyFeature.tsx # Main component (< 300 lines)
├── MyFeatureHeader.tsx # Sub-component
├── MyFeatureList.tsx # Sub-component
├── useMyFeature.ts # Custom hooks
├── MyFeature.types.ts # Types (if > 50 lines)
├── MyFeature.utils.ts # Utilities
└── MyFeature.test.tsx # Tests
src/features/canvas/
├── index.ts # Public API
├── components/ # UI components
├── hooks/ # Feature hooks
├── store/ # Zustand store(s)
├── utils/ # Utilities
├── types.ts # Types
└── constants.ts # Constants
any unless necessary)// Components: PascalCase
MyComponent.tsx
WidgetCard.tsx
// Hooks: camelCase with 'use' prefix
useCanvasStore.ts
useWidgetDrag.ts
// Utilities: camelCase
formatDate.ts
validateInput.ts
// Types: PascalCase
types.ts → interface WidgetInstance { }
types.ts → type CanvasMode = 'edit' | 'view';
// Constants: SCREAMING_SNAKE_CASE
const MAX_WIDGETS = 100;
const API_ENDPOINT = '/api/v1';
// ❌ God objects
const everythingStore = { /* 1000+ properties */ };
// ❌ Prop drilling
<A><B><C><D><E prop={value} /></D></C></B></A>
// ❌ Inline styles everywhere
<div style={{ color: 'red', padding: 20, margin: 10, ... }}>
// ❌ Magic numbers
if (widgets.length > 47) { /* why 47? */ }
// ❌ Nested ternaries
const result = a ? b ? c : d : e ? f : g;
// ❌ any types
function process(data: any): any { }
// ❌ Mutation
state.items.push(newItem); // ❌
set({ items: [...get().items, newItem] }); // ✅
# Find large files
find src -name "*.ts" -o -name "*.tsx" | xargs wc -l | sort -n | tail -20
# Find files with too many imports
grep -l "^import" src/**/*.{ts,tsx} | xargs -I{} sh -c 'echo "$(grep "^import" {} | wc -l) {}"' | sort -n | tail -10
# Find potential duplicates (similar function names)
grep -rh "function\|const.*=.*=>" src/ | sort | uniq -c | sort -n | tail -20