123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269 |
- package main
- import (
- "fmt"
- "go/ast"
- "go/parser"
- "go/token"
- "os"
- "path/filepath"
- "regexp"
- "sort"
- "strings"
- "text/template"
- )
- // Structure to hold extracted notifier information
- type NotifierInfo struct {
- Name string
- Fields []FieldInfo
- FileName string
- ConfigKey string
- }
- // Structure to hold field information for notifier
- type FieldInfo struct {
- Name string
- Key string
- Title string
- }
- // Template for the TypeScript config file
- const tsConfigTemplate = `// This file is auto-generated by notification generator. DO NOT EDIT.
- import type { ExternalNotifyConfig } from './types'
- const {{.Name}}Config: ExternalNotifyConfig = {
- name: () => $gettext('{{.Name}}'),
- config: [
- {{- range .Fields}}
- {
- key: '{{.Key}}',
- label: () => $gettext('{{.Title}}'),
- },
- {{- end}}
- ],
- }
- export default {{.Name}}Config
- `
- // Regular expression to extract @external_notifier annotation
- var externalNotifierRegex = regexp.MustCompile(`@external_notifier\((\w+)\)`)
- func main() {
- if err := GenerateExternalNotifiers(); err != nil {
- fmt.Printf("error generating external notifier configs: %v\n", err)
- }
- }
- // GenerateExternalNotifiers generates TypeScript config files for external notifiers
- func GenerateExternalNotifiers() error {
- fmt.Println("Generating external notifier configs...")
- // Notification package path
- notificationPkgPath := "internal/notification"
- outputDir := "app/src/views/preference/components/ExternalNotify"
- // Create output directory if it doesn't exist
- if err := os.MkdirAll(outputDir, 0755); err != nil {
- return fmt.Errorf("error creating output directory: %w", err)
- }
- // Get all Go files in the notification package
- files, err := filepath.Glob(filepath.Join(notificationPkgPath, "*.go"))
- if err != nil {
- return fmt.Errorf("error scanning notification package: %w", err)
- }
- // Collect all notifier info
- notifiers := []NotifierInfo{}
- for _, file := range files {
- notifier, found := extractNotifierInfo(file)
- if found {
- notifiers = append(notifiers, notifier)
- fmt.Printf("Found notifier: %s in %s\n", notifier.Name, file)
- }
- }
- // Generate TypeScript config files
- for _, notifier := range notifiers {
- if err := generateTSConfig(notifier, outputDir); err != nil {
- return fmt.Errorf("error generating config for %s: %w", notifier.Name, err)
- }
- }
- // Update index.ts
- if err := updateIndexFile(notifiers, outputDir); err != nil {
- return fmt.Errorf("error updating index.ts: %w", err)
- }
- fmt.Println("Generation completed successfully!")
- return nil
- }
- // Extract notifier information from a Go file
- func extractNotifierInfo(filePath string) (NotifierInfo, bool) {
- // Create the FileSet
- fset := token.NewFileSet()
- // Parse the file
- file, err := parser.ParseFile(fset, filePath, nil, parser.ParseComments)
- if err != nil {
- fmt.Printf("Error parsing file %s: %v\n", filePath, err)
- return NotifierInfo{}, false
- }
- var notifierInfo NotifierInfo
- found := false
- // Look for the type declaration with the @external_notifier annotation
- for _, decl := range file.Decls {
- genDecl, ok := decl.(*ast.GenDecl)
- if !ok || genDecl.Tok != token.TYPE {
- continue
- }
- for _, spec := range genDecl.Specs {
- typeSpec, ok := spec.(*ast.TypeSpec)
- if !ok {
- continue
- }
- structType, ok := typeSpec.Type.(*ast.StructType)
- if !ok {
- continue
- }
- // Check if we have a comment with @external_notifier
- if genDecl.Doc != nil {
- for _, comment := range genDecl.Doc.List {
- matches := externalNotifierRegex.FindStringSubmatch(comment.Text)
- if len(matches) > 1 {
- notifierInfo.Name = matches[1]
- notifierInfo.ConfigKey = strings.ToLower(typeSpec.Name.Name)
- notifierInfo.FileName = strings.ToLower(matches[1])
- found = true
- // Extract fields
- for _, field := range structType.Fields.List {
- if len(field.Names) > 0 {
- fieldName := field.Names[0].Name
- // Get json tag and title from field tags
- var jsonKey, title string
- if field.Tag != nil {
- tagValue := strings.Trim(field.Tag.Value, "`")
- // Extract json key
- jsonRegex := regexp.MustCompile(`json:"([^"]+)"`)
- jsonMatches := jsonRegex.FindStringSubmatch(tagValue)
- if len(jsonMatches) > 1 {
- jsonKey = jsonMatches[1]
- }
- // Extract title
- titleRegex := regexp.MustCompile(`title:"([^"]+)"`)
- titleMatches := titleRegex.FindStringSubmatch(tagValue)
- if len(titleMatches) > 1 {
- title = titleMatches[1]
- }
- }
- if jsonKey == "" {
- jsonKey = strings.ToLower(fieldName)
- }
- if title == "" {
- title = fieldName
- }
- notifierInfo.Fields = append(notifierInfo.Fields, FieldInfo{
- Name: fieldName,
- Key: jsonKey,
- Title: title,
- })
- }
- }
- break
- }
- }
- }
- if found {
- break
- }
- }
- if found {
- break
- }
- }
- return notifierInfo, found
- }
- // Generate TypeScript config file for a notifier
- func generateTSConfig(notifier NotifierInfo, outputDir string) error {
- // Create template
- tmpl, err := template.New("tsConfig").Parse(tsConfigTemplate)
- if err != nil {
- return fmt.Errorf("error creating template: %w", err)
- }
- // Create output file
- outputFile := filepath.Join(outputDir, notifier.FileName+".ts")
- file, err := os.Create(outputFile)
- if err != nil {
- return fmt.Errorf("error creating output file %s: %w", outputFile, err)
- }
- defer file.Close()
- // Execute template
- err = tmpl.Execute(file, notifier)
- if err != nil {
- return fmt.Errorf("error executing template: %w", err)
- }
- fmt.Printf("Generated TypeScript config for %s at %s\n", notifier.Name, outputFile)
- return nil
- }
- // Update index.ts file
- func updateIndexFile(notifiers []NotifierInfo, outputDir string) error {
- // Create content for index.ts
- var imports strings.Builder
- var configMap strings.Builder
- // Sort notifiers alphabetically by name for stable output
- sort.Slice(notifiers, func(i, j int) bool {
- return notifiers[i].Name < notifiers[j].Name
- })
- for _, notifier := range notifiers {
- fileName := notifier.FileName
- configName := notifier.Name + "Config"
- imports.WriteString(fmt.Sprintf("import %s from './%s'\n", configName, fileName))
- }
- // Generate the map
- configMap.WriteString("const configMap = {\n")
- for _, notifier := range notifiers {
- configMap.WriteString(fmt.Sprintf(" %s: %sConfig", strings.ToLower(notifier.Name), notifier.Name))
- configMap.WriteString(",\n")
- }
- configMap.WriteString("}\n")
- content := fmt.Sprintf("// This file is auto-generated by notification generator. DO NOT EDIT.\n%s\n%s\nexport default configMap\n", imports.String(), configMap.String())
- // Write to index.ts
- indexPath := filepath.Join(outputDir, "index.ts")
- err := os.WriteFile(indexPath, []byte(content), 0644)
- if err != nil {
- return fmt.Errorf("error writing index.ts: %w", err)
- }
- fmt.Printf("Updated index.ts at %s\n", indexPath)
- return nil
- }
|