Skip to content

Commit 52cefe0

Browse files
authored
test: support inline languageOptions (#300)
1 parent 15c077d commit 52cefe0

File tree

9 files changed

+277
-611
lines changed

9 files changed

+277
-611
lines changed

cmd/rslint/api.go

Lines changed: 32 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -62,8 +62,38 @@ func (h *IPCHandler) HandleLint(req api.LintRequest) (*api.LintResponse, error)
6262
rslintconfig.RegisterAllRules()
6363

6464
// Load rslint configuration and determine which tsconfig files to use
65-
_, tsConfigs, configDirectory := rslintconfig.LoadConfigurationWithFallback(req.Config, currentDirectory, fs)
65+
rslintConfig, tsConfigs, configDirectory := rslintconfig.LoadConfigurationWithFallback(req.Config, currentDirectory, fs)
66+
67+
// Merge languageOptions from request with config file if provided
68+
if req.LanguageOptions != nil && len(rslintConfig) > 0 {
69+
// Convert API LanguageOptions to config LanguageOptions
70+
configLanguageOptions := &rslintconfig.LanguageOptions{}
71+
if req.LanguageOptions.ParserOptions != nil {
72+
configLanguageOptions.ParserOptions = &rslintconfig.ParserOptions{
73+
ProjectService: req.LanguageOptions.ParserOptions.ProjectService,
74+
Project: rslintconfig.ProjectPaths(req.LanguageOptions.ParserOptions.Project),
75+
}
76+
}
6677

78+
// Override languageOptions for the first config entry
79+
rslintConfig[0].LanguageOptions = configLanguageOptions
80+
81+
// Re-extract tsconfig files with updated languageOptions
82+
overrideTsconfigs := []string{}
83+
for _, entry := range rslintConfig {
84+
if entry.LanguageOptions != nil && entry.LanguageOptions.ParserOptions != nil {
85+
for _, config := range entry.LanguageOptions.ParserOptions.Project {
86+
tsconfigPath := tspath.ResolvePath(configDirectory, config)
87+
if fs.FileExists(tsconfigPath) {
88+
overrideTsconfigs = append(overrideTsconfigs, tsconfigPath)
89+
}
90+
}
91+
}
92+
}
93+
if len(overrideTsconfigs) > 0 {
94+
tsConfigs = overrideTsconfigs
95+
}
96+
}
6797
type RuleWithOption struct {
6898
rule rule.Rule
6999
option interface{}
@@ -232,7 +262,7 @@ func (h *IPCHandler) HandleApplyFixes(req api.ApplyFixesRequest) (*api.ApplyFixe
232262

233263
// Apply fixes iteratively to handle overlapping fixes
234264
for {
235-
fixedContent,unapplied, fixed := linter.ApplyRuleFixes(code, ruleDiagnostics)
265+
fixedContent, unapplied, fixed := linter.ApplyRuleFixes(code, ruleDiagnostics)
236266
if !fixed {
237267
break
238268
}

internal/api/api.go

Lines changed: 39 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -57,13 +57,46 @@ type HandshakeResponse struct {
5757

5858
// LintRequest represents a lint request from JS to Go
5959
type LintRequest struct {
60-
Files []string `json:"files,omitempty"`
61-
Config string `json:"config,omitempty"` // Path to rslint.json config file
62-
Format string `json:"format,omitempty"`
63-
WorkingDirectory string `json:"workingDirectory,omitempty"`
60+
Files []string `json:"files,omitempty"`
61+
Config string `json:"config,omitempty"` // Path to rslint.json config file
62+
Format string `json:"format,omitempty"`
63+
WorkingDirectory string `json:"workingDirectory,omitempty"`
6464
// Supports both string level and array [level, options] format
65-
RuleOptions map[string]interface{} `json:"ruleOptions,omitempty"`
66-
FileContents map[string]string `json:"fileContents,omitempty"` // Map of file paths to their contents for VFS
65+
RuleOptions map[string]interface{} `json:"ruleOptions,omitempty"`
66+
FileContents map[string]string `json:"fileContents,omitempty"` // Map of file paths to their contents for VFS
67+
LanguageOptions *LanguageOptions `json:"languageOptions,omitempty"` // Override languageOptions from config file
68+
}
69+
70+
// LanguageOptions contains language-specific configuration options
71+
type LanguageOptions struct {
72+
ParserOptions *ParserOptions `json:"parserOptions,omitempty"`
73+
}
74+
75+
// ProjectPaths represents project paths that can be either a single string or an array of strings
76+
type ProjectPaths []string
77+
78+
// UnmarshalJSON implements custom JSON unmarshaling to support both string and string[] formats
79+
func (p *ProjectPaths) UnmarshalJSON(data []byte) error {
80+
// Try to unmarshal as string first
81+
var singlePath string
82+
if err := json.Unmarshal(data, &singlePath); err == nil {
83+
*p = []string{singlePath}
84+
return nil
85+
}
86+
87+
// If that fails, try to unmarshal as array of strings
88+
var paths []string
89+
if err := json.Unmarshal(data, &paths); err != nil {
90+
return err
91+
}
92+
*p = paths
93+
return nil
94+
}
95+
96+
// ParserOptions contains parser-specific configuration
97+
type ParserOptions struct {
98+
ProjectService bool `json:"projectService"`
99+
Project ProjectPaths `json:"project,omitempty"`
67100
}
68101

69102
// LintResponse represents a lint response from Go to JS

internal/api/api_test.go

Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
package ipc
2+
3+
import (
4+
"encoding/json"
5+
"testing"
6+
)
7+
8+
func TestAPIProjectPathsUnmarshalJSON(t *testing.T) {
9+
tests := []struct {
10+
name string
11+
input string
12+
expected []string
13+
}{
14+
{
15+
name: "single string",
16+
input: `"tsconfig.json"`,
17+
expected: []string{"tsconfig.json"},
18+
},
19+
{
20+
name: "array of strings",
21+
input: `["tsconfig.json", "packages/*/tsconfig.json"]`,
22+
expected: []string{"tsconfig.json", "packages/*/tsconfig.json"},
23+
},
24+
{
25+
name: "empty array",
26+
input: `[]`,
27+
expected: []string{},
28+
},
29+
}
30+
31+
for _, tt := range tests {
32+
t.Run(tt.name, func(t *testing.T) {
33+
var paths ProjectPaths
34+
err := json.Unmarshal([]byte(tt.input), &paths)
35+
if err != nil {
36+
t.Fatalf("Failed to unmarshal: %v", err)
37+
}
38+
39+
if len(paths) != len(tt.expected) {
40+
t.Errorf("Expected length %d, got %d", len(tt.expected), len(paths))
41+
}
42+
43+
for i, expected := range tt.expected {
44+
if i >= len(paths) {
45+
t.Errorf("Expected %s at index %d, but paths is too short", expected, i)
46+
continue
47+
}
48+
if paths[i] != expected {
49+
t.Errorf("Expected %s at index %d, got %s", expected, i, paths[i])
50+
}
51+
}
52+
})
53+
}
54+
}
55+
56+
func TestAPIParserOptionsUnmarshalJSON(t *testing.T) {
57+
tests := []struct {
58+
name string
59+
input string
60+
expected ProjectPaths
61+
}{
62+
{
63+
name: "single project string",
64+
input: `{"projectService": false, "project": "tsconfig.json"}`,
65+
expected: ProjectPaths{"tsconfig.json"},
66+
},
67+
{
68+
name: "multiple project strings",
69+
input: `{"projectService": false, "project": ["tsconfig.json", "packages/*/tsconfig.json"]}`,
70+
expected: ProjectPaths{"tsconfig.json", "packages/*/tsconfig.json"},
71+
},
72+
{
73+
name: "no project field",
74+
input: `{"projectService": false}`,
75+
expected: ProjectPaths{},
76+
},
77+
}
78+
79+
for _, tt := range tests {
80+
t.Run(tt.name, func(t *testing.T) {
81+
var opts ParserOptions
82+
err := json.Unmarshal([]byte(tt.input), &opts)
83+
if err != nil {
84+
t.Fatalf("Failed to unmarshal: %v", err)
85+
}
86+
87+
if len(opts.Project) != len(tt.expected) {
88+
t.Errorf("Expected project length %d, got %d", len(tt.expected), len(opts.Project))
89+
}
90+
91+
for i, expected := range tt.expected {
92+
if i >= len(opts.Project) {
93+
t.Errorf("Expected %s at index %d, but project is too short", expected, i)
94+
continue
95+
}
96+
if opts.Project[i] != expected {
97+
t.Errorf("Expected %s at index %d, got %s", expected, i, opts.Project[i])
98+
}
99+
}
100+
})
101+
}
102+
}

internal/config/config.go

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package config
22

33
import (
4+
"encoding/json"
45
"fmt"
56
"os"
67
"path/filepath"
@@ -82,12 +83,34 @@ type LanguageOptions struct {
8283
ParserOptions *ParserOptions `json:"parserOptions,omitempty"`
8384
}
8485

86+
// ProjectPaths represents project paths that can be either a single string or an array of strings
87+
type ProjectPaths []string
88+
89+
// UnmarshalJSON implements custom JSON unmarshaling to support both string and string[] formats
90+
func (p *ProjectPaths) UnmarshalJSON(data []byte) error {
91+
// Try to unmarshal as string first
92+
var singlePath string
93+
if err := json.Unmarshal(data, &singlePath); err == nil {
94+
*p = []string{singlePath}
95+
return nil
96+
}
97+
98+
// If that fails, try to unmarshal as array of strings
99+
var paths []string
100+
if err := json.Unmarshal(data, &paths); err != nil {
101+
return err
102+
}
103+
*p = paths
104+
return nil
105+
}
106+
85107
// ParserOptions contains parser-specific configuration
86108
type ParserOptions struct {
87-
ProjectService bool `json:"projectService"`
88-
Project []string `json:"project,omitempty"`
109+
ProjectService bool `json:"projectService"`
110+
Project ProjectPaths `json:"project,omitempty"`
89111
}
90112

113+
91114
// Rules represents the rules configuration
92115
// This can be extended to include specific rule configurations
93116
type Rules map[string]interface{}

0 commit comments

Comments
 (0)