1package main 2 3import ( 4 "fmt" 5 "io" 6 "os" 7 "runtime/debug" 8 "strings" 9 "time" 10 11 "github.com/evanw/esbuild/internal/api_helpers" 12 "github.com/evanw/esbuild/internal/logger" 13 "github.com/evanw/esbuild/pkg/cli" 14) 15 16var helpText = func(colors logger.Colors) string { 17 // Read "NO_COLOR" from the environment. This is a convention that some 18 // software follows. See https://no-color.org/ for more information. 19 for _, key := range os.Environ() { 20 if strings.HasPrefix(key, "NO_COLOR=") { 21 colors = logger.Colors{} 22 break 23 } 24 } 25 26 return ` 27` + colors.Bold + `Usage:` + colors.Reset + ` 28 esbuild [options] [entry points] 29 30` + colors.Bold + `Documentation:` + colors.Reset + ` 31 ` + colors.Underline + `https://esbuild.github.io/` + colors.Reset + ` 32 33` + colors.Bold + `Repository:` + colors.Reset + ` 34 ` + colors.Underline + `https://github.com/evanw/esbuild` + colors.Reset + ` 35 36` + colors.Bold + `Simple options:` + colors.Reset + ` 37 --bundle Bundle all dependencies into the output files 38 --define:K=V Substitute K with V while parsing 39 --external:M Exclude module M from the bundle (can use * wildcards) 40 --format=... Output format (iife | cjs | esm, no default when not 41 bundling, otherwise default is iife when platform 42 is browser and cjs when platform is node) 43 --loader:X=L Use loader L to load file extension X, where L is 44 one of: js | jsx | ts | tsx | json | text | base64 | 45 file | dataurl | binary 46 --minify Minify the output (sets all --minify-* flags) 47 --outdir=... The output directory (for multiple entry points) 48 --outfile=... The output file (for one entry point) 49 --platform=... Platform target (browser | node | neutral, 50 default browser) 51 --serve=... Start a local HTTP server on this host:port for outputs 52 --sourcemap Emit a source map 53 --splitting Enable code splitting (currently only for esm) 54 --target=... Environment target (e.g. es2017, chrome58, firefox57, 55 safari11, edge16, node10, default esnext) 56 --watch Watch mode: rebuild on file system changes 57 58` + colors.Bold + `Advanced options:` + colors.Reset + ` 59 --allow-overwrite Allow output files to overwrite input files 60 --asset-names=... Path template to use for "file" loader files 61 (default "[name]-[hash]") 62 --banner:T=... Text to be prepended to each output file of type T 63 where T is one of: css | js 64 --charset=utf8 Do not escape UTF-8 code points 65 --chunk-names=... Path template to use for code splitting chunks 66 (default "[name]-[hash]") 67 --color=... Force use of color terminal escapes (true | false) 68 --entry-names=... Path template to use for entry point output paths 69 (default "[dir]/[name]", can also use "[hash]") 70 --footer:T=... Text to be appended to each output file of type T 71 where T is one of: css | js 72 --global-name=... The name of the global for the IIFE format 73 --inject:F Import the file F into all input files and 74 automatically replace matching globals with imports 75 --jsx-factory=... What to use for JSX instead of React.createElement 76 --jsx-fragment=... What to use for JSX instead of React.Fragment 77 --jsx=... Set to "preserve" to disable transforming JSX to JS 78 --keep-names Preserve "name" on functions and classes 79 --legal-comments=... Where to place license comments (none | inline | 80 eof | linked | external, default eof when bundling 81 and inline otherwise) 82 --log-level=... Disable logging (verbose | debug | info | warning | 83 error | silent, default info) 84 --log-limit=... Maximum message count or 0 to disable (default 10) 85 --main-fields=... Override the main file order in package.json 86 (default "browser,module,main" when platform is 87 browser and "main,module" when platform is node) 88 --metafile=... Write metadata about the build to a JSON file 89 --minify-whitespace Remove whitespace in output files 90 --minify-identifiers Shorten identifiers in output files 91 --minify-syntax Use equivalent but shorter syntax in output files 92 --out-extension:.js=.mjs Use a custom output extension instead of ".js" 93 --outbase=... The base path used to determine entry point output 94 paths (for multiple entry points) 95 --preserve-symlinks Disable symlink resolution for module lookup 96 --public-path=... Set the base URL for the "file" loader 97 --pure:N Mark the name N as a pure function for tree shaking 98 --resolve-extensions=... A comma-separated list of implicit extensions 99 (default ".tsx,.ts,.jsx,.js,.css,.json") 100 --servedir=... What to serve in addition to generated output files 101 --source-root=... Sets the "sourceRoot" field in generated source maps 102 --sourcefile=... Set the source file for the source map (for stdin) 103 --sourcemap=external Do not link to the source map with a comment 104 --sourcemap=inline Emit the source map with an inline data URL 105 --sources-content=false Omit "sourcesContent" in generated source maps 106 --tree-shaking=... Set to "ignore-annotations" to work with packages 107 that have incorrect tree-shaking annotations 108 --tsconfig=... Use this tsconfig.json file instead of other ones 109 --version Print the current version (` + esbuildVersion + `) and exit 110 111` + colors.Bold + `Examples:` + colors.Reset + ` 112 ` + colors.Dim + `# Produces dist/entry_point.js and dist/entry_point.js.map` + colors.Reset + ` 113 esbuild --bundle entry_point.js --outdir=dist --minify --sourcemap 114 115 ` + colors.Dim + `# Allow JSX syntax in .js files` + colors.Reset + ` 116 esbuild --bundle entry_point.js --outfile=out.js --loader:.js=jsx 117 118 ` + colors.Dim + `# Substitute the identifier RELEASE for the literal true` + colors.Reset + ` 119 esbuild example.js --outfile=out.js --define:RELEASE=true 120 121 ` + colors.Dim + `# Provide input via stdin, get output via stdout` + colors.Reset + ` 122 esbuild --minify --loader=ts < input.ts > output.js 123 124 ` + colors.Dim + `# Automatically rebuild when input files are changed` + colors.Reset + ` 125 esbuild app.ts --bundle --watch 126 127 ` + colors.Dim + `# Start a local HTTP server for everything in "www"` + colors.Reset + ` 128 esbuild app.ts --bundle --servedir=www --outdir=www/js 129 130` 131} 132 133func main() { 134 logger.API = logger.CLIAPI 135 136 osArgs := os.Args[1:] 137 heapFile := "" 138 traceFile := "" 139 cpuprofileFile := "" 140 isRunningService := false 141 sendPings := false 142 143 // Do an initial scan over the argument list 144 argsEnd := 0 145 for _, arg := range osArgs { 146 switch { 147 // Show help if a common help flag is provided 148 case arg == "-h", arg == "-help", arg == "--help", arg == "/?": 149 logger.PrintText(os.Stdout, logger.LevelSilent, os.Args, helpText) 150 os.Exit(0) 151 152 // Special-case the version flag here 153 case arg == "--version": 154 fmt.Printf("%s\n", esbuildVersion) 155 os.Exit(0) 156 157 case strings.HasPrefix(arg, "--heap="): 158 heapFile = arg[len("--heap="):] 159 160 case strings.HasPrefix(arg, "--trace="): 161 traceFile = arg[len("--trace="):] 162 163 case strings.HasPrefix(arg, "--timing"): 164 // This is a hidden flag because it's only intended for debugging esbuild 165 // itself. The output is not documented and not stable. 166 api_helpers.UseTimer = true 167 168 case strings.HasPrefix(arg, "--cpuprofile="): 169 cpuprofileFile = arg[len("--cpuprofile="):] 170 171 // This flag turns the process into a long-running service that uses 172 // message passing with the host process over stdin/stdout 173 case strings.HasPrefix(arg, "--service="): 174 hostVersion := arg[len("--service="):] 175 isRunningService = true 176 177 // Validate the host's version number to make sure esbuild was installed 178 // correctly. This check was added because some people have reported 179 // errors that appear to indicate an incorrect installation. 180 if hostVersion != esbuildVersion { 181 logger.PrintErrorToStderr(osArgs, 182 fmt.Sprintf("Cannot start service: Host version %q does not match binary version %q", 183 hostVersion, esbuildVersion)) 184 os.Exit(1) 185 } 186 187 case strings.HasPrefix(arg, "--ping"): 188 sendPings = true 189 190 default: 191 // Strip any arguments that were handled above 192 osArgs[argsEnd] = arg 193 argsEnd++ 194 } 195 } 196 osArgs = osArgs[:argsEnd] 197 198 // Run in service mode if requested 199 if isRunningService { 200 runService(sendPings) 201 return 202 } 203 204 // Print help text when there are no arguments 205 isStdinTTY := logger.GetTerminalInfo(os.Stdin).IsTTY 206 if len(osArgs) == 0 && isStdinTTY { 207 logger.PrintText(os.Stdout, logger.LevelSilent, osArgs, helpText) 208 os.Exit(0) 209 } 210 211 // Capture the defer statements below so the "done" message comes last 212 exitCode := 1 213 func() { 214 // To view a CPU trace, use "go tool trace [file]". Note that the trace 215 // viewer doesn't work under Windows Subsystem for Linux for some reason. 216 if traceFile != "" { 217 if done := createTraceFile(osArgs, traceFile); done == nil { 218 return 219 } else { 220 defer done() 221 } 222 } 223 224 // To view a heap trace, use "go tool pprof [file]" and type "top". You can 225 // also drop it into https://speedscope.app and use the "left heavy" or 226 // "sandwich" view modes. 227 if heapFile != "" { 228 if done := createHeapFile(osArgs, heapFile); done == nil { 229 return 230 } else { 231 defer done() 232 } 233 } 234 235 // To view a CPU profile, drop the file into https://speedscope.app. 236 // Note: Running the CPU profiler doesn't work under Windows subsystem for 237 // Linux. The profiler has to be built for native Windows and run using the 238 // command prompt instead. 239 if cpuprofileFile != "" { 240 if done := createCpuprofileFile(osArgs, cpuprofileFile); done == nil { 241 return 242 } else { 243 defer done() 244 } 245 } 246 247 if cpuprofileFile != "" { 248 // The CPU profiler in Go only runs at 100 Hz, which is far too slow to 249 // return useful information for esbuild, since it's so fast. Let's keep 250 // running for 30 seconds straight, which should give us 3,000 samples. 251 seconds := 30.0 252 start := time.Now() 253 for time.Since(start).Seconds() < seconds { 254 exitCode = cli.Run(osArgs) 255 } 256 } else { 257 // Don't disable the GC if this is a long-running process 258 isServeOrWatch := false 259 for _, arg := range osArgs { 260 if arg == "--serve" || arg == "--watch" || strings.HasPrefix(arg, "--serve=") { 261 isServeOrWatch = true 262 break 263 } 264 } 265 266 if !isServeOrWatch { 267 // Disable the GC since we're just going to allocate a bunch of memory 268 // and then exit anyway. This speedup is not insignificant. Make sure to 269 // only do this here once we know that we're not going to be a long-lived 270 // process though. 271 debug.SetGCPercent(-1) 272 } else if !isStdinTTY { 273 // If stdin isn't a TTY, watch stdin and abort in case it is closed. 274 // This is necessary when the esbuild binary executable is invoked via 275 // the Erlang VM, which doesn't provide a way to exit a child process. 276 // See: https://github.com/brunch/brunch/issues/920. 277 // 278 // We don't do this when stdin is a TTY because that interferes with 279 // the Unix background job system. If we read from stdin then Ctrl+Z 280 // to move the process to the background will incorrectly cause the 281 // job to stop. See: https://github.com/brunch/brunch/issues/998. 282 go func() { 283 // This just discards information from stdin because we don't use 284 // it and we can avoid unnecessarily allocating space for it 285 buffer := make([]byte, 512) 286 for { 287 _, err := os.Stdin.Read(buffer) 288 if err != nil { 289 // Only exit cleanly if stdin was closed cleanly 290 if err == io.EOF { 291 os.Exit(0) 292 } else { 293 os.Exit(1) 294 } 295 } 296 } 297 }() 298 } 299 300 exitCode = cli.Run(osArgs) 301 } 302 }() 303 304 os.Exit(exitCode) 305} 306