css-variable-lsp 1.0.1 → 1.0.3

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.
@@ -0,0 +1,363 @@
1
+ import { Color } from 'vscode-languageserver/node';
2
+
3
+ /**
4
+ * Parse a CSS color string into an LSP Color object.
5
+ * Supports hex, rgb, rgba, hsl, hsla, and named colors.
6
+ */
7
+ export function parseColor(value: string): Color | null {
8
+ value = value.trim().toLowerCase();
9
+
10
+ // Hex
11
+ if (value.startsWith('#')) {
12
+ return parseHex(value);
13
+ }
14
+
15
+ // RGB / RGBA
16
+ if (value.startsWith('rgb')) {
17
+ return parseRgb(value);
18
+ }
19
+
20
+ // HSL / HSLA
21
+ if (value.startsWith('hsl')) {
22
+ return parseHsl(value);
23
+ }
24
+
25
+ // Named colors
26
+ return parseNamedColor(value);
27
+ }
28
+
29
+ /**
30
+ * Format an LSP Color object back into a CSS string.
31
+ * Defaults to Hex if alpha is 1, otherwise rgba.
32
+ */
33
+ export function formatColor(color: Color): string {
34
+ const r = Math.round(color.red * 255);
35
+ const g = Math.round(color.green * 255);
36
+ const b = Math.round(color.blue * 255);
37
+ const a = color.alpha;
38
+
39
+ if (a >= 1) {
40
+ return `#${toHex(r)}${toHex(g)}${toHex(b)}`;
41
+ } else {
42
+ return `rgba(${r}, ${g}, ${b}, ${Number(a.toFixed(2))})`;
43
+ }
44
+ }
45
+
46
+ /**
47
+ * Format color as hex (with alpha if not fully opaque)
48
+ */
49
+ export function formatColorAsHex(color: Color): string {
50
+ const r = Math.round(color.red * 255);
51
+ const g = Math.round(color.green * 255);
52
+ const b = Math.round(color.blue * 255);
53
+ const a = Math.round(color.alpha * 255);
54
+
55
+ if (a >= 255) {
56
+ return `#${toHex(r)}${toHex(g)}${toHex(b)}`;
57
+ } else {
58
+ return `#${toHex(r)}${toHex(g)}${toHex(b)}${toHex(a)}`;
59
+ }
60
+ }
61
+
62
+ /**
63
+ * Format color as rgb() or rgba()
64
+ */
65
+ export function formatColorAsRgb(color: Color): string {
66
+ const r = Math.round(color.red * 255);
67
+ const g = Math.round(color.green * 255);
68
+ const b = Math.round(color.blue * 255);
69
+ const a = color.alpha;
70
+
71
+ if (a >= 1) {
72
+ return `rgb(${r}, ${g}, ${b})`;
73
+ } else {
74
+ return `rgba(${r}, ${g}, ${b}, ${Number(a.toFixed(2))})`;
75
+ }
76
+ }
77
+
78
+ /**
79
+ * Format color as hsl() or hsla()
80
+ */
81
+ export function formatColorAsHsl(color: Color): string {
82
+ const { h, s, l } = rgbToHsl(color.red, color.green, color.blue);
83
+ const a = color.alpha;
84
+
85
+ const hDeg = Math.round(h * 360);
86
+ const sPercent = Math.round(s * 100);
87
+ const lPercent = Math.round(l * 100);
88
+
89
+ if (a >= 1) {
90
+ return `hsl(${hDeg}, ${sPercent}%, ${lPercent}%)`;
91
+ } else {
92
+ return `hsla(${hDeg}, ${sPercent}%, ${lPercent}%, ${Number(a.toFixed(2))})`;
93
+ }
94
+ }
95
+
96
+ /**
97
+ * Convert RGB to HSL
98
+ */
99
+ function rgbToHsl(r: number, g: number, b: number): { h: number; s: number; l: number } {
100
+ const max = Math.max(r, g, b);
101
+ const min = Math.min(r, g, b);
102
+ const l = (max + min) / 2;
103
+
104
+ if (max === min) {
105
+ return { h: 0, s: 0, l };
106
+ }
107
+
108
+ const d = max - min;
109
+ const s = l > 0.5 ? d / (2 - max - min) : d / (max + min);
110
+
111
+ let h: number;
112
+ if (max === r) {
113
+ h = ((g - b) / d + (g < b ? 6 : 0)) / 6;
114
+ } else if (max === g) {
115
+ h = ((b - r) / d + 2) / 6;
116
+ } else {
117
+ h = ((r - g) / d + 4) / 6;
118
+ }
119
+
120
+ return { h, s, l };
121
+ }
122
+
123
+ function toHex(n: number): string {
124
+ const hex = n.toString(16);
125
+ return hex.length === 1 ? '0' + hex : hex;
126
+ }
127
+
128
+ function parseHex(hex: string): Color | null {
129
+ hex = hex.substring(1); // Remove #
130
+ if (hex.length === 3) {
131
+ hex = hex.split('').map(c => c + c).join('');
132
+ }
133
+ if (hex.length !== 6 && hex.length !== 8) {
134
+ return null;
135
+ }
136
+
137
+ const r = parseInt(hex.substring(0, 2), 16) / 255;
138
+ const g = parseInt(hex.substring(2, 4), 16) / 255;
139
+ const b = parseInt(hex.substring(4, 6), 16) / 255;
140
+ let a = 1;
141
+
142
+ if (hex.length === 8) {
143
+ a = parseInt(hex.substring(6, 8), 16) / 255;
144
+ }
145
+
146
+ return { red: r, green: g, blue: b, alpha: a };
147
+ }
148
+
149
+ function parseRgb(value: string): Color | null {
150
+ const match = value.match(/rgba?\(([\d\s\.]+),?\s*([\d\s\.]+),?\s*([\d\s\.]+)(?:,?\s*\/?,?\s*([\d\s\.]+))?\)/);
151
+ if (!match) return null;
152
+
153
+ const r = parseFloat(match[1]) / 255;
154
+ const g = parseFloat(match[2]) / 255;
155
+ const b = parseFloat(match[3]) / 255;
156
+ let a = 1;
157
+
158
+ if (match[4]) {
159
+ a = parseFloat(match[4]);
160
+ }
161
+
162
+ return { red: r, green: g, blue: b, alpha: a };
163
+ }
164
+
165
+ function parseHsl(value: string): Color | null {
166
+ const match = value.match(/hsla?\(([\d\s\.]+)(?:deg)?,?\s*([\d\s\.]+)%?,?\s*([\d\s\.]+)%?(?:,?\s*\/?,?\s*([\d\s\.]+))?\)/);
167
+ if (!match) return null;
168
+
169
+ const h = parseFloat(match[1]) / 360;
170
+ const s = parseFloat(match[2]) / 100;
171
+ const l = parseFloat(match[3]) / 100;
172
+ let a = 1;
173
+
174
+ if (match[4]) {
175
+ a = parseFloat(match[4]);
176
+ }
177
+
178
+ return hslToRgb(h, s, l, a);
179
+ }
180
+
181
+ function hslToRgb(h: number, s: number, l: number, a: number): Color {
182
+ let r, g, b;
183
+
184
+ if (s === 0) {
185
+ r = g = b = l; // achromatic
186
+ } else {
187
+ const hue2rgb = (p: number, q: number, t: number) => {
188
+ if (t < 0) t += 1;
189
+ if (t > 1) t -= 1;
190
+ if (t < 1 / 6) return p + (q - p) * 6 * t;
191
+ if (t < 1 / 2) return q;
192
+ if (t < 2 / 3) return p + (q - p) * (2 / 3 - t) * 6;
193
+ return p;
194
+ };
195
+
196
+ const q = l < 0.5 ? l * (1 + s) : l + s - l * s;
197
+ const p = 2 * l - q;
198
+ r = hue2rgb(p, q, h + 1 / 3);
199
+ g = hue2rgb(p, q, h);
200
+ b = hue2rgb(p, q, h - 1 / 3);
201
+ }
202
+
203
+ return { red: r, green: g, blue: b, alpha: a };
204
+ }
205
+
206
+ function parseNamedColor(name: string): Color | null {
207
+ const colors: { [key: string]: string } = {
208
+ black: '#000000',
209
+ white: '#ffffff',
210
+ red: '#ff0000',
211
+ green: '#008000',
212
+ blue: '#0000ff',
213
+ yellow: '#ffff00',
214
+ cyan: '#00ffff',
215
+ magenta: '#ff00ff',
216
+ gray: '#808080',
217
+ grey: '#808080',
218
+ silver: '#c0c0c0',
219
+ maroon: '#800000',
220
+ olive: '#808000',
221
+ purple: '#800080',
222
+ teal: '#008080',
223
+ navy: '#000080',
224
+ orange: '#ffa500',
225
+ aliceblue: '#f0f8ff',
226
+ antiquewhite: '#faebd7',
227
+ aqua: '#00ffff',
228
+ aquamarine: '#7fffd4',
229
+ azure: '#f0ffff',
230
+ beige: '#f5f5dc',
231
+ bisque: '#ffe4c4',
232
+ blanchedalmond: '#ffebcd',
233
+ blueviolet: '#8a2be2',
234
+ brown: '#a52a2a',
235
+ burlywood: '#deb887',
236
+ cadetblue: '#5f9ea0',
237
+ chartreuse: '#7fff00',
238
+ chocolate: '#d2691e',
239
+ coral: '#ff7f50',
240
+ cornflowerblue: '#6495ed',
241
+ cornsilk: '#fff8dc',
242
+ crimson: '#dc143c',
243
+ darkblue: '#00008b',
244
+ darkcyan: '#008b8b',
245
+ darkgoldenrod: '#b8860b',
246
+ darkgray: '#a9a9a9',
247
+ darkgreen: '#006400',
248
+ darkgrey: '#a9a9a9',
249
+ darkkhaki: '#bdb76b',
250
+ darkmagenta: '#8b008b',
251
+ darkolivegreen: '#556b2f',
252
+ darkorange: '#ff8c00',
253
+ darkorchid: '#9932cc',
254
+ darkred: '#8b0000',
255
+ darksalmon: '#e9967a',
256
+ darkseagreen: '#8fbc8f',
257
+ darkslateblue: '#483d8b',
258
+ darkslategray: '#2f4f4f',
259
+ darkslategrey: '#2f4f4f',
260
+ darkturquoise: '#00ced1',
261
+ darkviolet: '#9400d3',
262
+ deeppink: '#ff1493',
263
+ deepskyblue: '#00bfff',
264
+ dimgray: '#696969',
265
+ dimgrey: '#696969',
266
+ dodgerblue: '#1e90ff',
267
+ firebrick: '#b22222',
268
+ floralwhite: '#fffaf0',
269
+ forestgreen: '#228b22',
270
+ fuchsia: '#ff00ff',
271
+ gainsboro: '#dcdcdc',
272
+ ghostwhite: '#f8f8ff',
273
+ gold: '#ffd700',
274
+ goldenrod: '#daa520',
275
+ greenyellow: '#adff2f',
276
+ honeydew: '#f0fff0',
277
+ hotpink: '#ff69b4',
278
+ indianred: '#cd5c5c',
279
+ indigo: '#4b0082',
280
+ ivory: '#fffff0',
281
+ khaki: '#f0e68c',
282
+ lavender: '#e6e6fa',
283
+ lavenderblush: '#fff0f5',
284
+ lawngreen: '#7cfc00',
285
+ lemonchiffon: '#fffacd',
286
+ lightblue: '#add8e6',
287
+ lightcoral: '#f08080',
288
+ lightcyan: '#e0ffff',
289
+ lightgoldenrodyellow: '#fafad2',
290
+ lightgray: '#d3d3d3',
291
+ lightgreen: '#90ee90',
292
+ lightgrey: '#d3d3d3',
293
+ lightpink: '#ffb6c1',
294
+ lightsalmon: '#ffa07a',
295
+ lightseagreen: '#20b2aa',
296
+ lightskyblue: '#87cefa',
297
+ lightslategray: '#778899',
298
+ lightslategrey: '#778899',
299
+ lightsteelblue: '#b0c4de',
300
+ lightyellow: '#ffffe0',
301
+ lime: '#00ff00',
302
+ limegreen: '#32cd32',
303
+ linen: '#faf0e6',
304
+ mediumaquamarine: '#66cdaa',
305
+ mediumblue: '#0000cd',
306
+ mediumorchid: '#ba55d3',
307
+ mediumpurple: '#9370db',
308
+ mediumseagreen: '#3cb371',
309
+ mediumslateblue: '#7b68ee',
310
+ mediumspringgreen: '#00fa9a',
311
+ mediumturquoise: '#48d1cc',
312
+ mediumvioletred: '#c71585',
313
+ midnightblue: '#191970',
314
+ mintcream: '#f5fffa',
315
+ mistyrose: '#ffe4e1',
316
+ moccasin: '#ffe4b5',
317
+ navajowhite: '#ffdead',
318
+ oldlace: '#fdf5e6',
319
+ olivedrab: '#6b8e23',
320
+ orangered: '#ff4500',
321
+ orchid: '#da70d6',
322
+ palegoldenrod: '#eee8aa',
323
+ palegreen: '#98fb98',
324
+ paleturquoise: '#afeeee',
325
+ palevioletred: '#db7093',
326
+ papayawhip: '#ffefd5',
327
+ peachpuff: '#ffdab9',
328
+ peru: '#cd853f',
329
+ pink: '#ffc0cb',
330
+ plum: '#dda0dd',
331
+ powderblue: '#b0e0e6',
332
+ rosybrown: '#bc8f8f',
333
+ royalblue: '#4169e1',
334
+ saddlebrown: '#8b4513',
335
+ salmon: '#fa8072',
336
+ sandybrown: '#f4a460',
337
+ seagreen: '#2e8b57',
338
+ seashell: '#fff5ee',
339
+ sienna: '#a0522d',
340
+ skyblue: '#87ceeb',
341
+ slateblue: '#6a5acd',
342
+ slategray: '#708090',
343
+ slategrey: '#708090',
344
+ snow: '#fffafa',
345
+ springgreen: '#00ff7f',
346
+ steelblue: '#4682b4',
347
+ tan: '#d2b48c',
348
+ thistle: '#d8bfd8',
349
+ tomato: '#ff6347',
350
+ transparent: '#00000000', // Special case
351
+ turquoise: '#40e0d0',
352
+ violet: '#ee82ee',
353
+ wheat: '#f5deb3',
354
+ whitesmoke: '#f5f5f5',
355
+ yellowgreen: '#9acd32',
356
+ rebeccapurple: '#663399'
357
+ };
358
+
359
+ if (colors[name]) {
360
+ return parseHex(colors[name]);
361
+ }
362
+ return null;
363
+ }
@@ -6,12 +6,16 @@ import * as fs from 'fs';
6
6
  import * as csstree from 'css-tree';
