blob: 466a63de6c798b4fc0208a1c38548fa71d3e4d35 [file] [log] [blame]
Alex Eagled14d0832017-11-30 01:08:45 +01001package main
2
3import (
Greg Magolanb21ead92017-12-12 15:18:16 +01004 "bufio"
Alex Eagled14d0832017-11-30 01:08:45 +01005 "flag"
6 "fmt"
Greg Magolanb21ead92017-12-12 15:18:16 +01007 "io"
8 "io/ioutil"
Alex Eagled14d0832017-11-30 01:08:45 +01009 "net/http"
10 "os"
11 "strings"
12
alexeagle5af92292018-07-17 03:16:56 +020013 "github.com/bazelbuild/rules_typescript/devserver/concatjs"
14 "github.com/bazelbuild/rules_typescript/devserver/devserver"
Paul Gschwendtner58b19102019-02-08 11:53:07 -080015 "github.com/bazelbuild/rules_typescript/devserver/runfiles"
Alex Eagled14d0832017-11-30 01:08:45 +010016)
17
18var (
alexeagleaf0309e2019-03-27 10:17:59 -070019 port = flag.Int("port", 5432, "server port to listen on")
Paul Gschwendtner58b19102019-02-08 11:53:07 -080020 // The "base" CLI flag is only kept because within Google3 because removing would be a breaking change due to
21 // ConcatJS and "devserver/devserver.go" still respecting the specified base flag.
Greg Magolanb21ead92017-12-12 15:18:16 +010022 base = flag.String("base", "", "server base (required, runfiles of the binary)")
23 pkgs = flag.String("packages", "", "root package(s) to serve, comma-separated")
24 manifest = flag.String("manifest", "", "sources manifest (.MF)")
25 scriptsManifest = flag.String("scripts_manifest", "", "preScripts manifest (.MF)")
26 servingPath = flag.String("serving_path", "/_/ts_scripts.js", "path to serve the combined sources at")
27 entryModule = flag.String("entry_module", "", "entry module name")
Alex Eagled14d0832017-11-30 01:08:45 +010028)
29
30func main() {
31 flag.Parse()
32
Paul Gschwendtner58b19102019-02-08 11:53:07 -080033 if len(*pkgs) == 0 || (*manifest == "") || (*scriptsManifest == "") {
Alex Eagled14d0832017-11-30 01:08:45 +010034 fmt.Fprintf(os.Stderr, "Required argument not set\n")
35 os.Exit(1)
36 }
37
Paul Gschwendtner58b19102019-02-08 11:53:07 -080038 manifestPath, err := runfiles.Runfile(*base, *scriptsManifest)
39
40 if err != nil {
41 fmt.Fprintf(os.Stderr, "Failed to find scripts_manifest in runfiles: %v\n", err)
Alex Eagled14d0832017-11-30 01:08:45 +010042 os.Exit(1)
43 }
44
Paul Gschwendtner58b19102019-02-08 11:53:07 -080045 scriptFiles, err := manifestFiles(manifestPath)
Greg Magolanb21ead92017-12-12 15:18:16 +010046 if err != nil {
47 fmt.Fprintf(os.Stderr, "Failed to read scripts_manifest: %v\n", err)
48 os.Exit(1)
49 }
50
Paul Gschwendtner58b19102019-02-08 11:53:07 -080051 if !strings.HasPrefix(*servingPath, "/") {
52 fmt.Fprintf(os.Stderr, "The specified serving_path does not start with a slash. "+
53 "This causes the serving path to not have any effect.\n")
54 os.Exit(1)
55 }
56
Greg Magolanb21ead92017-12-12 15:18:16 +010057 preScripts := make([]string, 0, 100)
58 postScripts := make([]string, 0, 1)
59
60 // Include the livereload script if IBAZEL_LIVERELOAD_URL is set.
61 livereloadUrl := os.Getenv("IBAZEL_LIVERELOAD_URL")
62 if livereloadUrl != "" {
63 fmt.Printf("Serving livereload script from %s\n", livereloadUrl)
64 livereloadLoaderSnippet := fmt.Sprintf(`(function(){
65 const script = document.createElement('script');
66 script.src = "%s";
67 document.head.appendChild(script);
68})();`, livereloadUrl)
69 preScripts = append(preScripts, livereloadLoaderSnippet)
70 }
71
Greg Magoland6cb50f2017-12-22 18:51:31 +010072 // Include the profiler script if IBAZEL_PROFILER_URL is set.
73 profilerScriptURL := os.Getenv("IBAZEL_PROFILER_URL")
74 if profilerScriptURL != "" {
75 fmt.Printf("Serving profiler script from %s\n", profilerScriptURL)
76 profilerLoaderSnippet := fmt.Sprintf(`(function(){
77 const script = document.createElement('script');
78 script.src = "%s";
79 document.head.appendChild(script);
80})();`, profilerScriptURL)
81 preScripts = append(preScripts, profilerLoaderSnippet)
82 }
83
Greg Magolanb21ead92017-12-12 15:18:16 +010084 // Include all user scripts in preScripts. This should always include
85 // the requirejs script which is added to scriptFiles by the devserver
86 // skylark rule.
87 for _, v := range scriptFiles {
Paul Gschwendtner58b19102019-02-08 11:53:07 -080088 runfile, err := runfiles.Runfile(*base, v)
89 if err != nil {
90 fmt.Fprintf(os.Stderr, "Could not find runfile %s, got error %s", v, err)
91 }
92
93 js, err := loadScript(runfile)
Greg Magolanb21ead92017-12-12 15:18:16 +010094 if err != nil {
95 fmt.Fprintf(os.Stderr, "Failed to read script %s: %v\n", v, err)
96 } else {
97 preScripts = append(preScripts, js)
98 }
99 }
100
101 // If the entryModule is set then add a snippet to load
102 // the application to postScripts to be outputted after the sources
103 if *entryModule != "" {
104 postScripts = append(postScripts, fmt.Sprintf("require([\"%s\"]);", *entryModule))
105 }
106
Paul Gschwendtner58b19102019-02-08 11:53:07 -0800107 http.Handle(*servingPath, concatjs.ServeConcatenatedJS(*manifest, *base, preScripts, postScripts,
108 &RunfileFileSystem{}))
Alex Eagled14d0832017-11-30 01:08:45 +0100109 pkgList := strings.Split(*pkgs, ",")
110 http.HandleFunc("/", devserver.CreateFileHandler(*servingPath, *manifest, pkgList, *base))
111
112 h, err := os.Hostname()
113 if err != nil {
114 h = "localhost"
115 }
alexeagleaf0309e2019-03-27 10:17:59 -0700116 // Detect if we are running in a linux container inside ChromeOS
117 // If so, we assume you want to use the native browser (outside the container)
118 // so you'll need to modify the hostname to access the server
119 if _, err := os.Stat("/etc/apt/sources.list.d/cros.list"); err == nil {
120 h = h + ".linux.test"
121 }
Alex Eagled14d0832017-11-30 01:08:45 +0100122
123 fmt.Printf("Server listening on http://%s:%d/\n", h, *port)
124 fmt.Fprintln(os.Stderr, http.ListenAndServe(fmt.Sprintf(":%d", *port), nil).Error())
125 os.Exit(1)
126}
Greg Magolanb21ead92017-12-12 15:18:16 +0100127
128func loadScript(path string) (string, error) {
129 buf, err := ioutil.ReadFile(path)
130 if err != nil {
131 return "", err
132 }
TypeScript Team6a3780f2019-04-08 15:48:50 -0700133 return fmt.Sprintf("// %s\n%s", path, buf), nil
Greg Magolanb21ead92017-12-12 15:18:16 +0100134}
135
136// manifestFiles parses a manifest, returning a list of the files in the manifest.
137func manifestFiles(manifest string) ([]string, error) {
138 f, err := os.Open(manifest)
139 if err != nil {
140 return nil, fmt.Errorf("could not read manifest %s: %s", manifest, err)
141 }
142 defer f.Close()
143 return manifestFilesFromReader(f)
144}
145
146// manifestFilesFromReader is a helper for manifestFiles, split out for testing.
147func manifestFilesFromReader(r io.Reader) ([]string, error) {
148 var lines []string
149 s := bufio.NewScanner(r)
150 for s.Scan() {
151 path := s.Text()
152 if path == "" {
153 continue
154 }
155 lines = append(lines, path)
156 }
157 if err := s.Err(); err != nil {
158 return nil, err
159 }
160
161 return lines, nil
162}