A simple CSS parser and inliner in Go

douceur Build Status

A simple CSS parser and inliner in Golang.

Douceur Logo

Parser is vaguely inspired by CSS Syntax Module Level 3 and corresponding JS parser.

Inliner only parses CSS defined in HTML document, it DOES NOT fetch external stylesheets (for now).

Inliner inserts additional attributes when possible, for example:

<html>
  <head>
  <style type="text/css">
    body {
      background-color: #f2f2f2;
    }
  </style>
  </head>
  <body>
    <p>Inline me !</p>
  </body>
</html>`

Becomes:

<html>
  <head>
  </head>
  <body style="background-color: #f2f2f2;" bgcolor="#f2f2f2">
    <p>Inline me !</p>
  </body>
</html>`

The bgcolor attribute is inserted, in addition to the inlined background-color style.

Tool usage

Install tool:

$ go install github.com/aymerick/douceur

Parse a CSS file and display result:

$ douceur parse inputfile.css

Inline CSS in an HTML document and display result:

$ douceur inline inputfile.html

Library usage

Fetch package:

$ go get github.com/aymerick/douceur

Parse CSS

package main

import (
    "fmt"

    "github.com/aymerick/douceur/parser"
)

func main() {
    input := `body {
    /* D4rK s1T3 */
    background-color: black;
        }

  p     {
    /* Try to read that ! HAHA! */
    color: red; /* L O L */
 }
`

    stylesheet, err := parser.Parse(input)
    if err != nil {
        panic("Please fill a bug :)")
    }

    fmt.Print(stylesheet.String())
}

Displays:

body {
  background-color: black;
}
p {
  color: red;
}

Inline HTML

package main

import (
    "fmt"

    "github.com/aymerick/douceur/inliner"
)

func main() {
    input := `<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
  <head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
<style type="text/css">
  p {
    font-family: 'Helvetica Neue', Verdana, sans-serif;
    color: #eee;
  }
</style>
  </head>
  <body>
    <p>
      Inline me please!
    </p>
</body>
</html>`

    html, err := inliner.Inline(input)
    if err != nil {
        panic("Please fill a bug :)")
    }

    fmt.Print(html)
}

Displays:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"><html xmlns="http://www.w3.org/1999/xhtml"><head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>

  </head>
  <body>
    <p style="color: #eee; font-family: &#39;Helvetica Neue&#39;, Verdana, sans-serif;">
      Inline me please!
    </p>

</body></html>

Test

go test ./... -v

Dependencies

Similar projects