7
7
  import { DOMTree, DOMNodeInfo } from './domTree';
8
8
  import { parse, HTMLElement as ParsedHTMLElement } from 'node-html-parser';
9
+ import { Color } from 'vscode-languageserver/node';
10
+ import { parseColor } from './colorService';
11
+ import { calculateSpecificity, compareSpecificity } from './specificity';
9
12
 
10
13
  export interface CssVariable {
11
14
  name: string;
12
15
  value: string;
13
16
  uri: string;
14
- range: Range;
17
+ range: Range; // Range of the entire declaration (e.g., "--foo: red")
18
+ valueRange?: Range; // Range of just the value part (e.g., "red")
15
19
  selector: string; // CSS selector where this variable is defined (e.g., ":root", "div", ".class")
16
20
  important: boolean; // Whether this definition uses !important
17
21
  sourcePosition: number; // Character position in file (for source order)
@@ -210,11 +214,29 @@ export class CssVariableManager {
210
214
  const startPos = document.positionAt(offset + node.loc.start.offset);
211
215
  const endPos = document.positionAt(offset + node.loc.end.offset);
212
216
 
217
+ // Capture valueRange from node.value location
218
+ let valueRange: Range | undefined;
219
+ if (node.value && node.value.loc) {
220
+ // Get the raw text from the value node
221
+ const valueStartOffset = offset + node.value.loc.start.offset;
222
+ const valueEndOffset = offset + node.value.loc.end.offset;
223
+ const rawValueText = text.substring(valueStartOffset, valueEndOffset);
224
+
225
+ // Trim leading/trailing whitespace to get the actual value position
226
+ const leadingWhitespace = rawValueText.length - rawValueText.trimStart().length;
227
+ const trailingWhitespace = rawValueText.length - rawValueText.trimEnd().length;
228
+
229
+ const valueStartPos = document.positionAt(valueStartOffset + leadingWhitespace);
230
+ const valueEndPos = document.positionAt(valueEndOffset - trailingWhitespace);
231
+ valueRange = Range.create(valueStartPos, valueEndPos);
232
+ }
233
+
213
234
  const variable: CssVariable = {
214
235
  name,
215
236
  value,
216
237
  uri,
217
238
  range: Range.create(startPos, endPos),
239
+ valueRange,
218
240
  selector,
219
241
  important,
220
242
  sourcePosition: offset + node.loc.start.offset
@@ -350,7 +372,6 @@ export class CssVariableManager {
350
372
  return;
351
373
  }
352
374
 
353
- this.parseContent(content, uri, languageId);
354
375
  this.parseContent(content, uri, languageId);
355
376
  this.logger.log(`[css-lsp] Updated file ${uri} from disk.`);
356
377
  } catch (error) {
@@ -437,4 +458,53 @@ export class CssVariableManager {
437
458
  return this.domTrees.get(uri);
438
459
  }
439
460
 
461
+ /**
462
+ * Resolve a variable name to a Color if possible.
463
+ * Handles recursive variable references: var(--a) -> var(--b) -> #fff
464
+ * Uses CSS cascade rules: !important > specificity > source order
465
+ */
466
+ public resolveVariableColor(name: string, context?: string, seen = new Set<string>()): Color | null {
467
+ if (seen.has(name)) {
468
+ return null; // Cycle detected
469
+ }
470
+ seen.add(name);
471
+
472
+ const variables = this.getVariables(name);
473
+ if (variables.length === 0) {
474
+ return null;
475
+ }
476
+
477
+ // Apply CSS cascade rules to find the winning definition
478
+ // Sort by cascade rules: !important > specificity > source order
479
+ const sortedVars = [...variables].sort((a, b) => {
480
+ // !important always wins (unless both are !important)
481
+ if (a.important !== b.important) {
482
+ return a.important ? -1 : 1;
483
+ }
484
+
485
+ // After !important, check specificity
486
+ const specA = calculateSpecificity(a.selector);
487
+ const specB = calculateSpecificity(b.selector);
488
+ const specCompare = compareSpecificity(specA, specB);
489
+
490
+ if (specCompare !== 0) {
491
+ return -specCompare; // Negative for descending order
492
+ }
493
+
494
+ // Equal specificity - later in source wins
495
+ return b.sourcePosition - a.sourcePosition;
496
+ });
497
+
498
+ // Use the winning definition (first after sort)
499
+ const variable = sortedVars[0];
500
+ let value = variable.value;
501
+
502
+ // Check if it's a reference to another variable
503
+ const match = value.match(/^var\((--[\w-]+)\)$/);
504
+ if (match) {
505
+ return this.resolveVariableColor(match[1], context, seen);
506
+ }
507
+
508
+ return parseColor(value);
509
+ }
440
510
  }
@@ -1,3 +1,5 @@
1
+ #!/usr/bin/env node
2
+
1
3
  import {
2
4
  createConnection,
3
5
  TextDocuments,
@@ -17,7 +19,9 @@ import {
17
19
  WorkspaceSymbol,
18
20
  WorkspaceEdit,
19
21
  TextEdit,
20
- FileChangeType
22
+ FileChangeType,
23
+ ColorInformation,
24
+ ColorPresentation
21
25
  } from 'vscode-languageserver/node';
22
26
  import * as fs from 'fs'
23
27
  import {
@@ -27,6 +31,7 @@ import { CssVariable } from './cssVariableManager';
27
31
 
28
32
  import { CssVariableManager } from './cssVariableManager';
29
33
  import { calculateSpecificity, compareSpecificity, formatSpecificity, matchesContext } from './specificity';
34
+ import { parseColor, formatColor, formatColorAsHex, formatColorAsRgb, formatColorAsHsl } from './colorService';
30
35
 
31
36
  // Write startup log immediately
32
37
  try {
@@ -97,7 +102,8 @@ connection.onInitialize((params: InitializeParams) => {
97
102
  referencesProvider: true,
98
103
  renameProvider: true,
99
104
  documentSymbolProvider: true,
100
- workspaceSymbolProvider: true
105
+ workspaceSymbolProvider: true,
106
+ colorProvider: true
101
107
  }
102
108
  };
103
109
  if (hasWorkspaceFolderCapability) {
@@ -572,6 +578,99 @@ connection.onWorkspaceSymbol((params) => {
572
578
  ));
573
579
  });
574
580
 
581
+ // Color Provider: Document Colors
582
+ connection.onDocumentColor((params) => {
583
+ const document = documents.get(params.textDocument.uri);
584
+ if (!document) {
585
+ return [];
586
+ }
587
+
588
+ const colors: ColorInformation[] = [];
589
+ const text = document.getText();
590
+
591
+ // 1. Check variable definitions: --my-color: #f00;
592
+ const definitions = cssVariableManager.getDocumentDefinitions(document.uri);
593
+ for (const def of definitions) {
594
+ const color = parseColor(def.value);
595
+ if (color) {
596
+ // Use the stored valueRange if available (accurate from csstree parsing)
597
+ if (def.valueRange) {
598
+ colors.push({
599
+ range: def.valueRange,
600
+ color: color
601
+ });
602
+ } else {
603
+ // Fallback: find the value within the declaration text
604
+ // This handles cases where valueRange wasn't captured (shouldn't happen normally)
605
+ const defText = text.substring(document.offsetAt(def.range.start), document.offsetAt(def.range.end));
606
+ const colonIndex = defText.indexOf(':');
607
+ if (colonIndex !== -1) {
608
+ const afterColon = defText.substring(colonIndex + 1);
609
+ const valueIndex = afterColon.indexOf(def.value.trim());
610
+
611
+ if (valueIndex !== -1) {
612
+ const absoluteValueStart = document.offsetAt(def.range.start) + colonIndex + 1 + valueIndex;
613
+ const start = document.positionAt(absoluteValueStart);
614
+ const end = document.positionAt(absoluteValueStart + def.value.trim().length);
615
+ colors.push({
616
+ range: { start, end },
617
+ color: color
618
+ });
619
+ }
620
+ }
621
+ }
622
+ }
623
+ }
624
+
625
+ // 2. Check variable usages: var(--my-color)
626
+ // We want to show the color of the variable being used.
627
+ // But we can't easily edit it (color picker on usage).
628
+ // VS Code allows read-only color information.
629
+ const regex = /var\((--[\w-]+)\)/g;
630
+ let match;
631
+ while ((match = regex.exec(text)) !== null) {
632
+ const varName = match[1];
633
+ const color = cssVariableManager.resolveVariableColor(varName);
634
+ if (color) {
635
+ const start = document.positionAt(match.index);
636
+ const end = document.positionAt(match.index + match[0].length);
637
+ colors.push({
638
+ range: { start, end },
639
+ color: color
640
+ });
641
+ }
642
+ }
643
+
644
+ return colors;
645
+ });
646
+
647
+ // Color Provider: Color Presentation
648
+ connection.onColorPresentation((params) => {
649
+ const color = params.color;
650
+ const range = params.range;
651
+ const document = documents.get(params.textDocument.uri);
652
+ if (!document) {
653
+ return [];
654
+ }
655
+
656
+ // Offer multiple format options for the color picker
657
+ const presentations: ColorPresentation[] = [];
658
+
659
+ // 1. Hex format (most common)
660
+ const hexStr = formatColorAsHex(color);
661
+ presentations.push(ColorPresentation.create(hexStr, TextEdit.replace(range, hexStr)));
662
+
663
+ // 2. RGB format
664
+ const rgbStr = formatColorAsRgb(color);
665
+ presentations.push(ColorPresentation.create(rgbStr, TextEdit.replace(range, rgbStr)));
666
+
667
+ // 3. HSL format
668
+ const hslStr = formatColorAsHsl(color);
669
+ presentations.push(ColorPresentation.create(hslStr, TextEdit.replace(range, hslStr)));
670
+
671
+ return presentations;
672
+ });
673
+
575
674
  // Make the text document manager listen on the connection
576
675
  // for open, change and close text document events
577
676
  documents.listen(connection);
@@ -1 +1 @@
1
- {"root":["./src/cascadeandinline.test.ts","./src/cssvariablemanager.ts","./src/domtree.test.ts","./src/domtree.ts","./src/filelifecycle.test.ts","./src/filetypesandupdates.test.ts","./src/htmlcomments.test.ts","./src/server.ts","./src/specificity.test.ts","./src/specificity.ts","./src/test.ts"],"version":"5.9.3"}
1
+ {"root":["./src/cascadeandinline.test.ts","./src/colorprovider.test.ts","./src/colorservice.ts","./src/cssvariablemanager.ts","./src/domtree.test.ts","./src/domtree.ts","./src/filelifecycle.test.ts","./src/filetypesandupdates.test.ts","./src/htmlcomments.test.ts","./src/server.ts","./src/specificity.test.ts","./src/specificity.ts","./src/test.ts"],"version":"5.9.3"}