webperf-snippets 1.7.0 → 1.7.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.
package/README.md
CHANGED
|
@@ -73,7 +73,7 @@ npx webperf-snippets https://example.com --json
|
|
|
73
73
|
The JSON output records how the page was measured in `conditions`, so a later run can tell whether it measured the same way: the viewport, the workflow (or the path of the snippet with `--snippet`), the wait and the version of the CLI.
|
|
74
74
|
|
|
75
75
|
```json
|
|
76
|
-
"conditions": { "viewport": "mobile", "workflow": "core-web-vitals", "waitMs": 3000, "cliVersion": "1.7.
|
|
76
|
+
"conditions": { "viewport": "mobile", "workflow": "core-web-vitals", "waitMs": 3000, "cliVersion": "1.7.1" }
|
|
77
77
|
```
|
|
78
78
|
|
|
79
79
|
Single snippet:
|
|
@@ -158,7 +158,7 @@ Without `--report-to` the CLI makes no external calls. With it, the results are
|
|
|
158
158
|
"timestamp": "2026-05-06T10:00:00.000Z",
|
|
159
159
|
"navMs": 1240,
|
|
160
160
|
"results": [],
|
|
161
|
-
"meta": { "viewport": "mobile", "waitMs": 3000, "cli_version": "1.7.
|
|
161
|
+
"meta": { "viewport": "mobile", "waitMs": 3000, "cli_version": "1.7.1" }
|
|
162
162
|
}
|
|
163
163
|
```
|
|
164
164
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "webperf-snippets",
|
|
3
|
-
"version": "1.7.
|
|
3
|
+
"version": "1.7.1",
|
|
4
4
|
"mcpName": "io.github.nucliweb/webperf-snippets",
|
|
5
5
|
"description": "Run curated WebPerf Snippets headlessly via Playwright. Diagnose Core Web Vitals beyond what Lighthouse exposes.",
|
|
6
6
|
"type": "module",
|
|
@@ -34,6 +34,14 @@ function uniqueSelector(el) {
|
|
|
34
34
|
}
|
|
35
35
|
// @end-shared uniqueSelector
|
|
36
36
|
|
|
37
|
+
// An element with content-visibility: auto has no size before it first renders unless
|
|
38
|
+
// contain-intrinsic-size gives one, so the page moves when it does. content-visibility: auto adds auto
|
|
39
|
+
// to contain-intrinsic-size, so Chrome reports an element with no size as "auto none", also when the CSS
|
|
40
|
+
// says none: what counts is whether the value has a length, as in "auto 500px".
|
|
41
|
+
function lacksIntrinsicSize(value) {
|
|
42
|
+
return !/\d/.test(value);
|
|
43
|
+
}
|
|
44
|
+
|
|
37
45
|
function detectContentVisibility() {
|
|
38
46
|
// Create an object to store the results
|
|
39
47
|
const results = {
|
|
@@ -125,7 +133,7 @@ function detectContentVisibility() {
|
|
|
125
133
|
|
|
126
134
|
// Check for missing contain-intrinsic-size
|
|
127
135
|
const missingIntrinsicSize = results.autoElements.filter(
|
|
128
|
-
(el) => el.containIntrinsicSize
|
|
136
|
+
(el) => lacksIntrinsicSize(el.containIntrinsicSize),
|
|
129
137
|
);
|
|
130
138
|
|
|
131
139
|
if (missingIntrinsicSize.length > 0) {
|
|
@@ -327,7 +335,7 @@ window.analyzeContentVisibilityOpportunities = analyzeContentVisibilityOpportuni
|
|
|
327
335
|
],
|
|
328
336
|
issues: [
|
|
329
337
|
...cvResults.autoElements
|
|
330
|
-
.filter(el => el.containIntrinsicSize
|
|
338
|
+
.filter(el => lacksIntrinsicSize(el.containIntrinsicSize))
|
|
331
339
|
.map(el => ({ code: "missing-intrinsic-size", severity: "warning", message: `${el.selector}: missing contain-intrinsic-size (CLS risk)`, target: el.selector })),
|
|
332
340
|
...(cvResults.autoElements.length + cvResults.hiddenElements.length === 0
|
|
333
341
|
? [{ code: "not-used", severity: "info", message: "No content-visibility usage found. Consider content-visibility: auto on below-the-fold sections to reduce the initial render cost." }]
|
|
@@ -76,8 +76,13 @@
|
|
|
76
76
|
}
|
|
77
77
|
// @end-shared uniqueSelector
|
|
78
78
|
|
|
79
|
+
// An SVG scales to any size, so its pixels are not measured. The extension names most of them; the
|
|
80
|
+
// content type of the response names an SVG of the page served without it, such as an image service
|
|
81
|
+
// that takes the format in the query. The browser gives the content type only for a response it can
|
|
82
|
+
// read: an SVG of another origin without the extension is measured as a raster image.
|
|
83
|
+
const typeByUrl = new Map(performance.getEntriesByType("resource").map((e) => [e.name, e.contentType]));
|
|
79
84
|
function isVector(url) {
|
|
80
|
-
return /^data:image\/svg/i.test(url) || /\.svg([?#]|$)/i.test(url);
|
|
85
|
+
return /^data:image\/svg/i.test(url) || /\.svg([?#]|$)/i.test(url) || typeByUrl.get(url) === "image/svg+xml";
|
|
81
86
|
}
|
|
82
87
|
|
|
83
88
|
// The size of the file in pixels. HTMLImageElement.naturalWidth is corrected by the srcset
|
package/snippets/SCHEMA.md
CHANGED
|
@@ -1137,7 +1137,7 @@ Returns buffered long tasks immediately. Ongoing tracking continues. `items` lis
|
|
|
1137
1137
|
|
|
1138
1138
|
#### Oversized-Images (async)
|
|
1139
1139
|
|
|
1140
|
-
Compares the pixels of each loaded image file with the pixels drawn (rendered size times `devicePixelRatio`, scaled by `object-fit`). `ratio` is file pixels per pixel drawn: `oversized` at 1.5 or more, `undersized` (blurry) under 0.9. `items` lists the flagged images, the most wasted pixels first (at most 50); `count` and `details` cover all of them. `wastedBytes` and `transferBytes` are `null` when the transfer size is hidden (cross-origin without `Timing-Allow-Origin`); `details.wastedBytes` counts each URL once. Images with a zero-size box, unloaded images and SVGs are counted in `details.skipped
|
|
1140
|
+
Compares the pixels of each loaded image file with the pixels drawn (rendered size times `devicePixelRatio`, scaled by `object-fit`). `ratio` is file pixels per pixel drawn: `oversized` at 1.5 or more, `undersized` (blurry) under 0.9. `items` lists the flagged images, the most wasted pixels first (at most 50); `count` and `details` cover all of them. `wastedBytes` and `transferBytes` are `null` when the transfer size is hidden (cross-origin without `Timing-Allow-Origin`); `details.wastedBytes` counts each URL once. Images with a zero-size box, unloaded images and SVGs are counted in `details.skipped*`; an SVG is known by its extension, a `data:image/svg` URL, or, for a file of the same origin, the `contentType` of its resource entry. `selector` matches only the image of its item, built like `details.element` of LCP, so an image with no id or class has a path such as `div.partners > img:nth-of-type(3)` instead of `img`.
|
|
1141
1141
|
|
|
1142
1142
|
```json
|
|
1143
1143
|
{
|
|
@@ -1250,7 +1250,7 @@ An issue about one thing among several, one issue per thing, also names it in `t
|
|
|
1250
1250
|
|
|
1251
1251
|
| Code | Severity | When |
|
|
1252
1252
|
|------|----------|------|
|
|
1253
|
-
| `missing-intrinsic-size` | warning | An element with `content-visibility: auto`
|
|
1253
|
+
| `missing-intrinsic-size` | warning | An element with `content-visibility: auto` whose `contain-intrinsic-size` has no length, such as `none` or the `auto none` Chrome reports when no size is declared; `target` is its selector |
|
|
1254
1254
|
| `not-used` | info | No `content-visibility` on the page |
|
|
1255
1255
|
|
|
1256
1256
|
### Critical-CSS-Detection
|