Skip to content

Commit

Permalink
Merge branch 'open-dev' into open
Browse files Browse the repository at this point in the history
  • Loading branch information
StJudeWasHere committed Oct 18, 2024
2 parents 382d964 + a28266f commit d7ee311
Show file tree
Hide file tree
Showing 23 changed files with 1,255 additions and 1,089 deletions.
14 changes: 7 additions & 7 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ module github.com/stjudewashere/seonaut
go 1.23

require (
github.com/antchfx/htmlquery v1.3.2
github.com/antchfx/htmlquery v1.3.3
github.com/go-sql-driver/mysql v1.8.1
github.com/golang-migrate/migrate/v4 v4.18.1
github.com/google/uuid v1.6.0
Expand All @@ -15,15 +15,15 @@ require (
github.com/spf13/viper v1.19.0
github.com/temoto/robotstxt v1.1.2
github.com/turk/go-sitemap v0.0.0-20210912154218-82ad01095e30
golang.org/x/crypto v0.27.0
golang.org/x/net v0.29.0
golang.org/x/text v0.18.0
golang.org/x/crypto v0.28.0
golang.org/x/net v0.30.0
golang.org/x/text v0.19.0
gopkg.in/yaml.v3 v3.0.1
)

require (
filippo.io/edwards25519 v1.1.0 // indirect
github.com/antchfx/xpath v1.3.1 // indirect
github.com/antchfx/xpath v1.3.2 // indirect
github.com/aymerick/douceur v0.2.0 // indirect
github.com/fsnotify/fsnotify v1.7.0 // indirect
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
Expand All @@ -43,7 +43,7 @@ require (
github.com/subosito/gotenv v1.6.0 // indirect
go.uber.org/atomic v1.11.0 // indirect
go.uber.org/multierr v1.11.0 // indirect
golang.org/x/exp v0.0.0-20240909161429-701f63a606c0 // indirect
golang.org/x/sys v0.25.0 // indirect
golang.org/x/exp v0.0.0-20241009180824-f66d83c29e7c // indirect
golang.org/x/sys v0.26.0 // indirect
gopkg.in/ini.v1 v1.67.0 // indirect
)
28 changes: 14 additions & 14 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,10 @@ github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161 h1:L/gRVlceqvL25
github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E=
github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY=
github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU=
github.com/antchfx/htmlquery v1.3.2 h1:85YdttVkR1rAY+Oiv/nKI4FCimID+NXhDn82kz3mEvs=
github.com/antchfx/htmlquery v1.3.2/go.mod h1:1mbkcEgEarAokJiWhTfr4hR06w/q2ZZjnYLrDt6CTUk=
github.com/antchfx/xpath v1.3.1 h1:PNbFuUqHwWl0xRjvUPjJ95Agbmdj2uzzIwmQKgu4oCk=
github.com/antchfx/xpath v1.3.1/go.mod h1:i54GszH55fYfBmoZXapTHN8T8tkcHfRgLyVwwqzXNcs=
github.com/antchfx/htmlquery v1.3.3 h1:x6tVzrRhVNfECDaVxnZi1mEGrQg3mjE/rxbH2Pe6dNE=
github.com/antchfx/htmlquery v1.3.3/go.mod h1:WeU3N7/rL6mb6dCwtE30dURBnBieKDC/fR8t6X+cKjU=
github.com/antchfx/xpath v1.3.2 h1:LNjzlsSjinu3bQpw9hWMY9ocB80oLOWuQqFvO6xt51U=
github.com/antchfx/xpath v1.3.2/go.mod h1:i54GszH55fYfBmoZXapTHN8T8tkcHfRgLyVwwqzXNcs=
github.com/aymerick/douceur v0.2.0 h1:Mv+mAeH1Q+n9Fr+oyamOlAkUNPWPlA8PPGR0QAaYuPk=
github.com/aymerick/douceur v0.2.0/go.mod h1:wlT5vV2O3h55X9m7iVYN0TBM0NH/MmbLnd30/FjWUq4=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
Expand Down Expand Up @@ -135,17 +135,17 @@ go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=
go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.27.0 h1:GXm2NjJrPaiv/h1tb2UH8QfgC/hOf/+z0p6PT8o1w7A=
golang.org/x/crypto v0.27.0/go.mod h1:1Xngt8kV6Dvbssa53Ziq6Eqn0HqbZi5Z6R0ZpwQzt70=
golang.org/x/exp v0.0.0-20240909161429-701f63a606c0 h1:e66Fs6Z+fZTbFBAxKfP3PALWBtpfqks2bwGcexMxgtk=
golang.org/x/exp v0.0.0-20240909161429-701f63a606c0/go.mod h1:2TbTHSBQa924w8M6Xs1QcRcFwyucIwBGpK1p2f1YFFY=
golang.org/x/crypto v0.28.0 h1:GBDwsMXVQi34v5CCYUm2jkJvu4cbtru2U4TN2PSyQnw=
golang.org/x/crypto v0.28.0/go.mod h1:rmgy+3RHxRZMyY0jjAJShp2zgEdOqj2AO7U0pYmeQ7U=
golang.org/x/exp v0.0.0-20241009180824-f66d83c29e7c h1:7dEasQXItcW1xKJ2+gg5VOiBnqWrJc+rq0DPKyvvdbY=
golang.org/x/exp v0.0.0-20241009180824-f66d83c29e7c/go.mod h1:NQtJDoLvd6faHhE7m4T/1IY708gDefGGjR/iUW8yQQ8=
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
golang.org/x/net v0.29.0 h1:5ORfpBpCs4HzDYoodCDBbwHzdR5UrLBZ3sOnUJmFoHo=
golang.org/x/net v0.29.0/go.mod h1:gLkgy8jTGERgjzMic6DS9+SP0ajcu6Xu3Orq/SpETg0=
golang.org/x/net v0.30.0 h1:AcW1SDZMkb8IpzCdQUaIq2sP4sZ4zw+55h6ynffypl4=
golang.org/x/net v0.30.0/go.mod h1:2wGyMJ5iFasEhkwi13ChkO/t1ECNC4X4eBKkVFyYFlU=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
Expand All @@ -154,17 +154,17 @@ golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBc
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.25.0 h1:r+8e+loiHxRqhXVl6ML1nO3l1+oFoWbnlu2Ehimmi34=
golang.org/x/sys v0.25.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.26.0 h1:KHjCJyddX0LoSTb3J+vWpupP9p0oznkqVk/IfjymZbo=
golang.org/x/sys v0.26.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.18.0 h1:XvMDiNzPAl0jr17s6W9lcaIhGUfUORdGCNsuLmPG224=
golang.org/x/text v0.18.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY=
golang.org/x/text v0.19.0 h1:kTxAhCbGbxhK0IwgSKiMO5awPoDQ0RpfiVYBfK860YM=
golang.org/x/text v0.19.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
Expand Down
1 change: 1 addition & 0 deletions internal/issues/errors/errors.go
Original file line number Diff line number Diff line change
Expand Up @@ -81,4 +81,5 @@ const (
ErrorMissingImgElement // Pages with Picture missing the img element
ErrorMetasInBody // Pages with meta tags in the document's body
ErrorNosnippet // Pages with the nosnippet directive
ErrorImgWithoutSize // Pages with img elements that have no size attribtues
)
34 changes: 34 additions & 0 deletions internal/issues/page/images.go
Original file line number Diff line number Diff line change
Expand Up @@ -133,3 +133,37 @@ func NewMissingImgTagInPictureReporter() *models.PageIssueReporter {
Callback: c,
}
}

// Returns a report_manager.PageIssueReporter with a callback function to check
// if a page has img elements without width or height attributes.
func NewImgWithoutSizeReporter() *models.PageIssueReporter {
c := func(pageReport *models.PageReport, htmlNode *html.Node, header *http.Header) bool {
if !pageReport.Crawled {
return false
}

if pageReport.MediaType != "text/html" {
return false
}

e := htmlquery.Find(htmlNode, "//img")
for _, n := range e {
w := htmlquery.SelectAttr(n, "width")
if w == "" {
return true
}

h := htmlquery.SelectAttr(n, "height")
if h == "" {
return true
}
}

return false
}

return &models.PageIssueReporter{
ErrorType: errors.ErrorImgWithoutSize,
Callback: c,
}
}
107 changes: 107 additions & 0 deletions internal/issues/page/images_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -264,3 +264,110 @@ func TestMissingImgTagInPictureReporterIssues(t *testing.T) {
t.Errorf("reportsIssue should be true")
}
}

