vscode-css-languageservice 5.4.2 → 6.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (84) hide show
  1. package/CHANGELOG.md +5 -1
  2. package/lib/esm/cssLanguageService.d.ts +37 -37
  3. package/lib/esm/cssLanguageService.js +72 -75
  4. package/lib/esm/cssLanguageTypes.d.ts +238 -238
  5. package/lib/esm/cssLanguageTypes.js +42 -42
  6. package/lib/esm/data/webCustomData.js +21965 -21965
  7. package/lib/esm/languageFacts/builtinData.js +142 -142
  8. package/lib/esm/languageFacts/colors.js +469 -472
  9. package/lib/esm/languageFacts/dataManager.js +88 -92
  10. package/lib/esm/languageFacts/dataProvider.js +73 -79
  11. package/lib/esm/languageFacts/entry.js +137 -138
  12. package/lib/esm/languageFacts/facts.js +8 -8
  13. package/lib/esm/parser/cssErrors.js +48 -50
  14. package/lib/esm/parser/cssNodes.js +1502 -2019
  15. package/lib/esm/parser/cssParser.js +1534 -1563
  16. package/lib/esm/parser/cssScanner.js +592 -599
  17. package/lib/esm/parser/cssSymbolScope.js +311 -341
  18. package/lib/esm/parser/lessParser.js +714 -740
  19. package/lib/esm/parser/lessScanner.js +57 -78
  20. package/lib/esm/parser/scssErrors.js +18 -20
  21. package/lib/esm/parser/scssParser.js +796 -818
  22. package/lib/esm/parser/scssScanner.js +95 -116
  23. package/lib/esm/services/cssCodeActions.js +77 -81
  24. package/lib/esm/services/cssCompletion.js +1054 -1149
  25. package/lib/esm/services/cssFolding.js +190 -193
  26. package/lib/esm/services/cssFormatter.js +136 -136
  27. package/lib/esm/services/cssHover.js +148 -151
  28. package/lib/esm/services/cssNavigation.js +378 -470
  29. package/lib/esm/services/cssSelectionRange.js +47 -47
  30. package/lib/esm/services/cssValidation.js +41 -44
  31. package/lib/esm/services/lessCompletion.js +378 -397
  32. package/lib/esm/services/lint.js +518 -532
  33. package/lib/esm/services/lintRules.js +76 -83
  34. package/lib/esm/services/lintUtil.js +196 -205
  35. package/lib/esm/services/pathCompletion.js +157 -231
  36. package/lib/esm/services/scssCompletion.js +354 -378
  37. package/lib/esm/services/scssNavigation.js +82 -154
  38. package/lib/esm/services/selectorPrinting.js +492 -536
  39. package/lib/esm/utils/arrays.js +40 -46
  40. package/lib/esm/utils/objects.js +11 -11
  41. package/lib/esm/utils/resources.js +11 -24
  42. package/lib/esm/utils/strings.js +102 -104
  43. package/lib/umd/cssLanguageService.d.ts +37 -37
  44. package/lib/umd/cssLanguageService.js +99 -102
  45. package/lib/umd/cssLanguageTypes.d.ts +238 -238
  46. package/lib/umd/cssLanguageTypes.js +89 -88
  47. package/lib/umd/data/webCustomData.js +21978 -21978
  48. package/lib/umd/languageFacts/builtinData.js +154 -154
  49. package/lib/umd/languageFacts/colors.js +492 -495
  50. package/lib/umd/languageFacts/dataManager.js +101 -104
  51. package/lib/umd/languageFacts/dataProvider.js +86 -91
  52. package/lib/umd/languageFacts/entry.js +152 -153
  53. package/lib/umd/languageFacts/facts.js +29 -29
  54. package/lib/umd/parser/cssErrors.js +61 -62
  55. package/lib/umd/parser/cssNodes.js +1587 -2034
  56. package/lib/umd/parser/cssParser.js +1547 -1575
  57. package/lib/umd/parser/cssScanner.js +606 -611
  58. package/lib/umd/parser/cssSymbolScope.js +328 -353
  59. package/lib/umd/parser/lessParser.js +727 -752
  60. package/lib/umd/parser/lessScanner.js +70 -90
  61. package/lib/umd/parser/scssErrors.js +31 -32
  62. package/lib/umd/parser/scssParser.js +809 -830
  63. package/lib/umd/parser/scssScanner.js +108 -128
  64. package/lib/umd/services/cssCodeActions.js +90 -93
  65. package/lib/umd/services/cssCompletion.js +1067 -1161
  66. package/lib/umd/services/cssFolding.js +203 -206
  67. package/lib/umd/services/cssFormatter.js +150 -150
  68. package/lib/umd/services/cssHover.js +161 -163
  69. package/lib/umd/services/cssNavigation.js +391 -482
  70. package/lib/umd/services/cssSelectionRange.js +60 -60
  71. package/lib/umd/services/cssValidation.js +54 -56
  72. package/lib/umd/services/lessCompletion.js +391 -409
  73. package/lib/umd/services/lint.js +531 -544
  74. package/lib/umd/services/lintRules.js +91 -95
  75. package/lib/umd/services/lintUtil.js +210 -218
  76. package/lib/umd/services/pathCompletion.js +171 -244
  77. package/lib/umd/services/scssCompletion.js +367 -390
  78. package/lib/umd/services/scssNavigation.js +95 -166
  79. package/lib/umd/services/selectorPrinting.js +510 -550
  80. package/lib/umd/utils/arrays.js +55 -61
  81. package/lib/umd/utils/objects.js +25 -25
  82. package/lib/umd/utils/resources.js +26 -39
  83. package/lib/umd/utils/strings.js +120 -122
  84. package/package.json +10 -10
