Skip to content

Instantly share code, notes, and snippets.

@jacob-ebey
Created July 2, 2022 06:41
Show Gist options
  • Save jacob-ebey/7a02af8a3cb27db17534cbca15556180 to your computer and use it in GitHub Desktop.
Save jacob-ebey/7a02af8a3cb27db17534cbca15556180 to your computer and use it in GitHub Desktop.
Inline CSS for Remix express applications
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;
@lili21
Copy link

lili21 commented Jul 28, 2022

how about do it at build stage?

@jacob-ebey
Copy link
Author

jacob-ebey commented Oct 8, 2022

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