// Test the NewImgWithoutSizeReporter reporter with a pageReport that the img elements
// with size attributes. The reporter should not report the issue.
func TestImgWithoutSizeReporterNoIssues(t *testing.T) {
pageReport := &models.PageReport{
Crawled: true,
MediaType: "text/html",
}

reporter := page.NewImgWithoutSizeReporter()
if reporter.ErrorType != errors.ErrorImgWithoutSize {
t.Errorf("error type is not correct")
}

source := `
<html>
<body>
<img src="example.jpg" width="80vw" height="100%">
<img src="example-2.jpg" width="400" height="400">
</body>
</html>
`

doc, err := html.Parse(strings.NewReader(source))
if err != nil {
t.Errorf("error parsing html source")
}

reportsIssue := reporter.Callback(pageReport, doc, &http.Header{})

if reportsIssue == true {
t.Errorf("reportsIssue should be false")
}
}

// Test the NewImgWithoutSizeReporter reporter with a pageReport that the img elements
// without size attributes. The reporter should report the issue.
func TestImgWithoutSizeReporterIssues(t *testing.T) {
pageReport := &models.PageReport{
Crawled: true,
MediaType: "text/html",
}

reporter := page.NewImgWithoutSizeReporter()
if reporter.ErrorType != errors.ErrorImgWithoutSize {
t.Errorf("error type is not correct")
}

source := `
<html>
<body>
<img src="example.jpg">
</body>
</html>
`

doc, err := html.Parse(strings.NewReader(source))
if err != nil {
t.Errorf("error parsing html source")
}

reportsIssue := reporter.Callback(pageReport, doc, &http.Header{})

if reportsIssue == false {
t.Errorf("reportsIssue should be true")
}

// Test img only with the height attribute.
source = `
<html>
<body>
<img src="example.jpg" height="200">
</body>
</html>
`

doc, err = html.Parse(strings.NewReader(source))
if err != nil {
t.Errorf("error parsing html source")
}

reportsIssue = reporter.Callback(pageReport, doc, &http.Header{})

if reportsIssue == false {
t.Errorf("reportsIssue should be true")
}

// Test img only with the width attribute.
source = `
<html>
<body>
<img src="example.jpg" width="200">
</body>
</html>
`

doc, err = html.Parse(strings.NewReader(source))
if err != nil {
t.Errorf("error parsing html source")
}

reportsIssue = reporter.Callback(pageReport, doc, &http.Header{})

if reportsIssue == false {
t.Errorf("reportsIssue should be true")
}
}
1 change: 1 addition & 0 deletions internal/issues/page/reporters.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ func GetAllReporters() []*models.PageIssueReporter {
NewLargeImageReporter(),
NewNoImageIndexReporter(),
NewMissingImgTagInPictureReporter(),
NewImgWithoutSizeReporter(),

// Add language issue reporters
NewInvalidLangReporter(),
Expand Down
55 changes: 46 additions & 9 deletions internal/services/renderer.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ import (
"io"
"log"
"os"
"path/filepath"
"strings"
"time"

"gopkg.in/yaml.v3"
Expand All @@ -20,6 +22,7 @@ type (
Renderer struct {
translationMap map[string]interface{}
config *RendererConfig
templates *template.Template
}
)

Expand All @@ -41,20 +44,22 @@ func NewRenderer(config *RendererConfig) (*Renderer, error) {
config: config,
}

r.templates, err = findAndParseTemplates(config.TemplatesFolder, template.FuncMap{
"trans": r.trans,
"total_time": r.totalTime,
"add": r.add,
"to_kb": r.ToKByte,
})
if err != nil {
return nil, fmt.Errorf("renderer initialisation failed: %w", err)
}

return r, nil
}

// Render a template with the specified PageView data.
func (r *Renderer) RenderTemplate(w io.Writer, t string, v interface{}) {
var templates = template.Must(
template.New("").Funcs(template.FuncMap{
"trans": r.trans,
"total_time": r.totalTime,
"add": r.add,
"to_kb": r.ToKByte,
}).ParseGlob(r.config.TemplatesFolder + "/*.html"))

err := templates.ExecuteTemplate(w, t+".html", v)
err := r.templates.ExecuteTemplate(w, t+".html", v)
if err != nil {
log.Printf("RenderTemplate: %v\n", err)
}
Expand Down Expand Up @@ -97,3 +102,35 @@ func (r *Renderer) ToKByte(b int64) string {

return formatted
}

// findAndParseTemplates locates and parses all HTML template files in the specified directory.
// It returns the Template object and an error if any issues occur during parsing.
func findAndParseTemplates(rootDir string, funcMap template.FuncMap) (*template.Template, error) {
cleanRoot := filepath.Clean(rootDir)
pfx := len(cleanRoot) + 1
root := template.New("")

err := filepath.Walk(cleanRoot, func(path string, info os.FileInfo, e1 error) error {
if !info.IsDir() && strings.HasSuffix(path, ".html") {
if e1 != nil {
return fmt.Errorf("file walk error: %w", e1)
}

b, e2 := os.ReadFile(path)
if e2 != nil {
return fmt.Errorf("read file %s error: %w", path, e2)
}

name := path[pfx:]
t := root.New(name).Funcs(funcMap)
_, e2 = t.Parse(string(b))
if e2 != nil {
return fmt.Errorf("parse template %s error: %w", name, e2)
}
}

return nil
})

return root, err
}
1 change: 1 addition & 0 deletions migrations/0065_img_size.down.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
DELETE FROM issue_types WHERE id = 73;
1 change: 1 addition & 0 deletions migrations/0065_img_size.up.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
INSERT INTO issue_types (id, type, priority) VALUES(73, "ERROR_IMG_SIZE_ATTR", 3);
5 changes: 4 additions & 1 deletion translations/translation.en.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -238,4 +238,7 @@ ERROR_METAS_IN_BODY: Pages with meta tags in the document's body
ERROR_METAS_IN_BODY_DESC: Pages that have meta tags in the document's body. The meta tags must be placed in the head section of the document, otherwise they may get ignored by browsers as well as search engines, causing indexability issues.

ERROR_NOSNIPPET: Pages with the nosnippet directive
ERROR_NOSNIPPET_DESC: The nosnippet or max-snippet:0 directives tell search engines not to display a text snippet or video preview in the search results. Review these pages to make sure this is the wanted behavior.
ERROR_NOSNIPPET_DESC: The nosnippet or max-snippet:0 directives tell search engines not to display a text snippet or video preview in the search results. Review these pages to make sure this is the wanted behavior.

ERROR_IMG_SIZE_ATTR: Pages containing images missing size attributes
ERROR_IMG_SIZE_ATTR_DESC: Not setting the size attributes for images can cause layout shifts as the page loads, which can negatively impact user experience as well as SEO. Make sure your images have the corresponding size attributes in place.
46 changes: 1 addition & 45 deletions web/static/echarts.min.js

Large diffs are not rendered by default.

Loading

0 comments on commit d7ee311

Please sign in to comment.