From 09303673181ac0fe27fe03a0edf4aae3fdf52731 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Corn=C3=A9=20de=20Jong?= <5366568-cornedejong@users.noreply.gitlab.com> Date: Sun, 2 Jun 2024 09:39:11 +0200 Subject: [PATCH 1/7] added minify to generator --- cmd/templ/generatecmd/cmd.go | 3 +++ cmd/templ/generatecmd/main.go | 1 + cmd/templ/main.go | 4 ++++ generator/generator.go | 18 ++++++++++++++++++ go.mod | 2 ++ go.sum | 5 +++++ 6 files changed, 33 insertions(+) diff --git a/cmd/templ/generatecmd/cmd.go b/cmd/templ/generatecmd/cmd.go index dbbd2ccbe..2c1c8179a 100644 --- a/cmd/templ/generatecmd/cmd.go +++ b/cmd/templ/generatecmd/cmd.go @@ -84,6 +84,9 @@ func (cmd Generate) Run(ctx context.Context) (err error) { if cmd.Args.IncludeTimestamp { opts = append(opts, generator.WithTimestamp(time.Now())) } + if cmd.Args.MinifyJS { + opts = append(opts, generator.WithJsMinification()) + } // Check the version of the templ module. if err := modcheck.Check(cmd.Args.Path); err != nil { diff --git a/cmd/templ/generatecmd/main.go b/cmd/templ/generatecmd/main.go index d72121790..6c0bd3492 100644 --- a/cmd/templ/generatecmd/main.go +++ b/cmd/templ/generatecmd/main.go @@ -23,6 +23,7 @@ type Arguments struct { GenerateSourceMapVisualisations bool IncludeVersion bool IncludeTimestamp bool + MinifyJS bool // PPROFPort is the port to run the pprof server on. PPROFPort int KeepOrphanedFiles bool diff --git a/cmd/templ/main.go b/cmd/templ/main.go index 72a675a14..98aacb192 100644 --- a/cmd/templ/main.go +++ b/cmd/templ/main.go @@ -116,6 +116,8 @@ Args: Port to run the pprof server on. -keep-orphaned-files Keeps orphaned generated templ files. (default false) + -minify-js + Minify the Javascript script blocks -v Set log verbosity level to "debug". (default "info") -log-level @@ -159,6 +161,7 @@ func generateCmd(stdout, stderr io.Writer, args []string) (code int) { verboseFlag := cmd.Bool("v", false, "") logLevelFlag := cmd.String("log-level", "info", "") helpFlag := cmd.Bool("help", false, "") + minifyJSFlag := cmd.Bool("minify-js", false, "") err := cmd.Parse(args) if err != nil { fmt.Fprint(stderr, generateUsageText) @@ -202,6 +205,7 @@ func generateCmd(stdout, stderr io.Writer, args []string) (code int) { IncludeTimestamp: *includeTimestampFlag, PPROFPort: *pprofPortFlag, KeepOrphanedFiles: *keepOrphanedFilesFlag, + MinifyJS: *minifyJSFlag, }) if err != nil { color.New(color.FgRed).Fprint(stderr, "(✗) ") diff --git a/generator/generator.go b/generator/generator.go index 404bdb733..c970b2b6d 100644 --- a/generator/generator.go +++ b/generator/generator.go @@ -16,6 +16,7 @@ import ( _ "embed" "github.com/a-h/templ/parser/v2" + "github.com/tdewolff/minify/v2/minify" ) type GenerateOpt func(g *generator) error @@ -57,6 +58,13 @@ func WithExtractStrings() GenerateOpt { } } +func WithJsMinification() GenerateOpt { + return func(g *generator) error { + g.minifyJs = true + return nil + } +} + // Generate generates Go code from the input template file to w, and returns a map of the location of Go expressions in the template // to the location of the generated Go code in the output. func Generate(template parser.TemplateFile, w io.Writer, opts ...GenerateOpt) (sm *parser.SourceMap, literals string, err error) { @@ -89,6 +97,8 @@ type generator struct { generatedDate string // fileName to include in error messages if string expressions return an error. fileName string + // minifyJs bool to set js minification on or off + minifyJs bool } func (g *generator) generate() (err error) { @@ -1449,6 +1459,14 @@ func (g *generator) writeScript(t parser.ScriptTemplate) error { prefix := "function " + fn + "(" + stripTypes(t.Parameters.Value) + "){" body := strings.TrimLeftFunc(t.Value, unicode.IsSpace) suffix := "}" + + if g.minifyJs { + body, err = minify.JS(body) + if err != nil { + return err + } + } + if _, err = g.w.WriteIndent(indentLevel, "Function: "+createGoString(prefix+body+suffix)+",\n"); err != nil { return err } diff --git a/go.mod b/go.mod index c5343a5e9..f0e8fe986 100644 --- a/go.mod +++ b/go.mod @@ -30,6 +30,8 @@ require ( github.com/segmentio/asm v1.2.0 // indirect github.com/segmentio/encoding v0.4.0 // indirect github.com/stretchr/testify v1.8.4 // indirect + github.com/tdewolff/minify/v2 v2.20.32 // indirect + github.com/tdewolff/parse/v2 v2.7.14 // indirect go.lsp.dev/pkg v0.0.0-20210717090340-384b27a52fb2 // indirect go.uber.org/multierr v1.11.0 // indirect golang.org/x/net v0.24.0 // indirect diff --git a/go.sum b/go.sum index b06ed0ad6..bf58583d3 100644 --- a/go.sum +++ b/go.sum @@ -42,6 +42,11 @@ github.com/segmentio/encoding v0.4.0 h1:MEBYvRqiUB2nfR2criEXWqwdY6HJOUrCn5hboVOV github.com/segmentio/encoding v0.4.0/go.mod h1:/d03Cd8PoaDeceuhUUUQWjU0KhWjrmYrWPgtJHYZSnI= github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +github.com/tdewolff/minify/v2 v2.20.32 h1:rk4THvBPLEU+gGDKaJxyvFhF5+quSwCk3HKv1GpSVyE= +github.com/tdewolff/minify/v2 v2.20.32/go.mod h1:1TJni7+mATKu24cBQQpgwakrYRD27uC1/rdJOgdv8ns= +github.com/tdewolff/parse/v2 v2.7.14 h1:100KJ+QAO3PpMb3uUjzEU/NpmCdbBYz6KPmCIAfWpR8= +github.com/tdewolff/parse/v2 v2.7.14/go.mod h1:3FbJWZp3XT9OWVN3Hmfp0p/a08v4h8J9W1aghka0soA= +github.com/tdewolff/test v1.0.11-0.20231101010635-f1265d231d52/go.mod h1:6DAvZliBAAnD7rhVgwaM7DE5/d9NMOAJ09SqYqeK4QE= github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= go.lsp.dev/jsonrpc2 v0.10.0 h1:Pr/YcXJoEOTMc/b6OTmcR1DPJ3mSWl/SWiU1Cct6VmI= go.lsp.dev/jsonrpc2 v0.10.0/go.mod h1:fmEzIdXPi/rf6d4uFcayi8HpFP1nBF99ERP1htC72Ac= From ca2ed5cf69ca8b96afa51bca9372c3eee36d38f1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Corn=C3=A9=20de=20Jong?= <5366568-cornedejong@users.noreply.gitlab.com> Date: Fri, 16 Aug 2024 21:14:51 +0200 Subject: [PATCH 2/7] Added GetName to Attribute interface --- parser/v2/types.go | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/parser/v2/types.go b/parser/v2/types.go index 12c305c77..4644937cd 100644 --- a/parser/v2/types.go +++ b/parser/v2/types.go @@ -711,6 +711,9 @@ func (e RawElement) Write(w io.Writer, indent int) error { } type Attribute interface { + // GetName returns the attribute name if possible, otherwise empty string + // eg. ConditionalAttribute or SpreadAttributes may contain many names + GetName() string // Write out the string. Write(w io.Writer, indent int) error } @@ -721,6 +724,10 @@ type BoolConstantAttribute struct { NameRange Range } +func (bca BoolConstantAttribute) GetName() string { + return bca.Name +} + func (bca BoolConstantAttribute) String() string { return bca.Name } @@ -737,6 +744,10 @@ type ConstantAttribute struct { NameRange Range } +func (ca ConstantAttribute) GetName() string { + return ca.Name +} + func (ca ConstantAttribute) String() string { quote := `"` if ca.SingleQuote { @@ -756,6 +767,10 @@ type BoolExpressionAttribute struct { NameRange Range } +func (bea BoolExpressionAttribute) GetName() string { + return bea.Name +} + func (bea BoolExpressionAttribute) String() string { return bea.Name + `?={ ` + bea.Expression.Value + ` }` } @@ -771,6 +786,10 @@ type ExpressionAttribute struct { NameRange Range } +func (ea ExpressionAttribute) GetName() string { + return ea.Name +} + func (ea ExpressionAttribute) String() string { sb := new(strings.Builder) _ = ea.Write(sb, 0) @@ -828,6 +847,10 @@ type SpreadAttributes struct { Expression Expression } +func (sa SpreadAttributes) GetName() string { + return "" +} + func (sa SpreadAttributes) String() string { return `{ ` + sa.Expression.Value + `... }` } @@ -846,6 +869,10 @@ type ConditionalAttribute struct { Else []Attribute } +func (ca ConditionalAttribute) GetName() string { + return "" +} + func (ca ConditionalAttribute) String() string { sb := new(strings.Builder) _ = ca.Write(sb, 0) From b2529828c83d1f98eab4cfd846aad06e3a5eed0c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Corn=C3=A9=20de=20Jong?= <5366568-cornedejong@users.noreply.gitlab.com> Date: Fri, 16 Aug 2024 21:16:01 +0200 Subject: [PATCH 3/7] added minification for

Hello

World
\ No newline at end of file diff --git a/generator/test-raw-elements/render_test.go b/generator/test-raw-elements/render_test.go index bbb5ecb26..9b8006d93 100644 --- a/generator/test-raw-elements/render_test.go +++ b/generator/test-raw-elements/render_test.go @@ -10,6 +10,9 @@ import ( //go:embed expected.html var expected string +//go:embed expected_with_minification.html +var expectedWithMinification string + func Test(t *testing.T) { component := Example() diff, err := htmldiff.Diff(component, expected) @@ -20,3 +23,14 @@ func Test(t *testing.T) { t.Error(diff) } } + +func TestWithJSMinification(t *testing.T) { + component := ExampleWithMinification() + diff, err := htmldiff.Diff(component, expectedWithMinification) + if err != nil { + t.Fatal(err) + } + if diff != "" { + t.Error(diff) + } +} diff --git a/generator/test-raw-elements/template_with_js_minification.templ b/generator/test-raw-elements/template_with_js_minification.templ new file mode 100644 index 000000000..51c9febee --- /dev/null +++ b/generator/test-raw-elements/template_with_js_minification.templ @@ -0,0 +1,47 @@ +package testrawelements + +templ ExampleWithMinification() { + + + + + + + + + +

Hello

+ @templ.Raw("
World
") + + +} diff --git a/generator/test-raw-elements/template_with_js_minification_templ.go b/generator/test-raw-elements/template_with_js_minification_templ.go new file mode 100644 index 000000000..08b8b0a02 --- /dev/null +++ b/generator/test-raw-elements/template_with_js_minification_templ.go @@ -0,0 +1,45 @@ +// Code generated by templ - DO NOT EDIT. + +// templ: version: v0.2.759 +package testrawelements + +//lint:file-ignore SA4006 This context is only used if a nested component is present. + +import "github.com/a-h/templ" +import templruntime "github.com/a-h/templ/runtime" + +func ExampleWithMinification() templ.Component { + return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) { + templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context + templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W) + if !templ_7745c5c3_IsBuffer { + defer func() { + templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer) + if templ_7745c5c3_Err == nil { + templ_7745c5c3_Err = templ_7745c5c3_BufErr + } + }() + } + ctx = templ.InitializeContext(ctx) + templ_7745c5c3_Var1 := templ.GetChildren(ctx) + if templ_7745c5c3_Var1 == nil { + templ_7745c5c3_Var1 = templ.NopComponent + } + ctx = templ.ClearChildren(ctx) + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("

Hello

") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + templ_7745c5c3_Err = templ.Raw("
World
").Render(ctx, templ_7745c5c3_Buffer) + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + return templ_7745c5c3_Err + }) +} + +var _ = templruntime.GeneratedTemplate