minimalcss requires that you have your HTML in a serving HTTP web page so that puppeteer
can open it to find out the CSS within. Suppose, in your build system, you don't yet really have a server. Well, what you can do is start one on-the-fly and shut it down as soon as you're done.
Suppose you have .html file
First install all the stuff:
yarn add minimalcss http-server
Then run it:
const path = require("path");
const minimalcss = require("minimalcss");
const httpServer = require("http-server");
const HTML_FILE = "index.html";
(async () => {
const server = httpServer.createServer({
root: path.dirname(path.resolve(HTML_FILE)),
});
server.listen(8080);
let result;
try {
result = await minimalcss.minimize({
urls: ["http://0.0.0.0:8080/" + HTML_FILE],
});
} catch (err) {
console.error(err);
throw err;
} finally {
server.close();
}
console.log(result.finalCss);
})();
And the index.html
file:
<!DOCTYPE html>
<html>
<head>
<link rel="stylesheet" href="styles.css">
</head>
<body>
<p>Hi @peterbe</p>
</body>
</html>
And the styles.css
file:
h1 {
color: red;
}
p,
li {
font-weight: bold;
}
And the output from running that Node script:
p{font-weight:700}
It works!
Suppose all you have is the HTML string and the CSS blob(s)
Suppose all you have is a string of HTML and a list of strings of CSS:
const fs = require("fs");
const path = require("path");
const minimalcss = require("minimalcss");
const httpServer = require("http-server");
const HTML_BODY = `
<p>Hi Peterbe</p>
`;
const CSSes = [
`
h1 {
color: red;
}
p,
li {
font-weight: bold;
}
`,
];
(async () => {
const csses = CSSes.map((css, i) => {
fs.writeFileSync(`${i}.css`, css);
return `<link rel="stylesheet" href="${i}.css">`;
});
const html = `<!doctype html><html>
<head>${csses}</head>
<body>${HTML_BODY}</body>
</html>`;
const fp = path.resolve("./index.html");
fs.writeFileSync(fp, html);
const server = httpServer.createServer({
root: path.dirname(fp),
});
server.listen(8080);
let result;
try {
result = await minimalcss.minimize({
urls: ["http://0.0.0.0:8080/" + path.basename(fp)],
});
} catch (err) {
console.error(err);
throw err;
} finally {
server.close();
fs.unlinkSync(fp);
CSSes.forEach((_, i) => fs.unlinkSync(`${i}.css`));
}
console.log(result.finalCss);
})();
Truth be told, you'll need a good pinch of salt to appreciate that example code. It works but most likely, if you're into web performance so much that you're even doing this, your parameters are likely to be more complex.
Suppose you have your own puppeteer
instance
In the first example above, minimalcss
will create an instance of puppeteer
(e.g. const browser = await puppeteer.launch()
) but that means you have less control over which version of puppeteer
or which parameters you need. Also, if you have to run minimalcss
on a bunch of pages it's costly to have to create and destroy puppeteer
browser instances repeatedly.
To modify the original example, here's how you use your own instance of puppeteer
:
const path = require("path");
+ const puppeteer = require("puppeteer");
const minimalcss = require("minimalcss");
const httpServer = require("http-server");
const HTML_FILE = "index.html"; // THIS IS YOURS
(async () => {
const server = httpServer.createServer({
root: path.dirname(path.resolve(HTML_FILE)),
});
server.listen(8080);
+ const browser = await puppeteer.launch(/* your special options */);
+
let result;
try {
result = await minimalcss.minimize({
urls: ["http://0.0.0.0:8080/" + HTML_FILE],
+ browser,
});
} catch (err) {
console.error(err);
throw err;
} finally {
+ await browser.close();
server.close();
}
console.log(result.finalCss);
})();
Note that this doesn't buy us anything in this particular example. But that's where your imagination comes in!
Conclusion
You can see the code here as a git
repo if that helps.
The point is that this might solve some of the chicken-and-egg problem you might have is that you're building your perfect HTML + CSS and you want to perfect it before you ship it.
Note also that there are other ways to run minimalcss
other than programmatically. For example, minimalcss-server
is minimalcss
wrapped in a express
server.
Another thing that you might have is that you have multiple .html
files that you want to process. The same technique applies but you just need to turn it into a loop and make sure you call server.close()
(and optionally await browser.close()
) when you know you've processed the last file. Exercise left to the reader?