Skip to content

Commit

Permalink
Adopt script rejection pattern for link onerror. (#42645)
Browse files Browse the repository at this point in the history
The HTML `<link>` element's `onerror` function receives an Event-shaped object. The rejection here expects an `Error` shaped object. This PR 1:1 adopts the same pattern for `<link>` `onerror` that `script` `onerror` uses.

Attached you'll see an image that demonstrates that it _can_ end up in this state from turbo.build's Sentry logs:
<img width="919" alt="Screen Shot 2022-11-09 at 2 43 11 AM" src="https://user-images.githubusercontent.com/20542/200648928-12b85642-c9b5-4d7e-b4fe-2b2d420a6669.png">
  • Loading branch information
nathanhammond committed Nov 9, 2022
1 parent c1b76bd commit c962f2d
Showing 1 changed file with 15 additions and 14 deletions.
29 changes: 15 additions & 14 deletions packages/next/client/route-loader.ts
Expand Up @@ -80,6 +80,16 @@ export interface RouteLoader {
prefetch(route: string): Promise<void>
}

const ASSET_LOAD_ERROR = Symbol('ASSET_LOAD_ERROR')
// TODO: unexport
export function markAssetError(err: Error): Error {
return Object.defineProperty(err, ASSET_LOAD_ERROR, {})
}

export function isAssetError(err?: Error): boolean | undefined {
return err && ASSET_LOAD_ERROR in err
}

function hasPrefetch(link?: HTMLLinkElement): boolean {
try {
link = document.createElement('link')
Expand All @@ -101,13 +111,13 @@ function prefetchViaDom(
as: string,
link?: HTMLLinkElement
): Promise<any> {
return new Promise<void>((res, rej) => {
return new Promise<void>((resolve, reject) => {
const selector = `
link[rel="prefetch"][href^="${href}"],
link[rel="preload"][href^="${href}"],
script[src^="${href}"]`
if (document.querySelector(selector)) {
return res()
return resolve()
}

link = document.createElement('link')
Expand All @@ -116,8 +126,9 @@ function prefetchViaDom(
if (as) link!.as = as
link!.rel = `prefetch`
link!.crossOrigin = process.env.__NEXT_CROSS_ORIGIN!
link!.onload = res as any
link!.onerror = rej
link!.onload = resolve as any
link!.onerror = () =>
reject(markAssetError(new Error(`Failed to prefetch: ${href}`)))

// `href` should always be last:
link!.href = href
Expand All @@ -126,16 +137,6 @@ function prefetchViaDom(
})
}

const ASSET_LOAD_ERROR = Symbol('ASSET_LOAD_ERROR')
// TODO: unexport
export function markAssetError(err: Error): Error {
return Object.defineProperty(err, ASSET_LOAD_ERROR, {})
}

export function isAssetError(err?: Error): boolean | undefined {
return err && ASSET_LOAD_ERROR in err
}

function appendScript(
src: TrustedScriptURL | string,
script?: HTMLScriptElement
Expand Down

0 comments on commit c962f2d

Please sign in to comment.