Skip to content

Commit 4619375

Browse files
committed
fix(core): properly recognize failed fetch responses when loading external resources in JIT
Currently when loading external resources in JIT, when `fetch` fails, the `text` is empty and the component is loading. This hides the actual underlying fetch error. We should properly detect this and error out.
1 parent 6597ac0 commit 4619375

File tree

2 files changed

+21
-5
lines changed

2 files changed

+21
-5
lines changed

packages/core/src/errors.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,7 @@ export const enum RuntimeErrorCode {
126126
MISSING_NG_MODULE_DEFINITION = 915,
127127
MISSING_DIRECTIVE_DEFINITION = 916,
128128
NO_COMPONENT_FACTORY_FOUND = 917,
129+
EXTERNAL_RESOURCE_LOADING_FAILED = 918,
129130

130131
// Signal integration errors
131132
REQUIRED_INPUT_NO_VALUE = -950,
@@ -143,7 +144,7 @@ export const enum RuntimeErrorCode {
143144
RUNTIME_DEPS_INVALID_IMPORTED_TYPE = 980,
144145
RUNTIME_DEPS_ORPHAN_COMPONENT = 981,
145146

146-
// Resource errors
147+
// resource() API errors
147148
MUST_PROVIDE_STREAM_OPTION = 990,
148149
RESOURCE_COMPLETED_BEFORE_PRODUCING_VALUE = 991,
149150

packages/core/src/metadata/resource_loading.ts

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
* found in the LICENSE file at https://angular.dev/license
77
*/
88

9+
import {RuntimeError, RuntimeErrorCode} from '../errors';
910
import {Type} from '../interface/type';
1011

1112
import type {Component} from './directives';
@@ -43,7 +44,7 @@ import type {Component} from './directives';
4344
* contents of the resolved URL. Browser's `fetch()` method is a good default implementation.
4445
*/
4546
export function resolveComponentResources(
46-
resourceResolver: (url: string) => Promise<string | {text(): Promise<string>}>,
47+
resourceResolver: (url: string) => Promise<string | {text(): Promise<string>; status?: number}>,
4748
): Promise<void> {
4849
// Store all promises which are fetching the resources.
4950
const componentResolved: Promise<void>[] = [];
@@ -54,7 +55,7 @@ export function resolveComponentResources(
5455
let promise = urlMap.get(url);
5556
if (!promise) {
5657
const resp = resourceResolver(url);
57-
urlMap.set(url, (promise = resp.then(unwrapResponse)));
58+
urlMap.set(url, (promise = resp.then((res) => unwrapResponse(url, res))));
5859
}
5960
return promise;
6061
}
@@ -147,8 +148,22 @@ export function isComponentResourceResolutionQueueEmpty() {
147148
return componentResourceResolutionQueue.size === 0;
148149
}
149150

150-
function unwrapResponse(response: string | {text(): Promise<string>}): string | Promise<string> {
151-
return typeof response == 'string' ? response : response.text();
151+
function unwrapResponse(
152+
url: string,
153+
response: string | {text(): Promise<string>; status?: number},
154+
): string | Promise<string> {
155+
if (typeof response === 'string') {
156+
return response;
157+
}
158+
if (response.status !== 200) {
159+
return Promise.reject(
160+
new RuntimeError(
161+
RuntimeErrorCode.EXTERNAL_RESOURCE_LOADING_FAILED,
162+
ngDevMode && `Could not load resource: ${url}. Response status: ${response.status}`,
163+
),
164+
);
165+
}
166+
return response.text();
152167
}
153168

154169
function componentDefResolved(type: Type<any>): void {

0 commit comments

Comments
 (0)