From fe2fd7e1a898a4525c219065a6d0908988dfd7e2 Mon Sep 17 00:00:00 2001 From: Alex Castle Date: Tue, 12 Sep 2023 15:45:14 -0700 Subject: [PATCH] feat(common): make the warning for lazy-loaded lcp image an error (#51748) upgrade the warning for lazy-loaded lcp images when using NgOptimizedImage to an error BREAKING CHANGE: Previously when NgOptimizedImage directive detected that an LCP image is lazy-loaded, a console warning was produced. Now the directive throws an error to make it more discoverable in a console. If you receive this error, refer to this guide for additional information: https://angular.io/guide/image-directive#step-4-mark-images-as-priority PR Close #51748 --- aio/content/guide/image-directive.md | 2 +- .../ng_optimized_image/lcp_image_observer.ts | 16 ++++++++-------- .../e2e/lcp-check/lcp-check.e2e-spec.ts | 11 +++++------ 3 files changed, 14 insertions(+), 15 deletions(-) diff --git a/aio/content/guide/image-directive.md b/aio/content/guide/image-directive.md index 5aa9118162b..ff039792765 100644 --- a/aio/content/guide/image-directive.md +++ b/aio/content/guide/image-directive.md @@ -65,7 +65,7 @@ Marking an image as `priority` applies the following optimizations: * Sets `loading=eager` (read more about native lazy loading [here](https://web.dev/browser-level-image-lazy-loading)) * Automatically generates a [preload link element](https://developer.mozilla.org/en-US/docs/Web/HTML/Link_types/preload) if [rendering on the server](/guide/universal). -Angular displays a warning during development if the LCP element is an image that does not have the `priority` attribute. A page’s LCP element can vary based on a number of factors - such as the dimensions of a user's screen, so a page may have multiple images that should be marked `priority`. See [CSS for Web Vitals](https://web.dev/css-web-vitals/#images-and-largest-contentful-paint-lcp) for more details. +Angular throws an error during development if the LCP element is an image that does not have the `priority` attribute, as this can hurt loading performance significantly. A page’s LCP element can vary based on a number of factors - such as the dimensions of a user's screen, so a page may have multiple images that should be marked `priority`. See [CSS for Web Vitals](https://web.dev/css-web-vitals/#images-and-largest-contentful-paint-lcp) for more details. #### Step 5: Include Height and Width diff --git a/packages/common/src/directives/ng_optimized_image/lcp_image_observer.ts b/packages/common/src/directives/ng_optimized_image/lcp_image_observer.ts index b464b4583d4..92f64a52a55 100644 --- a/packages/common/src/directives/ng_optimized_image/lcp_image_observer.ts +++ b/packages/common/src/directives/ng_optimized_image/lcp_image_observer.ts @@ -6,7 +6,7 @@ * found in the LICENSE file at https://angular.io/license */ -import {inject, Injectable, OnDestroy, ɵformatRuntimeError as formatRuntimeError} from '@angular/core'; +import {inject, Injectable, OnDestroy, ɵformatRuntimeError as formatRuntimeError, ɵRuntimeError as RuntimeError} from '@angular/core'; import {DOCUMENT} from '../../dom_tokens'; import {RuntimeErrorCode} from '../../errors'; @@ -74,7 +74,7 @@ export class LCPImageObserver implements OnDestroy { if (!img) return; if (!img.priority && !img.alreadyWarnedPriority) { img.alreadyWarnedPriority = true; - logMissingPriorityWarning(imgSrc); + throwMissingPriorityError(imgSrc); } if (img.modified && !img.alreadyWarnedModified) { img.alreadyWarnedModified = true; @@ -118,14 +118,14 @@ export class LCPImageObserver implements OnDestroy { } } -function logMissingPriorityWarning(ngSrc: string) { +function throwMissingPriorityError(ngSrc: string) { const directiveDetails = imgDirectiveDetails(ngSrc); - console.warn(formatRuntimeError( + throw new RuntimeError( RuntimeErrorCode.LCP_IMG_MISSING_PRIORITY, - `${directiveDetails} this image is the Largest Contentful Paint (LCP) ` + - `element but was not marked "priority". This image should be marked ` + - `"priority" in order to prioritize its loading. ` + - `To fix this, add the "priority" attribute.`)); + `${directiveDetails} this image is the Largest Contentful Paint (LCP) element ` + + `but was not marked "priority". This can have a strong negative impact ` + + `on the LCP score of the entire application. This error can be fixed by ` + + `adding the 'priority' attribute to all images which are possibly the LCP element.`); } function logModifiedWarning(ngSrc: string) { diff --git a/packages/core/test/bundling/image-directive/e2e/lcp-check/lcp-check.e2e-spec.ts b/packages/core/test/bundling/image-directive/e2e/lcp-check/lcp-check.e2e-spec.ts index b57818dbf88..ffa3bdccc79 100644 --- a/packages/core/test/bundling/image-directive/e2e/lcp-check/lcp-check.e2e-spec.ts +++ b/packages/core/test/bundling/image-directive/e2e/lcp-check/lcp-check.e2e-spec.ts @@ -27,12 +27,11 @@ describe('NgOptimizedImage directive', () => { srcB = await imgs.get(2).getAttribute('src'); expect(srcB.endsWith('b.png')).toBe(true); - // Make sure that only one warning is in the console for image `a.png`, + // Make sure that only one error is in the console for image `a.png`, // since the `b.png` should be below the fold and not treated as an LCP element. - const logs = await collectBrowserLogs(logging.Level.WARNING); - expect(logs.length).toEqual(2); - // Verify that the error code and the image src are present in the error message. - expect(logs[0].message).toMatch(/NG02955.*?a\.png/); - expect(logs[1].message).toMatch(/NG02964.*?logo-500w\.jpg/); + const logs = await collectBrowserLogs(logging.Level.SEVERE); + expect(logs.length).toEqual(1); + + expect(logs[0].message).toMatch(/RuntimeError: NG02955/); }); });