mirror of
https://github.com/avelino/awesome-go.git
synced 2026-04-11 02:11:43 +08:00
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:
parent
a234403f92
commit
a532200f99
8
.github/workflows/site-deploy.yaml
vendored
8
.github/workflows/site-deploy.yaml
vendored
@ -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
1
.gitignore
vendored
@ -1,5 +1,6 @@
|
||||
out/
|
||||
awesome-go
|
||||
.cache/
|
||||
|
||||
# Folders
|
||||
.idea
|
||||
|
||||
423
main.go
423
main.go
@ -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
|
||||
}
|
||||
|
||||
1095
tmpl/assets/awesome-go.css
vendored
1095
tmpl/assets/awesome-go.css
vendored
File diff suppressed because it is too large
Load Diff
226
tmpl/category-index.tmpl.html
vendored
226
tmpl/category-index.tmpl.html
vendored
@ -2,104 +2,142 @@
|
||||
<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>
|
||||
<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&logo=slack&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&utm_medium=badge&utm_souce=badge-awesome-go"
|
||||
rel="nofollow"><img src=
|
||||
"https://api.producthunt.com/widgets/embed-image/v1/featured.svg?post_id=291535&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>
|
||||
{{range .Links}}
|
||||
<li>
|
||||
<a href=
|
||||
"{{.URL}}?utm_campaign=awesomego&utm_medium=referral&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>
|
||||
</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>
|
||||
<!-- 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>
|
||||
<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">
|
||||
<ul class="category-list">
|
||||
{{range .Links}}
|
||||
<li>
|
||||
{{if .ProjectSlug}}
|
||||
<a href="/{{$.Slug}}/{{.ProjectSlug}}/">{{.Description}}</a>
|
||||
{{else}}
|
||||
<a href="{{.URL}}?utm_campaign=awesomego&utm_medium=referral&utm_source=awesomego" rel="noopener nofollow">{{.Description}}</a>
|
||||
{{end}}
|
||||
</li>
|
||||
{{end}}
|
||||
</ul>
|
||||
|
||||
<a href="/" class="back-link">← All categories</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>
|
||||
|
||||
132
tmpl/index.tmpl.html
vendored
132
tmpl/index.tmpl.html
vendored
@ -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>
|
||||
|
||||
<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>
|
||||
|
||||
{{.Body}}
|
||||
<main>
|
||||
<div id="content">
|
||||
{{.Body}}
|
||||
</div>
|
||||
</main>
|
||||
|
||||
<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>
|
||||
</div>
|
||||
<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>
|
||||
<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
206
tmpl/project.tmpl.html
vendored
Normal 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">← 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>
|
||||
@ -1,2 +1,4 @@
|
||||
User-Agent: *
|
||||
Allow: /
|
||||
|
||||
Sitemap: https://awesome-go.com/sitemap.xml
|
||||
|
||||
@ -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>
|
||||
|
||||
Loading…
Reference in New Issue
Block a user