vscode-css-languageservice 5.4.1 → 6.0.1

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