Skip to content

Commit c19d57e

Browse files
committed
fix(no-missing-syntax, no-restricted-syntax): support "any" with comment including global comments
1 parent 9ab7709 commit c19d57e

File tree

6 files changed

+294
-19
lines changed

6 files changed

+294
-19
lines changed

README.md

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8185,6 +8185,21 @@ function quux () {
81858185
}
81868186
// "jsdoc/no-missing-syntax": ["error"|"warn", {"contexts":[{"comment":"JsdocBlock[postDelimiter=\"\"]:has(JsdocTypeUnion > JsdocTypeName[value=\"Bar\"]:nth-child(1))","context":"FunctionDeclaration","minimum":2}]}]
81878187
// Message: Syntax is required: FunctionDeclaration with JsdocBlock[postDelimiter=""]:has(JsdocTypeUnion > JsdocTypeName[value="Bar"]:nth-child(1))
8188+
8189+
/**
8190+
* @param ab
8191+
* @param cd
8192+
*/
8193+
// "jsdoc/no-missing-syntax": ["error"|"warn", {"contexts":[{"comment":"JsdocBlock:has(JsdocTag[name=/opt_/])","context":"any","message":"Require names matching `/^opt_/i`."}]}]
8194+
// Message: Require names matching `/^opt_/i`.
8195+
8196+
/**
8197+
* @param ab
8198+
* @param cd
8199+
*/
8200+
function quux () {}
8201+
// "jsdoc/no-missing-syntax": ["error"|"warn", {"contexts":[{"comment":"JsdocBlock:has(JsdocTag[name=/opt_/])","context":"any","message":"Require names matching `/^opt_/i`."}]}]
8202+
// Message: Require names matching `/^opt_/i`.
81888203
````
81898204

81908205
The following patterns are not considered problems:
@@ -8219,6 +8234,19 @@ function baz () {
82198234

82208235
}
82218236
// "jsdoc/no-missing-syntax": ["error"|"warn", {"contexts":[{"comment":"JsdocBlock[postDelimiter=\"\"]:has(JsdocTypeUnion > JsdocTypeName[value=\"Bar\"]:nth-child(1))","context":"FunctionDeclaration","minimum":2}]}]
8237+
8238+
/**
8239+
* @param opt_a
8240+
* @param opt_b
8241+
*/
8242+
// "jsdoc/no-missing-syntax": ["error"|"warn", {"contexts":[{"comment":"JsdocBlock:has(JsdocTag[name=/opt_/])","context":"any","message":"Require names matching `/^opt_/i`."}]}]
8243+
8244+
/**
8245+
* @param opt_a
8246+
* @param opt_b
8247+
*/
8248+
function quux () {}
8249+
// "jsdoc/no-missing-syntax": ["error"|"warn", {"contexts":[{"comment":"JsdocBlock:has(JsdocTag[name=/opt_/])","context":"any","message":"Require names matching `/^opt_/i`."}]}]
82228250
````
82238251

82248252

