Skip to content

Commit 7bab2ea

Browse files
requireDictionaryWords: Add inline configuration
1 parent 1cbba75 commit 7bab2ea

File tree

2 files changed

+209
-9
lines changed

2 files changed

+209
-9
lines changed

lib/rules/require-dictionary-words.js

Lines changed: 107 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -296,20 +296,117 @@ module.exports.prototype = {
296296
check: function(file, errors) {
297297
var _this = this;
298298

299-
function check(wordDictionaries, nameDictionaries, name, start) {
300-
if (!dictionariesHaveWord(nameDictionaries, name)) {
301-
checkWords(
302-
wordDictionaries,
303-
_this._excludedWords,
304-
errors,
305-
name,
306-
start
307-
);
299+
var allowedIndex = [];
300+
301+
function addToAllowedIndex(allowed, rule, namesStr, line) {
302+
namesStr.split(',').forEach(function(name) {
303+
name = name.trim();
304+
305+
if (!name) {
306+
return;
307+
}
308+
309+
allowedIndex.push({
310+
rule: rule,
311+
name: name,
312+
allowed: allowed,
313+
line: line
314+
});
315+
});
316+
}
317+
318+
function buildAllowedIndex() {
319+
var comments = file.getComments();
320+
var commentRe = new RegExp([
321+
'(jscs\\s*:\\s*(allow|disallow)(',
322+
[
323+
'Words',
324+
'WordsInIdentifiers',
325+
'WordsInProperties',
326+
'Names',
327+
'NamesAsIdentifiers',
328+
'NamesAsProperties'
329+
].join('|'),
330+
'))\\s+(.*)',
331+
].join(''));
332+
333+
comments.forEach(function(comment) {
334+
var allowed;
335+
var parsed = commentRe.exec(comment.value.trim());
336+
337+
if (!parsed || parsed.index !== 0) {
338+
return;
339+
}
340+
341+
allowed = parsed[2] === 'allow';
342+
var rule = 'allow' + parsed[3];
343+
var namesStr = parsed[4];
344+
addToAllowedIndex(allowed, rule, namesStr, comment.loc.start.line);
345+
}, this);
346+
}
347+
348+
buildAllowedIndex();
349+
350+
function isAllowed(context, as, name, line) {
351+
var allowed = false;
352+
allowedIndex.some(function(region) {
353+
// once the comment we're inspecting occurs after the location of the error,
354+
// no longer check for whether the state is allowed or disallow
355+
if (region.line > line) {
356+
return true;
357+
}
358+
359+
var contextRe = new RegExp([
360+
'^allow',
361+
as === 'name' ? 'Names(|As' : 'Words(|In',
362+
context === 'identifier' ? 'Identifiers)' : 'Properties)',
363+
'$'
364+
].join(''));
365+
366+
if (contextRe.test(region.rule)) {
367+
if (as === 'name' && /^allowNames/.test(region.rule)) {
368+
if (region.name === name) {
369+
allowed = region.allowed;
370+
}
371+
} else if (as === 'word' && /^allowWords/.test(region.rule)) {
372+
if (name.match(reWords).indexOf(region.name) > -1) {
373+
allowed = region.allowed;
374+
}
375+
}
376+
}
377+
});
378+
379+
return allowed;
380+
}
381+
382+
function isAllowedName(context, name, line) {
383+
return isAllowed(context, 'name', name, line);
384+
}
385+
386+
function isAllowedWord(context, name, line) {
387+
return isAllowed(context, 'word', name, line);
388+
}
389+
390+
function check(context, wordDictionaries, nameDictionaries, name, start) {
391+
if (
392+
isAllowedName(context, name, start.line) ||
393+
dictionariesHaveWord(nameDictionaries, name) ||
394+
isAllowedWord(context, name, start.line)
395+
) {
396+
return;
308397
}
398+
checkWords(
399+
wordDictionaries,
400+
_this._excludedWords,
401+
errors,
402+
name,
403+
start
404+
);
309405
}
310406

311407
function checkIdentifier(name, start) {
312408
check(
409+
'identifier',
313410
_this._identifierWordDictionaries,
314411
_this._identifierNameDictionaries,
315412
name,
@@ -319,6 +416,7 @@ module.exports.prototype = {
319416

320417
function checkProperty(name, start) {
321418
check(
419+
'property',
322420
_this._propertyWordDictionaries,
323421
_this._propertyNameDictionaries,
324422
name,

test/specs/rules/require-dictionary-words.js

Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,108 @@ describe('rules/require-dictionary-words', function() {
99
checker.configure({ plugins: ['./lib/index.js'] });
1010
});
1111

12+
describe('inline configuration', function() {
13+
beforeEach(function() {
14+
checker.configure({ requireDictionaryWords: true });
15+
});
16+
17+
it('should later allow and later disallow inline', function() {
18+
assert(checker.checkString([
19+
'asdf = 1',
20+
'// jscs:allowWords asdf, jkl',
21+
'asdf = 1',
22+
'// jscs:disallowWords asdf, jkl',
23+
'asdf = 1',
24+
].join('\n')).getErrorCount() === 2);
25+
26+
assert(checker.checkString([
27+
'asdf = 1',
28+
'jkl = 1',
29+
'// jscs:allowWords asdf, jkl',
30+
'asdf = 1',
31+
'jkl = 1',
32+
'// jscs:disallowWords asdf, jkl',
33+
'asdf = 1',
34+
'jkl = 1'
35+
].join('\n')).getErrorCount() === 4);
36+
});
37+
38+
it('should allow words inline', function() {
39+
assert(checker.checkString([
40+
'// jscs:allowWords asdf, jkl',
41+
'asdf = 1',
42+
'asdfAsdf = 1',
43+
'object.jkl = 1',
44+
'object.jklJkl = 1'
45+
].join('\n')).isEmpty());
46+
});
47+
48+
it('should allow words in identifiers inline', function() {
49+
assert(checker.checkString([
50+
'// jscs:allowWordsInIdentifiers asdf, jkl',
51+
'asdf = 1',
52+
'asdfAsdf = 1'
53+
].join('\n')).isEmpty());
54+
assert(checker.checkString([
55+
'// jscs:allowWordsInIdentifiers asdf, jkl',
56+
'object.jkl = 1',
57+
'object.jklJkl = 1'
58+
].join('\n')).getErrorCount() === 3);
59+
});
60+
61+
it('should allow words in properties inline', function() {
62+
assert(checker.checkString([
63+
'// jscs:allowWordsInProperties asdf, jkl',
64+
'object.jkl = 1',
65+
'object.jklJkl = 1'
66+
].join('\n')).isEmpty());
67+
assert(checker.checkString([
68+
'// jscs:allowWordsInProperties asdf, jkl',
69+
'asdf = 1',
70+
'asdfAsdf = 1'
71+
].join('\n')).getErrorCount() === 3);
72+
});
73+
74+
it('should allow names inline', function() {
75+
assert(checker.checkString([
76+
'// jscs:allowNames asdf, jkl',
77+
'asdf = 1',
78+
'object.jkl = 1'
79+
].join('\n')).isEmpty());
80+
assert(checker.checkString([
81+
'// jscs:allowNames asdf, jkl',
82+
'asdfAsdf = 1',
83+
'object.jklJkl = 1'
84+
].join('\n')).getErrorCount() === 4);
85+
});
86+
87+
it('should allow names as identifiers inline', function() {
88+
assert(checker.checkString([
89+
'// jscs:allowNamesAsIdentifiers asdf, jkl',
90+
'asdf = 1'
91+
].join('\n')).isEmpty());
92+
assert(checker.checkString([
93+
'// jscs:allowNamesAsIdentifiers asdf, jkl',
94+
'asdfAsdf = 1',
95+
'object.jkl = 1',
96+
'object.jklJkl = 1'
97+
].join('\n')).getErrorCount() === 5);
98+
});
99+
100+
it('should allow names as properties inline', function() {
101+
assert(checker.checkString([
102+
'// jscs:allowNamesAsProperties asdf, jkl',
103+
'object.jkl = 1'
104+
].join('\n')).isEmpty());
105+
assert(checker.checkString([
106+
'// jscs:allowNamesAsProperties asdf, jkl',
107+
'asdf = 1',
108+
'asdfAsdf = 1',
109+
'object.jklJkl = 1'
110+
].join('\n')).getErrorCount() === 5);
111+
});
112+
});
113+
12114
describe('true', function() {
13115
beforeEach(function() {
14116
checker.configure({ requireDictionaryWords: true });

0 commit comments

Comments
 (0)