355 lines
		
	
	
		
			6.8 KiB
		
	
	
	
		
			Markdown
		
	
	
	
			
		
		
	
	
			355 lines
		
	
	
		
			6.8 KiB
		
	
	
	
		
			Markdown
		
	
	
	
# @coze-arch/eslint-plugin
 | 
						|
 | 
						|
A comprehensive ESLint plugin designed for Flow applications, providing essential linting rules for code quality, import management, and Zustand state management best practices.
 | 
						|
 | 
						|
## Features
 | 
						|
 | 
						|
### Core Rules
 | 
						|
- **Import Management**: Prevent deep relative imports and batch import/export issues
 | 
						|
- **Code Quality**: Enforce function length limits, proper error handling, and catch block usage
 | 
						|
- **Package Management**: Validate package.json structure and dependencies
 | 
						|
- **React/TSX**: Prevent leaked renders and other React-specific issues
 | 
						|
 | 
						|
### Zustand Rules
 | 
						|
- **State Management**: Enforce proper state mutation patterns and store conventions
 | 
						|
- **Performance**: Optimize selector usage and prevent unnecessary re-renders
 | 
						|
- **Best Practices**: Enforce naming conventions and proper store typing
 | 
						|
 | 
						|
### Processors
 | 
						|
- **JSON Processor**: Custom processor for linting package.json files
 | 
						|
 | 
						|
## Get Started
 | 
						|
 | 
						|
### Installation
 | 
						|
 | 
						|
```bash
 | 
						|
# Install the package
 | 
						|
rush update
 | 
						|
 | 
						|
# Or using pnpm in workspace
 | 
						|
pnpm add @coze-arch/eslint-plugin@workspace:*
 | 
						|
```
 | 
						|
 | 
						|
### Basic Usage
 | 
						|
 | 
						|
Add the plugin to your ESLint configuration:
 | 
						|
 | 
						|
```js
 | 
						|
// eslint.config.js
 | 
						|
import flowPlugin from '@coze-arch/eslint-plugin';
 | 
						|
 | 
						|
export default [
 | 
						|
  {
 | 
						|
    plugins: {
 | 
						|
      '@coze-arch': flowPlugin,
 | 
						|
    },
 | 
						|
    rules: {
 | 
						|
      '@coze-arch/no-deep-relative-import': ['error', { max: 4 }],
 | 
						|
      '@coze-arch/max-line-per-function': ['error', { max: 150 }],
 | 
						|
      '@coze-arch/tsx-no-leaked-render': 'warn',
 | 
						|
    },
 | 
						|
  },
 | 
						|
];
 | 
						|
```
 | 
						|
 | 
						|
### Using Recommended Configuration
 | 
						|
 | 
						|
```js
 | 
						|
// eslint.config.js
 | 
						|
import flowPlugin from '@coze-arch/eslint-plugin';
 | 
						|
 | 
						|
export default [
 | 
						|
  ...flowPlugin.configs.recommended,
 | 
						|
];
 | 
						|
```
 | 
						|
 | 
						|
### Zustand Rules
 | 
						|
 | 
						|
```js
 | 
						|
// eslint.config.js
 | 
						|
import zustandPlugin from '@coze-arch/eslint-plugin/zustand';
 | 
						|
 | 
						|
export default [
 | 
						|
  {
 | 
						|
    plugins: {
 | 
						|
      '@coze-arch/zustand': zustandPlugin,
 | 
						|
    },
 | 
						|
    ...zustandPlugin.configs.recommended,
 | 
						|
  },
 | 
						|
];
 | 
						|
```
 | 
						|
 | 
						|
## API Reference
 | 
						|
 | 
						|
### Core Rules
 | 
						|
 | 
						|
#### `no-deep-relative-import`
 | 
						|
Prevents excessive relative import nesting.
 | 
						|
 | 
						|
```js
 | 
						|
// ❌ Bad (default max: 3)
 | 
						|
import something from '../../../deep/path';
 | 
						|
 | 
						|
// ✅ Good
 | 
						|
import something from '../../shallow/path';
 | 
						|
```
 | 
						|
 | 
						|
**Options:**
 | 
						|
- `max` (number): Maximum allowed relative path depth (default: 3)
 | 
						|
 | 
						|
#### `max-line-per-function`
 | 
						|
Enforces maximum lines per function.
 | 
						|
 | 
						|
```js
 | 
						|
// ❌ Bad (exceeds limit)
 | 
						|
function longFunction() {
 | 
						|
  // ... 200 lines of code
 | 
						|
}
 | 
						|
 | 
						|
// ✅ Good
 | 
						|
function shortFunction() {
 | 
						|
  // ... less than 150 lines
 | 
						|
}
 | 
						|
```
 | 
						|
 | 
						|
**Options:**
 | 
						|
- `max` (number): Maximum lines per function (default: 150)
 | 
						|
 | 
						|
#### `tsx-no-leaked-render`
 | 
						|
Prevents leaked renders in TSX components.
 | 
						|
 | 
						|
```js
 | 
						|
// ❌ Bad
 | 
						|
{count && <Component />} // count could be 0
 | 
						|
 | 
						|
// ✅ Good
 | 
						|
{count > 0 && <Component />}
 | 
						|
{Boolean(count) && <Component />}
 | 
						|
```
 | 
						|
 | 
						|
#### `no-pkg-dir-import`
 | 
						|
Prevents importing from package directories.
 | 
						|
 | 
						|
```js
 | 
						|
// ❌ Bad
 | 
						|
import something from 'package/src/internal';
 | 
						|
 | 
						|
// ✅ Good
 | 
						|
import something from 'package';
 | 
						|
```
 | 
						|
 | 
						|
#### `use-error-in-catch`
 | 
						|
Enforces proper error handling in catch blocks.
 | 
						|
 | 
						|
```js
 | 
						|
// ❌ Bad
 | 
						|
try {
 | 
						|
  doSomething();
 | 
						|
} catch (e) {
 | 
						|
  console.log('error occurred');
 | 
						|
}
 | 
						|
 | 
						|
// ✅ Good
 | 
						|
try {
 | 
						|
  doSomething();
 | 
						|
} catch (error) {
 | 
						|
  console.error('error occurred:', error);
 | 
						|
}
 | 
						|
```
 | 
						|
 | 
						|
#### `no-empty-catch`
 | 
						|
Prevents empty catch blocks.
 | 
						|
 | 
						|
```js
 | 
						|
// ❌ Bad
 | 
						|
try {
 | 
						|
  doSomething();
 | 
						|
} catch (error) {
 | 
						|
  // empty
 | 
						|
}
 | 
						|
 | 
						|
// ✅ Good
 | 
						|
try {
 | 
						|
  doSomething();
 | 
						|
} catch (error) {
 | 
						|
  console.error(error);
 | 
						|
}
 | 
						|
```
 | 
						|
 | 
						|
#### `no-new-error`
 | 
						|
Discourages creating new Error instances.
 | 
						|
 | 
						|
```js
 | 
						|
// ❌ Bad
 | 
						|
throw new Error('Something went wrong');
 | 
						|
 | 
						|
// ✅ Good (when configured)
 | 
						|
throw createError('Something went wrong');
 | 
						|
```
 | 
						|
 | 
						|
### Zustand Rules
 | 
						|
 | 
						|
#### `no-state-mutation`
 | 
						|
Prevents direct state mutation in Zustand stores.
 | 
						|
 | 
						|
```js
 | 
						|
// ❌ Bad
 | 
						|
const state = useStore.getState();
 | 
						|
state.count = 5;
 | 
						|
 | 
						|
// ✅ Good
 | 
						|
useStore.setState({ count: 5 });
 | 
						|
```
 | 
						|
 | 
						|
#### `prefer-selector`
 | 
						|
Encourages using selectors for state access.
 | 
						|
 | 
						|
```js
 | 
						|
// ❌ Bad
 | 
						|
const { count, name } = useStore();
 | 
						|
 | 
						|
// ✅ Good
 | 
						|
const count = useStore(state => state.count);
 | 
						|
const name = useStore(state => state.name);
 | 
						|
```
 | 
						|
 | 
						|
#### `store-name-convention`
 | 
						|
Enforces naming conventions for stores.
 | 
						|
 | 
						|
```js
 | 
						|
// ❌ Bad
 | 
						|
const myStore = create(() => ({}));
 | 
						|
 | 
						|
// ✅ Good
 | 
						|
const useMyStore = create(() => ({}));
 | 
						|
```
 | 
						|
 | 
						|
#### `prefer-shallow`
 | 
						|
Encourages using shallow equality for object selections.
 | 
						|
 | 
						|
```js
 | 
						|
// ❌ Bad
 | 
						|
const { user, settings } = useStore(state => ({
 | 
						|
  user: state.user,
 | 
						|
  settings: state.settings
 | 
						|
}));
 | 
						|
 | 
						|
// ✅ Good
 | 
						|
const { user, settings } = useStore(
 | 
						|
  state => ({ user: state.user, settings: state.settings }),
 | 
						|
  shallow
 | 
						|
);
 | 
						|
```
 | 
						|
 | 
						|
