|
| 1 | +# Custom ESLint Rules |
| 2 | + |
| 3 | +This directory contains custom ESLint rules specific to React on Rails. |
| 4 | + |
| 5 | +## Rules |
| 6 | + |
| 7 | +### `no-use-client-in-server-files` |
| 8 | + |
| 9 | +Prevents the `'use client'` directive from being used in `.server.tsx` and `.server.ts` files. |
| 10 | + |
| 11 | +#### Why This Rule Exists |
| 12 | + |
| 13 | +Files ending with `.server.tsx` are intended for server-side rendering in React Server Components (RSC) architecture. The `'use client'` directive forces webpack to bundle these files as client components, which creates a fundamental contradiction and causes errors when using React's `react-server` conditional exports. |
| 14 | + |
| 15 | +This issue became apparent with Shakapacker 9.3.0+, which properly honors `resolve.conditionNames` in webpack configurations. When webpack resolves imports with the `react-server` condition, React's server exports intentionally omit client-only APIs like: |
| 16 | + |
| 17 | +- `createContext`, `useContext` |
| 18 | +- `useState`, `useEffect`, `useLayoutEffect`, `useReducer` |
| 19 | +- `Component`, `PureComponent` |
| 20 | +- Other hooks (`use*` functions) |
| 21 | + |
| 22 | +#### Examples |
| 23 | + |
| 24 | +❌ **Incorrect** - Will trigger an error: |
| 25 | + |
| 26 | +```typescript |
| 27 | +// Component.server.tsx |
| 28 | +'use client'; |
| 29 | + |
| 30 | +import React from 'react'; |
| 31 | + |
| 32 | +export function MyComponent() { |
| 33 | + return <div>Component</div>; |
| 34 | +} |
| 35 | +``` |
| 36 | + |
| 37 | +✅ **Correct** - No directive in server files: |
| 38 | + |
| 39 | +```typescript |
| 40 | +// Component.server.tsx |
| 41 | +import React from 'react'; |
| 42 | + |
| 43 | +export function MyComponent() { |
| 44 | + return <div>Component</div>; |
| 45 | +} |
| 46 | +``` |
| 47 | + |
| 48 | +✅ **Correct** - Use `'use client'` in client files: |
| 49 | + |
| 50 | +```typescript |
| 51 | +// Component.client.tsx or Component.tsx |
| 52 | +'use client'; |
| 53 | + |
| 54 | +import React, { useState } from 'react'; |
| 55 | + |
| 56 | +export function MyComponent() { |
| 57 | + const [count, setCount] = useState(0); |
| 58 | + return <div>Count: {count}</div>; |
| 59 | +} |
| 60 | +``` |
| 61 | + |
| 62 | +#### Auto-fix |
| 63 | + |
| 64 | +This rule includes an automatic fixer that will remove the `'use client'` directive from `.server.tsx` files when you run ESLint with the `--fix` option: |
| 65 | + |
| 66 | +```bash |
| 67 | +npx eslint --fix path/to/file.server.tsx |
| 68 | +``` |
| 69 | + |
| 70 | +#### Related |
| 71 | + |
| 72 | +- **Issue:** [Shakapacker #805 - Breaking change in 9.3.0](https://github.com/shakacode/shakapacker/issues/805) |
| 73 | +- **Fix PR:** [React on Rails #1896](https://github.com/shakacode/react_on_rails/pull/1896) |
| 74 | +- **Commit:** [86979dca - Remove 'use client' from .server.tsx files](https://github.com/shakacode/react_on_rails/commit/86979dca) |
| 75 | + |
| 76 | +#### Configuration |
| 77 | + |
| 78 | +This rule is automatically enabled in the React on Rails ESLint configuration at the `error` level. It's defined in `eslint.config.ts`: |
| 79 | + |
| 80 | +```typescript |
| 81 | +plugins: { |
| 82 | + 'react-on-rails': { |
| 83 | + rules: { |
| 84 | + 'no-use-client-in-server-files': noUseClientInServerFiles, |
| 85 | + }, |
| 86 | + }, |
| 87 | +}, |
| 88 | + |
| 89 | +rules: { |
| 90 | + 'react-on-rails/no-use-client-in-server-files': 'error', |
| 91 | + // ... other rules |
| 92 | +} |
| 93 | +``` |
| 94 | + |
| 95 | +## Testing |
| 96 | + |
| 97 | +To run tests for the custom rules: |
| 98 | + |
| 99 | +```bash |
| 100 | +node eslint-rules/no-use-client-in-server-files.test.cjs |
| 101 | +``` |
| 102 | + |
| 103 | +## Adding New Custom Rules |
| 104 | + |
| 105 | +To add a new custom ESLint rule: |
| 106 | + |
| 107 | +1. Create the rule file in this directory (use `.cjs` extension for CommonJS) |
| 108 | +2. Create a corresponding test file (e.g., `rule-name.test.cjs`) |
| 109 | +3. Import and register the rule in `eslint.config.ts` |
| 110 | +4. Add documentation to this README |
0 commit comments