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?

Frongo is a Golang package to create HTML/CSS components using only the Go language.

Frongo Frongo is a Go tool to make HTML/CSS document out of Golang code. It was designed with readability and usability in mind, so HTML objects are c

Jul 29, 2021
Quick and simple parser for PFSense XML configuration files, good for auditing firewall rules

pfcfg-parser version 0.0.1 : 13 January 2022 A quick and simple parser for PFSense XML configuration files to generate a plain text file of the main c

Jan 13, 2022
Simple HCL (HashiCorp Configuration Language) parser for your vars.

HCL to Markdown About To write a good documentation for terraform module, quite often we just need to print all our input variables as a fancy table.

Dec 14, 2021
A dead simple parser package for Go
A dead simple parser package for Go

A dead simple parser package for Go V2 Introduction Tutorial Tag syntax Overview Grammar syntax Capturing Capturing boolean value Streaming Lexing Sta

Dec 30, 2022
A simple json parser built using golang

jsonparser A simple json parser built using golang Installation: go get -u githu

Dec 29, 2021
omniparser: a native Golang ETL streaming parser and transform library for CSV, JSON, XML, EDI, text, etc.
omniparser: a native Golang ETL streaming parser and transform library for CSV, JSON, XML, EDI, text, etc.

omniparser Omniparser is a native Golang ETL parser that ingests input data of various formats (CSV, txt, fixed length/width, XML, EDI/X12/EDIFACT, JS

Jan 4, 2023
A shell parser, formatter, and interpreter with bash support; includes shfmt

sh A shell parser, formatter, and interpreter. Supports POSIX Shell, Bash, and mksh. Requires Go 1.14 or later. Quick start To parse shell scripts, in

Dec 29, 2022
Unified diff parser and printer for Go

go-diff Diff parser and printer for Go. Installing go get -u github.com/sourcegraph/go-diff/diff Usage It doesn't actually compute a diff. It only rea

Dec 14, 2022
A NMEA parser library in pure Go

go-nmea This is a NMEA library for the Go programming language (Golang). Features Parse individual NMEA 0183 sentences Support for sentences with NMEA

Dec 20, 2022
TOML parser for Golang with reflection.

THIS PROJECT IS UNMAINTAINED The last commit to this repo before writing this message occurred over two years ago. While it was never my intention to

Dec 30, 2022
User agent string parser in golang

User agent parsing useragent is a library written in golang to parse user agent strings. Usage First install the library with: go get xojoc.pw/userage

Aug 2, 2021
A markdown parser written in Go. Easy to extend, standard(CommonMark) compliant, well structured.

goldmark A Markdown parser written in Go. Easy to extend, standards-compliant, well-structured. goldmark is compliant with CommonMark 0.29. Motivation

Dec 29, 2022
A PDF renderer for the goldmark markdown parser.
A PDF renderer for the goldmark markdown parser.

goldmark-pdf goldmark-pdf is a renderer for goldmark that allows rendering to PDF. Reference See https://pkg.go.dev/github.com/stephenafamo/goldmark-p

Jan 7, 2023
Experimental parser Angular template

Experimental parser Angular template This repository only shows what a parser on the Go might look like Benchmark 100k line of template Parser ms @ang

Dec 15, 2021
Freestyle xml parser with golang

fxml - FreeStyle XML Parser This package provides a simple parser which reads a XML document and output a tree structure, which does not need a pre-de

Jul 1, 2022
An extension to the Goldmark Markdown Parser

Goldmark-Highlight An extension to the Goldmark Markdown Parser which adds parsing / rendering capabilities for rendering highlighted text. Highlighte

May 25, 2022
A parser combinator library for Go.

Takenoco A parser combinator library for Go. Examples CSV parser Dust - toy scripting language Usage Define the parser: package csv import ( "err

Oct 30, 2022
Interpreted Programming Language built in Go. Lexer, Parser, AST, VM.

Gago | Programming Language Built in Go if you are looking for the docs, go here Gago is a interpreted programming language. It is fully written in Go

May 6, 2022
Pagser is a simple, extensible, configurable parse and deserialize html page to struct based on goquery and struct tags for golang crawler
Pagser is a simple, extensible, configurable parse and deserialize html page to struct based on goquery and struct tags for golang crawler

Pagser Pagser inspired by page parser。 Pagser is a simple, extensible, configurable parse and deserialize html page to struct based on goquery and str

Dec 13, 2022