Add ContentTypes to config

This is an empty struct for now, but we will most likely expand on that.

```
[contentTypes]
  [contentTypes.'text/markdown']
```

The above means that only Markdown will be considered a content type. E.g. HTML will be treated as plain text.

Fixes #12274
This commit is contained in:
Bjørn Erik Pedersen
2025-02-07 10:29:35 +01:00
parent 4245a4514d
commit c2fb221209
12 changed files with 182 additions and 52 deletions

View File

@@ -71,11 +71,15 @@ func init() {
EmacsOrgMode: Builtin.EmacsOrgModeType,
}
DefaultContentTypes.init()
DefaultContentTypes.init(nil)
}
var DefaultContentTypes ContentTypes
type ContentTypeConfig struct {
// Empty for now.
}
// ContentTypes holds the media types that are considered content in Hugo.
type ContentTypes struct {
HTML Type
@@ -85,13 +89,36 @@ type ContentTypes struct {
ReStructuredText Type
EmacsOrgMode Type
types Types
// Created in init().
types Types
extensionSet map[string]bool
}
func (t *ContentTypes) init() {
t.types = Types{t.HTML, t.Markdown, t.AsciiDoc, t.Pandoc, t.ReStructuredText, t.EmacsOrgMode}
func (t *ContentTypes) init(types Types) {
sort.Slice(t.types, func(i, j int) bool {
return t.types[i].Type < t.types[j].Type
})
if tt, ok := types.GetByType(t.HTML.Type); ok {
t.HTML = tt
}
if tt, ok := types.GetByType(t.Markdown.Type); ok {
t.Markdown = tt
}
if tt, ok := types.GetByType(t.AsciiDoc.Type); ok {
t.AsciiDoc = tt
}
if tt, ok := types.GetByType(t.Pandoc.Type); ok {
t.Pandoc = tt
}
if tt, ok := types.GetByType(t.ReStructuredText.Type); ok {
t.ReStructuredText = tt
}
if tt, ok := types.GetByType(t.EmacsOrgMode.Type); ok {
t.EmacsOrgMode = tt
}
t.extensionSet = make(map[string]bool)
for _, mt := range t.types {
for _, suffix := range mt.Suffixes() {
@@ -135,32 +162,6 @@ func (t ContentTypes) Types() Types {
return t.types
}
// FromTypes creates a new ContentTypes updated with the values from the given Types.
func (t ContentTypes) FromTypes(types Types) ContentTypes {
if tt, ok := types.GetByType(t.HTML.Type); ok {
t.HTML = tt
}
if tt, ok := types.GetByType(t.Markdown.Type); ok {
t.Markdown = tt
}
if tt, ok := types.GetByType(t.AsciiDoc.Type); ok {
t.AsciiDoc = tt
}
if tt, ok := types.GetByType(t.Pandoc.Type); ok {
t.Pandoc = tt
}
if tt, ok := types.GetByType(t.ReStructuredText.Type); ok {
t.ReStructuredText = tt
}
if tt, ok := types.GetByType(t.EmacsOrgMode.Type); ok {
t.EmacsOrgMode = tt
}
t.init()
return t
}
// Hold the configuration for a given media type.
type MediaTypeConfig struct {
// The file suffixes used for this media type.
@@ -169,6 +170,58 @@ type MediaTypeConfig struct {
Delimiter string
}
var defaultContentTypesConfig = map[string]ContentTypeConfig{
Builtin.HTMLType.Type: {},
Builtin.MarkdownType.Type: {},
Builtin.AsciiDocType.Type: {},
Builtin.PandocType.Type: {},
Builtin.ReStructuredTextType.Type: {},
Builtin.EmacsOrgModeType.Type: {},
}
// DecodeContentTypes decodes the given map of content types.
func DecodeContentTypes(in map[string]any, types Types) (*config.ConfigNamespace[map[string]ContentTypeConfig, ContentTypes], error) {
buildConfig := func(v any) (ContentTypes, any, error) {
var s map[string]ContentTypeConfig
c := DefaultContentTypes
m, err := maps.ToStringMapE(v)
if err != nil {
return c, nil, err
}
if len(m) == 0 {
s = defaultContentTypesConfig
} else {
s = make(map[string]ContentTypeConfig)
m = maps.CleanConfigStringMap(m)
for k, v := range m {
var ctc ContentTypeConfig
if err := mapstructure.WeakDecode(v, &ctc); err != nil {
return c, nil, err
}
s[k] = ctc
}
}
for k := range s {
mediaType, found := types.GetByType(k)
if !found {
return c, nil, fmt.Errorf("unknown media type %q", k)
}
c.types = append(c.types, mediaType)
}
c.init(types)
return c, s, nil
}
ns, err := config.DecodeNamespace[map[string]ContentTypeConfig](in, buildConfig)
if err != nil {
return nil, fmt.Errorf("failed to decode media types: %w", err)
}
return ns, nil
}
// DecodeTypes decodes the given map of media types.
func DecodeTypes(in map[string]any) (*config.ConfigNamespace[map[string]MediaTypeConfig, Types], error) {
buildConfig := func(v any) (Types, any, error) {
@@ -220,6 +273,6 @@ func DecodeTypes(in map[string]any) (*config.ConfigNamespace[map[string]MediaTyp
// TODO(bep) get rid of this.
var DefaultPathParser = &paths.PathParser{
IsContentExt: func(ext string) bool {
return DefaultContentTypes.IsContentSuffix(ext)
panic("not supported")
},
}