Skip to content

Commit ab25099

Browse files
committed
feat(check-types, no-undefined-types, valid-types): use mode-aware type parsing; fixes part of gajus#356; fixes gajus#495
BREAKING CHANGE: Requires Node 10+ Also: 1. Adds "permissive" mode 2. Checks "param" for valid namepaths
1 parent 88bb1ce commit ab25099

File tree

11 files changed

+176
-68
lines changed

11 files changed

+176
-68
lines changed

.README/README.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -131,13 +131,18 @@ how many line breaks to add when a block is missing.
131131
### Mode
132132

133133
- `settings.jsdoc.mode` - Set to `jsdoc` (the default), `typescript`, or `closure`.
134+
Note that if you do not wish to use separate `.eslintrc.*` files for a project
135+
containing both JavaScript and TypeScript, you can also use [`overrides`](https://eslint.org/docs/user-guide/configuring). You may also set to `"permissive"` to
136+
try to be as accommodating to any of the styles, but this is not recommended.
134137
Currently is used for the following:
135138
- Determine valid tags for `check-tag-names`
136139
- Only check `@template` in `no-undefined-types` for types in "closure" and
137140
"typescript" modes
138141
- For type-checking rules, determine which tags will be checked for types
139142
(Closure allows types on some tags which the others do not,
140143
so these tags will additionally be checked in "closure" mode)
144+
- For type-checking rules, impacts parsing of types (through
145+
[jsdoctypeparser](https://github.com/jsdoctypeparser/jsdoctypeparser) dependency)
141146
- Check preferred tag names
142147

143148
### Alias Preference

.travis.yml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@ node_js:
33
- 14
44
- 12
55
- 10
6-
- 8
76
before_install:
87
- npm config set depth 0
98
before_script: >

README.md

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -189,13 +189,18 @@ how many line breaks to add when a block is missing.
189189
### Mode
190190

191191
- `settings.jsdoc.mode` - Set to `jsdoc` (the default), `typescript`, or `closure`.
192+
Note that if you do not wish to use separate `.eslintrc.*` files for a project
193+
containing both JavaScript and TypeScript, you can also use [`overrides`](https://eslint.org/docs/user-guide/configuring). You may also set to `"permissive"` to
194+
try to be as accommodating to any of the styles, but this is not recommended.
192195
Currently is used for the following:
193196
- Determine valid tags for `check-tag-names`
194197
- Only check `@template` in `no-undefined-types` for types in "closure" and
195198
"typescript" modes
196199
- For type-checking rules, determine which tags will be checked for types
197200
(Closure allows types on some tags which the others do not,
198201
so these tags will additionally be checked in "closure" mode)
202+
- For type-checking rules, impacts parsing of types (through
203+
[jsdoctypeparser](https://github.com/jsdoctypeparser/jsdoctypeparser) dependency)
199204
- Check preferred tag names
200205

201206
<a name="eslint-plugin-jsdoc-settings-alias-preference"></a>
@@ -13256,6 +13261,14 @@ function quux() {
1325613261
}
1325713262
// Message: Syntax error in namepath: module:namespace.SomeClass<~
1325813263
13264+
/**
13265+
* @param someParam<~
13266+
*/
13267+
function quux() {
13268+
13269+
}
13270+
// Message: Syntax error in namepath: someParam<~
13271+
1325913272
/**
1326013273
* @memberof module:namespace.SomeClass~<
1326113274
*/
@@ -13391,6 +13404,15 @@ function quux () {}
1339113404
let foo;
1339213405
// Settings: {"jsdoc":{"mode":"closure"}}
1339313406
// Message: Tag @this must have a type
13407+
13408+
/**
13409+
* Foo function.
13410+
*
13411+
* @param {[number, string]} bar - The bar array.
13412+
*/
13413+
function foo(bar) {}
13414+
// Settings: {"jsdoc":{"mode":"jsdoc"}}
13415+
// Message: Syntax error in type: [number, string]
1339413416
````
1339513417
1339613418
The following patterns are not considered problems:
@@ -13565,6 +13587,22 @@ function quux () {}
1356513587
* @define
1356613588
*/
1356713589
function quux () {}
13590+
13591+
/**
13592+
* Foo function.
13593+
*
13594+
* @param {[number, string]} bar - The bar array.
13595+
*/
13596+
function foo(bar) {}
13597+
// Settings: {"jsdoc":{"mode":"typescript"}}
13598+
13599+
/**
13600+
* Foo function.
13601+
*
13602+
* @param {[number, string]} bar - The bar array.
13603+
*/
13604+
function foo(bar) {}
13605+
// Settings: {"jsdoc":{"mode":"permissive"}}
1356813606
````
1356913607
1357013608

package.json

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
"dependencies": {
88
"comment-parser": "^0.7.5",
99
"debug": "^4.1.1",
10-
"jsdoctypeparser": "^6.1.0",
10+
"jsdoctypeparser": "^7.0.0",
1111
"lodash": "^4.17.15",
1212
"regextras": "^0.7.1",
1313
"semver": "^6.3.0",
@@ -32,14 +32,14 @@
3232
"gitdown": "^3.1.3",
3333
"glob": "^7.1.6",
3434
"husky": "^4.2.5",
35-
"mocha": "^7.2.0",
35+
"mocha": "^8.0.1",
3636
"nyc": "^15.1.0",
3737
"rimraf": "^3.0.2",
3838
"semantic-release": "^17.0.8",
3939
"typescript": "^3.9.5"
4040
},
4141
"engines": {
42-
"node": ">=8"
42+
"node": ">=10"
4343
},
4444
"husky": {
4545
"hooks": {

src/jsdocUtils.js

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -192,7 +192,7 @@ const getTagNamesForMode = (mode, context) => {
192192
return jsdocTags;
193193
case 'typescript':
194194
return typeScriptTags;
195-
case 'closure':
195+
case 'closure': case 'permissive':
196196
return closureTags;
197197
default:
198198
if (!modeWarnSettings.hasBeenWarned(context, 'mode')) {
@@ -399,9 +399,10 @@ const namepathDefiningTags = new Set([
399399
]);
400400

401401
// The following do not seem to allow curly brackets in their doc
402-
// signature or examples (besides `modifies`)
402+
// signature or examples (besides `modifies` and `param`)
403403
const tagsWithOptionalNamePosition = new Set([
404404
...namepathDefiningTags,
405+
'param',
405406

406407
// `borrows` has a different format, however, so needs special parsing;
407408
// seems to require both, and as "namepath"'s

src/rules/checkTypes.js

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,11 @@ const adjustNames = (type, preferred, isGenericMatch, nodeName, node, parentNode
3838
if (bracketEnd) {
3939
parentNode.meta.syntax = 'ANGLE_BRACKET';
4040
ret = preferred.slice(0, -2);
41+
} else if (
42+
parentNode.meta.syntax === 'SQUARE_BRACKET' &&
43+
(nodeName === '[]' || nodeName === 'Array')
44+
) {
45+
parentNode.meta.syntax = 'ANGLE_BRACKET';
4146
}
4247
}
4348
}
@@ -64,7 +69,7 @@ export default iterateJsdoc(({
6469
return utils.tagMightHaveTypePosition(tag.tag);
6570
});
6671

67-
const {preferredTypes} = settings;
72+
const {preferredTypes, mode} = settings;
6873
const {
6974
noDefaults,
7075
unifyParentAndChildTypeChecks,
@@ -126,7 +131,7 @@ export default iterateJsdoc(({
126131
let typeAst;
127132

128133
try {
129-
typeAst = parse(jsdocTag.type);
134+
typeAst = parse(jsdocTag.type, {mode});
130135
} catch {
131136
return;
132137
}

src/rules/noUndefinedTypes.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ export default iterateJsdoc(({
3030
const {definedTypes = []} = context.options[0] || {};
3131

3232
let definedPreferredTypes = [];
33-
const {preferredTypes} = settings;
33+
const {preferredTypes, mode} = settings;
3434
if (Object.keys(preferredTypes).length) {
3535
// Replace `_.values` with `Object.values` when we may start requiring Node 7+
3636
definedPreferredTypes = _.values(preferredTypes).map((preferredType) => {
@@ -139,7 +139,7 @@ export default iterateJsdoc(({
139139
let parsedType;
140140

141141
try {
142-
parsedType = parseType(tag.type);
142+
parsedType = parseType(tag.type, {mode});
143143
} catch {
144144
// On syntax error, will be handled by valid-types.
145145
return;

src/rules/requireDescriptionCompleteSentence.js

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -11,11 +11,7 @@ const otherDescriptiveTags = new Set([
1111
]);
1212

1313
const extractParagraphs = (text) => {
14-
// Todo [engine:node@>8.11.0]: Uncomment following line with neg. lookbehind instead
15-
// return text.split(/(?<![;:])\n\n/u);
16-
return [...text].reverse().join('').split(/\n\n(?![;:])/u).map((par) => {
17-
return [...par].reverse().join('');
18-
}).reverse();
14+
return text.split(/(?<![;:])\n\n/u);
1915
};
2016

2117
const extractSentences = (text, abbreviationsRegex) => {

src/rules/requireJsdoc.js

Lines changed: 44 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -225,67 +225,63 @@ export default {
225225
}
226226
};
227227

228-
// todo[engine:node@>=8.3.0]: Change to object spread
229-
// eslint-disable-next-line fp/no-mutating-assign
230-
return Object.assign(
231-
jsdocUtils.getContextObject(jsdocUtils.enforcedContexts(context, []), checkJsDoc),
232-
{
233-
ArrowFunctionExpression (node) {
234-
if (!requireOption.ArrowFunctionExpression) {
235-
return;
236-
}
228+
return {
229+
...jsdocUtils.getContextObject(jsdocUtils.enforcedContexts(context, []), checkJsDoc),
230+
ArrowFunctionExpression (node) {
231+
if (!requireOption.ArrowFunctionExpression) {
232+
return;
233+
}
237234

238-
if (!['VariableDeclarator', 'ExportDefaultDeclaration', 'AssignmentExpression'].includes(node.parent.type)) {
239-
return;
240-
}
235+
if (!['VariableDeclarator', 'ExportDefaultDeclaration', 'AssignmentExpression'].includes(node.parent.type)) {
236+
return;
237+
}
241238

242-
checkJsDoc(node, true);
243-
},
239+
checkJsDoc(node, true);
240+
},
244241

245-
ClassDeclaration (node) {
246-
if (!requireOption.ClassDeclaration) {
247-
return;
248-
}
242+
ClassDeclaration (node) {
243+
if (!requireOption.ClassDeclaration) {
244+
return;
245+
}
249246

250-
checkJsDoc(node);
251-
},
247+
checkJsDoc(node);
248+
},
252249

253-
ClassExpression (node) {
254-
if (!requireOption.ClassExpression) {
255-
return;
256-
}
250+
ClassExpression (node) {
251+
if (!requireOption.ClassExpression) {
252+
return;
253+
}
257254

258-
checkJsDoc(node);
259-
},
255+
checkJsDoc(node);
256+
},
260257

261-
FunctionDeclaration (node) {
262-
if (!requireOption.FunctionDeclaration) {
263-
return;
264-
}
258+
FunctionDeclaration (node) {
259+
if (!requireOption.FunctionDeclaration) {
260+
return;
261+
}
265262

266-
checkJsDoc(node, true);
267-
},
263+
checkJsDoc(node, true);
264+
},
268265

269-
FunctionExpression (node) {
270-
if (requireOption.MethodDefinition && node.parent.type === 'MethodDefinition') {
271-
checkJsDoc(node, true);
266+
FunctionExpression (node) {
267+
if (requireOption.MethodDefinition && node.parent.type === 'MethodDefinition') {
268+
checkJsDoc(node, true);
272269

273-
return;
274-
}
270+
return;
271+
}
275272

276-
if (!requireOption.FunctionExpression) {
277-
return;
278-
}
273+
if (!requireOption.FunctionExpression) {
274+
return;
275+
}
279276

280-
if (
281-
['VariableDeclarator', 'AssignmentExpression', 'ExportDefaultDeclaration'].includes(node.parent.type) ||
282-
node.parent.type === 'Property' && node === node.parent.value
283-
) {
284-
checkJsDoc(node, true);
285-
}
286-
},
277+
if (
278+
['VariableDeclarator', 'AssignmentExpression', 'ExportDefaultDeclaration'].includes(node.parent.type) ||
279+
node.parent.type === 'Property' && node === node.parent.value
280+
) {
281+
checkJsDoc(node, true);
282+
}
287283
},
288-
);
284+
};
289285
},
290286
meta: {
291287
docs: {

src/rules/validTypes.js

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,19 +8,20 @@ export default iterateJsdoc(({
88
report,
99
utils,
1010
context,
11+
settings,
1112
}) => {
1213
const {
1314
allowEmptyNamepaths = true,
1415
checkSeesForNamepaths = false,
1516
} = context.options[0] || {};
16-
17+
const {mode} = settings;
1718
if (!jsdoc.tags) {
1819
return;
1920
}
2021
jsdoc.tags.forEach((tag) => {
2122
const validNamepathParsing = function (namepath, tagName) {
2223
try {
23-
parse(namepath);
24+
parse(namepath, {mode});
2425
} catch {
2526
let handled = false;
2627

@@ -29,7 +30,7 @@ export default iterateJsdoc(({
2930
const endChar = namepath.slice(-1);
3031
if (['#', '.', '~'].includes(endChar)) {
3132
try {
32-
parse(namepath.slice(0, -1));
33+
parse(namepath.slice(0, -1), {mode});
3334
handled = true;
3435
} catch {
3536
// Use the original error for including the whole type
@@ -39,7 +40,7 @@ export default iterateJsdoc(({
3940
const startChar = namepath.charAt();
4041
if (['#', '.', '~'].includes(startChar)) {
4142
try {
42-
parse(namepath.slice(1));
43+
parse(namepath.slice(1), {mode});
4344
handled = true;
4445
} catch {
4546
// Use the original error for including the whole type
@@ -60,7 +61,7 @@ export default iterateJsdoc(({
6061

6162
const validTypeParsing = function (type) {
6263
try {
63-
parse(type);
64+
parse(type, {mode});
6465
} catch {
6566
report(`Syntax error in type: ${type}`, null, tag);
6667

0 commit comments

Comments
 (0)