UNPKG

4.74 kBJavaScriptView Raw
1var assign = require('lodash').assign;
2
3var BLOCK_REGEXP = /^\s*(?:jscs\s*:\s*(en|dis)able)(.*)/;
4var LINE_REGEXP = /^\s*(?:jscs\s*:\s*ignore)(.*)/;
5
6/**
7 * Parses rule names in enable/disable/ignore statements.
8 *
9 * @param {String} text
10 * @param {Boolean} enabled
11 * @returns {Object}
12 */
13function parseRuleNames(text, enabled) {
14 text = text.trim();
15
16 if (!text) {
17 return {'*': enabled};
18 }
19
20 return text.split(',').reduce(function(result, ruleName) {
21 ruleName = ruleName.trim();
22 if (ruleName) {
23 result[ruleName] = enabled;
24 }
25 return result;
26 }, {});
27}
28
29/**
30 * Pragma index implementation.
31 * Checks if rule is enabled or disabled for the specified element.
32 *
33 * @param {Element} firstToken
34 * @constructor
35 */
36function TokenIndex(firstToken) {
37 this._buildIndex(firstToken);
38}
39
40/**
41 * Builds pragma index.
42 *
43 * @param {Element} firstToken
44 * @private
45 */
46TokenIndex.prototype._buildIndex = function(firstToken) {
47 this._hasPragmas = false;
48
49 var tokens = [];
50 var index = [];
51 var positions = [];
52 var currentPosition = 0;
53 var currentToken = firstToken;
54 var lastBlockState = {'*': true};
55 var tokenState;
56 var previousLoc = {line: 1, column: 0};
57
58 while (currentToken) {
59 tokens.push(currentToken);
60 currentToken.__loc = previousLoc;
61
62 var newlineCount = currentToken.getNewlineCount();
63 if (newlineCount > 0) {
64 var lines = currentToken.getSourceCodeLines();
65 previousLoc = {
66 line: previousLoc.line + newlineCount,
67 column: lines[lines.length - 1].length
68 };
69 } else {
70 previousLoc = {
71 line: previousLoc.line,
72 column: previousLoc.column + currentToken.getSourceCodeLength()
73 };
74 }
75
76 if (currentToken.isComment) {
77 var value = currentToken.value;
78 var blockMatch = BLOCK_REGEXP.exec(value);
79 if (blockMatch) {
80 this._hasPragmas = true;
81 lastBlockState = assign({}, lastBlockState, parseRuleNames(blockMatch[2], blockMatch[1] === 'en'));
82 tokenState = lastBlockState;
83 } else {
84 var lineMatch = LINE_REGEXP.exec(value);
85 if (lineMatch) {
86 this._hasPragmas = true;
87 var ignoreState = parseRuleNames(lineMatch[1], false);
88 index.push(null);
89 var ignoreToken = currentToken.getPreviousToken();
90 var i = index.length - 1;
91 while (ignoreToken) {
92 i--;
93 index[i] = assign({}, index[i], ignoreState);
94 if (ignoreToken.getNewlineCount() > 0) {
95 break;
96 }
97 ignoreToken = ignoreToken.getPreviousToken();
98 }
99 ignoreToken = currentToken.getNextToken();
100 while (ignoreToken) {
101 index.push(ignoreState);
102 if (ignoreToken.getNewlineCount() > 0) {
103 break;
104 }
105 ignoreToken = ignoreToken.getNextToken();
106 }
107 tokenState = assign({}, lastBlockState, ignoreState);
108 } else {
109 tokenState = lastBlockState;
110 }
111 }
112 } else {
113 tokenState = lastBlockState;
114 }
115
116 if (index[currentPosition]) {
117 tokenState = assign({}, tokenState, index[currentPosition]);
118 }
119
120 index[currentPosition] = tokenState;
121 currentPosition++;
122
123 currentToken = currentToken.getNextToken();
124 }
125 this._tokens = tokens;
126 this._index = index;
127 this._positions = positions;
128};
129
130/**
131 * Checks if rule whether rule is enabled for the specified element.
132 *
133 * @param {String} ruleName
134 * @param {Element} element
135 * @returns {Boolean}
136 */
137TokenIndex.prototype.isRuleEnabled = function(ruleName, element) {
138 if (!this._hasPragmas) {
139 return true;
140 }
141 var pos = this._tokens.indexOf(element.getFirstToken());
142 if (pos !== -1) {
143 var state = this._index[pos];
144 if (ruleName in state) {
145 return state[ruleName];
146 }
147
148 return state['*'];
149 }
150
151 return true;
152};
153
154/**
155 * Return calculated element location.
156 *
157 * @param {Element} element
158 * @returns {Object}
159 */
160TokenIndex.prototype.getElementLoc = function(element) {
161 return element.getFirstToken().__loc || {
162 line: 1,
163 column: 0
164 };
165};
166
167module.exports = TokenIndex;
168