This repository has no description
1package main
2
3import (
4 "encoding/json"
5 "fmt"
6 "io"
7 "os"
8 "os/exec"
9 "strings"
10
11 "git.inpt.fr/churros/notella"
12 "github.com/invopop/jsonschema"
13
14 esbuild "github.com/evanw/esbuild/pkg/api"
15 ll "github.com/gwennlbh/label-logger-go"
16)
17
18func main() {
19 ll.Log("Reflecting", "cyan", "structs")
20 reflector := new(jsonschema.Reflector)
21 if err := reflector.AddGoComments("git.inpt.fr/churros/notella", "./"); err != nil {
22 fmt.Printf("Error adding Go comments: %v\n", err)
23 }
24
25 ll.Log("Writing", "cyan", "typescript types")
26 writeTypescriptDefinition(reflector, "Message", ¬ella.Message{}, "typescript/message.ts")
27 writeTypescriptDefinition(reflector, "HealthResponse", ¬ella.HealthResponse{}, "typescript/health.ts")
28 reflector.FieldNameTag = "env"
29 writeTypescriptDefinition(reflector, "Configuration", ¬ella.Configuration{}, "typescript/configuration.ts")
30
31 // Also save useful constants
32 ll.Log("Writing", "cyan", "exported constants")
33 os.WriteFile("typescript/constants.ts", printTypescriptConstants(
34 constant{"STREAM_NAME", notella.StreamName},
35 constant{"SUBJECT_NAME", notella.SubjectName},
36 constant{"CONSUMER_NAME", notella.ConsumerName},
37 ), 0644)
38
39 // Write barrel
40 ll.Log("Writing", "cyan", "barrel file")
41 os.WriteFile("typescript/index.ts", []byte(strings.Join([]string{
42 "export * from './message.js';",
43 "export * from './configuration.js';",
44 "export * from './health.js';",
45 "export * from './constants.js';",
46 }, "\n")), 0644)
47
48 ll.Log("Transpiling", "cyan", "to JS using esbuild")
49 result := esbuild.Build(esbuild.BuildOptions{
50 EntryPoints: []string{"typescript/index.ts"},
51 Outdir: "typescript-dist/",
52 Bundle: true,
53 Sourcemap: esbuild.SourceMapLinked,
54 Format: esbuild.FormatESModule,
55 Platform: esbuild.PlatformNeutral,
56 })
57
58 for _, msg := range result.Warnings {
59 ll.Warn(formatEsbuildMessage(msg))
60 }
61
62 if len(result.Errors) > 0 {
63 for _, msg := range result.Errors {
64 ll.Error(formatEsbuildMessage(msg))
65 os.Exit(1)
66 }
67 } else {
68 for _, file := range result.OutputFiles {
69 err := os.WriteFile(file.Path, file.Contents, 0o677)
70 if err != nil {
71 ll.ErrorDisplay("could not write %s [%s]", err, file.Path, file.Hash)
72 os.Exit(1)
73 }
74 ll.Log("Wrote", "blue", "%s [dim][%s][reset]", file.Path, file.Hash)
75 }
76 ll.Log("Built", "green", "typescript library to [bold]typescript-dist/[reset]")
77 }
78}
79
80func formatEsbuildMessage(msg esbuild.Message) string {
81 notes := ""
82 for _, note := range msg.Notes {
83 notes += fmt.Sprintf("\nat %s: %s", formatEsbuildLocation(note.Location), note.Text)
84 }
85 return fmt.Sprintf("at %s: %s%s", formatEsbuildLocation(msg.Location), msg.Text, notes)
86}
87
88func formatEsbuildLocation(loc *esbuild.Location) string {
89 if loc == nil {
90 return ""
91 }
92 return fmt.Sprintf("[blue]%s:%d:%d[reset]", loc.File, loc.Line, loc.Column)
93}
94
95type constant struct {
96 name string
97 value string
98}
99
100func printTypescriptConstants(declarations ...constant) []byte {
101 lines := make([]string, 0, len(declarations))
102 for _, decl := range declarations {
103 lines = append(lines, fmt.Sprintf("export const %s = '%s';", decl.name, decl.value))
104 }
105 return []byte(strings.Join(lines, "\n"))
106}
107
108func writeTypescriptDefinition(reflector *jsonschema.Reflector, typename string, typ interface{}, filename string) {
109 schema := reflector.Reflect(typ)
110 schemaJSON, err := json.Marshal(schema)
111 if err != nil {
112 fmt.Printf("Error generating schema: %v\n", err)
113 return
114 }
115
116 // Set up quicktype command to read from stdin
117 cmd := exec.Command("npm", "exec", "quicktype", "--", "--lang=ts", "--src-lang=schema", "--just-types", fmt.Sprintf("--top-level=%s", typename))
118
119 // Create a pipe to stdin for the quicktype command
120 stdin, err := cmd.StdinPipe()
121 if err != nil {
122 fmt.Printf("Error creating stdin pipe: %v\n", err)
123 return
124 }
125
126 stdout, err := cmd.StdoutPipe()
127 if err != nil {
128 fmt.Printf("Error creating stdout pipe: %v\n", err)
129 return
130 }
131
132 stderr, err := cmd.StderrPipe() // Create a pipe for stderr
133 if err != nil {
134 fmt.Printf("Error creating stderr pipe: %v\n", err)
135 return
136 }
137
138 // Start the command
139 if err := cmd.Start(); err != nil {
140 fmt.Printf("Error starting quicktype: %v\n", err)
141 return
142 }
143
144 // Write the JSON schema to quicktype's stdin
145 _, err = stdin.Write(schemaJSON)
146 if err != nil {
147 fmt.Printf("Error writing to stdin: %v\n", err)
148 return
149 }
150 stdin.Close() // Important to close the pipe to signal EOF to quicktype
151
152 output, err := io.ReadAll(stdout)
153 if err != nil {
154 fmt.Printf("Error reading from stdout: %v\n", err)
155 return
156 }
157
158 // Read any error messages from quicktype's stderr
159 errorOutput, err := io.ReadAll(stderr)
160 if err != nil {
161 fmt.Printf("Error reading quicktype stderr: %v\n", err)
162 return
163 }
164
165 // Wait for the command to finish
166 if err := cmd.Wait(); err != nil {
167 fmt.Printf("Error waiting for quicktype command: %v\n", err)
168 // Print the stderr output in case of an error
169 fmt.Printf("Quicktype stderr: %s\n", errorOutput)
170 return
171 }
172
173 // Print or save the TypeScript output
174 os.WriteFile(filename, output, 0644)
175}