@@ -1,532 +1,518 @@
1
- /*---------------------------------------------------------------------------------------------
2
- * Copyright (c) Microsoft Corporation. All rights reserved.
3
- * Licensed under the MIT License. See License.txt in the project root for license information.
4
- *--------------------------------------------------------------------------------------------*/
5
- 'use strict';
6
- import * as nls from 'vscode-nls';
7
- import * as languageFacts from '../languageFacts/facts';
8
- import * as nodes from '../parser/cssNodes';
9
- import { union } from '../utils/arrays';
10
- import { Rules, Settings } from './lintRules';
11
- import calculateBoxModel, { Element } from './lintUtil';
12
- var localize = nls.loadMessageBundle();
13
- var NodesByRootMap = /** @class */ (function () {
14
- function NodesByRootMap() {
15
- this.data = {};
16
- }
17
- NodesByRootMap.prototype.add = function (root, name, node) {
18
- var entry = this.data[root];
19
- if (!entry) {
20
- entry = { nodes: [], names: [] };
21
- this.data[root] = entry;
22
- }
23
- entry.names.push(name);
24
- if (node) {
25
- entry.nodes.push(node);
26
- }
27
- };
28
- return NodesByRootMap;
29
- }());
30
- var LintVisitor = /** @class */ (function () {
31
- function LintVisitor(document, settings, cssDataManager) {
32
- var _this = this;
33
- this.cssDataManager = cssDataManager;
34
- this.warnings = [];
35
- this.settings = settings;
36
- this.documentText = document.getText();
37
- this.keyframes = new NodesByRootMap();
38
- this.validProperties = {};
39
- var properties = settings.getSetting(Settings.ValidProperties);
40
- if (Array.isArray(properties)) {
41
- properties.forEach(function (p) {
42
- if (typeof p === 'string') {
43
- var name = p.trim().toLowerCase();
44
- if (name.length) {
45
- _this.validProperties[name] = true;
46
- }
47
- }
48
- });
49
- }
50
- }
51
- LintVisitor.entries = function (node, document, settings, cssDataManager, entryFilter) {
52
- var visitor = new LintVisitor(document, settings, cssDataManager);
53
- node.acceptVisitor(visitor);
54
- visitor.completeValidations();
55
- return visitor.getEntries(entryFilter);
56
- };
57
- LintVisitor.prototype.isValidPropertyDeclaration = function (element) {
58
- var propertyName = element.fullPropertyName;
59
- return this.validProperties[propertyName];
60
- };
61
- LintVisitor.prototype.fetch = function (input, s) {
62
- var elements = [];
63
- for (var _i = 0, input_1 = input; _i < input_1.length; _i++) {
64
- var curr = input_1[_i];
65
- if (curr.fullPropertyName === s) {
66
- elements.push(curr);
67
- }
68
- }
69
- return elements;
70
- };
71
- LintVisitor.prototype.fetchWithValue = function (input, s, v) {
72
- var elements = [];
73
- for (var _i = 0, input_2 = input; _i < input_2.length; _i++) {
74
- var inputElement = input_2[_i];
75
- if (inputElement.fullPropertyName === s) {
76
- var expression = inputElement.node.getValue();
77
- if (expression && this.findValueInExpression(expression, v)) {
78
- elements.push(inputElement);
79
- }
80
- }
81
- }
82
- return elements;
83
- };
84
- LintVisitor.prototype.findValueInExpression = function (expression, v) {
85
- var found = false;
86
- expression.accept(function (node) {
87
- if (node.type === nodes.NodeType.Identifier && node.matches(v)) {
88
- found = true;
89
- }
90
- return !found;
91
- });
92
- return found;
93
- };
94
- LintVisitor.prototype.getEntries = function (filter) {
95
- if (filter === void 0) { filter = (nodes.Level.Warning | nodes.Level.Error); }
96
- return this.warnings.filter(function (entry) {
97
- return (entry.getLevel() & filter) !== 0;
98
- });
99
- };
100
- LintVisitor.prototype.addEntry = function (node, rule, details) {
101
- var entry = new nodes.Marker(node, rule, this.settings.getRule(rule), details);
102
- this.warnings.push(entry);
103
- };
104
- LintVisitor.prototype.getMissingNames = function (expected, actual) {
105
- var expectedClone = expected.slice(0); // clone
106
- for (var i = 0; i < actual.length; i++) {
107
- var k = expectedClone.indexOf(actual[i]);
108
- if (k !== -1) {
109
- expectedClone[k] = null;
110
- }
111
- }
112
- var result = null;
113
- for (var i = 0; i < expectedClone.length; i++) {
114
- var curr = expectedClone[i];
115
- if (curr) {
116
- if (result === null) {
117
- result = localize('namelist.single', "'{0}'", curr);
118
- }
119
- else {
120
- result = localize('namelist.concatenated', "{0}, '{1}'", result, curr);
121
- }
122
- }
123
- }
124
- return result;
125
- };
126
- LintVisitor.prototype.visitNode = function (node) {
127
- switch (node.type) {
128
- case nodes.NodeType.UnknownAtRule:
129
- return this.visitUnknownAtRule(node);
130
- case nodes.NodeType.Keyframe:
131
- return this.visitKeyframe(node);
132
- case nodes.NodeType.FontFace:
133
- return this.visitFontFace(node);
134
- case nodes.NodeType.Ruleset:
135
- return this.visitRuleSet(node);
136
- case nodes.NodeType.SimpleSelector:
137
- return this.visitSimpleSelector(node);
138
- case nodes.NodeType.Function:
139
- return this.visitFunction(node);
140
- case nodes.NodeType.NumericValue:
141
- return this.visitNumericValue(node);
142
- case nodes.NodeType.Import:
143
- return this.visitImport(node);
144
- case nodes.NodeType.HexColorValue:
145
- return this.visitHexColorValue(node);
146
- case nodes.NodeType.Prio:
147
- return this.visitPrio(node);
148
- case nodes.NodeType.IdentifierSelector:
149
- return this.visitIdentifierSelector(node);
150
- }
151
- return true;
152
- };
153
- LintVisitor.prototype.completeValidations = function () {
154
- this.validateKeyframes();
155
- };
156
- LintVisitor.prototype.visitUnknownAtRule = function (node) {
157
- var atRuleName = node.getChild(0);
158
- if (!atRuleName) {
159
- return false;
160
- }
161
- var atDirective = this.cssDataManager.getAtDirective(atRuleName.getText());
162
- if (atDirective) {
163
- return false;
164
- }
165
- this.addEntry(atRuleName, Rules.UnknownAtRules, "Unknown at rule ".concat(atRuleName.getText()));
166
- return true;
167
- };
168
- LintVisitor.prototype.visitKeyframe = function (node) {
169
- var keyword = node.getKeyword();
170
- if (!keyword) {
171
- return false;
172
- }
173
- var text = keyword.getText();
174
- this.keyframes.add(node.getName(), text, (text !== '@keyframes') ? keyword : null);
175
- return true;
176
- };
177
- LintVisitor.prototype.validateKeyframes = function () {
178
- // @keyframe and it's vendor specific alternatives
179
- // @keyframe should be included
180
- var expected = ['@-webkit-keyframes', '@-moz-keyframes', '@-o-keyframes'];
181
- for (var name in this.keyframes.data) {
182
- var actual = this.keyframes.data[name].names;
183
- var needsStandard = (actual.indexOf('@keyframes') === -1);
184
- if (!needsStandard && actual.length === 1) {
185
- continue; // only the non-vendor specific keyword is used, that's fine, no warning
186
- }
187
- var missingVendorSpecific = this.getMissingNames(expected, actual);
188
- if (missingVendorSpecific || needsStandard) {
189
- for (var _i = 0, _a = this.keyframes.data[name].nodes; _i < _a.length; _i++) {
190
- var node = _a[_i];
191
- if (needsStandard) {
192
- var message = localize('keyframes.standardrule.missing', "Always define standard rule '@keyframes' when defining keyframes.");
193
- this.addEntry(node, Rules.IncludeStandardPropertyWhenUsingVendorPrefix, message);
194
- }
195
- if (missingVendorSpecific) {
196
- var message = localize('keyframes.vendorspecific.missing', "Always include all vendor specific rules: Missing: {0}", missingVendorSpecific);
197
- this.addEntry(node, Rules.AllVendorPrefixes, message);
198
- }
199
- }
200
- }
201
- }
202
- return true;
203
- };
204
- LintVisitor.prototype.visitSimpleSelector = function (node) {
205
- /////////////////////////////////////////////////////////////
206
- // Lint - The universal selector (*) is known to be slow.
207
- /////////////////////////////////////////////////////////////
208
- var firstChar = this.documentText.charAt(node.offset);
209
- if (node.length === 1 && firstChar === '*') {
210
- this.addEntry(node, Rules.UniversalSelector);
211
- }
212
- return true;
213
- };
214
- LintVisitor.prototype.visitIdentifierSelector = function (node) {
215
- /////////////////////////////////////////////////////////////
216
- // Lint - Avoid id selectors
217
- /////////////////////////////////////////////////////////////
218
- this.addEntry(node, Rules.AvoidIdSelector);
219
- return true;
220
- };
221
- LintVisitor.prototype.visitImport = function (node) {
222
- /////////////////////////////////////////////////////////////
223
- // Lint - Import statements shouldn't be used, because they aren't offering parallel downloads.
224
- /////////////////////////////////////////////////////////////
225
- this.addEntry(node, Rules.ImportStatemement);
226
- return true;
227
- };
228
- LintVisitor.prototype.visitRuleSet = function (node) {
229
- /////////////////////////////////////////////////////////////
230
- // Lint - Don't use empty rulesets.
231
- /////////////////////////////////////////////////////////////
232
- var declarations = node.getDeclarations();
233
- if (!declarations) {
234
- // syntax error
235
- return false;
236
- }
237
- if (!declarations.hasChildren()) {
238
- this.addEntry(node.getSelectors(), Rules.EmptyRuleSet);
239
- }
240
- var propertyTable = [];
241
- for (var _i = 0, _a = declarations.getChildren(); _i < _a.length; _i++) {
242
- var element = _a[_i];
243
- if (element instanceof nodes.Declaration) {
244
- propertyTable.push(new Element(element));
245
- }
246
- }
247
- /////////////////////////////////////////////////////////////
248
- // the rule warns when it finds:
249
- // width being used with border, border-left, border-right, padding, padding-left, or padding-right
250
- // height being used with border, border-top, border-bottom, padding, padding-top, or padding-bottom
251
- // No error when box-sizing property is specified, as it assumes the user knows what he's doing.
252
- // see https://github.com/CSSLint/csslint/wiki/Beware-of-box-model-size
253
- /////////////////////////////////////////////////////////////
254
- var boxModel = calculateBoxModel(propertyTable);
255
- if (boxModel.width) {
256
- var properties = [];
257
- if (boxModel.right.value) {
258
- properties = union(properties, boxModel.right.properties);
259
- }
260
- if (boxModel.left.value) {
261
- properties = union(properties, boxModel.left.properties);
262
- }
263
- if (properties.length !== 0) {
264
- for (var _b = 0, properties_1 = properties; _b < properties_1.length; _b++) {
265
- var item = properties_1[_b];
266
- this.addEntry(item.node, Rules.BewareOfBoxModelSize);
267
- }
268
- this.addEntry(boxModel.width.node, Rules.BewareOfBoxModelSize);
269
- }
270
- }
271
- if (boxModel.height) {
272
- var properties = [];
273
- if (boxModel.top.value) {
274
- properties = union(properties, boxModel.top.properties);
275
- }
276
- if (boxModel.bottom.value) {
277
- properties = union(properties, boxModel.bottom.properties);
278
- }
279
- if (properties.length !== 0) {
280
- for (var _c = 0, properties_2 = properties; _c < properties_2.length; _c++) {
281
- var item = properties_2[_c];
282
- this.addEntry(item.node, Rules.BewareOfBoxModelSize);
283
- }
284
- this.addEntry(boxModel.height.node, Rules.BewareOfBoxModelSize);
285
- }
286
- }
287
- /////////////////////////////////////////////////////////////
288
- // Properties ignored due to display
289
- /////////////////////////////////////////////////////////////
290
- // With 'display: inline-block', 'float' has no effect
291
- var displayElems = this.fetchWithValue(propertyTable, 'display', 'inline-block');
292
- if (displayElems.length > 0) {
293
- var elem = this.fetch(propertyTable, 'float');
294
- for (var index = 0; index < elem.length; index++) {
295
- var node_1 = elem[index].node;
296
- var value = node_1.getValue();
297
- if (value && !value.matches('none')) {
298
- this.addEntry(node_1, Rules.PropertyIgnoredDueToDisplay, localize('rule.propertyIgnoredDueToDisplayInlineBlock', "inline-block is ignored due to the float. If 'float' has a value other than 'none', the box is floated and 'display' is treated as 'block'"));
299
- }
300
- }
301
- }
302
- // With 'display: block', 'vertical-align' has no effect
303
- displayElems = this.fetchWithValue(propertyTable, 'display', 'block');
304
- if (displayElems.length > 0) {
305
- var elem = this.fetch(propertyTable, 'vertical-align');
306
- for (var index = 0; index < elem.length; index++) {
307
- this.addEntry(elem[index].node, Rules.PropertyIgnoredDueToDisplay, localize('rule.propertyIgnoredDueToDisplayBlock', "Property is ignored due to the display. With 'display: block', vertical-align should not be used."));
308
- }
309
- }
310
- /////////////////////////////////////////////////////////////
311
- // Avoid 'float'
312
- /////////////////////////////////////////////////////////////
313
- var elements = this.fetch(propertyTable, 'float');
314
- for (var index = 0; index < elements.length; index++) {
315
- var element = elements[index];
316
- if (!this.isValidPropertyDeclaration(element)) {
317
- this.addEntry(element.node, Rules.AvoidFloat);
318
- }
319
- }
320
- /////////////////////////////////////////////////////////////
321
- // Don't use duplicate declarations.
322
- /////////////////////////////////////////////////////////////
323
- for (var i = 0; i < propertyTable.length; i++) {
324
- var element = propertyTable[i];
325
- if (element.fullPropertyName !== 'background' && !this.validProperties[element.fullPropertyName]) {
326
- var value = element.node.getValue();
327
- if (value && this.documentText.charAt(value.offset) !== '-') {
328
- var elements_1 = this.fetch(propertyTable, element.fullPropertyName);
329
- if (elements_1.length > 1) {
330
- for (var k = 0; k < elements_1.length; k++) {
331
- var value_1 = elements_1[k].node.getValue();
332
- if (value_1 && this.documentText.charAt(value_1.offset) !== '-' && elements_1[k] !== element) {
333
- this.addEntry(element.node, Rules.DuplicateDeclarations);
334
- }
335
- }
336
- }
337
- }
338
- }
339
- }
340
- /////////////////////////////////////////////////////////////
341
- // Unknown propery & When using a vendor-prefixed gradient, make sure to use them all.
342
- /////////////////////////////////////////////////////////////
343
- var isExportBlock = node.getSelectors().matches(":export");
344
- if (!isExportBlock) {
345
- var propertiesBySuffix = new NodesByRootMap();
346
- var containsUnknowns = false;
347
- for (var _d = 0, propertyTable_1 = propertyTable; _d < propertyTable_1.length; _d++) {
348
- var element = propertyTable_1[_d];
349
- var decl = element.node;
350
- if (this.isCSSDeclaration(decl)) {
351
- var name = element.fullPropertyName;
352
- var firstChar = name.charAt(0);
353
- if (firstChar === '-') {
354
- if (name.charAt(1) !== '-') { // avoid css variables
355
- if (!this.cssDataManager.isKnownProperty(name) && !this.validProperties[name]) {
356
- this.addEntry(decl.getProperty(), Rules.UnknownVendorSpecificProperty);
357
- }
358
- var nonPrefixedName = decl.getNonPrefixedPropertyName();
359
- propertiesBySuffix.add(nonPrefixedName, name, decl.getProperty());
360
- }
361
- }
362
- else {
363
- var fullName = name;
364
- if (firstChar === '*' || firstChar === '_') {
365
- this.addEntry(decl.getProperty(), Rules.IEStarHack);
366
- name = name.substr(1);
367
- }
368
- // _property and *property might be contributed via custom data
369
- if (!this.cssDataManager.isKnownProperty(fullName) && !this.cssDataManager.isKnownProperty(name)) {
370
- if (!this.validProperties[name]) {
371
- this.addEntry(decl.getProperty(), Rules.UnknownProperty, localize('property.unknownproperty.detailed', "Unknown property: '{0}'", decl.getFullPropertyName()));
372
- }
373
- }
374
- propertiesBySuffix.add(name, name, null); // don't pass the node as we don't show errors on the standard
375
- }
376
- }
377
- else {
378
- containsUnknowns = true;
379
- }
380
- }
381
- if (!containsUnknowns) { // don't perform this test if there are
382
- for (var suffix in propertiesBySuffix.data) {
383
- var entry = propertiesBySuffix.data[suffix];
384
- var actual = entry.names;
385
- var needsStandard = this.cssDataManager.isStandardProperty(suffix) && (actual.indexOf(suffix) === -1);
386
- if (!needsStandard && actual.length === 1) {
387
- continue; // only the non-vendor specific rule is used, that's fine, no warning
388
- }
389
- var expected = [];
390
- for (var i = 0, len = LintVisitor.prefixes.length; i < len; i++) {
391
- var prefix = LintVisitor.prefixes[i];
392
- if (this.cssDataManager.isStandardProperty(prefix + suffix)) {
393
- expected.push(prefix + suffix);
394
- }
395
- }
396
- var missingVendorSpecific = this.getMissingNames(expected, actual);
397
- if (missingVendorSpecific || needsStandard) {
398
- for (var _e = 0, _f = entry.nodes; _e < _f.length; _e++) {
399
- var node_2 = _f[_e];
400
- if (needsStandard) {
401
- var message = localize('property.standard.missing', "Also define the standard property '{0}' for compatibility", suffix);
402
- this.addEntry(node_2, Rules.IncludeStandardPropertyWhenUsingVendorPrefix, message);
403
- }
404
- if (missingVendorSpecific) {
405
- var message = localize('property.vendorspecific.missing', "Always include all vendor specific properties: Missing: {0}", missingVendorSpecific);
406
- this.addEntry(node_2, Rules.AllVendorPrefixes, message);
407
- }
408
- }
409
- }
410
- }
411
- }
412
- }
413
- return true;
414
- };
415
- LintVisitor.prototype.visitPrio = function (node) {
416
- /////////////////////////////////////////////////////////////
417
- // Don't use !important
418
- /////////////////////////////////////////////////////////////
419
- this.addEntry(node, Rules.AvoidImportant);
420
- return true;
421
- };
422
- LintVisitor.prototype.visitNumericValue = function (node) {
423
- /////////////////////////////////////////////////////////////
424
- // 0 has no following unit
425
- /////////////////////////////////////////////////////////////
426
- var funcDecl = node.findParent(nodes.NodeType.Function);
427
- if (funcDecl && funcDecl.getName() === 'calc') {
428
- return true;
429
- }
430
- var decl = node.findParent(nodes.NodeType.Declaration);
431
- if (decl) {
432
- var declValue = decl.getValue();
433
- if (declValue) {
434
- var value = node.getValue();
435
- if (!value.unit || languageFacts.units.length.indexOf(value.unit.toLowerCase()) === -1) {
436
- return true;
437
- }
438
- if (parseFloat(value.value) === 0.0 && !!value.unit && !this.validProperties[decl.getFullPropertyName()]) {
439
- this.addEntry(node, Rules.ZeroWithUnit);
440
- }
441
- }
442
- }
443
- return true;
444
- };
445
- LintVisitor.prototype.visitFontFace = function (node) {
446
- var declarations = node.getDeclarations();
447
- if (!declarations) {
448
- // syntax error
449
- return false;
450
- }
451
- var definesSrc = false, definesFontFamily = false;
452
- var containsUnknowns = false;
453
- for (var _i = 0, _a = declarations.getChildren(); _i < _a.length; _i++) {
454
- var node_3 = _a[_i];
455
- if (this.isCSSDeclaration(node_3)) {
456
- var name = node_3.getProperty().getName().toLowerCase();
457
- if (name === 'src') {
458
- definesSrc = true;
459
- }
460
- if (name === 'font-family') {
461
- definesFontFamily = true;
462
- }
463
- }
464
- else {
465
- containsUnknowns = true;
466
- }
467
- }
468
- if (!containsUnknowns && (!definesSrc || !definesFontFamily)) {
469
- this.addEntry(node, Rules.RequiredPropertiesForFontFace);
470
- }
471
- return true;
472
- };
473
- LintVisitor.prototype.isCSSDeclaration = function (node) {
474
- if (node instanceof nodes.Declaration) {
475
- if (!node.getValue()) {
476
- return false;
477
- }
478
- var property = node.getProperty();
479
- if (!property) {
480
- return false;
481
- }
482
- var identifier = property.getIdentifier();
483
- if (!identifier || identifier.containsInterpolation()) {
484
- return false;
485
- }
486
- return true;
487
- }
488
- return false;
489
- };
490
- LintVisitor.prototype.visitHexColorValue = function (node) {
491
- // Rule: #eeff0011 or #eeff00 or #ef01 or #ef0
492
- var length = node.length;
493
- if (length !== 9 && length !== 7 && length !== 5 && length !== 4) {
494
- this.addEntry(node, Rules.HexColorLength);
495
- }
496
- return false;
497
- };
498
- LintVisitor.prototype.visitFunction = function (node) {
499
- var fnName = node.getName().toLowerCase();
500
- var expectedAttrCount = -1;
501
- var actualAttrCount = 0;
502
- switch (fnName) {
503
- case 'rgb(':
504
- case 'hsl(':
505
- expectedAttrCount = 3;
506
- break;
507
- case 'rgba(':
508
- case 'hsla(':
509
- expectedAttrCount = 4;
510
- break;
511
- }
512
- if (expectedAttrCount !== -1) {
513
- node.getArguments().accept(function (n) {
514
- if (n instanceof nodes.BinaryExpression) {
515
- actualAttrCount += 1;
516
- return false;
517
- }
518
- return true;
519
- });
520
- if (actualAttrCount !== expectedAttrCount) {
521
- this.addEntry(node, Rules.ArgsInColorFunction);
522
- }
523
- }
524
- return true;
525
- };
526
- LintVisitor.prefixes = [
527
- '-ms-', '-moz-', '-o-', '-webkit-', // Quite common
528
- // '-xv-', '-atsc-', '-wap-', '-khtml-', 'mso-', 'prince-', '-ah-', '-hp-', '-ro-', '-rim-', '-tc-' // Quite un-common
529
- ];
530
- return LintVisitor;
531
- }());
532
- export { LintVisitor };
1
+ /*---------------------------------------------------------------------------------------------
2
+ * Copyright (c) Microsoft Corporation. All rights reserved.
3
+ * Licensed under the MIT License. See License.txt in the project root for license information.
4
+ *--------------------------------------------------------------------------------------------*/
5
+ 'use strict';
6
+ import * as nls from 'vscode-nls';
7
+ import * as languageFacts from '../languageFacts/facts';
8
+ import * as nodes from '../parser/cssNodes';
9
+ import { union } from '../utils/arrays';
10
+ import { Rules, Settings } from './lintRules';
11
+ import calculateBoxModel, { Element } from './lintUtil';
12
+ const localize = nls.loadMessageBundle();
13
+ class NodesByRootMap {
14
+ constructor() {
15
+ this.data = {};
16
+ }
17
+ add(root, name, node) {
18
+ let entry = this.data[root];
19
+ if (!entry) {
20
+ entry = { nodes: [], names: [] };
21
+ this.data[root] = entry;
22
+ }
23
+ entry.names.push(name);
24
+ if (node) {
25
+ entry.nodes.push(node);
26
+ }
27
+ }
28
+ }
29
+ export class LintVisitor {
30
+ constructor(document, settings, cssDataManager) {
31
+ this.cssDataManager = cssDataManager;
32
+ this.warnings = [];
33
+ this.settings = settings;
34
+ this.documentText = document.getText();
35
+ this.keyframes = new NodesByRootMap();
36
+ this.validProperties = {};
37
+ const properties = settings.getSetting(Settings.ValidProperties);
38
+ if (Array.isArray(properties)) {
39
+ properties.forEach((p) => {
40
+ if (typeof p === 'string') {
41
+ const name = p.trim().toLowerCase();
42
+ if (name.length) {
43
+ this.validProperties[name] = true;
44
+ }
45
+ }
46
+ });
47
+ }
48
+ }
49
+ static entries(node, document, settings, cssDataManager, entryFilter) {
50
+ const visitor = new LintVisitor(document, settings, cssDataManager);
51
+ node.acceptVisitor(visitor);
52
+ visitor.completeValidations();
53
+ return visitor.getEntries(entryFilter);
54
+ }
55
+ isValidPropertyDeclaration(element) {
56
+ const propertyName = element.fullPropertyName;
57
+ return this.validProperties[propertyName];
58
+ }
59
+ fetch(input, s) {
60
+ const elements = [];
61
+ for (const curr of input) {
62
+ if (curr.fullPropertyName === s) {
63
+ elements.push(curr);
64
+ }
65
+ }
66
+ return elements;
67
+ }
68
+ fetchWithValue(input, s, v) {
69
+ const elements = [];
70
+ for (const inputElement of input) {
71
+ if (inputElement.fullPropertyName === s) {
72
+ const expression = inputElement.node.getValue();
73
+ if (expression && this.findValueInExpression(expression, v)) {
74
+ elements.push(inputElement);
75
+ }
76
+ }
77
+ }
78
+ return elements;
79
+ }
80
+ findValueInExpression(expression, v) {
81
+ let found = false;
82
+ expression.accept(node => {
83
+ if (node.type === nodes.NodeType.Identifier && node.matches(v)) {
84
+ found = true;
85
+ }
86
+ return !found;
87
+ });
88
+ return found;
89
+ }
90
+ getEntries(filter = (nodes.Level.Warning | nodes.Level.Error)) {
91
+ return this.warnings.filter(entry => {
92
+ return (entry.getLevel() & filter) !== 0;
93
+ });
94
+ }
95
+ addEntry(node, rule, details) {
96
+ const entry = new nodes.Marker(node, rule, this.settings.getRule(rule), details);
97
+ this.warnings.push(entry);
98
+ }
99
+ getMissingNames(expected, actual) {
100
+ const expectedClone = expected.slice(0); // clone
101
+ for (let i = 0; i < actual.length; i++) {
102
+ const k = expectedClone.indexOf(actual[i]);
103
+ if (k !== -1) {
104
+ expectedClone[k] = null;
105
+ }
106
+ }
107
+ let result = null;
108
+ for (let i = 0; i < expectedClone.length; i++) {
109
+ const curr = expectedClone[i];
110
+ if (curr) {
111
+ if (result === null) {
112
+ result = localize('namelist.single', "'{0}'", curr);
113
+ }
114
+ else {
115
+ result = localize('namelist.concatenated', "{0}, '{1}'", result, curr);
116
+ }
117
+ }
118
+ }
119
+ return result;
120
+ }
121
+ visitNode(node) {
122
+ switch (node.type) {
123
+ case nodes.NodeType.UnknownAtRule:
124
+ return this.visitUnknownAtRule(node);
125
+ case nodes.NodeType.Keyframe:
126
+ return this.visitKeyframe(node);
127
+ case nodes.NodeType.FontFace:
128
+ return this.visitFontFace(node);
129
+ case nodes.NodeType.Ruleset:
130
+ return this.visitRuleSet(node);
131
+ case nodes.NodeType.SimpleSelector:
132
+ return this.visitSimpleSelector(node);
133
+ case nodes.NodeType.Function:
134
+ return this.visitFunction(node);
135
+ case nodes.NodeType.NumericValue:
136
+ return this.visitNumericValue(node);
137
+ case nodes.NodeType.Import:
138
+ return this.visitImport(node);
139
+ case nodes.NodeType.HexColorValue:
140
+ return this.visitHexColorValue(node);
141
+ case nodes.NodeType.Prio:
142
+ return this.visitPrio(node);
143
+ case nodes.NodeType.IdentifierSelector:
144
+ return this.visitIdentifierSelector(node);
145
+ }
146
+ return true;
147
+ }
148
+ completeValidations() {
149
+ this.validateKeyframes();
150
+ }
151
+ visitUnknownAtRule(node) {
152
+ const atRuleName = node.getChild(0);
153
+ if (!atRuleName) {
154
+ return false;
155
+ }
156
+ const atDirective = this.cssDataManager.getAtDirective(atRuleName.getText());
157
+ if (atDirective) {
158
+ return false;
159
+ }
160
+ this.addEntry(atRuleName, Rules.UnknownAtRules, `Unknown at rule ${atRuleName.getText()}`);
161
+ return true;
162
+ }
163
+ visitKeyframe(node) {
164
+ const keyword = node.getKeyword();
165
+ if (!keyword) {
166
+ return false;
167
+ }
168
+ const text = keyword.getText();
169
+ this.keyframes.add(node.getName(), text, (text !== '@keyframes') ? keyword : null);
170
+ return true;
171
+ }
172
+ validateKeyframes() {
173
+ // @keyframe and it's vendor specific alternatives
174
+ // @keyframe should be included
175
+ const expected = ['@-webkit-keyframes', '@-moz-keyframes', '@-o-keyframes'];
176
+ for (const name in this.keyframes.data) {
177
+ const actual = this.keyframes.data[name].names;
178
+ const needsStandard = (actual.indexOf('@keyframes') === -1);
179
+ if (!needsStandard && actual.length === 1) {
180
+ continue; // only the non-vendor specific keyword is used, that's fine, no warning
181
+ }
182
+ const missingVendorSpecific = this.getMissingNames(expected, actual);
183
+ if (missingVendorSpecific || needsStandard) {
184
+ for (const node of this.keyframes.data[name].nodes) {
185
+ if (needsStandard) {
186
+ const message = localize('keyframes.standardrule.missing', "Always define standard rule '@keyframes' when defining keyframes.");
187
+ this.addEntry(node, Rules.IncludeStandardPropertyWhenUsingVendorPrefix, message);
188
+ }
189
+ if (missingVendorSpecific) {
190
+ const message = localize('keyframes.vendorspecific.missing', "Always include all vendor specific rules: Missing: {0}", missingVendorSpecific);
191
+ this.addEntry(node, Rules.AllVendorPrefixes, message);
192
+ }
193
+ }
194
+ }
195
+ }
196
+ return true;
197
+ }
198
+ visitSimpleSelector(node) {
199
+ /////////////////////////////////////////////////////////////
200
+ // Lint - The universal selector (*) is known to be slow.
201
+ /////////////////////////////////////////////////////////////
202
+ const firstChar = this.documentText.charAt(node.offset);
203
+ if (node.length === 1 && firstChar === '*') {
204
+ this.addEntry(node, Rules.UniversalSelector);
205
+ }
206
+ return true;
207
+ }
208
+ visitIdentifierSelector(node) {
209
+ /////////////////////////////////////////////////////////////
210
+ // Lint - Avoid id selectors
211
+ /////////////////////////////////////////////////////////////
212
+ this.addEntry(node, Rules.AvoidIdSelector);
213
+ return true;
214
+ }
215
+ visitImport(node) {
216
+ /////////////////////////////////////////////////////////////
217
+ // Lint - Import statements shouldn't be used, because they aren't offering parallel downloads.
218
+ /////////////////////////////////////////////////////////////
219
+ this.addEntry(node, Rules.ImportStatemement);
220
+ return true;
221
+ }
222
+ visitRuleSet(node) {
223
+ /////////////////////////////////////////////////////////////
224
+ // Lint - Don't use empty rulesets.
225
+ /////////////////////////////////////////////////////////////
226
+ const declarations = node.getDeclarations();
227
+ if (!declarations) {
228
+ // syntax error
229
+ return false;
230
+ }
231
+ if (!declarations.hasChildren()) {
232
+ this.addEntry(node.getSelectors(), Rules.EmptyRuleSet);
233
+ }
234
+ const propertyTable = [];
235
+ for (const element of declarations.getChildren()) {
236
+ if (element instanceof nodes.Declaration) {
237
+ propertyTable.push(new Element(element));
238
+ }
239
+ }
240
+ /////////////////////////////////////////////////////////////
241
+ // the rule warns when it finds:
242
+ // width being used with border, border-left, border-right, padding, padding-left, or padding-right
243
+ // height being used with border, border-top, border-bottom, padding, padding-top, or padding-bottom
244
+ // No error when box-sizing property is specified, as it assumes the user knows what he's doing.
245
+ // see https://github.com/CSSLint/csslint/wiki/Beware-of-box-model-size
246
+ /////////////////////////////////////////////////////////////
247
+ const boxModel = calculateBoxModel(propertyTable);
248
+ if (boxModel.width) {
249
+ let properties = [];
250
+ if (boxModel.right.value) {
251
+ properties = union(properties, boxModel.right.properties);
252
+ }
253
+ if (boxModel.left.value) {
254
+ properties = union(properties, boxModel.left.properties);
255
+ }
256
+ if (properties.length !== 0) {
257
+ for (const item of properties) {
258
+ this.addEntry(item.node, Rules.BewareOfBoxModelSize);
259
+ }
260
+ this.addEntry(boxModel.width.node, Rules.BewareOfBoxModelSize);
261
+ }
262
+ }
263
+ if (boxModel.height) {
264
+ let properties = [];
265
+ if (boxModel.top.value) {
266
+ properties = union(properties, boxModel.top.properties);
267
+ }
268
+ if (boxModel.bottom.value) {
269
+ properties = union(properties, boxModel.bottom.properties);
270
+ }
271
+ if (properties.length !== 0) {
272
+ for (const item of properties) {
273
+ this.addEntry(item.node, Rules.BewareOfBoxModelSize);
274
+ }
275
+ this.addEntry(boxModel.height.node, Rules.BewareOfBoxModelSize);
276
+ }
277
+ }
278
+ /////////////////////////////////////////////////////////////
279
+ // Properties ignored due to display
280
+ /////////////////////////////////////////////////////////////
281
+ // With 'display: inline-block', 'float' has no effect
282
+ let displayElems = this.fetchWithValue(propertyTable, 'display', 'inline-block');
283
+ if (displayElems.length > 0) {
284
+ const elem = this.fetch(propertyTable, 'float');
285
+ for (let index = 0; index < elem.length; index++) {
286
+ const node = elem[index].node;
287
+ const value = node.getValue();
288
+ if (value && !value.matches('none')) {
289
+ this.addEntry(node, Rules.PropertyIgnoredDueToDisplay, localize('rule.propertyIgnoredDueToDisplayInlineBlock', "inline-block is ignored due to the float. If 'float' has a value other than 'none', the box is floated and 'display' is treated as 'block'"));
290
+ }
291
+ }
292
+ }
293
+ // With 'display: block', 'vertical-align' has no effect
294
+ displayElems = this.fetchWithValue(propertyTable, 'display', 'block');
295
+ if (displayElems.length > 0) {
296
+ const elem = this.fetch(propertyTable, 'vertical-align');
297
+ for (let index = 0; index < elem.length; index++) {
298
+ this.addEntry(elem[index].node, Rules.PropertyIgnoredDueToDisplay, localize('rule.propertyIgnoredDueToDisplayBlock', "Property is ignored due to the display. With 'display: block', vertical-align should not be used."));
299
+ }
300
+ }
301
+ /////////////////////////////////////////////////////////////
302
+ // Avoid 'float'
303
+ /////////////////////////////////////////////////////////////
304
+ const elements = this.fetch(propertyTable, 'float');
305
+ for (let index = 0; index < elements.length; index++) {
306
+ const element = elements[index];
307
+ if (!this.isValidPropertyDeclaration(element)) {
308
+ this.addEntry(element.node, Rules.AvoidFloat);
309
+ }
310
+ }
311
+ /////////////////////////////////////////////////////////////
312
+ // Don't use duplicate declarations.
313
+ /////////////////////////////////////////////////////////////
314
+ for (let i = 0; i < propertyTable.length; i++) {
315
+ const element = propertyTable[i];
316
+ if (element.fullPropertyName !== 'background' && !this.validProperties[element.fullPropertyName]) {
317
+ const value = element.node.getValue();
318
+ if (value && this.documentText.charAt(value.offset) !== '-') {
319
+ const elements = this.fetch(propertyTable, element.fullPropertyName);
320
+ if (elements.length > 1) {
321
+ for (let k = 0; k < elements.length; k++) {
322
+ const value = elements[k].node.getValue();
323
+ if (value && this.documentText.charAt(value.offset) !== '-' && elements[k] !== element) {
324
+ this.addEntry(element.node, Rules.DuplicateDeclarations);
325
+ }
326
+ }
327
+ }
328
+ }
329
+ }
330
+ }
331
+ /////////////////////////////////////////////////////////////
332
+ // Unknown propery & When using a vendor-prefixed gradient, make sure to use them all.
333
+ /////////////////////////////////////////////////////////////
334
+ const isExportBlock = node.getSelectors().matches(":export");
335
+ if (!isExportBlock) {
336
+ const propertiesBySuffix = new NodesByRootMap();
337
+ let containsUnknowns = false;
338
+ for (const element of propertyTable) {
339
+ const decl = element.node;
340
+ if (this.isCSSDeclaration(decl)) {
341
+ let name = element.fullPropertyName;
342
+ const firstChar = name.charAt(0);
343
+ if (firstChar === '-') {
344
+ if (name.charAt(1) !== '-') { // avoid css variables
345
+ if (!this.cssDataManager.isKnownProperty(name) && !this.validProperties[name]) {
346
+ this.addEntry(decl.getProperty(), Rules.UnknownVendorSpecificProperty);
347
+ }
348
+ const nonPrefixedName = decl.getNonPrefixedPropertyName();
349
+ propertiesBySuffix.add(nonPrefixedName, name, decl.getProperty());
350
+ }
351
+ }
352
+ else {
353
+ const fullName = name;
354
+ if (firstChar === '*' || firstChar === '_') {
355
+ this.addEntry(decl.getProperty(), Rules.IEStarHack);
356
+ name = name.substr(1);
357
+ }
358
+ // _property and *property might be contributed via custom data
359
+ if (!this.cssDataManager.isKnownProperty(fullName) && !this.cssDataManager.isKnownProperty(name)) {
360
+ if (!this.validProperties[name]) {
361
+ this.addEntry(decl.getProperty(), Rules.UnknownProperty, localize('property.unknownproperty.detailed', "Unknown property: '{0}'", decl.getFullPropertyName()));
362
+ }
363
+ }
364
+ propertiesBySuffix.add(name, name, null); // don't pass the node as we don't show errors on the standard
365
+ }
366
+ }
367
+ else {
368
+ containsUnknowns = true;
369
+ }
370
+ }
371
+ if (!containsUnknowns) { // don't perform this test if there are
372
+ for (const suffix in propertiesBySuffix.data) {
373
+ const entry = propertiesBySuffix.data[suffix];
374
+ const actual = entry.names;
375
+ const needsStandard = this.cssDataManager.isStandardProperty(suffix) && (actual.indexOf(suffix) === -1);
376
+ if (!needsStandard && actual.length === 1) {
377
+ continue; // only the non-vendor specific rule is used, that's fine, no warning
378
+ }
379
+ const expected = [];
380
+ for (let i = 0, len = LintVisitor.prefixes.length; i < len; i++) {
381
+ const prefix = LintVisitor.prefixes[i];
382
+ if (this.cssDataManager.isStandardProperty(prefix + suffix)) {
383
+ expected.push(prefix + suffix);
384
+ }
385
+ }
386
+ const missingVendorSpecific = this.getMissingNames(expected, actual);
387
+ if (missingVendorSpecific || needsStandard) {
388
+ for (const node of entry.nodes) {
389
+ if (needsStandard) {
390
+ const message = localize('property.standard.missing', "Also define the standard property '{0}' for compatibility", suffix);
391
+ this.addEntry(node, Rules.IncludeStandardPropertyWhenUsingVendorPrefix, message);
392
+ }
393
+ if (missingVendorSpecific) {
394
+ const message = localize('property.vendorspecific.missing', "Always include all vendor specific properties: Missing: {0}", missingVendorSpecific);
395
+ this.addEntry(node, Rules.AllVendorPrefixes, message);
396
+ }
397
+ }
398
+ }
399
+ }
400
+ }
401
+ }
402
+ return true;
403
+ }
404
+ visitPrio(node) {
405
+ /////////////////////////////////////////////////////////////
406
+ // Don't use !important
407
+ /////////////////////////////////////////////////////////////
408
+ this.addEntry(node, Rules.AvoidImportant);
409
+ return true;
410
+ }
411
+ visitNumericValue(node) {
412
+ /////////////////////////////////////////////////////////////
413
+ // 0 has no following unit
414
+ /////////////////////////////////////////////////////////////
415
+ const funcDecl = node.findParent(nodes.NodeType.Function);
416
+ if (funcDecl && funcDecl.getName() === 'calc') {
417
+ return true;
418
+ }
419
+ const decl = node.findParent(nodes.NodeType.Declaration);
420
+ if (decl) {
421
+ const declValue = decl.getValue();
422
+ if (declValue) {
423
+ const value = node.getValue();
424
+ if (!value.unit || languageFacts.units.length.indexOf(value.unit.toLowerCase()) === -1) {
425
+ return true;
426
+ }
427
+ if (parseFloat(value.value) === 0.0 && !!value.unit && !this.validProperties[decl.getFullPropertyName()]) {
428
+ this.addEntry(node, Rules.ZeroWithUnit);
429
+ }
430
+ }
431
+ }
432
+ return true;
433
+ }
434
+ visitFontFace(node) {
435
+ const declarations = node.getDeclarations();
436
+ if (!declarations) {
437
+ // syntax error
438
+ return false;
439
+ }
440
+ let definesSrc = false, definesFontFamily = false;
441
+ let containsUnknowns = false;
442
+ for (const node of declarations.getChildren()) {
443
+ if (this.isCSSDeclaration(node)) {
444
+ const name = node.getProperty().getName().toLowerCase();
445
+ if (name === 'src') {
446
+ definesSrc = true;
447
+ }
448
+ if (name === 'font-family') {
449
+ definesFontFamily = true;
450
+ }
451
+ }
452
+ else {
453
+ containsUnknowns = true;
454
+ }
455
+ }
456
+ if (!containsUnknowns && (!definesSrc || !definesFontFamily)) {
457
+ this.addEntry(node, Rules.RequiredPropertiesForFontFace);
458
+ }
459
+ return true;
460
+ }
461
+ isCSSDeclaration(node) {
462
+ if (node instanceof nodes.Declaration) {
463
+ if (!node.getValue()) {
464
+ return false;
465
+ }
466
+ const property = node.getProperty();
467
+ if (!property) {
468
+ return false;
469
+ }
470
+ const identifier = property.getIdentifier();
471
+ if (!identifier || identifier.containsInterpolation()) {
472
+ return false;
473
+ }
474
+ return true;
475
+ }
476
+ return false;
477
+ }
478
+ visitHexColorValue(node) {
479
+ // Rule: #eeff0011 or #eeff00 or #ef01 or #ef0
480
+ const length = node.length;
481
+ if (length !== 9 && length !== 7 && length !== 5 && length !== 4) {
482
+ this.addEntry(node, Rules.HexColorLength);
483
+ }
484
+ return false;
485
+ }
486
+ visitFunction(node) {
487
+ const fnName = node.getName().toLowerCase();
488
+ let expectedAttrCount = -1;
489
+ let actualAttrCount = 0;
490
+ switch (fnName) {
491
+ case 'rgb(':
492
+ case 'hsl(':
493
+ expectedAttrCount = 3;
494
+ break;
495
+ case 'rgba(':
496
+ case 'hsla(':
497
+ expectedAttrCount = 4;
498
+ break;
499
+ }
500
+ if (expectedAttrCount !== -1) {
501
+ node.getArguments().accept(n => {
502
+ if (n instanceof nodes.BinaryExpression) {
503
+ actualAttrCount += 1;
504
+ return false;
505
+ }
506
+ return true;
507
+ });
508
+ if (actualAttrCount !== expectedAttrCount) {
509
+ this.addEntry(node, Rules.ArgsInColorFunction);
510
+ }
511
+ }
512
+ return true;
513
+ }
514
+ }
515
+ LintVisitor.prefixes = [
516
+ '-ms-', '-moz-', '-o-', '-webkit-', // Quite common
517
+ // '-xv-', '-atsc-', '-wap-', '-khtml-', 'mso-', 'prince-', '-ah-', '-hp-', '-ro-', '-rim-', '-tc-' // Quite un-common
518
+ ];