Skip to content

Commit c34312b

Browse files
committed
Check invalid and/or empty namepaths
1 parent 036dc4d commit c34312b

File tree

5 files changed

+301
-4
lines changed

5 files changed

+301
-4
lines changed

README.md

Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -823,6 +823,13 @@ The following patterns are not considered problems:
823823
*/
824824
function quux (foo) {
825825

826+
}
827+
828+
/**
829+
*
830+
*/
831+
function quux (foo) {
832+
826833
}
827834
````
828835

@@ -874,6 +881,7 @@ license
874881
listens
875882
member
876883
memberof
884+
memberof!
877885
mixes
878886
mixin
879887
module
@@ -992,6 +1000,13 @@ function quux (foo) {
9921000

9931001
}
9941002

1003+
/**
1004+
* @memberof! foo
1005+
*/
1006+
function quux (foo) {
1007+
1008+
}
1009+
9951010
/**
9961011
* @arg foo
9971012
*/
@@ -1053,6 +1068,7 @@ function quux (foo) {
10531068
* @listens
10541069
* @member
10551070
* @memberof
1071+
* @memberof!
10561072
* @mixes
10571073
* @mixin
10581074
* @module
@@ -2694,6 +2710,56 @@ function quux() {
26942710

26952711
}
26962712
// Message: Syntax error in type: Array<string
2713+
2714+
/**
2715+
* @borrows foo% as bar
2716+
*/
2717+
function quux() {
2718+
2719+
}
2720+
// Message: Syntax error in type: foo%
2721+
2722+
/**
2723+
* @borrows foo as bar%
2724+
*/
2725+
function quux() {
2726+
2727+
}
2728+
// Message: Syntax error in type: bar%
2729+
2730+
/**
2731+
* @borrows foo
2732+
*/
2733+
function quux() {
2734+
2735+
}
2736+
// Message: @borrows must have an "as" expression. Found ""
2737+
2738+
/**
2739+
* @see foo%
2740+
*/
2741+
function quux() {
2742+
2743+
}
2744+
// Settings: {"jsdoc":{"checkSeesForNamepaths":true}}
2745+
// Message: Syntax error in type: foo%
2746+
2747+
/**
2748+
* @alias module:abc#event:foo-bar
2749+
*/
2750+
function quux() {
2751+
2752+
}
2753+
// Message: Syntax error in type: module:abc#event:foo-bar
2754+
2755+
/**
2756+
* @callback
2757+
*/
2758+
function quux() {
2759+
2760+
}
2761+
// Settings: {"jsdoc":{"allowEmptyNamepaths":false}}
2762+
// Message: Syntax error in type:
26972763
````
26982764

26992765
The following patterns are not considered problems:
@@ -2720,6 +2786,34 @@ function quux() {
27202786

27212787
}
27222788

2789+
/**
2790+
* @borrows foo as bar
2791+
*/
2792+
function quux() {
2793+
2794+
}
2795+
2796+
/**
2797+
* @see foo%
2798+
*/
2799+
function quux() {
2800+
2801+
}
2802+
2803+
/**
2804+
* @alias module:svgcanvas.SvgCanvas#event:ext_langReady
2805+
*/
2806+
function quux() {
2807+
2808+
}
2809+
2810+
/**
2811+
* @callback
2812+
*/
2813+
function quux() {
2814+
2815+
}
2816+
27232817
/**
27242818
* @see {@link foo}
27252819
*/

src/iterateJsdoc.js

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,11 +34,13 @@ const curryUtils = (
3434
matchingFileName,
3535
eslintrcForExamples,
3636
allowInlineConfig,
37+
allowEmptyNamepaths,
3738
reportUnusedDisableDirectives,
3839
noDefaultExampleRules,
3940
allowOverrideWithoutParam,
4041
allowImplementsWithoutParam,
4142
allowAugmentsExtendsWithoutParam,
43+
checkSeesForNamepaths,
4244
ancestors,
4345
sourceCode
4446
) => {
@@ -131,6 +133,15 @@ const curryUtils = (
131133
utils.isAugmentsExtendsAllowedWithoutParam = () => {
132134
return allowAugmentsExtendsWithoutParam;
133135
};
136+
utils.isNamepathType = (tagName) => {
137+
return jsdocUtils.isNamepathType(tagName, checkSeesForNamepaths);
138+
};
139+
utils.passesEmptyNamepathCheck = (tag) => {
140+
return !tag.name && allowEmptyNamepaths && _.includes([
141+
// These may serve some minor purpose when empty
142+
'callback', 'event', 'listens', 'fires', 'emits'
143+
], tag.tag);
144+
};
134145

135146
utils.getClassJsdocNode = () => {
136147
const greatGrandParent = ancestors.slice(-3)[0];
@@ -179,12 +190,14 @@ export default (iterator) => {
179190
const configFile = _.get(context, 'settings.jsdoc.configFile');
180191
const eslintrcForExamples = _.get(context, 'settings.jsdoc.eslintrcForExamples') !== false;
181192
const allowInlineConfig = _.get(context, 'settings.jsdoc.allowInlineConfig') !== false;
193+
const allowEmptyNamepaths = _.get(context, 'settings.jsdoc.allowEmptyNamepaths') !== false;
182194
const reportUnusedDisableDirectives = _.get(context, 'settings.jsdoc.reportUnusedDisableDirectives') !== false;
183195
const captionRequired = Boolean(_.get(context, 'settings.jsdoc.captionRequired'));
184196
const noDefaultExampleRules = Boolean(_.get(context, 'settings.jsdoc.noDefaultExampleRules'));
185197
const allowOverrideWithoutParam = Boolean(_.get(context, 'settings.jsdoc.allowOverrideWithoutParam'));
186198
const allowImplementsWithoutParam = Boolean(_.get(context, 'settings.jsdoc.allowImplementsWithoutParam'));
187199
const allowAugmentsExtendsWithoutParam = Boolean(_.get(context, 'settings.jsdoc.allowAugmentsExtendsWithoutParam'));
200+
const checkSeesForNamepaths = Boolean(_.get(context, 'settings.jsdoc.checkSeesForNamepaths'));
188201

189202
const checkJsdoc = (functionNode) => {
190203
const jsdocNode = sourceCode.getJSDocComment(functionNode);
@@ -245,11 +258,13 @@ export default (iterator) => {
245258
matchingFileName,
246259
eslintrcForExamples,
247260
allowInlineConfig,
261+
allowEmptyNamepaths,
248262
reportUnusedDisableDirectives,
249263
noDefaultExampleRules,
250264
allowOverrideWithoutParam,
251265
allowImplementsWithoutParam,
252266
allowAugmentsExtendsWithoutParam,
267+
checkSeesForNamepaths,
253268
ancestors,
254269
sourceCode
255270
);

src/jsdocUtils.js

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,12 +92,36 @@ const hasATag = (jsdoc : Object, targetTagNames : Array) : boolean => {
9292
});
9393
};
9494

95+
const namepathAsNameTags = [
96+
'alias',
97+
'augments',
98+
'callback',
99+
'extends',
100+
'lends',
101+
'memberof',
102+
'memberof!',
103+
'mixes',
104+
'name',
105+
'this',
106+
107+
'emits',
108+
'event',
109+
'fires',
110+
'listens'
111+
];
112+
113+
const isNamepathType = (tagName, checkSeesForNamepaths) => {
114+
return _.includes(namepathAsNameTags, tagName) ||
115+
tagName === 'see' && checkSeesForNamepaths;
116+
};
117+
95118
export default {
96119
getFunctionParameterNames,
97120
getJsdocParameterNames,
98121
getJsdocParameterNamesDeep,
99122
getPreferredTagName,
100123
hasATag,
101124
hasTag,
125+
isNamepathType,
102126
isValidTag
103127
};

src/rules/validTypes.js

Lines changed: 34 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,19 +7,49 @@ const isLink = (tag) => {
77
return /^(@link|@linkcode|@linkplain|@tutorial) /.test(tag);
88
};
99

10+
const asExpression = /as\s+/;
11+
1012
export default iterateJsdoc(({
1113
jsdoc,
12-
report
14+
report,
15+
utils
1316
}) => {
1417
_.forEach(jsdoc.tags, (tag) => {
15-
if (tag.type && !isLink(tag.type)) {
18+
const validTypeParsing = function (type) {
1619
try {
17-
parse(tag.type);
20+
parse(type);
1821
} catch (error) {
1922
if (error.name === 'SyntaxError') {
20-
report('Syntax error in type: ' + tag.type, null, tag);
23+
report('Syntax error in type: ' + type, null, tag);
24+
25+
return false;
2126
}
2227
}
28+
29+
return true;
30+
};
31+
32+
if (tag.tag === 'borrows') {
33+
const thisNamepath = tag.description.replace(asExpression, '');
34+
35+
if (!asExpression.test(tag.description) || !thisNamepath) {
36+
report('@borrows must have an "as" expression. Found "' + tag.description + '"', null, tag);
37+
38+
return;
39+
}
40+
41+
if (validTypeParsing(thisNamepath)) {
42+
const thatNamepath = tag.name;
43+
44+
validTypeParsing(thatNamepath);
45+
}
46+
} else if (utils.isNamepathType(tag.tag)) {
47+
if (utils.passesEmptyNamepathCheck(tag)) {
48+
return;
49+
}
50+
validTypeParsing(tag.name);
51+
} else if (tag.type && !isLink(tag.type)) {
52+
validTypeParsing(tag.type);
2353
}
2454
});
2555
});

0 commit comments

Comments
 (0)