luckyframework / lucky

A full-featured Crystal web framework that catches bugs for you, runs incredibly fast, and helps you write code that lasts.
https://luckyframework.org
MIT License
2.59k stars 156 forks source link

uncompressed html? #668

Open robacarp opened 5 years ago

robacarp commented 5 years ago

HTML is a finicky beast, and so are browsers. Most of the time, what's rendered in the browser inspector is fine, but some times it's not and reading the source is necessary. Currently the source shows up all on one or two lines, and it's a bit of a bear.

Is it possible and reasonable to produce non-minified html?

hanneskaeufler commented 5 years ago

I don't know of a way currently (bar injecting a middleware that messes with the response body - gonna be uuuugly :D).

Widening the scope of this issue a bit (sorry for that): I still think what Brandon Williams detailed in a series of posts starting with https://www.fewbutripe.com/swift/html/dsl/2017/06/22/type-safe-html-in-swift.html does would be a nice goal for luckys html dsl to pursuit as well. We have a nice block based html dsl, but as we append strings to internal state @view it's not trivial to apply transforms to that.

If we could build our html roughly like we do right now, and then have a tree that we can apply functions to, that could make pretty-printed html soo easy to achieve.

jwoertink commented 5 years ago

I've been thinking about this a lot lately. I've even caught a bug in Lucky from viewing the source that the dev inspector didn't catch. Right now we're doing a lot of new SPA stuff, and it's nice to view the source to see what's actually being rendered in the raw especially when using curl and printing the source to the terminal.

This would greatly help out in development; however, this would require an HTML beautifier to work.

I did find a shard to do the html beautifying! My thought is after the view is generated, we pass that @view to this MyHTML shard.

myhtml = Myhtml::Parser.new(@view)
myhtml.to_pretty_html

I talked with @paulcsmith a little bit about this. One thing is Lucky generally doesn't add in outside libraries, so adding this in by default might not be a thing. What is the consensus on adding a handler in to Lucky that gives you access to the markup for any processing?

I'm not sure exactly how'd that look just yet, but

class MyPrettyFormatter < Lucky::BeforeRenderHandler
  def call(view)
    myhtml = Myhtml::Parser.new(view)
    myhtml.to_pretty_html
  end
end

So the idea here would be that Lucky doesn't actually do the "uncompressing". Instead, you as the dev would just include this shard in each project you wanted to use it in as a development dependecy, and you just hooked in to this call.

robacarp commented 3 years ago

I ran into this again. People tell me I shouldn't read the source so much.

require "myhtml"

class HtmlBeautificationHandler
  include HTTP::Handler

  def initialize(@enabled = false)
  end

  def call(context : HTTP::Server::Context)
    unless @enabled
      return call_next context
    end

    to_client = context.response.output
    unpretty = IO::Memory.new
    context.response.output = unpretty

    call_next context

    if context.response.headers["Content-Type"]? == "text/html"
      to_client << Myhtml::Parser.new(unpretty.to_s).to_pretty_html
    else
      to_client << unpretty
    end

    context.response.output = to_client
  end
end
class AppServer < Lucky::BaseAppServer
  def middleware : Array(HTTP::Handler)
    [
      Lucky::ForceSSLHandler.new,
      Lucky::HttpMethodOverrideHandler.new,
      Lucky::LogHandler.new,
      Lucky::ErrorHandler.new(action: Errors::Show),
      Lucky::RemoteIpHandler.new,
+      HtmlBeautificationHandler.new(enabled: Lucky::Env.development?),
      Lucky::RouteHandler.new,
      Lucky::StaticCompressionHandler.new("./public", file_ext: "gz", content_encoding: "gzip"),
      Lucky::StaticFileHandler.new("./public", fallthrough: false, directory_listing: false),
      Lucky::RouteNotFoundHandler.new,
    ] of HTTP::Handler
  end

This solved it for me, at least for the general case. If that warrants closing the issue here, I'm game.

jwoertink commented 3 years ago

That's nice. I actually forgot about this, but I think we can keep this open. I still like that idea of the Lucky::BeforeRenderHandler that you could hook in to. It's nice to see an example though. We may be able to use that as some guide in to adding this thing in.

robacarp commented 3 years ago

Perhaps it got renamed, but I couldn't find anything under BeforeRenderHandler. Adding a middleware feels risky, especially if anything is done with chunked responses or socket type open comms. But it's not a bad way to read the source for a wedge or two.

jwoertink commented 3 years ago

We don't have one yet 😅 I was saying I liked the idea of adding one. I don't think it would be a middleware, but more just a hook in to the @view IO that you could alter before sending the response.