Created
July 2, 2022 06:41
-
-
Save jacob-ebey/7a02af8a3cb27db17534cbca15556180 to your computer and use it in GitHub Desktop.
Inline CSS for Remix express applications
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
let fs = require("fs"); | |
// TODO: Make it configurable based on publicPath and assetsBuildDirectory | |
function inlineCssMiddleware() { | |
/** | |
* | |
* @param {import("express").Request} req | |
* @param {import("express").Response} res | |
* @param {import("express").NextFunction} next | |
*/ | |
function inlineCssMiddlewareHandler(req, res, next) { | |
let ogWrite = res.write; | |
let ogEnd = res.end; | |
/** @type {[Uint8Array[], BufferEncoding ((error?: Error | null | undefined) => void) | undefined]} */ | |
let chunks = []; | |
res.write = function inlineCssWrite(chunk, encoding, callback) { | |
chunks.push([chunk, encoding, callback]); | |
}; | |
res.end = function inlineCssEnd(chunk) { | |
if (chunk && typeof chunk !== "function") { | |
chunks.push([chunk, "utf8", undefined]); | |
} | |
if (chunks.length) { | |
let body = Buffer.concat( | |
chunks.map(([chunk, encoding]) => Buffer.from(chunk, encoding)) | |
).toString("utf8"); | |
let cssMatches = body.matchAll( | |
/<link[\s0-9A-z\.\_\-\=\"\']{1,}rel=["']stylesheet["'][\s0-9A-z\.\_\-\=\"\']{1,}("\/build\/_assets\/[0-9A-z\.\_\-]+\.css")/g | |
); | |
let css = ""; | |
for (let match of [...cssMatches].reverse()) { | |
css += | |
fs | |
.readFileSync(`public${match[1].slice(1, -1)}`, "utf8") | |
.replace(/\/*# sourceMappingURL=.* \*\/[\s\r\n]{0,}$/, "") + "\n"; | |
body = | |
body.slice(0, match.index) + | |
match[0].replace( | |
match[1], | |
`${match[1]} media="none" onload="if(media!='all')media='all'"` | |
) + | |
body.slice(match.index + match[0].length); | |
} | |
if (css) { | |
body = body.replace("</head>", `<style>${css}</style></head>`); | |
} | |
ogWrite.call(res, body); | |
chunks.forEach(([, , callback]) => callback && callback()); | |
} | |
ogEnd.call(res); | |
}; | |
next(); | |
} | |
return inlineCssMiddlewareHandler; | |
} | |
module.exports = inlineCssMiddleware; |
This was done to show it's possible. I don't think it's a good idea for perf / caching at all and shouldn't be used. That said, this is 100x better than other framework approaches as it inlines and also still loads the css asset to cache it for client side navigations in the future.
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
how about do it at build stage?