Skip to content

Instantly share code, notes, and snippets.

@Igloczek
Last active June 17, 2024 17:45
Show Gist options
  • Save Igloczek/30d03cae4230396190c18916c95601d3 to your computer and use it in GitHub Desktop.
Save Igloczek/30d03cae4230396190c18916c95601d3 to your computer and use it in GitHub Desktop.
Astro middleware for inlining critical JS to reduce requests chains
// Not sure whether it's genius or stupid, but it definitely works.
// The goal is to reduce chaining critical requests.
// By default in most cases Astro do this: html -> small chunk -> big chunk(s), which is kinda like Require.js, where you need to download file, to know what file you want to download.
// Code below takes this small chunk and inlines it into html, so we have at least one client-side request to make less: html -> big chunk(s).
// Additionally we can add "modulepreload" to start fetching those files faster (not sure where in this particular case it makes any sense, but was easy to add, so I did it).
// For sure this code isn't optimal, I just quickly hacked it just to have PoC, but I belive it won't hurt server-side performance that much.
// If you know some better way to do it, that doesn't require digging through HTML and replacing strings, please let me know.
import fs from "node:fs"
import path from "node:path"
import { fileURLToPath } from "node:url"
import { defineMiddleware } from "astro:middleware"
const astroRoot = fileURLToPath(
new URL("../../../dist/client/_astro", import.meta.url),
)
const jsFiles = fs
.readdirSync(astroRoot)
.filter((file) => file.endsWith(".js"))
.map((file) => {
const filePath = path.join(astroRoot, file)
const content = fs
.readFileSync(filePath, "utf-8")
.replaceAll('import"./', 'import"/_astro/')
.replaceAll('from"./', 'from"/_astro/')
.replaceAll("sourceMappingURL=", "sourceMappingURL=/_astro/")
const scripts = [
...content.matchAll(/import"([^"]+)"|import[^"]+from"([^"]+)/g),
]?.map((match) => match[1] || match[2])
const modules = scripts
.map((path) => `<link rel="modulepreload" href="${path}" />`)
.join("")
return {
path: file,
content,
modules,
}
})
export const onRequest = defineMiddleware(async (context, next) => {
try {
const response = await next()
if (response.headers.get("Content-Type") !== "text/html") {
return response
}
const raw = await response.text()
const html = raw.replace(
/<script\stype="module"\s+src="\/_astro\/([^"]+)"><\/script>/g,
(match, scriptPath) => {
const jsFile = jsFiles.find((file) => file.path === scriptPath)
if (jsFile) {
return `${jsFile.modules}<script type="module">${jsFile.content}</script>`
} else {
return match
}
},
)
return new Response(html, {
status: response.status,
headers: response.headers,
})
} catch (error) {
console.error(error)
return next()
}
})
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment