Skip to content

Commit

Permalink
Dynamic stylesheets can be replaced in-place
Browse files Browse the repository at this point in the history
  • Loading branch information
aaronjensen committed Feb 9, 2024
1 parent edec440 commit 567b55a
Showing 1 changed file with 87 additions and 11 deletions.
98 changes: 87 additions & 11 deletions src/core/drive/page_renderer.js
Original file line number Diff line number Diff line change
Expand Up @@ -77,9 +77,9 @@ export class PageRenderer extends Renderer {
await mergedHeadElements
await newStylesheetElements

if (this.willRender) {
this.removeUnusedDynamicStylesheetElements()
}
// if (this.willRender) {
// this.removeUnusedDynamicStylesheetElements()
// }
}

async replaceBody() {
Expand All @@ -93,16 +93,92 @@ export class PageRenderer extends Renderer {
return this.currentHeadSnapshot.trackedElementSignature == this.newHeadSnapshot.trackedElementSignature
}

// async copyNewHeadStylesheetElements() {
// const loadingElements = []
//
// for (const element of this.newHeadStylesheetElements) {
// loadingElements.push(waitForLoad(element))
//
// document.head.appendChild(element)
// }
//
// await Promise.all(loadingElements)
// }

removeFingerprint(url) {
if (!url) { return url }
return url.replace(/-[0-9a-f]{7,128}(\.digested)?\.css$/, ".css")
}

stylesheetElementIsDynamic(element) {
return element.getAttribute("data-turbo-track") == "dynamic" &&
element.getAttribute("href") != null
}

async copyNewHeadStylesheetElements() {
const loadingElements = []
const preloadElements = []

for (const element of this.newHeadStylesheetElements) {
loadingElements.push(waitForLoad(element))
const newStylesheetElements = this.newHeadStylesheetElements.filter(this.stylesheetElementIsDynamic)
const oldStylesheetElements = this.currentHeadSnapshot.getStylesheetElementsNotInSnapshot(this.newHeadSnapshot).filter(this.stylesheetElementIsDynamic)

document.head.appendChild(element)
const oldElementIndex = Object.fromEntries(oldStylesheetElements.map(element => [this.removeFingerprint(element.getAttribute("href")), element]))

let lastStylesheetElement = null

for (const newElement of newStylesheetElements) {
const newHref = newElement.getAttribute("href")
const hrefWithoutFingerprint = this.removeFingerprint(newHref)
const oldElement = oldElementIndex[hrefWithoutFingerprint]
const oldHref = oldElement && oldElement.getAttribute("href")

if (oldHref == newHref) {
console.log(`Skipping ${newHref}`)
delete oldElementIndex[hrefWithoutFingerprint]
continue
}

// Convert the link tag to a preload so that it can be enabled at the same
// time as others (after everything has loaded) and any old stylesheets can
// be removed at the same time as well.
newElement.setAttribute("as", "style")
newElement.setAttribute("rel", "preload")
preloadElements.push(newElement)

loadingElements.push(waitForLoad(newElement))

if (oldElement) {
console.log(`Updating ${oldElement.getAttribute("href")} to ${newHref}`)

oldElement.after(newElement)
} else {
console.log(`Appending ${newHref} after`, lastStylesheetElement)

// Ensure that the new elements maintain their order as much as possible
if (lastStylesheetElement) {
lastStylesheetElement.after(newElement)
} else {
document.head.appendChild(newElement)
}
}

lastStylesheetElement = newElement
}

await Promise.all(loadingElements)

for (const element of Object.values(oldElementIndex)) {
console.log(`Removing ${element.getAttribute("href")}`)

element.remove()
}

for (const element of preloadElements) {
console.log(`Converting preload ${element.getAttribute("href")}`)

element.setAttribute("rel", "stylesheet")
element.removeAttribute("as")
}
}

copyNewHeadScriptElements() {
Expand All @@ -111,11 +187,11 @@ export class PageRenderer extends Renderer {
}
}

removeUnusedDynamicStylesheetElements() {
for (const element of this.unusedDynamicStylesheetElements) {
document.head.removeChild(element)
}
}
// removeUnusedDynamicStylesheetElements() {
// for (const element of this.unusedDynamicStylesheetElements) {
// document.head.removeChild(element)
// }
// }

async mergeProvisionalElements() {
const newHeadElements = [...this.newHeadProvisionalElements]
Expand Down

0 comments on commit 567b55a

Please sign in to comment.