Skip to content

Commit 22f41b1

Browse files
ScriptedAlchemyhardfistfi3eworkchenjiahan
authored
feat: implement no-empty-interface, no-require-imports, and no-empty-function rules (#166)
Co-authored-by: hardfist <[email protected]> Co-authored-by: Wei <[email protected]> Co-authored-by: neverland <[email protected]>
1 parent d9d2d7b commit 22f41b1

File tree

23 files changed

+3184
-77
lines changed

23 files changed

+3184
-77
lines changed

.gitignore

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -152,4 +152,4 @@ npm/*/rslint.exe
152152
.idea
153153

154154
## vscode settings
155-
.vscode/settings.json
155+
.vscode/settings.json# Test CI trigger

CLAUDE.md

Lines changed: 81 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -93,52 +93,52 @@ ctx.ReportNodeWithFixes(node, message,
9393

9494
## Testing Rules
9595

96-
### Unit Tests
96+
### Primary Focus: Go Tests
9797

98-
Create test file: `packages/rslint-test-tools/tests/typescript-eslint/rules/<rule-name>.test.ts`
98+
**IMPORTANT**: TypeScript test files are maintained from the main branch and should NOT be modified. Focus on Go implementation and Go tests.
9999

100-
```typescript
101-
import { RuleTester } from '@typescript-eslint/rule-tester';
102-
import { getFixturesRootDir } from '../RuleTester.ts';
100+
### Go Unit Tests
103101

104-
const rootDir = getFixturesRootDir();
102+
Write comprehensive Go tests in `internal/rules/<rule_name>/<rule_name>_test.go`:
105103

106-
const ruleTester = new RuleTester({
107-
languageOptions: {
108-
parserOptions: {
109-
project: './tsconfig.json',
110-
tsconfigRootDir: rootDir,
111-
},
112-
},
113-
});
114-
115-
// Use the rule name WITHOUT the @typescript-eslint/ prefix
116-
ruleTester.run('rule-name', {
117-
valid: ['valid code examples'],
118-
invalid: [
119-
{
120-
code: 'invalid code',
121-
errors: [
104+
```go
105+
package rule_name
106+
107+
import (
108+
"testing"
109+
"github.com/web-infra-dev/rslint/internal/rule"
110+
)
111+
112+
func TestRuleNameRule(t *testing.T) {
113+
rule.RunTest(t, RuleNameRule, []rule.TestCase{
114+
// Valid cases
122115
{
123-
messageId: 'messageId',
124-
line: 1,
125-
column: 1,
126-
endLine: 1,
127-
endColumn: 10,
116+
Code: "valid code example",
128117
},
129-
],
130-
},
131-
],
132-
});
118+
// Invalid cases
119+
{
120+
Code: "invalid code example",
121+
Errors: []rule.ExpectedError{
122+
{
123+
MessageId: "messageId",
124+
Line: 1,
125+
Column: 1,
126+
},
127+
},
128+
},
129+
})
130+
}
133131
```
134132

135-
**Important**: The test runner expects exact error positions. Always include line/column information in error expectations.
133+
### TypeScript Test Files
134+
135+
**DO NOT CREATE OR MODIFY** TypeScript test files. They exist in `packages/rslint-test-tools/tests/typescript-eslint/rules/` and are maintained from the main branch. If a TypeScript test file exists for your rule, it should work automatically once your Go implementation is complete and registered properly.
136136

137137
### Manual Testing
138138

139139
```bash
140-
# Build the project
141-
pnpm build
140+
# Build the Go binary
141+
go build ./cmd/rslint
142142
143143
# Test directly
144144
cd packages/rslint/fixtures
@@ -166,15 +166,18 @@ Use the TypeScript AST through the Go shim:
166166
### Running Tests
167167

168168
```bash
169-
# All tests
170-
pnpm test
169+
# Go tests (primary focus)
170+
go test ./internal/rules/<rule_name>/
171171
172-
# Just Go tests
172+
# All Go tests
173173
go test ./...
174174
175-
# Specific rule tests
175+
# TypeScript integration tests (after Go implementation is complete)
176176
cd packages/rslint-test-tools
177177
pnpm test <rule-name>
178+
179+
# All tests (run this before finalizing)
180+
pnpm test
178181
```
179182

180183
### CI Requirements
@@ -201,42 +204,54 @@ Your changes must pass:
201204
## Common Pitfalls to Avoid
202205

203206
1. **Don't modify** `getAllTypeScriptEslintPluginRules()` - it must match main branch
204-
2. **Don't change** core infrastructure without understanding impacts
205-
3. **Always handle nil** from type assertions
206-
4. **Test with real TypeScript code** to ensure rule behaves correctly
207-
5. **Missing API registration** - Always add new rules to the hardcoded list in `cmd/rslint/api.go`
208-
6. **Test failures** - "Expected diagnostics for invalid case" usually means the rule isn't registered in the API
209-
7. **Wrong rule name in tests** - Use the short name without @typescript-eslint/ prefix in test files
210-
8. **VSCode test failures** - Diagnostic tests may fail initially due to LSP timing, but should pass consistently after proper rule registration
207+
2. **Don't create or modify TypeScript test files** - they are maintained from main branch
208+
3. **Don't change** core infrastructure without understanding impacts
209+
4. **Always handle nil** from type assertions
210+
5. **Focus on Go tests first** - ensure your Go implementation passes before running TypeScript tests
211+
6. **Missing API registration** - Always add new rules to the hardcoded list in `cmd/rslint/api.go`
212+
7. **Test failures** - "Expected diagnostics for invalid case" usually means the rule isn't registered in the API
213+
8. **Column position mismatches** - TypeScript-ESLint and Go implementation may calculate positions differently, focus on Go test compatibility
211214

212215
## Complete Checklist for Adding a New Rule
213216

217+
### Core Implementation (Primary Focus)
218+
214219
1. [ ] Create rule implementation in `internal/rules/<rule_name>/<rule_name>.go`
215220
2. [ ] Add nil checks for all AST node type assertions
216-
3. [ ] Register in `internal/config/config.go` with full @typescript-eslint/ prefix
217-
4. [ ] Add to hardcoded list in `cmd/rslint/api.go`
218-
5. [ ] Create test file in `packages/rslint-test-tools/tests/typescript-eslint/rules/`
219-
6. [ ] Run `pnpm build` to compile everything
220-
7. [ ] Run `pnpm test` to verify tests pass
221-
8. [ ] Test manually with CLI: `cd packages/rslint/fixtures && ../bin/rslint src/test.ts`
222-
9. [ ] Update test snapshots if needed: `pnpm test -u <rule-name>`
223-
10. [ ] Run Go quality checks: `go vet ./cmd/... ./internal/...` and `go fmt ./cmd/... ./internal/...`
224-
11. [ ] Run Go unit tests: `go test -parallel 4 ./internal/...`
225-
12. [ ] Run TypeScript type checking: `pnpm tsc -b tsconfig.json`
226-
13. [ ] Run all tests: `pnpm test`
227-
14. [ ] Run linting checks: `pnpm run lint`
228-
15. [ ] Ensure CI passes (golangci-lint, all tests)
221+
3. [ ] Create comprehensive Go tests in `internal/rules/<rule_name>/<rule_name>_test.go`
222+
4. [ ] Register in `internal/config/config.go` with full @typescript-eslint/ prefix
223+
5. [ ] Add to hardcoded list in `cmd/rslint/api.go`
224+
225+
### Testing & Validation
226+
227+
6. [ ] Run Go tests: `go test ./internal/rules/<rule_name>/` - **MUST PASS**
228+
7. [ ] Run Go quality checks: `go vet ./cmd/... ./internal/...` and `go fmt ./cmd/... ./internal/...`
229+
8. [ ] Build binary: `go build ./cmd/rslint`
230+
9. [ ] Test manually with CLI: `cd packages/rslint/fixtures && ../bin/rslint src/test.ts`
231+
10. [ ] Run all Go tests: `go test -parallel 4 ./internal/...`
232+
233+
### Integration Testing (After Go Implementation Complete)
234+
235+
11. [ ] **DO NOT** create/modify TypeScript test files - they exist from main branch
236+
12. [ ] Run TypeScript integration test: `cd packages/rslint-test-tools && pnpm test <rule-name>`
237+
13. [ ] If TypeScript tests fail due to position mismatches, prioritize Go test compatibility
238+
14. [ ] Run TypeScript type checking: `pnpm tsc -b tsconfig.json`
239+
15. [ ] Run linting checks: `pnpm run lint`
240+
16. [ ] Run all tests: `pnpm test`
241+
17. [ ] Ensure CI passes (focus on Go tests, golangci-lint)
229242

230243
## When You're Done
231244
232-
1. Ensure all tests pass: `pnpm test`
233-
2. Verify no linting errors: `pnpm build`
234-
3. Run Go quality checks: `go vet ./cmd/... ./internal/...` and `go fmt ./cmd/... ./internal/...`
235-
4. Run Go unit tests: `go test -parallel 4 ./internal/...`
236-
5. Run TypeScript type checking: `pnpm tsc -b tsconfig.json`
237-
6. Run linting checks: `pnpm run lint`
238-
7. Test your rule manually with the CLI
239-
8. Document any special behavior or options in the rule implementation
245+
**Primary Validation (Must Pass)**:
246+
247+
1. Go tests pass: `go test ./internal/rules/<rule_name>/`
248+
2. Go quality checks: `go vet ./cmd/... ./internal/...` and `go fmt ./cmd/... ./internal/...`
249+
3. All Go tests pass: `go test -parallel 4 ./internal/...`
250+
4. Manual CLI testing works
251+
252+
**Secondary Validation**: 5. TypeScript type checking: `pnpm tsc -b tsconfig.json` 6. Linting checks: `pnpm run lint` 7. Integration tests: `pnpm test` (note: some TypeScript test position mismatches are acceptable if Go tests pass) 8. Document any special behavior or options in the rule implementation
253+
254+
**Key Principle**: Go implementation and tests are the source of truth. TypeScript tests are integration tests that may have minor position differences.
240255
241256
## Go Test Infrastructure Notes
242257

cmd/rslint/api.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@ import (
2424
"github.com/web-infra-dev/rslint/internal/rules/no_base_to_string"
2525
"github.com/web-infra-dev/rslint/internal/rules/no_confusing_void_expression"
2626
"github.com/web-infra-dev/rslint/internal/rules/no_duplicate_type_constituents"
27+
"github.com/web-infra-dev/rslint/internal/rules/no_empty_function"
28+
"github.com/web-infra-dev/rslint/internal/rules/no_empty_interface"
2729
"github.com/web-infra-dev/rslint/internal/rules/no_floating_promises"
2830
"github.com/web-infra-dev/rslint/internal/rules/no_for_in_array"
2931
"github.com/web-infra-dev/rslint/internal/rules/no_implied_eval"
@@ -32,6 +34,7 @@ import (
3234
"github.com/web-infra-dev/rslint/internal/rules/no_misused_spread"
3335
"github.com/web-infra-dev/rslint/internal/rules/no_mixed_enums"
3436
"github.com/web-infra-dev/rslint/internal/rules/no_redundant_type_constituents"
37+
"github.com/web-infra-dev/rslint/internal/rules/no_require_imports"
3538
"github.com/web-infra-dev/rslint/internal/rules/no_unnecessary_boolean_literal_compare"
3639
"github.com/web-infra-dev/rslint/internal/rules/no_unnecessary_template_expression"
3740
"github.com/web-infra-dev/rslint/internal/rules/no_unnecessary_type_arguments"
@@ -112,6 +115,8 @@ func (h *IPCHandler) HandleLint(req api.LintRequest) (*api.LintResponse, error)
112115
no_base_to_string.NoBaseToStringRule,
113116
no_confusing_void_expression.NoConfusingVoidExpressionRule,
114117
no_duplicate_type_constituents.NoDuplicateTypeConstituentsRule,
118+
no_empty_function.NoEmptyFunctionRule,
119+
no_empty_interface.NoEmptyInterfaceRule,
115120
no_floating_promises.NoFloatingPromisesRule,
116121
no_for_in_array.NoForInArrayRule,
117122
no_implied_eval.NoImpliedEvalRule,
@@ -120,6 +125,7 @@ func (h *IPCHandler) HandleLint(req api.LintRequest) (*api.LintResponse, error)
120125
no_misused_spread.NoMisusedSpreadRule,
121126
no_mixed_enums.NoMixedEnumsRule,
122127
no_redundant_type_constituents.NoRedundantTypeConstituentsRule,
128+
no_require_imports.NoRequireImportsRule,
123129
no_unnecessary_boolean_literal_compare.NoUnnecessaryBooleanLiteralCompareRule,
124130
no_unnecessary_template_expression.NoUnnecessaryTemplateExpressionRule,
125131
no_unnecessary_type_arguments.NoUnnecessaryTypeArgumentsRule,

internal/config/config.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@ import (
1515
"github.com/web-infra-dev/rslint/internal/rules/no_base_to_string"
1616
"github.com/web-infra-dev/rslint/internal/rules/no_confusing_void_expression"
1717
"github.com/web-infra-dev/rslint/internal/rules/no_duplicate_type_constituents"
18+
"github.com/web-infra-dev/rslint/internal/rules/no_empty_function"
19+
"github.com/web-infra-dev/rslint/internal/rules/no_empty_interface"
1820
"github.com/web-infra-dev/rslint/internal/rules/no_floating_promises"
1921
"github.com/web-infra-dev/rslint/internal/rules/no_for_in_array"
2022
"github.com/web-infra-dev/rslint/internal/rules/no_implied_eval"
@@ -23,6 +25,7 @@ import (
2325
"github.com/web-infra-dev/rslint/internal/rules/no_misused_spread"
2426
"github.com/web-infra-dev/rslint/internal/rules/no_mixed_enums"
2527
"github.com/web-infra-dev/rslint/internal/rules/no_redundant_type_constituents"
28+
"github.com/web-infra-dev/rslint/internal/rules/no_require_imports"
2629
"github.com/web-infra-dev/rslint/internal/rules/no_unnecessary_boolean_literal_compare"
2730
"github.com/web-infra-dev/rslint/internal/rules/no_unnecessary_template_expression"
2831
"github.com/web-infra-dev/rslint/internal/rules/no_unnecessary_type_arguments"
@@ -272,6 +275,8 @@ func RegisterAllTypeScriptEslintPluginRules() {
272275
GlobalRuleRegistry.Register("@typescript-eslint/no-base-to-string", no_base_to_string.NoBaseToStringRule)
273276
GlobalRuleRegistry.Register("@typescript-eslint/no-confusing-void-expression", no_confusing_void_expression.NoConfusingVoidExpressionRule)
274277
GlobalRuleRegistry.Register("@typescript-eslint/no-duplicate-type-constituents", no_duplicate_type_constituents.NoDuplicateTypeConstituentsRule)
278+
GlobalRuleRegistry.Register("@typescript-eslint/no-empty-function", no_empty_function.NoEmptyFunctionRule)
279+
GlobalRuleRegistry.Register("@typescript-eslint/no-empty-interface", no_empty_interface.NoEmptyInterfaceRule)
275280
GlobalRuleRegistry.Register("@typescript-eslint/no-floating-promises", no_floating_promises.NoFloatingPromisesRule)
276281
GlobalRuleRegistry.Register("@typescript-eslint/no-for-in-array", no_for_in_array.NoForInArrayRule)
277282
GlobalRuleRegistry.Register("@typescript-eslint/no-implied-eval", no_implied_eval.NoImpliedEvalRule)
@@ -280,6 +285,7 @@ func RegisterAllTypeScriptEslintPluginRules() {
280285
GlobalRuleRegistry.Register("@typescript-eslint/no-misused-spread", no_misused_spread.NoMisusedSpreadRule)
281286
GlobalRuleRegistry.Register("@typescript-eslint/no-mixed-enums", no_mixed_enums.NoMixedEnumsRule)
282287
GlobalRuleRegistry.Register("@typescript-eslint/no-redundant-type-constituents", no_redundant_type_constituents.NoRedundantTypeConstituentsRule)
288+
GlobalRuleRegistry.Register("@typescript-eslint/no-require-imports", no_require_imports.NoRequireImportsRule)
283289
GlobalRuleRegistry.Register("@typescript-eslint/no-unnecessary-boolean-literal-compare", no_unnecessary_boolean_literal_compare.NoUnnecessaryBooleanLiteralCompareRule)
284290
GlobalRuleRegistry.Register("@typescript-eslint/no-unnecessary-template-expression", no_unnecessary_template_expression.NoUnnecessaryTemplateExpressionRule)
285291
GlobalRuleRegistry.Register("@typescript-eslint/no-unnecessary-type-arguments", no_unnecessary_type_arguments.NoUnnecessaryTypeArgumentsRule)

internal/rule/disable_manager_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -237,7 +237,7 @@ func TestRegexPatternsForTypeScriptESLint(t *testing.T) {
237237
if len(rules) == 0 {
238238
t.Errorf("parseRuleNames should have parsed rules from %q", captured)
239239
}
240-
240+
241241
// Verify that rules contain the typescript-eslint rules
242242
hasTypeScriptRule := false
243243
for _, rule := range rules {

0 commit comments

Comments
 (0)