webperf-snippets 0.1.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/README.md +139 -0
- package/package.json +54 -0
- package/snippets/CoreWebVitals/CLS.js +88 -0
- package/snippets/CoreWebVitals/INP.js +432 -0
- package/snippets/CoreWebVitals/LCP-Image-Entropy.js +198 -0
- package/snippets/CoreWebVitals/LCP-Subparts.js +286 -0
- package/snippets/CoreWebVitals/LCP-Trail.js +184 -0
- package/snippets/CoreWebVitals/LCP-Video-Candidate.js +228 -0
- package/snippets/CoreWebVitals/LCP.js +135 -0
- package/snippets/CoreWebVitals/WORKFLOWS.md +203 -0
- package/snippets/DevTools-Overrides/Fetch-XHR-Timeline-inject.js +73 -0
- package/snippets/DevTools-Overrides/Fetch-XHR-Timeline-read.js +191 -0
- package/snippets/Interaction/Forced-Synchronous-Layout.js +286 -0
- package/snippets/Interaction/Input-Latency-Breakdown.js +199 -0
- package/snippets/Interaction/Interactions.js +220 -0
- package/snippets/Interaction/Layout-Shift-Loading-and-Interaction.js +266 -0
- package/snippets/Interaction/Long-Animation-Frames-Helpers.js +397 -0
- package/snippets/Interaction/Long-Animation-Frames-Script-Attribution.js +137 -0
- package/snippets/Interaction/Long-Animation-Frames.js +298 -0
- package/snippets/Interaction/LongTask.js +203 -0
- package/snippets/Interaction/Scroll-Performance.js +359 -0
- package/snippets/Interaction/WORKFLOWS.md +205 -0
- package/snippets/Loading/Back-Forward-Cache.js +473 -0
- package/snippets/Loading/CSS-Media-Queries-Analysis.js +512 -0
- package/snippets/Loading/Cache-Strategy-Analysis.js +808 -0
- package/snippets/Loading/Client-Side-Redirect-Detection.js +337 -0
- package/snippets/Loading/Content-Visibility.js +352 -0
- package/snippets/Loading/Critical-CSS-Detection.js +329 -0
- package/snippets/Loading/Event-Processing-Time.js +179 -0
- package/snippets/Loading/FCP.js +101 -0
- package/snippets/Loading/Find-Above-The-Fold-Lazy-Loaded-Images.js +264 -0
- package/snippets/Loading/Find-Images-With-Lazy-and-Fetchpriority.js +155 -0
- package/snippets/Loading/Find-non-Lazy-Loaded-Images-outside-of-the-viewport.js +300 -0
- package/snippets/Loading/Find-render-blocking-resources.js +161 -0
- package/snippets/Loading/First-And-Third-Party-Script-Info.js +269 -0
- package/snippets/Loading/First-And-Third-Party-Script-Timings.js +295 -0
- package/snippets/Loading/Fonts-Preloaded-Loaded-and-used-above-the-fold.js +317 -0
- package/snippets/Loading/Inline-CSS-Info-and-Size.js +234 -0
- package/snippets/Loading/Inline-Script-Info-and-Size.js +323 -0
- package/snippets/Loading/JS-Execution-Time-Breakdown.js +336 -0
- package/snippets/Loading/Prefetch-Resource-Validation.js +456 -0
- package/snippets/Loading/Priority-Hints-Audit.js +336 -0
- package/snippets/Loading/Resource-Hints-Validation.js +503 -0
- package/snippets/Loading/Resource-Hints.js +400 -0
- package/snippets/Loading/SSR-Hydration-Data-Analysis.js +364 -0
- package/snippets/Loading/Script-Loading.js +332 -0
- package/snippets/Loading/Service-Worker-Analysis.js +192 -0
- package/snippets/Loading/TTFB-Resources.js +115 -0
- package/snippets/Loading/TTFB-Sub-Parts.js +117 -0
- package/snippets/Loading/TTFB.js +37 -0
- package/snippets/Loading/Validate-Preload-Async-Defer-Scripts.js +394 -0
- package/snippets/Loading/WORKFLOWS.md +163 -0
- package/snippets/Media/Image-Element-Audit.js +350 -0
- package/snippets/Media/SVG-Embedded-Bitmap-Analysis.js +225 -0
- package/snippets/Media/Video-Element-Audit.js +282 -0
- package/snippets/Media/WORKFLOWS.md +190 -0
- package/snippets/README.md +98 -0
- package/snippets/Resources/Network-Bandwidth-Connection-Quality.js +103 -0
- package/snippets/Resources/WORKFLOWS.md +173 -0
- package/src/bin.js +229 -0
- package/src/decision-tree.js +19 -0
- package/src/load-snippet.js +23 -0
- package/src/reporters/human.js +179 -0
- package/src/reporters/json.js +3 -0
- package/src/runner.js +61 -0
- package/src/workflows/audit.js +21 -0
- package/src/workflows/cwv.js +10 -0
|
@@ -0,0 +1,394 @@
|
|
|
1
|
+
// Validate Preload on Async/Defer Scripts
|
|
2
|
+
// https://webperf-snippets.nucliweb.net
|
|
3
|
+
|
|
4
|
+
(() => {
|
|
5
|
+
// Get all preloaded scripts
|
|
6
|
+
const preloadedScripts = Array.from(
|
|
7
|
+
document.querySelectorAll('link[rel="preload"][as="script"]'),
|
|
8
|
+
);
|
|
9
|
+
|
|
10
|
+
if (preloadedScripts.length === 0) {
|
|
11
|
+
console.log(
|
|
12
|
+
'%c✅ No script preloads found (rel="preload" as="script").',
|
|
13
|
+
"color: #22c55e; font-weight: bold;",
|
|
14
|
+
);
|
|
15
|
+
console.log(
|
|
16
|
+
"%cℹ️ This is fine - only preload scripts if they're critical and blocking.",
|
|
17
|
+
"color: #3b82f6;",
|
|
18
|
+
);
|
|
19
|
+
return { script: "Validate-Preload-Async-Defer-Scripts", status: "ok", count: 0, items: [], issues: [] };
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
// Get performance entries for scripts
|
|
23
|
+
const performanceScripts = performance
|
|
24
|
+
.getEntriesByType("resource")
|
|
25
|
+
.filter((r) => r.initiatorType === "script" || r.initiatorType === "link");
|
|
26
|
+
|
|
27
|
+
// Find corresponding script elements for each preload
|
|
28
|
+
const issues = [];
|
|
29
|
+
const validPreloads = [];
|
|
30
|
+
const allScripts = Array.from(document.querySelectorAll("script[src]"));
|
|
31
|
+
|
|
32
|
+
preloadedScripts.forEach((preload) => {
|
|
33
|
+
const preloadHref = preload.href;
|
|
34
|
+
const preloadUrl = new URL(preloadHref, location.origin).href;
|
|
35
|
+
|
|
36
|
+
// Find matching script element
|
|
37
|
+
const matchingScript = allScripts.find((script) => {
|
|
38
|
+
try {
|
|
39
|
+
const scriptUrl = new URL(script.src, location.origin).href;
|
|
40
|
+
return scriptUrl === preloadUrl;
|
|
41
|
+
} catch {
|
|
42
|
+
return false;
|
|
43
|
+
}
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
const shortUrl = preloadHref.split("/").pop()?.split("?")[0] || preloadHref;
|
|
47
|
+
|
|
48
|
+
if (!matchingScript) {
|
|
49
|
+
// Check if script was loaded via Performance API (dynamic import, etc.)
|
|
50
|
+
const wasLoaded = performanceScripts.some((entry) => {
|
|
51
|
+
try {
|
|
52
|
+
// First try exact URL match
|
|
53
|
+
if (entry.name === preloadUrl) return true;
|
|
54
|
+
|
|
55
|
+
// For relative/partial matches, compare pathnames to reduce false positives
|
|
56
|
+
const entryUrl = new URL(entry.name, location.origin);
|
|
57
|
+
const preloadUrlObj = new URL(preloadUrl, location.origin);
|
|
58
|
+
|
|
59
|
+
// Match if pathnames end with the same file (more precise than includes)
|
|
60
|
+
return entryUrl.pathname.endsWith(preloadUrlObj.pathname);
|
|
61
|
+
} catch {
|
|
62
|
+
return false;
|
|
63
|
+
}
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
if (wasLoaded) {
|
|
67
|
+
// Script was loaded dynamically (import(), injected, etc.) - this is actually OK
|
|
68
|
+
validPreloads.push({
|
|
69
|
+
type: "dynamic",
|
|
70
|
+
preload,
|
|
71
|
+
url: preloadHref,
|
|
72
|
+
shortUrl,
|
|
73
|
+
note: "Dynamic script (not in DOM, but loaded)",
|
|
74
|
+
});
|
|
75
|
+
} else {
|
|
76
|
+
// Preload without matching script and not loaded - likely unused
|
|
77
|
+
issues.push({
|
|
78
|
+
type: "orphan",
|
|
79
|
+
severity: "warning",
|
|
80
|
+
preload,
|
|
81
|
+
url: preloadHref,
|
|
82
|
+
shortUrl,
|
|
83
|
+
message: "Preloaded script not found or loaded",
|
|
84
|
+
explanation: "This preload appears unused - script not in DOM and not in Performance API",
|
|
85
|
+
fix: "Verify the script is needed, or remove the preload",
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
} else {
|
|
89
|
+
const isAsync = matchingScript.async;
|
|
90
|
+
const isDefer = matchingScript.defer;
|
|
91
|
+
const isModule = matchingScript.type === "module";
|
|
92
|
+
const inHead = matchingScript.closest("head") !== null;
|
|
93
|
+
const scriptLocation = inHead ? "head" : "body";
|
|
94
|
+
const fetchPriority = matchingScript.getAttribute("fetchpriority");
|
|
95
|
+
|
|
96
|
+
// Determine natural priority
|
|
97
|
+
let naturalPriority = "Medium/High";
|
|
98
|
+
|
|
99
|
+
if (isAsync) {
|
|
100
|
+
naturalPriority = "Lowest/Low";
|
|
101
|
+
} else if (isDefer) {
|
|
102
|
+
naturalPriority = "Lowest/Low";
|
|
103
|
+
} else if (inHead) {
|
|
104
|
+
naturalPriority = "Medium/High";
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
// Check for anti-pattern: preload + async/defer
|
|
108
|
+
if (isAsync || isDefer) {
|
|
109
|
+
const attributes = [];
|
|
110
|
+
if (isAsync) attributes.push("async");
|
|
111
|
+
if (isDefer) attributes.push("defer");
|
|
112
|
+
if (isModule) attributes.push("type='module'");
|
|
113
|
+
|
|
114
|
+
// Check if fetchpriority="low" is used to mitigate the issue
|
|
115
|
+
const hasMitigation = fetchPriority === "low";
|
|
116
|
+
|
|
117
|
+
if (hasMitigation) {
|
|
118
|
+
// Valid: preload with async/defer but fetchpriority="low" mitigates the issue
|
|
119
|
+
validPreloads.push({
|
|
120
|
+
type: "mitigated",
|
|
121
|
+
preload,
|
|
122
|
+
script: matchingScript,
|
|
123
|
+
url: preloadHref,
|
|
124
|
+
shortUrl,
|
|
125
|
+
location: scriptLocation,
|
|
126
|
+
attributes: attributes.join(" + ") + " + fetchpriority='low'",
|
|
127
|
+
naturalPriority,
|
|
128
|
+
note: "Early discovery with low priority - acceptable pattern",
|
|
129
|
+
});
|
|
130
|
+
} else {
|
|
131
|
+
issues.push({
|
|
132
|
+
type: "async-defer-preload",
|
|
133
|
+
severity: "error",
|
|
134
|
+
preload,
|
|
135
|
+
script: matchingScript,
|
|
136
|
+
url: preloadHref,
|
|
137
|
+
shortUrl,
|
|
138
|
+
attributes: attributes.join(" + "),
|
|
139
|
+
location: scriptLocation,
|
|
140
|
+
naturalPriority,
|
|
141
|
+
preloadPriority: "Medium/High",
|
|
142
|
+
fetchPriority: fetchPriority || "not set",
|
|
143
|
+
message: `Script has ${attributes.join("/")} but is also preloaded`,
|
|
144
|
+
explanation: `${attributes.join("/")} scripts load at ${naturalPriority} priority. Preloading escalates them to Medium/High, causing bandwidth competition with critical resources.`,
|
|
145
|
+
fix: `Remove the preload, OR add fetchpriority="low" to the <script> tag to keep early discovery without priority escalation`,
|
|
146
|
+
});
|
|
147
|
+
}
|
|
148
|
+
} else if (isModule) {
|
|
149
|
+
// ES modules without async/defer behave like defer by default
|
|
150
|
+
issues.push({
|
|
151
|
+
type: "module-preload",
|
|
152
|
+
severity: "warning",
|
|
153
|
+
preload,
|
|
154
|
+
script: matchingScript,
|
|
155
|
+
url: preloadHref,
|
|
156
|
+
shortUrl,
|
|
157
|
+
attributes: "type='module'",
|
|
158
|
+
location: scriptLocation,
|
|
159
|
+
naturalPriority: "Lowest/Low (module default)",
|
|
160
|
+
preloadPriority: "Medium/High",
|
|
161
|
+
message: "ES module is using rel='preload' instead of rel='modulepreload'",
|
|
162
|
+
explanation:
|
|
163
|
+
"ES modules behave like defer scripts by default (low priority). Using rel='preload' as='script' escalates priority unnecessarily. For modules, use rel='modulepreload' instead.",
|
|
164
|
+
fix: "Change <link rel='preload' as='script'> to <link rel='modulepreload'> for proper module preloading",
|
|
165
|
+
});
|
|
166
|
+
} else {
|
|
167
|
+
// Valid preload (blocking script) - but check if it's really needed
|
|
168
|
+
const needsReview = !inHead; // Blocking scripts at end of body rarely need preload
|
|
169
|
+
|
|
170
|
+
validPreloads.push({
|
|
171
|
+
preload,
|
|
172
|
+
script: matchingScript,
|
|
173
|
+
url: preloadHref,
|
|
174
|
+
shortUrl,
|
|
175
|
+
location: scriptLocation,
|
|
176
|
+
naturalPriority,
|
|
177
|
+
needsReview,
|
|
178
|
+
reviewNote: needsReview ? "Consider using 'defer' instead" : "Valid for critical scripts",
|
|
179
|
+
});
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
});
|
|
183
|
+
|
|
184
|
+
// Display results
|
|
185
|
+
console.group(
|
|
186
|
+
"%c🔍 Preload + Async/Defer Script Validation",
|
|
187
|
+
"font-weight: bold; font-size: 14px;",
|
|
188
|
+
);
|
|
189
|
+
|
|
190
|
+
console.log("");
|
|
191
|
+
console.log("%cSummary:", "font-weight: bold;");
|
|
192
|
+
console.log(` Total preloaded scripts: ${preloadedScripts.length}`);
|
|
193
|
+
console.log(` Valid preloads (blocking scripts): ${validPreloads.length}`);
|
|
194
|
+
console.log(` Issues found: ${issues.length}`);
|
|
195
|
+
|
|
196
|
+
// Show issues
|
|
197
|
+
const errors = issues.filter((i) => i.severity === "error");
|
|
198
|
+
const warnings = issues.filter((i) => i.severity === "warning");
|
|
199
|
+
|
|
200
|
+
if (errors.length > 0) {
|
|
201
|
+
console.log("");
|
|
202
|
+
console.group(
|
|
203
|
+
`%c⚠️ Anti-patterns Found (${errors.length})`,
|
|
204
|
+
"color: #ef4444; font-weight: bold;",
|
|
205
|
+
);
|
|
206
|
+
|
|
207
|
+
const errorTable = errors
|
|
208
|
+
.filter((e) => e.type === "async-defer-preload")
|
|
209
|
+
.map((issue) => ({
|
|
210
|
+
Script: issue.shortUrl,
|
|
211
|
+
Location: issue.location,
|
|
212
|
+
Attributes: issue.attributes,
|
|
213
|
+
"Natural Priority": issue.naturalPriority,
|
|
214
|
+
"Preload Priority": "⚠️ Medium/High",
|
|
215
|
+
Issue: "❌ Priority escalation",
|
|
216
|
+
}));
|
|
217
|
+
|
|
218
|
+
if (errorTable.length > 0) {
|
|
219
|
+
console.table(errorTable);
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
console.log("");
|
|
223
|
+
console.log("%c🔴 Detailed Issues:", "font-weight: bold; color: #ef4444;");
|
|
224
|
+
errors
|
|
225
|
+
.filter((e) => e.type === "async-defer-preload")
|
|
226
|
+
.forEach((issue, i) => {
|
|
227
|
+
console.log("");
|
|
228
|
+
console.log(`%c${i + 1}. ${issue.message}`, "font-weight: bold;");
|
|
229
|
+
console.log(` URL: ${issue.url}`);
|
|
230
|
+
console.log(` Problem: ${issue.explanation}`);
|
|
231
|
+
console.log(` Fix: ${issue.fix}`);
|
|
232
|
+
console.log("");
|
|
233
|
+
console.log(" Elements:");
|
|
234
|
+
console.log(" Preload:", issue.preload);
|
|
235
|
+
console.log(" Script:", issue.script);
|
|
236
|
+
});
|
|
237
|
+
|
|
238
|
+
console.groupEnd();
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
if (warnings.length > 0) {
|
|
242
|
+
console.log("");
|
|
243
|
+
console.group(`%c💡 Warnings (${warnings.length})`, "color: #f59e0b; font-weight: bold;");
|
|
244
|
+
|
|
245
|
+
warnings.forEach((issue) => {
|
|
246
|
+
console.log("");
|
|
247
|
+
console.log(`%c${issue.message}`, "font-weight: bold;");
|
|
248
|
+
console.log(` URL: ${issue.url}`);
|
|
249
|
+
console.log(` ${issue.fix}`);
|
|
250
|
+
});
|
|
251
|
+
|
|
252
|
+
console.groupEnd();
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
// Show valid preloads
|
|
256
|
+
if (validPreloads.length > 0) {
|
|
257
|
+
console.log("");
|
|
258
|
+
console.group(
|
|
259
|
+
`%c✅ Valid Preloads (${validPreloads.length})`,
|
|
260
|
+
"color: #22c55e; font-weight: bold;",
|
|
261
|
+
);
|
|
262
|
+
|
|
263
|
+
const blockingScripts = validPreloads.filter((v) => v.type !== "dynamic");
|
|
264
|
+
const dynamicScripts = validPreloads.filter((v) => v.type === "dynamic");
|
|
265
|
+
|
|
266
|
+
if (blockingScripts.length > 0) {
|
|
267
|
+
console.log("Blocking scripts that may benefit from preload:");
|
|
268
|
+
console.log("");
|
|
269
|
+
|
|
270
|
+
const validTable = blockingScripts.map((v) => ({
|
|
271
|
+
Script: v.shortUrl,
|
|
272
|
+
Location: v.location,
|
|
273
|
+
Priority: v.naturalPriority,
|
|
274
|
+
Status: v.needsReview ? "⚠️ Review needed" : "✅ Valid",
|
|
275
|
+
Note: v.reviewNote,
|
|
276
|
+
}));
|
|
277
|
+
|
|
278
|
+
console.table(validTable);
|
|
279
|
+
|
|
280
|
+
if (blockingScripts.some((v) => v.needsReview)) {
|
|
281
|
+
console.log("");
|
|
282
|
+
console.log(
|
|
283
|
+
"%c💡 Tip: Blocking scripts at end of <body> rarely need preload.",
|
|
284
|
+
"color: #f59e0b;",
|
|
285
|
+
);
|
|
286
|
+
console.log(" Consider using 'defer' instead for better performance.");
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
if (dynamicScripts.length > 0) {
|
|
291
|
+
console.log("");
|
|
292
|
+
console.log(
|
|
293
|
+
`%c📦 Dynamic Scripts (${dynamicScripts.length})`,
|
|
294
|
+
"font-weight: bold; color: #3b82f6;",
|
|
295
|
+
);
|
|
296
|
+
console.log("These scripts are loaded dynamically (import(), code splitting, etc.):");
|
|
297
|
+
console.log("");
|
|
298
|
+
|
|
299
|
+
const dynamicTable = dynamicScripts.map((v) => ({
|
|
300
|
+
Script: v.shortUrl,
|
|
301
|
+
Type: "Dynamic/Code-split",
|
|
302
|
+
Status: "✅ OK",
|
|
303
|
+
Note: v.note,
|
|
304
|
+
}));
|
|
305
|
+
|
|
306
|
+
console.table(dynamicTable);
|
|
307
|
+
|
|
308
|
+
console.log("");
|
|
309
|
+
console.log(
|
|
310
|
+
"%c💡 Info: Dynamic scripts don't appear in the DOM but are loaded by other scripts.",
|
|
311
|
+
"color: #3b82f6;",
|
|
312
|
+
);
|
|
313
|
+
console.log(" This is normal for code-splitting, dynamic imports, or lazy-loaded modules.");
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
console.groupEnd();
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
// Recommendations
|
|
320
|
+
console.log("");
|
|
321
|
+
console.group("%c📝 Best Practices", "color: #3b82f6; font-weight: bold;");
|
|
322
|
+
console.log("");
|
|
323
|
+
console.log("%c❌ DON'T preload async/defer scripts:", "font-weight: bold;");
|
|
324
|
+
console.log(" Bad pattern:");
|
|
325
|
+
console.log(
|
|
326
|
+
'%c <link rel="preload" href="analytics.js" as="script">',
|
|
327
|
+
"font-family: monospace; color: #ef4444;",
|
|
328
|
+
);
|
|
329
|
+
console.log(
|
|
330
|
+
'%c <script src="analytics.js" async></script>',
|
|
331
|
+
"font-family: monospace; color: #ef4444;",
|
|
332
|
+
);
|
|
333
|
+
console.log("");
|
|
334
|
+
console.log("%c✅ DO let async scripts load naturally:", "font-weight: bold;");
|
|
335
|
+
console.log(" Good pattern:");
|
|
336
|
+
console.log(
|
|
337
|
+
'%c <script src="analytics.js" async></script>',
|
|
338
|
+
"font-family: monospace; color: #22c55e;",
|
|
339
|
+
);
|
|
340
|
+
console.log("");
|
|
341
|
+
console.log("%c✅ DO preload ONLY critical blocking scripts:", "font-weight: bold;");
|
|
342
|
+
console.log(" When script is needed early and blocks parsing:");
|
|
343
|
+
console.log(
|
|
344
|
+
'%c <link rel="preload" href="critical.js" as="script">',
|
|
345
|
+
"font-family: monospace; color: #22c55e;",
|
|
346
|
+
);
|
|
347
|
+
console.log(
|
|
348
|
+
'%c <script src="critical.js"></script>',
|
|
349
|
+
"font-family: monospace; color: #22c55e;",
|
|
350
|
+
);
|
|
351
|
+
console.log("");
|
|
352
|
+
console.log("%cResource priority order:", "font-weight: bold;");
|
|
353
|
+
console.log(" 1. Critical CSS (for LCP)");
|
|
354
|
+
console.log(" 2. Critical fonts (with crossorigin)");
|
|
355
|
+
console.log(" 3. LCP images (fetchpriority='high')");
|
|
356
|
+
console.log(" 4. Critical blocking scripts (rare)");
|
|
357
|
+
console.log(" 5. Everything else (no preload needed)");
|
|
358
|
+
console.groupEnd();
|
|
359
|
+
|
|
360
|
+
// Summary
|
|
361
|
+
if (errors.length === 0 && warnings.length === 0) {
|
|
362
|
+
console.log("");
|
|
363
|
+
console.log(
|
|
364
|
+
"%c✅ Great! No anti-patterns detected.",
|
|
365
|
+
"color: #22c55e; font-weight: bold; font-size: 14px;",
|
|
366
|
+
);
|
|
367
|
+
console.log(
|
|
368
|
+
'%cAll script preloads are either on blocking scripts or properly mitigated with fetchpriority="low".',
|
|
369
|
+
"color: #22c55e;",
|
|
370
|
+
);
|
|
371
|
+
} else {
|
|
372
|
+
console.log("");
|
|
373
|
+
console.log(
|
|
374
|
+
`%c⚠️ Found ${errors.length} error(s) and ${warnings.length} warning(s). Review recommendations above.`,
|
|
375
|
+
"color: #ef4444; font-weight: bold;",
|
|
376
|
+
);
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
console.groupEnd();
|
|
380
|
+
|
|
381
|
+
return {
|
|
382
|
+
script: "Validate-Preload-Async-Defer-Scripts",
|
|
383
|
+
status: "ok",
|
|
384
|
+
count: preloadedScripts.length,
|
|
385
|
+
details: {
|
|
386
|
+
validCount: validPreloads.length,
|
|
387
|
+
issueCount: issues.length,
|
|
388
|
+
},
|
|
389
|
+
items: preloadedScripts.map(p => ({
|
|
390
|
+
url: p.href.split("/").pop()?.split("?")[0] || p.href,
|
|
391
|
+
})),
|
|
392
|
+
issues: issues.map(i => ({ severity: i.severity, message: i.message })),
|
|
393
|
+
};
|
|
394
|
+
})();
|
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
## Common Workflows
|
|
2
|
+
|
|
3
|
+
### Complete Loading Performance Audit
|
|
4
|
+
|
|
5
|
+
When the user asks for a comprehensive loading analysis or "audit loading performance":
|
|
6
|
+
|
|
7
|
+
1. **TTFB.js** - Establish baseline server/network performance
|
|
8
|
+
2. **FCP.js** - Check initial render timing
|
|
9
|
+
3. **Find-render-blocking-resources.js** - Identify what's blocking rendering
|
|
10
|
+
4. **Critical-CSS-Detection.js** - Validate CSS strategy
|
|
11
|
+
5. **Script-Loading.js** - Audit script loading patterns
|
|
12
|
+
6. **Resource-Hints-Validation.js** - Check optimization hints
|
|
13
|
+
|
|
14
|
+
### Server/Backend Performance Investigation
|
|
15
|
+
|
|
16
|
+
When TTFB is slow or the user asks "why is my server slow":
|
|
17
|
+
|
|
18
|
+
1. **TTFB.js** - Measure overall TTFB
|
|
19
|
+
2. **TTFB-Sub-Parts.js** - Break down into DNS, connection, server time
|
|
20
|
+
3. **Service-Worker-Analysis.js** - Check for SW overhead impacting TTFB
|
|
21
|
+
4. **TTFB-Resources.js** - Identify slow third-party or API endpoints
|
|
22
|
+
|
|
23
|
+
### Font Loading Optimization
|
|
24
|
+
|
|
25
|
+
When the user asks about fonts, FOIT, FOUT, or font performance:
|
|
26
|
+
|
|
27
|
+
1. **Fonts-Preloaded-Loaded-and-used-above-the-fold.js** - Full font audit
|
|
28
|
+
2. **Resource-Hints-Validation.js** - Verify font preloads are correct
|
|
29
|
+
3. **Find-render-blocking-resources.js** - Check if fonts block rendering
|
|
30
|
+
|
|
31
|
+
### Script Performance Deep Dive
|
|
32
|
+
|
|
33
|
+
When scripts are suspected to slow down the page:
|
|
34
|
+
|
|
35
|
+
1. **Script-Loading.js** - Identify blocking scripts and loading strategy
|
|
36
|
+
2. **First-And-Third-Party-Script-Info.js** - Separate first vs third-party impact
|
|
37
|
+
3. **First-And-Third-Party-Script-Timings.js** - Diagnose slow script connections
|
|
38
|
+
4. **JS-Execution-Time-Breakdown.js** - Network vs parse/execution time
|
|
39
|
+
5. **Inline-Script-Info-and-Size.js** - Measure inline script overhead
|
|
40
|
+
6. **Validate-Preload-Async-Defer-Scripts.js** - Find preload anti-patterns
|
|
41
|
+
|
|
42
|
+
### Resource Hints & Priority Optimization
|
|
43
|
+
|
|
44
|
+
When the user wants to optimize resource loading priorities:
|
|
45
|
+
|
|
46
|
+
1. **Resource-Hints.js** - Overview of all hints in use
|
|
47
|
+
2. **Resource-Hints-Validation.js** - Verify hints are actually used
|
|
48
|
+
3. **Priority-Hints-Audit.js** - Check fetchpriority usage
|
|
49
|
+
4. **Prefetch-Resource-Validation.js** - Validate prefetch strategy
|
|
50
|
+
5. **Validate-Preload-Async-Defer-Scripts.js** - Find conflicts
|
|
51
|
+
|
|
52
|
+
### CSS Optimization Workflow
|
|
53
|
+
|
|
54
|
+
When CSS is bloated or blocking rendering:
|
|
55
|
+
|
|
56
|
+
1. **Critical-CSS-Detection.js** - Check critical CSS strategy
|
|
57
|
+
2. **Inline-CSS-Info-and-Size.js** - Measure inline CSS overhead
|
|
58
|
+
3. **CSS-Media-Queries-Analysis.js** - Find unused responsive CSS
|
|
59
|
+
4. **Find-render-blocking-resources.js** - Identify blocking stylesheets
|
|
60
|
+
|
|
61
|
+
### SSR/Framework Performance
|
|
62
|
+
|
|
63
|
+
When analyzing Next.js, Nuxt, Remix, or other SSR frameworks:
|
|
64
|
+
|
|
65
|
+
1. **SSR-Hydration-Data-Analysis.js** - Analyze hydration data size
|
|
66
|
+
2. **Script-Loading.js** - Check framework script loading patterns
|
|
67
|
+
3. **JS-Execution-Time-Breakdown.js** - Measure hydration execution cost
|
|
68
|
+
4. **Content-Visibility.js** - Check if content-visibility is used for optimization
|
|
69
|
+
|
|
70
|
+
## Decision Tree
|
|
71
|
+
|
|
72
|
+
Use this decision tree to automatically run follow-up snippets based on results:
|
|
73
|
+
|
|
74
|
+
### After TTFB.js
|
|
75
|
+
|
|
76
|
+
- **If TTFB > 600ms** → Run **TTFB-Sub-Parts.js** to diagnose where time is spent
|
|
77
|
+
- **If Service Worker detected** → Run **Service-Worker-Analysis.js** to check for SW overhead
|
|
78
|
+
- **If TTFB varies significantly across resources** → Run **TTFB-Resources.js**
|
|
79
|
+
|
|
80
|
+
### After FCP.js
|
|
81
|
+
|
|
82
|
+
- **If FCP > 1.8s** → Run:
|
|
83
|
+
1. **Find-render-blocking-resources.js** (CSS/JS blocking)
|
|
84
|
+
2. **Critical-CSS-Detection.js** (CSS strategy)
|
|
85
|
+
3. **Script-Loading.js** (blocking scripts)
|
|
86
|
+
- **If FCP is good but user complains about perceived slowness** → Check LCP with **webperf-core-web-vitals** skill
|
|
87
|
+
|
|
88
|
+
### After Find-render-blocking-resources.js
|
|
89
|
+
|
|
90
|
+
- **If blocking stylesheets found** → Run **Critical-CSS-Detection.js**
|
|
91
|
+
- **If blocking scripts found** → Run:
|
|
92
|
+
1. **Script-Loading.js** (loading strategy)
|
|
93
|
+
2. **Validate-Preload-Async-Defer-Scripts.js** (check for anti-patterns)
|
|
94
|
+
- **If fonts blocking** → Run **Fonts-Preloaded-Loaded-and-used-above-the-fold.js**
|
|
95
|
+
|
|
96
|
+
### After Script-Loading.js
|
|
97
|
+
|
|
98
|
+
- **If many blocking/parser-blocking scripts** → Run:
|
|
99
|
+
1. **JS-Execution-Time-Breakdown.js** (measure execution cost)
|
|
100
|
+
2. **First-And-Third-Party-Script-Info.js** (identify third-party culprits)
|
|
101
|
+
- **If third-party scripts detected** → Run **First-And-Third-Party-Script-Timings.js**
|
|
102
|
+
- **If large inline scripts** → Run **Inline-Script-Info-and-Size.js**
|
|
103
|
+
|
|
104
|
+
### After Resource-Hints-Validation.js
|
|
105
|
+
|
|
106
|
+
- **If unused preloads found** → Recommend removing them
|
|
107
|
+
- **If missing preloads for critical resources** → Run:
|
|
108
|
+
1. **Fonts-Preloaded-Loaded-and-used-above-the-fold.js** (fonts)
|
|
109
|
+
2. **Priority-Hints-Audit.js** (LCP candidate)
|
|
110
|
+
- **If preloads on async/defer scripts** → Run **Validate-Preload-Async-Defer-Scripts.js**
|
|
111
|
+
|
|
112
|
+
### After Service-Worker-Analysis.js
|
|
113
|
+
|
|
114
|
+
- **If SW overhead > 100ms** → Recommend Navigation Preload API
|
|
115
|
+
- **If SW caching many resources** → Run **TTFB-Resources.js** to verify cache hits
|
|
116
|
+
- **If SW not detected but site is SPA/PWA** → Recommend implementing SW
|
|
117
|
+
|
|
118
|
+
### After Fonts-Preloaded-Loaded-and-used-above-the-fold.js
|
|
119
|
+
|
|
120
|
+
- **If fonts preloaded but not used above-the-fold** → Recommend removing preloads
|
|
121
|
+
- **If fonts used but not preloaded** → Recommend adding preload
|
|
122
|
+
- **If many font variants loaded** → Suggest subsetting or reducing variants
|
|
123
|
+
|
|
124
|
+
### After First-And-Third-Party-Script-Info.js
|
|
125
|
+
|
|
126
|
+
- **If many third-party scripts (>5)** → Run:
|
|
127
|
+
1. **First-And-Third-Party-Script-Timings.js** (detailed timing)
|
|
128
|
+
2. **JS-Execution-Time-Breakdown.js** (execution impact)
|
|
129
|
+
- **If third-party scripts are slow** → Recommend async/defer or removal
|
|
130
|
+
|
|
131
|
+
### After SSR-Hydration-Data-Analysis.js
|
|
132
|
+
|
|
133
|
+
- **If hydration data > 100KB** → Recommend optimization strategies
|
|
134
|
+
- **If multiple hydration scripts** → Investigate framework configuration
|
|
135
|
+
- **If large inline hydration data** → Consider streaming or chunking
|
|
136
|
+
|
|
137
|
+
### After Priority-Hints-Audit.js
|
|
138
|
+
|
|
139
|
+
- **If LCP candidate lacks fetchpriority="high"** → Recommend adding it
|
|
140
|
+
- **If conflicting priorities (preload + low)** → Recommend resolving conflict
|
|
141
|
+
- **If fetchpriority on non-critical resources** → Review priority strategy
|
|
142
|
+
|
|
143
|
+
### After Prefetch-Resource-Validation.js
|
|
144
|
+
|
|
145
|
+
- **If >10 prefetch hints** → Recommend reducing to critical resources
|
|
146
|
+
- **If individual prefetch > 500KB** → Question necessity
|
|
147
|
+
- **If total prefetch > 2MB** → Flag as mobile bandwidth concern
|
|
148
|
+
|
|
149
|
+
### After Critical-CSS-Detection.js
|
|
150
|
+
|
|
151
|
+
- **If render-blocking CSS > 14KB** → Recommend inlining critical CSS
|
|
152
|
+
- **If no inline CSS but has blocking stylesheets** → Suggest critical CSS extraction
|
|
153
|
+
- **If CSS > 50KB total** → Run **CSS-Media-Queries-Analysis.js** to find savings
|
|
154
|
+
|
|
155
|
+
### After Back-Forward-Cache.js
|
|
156
|
+
|
|
157
|
+
- **If bfcache blocked** → Provide specific remediation based on blocking reasons
|
|
158
|
+
- **If bfcache eligible** → Confirm optimization is working
|
|
159
|
+
|
|
160
|
+
### After Client-Side-Redirect-Detection.js
|
|
161
|
+
|
|
162
|
+
- **If client-side redirects detected** → Recommend server-side redirects
|
|
163
|
+
- **If redirect chain found** → Suggest eliminating intermediate hops
|