Skip to content

Commit 29f3eed

Browse files
refactor to use seperate files for demo cwv suggestions
1 parent 555ef98 commit 29f3eed

9 files changed

Lines changed: 345 additions & 24 deletions

File tree

src/tasks/cwv-demo-suggestions-processor/handler.js

Lines changed: 34 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -69,19 +69,47 @@ function hasExistingIssues(suggestion) {
6969
return data.issues && isNonEmptyArray(data.issues);
7070
}
7171

72+
/**
73+
* Reads content from a static file
74+
* @param {string} fileName - The filename to read
75+
* @param {object} logger - The logger object
76+
* @returns {string|null} The file content or null if error
77+
*/
78+
function readStaticFile(fileName, logger) {
79+
try {
80+
const filePath = path.resolve(dirname, '../../../static', fileName);
81+
const content = fs.readFileSync(filePath, 'utf-8');
82+
logger.debug(`Successfully read content from ${fileName}`);
83+
return content;
84+
} catch (error) {
85+
logger.error(`Error reading static file ${fileName}: ${error.message}`);
86+
return null;
87+
}
88+
}
89+
7290
/**
7391
* Gets a random suggestion from the available suggestions for a given issue type
7492
* @param {string} issueType - The type of issue (lcp, cls, inp)
93+
* @param {object} logger - The logger object
7594
* @returns {string|null} A random suggestion or null if none available
7695
*/
77-
function getRandomSuggestion(issueType, cwvReferenceSuggestions) {
96+
function getRandomSuggestion(issueType, cwvReferenceSuggestions, logger) {
7897
const suggestions = cwvReferenceSuggestions[issueType];
7998
if (!isNonEmptyArray(suggestions)) {
8099
return null;
81100
}
82101

83102
const randomIndex = Math.floor(Math.random() * suggestions.length);
84-
return suggestions[randomIndex];
103+
const fileName = suggestions[randomIndex];
104+
105+
// Read content from the referenced file
106+
const content = readStaticFile(fileName, logger);
107+
if (!content) {
108+
logger.error(`Failed to read content from ${fileName}`);
109+
return null;
110+
}
111+
112+
return content;
85113
}
86114

87115
/**
@@ -123,7 +151,7 @@ async function updateSuggestionWithGenericIssues(
123151
}
124152

125153
for (const issueType of metricIssues) {
126-
const randomSuggestion = getRandomSuggestion(issueType, cwvReferenceSuggestions);
154+
const randomSuggestion = getRandomSuggestion(issueType, cwvReferenceSuggestions, logger);
127155
if (randomSuggestion) {
128156
const genericIssue = {
129157
type: issueType,
@@ -163,6 +191,7 @@ async function processCWVOpportunity(opportunity, logger, env, slackContext) {
163191

164192
if (hasSuggestionsWithIssues) {
165193
logger.info(`Opportunity ${opportunity.getId()} already has suggestions with issues, skipping generic suggestions`);
194+
await say(env, logger, slackContext, `ℹ️ CWV suggestions already exist for opportunity ${opportunity.getId()}, skipping demo suggestions`);
166195
return 0;
167196
}
168197

@@ -216,8 +245,8 @@ async function processCWVOpportunity(opportunity, logger, env, slackContext) {
216245
const issuesAddedResults = await Promise.all(updatePromises);
217246
const totalIssuesAdded = issuesAddedResults.reduce((sum, issuesAdded) => sum + issuesAdded, 0);
218247
if (totalIssuesAdded > 0) {
219-
await say(env, logger, slackContext, `🎯 Added ${totalIssuesAdded} generic CWV suggestions for opportunity ${opportunity.getId()}`);
220-
logger.info(`Added ${totalIssuesAdded} generic CWV suggestions for opportunity ${opportunity.getId()}`);
248+
logger.info(`Added ${totalIssuesAdded} demo CWV suggestions for opportunity ${opportunity.getId()} (regular CWV suggestions were not present)`);
249+
await say(env, logger, slackContext, `✅ Added ${totalIssuesAdded} demo CWV suggestions for opportunity ${opportunity.getId()} (regular CWV suggestions were not present)`);
221250
} else {
222251
await say(env, logger, slackContext, `:x: No generic CWV suggestions added for opportunity ${opportunity.getId()}`);
223252
logger.info(`No generic CWV suggestions added for opportunity ${opportunity.getId()}`);

static/aem-best-practices.json

Lines changed: 6 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,14 @@
11
{
22
"lcp": [
3-
"## **Optimize Image Delivery:**\n• Use next-gen formats (WebP, AVIF)\n• Implement lazy loading\n• Compress images appropriately\n• Use responsive images with srcset\n• Consider image sprites for icons\n• Preload critical above-the-fold images",
4-
"## **Optimize CSS Loading:**\n• Inline critical CSS\n• Defer non-critical CSS\n• Use media queries conditionally\n• Minify CSS files\n• Consider CSS-in-JS for dynamic styling\n• Implement CSS containment",
5-
"## **Optimize Server & Network:**\n• Use CDN for global distribution\n• Enable GZIP/Brotli compression\n• Implement HTTP/2 or HTTP/3\n• Use edge caching strategies\n• Consider service workers\n• Use resource hints (preconnect, dns-prefetch)"
3+
"lcp1.md",
4+
"lcp2.md",
5+
"lcp3.md"
66
],
77
"cls": [
8-
"## **Prevent Layout Shifts:**\n• Set explicit width/height on images/videos\n• Use aspect-ratio CSS property\n• Implement skeleton screens\n• Reserve space for dynamic content\n• Avoid inserting content above existing content\n• Use CSS Grid/Flexbox for predictable layouts",
9-
"## **Optimize Animations:**\n• Use CSS transforms and opacity\n• Implement will-change property\n• Use transform3d for stacking context\n• Avoid changing layout properties during animations\n• Use requestAnimationFrame for smooth animations\n• Consider CSS transitions over JavaScript animations",
10-
"## **Maintain Visual Stability:**\n• Implement skeleton screens\n• Reserve space for dynamic content\n• Use content-visibility CSS property\n• Implement CSS containment\n• Use CSS Grid and Flexbox\n• Avoid cumulative layout shifts"
8+
"cls1.md",
9+
"cls2.md"
1110
],
1211
"inp": [
13-
"## **Break Up JavaScript Tasks:**\n• Use setTimeout or requestIdleCallback\n• Implement web workers for CPU-intensive operations\n• Use requestAnimationFrame for visual updates\n• Consider Intersection Observer API\n• Implement virtual scrolling for long lists\n• Use microtasks and macrotasks appropriately",
14-
"## **Optimize Event Handling:**\n• Debounce input events\n• Use passive event listeners\n• Implement event delegation\n• Use AbortController to cancel operations\n• Consider ResizeObserver and MutationObserver\n• Optimize event listener registration",
15-
"## **Optimize DOM Manipulation:**\n• Implement virtual scrolling\n• Use DocumentFragment for batch updates\n• Implement object pooling\n• Use Web Components for encapsulation\n• Implement progressive enhancement\n• Minimize DOM queries and mutations"
12+
"inp1.md"
1613
]
1714
}

static/cls1.md

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
### Prevent Layout Shifts by Specifying Image Dimensions
2+
3+
- **Metric**: CLS
4+
- **Category**: images
5+
- **Priority**: High
6+
- **Effort**: Easy
7+
- **Impact**: Reduces CLS by 0.1-0.2
8+
9+
**Description**
10+
11+
Images on the page are loading without their dimensions being specified. This causes content to jump around as images load, creating a jarring user experience and a high Cumulative Layout Shift (CLS) score.
12+
13+
**Implementation**
14+
15+
Add `width` and `height` attributes to all `<img>` elements. This allows the browser to reserve the correct amount of space for the image before it loads, preventing content from shifting. Use CSS to ensure images remain responsive.
16+
17+
**Code Example**
18+
```html
19+
<!-- Before -->
20+
<img src="/path/to/image.jpg" alt="Description">
21+
22+
<!-- After -->
23+
<img src="/path/to/image.jpg" width="800" height="600" alt="Description" style="max-width: 100%; height: auto;">
24+
```

static/cls2.md

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
### Stabilize Layout During Font Loading
2+
3+
- **Metric**: CLS
4+
- **Category**: fonts
5+
- **Priority**: Medium
6+
- **Effort**: Medium
7+
- **Impact**: Reduces CLS by 0.05-0.1
8+
9+
**Description**
10+
11+
The switch between the fallback font and the custom web font causes a noticeable shift in layout because the two fonts have different sizes. This contributes to the CLS score and makes the page feel unstable.
12+
13+
**Implementation**
14+
15+
Use the `size-adjust` CSS descriptor in your `@font-face` rule to normalize the size of the fallback font to match the custom font. This minimizes the layout shift when the custom font loads. You can use online tools to calculate the correct `size-adjust` value.
16+
17+
**Code Example**
18+
```css
19+
/* Example for matching Arial to a custom font */
20+
@font-face {
21+
font-family: 'FallbackFont';
22+
size-adjust: 95%; /* Adjust this value based on font metrics */
23+
src: local('Arial');
24+
}
25+
26+
body {
27+
/* The browser will use the adjusted fallback font until YourAppFont loads */
28+
font-family: 'YourAppFont', 'FallbackFont', sans-serif;
29+
}
30+
```

static/inp1.md

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
### Improve Page Interactivity by Deferring Non-Essential JavaScript
2+
3+
- **Metric**: INP
4+
- **Category**: javascript
5+
- **Priority**: High
6+
- **Effort**: Medium
7+
- **Impact**: Reduces INP by 100ms-200ms
8+
9+
**Description**
10+
11+
A large JavaScript bundle is being downloaded and executed early during page load, which blocks the browser from responding to user interactions like clicks or typing. This leads to a poor Interaction to Next Paint (INP) score and makes the page feel sluggish.
12+
13+
**Implementation**
14+
15+
Split your JavaScript into smaller chunks. Load essential, interactive scripts with `defer` so they don't block parsing. Load scripts for non-critical features (e.g., social media widgets, analytics) after the page is interactive, either on a delay (`setTimeout`) or when the user scrolls them into view.
16+
17+
**Code Example**
18+
```html
19+
<!-- Critical interactive script, does not block HTML parsing -->
20+
<script src="main-interactive.js" defer></script>
21+
22+
<!-- Non-critical script loaded after a 4-second delay -->
23+
<script>
24+
setTimeout(() => {
25+
const script = document.createElement('script');
26+
script.src = 'heavy-analytics.js';
27+
document.body.appendChild(script);
28+
}, 4000);
29+
</script>
30+
```

static/lcp1.md

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
### Prioritize the LCP Image and Lazy-Load Other Images
2+
3+
- **Metric**: LCP
4+
- **Category**: images
5+
- **Priority**: High
6+
- **Effort**: Easy
7+
- **Impact**: Reduces LCP by 400ms-800ms
8+
9+
**Description**
10+
11+
The most important image on the page (the LCP element) is competing for network resources with other, less critical images. This delays the LCP and worsens the user experience. By explicitly telling the browser which image to load eagerly and which to load lazily, we can ensure the main content is visible much faster.
12+
13+
**Implementation**
14+
15+
Set `loading="eager"` on the LCP `<img>` element. While this is often the browser's default, explicitly setting it can help override other platform-level lazy-loading defaults. Crucially, set `loading="lazy"` on all other non-critical images that appear below the fold. This prevents them from being loaded until the user scrolls near them, freeing up bandwidth for the LCP image.
16+
17+
**Code Example**
18+
```html
19+
<!-- LCP Image (Above the fold): Prioritize with loading="eager" -->
20+
<img src="/path/to/lcp-image.jpg" loading="eager" width="1200" height="800" alt="Main hero image">
21+
22+
<!-- Other Images (Below the fold): Defer with loading="lazy" -->
23+
<img src="/path/to/another-image-1.jpg" loading="lazy" width="600" height="400" alt="A secondary image">
24+
<img src="/path/to/another-image-2.jpg" loading="lazy" width="600" height="400" alt="Another secondary image">
25+
```

static/lcp2.md

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
### Split CSS into Critical and Non-Critical Files to Unblock Rendering
2+
3+
- **Metric**: LCP
4+
- **Category**: css
5+
- **Priority**: High
6+
- **Effort**: Medium
7+
- **Impact**: Reduces LCP by 300ms-600ms
8+
9+
**Description**
10+
11+
A large, single CSS file is blocking the page from rendering until it is fully downloaded and parsed. Much of this CSS is not needed for the initial view. This "render-blocking" behavior significantly delays when users can see content, negatively impacting LCP.
12+
13+
**Implementation**
14+
15+
Separate your CSS into two parts: "critical" and "non-critical". The critical CSS file should contain only the minimal styles required to render the content visible in the initial viewport (above the fold). Load this file synchronously in the `<head>`. The rest of the styles should be in a separate, non-critical CSS file that is loaded asynchronously, so it doesn't block the initial rendering of the page.
16+
17+
**Code Example**
18+
```html
19+
<head>
20+
<!-- Load critical CSS synchronously to render above-the-fold content -->
21+
<link rel="stylesheet" href="/styles/critical.css">
22+
23+
<!-- Preload and then asynchronously load the main stylesheet -->
24+
<link rel="preload" href="/styles/main.css" as="style" onload="this.onload=null;this.rel='stylesheet'">
25+
26+
<!-- Fallback for browsers without JavaScript -->
27+
<noscript>
28+
<link rel="stylesheet" href="/styles/main.css">
29+
</noscript>
30+
</head>
31+
```

static/lcp3.md

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
### Optimize Custom Font Loading to Speed Up Text Rendering
2+
3+
- **Metric**: LCP
4+
- **Category**: fonts
5+
- **Priority**: Medium
6+
- **Effort**: Medium
7+
- **Impact**: Reduces LCP by 200ms-400ms
8+
9+
**Description**
10+
11+
Custom fonts are blocking the display of important text, including the page's headline, until the font files are fully downloaded. This delay contributes to a higher LCP if the LCP element is a block of text.
12+
13+
**Implementation**
14+
15+
Host fonts on your own domain to avoid an extra connection to a third-party domain. Preload the most critical font files in the `<head>`. Use `font-display: swap;` in your `@font-face` declaration to allow the browser to show a fallback font immediately while the custom font loads.
16+
17+
**Code Example**
18+
```css
19+
/* In your CSS file */
20+
@font-face {
21+
font-family: 'YourAppFont';
22+
src: url('/fonts/yourappfont.woff2') format('woff2');
23+
font-weight: 400;
24+
font-style: normal;
25+
font-display: swap; /* This allows text to be visible while font loads */
26+
}
27+
```
28+
```html
29+
<!-- In HTML <head> -->
30+
<link rel="preload" href="/fonts/yourappfont.woff2" as="font" type="font/woff2" crossorigin>
31+
```

0 commit comments

Comments
 (0)