You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: docs/DEV.md
+299Lines changed: 299 additions & 0 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -307,6 +307,305 @@ You can simply copy and paste compliant and non-compliant examples from your RSP
307
307
- Use comment-based tests with `package.json` dependencies dependent rule: [PR](<[TBD](https://github.com/SonarSource/SonarJS/pull/4443/files#diff-92d7c68b7e4cc945d0f128acbd458648eb8021903587c1ee7025243f2fae89d2)>)
308
308
- Use ESLint's Rule tester with `package.json` dependencies dependent rule: [PR](<[TBD](https://github.com/SonarSource/SonarJS/commit/dc9435738093286869edff742c90d17d74e39b1c#diff-55f5136cfbed4170ed04f718f78f46015d6bb1f78c26403e036136211a333425R154-R213)>)
309
309
310
+
## Rule Options Architecture
311
+
312
+
This section explains how rule options (configurations) work across the SonarJS stack.
313
+
314
+
### Overview
315
+
316
+
There are two parallel workflows for requesting JS/TS analysis from Node.js:
317
+
318
+
**1. SonarQube workflow (HTTP bridge via WebSocket):**
The key difference is that SonarQube's Java side sends already-typed values via `configurations()`, while the gRPC endpoint receives string key-value pairs that need type parsing.
337
+
338
+
Each rule can have configurable options defined in several places that serve different purposes.
339
+
340
+
### File Structure for a Rule
341
+
342
+
Each rule lives in `packages/jsts/src/rules/SXXXX/` with these key files:
The `implementation` field in `meta.ts` determines how a rule is structured:
354
+
355
+
#### `original`
356
+
357
+
Rules written from scratch for SonarJS. If the rule accepts options, it defines its own JSON Schema in `meta.ts`. Rules without options don't need a schema or config.ts:
Rules that directly use an ESLint rule without modification. The schema is inherited from the external rule at runtime. Some external rules expose user-configurable options via `config.ts` (e.g., S103, S139, S1441):
399
+
400
+
```typescript
401
+
// S106/meta.ts
402
+
exportconst implementation ='external';
403
+
exportconst eslintId ='no-console';
404
+
exportconst externalPlugin ='eslint';
405
+
export*from'./config.js';
406
+
```
407
+
408
+
### The `fields` Array (`config.ts`)
409
+
410
+
The `fields` array is the **source of truth** for rule options. It defines:
411
+
412
+
- ESLint field names
413
+
- Default values (which determine types)
414
+
- SonarQube UI descriptions
415
+
- Key mappings when SQ and ESLint names differ
416
+
417
+
```typescript
418
+
// S107/config.ts
419
+
exportconst fields = [
420
+
[
421
+
{
422
+
field: 'max', // ESLint option name
423
+
displayName: 'maximumFunctionParameters', // SonarQube UI name (optional)
424
+
description: 'Maximum authorized...', // Shows in SQ UI
**Important:** The schema is for ESLint validation. The `fields` array provides default values and metadata for SonarQube integration. For `original` rules with options, both schema and fields must be kept in sync manually. For `decorated`/`external` rules, the schema is inherited from the external rule at runtime.
506
+
507
+
### `defaultOptions` in `generated-meta.ts`
508
+
509
+
The `npm run generate-meta` script reads `fields` and generates `defaultOptions`:
510
+
511
+
```typescript
512
+
// generated-meta.ts (auto-generated)
513
+
exportconst meta = {
514
+
// ...
515
+
defaultOptions: [
516
+
{ format: '^[_a-z][a-zA-Z0-9]*$' }, // From fields[0][0].default
517
+
],
518
+
};
519
+
```
520
+
521
+
This is extracted using the `defaultOptions()` helper from `helpers/configs.ts`.
0 commit comments