diff --git a/commands/hugobuilder.go b/commands/hugobuilder.go index 657048d481a..32b7e1de87c 100644 --- a/commands/hugobuilder.go +++ b/commands/hugobuilder.go @@ -854,7 +854,7 @@ func (c *hugoBuilder) handleEvents(watcher *watcher.Batcher, h.BaseFs.SourceFilesystems, dynamicEvents) - onePageName := pickOneWriteOrCreatePath(partitionedEvents.ContentEvents) + onePageName := pickOneWriteOrCreatePath(h.Conf.ContentTypes(), partitionedEvents.ContentEvents) c.printChangeDetected("") c.changeDetector.PrepareNew() diff --git a/commands/server.go b/commands/server.go index e90c2a4b8f1..5832c83d864 100644 --- a/commands/server.go +++ b/commands/server.go @@ -46,7 +46,6 @@ import ( "github.com/fsnotify/fsnotify" "github.com/gohugoio/hugo/common/herrors" "github.com/gohugoio/hugo/common/hugo" - "github.com/gohugoio/hugo/media" "github.com/gohugoio/hugo/common/types" "github.com/gohugoio/hugo/common/urls" @@ -1189,16 +1188,16 @@ func partitionDynamicEvents(sourceFs *filesystems.SourceFilesystems, events []fs return } -func pickOneWriteOrCreatePath(events []fsnotify.Event) string { +func pickOneWriteOrCreatePath(contentTypes config.ContentTypesProvider, events []fsnotify.Event) string { name := "" for _, ev := range events { if ev.Op&fsnotify.Write == fsnotify.Write || ev.Op&fsnotify.Create == fsnotify.Create { - if media.IsIndexContentFile(ev.Name) { + if contentTypes.IsIndexContentFile(ev.Name) { return ev.Name } - if media.IsContentFile(ev.Name) { + if contentTypes.IsContentFile(ev.Name) { name = ev.Name } diff --git a/common/paths/pathparser.go b/common/paths/pathparser.go index 65399819b69..5fa798fb057 100644 --- a/common/paths/pathparser.go +++ b/common/paths/pathparser.go @@ -25,13 +25,6 @@ import ( "github.com/gohugoio/hugo/identity" ) -var DefaultPathParser = &PathParser{ - IsContentExt: func(ext string) bool { - // TODO1 - return ext == "md" - }, -} - // PathParser parses a path into a Path. type PathParser struct { // Maps the language code to its index in the languages/sites slice. @@ -44,11 +37,6 @@ type PathParser struct { IsContentExt func(string) bool } -// Parse parses component c with path s into Path using the default path parser. -func Parse(c, s string) *Path { - return DefaultPathParser.Parse(c, s) -} - // NormalizePathString returns a normalized path string using the very basic Hugo rules. func NormalizePathStringBasic(s string) string { // All lower case. diff --git a/config/allconfig/allconfig.go b/config/allconfig/allconfig.go index 17e64a0fe0a..76153f5c0dd 100644 --- a/config/allconfig/allconfig.go +++ b/config/allconfig/allconfig.go @@ -761,7 +761,7 @@ func (c *Configs) Init() error { c.Languages = languages c.LanguagesDefaultFirst = languagesDefaultFirst - c.ContentPathParser = &paths.PathParser{LanguageIndex: languagesDefaultFirst.AsIndexSet(), IsLangDisabled: c.Base.IsLangDisabled, IsContentExt: media.IsContentSuffix} + c.ContentPathParser = &paths.PathParser{LanguageIndex: languagesDefaultFirst.AsIndexSet(), IsLangDisabled: c.Base.IsLangDisabled, IsContentExt: c.Base.C.ContentTypes.IsContentSuffix} c.configLangs = make([]config.AllProvider, len(c.Languages)) for i, l := range c.LanguagesDefaultFirst { diff --git a/config/allconfig/configlanguage.go b/config/allconfig/configlanguage.go index c7f1c276ae8..a215fb5e49b 100644 --- a/config/allconfig/configlanguage.go +++ b/config/allconfig/configlanguage.go @@ -144,6 +144,10 @@ func (c ConfigLanguage) NewIdentityManager(name string) identity.Manager { return identity.NewManager(name) } +func (c ConfigLanguage) ContentTypes() config.ContentTypesProvider { + return c.config.C.ContentTypes +} + // GetConfigSection is mostly used in tests. The switch statement isn't complete, but what's in use. func (c ConfigLanguage) GetConfigSection(s string) any { switch s { diff --git a/config/configProvider.go b/config/configProvider.go index 8f74202abf2..ba10d44dd84 100644 --- a/config/configProvider.go +++ b/config/configProvider.go @@ -41,6 +41,7 @@ type AllProvider interface { Dirs() CommonDirs Quiet() bool DirsBase() CommonDirs + ContentTypes() ContentTypesProvider GetConfigSection(string) any GetConfig() any CanonifyURLs() bool @@ -75,6 +76,15 @@ type AllProvider interface { EnableEmoji() bool } +// We cannot import the media package as that would create a circular dependency. +// This interface defineds a sub set of what media.ContentTypes provides. +type ContentTypesProvider interface { + IsContentSuffix(suffix string) bool + IsContentFile(filename string) bool + IsIndexContentFile(filename string) bool + IsHTMLSuffix(suffix string) bool +} + // Provider provides the configuration settings for Hugo. type Provider interface { GetString(key string) string diff --git a/create/content.go b/create/content.go index bbf5c969e08..f7c343d424e 100644 --- a/create/content.go +++ b/create/content.go @@ -24,7 +24,6 @@ import ( "strings" "github.com/gohugoio/hugo/hugofs/glob" - "github.com/gohugoio/hugo/media" "github.com/gohugoio/hugo/common/hexec" "github.com/gohugoio/hugo/common/hstrings" @@ -97,7 +96,7 @@ func NewContent(h *hugolib.HugoSites, kind, targetPath string, force bool) error return "", fmt.Errorf("failed to resolve %q to an archetype template", targetPath) } - if !media.IsContentFile(b.targetPath) { + if !h.Conf.ContentTypes().IsContentFile(b.targetPath) { return "", fmt.Errorf("target path %q is not a known content format", b.targetPath) } diff --git a/hugofs/walk.go b/hugofs/walk.go index 252cf997067..4af46d89ee0 100644 --- a/hugofs/walk.go +++ b/hugofs/walk.go @@ -23,6 +23,7 @@ import ( "github.com/gohugoio/hugo/common/herrors" "github.com/gohugoio/hugo/common/loggers" "github.com/gohugoio/hugo/common/paths" + "github.com/gohugoio/hugo/media" "github.com/spf13/afero" ) @@ -74,7 +75,7 @@ func NewWalkway(cfg WalkwayConfig) *Walkway { } if cfg.PathParser == nil { - cfg.PathParser = paths.DefaultPathParser + cfg.PathParser = media.DefaultPathParser } logger := cfg.Logger diff --git a/markup/markup.go b/markup/markup.go index da45b78651d..6b22322f114 100644 --- a/markup/markup.go +++ b/markup/markup.go @@ -65,19 +65,21 @@ func NewConverterProvider(cfg converter.ProviderConfig) (ConverterProvider, erro return nil } - if err := add(goldmark.Provider, media.Builtin.MarkdownType.SubType, media.Builtin.MarkdownType.Suffixes()...); err != nil { + contentTypes := cfg.Conf.ContentTypes().(media.ContentTypes) + + if err := add(goldmark.Provider, contentTypes.Markdown.SubType, contentTypes.Markdown.Suffixes()...); err != nil { return nil, err } - if err := add(asciidocext.Provider, media.Builtin.AsciiDocType.SubType, media.Builtin.AsciiDocType.Suffixes()...); err != nil { + if err := add(asciidocext.Provider, contentTypes.AsciiDoc.SubType, contentTypes.AsciiDoc.Suffixes()...); err != nil { return nil, err } - if err := add(rst.Provider, media.Builtin.ReStructuredTextType.SubType, media.Builtin.ReStructuredTextType.Suffixes()...); err != nil { + if err := add(rst.Provider, contentTypes.ReStructuredText.SubType, contentTypes.ReStructuredText.Suffixes()...); err != nil { return nil, err } - if err := add(pandoc.Provider, media.Builtin.PandocType.SubType, media.Builtin.PandocType.Suffixes()...); err != nil { + if err := add(pandoc.Provider, contentTypes.Pandoc.SubType, contentTypes.Pandoc.Suffixes()...); err != nil { return nil, err } - if err := add(org.Provider, media.Builtin.EmacsOrgModeType.SubType, media.Builtin.EmacsOrgModeType.Suffixes()...); err != nil { + if err := add(org.Provider, contentTypes.EmacsOrgMode.SubType, contentTypes.EmacsOrgMode.Suffixes()...); err != nil { return nil, err } diff --git a/media/builtin.go b/media/builtin.go index 4845755e4ae..20a5d376901 100644 --- a/media/builtin.go +++ b/media/builtin.go @@ -1,10 +1,5 @@ package media -import ( - "path/filepath" - "strings" -) - type BuiltinTypes struct { CalendarType Type CSSType Type @@ -172,36 +167,3 @@ var defaultMediaTypesConfig = map[string]any{ "application/octet-stream": map[string]any{}, } - -// IsContentSuffix returns whether the given suffix is a content media type. -func IsContentSuffix(suffix string) bool { - return contentMediaTypesExtensionsSet[suffix] -} - -// IsContentFile returns whether the given filename is a content file. -func IsContentFile(filename string) bool { - return IsContentSuffix(strings.TrimPrefix(filepath.Ext(filename), ".")) -} - -// IsIndexContentFile returns whether the given filename is an index content file. -func IsIndexContentFile(filename string) bool { - if !IsContentFile(filename) { - return false - } - - base := filepath.Base(filename) - - return strings.HasPrefix(base, "index.") || strings.HasPrefix(base, "_index.") -} - -// IsHTMLSuffix returns whether the given suffix is a HTML media type. -func IsHTMLSuffix(suffix string) bool { - for _, suffix := range Builtin.HTMLType.Suffixes() { - if suffix == suffix { - return true - } - } - return false -} - -var contentMediaTypesExtensionsSet map[string]bool diff --git a/media/config.go b/media/config.go index fe12a943710..2bfed169af7 100644 --- a/media/config.go +++ b/media/config.go @@ -22,6 +22,7 @@ import ( "strings" "github.com/gohugoio/hugo/common/maps" + "github.com/gohugoio/hugo/common/paths" "github.com/gohugoio/hugo/config" "github.com/mitchellh/mapstructure" @@ -68,13 +69,6 @@ func init() { } DefaultContentTypes.init() - - contentMediaTypesExtensionsSet = make(map[string]bool) - for _, mt := range DefaultContentTypes.Types() { - for _, suffix := range mt.Suffixes() { - contentMediaTypesExtensionsSet[suffix] = true - } - } } var DefaultContentTypes ContentTypes @@ -123,6 +117,16 @@ func (t ContentTypes) IsIndexContentFile(filename string) bool { return strings.HasPrefix(base, "index.") || strings.HasPrefix(base, "_index.") } +// IsHTMLSuffix returns whether the given suffix is a HTML media type. +func (t ContentTypes) IsHTMLSuffix(suffix string) bool { + for _, suffix := range t.HTML.Suffixes() { + if suffix == suffix { + return true + } + } + return false +} + // Types is a slice of media types. func (t ContentTypes) Types() Types { return t.types @@ -209,3 +213,10 @@ func DecodeTypes(in map[string]any) (*config.ConfigNamespace[map[string]MediaTyp } return ns, nil } + +// TODO(bep) get rid of this. +var DefaultPathParser = &paths.PathParser{ + IsContentExt: func(ext string) bool { + return DefaultContentTypes.IsContentSuffix(ext) + }, +} diff --git a/media/config_test.go b/media/config_test.go index df7cb43faee..2f0c07f08ef 100644 --- a/media/config_test.go +++ b/media/config_test.go @@ -125,10 +125,10 @@ func TestDefaultTypes(t *testing.T) { {Builtin.HTMLType, "text", "html", "html,htm", "text/html", "text/html"}, {Builtin.MarkdownType, "text", "markdown", "md,mdown,markdown", "text/markdown", "text/markdown"}, {Builtin.EmacsOrgModeType, "text", "org", "org", "text/org", "text/org"}, - {Builtin.PandocType, "text", "x-pandoc", "pandoc,pdc", "text/pandoc", "text/pandoc"}, - {Builtin.ReStructuredTextType, "text", "x-restructuredtext", "rst", "text/x-rst", "text/x-rst"}, + {Builtin.PandocType, "text", "pandoc", "pandoc,pdc", "text/pandoc", "text/pandoc"}, + {Builtin.ReStructuredTextType, "text", "x-rst", "rst", "text/x-rst", "text/x-rst"}, {Builtin.GoTmplType, "text", "x-gotmpl", "gotmpl", "text/x-gotmpl", "text/x-gotmpl"}, - {Builtin.AsciiDocType, "text", "x-asciidoc", "adoc,asciidoc,ad", "text/asciidoc", "text/asciidoc"}, + {Builtin.AsciiDocType, "text", "asciidoc", "adoc,asciidoc,ad", "text/asciidoc", "text/asciidoc"}, {Builtin.JavascriptType, "text", "javascript", "js,jsm,mjs", "text/javascript", "text/javascript"}, {Builtin.TypeScriptType, "text", "typescript", "ts", "text/typescript", "text/typescript"}, {Builtin.TSXType, "text", "tsx", "tsx", "text/tsx", "text/tsx"}, diff --git a/media/mediaType_test.go b/media/mediaType_test.go index abf0ed91ea9..fb3eb664f9e 100644 --- a/media/mediaType_test.go +++ b/media/mediaType_test.go @@ -218,7 +218,7 @@ func BenchmarkTypeOps(b *testing.B) { func TestIsContentFile(t *testing.T) { c := qt.New(t) - c.Assert(IsContentFile(filepath.FromSlash("my/file.md")), qt.Equals, true) - c.Assert(IsContentFile(filepath.FromSlash("my/file.ad")), qt.Equals, true) - c.Assert(IsContentFile(filepath.FromSlash("textfile.txt")), qt.Equals, false) + c.Assert(DefaultContentTypes.IsContentFile(filepath.FromSlash("my/file.md")), qt.Equals, true) + c.Assert(DefaultContentTypes.IsContentFile(filepath.FromSlash("my/file.ad")), qt.Equals, true) + c.Assert(DefaultContentTypes.IsContentFile(filepath.FromSlash("textfile.txt")), qt.Equals, false) } diff --git a/resources/page/page_nop.go b/resources/page/page_nop.go index d3813337dc0..f745d8622d3 100644 --- a/resources/page/page_nop.go +++ b/resources/page/page_nop.go @@ -57,7 +57,7 @@ var ( // PageNop implements Page, but does nothing. type nopPage int -var noOpPathInfo = paths.Parse(files.ComponentFolderContent, "no-op.md") +var noOpPathInfo = media.DefaultPathParser.Parse(files.ComponentFolderContent, "no-op.md") func (p *nopPage) Err() resource.ResourceError { return nil diff --git a/source/fileInfo.go b/source/fileInfo.go index 3263428cc28..2d84f988abd 100644 --- a/source/fileInfo.go +++ b/source/fileInfo.go @@ -21,6 +21,7 @@ import ( "github.com/bep/gitmap" "github.com/gohugoio/hugo/common/hugo" "github.com/gohugoio/hugo/common/paths" + "github.com/gohugoio/hugo/media" "github.com/gohugoio/hugo/common/hugio" @@ -141,7 +142,7 @@ func (fi *File) p() *paths.Path { func NewFileInfoFrom(path, filename string) *File { meta := &hugofs.FileMeta{ Filename: filename, - PathInfo: paths.Parse("", filepath.ToSlash(path)), + PathInfo: media.DefaultPathParser.Parse("", filepath.ToSlash(path)), } return NewFileInfo(hugofs.NewFileMetaInfo(nil, meta))