diff --git a/resource/image.go b/resource/image.go index 84dc56fa7..19b68a296 100644 --- a/resource/image.go +++ b/resource/image.go @@ -568,6 +568,7 @@ func (i *Image) encodeToDestinations(img image.Image, conf imageConfig, resource func (i *Image) clone() *Image { g := *i.genericResource + g.resourceContent = &resourceContent{} return &Image{ imaging: i.imaging, diff --git a/resource/image_test.go b/resource/image_test.go index 39e538d33..11807d695 100644 --- a/resource/image_test.go +++ b/resource/image_test.go @@ -322,6 +322,18 @@ func TestSVGImage(t *testing.T) { assert.NotNil(svg) } +func TestSVGImageContent(t *testing.T) { + assert := require.New(t) + spec := newTestResourceSpec(assert) + svg := fetchResourceForSpec(spec, assert, "circle.svg") + assert.NotNil(svg) + + content, err := svg.Content() + assert.NoError(err) + assert.IsType("", content) + assert.Contains(content.(string), ``) +} + func BenchmarkResizeParallel(b *testing.B) { assert := require.New(b) img := fetchSunset(assert) diff --git a/resource/resource.go b/resource/resource.go index 7fe3b4ff9..12e1160cf 100644 --- a/resource/resource.go +++ b/resource/resource.go @@ -87,6 +87,14 @@ type Resource interface { // Params set in front matter for this resource. Params() map[string]interface{} + + // Content returns this resource's content. It will be equivalent to reading the content + // that RelPermalink points to in the published folder. + // The return type will be contextual, and should be what you would expect: + // * Page: template.HTML + // * JSON: String + // * Etc. + Content() (interface{}, error) } // Resources represents a slice of resources, which can be a mix of different types. @@ -360,6 +368,11 @@ func (d dirFile) path() string { return path.Join(d.dir, d.file) } +type resourceContent struct { + content string + contentInit sync.Once +} + // genericResource represents a generic linkable resource. type genericResource struct { // The relative path to this resource. @@ -390,6 +403,26 @@ type genericResource struct { osFileInfo os.FileInfo targetPathBuilder func(rel string) string + + // We create copies of this struct, so this needs to be a pointer. + *resourceContent +} + +func (l *genericResource) Content() (interface{}, error) { + var err error + l.contentInit.Do(func() { + var b []byte + + b, err := afero.ReadFile(l.sourceFs(), l.AbsSourceFilename()) + if err != nil { + return + } + + l.content = string(b) + + }) + + return l.content, err } func (l *genericResource) sourceFs() afero.Fs { @@ -444,6 +477,7 @@ func (l *genericResource) updateParams(params map[string]interface{}) { // Implement the Cloner interface. func (l genericResource) WithNewBase(base string) Resource { l.base = base + l.resourceContent = &resourceContent{} return &l } @@ -611,5 +645,6 @@ func (r *Spec) newGenericResource( params: make(map[string]interface{}), name: baseFilename, title: baseFilename, + resourceContent: &resourceContent{}, } }