Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

recursive inline of css assets via url('./path/to/file') #19

Closed
milahu opened this issue Mar 11, 2021 · 2 comments
Closed

recursive inline of css assets via url('./path/to/file') #19

milahu opened this issue Mar 11, 2021 · 2 comments

Comments

@milahu
Copy link

milahu commented Mar 11, 2021

css stylesheets can include other files like

@font-face { src: url('./files/noto-sans-all-400-normal.woff') format('woff'); }
div { background-image: url('something.png'); }

currently posthtml-inline-assets does not inline these files

here is a snippet i use to inline woff binary font files

// based on posthtml-inline-assets/lib/default-transforms.js
const inlineAssetsOptions = {
  transforms: {
    style: {
      transform: (node, data) => {
        delete node.attrs.href;
        delete node.attrs.rel;
        node.tag = 'style';
        let content = data.buffer.toString('utf8');
        const fromDir = require('path').dirname(data.from);
        content = content.replace(
          /url\(('.+?'|".+?")\)(?:\s+format\(('.+?'|".+?"))?\)/g,
          (fullMatch, url, format) => {
            const srcPath = fromDir + '/' + url.slice(1, -1);
            const srcFormat = format.slice(1, -1);
            console.log(`inline style url: ${srcPath} format ${srcFormat}`);
            if (!fs.existsSync(srcPath)) return fullMatch; // no change
            // TODO detect mimetype of src

            // type == woff: binary file
            const mimeType = 'application/octet-stream;charset=binary';
            const b64 = require('fs').readFileSync(srcPath).toString('base64');
            return `url('data:${mimeType};base64,${b64}')`;

            // TODO recurse? find `url(...)` in src
            // TODO use a proper css parser to avoid false positives

            // text file:
            //const src = require('fs').readFileSync(srcPath, 'utf8');
            //const b64 = Buffer.from(src).toString('base64');
            //return `url('data:text/css;charset=utf-8;base64,${b64}')`;
        });
        node.content = [content];
      }
    }
  }
};

const posthtml = require('posthtml')
const inlineAssets = require('posthtml-inline-assets')

posthtml()
  .use(inlineAssets(inlineAssetsOptions))
  .process('<html>....</html>')
  .then(res => ....)

it works, but its ugly ... ideally we would use some css loader to do this recursively for all file types

but maybe i should use a proper bundler like rollup or webpack or 11ty

my goal is to bundle many small html pages + styles + scripts into one single html file as shown here

@milahu
Copy link
Author

milahu commented Mar 12, 2021

closing as 'already solved by inliner'

@milahu milahu closed this as completed Mar 12, 2021
@kimmobrunfeldt
Copy link

I had the same issue and tried a few approaches. First idea I had was to use postcss to inline fonts from the CSS within <style elements. However postcss-url didn't support font inlining either. Eventually I settled to write my own simple posthtml plugin:

import fs from 'fs'
import _ from 'lodash'
import mimeTypes from 'mime-types'
import path from 'path'
import { Node } from 'posthtml'

const posthtmlInlineStyleCssImports = () => {
  return (tree: Node): Node => {
    tree.match({ tag: 'style' }, (node) => {
      const css = node.content?.[0]
      if (!_.isString(css)) {
        return node
      }

      // Regex is good enough here
      const processedCss = css.replace(
        /url\('(.*?)'\)/gi,
        (match, contents) => {
          const dataMime = `data:${mimeTypes.lookup(contents)}`
          const fileContent = fs.readFileSync(
            path.join(__dirname, 'templates/', contents)
          )
          return `url(${dataMime};base64,${fileContent.toString('base64')})`
        }
      )

      node.content = [processedCss]
      return node
    })

    return tree
  }
}

export default posthtmlInlineStyleCssImports

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants