Skip to content

Commit

Permalink
Dispatch turbo:before-fetch-{request,response} during preloading
Browse files Browse the repository at this point in the history
Closes [hotwired#963][]

Replace the raw call to `fetch` with a new `FetchRequest` instance that
treats the `Preloaded` instances as its delegate. During that request's
lifecycle, dispatch the `turbo:before-fetch-request` and
`turbo:before-fetch-response` events with the `<a>` element as its
target.

Prepare the request with the [Sec-Purpose][] header in the
`prepareRequest` delegate callback.

Write to the snapshot cache from within the
`requestSucceededWithResponse` delegate callback.

[hotwired#963]: hotwired#963
[Sec-Purpose]: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Sec-Purpose#prefetch
  • Loading branch information
seanpdoyle committed Dec 8, 2023
1 parent 098aafc commit f214f61
Show file tree
Hide file tree
Showing 5 changed files with 43 additions and 6 deletions.
30 changes: 25 additions & 5 deletions src/core/drive/preloader.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { PageSnapshot } from "./page_snapshot"
import { fetch } from "../../http/fetch"
import { FetchMethod, FetchRequest } from "../../http/fetch_request"

export class Preloader {
selector = "a[data-turbo-preload]"
Expand Down Expand Up @@ -36,17 +36,37 @@ export class Preloader {
return
}

const fetchRequest = new FetchRequest(this, FetchMethod.get, location, new URLSearchParams(), link)
await fetchRequest.perform()
}

// Fetch request delegate

prepareRequest(fetchRequest) {
fetchRequest.headers["Sec-Purpose"] = "prefetch"
}

async requestSucceededWithResponse(fetchRequest, fetchResponse) {
try {
const response = await fetch(location.toString(), { headers: { "Sec-Purpose": "prefetch", Accept: "text/html" } })
const responseText = await response.text()
const snapshot = PageSnapshot.fromHTMLString(responseText)
const responseHTML = await fetchResponse.responseHTML
const snapshot = PageSnapshot.fromHTMLString(responseHTML)

this.snapshotCache.put(location, snapshot)
this.snapshotCache.put(fetchRequest.url, snapshot)
} catch (_) {
// If we cannot preload that is ok!
}
}

requestStarted(fetchRequest) {}

requestErrored(fetchRequest) {}

requestFinished(fetchRequest) {}

requestPreventedHandlingResponse(fetchRequest, fetchResponse) {}

requestFailedWithResponse(fetchRequest, fetchResponse) {}

#preloadAll = () => {
this.preloadOnLoadLinksForView(document.body)
}
Expand Down
1 change: 1 addition & 0 deletions src/tests/fixtures/hot_preloading.html
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
<meta charset="utf-8">
<title>Page That Links to Preloading Page</title>
<script src="/dist/turbo.es2017-umd.js" data-turbo-track="reload"></script>
<script src="/src/tests/fixtures/test.js"></script>
</head>

<body>
Expand Down
1 change: 1 addition & 0 deletions src/tests/fixtures/preloaded.html
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
<meta charset="utf-8">
<title>Preloaded Page</title>
<script src="/dist/turbo.es2017-umd.js" data-turbo-track="reload"></script>
<script src="/src/tests/fixtures/test.js"></script>
</head>

<body>
Expand Down
1 change: 1 addition & 0 deletions src/tests/fixtures/preloading.html
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
<meta charset="utf-8">
<title>Preloading Page</title>
<script src="/dist/turbo.es2017-umd.js" data-turbo-track="reload"></script>
<script src="/src/tests/fixtures/test.js"></script>
</head>

<body>
Expand Down
16 changes: 15 additions & 1 deletion src/tests/functional/preloader_tests.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { test } from "@playwright/test"
import { assert } from "chai"
import { nextEventOnTarget } from "../helpers/page"
import { nextEventNamed, nextEventOnTarget } from "../helpers/page"

test("preloads snapshot on initial load", async ({ page }) => {
// contains `a[rel="preload"][href="http://localhost:9000/src/tests/fixtures/preloaded.html"]`
Expand All @@ -12,6 +12,20 @@ test("preloads snapshot on initial load", async ({ page }) => {
assert.ok(await urlInSnapshotCache(page, href))
})

test("preloading dispatch turbo:before-fetch-{request,response} events", async ({ page }) => {
await page.goto("/src/tests/fixtures/preloading.html")

const link = await page.locator("#preload_anchor")
const href = await link.evaluate((link) => link.href)

const { url, fetchOptions } = await nextEventNamed(page, "turbo:before-fetch-request")
const { fetchResponse } = await nextEventNamed(page, "turbo:before-fetch-response")

assert.equal(href, url, "dispatches request during preloading")
assert.equal(fetchOptions.headers.Accept, "text/html, application/xhtml+xml")
assert.equal(fetchResponse.response.url, href)
})

test("preloads snapshot on page visit", async ({ page }) => {
// contains `a[rel="preload"][href="http://localhost:9000/src/tests/fixtures/preloading.html"]`
await page.goto("/src/tests/fixtures/hot_preloading.html")
Expand Down

0 comments on commit f214f61

Please sign in to comment.