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