diff options
Diffstat (limited to 'tpl/tplimpl')
-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 |
3 files changed, 144 insertions, 4 deletions
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 == "" } |