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