### Install Dependencies and Setup Linters
Source: https://github.com/factory-ai/eslint-plugin/blob/main/examples/typescript-project/README.md
Run these commands to install project dependencies and set up the Factory linters.
```bash
npm install
npx factory-linters-setup
```
--------------------------------
### Install @factory/eslint-plugin and ESLint
Source: https://github.com/factory-ai/eslint-plugin/blob/main/README.md
Install the @factory/eslint-plugin and ESLint as development dependencies.
```bash
npm install @factory/eslint-plugin eslint --save-dev
```
--------------------------------
### Correct File Structure Example
Source: https://github.com/factory-ai/eslint-plugin/blob/main/rules/require-tsx-test-stories-files/README.md
Demonstrates a file structure that adheres to the rule, showing TSX components with their required test and Storybook files, as well as examples of exempt files.
```text
src/
components/
Button.tsx
Button.test.tsx
Button.stories.tsx
Card.tsx
Card.test.tsx
Card.stories.tsx
index.tsx # Exempt - barrel file
app/
page.tsx # Exempt - Next.js page
layout.tsx # Exempt - Next.js layout
```
--------------------------------
### Correct Test File Content
Source: https://github.com/factory-ai/eslint-plugin/blob/main/rules/test-file-location/README.md
Provides an example of a valid test file containing the necessary `describe` and `it` blocks.
```typescript
// format.test.ts
describe('format', () => {
it('should format dates correctly', () => {
expect(formatDate(new Date())).toBe('2024-01-01');
});
});
```
--------------------------------
### Correct Component Declarations
Source: https://github.com/factory-ai/eslint-plugin/blob/main/rules/restrict-tsx-components/README.md
These examples demonstrate correct component declarations within files that adhere to the specified naming conventions for different component types.
```tsx
// pages/Dashboard.page.tsx
export const DashboardPage = () => {
return
Dashboard
;
};
// components/Sidebar.module.tsx
export function SidebarModule() {
return ;
}
// layouts/App.layout.tsx
export const AppLayout = ({ children }) => {
return {children};
};
// providers/Theme.provider.tsx
export const ThemeProvider = ({ children }) => {
return {children};
};
```
--------------------------------
### Correct: Types File Organization
Source: https://github.com/factory-ai/eslint-plugin/blob/main/rules/types-file-organization/README.md
This example demonstrates the correct placement of types and interfaces within a `types.ts` file, adhering to import restrictions.
```typescript
// src/types.ts
import { Status } from './enums';
import type { BaseSchema } from './schema';
export interface User {
id: string;
name: string;
status: Status;
}
export type UserId = string;
export type Config = {
schema: BaseSchema;
enabled: boolean;
};
```
--------------------------------
### Configuring Ignored Files
Source: https://github.com/factory-ai/eslint-plugin/blob/main/rules/filename-match-export/README.md
This example shows how to configure the rule to ignore specific filenames, such as utility or constant files, which may contain multiple exports.
```javascript
{
"rules": {
"@factory/filename-match-export": ["error", {
"ignoredFiles": ["utils.ts", "constants.ts", "types.ts"]
}]
}
}
```
--------------------------------
### Incorrect File Structure Example
Source: https://github.com/factory-ai/eslint-plugin/blob/main/rules/require-tsx-test-stories-files/README.md
Illustrates a file structure where TSX components are missing their corresponding test and Storybook files, violating the rule.
```text
src/
components/
Button.tsx # Missing Button.test.tsx and Button.stories.tsx
Card.tsx # Missing Card.test.tsx and Card.stories.tsx
```
--------------------------------
### Correct Route Handler - Admin Middleware
Source: https://github.com/factory-ai/eslint-plugin/blob/main/rules/require-route-middleware/README.md
This example demonstrates a route handler using `handleAdminRouteMiddleware` as the first statement, ensuring administrative-specific middleware runs before the main logic.
```typescript
export async function POST(request: Request) {
return handleAdminRouteMiddleware(request, async (user) => {
const body = await request.json();
return NextResponse.json({ success: true });
});
}
```
--------------------------------
### Correct v0 Route Handler - Using handleV0RouteMiddleware
Source: https://github.com/factory-ai/eslint-plugin/blob/main/rules/require-v0-route-handle-middleware/README.md
This example demonstrates the correct implementation where the route handler in an API v0 route properly calls `handleV0RouteMiddleware`. This ensures consistent API behavior.
```typescript
export const { GET } = route({
listUsers: routeOperation({ method: 'GET' })
.outputs([])
.handler(async (req)
=>
handleV0RouteMiddleware(req, async (user) => {
const users = await getUsers();
return NextResponse.json(users);
})
),
});
```
--------------------------------
### Correct Filename/Export Matches
Source: https://github.com/factory-ai/eslint-plugin/blob/main/rules/filename-match-export/README.md
These examples demonstrate correct usage where filenames align with their exported React components or functions, including those with special suffixes.
```typescript
// Button.tsx
export const Button = () => ;
```
```typescript
// Sessions.page.tsx
export const SessionsPage = () =>
;
```
```typescript
// Theme.provider.tsx
export const ThemeProvider = ({ children }) => (
{children}
);
```
--------------------------------
### Incorrect Test File Content
Source: https://github.com/factory-ai/eslint-plugin/blob/main/rules/test-file-location/README.md
Shows an example of an invalid test file that lacks the required `describe`, `it`, or `test` blocks.
```typescript
// format.test.ts - empty test file
// No describe/it/test blocks
export {};
```
--------------------------------
### Incorrect Component Declarations
Source: https://github.com/factory-ai/eslint-plugin/blob/main/rules/restrict-tsx-components/README.md
These examples show component declarations in files that do not follow the required naming conventions (.page.tsx, .module.tsx, .layout.tsx, .view.tsx, .provider.tsx).
```tsx
// components/Dashboard.tsx - should be Dashboard.page.tsx or Dashboard.module.tsx
export const Dashboard = () => {
return
Dashboard
;
};
// components/Sidebar.tsx - should be Sidebar.module.tsx or Sidebar.layout.tsx
export function Sidebar() {
return ;
}
```
--------------------------------
### Correct: Test utilities in `test-utils` directory
Source: https://github.com/factory-ai/eslint-plugin/blob/main/rules/test-utils-organization/README.md
This example demonstrates the correct organization of test utilities within a `test-utils` directory, including shared mocks, fixtures, and helpers.
```typescript
// src/test-utils/mocks.ts
export function mockFetchData() {
return { data: 'test' };
}
export const createMockUser = (overrides = {}) => ({
id: '1',
name: 'Test User',
...overrides,
});
```
--------------------------------
### Correct Route Handler - Middleware as First Statement
Source: https://github.com/factory-ai/eslint-plugin/blob/main/rules/require-route-middleware/README.md
This example shows a correctly implemented route handler where `handleRouteMiddleware` is called as the first statement, ensuring middleware execution before route logic.
```typescript
// route.ts
export async function GET(request: Request) {
return handleRouteMiddleware(request, async () => {
const data = await fetchData();
return NextResponse.json(data);
});
}
```
--------------------------------
### Incorrect Filename/Export Mismatches
Source: https://github.com/factory-ai/eslint-plugin/blob/main/rules/filename-match-export/README.md
These examples show common violations where the component name does not match the filename or the expected suffix for special file types.
```typescript
// Button.tsx - component name doesn't match filename
export const PrimaryButton = () => ;
```
```typescript
// Sessions.page.tsx - should export SessionsPage, not Sessions
export const Sessions = () =>
Sessions
;
```
```typescript
// UserProfile.tsx - should be in UserProfile.page.tsx if it's a page
export const UserProfilePage = () =>
Profile
;
```
--------------------------------
### Incorrect jest.mock() calls
Source: https://github.com/factory-ai/eslint-plugin/blob/main/rules/jest-mock-require-actual/README.md
These examples show incorrect usage of jest.mock() where the second argument is missing or does not preserve actual exports.
```ts
jest.mock('@/utils/api');
```
```ts
jest.mock('@/utils/api', () => ({ fetchData: jest.fn() }));
```
```ts
jest.mock('@/utils/api', () => ({ ...jest.requireActual('@/utils/api') }));
```
--------------------------------
### Correct: Constants in constants.ts
Source: https://github.com/factory-ai/eslint-plugin/blob/main/rules/constants-file-organization/README.md
This example shows the correct way to define exported constants in a `constants.ts` file, including imports from other internal modules.
```typescript
// src/constants.ts
import { Environment } from './enums';
export const API_URL = 'https://api.example.com';
export const MAX_RETRIES = 3;
export const TIMEOUT_MS = 5000;
export const CONFIG = {
environment: Environment.Production,
debug: false,
};
```
--------------------------------
### Correct File Structure
Source: https://github.com/factory-ai/eslint-plugin/blob/main/rules/require-test-files/README.md
Shows a file structure that adheres to the rule, with each TypeScript file having a corresponding test file, and also includes examples of exempt files.
```plaintext
src/
utils/
format.ts
format.test.ts
validate.ts
validate.test.ts
types.ts # Exempt - type definitions
constants.ts # Exempt - constants
```
--------------------------------
### Correct jest.mock() calls with jest.requireActual()
Source: https://github.com/factory-ai/eslint-plugin/blob/main/rules/jest-mock-require-actual/README.md
These examples demonstrate correct usage of jest.mock() by including jest.requireActual() to preserve existing exports while mocking specific functions.
```ts
jest.mock('@/utils/api', () => ({ ...jest.requireActual('@/utils/api'), fetchData: jest.fn() }));
```
```ts
jest.mock('@/services/auth', () => ({ ...jest.requireActual('@/services/auth'), login: jest.fn().mockResolvedValue({ token: 'test' }), logout: jest.fn() }));
```
--------------------------------
### Incorrect: Mock functions in source files
Source: https://github.com/factory-ai/eslint-plugin/blob/main/rules/test-utils-organization/README.md
This example shows mock functions placed incorrectly within source files, which this rule aims to prevent.
```typescript
// src/utils/api.ts - mock functions don't belong in source files
export function mockFetchData() {
return { data: 'test' };
}
export const createMockUser = () => ({ id: '1', name: 'Test' });
```
--------------------------------
### Correct Zod Schema with .strict()
Source: https://github.com/factory-ai/eslint-plugin/blob/main/rules/require-v0-strict-schemas/README.md
This example demonstrates the correct way to define Zod schemas using .strict() to reject unknown properties. It also shows how to apply .strict() to extended schemas.
```typescript
// api/v0/users/schemas/create-user.ts
import { z } from 'zod';
export const CreateUserSchema = z
.object({
name: z.string(),
email: z.string().email(),
})
.strict();
// Extended schemas also need .strict()
export const UpdateUserSchema = CreateUserSchema.extend({
id: z.string(),
}).strict();
```
--------------------------------
### Incorrect v0 Route Handler - Missing handleV0RouteMiddleware
Source: https://github.com/factory-ai/eslint-plugin/blob/main/rules/require-v0-route-handle-middleware/README.md
This example shows an incorrect implementation where the route handler in an API v0 route does not call `handleV0RouteMiddleware`. This violates the rule and should be corrected.
```typescript
export const { GET } = route({
listUsers: routeOperation({ method: 'GET' })
.outputs([])
.handler(async (req) => {
const users = await getUsers();
return NextResponse.json(users);
}),
});
```
--------------------------------
### Incorrect Route Handler - Missing Middleware
Source: https://github.com/factory-ai/eslint-plugin/blob/main/rules/require-route-middleware/README.md
This example shows a route handler where the middleware call is completely omitted, leaving the route unprotected.
```typescript
// route.ts - missing middleware call
export async function GET(request: Request) {
const data = await fetchData();
return NextResponse.json(data);
}
```
--------------------------------
### Incorrect Enum Placement
Source: https://github.com/factory-ai/eslint-plugin/blob/main/rules/enum-file-organization/README.md
Enums should not be placed in utility files. This example shows an enum defined in `src/utils.ts`, which violates the rule.
```typescript
// src/utils.ts - enums should not be in utility files
export enum Status {
Active = 'ACTIVE',
Inactive = 'INACTIVE',
}
```
--------------------------------
### Incorrect Route Handler - Middleware Not First
Source: https://github.com/factory-ai/eslint-plugin/blob/main/rules/require-route-middleware/README.md
This example demonstrates a route handler where middleware is called, but not as the very first statement, potentially allowing logic to execute before security checks.
```typescript
// route.ts - middleware not first statement
export async function POST(request: Request) {
const body = await request.json();
return handleRouteMiddleware(request, async () => {
// ...
});
}
```
--------------------------------
### Correct: Error classes in errors.ts
Source: https://github.com/factory-ai/eslint-plugin/blob/main/rules/errors-file-organization/README.md
This example demonstrates the correct placement and structure for custom Error classes within an `errors.ts` file, including necessary imports.
```typescript
// src/errors.ts
import { ErrorCode } from './enums';
import type { ErrorDetails } from './types';
export class ValidationError extends Error {
constructor(message: string) {
super(message);
this.name = 'ValidationError';
}
}
export class ApiError extends Error {
code: ErrorCode;
constructor(message: string, code: ErrorCode) {
super(message);
this.name = 'ApiError';
this.code = code;
}
}
```
--------------------------------
### Configure Ignored Files for Filename Match Rule
Source: https://context7.com/factory-ai/eslint-plugin/llms.txt
Example of how to configure the 'ignoredFiles' option for the '@factory/filename-match-export' ESLint rule in an '.eslintrc.js' file. This allows specific files like utility or constant files to be excluded from the naming convention check.
```javascript
// .eslintrc.js — override ignored files
"@factory/filename-match-export": ["error", {
"ignoredFiles": ["utils.ts", "constants.ts", "types.ts", "helpers.ts"]
}]
```
--------------------------------
### Incorrect Import in enums.ts
Source: https://github.com/factory-ai/eslint-plugin/blob/main/rules/enum-file-organization/README.md
Files named `enums.ts` are not allowed to contain import statements. This example demonstrates an enum in `src/enums.ts` with an import, which is disallowed.
```typescript
// src/enums.ts - enums.ts files cannot have imports
import { something } from './other';
export enum Direction {
Up = 'UP',
Down = 'DOWN',
}
```
--------------------------------
### Correct Enum Declaration in enums.ts
Source: https://github.com/factory-ai/eslint-plugin/blob/main/rules/enum-file-organization/README.md
This example shows the correct way to define multiple enums and type exports within an `enums.ts` file. It adheres to the rule by having no imports and only enum/type declarations.
```typescript
// src/enums.ts
export enum Status {
Active = 'ACTIVE',
Inactive = 'INACTIVE',
}
export enum Direction {
Up = 'UP',
Down = 'DOWN',
}
// Type exports are allowed
export type StatusType = keyof typeof Status;
```
--------------------------------
### Incorrect Zod Schema without .strict()
Source: https://github.com/factory-ai/eslint-plugin/blob/main/rules/require-v0-strict-schemas/README.md
This example shows an incorrect Zod schema definition where unknown properties in request payloads would be silently accepted. Ensure .strict() is used for API safety.
```typescript
// api/v0/users/schemas/create-user.ts
import { z } from 'zod';
export const CreateUserSchema = z.object({
name: z.string(),
email: z.string().email(),
});
// Unknown properties like { name: "John", emial: "typo@test.com" }
// would be silently accepted
```
--------------------------------
### Disallow useEffect in Custom Hooks
Source: https://github.com/factory-ai/eslint-plugin/blob/main/rules/no-use-effect-in-hooks/README.md
Custom hooks should not contain side effects like those managed by useEffect. This example shows an incorrect usage within a custom hook.
```tsx
function useUserData(userId: string) {
const [user, setUser] = useState(null);
useEffect(() => {
fetchUser(userId).then(setUser);
}, [userId]);
return user;
}
```
```tsx
const useWindowSize = () => {
const [size, setSize] = useState({ width: 0, height: 0 });
useLayoutEffect(() => {
const handleResize = () => {
setSize({ width: window.innerWidth, height: window.innerHeight });
};
window.addEventListener('resize', handleResize);
return () => window.removeEventListener('resize', handleResize);
}, []);
return size;
};
```
--------------------------------
### Require Middleware in Next.js Route Handlers
Source: https://context7.com/factory-ai/eslint-plugin/llms.txt
Ensures that exported HTTP method handlers (GET, POST, etc.) in route.ts files invoke a recognized handle*Middleware function as their first statement. Excludes v0 routes.
```typescript
// ❌ — missing middleware
export async function GET(request: Request) {
const data = await fetchData();
return NextResponse.json(data);
}
// ✅
export async function GET(request: Request) {
return handleRouteMiddleware(request, async () => {
const data = await fetchData();
return NextResponse.json(data);
});
}
export async function POST(request: Request) {
return handleAdminRouteMiddleware(request, async (user) => {
const body = await request.json();
await createRecord({ ...body, createdBy: user.id });
return NextResponse.json({ success: true });
});
}
```
--------------------------------
### Rule Configuration with Allowlist
Source: https://github.com/factory-ai/eslint-plugin/blob/main/rules/restrict-tsx-components/README.md
This configuration shows how to set up the rule and specify an allowlist of component names that are permitted in any file, overriding the strict naming convention for specific components.
```js
{
"rules": {
"@factory/restrict-tsx-components": ["error", {
"allowlist": ["Button", "Icon", "Avatar"]
}]
}
}
```
--------------------------------
### Run ESLint
Source: https://github.com/factory-ai/eslint-plugin/blob/main/examples/typescript-project/README.md
Execute this command to run ESLint for code linting.
```bash
npm run lint
```
--------------------------------
### Correct Test File Structure
Source: https://github.com/factory-ai/eslint-plugin/blob/main/rules/test-file-location/README.md
Demonstrates the correct structure where test files are colocated with their source files.
```text
src/
utils/
format.ts
format.test.ts # Colocated with source file
components/
Button.tsx
Button.test.tsx # Colocated with source file
```
--------------------------------
### Configure @factory/frontend ESLint for React/Vite
Source: https://context7.com/factory-ai/eslint-plugin/llms.txt
Extend the recommended configuration with React-specific rules for JSX, React Compiler integration, and TanStack Router.
```javascript
module.exports = {
root: true,
plugins: ['@factory'],
extends: ['plugin:@factory/frontend'],
rules: {
// Allow component allowlist for shared UI primitives
'@factory/restrict-tsx-components': ['error', {
allowlist: ['Button', 'Icon', 'Avatar', 'Spinner'],
}],
'@factory/filename-match-export': ['error', {
ignoredFiles: ['utils.ts', 'constants.ts', 'types.ts'],
}],
},
};
```
--------------------------------
### Correct: Log and handle locally
Source: https://github.com/factory-ai/eslint-plugin/blob/main/rules/no-log-exception-with-throw/README.md
When an error occurs, log it and then handle it locally by returning a result or performing a fallback action, instead of throwing.
```ts
function processData(data) {
try {
validate(data);
} catch (error) {
logException(error, 'Validation failed');
return { success: false, error: 'Invalid data' };
}
}
```
--------------------------------
### Incorrect Test File Structure
Source: https://github.com/factory-ai/eslint-plugin/blob/main/rules/test-file-location/README.md
Illustrates incorrect placement of test files in separate `__tests__` or `test/` directories instead of colocating them with source files.
```text
src/
utils/
format.ts
__tests__/ # Tests should not be in __tests__
format.test.ts
test/ # Tests should not be in test/
utils/
format.test.ts
```
--------------------------------
### Configure @factory/base ESLint
Source: https://context7.com/factory-ai/eslint-plugin/llms.txt
Extend the base configuration for core TypeScript/JavaScript rules, including Airbnb base and Jest recommended.
```javascript
module.exports = {
root: true,
plugins: ['@factory'],
extends: ['plugin:@factory/base'],
};
```
--------------------------------
### ESLint Configuration for Frontend Applications
Source: https://github.com/factory-ai/eslint-plugin/blob/main/README.md
Configure ESLint for React/Vite frontend applications using the plugin's frontend configuration.
```javascript
module.exports = {
root: true,
plugins: ['@factory'],
extends: ['plugin:@factory/frontend'],
};
```
--------------------------------
### Correct Usage: Effects in Components, Not Custom Hooks
Source: https://github.com/factory-ai/eslint-plugin/blob/main/rules/no-use-effect-in-hooks/README.md
This demonstrates correct patterns where effects are managed in components, or custom hooks manage derived state without side effects.
```tsx
function useUserQuery(userId: string) {
return useQuery(['user', userId], () => fetchUser(userId));
}
```
```tsx
function UserProfile({ userId }) {
const [user, setUser] = useState(null);
useEffect(() => {
fetchUser(userId).then(setUser);
}, [userId]);
return
{user?.name}
;
}
```
```tsx
function useFormattedDate(date: Date) {
return useMemo(() => formatDate(date), [date]);
}
```
--------------------------------
### ESLint Configuration for Backend Applications
Source: https://github.com/factory-ai/eslint-plugin/blob/main/README.md
Configure ESLint for backend applications using the plugin's backend configuration.
```javascript
module.exports = {
root: true,
plugins: ['@factory'],
extends: ['plugin:@factory/backend'],
};
```
--------------------------------
### ESLint Configuration for TypeScript Packages
Source: https://github.com/factory-ai/eslint-plugin/blob/main/README.md
Configure ESLint for TypeScript packages and libraries using the plugin's recommended configuration.
```javascript
module.exports = {
root: true,
plugins: ['@factory'],
extends: ['plugin:@factory/recommended'],
};
```
--------------------------------
### Incorrect: Logging then throwing
Source: https://github.com/factory-ai/eslint-plugin/blob/main/rules/no-log-exception-with-throw/README.md
Avoid logging an exception and then throwing a new error in the same block, as this causes duplicate error reporting.
```ts
function processData(data) {
try {
validate(data);
} catch (error) {
logException(error, 'Validation failed');
throw new Error('Invalid data');
}
}
```
```ts
if (condition) {
logError('Something went wrong');
throw new Error('Failed');
}
```
--------------------------------
### Correct jest.mock() usage with absolute paths
Source: https://github.com/factory-ai/eslint-plugin/blob/main/rules/jest-mock-absolute-paths/README.md
Shows the correct usage of `jest.mock()` with absolute paths, including path aliases, external modules, and scoped packages. This improves test clarity and prevents resolution issues.
```ts
// Absolute paths with path aliases
jest.mock('@/utils/api');
jest.mock('@factory/common/logger');
// External modules (no path prefix)
jest.mock('axios');
jest.mock('lodash');
// Scoped packages
jest.mock('@testing-library/react');
```
--------------------------------
### Incorrect File Structure
Source: https://github.com/factory-ai/eslint-plugin/blob/main/rules/require-test-files/README.md
Illustrates a file structure where TypeScript files are missing their corresponding test files, violating the rule.
```plaintext
src/
utils/
format.ts # Missing format.test.ts
validate.ts # Missing validate.test.ts
```
--------------------------------
### Use jest.mock() for Stable Mocking
Source: https://github.com/factory-ai/eslint-plugin/blob/main/rules/no-unstable-mock-module/README.md
Prefer `jest.mock()` with `jest.requireActual()` to ensure stable and predictable test behavior. This approach integrates actual module functionality while allowing specific overrides.
```ts
jest.mock('fs', () => ({
...jest.requireActual('fs'),
readFileSync: jest.fn(),
}));
jest.mock('@/utils/api', () => ({
...jest.requireActual('@/utils/api'),
fetchData: jest.fn(),
}));
```
--------------------------------
### Correct: Logging and throwing in different blocks
Source: https://github.com/factory-ai/eslint-plugin/blob/main/rules/no-log-exception-with-throw/README.md
Logging an error and throwing a different error are permissible if they occur in separate, distinct code blocks.
```ts
function handleRequest() {
if (isRecoverable) {
logError('Recoverable error occurred');
return fallback();
}
if (isFatal) {
throw new Error('Fatal error');
}
}
```
--------------------------------
### Correct Usage of Typography Components
Source: https://github.com/factory-ai/eslint-plugin/blob/main/rules/no-plain-html-text-elements/README.md
Shows the correct way to handle text content using design system typography components. This adheres to the rule by replacing plain HTML elements with appropriate components.
```tsx
Hello World
```
```tsx
```
```tsx
This is a paragraph
```
```tsx
Page Title
```
```tsx
```
```tsx
```
--------------------------------
### Enforce handleV0RouteMiddleware for v0 Routes
Source: https://context7.com/factory-ai/eslint-plugin/llms.txt
API version 0 routes must use `handleV0RouteMiddleware` within their `.handler()` callback for consistent API versioning and authentication. Ensure this middleware is correctly applied to all v0 route handlers.
```typescript
// ❌ api/v0/users/route.ts
export const { GET } = route({
listUsers: routeOperation({ method: 'GET' })
.outputs([])
.handler(async (req) => {
return NextResponse.json(await getUsers());
}),
});
// ✅ api/v0/users/route.ts
export const { GET } = route({
listUsers: routeOperation({ method: 'GET' })
.outputs([])
.handler(async (req)
=>
handleV0RouteMiddleware(req, async (user) => {
const users = await getUsers();
return NextResponse.json(users);
})
),
});
```
--------------------------------
### Require Test and Stories Files for TSX Components
Source: https://context7.com/factory-ai/eslint-plugin/llms.txt
Mandates that each .tsx component file must have both a .test.tsx and a .stories.tsx sibling file. Excludes Next.js App Router files and definition files.
--------------------------------
### Correct: Throw and let upstream handle logging
Source: https://github.com/factory-ai/eslint-plugin/blob/main/rules/no-log-exception-with-throw/README.md
If the error needs to be handled by upstream logic, simply throw the error without logging it locally.
```ts
function processData(data) {
try {
validate(data);
} catch (error) {
throw new Error('Invalid data');
}
}
```
--------------------------------
### Require typed file suffixes for React components
Source: https://context7.com/factory-ai/eslint-plugin/llms.txt
React component declarations must reside in files ending with `.page.tsx`, `.module.tsx`, `.layout.tsx`, `.view.tsx`, or `.provider.tsx`. An `allowlist` option permits exceptions for atomic UI primitives.
```tsx
// ❌ components/Dashboard.tsx
export const Dashboard = () =>
;
// ✅ layouts/App.layout.tsx
export const AppLayout = ({ children }) => {children};
// ✅ providers/Theme.provider.tsx
export const ThemeProvider = ({ children }) => (
{children}
);
```
```js
// Allow atomic UI primitives in any file
"@factory/restrict-tsx-components": ["error", {
"allowlist": ["Button", "Icon", "Avatar", "Spinner", "Badge"]
}]
```
--------------------------------
### Incorrect jest.mock() usage with relative paths
Source: https://github.com/factory-ai/eslint-plugin/blob/main/rules/jest-mock-absolute-paths/README.md
Demonstrates disallowed usage of relative paths and `require.resolve` in `jest.mock()` calls. These patterns can lead to brittle tests.
```ts
jest.mock('./utils');
jest.mock('../services/api');
jest.mock('../../lib/helpers');
// require.resolve is also not allowed
jest.mock(require.resolve('some-module'));
```
--------------------------------
### Allow Enums and Non-Exported/Mixed String Unions
Source: https://github.com/factory-ai/eslint-plugin/blob/main/rules/no-exported-string-union-types/README.md
Demonstrates the correct usage of TypeScript enums and allowed string union types. Non-exported unions, unions with non-string types, and unions with other types are permitted.
```typescript
export enum Status {
Active = 'active',
Inactive = 'inactive',
Pending = 'pending',
}
```
```typescript
export enum Direction {
Up = 'up',
Down = 'down',
Left = 'left',
Right = 'right',
}
```
```typescript
// Non-exported unions are allowed
type InternalStatus = 'loading' | 'ready';
```
```typescript
// Mixed unions are allowed (not all strings)
export type Result = 'success' | 'error' | number;
```
```typescript
// Unions with other types are allowed
export type Nullable = T | null;
```
--------------------------------
### Allowed Static Messages with Structured Metadata
Source: https://github.com/factory-ai/eslint-plugin/blob/main/rules/structured-logging/README.md
Use static message strings for logging functions and pass dynamic values as the second argument in an object. This is the preferred pattern for structured logging.
```typescript
logError('Failed to fetch data', { userId });
```
```typescript
throw new Error('Processing failed');
```
```typescript
logException(error, 'Request timed out', { requestId });
```
--------------------------------
### Incorrect: Types in Utility Files
Source: https://github.com/factory-ai/eslint-plugin/blob/main/rules/types-file-organization/README.md
Exported types and interfaces should not be defined in utility files. Place them in `types.ts` files instead.
```typescript
// src/utils.ts - types should not be in utility files
export interface User {
id: string;
name: string;
}
export type UserId = string;
```
--------------------------------
### Require Test Files for TS Source Files
Source: https://context7.com/factory-ai/eslint-plugin/llms.txt
Ensures every .ts file has a corresponding .test.ts file. Exempts specific files like enums, types, constants, and index files.
```typescript
// src/utils/format.test.ts
describe('formatDate', () => {
it('formats ISO dates correctly', () => {
expect(formatDate(new Date('2024-01-15'))).toBe('Jan 15, 2024');
});
});
```
--------------------------------
### Use design-system typography components instead of plain HTML text elements
Source: https://context7.com/factory-ai/eslint-plugin/llms.txt
Plain text nodes should not appear directly inside common HTML elements. Use typography components from the design system for consistent styling and structure.
```tsx
// ❌
Hello World
Page Title
Welcome back,
// ✅
Hello WorldPage Title
```
--------------------------------
### Enforce Structured Log Messages
Source: https://context7.com/factory-ai/eslint-plugin/llms.txt
Logging calls and Error constructors must use static string messages. Dynamic values should be passed as a separate structured metadata argument, avoiding template literals with expressions or string concatenation. This ensures logs are consistent and easily parsable.
```typescript
// ❌
logError(`Failed to fetch data for user ${userId}`);
logException(error, `Request ${requestId} timed out`);
throw new Error(`Processing failed: ${error.message}`);
logError('User ' + userId + ' not found');
```
```typescript
// ✅
logError('Failed to fetch data', { userId });
logException(error, 'Request timed out', { requestId });
throw new Error('Processing failed');
logInfo('User created', { userId, email });
```
--------------------------------
### Incorrect: Constants in Utility Files
Source: https://github.com/factory-ai/eslint-plugin/blob/main/rules/constants-file-organization/README.md
Exported constants should not be defined in utility files like `utils.ts`. They must be centralized in `constants.ts` files.
```typescript
// src/utils.ts - constants should not be in utility files
export const API_URL = 'https://api.example.com';
export const MAX_RETRIES = 3;
```
--------------------------------
### Disallow jest.unstable_mockModule()
Source: https://github.com/factory-ai/eslint-plugin/blob/main/rules/no-unstable-mock-module/README.md
Avoid using `jest.unstable_mockModule()` as it is an experimental API. Use `jest.mock()` instead for stable and reliable testing.
```ts
jest.unstable_mockModule('fs', () => ({
default: {},
}));
jest.unstable_mockModule('@/utils/api', () => ({
fetchData: jest.fn(),
}));
```
--------------------------------
### Filename Must Match Primary Export
Source: https://context7.com/factory-ai/eslint-plugin/llms.txt
Ensures a file's name corresponds to its main exported component or function. Supports special suffixes like '.page.tsx' for '*Page', '.layout.tsx' for '*Layout', and '.provider.tsx' for '*Provider'. The 'ignoredFiles' option can be used to specify files that should be exempt from this rule.
```typescript
// ❌ Button.tsx
export const PrimaryButton = () => ;
```
```typescript
// ❌ Sessions.page.tsx
export const Sessions = () =>
;
```
```typescript
// ✅ Theme.provider.tsx
export const ThemeProvider = ({ children }) => (
{children}
);
```
--------------------------------
### Disallowed String Concatenation for Logging
Source: https://github.com/factory-ai/eslint-plugin/blob/main/rules/structured-logging/README.md
String concatenation should not be used for logging functions. Use static messages with structured metadata instead.
```typescript
logError('User ' + userId + ' not found');
```
--------------------------------
### Correct: Styled Components at Module Scope
Source: https://github.com/factory-ai/eslint-plugin/blob/main/rules/no-dynamic-styled-components/README.md
Define styled-components at the module scope for optimal performance and testability. This ensures components are created once and reused across renders.
```tsx
const Container = styled.div<{ $highlighted: boolean }>`
background: ${(props) => (props.$highlighted ? 'yellow' : 'white')};
`;
const StyledButton = styled.button`
padding: 8px;
`;
function Card({ highlighted }) {
return Content;
}
const Button = () => {
return Click;
};
```
--------------------------------
### Enforce .strict() for v0 Zod Schemas
Source: https://context7.com/factory-ai/eslint-plugin/llms.txt
All `z.object()` calls in `/api/v0/` schema files and route files must chain `.strict()`. This rejects unknown request properties, preventing silent data passthrough.
```typescript
// ❌ api/v0/users/schemas/create-user.ts
export const CreateUserSchema = z.object({
name: z.string(),
email: z.string().email(),
}); // unknown fields silently accepted
// ✅
export const CreateUserSchema = z
.object({
name: z.string(),
email: z.string().email(),
})
.strict();
export const UpdateUserSchema = CreateUserSchema.extend({
id: z.string(),
}).strict();
```
--------------------------------
### Incorrect Usage of Plain HTML Elements
Source: https://github.com/factory-ai/eslint-plugin/blob/main/rules/no-plain-html-text-elements/README.md
Demonstrates the incorrect use of plain HTML elements to wrap text content. This violates the rule by not using design system typography components.
```tsx
Hello World
```
```tsx
Click here
```
```tsx
This is a paragraph
```
```tsx
Page Title
```
```tsx
Welcome back,
```
--------------------------------
### Use Absolute Paths in jest.mock()
Source: https://context7.com/factory-ai/eslint-plugin/llms.txt
Requires jest.mock(), jest.doMock(), and jest.unmock() calls to use absolute paths or package names, disallowing relative paths. This rule is particularly relevant for the 'backend' configuration.
```typescript
// ❌
jest.mock('./utils');
jest.mock('../services/api');
jest.mock(require.resolve('some-module'));
// ✅
jest.mock('@/utils/api');
jest.mock('@factory/common/logger');
jest.mock('axios'); // external package — no path prefix needed
jest.mock('@testing-library/react');
```
--------------------------------
### Incorrect: Styled Component Inside Function Component
Source: https://github.com/factory-ai/eslint-plugin/blob/main/rules/no-dynamic-styled-components/README.md
Avoid defining styled-components within function components. This leads to components being recreated on each render, negating memoization benefits and causing unnecessary DOM updates.
```tsx
function Card({ highlighted }) {
const Container = styled.div`
background: ${highlighted ? 'yellow' : 'white'};
`;
return Content;
}
```
--------------------------------
### Spread jest.requireActual() in jest.mock() Factory Functions
Source: https://context7.com/factory-ai/eslint-plugin/llms.txt
Ensures that every jest.mock() factory function includes jest.requireActual() to preserve real implementations of non-overridden exports. This prevents accidental auto-mocking of all exports.
```typescript
// ❌ — all exports auto-mocked
jest.mock('@/utils/api');
// ❌ — real exports discarded
jest.mock('@/utils/api', () => ({
fetchData: jest.fn(),
}));
// ✅
jest.mock('@/utils/api', () => ({
...jest.requireActual('@/utils/api'),
fetchData: jest.fn(),
}));
jest.mock('@/services/auth', () => ({
...jest.requireActual('@/services/auth'),
login: jest.fn().mockResolvedValue({ token: 'test-token' }),
logout: jest.fn(),
}));
```
--------------------------------
### Incorrect: Error class in utility file
Source: https://github.com/factory-ai/eslint-plugin/blob/main/rules/errors-file-organization/README.md
Exported classes extending `Error` must not be placed in utility files like `utils.ts`. They should be centralized in `errors.ts`.
```typescript
// src/utils.ts - error classes should not be in utility files
export class ValidationError extends Error {
constructor(message: string) {
super(message);
this.name = 'ValidationError';
}
}
```
--------------------------------
### Disallowed Template Literals with Dynamic Values
Source: https://github.com/factory-ai/eslint-plugin/blob/main/rules/structured-logging/README.md
Avoid using template literals with embedded expressions for logging functions. Dynamic values should be passed as structured metadata.
```typescript
logError(`Failed to fetch data for user ${userId}`);
```
```typescript
throw new Error(`Processing failed: ${error.message}`);
```
```typescript
logException(error, `Request ${requestId} timed out`);
```
--------------------------------
### Incorrect: Functions in Constants Files
Source: https://github.com/factory-ai/eslint-plugin/blob/main/rules/constants-file-organization/README.md
Files named `constants.ts` should only contain `const` declarations. Functions are not permitted in these files.
```typescript
// src/constants.ts - cannot contain functions
export const API_URL = 'https://api.example.com';
export function getUrl() {
return API_URL;
}
```
--------------------------------
### Disallow dynamic styled-components inside functions
Source: https://context7.com/factory-ai/eslint-plugin/llms.txt
All `styled.*` definitions must be at module scope. Defining them inside component bodies or functions causes React to treat them as new component types on every render, leading to performance issues.
```tsx
// ❌
function Card({ highlighted }) {
const Container = styled.div`background: ${highlighted ? 'yellow' : 'white'};`;
return Content;
}
// ✅
const Container = styled.div<{ $highlighted: boolean }>`
background: ${(props) => (props.$highlighted ? 'yellow' : 'white')};
`;
function Card({ highlighted }: { highlighted: boolean }) {
return Content;
}
```
--------------------------------
### Correct: Constants initialized by function calls
Source: https://github.com/factory-ai/eslint-plugin/blob/main/rules/constants-file-organization/README.md
Constants initialized by function calls, `new` expressions, or tagged templates are exempt from the rule when defined in non-constants files.
```typescript
// src/logger.ts - function calls are allowed elsewhere
export const logger = createLogger({ level: 'info' });
```
--------------------------------
### Allowed Static Template Literals
Source: https://github.com/factory-ai/eslint-plugin/blob/main/rules/structured-logging/README.md
Static template literals without embedded expressions are permitted for logging functions.
```typescript
logError(`Failed to fetch data`, { userId });
```
--------------------------------
### Allowed Dynamic Strings in Non-Logging Functions
Source: https://github.com/factory-ai/eslint-plugin/blob/main/rules/structured-logging/README.md
Dynamic strings are allowed in non-logging functions like `console.log` or general variable assignments.
```typescript
console.log(`Debug: ${value}`);
```
```typescript
const message = `Hello ${name}`;
```
--------------------------------
### Incorrect: Arbitrary Imports in Types Files
Source: https://github.com/factory-ai/eslint-plugin/blob/main/rules/types-file-organization/README.md
Files named `types.ts` cannot import from arbitrary internal files. Imports are limited to external modules or specific internal files like `enums.ts`, `types.ts`, and `schema.ts`.
```typescript
// src/types.ts - cannot import from arbitrary files
import { helper } from './utils';
export interface Config {
value: ReturnType;
}
```
--------------------------------
### Disallow Logging Before Re-throwing Errors
Source: https://context7.com/factory-ai/eslint-plugin/llms.txt
This rule prevents calling 'logException()' or 'logError()' in the same code block where an error is re-thrown. This avoids creating duplicate error entries in log aggregators when an error is both logged and then thrown.
```typescript
// ❌ — logged AND thrown → duplicate error events
function processData(data) {
try {
validate(data);
} catch (error) {
logException(error, 'Validation failed');
throw new Error('Invalid data');
}
}
```
```typescript
// ✅ Option 1 — log and handle locally
function processData(data) {
try {
validate(data);
} catch (error) {
logException(error, 'Validation failed');
return { success: false, error: 'Invalid data' };
}
}
```
```typescript
// ✅ Option 2 — throw and let the boundary log
function processData(data) {
try {
validate(data);
} catch (error) {
throw new Error('Invalid data');
}
}
```
--------------------------------
### Correct Exported Function Declarations
Source: https://github.com/factory-ai/eslint-plugin/blob/main/rules/no-exported-function-expressions/README.md
Use function declarations with the `function` keyword for exported functions. This includes named default exports.
```typescript
export async function fetchData() {
return await api.get('/data');
}
```
```typescript
export function processItem(item) {
return item.value * 2;
}
```
```typescript
export default function HelloComponent() {
return
Hello
;
}
```
--------------------------------
### Incorrect: Styled Component Inside Arrow Function
Source: https://github.com/factory-ai/eslint-plugin/blob/main/rules/no-dynamic-styled-components/README.md
Defining styled-components within arrow functions also causes performance issues. Ensure all styled-component definitions are at the module scope.
```tsx
const Button = () => {
const StyledButton = styled.button`
padding: 8px;
`;
return Click;
};
```
--------------------------------
### Prefer Function Declarations for Exports
Source: https://context7.com/factory-ai/eslint-plugin/llms.txt
This rule prohibits exporting arrow functions or function expressions. Exported functions must use the 'function' keyword to ensure they are hoisted and generate more informative stack traces.
```typescript
// ❌
export const fetchData = async () => await api.get('/data');
export const processItem = function (item) { return item.value * 2; };
export default () =>
Hello
;
```
```typescript
// ✅
export async function fetchData() {
return await api.get('/data');
}
export function processItem(item) {
return item.value * 2;
}
export default function HelloComponent() {
return
Hello
;
}
```
--------------------------------
### Enum File Organization Rule
Source: https://context7.com/factory-ai/eslint-plugin/llms.txt
Enforces that exported TypeScript enums must be declared in `enums.ts` files, which should only contain enum declarations and type exports.
```typescript
// ❌ src/utils.ts
export enum Status { Active = 'ACTIVE', Inactive = 'INACTIVE' }
// ✅ src/enums.ts
export enum Status {
Active = 'ACTIVE',
Inactive = 'INACTIVE',
}
export enum Direction { Up = 'UP', Down = 'DOWN' }
export type StatusType = keyof typeof Status; // type exports are allowed
```
--------------------------------
### Constants File Organization Rule
Source: https://context7.com/factory-ai/eslint-plugin/llms.txt
Requires exported `const` declarations to be in `constants.ts` files. These files are restricted to `const` declarations, with limited import capabilities. Constants initialized by function calls or `new` expressions are exempt.
```typescript
// ❌ src/utils.ts
export const API_URL = 'https://api.example.com';
// ✅ src/constants.ts
import { Environment } from './enums';
export const API_URL = 'https://api.example.com';
export const MAX_RETRIES = 3;
export const TIMEOUT_MS = 5000;
export const CONFIG = {
environment: Environment.Production,
debug: false,
};
// ✅ src/logger.ts — function-call initializers are exempt
export const logger = createLogger({ level: 'info' });
```
--------------------------------
### Incorrect: Non-error code in errors.ts
Source: https://github.com/factory-ai/eslint-plugin/blob/main/rules/errors-file-organization/README.md
Files named `errors.ts` should exclusively contain Error class declarations. Helper functions or other non-error code are not permitted.
```typescript
// src/errors.ts - cannot contain non-error code
export class ApiError extends Error {
constructor(message: string) {
super(message);
}
}
// Not allowed - helper functions don't belong here
export function formatError(error: Error): string {
return error.message;
}
```
--------------------------------
### Disallow effect hooks inside custom hooks
Source: https://context7.com/factory-ai/eslint-plugin/llms.txt
Custom hooks (functions prefixed with `use`) must not call any hook ending in `"effect"` (`useEffect`, `useLayoutEffect`, etc.). Side effects should be controlled by components, not buried in hooks.
```typescript
// ❌ useUserData.ts
function useUserData(userId: string) {
const [user, setUser] = useState(null);
useEffect(() => {
fetchUser(userId).then(setUser);
}, [userId]);
return user;
}
// ✅ — hook returns a query, component owns the lifecycle
function useUserQuery(userId: string) {
return useQuery(['user', userId], () => fetchUser(userId));
}
function UserProfile({ userId }: { userId: string }) {
const [user, setUser] = useState(null);
useEffect(() => { // ✅ effect in a component is fine
fetchUser(userId).then(setUser);
}, [userId]);
return
{user?.name}
;
}
```
--------------------------------
### Prefer Enums Over Exported String Union Types
Source: https://context7.com/factory-ai/eslint-plugin/llms.txt
Avoid exporting type aliases that are simple unions of string literals. Instead, use enums for better type safety and readability. Non-exported unions and mixed-type unions are permitted.
```typescript
// ❌
export type Status = 'active' | 'inactive' | 'pending';
export type Theme = 'light' | 'dark';
```
```typescript
// ✅ — use enums (in enums.ts)
export enum Status {
Active = 'active',
Inactive = 'inactive',
Pending = 'pending',
}
```
```typescript
// ✅ — non-exported union is allowed
type InternalState = 'loading' | 'ready';
```
```typescript
// ✅ — mixed union is allowed
export type Result = 'success' | 'error' | number;
```
--------------------------------
### Enforce Custom Error Classes in errors.ts
Source: https://context7.com/factory-ai/eslint-plugin/llms.txt
Custom Error classes extending 'Error' must be declared in 'errors.ts'. This file should only contain Error class declarations, disallowing helper functions. Imports are restricted to external modules or specific sibling files.
```typescript
// ❌ src/utils.ts
export class ValidationError extends Error { /* ... */ }
```
```typescript
// ✅ src/errors.ts
import { ErrorCode } from './enums';
import type { ErrorDetails } from './types';
export class ValidationError extends Error {
constructor(message: string) {
super(message);
this.name = 'ValidationError';
}
}
export class ApiError extends Error {
code: ErrorCode;
constructor(message: string, code: ErrorCode) {
super(message);
this.name = 'ApiError';
this.code = code;
}
}
```
--------------------------------
### Incorrect Exported Function Expressions
Source: https://github.com/factory-ai/eslint-plugin/blob/main/rules/no-exported-function-expressions/README.md
This rule disallows exporting arrow functions or function expressions. Use function declarations with the `function` keyword instead.
```typescript
export const fetchData = async () => {
return await api.get('/data');
};
```
```typescript
export const processItem = function (item) {
return item.value * 2;
};
```
```typescript
export default () =>
Hello
;
```
--------------------------------
### Disallow Exported String Union Types
Source: https://github.com/factory-ai/eslint-plugin/blob/main/rules/no-exported-string-union-types/README.md
Exported string union types are disallowed by this rule. Use TypeScript enums as an alternative.
```typescript
export type Status = 'active' | 'inactive' | 'pending';
```
```typescript
export type Direction = 'up' | 'down' | 'left' | 'right';
```
```typescript
export type Theme = 'light' | 'dark';
```
=== COMPLETE CONTENT === This response contains all available snippets from this library. No additional content exists. Do not make further requests.