Owner
Aymerick
Senior Architect @fairjungle
Aymerick
Comments
  • Handle unterminated declaration lists

    Handle unterminated declaration lists

    what

    Make a small tweak to the CSS parser to handle declaration lists that are not terminated with a ';' nor a '}'

    why

    We're using douceur to tokenize the css in the style attribute of an html dom node, and in this case it's valid and common to exclude the trailing semicolon

  • fix: Add go.mod and go.sum files

    fix: Add go.mod and go.sum files

    To fall in line with current best practices i have added go.mod and go.sum files.

    Maybe it is wise to add Renovate to the repo with grouped minor and patch updates for the project

  • Preserve Golang template structure

    Preserve Golang template structure

    I'm seeing some oddities for this trivial example

    package main
    
    import (
    	"fmt"
    	"github.com/aymerick/douceur/inliner"
    )
    
    func main() {
    	tmpl := `
    <html>
    <head>
    </head>
    <body>
    <div>
    <table>
    {{- range .Trash -}}<tr><td>{{.SomeId}}</td></tr>{{- end -}}
    </table>
    </div>
    </body>
    </html>`
    
    	compiled, _ := inliner.Inline(tmpl)
    
    	fmt.Println(compiled)
    }
    
    $ go run example.go
    <html><head>
    </head>
    <body>
    <div>
    
    {{- range .Trash -}}{{- end -}}
    <table><tbody><tr><td>{{.SomeId}}</td></tr></tbody></table>
    </div>
    
    </body></html>
    

    It seems like there are a couple strange things happening here.

    1. A tbody tag is automatically introduced for some reason
    2. The Go template tags are re-arranged so that the compiled output cannot be re-used for templating
  • Allow Inline be configured to leave !important css attributes

    Allow Inline be configured to leave !important css attributes

    Consider the case below:

    <html>
      <head>
      <style type="text/css">
        .mycomponent {
          background-color: red;
          text-align: left !important;
        }
    
        @media screen (min-width: 600px) {
          .menu {
            text-align: center;
          }
        }
      }
      </style>
      </head>
      <body>
        <div class="mycomponent menu">Hello</div>
      </body>
    </html>`
    
    

    If we do not keep "!important" for "text-align" for rule .mycomponent, it gets overriden by media query rule .menu.

  • Repository activity

    Repository activity

    I use this code for my mail service, but would like to know if there is any activity. I did a lot of refactoring and added Renovate to my fork.

    If interested we could integrate the changes into this repo.

    Is there any activity on this repo anyway? I noticed old PR's and issues that are not being replied to/merged or resolved in any way

  • Added settings to Inliner.

    Added settings to Inliner.

    The setting KeepStyle allows the existing style in the HTML document to be preserved after running the inliner.

    The setting AppendToBody appends back all the style rules to the end of the body instead of the head node.

    This settings are needed for compatibility with some email clients.

  • Preserve css style header after inlining.

    Preserve css style header after inlining.

    This is more question/feature-request.

    I want to preserve the existing CSS selectors after running through the inliner, eg;

    Before

    <html>
    <head>
    <style>
    .my-class {
      color: red;
    }
    </style>
    </head>
    <body>
    <p class="my-class">Test</p>
    </body>
    </html>
    

    After

    <html>
    <head>
    <style>
    .my-class {
      color: red;
    }
    </style>
    </head>
    <body>
    <p class="my-class" style="color: red;">Test</p>
    </body>
    </html>
    

    So the my-class class is both inlined in the p element as well as preserved in the <style> header.

  • Infinite loop parsing CSS

    Infinite loop parsing CSS

    The following code loops forever in the latest version:

    package css
    
    import (
        "testing"
    
        "github.com/aymerick/douceur/parser"
    )
    
    func TestInfiniteLoop(t *testing.T) {
        parser.Parse(`
    @media ( __desktop ) {
      background-color: red;
    }
    `)
    }
    
  • nbsp is replaced with space

    nbsp is replaced with space

    The inliner library replaces &nbsp; is replaced with a space character. In my opinion, inliner should leave &nbsp; as it is. Here is a short test.

    html := `
    <html>
      <head>
      <style type="text/css">
        body {
          background-color: #f2f2f2;
        }
      </style>
      </head>
      <body>
        <p>&nbsp;</p>
      </body>
    </html>`
    inline, _ := inliner.Inline(html)
    fmt.Println(inline)
    

    This prints:

    <html><head>
    
      </head>
      <body style="background-color: #f2f2f2;" bgcolor="#f2f2f2">
        <p> </p>
    
    </body></html>
    

    Any quick fix for this?

Related tags
A simple parser for the query used in gmail to filter for e-mails

Go Gmail Query Parser by Dustin Breuer This project is work in progress. Prerequisites Before installing this project you need: ?? Go (at least 1.17)

Feb 2, 2022
A simple Go POP3 client library for connecting and reading mails from POP3 servers.

go-pop3 A simple Go POP3 client library for connecting and reading mails from POP3 servers. This is a full rewrite of TheCreeper/go-pop3 with bug fixe

Dec 17, 2022
a simple api that sent spam via sms and email

a simple api that sent spam via sms and email routes: /sms /email example request with python

Oct 19, 2021
📮 Simple (but useful) email sender written in pure Go v1.17. Support HTML templates and attachments.

?? Go Email Sender Simple (but useful) email sender written in pure Go v1.17. Yes, yet another email package here! ?? Support HTML templates and attac

Dec 31, 2021
A simple microservice designed in Go using Echo Microframework for sending emails and/or calendar invitations to users.

Calenvite A simple microservice designed in GO using Echo Microframework for sending emails and/or calendar invitations to users. Features Send emails

Oct 29, 2022
Hxgomail - Gomail - a simple and efficient package to send emails

Gomail Introduction Gomail is a simple and efficient package to send emails. It

Jan 4, 2022
:incoming_envelope: Simple email interface across multiple service providers (ses, postmark, mandrill, smtp)
:incoming_envelope: Simple email interface across multiple service providers (ses, postmark, mandrill, smtp)

go-mail Lightweight email package with multi-provider support (ses, mandrill, postmark) Table of Contents Installation Documentation Examples & Tests

Dec 10, 2022
Simple SMTP Server for Testing

go-smtptester Simple SMTP Server for Testing. How it works All received mails are saved in a sync.Map with a key: From+Recipient1+Recipient2 Mails to

Nov 18, 2021
Simple tool to test SMTP mail send with various settings including TLS1.1 downgrade

smtptest Simple tool to test SMTP mail send with various settings including TLS1.1 downgrade All settings are configurable in the config.yaml file ser

Sep 19, 2022
Robust and flexible email library for Go

email Robust and flexible email library for Go Email for humans The email package is designed to be simple to use, but flexible enough so as not to be

Dec 30, 2022
:inbox_tray: An IMAP library for clients and servers

go-imap An IMAP4rev1 library written in Go. It can be used to build a client and/or a server. Usage Client package main import ( "log" "github.com

Jan 6, 2023
:envelope: A streaming Go library for the Internet Message Format and mail messages

go-message A Go library for the Internet Message Format. It implements: RFC 5322: Internet Message Format RFC 2045, RFC 2046 and RFC 2047: Multipurpos

Dec 26, 2022
Golang package for send email. Support keep alive connection, TLS and SSL. Easy for bulk SMTP.

Go Simple Mail The best way to send emails in Go with SMTP Keep Alive and Timeout for Connect and Send. IMPORTANT Examples in this README are for v2.2

Jan 8, 2023
Using Mailchain, blockchain users can now send and receive rich-media HTML messages with attachments via a blockchain address.

Mailchain Introduction Mailchain enables blockchain-based email-like messaging with plain or rich text and attachment capabilities. Using blockchain p

Dec 28, 2022
Web and API based SMTP testing
Web and API based SMTP testing

MailHog Inspired by MailCatcher, easier to install. Download and run MailHog Configure your outgoing SMTP server View your outgoing email in a web UI

Jan 4, 2023
High performance, self-hosted newsletter and mailing list manager with a modern dashboard. Single binary app.
High performance, self-hosted newsletter and mailing list manager with a modern dashboard. Single binary app.

listmonk is a standalone, self-hosted, newsletter and mailing list manager. It is fast, feature-rich, and packed into a single binary. It uses a Postg

Dec 30, 2022
MIME mail encoding and decoding package for Go

enmime enmime is a MIME encoding and decoding library for Go, focused on generating and parsing MIME encoded emails. It is being developed in tandem w

Nov 30, 2022
Monitoring and automation for Open Source email servers, starting with Postfix.
Monitoring and automation for Open Source email servers, starting with Postfix.

Welcome to Lightmeter Control Center, the Open Source mailops monitoring application.

Dec 19, 2022
Filtering spam in mail server, protecting both client privacy and server algorithm

HE Spamfilter SNUCSE 2021 "Intelligent Computing System Design Project" Hyesun Kwak Myeonghwan Ahn Dongwon Lee abstract Naïve Bayesian spam filtering

Mar 23, 2022