### Package.json Rules
 | 
						|
 | 
						|
#### `package-require-author`
 | 
						|
Ensures package.json has an author field.
 | 
						|
 | 
						|
```json
 | 
						|
{
 | 
						|
  "name": "my-package",
 | 
						|
  "author": "developer@example.com"
 | 
						|
}
 | 
						|
```
 | 
						|
 | 
						|
#### `package-disallow-deps`
 | 
						|
Prevents usage of disallowed dependencies (configurable).
 | 
						|
 | 
						|
## Development
 | 
						|
 | 
						|
### Setup
 | 
						|
 | 
						|
```bash
 | 
						|
# Install dependencies
 | 
						|
rush update
 | 
						|
 | 
						|
# Run tests
 | 
						|
rushx test
 | 
						|
 | 
						|
# Run with coverage
 | 
						|
rushx test:cov
 | 
						|
 | 
						|
# Lint code
 | 
						|
rushx lint
 | 
						|
 | 
						|
# Build (no-op for this package)
 | 
						|
rushx build
 | 
						|
```
 | 
						|
 | 
						|
### Project Structure
 | 
						|
 | 
						|
```
 | 
						|
src/
 | 
						|
├── index.ts              # Main plugin entry
 | 
						|
├── processors/
 | 
						|
│   └── json.ts          # JSON processor for package.json
 | 
						|
├── rules/               # Core ESLint rules
 | 
						|
│   ├── no-deep-relative-import/
 | 
						|
│   ├── max-lines-per-function/
 | 
						|
│   ├── tsx-no-leaked-render/
 | 
						|
│   └── ...
 | 
						|
└── zustand/             # Zustand-specific rules
 | 
						|
    ├── index.ts         # Zustand plugin entry
 | 
						|
    └── rules/
 | 
						|
        ├── no-state-mutation/
 | 
						|
        ├── prefer-selector/
 | 
						|
        └── ...
 | 
						|
```
 | 
						|
 | 
						|
### Adding New Rules
 | 
						|
 | 
						|
1. Create a new directory under `src/rules/` or `src/zustand/rules/`
 | 
						|
2. Implement the rule in `index.ts`
 | 
						|
3. Add comprehensive tests in `index.test.ts`
 | 
						|
4. Export the rule in the main plugin file
 | 
						|
5. Add the rule to recommended configuration if appropriate
 | 
						|
 | 
						|
### Testing
 | 
						|
 | 
						|
Tests are written using ESLint's `RuleTester`:
 | 
						|
 | 
						|
```ts
 | 
						|
import { RuleTester } from 'eslint';
 | 
						|
import { myRule } from './index';
 | 
						|
 | 
						|
const ruleTester = new RuleTester();
 | 
						|
 | 
						|
ruleTester.run('my-rule', myRule, {
 | 
						|
  valid: [
 | 
						|
    // Valid code examples
 | 
						|
  ],
 | 
						|
  invalid: [
 | 
						|
    // Invalid code examples with expected errors
 | 
						|
  ],
 | 
						|
});
 | 
						|
```
 | 
						|
 | 
						|
## Dependencies
 | 
						|
 | 
						|
### Runtime Dependencies
 | 
						|
- `@typescript-eslint/utils` - TypeScript ESLint utilities
 | 
						|
- `eslint-module-utils` - ESLint module resolution utilities
 | 
						|
- `eslint-rule-composer` - Rule composition utilities
 | 
						|
- `eslint-traverse` - AST traversal utilities
 | 
						|
- `eslint-utils` - General ESLint utilities
 | 
						|
- `semver` - Semantic versioning utilities
 | 
						|
 | 
						|
### Development Dependencies
 | 
						|
- `@typescript-eslint/rule-tester` - Rule testing utilities
 | 
						|
- `vitest` - Test runner
 | 
						|
- `eslint` - ESLint core
 | 
						|
- TypeScript and various ESLint plugins for development
 | 
						|
 | 
						|
## License
 | 
						|
 | 
						|
Apache-2.0 License
 | 
						|
 | 
						|
## Author
 | 
						|
 | 
						|
fanwenjie.fe@bytedance.com
 | 
						|
 | 
						|
---
 | 
						|
 | 
						|
For more information about ESLint plugin development, see the [ESLint Plugin Developer Guide](https://eslint.org/docs/developer-guide/).
 |