From 5c0732b2ca331a604934c0f44477d784ce7a444d Mon Sep 17 00:00:00 2001 From: Frits van Bommel Date: Mon, 12 Oct 2020 19:20:08 +0200 Subject: [PATCH] Implement --prettify parameter (only supports HTML for now). This is similar to --minify, but has essentially the opposite effect: instead of making the output less readable it makes it *more* readable. This uses github.com/yosssi/gohtml to perform the actual transformation. Fixes #7190 --- README.md | 1 + commands/commands.go | 1 + commands/hugo.go | 1 + go.mod | 3 +- go.sum | 10 ++ prettifiers/config.go | 106 +++++++++++++++++ prettifiers/config_test.go | 64 +++++++++++ prettifiers/prettifiers.go | 107 +++++++++++++++++ prettifiers/prettifiers_html_test.go | 165 +++++++++++++++++++++++++++ publisher/publisher.go | 17 ++- 10 files changed, 473 insertions(+), 2 deletions(-) create mode 100644 prettifiers/config.go create mode 100644 prettifiers/config_test.go create mode 100644 prettifiers/prettifiers.go create mode 100644 prettifiers/prettifiers_html_test.go diff --git a/README.md b/README.md index f3c9f2bf98b..410c51b6391 100644 --- a/README.md +++ b/README.md @@ -178,6 +178,7 @@ Hugo stands on the shoulder of many great open source libraries, in lexical orde | [github.com/spf13/viper](https://github.com/spf13/viper) | MIT License | | [github.com/tdewolff/minify](https://github.com/tdewolff/minify) | MIT License | | [github.com/tdewolff/parse](https://github.com/tdewolff/parse) | MIT License | + | [github.com/yosssi/gohtml](https://github.com/yosssi/gohtml) | MIT License | | [github.com/yuin/goldmark](https://github.com/yuin/goldmark) | MIT License | | [github.com/yuin/goldmark-highlighting](https://github.com/yuin/goldmark-highlighting) | MIT License | | [go.opencensus.io](https://go.opencensus.io) | Apache License 2.0 | diff --git a/commands/commands.go b/commands/commands.go index ddacc7cf3ba..c0976c6f945 100644 --- a/commands/commands.go +++ b/commands/commands.go @@ -313,6 +313,7 @@ func (cc *hugoBuilderCommon) handleFlags(cmd *cobra.Command) { cmd.Flags().StringSlice("disableKinds", []string{}, "disable different kind of pages (home, RSS etc.)") cmd.Flags().Bool("minify", false, "minify any supported output format (HTML, XML etc.)") + cmd.Flags().Bool("prettify", false, "prettify any supported output format (HTML)") // Set bash-completion. // Each flag must first be defined before using the SetAnnotation() call. diff --git a/commands/hugo.go b/commands/hugo.go index 7eaaedbc9a6..fb03ea05298 100644 --- a/commands/hugo.go +++ b/commands/hugo.go @@ -241,6 +241,7 @@ func initializeFlags(cmd *cobra.Command, cfg config.Provider) { } setValueFromFlag(cmd.Flags(), "minify", cfg, "minifyOutput", true) + setValueFromFlag(cmd.Flags(), "prettify", cfg, "prettifyOutput", true) // Set some "config aliases" setValueFromFlag(cmd.Flags(), "destination", cfg, "publishDir", false) diff --git a/go.mod b/go.mod index 09190bf63da..31d4dd4a560 100644 --- a/go.mod +++ b/go.mod @@ -52,11 +52,12 @@ require ( github.com/spf13/pflag v1.0.5 github.com/spf13/viper v1.7.1 github.com/tdewolff/minify/v2 v2.6.2 + github.com/yosssi/gohtml v0.0.0-20201013000340-ee4748c638f4 github.com/yuin/goldmark v1.2.1 github.com/yuin/goldmark-highlighting v0.0.0-20200307114337-60d527fdb691 gocloud.dev v0.15.0 golang.org/x/image v0.0.0-20191214001246-9130b4cfad52 - golang.org/x/net v0.0.0-20200202094626-16171245cfb2 + golang.org/x/net v0.0.0-20201010224723-4f7140c49acb golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e golang.org/x/text v0.3.3 google.golang.org/api v0.13.0 diff --git a/go.sum b/go.sum index 624da9bfb56..1b9730636c8 100644 --- a/go.sum +++ b/go.sum @@ -297,6 +297,7 @@ github.com/jmespath/go-jmespath v0.3.0 h1:OS12ieG61fsCg5+qLJ+SsW9NicxNkg3b25OyT2 github.com/jmespath/go-jmespath v0.3.0/go.mod h1:9QtRXoHjLGCJ5IBSaohpXITPlowMeeYCZ7fLUTSywik= github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg= github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo= +github.com/jmespath/go-jmespath/internal/testify v1.5.1 h1:shLQSRRSCCPj3f2gpwzGwWFoC7ycTf1rcQZHOlsJ6N8= github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U= github.com/joho/godotenv v1.3.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqxOKXbg= github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= @@ -481,6 +482,7 @@ github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UV github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H4= github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= +github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0= github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/subosito/gotenv v1.2.0 h1:Slr1R9HxAlEKefgq5jn9U+DnETlIUa6HfgEzj0g5d7s= github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= @@ -503,6 +505,8 @@ github.com/xdg/scram v0.0.0-20180814205039-7eeb5667e42c/go.mod h1:lB8K/P019DLNhe github.com/xdg/stringprep v1.0.0/go.mod h1:Jhud4/sHMO4oL310DaZAKk9ZaJ08SJfe+sJh0HrGL1Y= github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= +github.com/yosssi/gohtml v0.0.0-20201013000340-ee4748c638f4 h1:0sw0nJM544SpsihWx1bkXdYLQDlzRflMgFJQ4Yih9ts= +github.com/yosssi/gohtml v0.0.0-20201013000340-ee4748c638f4/go.mod h1:+ccdNT0xMY1dtc5XBxumbYfOUhmduiGudqaDgD2rVRE= github.com/yuin/goldmark v1.1.22 h1:0e0f6Zee9SAQ5yOZGNMWaOxqVvcc/9/kUWu/Kl91Jk8= github.com/yuin/goldmark v1.1.22/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1 h1:ruQGxdhGHe7FWOJPT0mKs5+pD2Xs1Bm/kdGlHO04FmM= @@ -533,6 +537,7 @@ golang.org/x/crypto v0.0.0-20190422183909-d864b10871cd/go.mod h1:yigFU9vqHzYiE8U golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= @@ -578,6 +583,8 @@ golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200202094626-16171245cfb2 h1:CCH4IOTTfewWjGOlSp+zGcjutRKlBEZQ6wTn8ozI/nI= golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20201010224723-4f7140c49acb h1:mUVeFHoDKis5nxCAzoAi7E8Ghb86EXh/RK6wtvJIqRY= +golang.org/x/net v0.0.0-20201010224723-4f7140c49acb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421 h1:Wo7BWFiOk0QRFMLYMqJGFMd9CgUAcGx7V+qEg/h5IBI= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -623,6 +630,8 @@ golang.org/x/sys v0.0.0-20200413165638-669c56c373c4 h1:opSr2sbRXk5X5/givKrrKj9HX golang.org/x/sys v0.0.0-20200413165638-669c56c373c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200501145240-bc7a7d42d5c3 h1:5B6i6EAiSYyejWfvc5Rc9BbI3rzIsrrXfAQBWnYfn+w= golang.org/x/sys v0.0.0-20200501145240-bc7a7d42d5c3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f h1:+Nyd8tzPX9R7BWHguqsrbFdRx3WQ/1ib8I44HXV5yTA= +golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2 h1:z99zHgr7hKfrUcX/KsoJk5FJfjTceCKIp96+biqP4To= @@ -724,6 +733,7 @@ gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU= gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= diff --git a/prettifiers/config.go b/prettifiers/config.go new file mode 100644 index 00000000000..879c8793257 --- /dev/null +++ b/prettifiers/config.go @@ -0,0 +1,106 @@ +// Copyright 2019 The Hugo Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package prettifiers + +import ( + "sort" + "strings" + + "github.com/gohugoio/hugo/common/maps" + "github.com/gohugoio/hugo/config" + "github.com/gohugoio/hugo/docshelper" + "github.com/gohugoio/hugo/parser" + + "github.com/mitchellh/mapstructure" + "github.com/yosssi/gohtml" +) + +type prettifyConfig struct { + // Whether to prettify the published output (the HTML written to /public). + PrettifyOutput bool + + DisableHTML bool + + HTML htmlConfig +} + +type htmlConfig struct { + Condense bool + InlineTags []string + InlineTagMaxLength int +} + +var defaultConfig = prettifyConfig{ + HTML: htmlConfig{ + // Copy the defaults of gohtml + Condense: gohtml.Condense, + InlineTags: boolSetToSlice(gohtml.InlineTags), + InlineTagMaxLength: gohtml.InlineTagMaxLength, + }, +} + +func decodeConfig(cfg config.Provider) (conf prettifyConfig, err error) { + conf = defaultConfig + + // May be set by CLI. + conf.PrettifyOutput = cfg.GetBool("prettifyOutput") + + v := cfg.Get("prettify") + if v == nil { + return + } + + m := maps.ToStringMap(v) + + err = mapstructure.WeakDecode(m, &conf) + + if err != nil { + return + } + + // Set some global properties for the HTML formatter + gohtml.Condense = conf.HTML.Condense + gohtml.InlineTags = sliceToBoolSet(conf.HTML.InlineTags) + gohtml.InlineTagMaxLength = conf.HTML.InlineTagMaxLength + + return +} + +// boolSetToSlice converts a map[string]bool to a sorted list of keys. +func boolSetToSlice(set map[string]bool) []string { + slice := make([]string, 0, len(set)) + for tag, isShort := range set { + if isShort { + slice = append(slice, tag) + } + } + sort.Strings(slice) // Ensure consistent ordering + return slice +} + +// sliceToBoolSet converts a list of strings to a map[string]bool mapping the items in the list to true. +func sliceToBoolSet(items []string) map[string]bool { + set := make(map[string]bool) + for _, tag := range items { + set[strings.ToLower(tag)] = true + } + return set +} + +func init() { + docsProvider := func() docshelper.DocProvider { + return docshelper.DocProvider{"config": map[string]interface{}{"prettify": parser.LowerCaseCamelJSONMarshaller{Value: defaultConfig}}} + } + docshelper.AddDocProviderFunc(docsProvider) +} diff --git a/prettifiers/config_test.go b/prettifiers/config_test.go new file mode 100644 index 00000000000..b0f74508109 --- /dev/null +++ b/prettifiers/config_test.go @@ -0,0 +1,64 @@ +// Copyright 2019 The Hugo Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package prettifiers + +import ( + "testing" + + "github.com/spf13/viper" + "github.com/yosssi/gohtml" + + qt "github.com/frankban/quicktest" +) + +func TestConfig(t *testing.T) { + c := qt.New(t) + v := viper.New() + + v.Set("prettifyOutput", true) + v.Set("prettify", map[string]interface{}{ + "disableHTML": true, + }) + + conf, err := decodeConfig(v) + + c.Assert(err, qt.IsNil) + + c.Assert(conf.PrettifyOutput, qt.Equals, true) + + // `enable` flags + c.Assert(conf.DisableHTML, qt.Equals, true) +} + +func TestConfigCondensedHTML(t *testing.T) { testHTMLCondense(t, true) } +func TestConfigUncondensedHTML(t *testing.T) { testHTMLCondense(t, false) } + +func testHTMLCondense(t *testing.T, condense bool) { + c := qt.New(t) + v := viper.New() + + v.Set("prettify", map[string]interface{}{ + "html": map[string]interface{}{ + "condense": condense, + }, + }) + + conf, err := decodeConfig(v) + + c.Assert(err, qt.IsNil) + + c.Assert(conf.HTML.Condense, qt.Equals, condense) + c.Assert(gohtml.Condense, qt.Equals, condense) + +} diff --git a/prettifiers/prettifiers.go b/prettifiers/prettifiers.go new file mode 100644 index 00000000000..1c836d5f39c --- /dev/null +++ b/prettifiers/prettifiers.go @@ -0,0 +1,107 @@ +// Copyright 2018 The Hugo Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Package prettifiers contains prettifiers mapped to MIME types. This package is used +// in the publishing chain. +package prettifiers + +import ( + "io" + + "github.com/gohugoio/hugo/config" + "github.com/gohugoio/hugo/media" + "github.com/gohugoio/hugo/output" + "github.com/gohugoio/hugo/transform" + + "github.com/yosssi/gohtml" +) + +// Client wraps a prettifier. +type Client struct { + // Whether output prettifying is enabled (HTML in /public) + PrettifyOutput bool + + prettifiers map[string]prettifier +} + +type prettifier func(input []byte, dst io.Writer) error + +// Transformer returns a func that can be used in the transformer publishing chain. +func (m Client) Transformer(mediatype media.Type) transform.Transformer { + if !m.PrettifyOutput { + return nil + } + prettifier := m.prettifiers[mediatype.Type()] + if prettifier == nil { + return nil + } + return func(ft transform.FromTo) error { + return prettifier(ft.From().Bytes(), ft.To()) + } +} + +// Prettify tries to prettify the src into dst given a MIME type. +func (m Client) Prettify(mediatype media.Type, dst io.Writer, src io.Reader) error { + prettifier := m.prettifiers[mediatype.Type()] + if prettifier == nil { + // No supported prettifier. Just pass it through. + _, err := io.Copy(dst, src) + return err + } + + var w = gohtml.NewWriter(dst) + _, err := io.Copy(w, src) + return err +} + +// New creates a new Client with the provided MIME types as the mapping foundation. +// The HTML prettifier is also registered for additional HTML types (AMP etc.) in the +// provided list of output formats. +func New(mediaTypes media.Types, outputFormats output.Formats, cfg config.Provider) (Client, error) { + conf, err := decodeConfig(cfg) + + if err != nil { + return Client{}, err + } + + client := Client{ + PrettifyOutput: conf.PrettifyOutput, + prettifiers: make(map[string]prettifier), + } + + // We use the Type definition of the media types defined in the site if found. + + // TODO: implement other media types (see ../minifiers/minifiers.go) + + // HTML + if !conf.DisableHTML { + for _, of := range outputFormats { + if of.IsHTML { + client.prettifiers[of.MediaType.Type()] = formatHTML + } + } + } + + return client, nil +} + +func formatHTML(input []byte, w io.Writer) error { + prettified := gohtml.FormatBytes(input) + + n, err := w.Write(prettified) + if err == nil && n != len(prettified) { + err = io.ErrShortWrite + } + + return err +} diff --git a/prettifiers/prettifiers_html_test.go b/prettifiers/prettifiers_html_test.go new file mode 100644 index 00000000000..c4f3a0364b9 --- /dev/null +++ b/prettifiers/prettifiers_html_test.go @@ -0,0 +1,165 @@ +// Copyright 2018 The Hugo Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package prettifiers + +import ( + "bytes" + "strings" + "testing" + + "github.com/gohugoio/hugo/media" + + qt "github.com/frankban/quicktest" + "github.com/gohugoio/hugo/output" + "github.com/spf13/viper" +) + +func TestPrettifyUncondensedHTML(t *testing.T) { + v := viper.New() + v.Set("prettify", map[string]interface{}{ + "html": map[string]interface{}{ + "condense": false, + "inlinetags": []string{"inline"}, + }, + }) + m, _ := New(media.DefaultTypes, output.DefaultFormats, v) + + for _, test := range htmlTable { + t.Run(test.name, func(t *testing.T) { + var b bytes.Buffer + c := qt.New(t) + want := test.uncondensed[1:] // Strip initial newline, it's only for formatting. + c.Assert(m.Prettify(media.HTMLType, &b, strings.NewReader(test.input)), qt.IsNil) + c.Assert(b.String(), qt.Equals, want) + }) + } +} + +func TestPrettifyCondensedHTML(t *testing.T) { + v := viper.New() + v.Set("prettify", map[string]interface{}{ + "html": map[string]interface{}{ + "condense": true, + "inlinetags": []string{"inline"}, + }, + }) + m, _ := New(media.DefaultTypes, output.DefaultFormats, v) + + for _, test := range htmlTable { + t.Run(test.name, func(t *testing.T) { + var b bytes.Buffer + c := qt.New(t) + want := test.condensed[1:] // Strip initial newline, it's only for formatting. + c.Assert(m.Prettify(media.HTMLType, &b, strings.NewReader(test.input)), qt.IsNil) + c.Assert(b.String(), qt.Equals, want) + }) + } +} + +var htmlTable = []struct { + name string + input string + condensed string + uncondensed string +}{ + { + name: "basic", + input: ` +

