Skip to content

Commit fcb46a5

Browse files
Fix API to handle project as string or array - member ordering now works with virtual files
- Fixed ParserOptions.Project to accept either string or []string - Updated API processing logic to handle both formats - Member ordering rule now processes virtual files correctly - Tests are now running but need rule logic tuning 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <[email protected]>
1 parent a656bb0 commit fcb46a5

File tree

12 files changed

+230
-9
lines changed

12 files changed

+230
-9
lines changed

cmd/rslint/api.go

Lines changed: 37 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -110,14 +110,36 @@ func (h *IPCHandler) HandleLint(req api.LintRequest) (*api.LintResponse, error)
110110
var tsConfigs []string
111111
var configDirectory string
112112

113-
if req.LanguageOptions != nil && req.LanguageOptions.ParserOptions != nil && req.LanguageOptions.ParserOptions.Project != "" {
113+
if req.LanguageOptions != nil && req.LanguageOptions.ParserOptions != nil && req.LanguageOptions.ParserOptions.Project != nil {
114114
// Use project from languageOptions
115115
configDirectory = currentDirectory
116-
projectPath := tspath.ResolvePath(currentDirectory, req.LanguageOptions.ParserOptions.Project)
117-
if !fs.FileExists(projectPath) {
118-
return nil, fmt.Errorf("tsconfig file specified in languageOptions %q doesn't exist", projectPath)
116+
117+
var projectPaths []string
118+
switch project := req.LanguageOptions.ParserOptions.Project.(type) {
119+
case string:
120+
if project != "" {
121+
projectPaths = []string{project}
122+
}
123+
case []interface{}:
124+
for _, p := range project {
125+
if str, ok := p.(string); ok && str != "" {
126+
projectPaths = append(projectPaths, str)
127+
}
128+
}
129+
}
130+
131+
if len(projectPaths) == 0 {
132+
return nil, fmt.Errorf("no valid project paths found in languageOptions")
133+
}
134+
135+
// Resolve and validate all project paths
136+
for _, projectPath := range projectPaths {
137+
resolvedPath := tspath.ResolvePath(currentDirectory, projectPath)
138+
if !fs.FileExists(resolvedPath) {
139+
return nil, fmt.Errorf("tsconfig file specified in languageOptions %q doesn't exist", resolvedPath)
140+
}
141+
tsConfigs = append(tsConfigs, resolvedPath)
119142
}
120-
tsConfigs = []string{projectPath}
121143
} else {
122144
// Use default configuration loading
123145
_, tsConfigs, configDirectory = rslintconfig.LoadConfigurationWithFallback(req.Config, currentDirectory, fs)
@@ -188,7 +210,16 @@ func (h *IPCHandler) HandleLint(req api.LintRequest) (*api.LintResponse, error)
188210
// filter rule based on request.RuleOptions
189211
if len(req.RuleOptions) > 0 {
190212
for _, r := range origin_rules {
191-
if option, ok := req.RuleOptions[r.Name]; ok {
213+
// Try both short name and full @typescript-eslint/ prefixed name
214+
var option interface{}
215+
var found bool
216+
if option, found = req.RuleOptions[r.Name]; found {
217+
// Found with short name (e.g., "member-ordering")
218+
} else if option, found = req.RuleOptions["@typescript-eslint/"+r.Name]; found {
219+
// Found with full name (e.g., "@typescript-eslint/member-ordering")
220+
}
221+
222+
if found {
192223
rulesWithOptions = append(rulesWithOptions, RuleWithOption{
193224
rule: r,
194225
option: option,

debug-member-ordering.ts

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
// no accessibility === public
2+
interface Foo {
3+
[Z: string]: any;
4+
A: string;
5+
B: string;
6+
C: string;
7+
D: string;
8+
E: string;
9+
F: string;
10+
G();
11+
H();
12+
I();
13+
J();
14+
K();
15+
L();
16+
new ();
17+
}

debug-rslint.json

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
[
2+
{
3+
"language": "javascript",
4+
"files": ["./debug-valid-test.ts"],
5+
"languageOptions": {
6+
"parserOptions": {
7+
"project": ["./tsconfig.json"]
8+
}
9+
},
10+
"rules": {
11+
"@typescript-eslint/member-ordering": []
12+
}
13+
}
14+
]

debug-test.mjs

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
import { lint } from './packages/rslint/dist/index.js';
2+
3+
const result = await lint({
4+
config: '/Users/bytedance/dev/rslint/packages/rslint-test-tools/tests/typescript-eslint/fixtures/rslint.json',
5+
workingDirectory: '/Users/bytedance/dev/rslint/packages/rslint-test-tools/tests/typescript-eslint/fixtures',
6+
fileContents: {
7+
'src/virtual.ts': `// no accessibility === public
8+
interface Foo {
9+
[Z: string]: any;
10+
A: string;
11+
B: string;
12+
C: string;
13+
D: string;
14+
E: string;
15+
F: string;
16+
new ();
17+
G();
18+
H();
19+
I();
20+
J();
21+
K();
22+
L();
23+
}`
24+
},
25+
ruleOptions: {
26+
'@typescript-eslint/member-ordering': []
27+
}
28+
});
29+
30+
console.log('Result:', JSON.stringify(result, null, 2));

debug-valid-test.ts

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
// no accessibility === public
2+
interface Foo {
3+
[Z: string]: any;
4+
A: string;
5+
B: string;
6+
C: string;
7+
D: string;
8+
E: string;
9+
F: string;
10+
new ();
11+
G();
12+
H();
13+
I();
14+
J();
15+
K();
16+
L();
17+
}

debug_ranks.go

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
package main
2+
3+
import "fmt"
4+
5+
func main() {
6+
order := []string{
7+
"signature",
8+
"call-signature",
9+
"public-static-field",
10+
"protected-static-field",
11+
"private-static-field",
12+
"#private-static-field",
13+
"public-decorated-field",
14+
"protected-decorated-field",
15+
"private-decorated-field",
16+
"public-instance-field",
17+
"protected-instance-field",
18+
"private-instance-field",
19+
"#private-instance-field",
20+
"public-abstract-field",
21+
"protected-abstract-field",
22+
"public-field",
23+
"protected-field",
24+
"private-field",
25+
"#private-field",
26+
"static-field",
27+
"instance-field",
28+
"abstract-field",
29+
"decorated-field",
30+
"field",
31+
"static-initialization",
32+
"public-constructor",
33+
"protected-constructor",
34+
"private-constructor",
35+
"constructor",
36+
}
37+
for i, item := range order {
38+
if item == "signature" {
39+
fmt.Printf("signature: %d\n", i)
40+
}
41+
if item == "field" {
42+
fmt.Printf("field: %d\n", i)
43+
}
44+
if item == "constructor" {
45+
fmt.Printf("constructor: %d\n", i)
46+
}
47+
}
48+
}

internal/api/api.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -60,8 +60,8 @@ type LanguageOptions struct {
6060

6161
// ParserOptions represents TypeScript parser options
6262
type ParserOptions struct {
63-
Project string `json:"project,omitempty"`
64-
ProjectService bool `json:"projectService,omitempty"`
63+
Project interface{} `json:"project,omitempty"` // Can be string or []string
64+
ProjectService bool `json:"projectService,omitempty"`
6565
}
6666

6767
// LintRequest represents a lint request from JS to Go

minimal-debug.mjs

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
import { lint } from './packages/rslint/dist/index.js';
2+
3+
// Test the invalid case that should produce errors using VIRTUAL FILES like the rule tester
4+
const invalidResult = await lint({
5+
config: '/Users/bytedance/dev/rslint/packages/rslint-test-tools/tests/typescript-eslint/fixtures/rslint.json',
6+
workingDirectory: '/Users/bytedance/dev/rslint/packages/rslint-test-tools/tests/typescript-eslint/fixtures',
7+
fileContents: {
8+
'src/virtual.ts': `class Foo {
9+
// This should be out of order - method before field
10+
doSomething() {}
11+
name: string = '';
12+
}`
13+
},
14+
ruleOptions: {
15+
'@typescript-eslint/member-ordering': []
16+
},
17+
languageOptions: {
18+
parserOptions: {
19+
project: './tsconfig.virtual.json',
20+
tsconfigRootDir: '/Users/bytedance/dev/rslint/packages/rslint-test-tools/tests/typescript-eslint/fixtures'
21+
}
22+
}
23+
});
24+
25+
console.log('Invalid test result:');
26+
console.log('- Error count:', invalidResult.errorCount);
27+
console.log('- Rule count:', invalidResult.ruleCount);
28+
console.log('- File count:', invalidResult.fileCount);
29+
console.log('- Diagnostics:', invalidResult.diagnostics.length);
30+
31+
if (invalidResult.diagnostics.length > 0) {
32+
console.log('\nDiagnostics:');
33+
for (let i = 0; i < invalidResult.diagnostics.length; i++) {
34+
const d = invalidResult.diagnostics[i];
35+
console.log(` ${i+1}. Line ${d.range.start.line}: ${d.message}`);
36+
}
37+
}
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
class Foo {
2+
// This should be out of order - method before field
3+
doSomething() {}
4+
name: string = '';
5+
}

packages/rslint-test-tools/tests/typescript-eslint/fixtures/tsconfig.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
"react.tsx",
2121
"var-declaration.ts",
2222
"errors.ts",
23-
"switch-exhaustiveness-check.ts"
23+
"switch-exhaustiveness-check.ts",
24+
"debug-test.ts"
2425
]
2526
}

0 commit comments

Comments
 (0)