Skip to content

Commit 24b0bc1

Browse files
feat(linter): add languageOptions support for TypeScript parser
Add languageOptions configuration to lint requests to support custom TypeScript parser options Remove special case handling for dot-notation rule as it's now handled by languageOptions
1 parent bc4ac1c commit 24b0bc1

File tree

4 files changed

+61
-54
lines changed

4 files changed

+61
-54
lines changed

cmd/rslint/api.go

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -107,7 +107,21 @@ func (h *IPCHandler) HandleLint(req api.LintRequest) (*api.LintResponse, error)
107107
rslintconfig.RegisterAllTypeScriptEslintPluginRules()
108108

109109
// Load rslint configuration and determine which tsconfig files to use
110-
_, tsConfigs, configDirectory := rslintconfig.LoadConfigurationWithFallback(req.Config, currentDirectory, fs)
110+
var tsConfigs []string
111+
var configDirectory string
112+
113+
if req.LanguageOptions != nil && req.LanguageOptions.ParserOptions != nil && req.LanguageOptions.ParserOptions.Project != "" {
114+
// Use project from languageOptions
115+
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)
119+
}
120+
tsConfigs = []string{projectPath}
121+
} else {
122+
// Use default configuration loading
123+
_, tsConfigs, configDirectory = rslintconfig.LoadConfigurationWithFallback(req.Config, currentDirectory, fs)
124+
}
111125

112126
// Create rules
113127
var origin_rules = []rule.Rule{

internal/api/api.go

Lines changed: 18 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -53,15 +53,27 @@ type HandshakeResponse struct {
5353
OK bool `json:"ok"`
5454
}
5555

56+
// LanguageOptions represents language-specific parser options
57+
type LanguageOptions struct {
58+
ParserOptions *ParserOptions `json:"parserOptions,omitempty"`
59+
}
60+
61+
// ParserOptions represents TypeScript parser options
62+
type ParserOptions struct {
63+
Project string `json:"project,omitempty"`
64+
ProjectService bool `json:"projectService,omitempty"`
65+
}
66+
5667
// LintRequest represents a lint request from JS to Go
5768
type LintRequest struct {
58-
Files []string `json:"files,omitempty"`
59-
Config string `json:"config,omitempty"` // Path to rslint.json config file
60-
Format string `json:"format,omitempty"`
61-
WorkingDirectory string `json:"workingDirectory,omitempty"`
69+
Files []string `json:"files,omitempty"`
70+
Config string `json:"config,omitempty"` // Path to rslint.json config file
71+
Format string `json:"format,omitempty"`
72+
WorkingDirectory string `json:"workingDirectory,omitempty"`
6273
// Supports both string level and array [level, options] format
63-
RuleOptions map[string]interface{} `json:"ruleOptions,omitempty"`
64-
FileContents map[string]string `json:"fileContents,omitempty"` // Map of file paths to their contents for VFS
74+
RuleOptions map[string]interface{} `json:"ruleOptions,omitempty"`
75+
FileContents map[string]string `json:"fileContents,omitempty"` // Map of file paths to their contents for VFS
76+
LanguageOptions *LanguageOptions `json:"languageOptions,omitempty"`
6577
}
6678

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

packages/rslint/src/service.ts

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,12 @@ export interface LintOptions {
3838
workingDirectory?: string;
3939
ruleOptions?: Record<string, string>;
4040
fileContents?: Record<string, string>; // Map of file paths to their contents for VFS
41+
languageOptions?: {
42+
parserOptions?: {
43+
project?: string;
44+
projectService?: boolean;
45+
};
46+
};
4147
}
4248

4349
interface RSlintOptions {
@@ -177,8 +183,14 @@ export class RSLintService {
177183
* Run the linter on specified files
178184
*/
179185
async lint(options: LintOptions = {}): Promise<LintResponse> {
180-
const { files, config, workingDirectory, ruleOptions, fileContents } =
181-
options;
186+
const {
187+
files,
188+
config,
189+
workingDirectory,
190+
ruleOptions,
191+
fileContents,
192+
languageOptions,
193+
} = options;
182194
// Send handshake
183195
await this.sendMessage('handshake', { version: '1.0.0' });
184196

@@ -189,6 +201,7 @@ export class RSLintService {
189201
workingDirectory,
190202
ruleOptions,
191203
fileContents,
204+
languageOptions,
192205
format: 'jsonline',
193206
});
194207
}

packages/rule-tester/src/index.ts

Lines changed: 13 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -177,30 +177,8 @@ export class RuleTester {
177177
? false
178178
: validCase.languageOptions?.parserOptions?.ecmaFeatures?.jsx;
179179

180-
let options =
180+
const options =
181181
typeof validCase === 'string' ? [] : validCase.options || [];
182-
183-
// Handle special case for dot-notation rule with noPropertyAccessFromIndexSignature tsconfig
184-
if (
185-
ruleName === '@typescript-eslint/dot-notation' &&
186-
typeof validCase === 'object' &&
187-
validCase.languageOptions?.parserOptions?.project ===
188-
'./tsconfig.noPropertyAccessFromIndexSignature.json'
189-
) {
190-
// Simulate noPropertyAccessFromIndexSignature: true by setting allowIndexSignaturePropertyAccess: true
191-
if (Array.isArray(options) && options.length === 0) {
192-
options = [{ allowIndexSignaturePropertyAccess: true }];
193-
} else if (
194-
Array.isArray(options) &&
195-
options.length > 0 &&
196-
typeof options[0] === 'object'
197-
) {
198-
options[0] = {
199-
...options[0],
200-
allowIndexSignaturePropertyAccess: true,
201-
};
202-
}
203-
}
204182
let virtual_entry = path.resolve(
205183
cwd,
206184
isJSX ? 'src/virtual.tsx' : 'src/virtual.ts',
@@ -226,6 +204,10 @@ export class RuleTester {
226204
ruleOptions: {
227205
[ruleName]: options,
228206
},
207+
languageOptions:
208+
typeof validCase === 'object'
209+
? validCase.languageOptions
210+
: undefined,
229211
});
230212

231213
assert(
@@ -236,28 +218,13 @@ export class RuleTester {
236218
});
237219
test('invalid', async t => {
238220
for (const item of cases.invalid) {
239-
let { code, errors, only = false, skip = false, options = [] } = item;
240-
241-
// Handle special case for dot-notation rule with noPropertyAccessFromIndexSignature tsconfig
242-
if (
243-
ruleName === '@typescript-eslint/dot-notation' &&
244-
item.languageOptions?.parserOptions?.project ===
245-
'./tsconfig.noPropertyAccessFromIndexSignature.json'
246-
) {
247-
// Simulate noPropertyAccessFromIndexSignature: true by setting allowIndexSignaturePropertyAccess: true
248-
if (Array.isArray(options) && options.length === 0) {
249-
options = [{ allowIndexSignaturePropertyAccess: true }];
250-
} else if (
251-
Array.isArray(options) &&
252-
options.length > 0 &&
253-
typeof options[0] === 'object'
254-
) {
255-
options[0] = {
256-
...options[0],
257-
allowIndexSignaturePropertyAccess: true,
258-
};
259-
}
260-
}
221+
const {
222+
code,
223+
errors,
224+
only = false,
225+
skip = false,
226+
options = [],
227+
} = item;
261228
if (skip) {
262229
continue;
263230
}
@@ -278,6 +245,7 @@ export class RuleTester {
278245
ruleOptions: {
279246
[ruleName]: options,
280247
},
248+
languageOptions: item.languageOptions,
281249
});
282250

283251
expect(diags).toMatchSnapshot();

0 commit comments

Comments
 (0)