Alex Eagle | d14d083 | 2017-11-30 01:08:45 +0100 | [diff] [blame] | 1 | package main |
| 2 | |
| 3 | import ( |
Greg Magolan | b21ead9 | 2017-12-12 15:18:16 +0100 | [diff] [blame] | 4 | "bufio" |
Alex Eagle | d14d083 | 2017-11-30 01:08:45 +0100 | [diff] [blame] | 5 | "flag" |
| 6 | "fmt" |
Greg Magolan | b21ead9 | 2017-12-12 15:18:16 +0100 | [diff] [blame] | 7 | "io" |
| 8 | "io/ioutil" |
Alex Eagle | d14d083 | 2017-11-30 01:08:45 +0100 | [diff] [blame] | 9 | "net/http" |
| 10 | "os" |
| 11 | "strings" |
| 12 | |
alexeagle | 5af9229 | 2018-07-17 03:16:56 +0200 | [diff] [blame] | 13 | "github.com/bazelbuild/rules_typescript/devserver/concatjs" |
| 14 | "github.com/bazelbuild/rules_typescript/devserver/devserver" |
Paul Gschwendtner | 58b1910 | 2019-02-08 11:53:07 -0800 | [diff] [blame] | 15 | "github.com/bazelbuild/rules_typescript/devserver/runfiles" |
Alex Eagle | d14d083 | 2017-11-30 01:08:45 +0100 | [diff] [blame] | 16 | ) |
| 17 | |
| 18 | var ( |
alexeagle | af0309e | 2019-03-27 10:17:59 -0700 | [diff] [blame] | 19 | port = flag.Int("port", 5432, "server port to listen on") |
Paul Gschwendtner | 58b1910 | 2019-02-08 11:53:07 -0800 | [diff] [blame] | 20 | // 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 Magolan | b21ead9 | 2017-12-12 15:18:16 +0100 | [diff] [blame] | 22 | 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 Eagle | d14d083 | 2017-11-30 01:08:45 +0100 | [diff] [blame] | 28 | ) |
| 29 | |
| 30 | func main() { |
| 31 | flag.Parse() |
| 32 | |
Paul Gschwendtner | 58b1910 | 2019-02-08 11:53:07 -0800 | [diff] [blame] | 33 | if len(*pkgs) == 0 || (*manifest == "") || (*scriptsManifest == "") { |
Alex Eagle | d14d083 | 2017-11-30 01:08:45 +0100 | [diff] [blame] | 34 | fmt.Fprintf(os.Stderr, "Required argument not set\n") |
| 35 | os.Exit(1) |
| 36 | } |
| 37 | |
Paul Gschwendtner | 58b1910 | 2019-02-08 11:53:07 -0800 | [diff] [blame] | 38 | 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 Eagle | d14d083 | 2017-11-30 01:08:45 +0100 | [diff] [blame] | 42 | os.Exit(1) |
| 43 | } |
| 44 | |
Paul Gschwendtner | 58b1910 | 2019-02-08 11:53:07 -0800 | [diff] [blame] | 45 | scriptFiles, err := manifestFiles(manifestPath) |
Greg Magolan | b21ead9 | 2017-12-12 15:18:16 +0100 | [diff] [blame] | 46 | if err != nil { |
| 47 | fmt.Fprintf(os.Stderr, "Failed to read scripts_manifest: %v\n", err) |
| 48 | os.Exit(1) |
| 49 | } |
| 50 | |
Paul Gschwendtner | 58b1910 | 2019-02-08 11:53:07 -0800 | [diff] [blame] | 51 | 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 Magolan | b21ead9 | 2017-12-12 15:18:16 +0100 | [diff] [blame] | 57 | 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 Magolan | d6cb50f | 2017-12-22 18:51:31 +0100 | [diff] [blame] | 72 | // 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 Magolan | b21ead9 | 2017-12-12 15:18:16 +0100 | [diff] [blame] | 84 | // 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 Gschwendtner | 58b1910 | 2019-02-08 11:53:07 -0800 | [diff] [blame] | 88 | 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 Magolan | b21ead9 | 2017-12-12 15:18:16 +0100 | [diff] [blame] | 94 | 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 Gschwendtner | 58b1910 | 2019-02-08 11:53:07 -0800 | [diff] [blame] | 107 | http.Handle(*servingPath, concatjs.ServeConcatenatedJS(*manifest, *base, preScripts, postScripts, |
| 108 | &RunfileFileSystem{})) |
Alex Eagle | d14d083 | 2017-11-30 01:08:45 +0100 | [diff] [blame] | 109 | 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 | } |
alexeagle | af0309e | 2019-03-27 10:17:59 -0700 | [diff] [blame] | 116 | // 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 Eagle | d14d083 | 2017-11-30 01:08:45 +0100 | [diff] [blame] | 122 | |
| 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 Magolan | b21ead9 | 2017-12-12 15:18:16 +0100 | [diff] [blame] | 127 | |
| 128 | func loadScript(path string) (string, error) { |
| 129 | buf, err := ioutil.ReadFile(path) |
| 130 | if err != nil { |
| 131 | return "", err |
| 132 | } |
TypeScript Team | 6a3780f | 2019-04-08 15:48:50 -0700 | [diff] [blame] | 133 | return fmt.Sprintf("// %s\n%s", path, buf), nil |
Greg Magolan | b21ead9 | 2017-12-12 15:18:16 +0100 | [diff] [blame] | 134 | } |
| 135 | |
| 136 | // manifestFiles parses a manifest, returning a list of the files in the manifest. |
| 137 | func 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. |
| 147 | func 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 | } |