//go:build dev /* Package cmd Copyright © 2024 Shane C. */ package cmd import ( "errors" "github.com/a-h/templ/parser/v2" "github.com/evanw/esbuild/pkg/api" "github.com/kr/pretty" "github.com/spf13/cobra" "io" "io/fs" "log" "os" "path/filepath" "strings" ) var ignoredAssetExtensions = []string{ ".css", ".js", ".ts", } var ignoredDirectories = []string{ "svg", "dist", } var foundIcons []string // assetsCmd represents the assets command var assetsCmd = &cobra.Command{ Use: "assets", Short: "Generates assets needed for webserver", Run: func(cmd *cobra.Command, args []string) { err := filepath.Walk("./web/assets", func(path string, info os.FileInfo, err error) error { if info.IsDir() { return nil } relPath, _ := strings.CutPrefix(path, "web/assets/") for _, dir := range ignoredDirectories { if strings.HasPrefix(relPath, dir) { return filepath.SkipDir } } for _, ext := range ignoredAssetExtensions { if strings.HasSuffix(info.Name(), ext) { return nil } } distFolder, _ := strings.CutSuffix(relPath, info.Name()) if err := os.MkdirAll("web/assets/dist/"+distFolder, 0700); err != nil && !errors.Is(err, fs.ErrExist) { log.Fatalln(err) } copyFile(path, filepath.Join("web/assets/dist/"+distFolder, info.Name())) return nil }) if err != nil { log.Fatalln(err) } result := api.Build(api.BuildOptions{ Format: api.FormatESModule, EntryPoints: []string{"./web/assets/js/*.ts", "./web/assets/js/**/*.ts"}, Outdir: "./web/assets/dist/js", Sourcemap: api.SourceMapLinked, MinifyWhitespace: true, MinifyIdentifiers: true, MinifySyntax: true, Splitting: true, Write: true, TreeShaking: api.TreeShakingTrue, Bundle: true, }) if len(result.Errors) != 0 { pretty.Println(result.Errors) os.Exit(1) } tmplFile, err := parser.Parse("./web/views/auth/register/show.templ") if err != nil { log.Fatalln(err) } for _, node := range tmplFile.Nodes { templateNode, ok := node.(parser.HTMLTemplate) if !ok { continue } for _, htmlTemplChild := range templateNode.Children { if elExpress, ok := htmlTemplChild.(parser.TemplElementExpression); ok { for _, htmlNode := range elExpress.Children { if htmlNodeEl, ok := htmlNode.(parser.Element); ok { parseHtmlNode(htmlNodeEl) } } } } } err = filepath.Walk("./web/views/", func(path string, info os.FileInfo, err error) error { if info.IsDir() || filepath.Ext(info.Name()) != ".templ" { return nil } tmplFile, err := parser.Parse(path) if err != nil { return err } for _, node := range tmplFile.Nodes { templateNode, ok := node.(parser.HTMLTemplate) if !ok { continue } for _, htmlTemplChild := range templateNode.Children { if elExpress, ok := htmlTemplChild.(parser.TemplElementExpression); ok { for _, htmlNode := range elExpress.Children { if htmlNodeEl, ok := htmlNode.(parser.Element); ok { parseHtmlNode(htmlNodeEl) } } } } } return nil }) for _, icon := range foundIcons { copyFile(filepath.Join("web/assets/icons", icon+".svg"), filepath.Join("web/assets/dist/svg/", icon+".svg")) } }, } func parseHtmlNode(node parser.Element) { if node.Children != nil { for _, nodeChild := range node.Children { if nodeChildEl, ok := nodeChild.(parser.Element); ok { parseHtmlNode(nodeChildEl) } } } if node.Name == "ion-icon" { for _, attr := range node.Attributes { if constAttr, ok := attr.(parser.ConstantAttribute); ok { if constAttr.Name == "name" { for _, icon := range foundIcons { if constAttr.Value == icon { return } } foundIcons = append(foundIcons, constAttr.Value) } } } } } func init() { generateCmd.AddCommand(assetsCmd) } func copyFile(sourcePath, destinationPath string) { sourcePath = filepath.Clean(sourcePath) sourceFile, err := os.Open(sourcePath) if err != nil { log.Fatalln(err) } defer sourceFile.Close() destinationPath = filepath.Clean(destinationPath) if _, err := os.Stat(filepath.Dir(destinationPath)); errors.Is(err, fs.ErrNotExist) { if err := os.MkdirAll(filepath.Dir(destinationPath), 0700); err != nil { log.Fatalln(err) } } destinationFile, err := os.Create(destinationPath) if err != nil { log.Fatalln(err) } defer destinationFile.Close() _, err = io.Copy(destinationFile, sourceFile) if err != nil { log.Fatalln(err) } if err := sourceFile.Close(); err != nil { log.Fatalln(err) } if err := destinationFile.Close(); err != nil { log.Fatalln(err) } }