Add --printUnusedTemplates

Fixes #9502
This commit is contained in:
Bjørn Erik Pedersen
2022-02-15 15:26:18 +01:00
parent 923419d7fd
commit f2e7b49acf
12 changed files with 167 additions and 9 deletions

View File

@@ -0,0 +1,61 @@
package tplimpl_test
import (
"path/filepath"
"testing"
qt "github.com/frankban/quicktest"
"github.com/gohugoio/hugo/hugolib"
"github.com/gohugoio/hugo/tpl"
)
func TestPrintUnusedTemplates(t *testing.T) {
t.Parallel()
files := `
-- config.toml --
baseURL = 'http://example.com/'
printUnusedTemplates=true
-- content/p1.md --
---
title: "P1"
---
{{< usedshortcode >}}
-- layouts/baseof.html --
{{ block "main" . }}{{ end }}
-- layouts/baseof.json --
{{ block "main" . }}{{ end }}
-- layouts/index.html --
{{ define "main" }}FOO{{ end }}
-- layouts/_default/single.json --
-- layouts/_default/single.html --
{{ define "main" }}MAIN{{ end }}
-- layouts/post/single.html --
{{ define "main" }}MAIN{{ end }}
-- layouts/partials/usedpartial.html --
-- layouts/partials/unusedpartial.html --
-- layouts/shortcodes/usedshortcode.html --
{{ partial "usedpartial.html" }}
-- layouts/shortcodes/unusedshortcode.html --
`
b := hugolib.NewIntegrationTestBuilder(
hugolib.IntegrationTestConfig{
T: t,
TxtarString: files,
NeedsOsFS: true,
},
)
b.Build()
unused := b.H.Tmpl().(tpl.UnusedTemplatesProvider).UnusedTemplates()
var names []string
for _, tmpl := range unused {
names = append(names, tmpl.Name())
}
b.Assert(names, qt.DeepEquals, []string{"_default/single.json", "baseof.json", "partials/unusedpartial.html", "post/single.html", "shortcodes/unusedshortcode.html"})
b.Assert(unused[0].Filename(), qt.Equals, filepath.Join(b.Cfg.WorkingDir, "layouts/_default/single.json"))
}

View File

@@ -67,10 +67,11 @@ var embeddedTemplatesAliases = map[string][]string{
}
var (
_ tpl.TemplateManager = (*templateExec)(nil)
_ tpl.TemplateHandler = (*templateExec)(nil)
_ tpl.TemplateFuncGetter = (*templateExec)(nil)
_ tpl.TemplateFinder = (*templateExec)(nil)
_ tpl.TemplateManager = (*templateExec)(nil)
_ tpl.TemplateHandler = (*templateExec)(nil)
_ tpl.TemplateFuncGetter = (*templateExec)(nil)
_ tpl.TemplateFinder = (*templateExec)(nil)
_ tpl.UnusedTemplatesProvider = (*templateExec)(nil)
_ tpl.Template = (*templateState)(nil)
_ tpl.Info = (*templateState)(nil)
@@ -130,6 +131,11 @@ func newTemplateExec(d *deps.Deps) (*templateExec, error) {
funcMap[k] = v.Interface()
}
var templateUsageTracker map[string]templateInfo
if d.Cfg.GetBool("printUnusedTemplates") {
templateUsageTracker = make(map[string]templateInfo)
}
h := &templateHandler{
nameBaseTemplateName: make(map[string]string),
transformNotFound: make(map[string]*templateState),
@@ -146,6 +152,8 @@ func newTemplateExec(d *deps.Deps) (*templateExec, error) {
layoutHandler: output.NewLayoutHandler(),
layoutsFs: d.BaseFs.Layouts.Fs,
layoutTemplateCache: make(map[layoutCacheKey]tpl.Template),
templateUsageTracker: templateUsageTracker,
}
if err := h.loadEmbedded(); err != nil {
@@ -225,13 +233,72 @@ func (t *templateExec) Execute(templ tpl.Template, wr io.Writer, data interface{
defer t.Metrics.MeasureSince(templ.Name(), time.Now())
}
if t.templateUsageTracker != nil {
if ts, ok := templ.(*templateState); ok {
t.templateUsageTrackerMu.Lock()
if _, found := t.templateUsageTracker[ts.Name()]; !found {
t.templateUsageTracker[ts.Name()] = ts.info
}
if !ts.baseInfo.IsZero() {
if _, found := t.templateUsageTracker[ts.baseInfo.name]; !found {
t.templateUsageTracker[ts.baseInfo.name] = ts.baseInfo
}
}
t.templateUsageTrackerMu.Unlock()
}
}
execErr := t.executor.Execute(templ, wr, data)
if execErr != nil {
execErr = t.addFileContext(templ, execErr)
}
return execErr
}
// TODO1
func (t *templateExec) UnusedTemplates() []tpl.FileInfo {
if t.templateUsageTracker == nil {
return nil
}
var unused []tpl.FileInfo
for _, ti := range t.needsBaseof {
if _, found := t.templateUsageTracker[ti.name]; !found {
unused = append(unused, ti)
}
}
for _, ti := range t.baseof {
if _, found := t.templateUsageTracker[ti.name]; !found {
unused = append(unused, ti)
}
}
for _, ts := range t.main.templates {
ti := ts.info
if strings.HasPrefix(ti.name, "_internal/") {
continue
}
if strings.HasPrefix(ti.name, "partials/inline/pagination") {
// TODO(bep) we need to fix this. These are internal partials, but
// they may also be defined in the project, which currently could
// lead to some false negatives.
continue
}
if _, found := t.templateUsageTracker[ti.name]; !found {
unused = append(unused, ti)
}
}
sort.Slice(unused, func(i, j int) bool {
return unused[i].Name() < unused[j].Name()
})
return unused
}
func (t *templateExec) GetFunc(name string) (reflect.Value, bool) {
v, found := t.funcs[name]
return v, found
@@ -285,6 +352,10 @@ type templateHandler struct {
// Note that for shortcodes that same information is embedded in the
// shortcodeTemplates type.
templateInfo map[string]tpl.Info
// May be nil.
templateUsageTracker map[string]templateInfo
templateUsageTrackerMu sync.Mutex
}
// AddTemplate parses and adds a template to the collection.

View File

@@ -34,6 +34,14 @@ type templateInfo struct {
realFilename string
}
func (t templateInfo) Name() string {
return t.name
}
func (t templateInfo) Filename() string {
return t.realFilename
}
func (t templateInfo) IsZero() bool {
return t.name == ""
}