main.go 2.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155
  1. package main
  2. import (
  3. "context"
  4. "fmt"
  5. "log"
  6. "net/http"
  7. "os"
  8. "os/exec"
  9. "os/signal"
  10. "time"
  11. "github.com/fatih/color"
  12. "github.com/spf13/pflag"
  13. "golang.org/x/sync/errgroup"
  14. )
  15. func startViteServer(
  16. ctx context.Context,
  17. root string,
  18. port int,
  19. ) error {
  20. c := exec.CommandContext(
  21. ctx,
  22. "node_modules/.bin/vite",
  23. "--clearScreen=false",
  24. fmt.Sprintf("--port=%d", port))
  25. c.Dir = root
  26. c.Stdout = os.Stdout
  27. c.Stderr = os.Stderr
  28. return c.Start()
  29. }
  30. func startGoServer(
  31. ctx context.Context,
  32. root string,
  33. proxyURL string,
  34. ) error {
  35. c := exec.CommandContext(
  36. ctx,
  37. "bin/go",
  38. fmt.Sprintf("--asset-proxy-url=%s", proxyURL))
  39. c.Dir = root
  40. c.Stdout = os.Stdout
  41. c.Stderr = os.Stderr
  42. return c.Start()
  43. }
  44. func isAvailable(
  45. ctx context.Context,
  46. url string,
  47. ) error {
  48. req, err := http.NewRequestWithContext(
  49. ctx,
  50. http.MethodHead,
  51. url,
  52. nil)
  53. if err != nil {
  54. return err
  55. }
  56. res, err := http.DefaultClient.Do(req)
  57. if err != nil {
  58. return err
  59. }
  60. defer res.Body.Close()
  61. return nil
  62. }
  63. func waitForAvailability(
  64. ctx context.Context,
  65. urls ...string,
  66. ) error {
  67. g, ctx := errgroup.WithContext(ctx)
  68. for _, url := range urls {
  69. g.Go(func() error {
  70. for {
  71. if err := isAvailable(ctx, url); err == nil {
  72. return nil
  73. }
  74. select {
  75. case <-ctx.Done():
  76. return ctx.Err()
  77. case <-time.After(time.Millisecond * 100):
  78. }
  79. }
  80. })
  81. }
  82. return g.Wait()
  83. }
  84. func main() {
  85. var flags struct {
  86. Root string
  87. Vite struct {
  88. Port int
  89. }
  90. }
  91. pflag.StringVar(
  92. &flags.Root,
  93. "root",
  94. ".",
  95. "Root directory for the server")
  96. pflag.IntVar(
  97. &flags.Vite.Port,
  98. "vite.port",
  99. 3000,
  100. "Port for the vite server")
  101. pflag.Parse()
  102. ctx, done := signal.NotifyContext(context.Background(), os.Interrupt)
  103. defer done()
  104. if err := startViteServer(
  105. ctx,
  106. flags.Root,
  107. flags.Vite.Port,
  108. ); err != nil {
  109. log.Panic(err)
  110. }
  111. viteURL := fmt.Sprintf("http://localhost:%d", flags.Vite.Port)
  112. if err := startGoServer(
  113. ctx,
  114. flags.Root,
  115. viteURL,
  116. ); err != nil {
  117. log.Panic(err)
  118. }
  119. {
  120. goURL := "http://localhost:8067/"
  121. ctx, done := context.WithTimeout(ctx, time.Second*5)
  122. defer done()
  123. if err := waitForAvailability(
  124. ctx,
  125. viteURL,
  126. goURL,
  127. ); err != nil {
  128. log.Panic(err)
  129. }
  130. green := color.New(color.FgGreen).SprintFunc()
  131. fmt.Println()
  132. fmt.Printf("development server running...\n%s\n", green(goURL))
  133. }
  134. <-ctx.Done()
  135. }