Need guidance to implement minify functionality #1021

Closed
jeevatkm opened this Issue Jan 14, 2016 · 10 comments

Projects

None yet

4 participants

@jeevatkm
Member

@core-team - Can you please provide help/guidance on building Minify functionality?

@xpbliss
xpbliss commented Jan 16, 2016

In the app.conf ,
Set results.compressed=true?

@jeevatkm
Member

Not a gzip functionality. Need HTML minify for my revel app. Planning to use https://github.com/tdewolff/minify.

Any guidance?

@pedromorgan
Member

Not sure if this falls within the scope of revel..

Are u suggesting processing stuff on the fly? shoulnt this kinda stuff be served statically ?

@jeevatkm
Member

I'm not sure about revel scope. however if revel provides HTML minify (not JS or CSS) like compress filter. It would be great :)

You're correct for JS & CSS, it should be statically served. But I'm looking for HTML only.

@jeevatkm
Member

I'm trying to create one, but facing Content-Length issue. Will post my code later.

@jeevatkm
Member

Sorted out Content-Length issue. Created Revel MinifyFilter by referring revel.CompressFilter.

Now MinifyFilter is working fine. Can core team have a look and comment on it? if I'm missing something!

import (
    "io"
    "net/http"
    "regexp"

    "github.com/revel/revel"

    "github.com/tdewolff/minify"
    "github.com/tdewolff/minify/css"
    "github.com/tdewolff/minify/html"
    "github.com/tdewolff/minify/js"
)

var (
    m            *minify.M
    htmlMimeType *regexp.Regexp
)

type MinifyWriter struct {
    http.ResponseWriter
    minifyWriter   io.WriteCloser
    shouldMinify   bool
    headersWritten bool
    closeNotify    chan bool
    parentNotify   <-chan bool
    closed         bool
}

func Minify(c *revel.Controller, fc []revel.Filter) {
    fc[0](c, fc[1:])

    if revel.Config.BoolDefault("results.minify", false) {
        if c.Response.Status != http.StatusNoContent && c.Response.Status != http.StatusNotModified {
            mw := m.Writer("text/html", c.Response.Out)
            writer := MinifyWriter{c.Response.Out, mw, false, false, make(chan bool, 1), nil, false}

            w, ok := c.Response.Out.(http.CloseNotifier)
            if ok {
                writer.parentNotify = w.CloseNotify()
            }

            c.Response.Out = &writer
        } else {
            revel.TRACE.Printf("Minify disabled for response status (%d)", c.Response.Status)
        }
    }
}

func (c MinifyWriter) CloseNotify() <-chan bool {
    if c.parentNotify != nil {
        return c.parentNotify
    }
    return c.closeNotify
}

func (c *MinifyWriter) WriteHeader(status int) {
    c.headersWritten = true
    c.prepareHeader()
    c.ResponseWriter.WriteHeader(status)
}

func (c *MinifyWriter) prepareHeader() {
    // minify is only applied to HTML content
    if htmlMimeType.MatchString(c.Header().Get("Content-Type")) {
        c.shouldMinify = true
        c.Header().Del("Content-Length")
    }
}

func (c *MinifyWriter) Close() error {
    if c.shouldMinify {
        c.minifyWriter.Close()
    }

    if w, ok := c.ResponseWriter.(io.Closer); ok {
        w.Close()
    }

    // Non-blocking write to the closenotifier, if we for some reason should
    // get called multiple times
    select {
    case c.closeNotify <- true:
    default:
    }
    c.closed = true
    return nil
}

func (c *MinifyWriter) Write(b []byte) (int, error) {
    // Abort if parent has been closed
    if c.parentNotify != nil {
        select {
        case <-c.parentNotify:
            return 0, io.ErrClosedPipe
        default:
        }
    }

    // Abort if we ourselves have been closed
    if c.closed {
        return 0, io.ErrClosedPipe
    }

    if !c.headersWritten {
        c.prepareHeader()
        c.headersWritten = true
    }

    if c.shouldMinify {
        return c.minifyWriter.Write(b)
    } else {
        return c.ResponseWriter.Write(b)
    }
}

func init() {
    m = minify.New()

    m.AddFunc("text/css", css.Minify)
    m.AddFunc("text/html", html.Minify)
    m.AddFunc("text/javascript", js.Minify)

    htmlMimeType = regexp.MustCompile("[text|appliation]/html")
}
@jeevatkm
Member

It minify's HTML, inline CSS and inline JS in the HTML content.

@pedromorgan
Member

Ok so I get the drift.. its packing siles and js minify and gzip and compress...

The only "realm" that revel could minify in, in in the compilation of the template,, and not its variables..

However it is interesting to consider dynamic css and build stuff as part of packaging is exiting idea..

This also goes along with the "embedded data", and having a dev enviroment reading siles, and an executable with all within.. ;-)))

In go 1.6 there the {{block}} and the {{foo -}} strip me naked {{-foo}} from other temaplaing languages..

The templating and stuff is also kinda in limbo as

  • smarty (confess as developer)
  • django/jinga2
  • golang is a pain in ass.. Am in with pongo2 with there was jinga2
@pedromorgan
Member

If we gonna implemeny minify etc.. then please come up with some more detail, code an even an RFC.. for now I am closing this issue. .and apologies...

@pedromorgan pedromorgan closed this Feb 8, 2016
@brendensoares
Member

You have a type: "appliation".

How does this affect performance? Can you cache results?

Perhaps we can discuss this later and even add it to Revel. You might consider a PR. Also, we recommend using the RFC format in revel/rfcs to outline the reasons for adding this to Revel.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment