Inline all css stylesheets into html.

I needed a quick an dirty way to inline css in all html files within a folder structure such as this:

dist
├── assets
│   ├── another.css
│   └── style.css
├── a
│   └── 1.html
├── b
│   ├── 1.html
│   ├── 2.html
│   └── 3.html
└── c
    ├── 1.html
    ├── 2.html
    └── c1
        └── c11.html

The following script will modifiy all the html files and replace the stylessheets <link> elements with a single <style> tag containing all the encountered css rules.

Note, that if you are working on a bigger site, that does have a frontend build pipeline, you probably want use something like netlify-plugin-inline-critical-css, critical or Penthouse to extract only the critical css and inline it.

'use strict';

let glob = require('glob');
let fs = require('fs');
let path = require('path');
let cheerio = require('cheerio');

let base = 'dist';

glob('/**/*.html', { root: base }, (err, files) => {
  if (err) throw err;

  files.forEach(filePath => {

    let css = [];
    const html = fs.readFileSync(filePath, 'utf8');
    const $ = cheerio.load(html);

    $('link[rel=stylesheet]').each((_, link) => {
      const $link = $(link);
      const cssPath = $link.attr('href')
      
      if(cssPath.match('^https?://')){
        return;
      }

      const dir = path.dirname(filePath);
      const absPath = path.join(dir, cssPath)

      const data = fs.readFileSync(absPath, 'utf8')
      css.push(data)

      $link.remove()
    })

    const cssOutput = css.join('\n')
    const style = $('<style>').text(cssOutput)
    $('head').append(style)

    fs.writeFileSync(filePath, $.html())
    console.log('Wrote', filePath)

  });

});

Line 10: Get file paths of all html files within dist and its subfolders

Line 19: Iterate trough all linked stylesheets of the page. See the cheerio library which provides a jQuery compatible interface for nodejs

Line 28: The css files are mostly linked trough relatives paths. Join them with the directory of the current html file to the the absolute path of the css file. E.g.:

join(dirname("/home/test/dist/a/b/a.html"), "../../assets/style.css")
> `/home/test/dist/assets/style.css`

Line 33: Remove stylesheet from DOM.

Line 36: Create inline style element with the css collected from the external files. Inserted them in same order should avoid problems with the css rules therein.

Line 40: Overwrite existing html