diff options
author | Bjørn Erik Pedersen <bjorn.erik.pedersen@gmail.com> | 2022-02-15 17:26:18 +0300 |
---|---|---|
committer | Bjørn Erik Pedersen <bjorn.erik.pedersen@gmail.com> | 2022-02-15 22:01:57 +0300 |
commit | f2e7b49acfaeab4e1a28cb1096f6461b555900fa (patch) | |
tree | 1dbcdfe580801a81f978ee0b5dd7b89523da2b9c /tpl | |
parent | 923419d7fde2056f47668acb0981135bce543b7e (diff) |
Add --printUnusedTemplates
Fixes #9502
Diffstat (limited to 'tpl')
-rw-r--r-- | tpl/template.go | 5 | ||||
-rw-r--r-- | tpl/template_info.go | 9 | ||||
-rw-r--r-- | tpl/tplimpl/integration_test.go | 61 | ||||
-rw-r--r-- | tpl/tplimpl/template.go | 79 | ||||
-rw-r--r-- | tpl/tplimpl/template_errors.go | 8 |
5 files changed, 154 insertions, 8 deletions
diff --git a/tpl/template.go b/tpl/template.go index 0375b4a17..c5a6a44c0 100644 --- a/tpl/template.go +++ b/tpl/template.go @@ -44,6 +44,11 @@ type TemplateFinder interface { TemplateLookupVariant } +// UnusedTemplatesProvider lists unused templates if the build is configured to track those. +type UnusedTemplatesProvider interface { + UnusedTemplates() []FileInfo +} + // TemplateHandler finds and executes templates. type TemplateHandler interface { TemplateFinder diff --git a/tpl/template_info.go b/tpl/template_info.go index d9b438138..c21c0ae7d 100644 --- a/tpl/template_info.go +++ b/tpl/template_info.go @@ -27,6 +27,11 @@ type Info interface { identity.Provider } +type FileInfo interface { + Name() string + Filename() string +} + type InfoManager interface { ParseInfo() ParseInfo @@ -65,10 +70,6 @@ func (info ParseInfo) IsZero() bool { return info.Config.Version == 0 } -// Info holds some info extracted from a parsed template. -type Info1 struct { -} - type ParseConfig struct { Version int } diff --git a/tpl/tplimpl/integration_test.go b/tpl/tplimpl/integration_test.go new file mode 100644 index 000000000..f71e28bc1 --- /dev/null +++ b/tpl/tplimpl/integration_test.go @@ -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")) +} diff --git a/tpl/tplimpl/template.go b/tpl/tplimpl/template.go index 66ef7fc21..9d2911ee9 100644 --- a/tpl/tplimpl/template.go +++ b/tpl/tplimpl/template.go @@ -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. diff --git a/tpl/tplimpl/template_errors.go b/tpl/tplimpl/template_errors.go index df80726f5..06d895536 100644 --- a/tpl/tplimpl/template_errors.go +++ b/tpl/tplimpl/template_errors.go @@ -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 == "" } |