new layout (#6065)

* new layout

Signed-off-by: Avelino <31996+avelino@users.noreply.github.com>

* project page in site

Signed-off-by: Avelino <31996+avelino@users.noreply.github.com>

---------

Signed-off-by: Avelino <31996+avelino@users.noreply.github.com>
This commit is contained in:
Avelino 2026-02-28 11:27:46 -03:00 committed by GitHub
parent a234403f92
commit a532200f99
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
9 changed files with 1876 additions and 239 deletions

View File

@ -18,8 +18,16 @@ jobs:
- uses: actions/checkout@v6
- name: Get dependencies
run: go get -v -t -d ./...
- name: Restore GitHub metadata cache
uses: actions/cache@v4
with:
path: .cache/repos
key: repo-meta-${{ github.run_id }}
restore-keys: repo-meta-
- name: Make awesome-go.com
run: go run .
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Setup node
uses: actions/setup-node@v6
with:

1
.gitignore vendored
View File

@ -1,5 +1,6 @@
out/
awesome-go
.cache/
# Folders
.idea

423
main.go
View File

@ -4,13 +4,19 @@ package main
import (
"bytes"
"embed"
"encoding/json"
"errors"
"fmt"
template2 "html/template"
"io"
"log"
"net/http"
"net/url"
"os"
"path/filepath"
"strings"
"text/template"
"time"
"github.com/avelino/awesome-go/pkg/markdown"
cp "github.com/otiai10/copy"
@ -24,6 +30,7 @@ type Link struct {
Title string
URL string
Description string
ProjectSlug string // non-empty if project page exists → internal link
}
// Category describe link category
@ -34,6 +41,40 @@ type Category struct {
Links []Link
}
// RepoMeta holds metadata fetched from GitHub/GitLab API
type RepoMeta struct {
Stars int `json:"stars"`
Forks int `json:"forks"`
License string `json:"license"`
Language string `json:"language"`
Topics []string `json:"topics"`
LastPush string `json:"last_push"`
OpenIssues int `json:"open_issues"`
Archived bool `json:"archived"`
FetchedAt string `json:"fetched_at"`
}
// Project represents an individual project page
type Project struct {
Title string
URL string
Description string
Slug string
Host string // "github" or "gitlab"
Owner string
Repo string
Meta *RepoMeta
CategoryTitle string
CategorySlug string
Related []Link
}
// SitemapData holds data for sitemap generation
type SitemapData struct {
Categories map[string]Category
Projects []*Project
}
// Source files
const readmePath = "README.md"
@ -47,7 +88,18 @@ var staticFiles = []string{
//go:embed tmpl/*.tmpl.html tmpl/*.tmpl.xml
var tplFs embed.FS
var tpl = template.Must(template.ParseFS(tplFs, "tmpl/*.tmpl.html", "tmpl/*.tmpl.xml"))
var tpl = template.Must(
template.New("").Funcs(template.FuncMap{
"now": func() string { return time.Now().Format("2006-01-02") },
"jsonEscape": func(s string) string {
b, _ := json.Marshal(s)
if len(b) < 2 {
return ""
}
return string(b[1 : len(b)-1])
},
}).ParseFS(tplFs, "tmpl/*.tmpl.html", "tmpl/*.tmpl.xml"),
)
// Output files
const outDir = "out/" // NOTE: trailing slash is required
@ -85,15 +137,24 @@ func buildStaticSite() error {
return fmt.Errorf("extract categories: %w", err)
}
projects := buildProjects(categories)
if err := fetchProjectMeta(projects); err != nil {
return fmt.Errorf("fetch project metadata: %w", err)
}
if err := renderCategories(categories); err != nil {
return fmt.Errorf("render categories: %w", err)
}
if err := renderProjects(projects); err != nil {
return fmt.Errorf("render projects: %w", err)
}
if err := rewriteLinksInIndex(doc, categories); err != nil {
return fmt.Errorf("rewrite links in index: %w", err)
}
if err := renderSitemap(categories); err != nil {
if err := renderSitemap(categories, projects); err != nil {
return fmt.Errorf("render sitemap: %w", err)
}
@ -179,7 +240,7 @@ func renderCategories(categories map[string]Category) error {
return nil
}
func renderSitemap(categories map[string]Category) error {
func renderSitemap(categories map[string]Category, projects []*Project) error {
f, err := os.Create(outSitemapFile)
if err != nil {
return fmt.Errorf("create sitemap file `%s`: %w", outSitemapFile, err)
@ -187,7 +248,12 @@ func renderSitemap(categories map[string]Category) error {
fmt.Printf("Render Sitemap to: %s\n", outSitemapFile)
if err := tpl.Lookup("sitemap.tmpl.xml").Execute(f, categories); err != nil {
data := SitemapData{
Categories: categories,
Projects: projects,
}
if err := tpl.Lookup("sitemap.tmpl.xml").Execute(f, data); err != nil {
return fmt.Errorf("render sitemap: %w", err)
}
@ -198,9 +264,14 @@ func extractCategories(doc *goquery.Document) (map[string]Category, error) {
categories := make(map[string]Category)
var rootErr error
doc.
Find("body #contents").
NextFiltered("ul").
contentsHeading := doc.Find("body #contents")
// Support both direct <ul> sibling and <ul> wrapped in <details>
contentsList := contentsHeading.NextFiltered("ul")
if contentsList.Length() == 0 {
contentsList = contentsHeading.NextFiltered("details").Find("ul").First()
}
contentsList.
Find("ul").
EachWithBreak(func(_ int, selUl *goquery.Selection) bool {
if rootErr != nil {
@ -217,8 +288,8 @@ func extractCategories(doc *goquery.Document) (map[string]Category, error) {
category, err := extractCategory(doc, selector)
if err != nil {
rootErr = fmt.Errorf("extract category: %w", err)
return false
// Skip entries without links (e.g. #contents, #awesome-go)
return true
}
categories[selector] = *category
@ -363,3 +434,337 @@ func renderIndex(srcFilename, outFilename string) error {
return nil
}
// parseRepoURL extracts host, owner, and repo from a GitHub or GitLab URL
func parseRepoURL(rawURL string) (host, owner, repo string, ok bool) {
u, err := url.Parse(rawURL)
if err != nil {
return "", "", "", false
}
parts := strings.Split(strings.Trim(u.Path, "/"), "/")
switch u.Hostname() {
case "github.com":
if len(parts) < 2 {
return "", "", "", false
}
return "github", parts[0], parts[1], true
case "gitlab.com":
if len(parts) < 2 {
return "", "", "", false
}
return "gitlab", parts[0], parts[len(parts)-1], true
}
return "", "", "", false
}
// buildProjects creates Project structs from category links and sets internal link slugs
func buildProjects(categories map[string]Category) []*Project {
var projects []*Project
for key, cat := range categories {
for i, link := range cat.Links {
host, owner, repo, ok := parseRepoURL(link.URL)
if !ok {
continue
}
projectSlug := slug.Generate(repo + "-" + owner + "-" + host)
cat.Links[i].ProjectSlug = projectSlug
p := &Project{
Title: link.Title,
URL: link.URL,
Description: link.Description,
Slug: projectSlug,
Host: host,
Owner: owner,
Repo: repo,
CategoryTitle: cat.Title,
CategorySlug: cat.Slug,
}
projects = append(projects, p)
}
categories[key] = cat
}
// Populate Related: group by category, pick up to 10 siblings
byCat := make(map[string][]*Project)
for _, p := range projects {
byCat[p.CategorySlug] = append(byCat[p.CategorySlug], p)
}
for _, p := range projects {
var related []Link
for _, s := range byCat[p.CategorySlug] {
if s.Slug == p.Slug {
continue
}
related = append(related, Link{
Title: s.Title,
URL: s.URL,
Description: s.Description,
ProjectSlug: s.Slug,
})
if len(related) >= 10 {
break
}
}
p.Related = related
}
fmt.Printf("Built %d project pages\n", len(projects))
return projects
}
// fetchProjectMeta fetches metadata from GitHub/GitLab APIs with file-per-repo caching
func fetchProjectMeta(projects []*Project) error {
if os.Getenv("AWESOME_SKIP_FETCH") != "" {
log.Println("AWESOME_SKIP_FETCH set, skipping metadata fetch")
return nil
}
token := os.Getenv("GITHUB_TOKEN")
client := &http.Client{Timeout: 10 * time.Second}
fetched := 0
for _, p := range projects {
cached, err := readCachedMeta(p)
if err == nil && cached != nil {
p.Meta = cached
continue
}
var meta *RepoMeta
switch p.Host {
case "github":
meta = fetchGitHubMeta(client, p.Owner, p.Repo, token)
case "gitlab":
meta = fetchGitLabMeta(client, p.URL)
}
if meta != nil {
meta.FetchedAt = time.Now().Format("2006-01-02")
p.Meta = meta
if err := writeCachedMeta(p, meta); err != nil {
log.Printf("warning: cache write failed for %s/%s: %v", p.Owner, p.Repo, err)
}
fetched++
}
time.Sleep(50 * time.Millisecond)
}
fmt.Printf("Fetched metadata for %d projects (%d from cache)\n", fetched, len(projects)-fetched)
return nil
}
func cacheFilePath(p *Project) string {
return filepath.Join(".cache", "repos", p.Owner, p.Repo+".json")
}
func readCachedMeta(p *Project) (*RepoMeta, error) {
data, err := os.ReadFile(cacheFilePath(p))
if err != nil {
return nil, err
}
var meta RepoMeta
if err := json.Unmarshal(data, &meta); err != nil {
return nil, err
}
fetchedAt, err := time.Parse("2006-01-02", meta.FetchedAt)
if err != nil {
return nil, err
}
if time.Since(fetchedAt) > 7*24*time.Hour {
return nil, fmt.Errorf("cache stale")
}
return &meta, nil
}
func writeCachedMeta(p *Project, meta *RepoMeta) error {
path := cacheFilePath(p)
if err := os.MkdirAll(filepath.Dir(path), 0755); err != nil {
return err
}
data, err := json.MarshalIndent(meta, "", " ")
if err != nil {
return err
}
return os.WriteFile(path, data, 0644)
}
func fetchGitHubMeta(client *http.Client, owner, repo, token string) *RepoMeta {
apiURL := fmt.Sprintf("https://api.github.com/repos/%s/%s", owner, repo)
req, err := http.NewRequest("GET", apiURL, nil)
if err != nil {
return nil
}
req.Header.Set("Accept", "application/vnd.github.v3+json")
if token != "" {
req.Header.Set("Authorization", "Bearer "+token)
}
resp, err := client.Do(req)
if err != nil {
return nil
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
return nil
}
body, err := io.ReadAll(resp.Body)
if err != nil {
return nil
}
var ghRepo struct {
StargazersCount int `json:"stargazers_count"`
ForksCount int `json:"forks_count"`
License *struct {
SpdxID string `json:"spdx_id"`
} `json:"license"`
Language string `json:"language"`
Topics []string `json:"topics"`
PushedAt string `json:"pushed_at"`
OpenIssuesCount int `json:"open_issues_count"`
Archived bool `json:"archived"`
}
if err := json.Unmarshal(body, &ghRepo); err != nil {
return nil
}
license := ""
if ghRepo.License != nil {
license = ghRepo.License.SpdxID
}
lastPush := ""
if ghRepo.PushedAt != "" {
if t, err := time.Parse(time.RFC3339, ghRepo.PushedAt); err == nil {
lastPush = t.Format("2006-01-02")
}
}
return &RepoMeta{
Stars: ghRepo.StargazersCount,
Forks: ghRepo.ForksCount,
License: license,
Language: ghRepo.Language,
Topics: ghRepo.Topics,
LastPush: lastPush,
OpenIssues: ghRepo.OpenIssuesCount,
Archived: ghRepo.Archived,
}
}
func fetchGitLabMeta(client *http.Client, projectURL string) *RepoMeta {
u, err := url.Parse(projectURL)
if err != nil {
return nil
}
path := strings.Trim(u.Path, "/")
// Remove tree/branch paths (e.g., /-/tree/main/...)
if idx := strings.Index(path, "/-/"); idx != -1 {
path = path[:idx]
}
apiURL := fmt.Sprintf("https://gitlab.com/api/v4/projects/%s", url.PathEscape(path))
resp, err := client.Get(apiURL)
if err != nil {
return nil
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
return nil
}
body, err := io.ReadAll(resp.Body)
if err != nil {
return nil
}
var glProject struct {
StarCount int `json:"star_count"`
ForksCount int `json:"forks_count"`
License *struct {
Key string `json:"key"`
} `json:"license"`
Topics []string `json:"topics"`
LastActivityAt string `json:"last_activity_at"`
OpenIssuesCount int `json:"open_issues_count"`
Archived bool `json:"archived"`
}
if err := json.Unmarshal(body, &glProject); err != nil {
return nil
}
license := ""
if glProject.License != nil {
license = glProject.License.Key
}
lastPush := ""
if glProject.LastActivityAt != "" {
if t, err := time.Parse(time.RFC3339, glProject.LastActivityAt); err == nil {
lastPush = t.Format("2006-01-02")
}
}
return &RepoMeta{
Stars: glProject.StarCount,
Forks: glProject.ForksCount,
License: license,
Language: "Go",
Topics: glProject.Topics,
LastPush: lastPush,
OpenIssues: glProject.OpenIssuesCount,
Archived: glProject.Archived,
}
}
func renderProjects(projects []*Project) error {
for _, p := range projects {
projectDir := filepath.Join(outDir, p.CategorySlug, p.Slug)
if err := mkdirAll(projectDir); err != nil {
return fmt.Errorf("create project dir %s: %w", projectDir, err)
}
projectFile := filepath.Join(projectDir, "index.html")
buf := bytes.NewBuffer(nil)
if err := tpl.Lookup("project.tmpl.html").Execute(buf, p); err != nil {
return fmt.Errorf("render project %s: %w", p.Slug, err)
}
doc, err := goquery.NewDocumentFromReader(buf)
if err != nil {
return fmt.Errorf("goquery for project %s: %w", p.Slug, err)
}
html, err := doc.Html()
if err != nil {
return fmt.Errorf("html for project %s: %w", p.Slug, err)
}
if err := os.WriteFile(projectFile, []byte(html), 0644); err != nil {
return fmt.Errorf("write project file %s: %w", p.Slug, err)
}
}
fmt.Printf("Rendered %d project pages\n", len(projects))
return nil
}

File diff suppressed because it is too large Load Diff

View File

@ -2,103 +2,141 @@
<html lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width">
<title>{{.Title}} - Awesome Go / Golang</title>
<meta name="description" content=
"{{.Description}} - Awesome Go / Golang">
<meta name="keywords" content=
"{{.Title}}, golang, go, awesome, awesome-go, go framework, golang framework">
<meta name="twitter:card" value="summary">
<meta property="og:title" content=
"{{.Description}} - Awesome Go">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="color-scheme" content="light dark">
<title>{{.Title}} - Awesome Go</title>
<meta name="description" content="{{.Description}} - Curated list of awesome Go {{.Title}} libraries and tools.">
<meta name="keywords" content="{{.Title}}, golang, go, awesome-go, go libraries">
<!-- Open Graph -->
<meta property="og:title" content="{{.Title}} - Awesome Go">
<meta property="og:type" content="article">
<meta property="og:url" content=
"https://awesome-go.com/{{.Slug}}">
<meta property="og:image" content=
"https://awesome-go.com/assets/logo.png">
<meta property="og:description" content=
"{{.Description}} - Awesome Go">
<link rel="canonical" href="https://awesome-go.com/{{.Slug}}">
<link rel="stylesheet" type="text/css" href=
"/assets/fonts/firasans.css">
<link rel="stylesheet" type="text/css" href=
"/assets/normalize.css">
<link rel="stylesheet" type="text/css" href=
"/assets/awesome-go.css"><!--ICONS-->
<link rel="icon" href="/assets/favicon/favicon.ico" type=
"image/x-icon">
<link rel="apple-touch-icon" href=
"/assets/favicon/apple-touch-icon.png">
<meta property="og:url" content="https://awesome-go.com/{{.Slug}}/">
<meta property="og:image" content="https://awesome-go.com/assets/logo.png">
<meta property="og:description" content="{{.Description}} - Awesome Go">
<meta property="og:site_name" content="Awesome Go">
<!-- Twitter -->
<meta name="twitter:card" content="summary">
<meta name="twitter:title" content="{{.Title}} - Awesome Go">
<meta name="twitter:description" content="{{.Description}}">
<link rel="canonical" href="https://awesome-go.com/{{.Slug}}/">
<!-- Fonts -->
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800&display=swap">
<!-- Styles -->
<link rel="stylesheet" href="/assets/awesome-go.css">
<!-- Icons -->
<link rel="icon" href="/assets/favicon/favicon.ico" type="image/x-icon">
<link rel="apple-touch-icon" href="/assets/favicon/apple-touch-icon.png">
<link rel="manifest" href="/assets/favicon/manifest.json">
<!-- Structured Data -->
<script type="application/ld+json">
{
"@context": "https://schema.org",
"@type": "CollectionPage",
"name": "{{.Title}} - Awesome Go",
"description": "{{.Description}}",
"url": "https://awesome-go.com/{{.Slug}}/",
"isPartOf": {
"@type": "WebSite",
"name": "Awesome Go",
"url": "https://awesome-go.com/"
},
"breadcrumb": {
"@type": "BreadcrumbList",
"itemListElement": [
{
"@type": "ListItem",
"position": 1,
"name": "Home",
"item": "https://awesome-go.com/"
},
{
"@type": "ListItem",
"position": 2,
"name": "{{.Title}}"
}
]
}
}
</script>
</head>
<body>
<header class="site-header" role="banner">
<div class="header-inner">
<a href="/" class="header-brand" aria-label="Awesome Go home">
<img src="/assets/logo.png" alt="" width="30" height="30">
Awesome Go
</a>
<nav class="header-nav" role="navigation" aria-label="Main navigation">
<a href="/#contents">Categories</a>
<a href="https://github.com/avelino/awesome-go" rel="noopener" class="github-link">
<svg viewBox="0 0 16 16" aria-hidden="true"><path d="M8 0C3.58 0 0 3.58 0 8c0 3.54 2.29 6.53 5.47 7.59.4.07.55-.17.55-.38 0-.19-.01-.82-.01-1.49-2.01.37-2.53-.49-2.69-.94-.09-.23-.48-.94-.82-1.13-.28-.15-.68-.52-.01-.53.63-.01 1.08.58 1.23.82.72 1.21 1.87.87 2.33.66.07-.52.28-.87.51-1.07-1.78-.2-3.64-.89-3.64-3.95 0-.87.31-1.59.82-2.15-.08-.2-.36-1.02.08-2.12 0 0 .67-.21 2.2.82.64-.18 1.32-.27 2-.27.68 0 1.36.09 2 .27 1.53-1.04 2.2-.82 2.2-.82.44 1.1.16 1.92.08 2.12.51.56.82 1.27.82 2.15 0 3.07-1.87 3.75-3.65 3.95.29.25.54.73.54 1.48 0 1.07-.01 1.93-.01 2.2 0 .21.15.46.55.38A8.013 8.013 0 0016 8c0-4.42-3.58-8-8-8z"/></svg>
GitHub
</a>
</nav>
</div>
</header>
<div class="category-hero">
<div class="category-hero-inner">
<nav class="breadcrumb" aria-label="Breadcrumb">
<a href="/">Home</a>
<span class="breadcrumb-separator" aria-hidden="true">/</span>
<span aria-current="page">{{.Title}}</span>
</nav>
<div class="category-header">
<h1>{{.Title}}</h1>
<p>{{.Description}}</p>
</div>
</div>
</div>
<main>
<div id="content">
<header>
<h1><a href="https://awesome-go.com/"><img align=
"right" src=
"https://github.com/avelino/awesome-go/raw/main/tmpl/assets/logo.png"
alt="awesome-go" title="awesome-go"></a> {{.Title}} -
<a href="https://awesome-go.com/">Awesome Go</a></h1>
<p><em>{{.Description}}</em></p>
<p><a href=
"https://github.com/avelino/awesome-go/actions/workflows/tests.yaml?query=branch%3Amain"
rel="nofollow"><img src=
"https://github.com/avelino/awesome-go/actions/workflows/tests.yaml/badge.svg?branch=main"
alt="Build Status"></a> <a href=
"https://github.com/sindresorhus/awesome" rel=
"nofollow"><img src=
"https://cdn.rawgit.com/sindresorhus/awesome/d7305f38d29fed78fa85652e3a63e154dd8e8829/media/badge.svg"
alt="Awesome"></a> <a href=
"https://gophers.slack.com/messages/awesome" rel=
"nofollow"><img src=
"https://img.shields.io/badge/join-us%20on%20slack-gray.svg?longCache=true&amp;logo=slack&amp;colorB=red"
alt="Slack Widget"></a> <a href=
"https://app.netlify.com/sites/awesome-go/deploys" rel=
"nofollow"><img src=
"https://api.netlify.com/api/v1/badges/83a6dcbe-0da6-433e-b586-f68109286bd5/deploy-status"
alt="Netlify Status"></a> <a href=
"https://www.trackawesomelist.com/avelino/awesome-go/"
rel="nofollow"><img src=
"https://www.trackawesomelist.com/badge.svg" alt=
"Track Awesome List"></a></p>
<p><a href=
"https://www.producthunt.com/posts/awesome-go?utm_source=badge-featured&amp;utm_medium=badge&amp;utm_souce=badge-awesome-go"
rel="nofollow"><img src=
"https://api.producthunt.com/widgets/embed-image/v1/featured.svg?post_id=291535&amp;theme=light"
width="250" height="54"></a></p>
</header><i><a href="/#contents" alt="back to content menu"
title="back to content menu">🗺️ back to content
menu</a></i>
<ul>
<ul class="category-list">
{{range .Links}}
<li>
<a href=
"{{.URL}}?utm_campaign=awesomego&amp;utm_medium=referral&amp;utm_source=awesomego"
alt="Go to {{.Title}} link" title=
"Go to {{.Title}} link">{{.Description}}</a>
</li>{{end}}
</ul><a href="https://bit.ly/awesome-go-netlify"><img src=
"https://www.netlify.com/img/global/badges/netlify-dark.svg"
alt="Deploys by Netlify"></a>
{{if .ProjectSlug}}
<a href="/{{$.Slug}}/{{.ProjectSlug}}/">{{.Description}}</a>
{{else}}
<a href="{{.URL}}?utm_campaign=awesomego&amp;utm_medium=referral&amp;utm_source=awesomego" rel="noopener nofollow">{{.Description}}</a>
{{end}}
</li>
{{end}}
</ul>
<a href="/" class="back-link">&larr; All categories</a>
</div>
<script src=
"https://cdnjs.cloudflare.com/ajax/libs/jquery/3.5.1/jquery.min.js"
integrity=
"sha512-bLT0Qm9VnAYZDflyKcBaQ2gg0hSYNQrJ8RilYldYQ1FxQYoCLtUjuuRuZo+fjqhx/qtq/1itJ0C2ejDxltZVFg=="
crossorigin="anonymous"></script>
<script src=
"https://cdnjs.cloudflare.com/ajax/libs/marked/1.1.0/marked.min.js"
integrity=
"sha512-uggp1jOpxGjqTeS8Fit5x6+lqyJoIuXXn/VziVPlxBRnqZ0FhCaxsUnQsPL5PKylHr0KIoMtNbBIiU6n31dDTg=="
crossorigin="anonymous"></script>
</main>
<footer class="site-footer" role="contentinfo">
<div class="footer-inner">
<div class="footer-links">
<a href="https://github.com/avelino/awesome-go" rel="noopener">GitHub</a>
<a href="https://github.com/avelino/awesome-go/blob/main/CONTRIBUTING.md" rel="noopener">Contributing</a>
<a href="https://awesome-go.com/sitemap.xml">Sitemap</a>
</div>
<p class="footer-credit">Maintained by the <a href="https://github.com/avelino/awesome-go/graphs/contributors" rel="noopener">awesome-go community</a></p>
</div>
</footer>
<!-- Google tag (gtag.js) -->
<script async src="https://www.googletagmanager.com/gtag/js?id=G-DXZMLYYVYM"></script>
<script>
window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}
gtag('js', new Date());
gtag('config', 'G-DXZMLYYVYM');
</script>
</body>

116
tmpl/index.tmpl.html vendored
View File

@ -1,58 +1,104 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width" />
<title>A curated list of awesome Go frameworks, libraries and software - Awesome Go / Golang</title>
<meta name="description" content="A curated list of awesome Go / Golang frameworks, libraries and software" />
<meta name="keywords" content="golang, go, awesome, awesome-go, go framework, golang framework" />
<meta name="twitter:card" value="summary" />
<meta property="og:title" content="A curated list of awesome Go frameworks, libraries and software - Awesome Go" />
<meta property="og:type" content="article" />
<meta property="og:url" content="https://awesome-go.com/" />
<meta property="og:image" content="https://awesome-go.com/assets/logo.png" />
<meta property="og:description" content="A curated list of awesome #Golang frameworks, libraries and software" />
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="color-scheme" content="light dark">
<link rel="canonical" href="https://awesome-go.com/" />
<title>Awesome Go - A curated list of awesome Go frameworks, libraries and software</title>
<meta name="description" content="A curated list of awesome Go frameworks, libraries and software. Community-driven, regularly updated.">
<meta name="keywords" content="golang, go, awesome, awesome-go, go framework, golang framework, go library, go packages">
<link rel="stylesheet" type="text/css" href="/assets/fonts/firasans.css" />
<link rel="stylesheet" type="text/css" href="/assets/normalize.css" />
<link rel="stylesheet" type="text/css" href="/assets/awesome-go.css" />
<!-- Open Graph -->
<meta property="og:title" content="Awesome Go - Curated list of Go frameworks, libraries and software">
<meta property="og:type" content="website">
<meta property="og:url" content="https://awesome-go.com/">
<meta property="og:image" content="https://awesome-go.com/assets/logo.png">
<meta property="og:description" content="A curated list of awesome Go frameworks, libraries and software. Community-driven, regularly updated.">
<meta property="og:site_name" content="Awesome Go">
<!--ICONS-->
<link rel="icon" href="/assets/favicon/favicon.ico" type="image/x-icon" />
<link rel="apple-touch-icon" href="/assets/favicon/apple-touch-icon.png" />
<link rel="manifest" href="/assets/favicon/manifest.json" />
<!-- Twitter -->
<meta name="twitter:card" content="summary">
<meta name="twitter:title" content="Awesome Go - Curated Go frameworks, libraries and software">
<meta name="twitter:description" content="A curated list of awesome Go frameworks, libraries and software.">
<meta name="twitter:image" content="https://awesome-go.com/assets/logo.png">
<link rel="canonical" href="https://awesome-go.com/">
<!-- Fonts -->
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800&display=swap">
<!-- Styles -->
<link rel="stylesheet" href="/assets/awesome-go.css">
<!-- Icons -->
<link rel="icon" href="/assets/favicon/favicon.ico" type="image/x-icon">
<link rel="apple-touch-icon" href="/assets/favicon/apple-touch-icon.png">
<link rel="manifest" href="/assets/favicon/manifest.json">
<!-- Structured Data -->
<script type="application/ld+json">
{
"@context": "https://schema.org",
"@type": "WebSite",
"name": "Awesome Go",
"url": "https://awesome-go.com/",
"description": "A curated list of awesome Go frameworks, libraries and software",
"publisher": {
"@type": "Organization",
"name": "Awesome Go",
"logo": {
"@type": "ImageObject",
"url": "https://awesome-go.com/assets/logo.png"
}
}
}
</script>
</head>
<body>
<div id="content">
<div>
<div id="amzn-assoc-ad-ce1dd292-c6f0-4062-ac99-55bc005bbbf9"></div>
<script async src="//z-na.amazon-adsystem.com/widgets/onejs?MarketPlace=US&adInstanceId=ce1dd292-c6f0-4062-ac99-55bc005bbbf9"></script>
</div>
{{.Body}}
<a href="https://bit.ly/awesome-go-netlify">
<img src="https://www.netlify.com/img/global/badges/netlify-dark.svg" alt="Deploys by Netlify" />
<header class="site-header" role="banner">
<div class="header-inner">
<a href="/" class="header-brand" aria-label="Awesome Go home">
<img src="/assets/logo.png" alt="" width="30" height="30">
Awesome Go
</a>
<nav class="header-nav" role="navigation" aria-label="Main navigation">
<a href="#contents">Categories</a>
<a href="https://github.com/avelino/awesome-go" rel="noopener" class="github-link">
<svg viewBox="0 0 16 16" aria-hidden="true"><path d="M8 0C3.58 0 0 3.58 0 8c0 3.54 2.29 6.53 5.47 7.59.4.07.55-.17.55-.38 0-.19-.01-.82-.01-1.49-2.01.37-2.53-.49-2.69-.94-.09-.23-.48-.94-.82-1.13-.28-.15-.68-.52-.01-.53.63-.01 1.08.58 1.23.82.72 1.21 1.87.87 2.33.66.07-.52.28-.87.51-1.07-1.78-.2-3.64-.89-3.64-3.95 0-.87.31-1.59.82-2.15-.08-.2-.36-1.02.08-2.12 0 0 .67-.21 2.2.82.64-.18 1.32-.27 2-.27.68 0 1.36.09 2 .27 1.53-1.04 2.2-.82 2.2-.82.44 1.1.16 1.92.08 2.12.51.56.82 1.27.82 2.15 0 3.07-1.87 3.75-3.65 3.95.29.25.54.73.54 1.48 0 1.07-.01 1.93-.01 2.2 0 .21.15.46.55.38A8.013 8.013 0 0016 8c0-4.42-3.58-8-8-8z"/></svg>
GitHub
</a>
</nav>
</div>
</header>
<main>
<div id="content">
{{.Body}}
</div>
</main>
<footer class="site-footer" role="contentinfo">
<div class="footer-inner">
<div class="footer-links">
<a href="https://github.com/avelino/awesome-go" rel="noopener">GitHub</a>
<a href="https://github.com/avelino/awesome-go/blob/main/CONTRIBUTING.md" rel="noopener">Contributing</a>
<a href="https://awesome-go.com/sitemap.xml">Sitemap</a>
</div>
<p class="footer-credit">Maintained by the <a href="https://github.com/avelino/awesome-go/graphs/contributors" rel="noopener">awesome-go community</a></p>
</div>
</footer>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.5.1/jquery.min.js" integrity="sha512-bLT0Qm9VnAYZDflyKcBaQ2gg0hSYNQrJ8RilYldYQ1FxQYoCLtUjuuRuZo+fjqhx/qtq/1itJ0C2ejDxltZVFg==" crossorigin="anonymous"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/marked/1.1.0/marked.min.js" integrity="sha512-uggp1jOpxGjqTeS8Fit5x6+lqyJoIuXXn/VziVPlxBRnqZ0FhCaxsUnQsPL5PKylHr0KIoMtNbBIiU6n31dDTg==" crossorigin="anonymous"></script>
<!-- Google tag (gtag.js) -->
<script async src="https://www.googletagmanager.com/gtag/js?id=G-DXZMLYYVYM"></script>
<script>
window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}
gtag('js', new Date());
gtag('config', 'G-DXZMLYYVYM');
</script>
</body>
</html>

206
tmpl/project.tmpl.html vendored Normal file
View File

@ -0,0 +1,206 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="color-scheme" content="light dark">
<title>{{.Title}} - {{.CategoryTitle}} - Awesome Go</title>
<meta name="description" content="{{.Description}} - Curated Go library in {{.CategoryTitle}}.">
<meta name="keywords" content="{{.Title}}, {{.CategoryTitle}}, golang, go, awesome-go">
<!-- Open Graph -->
<meta property="og:title" content="{{.Title}} - Awesome Go">
<meta property="og:type" content="article">
<meta property="og:url" content="https://awesome-go.com/{{.CategorySlug}}/{{.Slug}}/">
<meta property="og:image" content="https://awesome-go.com/assets/logo.png">
<meta property="og:description" content="{{.Description}}">
<meta property="og:site_name" content="Awesome Go">
<!-- Twitter -->
<meta name="twitter:card" content="summary">
<meta name="twitter:title" content="{{.Title}} - Awesome Go">
<meta name="twitter:description" content="{{.Description}}">
<link rel="canonical" href="https://awesome-go.com/{{.CategorySlug}}/{{.Slug}}/">
<!-- Fonts -->
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800&display=swap">
<!-- Styles -->
<link rel="stylesheet" href="/assets/awesome-go.css">
<!-- Icons -->
<link rel="icon" href="/assets/favicon/favicon.ico" type="image/x-icon">
<link rel="apple-touch-icon" href="/assets/favicon/apple-touch-icon.png">
<link rel="manifest" href="/assets/favicon/manifest.json">
<!-- Structured Data -->
<script type="application/ld+json">
{
"@context": "https://schema.org",
"@type": "SoftwareApplication",
"name": "{{jsonEscape .Title}}",
"description": "{{jsonEscape .Description}}",
"url": "https://awesome-go.com/{{.CategorySlug}}/{{.Slug}}/",
"applicationCategory": "DeveloperApplication",
"operatingSystem": "Cross-platform",
"programmingLanguage": "Go",
"codeRepository": "{{.URL}}"{{if .Meta}},
"aggregateRating": {
"@type": "AggregateRating",
"ratingCount": {{.Meta.Stars}}
}{{end}},
"breadcrumb": {
"@type": "BreadcrumbList",
"itemListElement": [
{
"@type": "ListItem",
"position": 1,
"name": "Home",
"item": "https://awesome-go.com/"
},
{
"@type": "ListItem",
"position": 2,
"name": "{{jsonEscape .CategoryTitle}}",
"item": "https://awesome-go.com/{{.CategorySlug}}/"
},
{
"@type": "ListItem",
"position": 3,
"name": "{{jsonEscape .Title}}"
}
]
}
}
</script>
</head>
<body>
<header class="site-header" role="banner">
<div class="header-inner">
<a href="/" class="header-brand" aria-label="Awesome Go home">
<img src="/assets/logo.png" alt="" width="30" height="30">
Awesome Go
</a>
<nav class="header-nav" role="navigation" aria-label="Main navigation">
<a href="/#contents">Categories</a>
<a href="https://github.com/avelino/awesome-go" rel="noopener" class="github-link">
<svg viewBox="0 0 16 16" aria-hidden="true"><path d="M8 0C3.58 0 0 3.58 0 8c0 3.54 2.29 6.53 5.47 7.59.4.07.55-.17.55-.38 0-.19-.01-.82-.01-1.49-2.01.37-2.53-.49-2.69-.94-.09-.23-.48-.94-.82-1.13-.28-.15-.68-.52-.01-.53.63-.01 1.08.58 1.23.82.72 1.21 1.87.87 2.33.66.07-.52.28-.87.51-1.07-1.78-.2-3.64-.89-3.64-3.95 0-.87.31-1.59.82-2.15-.08-.2-.36-1.02.08-2.12 0 0 .67-.21 2.2.82.64-.18 1.32-.27 2-.27.68 0 1.36.09 2 .27 1.53-1.04 2.2-.82 2.2-.82.44 1.1.16 1.92.08 2.12.51.56.82 1.27.82 2.15 0 3.07-1.87 3.75-3.65 3.95.29.25.54.73.54 1.48 0 1.07-.01 1.93-.01 2.2 0 .21.15.46.55.38A8.013 8.013 0 0016 8c0-4.42-3.58-8-8-8z"/></svg>
GitHub
</a>
</nav>
</div>
</header>
<div class="project-hero">
<div class="project-hero-inner">
<nav class="breadcrumb" aria-label="Breadcrumb">
<a href="/">Home</a>
<span class="breadcrumb-separator" aria-hidden="true">/</span>
<a href="/{{.CategorySlug}}/">{{.CategoryTitle}}</a>
<span class="breadcrumb-separator" aria-hidden="true">/</span>
<span aria-current="page">{{.Title}}</span>
</nav>
<div class="project-header">
<h1>{{.Title}}</h1>
<p>{{.Description}}</p>
<a href="{{.URL}}" rel="nofollow noopener" target="_blank" class="repo-link">
View Repository
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6"/><polyline points="15 3 21 3 21 9"/><line x1="10" y1="14" x2="21" y2="3"/></svg>
</a>
</div>
</div>
</div>
<main>
<div id="content">
{{if .Meta}}
<div class="project-meta">
<div class="meta-card">
<span class="meta-value">{{.Meta.Stars}}</span>
<span class="meta-label">Stars</span>
</div>
<div class="meta-card">
<span class="meta-value">{{.Meta.Forks}}</span>
<span class="meta-label">Forks</span>
</div>
{{if .Meta.License}}
<div class="meta-card">
<span class="meta-value">{{.Meta.License}}</span>
<span class="meta-label">License</span>
</div>
{{end}}
{{if .Meta.Language}}
<div class="meta-card">
<span class="meta-value">{{.Meta.Language}}</span>
<span class="meta-label">Language</span>
</div>
{{end}}
{{if .Meta.LastPush}}
<div class="meta-card">
<span class="meta-value">{{.Meta.LastPush}}</span>
<span class="meta-label">Last Update</span>
</div>
{{end}}
<div class="meta-card">
<span class="meta-value">{{.Meta.OpenIssues}}</span>
<span class="meta-label">Open Issues</span>
</div>
</div>
{{if .Meta.Topics}}
<div class="project-topics">
{{range .Meta.Topics}}
<span class="topic-tag">{{.}}</span>
{{end}}
</div>
{{end}}
{{end}}
{{if .Related}}
<section class="related-projects">
<h2>Related in {{.CategoryTitle}}</h2>
<ul class="category-list">
{{range .Related}}
<li>
{{if .ProjectSlug}}
<a href="/{{$.CategorySlug}}/{{.ProjectSlug}}/">{{.Description}}</a>
{{else}}
<a href="{{.URL}}" rel="noopener nofollow">{{.Description}}</a>
{{end}}
</li>
{{end}}
</ul>
</section>
{{end}}
<a href="/{{.CategorySlug}}/" class="back-link">&larr; All {{.CategoryTitle}}</a>
</div>
</main>
<footer class="site-footer" role="contentinfo">
<div class="footer-inner">
<div class="footer-links">
<a href="https://github.com/avelino/awesome-go" rel="noopener">GitHub</a>
<a href="https://github.com/avelino/awesome-go/blob/main/CONTRIBUTING.md" rel="noopener">Contributing</a>
<a href="https://awesome-go.com/sitemap.xml">Sitemap</a>
</div>
<p class="footer-credit">Maintained by the <a href="https://github.com/avelino/awesome-go/graphs/contributors" rel="noopener">awesome-go community</a></p>
</div>
</footer>
<!-- Google tag (gtag.js) -->
<script async src="https://www.googletagmanager.com/gtag/js?id=G-DXZMLYYVYM"></script>
<script>
window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}
gtag('js', new Date());
gtag('config', 'G-DXZMLYYVYM');
</script>
</body>
</html>

View File

@ -1,2 +1,4 @@
User-Agent: *
Allow: /
Sitemap: https://awesome-go.com/sitemap.xml

View File

@ -6,13 +6,25 @@
http://www.sitemaps.org/schemas/sitemap/0.9/sitemap.xsd">
<url>
<loc>https://www.awesome-go.com/</loc>
<lastmod>2016-10-10T07:39:03+00:00</lastmod>
<loc>https://awesome-go.com/</loc>
<lastmod>{{now}}</lastmod>
<changefreq>daily</changefreq>
<priority>1.0</priority>
</url>
{{range .}}
{{range .Categories}}
<url>
<loc>https://www.awesome-go.com/{{.Slug}}</loc>
<lastmod>2016-10-10T07:39:03+00:00</lastmod>
<loc>https://awesome-go.com/{{.Slug}}/</loc>
<lastmod>{{now}}</lastmod>
<changefreq>weekly</changefreq>
<priority>0.8</priority>
</url>
{{end}}
{{range .Projects}}
<url>
<loc>https://awesome-go.com/{{.CategorySlug}}/{{.Slug}}/</loc>
<lastmod>{{now}}</lastmod>
<changefreq>weekly</changefreq>
<priority>0.6</priority>
</url>
{{end}}
</urlset>