Skip to content

Commit 81cb787

Browse files
committed
feat: add type-formatting rule
1 parent 93e6b1c commit 81cb787

File tree

9 files changed

+1233
-4
lines changed

9 files changed

+1233
-4
lines changed

.README/rules/type-formatting.md

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
# `type-formatting`
2+
3+
Formats JSDoc type values.
4+
5+
Note that this rule should be considered **experimental**. The stringification
6+
might not preserve other aspects of your original formatting and there could be
7+
errors.
8+
9+
Currently offers the following options for formatting types.
10+
11+
## Options
12+
13+
### `arrayBrackets`
14+
15+
Determines how array generics are represented. Set to `angle` for the style `Array<type>` or `square` for the style `type[]`. Defaults to "square".
16+
17+
### `enableFixer`
18+
19+
Whether to enable the fixer. Defaults to `true`.
20+
21+
### `genericDot`
22+
23+
Boolean value of whether to use a dot before the angled brackets of a generic (e.g., `SomeType.<AnotherType>`). Defaults to `false`.
24+
25+
### `objectFieldQuote`
26+
27+
Whether and how object field properties should be quoted (e.g., `{"a": string}`).
28+
Set to `single`, `double`, or `null`. Defaults to `null` (no quotes unless
29+
required due to whitespace within the field).
30+
31+
### `propertyQuotes`
32+
33+
Whether and how namepath properties should be quoted (e.g., `ab."cd"."ef"`).
34+
Set to `single`, `double`, or `null`. Defaults to `null` (no quotes unless
35+
required due to whitespace within the property).
36+
37+
### stringQuotes
38+
39+
How string literals should be quoted (e.g., `"abc"`). Set to `single`
40+
or `double`. Defaults to 'single'.
41+
42+
### `objectFieldSeparator`
43+
44+
For object properties, specify whether a "semicolon", "comma", "linebreak",
45+
"semicolon-and-linebreak", or "comma-and-linebreak" should be used after
46+
each object property-value pair.
47+
48+
Defaults to `null` which is equivalent to "semicolon".
49+
50+
### `objectFieldIndent`
51+
52+
Indicates the whitespace to be added on each line preceding an object
53+
property-value field. Defaults to the empty string.
54+
55+
### `separatorForSingleObjectField`
56+
57+
Whether to apply the `objectFieldSeparator` when there is only one
58+
property-value object field present. Defaults to `false`.
59+
60+
|||
61+
|---|---|
62+
|Context|everywhere|
63+
|Tags|``|
64+
|Recommended|false|
65+
|Settings||
66+
|Options|`arrayBrackets`, `enableFixer`, `genericDot`, `objectFieldIndent`, `objectFieldQuote`, `objectFieldSeparator`, `propertyQuotes`, `separatorForSingleObjectField`, `stringQuotes`|
67+
68+
## Failing examples
69+
70+
<!-- assertions-failing typeFormatting -->
71+
72+
## Passing examples
73+
74+
<!-- assertions-passing typeFormatting -->

docs/rules/type-formatting.md

