Skip to content

Commit 93a083c

Browse files
committed
refactor(rules): consolidate imports and add contributing guide 🦖
- Add CONTRIBUTING.md documentation file - Reduce code duplication across rule implementations - Refactor all rule files to use namespace imports - Replace named imports with namespace imports for better organization
1 parent ab30fad commit 93a083c

18 files changed

Lines changed: 372 additions & 318 deletions

CONTRIBUTING.md

Lines changed: 138 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,138 @@
1+
# Contributing to @neabyte/deno-lint
2+
3+
Thank you for your interest in contributing to this Deno lint plugin collection!
4+
5+
## Development Setup
6+
7+
1. **Prerequisites**: Deno 2.2.0+
8+
2. **Clone the repository**
9+
3. **Run tests**: `deno task test`
10+
4. **Check code**: `deno task check`
11+
12+
## Adding New Rules
13+
14+
### 1. Create Rule Implementation
15+
16+
Create a new file in `src/rules/` following the naming convention `RuleName.ts`:
17+
18+
```typescript
19+
import type * as types from '@interfaces/index.ts'
20+
import * as utils from '@utils/index.ts'
21+
22+
/**
23+
* Lint rule for [rule description].
24+
*/
25+
export const yourRuleNameRule = {
26+
/**
27+
* Creates the lint rule implementation.
28+
* @param context - The Deno lint context for reporting issues and fixes
29+
* @returns Object containing visitor functions for AST node types
30+
*/
31+
create(context: types.LintContext): Record<string, (node: types.DenoASTNode) => void> {
32+
return {
33+
/**
34+
* Visitor function for [node type].
35+
* @param node - The AST node representing a [node type]
36+
*/
37+
YourNodeType(node: types.DenoASTNode): void {
38+
if (!utils.isYourNodeType(node)) {
39+
return
40+
}
41+
// Rule logic here
42+
context.report({
43+
node,
44+
message: 'Your rule message',
45+
fix: fixer => fixer.replaceText(node, 'fixed code') // optional
46+
})
47+
}
48+
}
49+
}
50+
}
51+
```
52+
53+
### 2. Export Rule
54+
55+
Add your rule to `src/rules/index.ts`:
56+
57+
```typescript
58+
export * from '@rules/YourRuleName.ts'
59+
```
60+
61+
### 3. Register Rule
62+
63+
Add your rule to `src/index.ts`:
64+
65+
```typescript
66+
const plugin: LintPlugin = {
67+
name: 'deno-lint',
68+
rules: {
69+
// ... existing rules
70+
'your-rule-name': rules.yourRuleNameRule
71+
}
72+
}
73+
```
74+
75+
### 4. Write Tests
76+
77+
Create tests in `tests/rules/YourRuleName.ts`:
78+
79+
```typescript
80+
import { runnerTest, verifyAutoFix } from '@tests/index.ts'
81+
82+
const rulesId = 'deno-lint/your-rule-name'
83+
84+
Deno.test('your-rule-name (should trigger)', () => runnerTest(rulesId, 'problematic code', 1))
85+
86+
Deno.test('your-rule-name (should not trigger)', () => runnerTest(rulesId, 'valid code', 0))
87+
88+
Deno.test('verify auto-fix', () =>
89+
verifyAutoFix(rulesId, 'code to fix', 'expected fixed code', 'description')
90+
)
91+
```
92+
93+
### 5. Add Documentation
94+
95+
Create an example file in `examples/your-rule-name.md` with:
96+
97+
- Rule description
98+
- Examples of violations
99+
- Examples of correct usage
100+
- Auto-fix examples (if applicable)
101+
102+
## Code Style
103+
104+
- Follow the project's TypeScript configuration
105+
- Use JSDoc comments for functions and exports
106+
- Prefer explicit type annotations
107+
- Use arrow functions for callbacks
108+
- Follow the existing naming conventions
109+
- Use utility functions from `@utils/index.ts` when available
110+
- Include comprehensive test coverage with both positive and negative cases
111+
- **Import all types using namespace imports from `@interfaces/index.ts`** (may not be 100% complete yet) - if you find missing types, add them following the existing style
112+
113+
## Testing
114+
115+
- All rules must have comprehensive tests
116+
- Test both positive and negative cases
117+
- Include auto-fix tests when applicable
118+
- Run `deno task test` before submitting
119+
120+
## Submitting Changes
121+
122+
1. Ensure all tests pass: `deno task test`
123+
2. Run code checks: `deno task check`
124+
3. Create a pull request with a clear description
125+
4. Reference any related issues
126+
127+
## Rule Guidelines
128+
129+
- **Do not duplicate existing Deno lint features** - Check [Deno's built-in rules](./development/DENO-RULES.md) before creating new ones
130+
- Rules should be focused and specific
131+
- Provide helpful error messages
132+
- Include auto-fixes when possible
133+
- Consider performance implications
134+
- Follow Deno's lint plugin conventions
135+
136+
## Questions?
137+
138+
Feel free to open an issue for questions about contributing or rule implementation.

src/rules/AsyncFunctionNaming.ts

Lines changed: 7 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,5 @@
1-
import type { DenoASTNode, LintContext } from '@interfaces/index.ts'
2-
import {
3-
createAddSuffixFix,
4-
followsAsyncNamingConvention,
5-
getFunctionName,
6-
isAsyncFunction
7-
} from '@utils/index.ts'
1+
import type * as types from '@interfaces/index.ts'
2+
import * as utils from '@utils/index.ts'
83

94
/**
105
* Lint rule for enforcing async function naming conventions.
@@ -15,20 +10,20 @@ export const asyncFunctionNamingRule = {
1510
* @param context - The Deno lint context for reporting issues and fixes
1611
* @returns Object containing visitor functions for AST node types
1712
*/
18-
create(context: LintContext): Record<string, (node: DenoASTNode) => void> {
13+
create(context: types.LintContext): Record<string, (node: types.DenoASTNode) => void> {
1914
return {
2015
/**
2116
* Visitor function for function declarations.
2217
* @param node - The AST node representing a function declaration
2318
*/
24-
FunctionDeclaration(node: DenoASTNode): void {
25-
if (isAsyncFunction(node) && !followsAsyncNamingConvention(node, 'Async')) {
26-
const functionName = getFunctionName(node)
19+
FunctionDeclaration(node: types.DenoASTNode): void {
20+
if (utils.isAsyncFunction(node) && !utils.followsAsyncNamingConvention(node, 'Async')) {
21+
const functionName = utils.getFunctionName(node)
2722
if (functionName) {
2823
context.report({
2924
node,
3025
message: "Async functions should be named with 'Async' suffix",
31-
fix: createAddSuffixFix(context, node, 'Async')
26+
fix: utils.createAddSuffixFix(context, node, 'Async')
3227
})
3328
}
3429
}

src/rules/ExplicitParameterTypes.ts

Lines changed: 27 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,5 @@
1-
import type { DenoASTNode, LintContext, LintFixer, ParameterNode } from '@interfaces/index.ts'
2-
import {
3-
createAddDefaultTypeFix,
4-
createAddDestructuredTypeFix,
5-
createAddParameterTypeFix,
6-
createAddRestTypeFix,
7-
getParameterName,
8-
hasParameterType,
9-
isArrowFunctionExpression,
10-
isFunctionDeclaration,
11-
isFunctionExpression,
12-
isMethodDefinition
13-
} from '@utils/index.ts'
1+
import type * as types from '@interfaces/index.ts'
2+
import * as utils from '@utils/index.ts'
143

154
/**
165
* Checks all parameters in a function for explicit type annotations.
@@ -19,13 +8,13 @@ import {
198
* @param params - Array of parameter nodes to check
209
*/
2110
function checkParameters(
22-
context: LintContext,
23-
node: DenoASTNode,
24-
params: Array<ParameterNode>
11+
context: types.LintContext,
12+
node: types.DenoASTNode,
13+
params: Array<types.ParameterNode>
2514
): void {
26-
params.forEach((param: ParameterNode) => {
27-
if (!hasParameterType(param)) {
28-
const paramName = getParameterName(param)
15+
params.forEach((param: types.ParameterNode) => {
16+
if (!utils.hasParameterType(param)) {
17+
const paramName = utils.getParameterName(param)
2918
const message = paramName
3019
? `Parameter '${paramName}' must have explicit type annotation`
3120
: 'Parameter must have explicit type annotation'
@@ -46,21 +35,21 @@ function checkParameters(
4635
* @returns The appropriate fix function
4736
*/
4837
function getFixFunction(
49-
context: LintContext,
50-
node: DenoASTNode,
51-
param: ParameterNode
52-
): (fixer: LintFixer) => unknown {
38+
context: types.LintContext,
39+
node: types.DenoASTNode,
40+
param: types.ParameterNode
41+
): (fixer: types.LintFixer) => unknown {
5342
switch (param.type) {
5443
case 'AssignmentPattern':
55-
return createAddDefaultTypeFix(context, node)
44+
return utils.createAddDefaultTypeFix(context, node)
5645
case 'Identifier':
57-
return createAddParameterTypeFix(context, node, param.name || 'param')
46+
return utils.createAddParameterTypeFix(context, node, param.name || 'param')
5847
case 'ObjectPattern':
59-
return createAddDestructuredTypeFix(context, node)
48+
return utils.createAddDestructuredTypeFix(context, node)
6049
case 'RestElement':
61-
return createAddRestTypeFix(context, node)
50+
return utils.createAddRestTypeFix(context, node)
6251
default:
63-
return createAddParameterTypeFix(context, node, 'param')
52+
return utils.createAddParameterTypeFix(context, node, 'param')
6453
}
6554
}
6655

@@ -73,14 +62,14 @@ export const explicitParameterTypesRule = {
7362
* @param context - The Deno lint context for reporting issues and fixes
7463
* @returns Object containing visitor functions for AST node types
7564
*/
76-
create(context: LintContext): Record<string, (node: DenoASTNode) => void> {
65+
create(context: types.LintContext): Record<string, (node: types.DenoASTNode) => void> {
7766
return {
7867
/**
7968
* Visitor function for arrow function expressions.
8069
* @param node - The AST node representing an arrow function expression
8170
*/
82-
ArrowFunctionExpression(node: DenoASTNode): void {
83-
if (!isArrowFunctionExpression(node)) {
71+
ArrowFunctionExpression(node: types.DenoASTNode): void {
72+
if (!utils.isArrowFunctionExpression(node)) {
8473
return
8574
}
8675
checkParameters(context, node, node.params)
@@ -89,8 +78,8 @@ export const explicitParameterTypesRule = {
8978
* Visitor function for function declarations.
9079
* @param node - The AST node representing a function declaration
9180
*/
92-
FunctionDeclaration(node: DenoASTNode): void {
93-
if (!isFunctionDeclaration(node)) {
81+
FunctionDeclaration(node: types.DenoASTNode): void {
82+
if (!utils.isFunctionDeclaration(node)) {
9483
return
9584
}
9685
checkParameters(context, node, node.params)
@@ -99,11 +88,11 @@ export const explicitParameterTypesRule = {
9988
* Visitor function for function expressions.
10089
* @param node - The AST node representing a function expression
10190
*/
102-
FunctionExpression(node: DenoASTNode): void {
103-
if (!isFunctionExpression(node)) {
91+
FunctionExpression(node: types.DenoASTNode): void {
92+
if (!utils.isFunctionExpression(node)) {
10493
return
10594
}
106-
if (node.parent && isMethodDefinition(node.parent as DenoASTNode)) {
95+
if (node.parent && utils.isMethodDefinition(node.parent as types.DenoASTNode)) {
10796
return
10897
}
10998
checkParameters(context, node, node.params)
@@ -112,8 +101,8 @@ export const explicitParameterTypesRule = {
112101
* Visitor function for method definitions.
113102
* @param node - The AST node representing a method definition
114103
*/
115-
MethodDefinition(node: DenoASTNode): void {
116-
if (!isMethodDefinition(node)) {
104+
MethodDefinition(node: types.DenoASTNode): void {
105+
if (!utils.isMethodDefinition(node)) {
117106
return
118107
}
119108
checkParameters(context, node, node.value.params)

src/rules/ExplicitReturnTypes.ts

Lines changed: 8 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,5 @@
1-
import type { DenoASTNode, LintContext } from '@interfaces/index.ts'
2-
import {
3-
createAddReturnTypeFix,
4-
hasReturnType,
5-
inferReturnType,
6-
isFunctionDeclaration
7-
} from '@utils/index.ts'
1+
import type * as types from '@interfaces/index.ts'
2+
import * as utils from '@utils/index.ts'
83

94
/**
105
* Lint rule for enforcing explicit return type annotations.
@@ -15,22 +10,22 @@ export const explicitReturnTypesRule = {
1510
* @param context - The Deno lint context for reporting issues and fixes
1611
* @returns Object containing visitor functions for AST node types
1712
*/
18-
create(context: LintContext): Record<string, (node: DenoASTNode) => void> {
13+
create(context: types.LintContext): Record<string, (node: types.DenoASTNode) => void> {
1914
return {
2015
/**
2116
* Visitor function for function declarations.
2217
* @param node - The AST node representing a function declaration
2318
*/
24-
FunctionDeclaration(node: DenoASTNode): void {
25-
if (!isFunctionDeclaration(node)) {
19+
FunctionDeclaration(node: types.DenoASTNode): void {
20+
if (!utils.isFunctionDeclaration(node)) {
2621
return
2722
}
28-
if (!hasReturnType(node)) {
29-
const returnType = inferReturnType(node, context)
23+
if (!utils.hasReturnType(node)) {
24+
const returnType = utils.inferReturnType(node, context)
3025
context.report({
3126
node,
3227
message: 'Function must have explicit return type annotation',
33-
fix: createAddReturnTypeFix(context, node, returnType)
28+
fix: utils.createAddReturnTypeFix(context, node, returnType)
3429
})
3530
}
3631
}

src/rules/PreferArrayEvery.ts

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
import type { DenoASTNode, LintContext } from '@interfaces/index.ts'
2-
import { canReplaceWithArrayMethod, createArrayMethodFix, isForStatement } from '@utils/index.ts'
1+
import type * as types from '@interfaces/index.ts'
2+
import * as utils from '@utils/index.ts'
33

44
/**
55
* Lint rule for preferring Array.every() over manual for loops.
@@ -10,21 +10,21 @@ export const preferArrayEveryRule = {
1010
* @param context - The Deno lint context for reporting issues and fixes
1111
* @returns Object containing visitor functions for AST node types
1212
*/
13-
create(context: LintContext): Record<string, (node: DenoASTNode) => void> {
13+
create(context: types.LintContext): Record<string, (node: types.DenoASTNode) => void> {
1414
return {
1515
/**
1616
* Visitor function for for statements.
1717
* @param node - The AST node representing a for statement
1818
*/
19-
ForStatement(node: DenoASTNode): void {
20-
if (!isForStatement(node)) {
19+
ForStatement(node: types.DenoASTNode): void {
20+
if (!utils.isForStatement(node)) {
2121
return
2222
}
23-
if (canReplaceWithArrayMethod(node, 'every')) {
23+
if (utils.canReplaceWithArrayMethod(node, 'every')) {
2424
context.report({
2525
node,
2626
message: 'Prefer Array.every() over manual for loops that return false',
27-
fix: createArrayMethodFix(context, node, 'every')
27+
fix: utils.createArrayMethodFix(context, node, 'every')
2828
})
2929
}
3030
}

0 commit comments

Comments
 (0)