@@ -8513,6 +8541,29 @@ function quux () {
85138541
function a () {}
85148542
// "jsdoc/no-restricted-syntax": ["error"|"warn", {"contexts":[{"comment":"JsdocBlock:has(JsdocTag[name=/opt_/])","context":"FunctionDeclaration","message":"Only allowing names not matching `/^opt_/i`."}]}]
85158543
// Message: Only allowing names not matching `/^opt_/i`.
8544+
8545+
/**
8546+
* @param opt_a
8547+
* @param opt_b
8548+
*/
8549+
function a () {}
8550+
// "jsdoc/no-restricted-syntax": ["error"|"warn", {"contexts":[{"comment":"JsdocBlock:has(JsdocTag[name=/opt_/])","context":"any","message":"Only allowing names not matching `/^opt_/i`."}]}]
8551+
// Message: Only allowing names not matching `/^opt_/i`.
8552+
8553+
/**
8554+
* @param opt_a
8555+
* @param opt_b
8556+
*/
8557+
function a () {}
8558+
// "jsdoc/no-restricted-syntax": ["error"|"warn", {"contexts":[{"comment":"JsdocBlock:has(JsdocTag[name=/not-this/])","context":"any","message":"Only allowing names not matching `/^not-this/i`."},{"comment":"JsdocBlock:has(JsdocTag[name=/opt_/])","context":"any","message":"Only allowing names not matching `/^opt_/i`."}]}]
8559+
// Message: Only allowing names not matching `/^opt_/i`.
8560+
8561+
/**
8562+
* @param opt_a
8563+
* @param opt_b
8564+
*/
8565+
// "jsdoc/no-restricted-syntax": ["error"|"warn", {"contexts":[{"comment":"JsdocBlock:has(JsdocTag[name=/opt_/])","context":"any","message":"Only allowing names not matching `/^opt_/i`."}]}]
8566+
// Message: Only allowing names not matching `/^opt_/i`.
85168567
````
85178568

85188569
The following patterns are not considered problems:
@@ -8533,6 +8584,19 @@ function quux () {
85338584

85348585
}
85358586
// "jsdoc/no-restricted-syntax": ["error"|"warn", {"contexts":[{"comment":"JsdocBlock[postDelimiter=\"\"]:has(JsdocTypeUnion > JsdocTypeName[value=\"Foo\"]:nth-child(1))","context":"FunctionDeclaration"}]}]
8587+
8588+
/**
8589+
* @param ab
8590+
* @param cd
8591+
*/
8592+
function a () {}
8593+
// "jsdoc/no-restricted-syntax": ["error"|"warn", {"contexts":[{"comment":"JsdocBlock:has(JsdocTag[name=/opt_/])","context":"any","message":"Only allowing names not matching `/^opt_/i`."}]}]
8594+
8595+
/**
8596+
* @param ab
8597+
* @param cd
8598+
*/
8599+
// "jsdoc/no-restricted-syntax": ["error"|"warn", {"contexts":[{"comment":"JsdocBlock:has(JsdocTag[name=/opt_/])","context":"any","message":"Only allowing names not matching `/^opt_/i`."}]}]
85368600
````
85378601

85388602

src/iterateJsdoc.js

Lines changed: 36 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -681,7 +681,7 @@ const iterate = (
681681
info,
682682
indent, jsdoc,
683683
ruleConfig, context, lines, jsdocNode, node, settings,
684-
sourceCode, iterator, state, iteratingAll,
684+
sourceCode, iterator, state, lastComment, iteratingAll,
685685
) => {
686686
const report = makeReport(context, jsdocNode);
687687

@@ -745,10 +745,12 @@ const getIndentAndJSDoc = function (lines, jsdocNode) {
745745
*
746746
* @param {JsdocVisitor} iterator
747747
* @param {{meta: any}} ruleConfig
748+
* @param contexts
748749
*/
749-
const iterateAllJsdocs = (iterator, ruleConfig) => {
750+
const iterateAllJsdocs = (iterator, ruleConfig, contexts) => {
750751
const trackedJsdocs = [];
751752

753+
let handler;
752754
let settings;
753755
const callIterator = (context, node, jsdocNodes, state, lastCall) => {
754756
const sourceCode = context.getSourceCode();
@@ -764,12 +766,21 @@ const iterateAllJsdocs = (iterator, ruleConfig) => {
764766
lines, jsdocNode,
765767
);
766768

769+
let lastComment;
770+
if (contexts && contexts.every(({comment}) => {
771+
lastComment = comment;
772+
773+
return handler(comment, jsdoc) === false;
774+
})) {
775+
return;
776+
}
777+
767778
iterate(
768-
null,
779+
lastComment ? {comment: lastComment, selector: node?.type} : null,
769780
indent, jsdoc,
770781
ruleConfig, context, lines, jsdocNode, node,
771782
settings, sourceCode, iterator,
772-
state, true,
783+
state, lastComment, true,
773784
);
774785
});
775786
if (lastCall && ruleConfig.exit) {
@@ -788,6 +799,9 @@ const iterateAllJsdocs = (iterator, ruleConfig) => {
788799
if (!settings) {
789800
return {};
790801
}
802+
if (contexts) {
803+
handler = commentHandler(settings);
804+
}
791805

792806
const state = {};
793807

@@ -799,11 +813,11 @@ const iterateAllJsdocs = (iterator, ruleConfig) => {
799813
return;
800814
}
801815

802-
const comment = getJSDocComment(sourceCode, node, settings);
803-
if (trackedJsdocs.includes(comment)) {
816+
const commentNode = getJSDocComment(sourceCode, node, settings);
817+
if (trackedJsdocs.includes(commentNode)) {
804818
return;
805819
}
806-
if (!comment) {
820+
if (!commentNode) {
807821
if (ruleConfig.nonComment) {
808822
ruleConfig.nonComment({
809823
node,
@@ -814,8 +828,8 @@ const iterateAllJsdocs = (iterator, ruleConfig) => {
814828
return;
815829
}
816830

817-
trackedJsdocs.push(comment);
818-
callIterator(context, node, [comment], state);
831+
trackedJsdocs.push(commentNode);
832+
callIterator(context, node, [commentNode], state);
819833
},
820834
'Program:exit' () {
821835
const allComments = sourceCode.getAllComments();
@@ -911,18 +925,25 @@ export default function iterateJsdoc (iterator, ruleConfig) {
911925
* a list with parser callback function.
912926
*/
913927
create (context) {
928+
const settings = getSettings(context);
929+
if (!settings) {
930+
return {};
931+
}
932+
914933
let contexts;
915934
if (ruleConfig.contextDefaults || ruleConfig.contextSelected) {
916935
contexts = jsdocUtils.enforcedContexts(context, ruleConfig.contextDefaults);
917-
if (contexts?.includes('any')) {
918-
return iterateAllJsdocs(iterator, ruleConfig).create(context);
936+
const hasPlainAny = contexts?.includes('any');
937+
const hasObjectAny = !hasPlainAny && contexts?.find((ctxt) => {
938+
return ctxt?.context === 'any';
939+
});
940+
if (hasPlainAny || hasObjectAny) {
941+
return iterateAllJsdocs(
942+
iterator, ruleConfig, hasObjectAny ? contexts : null,
943+
).create(context);
919944
}
920945
}
921946
const sourceCode = context.getSourceCode();
922-
const settings = getSettings(context);
923-
if (!settings) {
924-
return {};
925-
}
926947
const {lines} = sourceCode;
927948

928949
const state = {};

src/rules/noMissingSyntax.js

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -52,9 +52,15 @@ export default iterateJsdoc(({
5252
const contextStr = typeof cntxt === 'object' ? cntxt.context : cntxt;
5353
const comment = cntxt?.comment ?? '';
5454

55-
if (!state.selectorMap[contextStr] ||
56-
!state.selectorMap[contextStr][comment] ||
57-
state.selectorMap[contextStr][comment] < (cntxt?.minimum ?? 1)
55+
const contextKey = contextStr === 'any' ? undefined : contextStr;
56+
57+
if (
58+
(!state.selectorMap[contextKey] ||
59+
!state.selectorMap[contextKey][comment] ||
60+
state.selectorMap[contextKey][comment] < (cntxt?.minimum ?? 1)) &&
61+
(contextStr !== 'any' || Object.values(state.selectorMap).every((cmmnt) => {
62+
return !cmmnt[comment] || cmmnt[comment] < (cntxt?.minimum ?? 1);
63+
}))
5864
) {
5965
const message = cntxt?.message ?? 'Syntax is required: {{context}}' +
6066
(comment ? ' with {{comment}}' : '');

src/rules/noRestrictedSyntax.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,8 @@ export default iterateJsdoc(({
1414

1515
const foundContext = contexts.find((cntxt) => {
1616
return cntxt === selector ||
17-
typeof cntxt === 'object' && selector === cntxt.context &&
17+
typeof cntxt === 'object' &&
18+
(cntxt.context === 'any' || selector === cntxt.context) &&
1819
comment === cntxt.comment;
1920
});
2021

test/rules/assertions/noMissingSyntax.js

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,49 @@ export default {
115115
],
116116
}],
117117
},
118+
{
119+
code: `
120+
/**
121+
* @param ab
122+
* @param cd
123+
*/
124+
`,
125+
errors: [{
126+
line: 1,
127+
message: 'Require names matching `/^opt_/i`.',
128+
}],
129+
options: [{
130+
contexts: [
131+
{
132+
comment: 'JsdocBlock:has(JsdocTag[name=/opt_/])',
133+
context: 'any',
134+
message: 'Require names matching `/^opt_/i`.',
135+
},
136+
],
137+
}],
138+
},
139+
{
140+
code: `
141+
/**
142+
* @param ab
143+
* @param cd
144+
*/
145+
function quux () {}
146+
`,
147+
errors: [{
148+
line: 1,
149+
message: 'Require names matching `/^opt_/i`.',
150+
}],
151+
options: [{
152+
contexts: [
153+
{
154+
comment: 'JsdocBlock:has(JsdocTag[name=/opt_/])',
155+
context: 'any',
156+
message: 'Require names matching `/^opt_/i`.',
157+
},
158+
],
159+
}],
160+
},
118161
],
119162
valid: [
120163
{
@@ -168,5 +211,40 @@ export default {
168211
],
169212
}],
170213
},
214+
{
215+
code: `
216+
/**
217+
* @param opt_a
218+
* @param opt_b
219+
*/
220+
`,
221+
options: [{
222+
contexts: [
223+
{
224+
comment: 'JsdocBlock:has(JsdocTag[name=/opt_/])',
225+
context: 'any',
226+
message: 'Require names matching `/^opt_/i`.',
227+
},
228+
],
229+
}],
230+
},
231+
{
232+
code: `
233+
/**
234+
* @param opt_a
235+
* @param opt_b
236+
*/
237+
function quux () {}
238+
`,
239+
options: [{
240+
contexts: [
241+
{
242+
comment: 'JsdocBlock:has(JsdocTag[name=/opt_/])',
243+
context: 'any',
244+
message: 'Require names matching `/^opt_/i`.',
245+
},
246+
],
247+
}],
248+
},
171249
],
172250
};

0 commit comments

Comments
 (0)