Lines changed: 258 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,258 @@
1+
<a name="user-content-type-formatting"></a>
2+
<a name="type-formatting"></a>
3+
# <code>type-formatting</code>
4+
5+
Formats JSDoc type values.
6+
7+
Note that this rule should be considered **experimental**. The stringification
8+
might not preserve other aspects of your original formatting and there could be
9+
errors.
10+
11+
Currently offers the following options for formatting types.
12+
13+
<a name="user-content-type-formatting-options"></a>
14+
<a name="type-formatting-options"></a>
15+
## Options
16+
17+
<a name="user-content-type-formatting-options-arraybrackets"></a>
18+
<a name="type-formatting-options-arraybrackets"></a>
19+
### <code>arrayBrackets</code>
20+
21+
Determines how array generics are represented. Set to `angle` for the style `Array<type>` or `square` for the style `type[]`. Defaults to "square".
22+
23+
<a name="user-content-type-formatting-options-enablefixer"></a>
24+
<a name="type-formatting-options-enablefixer"></a>
25+
### <code>enableFixer</code>
26+
27+
Whether to enable the fixer. Defaults to `true`.
28+
29+
<a name="user-content-type-formatting-options-genericdot"></a>
30+
<a name="type-formatting-options-genericdot"></a>
31+
### <code>genericDot</code>
32+
33+
Boolean value of whether to use a dot before the angled brackets of a generic (e.g., `SomeType.<AnotherType>`). Defaults to `false`.
34+
35+
<a name="user-content-type-formatting-options-objectfieldquote"></a>
36+
<a name="type-formatting-options-objectfieldquote"></a>
37+
### <code>objectFieldQuote</code>
38+
39+
Whether and how object field properties should be quoted (e.g., `{"a": string}`).
40+
Set to `single`, `double`, or `null`. Defaults to `null` (no quotes unless
41+
required due to whitespace within the field).
42+
43+
<a name="user-content-type-formatting-options-propertyquotes"></a>
44+
<a name="type-formatting-options-propertyquotes"></a>
45+
### <code>propertyQuotes</code>
46+
47+
Whether and how namepath properties should be quoted (e.g., `ab."cd"."ef"`).
48+
Set to `single`, `double`, or `null`. Defaults to `null` (no quotes unless
49+
required due to whitespace within the property).
50+
51+
<a name="user-content-type-formatting-options-stringquotes"></a>
52+
<a name="type-formatting-options-stringquotes"></a>
53+
### stringQuotes
54+
55+
How string literals should be quoted (e.g., `"abc"`). Set to `single`
56+
or `double`. Defaults to 'single'.
57+
58+
<a name="user-content-type-formatting-options-objectfieldseparator"></a>
59+
<a name="type-formatting-options-objectfieldseparator"></a>
60+
### <code>objectFieldSeparator</code>
61+
62+
For object properties, specify whether a "semicolon", "comma", "linebreak",
63+
"semicolon-and-linebreak", or "comma-and-linebreak" should be used after
64+
each object property-value pair.
65+
66+
Defaults to `null` which is equivalent to "semicolon".
67+
68+
<a name="user-content-type-formatting-options-objectfieldindent"></a>
69+
<a name="type-formatting-options-objectfieldindent"></a>
70+
### <code>objectFieldIndent</code>
71+
72+
Indicates the whitespace to be added on each line preceding an object
73+
property-value field. Defaults to the empty string.
74+
75+
<a name="user-content-type-formatting-options-separatorforsingleobjectfield"></a>
76+
<a name="type-formatting-options-separatorforsingleobjectfield"></a>
77+
### <code>separatorForSingleObjectField</code>
78+
79+
Whether to apply the `objectFieldSeparator` when there is only one
80+
property-value object field present. Defaults to `false`.
81+
82+
|||
83+
|---|---|
84+
|Context|everywhere|
85+
|Tags|``|
86+
|Recommended|false|
87+
|Settings||
88+
|Options|`arrayBrackets`, `enableFixer`, `genericDot`, `objectFieldIndent`, `objectFieldQuote`, `objectFieldSeparator`, `propertyQuotes`, `separatorForSingleObjectField`, `stringQuotes`|
89+
90+
<a name="user-content-type-formatting-failing-examples"></a>
91+
<a name="type-formatting-failing-examples"></a>
92+
## Failing examples
93+
94+
The following patterns are considered problems:
95+
96+
````ts
97+
/**
98+
* @param {{a: string; b: number; c: boolean,}} cfg
99+
*/
100+
// "jsdoc/type-formatting": ["error"|"warn", {"objectFieldSeparator":"semicolon"}]
101+
// Message: Inconsistent semicolon usage
102+
103+
/**
104+
* @param {{a: string; b: number; c: boolean,}} cfg
105+
*/
106+
// Settings: {"jsdoc":{"mode":"permissive"}}
107+
// "jsdoc/type-formatting": ["error"|"warn", {"objectFieldSeparator":"semicolon"}]
108+
// Message: Inconsistent semicolon usage
109+
110+
/**
111+
* @param {{a: string; b: number; c: boolean,}} cfg
112+
*/
113+
// "jsdoc/type-formatting": ["error"|"warn", {"enableFixer":false,"objectFieldSeparator":"semicolon"}]
114+
// Message: Inconsistent semicolon usage
115+
116+
/**
117+
* @param {{a: string, b: number; c: boolean;}} cfg
118+
*/
119+
// "jsdoc/type-formatting": ["error"|"warn", {"objectFieldSeparator":"comma"}]
120+
// Message: Inconsistent comma usage
121+
122+
/**
123+
* @param {{a: string, b: number; c: boolean,}} cfg
124+
*/
125+
// "jsdoc/type-formatting": ["error"|"warn", {"objectFieldIndent":" ","objectFieldSeparator":"linebreak"}]
126+
// Message: Inconsistent linebreak usage
127+
128+
/**
129+
* @param {{
130+
* a: string,
131+
* b: number;
132+
* c: boolean,
133+
* }} cfg
134+
*/
135+
// "jsdoc/type-formatting": ["error"|"warn", {"objectFieldIndent":" ","objectFieldSeparator":"linebreak"}]
136+
// Message: Inconsistent linebreak usage
137+
138+
/**
139+
* @param {'abc'} cfg
140+
*/
141+
// "jsdoc/type-formatting": ["error"|"warn", {"stringQuotes":"double"}]
142+
// Message: Inconsistent double string quotes usage
143+
144+
/**
145+
* @param {Array<string>} cfg
146+
*/
147+
// "jsdoc/type-formatting": ["error"|"warn", {"arrayBrackets":"square"}]
148+
// Message: Array bracket style should be square
149+
150+
/**
151+
* @param {SomeType<string>} cfg
152+
*/
153+
// "jsdoc/type-formatting": ["error"|"warn", {"genericDot":true}]
154+
// Message: Dot usage should be true
155+
156+
/**
157+
* @param {{a: string}} cfg
158+
*/
159+
// "jsdoc/type-formatting": ["error"|"warn", {"objectFieldQuote":"double"}]
160+
// Message: Inconsistent object field quotes double
161+
162+
/**
163+
* @param {ab.cd.ef} cfg
164+
*/
165+
// "jsdoc/type-formatting": ["error"|"warn", {"propertyQuotes":"double"}]
166+
// Message: Inconsistent double property quotes usage
167+
168+
/**
169+
* @param {{a: string}} cfg A long
170+
* description
171+
*/
172+
// "jsdoc/type-formatting": ["error"|"warn", {"objectFieldIndent":" ","objectFieldSeparator":"semicolon","separatorForSingleObjectField":true}]
173+
// Message: Inconsistent semicolon usage
174+
175+
/** @param {{a: string, b: number; c: boolean,}} cfg */
176+
// "jsdoc/type-formatting": ["error"|"warn", {"objectFieldIndent":" ","objectFieldSeparator":"linebreak"}]
177+
// Message: Inconsistent linebreak usage
178+
179+
/**
180+
* @param {{a: string, b: number; c: boolean,}} cfg */
181+
// "jsdoc/type-formatting": ["error"|"warn", {"objectFieldIndent":" ","objectFieldSeparator":"linebreak"}]
182+
// Message: Inconsistent linebreak usage
183+
184+
/** @param {{a: string, b: number; c: boolean,}} cfg
185+
*/
186+
// "jsdoc/type-formatting": ["error"|"warn", {"objectFieldIndent":" ","objectFieldSeparator":"linebreak"}]
187+
// Message: Inconsistent linebreak usage
188+
189+
/**
190+
* @param {{
191+
* a: string,
192+
* b: number
193+
* }} cfg A long
194+
* description
195+
*/
196+
// "jsdoc/type-formatting": ["error"|"warn", {"objectFieldIndent":" ","objectFieldSeparator":"semicolon-and-linebreak"}]
197+
// Message: Inconsistent semicolon-and-linebreak usage
198+
````
199+
200+
201+
202+
<a name="user-content-type-formatting-passing-examples"></a>
203+
<a name="type-formatting-passing-examples"></a>
204+
## Passing examples
205+
206+
The following patterns are not considered problems:
207+
208+
````ts
209+
/**
210+
* @param {{a: string; b: number; c: boolean}} cfg
211+
*/
212+
// "jsdoc/type-formatting": ["error"|"warn", {"objectFieldSeparator":"semicolon"}]
213+
214+
/**
215+
* @param {"abc"} cfg
216+
*/
217+
// "jsdoc/type-formatting": ["error"|"warn", {"stringQuotes":"double"}]
218+
219+
/**
220+
* @param {string[]} cfg
221+
*/
222+
// "jsdoc/type-formatting": ["error"|"warn", {"arrayBrackets":"square"}]
223+
224+
/**
225+
* @param {SomeType.<string>} cfg
226+
*/
227+
// "jsdoc/type-formatting": ["error"|"warn", {"genericDot":true}]
228+
229+
/**
230+
* Due to jsdoc-type-pratt-parser representing the separator at the
231+
* object level, this will not be reported.
232+
* @param {{a: string, b: number; c: boolean,}} cfg
233+
*/
234+
// "jsdoc/type-formatting": ["error"|"warn", {"objectFieldSeparator":"comma"}]
235+
236+
/**
237+
* @param {A<} badParam
238+
*/
239+
240+
/**
241+
* @param {{"a bc": string}} quotedKeyParam
242+
*/
243+
244+
/**
245+
* @param {ab.cd.ef} cfg
246+
*/
247+
// "jsdoc/type-formatting": ["error"|"warn", {"propertyQuotes":null}]
248+
249+
/**
250+
* @param {ab."cd ef".gh} cfg
251+
*/
252+
// "jsdoc/type-formatting": ["error"|"warn", {"propertyQuotes":null}]
253+
254+
/**
255+
* @param cfg
256+
*/
257+
````
258+