+ Hugo! +

`, + condensed: ` + + +

Hugo!

+ + +`, + uncondensed: ` + + +

+ Hugo! +

+ + +`}, + { + name: "inline", + input: ` + + Hugo! + `, + condensed: ` + + + Hugo! + +`, + uncondensed: ` + + + + + Hugo! + + + +`}, + { + name: "block", + input: ` + + Hugo! + `, + condensed: ` + + + + Hugo! + + +`, + uncondensed: ` + + + + + Hugo! + + + +`}, + { + name: "empty-lines", + input: ` + + +
Hugo!
+ + +`, + condensed: ` + +
Hugo!
+ +`, + uncondensed: ` + +
+ Hugo! +
+ +`}, +} diff --git a/publisher/publisher.go b/publisher/publisher.go index 8b8d2fa631f..b5749356b45 100644 --- a/publisher/publisher.go +++ b/publisher/publisher.go @@ -23,6 +23,7 @@ import ( "github.com/gohugoio/hugo/media" "github.com/gohugoio/hugo/minifiers" + "github.com/gohugoio/hugo/prettifiers" bp "github.com/gohugoio/hugo/bufferpool" "github.com/gohugoio/hugo/helpers" @@ -71,6 +72,7 @@ type Descriptor struct { type DestinationPublisher struct { fs afero.Fs min minifiers.Client + pretty prettifiers.Client htmlElementsCollector *htmlElementsCollector } @@ -84,6 +86,13 @@ func NewDestinationPublisher(rs *resources.Spec, outputFormats output.Formats, m } pub = DestinationPublisher{fs: fs, htmlElementsCollector: classCollector} pub.min, err = minifiers.New(mediaTypes, outputFormats, cfg) + + if err != nil { + return pub, err + } + + pub.pretty, err = prettifiers.New(mediaTypes, outputFormats, cfg) + return } @@ -184,6 +193,12 @@ func (p DestinationPublisher) createTransformerChain(f Descriptor) transform.Cha } } - return transformers + if p.pretty.PrettifyOutput { + prettifyTransformer := p.pretty.Transformer(f.OutputFormat.MediaType) + if prettifyTransformer != nil { + transformers = append(transformers, prettifyTransformer) + } + } + return transformers }