mirror of
https://github.com/go-gitea/gitea.git
synced 2026-09-26 06:33:42 +09:00
Generate emoji data from Unicode 17's `emoji-test.txt`, keeping existing aliases. `public/assets/emoji.json` is now the single emoji data file, also loaded by the backend. Rendered emoji drop their `aria-label`, the dark theme inverts key on a new `data-alias` attribute instead. Skin tone variants and their Gitea-only aliases are removed, GitHub has none either. Emoji autocompletion is now lazy-loaded with the markdown editor, shrinking the index JS chunk from 653KB to 563KB. --------- Signed-off-by: silverwind <me@silverwind.io> Signed-off-by: wxiaoguang <wxiaoguang@gmail.com> Co-authored-by: wxiaoguang <wxiaoguang@gmail.com> Co-authored-by: bircni <bircni@icloud.com>
133 lines
3.1 KiB
Go
133 lines
3.1 KiB
Go
// Copyright 2020 The Gitea Authors. All rights reserved.
|
||
// Copyright 2015 Kenneth Shaw
|
||
// SPDX-License-Identifier: MIT
|
||
|
||
//go:build ignore
|
||
|
||
package main
|
||
|
||
import (
|
||
"bufio"
|
||
"errors"
|
||
"fmt"
|
||
"log"
|
||
"net/http"
|
||
"os"
|
||
"regexp"
|
||
"slices"
|
||
"strings"
|
||
|
||
"gitea.dev/modules/json"
|
||
)
|
||
|
||
const (
|
||
emojiTestURL = "https://www.unicode.org/Public/17.0.0/emoji/emoji-test.txt"
|
||
jsonFile = "public/assets/emoji.json"
|
||
)
|
||
|
||
type emoji struct {
|
||
Emoji string `json:"emoji"`
|
||
Aliases []string `json:"aliases"`
|
||
description string
|
||
}
|
||
|
||
var slugRe = regexp.MustCompile(`[^a-z0-9]+`)
|
||
|
||
func main() {
|
||
if err := generate(); err != nil {
|
||
log.Fatal(err)
|
||
}
|
||
}
|
||
|
||
func generate() error {
|
||
// the existing file is also the alias source, so existing aliases stay stable
|
||
existing, err := os.ReadFile(jsonFile)
|
||
if err != nil {
|
||
return err
|
||
}
|
||
var existingEmojis []*emoji
|
||
if err := json.Unmarshal(existing, &existingEmojis); err != nil {
|
||
return err
|
||
}
|
||
existingAliases := make(map[string][]string, len(existingEmojis))
|
||
for _, e := range existingEmojis {
|
||
existingAliases[e.Emoji] = e.Aliases
|
||
}
|
||
|
||
emojis, err := fetchEmojis(existingAliases)
|
||
if err != nil {
|
||
return err
|
||
}
|
||
|
||
lines := make([]string, len(emojis))
|
||
for i, e := range emojis {
|
||
line, err := json.Marshal(e)
|
||
if err != nil {
|
||
return err
|
||
}
|
||
lines[i] = string(line)
|
||
}
|
||
return os.WriteFile(jsonFile, []byte("[\n"+strings.Join(lines, ",\n")+"\n]\n"), 0o644)
|
||
}
|
||
|
||
func isSkinTone(r rune) bool {
|
||
return r >= 0x1f3fb && r <= 0x1f3ff
|
||
}
|
||
|
||
func fetchEmojis(existingAliases map[string][]string) ([]*emoji, error) {
|
||
res, err := http.Get(emojiTestURL)
|
||
if err != nil {
|
||
return nil, err
|
||
}
|
||
defer res.Body.Close()
|
||
if res.StatusCode != http.StatusOK {
|
||
return nil, fmt.Errorf("fetching %s: %s", emojiTestURL, res.Status)
|
||
}
|
||
|
||
var emojis []*emoji
|
||
scanner := bufio.NewScanner(res.Body)
|
||
for scanner.Scan() {
|
||
// e.g. "1F44D ; fully-qualified # 👍 E0.6 thumbs up"
|
||
_, rest, _ := strings.Cut(scanner.Text(), ";")
|
||
status, comment, _ := strings.Cut(rest, "#")
|
||
if strings.TrimSpace(status) != "fully-qualified" {
|
||
continue
|
||
}
|
||
fields := strings.SplitN(strings.TrimSpace(comment), " ", 3)
|
||
if strings.ContainsFunc(fields[0], isSkinTone) {
|
||
continue
|
||
}
|
||
emojis = append(emojis, &emoji{Emoji: fields[0], description: fields[2]})
|
||
}
|
||
if err := scanner.Err(); err != nil {
|
||
return nil, err
|
||
}
|
||
|
||
var errs []error
|
||
aliasOwners := map[string]string{}
|
||
for _, e := range emojis {
|
||
e.Aliases = existingAliases[e.Emoji]
|
||
if e.Aliases == nil {
|
||
e.Aliases = []string{strings.Trim(slugRe.ReplaceAllString(strings.ReplaceAll(strings.ToLower(e.description), "’", ""), "_"), "_")}
|
||
}
|
||
delete(existingAliases, e.Emoji)
|
||
for _, alias := range e.Aliases {
|
||
if owner, ok := aliasOwners[alias]; ok {
|
||
errs = append(errs, fmt.Errorf("alias %q used by both %q and %q", alias, owner, e.Emoji))
|
||
}
|
||
aliasOwners[alias] = e.Emoji
|
||
}
|
||
}
|
||
for code, aliases := range existingAliases {
|
||
errs = append(errs, fmt.Errorf("emoji %q with aliases %v is missing from Unicode data", code, aliases))
|
||
}
|
||
if len(errs) > 0 {
|
||
return nil, errors.Join(errs...)
|
||
}
|
||
|
||
slices.SortFunc(emojis, func(a, b *emoji) int {
|
||
return strings.Compare(a.Aliases[0], b.Aliases[0])
|
||
})
|
||
return emojis, nil
|
||
}
|