src/bin/generateRule.js

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -250,21 +250,21 @@ export default iterateJsdoc(({
250250
newLine: `import ${camelCasedRuleName} from './rules/${camelCasedRuleName}.js';`,
251251
oldIsCamel: true,
252252
oldRegex: /\nimport (?<oldRule>[^ ]*) from '.\/rules\/\1\.js';/gv,
253-
path: './src/index.js',
253+
path: './src/index-cjs.js',
254254
});
255255

256256
await replaceInOrder({
257257
checkName: 'index recommended',
258258
newLine: `${' '.repeat(6)}'jsdoc/${ruleName}': ${recommended ? 'warnOrError' : '\'off\''},`,
259259
oldRegex: /\n\s{6}'jsdoc\/(?<oldRule>[^']*)': .*?,/gv,
260-
path: './src/index.js',
260+
path: './src/index-cjs.js',
261261
});
262262

263263
await replaceInOrder({
264264
checkName: 'index rules',
265-
newLine: `${' '.repeat(4)}'${ruleName}': ${camelCasedRuleName},`,
265+
newLine: `${' '.repeat(2)}'${ruleName}': ${camelCasedRuleName},`,
266266
oldRegex: /\n\s{2}'(?<oldRule>[^']*)': [^,]*,/gv,
267-
path: './src/index.js',
267+
path: './src/index-cjs.js',
268268
});
269269

270270
await import('./generateDocs.js');

src/index-cjs.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@ import requireYieldsCheck from './rules/requireYieldsCheck.js';
5757
import sortTags from './rules/sortTags.js';
5858
import tagLines from './rules/tagLines.js';
5959
import textEscaping from './rules/textEscaping.js';
60+
import typeFormatting from './rules/typeFormatting.js';
6061
import validTypes from './rules/validTypes.js';
6162

6263
/* eslint-disable jsdoc/valid-types -- Bug */
@@ -129,6 +130,7 @@ index.rules = {
129130
'sort-tags': sortTags,
130131
'tag-lines': tagLines,
131132
'text-escaping': textEscaping,
133+
'type-formatting': typeFormatting,
132134
'valid-types': validTypes,
133135
};
134136

@@ -206,6 +208,7 @@ const createRecommendedRuleset = (warnOrError, flatName) => {
206208
'jsdoc/sort-tags': 'off',
207209
'jsdoc/tag-lines': warnOrError,
208210
'jsdoc/text-escaping': 'off',
211+
'jsdoc/type-formatting': 'off',
209212
'jsdoc/valid-types': warnOrError,
210213
},
211214
};

0 commit comments

Comments
 (0)