1 | /**
|
2 | * Requires member expressions to use dot notation when possible
|
3 | *
|
4 | * Types: `Boolean` or `Object`
|
5 | *
|
6 | * Values:
|
7 | * - `true`
|
8 | * - `"except_snake_case"` (*deprecated* use `"allExcept": ["snake_case"]`) allow quoted snake cased identifiers
|
9 | * - `Object`:
|
10 | * - `'allExcept'` array of exceptions:
|
11 | * - `'keywords'` allow quoted identifiers made of reserved words
|
12 | * - `'snake_case'` allow quoted snake cased identifiers
|
13 | *
|
14 | * N.B.: keywords are always allowed with es3 enabled (http://jscs.info/overview.html#es3)
|
15 | *
|
16 | * JSHint: [`sub`](http://www.jshint.com/docs/options/#sub)
|
17 | *
|
18 | * #### Example
|
19 | *
|
20 | * ```js
|
21 | * "requireDotNotation": true
|
22 | * ```
|
23 | *
|
24 | * ##### Valid
|
25 | *
|
26 | * ```js
|
27 | * var a = b[c];
|
28 | * var a = b.c;
|
29 | * var a = b[c.d];
|
30 | * var a = b[1];
|
31 | * var a = b.while; // reserved words can be property names in ES5
|
32 | * ```
|
33 | *
|
34 | * ##### Invalid
|
35 | *
|
36 | * ```js
|
37 | * var a = b['c'];
|
38 | * var a = b['snake_cased'];
|
39 | * var a = b['_camelCased'];
|
40 | * var a = b['camelCased_'];
|
41 | * ```
|
42 | *
|
43 | * #### Example for allExcept snake_case
|
44 | *
|
45 | * ```js
|
46 | * "requireDotNotation": { "allExcept": [ "snake_case" ] }
|
47 | * ```
|
48 | *
|
49 | * ##### Valid
|
50 | * ```js
|
51 | * var a = b[c];
|
52 | * var a = b.c;
|
53 | * var a = b['snake_cased'];
|
54 | * var a = b['camelCased_butWithSnakes'];
|
55 | * ```
|
56 | *
|
57 | * #### Example for allExcept keywords
|
58 | *
|
59 | * ```js
|
60 | * "requireDotNotation": { "allExcept": [ "keywords" ] }
|
61 | * ```
|
62 | *
|
63 | * ##### Valid
|
64 | *
|
65 | * ```js
|
66 | * var a = b['await']; // reserved word in ES6
|
67 | * var a = b['yield']; // reserved word in ES5
|
68 | * var a = b['let'];
|
69 | * ```
|
70 | *
|
71 | * ##### Invalid
|
72 | *
|
73 | * ```js
|
74 | * var a = b['c'];
|
75 | * ```
|
76 | *
|
77 | * #### Example for `"es3": true`
|
78 | *
|
79 | * ```js
|
80 | * "requireDotNotation": true,
|
81 | * "es3": true
|
82 | * ```
|
83 | *
|
84 | * ##### Valid
|
85 | *
|
86 | * ```js
|
87 | * var a = b[c];
|
88 | * var a = b.c;
|
89 | * var a = b[c.d];
|
90 | * var a = b[1];
|
91 | * var a = b['while']; // reserved word in ES3
|
92 | * ```
|
93 | *
|
94 | * ##### Invalid
|
95 | *
|
96 | * ```js
|
97 | * var a = b['c'];
|
98 | * ```
|
99 | */
|
100 |
|
101 | var assert = require('assert');
|
102 | var utils = require('../utils');
|
103 | var reservedWords = require('reserved-words');
|
104 |
|
105 | module.exports = function() {};
|
106 |
|
107 | module.exports.prototype = {
|
108 |
|
109 | configure: function(options) {
|
110 | if (typeof options !== 'object') {
|
111 | assert(
|
112 | options === true || options === 'except_snake_case',
|
113 | this.getOptionName() + ' option requires either a true value or an object'
|
114 | );
|
115 |
|
116 | var _options = {};
|
117 | if (options === 'except_snake_case') {
|
118 | _options.allExcept = ['snake_case'];
|
119 | }
|
120 |
|
121 | return this.configure(_options);
|
122 | }
|
123 |
|
124 | assert(
|
125 | !options.allExcept || Array.isArray(options.allExcept),
|
126 | 'allExcept value of ' + this.getOptionName() + ' option requires an array with exceptions'
|
127 | );
|
128 |
|
129 | if (Array.isArray(options.allExcept)) {
|
130 | this._exceptSnakeCase = options.allExcept.indexOf('snake_case') > -1;
|
131 | this._exceptKeywords = options.allExcept.indexOf('keywords') > -1;
|
132 | }
|
133 | },
|
134 |
|
135 | getOptionName: function() {
|
136 | return 'requireDotNotation';
|
137 | },
|
138 |
|
139 | check: function(file, errors) {
|
140 | var exceptSnakeCase = this._exceptSnakeCase;
|
141 | var exceptKeywords = this._exceptKeywords;
|
142 |
|
143 | var dialect = file.getDialect();
|
144 | file.iterateNodesByType('MemberExpression', function(node) {
|
145 | if (!node.computed || node.property.type !== 'StringLiteral') {
|
146 | return;
|
147 | }
|
148 |
|
149 | var value = node.property.value;
|
150 | if (// allow numbers, nulls, and anything else
|
151 | typeof value !== 'string' ||
|
152 | // allow invalid identifiers
|
153 | !utils.isValidIdentifierName(value, file.getDialect()) ||
|
154 | // allow quoted snake cased identifiers if allExcept: ['snake_case']
|
155 | (exceptSnakeCase && utils.isSnakeCased(utils.trimUnderscores(value))) ||
|
156 | // allow quoted reserved words if allExcept: ['keywords']
|
157 | ((dialect === 'es3' || exceptKeywords) && reservedWords.check(value, dialect, true))
|
158 | ) {
|
159 | return;
|
160 | }
|
161 |
|
162 | errors.add('Use dot notation instead of brackets for member expressions', node.property);
|
163 | });
|
164 | }
|
165 |
|
166 | };
|