@projectwallace/css-code-coverage 0.1.0 → 0.1.2

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/README.md CHANGED
@@ -36,11 +36,14 @@ npm install @projectwallace/css-code-coverage
36
36
  // { start: 0, end: 46 }
37
37
  // ]
38
38
  // }
39
+ import { parse_coverage } from '@projectwallace/css-code-coverage'
40
+
39
41
  let files = await fs.glob('./css-coverage/**/*.json')
40
42
  let coverage_data = []
43
+
41
44
  for (let file of files) {
42
45
  let json_content = await fs.readFile(file, 'urf-8')
43
- coverage_data.push(JSON.parse(json_content))
46
+ coverage_data.push(...parse_coverage(json_content))
44
47
  }
45
48
  ```
46
49
 
@@ -1,169 +1,200 @@
1
+ import * as v from "valibot";
1
2
  import { format as q } from "@projectwallace/format-css";
2
- import { tokenTypes as p, tokenize as $ } from "css-tree/tokenizer";
3
- function z(s) {
4
- return s.map(({ url: n, text: r, ranges: e }) => {
5
- if (!r)
6
- return { url: n, text: r, ranges: e };
7
- let d = q(r), u = /* @__PURE__ */ new Set([
8
- p.EOF,
9
- p.BadString,
10
- p.BadUrl,
11
- p.WhiteSpace,
12
- p.Semicolon,
13
- p.Comment,
14
- p.Colon
15
- ]), i = e.map(({ start: o, end: l }) => ({ start: o, end: l, tokens: [] }));
16
- function h(o, l) {
17
- let t = 0;
18
- for (let _ of i) {
19
- if (_.start > l) return -1;
20
- if (_.start <= o && _.end >= l)
21
- return t;
22
- t++;
3
+ import { tokenTypes as b, tokenize as N } from "css-tree/tokenizer";
4
+ let z = v.array(
5
+ v.object({
6
+ text: v.optional(v.string()),
7
+ url: v.string(),
8
+ ranges: v.array(
9
+ v.object({
10
+ start: v.number(),
11
+ end: v.number()
12
+ })
13
+ )
14
+ })
15
+ );
16
+ function T(t) {
17
+ return v.safeParse(z, t).success;
18
+ }
19
+ function Q(t) {
20
+ try {
21
+ let e = JSON.parse(t);
22
+ return T(e) ? e : [];
23
+ } catch {
24
+ return [];
25
+ }
26
+ }
27
+ function F(t) {
28
+ return t.map(({ url: e, text: l, ranges: n }) => {
29
+ if (!l)
30
+ return { url: e, text: l, ranges: n };
31
+ let d = q(l), u = /* @__PURE__ */ new Set([
32
+ b.EOF,
33
+ b.BadString,
34
+ b.BadUrl,
35
+ b.WhiteSpace,
36
+ b.Semicolon,
37
+ b.Comment,
38
+ b.Colon
39
+ ]), o = n.map(({ start: i, end: s }) => ({ start: i, end: s, tokens: [] }));
40
+ function p(i, s) {
41
+ let r = 0;
42
+ for (let _ of o) {
43
+ if (_.start > s) return -1;
44
+ if (_.start <= i && _.end >= s)
45
+ return r;
46
+ r++;
23
47
  }
24
48
  return -1;
25
49
  }
26
50
  let a = 0;
27
- $(r, (o, l, t) => {
28
- if (u.has(o)) return;
29
- a++, o === p.Url && (a += 2);
30
- let _ = h(l, t);
31
- _ !== -1 && i[_].tokens.push(a);
51
+ N(l, (i, s, r) => {
52
+ if (u.has(i)) return;
53
+ a++, i === b.Url && (a += 2);
54
+ let _ = p(s, r);
55
+ _ !== -1 && o[_].tokens.push(a);
32
56
  });
33
57
  let c = /* @__PURE__ */ new Map();
34
- a = 0, $(d, (o, l, t) => {
35
- u.has(o) || (a++, o === p.Url && (a += 2), c.set(a, { start: l, end: t }));
58
+ a = 0, N(d, (i, s, r) => {
59
+ u.has(i) || (a++, i === b.Url && (a += 2), c.set(a, { start: s, end: r }));
36
60
  });
37
61
  let g = [];
38
- for (let o of i) {
39
- let l = c.get(o.tokens.at(0)), t = c.get(o.tokens.at(-1));
40
- l !== void 0 && t !== void 0 && g.push({
41
- start: l.start,
42
- end: t.end
62
+ for (let i of o) {
63
+ let s = c.get(i.tokens.at(0)), r = c.get(i.tokens.at(-1));
64
+ s !== void 0 && r !== void 0 && g.push({
65
+ start: s.start,
66
+ end: r.end
43
67
  });
44
68
  }
45
- return { url: n, text: d, ranges: g };
69
+ return { url: e, text: d, ranges: g };
46
70
  });
47
71
  }
48
- function E(s) {
49
- let n = /* @__PURE__ */ new Map();
50
- for (let r of s) {
51
- let e = r.text || "";
52
- if (n.has(e)) {
53
- let u = n.get(e).ranges;
54
- for (let i of r.ranges) {
55
- let h = !1;
72
+ function J(t) {
73
+ let e = /* @__PURE__ */ new Map();
74
+ for (let l of t) {
75
+ let n = l.text || "";
76
+ if (e.has(n)) {
77
+ let u = e.get(n).ranges;
78
+ for (let o of l.ranges) {
79
+ let p = !1;
56
80
  for (let a of u)
57
- if (a.start === i.start && a.end === i.end) {
58
- h = !0;
81
+ if (a.start === o.start && a.end === o.end) {
82
+ p = !0;
59
83
  break;
60
84
  }
61
- h || u.push(i);
85
+ p || u.push(o);
62
86
  }
63
87
  } else
64
- n.set(e, {
65
- url: r.url,
66
- ranges: r.ranges
88
+ e.set(n, {
89
+ url: l.url,
90
+ ranges: l.ranges
67
91
  });
68
92
  }
69
- return n;
93
+ return e;
70
94
  }
71
- function F(s) {
95
+ function P(t) {
72
96
  try {
73
- let n = new URL(s);
74
- return n.pathname.slice(n.pathname.lastIndexOf(".") + 1);
97
+ let e = new URL(t);
98
+ return e.pathname.slice(e.pathname.lastIndexOf(".") + 1);
75
99
  } catch {
76
- let n = s.lastIndexOf(".");
77
- return s.slice(n, s.indexOf("/", n) + 1);
100
+ let e = t.lastIndexOf(".");
101
+ return t.slice(e, t.indexOf("/", e) + 1);
78
102
  }
79
103
  }
80
- function R(s, n, r) {
81
- let e = s(n), d = "", u = [], i = 0, h = e.querySelectorAll("style");
82
- for (let a of Array.from(h)) {
104
+ function R(t, e, l) {
105
+ let n = t(e), d = "", u = [], o = 0, p = n.querySelectorAll("style");
106
+ for (let a of Array.from(p)) {
83
107
  let c = a.textContent;
84
108
  if (!c.trim()) continue;
85
109
  d += c;
86
- let g = n.indexOf(c), o = g + c.length;
87
- for (let l of r)
88
- l.start >= g && l.end <= o && u.push({
89
- start: i + (l.start - g),
90
- end: i + (l.end - g)
110
+ let g = e.indexOf(c), i = g + c.length;
111
+ for (let s of l)
112
+ s.start >= g && s.end <= i && u.push({
113
+ start: o + (s.start - g),
114
+ end: o + (s.end - g)
91
115
  });
92
- i += c.length;
116
+ o += c.length;
93
117
  }
94
118
  return {
95
119
  css: d,
96
120
  ranges: u
97
121
  };
98
122
  }
99
- function T(s) {
100
- return /<\/?(html|body|head|div|span|script|style)/i.test(s);
123
+ function D(t) {
124
+ return /<\/?(html|body|head|div|span|script|style)/i.test(t);
101
125
  }
102
- function D(s, n) {
103
- let r = [];
104
- for (let e of s) {
105
- if (!e.text) continue;
106
- let d = F(e.url).toLowerCase();
126
+ function G(t, e) {
127
+ let l = [];
128
+ for (let n of t) {
129
+ if (!n.text) continue;
130
+ let d = P(n.url).toLowerCase();
107
131
  if (d !== "js") {
108
132
  if (d === "css") {
109
- r.push(e);
133
+ l.push(n);
110
134
  continue;
111
135
  }
112
- if (T(e.text)) {
113
- let { css: u, ranges: i } = R(n, e.text, e.ranges);
114
- r.push({
115
- url: e.url,
136
+ if (D(n.text)) {
137
+ let { css: u, ranges: o } = R(e, n.text, n.ranges);
138
+ l.push({
139
+ url: n.url,
116
140
  text: u,
117
- ranges: i
141
+ ranges: o
118
142
  });
119
143
  continue;
120
144
  }
121
- r.push({
122
- url: e.url,
123
- text: e.text,
124
- ranges: e.ranges
145
+ l.push({
146
+ url: n.url,
147
+ text: n.text,
148
+ ranges: n.ranges
125
149
  });
126
150
  }
127
151
  }
128
- return r;
152
+ return l;
153
+ }
154
+ function A(t, e) {
155
+ return e === 0 ? 0 : t / e;
129
156
  }
130
- function J(s, n) {
131
- let r = s.length, e = D(s, n), d = z(e), u = E(d), i = Array.from(u).map(([t, { url: _, ranges: B }]) => {
157
+ function V(t, e) {
158
+ let l = t.length;
159
+ if (!T(t))
160
+ throw new TypeError("No valid coverage data found");
161
+ let n = G(t, e), d = F(n), u = J(d), o = Array.from(u).map(([r, { url: _, ranges: E }]) => {
132
162
  function I(f, y) {
133
- let b = y + f.length, L = b + 1, U = /^\s*$/.test(f), m = f.endsWith("}");
134
- if (!U && !m) {
135
- for (let v of B)
136
- if (!(v.start > b || v.end < y)) {
137
- if (v.start <= y && v.end >= b)
163
+ let m = y + f.length, L = m + 1, U = /^\s*$/.test(f), x = f.endsWith("}");
164
+ if (!U && !x) {
165
+ for (let h of E)
166
+ if (!(h.start > m || h.end < y)) {
167
+ if (h.start <= y && h.end >= m)
138
168
  return !0;
139
- if (f.startsWith("@") && v.start > y && v.start < L)
169
+ if (f.startsWith("@") && h.start > y && h.start < L)
140
170
  return !0;
141
171
  }
142
172
  }
143
173
  return !1;
144
174
  }
145
- let k = t.split(`
146
- `), w = k.length, A = new Uint8Array(w), O = 0, C = t.length, S = 0, W = 0;
147
- for (let f = 0; f < k.length; f++) {
148
- let y = k[f], b = W, U = W + y.length + 1, m = /^\s*$/.test(y), v = y.endsWith("}"), j = I(y, b), x = !1, M = f > 0 && A[f - 1] === 1;
149
- (j && !v && !m || (m || v) && M || m && !M && I(k[f + 1], U)) && (x = !0), A[f] = x ? 1 : 0, x && (O++, S += y.length + 1), W = U;
175
+ let w = r.split(`
176
+ `), S = w.length, W = new Uint8Array(S), O = 0, j = r.length, C = 0, B = 0;
177
+ for (let f = 0; f < w.length; f++) {
178
+ let y = w[f], m = B, U = B + y.length + 1, x = /^\s*$/.test(y), h = y.endsWith("}"), $ = I(y, m), k = !1, M = f > 0 && W[f - 1] === 1;
179
+ ($ && !h && !x || (x || h) && M || x && !M && I(w[f + 1], U)) && (k = !0), W[f] = k ? 1 : 0, k && (O++, C += y.length + 1), B = U;
150
180
  }
151
181
  return {
152
182
  url: _,
153
- text: t,
154
- ranges: B,
155
- unused_bytes: C - S,
156
- used_bytes: S,
157
- total_bytes: C,
158
- line_coverage_ratio: O / w,
159
- byte_coverage_ratio: S / C,
160
- line_coverage: A,
161
- total_lines: w,
183
+ text: r,
184
+ ranges: E,
185
+ unused_bytes: j - C,
186
+ used_bytes: C,
187
+ total_bytes: j,
188
+ line_coverage_ratio: A(O, S),
189
+ byte_coverage_ratio: A(C, j),
190
+ line_coverage: W,
191
+ total_lines: S,
162
192
  covered_lines: O,
163
- uncovered_lines: w - O
193
+ uncovered_lines: S - O
194
+ // TODO: { is_covered: boolean, start_offset: number, start_line: number, end_offset: number, end_line: number }[]
164
195
  };
165
- }), { total_lines: h, total_covered_lines: a, total_uncovered_lines: c, total_bytes: g, total_used_bytes: o, total_unused_bytes: l } = i.reduce(
166
- (t, _) => (t.total_lines += _.total_lines, t.total_covered_lines += _.covered_lines, t.total_uncovered_lines += _.uncovered_lines, t.total_bytes += _.total_bytes, t.total_used_bytes += _.used_bytes, t.total_unused_bytes += _.unused_bytes, t),
196
+ }), { total_lines: p, total_covered_lines: a, total_uncovered_lines: c, total_bytes: g, total_used_bytes: i, total_unused_bytes: s } = o.reduce(
197
+ (r, _) => (r.total_lines += _.total_lines, r.total_covered_lines += _.covered_lines, r.total_uncovered_lines += _.uncovered_lines, r.total_bytes += _.total_bytes, r.total_used_bytes += _.used_bytes, r.total_unused_bytes += _.unused_bytes, r),
167
198
  {
168
199
  total_lines: 0,
169
200
  total_covered_lines: 0,
@@ -174,19 +205,20 @@ function J(s, n) {
174
205
  }
175
206
  );
176
207
  return {
177
- total_files_found: r,
208
+ total_files_found: l,
178
209
  total_bytes: g,
179
- total_lines: h,
180
- used_bytes: o,
210
+ total_lines: p,
211
+ used_bytes: i,
181
212
  covered_lines: a,
182
- unused_bytes: l,
213
+ unused_bytes: s,
183
214
  uncovered_lines: c,
184
- byte_coverage_ratio: o / g,
185
- line_coverage_ratio: a / h,
186
- coverage_per_stylesheet: i,
187
- total_stylesheets: i.length
215
+ byte_coverage_ratio: A(i, g),
216
+ line_coverage_ratio: A(a, p),
217
+ coverage_per_stylesheet: o,
218
+ total_stylesheets: o.length
188
219
  };
189
220
  }
190
221
  export {
191
- J as calculate_coverage
222
+ V as calculate_coverage,
223
+ Q as parse_coverage
192
224
  };
@@ -18,8 +18,8 @@ export type StylesheetCoverage = CoverageData & {
18
18
  };
19
19
  export type CoverageResult = CoverageData & {
20
20
  total_files_found: number;
21
- coverage_per_stylesheet: StylesheetCoverage[];
22
21
  total_stylesheets: number;
22
+ coverage_per_stylesheet: StylesheetCoverage[];
23
23
  };
24
24
  /**
25
25
  * @description
@@ -33,3 +33,6 @@ export type CoverageResult = CoverageData & {
33
33
  * 5. Calculate line-coverage, byte-coverage per stylesheet
34
34
  */
35
35
  export declare function calculate_coverage(coverage: Coverage[], parse_html: Parser): CoverageResult;
36
+ export type { Coverage, Range } from './parse-coverage.ts';
37
+ export { parse_coverage } from './parse-coverage.ts';
38
+ export type { Parser } from './types.ts';
@@ -7,4 +7,5 @@ export type Coverage = {
7
7
  text?: string;
8
8
  ranges: Range[];
9
9
  };
10
- export declare function parse_json(input: string): Coverage[];
10
+ export declare function is_valid_coverage(input: unknown): boolean;
11
+ export declare function parse_coverage(input: string): Coverage[];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@projectwallace/css-code-coverage",
3
- "version": "0.1.0",
3
+ "version": "0.1.2",
4
4
  "description": "",
5
5
  "author": "Bart Veneman <bart@projectwallace.com>",
6
6
  "repository": {